[PATCH] ash: fix out-of-bounds read in ifsbreakup()

Sanghyun Park via busybox <[email protected]> Tue, 16 Jun 2026 12:19:01 +0900
Newsgroups gmane.linux.busybox
Message-ID <[email protected]>
ifsbreakup() skips over CTLESC before testing the following byte against
IFS. If CTLESC is the last byte in the recorded region, the scan
advances to the region end and can dereference one byte past it.

The recorded region can also be stale after error unwinding, so cap each
scan to the current stack block before walking it and make the final
empty-string check respect the same bound.

Signed-off-by: Sanghyun Park <[email protected]>
---
 shell/ash.c | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/shell/ash.c b/shell/ash.c
index fb887f3..cda6c85 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -6412,13 +6412,16 @@ ifsbreakup(char *string, struct arglist *arglist)
 	struct ifsregion *ifsp;
 	struct strlist *sp;
 	char *start;
+	char *end;
 	char *p;
 	char *q;
+	char *stack_end;
 	const char *ifs, *realifs;
 	int ifsspc;
 	int nulonly;
 
 	start = string;
+	stack_end = (char *)stackblock() + stackblocksize();
 	if (ifslastp != NULL) {
 		ifsspc = 0;
 		nulonly = 0;
@@ -6428,14 +6431,26 @@ ifsbreakup(char *string, struct arglist *arglist)
 			int afternul;
 
 			p = string + ifsp->begoff;
+			end = string + ifsp->endoff;
+			if (end > stack_end)
+				end = stack_end;
+			if (p >= end) {
+				ifsp = ifsp->next;
+				continue;
+			}
 			afternul = nulonly;
 			nulonly = ifsp->nulonly;
 			ifs = nulonly ? nullstr : realifs;
 			ifsspc = 0;
-			while (p < string + ifsp->endoff) {
+			while (p < end) {
 				q = p;
-				if ((unsigned char)*p == CTLESC)
+				if ((unsigned char)*p == CTLESC) {
 					p++;
+					if (p >= end) {
+						p = q;
+						break;
+					}
+				}
 				if (!strchr(ifs, *p)) {
 					p++;
 					continue;
@@ -6456,12 +6471,17 @@ ifsbreakup(char *string, struct arglist *arglist)
 				p++;
 				if (!nulonly) {
 					for (;;) {
-						if (p >= string + ifsp->endoff) {
+						if (p >= end) {
 							break;
 						}
 						q = p;
-						if ((unsigned char)*p == CTLESC)
+						if ((unsigned char)*p == CTLESC) {
 							p++;
+							if (p >= end) {
+								p = q;
+								break;
+							}
+						}
 						if (strchr(ifs, *p) == NULL) {
 							p = q;
 							break;
@@ -6486,7 +6506,7 @@ ifsbreakup(char *string, struct arglist *arglist)
 			goto add;
 	}
 
-	if (!*start)
+	if (start >= stack_end || !*start)
 		return;
 
  add:
-- 
2.48.1