mirror of
https://github.com/torvalds/linux.git
synced 2026-09-14 16:10:02 +02:00
Generic entry code updates:
- Make syscall user dispatching configurable
Not all architectures can makes use of syscall user dispatching. Allow
them to disable the feature completely.
- Consolidate stack randomization for the generic entry code and the
architectures using it.
Stack randomization on syscall entry was sprinkled throughout the
architecture specific low level entry code and in some cases at the
wrong points, e.g. before establishing state, which violates the
non-instrumentable constraints of that code.
Clean this up by integrating stack randomization into the generic entry
code helpers so that it is invoked at the earliest possible point right
after establishing state and converting all generic entry code using
architecture over.
- Clean up the syscall number handling in the generic entry code. It
works correctly for architectures which have a separate return value
storage in pt_regs, but fails to distinguish the case where user space
handed in -1 as syscall number from the case where the entry code
rejects it by returning -1 to the callers. Aside of that the return
value functionality of those interfaces is not really intuitive.
Fix this by separating the decision to reject a syscall (user dispatch,
ptrace, seccomp ...) from the potential modification of the syscall
number through these mechanisms.
This solves most of the problems for architectures which do not have a
separate return value storage in pt_regs except for the case where a
tracepoint has a BPF script or a probe attached which overwrite both
the syscall number and the return value. But that's a problem which
cannot be solved in the generic code, that only can be addressed by
separating the storage model in the affected architectures.
-----BEGIN PGP SIGNATURE-----
iQJEBAABCgAuFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmqCs10QHHRnbHhAa2Vy
bmVsLm9yZwAKCRCmGPVMDXSYoaf6D/0ZBG1Yb0/C/6lrI185qPu38aGOROuAcxP+
RV1O1x6C83w2hCLBH8LeswY2x4/iGbdftne/hfmvu8eNCE5MzBfYvXhLL4If75Tc
IJ6C8uummnDmrT1TFuWHryTAfjyF28gt0+GGq0Zy5Hyz9b4CTJqOMx5u6KV4cZuJ
odoNQpE/GlWo40wCSTYP/Tt5xONrogk2pMQtFyV8JEoaXkdYSj/V815yojEmofYU
fmgPPO5/vOnZzE4b29gZyndXnU1Boah7r1l5fg7c9za376yCEEzh/ApPhovHyY0A
t8zjnrtooZ27IUKbcsyycrAM14asfcmViDNDgaCj8ttBioQaCnxO1BpKWjVxEZhE
AbM6q3Q66ER4Df6GNhZjPqT5Lr7E7+vLLarhXLWztsGQklIx4AFbrsa73hA20UC9
1PSeMd45JSxH3yA8vMauXAGHFK1tD1V8Lgofu69+2Z3jtKB+aU0fqWeL1jesSEM0
oCGhUb3hIC1pz3KVA0MGmNTm0yyQJYTGZL7wADYNV5NbxJVqXgo37qa/0n94Gf/4
TG3OwY4Sb/H/sve7v/eY4IvxVh+xs3dLZP8ZoqMlPCp9JIxc6iNoe6VHqPI7PFnM
fXwDtsy+bRF/SKnB/32qxnR7UJqmdNH3XIjd+lXWliKt6UYoC79/MEKN5DmJcO9P
CykZUWa72A==
=XUd9
-----END PGP SIGNATURE-----
Merge tag 'core-entry-2026-08-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull generic entry code updates from Thomas Gleixner:
- Make syscall user dispatching configurable
Not all architectures can makes use of syscall user dispatching.
Allow them to disable the feature completely.
- Consolidate stack randomization for the generic entry code and the
architectures using it.
Stack randomization on syscall entry was sprinkled throughout the
architecture specific low level entry code and in some cases at the
wrong points, e.g. before establishing state, which violates the
non-instrumentable constraints of that code.
Clean this up by integrating stack randomization into the generic
entry code helpers so that it is invoked at the earliest possible
point right after establishing state and converting all generic entry
code using architecture over.
- Clean up the syscall number handling in the generic entry code. It
works correctly for architectures which have a separate return value
storage in pt_regs, but fails to distinguish the case where user
space handed in -1 as syscall number from the case where the entry
code rejects it by returning -1 to the callers. Aside of that the
return value functionality of those interfaces is not really
intuitive.
Fix this by separating the decision to reject a syscall (user
dispatch, ptrace, seccomp ...) from the potential modification of the
syscall number through these mechanisms.
This solves most of the problems for architectures which do not have
a separate return value storage in pt_regs except for the case where
a tracepoint has a BPF script or a probe attached which overwrite
both the syscall number and the return value. But that's a problem
which cannot be solved in the generic code, that only can be
addressed by separating the storage model in the affected
architectures.
* tag 'core-entry-2026-08-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (23 commits)
entry, treewide: Make syscall_enter_from_user_mode[_work]() indicate syscall execution
entry: Make return type of syscall_trace_enter() bool
entry: Rework trace_syscall_enter()
entry: Rework syscall_audit_enter()
syscall_user_dispatch: Introduce ARCH_SUPPORTS_SYSCALL_USER_DISPATCH
entry: Fix seccomp bypass after ptrace with TSYNC
x86/entry: Simplify the syscall number logic
x86/entry: Get rid of the sys_ni_syscall() indirection
x86/entry: Make syscall functions static
ptrace, treewide: Rename ptrace_report_syscall_entry() to ptrace_report_syscall_permit_entry()
seccomp, treewide: Rename and convert __secure_computing() to return boolean
entry: Use syscall number instead of rereading it
entry: Remove syscall_enter_from_user_mode()
x86/syscall: Use [syscall_]enter_from_user_mode_randomize_stack()
s390/syscall: Use enter_from_user_mode_randomize_stack()
riscv/syscall: Use syscall_enter_from_user_mode_randomize_stack()
powerpc/syscall: Use syscall_enter_from_user_mode_randomize_stack()
loongarch/syscall: Use syscall_enter_from_user_mode_randomize_stack()
entry: Provide [syscall_]enter_from_user_mode_randomize_stack()
randomize_kstack: Provide add_random_kstack_offset_irqsoff()
...
This commit is contained in:
commit
3424d8c18a
|
|
@ -1402,6 +1402,23 @@ Note that if you change this from 0 to 1, already created segments
|
|||
without users and with a dead originative process will be destroyed.
|
||||
|
||||
|
||||
syscall_user_dispatch
|
||||
=====================
|
||||
|
||||
Controls whether userspace may arm Syscall User Dispatch via
|
||||
``prctl(PR_SET_SYSCALL_USER_DISPATCH, ...)`` or the
|
||||
``PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG`` ptrace request:
|
||||
|
||||
== ===================================================================
|
||||
0 Arming syscall user dispatch is denied with ``-EPERM``. Tasks that
|
||||
already armed it keep it, and disabling it is always permitted.
|
||||
1 (default) Arming syscall user dispatch is permitted.
|
||||
== ===================================================================
|
||||
|
||||
Only present when the kernel is built with ``CONFIG_SYSCALL_USER_DISPATCH``
|
||||
and ``CONFIG_PROC_SYSCTL``.
|
||||
|
||||
|
||||
sysctl_writes_strict
|
||||
====================
|
||||
|
||||
|
|
|
|||
|
|
@ -58,32 +58,59 @@ state transitions must run with interrupts disabled.
|
|||
Syscalls
|
||||
--------
|
||||
|
||||
Syscall-entry code starts in assembly code and calls out into low-level C code
|
||||
after establishing low-level architecture-specific state and stack frames. This
|
||||
low-level C code must not be instrumented. A typical syscall handling function
|
||||
invoked from low-level assembly code looks like this:
|
||||
Syscall-entry code starts in assembly code and calls out into low-level C
|
||||
code after establishing low-level architecture-specific state and stack
|
||||
frames. This low-level C code must not be instrumented. The recommended
|
||||
syscall handling function invoked from low-level assembly code looks like
|
||||
this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
noinstr void syscall(struct pt_regs *regs, int nr)
|
||||
noinstr void syscall(struct pt_regs *regs, long nr)
|
||||
{
|
||||
arch_syscall_enter(regs);
|
||||
nr = syscall_enter_from_user_mode(regs, nr);
|
||||
|
||||
instrumentation_begin();
|
||||
if (!invoke_syscall(regs, nr) && nr != -1)
|
||||
result_reg(regs) = __sys_ni_syscall(regs);
|
||||
instrumentation_end();
|
||||
|
||||
result_reg(regs) = -ENOSYS;
|
||||
if (syscall_enter_from_user_mode_randomize_stack(regs, &nr)) {
|
||||
instrumentation_begin();
|
||||
if (valid(nr)
|
||||
result_reg(regs) = invoke_syscall(regs, nr);
|
||||
instrumentation_end();
|
||||
}
|
||||
syscall_exit_to_user_mode(regs);
|
||||
}
|
||||
|
||||
syscall_enter_from_user_mode() first invokes enter_from_user_mode() which
|
||||
establishes state in the following order:
|
||||
This is the most resilent variant as it has always a guaranteed valid
|
||||
return code. The alternative variant is:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
noinstr void syscall(struct pt_regs *regs, long nr)
|
||||
{
|
||||
arch_syscall_enter(regs);
|
||||
if (syscall_enter_from_user_mode_randomize_stack(regs, &nr)) {
|
||||
instrumentation_begin();
|
||||
if (valid(nr)
|
||||
result_reg(regs) = invoke_syscall(regs, nr);
|
||||
else
|
||||
result_reg(regs) = -ENOSYS;
|
||||
instrumentation_end();
|
||||
}
|
||||
syscall_exit_to_user_mode(regs);
|
||||
}
|
||||
|
||||
That works for most situations except when a probe/BPF attached to the
|
||||
syscall tracepoint sets an invalid syscall number e.g. -1 and also modifies
|
||||
the result register. So this variant will obviously overwrite the modified
|
||||
result with -ENOSYS.
|
||||
|
||||
syscall_enter_from_user_mode_randomize_stack() first invokes
|
||||
enter_from_user_mode_randomize_stack() which establishes state in the
|
||||
following order:
|
||||
|
||||
* Lockdep
|
||||
* RCU / Context tracking
|
||||
* Tracing
|
||||
* Apply stack randomization
|
||||
|
||||
and then invokes the various entry work functions like ptrace, seccomp, audit,
|
||||
syscall tracing, etc. After all that is done, the instrumentable invoke_syscall
|
||||
|
|
@ -99,10 +126,11 @@ transition in the reverse order:
|
|||
* RCU / Context tracking
|
||||
* Lockdep
|
||||
|
||||
syscall_enter_from_user_mode() and syscall_exit_to_user_mode() are also
|
||||
available as fine grained subfunctions in cases where the architecture code
|
||||
has to do extra work between the various steps. In such cases it has to
|
||||
ensure that enter_from_user_mode() is called first on entry and
|
||||
syscall_enter_from_user_mode_randomize_stack() and
|
||||
syscall_exit_to_user_mode() are also available as fine grained subfunctions
|
||||
in cases where the architecture code has to do extra work between the
|
||||
various steps. In such cases it has to ensure that
|
||||
enter_from_user_mode_randomize_stack() is called first on entry and
|
||||
exit_to_user_mode() is called last on exit.
|
||||
|
||||
Do not nest syscalls. Nested syscalls will cause RCU and/or context tracking
|
||||
|
|
|
|||
16
arch/Kconfig
16
arch/Kconfig
|
|
@ -114,6 +114,20 @@ config GENERIC_ENTRY
|
|||
select GENERIC_IRQ_ENTRY
|
||||
select GENERIC_SYSCALL
|
||||
|
||||
config ARCH_SUPPORTS_SYSCALL_USER_DISPATCH
|
||||
bool
|
||||
|
||||
config SYSCALL_USER_DISPATCH
|
||||
bool "Syscall User Dispatch"
|
||||
depends on ARCH_SUPPORTS_SYSCALL_USER_DISPATCH
|
||||
depends on GENERIC_ENTRY
|
||||
default y
|
||||
help
|
||||
Syscall User Dispatch lets a thread have its own system calls
|
||||
intercepted and redirected to a userspace signal handler based
|
||||
on a prctl() configured instruction pointer range.
|
||||
If unsure, say Y.
|
||||
|
||||
config KPROBES
|
||||
bool "Kprobes"
|
||||
depends on HAVE_KPROBES
|
||||
|
|
@ -637,7 +651,7 @@ config HAVE_ARCH_SECCOMP_FILTER
|
|||
- syscall_set_return_value()
|
||||
- SIGSYS siginfo_t support
|
||||
- secure_computing is called from a ptrace_event()-safe context
|
||||
- secure_computing return value is checked and a return value of -1
|
||||
- secure_computing return value is checked and if false it
|
||||
results in the system call being skipped immediately.
|
||||
- seccomp syscall wired up
|
||||
- if !HAVE_SPARSE_SYSCALL_NR, have SECCOMP_ARCH_NATIVE,
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@ asmlinkage unsigned long syscall_trace_enter(void)
|
|||
struct pt_regs *regs = current_pt_regs();
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
|
||||
ptrace_report_syscall_entry(regs)) {
|
||||
!ptrace_report_syscall_permit_entry(regs)) {
|
||||
syscall_set_nr(current, regs, -1);
|
||||
if (regs->r19 == 0 && regs->r0 == (unsigned long)-1)
|
||||
syscall_set_return_value(current, regs, -ENOSYS, 0);
|
||||
|
|
@ -387,7 +387,7 @@ asmlinkage unsigned long syscall_trace_enter(void)
|
|||
* If this fails, seccomp may already have set up the return value
|
||||
* (e.g. SECCOMP_RET_ERRNO / TRACE).
|
||||
*/
|
||||
if (secure_computing() == -1) {
|
||||
if (!seccomp_permit_syscall()) {
|
||||
if (regs->r19 == 0 && regs->r0 == (unsigned long)-1)
|
||||
syscall_set_return_value(current, regs, -ENOSYS, 0);
|
||||
syscall_set_nr(current, regs, -1);
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ long arch_ptrace(struct task_struct *child, long request,
|
|||
asmlinkage int syscall_trace_enter(struct pt_regs *regs)
|
||||
{
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
if (ptrace_report_syscall_entry(regs))
|
||||
if (!ptrace_report_syscall_permit_entry(regs))
|
||||
return ULONG_MAX;
|
||||
|
||||
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
||||
|
|
|
|||
|
|
@ -840,7 +840,7 @@ static void report_syscall(struct pt_regs *regs, enum ptrace_syscall_dir dir)
|
|||
|
||||
if (dir == PTRACE_SYSCALL_EXIT)
|
||||
ptrace_report_syscall_exit(regs, 0);
|
||||
else if (ptrace_report_syscall_entry(regs))
|
||||
else if (!ptrace_report_syscall_permit_entry(regs))
|
||||
current_thread_info()->abi_syscall = -1;
|
||||
|
||||
regs->ARM_ip = ip;
|
||||
|
|
@ -855,7 +855,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
|
|||
|
||||
/* Do seccomp after ptrace; syscall may have changed. */
|
||||
#ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
|
||||
if (secure_computing() == -1)
|
||||
if (!seccomp_permit_syscall())
|
||||
return -1;
|
||||
#else
|
||||
/* XXX: remove this once OABI gets fixed */
|
||||
|
|
|
|||
|
|
@ -2429,7 +2429,7 @@ static int report_syscall_entry(struct pt_regs *regs)
|
|||
int regno, ret;
|
||||
|
||||
saved_reg = ptrace_save_reg(regs, PTRACE_SYSCALL_ENTER, ®no);
|
||||
ret = ptrace_report_syscall_entry(regs);
|
||||
ret = !ptrace_report_syscall_permit_entry(regs);
|
||||
if (ret)
|
||||
forget_syscall(regs);
|
||||
regs->regs[regno] = saved_reg;
|
||||
|
|
@ -2470,7 +2470,7 @@ int syscall_trace_enter(struct pt_regs *regs)
|
|||
}
|
||||
|
||||
/* Do the secure computing after ptrace; failures should be fast. */
|
||||
if (secure_computing() == -1)
|
||||
if (!seccomp_permit_syscall())
|
||||
return NO_SYSCALL;
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
|
||||
|
|
|
|||
|
|
@ -320,10 +320,10 @@ long arch_ptrace(struct task_struct *child, long request,
|
|||
asmlinkage int syscall_trace_enter(struct pt_regs *regs)
|
||||
{
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
if (ptrace_report_syscall_entry(regs))
|
||||
if (!ptrace_report_syscall_permit_entry(regs))
|
||||
return -1;
|
||||
|
||||
if (secure_computing() == -1)
|
||||
if (!seccomp_permit_syscall())
|
||||
return -1;
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ void do_trap0(struct pt_regs *regs)
|
|||
|
||||
/* allow strace to catch syscall args */
|
||||
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACE) &&
|
||||
ptrace_report_syscall_entry(regs)))
|
||||
!ptrace_report_syscall_permit_entry(regs)))
|
||||
return; /* return -ENOSYS somewhere? */
|
||||
|
||||
/* Interrupts should be re-enabled for syscall processing */
|
||||
|
|
|
|||
|
|
@ -84,10 +84,4 @@ static inline int syscall_get_arch(struct task_struct *task)
|
|||
return AUDIT_ARCH_LOONGARCH64;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* __ASM_LOONGARCH_SYSCALL_H */
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/linkage.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/randomize_kstack.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
|
|
@ -58,8 +57,8 @@ typedef long (*sys_call_fn)(unsigned long, unsigned long,
|
|||
|
||||
void noinstr __no_stack_protector do_syscall(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long nr;
|
||||
sys_call_fn syscall_fn;
|
||||
unsigned long nr;
|
||||
|
||||
nr = regs->regs[11];
|
||||
/* Set for syscall restarting */
|
||||
|
|
@ -70,14 +69,12 @@ void noinstr __no_stack_protector do_syscall(struct pt_regs *regs)
|
|||
regs->orig_a0 = regs->regs[4];
|
||||
regs->regs[4] = -ENOSYS;
|
||||
|
||||
nr = syscall_enter_from_user_mode(regs, nr);
|
||||
|
||||
add_random_kstack_offset();
|
||||
|
||||
if (nr < NR_syscalls) {
|
||||
syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)];
|
||||
regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6],
|
||||
regs->regs[7], regs->regs[8], regs->regs[9]);
|
||||
if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &nr))) {
|
||||
if (nr < NR_syscalls) {
|
||||
syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)];
|
||||
regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6],
|
||||
regs->regs[7], regs->regs[8], regs->regs[9]);
|
||||
}
|
||||
}
|
||||
|
||||
syscall_exit_to_user_mode(regs);
|
||||
|
|
|
|||
|
|
@ -279,9 +279,9 @@ asmlinkage int syscall_trace_enter(void)
|
|||
int ret = 0;
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
ret = ptrace_report_syscall_entry(task_pt_regs(current));
|
||||
ret = !ptrace_report_syscall_permit_entry(task_pt_regs(current));
|
||||
|
||||
if (secure_computing() == -1)
|
||||
if (!seccomp_permit_syscall())
|
||||
return -1;
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ asmlinkage unsigned long do_syscall_trace_enter(struct pt_regs *regs)
|
|||
secure_computing_strict(regs->r12);
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
|
||||
ptrace_report_syscall_entry(regs))
|
||||
!ptrace_report_syscall_permit_entry(regs))
|
||||
/*
|
||||
* Tracing decided this syscall should not happen.
|
||||
* We'll return a bogus call number to get an ENOSYS
|
||||
|
|
|
|||
|
|
@ -1324,11 +1324,11 @@ asmlinkage long syscall_trace_enter(struct pt_regs *regs)
|
|||
user_exit();
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE)) {
|
||||
if (ptrace_report_syscall_entry(regs))
|
||||
if (!ptrace_report_syscall_permit_entry(regs))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (secure_computing())
|
||||
if (!seccomp_permit_syscall())
|
||||
return -1;
|
||||
|
||||
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ asmlinkage int do_syscall_trace_enter(void)
|
|||
int ret = 0;
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
ret = ptrace_report_syscall_entry(task_pt_regs(current));
|
||||
ret = !ptrace_report_syscall_permit_entry(task_pt_regs(current));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
|
|||
long ret = 0;
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
|
||||
ptrace_report_syscall_entry(regs))
|
||||
!ptrace_report_syscall_permit_entry(regs))
|
||||
/*
|
||||
* Tracing decided this syscall should not happen.
|
||||
* We'll return a bogus call number to get an ENOSYS
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
|||
long do_syscall_trace_enter(struct pt_regs *regs)
|
||||
{
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE)) {
|
||||
int rc = ptrace_report_syscall_entry(regs);
|
||||
bool permit = ptrace_report_syscall_permit_entry(regs);
|
||||
|
||||
/*
|
||||
* As tracesys_next does not set %r28 to -ENOSYS
|
||||
|
|
@ -334,12 +334,10 @@ long do_syscall_trace_enter(struct pt_regs *regs)
|
|||
*/
|
||||
regs->gr[28] = -ENOSYS;
|
||||
|
||||
if (rc) {
|
||||
if (!permit) {
|
||||
/*
|
||||
* A nonzero return code from
|
||||
* ptrace_report_syscall_entry() tells us
|
||||
* to prevent the syscall execution. Skip
|
||||
* the syscall call and the syscall restart handling.
|
||||
* Skip the syscall call and the syscall restart
|
||||
* handling.
|
||||
*
|
||||
* Note that the tracer may also just change
|
||||
* regs->gr[20] to an invalid syscall number,
|
||||
|
|
@ -351,7 +349,7 @@ long do_syscall_trace_enter(struct pt_regs *regs)
|
|||
}
|
||||
|
||||
/* Do the secure computing check after ptrace. */
|
||||
if (secure_computing() == -1)
|
||||
if (!seccomp_permit_syscall())
|
||||
return -1;
|
||||
|
||||
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
||||
|
|
|
|||
|
|
@ -145,9 +145,4 @@ static inline int syscall_get_arch(struct task_struct *task)
|
|||
else
|
||||
return AUDIT_ARCH_PPC64;
|
||||
}
|
||||
|
||||
static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* _ASM_SYSCALL_H */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <linux/compat.h>
|
||||
#include <linux/context_tracking.h>
|
||||
#include <linux/randomize_kstack.h>
|
||||
#include <linux/entry-common.h>
|
||||
|
||||
#include <asm/interrupt.h>
|
||||
|
|
@ -19,8 +18,8 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
|
|||
long ret;
|
||||
syscall_fn f;
|
||||
|
||||
add_random_kstack_offset();
|
||||
r0 = syscall_enter_from_user_mode(regs, r0);
|
||||
if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0)))
|
||||
return syscall_get_error(current, regs);
|
||||
|
||||
if (unlikely(test_and_clear_thread_flag(TIF_SYSCALL_RET)))
|
||||
return syscall_get_error(current, regs);
|
||||
|
|
|
|||
|
|
@ -112,11 +112,6 @@ static inline void syscall_handler(struct pt_regs *regs, ulong syscall)
|
|||
regs->a0 = fn(regs);
|
||||
}
|
||||
|
||||
static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
asmlinkage long sys_riscv_flush_icache(uintptr_t, uintptr_t, uintptr_t);
|
||||
|
||||
asmlinkage long sys_riscv_hwprobe(struct riscv_hwprobe *, size_t, size_t,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/randomize_kstack.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/sched/signal.h>
|
||||
|
|
@ -333,15 +332,12 @@ void do_trap_ecall_u(struct pt_regs *regs)
|
|||
|
||||
riscv_v_vstate_discard(regs);
|
||||
|
||||
syscall = syscall_enter_from_user_mode(regs, syscall);
|
||||
|
||||
add_random_kstack_offset();
|
||||
|
||||
if (syscall >= 0 && syscall < NR_syscalls) {
|
||||
syscall = array_index_nospec(syscall, NR_syscalls);
|
||||
syscall_handler(regs, syscall);
|
||||
if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &syscall))) {
|
||||
if (syscall >= 0 && syscall < NR_syscalls) {
|
||||
syscall = array_index_nospec(syscall, NR_syscalls);
|
||||
syscall_handler(regs, syscall);
|
||||
}
|
||||
}
|
||||
|
||||
syscall_exit_to_user_mode(regs);
|
||||
} else {
|
||||
irqentry_state_t state = irqentry_nmi_enter(regs);
|
||||
|
|
|
|||
|
|
@ -89,11 +89,6 @@ static inline int syscall_get_arch(struct task_struct *task)
|
|||
return AUDIT_ARCH_S390X;
|
||||
}
|
||||
|
||||
static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#define SYSCALL_FMT_0
|
||||
#define SYSCALL_FMT_1 , "0" (r2)
|
||||
#define SYSCALL_FMT_2 , "d" (r3) SYSCALL_FMT_1
|
||||
|
|
|
|||
|
|
@ -96,9 +96,10 @@ SYSCALL_DEFINE0(ni_syscall)
|
|||
void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
|
||||
{
|
||||
unsigned long nr;
|
||||
bool permit;
|
||||
|
||||
enter_from_user_mode_randomize_stack(regs);
|
||||
|
||||
enter_from_user_mode(regs);
|
||||
add_random_kstack_offset();
|
||||
regs->psw = get_lowcore()->svc_old_psw;
|
||||
regs->int_code = get_lowcore()->svc_int_code;
|
||||
update_timer_sys();
|
||||
|
|
@ -121,7 +122,9 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
|
|||
regs->psw.addr = current->restart_block.arch_data;
|
||||
current->restart_block.arch_data = 1;
|
||||
}
|
||||
nr = syscall_enter_from_user_mode_work(regs, nr);
|
||||
|
||||
permit = syscall_enter_from_user_mode_work(regs, &nr);
|
||||
|
||||
/*
|
||||
* In the s390 ptrace ABI, both the syscall number and the return value
|
||||
* use gpr2. However, userspace puts the syscall number either in the
|
||||
|
|
@ -129,7 +132,7 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
|
|||
* work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
|
||||
* and if set, the syscall will be skipped.
|
||||
*/
|
||||
if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)))
|
||||
if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET) || !permit))
|
||||
goto out;
|
||||
regs->gprs[2] = -ENOSYS;
|
||||
if (likely(nr < NR_syscalls)) {
|
||||
|
|
|
|||
|
|
@ -455,12 +455,12 @@ long arch_ptrace(struct task_struct *child, long request,
|
|||
asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
|
||||
{
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
|
||||
ptrace_report_syscall_entry(regs)) {
|
||||
!ptrace_report_syscall_permit_entry(regs)) {
|
||||
regs->regs[0] = -ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (secure_computing() == -1)
|
||||
if (!seccomp_permit_syscall())
|
||||
return -1;
|
||||
|
||||
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
|
||||
|
|
|
|||
|
|
@ -441,7 +441,7 @@ asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p)
|
|||
if (syscall_exit_p)
|
||||
ptrace_report_syscall_exit(regs, 0);
|
||||
else
|
||||
ret = ptrace_report_syscall_entry(regs);
|
||||
ret = !ptrace_report_syscall_permit_entry(regs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -1093,7 +1093,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
|
|||
user_exit();
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
ret = ptrace_report_syscall_entry(regs);
|
||||
ret = !ptrace_report_syscall_permit_entry(regs);
|
||||
|
||||
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
|
||||
trace_sys_enter(regs, regs->u_regs[UREG_G1]);
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ int syscall_trace_enter(struct pt_regs *regs)
|
|||
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
return 0;
|
||||
|
||||
return ptrace_report_syscall_entry(regs);
|
||||
return !ptrace_report_syscall_permit_entry(regs);
|
||||
}
|
||||
|
||||
void syscall_trace_leave(struct pt_regs *regs)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ void handle_syscall(struct uml_pt_regs *r)
|
|||
goto out;
|
||||
|
||||
/* Do the seccomp check after ptrace; failures should be fast. */
|
||||
if (secure_computing() == -1)
|
||||
if (!seccomp_permit_syscall())
|
||||
goto out;
|
||||
|
||||
syscall = UPT_SYSCALL_NR(r);
|
||||
|
|
|
|||
|
|
@ -336,6 +336,7 @@ config X86
|
|||
select SCHED_SMT if SMP
|
||||
select ARCH_SUPPORTS_SCHED_CLUSTER if SMP
|
||||
select ARCH_SUPPORTS_SCHED_MC if SMP
|
||||
select ARCH_SUPPORTS_SYSCALL_USER_DISPATCH
|
||||
select HAVE_SINGLE_FTRACE_DIRECT_OPS if X86_64 && DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
||||
|
||||
config INSTRUCTION_DECODER
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ const sys_call_ptr_t sys_call_table[] = {
|
|||
#endif
|
||||
|
||||
#define __SYSCALL(nr, sym) case nr: return __ia32_##sym(regs);
|
||||
long ia32_sys_call(const struct pt_regs *regs, unsigned int nr)
|
||||
|
||||
/* The unsigned int @nr argument is intentional as it creates denser code in a 64-bit build */
|
||||
static noinline long ia32_sys_call(const struct pt_regs *regs, unsigned int nr)
|
||||
{
|
||||
switch (nr) {
|
||||
#include <asm/syscalls_32.h>
|
||||
|
|
@ -49,7 +51,7 @@ long ia32_sys_call(const struct pt_regs *regs, unsigned int nr)
|
|||
}
|
||||
}
|
||||
|
||||
static __always_inline int syscall_32_enter(struct pt_regs *regs)
|
||||
static __always_inline long syscall_32_enter(struct pt_regs *regs)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_IA32_EMULATION))
|
||||
current_thread_info()->status |= TS_COMPAT;
|
||||
|
|
@ -70,19 +72,11 @@ early_param("ia32_emulation", ia32_emulation_override_cmdline);
|
|||
/*
|
||||
* Invoke a 32-bit syscall. Called with IRQs on in CT_STATE_KERNEL.
|
||||
*/
|
||||
static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs, int nr)
|
||||
static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs, unsigned long nr)
|
||||
{
|
||||
/*
|
||||
* Convert negative numbers to very high and thus out of range
|
||||
* numbers for comparisons.
|
||||
*/
|
||||
unsigned int unr = nr;
|
||||
|
||||
if (likely(unr < IA32_NR_syscalls)) {
|
||||
unr = array_index_nospec(unr, IA32_NR_syscalls);
|
||||
regs->ax = ia32_sys_call(regs, unr);
|
||||
} else if (nr != -1) {
|
||||
regs->ax = __ia32_sys_ni_syscall(regs);
|
||||
if (likely(nr < IA32_NR_syscalls)) {
|
||||
nr = array_index_nospec(nr, IA32_NR_syscalls);
|
||||
regs->ax = ia32_sys_call(regs, (unsigned int)nr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -128,7 +122,7 @@ static __always_inline bool int80_is_external(void)
|
|||
*/
|
||||
__visible noinstr void do_int80_emulation(struct pt_regs *regs)
|
||||
{
|
||||
int nr;
|
||||
long nr;
|
||||
|
||||
/* Kernel does not use INT $0x80! */
|
||||
if (unlikely(!user_mode(regs))) {
|
||||
|
|
@ -142,10 +136,9 @@ __visible noinstr void do_int80_emulation(struct pt_regs *regs)
|
|||
* int80_is_external() below which calls into the APIC driver.
|
||||
* Identical for soft and external interrupts.
|
||||
*/
|
||||
enter_from_user_mode(regs);
|
||||
enter_from_user_mode_randomize_stack(regs);
|
||||
|
||||
instrumentation_begin();
|
||||
add_random_kstack_offset();
|
||||
|
||||
/* Validate that this is a soft interrupt to the extent possible */
|
||||
if (unlikely(int80_is_external()))
|
||||
|
|
@ -168,8 +161,9 @@ __visible noinstr void do_int80_emulation(struct pt_regs *regs)
|
|||
nr = syscall_32_enter(regs);
|
||||
|
||||
local_irq_enable();
|
||||
nr = syscall_enter_from_user_mode_work(regs, nr);
|
||||
do_syscall_32_irqs_on(regs, nr);
|
||||
|
||||
if (likely(syscall_enter_from_user_mode_work(regs, &nr)))
|
||||
do_syscall_32_irqs_on(regs, nr);
|
||||
|
||||
instrumentation_end();
|
||||
syscall_exit_to_user_mode(regs);
|
||||
|
|
@ -208,13 +202,11 @@ __visible noinstr void do_int80_emulation(struct pt_regs *regs)
|
|||
*/
|
||||
DEFINE_FREDENTRY_RAW(int80_emulation)
|
||||
{
|
||||
int nr;
|
||||
long nr;
|
||||
|
||||
enter_from_user_mode(regs);
|
||||
enter_from_user_mode_randomize_stack(regs);
|
||||
|
||||
instrumentation_begin();
|
||||
add_random_kstack_offset();
|
||||
|
||||
/*
|
||||
* FRED pushed 0 into regs::orig_ax and regs::ax contains the
|
||||
* syscall number.
|
||||
|
|
@ -232,8 +224,8 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
|
|||
nr = syscall_32_enter(regs);
|
||||
|
||||
local_irq_enable();
|
||||
nr = syscall_enter_from_user_mode_work(regs, nr);
|
||||
do_syscall_32_irqs_on(regs, nr);
|
||||
if (likely(syscall_enter_from_user_mode_work(regs, &nr)))
|
||||
do_syscall_32_irqs_on(regs, nr);
|
||||
|
||||
instrumentation_end();
|
||||
syscall_exit_to_user_mode(regs);
|
||||
|
|
@ -245,38 +237,32 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
|
|||
/* Handles int $0x80 on a 32bit kernel */
|
||||
__visible noinstr void do_int80_syscall_32(struct pt_regs *regs)
|
||||
{
|
||||
int nr = syscall_32_enter(regs);
|
||||
long nr = syscall_32_enter(regs);
|
||||
|
||||
/*
|
||||
* Subtlety here: if ptrace pokes something larger than 2^31-1 into
|
||||
* orig_ax, the int return value truncates it. This matches
|
||||
* the semantics of syscall_get_nr().
|
||||
*/
|
||||
nr = syscall_enter_from_user_mode(regs, nr);
|
||||
instrumentation_begin();
|
||||
if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &nr))) {
|
||||
instrumentation_begin();
|
||||
|
||||
add_random_kstack_offset();
|
||||
do_syscall_32_irqs_on(regs, nr);
|
||||
do_syscall_32_irqs_on(regs, nr);
|
||||
|
||||
instrumentation_end();
|
||||
instrumentation_end();
|
||||
}
|
||||
syscall_exit_to_user_mode(regs);
|
||||
}
|
||||
#endif /* !CONFIG_IA32_EMULATION */
|
||||
|
||||
static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)
|
||||
{
|
||||
int nr = syscall_32_enter(regs);
|
||||
long nr = syscall_32_enter(regs);
|
||||
int res;
|
||||
|
||||
/*
|
||||
* This cannot use syscall_enter_from_user_mode() as it has to
|
||||
* fetch EBP before invoking any of the syscall entry work
|
||||
* functions.
|
||||
*/
|
||||
enter_from_user_mode(regs);
|
||||
enter_from_user_mode_randomize_stack(regs);
|
||||
|
||||
instrumentation_begin();
|
||||
add_random_kstack_offset();
|
||||
local_irq_enable();
|
||||
/* Fetch EBP from where the vDSO stashed it. */
|
||||
if (IS_ENABLED(CONFIG_X86_64)) {
|
||||
|
|
@ -301,10 +287,8 @@ static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)
|
|||
return false;
|
||||
}
|
||||
|
||||
nr = syscall_enter_from_user_mode_work(regs, nr);
|
||||
|
||||
/* Now this is just like a normal syscall. */
|
||||
do_syscall_32_irqs_on(regs, nr);
|
||||
if (likely(syscall_enter_from_user_mode_work(regs, &nr)))
|
||||
do_syscall_32_irqs_on(regs, nr);
|
||||
|
||||
instrumentation_end();
|
||||
syscall_exit_to_user_mode(regs);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ const sys_call_ptr_t sys_call_table[] = {
|
|||
#undef __SYSCALL
|
||||
|
||||
#define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs);
|
||||
long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
|
||||
|
||||
/* The unsigned int @nr argument is intentional as it creates denser code */
|
||||
static noinline long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
|
||||
{
|
||||
switch (nr) {
|
||||
#include <asm/syscalls_64.h>
|
||||
|
|
@ -40,63 +42,50 @@ long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_X32_ABI
|
||||
long x32_sys_call(const struct pt_regs *regs, unsigned int nr)
|
||||
static noinline long x32_sys_call(const struct pt_regs *regs, unsigned int nr)
|
||||
{
|
||||
#ifdef CONFIG_X86_X32_ABI
|
||||
switch (nr) {
|
||||
#include <asm/syscalls_x32.h>
|
||||
default: return __x64_sys_ni_syscall(regs);
|
||||
}
|
||||
}
|
||||
#else
|
||||
return -ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr)
|
||||
static __always_inline bool do_syscall_x64(struct pt_regs *regs, unsigned long nr)
|
||||
{
|
||||
/*
|
||||
* Convert negative numbers to very high and thus out of range
|
||||
* numbers for comparisons.
|
||||
*/
|
||||
unsigned int unr = nr;
|
||||
|
||||
if (likely(unr < NR_syscalls)) {
|
||||
unr = array_index_nospec(unr, NR_syscalls);
|
||||
regs->ax = x64_sys_call(regs, unr);
|
||||
if (likely(nr < NR_syscalls)) {
|
||||
nr = array_index_nospec(nr, NR_syscalls);
|
||||
regs->ax = x64_sys_call(regs, (unsigned int)nr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
|
||||
static __always_inline void do_syscall_x32(struct pt_regs *regs, unsigned long nr)
|
||||
{
|
||||
/*
|
||||
* Adjust the starting offset of the table, and convert numbers
|
||||
* < __X32_SYSCALL_BIT to very high and thus out of range
|
||||
* numbers for comparisons.
|
||||
*/
|
||||
unsigned int xnr = nr - __X32_SYSCALL_BIT;
|
||||
/* Adjust the starting offset of the table */
|
||||
nr -= __X32_SYSCALL_BIT;
|
||||
|
||||
if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) {
|
||||
xnr = array_index_nospec(xnr, X32_NR_syscalls);
|
||||
regs->ax = x32_sys_call(regs, xnr);
|
||||
return true;
|
||||
if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(nr < X32_NR_syscalls)) {
|
||||
nr = array_index_nospec(nr, X32_NR_syscalls);
|
||||
regs->ax = x32_sys_call(regs, (unsigned int)nr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns true to return using SYSRET, or false to use IRET */
|
||||
__visible noinstr bool do_syscall_64(struct pt_regs *regs, int nr)
|
||||
__visible noinstr bool do_syscall_64(struct pt_regs *regs, long nr)
|
||||
{
|
||||
nr = syscall_enter_from_user_mode(regs, nr);
|
||||
if (likely(syscall_enter_from_user_mode_randomize_stack(regs, &nr))) {
|
||||
instrumentation_begin();
|
||||
|
||||
instrumentation_begin();
|
||||
add_random_kstack_offset();
|
||||
if (!do_syscall_x64(regs, nr))
|
||||
do_syscall_x32(regs, nr);
|
||||
|
||||
if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) {
|
||||
/* Invalid system call, but still a system call. */
|
||||
regs->ax = __x64_sys_ni_syscall(regs);
|
||||
instrumentation_end();
|
||||
}
|
||||
|
||||
instrumentation_end();
|
||||
syscall_exit_to_user_mode(regs);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -118,10 +118,10 @@ static bool write_ok_or_segv(unsigned long ptr, size_t size)
|
|||
|
||||
static bool __emulate_vsyscall(struct pt_regs *regs, unsigned long address)
|
||||
{
|
||||
unsigned long caller;
|
||||
int vsyscall_nr, syscall_nr, tmp;
|
||||
unsigned long caller, orig_dx;
|
||||
int vsyscall_nr, syscall_nr;
|
||||
bool skip;
|
||||
long ret;
|
||||
unsigned long orig_dx;
|
||||
|
||||
/* Confirm that the fault happened in 64-bit user mode */
|
||||
if (!user_64bit_mode(regs))
|
||||
|
|
@ -197,16 +197,16 @@ static bool __emulate_vsyscall(struct pt_regs *regs, unsigned long address)
|
|||
*/
|
||||
regs->orig_ax = syscall_nr;
|
||||
regs->ax = -ENOSYS;
|
||||
tmp = secure_computing();
|
||||
if ((!tmp && regs->orig_ax != syscall_nr) || regs->ip != address) {
|
||||
skip = !seccomp_permit_syscall();
|
||||
if ((!skip && regs->orig_ax != syscall_nr) || regs->ip != address) {
|
||||
warn_bad_vsyscall(KERN_DEBUG, regs,
|
||||
"seccomp tried to change syscall nr or ip");
|
||||
force_exit_sig(SIGSYS);
|
||||
return true;
|
||||
}
|
||||
regs->orig_ax = -1;
|
||||
if (tmp)
|
||||
goto do_ret; /* skip requested */
|
||||
if (skip)
|
||||
goto do_ret;
|
||||
|
||||
/*
|
||||
* With a real vsyscall, page faults cause SIGSEGV.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#ifndef _ASM_X86_ENTRY_COMMON_H
|
||||
#define _ASM_X86_ENTRY_COMMON_H
|
||||
|
||||
#include <linux/randomize_kstack.h>
|
||||
#include <linux/user-return-notifier.h>
|
||||
|
||||
#include <asm/nospec-branch.h>
|
||||
|
|
|
|||
|
|
@ -20,14 +20,6 @@
|
|||
typedef long (*sys_call_ptr_t)(const struct pt_regs *);
|
||||
extern const sys_call_ptr_t sys_call_table[];
|
||||
|
||||
/*
|
||||
* These may not exist, but still put the prototypes in so we
|
||||
* can use IS_ENABLED().
|
||||
*/
|
||||
extern long ia32_sys_call(const struct pt_regs *, unsigned int nr);
|
||||
extern long x32_sys_call(const struct pt_regs *, unsigned int nr);
|
||||
extern long x64_sys_call(const struct pt_regs *, unsigned int nr);
|
||||
|
||||
/*
|
||||
* Only the low 32 bits of orig_ax are meaningful, so we return int.
|
||||
* This importantly ignores the high bits on 64-bit, so comparisons
|
||||
|
|
@ -172,7 +164,7 @@ static inline int syscall_get_arch(struct task_struct *task)
|
|||
? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64;
|
||||
}
|
||||
|
||||
bool do_syscall_64(struct pt_regs *regs, int nr);
|
||||
bool do_syscall_64(struct pt_regs *regs, long nr);
|
||||
void do_int80_emulation(struct pt_regs *regs);
|
||||
|
||||
#endif /* CONFIG_X86_32 */
|
||||
|
|
|
|||
|
|
@ -547,14 +547,13 @@ int do_syscall_trace_enter(struct pt_regs *regs)
|
|||
regs->areg[2] = -ENOSYS;
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
|
||||
ptrace_report_syscall_entry(regs)) {
|
||||
!ptrace_report_syscall_permit_entry(regs)) {
|
||||
regs->areg[2] = -ENOSYS;
|
||||
regs->syscall = NO_SYSCALL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (regs->syscall == NO_SYSCALL ||
|
||||
secure_computing() == -1) {
|
||||
if (regs->syscall == NO_SYSCALL || !seccomp_permit_syscall()) {
|
||||
do_syscall_trace_leave(regs);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ void syscall_set_nr(struct task_struct *task, struct pt_regs *regs, int nr);
|
|||
*
|
||||
* It's only valid to call this when @task is stopped for system
|
||||
* call exit tracing (due to %SYSCALL_WORK_SYSCALL_TRACE or
|
||||
* %SYSCALL_WORK_SYSCALL_AUDIT), after ptrace_report_syscall_entry()
|
||||
* returned nonzero to prevent the system call from taking place.
|
||||
* %SYSCALL_WORK_SYSCALL_AUDIT), after ptrace_report_syscall_permit_entry()
|
||||
* returned False to prevent the system call from taking place.
|
||||
*
|
||||
* This rolls back the register state in @regs so it's as if the
|
||||
* system call instruction was a no-op. The registers containing
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@
|
|||
#include <linux/irq-entry-common.h>
|
||||
#include <linux/livepatch.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/randomize_kstack.h>
|
||||
#include <linux/resume_user_mode.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/syscall_user_dispatch.h>
|
||||
|
||||
#include <asm/entry-common.h>
|
||||
#include <asm/syscall.h>
|
||||
|
|
@ -18,7 +20,7 @@
|
|||
#endif
|
||||
|
||||
/*
|
||||
* SYSCALL_WORK flags handled in syscall_enter_from_user_mode()
|
||||
* SYSCALL_WORK flags handled in syscall_enter_from_user_mode_work()
|
||||
*/
|
||||
#define SYSCALL_WORK_ENTER (SYSCALL_WORK_SECCOMP | \
|
||||
SYSCALL_WORK_SYSCALL_TRACEPOINT | \
|
||||
|
|
@ -37,42 +39,32 @@
|
|||
SYSCALL_WORK_SYSCALL_EXIT_TRAP)
|
||||
|
||||
/**
|
||||
* arch_ptrace_report_syscall_entry - Architecture specific ptrace_report_syscall_entry() wrapper
|
||||
* arch_ptrace_report_syscall_permit_entry - Architecture specific wrapper for
|
||||
* ptrace_report_syscall_permit_entry()
|
||||
* @regs: Pointer to the register state at syscall entry
|
||||
*
|
||||
* Invoked from syscall_trace_enter() to wrap ptrace_report_syscall_entry().
|
||||
* Invoked from syscall_trace_enter() to wrap ptrace_report_syscall_permit_entry().
|
||||
*
|
||||
* This allows architecture specific ptrace_report_syscall_entry()
|
||||
* This allows architecture specific ptrace_report_syscall_permit_entry()
|
||||
* implementations. If not defined by the architecture this falls back to
|
||||
* to ptrace_report_syscall_entry().
|
||||
* to ptrace_report_syscall_permit_entry().
|
||||
*/
|
||||
static __always_inline int arch_ptrace_report_syscall_entry(struct pt_regs *regs);
|
||||
static __always_inline bool arch_ptrace_report_syscall_permit_entry(struct pt_regs *regs);
|
||||
|
||||
#ifndef arch_ptrace_report_syscall_entry
|
||||
static __always_inline int arch_ptrace_report_syscall_entry(struct pt_regs *regs)
|
||||
#ifndef arch_ptrace_report_syscall_permit_entry
|
||||
static __always_inline bool arch_ptrace_report_syscall_permit_entry(struct pt_regs *regs)
|
||||
{
|
||||
return ptrace_report_syscall_entry(regs);
|
||||
return ptrace_report_syscall_permit_entry(regs);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool syscall_user_dispatch(struct pt_regs *regs);
|
||||
long trace_syscall_enter(struct pt_regs *regs, long syscall);
|
||||
void trace_syscall_enter(struct pt_regs *regs);
|
||||
void trace_syscall_exit(struct pt_regs *regs, long ret);
|
||||
void syscall_enter_audit(struct pt_regs *regs);
|
||||
|
||||
static inline void syscall_enter_audit(struct pt_regs *regs, long syscall)
|
||||
static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work,
|
||||
long syscall)
|
||||
{
|
||||
if (unlikely(audit_context())) {
|
||||
unsigned long args[6];
|
||||
|
||||
syscall_get_arguments(current, regs, args);
|
||||
audit_syscall_entry(syscall, args[0], args[1], args[2], args[3]);
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work)
|
||||
{
|
||||
long syscall, ret = 0;
|
||||
|
||||
/*
|
||||
* Handle Syscall User Dispatch. This must comes first, since
|
||||
* the ABI here can be something that doesn't make sense for
|
||||
|
|
@ -80,7 +72,7 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
|
|||
*/
|
||||
if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
|
||||
if (syscall_user_dispatch(regs))
|
||||
return -1L;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -89,31 +81,31 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
|
|||
* through hrtimer_interrupt().
|
||||
*/
|
||||
if (work & SYSCALL_WORK_SYSCALL_RSEQ_SLICE)
|
||||
rseq_syscall_enter_work(syscall_get_nr(current, regs));
|
||||
rseq_syscall_enter_work(syscall);
|
||||
|
||||
/* Handle ptrace */
|
||||
if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
|
||||
ret = arch_ptrace_report_syscall_entry(regs);
|
||||
if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
|
||||
return -1L;
|
||||
if (!arch_ptrace_report_syscall_permit_entry(regs) ||
|
||||
(work & SYSCALL_WORK_SYSCALL_EMU))
|
||||
return false;
|
||||
|
||||
/* ptrace might have changed work flags */
|
||||
work = READ_ONCE(current_thread_info()->syscall_work);
|
||||
}
|
||||
|
||||
/* Do seccomp after ptrace, to catch any tracer changes. */
|
||||
if (work & SYSCALL_WORK_SECCOMP) {
|
||||
ret = __secure_computing();
|
||||
if (ret == -1L)
|
||||
return ret;
|
||||
if (!__seccomp_permit_syscall())
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Either of the above might have changed the syscall number */
|
||||
syscall = syscall_get_nr(current, regs);
|
||||
|
||||
if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT))
|
||||
syscall = trace_syscall_enter(regs, syscall);
|
||||
trace_syscall_enter(regs);
|
||||
|
||||
syscall_enter_audit(regs, syscall);
|
||||
if (unlikely(audit_context()))
|
||||
syscall_enter_audit(regs);
|
||||
|
||||
return ret ? : syscall;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -122,36 +114,63 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
|
|||
* @regs: Pointer to currents pt_regs
|
||||
* @syscall: The syscall number
|
||||
*
|
||||
* Invoked from architecture specific syscall entry code with interrupts
|
||||
* enabled after invoking enter_from_user_mode(), enabling interrupts and
|
||||
* extra architecture specific work.
|
||||
* Invoked from architecture specific syscall entry code with interrupts enabled
|
||||
* after invoking enter_from_user_mode(), enabling interrupts and extra
|
||||
* architecture specific work with the syscall return value preset to -ENOSYS.
|
||||
*
|
||||
* Returns: The original or a modified syscall number
|
||||
* Returns: True if the syscall should be invoked, False otherwise.
|
||||
*
|
||||
* If the returned syscall number is -1 then the syscall should be
|
||||
* skipped. In this case the caller may invoke syscall_set_error() or
|
||||
* syscall_set_return_value() first. If neither of those are called and -1
|
||||
* is returned, then the syscall will fail with ENOSYS.
|
||||
* If the return value is false, the caller must skip the syscall and leave the
|
||||
* syscall return value unmodified as it might have been set by one of the entry
|
||||
* work functions.
|
||||
*
|
||||
* It handles the following work items:
|
||||
*
|
||||
* 1) syscall_work flag dependent invocations of
|
||||
* ptrace_report_syscall_entry(), __secure_computing(), trace_sys_enter()
|
||||
* ptrace_report_syscall_permit_entry(), __seccomp_permit_syscall(), trace_sys_enter()
|
||||
* 2) Invocation of audit_syscall_entry()
|
||||
*/
|
||||
static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *regs, long syscall)
|
||||
static __always_inline bool syscall_enter_from_user_mode_work(struct pt_regs *regs, long *syscall)
|
||||
{
|
||||
unsigned long work = READ_ONCE(current_thread_info()->syscall_work);
|
||||
|
||||
if (work & SYSCALL_WORK_ENTER)
|
||||
syscall = syscall_trace_enter(regs, work);
|
||||
if (!(work & SYSCALL_WORK_ENTER))
|
||||
return true;
|
||||
|
||||
return syscall;
|
||||
if (unlikely(!syscall_trace_enter(regs, work, *syscall)))
|
||||
return false;
|
||||
|
||||
/* Reread the syscall number as it might have been modified */
|
||||
*syscall = syscall_get_nr(current, regs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* syscall_enter_from_user_mode - Establish state and check and handle work
|
||||
* before invoking a syscall
|
||||
* enter_from_user_mode_randomize_stack - Establish state and add stack randomization
|
||||
* before invoking syscall_enter_from_user_mode_work()
|
||||
* @regs: Pointer to currents pt_regs
|
||||
*
|
||||
* Invoked from architecture specific syscall entry code with interrupts
|
||||
* disabled. The calling code has to be non-instrumentable. When the function
|
||||
* returns all state is correct, interrupts are still disabled and the
|
||||
* subsequent functions can be instrumented.
|
||||
*
|
||||
* Implemented as a macro so that the stack randomization is effective
|
||||
* throughout the function in which it is invoked. An inline would only make it
|
||||
* effective in the scope of the inline function.
|
||||
*/
|
||||
#define enter_from_user_mode_randomize_stack(regs) \
|
||||
do { \
|
||||
enter_from_user_mode(regs); \
|
||||
instrumentation_begin(); \
|
||||
add_random_kstack_offset_irqsoff(); \
|
||||
instrumentation_end(); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* syscall_enter_from_user_mode_randomize_stack - Establish state and check and handle work
|
||||
* before invoking a syscall
|
||||
* @regs: Pointer to currents pt_regs
|
||||
* @syscall: The syscall number
|
||||
*
|
||||
|
|
@ -160,31 +179,32 @@ static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *re
|
|||
* function returns all state is correct, interrupts are enabled and the
|
||||
* subsequent functions can be instrumented.
|
||||
*
|
||||
* This is the combination of enter_from_user_mode() and
|
||||
* This is the combination of enter_from_user_mode_randomize_stack() and
|
||||
* syscall_enter_from_user_mode_work() to be used when there is no
|
||||
* architecture specific work to be done between the two.
|
||||
*
|
||||
* Returns: The original or a modified syscall number. See
|
||||
* syscall_enter_from_user_mode_work() for further explanation.
|
||||
*
|
||||
* Implemented as a macro to make stack randomization effective in the calling
|
||||
* scope.
|
||||
*/
|
||||
static __always_inline long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall)
|
||||
{
|
||||
long ret;
|
||||
|
||||
enter_from_user_mode(regs);
|
||||
|
||||
instrumentation_begin();
|
||||
local_irq_enable();
|
||||
ret = syscall_enter_from_user_mode_work(regs, syscall);
|
||||
instrumentation_end();
|
||||
|
||||
return ret;
|
||||
}
|
||||
#define syscall_enter_from_user_mode_randomize_stack(regs, syscall) \
|
||||
({ \
|
||||
enter_from_user_mode_randomize_stack(regs); \
|
||||
\
|
||||
instrumentation_begin(); \
|
||||
local_irq_enable(); \
|
||||
long _ret = syscall_enter_from_user_mode_work(regs, syscall); \
|
||||
instrumentation_end(); \
|
||||
\
|
||||
_ret; \
|
||||
})
|
||||
|
||||
/*
|
||||
* If SYSCALL_EMU is set, then the only reason to report is when
|
||||
* SINGLESTEP is set (i.e. PTRACE_SYSEMU_SINGLESTEP). This syscall
|
||||
* instruction has been already reported in syscall_enter_from_user_mode().
|
||||
* If SYSCALL_EMU is set, then the only reason to report is when SINGLESTEP is
|
||||
* set (i.e. PTRACE_SYSEMU_SINGLESTEP). This syscall instruction has been
|
||||
* already reported in syscall_enter_from_user_mode_work().
|
||||
*/
|
||||
static __always_inline bool report_single_step(unsigned long work)
|
||||
{
|
||||
|
|
@ -232,10 +252,8 @@ static __always_inline void syscall_exit_work(struct pt_regs *regs, unsigned lon
|
|||
* of these syscalls is unknown.
|
||||
*/
|
||||
if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
|
||||
if (unlikely(current->syscall_dispatch.on_dispatch)) {
|
||||
current->syscall_dispatch.on_dispatch = false;
|
||||
if (syscall_user_dispatch_clear_on_dispatch())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
audit_syscall_exit(regs);
|
||||
|
|
|
|||
|
|
@ -49,9 +49,9 @@
|
|||
* Defaults to an empty implementation. Can be replaced by architecture
|
||||
* specific code.
|
||||
*
|
||||
* Invoked from syscall_enter_from_user_mode() in the non-instrumentable
|
||||
* section. Use __always_inline so the compiler cannot push it out of line
|
||||
* and make it instrumentable.
|
||||
* Invoked from enter_from_user_mode() in the non-instrumentable section. Use
|
||||
* __always_inline so the compiler cannot push it out of line and make it
|
||||
* instrumentable.
|
||||
*/
|
||||
static __always_inline void arch_enter_from_user_mode(struct pt_regs *regs);
|
||||
|
||||
|
|
|
|||
|
|
@ -405,13 +405,13 @@ extern void sigaction_compat_abi(struct k_sigaction *act, struct k_sigaction *oa
|
|||
/*
|
||||
* ptrace report for syscall entry and exit looks identical.
|
||||
*/
|
||||
static inline int ptrace_report_syscall(unsigned long message)
|
||||
static inline bool ptrace_report_syscall(unsigned long message)
|
||||
{
|
||||
int ptrace = current->ptrace;
|
||||
int signr;
|
||||
|
||||
if (!(ptrace & PT_PTRACED))
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
signr = ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0),
|
||||
message);
|
||||
|
|
@ -424,11 +424,11 @@ static inline int ptrace_report_syscall(unsigned long message)
|
|||
if (signr)
|
||||
send_sig(signr, current, 1);
|
||||
|
||||
return fatal_signal_pending(current);
|
||||
return !fatal_signal_pending(current);
|
||||
}
|
||||
|
||||
/**
|
||||
* ptrace_report_syscall_entry - task is about to attempt a system call
|
||||
* ptrace_report_syscall_permit_entry - task is about to attempt a system call
|
||||
* @regs: user register state of current task
|
||||
*
|
||||
* This will be called if %SYSCALL_WORK_SYSCALL_TRACE or
|
||||
|
|
@ -438,7 +438,7 @@ static inline int ptrace_report_syscall(unsigned long message)
|
|||
* call number and arguments to be tried. It is safe to block here,
|
||||
* preventing the system call from beginning.
|
||||
*
|
||||
* Returns zero normally, or nonzero if the calling arch code should abort
|
||||
* Returns True normally, or False if the calling architecture code should abort
|
||||
* the system call. That must prevent normal entry so no system call is
|
||||
* made. If @task ever returns to user mode after this, its register state
|
||||
* is unspecified, but should be something harmless like an %ENOSYS error
|
||||
|
|
@ -447,8 +447,7 @@ static inline int ptrace_report_syscall(unsigned long message)
|
|||
*
|
||||
* Called without locks, just after entering kernel mode.
|
||||
*/
|
||||
static inline __must_check int ptrace_report_syscall_entry(
|
||||
struct pt_regs *regs)
|
||||
static inline __must_check bool ptrace_report_syscall_permit_entry(struct pt_regs *regs)
|
||||
{
|
||||
return ptrace_report_syscall(PTRACE_EVENTMSG_SYSCALL_ENTRY);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,8 +77,27 @@ static __always_inline u32 get_kstack_offset(void)
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* add_random_kstack_offset_irqsoff - Increase stack utilization by a random offset.
|
||||
*
|
||||
* This should be used in the syscall entry path after user registers have been
|
||||
* stored to the stack. Interrupts must be still disabled.
|
||||
*/
|
||||
#define add_random_kstack_offset_irqsoff() \
|
||||
do { \
|
||||
lockdep_assert_irqs_disabled(); \
|
||||
if (static_branch_maybe(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, \
|
||||
&randomize_kstack_offset)) { \
|
||||
u32 offset = prandom_u32_state(raw_cpu_ptr(&kstack_rnd_state)); \
|
||||
u8 *ptr = __kstack_alloca(KSTACK_OFFSET_MAX(offset)); \
|
||||
/* Keep allocation even after "ptr" loses scope. */ \
|
||||
asm volatile("" :: "r"(ptr) : "memory"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else /* CONFIG_RANDOMIZE_KSTACK_OFFSET */
|
||||
#define add_random_kstack_offset() do { } while (0)
|
||||
#define add_random_kstack_offset_irqsoff() do { } while (0)
|
||||
#endif /* CONFIG_RANDOMIZE_KSTACK_OFFSET */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@
|
|||
#include <linux/atomic.h>
|
||||
#include <asm/seccomp.h>
|
||||
|
||||
extern int __secure_computing(void);
|
||||
extern bool __seccomp_permit_syscall(void);
|
||||
|
||||
#ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
|
||||
static inline int secure_computing(void)
|
||||
static __always_inline bool seccomp_permit_syscall(void)
|
||||
{
|
||||
if (unlikely(test_syscall_work(SECCOMP)))
|
||||
return __secure_computing();
|
||||
return 0;
|
||||
return __seccomp_permit_syscall();
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
extern void secure_computing_strict(int this_syscall);
|
||||
|
|
@ -50,11 +50,11 @@ static inline int seccomp_mode(struct seccomp *s)
|
|||
struct seccomp_data;
|
||||
|
||||
#ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
|
||||
static inline int secure_computing(void) { return 0; }
|
||||
static inline bool seccomp_permit_syscall(void) { return true; }
|
||||
#else
|
||||
static inline void secure_computing_strict(int this_syscall) { return; }
|
||||
#endif
|
||||
static inline int __secure_computing(void) { return 0; }
|
||||
static inline bool __seccomp_permit_syscall(void) { return true; }
|
||||
|
||||
static inline long prctl_get_seccomp(void)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,9 +6,23 @@
|
|||
#define _SYSCALL_USER_DISPATCH_H
|
||||
|
||||
#include <linux/thread_info.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/syscall_user_dispatch_types.h>
|
||||
|
||||
#ifdef CONFIG_GENERIC_ENTRY
|
||||
struct pt_regs;
|
||||
|
||||
#ifdef CONFIG_SYSCALL_USER_DISPATCH
|
||||
|
||||
bool syscall_user_dispatch(struct pt_regs *regs);
|
||||
|
||||
static __always_inline bool syscall_user_dispatch_clear_on_dispatch(void)
|
||||
{
|
||||
if (likely(!current->syscall_dispatch.on_dispatch))
|
||||
return false;
|
||||
|
||||
current->syscall_dispatch.on_dispatch = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
|
||||
unsigned long len, char __user *selector);
|
||||
|
|
@ -24,6 +38,16 @@ int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long siz
|
|||
|
||||
#else
|
||||
|
||||
static __always_inline bool syscall_user_dispatch(struct pt_regs *regs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline bool syscall_user_dispatch_clear_on_dispatch(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
|
||||
unsigned long len, char __user *selector)
|
||||
{
|
||||
|
|
@ -46,6 +70,6 @@ static inline int syscall_user_dispatch_set_config(struct task_struct *task,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_GENERIC_ENTRY */
|
||||
#endif /* CONFIG_SYSCALL_USER_DISPATCH */
|
||||
|
||||
#endif /* _SYSCALL_USER_DISPATCH_H */
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef CONFIG_GENERIC_ENTRY
|
||||
#ifdef CONFIG_SYSCALL_USER_DISPATCH
|
||||
|
||||
struct syscall_user_dispatch {
|
||||
char __user *selector;
|
||||
|
|
|
|||
|
|
@ -13,5 +13,6 @@ CFLAGS_REMOVE_common.o = -fstack-protector -fstack-protector-strong
|
|||
CFLAGS_common.o += -fno-stack-protector
|
||||
|
||||
obj-$(CONFIG_GENERIC_IRQ_ENTRY) += common.o
|
||||
obj-$(CONFIG_GENERIC_SYSCALL) += syscall-common.o syscall_user_dispatch.o
|
||||
obj-$(CONFIG_GENERIC_SYSCALL) += syscall-common.o
|
||||
obj-$(CONFIG_SYSCALL_USER_DISPATCH) += syscall_user_dispatch.o
|
||||
obj-$(CONFIG_VIRT_XFER_TO_GUEST_WORK) += virt.o
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/audit.h>
|
||||
#include <linux/entry-common.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
|
|
@ -7,17 +8,23 @@
|
|||
|
||||
/* Out of line to prevent tracepoint code duplication */
|
||||
|
||||
long trace_syscall_enter(struct pt_regs *regs, long syscall)
|
||||
void trace_syscall_enter(struct pt_regs *regs)
|
||||
{
|
||||
trace_sys_enter(regs, syscall);
|
||||
/*
|
||||
* Probes or BPF hooks in the tracepoint may have changed the
|
||||
* system call number. Reread it.
|
||||
*/
|
||||
return syscall_get_nr(current, regs);
|
||||
trace_sys_enter(regs, syscall_get_nr(current, regs));
|
||||
}
|
||||
|
||||
void trace_syscall_exit(struct pt_regs *regs, long ret)
|
||||
{
|
||||
trace_sys_exit(regs, ret);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
void syscall_enter_audit(struct pt_regs *regs)
|
||||
{
|
||||
long syscall = syscall_get_nr(current, regs);
|
||||
unsigned long args[6];
|
||||
|
||||
syscall_get_arguments(current, regs, args);
|
||||
__audit_syscall_entry(syscall, args[0], args[1], args[2], args[3]);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2,21 +2,22 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Collabora Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/elf.h>
|
||||
#include <linux/entry-common.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/prctl.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/syscall_user_dispatch.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/elf.h>
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/syscall_user_dispatch.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/syscall.h>
|
||||
|
||||
static bool syscall_user_dispatch_allowed __read_mostly = true;
|
||||
|
||||
static void trigger_sigsys(struct pt_regs *regs)
|
||||
{
|
||||
struct kernel_siginfo info;
|
||||
|
|
@ -102,6 +103,10 @@ static int task_set_syscall_user_dispatch(struct task_struct *task, unsigned lon
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Arming can be denied at runtime via sysctl, disarming is allowed */
|
||||
if (mode != PR_SYS_DISPATCH_OFF && !syscall_user_dispatch_allowed)
|
||||
return -EPERM;
|
||||
|
||||
/*
|
||||
* access_ok() will clear memory tags for tagged addresses
|
||||
* if current has memory tagging enabled.
|
||||
|
|
@ -172,3 +177,22 @@ int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long siz
|
|||
return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len,
|
||||
(char __user *)(uintptr_t)cfg.selector);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_SYSCTL
|
||||
static const struct ctl_table syscall_user_dispatch_sysctls[] = {
|
||||
{
|
||||
.procname = "syscall_user_dispatch",
|
||||
.data = &syscall_user_dispatch_allowed,
|
||||
.maxlen = sizeof(syscall_user_dispatch_allowed),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dobool,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init syscall_user_dispatch_sysctl_init(void)
|
||||
{
|
||||
register_sysctl_init("kernel", syscall_user_dispatch_sysctls);
|
||||
return 0;
|
||||
}
|
||||
late_initcall(syscall_user_dispatch_sysctl_init);
|
||||
#endif /* CONFIG_PROC_SYSCTL */
|
||||
|
|
|
|||
|
|
@ -1100,12 +1100,13 @@ void secure_computing_strict(int this_syscall)
|
|||
else
|
||||
BUG();
|
||||
}
|
||||
int __secure_computing(void)
|
||||
|
||||
bool __seccomp_permit_syscall(void)
|
||||
{
|
||||
int this_syscall = syscall_get_nr(current, current_pt_regs());
|
||||
|
||||
secure_computing_strict(this_syscall);
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
|
||||
|
|
@ -1256,7 +1257,7 @@ static int seccomp_do_user_notification(int this_syscall,
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
|
||||
static bool __seccomp_filter(int this_syscall, const bool recheck_after_trace)
|
||||
{
|
||||
u32 filter_ret, action;
|
||||
struct seccomp_data sd;
|
||||
|
|
@ -1294,7 +1295,7 @@ static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
|
|||
case SECCOMP_RET_TRACE:
|
||||
/* We've been put in this state by the ptracer already. */
|
||||
if (recheck_after_trace)
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
/* ENOSYS these calls if there is no tracer attached. */
|
||||
if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) {
|
||||
|
|
@ -1329,20 +1330,17 @@ static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
|
|||
* a reload of all registers. This does not goto skip since
|
||||
* a skip would have already been reported.
|
||||
*/
|
||||
if (__seccomp_filter(this_syscall, true))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
return __seccomp_filter(this_syscall, true);
|
||||
|
||||
case SECCOMP_RET_USER_NOTIF:
|
||||
if (seccomp_do_user_notification(this_syscall, match, &sd))
|
||||
goto skip;
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
case SECCOMP_RET_LOG:
|
||||
seccomp_log(this_syscall, 0, action, true);
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
case SECCOMP_RET_ALLOW:
|
||||
/*
|
||||
|
|
@ -1350,7 +1348,7 @@ static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
|
|||
* this action since SECCOMP_RET_ALLOW is the starting
|
||||
* state in seccomp_run_filters().
|
||||
*/
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
case SECCOMP_RET_KILL_THREAD:
|
||||
case SECCOMP_RET_KILL_PROCESS:
|
||||
|
|
@ -1367,46 +1365,46 @@ static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
|
|||
} else {
|
||||
do_exit(SIGSYS);
|
||||
}
|
||||
return -1; /* skip the syscall go directly to signal handling */
|
||||
return false; /* skip the syscall go directly to signal handling */
|
||||
}
|
||||
|
||||
unreachable();
|
||||
|
||||
skip:
|
||||
seccomp_log(this_syscall, 0, action, match ? match->log : false);
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
static int __seccomp_filter(int this_syscall, const bool recheck_after_trace)
|
||||
static bool __seccomp_filter(int this_syscall, const bool recheck_after_trace)
|
||||
{
|
||||
BUG();
|
||||
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
int __secure_computing(void)
|
||||
bool __seccomp_permit_syscall(void)
|
||||
{
|
||||
int mode = current->seccomp.mode;
|
||||
int this_syscall;
|
||||
|
||||
if (IS_ENABLED(CONFIG_CHECKPOINT_RESTORE) &&
|
||||
unlikely(current->ptrace & PT_SUSPEND_SECCOMP))
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
this_syscall = syscall_get_nr(current, current_pt_regs());
|
||||
|
||||
switch (mode) {
|
||||
case SECCOMP_MODE_STRICT:
|
||||
__secure_computing_strict(this_syscall); /* may call do_exit */
|
||||
return 0;
|
||||
return true;
|
||||
case SECCOMP_MODE_FILTER:
|
||||
return __seccomp_filter(this_syscall, false);
|
||||
/* Surviving SECCOMP_RET_KILL_* must be proactively impossible. */
|
||||
case SECCOMP_MODE_DEAD:
|
||||
WARN_ON_ONCE(1);
|
||||
do_exit(SIGKILL);
|
||||
return -1;
|
||||
return false;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
CONFIG_GENERIC_ENTRY=y
|
||||
CONFIG_SYSCALL_USER_DISPATCH=y
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user