blob: e1b83ac6dd85a9e7493f46933795c2fc7d05d939 [file] [log] [blame]
Yavor Goulishev9c08e842020-04-29 14:03:33 -07001// Copyright 2018 The Crashpad Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h"
16
17#include <dbghelp.h>
18#include <zircon/syscalls.h>
19
20#include "base/fuchsia/fuchsia_logging.h"
21#include "base/logging.h"
22#include "base/stl_util.h"
23#include "base/strings/stringprintf.h"
24#include "gtest/gtest.h"
25#include "snapshot/fuchsia/process_snapshot_fuchsia.h"
26#include "test/multiprocess_exec.h"
27#include "util/fuchsia/koid_utilities.h"
28#include "util/fuchsia/scoped_task_suspend.h"
29
30namespace crashpad {
31namespace test {
32namespace {
33
34constexpr struct {
35 uint32_t zircon_perm;
36 size_t pages;
37 uint32_t minidump_perm;
38} kTestMappingPermAndSizes[] = {
39 // Zircon doesn't currently allow write-only, execute-only, or
40 // write-execute-only, returning ZX_ERR_INVALID_ARGS on map.
41 {0, 5, PAGE_NOACCESS},
42 {ZX_VM_PERM_READ, 6, PAGE_READONLY},
43 // {ZX_VM_PERM_WRITE, 7, PAGE_WRITECOPY},
44 // {ZX_VM_PERM_EXECUTE, 8, PAGE_EXECUTE},
45 {ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 9, PAGE_READWRITE},
46 {ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE, 10, PAGE_EXECUTE_READ},
47 // {ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE, 11, PAGE_EXECUTE_WRITECOPY},
48 {ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE,
49 12,
50 PAGE_EXECUTE_READWRITE},
51};
52
53CRASHPAD_CHILD_TEST_MAIN(AddressSpaceChildTestMain) {
54 // Create specifically sized mappings/permissions and write the address in
55 // our address space to the parent so that the reader can check they're read
56 // correctly.
57 for (const auto& t : kTestMappingPermAndSizes) {
58 zx_handle_t vmo = ZX_HANDLE_INVALID;
59 const size_t size = t.pages * PAGE_SIZE;
60 zx_status_t status = zx_vmo_create(size, 0, &vmo);
61 ZX_CHECK(status == ZX_OK, status) << "zx_vmo_create";
62 status = zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo);
63 ZX_CHECK(status == ZX_OK, status) << "zx_vmo_replace_as_executable";
64 uintptr_t mapping_addr = 0;
65 status = zx_vmar_map(
66 zx_vmar_root_self(), t.zircon_perm, 0, vmo, 0, size, &mapping_addr);
67 ZX_CHECK(status == ZX_OK, status) << "zx_vmar_map";
68 CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
69 &mapping_addr,
70 sizeof(mapping_addr));
71 }
72
73 CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
74 return 0;
75}
76
77bool HasSingleMatchingMapping(
78 const std::vector<const MemoryMapRegionSnapshot*>& memory_map,
79 uintptr_t address,
80 size_t size,
81 uint32_t perm) {
82 const MemoryMapRegionSnapshot* matching = nullptr;
83 for (const auto* region : memory_map) {
84 const MINIDUMP_MEMORY_INFO& mmi = region->AsMinidumpMemoryInfo();
85 if (mmi.BaseAddress == address) {
86 if (matching) {
87 LOG(ERROR) << "multiple mappings matching address";
88 return false;
89 }
90 matching = region;
91 }
92 }
93
94 if (!matching)
95 return false;
96
97 const MINIDUMP_MEMORY_INFO& matching_mmi = matching->AsMinidumpMemoryInfo();
98 return matching_mmi.Protect == perm && matching_mmi.RegionSize == size;
99}
100
101class AddressSpaceTest : public MultiprocessExec {
102 public:
103 AddressSpaceTest() : MultiprocessExec() {
104 SetChildTestMainFunction("AddressSpaceChildTestMain");
105 }
106 ~AddressSpaceTest() {}
107
108 private:
109 void MultiprocessParent() override {
110 uintptr_t test_addresses[base::size(kTestMappingPermAndSizes)];
111 for (size_t i = 0; i < base::size(test_addresses); ++i) {
112 ASSERT_TRUE(ReadFileExactly(
113 ReadPipeHandle(), &test_addresses[i], sizeof(test_addresses[i])));
114 }
115
116 ScopedTaskSuspend suspend(*ChildProcess());
117
118 ProcessSnapshotFuchsia process_snapshot;
119 ASSERT_TRUE(process_snapshot.Initialize(*ChildProcess()));
120
121 for (size_t i = 0; i < base::size(test_addresses); ++i) {
122 const auto& t = kTestMappingPermAndSizes[i];
123 EXPECT_TRUE(HasSingleMatchingMapping(process_snapshot.MemoryMap(),
124 test_addresses[i],
125 t.pages * PAGE_SIZE,
126 t.minidump_perm))
127 << base::StringPrintf(
128 "index %zu, zircon_perm 0x%x, minidump_perm 0x%x",
129 i,
130 t.zircon_perm,
131 t.minidump_perm);
132 }
133 }
134
135 DISALLOW_COPY_AND_ASSIGN(AddressSpaceTest);
136};
137
138TEST(ProcessSnapshotFuchsiaTest, AddressSpaceMapping) {
139 AddressSpaceTest test;
140 test.Run();
141}
142
143CRASHPAD_CHILD_TEST_MAIN(StackPointerIntoInvalidLocation) {
144 // Map a large block, output the base address of it, and block. The parent
145 // will artificially set the SP into this large block to confirm that a huge
146 // stack is not accidentally captured.
147 zx_handle_t large_vmo;
148 constexpr uint64_t kSize = 1 << 30u;
149 zx_status_t status = zx_vmo_create(kSize, 0, &large_vmo);
150 ZX_CHECK(status == ZX_OK, status) << "zx_vmo_create";
151 zx_vaddr_t mapped_addr;
152 status = zx_vmar_map(zx_vmar_root_self(),
153 ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
154 0,
155 large_vmo,
156 0,
157 kSize,
158 &mapped_addr);
159 ZX_CHECK(status == ZX_OK, status) << "zx_vmar_map";
160 CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
161 &mapped_addr,
162 sizeof(mapped_addr));
163 zx_nanosleep(ZX_TIME_INFINITE);
164 return 0;
165}
166
167class InvalidStackPointerTest : public MultiprocessExec {
168 public:
169 InvalidStackPointerTest() : MultiprocessExec() {
170 SetChildTestMainFunction("StackPointerIntoInvalidLocation");
171 SetExpectedChildTermination(kTerminationNormal,
172 ZX_TASK_RETCODE_SYSCALL_KILL);
173 }
174 ~InvalidStackPointerTest() {}
175
176 private:
177 void MultiprocessParent() override {
178 uint64_t address_of_large_mapping;
179 ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(),
180 &address_of_large_mapping,
181 sizeof(address_of_large_mapping)));
182
183 ScopedTaskSuspend suspend(*ChildProcess());
184
185 std::vector<zx::thread> threads = GetThreadHandles(*ChildProcess());
186 ASSERT_EQ(threads.size(), 1u);
187
188 zx_thread_state_general_regs_t regs;
189 ASSERT_EQ(threads[0].read_state(
190 ZX_THREAD_STATE_GENERAL_REGS, &regs, sizeof(regs)),
191 ZX_OK);
192
193 constexpr uint64_t kOffsetIntoMapping = 1024;
194#if defined(ARCH_CPU_X86_64)
195 regs.rsp = address_of_large_mapping + kOffsetIntoMapping;
196#elif defined(ARCH_CPU_ARM64)
197 regs.sp = address_of_large_mapping + kOffsetIntoMapping;
198#else
199#error
200#endif
201
202 ASSERT_EQ(threads[0].write_state(
203 ZX_THREAD_STATE_GENERAL_REGS, &regs, sizeof(regs)),
204 ZX_OK);
205
206 ProcessSnapshotFuchsia process_snapshot;
207 ASSERT_TRUE(process_snapshot.Initialize(*ChildProcess()));
208
209 ASSERT_EQ(process_snapshot.Threads().size(), 1u);
210 const MemorySnapshot* stack = process_snapshot.Threads()[0]->Stack();
211 ASSERT_TRUE(stack);
212 // Ensure the stack capture isn't unreasonably large.
213 EXPECT_LT(stack->Size(), 10 * 1048576u);
214
215 // As we've corrupted the child, don't let it run again.
216 ASSERT_EQ(ChildProcess()->kill(), ZX_OK);
217 }
218
219 DISALLOW_COPY_AND_ASSIGN(InvalidStackPointerTest);
220};
221
222// This is a test for a specific failure detailed in
223// https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=41212. A test of stack
224// behavior that was intentionally overflowing the stack, and so when Crashpad
225// received the exception the SP did not point into the actual stack. This
226// caused Crashpad to erronously capture the "stack" from the next mapping in
227// the address space (which could be very large, cause OOM, etc.).
228TEST(ProcessSnapshotFuchsiaTest, InvalidStackPointer) {
229 InvalidStackPointerTest test;
230 test.Run();
231}
232
233} // namespace
234} // namespace test
235} // namespace crashpad