Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for issue 3845 -- null pointer deref in Cpp target #3952

Closed
wants to merge 2 commits into from

Conversation

kaby76
Copy link
Contributor

@kaby76 kaby76 commented Nov 5, 2022

Very simply, the code that compares two PredictionContext must test before dereferencing.

. If both pointers are null then the compare is "true".

Comment on lines 25 to 30
if (lhs == nullptr && rhs == nullptr)
return true;
if (lhs == nullptr && !(rhs == nullptr))
return false;
if (!(lhs == nullptr) && rhs == nullptr)
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please simplify:

Suggested change
if (lhs == nullptr && rhs == nullptr)
return true;
if (lhs == nullptr && !(rhs == nullptr))
return false;
if (!(lhs == nullptr) && rhs == nullptr)
return false;
if (lhs == nullptr)
return rhs == nullptr;
else if (rhs == nullptr)
return false;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

But, I bet there are other places like this. For example, these two statements are very similar to this bug, with parameters of type Ref<> &.

People make changes that "fix" a tiny little problem, but don't read the damn code. @parrt what about these occurrences?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's why I'm opposed to refactoring for refactoring sake. It can introduce all sorts of subtle bugs.

We have to be careful here for example because the real solution is that context objects should never be null... There is a sentinel that indicates empty stack. That is why I didn't simply add the null checks... There's a larger bug and we shouldn't paint over it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's why I'm opposed to refactoring for refactoring sake. It can introduce all sorts of subtle bugs.

We have to be careful here for example because the real solution is that context objects should never be null... There is a sentinel that indicates empty stack. That is why I didn't simply add the null checks... There's a larger bug and we shouldn't paint over it.

Then, these should be at least assert()'s so that it crashes with a message.

Is there a method that anyone wrote that checks the integrity of the data structure so I can percolate the check around and snuff this bug out? Someone is creating an array with a null pointer at the end of the array.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I was hoping to do with the Data dump from the ATN simulator... We could use it to compare how the different targets work.

BTW, here is the start of me trying to fix C++ by refracturing the complex code: #3863

I gave up trying to debug when a print statement exploded all over my screen with compile errors.

Here is my attempt to start a tracer for ATN: #3817

Copy link
Contributor Author

@kaby76 kaby76 Nov 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think your assumption that there cannot be null parents in an array of PredicateContext may be wrong. Many ports violate that assumption.

I wrote a Check() function that checks the integrity of the ArrayPredictionContext and it led to this code in the Cpp target. The code creates a "parents" array containing a null pointer.

Compare this with CSharp, Java, PHP, and Python3.

For Dart, null pointers are disallowed (I think??). In any case, the code looks like it never creates a "parents" array with a null pointer.

For Go, I think it too was written without using null pointers:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, dang. You're right. Hmm...yeah, touching the ATN simulation is really risky as I don't remember the details well. :(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I can relate to forgetting details. It definitely is concerning that a couple of the targets don't have nulls in the parents array. I think to be "safe", I should test this against the regression tests of grammars-v4 for Cpp.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love to do a comparison of the java target against other targets. I have good trust in java behavior.

@kaby76 kaby76 marked this pull request as draft November 6, 2022 09:42
@kaby76 kaby76 marked this pull request as ready for review November 6, 2022 11:22
@kaby76
Copy link
Contributor Author

kaby76 commented Nov 6, 2022

Changing back from "draft" to "review" because ArrayPredictionContext can contain null pointers.

@@ -22,6 +22,10 @@ namespace {
}

bool predictionContextEqual(const Ref<const PredictionContext> &lhs, const Ref<const PredictionContext> &rhs) {
if (lhs == nullptr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how badly this will hit performance

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how badly this will hit performance

I think this question is difficult to answer without changing the Cpp runtime to not create "parent" arrays with null pointers. For tests that call the comparison function, the parse crashes; for tests that don't call the function, the parse works. Presumably, in Java when it calls equals()] on comparing two "parents" arrays, it will test whether the element in the array is null (see Arrays.equals() => Object.equals()). This change makes Cpp operate the same way as the Java code. The choice is either to get the Cpp working, or rewrite all the runtimes to not use null pointers in "parents".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect it doesn't make any sense to have nulls in parents arrays, so I would opt for x-target cleanup, which as a side effect is likely to slightly improve performance

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could swear that we had a sentinel node that represented top of the stack rather than null but I have to look at the code more carefully because @kaby76 found something that makes it look like it really is using null. I am terrified of changing anything in the ATN simulation given just how much code in the world relies on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old code compare called Arrays::equals(). That in turn would test null pointers. The nullptr in the parents array has existed for 6 years at least.

@parrt
Copy link
Member

parrt commented Nov 19, 2022

Fixed by #3958

@parrt parrt closed this Nov 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants