On Fri, Jul 31, 2015 at 11:18:15AM -0700, enh wrote:
> automated fuzzing caught this:
>
> #include <fnmatch.h>
> #include <string.h>
> int main() {
> char *str = strdup("*[\\$:*[:lower:]");
> fnmatch(str, str, 0x27);
> }
>
> ==14566==ERROR: AddressSanitizer: heap-buffer-overflow on address
> 0x60200000f000 at pc 0x0000004d32d9 bp 0x7ffc96df9710 sp
> 0x7ffc96df9708
> READ of size 1 at 0x60200000f000 thread T0
> #0 0x4d32d8 in fnmatch_ch
> bionic/libc/upstream-openbsd/lib/libc/gen/fnmatch.c:202:18
> #1 0x4cf780 in my_fnmatch
> bionic/libc/upstream-openbsd/lib/libc/gen/fnmatch.c:422:25
> #2 0x4d3b4a in main fnmatch-test.c:5:3
>
> 0x60200000f000 is located 0 bytes to the right of 16-byte region
> [0x60200000eff0,0x60200000f000)
> allocated by thread T0 here:
> #0 0x493ac3 in strdup
> llvm/projects/compiler-rt/lib/asan/asan_interceptors.cc:556:3
> #1 0x4d3b3a in main fnmatch-test.c:4:15
>
>
> "[[:lower:]" seems to be an even more minimal test case.
>
> this patch seems to fix the bug:
>
> diff --git a/libc/upstream-openbsd/lib/libc/gen/fnmatch.c
> b/libc/upstream-openbsd/lib/libc/gen/fnmatch.c
> index e83dc43..c419dc9 100644
> --- a/libc/upstream-openbsd/lib/libc/gen/fnmatch.c
> +++ b/libc/upstream-openbsd/lib/libc/gen/fnmatch.c
> @@ -198,7 +198,7 @@ leadingclosebrace:
> * "x-]" is not allowed unless escaped ("x-\]")
> * XXX: Fix for locale/MBCS character width
> */
> - if (((*pattern)[1] == '-') && ((*pattern)[2] != ']'))
> + if (**pattern && ((*pattern)[1] == '-') && ((*pattern)[2] !=
> ']'))
> {
> startch = *pattern;
> *pattern += (escape && ((*pattern)[2] == '\\')) ? 3 : 2;
> @@ -235,6 +235,8 @@ leadingclosebrace:
> tolower((unsigned char)**pattern)))
> result = 0;
>
> + if (!**pattern) break;
> +
> ++*pattern;
> }
>
>
>
> --
> Elliott Hughes - http://who/enh - http://jessies.org/~enh/
> Android native code/tools questions? Mail me/drop by/add me as a reviewer.
Thanks Elliott!
I can confirm with gdb that fnmatch reads past the pattern string passed
in by the caller in your test case:
Breakpoint 2, fnmatch_ch (pattern=0x7f7ffffd2340, string=0x7f7ffffd2338,
flags=Variable "flags" is not available.
) at /usr/src/lib/libc/gen/fnmatch.c:201
201 if (((*pattern)[1] == '-') && ((*pattern)[2] != ']'))
(gdb) p (*pattern)[0]
$1 = 0 '\0'
(gdb) p (const char *)&(*pattern)[-10]
$2 = 0xb2012951860 "[[:lower:]"
(gdb) up
#1 0x00000b2091056dc9 in fnmatch (pattern=0xb2012951860 "[[:lower:]",
string=0xb2012951860 "[[:lower:]", flags=Variable "flags" is not
available.
)
at /usr/src/lib/libc/gen/fnmatch.c:443
443 if (!fnmatch_ch(&pattern, &string, flags))
I'd suggest we commit this patch which adds a small formatting
tweak to yours:
Index: gen/fnmatch.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/fnmatch.c,v
retrieving revision 1.18
diff -u -p -r1.18 fnmatch.c
--- gen/fnmatch.c 11 Dec 2014 16:25:34 -0000 1.18
+++ gen/fnmatch.c 31 Jul 2015 18:53:36 -0000
@@ -198,7 +198,7 @@ leadingclosebrace:
* "x-]" is not allowed unless escaped ("x-\]")
* XXX: Fix for locale/MBCS character width
*/
- if (((*pattern)[1] == '-') && ((*pattern)[2] != ']'))
+ if (**pattern && ((*pattern)[1] == '-') && ((*pattern)[2] != ']'))
{
startch = *pattern;
*pattern += (escape && ((*pattern)[2] == '\\')) ? 3 : 2;
@@ -234,6 +234,9 @@ leadingclosebrace:
&& (tolower((unsigned char)**string) ==
tolower((unsigned char)**pattern)))
result = 0;
+
+ if (!**pattern)
+ break;
++*pattern;
}