1 /*
2  * Copyright (C) 2022 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 "dlmalloc_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 <trusty_log.h>
26 #include <uapi/err.h>
27 
28 #include <dlmalloc_app.h>
29 #include <dlmalloc_consts.h>
30 
31 #define ARR_SIZE 10
32 
33 static struct tipc_port_acl dlmalloc_port_acl = {
34         .flags = IPC_PORT_ALLOW_TA_CONNECT,
35         .uuid_num = 0,
36         .uuids = NULL,
37         .extra_data = NULL,
38 };
39 
40 static struct tipc_port dlmalloc_port = {
41         .name = DLMALLOC_TEST_SRV_PORT,
42         .msg_max_size = sizeof(struct dlmalloc_test_msg),
43         .msg_queue_len = 1,
44         .acl = &dlmalloc_port_acl,
45         .priv = NULL,
46 };
47 
48 /*
49  * To make sure the variable isn't optimized away.
50  */
touch(volatile void * a)51 static void touch(volatile void* a) {
52     *(reinterpret_cast<volatile char*>(a)) =
53             *(reinterpret_cast<volatile char*>(a));
54 }
55 
56 /*
57  * In addition to touching arr, it is memset with fill_char
58  * and printed as a check that arr points to valid writable memory.
59  */
touch_and_print(char * arr,const char fill_char)60 static void touch_and_print(char* arr, const char fill_char) {
61     touch(arr);
62     memset(arr, fill_char, ARR_SIZE - 1);
63     arr[ARR_SIZE - 1] = '\0';
64     TLOGI("arr = %s\n", arr);
65 }
66 
dlmalloc_on_message(const struct tipc_port * port,handle_t chan,void * ctx)67 static int dlmalloc_on_message(const struct tipc_port* port,
68                                handle_t chan,
69                                void* ctx) {
70     struct dlmalloc_test_msg msg;
71 
72     int ret = tipc_recv1(chan, sizeof(msg), &msg, sizeof(msg));
73     if (ret < 0) {
74         TLOGE("Failed to receive message (%d)\n", ret);
75         return ret;
76     } else if (ret != sizeof(msg)) {
77         TLOGE("Bad response length\n");
78         return ERR_BAD_LEN;
79     }
80 
81     switch (msg.cmd) {
82     /*
83      * DLMALLOC_TEST_NOP test checks that the internal testing machinery
84      * is working properly even when no dlmalloc functions are called.
85      * Since some of the tests are expected to crash the server, we
86      * need to make sure the server isn't just always crashing.
87      */
88     case DLMALLOC_TEST_NOP: {
89         TLOGI("nop\n");
90         break;
91     }
92     /*
93      * DLMALLOC_TEST_ONE_MALLOC tests that a single call to malloc and free
94      * works as intended.
95      */
96     case DLMALLOC_TEST_ONE_MALLOC: {
97         TLOGI("one malloc\n");
98         char* arr = reinterpret_cast<char*>(malloc(ARR_SIZE));
99         touch_and_print(arr, 'a');
100         free(arr);
101         break;
102     }
103     /*
104      * Similar to DLMALLOC_TEST_ONE_MALLOC, DLMALLOC_TEST_ONE_CALLOC tests that
105      * a single call to calloc and free works as intended.
106      */
107     case DLMALLOC_TEST_ONE_CALLOC: {
108         TLOGI("one calloc\n");
109         char* arr = reinterpret_cast<char*>(calloc(ARR_SIZE, 1));
110         touch_and_print(arr, 'a');
111         free(arr);
112         break;
113     }
114     /* Tests that a single call to realloc works. */
115     case DLMALLOC_TEST_ONE_REALLOC: {
116         TLOGI("one realloc\n");
117         char* arr = reinterpret_cast<char*>(malloc(ARR_SIZE));
118         touch_and_print(arr, 'a');
119         arr = reinterpret_cast<char*>(realloc(arr, 2 * ARR_SIZE));
120         touch_and_print(arr + ARR_SIZE - 1, 'b');
121         TLOGI("arr = %s\n", arr);
122         free(arr);
123         break;
124     }
125     /*
126      * DLMALLOC_TEST_MANY_MALLOC performs a series of allocations and
127      * deallocations to test (1) that deallocated chunks can be
128      * reused, and (2) that dlmalloc can service various different
129      * sizes of allocations requests. We know chunks are reused
130      * because this app has 2.1MB bytes of heap memory and at least
131      * 3MB bytes are malloc-ed by DLMALLOC_TEST_MANY_MALLOC.
132      */
133     case DLMALLOC_TEST_MANY_MALLOC: {
134         TLOGI("many malloc\n");
135         for (int i = 0; i < 3000; ++i) {
136             char* arr = reinterpret_cast<char*>(malloc(1000 + i));
137             touch(arr);
138             snprintf(arr, ARR_SIZE, "(%d)!", i);
139             TLOGI("arr = %s\n", arr);
140             free(arr);
141         }
142         break;
143     }
144     /* Tests that a single allocation with new and delete works. */
145     case DLMALLOC_TEST_ONE_NEW: {
146         TLOGI("one new\n");
147         int* foo = new int(37);
148         touch(foo);
149         TLOGI("*foo = %d\n", *foo);
150         delete foo;
151         break;
152     }
153     /* Tests that a single allocation with new[] and delete[] works. */
154     case DLMALLOC_TEST_ONE_NEW_ARR: {
155         TLOGI("one new arr\n");
156         char* arr = new char[ARR_SIZE];
157         touch_and_print(arr, 'a');
158         delete[] arr;
159         break;
160     }
161     /* Tests that dlmalloc can service allocation requests using both malloc and
162      * new. */
163     case DLMALLOC_TEST_MALLOC_AND_NEW: {
164         TLOGI("malloc and new\n");
165         char* arr1 = reinterpret_cast<char*>(malloc(ARR_SIZE));
166         touch_and_print(arr1, 'a');
167         char* arr2 = new char[ARR_SIZE];
168         touch_and_print(arr2, 'b');
169         free(arr1);
170         delete[] arr2;
171         break;
172     }
173     /*
174      * Test which attempts to free a chunk twice should crash.
175      */
176     case DLMALLOC_TEST_DOUBLE_FREE: {
177         TLOGI("double free\n");
178         char* arr = reinterpret_cast<char*>(malloc(ARR_SIZE));
179         touch_and_print(arr, 'a');
180         free(arr);
181         free(arr);
182         break;
183     }
184     /*
185      * Test which attempts to realloc a freed chunk should crash.
186      */
187     case DLMALLOC_TEST_REALLOC_AFTER_FREE: {
188         TLOGI("realloc after free\n");
189         char* arr = reinterpret_cast<char*>(malloc(ARR_SIZE));
190         touch_and_print(arr, 'a');
191         free(arr);
192         arr = reinterpret_cast<char*>(realloc(arr, 2 * ARR_SIZE));
193         /* touch arr so realloc is not optimized away */
194         touch(arr);
195         break;
196     }
197     /*
198      * Allocates a chunk with new and deallocates it with free,
199      * it should crash the server.
200      */
201     case DLMALLOC_TEST_DEALLOC_TYPE_MISMATCH: {
202         TLOGI("dealloc type mismatch\n");
203         char* arr = new char[ARR_SIZE];
204         touch_and_print(arr, 'a');
205         free(arr);
206         break;
207     }
208 
209     case DLMALLOC_TEST_ALLOC_LARGE: {
210         TLOGI("alloc 1.5MB\n");
211         char* arr = reinterpret_cast<char*>(malloc(1500000));
212         touch(arr);
213         free(arr);
214         break;
215     }
216 
217     default:
218         TLOGE("Bad command: %d\n", msg.cmd);
219         msg.cmd = DLMALLOC_TEST_BAD_CMD;
220     }
221     /*
222      * We echo the incoming command in the case where the app
223      * runs the test without crashing. This is effectively saying "did
224      * not crash when executing command X."
225      */
226     ret = tipc_send1(chan, &msg, sizeof(msg));
227     if (ret < 0 || ret != sizeof(msg)) {
228         TLOGE("Failed to send message (%d)\n", ret);
229         return ret < 0 ? ret : ERR_IO;
230     }
231 
232     return 0;
233 }
234 
235 static struct tipc_srv_ops dlmalloc_ops = {
236         .on_message = dlmalloc_on_message,
237 };
238 
main(void)239 int main(void) {
240     struct tipc_hset* hset = tipc_hset_create();
241     if (IS_ERR(hset)) {
242         TLOGE("Failed to create handle set (%d)\n", PTR_ERR(hset));
243         return PTR_ERR(hset);
244     }
245 
246     int rc = tipc_add_service(hset, &dlmalloc_port, 1, 1, &dlmalloc_ops);
247     if (rc < 0) {
248         TLOGE("Failed to add service (%d)\n", rc);
249         return rc;
250     }
251 
252     /* if app exits, kernel will log that */
253     return tipc_run_event_loop(hset);
254 }
255