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
10#include <zircon/syscalls.h>
11
12// defined in cpp_specific.cpp.
13int cpp_out_of_mem(void);
14
15typedef struct {
16    const char* name;
17    int (*func)(volatile unsigned int*);
18    const char* desc;
19} command_t;
20
21int blind_write(volatile unsigned int* addr) {
22    *addr = 0xBAD1DEA;
23    return 0;
24}
25
26int blind_read(volatile unsigned int* addr) {
27    return (int)(*addr);
28}
29
30int ro_write(volatile unsigned int* addr) {
31    // test that we cannot write to RO code memory
32    volatile unsigned int* p = (volatile unsigned int*)&ro_write;
33    *p = 99;
34    return 0;
35}
36
37int nx_run(volatile unsigned int* addr) {
38    // Test that we cannot execute NX memory.  Use stack memory for this
39    // because using a static means the compiler might generate a direct
40    // branch to the symbol rather than computing the function pointer
41    // address in a register as the code looks like it would do, and
42    // declaring a static writable variable that the compiler can see
43    // nobody writes leaves the compiler free to morph it into a static
44    // const variable, which gets put into a mergeable rodata section, and
45    // the Gold linker for aarch64 cannot handle a branch into a mergeable
46    // section.
47    uint8_t codebuf[16] = {};
48    void (*func)(void) = (void*)codebuf;
49    func();
50    return 0;
51}
52
53// Note that as of 5/21/16 the crash reads:
54// PageFault:199: UNIMPLEMENTED: faulting with a page already present.
55int stack_overflow(volatile unsigned int* i_array) {
56    volatile unsigned int array[512];
57    if (i_array) {
58        array[0] = i_array[0] + 1;
59        if (array[0] < 4096)
60            return stack_overflow(array);
61    } else {
62        array[0] = 0;
63        return stack_overflow(array);
64    }
65    return 0;
66}
67
68int stack_buf_overrun(volatile unsigned int* arg) {
69    volatile unsigned int array[6];
70    if (!arg) {
71        return stack_buf_overrun(array);
72    } else {
73        memset((void*)arg, 0, sizeof(array[0]) * 7);
74    }
75    return 0;
76}
77
78int undefined(volatile unsigned int* unused) {
79#if defined(__x86_64__)
80    __asm__ volatile("ud2");
81#elif defined(__aarch64__)
82    __asm__ volatile("brk #0"); // not undefined, but close enough
83#else
84#error "need to define undefined for this architecture"
85#endif
86    return 0;
87}
88
89int oom(volatile unsigned int* unused) {
90    return cpp_out_of_mem();
91}
92
93#include <unistd.h>
94
95// volatile to ensure compiler doesn't optimize the allocs away
96volatile char* mem_alloc;
97
98int mem(volatile unsigned int* arg) {
99    int count = 0;
100    for (;;) {
101        mem_alloc = malloc(1024*1024);
102        memset((void*)mem_alloc, 0xa5, 1024*1024);
103        count++;
104        if ((count % 128) == 0) {
105            zx_nanosleep(zx_deadline_after(ZX_MSEC(250)));
106            write(1, ".", 1);
107        }
108    }
109
110}
111
112int use_after_free(volatile unsigned int* arg) {
113    char *p = strdup("Hello, world!");
114    free(p);
115    puts(p);
116    return 0;
117}
118
119command_t commands[] = {
120    {"write0", blind_write, "write to address 0x0"},
121    {"read0", blind_read, "read address 0x0"},
122    {"writero", ro_write, "write to read only code segment"},
123    {"stackov", stack_overflow, "overflow the stack (recursive)"},
124    {"stackbuf", stack_buf_overrun, "overrun a buffer on the stack"},
125    {"und", undefined, "undefined instruction"},
126    {"nx_run", nx_run, "run in no-execute memory"},
127    {"oom", oom, "out of memory c++ death"},
128    {"mem", mem, "out of memory"},
129    {"use_after_free", use_after_free, "use memory after freeing it"},
130    {NULL, NULL, NULL}};
131
132int main(int argc, char** argv) {
133    printf("=@ crasher @=\n");
134
135    if (argc < 2) {
136        printf("default to write0  (use 'help' for more options).\n");
137        blind_write(NULL);
138    } else {
139        if (strcmp("help", argv[1])) {
140            for (command_t* cmd = commands; cmd->name != NULL; ++cmd) {
141                if (strcmp(cmd->name, argv[1]) == 0) {
142                    printf("doing : %s\n", cmd->desc);
143                    cmd->func(NULL);
144                    goto exit; // should not reach here.
145                }
146            }
147        }
148
149        printf("known commands are:\n");
150        for (command_t* cmd = commands; cmd->name != NULL; ++cmd) {
151            printf("%s : %s\n", cmd->name, cmd->desc);
152        }
153        return 0;
154    }
155
156exit:
157    printf("crasher: exiting normally ?!!\n");
158    return 0;
159}
160