diff mbox series

[4/9] ARM: syscall: always store thread_info->syscall

Message ID 20201030154919.1246645-4-arnd@kernel.org (mailing list archive)
State New, archived
Headers show
Series ARM: remove set_fs callers and implementation | expand

Commit Message

Arnd Bergmann Oct. 30, 2020, 3:49 p.m. UTC
From: Arnd Bergmann <arnd@arndb.de>

The system call number is used in a a couple of places, in particular
ptrace, seccomp and /proc/<pid>/syscall.

The last one apparently never worked reliably on ARM for tasks
that are not currently getting traced.

Storing the syscall number in the normal entry path makes it work,
as well as allowing us to see if the current system call is for
OABI compat mode, which is the next thing I want to hook into.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 arch/arm/include/asm/syscall.h | 5 ++++-
 arch/arm/kernel/asm-offsets.c  | 1 +
 arch/arm/kernel/entry-common.S | 8 ++++++--
 arch/arm/kernel/ptrace.c       | 9 +++++----
 4 files changed, 16 insertions(+), 7 deletions(-)

Comments

Russell King (Oracle) Oct. 30, 2020, 4:53 p.m. UTC | #1
On Fri, Oct 30, 2020 at 04:49:14PM +0100, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
> 
> The system call number is used in a a couple of places, in particular
> ptrace, seccomp and /proc/<pid>/syscall.
> 
> The last one apparently never worked reliably on ARM for tasks
> that are not currently getting traced.
> 
> Storing the syscall number in the normal entry path makes it work,
> as well as allowing us to see if the current system call is for
> OABI compat mode, which is the next thing I want to hook into.

I'm not sure this patch is correct.

Tracing the existing code for OABI:

asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno)
{
        current_thread_info()->syscall = scno;

        /* Legacy ABI only. */
USER(	ldr     scno, [saved_pc, #-4]   )       @ get SWI instruction
	bic     scno, scno, #0xff000000         @ mask off SWI op-code
	eor     scno, scno, #__NR_SYSCALL_BASE  @ check OS number
	tst     r10, #_TIF_SYSCALL_WORK         @ are we tracing syscalls?
	bne     __sys_trace

__sys_trace:
	mov     r1, scno
	add     r0, sp, #S_OFF
	bl      syscall_trace_enter

So, thread_info->syscall does not include __NR_SYSCALL_BASE. The
reason for this is the code that makes use of that via syscall_get_nr().
kernel/trace/trace_syscalls.c:

	syscall_nr = trace_get_syscall_nr(current, regs);
	if (syscall_nr < 0 || syscall_nr >= NR_syscalls)
		return;

and NR_syscalls is the number of syscalls, which doesn't include the
__NR_SYSCALL_BASE offset.

So, I think this patch actually breaks OABI.
Arnd Bergmann Oct. 30, 2020, 9:28 p.m. UTC | #2
On Fri, Oct 30, 2020 at 5:53 PM Russell King - ARM Linux admin
<linux@armlinux.org.uk> wrote:
>
> On Fri, Oct 30, 2020 at 04:49:14PM +0100, Arnd Bergmann wrote:
> > From: Arnd Bergmann <arnd@arndb.de>
> >
> > The system call number is used in a a couple of places, in particular
> > ptrace, seccomp and /proc/<pid>/syscall.
> >
> > The last one apparently never worked reliably on ARM for tasks
> > that are not currently getting traced.
> >
> > Storing the syscall number in the normal entry path makes it work,
> > as well as allowing us to see if the current system call is for
> > OABI compat mode, which is the next thing I want to hook into.
>
> I'm not sure this patch is correct.

I'm not following where you still see a mismatch, I was hoping I
had fixed them all after your previous review :(

The thread_info->syscall entry should now consistently contain
__NR_SYSCALL_BASE on an EABI kernel, and all users of
that should consistently mask it out.

> Tracing the existing code for OABI:
>
> asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno)
> {
>         current_thread_info()->syscall = scno;

This no longer stores to current_thread_info()->syscall but instead
reads the number from syscall_get_nr().

>         /* Legacy ABI only. */
> USER(   ldr     scno, [saved_pc, #-4]   )       @ get SWI instruction
>         bic     scno, scno, #0xff000000         @ mask off SWI op-code
>         eor     scno, scno, #__NR_SYSCALL_BASE  @ check OS number
>         tst     r10, #_TIF_SYSCALL_WORK         @ are we tracing syscalls?
>         bne     __sys_trace
>
> __sys_trace:
>         mov     r1, scno
>         add     r0, sp, #S_OFF
>         bl      syscall_trace_enter
>
> So, thread_info->syscall does not include __NR_SYSCALL_BASE. The
> reason for this is the code that makes use of that via syscall_get_nr().
> kernel/trace/trace_syscalls.c:

On both CONFIG_OABI_COMPAT and on !CONFIG_AEABI kernels,
I store the value before masking out __NR_SYSCALL_BASE
after my change. For EABI-only kernels there is no need for
the mask.

>         syscall_nr = trace_get_syscall_nr(current, regs);
>         if (syscall_nr < 0 || syscall_nr >= NR_syscalls)
>                 return;
>
> and NR_syscalls is the number of syscalls, which doesn't include the
> __NR_SYSCALL_BASE offset.
>
> So, I think this patch actually breaks OABI.

The value returned from trace_get_syscall_nr() is always in
the 0...NR_syscalls range without the __NR_SYSCALL_BASE
for a valid syscall. because of the added

 static inline int syscall_get_nr(struct task_struct *task,
                                 struct pt_regs *regs)
 {
-       return task_thread_info(task)->syscall;
+       return task_thread_info(task)->syscall & ~__NR_OABI_SYSCALL_BASE;
 }

(plus the corresponding logic for OABI_COMPAT.

Which of the above do you think I got wrong?

      Arnd
diff mbox series

Patch

diff --git a/arch/arm/include/asm/syscall.h b/arch/arm/include/asm/syscall.h
index fd02761ba06c..89898497edd6 100644
--- a/arch/arm/include/asm/syscall.h
+++ b/arch/arm/include/asm/syscall.h
@@ -22,7 +22,10 @@  extern const unsigned long sys_call_table[];
 static inline int syscall_get_nr(struct task_struct *task,
 				 struct pt_regs *regs)
 {
-	return task_thread_info(task)->syscall;
+	if (IS_ENABLED(CONFIG_AEABI) && !IS_ENABLED(CONFIG_OABI_COMPAT))
+		return task_thread_info(task)->syscall;
+
+	return task_thread_info(task)->syscall & ~__NR_OABI_SYSCALL_BASE;
 }
 
 static inline void syscall_rollback(struct task_struct *task,
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index a1570c8bab25..97af6735172b 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -46,6 +46,7 @@  int main(void)
   DEFINE(TI_CPU,		offsetof(struct thread_info, cpu));
   DEFINE(TI_CPU_DOMAIN,		offsetof(struct thread_info, cpu_domain));
   DEFINE(TI_CPU_SAVE,		offsetof(struct thread_info, cpu_context));
+  DEFINE(TI_SYSCALL,		offsetof(struct thread_info, syscall));
   DEFINE(TI_USED_CP,		offsetof(struct thread_info, used_cp));
   DEFINE(TI_TP_VALUE,		offsetof(struct thread_info, tp_value));
   DEFINE(TI_FPSTATE,		offsetof(struct thread_info, fpstate));
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 271cb8a1eba1..9a76467bbb47 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -223,6 +223,7 @@  ENTRY(vector_swi)
 	/* saved_psr and saved_pc are now dead */
 
 	uaccess_disable tbl
+	get_thread_info tsk
 
 	adr	tbl, sys_call_table		@ load syscall table pointer
 
@@ -234,13 +235,17 @@  ENTRY(vector_swi)
 	 * get the old ABI syscall table address.
 	 */
 	bics	r10, r10, #0xff000000
+	strne	r10, [tsk, #TI_SYSCALL]
+	streq	scno, [tsk, #TI_SYSCALL]
 	eorne	scno, r10, #__NR_OABI_SYSCALL_BASE
 	ldrne	tbl, =sys_oabi_call_table
 #elif !defined(CONFIG_AEABI)
 	bic	scno, scno, #0xff000000		@ mask off SWI op-code
+	str	scno, [tsk, #TI_SYSCALL]
 	eor	scno, scno, #__NR_SYSCALL_BASE	@ check OS number
+#else
+	str	scno, [tsk, #TI_SYSCALL]
 #endif
-	get_thread_info tsk
 	/*
 	 * Reload the registers that may have been corrupted on entry to
 	 * the syscall assembly (by tracing or context tracking.)
@@ -285,7 +290,6 @@  ENDPROC(vector_swi)
 	 * context switches, and waiting for our parent to respond.
 	 */
 __sys_trace:
-	mov	r1, scno
 	add	r0, sp, #S_OFF
 	bl	syscall_trace_enter
 	mov	scno, r0
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 2771e682220b..683edb8b627d 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -25,6 +25,7 @@ 
 #include <linux/tracehook.h>
 #include <linux/unistd.h>
 
+#include <asm/syscall.h>
 #include <asm/traps.h>
 
 #define CREATE_TRACE_POINTS
@@ -885,9 +886,9 @@  static void tracehook_report_syscall(struct pt_regs *regs,
 	regs->ARM_ip = ip;
 }
 
-asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno)
+asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 {
-	current_thread_info()->syscall = scno;
+	int scno;
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE))
 		tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER);
@@ -898,11 +899,11 @@  asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno)
 		return -1;
 #else
 	/* XXX: remove this once OABI gets fixed */
-	secure_computing_strict(current_thread_info()->syscall);
+	secure_computing_strict(syscall_get_nr(current, regs));
 #endif
 
 	/* Tracer or seccomp may have changed syscall. */
-	scno = current_thread_info()->syscall;
+	scno = syscall_get_nr(current, regs);
 
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, scno);