Example #1
0
    def RegisterBinfmt(self):
        """Make sure qemu has been registered as a format handler

    Prep the binfmt handler. First mount if needed, then unregister any bad
    mappings, and then register our mapping.

    There may still be some race conditions here where one script
    de-registers and another script starts executing before it gets
    re-registered, however it should be rare.
    """
        if not os.path.exists(self._BINFMT_REGISTER_PATH):
            osutils.Mount('binfmt_misc', self._BINFMT_PATH, 'binfmt_misc', 0)

        if os.path.exists(self.binfmt_path):
            interp = 'interpreter %s\n' % self.build_path
            for line in osutils.ReadFile(self.binfmt_path):
                if line == interp:
                    break
            else:
                osutils.WriteFile(self.binfmt_path, '-1')

        if not os.path.exists(self.binfmt_path):
            register = self.GetRegisterBinfmtStr(self.arch, self.name,
                                                 self.build_path)
            try:
                osutils.WriteFile(self._BINFMT_REGISTER_PATH, register)
            except IOError:
                logging.error('error: attempted to register: (len:%i) %s',
                              len(register), register)
Example #2
0
    def Run(self):
        """Runs the debugger in a proper environment (e.g. qemu)."""

        self.VerifyAndFinishInitialization(None)
        self.SetupUser()
        if self.framework == 'qemu':
            self.qemu.Install(self.sysroot)
            self.qemu.RegisterBinfmt()

        for mount in self._BIND_MOUNT_PATHS:
            path = os.path.join(self.sysroot, mount)
            osutils.SafeMakedirs(path)
            osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)

        gdb_cmd = self._GDB
        inferior_cmd = self.inf_cmd

        gdb_argv = self.gdb_args[:]
        if gdb_argv:
            gdb_argv[0] = self.RemoveSysrootPrefix(gdb_argv[0])
        # Some programs expect to find data files via $CWD, so doing a chroot
        # and dropping them into / would make them fail.
        cwd = self.RemoveSysrootPrefix(os.getcwd())

        os.chroot(self.sysroot)
        os.chdir(cwd)
        # The TERM the user is leveraging might not exist in the sysroot.
        # Force a sane default that supports standard color sequences.
        os.environ['TERM'] = 'ansi'
        # Some progs want this like bash else they get super confused.
        os.environ['PWD'] = cwd
        if not self.run_as_root:
            _, uid, gid, home = self.GetNonRootAccount()
            os.setgid(gid)
            os.setuid(uid)
            os.environ['HOME'] = home

        gdb_commands = self.GetGdbInitCommands(inferior_cmd)

        gdb_args = [gdb_cmd, '--quiet'
                    ] + ['--eval-command=%s' % x for x in gdb_commands]
        gdb_args += self.gdb_args

        sys.exit(os.execvp(gdb_cmd, gdb_args))
Example #3
0
    def RegisterBinfmt(self):
        """Make sure qemu has been registered as a format handler

    Prep the binfmt handler. First mount if needed, then unregister any bad
    mappings, and then register our mapping.

    There may still be some race conditions here where one script
    de-registers and another script starts executing before it gets
    re-registered, however it should be rare.
    """
        if not os.path.exists(self._BINFMT_REGISTER_PATH):
            osutils.Mount('binfmt_misc', self._BINFMT_PATH, 'binfmt_misc', 0)

        if os.path.exists(self.binfmt_path):
            interp = b'\ninterpreter %s\n' % (self.GetFullInterpPath(
                self.build_path).encode('utf-8'), )
            data = b'\n' + osutils.ReadFile(self.binfmt_path, mode='rb')
            if interp not in data:
                logging.info('recreating binfmt config: %s', self.binfmt_path)
                logging.debug('config was missing line: %s', interp.strip())
                logging.debug('existing config:\n%s', data.strip())
                try:
                    osutils.WriteFile(self.binfmt_path, '-1')
                except IOError as e:
                    logging.error('unregistering config failed: %s: %s',
                                  self.binfmt_path, e)
                    return

        if not os.path.exists(self.binfmt_path):
            register = self.GetRegisterBinfmtStr(self.arch, self.name,
                                                 self.build_path)
            try:
                osutils.WriteFile(self._BINFMT_REGISTER_PATH,
                                  register,
                                  mode='wb')
            except IOError as e:
                logging.error('binfmt register attempt failed: %s: %s',
                              self._BINFMT_REGISTER_PATH, e)
                logging.error('register data (len:%i): %s', len(register),
                              register)
    def run(self):
        """Runs the test in a proper environment (e.g. qemu)."""

        # We know these pre-tests are fast (especially if they've already been run
        # once), so run them automatically for the user if they test by hand.
        self.pre_test()

        paths_to_mount = (list(self._BIND_MOUNT_PATHS) + [
            mount for mount in self._BIND_MOUNT_IF_NOT_SYMLINK_PATHS
            if not os.path.islink('/' + mount)
        ])
        for mount in paths_to_mount:
            path = os.path.join(self.sysroot, mount)
            osutils.SafeMakedirs(path)
            osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)

        positive_filters = self.gtest_filter[0]
        negative_filters = self.gtest_filter[1]

        if self.user_gtest_filter:
            positive_filters += self.user_gtest_filter[0]
            negative_filters += self.user_gtest_filter[1]

        filters = (':'.join(positive_filters), ':'.join(negative_filters))
        gtest_filter = '%s-%s' % filters

        cmd = self.removeSysrootPrefix(self.bin)
        argv = self.args[:]
        argv[0] = self.removeSysrootPrefix(argv[0])
        if gtest_filter != '-':
            argv.append('--gtest_filter=' + gtest_filter)

        # Some programs expect to find data files via $CWD, so doing a chroot
        # and dropping them into / would make them fail.
        cwd = self.removeSysrootPrefix(os.getcwd())

        # Make orphaned child processes reparent to this process instead of the init
        # process.  This allows us to kill them if they do not terminate after the
        # test has finished running.
        _MakeProcessSubreaper()

        # Fork off a child to run the test.  This way we can make tweaks to the
        # env that only affect the child (gid/uid/chroot/cwd/etc...).  We have
        # to fork anyways to run the test, so might as well do it all ourselves
        # to avoid (slow) chaining through programs like:
        #   sudo -u $SUDO_UID -g $SUDO_GID chroot $SYSROOT bash -c 'cd $CWD; $BIN'
        child = os.fork()
        if child == 0:
            print('chroot: %s' % self.sysroot)
            print('cwd: %s' % cwd)
            print('cmd: {%s} %s' % (cmd, ' '.join(map(repr, argv))))
            os.chroot(self.sysroot)
            os.chdir(cwd)

            # Set the child's pgid to its pid, so we can kill any processes that the
            # child creates after the child terminates.
            os.setpgid(0, 0)

            # Remove sysroot from path environment variables.
            for var in ('OUT', 'SRC', 'T'):
                if var in os.environ:
                    os.environ[var] = self.removeSysrootPrefix(os.environ[var])

            # The TERM the user is leveraging might not exist in the sysroot.
            # Force a sane default that supports standard color sequences.
            os.environ['TERM'] = 'ansi'
            # Some progs want this like bash else they get super confused.
            os.environ['PWD'] = cwd
            os.environ['GTEST_COLOR'] = 'yes'
            if not self.run_as_root:
                user, uid, gid, home = self.GetNonRootAccount()
                os.setgid(gid)
                os.setuid(uid)
                os.environ['HOME'] = home
                os.environ['USER'] = user
            sys.exit(os.execvp(cmd, argv))

        proctitle.settitle('sysroot watcher', cmd)

        # Mask SIGINT with the assumption that the child will catch & process it.
        # We'll pass that back up below.
        signal.signal(signal.SIGINT, signal.SIG_IGN)

        # Reap any processes that were reparented to us until the child exits.
        status = _ReapUntilProcessExits(child)

        leaked_children = psutil.Process().get_children(recursive=True)
        if leaked_children:
            # It's possible the child forked and the forked processes are still
            # running.  Kill the forked processes.
            try:
                os.killpg(child, signal.SIGTERM)
            except OSError as e:
                if e.errno != errno.ESRCH:
                    print(
                        'Warning: while trying to kill pgid %s caught exception\n%s'
                        % (child, e),
                        file=sys.stderr)

            # Kill any orphaned processes originally created by the test that were in
            # a different process group.  This will also kill any processes that did
            # not respond to the SIGTERM.
            for child in leaked_children:
                try:
                    child.kill()
                except psutil.NoSuchProcess:
                    pass

        failmsg = None
        if os.WIFSIGNALED(status):
            sig = os.WTERMSIG(status)
            failmsg = 'signal %s(%i)' % (signals.StrSignal(sig), sig)
        else:
            exit_status = os.WEXITSTATUS(status)
            if exit_status:
                failmsg = 'exit code %i' % exit_status
        if failmsg:
            print('Error: %s: failed with %s' % (cmd, failmsg),
                  file=sys.stderr)

        if leaked_children:
            for p in leaked_children:
                print(
                    'Error: the test leaked process %s with pid %s (it was forcefully'
                    ' killed)' % (p.name(), p.pid),
                    file=sys.stderr)
            # TODO(vapier): Make this an error.  We need to track down some scenarios
            # where processes do leak though before we can make this fatal :(.
            #sys.exit(100)

        process_util.ExitAsStatus(status)
