From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=130.136.161.50 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1792510200; b=1SrcMtwY/PAPgPWpRjn30GFpdH+ny/NqHrNpipEwPiD9FX3PN15k6ofHe2MEbgXdj0wU nc7zN6E885pPMXRAi+YcSZZYz/EPhbuNlVRur308LpVuvNgYs53rGQDsmvIwMkYbHx0HX KBmDGt3JzoJDizsZl6UAGlvAn/BQn9FeSJTmSUSn/rTGtIuTfteatGqszfx55tdpn4wqz 2ceO6E9J/v6qN3+NbGy7RieKp3jyOrVou3l4UZ633f2FUXaMw6vZAyMtcn7rJXkU213YP EfNCsy3YH3oXUyuEwlrqQcgWxmO5ZoUS7XwhES67kF9+mzCmAXiyBj2CflScMk1OPTA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792510200; h=DMARC-Filter:Received:DKIM-Signature:From:To:Cc:Subject:Date: Message-ID:X-Mailer:In-Reply-To:References:MIME-Version:Content-Type: Content-Transfer-Encoding; bh=X7li7zppPfXAQDWIziNGfnEjMbFeJImmkhr9aLqumbs=; b=tiIPG2g+J+kltBXSYp28cjeEsDLqw6ff9Q+Qe2M/csUBzoJMYNztLLIQGhnoLCKf9xyW hJyz5nQ3Znnq9TbouHtiXZVeCsKDcl9M+wKsZqLxRllnP5QEZ+HHxz57LKGuZS9jcWM3R 5UzMP3+5aXOkTr4ct/nfI5vY/v76kokEeUMxLClF7H8RRmSSWfPTJKo803f8x2Dcxy1ni 3JggCLIrg/SNV92wis+bB4i06Zb1fUO67J9You6qE4wJbUsAD57EQ5Pq3S4+3ssp+JGeW /u0LTJ2/WtTinuBKBP3Sh9nvSbexCT9UZolabCaypIwr1q7nejWDUEOAFJb37WE+nzg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=cs.unibo.it; spf=pass smtp.mailfrom=cs.unibo.it; dkim=pass header.d=cs.unibo.it header.i=@cs.unibo.it header.a=rsa-sha256 header.s=virtlab header.b=veAqQRvm; arc=none smtp.remote-ip=130.136.161.50 Received: from mail.virtlab.unibo.it (mail.virtlab.unibo.it [130.136.161.50]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 13B1F606A320; Tue, 20 Oct 2026 15:29:58 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 13B1F606A320 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cs.unibo.it Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cs.unibo.it Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=cs.unibo.it header.i=@cs.unibo.it header.a=rsa-sha256 header.s=virtlab header.b=veAqQRvm Received: from eipi10.cs.unibo.it (unknown [94.32.99.206]) by mail.virtlab.unibo.it (Postfix) with ESMTPA id 9DA8B1C0251; Tue, 20 Oct 2026 17:29:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=cs.unibo.it; s=virtlab; t=1792510198; bh=kecThFQauD5pORZjNAwNTrsBZzkWTRHw2R4hetODibc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=veAqQRvmf/f/cYUdyAWyB4Uey00XfxCpUsln+jiOijpHEIBrVJuv+RtjR0x4kDt6w A3lZBmvae+xZhBiEc09i4SGQKNhlX6qNQnKOvja3s485mwJHbIPTRUE2i6450E8YSO hZDqpGJbM0+fIPAbmM/JLGL8n89otuV1JnruMC+E= From: Renzo Davoli To: linux-kernel@vger.kernel.org Cc: Davide Berardi , Oleg Nesterov , =?UTF-8?q?Michal=20Such=C3=A1nek?= , Kees Cook , Andy Lutomirski , Will Drewry , Thomas Gleixner , Andrew Morton , Shuah Khan , "Dmitry V . Levin" , Alexey Gladkov , Eugene Syromyatnikov , Thomas Bogendoerfer , Heiko Carstens , Vasily Gorbik , Madhavan Srinivasan , linuxppc-dev@lists.ozlabs.org, linux-s390@vger.kernel.org, linux-kselftest@vger.kernel.org, strace-devel@lists.strace.io, Renzo Davoli Subject: [PATCH v9 1/3] seccomp: let the tracer skip a syscall independently of its number Date: Tue, 20 Oct 2026 17:29:41 +0200 Message-ID: <20261020152945.575664-2-renzo@cs.unibo.it> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261020152945.575664-1-renzo@cs.unibo.it> References: <20261020152945.575664-1-renzo@cs.unibo.it> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Davide Berardi At a PTRACE_EVENT_SECCOMP stop the tracer can only skip the system call by setting its number to -1, which seccomp reads back with syscall_get_nr(). This depends on how each architecture lays out the syscall number and the return value in the registers: - on some architectures the syscall number shares a register with the return value (e.g. v0 on MIPS), so setting the return value after the number would overwrite the skip request, and each of them needs its own way to keep it (MIPS keeps a copy of the number in thread_info); - on s390 syscall_get_nr() returns the low 16 bits of int_code, so -1 is read back as 65535: seccomp does not skip the call but runs the filters again on syscall 65535, which kills the task if the filter denies unknown syscalls. Add a per-task flag which the tracer sets with seccomp_ptrace_skip_syscall() while the tracee is stopped at the PTRACE_EVENT_SECCOMP stop, after it has set the return value. When the tracee resumes, seccomp skips the system call like for SECCOMP_RET_ERRNO, which every architecture already handles without touching the return value. The flag is cleared by the tracee before the stop and written by the tracer only while the tracee is stopped, so it needs no locking: the wake-up of the tracee orders it like the registers written by the tracer. The next patch uses this in PTRACE_SET_SYSCALL_INFO. Suggested-by: Michal Suchánek Assisted-by: claude-opus-5-5 Signed-off-by: Renzo Davoli --- include/linux/seccomp.h | 33 +++++++++++++++++++++++++++++++++ include/linux/seccomp_types.h | 4 ++++ kernel/seccomp.c | 11 ++++++++++- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index fcb3eb9825e5..ee31857f3ba6 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -75,6 +75,32 @@ static inline int seccomp_mode(struct seccomp *s) #ifdef CONFIG_SECCOMP_FILTER extern void seccomp_filter_release(struct task_struct *tsk); extern void get_seccomp_filter(struct task_struct *tsk); + +/** + * seccomp_ptrace_skip_syscall - make a tracee skip the current system call + * @tsk: tracee stopped at a PTRACE_EVENT_SECCOMP stop + * + * Must only be called by the tracer of @tsk while @tsk is stopped at a + * PTRACE_EVENT_SECCOMP stop, after it has set the return value of the system + * call. When the tracee resumes, the system call is skipped and the return + * value is left untouched, like with SECCOMP_RET_ERRNO. + */ +static inline void seccomp_ptrace_skip_syscall(struct task_struct *tsk) +{ + tsk->seccomp.ptrace_skip = true; +} + +/** + * seccomp_ptrace_syscall_skipped - check for seccomp_ptrace_skip_syscall() + * @tsk: tracee stopped at a PTRACE_EVENT_SECCOMP stop + * + * Return: true if the tracer requested to skip the system call during the + * current PTRACE_EVENT_SECCOMP stop of @tsk. + */ +static inline bool seccomp_ptrace_syscall_skipped(struct task_struct *tsk) +{ + return tsk->seccomp.ptrace_skip; +} #else /* CONFIG_SECCOMP_FILTER */ static inline void seccomp_filter_release(struct task_struct *tsk) { @@ -84,6 +110,13 @@ static inline void get_seccomp_filter(struct task_struct *tsk) { return; } +static inline void seccomp_ptrace_skip_syscall(struct task_struct *tsk) +{ +} +static inline bool seccomp_ptrace_syscall_skipped(struct task_struct *tsk) +{ + return false; +} #endif /* CONFIG_SECCOMP_FILTER */ #if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE) diff --git a/include/linux/seccomp_types.h b/include/linux/seccomp_types.h index cf0a0355024f..4bdb8424055b 100644 --- a/include/linux/seccomp_types.h +++ b/include/linux/seccomp_types.h @@ -18,11 +18,15 @@ struct seccomp_filter; * * @filter must only be accessed from the context of current as there * is no read locking. + * @ptrace_skip: set by the tracer while the task is stopped at a + * PTRACE_EVENT_SECCOMP stop to request that the system call is + * skipped, after it has set the return value. */ struct seccomp { int mode; atomic_t filter_count; struct seccomp_filter *filter; + bool ptrace_skip; }; #else diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 86cf4460d69e..edeb21fa3e23 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -1306,6 +1306,7 @@ static bool __seccomp_filter(int this_syscall, const bool recheck_after_trace) } /* Allow the BPF to provide the event message */ + current->seccomp.ptrace_skip = false; ptrace_event(PTRACE_EVENT_SECCOMP, data); /* * The delivery of a fatal signal during event @@ -1319,7 +1320,15 @@ static bool __seccomp_filter(int this_syscall, const bool recheck_after_trace) */ if (fatal_signal_pending(current)) goto skip; - /* Check if the tracer forced the syscall to be skipped. */ + /* + * Check if the tracer forced the syscall to be skipped, either + * with seccomp_ptrace_skip_syscall(), which does not depend on + * how the architecture encodes the syscall number and the + * return value in the registers, or by setting the syscall + * number to -1. + */ + if (current->seccomp.ptrace_skip) + goto skip; this_syscall = syscall_get_nr(current, current_pt_regs()); if (this_syscall < 0) goto skip; -- 2.55.0