From: Cai Xinchen <caixinchen1@huawei.com>
To: <mic@digikod.net>, <gnoack@google.com>, <paul@paul-moore.com>,
<jmorris@namei.org>, <serge@hallyn.com>, <corbet@lwn.net>,
<skhan@linuxfoundation.org>, <utilityemal77@gmail.com>,
<caixinchen1@huawei.com>
Cc: <ast@kernel.org>, <daniel@iogearbox.net>, <andrii@kernel.org>,
<eddyz87@gmail.com>, <memxor@gmail.com>, <martin.lau@linux.dev>,
<song@kernel.org>, <yonghong.song@linux.dev>, <jolsa@kernel.org>,
<emil@etsalapatis.com>, <ihor.solodrai@linux.dev>,
<rdunlap@infradead.org>, <gregkh@linuxfoundation.org>,
<rafael@kernel.org>, <dakr@kernel.org>, <johan@kernel.org>,
<dlemoal@kernel.org>, <hch@lst.de>, <axboe@kernel.dk>,
<viro@zeniv.linux.org.uk>, <brauner@kernel.org>, <jack@suse.cz>,
<dhowells@redhat.com>, <code@tyhicks.com>,
<linkinjeon@kernel.org>, <sj1557.seo@samsung.com>,
<yuezhang.mo@sony.com>, <hirofumi@mail.parknet.co.jp>,
<cel@kernel.org>, <jlayton@kernel.org>, <neil@brown.name>,
<okorniev@redhat.com>, <Dai.Ngo@oracle.com>, <tom@talpey.com>,
<hyc.lee@gmail.com>, <miklos@szeredi.hu>, <amir73il@gmail.com>,
<senozhatsky@chromium.org>, <chenxiaosong@chenxiaosong.com>,
<dsterba@suse.com>, <mason@kernel.org>, <kpsingh@kernel.org>,
<matt@bobrowski.net>, <rostedt@goodmis.org>,
<mhiramat@kernel.org>, <mathieu.desnoyers@efficios.com>,
<john.johansen@canonical.com>, <georgia.garcia@canonical.com>,
<zohar@linux.ibm.com>, <roberto.sassu@huawei.com>,
<dmitry.kasatkin@gmail.com>, <eric.snowberg@oracle.com>,
<stephen.smalley.work@gmail.com>, <omosnacek@gmail.com>,
<casey@schaufler-ca.com>, <takedakn@nttdata.co.jp>,
<penguin-kernel@I-love.SAKURA.ne.jp>, <xiujianfeng@huawei.com>,
<nanx95726@gmail.com>, <djwong@kernel.org>,
<lujialin4@huawei.com>, <bpf@vger.kernel.org>,
<linux-doc@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<linux-security-module@vger.kernel.org>,
<driver-core@lists.linux.dev>, <linux-block@vger.kernel.org>,
<linux-btrfs@vger.kernel.org>, <linux-fsdevel@vger.kernel.org>,
<netfs@lists.linux.dev>, <ecryptfs@vger.kernel.org>,
<exfat@lists.linux.dev>, <linux-nfs@vger.kernel.org>,
<ntfs@lists.linux.dev>, <linux-unionfs@vger.kernel.org>,
<linux-cifs@vger.kernel.org>,
<linux-trace-kernel@vger.kernel.org>, <apparmor@lists.ubuntu.com>,
<linux-integrity@vger.kernel.org>, <selinux@vger.kernel.org>,
<linux-kselftest@vger.kernel.org>
Subject: [PATCH v3 -next 16/18] selftests/landlock: Add tests for metadata access rights
Date: Tue, 27 Oct 2026 20:11:49 +0800 [thread overview]
Message-ID: <20261027121151.1887650-17-caixinchen1@huawei.com> (raw)
In-Reply-To: <20261027121151.1887650-1-caixinchen1@huawei.com>
Add layout1 tests for the LANDLOCK_ACCESS_FS_READ_METADATA and
LANDLOCK_ACCESS_FS_WRITE_METADATA access rights:
- unhandled_metadata: metadata access is allowed when the new rights
are not handled by the ruleset;
- write_metadata: chmod(2), fchmod(2), chown(2), fchown(2), lchown(2),
utimensat(2) (both with explicit times and with UTIME_NOW),
setxattr(2), and removexattr(2) are denied with EACCES without
WRITE_METADATA, and allowed with it. Writing to or truncating a
set-user-ID file clears the set-user-ID bit implicitly, which
stays allowed without WRITE_METADATA, while an explicit chown(2)
with (-1, -1) requesting the same clearing is denied without
WRITE_METADATA and allowed with it;
- read_metadata: stat(2), fstat(2), getxattr(2), and listxattr(2) are
denied with EACCES without READ_METADATA, and allowed with it.
Add layout1 and layout2_overlay tests for the fileattr interfaces
controlled by the same rights:
- fileattr: ioctl(FS_IOC_GETFLAGS), ioctl(FS_IOC_SETFLAGS),
file_getattr(2) and file_setattr(2) (both with a path and with
AT_EMPTY_PATH) are denied with EACCES without the corresponding
right, and allowed with it;
- fileattr_write_only: with only WRITE_METADATA handled, metadata
writes (ioctl(FS_IOC_SETFLAGS)) and reads (ioctl(FS_IOC_GETFLAGS))
are both allowed, as READ_METADATA is not handled; when
READ_METADATA is handled as well but not granted, metadata reads
through a file descriptor opened from then on are denied with
EACCES while writes are still allowed, because the kernel-side
read of the old flags performed when setting new ones does not go
through the READ_METADATA check. Reading metadata through a file
descriptor opened while READ_METADATA was not handled yet is still
allowed;
- layout2_overlay.fileattr: the same fileattr checks on files
accessed through an overlayfs mount created by the (unrestricted)
test process;
- overlay_mount_denied: a restricted process cannot bypass the
metadata rights by mounting a new overlayfs with mount(2), even
with CAP_SYS_ADMIN: landlock denies mount(2) with EPERM for any
landlocked process.
The file-based metadata checks follow the
LANDLOCK_ACCESS_FS_TRUNCATE mechanic: it is the access rights at the
time of open(2) which decide whether an operation on an already
opened file is permitted. Adapt the fchown(2) denial case of the
write_metadata test and the second phase of the fileattr_write_only
test to that semantic by (re)opening the file descriptors after the
ruleset is enforced, and add layout1 tests for the open-time
semantic itself:
- metadata_open_time: a file descriptor opened before the ruleset is
enforced keeps allowing fchmod(2), fchown(2), futimens(2),
fstat(2), fgetxattr(2), fsetxattr(2), ioctl(FS_IOC_GETFLAGS) and
file_getattr(2)/file_setattr(2) with AT_EMPTY_PATH, whereas the
same operations through a file descriptor opened while the
ruleset is enforced are denied with EACCES, and the same
operations through paths are denied as well. Data accesses
(write(2)) still work through the restricted file descriptor, and
an O_PATH file descriptor is checked against the current domain,
like path-based accesses;
- metadata_transfer: a file descriptor opened by a landlocked
process without the metadata access rights and passed to a
non-landlocked process through a unix socket still denies metadata
accesses, while data accesses work: the file descriptor carries
the rights of the process which opened it, whatever the rights of
the process using it.
The fallback syscall numbers for file_getattr(2) and file_setattr(2)
used when building against older headers are not universal: alpha
assigns 578 and 579 to them instead of the generic 468 and 469.
Rule enforcement is checked per file and per directory.
Update the ACCESS_FILE and ACCESS_LAST definitions to include the new
rights.
The new rights are appended to _LANDLOCK_ACCESS_FS_NAMES, which
extends the absolute access mask expanded by
landlock_append_fs_rule(); update the expected access_rights suffix in
the trace_fs.add_rule_path_beneath_escaped_path_overflow test
accordingly.
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
tools/testing/selftests/landlock/base_test.c | 2 +-
tools/testing/selftests/landlock/fs_test.c | 921 +++++++++++++++++-
.../selftests/landlock/trace_fs_test.c | 3 +-
3 files changed, 923 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index 89c0d96607de..14078ad203d8 100644
--- a/tools/testing/selftests/landlock/base_test.c
+++ b/tools/testing/selftests/landlock/base_test.c
@@ -76,7 +76,7 @@ TEST(abi_version)
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
};
- ASSERT_EQ(12, landlock_create_ruleset(NULL, 0,
+ ASSERT_EQ(13, landlock_create_ruleset(NULL, 0,
LANDLOCK_CREATE_RULESET_VERSION));
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index f725882fa4a7..1e819ba952ac 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -12,6 +12,7 @@
#include <fcntl.h>
#include <libgen.h>
#include <linux/fiemap.h>
+#include <linux/fs.h>
#include <linux/landlock.h>
#include <linux/magic.h>
#include <sched.h>
@@ -26,8 +27,10 @@
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <sys/un.h>
+#include <sys/xattr.h>
#include <sys/vfs.h>
#include <unistd.h>
@@ -571,11 +574,13 @@ TEST_F_FORK(layout1, inval)
LANDLOCK_ACCESS_FS_EXECUTE | \
LANDLOCK_ACCESS_FS_WRITE_FILE | \
LANDLOCK_ACCESS_FS_READ_FILE | \
+ LANDLOCK_ACCESS_FS_READ_METADATA | \
+ LANDLOCK_ACCESS_FS_WRITE_METADATA | \
LANDLOCK_ACCESS_FS_TRUNCATE | \
LANDLOCK_ACCESS_FS_IOCTL_DEV | \
LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
-#define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX
+#define ACCESS_LAST LANDLOCK_ACCESS_FS_WRITE_METADATA
#define ACCESS_ALL ( \
ACCESS_FILE | \
@@ -10916,4 +10921,918 @@ TEST_F(trace_layout1, check_rule_fs_empty_grant)
free(buf);
}
+/* Invokes chmod(2) and returns its errno or 0. */
+static int test_chmod(const char *const path, mode_t mode)
+{
+ if (chmod(path, mode) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes fchmod(2) and returns its errno or 0. */
+static int test_fchmod(int fd, mode_t mode)
+{
+ if (fchmod(fd, mode) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes chown(2) and returns its errno or 0. */
+static int test_chown(const char *const path, uid_t uid, gid_t gid)
+{
+ if (chown(path, uid, gid) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes fchown(2) and returns its errno or 0. */
+static int test_fchown(int fd, uid_t uid, gid_t gid)
+{
+ if (fchown(fd, uid, gid) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes lchown(2) and returns its errno or 0. */
+static int test_lchown(const char *const path, uid_t uid, gid_t gid)
+{
+ if (lchown(path, uid, gid) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes utimensat(2) with explicit times and returns its errno or 0. */
+static int test_utimensat(const char *const path)
+{
+ const struct timespec times[] = {
+ {
+ .tv_sec = 0,
+ .tv_nsec = UTIME_OMIT,
+ },
+ {
+ .tv_sec = 1,
+ .tv_nsec = 0,
+ },
+ };
+
+ if (utimensat(AT_FDCWD, path, times, 0) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes utimensat(2) with UTIME_NOW and returns its errno or 0. */
+static int test_utimensat_now(const char *const path)
+{
+ if (utimensat(AT_FDCWD, path, NULL, 0) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes futimens(2) and returns its errno or 0. */
+static int test_futimensat(int fd)
+{
+ const struct timespec times[] = {
+ {
+ .tv_sec = 0,
+ .tv_nsec = UTIME_OMIT,
+ },
+ {
+ .tv_sec = 1,
+ .tv_nsec = 0,
+ },
+ };
+
+ if (futimens(fd, times) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes setxattr(2) and returns its errno or 0. */
+static int test_setxattr(const char *const path, const char *const name,
+ const void *const value, const size_t size)
+{
+ if (setxattr(path, name, value, size, 0) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes getxattr(2) and returns its errno or 0. */
+static int test_getxattr(const char *const path, const char *const name,
+ void *const value, const size_t size)
+{
+ if (getxattr(path, name, value, size) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes listxattr(2) and returns its errno or 0. */
+static int test_listxattr(const char *const path, void *const list,
+ const size_t size)
+{
+ if (listxattr(path, list, size) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes removexattr(2) and returns its errno or 0. */
+static int test_removexattr(const char *const path, const char *const name)
+{
+ if (removexattr(path, name) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes fsetxattr(2) and returns its errno or 0. */
+static int test_fsetxattr(int fd, const char *const name,
+ const void *const value, const size_t size)
+{
+ if (fsetxattr(fd, name, value, size, 0) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes fgetxattr(2) and returns its errno or 0. */
+static int test_fgetxattr(int fd, const char *const name, void *const value,
+ const size_t size)
+{
+ if (fgetxattr(fd, name, value, size) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes stat(2) and returns its errno or 0. */
+static int test_stat(const char *const path, struct stat *statbuf)
+{
+ if (stat(path, statbuf) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes fstat(2) and returns its errno or 0. */
+static int test_fstat(int fd, struct stat *statbuf)
+{
+ if (fstat(fd, statbuf) < 0)
+ return errno;
+ return 0;
+}
+
+TEST_F_FORK(layout1, unhandled_metadata)
+{
+ int file_fd;
+ int ruleset_fd;
+ const char *const file1 = file1_s1d1;
+ const char *const file2 = file2_s1d1;
+ const char *const dir1 = dir_s1d1;
+ struct stat statbuf;
+ const struct rule rules[] = {
+ {
+ .path = file1,
+ .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
+ },
+ {
+ .path = file2,
+ .access = LANDLOCK_ACCESS_FS_READ_FILE |
+ LANDLOCK_ACCESS_FS_WRITE_FILE,
+ },
+ {
+ .path = dir1,
+ .access = ACCESS_RW,
+ },
+ {},
+ };
+
+ /*
+ * READ_METADATA and WRITE_METADATA are not handled, so metadata
+ * access should be allowed.
+ */
+ ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
+ ASSERT_LE(0, ruleset_fd);
+ file_fd = open(file1, O_WRONLY | O_CLOEXEC);
+ ASSERT_LE(0, file_fd);
+
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ EXPECT_EQ(0, test_chmod(file1, 0644));
+ EXPECT_EQ(0, test_fchmod(file_fd, 0644));
+ EXPECT_EQ(0, test_chmod(file2, 0400));
+ EXPECT_EQ(0, test_chmod(dir1, 0700));
+
+ EXPECT_EQ(0, test_chown(file1, -1, 0));
+ EXPECT_EQ(0, test_fchown(file_fd, -1, 0));
+ EXPECT_EQ(0, test_lchown(file1, -1, 0));
+
+ EXPECT_EQ(0, test_utimensat(file1));
+ EXPECT_EQ(0, test_utimensat_now(file1));
+
+ EXPECT_EQ(0, test_setxattr(file1, "user.test", "a", 1));
+ EXPECT_EQ(0, test_getxattr(file1, "user.test", NULL, 0));
+ EXPECT_EQ(0, test_listxattr(file1, NULL, 0));
+ EXPECT_EQ(0, test_removexattr(file1, "user.test"));
+
+ EXPECT_EQ(0, test_stat(file1, &statbuf));
+ EXPECT_EQ(0, test_fstat(file_fd, &statbuf));
+ EXPECT_EQ(0, test_stat(dir1, &statbuf));
+
+ ASSERT_EQ(0, close(file_fd));
+}
+
+TEST_F_FORK(layout1, write_metadata)
+{
+ int file_fd, file2_fd, trunc_fd, chown_fd;
+ int ruleset_fd;
+ const char *const file1 = file1_s1d1;
+ const char *const file2 = file2_s1d1;
+ const char *const trunc_file = file1_s1d2;
+ const char *const chown_file = file2_s1d2;
+ const char *const dir1 = dir_s1d1;
+ struct stat statbuf;
+ const struct rule rules[] = {
+ {
+ .path = file1,
+ .access = LANDLOCK_ACCESS_FS_WRITE_FILE |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ },
+ {
+ .path = file2,
+ .access = LANDLOCK_ACCESS_FS_READ_FILE |
+ LANDLOCK_ACCESS_FS_WRITE_FILE,
+ },
+ {
+ .path = chown_file,
+ .access = LANDLOCK_ACCESS_FS_READ_FILE |
+ LANDLOCK_ACCESS_FS_WRITE_FILE,
+ },
+ {
+ .path = dir1,
+ .access = ACCESS_RW,
+ },
+ {},
+ };
+
+ ruleset_fd = create_ruleset(_metadata, ACCESS_RW |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA, rules);
+ ASSERT_LE(0, ruleset_fd);
+ file_fd = open(file1, O_WRONLY | O_CLOEXEC);
+ ASSERT_LE(0, file_fd);
+ file2_fd = open(file2, O_RDWR | O_CLOEXEC);
+ ASSERT_LE(0, file2_fd);
+ trunc_fd = open(trunc_file, O_RDWR | O_CLOEXEC);
+ ASSERT_LE(0, trunc_fd);
+ chown_fd = open(chown_file, O_RDWR | O_CLOEXEC);
+ ASSERT_LE(0, chown_fd);
+
+ /*
+ * Arm the setuid bit for the setuid-clearing tests below:
+ * writing or truncating a setuid file clears that bit
+ * implicitly, which must not require WRITE_METADATA, whereas
+ * an explicit chown(2) with (-1, -1) requests the same
+ * clearing and must require it. The clearing happens when
+ * the caller has no CAP_FSETID, which is the case here
+ * because tests run without capabilities (see disable_caps()
+ * in prepare_layout()).
+ */
+ ASSERT_EQ(0, fchmod(file_fd, 04666));
+ ASSERT_EQ(0, fchmod(file2_fd, 04666));
+ ASSERT_EQ(0, fchmod(trunc_fd, 04666));
+ ASSERT_EQ(0, fchmod(chown_fd, 04666));
+ /* Closes the file descriptor opened before sandboxing. */
+ ASSERT_EQ(0, close(chown_fd));
+
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ /*
+ * Reopens the file while the ruleset is enforced: the fchown(2)
+ * below is denied because the WRITE_METADATA right is missing from
+ * the rights stashed at open time.
+ */
+ chown_fd = open(chown_file, O_RDWR | O_CLOEXEC);
+ ASSERT_LE(0, chown_fd);
+
+ /*
+ * An explicit chown(2) with (-1, -1) on a setuid file is an
+ * explicit request to clear the setuid bit: it requires
+ * WRITE_METADATA, which file1 has, and clears the bit.
+ */
+ EXPECT_EQ(0, test_fchown(file_fd, -1, -1));
+ ASSERT_EQ(0, fstat(file_fd, &statbuf));
+ EXPECT_EQ(0, statbuf.st_mode & S_ISUID);
+
+ /* file1 has WRITE_METADATA: allowed */
+ EXPECT_EQ(0, test_chmod(file1, 0644));
+ EXPECT_EQ(0, test_fchmod(file_fd, 0644));
+ EXPECT_EQ(0, test_chown(file1, -1, 0));
+ EXPECT_EQ(0, test_fchown(file_fd, -1, 0));
+ EXPECT_EQ(0, test_lchown(file1, -1, 0));
+ EXPECT_EQ(0, test_utimensat(file1));
+ EXPECT_EQ(0, test_utimensat_now(file1));
+ EXPECT_EQ(0, test_setxattr(file1, "user.test", "a", 1));
+ EXPECT_EQ(0, test_removexattr(file1, "user.test"));
+
+ /*
+ * Writing is not an explicit metadata change: implicit timestamp
+ * updates and the implicit setuid-bit clearing are allowed
+ * without WRITE_METADATA.
+ */
+ EXPECT_EQ(1, write(file2_fd, "a", 1));
+ ASSERT_EQ(0, fstat(file2_fd, &statbuf));
+ EXPECT_EQ(0, statbuf.st_mode & S_ISUID);
+
+ /* file2 does not have WRITE_METADATA: denied */
+ EXPECT_EQ(EACCES, test_chmod(file2, 0400));
+ EXPECT_EQ(EACCES, test_chown(file2, -1, 0));
+ EXPECT_EQ(EACCES, test_lchown(file2, -1, 0));
+ EXPECT_EQ(EACCES, test_utimensat(file2));
+ EXPECT_EQ(EACCES, test_utimensat_now(file2));
+ EXPECT_EQ(EACCES, test_setxattr(file2, "user.test", "a", 1));
+ EXPECT_EQ(EACCES, test_removexattr(file2, "user.test"));
+
+ /*
+ * Without the WRITE_METADATA right (i.e. without the right stashed
+ * at open time), the same explicit chown(2) with (-1, -1) is
+ * denied and leaves the setuid bit in place.
+ */
+ EXPECT_EQ(EACCES, test_fchown(chown_fd, -1, -1));
+ ASSERT_EQ(0, fstat(chown_fd, &statbuf));
+ EXPECT_EQ(S_ISUID, statbuf.st_mode & S_ISUID);
+
+ /* dir1 does not have WRITE_METADATA: denied */
+ EXPECT_EQ(EACCES, test_chmod(dir1, 0700));
+ EXPECT_EQ(EACCES, test_chown(dir1, -1, 0));
+
+ /*
+ * Truncating a setuid file implicitly clears the setuid bit as
+ * well: this must not require WRITE_METADATA, which trunc_file
+ * does not have.
+ */
+ EXPECT_EQ(0, ftruncate(trunc_fd, 0));
+ ASSERT_EQ(0, fstat(trunc_fd, &statbuf));
+ EXPECT_EQ(0, statbuf.st_mode & S_ISUID);
+
+ ASSERT_EQ(0, close(file_fd));
+ ASSERT_EQ(0, close(file2_fd));
+ ASSERT_EQ(0, close(trunc_fd));
+ ASSERT_EQ(0, close(chown_fd));
+}
+
+TEST_F_FORK(layout1, read_metadata)
+{
+ int file_fd, file2_fd, sandbox_fd;
+ int ruleset_fd;
+ const char *const file1 = file1_s1d1;
+ const char *const file2 = file2_s1d1;
+ const char *const dir1 = dir_s1d1;
+ struct stat statbuf;
+ const struct rule rules[] = {
+ {
+ .path = file1,
+ .access = LANDLOCK_ACCESS_FS_READ_FILE |
+ LANDLOCK_ACCESS_FS_READ_METADATA,
+ },
+ {
+ .path = file2,
+ .access = LANDLOCK_ACCESS_FS_READ_FILE |
+ LANDLOCK_ACCESS_FS_WRITE_FILE,
+ },
+ {
+ .path = dir1,
+ .access = ACCESS_RW,
+ },
+ {},
+ };
+
+ ruleset_fd = create_ruleset(_metadata, ACCESS_RW |
+ LANDLOCK_ACCESS_FS_READ_METADATA, rules);
+ ASSERT_LE(0, ruleset_fd);
+
+ ASSERT_EQ(0, setxattr(file1, "user.test", "a", 1, 0));
+ ASSERT_EQ(0, setxattr(file2, "user.test", "a", 1, 0));
+
+ /*
+ * A file descriptor opened before the ruleset is enforced carries
+ * all access rights.
+ */
+ sandbox_fd = open(file1, O_RDONLY | O_CLOEXEC);
+ ASSERT_LE(0, sandbox_fd);
+
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ /* file1 has READ_METADATA: allowed */
+ EXPECT_EQ(0, test_stat(file1, &statbuf));
+ file_fd = open(file1, O_RDONLY | O_CLOEXEC);
+ ASSERT_LE(0, file_fd);
+ EXPECT_EQ(0, test_fstat(file_fd, &statbuf));
+ EXPECT_EQ(0, test_getxattr(file1, "user.test", NULL, 0));
+ EXPECT_EQ(0, test_listxattr(file1, NULL, 0));
+
+ /*
+ * Reading metadata through the file descriptor opened before
+ * sandboxing is still allowed.
+ */
+ EXPECT_EQ(0, test_fstat(sandbox_fd, &statbuf));
+ ASSERT_EQ(0, close(sandbox_fd));
+
+ /*
+ * file2 does not have READ_METADATA: denied, both through the path
+ * and through a file descriptor opened while the ruleset is
+ * enforced.
+ */
+ EXPECT_EQ(EACCES, test_stat(file2, &statbuf));
+ file2_fd = open(file2, O_RDONLY | O_CLOEXEC);
+ ASSERT_LE(0, file2_fd);
+ EXPECT_EQ(EACCES, test_fstat(file2_fd, &statbuf));
+ EXPECT_EQ(EACCES, test_fgetxattr(file2_fd, "user.test", NULL, 0));
+ ASSERT_EQ(0, close(file2_fd));
+ EXPECT_EQ(EACCES, test_getxattr(file2, "user.test", NULL, 0));
+ EXPECT_EQ(EACCES, test_listxattr(file2, NULL, 0));
+ /* dir1 does not have READ_METADATA: denied */
+ EXPECT_EQ(EACCES, test_stat(dir1, &statbuf));
+
+ ASSERT_EQ(0, close(file_fd));
+}
+
+/* Invokes ioctl(FS_IOC_GETFLAGS) and returns its errno or 0. */
+static int test_getflags(int fd, unsigned int *const flags)
+{
+ if (ioctl(fd, FS_IOC_GETFLAGS, flags) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes ioctl(FS_IOC_SETFLAGS) and returns its errno or 0. */
+static int test_setflags(int fd, const unsigned int flags)
+{
+ unsigned int flags_ = flags;
+
+ if (ioctl(fd, FS_IOC_SETFLAGS, &flags_) < 0)
+ return errno;
+ return 0;
+}
+
+#ifndef __NR_file_getattr
+#if defined(__alpha__)
+#define __NR_file_getattr 578
+#else
+#define __NR_file_getattr 468
+#endif
+#endif
+#ifndef __NR_file_setattr
+#if defined(__alpha__)
+#define __NR_file_setattr 579
+#else
+#define __NR_file_setattr 469
+#endif
+#endif
+
+/* Invokes file_getattr(2) and returns its errno or 0. */
+static int test_file_getattr(int dfd, const char *const path,
+ struct file_attr *const fattr,
+ const unsigned int at_flags)
+{
+ if (syscall(__NR_file_getattr, dfd, path, fattr, sizeof(*fattr),
+ at_flags) < 0)
+ return errno;
+ return 0;
+}
+
+/* Invokes file_setattr(2) and returns its errno or 0. */
+static int test_file_setattr(int dfd, const char *const path,
+ struct file_attr *const fattr,
+ const unsigned int at_flags)
+{
+ if (syscall(__NR_file_setattr, dfd, path, fattr, sizeof(*fattr),
+ at_flags) < 0)
+ return errno;
+ return 0;
+}
+
+TEST_F_FORK(layout1, fileattr)
+{
+ const struct rule rules[] = {
+ {
+ .path = file1_s1d1,
+ .access = LANDLOCK_ACCESS_FS_READ_METADATA |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ },
+ {},
+ };
+ struct file_attr fattr;
+ unsigned int flags;
+ int fd, fd2, ruleset_fd;
+
+ ruleset_fd = create_ruleset(
+ _metadata,
+ LANDLOCK_ACCESS_FS_READ_METADATA |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ rules);
+ ASSERT_LE(0, ruleset_fd);
+
+ fd = open(file1_s1d1, O_RDONLY | O_CLOEXEC);
+ ASSERT_LE(0, fd);
+
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ /* Allowed file: FS_IOC_GETFLAGS */
+ flags = 0;
+ EXPECT_EQ(0, test_getflags(fd, &flags));
+ EXPECT_EQ(0, flags & FS_NODUMP_FL);
+
+ /* Allowed file: FS_IOC_SETFLAGS */
+ EXPECT_EQ(0, test_setflags(fd, FS_NODUMP_FL));
+ flags = 0;
+ EXPECT_EQ(0, test_getflags(fd, &flags));
+ EXPECT_EQ(FS_NODUMP_FL, flags & FS_NODUMP_FL);
+ EXPECT_EQ(0, test_setflags(fd, 0));
+
+ /* Allowed file: file_getattr(2), with a path and with AT_EMPTY_PATH */
+ memset(&fattr, 0, sizeof(fattr));
+ EXPECT_EQ(0, test_file_getattr(AT_FDCWD, file1_s1d1, &fattr, 0));
+ EXPECT_EQ(0, fattr.fa_xflags & FS_XFLAG_NODUMP);
+ memset(&fattr, 0, sizeof(fattr));
+ EXPECT_EQ(0, test_file_getattr(fd, NULL, &fattr, AT_EMPTY_PATH));
+
+ /* Allowed file: file_setattr(2), with a path and with AT_EMPTY_PATH */
+ memset(&fattr, 0, sizeof(fattr));
+ fattr.fa_xflags = FS_XFLAG_NODUMP;
+ EXPECT_EQ(0, test_file_setattr(AT_FDCWD, file1_s1d1, &fattr, 0));
+ memset(&fattr, 0, sizeof(fattr));
+ EXPECT_EQ(0, test_file_getattr(AT_FDCWD, file1_s1d1, &fattr, 0));
+ EXPECT_EQ(FS_XFLAG_NODUMP, fattr.fa_xflags & FS_XFLAG_NODUMP);
+ memset(&fattr, 0, sizeof(fattr));
+ fattr.fa_xflags = 0;
+ EXPECT_EQ(0, test_file_setattr(fd, NULL, &fattr, AT_EMPTY_PATH));
+
+ /* Non-allowed file: FS_IOC_GETFLAGS and FS_IOC_SETFLAGS */
+ fd2 = open(file2_s1d1, O_RDONLY | O_CLOEXEC);
+ ASSERT_LE(0, fd2);
+ EXPECT_EQ(EACCES, test_getflags(fd2, &flags));
+ EXPECT_EQ(EACCES, test_setflags(fd2, FS_NODUMP_FL));
+
+ /* Non-allowed file: file_getattr(2) and file_setattr(2) */
+ memset(&fattr, 0, sizeof(fattr));
+ EXPECT_EQ(EACCES, test_file_getattr(AT_FDCWD, file2_s1d1, &fattr, 0));
+ fattr.fa_xflags = FS_XFLAG_NODUMP;
+ EXPECT_EQ(EACCES, test_file_setattr(AT_FDCWD, file2_s1d1, &fattr, 0));
+
+ ASSERT_EQ(0, close(fd));
+ ASSERT_EQ(0, close(fd2));
+}
+
+TEST_F_FORK(layout1, fileattr_write_only)
+{
+ const struct rule rules[] = {
+ {
+ .path = file1_s1d1,
+ .access = LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ },
+ {},
+ };
+ struct file_attr fattr;
+ unsigned int flags;
+ int fd, fd2, ruleset_fd;
+
+ ruleset_fd = create_ruleset(_metadata,
+ LANDLOCK_ACCESS_FS_WRITE_METADATA, rules);
+ ASSERT_LE(0, ruleset_fd);
+
+ fd = open(file1_s1d1, O_RDONLY | O_CLOEXEC);
+ ASSERT_LE(0, fd);
+
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ /*
+ * WRITE_METADATA alone must be enough to set flags: the kernel-side
+ * read of the old flags must not require READ_METADATA.
+ */
+ EXPECT_EQ(0, test_setflags(fd, FS_NODUMP_FL));
+ flags = 0;
+ /* READ_METADATA is not handled: reading is allowed. */
+ EXPECT_EQ(0, test_getflags(fd, &flags));
+ EXPECT_EQ(FS_NODUMP_FL, flags & FS_NODUMP_FL);
+
+ /*
+ * Handle READ_METADATA as well, without granting it. Reading is
+ * now denied for file descriptors opened from now on, but writing
+ * must still work because the kernel-side read of the old flags
+ * does not go through the READ_METADATA check.
+ */
+ ruleset_fd = create_ruleset(
+ _metadata,
+ LANDLOCK_ACCESS_FS_READ_METADATA |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ rules);
+ ASSERT_LE(0, ruleset_fd);
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ fd2 = open(file1_s1d1, O_RDONLY | O_CLOEXEC);
+ ASSERT_LE(0, fd2);
+ EXPECT_EQ(EACCES, test_getflags(fd2, &flags));
+ memset(&fattr, 0, sizeof(fattr));
+ EXPECT_EQ(EACCES,
+ test_file_getattr(fd2, NULL, &fattr, AT_EMPTY_PATH));
+
+ EXPECT_EQ(0, test_setflags(fd2, 0));
+ memset(&fattr, 0, sizeof(fattr));
+ fattr.fa_xflags = FS_XFLAG_NODUMP;
+ EXPECT_EQ(0, test_file_setattr(fd2, NULL, &fattr, AT_EMPTY_PATH));
+ memset(&fattr, 0, sizeof(fattr));
+ fattr.fa_xflags = 0;
+ EXPECT_EQ(0, test_file_setattr(fd2, NULL, &fattr, AT_EMPTY_PATH));
+
+ /*
+ * Reading through the file descriptor opened before READ_METADATA
+ * was handled is still allowed: it carries the rights of the time
+ * it was opened with.
+ */
+ EXPECT_EQ(0, test_getflags(fd, &flags));
+
+ ASSERT_EQ(0, close(fd));
+ ASSERT_EQ(0, close(fd2));
+}
+
+TEST_F_FORK(layout1, metadata_open_time)
+{
+ int file_fd, opath_fd, ruleset_fd;
+ struct stat statbuf;
+ unsigned int flags;
+ struct file_attr fattr;
+ const char *const file1 = file1_s1d1;
+ const struct rule rules[] = {
+ {
+ .path = file1,
+ .access = LANDLOCK_ACCESS_FS_READ_FILE |
+ LANDLOCK_ACCESS_FS_WRITE_FILE,
+ },
+ {},
+ };
+
+ ruleset_fd = create_ruleset(_metadata, ACCESS_RW |
+ LANDLOCK_ACCESS_FS_READ_METADATA |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ rules);
+ ASSERT_LE(0, ruleset_fd);
+
+ /*
+ * A file descriptor opened before the ruleset is enforced carries
+ * all access rights: metadata accesses through it remain allowed
+ * even though the current thread cannot access the file's metadata
+ * anymore.
+ */
+ file_fd = open(file1, O_RDWR | O_CLOEXEC);
+ ASSERT_LE(0, file_fd);
+
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ EXPECT_EQ(0, test_fchmod(file_fd, 0600));
+ EXPECT_EQ(0, test_fchown(file_fd, -1, 0));
+ EXPECT_EQ(0, test_futimensat(file_fd));
+ EXPECT_EQ(0, test_fstat(file_fd, &statbuf));
+ EXPECT_EQ(0, test_getflags(file_fd, &flags));
+ EXPECT_EQ(0, test_fsetxattr(file_fd, "user.test", "a", 1));
+ EXPECT_EQ(0, test_fgetxattr(file_fd, "user.test", NULL, 0));
+ memset(&fattr, 0, sizeof(fattr));
+ EXPECT_EQ(0,
+ test_file_getattr(file_fd, NULL, &fattr, AT_EMPTY_PATH));
+ memset(&fattr, 0, sizeof(fattr));
+ fattr.fa_xflags = FS_XFLAG_NODUMP;
+ EXPECT_EQ(0,
+ test_file_setattr(file_fd, NULL, &fattr, AT_EMPTY_PATH));
+ memset(&fattr, 0, sizeof(fattr));
+ fattr.fa_xflags = 0;
+ EXPECT_EQ(0,
+ test_file_setattr(file_fd, NULL, &fattr, AT_EMPTY_PATH));
+
+ /* Path-based accesses use the rights of the current thread. */
+ EXPECT_EQ(EACCES, test_chmod(file1, 0644));
+ EXPECT_EQ(EACCES, test_chown(file1, -1, 0));
+ EXPECT_EQ(EACCES, test_utimensat(file1));
+ EXPECT_EQ(EACCES, test_stat(file1, &statbuf));
+ EXPECT_EQ(EACCES, test_setxattr(file1, "user.test", "a", 1));
+ EXPECT_EQ(EACCES, test_getxattr(file1, "user.test", NULL, 0));
+
+ ASSERT_EQ(0, close(file_fd));
+
+ /*
+ * A file descriptor opened while the ruleset is enforced only
+ * carries the rights granted to the file: metadata accesses
+ * through it are denied, but data accesses still work.
+ */
+ file_fd = open(file1, O_RDWR | O_CLOEXEC);
+ ASSERT_LE(0, file_fd);
+
+ EXPECT_EQ(EACCES, test_fchmod(file_fd, 0600));
+ EXPECT_EQ(EACCES, test_fchown(file_fd, -1, 0));
+ EXPECT_EQ(EACCES, test_futimensat(file_fd));
+ EXPECT_EQ(EACCES, test_fstat(file_fd, &statbuf));
+ EXPECT_EQ(EACCES, test_getflags(file_fd, &flags));
+ EXPECT_EQ(EACCES, test_fsetxattr(file_fd, "user.test", "a", 1));
+ EXPECT_EQ(EACCES, test_fgetxattr(file_fd, "user.test", NULL, 0));
+ memset(&fattr, 0, sizeof(fattr));
+ EXPECT_EQ(EACCES,
+ test_file_getattr(file_fd, NULL, &fattr, AT_EMPTY_PATH));
+ memset(&fattr, 0, sizeof(fattr));
+ fattr.fa_xflags = FS_XFLAG_NODUMP;
+ EXPECT_EQ(EACCES,
+ test_file_setattr(file_fd, NULL, &fattr, AT_EMPTY_PATH));
+
+ EXPECT_EQ(0, lseek(file_fd, 0, SEEK_SET));
+ EXPECT_EQ(1, write(file_fd, "a", 1));
+
+ /*
+ * An O_PATH file descriptor does not carry open-time rights: it is
+ * checked against the current domain, like path-based accesses.
+ */
+ opath_fd = open(file1, O_PATH | O_CLOEXEC);
+ ASSERT_LE(0, opath_fd);
+ EXPECT_EQ(EACCES, test_fstat(opath_fd, &statbuf));
+
+ ASSERT_EQ(0, close(file_fd));
+ ASSERT_EQ(0, close(opath_fd));
+}
+
+TEST_F_FORK(layout1, metadata_transfer)
+{
+ int child, fd, status;
+ int socket_fds[2];
+ struct stat statbuf;
+ unsigned int flags;
+ const char *const file1 = file1_s1d1;
+ const struct rule rules[] = {
+ {
+ .path = file1,
+ .access = LANDLOCK_ACCESS_FS_READ_FILE |
+ LANDLOCK_ACCESS_FS_WRITE_FILE,
+ },
+ {},
+ };
+
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
+ socket_fds));
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (child == 0) {
+ /*
+ * Enables Landlock in the child process, opens a file
+ * descriptor without the metadata access rights and sends it
+ * to the non-landlocked parent process.
+ */
+ int fd;
+
+ enforce_fs(_metadata, ACCESS_RW |
+ LANDLOCK_ACCESS_FS_READ_METADATA |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ rules);
+
+ fd = open(file1, O_RDWR | O_CLOEXEC);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(0, send_fd(socket_fds[0], fd));
+ ASSERT_EQ(0, close(fd));
+ ASSERT_EQ(0, close(socket_fds[0]));
+
+ _exit(_metadata->exit_code);
+ return;
+ }
+
+ /*
+ * The non-landlocked parent process can use the received file
+ * descriptor for data accesses, but its metadata accesses are
+ * still denied with the rights stashed at open time.
+ */
+ fd = recv_fd(socket_fds[1]);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(1, write(fd, "a", 1));
+ EXPECT_EQ(EACCES, test_fchmod(fd, 0600));
+ EXPECT_EQ(EACCES, test_fchown(fd, -1, 0));
+ EXPECT_EQ(EACCES, test_futimensat(fd));
+ EXPECT_EQ(EACCES, test_fstat(fd, &statbuf));
+ EXPECT_EQ(EACCES, test_getflags(fd, &flags));
+ ASSERT_EQ(0, close(fd));
+
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ ASSERT_EQ(1, WIFEXITED(status));
+ ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+ ASSERT_EQ(0, close(socket_fds[0]));
+ ASSERT_EQ(0, close(socket_fds[1]));
+}
+
+TEST_F_FORK(layout2_overlay, fileattr)
+{
+ const struct rule rules[] = {
+ {
+ .path = MERGE_DATA,
+ .access = LANDLOCK_ACCESS_FS_READ_METADATA |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ },
+ {},
+ };
+ unsigned int flags;
+ int fd, fd2, ruleset_fd;
+
+ if (self->skip_test)
+ SKIP(return, "overlayfs is not supported (test)");
+
+ ruleset_fd = create_ruleset(
+ _metadata,
+ LANDLOCK_ACCESS_FS_READ_METADATA |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ rules);
+ ASSERT_LE(0, ruleset_fd);
+
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ /*
+ * The overlay mount was created before the ruleset was enforced
+ * (i.e. as an administrator): the internal forwards to the real
+ * paths must not interfere with the allowed accesses below.
+ */
+ fd = open(merge_fl1, O_RDONLY | O_CLOEXEC);
+ ASSERT_LE(0, fd);
+
+ flags = 0;
+ EXPECT_EQ(0, test_getflags(fd, &flags));
+ EXPECT_EQ(0, flags & FS_NODUMP_FL);
+ /* Copies the file up and sets the flag on the upper real file. */
+ EXPECT_EQ(0, test_setflags(fd, FS_NODUMP_FL));
+ flags = 0;
+ EXPECT_EQ(0, test_getflags(fd, &flags));
+ EXPECT_EQ(FS_NODUMP_FL, flags & FS_NODUMP_FL);
+ EXPECT_EQ(0, test_setflags(fd, 0));
+
+ ASSERT_EQ(0, close(fd));
+
+ /* Non-allowed path: raw lower tmpfs path, outside of MERGE_DATA. */
+ fd2 = open(lower_fl1, O_RDONLY | O_CLOEXEC);
+ ASSERT_LE(0, fd2);
+ EXPECT_EQ(EACCES, test_getflags(fd2, &flags));
+ EXPECT_EQ(EACCES, test_setflags(fd2, FS_NODUMP_FL));
+ ASSERT_EQ(0, close(fd2));
+
+ /*
+ * Removes the copied-up file to keep the upper tree in the state
+ * expected by FIXTURE_TEARDOWN_PARENT().
+ */
+ EXPECT_EQ(0, remove_path(merge_fl1));
+}
+
+/*
+ * A landlocked process cannot change the mount topology with mount(2),
+ * even with CAP_SYS_ADMIN (see hook_sb_mount()). An overlay filesystem
+ * can therefore not be mounted this way by a restricted process.
+ */
+TEST_F_FORK(layout1, overlay_mount_denied)
+{
+ const char lower_dir[] = TMP_DIR "/ovl-lower";
+ const char upper_dir[] = TMP_DIR "/ovl-upper";
+ const char work_dir[] = TMP_DIR "/ovl-work";
+ const char merge_dir[] = TMP_DIR "/ovl-merge";
+ char mount_data[256];
+ const struct rule rules[] = {
+ {
+ .path = merge_dir,
+ .access = LANDLOCK_ACCESS_FS_READ_METADATA |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ },
+ {},
+ };
+ int ruleset_fd;
+
+ ASSERT_EQ(0, mkdir(lower_dir, 0700));
+ ASSERT_EQ(0, mkdir(upper_dir, 0700));
+ ASSERT_EQ(0, mkdir(work_dir, 0700));
+ ASSERT_EQ(0, mkdir(merge_dir, 0700));
+
+ snprintf(mount_data, sizeof(mount_data),
+ "lowerdir=%s,upperdir=%s,workdir=%s", lower_dir, upper_dir,
+ work_dir);
+
+ ruleset_fd = create_ruleset(
+ _metadata,
+ LANDLOCK_ACCESS_FS_READ_METADATA |
+ LANDLOCK_ACCESS_FS_WRITE_METADATA,
+ rules);
+ ASSERT_LE(0, ruleset_fd);
+
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ set_cap(_metadata, CAP_SYS_ADMIN);
+ EXPECT_EQ(-1, mount("overlay", merge_dir, "overlay", 0, mount_data));
+ EXPECT_EQ(EPERM, errno);
+ clear_cap(_metadata, CAP_SYS_ADMIN);
+
+ EXPECT_EQ(0, rmdir(lower_dir));
+ EXPECT_EQ(0, rmdir(upper_dir));
+ EXPECT_EQ(0, rmdir(work_dir));
+ EXPECT_EQ(0, rmdir(merge_dir));
+}
+
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/landlock/trace_fs_test.c b/tools/testing/selftests/landlock/trace_fs_test.c
index 64014ade3a0e..97506d249c4f 100644
--- a/tools/testing/selftests/landlock/trace_fs_test.c
+++ b/tools/testing/selftests/landlock/trace_fs_test.c
@@ -252,7 +252,8 @@ TEST_F(trace_fs, add_rule_path_beneath)
TEST_F(trace_fs, add_rule_path_beneath_escaped_path_overflow)
{
static const char access_prefix[] = "execute|write_file|read_file|";
- static const char access_suffix[] = "|ioctl_dev|resolve_unix";
+ static const char access_suffix[] =
+ "|ioctl_dev|resolve_unix|read_metadata|write_metadata";
struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
};
--
2.18.0.huawei.25
next prev parent reply other threads:[~2026-10-27 11:39 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 12:11 [PATCH v3 -next 00/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 01/18] fs: pass struct path to notify_change() Cai Xinchen
2026-10-27 12:07 ` sashiko-bot
2026-10-27 12:11 ` [PATCH v3 -next 02/18] LSM: pass struct path to the inode_setsecctx hook Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 03/18] fs: pass struct path to xattr helpers Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 04/18] fs: pass struct path to POSIX ACL helpers Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 05/18] LSM: pass struct path and struct file to the inode_setattr hook Cai Xinchen
2026-10-27 12:48 ` sashiko-bot
2026-10-27 12:11 ` [PATCH v3 -next 06/18] LSM: pass struct path and struct file to the inode xattr hooks Cai Xinchen
2026-10-27 13:02 ` sashiko-bot
2026-10-27 15:01 ` Alexei Starovoitov
2026-10-28 10:51 ` Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 07/18] LSM: pass struct path and struct file to the inode posix acl hooks Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 08/18] LSM: pass struct path and struct file to the inode fileattr hooks Cai Xinchen
2026-10-27 13:45 ` sashiko-bot
2026-10-27 12:11 ` [PATCH v3 -next 09/18] LSM: rename inode_getattr to path_getattr and pass struct file Cai Xinchen
2026-10-27 14:01 ` sashiko-bot
2026-10-27 12:11 ` [PATCH v3 -next 10/18] LSM: rename inode_setattr to path_setattr Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 11/18] LSM: rename the inode xattr hooks to path_xattr hooks Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 12/18] LSM: rename the inode posix acl hooks to path_*_acl hooks Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 13/18] LSM: rename the inode fileattr hooks to path_file_* hooks Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 14/18] LSM: rename inode_setsecctx to path_setsecctx Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 15/18] landlock: Add READ_METADATA and WRITE_METADATA access rights Cai Xinchen
2026-10-27 14:49 ` sashiko-bot
2026-10-27 12:11 ` Cai Xinchen [this message]
2026-10-27 15:11 ` [PATCH v3 -next 16/18] selftests/landlock: Add tests for metadata " sashiko-bot
2026-10-27 12:11 ` [PATCH v3 -next 17/18] samples/landlock: Add metadata rights to sandboxer Cai Xinchen
2026-10-27 12:11 ` [PATCH v3 -next 18/18] Documentation: Update landlock doc for metadata rights Cai Xinchen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261027121151.1887650-17-caixinchen1@huawei.com \
--to=caixinchen1@huawei.com \
--cc=Dai.Ngo@oracle.com \
--cc=amir73il@gmail.com \
--cc=andrii@kernel.org \
--cc=apparmor@lists.ubuntu.com \
--cc=ast@kernel.org \
--cc=axboe@kernel.dk \
--cc=bpf@vger.kernel.org \
--cc=brauner@kernel.org \
--cc=casey@schaufler-ca.com \
--cc=cel@kernel.org \
--cc=chenxiaosong@chenxiaosong.com \
--cc=code@tyhicks.com \
--cc=corbet@lwn.net \
--cc=dakr@kernel.org \
--cc=daniel@iogearbox.net \
--cc=dhowells@redhat.com \
--cc=djwong@kernel.org \
--cc=dlemoal@kernel.org \
--cc=dmitry.kasatkin@gmail.com \
--cc=driver-core@lists.linux.dev \
--cc=dsterba@suse.com \
--cc=ecryptfs@vger.kernel.org \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=eric.snowberg@oracle.com \
--cc=exfat@lists.linux.dev \
--cc=georgia.garcia@canonical.com \
--cc=gnoack@google.com \
--cc=gregkh@linuxfoundation.org \
--cc=hch@lst.de \
--cc=hirofumi@mail.parknet.co.jp \
--cc=hyc.lee@gmail.com \
--cc=ihor.solodrai@linux.dev \
--cc=jack@suse.cz \
--cc=jlayton@kernel.org \
--cc=jmorris@namei.org \
--cc=johan@kernel.org \
--cc=john.johansen@canonical.com \
--cc=jolsa@kernel.org \
--cc=kpsingh@kernel.org \
--cc=linkinjeon@kernel.org \
--cc=linux-block@vger.kernel.org \
--cc=linux-btrfs@vger.kernel.org \
--cc=linux-cifs@vger.kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-integrity@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=linux-unionfs@vger.kernel.org \
--cc=lujialin4@huawei.com \
--cc=martin.lau@linux.dev \
--cc=mason@kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=matt@bobrowski.net \
--cc=memxor@gmail.com \
--cc=mhiramat@kernel.org \
--cc=mic@digikod.net \
--cc=miklos@szeredi.hu \
--cc=nanx95726@gmail.com \
--cc=neil@brown.name \
--cc=netfs@lists.linux.dev \
--cc=ntfs@lists.linux.dev \
--cc=okorniev@redhat.com \
--cc=omosnacek@gmail.com \
--cc=paul@paul-moore.com \
--cc=penguin-kernel@I-love.SAKURA.ne.jp \
--cc=rafael@kernel.org \
--cc=rdunlap@infradead.org \
--cc=roberto.sassu@huawei.com \
--cc=rostedt@goodmis.org \
--cc=selinux@vger.kernel.org \
--cc=senozhatsky@chromium.org \
--cc=serge@hallyn.com \
--cc=sj1557.seo@samsung.com \
--cc=skhan@linuxfoundation.org \
--cc=song@kernel.org \
--cc=stephen.smalley.work@gmail.com \
--cc=takedakn@nttdata.co.jp \
--cc=tom@talpey.com \
--cc=utilityemal77@gmail.com \
--cc=viro@zeniv.linux.org.uk \
--cc=xiujianfeng@huawei.com \
--cc=yonghong.song@linux.dev \
--cc=yuezhang.mo@sony.com \
--cc=zohar@linux.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox