brintos

brintos / linux-shallow public Read only

0
0
Text · 147.5 KiB · 6788762 Raw
5404 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Landlock tests - Filesystem4 *5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>6 * Copyright © 2020 ANSSI7 * Copyright © 2020-2022 Microsoft Corporation8 */9 10#define _GNU_SOURCE11#include <asm/termbits.h>12#include <fcntl.h>13#include <libgen.h>14#include <linux/fiemap.h>15#include <linux/landlock.h>16#include <linux/magic.h>17#include <sched.h>18#include <stddef.h>19#include <stdio.h>20#include <string.h>21#include <sys/capability.h>22#include <sys/ioctl.h>23#include <sys/mount.h>24#include <sys/prctl.h>25#include <sys/sendfile.h>26#include <sys/socket.h>27#include <sys/stat.h>28#include <sys/sysmacros.h>29#include <sys/un.h>30#include <sys/vfs.h>31#include <unistd.h>32 33/*34 * Intentionally included last to work around header conflict.35 * See https://sourceware.org/glibc/wiki/Synchronizing_Headers.36 */37#include <linux/fs.h>38#include <linux/mount.h>39 40#include "common.h"41 42#ifndef renameat243int renameat2(int olddirfd, const char *oldpath, int newdirfd,44	      const char *newpath, unsigned int flags)45{46	return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath,47		       flags);48}49#endif50 51#ifndef open_tree52int open_tree(int dfd, const char *filename, unsigned int flags)53{54	return syscall(__NR_open_tree, dfd, filename, flags);55}56#endif57 58#ifndef RENAME_EXCHANGE59#define RENAME_EXCHANGE (1 << 1)60#endif61 62#define BINARY_PATH "./true"63 64/* Paths (sibling number and depth) */65static const char dir_s1d1[] = TMP_DIR "/s1d1";66static const char file1_s1d1[] = TMP_DIR "/s1d1/f1";67static const char file2_s1d1[] = TMP_DIR "/s1d1/f2";68static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2";69static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1";70static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2";71static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3";72static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1";73static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2";74 75static const char dir_s2d1[] = TMP_DIR "/s2d1";76static const char file1_s2d1[] = TMP_DIR "/s2d1/f1";77static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2";78static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1";79static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3";80static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1";81static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2";82 83static const char dir_s3d1[] = TMP_DIR "/s3d1";84static const char file1_s3d1[] = TMP_DIR "/s3d1/f1";85/* dir_s3d2 is a mount point. */86static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2";87static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3";88 89/*90 * layout1 hierarchy:91 *92 * tmp93 * ├── s1d194 * │   ├── f195 * │   ├── f296 * │   └── s1d297 * │       ├── f198 * │       ├── f299 * │       └── s1d3100 * │           ├── f1101 * │           └── f2102 * ├── s2d1103 * │   ├── f1104 * │   └── s2d2105 * │       ├── f1106 * │       └── s2d3107 * │           ├── f1108 * │           └── f2109 * └── s3d1110 *     ├── f1111 *     └── s3d2112 *         └── s3d3113 */114 115static bool fgrep(FILE *const inf, const char *const str)116{117	char line[32];118	const int slen = strlen(str);119 120	while (!feof(inf)) {121		if (!fgets(line, sizeof(line), inf))122			break;123		if (strncmp(line, str, slen))124			continue;125 126		return true;127	}128 129	return false;130}131 132static bool supports_filesystem(const char *const filesystem)133{134	char str[32];135	int len;136	bool res = true;137	FILE *const inf = fopen("/proc/filesystems", "r");138 139	/*140	 * Consider that the filesystem is supported if we cannot get the141	 * supported ones.142	 */143	if (!inf)144		return true;145 146	/* filesystem can be null for bind mounts. */147	if (!filesystem)148		goto out;149 150	len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem);151	if (len >= sizeof(str))152		/* Ignores too-long filesystem names. */153		goto out;154 155	res = fgrep(inf, str);156 157out:158	fclose(inf);159	return res;160}161 162static bool cwd_matches_fs(unsigned int fs_magic)163{164	struct statfs statfs_buf;165 166	if (!fs_magic)167		return true;168 169	if (statfs(".", &statfs_buf))170		return true;171 172	return statfs_buf.f_type == fs_magic;173}174 175static void mkdir_parents(struct __test_metadata *const _metadata,176			  const char *const path)177{178	char *walker;179	const char *parent;180	int i, err;181 182	ASSERT_NE(path[0], '\0');183	walker = strdup(path);184	ASSERT_NE(NULL, walker);185	parent = walker;186	for (i = 1; walker[i]; i++) {187		if (walker[i] != '/')188			continue;189		walker[i] = '\0';190		err = mkdir(parent, 0700);191		ASSERT_FALSE(err && errno != EEXIST)192		{193			TH_LOG("Failed to create directory \"%s\": %s", parent,194			       strerror(errno));195		}196		walker[i] = '/';197	}198	free(walker);199}200 201static void create_directory(struct __test_metadata *const _metadata,202			     const char *const path)203{204	mkdir_parents(_metadata, path);205	ASSERT_EQ(0, mkdir(path, 0700))206	{207		TH_LOG("Failed to create directory \"%s\": %s", path,208		       strerror(errno));209	}210}211 212static void create_file(struct __test_metadata *const _metadata,213			const char *const path)214{215	mkdir_parents(_metadata, path);216	ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0))217	{218		TH_LOG("Failed to create file \"%s\": %s", path,219		       strerror(errno));220	}221}222 223static int remove_path(const char *const path)224{225	char *walker;226	int i, ret, err = 0;227 228	walker = strdup(path);229	if (!walker) {230		err = ENOMEM;231		goto out;232	}233	if (unlink(path) && rmdir(path)) {234		if (errno != ENOENT && errno != ENOTDIR)235			err = errno;236		goto out;237	}238	for (i = strlen(walker); i > 0; i--) {239		if (walker[i] != '/')240			continue;241		walker[i] = '\0';242		ret = rmdir(walker);243		if (ret) {244			if (errno != ENOTEMPTY && errno != EBUSY)245				err = errno;246			goto out;247		}248		if (strcmp(walker, TMP_DIR) == 0)249			goto out;250	}251 252out:253	free(walker);254	return err;255}256 257struct mnt_opt {258	const char *const source;259	const char *const type;260	const unsigned long flags;261	const char *const data;262};263 264#define MNT_TMP_DATA "size=4m,mode=700"265 266static const struct mnt_opt mnt_tmp = {267	.type = "tmpfs",268	.data = MNT_TMP_DATA,269};270 271static int mount_opt(const struct mnt_opt *const mnt, const char *const target)272{273	return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags,274		     mnt->data);275}276 277static void prepare_layout_opt(struct __test_metadata *const _metadata,278			       const struct mnt_opt *const mnt)279{280	disable_caps(_metadata);281	umask(0077);282	create_directory(_metadata, TMP_DIR);283 284	/*285	 * Do not pollute the rest of the system: creates a private mount point286	 * for tests relying on pivot_root(2) and move_mount(2).287	 */288	set_cap(_metadata, CAP_SYS_ADMIN);289	ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP));290	ASSERT_EQ(0, mount_opt(mnt, TMP_DIR))291	{292		TH_LOG("Failed to mount the %s filesystem: %s", mnt->type,293		       strerror(errno));294		/*295		 * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP()296		 * failed, so we need to explicitly do a minimal cleanup to297		 * avoid cascading errors with other tests that don't depend on298		 * the same filesystem.299		 */300		remove_path(TMP_DIR);301	}302	ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL));303	clear_cap(_metadata, CAP_SYS_ADMIN);304}305 306static void prepare_layout(struct __test_metadata *const _metadata)307{308	prepare_layout_opt(_metadata, &mnt_tmp);309}310 311static void cleanup_layout(struct __test_metadata *const _metadata)312{313	set_cap(_metadata, CAP_SYS_ADMIN);314	if (umount(TMP_DIR)) {315		/*316		 * According to the test environment, the mount point of the317		 * current directory may be shared or not, which changes the318		 * visibility of the nested TMP_DIR mount point for the test's319		 * parent process doing this cleanup.320		 */321		ASSERT_EQ(EINVAL, errno);322	}323	clear_cap(_metadata, CAP_SYS_ADMIN);324	EXPECT_EQ(0, remove_path(TMP_DIR));325}326 327/* clang-format off */328FIXTURE(layout0) {};329/* clang-format on */330 331FIXTURE_SETUP(layout0)332{333	prepare_layout(_metadata);334}335 336FIXTURE_TEARDOWN_PARENT(layout0)337{338	cleanup_layout(_metadata);339}340 341static void create_layout1(struct __test_metadata *const _metadata)342{343	create_file(_metadata, file1_s1d1);344	create_file(_metadata, file1_s1d2);345	create_file(_metadata, file1_s1d3);346	create_file(_metadata, file2_s1d1);347	create_file(_metadata, file2_s1d2);348	create_file(_metadata, file2_s1d3);349 350	create_file(_metadata, file1_s2d1);351	create_file(_metadata, file1_s2d2);352	create_file(_metadata, file1_s2d3);353	create_file(_metadata, file2_s2d3);354 355	create_file(_metadata, file1_s3d1);356	create_directory(_metadata, dir_s3d2);357	set_cap(_metadata, CAP_SYS_ADMIN);358	ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2));359	clear_cap(_metadata, CAP_SYS_ADMIN);360 361	ASSERT_EQ(0, mkdir(dir_s3d3, 0700));362}363 364static void remove_layout1(struct __test_metadata *const _metadata)365{366	EXPECT_EQ(0, remove_path(file2_s1d3));367	EXPECT_EQ(0, remove_path(file2_s1d2));368	EXPECT_EQ(0, remove_path(file2_s1d1));369	EXPECT_EQ(0, remove_path(file1_s1d3));370	EXPECT_EQ(0, remove_path(file1_s1d2));371	EXPECT_EQ(0, remove_path(file1_s1d1));372	EXPECT_EQ(0, remove_path(dir_s1d3));373 374	EXPECT_EQ(0, remove_path(file2_s2d3));375	EXPECT_EQ(0, remove_path(file1_s2d3));376	EXPECT_EQ(0, remove_path(file1_s2d2));377	EXPECT_EQ(0, remove_path(file1_s2d1));378	EXPECT_EQ(0, remove_path(dir_s2d2));379 380	EXPECT_EQ(0, remove_path(file1_s3d1));381	EXPECT_EQ(0, remove_path(dir_s3d3));382	set_cap(_metadata, CAP_SYS_ADMIN);383	umount(dir_s3d2);384	clear_cap(_metadata, CAP_SYS_ADMIN);385	EXPECT_EQ(0, remove_path(dir_s3d2));386}387 388/* clang-format off */389FIXTURE(layout1) {};390/* clang-format on */391 392FIXTURE_SETUP(layout1)393{394	prepare_layout(_metadata);395 396	create_layout1(_metadata);397}398 399FIXTURE_TEARDOWN_PARENT(layout1)400{401	remove_layout1(_metadata);402 403	cleanup_layout(_metadata);404}405 406/*407 * This helper enables to use the ASSERT_* macros and print the line number408 * pointing to the test caller.409 */410static int test_open_rel(const int dirfd, const char *const path,411			 const int flags)412{413	int fd;414 415	/* Works with file and directories. */416	fd = openat(dirfd, path, flags | O_CLOEXEC);417	if (fd < 0)418		return errno;419	/*420	 * Mixing error codes from close(2) and open(2) should not lead to any421	 * (access type) confusion for this test.422	 */423	if (close(fd) != 0)424		return errno;425	return 0;426}427 428static int test_open(const char *const path, const int flags)429{430	return test_open_rel(AT_FDCWD, path, flags);431}432 433TEST_F_FORK(layout1, no_restriction)434{435	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));436	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));437	ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY));438	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));439	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));440	ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY));441	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));442	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));443 444	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));445	ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));446	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));447	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));448	ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY));449	ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY));450 451	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));452	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));453	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));454}455 456TEST_F_FORK(layout1, inval)457{458	struct landlock_path_beneath_attr path_beneath = {459		.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |460				  LANDLOCK_ACCESS_FS_WRITE_FILE,461		.parent_fd = -1,462	};463	struct landlock_ruleset_attr ruleset_attr = {464		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |465				     LANDLOCK_ACCESS_FS_WRITE_FILE,466	};467	int ruleset_fd;468 469	path_beneath.parent_fd =470		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);471	ASSERT_LE(0, path_beneath.parent_fd);472 473	ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);474	ASSERT_LE(0, ruleset_fd);475	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,476					&path_beneath, 0));477	/* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */478	ASSERT_EQ(EBADF, errno);479	ASSERT_EQ(0, close(ruleset_fd));480 481	ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC);482	ASSERT_LE(0, ruleset_fd);483	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,484					&path_beneath, 0));485	/* Returns EBADFD because ruleset_fd is not a valid ruleset. */486	ASSERT_EQ(EBADFD, errno);487	ASSERT_EQ(0, close(ruleset_fd));488 489	/* Gets a real ruleset. */490	ruleset_fd =491		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);492	ASSERT_LE(0, ruleset_fd);493	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,494				       &path_beneath, 0));495	ASSERT_EQ(0, close(path_beneath.parent_fd));496 497	/* Tests without O_PATH. */498	path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC);499	ASSERT_LE(0, path_beneath.parent_fd);500	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,501				       &path_beneath, 0));502	ASSERT_EQ(0, close(path_beneath.parent_fd));503 504	/* Tests with a ruleset FD. */505	path_beneath.parent_fd = ruleset_fd;506	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,507					&path_beneath, 0));508	ASSERT_EQ(EBADFD, errno);509 510	/* Checks unhandled allowed_access. */511	path_beneath.parent_fd =512		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);513	ASSERT_LE(0, path_beneath.parent_fd);514 515	/* Test with legitimate values. */516	path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE;517	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,518					&path_beneath, 0));519	ASSERT_EQ(EINVAL, errno);520	path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE;521 522	/* Tests with denied-by-default access right. */523	path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER;524	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,525					&path_beneath, 0));526	ASSERT_EQ(EINVAL, errno);527	path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER;528 529	/* Test with unknown (64-bits) value. */530	path_beneath.allowed_access |= (1ULL << 60);531	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,532					&path_beneath, 0));533	ASSERT_EQ(EINVAL, errno);534	path_beneath.allowed_access &= ~(1ULL << 60);535 536	/* Test with no access. */537	path_beneath.allowed_access = 0;538	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,539					&path_beneath, 0));540	ASSERT_EQ(ENOMSG, errno);541	path_beneath.allowed_access &= ~(1ULL << 60);542 543	ASSERT_EQ(0, close(path_beneath.parent_fd));544 545	/* Enforces the ruleset. */546	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));547	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));548 549	ASSERT_EQ(0, close(ruleset_fd));550}551 552/* clang-format off */553 554#define ACCESS_FILE ( \555	LANDLOCK_ACCESS_FS_EXECUTE | \556	LANDLOCK_ACCESS_FS_WRITE_FILE | \557	LANDLOCK_ACCESS_FS_READ_FILE | \558	LANDLOCK_ACCESS_FS_TRUNCATE | \559	LANDLOCK_ACCESS_FS_IOCTL_DEV)560 561#define ACCESS_LAST LANDLOCK_ACCESS_FS_IOCTL_DEV562 563#define ACCESS_ALL ( \564	ACCESS_FILE | \565	LANDLOCK_ACCESS_FS_READ_DIR | \566	LANDLOCK_ACCESS_FS_REMOVE_DIR | \567	LANDLOCK_ACCESS_FS_REMOVE_FILE | \568	LANDLOCK_ACCESS_FS_MAKE_CHAR | \569	LANDLOCK_ACCESS_FS_MAKE_DIR | \570	LANDLOCK_ACCESS_FS_MAKE_REG | \571	LANDLOCK_ACCESS_FS_MAKE_SOCK | \572	LANDLOCK_ACCESS_FS_MAKE_FIFO | \573	LANDLOCK_ACCESS_FS_MAKE_BLOCK | \574	LANDLOCK_ACCESS_FS_MAKE_SYM | \575	LANDLOCK_ACCESS_FS_REFER)576 577/* clang-format on */578 579TEST_F_FORK(layout1, file_and_dir_access_rights)580{581	__u64 access;582	int err;583	struct landlock_path_beneath_attr path_beneath_file = {},584					  path_beneath_dir = {};585	struct landlock_ruleset_attr ruleset_attr = {586		.handled_access_fs = ACCESS_ALL,587	};588	const int ruleset_fd =589		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);590 591	ASSERT_LE(0, ruleset_fd);592 593	/* Tests access rights for files. */594	path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);595	ASSERT_LE(0, path_beneath_file.parent_fd);596 597	/* Tests access rights for directories. */598	path_beneath_dir.parent_fd =599		open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);600	ASSERT_LE(0, path_beneath_dir.parent_fd);601 602	for (access = 1; access <= ACCESS_LAST; access <<= 1) {603		path_beneath_dir.allowed_access = access;604		ASSERT_EQ(0, landlock_add_rule(ruleset_fd,605					       LANDLOCK_RULE_PATH_BENEATH,606					       &path_beneath_dir, 0));607 608		path_beneath_file.allowed_access = access;609		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,610					&path_beneath_file, 0);611		if (access & ACCESS_FILE) {612			ASSERT_EQ(0, err);613		} else {614			ASSERT_EQ(-1, err);615			ASSERT_EQ(EINVAL, errno);616		}617	}618	ASSERT_EQ(0, close(path_beneath_file.parent_fd));619	ASSERT_EQ(0, close(path_beneath_dir.parent_fd));620	ASSERT_EQ(0, close(ruleset_fd));621}622 623TEST_F_FORK(layout0, ruleset_with_unknown_access)624{625	__u64 access_mask;626 627	for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST;628	     access_mask >>= 1) {629		struct landlock_ruleset_attr ruleset_attr = {630			.handled_access_fs = access_mask,631		};632 633		ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,634						      sizeof(ruleset_attr), 0));635		ASSERT_EQ(EINVAL, errno);636	}637}638 639TEST_F_FORK(layout0, rule_with_unknown_access)640{641	__u64 access;642	struct landlock_path_beneath_attr path_beneath = {};643	const struct landlock_ruleset_attr ruleset_attr = {644		.handled_access_fs = ACCESS_ALL,645	};646	const int ruleset_fd =647		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);648 649	ASSERT_LE(0, ruleset_fd);650 651	path_beneath.parent_fd =652		open(TMP_DIR, O_PATH | O_DIRECTORY | O_CLOEXEC);653	ASSERT_LE(0, path_beneath.parent_fd);654 655	for (access = 1ULL << 63; access != ACCESS_LAST; access >>= 1) {656		path_beneath.allowed_access = access;657		EXPECT_EQ(-1, landlock_add_rule(ruleset_fd,658						LANDLOCK_RULE_PATH_BENEATH,659						&path_beneath, 0));660		EXPECT_EQ(EINVAL, errno);661	}662	ASSERT_EQ(0, close(path_beneath.parent_fd));663	ASSERT_EQ(0, close(ruleset_fd));664}665 666TEST_F_FORK(layout1, rule_with_unhandled_access)667{668	struct landlock_ruleset_attr ruleset_attr = {669		.handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,670	};671	struct landlock_path_beneath_attr path_beneath = {};672	int ruleset_fd;673	__u64 access;674 675	ruleset_fd =676		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);677	ASSERT_LE(0, ruleset_fd);678 679	path_beneath.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);680	ASSERT_LE(0, path_beneath.parent_fd);681 682	for (access = 1; access > 0; access <<= 1) {683		int err;684 685		path_beneath.allowed_access = access;686		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,687					&path_beneath, 0);688		if (access == ruleset_attr.handled_access_fs) {689			EXPECT_EQ(0, err);690		} else {691			EXPECT_EQ(-1, err);692			EXPECT_EQ(EINVAL, errno);693		}694	}695 696	EXPECT_EQ(0, close(path_beneath.parent_fd));697	EXPECT_EQ(0, close(ruleset_fd));698}699 700static void add_path_beneath(struct __test_metadata *const _metadata,701			     const int ruleset_fd, const __u64 allowed_access,702			     const char *const path)703{704	struct landlock_path_beneath_attr path_beneath = {705		.allowed_access = allowed_access,706	};707 708	path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC);709	ASSERT_LE(0, path_beneath.parent_fd)710	{711		TH_LOG("Failed to open directory \"%s\": %s", path,712		       strerror(errno));713	}714	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,715				       &path_beneath, 0))716	{717		TH_LOG("Failed to update the ruleset with \"%s\": %s", path,718		       strerror(errno));719	}720	ASSERT_EQ(0, close(path_beneath.parent_fd));721}722 723struct rule {724	const char *path;725	__u64 access;726};727 728/* clang-format off */729 730#define ACCESS_RO ( \731	LANDLOCK_ACCESS_FS_READ_FILE | \732	LANDLOCK_ACCESS_FS_READ_DIR)733 734#define ACCESS_RW ( \735	ACCESS_RO | \736	LANDLOCK_ACCESS_FS_WRITE_FILE)737 738/* clang-format on */739 740static int create_ruleset(struct __test_metadata *const _metadata,741			  const __u64 handled_access_fs,742			  const struct rule rules[])743{744	int ruleset_fd, i;745	struct landlock_ruleset_attr ruleset_attr = {746		.handled_access_fs = handled_access_fs,747	};748 749	ASSERT_NE(NULL, rules)750	{751		TH_LOG("No rule list");752	}753	ASSERT_NE(NULL, rules[0].path)754	{755		TH_LOG("Empty rule list");756	}757 758	ruleset_fd =759		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);760	ASSERT_LE(0, ruleset_fd)761	{762		TH_LOG("Failed to create a ruleset: %s", strerror(errno));763	}764 765	for (i = 0; rules[i].path; i++) {766		if (!rules[i].access)767			continue;768 769		add_path_beneath(_metadata, ruleset_fd, rules[i].access,770				 rules[i].path);771	}772	return ruleset_fd;773}774 775TEST_F_FORK(layout0, proc_nsfs)776{777	const struct rule rules[] = {778		{779			.path = "/dev/null",780			.access = LANDLOCK_ACCESS_FS_READ_FILE |781				  LANDLOCK_ACCESS_FS_WRITE_FILE,782		},783		{},784	};785	struct landlock_path_beneath_attr path_beneath;786	const int ruleset_fd = create_ruleset(787		_metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR,788		rules);789 790	ASSERT_LE(0, ruleset_fd);791	ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));792 793	enforce_ruleset(_metadata, ruleset_fd);794 795	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));796	ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY));797	ASSERT_EQ(0, test_open("/dev/null", O_RDONLY));798	ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY));799 800	ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY));801	ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY));802	ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY));803	/*804	 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a805	 * disconnected path.  Such path cannot be identified and must then be806	 * allowed.807	 */808	ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));809 810	/*811	 * Checks that it is not possible to add nsfs-like filesystem812	 * references to a ruleset.813	 */814	path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |815				      LANDLOCK_ACCESS_FS_WRITE_FILE,816	path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC);817	ASSERT_LE(0, path_beneath.parent_fd);818	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,819					&path_beneath, 0));820	ASSERT_EQ(EBADFD, errno);821	ASSERT_EQ(0, close(path_beneath.parent_fd));822}823 824TEST_F_FORK(layout0, unpriv)825{826	const struct rule rules[] = {827		{828			.path = TMP_DIR,829			.access = ACCESS_RO,830		},831		{},832	};833	int ruleset_fd;834 835	drop_caps(_metadata);836 837	ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);838	ASSERT_LE(0, ruleset_fd);839	ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));840	ASSERT_EQ(EPERM, errno);841 842	/* enforce_ruleset() calls prctl(no_new_privs). */843	enforce_ruleset(_metadata, ruleset_fd);844	ASSERT_EQ(0, close(ruleset_fd));845}846 847TEST_F_FORK(layout1, effective_access)848{849	const struct rule rules[] = {850		{851			.path = dir_s1d2,852			.access = ACCESS_RO,853		},854		{855			.path = file1_s2d2,856			.access = LANDLOCK_ACCESS_FS_READ_FILE |857				  LANDLOCK_ACCESS_FS_WRITE_FILE,858		},859		{},860	};861	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);862	char buf;863	int reg_fd;864 865	ASSERT_LE(0, ruleset_fd);866	enforce_ruleset(_metadata, ruleset_fd);867	ASSERT_EQ(0, close(ruleset_fd));868 869	/* Tests on a directory (with or without O_PATH). */870	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));871	ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH));872	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));873	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH));874	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));875	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH));876 877	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));878	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));879	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));880	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));881 882	/* Tests on a file (with or without O_PATH). */883	ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY));884	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH));885 886	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));887 888	/* Checks effective read and write actions. */889	reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC);890	ASSERT_LE(0, reg_fd);891	ASSERT_EQ(1, write(reg_fd, ".", 1));892	ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET));893	ASSERT_EQ(1, read(reg_fd, &buf, 1));894	ASSERT_EQ('.', buf);895	ASSERT_EQ(0, close(reg_fd));896 897	/* Just in case, double-checks effective actions. */898	reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC);899	ASSERT_LE(0, reg_fd);900	ASSERT_EQ(-1, write(reg_fd, &buf, 1));901	ASSERT_EQ(EBADF, errno);902	ASSERT_EQ(0, close(reg_fd));903}904 905TEST_F_FORK(layout1, unhandled_access)906{907	const struct rule rules[] = {908		{909			.path = dir_s1d2,910			.access = ACCESS_RO,911		},912		{},913	};914	/* Here, we only handle read accesses, not write accesses. */915	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);916 917	ASSERT_LE(0, ruleset_fd);918	enforce_ruleset(_metadata, ruleset_fd);919	ASSERT_EQ(0, close(ruleset_fd));920 921	/*922	 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE,923	 * opening for write-only should be allowed, but not read-write.924	 */925	ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY));926	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));927 928	ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));929	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));930}931 932TEST_F_FORK(layout1, ruleset_overlap)933{934	const struct rule rules[] = {935		/* These rules should be ORed among them. */936		{937			.path = dir_s1d2,938			.access = LANDLOCK_ACCESS_FS_READ_FILE |939				  LANDLOCK_ACCESS_FS_WRITE_FILE,940		},941		{942			.path = dir_s1d2,943			.access = LANDLOCK_ACCESS_FS_READ_FILE |944				  LANDLOCK_ACCESS_FS_READ_DIR,945		},946		{},947	};948	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);949 950	ASSERT_LE(0, ruleset_fd);951	enforce_ruleset(_metadata, ruleset_fd);952	ASSERT_EQ(0, close(ruleset_fd));953 954	/* Checks s1d1 hierarchy. */955	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));956	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));957	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));958	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));959 960	/* Checks s1d2 hierarchy. */961	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));962	ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));963	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));964	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));965 966	/* Checks s1d3 hierarchy. */967	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));968	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));969	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));970	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));971}972 973TEST_F_FORK(layout1, layer_rule_unions)974{975	const struct rule layer1[] = {976		{977			.path = dir_s1d2,978			.access = LANDLOCK_ACCESS_FS_READ_FILE,979		},980		/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */981		{982			.path = dir_s1d3,983			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,984		},985		{},986	};987	const struct rule layer2[] = {988		/* Doesn't change anything from layer1. */989		{990			.path = dir_s1d2,991			.access = LANDLOCK_ACCESS_FS_READ_FILE |992				  LANDLOCK_ACCESS_FS_WRITE_FILE,993		},994		{},995	};996	const struct rule layer3[] = {997		/* Only allows write (but not read) to dir_s1d3. */998		{999			.path = dir_s1d2,1000			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,1001		},1002		{},1003	};1004	int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1);1005 1006	ASSERT_LE(0, ruleset_fd);1007	enforce_ruleset(_metadata, ruleset_fd);1008	ASSERT_EQ(0, close(ruleset_fd));1009 1010	/* Checks s1d1 hierarchy with layer1. */1011	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));1012	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1013	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));1014	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1015 1016	/* Checks s1d2 hierarchy with layer1. */1017	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));1018	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1019	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));1020	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1021 1022	/* Checks s1d3 hierarchy with layer1. */1023	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1024	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));1025	/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */1026	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));1027	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1028 1029	/* Doesn't change anything from layer1. */1030	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2);1031	ASSERT_LE(0, ruleset_fd);1032	enforce_ruleset(_metadata, ruleset_fd);1033	ASSERT_EQ(0, close(ruleset_fd));1034 1035	/* Checks s1d1 hierarchy with layer2. */1036	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));1037	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1038	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));1039	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1040 1041	/* Checks s1d2 hierarchy with layer2. */1042	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));1043	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1044	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));1045	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1046 1047	/* Checks s1d3 hierarchy with layer2. */1048	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1049	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));1050	/* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */1051	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));1052	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1053 1054	/* Only allows write (but not read) to dir_s1d3. */1055	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3);1056	ASSERT_LE(0, ruleset_fd);1057	enforce_ruleset(_metadata, ruleset_fd);1058	ASSERT_EQ(0, close(ruleset_fd));1059 1060	/* Checks s1d1 hierarchy with layer3. */1061	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));1062	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1063	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));1064	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1065 1066	/* Checks s1d2 hierarchy with layer3. */1067	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));1068	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1069	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));1070	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1071 1072	/* Checks s1d3 hierarchy with layer3. */1073	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));1074	ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));1075	/* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */1076	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR));1077	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1078}1079 1080TEST_F_FORK(layout1, non_overlapping_accesses)1081{1082	const struct rule layer1[] = {1083		{1084			.path = dir_s1d2,1085			.access = LANDLOCK_ACCESS_FS_MAKE_REG,1086		},1087		{},1088	};1089	const struct rule layer2[] = {1090		{1091			.path = dir_s1d3,1092			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,1093		},1094		{},1095	};1096	int ruleset_fd;1097 1098	ASSERT_EQ(0, unlink(file1_s1d1));1099	ASSERT_EQ(0, unlink(file1_s1d2));1100 1101	ruleset_fd =1102		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1);1103	ASSERT_LE(0, ruleset_fd);1104	enforce_ruleset(_metadata, ruleset_fd);1105	ASSERT_EQ(0, close(ruleset_fd));1106 1107	ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));1108	ASSERT_EQ(EACCES, errno);1109	ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));1110	ASSERT_EQ(0, unlink(file1_s1d2));1111 1112	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE,1113				    layer2);1114	ASSERT_LE(0, ruleset_fd);1115	enforce_ruleset(_metadata, ruleset_fd);1116	ASSERT_EQ(0, close(ruleset_fd));1117 1118	/* Unchanged accesses for file creation. */1119	ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));1120	ASSERT_EQ(EACCES, errno);1121	ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));1122 1123	/* Checks file removing. */1124	ASSERT_EQ(-1, unlink(file1_s1d2));1125	ASSERT_EQ(EACCES, errno);1126	ASSERT_EQ(0, unlink(file1_s1d3));1127}1128 1129TEST_F_FORK(layout1, interleaved_masked_accesses)1130{1131	/*1132	 * Checks overly restrictive rules:1133	 * layer 1: allows R   s1d1/s1d2/s1d3/file11134	 * layer 2: allows RW  s1d1/s1d2/s1d31135	 *          allows  W  s1d1/s1d21136	 *          denies R   s1d1/s1d21137	 * layer 3: allows R   s1d11138	 * layer 4: allows R   s1d1/s1d21139	 *          denies  W  s1d1/s1d21140	 * layer 5: allows R   s1d1/s1d21141	 * layer 6: allows   X ----1142	 * layer 7: allows  W  s1d1/s1d21143	 *          denies R   s1d1/s1d21144	 */1145	const struct rule layer1_read[] = {1146		/* Allows read access to file1_s1d3 with the first layer. */1147		{1148			.path = file1_s1d3,1149			.access = LANDLOCK_ACCESS_FS_READ_FILE,1150		},1151		{},1152	};1153	/* First rule with write restrictions. */1154	const struct rule layer2_read_write[] = {1155		/* Start by granting read-write access via its parent directory... */1156		{1157			.path = dir_s1d3,1158			.access = LANDLOCK_ACCESS_FS_READ_FILE |1159				  LANDLOCK_ACCESS_FS_WRITE_FILE,1160		},1161		/* ...but also denies read access via its grandparent directory. */1162		{1163			.path = dir_s1d2,1164			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,1165		},1166		{},1167	};1168	const struct rule layer3_read[] = {1169		/* Allows read access via its great-grandparent directory. */1170		{1171			.path = dir_s1d1,1172			.access = LANDLOCK_ACCESS_FS_READ_FILE,1173		},1174		{},1175	};1176	const struct rule layer4_read_write[] = {1177		/*1178		 * Try to confuse the deny access by denying write (but not1179		 * read) access via its grandparent directory.1180		 */1181		{1182			.path = dir_s1d2,1183			.access = LANDLOCK_ACCESS_FS_READ_FILE,1184		},1185		{},1186	};1187	const struct rule layer5_read[] = {1188		/*1189		 * Try to override layer2's deny read access by explicitly1190		 * allowing read access via file1_s1d3's grandparent.1191		 */1192		{1193			.path = dir_s1d2,1194			.access = LANDLOCK_ACCESS_FS_READ_FILE,1195		},1196		{},1197	};1198	const struct rule layer6_execute[] = {1199		/*1200		 * Restricts an unrelated file hierarchy with a new access1201		 * (non-overlapping) type.1202		 */1203		{1204			.path = dir_s2d1,1205			.access = LANDLOCK_ACCESS_FS_EXECUTE,1206		},1207		{},1208	};1209	const struct rule layer7_read_write[] = {1210		/*1211		 * Finally, denies read access to file1_s1d3 via its1212		 * grandparent.1213		 */1214		{1215			.path = dir_s1d2,1216			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,1217		},1218		{},1219	};1220	int ruleset_fd;1221 1222	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,1223				    layer1_read);1224	ASSERT_LE(0, ruleset_fd);1225	enforce_ruleset(_metadata, ruleset_fd);1226	ASSERT_EQ(0, close(ruleset_fd));1227 1228	/* Checks that read access is granted for file1_s1d3 with layer 1. */1229	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));1230	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1231	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));1232 1233	ruleset_fd = create_ruleset(_metadata,1234				    LANDLOCK_ACCESS_FS_READ_FILE |1235					    LANDLOCK_ACCESS_FS_WRITE_FILE,1236				    layer2_read_write);1237	ASSERT_LE(0, ruleset_fd);1238	enforce_ruleset(_metadata, ruleset_fd);1239	ASSERT_EQ(0, close(ruleset_fd));1240 1241	/* Checks that previous access rights are unchanged with layer 2. */1242	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));1243	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1244	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));1245 1246	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,1247				    layer3_read);1248	ASSERT_LE(0, ruleset_fd);1249	enforce_ruleset(_metadata, ruleset_fd);1250	ASSERT_EQ(0, close(ruleset_fd));1251 1252	/* Checks that previous access rights are unchanged with layer 3. */1253	ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));1254	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1255	ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));1256 1257	/* This time, denies write access for the file hierarchy. */1258	ruleset_fd = create_ruleset(_metadata,1259				    LANDLOCK_ACCESS_FS_READ_FILE |1260					    LANDLOCK_ACCESS_FS_WRITE_FILE,1261				    layer4_read_write);1262	ASSERT_LE(0, ruleset_fd);1263	enforce_ruleset(_metadata, ruleset_fd);1264	ASSERT_EQ(0, close(ruleset_fd));1265 1266	/*1267	 * Checks that the only change with layer 4 is that write access is1268	 * denied.1269	 */1270	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1271	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1272	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1273	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));1274 1275	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,1276				    layer5_read);1277	ASSERT_LE(0, ruleset_fd);1278	enforce_ruleset(_metadata, ruleset_fd);1279	ASSERT_EQ(0, close(ruleset_fd));1280 1281	/* Checks that previous access rights are unchanged with layer 5. */1282	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1283	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1284	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));1285	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1286 1287	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE,1288				    layer6_execute);1289	ASSERT_LE(0, ruleset_fd);1290	enforce_ruleset(_metadata, ruleset_fd);1291	ASSERT_EQ(0, close(ruleset_fd));1292 1293	/* Checks that previous access rights are unchanged with layer 6. */1294	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1295	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1296	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));1297	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1298 1299	ruleset_fd = create_ruleset(_metadata,1300				    LANDLOCK_ACCESS_FS_READ_FILE |1301					    LANDLOCK_ACCESS_FS_WRITE_FILE,1302				    layer7_read_write);1303	ASSERT_LE(0, ruleset_fd);1304	enforce_ruleset(_metadata, ruleset_fd);1305	ASSERT_EQ(0, close(ruleset_fd));1306 1307	/* Checks read access is now denied with layer 7. */1308	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));1309	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1310	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));1311	ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1312}1313 1314TEST_F_FORK(layout1, inherit_subset)1315{1316	const struct rule rules[] = {1317		{1318			.path = dir_s1d2,1319			.access = LANDLOCK_ACCESS_FS_READ_FILE |1320				  LANDLOCK_ACCESS_FS_READ_DIR,1321		},1322		{},1323	};1324	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1325 1326	ASSERT_LE(0, ruleset_fd);1327	enforce_ruleset(_metadata, ruleset_fd);1328 1329	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1330	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1331 1332	/* Write access is forbidden. */1333	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1334	/* Readdir access is allowed. */1335	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1336 1337	/* Write access is forbidden. */1338	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1339	/* Readdir access is allowed. */1340	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1341 1342	/*1343	 * Tests shared rule extension: the following rules should not grant1344	 * any new access, only remove some.  Once enforced, these rules are1345	 * ANDed with the previous ones.1346	 */1347	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,1348			 dir_s1d2);1349	/*1350	 * According to ruleset_fd, dir_s1d2 should now have the1351	 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE1352	 * access rights (even if this directory is opened a second time).1353	 * However, when enforcing this updated ruleset, the ruleset tied to1354	 * the current process (i.e. its domain) will still only have the1355	 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and1356	 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but1357	 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would1358	 * be a privilege escalation.1359	 */1360	enforce_ruleset(_metadata, ruleset_fd);1361 1362	/* Same tests and results as above. */1363	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1364	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1365 1366	/* It is still forbidden to write in file1_s1d2. */1367	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1368	/* Readdir access is still allowed. */1369	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1370 1371	/* It is still forbidden to write in file1_s1d3. */1372	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1373	/* Readdir access is still allowed. */1374	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1375 1376	/*1377	 * Try to get more privileges by adding new access rights to the parent1378	 * directory: dir_s1d1.1379	 */1380	add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1);1381	enforce_ruleset(_metadata, ruleset_fd);1382 1383	/* Same tests and results as above. */1384	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1385	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1386 1387	/* It is still forbidden to write in file1_s1d2. */1388	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1389	/* Readdir access is still allowed. */1390	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1391 1392	/* It is still forbidden to write in file1_s1d3. */1393	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1394	/* Readdir access is still allowed. */1395	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1396 1397	/*1398	 * Now, dir_s1d3 get a new rule tied to it, only allowing1399	 * LANDLOCK_ACCESS_FS_WRITE_FILE.  The (kernel internal) difference is1400	 * that there was no rule tied to it before.1401	 */1402	add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,1403			 dir_s1d3);1404	enforce_ruleset(_metadata, ruleset_fd);1405	ASSERT_EQ(0, close(ruleset_fd));1406 1407	/*1408	 * Same tests and results as above, except for open(dir_s1d3) which is1409	 * now denied because the new rule mask the rule previously inherited1410	 * from dir_s1d2.1411	 */1412 1413	/* Same tests and results as above. */1414	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1415	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1416 1417	/* It is still forbidden to write in file1_s1d2. */1418	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1419	/* Readdir access is still allowed. */1420	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1421 1422	/* It is still forbidden to write in file1_s1d3. */1423	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1424	/*1425	 * Readdir of dir_s1d3 is still allowed because of the OR policy inside1426	 * the same layer.1427	 */1428	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1429}1430 1431TEST_F_FORK(layout1, inherit_superset)1432{1433	const struct rule rules[] = {1434		{1435			.path = dir_s1d3,1436			.access = ACCESS_RO,1437		},1438		{},1439	};1440	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1441 1442	ASSERT_LE(0, ruleset_fd);1443	enforce_ruleset(_metadata, ruleset_fd);1444 1445	/* Readdir access is denied for dir_s1d2. */1446	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1447	/* Readdir access is allowed for dir_s1d3. */1448	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1449	/* File access is allowed for file1_s1d3. */1450	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1451 1452	/* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */1453	add_path_beneath(_metadata, ruleset_fd,1454			 LANDLOCK_ACCESS_FS_READ_FILE |1455				 LANDLOCK_ACCESS_FS_READ_DIR,1456			 dir_s1d2);1457	enforce_ruleset(_metadata, ruleset_fd);1458	ASSERT_EQ(0, close(ruleset_fd));1459 1460	/* Readdir access is still denied for dir_s1d2. */1461	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1462	/* Readdir access is still allowed for dir_s1d3. */1463	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1464	/* File access is still allowed for file1_s1d3. */1465	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1466}1467 1468TEST_F_FORK(layout0, max_layers)1469{1470	int i, err;1471	const struct rule rules[] = {1472		{1473			.path = TMP_DIR,1474			.access = ACCESS_RO,1475		},1476		{},1477	};1478	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1479 1480	ASSERT_LE(0, ruleset_fd);1481	for (i = 0; i < 16; i++)1482		enforce_ruleset(_metadata, ruleset_fd);1483 1484	for (i = 0; i < 2; i++) {1485		err = landlock_restrict_self(ruleset_fd, 0);1486		ASSERT_EQ(-1, err);1487		ASSERT_EQ(E2BIG, errno);1488	}1489	ASSERT_EQ(0, close(ruleset_fd));1490}1491 1492TEST_F_FORK(layout1, empty_or_same_ruleset)1493{1494	struct landlock_ruleset_attr ruleset_attr = {};1495	int ruleset_fd;1496 1497	/* Tests empty handled_access_fs. */1498	ruleset_fd =1499		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);1500	ASSERT_LE(-1, ruleset_fd);1501	ASSERT_EQ(ENOMSG, errno);1502 1503	/* Enforces policy which deny read access to all files. */1504	ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE;1505	ruleset_fd =1506		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);1507	ASSERT_LE(0, ruleset_fd);1508	enforce_ruleset(_metadata, ruleset_fd);1509	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));1510	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));1511 1512	/* Nests a policy which deny read access to all directories. */1513	ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR;1514	ruleset_fd =1515		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);1516	ASSERT_LE(0, ruleset_fd);1517	enforce_ruleset(_metadata, ruleset_fd);1518	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));1519	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));1520 1521	/* Enforces a second time with the same ruleset. */1522	enforce_ruleset(_metadata, ruleset_fd);1523	ASSERT_EQ(0, close(ruleset_fd));1524}1525 1526TEST_F_FORK(layout1, rule_on_mountpoint)1527{1528	const struct rule rules[] = {1529		{1530			.path = dir_s1d1,1531			.access = ACCESS_RO,1532		},1533		{1534			/* dir_s3d2 is a mount point. */1535			.path = dir_s3d2,1536			.access = ACCESS_RO,1537		},1538		{},1539	};1540	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1541 1542	ASSERT_LE(0, ruleset_fd);1543	enforce_ruleset(_metadata, ruleset_fd);1544	ASSERT_EQ(0, close(ruleset_fd));1545 1546	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));1547 1548	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));1549 1550	ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY));1551	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));1552	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));1553}1554 1555TEST_F_FORK(layout1, rule_over_mountpoint)1556{1557	const struct rule rules[] = {1558		{1559			.path = dir_s1d1,1560			.access = ACCESS_RO,1561		},1562		{1563			/* dir_s3d2 is a mount point. */1564			.path = dir_s3d1,1565			.access = ACCESS_RO,1566		},1567		{},1568	};1569	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1570 1571	ASSERT_LE(0, ruleset_fd);1572	enforce_ruleset(_metadata, ruleset_fd);1573	ASSERT_EQ(0, close(ruleset_fd));1574 1575	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));1576 1577	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));1578 1579	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));1580	ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));1581	ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));1582}1583 1584/*1585 * This test verifies that we can apply a landlock rule on the root directory1586 * (which might require special handling).1587 */1588TEST_F_FORK(layout1, rule_over_root_allow_then_deny)1589{1590	struct rule rules[] = {1591		{1592			.path = "/",1593			.access = ACCESS_RO,1594		},1595		{},1596	};1597	int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1598 1599	ASSERT_LE(0, ruleset_fd);1600	enforce_ruleset(_metadata, ruleset_fd);1601	ASSERT_EQ(0, close(ruleset_fd));1602 1603	/* Checks allowed access. */1604	ASSERT_EQ(0, test_open("/", O_RDONLY));1605	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));1606 1607	rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE;1608	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1609	ASSERT_LE(0, ruleset_fd);1610	enforce_ruleset(_metadata, ruleset_fd);1611	ASSERT_EQ(0, close(ruleset_fd));1612 1613	/* Checks denied access (on a directory). */1614	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));1615	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));1616}1617 1618TEST_F_FORK(layout1, rule_over_root_deny)1619{1620	const struct rule rules[] = {1621		{1622			.path = "/",1623			.access = LANDLOCK_ACCESS_FS_READ_FILE,1624		},1625		{},1626	};1627	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1628 1629	ASSERT_LE(0, ruleset_fd);1630	enforce_ruleset(_metadata, ruleset_fd);1631	ASSERT_EQ(0, close(ruleset_fd));1632 1633	/* Checks denied access (on a directory). */1634	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));1635	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));1636}1637 1638TEST_F_FORK(layout1, rule_inside_mount_ns)1639{1640	const struct rule rules[] = {1641		{1642			.path = "s3d3",1643			.access = ACCESS_RO,1644		},1645		{},1646	};1647	int ruleset_fd;1648 1649	set_cap(_metadata, CAP_SYS_ADMIN);1650	ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3))1651	{1652		TH_LOG("Failed to pivot root: %s", strerror(errno));1653	};1654	ASSERT_EQ(0, chdir("/"));1655	clear_cap(_metadata, CAP_SYS_ADMIN);1656 1657	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1658	ASSERT_LE(0, ruleset_fd);1659	enforce_ruleset(_metadata, ruleset_fd);1660	ASSERT_EQ(0, close(ruleset_fd));1661 1662	ASSERT_EQ(0, test_open("s3d3", O_RDONLY));1663	ASSERT_EQ(EACCES, test_open("/", O_RDONLY));1664}1665 1666TEST_F_FORK(layout1, mount_and_pivot)1667{1668	const struct rule rules[] = {1669		{1670			.path = dir_s3d2,1671			.access = ACCESS_RO,1672		},1673		{},1674	};1675	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1676 1677	ASSERT_LE(0, ruleset_fd);1678	enforce_ruleset(_metadata, ruleset_fd);1679	ASSERT_EQ(0, close(ruleset_fd));1680 1681	set_cap(_metadata, CAP_SYS_ADMIN);1682	ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));1683	ASSERT_EQ(EPERM, errno);1684	ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));1685	ASSERT_EQ(EPERM, errno);1686	clear_cap(_metadata, CAP_SYS_ADMIN);1687}1688 1689TEST_F_FORK(layout1, move_mount)1690{1691	const struct rule rules[] = {1692		{1693			.path = dir_s3d2,1694			.access = ACCESS_RO,1695		},1696		{},1697	};1698	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1699 1700	ASSERT_LE(0, ruleset_fd);1701 1702	set_cap(_metadata, CAP_SYS_ADMIN);1703	ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,1704			     dir_s1d2, 0))1705	{1706		TH_LOG("Failed to move mount: %s", strerror(errno));1707	}1708 1709	ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,1710			     dir_s3d2, 0));1711	clear_cap(_metadata, CAP_SYS_ADMIN);1712 1713	enforce_ruleset(_metadata, ruleset_fd);1714	ASSERT_EQ(0, close(ruleset_fd));1715 1716	set_cap(_metadata, CAP_SYS_ADMIN);1717	ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,1718			      dir_s1d2, 0));1719	ASSERT_EQ(EPERM, errno);1720	clear_cap(_metadata, CAP_SYS_ADMIN);1721}1722 1723TEST_F_FORK(layout1, topology_changes_with_net_only)1724{1725	const struct landlock_ruleset_attr ruleset_net = {1726		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |1727				      LANDLOCK_ACCESS_NET_CONNECT_TCP,1728	};1729	int ruleset_fd;1730 1731	/* Add network restrictions. */1732	ruleset_fd =1733		landlock_create_ruleset(&ruleset_net, sizeof(ruleset_net), 0);1734	ASSERT_LE(0, ruleset_fd);1735	enforce_ruleset(_metadata, ruleset_fd);1736	ASSERT_EQ(0, close(ruleset_fd));1737 1738	/* Mount, remount, move_mount, umount, and pivot_root checks. */1739	set_cap(_metadata, CAP_SYS_ADMIN);1740	ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s1d2));1741	ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC, NULL));1742	ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,1743			     dir_s2d2, 0));1744	ASSERT_EQ(0, umount(dir_s2d2));1745	ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));1746	ASSERT_EQ(0, chdir("/"));1747	clear_cap(_metadata, CAP_SYS_ADMIN);1748}1749 1750TEST_F_FORK(layout1, topology_changes_with_net_and_fs)1751{1752	const struct landlock_ruleset_attr ruleset_net_fs = {1753		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |1754				      LANDLOCK_ACCESS_NET_CONNECT_TCP,1755		.handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,1756	};1757	int ruleset_fd;1758 1759	/* Add network and filesystem restrictions. */1760	ruleset_fd = landlock_create_ruleset(&ruleset_net_fs,1761					     sizeof(ruleset_net_fs), 0);1762	ASSERT_LE(0, ruleset_fd);1763	enforce_ruleset(_metadata, ruleset_fd);1764	ASSERT_EQ(0, close(ruleset_fd));1765 1766	/* Mount, remount, move_mount, umount, and pivot_root checks. */1767	set_cap(_metadata, CAP_SYS_ADMIN);1768	ASSERT_EQ(-1, mount_opt(&mnt_tmp, dir_s1d2));1769	ASSERT_EQ(EPERM, errno);1770	ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC, NULL));1771	ASSERT_EQ(EPERM, errno);1772	ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,1773			      dir_s2d2, 0));1774	ASSERT_EQ(EPERM, errno);1775	ASSERT_EQ(-1, umount(dir_s3d2));1776	ASSERT_EQ(EPERM, errno);1777	ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));1778	ASSERT_EQ(EPERM, errno);1779	clear_cap(_metadata, CAP_SYS_ADMIN);1780}1781 1782TEST_F_FORK(layout1, release_inodes)1783{1784	const struct rule rules[] = {1785		{1786			.path = dir_s1d1,1787			.access = ACCESS_RO,1788		},1789		{1790			.path = dir_s3d2,1791			.access = ACCESS_RO,1792		},1793		{1794			.path = dir_s3d3,1795			.access = ACCESS_RO,1796		},1797		{},1798	};1799	const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1800 1801	ASSERT_LE(0, ruleset_fd);1802	/* Unmount a file hierarchy while it is being used by a ruleset. */1803	set_cap(_metadata, CAP_SYS_ADMIN);1804	ASSERT_EQ(0, umount(dir_s3d2));1805	clear_cap(_metadata, CAP_SYS_ADMIN);1806 1807	enforce_ruleset(_metadata, ruleset_fd);1808	ASSERT_EQ(0, close(ruleset_fd));1809 1810	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));1811	ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY));1812	/* This dir_s3d3 would not be allowed and does not exist anyway. */1813	ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY));1814}1815 1816enum relative_access {1817	REL_OPEN,1818	REL_CHDIR,1819	REL_CHROOT_ONLY,1820	REL_CHROOT_CHDIR,1821};1822 1823static void test_relative_path(struct __test_metadata *const _metadata,1824			       const enum relative_access rel)1825{1826	/*1827	 * Common layer to check that chroot doesn't ignore it (i.e. a chroot1828	 * is not a disconnected root directory).1829	 */1830	const struct rule layer1_base[] = {1831		{1832			.path = TMP_DIR,1833			.access = ACCESS_RO,1834		},1835		{},1836	};1837	const struct rule layer2_subs[] = {1838		{1839			.path = dir_s1d2,1840			.access = ACCESS_RO,1841		},1842		{1843			.path = dir_s2d2,1844			.access = ACCESS_RO,1845		},1846		{},1847	};1848	int dirfd, ruleset_fd;1849 1850	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);1851	ASSERT_LE(0, ruleset_fd);1852	enforce_ruleset(_metadata, ruleset_fd);1853	ASSERT_EQ(0, close(ruleset_fd));1854 1855	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs);1856 1857	ASSERT_LE(0, ruleset_fd);1858	switch (rel) {1859	case REL_OPEN:1860	case REL_CHDIR:1861		break;1862	case REL_CHROOT_ONLY:1863		ASSERT_EQ(0, chdir(dir_s2d2));1864		break;1865	case REL_CHROOT_CHDIR:1866		ASSERT_EQ(0, chdir(dir_s1d2));1867		break;1868	default:1869		ASSERT_TRUE(false);1870		return;1871	}1872 1873	set_cap(_metadata, CAP_SYS_CHROOT);1874	enforce_ruleset(_metadata, ruleset_fd);1875 1876	switch (rel) {1877	case REL_OPEN:1878		dirfd = open(dir_s1d2, O_DIRECTORY);1879		ASSERT_LE(0, dirfd);1880		break;1881	case REL_CHDIR:1882		ASSERT_EQ(0, chdir(dir_s1d2));1883		dirfd = AT_FDCWD;1884		break;1885	case REL_CHROOT_ONLY:1886		/* Do chroot into dir_s1d2 (relative to dir_s2d2). */1887		ASSERT_EQ(0, chroot("../../s1d1/s1d2"))1888		{1889			TH_LOG("Failed to chroot: %s", strerror(errno));1890		}1891		dirfd = AT_FDCWD;1892		break;1893	case REL_CHROOT_CHDIR:1894		/* Do chroot into dir_s1d2. */1895		ASSERT_EQ(0, chroot("."))1896		{1897			TH_LOG("Failed to chroot: %s", strerror(errno));1898		}1899		dirfd = AT_FDCWD;1900		break;1901	}1902 1903	ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES,1904		  test_open_rel(dirfd, "..", O_RDONLY));1905	ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY));1906 1907	if (rel == REL_CHROOT_ONLY) {1908		/* The current directory is dir_s2d2. */1909		ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY));1910	} else {1911		/* The current directory is dir_s1d2. */1912		ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY));1913	}1914 1915	if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) {1916		/* Checks the root dir_s1d2. */1917		ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY));1918		ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY));1919		ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY));1920		ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY));1921	}1922 1923	if (rel != REL_CHROOT_CHDIR) {1924		ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY));1925		ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY));1926		ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3",1927					   O_RDONLY));1928 1929		ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY));1930		ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY));1931		ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3",1932					   O_RDONLY));1933	}1934 1935	if (rel == REL_OPEN)1936		ASSERT_EQ(0, close(dirfd));1937	ASSERT_EQ(0, close(ruleset_fd));1938}1939 1940TEST_F_FORK(layout1, relative_open)1941{1942	test_relative_path(_metadata, REL_OPEN);1943}1944 1945TEST_F_FORK(layout1, relative_chdir)1946{1947	test_relative_path(_metadata, REL_CHDIR);1948}1949 1950TEST_F_FORK(layout1, relative_chroot_only)1951{1952	test_relative_path(_metadata, REL_CHROOT_ONLY);1953}1954 1955TEST_F_FORK(layout1, relative_chroot_chdir)1956{1957	test_relative_path(_metadata, REL_CHROOT_CHDIR);1958}1959 1960static void copy_binary(struct __test_metadata *const _metadata,1961			const char *const dst_path)1962{1963	int dst_fd, src_fd;1964	struct stat statbuf;1965 1966	dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC);1967	ASSERT_LE(0, dst_fd)1968	{1969		TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno));1970	}1971	src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC);1972	ASSERT_LE(0, src_fd)1973	{1974		TH_LOG("Failed to open \"" BINARY_PATH "\": %s",1975		       strerror(errno));1976	}1977	ASSERT_EQ(0, fstat(src_fd, &statbuf));1978	ASSERT_EQ(statbuf.st_size,1979		  sendfile(dst_fd, src_fd, 0, statbuf.st_size));1980	ASSERT_EQ(0, close(src_fd));1981	ASSERT_EQ(0, close(dst_fd));1982}1983 1984static void test_execute(struct __test_metadata *const _metadata, const int err,1985			 const char *const path)1986{1987	int status;1988	char *const argv[] = { (char *)path, NULL };1989	const pid_t child = fork();1990 1991	ASSERT_LE(0, child);1992	if (child == 0) {1993		ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL))1994		{1995			TH_LOG("Failed to execute \"%s\": %s", path,1996			       strerror(errno));1997		};1998		ASSERT_EQ(err, errno);1999		_exit(__test_passed(_metadata) ? 2 : 1);2000		return;2001	}2002	ASSERT_EQ(child, waitpid(child, &status, 0));2003	ASSERT_EQ(1, WIFEXITED(status));2004	ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status))2005	{2006		TH_LOG("Unexpected return code for \"%s\": %s", path,2007		       strerror(errno));2008	};2009}2010 2011TEST_F_FORK(layout1, execute)2012{2013	const struct rule rules[] = {2014		{2015			.path = dir_s1d2,2016			.access = LANDLOCK_ACCESS_FS_EXECUTE,2017		},2018		{},2019	};2020	const int ruleset_fd =2021		create_ruleset(_metadata, rules[0].access, rules);2022 2023	ASSERT_LE(0, ruleset_fd);2024	copy_binary(_metadata, file1_s1d1);2025	copy_binary(_metadata, file1_s1d2);2026	copy_binary(_metadata, file1_s1d3);2027 2028	enforce_ruleset(_metadata, ruleset_fd);2029	ASSERT_EQ(0, close(ruleset_fd));2030 2031	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));2032	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));2033	test_execute(_metadata, EACCES, file1_s1d1);2034 2035	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));2036	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));2037	test_execute(_metadata, 0, file1_s1d2);2038 2039	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));2040	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));2041	test_execute(_metadata, 0, file1_s1d3);2042}2043 2044TEST_F_FORK(layout1, link)2045{2046	const struct rule layer1[] = {2047		{2048			.path = dir_s1d2,2049			.access = LANDLOCK_ACCESS_FS_MAKE_REG,2050		},2051		{},2052	};2053	const struct rule layer2[] = {2054		{2055			.path = dir_s1d3,2056			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,2057		},2058		{},2059	};2060	int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);2061 2062	ASSERT_LE(0, ruleset_fd);2063 2064	ASSERT_EQ(0, unlink(file1_s1d1));2065	ASSERT_EQ(0, unlink(file1_s1d2));2066	ASSERT_EQ(0, unlink(file1_s1d3));2067 2068	enforce_ruleset(_metadata, ruleset_fd);2069	ASSERT_EQ(0, close(ruleset_fd));2070 2071	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));2072	ASSERT_EQ(EACCES, errno);2073 2074	/* Denies linking because of reparenting. */2075	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));2076	ASSERT_EQ(EXDEV, errno);2077	ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));2078	ASSERT_EQ(EXDEV, errno);2079	ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));2080	ASSERT_EQ(EXDEV, errno);2081 2082	ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));2083	ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));2084 2085	/* Prepares for next unlinks. */2086	ASSERT_EQ(0, unlink(file2_s1d2));2087	ASSERT_EQ(0, unlink(file2_s1d3));2088 2089	ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);2090	ASSERT_LE(0, ruleset_fd);2091	enforce_ruleset(_metadata, ruleset_fd);2092	ASSERT_EQ(0, close(ruleset_fd));2093 2094	/* Checks that linkind doesn't require the ability to delete a file. */2095	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));2096	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));2097}2098 2099static int test_rename(const char *const oldpath, const char *const newpath)2100{2101	if (rename(oldpath, newpath))2102		return errno;2103	return 0;2104}2105 2106static int test_exchange(const char *const oldpath, const char *const newpath)2107{2108	if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE))2109		return errno;2110	return 0;2111}2112 2113TEST_F_FORK(layout1, rename_file)2114{2115	const struct rule rules[] = {2116		{2117			.path = dir_s1d3,2118			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,2119		},2120		{2121			.path = dir_s2d2,2122			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,2123		},2124		{},2125	};2126	const int ruleset_fd =2127		create_ruleset(_metadata, rules[0].access, rules);2128 2129	ASSERT_LE(0, ruleset_fd);2130 2131	ASSERT_EQ(0, unlink(file1_s1d2));2132 2133	enforce_ruleset(_metadata, ruleset_fd);2134	ASSERT_EQ(0, close(ruleset_fd));2135 2136	/*2137	 * Tries to replace a file, from a directory that allows file removal,2138	 * but to a different directory (which also allows file removal).2139	 */2140	ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3));2141	ASSERT_EQ(EXDEV, errno);2142	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3,2143				RENAME_EXCHANGE));2144	ASSERT_EQ(EXDEV, errno);2145	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,2146				RENAME_EXCHANGE));2147	ASSERT_EQ(EXDEV, errno);2148 2149	/*2150	 * Tries to replace a file, from a directory that denies file removal,2151	 * to a different directory (which allows file removal).2152	 */2153	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));2154	ASSERT_EQ(EACCES, errno);2155	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3,2156				RENAME_EXCHANGE));2157	ASSERT_EQ(EACCES, errno);2158	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3,2159				RENAME_EXCHANGE));2160	ASSERT_EQ(EXDEV, errno);2161 2162	/* Exchanges files and directories that partially allow removal. */2163	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1,2164				RENAME_EXCHANGE));2165	ASSERT_EQ(EACCES, errno);2166	/* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */2167	ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1));2168	ASSERT_EQ(EACCES, errno);2169	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2,2170				RENAME_EXCHANGE));2171	ASSERT_EQ(EACCES, errno);2172	/* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */2173	ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));2174	ASSERT_EQ(EACCES, errno);2175 2176	/* Renames files with different parents. */2177	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));2178	ASSERT_EQ(EXDEV, errno);2179	ASSERT_EQ(0, unlink(file1_s1d3));2180	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));2181	ASSERT_EQ(EACCES, errno);2182 2183	/* Exchanges and renames files with same parent. */2184	ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3,2185			       RENAME_EXCHANGE));2186	ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3));2187 2188	/* Exchanges files and directories with same parent, twice. */2189	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,2190			       RENAME_EXCHANGE));2191	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,2192			       RENAME_EXCHANGE));2193}2194 2195TEST_F_FORK(layout1, rename_dir)2196{2197	const struct rule rules[] = {2198		{2199			.path = dir_s1d2,2200			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,2201		},2202		{2203			.path = dir_s2d1,2204			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,2205		},2206		{},2207	};2208	const int ruleset_fd =2209		create_ruleset(_metadata, rules[0].access, rules);2210 2211	ASSERT_LE(0, ruleset_fd);2212 2213	/* Empties dir_s1d3 to allow renaming. */2214	ASSERT_EQ(0, unlink(file1_s1d3));2215	ASSERT_EQ(0, unlink(file2_s1d3));2216 2217	enforce_ruleset(_metadata, ruleset_fd);2218	ASSERT_EQ(0, close(ruleset_fd));2219 2220	/* Exchanges and renames directory to a different parent. */2221	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,2222				RENAME_EXCHANGE));2223	ASSERT_EQ(EXDEV, errno);2224	ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3));2225	ASSERT_EQ(EXDEV, errno);2226	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,2227				RENAME_EXCHANGE));2228	ASSERT_EQ(EXDEV, errno);2229 2230	/*2231	 * Exchanges directory to the same parent, which doesn't allow2232	 * directory removal.2233	 */2234	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1,2235				RENAME_EXCHANGE));2236	ASSERT_EQ(EACCES, errno);2237	/* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */2238	ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1));2239	ASSERT_EQ(EACCES, errno);2240	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2,2241				RENAME_EXCHANGE));2242	ASSERT_EQ(EACCES, errno);2243	/* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */2244	ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));2245	ASSERT_EQ(EACCES, errno);2246 2247	/*2248	 * Exchanges and renames directory to the same parent, which allows2249	 * directory removal.2250	 */2251	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2,2252			       RENAME_EXCHANGE));2253	ASSERT_EQ(0, unlink(dir_s1d3));2254	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));2255	ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3));2256	ASSERT_EQ(0, rmdir(dir_s1d3));2257}2258 2259TEST_F_FORK(layout1, reparent_refer)2260{2261	const struct rule layer1[] = {2262		{2263			.path = dir_s1d2,2264			.access = LANDLOCK_ACCESS_FS_REFER,2265		},2266		{2267			.path = dir_s2d2,2268			.access = LANDLOCK_ACCESS_FS_REFER,2269		},2270		{},2271	};2272	int ruleset_fd =2273		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1);2274 2275	ASSERT_LE(0, ruleset_fd);2276	enforce_ruleset(_metadata, ruleset_fd);2277	ASSERT_EQ(0, close(ruleset_fd));2278 2279	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1));2280	ASSERT_EQ(EXDEV, errno);2281	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2));2282	ASSERT_EQ(EXDEV, errno);2283	ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3));2284	ASSERT_EQ(EXDEV, errno);2285 2286	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1));2287	ASSERT_EQ(EXDEV, errno);2288	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2));2289	ASSERT_EQ(EXDEV, errno);2290	/*2291	 * Moving should only be allowed when the source and the destination2292	 * parent directory have REFER.2293	 */2294	ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3));2295	ASSERT_EQ(ENOTEMPTY, errno);2296	ASSERT_EQ(0, unlink(file1_s2d3));2297	ASSERT_EQ(0, unlink(file2_s2d3));2298	ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3));2299}2300 2301/* Checks renames beneath dir_s1d1. */2302static void refer_denied_by_default(struct __test_metadata *const _metadata,2303				    const struct rule layer1[],2304				    const int layer1_err,2305				    const struct rule layer2[])2306{2307	int ruleset_fd;2308 2309	ASSERT_EQ(0, unlink(file1_s1d2));2310 2311	ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);2312	ASSERT_LE(0, ruleset_fd);2313	enforce_ruleset(_metadata, ruleset_fd);2314	ASSERT_EQ(0, close(ruleset_fd));2315 2316	/*2317	 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to2318	 * layer1_err), then it allows some different-parent renames and links.2319	 */2320	ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2));2321	if (layer1_err == 0)2322		ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1));2323	ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2));2324	ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1));2325 2326	ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);2327	ASSERT_LE(0, ruleset_fd);2328	enforce_ruleset(_metadata, ruleset_fd);2329	ASSERT_EQ(0, close(ruleset_fd));2330 2331	/*2332	 * Now, either the first or the second layer does not handle2333	 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent2334	 * renames and links are denied, thus making the layer handling2335	 * LANDLOCK_ACCESS_FS_REFER null and void.2336	 */2337	ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2));2338	ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2));2339	ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1));2340}2341 2342const struct rule layer_dir_s1d1_refer[] = {2343	{2344		.path = dir_s1d1,2345		.access = LANDLOCK_ACCESS_FS_REFER,2346	},2347	{},2348};2349 2350const struct rule layer_dir_s1d1_execute[] = {2351	{2352		/* Matches a parent directory. */2353		.path = dir_s1d1,2354		.access = LANDLOCK_ACCESS_FS_EXECUTE,2355	},2356	{},2357};2358 2359const struct rule layer_dir_s2d1_execute[] = {2360	{2361		/* Does not match a parent directory. */2362		.path = dir_s2d1,2363		.access = LANDLOCK_ACCESS_FS_EXECUTE,2364	},2365	{},2366};2367 2368/*2369 * Tests precedence over renames: denied by default for different parent2370 * directories, *with* a rule matching a parent directory, but not directly2371 * denying access (with MAKE_REG nor REMOVE).2372 */2373TEST_F_FORK(layout1, refer_denied_by_default1)2374{2375	refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,2376				layer_dir_s1d1_execute);2377}2378 2379/*2380 * Same test but this time turning around the ABI version order: the first2381 * layer does not handle LANDLOCK_ACCESS_FS_REFER.2382 */2383TEST_F_FORK(layout1, refer_denied_by_default2)2384{2385	refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV,2386				layer_dir_s1d1_refer);2387}2388 2389/*2390 * Tests precedence over renames: denied by default for different parent2391 * directories, *without* a rule matching a parent directory, but not directly2392 * denying access (with MAKE_REG nor REMOVE).2393 */2394TEST_F_FORK(layout1, refer_denied_by_default3)2395{2396	refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,2397				layer_dir_s2d1_execute);2398}2399 2400/*2401 * Same test but this time turning around the ABI version order: the first2402 * layer does not handle LANDLOCK_ACCESS_FS_REFER.2403 */2404TEST_F_FORK(layout1, refer_denied_by_default4)2405{2406	refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV,2407				layer_dir_s1d1_refer);2408}2409 2410/*2411 * Tests walking through a denied root mount.2412 */2413TEST_F_FORK(layout1, refer_mount_root_deny)2414{2415	const struct landlock_ruleset_attr ruleset_attr = {2416		.handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_DIR,2417	};2418	int root_fd, ruleset_fd;2419 2420	/* Creates a mount object from a non-mount point. */2421	set_cap(_metadata, CAP_SYS_ADMIN);2422	root_fd =2423		open_tree(AT_FDCWD, dir_s1d1,2424			  AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);2425	clear_cap(_metadata, CAP_SYS_ADMIN);2426	ASSERT_LE(0, root_fd);2427 2428	ruleset_fd =2429		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);2430	ASSERT_LE(0, ruleset_fd);2431 2432	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));2433	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));2434	EXPECT_EQ(0, close(ruleset_fd));2435 2436	/* Link denied by Landlock: EACCES. */2437	EXPECT_EQ(-1, linkat(root_fd, ".", root_fd, "does_not_exist", 0));2438	EXPECT_EQ(EACCES, errno);2439 2440	/* renameat2() always returns EBUSY. */2441	EXPECT_EQ(-1, renameat2(root_fd, ".", root_fd, "does_not_exist", 0));2442	EXPECT_EQ(EBUSY, errno);2443 2444	EXPECT_EQ(0, close(root_fd));2445}2446 2447TEST_F_FORK(layout1, reparent_link)2448{2449	const struct rule layer1[] = {2450		{2451			.path = dir_s1d2,2452			.access = LANDLOCK_ACCESS_FS_MAKE_REG,2453		},2454		{2455			.path = dir_s1d3,2456			.access = LANDLOCK_ACCESS_FS_REFER,2457		},2458		{2459			.path = dir_s2d2,2460			.access = LANDLOCK_ACCESS_FS_REFER,2461		},2462		{2463			.path = dir_s2d3,2464			.access = LANDLOCK_ACCESS_FS_MAKE_REG,2465		},2466		{},2467	};2468	const int ruleset_fd = create_ruleset(2469		_metadata,2470		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);2471 2472	ASSERT_LE(0, ruleset_fd);2473	enforce_ruleset(_metadata, ruleset_fd);2474	ASSERT_EQ(0, close(ruleset_fd));2475 2476	ASSERT_EQ(0, unlink(file1_s1d1));2477	ASSERT_EQ(0, unlink(file1_s1d2));2478	ASSERT_EQ(0, unlink(file1_s1d3));2479 2480	/* Denies linking because of missing MAKE_REG. */2481	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));2482	ASSERT_EQ(EACCES, errno);2483	/* Denies linking because of missing source and destination REFER. */2484	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));2485	ASSERT_EQ(EXDEV, errno);2486	/* Denies linking because of missing source REFER. */2487	ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3));2488	ASSERT_EQ(EXDEV, errno);2489 2490	/* Denies linking because of missing MAKE_REG. */2491	ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1));2492	ASSERT_EQ(EACCES, errno);2493	/* Denies linking because of missing destination REFER. */2494	ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2));2495	ASSERT_EQ(EXDEV, errno);2496 2497	/* Allows linking because of REFER and MAKE_REG. */2498	ASSERT_EQ(0, link(file1_s2d2, file1_s1d3));2499	ASSERT_EQ(0, unlink(file1_s2d2));2500	/* Reverse linking denied because of missing MAKE_REG. */2501	ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2));2502	ASSERT_EQ(EACCES, errno);2503	ASSERT_EQ(0, unlink(file1_s2d3));2504	/* Checks reverse linking. */2505	ASSERT_EQ(0, link(file1_s1d3, file1_s2d3));2506	ASSERT_EQ(0, unlink(file1_s1d3));2507 2508	/*2509	 * This is OK for a file link, but it should not be allowed for a2510	 * directory rename (because of the superset of access rights.2511	 */2512	ASSERT_EQ(0, link(file1_s2d3, file1_s1d3));2513	ASSERT_EQ(0, unlink(file1_s1d3));2514 2515	ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));2516	ASSERT_EQ(EXDEV, errno);2517	ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));2518	ASSERT_EQ(EXDEV, errno);2519 2520	ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));2521	ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));2522}2523 2524TEST_F_FORK(layout1, reparent_rename)2525{2526	/* Same rules as for reparent_link. */2527	const struct rule layer1[] = {2528		{2529			.path = dir_s1d2,2530			.access = LANDLOCK_ACCESS_FS_MAKE_REG,2531		},2532		{2533			.path = dir_s1d3,2534			.access = LANDLOCK_ACCESS_FS_REFER,2535		},2536		{2537			.path = dir_s2d2,2538			.access = LANDLOCK_ACCESS_FS_REFER,2539		},2540		{2541			.path = dir_s2d3,2542			.access = LANDLOCK_ACCESS_FS_MAKE_REG,2543		},2544		{},2545	};2546	const int ruleset_fd = create_ruleset(2547		_metadata,2548		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);2549 2550	ASSERT_LE(0, ruleset_fd);2551	enforce_ruleset(_metadata, ruleset_fd);2552	ASSERT_EQ(0, close(ruleset_fd));2553 2554	ASSERT_EQ(0, unlink(file1_s1d2));2555	ASSERT_EQ(0, unlink(file1_s1d3));2556 2557	/* Denies renaming because of missing MAKE_REG. */2558	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1,2559				RENAME_EXCHANGE));2560	ASSERT_EQ(EACCES, errno);2561	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1,2562				RENAME_EXCHANGE));2563	ASSERT_EQ(EACCES, errno);2564	ASSERT_EQ(0, unlink(file1_s1d1));2565	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));2566	ASSERT_EQ(EACCES, errno);2567	/* Even denies same file exchange. */2568	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1,2569				RENAME_EXCHANGE));2570	ASSERT_EQ(EACCES, errno);2571 2572	/* Denies renaming because of missing source and destination REFER. */2573	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2));2574	ASSERT_EQ(EXDEV, errno);2575	/*2576	 * Denies renaming because of missing MAKE_REG, source and destination2577	 * REFER.2578	 */2579	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1,2580				RENAME_EXCHANGE));2581	ASSERT_EQ(EACCES, errno);2582	ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1,2583				RENAME_EXCHANGE));2584	ASSERT_EQ(EACCES, errno);2585 2586	/* Denies renaming because of missing source REFER. */2587	ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));2588	ASSERT_EQ(EXDEV, errno);2589	/* Denies renaming because of missing MAKE_REG. */2590	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3,2591				RENAME_EXCHANGE));2592	ASSERT_EQ(EACCES, errno);2593 2594	/* Denies renaming because of missing MAKE_REG. */2595	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1));2596	ASSERT_EQ(EACCES, errno);2597	/* Denies renaming because of missing destination REFER*/2598	ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));2599	ASSERT_EQ(EXDEV, errno);2600 2601	/* Denies exchange because of one missing MAKE_REG. */2602	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3,2603				RENAME_EXCHANGE));2604	ASSERT_EQ(EACCES, errno);2605	/* Allows renaming because of REFER and MAKE_REG. */2606	ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3));2607 2608	/* Reverse renaming denied because of missing MAKE_REG. */2609	ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2));2610	ASSERT_EQ(EACCES, errno);2611	ASSERT_EQ(0, unlink(file1_s2d3));2612	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));2613 2614	/* Tests reverse renaming. */2615	ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));2616	ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3,2617			       RENAME_EXCHANGE));2618	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));2619 2620	/*2621	 * This is OK for a file rename, but it should not be allowed for a2622	 * directory rename (because of the superset of access rights).2623	 */2624	ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));2625	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));2626 2627	/*2628	 * Tests superset restrictions applied to directories.  Not only the2629	 * dir_s2d3's parent (dir_s2d2) should be taken into account but also2630	 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right2631	 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided2632	 * directly by the moved dir_s2d3.2633	 */2634	ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3));2635	ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3));2636	/*2637	 * The first rename is allowed but not the exchange because dir_s1d3's2638	 * parent (dir_s1d2) doesn't have REFER.2639	 */2640	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,2641				RENAME_EXCHANGE));2642	ASSERT_EQ(EXDEV, errno);2643	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3,2644				RENAME_EXCHANGE));2645	ASSERT_EQ(EXDEV, errno);2646	ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3));2647	ASSERT_EQ(EXDEV, errno);2648 2649	ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3));2650	ASSERT_EQ(EXDEV, errno);2651	ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2));2652	ASSERT_EQ(EXDEV, errno);2653 2654	/* Renaming in the same directory is always allowed. */2655	ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2));2656	ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3));2657 2658	ASSERT_EQ(0, unlink(file1_s1d2));2659	/* Denies because of missing source MAKE_REG and destination REFER. */2660	ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2));2661	ASSERT_EQ(EXDEV, errno);2662 2663	ASSERT_EQ(0, unlink(file1_s1d3));2664	/* Denies because of missing source MAKE_REG and REFER. */2665	ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3));2666	ASSERT_EQ(EXDEV, errno);2667}2668 2669static void2670reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata)2671{2672	const struct rule layer1[] = {2673		{2674			.path = dir_s1d2,2675			.access = LANDLOCK_ACCESS_FS_REFER,2676		},2677		{2678			/* Interesting for the layer2 tests. */2679			.path = dir_s1d3,2680			.access = LANDLOCK_ACCESS_FS_MAKE_REG,2681		},2682		{2683			.path = dir_s2d2,2684			.access = LANDLOCK_ACCESS_FS_REFER,2685		},2686		{2687			.path = dir_s2d3,2688			.access = LANDLOCK_ACCESS_FS_MAKE_REG,2689		},2690		{},2691	};2692	const int ruleset_fd = create_ruleset(2693		_metadata,2694		LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);2695 2696	ASSERT_LE(0, ruleset_fd);2697	enforce_ruleset(_metadata, ruleset_fd);2698	ASSERT_EQ(0, close(ruleset_fd));2699}2700 2701static void2702reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata)2703{2704	const struct rule layer2[] = {2705		{2706			.path = dir_s2d3,2707			.access = LANDLOCK_ACCESS_FS_MAKE_DIR,2708		},2709		{},2710	};2711	/*2712	 * Same checks as before but with a second layer and a new MAKE_DIR2713	 * rule (and no explicit handling of REFER).2714	 */2715	const int ruleset_fd =2716		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2);2717 2718	ASSERT_LE(0, ruleset_fd);2719	enforce_ruleset(_metadata, ruleset_fd);2720	ASSERT_EQ(0, close(ruleset_fd));2721}2722 2723TEST_F_FORK(layout1, reparent_exdev_layers_rename1)2724{2725	ASSERT_EQ(0, unlink(file1_s2d2));2726	ASSERT_EQ(0, unlink(file1_s2d3));2727 2728	reparent_exdev_layers_enforce1(_metadata);2729 2730	/*2731	 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock2732	 * because it doesn't inherit new access rights.2733	 */2734	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));2735	ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));2736 2737	/*2738	 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it2739	 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is2740	 * already allowed for dir_s1d3.2741	 */2742	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3));2743	ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3));2744 2745	/*2746	 * However, moving the file1_s1d3 file below dir_s2d3 is allowed2747	 * because it cannot inherit MAKE_REG right (which is dedicated to2748	 * directories).2749	 */2750	ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));2751 2752	reparent_exdev_layers_enforce2(_metadata);2753 2754	/*2755	 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because2756	 * MAKE_DIR is not tied to dir_s2d2.2757	 */2758	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2));2759	ASSERT_EQ(EACCES, errno);2760 2761	/*2762	 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it2763	 * would grants MAKE_REG and MAKE_DIR rights to it.2764	 */2765	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));2766	ASSERT_EQ(EXDEV, errno);2767 2768	/*2769	 * Moving the file2_s1d3 file below dir_s2d3 is denied because the2770	 * second layer does not handle REFER, which is always denied by2771	 * default.2772	 */2773	ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3));2774	ASSERT_EQ(EXDEV, errno);2775}2776 2777TEST_F_FORK(layout1, reparent_exdev_layers_rename2)2778{2779	reparent_exdev_layers_enforce1(_metadata);2780 2781	/* Checks EACCES predominance over EXDEV. */2782	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));2783	ASSERT_EQ(EACCES, errno);2784	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2));2785	ASSERT_EQ(EACCES, errno);2786	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));2787	ASSERT_EQ(EXDEV, errno);2788	/* Modify layout! */2789	ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3));2790 2791	/* Without REFER source. */2792	ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));2793	ASSERT_EQ(EXDEV, errno);2794	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));2795	ASSERT_EQ(EXDEV, errno);2796 2797	reparent_exdev_layers_enforce2(_metadata);2798 2799	/* Checks EACCES predominance over EXDEV. */2800	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));2801	ASSERT_EQ(EACCES, errno);2802	/* Checks with actual file2_s1d2. */2803	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2));2804	ASSERT_EQ(EACCES, errno);2805	ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));2806	ASSERT_EQ(EXDEV, errno);2807	/*2808	 * Modifying the layout is now denied because the second layer does not2809	 * handle REFER, which is always denied by default.2810	 */2811	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));2812	ASSERT_EQ(EXDEV, errno);2813 2814	/* Without REFER source, EACCES wins over EXDEV. */2815	ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));2816	ASSERT_EQ(EACCES, errno);2817	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));2818	ASSERT_EQ(EACCES, errno);2819}2820 2821TEST_F_FORK(layout1, reparent_exdev_layers_exchange1)2822{2823	const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 =2824							       file2_s2d3;2825 2826	ASSERT_EQ(0, unlink(file1_s1d2));2827	ASSERT_EQ(0, mkdir(file1_s1d2, 0700));2828	ASSERT_EQ(0, unlink(file2_s2d3));2829	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));2830 2831	reparent_exdev_layers_enforce1(_metadata);2832 2833	/* Error predominance with file exchange: returns EXDEV and EACCES. */2834	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,2835				RENAME_EXCHANGE));2836	ASSERT_EQ(EACCES, errno);2837	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,2838				RENAME_EXCHANGE));2839	ASSERT_EQ(EACCES, errno);2840 2841	/*2842	 * Checks with directories which creation could be allowed, but denied2843	 * because of access rights that would be inherited.2844	 */2845	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,2846				dir_file2_s2d3, RENAME_EXCHANGE));2847	ASSERT_EQ(EXDEV, errno);2848	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,2849				dir_file1_s1d2, RENAME_EXCHANGE));2850	ASSERT_EQ(EXDEV, errno);2851 2852	/* Checks with same access rights. */2853	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,2854			       RENAME_EXCHANGE));2855	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,2856			       RENAME_EXCHANGE));2857 2858	/* Checks with different (child-only) access rights. */2859	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,2860			       RENAME_EXCHANGE));2861	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,2862			       RENAME_EXCHANGE));2863 2864	/*2865	 * Checks that exchange between file and directory are consistent.2866	 *2867	 * Moving a file (file1_s2d2) to a directory which only grants more2868	 * directory-related access rights is allowed, and at the same time2869	 * moving a directory (dir_file2_s2d3) to another directory which2870	 * grants less access rights is allowed too.2871	 *2872	 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments.2873	 */2874	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,2875			       RENAME_EXCHANGE));2876	/*2877	 * However, moving back the directory is denied because it would get2878	 * more access rights than the current state and because file creation2879	 * is forbidden (in dir_s2d2).2880	 */2881	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,2882				RENAME_EXCHANGE));2883	ASSERT_EQ(EACCES, errno);2884	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,2885				RENAME_EXCHANGE));2886	ASSERT_EQ(EACCES, errno);2887 2888	reparent_exdev_layers_enforce2(_metadata);2889 2890	/* Error predominance with file exchange: returns EXDEV and EACCES. */2891	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,2892				RENAME_EXCHANGE));2893	ASSERT_EQ(EACCES, errno);2894	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,2895				RENAME_EXCHANGE));2896	ASSERT_EQ(EACCES, errno);2897 2898	/* Checks with directories which creation is now denied. */2899	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,2900				dir_file2_s2d3, RENAME_EXCHANGE));2901	ASSERT_EQ(EACCES, errno);2902	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,2903				dir_file1_s1d2, RENAME_EXCHANGE));2904	ASSERT_EQ(EACCES, errno);2905 2906	/* Checks with different (child-only) access rights. */2907	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,2908				RENAME_EXCHANGE));2909	/* Denied because of MAKE_DIR. */2910	ASSERT_EQ(EACCES, errno);2911	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,2912				RENAME_EXCHANGE));2913	ASSERT_EQ(EACCES, errno);2914 2915	/* Checks with different (child-only) access rights. */2916	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,2917				RENAME_EXCHANGE));2918	/* Denied because of MAKE_DIR. */2919	ASSERT_EQ(EACCES, errno);2920	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,2921				RENAME_EXCHANGE));2922	ASSERT_EQ(EACCES, errno);2923 2924	/* See layout1.reparent_exdev_layers_exchange2 for complement. */2925}2926 2927TEST_F_FORK(layout1, reparent_exdev_layers_exchange2)2928{2929	const char *const dir_file2_s2d3 = file2_s2d3;2930 2931	ASSERT_EQ(0, unlink(file2_s2d3));2932	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));2933 2934	reparent_exdev_layers_enforce1(_metadata);2935	reparent_exdev_layers_enforce2(_metadata);2936 2937	/* Checks that exchange between file and directory are consistent. */2938	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,2939				RENAME_EXCHANGE));2940	ASSERT_EQ(EACCES, errno);2941	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,2942				RENAME_EXCHANGE));2943	ASSERT_EQ(EACCES, errno);2944}2945 2946TEST_F_FORK(layout1, reparent_exdev_layers_exchange3)2947{2948	const char *const dir_file2_s2d3 = file2_s2d3;2949 2950	ASSERT_EQ(0, unlink(file2_s2d3));2951	ASSERT_EQ(0, mkdir(file2_s2d3, 0700));2952 2953	reparent_exdev_layers_enforce1(_metadata);2954 2955	/*2956	 * Checks that exchange between file and directory are consistent,2957	 * including with inverted arguments (see2958	 * layout1.reparent_exdev_layers_exchange1).2959	 */2960	ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,2961			       RENAME_EXCHANGE));2962	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,2963				RENAME_EXCHANGE));2964	ASSERT_EQ(EACCES, errno);2965	ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,2966				RENAME_EXCHANGE));2967	ASSERT_EQ(EACCES, errno);2968}2969 2970TEST_F_FORK(layout1, reparent_remove)2971{2972	const struct rule layer1[] = {2973		{2974			.path = dir_s1d1,2975			.access = LANDLOCK_ACCESS_FS_REFER |2976				  LANDLOCK_ACCESS_FS_REMOVE_DIR,2977		},2978		{2979			.path = dir_s1d2,2980			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,2981		},2982		{2983			.path = dir_s2d1,2984			.access = LANDLOCK_ACCESS_FS_REFER |2985				  LANDLOCK_ACCESS_FS_REMOVE_FILE,2986		},2987		{},2988	};2989	const int ruleset_fd = create_ruleset(2990		_metadata,2991		LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR |2992			LANDLOCK_ACCESS_FS_REMOVE_FILE,2993		layer1);2994 2995	ASSERT_LE(0, ruleset_fd);2996	enforce_ruleset(_metadata, ruleset_fd);2997	ASSERT_EQ(0, close(ruleset_fd));2998 2999	/* Access denied because of wrong/swapped remove file/dir. */3000	ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2));3001	ASSERT_EQ(EACCES, errno);3002	ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1));3003	ASSERT_EQ(EACCES, errno);3004	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2,3005				RENAME_EXCHANGE));3006	ASSERT_EQ(EACCES, errno);3007	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3,3008				RENAME_EXCHANGE));3009	ASSERT_EQ(EACCES, errno);3010 3011	/* Access allowed thanks to the matching rights. */3012	ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2));3013	ASSERT_EQ(EISDIR, errno);3014	ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1));3015	ASSERT_EQ(ENOTDIR, errno);3016	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));3017	ASSERT_EQ(ENOTDIR, errno);3018	ASSERT_EQ(0, unlink(file1_s2d1));3019	ASSERT_EQ(0, unlink(file1_s1d3));3020	ASSERT_EQ(0, unlink(file2_s1d3));3021	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1));3022 3023	/* Effectively removes a file and a directory by exchanging them. */3024	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));3025	ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,3026			       RENAME_EXCHANGE));3027	ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,3028				RENAME_EXCHANGE));3029	ASSERT_EQ(EACCES, errno);3030}3031 3032TEST_F_FORK(layout1, reparent_dom_superset)3033{3034	const struct rule layer1[] = {3035		{3036			.path = dir_s1d2,3037			.access = LANDLOCK_ACCESS_FS_REFER,3038		},3039		{3040			.path = file1_s1d2,3041			.access = LANDLOCK_ACCESS_FS_EXECUTE,3042		},3043		{3044			.path = dir_s1d3,3045			.access = LANDLOCK_ACCESS_FS_MAKE_SOCK |3046				  LANDLOCK_ACCESS_FS_EXECUTE,3047		},3048		{3049			.path = dir_s2d2,3050			.access = LANDLOCK_ACCESS_FS_REFER |3051				  LANDLOCK_ACCESS_FS_EXECUTE |3052				  LANDLOCK_ACCESS_FS_MAKE_SOCK,3053		},3054		{3055			.path = dir_s2d3,3056			.access = LANDLOCK_ACCESS_FS_READ_FILE |3057				  LANDLOCK_ACCESS_FS_MAKE_FIFO,3058		},3059		{},3060	};3061	int ruleset_fd = create_ruleset(_metadata,3062					LANDLOCK_ACCESS_FS_REFER |3063						LANDLOCK_ACCESS_FS_EXECUTE |3064						LANDLOCK_ACCESS_FS_MAKE_SOCK |3065						LANDLOCK_ACCESS_FS_READ_FILE |3066						LANDLOCK_ACCESS_FS_MAKE_FIFO,3067					layer1);3068 3069	ASSERT_LE(0, ruleset_fd);3070	enforce_ruleset(_metadata, ruleset_fd);3071	ASSERT_EQ(0, close(ruleset_fd));3072 3073	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1));3074	ASSERT_EQ(EXDEV, errno);3075	/*3076	 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE3077	 * access right.3078	 */3079	ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3));3080	ASSERT_EQ(EXDEV, errno);3081	/*3082	 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a3083	 * superset of access rights compared to dir_s1d2, because file1_s1d23084	 * already has these access rights anyway.3085	 */3086	ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2));3087	ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2));3088 3089	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));3090	ASSERT_EQ(EXDEV, errno);3091	/*3092	 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access3093	 * right.3094	 */3095	ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));3096	ASSERT_EQ(EXDEV, errno);3097	/*3098	 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset3099	 * of access rights compared to dir_s1d2, because dir_s1d3 already has3100	 * these access rights anyway.3101	 */3102	ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));3103	ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));3104 3105	/*3106	 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back3107	 * will be denied because the new inherited access rights from dir_s1d23108	 * will be less than the destination (original) dir_s2d3.  This is a3109	 * sinkhole scenario where we cannot move back files or directories.3110	 */3111	ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2));3112	ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));3113	ASSERT_EQ(EXDEV, errno);3114	ASSERT_EQ(0, unlink(file2_s1d2));3115	ASSERT_EQ(0, unlink(file2_s2d3));3116	/*3117	 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and3118	 * MAKE_SOCK which were inherited from dir_s1d3.3119	 */3120	ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2));3121	ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3));3122	ASSERT_EQ(EXDEV, errno);3123}3124 3125TEST_F_FORK(layout1, remove_dir)3126{3127	const struct rule rules[] = {3128		{3129			.path = dir_s1d2,3130			.access = LANDLOCK_ACCESS_FS_REMOVE_DIR,3131		},3132		{},3133	};3134	const int ruleset_fd =3135		create_ruleset(_metadata, rules[0].access, rules);3136 3137	ASSERT_LE(0, ruleset_fd);3138 3139	ASSERT_EQ(0, unlink(file1_s1d1));3140	ASSERT_EQ(0, unlink(file1_s1d2));3141	ASSERT_EQ(0, unlink(file1_s1d3));3142	ASSERT_EQ(0, unlink(file2_s1d3));3143 3144	enforce_ruleset(_metadata, ruleset_fd);3145	ASSERT_EQ(0, close(ruleset_fd));3146 3147	ASSERT_EQ(0, rmdir(dir_s1d3));3148	ASSERT_EQ(0, mkdir(dir_s1d3, 0700));3149	ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));3150 3151	/* dir_s1d2 itself cannot be removed. */3152	ASSERT_EQ(-1, rmdir(dir_s1d2));3153	ASSERT_EQ(EACCES, errno);3154	ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR));3155	ASSERT_EQ(EACCES, errno);3156	ASSERT_EQ(-1, rmdir(dir_s1d1));3157	ASSERT_EQ(EACCES, errno);3158	ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR));3159	ASSERT_EQ(EACCES, errno);3160}3161 3162TEST_F_FORK(layout1, remove_file)3163{3164	const struct rule rules[] = {3165		{3166			.path = dir_s1d2,3167			.access = LANDLOCK_ACCESS_FS_REMOVE_FILE,3168		},3169		{},3170	};3171	const int ruleset_fd =3172		create_ruleset(_metadata, rules[0].access, rules);3173 3174	ASSERT_LE(0, ruleset_fd);3175	enforce_ruleset(_metadata, ruleset_fd);3176	ASSERT_EQ(0, close(ruleset_fd));3177 3178	ASSERT_EQ(-1, unlink(file1_s1d1));3179	ASSERT_EQ(EACCES, errno);3180	ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0));3181	ASSERT_EQ(EACCES, errno);3182	ASSERT_EQ(0, unlink(file1_s1d2));3183	ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0));3184}3185 3186static void test_make_file(struct __test_metadata *const _metadata,3187			   const __u64 access, const mode_t mode,3188			   const dev_t dev)3189{3190	const struct rule rules[] = {3191		{3192			.path = dir_s1d2,3193			.access = access,3194		},3195		{},3196	};3197	const int ruleset_fd = create_ruleset(_metadata, access, rules);3198 3199	ASSERT_LE(0, ruleset_fd);3200 3201	ASSERT_EQ(0, unlink(file1_s1d1));3202	ASSERT_EQ(0, unlink(file2_s1d1));3203	ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev))3204	{3205		TH_LOG("Failed to make file \"%s\": %s", file2_s1d1,3206		       strerror(errno));3207	};3208 3209	ASSERT_EQ(0, unlink(file1_s1d2));3210	ASSERT_EQ(0, unlink(file2_s1d2));3211 3212	ASSERT_EQ(0, unlink(file1_s1d3));3213	ASSERT_EQ(0, unlink(file2_s1d3));3214 3215	enforce_ruleset(_metadata, ruleset_fd);3216	ASSERT_EQ(0, close(ruleset_fd));3217 3218	ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev));3219	ASSERT_EQ(EACCES, errno);3220	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));3221	ASSERT_EQ(EACCES, errno);3222	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));3223	ASSERT_EQ(EACCES, errno);3224 3225	ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev))3226	{3227		TH_LOG("Failed to make file \"%s\": %s", file1_s1d2,3228		       strerror(errno));3229	};3230	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));3231	ASSERT_EQ(0, unlink(file2_s1d2));3232	ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));3233 3234	ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev));3235	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));3236	ASSERT_EQ(0, unlink(file2_s1d3));3237	ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));3238}3239 3240TEST_F_FORK(layout1, make_char)3241{3242	/* Creates a /dev/null device. */3243	set_cap(_metadata, CAP_MKNOD);3244	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR,3245		       makedev(1, 3));3246}3247 3248TEST_F_FORK(layout1, make_block)3249{3250	/* Creates a /dev/loop0 device. */3251	set_cap(_metadata, CAP_MKNOD);3252	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK,3253		       makedev(7, 0));3254}3255 3256TEST_F_FORK(layout1, make_reg_1)3257{3258	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0);3259}3260 3261TEST_F_FORK(layout1, make_reg_2)3262{3263	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0);3264}3265 3266TEST_F_FORK(layout1, make_sock)3267{3268	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0);3269}3270 3271TEST_F_FORK(layout1, make_fifo)3272{3273	test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0);3274}3275 3276TEST_F_FORK(layout1, make_sym)3277{3278	const struct rule rules[] = {3279		{3280			.path = dir_s1d2,3281			.access = LANDLOCK_ACCESS_FS_MAKE_SYM,3282		},3283		{},3284	};3285	const int ruleset_fd =3286		create_ruleset(_metadata, rules[0].access, rules);3287 3288	ASSERT_LE(0, ruleset_fd);3289 3290	ASSERT_EQ(0, unlink(file1_s1d1));3291	ASSERT_EQ(0, unlink(file2_s1d1));3292	ASSERT_EQ(0, symlink("none", file2_s1d1));3293 3294	ASSERT_EQ(0, unlink(file1_s1d2));3295	ASSERT_EQ(0, unlink(file2_s1d2));3296 3297	ASSERT_EQ(0, unlink(file1_s1d3));3298	ASSERT_EQ(0, unlink(file2_s1d3));3299 3300	enforce_ruleset(_metadata, ruleset_fd);3301	ASSERT_EQ(0, close(ruleset_fd));3302 3303	ASSERT_EQ(-1, symlink("none", file1_s1d1));3304	ASSERT_EQ(EACCES, errno);3305	ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));3306	ASSERT_EQ(EACCES, errno);3307	ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));3308	ASSERT_EQ(EACCES, errno);3309 3310	ASSERT_EQ(0, symlink("none", file1_s1d2));3311	ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));3312	ASSERT_EQ(0, unlink(file2_s1d2));3313	ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));3314 3315	ASSERT_EQ(0, symlink("none", file1_s1d3));3316	ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));3317	ASSERT_EQ(0, unlink(file2_s1d3));3318	ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));3319}3320 3321TEST_F_FORK(layout1, make_dir)3322{3323	const struct rule rules[] = {3324		{3325			.path = dir_s1d2,3326			.access = LANDLOCK_ACCESS_FS_MAKE_DIR,3327		},3328		{},3329	};3330	const int ruleset_fd =3331		create_ruleset(_metadata, rules[0].access, rules);3332 3333	ASSERT_LE(0, ruleset_fd);3334 3335	ASSERT_EQ(0, unlink(file1_s1d1));3336	ASSERT_EQ(0, unlink(file1_s1d2));3337	ASSERT_EQ(0, unlink(file1_s1d3));3338 3339	enforce_ruleset(_metadata, ruleset_fd);3340	ASSERT_EQ(0, close(ruleset_fd));3341 3342	/* Uses file_* as directory names. */3343	ASSERT_EQ(-1, mkdir(file1_s1d1, 0700));3344	ASSERT_EQ(EACCES, errno);3345	ASSERT_EQ(0, mkdir(file1_s1d2, 0700));3346	ASSERT_EQ(0, mkdir(file1_s1d3, 0700));3347}3348 3349static int open_proc_fd(struct __test_metadata *const _metadata, const int fd,3350			const int open_flags)3351{3352	static const char path_template[] = "/proc/self/fd/%d";3353	char procfd_path[sizeof(path_template) + 10];3354	const int procfd_path_size =3355		snprintf(procfd_path, sizeof(procfd_path), path_template, fd);3356 3357	ASSERT_LT(procfd_path_size, sizeof(procfd_path));3358	return open(procfd_path, open_flags);3359}3360 3361TEST_F_FORK(layout1, proc_unlinked_file)3362{3363	const struct rule rules[] = {3364		{3365			.path = file1_s1d2,3366			.access = LANDLOCK_ACCESS_FS_READ_FILE,3367		},3368		{},3369	};3370	int reg_fd, proc_fd;3371	const int ruleset_fd = create_ruleset(3372		_metadata,3373		LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,3374		rules);3375 3376	ASSERT_LE(0, ruleset_fd);3377	enforce_ruleset(_metadata, ruleset_fd);3378	ASSERT_EQ(0, close(ruleset_fd));3379 3380	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));3381	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));3382	reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC);3383	ASSERT_LE(0, reg_fd);3384	ASSERT_EQ(0, unlink(file1_s1d2));3385 3386	proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC);3387	ASSERT_LE(0, proc_fd);3388	ASSERT_EQ(0, close(proc_fd));3389 3390	proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC);3391	ASSERT_EQ(-1, proc_fd)3392	{3393		TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd,3394		       strerror(errno));3395	}3396	ASSERT_EQ(EACCES, errno);3397 3398	ASSERT_EQ(0, close(reg_fd));3399}3400 3401TEST_F_FORK(layout1, proc_pipe)3402{3403	int proc_fd;3404	int pipe_fds[2];3405	char buf = '\0';3406	const struct rule rules[] = {3407		{3408			.path = dir_s1d2,3409			.access = LANDLOCK_ACCESS_FS_READ_FILE |3410				  LANDLOCK_ACCESS_FS_WRITE_FILE,3411		},3412		{},3413	};3414	/* Limits read and write access to files tied to the filesystem. */3415	const int ruleset_fd =3416		create_ruleset(_metadata, rules[0].access, rules);3417 3418	ASSERT_LE(0, ruleset_fd);3419	enforce_ruleset(_metadata, ruleset_fd);3420	ASSERT_EQ(0, close(ruleset_fd));3421 3422	/* Checks enforcement for normal files. */3423	ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));3424	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));3425 3426	/* Checks access to pipes through FD. */3427	ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC));3428	ASSERT_EQ(1, write(pipe_fds[1], ".", 1))3429	{3430		TH_LOG("Failed to write in pipe: %s", strerror(errno));3431	}3432	ASSERT_EQ(1, read(pipe_fds[0], &buf, 1));3433	ASSERT_EQ('.', buf);3434 3435	/* Checks write access to pipe through /proc/self/fd . */3436	proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC);3437	ASSERT_LE(0, proc_fd);3438	ASSERT_EQ(1, write(proc_fd, ".", 1))3439	{3440		TH_LOG("Failed to write through /proc/self/fd/%d: %s",3441		       pipe_fds[1], strerror(errno));3442	}3443	ASSERT_EQ(0, close(proc_fd));3444 3445	/* Checks read access to pipe through /proc/self/fd . */3446	proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC);3447	ASSERT_LE(0, proc_fd);3448	buf = '\0';3449	ASSERT_EQ(1, read(proc_fd, &buf, 1))3450	{3451		TH_LOG("Failed to read through /proc/self/fd/%d: %s",3452		       pipe_fds[1], strerror(errno));3453	}3454	ASSERT_EQ(0, close(proc_fd));3455 3456	ASSERT_EQ(0, close(pipe_fds[0]));3457	ASSERT_EQ(0, close(pipe_fds[1]));3458}3459 3460/* Invokes truncate(2) and returns its errno or 0. */3461static int test_truncate(const char *const path)3462{3463	if (truncate(path, 10) < 0)3464		return errno;3465	return 0;3466}3467 3468/*3469 * Invokes creat(2) and returns its errno or 0.3470 * Closes the opened file descriptor on success.3471 */3472static int test_creat(const char *const path)3473{3474	int fd = creat(path, 0600);3475 3476	if (fd < 0)3477		return errno;3478 3479	/*3480	 * Mixing error codes from close(2) and creat(2) should not lead to any3481	 * (access type) confusion for this test.3482	 */3483	if (close(fd) < 0)3484		return errno;3485	return 0;3486}3487 3488/*3489 * Exercises file truncation when it's not restricted,3490 * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed.3491 */3492TEST_F_FORK(layout1, truncate_unhandled)3493{3494	const char *const file_r = file1_s1d1;3495	const char *const file_w = file2_s1d1;3496	const char *const file_none = file1_s1d2;3497	const struct rule rules[] = {3498		{3499			.path = file_r,3500			.access = LANDLOCK_ACCESS_FS_READ_FILE,3501		},3502		{3503			.path = file_w,3504			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,3505		},3506		/* Implicitly: No rights for file_none. */3507		{},3508	};3509 3510	const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |3511			      LANDLOCK_ACCESS_FS_WRITE_FILE;3512	int ruleset_fd;3513 3514	/* Enables Landlock. */3515	ruleset_fd = create_ruleset(_metadata, handled, rules);3516 3517	ASSERT_LE(0, ruleset_fd);3518	enforce_ruleset(_metadata, ruleset_fd);3519	ASSERT_EQ(0, close(ruleset_fd));3520 3521	/*3522	 * Checks read right: truncate and open with O_TRUNC work, unless the3523	 * file is attempted to be opened for writing.3524	 */3525	EXPECT_EQ(0, test_truncate(file_r));3526	EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC));3527	EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC));3528	EXPECT_EQ(EACCES, test_creat(file_r));3529 3530	/*3531	 * Checks write right: truncate and open with O_TRUNC work, unless the3532	 * file is attempted to be opened for reading.3533	 */3534	EXPECT_EQ(0, test_truncate(file_w));3535	EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC));3536	EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC));3537	EXPECT_EQ(0, test_creat(file_w));3538 3539	/*3540	 * Checks "no rights" case: truncate works but all open attempts fail,3541	 * including creat.3542	 */3543	EXPECT_EQ(0, test_truncate(file_none));3544	EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));3545	EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));3546	EXPECT_EQ(EACCES, test_creat(file_none));3547}3548 3549TEST_F_FORK(layout1, truncate)3550{3551	const char *const file_rwt = file1_s1d1;3552	const char *const file_rw = file2_s1d1;3553	const char *const file_rt = file1_s1d2;3554	const char *const file_t = file2_s1d2;3555	const char *const file_none = file1_s1d3;3556	const char *const dir_t = dir_s2d1;3557	const char *const file_in_dir_t = file1_s2d1;3558	const char *const dir_w = dir_s3d1;3559	const char *const file_in_dir_w = file1_s3d1;3560	const struct rule rules[] = {3561		{3562			.path = file_rwt,3563			.access = LANDLOCK_ACCESS_FS_READ_FILE |3564				  LANDLOCK_ACCESS_FS_WRITE_FILE |3565				  LANDLOCK_ACCESS_FS_TRUNCATE,3566		},3567		{3568			.path = file_rw,3569			.access = LANDLOCK_ACCESS_FS_READ_FILE |3570				  LANDLOCK_ACCESS_FS_WRITE_FILE,3571		},3572		{3573			.path = file_rt,3574			.access = LANDLOCK_ACCESS_FS_READ_FILE |3575				  LANDLOCK_ACCESS_FS_TRUNCATE,3576		},3577		{3578			.path = file_t,3579			.access = LANDLOCK_ACCESS_FS_TRUNCATE,3580		},3581		/* Implicitly: No access rights for file_none. */3582		{3583			.path = dir_t,3584			.access = LANDLOCK_ACCESS_FS_TRUNCATE,3585		},3586		{3587			.path = dir_w,3588			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,3589		},3590		{},3591	};3592	const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |3593			      LANDLOCK_ACCESS_FS_WRITE_FILE |3594			      LANDLOCK_ACCESS_FS_TRUNCATE;3595	int ruleset_fd;3596 3597	/* Enables Landlock. */3598	ruleset_fd = create_ruleset(_metadata, handled, rules);3599 3600	ASSERT_LE(0, ruleset_fd);3601	enforce_ruleset(_metadata, ruleset_fd);3602	ASSERT_EQ(0, close(ruleset_fd));3603 3604	/* Checks read, write and truncate rights: truncation works. */3605	EXPECT_EQ(0, test_truncate(file_rwt));3606	EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC));3607	EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC));3608 3609	/* Checks read and write rights: no truncate variant works. */3610	EXPECT_EQ(EACCES, test_truncate(file_rw));3611	EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC));3612	EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC));3613 3614	/*3615	 * Checks read and truncate rights: truncation works.3616	 *3617	 * Note: Files can get truncated using open() even with O_RDONLY.3618	 */3619	EXPECT_EQ(0, test_truncate(file_rt));3620	EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC));3621	EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC));3622 3623	/* Checks truncate right: truncate works, but can't open file. */3624	EXPECT_EQ(0, test_truncate(file_t));3625	EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC));3626	EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC));3627 3628	/* Checks "no rights" case: No form of truncation works. */3629	EXPECT_EQ(EACCES, test_truncate(file_none));3630	EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));3631	EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));3632 3633	/*3634	 * Checks truncate right on directory: truncate works on contained3635	 * files.3636	 */3637	EXPECT_EQ(0, test_truncate(file_in_dir_t));3638	EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC));3639	EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC));3640 3641	/*3642	 * Checks creat in dir_w: This requires the truncate right when3643	 * overwriting an existing file, but does not require it when the file3644	 * is new.3645	 */3646	EXPECT_EQ(EACCES, test_creat(file_in_dir_w));3647 3648	ASSERT_EQ(0, unlink(file_in_dir_w));3649	EXPECT_EQ(0, test_creat(file_in_dir_w));3650}3651 3652/* Invokes ftruncate(2) and returns its errno or 0. */3653static int test_ftruncate(int fd)3654{3655	if (ftruncate(fd, 10) < 0)3656		return errno;3657	return 0;3658}3659 3660TEST_F_FORK(layout1, ftruncate)3661{3662	/*3663	 * This test opens a new file descriptor at different stages of3664	 * Landlock restriction:3665	 *3666	 * without restriction:                    ftruncate works3667	 * something else but truncate restricted: ftruncate works3668	 * truncate restricted and permitted:      ftruncate works3669	 * truncate restricted and not permitted:  ftruncate fails3670	 *3671	 * Whether this works or not is expected to depend on the time when the3672	 * FD was opened, not to depend on the time when ftruncate() was3673	 * called.3674	 */3675	const char *const path = file1_s1d1;3676	const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE |3677			       LANDLOCK_ACCESS_FS_WRITE_FILE;3678	const struct rule layer1[] = {3679		{3680			.path = path,3681			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,3682		},3683		{},3684	};3685	const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE;3686	const struct rule layer2[] = {3687		{3688			.path = path,3689			.access = LANDLOCK_ACCESS_FS_TRUNCATE,3690		},3691		{},3692	};3693	const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE |3694			       LANDLOCK_ACCESS_FS_WRITE_FILE;3695	const struct rule layer3[] = {3696		{3697			.path = path,3698			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,3699		},3700		{},3701	};3702	int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd;3703 3704	fd_layer0 = open(path, O_WRONLY);3705	EXPECT_EQ(0, test_ftruncate(fd_layer0));3706 3707	ruleset_fd = create_ruleset(_metadata, handled1, layer1);3708	ASSERT_LE(0, ruleset_fd);3709	enforce_ruleset(_metadata, ruleset_fd);3710	ASSERT_EQ(0, close(ruleset_fd));3711 3712	fd_layer1 = open(path, O_WRONLY);3713	EXPECT_EQ(0, test_ftruncate(fd_layer0));3714	EXPECT_EQ(0, test_ftruncate(fd_layer1));3715 3716	ruleset_fd = create_ruleset(_metadata, handled2, layer2);3717	ASSERT_LE(0, ruleset_fd);3718	enforce_ruleset(_metadata, ruleset_fd);3719	ASSERT_EQ(0, close(ruleset_fd));3720 3721	fd_layer2 = open(path, O_WRONLY);3722	EXPECT_EQ(0, test_ftruncate(fd_layer0));3723	EXPECT_EQ(0, test_ftruncate(fd_layer1));3724	EXPECT_EQ(0, test_ftruncate(fd_layer2));3725 3726	ruleset_fd = create_ruleset(_metadata, handled3, layer3);3727	ASSERT_LE(0, ruleset_fd);3728	enforce_ruleset(_metadata, ruleset_fd);3729	ASSERT_EQ(0, close(ruleset_fd));3730 3731	fd_layer3 = open(path, O_WRONLY);3732	EXPECT_EQ(0, test_ftruncate(fd_layer0));3733	EXPECT_EQ(0, test_ftruncate(fd_layer1));3734	EXPECT_EQ(0, test_ftruncate(fd_layer2));3735	EXPECT_EQ(EACCES, test_ftruncate(fd_layer3));3736 3737	ASSERT_EQ(0, close(fd_layer0));3738	ASSERT_EQ(0, close(fd_layer1));3739	ASSERT_EQ(0, close(fd_layer2));3740	ASSERT_EQ(0, close(fd_layer3));3741}3742 3743/* clang-format off */3744FIXTURE(ftruncate) {};3745/* clang-format on */3746 3747FIXTURE_SETUP(ftruncate)3748{3749	prepare_layout(_metadata);3750	create_file(_metadata, file1_s1d1);3751}3752 3753FIXTURE_TEARDOWN_PARENT(ftruncate)3754{3755	EXPECT_EQ(0, remove_path(file1_s1d1));3756	cleanup_layout(_metadata);3757}3758 3759FIXTURE_VARIANT(ftruncate)3760{3761	const __u64 handled;3762	const __u64 allowed;3763	const int expected_open_result;3764	const int expected_ftruncate_result;3765};3766 3767/* clang-format off */3768FIXTURE_VARIANT_ADD(ftruncate, w_w) {3769	/* clang-format on */3770	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE,3771	.allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,3772	.expected_open_result = 0,3773	.expected_ftruncate_result = 0,3774};3775 3776/* clang-format off */3777FIXTURE_VARIANT_ADD(ftruncate, t_t) {3778	/* clang-format on */3779	.handled = LANDLOCK_ACCESS_FS_TRUNCATE,3780	.allowed = LANDLOCK_ACCESS_FS_TRUNCATE,3781	.expected_open_result = 0,3782	.expected_ftruncate_result = 0,3783};3784 3785/* clang-format off */3786FIXTURE_VARIANT_ADD(ftruncate, wt_w) {3787	/* clang-format on */3788	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,3789	.allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,3790	.expected_open_result = 0,3791	.expected_ftruncate_result = EACCES,3792};3793 3794/* clang-format off */3795FIXTURE_VARIANT_ADD(ftruncate, wt_wt) {3796	/* clang-format on */3797	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,3798	.allowed = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,3799	.expected_open_result = 0,3800	.expected_ftruncate_result = 0,3801};3802 3803/* clang-format off */3804FIXTURE_VARIANT_ADD(ftruncate, wt_t) {3805	/* clang-format on */3806	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,3807	.allowed = LANDLOCK_ACCESS_FS_TRUNCATE,3808	.expected_open_result = EACCES,3809};3810 3811TEST_F_FORK(ftruncate, open_and_ftruncate)3812{3813	const char *const path = file1_s1d1;3814	const struct rule rules[] = {3815		{3816			.path = path,3817			.access = variant->allowed,3818		},3819		{},3820	};3821	int fd, ruleset_fd;3822 3823	/* Enables Landlock. */3824	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);3825	ASSERT_LE(0, ruleset_fd);3826	enforce_ruleset(_metadata, ruleset_fd);3827	ASSERT_EQ(0, close(ruleset_fd));3828 3829	fd = open(path, O_WRONLY);3830	EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));3831	if (fd >= 0) {3832		EXPECT_EQ(variant->expected_ftruncate_result,3833			  test_ftruncate(fd));3834		ASSERT_EQ(0, close(fd));3835	}3836}3837 3838TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes)3839{3840	int child, fd, status;3841	int socket_fds[2];3842 3843	ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,3844				socket_fds));3845 3846	child = fork();3847	ASSERT_LE(0, child);3848	if (child == 0) {3849		/*3850		 * Enables Landlock in the child process, open a file descriptor3851		 * where truncation is forbidden and send it to the3852		 * non-landlocked parent process.3853		 */3854		const char *const path = file1_s1d1;3855		const struct rule rules[] = {3856			{3857				.path = path,3858				.access = variant->allowed,3859			},3860			{},3861		};3862		int fd, ruleset_fd;3863 3864		ruleset_fd = create_ruleset(_metadata, variant->handled, rules);3865		ASSERT_LE(0, ruleset_fd);3866		enforce_ruleset(_metadata, ruleset_fd);3867		ASSERT_EQ(0, close(ruleset_fd));3868 3869		fd = open(path, O_WRONLY);3870		ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));3871 3872		if (fd >= 0) {3873			ASSERT_EQ(0, send_fd(socket_fds[0], fd));3874			ASSERT_EQ(0, close(fd));3875		}3876 3877		ASSERT_EQ(0, close(socket_fds[0]));3878 3879		_exit(_metadata->exit_code);3880		return;3881	}3882 3883	if (variant->expected_open_result == 0) {3884		fd = recv_fd(socket_fds[1]);3885		ASSERT_LE(0, fd);3886 3887		EXPECT_EQ(variant->expected_ftruncate_result,3888			  test_ftruncate(fd));3889		ASSERT_EQ(0, close(fd));3890	}3891 3892	ASSERT_EQ(child, waitpid(child, &status, 0));3893	ASSERT_EQ(1, WIFEXITED(status));3894	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));3895 3896	ASSERT_EQ(0, close(socket_fds[0]));3897	ASSERT_EQ(0, close(socket_fds[1]));3898}3899 3900/* Invokes the FS_IOC_GETFLAGS IOCTL and returns its errno or 0. */3901static int test_fs_ioc_getflags_ioctl(int fd)3902{3903	uint32_t flags;3904 3905	if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0)3906		return errno;3907	return 0;3908}3909 3910TEST(memfd_ftruncate_and_ioctl)3911{3912	const struct landlock_ruleset_attr attr = {3913		.handled_access_fs = ACCESS_ALL,3914	};3915	int ruleset_fd, fd, i;3916 3917	/*3918	 * We exercise the same test both with and without Landlock enabled, to3919	 * ensure that it behaves the same in both cases.3920	 */3921	for (i = 0; i < 2; i++) {3922		/* Creates a new memfd. */3923		fd = memfd_create("name", MFD_CLOEXEC);3924		ASSERT_LE(0, fd);3925 3926		/*3927		 * Checks that operations associated with the opened file3928		 * (ftruncate, ioctl) are permitted on file descriptors that are3929		 * created in ways other than open(2).3930		 */3931		EXPECT_EQ(0, test_ftruncate(fd));3932		EXPECT_EQ(0, test_fs_ioc_getflags_ioctl(fd));3933 3934		ASSERT_EQ(0, close(fd));3935 3936		/* Enables Landlock. */3937		ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);3938		ASSERT_LE(0, ruleset_fd);3939		enforce_ruleset(_metadata, ruleset_fd);3940		ASSERT_EQ(0, close(ruleset_fd));3941	}3942}3943 3944static int test_fionread_ioctl(int fd)3945{3946	size_t sz = 0;3947 3948	if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES)3949		return errno;3950	return 0;3951}3952 3953TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl)3954{3955	const struct landlock_ruleset_attr attr = {3956		.handled_access_fs = ACCESS_ALL,3957	};3958	int ruleset_fd, fd;3959 3960	/*3961	 * Checks that for files opened with O_PATH, both ioctl(2) and3962	 * ftruncate(2) yield EBADF, as it is documented in open(2) for the3963	 * O_PATH flag.3964	 */3965	fd = open(dir_s1d1, O_PATH | O_CLOEXEC);3966	ASSERT_LE(0, fd);3967 3968	EXPECT_EQ(EBADF, test_ftruncate(fd));3969	EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd));3970 3971	ASSERT_EQ(0, close(fd));3972 3973	/* Enables Landlock. */3974	ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);3975	ASSERT_LE(0, ruleset_fd);3976	enforce_ruleset(_metadata, ruleset_fd);3977	ASSERT_EQ(0, close(ruleset_fd));3978 3979	/*3980	 * Checks that after enabling Landlock,3981	 * - the file can still be opened with O_PATH3982	 * - both ioctl and truncate still yield EBADF (not EACCES).3983	 */3984	fd = open(dir_s1d1, O_PATH | O_CLOEXEC);3985	ASSERT_LE(0, fd);3986 3987	EXPECT_EQ(EBADF, test_ftruncate(fd));3988	EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd));3989 3990	ASSERT_EQ(0, close(fd));3991}3992 3993/*3994 * ioctl_error - generically call the given ioctl with a pointer to a3995 * sufficiently large zeroed-out memory region.3996 *3997 * Returns the IOCTLs error, or 0.3998 */3999static int ioctl_error(struct __test_metadata *const _metadata, int fd,4000		       unsigned int cmd)4001{4002	char buf[128]; /* sufficiently large */4003	int res, stdinbak_fd;4004 4005	/*4006	 * Depending on the IOCTL command, parts of the zeroed-out buffer might4007	 * be interpreted as file descriptor numbers.  We do not want to4008	 * accidentally operate on file descriptor 0 (stdin), so we temporarily4009	 * move stdin to a different FD and close FD 0 for the IOCTL call.4010	 */4011	stdinbak_fd = dup(0);4012	ASSERT_LT(0, stdinbak_fd);4013	ASSERT_EQ(0, close(0));4014 4015	/* Invokes the IOCTL with a zeroed-out buffer. */4016	bzero(&buf, sizeof(buf));4017	res = ioctl(fd, cmd, &buf);4018 4019	/* Restores the old FD 0 and closes the backup FD. */4020	ASSERT_EQ(0, dup2(stdinbak_fd, 0));4021	ASSERT_EQ(0, close(stdinbak_fd));4022 4023	if (res < 0)4024		return errno;4025 4026	return 0;4027}4028 4029/* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */4030struct space_resv {4031	__s16 l_type;4032	__s16 l_whence;4033	__s64 l_start;4034	__s64 l_len; /* len == 0 means until end of file */4035	__s32 l_sysid;4036	__u32 l_pid;4037	__s32 l_pad[4]; /* reserved area */4038};4039 4040#define FS_IOC_RESVSP _IOW('X', 40, struct space_resv)4041#define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv)4042#define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv)4043#define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv)4044#define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv)4045 4046/*4047 * Tests a series of blanket-permitted and denied IOCTLs.4048 */4049TEST_F_FORK(layout1, blanket_permitted_ioctls)4050{4051	const struct landlock_ruleset_attr attr = {4052		.handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,4053	};4054	int ruleset_fd, fd;4055 4056	/* Enables Landlock. */4057	ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);4058	ASSERT_LE(0, ruleset_fd);4059	enforce_ruleset(_metadata, ruleset_fd);4060	ASSERT_EQ(0, close(ruleset_fd));4061 4062	fd = open("/dev/null", O_RDWR | O_CLOEXEC);4063	ASSERT_LE(0, fd);4064 4065	/*4066	 * Checks permitted commands.4067	 * These ones may return errors, but should not be blocked by Landlock.4068	 */4069	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX));4070	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX));4071	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO));4072	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC));4073	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE));4074	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE));4075	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW));4076	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP));4077	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ));4078	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE));4079	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE));4080	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE));4081	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID));4082	EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH));4083 4084	/*4085	 * Checks blocked commands.4086	 * A call to a blocked IOCTL command always returns EACCES.4087	 */4088	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD));4089	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS));4090	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS));4091	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR));4092	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR));4093	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP));4094	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP));4095	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64));4096	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP));4097	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64));4098	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE));4099 4100	/* Default case is also blocked. */4101	EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee));4102 4103	ASSERT_EQ(0, close(fd));4104}4105 4106/*4107 * Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right,4108 * because they are not character or block devices.4109 */4110TEST_F_FORK(layout1, named_pipe_ioctl)4111{4112	pid_t child_pid;4113	int fd, ruleset_fd;4114	const char *const path = file1_s1d1;4115	const struct landlock_ruleset_attr attr = {4116		.handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,4117	};4118 4119	ASSERT_EQ(0, unlink(path));4120	ASSERT_EQ(0, mkfifo(path, 0600));4121 4122	/* Enables Landlock. */4123	ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);4124	ASSERT_LE(0, ruleset_fd);4125	enforce_ruleset(_metadata, ruleset_fd);4126	ASSERT_EQ(0, close(ruleset_fd));4127 4128	/* The child process opens the pipe for writing. */4129	child_pid = fork();4130	ASSERT_NE(-1, child_pid);4131	if (child_pid == 0) {4132		fd = open(path, O_WRONLY);4133		close(fd);4134		exit(0);4135	}4136 4137	fd = open(path, O_RDONLY);4138	ASSERT_LE(0, fd);4139 4140	/* FIONREAD is implemented by pipefifo_fops. */4141	EXPECT_EQ(0, test_fionread_ioctl(fd));4142 4143	ASSERT_EQ(0, close(fd));4144	ASSERT_EQ(0, unlink(path));4145 4146	ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0));4147}4148 4149/* For named UNIX domain sockets, no IOCTL restrictions apply. */4150TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)4151{4152	const char *const path = file1_s1d1;4153	int srv_fd, cli_fd, ruleset_fd;4154	socklen_t size;4155	struct sockaddr_un srv_un, cli_un;4156	const struct landlock_ruleset_attr attr = {4157		.handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,4158	};4159 4160	/* Sets up a server */4161	srv_un.sun_family = AF_UNIX;4162	strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path));4163 4164	ASSERT_EQ(0, unlink(path));4165	srv_fd = socket(AF_UNIX, SOCK_STREAM, 0);4166	ASSERT_LE(0, srv_fd);4167 4168	size = offsetof(struct sockaddr_un, sun_path) + strlen(srv_un.sun_path);4169	ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, size));4170	ASSERT_EQ(0, listen(srv_fd, 10 /* qlen */));4171 4172	/* Enables Landlock. */4173	ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);4174	ASSERT_LE(0, ruleset_fd);4175	enforce_ruleset(_metadata, ruleset_fd);4176	ASSERT_EQ(0, close(ruleset_fd));4177 4178	/* Sets up a client connection to it */4179	cli_un.sun_family = AF_UNIX;4180	cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);4181	ASSERT_LE(0, cli_fd);4182 4183	size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path);4184	ASSERT_EQ(0, bind(cli_fd, (struct sockaddr *)&cli_un, size));4185 4186	bzero(&cli_un, sizeof(cli_un));4187	cli_un.sun_family = AF_UNIX;4188	strncpy(cli_un.sun_path, path, sizeof(cli_un.sun_path));4189	size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path);4190 4191	ASSERT_EQ(0, connect(cli_fd, (struct sockaddr *)&cli_un, size));4192 4193	/* FIONREAD and other IOCTLs should not be forbidden. */4194	EXPECT_EQ(0, test_fionread_ioctl(cli_fd));4195 4196	ASSERT_EQ(0, close(cli_fd));4197}4198 4199/* clang-format off */4200FIXTURE(ioctl) {};4201 4202FIXTURE_SETUP(ioctl) {};4203 4204FIXTURE_TEARDOWN(ioctl) {};4205/* clang-format on */4206 4207FIXTURE_VARIANT(ioctl)4208{4209	const __u64 handled;4210	const __u64 allowed;4211	const mode_t open_mode;4212	/*4213	 * FIONREAD is used as a characteristic device-specific IOCTL command.4214	 * It is implemented in fs/ioctl.c for regular files,4215	 * but we do not blanket-permit it for devices.4216	 */4217	const int expected_fionread_result;4218};4219 4220/* clang-format off */4221FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) {4222	/* clang-format on */4223	.handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,4224	.allowed = 0,4225	.open_mode = O_RDWR,4226	.expected_fionread_result = EACCES,4227};4228 4229/* clang-format off */4230FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) {4231	/* clang-format on */4232	.handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,4233	.allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV,4234	.open_mode = O_RDWR,4235	.expected_fionread_result = 0,4236};4237 4238/* clang-format off */4239FIXTURE_VARIANT_ADD(ioctl, unhandled) {4240	/* clang-format on */4241	.handled = LANDLOCK_ACCESS_FS_EXECUTE,4242	.allowed = LANDLOCK_ACCESS_FS_EXECUTE,4243	.open_mode = O_RDWR,4244	.expected_fionread_result = 0,4245};4246 4247TEST_F_FORK(ioctl, handle_dir_access_file)4248{4249	const int flag = 0;4250	const struct rule rules[] = {4251		{4252			.path = "/dev",4253			.access = variant->allowed,4254		},4255		{},4256	};4257	int file_fd, ruleset_fd;4258 4259	/* Enables Landlock. */4260	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);4261	ASSERT_LE(0, ruleset_fd);4262	enforce_ruleset(_metadata, ruleset_fd);4263	ASSERT_EQ(0, close(ruleset_fd));4264 4265	file_fd = open("/dev/zero", variant->open_mode);4266	ASSERT_LE(0, file_fd);4267 4268	/* Checks that IOCTL commands return the expected errors. */4269	EXPECT_EQ(variant->expected_fionread_result,4270		  test_fionread_ioctl(file_fd));4271 4272	/* Checks that unrestrictable commands are unrestricted. */4273	EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));4274	EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));4275	EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));4276	EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));4277	EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag));4278 4279	ASSERT_EQ(0, close(file_fd));4280}4281 4282TEST_F_FORK(ioctl, handle_dir_access_dir)4283{4284	const int flag = 0;4285	const struct rule rules[] = {4286		{4287			.path = "/dev",4288			.access = variant->allowed,4289		},4290		{},4291	};4292	int dir_fd, ruleset_fd;4293 4294	/* Enables Landlock. */4295	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);4296	ASSERT_LE(0, ruleset_fd);4297	enforce_ruleset(_metadata, ruleset_fd);4298	ASSERT_EQ(0, close(ruleset_fd));4299 4300	/*4301	 * Ignore variant->open_mode for this test, as we intend to open a4302	 * directory.  If the directory can not be opened, the variant is4303	 * infeasible to test with an opened directory.4304	 */4305	dir_fd = open("/dev", O_RDONLY);4306	if (dir_fd < 0)4307		return;4308 4309	/*4310	 * Checks that IOCTL commands return the expected errors.4311	 * We do not use the expected values from the fixture here.4312	 *4313	 * When using IOCTL on a directory, no Landlock restrictions apply.4314	 */4315	EXPECT_EQ(0, test_fionread_ioctl(dir_fd));4316 4317	/* Checks that unrestrictable commands are unrestricted. */4318	EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX));4319	EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX));4320	EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag));4321	EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag));4322	EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag));4323 4324	ASSERT_EQ(0, close(dir_fd));4325}4326 4327TEST_F_FORK(ioctl, handle_file_access_file)4328{4329	const int flag = 0;4330	const struct rule rules[] = {4331		{4332			.path = "/dev/zero",4333			.access = variant->allowed,4334		},4335		{},4336	};4337	int file_fd, ruleset_fd;4338 4339	/* Enables Landlock. */4340	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);4341	ASSERT_LE(0, ruleset_fd);4342	enforce_ruleset(_metadata, ruleset_fd);4343	ASSERT_EQ(0, close(ruleset_fd));4344 4345	file_fd = open("/dev/zero", variant->open_mode);4346	ASSERT_LE(0, file_fd)4347	{4348		TH_LOG("Failed to open /dev/zero: %s", strerror(errno));4349	}4350 4351	/* Checks that IOCTL commands return the expected errors. */4352	EXPECT_EQ(variant->expected_fionread_result,4353		  test_fionread_ioctl(file_fd));4354 4355	/* Checks that unrestrictable commands are unrestricted. */4356	EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));4357	EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));4358	EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));4359	EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));4360	EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag));4361 4362	ASSERT_EQ(0, close(file_fd));4363}4364 4365/* clang-format off */4366FIXTURE(layout1_bind) {};4367/* clang-format on */4368 4369FIXTURE_SETUP(layout1_bind)4370{4371	prepare_layout(_metadata);4372 4373	create_layout1(_metadata);4374 4375	set_cap(_metadata, CAP_SYS_ADMIN);4376	ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));4377	clear_cap(_metadata, CAP_SYS_ADMIN);4378}4379 4380FIXTURE_TEARDOWN_PARENT(layout1_bind)4381{4382	/* umount(dir_s2d2)) is handled by namespace lifetime. */4383 4384	remove_layout1(_metadata);4385 4386	cleanup_layout(_metadata);4387}4388 4389static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3";4390static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";4391 4392/*4393 * layout1_bind hierarchy:4394 *4395 * tmp4396 * ├── s1d14397 * │   ├── f14398 * │   ├── f24399 * │   └── s1d24400 * │       ├── f14401 * │       ├── f24402 * │       └── s1d34403 * │           ├── f14404 * │           └── f24405 * ├── s2d14406 * │   ├── f14407 * │   └── s2d24408 * │       ├── f14409 * │       ├── f24410 * │       └── s1d34411 * │           ├── f14412 * │           └── f24413 * └── s3d14414 *     └── s3d24415 *         └── s3d34416 */4417 4418TEST_F_FORK(layout1_bind, no_restriction)4419{4420	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));4421	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));4422	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));4423	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));4424	ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));4425	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));4426 4427	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));4428	ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));4429	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));4430	ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));4431	ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY));4432	ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY));4433 4434	ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY));4435	ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));4436 4437	ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));4438}4439 4440TEST_F_FORK(layout1_bind, same_content_same_file)4441{4442	/*4443	 * Sets access right on parent directories of both source and4444	 * destination mount points.4445	 */4446	const struct rule layer1_parent[] = {4447		{4448			.path = dir_s1d1,4449			.access = ACCESS_RO,4450		},4451		{4452			.path = dir_s2d1,4453			.access = ACCESS_RW,4454		},4455		{},4456	};4457	/*4458	 * Sets access rights on the same bind-mounted directories.  The result4459	 * should be ACCESS_RW for both directories, but not both hierarchies4460	 * because of the first layer.4461	 */4462	const struct rule layer2_mount_point[] = {4463		{4464			.path = dir_s1d2,4465			.access = LANDLOCK_ACCESS_FS_READ_FILE,4466		},4467		{4468			.path = dir_s2d2,4469			.access = ACCESS_RW,4470		},4471		{},4472	};4473	/* Only allow read-access to the s1d3 hierarchies. */4474	const struct rule layer3_source[] = {4475		{4476			.path = dir_s1d3,4477			.access = LANDLOCK_ACCESS_FS_READ_FILE,4478		},4479		{},4480	};4481	/* Removes all access rights. */4482	const struct rule layer4_destination[] = {4483		{4484			.path = bind_file1_s1d3,4485			.access = LANDLOCK_ACCESS_FS_WRITE_FILE,4486		},4487		{},4488	};4489	int ruleset_fd;4490 4491	/* Sets rules for the parent directories. */4492	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent);4493	ASSERT_LE(0, ruleset_fd);4494	enforce_ruleset(_metadata, ruleset_fd);4495	ASSERT_EQ(0, close(ruleset_fd));4496 4497	/* Checks source hierarchy. */4498	ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));4499	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));4500	ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));4501 4502	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));4503	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));4504	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));4505 4506	/* Checks destination hierarchy. */4507	ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR));4508	ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));4509 4510	ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));4511	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));4512 4513	/* Sets rules for the mount points. */4514	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point);4515	ASSERT_LE(0, ruleset_fd);4516	enforce_ruleset(_metadata, ruleset_fd);4517	ASSERT_EQ(0, close(ruleset_fd));4518 4519	/* Checks source hierarchy. */4520	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));4521	ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));4522	ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));4523 4524	ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));4525	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));4526	ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));4527 4528	/* Checks destination hierarchy. */4529	ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY));4530	ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY));4531	ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));4532 4533	ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));4534	ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));4535	ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));4536 4537	/* Sets a (shared) rule only on the source. */4538	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source);4539	ASSERT_LE(0, ruleset_fd);4540	enforce_ruleset(_metadata, ruleset_fd);4541	ASSERT_EQ(0, close(ruleset_fd));4542 4543	/* Checks source hierarchy. */4544	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));4545	ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));4546	ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));4547 4548	ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));4549	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));4550	ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));4551 4552	/* Checks destination hierarchy. */4553	ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY));4554	ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY));4555	ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));4556 4557	ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));4558	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));4559	ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));4560 4561	/* Sets a (shared) rule only on the destination. */4562	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination);4563	ASSERT_LE(0, ruleset_fd);4564	enforce_ruleset(_metadata, ruleset_fd);4565	ASSERT_EQ(0, close(ruleset_fd));4566 4567	/* Checks source hierarchy. */4568	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));4569	ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));4570 4571	/* Checks destination hierarchy. */4572	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY));4573	ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));4574}4575 4576TEST_F_FORK(layout1_bind, reparent_cross_mount)4577{4578	const struct rule layer1[] = {4579		{4580			/* dir_s2d1 is beneath the dir_s2d2 mount point. */4581			.path = dir_s2d1,4582			.access = LANDLOCK_ACCESS_FS_REFER,4583		},4584		{4585			.path = bind_dir_s1d3,4586			.access = LANDLOCK_ACCESS_FS_EXECUTE,4587		},4588		{},4589	};4590	int ruleset_fd = create_ruleset(4591		_metadata,4592		LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1);4593 4594	ASSERT_LE(0, ruleset_fd);4595	enforce_ruleset(_metadata, ruleset_fd);4596	ASSERT_EQ(0, close(ruleset_fd));4597 4598	/* Checks basic denied move. */4599	ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2));4600	ASSERT_EQ(EXDEV, errno);4601 4602	/* Checks real cross-mount move (Landlock is not involved). */4603	ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2));4604	ASSERT_EQ(EXDEV, errno);4605 4606	/* Checks move that will give more accesses. */4607	ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3));4608	ASSERT_EQ(EXDEV, errno);4609 4610	/* Checks legitimate downgrade move. */4611	ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2));4612}4613 4614#define LOWER_BASE TMP_DIR "/lower"4615#define LOWER_DATA LOWER_BASE "/data"4616static const char lower_fl1[] = LOWER_DATA "/fl1";4617static const char lower_dl1[] = LOWER_DATA "/dl1";4618static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2";4619static const char lower_fo1[] = LOWER_DATA "/fo1";4620static const char lower_do1[] = LOWER_DATA "/do1";4621static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2";4622static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3";4623 4624static const char (*lower_base_files[])[] = {4625	&lower_fl1,4626	&lower_fo1,4627	NULL,4628};4629static const char (*lower_base_directories[])[] = {4630	&lower_dl1,4631	&lower_do1,4632	NULL,4633};4634static const char (*lower_sub_files[])[] = {4635	&lower_dl1_fl2,4636	&lower_do1_fo2,4637	&lower_do1_fl3,4638	NULL,4639};4640 4641#define UPPER_BASE TMP_DIR "/upper"4642#define UPPER_DATA UPPER_BASE "/data"4643#define UPPER_WORK UPPER_BASE "/work"4644static const char upper_fu1[] = UPPER_DATA "/fu1";4645static const char upper_du1[] = UPPER_DATA "/du1";4646static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2";4647static const char upper_fo1[] = UPPER_DATA "/fo1";4648static const char upper_do1[] = UPPER_DATA "/do1";4649static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2";4650static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3";4651 4652static const char (*upper_base_files[])[] = {4653	&upper_fu1,4654	&upper_fo1,4655	NULL,4656};4657static const char (*upper_base_directories[])[] = {4658	&upper_du1,4659	&upper_do1,4660	NULL,4661};4662static const char (*upper_sub_files[])[] = {4663	&upper_du1_fu2,4664	&upper_do1_fo2,4665	&upper_do1_fu3,4666	NULL,4667};4668 4669#define MERGE_BASE TMP_DIR "/merge"4670#define MERGE_DATA MERGE_BASE "/data"4671static const char merge_fl1[] = MERGE_DATA "/fl1";4672static const char merge_dl1[] = MERGE_DATA "/dl1";4673static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2";4674static const char merge_fu1[] = MERGE_DATA "/fu1";4675static const char merge_du1[] = MERGE_DATA "/du1";4676static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2";4677static const char merge_fo1[] = MERGE_DATA "/fo1";4678static const char merge_do1[] = MERGE_DATA "/do1";4679static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2";4680static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3";4681static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3";4682 4683static const char (*merge_base_files[])[] = {4684	&merge_fl1,4685	&merge_fu1,4686	&merge_fo1,4687	NULL,4688};4689static const char (*merge_base_directories[])[] = {4690	&merge_dl1,4691	&merge_du1,4692	&merge_do1,4693	NULL,4694};4695static const char (*merge_sub_files[])[] = {4696	&merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2,4697	&merge_do1_fl3, &merge_do1_fu3, NULL,4698};4699 4700/*4701 * layout2_overlay hierarchy:4702 *4703 * tmp4704 * ├── lower4705 * │   └── data4706 * │       ├── dl14707 * │       │   └── fl24708 * │       ├── do14709 * │       │   ├── fl34710 * │       │   └── fo24711 * │       ├── fl14712 * │       └── fo14713 * ├── merge4714 * │   └── data4715 * │       ├── dl14716 * │       │   └── fl24717 * │       ├── do14718 * │       │   ├── fl34719 * │       │   ├── fo24720 * │       │   └── fu34721 * │       ├── du14722 * │       │   └── fu24723 * │       ├── fl14724 * │       ├── fo14725 * │       └── fu14726 * └── upper4727 *     ├── data4728 *     │   ├── do14729 *     │   │   ├── fo24730 *     │   │   └── fu34731 *     │   ├── du14732 *     │   │   └── fu24733 *     │   ├── fo14734 *     │   └── fu14735 *     └── work4736 *         └── work4737 */4738 4739FIXTURE(layout2_overlay)4740{4741	bool skip_test;4742};4743 4744FIXTURE_SETUP(layout2_overlay)4745{4746	if (!supports_filesystem("overlay")) {4747		self->skip_test = true;4748		SKIP(return, "overlayfs is not supported (setup)");4749	}4750 4751	prepare_layout(_metadata);4752 4753	create_directory(_metadata, LOWER_BASE);4754	set_cap(_metadata, CAP_SYS_ADMIN);4755	/* Creates tmpfs mount points to get deterministic overlayfs. */4756	ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE));4757	clear_cap(_metadata, CAP_SYS_ADMIN);4758	create_file(_metadata, lower_fl1);4759	create_file(_metadata, lower_dl1_fl2);4760	create_file(_metadata, lower_fo1);4761	create_file(_metadata, lower_do1_fo2);4762	create_file(_metadata, lower_do1_fl3);4763 4764	create_directory(_metadata, UPPER_BASE);4765	set_cap(_metadata, CAP_SYS_ADMIN);4766	ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE));4767	clear_cap(_metadata, CAP_SYS_ADMIN);4768	create_file(_metadata, upper_fu1);4769	create_file(_metadata, upper_du1_fu2);4770	create_file(_metadata, upper_fo1);4771	create_file(_metadata, upper_do1_fo2);4772	create_file(_metadata, upper_do1_fu3);4773	ASSERT_EQ(0, mkdir(UPPER_WORK, 0700));4774 4775	create_directory(_metadata, MERGE_DATA);4776	set_cap(_metadata, CAP_SYS_ADMIN);4777	set_cap(_metadata, CAP_DAC_OVERRIDE);4778	ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0,4779			   "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA4780			   ",workdir=" UPPER_WORK));4781	clear_cap(_metadata, CAP_DAC_OVERRIDE);4782	clear_cap(_metadata, CAP_SYS_ADMIN);4783}4784 4785FIXTURE_TEARDOWN_PARENT(layout2_overlay)4786{4787	if (self->skip_test)4788		SKIP(return, "overlayfs is not supported (teardown)");4789 4790	EXPECT_EQ(0, remove_path(lower_do1_fl3));4791	EXPECT_EQ(0, remove_path(lower_dl1_fl2));4792	EXPECT_EQ(0, remove_path(lower_fl1));4793	EXPECT_EQ(0, remove_path(lower_do1_fo2));4794	EXPECT_EQ(0, remove_path(lower_fo1));4795 4796	/* umount(LOWER_BASE)) is handled by namespace lifetime. */4797	EXPECT_EQ(0, remove_path(LOWER_BASE));4798 4799	EXPECT_EQ(0, remove_path(upper_do1_fu3));4800	EXPECT_EQ(0, remove_path(upper_du1_fu2));4801	EXPECT_EQ(0, remove_path(upper_fu1));4802	EXPECT_EQ(0, remove_path(upper_do1_fo2));4803	EXPECT_EQ(0, remove_path(upper_fo1));4804	EXPECT_EQ(0, remove_path(UPPER_WORK "/work"));4805 4806	/* umount(UPPER_BASE)) is handled by namespace lifetime. */4807	EXPECT_EQ(0, remove_path(UPPER_BASE));4808 4809	/* umount(MERGE_DATA)) is handled by namespace lifetime. */4810	EXPECT_EQ(0, remove_path(MERGE_DATA));4811 4812	cleanup_layout(_metadata);4813}4814 4815TEST_F_FORK(layout2_overlay, no_restriction)4816{4817	if (self->skip_test)4818		SKIP(return, "overlayfs is not supported (test)");4819 4820	ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));4821	ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));4822	ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY));4823	ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY));4824	ASSERT_EQ(0, test_open(lower_do1, O_RDONLY));4825	ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY));4826	ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY));4827 4828	ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY));4829	ASSERT_EQ(0, test_open(upper_du1, O_RDONLY));4830	ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY));4831	ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY));4832	ASSERT_EQ(0, test_open(upper_do1, O_RDONLY));4833	ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY));4834	ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY));4835 4836	ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY));4837	ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY));4838	ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY));4839	ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY));4840	ASSERT_EQ(0, test_open(merge_du1, O_RDONLY));4841	ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY));4842	ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY));4843	ASSERT_EQ(0, test_open(merge_do1, O_RDONLY));4844	ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY));4845	ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY));4846	ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY));4847}4848 4849#define for_each_path(path_list, path_entry, i)               \4850	for (i = 0, path_entry = *path_list[i]; path_list[i]; \4851	     path_entry = *path_list[++i])4852 4853TEST_F_FORK(layout2_overlay, same_content_different_file)4854{4855	/* Sets access right on parent directories of both layers. */4856	const struct rule layer1_base[] = {4857		{4858			.path = LOWER_BASE,4859			.access = LANDLOCK_ACCESS_FS_READ_FILE,4860		},4861		{4862			.path = UPPER_BASE,4863			.access = LANDLOCK_ACCESS_FS_READ_FILE,4864		},4865		{4866			.path = MERGE_BASE,4867			.access = ACCESS_RW,4868		},4869		{},4870	};4871	const struct rule layer2_data[] = {4872		{4873			.path = LOWER_DATA,4874			.access = LANDLOCK_ACCESS_FS_READ_FILE,4875		},4876		{4877			.path = UPPER_DATA,4878			.access = LANDLOCK_ACCESS_FS_READ_FILE,4879		},4880		{4881			.path = MERGE_DATA,4882			.access = ACCESS_RW,4883		},4884		{},4885	};4886	/* Sets access right on directories inside both layers. */4887	const struct rule layer3_subdirs[] = {4888		{4889			.path = lower_dl1,4890			.access = LANDLOCK_ACCESS_FS_READ_FILE,4891		},4892		{4893			.path = lower_do1,4894			.access = LANDLOCK_ACCESS_FS_READ_FILE,4895		},4896		{4897			.path = upper_du1,4898			.access = LANDLOCK_ACCESS_FS_READ_FILE,4899		},4900		{4901			.path = upper_do1,4902			.access = LANDLOCK_ACCESS_FS_READ_FILE,4903		},4904		{4905			.path = merge_dl1,4906			.access = ACCESS_RW,4907		},4908		{4909			.path = merge_du1,4910			.access = ACCESS_RW,4911		},4912		{4913			.path = merge_do1,4914			.access = ACCESS_RW,4915		},4916		{},4917	};4918	/* Tighten access rights to the files. */4919	const struct rule layer4_files[] = {4920		{4921			.path = lower_dl1_fl2,4922			.access = LANDLOCK_ACCESS_FS_READ_FILE,4923		},4924		{4925			.path = lower_do1_fo2,4926			.access = LANDLOCK_ACCESS_FS_READ_FILE,4927		},4928		{4929			.path = lower_do1_fl3,4930			.access = LANDLOCK_ACCESS_FS_READ_FILE,4931		},4932		{4933			.path = upper_du1_fu2,4934			.access = LANDLOCK_ACCESS_FS_READ_FILE,4935		},4936		{4937			.path = upper_do1_fo2,4938			.access = LANDLOCK_ACCESS_FS_READ_FILE,4939		},4940		{4941			.path = upper_do1_fu3,4942			.access = LANDLOCK_ACCESS_FS_READ_FILE,4943		},4944		{4945			.path = merge_dl1_fl2,4946			.access = LANDLOCK_ACCESS_FS_READ_FILE |4947				  LANDLOCK_ACCESS_FS_WRITE_FILE,4948		},4949		{4950			.path = merge_du1_fu2,4951			.access = LANDLOCK_ACCESS_FS_READ_FILE |4952				  LANDLOCK_ACCESS_FS_WRITE_FILE,4953		},4954		{4955			.path = merge_do1_fo2,4956			.access = LANDLOCK_ACCESS_FS_READ_FILE |4957				  LANDLOCK_ACCESS_FS_WRITE_FILE,4958		},4959		{4960			.path = merge_do1_fl3,4961			.access = LANDLOCK_ACCESS_FS_READ_FILE |4962				  LANDLOCK_ACCESS_FS_WRITE_FILE,4963		},4964		{4965			.path = merge_do1_fu3,4966			.access = LANDLOCK_ACCESS_FS_READ_FILE |4967				  LANDLOCK_ACCESS_FS_WRITE_FILE,4968		},4969		{},4970	};4971	const struct rule layer5_merge_only[] = {4972		{4973			.path = MERGE_DATA,4974			.access = LANDLOCK_ACCESS_FS_READ_FILE |4975				  LANDLOCK_ACCESS_FS_WRITE_FILE,4976		},4977		{},4978	};4979	int ruleset_fd;4980	size_t i;4981	const char *path_entry;4982 4983	if (self->skip_test)4984		SKIP(return, "overlayfs is not supported (test)");4985 4986	/* Sets rules on base directories (i.e. outside overlay scope). */4987	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);4988	ASSERT_LE(0, ruleset_fd);4989	enforce_ruleset(_metadata, ruleset_fd);4990	ASSERT_EQ(0, close(ruleset_fd));4991 4992	/* Checks lower layer. */4993	for_each_path(lower_base_files, path_entry, i) {4994		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));4995		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));4996	}4997	for_each_path(lower_base_directories, path_entry, i) {4998		ASSERT_EQ(EACCES,4999			  test_open(path_entry, O_RDONLY | O_DIRECTORY));5000	}5001	for_each_path(lower_sub_files, path_entry, i) {5002		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));5003		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));5004	}5005	/* Checks upper layer. */5006	for_each_path(upper_base_files, path_entry, i) {5007		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));5008		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));5009	}5010	for_each_path(upper_base_directories, path_entry, i) {5011		ASSERT_EQ(EACCES,5012			  test_open(path_entry, O_RDONLY | O_DIRECTORY));5013	}5014	for_each_path(upper_sub_files, path_entry, i) {5015		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));5016		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));5017	}5018	/*5019	 * Checks that access rights are independent from the lower and upper5020	 * layers: write access to upper files viewed through the merge point5021	 * is still allowed, and write access to lower file viewed (and copied)5022	 * through the merge point is still allowed.5023	 */5024	for_each_path(merge_base_files, path_entry, i) {5025		ASSERT_EQ(0, test_open(path_entry, O_RDWR));5026	}5027	for_each_path(merge_base_directories, path_entry, i) {5028		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));5029	}5030	for_each_path(merge_sub_files, path_entry, i) {5031		ASSERT_EQ(0, test_open(path_entry, O_RDWR));5032	}5033 5034	/* Sets rules on data directories (i.e. inside overlay scope). */5035	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data);5036	ASSERT_LE(0, ruleset_fd);5037	enforce_ruleset(_metadata, ruleset_fd);5038	ASSERT_EQ(0, close(ruleset_fd));5039 5040	/* Checks merge. */5041	for_each_path(merge_base_files, path_entry, i) {5042		ASSERT_EQ(0, test_open(path_entry, O_RDWR));5043	}5044	for_each_path(merge_base_directories, path_entry, i) {5045		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));5046	}5047	for_each_path(merge_sub_files, path_entry, i) {5048		ASSERT_EQ(0, test_open(path_entry, O_RDWR));5049	}5050 5051	/* Same checks with tighter rules. */5052	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs);5053	ASSERT_LE(0, ruleset_fd);5054	enforce_ruleset(_metadata, ruleset_fd);5055	ASSERT_EQ(0, close(ruleset_fd));5056 5057	/* Checks changes for lower layer. */5058	for_each_path(lower_base_files, path_entry, i) {5059		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));5060	}5061	/* Checks changes for upper layer. */5062	for_each_path(upper_base_files, path_entry, i) {5063		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));5064	}5065	/* Checks all merge accesses. */5066	for_each_path(merge_base_files, path_entry, i) {5067		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));5068	}5069	for_each_path(merge_base_directories, path_entry, i) {5070		ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));5071	}5072	for_each_path(merge_sub_files, path_entry, i) {5073		ASSERT_EQ(0, test_open(path_entry, O_RDWR));5074	}5075 5076	/* Sets rules directly on overlayed files. */5077	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files);5078	ASSERT_LE(0, ruleset_fd);5079	enforce_ruleset(_metadata, ruleset_fd);5080	ASSERT_EQ(0, close(ruleset_fd));5081 5082	/* Checks unchanged accesses on lower layer. */5083	for_each_path(lower_sub_files, path_entry, i) {5084		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));5085		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));5086	}5087	/* Checks unchanged accesses on upper layer. */5088	for_each_path(upper_sub_files, path_entry, i) {5089		ASSERT_EQ(0, test_open(path_entry, O_RDONLY));5090		ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));5091	}5092	/* Checks all merge accesses. */5093	for_each_path(merge_base_files, path_entry, i) {5094		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));5095	}5096	for_each_path(merge_base_directories, path_entry, i) {5097		ASSERT_EQ(EACCES,5098			  test_open(path_entry, O_RDONLY | O_DIRECTORY));5099	}5100	for_each_path(merge_sub_files, path_entry, i) {5101		ASSERT_EQ(0, test_open(path_entry, O_RDWR));5102	}5103 5104	/* Only allowes access to the merge hierarchy. */5105	ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only);5106	ASSERT_LE(0, ruleset_fd);5107	enforce_ruleset(_metadata, ruleset_fd);5108	ASSERT_EQ(0, close(ruleset_fd));5109 5110	/* Checks new accesses on lower layer. */5111	for_each_path(lower_sub_files, path_entry, i) {5112		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));5113	}5114	/* Checks new accesses on upper layer. */5115	for_each_path(upper_sub_files, path_entry, i) {5116		ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));5117	}5118	/* Checks all merge accesses. */5119	for_each_path(merge_base_files, path_entry, i) {5120		ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));5121	}5122	for_each_path(merge_base_directories, path_entry, i) {5123		ASSERT_EQ(EACCES,5124			  test_open(path_entry, O_RDONLY | O_DIRECTORY));5125	}5126	for_each_path(merge_sub_files, path_entry, i) {5127		ASSERT_EQ(0, test_open(path_entry, O_RDWR));5128	}5129}5130 5131FIXTURE(layout3_fs)5132{5133	bool has_created_dir;5134	bool has_created_file;5135	bool skip_test;5136};5137 5138FIXTURE_VARIANT(layout3_fs)5139{5140	const struct mnt_opt mnt;5141	const char *const file_path;5142	unsigned int cwd_fs_magic;5143};5144 5145/* clang-format off */5146FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) {5147	/* clang-format on */5148	.mnt = {5149		.type = "tmpfs",5150		.data = MNT_TMP_DATA,5151	},5152	.file_path = file1_s1d1,5153};5154 5155FIXTURE_VARIANT_ADD(layout3_fs, ramfs) {5156	.mnt = {5157		.type = "ramfs",5158		.data = "mode=700",5159	},5160	.file_path = TMP_DIR "/dir/file",5161};5162 5163FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) {5164	.mnt = {5165		.type = "cgroup2",5166	},5167	.file_path = TMP_DIR "/test/cgroup.procs",5168};5169 5170FIXTURE_VARIANT_ADD(layout3_fs, proc) {5171	.mnt = {5172		.type = "proc",5173	},5174	.file_path = TMP_DIR "/self/status",5175};5176 5177FIXTURE_VARIANT_ADD(layout3_fs, sysfs) {5178	.mnt = {5179		.type = "sysfs",5180	},5181	.file_path = TMP_DIR "/kernel/notes",5182};5183 5184FIXTURE_VARIANT_ADD(layout3_fs, hostfs) {5185	.mnt = {5186		.source = TMP_DIR,5187		.flags = MS_BIND,5188	},5189	.file_path = TMP_DIR "/dir/file",5190	.cwd_fs_magic = HOSTFS_SUPER_MAGIC,5191};5192 5193static char *dirname_alloc(const char *path)5194{5195	char *dup;5196 5197	if (!path)5198		return NULL;5199 5200	dup = strdup(path);5201	if (!dup)5202		return NULL;5203 5204	return dirname(dup);5205}5206 5207FIXTURE_SETUP(layout3_fs)5208{5209	struct stat statbuf;5210	char *dir_path = dirname_alloc(variant->file_path);5211 5212	if (!supports_filesystem(variant->mnt.type) ||5213	    !cwd_matches_fs(variant->cwd_fs_magic)) {5214		self->skip_test = true;5215		SKIP(return, "this filesystem is not supported (setup)");5216	}5217 5218	prepare_layout_opt(_metadata, &variant->mnt);5219 5220	/* Creates directory when required. */5221	if (stat(dir_path, &statbuf)) {5222		set_cap(_metadata, CAP_DAC_OVERRIDE);5223		EXPECT_EQ(0, mkdir(dir_path, 0700))5224		{5225			TH_LOG("Failed to create directory \"%s\": %s",5226			       dir_path, strerror(errno));5227		}5228		self->has_created_dir = true;5229		clear_cap(_metadata, CAP_DAC_OVERRIDE);5230	}5231 5232	/* Creates file when required. */5233	if (stat(variant->file_path, &statbuf)) {5234		int fd;5235 5236		set_cap(_metadata, CAP_DAC_OVERRIDE);5237		fd = creat(variant->file_path, 0600);5238		EXPECT_LE(0, fd)5239		{5240			TH_LOG("Failed to create file \"%s\": %s",5241			       variant->file_path, strerror(errno));5242		}5243		EXPECT_EQ(0, close(fd));5244		self->has_created_file = true;5245		clear_cap(_metadata, CAP_DAC_OVERRIDE);5246	}5247 5248	free(dir_path);5249}5250 5251FIXTURE_TEARDOWN_PARENT(layout3_fs)5252{5253	if (self->skip_test)5254		SKIP(return, "this filesystem is not supported (teardown)");5255 5256	if (self->has_created_file) {5257		set_cap(_metadata, CAP_DAC_OVERRIDE);5258		/*5259		 * Don't check for error because the file might already5260		 * have been removed (cf. release_inode test).5261		 */5262		unlink(variant->file_path);5263		clear_cap(_metadata, CAP_DAC_OVERRIDE);5264	}5265 5266	if (self->has_created_dir) {5267		char *dir_path = dirname_alloc(variant->file_path);5268 5269		set_cap(_metadata, CAP_DAC_OVERRIDE);5270		/*5271		 * Don't check for error because the directory might already5272		 * have been removed (cf. release_inode test).5273		 */5274		rmdir(dir_path);5275		clear_cap(_metadata, CAP_DAC_OVERRIDE);5276		free(dir_path);5277	}5278 5279	cleanup_layout(_metadata);5280}5281 5282static void layer3_fs_tag_inode(struct __test_metadata *const _metadata,5283				FIXTURE_DATA(layout3_fs) * self,5284				const FIXTURE_VARIANT(layout3_fs) * variant,5285				const char *const rule_path)5286{5287	const struct rule layer1_allow_read_file[] = {5288		{5289			.path = rule_path,5290			.access = LANDLOCK_ACCESS_FS_READ_FILE,5291		},5292		{},5293	};5294	const struct landlock_ruleset_attr layer2_deny_everything_attr = {5295		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,5296	};5297	const char *const dev_null_path = "/dev/null";5298	int ruleset_fd;5299 5300	if (self->skip_test)5301		SKIP(return, "this filesystem is not supported (test)");5302 5303	/* Checks without Landlock. */5304	EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));5305	EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));5306 5307	ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,5308				    layer1_allow_read_file);5309	EXPECT_LE(0, ruleset_fd);5310	enforce_ruleset(_metadata, ruleset_fd);5311	EXPECT_EQ(0, close(ruleset_fd));5312 5313	EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));5314	EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));5315 5316	/* Forbids directory reading. */5317	ruleset_fd =5318		landlock_create_ruleset(&layer2_deny_everything_attr,5319					sizeof(layer2_deny_everything_attr), 0);5320	EXPECT_LE(0, ruleset_fd);5321	enforce_ruleset(_metadata, ruleset_fd);5322	EXPECT_EQ(0, close(ruleset_fd));5323 5324	/* Checks with Landlock and forbidden access. */5325	EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));5326	EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));5327}5328 5329/* Matrix of tests to check file hierarchy evaluation. */5330 5331TEST_F_FORK(layout3_fs, tag_inode_dir_parent)5332{5333	/* The current directory must not be the root for this test. */5334	layer3_fs_tag_inode(_metadata, self, variant, ".");5335}5336 5337TEST_F_FORK(layout3_fs, tag_inode_dir_mnt)5338{5339	layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR);5340}5341 5342TEST_F_FORK(layout3_fs, tag_inode_dir_child)5343{5344	char *dir_path = dirname_alloc(variant->file_path);5345 5346	layer3_fs_tag_inode(_metadata, self, variant, dir_path);5347	free(dir_path);5348}5349 5350TEST_F_FORK(layout3_fs, tag_inode_file)5351{5352	layer3_fs_tag_inode(_metadata, self, variant, variant->file_path);5353}5354 5355/* Light version of layout1.release_inodes */5356TEST_F_FORK(layout3_fs, release_inodes)5357{5358	const struct rule layer1[] = {5359		{5360			.path = TMP_DIR,5361			.access = LANDLOCK_ACCESS_FS_READ_DIR,5362		},5363		{},5364	};5365	int ruleset_fd;5366 5367	if (self->skip_test)5368		SKIP(return, "this filesystem is not supported (test)");5369 5370	/* Clean up for the teardown to not fail. */5371	if (self->has_created_file)5372		EXPECT_EQ(0, remove_path(variant->file_path));5373 5374	if (self->has_created_dir) {5375		char *dir_path = dirname_alloc(variant->file_path);5376 5377		/* Don't check for error because of cgroup specificities. */5378		remove_path(dir_path);5379		free(dir_path);5380	}5381 5382	ruleset_fd =5383		create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1);5384	ASSERT_LE(0, ruleset_fd);5385 5386	/* Unmount the filesystem while it is being used by a ruleset. */5387	set_cap(_metadata, CAP_SYS_ADMIN);5388	ASSERT_EQ(0, umount(TMP_DIR));5389	clear_cap(_metadata, CAP_SYS_ADMIN);5390 5391	/* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */5392	set_cap(_metadata, CAP_SYS_ADMIN);5393	ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR));5394	clear_cap(_metadata, CAP_SYS_ADMIN);5395 5396	enforce_ruleset(_metadata, ruleset_fd);5397	ASSERT_EQ(0, close(ruleset_fd));5398 5399	/* Checks that access to the new mount point is denied. */5400	ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY));5401}5402 5403TEST_HARNESS_MAIN5404