コード例 #1
0
ファイル: test_clone.py プロジェクト: catern/rsyscall
 async def test_signal_queue(self) -> None:
     epoller = await Epoller.make_root(self.child.task)
     sigfd = await AsyncSignalfd.make(self.child.task, epoller,
                                      Sigset({SIG.INT}))
     sigevent = sigfd.next_signal
     await self.child.pid.kill(SIG.INT)
     await sigevent.wait()
コード例 #2
0
ファイル: test_ssh.py プロジェクト: catern/rsyscall
 async def test_sigmask_bug(self) -> None:
     process = await self.remote.fork()
     await rsyscall.thread.do_cloexec_except(
         process, set([fd.near for fd in process.task.fd_handles]))
     await self.remote.task.sigprocmask(
         (HowSIG.SETMASK, await self.remote.task.ptr(Sigset())), await
         self.remote.task.malloc(Sigset))
     await self.remote.task.read_oldset_and_check()
コード例 #3
0
ファイル: __init__.py プロジェクト: seh9000/rsyscall
 async def sigaction(self, signum: SIG,
                     act: t.Optional[Pointer[Sigaction]],
                     oldact: t.Optional[Pointer[Sigaction]]) -> None:
     with contextlib.ExitStack() as stack:
         act_n = self._borrow_optional(stack, act)
         oldact_n = self._borrow_optional(stack, oldact)
         # rt_sigaction takes the size of the sigset, not the size of the sigaction;
         # and sigset is a fixed size.
         await rsyscall.near.rt_sigaction(self.sysif, signum, act_n, oldact_n, Sigset.sizeof())
コード例 #4
0
ファイル: test_fork.py プロジェクト: seh9000/rsyscall
 async def test_signal_queue(self) -> None:
     # have to use an epoller for this specific task
     epoller = await Epoller.make_root(self.thr.ram, self.thr.task)
     sigfd = await AsyncSignalfd.make(self.thr.ram, self.thr.task, epoller,
                                      Sigset({SIG.INT}))
     await self.thr.task.process.kill(SIG.INT)
     buf = await self.thr.ram.malloc(SignalfdSiginfo)
     sigdata, _ = await sigfd.afd.read(buf)
     self.assertEqual((await sigdata.read()).signo, SIG.INT)
コード例 #5
0
 async def test_sigmask_bug(self) -> None:
     thread = await self.remote.fork()
     await thread.unshare_files(going_to_exec=True)
     await rsyscall.thread.do_cloexec_except(
         thread, set([fd.near for fd in thread.task.fd_handles]))
     await self.remote.task.sigprocmask(
         (HowSIG.SETMASK, await self.remote.ram.ptr(Sigset())), await
         self.remote.ram.malloc(Sigset))
     await self.remote.task.read_oldset_and_check()
コード例 #6
0
    async def clone(self, flags: CLONE = CLONE.NONE) -> ChildThread:
        """Create a new child thread

        manpage: clone(2)
        """
        child_process, task = await clone_child_task(
            self.task, self.ram, self.connection, self.loader, self.monitor,
            flags,
            lambda sock: Trampoline(self.loader.server_func, [sock, sock]))
        ram = RAM(
            task,
            # We don't inherit the transport because it leads to a deadlock:
            # If when a child task calls transport.read, it performs a syscall in the child task,
            # then the parent task will need to call waitid to monitor the child task during the syscall,
            # which will in turn need to also call transport.read.
            # But the child is already using the transport and holding the lock,
            # so the parent will block forever on taking the lock,
            # and child's read syscall will never complete.
            self.ram.transport,
            self.ram.allocator.inherit(task),
        )
        if flags & CLONE.NEWPID:
            # if the new process is pid 1, then CLONE_PARENT isn't allowed so we can't use inherit_to_child.
            # if we are a reaper, than we don't want our child CLONE_PARENTing to us, so we can't use inherit_to_child.
            # in both cases we just fall back to making a new ChildProcessMonitor for the child.
            epoller = await Epoller.make_root(ram, task)
            # this signal is already blocked, we inherited the block, um... I guess...
            # TODO handle this more formally
            signal_block = SignalBlock(task, await ram.ptr(Sigset({SIG.CHLD})))
            monitor = await ChildProcessMonitor.make(ram,
                                                     task,
                                                     epoller,
                                                     signal_block=signal_block)
        else:
            epoller = self.epoller.inherit(ram)
            monitor = self.monitor.inherit_to_child(task)
        thread = ChildThread(
            Thread(
                task,
                ram,
                self.connection.inherit(task, ram),
                self.loader,
                epoller,
                monitor,
                self.environ.inherit(task, ram),
                stdin=self.stdin.inherit(task),
                stdout=self.stdout.inherit(task),
                stderr=self.stderr.inherit(task),
            ), child_process)
        if flags & CLONE.NEWUSER:
            # hack, we should really track the [ug]id ahead of this so we don't have to get it
            # we have to get the [ug]id from the parent because it will fail in the child
            uid = await self.task.getuid()
            gid = await self.task.getgid()
            await write_user_mappings(thread, uid, gid)
        return thread
