mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-07 21:53:44 +00:00
7fe33e9f66
This is a straight conversion from the old asm/unistd32.h into the format used by 32-bit arm and most other architectures, calling scripts to generate the asm/unistd32.h header and a new asm/syscalls32.h headers. I used a semi-automated text replacement method to do the conversion, and then used 'vimdiff' to synchronize the whitespace and the (unused) names of the non-compat syscalls with the arm version. There are two differences between the generated syscalls names and the old version: - the old asm/unistd32.h contained only a __NR_sync_file_range2 entry, while the arm32 version also defines __NR_arm_sync_file_range with the same number. I added this duplicate back in asm/unistd32.h. - __NR__sysctl was removed from the arm64 file a while ago, but all the tables still contain it. This should probably get removed everywhere but I added it here for consistency. On top of that, the arm64 version does not contain any references to the 32-bit OABI syscalls that are not supported by arm64. If we ever want to share the file between arm32 and arm64, it would not be hard to add support for both in one file. Acked-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
161 lines
4.6 KiB
C
161 lines
4.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include <linux/compiler.h>
|
|
#include <linux/context_tracking.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/nospec.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/randomize_kstack.h>
|
|
#include <linux/syscalls.h>
|
|
|
|
#include <asm/debug-monitors.h>
|
|
#include <asm/exception.h>
|
|
#include <asm/fpsimd.h>
|
|
#include <asm/syscall.h>
|
|
#include <asm/thread_info.h>
|
|
#include <asm/unistd.h>
|
|
#include <asm/unistd_compat_32.h>
|
|
|
|
long compat_arm_syscall(struct pt_regs *regs, int scno);
|
|
long sys_ni_syscall(void);
|
|
|
|
static long do_ni_syscall(struct pt_regs *regs, int scno)
|
|
{
|
|
if (is_compat_task()) {
|
|
long ret = compat_arm_syscall(regs, scno);
|
|
if (ret != -ENOSYS)
|
|
return ret;
|
|
}
|
|
|
|
return sys_ni_syscall();
|
|
}
|
|
|
|
static long __invoke_syscall(struct pt_regs *regs, syscall_fn_t syscall_fn)
|
|
{
|
|
return syscall_fn(regs);
|
|
}
|
|
|
|
static void invoke_syscall(struct pt_regs *regs, unsigned int scno,
|
|
unsigned int sc_nr,
|
|
const syscall_fn_t syscall_table[])
|
|
{
|
|
long ret;
|
|
|
|
add_random_kstack_offset();
|
|
|
|
if (scno < sc_nr) {
|
|
syscall_fn_t syscall_fn;
|
|
syscall_fn = syscall_table[array_index_nospec(scno, sc_nr)];
|
|
ret = __invoke_syscall(regs, syscall_fn);
|
|
} else {
|
|
ret = do_ni_syscall(regs, scno);
|
|
}
|
|
|
|
syscall_set_return_value(current, regs, 0, ret);
|
|
|
|
/*
|
|
* This value will get limited by KSTACK_OFFSET_MAX(), which is 10
|
|
* bits. The actual entropy will be further reduced by the compiler
|
|
* when applying stack alignment constraints: the AAPCS mandates a
|
|
* 16-byte aligned SP at function boundaries, which will remove the
|
|
* 4 low bits from any entropy chosen here.
|
|
*
|
|
* The resulting 6 bits of entropy is seen in SP[9:4].
|
|
*/
|
|
choose_random_kstack_offset(get_random_u16());
|
|
}
|
|
|
|
static inline bool has_syscall_work(unsigned long flags)
|
|
{
|
|
return unlikely(flags & _TIF_SYSCALL_WORK);
|
|
}
|
|
|
|
static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
|
|
const syscall_fn_t syscall_table[])
|
|
{
|
|
unsigned long flags = read_thread_flags();
|
|
|
|
regs->orig_x0 = regs->regs[0];
|
|
regs->syscallno = scno;
|
|
|
|
/*
|
|
* BTI note:
|
|
* The architecture does not guarantee that SPSR.BTYPE is zero
|
|
* on taking an SVC, so we could return to userspace with a
|
|
* non-zero BTYPE after the syscall.
|
|
*
|
|
* This shouldn't matter except when userspace is explicitly
|
|
* doing something stupid, such as setting PROT_BTI on a page
|
|
* that lacks conforming BTI/PACIxSP instructions, falling
|
|
* through from one executable page to another with differing
|
|
* PROT_BTI, or messing with BTYPE via ptrace: in such cases,
|
|
* userspace should not be surprised if a SIGILL occurs on
|
|
* syscall return.
|
|
*
|
|
* So, don't touch regs->pstate & PSR_BTYPE_MASK here.
|
|
* (Similarly for HVC and SMC elsewhere.)
|
|
*/
|
|
|
|
if (flags & _TIF_MTE_ASYNC_FAULT) {
|
|
/*
|
|
* Process the asynchronous tag check fault before the actual
|
|
* syscall. do_notify_resume() will send a signal to userspace
|
|
* before the syscall is restarted.
|
|
*/
|
|
syscall_set_return_value(current, regs, -ERESTARTNOINTR, 0);
|
|
return;
|
|
}
|
|
|
|
if (has_syscall_work(flags)) {
|
|
/*
|
|
* The de-facto standard way to skip a system call using ptrace
|
|
* is to set the system call to -1 (NO_SYSCALL) and set x0 to a
|
|
* suitable error code for consumption by userspace. However,
|
|
* this cannot be distinguished from a user-issued syscall(-1)
|
|
* and so we must set x0 to -ENOSYS here in case the tracer doesn't
|
|
* issue the skip and we fall into trace_exit with x0 preserved.
|
|
*
|
|
* This is slightly odd because it also means that if a tracer
|
|
* sets the system call number to -1 but does not initialise x0,
|
|
* then x0 will be preserved for all system calls apart from a
|
|
* user-issued syscall(-1). However, requesting a skip and not
|
|
* setting the return value is unlikely to do anything sensible
|
|
* anyway.
|
|
*/
|
|
if (scno == NO_SYSCALL)
|
|
syscall_set_return_value(current, regs, -ENOSYS, 0);
|
|
scno = syscall_trace_enter(regs);
|
|
if (scno == NO_SYSCALL)
|
|
goto trace_exit;
|
|
}
|
|
|
|
invoke_syscall(regs, scno, sc_nr, syscall_table);
|
|
|
|
/*
|
|
* The tracing status may have changed under our feet, so we have to
|
|
* check again. However, if we were tracing entry, then we always trace
|
|
* exit regardless, as the old entry assembly did.
|
|
*/
|
|
if (!has_syscall_work(flags) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
|
|
flags = read_thread_flags();
|
|
if (!has_syscall_work(flags) && !(flags & _TIF_SINGLESTEP))
|
|
return;
|
|
}
|
|
|
|
trace_exit:
|
|
syscall_trace_exit(regs);
|
|
}
|
|
|
|
void do_el0_svc(struct pt_regs *regs)
|
|
{
|
|
el0_svc_common(regs, regs->regs[8], __NR_syscalls, sys_call_table);
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
void do_el0_svc_compat(struct pt_regs *regs)
|
|
{
|
|
el0_svc_common(regs, regs->regs[7], __NR_compat32_syscalls,
|
|
compat_sys_call_table);
|
|
}
|
|
#endif
|