1 /*
2 * Copyright (C) 2020 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 <inttypes.h>
18 #include <lk/macros.h>
19 #include <stddef.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/mman.h>
24
25 #include <lib/tipc/tipc.h>
26 #include <lib/unittest/unittest.h>
27
28 #include <trusty/memref.h>
29 #include <trusty/sys/mman.h>
30 #include <trusty/time.h>
31 #include <trusty_unittest.h>
32
33 #include <lender.h>
34 #include <lender_consts.h>
35
36 #define TLOG_TAG "memref-test"
37
38 #define PAGE_SIZE 0x1000
39
40 static __attribute__((aligned(PAGE_SIZE))) char bss_page[PAGE_SIZE];
41 static __attribute__((aligned(PAGE_SIZE))) const char ro_page[PAGE_SIZE] = {1};
42
lender_connect(handle_t * chan)43 static int lender_connect(handle_t* chan) {
44 return tipc_connect(chan, LENDER_PORT);
45 }
46
lender_command(handle_t chan,enum lender_command cmd)47 static int lender_command(handle_t chan, enum lender_command cmd) {
48 struct lender_msg msg = {
49 .cmd = cmd,
50 };
51 int rc = tipc_send1(chan, &msg, sizeof(msg));
52 if (rc != sizeof(msg)) {
53 TLOGE("Failed to send command (%d)\n", rc);
54 return -1;
55 }
56
57 return 0;
58 }
59
lender_recv_handle(handle_t chan,handle_t * out)60 static int lender_recv_handle(handle_t chan, handle_t* out) {
61 struct uevent evt;
62 int rc = wait(chan, &evt, INFINITE_TIME);
63 if (rc) {
64 return rc;
65 }
66
67 struct ipc_msg msg = {
68 .iov = NULL,
69 .num_iov = 0,
70 .handles = out,
71 .num_handles = 1,
72 };
73
74 struct ipc_msg_info msg_inf;
75 rc = get_msg(chan, &msg_inf);
76 if (rc) {
77 return rc;
78 }
79
80 rc = read_msg(chan, msg_inf.id, 0, &msg);
81 put_msg(chan, msg_inf.id);
82 return rc;
83 }
84
lender_lend_bss(handle_t chan,handle_t * memref)85 static int lender_lend_bss(handle_t chan, handle_t* memref) {
86 int rc = lender_command(chan, LENDER_LEND_BSS);
87 if (rc) {
88 return rc;
89 }
90
91 return lender_recv_handle(chan, memref);
92 }
93
lender_read_bss(handle_t chan,size_t offset,size_t size,char * buf)94 int lender_read_bss(handle_t chan, size_t offset, size_t size, char* buf) {
95 struct lender_msg message = {.cmd = LENDER_READ_BSS,
96 .region = {
97 .offset = offset,
98 .size = size,
99 }};
100
101 int rc = tipc_send1(chan, &message, sizeof(message));
102 if (rc != (int)sizeof(message)) {
103 return -1;
104 }
105
106 struct uevent evt;
107 rc = wait(chan, &evt, INFINITE_TIME);
108 if (rc) {
109 return rc;
110 }
111
112 rc = tipc_recv1(chan, size, buf, size);
113 if (rc != (int)size) {
114 return -1;
115 }
116
117 return 0;
118 }
119
lender_write_bss(handle_t chan,size_t offset,size_t size,const char * buf)120 int lender_write_bss(handle_t chan,
121 size_t offset,
122 size_t size,
123 const char* buf) {
124 struct lender_msg message = {
125 .cmd = LENDER_WRITE_BSS,
126 .region =
127 {
128 .offset = offset,
129 .size = size,
130 },
131 };
132
133 int rc = tipc_send2(chan, &message, sizeof(message), buf, size);
134 if (rc != (int)(sizeof(message) + size)) {
135 return -1;
136 }
137
138 struct uevent evt;
139 rc = wait(chan, &evt, INFINITE_TIME);
140 if (rc) {
141 return rc;
142 }
143 return tipc_recv1(chan, 0, NULL, 0);
144 }
145
lender_suicide(handle_t chan)146 int lender_suicide(handle_t chan) {
147 int rc = lender_command(chan, LENDER_SUICIDE);
148 if (rc) {
149 return rc;
150 }
151
152 close(chan);
153 return 0;
154 }
155
156 #define MM_RW (MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE)
157
TEST(memref,self_map)158 TEST(memref, self_map) {
159 volatile char* out = NULL;
160 handle_t mref = memref_create(bss_page, PAGE_SIZE, MM_RW);
161 ASSERT_GT(mref, 0);
162 out = mmap(0, PAGE_SIZE, MM_RW, 0, mref, 0);
163 ASSERT_NE((void*)out, MAP_FAILED);
164
165 *out = 3;
166
167 EXPECT_EQ(bss_page[0], 3);
168
169 EXPECT_EQ(0, munmap((void*)out, PAGE_SIZE));
170
171 test_abort:
172 close(mref);
173 bss_page[0] = 0;
174 }
175
TEST(memref,dup_map)176 TEST(memref, dup_map) {
177 volatile char* mbuf = NULL;
178 volatile char* dbuf = NULL;
179 handle_t dref = INVALID_IPC_HANDLE;
180 handle_t mref = memref_create(bss_page, PAGE_SIZE, MM_RW);
181 ASSERT_GT(mref, 0);
182
183 dref = dup(mref);
184 ASSERT_GT(dref, 0);
185
186 mbuf = mmap(0, PAGE_SIZE, MM_RW, 0, mref, 0);
187 ASSERT_NE((void*)mbuf, MAP_FAILED);
188
189 dbuf = mmap(0, PAGE_SIZE, MM_RW, 0, dref, 0);
190 ASSERT_NE((void*)dbuf, MAP_FAILED);
191
192 dbuf[0] = 42;
193 EXPECT_EQ(bss_page[0], 42);
194 EXPECT_EQ(mbuf[0], 42);
195
196 test_abort:
197 if (dbuf && dbuf != MAP_FAILED) {
198 EXPECT_EQ(0, munmap((void*)dbuf, PAGE_SIZE));
199 }
200 if (mbuf && mbuf != MAP_FAILED) {
201 EXPECT_EQ(0, munmap((void*)mbuf, PAGE_SIZE));
202 }
203 close(dref);
204 close(mref);
205 bss_page[0] = 0;
206 }
207
208 #define EXPECT_CLOSE(e) \
209 { \
210 handle_t rc = e; \
211 EXPECT_LT(rc, 0); \
212 close(rc); \
213 }
214
TEST(memref,creation)215 TEST(memref, creation) {
216 /* should fail due to bad alignment */
217 EXPECT_CLOSE(memref_create(bss_page + 1, PAGE_SIZE, MM_RW));
218 /* should fail due to non-page size */
219 EXPECT_CLOSE(memref_create(bss_page, PAGE_SIZE - 1, MM_RW));
220 /* should fail due to out-of-bounds size */
221 EXPECT_CLOSE(memref_create(bss_page, 10 * PAGE_SIZE, MM_RW));
222 /* should fail due to exec */
223 EXPECT_CLOSE(memref_create(bss_page, PAGE_SIZE,
224 MM_RW | MMAP_FLAG_PROT_EXEC));
225 /* should fail due to garbage prot */
226 EXPECT_CLOSE(memref_create(bss_page, PAGE_SIZE, 0xF000));
227 /* should fail due to rw perms on a ro page */
228 EXPECT_CLOSE(memref_create((void*)ro_page, PAGE_SIZE, MM_RW));
229 /* should succeed, ro perms on a rw page */
230 handle_t mref = memref_create((void*)bss_page, PAGE_SIZE,
231 MMAP_FLAG_PROT_READ);
232 EXPECT_GT(mref, 0);
233 close(mref);
234 }
235
test_recv_ref(bool do_dup)236 static void test_recv_ref(bool do_dup) {
237 handle_t chan = INVALID_IPC_HANDLE;
238 handle_t remote_memref = INVALID_IPC_HANDLE;
239 handle_t dup_memref = INVALID_IPC_HANDLE;
240 handle_t mmap_memref;
241 volatile char* out = NULL;
242 char remote_buf[1] = {0};
243 ASSERT_EQ(lender_connect(&chan), 0);
244 int rc = lender_lend_bss(chan, &remote_memref);
245 ASSERT_EQ(rc, 0);
246
247 if (do_dup) {
248 dup_memref = dup(remote_memref);
249 ASSERT_GE(dup_memref, 0);
250 mmap_memref = dup_memref;
251 } else {
252 mmap_memref = remote_memref;
253 }
254
255 out = mmap(0, PAGE_SIZE, MM_RW, 0, mmap_memref, 0);
256 ASSERT_NE((void*)out, NULL);
257
258 *out = 1;
259 EXPECT_EQ(0, lender_read_bss(chan, 0, sizeof(remote_buf), remote_buf));
260 EXPECT_EQ(1, remote_buf[0]);
261
262 remote_buf[0] = 42;
263 EXPECT_EQ(0, lender_write_bss(chan, 1, sizeof(remote_buf), remote_buf));
264 EXPECT_EQ(out[0], 1);
265 EXPECT_EQ(out[1], 42);
266
267 out[0] = 0;
268 out[1] = 0;
269
270 EXPECT_EQ(0, munmap((void*)out, PAGE_SIZE));
271
272 test_abort:
273 /* double close or INVALID_HANDLE should not cause an issue */
274 close(dup_memref);
275 close(remote_memref);
276 close(chan);
277 }
278
TEST(memref,recv_ref)279 TEST(memref, recv_ref) {
280 test_recv_ref(false);
281 }
282
TEST(memref,dup_ref)283 TEST(memref, dup_ref) {
284 test_recv_ref(true);
285 }
286
287 /*
288 * Tries to cause an unintended memref leak.
289 * Many iterations are needed to make the leak surface in the form
290 * of the lender application failing to reset itself.
291 *
292 * Ideally keep this test at the end so that if it OOMs the VM, the suite
293 * will have already produced all other results.
294 */
295 const int leak_creation_iters = 1000;
TEST(memref,leak_creation)296 TEST(memref, leak_creation) {
297 handle_t chan = INVALID_IPC_HANDLE;
298 handle_t memref = INVALID_IPC_HANDLE;
299 for (int i = 0; i < leak_creation_iters; i++) {
300 ASSERT_EQ(0, lender_connect(&chan));
301 ASSERT_EQ(0, lender_lend_bss(chan, &memref));
302 close(memref);
303 ASSERT_EQ(0, lender_suicide(chan));
304 close(chan);
305 }
306
307 test_abort:
308 /* double closes should be safe */
309 close(memref);
310 close(chan);
311 }
312
313 PORT_TEST(memref, "com.android.memref.test")
314