platform_bionic/tests/gwp_asan_test.cpp
Christopher Ferris 15c22cdee0 No tombstones under normal bionic unit tests run.
Change a test to use a signal that does not trigger a tombstone.

Also changed the gwp_asan_integration.DISABLED_assert_gwp_asan_enabled
test to inherit from the SilentDeathTest to prevent tombstones
being generated.

Test: Ran tests and no tombstones generated.
Change-Id: I0f104704829dde692aa515a63dea1c6971278c29
2023-07-24 17:51:41 -07:00

270 lines
10 KiB
C++

/*
* Copyright (C) 2021 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 <gtest/gtest.h>
#include <stdio.h>
#include <sys/file.h>
#include <string>
#if defined(__BIONIC__)
#include "android-base/file.h"
#include "android-base/silent_death_test.h"
#include "android-base/test_utils.h"
#include "gwp_asan/options.h"
#include "platform/bionic/malloc.h"
#include "sys/system_properties.h"
#include "utils.h"
using gwp_asan_integration_DeathTest = SilentDeathTest;
// basename is a mess, use gnu basename explicitly to avoid the need for string
// mutation.
extern "C" const char* __gnu_basename(const char* path);
// GWP-ASan tests can run much slower, especially when combined with HWASan.
// Triple the deadline to avoid flakes (b/238585984).
extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
static const char* initial_args[] = {"--deadline_threshold_ms=270000"};
*args = initial_args;
*num_args = 1;
return true;
}
// This file implements "torture testing" under GWP-ASan, where we sample every
// single allocation. The upper limit for the number of GWP-ASan allocations in
// the torture mode is is generally 40,000, so that svelte devices don't
// explode, as this uses ~163MiB RAM (4KiB per live allocation).
TEST(gwp_asan_integration, malloc_tests_under_torture) {
// Do not override HWASan with GWP ASan.
SKIP_WITH_HWASAN;
RunGwpAsanTest("malloc.*:-malloc.mallinfo*");
}
class SyspropRestorer {
private:
std::vector<std::pair<std::string, std::string>> props_to_restore_;
// System properties are global for a device, so the tests that mutate the
// GWP-ASan system properties must be run mutually exclusive. Because
// bionic-unit-tests is run in an isolated gtest fashion (each test is run in
// its own process), we have to use flocks to synchronise between tests.
int flock_fd_;
public:
SyspropRestorer() {
std::string path = testing::internal::GetArgvs()[0];
flock_fd_ = open(path.c_str(), O_RDONLY);
EXPECT_NE(flock_fd_, -1) << "failed to open self for a flock";
EXPECT_NE(flock(flock_fd_, LOCK_EX), -1) << "failed to flock myself";
const char* basename = __gnu_basename(path.c_str());
std::vector<std::string> props = {
std::string("libc.debug.gwp_asan.sample_rate.") + basename,
std::string("libc.debug.gwp_asan.process_sampling.") + basename,
std::string("libc.debug.gwp_asan.max_allocs.") + basename,
"libc.debug.gwp_asan.sample_rate.system_default",
"libc.debug.gwp_asan.sample_rate.app_default",
"libc.debug.gwp_asan.process_sampling.system_default",
"libc.debug.gwp_asan.process_sampling.app_default",
"libc.debug.gwp_asan.max_allocs.system_default",
"libc.debug.gwp_asan.max_allocs.app_default",
};
size_t base_props_size = props.size();
for (size_t i = 0; i < base_props_size; ++i) {
props.push_back("persist." + props[i]);
}
std::string reset_log;
for (const std::string& prop : props) {
std::string value = GetSysprop(prop);
props_to_restore_.emplace_back(prop, value);
if (!value.empty()) {
__system_property_set(prop.c_str(), "");
}
}
}
~SyspropRestorer() {
for (const auto& kv : props_to_restore_) {
if (kv.second != GetSysprop(kv.first)) {
__system_property_set(kv.first.c_str(), kv.second.c_str());
}
}
close(flock_fd_);
}
static std::string GetSysprop(const std::string& name) {
std::string value;
const prop_info* pi = __system_property_find(name.c_str());
if (pi == nullptr) return value;
__system_property_read_callback(
pi,
[](void* cookie, const char* /* name */, const char* value, uint32_t /* serial */) {
std::string* v = static_cast<std::string*>(cookie);
*v = value;
},
&value);
return value;
}
};
TEST_F(gwp_asan_integration_DeathTest, DISABLED_assert_gwp_asan_enabled) {
std::string maps;
EXPECT_TRUE(android::base::ReadFileToString("/proc/self/maps", &maps));
EXPECT_TRUE(maps.find("GWP-ASan") != std::string::npos) << maps;
volatile int* x = new int;
delete x;
EXPECT_DEATH({ *x = 7; }, "");
}
// A weaker version of the above tests, only checking that GWP-ASan is enabled
// for any pointer, not *our* pointer. This allows us to test the system_default
// sysprops without potentially OOM-ing other random processes:
// b/273904016#comment5
TEST(gwp_asan_integration, DISABLED_assert_gwp_asan_enabled_weaker) {
std::string maps;
EXPECT_TRUE(android::base::ReadFileToString("/proc/self/maps", &maps));
EXPECT_TRUE(maps.find("GWP-ASan") != std::string::npos) << maps;
}
TEST(gwp_asan_integration, DISABLED_assert_gwp_asan_disabled) {
std::string maps;
EXPECT_TRUE(android::base::ReadFileToString("/proc/self/maps", &maps));
EXPECT_TRUE(maps.find("GWP-ASan") == std::string::npos);
}
TEST(gwp_asan_integration, sysprops_program_specific) {
// Do not override HWASan with GWP ASan.
SKIP_WITH_HWASAN;
SyspropRestorer restorer;
std::string path = testing::internal::GetArgvs()[0];
const char* basename = __gnu_basename(path.c_str());
__system_property_set((std::string("libc.debug.gwp_asan.sample_rate.") + basename).c_str(), "1");
__system_property_set((std::string("libc.debug.gwp_asan.process_sampling.") + basename).c_str(),
"1");
__system_property_set((std::string("libc.debug.gwp_asan.max_allocs.") + basename).c_str(),
"40000");
RunSubtestNoEnv("gwp_asan_integration_DeathTest.DISABLED_assert_gwp_asan_enabled");
}
TEST(gwp_asan_integration, sysprops_persist_program_specific) {
// Do not override HWASan with GWP ASan.
SKIP_WITH_HWASAN;
SyspropRestorer restorer;
std::string path = testing::internal::GetArgvs()[0];
const char* basename = __gnu_basename(path.c_str());
__system_property_set(
(std::string("persist.libc.debug.gwp_asan.sample_rate.") + basename).c_str(), "1");
__system_property_set(
(std::string("persist.libc.debug.gwp_asan.process_sampling.") + basename).c_str(), "1");
__system_property_set((std::string("persist.libc.debug.gwp_asan.max_allocs.") + basename).c_str(),
"40000");
RunSubtestNoEnv("gwp_asan_integration_DeathTest.DISABLED_assert_gwp_asan_enabled");
}
TEST(gwp_asan_integration, sysprops_non_persist_overrides_persist) {
// Do not override HWASan with GWP ASan.
SKIP_WITH_HWASAN;
SyspropRestorer restorer;
__system_property_set("libc.debug.gwp_asan.sample_rate.system_default", "1");
__system_property_set("libc.debug.gwp_asan.process_sampling.system_default", "1");
// Note, any processes launched elsewhere on the system right now will have
// GWP-ASan enabled. Make sure that we only use a single slot, otherwise we
// could end up causing said badly-timed processes to use up to 163MiB extra
// penalty that 40,000 allocs would cause. See b/273904016#comment5 for more
// context.
__system_property_set("libc.debug.gwp_asan.max_allocs.system_default", "1");
__system_property_set("persist.libc.debug.gwp_asan.sample_rate.system_default", "0");
__system_property_set("persist.libc.debug.gwp_asan.process_sampling.system_default", "0");
__system_property_set("persist.libc.debug.gwp_asan.max_allocs.system_default", "0");
RunSubtestNoEnv("gwp_asan_integration.DISABLED_assert_gwp_asan_enabled_weaker");
}
TEST(gwp_asan_integration, sysprops_program_specific_overrides_default) {
// Do not override HWASan with GWP ASan.
SKIP_WITH_HWASAN;
SyspropRestorer restorer;
std::string path = testing::internal::GetArgvs()[0];
const char* basename = __gnu_basename(path.c_str());
__system_property_set(
(std::string("persist.libc.debug.gwp_asan.sample_rate.") + basename).c_str(), "1");
__system_property_set(
(std::string("persist.libc.debug.gwp_asan.process_sampling.") + basename).c_str(), "1");
__system_property_set((std::string("persist.libc.debug.gwp_asan.max_allocs.") + basename).c_str(),
"40000");
__system_property_set("libc.debug.gwp_asan.sample_rate.system_default", "0");
__system_property_set("libc.debug.gwp_asan.process_sampling.system_default", "0");
__system_property_set("libc.debug.gwp_asan.max_allocs.system_default", "0");
RunSubtestNoEnv("gwp_asan_integration_DeathTest.DISABLED_assert_gwp_asan_enabled");
}
TEST(gwp_asan_integration, sysprops_can_disable) {
// Do not override HWASan with GWP ASan.
SKIP_WITH_HWASAN;
SyspropRestorer restorer;
__system_property_set("libc.debug.gwp_asan.sample_rate.system_default", "0");
__system_property_set("libc.debug.gwp_asan.process_sampling.system_default", "0");
__system_property_set("libc.debug.gwp_asan.max_allocs.system_default", "0");
RunSubtestNoEnv("gwp_asan_integration.DISABLED_assert_gwp_asan_disabled");
}
TEST(gwp_asan_integration, env_overrides_sysprop) {
// Do not override HWASan with GWP ASan.
SKIP_WITH_HWASAN;
SyspropRestorer restorer;
__system_property_set("libc.debug.gwp_asan.sample_rate.system_default", "0");
__system_property_set("libc.debug.gwp_asan.process_sampling.system_default", "0");
__system_property_set("libc.debug.gwp_asan.max_allocs.system_default", "0");
RunGwpAsanTest("gwp_asan_integration_DeathTest.DISABLED_assert_gwp_asan_enabled");
}
#endif // defined(__BIONIC__)