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 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <AK/Function.h>
29#include <AK/String.h>
30#include <Kernel/Syscall.h>
31#include <LibBareMetal/IO.h>
32#include <LibCore/ArgsParser.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <sys/mman.h>
36#include <sys/wait.h>
37
38#pragma GCC optimize("O0")
39
40class Crash {
41public:
42 enum class RunType {
43 UsingChildProcess,
44 UsingCurrentProcess,
45 };
46
47 enum class Failure {
48 DidNotCrash,
49 UnexpectedError,
50 };
51
52 Crash(String test_type, Function<Crash::Failure()> crash_function)
53 : m_type(test_type)
54 , m_crash_function(move(crash_function))
55 {
56 }
57
58 void run(RunType run_type)
59 {
60 printf("\x1B[33mTesting\x1B[0m: \"%s\"\n", m_type.characters());
61
62 auto run_crash_and_print_if_error = [this]() {
63 auto failure = m_crash_function();
64
65 // If we got here something went wrong
66 printf("\x1B[31mFAIL\x1B[0m: ");
67 switch (failure) {
68 case Failure::DidNotCrash:
69 printf("Did not crash!\n");
70 break;
71 case Failure::UnexpectedError:
72 printf("Unexpected error!\n");
73 break;
74 default:
75 ASSERT_NOT_REACHED();
76 }
77 };
78
79 if (run_type == RunType::UsingCurrentProcess) {
80 run_crash_and_print_if_error();
81 } else {
82
83 // Run the test in a child process so that we do not crash the crash program :^)
84 pid_t pid = fork();
85 if (pid < 0) {
86 perror("fork");
87 ASSERT_NOT_REACHED();
88 } else if (pid == 0) {
89 run_crash_and_print_if_error();
90 exit(0);
91 }
92
93 int status;
94 waitpid(pid, &status, 0);
95 if (WIFSIGNALED(status))
96 printf("\x1B[32mPASS\x1B[0m: Terminated with signal %d\n", WTERMSIG(status));
97 }
98 }
99
100private:
101 String m_type;
102 Function<Crash::Failure()> m_crash_function;
103};
104
105int main(int argc, char** argv)
106{
107 bool do_all_crash_types = false;
108 bool do_segmentation_violation = false;
109 bool do_division_by_zero = false;
110 bool do_illegal_instruction = false;
111 bool do_abort = false;
112 bool do_write_to_uninitialized_malloc_memory = false;
113 bool do_write_to_freed_memory = false;
114 bool do_write_to_read_only_memory = false;
115 bool do_read_from_uninitialized_malloc_memory = false;
116 bool do_read_from_freed_memory = false;
117 bool do_invalid_stack_pointer_on_syscall = false;
118 bool do_invalid_stack_pointer_on_page_fault = false;
119 bool do_syscall_from_writeable_memory = false;
120 bool do_write_to_freed_memory_still_cached_by_malloc = false;
121 bool do_read_from_freed_memory_still_cached_by_malloc = false;
122 bool do_execute_non_executable_memory = false;
123 bool do_trigger_user_mode_instruction_prevention = false;
124 bool do_use_io_instruction = false;
125 bool do_read_cpu_counter = false;
126
127 auto args_parser = Core::ArgsParser();
128 args_parser.add_option(do_all_crash_types, "Test that all of the following crash types crash as expected", nullptr, 'A');
129 args_parser.add_option(do_segmentation_violation, "Perform a segmentation violation by dereferencing an invalid pointer", nullptr, 's');
130 args_parser.add_option(do_division_by_zero, "Perform a division by zero", nullptr, 'd');
131 args_parser.add_option(do_illegal_instruction, "Execute an illegal CPU instruction", nullptr, 'i');
132 args_parser.add_option(do_abort, "Call `abort()`", nullptr, 'a');
133 args_parser.add_option(do_read_from_uninitialized_malloc_memory, "Read a pointer from uninitialized malloc memory, then read from it", nullptr, 'm');
134 args_parser.add_option(do_read_from_freed_memory, "Read a pointer from memory freed using `free()`, then read from it", nullptr, 'f');
135 args_parser.add_option(do_write_to_uninitialized_malloc_memory, "Read a pointer from uninitialized malloc memory, then write to it", nullptr, 'M');
136 args_parser.add_option(do_write_to_freed_memory, "Read a pointer from memory freed using `free()`, then write to it", nullptr, 'F');
137 args_parser.add_option(do_write_to_read_only_memory, "Write to read-only memory", nullptr, 'r');
138 args_parser.add_option(do_invalid_stack_pointer_on_syscall, "Make a syscall while using an invalid stack pointer", nullptr, 'T');
139 args_parser.add_option(do_invalid_stack_pointer_on_page_fault, "Trigger a page fault while using an invalid stack pointer", nullptr, 't');
140 args_parser.add_option(do_syscall_from_writeable_memory, "Make a syscall from writeable memory", nullptr, 'S');
141 args_parser.add_option(do_write_to_freed_memory_still_cached_by_malloc, "Read from recently freed memory (tests an opportunistic malloc guard)", nullptr, 'x');
142 args_parser.add_option(do_read_from_freed_memory_still_cached_by_malloc, "Write to recently free memory (tests an opportunistic malloc guard)", nullptr, 'y');
143 args_parser.add_option(do_execute_non_executable_memory, "Attempt to execute non-executable memory (not mapped with PROT_EXEC)", nullptr, 'X');
144 args_parser.add_option(do_trigger_user_mode_instruction_prevention, "Attempt to trigger an x86 User Mode Instruction Prevention fault", nullptr, 'U');
145 args_parser.add_option(do_use_io_instruction, "Use an x86 I/O instruction in userspace", nullptr, 'I');
146 args_parser.add_option(do_read_cpu_counter, "Read the x86 TSC (Time Stamp Counter) directly", nullptr, 'c');
147
148 if (argc != 2) {
149 args_parser.print_usage(stderr, argv[0]);
150 exit(1);
151 }
152
153 args_parser.parse(argc, argv);
154
155 Crash::RunType run_type = do_all_crash_types ? Crash::RunType::UsingChildProcess
156 : Crash::RunType::UsingCurrentProcess;
157
158 if (do_segmentation_violation || do_all_crash_types) {
159 Crash("Segmentation violation", []() {
160 volatile int* crashme = nullptr;
161 *crashme = 0xbeef;
162 return Crash::Failure::DidNotCrash;
163 }).run(run_type);
164 }
165
166 if (do_division_by_zero || do_all_crash_types) {
167 Crash("Division by zero", []() {
168 volatile int lala = 10;
169 volatile int zero = 0;
170 volatile int test = lala / zero;
171 UNUSED_PARAM(test);
172 return Crash::Failure::DidNotCrash;
173 }).run(run_type);
174 }
175
176 if (do_illegal_instruction|| do_all_crash_types) {
177 Crash("Illegal instruction", []() {
178 asm volatile("ud2");
179 return Crash::Failure::DidNotCrash;
180 }).run(run_type);
181 }
182
183 if (do_abort || do_all_crash_types) {
184 Crash("Abort", []() {
185 abort();
186 return Crash::Failure::DidNotCrash;
187 }).run(run_type);
188 }
189
190 if (do_read_from_uninitialized_malloc_memory || do_all_crash_types) {
191 Crash("Read from uninitialized malloc memory", []() {
192 auto* uninitialized_memory = (volatile u32**)malloc(1024);
193 if (!uninitialized_memory)
194 return Crash::Failure::UnexpectedError;
195
196 volatile auto x = uninitialized_memory[0][0];
197 UNUSED_PARAM(x);
198 return Crash::Failure::DidNotCrash;
199 }).run(run_type);
200 }
201
202 if (do_read_from_uninitialized_malloc_memory || do_all_crash_types) {
203 Crash("Read from freed memory", []() {
204 auto* uninitialized_memory = (volatile u32**)malloc(1024);
205 if (!uninitialized_memory)
206 return Crash::Failure::UnexpectedError;
207
208 free(uninitialized_memory);
209 volatile auto x = uninitialized_memory[4][0];
210 UNUSED_PARAM(x);
211 return Crash::Failure::DidNotCrash;
212 }).run(run_type);
213 }
214
215 if (do_write_to_uninitialized_malloc_memory || do_all_crash_types) {
216 Crash("Write to uninitialized malloc memory", []() {
217 auto* uninitialized_memory = (volatile u32**)malloc(1024);
218 if (!uninitialized_memory)
219 return Crash::Failure::UnexpectedError;
220
221 uninitialized_memory[4][0] = 1;
222 return Crash::Failure::DidNotCrash;
223 }).run(run_type);
224 }
225
226 if (do_write_to_freed_memory || do_all_crash_types) {
227 Crash("Write to freed memory", []() {
228 auto* uninitialized_memory = (volatile u32**)malloc(1024);
229 if (!uninitialized_memory)
230 return Crash::Failure::UnexpectedError;
231
232 free(uninitialized_memory);
233 uninitialized_memory[4][0] = 1;
234 return Crash::Failure::DidNotCrash;
235 }).run(run_type);
236 }
237
238 if (do_write_to_read_only_memory || do_all_crash_types) {
239 Crash("Write to read only memory", []() {
240 auto* ptr = (u8*)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
241 if (ptr != MAP_FAILED)
242 return Crash::Failure::UnexpectedError;
243
244 *ptr = 'x'; // This should work fine.
245 int rc = mprotect(ptr, 4096, PROT_READ);
246 if (rc != 0 || *ptr != 'x')
247 return Crash::Failure::UnexpectedError;
248
249 *ptr = 'y'; // This should crash!
250 return Crash::Failure::DidNotCrash;
251 }).run(run_type);
252 }
253
254 if (do_invalid_stack_pointer_on_syscall || do_all_crash_types) {
255 Crash("Invalid stack pointer on syscall", []() {
256 u8* makeshift_stack = (u8*)mmap(nullptr, 0, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_STACK, 0, 0);
257 if (!makeshift_stack)
258 return Crash::Failure::UnexpectedError;
259
260 u8* makeshift_esp = makeshift_stack + 2048;
261 asm volatile("mov %%eax, %%esp" ::"a"(makeshift_esp));
262 getuid();
263 dbgprintf("Survived syscall with MAP_STACK stack\n");
264
265 u8* bad_stack = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
266 if (!bad_stack)
267 return Crash::Failure::UnexpectedError;
268
269 u8* bad_esp = bad_stack + 2048;
270 asm volatile("mov %%eax, %%esp" ::"a"(bad_esp));
271 getuid();
272 return Crash::Failure::DidNotCrash;
273 }).run(run_type);
274 }
275
276 if (do_invalid_stack_pointer_on_page_fault || do_all_crash_types) {
277 Crash("Invalid stack pointer on page fault", []() {
278 u8* bad_stack = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
279 if (!bad_stack)
280 return Crash::Failure::UnexpectedError;
281
282 u8* bad_esp = bad_stack + 2048;
283 asm volatile("mov %%eax, %%esp" ::"a"(bad_esp));
284 asm volatile("pushl $0");
285 return Crash::Failure::DidNotCrash;
286 }).run(run_type);
287 }
288
289 if (do_syscall_from_writeable_memory || do_all_crash_types) {
290 Crash("Syscall from writable memory", []() {
291 u8 buffer[] = { 0xb8, Syscall::SC_getuid, 0, 0, 0, 0xcd, 0x82 };
292 ((void (*)())buffer)();
293 return Crash::Failure::DidNotCrash;
294 }).run(run_type);
295 }
296
297 if (do_read_from_freed_memory_still_cached_by_malloc || do_all_crash_types) {
298 Crash("Read from memory still cached by malloc", []() {
299 auto* ptr = (u8*)malloc(1024);
300 if (!ptr)
301 return Crash::Failure::UnexpectedError;
302
303 free(ptr);
304 dbgprintf("ptr = %p\n", ptr);
305 volatile auto foo = *ptr;
306 UNUSED_PARAM(foo);
307 return Crash::Failure::DidNotCrash;
308 }).run(run_type);
309 }
310
311 if (do_write_to_freed_memory_still_cached_by_malloc || do_all_crash_types) {
312 Crash("Write to freed memory still cached by malloc", []() {
313 auto* ptr = (u8*)malloc(1024);
314 if (!ptr)
315 return Crash::Failure::UnexpectedError;
316 free(ptr);
317 dbgprintf("ptr = %p\n", ptr);
318 *ptr = 'x';
319 return Crash::Failure::DidNotCrash;
320 }).run(run_type);
321 }
322
323 if (do_execute_non_executable_memory || do_all_crash_types) {
324 Crash("Execute non executable memory", []() {
325 auto* ptr = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
326 if (ptr == MAP_FAILED)
327 return Crash::Failure::UnexpectedError;
328
329 ptr[0] = 0xc3; // ret
330 typedef void* (*CrashyFunctionPtr)();
331 ((CrashyFunctionPtr)ptr)();
332 return Crash::Failure::DidNotCrash;
333 }).run(run_type);
334 }
335
336 if (do_trigger_user_mode_instruction_prevention || do_all_crash_types) {
337 Crash("Trigger x86 User Mode Instruction Prevention", []() {
338 asm volatile("str %eax");
339 return Crash::Failure::DidNotCrash;
340 }).run(run_type);
341 }
342
343 if (do_use_io_instruction || do_all_crash_types) {
344 Crash("Attempt to use an I/O instruction", [] {
345 u8 keyboard_status = IO::in8(0x64);
346 printf("Keyboard status: %#02x\n", keyboard_status);
347 return Crash::Failure::DidNotCrash;
348 }).run(run_type);
349 }
350
351 if (do_read_cpu_counter || do_all_crash_types) {
352 Crash("Read the CPU timestamp counter", [] {
353 asm volatile("rdtsc");
354 return Crash::Failure::DidNotCrash;
355 }).run(run_type);
356 }
357
358 return 0;
359}