Merge "Add the L() macro for local labels to <private/bionic_asm.h>." into main
diff --git a/libc/bionic/c32rtomb.cpp b/libc/bionic/c32rtomb.cpp
index 4fa76ff..a7cd207 100644
--- a/libc/bionic/c32rtomb.cpp
+++ b/libc/bionic/c32rtomb.cpp
@@ -78,7 +78,7 @@
     length = 4;
   } else {
     errno = EILSEQ;
-    return __MB_ERR_ILLEGAL_SEQUENCE;
+    return BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE;
   }
 
   // Output the octets representing the character in chunks
diff --git a/libc/bionic/iconv.cpp b/libc/bionic/iconv.cpp
index 79a429c..5bff50a 100644
--- a/libc/bionic/iconv.cpp
+++ b/libc/bionic/iconv.cpp
@@ -160,9 +160,9 @@
 
       case UTF_8:
         src_bytes_used = mbrtoc32(&wc, *src_buf, *src_bytes_left, &ps);
-        if (src_bytes_used == __MB_ERR_ILLEGAL_SEQUENCE) {
+        if (src_bytes_used == BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE) {
           break;  // EILSEQ already set.
-        } else if (src_bytes_used == __MB_ERR_INCOMPLETE_SEQUENCE) {
+        } else if (src_bytes_used == BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE) {
           errno = EINVAL;
           return false;
         }
@@ -235,9 +235,9 @@
 
       case UTF_8:
         dst_bytes_used = c32rtomb(buf, wc, &ps);
-        if (dst_bytes_used == __MB_ERR_ILLEGAL_SEQUENCE) {
+        if (dst_bytes_used == BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE) {
           break;  // EILSEQ already set.
-        } else if (dst_bytes_used == __MB_ERR_INCOMPLETE_SEQUENCE) {
+        } else if (dst_bytes_used == BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE) {
           errno = EINVAL;
           return false;
         }
diff --git a/libc/bionic/malloc_limit.cpp b/libc/bionic/malloc_limit.cpp
index 1405a39..deb63f4 100644
--- a/libc/bionic/malloc_limit.cpp
+++ b/libc/bionic/malloc_limit.cpp
@@ -278,7 +278,7 @@
   // being called, allow a short period for the signal handler to complete
   // before failing.
   bool enabled = false;
-  size_t num_tries = 20;
+  size_t num_tries = 200;
   while (true) {
     if (!atomic_exchange(&gGlobalsMutating, true)) {
       __libc_globals.mutate([](libc_globals* globals) {
@@ -328,9 +328,16 @@
     current_allocated = Malloc(mallinfo)().uordblks;
   }
 #endif
+  // This has to be set before the enable occurs since "gAllocated" is used
+  // to compute the limit. If the enable fails, "gAllocated" is never used.
   atomic_store(&gAllocated, current_allocated);
 
-  return EnableLimitDispatchTable();
+  if (!EnableLimitDispatchTable()) {
+    // Failed to enable, reset so a future enable will pass.
+    atomic_store(&limit_enabled, false);
+    return false;
+  }
+  return true;
 }
 
 static size_t LimitUsableSize(const void* mem) {
diff --git a/libc/bionic/mbrtoc32.cpp b/libc/bionic/mbrtoc32.cpp
index d37ca66..c26dd71 100644
--- a/libc/bionic/mbrtoc32.cpp
+++ b/libc/bionic/mbrtoc32.cpp
@@ -109,7 +109,7 @@
     mbstate_set_byte(state, bytes_so_far + i, *s++);
   }
   if (i < bytes_wanted) {
-    return __MB_ERR_INCOMPLETE_SEQUENCE;
+    return BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE;
   }
 
   // Decode the octet sequence representing the character in chunks
diff --git a/libc/bionic/wchar.cpp b/libc/bionic/wchar.cpp
index bb97b3e..b8c4432 100644
--- a/libc/bionic/wchar.cpp
+++ b/libc/bionic/wchar.cpp
@@ -27,10 +27,10 @@
  */
 
 #include <errno.h>
-#include <sys/param.h>
 #include <string.h>
-#include <wchar.h>
+#include <sys/param.h>
 #include <uchar.h>
+#include <wchar.h>
 
 #include "private/bionic_mbstate.h"
 
@@ -88,10 +88,10 @@
         r = 1;
       } else {
         r = mbrtowc(nullptr, *src + i, nmc - i, state);
-        if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
+        if (r == BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE) {
           return mbstate_reset_and_return_illegal(EILSEQ, state);
         }
-        if (r == __MB_ERR_INCOMPLETE_SEQUENCE) {
+        if (r == BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE) {
           return mbstate_reset_and_return_illegal(EILSEQ, state);
         }
         if (r == 0) {
@@ -114,11 +114,11 @@
       }
     } else {
       r = mbrtowc(dst + o, *src + i, nmc - i, state);
-      if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
+      if (r == BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE) {
         *src += i;
         return mbstate_reset_and_return_illegal(EILSEQ, state);
       }
-      if (r == __MB_ERR_INCOMPLETE_SEQUENCE) {
+      if (r == BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE) {
         *src += nmc;
         return mbstate_reset_and_return_illegal(EILSEQ, state);
       }
@@ -166,7 +166,7 @@
         r = 1;
       } else {
         r = wcrtomb(buf, wc, state);
-        if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
+        if (r == BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE) {
           return r;
         }
       }
@@ -187,14 +187,14 @@
     } else if (len - o >= sizeof(buf)) {
       // Enough space to translate in-place.
       r = wcrtomb(dst + o, wc, state);
-      if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
+      if (r == BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE) {
         *src += i;
         return r;
       }
     } else {
       // May not be enough space; use temp buffer.
       r = wcrtomb(buf, wc, state);
-      if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
+      if (r == BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE) {
         *src += i;
         return r;
       }
diff --git a/libc/include/bits/bionic_multibyte_result.h b/libc/include/bits/bionic_multibyte_result.h
new file mode 100644
index 0000000..0d5cf21
--- /dev/null
+++ b/libc/include/bits/bionic_multibyte_result.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#pragma once
+
+/**
+ * @file bits/bionic_multibyte_result.h
+ * @brief Named values for the magic number return values of multibyte
+ * conversion APIs defined by C.
+ */
+
+#include <stddef.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * @brief The error values defined by C for multibyte conversion APIs.
+ *
+ * Refer to C23 7.30.1 Restartable multibyte/wide character conversion functions
+ * for more details.
+ */
+enum : size_t {
+  /// @brief An encoding error occurred. The bytes read are not a valid unicode
+  /// character, nor are they a partially valid character.
+  BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE = -1UL,
+#define BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE
+
+  /// @brief The bytes read may produce a valid unicode character, but the
+  /// sequence is incomplete. Future calls may complete the character.
+  BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE = -2UL,
+#define BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE
+
+  /// @brief The output of the call was the result of a previous successful
+  /// decoding. No new bytes were consumed.
+  ///
+  /// The common case for this return value is when mbrtoc16 returns the low
+  /// surrogate of a pair.
+  BIONIC_MULTIBYTE_RESULT_NO_BYTES_CONSUMED = -3UL,
+#define BIONIC_MULTIBYTE_RESULT_NO_BYTES_CONSUMED BIONIC_MULTIBYTE_RESULT_NO_BYTES_CONSUMED
+};
+
+__END_DECLS
diff --git a/libc/include/uchar.h b/libc/include/uchar.h
index 0c7424d..626372a 100644
--- a/libc/include/uchar.h
+++ b/libc/include/uchar.h
@@ -35,6 +35,8 @@
 
 #include <stddef.h>
 #include <sys/cdefs.h>
+
+#include <bits/bionic_multibyte_result.h>
 #include <bits/mbstate_t.h>
 
 __BEGIN_DECLS
diff --git a/libc/include/wchar.h b/libc/include/wchar.h
index 2671580..c4e9679 100644
--- a/libc/include/wchar.h
+++ b/libc/include/wchar.h
@@ -37,6 +37,7 @@
 #include <time.h>
 #include <xlocale.h>
 
+#include <bits/bionic_multibyte_result.h>
 #include <bits/mbstate_t.h>
 #include <bits/wchar_limits.h>
 #include <bits/wctype.h>
diff --git a/libc/private/bionic_mbstate.h b/libc/private/bionic_mbstate.h
index 243b220..29f5aa6 100644
--- a/libc/private/bionic_mbstate.h
+++ b/libc/private/bionic_mbstate.h
@@ -34,15 +34,9 @@
 
 __BEGIN_DECLS
 
-/*
- * These return values are specified by POSIX for multibyte conversion
- * functions.
- */
-#define __MB_ERR_ILLEGAL_SEQUENCE static_cast<size_t>(-1)
-#define __MB_ERR_INCOMPLETE_SEQUENCE static_cast<size_t>(-2)
-
-#define __MB_IS_ERR(rv) (rv == __MB_ERR_ILLEGAL_SEQUENCE || \
-                         rv == __MB_ERR_INCOMPLETE_SEQUENCE)
+#define __MB_IS_ERR(rv)                              \
+  (rv == BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE || \
+   rv == BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE)
 
 static inline __wur bool mbstate_is_initial(const mbstate_t* ps) {
   return *(reinterpret_cast<const uint32_t*>(ps->__seq)) == 0;
@@ -66,10 +60,10 @@
 static inline __wur size_t mbstate_reset_and_return_illegal(int _errno, mbstate_t* ps) {
   errno = _errno;
   *(reinterpret_cast<uint32_t*>(ps->__seq)) = 0;
-  return __MB_ERR_ILLEGAL_SEQUENCE;
+  return BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE;
 }
 
-static inline __wur size_t mbstate_reset_and_return(int _return, mbstate_t* ps) {
+static inline __wur size_t mbstate_reset_and_return(size_t _return, mbstate_t* ps) {
   *(reinterpret_cast<uint32_t*>(ps->__seq)) = 0;
   return _return;
 }
diff --git a/libc/stdio/vfscanf.cpp b/libc/stdio/vfscanf.cpp
index 3133f1f..3607995 100644
--- a/libc/stdio/vfscanf.cpp
+++ b/libc/stdio/vfscanf.cpp
@@ -327,12 +327,12 @@
             fp->_r--;
             memset(&mbs, 0, sizeof(mbs));
             nconv = mbrtowc(wcp, buf, bytes, &mbs);
-            if (nconv == __MB_ERR_ILLEGAL_SEQUENCE) {
+            if (nconv == BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE) {
               fp->_flags |= __SERR;
               goto input_failure;
             }
             if (nconv == 0 && !(flags & SUPPRESS)) *wcp = L'\0';
-            if (nconv != __MB_ERR_INCOMPLETE_SEQUENCE) {
+            if (nconv != BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE) {
               nread += bytes;
               width--;
               if (!(flags & SUPPRESS)) wcp++;
@@ -417,11 +417,11 @@
             wchar_t wc = L'\0';
             memset(&mbs, 0, sizeof(mbs));
             nconv = mbrtowc(&wc, buf, bytes, &mbs);
-            if (nconv == __MB_ERR_ILLEGAL_SEQUENCE) {
+            if (nconv == BIONIC_MULTIBYTE_RESULT_ILLEGAL_SEQUENCE) {
               fp->_flags |= __SERR;
               goto input_failure;
             }
-            if (nconv != __MB_ERR_INCOMPLETE_SEQUENCE) {
+            if (nconv != BIONIC_MULTIBYTE_RESULT_INCOMPLETE_SEQUENCE) {
               if ((c == CT_CCL && wctob(wc) != EOF && !ccltab[wctob(wc)]) || (c == CT_STRING && iswspace(wc))) {
                 while (bytes != 0) {
                   bytes--;
diff --git a/libm/NOTICE b/libm/NOTICE
index 5e2f8ca..31337e7 100644
--- a/libm/NOTICE
+++ b/libm/NOTICE
@@ -1176,6 +1176,32 @@
 
 -------------------------------------------------------------------
 
+Copyright (c) 2017, 2023 Steven G. Kargl
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice unmodified, this list of conditions, and the following
+   disclaimer.
+2. 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 AUTHOR ``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 AUTHOR 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.
+
+-------------------------------------------------------------------
+
 From: @(#)s_ilogb.c 5.1 93/09/24
 ====================================================
 Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 2dbc680..776643d 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -1384,7 +1384,7 @@
     threads[i]->join();
   }
   ASSERT_EQ(1U, num_successful) << "Only one thread should be able to set the limit.";
-  exit(0);
+  _exit(0);
 }
 #endif
 
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index 8716810..04932ba 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -29,6 +29,62 @@
 
 #define NUM_WCHARS(num_bytes) ((num_bytes)/sizeof(wchar_t))
 
+#ifdef __GLIBC__
+// glibc immediately dereferences the locale passed to all wcsto*_l functions,
+// even if it won't be used, and even if it's LC_GLOBAL_LOCALE, which isn't a
+// pointer to valid memory.
+static locale_t SAFE_LC_GLOBAL_LOCALE = duplocale(LC_GLOBAL_LOCALE);
+#else
+static locale_t SAFE_LC_GLOBAL_LOCALE = LC_GLOBAL_LOCALE;
+#endif
+
+// Modern versions of UTF-8 (https://datatracker.ietf.org/doc/html/rfc3629 and
+// newer) explicitly disallow code points beyond U+10FFFF, which exclude all 5-
+// and 6-byte sequences. Earlier versions of UTF-8 allowed the wider range:
+// https://datatracker.ietf.org/doc/html/rfc2279.
+//
+// Bionic's unicode implementation was written after the high values were
+// excluded, so it has never supported them. Other implementations (at least
+// as of glibc 2.36), do support those sequences.
+#if defined(__ANDROID__) || defined(ANDROID_HOST_MUSL)
+constexpr bool kLibcRejectsOverLongUtf8Sequences = true;
+#elif defined(__GLIBC__)
+constexpr bool kLibcRejectsOverLongUtf8Sequences = false;
+#else
+#error kLibcRejectsOverLongUtf8Sequences must be configured for this platform
+#endif
+
+// C23 7.31.6.3.2 (mbrtowc) says:
+//
+// Returns:
+//
+//     0 if the next n or fewer bytes complete the multibyte character that
+//     corresponds to the null wide character (which is the value stored).
+//
+//     (size_t)(-2) if the next n bytes contribute to an incomplete (but
+//     potentially valid) multibyte character, and all n bytes have been
+//     processed (no value is stored).
+//
+// Bionic historically interpreted the behavior when n is 0 to be the next 0
+// bytes decoding to the null. That's a pretty bad interpretation, and both
+// glibc and musl return -2 for that case.
+//
+// The tests currently checks the incorrect behavior for bionic because gtest
+// doesn't support explicit xfail annotations. The behavior difference here
+// should be fixed, but danalbert wants to add more tests before tackling the
+// bugs.
+#ifdef __ANDROID__
+constexpr size_t kExpectedResultForZeroLength = 0U;
+#else
+constexpr size_t kExpectedResultForZeroLength = static_cast<size_t>(-2);
+#endif
+
+#if defined(__GLIBC__)
+constexpr bool kLibcSupportsParsingBinaryLiterals = __GLIBC_PREREQ(2, 38);
+#else
+constexpr bool kLibcSupportsParsingBinaryLiterals = true;
+#endif
+
 TEST(wchar, sizeof_wchar_t) {
   EXPECT_EQ(4U, sizeof(wchar_t));
   EXPECT_EQ(4U, sizeof(wint_t));
@@ -36,7 +92,7 @@
 
 TEST(wchar, mbrlen) {
   char bytes[] = { 'h', 'e', 'l', 'l', 'o', '\0' };
-  EXPECT_EQ(0U, mbrlen(&bytes[0], 0, nullptr));
+  EXPECT_EQ(kExpectedResultForZeroLength, mbrlen(&bytes[0], 0, nullptr));
   EXPECT_EQ(1U, mbrlen(&bytes[0], 1, nullptr));
 
   EXPECT_EQ(1U, mbrlen(&bytes[4], 1, nullptr));
@@ -95,6 +151,9 @@
 }
 
 TEST(wchar, wcrtomb_start_state) {
+  ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
+  uselocale(LC_GLOBAL_LOCALE);
+
   char out[MB_LEN_MAX];
   mbstate_t ps;
 
@@ -118,6 +177,9 @@
 }
 
 TEST(wchar, wcstombs_wcrtombs) {
+  ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
+  uselocale(LC_GLOBAL_LOCALE);
+
   const wchar_t chars[] = { L'h', L'e', L'l', L'l', L'o', 0 };
   const wchar_t bad_chars[] = { L'h', L'i', static_cast<wchar_t>(0xffffffff), 0 };
   const wchar_t* src;
@@ -255,63 +317,98 @@
 TEST(wchar, mbtowc) {
   wchar_t out[8];
 
+  // bionic has the same misunderstanding of the result for a zero-length
+  // conversion for mbtowc as it does for all the other multibyte conversion
+  // functions but mbtowc returns different values than all the others:
+  //
+  // C23 7.24.7.2.4:
+  //
+  // If s is a null pointer, the mbtowc function returns a nonzero or zero
+  // value, if multibyte character encodings, respectively, do or do not have
+  // state-dependent encodings. If s is not a null pointer, the mbtowc function
+  // either returns 0 (if s points to the null character), or returns the number
+  // of bytes that are contained in the converted multibyte character (if the
+  // next n or fewer bytes form a valid multibyte character), or returns -1 (if
+  // they do not form a valid multibyte character).
+
+#ifdef __BIONIC__
+  int expected_result_for_zero_length = 0;
+#else
+  int expected_result_for_zero_length = -1;
+#endif
+
   out[0] = 'x';
-  ASSERT_EQ(0, mbtowc(out, "hello", 0));
-  ASSERT_EQ('x', out[0]);
+  EXPECT_EQ(expected_result_for_zero_length, mbtowc(out, "hello", 0));
+  EXPECT_EQ('x', out[0]);
 
-  ASSERT_EQ(0, mbtowc(out, "hello", 0));
-  ASSERT_EQ(0, mbtowc(out, "", 0));
-  ASSERT_EQ(1, mbtowc(out, "hello", 1));
-  ASSERT_EQ(L'h', out[0]);
+  EXPECT_EQ(expected_result_for_zero_length, mbtowc(out, "hello", 0));
+  EXPECT_EQ(0, mbtowc(out, "", 0));
+  EXPECT_EQ(1, mbtowc(out, "hello", 1));
+  EXPECT_EQ(L'h', out[0]);
 
-  ASSERT_EQ(0, mbtowc(nullptr, "hello", 0));
-  ASSERT_EQ(0, mbtowc(nullptr, "", 0));
-  ASSERT_EQ(1, mbtowc(nullptr, "hello", 1));
+  EXPECT_EQ(expected_result_for_zero_length, mbtowc(nullptr, "hello", 0));
+  EXPECT_EQ(0, mbtowc(nullptr, "", 0));
+  EXPECT_EQ(1, mbtowc(nullptr, "hello", 1));
 
-  ASSERT_EQ(0, mbtowc(nullptr, nullptr, 0));
+  EXPECT_EQ(0, mbtowc(nullptr, nullptr, 0));
 }
 
 TEST(wchar, mbrtowc) {
   wchar_t out[8];
 
   out[0] = 'x';
-  ASSERT_EQ(0U, mbrtowc(out, "hello", 0, nullptr));
-  ASSERT_EQ('x', out[0]);
+  EXPECT_EQ(kExpectedResultForZeroLength, mbrtowc(out, "hello", 0, nullptr));
+  EXPECT_EQ('x', out[0]);
 
-  ASSERT_EQ(0U, mbrtowc(out, "hello", 0, nullptr));
-  ASSERT_EQ(0U, mbrtowc(out, "", 0, nullptr));
-  ASSERT_EQ(1U, mbrtowc(out, "hello", 1, nullptr));
-  ASSERT_EQ(L'h', out[0]);
+  EXPECT_EQ(kExpectedResultForZeroLength, mbrtowc(out, "hello", 0, nullptr));
+  EXPECT_EQ(kExpectedResultForZeroLength, mbrtowc(out, "", 0, nullptr));
+  EXPECT_EQ(1U, mbrtowc(out, "hello", 1, nullptr));
+  EXPECT_EQ(L'h', out[0]);
 
-  ASSERT_EQ(0U, mbrtowc(nullptr, "hello", 0, nullptr));
-  ASSERT_EQ(0U, mbrtowc(nullptr, "", 0, nullptr));
-  ASSERT_EQ(1U, mbrtowc(nullptr, "hello", 1, nullptr));
+  EXPECT_EQ(kExpectedResultForZeroLength, mbrtowc(nullptr, "hello", 0, nullptr));
+  EXPECT_EQ(kExpectedResultForZeroLength, mbrtowc(nullptr, "", 0, nullptr));
+  EXPECT_EQ(1U, mbrtowc(nullptr, "hello", 1, nullptr));
 
-  ASSERT_EQ(0U, mbrtowc(nullptr, nullptr, 0, nullptr));
+  EXPECT_EQ(0U, mbrtowc(nullptr, nullptr, 0, nullptr));
 
-  ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
+  EXPECT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
   uselocale(LC_GLOBAL_LOCALE);
 
   // 1-byte UTF-8.
-  ASSERT_EQ(1U, mbrtowc(out, "abcdef", 6, nullptr));
-  ASSERT_EQ(L'a', out[0]);
+  EXPECT_EQ(1U, mbrtowc(out, "abcdef", 6, nullptr));
+  EXPECT_EQ(L'a', out[0]);
   // 2-byte UTF-8.
-  ASSERT_EQ(2U, mbrtowc(out, "\xc2\xa2" "cdef", 6, nullptr));
-  ASSERT_EQ(static_cast<wchar_t>(0x00a2), out[0]);
+  EXPECT_EQ(2U, mbrtowc(out,
+                        "\xc2\xa2"
+                        "cdef",
+                        6, nullptr));
+  EXPECT_EQ(static_cast<wchar_t>(0x00a2), out[0]);
   // 3-byte UTF-8.
-  ASSERT_EQ(3U, mbrtowc(out, "\xe2\x82\xac" "def", 6, nullptr));
-  ASSERT_EQ(static_cast<wchar_t>(0x20ac), out[0]);
+  EXPECT_EQ(3U, mbrtowc(out,
+                        "\xe2\x82\xac"
+                        "def",
+                        6, nullptr));
+  EXPECT_EQ(static_cast<wchar_t>(0x20ac), out[0]);
   // 4-byte UTF-8.
-  ASSERT_EQ(4U, mbrtowc(out, "\xf0\xa4\xad\xa2" "ef", 6, nullptr));
-  ASSERT_EQ(static_cast<wchar_t>(0x24b62), out[0]);
+  EXPECT_EQ(4U, mbrtowc(out,
+                        "\xf0\xa4\xad\xa2"
+                        "ef",
+                        6, nullptr));
+  EXPECT_EQ(static_cast<wchar_t>(0x24b62), out[0]);
 #if defined(__BIONIC__) // glibc allows this.
   // Illegal 5-byte UTF-8.
-  ASSERT_EQ(static_cast<size_t>(-1), mbrtowc(out, "\xf8\xa1\xa2\xa3\xa4" "f", 6, nullptr));
-  ASSERT_EQ(EILSEQ, errno);
+  EXPECT_EQ(static_cast<size_t>(-1), mbrtowc(out,
+                                             "\xf8\xa1\xa2\xa3\xa4"
+                                             "f",
+                                             6, nullptr));
+  EXPECT_EQ(EILSEQ, errno);
 #endif
   // Illegal over-long sequence.
-  ASSERT_EQ(static_cast<size_t>(-1), mbrtowc(out, "\xf0\x82\x82\xac" "ef", 6, nullptr));
-  ASSERT_EQ(EILSEQ, errno);
+  EXPECT_EQ(static_cast<size_t>(-1), mbrtowc(out,
+                                             "\xf0\x82\x82\xac"
+                                             "ef",
+                                             6, nullptr));
+  EXPECT_EQ(EILSEQ, errno);
 }
 
 TEST(wchar, mbrtowc_valid_non_characters) {
@@ -332,8 +429,14 @@
 
   wchar_t out[8] = {};
   errno = 0;
-  ASSERT_EQ(static_cast<size_t>(-1), mbrtowc(out, "\xf5\x80\x80\x80", 4, nullptr));
-  ASSERT_EQ(EILSEQ, errno);
+  auto result = mbrtowc(out, "\xf5\x80\x80\x80", 4, nullptr);
+  if (kLibcRejectsOverLongUtf8Sequences) {
+    ASSERT_EQ(static_cast<size_t>(-1), result);
+    ASSERT_EQ(EILSEQ, errno);
+  } else {
+    ASSERT_EQ(4U, result);
+    ASSERT_EQ(0, errno);
+  }
 }
 
 static void test_mbrtowc_incomplete(mbstate_t* ps) {
@@ -446,8 +549,8 @@
 void TestSingleWcsToInt(WcsToIntFn<T> fn, const wchar_t* str, int base,
                         T expected_value, ptrdiff_t expected_len) {
   wchar_t* p;
-  ASSERT_EQ(expected_value, fn(str, &p, base));
-  ASSERT_EQ(expected_len, p - str) << str;
+  EXPECT_EQ(expected_value, fn(str, &p, base)) << str << " " << base;
+  EXPECT_EQ(expected_len, p - str) << str << " " << base;
 }
 
 template <typename T>
@@ -460,7 +563,9 @@
   TestSingleWcsToInt(fn, L"   123 45", 0, static_cast<T>(123), 6);
   TestSingleWcsToInt(fn, L"  -123", 0, static_cast<T>(-123), 6);
   TestSingleWcsToInt(fn, L"0x10000", 0, static_cast<T>(65536), 7);
-  TestSingleWcsToInt(fn, L"0b1011", 0, static_cast<T>(0b1011), 6);
+  if (kLibcSupportsParsingBinaryLiterals) {
+    TestSingleWcsToInt(fn, L"0b1011", 0, static_cast<T>(0b1011), 6);
+  }
 }
 
 template <typename T>
@@ -584,7 +689,7 @@
 
   EXPECT_EQ(24U, wcsftime(buf, sizeof(buf), L"%c", &t));
   EXPECT_STREQ(L"Sun Mar 10 00:00:00 2100", buf);
-  EXPECT_EQ(24U, wcsftime_l(buf, sizeof(buf), L"%c", &t, LC_GLOBAL_LOCALE));
+  EXPECT_EQ(24U, wcsftime_l(buf, sizeof(buf), L"%c", &t, SAFE_LC_GLOBAL_LOCALE));
   EXPECT_STREQ(L"Sun Mar 10 00:00:00 2100", buf);
 }
 
@@ -781,25 +886,25 @@
 
 TEST(wchar, wcstoll_l_EINVAL) {
   errno = 0;
-  wcstoll_l(L"123", nullptr, -1, LC_GLOBAL_LOCALE);
+  wcstoll_l(L"123", nullptr, -1, SAFE_LC_GLOBAL_LOCALE);
   ASSERT_EQ(EINVAL, errno);
   errno = 0;
-  wcstoll_l(L"123", nullptr, 1, LC_GLOBAL_LOCALE);
+  wcstoll_l(L"123", nullptr, 1, SAFE_LC_GLOBAL_LOCALE);
   ASSERT_EQ(EINVAL, errno);
   errno = 0;
-  wcstoll_l(L"123", nullptr, 37, LC_GLOBAL_LOCALE);
+  wcstoll_l(L"123", nullptr, 37, SAFE_LC_GLOBAL_LOCALE);
   ASSERT_EQ(EINVAL, errno);
 }
 
 TEST(wchar, wcstoull_l_EINVAL) {
   errno = 0;
-  wcstoull_l(L"123", nullptr, -1, LC_GLOBAL_LOCALE);
+  wcstoull_l(L"123", nullptr, -1, SAFE_LC_GLOBAL_LOCALE);
   ASSERT_EQ(EINVAL, errno);
   errno = 0;
-  wcstoull_l(L"123", nullptr, 1, LC_GLOBAL_LOCALE);
+  wcstoull_l(L"123", nullptr, 1, SAFE_LC_GLOBAL_LOCALE);
   ASSERT_EQ(EINVAL, errno);
   errno = 0;
-  wcstoull_l(L"123", nullptr, 37, LC_GLOBAL_LOCALE);
+  wcstoull_l(L"123", nullptr, 37, SAFE_LC_GLOBAL_LOCALE);
   ASSERT_EQ(EINVAL, errno);
 }
 
@@ -922,7 +1027,7 @@
 
 TEST(wchar, wcstod_l) {
 #if !defined(ANDROID_HOST_MUSL)
-  EXPECT_EQ(1.23, wcstod_l(L"1.23", nullptr, LC_GLOBAL_LOCALE));
+  EXPECT_EQ(1.23, wcstod_l(L"1.23", nullptr, SAFE_LC_GLOBAL_LOCALE));
 #else
   GTEST_SKIP() << "musl doesn't have wcstod_l";
 #endif
@@ -930,7 +1035,7 @@
 
 TEST(wchar, wcstof_l) {
 #if !defined(ANDROID_HOST_MUSL)
-  EXPECT_EQ(1.23f, wcstof_l(L"1.23", nullptr, LC_GLOBAL_LOCALE));
+  EXPECT_EQ(1.23f, wcstof_l(L"1.23", nullptr, SAFE_LC_GLOBAL_LOCALE));
 #else
   GTEST_SKIP() << "musl doesn't have wcstof_l";
 #endif
@@ -938,30 +1043,30 @@
 
 TEST(wchar, wcstol_l) {
 #if !defined(ANDROID_HOST_MUSL)
-  EXPECT_EQ(123L, wcstol_l(L"123", nullptr, 10, LC_GLOBAL_LOCALE));
+  EXPECT_EQ(123L, wcstol_l(L"123", nullptr, 10, SAFE_LC_GLOBAL_LOCALE));
 #else
   GTEST_SKIP() << "musl doesn't have wcstol_l";
 #endif
 }
 
 TEST(wchar, wcstold_l) {
-  EXPECT_EQ(1.23L, wcstold_l(L"1.23", nullptr, LC_GLOBAL_LOCALE));
+  EXPECT_EQ(1.23L, wcstold_l(L"1.23", nullptr, SAFE_LC_GLOBAL_LOCALE));
 }
 
 TEST(wchar, wcstoll_l) {
-  EXPECT_EQ(123LL, wcstoll_l(L"123", nullptr, 10, LC_GLOBAL_LOCALE));
+  EXPECT_EQ(123LL, wcstoll_l(L"123", nullptr, 10, SAFE_LC_GLOBAL_LOCALE));
 }
 
 TEST(wchar, wcstoul_l) {
 #if !defined(ANDROID_HOST_MUSL)
-  EXPECT_EQ(123UL, wcstoul_l(L"123", nullptr, 10, LC_GLOBAL_LOCALE));
+  EXPECT_EQ(123UL, wcstoul_l(L"123", nullptr, 10, SAFE_LC_GLOBAL_LOCALE));
 #else
   GTEST_SKIP() << "musl doesn't have wcstoul_l";
 #endif
 }
 
 TEST(wchar, wcstoull_l) {
-  EXPECT_EQ(123ULL, wcstoull_l(L"123", nullptr, 10, LC_GLOBAL_LOCALE));
+  EXPECT_EQ(123ULL, wcstoull_l(L"123", nullptr, 10, SAFE_LC_GLOBAL_LOCALE));
 }
 
 static void AssertWcwidthRange(wchar_t begin, wchar_t end, int expected) {