From: Thomas Weißschuh Date: Tue, 16 May 2023 21:18:58 +0000 (+0200) Subject: enosys: properly block execve syscall X-Git-Tag: v2.40-rc1~466^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=afb669afededdc2865bc132b41a4d70b8c2ee840;p=thirdparty%2Futil-linux.git enosys: properly block execve syscall Signed-off-by: Thomas Weißschuh --- diff --git a/misc-utils/enosys.c b/misc-utils/enosys.c index 0534fc9867..1aa673717c 100644 --- a/misc-utils/enosys.c +++ b/misc-utils/enosys.c @@ -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 @@ -59,9 +60,11 @@ #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 index 0000000000..be9e72f3df --- /dev/null +++ b/tests/expected/misc/enosys-exec @@ -0,0 +1 @@ +test_enosys: exec failed: Function not implemented diff --git a/tests/helpers/test_enosys.c b/tests/helpers/test_enosys.c index b38fba168c..69f7af9af9 100644 --- a/tests/helpers/test_enosys.c +++ b/tests/helpers/test_enosys.c @@ -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]); diff --git a/tests/ts/misc/enosys b/tests/ts/misc/enosys index 8f176da4f8..2a8241296f 100755 --- a/tests/ts/misc/enosys +++ b/tests/ts/misc/enosys @@ -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