diff mbox series

[v3,09/11] Add landlock04 test

Message ID 20240711-landlock-v3-9-c7b0e9edf9b0@suse.com
State Accepted
Headers show
Series landlock testing suite | expand

Commit Message

Andrea Cervesato July 11, 2024, 11:18 a.m. UTC
From: Andrea Cervesato <andrea.cervesato@suse.com>

This test verifies that all landlock rules are working properly.
The way we do it is to verify that all disabled syscalls are not
working but the one we enabled via specifc landlock rules.

Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
 runtest/syscalls                                   |   1 +
 testcases/kernel/syscalls/landlock/.gitignore      |   2 +
 testcases/kernel/syscalls/landlock/landlock04.c    | 214 +++++++++++++
 testcases/kernel/syscalls/landlock/landlock_exec.c |   9 +
 .../kernel/syscalls/landlock/landlock_tester.h     | 350 +++++++++++++++++++++
 5 files changed, 576 insertions(+)

Comments

Li Wang July 12, 2024, 7:50 a.m. UTC | #1
Nice work!
Reviewed-by: Li Wang <liwang@redhat.com>

On Thu, Jul 11, 2024 at 7:21 PM Andrea Cervesato <andrea.cervesato@suse.de>
wrote:

> From: Andrea Cervesato <andrea.cervesato@suse.com>
>
> This test verifies that all landlock rules are working properly.
> The way we do it is to verify that all disabled syscalls are not
> working but the one we enabled via specifc landlock rules.
>
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
>  runtest/syscalls                                   |   1 +
>  testcases/kernel/syscalls/landlock/.gitignore      |   2 +
>  testcases/kernel/syscalls/landlock/landlock04.c    | 214 +++++++++++++
>  testcases/kernel/syscalls/landlock/landlock_exec.c |   9 +
>  .../kernel/syscalls/landlock/landlock_tester.h     | 350
> +++++++++++++++++++++
>  5 files changed, 576 insertions(+)
>
> diff --git a/runtest/syscalls b/runtest/syscalls
> index f2b64c0df..3c7cd66e2 100644
> --- a/runtest/syscalls
> +++ b/runtest/syscalls
> @@ -688,6 +688,7 @@ kill13 kill13
>  landlock01 landlock01
>  landlock02 landlock02
>  landlock03 landlock03
> +landlock04 landlock04
>
>  lchown01 lchown01
>  lchown01_16 lchown01_16
> diff --git a/testcases/kernel/syscalls/landlock/.gitignore
> b/testcases/kernel/syscalls/landlock/.gitignore
> index f79cd090b..4fe8d7cba 100644
> --- a/testcases/kernel/syscalls/landlock/.gitignore
> +++ b/testcases/kernel/syscalls/landlock/.gitignore
> @@ -1,3 +1,5 @@
> +landlock_exec
>  landlock01
>  landlock02
>  landlock03
> +landlock04
> diff --git a/testcases/kernel/syscalls/landlock/landlock04.c
> b/testcases/kernel/syscalls/landlock/landlock04.c
> new file mode 100644
> index 000000000..30fd9400f
> --- /dev/null
> +++ b/testcases/kernel/syscalls/landlock/landlock04.c
> @@ -0,0 +1,214 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2024 SUSE LLC Andrea Cervesato <
> andrea.cervesato@suse.com>
> + */
> +
> +/*\
> + * [Description]
> + *
> + * This test verifies that all landlock rules are working properly. The
> way we
> + * do it is to verify that all disabled syscalls are not working but the
> one we
> + * enabled via specifc landlock rules.
> + */
> +
> +#include "landlock_common.h"
> +#include "landlock_tester.h"
> +#include "tst_safe_stdio.h"
> +
> +#define ACCESS_NAME(x) #x
> +
> +static struct landlock_ruleset_attr *ruleset_attr;
> +static struct landlock_path_beneath_attr *path_beneath_attr;
> +
> +static struct tvariant {
> +       int access;
> +       char *desc;
> +} tvariants[] = {
> +       {
> +               LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_EXECUTE,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_EXECUTE)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_WRITE_FILE,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_WRITE_FILE)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_READ_FILE,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_READ_FILE)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_READ_DIR,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_READ_DIR)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_REMOVE_DIR,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_REMOVE_DIR)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_REMOVE_FILE,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_REMOVE_FILE)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_MAKE_CHAR,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_CHAR)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_MAKE_BLOCK,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_BLOCK)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_MAKE_REG,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_REG)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_MAKE_SOCK,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_SOCK)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_MAKE_FIFO,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_FIFO)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_MAKE_SYM,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_SYM)
> +       },
> +       {
> +               LANDLOCK_ACCESS_FS_WRITE_FILE |
> LANDLOCK_ACCESS_FS_TRUNCATE,
> +               ACCESS_NAME(LANDLOCK_ACCESS_FS_TRUNCATE)
> +       },
> +};
> +
> +static void run(void)
> +{
> +       if (!SAFE_FORK()) {
> +               struct tvariant  variant = tvariants[tst_variant];
> +
> +               tester_run_all_rules(variant.access);
> +               _exit(0);
> +       }
> +}
> +
> +static void enable_exec_libs(const int ruleset_fd)
> +{
> +       FILE *fp;
> +       char line[1024];
> +       char path[PATH_MAX];
> +       char dependency[8][PATH_MAX];
> +       int count = 0;
> +       int duplicate = 0;
> +
> +       fp = SAFE_FOPEN("/proc/self/maps", "r");
> +
> +       while (fgets(line, sizeof(line), fp)) {
> +               if (strstr(line, ".so") == NULL)
> +                       continue;
> +
> +               SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path);
> +
> +               for (int i = 0; i < count; i++) {
> +                       if (strcmp(path, dependency[i]) == 0) {
> +                               duplicate = 1;
> +                               break;
> +                       }
> +               }
> +
> +               if (duplicate) {
> +                       duplicate = 0;
> +                       continue;
> +               }
> +
> +               strncpy(dependency[count], path, PATH_MAX);
> +               count++;
> +
> +               tst_res(TINFO, "Enable read/exec permissions for %s",
> path);
> +
> +               path_beneath_attr->allowed_access =
> +                       LANDLOCK_ACCESS_FS_READ_FILE |
> +                       LANDLOCK_ACCESS_FS_EXECUTE;
> +               path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH |
> O_CLOEXEC);
> +
> +               SAFE_LANDLOCK_ADD_RULE(
> +                       ruleset_fd,
> +                       LANDLOCK_RULE_PATH_BENEATH,
> +                       path_beneath_attr,
> +                       0);
> +
> +               SAFE_CLOSE(path_beneath_attr->parent_fd);
> +       }
> +
> +       SAFE_FCLOSE(fp);
> +}
> +
> +static void setup(void)
> +{
> +       struct tvariant variant = tvariants[tst_variant];
> +       int ruleset_fd;
> +
> +       verify_landlock_is_enabled();
> +       tester_create_tree();
> +
> +       tst_res(TINFO, "Testing %s", variant.desc);
> +
> +       ruleset_attr->handled_access_fs = tester_get_all_rules();
> +
> +       ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET(
> +               ruleset_attr, sizeof(struct landlock_ruleset_attr), 0);
> +
> +       /* since our binary is dynamically linked, we need to enable
> dependences
> +        * to be read and executed
> +        */
> +       enable_exec_libs(ruleset_fd);
> +
> +       path_beneath_attr->allowed_access = variant.access;
> +       path_beneath_attr->parent_fd = SAFE_OPEN(
> +               SANDBOX_FOLDER, O_PATH | O_CLOEXEC);
> +
> +       SAFE_LANDLOCK_ADD_RULE(
> +               ruleset_fd,
> +               LANDLOCK_RULE_PATH_BENEATH,
> +               path_beneath_attr,
> +               0);
> +
> +       SAFE_CLOSE(path_beneath_attr->parent_fd);
> +
> +       enforce_ruleset(ruleset_fd);
> +       SAFE_CLOSE(ruleset_fd);
> +}
> +
> +static struct tst_test test = {
> +       .test_all = run,
> +       .setup = setup,
> +       .min_kver = "5.13",
> +       .forks_child = 1,
> +       .needs_tmpdir = 1,
> +       .needs_root = 1,
> +       .test_variants = ARRAY_SIZE(tvariants),
> +       .resource_files = (const char *[]) {
> +               TESTAPP,
> +               NULL,
> +       },
> +       .needs_kconfigs = (const char *[]) {
> +               "CONFIG_SECURITY_LANDLOCK=y",
> +               NULL
> +       },
> +       .bufs = (struct tst_buffers []) {
> +               {&ruleset_attr, .size = sizeof(struct
> landlock_ruleset_attr)},
> +               {&path_beneath_attr, .size = sizeof(struct
> landlock_path_beneath_attr)},
> +               {},
> +       },
> +       .caps = (struct tst_cap []) {
> +               TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN),
> +               TST_CAP(TST_CAP_REQ, CAP_MKNOD),
> +               {}
> +       },
> +       .format_device = 1,
> +       .mount_device = 1,
> +       .mntpoint = SANDBOX_FOLDER,
> +       .all_filesystems = 1,
> +       .skip_filesystems = (const char *[]) {
> +               "vfat",
> +               "exfat",
> +               NULL
> +       },
> +       .max_runtime = 3600,
> +};
> diff --git a/testcases/kernel/syscalls/landlock/landlock_exec.c
> b/testcases/kernel/syscalls/landlock/landlock_exec.c
> new file mode 100644
> index 000000000..aae5c76b2
> --- /dev/null
> +++ b/testcases/kernel/syscalls/landlock/landlock_exec.c
> @@ -0,0 +1,9 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2024 SUSE LLC Andrea Cervesato <
> andrea.cervesato@suse.com>
> + */
> +
> +int main(void)
> +{
> +       return 0;
> +}
> diff --git a/testcases/kernel/syscalls/landlock/landlock_tester.h
> b/testcases/kernel/syscalls/landlock/landlock_tester.h
> new file mode 100644
> index 000000000..89ca085d7
> --- /dev/null
> +++ b/testcases/kernel/syscalls/landlock/landlock_tester.h
> @@ -0,0 +1,350 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2024 SUSE LLC Andrea Cervesato <
> andrea.cervesato@suse.com>
> + */
> +
> +#ifndef LANDLOCK_TESTER_H
> +
> +#include "tst_test.h"
> +#include "lapi/landlock.h"
> +#include <sys/sysmacros.h>
> +
> +#define PERM_MODE 0700
> +
> +#define SANDBOX_FOLDER "sandbox"
> +#define TESTAPP                        "landlock_exec"
> +
> +#define FILE_EXEC              SANDBOX_FOLDER"/"TESTAPP
> +#define FILE_READ              SANDBOX_FOLDER"/file_read"
> +#define FILE_WRITE             SANDBOX_FOLDER"/file_write"
> +#define FILE_REMOVE            SANDBOX_FOLDER"/file_remove"
> +#define FILE_UNLINK            SANDBOX_FOLDER"/file_unlink"
> +#define FILE_UNLINKAT  SANDBOX_FOLDER"/file_unlinkat"
> +#define FILE_TRUNCATE  SANDBOX_FOLDER"/file_truncate"
> +#define FILE_REGULAR   SANDBOX_FOLDER"/regular0"
> +#define FILE_SOCKET            SANDBOX_FOLDER"/socket0"
> +#define FILE_FIFO              SANDBOX_FOLDER"/fifo0"
> +#define FILE_SYM0              SANDBOX_FOLDER"/symbolic0"
> +#define FILE_SYM1              SANDBOX_FOLDER"/symbolic1"
> +#define DIR_READDIR            SANDBOX_FOLDER"/dir_readdir"
> +#define DIR_RMDIR              SANDBOX_FOLDER"/dir_rmdir"
> +#define DEV_CHAR0              SANDBOX_FOLDER"/chardev0"
> +#define DEV_BLK0               SANDBOX_FOLDER"/blkdev0"
> +
> +#define ALL_RULES (\
> +       LANDLOCK_ACCESS_FS_EXECUTE | \
> +       LANDLOCK_ACCESS_FS_WRITE_FILE | \
> +       LANDLOCK_ACCESS_FS_READ_FILE | \
> +       LANDLOCK_ACCESS_FS_READ_DIR | \
> +       LANDLOCK_ACCESS_FS_REMOVE_DIR | \
> +       LANDLOCK_ACCESS_FS_REMOVE_FILE | \
> +       LANDLOCK_ACCESS_FS_MAKE_CHAR | \
> +       LANDLOCK_ACCESS_FS_MAKE_DIR | \
> +       LANDLOCK_ACCESS_FS_MAKE_REG | \
> +       LANDLOCK_ACCESS_FS_MAKE_SOCK | \
> +       LANDLOCK_ACCESS_FS_MAKE_FIFO | \
> +       LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
> +       LANDLOCK_ACCESS_FS_MAKE_SYM | \
> +       LANDLOCK_ACCESS_FS_REFER | \
> +       LANDLOCK_ACCESS_FS_TRUNCATE | \
> +       LANDLOCK_ACCESS_NET_BIND_TCP | \
> +       LANDLOCK_ACCESS_NET_CONNECT_TCP | \
> +       LANDLOCK_ACCESS_FS_IOCTL_DEV)
> +
> +static char *readdir_files[] = {
> +       DIR_READDIR"/file0",
> +       DIR_READDIR"/file1",
> +       DIR_READDIR"/file2",
> +};
> +
> +static int dev_chr;
> +static int dev_blk;
> +
> +static int tester_get_all_rules(void)
> +{
> +       int abi;
> +       int all_rules = ALL_RULES;
> +
> +       abi = SAFE_LANDLOCK_CREATE_RULESET(
> +               NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
> +
> +       if (abi < 2)
> +               all_rules &= ~LANDLOCK_ACCESS_FS_REFER;
> +
> +       if (abi < 3)
> +               all_rules &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
> +
> +       if (abi < 4) {
> +               all_rules &= ~(LANDLOCK_ACCESS_NET_BIND_TCP |
> +                       LANDLOCK_ACCESS_NET_CONNECT_TCP);
> +       }
> +
> +       if (abi < 5)
> +               all_rules &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
> +
> +       return all_rules;
> +}
> +
> +static void tester_create_tree(void)
> +{
> +       if (access(SANDBOX_FOLDER, F_OK) == -1)
> +               SAFE_MKDIR(SANDBOX_FOLDER, PERM_MODE);
> +
> +       /* folders */
> +       SAFE_MKDIR(DIR_RMDIR, PERM_MODE);
> +       SAFE_MKDIR(DIR_READDIR, PERM_MODE);
> +       for (size_t i = 0; i < ARRAY_SIZE(readdir_files); i++)
> +               SAFE_TOUCH(readdir_files[i], PERM_MODE, NULL);
> +
> +       /* files */
> +       tst_fill_file(FILE_READ, 'a', getpagesize(), 1);
> +       SAFE_TOUCH(FILE_WRITE, PERM_MODE, NULL);
> +       SAFE_TOUCH(FILE_REMOVE, PERM_MODE, NULL);
> +       SAFE_TOUCH(FILE_UNLINK, PERM_MODE, NULL);
> +       SAFE_TOUCH(FILE_UNLINKAT, PERM_MODE, NULL);
> +       SAFE_TOUCH(FILE_TRUNCATE, PERM_MODE, NULL);
> +       SAFE_TOUCH(FILE_SYM0, PERM_MODE, NULL);
> +       SAFE_CP(TESTAPP, FILE_EXEC);
> +
> +       /* devices */
> +       dev_chr = makedev(1, 3);
> +       dev_blk = makedev(7, 0);
> +}
> +
> +static void _test_exec(const int result)
> +{
> +       int status;
> +       pid_t pid;
> +       char *const args[] = {(char *)FILE_EXEC, NULL};
> +
> +       tst_res(TINFO, "Test binary execution");
> +
> +       pid = SAFE_FORK();
> +       if (!pid) {
> +               int rval;
> +
> +               if (result == TPASS) {
> +                       rval = execve(FILE_EXEC, args, NULL);
> +                       if (rval == -1)
> +                               tst_res(TFAIL | TERRNO, "Failed to execute
> test binary");
> +               } else {
> +                       TST_EXP_FAIL(execve(FILE_EXEC, args, NULL),
> EACCES);
> +               }
> +
> +               _exit(1);
> +       }
> +
> +       SAFE_WAITPID(pid, &status, 0);
> +       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
> +               return;
> +
> +       tst_res(result, "Test binary has been executed");
> +}
> +
> +static void _test_write(const int result)
> +{
> +       tst_res(TINFO, "Test writing file");
> +
> +       if (result == TPASS)
> +               TST_EXP_FD(open(FILE_WRITE, O_WRONLY, PERM_MODE));
> +       else
> +               TST_EXP_FAIL(open(FILE_WRITE, O_WRONLY, PERM_MODE),
> EACCES);
> +
> +       if (TST_RET != -1)
> +               SAFE_CLOSE(TST_RET);
> +}
> +
> +static void _test_read(const int result)
> +{
> +       tst_res(TINFO, "Test reading file");
> +
> +       if (result == TPASS)
> +               TST_EXP_FD(open(FILE_READ, O_RDONLY, PERM_MODE));
> +       else
> +               TST_EXP_FAIL(open(FILE_READ, O_RDONLY, PERM_MODE), EACCES);
> +
> +       if (TST_RET != -1)
> +               SAFE_CLOSE(TST_RET);
> +}
> +
> +static void _test_readdir(const int result)
> +{
> +       tst_res(TINFO, "Test reading directory");
> +
> +       DIR *dir;
> +       struct dirent *de;
> +       int files_counted = 0;
> +
> +       dir = opendir(DIR_READDIR);
> +       if (!dir) {
> +               tst_res(result == TPASS ? TFAIL : TPASS,
> +                       "Can't read '%s' directory", DIR_READDIR);
> +
> +               return;
> +       }
> +
> +       tst_res(result, "Can read '%s' directory", DIR_READDIR);
> +       if (result == TFAIL)
> +               return;
> +
> +       while ((de = readdir(dir)) != NULL) {
> +               if (de->d_type != DT_REG)
> +                       continue;
> +
> +               for (size_t i = 0; i < ARRAY_SIZE(readdir_files); i++) {
> +                       if (readdir_files[i] == NULL)
> +                               continue;
> +
> +                       if (strstr(readdir_files[i], de->d_name) != NULL)
> +                               files_counted++;
> +               }
> +       }
> +
> +       SAFE_CLOSEDIR(dir);
> +
> +       TST_EXP_EQ_LI(files_counted, ARRAY_SIZE(readdir_files));
> +}
> +
> +static void _test_rmdir(const int result)
> +{
> +       tst_res(TINFO, "Test removing directory");
> +
> +       if (result == TPASS)
> +               TST_EXP_PASS(rmdir(DIR_RMDIR));
> +       else
> +               TST_EXP_FAIL(rmdir(DIR_RMDIR), EACCES);
> +}
> +
> +static void _test_rmfile(const int result)
> +{
> +       tst_res(TINFO, "Test removing file");
> +
> +       if (result == TPASS) {
> +               TST_EXP_PASS(unlink(FILE_UNLINK));
> +               TST_EXP_PASS(remove(FILE_REMOVE));
> +       } else {
> +               TST_EXP_FAIL(unlink(FILE_UNLINK), EACCES);
> +               TST_EXP_FAIL(remove(FILE_REMOVE), EACCES);
> +       }
> +}
> +
> +static void _test_make(
> +       const char *path,
> +       const int type,
> +       const int dev,
> +       const int result)
> +{
> +       tst_res(TINFO, "Test normal or special files creation");
> +
> +       if (result == TPASS)
> +               TST_EXP_PASS(mknod(path, type | 0400, dev));
> +       else
> +               TST_EXP_FAIL(mknod(path, type | 0400, dev), EACCES);
> +}
> +
> +static void _test_symbolic(const int result)
> +{
> +       tst_res(TINFO, "Test symbolic links");
> +
> +       if (result == TPASS)
> +               TST_EXP_PASS(symlink(FILE_SYM0, FILE_SYM1));
> +       else
> +               TST_EXP_FAIL(symlink(FILE_SYM0, FILE_SYM1), EACCES);
> +}
> +
> +static void _test_truncate(const int result)
> +{
> +       int fd;
> +
> +       tst_res(TINFO, "Test truncating file");
> +
> +       if (result == TPASS) {
> +               TST_EXP_PASS(truncate(FILE_TRUNCATE, 10));
> +
> +               fd = TST_EXP_FD(open(FILE_TRUNCATE, O_WRONLY, PERM_MODE));
> +               if (fd != -1) {
> +                       TST_EXP_PASS(ftruncate(fd, 10));
> +                       SAFE_CLOSE(fd);
> +               }
> +
> +               fd = TST_EXP_FD(open(FILE_TRUNCATE, O_WRONLY | O_TRUNC,
> PERM_MODE));
> +               if (fd != -1)
> +                       SAFE_CLOSE(fd);
> +       } else {
> +               TST_EXP_FAIL(truncate(FILE_TRUNCATE, 10), EACCES);
> +
> +               fd = open(FILE_TRUNCATE, O_WRONLY, PERM_MODE);
> +               if (fd != -1) {
> +                       TST_EXP_FAIL(ftruncate(fd, 10), EACCES);
> +                       SAFE_CLOSE(fd);
> +               }
> +
> +               TST_EXP_FAIL(open(FILE_TRUNCATE, O_WRONLY | O_TRUNC,
> PERM_MODE),
> +                       EACCES);
> +
> +               if (TST_RET != -1)
> +                       SAFE_CLOSE(TST_RET);
> +       }
> +}
> +
> +static void tester_run_rules(const int rules, const int result)
> +{
> +       if (rules & LANDLOCK_ACCESS_FS_EXECUTE)
> +               _test_exec(result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_WRITE_FILE)
> +               _test_write(result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_READ_FILE)
> +               _test_read(result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_READ_DIR)
> +               _test_readdir(result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_REMOVE_DIR)
> +               _test_rmdir(result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_REMOVE_FILE)
> +               _test_rmfile(result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_MAKE_CHAR)
> +               _test_make(DEV_CHAR0, S_IFCHR, dev_chr, result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_MAKE_BLOCK)
> +               _test_make(DEV_BLK0, S_IFBLK, dev_blk, result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_MAKE_REG)
> +               _test_make(FILE_REGULAR, S_IFREG, 0, result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_MAKE_SOCK)
> +               _test_make(FILE_SOCKET, S_IFSOCK, 0, result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_MAKE_FIFO)
> +               _test_make(FILE_FIFO, S_IFIFO, 0, result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_MAKE_SYM)
> +               _test_symbolic(result);
> +
> +       if (rules & LANDLOCK_ACCESS_FS_TRUNCATE) {
> +               if ((tst_kvercmp(6, 2, 0)) < 0) {
> +                       tst_res(TINFO, "Skip truncate test. Minimum kernel
> version is 6.2");
> +                       return;
> +               }
> +
> +               _test_truncate(result);
> +       }
> +}
> +
> +static inline void tester_run_all_rules(const int pass_rules)
> +{
> +       int fail_rules;
> +       int all_rules;
> +
> +       all_rules = tester_get_all_rules();
> +       fail_rules = all_rules & ~pass_rules;
> +
> +       tester_run_rules(pass_rules, TPASS);
> +       tester_run_rules(fail_rules, TFAIL);
> +}
> +
> +#endif
>
> --
> 2.43.0
>
>
> --
> Mailing list info: https://lists.linux.it/listinfo/ltp
>
>
Petr Vorel July 16, 2024, 5:27 p.m. UTC | #2
Hi Andrea,

...
> +static void enable_exec_libs(const int ruleset_fd)
> +{
> +	FILE *fp;
> +	char line[1024];
> +	char path[PATH_MAX];
> +	char dependency[8][PATH_MAX];
> +	int count = 0;
> +	int duplicate = 0;
> +
> +	fp = SAFE_FOPEN("/proc/self/maps", "r");
> +
> +	while (fgets(line, sizeof(line), fp)) {
> +		if (strstr(line, ".so") == NULL)
> +			continue;
> +
> +		SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path);
> +
> +		for (int i = 0; i < count; i++) {
> +			if (strcmp(path, dependency[i]) == 0) {
> +				duplicate = 1;
> +				break;
> +			}
> +		}
> +
> +		if (duplicate) {
> +			duplicate = 0;
> +			continue;
> +		}
> +
> +		strncpy(dependency[count], path, PATH_MAX);
> +		count++;
> +
> +		tst_res(TINFO, "Enable read/exec permissions for %s", path);
> +
> +		path_beneath_attr->allowed_access =
> +			LANDLOCK_ACCESS_FS_READ_FILE |
> +			LANDLOCK_ACCESS_FS_EXECUTE;
> +		path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | O_CLOEXEC);
> +
> +		SAFE_LANDLOCK_ADD_RULE(
> +			ruleset_fd,
> +			LANDLOCK_RULE_PATH_BENEATH,
> +			path_beneath_attr,
> +			0);

Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it fails (after
fresh boot) with:

...
tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
tst_test.c:1746: TINFO: === Testing on ext2 ===
tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra opts=''
mke2fs 1.47.0 (5-Feb-2023)
tst_test.c:1123: TINFO: Mounting /dev/loop1 to /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0
landlock_common.h:30: TINFO: Landlock ABI v3
landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
landlock04.c:123: TINFO: Enable read/exec permissions for /usr/lib/i386-linux-gnu/libc.so.6
landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL (22)
tst_test.c:1746: TINFO: === Testing on ext3 ===

It works on 6.10.0-rc7-3.g92abc10-default (Tumbleweed), 6.9.8-amd64 (Debian).

Any hint, what could be wrong?

Kind regards,
Petr

> +
> +		SAFE_CLOSE(path_beneath_attr->parent_fd);
> +	}
> +
> +	SAFE_FCLOSE(fp);
> +}
Andrea Cervesato July 24, 2024, 10:41 a.m. UTC | #3
Hi Petr,

I honestly have no idea why it fails on that machine. All the landlock 
features we are using are there since version 1 and this test should 
work from kernel 5.13, so EINVAL is not justified to that. The 
exable_exec_libs is correctly guessing the path of the libc and that is 
also correct.

Andrea

On 7/16/24 19:27, Petr Vorel wrote:
> Hi Andrea,
>
> ...
>> +static void enable_exec_libs(const int ruleset_fd)
>> +{
>> +	FILE *fp;
>> +	char line[1024];
>> +	char path[PATH_MAX];
>> +	char dependency[8][PATH_MAX];
>> +	int count = 0;
>> +	int duplicate = 0;
>> +
>> +	fp = SAFE_FOPEN("/proc/self/maps", "r");
>> +
>> +	while (fgets(line, sizeof(line), fp)) {
>> +		if (strstr(line, ".so") == NULL)
>> +			continue;
>> +
>> +		SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path);
>> +
>> +		for (int i = 0; i < count; i++) {
>> +			if (strcmp(path, dependency[i]) == 0) {
>> +				duplicate = 1;
>> +				break;
>> +			}
>> +		}
>> +
>> +		if (duplicate) {
>> +			duplicate = 0;
>> +			continue;
>> +		}
>> +
>> +		strncpy(dependency[count], path, PATH_MAX);
>> +		count++;
>> +
>> +		tst_res(TINFO, "Enable read/exec permissions for %s", path);
>> +
>> +		path_beneath_attr->allowed_access =
>> +			LANDLOCK_ACCESS_FS_READ_FILE |
>> +			LANDLOCK_ACCESS_FS_EXECUTE;
>> +		path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | O_CLOEXEC);
>> +
>> +		SAFE_LANDLOCK_ADD_RULE(
>> +			ruleset_fd,
>> +			LANDLOCK_RULE_PATH_BENEATH,
>> +			path_beneath_attr,
>> +			0);
> Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it fails (after
> fresh boot) with:
>
> ...
> tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
> tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
> tst_test.c:1746: TINFO: === Testing on ext2 ===
> tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra opts=''
> mke2fs 1.47.0 (5-Feb-2023)
> tst_test.c:1123: TINFO: Mounting /dev/loop1 to /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0
> landlock_common.h:30: TINFO: Landlock ABI v3
> landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
> landlock04.c:123: TINFO: Enable read/exec permissions for /usr/lib/i386-linux-gnu/libc.so.6
> landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL (22)
> tst_test.c:1746: TINFO: === Testing on ext3 ===
>
> It works on 6.10.0-rc7-3.g92abc10-default (Tumbleweed), 6.9.8-amd64 (Debian).
>
> Any hint, what could be wrong?
>
> Kind regards,
> Petr
>
>> +
>> +		SAFE_CLOSE(path_beneath_attr->parent_fd);
>> +	}
>> +
>> +	SAFE_FCLOSE(fp);
>> +}
Li Wang July 24, 2024, 12:12 p.m. UTC | #4
Hi Petr, Andrea,

