| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <threads.h> |
| |
| #include <zircon/syscalls.h> |
| |
| // defined in cpp_specific.cpp. |
| int cpp_out_of_mem(void); |
| |
| typedef struct { |
| const char* name; |
| int (*func)(volatile unsigned int*); |
| const char* desc; |
| } command_t; |
| |
| int blind_write(volatile unsigned int* addr) { |
| *addr = 0xBAD1DEA; |
| return 0; |
| } |
| |
| int blind_read(volatile unsigned int* addr) { |
| return (int)(*addr); |
| } |
| |
| int ro_write(volatile unsigned int* addr) { |
| // test that we cannot write to RO code memory |
| volatile unsigned int* p = (volatile unsigned int*)&ro_write; |
| *p = 99; |
| return 0; |
| } |
| |
| int nx_run(volatile unsigned int* addr) { |
| // Test that we cannot execute NX memory. Use stack memory for this |
| // because using a static means the compiler might generate a direct |
| // branch to the symbol rather than computing the function pointer |
| // address in a register as the code looks like it would do, and |
| // declaring a static writable variable that the compiler can see |
| // nobody writes leaves the compiler free to morph it into a static |
| // const variable, which gets put into a mergeable rodata section, and |
| // the Gold linker for aarch64 cannot handle a branch into a mergeable |
| // section. |
| uint8_t codebuf[16] = {}; |
| void (*func)(void) = (void*)codebuf; |
| func(); |
| return 0; |
| } |
| |
| // Note that as of 5/21/16 the crash reads: |
| // PageFault:199: UNIMPLEMENTED: faulting with a page already present. |
| int stack_overflow(volatile unsigned int* i_array) { |
| volatile unsigned int array[512]; |
| if (i_array) { |
| array[0] = i_array[0] + 1; |
| if (array[0] < 4096) |
| return stack_overflow(array); |
| } else { |
| array[0] = 0; |
| return stack_overflow(array); |
| } |
| return 0; |
| } |
| |
| int stack_buf_overrun(volatile unsigned int* arg) { |
| volatile unsigned int array[6]; |
| if (!arg) { |
| return stack_buf_overrun(array); |
| } else { |
| memset((void*)arg, 0, sizeof(array[0]) * 7); |
| } |
| return 0; |
| } |
| |
| int undefined(volatile unsigned int* unused) { |
| #if defined(__x86_64__) |
| __asm__ volatile("ud2"); |
| #elif defined(__aarch64__) |
| __asm__ volatile("brk #0"); // not undefined, but close enough |
| #else |
| #error "need to define undefined for this architecture" |
| #endif |
| return 0; |
| } |
| |
| int oom(volatile unsigned int* unused) { |
| return cpp_out_of_mem(); |
| } |
| |
| #include <unistd.h> |
| |
| // volatile to ensure compiler doesn't optimize the allocs away |
| volatile char* mem_alloc; |
| |
| int mem(volatile unsigned int* arg) { |
| int count = 0; |
| for (;;) { |
| mem_alloc = malloc(1024 * 1024); |
| memset((void*)mem_alloc, 0xa5, 1024 * 1024); |
| count++; |
| if ((count % 128) == 0) { |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(250))); |
| write(1, ".", 1); |
| } |
| } |
| } |
| |
| int use_after_free(volatile unsigned int* arg) { |
| char* p = strdup("Hello, world!"); |
| free(p); |
| puts(p); |
| return 0; |
| } |
| |
| typedef struct { |
| int depth; |
| int max_depth; |
| } deep_sleep_args_t; |
| |
| int deep_sleep(deep_sleep_args_t* args) { |
| if (args->depth < args->max_depth) { |
| args->depth++; |
| deep_sleep(args); |
| } |
| // The compiler chokes on -Winfinite-recursion otherwise. |
| volatile bool running = true; |
| while (running) { |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(10))); |
| } |
| return 0; |
| } |
| |
| int thread_func(void* arg) { |
| return deep_sleep((deep_sleep_args_t*)arg); |
| } |
| |
| int blind_write_multithreaded(volatile unsigned int* addr) { |
| // Start 5 separate threads that will recurse a bit then sleep. |
| int kThreads = 5; |
| thrd_t threads[kThreads]; |
| deep_sleep_args_t args[kThreads]; |
| for (int i = 0; i < kThreads; ++i) { |
| args[i].depth = 0; |
| args[i].max_depth = 5; |
| int ret = thrd_create_with_name(&threads[i], thread_func, &args[i], "deep_sleep"); |
| if (ret != thrd_success) { |
| printf("Unexpected thread create return: %d\n", ret); |
| return 1; |
| } |
| } |
| |
| // Wait for the threads to have finished their recursion then crash the main thread. |
| for (int i = 0; i < kThreads; ++i) { |
| while (args[i].depth < args[i].max_depth) { |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(1))); |
| } |
| } |
| blind_write(addr); |
| |
| for (int i = 0; i < kThreads; ++i) { |
| int ret = thrd_join(threads[i], NULL); |
| if (ret != thrd_success) { |
| printf("Unexpected thread join return: %d\n", ret); |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| command_t commands[] = { |
| {"write0", blind_write, "write to address 0x0"}, |
| {"read0", blind_read, "read address 0x0"}, |
| {"writero", ro_write, "write to read only code segment"}, |
| {"stackov", stack_overflow, "overflow the stack (recursive)"}, |
| {"stackbuf", stack_buf_overrun, "overrun a buffer on the stack"}, |
| {"und", undefined, "undefined instruction"}, |
| {"nx_run", nx_run, "run in no-execute memory"}, |
| {"oom", oom, "out of memory c++ death"}, |
| {"mem", mem, "out of memory"}, |
| {"use_after_free", use_after_free, "use memory after freeing it"}, |
| {"write0_mt", blind_write_multithreaded, "write to address 0x0 in one thread, sleeping in 5 others"}, |
| {NULL, NULL, NULL}, |
| }; |
| |
| int main(int argc, char** argv) { |
| printf("=@ crasher @=\n"); |
| |
| if (argc < 2) { |
| printf("default to write0 (use 'help' for more options).\n"); |
| blind_write(NULL); |
| } else { |
| if (strcmp("help", argv[1])) { |
| for (command_t* cmd = commands; cmd->name != NULL; ++cmd) { |
| if (strcmp(cmd->name, argv[1]) == 0) { |
| printf("doing : %s\n", cmd->desc); |
| cmd->func(NULL); |
| goto exit; // should not reach here. |
| } |
| } |
| } |
| |
| printf("known commands are:\n"); |
| for (command_t* cmd = commands; cmd->name != NULL; ++cmd) { |
| printf("%s : %s\n", cmd->name, cmd->desc); |
| } |
| return 0; |
| } |
| |
| exit: |
| printf("crasher: exiting normally ?!!\n"); |
| return 0; |
| } |