Welcome to pyprctl’s documentation!

pyprctl provides an interface to Linux’s prctl() system call, and to Linux capabilities in general, that is written in pure Python with no external dependencies. (You don’t even need to have libcap installed!)

This library and portions of its API were inspired by python-prctl, but it is not a fork.

Important security note!

On Linux, credentials (real/effective/saved UIDs, real/effective/saved GIDs, supplementary groups, and all of the capability sets) are per-thread attributes. If you change these attributes in one thread, other threads in the same process are unaffected. This can lead to security issues.

To try to mitigate this, glibc and musl make clever use of realtime signals to synchronize changes made to the real/effective/saved UIDs/GIDs across all threads. libcap offers “libpsx”, which enables similar synchronization of capability changes.

pyprctl, however, makes no attempt to synchronize any changes it makes to a thread’s UIDs/GIDs/capabilities. In fact, when changing UIDs/GIDs it deliberately works around glibc/musl’s UID/GID synchronization. If you use pyprctl’s capability manipulation functions in multithreaded programs, you are responsible for synchronizing any changes across threads (if this is necessary to properly secure your application).

(Note: python-prctl doesn’t link against libpsx, so it behaves largely the same way! It just doesn’t document this.)

Platform support

Architecture support

pyprctl should work on at least the following architectures (both 32-bit and 64-bit versions): x86, ARM, RISC-V, SPARC, and PowerPC. However, it has only been tested on x86_64 and 32-bit ARM.

musl libc

pyprctl should work on systems using musl libc. It’s been tested on Alpine Linux and on the musl-based distribution of Void Linux.

Statically linked systems

pyprctl might work on statically linked systems, but this has not been tested.

API documentation

class pyprctl.Cap(value)

Bases: enum.Enum

An enum representing the different Linux capabilities.

See capabilities(7) for more information on each capability.

AUDIT_CONTROL = 30
AUDIT_READ = 37
AUDIT_WRITE = 29
BLOCK_SUSPEND = 36
BPF = 39
CHECKPOINT_RESTORE = 40
CHOWN = 0
DAC_OVERRIDE = 1
FOWNER = 3
FSETID = 4
IPC_LOCK = 14
IPC_OWNER = 15
KILL = 5
LEASE = 28
LINUX_IMMUTABLE = 9
MAC_ADMIN = 33
MAC_OVERRIDE = 32
MKNOD = 27
NET_ADMIN = 12
NET_BIND_SERVICE = 10
NET_BROADCAST = 11
NET_RAW = 13
PERFMON = 38
SETFCAP = 31
SETGID = 6
SETPCAP = 8
SETUID = 7
SYSLOG = 34
SYS_ADMIN = 21
SYS_BOOT = 22
SYS_CHROOT = 18
SYS_MODULE = 16
SYS_NICE = 23
SYS_PACCT = 20
SYS_PTRACE = 19
SYS_RAWIO = 17
SYS_RESOURCE = 24
SYS_TIME = 25
SYS_TTY_CONFIG = 26
WAKE_ALARM = 35
classmethod from_name(name: str) pyprctl.caps.Cap

Look up a capability by name.

Roughly equivalent to cap_from_name() in libcap. Names are matched case-insensitively, but they must include a cap_ prefix (also case-insensitive; CAP_ and Cap_ are valid too).

is_supported() bool

Returns whether the running kernel supports this capability.

classmethod probe_supported() Set[pyprctl.caps.Cap]

Returns the set of capabilities supported by the running kernel.

class pyprctl.CapState(effective: Set[pyprctl.caps.Cap] = <factory>, permitted: Set[pyprctl.caps.Cap] = <factory>, inheritable: Set[pyprctl.caps.Cap] = <factory>)

Bases: object

Represents a thread’s capability state (i.e. the effective, permitted and inheritable capability sets).

copy() pyprctl.caps.CapState
effective: Set[pyprctl.caps.Cap]

The effective capability set (used for permission checks)

classmethod from_text(text: str) pyprctl.caps.CapState

Reconstruct a capability state from a textual representation. For example: =, =p, or cap_chown=ep.

classmethod get_current() pyprctl.caps.CapState

Get the capability state of the current thread.

classmethod get_for_pid(pid: int) pyprctl.caps.CapState

Get the capability state of the process (or thread) with the given PID (or TID).

If pid is 0, this is equivalent to CapState.get_current().

inheritable: Set[pyprctl.caps.Cap]

