aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-04-14 14:50:47 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2018-04-14 14:50:47 +0200
commitd5f5045b43bd7a55402dfcecde511a2c207d07b7 (patch)
tree9e57ed30cf4918a5e205b964ce7dd5d15b295590
parent0dd3be8c0933d5649152b6019b975876e804e22a (diff)
downloadbusybox-w32-d5f5045b43bd7a55402dfcecde511a2c207d07b7.tar.gz
busybox-w32-d5f5045b43bd7a55402dfcecde511a2c207d07b7.tar.bz2
busybox-w32-d5f5045b43bd7a55402dfcecde511a2c207d07b7.zip
ash: expand: Fix buffer overflow in expandmeta
Upstream commit: Date: Sun, 25 Mar 2018 16:38:00 +0800 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 <herbert@gondor.apana.org.au> function old new delta expmeta 517 635 +118 expandarg 990 996 +6 mklocal 288 290 +2 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/0 up/down: 126/0) Total: 126 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash.c67
1 files changed, 42 insertions, 25 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 40ca82d0b..051cc671f 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -7575,9 +7575,16 @@ expandmeta(struct strlist *str /*, int flag*/)
7575/* 7575/*
7576 * Do metacharacter (i.e. *, ?, [...]) expansion. 7576 * Do metacharacter (i.e. *, ?, [...]) expansion.
7577 */ 7577 */
7578typedef struct exp_t {
7579 char *dir;
7580 unsigned dir_max;
7581} exp_t;
7578static void 7582static void
7579expmeta(char *expdir, char *enddir, char *name) 7583expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7580{ 7584{
7585#define expdir exp->dir
7586#define expdir_max exp->dir_max
7587 char *enddir = expdir + expdir_len;
7581 char *p; 7588 char *p;
7582 const char *cp; 7589 const char *cp;
7583 char *start; 7590 char *start;
@@ -7620,15 +7627,15 @@ expmeta(char *expdir, char *enddir, char *name)
7620 } 7627 }
7621 } 7628 }
7622 if (metaflag == 0) { /* we've reached the end of the file name */ 7629 if (metaflag == 0) { /* we've reached the end of the file name */
7623 if (enddir != expdir) 7630 if (!expdir_len)
7624 metaflag++; 7631 return;
7625 p = name; 7632 p = name;
7626 do { 7633 do {
7627 if (*p == '\\') 7634 if (*p == '\\')
7628 p++; 7635 p++;
7629 *enddir++ = *p; 7636 *enddir++ = *p;
7630 } while (*p++); 7637 } while (*p++);
7631 if (metaflag == 0 || lstat(expdir, &statb) >= 0) 7638 if (lstat(expdir, &statb) == 0)
7632 addfname(expdir); 7639 addfname(expdir);
7633 return; 7640 return;
7634 } 7641 }
@@ -7641,19 +7648,14 @@ expmeta(char *expdir, char *enddir, char *name)
7641 *enddir++ = *p++; 7648 *enddir++ = *p++;
7642 } while (p < start); 7649 } while (p < start);
7643 } 7650 }
7644 if (enddir == expdir) { 7651 *enddir = '\0';
7652 cp = expdir;
7653 expdir_len = enddir - cp;
7654 if (!expdir_len)
7645 cp = "."; 7655 cp = ".";
7646 } else if (enddir == expdir + 1 && *expdir == '/') {
7647 cp = "/";
7648 } else {
7649 cp = expdir;
7650 enddir[-1] = '\0';
7651 }
7652 dirp = opendir(cp); 7656 dirp = opendir(cp);
7653 if (dirp == NULL) 7657 if (dirp == NULL)
7654 return; 7658 return;
7655 if (enddir != expdir)
7656 enddir[-1] = '/';
7657 if (*endname == 0) { 7659 if (*endname == 0) {
7658 atend = 1; 7660 atend = 1;
7659 } else { 7661 } else {
@@ -7661,6 +7663,7 @@ expmeta(char *expdir, char *enddir, char *name)
7661 *endname = '\0'; 7663 *endname = '\0';
7662 endname += esc + 1; 7664 endname += esc + 1;
7663 } 7665 }
7666 name_len -= endname - name;
7664 matchdot = 0; 7667 matchdot = 0;
7665 p = start; 7668 p = start;
7666 if (*p == '\\') 7669 if (*p == '\\')
@@ -7675,16 +7678,30 @@ expmeta(char *expdir, char *enddir, char *name)
7675 strcpy(enddir, dp->d_name); 7678 strcpy(enddir, dp->d_name);
7676 addfname(expdir); 7679 addfname(expdir);
7677 } else { 7680 } else {
7678 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';) 7681 unsigned offset;
7679 continue; 7682 unsigned len;
7680 p[-1] = '/'; 7683
7681 expmeta(expdir, p, endname); 7684 p = stpcpy(enddir, dp->d_name);
7685 *p = '/';
7686
7687 offset = p - expdir + 1;
7688 len = offset + name_len + NAME_MAX;
7689 if (len > expdir_max) {
7690 len += PATH_MAX;
7691 expdir = ckrealloc(expdir, len);
7692 expdir_max = len;
7693 }
7694
7695 expmeta(exp, endname, name_len, offset);
7696 enddir = expdir + expdir_len;
7682 } 7697 }
7683 } 7698 }
7684 } 7699 }
7685 closedir(dirp); 7700 closedir(dirp);
7686 if (!atend) 7701 if (!atend)
7687 endname[-esc - 1] = esc ? '\\' : '/'; 7702 endname[-esc - 1] = esc ? '\\' : '/';
7703#undef expdir
7704#undef expdir_max
7688} 7705}
7689 7706
7690static struct strlist * 7707static struct strlist *
@@ -7757,10 +7774,11 @@ expandmeta(struct strlist *str /*, int flag*/)
7757 /* TODO - EXP_REDIR */ 7774 /* TODO - EXP_REDIR */
7758 7775
7759 while (str) { 7776 while (str) {
7760 char *expdir; 7777 exp_t exp;
7761 struct strlist **savelastp; 7778 struct strlist **savelastp;
7762 struct strlist *sp; 7779 struct strlist *sp;
7763 char *p; 7780 char *p;
7781 unsigned len;
7764 7782
7765 if (fflag) 7783 if (fflag)
7766 goto nometa; 7784 goto nometa;
@@ -7770,13 +7788,12 @@ expandmeta(struct strlist *str /*, int flag*/)
7770 7788
7771 INT_OFF; 7789 INT_OFF;
7772 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); 7790 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7773 { 7791 len = strlen(p);
7774 int i = strlen(str->text); 7792 exp.dir_max = len + PATH_MAX;
7775//BUGGY estimation of how long expanded name can be 7793 exp.dir = ckmalloc(exp.dir_max);
7776 expdir = ckmalloc(i < 2048 ? 2048 : i+1); 7794
7777 } 7795 expmeta(&exp, p, len, 0);
7778 expmeta(expdir, expdir, p); 7796 free(exp.dir);
7779 free(expdir);
7780 if (p != str->text) 7797 if (p != str->text)
7781 free(p); 7798 free(p);
7782 INT_ON; 7799 INT_ON;