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