The inheritable capabilities set. This is preserved across exec(). In addition, when exec()-ing a program that has the corresponding capabilities in its inheritable set, these capabilities will be added to the permitted set. See capabilities(7) for more details.

permitted: Set[pyprctl.caps.Cap]

The permitted capability set. This is the bounding set for the thread’s effective capabilities. It also limits which capabilities a thread that does not have CAP_SETPCAP can add to its inheritable set. See capabilities(7) for more details.

set_current() None

Set the capability state of the current thread to the capability set represented by this object.

Note: If the capability sets stored in this object contain capabilities that the running kernel does not support, the kernel will silently ignore them!

class pyprctl.FileCaps(effective: bool, permitted: Set[pyprctl.caps.Cap], inheritable: Set[pyprctl.caps.Cap], rootid: Optional[int] = None)

Bases: object

Represents the capability sets attached to an executable file.

copy() pyprctl.file_caps.FileCaps
effective: bool

If this is True, it indicates a “capability-dumb” binary. When this program is executed, all capabilities in the new process’s permitted set will also be copied to the process’s effective set.

In addition, if a binary has this bit set, the kernel will refuse to launch it if the new process would not obtain the full set of capabilities specified in the permitted set. See capabilities(7) for more details.

classmethod from_text(text: str) pyprctl.file_caps.FileCaps

Reconstruct a set of file capabilities from a textual representation. For example: =, =p, or cap_chown=ep.

Note that this method will raise an error if the specified “effective” set is not empty and is also different from the “permitted” set. This is because Linux file capabilities only have a single bit for specifying the “effective” permissions, which indicates whether or not the permitted set should be copied to the effective set.

classmethod get_for_file(path: Union[int, str, bytes, os.PathLike[str], os.PathLike[bytes]]) FileCaps

Retrieves the file capabilities attached to the given file. path is as for os.getxattr().

inheritable: Set[pyprctl.caps.Cap]

The inheritable set. This set is ANDed with the inheritable set of the launching program and the resulting capabilities are added to the thread’s permitted set.

permitted: Set[pyprctl.caps.Cap]

The permitted set; automatically added to the thread’s permitted set.

classmethod remove_for_file(path: Union[int, str, bytes, os.PathLike[str], os.PathLike[bytes]]) None

Removes the file capabilities attached to the given file. path is as for os.removexattr().

rootid: Optional[int] = None

For version 3 capability sets, this represents the root user ID of the user namespace in which the file capability extended attribute was created. See capabilities(7) for more details.

set_for_file(path: Union[int, str, bytes, os.PathLike[str], os.PathLike[bytes]]) None

Sets the file capabilities attached to the given file to the state represented by this object. path is as for os.setxattr().

class pyprctl.MCEKillPolicy(value)

Bases: enum.Enum

An enum representing the possible machine check memory corruption kill policies, for use with get_mce_kill() and set_mce_kill().

Essentially, this determines when a thread will be sent a SIGBUS signal if memory corruption is detected. See PR_MCE_KILL in prctl(2) and /proc/sys/vm/memory_failure_early_kill in proc(5) for more information.

DEFAULT = 2

When irrecoverable corruption is detected, follow the system-wide default action as specified by the contents of /proc/sys/vm/memory_failure_early_kill (see proc(5) for more information).

EARLY = 1

“Early kill” -> As soon as irrecoverable corruption is detected, kill this thread if it has the corrupted page mapped.

LATE = 0

“Late kill” -> If irrecoverable corruption is detected, only kill this thread if it tries to access the corrupted page.

class pyprctl.Secbits(value)

Bases: enum.Flag

Represents the different securebits that can be used to change the kernel’s handling of capabilities for UID 0.

KEEP_CAPS = 16

Provides the same functionality as get_keepcaps() and set_keepcaps().

Note that changes made with get_keepcaps()/set_keepcaps() are reflected in the value of this flag as returned by get_securebits(), and vice versa. Since changing the securebits requires CAP_SETPCAP, it may be better to use those functions instead if this is the only securebit that you need to change.

KEEP_CAPS_LOCKED = 32

“Locks” the KEEP_CAPS securebit so it cannot be changed.

Note: If the KEEP_CAPS securebit is set, even if it is “locked” using this flag, the kernel will still clear it on an exec(). So this setting is only really useful to lock the KEEP_CAPS securebit as “off”.

NOROOT = 1

If this bit is set, the kernel will not grant capabilities to set-user-ID-root programs, or to processes with an effective or real user ID of 0 on exec(). See capabilities(7) for more details.

NOROOT_LOCKED = 2

“Locks” the NOROOT securebit so it cannot be changed.

