diff mbox

ARM: pass syscall return value to sys_exit tracepoint

Message ID 20121204115526.GK23368@mudshark.cambridge.arm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Will Deacon Dec. 4, 2012, 11:55 a.m. UTC
On Tue, Dec 04, 2012 at 10:40:38AM +0000, Will Deacon wrote:
> On Tue, Dec 04, 2012 at 10:38:09AM +0000, Russell King - ARM Linux wrote:
> > On Tue, Dec 04, 2012 at 09:48:28AM +0000, Gabbasov, Andrew wrote:
> > > The other registers in "regs" structure are not saved around ptrace_syscall_trace,
> > > and both audit_syscall_exit and trace_sys_exit get the register values, potentially
> > > changed by a debugger. Does it make sense to save the isolated return value
> > > for trace_sys_exit call only and not to save other registers, passed, for example,
> > > to audit_syscall_exit function that takes the return value from regs structure?
> > > Isn't it a reasonable assumption that a debugger will preserve important
> > > register values (or intentionally change them for some purpose) in case
> > > of syscall_exit, as we rely on this for syscall_enter case?
> > 
> > Actually, why are we doing things in a different order to x86?  If we
> > assume that x86 has this stuff correctly ordered, then shouldn't we
> > be following the sequence presented there?
> > 
> > x86 on entry does:
> > - secure_computing
> > - tracehook_report_syscall_entry
> > - trace_sys_enter
> > - audit_syscall_entry
> > and on exit:
> > - audit_syscall_exit
> > - trace_sys_exit
> > - tracehook_report_syscall_exit
> > 
> > We appear to be doing the _exact_ reverse ordering in our syscall exit
> > path - why is that?
> 
> 
> I've just been looking at that and the problem stems around having ->syscall
> set for the current thread before calling the sys_exit tracepoint. The audit
> code should definitely be moved earlier.
> 
> Working on a patch...

Ok, how about something like the following?

Will

--->8

commit 0109b339755b29f92733c1f257911615fc231727
Author: Will Deacon <will.deacon@arm.com>
Date:   Tue Dec 4 11:47:14 2012 +0000

    ARM: syscall: rework ordering in syscall_trace_exit
    
    syscall_trace_exit is currently doing things back-to-front; invoking
    the audit hook *after* signalling the debugger, which presents an
    opportunity for the registers to be re-written by userspace in order to
    bypass auditing constaints.
    
    This patch fixes the ordering by moving the audit code first and the
    tracehook code last. On the face of it, it looks like
    current_thread_info()->syscall may be incorrect for the sys_exit
    tracepoint, but that's actually not an issue because it will have been
    set during syscall entry and cannot have changed since then.
    
    Reported-by: Andrew Gabbasov <Andrew_Gabbasov@mentor.com>
    Signed-off-by: Will Deacon <will.deacon@arm.com>

Comments

