fix(compat): bound Bionic strerror_r by errno table and gate atoi shim to non-glibc

strerror_r: Bionic's message table covers errno 0..133 except the
unassigned 41 and 58. Numbers outside that set now get "Unknown error N"
without consulting the host, so the text is the same on glibc and musl and
no longer relies on comparing against strerror_r(-1).

atoi: glibc's atoi is (int)strtol() like Bionic's, so the host symbol is
used there as before. The Bionic shim is registered only on libcs where
that is not true (musl). linker_registry_test expects the matching result.
This commit is contained in:
vii committed 2026-10-08 16:10:03 -05:00
1 parent fe2f641a9e
commit 6cb3dd00a9
5 files changed
+41 -16

No files matched your search

@@ -11,6 +11,14 @@
namespace mocktail::compat {
// glibc's atoi is (int)strtol(), exactly like Bionic's, so the host symbol is
// used as-is there. Other libcs (musl) differ on overflow and need the shim.
#ifdef __GLIBC__
inline constexpr bool kHostAtoiMatchesBionic = true;
#else
inline constexpr bool kHostAtoiMatchesBionic = false;
#endif
// Android uses size_t fields here; glibc uses int and musl has no equivalent.
// An all-zero fallback means unknown, not zero usage.
struct BionicMallinfoSnapshot {
+20 -10
View File
@@ -150,6 +150,15 @@ Result ParseInHostCLocale(Parser parser) noexcept {
return result;
}
// Highest errno in Bionic's message table (EHWPOISON). Numbers 41 and 58 are
// unassigned in the generic Linux errno list, so Bionic has no text for them.
constexpr int kBionicMaxErrno = 133;
constexpr bool HasBionicErrnoMessage(int error_number) noexcept {
return error_number >= 0 && error_number <= kBionicMaxErrno &&
error_number != 41 && error_number != 58;
}
template <typename Result>
const char* HostStrErrorResult(Result result, char* buffer) noexcept {
if constexpr (std::is_integral_v<Result>) {
@@ -318,18 +327,19 @@ int BionicStrError(int error_number, char* buffer,
return -1;
}
// Bionic's table ends at EHWPOISON. Unknown numbers get the same text on
// every host instead of whatever the host libc prints (musl has no
// "Unknown error N" form).
std::array<char, 256> host_buffer{};
auto host_result =
::strerror_r(error_number, host_buffer.data(), host_buffer.size());
const char* message = HostStrErrorResult(host_result, host_buffer.data());
const char* message = nullptr;
if (HasBionicErrnoMessage(error_number)) {
auto host_result =
::strerror_r(error_number, host_buffer.data(), host_buffer.size());
message = HostStrErrorResult(host_result, host_buffer.data());
}
std::array<char, 256> unknown_buffer{};
const char* unknown_message = HostStrErrorResult(
::strerror_r(-1, unknown_buffer.data(), unknown_buffer.size()),
unknown_buffer.data());
if (message == nullptr ||
(error_number != 0 && unknown_message != nullptr &&
std::strcmp(message, unknown_message) == 0)) {
std::array<char, 64> unknown_buffer{};
if (message == nullptr) {
std::snprintf(unknown_buffer.data(), unknown_buffer.size(),
"Unknown error %d", error_number);
message = unknown_buffer.data();
+6 -3
View File
@@ -134,7 +134,8 @@ bool IsBionicLibcOnlySymbol(std::string_view symbol) {
}
return symbol == "__cmsg_nxthdr" || symbol == "__cxa_thread_atexit_impl" ||
symbol == "__readlink_chk" || symbol == "__register_atfork" ||
symbol == "arc4random_buf" || symbol == "atoi" || symbol == "geteuid" ||
symbol == "arc4random_buf" || symbol == "geteuid" ||
(!mocktail::compat::kHostAtoiMatchesBionic && symbol == "atoi") ||
symbol == "getuid" || symbol == "lseek64" ||
symbol == "mallinfo" || symbol == "pread64" || symbol == "pwrite64" ||
symbol == "sigaction" || symbol == "strerror_r" ||
@@ -384,8 +385,10 @@ void RegisterBionicHostLibcRuntimeForLibc() {
RegisterSyntheticSymbol("libc.so", "arc4random_buf",
reinterpret_cast<void*>(
mocktail_bionic_arc4random_buf));
RegisterSyntheticSymbol("libc.so", "atoi",
reinterpret_cast<void*>(mocktail_bionic_atoi));
if (!mocktail::compat::kHostAtoiMatchesBionic) {
RegisterSyntheticSymbol("libc.so", "atoi",
reinterpret_cast<void*>(mocktail_bionic_atoi));
}
RegisterSyntheticSymbol("libc.so", "mallinfo",
reinterpret_cast<void*>(mocktail_bionic_mallinfo));
RegisterSyntheticSymbol("libc.so", "sysinfo",
+1 -1
View File
@@ -234,7 +234,7 @@ TEST(BionicHostLibcRuntimeTest, UsesBionicPosixStrErrorContract) {
EXPECT_STREQ(buffer.data(), std::strerror(0));
EXPECT_EQ(errno, EDOM);
for (const int error_number : {-1, 4567, INT_MIN, INT_MAX}) {
for (const int error_number : {-1, 41, 58, 134, 4567, INT_MIN, INT_MAX}) {
errno = EDOM;
ASSERT_EQ(mocktail_bionic_strerror_r(error_number, buffer.data(),
buffer.size()), 0);
+6 -2
View File
@@ -130,8 +130,12 @@ TEST(LinkerRegistryTest, KeepsHostIndependentBionicAbiOwnedByLibc) {
reinterpret_cast<void *>(mocktail___readlink_chk));
EXPECT_EQ(symbols.at("arc4random_buf"),
reinterpret_cast<void *>(mocktail_bionic_arc4random_buf));
EXPECT_EQ(symbols.at("atoi"),
reinterpret_cast<void *>(mocktail_bionic_atoi));
if (mocktail::compat::kHostAtoiMatchesBionic) {
EXPECT_EQ(symbols.count("atoi"), 0u);
} else {
EXPECT_EQ(symbols.at("atoi"),
reinterpret_cast<void *>(mocktail_bionic_atoi));
}
EXPECT_EQ(symbols.at("mallinfo"),
reinterpret_cast<void *>(mocktail_bionic_mallinfo));
EXPECT_EQ(symbols.at("sysinfo"),