NO_CAP_AMBIENT_RAISE = 64

Disables raising ambient capabilities (such as with cap_ambient_raise()).

NO_CAP_AMBIENT_RAISE_LOCKED = 128

“Locks” the NO_CAP_AMBIENT_RAISE securebit so it cannot be changed.

NO_SETUID_FIXUP = 4

Stops the kernel from adjusting the process’s permitted/effective/ambient capabilities when the process’s effective and filesystem UIDs are switched between 0 and nonzero. See capabilities(7) for more details.

NO_SETUID_FIXUP_LOCKED = 8

“Locks” the NO_SETUID_FIXUP securebit so it cannot be changed.

class pyprctl.TimingMethod(value)

Bases: enum.Enum

An enum representing the possible process timing methods, for use with get_timing() and set_timing().

See PR_SET_TIMING in prctl(2) for more details.

STATISTICAL = 0

The traditional statistical process timing method.

TIMESTAMP = 1

Accurate timestamp-based process timing (currently unimplemented; trying to select it will raise an error).

pyprctl._sys_exit(res: int) None

Call the _exit() system call. This exits the calling thread, but does not terminate other threads in the same process.

pyprctl.cap_ambient_clear_all() None

Clear all ambient capabilities from the current thread.

pyprctl.cap_ambient_is_set(cap: pyprctl.caps.Cap) Optional[bool]

Check whether the given capability is raised in the current thread’s ambient capability set.

This returns True if the capability is raised, False if it is lowered, and None if the kernel does not support this capability (may arise when using newer capabilities on older kernels).

pyprctl.cap_ambient_lower(cap: pyprctl.caps.Cap) None

Lower the given capability in the current thread’s ambient set.

This function will fail with EINVAL if the kernel does not support the specified capability.

pyprctl.cap_ambient_probe() Set[pyprctl.caps.Cap]

“Probe” the current thread’s ambient capability set and return a set of all the capabilities that are raised in this thread’s ambient capability set.

pyprctl.cap_ambient_raise(cap: pyprctl.caps.Cap) None

Raise the given capability in the current thread’s ambient set.

(The capability must already be present in the thread’s permitted set and and the thread’s inheritable set, and the SECBIT_NO_CAP_AMBIENT_RAISE securebit must not be set.)

This function will fail with EINVAL if the kernel does not support the specified capability.

pyprctl.cap_ambient_supported() bool

Check whether the running kernel supports ambient capabilities.

pyprctl.cap_set_ids(*, uid: Optional[int] = None, gid: Optional[int] = None, groups: Optional[Iterable[int]] = None, preserve_effective_caps: bool = False) None

Set UID/GID/supplementary groups while preserving permitted capabilities.

This combines the functionality of libcap’s cap_setuid() and cap_setgroups(), while also providing greater flexibility.

Note: This function only operates on the current thread, not the process as a whole. This is because of the way Linux operates. If you call this function from a multithreaded program, you are responsible for synchronizing changes across threads to ensure proper security.

This function performs the following actions in order. (Note: If gid is not None or groups is not None, CAP_SETGID will first be raised in the thread’s effective set, and if uid is not None then CAP_SETUID will be raised.)

  • If gid is not None, the thread’s real, effective and saved GIDs will be set to gid.

  • If groups is not None, the thread’s supplementary group list will be set to groups.

  • If uid is not None, the thread’s real, effective and saved UIDs will be set to uid.

  • If preserve_effective_caps is True, after this is done, the effective capability set will be restored to its original contents. By default, this function mimics libcap and empties the effective capability set before returning.

Note: If this function fails and raises an OSError, the thread’s UIDs, GIDs, supplementary groups, and capability sets are in an unknown and possibly inconsistent state. This is EXTREMELY DANGEROUS! If you are unable to revert the changes, abort as soon as possible.

pyprctl.capbset_drop(cap: pyprctl.caps.Cap) None

Remove the given capability from the current thread’s bounding capability set.

(This requires the CAP_SETPCAP capability.)

This function will fail with EINVAL if the kernel does not support the specified capability.

pyprctl.capbset_probe() Set[pyprctl.caps.Cap]

“Probe” the current thread’s bounding capability set and return a set of all the capabilities that are present in this thread’s bounding capability set.

pyprctl.capbset_read(cap: pyprctl.caps.Cap) Optional[bool]

Check whether the given capability is present in the current thread’s bounding capability set.

This returns True if the capability is present, False if it is not, and None if the kernel does not support this capability (may arise when using newer capabilities on older kernels).

