-
Notifications
You must be signed in to change notification settings - Fork 180
fix use-after-free in congruence closure gate table updates #162
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2744,8 +2744,14 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { | |||||||||
| void Closure::simplify_and_gate (Gate *g) { | ||||||||||
| if (skip_and_gate (g)) | ||||||||||
| return; | ||||||||||
| GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); | ||||||||||
| assert (!g->indexed || git != end (table)); | ||||||||||
| // Remove before modifying rhs: MSVC's unordered_set::erase(iterator) | ||||||||||
| // recomputes the hash from the current key to fix up bucket pointers, | ||||||||||
| // so the erase must happen before rhs changes (unlike GCC/Clang STL). | ||||||||||
| if (g->indexed) { | ||||||||||
| table.erase (table.find (g)); | ||||||||||
| g->indexed = false; | ||||||||||
| } | ||||||||||
| GatesTable::iterator git = end (table); | ||||||||||
| LOG (g, "simplifying"); | ||||||||||
| int falsifies = 0; | ||||||||||
| std::vector<int>::iterator it = begin (g->rhs); | ||||||||||
|
|
@@ -4496,8 +4502,11 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, | |||||||||
| assert (dst); | ||||||||||
| assert (internal->val (src) == internal->val (dst)); | ||||||||||
| LOG (g, "rewriting %d into %d in", src, dst); | ||||||||||
| GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); | ||||||||||
| assert (!g->indexed || git != table.end ()); | ||||||||||
| if (g->indexed) { | ||||||||||
| table.erase (table.find (g)); | ||||||||||
|
||||||||||
| table.erase (table.find (g)); | |
| GatesTable::iterator it = table.find (g); | |
| assert (it != end (table)); | |
| table.erase (it); |
Copilot
AI
Mar 9, 2026
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 table.erase(table.find(g)) sequence can call erase(end()) if find(g) fails, which is undefined behavior. Recommend capturing the iterator, asserting it is valid (or calling remove_gate(g)), then erasing and clearing g->indexed.
| table.erase (table.find (g)); | |
| GatesTable::iterator it = table.find (g); | |
| assert (it != table.end ()); | |
| table.erase (it); |
Copilot
AI
Mar 9, 2026
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.
Potential UB: if table.find(g) returns end(table) while g->indexed is true, table.erase(table.find(g)) would erase end(). Suggest storing the iterator and asserting it is valid (or using remove_gate(g)), matching the previous invariant checks.
| table.erase (table.find (g)); | |
| GatesTable::iterator it = table.find (g); | |
| assert (it != end (table)); | |
| table.erase (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.
table.erase(table.find(g))will invokeerasewithend()iffind(g)fails, which is undefined behavior. Previously this was guarded by anasserton the iterator. Consider storing the iterator, asserting it is notend(table)(or callingremove_gate(g)which already enforces the invariant), then erasing it and clearingg->indexed.