def CreatePidNs():
    """Start a new pid namespace

  This will launch all the right manager processes.  The child that returns
  will be isolated in a new pid namespace.

  If functionality is not available, then it will return w/out doing anything.

  A note about the processes generated as a result of calling this function:
  You call CreatePidNs() in pid X
  - X launches Pid Y,
    - Pid X will now do nothing but wait for Pid Y to finish and then sys.exit()
      with that return code
    - Y launches Pid Z
      - Pid Y will now do nothing but wait for Pid Z to finish and then
        sys.exit() with that return code
      - **Pid Z returns from CreatePidNs**. So, the caller of this function
        continues in a different process than the one that made the call.
          - All SIGTERM/SIGINT signals are forwarded down from pid X to pid Z to
            handle.
          - SIGKILL will only kill pid X, and leak Pid Y and Z.

  Returns:
    The last pid outside of the namespace. (i.e., pid X)
  """
    first_pid = os.getpid()

    try:
        # First create the namespace.
        Unshare(CLONE_NEWPID)
    except OSError as e:
        if e.errno == errno.EINVAL:
            # For older kernels, or the functionality is disabled in the config,
            # return silently.  We don't want to hard require this stuff.
            return first_pid
        else:
            # For all other errors, abort.  They shouldn't happen.
            raise

    # Used to make sure process groups are in the right state before we try to
    # forward the controlling terminal.
    lock = locking.PipeLock()

    # Now that we're in the new pid namespace, fork.  The parent is the master
    # of it in the original namespace, so it only monitors the child inside it.
    # It is only allowed to fork once too.
    pid = os.fork()
    if pid:
        proctitle.settitle('pid ns', 'external init')

        # We forward termination signals to the child and trust the child to respond
        # sanely. Later, ExitAsStatus propagates the exit status back up.
        _ForwardToChildPid(pid, signal.SIGINT)
        _ForwardToChildPid(pid, signal.SIGTERM)

        # Forward the control of the terminal to the child so it can manage input.
        _SafeTcSetPgrp(sys.stdin.fileno(), pid)

        # Signal our child it can move forward.
        lock.Post()
        del lock

        # Reap the children as the parent of the new namespace.
        process_util.ExitAsStatus(_ReapChildren(pid))
    else:
        # Make sure to unshare the existing mount point if needed.  Some distros
        # create shared mount points everywhere by default.
        try:
            osutils.Mount('none', '/proc', 0,
                          osutils.MS_PRIVATE | osutils.MS_REC)
        except OSError as e:
            if e.errno != errno.EINVAL:
                raise

        # The child needs its own proc mount as it'll be different.
        osutils.Mount(
            'proc', '/proc', 'proc', osutils.MS_NOSUID | osutils.MS_NODEV
            | osutils.MS_NOEXEC | osutils.MS_RELATIME)

        # Wait for our parent to finish initialization.
        lock.Wait()
        del lock

        # Resetup the locks for the next phase.
        lock = locking.PipeLock()

        pid = os.fork()
        if pid:
            proctitle.settitle('pid ns', 'init')

            # We forward termination signals to the child and trust the child to
            # respond sanely. Later, ExitAsStatus propagates the exit status back up.
            _ForwardToChildPid(pid, signal.SIGINT)
            _ForwardToChildPid(pid, signal.SIGTERM)

            # Now that we're in a new pid namespace, start a new process group so that
            # children have something valid to use.  Otherwise getpgrp/etc... will get
            # back 0 which tends to confuse -- you can't setpgrp(0) for example.
            os.setpgrp()

            # Forward the control of the terminal to the child so it can manage input.
            _SafeTcSetPgrp(sys.stdin.fileno(), pid)

            # Signal our child it can move forward.
            lock.Post()
            del lock

            # Watch all of the children.  We need to act as the master inside the
            # namespace and reap old processes.
            process_util.ExitAsStatus(_ReapChildren(pid))

    # Wait for our parent to finish initialization.
    lock.Wait()
    del lock

    # Create a process group for the grandchild so it can manage things
    # independent of the init process.
    os.setpgrp()

    # The grandchild will return and take over the rest of the sdk steps.
    return first_pid
