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