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