FA-43936 / Borrow checking / Open access
A field borrow gets its own allocation identity instead of its base owner · case 01
A field borrow gets its own allocation identity instead of its base owner.
ROOT CAUSE
The static analyzer mishandles subobject owner: a field borrow gets its own allocation identity instead of its base owner.
VERIFIED REPAIR
Apply the specified transfer or inference rule at this site: if d['base_owner']!=d['subobject_owner']: errors.append('subobject-owner').
Unsuccessful approach: The partial repair uses if d['base_owner'] is not None and d['subobject_owner'] is None: errors.append('subobject-owner'), which still violates the stipulated analysis contract.
Case contract
Check statically tracked reference provenance in a toy allocation IR. An address is not an allocation identity; allocation epoch must match; offset within object or one-past only for nondereferenced pointer; raw-to-reference conversion needs a live origin; integer roundtrip cannot invent provenance; reallocation invalidates old origins; zero-sized allocations still have separate ownership IDs; union reinterpretation needs a declared active variant; stack origins cannot become heap origins by cast; a subobject reference retains the base owner. Input is a fully explicit descriptor of the stated toy IR. Return rule identifiers in declaration order; absent optional obligations use the provided neutral defaults. No rule is an assertion about a production language.
Why this case matters
A finite offline static-analysis model of ownership and borrowing; it does not execute the analyzed program.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(d):
errors=[]
if d['address_equal'] and not d['allocation_equal'] and d['alias_inferred']: errors.append('address-not-owner')
if d['epoch']!=d['current_epoch']: errors.append('epoch-validity')
if d['offset']<0 or d['offset']>d['size'] or (d['dereference'] and d['offset']==d['size']): errors.append('one-past-dereference')
if d['raw_reference'] and not d['origin_live']: errors.append('raw-live-origin')
if d['integer_only'] and d['provenance_granted']: errors.append('integer-provenance')
if d['reallocated'] and d['old_origin_used']: errors.append('reallocation-invalidation')
if not set(d['zero_size_owners'])<=set(d['merged_owners']): errors.append('zst-identity')
if d['union_read'] and d['variant']!=d['active_variant']: errors.append('union-variant-witness')
if d['stack_origin'] and d['heap_cast']: errors.append('storage-class-cast')
if False: errors.append('subobject-owner')
return errors
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
base={'address_equal': False, 'allocation_equal': True, 'alias_inferred': False, 'epoch': 0, 'current_epoch': 0, 'offset': 0, 'size': 0, 'dereference': False, 'raw_reference': False, 'origin_live': True, 'integer_only': False, 'provenance_granted': False, 'reallocated': False, 'old_origin_used': False, 'zero_size_owners': [], 'merged_owners': [], 'variant': None, 'active_variant': None, 'union_read': False, 'stack_origin': False, 'heap_cast': False, 'base_owner': None, 'subobject_owner': None}
check('well formed empty obligations',solve(base),[])
check('address-not-owner regression 0', solve(dict(base, **({'address_equal':True,'allocation_equal':False,'alias_inferred':True,'size':N}))), ['address-not-owner'])
check('address-not-owner regression 1', solve(dict(base, **({'address_equal':True,'allocation_equal':False,'alias_inferred':True,'size':N+1}))), ['address-not-owner'])
check('epoch-validity regression 0', solve(dict(base, **({'epoch':N,'current_epoch':N+1}))), ['epoch-validity'])
check('epoch-validity regression 1', solve(dict(base, **({'epoch':N+1,'current_epoch':N+2}))), ['epoch-validity'])
check('one-past-dereference regression 0', solve(dict(base, **({'offset':N,'size':N,'dereference':True}))), ['one-past-dereference'])
check('one-past-dereference regression 1', solve(dict(base, **({'offset':N+1,'size':N+1,'dereference':True}))), ['one-past-dereference'])
check('raw-live-origin regression 0', solve(dict(base, **({'raw_reference':True,'origin_live':False}))), ['raw-live-origin'])
check('raw-live-origin regression 1', solve(dict(base, **({'raw_reference':True,'origin_live':False,'size':N}))), ['raw-live-origin'])
check('integer-provenance regression 0', solve(dict(base, **({'integer_only':True,'provenance_granted':True}))), ['integer-provenance'])
check('integer-provenance regression 1', solve(dict(base, **({'integer_only':True,'provenance_granted':True,'size':N}))), ['integer-provenance'])
check('reallocation-invalidation regression 0', solve(dict(base, **({'reallocated':True,'old_origin_used':True,'address_equal':True}))), ['reallocation-invalidation'])
check('reallocation-invalidation regression 1', solve(dict(base, **({'reallocated':True,'old_origin_used':True,'address_equal':True,'size':N}))), ['reallocation-invalidation'])
check('zst-identity regression 0', solve(dict(base, **({'zero_size_owners':['a','b'],'merged_owners':['a']}))), ['zst-identity'])
check('zst-identity regression 1', solve(dict(base, **({'zero_size_owners':[N,N+1],'merged_owners':[N]}))), ['zst-identity'])
check('union-variant-witness regression 0', solve(dict(base, **({'union_read':True,'variant':'b','active_variant':'a'}))), ['union-variant-witness'])
check('union-variant-witness regression 1', solve(dict(base, **({'union_read':True,'variant':N,'active_variant':N+1}))), ['union-variant-witness'])
check('storage-class-cast regression 0', solve(dict(base, **({'stack_origin':True,'heap_cast':True}))), ['storage-class-cast'])
check('storage-class-cast regression 1', solve(dict(base, **({'stack_origin':True,'heap_cast':True,'size':N}))), ['storage-class-cast'])
check('subobject-owner regression 0', solve(dict(base, **({'base_owner':'r','subobject_owner':'field'}))), ['subobject-owner'])
check('subobject-owner regression 1', solve(dict(base, **({'base_owner':N,'subobject_owner':N+1}))), ['subobject-owner'])
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| well formed empty obligations | [] | [] | Passed |
| address-not-owner regression 0 | ['address-not-owner'] | ['address-not-owner'] | Passed |
| address-not-owner regression 1 | ['address-not-owner'] | ['address-not-owner'] | Passed |
| epoch-validity regression 0 | ['epoch-validity'] | ['epoch-validity'] | Passed |
| epoch-validity regression 1 | ['epoch-validity'] | ['epoch-validity'] | Passed |
| one-past-dereference regression 0 | ['one-past-dereference'] | ['one-past-dereference'] | Passed |
| one-past-dereference regression 1 | ['one-past-dereference'] | ['one-past-dereference'] | Passed |
| raw-live-origin regression 0 | ['raw-live-origin'] | ['raw-live-origin'] | Passed |
| raw-live-origin regression 1 | ['raw-live-origin'] | ['raw-live-origin'] | Passed |
| integer-provenance regression 0 | ['integer-provenance'] | ['integer-provenance'] | Passed |
| integer-provenance regression 1 | ['integer-provenance'] | ['integer-provenance'] | Passed |
| reallocation-invalidation regression 0 | ['reallocation-invalidation'] | ['reallocation-invalidation'] | Passed |
| reallocation-invalidation regression 1 | ['reallocation-invalidation'] | ['reallocation-invalidation'] | Passed |
| zst-identity regression 0 | ['zst-identity'] | ['zst-identity'] | Passed |
| zst-identity regression 1 | ['zst-identity'] | ['zst-identity'] | Passed |
| union-variant-witness regression 0 | ['union-variant-witness'] | ['union-variant-witness'] | Passed |
| union-variant-witness regression 1 | ['union-variant-witness'] | ['union-variant-witness'] | Passed |
| storage-class-cast regression 0 | ['storage-class-cast'] | ['storage-class-cast'] | Passed |
| storage-class-cast regression 1 | ['storage-class-cast'] | ['storage-class-cast'] | Passed |
| subobject-owner regression 0 | [] | ['subobject-owner'] | Failed |
| subobject-owner regression 1 | [] | ['subobject-owner'] | Failed |
SHA-256 / ad3c4afe83e8cc8a229131b3ca5822c59fbaf3a0df209245274398b89a8e001d
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(d):
errors=[]
if d['address_equal'] and not d['allocation_equal'] and d['alias_inferred']: errors.append('address-not-owner')
if d['epoch']!=d['current_epoch']: errors.append('epoch-validity')
if d['offset']<0 or d['offset']>d['size'] or (d['dereference'] and d['offset']==d['size']): errors.append('one-past-dereference')
if d['raw_reference'] and not d['origin_live']: errors.append('raw-live-origin')
if d['integer_only'] and d['provenance_granted']: errors.append('integer-provenance')
if d['reallocated'] and d['old_origin_used']: errors.append('reallocation-invalidation')
if not set(d['zero_size_owners'])<=set(d['merged_owners']): errors.append('zst-identity')
if d['union_read'] and d['variant']!=d['active_variant']: errors.append('union-variant-witness')
if d['stack_origin'] and d['heap_cast']: errors.append('storage-class-cast')
if d['base_owner'] is not None and d['subobject_owner'] is None: errors.append('subobject-owner')
return errors
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
base={'address_equal': False, 'allocation_equal': True, 'alias_inferred': False, 'epoch': 0, 'current_epoch': 0, 'offset': 0, 'size': 0, 'dereference': False, 'raw_reference': False, 'origin_live': True, 'integer_only': False, 'provenance_granted': False, 'reallocated': False, 'old_origin_used': False, 'zero_size_owners': [], 'merged_owners': [], 'variant': None, 'active_variant': None, 'union_read': False, 'stack_origin': False, 'heap_cast': False, 'base_owner': None, 'subobject_owner': None}
check('well formed empty obligations',solve(base),[])
check('address-not-owner regression 0', solve(dict(base, **({'address_equal':True,'allocation_equal':False,'alias_inferred':True,'size':N}))), ['address-not-owner'])
check('address-not-owner regression 1', solve(dict(base, **({'address_equal':True,'allocation_equal':False,'alias_inferred':True,'size':N+1}))), ['address-not-owner'])
check('epoch-validity regression 0', solve(dict(base, **({'epoch':N,'current_epoch':N+1}))), ['epoch-validity'])
check('epoch-validity regression 1', solve(dict(base, **({'epoch':N+1,'current_epoch':N+2}))), ['epoch-validity'])
check('one-past-dereference regression 0', solve(dict(base, **({'offset':N,'size':N,'dereference':True}))), ['one-past-dereference'])
check('one-past-dereference regression 1', solve(dict(base, **({'offset':N+1,'size':N+1,'dereference':True}))), ['one-past-dereference'])
check('raw-live-origin regression 0', solve(dict(base, **({'raw_reference':True,'origin_live':False}))), ['raw-live-origin'])
check('raw-live-origin regression 1', solve(dict(base, **({'raw_reference':True,'origin_live':False,'size':N}))), ['raw-live-origin'])
check('integer-provenance regression 0', solve(dict(base, **({'integer_only':True,'provenance_granted':True}))), ['integer-provenance'])
check('integer-provenance regression 1', solve(dict(base, **({'integer_only':True,'provenance_granted':True,'size':N}))), ['integer-provenance'])
check('reallocation-invalidation regression 0', solve(dict(base, **({'reallocated':True,'old_origin_used':True,'address_equal':True}))), ['reallocation-invalidation'])
check('reallocation-invalidation regression 1', solve(dict(base, **({'reallocated':True,'old_origin_used':True,'address_equal':True,'size':N}))), ['reallocation-invalidation'])
check('zst-identity regression 0', solve(dict(base, **({'zero_size_owners':['a','b'],'merged_owners':['a']}))), ['zst-identity'])
check('zst-identity regression 1', solve(dict(base, **({'zero_size_owners':[N,N+1],'merged_owners':[N]}))), ['zst-identity'])
check('union-variant-witness regression 0', solve(dict(base, **({'union_read':True,'variant':'b','active_variant':'a'}))), ['union-variant-witness'])
check('union-variant-witness regression 1', solve(dict(base, **({'union_read':True,'variant':N,'active_variant':N+1}))), ['union-variant-witness'])
check('storage-class-cast regression 0', solve(dict(base, **({'stack_origin':True,'heap_cast':True}))), ['storage-class-cast'])
check('storage-class-cast regression 1', solve(dict(base, **({'stack_origin':True,'heap_cast':True,'size':N}))), ['storage-class-cast'])
check('subobject-owner regression 0', solve(dict(base, **({'base_owner':'r','subobject_owner':'field'}))), ['subobject-owner'])
check('subobject-owner regression 1', solve(dict(base, **({'base_owner':N,'subobject_owner':N+1}))), ['subobject-owner'])
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| well formed empty obligations | [] | [] | Passed |
| address-not-owner regression 0 | ['address-not-owner'] | ['address-not-owner'] | Passed |
| address-not-owner regression 1 | ['address-not-owner'] | ['address-not-owner'] | Passed |
| epoch-validity regression 0 | ['epoch-validity'] | ['epoch-validity'] | Passed |
| epoch-validity regression 1 | ['epoch-validity'] | ['epoch-validity'] | Passed |
| one-past-dereference regression 0 | ['one-past-dereference'] | ['one-past-dereference'] | Passed |
| one-past-dereference regression 1 | ['one-past-dereference'] | ['one-past-dereference'] | Passed |
| raw-live-origin regression 0 | ['raw-live-origin'] | ['raw-live-origin'] | Passed |
| raw-live-origin regression 1 | ['raw-live-origin'] | ['raw-live-origin'] | Passed |
| integer-provenance regression 0 | ['integer-provenance'] | ['integer-provenance'] | Passed |
| integer-provenance regression 1 | ['integer-provenance'] | ['integer-provenance'] | Passed |
| reallocation-invalidation regression 0 | ['reallocation-invalidation'] | ['reallocation-invalidation'] | Passed |
| reallocation-invalidation regression 1 | ['reallocation-invalidation'] | ['reallocation-invalidation'] | Passed |
| zst-identity regression 0 | ['zst-identity'] | ['zst-identity'] | Passed |
| zst-identity regression 1 | ['zst-identity'] | ['zst-identity'] | Passed |
| union-variant-witness regression 0 | ['union-variant-witness'] | ['union-variant-witness'] | Passed |
| union-variant-witness regression 1 | ['union-variant-witness'] | ['union-variant-witness'] | Passed |
| storage-class-cast regression 0 | ['storage-class-cast'] | ['storage-class-cast'] | Passed |
| storage-class-cast regression 1 | ['storage-class-cast'] | ['storage-class-cast'] | Passed |
| subobject-owner regression 0 | [] | ['subobject-owner'] | Failed |
| subobject-owner regression 1 | [] | ['subobject-owner'] | Failed |
SHA-256 / 61be0edec2ec64c85b78d035ee42916300917e328ca0b03821c575dea0f6f487
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(d):
errors=[]
if d['address_equal'] and not d['allocation_equal'] and d['alias_inferred']: errors.append('address-not-owner')
if d['epoch']!=d['current_epoch']: errors.append('epoch-validity')
if d['offset']<0 or d['offset']>d['size'] or (d['dereference'] and d['offset']==d['size']): errors.append('one-past-dereference')
if d['raw_reference'] and not d['origin_live']: errors.append('raw-live-origin')
if d['integer_only'] and d['provenance_granted']: errors.append('integer-provenance')
if d['reallocated'] and d['old_origin_used']: errors.append('reallocation-invalidation')
if not set(d['zero_size_owners'])<=set(d['merged_owners']): errors.append('zst-identity')
if d['union_read'] and d['variant']!=d['active_variant']: errors.append('union-variant-witness')
if d['stack_origin'] and d['heap_cast']: errors.append('storage-class-cast')
if d['base_owner']!=d['subobject_owner']: errors.append('subobject-owner')
return errors
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
base={'address_equal': False, 'allocation_equal': True, 'alias_inferred': False, 'epoch': 0, 'current_epoch': 0, 'offset': 0, 'size': 0, 'dereference': False, 'raw_reference': False, 'origin_live': True, 'integer_only': False, 'provenance_granted': False, 'reallocated': False, 'old_origin_used': False, 'zero_size_owners': [], 'merged_owners': [], 'variant': None, 'active_variant': None, 'union_read': False, 'stack_origin': False, 'heap_cast': False, 'base_owner': None, 'subobject_owner': None}
check('well formed empty obligations',solve(base),[])
check('address-not-owner regression 0', solve(dict(base, **({'address_equal':True,'allocation_equal':False,'alias_inferred':True,'size':N}))), ['address-not-owner'])
check('address-not-owner regression 1', solve(dict(base, **({'address_equal':True,'allocation_equal':False,'alias_inferred':True,'size':N+1}))), ['address-not-owner'])
check('epoch-validity regression 0', solve(dict(base, **({'epoch':N,'current_epoch':N+1}))), ['epoch-validity'])
check('epoch-validity regression 1', solve(dict(base, **({'epoch':N+1,'current_epoch':N+2}))), ['epoch-validity'])
check('one-past-dereference regression 0', solve(dict(base, **({'offset':N,'size':N,'dereference':True}))), ['one-past-dereference'])
check('one-past-dereference regression 1', solve(dict(base, **({'offset':N+1,'size':N+1,'dereference':True}))), ['one-past-dereference'])
check('raw-live-origin regression 0', solve(dict(base, **({'raw_reference':True,'origin_live':False}))), ['raw-live-origin'])
check('raw-live-origin regression 1', solve(dict(base, **({'raw_reference':True,'origin_live':False,'size':N}))), ['raw-live-origin'])
check('integer-provenance regression 0', solve(dict(base, **({'integer_only':True,'provenance_granted':True}))), ['integer-provenance'])
check('integer-provenance regression 1', solve(dict(base, **({'integer_only':True,'provenance_granted':True,'size':N}))), ['integer-provenance'])
check('reallocation-invalidation regression 0', solve(dict(base, **({'reallocated':True,'old_origin_used':True,'address_equal':True}))), ['reallocation-invalidation'])
check('reallocation-invalidation regression 1', solve(dict(base, **({'reallocated':True,'old_origin_used':True,'address_equal':True,'size':N}))), ['reallocation-invalidation'])
check('zst-identity regression 0', solve(dict(base, **({'zero_size_owners':['a','b'],'merged_owners':['a']}))), ['zst-identity'])
check('zst-identity regression 1', solve(dict(base, **({'zero_size_owners':[N,N+1],'merged_owners':[N]}))), ['zst-identity'])
check('union-variant-witness regression 0', solve(dict(base, **({'union_read':True,'variant':'b','active_variant':'a'}))), ['union-variant-witness'])
check('union-variant-witness regression 1', solve(dict(base, **({'union_read':True,'variant':N,'active_variant':N+1}))), ['union-variant-witness'])
check('storage-class-cast regression 0', solve(dict(base, **({'stack_origin':True,'heap_cast':True}))), ['storage-class-cast'])
check('storage-class-cast regression 1', solve(dict(base, **({'stack_origin':True,'heap_cast':True,'size':N}))), ['storage-class-cast'])
check('subobject-owner regression 0', solve(dict(base, **({'base_owner':'r','subobject_owner':'field'}))), ['subobject-owner'])
check('subobject-owner regression 1', solve(dict(base, **({'base_owner':N,'subobject_owner':N+1}))), ['subobject-owner'])
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| well formed empty obligations | [] | [] | Passed |
| address-not-owner regression 0 | ['address-not-owner'] | ['address-not-owner'] | Passed |
| address-not-owner regression 1 | ['address-not-owner'] | ['address-not-owner'] | Passed |
| epoch-validity regression 0 | ['epoch-validity'] | ['epoch-validity'] | Passed |
| epoch-validity regression 1 | ['epoch-validity'] | ['epoch-validity'] | Passed |
| one-past-dereference regression 0 | ['one-past-dereference'] | ['one-past-dereference'] | Passed |
| one-past-dereference regression 1 | ['one-past-dereference'] | ['one-past-dereference'] | Passed |
| raw-live-origin regression 0 | ['raw-live-origin'] | ['raw-live-origin'] | Passed |
| raw-live-origin regression 1 | ['raw-live-origin'] | ['raw-live-origin'] | Passed |
| integer-provenance regression 0 | ['integer-provenance'] | ['integer-provenance'] | Passed |
| integer-provenance regression 1 | ['integer-provenance'] | ['integer-provenance'] | Passed |
| reallocation-invalidation regression 0 | ['reallocation-invalidation'] | ['reallocation-invalidation'] | Passed |
| reallocation-invalidation regression 1 | ['reallocation-invalidation'] | ['reallocation-invalidation'] | Passed |
| zst-identity regression 0 | ['zst-identity'] | ['zst-identity'] | Passed |
| zst-identity regression 1 | ['zst-identity'] | ['zst-identity'] | Passed |
| union-variant-witness regression 0 | ['union-variant-witness'] | ['union-variant-witness'] | Passed |
| union-variant-witness regression 1 | ['union-variant-witness'] | ['union-variant-witness'] | Passed |
| storage-class-cast regression 0 | ['storage-class-cast'] | ['storage-class-cast'] | Passed |
| storage-class-cast regression 1 | ['storage-class-cast'] | ['storage-class-cast'] | Passed |
| subobject-owner regression 0 | ['subobject-owner'] | ['subobject-owner'] | Passed |
| subobject-owner regression 1 | ['subobject-owner'] | ['subobject-owner'] | Passed |
SHA-256 / 70c14674c67aa671c2f3eb0fe660769463f0dcf27e43c5ae65ae60959416e9ff
Verification & scope
The explicitly stated toy language is the complete scope; this is not a production compiler or a claim about Rust semantics. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:44:07.133133+00:00.
Case digest / d8264285531d26442209380b235c9ec0ea8147c89158ff9cf819842568dc9ebe