Gabbasov, Andrew Dec. 6, 2012, 9:53 a.m. UTC | #1
> On Tue, Dec 04, 2012 at 10:40:38AM +0000, Will Deacon wrote:
> > On Tue, Dec 04, 2012 at 10:38:09AM +0000, Russell King - ARM Linux wrote:
> > > On Tue, Dec 04, 2012 at 09:48:28AM +0000, Gabbasov, Andrew wrote:
> > > > The other registers in "regs" structure are not saved around ptrace_syscall_trace,
> > > > and both audit_syscall_exit and trace_sys_exit get the register values, potentially
> > > > changed by a debugger. Does it make sense to save the isolated return value
> > > > for trace_sys_exit call only and not to save other registers, passed, for example,
> > > > to audit_syscall_exit function that takes the return value from regs structure?
> > > > Isn't it a reasonable assumption that a debugger will preserve important
> > > > register values (or intentionally change them for some purpose) in case
> > > > of syscall_exit, as we rely on this for syscall_enter case?
> > >
> > > Actually, why are we doing things in a different order to x86?  If we
> > > assume that x86 has this stuff correctly ordered, then shouldn't we
> > > be following the sequence presented there?
> > >
> > > x86 on entry does:
> > > - secure_computing
> > > - tracehook_report_syscall_entry
> > > - trace_sys_enter
> > > - audit_syscall_entry
> > > and on exit:
> > > - audit_syscall_exit
> > > - trace_sys_exit
> > > - tracehook_report_syscall_exit
> > >
> > > We appear to be doing the _exact_ reverse ordering in our syscall exit
> > > path - why is that?
> >
> >
> > I've just been looking at that and the problem stems around having ->syscall
> > set for the current thread before calling the sys_exit tracepoint. The audit
> > code should definitely be moved earlier.
> >
> > Working on a patch...
> 
> Ok, how about something like the following?
> 
> Will
> 
> --->8
> 
> commit 0109b339755b29f92733c1f257911615fc231727
> Author: Will Deacon <will.deacon@arm.com>
> Date:   Tue Dec 4 11:47:14 2012 +0000
> 
>     ARM: syscall: rework ordering in syscall_trace_exit
> 
>    syscall_trace_exit is currently doing things back-to-front; invoking
>     the audit hook *after* signalling the debugger, which presents an
>     opportunity for the registers to be re-written by userspace in order to
>     bypass auditing constaints.
> 
>     This patch fixes the ordering by moving the audit code first and the
>     tracehook code last. On the face of it, it looks like
>     current_thread_info()->syscall may be incorrect for the sys_exit
>     tracepoint, but that's actually not an issue because it will have been
>     set during syscall entry and cannot have changed since then.
> 
>     Reported-by: Andrew Gabbasov <Andrew_Gabbasov@mentor.com>
>     Signed-off-by: Will Deacon <will.deacon@arm.com>
> 
> diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
> index 8355d4b..1dd5122 100644
> --- a/arch/arm/kernel/entry-common.S
> +++ b/arch/arm/kernel/entry-common.S
> @@ -452,7 +452,6 @@ __sys_trace:
> 
>  __sys_trace_return:
>         str     r0, [sp, #S_R0 + S_OFF]!        @ save returned r0
> -       mov     r1, scno
>         mov     r0, sp
>         bl      syscall_trace_exit
>         b       ret_slow_syscall
> diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
> index 518536d..0e9c779 100644
> --- a/arch/arm/kernel/ptrace.c
> +++ b/arch/arm/kernel/ptrace.c
> @@ -957,17 +957,22 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno)
>         return scno;
>  }
> 
> -asmlinkage int syscall_trace_exit(struct pt_regs *regs, int scno)
> +asmlinkage void syscall_trace_exit(struct pt_regs *regs)
>  {
> -       current_thread_info()->syscall = scno;
> -
> -       if (test_thread_flag(TIF_SYSCALL_TRACE))
> -               scno = tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);
> +       /*
> +        * Audit the syscall before anything else, as a debugger may
> +        * come in and change the current registers.
> +        */
> +       audit_syscall_exit(regs);
> 
> +       /*
> +        * Note that we haven't updated the ->syscall field for the
> +        * current thread. This isn't a problem because it will have
> +        * been set on syscall entry and there hasn't been an opportunity
> +        * for a PTRACE_SET_SYSCALL since then.
> +        */
>         if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
> -               trace_sys_exit(regs, scno);
> +               trace_sys_exit(regs, regs_return_value(regs));
> 
> -       audit_syscall_exit(regs);
> -
> -       return scno;
> +       tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);
>  }
> 

Hi Will,

Thank you for the patch, I was thinking about something like this too, but was in a doubt
that reverse order could be for a purpose ;-)

Minor question: what is the version this patch is against? The latest version in Linus'es tree
does not have "current_thread_info()->syscall = scno;" at the beginning of
syscall_trace_exit. Or is it just a "proof of concept" patch and will be modified before
merging to the source?

Thanks,
Andrew
Will Deacon Dec. 6, 2012, 9:57 a.m. UTC | #2
On Thu, Dec 06, 2012 at 09:53:58AM +0000, Gabbasov, Andrew wrote:
> Hi Will,

Hello Andrew,

> Thank you for the patch, I was thinking about something like this too, but was in a doubt
> that reverse order could be for a purpose ;-)

Nope, it think it was an oversight when the entry path was cleaned up.

> Minor question: what is the version this patch is against? The latest version in Linus'es tree
> does not have "current_thread_info()->syscall = scno;" at the beginning of
> syscall_trace_exit. Or is it just a "proof of concept" patch and will be modified before
> merging to the source?

It's based on what Russell has queued for 3.8 (there is some seccomp stuff
in there). If you're happy with the patch, I can stick it in the patch
system once I've given it some more testing.

