1 /*
2 * Check decoding of name_to_handle_at and open_by_handle_at syscalls.
3 *
4 * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@strace.io>
5 * Copyright (c) 2016 Eugene Syromyatnikov <evgsyr@gmail.com>
6 * Copyright (c) 2015-2023 The strace developers.
7 * All rights reserved.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 #include "tests.h"
13 #include "scno.h"
14
15 #include <assert.h>
16 #include <errno.h>
17 #include <inttypes.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <unistd.h>
21
22 #include "secontext.h"
23
24 enum assert_rc {
25 ASSERT_NONE,
26 ASSERT_SUCCESS,
27 ASSERT_ERROR,
28 };
29
30 #ifndef MAX_HANDLE_SZ
31
32 # define MAX_HANDLE_SZ 128
33
34 struct file_handle {
35 unsigned int handle_bytes;
36 int handle_type;
37 unsigned char f_handle[0];
38 };
39 #endif /* !MAX_HANDLE_SZ */
40
41
42 static void
43 print_handle_data(unsigned char *bytes, unsigned int size)
44 {
45 unsigned int len = MIN(size, MAX_HANDLE_SZ);
46 print_quoted_hex(bytes, len);
47 if (size > len)
48 printf("...");
49 }
50
51 #ifndef TEST_SECONTEXT
52 static void
53 do_name_to_handle_at(kernel_ulong_t dirfd, const char *dirfd_str,
54 kernel_ulong_t pathname, const char *pathname_str,
55 kernel_ulong_t handle, const char *handle_str,
56 kernel_ulong_t mount_id,
57 kernel_ulong_t flags, const char *flags_str,
58 enum assert_rc assert_rc, long assert_errno)
59 {
60 long rc;
61 const char *errstr;
62
63 rc = syscall(__NR_name_to_handle_at, dirfd, pathname, handle, mount_id,
64 flags);
65 errstr = sprintrc(rc);
66
67 if (assert_rc != ASSERT_NONE)
68 assert(rc == (assert_rc == ASSERT_SUCCESS ? 0 : -1));
69 if (assert_errno)
70 assert(errno != assert_errno);
71
72 printf("name_to_handle_at(%s, %s, %s",
73 dirfd_str, pathname_str, handle_str);
74
75 if (rc != -1) {
76 struct file_handle *fh =
77 (struct file_handle *) (uintptr_t) handle;
78 int *mount_id_ptr = (int *) (uintptr_t) mount_id;
79
80 printf(" => %u, handle_type=%d, f_handle=",
81 fh->handle_bytes, fh->handle_type);
82 print_handle_data((unsigned char *) fh +
83 sizeof(struct file_handle),
84 fh->handle_bytes);
85 printf("}, [%d]", *mount_id_ptr);
86 } else {
87 if (mount_id)
88 printf(", %#llx", (unsigned long long) mount_id);
89 else
90 printf(", NULL");
91 }
92
93 printf(", %s) = %s\n", flags_str, errstr);
94 }
95
96 static void
97 do_open_by_handle_at(kernel_ulong_t mount_fd,
98 kernel_ulong_t handle, bool valid_handle, bool valid_data,
99 kernel_ulong_t flags, const char *flags_str)
100 {
101 long rc;
102
103 printf("open_by_handle_at(%d, ", (int) mount_fd);
104 if (valid_handle) {
105 struct file_handle *fh =
106 (struct file_handle *) (uintptr_t) handle;
107
108 printf("{handle_bytes=%u, handle_type=%d", fh->handle_bytes,
109 fh->handle_type);
110
111 printf(", f_handle=");
112 if (valid_data) {
113 print_handle_data((unsigned char *) fh +
114 sizeof(struct file_handle),
115 fh->handle_bytes);
116 } else {
117 printf("???");
118 }
119
120 printf("}");
121 } else {
122 if (handle)
123 printf("%#llx", (unsigned long long) handle);
124 else
125 printf("NULL");
126 }
127 printf(", %s) = ", flags_str);
128
129 rc = syscall(__NR_open_by_handle_at, mount_fd, handle, flags);
130
131 printf("%s\n", sprintrc(rc));
132 }
133 #endif /* !TEST_SECONTEXT */
134
135 struct strval {
136 kernel_ulong_t val;
137 const char *str;
138 };
139
140 #define STR16 "0123456789abcdef"
141 #define STR64 STR16 STR16 STR16 STR16
142
143 int
144 main(void)
145 {
146 char *my_secontext = SECONTEXT_PID_MY();
147 enum {
148 PATH1_SIZE = 64,
149 };
150
151 static const kernel_ulong_t fdcwd =
152 (kernel_ulong_t) 0x87654321ffffff9cULL;
153
154 struct file_handle *handle =
155 tail_alloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
156 struct file_handle *handle_0 =
157 tail_alloc(sizeof(struct file_handle) + 0);
158 struct file_handle *handle_8 =
159 tail_alloc(sizeof(struct file_handle) + 8);
160 struct file_handle *handle_128 =
161 tail_alloc(sizeof(struct file_handle) + 128);
162 struct file_handle *handle_256 =
163 tail_alloc(sizeof(struct file_handle) + 256);
164 TAIL_ALLOC_OBJECT_CONST_PTR(int, bogus_mount_id);
165
166 char handle_0_addr[sizeof("0x") + sizeof(void *) * 2];
167
168 const int flags = 0x400;
169 int mount_id;
170
171 handle_0->handle_bytes = 256;
172 handle_8->handle_bytes = 0;
173 handle_128->handle_bytes = 128;
174 handle_256->handle_bytes = 256;
175
176 fill_memory((char *) handle_128 + sizeof(struct file_handle), 128);
177 fill_memory((char *) handle_256 + sizeof(struct file_handle), 256);
178
179 snprintf(handle_0_addr, sizeof(handle_0_addr), "%p",
180 handle_0 + sizeof(struct file_handle));
181
182 handle->handle_bytes = 0;
183
184 char path[] = ".";
185 char *path_secontext = SECONTEXT_FILE(path);
186
187 assert(syscall(__NR_name_to_handle_at, fdcwd, path, handle, &mount_id,
188 flags | 1) == -1);
189 if (EINVAL != errno)
190 perror_msg_and_skip("name_to_handle_at");
191 printf("%s%s(AT_FDCWD, \"%s\"%s, {handle_bytes=0}, %p"
192 ", AT_SYMLINK_FOLLOW|0x1) = -1 EINVAL (%m)\n",
193 my_secontext, "name_to_handle_at",
194 path, path_secontext,
195 &mount_id);
196
197 assert(syscall(__NR_name_to_handle_at, fdcwd, path, handle, &mount_id,
198 flags) == -1);
199 if (EOVERFLOW != errno)
200 perror_msg_and_skip("name_to_handle_at");
201 printf("%s%s(AT_FDCWD, \"%s\"%s, {handle_bytes=0 => %u}"
202 ", %p, AT_SYMLINK_FOLLOW) = -1 EOVERFLOW (%m)\n",
203 my_secontext, "name_to_handle_at",
204 path, path_secontext,
205 handle->handle_bytes, &mount_id);
206
207 if (syscall(__NR_name_to_handle_at, fdcwd, path, handle, &mount_id,
208 flags)) {
209 if (EOVERFLOW == errno)
210 perror_msg_and_skip("name_to_handle_at");
211 else
212 perror_msg_and_fail("name_to_handle_at");
213 }
214 printf("%s%s(AT_FDCWD, \"%s\"%s, {handle_bytes=%u"
215 ", handle_type=%d, f_handle=",
216 my_secontext, "name_to_handle_at",
217 path, path_secontext,
218 handle->handle_bytes, handle->handle_type);
219 print_handle_data(handle->f_handle, handle->handle_bytes);
220 printf("}, [%d], AT_SYMLINK_FOLLOW) = 0\n", mount_id);
221
222 printf("%s%s(-1, {handle_bytes=%u, handle_type=%d, f_handle=",
223 my_secontext, "open_by_handle_at",
224 handle->handle_bytes, handle->handle_type);
225 print_handle_data(handle->f_handle, handle->handle_bytes);
226 int rc = syscall(__NR_open_by_handle_at, -1, handle,
227 O_RDONLY | O_DIRECTORY);
228 printf("}, O_RDONLY|O_DIRECTORY) = %d %s (%m)\n", rc, errno2name());
229
230 #ifndef TEST_SECONTEXT
231 static const struct strval dirfds[] = {
232 { (kernel_ulong_t) 0xdeadca57badda7a1ULL, "-1159878751" },
233 { (kernel_ulong_t) 0x12345678ffffff9cULL, "AT_FDCWD" },
234 };
235 static const struct strval name_flags[] = {
236 { (kernel_ulong_t) 0xdeadf15700000000ULL, "0" },
237 { (kernel_ulong_t) 0xbadc0ded00001000ULL,
238 "AT_EMPTY_PATH" },
239 { (kernel_ulong_t) 0xdeadc0deda7a1657ULL,
240 "AT_HANDLE_FID|AT_SYMLINK_FOLLOW|AT_EMPTY_PATH|0xda7a0057" },
241 { (kernel_ulong_t) 0xdefaced1ffffe9ffULL,
242 "0xffffe9ff /* AT_??? */" },
243 };
244 static const kernel_ulong_t mount_fds[] = {
245 (kernel_ulong_t) 0xdeadca5701234567ULL,
246 (kernel_ulong_t) 0x12345678ffffff9cULL,
247 };
248 static const struct strval open_flags[] = {
249 { F8ILL_KULONG_MASK, "O_RDONLY" },
250 { (kernel_ulong_t) 0xdeadbeef80000001ULL,
251 "O_WRONLY|0x80000000" }
252 };
253
254 static const char str64[] = STR64;
255 char *bogus_path1 = tail_memdup(str64, PATH1_SIZE);
256 char *bogus_path2 = tail_memdup(str64, sizeof(str64));
257 char bogus_path1_addr[sizeof("0x") + sizeof(void *) * 2];
258 char bogus_path1_after_addr[sizeof("0x") + sizeof(void *) * 2];
259
260 struct strval paths[] = {
261 { (kernel_ulong_t) 0, "NULL" },
262 { (kernel_ulong_t) (uintptr_t) (bogus_path1 + PATH1_SIZE),
263 bogus_path1_after_addr },
264 { (kernel_ulong_t) (uintptr_t) bogus_path1, bogus_path1_addr },
265 { (kernel_ulong_t) (uintptr_t) bogus_path2, "\"" STR64 "\"" },
266 };
267 struct strval name_handles[] = {
268 { (uintptr_t) (handle_0 + sizeof(struct file_handle)),
269 handle_0_addr },
270 { (uintptr_t) handle_0, "{handle_bytes=256}" },
271 { (uintptr_t) handle_8, "{handle_bytes=0}" },
272 { (uintptr_t) handle_128, "{handle_bytes=128}" },
273 { (uintptr_t) handle_256, "{handle_bytes=256}" },
274 };
275 struct {
276 kernel_ulong_t addr;
277 bool valid;
278 bool valid_data;
279 } open_handles[] = {
280 { 0, false, false },
281 { (uintptr_t) (handle_0 + sizeof(struct file_handle)),
282 false, false },
283 { (uintptr_t) handle_0 + 4, false, false },
284 { (uintptr_t) handle_0, true, false },
285 { (uintptr_t) handle_8, true, true },
286 { (uintptr_t) handle_128, true, true },
287 { (uintptr_t) handle_256, true, true },
288 };
289 kernel_ulong_t mount_ids[] = {
290 0,
291 (kernel_ulong_t) (uintptr_t) (bogus_mount_id + 1),
292 (kernel_ulong_t) (uintptr_t) bogus_mount_id,
293 };
294
295 snprintf(bogus_path1_addr, sizeof(bogus_path1_addr), "%p", bogus_path1);
296 snprintf(bogus_path1_after_addr, sizeof(bogus_path1_after_addr), "%p",
297 bogus_path1 + PATH1_SIZE);
298
299 for (unsigned int i = 0;
300 i < ARRAY_SIZE(dirfds); ++i) {
301 for (unsigned int j = 0;
302 j < ARRAY_SIZE(paths); ++j) {
303 for (unsigned int k = 0;
304 k < ARRAY_SIZE(name_handles); ++k) {
305 for (unsigned int l = 0;
306 l < ARRAY_SIZE(mount_ids); ++l) {
307 for (unsigned int m = 0;
308 m < ARRAY_SIZE(name_flags); ++m) {
309 do_name_to_handle_at(
310 dirfds[i].val,
311 dirfds[i].str,
312 paths[j].val,
313 paths[j].str,
314 name_handles[k].val,
315 name_handles[k].str,
316 mount_ids[l],
317 name_flags[m].val,
318 name_flags[m].str,
319 ASSERT_ERROR, 0);
320 }
321 }
322 }
323 }
324 }
325
326 for (unsigned int i = 0;
327 i < ARRAY_SIZE(mount_fds); ++i) {
328 for (unsigned int j = 0;
329 j < ARRAY_SIZE(open_handles); ++j) {
330 for (unsigned int k = 0;
331 k < ARRAY_SIZE(open_flags); ++k) {
332 do_open_by_handle_at(mount_fds[i],
333 open_handles[j].addr,
334 open_handles[j].valid,
335 open_handles[j].valid_data,
336 open_flags[k].val,
337 open_flags[k].str);
338 }
339 }
340 }
341 #endif
342
343 /*
344 * Tests with dirfd.
345 */
346
347 int cwd_fd = get_dir_fd(".");
348 char *cwd = get_fd_path(cwd_fd);
349 char *cwd_secontext = SECONTEXT_FILE(".");
350
351 assert(syscall(__NR_name_to_handle_at, cwd_fd, path, handle, &mount_id,
352 flags) == 0);
353 printf("%s%s(%d%s, \"%s\"%s, {handle_bytes=%u, handle_type=%d"
354 ", f_handle=",
355 my_secontext, "name_to_handle_at",
356 cwd_fd, cwd_secontext,
357 path, path_secontext,
358 handle->handle_bytes, handle->handle_type);
359 print_handle_data((unsigned char *) handle +
360 sizeof(struct file_handle),
361 handle->handle_bytes);
362 printf("}, [%d], AT_SYMLINK_FOLLOW) = 0\n", mount_id);
363
364 printf("%s%s(-1, {handle_bytes=%u, handle_type=%d, f_handle=",
365 my_secontext, "open_by_handle_at",
366 handle->handle_bytes, handle->handle_type);
367 print_handle_data((unsigned char *) handle +
368 sizeof(struct file_handle),
369 handle->handle_bytes);
370 rc = syscall(__NR_open_by_handle_at, -1, handle,
371 O_RDONLY | O_DIRECTORY);
372 printf("}, O_RDONLY|O_DIRECTORY) = %s\n", sprintrc(rc));
373
374 /* cwd_fd ignored when path is absolute */
375 if (chdir(".."))
376 perror_msg_and_fail("chdir");
377
378 assert(syscall(__NR_name_to_handle_at, cwd_fd, cwd, handle, &mount_id,
379 flags) == 0);
380 printf("%s%s(%d%s, \"%s\"%s, {handle_bytes=%u"
381 ", handle_type=%d, f_handle=",
382 my_secontext, "name_to_handle_at",
383 cwd_fd, cwd_secontext,
384 cwd, cwd_secontext,
385 handle->handle_bytes, handle->handle_type);
386 print_handle_data((unsigned char *) handle +
387 sizeof(struct file_handle),
388 handle->handle_bytes);
389 printf("}, [%d], AT_SYMLINK_FOLLOW) = 0\n", mount_id);
390
391 printf("%s%s(-1, {handle_bytes=%u, handle_type=%d, f_handle=",
392 my_secontext, "open_by_handle_at",
393 handle->handle_bytes, handle->handle_type);
394 print_handle_data((unsigned char *) handle +
395 sizeof(struct file_handle),
396 handle->handle_bytes);
397 rc = syscall(__NR_open_by_handle_at, -1, handle,
398 O_RDONLY | O_DIRECTORY);
399 printf("}, O_RDONLY|O_DIRECTORY) = %s\n", sprintrc(rc));
400
401 if (fchdir(cwd_fd))
402 perror_msg_and_fail("fchdir");
403
404 puts("+++ exited with 0 +++");
405 return 0;
406 }