fix(lib): heap buffer overflow in make_job_account() for multi-host jobs (bug-1722)

make_job_account() allocates account->accounts via calloc(1,
sizeof(xce_host_account_t*)) -- room for exactly 1 host pointer -- but
its own loop writes account->accounts[i] for i up to jalloc->nhosts-1,
overflowing the heap allocation for any job spanning more than one
host. Already reachable from live production dispatch (core_events.c,
schedobj.c, scheduler.c) for any multi-host job -- found while
verifying viability of a proposed scheduler snapshot-allocation-capture
fix, not from a user report.

Sibling function make_host_allocs() has the identical bug shape
(calloc(1, ...) vs a loop writing up to nhosts-1); every current call
site happens to pass nhosts=1 so it has never actually overflowed in
production, but it's the same live footgun in a public exported
helper. Fixed alongside since it's the identical bug class in the same
file.

New test_xcealloc.c drives a 2-host allocation through both functions,
reproducing the exact ASAN heap-buffer-overflow at xcealloc.c:188
pre-fix (RED), passing post-fix (GREEN). Both calloc(1, ...) sites
changed to size the allocation by the actual host count.

Full regression: libxce 303/303 (was 301, +2 new), scheduler 53/54 (1
pre-existing unrelated bug-1560 flake), controller 1211/1213 (2
pre-existing unrelated bug-1561 overlayfs flakes) -- zero new
failures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
This commit is contained in:
Rongsong Shen 2026-08-27 08:43:33 +00:00
parent 5e68fcc0d8
commit 002075030b
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
4 changed files with 103 additions and 3 deletions

View file

@ -131,7 +131,7 @@ xce_host_alloc_t** make_host_allocs(int nhosts, ...)
return NULL;
}
hallocs = (xce_host_alloc_t**)calloc(1, sizeof(*hallocs));
hallocs = (xce_host_alloc_t**)calloc((size_t)nhosts, sizeof(*hallocs));
if (hallocs == NULL) {
return NULL;
}
@ -177,8 +177,8 @@ xce_job_account_t* make_job_account(xce_job_alloc_t* jalloc)
return NULL;
}
account->accounts
= (xce_host_account_t**)calloc(1, sizeof(xce_host_account_t*));
account->accounts = (xce_host_account_t**)calloc(
(size_t)jalloc->nhosts, sizeof(xce_host_account_t*));
if (account->accounts == NULL) {
FREEUP(account);
return NULL;