Re: syscall_get_error() && TS_ checks

Oleg Nesterov
2017-03-30T15:49:12Z

On 03/30, Oleg Nesterov wrote:
>
> On 03/29, Linus Torvalds wrote:
> >
> > On Wed, Mar 29, 2017 at 11:50 AM, Oleg Nesterov <oleg@redhat.com> wrote:
> > >
> > > Again, afaics we only need these compat checks because regs->ax could be
> > > changed by 32-bit debugger without sign-extension.
> >
> > You don't explain how you were planning on *fixing* that code. You
> > know why it exists, but then you just say "let's remove it", without
> > any explanation of what you'd replace it with.
>
> Hmm. I tried to explain... Let me quote my initial email,
>
> So why we can't simply change putreg32() to always sign-extend regs->ax
> regs->orig_ax and just do
>
> static inline long syscall_get_error(struct task_struct *task,
> struct pt_regs *regs)
> {
> return regs-ax;
> }
>
> ? Or, better, simply kill it and use syscall_get_return_value() in
> arch/x86/kernel/signal.c.
>
> Of course, if the tracee is 64-bit and debugger is 32-bit then the
> unconditional sign-extend can be wrong, but do we really care about
> this case? This can't really work anyway. And the current code is not
> right too. Say, debugger nacks the signal which interrupted syscall
> and sets regs->ax = -ERESTARTSYS to force the restart, this won't work
> because TS_COMPAT|TS_I386_REGS_POKED are not set.
>
> In short. can the patch below work?
>
> Oleg.
>
> diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
> index 9cc7d5a..96f21fc 100644
> --- a/arch/x86/kernel/ptrace.c
> +++ b/arch/x86/kernel/ptrace.c
> @@ -917,11 +917,14 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 value)
> R32(edi, di);
> R32(esi, si);
> R32(ebp, bp);
> - R32(eax, ax);
> R32(eip, ip);
> R32(esp, sp);
>
> case offsetof(struct user32, regs.orig_eax):
^^^^^^^^^^^^^^
damn, just in case, of course this is typo, should be

case offsetof(struct user32, regs.eax);

> + regs->ax = (long) (int) value;
> + break;
> +
> + case offsetof(struct user32, regs.orig_eax):
> /*
> * Warning: bizarre corner case fixup here. A 32-bit
> * debugger setting orig_eax to -1 wants to disable
> diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
> index b3b98ff..41023f8 100644
> --- a/arch/x86/kernel/signal.c
> +++ b/arch/x86/kernel/signal.c
> @@ -710,7 +710,7 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
> /* Are we from a system call? */
> if (syscall_get_nr(current, regs) >= 0) {
> /* If so, check system call restarting.. */
> - switch (syscall_get_error(current, regs)) {
> + switch (syscall_get_return_value(current, regs)) {
> case -ERESTART_RESTARTBLOCK:
> case -ERESTARTNOHAND:
> regs->ax = -EINTR;
> @@ -813,7 +813,7 @@ void do_signal(struct pt_regs *regs)
> /* Did we come from a system call? */
> if (syscall_get_nr(current, regs) >= 0) {
> /* Restart the system call - no handlers present */
> - switch (syscall_get_error(current, regs)) {
> + switch (syscall_get_return_value(current, regs)) {