On Wed, Jul 17, 2024 at 1:27 AM Petr Vorel <pvorel@suse.cz> wrote:

> Hi Andrea,
>
> ...
> > +static void enable_exec_libs(const int ruleset_fd)
> > +{
> > +     FILE *fp;
> > +     char line[1024];
> > +     char path[PATH_MAX];
> > +     char dependency[8][PATH_MAX];
> > +     int count = 0;
> > +     int duplicate = 0;
> > +
> > +     fp = SAFE_FOPEN("/proc/self/maps", "r");
> > +
> > +     while (fgets(line, sizeof(line), fp)) {
> > +             if (strstr(line, ".so") == NULL)
> > +                     continue;
> > +
> > +             SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path);
> > +
> > +             for (int i = 0; i < count; i++) {
> > +                     if (strcmp(path, dependency[i]) == 0) {
> > +                             duplicate = 1;
> > +                             break;
> > +                     }
> > +             }
> > +
> > +             if (duplicate) {
> > +                     duplicate = 0;
> > +                     continue;
> > +             }
> > +
> > +             strncpy(dependency[count], path, PATH_MAX);
> > +             count++;
> > +
> > +             tst_res(TINFO, "Enable read/exec permissions for %s",
> path);
> > +
> > +             path_beneath_attr->allowed_access =
> > +                     LANDLOCK_ACCESS_FS_READ_FILE |
> > +                     LANDLOCK_ACCESS_FS_EXECUTE;
> > +             path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH |
> O_CLOEXEC);
> > +
> > +             SAFE_LANDLOCK_ADD_RULE(
> > +                     ruleset_fd,
> > +                     LANDLOCK_RULE_PATH_BENEATH,
> > +                     path_beneath_attr,
> > +                     0);
>
> Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it fails
> (after
> fresh boot) with:
>
> ...
> tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
> tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
> tst_test.c:1746: TINFO: === Testing on ext2 ===
> tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra
> opts=''
> mke2fs 1.47.0 (5-Feb-2023)
> tst_test.c:1123: TINFO: Mounting /dev/loop1 to /tmp/LTP_lant6WbKJ/sandbox
> fstyp=ext2 flags=0
> landlock_common.h:30: TINFO: Landlock ABI v3
> landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
> landlock04.c:123: TINFO: Enable read/exec permissions for
> /usr/lib/i386-linux-gnu/libc.so.6
> landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL
> (22)
>

Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH'  was
refactored from the v6.7 mainline kernel, so it can't add the rule correctly
with older kernels.

commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203
Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
Date:   Thu Oct 26 09:47:46 2023 +0800

    landlock: Refactor landlock_add_rule() syscall

But this is my guess (through reading the code), I didn't do more to
verify that by installing such a kernel.
Petr Vorel July 24, 2024, 1:30 p.m. UTC | #5
Hi Li, Andrea,

> Hi Petr, Andrea,
...
> > ...
> > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
> > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
> > tst_test.c:1746: TINFO: === Testing on ext2 ===
> > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra
> > opts=''
> > mke2fs 1.47.0 (5-Feb-2023)
> > tst_test.c:1123: TINFO: Mounting /dev/loop1 to /tmp/LTP_lant6WbKJ/sandbox
> > fstyp=ext2 flags=0
> > landlock_common.h:30: TINFO: Landlock ABI v3
> > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
> > landlock04.c:123: TINFO: Enable read/exec permissions for
> > /usr/lib/i386-linux-gnu/libc.so.6
> > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL
> > (22)


> Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH'  was
> refactored from the v6.7 mainline kernel, so it can't add the rule correctly
> with older kernels.

> commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203
> Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> Date:   Thu Oct 26 09:47:46 2023 +0800

>     landlock: Refactor landlock_add_rule() syscall

> But this is my guess (through reading the code), I didn't do more to
> verify that by installing such a kernel.

