From nobody Sat Jul 25 05:31:26 2026 Received: from canpmsgout09.his.huawei.com (canpmsgout09.his.huawei.com [113.46.200.224]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 70DD7331230; Fri, 17 Jul 2026 08:56:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.224 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784278617; cv=none; b=apgx0nWKUsLeIXN2NLm2vobMeO/YBUn+7NccCD6jLyUG8XrjU30ih5PI3PX9jUlwer3Wud1S1PML5dE1c8sCqWC0X1W+bJUvECnWF/Y9NTICIYEACMP52o6iJNkwJ32YoUhBi/QjCtG9EBWnKvwnFovQqfITSjSaBnXVFKFePD8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784278617; c=relaxed/simple; bh=M33hY5Y97gV7WejKRtksyjn/3FXnhILeCczyx7Loo7g=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qwjYg8YJ2XPUuEpQACs6es4cHO61DBM/TJWyLbSWuICeZNpmqoRYYz0FHpNKEESIqygZuRteTrsYeK4RYGG9yMceM1Q9ObAdFQ2JnDP3JXz+INEVcgm2z40wWVOH0bE6edvJD7+K4s4X/ROAC/V7ZjnGDdqDyYEIykKCsabK/jY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=1fATCpot; arc=none smtp.client-ip=113.46.200.224 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="1fATCpot" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=6Kb3DwHOJRoxaCruQM++shG+RbOi01rLJ7Tldla4u9Q=; b=1fATCpot+TPP1h7BtwzkOxn+bToPVszOnjNK5ds/nJcYqmqWshjXRkPV5/RyyO8Tf/F4nqGxm tge8QqKo7+dOld62Nd8Wx5I6DaAiNuM7IzkC4addw8ZJ0djB8ZQQPE4CbrKvdWxpX8NlPnPJsu/ l6pnxdfsa9/27p5QoYZh5MA= Received: from mail.maildlp.com (unknown [172.19.163.127]) by canpmsgout09.his.huawei.com (SkyGuard) with ESMTPS id 4h1k7R6YXBz1cyR3; Fri, 17 Jul 2026 16:47:31 +0800 (CST) Received: from dggpemf500011.china.huawei.com (unknown [7.185.36.131]) by mail.maildlp.com (Postfix) with ESMTPS id 1574440572; Fri, 17 Jul 2026 16:56:50 +0800 (CST) Received: from huawei.com (10.90.53.73) by dggpemf500011.china.huawei.com (7.185.36.131) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Fri, 17 Jul 2026 16:56:49 +0800 From: Jinjie Ruan To: , , , , , , CC: Subject: [PATCH 1/2] kselftest/arm64: Add seccomp ptrace x0 bypass test Date: Fri, 17 Jul 2026 16:56:56 +0800 Message-ID: <20260717085657.3600248-2-ruanjinjie@huawei.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260717085657.3600248-1-ruanjinjie@huawei.com> References: <20260717085657.3600248-1-ruanjinjie@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems100001.china.huawei.com (7.221.188.238) To dggpemf500011.china.huawei.com (7.185.36.131) As Kees suggested, add a test that verifies that seccomp observes the correct first argument after a ptracer modifies x0 at a syscall-enter-stop on arm64. The first syscall argument and the return value share register x0. The original value is saved in orig_x0 on entry and used by syscall_get_arguments(), but ptrace changes to x0 were not automatically reflected there. This test checks the kernel re-syncs orig_x0 after a ptrace stop so that seccomp sees the modified argument. A seccomp filter allows write(2,...) and kills the task for any other fd. The tracer changes fd from 2 to 1 at entry. If orig_x0 remains stale, the child exits normally (bypass, test fails). If orig_x0 is correctly updated, the child is killed by SIGSYS (test passes). Before the fix: ./seccomp_ptrace_x0_bypass TAP version 13 1..1 not ok 1 seccomp_ptrace_x0_bypass # Totals: pass:0 fail:1 xfail:0 xpass:0 skip:0 error:0 After the fix: # ./seccomp_ptrace_x0_bypass TAP version 13 1..1 [ 19.475951] audit: type=3D1326 audit(1784254846.284:2): auid=3D42949672= 95 uid=3D0 gid=3D0 ses=3D4294967295 pid=3D227 comm=3D"seccomp_ptrace_" exe= =3D"/mnt/seccomp0 [ 19.477852] audit: type=3D1701 audit(1784254846.284:3): auid=3D42949672= 95 uid=3D0 gid=3D0 ses=3D4294967295 pid=3D227 comm=3D"seccomp_ptrace_" exe= =3D"/mnt/seccomp1 ok 1 seccomp_ptrace_x0_bypass # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Cc: Kees Cook Cc: Will Deacon Cc: Catalin Marinas Cc: Mark Rutland Link: https://lore.kernel.org/all/20260716120640.6590-1-will@kernel.org/ Link: https://lore.kernel.org/all/202607152004.DEA95D63@keescook/ Suggested-by: Kees Cook Signed-off-by: Jinjie Ruan --- tools/testing/selftests/arm64/abi/.gitignore | 1 + tools/testing/selftests/arm64/abi/Makefile | 2 +- .../arm64/abi/seccomp_ptrace_x0_bypass.c | 181 ++++++++++++++++++ 3 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/arm64/abi/seccomp_ptrace_x0_byp= ass.c diff --git a/tools/testing/selftests/arm64/abi/.gitignore b/tools/testing/s= elftests/arm64/abi/.gitignore index 44f8b80f37e3..39129a9907c7 100644 --- a/tools/testing/selftests/arm64/abi/.gitignore +++ b/tools/testing/selftests/arm64/abi/.gitignore @@ -1,4 +1,5 @@ hwcap ptrace +seccomp_ptrace_x0_bypass syscall-abi tpidr2 diff --git a/tools/testing/selftests/arm64/abi/Makefile b/tools/testing/sel= ftests/arm64/abi/Makefile index 483488f8c2ad..5a16db379bd4 100644 --- a/tools/testing/selftests/arm64/abi/Makefile +++ b/tools/testing/selftests/arm64/abi/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright (C) 2021 ARM Limited =20 -TEST_GEN_PROGS :=3D hwcap ptrace syscall-abi tpidr2 +TEST_GEN_PROGS :=3D hwcap ptrace syscall-abi tpidr2 seccomp_ptrace_x0_bypa= ss =20 include ../../lib.mk =20 diff --git a/tools/testing/selftests/arm64/abi/seccomp_ptrace_x0_bypass.c b= /tools/testing/selftests/arm64/abi/seccomp_ptrace_x0_bypass.c new file mode 100644 index 000000000000..718d3dcb3264 --- /dev/null +++ b/tools/testing/selftests/arm64/abi/seccomp_ptrace_x0_bypass.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test that seccomp, tracepoints and audit observe the correct syscall + * arguments after a ptracer has modified them at syscall-enter-stop. + * + * On arm64, both the first argument and the return value of a syscall + * are passed in register x0. The original x0 is saved in + * pt_regs::orig_x0 during syscall entry and returned as the first + * argument by syscall_get_arguments(). Because ptrace modifications + * to x0 are not automatically reflected in orig_x0, seccomp, tracepoints + * and audit may see a stale value unless orig_x0 is explicitly + * re-synchronised after a ptrace stop. + * + * This test sets up a seccomp filter that allows write(2, ...) but kills + * the task for any other fd. A ptracer changes the fd argument from 2 + * to 1 at the syscall-enter stop. If the orig_x0 re-sync works, seccomp + * sees the modified argument (fd=3D1) and kills the child with SIGSYS + * (test passes). If orig_x0 is not re-synced, seccomp sees the original + * fd=3D2, the write succeeds and the child exits normally (test fails, + * vulnerability present). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kselftest.h" + +#ifndef __NR_write +#define __NR_write 64 +#endif + +#define EXPECTED_TESTS 1 + +static int do_child(void) +{ + if (ptrace(PTRACE_TRACEME, 0, NULL, NULL)) + ksft_exit_fail_perror("PTRACE_TRACEME"); + + if (raise(SIGSTOP)) + ksft_exit_fail_perror("raise(SIGSTOP)"); + + /* + * Seccomp filter: + * If syscall is not write -> ALLOW + * If syscall is write: + * - If args[0] (fd) =3D=3D 2 -> ALLOW + * - Otherwise -> KILL + */ + struct sock_filter filter[] =3D { + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 0), /* nr */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 3), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 16), /* args[0] */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 2, 1, 0), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog =3D { + .len =3D ARRAY_SIZE(filter), + .filter =3D filter, + }; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) + ksft_exit_fail_perror("prctl NO_NEW_PRIVS"); + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) + ksft_exit_fail_perror("prctl SECCOMP"); + + /* + * Invoke write(2, ...) while the tracer will change the first + * argument (fd) from 2 to 1 at syscall entry. + */ + syscall(__NR_write, 2, NULL, 0); + _exit(0); +} + +static int do_parent(pid_t child) +{ + bool bypass =3D false; + int status; + + /* Wait for the initial SIGSTOP */ + if (waitpid(child, &status, 0) !=3D child) + ksft_exit_fail_msg("waitpid failed"); + + if (!WIFSTOPPED(status) || WSTOPSIG(status) !=3D SIGSTOP) + ksft_exit_fail_msg("unexpected stop status"); + + if (ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD)) + ksft_exit_fail_perror("PTRACE_SETOPTIONS"); + + if (ptrace(PTRACE_SYSCALL, child, 0, 0)) + ksft_exit_fail_perror("PTRACE_SYSCALL"); + + while (1) { + int sig; + + if (waitpid(child, &status, 0) !=3D child) + ksft_exit_fail_msg("waitpid lost child"); + + if (WIFEXITED(status)) { + /* Child exited normally =E2=80=93 bypass succeeded */ + bypass =3D true; + break; + } + + if (WIFSIGNALED(status)) + break; + + if (!WIFSTOPPED(status)) + ksft_exit_fail_msg("unexpected wait status"); + + sig =3D WSTOPSIG(status); + + if (sig =3D=3D (SIGTRAP | 0x80)) { + struct user_regs_struct regs; + struct iovec iov =3D { + .iov_base =3D ®s, + .iov_len =3D sizeof(regs), + }; + + if (ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov)) + ksft_exit_fail_perror("PTRACE_GETREGSET"); + + unsigned long syscall_nr =3D regs.regs[8]; + unsigned long x0 =3D regs.regs[0]; + + /* Modify fd from 2 to 1 at write entry */ + if (syscall_nr =3D=3D __NR_write && x0 =3D=3D 2) { + regs.regs[0] =3D 1; + if (ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov)) + ksft_exit_fail_perror("PTRACE_SETREGSET"); + } + + if (ptrace(PTRACE_SYSCALL, child, 0, 0)) + ksft_exit_fail_perror("PTRACE_SYSCALL"); + } else { + /* Forward other signals */ + if (ptrace(PTRACE_SYSCALL, child, 0, sig)) + ksft_exit_fail_perror("PTRACE_SYSCALL"); + } + } + + /* bypass =3D=3D true means vulnerability exists -> test fails */ + return bypass ? EXIT_FAILURE : EXIT_SUCCESS; +} + +int main(void) +{ + pid_t child; + + ksft_print_header(); + ksft_set_plan(EXPECTED_TESTS); + + child =3D fork(); + if (!child) + return do_child(); + + /* + * do_parent() returns EXIT_SUCCESS if the child was killed by + * SIGSYS (i.e. seccomp correctly saw the modified argument), + * and EXIT_FAILURE if the child exited normally (bypass). + */ + int result =3D do_parent(child); + + ksft_test_result(result =3D=3D EXIT_SUCCESS, "seccomp_ptrace_x0_bypass\n"= ); + + ksft_print_cnts(); + return result; +} --=20 2.34.1 From nobody Sat Jul 25 05:31:26 2026 Received: from canpmsgout04.his.huawei.com (canpmsgout04.his.huawei.com [113.46.200.219]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A8D873AB288; Fri, 17 Jul 2026 08:56:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.219 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784278619; cv=none; b=ueK78wf/dpi2vPkLjhLuHmLGwZn7ymLqrcCUabtGE+xujQSVuvWYmnjdAW8kW8vMpamAFZaLjtrHyTFD8XUVD3X5t6QxGeK1VGEwcdd9egUuoM2fQW7QNN4SOfXvTOd4ur12wQ5PmCcQYTsSJIqTVcu4Ci41Z/ugW3ta7Tr9wPM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784278619; c=relaxed/simple; bh=T6FIl2qBNzHVux9fRPl8zy0+exJR/zlmu3RzkHklYpo=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=K2bTxZVu0zv+XG7hzhTp8QyNg/Kwfs4K6pdLm9AnPxCsiWnpS7v7sL98Xpxstr+qjmeEh/5xNy1Bf1hEJYbcHDpwlwdYiGOoZkPhcFrn15ECVEe+KRvvrAtLa6mFeaZHCX/onTpwQ0B1tz+jfy8HtBCdKwVOnszHqQLxwrczxao= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=EOHAj8GJ; arc=none smtp.client-ip=113.46.200.219 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="EOHAj8GJ" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=1pe6oNWRgyqsCINQc9IE5lBArQDCM88lWlgPXH66SvQ=; b=EOHAj8GJtMLgv+LgoapPh8rztrXJQFsDep6/qc9p1559QU3zdRaojdXPsgc3kvSq/8eknbrjJ g02kLTYLv1zrDrxBajvUgBbUZ4OojxMYwO3UCnBa7knnmfXImNhPd+UH33pRQsS4NUo7VxXM5FC gmZ2tIIidIScGRLXuX3fXz0= Received: from mail.maildlp.com (unknown [172.19.162.197]) by canpmsgout04.his.huawei.com (SkyGuard) with ESMTPS id 4h1k7N75mDz1prmX; Fri, 17 Jul 2026 16:47:28 +0800 (CST) Received: from dggpemf500011.china.huawei.com (unknown [7.185.36.131]) by mail.maildlp.com (Postfix) with ESMTPS id 991304057D; Fri, 17 Jul 2026 16:56:50 +0800 (CST) Received: from huawei.com (10.90.53.73) by dggpemf500011.china.huawei.com (7.185.36.131) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Fri, 17 Jul 2026 16:56:50 +0800 From: Jinjie Ruan To: , , , , , , CC: Subject: [PATCH 2/2] kselftest/arm64: Add SECCOMP_RET_TRACE x0 bypass test Date: Fri, 17 Jul 2026 16:56:57 +0800 Message-ID: <20260717085657.3600248-3-ruanjinjie@huawei.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260717085657.3600248-1-ruanjinjie@huawei.com> References: <20260717085657.3600248-1-ruanjinjie@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems100001.china.huawei.com (7.221.188.238) To dggpemf500011.china.huawei.com (7.185.36.131) Add a selftest that verifies the kernel re-synchronises orig_x0 after a SECCOMP_RET_TRACE ptrace event, so that tracepoints (and audit) see the modified argument rather than the original value. The child installs a seccomp filter returning SECCOMP_RET_TRACE for write(). The tracer changes the first argument (fd) from 2 to 1 at the SECCOMP event, then the test checks that the sys_enter_write tracepoint records fd=3D1. The test requires root and tracefs (at /sys/kernel/debug/tracing); it is skipped gracefully when these are unavailable. Before the fix: # ./seccomp_ret_trace_x0_bypass TAP version 13 1..1 not ok 1 seccomp_ret_trace_x0_tracepoint # Totals: pass:0 fail:1 xfail:0 xpass:0 skip:0 error:0 After the fix: # ./seccomp_ret_trace_x0_bypass TAP version 13 1..1 ok 1 seccomp_ret_trace_x0_tracepoint # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Cc: Kees Cook Cc: Will Deacon Cc: Catalin Marinas Cc: Mark Rutland Link: https://lore.kernel.org/all/20260716120640.6590-1-will@kernel.org/ Link: https://lore.kernel.org/all/202607152004.DEA95D63@keescook/ Suggested-by: Kees Cook Signed-off-by: Jinjie Ruan --- tools/testing/selftests/arm64/abi/.gitignore | 1 + tools/testing/selftests/arm64/abi/Makefile | 2 +- .../arm64/abi/seccomp_ret_trace_x0_bypass.c | 249 ++++++++++++++++++ 3 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/arm64/abi/seccomp_ret_trace_x0_= bypass.c diff --git a/tools/testing/selftests/arm64/abi/.gitignore b/tools/testing/s= elftests/arm64/abi/.gitignore index 39129a9907c7..491a80db9dff 100644 --- a/tools/testing/selftests/arm64/abi/.gitignore +++ b/tools/testing/selftests/arm64/abi/.gitignore @@ -1,5 +1,6 @@ hwcap ptrace seccomp_ptrace_x0_bypass +seccomp_ret_trace_x0_bypass syscall-abi tpidr2 diff --git a/tools/testing/selftests/arm64/abi/Makefile b/tools/testing/sel= ftests/arm64/abi/Makefile index 5a16db379bd4..a01d3806eba8 100644 --- a/tools/testing/selftests/arm64/abi/Makefile +++ b/tools/testing/selftests/arm64/abi/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright (C) 2021 ARM Limited =20 -TEST_GEN_PROGS :=3D hwcap ptrace syscall-abi tpidr2 seccomp_ptrace_x0_bypa= ss +TEST_GEN_PROGS :=3D hwcap ptrace syscall-abi tpidr2 seccomp_ptrace_x0_bypa= ss seccomp_ret_trace_x0_bypass =20 include ../../lib.mk =20 diff --git a/tools/testing/selftests/arm64/abi/seccomp_ret_trace_x0_bypass.= c b/tools/testing/selftests/arm64/abi/seccomp_ret_trace_x0_bypass.c new file mode 100644 index 000000000000..a4e56a382fe2 --- /dev/null +++ b/tools/testing/selftests/arm64/abi/seccomp_ret_trace_x0_bypass.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Verify that after a SECCOMP_RET_TRACE stop, changes to x0 are visible + * to the syscall tracepoint (orig_x0 re-sync). + * + * The child installs a seccomp filter that returns SECCOMP_RET_TRACE for + * write(). The parent waits for PTRACE_EVENT_SECCOMP, changes the first + * argument (fd) from 2 to 1, then resumes the child. By monitoring the + * sys_enter_write tracepoint, we check whether the kernel recorded fd=3D1 + * (test passes) or fd=3D2 (test fails). + * + * Requires root and tracefs at /sys/kernel/debug/tracing. If unavailable, + * the test is skipped. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kselftest.h" + +#ifndef __NR_write +#define __NR_write 64 +#endif + +#define PTRACE_EVENT_MASK(status) ((status) >> 16) + +#define TRACEFS_PATH "/sys/kernel/debug/tracing" +#define TRACE_EVENT "syscalls/sys_enter_write" /* note '/' not ':' */ +#define TRACE_PIPE TRACEFS_PATH "/trace_pipe" +#define TRACE_ON TRACEFS_PATH "/events/" TRACE_EVENT "/enable" +#define TRACE_CLR TRACEFS_PATH "/trace" + +static int do_child(void) +{ + int null_fd =3D open("/dev/null", O_WRONLY); + + if (null_fd >=3D 0) { + dup2(null_fd, STDOUT_FILENO); + close(null_fd); + } + + if (ptrace(PTRACE_TRACEME, 0, NULL, NULL)) + _exit(1); + raise(SIGSTOP); /* let parent configure ptrace options */ + + struct sock_filter filter[] =3D { + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 0), /* syscall nr */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE), /* write -> trace */ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog =3D { + .len =3D ARRAY_SIZE(filter), + .filter =3D filter, + }; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) + _exit(2); + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) + _exit(3); + + /* write(2, "", 0) =E2=80=93 parent will change fd to 1 at SECCOMP stop */ + syscall(__NR_write, 2, "", 0); + _exit(0); +} + +static int trace_pipe_expect(const char *expect, int timeout_secs) +{ + FILE *fp =3D fopen(TRACE_PIPE, "r"); + char buf[4096]; + int found =3D 0; + time_t deadline =3D time(NULL) + timeout_secs; + + if (!fp) + return 0; + + fcntl(fileno(fp), F_SETFL, O_NONBLOCK); + while (time(NULL) < deadline) { + ssize_t n =3D fread(buf, 1, sizeof(buf) - 1, fp); + + if (n <=3D 0) { + usleep(10000); + continue; + } + buf[n] =3D '\0'; + if (strstr(buf, expect)) { + found =3D 1; + break; + } + } + fclose(fp); + return found; +} + +static void enable_trace(bool on) +{ + int fd =3D open(TRACE_ON, O_WRONLY); + char c =3D on ? '1' : '0'; + + if (fd >=3D 0) { + write(fd, &c, 1); + close(fd); + } +} + +static void clear_trace(void) +{ + int fd =3D open(TRACE_CLR, O_WRONLY); + + if (fd >=3D 0) { + write(fd, "0", 1); + close(fd); + } +} + +int main(void) +{ + /* Wait for SECCOMP event or child exit */ + bool seccomp_event =3D false; + bool ok =3D false; + pid_t child; + int status; + + ksft_print_header(); + ksft_set_plan(1); + + if (geteuid() !=3D 0) { + ksft_test_result_skip("not root\n"); + goto out; + } + if (access(TRACE_ON, W_OK) !=3D 0) { + ksft_test_result_skip("tracefs unavailable\n"); + goto out; + } + + child =3D fork(); + if (!child) + return do_child(); + + /* Wait for initial SIGSTOP */ + if (waitpid(child, &status, 0) !=3D child) + ksft_exit_fail_msg("waitpid initial"); + if (!WIFSTOPPED(status) || WSTOPSIG(status) !=3D SIGSTOP) + ksft_exit_fail_msg("unexpected initial stop"); + + if (ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESECCOMP)) { + ksft_test_result_fail("setoptions\n"); + goto out; + } + + if (ptrace(PTRACE_CONT, child, 0, 0)) { + ksft_test_result_fail("PTRACE_CONT\n"); + goto out; + } + + while (1) { + if (waitpid(child, &status, 0) !=3D child) + ksft_exit_fail_msg("waitpid lost"); + + if (WIFEXITED(status)) { + int code =3D WEXITSTATUS(status); + + ksft_test_result(code =3D=3D 2 || code =3D=3D 3, + "seccomp filter not installed\n"); + goto out; + } + if (WIFSIGNALED(status)) { + ksft_test_result_fail("killed\n"); + goto out; + } + if (WIFSTOPPED(status)) { + if (WSTOPSIG(status) =3D=3D SIGTRAP && + PTRACE_EVENT_MASK(status) =3D=3D PTRACE_EVENT_SECCOMP) { + seccomp_event =3D true; + break; + } + ptrace(PTRACE_CONT, child, 0, WSTOPSIG(status)); + } + } + + if (!seccomp_event) + goto out; + + /* At SECCOMP stop: modify x0 (fd) from 2 to 1 */ + { + unsigned long long syscall_nr, x0; + struct user_pt_regs regs; + struct iovec iov =3D { .iov_base =3D ®s, .iov_len =3D sizeof(regs) }; + + if (ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov)) + ksft_exit_fail_perror("GETREGSET"); + + syscall_nr =3D regs.regs[8]; + x0 =3D regs.regs[0]; + if (syscall_nr !=3D __NR_write || x0 !=3D 2) { + ksft_test_result_fail("bad regs\n"); + goto out; + } + + regs.regs[0] =3D 1; + if (ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov)) { + ksft_test_result_fail("SETREGSET\n"); + goto out; + } + } + + /* Enable tracepoint and clear buffer */ + clear_trace(); + enable_trace(true); + + /* Continue child; it will execute write(1, ...) and hit tracepoint */ + if (ptrace(PTRACE_CONT, child, 0, 0)) + ksft_exit_fail_perror("PTRACE_CONT after SECCOMP"); + + /* Reap the child */ + while (1) { + if (waitpid(child, &status, 0) !=3D child) + break; + if (WIFEXITED(status) || WIFSIGNALED(status)) + break; + ptrace(PTRACE_CONT, child, 0, WSTOPSIG(status)); + } + enable_trace(false); + + /* Check trace for the modified fd */ + ok =3D trace_pipe_expect("fd: 1", 5); + ksft_test_result(ok, "seccomp_ret_trace_x0_tracepoint\n"); + +out: + if (child > 0) { + kill(child, SIGKILL); + waitpid(child, NULL, 0); + } + ksft_print_cnts(); + return ok ? EXIT_SUCCESS : EXIT_FAILURE; +} --=20 2.34.1