aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2018-03-25 16:38:00 +0800
committerBen Hutchings <ben@decadent.org.uk>2020-03-28 21:42:54 +0000
commite09252abe2baa4f033807c0c77ce273a804f041a (patch)
treef361abd8d63df253d93329c54dc4eaebefaa5235
parent8f2448fc5da9bdcdbfabb501a8af0912cc2f181d (diff)
downloadklibc-e09252abe2baa4f033807c0c77ce273a804f041a.tar.gz
[klibc] dash: expand: Fix buffer overflow in expandmeta
[ dash commit 0f3806dd899ace97d5909f195882697ef9dd1eaa ] 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 <herbert@gondor.apana.org.au> Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
-rw-r--r--usr/dash/expand.c57
1 files changed, 33 insertions, 24 deletions
diff --git a/usr/dash/expand.c b/usr/dash/expand.c
index 974a31de7063b..f52f34c9fa0ee 100644
--- a/usr/dash/expand.c
+++ b/usr/dash/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;
}
}
}