コード例 #7
0
ファイル: persistent.py プロジェクト: gc-ss/rsyscall
async def clone_persistent(
    parent: Thread,
    path: t.Union[str, os.PathLike],
) -> PersistentThread:
    """Create a new not-yet-persistent thread and return the thread and its tracking object

    To make the thread actually persistent, you must call PersistentServer.make_persistent().

    The point of this hoop-jumping is just to prevent unnecessary resource leakage, so you
    can set up things in a persistent thread and only make it persistent when you're
    actually ready.

    A persistent thread is essentially the same as a normal thread, just running a
    different function. As such, it starts off sharing its file descriptor table and
    everything else with its parent thread. It's only when we disconnect and reconnect
    that it changes behavior.

    """
    listening_sock = await parent.task.socket(AF.UNIX, SOCK.STREAM)
    await listening_sock.bind(await parent.ram.ptr(await SockaddrUn.from_path(
        parent, path)))
    await listening_sock.listen(1)
    child_process, task = await clone_child_task(
        parent.task, parent.ram, parent.connection, parent.loader,
        parent.monitor, CLONE.FILES | CLONE.FS | CLONE.SIGHAND,
        lambda sock: Trampoline(parent.loader.persistent_server_func,
                                [sock, sock, listening_sock]))
    listening_sock_handle = listening_sock.move(task)
    ram = RAM(task, parent.ram.transport, parent.ram.allocator.inherit(task))

    ## create the new persistent task
    epoller = await Epoller.make_root(ram, task)
    signal_block = SignalBlock(task, await ram.ptr(Sigset({SIG.CHLD})))
    # TODO use an inherited signalfd instead
    child_monitor = await ChildProcessMonitor.make(ram,
                                                   task,
                                                   epoller,
                                                   signal_block=signal_block)
    return PersistentThread(Thread(
        task,
        ram,
        parent.connection.inherit(task, ram),
        parent.loader,
        epoller,
        child_monitor,
        parent.environ.inherit(task, ram),
        stdin=parent.stdin.for_task(task),
        stdout=parent.stdout.for_task(task),
        stderr=parent.stderr.for_task(task),
    ),
                            persistent_path=path,
                            persistent_sock=listening_sock_handle)
コード例 #8
0
ファイル: monitor.py プロジェクト: catern/rsyscall
    async def make(
        task: Task,
        epoller: Epoller,
        *,
        signal_block: SignalBlock = None,
    ) -> ChildPidMonitor:
        """Make a ChildPidMonitor, possibly blocking signals

        If the signals are already blocked, the user can pass in a SignalBlock to
        represent that, and save the need to make the SignalBlock.

        """
        sigfd = await AsyncSignalfd.make(task,
                                         epoller,
                                         Sigset({SIG.CHLD}),
                                         signal_block=signal_block)
        return ChildPidMonitor(sigfd, task, use_clone_parent=False)
コード例 #9
0
    async def execve(
        self,
        path: t.Union[str, os.PathLike],
        argv: t.Sequence[t.Union[str, os.PathLike]],
        env_updates: t.Mapping[str, t.Union[str, os.PathLike]] = {},
        inherited_signal_blocks: t.List[SignalBlock] = [],
        command: Command = None,
    ) -> AsyncChildPid:
        """Replace the running executable in this process with another.

        self.exec is probably preferable; it takes a nice Command object which
        is easier to work with.

        We take inherited_signal_blocks as an argument so that we can default it
        to "inheriting" an empty signal mask. Most programs expect the signal
        mask to be cleared on startup. Since we're using signalfd as our signal
        handling method, we need to block signals with the signal mask; and if
        those blocked signals were inherited across exec, other programs would
        break (SIGCHLD is the most obvious example).

        We could depend on the user clearing the signal mask before calling
        exec, similar to how we require the user to remove CLOEXEC from
        inherited fds; but that is a fairly novel requirement to most, so for
        simplicity we just default to clearing the signal mask before exec, and
        allow the user to explicitly pass down additional signal blocks.

        """
        sigmask: t.Set[SIG] = set()
        for block in inherited_signal_blocks:
            sigmask = sigmask.union(block.mask)
        await self.task.sigprocmask((HowSIG.SETMASK, await
                                     self.task.ptr(Sigset(sigmask))))
        if not env_updates:
            # use execv if we aren't updating the env, as an optimization.
            return await self.execv(path, argv, command=command)
        envp: t.Dict[str, str] = {**self.environ.data}
        for key, value in env_updates.items():
            envp[key] = os.fsdecode(value)
        raw_envp: t.List[str] = [
            '='.join([key, value]) for key, value in envp.items()
        ]
        logger.debug("execveat(%s, %s, %s)", path, argv, env_updates)
        return await self._execve(path, [os.fsdecode(arg) for arg in argv],
                                  raw_envp,
                                  command=command)
