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 <inttypes.h>
6 #include <stdatomic.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 
12 #include <pthread.h>
13 
14 #include <zircon/syscalls.h>
15 #include <zircon/types.h>
16 
17 #define TICKS 0
18 
19 #define USE_PTHREAD_MUTEXES 0
20 #define USE_SPINLOCKS 0
21 #define USE_FUTEXES 1
22 
23 // malloc may behave differently for larger allocations (e.g., using mmap).
24 // Using up to 512k allocations will likely trigger this behavior.
25 #define LARGE_MALLOC 0
26 #define LARGE_MALLOC_SIZE (512 * 1024)
27 #define SMALL_MALLOC_SIZE 1024
28 #if LARGE_MALLOC
29 #define MALLOC_SIZE LARGE_MALLOC_SIZE
30 #else
31 #define MALLOC_SIZE SMALL_MALLOC_SIZE
32 #endif
33 
34 static atomic_int xlock = ATOMIC_VAR_INIT(0);
35 
_lock(atomic_int * lock)36 static void _lock(atomic_int* lock) {
37     while (atomic_exchange(lock, 1) != 0)
38         ;
39 }
_unlock(atomic_int * lock)40 static void _unlock(atomic_int* lock) {
41     atomic_store(lock, 0);
42 }
43 
_ftxlock(atomic_int * lock)44 static void _ftxlock(atomic_int* lock) {
45     while (atomic_exchange(lock, 1) != 0) {
46         zx_futex_wait(lock, 1, ZX_HANDLE_INVALID, ZX_TIME_INFINITE);
47     }
48 }
_ftxunlock(atomic_int * lock)49 static void _ftxunlock(atomic_int* lock) {
50     atomic_store(lock, 0);
51     zx_futex_wake(lock, 1);
52 }
53 
54 #if USE_PTHREAD_MUTEXES
55 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
56 #define LOCK()                         \
57     do {                               \
58         if (info->lock)                \
59             pthread_mutex_lock(&lock); \
60     } while (0)
61 #define UNLOCK()                         \
62     do {                                 \
63         if (info->lock)                  \
64             pthread_mutex_unlock(&lock); \
65     } while (0)
66 #endif
67 
68 #if USE_SPINLOCKS
69 #define LOCK()             \
70     do {                   \
71         if (info->lock)    \
72             _lock(&xlock); \
73     } while (0)
74 #define UNLOCK()             \
75     do {                     \
76         if (info->lock)      \
77             _unlock(&xlock); \
78     } while (0)
79 #endif
80 
81 #if USE_FUTEXES
82 #define LOCK()                \
83     do {                      \
84         if (info->lock)       \
85             _ftxlock(&xlock); \
86     } while (0)
87 #define UNLOCK()                \
88     do {                        \
89         if (info->lock)         \
90             _ftxunlock(&xlock); \
91     } while (0)
92 #endif
93 
94 #define THREADS 8
95 #define BUCKETS 16
96 
97 typedef struct info {
98     pthread_t t;
99     int n;
100     atomic_int lock;
101     int size[BUCKETS];
102     void* bucket[BUCKETS];
103 } info_t;
104 
rnum(int m)105 int rnum(int m) {
106     return (random() & 0x7FFFFFFFU) % m;
107 }
108 
blaster(void * arg)109 void* blaster(void* arg) {
110     info_t* info = arg;
111 #if TICKS
112     int tick = rnum(5000);
113 #endif
114 
115     for (;;) {
116 #if TICKS
117         tick++;
118         if (tick == 10000) {
119             printf("(%d)\n", info->n);
120             tick = rnum(5000);
121         }
122 #endif
123         int n = rnum(BUCKETS);
124         if (info->bucket[n] == NULL) {
125         allocnew:
126             info->size[n] = 7 + rnum(MALLOC_SIZE);
127             LOCK();
128             info->bucket[n] = malloc(info->size[n]);
129             UNLOCK();
130             if (info->bucket[n] == NULL) {
131                 printf("blaster %d malloc failed %d\n", info->n, n);
132                 __builtin_trap();
133             }
134             memset(info->bucket[n], info->n * n, info->size[n]);
135         } else {
136             int sz = info->size[n];
137             uint8_t* x = info->bucket[n];
138             int val = n * info->n;
139             for (int i = 0; i < sz; i++) {
140                 if (x[i] != val) {
141                     printf("blaster %d bad bucket %d\n", info->n, n);
142                     __builtin_trap();
143                 }
144             }
145             if (rnum(1000) < 750) {
146                 LOCK();
147                 free(info->bucket[n]);
148                 UNLOCK();
149                 goto allocnew;
150             } else {
151                 memset(x, val, sz);
152             }
153         }
154     }
155 
156     return NULL;
157 }
158 
heapblaster(int count,int locking)159 int heapblaster(int count, int locking) {
160     info_t info[THREADS] = {};
161     if (count < 1)
162         count = 1;
163     if (count >= THREADS)
164         count = THREADS;
165     printf("heapblaster: starting %d threads... (%s)\n",
166            count, locking ? "locking" : "not locking");
167     for (int n = 0; n < count; n++) {
168         info[n].lock = locking;
169         info[n].n = n;
170         if (count == 1) {
171             blaster(info + n);
172             return 0;
173         } else {
174             pthread_create(&info[n].t, NULL, blaster, info + n);
175         }
176     }
177     for (;;)
178         sleep(1000);
179     return 0;
180 }
181 
182 static uint8_t data[65534];
183 
writespam(int opt)184 int writespam(int opt) {
185     zx_handle_t p[2];
186     zx_status_t r;
187     uint64_t count = 0;
188 
189     if ((r = zx_channel_create(0, p, p + 1)) < 0) {
190         printf("cleanup-test: channel create 0 failed: %d\n", r);
191         return -1;
192     }
193 
194     printf("evil-tests: about to spam data into a channel\n");
195     for (;;) {
196         count++;
197         if ((r = zx_channel_write(p[0], 0, data, sizeof(data), NULL, 0)) < 0) {
198             printf("evil-tests: SUCCESS, writespammer error %d after only %" PRIu64 " writes\n", r, count);
199             return 0;
200         }
201         if ((count % 1000) == 0) {
202             printf("evil-tests: wrote %" PRIu64 " messages (%" PRIu64 " bytes).\n", count, count * sizeof(data));
203         }
204     }
205     if (opt == 0) {
206         printf("evil-tests: closing the channel (full of messages)\n");
207         zx_handle_close(p[0]);
208         zx_handle_close(p[1]);
209     } else {
210         printf("evil-tests: leaving the channel open (full of messages)\n");
211     }
212     return 0;
213 }
214 
handlespam(void)215 int handlespam(void) {
216     zx_handle_t p[2];
217     uint64_t count = 0;
218 
219     printf("evil-tests: about to create all the handles\n");
220     for (;;) {
221         zx_status_t status;
222         if ((status = zx_channel_create(0, p, p + 1)) < 0) {
223             printf("evil-tests: SUCCESS, channel create failed %d after %" PRIu64 " created\n", status, count);
224             return 0;
225         }
226         count++;
227         if ((count % 1000) == 0) {
228             printf("evil-tests: created %" PRIu64 " channels\n", count);
229         }
230     }
231     return 0;
232 }
233 
nanospam(void)234 int nanospam(void) {
235     for (;;) {
236         zx_nanosleep(1);
237     }
238 }
239 
main(int argc,char ** argv)240 int main(int argc, char** argv) {
241     if (argc < 2) {
242         printf(
243             "usage: evil-tests spam1        spam writes into channel\n"
244             "       evil-tests spam2        spam writes, don't close channel after\n"
245             "       evil-tests spam3        spam handle creation\n"
246             "       evil-tests nano         spam nanosleep\n"
247             "       evil-tests heap1 <n>    heap stress test, locking\n"
248             "       evil-tests heap2 <n>    heap stress test, no locking\n");
249         return -1;
250     } else if (!strcmp(argv[1], "spam1")) {
251         return writespam(0);
252     } else if (!strcmp(argv[1], "spam2")) {
253         return writespam(1);
254     } else if (!strcmp(argv[1], "spam3")) {
255         return handlespam();
256     } else if (!strcmp(argv[1], "nano")) {
257         return nanospam();
258     } else if (!strcmp(argv[1], "heap1")) {
259         int n = (argc > 2) ? strtoul(argv[2], 0, 10) : THREADS;
260         return heapblaster(n, 1);
261     } else if (!strcmp(argv[1], "heap2")) {
262         int n = (argc > 2) ? strtoul(argv[2], 0, 10) : THREADS;
263         return heapblaster(n, 0);
264     } else {
265         printf("unknown sub-command '%s'\n", argv[1]);
266         return -1;
267     }
268     return 0;
269 }
270