--- id: "bug-1848" status: "fixed" status_updated: "2026-09-24" status_note: "" component: "scheduler" severity: "leak" timestamp: "2026-09-24T05:30:00Z" files: ["src/scheduler/src/schedobj.c", "core_events.c", "snapshot_load.c (hlimits zarray_init)"] tags: ["scheduler", "limit", "hlimits", "memory-leak"] related: [] legacy: true gitea_issue: 291 gitea_synced_status: "fixed" --- # bug-1848: Leak: xce_sched_obj_t.hlimits was created with a NULL zarray item killer, so reset_sched_obj()… ## Description Leak: xce_sched_obj_t.hlimits was created with a NULL zarray item killer, so reset_sched_obj() (limit_job_election()'s config-version rebuild) and schedobj teardown leaked the per-worker xlist_t that match_job_host_limit_() callocs, one per worker a job had tried. Visible as part of the large LeakSanitizer report on every --sim replay run. ## Environment _Not recorded (migrated from buglog.json)._ ## Reproduction _Not recorded (migrated from buglog.json)._ ## Root cause zarray_fini() only frees items through the zarray's killer; all three schedobj constructors passed NULL. ## Fix FIXED (2026-09-24, TDD). New kill_hlimits_list() (schedobj.c, declared in sched/types.h) frees one per-worker list (xlist_fini + free; never its vres entries, owned by the limit plugin's instance table); installed at all three zarray_init(&(obj->hlimits), ...) sites -- schedobj.c uses kill_hlimits_list(); core_events.c and snapshot_load.c get identical static local copies (core_hlimits_list_killer / snapshot_hlimits_list_killer) because both deliberately don't share helpers with schedobj.c and several test binaries (sched_core_events_test, sched_core_full_cycle_test, sched_snapshot_save_load_test, sched_sim_startup_test) link them without schedobj.c -- a shared symbol broke those links. zarray_fini() preserves the killer, so reset_sched_obj() leaves the zarray reusable. Test: test_limit_host_dedup.c's test_hlimits_zarray_releases_its_lists (RED: killer NULL; GREEN). A direct __lsan_do_recoverable_leak_check() test was tried and rejected -- the fixture's own resreq parsing (parse_select strdup) leaks independently, so the baseline is never leak-free. ## Verification _Not recorded (migrated from buglog.json)._ ## References - Gitea: #291