Thanks, Li,  for a hint. Quick test shows that it's working on 6.9.9, but broken
with with 6.6.x or 6.5.x, also reproduced on more distros. I'll verify this
specific commit, I suppose we should require 6.7, right?

Kind regards,
Petr
Li Wang July 24, 2024, 1:37 p.m. UTC | #6
On Wed, Jul 24, 2024 at 9:30 PM Petr Vorel <pvorel@suse.cz> wrote:

> Hi Li, Andrea,
>
> > Hi Petr, Andrea,
> ...
> > > ...
> > > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
> > > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
> > > tst_test.c:1746: TINFO: === Testing on ext2 ===
> > > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra
> > > opts=''
> > > mke2fs 1.47.0 (5-Feb-2023)
> > > tst_test.c:1123: TINFO: Mounting /dev/loop1 to
> /tmp/LTP_lant6WbKJ/sandbox
> > > fstyp=ext2 flags=0
> > > landlock_common.h:30: TINFO: Landlock ABI v3
> > > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
> > > landlock04.c:123: TINFO: Enable read/exec permissions for
> > > /usr/lib/i386-linux-gnu/libc.so.6
> > > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL
> > > (22)
>
>
> > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH'  was
> > refactored from the v6.7 mainline kernel, so it can't add the rule
> correctly
> > with older kernels.
>
> > commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203
> > Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> > Date:   Thu Oct 26 09:47:46 2023 +0800
>
> >     landlock: Refactor landlock_add_rule() syscall
>
> > But this is my guess (through reading the code), I didn't do more to
> > verify that by installing such a kernel.
>
> Thanks, Li,  for a hint. Quick test shows that it's working on 6.9.9, but
> broken
> with with 6.6.x or 6.5.x, also reproduced on more distros. I'll verify this
> specific commit, I suppose we should require 6.7, right?
>

I think YES, 6.7 includes some vital improvement on the landlock syscall.
Petr Vorel July 24, 2024, 1:41 p.m. UTC | #7
> On Wed, Jul 24, 2024 at 9:30 PM Petr Vorel <pvorel@suse.cz> wrote:

> > Hi Li, Andrea,

> > > Hi Petr, Andrea,
> > ...
> > > > ...
> > > > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
> > > > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
> > > > tst_test.c:1746: TINFO: === Testing on ext2 ===
> > > > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra
> > > > opts=''
> > > > mke2fs 1.47.0 (5-Feb-2023)
> > > > tst_test.c:1123: TINFO: Mounting /dev/loop1 to
> > /tmp/LTP_lant6WbKJ/sandbox
> > > > fstyp=ext2 flags=0
> > > > landlock_common.h:30: TINFO: Landlock ABI v3
> > > > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
> > > > landlock04.c:123: TINFO: Enable read/exec permissions for
> > > > /usr/lib/i386-linux-gnu/libc.so.6
> > > > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL
> > > > (22)


> > > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH'  was
> > > refactored from the v6.7 mainline kernel, so it can't add the rule
> > correctly
> > > with older kernels.

> > > commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203
> > > Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> > > Date:   Thu Oct 26 09:47:46 2023 +0800

> > >     landlock: Refactor landlock_add_rule() syscall

> > > But this is my guess (through reading the code), I didn't do more to
> > > verify that by installing such a kernel.

> > Thanks, Li,  for a hint. Quick test shows that it's working on 6.9.9, but
> > broken
> > with with 6.6.x or 6.5.x, also reproduced on more distros. I'll verify this
> > specific commit, I suppose we should require 6.7, right?


> I think YES, 6.7 includes some vital improvement on the landlock syscall.

OK, I'm for merging with .min_kver = "6.7".

What blocking me to merge is the fact landlock03 needs to fix for -i2.
But let's discuss it there.

Kind regards,
Petr
Li Wang July 24, 2024, 1:41 p.m. UTC | #8
On Wed, Jul 24, 2024 at 9:37 PM Li Wang <liwang@redhat.com> wrote:

>
>
> On Wed, Jul 24, 2024 at 9:30 PM Petr Vorel <pvorel@suse.cz> wrote:
>
>> Hi Li, Andrea,
>>
>> > Hi Petr, Andrea,
>> ...
>> > > ...
>> > > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
>> > > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
>> > > tst_test.c:1746: TINFO: === Testing on ext2 ===
>> > > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra
>> > > opts=''
>> > > mke2fs 1.47.0 (5-Feb-2023)
>> > > tst_test.c:1123: TINFO: Mounting /dev/loop1 to
>> /tmp/LTP_lant6WbKJ/sandbox
>> > > fstyp=ext2 flags=0
>> > > landlock_common.h:30: TINFO: Landlock ABI v3
>> > > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
>> > > landlock04.c:123: TINFO: Enable read/exec permissions for
>> > > /usr/lib/i386-linux-gnu/libc.so.6
>> > > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0):
>> EINVAL
>> > > (22)
>>
>>
>> > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH'  was
>> > refactored from the v6.7 mainline kernel, so it can't add the rule
>> correctly
>> > with older kernels.
>>
>> > commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203
>> > Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> > Date:   Thu Oct 26 09:47:46 2023 +0800
>>
>> >     landlock: Refactor landlock_add_rule() syscall
>>
>> > But this is my guess (through reading the code), I didn't do more to
>> > verify that by installing such a kernel.
>>
>> Thanks, Li,  for a hint. Quick test shows that it's working on 6.9.9, but
>> broken
>> with with 6.6.x or 6.5.x, also reproduced on more distros. I'll verify
>> this
>> specific commit, I suppose we should require 6.7, right?
>>
>
And BTW, the below commit is another (on my suspicion list) that needs
check.

commit 13fc6455fa19b0859e1b9640bf09903bec8df4f4
Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
Date:   Thu Oct 26 09:47:40 2023 +0800

    landlock: Make ruleset's access masks more generic


> I think YES, 6.7 includes some vital improvement on the landlock syscall.
>
>
> --
> Regards,
> Li Wang
>
Andrea Cervesato July 24, 2024, 1:47 p.m. UTC | #9
Hi Li,

thanks for checking. Mmmh I don't know if it's because they added 
LANDLOCK_RULE_NET_PORT. It sounds strange to me, since that would break 
all the other features.

Andrea

On 7/24/24 14:12, Li Wang wrote:
> Hi Petr, Andrea,
>
> On Wed, Jul 17, 2024 at 1:27 AM Petr Vorel <pvorel@suse.cz> wrote:
>
>     Hi Andrea,
>
>     ...
>     > +static void enable_exec_libs(const int ruleset_fd)
>     > +{
>     > +     FILE *fp;
>     > +     char line[1024];
>     > +     char path[PATH_MAX];
>     > +     char dependency[8][PATH_MAX];
>     > +     int count = 0;
>     > +     int duplicate = 0;
>     > +
>     > +     fp = SAFE_FOPEN("/proc/self/maps", "r");
>     > +
>     > +     while (fgets(line, sizeof(line), fp)) {
>     > +             if (strstr(line, ".so") == NULL)
>     > +                     continue;
>     > +
>     > +             SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path);
>     > +
>     > +             for (int i = 0; i < count; i++) {
>     > +                     if (strcmp(path, dependency[i]) == 0) {
>     > +                             duplicate = 1;
>     > +                             break;
>     > +                     }
>     > +             }
>     > +
>     > +             if (duplicate) {
>     > +                     duplicate = 0;
>     > +                     continue;
>     > +             }
>     > +
>     > +             strncpy(dependency[count], path, PATH_MAX);
>     > +             count++;
>     > +
>     > +             tst_res(TINFO, "Enable read/exec permissions for
>     %s", path);
>     > +
>     > +             path_beneath_attr->allowed_access =
>     > +                     LANDLOCK_ACCESS_FS_READ_FILE |
>     > +                     LANDLOCK_ACCESS_FS_EXECUTE;
>     > +             path_beneath_attr->parent_fd = SAFE_OPEN(path,
>     O_PATH | O_CLOEXEC);
>     > +
>     > +             SAFE_LANDLOCK_ADD_RULE(
>     > +                     ruleset_fd,
>     > +                     LANDLOCK_RULE_PATH_BENEATH,
>     > +                     path_beneath_attr,
>     > +                     0);
>
>     Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it
>     fails (after
>     fresh boot) with:
>
>     ...
>     tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
>     tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
>     tst_test.c:1746: TINFO: === Testing on ext2 ===
>     tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts=''
>     extra opts=''
>     mke2fs 1.47.0 (5-Feb-2023)
>     tst_test.c:1123: TINFO: Mounting /dev/loop1 to
>     /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0
>     landlock_common.h:30: TINFO: Landlock ABI v3
>     landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
>     landlock04.c:123: TINFO: Enable read/exec permissions for
>     /usr/lib/i386-linux-gnu/libc.so.6
>     landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0):
>     EINVAL (22)
>
>
> Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH'  was
> refactored from the v6.7 mainline kernel, so it can't add the rule 
> correctly
> with older kernels.
>
> commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203
> Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> Date:   Thu Oct 26 09:47:46 2023 +0800
>
>     landlock: Refactor landlock_add_rule() syscall
>
> But this is my guess (through reading the code), I didn't do more to
> verify that by installing such a kernel.
>
>
> -- 
> Regards,
> Li Wang
Andrea Cervesato July 25, 2024, 7:12 a.m. UTC | #10
Hi!

it seems like the landlock() support in kernel 6.6 is different than the 
one in 6.7. The reason why we see that error in kernel <=6.6 is related 
to how landlock is handling the rules set according to the rule we want 
to enable.

Let's suppose we want to enable the execution for a file. What we should 
be able to do, is to consider __ALL__ the rules available for landlock, 
then to enable EXEC only for a specific file. Then, if we make any other 
operation that is not EXEC, we should have a permission error. This 
translates to:

- set ruleset_attr->handled_access_fs for all available 
LANDLOCK_ACCESS_FS_* rules
- set path_beneath_attr->allowed_access to LANDLOCK_ACCESS_FS_EXEC | 
LANDLOCK_ACCESS_FS_READ (we need to read in order to execute) for a binary
- enforce the rules inside a sandbox containing the binary
- execute the binary will work
- do any other operation inside the sandbox and obtain a permissions error
- at this point, any new rule that is added, will update the list of 
landlock rules, enabling the sandbox permissions

For some reasons that I don't know (and this is evident from kselftests 
as well), if the initial rules set (ruleset_attr->handled_access_fs) is 
not identical to the rules we are going to enable 
(path_beneath_attr->allowed_access), landlock_add_rule() will fail with 
EINVAL. And this is our case for all kernels <=6.6.

