3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-06-19 23:26:30 +00:00

Prevent expressions in partial dfa being freed to early

This commit is contained in:
CEisenhofer 2026-05-26 13:07:38 +02:00
parent c18aa647e1
commit 4cd908345a
5 changed files with 125 additions and 62 deletions

View file

@ -28,7 +28,7 @@ namespace euf {
m_rewriter(m),
m_egraph(eg),
m_str_sort(m_seq.str.mk_string_sort(), m),
m_add_plugin(add_plugin) {
m_pin(m) {
// create seq_plugin and register it with the egraph
if (add_plugin)
m_egraph.add_plugin(alloc(seq_plugin, m_egraph, this));
@ -341,7 +341,12 @@ namespace euf {
unsigned eid = e->get_id();
m_expr2snode.reserve(eid + 1, nullptr);
m_expr2snode[eid] = n;
// pin expression via egraph (the egraph has an expr trail)
// Pin the expression for the lifetime of the sgraph: the egraph trail
// would otherwise release it on pop, but the underlying snode lives in
// m_region (never freed) and may still be referenced by clients past
// that pop. See the comment on m_pin in euf_sgraph.h.
m_pin.push_back(e);
// also keep the enode pinning behaviour so congruence closure sees e
mk_enode(e);
++m_stats.m_num_nodes;
return n;

View file

@ -89,7 +89,16 @@ namespace euf {
unsigned_vector m_scopes;
unsigned m_num_scopes = 0;
stats m_stats;
bool m_add_plugin; // whether sgraph created the seq_plugin
// Pins every expression that any (live or popped) snode references via
// m_expr. snodes are allocated in m_region — which is never freed —
// but their m_expr field is owned by the egraph trail. Without this
// pin the egraph would release expressions on pop while clients still
// hold the matching snode* (e.g. inside nielsen_node str_mems, edge
// substitutions, or the partial-DFA cache), turning every later
// get_expr() into a use-after-free. The pin grows monotonically; it
// is dropped only when sgraph itself is destroyed.
expr_ref_vector m_pin;
// maps expression id to snode
ptr_vector<snode> m_expr2snode;