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:
Jilles Tjoelker 2009-08-23 21:09:46 +00:00
parent 6852110b64
commit eb33e843b8
6 changed files with 48 additions and 18 deletions

View File

@ -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);

View File

@ -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);
} }

View File

@ -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;
}; };

View File

@ -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) {

View File

@ -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);
}
} }

View File

@ -0,0 +1,4 @@
# $FreeBSD$
MALLOC_OPTIONS=J sh -c 'g() { g() { :; }; :; }; g' &&
MALLOC_OPTIONS=J sh -c 'g() { unset -f g; :; }; g'