Module rsyscall.sys.mman

#include <sys/mman.h>

Expand source code Browse git
"`#include <sys/mman.h>`"
from __future__ import annotations
from rsyscall._raw import lib # type: ignore
from rsyscall.linux.memfd import MFD
import enum

__all__ = [
    "MFD",
    "PROT",
    "MAP",
    "MappableFileDescriptor",
    "MemoryMappingTask",
    "MemoryMapping",
]

class PROT(enum.IntFlag):
    EXEC = lib.PROT_EXEC
    READ = lib.PROT_READ
    WRITE = lib.PROT_WRITE
    NONE = lib.PROT_NONE

class MAP(enum.IntFlag):
    PRIVATE = lib.MAP_PRIVATE
    SHARED = lib.MAP_SHARED
    ANONYMOUS = lib.MAP_ANONYMOUS
    POPULATE = lib.MAP_POPULATE

#### Classes ####
from dataclasses import dataclass
import rsyscall.far
import rsyscall.near
import rsyscall.near as near
import typing as t

@dataclass(frozen=True)
class MemoryMapping:
    __slots__ = ('task', 'near', 'file')
    task: MemoryMappingTask
    near: rsyscall.near.MemoryMapping
    file: rsyscall.far.File

    async def munmap(self) -> None:
        await _munmap(self.task.sysif, self.near)

    def for_task(self, task: MemoryMappingTask) -> MemoryMapping:
        if task.address_space != self.task.address_space:
            raise rsyscall.far.AddressSpaceMismatchError()
        return MemoryMapping(task, self.near, self.file)

    def __str__(self) -> str:
        return f"MemoryMapping({str(self.task)}, {str(self.near)})"

    def __repr__(self) -> str:
        return str(self)

from rsyscall.handle.fd import BaseFileDescriptor, FileDescriptorTask

class MemoryMappingTask(FileDescriptorTask):
    async def mmap(self, length: int, prot: PROT, flags: MAP,
                   page_size: int=4096,
    ) -> MemoryMapping:
        # a mapping without a file descriptor, is an anonymous mapping
        flags |= MAP.ANONYMOUS
        ret = await _mmap(self.sysif, length, prot, flags, page_size=page_size)
        return MemoryMapping(self, ret, rsyscall.far.File())

class MappableFileDescriptor(BaseFileDescriptor):
    task: MemoryMappingTask

    async def mmap(self, length: int, prot: PROT, flags: MAP,
                   offset: int=0,
                   page_size: int=4096,
                   file: rsyscall.far.File=None,
    ) -> MemoryMapping:
        self._validate()
        if file is None:
            file = rsyscall.far.File()
        ret = await _mmap(self.task.sysif, length, prot, flags, fd=self.near, offset=offset, page_size=page_size)
        return MemoryMapping(self.task, ret, file)

#### Raw syscalls ####
from rsyscall.near.sysif import SyscallInterface
from rsyscall.sys.syscall import SYS

async def _mmap(sysif: SyscallInterface, length: int, prot: PROT, flags: MAP,
               addr: t.Optional[near.Address]=None,
               fd: t.Optional[near.FileDescriptor]=None, offset: int=0,
               page_size: int=4096) -> near.MemoryMapping:
    if addr is None:
        addr = 0 # type: ignore
    else:
        assert (int(addr) % page_size) == 0
    if fd is None:
        fd = -1 # type: ignore
    # TODO we want Linux to enforce this for us, but instead it just rounds,
    # leaving us unable to later munmap.
    assert (int(length) % page_size) == 0
    ret = await sysif.syscall(SYS.mmap, addr, length, prot, flags, fd, offset)
    return near.MemoryMapping(address=ret, length=length, page_size=page_size)

async def _munmap(sysif: SyscallInterface, mapping: near.MemoryMapping) -> None:
    await sysif.syscall(SYS.munmap, mapping.address, mapping.length)

Classes