Will
Gabbasov, Andrew Dec. 6, 2012, 10:15 a.m. UTC | #3
> On Thu, Dec 06, 2012 at 09:53:58AM +0000, Gabbasov, Andrew wrote:
> > Hi Will,
> 
> Hello Andrew,
> 
> > Thank you for the patch, I was thinking about something like this too, but was in a doubt
> > that reverse order could be for a purpose ;-)
> 
> Nope, it think it was an oversight when the entry path was cleaned up.
> 
> > Minor question: what is the version this patch is against? The latest version in Linus'es tree
> > does not have "current_thread_info()->syscall = scno;" at the beginning of
> > syscall_trace_exit. Or is it just a "proof of concept" patch and will be modified before
> > merging to the source?
> 
> It's based on what Russell has queued for 3.8 (there is some seccomp stuff
> in there). If you're happy with the patch, I can stick it in the patch
> system once I've given it some more testing.
> 
> Will

Yes, the patch looks good to me.

Thanks.
Andrew
Mark Rutland Dec. 7, 2012, 10:45 a.m. UTC | #4
On Tue, Dec 04, 2012 at 11:55:26AM +0000, Will Deacon wrote:
> On Tue, Dec 04, 2012 at 10:40:38AM +0000, Will Deacon wrote:
> > On Tue, Dec 04, 2012 at 10:38:09AM +0000, Russell King - ARM Linux wrote:
> > > On Tue, Dec 04, 2012 at 09:48:28AM +0000, Gabbasov, Andrew wrote:
> > > > The other registers in "regs" structure are not saved around ptrace_syscall_trace,
> > > > and both audit_syscall_exit and trace_sys_exit get the register values, potentially
> > > > changed by a debugger. Does it make sense to save the isolated return value
> > > > for trace_sys_exit call only and not to save other registers, passed, for example,
> > > > to audit_syscall_exit function that takes the return value from regs structure?
> > > > Isn't it a reasonable assumption that a debugger will preserve important
> > > > register values (or intentionally change them for some purpose) in case
> > > > of syscall_exit, as we rely on this for syscall_enter case?
> > > 
> > > Actually, why are we doing things in a different order to x86?  If we
> > > assume that x86 has this stuff correctly ordered, then shouldn't we
> > > be following the sequence presented there?
> > > 
> > > x86 on entry does:
> > > - secure_computing
> > > - tracehook_report_syscall_entry
> > > - trace_sys_enter
> > > - audit_syscall_entry
> > > and on exit:
> > > - audit_syscall_exit
> > > - trace_sys_exit
> > > - tracehook_report_syscall_exit
> > > 
> > > We appear to be doing the _exact_ reverse ordering in our syscall exit
> > > path - why is that?
> > 
> > 
> > I've just been looking at that and the problem stems around having ->syscall
> > set for the current thread before calling the sys_exit tracepoint. The audit
> > code should definitely be moved earlier.
> > 
> > Working on a patch...
> 
> Ok, how about something like the following?
> 
> Will
> 
> --->8
> 
> commit 0109b339755b29f92733c1f257911615fc231727
> Author: Will Deacon <will.deacon@arm.com>
> Date:   Tue Dec 4 11:47:14 2012 +0000
> 
>     ARM: syscall: rework ordering in syscall_trace_exit
>     
>     syscall_trace_exit is currently doing things back-to-front; invoking
>     the audit hook *after* signalling the debugger, which presents an
>     opportunity for the registers to be re-written by userspace in order to
>     bypass auditing constaints.
>     
>     This patch fixes the ordering by moving the audit code first and the
>     tracehook code last. On the face of it, it looks like
>     current_thread_info()->syscall may be incorrect for the sys_exit
>     tracepoint, but that's actually not an issue because it will have been
>     set during syscall entry and cannot have changed since then.
>     
>     Reported-by: Andrew Gabbasov <Andrew_Gabbasov@mentor.com>
>     Signed-off-by: Will Deacon <will.deacon@arm.com>

My board locks up upon entering userspace with this applied.

> diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
> index 8355d4b..1dd5122 100644
> --- a/arch/arm/kernel/entry-common.S
> +++ b/arch/arm/kernel/entry-common.S
> @@ -452,7 +452,6 @@ __sys_trace:
>  
>  __sys_trace_return:
>  	str	r0, [sp, #S_R0 + S_OFF]!	@ save returned r0
> -	mov	r1, scno
>  	mov	r0, sp
>  	bl	syscall_trace_exit
>  	b	ret_slow_syscall
> diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
> index 518536d..0e9c779 100644
> --- a/arch/arm/kernel/ptrace.c
> +++ b/arch/arm/kernel/ptrace.c
> @@ -957,17 +957,22 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno)
>  	return scno;
>  }
>  
> -asmlinkage int syscall_trace_exit(struct pt_regs *regs, int scno)
> +asmlinkage void syscall_trace_exit(struct pt_regs *regs)
>  {
> -	current_thread_info()->syscall = scno;
> -
> -	if (test_thread_flag(TIF_SYSCALL_TRACE))
> -		scno = tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);

