aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2023-06-14 11:05:48 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2023-06-14 11:07:30 +0200
commit46dccd2ec0eafd850b2168d4dfe4e74949fd3424 (patch)
treecb853ae6459c7fd7c48fbcaa42d46056fe77c03f
parenta02450ff0bfa45618e72fc7103ea3a8f0e7fff80 (diff)
downloadbusybox-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.right3
-rwxr-xr-xshell/ash_test/ash-arith/arith-ternary1.tests7
-rw-r--r--shell/ash_test/ash-arith/arith-ternary2.right4
-rwxr-xr-xshell/ash_test/ash-arith/arith-ternary2.tests7
-rw-r--r--shell/ash_test/ash-arith/arith-ternary_nested.right1
-rwxr-xr-xshell/ash_test/ash-arith/arith-ternary_nested.tests2
-rw-r--r--shell/math.c80
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 @@
142:42 142:42
2a=0 2a=0
36:6
4a=b=+err+
5b=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
4echo 42:$((1 ? 42 : (a+=2))) 4echo 42:$((1 ? 42 : (a+=2)))
5echo "a=$a" 5echo "a=$a"
6
7a='b=+err+'
8b=5
9# The not-taken branch should not even parse variables
10echo 6:$((0 ? a : ++b))
11echo "a=$a"
12echo "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 @@
15:5 16:6
2a=b=+err+
3b=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 @@
1exec 2>&1 1exec 2>&1
2echo 5:$((1?2?3?4?5:6:7:8:9)) 2a='b=+err+'
3b=5
4# The not-taken branch should not parse variables
5echo 6:$((0 ? a : ++b))
6echo "a=$a"
7echo "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 @@
1exec 2>&1
2echo 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;