Merge "Sync with OpenBSD strptime.c." into main
diff --git a/libc/bionic/getentropy.cpp b/libc/bionic/getentropy.cpp
index 11f5028..5272c34 100644
--- a/libc/bionic/getentropy.cpp
+++ b/libc/bionic/getentropy.cpp
@@ -63,11 +63,17 @@
long count = TEMP_FAILURE_RETRY(getrandom(static_cast<char*>(buffer) + collected,
buffer_size - collected, GRND_NONBLOCK));
if (count == -1) {
+ // One of several things could have gone wrong:
// EAGAIN: there isn't enough entropy right now.
// ENOSYS/EINVAL: getrandom(2) or GRND_NONBLOCK isn't supported.
// EFAULT: `buffer` is invalid.
- // Try /dev/urandom regardless because it can't hurt,
+ // Realistically we're here because of EAGAIN,
+ // for which /dev/urandom is the solution ---
+ // it'll return low entropy randomness where getrandom() won't,
+ // but we fall back /dev/urandom for all cases because it can't hurt,
// and we don't need to optimize the EFAULT case.
+ // See https://man7.org/linux/man-pages/man7/random.7.html for getrandom()
+ // vs /dev/random vs /dev/urandom.
// See http://b/33059407 and http://b/67015565.
return getentropy_urandom(buffer, buffer_size, saved_errno);
}
diff --git a/libc/bionic/time.cpp b/libc/bionic/time.cpp
index 800395e..3b18390 100644
--- a/libc/bionic/time.cpp
+++ b/libc/bionic/time.cpp
@@ -28,6 +28,53 @@
#include <time.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+char* asctime(const tm* tm) {
+ static char buf[128];
+ return asctime_r(tm, buf);
+}
+
+char* asctime_r(const tm* tm, char* buf) {
+ if (tm == nullptr) {
+ errno = EINVAL;
+ return strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
+ }
+
+ auto pick = [](unsigned n, unsigned max, const char* s) {
+ return (n < max) ? s + 3*n : "???";
+ };
+ const char* day = pick(tm->tm_wday, 7, "SunMonTueWedThuFriSat");
+ const char* mon = pick(tm->tm_mon, 12, "JanFebMarAprMayJunJulAugSepOctNovDec");
+
+ char tmp_buf[26];
+ int n = snprintf(tmp_buf, 26, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", day, mon,
+ tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, 1900 + tm->tm_year);
+ if (n > 25) {
+ errno = EOVERFLOW;
+ return nullptr;
+ }
+ strcpy(buf, tmp_buf);
+ return buf;
+}
+
+char* ctime(const time_t* tp) {
+ static char buf[128];
+ return ctime_r(tp, buf);
+}
+
+char* ctime_r(const time_t* tp, char* buf) {
+ struct tm tm;
+ if (localtime_r(tp, &tm) == nullptr) return nullptr;
+ return asctime_r(&tm, buf);
+}
+
+double difftime(time_t t1, time_t t0) {
+ return t1 - t0;
+}
+
int timespec_get(timespec* ts, int base) {
return (clock_gettime(base - 1, ts) != -1) ? base : 0;
}
diff --git a/libc/include/time.h b/libc/include/time.h
index 3d7ac57..a002244 100644
--- a/libc/include/time.h
+++ b/libc/include/time.h
@@ -141,11 +141,11 @@
/**
* [difftime(3)](https://man7.org/linux/man-pages/man3/difftime.3.html) returns
- * the difference between two times.
+ * the difference between two times, equivalent to (time1 - time0).
*
* Returns the difference in seconds.
*/
-double difftime(time_t __lhs, time_t __rhs);
+double difftime(time_t __time1, time_t __time0);
/**
* [mktime(3)](https://man7.org/linux/man-pages/man3/mktime.3p.html) converts
diff --git a/libc/malloc_debug/Android.bp b/libc/malloc_debug/Android.bp
index 408a046..fadaae3 100644
--- a/libc/malloc_debug/Android.bp
+++ b/libc/malloc_debug/Android.bp
@@ -25,8 +25,9 @@
stl: "libc++_static",
whole_static_libs: [
- "libbase",
"libasync_safe",
+ "libbase",
+ "libunwindstack_demangle",
],
include_dirs: ["bionic/libc"],
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
index c8aaa08..5129bf6 100644
--- a/libc/malloc_debug/PointerData.cpp
+++ b/libc/malloc_debug/PointerData.cpp
@@ -26,7 +26,6 @@
* SUCH DAMAGE.
*/
-#include <cxxabi.h>
#include <errno.h>
#include <inttypes.h>
#include <signal.h>
@@ -49,6 +48,7 @@
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <platform/bionic/macros.h>
+#include <unwindstack/Demangle.h>
#include "Config.h"
#include "DebugData.h"
@@ -619,16 +619,9 @@
if (frame.function_name.empty()) {
dprintf(fd, " \"\" 0}");
} else {
- char* demangled_name =
- abi::__cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);
- const char* name;
- if (demangled_name != nullptr) {
- name = demangled_name;
- } else {
- name = frame.function_name.c_str();
- }
- dprintf(fd, " \"%s\" %" PRIx64 "}", name, frame.function_offset);
- free(demangled_name);
+ dprintf(fd, " \"%s\" %" PRIx64 "}",
+ unwindstack::DemangleNameIfNeeded(frame.function_name).c_str(),
+ frame.function_offset);
}
}
dprintf(fd, "\n");
diff --git a/libc/malloc_debug/UnwindBacktrace.cpp b/libc/malloc_debug/UnwindBacktrace.cpp
index 8a6ff7b..740fabe 100644
--- a/libc/malloc_debug/UnwindBacktrace.cpp
+++ b/libc/malloc_debug/UnwindBacktrace.cpp
@@ -26,7 +26,6 @@
* SUCH DAMAGE.
*/
-#include <cxxabi.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
@@ -38,6 +37,7 @@
#include <android-base/stringprintf.h>
#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Demangle.h>
#include <unwindstack/Unwinder.h>
#include "UnwindBacktrace.h"
@@ -88,14 +88,7 @@
if (!info->function_name.empty()) {
line += " (";
- char* demangled_name =
- abi::__cxa_demangle(info->function_name.c_str(), nullptr, nullptr, nullptr);
- if (demangled_name != nullptr) {
- line += demangled_name;
- free(demangled_name);
- } else {
- line += info->function_name;
- }
+ line += unwindstack::DemangleNameIfNeeded(info->function_name);
if (info->function_offset != 0) {
line += "+" + std::to_string(info->function_offset);
}
diff --git a/libc/malloc_debug/backtrace.cpp b/libc/malloc_debug/backtrace.cpp
index 6a32fca..8b58be3 100644
--- a/libc/malloc_debug/backtrace.cpp
+++ b/libc/malloc_debug/backtrace.cpp
@@ -26,7 +26,6 @@
* SUCH DAMAGE.
*/
-#include <cxxabi.h>
#include <dlfcn.h>
#include <errno.h>
#include <inttypes.h>
@@ -41,6 +40,8 @@
#include "backtrace.h"
#include "debug_log.h"
+#include <unwindstack/Demangle.h>
+
#if defined(__LP64__)
#define PAD_PTR "016" PRIxPTR
#else
@@ -154,18 +155,10 @@
char buf[1024];
if (symbol != nullptr) {
- char* demangled_name = abi::__cxa_demangle(symbol, nullptr, nullptr, nullptr);
- const char* name;
- if (demangled_name != nullptr) {
- name = demangled_name;
- } else {
- name = symbol;
- }
- async_safe_format_buffer(buf, sizeof(buf),
- " #%02zd pc %" PAD_PTR " %s%s (%s+%" PRIuPTR ")\n",
- frame_num, rel_pc, soname, offset_buf, name,
- frames[frame_num] - offset);
- free(demangled_name);
+ async_safe_format_buffer(
+ buf, sizeof(buf), " #%02zd pc %" PAD_PTR " %s%s (%s+%" PRIuPTR ")\n",
+ frame_num, rel_pc, soname, offset_buf, unwindstack::DemangleNameIfNeeded(symbol).c_str(),
+ frames[frame_num] - offset);
} else {
async_safe_format_buffer(buf, sizeof(buf), " #%02zd pc %" PAD_PTR " %s%s\n",
frame_num, rel_pc, soname, offset_buf);
diff --git a/libc/tzcode/asctime.c b/libc/tzcode/asctime.c
deleted file mode 100644
index 4cdfd13..0000000
--- a/libc/tzcode/asctime.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/* asctime and asctime_r a la POSIX and ISO C, except pad years before 1000. */
-
-/*
-** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson.
-*/
-
-/*
-** Avoid the temptation to punt entirely to strftime;
-** the output of strftime is supposed to be locale specific
-** whereas the output of asctime is supposed to be constant.
-*/
-
-/*LINTLIBRARY*/
-
-#include "private.h"
-#include <stdio.h>
-
-/*
-** All years associated with 32-bit time_t values are exactly four digits long;
-** some years associated with 64-bit time_t values are not.
-** Vintage programs are coded for years that are always four digits long
-** and may assume that the newline always lands in the same place.
-** For years that are less than four digits, we pad the output with
-** leading zeroes to get the newline in the traditional place.
-** The -4 ensures that we get four characters of output even if
-** we call a strftime variant that produces fewer characters for some years.
-** The ISO C and POSIX standards prohibit padding the year,
-** but many implementations pad anyway; most likely the standards are buggy.
-*/
-static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n";
-/*
-** For years that are more than four digits we put extra spaces before the year
-** so that code trying to overwrite the newline won't end up overwriting
-** a digit within a year and truncating the year (operating on the assumption
-** that no output is better than wrong output).
-*/
-static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d %s\n";
-
-enum { STD_ASCTIME_BUF_SIZE = 26 };
-/*
-** Big enough for something such as
-** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
-** (two three-character abbreviations, five strings denoting integers,
-** seven explicit spaces, two explicit colons, a newline,
-** and a trailing NUL byte).
-** The values above are for systems where an int is 32 bits and are provided
-** as an example; the size expression below is a bound for the system at
-** hand.
-*/
-static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1];
-
-/* A similar buffer for ctime.
- C89 requires that they be the same buffer.
- This requirement was removed in C99, so support it only if requested,
- as support is more likely to lead to bugs in badly written programs. */
-#if SUPPORT_C89
-# define buf_ctime buf_asctime
-#else
-static char buf_ctime[sizeof buf_asctime];
-#endif
-
-char *
-asctime_r(struct tm const *restrict timeptr, char *restrict buf)
-{
- static const char wday_name[][4] = {
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
- };
- static const char mon_name[][4] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
- };
- register const char * wn;
- register const char * mn;
- char year[INT_STRLEN_MAXIMUM(int) + 2];
- char result[sizeof buf_asctime];
-
- if (timeptr == NULL) {
- errno = EINVAL;
- return strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
- }
- if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
- wn = "???";
- else wn = wday_name[timeptr->tm_wday];
- if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
- mn = "???";
- else mn = mon_name[timeptr->tm_mon];
- /*
- ** Use strftime's %Y to generate the year, to avoid overflow problems
- ** when computing timeptr->tm_year + TM_YEAR_BASE.
- ** Assume that strftime is unaffected by other out-of-range members
- ** (e.g., timeptr->tm_mday) when processing "%Y".
- */
- strftime(year, sizeof year, "%Y", timeptr);
- /*
- ** We avoid using snprintf since it's not available on all systems.
- */
- snprintf(result, sizeof(result), /* Android change: use snprintf. */
- ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
- wn, mn,
- timeptr->tm_mday, timeptr->tm_hour,
- timeptr->tm_min, timeptr->tm_sec,
- year);
- if (strlen(result) < STD_ASCTIME_BUF_SIZE
- || buf == buf_ctime || buf == buf_asctime)
- return strcpy(buf, result);
- else {
- errno = EOVERFLOW;
- return NULL;
- }
-}
-
-char *
-asctime(register const struct tm *timeptr)
-{
- return asctime_r(timeptr, buf_asctime);
-}
-
-char *
-ctime_r(const time_t *timep, char *buf)
-{
- struct tm mytm;
- struct tm *tmp = localtime_r(timep, &mytm);
- return tmp ? asctime_r(tmp, buf) : NULL;
-}
-
-char *
-ctime(const time_t *timep)
-{
- return ctime_r(timep, buf_ctime);
-}
diff --git a/libc/tzcode/difftime.c b/libc/tzcode/difftime.c
deleted file mode 100644
index ff78f03..0000000
--- a/libc/tzcode/difftime.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Return the difference between two timestamps. */
-
-/*
-** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson.
-*/
-
-/*LINTLIBRARY*/
-
-#include "private.h" /* for time_t and TYPE_SIGNED */
-
-/* Return -X as a double. Using this avoids casting to 'double'. */
-static double
-dminus(double x)
-{
- return -x;
-}
-
-double
-difftime(time_t time1, time_t time0)
-{
- /*
- ** If double is large enough, simply convert and subtract
- ** (assuming that the larger type has more precision).
- */
- if (sizeof(time_t) < sizeof(double)) {
- double t1 = time1, t0 = time0;
- return t1 - t0;
- }
-
- /*
- ** The difference of two unsigned values can't overflow
- ** if the minuend is greater than or equal to the subtrahend.
- */
- if (!TYPE_SIGNED(time_t))
- return time0 <= time1 ? time1 - time0 : dminus(time0 - time1);
-
- /* Use uintmax_t if wide enough. */
- if (sizeof(time_t) <= sizeof(uintmax_t)) {
- uintmax_t t1 = time1, t0 = time0;
- return time0 <= time1 ? t1 - t0 : dminus(t0 - t1);
- }
-
- /*
- ** Handle cases where both time1 and time0 have the same sign
- ** (meaning that their difference cannot overflow).
- */
- if ((time1 < 0) == (time0 < 0))
- return time1 - time0;
-
- /*
- ** The values have opposite signs and uintmax_t is too narrow.
- ** This suffers from double rounding; attempt to lessen that
- ** by using long double temporaries.
- */
- {
- long double t1 = time1, t0 = time0;
- return t1 - t0;
- }
-}
diff --git a/libc/tzcode/strptime.c b/libc/tzcode/strptime.c
index 525dfc4..3ae010b 100644
--- a/libc/tzcode/strptime.c
+++ b/libc/tzcode/strptime.c
@@ -632,11 +632,19 @@
char *ep;
errno = 0;
+#if defined(__LP64__)
secs = strtoll(*buf, &ep, 10);
+#else
+ secs = strtol(*buf, &ep, 10);
+#endif
if (*buf == (unsigned char *)ep)
goto done;
if (secs < 0 ||
+#if defined(__LP64__)
secs == LLONG_MAX && errno == ERANGE)
+#else
+ secs == LONG_MAX && errno == ERANGE)
+#endif
goto done;
if (localtime_r(&secs, tm) == NULL)
goto done;
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index cf4de06..73590fa 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -484,6 +484,62 @@
EXPECT_EQ(0, tm.tm_hour);
}
+TEST(time, strptime_s) {
+ setenv("TZ", "UTC", 1);
+
+ struct tm tm;
+
+ // 0 + 1 --- trivial.
+ tm = {};
+ ASSERT_EQ('\0', *strptime("1", "%s", &tm));
+ EXPECT_EQ(70, tm.tm_year);
+ EXPECT_EQ(0, tm.tm_mon);
+ EXPECT_EQ(1, tm.tm_mday);
+ EXPECT_EQ(0, tm.tm_hour);
+ EXPECT_EQ(0, tm.tm_min);
+ EXPECT_EQ(1, tm.tm_sec);
+
+ // INT32_MAX (aka "time_t max" for ILP32).
+ tm = {};
+ ASSERT_EQ('\0', *strptime("2147483647", "%s", &tm));
+ EXPECT_EQ(138, tm.tm_year);
+ EXPECT_EQ(0, tm.tm_mon);
+ EXPECT_EQ(19, tm.tm_mday);
+ EXPECT_EQ(3, tm.tm_hour);
+ EXPECT_EQ(14, tm.tm_min);
+ EXPECT_EQ(7, tm.tm_sec);
+
+ // INT32_MAX + 1 (aka overflow for ILP32).
+ // This should be easy to detect because it'll be negative.
+ tm = {};
+#if defined(__LP64__)
+ ASSERT_EQ('\0', *strptime("2147483648", "%s", &tm));
+#else
+ ASSERT_EQ(nullptr, strptime("2147483648", "%s", &tm));
+#endif
+
+ // This wraps to 1 as an int32_t.
+ tm = {};
+#if defined(__LP64__)
+ ASSERT_EQ('\0', *strptime("4294967297", "%s", &tm));
+ EXPECT_EQ(206, tm.tm_year);
+ EXPECT_EQ(1, tm.tm_mon);
+ EXPECT_EQ(7, tm.tm_mday);
+ EXPECT_EQ(6, tm.tm_hour);
+ EXPECT_EQ(28, tm.tm_min);
+ EXPECT_EQ(17, tm.tm_sec);
+#else
+ ASSERT_EQ(nullptr, strptime("4294967297", "%s", &tm));
+#endif
+
+ // INT64_MAX (aka "time_t max" for LP64).
+ // This actually fails for LP64 too...
+ // ...but in localtime_r() because the year is too large.
+ // (Wolfram Alpha says this is 21 times the age of the universe!)
+ tm = {};
+ ASSERT_EQ(nullptr, strptime("9223372036854775807", "%s", &tm));
+}
+
TEST(time, strptime_u) {
setenv("TZ", "UTC", 1);
@@ -1197,17 +1253,63 @@
}
TEST(time, asctime) {
- const struct tm tm = {};
+ const tm tm = {};
ASSERT_STREQ("Sun Jan 0 00:00:00 1900\n", asctime(&tm));
}
TEST(time, asctime_r) {
- const struct tm tm = {};
+ const tm tm = {};
char buf[256];
ASSERT_EQ(buf, asctime_r(&tm, buf));
ASSERT_STREQ("Sun Jan 0 00:00:00 1900\n", buf);
}
+TEST(time, asctime_nullptr) {
+ tm* smuggled_null = nullptr;
+ char buf[256];
+ // I'd argue that the glibc behavior is more reasonable,
+ // but traditionally we've had the BSD behavior.
+ errno = 0;
+#if defined(__GLIBC__)
+ ASSERT_EQ(nullptr, asctime_r(smuggled_null, buf));
+#else
+ ASSERT_EQ(buf, asctime_r(smuggled_null, buf));
+ ASSERT_STREQ("??? ??? ?? ??:??:?? ????\n", buf);
+#endif
+ ASSERT_ERRNO(EINVAL);
+}
+
+TEST(time, asctime_bad_wday) {
+ // This is undefined behavior, but our traditional behavior is to substitute "???".
+ tm tm = { .tm_wday = -1 };
+ char buf[256];
+ ASSERT_EQ(buf, asctime_r(&tm, buf));
+ ASSERT_STREQ("??? Jan 0 00:00:00 1900\n", buf);
+ tm.tm_wday = 7;
+ ASSERT_EQ(buf, asctime_r(&tm, buf));
+ ASSERT_STREQ("??? Jan 0 00:00:00 1900\n", buf);
+}
+
+TEST(time, asctime_bad_mon) {
+ // This is undefined behavior, but our traditional behavior is to substitute "???".
+ tm tm = { .tm_mon = -1 };
+ char buf[256];
+ ASSERT_EQ(buf, asctime_r(&tm, buf));
+ ASSERT_STREQ("Sun ??? 0 00:00:00 1900\n", buf);
+ tm.tm_mon = 12;
+ ASSERT_EQ(buf, asctime_r(&tm, buf));
+ ASSERT_STREQ("Sun ??? 0 00:00:00 1900\n", buf);
+}
+
+TEST(time, asctime_bad_year) {
+ // This is undefined behavior, but our traditional behavior is to return NULL/EOVERFLOW.
+ tm tm = { .tm_year = 99999 };
+ char buf[256];
+ errno = 0;
+ ASSERT_EQ(nullptr, asctime_r(&tm, buf));
+ ASSERT_ERRNO(EOVERFLOW);
+}
+
TEST(time, ctime) {
setenv("TZ", "UTC", 1);
const time_t t = 0;