diff options
author | jilles <jilles@FreeBSD.org> | 2009-08-23 21:09:46 +0000 |
---|---|---|
committer | jilles <jilles@FreeBSD.org> | 2009-08-23 21:09:46 +0000 |
commit | 4a8e08a1103988b9e44d3ae9584cd654796998b1 (patch) | |
tree | 907e1a91ca6ca57f1742a6081e71d2fcd1f405a4 | |
parent | f0ee7a159b9f49405e827b40fed36a844334c3ac (diff) | |
download | FreeBSD-src-4a8e08a1103988b9e44d3ae9584cd654796998b1.zip FreeBSD-src-4a8e08a1103988b9e44d3ae9584cd654796998b1.tar.gz |
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
-rw-r--r-- | bin/sh/eval.c | 7 | ||||
-rw-r--r-- | bin/sh/exec.c | 8 | ||||
-rw-r--r-- | bin/sh/exec.h | 3 | ||||
-rw-r--r-- | bin/sh/mknodes.c | 9 | ||||
-rw-r--r-- | bin/sh/nodes.c.pat | 35 | ||||
-rw-r--r-- | tools/regression/bin/sh/execution/func1.0 | 4 |
6 files changed, 48 insertions, 18 deletions
diff --git a/bin/sh/eval.c b/bin/sh/eval.c index 39e1660..8dfa7dd 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -785,6 +785,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) INTOFF; savelocalvars = localvars; localvars = NULL; + reffunc(cmdentry.u.func); INTON; savehandler = handler; if (setjmp(jmploc.loc)) { @@ -794,6 +795,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) freeparam(&shellparam); shellparam = saveparam; } + unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; handler = savehandler; @@ -805,11 +807,12 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) funcnest++; exitstatus = oexitstatus; if (flags & EV_TESTED) - evaltree(cmdentry.u.func, EV_TESTED); + evaltree(&cmdentry.u.func->n, EV_TESTED); else - evaltree(cmdentry.u.func, 0); + evaltree(&cmdentry.u.func->n, 0); funcnest--; INTOFF; + unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; freeparam(&shellparam); diff --git a/bin/sh/exec.c b/bin/sh/exec.c index 3dc8895..639a23c 100644 --- a/bin/sh/exec.c +++ b/bin/sh/exec.c @@ -286,7 +286,7 @@ printentry(struct tblentry *cmdp, int verbose) out1fmt("function %s", cmdp->cmdname); if (verbose) { INTOFF; - name = commandtext(cmdp->param.func); + name = commandtext(&cmdp->param.func->n); out1c(' '); out1str(name); ckfree(name); @@ -583,7 +583,7 @@ deletefuncs(void) while ((cmdp = *pp) != NULL) { if (cmdp->cmdtype == CMDFUNCTION) { *pp = cmdp->next; - freefunc(cmdp->param.func); + unreffunc(cmdp->param.func); ckfree(cmdp); } else { pp = &cmdp->next; @@ -670,7 +670,7 @@ addcmdentry(char *name, struct cmdentry *entry) INTOFF; cmdp = cmdlookup(name, 1); if (cmdp->cmdtype == CMDFUNCTION) { - freefunc(cmdp->param.func); + unreffunc(cmdp->param.func); } cmdp->cmdtype = entry->cmdtype; cmdp->param = entry->u; @@ -705,7 +705,7 @@ unsetfunc(char *name) struct tblentry *cmdp; if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { - freefunc(cmdp->param.func); + unreffunc(cmdp->param.func); delete_cmd_entry(); return (0); } diff --git a/bin/sh/exec.h b/bin/sh/exec.h index 9f81a6a..e3b9acd 100644 --- a/bin/sh/exec.h +++ b/bin/sh/exec.h @@ -46,11 +46,12 @@ enum { TYPECMD_TYPE /* type */ }; +union node; struct cmdentry { int cmdtype; union param { int index; - union node *func; + struct funcdef *func; } u; int special; }; diff --git a/bin/sh/mknodes.c b/bin/sh/mknodes.c index f0afca6..904f9f4 100644 --- a/bin/sh/mknodes.c +++ b/bin/sh/mknodes.c @@ -248,8 +248,13 @@ output(char *file) fputs("\tstruct nodelist *next;\n", hfile); fputs("\tunion node *n;\n", hfile); fputs("};\n\n\n", hfile); - fputs("union node *copyfunc(union node *);\n", hfile); - fputs("void freefunc(union node *);\n", hfile); + fputs("struct funcdef {\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); while (fgets(line, sizeof line, patfile) != NULL) { diff --git a/bin/sh/nodes.c.pat b/bin/sh/nodes.c.pat index 10dab26..b6a8559 100644 --- a/bin/sh/nodes.c.pat +++ b/bin/sh/nodes.c.pat @@ -35,6 +35,7 @@ #include <sys/param.h> #include <stdlib.h> +#include <stddef.h> /* * Routine for dealing with parsed shell commands. */ @@ -65,17 +66,22 @@ STATIC char *nodesavestr(char *); * Make a copy of a parse tree. */ -union node * +struct funcdef * copyfunc(union node *n) { + struct funcdef *fn; + if (n == NULL) return NULL; - funcblocksize = 0; + funcblocksize = offsetof(struct funcdef, n); funcstringsize = 0; calcsize(n); - funcblock = ckmalloc(funcblocksize + funcstringsize); - funcstring = (char *)funcblock + funcblocksize; - return copynode(n); + fn = ckmalloc(funcblocksize + funcstringsize); + fn->refcount = 1; + 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 -freefunc(union node *n) +unreffunc(struct funcdef *fn) { - if (n) - ckfree(n); + if (fn) { + fn->refcount--; + if (fn->refcount > 0) + return; + ckfree(fn); + } } diff --git a/tools/regression/bin/sh/execution/func1.0 b/tools/regression/bin/sh/execution/func1.0 new file mode 100644 index 0000000..317b005 --- /dev/null +++ b/tools/regression/bin/sh/execution/func1.0 @@ -0,0 +1,4 @@ +# $FreeBSD$ + +MALLOC_OPTIONS=J sh -c 'g() { g() { :; }; :; }; g' && +MALLOC_OPTIONS=J sh -c 'g() { unset -f g; :; }; g' |