1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <threads.h>
10
11 #include <zircon/syscalls.h>
12
13 // defined in cpp_specific.cpp.
14 int cpp_out_of_mem(void);
15
16 typedef struct {
17 const char* name;
18 int (*func)(volatile unsigned int*);
19 const char* desc;
20 } command_t;
21
blind_write(volatile unsigned int * addr)22 int blind_write(volatile unsigned int* addr) {
23 *addr = 0xBAD1DEA;
24 return 0;
25 }
26
blind_read(volatile unsigned int * addr)27 int blind_read(volatile unsigned int* addr) {
28 return (int)(*addr);
29 }
30
ro_write(volatile unsigned int * addr)31 int ro_write(volatile unsigned int* addr) {
32 // test that we cannot write to RO code memory
33 volatile unsigned int* p = (volatile unsigned int*)&ro_write;
34 *p = 99;
35 return 0;
36 }
37
nx_run(volatile unsigned int * addr)38 int nx_run(volatile unsigned int* addr) {
39 // Test that we cannot execute NX memory. Use stack memory for this
40 // because using a static means the compiler might generate a direct
41 // branch to the symbol rather than computing the function pointer
42 // address in a register as the code looks like it would do, and
43 // declaring a static writable variable that the compiler can see
44 // nobody writes leaves the compiler free to morph it into a static
45 // const variable, which gets put into a mergeable rodata section, and
46 // the Gold linker for aarch64 cannot handle a branch into a mergeable
47 // section.
48 uint8_t codebuf[16] = {};
49 void (*func)(void) = (void*)codebuf;
50 func();
51 return 0;
52 }
53
54 // Note that as of 5/21/16 the crash reads:
55 // PageFault:199: UNIMPLEMENTED: faulting with a page already present.
stack_overflow(volatile unsigned int * i_array)56 int stack_overflow(volatile unsigned int* i_array) {
57 volatile unsigned int array[512];
58 if (i_array) {
59 array[0] = i_array[0] + 1;
60 if (array[0] < 4096)
61 return stack_overflow(array);
62 } else {
63 array[0] = 0;
64 return stack_overflow(array);
65 }
66 return 0;
67 }
68
stack_buf_overrun(volatile unsigned int * arg)69 int stack_buf_overrun(volatile unsigned int* arg) {
70 volatile unsigned int array[6];
71 if (!arg) {
72 return stack_buf_overrun(array);
73 } else {
74 memset((void*)arg, 0, sizeof(array[0]) * 7);
75 }
76 return 0;
77 }
78
undefined(volatile unsigned int * unused)79 int undefined(volatile unsigned int* unused) {
80 #if defined(__x86_64__)
81 __asm__ volatile("ud2");
82 #elif defined(__aarch64__)
83 __asm__ volatile("brk #0"); // not undefined, but close enough
84 #else
85 #error "need to define undefined for this architecture"
86 #endif
87 return 0;
88 }
89
oom(volatile unsigned int * unused)90 int oom(volatile unsigned int* unused) {
91 return cpp_out_of_mem();
92 }
93
94 #include <unistd.h>
95
96 // volatile to ensure compiler doesn't optimize the allocs away
97 volatile char* mem_alloc;
98
mem(volatile unsigned int * arg)99 int mem(volatile unsigned int* arg) {
100 int count = 0;
101 for (;;) {
102 mem_alloc = malloc(1024 * 1024);
103 memset((void*)mem_alloc, 0xa5, 1024 * 1024);
104 count++;
105 if ((count % 128) == 0) {
106 zx_nanosleep(zx_deadline_after(ZX_MSEC(250)));
107 write(1, ".", 1);
108 }
109 }
110 }
111
use_after_free(volatile unsigned int * arg)112 int use_after_free(volatile unsigned int* arg) {
113 char* p = strdup("Hello, world!");
114 free(p);
115 puts(p);
116 return 0;
117 }
118
119 typedef struct {
120 int depth;
121 int max_depth;
122 } deep_sleep_args_t;
123
deep_sleep(deep_sleep_args_t * args)124 int deep_sleep(deep_sleep_args_t* args) {
125 if (args->depth < args->max_depth) {
126 args->depth++;
127 deep_sleep(args);
128 }
129 // The compiler chokes on -Winfinite-recursion otherwise.
130 volatile bool running = true;
131 while (running) {
132 zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
133 }
134 return 0;
135 }
136
thread_func(void * arg)137 int thread_func(void* arg) {
138 return deep_sleep((deep_sleep_args_t*)arg);
139 }
140
blind_write_multithreaded(volatile unsigned int * addr)141 int blind_write_multithreaded(volatile unsigned int* addr) {
142 // Start 5 separate threads that will recurse a bit then sleep.
143 int kThreads = 5;
144 thrd_t threads[kThreads];
145 deep_sleep_args_t args[kThreads];
146 for (int i = 0; i < kThreads; ++i) {
147 args[i].depth = 0;
148 args[i].max_depth = 5;
149 int ret = thrd_create_with_name(&threads[i], thread_func, &args[i], "deep_sleep");
150 if (ret != thrd_success) {
151 printf("Unexpected thread create return: %d\n", ret);
152 return 1;
153 }
154 }
155
156 // Wait for the threads to have finished their recursion then crash the main thread.
157 for (int i = 0; i < kThreads; ++i) {
158 while (args[i].depth < args[i].max_depth) {
159 zx_nanosleep(zx_deadline_after(ZX_MSEC(1)));
160 }
161 }
162 blind_write(addr);
163
164 for (int i = 0; i < kThreads; ++i) {
165 int ret = thrd_join(threads[i], NULL);
166 if (ret != thrd_success) {
167 printf("Unexpected thread join return: %d\n", ret);
168 return 1;
169 }
170 }
171 return 0;
172 }
173
174 command_t commands[] = {
175 {"write0", blind_write, "write to address 0x0"},
176 {"read0", blind_read, "read address 0x0"},
177 {"writero", ro_write, "write to read only code segment"},
178 {"stackov", stack_overflow, "overflow the stack (recursive)"},
179 {"stackbuf", stack_buf_overrun, "overrun a buffer on the stack"},
180 {"und", undefined, "undefined instruction"},
181 {"nx_run", nx_run, "run in no-execute memory"},
182 {"oom", oom, "out of memory c++ death"},
183 {"mem", mem, "out of memory"},
184 {"use_after_free", use_after_free, "use memory after freeing it"},
185 {"write0_mt", blind_write_multithreaded, "write to address 0x0 in one thread, sleeping in 5 others"},
186 {NULL, NULL, NULL},
187 };
188
main(int argc,char ** argv)189 int main(int argc, char** argv) {
190 printf("=@ crasher @=\n");
191
192 if (argc < 2) {
193 printf("default to write0 (use 'help' for more options).\n");
194 blind_write(NULL);
195 } else {
196 if (strcmp("help", argv[1])) {
197 for (command_t* cmd = commands; cmd->name != NULL; ++cmd) {
198 if (strcmp(cmd->name, argv[1]) == 0) {
199 printf("doing : %s\n", cmd->desc);
200 cmd->func(NULL);
201 goto exit; // should not reach here.
202 }
203 }
204 }
205
206 printf("known commands are:\n");
207 for (command_t* cmd = commands; cmd->name != NULL; ++cmd) {
208 printf("%s : %s\n", cmd->name, cmd->desc);
209 }
210 return 0;
211 }
212
213 exit:
214 printf("crasher: exiting normally ?!!\n");
215 return 0;
216 }
217