From 09656b9beb583eee73b58f64e0e43c0df262c99c Mon Sep 17 00:00:00 2001
From: liuxu <liuxu156@huawei.com>
Date: Wed, 19 Jun 2024 11:32:49 +0800
Subject: [PATCH 3/4] awk: fix use-after-realloc (CVE-2021-42380), closes 15601

backport from upstream:
https://git.alpinelinux.org/aports/plain/main/busybox/0029-awk-fix-use-after-realloc-CVE-2021-42380-closes-1560.patch

Signed-off-by: liuxu <liuxu156@huawei.com>
---
 editors/awk.c | 34 ++++++++++++++++++++++++++++------
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/editors/awk.c b/editors/awk.c
index e1d510c..7a73f04 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -558,7 +558,7 @@ struct globals {
 	const char *g_progname;
 	int g_lineno;
 	int nfields;
-	int maxfields; /* used in fsrealloc() only */
+	unsigned maxfields;
 	var *Fields;
 	char *g_pos;
 	char g_saved_ch;
@@ -1965,9 +1965,9 @@ static void fsrealloc(int size)
 {
 	int i, newsize;
 
-	if (size >= maxfields) {
-		/* Sanity cap, easier than catering for overflows */
-		if (size > 0xffffff)
+	if ((unsigned)size >= maxfields) {
+		/* Sanity cap, easier than catering for over/underflows */
+		if ((unsigned)size > 0xffffff)
 			bb_die_memory_exhausted();
 
 		i = maxfields;
@@ -2925,6 +2925,7 @@ static var *evaluate(node *op, var *res)
 		uint32_t opinfo;
 		int opn;
 		node *op1;
+		var *old_Fields_ptr;
 
 		opinfo = op->info;
 		opn = (opinfo & OPNMASK);
@@ -2933,10 +2934,16 @@ static var *evaluate(node *op, var *res)
 		debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn);
 
 		/* execute inevitable things */
+		old_Fields_ptr = NULL;
 		if (opinfo & OF_RES1) {
 			if ((opinfo & OF_REQUIRED) && !op1)
 				syntax_error(EMSG_TOO_FEW_ARGS);
 			L.v = evaluate(op1, TMPVAR0);
+			/* Does L.v point to $n variable? */
+			if ((size_t)(L.v - Fields) < maxfields) {
+				/* yes, remember where Fields[] is */
+				old_Fields_ptr = Fields;
+			}
 			if (opinfo & OF_STR1) {
 				L.s = getvar_s(L.v);
 				debug_printf_eval("L.s:'%s'\n", L.s);
@@ -2955,14 +2962,29 @@ static var *evaluate(node *op, var *res)
 		 */
 		if (opinfo & OF_RES2) {
 			R.v = evaluate(op->r.n, TMPVAR1);
-			//TODO: L.v may be invalid now, set L.v to NULL to catch bugs?
-			//L.v = NULL;
+			/* Seen in $5=$$5=$0:
+			 * Evaluation of R.v ($$5=$0 expression)
+			 * made L.v ($5) invalid. It's detected here.
+			 */
+			if (old_Fields_ptr) {
+				//if (old_Fields_ptr != Fields)
+				//	debug_printf_eval("L.v moved\n");
+				L.v += Fields - old_Fields_ptr;
+			}
 			if (opinfo & OF_STR2) {
 				R.s = getvar_s(R.v);
 				debug_printf_eval("R.s:'%s'\n", R.s);
 			}
 		}
 
+		/* Must get L.s after R.v is evaluated in case it realloc's L.v.
+		 * eg: x = (v = "abc",  gsub("b", "X", v));
+		 */
+		if ((opinfo & OF_RES1) && (opinfo & OF_STR1)) {
+			L.s = getvar_s(L.v);
+			debug_printf_eval("L.s:'%s'\n", L.s);
+		}
+
 		debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK));
 		switch (XC(opinfo & OPCLSMASK)) {
 
-- 
2.43.0

