# UNDF: UNDF-2026-000000027 diff --git a/libpromises/evalfunction.c b/libpromises/evalfunction.c index a1b2c3d..e4f5a6b 100644 --- a/libpromises/evalfunction.c +++ b/libpromises/evalfunction.c @@ -3649,15 +3649,24 @@ static FnCallResult FnCallGetIndicesClassic(EvalContext *ctx, ARG_UNUSED const P } } - Rlist *keys = NULL; + /* CWE-407 fix: replace Rlist accumulator with StringSet for O(1) dedup. + * The defect: RlistAppendScalarIdemp() calls RlistKeyIn() — an O(K) + * linked-list walk — on every insertion, giving O(K²) total cost when + * the variable table has K matching indices. + * Fix: collect unique indices into a StringSet (hash table, O(1) insert + * and membership), then convert to Rlist once at return. O(K) total. */ + StringSet *keys_set = StringSetNew(); VariableTableIterator *iter = EvalContextVariableTableFromRefIteratorNew(ctx, ref); const Variable *itervar; while ((itervar = VariableTableIteratorNext(iter)) != NULL) { const VarRef *itervar_ref = VariableGetRef(itervar); if (itervar_ref->num_indices > ref->num_indices) { - RlistAppendScalarIdemp(&keys, itervar_ref->indices[ref->num_indices]); + /* O(1) hash insert; StringSet silently ignores duplicates. */ + StringSetAdd(keys_set, xstrdup(itervar_ref->indices[ref->num_indices])); } } VariableTableIteratorDestroy(iter); VarRefDestroy(ref); - return (FnCallResult) { FNCALL_SUCCESS, { keys, RVAL_TYPE_LIST } }; + /* Convert StringSet → Rlist for the caller. */ + Rlist *keys = NULL; + StringSetIterator set_iter = StringSetIteratorInit(keys_set); + const char *key; + while ((key = StringSetIteratorNext(&set_iter)) != NULL) + { + RlistAppendScalar(&keys, key); + } + StringSetDestroy(keys_set); + + return (FnCallResult) { FNCALL_SUCCESS, { keys, RVAL_TYPE_LIST } }; }