-
Notifications
You must be signed in to change notification settings - Fork 274
[TG-9299] Fix crash in case of user provided clinit for a clinit which is not in the symbol table #5073
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
[TG-9299] Fix crash in case of user provided clinit for a clinit which is not in the symbol table #5073
Conversation
ad55b0e
to
88a25c6
Compare
@@ -0,0 +1,3 @@ | |||
public class Empty { |
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.
Rather that emptiness, is having or not having static fields the critical attribute?
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've clarified that in the class names
@@ -838,7 +838,9 @@ void assign_from_json( | |||
location.set_function(function_id); | |||
allocate_objectst allocate(ID_java, location, function_id, symbol_table); | |||
code_blockt body_rec; | |||
const auto class_name = declaring_class(symbol_table.lookup_ref(function_id)); | |||
const symbolt *function_symbol = symbol_table.lookup(function_id); |
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.
is lookup-ref vs. lookup-then-invariant really that different?
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, you get a reasonable message on failure.
-- | ||
This tests what happens when the non-empty class is substitued with an empty | ||
one after compilation: After compiling EmptyNonEmpty.class and Empty.class, | ||
Empty.class is moved to NonEmpty.class. |
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.
What does 'moved' mean?
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've clarified that
public class EmptyNonEmpty { | ||
|
||
/// This test is meant to illustrate what happens when the non-empty | ||
/// class is substitued with an empty one after compilation. |
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.
substituted
^SIGNAL=0$ | ||
-- | ||
-- | ||
This tests what happens when the non-empty class is substitued with an empty |
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.
substituted
@@ -777,6 +777,7 @@ code_blockt get_user_specified_clinit_body( | |||
std::unordered_map<std::string, object_creation_referencet> &references) | |||
{ | |||
jsont json; | |||
const irep_idt real_clinit_name = clinit_function_name(class_id); |
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.
(commit message) careful
@@ -838,7 +838,9 @@ void assign_from_json( | |||
location.set_function(function_id); | |||
allocate_objectst allocate(ID_java, location, function_id, symbol_table); | |||
code_blockt body_rec; | |||
const auto class_name = declaring_class(symbol_table.lookup_ref(function_id)); | |||
const symbolt *function_symbol = symbol_table.lookup(function_id); |
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, you get a reasonable message on failure.
88a25c6
to
639ad43
Compare
@@ -0,0 +1,9 @@ | |||
public class EmptyNonEmpty { |
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.
Change this name too
639ad43
to
7cd1a87
Compare
7cd1a87
to
81e1c9b
Compare
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.
✔️
Passed Diffblue compatibility checks (cbmc commit: 81e1c9b).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/125753721
Codecov Report
@@ Coverage Diff @@
## develop #5073 +/- ##
===========================================
+ Coverage 69.67% 69.67% +<.01%
===========================================
Files 1319 1319
Lines 109290 109298 +8
===========================================
+ Hits 76150 76157 +7
- Misses 33140 33141 +1
Continue to review full report at Codecov.
|
81e1c9b
to
7a15634
Compare
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.
Is there any way to add a regression test for this? See my question below: I can't see how the bug that this is fixing would occur in practice.
@@ -838,7 +838,9 @@ void assign_from_json( | |||
location.set_function(function_id); | |||
allocate_objectst allocate(ID_java, location, function_id, symbol_table); | |||
code_blockt body_rec; | |||
const auto class_name = declaring_class(symbol_table.lookup_ref(function_id)); | |||
const symbolt *function_symbol = symbol_table.lookup(function_id); | |||
INVARIANT(function_symbol, "Function must appear in symbol table"); |
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.
💡 This message is quite general, maybe include function_id
in it and/or mention the context in which this was looked up.
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 don't know, the backtrace of the invariant should already give us the context.
if(symbol_table.lookup(real_clinit_name) == nullptr) | ||
{ | ||
// Case where there is a user provided clinit, but the real clinit | ||
// doesn't appear in the symbol table. This may occur when some class |
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.
❓ Do you have an example of such a class substitution? Having these additional checks in is good of course, but I don't understand how this ever happened in practice. get_user_specified_clinit_body
is only called to populate the body of a synthetic_method_typet::USER_SPECIFIED_STATIC_INITIALIZER
if we see one. Such a synthetic method is only created in create_user_specified_clinit_function_symbol
, which in turn is only called after several if
s, one of which is symbol_table.has_symbol(clinit_function_name(class_name))
.
So it looks like the only way we could end up with a user_specified_clinit
but no clinit
would be if clinit
was first added to the symbol table, then user_specified_clinit
was added, and finally clinit
was removed - but I'm not aware of the latter ever happening?
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.
@antlechner
create_user_specified_clinit_function_symbol
is called if needs_clinit_wrapper
is true, which is when clinit
exists or some other condition. If you think this is wrong then needs_clinit_wrapper
needs to be corrected.
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.
Ah, that's the part I overlooked! needs_clinit_wrapper
is true if clinit
exists for this class or a superclass.
So it looks like the mistake is that currently, we create a user_specified_clinit
iff we create a clinit_wrapper
. But really we should be creating a user_specified_clinit
iff there is a clinit
.
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.
ok, I'm still going to merge this PR since it prevents a crash but the condition for calling create_user_specified_clinit_function_symbol
should be fixed. Do you want to create an issue for that?
@@ -817,6 +817,13 @@ code_blockt get_user_specified_clinit_body( | |||
} | |||
} | |||
} | |||
if(symbol_table.lookup(real_clinit_name) == 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.
💡 Looks like it would make more sense to move this check just below the definition of real_clinit_name
.
Edit: Ah, this is done in the next commit. Those two commits should probably be squashed.
This code doesn't need a writable reference so it should call lookup instead. We also add an invariant to check that the reference does exists, this gives more helpful messages than the generic `_Map_base::at`.
7a15634
to
0bf781d
Compare
The assign_from_json will fail with an invariant failure if it is called on a method name that does not exist in the symbol table so we should be careful when calling it. This also make the function return early when the real clinit is not in the symbol table, which can avoid some unecessary computation.
The declaration of the reference class_json_value prevented merging the two if, and it can be removed without making the code more complicated since it is just an alias for class_entry->second
Test what happens when clinit for a class is not in the symbol table though the JSON file contains entries for this class. This used to cause map::at to throw an exception.
0bf781d
to
1b3c289
Compare
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.
✔️
Passed Diffblue compatibility checks (cbmc commit: 1b3c289).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/126241524
The look-up for clinit could fail when we had a user provided clinit but the real clinit was not in the symbol table.
This adds a test which illustrate the case that was previously causing the exception.