class MFD (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code Browse git
class MFD(enum.IntFlag):
    NONE = 0
    CLOEXEC = lib.MFD_CLOEXEC
    ALLOW_SEALING = lib.MFD_ALLOW_SEALING
    HUGETLB = lib.MFD_HUGETLB
    HUGE_2MB = lib.MFD_HUGE_2MB
    HUGE_1GB = lib.MFD_HUGE_1GB

Ancestors

  • enum.IntFlag
  • builtins.int
  • enum.Flag
  • enum.Enum

Class variables

var NONE
var CLOEXEC
var ALLOW_SEALING
var HUGETLB
var HUGE_2MB
var HUGE_1GB
class PROT (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code Browse git
class PROT(enum.IntFlag):
    EXEC = lib.PROT_EXEC
    READ = lib.PROT_READ
    WRITE = lib.PROT_WRITE
    NONE = lib.PROT_NONE

Ancestors

  • enum.IntFlag
  • builtins.int
  • enum.Flag
  • enum.Enum

Class variables

var EXEC
var READ
var WRITE
var NONE
class MAP (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code Browse git
class MAP(enum.IntFlag):
    PRIVATE = lib.MAP_PRIVATE
    SHARED = lib.MAP_SHARED
    ANONYMOUS = lib.MAP_ANONYMOUS
    POPULATE = lib.MAP_POPULATE

Ancestors

  • enum.IntFlag
  • builtins.int
  • enum.Flag
  • enum.Enum

Class variables

var PRIVATE
var SHARED
var ANONYMOUS
var POPULATE
class MappableFileDescriptor (task: FileDescriptorTask, near: FileDescriptor, valid: bool)

A file descriptor accessed through some Task

This is an rsyscall-internal base class, which other FileDescriptor objects inherit from for core lifecycle methods. See FileDescriptor for more information.

Expand source code Browse git
class MappableFileDescriptor(BaseFileDescriptor):
    task: MemoryMappingTask

    async def mmap(self, length: int, prot: PROT, flags: MAP,
                   offset: int=0,
                   page_size: int=4096,
                   file: rsyscall.far.File=None,
    ) -> MemoryMapping:
        self._validate()
        if file is None:
            file = rsyscall.far.File()
        ret = await _mmap(self.task.sysif, length, prot, flags, fd=self.near, offset=offset, page_size=page_size)
        return MemoryMapping(self.task, ret, file)

Ancestors

Subclasses

Methods

async def mmap(self, length: int, prot: PROT, flags: MAP, offset: int = 0, page_size: int = 4096, file: File = None) ‑> MemoryMapping
Expand source code Browse git
async def mmap(self, length: int, prot: PROT, flags: MAP,
               offset: int=0,
               page_size: int=4096,
               file: rsyscall.far.File=None,
) -> MemoryMapping:
    self._validate()
    if file is None:
        file = rsyscall.far.File()
    ret = await _mmap(self.task.sysif, length, prot, flags, fd=self.near, offset=offset, page_size=page_size)
    return MemoryMapping(self.task, ret, file)

Inherited members

class MemoryMappingTask (sysif: SyscallInterface, near_process: Process, fd_table: FDTable, address_space: AddressSpace, pidns: PidNamespace)

A wrapper around SyscallInterface which tracks the namespaces of the underlying process

Note that this is a base class for the more fully featured Task.

We store namespace objects to represent the namespaces that we believe that underlying processes is in. Since we have complete control over the process, we can make sure this belief is accurate, by updating our stored namespaces when the process changes namespace. That isn't done here; it's done in handle.Task.

Currently, we store only one PidNamespace. But each process actually has two pid namespaces:

  • the process's own pid namespace, which determines the pids returned from getpid, clone, and other syscalls.
  • the pid namespace that new children will be in.

The two pid namespaces only differ if we call unshare(CLONE.NEWPID). Currently we don't do that because unshare(CLONE.NEWPID) makes monitoring children more complex, since they can be deleted without leaving a zombie at any time if the pid namespace shuts down. But if we did call unshare(CLONE.NEWPID), we'd need to handle this right.

In the analogy to near and far pointers, this is like a segment register, if a segment register was write-only. Then we'd need to maintain the knowledge of what the segment register was set to, outside the segment register itself. That's what we do here.

There actually were systems where segment registers were, if not quite write-only, at least expensive to set and expensive to read. For example, x86_64 - the FS and GS segment registers can only be set via syscall. If you wanted to use segmentation on such systems, you'd probably have a structure much like this one.

Expand source code Browse git
class MemoryMappingTask(FileDescriptorTask):
    async def mmap(self, length: int, prot: PROT, flags: MAP,
                   page_size: int=4096,
    ) -> MemoryMapping:
        # a mapping without a file descriptor, is an anonymous mapping
        flags |= MAP.ANONYMOUS
        ret = await _mmap(self.sysif, length, prot, flags, page_size=page_size)
        return MemoryMapping(self, ret, rsyscall.far.File())

Ancestors

Subclasses

Class variables

var sysifSyscallInterface
var near_processProcess
var fd_tableFDTable
var address_spaceAddressSpace
var pidnsPidNamespace

Methods

async def mmap(self, length: int, prot: PROT, flags: MAP, page_size: int = 4096) ‑> MemoryMapping
Expand source code Browse git
async def mmap(self, length: int, prot: PROT, flags: MAP,
               page_size: int=4096,
) -> MemoryMapping:
    # a mapping without a file descriptor, is an anonymous mapping
    flags |= MAP.ANONYMOUS
    ret = await _mmap(self.sysif, length, prot, flags, page_size=page_size)
    return MemoryMapping(self, ret, rsyscall.far.File())

Inherited members

class MemoryMapping (task: MemoryMappingTask, near: MemoryMapping, file: File)

MemoryMapping(task: 'MemoryMappingTask', near: 'rsyscall.near.MemoryMapping', file: 'rsyscall.far.File')

Expand source code Browse git
@dataclass(frozen=True)
class MemoryMapping:
    __slots__ = ('task', 'near', 'file')
    task: MemoryMappingTask
    near: rsyscall.near.MemoryMapping
    file: rsyscall.far.File

    async def munmap(self) -> None:
        await _munmap(self.task.sysif, self.near)

    def for_task(self, task: MemoryMappingTask) -> MemoryMapping:
        if task.address_space != self.task.address_space:
            raise rsyscall.far.AddressSpaceMismatchError()
        return MemoryMapping(task, self.near, self.file)

    def __str__(self) -> str:
        return f"MemoryMapping({str(self.task)}, {str(self.near)})"

    def __repr__(self) -> str:
        return str(self)

Instance variables

var fileFile

Return an attribute of instance, which is of type owner.

var nearMemoryMapping

Return an attribute of instance, which is of type owner.

var taskMemoryMappingTask

Return an attribute of instance, which is of type owner.

Methods

async def munmap(self) ‑> NoneType
Expand source code Browse git
async def munmap(self) -> None:
    await _munmap(self.task.sysif, self.near)
def for_task(self, task: MemoryMappingTask) ‑> MemoryMapping
Expand source code Browse git
def for_task(self, task: MemoryMappingTask) -> MemoryMapping:
    if task.address_space != self.task.address_space:
        raise rsyscall.far.AddressSpaceMismatchError()
    return MemoryMapping(task, self.near, self.file)