Skip to content

acd: uninitialised read of bestPerm in enumerate_iset_combinations - #539

Open
marcelwa wants to merge 1 commit into
berkeley-abc:masterfrom
marcelwa:acd-uninit-bestperm
Open

acd: uninitialised read of bestPerm in enumerate_iset_combinations#539
marcelwa wants to merge 1 commit into
berkeley-abc:masterfrom
marcelwa:acd-uninit-bestperm

Conversation

@marcelwa

@marcelwa marcelwa commented Aug 8, 2026

Copy link
Copy Markdown

The defect

In src/map/if/acd/ac_decomposition.hpp, enumerate_iset_combinations declares

uint32_t pComb[16], pInvPerm[16], bestPerm[16];

and writes bestPerm only inside the cost < best_cost branch:

if ( cost < best_cost )
{
  ...
  for ( uint32_t i = 0; i < num_vars; ++i )
    bestPerm[i] = pComb[i];
  ...
}

When no combination beats the initial best_cost — which happens for an infeasible free-set size — bestPerm is never written. The tail of the function nevertheless evaluates it:

for ( uint32_t i = 0; i < num_vars; ++i )
  res_perm[i] = permutations[bestPerm[i]];

That is a read of uninitialised stack, and because the value is then used as an index into permutations[], it is an out-of-bounds read as well.

Impact

Benign in upstream today: the caller discards that permutation on the infeasible path, so results are unaffected. But it is undefined behaviour, and it becomes a reliable segfault the moment the stack layout shifts — adding two members to the decomposer object was enough to reproduce it consistently while instrumenting the pass.

The fix

Seed the identity permutation in the initialisation loop that already runs immediately above. Five lines, no behavioural change on any path that was previously well-defined, and no measurable cost.

Testing

Built clean, and if -K 10 -Z 6 still maps correctly (cavlc: nd = 121, lev = 4). Found while measuring ACD's column-multiplicity headroom across the EPFL suite; the pass was exercised over 180+ mapping jobs on this branch with no change in output.

…ombinations

bestPerm is only written inside the 'cost < best_cost' branch. When no
combination beats the initial best_cost -- which happens for an infeasible
free-set size -- the array is never written, yet the tail of the function still
evaluates permutations[bestPerm[i]]. That reads uninitialised stack and then
uses the value to index permutations[], so it is an out-of-bounds read as well.

Upstream results are unaffected in practice because the caller discards the
permutation on that path, but it is undefined behaviour and it becomes a hard
segfault as soon as the stack layout changes -- adding two members to the
decomposer object was enough to trigger it reliably.

Seeding the identity permutation in the existing initialisation loop is
sufficient and costs nothing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant