vfscanf: Avoid multiple reads of multi-byte character width

This avoids a race condition if the process-global locale is changed
while vfscanf is running.  MB_LEN_MAX is always larger than MB_CUR_MAX,
so we might realloc earlier than necessary (but even MB_CUR_MAX could
be larger than the minimum required space).

The existing length was a bit questionable because str + MB_LEN_MAX
might point past the end of the buffer.
This commit is contained in:
Florian Weimer 2016-09-02 15:59:34 +02:00
parent 326e288b1e
commit 9dd87afbf1
2 changed files with 18 additions and 12 deletions

View File

@ -1,3 +1,9 @@
2016-09-02 Florian Weimer <fweimer@redhat.com>
* stdio-common/vfscanf.c (_IO_vfwscanf): Use MB_LEN_MAX instead of
MB_CUR_MAX to avoid race condition. Avoid pointer arithmetic
outside of allocated array.
2016-09-02 Florian Weimer <fweimer@redhat.com> 2016-09-02 Florian Weimer <fweimer@redhat.com>
* stdio-common/vfprintf.c (process_string_arg): Use MB_LEN_MAX * stdio-common/vfprintf.c (process_string_arg): Use MB_LEN_MAX

View File

@ -757,7 +757,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
size_t n; size_t n;
if (!(flags & SUPPRESS) && (flags & POSIX_MALLOC) if (!(flags & SUPPRESS) && (flags & POSIX_MALLOC)
&& str + MB_CUR_MAX >= *strptr + strsize) && *strptr + strsize - str <= MB_LEN_MAX)
{ {
/* We have to enlarge the buffer if the `m' flag /* We have to enlarge the buffer if the `m' flag
was given. */ was given. */
@ -769,7 +769,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
{ {
/* Can't allocate that much. Last-ditch effort. */ /* Can't allocate that much. Last-ditch effort. */
newstr = (char *) realloc (*strptr, newstr = (char *) realloc (*strptr,
strleng + MB_CUR_MAX); strleng + MB_LEN_MAX);
if (newstr == NULL) if (newstr == NULL)
{ {
/* c can't have `a' flag, only `m'. */ /* c can't have `a' flag, only `m'. */
@ -780,7 +780,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
{ {
*strptr = newstr; *strptr = newstr;
str = newstr + strleng; str = newstr + strleng;
strsize = strleng + MB_CUR_MAX; strsize = strleng + MB_LEN_MAX;
} }
} }
else else
@ -1048,7 +1048,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
size_t n; size_t n;
if (!(flags & SUPPRESS) && (flags & MALLOC) if (!(flags & SUPPRESS) && (flags & MALLOC)
&& str + MB_CUR_MAX >= *strptr + strsize) && *strptr + strsize - str <= MB_LEN_MAX)
{ {
/* We have to enlarge the buffer if the `a' or `m' /* We have to enlarge the buffer if the `a' or `m'
flag was given. */ flag was given. */
@ -1061,7 +1061,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
/* Can't allocate that much. Last-ditch /* Can't allocate that much. Last-ditch
effort. */ effort. */
newstr = (char *) realloc (*strptr, newstr = (char *) realloc (*strptr,
strleng + MB_CUR_MAX); strleng + MB_LEN_MAX);
if (newstr == NULL) if (newstr == NULL)
{ {
if (flags & POSIX_MALLOC) if (flags & POSIX_MALLOC)
@ -1081,7 +1081,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
{ {
*strptr = newstr; *strptr = newstr;
str = newstr + strleng; str = newstr + strleng;
strsize = strleng + MB_CUR_MAX; strsize = strleng + MB_LEN_MAX;
} }
} }
else else
@ -1097,7 +1097,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
if (__glibc_unlikely (n == (size_t) -1)) if (__glibc_unlikely (n == (size_t) -1))
encode_error (); encode_error ();
assert (n <= MB_CUR_MAX); assert (n <= MB_LEN_MAX);
str += n; str += n;
} }
#else #else
@ -2675,7 +2675,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
/* Possibly correct character, just not enough /* Possibly correct character, just not enough
input. */ input. */
++cnt; ++cnt;
assert (cnt < MB_CUR_MAX); assert (cnt < MB_LEN_MAX);
continue; continue;
} }
cnt = 0; cnt = 0;
@ -2827,7 +2827,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
if (!(flags & SUPPRESS)) if (!(flags & SUPPRESS))
{ {
if ((flags & MALLOC) if ((flags & MALLOC)
&& str + MB_CUR_MAX >= *strptr + strsize) && *strptr + strsize - str <= MB_LEN_MAX)
{ {
/* Enlarge the buffer. */ /* Enlarge the buffer. */
size_t strleng = str - *strptr; size_t strleng = str - *strptr;
@ -2839,7 +2839,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
/* Can't allocate that much. Last-ditch /* Can't allocate that much. Last-ditch
effort. */ effort. */
newstr = (char *) realloc (*strptr, newstr = (char *) realloc (*strptr,
strleng + MB_CUR_MAX); strleng + MB_LEN_MAX);
if (newstr == NULL) if (newstr == NULL)
{ {
if (flags & POSIX_MALLOC) if (flags & POSIX_MALLOC)
@ -2859,7 +2859,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
{ {
*strptr = newstr; *strptr = newstr;
str = newstr + strleng; str = newstr + strleng;
strsize = strleng + MB_CUR_MAX; strsize = strleng + MB_LEN_MAX;
} }
} }
else else
@ -2875,7 +2875,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
if (__glibc_unlikely (n == (size_t) -1)) if (__glibc_unlikely (n == (size_t) -1))
encode_error (); encode_error ();
assert (n <= MB_CUR_MAX); assert (n <= MB_LEN_MAX);
str += n; str += n;
} }
while (--width > 0 && inchar () != WEOF); while (--width > 0 && inchar () != WEOF);