コード例 #10
0
    async def clone(
            self,
            flags: CLONE = CLONE.NONE,
            automatically_write_user_mappings: bool = True) -> ChildProcess:
        """Create a new child process

        manpage: clone(2)
        """
        child_pid, task = await clone_child_task(
            self.task, self.connection, self.loader, self.monitor, flags,
            lambda sock: Trampoline(self.loader.server_func, [sock, sock]))
        if flags & CLONE.NEWPID:
            # if the new process is pid 1, then CLONE_PARENT isn't allowed so we can't use inherit_to_child.
            # if we are a reaper, than we don't want our child CLONE_PARENTing to us, so we can't use inherit_to_child.
            # in both cases we just fall back to making a new ChildPidMonitor for the child.
            epoller = await Epoller.make_root(task)
            # this signal is already blocked, we inherited the block, um... I guess...
            # TODO handle this more formally
            signal_block = SignalBlock(task, await
                                       task.ptr(Sigset({SIG.CHLD})))
            monitor = await ChildPidMonitor.make(task,
                                                 epoller,
                                                 signal_block=signal_block)
        else:
            epoller = self.epoller.inherit(task)
            monitor = self.monitor.inherit_to_child(task)
        process = ChildProcess(
            Process(
                task,
                self.connection.inherit(task),
                self.loader,
                epoller,
                monitor,
                self.environ.inherit(task),
                stdin=self.stdin.inherit(task),
                stdout=self.stdout.inherit(task),
                stderr=self.stderr.inherit(task),
            ), child_pid)
        if flags & CLONE.NEWUSER and automatically_write_user_mappings:
            # hack, we should really track the [ug]id ahead of this so we don't have to get it
            # we have to get the [ug]id from the parent because it will fail in the child
            uid = await self.task.getuid()
            gid = await self.task.getgid()
            await write_user_mappings(process, uid, gid)
        return process
コード例 #11
0
 async def fork(self, flags: CLONE = CLONE.SIGHAND) -> ChildUnixThread:
     "Create a new child thread"
     child_process, task = await self._fork_task(flags)
     ram = RAM(
         task,
         # We don't inherit the transport because it leads to a deadlock:
         # If when a child task calls transport.read, it performs a syscall in the child task,
         # then the parent task will need to call waitid to monitor the child task during the syscall,
         # which will in turn need to also call transport.read.
         # But the child is already using the transport and holding the lock,
         # so the parent will block forever on taking the lock,
         # and child's read syscall will never complete.
         self.ram.transport,
         self.ram.allocator.inherit(task),
     )
     if flags & CLONE.NEWPID:
         # if the new process is pid 1, then CLONE_PARENT isn't allowed so we can't use inherit_to_child.
         # if we are a reaper, than we don't want our child CLONE_PARENTing to us, so we can't use inherit_to_child.
         # in both cases we just fall back to making a new ChildProcessMonitor for the child.
         epoller = await Epoller.make_root(ram, task)
         # this signal is already blocked, we inherited the block, um... I guess...
         # TODO handle this more formally
         signal_block = SignalBlock(task, await ram.ptr(Sigset({SIG.CHLD})))
         monitor = await ChildProcessMonitor.make(ram,
                                                  task,
                                                  epoller,
                                                  signal_block=signal_block)
     else:
         epoller = self.epoller.inherit(ram)
         monitor = self.monitor.inherit_to_child(ram, task)
     return ChildUnixThread(UnixThread(
         task,
         ram,
         self.connection.for_task(task, ram),
         self.loader,
         epoller,
         monitor,
         self.environ.inherit(task, ram),
         stdin=self.stdin.for_task(task),
         stdout=self.stdout.for_task(task),
         stderr=self.stderr.for_task(task),
     ),
                            process=child_process)
