Beispiel #1
0
 async def move_fds(self,
                    fds: t.List[FileDescriptor]) -> t.List[FileDescriptor]:
     "Move the passed-in file descriptors from self.access_task to self.task"
     if self.access_task.fd_table == self.task.fd_table:
         return [fd.move(self.task) for fd in fds]
     iovec = await self.access_task.ptr(
         IovecList([await self.access_task.malloc(bytes, 1)]))
     cmsgs = await self.access_task.ptr(
         CmsgList([CmsgSCMRights([fd for fd in fds])]))
     _, [] = await self.access_fd.sendmsg(await self.access_task.ptr(
         SendMsghdr(None, iovec, cmsgs)))
     iovec = await self.task.ptr(
         IovecList([await self.task.malloc(bytes, 1)]))
     cmsgs = await self.task.ptr(
         CmsgList([CmsgSCMRights([fd for fd in fds])]))
     _, [], hdr = await self.fd.recvmsg(await self.task.ptr(
         RecvMsghdr(None, iovec, cmsgs)))
     cmsgs_ptr = (await hdr.read()).control
     if cmsgs_ptr is None:
         raise Exception("cmsgs field of header is, impossibly, None")
     [cmsg] = await cmsgs_ptr.read()
     if not isinstance(cmsg, CmsgSCMRights):
         raise Exception("expected SCM_RIGHTS cmsg, instead got", cmsg)
     passed_socks = cmsg
     for sock in fds:
         await sock.close()
     return passed_socks
Beispiel #2
0
 async def test_readv_writev(self):
     in_data = [b"hello", b"world"]
     iov = await self.process.ptr(
         IovecList([await self.process.ptr(data) for data in in_data]))
     written, partial, rest = await self.pipe.write.writev(iov)
     read, partial, rest = await self.pipe.read.readv(written)
     self.assertEqual(in_data, [await ptr.read() for ptr in read.value])
Beispiel #3
0
async def _connect_and_send(
        self: PersistentProcess, process: Process,
        syscall_sock: FileDescriptor, data_sock: FileDescriptor,
) -> t.Tuple[FileDescriptor, FileDescriptor]:
    """Connect to a persistent process's socket, send some file descriptors

    """
    fds = [syscall_sock, data_sock]
    sock = await process.make_afd(await process.socket(AF.UNIX, SOCK.STREAM|SOCK.NONBLOCK))
    sockaddr_un = await SockaddrUn.from_path(process, self.persistent_path)
    addr = await process.ptr(sockaddr_un)
    count = await process.ptr(Int32(len(fds)))
    iovec = await process.ptr(IovecList([await process.malloc(bytes, 1)]))
    cmsgs = await process.ptr(CmsgList([CmsgSCMRights(fds)]))
    hdr = await process.ptr(SendMsghdr(None, iovec, cmsgs))
    response: Pointer = await process.ptr(StructList(Int32, [Int32(0)]*len(fds)))
    data = None
    await sock.connect(addr)
    _, _ = await sock.write(count)
    _, [] = await sock.handle.sendmsg(hdr, SendmsgFlags.NONE)
    while response.size() > 0:
        valid, response = await sock.read(response)
        data += valid
    remote_syscall_sock, remote_data_sock = [self.task.make_fd_handle(near.FileDescriptor(int(i)))
                  for i in ((await data.read()).elems if data else [])]
    await sock.close()
    return remote_syscall_sock, remote_data_sock
Beispiel #4
0
 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, connection_fd])
         ]))
     return await sem.ptr(SendMsghdr(None, iovec, cmsgs))
Beispiel #5
0
 async def sendmsg_op(
     sem: RAM
 ) -> t.Tuple[WrittenPointer[SockaddrUn], WrittenPointer[Int32],
              WrittenPointer[SendMsghdr], Pointer[StructList[Int32]]]:
     addr = await sem.ptr(sockaddr_un)
     count = await sem.ptr(Int32(len(fds)))
     iovec = await sem.ptr(IovecList([await sem.malloc(bytes, 1)]))
     cmsgs = await sem.ptr(CmsgList([CmsgSCMRights(fds)]))
     hdr = await sem.ptr(SendMsghdr(None, iovec, cmsgs))
     response_buf = await sem.ptr(StructList(Int32, [Int32(0)] * len(fds)))
     return addr, count, hdr, response_buf
Beispiel #6
0
    async def test_pass_fd(self) -> None:
        fds = await (await self.process.task.socketpair(
            AF.UNIX, SOCK.STREAM, 0,
            await self.process.task.malloc(Socketpair))).read()
        in_data = b"hello"

        iovec = await self.process.task.ptr(IovecList([await self.process.task.ptr(in_data)]))
        cmsgs = await self.process.task.ptr(CmsgList([CmsgSCMRights([fds.second])]))
        [written], [] = await fds.second.sendmsg(
            await self.process.task.ptr(SendMsghdr(None, iovec, cmsgs)), SendmsgFlags.NONE)

        [valid], [], hdr = await fds.first.recvmsg(
            await self.process.task.ptr(RecvMsghdr(None, iovec, cmsgs)), RecvmsgFlags.NONE)

        self.assertEqual(in_data, await valid.read())

        hdrval = await hdr.read()
        [[passed_fd]] = await hdrval.control.read() # type: ignore
        self.assertEqual(hdrval.name, None)
        self.assertEqual(hdrval.flags, MsghdrFlags.CMSG_CLOEXEC)
Beispiel #7
0
 async def recvmsg_op(sem: RAM) -> WrittenPointer[RecvMsghdr]:
     iovec = await sem.ptr(IovecList([await sem.malloc(bytes, 1)]))
     cmsgs = await sem.ptr(CmsgList([CmsgSCMRights([fd
                                                    for fd in fds])]))
     return await sem.ptr(RecvMsghdr(None, iovec, cmsgs))
