diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2023-06-18 00:47:55 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2023-06-18 18:12:14 +0200 |
commit | c72c5552edecb8a2f97cd7e2d9a09a3059986cca (patch) | |
tree | eafbd44ec238db465a6eaf95067820f6e7515a09 | |
parent | 182e5a4d000cdb5808830b1f02c59d40c6e61150 (diff) | |
download | busybox-w32-c72c5552edecb8a2f97cd7e2d9a09a3059986cca.tar.gz busybox-w32-c72c5552edecb8a2f97cd7e2d9a09a3059986cca.tar.bz2 busybox-w32-c72c5552edecb8a2f97cd7e2d9a09a3059986cca.zip |
shell/math: decrease stack usage by not allocating copies of variable names
We risk exhaust stack with alloca() with old code.
function old new delta
arith_apply 990 1023 +33
evaluate_string 1467 1494 +27
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 60/0) Total: 60 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/math.c | 83 |
1 files changed, 51 insertions, 32 deletions
diff --git a/shell/math.c b/shell/math.c index f6fed805c..4c1b1b762 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -245,7 +245,7 @@ is_right_associative(operator prec) | |||
245 | 245 | ||
246 | typedef struct { | 246 | typedef struct { |
247 | arith_t val; | 247 | arith_t val; |
248 | char *var_name; | 248 | const char *var_name; |
249 | } var_or_num_t; | 249 | } var_or_num_t; |
250 | 250 | ||
251 | #define VALID_NAME(name) (name) | 251 | #define VALID_NAME(name) (name) |
@@ -256,44 +256,58 @@ typedef struct remembered_name { | |||
256 | const char *var_name; | 256 | const char *var_name; |
257 | } remembered_name; | 257 | } remembered_name; |
258 | 258 | ||
259 | static ALWAYS_INLINE int isalnum_(int c) | ||
260 | { | ||
261 | return (isalnum(c) || c == '_'); | ||
262 | } | ||
263 | |||
259 | static arith_t | 264 | static arith_t |
260 | evaluate_string(arith_state_t *math_state, const char *expr); | 265 | evaluate_string(arith_state_t *math_state, const char *expr); |
261 | 266 | ||
262 | static const char* | 267 | static const char* |
263 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | 268 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) |
264 | { | 269 | { |
265 | if (VALID_NAME(t->var_name)) { | 270 | const char *name = t->var_name; |
266 | const char *p = math_state->lookupvar(t->var_name); | 271 | char c; |
267 | if (p) { | 272 | const char *p; |
268 | remembered_name *cur; | 273 | char *e = (char*)endofname(name); |
269 | remembered_name remember; | 274 | |
270 | 275 | c = *e; | |
271 | /* did we already see this name? | 276 | *e = '\0'; |
272 | * testcase: a=b; b=a; echo $((a)) | 277 | p = math_state->lookupvar(name); |
273 | */ | 278 | *e = c; |
274 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { | 279 | if (p) { |
275 | if (strcmp(cur->var_name, t->var_name) == 0) { | 280 | size_t len = e - name; |
276 | /* yes */ | 281 | remembered_name *cur; |
277 | return "expression recursion loop detected"; | 282 | remembered_name remember; |
278 | } | 283 | |
284 | /* did we already see this name? | ||
285 | * testcase: a=b; b=a; echo $((a)) | ||
286 | */ | ||
287 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { | ||
288 | if (strncmp(cur->var_name, name, len) == 0 | ||
289 | && !isalnum_(cur->var_name[len]) | ||
290 | ) { | ||
291 | /* yes */ | ||
292 | return "expression recursion loop detected"; | ||
279 | } | 293 | } |
294 | } | ||
280 | 295 | ||
281 | /* push current var name */ | 296 | /* push current var name */ |
282 | remember.var_name = t->var_name; | 297 | remember.var_name = name; |
283 | remember.next = math_state->list_of_recursed_names; | 298 | remember.next = math_state->list_of_recursed_names; |
284 | math_state->list_of_recursed_names = &remember; | 299 | math_state->list_of_recursed_names = &remember; |
285 | 300 | ||
286 | /* recursively evaluate p as expression */ | 301 | /* recursively evaluate p as expression */ |
287 | t->val = evaluate_string(math_state, p); | 302 | t->val = evaluate_string(math_state, p); |
288 | 303 | ||
289 | /* pop current var name */ | 304 | /* pop current var name */ |
290 | math_state->list_of_recursed_names = remember.next; | 305 | math_state->list_of_recursed_names = remember.next; |
291 | 306 | ||
292 | return math_state->errmsg; | 307 | return math_state->errmsg; |
293 | } | ||
294 | /* treat undefined var as 0 */ | ||
295 | t->val = 0; | ||
296 | } | 308 | } |
309 | /* treat undefined var as 0 */ | ||
310 | t->val = 0; | ||
297 | return NULL; | 311 | return NULL; |
298 | } | 312 | } |
299 | 313 | ||
@@ -447,7 +461,13 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
447 | } | 461 | } |
448 | /* Save to shell variable */ | 462 | /* Save to shell variable */ |
449 | sprintf(buf, ARITH_FMT, rez); | 463 | sprintf(buf, ARITH_FMT, rez); |
450 | math_state->setvar(top_of_stack->var_name, buf); | 464 | { |
465 | char *e = (char*)endofname(top_of_stack->var_name); | ||
466 | char c = *e; | ||
467 | *e = '\0'; | ||
468 | math_state->setvar(top_of_stack->var_name, buf); | ||
469 | *e = c; | ||
470 | } | ||
451 | /* After saving, make previous value for v++ or v-- */ | 471 | /* After saving, make previous value for v++ or v-- */ |
452 | if (op == TOK_POST_INC) | 472 | if (op == TOK_POST_INC) |
453 | rez--; | 473 | rez--; |
@@ -610,6 +630,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
610 | * (modulo "09v09v09v09v09v" case, | 630 | * (modulo "09v09v09v09v09v" case, |
611 | * but we have code to detect that early) | 631 | * but we have code to detect that early) |
612 | */ | 632 | */ |
633 | dbg("expr:'%s' expr_len:%u", expr, expr_len); | ||
613 | numstackptr = numstack = alloca((expr_len / 2) * sizeof(numstack[0])); | 634 | numstackptr = numstack = alloca((expr_len / 2) * sizeof(numstack[0])); |
614 | opstackptr = opstack = alloca(expr_len * sizeof(opstack[0])); | 635 | opstackptr = opstack = alloca(expr_len * sizeof(opstack[0])); |
615 | } | 636 | } |
@@ -655,10 +676,8 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
655 | if (p != expr) { | 676 | if (p != expr) { |
656 | /* Name */ | 677 | /* Name */ |
657 | if (!math_state->evaluation_disabled) { | 678 | if (!math_state->evaluation_disabled) { |
658 | size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ | 679 | numstackptr->var_name = expr; |
659 | numstackptr->var_name = alloca(var_name_size); | 680 | dbg("[%d] var:'%.*s'", (int)(numstackptr - numstack), (int)(p - expr), expr); |
660 | safe_strncpy(numstackptr->var_name, expr, var_name_size); | ||
661 | dbg("[%d] var:'%s'", (int)(numstackptr - numstack), numstackptr->var_name); | ||
662 | expr = skip_whitespace(p); | 681 | expr = skip_whitespace(p); |
663 | /* If it is not followed by "=" operator... */ | 682 | /* If it is not followed by "=" operator... */ |
664 | if (expr[0] != '=' /* not "=..." */ | 683 | if (expr[0] != '=' /* not "=..." */ |