xref: /xnu-12377.41.6/tests/vfs/resolve_beneath.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1 /*
2  * Copyright (c) 2024 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 /* compile: xcrun -sdk macosx.internal clang -ldarwintest -o resolve_beneath resolve_beneath.c -g -Weverything */
30 
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <sys/mount.h>
35 #include <sys/attr.h>
36 #include <sys/stat.h>
37 #include <sys/param.h>
38 #include <sys/xattr.h>
39 #include <sys/clonefile.h>
40 #include <pthread.h>
41 #include <time.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 
45 #include <darwintest.h>
46 #include <darwintest/utils.h>
47 
48 static char template[MAXPATHLEN];
49 static char *testdir = NULL;
50 static int testdir_fd = -1, test_fd = -1;
51 
52 #ifndef ENOTCAPABLE
53 #define ENOTCAPABLE             107
54 #endif
55 
56 #ifndef O_RESOLVE_BENEATH
57 #define O_RESOLVE_BENEATH       0x1000
58 #endif
59 
60 #ifndef AT_RESOLVE_BENEATH
61 #define AT_RESOLVE_BENEATH      0x2000
62 #endif
63 
64 #ifndef XATTR_RESOLVE_BENEATH
65 #define XATTR_RESOLVE_BENEATH   0x0080
66 #endif
67 
68 #ifndef CLONE_RESOLVE_BENEATH
69 #define CLONE_RESOLVE_BENEATH   0x0010
70 #endif
71 
72 #ifndef RENAME_RESOLVE_BENEATH
73 #define RENAME_RESOLVE_BENEATH  0x0020
74 #endif
75 
76 #ifndef FSOPT_RESOLVE_BENEATH
77 #define FSOPT_RESOLVE_BENEATH   0x1000
78 #endif
79 
80 #define TEST_DIR "test_dir"
81 #define NESTED_DIR "test_dir/nested"
82 #define OUTSIDE_FILE "outside_file.txt"
83 #define INSIDE_FILE "test_dir/inside_file.txt"
84 #define NESTED_FILE "test_dir/nested/nested_file.txt"
85 #define SYMLINK "test_dir/symlink"
86 #define SYMLINK_TO_NESTED "test_dir/symlink_to_nested"
87 #define PARENT_SYMLINK "test_dir/parent_symlink"
88 #define CIRCULAR_SYMLINK "test_dir/circular_symlink"
89 #define SYMLINK_ABSOLUTE "test_dir/symlink_absolute"
90 
91 #define SYMLINK_FROM "../outside_file.txt"
92 #define SYMLINK_TO_NESTED_FROM "nested/nested_file.txt"
93 #define PARENT_SYMLINK_FROM ".."
94 #define CIRCULAR_SYMLINK_FROM "circular_symlink"
95 
96 T_GLOBAL_META(
97 	T_META_NAMESPACE("xnu.vfs"),
98 	T_META_RADAR_COMPONENT_NAME("xnu"),
99 	T_META_RADAR_COMPONENT_VERSION("vfs"),
100 	T_META_ASROOT(false),
101 	T_META_CHECK_LEAKS(false));
102 
103 static void
setup(const char * dirname)104 setup(const char *dirname)
105 {
106 	int fd;
107 
108 	testdir_fd = test_fd = -1;
109 
110 	/* Create test root directory */
111 	snprintf(template, sizeof(template), "%s/%s-XXXXXX", dt_tmpdir(), dirname);
112 	T_ASSERT_POSIX_NOTNULL((testdir = mkdtemp(template)), "Creating test root directory");
113 	T_ASSERT_POSIX_SUCCESS((testdir_fd = open(testdir, O_SEARCH, 0777)), "Opening test root directory %s", testdir);
114 
115 	/* Create test directories */
116 	T_ASSERT_POSIX_SUCCESS(mkdirat(testdir_fd, TEST_DIR, 0777), "Creating %s/%s", testdir, TEST_DIR);
117 	T_ASSERT_POSIX_SUCCESS((test_fd = openat(testdir_fd, TEST_DIR, O_SEARCH, 0777)), "Opening test directory %s/%s", testdir, TEST_DIR);
118 	T_ASSERT_POSIX_SUCCESS(mkdirat(testdir_fd, NESTED_DIR, 0777), "Creating %s/%s", testdir, NESTED_DIR);
119 
120 	/* Create test files */
121 	T_ASSERT_POSIX_SUCCESS((fd = openat(testdir_fd, OUTSIDE_FILE, O_CREAT | O_RDWR, 0777)), "Creating file %s/%s", testdir, OUTSIDE_FILE);
122 	T_ASSERT_POSIX_SUCCESS(close(fd), "Closing %s", OUTSIDE_FILE);
123 
124 	T_ASSERT_POSIX_SUCCESS((fd = openat(testdir_fd, INSIDE_FILE, O_CREAT | O_RDWR, 0777)), "Creating file %s/%s", testdir, INSIDE_FILE);
125 	T_ASSERT_POSIX_SUCCESS(close(fd), "Closing %s", INSIDE_FILE);
126 
127 	T_ASSERT_POSIX_SUCCESS((fd = openat(testdir_fd, NESTED_FILE, O_CREAT | O_RDWR, 0777)), "Creating file %s/%s", testdir, NESTED_FILE);
128 	T_ASSERT_POSIX_SUCCESS(close(fd), "Closing %s", NESTED_FILE);
129 
130 	/* Create test symlinks */
131 	T_ASSERT_POSIX_SUCCESS(symlinkat(SYMLINK_FROM, testdir_fd, SYMLINK), "Creating symlink %s/%s -> %s", testdir, SYMLINK, SYMLINK_FROM);
132 	T_ASSERT_POSIX_SUCCESS(symlinkat(SYMLINK_TO_NESTED_FROM, testdir_fd, SYMLINK_TO_NESTED), "Creating symlink %s/%s -> %s", testdir, SYMLINK_TO_NESTED, SYMLINK_TO_NESTED_FROM);
133 	T_ASSERT_POSIX_SUCCESS(symlinkat(PARENT_SYMLINK_FROM, testdir_fd, PARENT_SYMLINK), "Creating symlink %s/%s -> %s", testdir, PARENT_SYMLINK, PARENT_SYMLINK_FROM);
134 	T_ASSERT_POSIX_SUCCESS(symlinkat(CIRCULAR_SYMLINK_FROM, testdir_fd, CIRCULAR_SYMLINK), "Creating symlink %s/%s -> %s", testdir, CIRCULAR_SYMLINK, CIRCULAR_SYMLINK_FROM);
135 	T_ASSERT_POSIX_SUCCESS(symlinkat(testdir, testdir_fd, SYMLINK_ABSOLUTE), "Creating symlink %s/%s -> %s", testdir, SYMLINK_ABSOLUTE, testdir);
136 }
137 
138 static void
cleanup(void)139 cleanup(void)
140 {
141 	if (test_fd != -1) {
142 		close(test_fd);
143 	}
144 	if (testdir_fd != -1) {
145 		unlinkat(testdir_fd, SYMLINK_ABSOLUTE, 0);
146 		unlinkat(testdir_fd, CIRCULAR_SYMLINK, 0);
147 		unlinkat(testdir_fd, PARENT_SYMLINK, 0);
148 		unlinkat(testdir_fd, SYMLINK_TO_NESTED, 0);
149 		unlinkat(testdir_fd, SYMLINK, 0);
150 		unlinkat(testdir_fd, NESTED_FILE, 0);
151 		unlinkat(testdir_fd, NESTED_DIR, AT_REMOVEDIR);
152 		unlinkat(testdir_fd, INSIDE_FILE, 0);
153 		unlinkat(testdir_fd, TEST_DIR, AT_REMOVEDIR);
154 		unlinkat(testdir_fd, OUTSIDE_FILE, 0);
155 
156 		close(testdir_fd);
157 		rmdir(testdir);
158 	}
159 }
160 
161 T_DECL(resolve_beneath_open,
162     "test open()/openat() using the O_RESOLVE_BENEATH flag")
163 {
164 	int fd, root_fd;
165 	char path[MAXPATHLEN];
166 
167 	T_SETUPBEGIN;
168 
169 	T_ATEND(cleanup);
170 	setup("resolve_beneath_open");
171 
172 	T_ASSERT_POSIX_SUCCESS((root_fd = open("/", O_SEARCH, 0777)), "Opening the root directory");
173 
174 	T_SETUPEND;
175 
176 	T_LOG("Testing the openat() syscall using O_RESOLVE_BENEATH");
177 
178 	/* Test Case 1: File within the directory */
179 	T_EXPECT_POSIX_SUCCESS((fd = openat(test_fd, "inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 1: File within the directory");
180 	if (fd >= 0) {
181 		close(fd);
182 	}
183 
184 	/* Test Case 2: File using a symlink pointing outside */
185 	T_EXPECT_POSIX_FAILURE(openat(test_fd, "symlink", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
186 
187 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
188 	T_EXPECT_POSIX_FAILURE(openat(test_fd, "../outside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
189 
190 	/* Test Case 4: File within a nested directory */
191 	T_EXPECT_POSIX_SUCCESS((fd = openat(test_fd, "nested/nested_file.txt", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 4: File within a nested directory");
192 	if (fd >= 0) {
193 		close(fd);
194 	}
195 
196 	/* Test Case 5: Symlink to a file in a nested directory */
197 	T_EXPECT_POSIX_SUCCESS((fd = openat(test_fd, "symlink_to_nested", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 5: Symlink to a file within the same directory");
198 	if (fd >= 0) {
199 		close(fd);
200 	}
201 
202 	/* Test Case 6: File using an absolute path */
203 	T_EXPECT_POSIX_FAILURE(openat(test_fd, "/etc/passwd", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
204 
205 	/* Test Case 7: Valid symlink to parent directory */
206 	T_EXPECT_POSIX_FAILURE(openat(test_fd, "parent_symlink/outside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
207 
208 	/* Test Case 8: Circular symlink within directory */
209 	T_EXPECT_POSIX_FAILURE(openat(test_fd, "circular_symlink", O_RDONLY | O_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
210 
211 	/* Test Case 9: Path can not escape outside at any point of the resolution */
212 	T_EXPECT_POSIX_FAILURE(openat(test_fd, "../test_dir/inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
213 
214 	/* Test Case 10: File using a symlink pointing to absolute path */
215 	T_EXPECT_POSIX_FAILURE(openat(test_fd, "symlink_absolute/test_dir/inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
216 
217 	/* Test Case 11: Absolute path relative to the root directory */
218 	T_EXPECT_POSIX_FAILURE(openat(root_fd, "/etc/passwd", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 11: Absolute path relative to the root directory");
219 
220 	/* Test Case 12: Path can not escape outside of the root directory using dotdot */
221 	T_EXPECT_POSIX_FAILURE((fd = openat(root_fd, "../private", O_RESOLVE_BENEATH)), ENOTCAPABLE, "Test Case 12: Path can not escape outside of the root directory using dotdot");
222 
223 	/* Changing current directory to the test directory */
224 	T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
225 
226 	T_LOG("Testing the open() syscall using O_RESOLVE_BENEATH");
227 
228 	/* Test Case 13: Open a file within the directory */
229 	T_EXPECT_POSIX_SUCCESS((fd = open("inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 13: Open a file within the directory");
230 	if (fd >= 0) {
231 		close(fd);
232 	}
233 
234 	/* Test Case 14: Attempt to open a file using a symlink pointing outside */
235 	T_EXPECT_POSIX_FAILURE(open("symlink", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 14: Attempt to open a file using a symlink pointing outside");
236 
237 	/* Test Case 15: Attempt to open a file using ".." to navigate outside */
238 	T_EXPECT_POSIX_FAILURE(open("../outside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 15: Attempt to open a file using \"..\" to navigate outside");
239 
240 	/* Test Case 16: Open a file within a nested directory */
241 	T_EXPECT_POSIX_SUCCESS((fd = open("nested/nested_file.txt", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 16: Open a file within a nested directory");
242 	if (fd >= 0) {
243 		close(fd);
244 	}
245 
246 	/* Test Case 17: Symlink to a file in a nested directory */
247 	T_EXPECT_POSIX_SUCCESS((fd = open("symlink_to_nested", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 17: Symlink to a file within the same directory");
248 	if (fd >= 0) {
249 		close(fd);
250 	}
251 
252 	/* Test Case 18: Attempt to open a file using an absolute path */
253 	T_EXPECT_POSIX_FAILURE(open("/etc/passwd", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 18: Attempt to open a file using an absolute path");
254 
255 	/* Test Case 19: Valid symlink to parent directory */
256 	T_EXPECT_POSIX_FAILURE(open("parent_symlink/outside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 19: Valid symlink to parent directory");
257 
258 	/* Test Case 20: Circular symlink within directory */
259 	T_EXPECT_POSIX_FAILURE(open("circular_symlink", O_RDONLY | O_RESOLVE_BENEATH), ELOOP, "Test Case 20: Circular symlink within directory");
260 
261 	/* Test Case 21: Path can not escape outside at any point of the resolution */
262 	T_EXPECT_POSIX_FAILURE(open("../test_dir/inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 21: Path can not escape outside at any point of the resolution");
263 
264 	/* Test Case 22: Attempt to open a file using a symlink pointing to absolute path */
265 	T_EXPECT_POSIX_FAILURE(open("symlink_absolute/test_dir/inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 22: Attempt to open a file using a symlink pointing to absolute path");
266 
267 	/* Test Case 23: Path can not escape outside at any point of the resolution using absolute path */
268 	snprintf(path, sizeof(path), "%s/%s", testdir, INSIDE_FILE);
269 	T_EXPECT_POSIX_FAILURE(open(path, O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 23: Path can not escape outside at any point of the resolution using absolute path");
270 
271 	T_EXPECT_POSIX_SUCCESS(close(root_fd), "Closing the root directory");
272 }
273 
274 T_DECL(resolve_beneath_faccessat,
275     "test faccessat() using the AT_RESOLVE_BENEATH flag")
276 {
277 	T_SETUPBEGIN;
278 
279 	T_ATEND(cleanup);
280 	setup("resolve_beneath_faccessat");
281 
282 	T_SETUPEND;
283 
284 	T_LOG("Testing the faccessat() syscall using AT_RESOLVE_BENEATH");
285 
286 	/* Test Case 1: File within the directory */
287 	T_EXPECT_POSIX_SUCCESS(faccessat(test_fd, "inside_file.txt", R_OK, AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
288 
289 	/* Test Case 2: File using a symlink pointing outside */
290 	T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "symlink", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
291 
292 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
293 	T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "../outside_file.txt", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
294 
295 	/* Test Case 4: File within a nested directory */
296 	T_EXPECT_POSIX_SUCCESS(faccessat(test_fd, "nested/nested_file.txt", R_OK, AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
297 
298 	/* Test Case 5: Symlink to a file in a nested directory */
299 	T_EXPECT_POSIX_SUCCESS(faccessat(test_fd, "symlink_to_nested", R_OK, AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
300 
301 	/* Test Case 6: File using an absolute path */
302 	T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "/etc/passwd", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
303 
304 	/* Test Case 7: Valid symlink to parent directory */
305 	T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "parent_symlink/outside_file.txt", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
306 
307 	/* Test Case 8: Circular symlink within directory */
308 	T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "circular_symlink", R_OK, AT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
309 
310 	/* Test Case 9: Path can not escape outside at any point of the resolution */
311 	T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "../test_dir/inside_file.txt", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
312 
313 	/* Test Case 10: File using a symlink pointing to absolute path */
314 	T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "symlink_absolute/test_dir/inside_file.txt", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
315 }
316 
317 T_DECL(resolve_beneath_fstatat,
318     "test fstatat() using the AT_RESOLVE_BENEATH flag")
319 {
320 	struct stat buf;
321 
322 	T_SETUPBEGIN;
323 
324 	T_ATEND(cleanup);
325 	setup("resolve_beneath_fstatat");
326 
327 	T_SETUPEND;
328 
329 	T_LOG("Testing the fstatat() syscall using AT_RESOLVE_BENEATH");
330 
331 	/* Test Case 1: File within the directory */
332 	T_EXPECT_POSIX_SUCCESS(fstatat(test_fd, "inside_file.txt", &buf, AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
333 
334 	/* Test Case 2: File using a symlink pointing outside */
335 	T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "symlink", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
336 
337 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
338 	T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "../outside_file.txt", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
339 
340 	/* Test Case 4: File within a nested directory */
341 	T_EXPECT_POSIX_SUCCESS(fstatat(test_fd, "nested/nested_file.txt", &buf, AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
342 
343 	/* Test Case 5: Symlink to a file in a nested directory */
344 	T_EXPECT_POSIX_SUCCESS(fstatat(test_fd, "symlink_to_nested", &buf, AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
345 
346 	/* Test Case 6: File using an absolute path */
347 	T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "/etc/passwd", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
348 
349 	/* Test Case 7: Valid symlink to parent directory */
350 	T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "parent_symlink/outside_file.txt", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
351 
352 	/* Test Case 8: Circular symlink within directory */
353 	T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "circular_symlink", &buf, AT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
354 
355 	/* Test Case 9: Path can not escape outside at any point of the resolution */
356 	T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "../test_dir/inside_file.txt", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
357 
358 	/* Test Case 10: File using a symlink pointing to absolute path */
359 	T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "symlink_absolute/test_dir/inside_file.txt", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
360 }
361 
362 T_DECL(resolve_beneath_fchmodat,
363     "test fchmodat() using the AT_RESOLVE_BENEATH flag")
364 {
365 	T_SETUPBEGIN;
366 
367 	T_ATEND(cleanup);
368 	setup("resolve_beneath_fchmodat");
369 
370 	T_SETUPEND;
371 
372 	T_LOG("Testing the fchmodat() syscall using AT_RESOLVE_BENEATH");
373 
374 	/* Test Case 1: File within the directory */
375 	T_EXPECT_POSIX_SUCCESS(fchmodat(test_fd, "inside_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
376 
377 	/* Test Case 2: File using a symlink pointing outside */
378 	T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "symlink", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
379 
380 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
381 	T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "../outside_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
382 
383 	/* Test Case 4: File within a nested directory */
384 	T_EXPECT_POSIX_SUCCESS(fchmodat(test_fd, "nested/nested_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
385 
386 	/* Test Case 5: Symlink to a file in a nested directory */
387 	T_EXPECT_POSIX_SUCCESS(fchmodat(test_fd, "symlink_to_nested", S_IRWXU, AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
388 
389 	/* Test Case 6: File using an absolute path */
390 	T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "/etc/passwd", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
391 
392 	/* Test Case 7: Valid symlink to parent directory */
393 	T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "parent_symlink/outside_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
394 
395 	/* Test Case 8: Circular symlink within directory */
396 	T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "circular_symlink", S_IRWXU, AT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
397 
398 	/* Test Case 9: Path can not escape outside at any point of the resolution */
399 	T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "../test_dir/inside_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
400 
401 	/* Test Case 10: File using a symlink pointing to absolute path */
402 	T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "symlink_absolute/test_dir/inside_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
403 }
404 
405 T_DECL(resolve_beneath_fchownat,
406     "test fchownat() using the AT_RESOLVE_BENEATH flag")
407 {
408 	T_SETUPBEGIN;
409 
410 	T_ATEND(cleanup);
411 	setup("resolve_beneath_fchownat");
412 
413 	T_SETUPEND;
414 
415 	T_LOG("Testing the fchownat() syscall using AT_RESOLVE_BENEATH");
416 
417 	/* Test Case 1: File within the directory */
418 	T_EXPECT_POSIX_SUCCESS(fchownat(test_fd, "inside_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
419 
420 	/* Test Case 2: File using a symlink pointing outside */
421 	T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "symlink", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
422 
423 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
424 	T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "../outside_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
425 
426 	/* Test Case 4: File within a nested directory */
427 	T_EXPECT_POSIX_SUCCESS(fchownat(test_fd, "nested/nested_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
428 
429 	/* Test Case 5: Symlink to a file in a nested directory */
430 	T_EXPECT_POSIX_SUCCESS(fchownat(test_fd, "symlink_to_nested", geteuid(), getgid(), AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
431 
432 	/* Test Case 6: File using an absolute path */
433 	T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "/etc/passwd", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
434 
435 	/* Test Case 7: Valid symlink to parent directory */
436 	T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "parent_symlink/outside_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
437 
438 	/* Test Case 8: Circular symlink within directory */
439 	T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "circular_symlink", geteuid(), getgid(), AT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
440 
441 	/* Test Case 9: Path can not escape outside at any point of the resolution */
442 	T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "../test_dir/inside_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
443 
444 	/* Test Case 10: File using a symlink pointing to absolute path */
445 	T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "symlink_absolute/test_dir/inside_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
446 }
447 
448 T_DECL(resolve_beneath_linkat,
449     "test linkat() using the AT_RESOLVE_BENEATH flag")
450 {
451 	T_SETUPBEGIN;
452 
453 	T_ATEND(cleanup);
454 	setup("resolve_beneath_linkat");
455 
456 	T_SETUPEND;
457 
458 	T_LOG("Testing the linkat() syscall using AT_RESOLVE_BENEATH");
459 
460 	/* Test Case 1: File within the directory */
461 	T_EXPECT_POSIX_SUCCESS(linkat(test_fd, "inside_file.txt", test_fd, "inside_file_2.txt", AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
462 	unlinkat(test_fd, "inside_file_2.txt", 0);
463 
464 	/* Test Case 2: File using a symlink pointing outside */
465 	T_EXPECT_POSIX_FAILURE(linkat(test_fd, "symlink/.", test_fd, "inside_file_2.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
466 
467 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
468 	T_EXPECT_POSIX_FAILURE(linkat(test_fd, "inside_file.txt", test_fd, "../outside_file.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
469 
470 	/* Test Case 4: File within a nested directory */
471 	T_EXPECT_POSIX_SUCCESS(linkat(test_fd, "nested/nested_file.txt", test_fd, "nested/nested_file_2.txt", AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
472 	unlinkat(test_fd, "nested/nested_file_2.txt", 0);
473 
474 	/* Test Case 5: Symlink to a file in a nested directory */
475 	T_EXPECT_POSIX_SUCCESS(linkat(test_fd, "symlink_to_nested", test_fd, "nested/nested_file_2.txt", AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
476 	unlinkat(test_fd, "nested/nested_file_2.txt", 0);
477 
478 	/* Test Case 6: File using an absolute path */
479 	T_EXPECT_POSIX_FAILURE(linkat(test_fd, "/etc/passwd", test_fd, "inside_file_2.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
480 }
481 
482 T_DECL(resolve_beneath_unlinkat,
483     "test unlinkat() using the AT_RESOLVE_BENEATH flag")
484 {
485 	int fd;
486 
487 	T_SETUPBEGIN;
488 
489 	T_ATEND(cleanup);
490 	setup("resolve_beneath_unlinkat");
491 
492 	T_SETUPEND;
493 
494 	T_LOG("Testing the unlinkat() syscall using AT_RESOLVE_BENEATH");
495 
496 	/* Test Case 1: File within the directory */
497 	T_EXPECT_POSIX_SUCCESS(unlinkat(test_fd, "inside_file.txt", AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
498 	if ((fd = openat(testdir_fd, INSIDE_FILE, O_CREAT | O_RDWR, 0777)) < 0) {
499 		T_FAIL("Unable to recreate %s", INSIDE_FILE);
500 	}
501 	close(fd);
502 
503 	/* Test Case 2: File using a symlink pointing outside */
504 	T_EXPECT_POSIX_SUCCESS(unlinkat(test_fd, "symlink", AT_RESOLVE_BENEATH), "Test Case 2: File using a symlink pointing outside");
505 	if (symlinkat(SYMLINK_FROM, testdir_fd, SYMLINK) < 0) {
506 		T_FAIL("Unable to recreate %s", INSIDE_FILE);
507 	}
508 
509 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
510 	T_EXPECT_POSIX_FAILURE(unlinkat(test_fd, "../outside_file.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
511 
512 	/* Test Case 4: File within a nested directory */
513 	T_EXPECT_POSIX_SUCCESS(unlinkat(test_fd, "nested/nested_file.txt", AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
514 	if ((fd = openat(testdir_fd, NESTED_FILE, O_CREAT | O_RDWR, 0777)) < 0) {
515 		T_FAIL("Unable to recreate %s", NESTED_FILE);
516 	}
517 	close(fd);
518 
519 	/* Test Case 5: Symlink to a file in a nested directory */
520 	T_EXPECT_POSIX_SUCCESS(unlinkat(test_fd, "symlink_to_nested", AT_RESOLVE_BENEATH), "Test Case 5: Symlink //to a file within the same directory");
521 	if (symlinkat(SYMLINK_TO_NESTED_FROM, testdir_fd, SYMLINK_TO_NESTED) < 0) {
522 		T_FAIL("Unable to recreate %s", SYMLINK_TO_NESTED);
523 	}
524 
525 	/* Test Case 6: File using an absolute path */
526 	T_EXPECT_POSIX_FAILURE(unlinkat(test_fd, "/etc/passwd", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
527 
528 	/* Test Case 7: Valid symlink to parent directory */
529 	T_EXPECT_POSIX_FAILURE(unlinkat(test_fd, "parent_symlink/outside_file.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
530 
531 	/* Test Case 8: Circular symlink within directory */
532 	T_EXPECT_POSIX_SUCCESS(unlinkat(test_fd, "circular_symlink", AT_RESOLVE_BENEATH), "Test Case 8: Circular symlink within directory");
533 	if (symlinkat(CIRCULAR_SYMLINK_FROM, testdir_fd, CIRCULAR_SYMLINK) < 0) {
534 		T_FAIL("Unable to recreate %s", CIRCULAR_SYMLINK);
535 	}
536 
537 	/* Test Case 9: Path can not escape outside at any point of the resolution */
538 	T_EXPECT_POSIX_FAILURE(unlinkat(test_fd, "../test_dir/inside_file.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
539 
540 	/* Test Case 10: File using a symlink pointing to absolute path */
541 	T_EXPECT_POSIX_FAILURE(unlinkat(test_fd, "symlink_absolute/test_dir/inside_file.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
542 }
543 
544 T_DECL(resolve_beneath_utimensat,
545     "test utimensat() using the AT_RESOLVE_BENEATH flag")
546 {
547 	static const struct timespec tptr[] = {
548 		{ 0x12345678, 987654321 },
549 		{ 0x15263748, 123456789 },
550 	};
551 
552 	T_SETUPBEGIN;
553 
554 	T_ATEND(cleanup);
555 	setup("resolve_beneath_utimensat");
556 
557 	T_SETUPEND;
558 
559 	T_LOG("Testing the utimensat() syscall using AT_RESOLVE_BENEATH");
560 
561 	/* Test Case 1: File within the directory */
562 	T_EXPECT_POSIX_SUCCESS(utimensat(test_fd, "inside_file.txt", tptr, AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
563 
564 	/* Test Case 2: File using a symlink pointing outside */
565 	T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "symlink", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
566 
567 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
568 	T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "../outside_file.txt", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
569 
570 	/* Test Case 4: File within a nested directory */
571 	T_EXPECT_POSIX_SUCCESS(utimensat(test_fd, "nested/nested_file.txt", tptr, AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
572 
573 	/* Test Case 5: Symlink to a file in a nested directory */
574 	T_EXPECT_POSIX_SUCCESS(utimensat(test_fd, "symlink_to_nested", tptr, AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
575 
576 	/* Test Case 6: File using an absolute path */
577 	T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "/etc/passwd", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
578 
579 	/* Test Case 7: Valid symlink to parent directory */
580 	T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "parent_symlink/outside_file.txt", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
581 
582 	/* Test Case 8: Circular symlink within directory */
583 	T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "circular_symlink", tptr, AT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
584 
585 	/* Test Case 9: Path can not escape outside at any point of the resolution */
586 	T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "../test_dir/inside_file.txt", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
587 
588 	/* Test Case 10: File using a symlink pointing to absolute path */
589 	T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "symlink_absolute/test_dir/inside_file.txt", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
590 }
591 
592 T_DECL(resolve_beneath_getxattr,
593     "test getxattr()/fgetxattr() using the XATTR_RESOLVE_BENEATH flag")
594 {
595 	char xattr_buff[100];
596 	const char *xattr = "test1234";
597 	size_t xattr_len = strlen(xattr);
598 
599 	T_SETUPBEGIN;
600 
601 	T_ATEND(cleanup);
602 	setup("resolve_beneath_getxattr");
603 
604 	/* Changing current directory to the test directory */
605 	T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
606 
607 	/* Setting extended attributes */
608 	T_ASSERT_POSIX_SUCCESS(setxattr("inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to inside_file.txt");
609 	T_ASSERT_POSIX_SUCCESS(setxattr("../outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to outside_file.txt");
610 	T_ASSERT_POSIX_SUCCESS(setxattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to nested_file.txt");
611 
612 	T_SETUPEND;
613 
614 	T_LOG("Testing the getxattr() syscall using XATTR_RESOLVE_BENEATH");
615 
616 	/* Test Case 1: File within the directory */
617 	T_EXPECT_POSIX_SUCCESS(getxattr("inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), "Test Case 1: File within the directory");
618 
619 	/* Test Case 2: File using a symlink pointing outside */
620 	T_EXPECT_POSIX_FAILURE(getxattr("symlink", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
621 
622 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
623 	T_EXPECT_POSIX_FAILURE(getxattr("../outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
624 
625 	/* Test Case 4: File within a nested directory */
626 	T_EXPECT_POSIX_SUCCESS(getxattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
627 
628 	/* Test Case 5: Symlink to a file in a nested directory */
629 	T_EXPECT_POSIX_SUCCESS(getxattr("symlink_to_nested", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
630 
631 	/* Test Case 6: File using an absolute path */
632 	T_EXPECT_POSIX_FAILURE(getxattr("/etc/passwd", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
633 
634 	/* Test Case 7: Valid symlink to parent directory */
635 	T_EXPECT_POSIX_FAILURE(getxattr("parent_symlink/outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
636 
637 	/* Test Case 8: Circular symlink within directory */
638 	T_EXPECT_POSIX_FAILURE(getxattr("circular_symlink", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
639 
640 	/* Test Case 9: Path can not escape outside at any point of the resolution */
641 	T_EXPECT_POSIX_FAILURE(getxattr("../test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
642 
643 	/* Test Case 10: File using a symlink pointing to absolute path */
644 	T_EXPECT_POSIX_FAILURE(getxattr("symlink_absolute/test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
645 
646 	T_LOG("Testing the fgetxattr() syscall using XATTR_RESOLVE_BENEATH");
647 
648 	/* Test Case 11: Verifying that fgetxattr() fails with EINVAL */
649 	T_EXPECT_POSIX_FAILURE(fgetxattr(test_fd, XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), EINVAL, "Test Case 11: Verifying that fgetxattr() fails with EINVAL");
650 }
651 
652 T_DECL(resolve_beneath_setxattr,
653     "test setxattr()/fsetxattr() using the XATTR_RESOLVE_BENEATH flag")
654 {
655 	const char *xattr = "test1234";
656 	size_t xattr_len = strlen(xattr);
657 
658 	T_SETUPBEGIN;
659 
660 	T_ATEND(cleanup);
661 	setup("resolve_beneath_setxattr");
662 
663 	/* Changing current directory to the test directory */
664 	T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
665 
666 	T_SETUPEND;
667 
668 	T_LOG("Testing the setxattr() syscall using XATTR_RESOLVE_BENEATH");
669 
670 	/* Test Case 1: File within the directory */
671 	T_EXPECT_POSIX_SUCCESS(setxattr("inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), "Test Case 1: File within the directory");
672 
673 	/* Test Case 2: File using a symlink pointing outside */
674 	T_EXPECT_POSIX_FAILURE(setxattr("symlink", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
675 
676 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
677 	T_EXPECT_POSIX_FAILURE(setxattr("../outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
678 
679 	/* Test Case 4: File within a nested directory */
680 	T_EXPECT_POSIX_SUCCESS(setxattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
681 
682 	/* Test Case 5: Symlink to a file in a nested directory */
683 	T_EXPECT_POSIX_SUCCESS(setxattr("symlink_to_nested", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
684 
685 	/* Test Case 6: File using an absolute path */
686 	T_EXPECT_POSIX_FAILURE(setxattr("/etc/passwd", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
687 
688 	/* Test Case 7: Valid symlink to parent directory */
689 	T_EXPECT_POSIX_FAILURE(setxattr("parent_symlink/outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
690 
691 	/* Test Case 8: Circular symlink within directory */
692 	T_EXPECT_POSIX_FAILURE(setxattr("circular_symlink", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
693 
694 	/* Test Case 9: Path can not escape outside at any point of the resolution */
695 	T_EXPECT_POSIX_FAILURE(setxattr("../test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
696 
697 	/* Test Case 10: File using a symlink pointing to absolute path */
698 	T_EXPECT_POSIX_FAILURE(setxattr("symlink_absolute/test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
699 
700 	T_LOG("Testing the fsetxattr() syscall using XATTR_RESOLVE_BENEATH");
701 
702 	/* Test Case 11: Verifying that fsetxattr() fails with EINVAL */
703 	T_EXPECT_POSIX_FAILURE(fsetxattr(test_fd, XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), EINVAL, "Test Case 11: Verifying that fsetxattr() fails with EINVAL");
704 }
705 
706 T_DECL(resolve_beneath_listxattr,
707     "test listxattr()/flistxattr() using the XATTR_RESOLVE_BENEATH flag")
708 {
709 	char xattr_buff[100];
710 
711 	T_SETUPBEGIN;
712 
713 	T_ATEND(cleanup);
714 	setup("resolve_beneath_listxattr");
715 
716 	/* Changing current directory to the test directory */
717 	T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
718 
719 	T_SETUPEND;
720 
721 	T_LOG("Testing the listxattr() syscall using XATTR_RESOLVE_BENEATH");
722 
723 	/* Test Case 1: File within the directory */
724 	T_EXPECT_POSIX_SUCCESS(listxattr("inside_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), "Test Case 1: File within the directory");
725 
726 	/* Test Case 2: File using a symlink pointing outside */
727 	T_EXPECT_POSIX_FAILURE(listxattr("symlink", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
728 
729 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
730 	T_EXPECT_POSIX_FAILURE(listxattr("../outside_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
731 
732 	/* Test Case 4: File within a nested directory */
733 	T_EXPECT_POSIX_SUCCESS(listxattr("nested/nested_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
734 
735 	/* Test Case 5: Symlink to a file in a nested directory */
736 	T_EXPECT_POSIX_SUCCESS(listxattr("symlink_to_nested", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
737 
738 	/* Test Case 6: File using an absolute path */
739 	T_EXPECT_POSIX_FAILURE(listxattr("/etc/passwd", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
740 
741 	/* Test Case 7: Valid symlink to parent directory */
742 	T_EXPECT_POSIX_FAILURE(listxattr("parent_symlink/outside_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
743 
744 	/* Test Case 8: Circular symlink within directory */
745 	T_EXPECT_POSIX_FAILURE(listxattr("circular_symlink", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
746 
747 	/* Test Case 9: Path can not escape outside at any point of the resolution */
748 	T_EXPECT_POSIX_FAILURE(listxattr("../test_dir/inside_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
749 
750 	/* Test Case 10: File using a symlink pointing to absolute path */
751 	T_EXPECT_POSIX_FAILURE(listxattr("symlink_absolute/test_dir/inside_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
752 
753 	T_LOG("Testing the flistxattr() syscall using XATTR_RESOLVE_BENEATH");
754 
755 	/* Test Case 11: Verifying that flistxattr() fails with EINVAL */
756 	T_EXPECT_POSIX_FAILURE(flistxattr(test_fd, xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), EINVAL, "Test Case 11: Verifying that flistxattr() fails with EINVAL");
757 }
758 
759 T_DECL(resolve_beneath_removexattr,
760     "test removexattr()/fremovexattr() using the XATTR_RESOLVE_BENEATH flag")
761 {
762 	const char *xattr = "test1234";
763 	size_t xattr_len = strlen(xattr);
764 
765 	T_SETUPBEGIN;
766 
767 	T_ATEND(cleanup);
768 	setup("resolve_beneath_removexattr");
769 
770 	/* Changing current directory to the test directory */
771 	T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
772 
773 	/* Setting extended attributes */
774 	T_ASSERT_POSIX_SUCCESS(setxattr("inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to inside_file.txt");
775 	T_ASSERT_POSIX_SUCCESS(setxattr("../outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to outside_file.txt");
776 	T_ASSERT_POSIX_SUCCESS(setxattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to nested_file.txt");
777 
778 	T_SETUPEND;
779 
780 	T_LOG("Testing the removexattr() syscall using XATTR_RESOLVE_BENEATH");
781 
782 	/* Test Case 1: File within the directory */
783 	T_EXPECT_POSIX_SUCCESS(removexattr("inside_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), "Test Case 1: File within the directory");
784 
785 	/* Test Case 2: File using a symlink pointing outside */
786 	T_EXPECT_POSIX_FAILURE(removexattr("symlink", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
787 
788 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
789 	T_EXPECT_POSIX_FAILURE(removexattr("../outside_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
790 
791 	/* Test Case 4: File within a nested directory */
792 	T_EXPECT_POSIX_SUCCESS(removexattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
793 
794 	if (setxattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0) < 0) {
795 		T_FAIL("Unable to setxattr to nested_file.txt");
796 	}
797 
798 	/* Test Case 5: Symlink to a file in a nested directory */
799 	T_EXPECT_POSIX_SUCCESS(removexattr("symlink_to_nested", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
800 
801 	/* Test Case 6: File using an absolute path */
802 	T_EXPECT_POSIX_FAILURE(removexattr("/etc/passwd", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
803 
804 	/* Test Case 7: Valid symlink to parent directory */
805 	T_EXPECT_POSIX_FAILURE(removexattr("parent_symlink/outside_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
806 
807 	/* Test Case 8: Circular symlink within directory */
808 	T_EXPECT_POSIX_FAILURE(removexattr("circular_symlink", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
809 
810 	/* Test Case 9: Path can not escape outside at any point of the resolution */
811 	T_EXPECT_POSIX_FAILURE(removexattr("../test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
812 
813 	/* Test Case 10: File using a symlink pointing to absolute path */
814 	T_EXPECT_POSIX_FAILURE(removexattr("symlink_absolute/test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
815 
816 	T_LOG("Testing the fremovexattr() syscall using XATTR_RESOLVE_BENEATH");
817 
818 	/* Test Case 11: Verifying that fremovexattr() fails with EINVAL */
819 	T_EXPECT_POSIX_FAILURE(fremovexattr(test_fd, XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), EINVAL, "Test Case 11: Verifying that fremovexattr() fails with EINVAL");
820 }
821 
822 T_DECL(resolve_beneath_clonefile,
823     "test clonefile()/clonefileat()/fclonefileat() using the CLONE_RESOLVE_BENEATH flag")
824 {
825 	int fd;
826 	T_SETUPBEGIN;
827 
828 	T_ATEND(cleanup);
829 	setup("resolve_beneath_clonefile");
830 
831 	/* Changing current directory to the test directory */
832 	T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
833 
834 	/* Open test file */
835 	T_ASSERT_POSIX_SUCCESS((fd = open("inside_file.txt", O_RDWR, 0777)), "Opening %s", INSIDE_FILE);
836 
837 	T_SETUPEND;
838 
839 	T_LOG("Testing the clonefile() syscall using CLONE_RESOLVE_BENEATH");
840 
841 	/* Test Case 1: File within the directory */
842 	T_EXPECT_POSIX_SUCCESS(clonefile("inside_file.txt", "inside_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 1: File within the directory");
843 	unlink("inside_file_2.txt");
844 
845 	/* Test Case 2: File using a symlink pointing outside */
846 	T_EXPECT_POSIX_FAILURE(clonefile("symlink", "inside_file_2.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
847 
848 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
849 	T_EXPECT_POSIX_FAILURE(clonefile("inside_file.txt", "../outside_file.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
850 
851 	/* Test Case 4: File within a nested directory */
852 	T_EXPECT_POSIX_SUCCESS(clonefile("nested/nested_file.txt", "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
853 	unlinkat(test_fd, "nested/nested_file_2.txt", 0);
854 
855 	/* Test Case 5: Symlink to a file in a nested directory */
856 	T_EXPECT_POSIX_SUCCESS(clonefile("symlink_to_nested", "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
857 	unlinkat(test_fd, "nested/nested_file_2.txt", 0);
858 
859 	/* Test Case 6: File using an absolute path */
860 	T_EXPECT_POSIX_FAILURE(clonefile("/etc/passwd", "inside_file_2.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
861 
862 	T_LOG("Testing the clonefileat() syscall using CLONE_RESOLVE_BENEATH");
863 
864 	/* Test Case 7: File within the directory */
865 	T_EXPECT_POSIX_SUCCESS(clonefileat(test_fd, "inside_file.txt", test_fd, "inside_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 7: File within the directory");
866 	unlinkat(test_fd, "inside_file_2.txt", 0);
867 
868 	/* Test Case 8: File using a symlink pointing outside */
869 	T_EXPECT_POSIX_FAILURE(clonefileat(test_fd, "symlink", test_fd, "inside_file_2.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 8: File using a symlink pointing outside");
870 
871 	/* Test Case 9: Attempt to open a file using ".." to navigate outside */
872 	T_EXPECT_POSIX_FAILURE(clonefileat(test_fd, "inside_file.txt", test_fd, "../outside_file.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: File using \"..\" to navigate outside");
873 
874 	/* Test Case 10: File within a nested directory */
875 	T_EXPECT_POSIX_SUCCESS(clonefileat(test_fd, "nested/nested_file.txt", test_fd, "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 10: File within a nested directory");
876 	unlinkat(test_fd, "nested/nested_file_2.txt", 0);
877 
878 	/* Test Case 11: Symlink to a file in a nested directory */
879 	T_EXPECT_POSIX_SUCCESS(clonefileat(test_fd, "symlink_to_nested", test_fd, "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 11: Symlink to a file within the same directory");
880 	unlinkat(test_fd, "nested/nested_file_2.txt", 0);
881 
882 	/* Test Case 12: File using an absolute path */
883 	T_EXPECT_POSIX_FAILURE(clonefileat(test_fd, "/etc/passwd", test_fd, "inside_file_2.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 12: File using an absolute path");
884 
885 	T_LOG("Testing the fclonefileat() syscall using CLONE_RESOLVE_BENEATH");
886 
887 	/* Test Case 13: File within the directory */
888 	T_EXPECT_POSIX_SUCCESS(fclonefileat(fd, test_fd, "inside_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 13: File within the directory");
889 	unlinkat(test_fd, "inside_file_2.txt", 0);
890 
891 	/* Test Case 14: File using a symlink pointing outside */
892 	T_EXPECT_POSIX_FAILURE(fclonefileat(fd, test_fd, "symlink_absolute/test_dir/inside_file.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 14: File using a symlink pointing outside");
893 
894 	/* Test Case 15: Attempt to open a file using ".." to navigate outside */
895 	T_EXPECT_POSIX_FAILURE(fclonefileat(fd, test_fd, "../outside_file.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 15: File using \"..\" to navigate outside");
896 
897 	/* Test Case 16: File within a nested directory */
898 	T_EXPECT_POSIX_SUCCESS(fclonefileat(fd, test_fd, "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 16: File within a nested directory");
899 	unlinkat(test_fd, "nested/nested_file_2.txt", 0);
900 
901 	/* Test Case 17: Symlink to a file in a nested directory */
902 	T_EXPECT_POSIX_SUCCESS(fclonefileat(fd, test_fd, "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 17: Symlink to a file within the same directory");
903 	unlinkat(test_fd, "nested/nested_file_2.txt", 0);
904 
905 	/* Test Case 18: File using an absolute path */
906 	T_EXPECT_POSIX_FAILURE(fclonefileat(fd, test_fd, "/etc/inside_file_2.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 18: File using an absolute path");
907 
908 	T_EXPECT_POSIX_SUCCESS(close(fd), "Closing %s", INSIDE_FILE);
909 }
910 
911 T_DECL(resolve_beneath_renamex_np,
912     "test renamex_np()/renameatx_np() using the RENAME_RESOLVE_BENEATH flag")
913 {
914 	T_SETUPBEGIN;
915 
916 	T_ATEND(cleanup);
917 	setup("resolve_beneath_renamex_np");
918 
919 	/* Changing current directory to the test directory */
920 	T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
921 
922 	T_SETUPEND;
923 
924 	T_LOG("Testing the renamex_np() syscall using RENAME_RESOLVE_BENEATH");
925 
926 	/* Test Case 1: File within the directory */
927 	T_EXPECT_POSIX_SUCCESS(renamex_np("inside_file.txt", "inside_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 1: File within the directory");
928 	if (renamex_np("inside_file_2.txt", "inside_file.txt", 0)) {
929 		T_FAIL("Unable to rename inside_file_2.txt to inside_file.txt");
930 	}
931 
932 	/* Test Case 2: File using a symlink pointing outside */
933 	T_EXPECT_POSIX_FAILURE(renamex_np("symlink/.", "inside_file_2.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
934 
935 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
936 	T_EXPECT_POSIX_FAILURE(renamex_np("inside_file.txt", "../outside_file.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
937 
938 	/* Test Case 4: File within a nested directory */
939 	T_EXPECT_POSIX_SUCCESS(renamex_np("nested/nested_file.txt", "nested/nested_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
940 	if (renamex_np("nested/nested_file_2.txt", "nested/nested_file.txt", 0)) {
941 		T_FAIL("Unable to rename nested/nested_file_2.txt to nested/nested_file.txt");
942 	}
943 
944 	/* Test Case 5: Symlink to a file in a nested directory */
945 	T_EXPECT_POSIX_SUCCESS(renamex_np("symlink_to_nested", "nested/nested_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
946 	if (renamex_np("nested/nested_file_2.txt", "symlink_to_nested", 0)) {
947 		T_FAIL("Unable to rename nested/nested_file_2.txt to symlink_to_nested");
948 	}
949 
950 	/* Test Case 6: File using an absolute path */
951 	T_EXPECT_POSIX_FAILURE(renamex_np("/etc/passwd", "inside_file_2.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
952 
953 	T_LOG("Testing the renameatx_np() syscall using RENAME_RESOLVE_BENEATH");
954 
955 	/* Test Case 7: File within the directory */
956 	T_EXPECT_POSIX_SUCCESS(renameatx_np(test_fd, "inside_file.txt", test_fd, "inside_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 7: File within the directory");
957 	if (renamex_np("inside_file_2.txt", "inside_file.txt", 0)) {
958 		T_FAIL("Unable to rename inside_file_2.txt to inside_file.txt");
959 	}
960 
961 	/* Test Case 8: File using a symlink pointing outside */
962 	T_EXPECT_POSIX_FAILURE(renameatx_np(test_fd, "symlink/.", test_fd, "inside_file_2.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 8: File using a symlink pointing outside");
963 
964 	/* Test Case 9: Attempt to open a file using ".." to navigate outside */
965 	T_EXPECT_POSIX_FAILURE(renameatx_np(test_fd, "inside_file.txt", test_fd, "../outside_file.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: File using \"..\" to navigate outside");
966 
967 	/* Test Case 10: File within a nested directory */
968 	T_EXPECT_POSIX_SUCCESS(renameatx_np(test_fd, "nested/nested_file.txt", test_fd, "nested/nested_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 10: File within a nested directory");
969 	if (renamex_np("nested/nested_file_2.txt", "nested/nested_file.txt", 0)) {
970 		T_FAIL("Unable to rename nested/nested_file_2.txt to nested/nested_file.txt");
971 	}
972 
973 	/* Test Case 11: Symlink to a file in a nested directory */
974 	T_EXPECT_POSIX_SUCCESS(renameatx_np(test_fd, "symlink_to_nested", test_fd, "nested/nested_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 11: Symlink to a file within the same directory");
975 	if (renamex_np("nested/nested_file_2.txt", "symlink_to_nested", 0)) {
976 		T_FAIL("Unable to rename nested/nested_file_2.txt to symlink_to_nested");
977 	}
978 
979 	/* Test Case 12: File using an absolute path */
980 	T_EXPECT_POSIX_FAILURE(renameatx_np(test_fd, "/etc/passwd", test_fd, "inside_file_2.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 12: File using an absolute path");
981 }
982 
983 T_DECL(resolve_beneath_getattrlist,
984     "test getattrlist()/fgetattrlist()/getattrlistat() using the FSOPT_RESOLVE_BENEATH flag")
985 {
986 	int fd;
987 
988 	struct myattrbuf {
989 		uint32_t length;
990 		attribute_set_t returned_attrs;
991 		vol_attributes_attr_t vol_attributes;
992 		attrreference_t fstypename_ref;
993 		uint32_t fssubtype;
994 		char fstypename[MFSTYPENAMELEN];
995 	} attrbuf;
996 
997 	struct attrlist attrs = {
998 		.bitmapcount = ATTR_BIT_MAP_COUNT,
999 		.commonattr = ATTR_CMN_RETURNED_ATTRS,
1000 		/*
1001 		 * Request ATTR_VOL_ATTRIBUTES to ensure that
1002 		 * ATTR_VOL_FSTYPENAME and ATTR_VOL_FSSUBTYPE
1003 		 * are packed into the buffer *after*.
1004 		 */
1005 		.volattr = ATTR_VOL_INFO | ATTR_VOL_ATTRIBUTES |
1006 	    ATTR_VOL_FSTYPENAME | ATTR_VOL_FSSUBTYPE,
1007 	};
1008 
1009 	T_SETUPBEGIN;
1010 
1011 	T_ATEND(cleanup);
1012 	setup("resolve_beneath_getattrlist");
1013 
1014 	/* Changing current directory to the test directory */
1015 	T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
1016 
1017 	/* Open test file */
1018 	T_ASSERT_POSIX_SUCCESS((fd = open("inside_file.txt", O_RDWR, 0777)), "Opening %s", INSIDE_FILE);
1019 
1020 	T_SETUPEND;
1021 
1022 	T_LOG("Testing the getattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1023 
1024 	/* Test Case 1: File within the directory */
1025 	T_EXPECT_POSIX_SUCCESS(getattrlist("inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
1026 
1027 	/* Test Case 2: File using a symlink pointing outside */
1028 	T_EXPECT_POSIX_FAILURE(getattrlist("symlink", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
1029 
1030 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
1031 	T_EXPECT_POSIX_FAILURE(getattrlist("../outside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
1032 
1033 	/* Test Case 4: File within a nested directory */
1034 	T_EXPECT_POSIX_SUCCESS(getattrlist("nested/nested_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
1035 
1036 	/* Test Case 5: Symlink to a file in a nested directory */
1037 	T_EXPECT_POSIX_SUCCESS(getattrlist("symlink_to_nested", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
1038 
1039 	/* Test Case 6: File using an absolute path */
1040 	T_EXPECT_POSIX_FAILURE(getattrlist("/etc/passwd", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
1041 
1042 	/* Test Case 7: Valid symlink to parent directory */
1043 	T_EXPECT_POSIX_FAILURE(getattrlist("parent_symlink/outside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
1044 
1045 	/* Test Case 8: Circular symlink within directory */
1046 	T_EXPECT_POSIX_FAILURE(getattrlist("circular_symlink", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
1047 
1048 	/* Test Case 9: Path can not escape outside at any point of the resolution */
1049 	T_EXPECT_POSIX_FAILURE(getattrlist("../test_dir/inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
1050 
1051 	/* Test Case 10: File using a symlink pointing to absolute path */
1052 	T_EXPECT_POSIX_FAILURE(getattrlist("symlink_absolute/test_dir/inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
1053 
1054 	T_LOG("Testing the fgetattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1055 
1056 	/* Test Case 11: fgetattrlist() syscall using FSOPT_RESOLVE_BENEATH */
1057 	T_EXPECT_POSIX_SUCCESS(fgetattrlist(fd, &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 11: fgetattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1058 
1059 	T_LOG("Testing the getattrlistat() syscall using FSOPT_RESOLVE_BENEATH");
1060 
1061 	/* Test Case 12: File within the directory */
1062 	T_EXPECT_POSIX_SUCCESS(getattrlistat(test_fd, "inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 12: File within the directory");
1063 
1064 	/* Test Case 13: File using a symlink pointing outside */
1065 	T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "symlink", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 13: File using a symlink pointing outside");
1066 
1067 	/* Test Case 14: Attempt to open a file using ".." to navigate outside */
1068 	T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "../outside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 14: File using \"..\" to navigate outside");
1069 
1070 	/* Test Case 15: File within a nested directory */
1071 	T_EXPECT_POSIX_SUCCESS(getattrlistat(test_fd, "nested/nested_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 15: File within a nested directory");
1072 
1073 	/* Test Case 16: Symlink to a file in a nested directory */
1074 	T_EXPECT_POSIX_SUCCESS(getattrlistat(test_fd, "symlink_to_nested", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 16: Symlink to a file within the same directory");
1075 
1076 	/* Test Case 17: File using an absolute path */
1077 	T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "/etc/passwd", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 17: File using an absolute path");
1078 
1079 	/* Test Case 18: Valid symlink to parent directory */
1080 	T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "parent_symlink/outside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 18: Valid symlink to parent directory");
1081 
1082 	/* Test Case 19: Circular symlink within directory */
1083 	T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "circular_symlink", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ELOOP, "Test Case 19: Circular symlink within directory");
1084 
1085 	/* Test Case 20: Path can not escape outside at any point of the resolution */
1086 	T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "../test_dir/inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 20: Path can not escape outside at any point of the resolution");
1087 
1088 	/* Test Case 21: File using a symlink pointing to absolute path */
1089 	T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "symlink_absolute/test_dir/inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 21: File using a symlink pointing to absolute path");
1090 
1091 	T_EXPECT_POSIX_SUCCESS(close(fd), "Closing %s", INSIDE_FILE);
1092 }
1093 
1094 T_DECL(resolve_beneath_setattrlist,
1095     "test setattrlist()/fsetattrlist()/setattrlistat() using the FSOPT_RESOLVE_BENEATH flag")
1096 {
1097 	int fd;
1098 	int flags;
1099 	struct attrlist attrlist;
1100 
1101 	T_SETUPBEGIN;
1102 
1103 	flags = 0;
1104 	memset(&attrlist, 0, sizeof(attrlist));
1105 	attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
1106 	attrlist.commonattr = ATTR_CMN_FLAGS;
1107 
1108 	T_ATEND(cleanup);
1109 	setup("resolve_beneath_setattrlist");
1110 
1111 	/* Changing current directory to the test directory */
1112 	T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
1113 
1114 	/* Open test file */
1115 	T_ASSERT_POSIX_SUCCESS((fd = open("inside_file.txt", O_RDWR, 0777)), "Opening %s", INSIDE_FILE);
1116 
1117 	T_SETUPEND;
1118 
1119 	T_LOG("Testing the setattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1120 
1121 	/* Test Case 1: File within the directory */
1122 	T_EXPECT_POSIX_SUCCESS(setattrlist("inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
1123 
1124 	/* Test Case 2: File using a symlink pointing outside */
1125 	T_EXPECT_POSIX_FAILURE(setattrlist("symlink", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
1126 
1127 	/* Test Case 3: Attempt to open a file using ".." to navigate outside */
1128 	T_EXPECT_POSIX_FAILURE(setattrlist("../outside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
1129 
1130 	/* Test Case 4: File within a nested directory */
1131 	T_EXPECT_POSIX_SUCCESS(setattrlist("nested/nested_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
1132 
1133 	/* Test Case 5: Symlink to a file in a nested directory */
1134 	T_EXPECT_POSIX_SUCCESS(setattrlist("symlink_to_nested", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
1135 
1136 	/* Test Case 6: File using an absolute path */
1137 	T_EXPECT_POSIX_FAILURE(setattrlist("/etc/passwd", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
1138 
1139 	/* Test Case 7: Valid symlink to parent directory */
1140 	T_EXPECT_POSIX_FAILURE(setattrlist("parent_symlink/outside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
1141 
1142 	/* Test Case 8: Circular symlink within directory */
1143 	T_EXPECT_POSIX_FAILURE(setattrlist("circular_symlink", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
1144 
1145 	/* Test Case 9: Path can not escape outside at any point of the resolution */
1146 	T_EXPECT_POSIX_FAILURE(setattrlist("../test_dir/inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
1147 
1148 	/* Test Case 10: File using a symlink pointing to absolute path */
1149 	T_EXPECT_POSIX_FAILURE(setattrlist("symlink_absolute/test_dir/inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
1150 
1151 	T_LOG("Testing the fsetattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1152 
1153 	/* Test Case 11: fsetattrlist() syscall using FSOPT_RESOLVE_BENEATH */
1154 	T_EXPECT_POSIX_SUCCESS(fsetattrlist(fd, &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 11: fsetattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1155 
1156 	T_LOG("Testing the setattrlistat() syscall using FSOPT_RESOLVE_BENEATH");
1157 
1158 	/* Test Case 12: File within the directory */
1159 	T_EXPECT_POSIX_SUCCESS(setattrlistat(test_fd, "inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 12: File within the directory");
1160 
1161 	/* Test Case 13: File using a symlink pointing outside */
1162 	T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "symlink", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 13: File using a symlink pointing outside");
1163 
1164 	/* Test Case 14: Attempt to open a file using ".." to navigate outside */
1165 	T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "../outside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 14: File using \"..\" to navigate outside");
1166 
1167 	/* Test Case 15: File within a nested directory */
1168 	T_EXPECT_POSIX_SUCCESS(setattrlistat(test_fd, "nested/nested_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 15: File within a nested directory");
1169 
1170 	/* Test Case 16: Symlink to a file in a nested directory */
1171 	T_EXPECT_POSIX_SUCCESS(setattrlistat(test_fd, "symlink_to_nested", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 16: Symlink to a file within the same directory");
1172 
1173 	/* Test Case 17: File using an absolute path */
1174 	T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "/etc/passwd", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 17: File using an absolute path");
1175 
1176 	/* Test Case 18: Valid symlink to parent directory */
1177 	T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "parent_symlink/outside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 18: Valid symlink to parent directory");
1178 
1179 	/* Test Case 19: Circular symlink within directory */
1180 	T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "circular_symlink", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ELOOP, "Test Case 19: Circular symlink within directory");
1181 
1182 	/* Test Case 20: Path can not escape outside at any point of the resolution */
1183 	T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "../test_dir/inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 20: Path can not escape outside at any point of the resolution");
1184 
1185 	/* Test Case 21: File using a symlink pointing to absolute path */
1186 	T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "symlink_absolute/test_dir/inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 21: File using a symlink pointing to absolute path");
1187 
1188 	T_EXPECT_POSIX_SUCCESS(close(fd), "Closing %s", INSIDE_FILE);
1189 }
1190 
1191 static volatile int stop_race = 0;
1192 static char race_mount_path[MAXPATHLEN];
1193 
1194 static void *
mounter_thread(void * arg)1195 mounter_thread(void *arg)
1196 {
1197 	while (!stop_race) {
1198 		/* Try to mount devfs over the directory */
1199 		if (mount("devfs", race_mount_path, 0, NULL) == 0) {
1200 			/* Immediately unmount it */
1201 			unmount(race_mount_path, MNT_FORCE);
1202 		}
1203 		/* Small delay to avoid overwhelming the system */
1204 		usleep(1000);
1205 	}
1206 
1207 	unmount(race_mount_path, MNT_FORCE);
1208 	return NULL;
1209 }
1210 
1211 T_DECL(resolve_beneath_mount_race,
1212     "test O_RESOLVE_BENEATH with concurrent mount operations")
1213 {
1214 	int underfd;
1215 	char underlying_dir[MAXPATHLEN];
1216 	pthread_t mounter;
1217 	time_t start_time, current_time;
1218 	int race_won = 0;
1219 
1220 	T_SETUPBEGIN;
1221 
1222 	T_ATEND(cleanup);
1223 	setup("resolve_beneath_mount_race");
1224 
1225 	/* Create the mount point and underlying directory structure */
1226 	snprintf(race_mount_path, sizeof(race_mount_path), "%s/beneath", testdir);
1227 	snprintf(underlying_dir, sizeof(underlying_dir), "%s/beneath/underlying_dir", testdir);
1228 
1229 	T_ASSERT_POSIX_SUCCESS(mkdir(race_mount_path, 0777), "Creating mount point %s", race_mount_path);
1230 	T_ASSERT_POSIX_SUCCESS(mkdir(underlying_dir, 0777), "Creating underlying directory %s", underlying_dir);
1231 
1232 	T_ASSERT_POSIX_SUCCESS((underfd = open(race_mount_path, O_SEARCH, 0777)), "Opening mount point %s", race_mount_path);
1233 
1234 	T_SETUPEND;
1235 
1236 	T_LOG("Testing O_RESOLVE_BENEATH with mount race condition for 5 seconds");
1237 
1238 	/* Start the mounter thread and immediately begin the race */
1239 	stop_race = 0;
1240 	start_time = time(NULL);
1241 	T_ASSERT_POSIX_ZERO(pthread_create(&mounter, NULL, mounter_thread, NULL), "Creating mounter thread");
1242 
1243 	/* Try to win the race for 5 seconds while the mounter thread is running */
1244 	while ((time(NULL) - start_time) < 5) {
1245 		int dotdot_fd = openat(underfd, "underlying_dir/../../../../../..", O_RESOLVE_BENEATH);
1246 		if (dotdot_fd != -1) {
1247 			char path[1024];
1248 			if (fcntl(dotdot_fd, F_GETPATH, path) == 0) {
1249 				T_LOG("Race condition exploit attempt succeeded, got path: %s", path);
1250 				/* If we got here, the old vulnerable code would have allowed this */
1251 				race_won = 1;
1252 				close(dotdot_fd);
1253 				break;
1254 			}
1255 			close(dotdot_fd);
1256 		}
1257 	}
1258 
1259 	/* Stop the mounter thread */
1260 	stop_race = 1;
1261 	T_ASSERT_POSIX_ZERO(pthread_join(mounter, NULL), "Joining mounter thread");
1262 
1263 	/* With the fix, we should never win the race */
1264 	T_EXPECT_EQ(race_won, 0, "Race condition should not succeed with the fix in place");
1265 
1266 	/* Clean up - only after the helper thread has finished */
1267 	close(underfd);
1268 	rmdir(underlying_dir);
1269 	rmdir(race_mount_path);
1270 }
1271