diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2021-06-16 09:18:08 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2021-06-16 09:21:40 +0200 |
commit | a885ce1af05c4eaa5ebcf883cb3da3433ca1c48b (patch) | |
tree | 783574a66ea5a5af83d047b2a2c1feba42230159 /editors | |
parent | 83a4967e50422867f340328d404994553e56b839 (diff) | |
download | busybox-w32-a885ce1af05c4eaa5ebcf883cb3da3433ca1c48b.tar.gz busybox-w32-a885ce1af05c4eaa5ebcf883cb3da3433ca1c48b.tar.bz2 busybox-w32-a885ce1af05c4eaa5ebcf883cb3da3433ca1c48b.zip |
awk: fix use-after-free in "$BIGNUM1 $BIGGERNUM2" concat op
Second reference to a field reallocs/moves Fields[] array, but first ref
still tries to use the element where it was before move.
function old new delta
fsrealloc 94 106 +12
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'editors')
-rw-r--r-- | editors/awk.c | 85 |
1 files changed, 71 insertions, 14 deletions
diff --git a/editors/awk.c b/editors/awk.c index b4f6a3741..48836298c 100644 --- a/editors/awk.c +++ b/editors/awk.c | |||
@@ -1745,12 +1745,22 @@ static char* qrealloc(char *b, int n, int *size) | |||
1745 | /* resize field storage space */ | 1745 | /* resize field storage space */ |
1746 | static void fsrealloc(int size) | 1746 | static void fsrealloc(int size) |
1747 | { | 1747 | { |
1748 | int i; | 1748 | int i, newsize; |
1749 | 1749 | ||
1750 | if (size >= maxfields) { | 1750 | if (size >= maxfields) { |
1751 | /* Sanity cap, easier than catering for overflows */ | ||
1752 | if (size > 0xffffff) | ||
1753 | bb_die_memory_exhausted(); | ||
1754 | |||
1751 | i = maxfields; | 1755 | i = maxfields; |
1752 | maxfields = size + 16; | 1756 | maxfields = size + 16; |
1753 | Fields = xrealloc(Fields, maxfields * sizeof(Fields[0])); | 1757 | |
1758 | newsize = maxfields * sizeof(Fields[0]); | ||
1759 | debug_printf_eval("fsrealloc: xrealloc(%p, %u)\n", Fields, newsize); | ||
1760 | Fields = xrealloc(Fields, newsize); | ||
1761 | debug_printf_eval("fsrealloc: Fields=%p..%p\n", Fields, (char*)Fields + newsize - 1); | ||
1762 | /* ^^^ did Fields[] move? debug aid for L.v getting "upstaged" by R.v in evaluate() */ | ||
1763 | |||
1754 | for (; i < maxfields; i++) { | 1764 | for (; i < maxfields; i++) { |
1755 | Fields[i].type = VF_SPECIAL; | 1765 | Fields[i].type = VF_SPECIAL; |
1756 | Fields[i].string = NULL; | 1766 | Fields[i].string = NULL; |
@@ -2614,20 +2624,30 @@ static var *evaluate(node *op, var *res) | |||
2614 | /* execute inevitable things */ | 2624 | /* execute inevitable things */ |
2615 | if (opinfo & OF_RES1) | 2625 | if (opinfo & OF_RES1) |
2616 | L.v = evaluate(op1, v1); | 2626 | L.v = evaluate(op1, v1); |
2617 | if (opinfo & OF_RES2) | ||
2618 | R.v = evaluate(op->r.n, v1+1); | ||
2619 | if (opinfo & OF_STR1) { | 2627 | if (opinfo & OF_STR1) { |
2620 | L.s = getvar_s(L.v); | 2628 | L.s = getvar_s(L.v); |
2621 | debug_printf_eval("L.s:'%s'\n", L.s); | 2629 | debug_printf_eval("L.s:'%s'\n", L.s); |
2622 | } | 2630 | } |
2623 | if (opinfo & OF_STR2) { | ||
2624 | R.s = getvar_s(R.v); | ||
2625 | debug_printf_eval("R.s:'%s'\n", R.s); | ||
2626 | } | ||
2627 | if (opinfo & OF_NUM1) { | 2631 | if (opinfo & OF_NUM1) { |
2628 | L_d = getvar_i(L.v); | 2632 | L_d = getvar_i(L.v); |
2629 | debug_printf_eval("L_d:%f\n", L_d); | 2633 | debug_printf_eval("L_d:%f\n", L_d); |
2630 | } | 2634 | } |
2635 | /* NB: Must get string/numeric values of L (done above) | ||
2636 | * _before_ evaluate()'ing R.v: if both L and R are $NNNs, | ||
2637 | * and right one is large, then L.v points to Fields[NNN1], | ||
2638 | * second evaluate() reallocates and moves (!) Fields[], | ||
2639 | * R.v points to Fields[NNN2] but L.v now points to freed mem! | ||
2640 | * (Seen trying to evaluate "$444 $44444") | ||
2641 | */ | ||
2642 | if (opinfo & OF_RES2) { | ||
2643 | R.v = evaluate(op->r.n, v1+1); | ||
2644 | //TODO: L.v may be invalid now, set L.v to NULL to catch bugs? | ||
2645 | //L.v = NULL; | ||
2646 | } | ||
2647 | if (opinfo & OF_STR2) { | ||
2648 | R.s = getvar_s(R.v); | ||
2649 | debug_printf_eval("R.s:'%s'\n", R.s); | ||
2650 | } | ||
2631 | 2651 | ||
2632 | debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK)); | 2652 | debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK)); |
2633 | switch (XC(opinfo & OPCLSMASK)) { | 2653 | switch (XC(opinfo & OPCLSMASK)) { |
@@ -2636,6 +2656,7 @@ static var *evaluate(node *op, var *res) | |||
2636 | 2656 | ||
2637 | /* test pattern */ | 2657 | /* test pattern */ |
2638 | case XC( OC_TEST ): | 2658 | case XC( OC_TEST ): |
2659 | debug_printf_eval("TEST\n"); | ||
2639 | if ((op1->info & OPCLSMASK) == OC_COMMA) { | 2660 | if ((op1->info & OPCLSMASK) == OC_COMMA) { |
2640 | /* it's range pattern */ | 2661 | /* it's range pattern */ |
2641 | if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) { | 2662 | if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) { |
@@ -2653,25 +2674,32 @@ static var *evaluate(node *op, var *res) | |||
2653 | 2674 | ||
2654 | /* just evaluate an expression, also used as unconditional jump */ | 2675 | /* just evaluate an expression, also used as unconditional jump */ |
2655 | case XC( OC_EXEC ): | 2676 | case XC( OC_EXEC ): |
2677 | debug_printf_eval("EXEC\n"); | ||
2656 | break; | 2678 | break; |
2657 | 2679 | ||
2658 | /* branch, used in if-else and various loops */ | 2680 | /* branch, used in if-else and various loops */ |
2659 | case XC( OC_BR ): | 2681 | case XC( OC_BR ): |
2682 | debug_printf_eval("BR\n"); | ||
2660 | op = istrue(L.v) ? op->a.n : op->r.n; | 2683 | op = istrue(L.v) ? op->a.n : op->r.n; |
2661 | break; | 2684 | break; |
2662 | 2685 | ||
2663 | /* initialize for-in loop */ | 2686 | /* initialize for-in loop */ |
2664 | case XC( OC_WALKINIT ): | 2687 | case XC( OC_WALKINIT ): |
2688 | debug_printf_eval("WALKINIT\n"); | ||
2665 | hashwalk_init(L.v, iamarray(R.v)); | 2689 | hashwalk_init(L.v, iamarray(R.v)); |
2666 | break; | 2690 | break; |
2667 | 2691 | ||
2668 | /* get next array item */ | 2692 | /* get next array item */ |
2669 | case XC( OC_WALKNEXT ): | 2693 | case XC( OC_WALKNEXT ): |
2694 | debug_printf_eval("WALKNEXT\n"); | ||
2670 | op = hashwalk_next(L.v) ? op->a.n : op->r.n; | 2695 | op = hashwalk_next(L.v) ? op->a.n : op->r.n; |
2671 | break; | 2696 | break; |
2672 | 2697 | ||
2673 | case XC( OC_PRINT ): | 2698 | case XC( OC_PRINT ): |
2674 | case XC( OC_PRINTF ): { | 2699 | debug_printf_eval("PRINT /\n"); |
2700 | case XC( OC_PRINTF ): | ||
2701 | debug_printf_eval("PRINTF\n"); | ||
2702 | { | ||
2675 | FILE *F = stdout; | 2703 | FILE *F = stdout; |
2676 | IF_FEATURE_AWK_GNU_EXTENSIONS(int len;) | 2704 | IF_FEATURE_AWK_GNU_EXTENSIONS(int len;) |
2677 | 2705 | ||
@@ -2726,22 +2754,28 @@ static var *evaluate(node *op, var *res) | |||
2726 | /* case XC( OC_DELETE ): - moved to happen before arg evaluation */ | 2754 | /* case XC( OC_DELETE ): - moved to happen before arg evaluation */ |
2727 | 2755 | ||
2728 | case XC( OC_NEWSOURCE ): | 2756 | case XC( OC_NEWSOURCE ): |
2757 | debug_printf_eval("NEWSOURCE\n"); | ||
2729 | g_progname = op->l.new_progname; | 2758 | g_progname = op->l.new_progname; |
2730 | break; | 2759 | break; |
2731 | 2760 | ||
2732 | case XC( OC_RETURN ): | 2761 | case XC( OC_RETURN ): |
2762 | debug_printf_eval("RETURN\n"); | ||
2733 | copyvar(res, L.v); | 2763 | copyvar(res, L.v); |
2734 | break; | 2764 | break; |
2735 | 2765 | ||
2736 | case XC( OC_NEXTFILE ): | 2766 | case XC( OC_NEXTFILE ): |
2767 | debug_printf_eval("NEXTFILE\n"); | ||
2737 | nextfile = TRUE; | 2768 | nextfile = TRUE; |
2738 | case XC( OC_NEXT ): | 2769 | case XC( OC_NEXT ): |
2770 | debug_printf_eval("NEXT\n"); | ||
2739 | nextrec = TRUE; | 2771 | nextrec = TRUE; |
2740 | case XC( OC_DONE ): | 2772 | case XC( OC_DONE ): |
2773 | debug_printf_eval("DONE\n"); | ||
2741 | clrvar(res); | 2774 | clrvar(res); |
2742 | break; | 2775 | break; |
2743 | 2776 | ||
2744 | case XC( OC_EXIT ): | 2777 | case XC( OC_EXIT ): |
2778 | debug_printf_eval("EXIT\n"); | ||
2745 | awk_exit(L_d); | 2779 | awk_exit(L_d); |
2746 | 2780 | ||
2747 | /* -- recursive node type -- */ | 2781 | /* -- recursive node type -- */ |
@@ -2761,15 +2795,18 @@ static var *evaluate(node *op, var *res) | |||
2761 | break; | 2795 | break; |
2762 | 2796 | ||
2763 | case XC( OC_IN ): | 2797 | case XC( OC_IN ): |
2798 | debug_printf_eval("IN\n"); | ||
2764 | setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0); | 2799 | setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0); |
2765 | break; | 2800 | break; |
2766 | 2801 | ||
2767 | case XC( OC_REGEXP ): | 2802 | case XC( OC_REGEXP ): |
2803 | debug_printf_eval("REGEXP\n"); | ||
2768 | op1 = op; | 2804 | op1 = op; |
2769 | L.s = getvar_s(intvar[F0]); | 2805 | L.s = getvar_s(intvar[F0]); |
2770 | goto re_cont; | 2806 | goto re_cont; |
2771 | 2807 | ||
2772 | case XC( OC_MATCH ): | 2808 | case XC( OC_MATCH ): |
2809 | debug_printf_eval("MATCH\n"); | ||
2773 | op1 = op->r.n; | 2810 | op1 = op->r.n; |
2774 | re_cont: | 2811 | re_cont: |
2775 | { | 2812 | { |
@@ -2795,6 +2832,7 @@ static var *evaluate(node *op, var *res) | |||
2795 | break; | 2832 | break; |
2796 | 2833 | ||
2797 | case XC( OC_TERNARY ): | 2834 | case XC( OC_TERNARY ): |
2835 | debug_printf_eval("TERNARY\n"); | ||
2798 | if ((op->r.n->info & OPCLSMASK) != OC_COLON) | 2836 | if ((op->r.n->info & OPCLSMASK) != OC_COLON) |
2799 | syntax_error(EMSG_POSSIBLE_ERROR); | 2837 | syntax_error(EMSG_POSSIBLE_ERROR); |
2800 | res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res); | 2838 | res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res); |
@@ -2803,6 +2841,7 @@ static var *evaluate(node *op, var *res) | |||
2803 | case XC( OC_FUNC ): { | 2841 | case XC( OC_FUNC ): { |
2804 | var *vbeg, *v; | 2842 | var *vbeg, *v; |
2805 | const char *sv_progname; | 2843 | const char *sv_progname; |
2844 | debug_printf_eval("FUNC\n"); | ||
2806 | 2845 | ||
2807 | /* The body might be empty, still has to eval the args */ | 2846 | /* The body might be empty, still has to eval the args */ |
2808 | if (!op->r.n->info && !op->r.f->body.first) | 2847 | if (!op->r.n->info && !op->r.f->body.first) |
@@ -2832,7 +2871,10 @@ static var *evaluate(node *op, var *res) | |||
2832 | } | 2871 | } |
2833 | 2872 | ||
2834 | case XC( OC_GETLINE ): | 2873 | case XC( OC_GETLINE ): |
2835 | case XC( OC_PGETLINE ): { | 2874 | debug_printf_eval("GETLINE /\n"); |
2875 | case XC( OC_PGETLINE ): | ||
2876 | debug_printf_eval("PGETLINE\n"); | ||
2877 | { | ||
2836 | rstream *rsm; | 2878 | rstream *rsm; |
2837 | int i; | 2879 | int i; |
2838 | 2880 | ||
@@ -2873,6 +2915,7 @@ static var *evaluate(node *op, var *res) | |||
2873 | /* simple builtins */ | 2915 | /* simple builtins */ |
2874 | case XC( OC_FBLTIN ): { | 2916 | case XC( OC_FBLTIN ): { |
2875 | double R_d = R_d; /* for compiler */ | 2917 | double R_d = R_d; /* for compiler */ |
2918 | debug_printf_eval("FBLTIN\n"); | ||
2876 | 2919 | ||
2877 | switch (opn) { | 2920 | switch (opn) { |
2878 | case F_in: | 2921 | case F_in: |
@@ -2986,14 +3029,18 @@ static var *evaluate(node *op, var *res) | |||
2986 | } | 3029 | } |
2987 | 3030 | ||
2988 | case XC( OC_BUILTIN ): | 3031 | case XC( OC_BUILTIN ): |
3032 | debug_printf_eval("BUILTIN\n"); | ||
2989 | res = exec_builtin(op, res); | 3033 | res = exec_builtin(op, res); |
2990 | break; | 3034 | break; |
2991 | 3035 | ||
2992 | case XC( OC_SPRINTF ): | 3036 | case XC( OC_SPRINTF ): |
3037 | debug_printf_eval("SPRINTF\n"); | ||
2993 | setvar_p(res, awk_printf(op1, NULL)); | 3038 | setvar_p(res, awk_printf(op1, NULL)); |
2994 | break; | 3039 | break; |
2995 | 3040 | ||
2996 | case XC( OC_UNARY ): { | 3041 | case XC( OC_UNARY ): |
3042 | debug_printf_eval("UNARY\n"); | ||
3043 | { | ||
2997 | double Ld, R_d; | 3044 | double Ld, R_d; |
2998 | 3045 | ||
2999 | Ld = R_d = getvar_i(R.v); | 3046 | Ld = R_d = getvar_i(R.v); |
@@ -3023,7 +3070,9 @@ static var *evaluate(node *op, var *res) | |||
3023 | break; | 3070 | break; |
3024 | } | 3071 | } |
3025 | 3072 | ||
3026 | case XC( OC_FIELD ): { | 3073 | case XC( OC_FIELD ): |
3074 | debug_printf_eval("FIELD\n"); | ||
3075 | { | ||
3027 | int i = (int)getvar_i(R.v); | 3076 | int i = (int)getvar_i(R.v); |
3028 | if (i < 0) | 3077 | if (i < 0) |
3029 | syntax_error(EMSG_NEGATIVE_FIELD); | 3078 | syntax_error(EMSG_NEGATIVE_FIELD); |
@@ -3040,8 +3089,10 @@ static var *evaluate(node *op, var *res) | |||
3040 | 3089 | ||
3041 | /* concatenation (" ") and index joining (",") */ | 3090 | /* concatenation (" ") and index joining (",") */ |
3042 | case XC( OC_CONCAT ): | 3091 | case XC( OC_CONCAT ): |
3092 | debug_printf_eval("CONCAT /\n"); | ||
3043 | case XC( OC_COMMA ): { | 3093 | case XC( OC_COMMA ): { |
3044 | const char *sep = ""; | 3094 | const char *sep = ""; |
3095 | debug_printf_eval("COMMA\n"); | ||
3045 | if ((opinfo & OPCLSMASK) == OC_COMMA) | 3096 | if ((opinfo & OPCLSMASK) == OC_COMMA) |
3046 | sep = getvar_s(intvar[SUBSEP]); | 3097 | sep = getvar_s(intvar[SUBSEP]); |
3047 | setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s)); | 3098 | setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s)); |
@@ -3049,17 +3100,22 @@ static var *evaluate(node *op, var *res) | |||
3049 | } | 3100 | } |
3050 | 3101 | ||
3051 | case XC( OC_LAND ): | 3102 | case XC( OC_LAND ): |
3103 | debug_printf_eval("LAND\n"); | ||
3052 | setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0); | 3104 | setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0); |
3053 | break; | 3105 | break; |
3054 | 3106 | ||
3055 | case XC( OC_LOR ): | 3107 | case XC( OC_LOR ): |
3108 | debug_printf_eval("LOR\n"); | ||
3056 | setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n)); | 3109 | setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n)); |
3057 | break; | 3110 | break; |
3058 | 3111 | ||
3059 | case XC( OC_BINARY ): | 3112 | case XC( OC_BINARY ): |
3060 | case XC( OC_REPLACE ): { | 3113 | debug_printf_eval("BINARY /\n"); |
3114 | case XC( OC_REPLACE ): | ||
3115 | debug_printf_eval("REPLACE\n"); | ||
3116 | { | ||
3061 | double R_d = getvar_i(R.v); | 3117 | double R_d = getvar_i(R.v); |
3062 | debug_printf_eval("BINARY/REPLACE: R_d:%f opn:%c\n", R_d, opn); | 3118 | debug_printf_eval("R_d:%f opn:%c\n", R_d, opn); |
3063 | switch (opn) { | 3119 | switch (opn) { |
3064 | case '+': | 3120 | case '+': |
3065 | L_d += R_d; | 3121 | L_d += R_d; |
@@ -3095,6 +3151,7 @@ static var *evaluate(node *op, var *res) | |||
3095 | case XC( OC_COMPARE ): { | 3151 | case XC( OC_COMPARE ): { |
3096 | int i = i; /* for compiler */ | 3152 | int i = i; /* for compiler */ |
3097 | double Ld; | 3153 | double Ld; |
3154 | debug_printf_eval("COMPARE\n"); | ||
3098 | 3155 | ||
3099 | if (is_numeric(L.v) && is_numeric(R.v)) { | 3156 | if (is_numeric(L.v) && is_numeric(R.v)) { |
3100 | Ld = getvar_i(L.v) - getvar_i(R.v); | 3157 | Ld = getvar_i(L.v) - getvar_i(R.v); |