1 #include <kernel/usercopy.h>
2 #include <lib/unittest/unittest.h>
3 
4 typedef struct iovectest {
5     struct vmm_aspace* aspace;
6     user_addr_t buffer_addr;
7     user_addr_t iovec_addr;
8 } iovectest_t;
9 
10 static const char test_pattern[] = "abcdefghijklmnopqrstuvwxyz!";
11 
TEST_F_SETUP(iovectest)12 TEST_F_SETUP(iovectest) {
13     _state->aspace = NULL;
14 
15     /* Setup a user address space. */
16     struct vmm_aspace* aspace = NULL;
17     ASSERT_EQ(0, vmm_create_aspace(&aspace, "iovectest", 0));
18     _state->aspace = aspace;
19     _state->buffer_addr = 8 * PAGE_SIZE;
20     _state->iovec_addr = _state->buffer_addr + 256;
21 
22     /* Allocate a page of memory. */
23     void* user_ptr = (void*)(uintptr_t)_state->buffer_addr;
24     ASSERT_EQ(0, vmm_alloc(aspace, "iovectest", PAGE_SIZE, &user_ptr, 0,
25                            VMM_FLAG_VALLOC_SPECIFIC,
26                            ARCH_MMU_FLAG_PERM_USER |
27                                    ARCH_MMU_FLAG_PERM_NO_EXECUTE));
28 
29     vmm_set_active_aspace(aspace);
30 
31     /* Write the test pattern into memory. */
32     ASSERT_EQ(0, copy_to_user(_state->buffer_addr, test_pattern,
33                               sizeof(test_pattern)));
34 
35 test_abort:;
36 }
37 
TEST_F_TEARDOWN(iovectest)38 TEST_F_TEARDOWN(iovectest) {
39     vmm_set_active_aspace(NULL);
40     if (_state->aspace) {
41         vmm_free_aspace(_state->aspace);
42         _state->aspace = NULL;
43     }
44 }
45 
46 /* Check the test fixture is OK. */
TEST_F(iovectest,fixture)47 TEST_F(iovectest, fixture) {
48     ASSERT_LT(0, sizeof(test_pattern));
49 
50     /* Non-zero clear because last character of the pattern is null. */
51     char buf[sizeof(test_pattern)];
52     memset(buf, 0xff, sizeof(test_pattern));
53 
54     /* Read back the test pattern. */
55     int ret = copy_from_user(buf, _state->buffer_addr, sizeof(test_pattern));
56     ASSERT_EQ(0, ret);
57 
58     /* Make sure it's what we expect. */
59     ASSERT_EQ(0, memcmp(test_pattern, buf, sizeof(test_pattern)));
60 
61 test_abort:;
62 }
63 
64 /* Write an iovec with evenly spaced chunks to userspace. */
write_chunked_iovec(iovectest_t * _state,int size)65 static int write_chunked_iovec(iovectest_t* _state, int size) {
66     struct iovec_user uiov[sizeof(test_pattern)];
67     unsigned int index = 0;
68     for (unsigned int i = 0; i < sizeof(test_pattern); i += size, index += 1) {
69         uiov[index].iov_base = _state->buffer_addr + i;
70         int len = sizeof(test_pattern) - i;
71         if (size < len) {
72             len = size;
73         }
74         uiov[index].iov_len = len;
75     }
76 
77     /* Copy into user space. */
78     ASSERT_EQ(0,
79               copy_to_user(_state->iovec_addr, uiov,
80                            sizeof(struct iovec_user) * index),
81               "chunk %d", size);
82 
83     return index;
84 
85 test_abort:
86     return 0;
87 }
88 
89 /* A format string to help understand the exact case the assertion failed. */
90 #define LOCATION_MESSAGE "chunk size %zu / iov_cnt %d", buffer_chunk, iov_cnt
91 
92 /* Copy all the data from userspace using a buffer of limited size. */
iovectest_readback(iovectest_t * _state,size_t buffer_chunk,int iov_cnt,const void * expected,size_t expected_len)93 static void iovectest_readback(iovectest_t* _state,
94                                size_t buffer_chunk,
95                                int iov_cnt,
96                                const void* expected,
97                                size_t expected_len) {
98     uint8_t buf[sizeof(test_pattern) * 2];
99     memset(buf, 0xff, sizeof(buf));
100 
101     uint8_t tmp[sizeof(test_pattern) * 2];
102     memset(tmp, 0xff, sizeof(tmp));
103 
104     /* Check chunk sizes. */
105     ASSERT_LE(expected_len, sizeof(buf), LOCATION_MESSAGE);
106     ASSERT_LE(0, buffer_chunk, LOCATION_MESSAGE);
107     ASSERT_LE(buffer_chunk, sizeof(tmp), LOCATION_MESSAGE);
108 
109     /* Read the data a buffer at a time. */
110     struct iovec_iter iter = iovec_iter_create(iov_cnt);
111     size_t total_bytes = 0;
112     while (iovec_iter_has_next(&iter)) {
113         int ret = user_iovec_to_membuf_iter(tmp, buffer_chunk,
114                                             _state->iovec_addr, &iter);
115         /* Check the return value. */
116         ASSERT_LE(0, ret, LOCATION_MESSAGE);
117         ASSERT_LE(ret, buffer_chunk, LOCATION_MESSAGE);
118         /* If there is more data, the buffer should be filled. */
119         if (iter.iov_index < iter.iov_cnt) {
120             ASSERT_EQ(ret, buffer_chunk, LOCATION_MESSAGE);
121         }
122         /* Accumulate the result. */
123         memcpy(buf + total_bytes, tmp, ret);
124         total_bytes += ret;
125     }
126     /* Did we get the data we expect? */
127     ASSERT_EQ(expected_len, total_bytes, LOCATION_MESSAGE);
128     ASSERT_EQ(0, memcmp(expected, buf, expected_len), LOCATION_MESSAGE);
129 
130 test_abort:;
131 }
132 
133 /* Test various combinations of iovec size and read buffer size. */
TEST_F(iovectest,varied_chunk_sizes)134 TEST_F(iovectest, varied_chunk_sizes) {
135     /* Note the chunk sizes can exceed the size of the payload. */
136     for (size_t iovec_chunk = 1; iovec_chunk <= sizeof(test_pattern) + 2;
137          iovec_chunk++) {
138         for (size_t buffer_chunk = 1; buffer_chunk <= sizeof(test_pattern) + 2;
139              buffer_chunk++) {
140             int iov_cnt = write_chunked_iovec(_state, iovec_chunk);
141             iovectest_readback(_state, buffer_chunk, iov_cnt, test_pattern,
142                                sizeof(test_pattern));
143         }
144     }
145 }
146 
147 /* Make sure that zero-length iovecs have no effect. */
TEST_F(iovectest,zerolength)148 TEST_F(iovectest, zerolength) {
149     struct iovec_user uiov[] = {
150             {
151                     .iov_base = _state->buffer_addr + 0,
152                     .iov_len = 0,
153             },
154             {
155                     .iov_base = 0,
156                     .iov_len = 0,
157             },
158             {
159                     .iov_base = _state->buffer_addr + 0,
160                     .iov_len = 3,
161             },
162             {
163                     .iov_base = _state->buffer_addr + 3,
164                     .iov_len = 0,
165             },
166             {
167                     .iov_base = 0,
168                     .iov_len = 0,
169             },
170             {
171                     .iov_base = _state->buffer_addr + 3,
172                     .iov_len = 0,
173             },
174             {
175                     .iov_base = _state->buffer_addr + 3,
176                     .iov_len = 25,
177             },
178             {
179                     .iov_base = _state->buffer_addr + 28,
180                     .iov_len = 0,
181             },
182             {
183                     .iov_base = 0,
184                     .iov_len = 0,
185             },
186     };
187     ASSERT_EQ(0, copy_to_user(_state->iovec_addr, uiov, sizeof(uiov)));
188 
189     iovectest_readback(_state, 10, countof(uiov), test_pattern,
190                        sizeof(test_pattern));
191 
192 test_abort:;
193 }
194 
195 /* Make sure we can read something other than the exact test pattern. */
TEST_F(iovectest,swap_data)196 TEST_F(iovectest, swap_data) {
197     struct iovec_user uiov[] = {
198             {
199                     .iov_base = _state->buffer_addr + 14,
200                     .iov_len = 14,
201             },
202             {
203                     .iov_base = _state->buffer_addr + 0,
204                     .iov_len = 14,
205             },
206     };
207     ASSERT_EQ(0, copy_to_user(_state->iovec_addr, uiov, sizeof(uiov)));
208 
209     const char expected[] = "opqrstuvwxyz!\0abcdefghijklmn";
210     iovectest_readback(_state, 11, countof(uiov), expected, 28);
211 
212 test_abort:;
213 }
214 
215 PORT_TEST(iovectest, "com.android.kernel.iovectest");
216