xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/openat2/openat2_test.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Author: Aleksa Sarai <cyphar@cyphar.com>
4*4882a593Smuzhiyun  * Copyright (C) 2018-2019 SUSE LLC.
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #define _GNU_SOURCE
8*4882a593Smuzhiyun #include <fcntl.h>
9*4882a593Smuzhiyun #include <sched.h>
10*4882a593Smuzhiyun #include <sys/stat.h>
11*4882a593Smuzhiyun #include <sys/types.h>
12*4882a593Smuzhiyun #include <sys/mount.h>
13*4882a593Smuzhiyun #include <stdlib.h>
14*4882a593Smuzhiyun #include <stdbool.h>
15*4882a593Smuzhiyun #include <string.h>
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include "../kselftest.h"
18*4882a593Smuzhiyun #include "helpers.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun /*
21*4882a593Smuzhiyun  * O_LARGEFILE is set to 0 by glibc.
22*4882a593Smuzhiyun  * XXX: This is wrong on {mips, parisc, powerpc, sparc}.
23*4882a593Smuzhiyun  */
24*4882a593Smuzhiyun #undef	O_LARGEFILE
25*4882a593Smuzhiyun #define	O_LARGEFILE 0x8000
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun struct open_how_ext {
28*4882a593Smuzhiyun 	struct open_how inner;
29*4882a593Smuzhiyun 	uint32_t extra1;
30*4882a593Smuzhiyun 	char pad1[128];
31*4882a593Smuzhiyun 	uint32_t extra2;
32*4882a593Smuzhiyun 	char pad2[128];
33*4882a593Smuzhiyun 	uint32_t extra3;
34*4882a593Smuzhiyun };
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun struct struct_test {
37*4882a593Smuzhiyun 	const char *name;
38*4882a593Smuzhiyun 	struct open_how_ext arg;
39*4882a593Smuzhiyun 	size_t size;
40*4882a593Smuzhiyun 	int err;
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #define NUM_OPENAT2_STRUCT_TESTS 7
44*4882a593Smuzhiyun #define NUM_OPENAT2_STRUCT_VARIATIONS 13
45*4882a593Smuzhiyun 
test_openat2_struct(void)46*4882a593Smuzhiyun void test_openat2_struct(void)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun 	int misalignments[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 };
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	struct struct_test tests[] = {
51*4882a593Smuzhiyun 		/* Normal struct. */
52*4882a593Smuzhiyun 		{ .name = "normal struct",
53*4882a593Smuzhiyun 		  .arg.inner.flags = O_RDONLY,
54*4882a593Smuzhiyun 		  .size = sizeof(struct open_how) },
55*4882a593Smuzhiyun 		/* Bigger struct, with zeroed out end. */
56*4882a593Smuzhiyun 		{ .name = "bigger struct (zeroed out)",
57*4882a593Smuzhiyun 		  .arg.inner.flags = O_RDONLY,
58*4882a593Smuzhiyun 		  .size = sizeof(struct open_how_ext) },
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 		/* TODO: Once expanded, check zero-padding. */
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 		/* Smaller than version-0 struct. */
63*4882a593Smuzhiyun 		{ .name = "zero-sized 'struct'",
64*4882a593Smuzhiyun 		  .arg.inner.flags = O_RDONLY, .size = 0, .err = -EINVAL },
65*4882a593Smuzhiyun 		{ .name = "smaller-than-v0 struct",
66*4882a593Smuzhiyun 		  .arg.inner.flags = O_RDONLY,
67*4882a593Smuzhiyun 		  .size = OPEN_HOW_SIZE_VER0 - 1, .err = -EINVAL },
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 		/* Bigger struct, with non-zero trailing bytes. */
70*4882a593Smuzhiyun 		{ .name = "bigger struct (non-zero data in first 'future field')",
71*4882a593Smuzhiyun 		  .arg.inner.flags = O_RDONLY, .arg.extra1 = 0xdeadbeef,
72*4882a593Smuzhiyun 		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
73*4882a593Smuzhiyun 		{ .name = "bigger struct (non-zero data in middle of 'future fields')",
74*4882a593Smuzhiyun 		  .arg.inner.flags = O_RDONLY, .arg.extra2 = 0xfeedcafe,
75*4882a593Smuzhiyun 		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
76*4882a593Smuzhiyun 		{ .name = "bigger struct (non-zero data at end of 'future fields')",
77*4882a593Smuzhiyun 		  .arg.inner.flags = O_RDONLY, .arg.extra3 = 0xabad1dea,
78*4882a593Smuzhiyun 		  .size = sizeof(struct open_how_ext), .err = -E2BIG },
79*4882a593Smuzhiyun 	};
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	BUILD_BUG_ON(ARRAY_LEN(misalignments) != NUM_OPENAT2_STRUCT_VARIATIONS);
82*4882a593Smuzhiyun 	BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_STRUCT_TESTS);
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	for (int i = 0; i < ARRAY_LEN(tests); i++) {
85*4882a593Smuzhiyun 		struct struct_test *test = &tests[i];
86*4882a593Smuzhiyun 		struct open_how_ext how_ext = test->arg;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 		for (int j = 0; j < ARRAY_LEN(misalignments); j++) {
89*4882a593Smuzhiyun 			int fd, misalign = misalignments[j];
90*4882a593Smuzhiyun 			char *fdpath = NULL;
91*4882a593Smuzhiyun 			bool failed;
92*4882a593Smuzhiyun 			void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 			void *copy = NULL, *how_copy = &how_ext;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 			if (!openat2_supported) {
97*4882a593Smuzhiyun 				ksft_print_msg("openat2(2) unsupported\n");
98*4882a593Smuzhiyun 				resultfn = ksft_test_result_skip;
99*4882a593Smuzhiyun 				goto skip;
100*4882a593Smuzhiyun 			}
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 			if (misalign) {
103*4882a593Smuzhiyun 				/*
104*4882a593Smuzhiyun 				 * Explicitly misalign the structure copying it with the given
105*4882a593Smuzhiyun 				 * (mis)alignment offset. The other data is set to be non-zero to
106*4882a593Smuzhiyun 				 * make sure that non-zero bytes outside the struct aren't checked
107*4882a593Smuzhiyun 				 *
108*4882a593Smuzhiyun 				 * This is effectively to check that is_zeroed_user() works.
109*4882a593Smuzhiyun 				 */
110*4882a593Smuzhiyun 				copy = malloc(misalign + sizeof(how_ext));
111*4882a593Smuzhiyun 				how_copy = copy + misalign;
112*4882a593Smuzhiyun 				memset(copy, 0xff, misalign);
113*4882a593Smuzhiyun 				memcpy(how_copy, &how_ext, sizeof(how_ext));
114*4882a593Smuzhiyun 			}
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 			fd = raw_openat2(AT_FDCWD, ".", how_copy, test->size);
117*4882a593Smuzhiyun 			if (test->err >= 0)
118*4882a593Smuzhiyun 				failed = (fd < 0);
119*4882a593Smuzhiyun 			else
120*4882a593Smuzhiyun 				failed = (fd != test->err);
121*4882a593Smuzhiyun 			if (fd >= 0) {
122*4882a593Smuzhiyun 				fdpath = fdreadlink(fd);
123*4882a593Smuzhiyun 				close(fd);
124*4882a593Smuzhiyun 			}
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 			if (failed) {
127*4882a593Smuzhiyun 				resultfn = ksft_test_result_fail;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 				ksft_print_msg("openat2 unexpectedly returned ");
130*4882a593Smuzhiyun 				if (fdpath)
131*4882a593Smuzhiyun 					ksft_print_msg("%d['%s']\n", fd, fdpath);
132*4882a593Smuzhiyun 				else
133*4882a593Smuzhiyun 					ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
134*4882a593Smuzhiyun 			}
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun skip:
137*4882a593Smuzhiyun 			if (test->err >= 0)
138*4882a593Smuzhiyun 				resultfn("openat2 with %s argument [misalign=%d] succeeds\n",
139*4882a593Smuzhiyun 					 test->name, misalign);
140*4882a593Smuzhiyun 			else
141*4882a593Smuzhiyun 				resultfn("openat2 with %s argument [misalign=%d] fails with %d (%s)\n",
142*4882a593Smuzhiyun 					 test->name, misalign, test->err,
143*4882a593Smuzhiyun 					 strerror(-test->err));
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 			free(copy);
146*4882a593Smuzhiyun 			free(fdpath);
147*4882a593Smuzhiyun 			fflush(stdout);
148*4882a593Smuzhiyun 		}
149*4882a593Smuzhiyun 	}
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun struct flag_test {
153*4882a593Smuzhiyun 	const char *name;
154*4882a593Smuzhiyun 	struct open_how how;
155*4882a593Smuzhiyun 	int err;
156*4882a593Smuzhiyun };
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun #define NUM_OPENAT2_FLAG_TESTS 23
159*4882a593Smuzhiyun 
test_openat2_flags(void)160*4882a593Smuzhiyun void test_openat2_flags(void)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun 	struct flag_test tests[] = {
163*4882a593Smuzhiyun 		/* O_TMPFILE is incompatible with O_PATH and O_CREAT. */
164*4882a593Smuzhiyun 		{ .name = "incompatible flags (O_TMPFILE | O_PATH)",
165*4882a593Smuzhiyun 		  .how.flags = O_TMPFILE | O_PATH | O_RDWR, .err = -EINVAL },
166*4882a593Smuzhiyun 		{ .name = "incompatible flags (O_TMPFILE | O_CREAT)",
167*4882a593Smuzhiyun 		  .how.flags = O_TMPFILE | O_CREAT | O_RDWR, .err = -EINVAL },
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 		/* O_PATH only permits certain other flags to be set ... */
170*4882a593Smuzhiyun 		{ .name = "compatible flags (O_PATH | O_CLOEXEC)",
171*4882a593Smuzhiyun 		  .how.flags = O_PATH | O_CLOEXEC },
172*4882a593Smuzhiyun 		{ .name = "compatible flags (O_PATH | O_DIRECTORY)",
173*4882a593Smuzhiyun 		  .how.flags = O_PATH | O_DIRECTORY },
174*4882a593Smuzhiyun 		{ .name = "compatible flags (O_PATH | O_NOFOLLOW)",
175*4882a593Smuzhiyun 		  .how.flags = O_PATH | O_NOFOLLOW },
176*4882a593Smuzhiyun 		/* ... and others are absolutely not permitted. */
177*4882a593Smuzhiyun 		{ .name = "incompatible flags (O_PATH | O_RDWR)",
178*4882a593Smuzhiyun 		  .how.flags = O_PATH | O_RDWR, .err = -EINVAL },
179*4882a593Smuzhiyun 		{ .name = "incompatible flags (O_PATH | O_CREAT)",
180*4882a593Smuzhiyun 		  .how.flags = O_PATH | O_CREAT, .err = -EINVAL },
181*4882a593Smuzhiyun 		{ .name = "incompatible flags (O_PATH | O_EXCL)",
182*4882a593Smuzhiyun 		  .how.flags = O_PATH | O_EXCL, .err = -EINVAL },
183*4882a593Smuzhiyun 		{ .name = "incompatible flags (O_PATH | O_NOCTTY)",
184*4882a593Smuzhiyun 		  .how.flags = O_PATH | O_NOCTTY, .err = -EINVAL },
185*4882a593Smuzhiyun 		{ .name = "incompatible flags (O_PATH | O_DIRECT)",
186*4882a593Smuzhiyun 		  .how.flags = O_PATH | O_DIRECT, .err = -EINVAL },
187*4882a593Smuzhiyun 		{ .name = "incompatible flags (O_PATH | O_LARGEFILE)",
188*4882a593Smuzhiyun 		  .how.flags = O_PATH | O_LARGEFILE, .err = -EINVAL },
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 		/* ->mode must only be set with O_{CREAT,TMPFILE}. */
191*4882a593Smuzhiyun 		{ .name = "non-zero how.mode and O_RDONLY",
192*4882a593Smuzhiyun 		  .how.flags = O_RDONLY, .how.mode = 0600, .err = -EINVAL },
193*4882a593Smuzhiyun 		{ .name = "non-zero how.mode and O_PATH",
194*4882a593Smuzhiyun 		  .how.flags = O_PATH,   .how.mode = 0600, .err = -EINVAL },
195*4882a593Smuzhiyun 		{ .name = "valid how.mode and O_CREAT",
196*4882a593Smuzhiyun 		  .how.flags = O_CREAT,  .how.mode = 0600 },
197*4882a593Smuzhiyun 		{ .name = "valid how.mode and O_TMPFILE",
198*4882a593Smuzhiyun 		  .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0600 },
199*4882a593Smuzhiyun 		/* ->mode must only contain 0777 bits. */
200*4882a593Smuzhiyun 		{ .name = "invalid how.mode and O_CREAT",
201*4882a593Smuzhiyun 		  .how.flags = O_CREAT,
202*4882a593Smuzhiyun 		  .how.mode = 0xFFFF, .err = -EINVAL },
203*4882a593Smuzhiyun 		{ .name = "invalid (very large) how.mode and O_CREAT",
204*4882a593Smuzhiyun 		  .how.flags = O_CREAT,
205*4882a593Smuzhiyun 		  .how.mode = 0xC000000000000000ULL, .err = -EINVAL },
206*4882a593Smuzhiyun 		{ .name = "invalid how.mode and O_TMPFILE",
207*4882a593Smuzhiyun 		  .how.flags = O_TMPFILE | O_RDWR,
208*4882a593Smuzhiyun 		  .how.mode = 0x1337, .err = -EINVAL },
209*4882a593Smuzhiyun 		{ .name = "invalid (very large) how.mode and O_TMPFILE",
210*4882a593Smuzhiyun 		  .how.flags = O_TMPFILE | O_RDWR,
211*4882a593Smuzhiyun 		  .how.mode = 0x0000A00000000000ULL, .err = -EINVAL },
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 		/* ->resolve must only contain RESOLVE_* flags. */
214*4882a593Smuzhiyun 		{ .name = "invalid how.resolve and O_RDONLY",
215*4882a593Smuzhiyun 		  .how.flags = O_RDONLY,
216*4882a593Smuzhiyun 		  .how.resolve = 0x1337, .err = -EINVAL },
217*4882a593Smuzhiyun 		{ .name = "invalid how.resolve and O_CREAT",
218*4882a593Smuzhiyun 		  .how.flags = O_CREAT,
219*4882a593Smuzhiyun 		  .how.resolve = 0x1337, .err = -EINVAL },
220*4882a593Smuzhiyun 		{ .name = "invalid how.resolve and O_TMPFILE",
221*4882a593Smuzhiyun 		  .how.flags = O_TMPFILE | O_RDWR,
222*4882a593Smuzhiyun 		  .how.resolve = 0x1337, .err = -EINVAL },
223*4882a593Smuzhiyun 		{ .name = "invalid how.resolve and O_PATH",
224*4882a593Smuzhiyun 		  .how.flags = O_PATH,
225*4882a593Smuzhiyun 		  .how.resolve = 0x1337, .err = -EINVAL },
226*4882a593Smuzhiyun 	};
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_FLAG_TESTS);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	for (int i = 0; i < ARRAY_LEN(tests); i++) {
231*4882a593Smuzhiyun 		int fd, fdflags = -1;
232*4882a593Smuzhiyun 		char *path, *fdpath = NULL;
233*4882a593Smuzhiyun 		bool failed = false;
234*4882a593Smuzhiyun 		struct flag_test *test = &tests[i];
235*4882a593Smuzhiyun 		void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 		if (!openat2_supported) {
238*4882a593Smuzhiyun 			ksft_print_msg("openat2(2) unsupported\n");
239*4882a593Smuzhiyun 			resultfn = ksft_test_result_skip;
240*4882a593Smuzhiyun 			goto skip;
241*4882a593Smuzhiyun 		}
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 		path = (test->how.flags & O_CREAT) ? "/tmp/ksft.openat2_tmpfile" : ".";
244*4882a593Smuzhiyun 		unlink(path);
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 		fd = sys_openat2(AT_FDCWD, path, &test->how);
247*4882a593Smuzhiyun 		if (fd < 0 && fd == -EOPNOTSUPP) {
248*4882a593Smuzhiyun 			/*
249*4882a593Smuzhiyun 			 * Skip the testcase if it failed because not supported
250*4882a593Smuzhiyun 			 * by FS. (e.g. a valid O_TMPFILE combination on NFS)
251*4882a593Smuzhiyun 			 */
252*4882a593Smuzhiyun 			ksft_test_result_skip("openat2 with %s fails with %d (%s)\n",
253*4882a593Smuzhiyun 					      test->name, fd, strerror(-fd));
254*4882a593Smuzhiyun 			goto next;
255*4882a593Smuzhiyun 		}
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 		if (test->err >= 0)
258*4882a593Smuzhiyun 			failed = (fd < 0);
259*4882a593Smuzhiyun 		else
260*4882a593Smuzhiyun 			failed = (fd != test->err);
261*4882a593Smuzhiyun 		if (fd >= 0) {
262*4882a593Smuzhiyun 			int otherflags;
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 			fdpath = fdreadlink(fd);
265*4882a593Smuzhiyun 			fdflags = fcntl(fd, F_GETFL);
266*4882a593Smuzhiyun 			otherflags = fcntl(fd, F_GETFD);
267*4882a593Smuzhiyun 			close(fd);
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 			E_assert(fdflags >= 0, "fcntl F_GETFL of new fd");
270*4882a593Smuzhiyun 			E_assert(otherflags >= 0, "fcntl F_GETFD of new fd");
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 			/* O_CLOEXEC isn't shown in F_GETFL. */
273*4882a593Smuzhiyun 			if (otherflags & FD_CLOEXEC)
274*4882a593Smuzhiyun 				fdflags |= O_CLOEXEC;
275*4882a593Smuzhiyun 			/* O_CREAT is hidden from F_GETFL. */
276*4882a593Smuzhiyun 			if (test->how.flags & O_CREAT)
277*4882a593Smuzhiyun 				fdflags |= O_CREAT;
278*4882a593Smuzhiyun 			if (!(test->how.flags & O_LARGEFILE))
279*4882a593Smuzhiyun 				fdflags &= ~O_LARGEFILE;
280*4882a593Smuzhiyun 			failed |= (fdflags != test->how.flags);
281*4882a593Smuzhiyun 		}
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 		if (failed) {
284*4882a593Smuzhiyun 			resultfn = ksft_test_result_fail;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 			ksft_print_msg("openat2 unexpectedly returned ");
287*4882a593Smuzhiyun 			if (fdpath)
288*4882a593Smuzhiyun 				ksft_print_msg("%d['%s'] with %X (!= %X)\n",
289*4882a593Smuzhiyun 					       fd, fdpath, fdflags,
290*4882a593Smuzhiyun 					       test->how.flags);
291*4882a593Smuzhiyun 			else
292*4882a593Smuzhiyun 				ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
293*4882a593Smuzhiyun 		}
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun skip:
296*4882a593Smuzhiyun 		if (test->err >= 0)
297*4882a593Smuzhiyun 			resultfn("openat2 with %s succeeds\n", test->name);
298*4882a593Smuzhiyun 		else
299*4882a593Smuzhiyun 			resultfn("openat2 with %s fails with %d (%s)\n",
300*4882a593Smuzhiyun 				 test->name, test->err, strerror(-test->err));
301*4882a593Smuzhiyun next:
302*4882a593Smuzhiyun 		free(fdpath);
303*4882a593Smuzhiyun 		fflush(stdout);
304*4882a593Smuzhiyun 	}
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun #define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
308*4882a593Smuzhiyun 		   NUM_OPENAT2_FLAG_TESTS)
309*4882a593Smuzhiyun 
main(int argc,char ** argv)310*4882a593Smuzhiyun int main(int argc, char **argv)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun 	ksft_print_header();
313*4882a593Smuzhiyun 	ksft_set_plan(NUM_TESTS);
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	test_openat2_struct();
316*4882a593Smuzhiyun 	test_openat2_flags();
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
319*4882a593Smuzhiyun 		ksft_exit_fail();
320*4882a593Smuzhiyun 	else
321*4882a593Smuzhiyun 		ksft_exit_pass();
322*4882a593Smuzhiyun }
323