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