2026-07-16
Added in Linux 6.10 (mid-2024), mseal() closes a decades-old attack window: even after you mprotect() a page to read-only, an attacker with a write primitive can just call mprotect() again to flip it back to PROT_WRITE, patch the code, and continue. Sealing removes that escape hatch — the seal itself cannot be undone for the lifetime of the process.
The signature is deliberately minimal:
int mseal(void *addr, size_t len, unsigned long flags);mprotect(), munmap(), mremap(), mmap() over the same range, and any madvise() that would drop pages (like MADV_DONTNEED on file-backed writable mappings).Concrete real-world example. glibc 2.40+ automatically seals the loader (ld.so), the vDSO, and PT_LOAD segments of the main executable and every shared library after relocations complete. Before mseal, exploit chains like the "RELRO bypass via mprotect gadget" would find an mprotect call in libc's PLT, pivot to it with a controlled second argument, and re-enable write on .got.plt. After sealing, that gadget returns -EPERM and the chain dies at the syscall boundary. Chrome's V8 also seals its JIT code pages once emitted, so a corrupted function pointer can no longer be turned into arbitrary code execution by remapping the page as writable.
Rule of thumb for sizing. The kernel tracks seals per-VMA, and sealing a range that partially overlaps a VMA splits it. If you seal 4KB in the middle of a 2MB mapping, you now have three VMAs where you had one, and every future page fault in that region walks a longer rbtree. Align sealed ranges to whole VMA boundaries: seal the entire .text segment, not individual functions. A useful heuristic — if you would need more than ~8 seal calls to protect a region, restructure your allocation so a single seal covers a contiguous, aligned block instead.
Gotchas. Sealing is one-way; there is no munseal. You cannot seal stack pages of a running thread (the kernel needs to grow/shrink them). fork() preserves seals in the child, but execve() resets everything since it builds a fresh address space. Check /proc/self/maps — sealed VMAs get an sl flag in the newer kernel format.
mseal() turns a memory region's permissions and layout into a one-way commitment, closing the "mprotect-back-to-writable" step that most modern code-injection exploits depend on.
