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 for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAP045ApWnr=UQrBrv3fHj-C6EweukMWEyrCgsiY6Bt_i1Vdj6A@mail.gmail.com>
Date:   Sun, 31 Jan 2021 15:17:54 -0800
From:   Kyle Huey <me@...ehuey.com>
To:     Andy Lutomirski <luto@...capital.net>
Cc:     Linus Torvalds <torvalds@...ux-foundation.org>,
        Thomas Gleixner <tglx@...utronix.de>,
        Andy Lutomirski <luto@...nel.org>,
        Gabriel Krisman Bertazi <krisman@...labora.com>,
        open list <linux-kernel@...r.kernel.org>,
        "Robert O'Callahan" <rocallahan@...il.com>
Subject: Re: [REGRESSION] x86/entry: TIF_SINGLESTEP handling is still broken

On Sun, Jan 31, 2021 at 2:27 PM Kyle Huey <me@...ehuey.com> wrote:
>
> On Sun, Jan 31, 2021 at 2:20 PM Andy Lutomirski <luto@...capital.net> wrote:
> >
> >
> >
> > > On Jan 31, 2021, at 2:08 PM, Kyle Huey <me@...ehuey.com> wrote:
> > >
> > > On Sun, Jan 31, 2021 at 2:04 PM Andy Lutomirski <luto@...capital.net> wrote:
> > >> Indeed, and I have tests for this.
> > >
> > > Do you mean you already have a test case or that you would like a
> > > minimized test case?
> >
> > A smallish test that we could stick in selftests would be great if that’s straightforward.
>
> I'll look into it.
>
> - Kyle

A minimal test case follows.

The key to triggering this bug is to enter a ptrace syscall stop and
then use PTRACE_SINGLESTEP to exit it. On a good kernel this will not
result in any userspace code execution in the tracee because on the
way out of the kernel's syscall handling path the singlestep trap will
be raised immediately. On a bad kernel that stop will not be raised,
and in the example below, the program will crash.

- Kyle

---

#include <assert.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>

void do_child() {
  /* Synchronize with the parent */
  kill(getpid(), SIGSTOP);
  /* Do a syscall */
  printf("child is alive\n");
  /* Return and exit */
}

int main() {
  pid_t child = -1;
  int status = 0;
  unsigned long long previous_rip = 0;
  struct user_regs_struct regs;

  if ((child = fork()) == 0) {
      do_child();
      return 0;
  }

  /* Adds 0x80 to syscall stops so we can see them easily */
  intptr_t options = PTRACE_O_TRACESYSGOOD;
  /* Take control of the child (which should be waiting */
  assert(ptrace(PTRACE_SEIZE, child, NULL, options) == 0);
  assert(waitpid(child, &status, 0) == child);
  assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);

  /* Advance to the syscall stop for the write underlying
   * the child's printf.
   */
  assert(ptrace(PTRACE_SYSCALL, child, NULL, 0) == 0);
  assert(waitpid(child, &status, 0) == child);
  /* Should be a syscall stop */
  assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP | 0x80);

  /* Mess with the child's registers, so it will crash if
   * it executes any code
   */
  assert(ptrace(PTRACE_GETREGS, child, NULL, &regs) == 0);
  previous_rip = regs.rip;
  regs.rip = 0xdeadbeef;
  assert(ptrace(PTRACE_SETREGS, child, NULL, &regs) == 0);
  /* Singlestep. This should trap without executing any code */
  assert(ptrace(PTRACE_SINGLESTEP, child, NULL, 0) == 0);
  assert(waitpid(child, &status, 0) == child);
  /* Should be at a singlestep SIGTRAP. In a buggy kernel,
   * the SIGTRAP is skipped, execution resumes, and we
   * get a SIGSEGV at the invalid address.
   */
  assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP);

  /* Restore registers */
  assert(ptrace(PTRACE_GETREGS, child, NULL, &regs) == 0);
  regs.rip = previous_rip;
  assert(ptrace(PTRACE_SETREGS, child, NULL, &regs) == 0);

  /* Continue to the end of the program */
  assert(ptrace(PTRACE_CONT, child, NULL, 0) == 0);
  assert(waitpid(child, &status, 0) == child);
  /* Verify the child exited cleanly */
  assert(WIFEXITED(status) && WEXITSTATUS(status) == 0);

  printf("SUCCESS\n");

  return 0;
}

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