From 8134b75b10abfa835ed41ffe0c16467d9d242bde Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Tue, 29 Sep 2026 09:00:13 +0000 Subject: [PATCH] fix(scheduler): the core owns and frees its config; the simulator frees its start snapshot (bug-1884, part 3) xce_sched_core_load_snapshot() keeps the snapshot's config as core->config and copies everything else, so the simulator now moves the config into the core and frees the rest of the decoded SnapshotEntry. xce_sched_core_fini() frees core->config last, after the plugins and group managers that may point into it; nothing freed it in either mode. Exit-time leak on the reproduction replay 25,807 -> 11,426 bytes. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/scheduler/src/core.c | 6 ++++++ src/scheduler/src/sim.c | 19 ++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/scheduler/src/core.c b/src/scheduler/src/core.c index 6d888c98..b86b3017 100644 --- a/src/scheduler/src/core.c +++ b/src/scheduler/src/core.c @@ -3,6 +3,7 @@ #include #include +#include "controller.capnp.h" #include "core.h" #include "decision.h" #include "group.h" @@ -253,6 +254,11 @@ void xce_sched_core_fini(xce_sched_core_t* core) resreq_fini(); + /* bug-1884: the core owns the config it was last given (a CONFIG_PUSH, + * sched_init_config() or a snapshot load); freed last, after the + * plugins and group managers that may point into it. */ + free_Conf4Sched_ptr(&(core->config)); + mylock_fini(&(core->lock)); return; diff --git a/src/scheduler/src/sim.c b/src/scheduler/src/sim.c index af0a7c63..ff8bfda0 100644 --- a/src/scheduler/src/sim.c +++ b/src/scheduler/src/sim.c @@ -281,17 +281,14 @@ static int sim_locate_and_load_snapshot(xce_sched_sim_driver_t* sim, return rc; } - /* `best` (specifically best.data->config) is NOT freed on success: - * xce_sched_core_load_snapshot() aliases `core->config = in->config` - * (a borrowed-reference assignment, not a deep copy -- core.h/ - * snapshot_load.c's own documented "config -- borrowed reference" - * choice) -- core->config now points at the SAME memory as - * best.data->config. Freeing `best` here would immediately dangle - * `sim->core.config`. Same "no free-snapshot function, deliberate - * accepted leak of the non-aliased remainder" precedent - * recording.c's write_snapshot_entry() already documents for the - * identical save-side ownership shape (detect_leaks=0 across this - * whole test suite, by design). */ + /* xce_sched_core_load_snapshot() keeps best.data->config as + * core->config (a borrowed reference, snapshot_load.c) and copies + * everything else. bug-1884: hand the config to the core, which frees + * it in xce_sched_core_fini(), and free the rest of the entry. */ + if (best.data != NULL) { + best.data->config = NULL; + } + free_SnapshotEntry(&best); /* Read from `best_ts` (captured while the message's own capn arena was * still live), never by re-reading `sp` here -- the capn_free(&c) call