Validate FlatZinc constraint arity before indexing arguments (heap OOB read)#5228
Validate FlatZinc constraint arity before indexing arguments (heap OOB read)#5228evilgensec wants to merge 3 commits into
Conversation
The FlatZinc front end accepts a constraint declaration with any number of arguments; the parser performs no arity check. Both the solution checker (CheckSolution) and the CP-SAT extractor (FillConstraint) then dispatch on the constraint type and read fixed positions of constraint.arguments, assuming a well-formed model always supplies the right count. A hand-crafted .fzn that declares a known constraint with too few arguments causes an out-of-bounds read past the heap-allocated std::vector<fz::Argument> (the attacker chooses the type and therefore which fixed offset is read past the end, and deeper handlers dereference the out-of-bounds Argument's pointer/list members). Add NumRequiredArguments(type), a table of the minimum argument count each constraint type needs (derived from the fixed positions read by the checker and extractor handlers), and reject constraints with fewer arguments before dispatching in both consumers.
|
This is just a tad over-engineered. This being said, creating the fzn file should be done by the minizinc compiler, so they should not generate invalid fzn files |
Replace the central NumRequiredArguments table and the dispatch-level checks with a direct argument-count guard at the start of each checker method, as suggested in review. Each Check* function now verifies it has enough arguments before indexing, so a malformed FlatZinc constraint with too few arguments is treated as unsatisfied instead of reading out of bounds on its argument vector. Removes the model.h/model.cc NumRequiredArguments table and the CheckSolution / FillConstraint dispatch guards. Net reduction in code.
|
Thanks, simplified. Each Agreed a correct MiniZinc compiler should not emit invalid One open point: the CP-SAT extractor ( |
|
Please a DCHECK_EQ() in the FillConstraint code. It should have been caught
before
Laurent Perron | Operations Research | ***@***.*** | (33) 1 42 68 53
00
Le jeu. 18 juin 2026 à 12:01, evilgensec ***@***.***> a
écrit :
… *evilgensec* left a comment (google/or-tools#5228)
<#5228 (comment)>
Thanks, simplified. Each Check* method now verifies its own argument
count at the start and returns unsatisfied if a constraint has too few
arguments, so there is no central table to keep in sync. Dropped the
NumRequiredArguments table and the CheckSolution dispatch check; net
reduction in code.
Agreed a correct MiniZinc compiler should not emit invalid .fzn. The
guard is defense-in-depth for the standalone case: the solver/checker can
be pointed at any .fzn (hand-written, third-party, or from another tool),
where today a too-short constraint is an out-of-bounds read rather than a
clean rejection.
One open point: the CP-SAT extractor (FillConstraint and the per-type
methods) also indexes arguments directly, and I dropped its guard along
with the table. If you would prefer the loader to fail gracefully on a
malformed .fzn, I can add the same per-method checks there; otherwise I
will leave it relying on the parser. Which do you prefer?
—
Reply to this email directly, view it on GitHub
<#5228?email_source=notifications&email_token=ACUPL3M3QKFA6ABF32BXSHL5AO4VXA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTINZUGA3TQNBQHA4KM4TFMFZW63VHMNXW23LFNZ2KKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-4740784088>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACUPL3MOMBMTOGOT4CIEMT35AO4VXAVCNFSNUABEKJSXA33TNF2G64TZHMZTCMJQGIYDSMR3JFZXG5LFHM2DMNZZGUYTANJZGGQXMAQ>
.
Triage notifications, keep track of coding agent tasks and review pull
requests on the go with GitHub Mobile for iOS
<https://github.com/notifications/mobile/ios/ACUPL3NG4JZUBZ5PWHBXXI35AO4VXA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTINZUGA3TQNBQHA4KM4TFMFZW63VHMNXW23LFNZ2KKZLWMVXHJKTGN5XXIZLSL5UW64Y>
and Android
<https://github.com/notifications/mobile/android/ACUPL3O3JTXJNQDW6SIG54L5AO4VXA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTINZUGA3TQNBQHA4KM4TFMFZW63VHMNXW23LFNZ2KKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI>.
Download it today!
You are receiving this because you commented.Message ID:
***@***.***>
|
Per review, assert the argument-count invariant at the start of each extractor method before it indexes fz_ct.arguments. A malformed FlatZinc constraint with too few arguments should have been rejected earlier, so this is a debug-build invariant (DCHECK) rather than runtime handling.
|
Done. Added a |
Problem
The FlatZinc front end accepts a
constraint <name>(<args>)declaration with any numberof arguments; the parser performs no arity check (
parser.yystores the argument vectorverbatim). Both consumers that dispatch on the constraint name then read fixed positions of
constraint.arguments:CpModelProtoWithMapping::FillConstraint(cp_model_fz_solver.cc),invoked for every constraint on every solve, e.g.
BoolNeConstraintreadsarguments[0]and
arguments[1],IntLinEqConstraintreadsarguments[2].values[0];CheckSolution(checker.cc), e.g.CheckBoolNotreadsarguments[1].Neither validates
arguments.size()first. A hand-crafted.fznthat declares a knownconstraint with too few arguments drives
arguments[k]past the end of the heap-allocatedstd::vector<fz::Argument>, an out-of-bounds read. The attacker chooses the constrainttype and therefore which fixed offset is read past the end; deeper handlers dereference the
out-of-bounds
Argument's pointer/list members..fznis untrusted input forfzn-ortools(constraint-solving services, CI, MiniZinc backends).Reproduced with AddressSanitizer through the real
ParseFlatzincString+CheckSolution:Fix
Add
NumRequiredArguments(absl::string_view type)(inmodel.{h,cc}): a table of theminimum number of arguments each constraint type needs, derived from the fixed argument
positions read by the checker and extractor handlers (the max literal
arguments[k]indexwell-formed model). Unknown types return 0 (no restriction). Both
CheckSolutionandFillConstraintreject a constraint with fewer arguments before dispatching.With this change the crafted model above no longer reads out of bounds: the checker reports
the constraint as unsatisfied (
CheckSolutionreturns false, ASAN clean), and theextractor errors out instead of indexing past the vector.
The arity table is generated from the current handler set; the FlatZinc regression suite in
CI exercises the well-formed paths to confirm no valid model is rejected.