pyprctl.get_child_subreaper() bool

Get the “child subreaper” attribute of the current process.

See set_child_subreaper().

pyprctl.get_dumpable() bool

Get whether the “dumpable” attribute is set on the current process.

See set_dumpable().

pyprctl.get_keepcaps() bool

Get the “keep capabilities” flag on the current process.

See set_keepcaps().

pyprctl.get_mce_kill() pyprctl.misc.MCEKillPolicy

Get the machine check memory corruption kill policy for the current thread. See set_mce_kill().

pyprctl.get_name() str

Get the name of the current thread as a string.

pyprctl.get_no_new_privs() bool

Get whether the no-new-privileges flag is set on the current thread.

See set_no_new_privs().

pyprctl.get_pdeathsig() Optional[Union[signal.Signals, int]]

Get the parent-death signal of the current process (see set_pdeathsig() for details).

If the parent-death signal is cleared, this function returns None. Otherwise, it returns a signal.Signals object (or an int if the value is not in signal.Signals).

pyprctl.get_securebits() pyprctl.caps.Secbits

Get the current secure bits.

pyprctl.get_timerslack() int

Get the current thread’s timer slack value.

See set_timerslack().

pyprctl.get_timing() pyprctl.misc.TimingMethod

Get the current process timing mode.

pyprctl.getfsgid() int

Get the current thread’s filesystem GID (see setfsgid(2) for details).

This calls the setfsgid() syscall with the argument -1 (which will make it always fail) and returns the result.

pyprctl.getfsuid() int

Get the current thread’s filesystem UID (see setfsuid(2) for details).

This calls the setfsuid() syscall with the argument -1 (which will make it always fail) and returns the result.

pyprctl.scoped_effective_caps(effective: Iterable[pyprctl.caps.Cap]) Iterator[None]

When used as a context manager, this function sets the effective capability set to contain only the specified capabilities, then restores it to its original contents after the body of the context manager has executed.

For example:

with scoped_effective_caps([Cap.CHOWN]):
    ...  # CAP_CHOWN is raised in the effective set; all other capabilites are lowered

Note

Changes made to the effective capability set in the body of the context manager will not be preserved. This function will still revert the effective capability set to its original contents when the body of the context manager finishes executing.

However, changes made to any of the other 4 capability sets (permitted, inheritable, ambient, and bounding) in the body of the context manager will be preserved. (Be careful not to remove any of the capabilities present in the original effective set from the permitted set, or this function may fail to revert to the original effective set.)

pyprctl.set_child_subreaper(flag: bool) None

Set the “child subreaper” attribute on the current process.

When a process dies, its children are reparented to the nearest ancestor with the “child subreaper” attribute set.

pyprctl.set_dumpable(flag: bool) None

Set the “dumpable” attribute on the current process.

This controls whether a core dump will be produced if the process receives a signal whose default behavior is to produce a core dump.

In addition, processes that are not dumpable cannot be attached with ptrace() PTRACE_ATTACH.

pyprctl.set_keepcaps(flag: bool) None

Set the “keep capabilities” flag on the current thread.

This flag, which is always cleared across an exec(), allows a thread to preserve its permitted capability set when switching all of its UIDs to nonzero values. See capabilities(7) for more information.

pyprctl.set_mce_kill(policy: pyprctl.misc.MCEKillPolicy) None

Set the machine check memory corruption kill policy for the current thread.

See MCEKillPolicy for more details.

pyprctl.set_name(name: Union[str, bytes]) None

Set the name of the current thread.

The name is silently truncated to the first 16 bytes. This includes the trailing NUL, so only the first 15 characters of the given name will be used.

pyprctl.set_no_new_privs() None

Set the no-new-privileges flag on the current thread.

Once this flag is set, it cannot be unset. This flag guarantees that in this thread and in all of its children, no exec() call can ever result in elevated privileges. See prctl(2) for more information.

pyprctl.set_pdeathsig(sig: Optional[Union[signal.Signals, int]]) None

Set the parent-death signal of the current process.

If sig is 0 or None, the parent-death signal is cleared. Otherwise, sig specifies the signal that will be sent to the current process when its parent dies.

This flag is is cleared in the following cases:

  1. In children of a fork().

  2. When exec()-ing a binary that is setuid, setgid, or has file capabilities.

  3. When the effective UID, effective GID, filesystem UID, or filesystem GID is changed.

See prctl(2) for more details.

pyprctl.set_seccomp_mode_strict() None

Enable strict seccomp mode.

