ash: eval: Only restore exit status on exit/return
Upstream commit: Date: Fri, 14 Dec 2018 13:52:02 +0800 eval: Only restore exit status on exit/return We unconditionally restore the saved status in exitreset, which is incorrect as we only want to do it for exitcmd and returncmd. This patch fixes the problem by introducing EXEND. Reported-by: Martijn Dekker <martijn@inlv.org> Fixes: da30b4b78769 ("[BUILTIN] Exit without arguments in a trap...") Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
cd24a50633
commit
f977e004ce
99
shell/ash.c
99
shell/ash.c
@ -404,11 +404,11 @@ struct globals_misc {
|
|||||||
volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
|
volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
|
||||||
volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
|
volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
|
||||||
volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
|
volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
|
||||||
smallint exception_type; /* kind of exception (0..5) */
|
smallint exception_type; /* kind of exception: */
|
||||||
/* exceptions */
|
|
||||||
#define EXINT 0 /* SIGINT received */
|
#define EXINT 0 /* SIGINT received */
|
||||||
#define EXERROR 1 /* a generic error */
|
#define EXERROR 1 /* a generic error */
|
||||||
#define EXEXIT 4 /* exit the shell */
|
#define EXEND 3 /* exit the shell */
|
||||||
|
#define EXEXIT 4 /* exit the shell via exitcmd */
|
||||||
|
|
||||||
char nullstr[1]; /* zero length string */
|
char nullstr[1]; /* zero length string */
|
||||||
|
|
||||||
@ -8237,7 +8237,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
|
|||||||
exitstatus = exerrno;
|
exitstatus = exerrno;
|
||||||
TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
|
TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
|
||||||
prog, e, suppress_int));
|
prog, e, suppress_int));
|
||||||
ash_msg_and_raise(EXEXIT, "%s: %s", prog, errmsg(e, "not found"));
|
ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found"));
|
||||||
/* NOTREACHED */
|
/* NOTREACHED */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9291,9 +9291,9 @@ evaltree(union node *n, int flags)
|
|||||||
dotrap();
|
dotrap();
|
||||||
|
|
||||||
if (checkexit & status)
|
if (checkexit & status)
|
||||||
raise_exception(EXEXIT);
|
raise_exception(EXEND);
|
||||||
if (flags & EV_EXIT)
|
if (flags & EV_EXIT)
|
||||||
raise_exception(EXEXIT);
|
raise_exception(EXEND);
|
||||||
|
|
||||||
popstackmark(&smark);
|
popstackmark(&smark);
|
||||||
TRACE(("leaving evaltree (no interrupts)\n"));
|
TRACE(("leaving evaltree (no interrupts)\n"));
|
||||||
@ -14146,6 +14146,47 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv)
|
|||||||
|
|
||||||
/* ============ main() and helpers */
|
/* ============ main() and helpers */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This routine is called when an error or an interrupt occurs in an
|
||||||
|
* interactive shell and control is returned to the main command loop
|
||||||
|
* but prior to exitshell.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
exitreset(void)
|
||||||
|
{
|
||||||
|
/* from eval.c: */
|
||||||
|
if (savestatus >= 0) {
|
||||||
|
if (exception_type == EXEXIT || evalskip == SKIPFUNCDEF)
|
||||||
|
exitstatus = savestatus;
|
||||||
|
savestatus = -1;
|
||||||
|
}
|
||||||
|
evalskip = 0;
|
||||||
|
loopnest = 0;
|
||||||
|
|
||||||
|
/* from expand.c: */
|
||||||
|
ifsfree();
|
||||||
|
|
||||||
|
/* from redir.c: */
|
||||||
|
unwindredir(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This routine is called when an error or an interrupt occurs in an
|
||||||
|
* interactive shell and control is returned to the main command loop.
|
||||||
|
* (In dash, this function is auto-generated by build machinery).
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
reset(void)
|
||||||
|
{
|
||||||
|
/* from input.c: */
|
||||||
|
g_parsefile->left_in_buffer = 0;
|
||||||
|
g_parsefile->left_in_line = 0; /* clear input buffer */
|
||||||
|
popallfiles();
|
||||||
|
|
||||||
|
/* from var.c: */
|
||||||
|
unwindlocalvars(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called to exit the shell.
|
* Called to exit the shell.
|
||||||
*/
|
*/
|
||||||
@ -14169,15 +14210,17 @@ exitshell(void)
|
|||||||
trap[0] = NULL;
|
trap[0] = NULL;
|
||||||
evalskip = 0;
|
evalskip = 0;
|
||||||
evalstring(p, 0);
|
evalstring(p, 0);
|
||||||
|
evalskip = SKIPFUNCDEF;
|
||||||
/*free(p); - we'll exit soon */
|
/*free(p); - we'll exit soon */
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
exitreset();
|
||||||
/* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
|
/* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
|
||||||
* our setjobctl(0) does not panic if tcsetpgrp fails inside it.
|
* our setjobctl(0) does not panic if tcsetpgrp fails inside it.
|
||||||
*/
|
*/
|
||||||
setjobctl(0);
|
setjobctl(0);
|
||||||
flush_stdout_stderr();
|
flush_stdout_stderr();
|
||||||
_exit(savestatus);
|
_exit(exitstatus);
|
||||||
/* NOTREACHED */
|
/* NOTREACHED */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14337,46 +14380,6 @@ read_profile(const char *name)
|
|||||||
popfile();
|
popfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This routine is called when an error or an interrupt occurs in an
|
|
||||||
* interactive shell and control is returned to the main command loop
|
|
||||||
* but prior to exitshell.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
exitreset(void)
|
|
||||||
{
|
|
||||||
/* from eval.c: */
|
|
||||||
evalskip = 0;
|
|
||||||
loopnest = 0;
|
|
||||||
if (savestatus >= 0) {
|
|
||||||
exitstatus = savestatus;
|
|
||||||
savestatus = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* from expand.c: */
|
|
||||||
ifsfree();
|
|
||||||
|
|
||||||
/* from redir.c: */
|
|
||||||
unwindredir(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This routine is called when an error or an interrupt occurs in an
|
|
||||||
* interactive shell and control is returned to the main command loop.
|
|
||||||
* (In dash, this function is auto-generated by build machinery).
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
reset(void)
|
|
||||||
{
|
|
||||||
/* from input.c: */
|
|
||||||
g_parsefile->left_in_buffer = 0;
|
|
||||||
g_parsefile->left_in_line = 0; /* clear input buffer */
|
|
||||||
popallfiles();
|
|
||||||
|
|
||||||
/* from var.c: */
|
|
||||||
unwindlocalvars(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if PROFILE
|
#if PROFILE
|
||||||
static short profile_buf[16384];
|
static short profile_buf[16384];
|
||||||
extern int etext();
|
extern int etext();
|
||||||
@ -14424,7 +14427,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
|
|
||||||
e = exception_type;
|
e = exception_type;
|
||||||
s = state;
|
s = state;
|
||||||
if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
|
if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
|
||||||
exitshell();
|
exitshell();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user