I see here we used to test TIF_SYSCALL_TRACE...

> +	/*
> +	 * Audit the syscall before anything else, as a debugger may
> +	 * come in and change the current registers.
> +	 */
> +	audit_syscall_exit(regs);
>  
> +	/*
> +	 * Note that we haven't updated the ->syscall field for the
> +	 * current thread. This isn't a problem because it will have
> +	 * been set on syscall entry and there hasn't been an opportunity
> +	 * for a PTRACE_SET_SYSCALL since then.
> +	 */
>  	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
> -		trace_sys_exit(regs, scno);
> +		trace_sys_exit(regs, regs_return_value(regs));
>  
> -	audit_syscall_exit(regs);
> -
> -	return scno;
> +	tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);

And here we don't. Restoring the test makes boot complete, and audit and
tracing seem to be happy afterwards.

With the test restored,
Tested-by: Mark Rutland <mark.rutland@arm.com>

Thanks,
Mark.
Will Deacon Dec. 7, 2012, 4:01 p.m. UTC | #5
On Fri, Dec 07, 2012 at 10:45:40AM +0000, Mark Rutland wrote:
> On Tue, Dec 04, 2012 at 11:55:26AM +0000, Will Deacon wrote:
> > +asmlinkage void syscall_trace_exit(struct pt_regs *regs)
> >  {
> > -	current_thread_info()->syscall = scno;
> > -
> > -	if (test_thread_flag(TIF_SYSCALL_TRACE))
> > -		scno = tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);
> 
> I see here we used to test TIF_SYSCALL_TRACE...
> 
> > +	/*
> > +	 * Audit the syscall before anything else, as a debugger may
> > +	 * come in and change the current registers.
> > +	 */
> > +	audit_syscall_exit(regs);
> >  
> > +	/*
> > +	 * Note that we haven't updated the ->syscall field for the
> > +	 * current thread. This isn't a problem because it will have
> > +	 * been set on syscall entry and there hasn't been an opportunity
> > +	 * for a PTRACE_SET_SYSCALL since then.
> > +	 */
> >  	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
> > -		trace_sys_exit(regs, scno);
> > +		trace_sys_exit(regs, regs_return_value(regs));
> >  
> > -	audit_syscall_exit(regs);
> > -
> > -	return scno;
> > +	tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);
> 
> And here we don't. Restoring the test makes boot complete, and audit and
> tracing seem to be happy afterwards.
> 
> With the test restored,
> Tested-by: Mark Rutland <mark.rutland@arm.com>

Cheers Mark, I'll fix this oversight and stick it in the patch system with
your tag.

Will
diff mbox

Patch

diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 8355d4b..1dd5122 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -452,7 +452,6 @@  __sys_trace:
 
 __sys_trace_return:
 	str	r0, [sp, #S_R0 + S_OFF]!	@ save returned r0
-	mov	r1, scno
 	mov	r0, sp
 	bl	syscall_trace_exit
 	b	ret_slow_syscall
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 518536d..0e9c779 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -957,17 +957,22 @@  asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno)
 	return scno;
 }
 
-asmlinkage int syscall_trace_exit(struct pt_regs *regs, int scno)
+asmlinkage void syscall_trace_exit(struct pt_regs *regs)
 {
-	current_thread_info()->syscall = scno;
-
-	if (test_thread_flag(TIF_SYSCALL_TRACE))
-		scno = tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);
+	/*
+	 * Audit the syscall before anything else, as a debugger may
+	 * come in and change the current registers.
+	 */
+	audit_syscall_exit(regs);
 
+	/*
+	 * Note that we haven't updated the ->syscall field for the
+	 * current thread. This isn't a problem because it will have
+	 * been set on syscall entry and there hasn't been an opportunity
+	 * for a PTRACE_SET_SYSCALL since then.
+	 */
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
-		trace_sys_exit(regs, scno);
+		trace_sys_exit(regs, regs_return_value(regs));
 
-	audit_syscall_exit(regs);
-
-	return scno;
+	tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);
 }