1. 10 Dec, 2008 1 commit
    • Roland McGrath's avatar
      tracehook: exec double-reporting fix · 85f33466
      Roland McGrath authored
      The patch 6341c393 "tracehook: exec" introduced a small regression in
      2.6.27 regarding binfmt_misc exec event reporting.  Since the reporting
      is now done in the common search_binary_handler() function, an exec
      of a misc binary will result in two (or possibly multiple) exec events
      being reported, instead of just a single one, because the misc handler
      contains a recursive call to search_binary_handler.
      
      To add to the confusion, if PTRACE_O_TRACEEXEC is not active, the multiple
      SIGTRAP signals will in fact cause only a single ptrace intercept, as the
      signals are not queued.  However, if PTRACE_O_TRACEEXEC is on, the debugger
      will actually see multiple ptrace intercepts (PTRACE_EVENT_EXEC).
      
      The test program included below demonstrates the problem.
      
      This change fixes the bug by calling tracehook_report_exec() only in the
      outermost search_binary_handler() call (bprm->recursion_depth == 0).
      
      The additional change to restore bprm->recursion_depth after each binfmt
      load_binary call is actually superfluous for this bug, since we test the
      value saved on entry to search_binary_handler().  But it keeps the use of
      of the depth count to its most obvious expected meaning.  Depending on what
      binfmt handlers do in certain cases, there could have been false-positive
      tests for recursion limits before this change.
      
          /* Test program using PTRACE_O_TRACEEXEC.
             This forks and exec's the first argument with the rest of the arguments,
             while ptrace'ing.  It expects to see one PTRACE_EVENT_EXEC stop and
             then a successful exit, with no other signals or events in between.
      
             Test for kernel doing two PTRACE_EVENT_EXEC stops for a binfmt_misc exec:
      
             $ gcc -g traceexec.c -o traceexec
             $ sudo sh -c 'echo :test:M::foobar::/bin/cat: > /proc/sys/fs/binfmt_misc/register'
             $ echo 'foobar test' > ./foobar
             $ chmod +x ./foobar
             $ ./traceexec ./foobar; echo $?
             ==> good <==
             foobar test
             0
             $
             ==> bad <==
             foobar test
             unexpected status 0x4057f != 0
             3
             $
      
          */
      
          #include <stdio.h>
          #include <sys/types.h>
          #include <sys/wait.h>
          #include <sys/ptrace.h>
          #include <unistd.h>
          #include <signal.h>
          #include <stdlib.h>
      
          static void
          wait_for (pid_t child, int expect)
          {
            int status;
            pid_t p = wait (&status);
            if (p != child)
      	{
      	  perror ("wait");
      	  exit (2);
      	}
            if (status != expect)
      	{
      	  fprintf (stderr, "unexpected status %#x != %#x\n", status, expect);
      	  exit (3);
      	}
          }
      
          int
          main (int argc, char **argv)
          {
            pid_t child = fork ();
      
            if (child < 0)
      	{
      	  perror ("fork");
      	  return 127;
      	}
            else if (child == 0)
      	{
      	  ptrace (PTRACE_TRACEME);
      	  raise (SIGUSR1);
      	  execv (argv[1], &argv[1]);
      	  perror ("execve");
      	  _exit (127);
      	}
      
            wait_for (child, W_STOPCODE (SIGUSR1));
      
            if (ptrace (PTRACE_SETOPTIONS, child,
      		  0L, (void *) (long) PTRACE_O_TRACEEXEC) != 0)
      	{
      	  perror ("PTRACE_SETOPTIONS");
      	  return 4;
      	}
      
            if (ptrace (PTRACE_CONT, child, 0L, 0L) != 0)
      	{
      	  perror ("PTRACE_CONT");
      	  return 5;
      	}
      
            wait_for (child, W_STOPCODE (SIGTRAP | (PTRACE_EVENT_EXEC << 8)));
      
            if (ptrace (PTRACE_CONT, child, 0L, 0L) != 0)
      	{
      	  perror ("PTRACE_CONT");
      	  return 6;
      	}
      
            wait_for (child, W_EXITCODE (0, 0));
      
            return 0;
          }
      Reported-by: default avatarArnd Bergmann <arnd@arndb.de>
      CC: Ulrich Weigand <ulrich.weigand@de.ibm.com>
      Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
      85f33466
  2. 09 Dec, 2008 25 commits
  3. 08 Dec, 2008 9 commits
  4. 07 Dec, 2008 1 commit
  5. 06 Dec, 2008 2 commits
  6. 05 Dec, 2008 2 commits