commit a27c0ec

shrub  ·  2026-08-30 15:39:01 +0000 UTC
parent 7ffaa2c
fix some memory issues with eval
4 files changed,  +35, -4
+2, -1
 1@@ -103,7 +103,8 @@ exportenv(const struct Target *t)
 2 		quoted = shellquote(t->env.v[i].val);
 3 		nname = strlen(t->env.v[i].name);
 4 		nquoted = strlen(quoted);
 5-		need = len + nname + nquoted + 10;
 6+		/* "export " + name + '=' + value + "; " + nul. */
 7+		need = len + 7 + nname + 1 + nquoted + 2 + 1;
 8 		if (need > cap) {
 9 			while (cap < need)
10 				cap *= 2;
+10, -2
 1@@ -371,7 +371,11 @@ expandvarref(struct EvalCtx *ctx, const char *s, size_t n)
 2 		return xstrdup("");
 3 	if (v->simple)
 4 		return xstrdup(v->val);
 5-	val = expandstr(ctx, v->val);
 6+	/* expansion could be $(eval ...) and replace this var, freeing its
 7+	 * current value so keep the input alive for the whole expansion */
 8+	raw = xstrdup(v->val);
 9+	val = expandstr(ctx, raw);
10+	free(raw);
11 	return val;
12 }
13 
14@@ -400,7 +404,11 @@ expandsubstref(struct EvalCtx *ctx, const char *s, size_t colon, size_t eq, size
15 	base = 0;
16 	auto_name = 0;
17 	if (v) {
18-		base = expandstr(ctx, v->val);
19+		char *rawval;
20+
21+		rawval = xstrdup(v->val);
22+		base = expandstr(ctx, rawval);
23+		free(rawval);
24 	} else if (name[0] && name[1] == 0) {
25 		/* we handle some substitution refs on automatic vars like $(@:.o=.c). */
26 		switch (name[0]) {
+16, -0
 1@@ -645,15 +645,31 @@ int
 2 evalsnippet(struct EvalCtx *ctx, const char *path, const char *src)
 3 {
 4 	struct Ast ast;
 5+	struct RuleSet scratch;
 6 	const char *saved_path;
 7+	struct RuleSet *saved_out;
 8 	int rc;
 9 
10 	if (parse(path, src, &ast, ctx->mode) < 0)
11 		return -1;
12+	/* recipe and graph expansion contexts do have an env but no mutable
13+	 * ruleset because the graph has already been constructed by then.
14+	 * give late evals a termporary ruleset for any new statements produced but 
15+	 * still apply assignments immediately */
16+	saved_out = ctx->out;
17+	if (!ctx->out) {
18+		memset(&scratch, 0, sizeof(scratch));
19+		scratch.export_all = ctx->export_all;
20+		ctx->out = &scratch;
21+	}
22 	saved_path = ctx->cur_path;
23 	ctx->cur_path = path;
24 	rc = evalnodes((const struct NodeList *)&ast, ctx->out, ctx);
25 	ctx->cur_path = saved_path;
26+	if (!saved_out) {
27+		freeruleset(&scratch);
28+		ctx->out = 0;
29+	}
30 	freeast(&ast);
31 	return rc;
32 }
+7, -1
 1@@ -1279,7 +1279,13 @@ fncall(struct EvalCtx *ctx, const char *args)
 2 
 3 	v = findvar(ctx->env, name);
 4 	if (v) {
 5-		val = expandstr(ctx, v->val);
 6+		char *body;
 7+
 8+		/* The function body may use $(eval ...) to replace its own variable,
 9+		 * so we make a private copy before expanding */
10+		body = xstrdup(v->val);
11+		val = expandstr(ctx, body);
12+		free(body);
13 	} else {
14 		size_t need, pos;
15 		char *inv;