3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-27 01:12:40 +00:00

Fix polymorphic application arity validation (#10179)

## Summary

Validate a polymorphic declaration's arity before matching argument
sorts. This prevents
`Z3_mk_app` from reading past the declaration domain and makes both
too-few and too-many
arguments return `Z3_INVALID_ARG`.

Adds C API regression coverage for valid, too-few, and too-many
applications.

Fixes #10177.

## Testing

- `./build-release/test-z3 api`
- `./build-release/test-z3 /a` (92 passed)
- Standalone #10177 reproducer against the patched shared library
This commit is contained in:
1sgtpepper 2026-07-22 10:11:38 +08:00 committed by GitHub
parent 0105b220fd
commit 479ff3476e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 107 additions and 8 deletions

View file

@ -190,16 +190,40 @@ extern "C" {
func_decl* _d = reinterpret_cast<func_decl*>(d);
ast_manager& m = mk_c(c)->m();
if (_d->is_polymorphic()) {
polymorphism::util u(m);
polymorphism::substitution sub(m);
ptr_buffer<sort> domain;
for (unsigned i = 0; i < num_args; ++i) {
if (!sub.match(_d->get_domain(i), arg_list[i]->get_sort()))
SET_ERROR_CODE(Z3_INVALID_ARG, "failed to match argument of polymorphic function");
domain.push_back(arg_list[i]->get_sort());
if (_d->get_arity() != num_args &&
!_d->is_left_associative() && !_d->is_right_associative() && !_d->is_chainable()) {
SET_ERROR_CODE(Z3_INVALID_ARG, "invalid function application, wrong number of arguments");
return nullptr;
}
polymorphism::substitution sub(m);
auto match = [&](unsigned domain_idx, unsigned arg_idx) {
if (!sub.match(_d->get_domain(domain_idx), arg_list[arg_idx]->get_sort())) {
SET_ERROR_CODE(Z3_INVALID_ARG, "failed to match argument of polymorphic function");
return false;
}
return true;
};
for (unsigned i = 0; i < num_args; ++i) {
unsigned domain_idx = i;
if (_d->is_associative())
domain_idx = 0;
else if (_d->is_right_associative())
domain_idx = i + 1 == num_args && num_args > 1 ? 1 : 0;
else if (_d->is_left_associative())
domain_idx = i == 0 ? 0 : 1;
else if (_d->is_chainable()) {
if ((i > 0 && !match(1, i)) || (i + 1 < num_args && !match(0, i)))
return nullptr;
continue;
}
if (!match(domain_idx, i))
return nullptr;
}
sort_ref_buffer domain(m);
for (unsigned i = 0; i < _d->get_arity(); ++i)
domain.push_back(sub(_d->get_domain(i)));
sort_ref range = sub(_d->get_range());
_d = m.instantiate_polymorphic(_d, num_args, domain.data(), range);
_d = m.instantiate_polymorphic(_d, _d->get_arity(), domain.data(), range);
}
app* a = m.mk_app(_d, num_args, arg_list.data());
mk_c(c)->save_ast_trail(a);