1 /*
2 * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@strace.io>
3 * Copyright (c) 2015-2021 The strace developers.
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include <stdio.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <assert.h>
15 #include <linux/fcntl.h>
16 #include "pidns.h"
17 #include "scno.h"
18
19 #define FILE_LEN 4096
20
21 #define TEST_FLOCK_EINVAL(cmd) test_flock_einval(cmd, #cmd)
22 #define TEST_FLOCK64_EINVAL(cmd) test_flock64_einval(cmd, #cmd)
23
24 #ifndef NEED_TEST_FLOCK64_EINVAL
25 # if defined F_OFD_GETLK && defined F_OFD_SETLK && defined F_OFD_SETLKW
26 # define NEED_TEST_FLOCK64_EINVAL
27 # endif
28 #endif
29
30 #ifdef HAVE_TYPEOF
31 # define TYPEOF_FLOCK_OFF_T typeof(((struct flock *) NULL)->l_len)
32 #else
33 # define TYPEOF_FLOCK_OFF_T off_t
34 #endif
35
36 static const char *errstr;
37
38 static long
39 invoke_test_syscall(const unsigned int fd, const unsigned int cmd, void *const p)
40 {
41 const kernel_ulong_t kfd = F8ILL_KULONG_MASK | fd;
42 const kernel_ulong_t op = F8ILL_KULONG_MASK | cmd;
43
44 long rc = syscall(TEST_SYSCALL_NR, kfd, op, (uintptr_t) p);
45 errstr = sprintrc(rc);
46 return rc;
47 }
48
49 static void
50 test_flock_einval(const int cmd, const char *name)
51 {
52 TAIL_ALLOC_OBJECT_CONST_PTR(struct flock, fl);
53 memset(fl, 0, sizeof(*fl));
54 fl->l_type = F_RDLCK;
55 fl->l_start = (TYPEOF_FLOCK_OFF_T) 0xdefaced1facefeedULL;
56 fl->l_len = (TYPEOF_FLOCK_OFF_T) 0xdefaced2cafef00dULL;
57
58 invoke_test_syscall(0, cmd, fl);
59 pidns_print_leader();
60 printf("%s(0, %s, {l_type=F_RDLCK, l_whence=SEEK_SET"
61 ", l_start=%jd, l_len=%jd}) = %s\n", TEST_SYSCALL_STR, name,
62 (intmax_t) fl->l_start, (intmax_t) fl->l_len, errstr);
63
64 void *const bad_addr = (void *) fl + 1;
65 invoke_test_syscall(0, cmd, bad_addr);
66 pidns_print_leader();
67 printf("%s(0, %s, %p) = %s\n",
68 TEST_SYSCALL_STR, name, bad_addr, errstr);
69 }
70
71 #ifdef NEED_TEST_FLOCK64_EINVAL
72 static void
73 test_flock64_einval(const int cmd, const char *name)
74 {
75 TAIL_ALLOC_OBJECT_CONST_PTR(struct flock64, fl);
76 memset(fl, 0, sizeof(*fl));
77 fl->l_type = F_RDLCK;
78 fl->l_start = (TYPEOF_FLOCK_OFF_T) 0xdefaced1facefeedULL;
79 fl->l_len = (TYPEOF_FLOCK_OFF_T) 0xdefaced2cafef00dULL;
80
81 invoke_test_syscall(0, cmd, fl);
82 pidns_print_leader();
83 printf("%s(0, %s, {l_type=F_RDLCK, l_whence=SEEK_SET"
84 ", l_start=%jd, l_len=%jd}) = %s\n", TEST_SYSCALL_STR, name,
85 (intmax_t) fl->l_start, (intmax_t) fl->l_len, errstr);
86
87 void *const bad_addr = (void *) fl + 1;
88 invoke_test_syscall(0, cmd, bad_addr);
89 pidns_print_leader();
90 printf("%s(0, %s, %p) = %s\n",
91 TEST_SYSCALL_STR, name, bad_addr, errstr);
92 }
93 #endif /* NEED_TEST_FLOCK64_EINVAL */
94
95 static void
96 test_flock(void)
97 {
98 TEST_FLOCK_EINVAL(F_SETLK);
99 TEST_FLOCK_EINVAL(F_SETLKW);
100
101 TAIL_ALLOC_OBJECT_CONST_PTR(struct flock, fl);
102 memset(fl, 0, sizeof(*fl));
103 fl->l_type = F_RDLCK;
104 fl->l_len = FILE_LEN;
105
106 long rc = invoke_test_syscall(0, F_SETLK, fl);
107 pidns_print_leader();
108 printf("%s(0, F_SETLK, {l_type=F_RDLCK, l_whence=SEEK_SET"
109 ", l_start=0, l_len=%d}) = %s\n",
110 TEST_SYSCALL_STR, FILE_LEN, errstr);
111 if (rc)
112 return;
113
114 invoke_test_syscall(0, F_GETLK, fl);
115 pidns_print_leader();
116 printf("%s(0, F_GETLK, {l_type=F_UNLCK, l_whence=SEEK_SET"
117 ", l_start=0, l_len=%d, l_pid=0}) = 0\n",
118 TEST_SYSCALL_STR, FILE_LEN);
119
120 invoke_test_syscall(0, F_SETLKW, fl);
121 pidns_print_leader();
122 printf("%s(0, F_SETLKW, {l_type=F_UNLCK, l_whence=SEEK_SET"
123 ", l_start=0, l_len=%d}) = 0\n",
124 TEST_SYSCALL_STR, FILE_LEN);
125 }
126
127 static void
128 test_flock64_ofd(void)
129 {
130 #if defined F_OFD_GETLK && defined F_OFD_SETLK && defined F_OFD_SETLKW
131 TEST_FLOCK64_EINVAL(F_OFD_SETLK);
132 TEST_FLOCK64_EINVAL(F_OFD_SETLKW);
133
134 TAIL_ALLOC_OBJECT_CONST_PTR(struct flock64, fl);
135 memset(fl, 0, sizeof(*fl));
136 fl->l_type = F_RDLCK;
137 fl->l_len = FILE_LEN;
138
139 long rc = invoke_test_syscall(0, F_OFD_SETLK, fl);
140 pidns_print_leader();
141 printf("%s(0, F_OFD_SETLK, {l_type=F_RDLCK, l_whence=SEEK_SET"
142 ", l_start=0, l_len=%d}) = %s\n",
143 TEST_SYSCALL_STR, FILE_LEN, errstr);
144 if (rc)
145 return;
146
147 invoke_test_syscall(0, F_OFD_GETLK, fl);
148 pidns_print_leader();
149 printf("%s(0, F_OFD_GETLK, {l_type=F_UNLCK, l_whence=SEEK_SET"
150 ", l_start=0, l_len=%d, l_pid=0}) = 0\n",
151 TEST_SYSCALL_STR, FILE_LEN);
152
153 invoke_test_syscall(0, F_OFD_SETLKW, fl);
154 pidns_print_leader();
155 printf("%s(0, F_OFD_SETLKW, {l_type=F_UNLCK, l_whence=SEEK_SET"
156 ", l_start=0, l_len=%d}) = 0\n",
157 TEST_SYSCALL_STR, FILE_LEN);
158 #endif /* F_OFD_GETLK && F_OFD_SETLK && F_OFD_SETLKW */
159 }
160
161 static void test_flock64_lk64(void);
162
163 static void
164 test_flock64(void)
165 {
166 test_flock64_ofd();
167 test_flock64_lk64();
168 }
169
170 static long
171 test_f_owner_ex_type_pid(const int cmd, const char *const cmd_name,
172 const int type, const char *const type_name,
173 enum pid_type pid_type, pid_t pid)
174 {
175 TAIL_ALLOC_OBJECT_CONST_PTR(struct f_owner_ex, fo);
176
177 fo->type = type;
178 fo->pid = pid;
179 long rc = invoke_test_syscall(0, cmd, fo);
180 pidns_print_leader();
181 printf("%s(0, %s, {type=%s, pid=%d%s}) = %s\n",
182 TEST_SYSCALL_STR, cmd_name, type_name,
183 fo->pid, pidns_pid2str(pid_type), errstr);
184
185 void *bad_addr = (void *) fo + 1;
186 invoke_test_syscall(0, cmd, bad_addr);
187 pidns_print_leader();
188 printf("%s(0, %s, %p) = %s\n",
189 TEST_SYSCALL_STR, cmd_name, bad_addr, errstr);
190
191 return rc;
192 }
193
194 static void
195 test_f_owner_ex_umove_or_printaddr(const int type, const char *const type_name,
196 enum pid_type pid_type, pid_t pid)
197 {
198 long rc = test_f_owner_ex_type_pid(ARG_STR(F_SETOWN_EX),
199 type, type_name, pid_type, pid);
200 if (!rc)
201 test_f_owner_ex_type_pid(ARG_STR(F_GETOWN_EX),
202 type, type_name, pid_type, pid);
203 }
204
205 static void
206 test_f_owner_ex(void)
207 {
208 struct {
209 int type;
210 const char *type_name;
211 enum pid_type pid_type;
212 pid_t pid;
213 } a[] = {
214 { ARG_STR(F_OWNER_TID), PT_NONE, 1234567890 },
215 { ARG_STR(F_OWNER_PID), PT_NONE, 1234567890 },
216 { ARG_STR(F_OWNER_PGRP), PT_NONE, 1234567890 },
217 { ARG_STR(F_OWNER_TID), PT_TID, syscall(__NR_gettid) },
218 { ARG_STR(F_OWNER_PID), PT_TGID, getpid() },
219 { ARG_STR(F_OWNER_PGRP), PT_PGID, getpgid(0) },
220 };
221
222 for (unsigned int i = 0; i < ARRAY_SIZE(a); i++)
223 test_f_owner_ex_umove_or_printaddr(a[i].type, a[i].type_name,
224 a[i].pid_type, a[i].pid);
225 }
226 struct fcntl_cmd_check {
227 int fd;
228 int cmd;
229 const char *cmd_str;
230 long arg;
231 const char *arg_str;
232 void (*print_flags)(long rc);
233 };
234
235 static void
236 test_xetown(void)
237 {
238 const int pid = getpid();
239 const char *pid_str = pidns_pid2str(PT_TGID);
240
241 invoke_test_syscall(0, F_SETOWN, (void *) (intptr_t) pid);
242 pidns_print_leader();
243 printf("%s(0, F_SETOWN, %d%s) = %s\n",
244 TEST_SYSCALL_STR, pid, pid_str, errstr);
245
246 invoke_test_syscall(0, F_GETOWN, NULL);
247 pidns_print_leader();
248 printf("%s(0, F_GETOWN) = %d%s\n",
249 TEST_SYSCALL_STR, pid, pid_str);
250 }
251
252 static void
253 print_retval_flags(const struct fcntl_cmd_check *check, long rc)
254 {
255 if (check->print_flags) {
256 check->print_flags(rc);
257 } else {
258 printf("%s", errstr);
259 }
260 printf("\n");
261 }
262
263 static void
264 test_other_set_cmd(const struct fcntl_cmd_check *check)
265 {
266 invoke_test_syscall(check->fd, check->cmd, (void *) check->arg);
267 pidns_print_leader();
268 printf("%s(%d, %s, %s) = %s\n",
269 TEST_SYSCALL_STR, check->fd,
270 check->cmd_str, check->arg_str, errstr);
271
272 /* bad file fd */
273 invoke_test_syscall(-1, check->cmd, (void *) check->arg);
274 pidns_print_leader();
275 printf("%s(-1, %s, %s) = %s\n",
276 TEST_SYSCALL_STR, check->cmd_str,
277 check->arg_str, errstr);
278 }
279
280 static void
281 test_other_get_cmd(const struct fcntl_cmd_check *check)
282 {
283 long rc = invoke_test_syscall(check->fd, check->cmd, NULL);
284 pidns_print_leader();
285 printf("%s(%d, %s) = ",
286 TEST_SYSCALL_STR, check->fd, check->cmd_str);
287 print_retval_flags(check, rc);
288
289 /* bad file fd */
290 invoke_test_syscall(-1, check->cmd, NULL);
291 pidns_print_leader();
292 printf("%s(-1, %s) = %s\n",
293 TEST_SYSCALL_STR, check->cmd_str, errstr);
294 }
295
296 static void
297 print_flags_getfd(long rc)
298 {
299 assert(rc >= 0);
300 printf("%#lx%s", rc, rc & 1 ? " (flags FD_CLOEXEC)" : "");
301 }
302
303 static void
304 print_flags_getsig(long rc)
305 {
306 assert(rc >= 0);
307
308 if (!rc) {
309 printf("%ld", rc);
310 } else {
311 printf("%ld (%s)", rc, signal2name((int) rc));
312 }
313 }
314
315 static void
316 print_flags_getlease(long rc)
317 {
318 assert(rc >= 0);
319 const char *text;
320
321 switch (rc) {
322 case F_RDLCK:
323 text = "F_RDLCK";
324 break;
325 case F_WRLCK:
326 text = "F_WRLCK";
327 break;
328 case F_UNLCK:
329 text = "F_UNLCK";
330 break;
331 default:
332 error_msg_and_fail("fcntl returned %#lx, does the"
333 " test have to be updated?", rc);
334 }
335 printf("%#lx (%s)", rc, text);
336 }
337
338 static void
339 test_fcntl_others(void)
340 {
341 static const struct fcntl_cmd_check set_checks[] = {
342 { 0, ARG_STR(F_SETFD), ARG_STR(FD_CLOEXEC) },
343 { 0, ARG_STR(F_SETPIPE_SZ), ARG_STR(4097) },
344 { 0, ARG_STR(F_DUPFD), ARG_STR(0) },
345 { 0, ARG_STR(F_DUPFD_CLOEXEC), ARG_STR(0) },
346 { 0, ARG_STR(F_SETFL), ARG_STR(O_RDWR|O_LARGEFILE) },
347 { 0, ARG_STR(F_NOTIFY), ARG_STR(DN_ACCESS) },
348 { 1, ARG_STR(F_SETLEASE), ARG_STR(F_RDLCK) },
349 { 0, ARG_STR(F_SETSIG), 0, "0" },
350 { 1, ARG_STR(F_SETSIG), 1, "SIGHUP" }
351 };
352 for (unsigned int i = 0; i < ARRAY_SIZE(set_checks); i++) {
353 test_other_set_cmd(set_checks + i);
354 }
355
356 static const struct fcntl_cmd_check get_checks[] = {
357 { 0, ARG_STR(F_GETFD), .print_flags = print_flags_getfd },
358 { 1, ARG_STR(F_GETFD), .print_flags = print_flags_getfd },
359 { 0, ARG_STR(F_GETPIPE_SZ) },
360 { 1, ARG_STR(F_GETLEASE), .print_flags = print_flags_getlease },
361 { 0, ARG_STR(F_GETSIG), .print_flags = print_flags_getsig },
362 { 1, ARG_STR(F_GETSIG), .print_flags = print_flags_getsig }
363 };
364 for (unsigned int j = 0; j < ARRAY_SIZE(get_checks); j++) {
365 test_other_get_cmd(get_checks + j);
366 }
367 }
368
369 static void
370 create_sample(void)
371 {
372 (void) close(0);
373 if (ftruncate(create_tmpfile(O_RDWR), FILE_LEN))
374 perror_msg_and_fail("ftruncate");
375 }
376
377 int
378 main(void)
379 {
380 PIDNS_TEST_INIT;
381
382 create_sample();
383 test_flock();
384 test_flock64();
385 test_f_owner_ex();
386 test_fcntl_others();
387 test_xetown();
388
389 pidns_print_leader();
390 puts("+++ exited with 0 +++");
391 return 0;
392 }