From 03ad7ae08189ed88dd7e0fcb6c6001fbf3b12efb Mon Sep 17 00:00:00 2001 From: Christoph Schulz Date: Tue, 20 Nov 2018 17:45:52 +0100 Subject: ash: reset tokpushback before prompting while parsing heredoc The parser reads from an already freed memory location, thereby causing unpredictable results, in the following situation: - ENABLE_ASH_EXPAND_PRMT is enabled - heredoc is being parsed - command substitution is used within heredoc Examples where this bug crops up are (PS2 is set to "> "): $ cat < `echo abc` > EOF -sh: O: not found $ cat < $(echo abc) > EOF -sh: {garbage}: not found The presumable reason is that setprompt_if() causes a nested expansion when ENABLE_ASH_EXPAND_PRMT is enabled, therefore leaving "wordtext" in an unusable state. However, when parseheredoc() is called, "tokpushback" is non-zero, which causes the next call to xxreadtoken() to return TWORD, causing the caller to use the invalid "wordtoken" instead of reading the next valid token. The call chain is: list() -> peektoken() [sets tokpushback to 1] -> parseheredoc() -> setprompt_if() -> pushstackmark() -> expandstr() -> readtoken1() [sets lasttoken to TWORD, wordtoken points to expanded prompt] -> popstackmark() [invalidates wordtoken, leaves lasttoken as is] -> readtoken1() -> ...parsebackq -> list() -> andor() -> pipeline() -> readtoken() -> xxreadtoken() [tokpushback non-zero, reuse lasttoken and wordtext] Note that in almost all other contexts, each call to setprompt_if() is preceded by setting "tokpushback" to zero. One exception is "oldstyle" backquote parsing in readtoken1(), but there "tokpushback" is reset afterwards. The other exception is nlprompt(), but this function is only used within readtoken1() (but in contexts where no nested calls to xxreadtoken() occur) and xxreadtoken() (where "tokpushback" is guaranteed to be zero). function old new delta parseheredoc 124 131 +7 Signed-off-by: Christoph Schulz Signed-off-by: Denys Vlasenko --- shell/ash.c | 1 + 1 file changed, 1 insertion(+) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 44b3569..04e4006 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -12961,6 +12961,7 @@ parseheredoc(void) heredoclist = NULL; while (here) { + tokpushback = 0; setprompt_if(needprompt, 2); readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX, here->eofmark, here->striptabs); -- cgit v1.1