1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Test for SECCOMP_RET_TRACE argument modification bypass 4 * via stale orig_x0 during filter re-evaluation. 5 * 6 * On arm64, syscall_get_arguments() reads the first argument from 7 * regs->orig_x0. When a seccomp filter returns SECCOMP_RET_TRACE, 8 * ptrace may modify regs->regs[0] while orig_x0 remains unchanged. 9 * The kernel then re-evaluates the filter; if it sees the stale 10 * orig_x0, it may incorrectly allow a syscall that the tracer intended 11 * to block. 12 * 13 * This test installs a filter that: 14 * - TRACEs write() when fd == 2 15 * - returns ERRNO(EPERM) when fd == 1 16 * - allows all other syscalls 17 * 18 * The child calls write(2, ...). The parent catches the SECCOMP stop, 19 * changes x0 (fd) from 2 to 1, and resumes the child. 20 * 21 * If re-evaluation sees the old fd=2 (stale orig_x0), the filter 22 * returns TRACE again; because recheck_after_trace is true, the kernel 23 * allows the syscall to proceed. write(1, ...) succeeds, child exits 0. 24 * -> test FAIL (bypass detected). 25 * 26 * If re-evaluation sees the new fd=1 (synced orig_x0), the filter 27 * returns ERRNO(EPERM), write fails, child exits 1. 28 * -> test PASS (no bypass). 29 * 30 * No special privileges required beyond CAP_SYS_PTRACE. 31 */ 32 #include <errno.h> 33 #include <signal.h> 34 #include <stddef.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <unistd.h> 38 #include <errno.h> 39 #include <sys/prctl.h> 40 #include <sys/ptrace.h> 41 #include <sys/uio.h> 42 #include <sys/wait.h> 43 #include <linux/elf.h> 44 #include <linux/filter.h> 45 #include <linux/seccomp.h> 46 #include <linux/ptrace.h> 47 #include <asm/unistd.h> 48 49 #include "kselftest.h" 50 51 #define PTRACE_EVENT_MASK(status) ((status) >> 16) 52 53 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 54 #define ARG0_OFFSET (offsetof(struct seccomp_data, args)) 55 #else 56 #define ARG0_OFFSET (offsetof(struct seccomp_data, args) + 4) 57 #endif 58 59 static int do_child(void) 60 { 61 long ret; 62 63 if (ptrace(PTRACE_TRACEME, 0, NULL, NULL)) 64 _exit(2); 65 66 raise(SIGSTOP); /* synchronize with parent */ 67 68 /* 69 * Filter: 70 * if syscall == write: 71 * if fd == 2 -> TRACE 72 * if fd == 1 -> ERRNO(EPERM) 73 * else -> ALLOW 74 * else -> ALLOW 75 */ 76 struct sock_filter filter[] = { 77 /* Load syscall number */ 78 BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)), 79 /* If not write, allow */ 80 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 5), 81 /* Load first argument (fd) */ 82 BPF_STMT(BPF_LD | BPF_W | BPF_ABS, ARG0_OFFSET), 83 /* fd == 2 ? */ 84 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 2, 0, 1), 85 /* Yes: TRACE */ 86 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE), 87 /* fd == 1 ? */ 88 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, 1), 89 /* Yes: ERRNO(EPERM) */ 90 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (EPERM & SECCOMP_RET_DATA)), 91 /* Other fd: ALLOW */ 92 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), 93 }; 94 95 struct sock_fprog prog = { 96 .len = ARRAY_SIZE(filter), 97 .filter = filter, 98 }; 99 100 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) 101 _exit(3); 102 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) 103 _exit(4); 104 105 /* 106 * write(2, ...) triggers TRACE, parent changes fd to 1. 107 * If re-eval sees fd=1 -> ERRNO -> write fails, ret = -EPERM. 108 * If re-eval sees fd=2 -> TRACE again -> allowed -> write succeeds. 109 */ 110 ret = syscall(__NR_write, 2, "", 0); 111 _exit(ret == 0 ? 0 : 1); 112 } 113 114 int main(void) 115 { 116 struct user_pt_regs regs; 117 struct iovec iov = { .iov_base = ®s, .iov_len = sizeof(regs) }; 118 pid_t child; 119 int status; 120 121 ksft_print_header(); 122 ksft_set_plan(1); 123 124 child = fork(); 125 if (child < 0) 126 ksft_exit_fail_msg("fork failed: %s", strerror(errno)); 127 128 if (!child) 129 return do_child(); 130 131 /* 1. Wait for initial SIGSTOP */ 132 if (waitpid(child, &status, 0) != child) 133 ksft_exit_fail_msg("waitpid SIGSTOP"); 134 if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP) 135 ksft_exit_fail_msg("unexpected initial stop"); 136 137 /* 2. Enable SECCOMP ptrace events */ 138 if (ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESECCOMP)) 139 ksft_exit_fail_msg("PTRACE_SETOPTIONS"); 140 141 /* 3. Continue child to hit SECCOMP stop */ 142 if (ptrace(PTRACE_CONT, child, 0, 0)) 143 ksft_exit_fail_msg("PTRACE_CONT"); 144 145 /* 4. Wait for SECCOMP stop */ 146 while (1) { 147 if (waitpid(child, &status, 0) != child) 148 ksft_exit_fail_msg("waitpid SECCOMP"); 149 if (WIFEXITED(status)) { 150 ksft_test_result_fail("child exited before SECCOMP stop\n"); 151 goto out; 152 } 153 if (WIFSIGNALED(status)) { 154 ksft_test_result_fail("child killed unexpectedly\n"); 155 goto out; 156 } 157 if (WIFSTOPPED(status) && 158 WSTOPSIG(status) == SIGTRAP && 159 PTRACE_EVENT_MASK(status) == PTRACE_EVENT_SECCOMP) 160 break; 161 ptrace(PTRACE_CONT, child, 0, WSTOPSIG(status)); 162 } 163 164 /* 5. Modify x0 (fd) from 2 to 1 */ 165 if (ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov)) 166 ksft_exit_fail_perror("GETREGSET"); 167 if (regs.regs[8] != __NR_write || regs.regs[0] != 2) { 168 ksft_test_result_fail("unexpected regs: syscall=%llu, x0=%llu\n", 169 regs.regs[8], regs.regs[0]); 170 goto out; 171 } 172 regs.regs[0] = 1; 173 if (ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov)) 174 ksft_exit_fail_perror("SETREGSET"); 175 176 /* 6. Resume child */ 177 if (ptrace(PTRACE_CONT, child, 0, 0)) 178 ksft_exit_fail_perror("PTRACE_CONT"); 179 180 /* 7. Reap child – must exit normally */ 181 if (waitpid(child, &status, 0) != child) 182 ksft_exit_fail_msg("final waitpid"); 183 184 if (!WIFEXITED(status)) { 185 ksft_test_result_fail("child did not exit normally\n"); 186 goto out; 187 } 188 189 if (WEXITSTATUS(status) != 0) 190 ksft_test_result_pass("seccomp correctly denied modified syscall\n"); 191 else 192 ksft_test_result_fail("write succeeded, orig_x0 bypass likely\n"); 193 194 out: 195 if (child > 0) { 196 kill(child, SIGKILL); 197 waitpid(child, NULL, 0); 198 } 199 ksft_print_cnts(); 200 return ksft_get_fail_cnt() ? EXIT_FAILURE : EXIT_SUCCESS; 201 } 202