aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2023-06-16 19:43:53 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2023-06-16 19:51:01 +0200
commite1279858394a6079be6816cbedaa3f10e74057cc (patch)
tree7a7a032ea71978437c8888c9f508db1d4fdf4fe3
parentf8263528cd44ac5dc95778556c6fd3feea14742e (diff)
downloadbusybox-w32-e1279858394a6079be6816cbedaa3f10e74057cc.tar.gz
busybox-w32-e1279858394a6079be6816cbedaa3f10e74057cc.tar.bz2
busybox-w32-e1279858394a6079be6816cbedaa3f10e74057cc.zip
shell/math: fix ?: to not evaluate not-taken branches
This fixes ash-arith-arith-ternary1/2.tests function old new delta evaluate_string 1271 1432 +161 arith_apply 968 1000 +32 arith 22 36 +14 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/0 up/down: 207/0) Total: 207 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash_test/ash-arith/arith-ternary-assign.right1
-rwxr-xr-xshell/ash_test/ash-arith/arith-ternary-assign.tests3
-rw-r--r--shell/ash_test/ash-arith/arith-ternary-comma.right1
-rwxr-xr-xshell/ash_test/ash-arith/arith-ternary-comma.tests3
-rw-r--r--shell/ash_test/ash-arith/arith-ternary-preincr.right1
-rwxr-xr-xshell/ash_test/ash-arith/arith-ternary-preincr.tests3
-rw-r--r--shell/ash_test/ash-arith/arith-ternary3.right1
-rwxr-xr-xshell/ash_test/ash-arith/arith-ternary3.tests4
-rw-r--r--shell/ash_test/ash-arith/arith-ternary_nested3.right2
-rwxr-xr-xshell/ash_test/ash-arith/arith-ternary_nested3.tests6
-rw-r--r--shell/hush_test/hush-arith/arith-ternary-assign.right1
-rwxr-xr-xshell/hush_test/hush-arith/arith-ternary-assign.tests3
-rw-r--r--shell/hush_test/hush-arith/arith-ternary-comma.right1
-rwxr-xr-xshell/hush_test/hush-arith/arith-ternary-comma.tests3
-rw-r--r--shell/hush_test/hush-arith/arith-ternary-preincr.right1
-rwxr-xr-xshell/hush_test/hush-arith/arith-ternary-preincr.tests3
-rw-r--r--shell/hush_test/hush-arith/arith-ternary1.right2
-rwxr-xr-xshell/hush_test/hush-arith/arith-ternary1.tests5
-rw-r--r--shell/hush_test/hush-arith/arith-ternary2.right3
-rwxr-xr-xshell/hush_test/hush-arith/arith-ternary2.tests7
-rw-r--r--shell/hush_test/hush-arith/arith-ternary3.right1
-rwxr-xr-xshell/hush_test/hush-arith/arith-ternary3.tests4
-rw-r--r--shell/hush_test/hush-arith/arith-ternary_nested3.right2
-rwxr-xr-xshell/hush_test/hush-arith/arith-ternary_nested3.tests6
-rw-r--r--shell/math.c85
-rw-r--r--shell/math.h3
26 files changed, 131 insertions, 24 deletions
diff --git a/shell/ash_test/ash-arith/arith-ternary-assign.right b/shell/ash_test/ash-arith/arith-ternary-assign.right
new file mode 100644
index 000000000..6644d86bf
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith-ternary-assign.right
@@ -0,0 +1 @@
42:42
diff --git a/shell/ash_test/ash-arith/arith-ternary-assign.tests b/shell/ash_test/ash-arith/arith-ternary-assign.tests
new file mode 100755
index 000000000..fa18fe7b9
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith-ternary-assign.tests
@@ -0,0 +1,3 @@
1exec 2>&1
2a='@'
3echo 42:$((a=1?42:3,a))
diff --git a/shell/ash_test/ash-arith/arith-ternary-comma.right b/shell/ash_test/ash-arith/arith-ternary-comma.right
new file mode 100644
index 000000000..6644d86bf
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith-ternary-comma.right
@@ -0,0 +1 @@
42:42
diff --git a/shell/ash_test/ash-arith/arith-ternary-comma.tests b/shell/ash_test/ash-arith/arith-ternary-comma.tests
new file mode 100755
index 000000000..5e05b58c4
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith-ternary-comma.tests
@@ -0,0 +1,3 @@
1exec 2>&1
2x='@'
3echo 42:$((1?4:x,20*2+2))
diff --git a/shell/ash_test/ash-arith/arith-ternary-preincr.right b/shell/ash_test/ash-arith/arith-ternary-preincr.right
new file mode 100644
index 000000000..6644d86bf
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith-ternary-preincr.right
@@ -0,0 +1 @@
42:42
diff --git a/shell/ash_test/ash-arith/arith-ternary-preincr.tests b/shell/ash_test/ash-arith/arith-ternary-preincr.tests
new file mode 100755
index 000000000..3985c7079
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith-ternary-preincr.tests
@@ -0,0 +1,3 @@
1exec 2>&1
2x='@'
3echo 42:$((1?42:++x))
diff --git a/shell/ash_test/ash-arith/arith-ternary3.right b/shell/ash_test/ash-arith/arith-ternary3.right
new file mode 100644
index 000000000..6644d86bf
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith-ternary3.right
@@ -0,0 +1 @@
42:42
diff --git a/shell/ash_test/ash-arith/arith-ternary3.tests b/shell/ash_test/ash-arith/arith-ternary3.tests
new file mode 100755
index 000000000..0bf9f3002
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith-ternary3.tests
@@ -0,0 +1,4 @@
1exec 2>&1
2# "EXPR ?..." should check _evaluated_ EXPR,
3# not its last value
4echo 42:$((1 < 1 ? -1 : 1 > 1 ? 1 : 42))
diff --git a/shell/ash_test/ash-arith/arith-ternary_nested3.right b/shell/ash_test/ash-arith/arith-ternary_nested3.right
new file mode 100644
index 000000000..1a34fde65
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith-ternary_nested3.right
@@ -0,0 +1,2 @@
142:42
2a=2:2
diff --git a/shell/ash_test/ash-arith/arith-ternary_nested3.tests b/shell/ash_test/ash-arith/arith-ternary_nested3.tests
new file mode 100755
index 000000000..b69dcc6e9
--- /dev/null
+++ b/shell/ash_test/ash-arith/arith-ternary_nested3.tests
@@ -0,0 +1,6 @@
1exec 2>&1
2x='@'
3a=2
4# After processing nested ?:, outermost ?: should still rememeber to NOT evaluate a*=2
5echo 42:$((1?0?41:42:(a*=2)))
6echo "a=2:$a"
diff --git a/shell/hush_test/hush-arith/arith-ternary-assign.right b/shell/hush_test/hush-arith/arith-ternary-assign.right
new file mode 100644
index 000000000..6644d86bf
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary-assign.right
@@ -0,0 +1 @@
42:42
diff --git a/shell/hush_test/hush-arith/arith-ternary-assign.tests b/shell/hush_test/hush-arith/arith-ternary-assign.tests
new file mode 100755
index 000000000..fa18fe7b9
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary-assign.tests
@@ -0,0 +1,3 @@
1exec 2>&1
2a='@'
3echo 42:$((a=1?42:3,a))
diff --git a/shell/hush_test/hush-arith/arith-ternary-comma.right b/shell/hush_test/hush-arith/arith-ternary-comma.right
new file mode 100644
index 000000000..6644d86bf
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary-comma.right
@@ -0,0 +1 @@
42:42
diff --git a/shell/hush_test/hush-arith/arith-ternary-comma.tests b/shell/hush_test/hush-arith/arith-ternary-comma.tests
new file mode 100755
index 000000000..5e05b58c4
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary-comma.tests
@@ -0,0 +1,3 @@
1exec 2>&1
2x='@'
3echo 42:$((1?4:x,20*2+2))
diff --git a/shell/hush_test/hush-arith/arith-ternary-preincr.right b/shell/hush_test/hush-arith/arith-ternary-preincr.right
new file mode 100644
index 000000000..6644d86bf
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary-preincr.right
@@ -0,0 +1 @@
42:42
diff --git a/shell/hush_test/hush-arith/arith-ternary-preincr.tests b/shell/hush_test/hush-arith/arith-ternary-preincr.tests
new file mode 100755
index 000000000..3985c7079
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary-preincr.tests
@@ -0,0 +1,3 @@
1exec 2>&1
2x='@'
3echo 42:$((1?42:++x))
diff --git a/shell/hush_test/hush-arith/arith-ternary1.right b/shell/hush_test/hush-arith/arith-ternary1.right
new file mode 100644
index 000000000..6b751d7b8
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary1.right
@@ -0,0 +1,2 @@
142:42
2a=0
diff --git a/shell/hush_test/hush-arith/arith-ternary1.tests b/shell/hush_test/hush-arith/arith-ternary1.tests
new file mode 100755
index 000000000..3532ce54d
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary1.tests
@@ -0,0 +1,5 @@
1exec 2>&1
2a=0
3# The not-taken branch should not evaluate
4echo 42:$((1 ? 42 : (a+=2)))
5echo "a=$a"
diff --git a/shell/hush_test/hush-arith/arith-ternary2.right b/shell/hush_test/hush-arith/arith-ternary2.right
new file mode 100644
index 000000000..a549b1b5c
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary2.right
@@ -0,0 +1,3 @@
16:6
2a=b=+err+
3b=6
diff --git a/shell/hush_test/hush-arith/arith-ternary2.tests b/shell/hush_test/hush-arith/arith-ternary2.tests
new file mode 100755
index 000000000..cb3163932
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary2.tests
@@ -0,0 +1,7 @@
1exec 2>&1
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/hush_test/hush-arith/arith-ternary3.right b/shell/hush_test/hush-arith/arith-ternary3.right
new file mode 100644
index 000000000..6644d86bf
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary3.right
@@ -0,0 +1 @@
42:42
diff --git a/shell/hush_test/hush-arith/arith-ternary3.tests b/shell/hush_test/hush-arith/arith-ternary3.tests
new file mode 100755
index 000000000..0bf9f3002
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary3.tests
@@ -0,0 +1,4 @@
1exec 2>&1
2# "EXPR ?..." should check _evaluated_ EXPR,
3# not its last value
4echo 42:$((1 < 1 ? -1 : 1 > 1 ? 1 : 42))
diff --git a/shell/hush_test/hush-arith/arith-ternary_nested3.right b/shell/hush_test/hush-arith/arith-ternary_nested3.right
new file mode 100644
index 000000000..1a34fde65
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary_nested3.right
@@ -0,0 +1,2 @@
142:42
2a=2:2
diff --git a/shell/hush_test/hush-arith/arith-ternary_nested3.tests b/shell/hush_test/hush-arith/arith-ternary_nested3.tests
new file mode 100755
index 000000000..b69dcc6e9
--- /dev/null
+++ b/shell/hush_test/hush-arith/arith-ternary_nested3.tests
@@ -0,0 +1,6 @@
1exec 2>&1
2x='@'
3a=2
4# After processing nested ?:, outermost ?: should still rememeber to NOT evaluate a*=2
5echo 42:$((1?0?41:42:(a*=2)))
6echo "a=2:$a"
diff --git a/shell/math.c b/shell/math.c
index a398bcb98..b1aabef9d 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -356,6 +356,11 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_
356 NUMPTR = top_of_stack; /* this decrements NUMPTR */ 356 NUMPTR = top_of_stack; /* this decrements NUMPTR */
357 top_of_stack--; /* now points to left side */ 357 top_of_stack--; /* now points to left side */
358 358
359 if (math_state->evaluation_disabled) {
360 dbg("binary op %02x skipped", op);
361 goto ret_NULL;
362 }
363
359 right_side_val = rez; 364 right_side_val = rez;
360 rez = top_of_stack->val; 365 rez = top_of_stack->val;
361 if (op == TOK_BOR || op == TOK_OR_ASSIGN) 366 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
@@ -428,6 +433,11 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_
428 } 433 }
429 } 434 }
430 435
436 if (math_state->evaluation_disabled) {
437 dbg("unary op %02x skipped", op);
438 goto ret_NULL;
439 }
440
431 if (is_assign_op(op)) { 441 if (is_assign_op(op)) {
432 char buf[sizeof(arith_t)*3 + 2]; 442 char buf[sizeof(arith_t)*3 + 2];
433 443
@@ -446,6 +456,7 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_
446 } 456 }
447 457
448 top_of_stack->val = rez; 458 top_of_stack->val = rez;
459 ret_NULL:
449 /* Erase var name, it is just a number now */ 460 /* Erase var name, it is just a number now */
450 top_of_stack->var_name = NULL; 461 top_of_stack->var_name = NULL;
451 return NULL; 462 return NULL;
@@ -594,8 +605,10 @@ static arith_t strto_arith_t(const char *nptr, char **endptr)
594static arith_t 605static arith_t
595evaluate_string(arith_state_t *math_state, const char *expr) 606evaluate_string(arith_state_t *math_state, const char *expr)
596{ 607{
608#define EVAL_DISABLED ((unsigned long long)math_state->evaluation_disabled)
609#define TOP_BIT_ULL ((unsigned long long)LLONG_MAX + 1)
597 operator lasttok; 610 operator lasttok;
598 const char *errmsg; 611 const char *errmsg = NULL;
599 const char *start_expr = expr = skip_whitespace(expr); 612 const char *start_expr = expr = skip_whitespace(expr);
600 unsigned expr_len = strlen(expr) + 2; 613 unsigned expr_len = strlen(expr) + 2;
601 /* Stack of integers/names */ 614 /* Stack of integers/names */
@@ -614,7 +627,6 @@ evaluate_string(arith_state_t *math_state, const char *expr)
614 /* Start with a left paren */ 627 /* Start with a left paren */
615 dbg("(%d) op:TOK_LPAREN", (int)(opstackptr - opstack)); 628 dbg("(%d) op:TOK_LPAREN", (int)(opstackptr - opstack));
616 *opstackptr++ = lasttok = TOK_LPAREN; 629 *opstackptr++ = lasttok = TOK_LPAREN;
617 errmsg = NULL;
618 630
619 while (1) { 631 while (1) {
620 const char *p; 632 const char *p;
@@ -653,19 +665,26 @@ evaluate_string(arith_state_t *math_state, const char *expr)
653 p = endofname(expr); 665 p = endofname(expr);
654 if (p != expr) { 666 if (p != expr) {
655 /* Name */ 667 /* Name */
656 size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ 668 if (!math_state->evaluation_disabled) {
657 numstackptr->var_name = alloca(var_name_size); 669 size_t var_name_size = (p - expr) + 1; /* +1 for NUL */
658 safe_strncpy(numstackptr->var_name, expr, var_name_size); 670 numstackptr->var_name = alloca(var_name_size);
659 dbg("[%d] var:'%s'", (int)(numstackptr - numstack), numstackptr->var_name); 671 safe_strncpy(numstackptr->var_name, expr, var_name_size);
660 expr = skip_whitespace(p); 672 dbg("[%d] var:'%s'", (int)(numstackptr - numstack), numstackptr->var_name);
661 /* If it is not followed by "=" operator... */ 673 expr = skip_whitespace(p);
662 if (expr[0] != '=' /* not "=..." */ 674 /* If it is not followed by "=" operator... */
663 || expr[1] == '=' /* or "==..." */ 675 if (expr[0] != '=' /* not "=..." */
664 ) { 676 || expr[1] == '=' /* or "==..." */
665 /* Evaluate variable to value */ 677 ) {
666 errmsg = arith_lookup_val(math_state, numstackptr); 678 /* Evaluate variable to value */
667 if (errmsg) 679 errmsg = arith_lookup_val(math_state, numstackptr);
668 goto err_with_custom_msg; 680 if (errmsg)
681 goto err_with_custom_msg;
682 }
683 } else {
684 dbg("[%d] var:IGNORED", (int)(numstackptr - numstack));
685 numstackptr->var_name = NULL;
686 numstackptr->val = 0; //paranoia, probably not needed
687 expr = p;
669 } 688 }
670 push_num: 689 push_num:
671 numstackptr++; 690 numstackptr++;
@@ -814,10 +833,11 @@ evaluate_string(arith_state_t *math_state, const char *expr)
814 * pop prev_op 833 * pop prev_op
815 * if can't evaluate prev_op (it is lower precedence than op): 834 * if can't evaluate prev_op (it is lower precedence than op):
816 * push prev_op back 835 * push prev_op back
817 * goto P 836 * goto C
818 * evaluate prev_op on top of numstack 837 * evaluate prev_op on top of numstack
819 * P: push op 838 * C:if op is "?": check result, set disable flag if needed
820 * N: loop to parse the rest of string 839 * push op
840 * N:loop to parse the rest of string
821 */ 841 */
822 while (opstackptr != opstack) { 842 while (opstackptr != opstack) {
823 operator prev_op = *--opstackptr; 843 operator prev_op = *--opstackptr;
@@ -840,7 +860,7 @@ evaluate_string(arith_state_t *math_state, const char *expr)
840 ) { 860 ) {
841 /* ...x~y@. push @ on opstack */ 861 /* ...x~y@. push @ on opstack */
842 opstackptr++; /* undo removal of ~ op */ 862 opstackptr++; /* undo removal of ~ op */
843 goto push_op; 863 goto check_cond;
844 } 864 }
845 /* else: ...x~y@. Evaluate x~y, replace it on stack with result. Then repeat */ 865 /* else: ...x~y@. Evaluate x~y, replace it on stack with result. Then repeat */
846 } 866 }
@@ -863,21 +883,41 @@ dbg(" numstack:%d val:%lld '%s'", (int)(numstackptr - numstack), numstackptr[
863 errmsg = "malformed ?: operator"; 883 errmsg = "malformed ?: operator";
864 goto err_with_custom_msg; 884 goto err_with_custom_msg;
865 } 885 }
886 /* Example: a=1?2:3,a. We just executed ":".
887 * Prevent assignment from being still disabled.
888 */
889 math_state->evaluation_disabled >>= 1;
890 dbg("':' executed: evaluation_disabled=%llx (restored)", EVAL_DISABLED);
866 } 891 }
867 } /* while (opstack not empty) */ 892 } /* while (opstack not empty) */
868 if (op == TOK_RPAREN) /* unpaired RPAREN? */ 893 if (op == TOK_RPAREN) /* unpaired RPAREN? */
869 goto err; 894 goto err;
870 } 895 check_cond:
896 if (op == TOK_CONDITIONAL) {
897 /* We know the value of EXPR in "EXPR ? ..."
898 * Should we stop evaluating now? */
899 if (math_state->evaluation_disabled & TOP_BIT_ULL)
900 goto err; /* >63 levels of ?: nesting not supported */
901 math_state->evaluation_disabled <<= 1;
902 if (numstackptr[-1].val == 0)
903 math_state->evaluation_disabled |= 1;
904 dbg("'?' entered: evaluation_disabled=%llx", EVAL_DISABLED);
905 }
906 } /* if */
871 /* else: LPAREN or UNARY: push it on opstack */ 907 /* else: LPAREN or UNARY: push it on opstack */
872 push_op: 908
873 /* Push this operator to opstack */ 909 /* Push this operator to opstack */
874 dbg("(%d) op:%02x insert_op:%02x", (int)(opstackptr - opstack), op, insert_op); 910 dbg("(%d) op:%02x insert_op:%02x", (int)(opstackptr - opstack), op, insert_op);
875 *opstackptr++ = lasttok = op; 911 *opstackptr++ = lasttok = op;
876 next: ; 912 next:
877 if (insert_op != 0xff) { 913 if (insert_op != 0xff) {
878 op = insert_op; 914 op = insert_op;
879 insert_op = 0xff; 915 insert_op = 0xff;
880 dbg("inserting %02x", op); 916 dbg("inserting %02x", op);
917 if (op == TOK_CONDITIONAL_SEP) {
918 math_state->evaluation_disabled ^= 1;
919 dbg("':' entered: evaluation_disabled=%llx (negated)", EVAL_DISABLED);
920 }
881 goto tok_found1; 921 goto tok_found1;
882 } 922 }
883 } /* while (1) */ 923 } /* while (1) */
@@ -896,6 +936,7 @@ arith(arith_state_t *math_state, const char *expr)
896{ 936{
897 math_state->errmsg = NULL; 937 math_state->errmsg = NULL;
898 math_state->list_of_recursed_names = NULL; 938 math_state->list_of_recursed_names = NULL;
939 math_state->evaluation_disabled = 0;
899 return evaluate_string(math_state, expr); 940 return evaluate_string(math_state, expr);
900} 941}
901 942
diff --git a/shell/math.h b/shell/math.h
index 41ef6e8df..452ddaddd 100644
--- a/shell/math.h
+++ b/shell/math.h
@@ -73,13 +73,12 @@ typedef long arith_t;
73 73
74typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name); 74typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name);
75typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val); 75typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val);
76//typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name);
77 76
78typedef struct arith_state_t { 77typedef struct arith_state_t {
79 const char *errmsg; 78 const char *errmsg;
80 arith_var_lookup_t lookupvar; 79 arith_var_lookup_t lookupvar;
81 arith_var_set_t setvar; 80 arith_var_set_t setvar;
82// arith_var_endofname_t endofname; 81 uint64_t evaluation_disabled;
83 void *list_of_recursed_names; 82 void *list_of_recursed_names;
84} arith_state_t; 83} arith_state_t;
85 84