Author: jilles
Date: Sun Oct 11 16:35:12 2009
New Revision: 197959
URL: http://svn.freebsd.org/changeset/base/197959

Log:
  MFC r196483,r196634:
  sh: Fix crash when undefining or redefining a currently executing function
  
  Add a reference count to function definitions.
  Memory may leak if a SIGINT arrives in interactive mode at exactly the wrong
  time, this will be fixed later by changing SIGINT handling.
  
  PR:           bin/137640
  Approved by:  re (kib)

Added:
  stable/8/tools/regression/bin/sh/execution/func1.0
     - copied unchanged from r196483, 
head/tools/regression/bin/sh/execution/func1.0
  stable/8/tools/regression/bin/sh/execution/func2.0
     - copied unchanged from r196634, 
head/tools/regression/bin/sh/execution/func2.0
Modified:
  stable/8/bin/sh/   (props changed)
  stable/8/bin/sh/eval.c
  stable/8/bin/sh/exec.c
  stable/8/bin/sh/exec.h
  stable/8/bin/sh/mknodes.c
  stable/8/bin/sh/nodes.c.pat
  stable/8/tools/regression/bin/sh/   (props changed)

Modified: stable/8/bin/sh/eval.c
==============================================================================
--- stable/8/bin/sh/eval.c      Sun Oct 11 16:23:11 2009        (r197958)
+++ stable/8/bin/sh/eval.c      Sun Oct 11 16:35:12 2009        (r197959)
@@ -785,6 +785,7 @@ evalcommand(union node *cmd, int flags, 
                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, 
                                freeparam(&shellparam);
                                shellparam = saveparam;
                        }
+                       unreffunc(cmdentry.u.func);
                        poplocalvars();
                        localvars = savelocalvars;
                        handler = savehandler;
@@ -805,11 +807,12 @@ evalcommand(union node *cmd, int flags, 
                funcnest++;
                exitstatus = oexitstatus;
                if (flags & EV_TESTED)
-                       evaltree(cmdentry.u.func, EV_TESTED);
+                       evaltree(getfuncnode(cmdentry.u.func), EV_TESTED);
                else
-                       evaltree(cmdentry.u.func, 0);
+                       evaltree(getfuncnode(cmdentry.u.func), 0);
                funcnest--;
                INTOFF;
+               unreffunc(cmdentry.u.func);
                poplocalvars();
                localvars = savelocalvars;
                freeparam(&shellparam);

Modified: stable/8/bin/sh/exec.c
==============================================================================
--- stable/8/bin/sh/exec.c      Sun Oct 11 16:23:11 2009        (r197958)
+++ stable/8/bin/sh/exec.c      Sun Oct 11 16:35:12 2009        (r197959)
@@ -286,7 +286,7 @@ printentry(struct tblentry *cmdp, int ve
                out1fmt("function %s", cmdp->cmdname);
                if (verbose) {
                        INTOFF;
-                       name = commandtext(cmdp->param.func);
+                       name = commandtext(getfuncnode(cmdp->param.func));
                        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 
        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);
        }

Modified: stable/8/bin/sh/exec.h
==============================================================================
--- stable/8/bin/sh/exec.h      Sun Oct 11 16:23:11 2009        (r197958)
+++ stable/8/bin/sh/exec.h      Sun Oct 11 16:35:12 2009        (r197959)
@@ -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;
 };

Modified: stable/8/bin/sh/mknodes.c
==============================================================================
--- stable/8/bin/sh/mknodes.c   Sun Oct 11 16:23:11 2009        (r197958)
+++ stable/8/bin/sh/mknodes.c   Sun Oct 11 16:35:12 2009        (r197959)
@@ -248,8 +248,11 @@ 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("struct funcdef *copyfunc(union node *);\n", hfile);
+       fputs("union node *getfuncnode(struct funcdef *);\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) {

Modified: stable/8/bin/sh/nodes.c.pat
==============================================================================
--- stable/8/bin/sh/nodes.c.pat Sun Oct 11 16:23:11 2009        (r197958)
+++ stable/8/bin/sh/nodes.c.pat Sun Oct 11 16:35:12 2009        (r197959)
@@ -35,6 +35,7 @@
 
 #include <sys/param.h>
 #include <stdlib.h>
+#include <stddef.h>
 /*
  * Routine for dealing with parsed shell commands.
  */
@@ -60,25 +61,40 @@ STATIC struct nodelist *copynodelist(str
 STATIC char *nodesavestr(char *);
 
 
+struct funcdef {
+       unsigned int refcount;
+       union node n;
+};
 
 /*
  * 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;
 }
 
 
+union node *
+getfuncnode(struct funcdef *fn)
+{
+       return fn == NULL ? NULL : &fn->n;
+}
+
 
 STATIC void
 calcsize(union node *n)
@@ -144,14 +160,26 @@ nodesavestr(char *s)
 }
 
 
+void
+reffunc(struct funcdef *fn)
+{
+       if (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);
+       }
 }

Copied: stable/8/tools/regression/bin/sh/execution/func1.0 (from r196483, 
head/tools/regression/bin/sh/execution/func1.0)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ stable/8/tools/regression/bin/sh/execution/func1.0  Sun Oct 11 16:35:12 
2009        (r197959, copy of r196483, 
head/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'

Copied: stable/8/tools/regression/bin/sh/execution/func2.0 (from r196634, 
head/tools/regression/bin/sh/execution/func2.0)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ stable/8/tools/regression/bin/sh/execution/func2.0  Sun Oct 11 16:35:12 
2009        (r197959, copy of r196634, 
head/tools/regression/bin/sh/execution/func2.0)
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+f() { }
+f
+hash -v f >/dev/null
+f() { { }; }
+f
+hash -v f >/dev/null
+f() { { } }
+f
+hash -v f >/dev/null
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to