Skip to content

C front-end: rewrite array size when refined from unknown to defined - #9163

Open
Th0rOnDoR wants to merge 1 commit into
diffblue:developfrom
Th0rOnDoR:develop
Open

Th0rOnDoR wants to merge 1 commit into
diffblue:developfrom
Th0rOnDoR:develop

Conversation

@Th0rOnDoR

@Th0rOnDoR Th0rOnDoR commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

cbmc crashes with an invariant violation in boolbv_mapt::get_literals ("number of literals in the literal map shall equal the bitvector width") on programs like:

extern unsigned long pdx_pfn_lookup[];

unsigned long pdx_to_pfn(unsigned long pdx, unsigned long i)
{
  return pdx + pdx_pfn_lookup[...];   // typechecked before size is known
}

unsigned long pdx_pfn_lookup[64];      // size refined here

The array is first declared with an unknown size (array[nil]), indexed in a function body, and later defined with a concrete size. The C front-end typechecks the function body before the sized definition, so the symbol expressions in that body keep the stale size-less type array[nil] (copied at typecheck time in typecheck_expr_symbol). The symbol table is later refined to array[64], but code bodies are not rewritten, leaving the same object typed both as a size-less and as a sized array.

In the solver, the zero-init equality is converted with the concrete type array[64] (4096 bits) while the index expression is converted with array[nil] (treated as unbounded : width 0). Since boolbv_mapt is keyed by identifier only, the second lookup finds the 4096-literal entry and 4096 != 0 trips the invariant.
The existing adjust_tentative_array_definitions fix (Fixes #7608) only handles arrays that are still size-less at end of TU (rewritten to size 1); it does not cover the size-less : concrete refinement.

In typecheck_redefinition_non_type (src/ansi-c/c_typecheck_base.cpp), at the point the array size is refined from unknown to a concrete value, rewrite the symbol expressions referring to that symbol across the symbol table so they carry the concrete array type. This uses unchecked_replace_symbolt, the same mechanism as adjust_tentative_array_definitions, keeping the symbol's type and the types carried on its uses consistent.

Tests:

  • Added regression test regression/cbmc/Extern_Array_Size_Refinement/ (extern unsized array indexed before its sized definition). It crashes (invariant violation) without the fix and passes with it.

  • Each commit message has a non-empty body, explaining why the change was made.

  • Methods or procedures I have added are documented, following the guidelines provided in CODING_STANDARD.md.

  • The feature or user visible behaviour I have added or modified has been documented in the User Guide in doc/cprover-manual/

  • Regression or unit tests are included, or existing tests cover the modified code (in this case I have detailed which ones those are in the commit message).

  • My commit message includes data points confirming performance improvements (if claimed).

  • My PR is restricted to a single feature or bugfix.

  • White-space or formatting changes outside the feature-related changed lines are in commits of their own.

@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.83%. Comparing base (820ff0f) to head (19a3112).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #9163   +/-   ##
========================================
  Coverage    80.83%   80.83%           
========================================
  Files         1717     1717           
  Lines       190069   190077    +8     
  Branches        73       73           
========================================
+ Hits        153647   153655    +8     
  Misses       36422    36422           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

cbmc crashes with an invariant violation in boolbv_mapt::get_literals when
an array is first defined as extern with an unknown size and later refined with
a defined size

The array is first declared with an unknown size (array[nil]), indexed in a
function body, and later defined with a concrete size. The C front-end
typechecks the function body before the sized definition, so the symbol
expressions in that body keep the stale size-less type array[nil]
(copied at typecheck time in typecheck_expr_symbol). The symbol table is later
refined to array[64], but code bodies are not rewritten — leaving the same
object typed both as a size-less and as a sized array.

In the solver, the zero-init equality is converted with the concrete type
array[64] (4096 bits) while the index expression is converted with array[nil]
(treated as unbounded : width 0). Since boolbv_mapt is keyed by identifier only,
the second lookup finds the 4096-literal entry and 4096 != 0 trips the invariant.

The existing adjust_tentative_array_definitions fix (Fixes diffblue#7608) only handles
arrays that are still size-less at end of TU (rewritten to size 1); it does not
cover the size-less to concrete refinement.

Signed-off-by: Th0rOnDoR <thomas.courrege@thorondor.fr>
@Th0rOnDoR Th0rOnDoR changed the title C front-end: rewrite array size is refined from unknown to defined C front-end: rewrite array size when refined from unknown to defined Sep 11, 2026
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.

SSA conversion crash caused by extern array declaration

1 participant