[PATCH] expand: Fix expmeta resource leakage

Herbert Xu <[email protected]>
Newsgroups org.kernel.vger.dash
Message-ID <[email protected]>
If memory allocation fails in expmeta, the DIRP will be leaked
along with expdir.

Fix this by adding setjmp/longjmp, and converting expdir to use
stack memory.

Signed-off-by: Herbert Xu <[email protected]>
---
 src/expand.c | 167 ++++++++++++++++++++++++++++-----------------------
 1 file changed, 92 insertions(+), 75 deletions(-)

diff --git a/src/expand.c b/src/expand.c
index e7e8ce0..ce245e4 100644
--- a/src/expand.c
+++ b/src/expand.c
@@ -32,49 +32,50 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/stat.h>
+#include <ctype.h>
 #include <dirent.h>
-#include <unistd.h>
-#ifdef HAVE_GETPWNAM
-#include <pwd.h>
-#endif
-#include <stdlib.h>
-#include <stdio.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <string.h>
 #ifdef HAVE_FNMATCH
 #include <fnmatch.h>
 #endif
 #ifdef HAVE_GLOB
 #include <glob.h>
 #endif
-#include <ctype.h>
+#include <inttypes.h>
+#include <limits.h>
+#ifdef HAVE_GETPWNAM
+#include <pwd.h>
+#endif
+#include <setjmp.h>
 #include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 /*
  * Routines to expand arguments to commands.  We have to deal with
  * backquotes, shell variables, and file metacharacters.
  */
 
-#include "shell.h"
-#include "main.h"
-#include "nodes.h"
+#include "error.h"
 #include "eval.h"
 #include "expand.h"
-#include "syntax.h"
-#include "parser.h"
 #include "jobs.h"
-#include "options.h"
-#include "var.h"
-#include "output.h"
+#include "main.h"
 #include "memalloc.h"
-#include "error.h"
 #include "mystring.h"
+#include "nodes.h"
+#include "options.h"
+#include "output.h"
+#include "parser.h"
+#include "shell.h"
 #include "show.h"
+#include "syntax.h"
 #include "system.h"
+#include "var.h"
 
 /*
  * _rmescape() flags
@@ -120,7 +121,7 @@ static size_t memtodest(const char *p, size_t len, int flags);
 STATIC ssize_t varvalue(char *, int, int, int);
 STATIC void expandmeta(struct strlist *);
 static void addglob(const glob64_t *);
-STATIC void expmeta(char *, unsigned, unsigned);
+static char *expmeta(char *, unsigned, size_t);
 STATIC struct strlist *expsort(struct strlist *);
 STATIC struct strlist *msort(struct strlist *, int);
 STATIC void addfname(char *);
@@ -1225,10 +1226,6 @@ static void addglob(const glob64_t *pglob)
 	} while (*++p);
 }
 
-STATIC char *expdir;
-STATIC unsigned expdir_max;
-
-
 STATIC void
 expandmeta(struct strlist *str)
 {
@@ -1255,11 +1252,8 @@ expandmeta(struct strlist *str)
 		INTOFF;
 		p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
 		len = strlen(p);
-		expdir_max = len + PATH_MAX;
-		expdir = ckmalloc(expdir_max);
 
 		expmeta(p, len, 0);
-		ckfree(expdir);
 		if (p != str->text)
 			ckfree(p);
 		INTON;
@@ -1282,27 +1276,59 @@ nometa:
 	}
 }
 
+static void addfname_common(char *name)
+{
+	struct strlist *sp;
+
+	sp = (struct strlist *)stalloc(sizeof *sp);
+	sp->text = name;
+	*exparg.lastp = sp;
+	exparg.lastp = &sp->next;
+}
+
+static char *addfnamealt(char *enddir, size_t expdir_len)
+{
+	char *name;
+
+	name = grabstackstr(enddir);
+	addfname_common(name);
+
+	STARTSTACKSTR(enddir);
+	return stnputs(name, expdir_len, enddir) - expdir_len;
+}
 
 /*
  * Do metacharacter (i.e. *, ?, [...]) expansion.
  */
 
