3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-02-10 11:00:52 +00:00

preserve the initial state of the solver with push/pop for multiple objectives (#8264)

* preserve the initial state of the solver with push/pop for multiple objectives

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* Fix memory corruption in Z3_polynomial_subresultants

The API function had a memory corruption bug where allocating the result
vector while the default_expr2polynomial converter was still in scope
could corrupt the converter's internal expr2var mapping.

Fixed by restructuring the code to:
1. Complete all polynomial computation in a scoped block
2. Store results in a temporary expr_ref_vector
3. Let the converter go out of scope
4. Then allocate and populate the result vector

Also improved the test to:
- Use randomized testing with 20 iterations
- Test both cases: variable in polynomials and variable not in polynomials
- Use proper reference counting (inc_ref before dec_ref)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Lev Nachmanson 2026-01-23 08:41:03 -10:00 committed by GitHub
parent 2a5bfb818b
commit c0583b828d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 107 additions and 40 deletions

View file

@ -33,34 +33,48 @@ extern "C" {
LOG_Z3_polynomial_subresultants(c, p, q, x);
RESET_ERROR_CODE();
polynomial::manager & pm = mk_c(c)->pm();
polynomial_ref _p(pm), _q(pm);
polynomial::scoped_numeral d(pm.m());
default_expr2polynomial converter(mk_c(c)->m(), pm);
if (!converter.to_polynomial(to_expr(p), _p, d) ||
!converter.to_polynomial(to_expr(q), _q, d)) {
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
return nullptr;
// Compute all polynomial results BEFORE allocating the result vector.
// This avoids a memory corruption issue where allocating API objects
// can interfere with the converter's internal state.
expr_ref_vector results(mk_c(c)->m());
{
polynomial_ref _p(pm), _q(pm);
polynomial::scoped_numeral d(pm.m());
default_expr2polynomial converter(mk_c(c)->m(), pm);
if (!converter.to_polynomial(to_expr(p), _p, d) ||
!converter.to_polynomial(to_expr(q), _q, d)) {
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
return nullptr;
}
if (converter.is_var(to_expr(x))) {
expr2var const & mapping = converter.get_mapping();
unsigned v_x = mapping.to_var(to_expr(x));
if (v_x != UINT_MAX) {
polynomial_ref_vector rs(pm);
polynomial_ref r(pm);
expr_ref _r(mk_c(c)->m());
{
cancel_eh<reslimit> eh(mk_c(c)->poly_limit());
api::context::set_interruptable si(*(mk_c(c)), eh);
scoped_timer timer(mk_c(c)->params().m_timeout, &eh);
pm.psc_chain(_p, _q, v_x, rs);
}
for (unsigned i = 0; i < rs.size(); ++i) {
r = rs.get(i);
converter.to_expr(r, true, _r);
results.push_back(_r);
}
}
}
}
// Converter is now out of scope - safe to allocate result vector
Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(result);
if (converter.is_var(to_expr(x))) {
expr2var const & mapping = converter.get_mapping();
unsigned v_x = mapping.to_var(to_expr(x));
polynomial_ref_vector rs(pm);
polynomial_ref r(pm);
expr_ref _r(mk_c(c)->m());
{
cancel_eh<reslimit> eh(mk_c(c)->poly_limit());
api::context::set_interruptable si(*(mk_c(c)), eh);
scoped_timer timer(mk_c(c)->params().m_timeout, &eh);
pm.psc_chain(_p, _q, v_x, rs);
}
for (unsigned i = 0; i < rs.size(); ++i) {
r = rs.get(i);
converter.to_expr(r, true, _r);
result->m_ast_vector.push_back(_r);
}
for (expr* e : results) {
result->m_ast_vector.push_back(e);
}
RETURN_Z3(of_ast_vector(result));
Z3_CATCH_RETURN(nullptr);