Commit 17c28958 authored by Dave Martin's avatar Dave Martin Committed by Catalin Marinas

arm64: Abstract syscallno manipulation

The -1 "no syscall" value is written in various ways, shared with
the user ABI in some places, and generally obscure.

This patch attempts to make things a little more consistent and
readable by replacing all these uses with a single #define.  A
couple of symbolic helpers are provided to clarify the intent
further.

Because the in-syscall check in do_signal() is changed from >= 0 to
!= NO_SYSCALL by this patch, different behaviour may be observable
if syscallno is set to values less than -1 by a tracer.  However,
this is not different from the behaviour that is already observable
if a tracer sets syscallno to a value >= __NR_(compat_)syscalls.

It appears that this can cause spurious syscall restarting, but
that is not a new behaviour either, and does not appear harmful.
Signed-off-by: default avatarDave Martin <Dave.Martin@arm.com>
Acked-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 35d0e6fb
...@@ -112,7 +112,7 @@ void tls_preserve_current_state(void); ...@@ -112,7 +112,7 @@ void tls_preserve_current_state(void);
static inline void start_thread_common(struct pt_regs *regs, unsigned long pc) static inline void start_thread_common(struct pt_regs *regs, unsigned long pc)
{ {
memset(regs, 0, sizeof(*regs)); memset(regs, 0, sizeof(*regs));
regs->syscallno = ~0; forget_syscall(regs);
regs->pc = pc; regs->pc = pc;
} }
......
...@@ -72,8 +72,19 @@ ...@@ -72,8 +72,19 @@
#define COMPAT_PT_TEXT_ADDR 0x10000 #define COMPAT_PT_TEXT_ADDR 0x10000
#define COMPAT_PT_DATA_ADDR 0x10004 #define COMPAT_PT_DATA_ADDR 0x10004
#define COMPAT_PT_TEXT_END_ADDR 0x10008 #define COMPAT_PT_TEXT_END_ADDR 0x10008
/*
* If pt_regs.syscallno == NO_SYSCALL, then the thread is not executing
* a syscall -- i.e., its most recent entry into the kernel from
* userspace was not via SVC, or otherwise a tracer cancelled the syscall.
*
* This must have the value -1, for ABI compatibility with ptrace etc.
*/
#define NO_SYSCALL (-1)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/types.h>
/* sizeof(struct user) for AArch32 */ /* sizeof(struct user) for AArch32 */
#define COMPAT_USER_SZ 296 #define COMPAT_USER_SZ 296
...@@ -128,6 +139,16 @@ struct pt_regs { ...@@ -128,6 +139,16 @@ struct pt_regs {
u64 unused; // maintain 16 byte alignment u64 unused; // maintain 16 byte alignment
}; };
static inline bool in_syscall(struct pt_regs const *regs)
{
return regs->syscallno != NO_SYSCALL;
}
static inline void forget_syscall(struct pt_regs *regs)
{
regs->syscallno = NO_SYSCALL;
}
#define MAX_REG_OFFSET offsetof(struct pt_regs, pstate) #define MAX_REG_OFFSET offsetof(struct pt_regs, pstate)
#define arch_has_single_step() (1) #define arch_has_single_step() (1)
......
...@@ -138,11 +138,9 @@ alternative_else_nop_endif ...@@ -138,11 +138,9 @@ alternative_else_nop_endif
stp x22, x23, [sp, #S_PC] stp x22, x23, [sp, #S_PC]
/* /* Not in a syscall by default (el0_svc overwrites for real syscall) */
* Set syscallno to -1 by default (overridden later if real syscall).
*/
.if \el == 0 .if \el == 0
mvn w21, wzr mov w21, #NO_SYSCALL
str w21, [sp, #S_SYSCALLNO] str w21, [sp, #S_SYSCALLNO]
.endif .endif
...@@ -825,13 +823,13 @@ ENDPROC(el0_svc) ...@@ -825,13 +823,13 @@ ENDPROC(el0_svc)
* switches, and waiting for our parent to respond. * switches, and waiting for our parent to respond.
*/ */
__sys_trace: __sys_trace:
cmp wscno, #-1 // user-issued syscall(-1)? cmp wscno, #NO_SYSCALL // user-issued syscall(-1)?
b.ne 1f b.ne 1f
mov x0, #-ENOSYS // set default errno if so mov x0, #-ENOSYS // set default errno if so
str x0, [sp, #S_X0] str x0, [sp, #S_X0]
1: mov x0, sp 1: mov x0, sp
bl syscall_trace_enter bl syscall_trace_enter
cmp w0, #-1 // skip the syscall? cmp w0, #NO_SYSCALL // skip the syscall?
b.eq __sys_trace_return_skipped b.eq __sys_trace_return_skipped
mov wscno, w0 // syscall number (possibly new) mov wscno, w0 // syscall number (possibly new)
mov x1, sp // pointer to regs mov x1, sp // pointer to regs
......
...@@ -1363,7 +1363,7 @@ static void tracehook_report_syscall(struct pt_regs *regs, ...@@ -1363,7 +1363,7 @@ static void tracehook_report_syscall(struct pt_regs *regs,
if (dir == PTRACE_SYSCALL_EXIT) if (dir == PTRACE_SYSCALL_EXIT)
tracehook_report_syscall_exit(regs, 0); tracehook_report_syscall_exit(regs, 0);
else if (tracehook_report_syscall_entry(regs)) else if (tracehook_report_syscall_entry(regs))
regs->syscallno = ~0; forget_syscall(regs);
regs->regs[regno] = saved_reg; regs->regs[regno] = saved_reg;
} }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <asm/ucontext.h> #include <asm/ucontext.h>
#include <asm/unistd.h> #include <asm/unistd.h>
#include <asm/fpsimd.h> #include <asm/fpsimd.h>
#include <asm/ptrace.h>
#include <asm/signal32.h> #include <asm/signal32.h>
#include <asm/vdso.h> #include <asm/vdso.h>
...@@ -387,7 +388,7 @@ static int restore_sigframe(struct pt_regs *regs, ...@@ -387,7 +388,7 @@ static int restore_sigframe(struct pt_regs *regs,
/* /*
* Avoid sys_rt_sigreturn() restarting. * Avoid sys_rt_sigreturn() restarting.
*/ */
regs->syscallno = ~0; forget_syscall(regs);
err |= !valid_user_regs(&regs->user_regs, current); err |= !valid_user_regs(&regs->user_regs, current);
if (err == 0) if (err == 0)
...@@ -673,13 +674,12 @@ static void do_signal(struct pt_regs *regs) ...@@ -673,13 +674,12 @@ static void do_signal(struct pt_regs *regs)
{ {
unsigned long continue_addr = 0, restart_addr = 0; unsigned long continue_addr = 0, restart_addr = 0;
int retval = 0; int retval = 0;
int syscall = regs->syscallno;
struct ksignal ksig; struct ksignal ksig;
/* /*
* If we were from a system call, check for system call restarting... * If we were from a system call, check for system call restarting...
*/ */
if (syscall >= 0) { if (in_syscall(regs)) {
continue_addr = regs->pc; continue_addr = regs->pc;
restart_addr = continue_addr - (compat_thumb_mode(regs) ? 2 : 4); restart_addr = continue_addr - (compat_thumb_mode(regs) ? 2 : 4);
retval = regs->regs[0]; retval = regs->regs[0];
...@@ -687,7 +687,7 @@ static void do_signal(struct pt_regs *regs) ...@@ -687,7 +687,7 @@ static void do_signal(struct pt_regs *regs)
/* /*
* Avoid additional syscall restarting via ret_to_user. * Avoid additional syscall restarting via ret_to_user.
*/ */
regs->syscallno = ~0; forget_syscall(regs);
/* /*
* Prepare for system call restart. We do this here so that a * Prepare for system call restart. We do this here so that a
...@@ -731,7 +731,7 @@ static void do_signal(struct pt_regs *regs) ...@@ -731,7 +731,7 @@ static void do_signal(struct pt_regs *regs)
* Handle restarting a different system call. As above, if a debugger * Handle restarting a different system call. As above, if a debugger
* has chosen to restart at a different PC, ignore the restart. * has chosen to restart at a different PC, ignore the restart.
*/ */
if (syscall >= 0 && regs->pc == restart_addr) { if (in_syscall(regs) && regs->pc == restart_addr) {
if (retval == -ERESTART_RESTARTBLOCK) if (retval == -ERESTART_RESTARTBLOCK)
setup_restart_syscall(regs); setup_restart_syscall(regs);
user_rewind_single_step(current); user_rewind_single_step(current);
......
...@@ -354,7 +354,7 @@ static int compat_restore_sigframe(struct pt_regs *regs, ...@@ -354,7 +354,7 @@ static int compat_restore_sigframe(struct pt_regs *regs,
/* /*
* Avoid compat_sys_sigreturn() restarting. * Avoid compat_sys_sigreturn() restarting.
*/ */
regs->syscallno = ~0; forget_syscall(regs);
err |= !valid_user_regs(&regs->user_regs, current); err |= !valid_user_regs(&regs->user_regs, current);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment