aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2017-01-31 21:58:55 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2017-01-31 21:58:55 +0100
commitb5b21126ca9217e1f721996cbef44c019185dd5d (patch)
tree1c8e640b4ea471ab7949863a9f07771a77a59e61
parentd6ace669737571c98977972b7392456f9e5156f6 (diff)
downloadbusybox-b5b21126ca9217e1f721996cbef44c019185dd5d.tar.gz
ash: improve / fix glob expansion
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 <nbd@nbd.name> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash.c71
1 files changed, 54 insertions, 17 deletions
diff --git a/shell/ash.c b/shell/ash.c
index e07b81c05..6be3ec600 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -7160,6 +7160,57 @@ addfname(const char *name)
exparg.lastp = &sp->next;
}
+/* Avoid glob() (and thus, stat() et al) for words like "echo" */
+static int
+hasmeta(const char *p)
+{
+ static const char chars[] ALIGN1 = {
+ '*', '?', '[', '\\', CTLQUOTEMARK, CTLESC, 0
+ };
+
+ for (;;) {
+ p = strpbrk(p, chars);
+ if (!p)
+ break;
+ switch ((unsigned char) *p) {
+ case CTLQUOTEMARK:
+ for (;;) {
+ p++;
+ if (*p == CTLQUOTEMARK)
+ break;
+ if (*p == CTLESC)
+ p++;
+ if (*p == '\0') /* huh? */
+ return 0;
+ }
+ break;
+ case '\\':
+ case CTLESC:
+ p++;
+ if (*p == '\0')
+ return 0;
+ break;
+ case '[':
+ if (!strchr(p + 1, ']')) {
+ /* It's not a properly closed [] pattern,
+ * but other metas may follow. Continue checking.
+ * my[file* _is_ globbed by bash
+ * and matches filenames like "my[file1".
+ */
+ break;
+ }
+ /* fallthrough */
+ default:
+ /* case '*': */
+ /* case '?': */
+ return 1;
+ }
+ p++;
+ }
+
+ return 0;
+}
+
/* If we want to use glob() from libc... */
#if !ENABLE_ASH_INTERNAL_GLOB
@@ -7186,20 +7237,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->text))
+ 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
@@ -7436,9 +7476,6 @@ expsort(struct strlist *str)
static void
expandmeta(struct strlist *str /*, int flag*/)
{
- static const char metachars[] ALIGN1 = {
- '*', '?', '[', 0
- };
/* TODO - EXP_REDIR */
while (str) {
@@ -7449,7 +7486,7 @@ expandmeta(struct strlist *str /*, int flag*/)
if (fflag)
goto nometa;
- if (!strpbrk(str->text, metachars))
+ if (!hasmeta(str->text))
goto nometa;
savelastp = exparg.lastp;