lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <94bda8cd5f326ae5591c80fb5d7c1c22624accec.1466244711.git.luto@kernel.org>
Date:	Sat, 18 Jun 2016 03:21:40 -0700
From:	Andy Lutomirski <luto@...nel.org>
To:	x86@...nel.org
Cc:	"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
	Kees Cook <keescook@...omium.org>,
	Borislav Petkov <bp@...en8.de>,
	Oleg Nesterov <oleg@...hat.com>,
	Andy Lutomirski <luto@...nel.org>
Subject: [PATCH] x86/ptrace: Remove questionable TS_COMPAT usage in ptrace

A 32-bit tracer can set a tracee's TS_COMPAT flag by poking orig_ax.

- If the tracee is stopped in a 32-bit syscall, this has no effect
  as TS_COMPAT will already be set.

- If the tracee is stopped on entry to a 64-bit syscall, this could
  cause problems: in_compat_syscall() etc will be out of sync with
  the actual syscall table in use.  I can imagine this bre aking
  audit.  (It can't meaningfully break seccomp, as a malicious
  tracer can already fully bypass seccomp.)  I could also imagine it
  subtly confusing the implementations of a few syscalls.

 - If the tracee is stopped in a non-syscall context, then TS_COMPAT
   will end up set in user mode, which isn't supposed to happen.  The results
   are likely to be similar to the 64-bit syscall entry case.

Fix it by deleting the code.

Here's my understanding of the previous intent.  Suppose a
32-bit tracee makes a syscall that returns one of the -ERESTART
codes.  A 32-bit tracer saves away its register state.  The tracee
resumes, returns from the system call, and gets stopped again for a
non-syscall reason (e.g. a signal).  Then the tracer tries to roll
back the tracee's state by writing all of the saved registers back.

The result of this sequence of events will be that the tracee's
registers' low bits match what they were at the end of the syscall
but TS_COMPAT will be clear.  This will cause syscall_get_error() to
return a *positive* number, because we zero-extend registers poked
by 32-bit tracers instead of sign-extending them.  As a result, with
this change, syscall restart won't happen, whereas, historically, it
would have happened.

As far as I can tell, this corner case isn't very important, and I
also don't see how one would reasonably have triggered it in the
first place.  In particular, syscall restart happens before ptrace
hooks in the syscall exit path, so a tracer should never see the
problematic pre-restart syscall state in the first place.

Signed-off-by: Andy Lutomirski <luto@...nel.org>
---

Oleg, you often have good insight into ptrace oddities.  Do you think I'm
correct that this can safely be deleted?

Kees, I think that either this or a similar fix is important to make the
seccomp reordering series be fully effective.

Ingo, this is intended for x86/urgent.  I deliberately didn't cc stable,
as the bug this fixes seems minor enough that I think we can wait a while
to backport it.

arch/x86/kernel/ptrace.c | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 600edd225e81..bde57caf9aa9 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -922,16 +922,7 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 value)
 	R32(esp, sp);
 
 	case offsetof(struct user32, regs.orig_eax):
-		/*
-		 * A 32-bit debugger setting orig_eax means to restore
-		 * the state of the task restarting a 32-bit syscall.
-		 * Make sure we interpret the -ERESTART* codes correctly
-		 * in case the task is not actually still sitting at the
-		 * exit from a 32-bit syscall with TS_COMPAT still set.
-		 */
 		regs->orig_ax = value;
-		if (syscall_get_nr(child, regs) >= 0)
-			task_thread_info(child)->status |= TS_COMPAT;
 		break;
 
 	case offsetof(struct user32, regs.eflags):
-- 
2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