[PATCH] parser: Skip parsing of old-style command substitution here-doc marker

Herbert Xu <[email protected]> Sat, 13 Sep 2025 17:59:28 +0800
Newsgroups org.kernel.vger.dash
Message-ID <[email protected]>
On Fri, Aug 29, 2025 at 07:40:16PM -0700, Nathan Mills wrote:
>
> * Crash #0 is a null-pointer-dereference. I haven't found the fix yet
> but it is probably simple.
> 
> redir->nhere.doc is NULL. The global variable **heredoclist** is also
> NULL and parseheredoc does nothing when heredoclist is NULL.
> 
> 1. readtoken1
> 2. parseheredoc (heredoclist equals heredoc which is the same as the
> value in the next step)
> 3. parseheredoc (heredoclist is NULL, heredoc is 0x5555560bb6a8 <stackbase+232>)
> 4. parsefname
> 5. parseheredoc (heredoclist->here->nhere.doc->narg.text equals the
> empty string)
> 6. **SIGSEGV** openhere (redir->nhere.doc is **NULL**)
> 
> ** Crash #0: null pointer dereference **
> 
> Base64'd
> 
> dmVzjHdyPDwAAACAYAAAAHd3cjw8AAAAgGAAAACA/38zZGlsaQ==
> 
> Minimized:
> 
> src/dash -c $(echo -c "<<\`<<\0\`")

This is caused by the unnecessary recursive parsing of old-style
command substitution which then gets confused by the embedded
here-document.

Fix this by skipping the recursive parsing of command substitution
if it is old-style and a here-document marker.

Reported-by: Nathan Mills <[email protected]>
Fixes: 7a11b3e330a3 ("parser: Extend coverage of CHKEOFMARK")
Signed-off-by: Herbert Xu <[email protected]>

diff --git a/src/parser.c b/src/parser.c
index a3b004b..975cf89 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -1510,7 +1510,6 @@ parsebackq: {
 	int uninitialized_var(saveprompt);
 	struct heredoc *saveheredoclist;
 	struct nodelist **nlpp;
-	size_t psavelen;
 	size_t savelen;
 	union node *n;
 	unsigned ml;
@@ -1521,24 +1520,28 @@ parsebackq: {
 		STADJUST(oldstyle - 1, out);
 		out[-1] = CTLBACKQ;
 	}
-	str = stackblock();
-	savelen = out - (char *)stackblock();
-	grabstackblock(savelen);
+	if (!chkeofmark || !oldstyle) {
+		str = stackblock();
+		savelen = out - (char *)stackblock();
+		grabstackblock(savelen);
+		STARTSTACKSTR(out);
+	}
         if (oldstyle) {
                 /* We must read until the closing backquote, giving special
                    treatment to some slashes, and then push the string and
                    reread it as input, interpreting it normally.  */
-                char *pout;
+		bool done = false;
+		char *pout = out;
                 int pc;
 
-                STARTSTACKSTR(pout);
-		for (;;) {
+		while (!done) {
 			if (needprompt) {
 				setprompt(2);
 			}
 			switch (pc = pgetc_eatbnl()) {
 			case '`':
-				goto done;
+				done = true;
+				break;
 
 			case '\\':
                                 pc = pgetc();
@@ -1564,9 +1567,11 @@ parsebackq: {
 			}
 			STPUTC(pc, pout);
                 }
-done:
-                STPUTC('\0', pout);
-                psavelen = pout - (char *)stackblock();
+		if (chkeofmark) {
+			out = pout;
+			goto parsebackq_oldreturn;
+		}
+		pout[-1] = 0;
 		pstr = grabstackstr(pout);
 		setinputstring(pstr);
         }
@@ -1608,10 +1613,6 @@ done:
 		 * parsing.
 		 */
 		tokpushback = 0;
-		if (chkeofmark) {
-			pstr[psavelen - 1] = '`';
-			out = stnputs(pstr, psavelen, out);
-		}
 		goto parsebackq_oldreturn;
 	} else {
 		if (chkeofmark) {
-- 
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt