1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define TLOG_TAG "scudo_app"
18 
19 #include <assert.h>
20 #include <lib/tipc/tipc.h>
21 #include <lib/tipc/tipc_srv.h>
22 #include <lk/err_ptr.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/mman.h>
26 #include <trusty/memref.h>
27 #include <trusty_log.h>
28 #include <uapi/err.h>
29 #include <uapi/mm.h>
30 
31 #include <scudo_app.h>
32 #include <scudo_consts.h>
33 
34 #define ARR_SIZE 10
35 
36 /*
37  * Scudo supports dealloc type mismatch checking. That is, Scudo
38  * can be configured to report an error if a chunk is allocated
39  * using new but deallocated using free instead of delete, for
40  * example. By default, dealloc type mismatch is disabled, but we
41  * enable it here to check its functionality in
42  * SCUDO_DEALLOC_TYPE_MISMATCH and also to ensure default Scudo
43  * options can be overridden.
44  */
45 extern "C" __attribute__((visibility("default"))) const char*
__scudo_default_options()46 __scudo_default_options() {
47     return "dealloc_type_mismatch=true";
48 }
49 
50 static int scudo_on_message(const struct tipc_port* port,
51                             handle_t chan,
52                             void* ctx);
53 
54 static struct tipc_port_acl scudo_port_acl = {
55         .flags = IPC_PORT_ALLOW_TA_CONNECT,
56         .uuid_num = 0,
57         .uuids = NULL,
58         .extra_data = NULL,
59 };
60 
61 static struct tipc_port scudo_port = {
62         .name = SCUDO_TEST_SRV_PORT,
63         .msg_max_size = sizeof(struct scudo_msg),
64         .msg_queue_len = 1,
65         .acl = &scudo_port_acl,
66         .priv = NULL,
67 };
68 
69 /*
70  * To make sure the variable isn't optimized away.
71  */
touch(volatile void * a)72 static void touch(volatile void* a) {
73     *(reinterpret_cast<volatile char*>(a)) =
74             *(reinterpret_cast<volatile char*>(a));
75 }
76 
77 /*
78  * Touch all bytes in a string (up to ARR_SIZE).
79  * String buffer should be at least ARR_SIZE long.
80  */
touch_string(char * arr)81 static void touch_string(char* arr) {
82     /* make sure we don't go out of the buffer */
83     arr[ARR_SIZE - 1] = '\0';
84 
85     while (*arr != '\0') {
86         *(reinterpret_cast<volatile char*>(arr)) =
87                 *(reinterpret_cast<volatile char*>(arr));
88         arr++;
89     }
90 }
91 
92 /*
93  * In addition to touching arr, it is memset with fill_char
94  * and printed as a check that arr points to valid writable memory.
95  */
touch_and_print(char * arr,const char fill_char)96 static void touch_and_print(char* arr, const char fill_char) {
97     touch(arr);
98     memset(arr, fill_char, ARR_SIZE - 1);
99     arr[ARR_SIZE - 1] = '\0';
100     TLOG("arr = %s\n", arr);
101 }
102 
retagged(void * taggedptr)103 static void* retagged(void* taggedptr) {
104     uint64_t tagged = reinterpret_cast<uint64_t>(taggedptr);
105     uint64_t tag = tagged & 0x0f00000000000000;
106     uint64_t untagged = tagged & 0x00ffffffffffffff;
107     uint64_t newtag = (tag + 0x0100000000000000) & 0x0f00000000000000;
108     ;
109     return reinterpret_cast<void*>(newtag | untagged);
110 }
111 
recv_memref_msg(handle_t chan,size_t min_sz,void * buf,size_t buf_sz,int * memref)112 int recv_memref_msg(handle_t chan,
113                     size_t min_sz,
114                     void* buf,
115                     size_t buf_sz,
116                     int* memref) {
117     int rc;
118     ipc_msg_info_t msg_inf;
119 
120     rc = get_msg(chan, &msg_inf);
121     if (rc)
122         return rc;
123 
124     if (msg_inf.len < min_sz || msg_inf.len > buf_sz ||
125         msg_inf.num_handles > 1) {
126         /* unexpected msg size: buffer too small or too big */
127         rc = ERR_BAD_LEN;
128     } else {
129         struct iovec iov = {
130                 .iov_base = buf,
131                 .iov_len = buf_sz,
132         };
133         ipc_msg_t msg = {
134                 .num_iov = 1,
135                 .iov = &iov,
136                 .num_handles = msg_inf.num_handles,
137                 .handles = msg_inf.num_handles ? memref : NULL,
138         };
139         rc = read_msg(chan, msg_inf.id, 0, &msg);
140     }
141 
142     put_msg(chan, msg_inf.id);
143     return rc;
144 }
145 
scudo_on_message(const struct tipc_port * port,handle_t chan,void * ctx)146 static int scudo_on_message(const struct tipc_port* port,
147                             handle_t chan,
148                             void* ctx) {
149     struct scudo_msg msg;
150     int memref = -1;
151 
152     int ret = recv_memref_msg(chan, sizeof(msg), &msg, sizeof(msg), &memref);
153     if (ret < 0 || ret != sizeof(msg)) {
154         TLOGE("Failed to receive message (%d)\n", ret);
155         return ret;
156     }
157 
158     switch (msg.cmd) {
159     /*
160      * SCUDO_NOP test checks that the internal testing machinery
161      * is working properly even when no Scudo functions are called.
162      * Since some of the tests are expected to crash the server, we
163      * need to make sure the server isn't just always crashing.
164      */
165     case SCUDO_NOP: {
166         TLOGI("nop\n");
167         break;
168     }
169     /*
170      * SCUDO_ONE_MALLOC tests that a single call to malloc and free
171      * works as intended.
172      */
173     case SCUDO_ONE_MALLOC: {
174         TLOGI("one malloc\n");
175         char* arr = reinterpret_cast<char*>(malloc(ARR_SIZE));
176         touch_and_print(arr, 'a');
177         free(arr);
178         break;
179     }
180     /*
181      * Similar to SCUDO_ONE_MALLOC, SCUDO_ONE_CALLOC tests that a
182      * single call to calloc and free works as intended.
183      */
184     case SCUDO_ONE_CALLOC: {
185         TLOGI("one calloc\n");
186         char* arr = reinterpret_cast<char*>(calloc(ARR_SIZE, 1));
187         touch_and_print(arr, 'a');
188         free(arr);
189         break;
190     }
191     /* Tests that a single call to realloc works. */
192     case SCUDO_ONE_REALLOC: {
193         TLOGI("one realloc\n");
194         char* arr = reinterpret_cast<char*>(malloc(ARR_SIZE));
195         touch_and_print(arr, 'a');
196         arr = reinterpret_cast<char*>(realloc(arr, 2 * ARR_SIZE));
197         touch_and_print(arr + ARR_SIZE - 1, 'b');
198         TLOG("arr = %s\n", arr);
199         free(arr);
200         break;
201     }
202     /*
203      * SCUDO_MANY_MALLOC performs a series of allocations and
204      * deallocations to test (1) that deallocated chunks can be
205      * reused, and (2) that Scudo can service various different
206      * sizes of allocations requests. We know chunks are reused
207      * because this app has 2MB bytes of heap memory and ~10MB
208      * bytes are malloc-ed by SCUDO_MANY_MALLOC.
209      */
210     case SCUDO_MANY_MALLOC: {
211         TLOGI("many malloc\n");
212         // exercise a few of the smaller size classes in the primary allocator
213         for (int i = 0; i < 4000; ++i) {
214             char* arr = reinterpret_cast<char*>(malloc(ARR_SIZE + i));
215             touch(arr);
216             snprintf(arr, ARR_SIZE, "(%d)!", i);
217             touch_string(arr);
218             if ((i % 100) == 0) {
219                 TLOG("arr = %s\n", arr);
220             }
221             free(arr);
222         }
223         // do some larger allocations to verify the secondary allocator
224         // releases memory
225         for (int i = 0; i < 20; ++i) {
226             TLOG("secondary %d\n", i);
227             char* arr = reinterpret_cast<char*>(malloc(128 * 1024));
228             touch(arr);
229             free(arr);
230         }
231         break;
232     }
233     /* Tests that a single allocation with new and delete works. */
234     case SCUDO_ONE_NEW: {
235         TLOGI("one new\n");
236         int* foo = new int(37);
237         touch(foo);
238         TLOG("*foo = %d\n", *foo);
239         delete foo;
240         break;
241     }
242     /* Tests that a single allocation with new[] and delete[] works. */
243     case SCUDO_ONE_NEW_ARR: {
244         TLOGI("one new arr\n");
245         char* arr = new char[ARR_SIZE];
246         touch_and_print(arr, 'a');
247         delete[] arr;
248         break;
249     }
250     /* Tests that Scudo can service allocation requests using both malloc and
251      * new. */
252     case SCUDO_MALLOC_AND_NEW: {
253         TLOGI("malloc and new\n");
254         char* arr1 = reinterpret_cast<char*>(malloc(ARR_SIZE));
255         touch_and_print(arr1, 'a');
256         char* arr2 = new char[ARR_SIZE];
257         touch_and_print(arr2, 'b');
258         free(arr1);
259         delete[] arr2;
260         break;
261     }
262     /*
263      * Scudo uses checksummed headers to protect against double-freeing,
264      * so this test which attempts to free a chunk twice should crash.
265      */
266     case SCUDO_DOUBLE_FREE: {
267         TLOGI("double free\n");
268         char* arr = reinterpret_cast<char*>(malloc(ARR_SIZE));
269         touch_and_print(arr, 'a');
270         free(arr);
271         free(arr);
272         break;
273     }
274     /*
275      * Scudo ensures that freed chunks cannot be realloc-ed, so this
276      * test which attempts to realloc a freed chunk should crash.
277      */
278     case SCUDO_REALLOC_AFTER_FREE: {
279         TLOGI("realloc after free\n");
280         char* arr = reinterpret_cast<char*>(malloc(ARR_SIZE));
281         touch_and_print(arr, 'a');
282         free(arr);
283         arr = reinterpret_cast<char*>(realloc(arr, 2 * ARR_SIZE));
284         /* touch arr so realloc is not optimized away */
285         touch(arr);
286         break;
287     }
288     /*
289      * When dealloc_type_mismatch is enabled, Scudo ensures that chunks
290      * are allocated and deallocated using corresponding functions. Since
291      * this test allocates a chunk with new and deallocates it with free,
292      * it should crash the server.
293      */
294     case SCUDO_DEALLOC_TYPE_MISMATCH: {
295         TLOGI("dealloc type mismatch\n");
296         char* arr = new char[ARR_SIZE];
297         touch_and_print(arr, 'a');
298         free(arr);
299         break;
300     }
301     /*
302      * Similar to SCUDO_DEALLOC_TYPE_MISMATCH, with dealloc_type_mismatch,
303      * Scudo should ensure that chunks from memalign() cannot be realloc()'d
304      * which could lose alignment.
305      */
306     case SCUDO_REALLOC_TYPE_MISMATCH: {
307         TLOGI("realloc type mismatch\n");
308         char* arr = reinterpret_cast<char*>(memalign(32, ARR_SIZE));
309         touch_and_print(arr, 'a');
310         arr = reinterpret_cast<char*>(realloc(arr, ARR_SIZE * 2));
311         break;
312     }
313 
314     case SCUDO_ALLOC_LARGE: {
315         TLOGI("alloc 1.5MB\n");
316         char* arr = reinterpret_cast<char*>(malloc(1500000));
317         touch(arr);
318         free(arr);
319         break;
320     }
321 
322     case SCUDO_TAGGED_MEMREF_SMALL:
323     case SCUDO_TAGGED_MEMREF_LARGE: {
324         size_t memrefsize = 4096;
325         if (msg.cmd == SCUDO_TAGGED_MEMREF_LARGE) {
326             memrefsize *= 32;
327         }
328         TLOGI("tagged memref (%d)\n", memref);
329         volatile char* mapped = (volatile char*)mmap(
330                 0, memrefsize,
331                 MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE | MMAP_FLAG_PROT_MTE,
332                 0, memref, 0);
333         if (mapped != MAP_FAILED) {
334             TLOGI("Tagged memref should have failed\n");
335             msg.cmd = SCUDO_TEST_FAIL;
336             munmap((void*)mapped, memrefsize);
337             close(memref);
338             break;
339         }
340 
341         mapped = (volatile char*)mmap(
342                 0, memrefsize, MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE, 0,
343                 memref, 0);
344         if (mapped == MAP_FAILED) {
345             TLOGI("Untagged mapping failed\n");
346             msg.cmd = SCUDO_TEST_FAIL;
347             close(memref);
348             break;
349         }
350         *mapped = 0x77;
351         munmap((void*)mapped, memrefsize);
352         close(memref);
353         break;
354     }
355 
356     case SCUDO_UNTAGGED_MEMREF_SMALL:
357     case SCUDO_UNTAGGED_MEMREF_LARGE: {
358         size_t memrefsize = 4096;
359         if (msg.cmd == SCUDO_UNTAGGED_MEMREF_LARGE) {
360             memrefsize *= 32;
361         }
362         TLOGI("untagged memref (%d)\n", memref);
363         volatile char* mapped = (volatile char*)mmap(
364                 0, memrefsize, MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE, 0,
365                 memref, 0);
366 
367         if (!mapped || *mapped != 0x33) {
368             TLOGI("no map or bad data in memref %p: %0x\n", mapped,
369                   mapped ? *mapped : 0);
370             msg.cmd = SCUDO_TEST_FAIL;
371             close(memref);
372             break;
373         }
374         *mapped = 0x77;
375         munmap((void*)mapped, memrefsize);
376         close(memref);
377         break;
378     }
379 
380     case SCUDO_MEMTAG_MISMATCHED_READ: {
381         void* mem = malloc(64);
382         char* arr = reinterpret_cast<char*>(mem);
383         *arr = 0x33;
384         volatile char* retagged_arr =
385                 3 + reinterpret_cast<char*>(retagged(mem));
386         TLOGI("mismatched tag read %016lx %016lx\n", (uint64_t)arr,
387               (uint64_t)retagged_arr);
388         *arr = *retagged_arr;
389         TLOGI("should not be here\n");
390         free(mem);
391         break;
392     }
393 
394     case SCUDO_MEMTAG_MISMATCHED_WRITE: {
395         void* mem = malloc(64);
396         char* arr = reinterpret_cast<char*>(mem);
397         *arr = 0x44;
398         volatile char* retagged_arr = reinterpret_cast<char*>(retagged(mem));
399         TLOGI("mismatched tag write %016lx %016lx\n", (uint64_t)arr,
400               (uint64_t)retagged_arr);
401         *retagged_arr = *arr;
402         TLOGI("should not be here\n");
403         free(mem);
404         break;
405     }
406 
407     case SCUDO_MEMTAG_READ_AFTER_FREE: {
408         void* mem = malloc(64);
409         memset(mem, 64, 0xaa);
410         char* arr = reinterpret_cast<char*>(mem);
411         free(mem);
412         TLOGI("read after free %016lx\n", (uint64_t)arr);
413         touch(arr);  // this reads before writing
414         TLOGI("should not be here\n");
415         break;
416     }
417 
418     case SCUDO_MEMTAG_WRITE_AFTER_FREE: {
419         void* mem = malloc(64);
420         memset(mem, 64, 0xbb);
421         char* arr = reinterpret_cast<char*>(mem);
422         free(mem);
423         TLOGI("write after free %016lx\n", (uint64_t)arr);
424         *arr = 1;
425         TLOGI("should not be here\n");
426         break;
427     }
428 
429     case SCUDO_ALLOC_BENCHMARK: {
430         TLOGI("alloc benchmark\n");
431         char* arr = reinterpret_cast<char*>(malloc(1500000));
432         touch(arr);
433         free(arr);
434         for (int i = 0; i < 1000; i++) {
435             uint num_allocs = rand() % 40 + 1;
436             char** arr2 = reinterpret_cast<char**>(
437                     malloc(sizeof(char*) * num_allocs));
438             for (uint j = 0; j < num_allocs; j++) {
439                 uint num_allocs_2 = rand() % 64 + 1;
440                 arr2[j] = reinterpret_cast<char*>(malloc(num_allocs_2));
441                 touch(arr2[j]);
442             }
443             for (uint j = 0; j < num_allocs; j++) {
444                 free(arr2[j]);
445             }
446             free(arr2);
447         }
448         break;
449     }
450 
451     default:
452         TLOGE("Bad command: %d\n", msg.cmd);
453         msg.cmd = SCUDO_BAD_CMD;
454     }
455     /*
456      * We echo the incoming command in the case where the app
457      * runs the test without crashing. This is effectively saying "did
458      * not crash when executing command X."
459      */
460     ret = tipc_send1(chan, &msg, sizeof(msg));
461     if (ret < 0 || ret != sizeof(msg)) {
462         TLOGE("Failed to send message (%d)\n", ret);
463         return ret < 0 ? ret : ERR_IO;
464     }
465 
466     return 0;
467 }
468 
469 static struct tipc_srv_ops scudo_ops = {
470         .on_message = scudo_on_message,
471 };
472 
main(void)473 int main(void) {
474     struct tipc_hset* hset = tipc_hset_create();
475     if (IS_ERR(hset)) {
476         TLOGE("Failed to create handle set (%d)\n", PTR_ERR(hset));
477         return PTR_ERR(hset);
478     }
479 
480     int rc = tipc_add_service(hset, &scudo_port, 1, 1, &scudo_ops);
481     if (rc < 0) {
482         TLOGE("Failed to add service (%d)\n", rc);
483         return rc;
484     }
485 
486     /* if app exits, kernel will log that */
487     return tipc_run_event_loop(hset);
488 }
489