Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix infinite loop on arithmetic for syntzx error on interactive
Marc Wilson (@posguy99) reports: > A completely errant paste that wasn't meant for the terminal > window exposed this... > > Reproducer: > > $ for ((i = 5 , i = 0, --i)) > -ksh: syntax error: `{' unmatched > -ksh: syntax error: `{' unmatched > -ksh: syntax error: `{' unmatched > -ksh: syntax error: `{' unmatched > -ksh: syntax error: `{' unmatched > > Continues forever until you kill the shell. Simplest reproducer is 'for(())', with identical symptoms. In scripts, the symptom is less severe, but the error message is still incorrect as above. For example: $ ksh -c 'for(())' ksh: syntax error at line 1: `{' unmatched The expected error is: ksh: syntax error at line 1: `))' unexpected ksh93u+ and ksh2020 are also broken, just not in the same way, and harder to reproduce. The current infinite-loop manifestation of the bug was introduced in commit f8f2c4b. Analysis: what happens is this: 1. arithfor() in parse.c is called to parse the arithmetic for loop 2. arithfor() saves current input stream and opens a new input stream to separate each of the three expressions in (( ... )) 3. on line 758, arithfor() calls sh_lexskip() to find the next ';' 4. in lex.c line 1749, sh_lexskip() calls the main lexer function, sh_lex() 5. in lex.c line 375, sh_lex() calls sh_syntax() to throw the syntax error in the reproducer 6. sh_syntax() calls errormsg() from libast, which causes a longjmp straight back to the prompt, without giving arithfor() a chance to restore the input stream that it saved in step 2. This is the bug. We can get all sorts of unpredictable/undefined behaviour that way. I can think of two strategies to fix this: 1. Push context and sigsetjmp in arithfor() before calling sh_lexskip(), so that it longjmps back to arithfor() and it can restore the input stream before doing its own longjmp. This may carry a minor performance hit for nested arithmetic for loops. 2. Since arithfor() throws a syntax error itself when needed anyway (and does so after restoring the input stream), we could also find a way for sh_lex() not to throw that syntax error when called from sh_lexskip. Strategy 2 might be the best way. It just so happens that sh_lexskip sets its own flag, lp->lexd.noarg, that we can use in sh_lex() to tell if it was called from sh_lexskip(). So we could use that to prevent it from throwing the syntax error and just return instead, handing control back to arithfor(), so that it can throw the syntax error after restoring state. src/cmd/ksh93/include/shlex.h, src/cmd/ksh93/sh/lex.c: - Rename lexd.noarg flag to lexd.inlexskip. Since it will now be multi-purpose, this is clearer to the code reader. It simply means that the current sh_lex() invocation was called from sh_lexwskip(). - When reading end-of-file, do not throw a symtax error from sh_lex() if lexd.inlexskip is set, letting the caller handle it instead. This fixes the bug. Resolves: #779
- Loading branch information