mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-07-04 03:18:23 +00:00
Improve stack sanitization unittests.
Rather than relying on the process stack having all the things that should/shouldn't be sanitized, create synthetic stacks to test all of the important cases. BUG=664460 Change-Id: I959266390e94d6fb83ca8ef11ac19fac89e68c31 Reviewed-on: https://chromium-review.googlesource.com/446108 Reviewed-by: Robert Sesek <rsesek@chromium.org>
This commit is contained in:
parent
19af23e3c0
commit
ccf03c13eb
|
@ -57,14 +57,15 @@
|
||||||
|
|
||||||
void *thread_function(void *data) {
|
void *thread_function(void *data) {
|
||||||
int pipefd = *static_cast<int *>(data);
|
int pipefd = *static_cast<int *>(data);
|
||||||
volatile pid_t thread_id = syscall(__NR_gettid);
|
volatile pid_t* thread_id = new pid_t;
|
||||||
|
*thread_id = syscall(__NR_gettid);
|
||||||
// Signal parent that a thread has started.
|
// Signal parent that a thread has started.
|
||||||
uint8_t byte = 1;
|
uint8_t byte = 1;
|
||||||
if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
|
if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
|
||||||
perror("ERROR: parent notification failed");
|
perror("ERROR: parent notification failed");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
|
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = thread_id;
|
||||||
while (true)
|
while (true)
|
||||||
asm volatile ("" : : "r" (thread_id_ptr));
|
asm volatile ("" : : "r" (thread_id_ptr));
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -490,58 +490,83 @@ TEST_F(LinuxPtraceDumperTest, SanitizeStackCopy) {
|
||||||
ThreadInfo thread_info;
|
ThreadInfo thread_info;
|
||||||
EXPECT_TRUE(dumper.GetThreadInfoByIndex(0, &thread_info));
|
EXPECT_TRUE(dumper.GetThreadInfoByIndex(0, &thread_info));
|
||||||
|
|
||||||
const void* stack;
|
const uintptr_t defaced =
|
||||||
size_t stack_len;
|
#if defined(__LP64__)
|
||||||
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, thread_info.stack_pointer));
|
0x0defaced0defaced;
|
||||||
|
#else
|
||||||
|
0x0defaced;
|
||||||
|
#endif
|
||||||
|
|
||||||
uint8_t* stack_copy = new uint8_t[stack_len];
|
uintptr_t simulated_stack[2];
|
||||||
dumper.CopyFromProcess(stack_copy, child_pid, stack, stack_len);
|
|
||||||
|
|
||||||
size_t stack_offset =
|
// Pointers into the stack shouldn't be sanitized.
|
||||||
thread_info.stack_pointer - reinterpret_cast<uintptr_t>(stack);
|
memset(simulated_stack, 0xff, sizeof(simulated_stack));
|
||||||
|
simulated_stack[1] = thread_info.stack_pointer;
|
||||||
|
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||||
|
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||||
|
sizeof(uintptr_t));
|
||||||
|
ASSERT_NE(simulated_stack[1], defaced);
|
||||||
|
|
||||||
const size_t word_count = (stack_len - stack_offset) / sizeof(uintptr_t);
|
// Memory prior to the stack pointer should be cleared.
|
||||||
uintptr_t* stack_words = new uintptr_t[word_count];
|
ASSERT_EQ(simulated_stack[0], 0u);
|
||||||
|
|
||||||
memcpy(stack_words, stack_copy + stack_offset, word_count * sizeof(uintptr_t));
|
// Small integers should not be sanitized.
|
||||||
std::map<uintptr_t, int> pre_sanitization_words;
|
for (int i = -4096; i <= 4096; ++i) {
|
||||||
for (size_t i = 0; i < word_count; ++i)
|
memset(simulated_stack, 0, sizeof(simulated_stack));
|
||||||
++pre_sanitization_words[stack_words[i]];
|
simulated_stack[0] = static_cast<uintptr_t>(i);
|
||||||
|
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||||
dumper.SanitizeStackCopy(stack_copy, stack_len, thread_info.stack_pointer,
|
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||||
stack_offset);
|
0u);
|
||||||
|
ASSERT_NE(simulated_stack[0], defaced);
|
||||||
// Memory below the stack pointer should be zeroed.
|
|
||||||
for (size_t i = 0; i < stack_offset; ++i) {
|
|
||||||
ASSERT_EQ(0, stack_copy[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(stack_words, stack_copy + stack_offset, word_count * sizeof(uintptr_t));
|
// The instruction pointer definitely should point into an executable mapping.
|
||||||
std::map<uintptr_t, int> post_sanitization_words;
|
const MappingInfo* mapping_info = dumper.FindMappingNoBias(
|
||||||
for (size_t i = 0; i < word_count; ++i)
|
reinterpret_cast<uintptr_t>(thread_info.GetInstructionPointer()));
|
||||||
++post_sanitization_words[stack_words[i]];
|
ASSERT_NE(mapping_info, nullptr);
|
||||||
|
ASSERT_TRUE(mapping_info->exec);
|
||||||
|
|
||||||
std::set<uintptr_t> words;
|
// Pointers to code shouldn't be sanitized.
|
||||||
for (auto &word : pre_sanitization_words) words.insert(word.first);
|
memset(simulated_stack, 0, sizeof(simulated_stack));
|
||||||
for (auto &word : post_sanitization_words) words.insert(word.first);
|
simulated_stack[1] = thread_info.GetInstructionPointer();
|
||||||
|
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||||
|
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||||
|
0u);
|
||||||
|
ASSERT_NE(simulated_stack[0], defaced);
|
||||||
|
|
||||||
for (auto word : words) {
|
// String fragments should be sanitized.
|
||||||
if (word == static_cast<uintptr_t>(0X0DEFACED0DEFACEDull)) {
|
memcpy(simulated_stack, "abcdefghijklmnop", sizeof(simulated_stack));
|
||||||
continue;
|
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||||
}
|
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||||
|
0u);
|
||||||
|
ASSERT_EQ(simulated_stack[0], defaced);
|
||||||
|
ASSERT_EQ(simulated_stack[1], defaced);
|
||||||
|
|
||||||
bool should_be_sanitized = true;
|
// Heap pointers should be sanititzed.
|
||||||
if (static_cast<intptr_t>(word) <= 4096 &&
|
#if defined(__ARM_EABI__)
|
||||||
static_cast<intptr_t>(word) >= -4096) should_be_sanitized = false;
|
uintptr_t heap_addr = thread_info.regs.uregs[3];
|
||||||
if (dumper.FindMappingNoBias(word)) should_be_sanitized = false;
|
#elif defined(__aarch64__)
|
||||||
|
uintptr_t heap_addr = thread_info.regs.regs[3];
|
||||||
ASSERT_EQ(should_be_sanitized, post_sanitization_words[word] == 0);
|
#elif defined(__i386)
|
||||||
}
|
uintptr_t heap_addr = thread_info.regs.ecx;
|
||||||
|
#elif defined(__x86_64)
|
||||||
|
uintptr_t heap_addr = thread_info.regs.rcx;
|
||||||
|
#elif defined(__mips__)
|
||||||
|
uintptr_t heap_addr = thread_info.mcontext.gregs[1];
|
||||||
|
#else
|
||||||
|
#error This test has not been ported to this platform.
|
||||||
|
#endif
|
||||||
|
memset(simulated_stack, 0, sizeof(simulated_stack));
|
||||||
|
simulated_stack[0] = heap_addr;
|
||||||
|
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||||
|
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||||
|
0u);
|
||||||
|
ASSERT_EQ(simulated_stack[0], defaced);
|
||||||
|
|
||||||
EXPECT_TRUE(dumper.ThreadsResume());
|
EXPECT_TRUE(dumper.ThreadsResume());
|
||||||
kill(child_pid, SIGKILL);
|
kill(child_pid, SIGKILL);
|
||||||
|
|
||||||
// Reap child
|
// Reap child.
|
||||||
int status;
|
int status;
|
||||||
ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
|
ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
|
||||||
ASSERT_TRUE(WIFSIGNALED(status));
|
ASSERT_TRUE(WIFSIGNALED(status));
|
||||||
|
|
Loading…
Reference in a new issue