From b5f053b5a7deb084e7a052d527e0aa41339ae05c Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Sat, 8 Sep 2012 09:17:54 +0300 Subject: [PATCH] Make strerror(3) and strsignal(3) thread-safe, and add psignal(3) and psiginfo(3). Change-Id: I426109db25e907980d6cb3a7a695796e45783b78 --- libc/Android.mk | 7 +- libc/bionic/ThreadLocalBuffer.h | 59 +++++++ .../strsignal.c => bionic/strerror.cpp} | 20 +-- libc/bionic/strerror_r.cpp | 71 ++++++++ libc/bionic/strsignal.cpp | 39 +++++ libc/include/signal.h | 2 + libc/string/strerror_r.c | 163 ------------------ libc/upstream-netbsd/extern.h | 22 +++ .../libc/gen/psignal.c} | 62 ++++++- tests/Android.mk | 1 + tests/string_test.cpp | 107 ++++++++++++ 11 files changed, 368 insertions(+), 185 deletions(-) create mode 100644 libc/bionic/ThreadLocalBuffer.h rename libc/{unistd/strsignal.c => bionic/strerror.cpp} (80%) create mode 100644 libc/bionic/strerror_r.cpp create mode 100644 libc/bionic/strsignal.cpp delete mode 100644 libc/string/strerror_r.c create mode 100644 libc/upstream-netbsd/extern.h rename libc/{string/strerror.c => upstream-netbsd/libc/gen/psignal.c} (57%) create mode 100644 tests/string_test.cpp diff --git a/libc/Android.mk b/libc/Android.mk index 65a738aa8..2604404ac 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -61,7 +61,6 @@ libc_common_src_files := \ unistd/sigwait.c \ unistd/sleep.c \ unistd/statfs.c \ - unistd/strsignal.c \ unistd/syslog.c \ unistd/system.c \ unistd/tcgetpgrp.c \ @@ -191,8 +190,6 @@ libc_common_src_files := \ string/strcoll.c \ string/strcspn.c \ string/strdup.c \ - string/strerror.c \ - string/strerror_r.c \ string/strlcat.c \ string/strlcpy.c \ string/strncat.c \ @@ -291,6 +288,9 @@ libc_common_src_files := \ bionic/sched_cpucount.c \ bionic/semaphore.c \ bionic/sha1.c \ + bionic/strerror.cpp \ + bionic/strerror_r.cpp \ + bionic/strsignal.cpp \ bionic/stubs.cpp \ bionic/system_properties.c \ bionic/tdestroy.c \ @@ -332,6 +332,7 @@ libc_common_src_files := \ libc_upstream_netbsd_src_files := \ upstream-netbsd/libc/compat-43/creat.c \ upstream-netbsd/libc/gen/nice.c \ + upstream-netbsd/libc/gen/psignal.c \ upstream-netbsd/libc/regex/regcomp.c \ upstream-netbsd/libc/regex/regerror.c \ upstream-netbsd/libc/regex/regexec.c \ diff --git a/libc/bionic/ThreadLocalBuffer.h b/libc/bionic/ThreadLocalBuffer.h new file mode 100644 index 000000000..99acdba17 --- /dev/null +++ b/libc/bionic/ThreadLocalBuffer.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _BIONIC_THREAD_LOCAL_BUFFER_H_included +#define _BIONIC_THREAD_LOCAL_BUFFER_H_included + +#include +#include + +// libstdc++ currently contains __cxa_guard_acquire and __cxa_guard_release, +// so we make do with macros instead of a C++ class. +// TODO: move __cxa_guard_acquire and __cxa_guard_release into libc. + +#define GLOBAL_INIT_THREAD_LOCAL_BUFFER(name) \ + static pthread_once_t name ## _once; \ + static pthread_key_t name ## _key; \ + static void name ## _key_destroy(void* buffer) { \ + free(buffer); \ + } \ + static void name ## _key_init() { \ + pthread_key_create(&name ## _key, name ## _key_destroy); \ + } + +// Leaves "name_buffer" and "name_byte_count" defined and initialized. +#define LOCAL_INIT_THREAD_LOCAL_BUFFER(type, name, byte_count) \ + pthread_once(&name ## _once, name ## _key_init); \ + type name ## _buffer = reinterpret_cast(pthread_getspecific(name ## _key)); \ + if (name ## _buffer == NULL) { \ + name ## _buffer = reinterpret_cast(malloc(byte_count)); \ + pthread_setspecific(name ## _key, name ## _buffer); \ + } \ + const size_t name ## _buffer_size = byte_count + +#endif // _BIONIC_THREAD_LOCAL_BUFFER_H_included diff --git a/libc/unistd/strsignal.c b/libc/bionic/strerror.cpp similarity index 80% rename from libc/unistd/strsignal.c rename to libc/bionic/strerror.cpp index 171de3d82..036ec8da0 100644 --- a/libc/unistd/strsignal.c +++ b/libc/bionic/strerror.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,14 +25,14 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#include -char* -strsignal(int sig) -{ - if ((unsigned)sig < NSIG) - return (char*) sys_siglist[sig]; - else - return "Invalid signal number"; +#include +#include "ThreadLocalBuffer.h" + +GLOBAL_INIT_THREAD_LOCAL_BUFFER(strerror); + +char* strerror(int error_number) { + LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, strerror, NL_TEXTMAX); + strerror_r(error_number, strerror_buffer, strerror_buffer_size); + return strerror_buffer; } diff --git a/libc/bionic/strerror_r.cpp b/libc/bionic/strerror_r.cpp new file mode 100644 index 000000000..92235a51d --- /dev/null +++ b/libc/bionic/strerror_r.cpp @@ -0,0 +1,71 @@ +/* $OpenBSD: strerror_r.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */ +/* Public Domain */ + +#include +#include +#include +#include +#include + +struct Pair { + int code; + const char* msg; +}; + +static const char* __code_string_lookup(const Pair* strings, int code) { + for (size_t i = 0; strings[i].msg != NULL; ++i) { + if (strings[i].code == code) { + return strings[i].msg; + } + } + return NULL; +} + +static const Pair _sys_error_strings[] = { +#define __BIONIC_ERRDEF(x,y,z) { x, z }, +#include + { 0, NULL } +}; + +int strerror_r(int error_number, char* buf, size_t buf_len) { + int saved_errno = errno; + size_t length; + + const char* error_name = __code_string_lookup(_sys_error_strings, error_number); + if (error_name != NULL) { + length = snprintf(buf, buf_len, "%s", error_name); + } else { + length = snprintf(buf, buf_len, "Unknown error %u", error_number); + } + if (length >= buf_len) { + errno = ERANGE; + return -1; + } + + errno = saved_errno; + return 0; +} + +static const Pair _sys_signal_strings[] = { +#define __BIONIC_SIGDEF(x,y,z) { y, z }, +#include + { 0, NULL } +}; + +extern "C" const char* __strsignal(int signal_number, char* buf, size_t buf_len) { + const char* signal_name = __code_string_lookup(_sys_signal_strings, signal_number); + if (signal_name != NULL) { + return signal_name; + } + + const char* prefix = "Unknown"; + if (signal_number >= SIGRTMIN && signal_number <= SIGRTMAX) { + prefix = "Real-time"; + signal_number -= SIGRTMIN; + } + size_t length = snprintf(buf, buf_len, "%s signal %d", prefix, signal_number); + if (length >= buf_len) { + return NULL; + } + return buf; +} diff --git a/libc/bionic/strsignal.cpp b/libc/bionic/strsignal.cpp new file mode 100644 index 000000000..1cbec9b5c --- /dev/null +++ b/libc/bionic/strsignal.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include "ThreadLocalBuffer.h" + +extern "C" const char* __strsignal(int, char*, size_t); + +GLOBAL_INIT_THREAD_LOCAL_BUFFER(strsignal); + +char* strsignal(int signal_number) { + LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, strsignal, NL_TEXTMAX); + return const_cast(__strsignal(signal_number, strsignal_buffer, strsignal_buffer_size)); +} diff --git a/libc/include/signal.h b/libc/include/signal.h index 91c3b00ad..6432c189b 100644 --- a/libc/include/signal.h +++ b/libc/include/signal.h @@ -125,6 +125,8 @@ extern int kill(pid_t, int); extern int killpg(int pgrp, int sig); extern int sigaltstack(const stack_t *ss, stack_t *oss); +extern void psiginfo(const siginfo_t* si, const char* message); +extern void psignal(int signal_number, const char* message); __END_DECLS diff --git a/libc/string/strerror_r.c b/libc/string/strerror_r.c deleted file mode 100644 index 30841f329..000000000 --- a/libc/string/strerror_r.c +++ /dev/null @@ -1,163 +0,0 @@ -/* $OpenBSD: strerror_r.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */ -/* Public Domain */ - -#define sys_siglist _sys_siglist - -#include -#include -#include -#include - -typedef struct { - int code; - const char* msg; -} CodeString; - -static const char* -__code_string_lookup( const CodeString* strings, - int code ) -{ - int nn = 0; - - for (;;) - { - if (strings[nn].msg == NULL) - break; - - if (strings[nn].code == code) - return strings[nn].msg; - - nn++; - } - return NULL; -} - - -static const CodeString _sys_error_strings[] = -{ -#define __BIONIC_ERRDEF(x,y,z) { x, z }, -#include - { 0, NULL } -}; - -static size_t -__digits10(unsigned int num) -{ - size_t i = 0; - - do { - num /= 10; - i++; - } while (num != 0); - - return i; -} - -static int -__itoa(int num, int sign, char *buffer, size_t start, size_t end) -{ - size_t pos; - unsigned int a; - int neg; - - if (sign && num < 0) { - a = -num; - neg = 1; - } - else { - a = num; - neg = 0; - } - - pos = start + __digits10(a); - if (neg) - pos++; - - if (pos < end) - buffer[pos] = '\0'; - else - return ERANGE; - pos--; - do { - buffer[pos] = (a % 10) + '0'; - pos--; - a /= 10; - } while (a != 0); - if (neg) - buffer[pos] = '-'; - return 0; -} - - -int -strerror_r(int errnum, char *strerrbuf, size_t buflen) -{ - int save_errno; - int len, ret = 0; - const char* msg; - - save_errno = errno; - msg = __code_string_lookup( _sys_error_strings, errnum ); - if (msg != NULL) { - len = strlcpy(strerrbuf, msg, buflen); - if ((size_t)len >= buflen) - ret = ERANGE; - } else { - len = strlcpy(strerrbuf, "Unknown error: ", buflen); - if ((size_t)len >= buflen) - ret = ERANGE; - else { - int ret = __itoa(errnum, 1, strerrbuf, len, buflen); - - if (ret == 0) - ret = EINVAL; - } - } - return ret; -} - -#if 0 -static const CodeString _sys_signal_strings[] = -{ -#define SIGDEF(x,y,z) { y, z }, -#include -}; - - -static int -__num2string(int num, int sign, int setid, char *buf, size_t buflen, - char * list[], size_t max, const char *def) -{ - int ret = 0; - size_t len; - - if (0 <= num && num < max) { - len = strlcpy(buf, def, buflen); - if (len >= buflen) - ret = ERANGE; - } else { - len = strlcpy(buf, def, buflen); - if (len >= buflen) - ret = ERANGE; - else { - ret = __itoa(num, sign, buf, len, buflen); - if (ret == 0) - ret = EINVAL; - } - } - - return ret; -} - - - -#define USIGPREFIX "Unknown signal: " - -char * -__strsignal(int num, char *buf) -{ - __num2string(num, 0, 2, buf, NL_TEXTMAX, (char **)sys_siglist, NSIG, - USIGPREFIX); - return buf; -} -#endif diff --git a/libc/upstream-netbsd/extern.h b/libc/upstream-netbsd/extern.h new file mode 100644 index 000000000..942e23754 --- /dev/null +++ b/libc/upstream-netbsd/extern.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _BIONIC_NETBSD_EXTERN_H_included +#define _BIONIC_NETBSD_EXTERN_H_included + +// Placeholder. + +#endif diff --git a/libc/string/strerror.c b/libc/upstream-netbsd/libc/gen/psignal.c similarity index 57% rename from libc/string/strerror.c rename to libc/upstream-netbsd/libc/gen/psignal.c index b2ae0017d..4472be69c 100644 --- a/libc/string/strerror.c +++ b/libc/upstream-netbsd/libc/gen/psignal.c @@ -1,7 +1,8 @@ -/* $OpenBSD: strerror.c,v 1.7 2005/08/08 08:05:37 espie Exp $ */ +/* $NetBSD: psignal.c,v 1.23 2012/03/13 21:13:36 christos Exp $ */ + /* - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,14 +29,57 @@ * SUCH DAMAGE. */ -#include +#include +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)psignal.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: psignal.c,v 1.23 2012/03/13 21:13:36 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include "namespace.h" + +#include +#include + #include +#include +#include +#include -char * -strerror(int num) +#include "extern.h" + +#ifdef __weak_alias +__weak_alias(psignal,_psignal) +#endif + +void +psignal(int sig, const char *s) { - static char buf[256]; + struct iovec *v; + struct iovec iov[4]; + char buf[NL_TEXTMAX]; - (void)strerror_r(num, buf, sizeof(buf)); - return (buf); + v = iov; + if (s && *s) { + v->iov_base = __UNCONST(s); + v->iov_len = strlen(s); + v++; + v->iov_base = __UNCONST(": "); + v->iov_len = 2; + v++; + } + v->iov_base = __UNCONST(__strsignal((int)sig, buf, sizeof(buf))); + v->iov_len = strlen(v->iov_base); + v++; + v->iov_base = __UNCONST("\n"); + v->iov_len = 1; + (void)writev(STDERR_FILENO, iov, (int)((v - iov) + 1)); +} + +void +psiginfo(const siginfo_t *si, const char *s) +{ + psignal(si->si_signo, s); } diff --git a/tests/Android.mk b/tests/Android.mk index a97334175..eef459c31 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -22,6 +22,7 @@ test_src_files = \ getcwd_test.cpp \ pthread_test.cpp \ regex_test.cpp \ + string_test.cpp \ test_dynamic_ldflags = -Wl,--export-dynamic -Wl,-u,DlSymTestFunction test_dynamic_src_files = \ diff --git a/tests/string_test.cpp b/tests/string_test.cpp new file mode 100644 index 000000000..ea1491c77 --- /dev/null +++ b/tests/string_test.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +TEST(string, strerror) { + // Valid. + ASSERT_STREQ("Success", strerror(0)); + ASSERT_STREQ("Operation not permitted", strerror(1)); + + // Invalid. + ASSERT_STREQ("Unknown error 4294967295", strerror(-1)); + ASSERT_STREQ("Unknown error 1234", strerror(1234)); +} + +void* ConcurrentStrErrorFn(void* arg) { + bool equal = (strcmp("Unknown error 2002", strerror(2002)) == 0); + return reinterpret_cast(equal); +} + +#if __BIONIC__ // glibc's strerror isn't thread safe, only its strsignal. +TEST(string, strerror_concurrent) { + const char* strerror1001 = strerror(1001); + ASSERT_STREQ("Unknown error 1001", strerror1001); + + pthread_t t; + ASSERT_EQ(0, pthread_create(&t, NULL, ConcurrentStrErrorFn, NULL)); + void* result; + ASSERT_EQ(0, pthread_join(t, &result)); + ASSERT_TRUE(static_cast(result)); + + ASSERT_STREQ("Unknown error 1001", strerror1001); +} +#endif + +#if __BIONIC__ // glibc's strerror_r doesn't even have the same signature as the POSIX one. +TEST(string, strerror_r) { + char buf[256]; + + // Valid. + ASSERT_EQ(0, strerror_r(0, buf, sizeof(buf))); + ASSERT_STREQ("Success", buf); + ASSERT_EQ(0, strerror_r(1, buf, sizeof(buf))); + ASSERT_STREQ("Operation not permitted", buf); + + // Invalid. + ASSERT_EQ(0, strerror_r(-1, buf, sizeof(buf))); + ASSERT_STREQ("Unknown error 4294967295", buf); + ASSERT_EQ(0, strerror_r(1234, buf, sizeof(buf))); + ASSERT_STREQ("Unknown error 1234", buf); + + // Buffer too small. + ASSERT_EQ(-1, strerror_r(0, buf, 2)); + ASSERT_EQ(ERANGE, errno); +} +#endif + +TEST(string, strsignal) { + // A regular signal. + ASSERT_STREQ("Hangup", strsignal(1)); + + // A real-time signal. +#ifdef __GLIBC__ // glibc reserves real-time signals for internal use, and doesn't count those. + ASSERT_STREQ("Real-time signal 14", strsignal(48)); +#else + ASSERT_STREQ("Real-time signal 16", strsignal(48)); +#endif + + // Errors. + ASSERT_STREQ("Unknown signal -1", strsignal(-1)); // Too small. + ASSERT_STREQ("Unknown signal 0", strsignal(0)); // Still too small. + ASSERT_STREQ("Unknown signal 1234", strsignal(1234)); // Too large. +} + +void* ConcurrentStrSignalFn(void* arg) { + bool equal = (strcmp("Unknown signal 2002", strsignal(2002)) == 0); + return reinterpret_cast(equal); +} + +TEST(string, strsignal_concurrent) { + const char* strsignal1001 = strsignal(1001); + ASSERT_STREQ("Unknown signal 1001", strsignal1001); + + pthread_t t; + ASSERT_EQ(0, pthread_create(&t, NULL, ConcurrentStrSignalFn, NULL)); + void* result; + ASSERT_EQ(0, pthread_join(t, &result)); + ASSERT_TRUE(static_cast(result)); + + ASSERT_STREQ("Unknown signal 1001", strsignal1001); +}