From 0f3806dd899ace97d5909f195882697ef9dd1eaa Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 25 Mar 2018 16:38:00 +0800 Subject: expand: Fix buffer overflow in expandmeta The native version of expandmeta allocates a buffer that may be overrun for two reasons. First of all the size is 1 byte too small but this is normally hidden because the minimum size is rounded up to 2048 bytes. Secondly, if the directory level is deep enough, any buffer can be overrun. This patch fixes both problems by calling realloc when necessary. Signed-off-by: Herbert Xu --- src/expand.c | 57 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 24 deletions(-) (limited to 'src/expand.c') diff --git a/src/expand.c b/src/expand.c index 0f747b3..30288db 100644 --- a/src/expand.c +++ b/src/expand.c @@ -124,7 +124,7 @@ STATIC void expandmeta(struct strlist *, int); #ifdef HAVE_GLOB STATIC void addglob(const glob_t *); #else -STATIC void expmeta(char *, char *); +STATIC void expmeta(char *, unsigned, unsigned); STATIC struct strlist *expsort(struct strlist *); STATIC struct strlist *msort(struct strlist *, int); #endif @@ -1246,6 +1246,7 @@ addglob(pglob) #else /* HAVE_GLOB */ STATIC char *expdir; +STATIC unsigned expdir_max; STATIC void @@ -1260,6 +1261,7 @@ expandmeta(struct strlist *str, int flag) struct strlist **savelastp; struct strlist *sp; char *p; + unsigned len; if (fflag) goto nometa; @@ -1269,12 +1271,11 @@ expandmeta(struct strlist *str, int flag) INTOFF; p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); - { - int i = strlen(str->text); - expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ - } + len = strlen(p); + expdir_max = len + PATH_MAX; + expdir = ckmalloc(expdir_max); - expmeta(expdir, p); + expmeta(p, len, 0); ckfree(expdir); if (p != str->text) ckfree(p); @@ -1304,8 +1305,9 @@ nometa: */ STATIC void -expmeta(char *enddir, char *name) +expmeta(char *name, unsigned name_len, unsigned expdir_len) { + char *enddir = expdir + expdir_len; char *p; const char *cp; char *start; @@ -1348,15 +1350,15 @@ expmeta(char *enddir, char *name) } } if (metaflag == 0) { /* we've reached the end of the file name */ - if (enddir != expdir) - metaflag++; + if (!expdir_len) + return; p = name; do { if (*p == '\\') p++; *enddir++ = *p; } while (*p++); - if (metaflag == 0 || lstat64(expdir, &statb) >= 0) + if (lstat64(expdir, &statb) >= 0) addfname(expdir); return; } @@ -1369,18 +1371,13 @@ expmeta(char *enddir, char *name) *enddir++ = *p++; } while (p < start); } - if (enddir == expdir) { + *enddir = 0; + cp = expdir; + expdir_len = enddir - cp; + if (!expdir_len) cp = "."; - } else if (enddir == expdir + 1 && *expdir == '/') { - cp = "/"; - } else { - cp = expdir; - enddir[-1] = '\0'; - } if ((dirp = opendir(cp)) == NULL) return; - if (enddir != expdir) - enddir[-1] = '/'; if (*endname == 0) { atend = 1; } else { @@ -1388,6 +1385,7 @@ expmeta(char *enddir, char *name) *endname = '\0'; endname += esc + 1; } + name_len -= endname - name; matchdot = 0; p = start; if (*p == '\\') @@ -1402,11 +1400,22 @@ expmeta(char *enddir, char *name) scopy(dp->d_name, enddir); addfname(expdir); } else { - for (p = enddir, cp = dp->d_name; - (*p++ = *cp++) != '\0';) - continue; - p[-1] = '/'; - expmeta(p, endname); + unsigned offset; + unsigned len; + + 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; } } } -- cgit 1.4.1