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 #include <lib/tipc/tipc.h>
18 #include <lib/unittest/unittest.h>
19 #include <stdlib.h>
20 #include <sys/auxv.h>
21 #include <trusty/memref.h>
22 #include <trusty_unittest.h>
23 #include <uapi/err.h>
24 #include <uapi/mm.h>
25 
26 #include <scudo_app.h>
27 #include <scudo_consts.h>
28 
29 #define TLOG_TAG "scudo_test"
30 
31 #ifndef HWCAP2_MTE
32 #define HWCAP2_MTE (1 << 18)
33 #endif
34 
35 #define PAGE_SIZE getauxval(AT_PAGESZ)
36 
send_memref_msg(handle_t chan,const void * buf,size_t len,handle_t memref)37 int send_memref_msg(handle_t chan,
38                     const void* buf,
39                     size_t len,
40                     handle_t memref) {
41     struct iovec iov = {
42             .iov_base = (void*)buf,
43             .iov_len = len,
44     };
45     ipc_msg_t msg = {
46             .iov = &iov,
47             .num_iov = 1,
48             .handles = memref < 0 ? NULL : &memref,
49             .num_handles = memref < 0 ? 0 : 1,
50     };
51     return send_msg(chan, &msg);
52 }
53 
54 /*
55  * Sends command to app and then waits for a
56  * reply or channel close. In the non-crashing case, the server
57  * should echo back the original command and scudo_srv_rpc returns
58  * NO_ERROR.
59  */
scudo_srv_rpc_memref(handle_t chan,enum scudo_command cmd,int memref)60 static int scudo_srv_rpc_memref(handle_t chan,
61                                 enum scudo_command cmd,
62                                 int memref) {
63     int ret;
64     struct scudo_msg msg = {
65             .cmd = cmd,
66     };
67 
68     ret = send_memref_msg(chan, &msg, sizeof(msg), memref);
69     ASSERT_GE(ret, 0);
70     ASSERT_EQ(ret, sizeof(msg));
71 
72     struct uevent evt;
73     ret = wait(chan, &evt, INFINITE_TIME);
74     if (ret) {
75         /* error while waiting on channel */
76         return ret;
77     }
78 
79     if (evt.event & IPC_HANDLE_POLL_HUP) {
80         ASSERT_EQ(evt.event & IPC_HANDLE_POLL_MSG, 0);
81         return ERR_CHANNEL_CLOSED;
82     }
83     ASSERT_NE(evt.event & IPC_HANDLE_POLL_MSG, 0);
84 
85     ret = tipc_recv1(chan, sizeof(msg), &msg, sizeof(msg));
86     if (ret < 0) {
87         return ret;
88     }
89     ASSERT_EQ(ret, sizeof(msg));
90     if (msg.cmd == cmd) {
91         return NO_ERROR;
92     }
93     return msg.cmd;
94 
95 test_abort:
96     /* Use ERR_IO to indicate internal error with the test app */
97     return ERR_IO;
98 }
99 
scudo_srv_rpc(handle_t chan,enum scudo_command cmd)100 static int scudo_srv_rpc(handle_t chan, enum scudo_command cmd) {
101     return scudo_srv_rpc_memref(chan, cmd, -1);
102 }
103 
104 typedef struct scudo_info {
105     handle_t chan;
106 } scudo_info_t;
107 
has_mte(void)108 static bool has_mte(void) {
109     return getauxval(AT_HWCAP2) & HWCAP2_MTE;
110 }
111 
TEST_F_SETUP(scudo_info)112 TEST_F_SETUP(scudo_info) {
113     _state->chan = INVALID_IPC_HANDLE;
114     ASSERT_EQ(tipc_connect(&_state->chan, SCUDO_TEST_SRV_PORT), 0);
115 
116 test_abort:;
117 }
118 
TEST_F_TEARDOWN(scudo_info)119 TEST_F_TEARDOWN(scudo_info) {
120     close(_state->chan);
121 }
122 
TEST_F(scudo_info,nop)123 TEST_F(scudo_info, nop) {
124     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_NOP), NO_ERROR);
125 }
126 
TEST_F(scudo_info,one_malloc)127 TEST_F(scudo_info, one_malloc) {
128     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_ONE_MALLOC), NO_ERROR);
129 }
130 
TEST_F(scudo_info,one_calloc)131 TEST_F(scudo_info, one_calloc) {
132     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_ONE_CALLOC), NO_ERROR);
133 }
134 
TEST_F(scudo_info,one_realloc)135 TEST_F(scudo_info, one_realloc) {
136     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_ONE_REALLOC), NO_ERROR);
137 }
138 
TEST_F(scudo_info,many_malloc)139 TEST_F(scudo_info, many_malloc) {
140     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_MANY_MALLOC), NO_ERROR);
141 }
142 
TEST_F(scudo_info,one_new)143 TEST_F(scudo_info, one_new) {
144     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_ONE_NEW), NO_ERROR);
145 }
146 
TEST_F(scudo_info,one_new_arr)147 TEST_F(scudo_info, one_new_arr) {
148     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_ONE_NEW_ARR), NO_ERROR);
149 }
150 
TEST_F(scudo_info,malloc_and_new)151 TEST_F(scudo_info, malloc_and_new) {
152     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_MALLOC_AND_NEW), NO_ERROR);
153 }
154 
TEST_F(scudo_info,double_free)155 TEST_F(scudo_info, double_free) {
156     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_DOUBLE_FREE),
157               ERR_CHANNEL_CLOSED);
158 }
159 
TEST_F(scudo_info,realloc_after_free)160 TEST_F(scudo_info, realloc_after_free) {
161     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_REALLOC_AFTER_FREE),
162               ERR_CHANNEL_CLOSED);
163 }
164 
TEST_F(scudo_info,dealloc_type_mismatch)165 TEST_F(scudo_info, dealloc_type_mismatch) {
166     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_DEALLOC_TYPE_MISMATCH),
167               ERR_CHANNEL_CLOSED);
168 }
169 
TEST_F(scudo_info,realloc_type_mismatch)170 TEST_F(scudo_info, realloc_type_mismatch) {
171     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_REALLOC_TYPE_MISMATCH),
172               ERR_CHANNEL_CLOSED);
173 }
174 
TEST_F(scudo_info,alloc_large)175 TEST_F(scudo_info, alloc_large) {
176     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_ALLOC_LARGE), NO_ERROR);
177 }
178 
TEST_F(scudo_info,mte_tagged_memref_small)179 TEST_F(scudo_info, mte_tagged_memref_small) {
180     if (!has_mte()) {
181         trusty_unittest_printf("[  SKIPPED ] MTE is not available\n");
182         return;
183     }
184     int ref = -1;
185     void* mem = memalign(PAGE_SIZE, PAGE_SIZE);
186     ASSERT_NE(mem, NULL);
187     memset(mem, 0x33, PAGE_SIZE);
188     ref = memref_create(
189             mem, PAGE_SIZE,
190             MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE | MMAP_FLAG_PROT_MTE);
191     ASSERT_GT(ref, 0);
192     printf("created memref %d for %p\n", ref, mem);
193     int rc = scudo_srv_rpc_memref(_state->chan, SCUDO_TAGGED_MEMREF_SMALL, ref);
194     EXPECT_EQ(rc, NO_ERROR);
195     EXPECT_EQ(*((volatile char*)mem), 0x77);
196 test_abort:
197     close(ref);
198     free(mem);
199 }
200 
TEST_F(scudo_info,mte_tagged_memref_large)201 TEST_F(scudo_info, mte_tagged_memref_large) {
202     if (!has_mte()) {
203         trusty_unittest_printf("[  SKIPPED ] MTE is not available\n");
204         return;
205     }
206     int ref = -1;
207     void* mem = memalign(PAGE_SIZE, PAGE_SIZE * 32);
208     ASSERT_NE(mem, NULL);
209     memset(mem, 0x33, PAGE_SIZE);
210     ref = memref_create(
211             mem, PAGE_SIZE * 32,
212             MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE | MMAP_FLAG_PROT_MTE);
213     ASSERT_GT(ref, 0);
214     printf("created memref %d for %p\n", ref, mem);
215     int rc = scudo_srv_rpc_memref(_state->chan, SCUDO_TAGGED_MEMREF_LARGE, ref);
216     EXPECT_EQ(rc, NO_ERROR);
217     EXPECT_EQ(*((volatile char*)mem), 0x77);
218 test_abort:
219     close(ref);
220     free(mem);
221 }
222 
TEST_F(scudo_info,mte_untagged_memref_small)223 TEST_F(scudo_info, mte_untagged_memref_small) {
224     int ref = -1;
225     void* mem = memalign(PAGE_SIZE, PAGE_SIZE);
226     ASSERT_NE(mem, NULL);
227     memset(mem, 0x33, PAGE_SIZE);
228     ref = memref_create(mem, PAGE_SIZE,
229                         MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE);
230     ASSERT_GT(ref, 0);
231     printf("created memref %d for %p\n", ref, mem);
232     int rc = scudo_srv_rpc_memref(_state->chan, SCUDO_UNTAGGED_MEMREF_SMALL,
233                                   ref);
234     EXPECT_EQ(rc, NO_ERROR);
235     EXPECT_EQ(*((volatile char*)mem), 0x77);
236 test_abort:
237     close(ref);
238     free(mem);
239 }
240 
TEST_F(scudo_info,mte_untagged_memref_large)241 TEST_F(scudo_info, mte_untagged_memref_large) {
242     int ref = -1;
243     void* mem = memalign(PAGE_SIZE, PAGE_SIZE * 32);
244     ASSERT_NE(mem, NULL);
245     memset(mem, 0x33, PAGE_SIZE);
246     ref = memref_create(mem, PAGE_SIZE * 32,
247                         MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE);
248     ASSERT_GT(ref, 0);
249     printf("created memref %d for %p\n", ref, mem);
250     int rc = scudo_srv_rpc_memref(_state->chan, SCUDO_UNTAGGED_MEMREF_LARGE,
251                                   ref);
252     EXPECT_EQ(rc, NO_ERROR);
253     EXPECT_EQ(*((volatile char*)mem), 0x77);
254 test_abort:
255     close(ref);
256     free(mem);
257 }
258 
TEST_F(scudo_info,mte_mismatched_tag_read)259 TEST_F(scudo_info, mte_mismatched_tag_read) {
260     if (!has_mte()) {
261         trusty_unittest_printf("[  SKIPPED ] MTE is not available\n");
262         return;
263     }
264     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_MEMTAG_MISMATCHED_READ),
265               ERR_CHANNEL_CLOSED);
266 }
267 
TEST_F(scudo_info,mte_mismatched_tag_write)268 TEST_F(scudo_info, mte_mismatched_tag_write) {
269     if (!has_mte()) {
270         trusty_unittest_printf("[  SKIPPED ] MTE is not available\n");
271         return;
272     }
273     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_MEMTAG_MISMATCHED_WRITE),
274               ERR_CHANNEL_CLOSED);
275 }
276 
TEST_F(scudo_info,mte_memtag_read_after_free)277 TEST_F(scudo_info, mte_memtag_read_after_free) {
278     if (!has_mte()) {
279         trusty_unittest_printf("[  SKIPPED ] MTE is not available\n");
280         return;
281     }
282     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_MEMTAG_READ_AFTER_FREE),
283               ERR_CHANNEL_CLOSED);
284 }
285 
TEST_F(scudo_info,mte_memtag_write_after_free)286 TEST_F(scudo_info, mte_memtag_write_after_free) {
287     if (!has_mte()) {
288         trusty_unittest_printf("[  SKIPPED ] MTE is not available\n");
289         return;
290     }
291     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_MEMTAG_WRITE_AFTER_FREE),
292               ERR_CHANNEL_CLOSED);
293 }
294 
TEST_F(scudo_info,alloc_benchmark)295 TEST_F(scudo_info, alloc_benchmark) {
296     EXPECT_EQ(scudo_srv_rpc(_state->chan, SCUDO_ALLOC_BENCHMARK), NO_ERROR);
297 }
298 
299 PORT_TEST(scudo_info, "com.android.trusty.scudotest")
300