I really have no idea why this happens and maybe we need to contact the 
landlock developers.

Andrea

On 7/24/24 15:47, Andrea Cervesato wrote:
> Hi Li,
>
> thanks for checking. Mmmh I don't know if it's because they added 
> LANDLOCK_RULE_NET_PORT. It sounds strange to me, since that would 
> break all the other features.
>
> Andrea
>
> On 7/24/24 14:12, Li Wang wrote:
>> Hi Petr, Andrea,
>>
>> On Wed, Jul 17, 2024 at 1:27 AM Petr Vorel <pvorel@suse.cz> wrote:
>>
>>     Hi Andrea,
>>
>>     ...
>>     > +static void enable_exec_libs(const int ruleset_fd)
>>     > +{
>>     > +     FILE *fp;
>>     > +     char line[1024];
>>     > +     char path[PATH_MAX];
>>     > +     char dependency[8][PATH_MAX];
>>     > +     int count = 0;
>>     > +     int duplicate = 0;
>>     > +
>>     > +     fp = SAFE_FOPEN("/proc/self/maps", "r");
>>     > +
>>     > +     while (fgets(line, sizeof(line), fp)) {
>>     > +             if (strstr(line, ".so") == NULL)
>>     > +                     continue;
>>     > +
>>     > +             SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s",
>>     path);
>>     > +
>>     > +             for (int i = 0; i < count; i++) {
>>     > +                     if (strcmp(path, dependency[i]) == 0) {
>>     > +                             duplicate = 1;
>>     > +                             break;
>>     > +                     }
>>     > +             }
>>     > +
>>     > +             if (duplicate) {
>>     > +                     duplicate = 0;
>>     > +                     continue;
>>     > +             }
>>     > +
>>     > +             strncpy(dependency[count], path, PATH_MAX);
>>     > +             count++;
>>     > +
>>     > +             tst_res(TINFO, "Enable read/exec permissions for
>>     %s", path);
>>     > +
>>     > +             path_beneath_attr->allowed_access =
>>     > +                     LANDLOCK_ACCESS_FS_READ_FILE |
>>     > +                     LANDLOCK_ACCESS_FS_EXECUTE;
>>     > +             path_beneath_attr->parent_fd = SAFE_OPEN(path,
>>     O_PATH | O_CLOEXEC);
>>     > +
>>     > +             SAFE_LANDLOCK_ADD_RULE(
>>     > +                     ruleset_fd,
>>     > +                     LANDLOCK_RULE_PATH_BENEATH,
>>     > +                     path_beneath_attr,
>>     > +                     0);
>>
>>     Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it
>>     fails (after
>>     fresh boot) with:
>>
>>     ...
>>     tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
>>     tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
>>     tst_test.c:1746: TINFO: === Testing on ext2 ===
>>     tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts=''
>>     extra opts=''
>>     mke2fs 1.47.0 (5-Feb-2023)
>>     tst_test.c:1123: TINFO: Mounting /dev/loop1 to
>>     /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0
>>     landlock_common.h:30: TINFO: Landlock ABI v3
>>     landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
>>     landlock04.c:123: TINFO: Enable read/exec permissions for
>>     /usr/lib/i386-linux-gnu/libc.so.6
>>     landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0):
>>     EINVAL (22)
>>
>>
>> Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH'  was
>> refactored from the v6.7 mainline kernel, so it can't add the rule 
>> correctly
>> with older kernels.
>>
>> commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203
>> Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> Date:   Thu Oct 26 09:47:46 2023 +0800
>>
>>     landlock: Refactor landlock_add_rule() syscall
>>
>> But this is my guess (through reading the code), I didn't do more to
>> verify that by installing such a kernel.
>>
>>
>> -- 
>> Regards,
>> Li Wang
>
>
Mickaël Salaün July 25, 2024, 9:06 a.m. UTC | #11
Hi Andrea,

On Thu, Jul 25, 2024 at 09:50:36AM +0200, Andrea Cervesato wrote:
> Hi all,
> 
> we are facing an issue with landlock support in kernels <=6.6. We have a
> test that takes in consideration all possible rules set and enable only one
> of them, checking that all the others are raising a permission error.
> The test can be found here:
> https://github.com/acerv/ltp/commit/9b1d6838592cebe3c89282a7339db543be2a00e7

There is definitely an issue in this commit.  ALL_RULES contains all
access right, mixing filesystem and network ones, which doesn't make
sense because they don't have the same "namespace".
LANDLOCK_ACCESS_NET_* and LANDLOCK_ACCESS_FS_* bits overlaps.
The tester_get_all_rules() function does the ABI checks and removes the
LANDLOCK_ACCESS_NET_{BIND,CONNECT}_TCP bits if they are not supported
(in the handled_access_net namespace), which is the same as removing
LANDLOCK_ACCESS_FS_{EXECUTE,WRITE_FILE} whereas they are supported (in
the handled_access_fs namespace).

> It work fine for all kernels >= 6.7.

LANDLOCK_ACCESS_NET_{BIND,CONNECT}_TCP were introduced with this kernel
(Landlock ABI 4), so I guess that's the issue.

> 
> Below you will find the discussion in the LTP mailing list. Can you please
> give any help with this?
> 
> Regards,
> Andrea
> 
> 
> 
> -------- Forwarded Message --------
> Subject: 	Re: [LTP] [PATCH v3 09/11] Add landlock04 test
> Date: 	Thu, 25 Jul 2024 09:12:39 +0200
> From: 	Andrea Cervesato <andrea.cervesato@suse.com>
> To: 	Li Wang <liwang@redhat.com>, Petr Vorel <pvorel@suse.cz>
> CC: 	Andrea Cervesato <andrea.cervesato@suse.de>, ltp@lists.linux.it,
> Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> 
> 
> 
> Hi!
> 
> it seems like the landlock() support in kernel 6.6 is different than the one
> in 6.7. The reason why we see that error in kernel <=6.6 is related to how
> landlock is handling the rules set according to the rule we want to enable.
> 
> Let's suppose we want to enable the execution for a file. What we should be
> able to do, is to consider __ALL__ the rules available for landlock, then to
> enable EXEC only for a specific file. Then, if we make any other operation
> that is not EXEC, we should have a permission error. This translates to:
> 
> - set ruleset_attr->handled_access_fs for all available LANDLOCK_ACCESS_FS_*
> rules
> - set path_beneath_attr->allowed_access to LANDLOCK_ACCESS_FS_EXEC |
> LANDLOCK_ACCESS_FS_READ (we need to read in order to execute) for a binary
> - enforce the rules inside a sandbox containing the binary
> - execute the binary will work
> - do any other operation inside the sandbox and obtain a permissions error
> - at this point, any new rule that is added, will update the list of
> landlock rules, enabling the sandbox permissions
> 
> For some reasons that I don't know (and this is evident from kselftests as
> well), if the initial rules set (ruleset_attr->handled_access_fs) is not
> identical to the rules we are going to enable
> (path_beneath_attr->allowed_access), landlock_add_rule() will fail with
> EINVAL. And this is our case for all kernels <=6.6.

path_beneath_attr->allowed_access needs to be a subset of
ruleset_attr->handled_access_fs, otherwise it doesn't make sense to
allow something which is not handled/denied.

> 
> I really have no idea why this happens and maybe we need to contact the
> landlock developers.

Feel free to Cc me and Günther for all Landlock-related patches.

> 
> Andrea
> 
> On 7/24/24 15:47, Andrea Cervesato wrote:
> > Hi Li,
> > 
> > thanks for checking. Mmmh I don't know if it's because they added
> > LANDLOCK_RULE_NET_PORT. It sounds strange to me, since that would break
> > all the other features.
> > 
> > Andrea
> > 
> > On 7/24/24 14:12, Li Wang wrote:
> > > Hi Petr, Andrea,
> > > 
> > > On Wed, Jul 17, 2024 at 1:27 AM Petr Vorel <pvorel@suse.cz> wrote:
> > > 
> > >     Hi Andrea,
> > > 
> > >     ...
> > >     > +static void enable_exec_libs(const int ruleset_fd)
> > >     > +{
> > >     > +     FILE *fp;
> > >     > +     char line[1024];
> > >     > +     char path[PATH_MAX];
> > >     > +     char dependency[8][PATH_MAX];
> > >     > +     int count = 0;
> > >     > +     int duplicate = 0;
> > >     > +
> > >     > +     fp = SAFE_FOPEN("/proc/self/maps", "r");
> > >     > +
> > >     > +     while (fgets(line, sizeof(line), fp)) {
> > >     > +             if (strstr(line, ".so") == NULL)
> > >     > +                     continue;
> > >     > +
> > >     > +             SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s",
> > >     path);
> > >     > +
> > >     > +             for (int i = 0; i < count; i++) {
> > >     > +                     if (strcmp(path, dependency[i]) == 0) {
> > >     > +                             duplicate = 1;
> > >     > +                             break;
> > >     > +                     }
> > >     > +             }
> > >     > +
> > >     > +             if (duplicate) {
> > >     > +                     duplicate = 0;
> > >     > +                     continue;
> > >     > +             }
> > >     > +
> > >     > +             strncpy(dependency[count], path, PATH_MAX);
> > >     > +             count++;
> > >     > +
> > >     > +             tst_res(TINFO, "Enable read/exec permissions for
> > >     %s", path);
> > >     > +
> > >     > +             path_beneath_attr->allowed_access =
> > >     > +                     LANDLOCK_ACCESS_FS_READ_FILE |
> > >     > +                     LANDLOCK_ACCESS_FS_EXECUTE;
> > >     > +             path_beneath_attr->parent_fd = SAFE_OPEN(path,
> > >     O_PATH | O_CLOEXEC);
> > >     > +
> > >     > +             SAFE_LANDLOCK_ADD_RULE(
> > >     > +                     ruleset_fd,
> > >     > +                     LANDLOCK_RULE_PATH_BENEATH,
> > >     > +                     path_beneath_attr,
> > >     > +                     0);
> > > 
> > >     Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it
> > >     fails (after
> > >     fresh boot) with:
> > > 
> > >     ...
> > >     tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
> > >     tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
> > >     tst_test.c:1746: TINFO: === Testing on ext2 ===
> > >     tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts=''
> > >     extra opts=''
> > >     mke2fs 1.47.0 (5-Feb-2023)
> > >     tst_test.c:1123: TINFO: Mounting /dev/loop1 to
> > >     /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0
> > >     landlock_common.h:30: TINFO: Landlock ABI v3
> > >     landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
> > >     landlock04.c:123: TINFO: Enable read/exec permissions for
> > >     /usr/lib/i386-linux-gnu/libc.so.6
> > >     landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0):
> > >     EINVAL (22)
> > > 
> > > 
> > > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH'  was
> > > refactored from the v6.7 mainline kernel, so it can't add the rule
> > > correctly
> > > with older kernels.
> > > 
> > > commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203
> > > Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> > > Date:   Thu Oct 26 09:47:46 2023 +0800
> > > 
> > >     landlock: Refactor landlock_add_rule() syscall
> > > 
> > > But this is my guess (through reading the code), I didn't do more to
> > > verify that by installing such a kernel.
> > > 
> > > 
> > > -- 
> > > Regards,
> > > Li Wang
> > 
> > 
> 
>
Andrea Cervesato July 25, 2024, 9:17 a.m. UTC | #12
On 7/25/24 11:06, Mickaël Salaün wrote:
> Hi Andrea,
>
> On Thu, Jul 25, 2024 at 09:50:36AM +0200, Andrea Cervesato wrote:
>> Hi all,
>>
>> we are facing an issue with landlock support in kernels <=6.6. We have a
>> test that takes in consideration all possible rules set and enable only one
>> of them, checking that all the others are raising a permission error.
>> The test can be found here:
>> https://github.com/acerv/ltp/commit/9b1d6838592cebe3c89282a7339db543be2a00e7
> There is definitely an issue in this commit.  ALL_RULES contains all
> access right, mixing filesystem and network ones, which doesn't make
> sense because they don't have the same "namespace".
> LANDLOCK_ACCESS_NET_* and LANDLOCK_ACCESS_FS_* bits overlaps.
> The tester_get_all_rules() function does the ABI checks and removes the
> LANDLOCK_ACCESS_NET_{BIND,CONNECT}_TCP bits if they are not supported
> (in the handled_access_net namespace), which is the same as removing
> LANDLOCK_ACCESS_FS_{EXECUTE,WRITE_FILE} whereas they are supported (in
> the handled_access_fs namespace).

Thank you very much. Indeed, there was an error in the test itself that 
was mixing namespaces.
Now problem seems to be solved.

>> It work fine for all kernels >= 6.7.
> LANDLOCK_ACCESS_NET_{BIND,CONNECT}_TCP were introduced with this kernel
> (Landlock ABI 4), so I guess that's the issue.
>
>> Below you will find the discussion in the LTP mailing list. Can you please
>> give any help with this?
>>
>> Regards,
>> Andrea
>>
>>
>>
>> -------- Forwarded Message --------
>> Subject: 	Re: [LTP] [PATCH v3 09/11] Add landlock04 test
>> Date: 	Thu, 25 Jul 2024 09:12:39 +0200
>> From: 	Andrea Cervesato <andrea.cervesato@suse.com>
>> To: 	Li Wang <liwang@redhat.com>, Petr Vorel <pvorel@suse.cz>
>> CC: 	Andrea Cervesato <andrea.cervesato@suse.de>, ltp@lists.linux.it,
>> Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>
>>
>>
>> Hi!
>>
>> it seems like the landlock() support in kernel 6.6 is different than the one
>> in 6.7. The reason why we see that error in kernel <=6.6 is related to how
>> landlock is handling the rules set according to the rule we want to enable.
>>
>> Let's suppose we want to enable the execution for a file. What we should be
>> able to do, is to consider __ALL__ the rules available for landlock, then to
>> enable EXEC only for a specific file. Then, if we make any other operation
>> that is not EXEC, we should have a permission error. This translates to:
>>
>> - set ruleset_attr->handled_access_fs for all available LANDLOCK_ACCESS_FS_*
>> rules
>> - set path_beneath_attr->allowed_access to LANDLOCK_ACCESS_FS_EXEC |
>> LANDLOCK_ACCESS_FS_READ (we need to read in order to execute) for a binary
>> - enforce the rules inside a sandbox containing the binary
>> - execute the binary will work
>> - do any other operation inside the sandbox and obtain a permissions error
>> - at this point, any new rule that is added, will update the list of
>> landlock rules, enabling the sandbox permissions
>>
>> For some reasons that I don't know (and this is evident from kselftests as
>> well), if the initial rules set (ruleset_attr->handled_access_fs) is not
>> identical to the rules we are going to enable
>> (path_beneath_attr->allowed_access), landlock_add_rule() will fail with
>> EINVAL. And this is our case for all kernels <=6.6.
> path_beneath_attr->allowed_access needs to be a subset of
> ruleset_attr->handled_access_fs, otherwise it doesn't make sense to
> allow something which is not handled/denied.
>
>> I really have no idea why this happens and maybe we need to contact the
>> landlock developers.
> Feel free to Cc me and Günther for all Landlock-related patches.
Sure.
>> Andrea
>>
>> On 7/24/24 15:47, Andrea Cervesato wrote:
>>> Hi Li,
>>>
>>> thanks for checking. Mmmh I don't know if it's because they added
>>> LANDLOCK_RULE_NET_PORT. It sounds strange to me, since that would break
>>> all the other features.
>>>
>>> Andrea
>>>
>>> On 7/24/24 14:12, Li Wang wrote:
>>>> Hi Petr, Andrea,
>>>>
>>>> On Wed, Jul 17, 2024 at 1:27 AM Petr Vorel <pvorel@suse.cz> wrote:
>>>>
>>>>      Hi Andrea,
>>>>
>>>>      ...
>>>>      > +static void enable_exec_libs(const int ruleset_fd)
>>>>      > +{
>>>>      > +     FILE *fp;
>>>>      > +     char line[1024];
>>>>      > +     char path[PATH_MAX];
>>>>      > +     char dependency[8][PATH_MAX];
>>>>      > +     int count = 0;
>>>>      > +     int duplicate = 0;
>>>>      > +
>>>>      > +     fp = SAFE_FOPEN("/proc/self/maps", "r");
>>>>      > +
>>>>      > +     while (fgets(line, sizeof(line), fp)) {
>>>>      > +             if (strstr(line, ".so") == NULL)
>>>>      > +                     continue;
>>>>      > +
>>>>      > +             SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s",
>>>>      path);
>>>>      > +
>>>>      > +             for (int i = 0; i < count; i++) {
>>>>      > +                     if (strcmp(path, dependency[i]) == 0) {
>>>>      > +                             duplicate = 1;
>>>>      > +                             break;
>>>>      > +                     }
>>>>      > +             }
>>>>      > +
>>>>      > +             if (duplicate) {
>>>>      > +                     duplicate = 0;
>>>>      > +                     continue;
>>>>      > +             }
>>>>      > +
>>>>      > +             strncpy(dependency[count], path, PATH_MAX);
>>>>      > +             count++;
>>>>      > +
>>>>      > +             tst_res(TINFO, "Enable read/exec permissions for
>>>>      %s", path);
>>>>      > +
>>>>      > +             path_beneath_attr->allowed_access =
>>>>      > +                     LANDLOCK_ACCESS_FS_READ_FILE |
>>>>      > +                     LANDLOCK_ACCESS_FS_EXECUTE;
>>>>      > +             path_beneath_attr->parent_fd = SAFE_OPEN(path,
>>>>      O_PATH | O_CLOEXEC);
>>>>      > +
>>>>      > +             SAFE_LANDLOCK_ADD_RULE(
>>>>      > +                     ruleset_fd,
>>>>      > +                     LANDLOCK_RULE_PATH_BENEATH,
>>>>      > +                     path_beneath_attr,
>>>>      > +                     0);
>>>>
>>>>      Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it
>>>>      fails (after
>>>>      fresh boot) with:
>>>>
>>>>      ...
>>>>      tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs
>>>>      tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs
>>>>      tst_test.c:1746: TINFO: === Testing on ext2 ===
>>>>      tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts=''
>>>>      extra opts=''
>>>>      mke2fs 1.47.0 (5-Feb-2023)
>>>>      tst_test.c:1123: TINFO: Mounting /dev/loop1 to
>>>>      /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0
>>>>      landlock_common.h:30: TINFO: Landlock ABI v3
>>>>      landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE
>>>>      landlock04.c:123: TINFO: Enable read/exec permissions for
>>>>      /usr/lib/i386-linux-gnu/libc.so.6
>>>>      landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0):
>>>>      EINVAL (22)
>>>>
>>>>
>>>> Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH'  was
>>>> refactored from the v6.7 mainline kernel, so it can't add the rule
>>>> correctly
>>>> with older kernels.
>>>>
>>>> commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203
>>>> Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>>> Date:   Thu Oct 26 09:47:46 2023 +0800
>>>>
>>>>      landlock: Refactor landlock_add_rule() syscall
>>>>
>>>> But this is my guess (through reading the code), I didn't do more to
>>>> verify that by installing such a kernel.
>>>>
>>>>
>>>> -- 
>>>> Regards,
>>>> Li Wang
>>>
>>
Regards,
Andrea
diff mbox series

Patch

diff --git a/runtest/syscalls b/runtest/syscalls
index f2b64c0df..3c7cd66e2 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -688,6 +688,7 @@  kill13 kill13
 landlock01 landlock01
 landlock02 landlock02
 landlock03 landlock03
+landlock04 landlock04
 
 lchown01 lchown01
 lchown01_16 lchown01_16
diff --git a/testcases/kernel/syscalls/landlock/.gitignore b/testcases/kernel/syscalls/landlock/.gitignore
index f79cd090b..4fe8d7cba 100644
--- a/testcases/kernel/syscalls/landlock/.gitignore
+++ b/testcases/kernel/syscalls/landlock/.gitignore
@@ -1,3 +1,5 @@ 
+landlock_exec
 landlock01
 landlock02
 landlock03
+landlock04
diff --git a/testcases/kernel/syscalls/landlock/landlock04.c b/testcases/kernel/syscalls/landlock/landlock04.c
new file mode 100644
index 000000000..30fd9400f
--- /dev/null
+++ b/testcases/kernel/syscalls/landlock/landlock04.c
@@ -0,0 +1,214 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * [Description]
+ *
+ * This test verifies that all landlock rules are working properly. The way we
+ * do it is to verify that all disabled syscalls are not working but the one we
+ * enabled via specifc landlock rules.
+ */
+
+#include "landlock_common.h"
+#include "landlock_tester.h"
+#include "tst_safe_stdio.h"
+
+#define ACCESS_NAME(x) #x
+
+static struct landlock_ruleset_attr *ruleset_attr;
+static struct landlock_path_beneath_attr *path_beneath_attr;
+
+static struct tvariant {
+	int access;
+	char *desc;
+} tvariants[] = {
+	{
+		LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_EXECUTE,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_EXECUTE)
+	},
+	{
+		LANDLOCK_ACCESS_FS_WRITE_FILE,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_WRITE_FILE)
+	},
+	{
+		LANDLOCK_ACCESS_FS_READ_FILE,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_READ_FILE)
+	},
+	{
+		LANDLOCK_ACCESS_FS_READ_DIR,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_READ_DIR)
+	},
+	{
+		LANDLOCK_ACCESS_FS_REMOVE_DIR,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_REMOVE_DIR)
+	},
+	{
+		LANDLOCK_ACCESS_FS_REMOVE_FILE,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_REMOVE_FILE)
+	},
+	{
+		LANDLOCK_ACCESS_FS_MAKE_CHAR,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_CHAR)
+	},
+	{
+		LANDLOCK_ACCESS_FS_MAKE_BLOCK,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_BLOCK)
+	},
+	{
+		LANDLOCK_ACCESS_FS_MAKE_REG,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_REG)
+	},
+	{
+		LANDLOCK_ACCESS_FS_MAKE_SOCK,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_SOCK)
+	},
+	{
+		LANDLOCK_ACCESS_FS_MAKE_FIFO,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_FIFO)
+	},
+	{
+		LANDLOCK_ACCESS_FS_MAKE_SYM,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_SYM)
+	},
+	{
+		LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
+		ACCESS_NAME(LANDLOCK_ACCESS_FS_TRUNCATE)
+	},
+};
+
+static void run(void)
+{
+	if (!SAFE_FORK()) {
+		struct tvariant  variant = tvariants[tst_variant];
+
+		tester_run_all_rules(variant.access);
+		_exit(0);
+	}
+}
+
+static void enable_exec_libs(const int ruleset_fd)
+{
+	FILE *fp;
+	char line[1024];
+	char path[PATH_MAX];
+	char dependency[8][PATH_MAX];
+	int count = 0;
+	int duplicate = 0;
+
+	fp = SAFE_FOPEN("/proc/self/maps", "r");
+
+	while (fgets(line, sizeof(line), fp)) {
+		if (strstr(line, ".so") == NULL)
+			continue;
+
+		SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path);
+
+		for (int i = 0; i < count; i++) {
+			if (strcmp(path, dependency[i]) == 0) {
+				duplicate = 1;
+				break;
+			}
+		}
+
+		if (duplicate) {
+			duplicate = 0;
+			continue;
+		}
+
+		strncpy(dependency[count], path, PATH_MAX);
+		count++;
+
+		tst_res(TINFO, "Enable read/exec permissions for %s", path);
+
+		path_beneath_attr->allowed_access =
+			LANDLOCK_ACCESS_FS_READ_FILE |
+			LANDLOCK_ACCESS_FS_EXECUTE;
+		path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | O_CLOEXEC);
+
+		SAFE_LANDLOCK_ADD_RULE(
+			ruleset_fd,
+			LANDLOCK_RULE_PATH_BENEATH,
+			path_beneath_attr,
+			0);
+
+		SAFE_CLOSE(path_beneath_attr->parent_fd);
+	}
+
+	SAFE_FCLOSE(fp);
+}
+
+static void setup(void)
+{
+	struct tvariant variant = tvariants[tst_variant];
+	int ruleset_fd;
+
+	verify_landlock_is_enabled();
+	tester_create_tree();
+
+	tst_res(TINFO, "Testing %s", variant.desc);
+
+	ruleset_attr->handled_access_fs = tester_get_all_rules();
+
+	ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET(
+		ruleset_attr, sizeof(struct landlock_ruleset_attr), 0);
+
+	/* since our binary is dynamically linked, we need to enable dependences
+	 * to be read and executed
+	 */
+	enable_exec_libs(ruleset_fd);
+
+	path_beneath_attr->allowed_access = variant.access;
+	path_beneath_attr->parent_fd = SAFE_OPEN(
+		SANDBOX_FOLDER, O_PATH | O_CLOEXEC);
+
+	SAFE_LANDLOCK_ADD_RULE(
+		ruleset_fd,
+		LANDLOCK_RULE_PATH_BENEATH,
+		path_beneath_attr,
+		0);
+
+	SAFE_CLOSE(path_beneath_attr->parent_fd);
+
+	enforce_ruleset(ruleset_fd);
+	SAFE_CLOSE(ruleset_fd);
+}
+
+static struct tst_test test = {
+	.test_all = run,
+	.setup = setup,
+	.min_kver = "5.13",
+	.forks_child = 1,
+	.needs_tmpdir = 1,
+	.needs_root = 1,
+	.test_variants = ARRAY_SIZE(tvariants),
+	.resource_files = (const char *[]) {
+		TESTAPP,
+		NULL,
+	},
+	.needs_kconfigs = (const char *[]) {
+		"CONFIG_SECURITY_LANDLOCK=y",
+		NULL
+	},
+	.bufs = (struct tst_buffers []) {
+		{&ruleset_attr, .size = sizeof(struct landlock_ruleset_attr)},
+		{&path_beneath_attr, .size = sizeof(struct landlock_path_beneath_attr)},
+		{},
+	},
+	.caps = (struct tst_cap []) {
+		TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN),
+		TST_CAP(TST_CAP_REQ, CAP_MKNOD),
+		{}
+	},
+	.format_device = 1,
+	.mount_device = 1,
+	.mntpoint = SANDBOX_FOLDER,
+	.all_filesystems = 1,
+	.skip_filesystems = (const char *[]) {
+		"vfat",
+		"exfat",
+		NULL
+	},
+	.max_runtime = 3600,
+};
diff --git a/testcases/kernel/syscalls/landlock/landlock_exec.c b/testcases/kernel/syscalls/landlock/landlock_exec.c
new file mode 100644
index 000000000..aae5c76b2
--- /dev/null
+++ b/testcases/kernel/syscalls/landlock/landlock_exec.c
@@ -0,0 +1,9 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+int main(void)
+{
+	return 0;
+}
diff --git a/testcases/kernel/syscalls/landlock/landlock_tester.h b/testcases/kernel/syscalls/landlock/landlock_tester.h
new file mode 100644
index 000000000..89ca085d7
--- /dev/null
+++ b/testcases/kernel/syscalls/landlock/landlock_tester.h
@@ -0,0 +1,350 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+#ifndef LANDLOCK_TESTER_H
+
+#include "tst_test.h"
+#include "lapi/landlock.h"
+#include <sys/sysmacros.h>
+
+#define PERM_MODE 0700
+
+#define SANDBOX_FOLDER	"sandbox"
+#define TESTAPP			"landlock_exec"
+
+#define FILE_EXEC		SANDBOX_FOLDER"/"TESTAPP
+#define FILE_READ		SANDBOX_FOLDER"/file_read"
+#define FILE_WRITE		SANDBOX_FOLDER"/file_write"
+#define FILE_REMOVE		SANDBOX_FOLDER"/file_remove"
+#define FILE_UNLINK		SANDBOX_FOLDER"/file_unlink"
+#define FILE_UNLINKAT	SANDBOX_FOLDER"/file_unlinkat"
+#define FILE_TRUNCATE	SANDBOX_FOLDER"/file_truncate"
+#define FILE_REGULAR	SANDBOX_FOLDER"/regular0"
+#define FILE_SOCKET		SANDBOX_FOLDER"/socket0"
+#define FILE_FIFO		SANDBOX_FOLDER"/fifo0"
+#define FILE_SYM0		SANDBOX_FOLDER"/symbolic0"
+#define FILE_SYM1		SANDBOX_FOLDER"/symbolic1"
+#define DIR_READDIR		SANDBOX_FOLDER"/dir_readdir"
+#define DIR_RMDIR		SANDBOX_FOLDER"/dir_rmdir"
+#define DEV_CHAR0		SANDBOX_FOLDER"/chardev0"
+#define DEV_BLK0		SANDBOX_FOLDER"/blkdev0"
+
+#define ALL_RULES (\
+	LANDLOCK_ACCESS_FS_EXECUTE | \
+	LANDLOCK_ACCESS_FS_WRITE_FILE | \
+	LANDLOCK_ACCESS_FS_READ_FILE | \
+	LANDLOCK_ACCESS_FS_READ_DIR | \
+	LANDLOCK_ACCESS_FS_REMOVE_DIR | \
+	LANDLOCK_ACCESS_FS_REMOVE_FILE | \
+	LANDLOCK_ACCESS_FS_MAKE_CHAR | \
+	LANDLOCK_ACCESS_FS_MAKE_DIR | \
+	LANDLOCK_ACCESS_FS_MAKE_REG | \
+	LANDLOCK_ACCESS_FS_MAKE_SOCK | \
+	LANDLOCK_ACCESS_FS_MAKE_FIFO | \
+	LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
+	LANDLOCK_ACCESS_FS_MAKE_SYM | \
+	LANDLOCK_ACCESS_FS_REFER | \
+	LANDLOCK_ACCESS_FS_TRUNCATE | \
+	LANDLOCK_ACCESS_NET_BIND_TCP | \
+	LANDLOCK_ACCESS_NET_CONNECT_TCP | \
+	LANDLOCK_ACCESS_FS_IOCTL_DEV)
+
+static char *readdir_files[] = {
+	DIR_READDIR"/file0",
+	DIR_READDIR"/file1",
+	DIR_READDIR"/file2",
+};
+
+static int dev_chr;
+static int dev_blk;
+
+static int tester_get_all_rules(void)
+{
+	int abi;
+	int all_rules = ALL_RULES;
+
+	abi = SAFE_LANDLOCK_CREATE_RULESET(
+		NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
+
+	if (abi < 2)
+		all_rules &= ~LANDLOCK_ACCESS_FS_REFER;
+
+	if (abi < 3)
+		all_rules &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
+
+	if (abi < 4) {
+		all_rules &= ~(LANDLOCK_ACCESS_NET_BIND_TCP |
+			LANDLOCK_ACCESS_NET_CONNECT_TCP);
+	}
+
+	if (abi < 5)
+		all_rules &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
+
+	return all_rules;
+}
+
+static void tester_create_tree(void)
+{
+	if (access(SANDBOX_FOLDER, F_OK) == -1)
+		SAFE_MKDIR(SANDBOX_FOLDER, PERM_MODE);
+
+	/* folders */
+	SAFE_MKDIR(DIR_RMDIR, PERM_MODE);
+	SAFE_MKDIR(DIR_READDIR, PERM_MODE);
+	for (size_t i = 0; i < ARRAY_SIZE(readdir_files); i++)
+		SAFE_TOUCH(readdir_files[i], PERM_MODE, NULL);
+
+	/* files */
+	tst_fill_file(FILE_READ, 'a', getpagesize(), 1);
+	SAFE_TOUCH(FILE_WRITE, PERM_MODE, NULL);
+	SAFE_TOUCH(FILE_REMOVE, PERM_MODE, NULL);
+	SAFE_TOUCH(FILE_UNLINK, PERM_MODE, NULL);
+	SAFE_TOUCH(FILE_UNLINKAT, PERM_MODE, NULL);
+	SAFE_TOUCH(FILE_TRUNCATE, PERM_MODE, NULL);
+	SAFE_TOUCH(FILE_SYM0, PERM_MODE, NULL);
+	SAFE_CP(TESTAPP, FILE_EXEC);
+
+	/* devices */
+	dev_chr = makedev(1, 3);
+	dev_blk = makedev(7, 0);
+}
+
+static void _test_exec(const int result)
+{
+	int status;
+	pid_t pid;
+	char *const args[] = {(char *)FILE_EXEC, NULL};
+
+	tst_res(TINFO, "Test binary execution");
+
+	pid = SAFE_FORK();
+	if (!pid) {
+		int rval;
+
+		if (result == TPASS) {
+			rval = execve(FILE_EXEC, args, NULL);
+			if (rval == -1)
+				tst_res(TFAIL | TERRNO, "Failed to execute test binary");
+		} else {
+			TST_EXP_FAIL(execve(FILE_EXEC, args, NULL), EACCES);
+		}
+
+		_exit(1);
+	}
+
+	SAFE_WAITPID(pid, &status, 0);
+	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+		return;
+
+	tst_res(result, "Test binary has been executed");
+}
+
+static void _test_write(const int result)
+{
+	tst_res(TINFO, "Test writing file");
+
+	if (result == TPASS)
+		TST_EXP_FD(open(FILE_WRITE, O_WRONLY, PERM_MODE));
+	else
+		TST_EXP_FAIL(open(FILE_WRITE, O_WRONLY, PERM_MODE), EACCES);
+
+	if (TST_RET != -1)
+		SAFE_CLOSE(TST_RET);
+}
+
+static void _test_read(const int result)
+{
+	tst_res(TINFO, "Test reading file");
+
+	if (result == TPASS)
+		TST_EXP_FD(open(FILE_READ, O_RDONLY, PERM_MODE));
+	else
+		TST_EXP_FAIL(open(FILE_READ, O_RDONLY, PERM_MODE), EACCES);
+
+	if (TST_RET != -1)
+		SAFE_CLOSE(TST_RET);
+}
+
+static void _test_readdir(const int result)
+{
+	tst_res(TINFO, "Test reading directory");
+
+	DIR *dir;
+	struct dirent *de;
+	int files_counted = 0;
+
+	dir = opendir(DIR_READDIR);
+	if (!dir) {
+		tst_res(result == TPASS ? TFAIL : TPASS,
+			"Can't read '%s' directory", DIR_READDIR);
+
+		return;
+	}
+
+	tst_res(result, "Can read '%s' directory", DIR_READDIR);
+	if (result == TFAIL)
+		return;
+
+	while ((de = readdir(dir)) != NULL) {
+		if (de->d_type != DT_REG)
+			continue;
+
+		for (size_t i = 0; i < ARRAY_SIZE(readdir_files); i++) {
+			if (readdir_files[i] == NULL)
+				continue;
+
+			if (strstr(readdir_files[i], de->d_name) != NULL)
+				files_counted++;
+		}
+	}
+
+	SAFE_CLOSEDIR(dir);
+
+	TST_EXP_EQ_LI(files_counted, ARRAY_SIZE(readdir_files));
+}
+
+static void _test_rmdir(const int result)
+{
+	tst_res(TINFO, "Test removing directory");
+
+	if (result == TPASS)
+		TST_EXP_PASS(rmdir(DIR_RMDIR));
+	else
+		TST_EXP_FAIL(rmdir(DIR_RMDIR), EACCES);
+}
+
+static void _test_rmfile(const int result)
+{
+	tst_res(TINFO, "Test removing file");
+
+	if (result == TPASS) {
+		TST_EXP_PASS(unlink(FILE_UNLINK));
+		TST_EXP_PASS(remove(FILE_REMOVE));
+	} else {
+		TST_EXP_FAIL(unlink(FILE_UNLINK), EACCES);
+		TST_EXP_FAIL(remove(FILE_REMOVE), EACCES);
+	}
+}
+
+static void _test_make(
+	const char *path,
+	const int type,
+	const int dev,
+	const int result)
+{
+	tst_res(TINFO, "Test normal or special files creation");
+
+	if (result == TPASS)
+		TST_EXP_PASS(mknod(path, type | 0400, dev));
+	else
+		TST_EXP_FAIL(mknod(path, type | 0400, dev), EACCES);
+}
+
+static void _test_symbolic(const int result)
+{
+	tst_res(TINFO, "Test symbolic links");
+
+	if (result == TPASS)
+		TST_EXP_PASS(symlink(FILE_SYM0, FILE_SYM1));
+	else
+		TST_EXP_FAIL(symlink(FILE_SYM0, FILE_SYM1), EACCES);
+}
+
+static void _test_truncate(const int result)
+{
+	int fd;
+
+	tst_res(TINFO, "Test truncating file");
+
+	if (result == TPASS) {
+		TST_EXP_PASS(truncate(FILE_TRUNCATE, 10));
+
+		fd = TST_EXP_FD(open(FILE_TRUNCATE, O_WRONLY, PERM_MODE));
+		if (fd != -1) {
+			TST_EXP_PASS(ftruncate(fd, 10));
+			SAFE_CLOSE(fd);
+		}
+
+		fd = TST_EXP_FD(open(FILE_TRUNCATE, O_WRONLY | O_TRUNC, PERM_MODE));
+		if (fd != -1)
+			SAFE_CLOSE(fd);
+	} else {
+		TST_EXP_FAIL(truncate(FILE_TRUNCATE, 10), EACCES);
+
+		fd = open(FILE_TRUNCATE, O_WRONLY, PERM_MODE);
+		if (fd != -1) {
+			TST_EXP_FAIL(ftruncate(fd, 10), EACCES);
+			SAFE_CLOSE(fd);
+		}
+
+		TST_EXP_FAIL(open(FILE_TRUNCATE, O_WRONLY | O_TRUNC, PERM_MODE),
+			EACCES);
+
+		if (TST_RET != -1)
+			SAFE_CLOSE(TST_RET);
+	}
+}
+
+static void tester_run_rules(const int rules, const int result)
+{
+	if (rules & LANDLOCK_ACCESS_FS_EXECUTE)
+		_test_exec(result);
+
+	if (rules & LANDLOCK_ACCESS_FS_WRITE_FILE)
+		_test_write(result);
+
+	if (rules & LANDLOCK_ACCESS_FS_READ_FILE)
+		_test_read(result);
+
+	if (rules & LANDLOCK_ACCESS_FS_READ_DIR)
+		_test_readdir(result);
+
+	if (rules & LANDLOCK_ACCESS_FS_REMOVE_DIR)
+		_test_rmdir(result);
+
+	if (rules & LANDLOCK_ACCESS_FS_REMOVE_FILE)
+		_test_rmfile(result);
+
+	if (rules & LANDLOCK_ACCESS_FS_MAKE_CHAR)
+		_test_make(DEV_CHAR0, S_IFCHR, dev_chr, result);
+
+	if (rules & LANDLOCK_ACCESS_FS_MAKE_BLOCK)
+		_test_make(DEV_BLK0, S_IFBLK, dev_blk, result);
+
+	if (rules & LANDLOCK_ACCESS_FS_MAKE_REG)
+		_test_make(FILE_REGULAR, S_IFREG, 0, result);
+
+	if (rules & LANDLOCK_ACCESS_FS_MAKE_SOCK)
+		_test_make(FILE_SOCKET, S_IFSOCK, 0, result);
+
+	if (rules & LANDLOCK_ACCESS_FS_MAKE_FIFO)
+		_test_make(FILE_FIFO, S_IFIFO, 0, result);
+
+	if (rules & LANDLOCK_ACCESS_FS_MAKE_SYM)
+		_test_symbolic(result);
+
+	if (rules & LANDLOCK_ACCESS_FS_TRUNCATE) {
+		if ((tst_kvercmp(6, 2, 0)) < 0) {
+			tst_res(TINFO, "Skip truncate test. Minimum kernel version is 6.2");
+			return;
+		}
+
+		_test_truncate(result);
+	}
+}
+
+static inline void tester_run_all_rules(const int pass_rules)
+{
+	int fail_rules;
+	int all_rules;
+
+	all_rules = tester_get_all_rules();
+	fail_rules = all_rules & ~pass_rules;
+
+	tester_run_rules(pass_rules, TPASS);
+	tester_run_rules(fail_rules, TFAIL);
+}
+
+#endif