fix(scheduler): plugin_end_session() guards on the wrong field (bug-1682/1492)

/xce-audit-invariants scheduler (dimension 1, driver state-equivalence,
re-run after scheduler-sim-live-allocation) found plugin_end_session()
still checked `plugin->begin_session_fn != NULL` before invoking
`plugin->end_session_fn(plugin)` -- a copy-paste of
plugin_begin_session()'s own (correct) guard. Two failure modes: a
plugin registering only end_session_fn silently never has it invoked;
a plugin registering only begin_session_fn crashes via a NULL
function-pointer call. Currently inert in production (no live plugin
hits either shape) but a real latent defect in exactly the
session-boundary code the driver-equivalence invariant governs.

This exact defect had been independently filed twice: bug-1492
(2026-08-04, scheduler-core task 5.2's review) and bug-1682
(2026-08-24, found adjacent to bug-1681's fix) -- neither investigation
cross-referenced the other. Both entries also carried a "fixed" tag
inconsistent with their own "not yet fixed" narrative text, caught by
this audit's independent code+text cross-check.

Fixed the one-line guard; added test_end_session_invokes_end_session_fn_
when_only_it_is_set() (test_plugin_hook_dispatch.c) proving the silent
non-invocation, confirmed RED (84/85) against the buggy guard and GREEN
(85/85) after the fix. Updated both buglog entries to FIXED with
cross-references, and corrected bug-1611's stale narrative to reflect
its narrowing (not elimination) by scheduler-sim-live-allocation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
Rongsong Shen 2026-09-19 11:44:58 +00:00
parent 2f99406788
commit a643966f50
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
4 changed files with 63 additions and 11 deletions

View file

@ -438,7 +438,7 @@ int plugin_end_session(xce_sched_core_t* core)
break;
}
if (plugin->begin_session_fn != NULL) {
if (plugin->end_session_fn != NULL) {
plugin->end_session_fn(plugin);
}
}

View file

@ -282,6 +282,53 @@ static void test_plugin_register_sets_honest_core_reference(void)
TEST_ASSERT_EQUAL_PTR(&sched, plugin.sched);
}
/* ---------------------------------------------------------------------- */
/* bug-1682: plugin_end_session() guards its call with */
/* `plugin->begin_session_fn != NULL` instead of `plugin->end_session_fn != */
/* NULL` (plugin.c, a copy-paste of plugin_begin_session()'s own guard). */
/* Two failure modes this produces: (a) a plugin registering ONLY */
/* end_session_fn never has it invoked (silently skipped, this test); (b) a */
/* plugin registering ONLY begin_session_fn crashes via a NULL */
/* function-pointer call the first time a round ends (xce-audit-invariants, */
/* scheduler dimension 1, 2026-09-19 -- currently inert in production since */
/* no live plugin registers begin_session_fn without end_session_fn, but a */
/* latent defect in exactly the session-boundary code the driver- */
/* equivalence invariant governs). This test exercises failure mode (a), */
/* which is safely reproducible without crashing the test binary. */
/* ---------------------------------------------------------------------- */
static int g_end_session_calls;
static int mock_end_session_fn(void* arg)
{
(void)arg;
g_end_session_calls++;
return 0;
}
static void test_end_session_invokes_end_session_fn_when_only_it_is_set(void)
{
xce_sched_core_t core;
xce_sched_plugin_t plugin;
int rc;
core_fixture_init(&core);
make_mock_plugin(&plugin, "end_session_only");
/* Deliberately leave begin_session_fn NULL -- only end_session_fn is
* set, isolating plugin_end_session()'s own guard from
* plugin_begin_session()'s (already-correct) one. */
plugin.begin_session_fn = NULL;
plugin.end_session_fn = mock_end_session_fn;
rc = plugin_register_core(&core, &plugin);
TEST_ASSERT_TRUE(rc >= 0);
g_end_session_calls = 0;
rc = plugin_end_session(&core);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(1, g_end_session_calls);
}
int main(void)
{
UNITY_BEGIN();
@ -289,5 +336,6 @@ int main(void)
RUN_TEST(test_dispatch_fanout_simulation_mode_shaped_container);
RUN_TEST(test_plugin_register_core_sets_honest_core_reference);
RUN_TEST(test_plugin_register_sets_honest_core_reference);
RUN_TEST(test_end_session_invokes_end_session_fn_when_only_it_is_set);
return UNITY_END();
}