FAILURE MAP
← Case archive

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.

Verified by executionVariant 1 · 7 checks per implementationDownload source bundle ↓JSON ↗

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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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