]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
enosys: properly block execve syscall
authorThomas Weißschuh <thomas@t-8ch.de>
Tue, 16 May 2023 21:18:58 +0000 (23:18 +0200)
committerThomas Weißschuh <thomas@t-8ch.de>
Tue, 16 May 2023 22:25:20 +0000 (00:25 +0200)
Signed-off-by: Thomas Weißschuh <thomas@t-8ch.de>
misc-utils/enosys.c
tests/expected/misc/enosys-exec [new file with mode: 0644]
tests/helpers/test_enosys.c
tests/ts/misc/enosys

index 0534fc98673d58702f61095cf0a1c15076ec6070..1aa673717c917b32f8d9c070cf7d4eff352ff82e 100644 (file)
@@ -29,6 +29,7 @@
 #include "c.h"
 #include "exitcodes.h"
 #include "nls.h"
+#include "bitops.h"
 
 #if __x86_64__
 #    define SECCOMP_ARCH_NATIVE AUDIT_ARCH_X86_64
 #endif
 
 #define UL_BPF_NOP (struct sock_filter) BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0)
+#define IS_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
 
 #define syscall_nr (offsetof(struct seccomp_data, nr))
 #define syscall_arch (offsetof(struct seccomp_data, arch))
+#define syscall_arg(n) (offsetof(struct seccomp_data, args[n]))
 
 struct syscall {
        const char *const name;
@@ -139,20 +142,35 @@ int main(int argc, char **argv)
        if (optind >= argc)
                errtryhelp(EXIT_FAILURE);
 
-#define N_FILTERS (ARRAY_SIZE(syscalls) * 2 + 5)
+#define N_FILTERS (ARRAY_SIZE(syscalls) * 2 + 12)
 
        struct sock_filter filter[N_FILTERS] = {
-               [0] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arch),
-               [1] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_ARCH_NATIVE, 1, 0),
-               [2] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP),
-               [3] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr),
+               [0]  = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arch),
+               [1]  = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_ARCH_NATIVE, 1, 0),
+               [2]  = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP),
+
+               /* Blocking "execve" normally would also block our own call to
+                * it and the end of main. To distinguish between our execve
+                * and the execve to be blocked, compare the environ pointer.
+                *
+                * See https://lore.kernel.org/all/CAAnLoWnS74dK9Wq4EQ-uzQ0qCRfSK-dLqh+HCais-5qwDjrVzg@mail.gmail.com/
+                */
+               [3]  = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr),
+               [4]  = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execve, 0, 5),
+               [5]  = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(2) + 4 * !IS_LITTLE_ENDIAN),
+               [6]  = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint64_t)(uintptr_t) environ, 0, 3),
+               [7]  = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(2) + 4 * IS_LITTLE_ENDIAN),
+               [8]  = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint64_t)(uintptr_t) environ >> 32, 0, 1),
+               [9]  = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+
+               [10] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr),
 
                [N_FILTERS - 1] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
        };
        static_assert(ARRAY_SIZE(filter) <= BPF_MAXINSNS, "bpf filter too big");
 
        for (i = 0; i < ARRAY_SIZE(syscalls); i++) {
-               struct sock_filter *f = &filter[4 + i * 2];
+               struct sock_filter *f = &filter[11 + i * 2];
 
                *f = (struct sock_filter) BPF_JUMP(
                                BPF_JMP | BPF_JEQ | BPF_K,
diff --git a/tests/expected/misc/enosys-exec b/tests/expected/misc/enosys-exec
new file mode 100644 (file)
index 0000000..be9e72f
--- /dev/null
@@ -0,0 +1 @@
+test_enosys: exec failed: Function not implemented
index b38fba168c4e212125cc64135cc516886a71cda5..69f7af9af91fd83aa046086af7b765e93d702b52 100644 (file)
@@ -37,6 +37,13 @@ int main(int argc, char **argv)
                errno = 0;
                r = fallocate(-1, 0, 0, 0);
                errx(EXIT_SUCCESS, "fallocate r=%d errno=%s", r, strerror(errno));
+       } else if (strcmp(argv[1], "exec") == 0) {
+               char *const cmd[] = {
+                       "/bin/false",
+                       NULL
+               };
+               execve(cmd[0], cmd, NULL);
+               err(EXIT_FAILURE, "exec failed");
        }
 
        errx(EXIT_FAILURE, "invalid mode %s", argv[1]);
index 8f176da4f89ea29b9fa0c76a57a7add18d88d8cc..2a8241296f5c2a7f0d3953aee6af256ec2944520 100755 (executable)
@@ -37,4 +37,13 @@ $FALLOCATE_TEST > /dev/null 2>> "$TS_OUTPUT"
 
 ts_finalize_subtest
 
+ts_init_subtest exec
+
+FALLOCATE_TEST="$TS_HELPER_ENOSYS exec"
+
+$FALLOCATE_TEST > /dev/null 2>> "$TS_OUTPUT"
+"$TS_CMD_ENOSYS" -s execve $FALLOCATE_TEST > /dev/null 2>> "$TS_OUTPUT"
+
+ts_finalize_subtest
+
 ts_finalize