diff mbox series

[08/10] Add landlock04 test

Message ID 20240701-landlock-v1-8-58e9af649a72@suse.com
State Superseded
Headers show
Series landlock testing suite | expand

Commit Message

Andrea Cervesato July 1, 2024, 3:42 p.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/Makefile        |   5 +
 testcases/kernel/syscalls/landlock/landlock04.c    | 143 +++++++++
 testcases/kernel/syscalls/landlock/landlock_exec.c |   9 +
 .../kernel/syscalls/landlock/landlock_tester.h     | 350 +++++++++++++++++++++
 6 files changed, 510 insertions(+)

Comments

Li Wang July 2, 2024, 8 a.m. UTC | #1
On Mon, Jul 1, 2024 at 11:44 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/Makefile        |   5 +
>  testcases/kernel/syscalls/landlock/landlock04.c    | 143 +++++++++
>  testcases/kernel/syscalls/landlock/landlock_exec.c |   9 +
>  .../kernel/syscalls/landlock/landlock_tester.h     | 350
> +++++++++++++++++++++
>  6 files changed, 510 insertions(+)
>
> diff --git a/runtest/syscalls b/runtest/syscalls
> index 1e2d682e3..9acdaf760 100644
> --- a/runtest/syscalls
> +++ b/runtest/syscalls
> @@ -687,6 +687,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/Makefile
> b/testcases/kernel/syscalls/landlock/Makefile
> index 4b3e3fd8f..bdc6bd2d4 100644
> --- a/testcases/kernel/syscalls/landlock/Makefile
> +++ b/testcases/kernel/syscalls/landlock/Makefile
> @@ -8,3 +8,8 @@ include $(top_srcdir)/include/mk/testcases.mk
>  LDLIBS += -lc
>
>  include $(top_srcdir)/include/mk/generic_leaf_target.mk
> +
> +# the reason why landlock_exec test binary is statically linked, is that
> +# we can't read libc out of the sandboxed folder once
> LANDLOCK_ACCESS_FS_EXECUTE
> +# has been activated
> +landlock_exec: LDLIBS += -static -fPIC
> diff --git a/testcases/kernel/syscalls/landlock/landlock04.c
> b/testcases/kernel/syscalls/landlock/landlock04.c
> new file mode 100644
> index 000000000..1e7c6f3d1
> --- /dev/null
> +++ b/testcases/kernel/syscalls/landlock/landlock04.c
> @@ -0,0 +1,143 @@
> +// 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"
> +
> +#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 setup(void)
> +{
> +       struct tvariant variant = tvariants[tst_variant];
> +
> +       verify_landlock_is_enabled();
> +       tester_create_tree();
> +
> +       tst_res(TINFO, "Testing %s", variant.desc);
> +
> +       ruleset_attr->handled_access_fs = tester_get_all_rules();
> +
> +       apply_landlock_layer(
> +               ruleset_attr,
> +               path_beneath_attr,
> +               SANDBOX_FOLDER,
> +               variant.access);
> +}
> +
> +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),
>

We have to define CAP_MKNOD in the include/lapi/capability.h,
otherwise it can't be built on some platform.

landlock04.c:131:38: error: ‘CAP_MKNOD’ undeclared here (not in a
function); did you mean ‘SAFE_MKNOD’?
  131 |                 TST_CAP(TST_CAP_REQ, CAP_MKNOD),
Li Wang July 2, 2024, 12:22 p.m. UTC | #2
On Tue, Jul 2, 2024 at 4:00 PM Li Wang <liwang@redhat.com> wrote:

>
>
> On Mon, Jul 1, 2024 at 11:44 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/Makefile        |   5 +
>>  testcases/kernel/syscalls/landlock/landlock04.c    | 143 +++++++++
>>  testcases/kernel/syscalls/landlock/landlock_exec.c |   9 +
>>  .../kernel/syscalls/landlock/landlock_tester.h     | 350
>> +++++++++++++++++++++
>>  6 files changed, 510 insertions(+)
>>
>> diff --git a/runtest/syscalls b/runtest/syscalls
>> index 1e2d682e3..9acdaf760 100644
>> --- a/runtest/syscalls
>> +++ b/runtest/syscalls
>> @@ -687,6 +687,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/Makefile
>> b/testcases/kernel/syscalls/landlock/Makefile
>> index 4b3e3fd8f..bdc6bd2d4 100644
>> --- a/testcases/kernel/syscalls/landlock/Makefile
>> +++ b/testcases/kernel/syscalls/landlock/Makefile
>> @@ -8,3 +8,8 @@ include $(top_srcdir)/include/mk/testcases.mk
>>  LDLIBS += -lc
>>
>>  include $(top_srcdir)/include/mk/generic_leaf_target.mk
>> +
>> +# the reason why landlock_exec test binary is statically linked, is that
>> +# we can't read libc out of the sandboxed folder once
>> LANDLOCK_ACCESS_FS_EXECUTE
>> +# has been activated
>> +landlock_exec: LDLIBS += -static -fPIC
>>
>
And, if we decided to build the program as static, we'd better
adding glibc-static into the dependency list, otherwise it always
failed to link the binary on RHEL distributions.

--- a/ci/fedora.sh
+++ b/ci/fedora.sh
@@ -17,6 +17,7 @@ $yum \
        numactl-devel \
        libtirpc \
        libtirpc-devel \
+       libc-static \
        perl-JSON \
        perl-libwww-perl \
        pkg-config \
Li Wang July 3, 2024, 8:20 a.m. UTC | #3
I got some failures when this test was performed with exfat FS, but haven't
figured out the reason, FYI:

# uname -r
6.9.6-200.fc40.aarch64

# LTP_SINGLE_FS_TYPE=exfat ./landlock04
...
tst_test.c:1694: TINFO: === Testing on exfat ===
tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra
opts=''
tst_test.c:1120: TINFO: Mounting /dev/loop0 to /tmp/LTP_lantEvE2G/sandbox
fstyp=exfat flags=0
landlock_common.h:30: TINFO: Landlock ABI v4
landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_CHAR
landlock_tester.h:237: TINFO: Test normal or special files creation
landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: EPERM
(1)
landlock_tester.h:120: TINFO: Test binary execution
landlock_tester.h:131: TPASS: execve(FILE_EXEC, args, NULL) : EACCES (13)
landlock_tester.h:146: TINFO: Test writing file
landlock_tester.h:151: TPASS: open(FILE_WRITE, O_WRONLY, PERM_MODE) :
EACCES (13)
landlock_tester.h:159: TINFO: Test reading file
...
tst_test.c:1694: TINFO: === Testing on exfat ===
tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra
opts=''
tst_test.c:1120: TINFO: Mounting /dev/loop0 to /tmp/LTP_lantEvE2G/sandbox
fstyp=exfat flags=0
landlock_common.h:30: TINFO: Landlock ABI v4
landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_BLOCK
landlock_tester.h:237: TINFO: Test normal or special files creation
landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: EPERM
(1)
...
tst_test.c:1694: TINFO: === Testing on exfat ===
tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra
opts=''
tst_test.c:1120: TINFO: Mounting /dev/loop0 to /tmp/LTP_lantEvE2G/sandbox
fstyp=exfat flags=0
landlock_common.h:30: TINFO: Landlock ABI v4
landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_SOCK
landlock_tester.h:237: TINFO: Test normal or special files creation
landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: EPERM
(1)
...


Regard,
Li Wang
Andrea Cervesato July 3, 2024, 9:22 a.m. UTC | #4
Hi Li,

thanks for catching it. I had to install exfat in my VM in order to test it.
Indeed, I think exfat doesn't support char/block/sock devices, since its 
focus is mainly SD cards and USB drives.

We can easily skip that specific FS from the test, as I already done for 
vfat.

Andrea

On 7/3/24 10:20, Li Wang wrote:
>
> I got some failures when this test was performed with exfat FS, but 
> haven't figured out the reason, FYI:
>
> # uname -r
> 6.9.6-200.fc40.aarch64
>
> # LTP_SINGLE_FS_TYPE=exfat ./landlock04
> ...
> tst_test.c:1694: TINFO: === Testing on exfat ===
> tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra 
> opts=''
> tst_test.c:1120: TINFO: Mounting /dev/loop0 to 
> /tmp/LTP_lantEvE2G/sandbox fstyp=exfat flags=0
> landlock_common.h:30: TINFO: Landlock ABI v4
> landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_CHAR
> landlock_tester.h:237: TINFO: Test normal or special files creation
> landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: 
> EPERM (1)
> landlock_tester.h:120: TINFO: Test binary execution
> landlock_tester.h:131: TPASS: execve(FILE_EXEC, args, NULL) : EACCES (13)
> landlock_tester.h:146: TINFO: Test writing file
> landlock_tester.h:151: TPASS: open(FILE_WRITE, O_WRONLY, PERM_MODE) : 
> EACCES (13)
> landlock_tester.h:159: TINFO: Test reading file
> ...
> tst_test.c:1694: TINFO: === Testing on exfat ===
> tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra 
> opts=''
> tst_test.c:1120: TINFO: Mounting /dev/loop0 to 
> /tmp/LTP_lantEvE2G/sandbox fstyp=exfat flags=0
> landlock_common.h:30: TINFO: Landlock ABI v4
> landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_BLOCK
> landlock_tester.h:237: TINFO: Test normal or special files creation
> landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: 
> EPERM (1)
> ...
> tst_test.c:1694: TINFO: === Testing on exfat ===
> tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra 
> opts=''
> tst_test.c:1120: TINFO: Mounting /dev/loop0 to 
> /tmp/LTP_lantEvE2G/sandbox fstyp=exfat flags=0
> landlock_common.h:30: TINFO: Landlock ABI v4
> landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_SOCK
> landlock_tester.h:237: TINFO: Test normal or special files creation
> landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: 
> EPERM (1)
> ...
>
>
> Regard,
> Li Wang
Andrea Cervesato July 3, 2024, 1:42 p.m. UTC | #5
Comments below.

On 7/2/24 14:22, Li Wang wrote:
>
>
> On Tue, Jul 2, 2024 at 4:00 PM Li Wang <liwang@redhat.com> wrote:
>
>
>
>     On Mon, Jul 1, 2024 at 11:44 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/Makefile        |  5 +
>          testcases/kernel/syscalls/landlock/landlock04.c    | 143
>         +++++++++
>          testcases/kernel/syscalls/landlock/landlock_exec.c |  9 +
>          .../kernel/syscalls/landlock/landlock_tester.h     | 350
>         +++++++++++++++++++++
>          6 files changed, 510 insertions(+)
>
>         diff --git a/runtest/syscalls b/runtest/syscalls
>         index 1e2d682e3..9acdaf760 100644
>         --- a/runtest/syscalls
>         +++ b/runtest/syscalls
>         @@ -687,6 +687,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/Makefile
>         b/testcases/kernel/syscalls/landlock/Makefile
>         index 4b3e3fd8f..bdc6bd2d4 100644
>         --- a/testcases/kernel/syscalls/landlock/Makefile
>         +++ b/testcases/kernel/syscalls/landlock/Makefile
>         @@ -8,3 +8,8 @@ include $(top_srcdir)/include/mk/testcases.mk
>         <http://testcases.mk>
>          LDLIBS += -lc
>
>          include $(top_srcdir)/include/mk/generic_leaf_target.mk
>         <http://generic_leaf_target.mk>
>         +
>         +# the reason why landlock_exec test binary is statically
>         linked, is that
>         +# we can't read libc out of the sandboxed folder once
>         LANDLOCK_ACCESS_FS_EXECUTE
>         +# has been activated
>         +landlock_exec: LDLIBS += -static -fPIC
>
>
> And, if we decided to build the program as static, we'd better
> adding glibc-static into the dependency list, otherwise it always
> failed to link the binary on RHEL distributions.
>
We found a solution for this. We compile landlock_exec with dynamic 
linking, then we apply READ + EXEC rules to /lib and /lib64.
In this way we can easily execute the binary and we don't need more LTP 
dependences.
> --- a/ci/fedora.sh
> +++ b/ci/fedora.sh
> @@ -17,6 +17,7 @@ $yum \
>         numactl-devel \
>         libtirpc \
>         libtirpc-devel \
> +       libc-static \
>         perl-JSON \
>         perl-libwww-perl \
>         pkg-config \
>
>
>
> -- 
> Regards,
> Li Wang

Andrea
Li Wang July 4, 2024, 1:53 a.m. UTC | #6
Andrea Cervesato <andrea.cervesato@suse.com> wrote:


> +# the reason why landlock_exec test binary is statically linked, is that
>>> +# we can't read libc out of the sandboxed folder once
>>> LANDLOCK_ACCESS_FS_EXECUTE
>>> +# has been activated
>>> +landlock_exec: LDLIBS += -static -fPIC
>>>
>>
> And, if we decided to build the program as static, we'd better
> adding glibc-static into the dependency list, otherwise it always
> failed to link the binary on RHEL distributions.
>
> We found a solution for this. We compile landlock_exec with dynamic
> linking, then we apply READ + EXEC rules to /lib and /lib64.
> In this way we can easily execute the binary and we don't need more LTP
> dependences.
>

Sounds good.
diff mbox series

Patch

diff --git a/runtest/syscalls b/runtest/syscalls
index 1e2d682e3..9acdaf760 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -687,6 +687,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/Makefile b/testcases/kernel/syscalls/landlock/Makefile
index 4b3e3fd8f..bdc6bd2d4 100644
--- a/testcases/kernel/syscalls/landlock/Makefile
+++ b/testcases/kernel/syscalls/landlock/Makefile
@@ -8,3 +8,8 @@  include $(top_srcdir)/include/mk/testcases.mk
 LDLIBS	+= -lc
 
 include $(top_srcdir)/include/mk/generic_leaf_target.mk
+
+# the reason why landlock_exec test binary is statically linked, is that
+# we can't read libc out of the sandboxed folder once LANDLOCK_ACCESS_FS_EXECUTE
+# has been activated
+landlock_exec: LDLIBS += -static -fPIC
diff --git a/testcases/kernel/syscalls/landlock/landlock04.c b/testcases/kernel/syscalls/landlock/landlock04.c
new file mode 100644
index 000000000..1e7c6f3d1
--- /dev/null
+++ b/testcases/kernel/syscalls/landlock/landlock04.c
@@ -0,0 +1,143 @@ 
+// 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"
+
+#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 setup(void)
+{
+	struct tvariant variant = tvariants[tst_variant];
+
+	verify_landlock_is_enabled();
+	tester_create_tree();
+
+	tst_res(TINFO, "Testing %s", variant.desc);
+
+	ruleset_attr->handled_access_fs = tester_get_all_rules();
+
+	apply_landlock_layer(
+		ruleset_attr,
+		path_beneath_attr,
+		SANDBOX_FOLDER,
+		variant.access);
+}
+
+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",
+		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