diff --git a/include/compat/bionic_host_libc_runtime.h b/include/compat/bionic_host_libc_runtime.h index 15c14d9..be55994 100644 --- a/include/compat/bionic_host_libc_runtime.h +++ b/include/compat/bionic_host_libc_runtime.h @@ -90,6 +90,7 @@ int mocktail_bionic_cxa_thread_atexit_impl( mocktail::compat::BionicThreadDestructor destructor, void* argument, void* dso_handle); void mocktail_bionic_arc4random_buf(void* buffer, size_t size); +int mocktail_bionic_atoi(const char* text); mocktail::compat::BionicMallinfoSnapshot mocktail_bionic_mallinfo(); int mocktail_bionic_sysinfo(struct sysinfo* info); int mocktail_bionic_uname(struct utsname* name); diff --git a/src/compat/bionic_host_libc_runtime.cc b/src/compat/bionic_host_libc_runtime.cc index 324e2e9..d1d6cd6 100644 --- a/src/compat/bionic_host_libc_runtime.cc +++ b/src/compat/bionic_host_libc_runtime.cc @@ -378,6 +378,10 @@ extern "C" void mocktail_bionic_arc4random_buf(void* buffer, size_t size) { mocktail::compat::BionicArc4RandomBuffer(buffer, size); } +extern "C" int mocktail_bionic_atoi(const char* text) { + return static_cast(std::strtol(text, nullptr, 10)); +} + extern "C" mocktail::compat::BionicMallinfoSnapshot mocktail_bionic_mallinfo() { return mocktail::compat::BionicMallinfo(); } diff --git a/src/linker/linker.cc b/src/linker/linker.cc index 24a8df1..e8461cb 100644 --- a/src/linker/linker.cc +++ b/src/linker/linker.cc @@ -134,7 +134,7 @@ 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 == "geteuid" || + symbol == "arc4random_buf" || symbol == "atoi" || symbol == "geteuid" || symbol == "getuid" || symbol == "lseek64" || symbol == "mallinfo" || symbol == "pread64" || symbol == "pwrite64" || symbol == "sigaction" || symbol == "strerror_r" || @@ -384,6 +384,8 @@ void RegisterBionicHostLibcRuntimeForLibc() { RegisterSyntheticSymbol("libc.so", "arc4random_buf", reinterpret_cast( mocktail_bionic_arc4random_buf)); + RegisterSyntheticSymbol("libc.so", "atoi", + reinterpret_cast(mocktail_bionic_atoi)); RegisterSyntheticSymbol("libc.so", "mallinfo", reinterpret_cast(mocktail_bionic_mallinfo)); RegisterSyntheticSymbol("libc.so", "sysinfo", diff --git a/src/update/readiness_canary.cc b/src/update/readiness_canary.cc index ea56bd2..a33c4cb 100644 --- a/src/update/readiness_canary.cc +++ b/src/update/readiness_canary.cc @@ -468,7 +468,9 @@ CanaryResult RunReadinessCanary(const CanaryOptions& options) { } std::filesystem::remove_all(isolated, filesystem_error); if (!result.error.empty()) return result; - result.exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : 128; + result.exit_code = WIFEXITED(status) + ? WEXITSTATUS(status) + : 128 + (WIFSIGNALED(status) ? WTERMSIG(status) : 0); if (result.exit_code != 0) { result.error = "graphics canary exited with status " + diff --git a/tests/bionic_host_libc_runtime_test.cc b/tests/bionic_host_libc_runtime_test.cc index b67e753..2809cb0 100644 --- a/tests/bionic_host_libc_runtime_test.cc +++ b/tests/bionic_host_libc_runtime_test.cc @@ -206,6 +206,21 @@ TEST(BionicHostLibcRuntimeTest, HidesPrivilegedHostIdentityFromAndroidGuest) { mocktail::compat::NormalizeBionicApplicationUid(::geteuid())); } +TEST(BionicHostLibcRuntimeTest, ParsesAtoiWithBionicOverflowBehavior) { + EXPECT_EQ(mocktail_bionic_atoi(""), 0); + EXPECT_EQ(mocktail_bionic_atoi("invalid"), 0); + EXPECT_EQ(mocktail_bionic_atoi(" -42suffix"), -42); + EXPECT_EQ(mocktail_bionic_atoi("2147483647"), INT_MAX); + EXPECT_EQ(mocktail_bionic_atoi("-2147483648"), INT_MIN); + EXPECT_EQ(mocktail_bionic_atoi("2147483648"), INT_MIN); + EXPECT_EQ(mocktail_bionic_atoi("4294967295"), -1); + EXPECT_EQ(mocktail_bionic_atoi("9999999999"), 1410065407); + + errno = 0; + EXPECT_EQ(mocktail_bionic_atoi("9223372036854775808"), -1); + EXPECT_EQ(errno, ERANGE); +} + TEST(BionicHostLibcRuntimeTest, UsesBionicPosixStrErrorContract) { std::array buffer{}; diff --git a/tests/linker_registry_test.cc b/tests/linker_registry_test.cc index 860f75d..75816bd 100644 --- a/tests/linker_registry_test.cc +++ b/tests/linker_registry_test.cc @@ -130,6 +130,8 @@ TEST(LinkerRegistryTest, KeepsHostIndependentBionicAbiOwnedByLibc) { reinterpret_cast(mocktail___readlink_chk)); EXPECT_EQ(symbols.at("arc4random_buf"), reinterpret_cast(mocktail_bionic_arc4random_buf)); + EXPECT_EQ(symbols.at("atoi"), + reinterpret_cast(mocktail_bionic_atoi)); EXPECT_EQ(symbols.at("mallinfo"), reinterpret_cast(mocktail_bionic_mallinfo)); EXPECT_EQ(symbols.at("sysinfo"), diff --git a/tests/native_update_test.cc b/tests/native_update_test.cc index c496a1c..e799eb5 100644 --- a/tests/native_update_test.cc +++ b/tests/native_update_test.cc @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -266,6 +267,24 @@ TEST(ReadinessCanaryTest, PreservesEtc2DriverOverride) { } } +TEST(ReadinessCanaryTest, ReportsTerminatingSignal) { + TemporaryDirectory temporary; + CanaryOptions options; + options.runtime_binary = temporary.root() / "runtime"; + options.cache_root = temporary.root() / "cache"; + options.state_root = temporary.root() / "state"; + options.payload_directory = temporary.root() / "payload"; + options.timeout_seconds = 5; + Write(options.runtime_binary, "#!/bin/sh\nkill -TERM $$\n"); + ASSERT_EQ(chmod(options.runtime_binary.c_str(), 0700), 0); + + const auto result = RunReadinessCanary(options); + EXPECT_FALSE(result); + EXPECT_EQ(result.exit_code, 128 + SIGTERM); + EXPECT_EQ(result.error, "graphics canary exited with status " + + std::to_string(128 + SIGTERM)); +} + TEST(ReadinessCanaryTest, AcceptsRealPresentWithoutShaderPackSummary) { const std::string valid = "[compat] legacy binary patches: disabled\n"