mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
Fix two more PS2/SIGINT crashing bugs (re: 3023d53b)
*** Crash 1: *** ksh crashed if the PS1 prompt contains one or more command substitutions and you enter a multi-line command substitution on the command line, then interrupt while on the PS2 prompt. $ ENV=/./dev/null /usr/local/bin/ksh -o emacs $ PS1='$(echo foo) $(echo bar) $(echo baz) ! % ' foo bar baz 16999 % echo $( > true <-- here, press Ctrl+C instead of Return Memory fault The crash occurred due to a corrupted lexer state while trying to display the PS1 prompt. Analysis: My fix for the crashing bug with Ctrl+C in commit 3023d53b is incorrect and only worked accidentally. sh_fault() is not the right place to reset the lexer state because, when we press Ctrl+C on a PS2 prompt, ksh had been waiting for input to finish lexing a multi-line command, so sh_lex() and other lexer functions are on the function call stack and will be returned to. src/cmd/ksh93/sh/fault.c: sh_fault(): - Remove incorrect SIGINT fix. src/cmd/ksh93/sh/io.c: io_prompt(): - Reset the lexer state immediately before printing every PS1 prompt. Even in situations where this is redundant it should be perfectly safe, the overhead is negligible, and it resolves this crash. It may pre-empt other problems as well. *** Crash 2: *** If an INT trap is set, and you start entering a multi-line command substitution, then press Ctrl+C on the PS2 prompt to trigger the crash, the lexer state is corrupted because the lexer is invoked to eval the trap action. A crash then occurs on entering the final ')' of the command substitution. $ trap 'echo TRAPPED' INT $ echo $( > trueTRAPPED <-- press Ctrl+C to output "TRAPPED" > ) Memory fault Technically, as SIGINT is trapped, it should not interrupt, so ksh should execute the trap, then continue with the PS2 prompt to let the user finish inputting the command. But I have been unsuccessful in many different attempts to make this work properly. I managed to get multi-line command substitutions to lex correctly by saving and restoring the lexer state, but command substitutions were still corrupted at the parser and/or execution level and I have not managed to trace the cause of that. My testing showed that all other shells interrupt the PS2 prompt and return to PS1 when the user presses Ctrl+C, even if SIGINT is trapped. I think that is a reasonable alternative, and it is something I managed to make work. src/cmd/ksh93/sh/fault.c: sh_chktrap(): - Immediately after invoking sh_trap() to run a trap action, check if we're in a PS2 prompt (sh.nextprompt == 2). If so, assume the lexer state is now overwritten. Closing the fcin stream with fcclose() seems to reliably force the lexer to stop doing anything else. Then we can just reset the prompt to PS1 and invoke sh_exit() to start new command line, which will now reset the lexer state as per above.
This commit is contained in:
parent
0180a65bbf
commit
65feb9641a
5 changed files with 19 additions and 4 deletions
6
NEWS
6
NEWS
|
@ -3,6 +3,12 @@ For full details, see the git log at: https://github.com/ksh93/ksh
|
||||||
|
|
||||||
Any uppercase BUG_* names are modernish shell bug IDs.
|
Any uppercase BUG_* names are modernish shell bug IDs.
|
||||||
|
|
||||||
|
2021-12-11:
|
||||||
|
|
||||||
|
- Fixed two more crashing bugs that occurred if ksh received a signal (such
|
||||||
|
as SIGINT due to Ctrl+C) while the user is entering a multi-line command
|
||||||
|
substitution in an interactive shell.
|
||||||
|
|
||||||
2021-12-09:
|
2021-12-09:
|
||||||
|
|
||||||
- Increased the general robustness of discipline function handling, fixing
|
- Increased the general robustness of discipline function handling, fixing
|
||||||
|
|
|
@ -924,6 +924,7 @@ make install
|
||||||
prev include/edit.h implicit
|
prev include/edit.h implicit
|
||||||
prev include/history.h implicit
|
prev include/history.h implicit
|
||||||
prev include/shnodes.h implicit
|
prev include/shnodes.h implicit
|
||||||
|
prev include/shlex.h implicit
|
||||||
prev include/jobs.h implicit
|
prev include/jobs.h implicit
|
||||||
prev include/io.h implicit
|
prev include/io.h implicit
|
||||||
prev include/path.h implicit
|
prev include/path.h implicit
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
|
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
|
||||||
#define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */
|
#define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */
|
||||||
#define SH_RELEASE_DATE "2021-12-09" /* must be in this format for $((.sh.version)) */
|
#define SH_RELEASE_DATE "2021-12-11" /* must be in this format for $((.sh.version)) */
|
||||||
#define SH_RELEASE_CPYR "(c) 2020-2021 Contributors to ksh " SH_RELEASE_FORK
|
#define SH_RELEASE_CPYR "(c) 2020-2021 Contributors to ksh " SH_RELEASE_FORK
|
||||||
|
|
||||||
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
|
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
|
||||||
|
|
|
@ -68,9 +68,6 @@ void sh_fault(register int sig)
|
||||||
register char *trap;
|
register char *trap;
|
||||||
register struct checkpt *pp = (struct checkpt*)shp->jmplist;
|
register struct checkpt *pp = (struct checkpt*)shp->jmplist;
|
||||||
int action=0;
|
int action=0;
|
||||||
/* reset lexer state on Ctrl+C */
|
|
||||||
if(sh_isstate(SH_INTERACTIVE) && sig==SIGINT)
|
|
||||||
sh_lexopen(sh.lex_context, &sh, 0);
|
|
||||||
/* reset handler */
|
/* reset handler */
|
||||||
if(!(sig&SH_TRAP))
|
if(!(sig&SH_TRAP))
|
||||||
signal(sig, sh_fault);
|
signal(sig, sh_fault);
|
||||||
|
@ -437,6 +434,15 @@ void sh_chktrap(Shell_t* shp)
|
||||||
cursig = sig;
|
cursig = sig;
|
||||||
sh_trap(trap,0);
|
sh_trap(trap,0);
|
||||||
cursig = -1;
|
cursig = -1;
|
||||||
|
/* If we're in a PS2 prompt, then we just parsed and executed a trap in the middle of parsing
|
||||||
|
* another command, so the lexer state is overwritten. Escape to avoid crashing the lexer. */
|
||||||
|
if(sh.nextprompt == 2)
|
||||||
|
{
|
||||||
|
fcclose(); /* force lexer to abort partial command */
|
||||||
|
sh.nextprompt = 1; /* next display prompt is PS1 */
|
||||||
|
sh.lastsig = sig; /* make sh_exit() set $? to signal exit status */
|
||||||
|
sh_exit(SH_EXITSIG); /* start a new command line */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "jobs.h"
|
#include "jobs.h"
|
||||||
|
#include "shlex.h"
|
||||||
#include "shnodes.h"
|
#include "shnodes.h"
|
||||||
#include "history.h"
|
#include "history.h"
|
||||||
#include "edit.h"
|
#include "edit.h"
|
||||||
|
@ -2154,6 +2155,7 @@ static int io_prompt(Shell_t *shp,Sfio_t *iop,register int flag)
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
register int c;
|
register int c;
|
||||||
|
sh_lexopen(sh.lex_context, &sh, 0); /* reset lexer state */
|
||||||
#if defined(TIOCLBIC) && defined(LFLUSHO)
|
#if defined(TIOCLBIC) && defined(LFLUSHO)
|
||||||
if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS))
|
if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue