When using musl libc glob() a very long string can cause glob() to fail,
which leads to an out of memory error being raised by ash.

This can happen easily if a very long quoted string contains *, even
though no glob expansion should ever be performed on it (since it's
quoted).

Fix this by properly parsing control characters and escaping and only
accept unquoted metacharacters. While we're at it, unify this check for
libc and built-in glob expansion

Signed-off-by: Felix Fietkau <[email protected]>
---
 shell/ash.c | 61 ++++++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 44 insertions(+), 17 deletions(-)

diff --git a/shell/ash.c b/shell/ash.c
index d8f41327b..642a2c727 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -7159,6 +7159,47 @@ addfname(const char *name)
        exparg.lastp = &sp->next;
 }
 
+static int hasmeta(struct strlist *str)
+{
+       static const char chars[] ALIGN1 = {
+               '*', '?', '[', '\\', CTLQUOTEMARK, CTLESC, 0
+       };
+       static const char qchars[] ALIGN1 = {
+               CTLQUOTEMARK, CTLESC, 0
+       };
+       char *p;
+
+       /* Avoid glob() (and thus, stat() et al) for words like "echo" */
+       for (p = str->text; p && *p; p = strpbrk(p + 1, chars)) {
+               switch ((unsigned char) *p) {
+               case CTLQUOTEMARK:
+                       while (1) {
+                               p = strpbrk(p + 1, qchars);
+                               if (!p)
+                                       return 0;
+                               if (*p == CTLQUOTEMARK)
+                                       break;
+                               p++;
+                       }
+                       continue;
+               case '\\':
+               case CTLESC:
+                       if (!p[1])
+                               return 0;
+
+                       p++;
+                       continue;
+               case '*':
+               case '?':
+                       return 1;
+               case '[':
+                       return !!strchr(p + 1, ']');
+               }
+       }
+
+       return 0;
+}
+
 /* If we want to use glob() from libc... */
 #if !ENABLE_ASH_INTERNAL_GLOB
 
@@ -7185,20 +7226,9 @@ expandmeta(struct strlist *str /*, int flag*/)
                if (fflag)
                        goto nometa;
 
-               /* Avoid glob() (and thus, stat() et al) for words like "echo" 
*/
-               p = str->text;
-               while (*p) {
-                       if (*p == '*')
-                               goto need_glob;
-                       if (*p == '?')
-                               goto need_glob;
-                       if (*p == '[')
-                               goto need_glob;
-                       p++;
-               }
-               goto nometa;
+               if (!hasmeta(str))
+                       goto nometa;
 
- need_glob:
                INT_OFF;
                p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
 // GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
@@ -7435,9 +7465,6 @@ expsort(struct strlist *str)
 static void
 expandmeta(struct strlist *str /*, int flag*/)
 {
-       static const char metachars[] ALIGN1 = {
-               '*', '?', '[', 0
-       };
        /* TODO - EXP_REDIR */
 
        while (str) {
@@ -7448,7 +7475,7 @@ expandmeta(struct strlist *str /*, int flag*/)
 
                if (fflag)
                        goto nometa;
-               if (!strpbrk(str->text, metachars))
+               if (!hasmeta(str))
                        goto nometa;
                savelastp = exparg.lastp;
 
-- 
2.11.0

_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to