mseal(2)
Permanently seal a virtual memory area against mprotect, munmap, and MAP_FIXED remapping.
- Since
- Linux 6.10check your kernel →
- glibc
- glibc 2.41 (syscall(SYS_mseal) required on older glibc)
- System
- Linux
- Man page
- mseal(2)
Synopsis
#include <sys/syscall.h>
#include <unistd.h>
/* no glibc wrapper before glibc 2.41 */
int ret = syscall(SYS_mseal, void *addr, size_t len, unsigned long flags);Rod's take
mseal(2) makes mprotect hardening permanent. Once you seal a JIT code region, executable library segment, or any VMA you are done modifying, no gadget can mprotect it back to writable. Chrome V8 seals its code pages after trampoline installation; glibc 2.41 seals internal linker control structures. One syscall, irreversible, no unsealing path.
Flags and arguments that matter
| Name | What it does |
|---|---|
| flags (reserved) | Must be 0. Non-zero returns EINVAL. Reserved for future use. |
Errors you will actually hit
| errno | Meaning |
|---|---|
| EPERM | The address range, or part of it, is already sealed. |
| EINVAL | addr is not page-aligned, flags is non-zero, or len rounds up to overflow. |
| ENOMEM | The address range is not fully mapped, or spans more VMAs than the kernel will process. |
| ENOSYS | Kernel older than 6.10. |
Example
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
void *code = mmap(NULL, sz, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memcpy(code, jit_buf, jit_len);
mprotect(code, sz, PROT_READ | PROT_EXEC);
/* nothing touches this VMA again */
syscall(SYS_mseal, code, sz, 0UL);Trimmed from the full walkthrough in mseal(2): permanently lock down your VMA after you are done with it.
BSD equivalent
OpenBSD: mimmutable(2) since 7.3 (April 2023) provides equivalent sealing against mprotect and munmap. FreeBSD: no equivalent in base as of 14.2.
The long version, from the console
Sources
Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.