-STATIC void
-expmeta(char *name, unsigned name_len, unsigned expdir_len)
+static char *expmeta(char *name, unsigned name_len, size_t expdir_len)
 {
-	char *enddir = expdir + expdir_len;
-	char *p;
-	const char *cp;
-	char *start;
-	char *endname;
-	int metaflag;
+	struct jmploc *volatile savehandler;
+	struct jmploc jmploc;
 	struct stat64 statb;
-	DIR *dirp;
 	struct dirent64 *dp;
-	int atend;
+	volatile int err;
+	char *endname;
+	char *enddir;
+	int metaflag;
 	int matchdot;
+	char *start;
+	size_t len;
+	DIR *dirp;
+	int atend;
+	char *cp;
+	char *p;
 	int esc;
 
+	*(DIR *volatile *)&dirp = NULL;
+	savehandler = handler;
+	if (unlikely(err = setjmp(jmploc.loc)))
+		goto out;
+
+	len = expdir_len + name_len + 1;
+	cp = growstackto(len);
+	enddir = cp + expdir_len;
+
 	metaflag = 0;
 	start = name;
 	for (p = name; esc = 0, *p; p += esc + 1) {
@@ -1334,16 +1360,16 @@ expmeta(char *name, unsigned name_len, unsigned expdir_len)
 	}
 	if (metaflag == 0) {	/* we've reached the end of the file name */
 		if (!expdir_len)
-			return;
+			goto out_opendir;
 		p = name;
 		do {
 			if (*p == '\\' && p[1])
 				p++;
 			*enddir++ = *p;
 		} while (*p++);
-		if (lstat64(expdir, &statb) >= 0)
-			addfname(expdir);
-		return;
+		if (lstat64(cp, &statb) >= 0)
+			cp = addfnamealt(enddir, expdir_len);
+		goto out_opendir;
 	}
 	endname = p;
 	if (name < start) {
@@ -1355,12 +1381,11 @@ expmeta(char *name, unsigned name_len, unsigned expdir_len)
 		} while (p < start);
 	}
 	*enddir = 0;
-	cp = expdir;
 	expdir_len = enddir - cp;
-	if (!expdir_len)
-		cp = dotdir;
-	if ((dirp = opendir(cp)) == NULL)
-		return;
+
+	*(DIR *volatile *)&dirp = opendir(expdir_len ? cp : dotdir);
+	if (!dirp)
+		goto out_opendir;
 	if (*endname == 0) {
 		atend = 1;
 	} else {
@@ -1379,32 +1404,29 @@ expmeta(char *name, unsigned name_len, unsigned expdir_len)
 		if (dp->d_name[0] == '.' && ! matchdot)
 			continue;
 		if (pmatch(start, dp->d_name)) {
-			if (atend) {
-				scopy(dp->d_name, enddir);
-				addfname(expdir);
-			} else {
-				unsigned offset;
-				unsigned len;
+			len = strlen(dp->d_name) + 1;
 
-				p = stpcpy(enddir, dp->d_name);
-				*p = '/';
-
-				offset = p - expdir + 1;
-				len = offset + name_len + NAME_MAX;
-				if (len > expdir_max) {
-					len += PATH_MAX;
-					expdir = ckrealloc(expdir, len);
-					expdir_max = len;
-				}
-
-				expmeta(endname, name_len, offset);
-				enddir = expdir + expdir_len;
+			enddir = cp + expdir_len;
+			enddir = stnputs(dp->d_name, len, enddir);
+			if (atend)
+				cp = addfnamealt(enddir, expdir_len);
+			else {
+				enddir[-1] = '/';
+				len += expdir_len;
+				cp = expmeta(endname, name_len, len);
 			}
 		}
 	}
-	closedir(dirp);
 	if (! atend)
 		endname[-esc - 1] = esc ? '\\' : '/';
+
+out:
+	closedir(*(DIR *volatile *)&dirp);
+out_opendir:
+	handler = savehandler;
+	if (err)
+		longjmp(handler->loc, 1);
+	return cp;
 }
 
 
@@ -1415,12 +1437,7 @@ expmeta(char *name, unsigned name_len, unsigned expdir_len)
 STATIC void
 addfname(char *name)
 {
-	struct strlist *sp;
-
-	sp = (struct strlist *)stalloc(sizeof *sp);
-	sp->text = sstrdup(name);
-	*exparg.lastp = sp;
-	exparg.lastp = &sp->next;
+	addfname_common(sstrdup(name));
 }
 
 
-- 
2.39.2

-- 
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.