fix(lib,scheduler): zarray_get rejects negative indexes; info resource bounds by capacity (bug-1824)

zarray_get_ex() checked only idx >= caps, so a negative index read
array->data[idx] out of bounds -- reachable through a resource whose
registration failed and kept idx == -1 (xcelimit.c add_vres()). It now
returns NULL for idx < 0.

repl_info_resource_one() bounded a shared resource's slot by
sharedInst.count / sharedInUse.count, the number of populated slots, not
the capacity: a populated slot above that count showed as value=(none).
It now relies on zarray_get()'s own bounds check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-09-30 03:34:57 +00:00
parent fd61721bab
commit 16b7aa4b60
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
4 changed files with 90 additions and 11 deletions

View file

@ -1858,6 +1858,68 @@ test_info_resource_load_metric_whole_number_omits_trailing_zeros(void)
"zeros: PASS\n");
}
/*
* bug-1824: info resource bounded a shared resource's slot by
* sharedInst.count -- the number of POPULATED slots, not the array's
* capacity -- so a populated slot at an index >= .count (a sparsely filled
* array) was reported as "value=(none)". Same shape as bug-1688's
* stale-count test for info worker.
*/
static void test_info_resource_shows_value_when_slot_is_above_count(void)
{
xce_sched_sim_driver_t sim;
xce_core_event_t events[1];
xce_resource_t wire_load;
xce_resource_t* r_load;
xce_val_t val_load;
xce_resval_t rv_load;
xce_resval_t* rv_ptrs[1];
char* captured;
int rc;
memset(&sim, 0, sizeof(sim));
rc = xce_sched_core_init(&(sim.core));
TEST_ASSERT_EQUAL_INT(0, rc);
memset(&wire_load, 0, sizeof(wire_load));
wire_load.name = "cluster_load1m";
wire_load.valtype = XCE_VAL_NUMERIC;
wire_load.flags = XCE_RESF_SHARED;
r_load = makeResource(sim.core.resmgr, &wire_load);
TEST_ASSERT_NOT_NULL(r_load);
memset(&val_load, 0, sizeof(val_load));
val_load.which = Value_u_fvalue;
val_load.u.fvalue = 5.0;
memset(&rv_load, 0, sizeof(rv_load));
rv_load.name = "cluster_load1m";
rv_load.val = &val_load;
rv_ptrs[0] = &rv_load;
memset(events, 0, sizeof(events));
events[0].type = XCE_CORE_EVENT_RESVALS_FULL;
events[0].u.resvals_full.n = 1;
events[0].u.resvals_full.resvals = rv_ptrs;
rc = xce_core_push_events(&(sim.core), events, 1);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_NOT_NULL(zarray_get(&(sim.core.sharedInst), r_load->idx));
/* The slot stays populated; only .count under-reports it. */
sim.core.sharedInst.count = r_load->idx;
captured = capture_dispatch(&sim, "info resource cluster_load1m");
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(captured, "value=5\n"),
"bug-1824: a populated shared slot at an index >= sharedInst.count "
"must still display its value");
free(captured);
printf("test_info_resource_shows_value_when_slot_is_above_count: "
"PASS\n");
}
/*
* AC 6.2 'both' / Req 6.3 default, `info resource <name>`: same
* units_mode-aware bytes rendering as info worker's own version above,
@ -2360,6 +2422,7 @@ int main(void)
RUN_TEST(test_info_worker_assigned_job_listing_falls_back_per_job_when_slots_differ);
RUN_TEST(test_info_resource_groups_resources_into_labeled_table_sections_in_order);
RUN_TEST(test_info_resource_load_metric_whole_number_omits_trailing_zeros);
RUN_TEST(test_info_resource_shows_value_when_slot_is_above_count);
RUN_TEST(test_info_resource_units_mode_both_renders_raw_and_human_for_bytes_resource);
RUN_TEST(test_info_resource_units_mode_both_renders_raw_and_human_for_consumable_bytes_resource);