docs(buglog): bug-1925 fixed (fcca4c9c)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fcca4c9cfa
commit
4d8d819201
2 changed files with 146 additions and 0 deletions
|
|
@ -17481,6 +17481,34 @@
|
|||
"record": ".wolf/bugs/bug-1924.md",
|
||||
"gitea_issue": 349,
|
||||
"gitea_synced_status": "fixed"
|
||||
},
|
||||
{
|
||||
"id": "bug-1925",
|
||||
"status": "fixed",
|
||||
"status_updated": "2026-10-08",
|
||||
"status_note": "",
|
||||
"component": "scheduler",
|
||||
"severity": "crash",
|
||||
"timestamp": "2026-10-08T15:35:55Z",
|
||||
"last_seen": "2026-10-08T15:35:55Z",
|
||||
"occurrences": 1,
|
||||
"error_message": "xce_scheduler --sim REPL quit mid-session frees trace settings the allocator thread may still read\n\n**Observed:** found by code reading, not by a crash. `repl_cmd_quit()`\n(src/scheduler/src/repl.c) ran `repl_trace_shutdown(sim)` first, which stores\n`sim->core.trace_ctl = NULL` and unrefs/frees the cur/next trace settings\n(including the `xce_sched_trace_ctl_t` they wrap), while the picker and\nallocator threads were still running. At a breakpoint mid-session the\nallocator keeps popping jobs, and `sched_allocator()`\n(src/scheduler/src/sched_election.c) reads the ctl unlocked on every pop:\n\n```c\nif (core->trace_ctl != NULL) {\n core->trace_ctl->position++;\n if (sched_trace_ctl_wants(core->trace_ctl, jobid)) {\n core->trace_rec = sched_trace_rec_new(core->trace_ctl, jobid, ...);\n```\n\nA pointer read before the NULL store can be dereferenced (and written, via\n`position++`) after the free. The new deterministic test shows the ordering:\n\n```\ntest_repl_trace_wiring.c:588:test_quit_stops_threads_before_trace_shutdown:FAIL: trace settings released while threads were live\ntest_repl_trace_wiring.c:588:test_eof_stops_threads_before_trace_shutdown:FAIL: trace settings released while threads were live\n```\n\n**Expected:** `quit` (and stdin EOF, which takes the same path) must not\nrelease anything the scheduler threads can still reach; the threads are\nstopped before the trace settings are freed.\n**Impact:** heap use-after-free (read and write) on the allocator thread in\nthe window just before `exit(0)` after `quit`/EOF at a mid-session REPL\npause with tracing on. Simulator REPL only; live mode never publishes\n`trace_ctl`. Worst case an ASAN abort or heap corruption at exit; no effect\non recorded output, which is flushed by then. Window is a few instructions,\nso it is rare in practice.\n**Found by:** review of scheduler-sim-decision-trace task 5.3 (code reading)",
|
||||
"file": "src/scheduler/src/repl.c, src/scheduler/src/repl_trace.c, src/scheduler/src/sched_election.c",
|
||||
"root_cause": "`repl_cmd_quit()` (repl.c), reached by the `quit` command and by a NULL\n`readline()` (EOF) in `repl_command_loop()`, called `repl_trace_shutdown()`\n(repl_trace.c) while the session's picker and allocator threads were live.\nThe REPL pauses on the driver thread at breakpoints in the middle of a\nsession's decision drain, and the allocator keeps working on the following\njobs, reading `core->trace_ctl` without a lock (by design, the ctl is\npublished only between sessions and the driver normally stops the threads\non return via `sched_sim_driver_run()` -> `sched_sim_driver_stop_threads()`).\n`quit` calls `exit(0)` from inside the run, so that stop never happened\nbefore the free. Since: `quit` started calling `repl_trace_shutdown` in\n6bac1681 (task 5.2); the race became reachable when 5.3 (a1cb9cba) began\npublishing `trace_ctl` from the sim session hooks. Scope: `repl_cmd_quit`\nis the only path that ends the process from inside a running sim session;\nthe other `repl_trace_shutdown` caller is test fixture code. The other frees\nin quit (closing record/replay/alt-events files) are of driver-thread-only\nstate and were not affected.",
|
||||
"fix": "`repl_cmd_quit()` (repl.c) now calls `sched_sim_driver_stop_threads(sim)`\nbefore `repl_trace_shutdown(sim)`. That cancels and joins the picker and\nallocator, so no thread can hold or load `trace_ctl` when it is freed.\nChecked: EOF goes through `repl_cmd_quit` too; stop_threads is a no-op when\n`allocator_thread_live` is false (quit at the pre-replay pause, before the\nthreads start); joining cannot hang at a REPL pause because both threads\nblock only in `cbuf_get`/`cbuf_put`, whose `pthread_cond_wait` is a\ncancellation point with an unlock cleanup handler (including the allocator\nparked on the full decisions buffer), and the driver thread holds no lock\nthe allocator needs while in the REPL; nothing after quit uses the threads\n(quit only closes files, then `exit(0)`). Rejected: locking or atomically\nswapping `trace_ctl` in the allocator, which would add a cost to the live\nhot path the design keeps at one pointer test (Req 11.2) for a problem that\nonly exists at process exit.\n\nVerification:\n- Test: `test_quit_stops_threads_before_trace_shutdown` and `test_eof_stops_threads_before_trace_shutdown` in src/scheduler/testsuite/test_repl_trace_wiring.c (link-time wrap added in src/scheduler/testsuite/meson.build); RED before the fix (\"trace settings released while threads were live\"), GREEN after (17 Tests 0 Failures)\n- Suites: `bash src/tools/xce-build.sh scheduler --jobs 12 --incremental`, scheduler suite 114/114 OK, asan\n- Live: post-fix, the same `trace on all / break DECISION / continue / quit` script against contention.replay, 200 runs: 0 ASAN reports, 0 non-zero exits, 0 hangs (timeout 20 s); plus quit after two breakpoint hits with a trace file (exit 0, file written), quit at the pre-replay pause with no threads (exit 0), and EOF at a breakpoint (exit 0)",
|
||||
"tags": [
|
||||
"sim",
|
||||
"repl",
|
||||
"trace",
|
||||
"use-after-free",
|
||||
"race",
|
||||
"scheduler-sim-decision-trace"
|
||||
],
|
||||
"related_bugs": [],
|
||||
"fix_commits": [
|
||||
"fcca4c9c"
|
||||
],
|
||||
"record": ".wolf/bugs/bug-1925.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue