The disclosed "move-on-push" gap detonated on iteration 16's route-table pattern: `push(self.routes, r)` moved the Route into the container while the `take r` parameter's scope-end DROP still fired — the container's own drop plan (multi_free) then freed the element a second time. ASan: SEGV in class_free during trap unwind; latent until now because pushed elements were Texts, which copy at the boundary (2026-08-14). owner.ml analyze_call: `push`'s value slot and `set`'s key/value slots now TRANSFER an Owned, non-copy-stored place (record_move, exactly the take-arg shape), so the pusher's drop disappears. Text/json.Value keep the copy-store path (stores_by_copy) and the caller still drops the fresh copy. Traced (gc) values remain exempt (tracing owns them). A user-declared push/set fn of the same name wins, per the builtin shadowing rule. Pinned by tests/corpus/run/container-owned-move (route table: interface- typed field values pushed via take params, dispatched by ICALL, mixed with Text pushes) — the exact iteration-16 shape, ASan-clean. Verified: woc-test 540/0; oop-e2e 88/0; log-watcher 7/0; employee 8/0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
45 lines
1.1 KiB
Text
45 lines
1.1 KiB
Text
-- Storing an OWNED class value into a container is a MOVE (iteration 16's
|
|
-- route-table pattern found the disclosed "move-on-push" double-free: the
|
|
-- container's drop plan frees elements, so the pusher must not also drop
|
|
-- what it pushed). Texts stay copy-stored — the mixed push below pins both.
|
|
interface Handler {
|
|
fn handle(x: Int) -> Int
|
|
}
|
|
|
|
class AddOne {
|
|
n: Int
|
|
fn handle(x: Int) -> Int { return x + self.n }
|
|
}
|
|
|
|
class Route {
|
|
path: Text
|
|
h: Handler
|
|
}
|
|
|
|
class App {
|
|
routes: multi Route
|
|
names: multi Text
|
|
|
|
fn add(take r: Route, name: Text) {
|
|
push(self.routes, r);
|
|
push(self.names, name);
|
|
}
|
|
|
|
fn run(path: Text, x: Int) -> Int {
|
|
for r in self.routes {
|
|
if r.path == path { return r.h.handle(x); }
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
fn main(args: multi Text) -> Int {
|
|
let app = App { routes: [], names: [] };
|
|
app.add(Route { path: "/a", h: AddOne { n: 7 } }, "a");
|
|
app.add(Route { path: "/b", h: AddOne { n: 100 } }, "b");
|
|
print_int(app.run("/b", 1));
|
|
print_int(app.run("/a", 1));
|
|
print_int(app.run("/x", 1));
|
|
print_int(count(app.names));
|
|
return 0;
|
|
}
|