svn commit: r200279 - in stable/7: bin/sh tools/regression/bin/sh/execution

Jilles Tjoelker jilles at FreeBSD.org
Tue Dec 8 14:37:08 PST 2009


Author: jilles
Date: Tue Dec  8 22:37:07 2009
New Revision: 200279
URL: http://svn.freebsd.org/changeset/base/200279

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.
  
  PR:		bin/137640

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

Modified: stable/7/bin/sh/eval.c
==============================================================================
--- stable/7/bin/sh/eval.c	Tue Dec  8 22:35:39 2009	(r200278)
+++ stable/7/bin/sh/eval.c	Tue Dec  8 22:37:07 2009	(r200279)
@@ -773,6 +773,7 @@ evalcommand(union node *cmd, int flags, 
 		INTOFF;
 		savelocalvars = localvars;
 		localvars = NULL;
+		reffunc(cmdentry.u.func);
 		INTON;
 		if (setjmp(jmploc.loc)) {
 			if (exception == EXSHELLPROC)
@@ -781,6 +782,7 @@ evalcommand(union node *cmd, int flags, 
 				freeparam(&shellparam);
 				shellparam = saveparam;
 			}
+			unreffunc(cmdentry.u.func);
 			poplocalvars();
 			localvars = savelocalvars;
 			handler = savehandler;
@@ -792,11 +794,12 @@ evalcommand(union node *cmd, int flags, 
 			mklocal(sp->text);
 		funcnest++;
 		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/7/bin/sh/exec.c
==============================================================================
--- stable/7/bin/sh/exec.c	Tue Dec  8 22:35:39 2009	(r200278)
+++ stable/7/bin/sh/exec.c	Tue Dec  8 22:37:07 2009	(r200279)
@@ -285,7 +285,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);
@@ -582,7 +582,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;
@@ -669,7 +669,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;
@@ -704,7 +704,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/7/bin/sh/exec.h
==============================================================================
--- stable/7/bin/sh/exec.h	Tue Dec  8 22:35:39 2009	(r200278)
+++ stable/7/bin/sh/exec.h	Tue Dec  8 22:37:07 2009	(r200279)
@@ -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/7/bin/sh/mknodes.c
==============================================================================
--- stable/7/bin/sh/mknodes.c	Tue Dec  8 22:35:39 2009	(r200278)
+++ stable/7/bin/sh/mknodes.c	Tue Dec  8 22:37:07 2009	(r200279)
@@ -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/7/bin/sh/nodes.c.pat
==============================================================================
--- stable/7/bin/sh/nodes.c.pat	Tue Dec  8 22:35:39 2009	(r200278)
+++ stable/7/bin/sh/nodes.c.pat	Tue Dec  8 22:37:07 2009	(r200279)
@@ -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/7/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/7/tools/regression/bin/sh/execution/func1.0	Tue Dec  8 22:37:07 2009	(r200279, 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/7/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/7/tools/regression/bin/sh/execution/func2.0	Tue Dec  8 22:37:07 2009	(r200279, 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


More information about the svn-src-all mailing list