Memfd Seals Turn Shared Memory Into Monotonic File Policy
A process prepares a binary object in memory, passes its file descriptor to another process, and expects the bytes to remain stable after validation. Ordinary shared memory does not provide that property by itself: another holder of writable authority can change the object after a check, resize it, or keep a writable mapping alive. Linux file seals provide a narrower contract. They remove selected mutation operations from a sealable file, and successfully added seals cannot later be removed.
That monotonic property makes sealing useful at trust boundaries built around shared file-backed memory. It also creates a precise limitation: a sealed memfd is not automatically immutable. The result depends on which seals were installed, when they were installed, and which mappings already existed.
Sealing is an inode property, not a descriptor flag
memfd_create() creates an anonymous file and returns a descriptor opened for reading and writing. The object has regular-file semantics without requiring a persistent pathname. Sealing is available when the object is created with MFD_ALLOW_SEALING; without that flag, the initial seal set includes F_SEAL_SEAL, which prevents additional seals from being installed.
Seals are attached to the file inode. Descriptors referring to the same object therefore observe the same seal set. Passing a memfd over a Unix-domain socket does not create an unsealed copy, and duplicating a descriptor does not create an independent sealing state.
F_ADD_SEALS adds restrictions through fcntl(). A successful addition takes effect immediately and cannot be reversed. F_GET_SEALS exposes the current bit mask so a receiver can verify the properties on the object it actually received rather than trusting metadata supplied beside it.
This differs from access mode on a descriptor. A read-only descriptor can limit operations available through that descriptor while another writable descriptor still exists elsewhere. A file seal changes permitted operations on the underlying object for all holders.
Write sealing and size sealing cover separate mutation paths
F_SEAL_WRITE blocks writes to file contents and prevents creation of new shared writable mappings. It does not, by itself, prohibit every size change. F_SEAL_SHRINK prevents reducing the file size, while F_SEAL_GROW prevents increasing it. A producer that needs a fixed-size, stable byte sequence generally has to treat content mutation and size mutation as separate policy dimensions.
The separation matters for parsers that validate offsets or lengths. Stable bytes are not enough if another process can truncate the backing file after bounds checks. A mapping that later reaches a removed portion of the object can produce a fault rather than preserving the assumptions made during validation.
Conversely, size seals alone do not freeze existing bytes. A fixed-length object can still be modified in place unless write mutation is also restricted.
F_SEAL_SEAL closes the policy itself. Once present, no additional seals can be added. It is useful only after the intended seal set is complete. Installing it too early can permanently prevent the process from adding a missing restriction.
Existing writable mappings define a critical timing boundary
The strongest write seal cannot simply be added while a shared writable mapping of the file still exists. Adding F_SEAL_WRITE fails with EBUSY in that condition. A producer seeking full write sealing must remove conflicting writable shared mappings before requesting the seal.
Linux also provides F_SEAL_FUTURE_WRITE. It blocks later writes through write() and blocks creation of new writable shared mappings, while shared writable mappings created before the seal remain able to modify the file.
That distinction is deliberate and security-relevant. F_SEAL_FUTURE_WRITE can let a producer retain an existing writable mapping while distributing descriptors that cannot acquire new write paths through normal writes or new writable mappings. It does not make the bytes stable against the producer that retains the earlier mapping.
A consumer that requires immutable input cannot treat F_SEAL_FUTURE_WRITE as equivalent to F_SEAL_WRITE. The former expresses a boundary around future write authority; the latter requires existing shared writable mappings to be absent before it can take effect.
Descriptor transfer preserves the kernel-enforced restrictions
A common architecture creates and populates a memfd in one process, seals it, then transfers the descriptor to another process with SCM_RIGHTS. The receiver gains a reference to the same file object. The seal state follows that object because it is not application metadata attached to the IPC message.
This can remove a classic check-versus-use race from a shared-memory protocol. A consumer can inspect F_GET_SEALS, validate the content, and then operate on the same sealed object without copying merely to protect against later file mutation. That statement is conditional on the required seals actually excluding every mutation relevant to the consumer’s assumptions.
The receiver still needs an acceptance policy. It should verify the expected seal bits on the received descriptor and validate other object properties required by the protocol. A peer-provided claim that an object is sealed is not a substitute for asking the kernel about the received object.
Sealing also does not authenticate the sender or decide which object the sender was entitled to provide. Unix-socket peer authorization, descriptor type checks, protocol framing, and application-level resource policy remain separate controls.
Seals constrain file mutation, not every form of authority
A memfd can carry sensitive data, executable material, serialized state, or other content, but seals do not turn it into a general isolation primitive. They do not revoke existing read access, prevent an authorized holder from copying bytes elsewhere, or erase descriptors already distributed.
Execution policy is also distinct. Linux provides F_SEAL_EXEC on kernels that support it. Its semantics concern executable state and imply additional seals, but applications must tie such use to the running kernel interface rather than assume availability across all deployments.
Likewise, close-on-exec is a descriptor inheritance property rather than a seal. MFD_CLOEXEC can keep the original descriptor from surviving an execve(), but that setting does not freeze file content. Sealing and descriptor-lifetime policy address different boundaries.
The anonymous nature of a memfd is not a confidentiality guarantee either. Its lack of a normal persistent pathname removes one namespace route to the object, but access can still flow through inherited descriptors, explicit descriptor transfer, and other mechanisms permitted by the system.
The useful guarantee is a verified reduction of mutation authority
Memfd sealing is strongest when the protocol names the exact properties it needs. A fixed-size immutable payload requires a different seal set from a buffer whose producer intentionally keeps an existing writable mapping. A receiver can verify those properties directly on the file object before trusting data derived from it.
The resulting boundary is compact: seals monotonically remove specified file operations, the kernel enforces them across references to the same object, and the seal set is inspectable. The boundary stops there. Sender identity, descriptor provenance, confidentiality, inheritance, and any authority retained through pre-existing mappings remain properties of the surrounding architecture.