Message ID | 87a5x07jxz.fsf@oldenburg.str.redhat.com |
---|---|
State | New |
Headers | show |
Series | [v2] string: strerror must not return NULL (bug 30555) | expand |
> For strerror, this fixes commit 28aff047818eb1726394296d27b ("string: > Implement strerror in terms of strerror_l"). This commit avoids > returning NULL for strerror_l as well, although POSIX allows this > behavior for strerror_l. This looks good to me. Reviewed-by: Arjun Shankar <arjun@redhat.com> > > --- > v2: Change wording of comment as suggested by Andreas. > string/Makefile | 1 + > string/strerror_l.c | 13 +++++--- > string/tst-strerror-fail.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 87 insertions(+), 4 deletions(-) > > diff --git a/string/Makefile b/string/Makefile > index 0ad276cc6a..d3106d10a9 100644 > --- a/string/Makefile > +++ b/string/Makefile > @@ -178,6 +178,7 @@ tests := \ > tst-endian \ > tst-inlcall \ > tst-memmove-overflow \ > + tst-strerror-fail \ > tst-strfry \ > tst-strlcat \ > tst-strlcpy \ OK. Add new test to Makefile. > diff --git a/string/strerror_l.c b/string/strerror_l.c > index 7fe9b1d903..e5c8666198 100644 > --- a/string/strerror_l.c > +++ b/string/strerror_l.c We want to: 1. Try to return "Unknown error NNN" with the error number included. 2. If that fails, simply return "Unknown error" and not NULL. > @@ -43,10 +43,15 @@ __strerror_l (int errnum, locale_t loc) > struct tls_internal_t *tls_internal = __glibc_tls_internal (); > free (tls_internal->strerror_l_buf); Buffer freed here unconditionally. > if (__asprintf (&tls_internal->strerror_l_buf, "%s%d", > - translate ("Unknown error ", loc), errnum) == -1) > - tls_internal->strerror_l_buf = NULL; > - > - err = tls_internal->strerror_l_buf; Earlier, if asprintf failed, we set the thread local buffer pointer to NULL and eventually returned that. Now: > + translate ("Unknown error ", loc), errnum) > 0) OK. We consider any negative or 0 return value (i.e. 0 bytes printed) from asprintf as error, which is safer than just considering -1 as an error. > + err = tls_internal->strerror_l_buf; OK. Success case comes first with the negated condition above. > + else > + { > + /* The memory was freed above. */ > + tls_internal->strerror_l_buf = NULL; OK. Yes, we did just free the buffer, so we set it to NULL. > + /* Provide a fallback translation. */ > + err = (char *) translate ("Unknown error", loc); > + } OK. We give up trying to return "Unknown error NNN" and simply return "Unknown error" instead. > } > else > err = (char *) translate (err, loc); > diff --git a/string/tst-strerror-fail.c b/string/tst-strerror-fail.c > new file mode 100644 > index 0000000000..e0fa45ab2b > --- /dev/null > +++ b/string/tst-strerror-fail.c > @@ -0,0 +1,77 @@ > +/* Check that strerror, strerror_l do not return NULL on failure (bug 30555). > + Copyright (C) 2023 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > + > +#include <locale.h> > +#include <stdbool.h> > +#include <stdlib.h> > +#include <string.h> > +#include <support/check.h> > +#include <support/namespace.h> > +#include <support/xdlfcn.h> > + > +/* Interposed malloc that can be used to inject allocation failures. */ > + > +static volatile bool fail_malloc; > + > +void * > +malloc (size_t size) > +{ > + if (fail_malloc) > + return NULL; > + > + static void *(*original_malloc) (size_t); > + if (original_malloc == NULL) > + original_malloc = xdlsym (RTLD_NEXT, "malloc"); > + return original_malloc (size); > +} OK. > + > +/* Callbacks for the actual tests. Use fork to run both tests with a > + clean state. */ > + > +static void > +test_strerror (void *closure) > +{ > + fail_malloc = true; > + const char *s = strerror (999); > + fail_malloc = false; OK. Force malloc failure. > + TEST_COMPARE_STRING (s, "Unknown error"); OK. Check for the correct string. > +} > + > +static void > +test_strerror_l (void *closure) > +{ > + locale_t loc = newlocale (LC_ALL, "C", (locale_t) 0); > + TEST_VERIFY (loc != (locale_t) 0); > + fail_malloc = true; > + const char *s = strerror_l (999, loc); > + fail_malloc = false; > + TEST_COMPARE_STRING (s, "Unknown error"); > + freelocale (loc); > +} OK. Same. > + > +static int > +do_test (void) > +{ > + support_isolate_in_subprocess (test_strerror, NULL); > + support_isolate_in_subprocess (test_strerror_l, NULL); > + > + return 0; > +} > + > +#include <support/test-driver.c> OK. Both strerror and strerror_l shouldn't return NULL on malloc failure. > > base-commit: 388ae538ddcb05c7d8966147b488a5f6e481656e >
diff --git a/string/Makefile b/string/Makefile index 0ad276cc6a..d3106d10a9 100644 --- a/string/Makefile +++ b/string/Makefile @@ -178,6 +178,7 @@ tests := \ tst-endian \ tst-inlcall \ tst-memmove-overflow \ + tst-strerror-fail \ tst-strfry \ tst-strlcat \ tst-strlcpy \ diff --git a/string/strerror_l.c b/string/strerror_l.c index 7fe9b1d903..e5c8666198 100644 --- a/string/strerror_l.c +++ b/string/strerror_l.c @@ -43,10 +43,15 @@ __strerror_l (int errnum, locale_t loc) struct tls_internal_t *tls_internal = __glibc_tls_internal (); free (tls_internal->strerror_l_buf); if (__asprintf (&tls_internal->strerror_l_buf, "%s%d", - translate ("Unknown error ", loc), errnum) == -1) - tls_internal->strerror_l_buf = NULL; - - err = tls_internal->strerror_l_buf; + translate ("Unknown error ", loc), errnum) > 0) + err = tls_internal->strerror_l_buf; + else + { + /* The memory was freed above. */ + tls_internal->strerror_l_buf = NULL; + /* Provide a fallback translation. */ + err = (char *) translate ("Unknown error", loc); + } } else err = (char *) translate (err, loc); diff --git a/string/tst-strerror-fail.c b/string/tst-strerror-fail.c new file mode 100644 index 0000000000..e0fa45ab2b --- /dev/null +++ b/string/tst-strerror-fail.c @@ -0,0 +1,77 @@ +/* Check that strerror, strerror_l do not return NULL on failure (bug 30555). + Copyright (C) 2023 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + + +#include <locale.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <support/check.h> +#include <support/namespace.h> +#include <support/xdlfcn.h> + +/* Interposed malloc that can be used to inject allocation failures. */ + +static volatile bool fail_malloc; + +void * +malloc (size_t size) +{ + if (fail_malloc) + return NULL; + + static void *(*original_malloc) (size_t); + if (original_malloc == NULL) + original_malloc = xdlsym (RTLD_NEXT, "malloc"); + return original_malloc (size); +} + +/* Callbacks for the actual tests. Use fork to run both tests with a + clean state. */ + +static void +test_strerror (void *closure) +{ + fail_malloc = true; + const char *s = strerror (999); + fail_malloc = false; + TEST_COMPARE_STRING (s, "Unknown error"); +} + +static void +test_strerror_l (void *closure) +{ + locale_t loc = newlocale (LC_ALL, "C", (locale_t) 0); + TEST_VERIFY (loc != (locale_t) 0); + fail_malloc = true; + const char *s = strerror_l (999, loc); + fail_malloc = false; + TEST_COMPARE_STRING (s, "Unknown error"); + freelocale (loc); +} + +static int +do_test (void) +{ + support_isolate_in_subprocess (test_strerror, NULL); + support_isolate_in_subprocess (test_strerror_l, NULL); + + return 0; +} + +#include <support/test-driver.c>