diff --git a/debug/tst-fortify.c b/debug/tst-fortify.c index 03c9867714..8e94643bf2 100644 --- a/debug/tst-fortify.c +++ b/debug/tst-fortify.c @@ -1478,10 +1478,15 @@ do_test (void) character which has a multibyte representation which does not fit. */ CHK_FAIL_START - char smallbuf[2]; + char smallbuf[1]; if (wcrtomb (smallbuf, L'\x100', &s) != 2) FAIL (); CHK_FAIL_END + + /* Same input with a large enough buffer and we're good. */ + char bigenoughbuf[2]; + if (wcrtomb (bigenoughbuf, L'\x100', &s) != 2) + FAIL (); #endif wchar_t wenough[10]; diff --git a/debug/wcrtomb_chk.c b/debug/wcrtomb_chk.c index 8b6d026560..28c3ea0d2d 100644 --- a/debug/wcrtomb_chk.c +++ b/debug/wcrtomb_chk.c @@ -1,4 +1,5 @@ /* Copyright (C) 2005-2022 Free Software Foundation, Inc. + Copyright The GNU Toolchain Authors. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -25,10 +26,5 @@ size_t __wcrtomb_chk (char *s, wchar_t wchar, mbstate_t *ps, size_t buflen) { - /* We do not have to implement the full wctomb semantics since we - know that S cannot be NULL when we come here. */ - if (buflen < MB_CUR_MAX) - __chk_fail (); - - return __wcrtomb (s, wchar, ps); + return __wcrtomb_internal (s, wchar, ps, buflen); } diff --git a/include/wchar.h b/include/wchar.h index 4267985625..db83297bca 100644 --- a/include/wchar.h +++ b/include/wchar.h @@ -172,6 +172,10 @@ libc_hidden_proto (__mbrtowc) libc_hidden_proto (__mbrlen) extern size_t __wcrtomb (char *__restrict __s, wchar_t __wc, __mbstate_t *__restrict __ps) attribute_hidden; +extern size_t __wcrtomb_internal (char *__restrict __s, wchar_t __wc, + __mbstate_t *__restrict __ps, + size_t __s_size) + attribute_hidden; extern size_t __mbsrtowcs (wchar_t *__restrict __dst, const char **__restrict __src, size_t __len, __mbstate_t *__restrict __ps) diff --git a/manual/charset.texi b/manual/charset.texi index a9b5cb4a37..427db3bc80 100644 --- a/manual/charset.texi +++ b/manual/charset.texi @@ -883,11 +883,12 @@ the string @var{s}. This includes all bytes representing shift sequences. One word about the interface of the function: there is no parameter -specifying the length of the array @var{s}. Instead the function -assumes that there are at least @code{MB_CUR_MAX} bytes available since -this is the maximum length of any byte sequence representing a single -character. So the caller has to make sure that there is enough space -available, otherwise buffer overruns can occur. +specifying the length of the array @var{s}, so the caller has to make sure +that there is enough space available, otherwise buffer overruns can occur. +This version of @theglibc{} does not assume that @var{s} is at least +@var{MB_CUR_MAX} bytes long, but programs that need to run on @glibcadj{} +versions that have this assumption documented in the manual must comply +with this limit. @pindex wchar.h @code{wcrtomb} was introduced in @w{Amendment 1} to @w{ISO C90} and is diff --git a/wcsmbs/wcrtomb.c b/wcsmbs/wcrtomb.c index e17438989f..c0cce3792f 100644 --- a/wcsmbs/wcrtomb.c +++ b/wcsmbs/wcrtomb.c @@ -1,4 +1,5 @@ /* Copyright (C) 1996-2022 Free Software Foundation, Inc. + Copyright The GNU Toolchain Authors. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -34,7 +36,7 @@ static mbstate_t state; size_t -__wcrtomb (char *s, wchar_t wc, mbstate_t *ps) +__wcrtomb_internal (char *s, wchar_t wc, mbstate_t *ps, size_t s_size) { char buf[MB_LEN_MAX]; struct __gconv_step_data data; @@ -52,14 +54,11 @@ __wcrtomb (char *s, wchar_t wc, mbstate_t *ps) /* A first special case is if S is NULL. This means put PS in the initial state. */ if (s == NULL) - { - s = buf; - wc = L'\0'; - } + wc = L'\0'; /* Tell where we want to have the result. */ - data.__outbuf = (unsigned char *) s; - data.__outbufend = (unsigned char *) s + MB_CUR_MAX; + data.__outbuf = (unsigned char *) buf; + data.__outbufend = (unsigned char *) buf + sizeof buf; /* Get the conversion functions. */ fcts = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE)); @@ -101,7 +100,17 @@ __wcrtomb (char *s, wchar_t wc, mbstate_t *ps) if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT || status == __GCONV_FULL_OUTPUT) - result = data.__outbuf - (unsigned char *) s; + { + result = data.__outbuf - (unsigned char *) buf; + + if (s != NULL) + { + if (result > s_size) + __chk_fail (); + + memcpy (s, buf, result); + } + } else { result = (size_t) -1; @@ -110,5 +119,11 @@ __wcrtomb (char *s, wchar_t wc, mbstate_t *ps) return result; } + +size_t +__wcrtomb (char *s, wchar_t wc, mbstate_t *ps) +{ + return __wcrtomb_internal (s, wc, ps, (size_t) -1); +} weak_alias (__wcrtomb, wcrtomb) libc_hidden_weak (wcrtomb)