--- id: "bug-1504" status: "fixed" status_updated: "2026-09-24" status_note: "" component: "lib" severity: "crash" timestamp: "2026-08-07T08:00:00Z" last_seen: "2026-09-19T09:00:00Z" occurrences: 1 files: ["src/schema/controller.capnp"] tags: ["scheduler-core", "task-12.1", "capnp-schema", "codegen-limitation", "sigsegv", "uninitialized-memory", "controller.capnp", "snapshot"] related: ["bug-1493", "bug-1505"] legacy: true --- # bug-1504: sched_sim_startup_test SIGSEGV in write_SnapshotData (lib/controller.capnp.c) -- deterministic… ## Description sched_sim_startup_test SIGSEGV in write_SnapshotData (lib/controller.capnp.c) -- deterministic crash inside libCapnC_Runtime's read_ptr/capn_setp, discovered during scheduler-core task 12.1 review remediation (full clean rebuild needed for a libedit dependency swap). ## Environment _Not recorded (migrated from buglog.json)._ ## Reproduction _Not recorded (migrated from buglog.json)._ ## Root cause SnapshotData.internalState was declared `:Data` in the capnp schema. This codebase's capnpc-c backend silently no-ops Data-field encoding inside generated encode_XXX() composite helpers (documented, deliberate limitation -- see controller.capnp's own prior comment citing ReplayHeader.configHash). But encode_SnapshotData_ptr()'s local `struct SnapshotData d;` is never zero-initialized before the (partially no-op) encode_SnapshotData() populates it, so the skipped Data field's capn_ptr is left as UNINITIALIZED STACK GARBAGE. write_SnapshotData() then unconditionally capn_setp()'s that garbage onto the wire. This is genuine UB: whether it crashes depends entirely on what garbage bytes land in that stack slot at each call site -- ReplayHeader.configHash has the identical gap but never crashed (lucky garbage); SnapshotData.internalState's stack layout in this one test's call chain was unlucky. A prior 'rm -rf xce.build' full rebuild (needed to pick up a new nix dependency) perturbed the whole binary's layout enough to flip this from dormant to a deterministic, reproducible SIGSEGV. ## Fix Changed SnapshotData.internalState from `Data` to `List(UInt8)` in controller.capnp, with a new companion internalStateLen (@11, UInt32) wire field for the maplistcount -- following the exact working precedent of FetchFileReply.content/bytesRead, which this same capnpc-c backend DOES correctly auto-generate real (non-no-op) encode/decode for (capn_new_list8/capn_set8 loop, not a placeholder comment). Updated typemap.h's sched_snapshot_data_t.internal_state_len from size_t to uint32_t to match the wire type, and updated test_e2e_capnp.c's test_snapshot_data_round_trip (which previously worked around the Data no-op with a manual low-level SnapshotData_get/set_internalState accessor) to rely on the now-working ordinary codec bridge instead. No hand-written low-level accessor code needed any more for this field. ## Verification _Not recorded (migrated from buglog.json)._ ## References - Related: bug-1493, bug-1505