[PATCH] ash: improve / fix glob expansion
Felix Fietkau
nbd at nbd.name
Tue Jan 31 20:33:55 UTC 2017
On 2017-01-31 21:29, Denys Vlasenko wrote:
> On Sun, Jan 29, 2017 at 2:14 PM, Felix Fietkau <nbd at nbd.name> wrote:
>> 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 at nbd.name>
>> ---
>> 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;
>> +}
>
> I plan to massage it into this - do you see any bugs?
Looks okay.
> static int
> hasmeta(const char *p)
> {
> static const char chars[] ALIGN1 = {
> '*', '?', '[', '\\', CTLQUOTEMARK, CTLESC, 0
> };
>
> for (;;) {
> switch ((unsigned char) *p) {
> case '\0':
> return 0;
> 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 '*':
> case '?':
> return 1;
> case '[':
> return strchr(p + 1, ']') != NULL;
> }
> p = strpbrk(p + 1, chars);
> if (!p)
> break;
> }
>
> return 0;
> }
>
> Oh. What about unclosed "[" followed by metachars? "file[weird_name.*"
I guess you could change it like this:
- return strchr(p + 1, ']') != NULL;
+ if (strchr(p + 1, ']'))
+ return 1;
+ break;
- Felix
More information about the busybox
mailing list