diff --git a/src/lib/xce_select.c b/src/lib/xce_select.c index 7c9b48ef..2166a874 100644 --- a/src/lib/xce_select.c +++ b/src/lib/xce_select.c @@ -527,15 +527,19 @@ static int is_str_cmp(const xce_select_node_t* node) static double simple_num(const xce_select_node_t* n, const host_eval_ctx_t* h) { - return n->type == SEL_NUM ? n->u.numval - : h->numvals[n->u.resname.res_idx]; + if (n->type == SEL_NUM) return n->u.numval; + int idx = n->u.resname.res_idx; + if (h->numvals == NULL || idx < 0 || idx >= h->num_res) return 0.0; + return h->numvals[idx]; } static const char* simple_str(const xce_select_node_t* n, const host_eval_ctx_t* h) { - return n->type == SEL_STR ? n->u.strval - : h->strvals[n->u.resname.res_idx]; + if (n->type == SEL_STR) return n->u.strval; + int idx = n->u.resname.res_idx; + if (h->strvals == NULL || idx < 0 || idx >= h->num_res) return NULL; + return h->strvals[idx]; } /* Safe cache reads: use cached value when in-bounds, fall back to host data @@ -566,8 +570,11 @@ int eval_select_simple(const xce_select_node_t* root, case SEL_RESNAME: switch (root->u.resname.res_type) { case RES_TYPE_BOOL: - case RES_TYPE_NUMERIC: - return host->numvals[root->u.resname.res_idx] != 0.0 ? 1 : 0; + case RES_TYPE_NUMERIC: { + int idx = root->u.resname.res_idx; + if (host->numvals == NULL || idx < 0 || idx >= host->num_res) return 0; + return host->numvals[idx] != 0.0 ? 1 : 0; + } case RES_TYPE_STRING: return -1; } @@ -670,13 +677,17 @@ static int eval_select_inner(const xce_select_node_t* node, switch (node->u.resname.res_type) { case RES_TYPE_BOOL: case RES_TYPE_NUMERIC: { - double v = host->numvals[node->u.resname.res_idx]; + int idx = node->u.resname.res_idx; + double v = (host->numvals != NULL && idx >= 0 && idx < host->num_res) + ? host->numvals[idx] : 0.0; if (node->node_id < ctx->num_vals_cap) ctx->num_vals[node->node_id] = v; return v != 0.0 ? 1 : 0; } case RES_TYPE_STRING: { - const char* sv = host->strvals[node->u.resname.res_idx]; + int idx = node->u.resname.res_idx; + const char* sv = (host->strvals != NULL && idx >= 0 && idx < host->num_res) + ? host->strvals[idx] : NULL; if (node->node_id < ctx->str_vals_cap) ctx->str_vals[node->node_id] = sv; return -1; diff --git a/src/scheduler/src/resource.c b/src/scheduler/src/resource.c index c7a814b6..e0d9f418 100644 --- a/src/scheduler/src/resource.c +++ b/src/scheduler/src/resource.c @@ -132,8 +132,9 @@ build_host_eval_ctx(const xce_sched_worker_t* w, int num_res) if (ctx == NULL) return NULL; if (num_res <= 0) { - free(ctx); - return NULL; + /* valid empty context — numvals/strvals remain NULL, bounds checks protect access */ + ctx->num_res = 0; + return ctx; } double* numvals = calloc(num_res, sizeof(double)); @@ -185,26 +186,14 @@ int sched_meet_host_selection(xce_scheduler_t* sched, xce_sched_worker_t* w, int num_res = (int)w->resvals.caps; - /* When num_res == 0, build_host_eval_ctx returns NULL (no resource slots). - * Use an empty stack-allocated context so eval_select_simple can still - * evaluate constant sub-expressions (SEL_TRUE, SEL_FALSE, SEL_ANY). */ - host_eval_ctx_t empty_ctx = { .num_res = 0, .numvals = NULL, .strvals = NULL }; - host_eval_ctx_t* hctx; - - if (num_res <= 0) { - hctx = &empty_ctx; - } else { - hctx = build_host_eval_ctx(w, num_res); - if (hctx == NULL) { - /* OOM: conservatively pass host */ - return 1; - } + host_eval_ctx_t* hctx = build_host_eval_ctx(w, num_res); + if (hctx == NULL) { + /* OOM: conservatively pass host */ + return 1; } int rc = eval_select_simple(s->root, hctx); - if (hctx != &empty_ctx) { - free_host_eval_ctx(hctx); - } + free_host_eval_ctx(hctx); /* eval_select_simple: 1=match, 0=no-match, <0=error (treat as no-match) */ return (rc > 0) ? 1 : 0;