After this function is is called, the only syscalls that can be made are read(), write(), sigreturn(), and _exit(). Making any other syscall will result in SIGKILL being sent to the thread.

Note: sys.exit() and os._exit() will call the exit_group() syscall, not _exit(). pyprctl exposes a function _sys_exit() that calls the _exit() syscall directly.

pyprctl.set_securebits(secbits: pyprctl.caps.Secbits) None

Set the current secure bits.

(This requires CAP_SETPCAP.)

pyprctl.set_timerslack(val: int) None

Set the current thread’s timer slack value.

The timer slack value is used by the kernel to “group” timer expirations that are close to each other. Essentially, it represents the maximum amount of time (in nanoseconds) by which timer expirations (poll(), select(), nanosleep(), etc.) might be delivered late.

Each thread has a “current” timer slack value and a “default” timer slack value. This function manipulates the “current” value; the “default” value cannot be altered. Passing a value of 0 will reset this thread’s “current” value to its “default” value.

See PR_SET_TIMERSLACK in prctl(2) for more details.

pyprctl.set_timing(timing: pyprctl.misc.TimingMethod) None

Set the process timing mode. Currently, only “statistical” process timing is implemented.

pyprctl.setfsgid(gid: int) None

Set the current thread’s filesystem GID (see setfsgid(2) for details).

This is a helper that calls the setfsgid() syscall up to twice to ensure that the filesystem GID has been changed properly. If it hds not, this helper raises a PermissionError.

pyprctl.setfsuid(uid: int) None

Set the current thread’s filesystem UID (see setfsuid(2) for details).

This is a helper that calls the setfsuid() syscall up to twice to ensure that the filesystem UID has been changed properly. If it has not, this helper raises a PermissionError.

Capability set objects

Note

This interface is designed after python-prctl’s interface.

Note

This interface makes it easier to manipulate the permitted/inheritable/effective sets than using CapState. However, for “bulk” operations (for example, clearing both the permitted and effective sets), this interface, by design, may result in significantly more syscalls than using CapState would, since it has to get and then set the full capability state every time something is changed.

If efficiency is important for your application, make sure to take this into account.

Warning

This interface provides methods of performing “bulk” operations on the ambient/bounding sets, but these operations are not performed atomically!

As a result, if the “bulk” methods operating on the ambient/bounding sets fail, these sets will be left in an inconsistent state.

For example, if the current thread’s permitted and inheritable capability sets have CAP_CHOWN but not CAP_SYS_CHROOT, cap_ambient.add(Cap.CHOWN, Cap.SYS_CHROOT) will successfully add CAP_CHOWN, but it will fail when trying to add CAP_SYS_CHROOT. It will NOT make any attempt to revert these changes, so the thread will still have CAP_CHOWN raised after the function exits.

In addition to CapState and the ambient/bounding set manipulation functions, pyprctl provides five objects that provide alternate ways of interacting with the capability sets:

pyprctl.cap_permitted

The permitted capability set.

pyprctl.cap_inheritable

The inheritable capability set.

pyprctl.cap_effective

The effective capability set.

pyprctl.cap_ambient

The ambient capability set.

pyprctl.capbset

The bounding capability set.

These set objects have boolean properties corresponding to each of the capabilities listed for Cap (except with lowercase names, for example chown, sys_chroot, etc.). Accessing any of these properties will check whether that capability is present in the given set, assigning True to any of these properties will raise them in the given set (if possible), and assigning False to any of these properties will lower them in the given set. For example:

pyprctl.cap_effective.chown = True
print(pyprctl.cap_effective.chown)
pyprctl.cap_effective.chown = False

The sets also have a few helper methods:

set.drop(\*caps)

Drop all the given capabilities from this set.

set.add(\*caps)

Raise all the given capabilities in this set. (This raises a ValueError for the capbset object.)

set.limit(\*caps)

Drop all capabilities except the given ones from this set.

set.replace(\*caps)

“Replace” this capability set with the given capabilities. This is equivalent to the following:

if caps:
    set.add(*caps)
set.limit(*caps)

However, it may be more efficient.

set.clear()

Remove all capabilities from this set. This is equivalent to set.limit() (i.e. with no arguments), but it is easier to understand.

set.has(\*caps)

Return True if the set contains all of the given capabilities, and False if it does not.

Similarly, pyprctl provides a securebits object with read/write properties that provide an alternate method of access to the secure bits. This object has a boolean property corresponding to each of the securebits listed for Secbits (with lowercase names, for example noroot and keep_caps).

Indices and tables