Example #6
0
def CreatePidNs():
    """Start a new pid namespace

  This will launch all the right manager processes.  The child that returns
  will be isolated in a new pid namespace.

  If functionality is not available, then it will return w/out doing anything.

  Returns:
    The last pid outside of the namespace.
  """
    first_pid = os.getpid()

    try:
        # First create the namespace.
        Unshare(CLONE_NEWPID)
    except OSError as e:
        if e.errno == errno.EINVAL:
            # For older kernels, or the functionality is disabled in the config,
            # return silently.  We don't want to hard require this stuff.
            return first_pid
        else:
            # For all other errors, abort.  They shouldn't happen.
            raise

    # Used to make sure process groups are in the right state before we try to
    # forward the controlling terminal.
    lock = locking.PipeLock()

    # Now that we're in the new pid namespace, fork.  The parent is the master
    # of it in the original namespace, so it only monitors the child inside it.
    # It is only allowed to fork once too.
    pid = os.fork()
    if pid:
        proctitle.settitle('pid ns', 'external init')

        # Mask SIGINT with the assumption that the child will catch & process it.
        # We'll pass that back up below.
        signal.signal(signal.SIGINT, signal.SIG_IGN)

        # Forward the control of the terminal to the child so it can manage input.
        _SafeTcSetPgrp(sys.stdin.fileno(), pid)

        # Signal our child it can move forward.
        lock.Post()
        del lock

        # Reap the children as the parent of the new namespace.
        process_util.ExitAsStatus(_ReapChildren(pid))
    else:
        # Make sure to unshare the existing mount point if needed.  Some distros
        # create shared mount points everywhere by default.
        try:
            osutils.Mount('none', '/proc', 0,
                          osutils.MS_PRIVATE | osutils.MS_REC)
        except OSError as e:
            if e.errno != errno.EINVAL:
                raise

        # The child needs its own proc mount as it'll be different.
        osutils.Mount(
            'proc', '/proc', 'proc', osutils.MS_NOSUID | osutils.MS_NODEV
            | osutils.MS_NOEXEC | osutils.MS_RELATIME)

        # Wait for our parent to finish initialization.
        lock.Wait()
        del lock

        # Resetup the locks for the next phase.
        lock = locking.PipeLock()

        pid = os.fork()
        if pid:
            proctitle.settitle('pid ns', 'init')

            # Mask SIGINT with the assumption that the child will catch & process it.
            # We'll pass that back up below.
            signal.signal(signal.SIGINT, signal.SIG_IGN)

            # Now that we're in a new pid namespace, start a new process group so that
            # children have something valid to use.  Otherwise getpgrp/etc... will get
            # back 0 which tends to confuse -- you can't setpgrp(0) for example.
            os.setpgrp()

            # Forward the control of the terminal to the child so it can manage input.
            _SafeTcSetPgrp(sys.stdin.fileno(), pid)

            # Signal our child it can move forward.
            lock.Post()
            del lock

            # Watch all of the children.  We need to act as the master inside the
            # namespace and reap old processes.
            process_util.ExitAsStatus(_ReapChildren(pid))

    # Wait for our parent to finish initialization.
    lock.Wait()
    del lock

    # Create a process group for the grandchild so it can manage things
    # independent of the init process.
    os.setpgrp()

    # The grandchild will return and take over the rest of the sdk steps.
    return first_pid
    def run(self):
        """Runs the test in a proper environment (e.g. qemu)."""

        # We know these pre-tests are fast (especially if they've already been run
        # once), so run them automatically for the user if they test by hand.
        self.pre_test()

        for mount in self._BIND_MOUNT_PATHS:
            path = os.path.join(self.sysroot, mount)
            osutils.SafeMakedirs(path)
            osutils.Mount('/' + mount, path, 'none', osutils.MS_BIND)

        positive_filters = self.gtest_filter[0]
        negative_filters = self.gtest_filter[1]

        if self.user_gtest_filter:
            positive_filters += self.user_gtest_filter[0]
            negative_filters += self.user_gtest_filter[1]

        filters = (':'.join(positive_filters), ':'.join(negative_filters))
        gtest_filter = '%s-%s' % filters

        cmd = self.removeSysrootPrefix(self.bin)
        argv = self.args[:]
        argv[0] = self.removeSysrootPrefix(argv[0])
        if gtest_filter != '-':
            argv.append('--gtest_filter=' + gtest_filter)

        # Some programs expect to find data files via $CWD, so doing a chroot
        # and dropping them into / would make them fail.
        cwd = self.removeSysrootPrefix(os.getcwd())

        # Fork off a child to run the test.  This way we can make tweaks to the
        # env that only affect the child (gid/uid/chroot/cwd/etc...).  We have
        # to fork anyways to run the test, so might as well do it all ourselves
        # to avoid (slow) chaining through programs like:
        #   sudo -u $SUDO_UID -g $SUDO_GID chroot $SYSROOT bash -c 'cd $CWD; $BIN'
        child = os.fork()
        if child == 0:
            print('chroot: %s' % self.sysroot)
            print('cwd: %s' % cwd)
            print('cmd: {%s} %s' % (cmd, ' '.join(map(repr, argv))))
            os.chroot(self.sysroot)
            os.chdir(cwd)
            # The TERM the user is leveraging might not exist in the sysroot.
            # Force a sane default that supports standard color sequences.
            os.environ['TERM'] = 'ansi'
            # Some progs want this like bash else they get super confused.
            os.environ['PWD'] = cwd
            if not self.run_as_root:
                _, uid, gid, home = self.GetNonRootAccount()
                os.setgid(gid)
                os.setuid(uid)
                os.environ['HOME'] = home
            sys.exit(os.execvp(cmd, argv))

        proctitle.settitle('sysroot watcher', cmd)

        # Mask SIGINT with the assumption that the child will catch & process it.
        # We'll pass that back up below.
        signal.signal(signal.SIGINT, signal.SIG_IGN)
        _, status = os.waitpid(child, 0)

        failmsg = None
        if os.WIFSIGNALED(status):
            sig = os.WTERMSIG(status)
            failmsg = 'signal %s(%i)' % (signals.StrSignal(sig), sig)
        else:
            exit_status = os.WEXITSTATUS(status)
            if exit_status:
                failmsg = 'exit code %i' % exit_status
        if failmsg:
            print('Error: %s: failed with %s' % (cmd, failmsg),
                  file=sys.stderr)

        process_util.ExitAsStatus(status)