Skip to content

uninitialized check#8545

Draft
kroening wants to merge 4 commits intodevelopfrom
uninitialized-check
Draft

uninitialized check#8545
kroening wants to merge 4 commits intodevelopfrom
uninitialized-check

Conversation

@kroening
Copy link
Copy Markdown
Collaborator

Uninitialized local or dynamically allocated variables have an indeterminate initial value.

Reading an indeterminate value may be undefined behavior, or may yield an unspecific value.

This adds a check for uninitialized local variables. The check is not added as a standard check.

  • 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).
  • n/a My commit message includes data points confirming performance improvements (if claimed).
  • My PR is restricted to a single feature or bugfix.
  • n/a White-space or formatting changes outside the feature-related changed lines are in commits of their own.

@kroening kroening force-pushed the uninitialized-check branch 2 times, most recently from 7c56ed4 to a8069ba Compare December 26, 2024 10:26
@codecov
Copy link
Copy Markdown

codecov bot commented Dec 26, 2024

Codecov Report

Attention: Patch coverage is 38.70968% with 19 lines in your changes missing coverage. Please review.

Project coverage is 78.27%. Comparing base (f9a7807) to head (a8069ba).

Files with missing lines Patch % Lines
src/ansi-c/goto-conversion/goto_check_c.cpp 38.70% 19 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #8545      +/-   ##
===========================================
- Coverage    78.38%   78.27%   -0.12%     
===========================================
  Files         1729     1729              
  Lines       200151   200453     +302     
  Branches     18244    18316      +72     
===========================================
+ Hits        156883   156895      +12     
- Misses       43268    43558     +290     

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

@rod-chapman
Copy link
Copy Markdown
Collaborator

Good to see this PR coming. What is your soundness argument? Does this check always detect all cases of uninitialized variables? Are there cases where it is known to miss? What are they?

tautschnig and others added 4 commits April 10, 2026 14:20
Replace bare uninitialized local variable declarations with explicit
initialization in ~175 regression test files. These tests use
uninitialized locals as a shorthand for nondeterministic values
(e.g., int x; __CPROVER_assume(x > 0);). This is valid C but
triggers --uninitialized-check when enabled globally.

Prepares for --uninitialized-check becoming a default check.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
28 test cases covering scalar locals, struct/union/array members,
nested structs, per-element array tracking, symbolic indices,
heap allocation, pointer aliasing, goto across initialization,
and various control flow patterns.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Shadow boolean tracking for detecting reads of uninitialized local
variables (C11 §6.3.2.1p2 undefined behavior). The standard scopes
UB to objects that "could have been declared with the register
storage class (never had its address taken)" — address-taken locals
yield merely indeterminate values, not UB.

Design: for each tracked local variable, a shadow boolean $init is
added to the symbol table. The first pass inserts DECL + init=false
after each tracked variable's DECL. The assignment handler sets
init=true on writes, and check_rec asserts init==true on reads.

Features:
- Per-member tracking for struct components (recursive, arbitrary depth)
- Per-element tracking for constant-size arrays (up to 64 elements)
- Symbolic array index handling (conjunction of all element flags)
- Heap allocation tracking via pointer init flags
- Heap aliasing via shared flag symbols
- Union/byte_extract write handling
- Function call return value tracking
- DECL+ASSIGN optimization (skip variables initialized at declaration)
- Skip address-taken (dirty) variables (not UB per C11 §6.3.2.1p2)
- Skip compiler-generated temporaries

Unifies goto-instrument --uninitialized-check to use the same
goto_check_c implementation, removing the old uninitializedt.

Includes documentation in properties.md and man pages.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Extend uninitialized variable checking to also cover address-taken
(dirty) local variables using CBMC's shadow memory subsystem.

While reading uninitialized dirty locals is NOT undefined behavior
per C11 §6.3.2.1p2 (the value is merely indeterminate), it often
indicates a bug. This check is therefore separate from the C UB
check (--uninitialized-check).

The GOTO transformation pass (uninitialized_precise.cpp):
1. Adds shadow memory built-in symbols and empty function bodies
2. Inserts __CPROVER_field_decl_local in __CPROVER_initialize
3. After each write (direct or through pointer), inserts set_field
4. Before each read of a tracked dirty local, inserts get_field+assert
5. Skips address_of subtrees (taking address is not a read)

Symex resolves pointer aliasing and interprocedural writes precisely
via the value set, giving full precision at 0-20% overhead.

Includes documentation in properties.md and man pages, comparison
test suite, and regression tests for dirty variable patterns.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
@tautschnig tautschnig force-pushed the uninitialized-check branch from 09908d0 to 3961467 Compare April 10, 2026 14:23
| `--undefined-shift-check` | add range checks for shift distances |
| `--nan-check` | add floating-point NaN checks |
| `--uninitialized-check` | add checks for uninitialized locals (experimental) |
| `--uninitialized-check` | add checks for reads of uninitialized locals (C11 UB) |
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it sound like uninitalized local reads have been UB only since C11 -- my understanding is that this was UB since C90.

- Heap allocations (via pointer init flags)

**What is not checked (by design, per C11 §6.3.2.1p2):**
- Variables whose address has been taken ("dirty" variables) — reading
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should say "local variables". There is talk about heap allocated variables above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants