sh: Fix crash when undefining or redefining a currently executing function.
Add a reference count to function definitions. Memory may leak if multiple SIGINTs arrive in interactive mode, this will be fixed later by changing SIGINT handling. PR: bin/137640
This commit is contained in:
parent
6852110b64
commit
eb33e843b8
@ -785,6 +785,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
|
|||||||
INTOFF;
|
INTOFF;
|
||||||
savelocalvars = localvars;
|
savelocalvars = localvars;
|
||||||
localvars = NULL;
|
localvars = NULL;
|
||||||
|
reffunc(cmdentry.u.func);
|
||||||
INTON;
|
INTON;
|
||||||
savehandler = handler;
|
savehandler = handler;
|
||||||
if (setjmp(jmploc.loc)) {
|
if (setjmp(jmploc.loc)) {
|
||||||
@ -794,6 +795,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
|
|||||||
freeparam(&shellparam);
|
freeparam(&shellparam);
|
||||||
shellparam = saveparam;
|
shellparam = saveparam;
|
||||||
}
|
}
|
||||||
|
unreffunc(cmdentry.u.func);
|
||||||
poplocalvars();
|
poplocalvars();
|
||||||
localvars = savelocalvars;
|
localvars = savelocalvars;
|
||||||
handler = savehandler;
|
handler = savehandler;
|
||||||
@ -805,11 +807,12 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
|
|||||||
funcnest++;
|
funcnest++;
|
||||||
exitstatus = oexitstatus;
|
exitstatus = oexitstatus;
|
||||||
if (flags & EV_TESTED)
|
if (flags & EV_TESTED)
|
||||||
evaltree(cmdentry.u.func, EV_TESTED);
|
evaltree(&cmdentry.u.func->n, EV_TESTED);
|
||||||
else
|
else
|
||||||
evaltree(cmdentry.u.func, 0);
|
evaltree(&cmdentry.u.func->n, 0);
|
||||||
funcnest--;
|
funcnest--;
|
||||||
INTOFF;
|
INTOFF;
|
||||||
|
unreffunc(cmdentry.u.func);
|
||||||
poplocalvars();
|
poplocalvars();
|
||||||
localvars = savelocalvars;
|
localvars = savelocalvars;
|
||||||
freeparam(&shellparam);
|
freeparam(&shellparam);
|
||||||
|
@ -286,7 +286,7 @@ printentry(struct tblentry *cmdp, int verbose)
|
|||||||
out1fmt("function %s", cmdp->cmdname);
|
out1fmt("function %s", cmdp->cmdname);
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
INTOFF;
|
INTOFF;
|
||||||
name = commandtext(cmdp->param.func);
|
name = commandtext(&cmdp->param.func->n);
|
||||||
out1c(' ');
|
out1c(' ');
|
||||||
out1str(name);
|
out1str(name);
|
||||||
ckfree(name);
|
ckfree(name);
|
||||||
@ -583,7 +583,7 @@ deletefuncs(void)
|
|||||||
while ((cmdp = *pp) != NULL) {
|
while ((cmdp = *pp) != NULL) {
|
||||||
if (cmdp->cmdtype == CMDFUNCTION) {
|
if (cmdp->cmdtype == CMDFUNCTION) {
|
||||||
*pp = cmdp->next;
|
*pp = cmdp->next;
|
||||||
freefunc(cmdp->param.func);
|
unreffunc(cmdp->param.func);
|
||||||
ckfree(cmdp);
|
ckfree(cmdp);
|
||||||
} else {
|
} else {
|
||||||
pp = &cmdp->next;
|
pp = &cmdp->next;
|
||||||
@ -670,7 +670,7 @@ addcmdentry(char *name, struct cmdentry *entry)
|
|||||||
INTOFF;
|
INTOFF;
|
||||||
cmdp = cmdlookup(name, 1);
|
cmdp = cmdlookup(name, 1);
|
||||||
if (cmdp->cmdtype == CMDFUNCTION) {
|
if (cmdp->cmdtype == CMDFUNCTION) {
|
||||||
freefunc(cmdp->param.func);
|
unreffunc(cmdp->param.func);
|
||||||
}
|
}
|
||||||
cmdp->cmdtype = entry->cmdtype;
|
cmdp->cmdtype = entry->cmdtype;
|
||||||
cmdp->param = entry->u;
|
cmdp->param = entry->u;
|
||||||
@ -705,7 +705,7 @@ unsetfunc(char *name)
|
|||||||
struct tblentry *cmdp;
|
struct tblentry *cmdp;
|
||||||
|
|
||||||
if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
|
if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
|
||||||
freefunc(cmdp->param.func);
|
unreffunc(cmdp->param.func);
|
||||||
delete_cmd_entry();
|
delete_cmd_entry();
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
@ -46,11 +46,12 @@ enum {
|
|||||||
TYPECMD_TYPE /* type */
|
TYPECMD_TYPE /* type */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
union node;
|
||||||
struct cmdentry {
|
struct cmdentry {
|
||||||
int cmdtype;
|
int cmdtype;
|
||||||
union param {
|
union param {
|
||||||
int index;
|
int index;
|
||||||
union node *func;
|
struct funcdef *func;
|
||||||
} u;
|
} u;
|
||||||
int special;
|
int special;
|
||||||
};
|
};
|
||||||
|
@ -248,8 +248,13 @@ output(char *file)
|
|||||||
fputs("\tstruct nodelist *next;\n", hfile);
|
fputs("\tstruct nodelist *next;\n", hfile);
|
||||||
fputs("\tunion node *n;\n", hfile);
|
fputs("\tunion node *n;\n", hfile);
|
||||||
fputs("};\n\n\n", hfile);
|
fputs("};\n\n\n", hfile);
|
||||||
fputs("union node *copyfunc(union node *);\n", hfile);
|
fputs("struct funcdef {\n", hfile);
|
||||||
fputs("void freefunc(union node *);\n", hfile);
|
fputs("\tunsigned int refcount;\n", hfile);
|
||||||
|
fputs("\tunion node n;\n", hfile);
|
||||||
|
fputs("};\n\n\n", hfile);
|
||||||
|
fputs("struct funcdef *copyfunc(union node *);\n", hfile);
|
||||||
|
fputs("void reffunc(struct funcdef *);\n", hfile);
|
||||||
|
fputs("void unreffunc(struct funcdef *);\n", hfile);
|
||||||
|
|
||||||
fputs(writer, cfile);
|
fputs(writer, cfile);
|
||||||
while (fgets(line, sizeof line, patfile) != NULL) {
|
while (fgets(line, sizeof line, patfile) != NULL) {
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
/*
|
/*
|
||||||
* Routine for dealing with parsed shell commands.
|
* Routine for dealing with parsed shell commands.
|
||||||
*/
|
*/
|
||||||
@ -65,17 +66,22 @@ STATIC char *nodesavestr(char *);
|
|||||||
* Make a copy of a parse tree.
|
* Make a copy of a parse tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
union node *
|
struct funcdef *
|
||||||
copyfunc(union node *n)
|
copyfunc(union node *n)
|
||||||
{
|
{
|
||||||
|
struct funcdef *fn;
|
||||||
|
|
||||||
if (n == NULL)
|
if (n == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
funcblocksize = 0;
|
funcblocksize = offsetof(struct funcdef, n);
|
||||||
funcstringsize = 0;
|
funcstringsize = 0;
|
||||||
calcsize(n);
|
calcsize(n);
|
||||||
funcblock = ckmalloc(funcblocksize + funcstringsize);
|
fn = ckmalloc(funcblocksize + funcstringsize);
|
||||||
funcstring = (char *)funcblock + funcblocksize;
|
fn->refcount = 1;
|
||||||
return copynode(n);
|
funcblock = (char *)fn + offsetof(struct funcdef, n);
|
||||||
|
funcstring = (char *)fn + funcblocksize;
|
||||||
|
copynode(n);
|
||||||
|
return fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -144,14 +150,25 @@ nodesavestr(char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
reffunc(struct funcdef *fn)
|
||||||
|
{
|
||||||
|
fn->refcount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free a parse tree.
|
* Decrement the reference count of a function definition, freeing it
|
||||||
|
* if it falls to 0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
freefunc(union node *n)
|
unreffunc(struct funcdef *fn)
|
||||||
{
|
{
|
||||||
if (n)
|
if (fn) {
|
||||||
ckfree(n);
|
fn->refcount--;
|
||||||
|
if (fn->refcount > 0)
|
||||||
|
return;
|
||||||
|
ckfree(fn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
4
tools/regression/bin/sh/execution/func1.0
Normal file
4
tools/regression/bin/sh/execution/func1.0
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# $FreeBSD$
|
||||||
|
|
||||||
|
MALLOC_OPTIONS=J sh -c 'g() { g() { :; }; :; }; g' &&
|
||||||
|
MALLOC_OPTIONS=J sh -c 'g() { unset -f g; :; }; g'
|
Loading…
x
Reference in New Issue
Block a user