fix(lib,scheduler): allocation constructors and allocators report OOM instead of building NULL-name records (bug-1882)
make_alloc() and make_host_alloc() assigned strdup() results unchecked, so under memory exhaustion they returned a record whose resname/reserve/ host was NULL; merge_job_alloc()'s same_alloc_slot() (bug-1863) then strcmp()'d it (ASAN SEGV at xcealloc.c:337). make_alloc() also passed a NULL resname straight to strdup(). - xcealloc.c: make_alloc()/make_host_alloc() reject a NULL name (EINVAL) and check every strdup(), freeing what they built and returning NULL (ENOMEM); on failure make_host_alloc()'s caller keeps `allocs`. dup_host_alloc() frees its partial copy on failure instead of leaking. - scheduler.c sched_make_allocs(): a record that cannot be built fails the whole array (NULL, ENOMEM) instead of leaving a NULL entry. - seqjob/parjob my_alloc(): check every constructor; on failure free what was built (parjob also rolls back the remaining claims) and return -ENOMEM with ctx->alloc NULL -- the caller moves ctx.alloc onto the job whatever the rc. Tests: new xcealloc_oom_test (8, --wrap=strdup) and sched_make_allocs_oom_test (real scheduler_srcs + --wrap=strdup); a new -ENOMEM test in the seqjob and parjob allocator suites. All RED before (SEGV / "Expected NULL" / "Expected -12 Was 1"), GREEN after. xce-build.sh all asan: 115/115 suites OK, Go tests OK. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c02d8e5cf6
commit
f9146864a3
10 changed files with 563 additions and 12 deletions
95
src/scheduler/testsuite/test_sched_make_allocs_oom.c
Normal file
95
src/scheduler/testsuite/test_sched_make_allocs_oom.c
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* bug-1882 regression, scheduler side: sched_make_allocs() (scheduler.c)
|
||||
* must return NULL when make_alloc() cannot build one of the records,
|
||||
* instead of an array with a NULL entry that the allocator plugins would
|
||||
* hand on as a host allocation silently missing a resource.
|
||||
*
|
||||
* Links the real scheduler_srcs (same technique as
|
||||
* sched_scheduler_connect_test) plus `-Wl,--wrap=strdup` (bug-1843's
|
||||
* `--wrap=realloc` idiom), so a chosen strdup() inside make_alloc() fails.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "resobj.h"
|
||||
#include "scheduler.h"
|
||||
#include "xcealloc.h"
|
||||
|
||||
extern char* __real_strdup(const char* s);
|
||||
|
||||
static int g_strdup_fail_at = -1; /* -1 == disabled */
|
||||
static int g_strdup_calls = 0;
|
||||
|
||||
char* __wrap_strdup(const char* s)
|
||||
{
|
||||
if (g_strdup_fail_at >= 0) {
|
||||
int this_call = g_strdup_calls++;
|
||||
|
||||
if (this_call == g_strdup_fail_at) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return __real_strdup(s);
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
g_strdup_fail_at = -1;
|
||||
g_strdup_calls = 0;
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
g_strdup_fail_at = -1;
|
||||
}
|
||||
|
||||
static void test_sched_make_allocs_returns_null_when_a_record_fails(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_resource_t res;
|
||||
xce_resource_t* slots;
|
||||
xce_alloc_t** allocs;
|
||||
int idxs[2];
|
||||
float vals[2] = { 1.0f, 2.0f };
|
||||
|
||||
memset(&sched, 0, sizeof(sched));
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_sched_core_init(&(sched.core)));
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
res.name = strdup("slots");
|
||||
slots = makeResource(sched.core.resmgr, &res);
|
||||
TEST_ASSERT_NOT_NULL(slots);
|
||||
idxs[0] = slots->idx;
|
||||
idxs[1] = slots->idx;
|
||||
|
||||
/* Control: both records built. */
|
||||
allocs = sched_make_allocs(&sched, (char*)"", 2, idxs, vals, NULL);
|
||||
TEST_ASSERT_NOT_NULL(allocs);
|
||||
TEST_ASSERT_NOT_NULL(allocs[0]);
|
||||
TEST_ASSERT_NOT_NULL(allocs[1]);
|
||||
free_ResAlloc_list(2, allocs);
|
||||
|
||||
/* strdup() calls: record 0 resname = 0, reserve = 1; record 1
|
||||
* resname = 2. Fail record 1's resname. */
|
||||
g_strdup_calls = 0;
|
||||
g_strdup_fail_at = 2;
|
||||
errno = 0;
|
||||
allocs = sched_make_allocs(&sched, (char*)"", 2, idxs, vals, NULL);
|
||||
g_strdup_fail_at = -1;
|
||||
|
||||
TEST_ASSERT_NULL_MESSAGE(allocs,
|
||||
"a record that cannot be built must fail the "
|
||||
"whole array, not leave a NULL entry");
|
||||
TEST_ASSERT_EQUAL_INT(ENOMEM, errno);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_sched_make_allocs_returns_null_when_a_record_fails);
|
||||
return UNITY_END();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue