FA-251 / Runtime and resources / Open access
Dropping a borrowed alias destroys a still-owned object · case 01
A borrowed pointer either keeps an object alive or decrements an ownership count it never incremented.
ROOT CAUSE
Pointer existence is confused with owning-reference lifetime.
VERIFIED REPAIR
Only clones create ownership and only dropping an owning handle decrements the reference count.
Unsuccessful approach: Avoiding increments for borrows while decrementing all dropped handles causes premature destruction.
Case contract
The object starts with owning handle root. Events create distinct clone or borrow names, or drop an existing handle once. Cloning occurs only while a live owner exists. Borrowed handles do not extend lifetime and are not dereferenced here. Return [owning reference count,is alive,destruction transitions].
Why this case matters
Models ownership versus borrowed aliases in manual or native resource wrappers, including dangling borrow records after the last owner disappears, without executing unsafe memory access.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(events):
handles, references, destroyed = {'root': True}, 1, 0
for kind, identity in events:
if kind == 'clone':
handles[identity] = True
references += 1
elif kind == 'borrow':
handles[identity] = False
references += 1
else:
owned = handles.pop(identity)
if True:
references -= 1
if references == 0:
destroyed += 1
return [references, references > 0, destroyed]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
borrows = [['borrow', 'b'+str(i)] for i in range(N)]
drops = [['drop', 'b'+str(i)] for i in range(N)]
check('borrows do not own', solve(borrows), [1, True, 0])
check('dropping borrows preserves owner', solve(borrows+drops), [1, True, 0])
check('last owner can die before borrow record', solve(borrows+[['drop', 'root']]), [0, False, 1])
check('clone keeps object alive', solve([['clone', 'second'], ['drop', 'root']]), [1, True, 0])
check('last cloned owner frees once', solve([['clone', 'second'], ['drop', 'root'], ['drop', 'second']]), [0, False, 1])
check('borrow after extra owner then drops', solve([['clone', 'second'], ['borrow', 'view'], ['drop', 'view'], ['drop', 'root']]), [1, True, 0])
check('no lifetime changes', solve([]), [1, True, 0])
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 |
|---|---|---|---|
| borrows do not own | [2, True, 0] | [1, True, 0] | Failed |
| dropping borrows preserves owner | [1, True, 0] | [1, True, 0] | Passed |
| last owner can die before borrow record | [1, True, 0] | [0, False, 1] | Failed |
| clone keeps object alive | [1, True, 0] | [1, True, 0] | Passed |
| last cloned owner frees once | [0, False, 1] | [0, False, 1] | Passed |
| borrow after extra owner then drops | [1, True, 0] | [1, True, 0] | Passed |
| no lifetime changes | [1, True, 0] | [1, True, 0] | Passed |
SHA-256 / 18f3338e462657d2a010067b567ac23208ece0f5a5a92f11f5148fa79ee09ef0
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(events):
handles, references, destroyed = {'root': True}, 1, 0
for kind, identity in events:
if kind == 'clone':
handles[identity] = True
references += 1
elif kind == 'borrow':
handles[identity] = False
pass
else:
owned = handles.pop(identity)
if True:
references -= 1
if references == 0:
destroyed += 1
return [references, references > 0, destroyed]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
borrows = [['borrow', 'b'+str(i)] for i in range(N)]
drops = [['drop', 'b'+str(i)] for i in range(N)]
check('borrows do not own', solve(borrows), [1, True, 0])
check('dropping borrows preserves owner', solve(borrows+drops), [1, True, 0])
check('last owner can die before borrow record', solve(borrows+[['drop', 'root']]), [0, False, 1])
check('clone keeps object alive', solve([['clone', 'second'], ['drop', 'root']]), [1, True, 0])
check('last cloned owner frees once', solve([['clone', 'second'], ['drop', 'root'], ['drop', 'second']]), [0, False, 1])
check('borrow after extra owner then drops', solve([['clone', 'second'], ['borrow', 'view'], ['drop', 'view'], ['drop', 'root']]), [1, True, 0])
check('no lifetime changes', solve([]), [1, True, 0])
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 |
|---|---|---|---|
| borrows do not own | [1, True, 0] | [1, True, 0] | Passed |
| dropping borrows preserves owner | [0, False, 1] | [1, True, 0] | Failed |
| last owner can die before borrow record | [0, False, 1] | [0, False, 1] | Passed |
| clone keeps object alive | [1, True, 0] | [1, True, 0] | Passed |
| last cloned owner frees once | [0, False, 1] | [0, False, 1] | Passed |
| borrow after extra owner then drops | [0, False, 1] | [1, True, 0] | Failed |
| no lifetime changes | [1, True, 0] | [1, True, 0] | Passed |
SHA-256 / 6b9a050cb8692e92e4f29c1bae5fbf445c9cb3093e8cf47550c6b75f2330eca7
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(events):
handles, references, destroyed = {'root': True}, 1, 0
for kind, identity in events:
if kind == 'clone':
handles[identity] = True
references += 1
elif kind == 'borrow':
handles[identity] = False
pass
else:
owned = handles.pop(identity)
if owned:
references -= 1
if references == 0:
destroyed += 1
return [references, references > 0, destroyed]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
borrows = [['borrow', 'b'+str(i)] for i in range(N)]
drops = [['drop', 'b'+str(i)] for i in range(N)]
check('borrows do not own', solve(borrows), [1, True, 0])
check('dropping borrows preserves owner', solve(borrows+drops), [1, True, 0])
check('last owner can die before borrow record', solve(borrows+[['drop', 'root']]), [0, False, 1])
check('clone keeps object alive', solve([['clone', 'second'], ['drop', 'root']]), [1, True, 0])
check('last cloned owner frees once', solve([['clone', 'second'], ['drop', 'root'], ['drop', 'second']]), [0, False, 1])
check('borrow after extra owner then drops', solve([['clone', 'second'], ['borrow', 'view'], ['drop', 'view'], ['drop', 'root']]), [1, True, 0])
check('no lifetime changes', solve([]), [1, True, 0])
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 |
|---|---|---|---|
| borrows do not own | [1, True, 0] | [1, True, 0] | Passed |
| dropping borrows preserves owner | [1, True, 0] | [1, True, 0] | Passed |
| last owner can die before borrow record | [0, False, 1] | [0, False, 1] | Passed |
| clone keeps object alive | [1, True, 0] | [1, True, 0] | Passed |
| last cloned owner frees once | [0, False, 1] | [0, False, 1] | Passed |
| borrow after extra owner then drops | [1, True, 0] | [1, True, 0] | Passed |
| no lifetime changes | [1, True, 0] | [1, True, 0] | Passed |
SHA-256 / 2991a55543e032aea5021dbc3225e168bd7eedef00916a273a4191730ded21ed
Verification & scope
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:36:51.533819+00:00.
Case digest / ac69e654fce8c48b7fc3ec42d2d3e0dc3bcb933bf8e2057cf256f3a2e52537b7