Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2019-2020, Shannon Booth <shannon.ml.booth@gmail.com>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/Assertions.h>
9#include <AK/DeprecatedString.h>
10#include <AK/Function.h>
11#if ARCH(X86_64)
12# include <Kernel/Arch/x86_64/IO.h>
13#endif
14#include <LibCore/ArgsParser.h>
15#include <LibCore/Object.h>
16#include <LibTest/CrashTest.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <sys/mman.h>
20#include <sys/wait.h>
21#include <syscall.h>
22#include <unistd.h>
23
24using Test::Crash;
25
26#if defined(AK_COMPILER_CLANG)
27# pragma clang optimize off
28#else
29# pragma GCC optimize("O0")
30#endif
31
32int main(int argc, char** argv)
33{
34 Vector<StringView> arguments;
35 arguments.ensure_capacity(argc);
36 for (auto i = 0; i < argc; ++i)
37 arguments.append({ argv[i], strlen(argv[i]) });
38
39 bool do_all_crash_types = false;
40 bool do_segmentation_violation = false;
41 bool do_division_by_zero = false;
42 bool do_illegal_instruction = false;
43 bool do_abort = false;
44 bool do_write_to_uninitialized_malloc_memory = false;
45 bool do_write_to_freed_memory = false;
46 bool do_write_to_read_only_memory = false;
47 bool do_read_from_uninitialized_malloc_memory = false;
48 bool do_read_from_freed_memory = false;
49 bool do_invalid_stack_pointer_on_syscall = false;
50 bool do_invalid_stack_pointer_on_page_fault = false;
51 bool do_syscall_from_writeable_memory = false;
52 bool do_legitimate_syscall = false;
53 bool do_execute_non_executable_memory = false;
54 bool do_trigger_user_mode_instruction_prevention = false;
55#if ARCH(X86_64)
56 bool do_use_io_instruction = false;
57#endif
58 bool do_pledge_violation = false;
59 bool do_failing_assertion = false;
60 bool do_deref_null_refptr = false;
61
62 auto args_parser = Core::ArgsParser();
63 args_parser.set_general_help(
64 "Exercise error-handling paths of the execution environment "
65 "(i.e., Kernel or UE) by crashing in many different ways.");
66 args_parser.add_option(do_all_crash_types, "Test that all (except -U) of the following crash types crash as expected (default behavior)", nullptr, 'A');
67 args_parser.add_option(do_segmentation_violation, "Perform a segmentation violation by dereferencing an invalid pointer", nullptr, 's');
68 args_parser.add_option(do_division_by_zero, "Perform a division by zero", nullptr, 'd');
69 args_parser.add_option(do_illegal_instruction, "Execute an illegal CPU instruction", nullptr, 'i');
70 args_parser.add_option(do_abort, "Call `abort()`", nullptr, 'a');
71 args_parser.add_option(do_read_from_uninitialized_malloc_memory, "Read a pointer from uninitialized malloc memory, then read from it", nullptr, 'm');
72 args_parser.add_option(do_read_from_freed_memory, "Read a pointer from memory freed using `free()`, then read from it", nullptr, 'f');
73 args_parser.add_option(do_write_to_uninitialized_malloc_memory, "Read a pointer from uninitialized malloc memory, then write to it", nullptr, 'M');
74 args_parser.add_option(do_write_to_freed_memory, "Read a pointer from memory freed using `free()`, then write to it", nullptr, 'F');
75 args_parser.add_option(do_write_to_read_only_memory, "Write to read-only memory", nullptr, 'r');
76 args_parser.add_option(do_invalid_stack_pointer_on_syscall, "Make a syscall while using an invalid stack pointer", nullptr, 'T');
77 args_parser.add_option(do_invalid_stack_pointer_on_page_fault, "Trigger a page fault while using an invalid stack pointer", nullptr, 't');
78 args_parser.add_option(do_syscall_from_writeable_memory, "Make a syscall from writeable memory", nullptr, 'S');
79 args_parser.add_option(do_legitimate_syscall, "Make a syscall from legitimate memory (but outside syscall-code mapped region)", nullptr, 'y');
80 args_parser.add_option(do_execute_non_executable_memory, "Attempt to execute non-executable memory (not mapped with PROT_EXEC)", nullptr, 'X');
81 args_parser.add_option(do_trigger_user_mode_instruction_prevention, "Attempt to trigger an x86 User Mode Instruction Prevention fault. WARNING: This test runs only when invoked manually, see #10042.", nullptr, 'U');
82#if ARCH(X86_64)
83 args_parser.add_option(do_use_io_instruction, "Use an x86 I/O instruction in userspace", nullptr, 'I');
84#endif
85 args_parser.add_option(do_pledge_violation, "Violate pledge()'d promises", nullptr, 'p');
86 args_parser.add_option(do_failing_assertion, "Perform a failing assertion", nullptr, 'n');
87 args_parser.add_option(do_deref_null_refptr, "Dereference a null RefPtr", nullptr, 'R');
88
89 if (argc == 1) {
90 do_all_crash_types = true;
91 } else if (argc != 2) {
92 args_parser.print_usage(stderr, arguments[0]);
93 exit(1);
94 }
95
96 args_parser.parse(arguments);
97
98 Crash::RunType run_type = do_all_crash_types ? Crash::RunType::UsingChildProcess
99 : Crash::RunType::UsingCurrentProcess;
100 bool any_failures = false;
101
102 if (do_segmentation_violation || do_all_crash_types) {
103 any_failures |= !Crash("Segmentation violation", []() {
104 volatile int* crashme = nullptr;
105 *crashme = 0xbeef;
106 return Crash::Failure::DidNotCrash;
107 }).run(run_type);
108 }
109
110 if (do_division_by_zero || do_all_crash_types) {
111 any_failures |= !Crash("Division by zero", []() {
112 volatile int lala = 10;
113 volatile int zero = 0;
114 [[maybe_unused]] volatile int test = lala / zero;
115 return Crash::Failure::DidNotCrash;
116 }).run(run_type);
117 }
118
119 if (do_illegal_instruction || do_all_crash_types) {
120 any_failures |= !Crash("Illegal instruction", []() {
121 __builtin_trap();
122 return Crash::Failure::DidNotCrash;
123 }).run(run_type);
124 }
125
126 if (do_abort || do_all_crash_types) {
127 any_failures |= !Crash("Abort", []() {
128 abort();
129 return Crash::Failure::DidNotCrash;
130 }).run(run_type);
131 }
132
133 if (do_read_from_uninitialized_malloc_memory || do_all_crash_types) {
134 any_failures |= !Crash("Read from uninitialized malloc memory", []() {
135 auto* uninitialized_memory = (volatile u32**)malloc(1024);
136 if (!uninitialized_memory)
137 return Crash::Failure::UnexpectedError;
138
139 [[maybe_unused]] volatile auto x = uninitialized_memory[0][0];
140 return Crash::Failure::DidNotCrash;
141 }).run(run_type);
142 }
143
144 if (do_read_from_freed_memory || do_all_crash_types) {
145 any_failures |= !Crash("Read from freed memory", []() {
146 auto* uninitialized_memory = (volatile u32**)malloc(1024);
147 if (!uninitialized_memory)
148 return Crash::Failure::UnexpectedError;
149
150 free(uninitialized_memory);
151#pragma GCC diagnostic push
152#pragma GCC diagnostic ignored "-Wuse-after-free"
153 [[maybe_unused]] volatile auto x = uninitialized_memory[4][0];
154#pragma GCC diagnostic pop
155 return Crash::Failure::DidNotCrash;
156 }).run(run_type);
157 }
158
159 if (do_write_to_uninitialized_malloc_memory || do_all_crash_types) {
160 any_failures |= !Crash("Write to uninitialized malloc memory", []() {
161 auto* uninitialized_memory = (volatile u32**)malloc(1024);
162 if (!uninitialized_memory)
163 return Crash::Failure::UnexpectedError;
164
165 uninitialized_memory[4][0] = 1;
166 return Crash::Failure::DidNotCrash;
167 }).run(run_type);
168 }
169
170 if (do_write_to_freed_memory || do_all_crash_types) {
171 any_failures |= !Crash("Write to freed memory", []() {
172 auto* uninitialized_memory = (volatile u32**)malloc(1024);
173 if (!uninitialized_memory)
174 return Crash::Failure::UnexpectedError;
175
176#pragma GCC diagnostic push
177#pragma GCC diagnostic ignored "-Wuse-after-free"
178 free(uninitialized_memory);
179 uninitialized_memory[4][0] = 1;
180#pragma GCC diagnostic pop
181 return Crash::Failure::DidNotCrash;
182 }).run(run_type);
183 }
184
185 if (do_write_to_read_only_memory || do_all_crash_types) {
186 any_failures |= !Crash("Write to read only memory", []() {
187 auto* ptr = (u8*)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
188 if (ptr == MAP_FAILED)
189 return Crash::Failure::UnexpectedError;
190
191 *ptr = 'x'; // This should work fine.
192 int rc = mprotect(ptr, 4096, PROT_READ);
193 if (rc != 0 || *ptr != 'x')
194 return Crash::Failure::UnexpectedError;
195
196 *ptr = 'y'; // This should crash!
197 return Crash::Failure::DidNotCrash;
198 }).run(run_type);
199 }
200
201 if (do_invalid_stack_pointer_on_syscall || do_all_crash_types) {
202 any_failures |= !Crash("Invalid stack pointer on syscall", []() {
203 u8* makeshift_stack = (u8*)mmap(nullptr, 0, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_STACK, 0, 0);
204 if (!makeshift_stack)
205 return Crash::Failure::UnexpectedError;
206
207 u8* makeshift_esp = makeshift_stack + 2048;
208#if ARCH(X86_64)
209 asm volatile("mov %%eax, %%esp" ::"a"(makeshift_esp));
210#elif ARCH(AARCH64)
211 (void)makeshift_esp;
212 TODO_AARCH64();
213#else
214# error Unknown architecture
215#endif
216 getuid();
217 dbgln("Survived syscall with MAP_STACK stack");
218
219 u8* bad_stack = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
220 if (!bad_stack)
221 return Crash::Failure::UnexpectedError;
222
223 u8* bad_esp = bad_stack + 2048;
224#if ARCH(X86_64)
225 asm volatile("mov %%eax, %%esp" ::"a"(bad_esp));
226#elif ARCH(AARCH64)
227 (void)bad_esp;
228 TODO_AARCH64();
229#else
230# error Unknown architecture
231#endif
232 getuid();
233 return Crash::Failure::DidNotCrash;
234 }).run(run_type);
235 }
236
237 if (do_invalid_stack_pointer_on_page_fault || do_all_crash_types) {
238 any_failures |= !Crash("Invalid stack pointer on page fault", []() {
239 u8* bad_stack = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
240 if (!bad_stack)
241 return Crash::Failure::UnexpectedError;
242
243 u8* bad_esp = bad_stack + 2048;
244#if ARCH(X86_64)
245 asm volatile("movq %%rax, %%rsp" ::"a"(bad_esp));
246 asm volatile("pushq $0");
247#elif ARCH(AARCH64)
248 (void)bad_esp;
249 TODO_AARCH64();
250#else
251# error Unknown architecture
252#endif
253
254 return Crash::Failure::DidNotCrash;
255 }).run(run_type);
256 }
257
258 if (do_syscall_from_writeable_memory || do_all_crash_types) {
259 any_failures |= !Crash("Syscall from writable memory", []() {
260 u8 buffer[] = { 0xb8, Syscall::SC_getuid, 0, 0, 0, 0xcd, 0x82 };
261 ((void (*)())buffer)();
262 return Crash::Failure::DidNotCrash;
263 }).run(run_type);
264 }
265
266 if (do_legitimate_syscall || do_all_crash_types) {
267 any_failures |= !Crash("Regular syscall from outside syscall-code mapped region", []() {
268 // Since 'crash' is dynamically linked, and DynamicLoader only allows LibSystem to make syscalls, this should kill us:
269 Syscall::invoke(Syscall::SC_getuid);
270 return Crash::Failure::DidNotCrash;
271 }).run(run_type);
272 }
273
274 if (do_execute_non_executable_memory || do_all_crash_types) {
275 any_failures |= !Crash("Execute non executable memory", []() {
276 auto* ptr = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
277 if (ptr == MAP_FAILED)
278 return Crash::Failure::UnexpectedError;
279
280 ptr[0] = 0xc3; // ret
281 typedef void* (*CrashyFunctionPtr)();
282 ((CrashyFunctionPtr)ptr)();
283 return Crash::Failure::DidNotCrash;
284 }).run(run_type);
285 }
286
287 if (do_trigger_user_mode_instruction_prevention) {
288 any_failures |= !Crash("Trigger x86 User Mode Instruction Prevention", []() {
289#if ARCH(X86_64)
290 asm volatile("str %eax");
291#elif ARCH(AARCH64)
292 TODO_AARCH64();
293#else
294# error Unknown architecture
295#endif
296 return Crash::Failure::DidNotCrash;
297 }).run(run_type);
298 }
299
300#if ARCH(X86_64)
301 if (do_use_io_instruction || do_all_crash_types) {
302 any_failures |= !Crash("Attempt to use an I/O instruction", [] {
303 u8 keyboard_status = IO::in8(0x64);
304 outln("Keyboard status: {:#02x}", keyboard_status);
305 return Crash::Failure::DidNotCrash;
306 }).run(run_type);
307 }
308#endif
309
310 if (do_pledge_violation || do_all_crash_types) {
311 any_failures |= !Crash("Violate pledge()'d promises", [] {
312 if (pledge("", nullptr) < 0) {
313 perror("pledge");
314 return Crash::Failure::DidNotCrash;
315 }
316 outln("Didn't pledge 'stdio', this should fail!");
317 return Crash::Failure::DidNotCrash;
318 }).run(run_type);
319 }
320
321 if (do_failing_assertion || do_all_crash_types) {
322 any_failures |= !Crash("Perform a failing assertion", [] {
323 VERIFY(1 == 2);
324 return Crash::Failure::DidNotCrash;
325 }).run(run_type);
326 }
327
328 if (do_deref_null_refptr || do_all_crash_types) {
329 any_failures |= !Crash("Dereference a null RefPtr", [] {
330 RefPtr<Core::Object> p;
331 *p;
332 return Crash::Failure::DidNotCrash;
333 }).run(run_type);
334 }
335
336 return any_failures;
337}