From b2d27c71bd13820a4263fa7ebda4c1a4a95b501c Mon Sep 17 00:00:00 2001 From: Will Newton Date: Fri, 8 May 2015 01:15:19 +0300 Subject: _scanf.c: Implement 'm' modifier for 'c' and '[' conversions. The current code implements the 'm' modifier only for 's' conversions and would cause a segfault if it was used for 'c' or '[' conversions. This patch extends the code to cover these cases too. The original version could write scanned data outside the passed buffer because index i used in the '[' conversion handling block was clobbered. Signed-off-by: Will Newton Signed-off-by: Max Filippov Signed-off-by: Bernhard Reutner-Fischer --- libc/stdio/_scanf.c | 51 +++++++++++++++++++++++++++++++++------------------ test/stdio/scanf_m.c | 24 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 test/stdio/scanf_m.c diff --git a/libc/stdio/_scanf.c b/libc/stdio/_scanf.c index d48fd1267..f2a96700b 100644 --- a/libc/stdio/_scanf.c +++ b/libc/stdio/_scanf.c @@ -1351,7 +1351,20 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) (psfs.conv_num >= CONV_c) #endif /* __UCLIBC_HAS_WCHAR__ */ { + /* We might have to handle the allocation ourselves */ + int len; + unsigned char **ptr; + b = (psfs.store ? ((unsigned char *) psfs.cur_ptr) : buf); + /* With 'm', we actually got a pointer to a pointer */ + ptr = (void *)b; + + if (psfs.flags & FLAG_MALLOC) { + len = 0; + b = NULL; + } else + len = -1; + fail = 1; if (psfs.conv_num == CONV_c) { @@ -1359,32 +1372,28 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) sc.width = 1; } + if (psfs.flags & FLAG_MALLOC) + b = malloc(sc.width); + + i = 0; while (__scan_getc(&sc) >= 0) { zero_conversions = 0; - *b = sc.cc; - b += psfs.store; + b[i] = sc.cc; + i += psfs.store; } __scan_ungetc(&sc); if (sc.width > 0) { /* Failed to read all required. */ goto DONE; } + if (psfs.flags & FLAG_MALLOC) + *ptr = b; psfs.cnt += psfs.store; goto NEXT_FMT; } if (psfs.conv_num == CONV_s) { - /* We might have to handle the allocation ourselves */ - int len; - /* With 'm', we actually got a pointer to a pointer */ - unsigned char **ptr = (void *)b; i = 0; - if (psfs.flags & FLAG_MALLOC) { - len = 0; - b = NULL; - } else - len = -1; - /* Yes, believe it or not, a %s conversion can store nuls. */ while ((__scan_getc(&sc) >= 0) && !isspace(sc.cc)) { zero_conversions = 0; @@ -1399,10 +1408,6 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) fail = 0; } - if (psfs.flags & FLAG_MALLOC) - *ptr = b; - /* The code below takes care of terminating NUL */ - b += i; } else { #ifdef __UCLIBC_HAS_WCHAR__ assert((psfs.conv_num == CONV_LEFTBRACKET) || \ @@ -1453,13 +1458,20 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) #endif /* __UCLIBC_HAS_WCHAR__ */ + i = 0; while (__scan_getc(&sc) >= 0) { zero_conversions = 0; if (!scanset[sc.cc]) { break; } - *b = sc.cc; - b += psfs.store; + if (i == len) { + /* Pick a size that won't trigger a lot of + * mallocs early on ... */ + len += 256; + b = realloc(b, len + 1); + } + b[i] = sc.cc; + i += psfs.store; fail = 0; } } @@ -1469,6 +1481,9 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) if (fail) { /* nothing stored! */ goto DONE; } + if (psfs.flags & FLAG_MALLOC) + *ptr = b; + b += i; *b = 0; /* Nul-terminate string. */ psfs.cnt += psfs.store; goto NEXT_FMT; diff --git a/test/stdio/scanf_m.c b/test/stdio/scanf_m.c new file mode 100644 index 000000000..0ce78b6e4 --- /dev/null +++ b/test/stdio/scanf_m.c @@ -0,0 +1,24 @@ +#include +#include +#include + +int main(void) +{ + const char *buf = "hello world"; + char *ps = NULL, *pc = NULL; + char s[6], c; + + /* Check that %[...]/%c work. */ + sscanf(buf, "%[a-z] %c", s, &c); + /* Check that %m[...]/%mc work. */ + sscanf(buf, "%m[a-z] %mc", &ps, &pc); + + if (strcmp(ps, "hello") != 0 || *pc != 'w' || + strcmp(s, "hello") != 0 || c != 'w') + return 1; + + free(ps); + free(pc); + + return 0; +} -- cgit v1.2.3