You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SMACK's memory-safety model needs a storage-aware overhaul. A run over 3,000 public SV-COMP 2026 Juliet valid-memsafety tasks exposed three related gaps:
stack objects do not become invalid when their lifetime ends;
$free does not distinguish heap allocations from stack objects; and
valid-memtrack and valid-memcleanup are implemented as the same allocation-counter check, although they have different semantics.
This is an umbrella issue for fixing those problems together and adding regression coverage for the individual SV-COMP memory-safety subproperties.
Related issues:
smack cannot find this type of memory error #539 reports an escaped stack pointer that remains dereferenceable after its function returns. The maintainer discussion already suggests invalidating alloca objects at function returns.
Missing memory safety bugs related to globals #190 required memory-safety checks for globals, including rejecting free on a global object. Its historical implementation distinguishes globals by their address range, but it does not provide general allocation provenance.
The experiment used the public SV-COMP 2026 Juliet_Test tasks with valid-memsafety.prp. Property parsing enabled every subproperty requested by the property file. The following counts include only definitive property results.
Wrong violated property: 514 tasks
423 tasks expected false(valid-free), but SMACK reported false(valid-memtrack).
91 tasks expected false(valid-deref), but SMACK reported false(valid-memtrack).
The direct valid-free or valid-deref check does not recognize the intended error. A later allocation-counter failure is reported instead.
valid-memtrack false alarms: 24 tasks
Twenty-four tasks expected true for valid-memsafety, but SMACK reported false(valid-memtrack). They are CWE401 flow variants 45 and 68 in which the allocation remains reachable through a global pointer at program exit.
These programs violate valid-memcleanup because allocated memory remains at exit, but they satisfy valid-memtrack because the allocation is still reachable. SMACK currently treats both properties as the same condition.
Root causes
1. No heap-allocation provenance
In share/smack/lib/smack.c, stack allocation and heap allocation ultimately use the same allocation state. $Alloc records whether an address is allocated, but not what kind of storage it denotes.
$free checks that the pointer is a live base address, but it has no explicit "allocated by malloc/calloc/realloc" condition. A stack object can therefore satisfy the current checks. The model then decrements $allocatedCounter, even though that counter is incremented only for heap allocation. This turns an invalid free into a later valid-memtrack violation.
The model should track allocation provenance independently of liveness, for example with a heap-allocation map or an equivalent storage-kind abstraction. $free(p) must require a live heap base and must not mutate allocation/leak state after an invalid free.
2. No stack-lifetime invalidation
An alloca object remains marked allocated after its function returns, so bounds and $Alloc checks accept a later dereference. This is the behavior reported in #539.
Each function activation's stack objects should be invalidated on every normal and exceptional exit. llvm.lifetime.end can provide a more precise earlier endpoint when present, but correctness cannot rely exclusively on that intrinsic because frontends do not always emit it.
The design also needs tests for recursion, multiple return sites, escaped interior pointers, and use-after-return through globals and parameters.
3. valid-memtrack is not valid-memcleanup
The SV-COMP adapter maps both valid-memtrack and valid-memcleanup to the same internal MEMLEAK check. The prelude implements that check as:
assert{:valid_memtrack} $allocatedCounter ==0;
This is a cleanup condition: every nonzero-size heap allocation must have been deallocated at exit. It is not a memory-tracking condition, which permits live allocated objects that remain reachable.
These properties need distinct internal flags and implementations:
valid-memcleanup: no outstanding heap allocations at the relevant program exit;
valid-memtrack: no allocated object has become unreachable.
Implementing valid-memtrack requires a conservative reachability model rooted in live program pointers. If SMACK cannot model that soundly yet, it should report the property as unsupported/unknown instead of silently substituting valid-memcleanup.
Proposed work
Represent allocation provenance: heap, stack activation, global, and invalid/freed.
Require a live heap base in $free; reject stack, global, interior, and already-freed pointers as valid-free violations.
Keep invalid frees from corrupting allocation-counter state or producing a secondary property alarm.
Invalidate stack allocations on every function exit, using lifetime intrinsics for additional precision when available.
Give valid-memtrack and valid-memcleanup separate internal properties and result labels.
Implement sound reachability for valid-memtrack, or explicitly return unknown until it is supported.
Ensure a counterexample is reported under the property actually violated (valid-free, valid-deref, valid-memtrack, or valid-memcleanup).
Add small C regressions for each case above.
Add a curated public Juliet regression subset covering the affected families and both good/bad variants.
Acceptance criteria
free of stack or global storage reports false(valid-free) directly.
dereferencing an escaped pointer after its stack object's lifetime ends reports false(valid-deref).
an allocated object retained through a live global pointer satisfies valid-memtrack but violates valid-memcleanup.
a genuinely lost heap object violates valid-memtrack.
Summary
SMACK's memory-safety model needs a storage-aware overhaul. A run over 3,000 public SV-COMP 2026 Juliet
valid-memsafetytasks exposed three related gaps:$freedoes not distinguish heap allocations from stack objects; andvalid-memtrackandvalid-memcleanupare implemented as the same allocation-counter check, although they have different semantics.This is an umbrella issue for fixing those problems together and adding regression coverage for the individual SV-COMP memory-safety subproperties.
Related issues:
allocaobjects at function returns.freeon a global object. Its historical implementation distinguishes globals by their address range, but it does not provide general allocation provenance.callocmemory-safety modeling problem and should remain compatible with this work.Public benchmark evidence
The experiment used the public SV-COMP 2026
Juliet_Testtasks withvalid-memsafety.prp. Property parsing enabled every subproperty requested by the property file. The following counts include only definitive property results.Wrong violated property: 514 tasks
false(valid-free), but SMACK reportedfalse(valid-memtrack).false(valid-deref), but SMACK reportedfalse(valid-memtrack).Affected public benchmark families include:
CWE590_Free_Memory_Not_on_HeapCWE401_Invalid_FreeCWE590_Deref_Out_Of_Scope_Memory_Not_on_HeapCWE590_Use_Stack_Memory_Out_Of_ScopeRepresentative tasks:
CWE590_Free_Memory_Not_on_Heap__free_char_alloca_01_badCWE590_Free_Memory_Not_on_Heap__free_int_declare_01_badCWE590_Deref_Out_Of_Scope_Memory_Not_on_Heap__deref_char_declare_01_badCWE590_Use_Stack_Memory_Out_Of_Scope__deref_int64_t_declare_01_badThe direct
valid-freeorvalid-derefcheck does not recognize the intended error. A later allocation-counter failure is reported instead.valid-memtrackfalse alarms: 24 tasksTwenty-four tasks expected
trueforvalid-memsafety, but SMACK reportedfalse(valid-memtrack). They are CWE401 flow variants 45 and 68 in which the allocation remains reachable through a global pointer at program exit.Representative tasks:
CWE401_Memory_Leak__int_malloc_45_badCWE401_Memory_Leak__int64_t_calloc_68_badCWE401_Memory_Leak__struct_twoIntsStruct_realloc_45_badThese programs violate
valid-memcleanupbecause allocated memory remains at exit, but they satisfyvalid-memtrackbecause the allocation is still reachable. SMACK currently treats both properties as the same condition.Root causes
1. No heap-allocation provenance
In
share/smack/lib/smack.c, stack allocation and heap allocation ultimately use the same allocation state.$Allocrecords whether an address is allocated, but not what kind of storage it denotes.$freechecks that the pointer is a live base address, but it has no explicit "allocated by malloc/calloc/realloc" condition. A stack object can therefore satisfy the current checks. The model then decrements$allocatedCounter, even though that counter is incremented only for heap allocation. This turns an invalid free into a latervalid-memtrackviolation.A minimal expected regression is:
The model should track allocation provenance independently of liveness, for example with a heap-allocation map or an equivalent storage-kind abstraction.
$free(p)must require a live heap base and must not mutate allocation/leak state after an invalid free.2. No stack-lifetime invalidation
An
allocaobject remains marked allocated after its function returns, so bounds and$Allocchecks accept a later dereference. This is the behavior reported in #539.A minimal expected regression is:
Each function activation's stack objects should be invalidated on every normal and exceptional exit.
llvm.lifetime.endcan provide a more precise earlier endpoint when present, but correctness cannot rely exclusively on that intrinsic because frontends do not always emit it.The design also needs tests for recursion, multiple return sites, escaped interior pointers, and use-after-return through globals and parameters.
3.
valid-memtrackis notvalid-memcleanupThe SV-COMP adapter maps both
valid-memtrackandvalid-memcleanupto the same internalMEMLEAKcheck. The prelude implements that check as:This is a cleanup condition: every nonzero-size heap allocation must have been deallocated at exit. It is not a memory-tracking condition, which permits live allocated objects that remain reachable.
These properties need distinct internal flags and implementations:
valid-memcleanup: no outstanding heap allocations at the relevant program exit;valid-memtrack: no allocated object has become unreachable.Implementing
valid-memtrackrequires a conservative reachability model rooted in live program pointers. If SMACK cannot model that soundly yet, it should report the property as unsupported/unknown instead of silently substitutingvalid-memcleanup.Proposed work
$free; reject stack, global, interior, and already-freed pointers asvalid-freeviolations.valid-memtrackandvalid-memcleanupseparate internal properties and result labels.valid-memtrack, or explicitly return unknown until it is supported.valid-free,valid-deref,valid-memtrack, orvalid-memcleanup).Acceptance criteria
freeof stack or global storage reportsfalse(valid-free)directly.false(valid-deref).valid-memtrackbut violatesvalid-memcleanup.valid-memtrack.