B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections. Squash of 94 local commits onto remote master.
62 lines
3.3 KiB
Diff
62 lines
3.3 KiB
Diff
diff --git a/libpromises/evalfunction.c b/libpromises/evalfunction.c
|
|
index a1b2c3d..c2e1f7b 100644
|
|
--- a/libpromises/evalfunction.c
|
|
+++ b/libpromises/evalfunction.c
|
|
@@ -4221,6 +4221,12 @@ static FnCallResult FnCallMapData(EvalContext *ctx, ARG_UNUSED const Policy *pol
|
|
bool mapdatamode = (strcmp(fp->name, "mapdata") == 0);
|
|
Rlist *returnlist = NULL;
|
|
|
|
+ /* CWE-407 fix: track already-appended values in a StringSet so the
|
|
+ * nested-container branch can dedup in O(1) per element instead of the
|
|
+ * O(R) linked-list walk performed by RlistAppendScalarIdemp.
|
|
+ * With N total sub-elements, that was O(N²); with StringSet it is O(N).
|
|
+ * Initialised here; destroyed at every exit path below. */
|
|
+ StringSet *seen = StringSetNew();
|
|
+
|
|
// This is a delayed evaluation function, so we have to resolve arguments ourselves
|
|
// We resolve them once now, to get the second or third argument with the iteration data
|
|
Rlist *expargs = NewExpArgs(ctx, policy, fp, NULL);
|
|
@@ -4335,6 +4341,7 @@ static FnCallResult FnCallMapData(EvalContext *ctx, ARG_UNUSED const Policy *pol
|
|
if (strstr(BufferData(expbuf), "$(this.k)") || strstr(BufferData(expbuf), "${this.k}") ||
|
|
strstr(BufferData(expbuf), "$(this.v)") || strstr(BufferData(expbuf), "${this.v}"))
|
|
{
|
|
+ StringSetDestroy(seen);
|
|
RlistDestroy(returnlist);
|
|
EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "k");
|
|
EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "v");
|
|
@@ -4385,6 +4392,7 @@ static FnCallResult FnCallMapData(EvalContext *ctx, ARG_UNUSED const Policy *pol
|
|
if (strstr(BufferData(expbuf), "$(this.k)") || strstr(BufferData(expbuf), "${this.k}") ||
|
|
(havekey && (strstr(BufferData(expbuf), "$(this.k[1])") || strstr(BufferData(expbuf), "${this.k[1]}"))) ||
|
|
strstr(BufferData(expbuf), "$(this.v)") || strstr(BufferData(expbuf), "${this.v}"))
|
|
{
|
|
+ StringSetDestroy(seen);
|
|
RlistDestroy(returnlist);
|
|
EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "k");
|
|
if (havekey)
|
|
@@ -4404,9 +4413,18 @@ static FnCallResult FnCallMapData(EvalContext *ctx, ARG_UNUSED const Policy *pol
|
|
if (canonifymode)
|
|
{
|
|
BufferCanonify(expbuf);
|
|
}
|
|
|
|
- RlistAppendScalarIdemp(&returnlist, BufferData(expbuf));
|
|
+ /* CWE-407 fix: O(1) hash membership test replaces O(R) list
|
|
+ * walk. RlistAppendScalarIdemp called RlistKeyIn() which
|
|
+ * scanned the full returnlist on every iteration. */
|
|
+ const char *expanded = BufferData(expbuf);
|
|
+ if (!StringSetContains(seen, expanded))
|
|
+ {
|
|
+ StringSetAdd(seen, xstrdup(expanded));
|
|
+ RlistAppendScalar(&returnlist, expanded);
|
|
+ }
|
|
if (havekey)
|
|
{
|
|
EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "k[1]");
|
|
@@ -4421,6 +4439,8 @@ static FnCallResult FnCallMapData(EvalContext *ctx, ARG_UNUSED const Policy *pol
|
|
}
|
|
|
|
BufferDestroy(expbuf);
|
|
+ StringSetDestroy(seen);
|
|
+
|
|
JsonDestroyMaybe(container, allocated);
|
|
RlistDestroy(expargs);
|