-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
Conversation
… null pointer.
if (lhs == nullptr && rhs == nullptr) | ||
return true; | ||
if (lhs == nullptr && !(rhs == nullptr)) | ||
return false; | ||
if (!(lhs == nullptr) && rhs == nullptr) | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please simplify:
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; |
There was a problem hiding this comment.
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<> &
.
return (lhs == rhs) || (*lhs == *rhs); return *lhs == *rhs;
People make changes that "fix" a tiny little problem, but don't read the damn code. @parrt what about these occurrences?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
- Cpp:
std::vector<Ref<const PredictionContext>> parents = { a->parent, nullptr };
Compare this with CSharp, Java, PHP, and Python3.
-
CSharp:
PredictionContext[] parents = { a.parent, null }; -
Java:
PredictionContext[] parents = {a.parent, null}; -
Python3:
parents = [ a.parentCtx, None ]
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.
- Dart:
parents = [b.parent, a.parent];
For Go, I think it too was written without using null pointers:
- Go:
antlr4/runtime/Go/antlr/prediction_context.go
Line 534 in 16cfc6b
parents = []PredictionContext{b.parentCtx, a.parentCtx}
There was a problem hiding this comment.
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. :(
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Fixed by #3958 |
Very simply, the code that compares two PredictionContext must test before dereferencing.
antlr4/runtime/Cpp/runtime/src/atn/ArrayPredictionContext.cpp
Line 25 in aa1f1f1