[PATCH v2] ash: fix out-of-bounds read in ifsbreakup()
Sanghyun Park via busybox <[email protected]> Tue, 16 Jun 2026 23:43:40 +0900
| Newsgroups | gmane.linux.busybox |
|---|---|
| Message-ID | <[email protected]> |
ifsfree() does not only release allocated ifsregion nodes; it also clears the global IFS region state used by ifsbreakup(). If argstr() raises an error while expanding an argument, ash longjmps out of expandarg() before that cleanup runs, leaving stale IFS split offsets behind. A later expansion can reuse the stack for a shorter string. ifsbreakup() then sees the stale IFS state, trusts the old offsets, and can walk past the current stack block before dereferencing p. Limit each saved IFS-region scan to the current stack block, skip stale regions that are now outside that bound, and make the final empty-string check use the same bound. Signed-off-by: Sanghyun Park <[email protected]> --- v2: - Drop the unsupported CTLESC-at-region-end explanation and guards. - Explain the stale IFS region state left after longjmp from argstr(). - Keep the fix scoped to bounding stale saved regions to the current stack block. v1: https://lists.busybox.net/pipermail/busybox/2026-June/092353.html shell/ash.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index fb887f3..366559a 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,11 +6431,18 @@ 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) p++; @@ -6456,7 +6466,7 @@ ifsbreakup(char *string, struct arglist *arglist) p++; if (!nulonly) { for (;;) { - if (p >= string + ifsp->endoff) { + if (p >= end) { break; } q = p; @@ -6486,7 +6496,7 @@ ifsbreakup(char *string, struct arglist *arglist) goto add; } - if (!*start) + if (start >= stack_end || !*start) return; add: -- 2.48.1