diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2023-06-14 11:05:48 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2023-06-14 11:07:30 +0200 |
commit | 46dccd2ec0eafd850b2168d4dfe4e74949fd3424 (patch) | |
tree | cb853ae6459c7fd7c48fbcaa42d46056fe77c03f | |
parent | a02450ff0bfa45618e72fc7103ea3a8f0e7fff80 (diff) | |
download | busybox-w32-46dccd2ec0eafd850b2168d4dfe4e74949fd3424.tar.gz busybox-w32-46dccd2ec0eafd850b2168d4dfe4e74949fd3424.tar.bz2 busybox-w32-46dccd2ec0eafd850b2168d4dfe4e74949fd3424.zip |
shell/math: fix nested ?: and do not parse variables in not-taken branch
Fixes arith-ternary1.tests and arith-ternary_nested.tests
function old new delta
evaluate_string 1043 1101 +58
arith_apply 1087 1137 +50
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 108/0) Total: 108 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/ash_test/ash-arith/arith-ternary1.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-arith/arith-ternary1.tests | 7 | ||||
-rw-r--r-- | shell/ash_test/ash-arith/arith-ternary2.right | 4 | ||||
-rwxr-xr-x | shell/ash_test/ash-arith/arith-ternary2.tests | 7 | ||||
-rw-r--r-- | shell/ash_test/ash-arith/arith-ternary_nested.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-arith/arith-ternary_nested.tests | 2 | ||||
-rw-r--r-- | shell/math.c | 80 |
7 files changed, 61 insertions, 43 deletions
diff --git a/shell/ash_test/ash-arith/arith-ternary1.right b/shell/ash_test/ash-arith/arith-ternary1.right index c968f1181..6b751d7b8 100644 --- a/shell/ash_test/ash-arith/arith-ternary1.right +++ b/shell/ash_test/ash-arith/arith-ternary1.right | |||
@@ -1,5 +1,2 @@ | |||
1 | 42:42 | 1 | 42:42 |
2 | a=0 | 2 | a=0 |
3 | 6:6 | ||
4 | a=b=+err+ | ||
5 | b=6 | ||
diff --git a/shell/ash_test/ash-arith/arith-ternary1.tests b/shell/ash_test/ash-arith/arith-ternary1.tests index 5a54e34b6..3532ce54d 100755 --- a/shell/ash_test/ash-arith/arith-ternary1.tests +++ b/shell/ash_test/ash-arith/arith-ternary1.tests | |||
@@ -3,10 +3,3 @@ a=0 | |||
3 | # The not-taken branch should not evaluate | 3 | # The not-taken branch should not evaluate |
4 | echo 42:$((1 ? 42 : (a+=2))) | 4 | echo 42:$((1 ? 42 : (a+=2))) |
5 | echo "a=$a" | 5 | echo "a=$a" |
6 | |||
7 | a='b=+err+' | ||
8 | b=5 | ||
9 | # The not-taken branch should not even parse variables | ||
10 | echo 6:$((0 ? a : ++b)) | ||
11 | echo "a=$a" | ||
12 | echo "b=$b" | ||
diff --git a/shell/ash_test/ash-arith/arith-ternary2.right b/shell/ash_test/ash-arith/arith-ternary2.right index aa54bd925..a549b1b5c 100644 --- a/shell/ash_test/ash-arith/arith-ternary2.right +++ b/shell/ash_test/ash-arith/arith-ternary2.right | |||
@@ -1 +1,3 @@ | |||
1 | 5:5 | 1 | 6:6 |
2 | a=b=+err+ | ||
3 | b=6 | ||
diff --git a/shell/ash_test/ash-arith/arith-ternary2.tests b/shell/ash_test/ash-arith/arith-ternary2.tests index eefc8e7ce..cb3163932 100755 --- a/shell/ash_test/ash-arith/arith-ternary2.tests +++ b/shell/ash_test/ash-arith/arith-ternary2.tests | |||
@@ -1,2 +1,7 @@ | |||
1 | exec 2>&1 | 1 | exec 2>&1 |
2 | echo 5:$((1?2?3?4?5:6:7:8:9)) | 2 | a='b=+err+' |
3 | b=5 | ||
4 | # The not-taken branch should not parse variables | ||
5 | echo 6:$((0 ? a : ++b)) | ||
6 | echo "a=$a" | ||
7 | echo "b=$b" | ||
diff --git a/shell/ash_test/ash-arith/arith-ternary_nested.right b/shell/ash_test/ash-arith/arith-ternary_nested.right new file mode 100644 index 000000000..aa54bd925 --- /dev/null +++ b/shell/ash_test/ash-arith/arith-ternary_nested.right | |||
@@ -0,0 +1 @@ | |||
5:5 | |||
diff --git a/shell/ash_test/ash-arith/arith-ternary_nested.tests b/shell/ash_test/ash-arith/arith-ternary_nested.tests new file mode 100755 index 000000000..eefc8e7ce --- /dev/null +++ b/shell/ash_test/ash-arith/arith-ternary_nested.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | exec 2>&1 | ||
2 | echo 5:$((1?2?3?4?5:6:7:8:9)) | ||
diff --git a/shell/math.c b/shell/math.c index 9ca7c6bb1..2b7a3494f 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -331,6 +331,28 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
331 | 331 | ||
332 | top_of_stack = NUMPTR - 1; | 332 | top_of_stack = NUMPTR - 1; |
333 | 333 | ||
334 | if (op == TOK_CONDITIONAL_SEP) { | ||
335 | /* "expr1 ? expr2 : expr3" operation */ | ||
336 | var_or_num_t *expr1 = &top_of_stack[-2]; | ||
337 | if (expr1 < numstack) { | ||
338 | return "malformed ?: operator"; | ||
339 | } | ||
340 | err = arith_lookup_val(math_state, expr1); | ||
341 | if (err) | ||
342 | return err; | ||
343 | if (expr1->val != 0) /* select expr2 or expr3 */ | ||
344 | top_of_stack--; | ||
345 | err = arith_lookup_val(math_state, top_of_stack); | ||
346 | if (err) | ||
347 | return err; | ||
348 | NUMPTR = expr1 + 1; | ||
349 | expr1->val = top_of_stack->val; | ||
350 | expr1->var_name = NULL; | ||
351 | return NULL; | ||
352 | } | ||
353 | if (op == TOK_CONDITIONAL) /* Example: $((a ? b)) */ | ||
354 | return "malformed ?: operator"; | ||
355 | |||
334 | /* Resolve name to value, if needed */ | 356 | /* Resolve name to value, if needed */ |
335 | err = arith_lookup_val(math_state, top_of_stack); | 357 | err = arith_lookup_val(math_state, top_of_stack); |
336 | if (err) | 358 | if (err) |
@@ -350,25 +372,12 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
350 | else if (op != TOK_UPLUS) { | 372 | else if (op != TOK_UPLUS) { |
351 | /* Binary operators */ | 373 | /* Binary operators */ |
352 | arith_t right_side_val; | 374 | arith_t right_side_val; |
353 | int bad_second_val; | ||
354 | 375 | ||
355 | /* Binary operators need two arguments */ | 376 | /* Binary operators need two arguments */ |
356 | if (top_of_stack == numstack) | 377 | if (top_of_stack == numstack) |
357 | goto syntax_err; | 378 | goto syntax_err; |
358 | /* ...and they pop one */ | 379 | /* ...and they pop one */ |
359 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ | 380 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ |
360 | |||
361 | bad_second_val = (top_of_stack->var_name == SECOND_VAL_VALID); | ||
362 | if (op == TOK_CONDITIONAL) { /* ? operation */ | ||
363 | /* Make next if (...) protect against | ||
364 | * $((expr1 ? expr2)) - that is, missing ": expr" */ | ||
365 | bad_second_val = !bad_second_val; | ||
366 | } | ||
367 | if (bad_second_val) { | ||
368 | /* Protect against $((expr <not_?_op> expr1 : expr2)) */ | ||
369 | return "malformed ?: operator"; | ||
370 | } | ||
371 | |||
372 | top_of_stack--; /* now points to left side */ | 381 | top_of_stack--; /* now points to left side */ |
373 | 382 | ||
374 | if (op != TOK_ASSIGN) { | 383 | if (op != TOK_ASSIGN) { |
@@ -380,19 +389,7 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
380 | 389 | ||
381 | right_side_val = rez; | 390 | right_side_val = rez; |
382 | rez = top_of_stack->val; | 391 | rez = top_of_stack->val; |
383 | if (op == TOK_CONDITIONAL) /* ? operation */ | 392 | if (op == TOK_BOR || op == TOK_OR_ASSIGN) |
384 | rez = (rez ? right_side_val : top_of_stack[1].second_val); | ||
385 | else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ | ||
386 | if (top_of_stack == numstack) { | ||
387 | /* Protect against $((expr : expr)) */ | ||
388 | return "malformed ?: operator"; | ||
389 | } | ||
390 | top_of_stack->val = rez; | ||
391 | top_of_stack->second_val = right_side_val; | ||
392 | top_of_stack->var_name = SECOND_VAL_VALID; | ||
393 | return NULL; | ||
394 | } | ||
395 | else if (op == TOK_BOR || op == TOK_OR_ASSIGN) | ||
396 | rez |= right_side_val; | 393 | rez |= right_side_val; |
397 | else if (op == TOK_OR) | 394 | else if (op == TOK_OR) |
398 | rez = right_side_val || rez; | 395 | rez = right_side_val || rez; |
@@ -833,7 +830,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
833 | lasttok = TOK_NUM; | 830 | lasttok = TOK_NUM; |
834 | goto next; | 831 | goto next; |
835 | } | 832 | } |
836 | /* Not (y), but ...x~y): fall through to evaluate x~y */ | 833 | /* Not (y), but ...x~y). Fall through to evaluate x~y */ |
837 | } else { | 834 | } else { |
838 | operator prev_prec = PREC(prev_op); | 835 | operator prev_prec = PREC(prev_op); |
839 | fix_assignment_prec(prec); | 836 | fix_assignment_prec(prec); |
@@ -841,11 +838,17 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
841 | if (prev_prec < prec | 838 | if (prev_prec < prec |
842 | || (prev_prec == prec && is_right_associative(prec)) | 839 | || (prev_prec == prec && is_right_associative(prec)) |
843 | ) { | 840 | ) { |
844 | /* ...x~y@: push @ on opstack */ | 841 | /* Unless a?b?c:d:... and we are at the second : */ |
845 | opstackptr++; /* undo removal of ~ op */ | 842 | if (op != TOK_CONDITIONAL_SEP |
846 | goto push_op; | 843 | || prev_op != TOK_CONDITIONAL_SEP |
844 | ) { | ||
845 | /* ...x~y@: push @ on opstack */ | ||
846 | opstackptr++; /* undo removal of ~ op */ | ||
847 | goto push_op; | ||
848 | } | ||
849 | /* else: a?b?c:d:. Evaluate b?c:d, replace it on stack with result. Then repeat */ | ||
847 | } | 850 | } |
848 | /* ...x~y@: evaluate x~y, replace it on stack with result. Then repeat */ | 851 | /* else: ...x~y@. Evaluate x~y, replace it on stack with result. Then repeat */ |
849 | } | 852 | } |
850 | dbg("arith_apply(prev_op:%02x, numstack:%d)", prev_op, (int)(numstackptr - numstack)); | 853 | dbg("arith_apply(prev_op:%02x, numstack:%d)", prev_op, (int)(numstackptr - numstack)); |
851 | errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); | 854 | errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); |
@@ -856,6 +859,21 @@ dbg(" numstack:%d val:%lld %lld %p", (int)(numstackptr - numstack), | |||
856 | numstackptr[-1].var_name == SECOND_VAL_VALID ? numstackptr[-1].second_val : 0, | 859 | numstackptr[-1].var_name == SECOND_VAL_VALID ? numstackptr[-1].second_val : 0, |
857 | numstackptr[-1].var_name | 860 | numstackptr[-1].var_name |
858 | ); | 861 | ); |
862 | /* For ternary ?: we need to remove ? from opstack too, not just : */ | ||
863 | if (prev_op == TOK_CONDITIONAL_SEP) { | ||
864 | // This is caught in arith_apply() | ||
865 | //if (opstackptr == opstack) { | ||
866 | // /* Example: $((2:3)) */ | ||
867 | // errmsg = "where is your ? in ?:"; | ||
868 | // goto err_with_custom_msg; | ||
869 | //} | ||
870 | opstackptr--; | ||
871 | if (*opstackptr != TOK_CONDITIONAL) { | ||
872 | /* Example: $((1,2:3)) */ | ||
873 | errmsg = "malformed ?: operator"; | ||
874 | goto err_with_custom_msg; | ||
875 | } | ||
876 | } | ||
859 | } /* while (opstack not empty) */ | 877 | } /* while (opstack not empty) */ |
860 | if (op == TOK_RPAREN) /* unpaired RPAREN? */ | 878 | if (op == TOK_RPAREN) /* unpaired RPAREN? */ |
861 | goto err; | 879 | goto err; |