Beispiel #8
0
async def stdin_bootstrap(
    parent: Process,
    bootstrap_command: Command,
) -> t.Tuple[AsyncChildPid, Process]:
    """Create a process from running an arbitrary command which must run rsyscall-stdin-bootstrap

    bootstrap_command can be any arbitrary command, but it must eventually exec
    rsyscall-stdin-bootstrap, and pass down stdin when it does.

    We'll clone and exec bootstrap_command, passing down a socketpair for stdin, and try to
    bootstrap over the other end of the socketpair. Once rsyscall-stdin-bootstrap starts,
    it will respond to our bootstrap and we'll create a new process.

    """
    #### clone and exec into the bootstrap command
    # create the socketpair that will be used as stdin
    stdin_pair = await (await parent.task.socketpair(
        AF.UNIX, SOCK.STREAM, 0, await parent.task.malloc(Socketpair))).read()
    parent_sock = stdin_pair.first
    child = await parent.fork()
    # set up stdin with socketpair
    await child.task.inherit_fd(stdin_pair.second).dup2(child.stdin)
    await stdin_pair.second.close()
    # exec
    child_pid = await child.exec(bootstrap_command)
    #### set up all the fds we'll want to pass over
    # the basic connections
    [(access_syscall_sock, passed_syscall_sock),
     (access_data_sock, passed_data_sock)
     ] = await parent.open_async_channels(2)
    # send the fds to the new process
    connection_fd, make_connection = await parent.connection.prep_fd_transfer()
    iovec = await parent.ptr(IovecList([await parent.malloc(bytes, 1)]))
    cmsgs = await parent.ptr(
        CmsgList([
            CmsgSCMRights(
                [passed_syscall_sock, passed_data_sock, connection_fd])
        ]))
    _, [] = await parent_sock.sendmsg(
        await parent.ptr(SendMsghdr(None, iovec, cmsgs)), SendmsgFlags.NONE)
    # close our reference to fds that only the new process needs
    await passed_syscall_sock.close()
    await passed_data_sock.close()
    # close the socketpair
    await parent_sock.close()
    #### 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_stdin_bootstrap')
    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
    # TODO include namespace inode numbers numbers in describe
    # note: if we start dealing with namespace numbers then we need to
    # have a Kernel namespace which tells us which kernel we get those
    # numbers from.
    # oh hey we can conveniently dump the inode numbers with getdents!
    pidns = parent.task.pidns
    # we assume mount namespace is not shared (can't hurt)
    mountns = far.MountNamespace(pid)
    pid = near.Pid(pid)
    base_task = Task(pid, fd_table, address_space, pidns, mountns)
    remote_syscall_fd = base_task.make_fd_handle(
        near.FileDescriptor(describe_struct.syscall_fd))
    base_task.sysif = SyscallConnection(
        logger.getChild(str(pid)),
        access_syscall_sock,
        remote_syscall_fd,
    )
    base_task.allocator = await memory.AllocatorClient.make_allocator(base_task
                                                                      )
    # we assume our SignalMask is zero'd before being started, so we don't inherit it
    # TODO I think I can maybe elide creating this epollcenter and instead inherit it or share it, maybe?
    epoller = await Epoller.make_root(base_task)
    child_monitor = await ChildPidMonitor.make(base_task, epoller)
    connection = make_connection(
        base_task,
        base_task.make_fd_handle(
            near.FileDescriptor(describe_struct.connecting_fd)))
    new_parent = Process(
        task=base_task,
        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, 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)),
    )
    return child_pid, new_parent
Beispiel #9
0
async def _setup_stub(
    process: Process,
    bootstrap_sock: FileDescriptor,
) -> t.Tuple[t.List[str], Process]:
    "Setup a stub process"
    [(access_syscall_sock, passed_syscall_sock),
     (access_data_sock, passed_data_sock)
     ] = await process.open_async_channels(2)
    # memfd for setting up the futex
    futex_memfd = await process.task.memfd_create(await process.task.ptr(
        Path("child_robust_futex_list")))
    # send the fds to the new process
    connection_fd, make_connection = await process.connection.prep_fd_transfer(
    )
    iovec = await process.ptr(IovecList([await process.malloc(bytes, 1)]))
    cmsgs = await process.ptr(
        CmsgList([
            CmsgSCMRights([
                passed_syscall_sock, passed_data_sock, futex_memfd,
                connection_fd
            ])
        ]))
    _, [] = await bootstrap_sock.sendmsg(
        await process.ptr(SendMsghdr(None, iovec, cmsgs)), 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 = process.task.pidns
    # we assume mount namespace is not shared (won't hurt)
    mountns = far.MountNamespace(pid)
    pid = near.Pid(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(pid, fd_table, address_space, pidns, mountns)
    remote_syscall_fd = base_task.make_fd_handle(
        near.FileDescriptor(describe_struct.syscall_fd))
    base_task.sysif = SyscallConnection(
        logger.getChild(str(pid)),
        access_syscall_sock,
        remote_syscall_fd,
    )
    base_task.allocator = await memory.AllocatorClient.make_allocator(base_task
                                                                      )
    base_task.sigmask = Sigset(
        {SIG(bit)
         for bit in rsyscall.struct.bits(describe_struct.sigmask)})
    # 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(base_task)
    child_monitor = await ChildPidMonitor.make(base_task, epoller)
    connection = make_connection(
        base_task,
        base_task.make_fd_handle(
            near.FileDescriptor(describe_struct.connecting_fd)))
    new_process = Process(
        task=base_task,
        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, 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_process