コード例 #12
0
ファイル: stub.py プロジェクト: gc-ss/rsyscall
async def _setup_stub(
    thread: Thread,
    bootstrap_sock: FileDescriptor,
) -> t.Tuple[t.List[str], Thread]:
    "Setup a stub thread"
    [(access_syscall_sock, passed_syscall_sock),
     (access_data_sock, passed_data_sock)
     ] = await thread.open_async_channels(2)
    # memfd for setting up the futex
    futex_memfd = await thread.task.memfd_create(await thread.ram.ptr(
        Path("child_robust_futex_list")))
    # send the fds to the new process
    connection_fd, make_connection = await thread.connection.prep_fd_transfer()

    async def sendmsg_op(sem: RAM) -> WrittenPointer[SendMsghdr]:
        iovec = await sem.ptr(IovecList([await sem.malloc(bytes, 1)]))
        cmsgs = await sem.ptr(
            CmsgList([
                CmsgSCMRights([
                    passed_syscall_sock, passed_data_sock, futex_memfd,
                    connection_fd
                ])
            ]))
        return await sem.ptr(SendMsghdr(None, iovec, cmsgs))

    _, [] = await bootstrap_sock.sendmsg(
        await thread.ram.perform_batch(sendmsg_op), SendmsgFlags.NONE)
    # close our reference to fds that only the new process needs
    await passed_syscall_sock.invalidate()
    await passed_data_sock.invalidate()
    # close the socketpair
    await bootstrap_sock.invalidate()
    #### read describe to get all the information we need from the new process
    describe_buf = AsyncReadBuffer(access_data_sock)
    describe_struct = await describe_buf.read_cffi('struct rsyscall_unix_stub')
    argv_raw = await describe_buf.read_length_prefixed_array(
        describe_struct.argc)
    argv = [os.fsdecode(arg) for arg in argv_raw]
    environ = await describe_buf.read_envp(describe_struct.envp_count)
    #### build the new task
    pid = describe_struct.pid
    fd_table = handle.FDTable(pid)
    address_space = far.AddressSpace(pid)
    # we assume pid namespace is shared
    pidns = thread.task.pidns
    process = near.Process(pid)
    # we assume net namespace is shared - that's dubious...
    # we should make it possible to control the namespace sharing more, hmm.
    # TODO maybe the describe should contain the net namespace number? and we can store our own as well?
    # then we can automatically do it right
    base_task = Task(process, fd_table, address_space, pidns)
    remote_syscall_fd = base_task.make_fd_handle(
        near.FileDescriptor(describe_struct.syscall_fd))
    base_task.sysif = SyscallConnection(
        logger.getChild(str(process)),
        access_syscall_sock,
        access_syscall_sock,
        remote_syscall_fd,
        remote_syscall_fd,
    )
    allocator = memory.AllocatorClient.make_allocator(base_task)
    base_task.sigmask = Sigset(
        {SIG(bit)
         for bit in rsyscall.struct.bits(describe_struct.sigmask)})
    ram = RAM(
        base_task,
        SocketMemoryTransport(
            access_data_sock,
            base_task.make_fd_handle(
                near.FileDescriptor(describe_struct.data_fd))), allocator)
    # TODO I think I can maybe elide creating this epollcenter and instead inherit it or share it, maybe?
    # I guess I need to write out the set too in describe
    epoller = await Epoller.make_root(ram, base_task)
    child_monitor = await ChildProcessMonitor.make(ram, base_task, epoller)
    connection = make_connection(
        base_task, ram,
        base_task.make_fd_handle(
            near.FileDescriptor(describe_struct.connecting_fd)))
    new_thread = Thread(
        task=base_task,
        ram=ram,
        connection=connection,
        loader=NativeLoader.make_from_symbols(base_task,
                                              describe_struct.symbols),
        epoller=epoller,
        child_monitor=child_monitor,
        environ=Environment.make_from_environ(base_task, ram, environ),
        stdin=base_task.make_fd_handle(near.FileDescriptor(0)),
        stdout=base_task.make_fd_handle(near.FileDescriptor(1)),
        stderr=base_task.make_fd_handle(near.FileDescriptor(2)),
    )
    #### TODO set up futex I guess
    remote_futex_memfd = near.FileDescriptor(describe_struct.futex_memfd)
    return argv, new_thread