Shared memory is efficient partly because two processes can observe the same storage without copying it. That property becomes a security problem when one side validates bytes and later consumes them while another side still holds authority to mutate the same object. Linux memfd sealing can narrow that race by making selected mutations fail in the kernel before the file descriptor crosses a trust boundary.

memfd_create() creates an anonymous file and returns an ordinary file descriptor. The object can be sized, written, mapped, and transferred over a UNIX domain socket. With MFD_ALLOW_SEALING, the inode starts with an empty seal set, allowing the producer to add irreversible restrictions after population.

Seals belong to the inode rather than one descriptor

F_ADD_SEALS adds restrictions to the inode referenced by a writable file descriptor. The seals are shared by every descriptor that refers to that inode, including descriptors duplicated locally or transferred to another process. Once a seal is added, it cannot be removed.

This property changes the trust model of descriptor transfer. A consumer does not need to rely on a promise that the producer will refrain from a prohibited mutation after handoff. The relevant mutation is rejected against the shared inode state.

F_GET_SEALS exposes the current seal bit mask. A receiving process can therefore require a particular seal set before treating the payload as stable. That check is useful only when the required seals actually cover the mutations that matter to the parser or protocol.

Size seals remove truncation and extension as mutation channels

F_SEAL_SHRINK prevents the file from being reduced. This matters for mapped shared data because truncation can remove pages underneath an existing mapping and later access can generate SIGBUS.

F_SEAL_GROW blocks operations that increase file size. Together, the two seals fix the object’s length, but they do not make bytes inside that length immutable. A producer that stops at size sealing can still leave a consumer exposed to content changes through permitted write paths.

The distinction is important for structured payloads. A fixed length can stabilize offsets and bounds while fields at those offsets remain mutable. Size integrity and content integrity are separate properties.

F_SEAL_WRITE requires writable shared mappings to be gone

F_SEAL_WRITE prevents modification of file contents through write-style operations and prevents new writable shared mappings. The kernel will not add this seal while an existing writable shared mapping of the file remains active. A producer that populated the object through such a mapping must first remove that mapping or otherwise eliminate the conflicting writable mapping state.

That requirement closes a direct bypass. If the kernel allowed a full write seal while an already writable shared mapping survived, the holder of that mapping could continue changing pages despite the apparent immutable state.

A typical handoff therefore has a state transition: create the memfd, set its size, populate it, remove writable shared mappings, add the required seals, inspect the resulting seal set, then transfer the descriptor. The security property comes from completing that transition before the less-trusted side relies on the bytes.

F_SEAL_FUTURE_WRITE has a deliberately weaker boundary

F_SEAL_FUTURE_WRITE blocks future write operations and future writable shared mappings, but existing writable shared mappings remain able to modify the file. This permits a producer to retain an established mapping while preventing newly acquired write authority through the file descriptor.

That behavior is useful for protocols that intentionally preserve an existing writer, but it is not equivalent to immutable content. A consumer that requires bytes to remain constant after validation cannot infer that property from F_SEAL_FUTURE_WRITE alone while a pre-existing writable mapping may survive.

The difference between the two write seals is therefore a trust-boundary decision, not merely an API detail. F_SEAL_WRITE targets a state with no surviving writable shared mapping; F_SEAL_FUTURE_WRITE can coexist with one.

F_SEAL_SEAL freezes the policy rather than the payload

F_SEAL_SEAL prevents additional seals from being added. It does not itself prohibit content writes, growth, or shrinkage. Applying it too early can permanently prevent the inode from receiving restrictions that the handoff protocol later expects.

A producer that needs a final immutable policy normally adds the mutation seals first and includes F_SEAL_SEAL only when no further restrictions should ever be added. Since seals are monotonic, the final set becomes an inode-level policy that neither side can relax.

There is also an important creation default. If memfd_create() is called without MFD_ALLOW_SEALING, the new file starts with F_SEAL_SEAL. In that state, ordinary sealing cannot subsequently add the write or size restrictions. Sealing capability must therefore be part of the creation decision, not an afterthought at handoff time.

Descriptor transfer does not transfer unrestricted mutation authority

A memfd can be sent to another process with SCM_RIGHTS over a UNIX domain socket. The receiving descriptor refers to the same underlying file, so its access is subject to the seals already attached to the inode.

This makes sealed memfds suitable for IPC designs in which one process constructs a payload and another parses or maps it without accepting continued mutation authority from the producer. The receiver can validate the expected seals and then operate directly on the shared object instead of copying solely to obtain a stable snapshot.

Seals do not authenticate the producer, establish authorization for the socket, or prove that the initial bytes are benign. Peer identity, message framing, payload validation, and descriptor provenance remain separate controls. Sealing only constrains later mutations represented by the installed seal set.

Execution permission is a separate memfd boundary

Content sealing and executable status are distinct concerns. Current Linux provides MFD_NOEXEC_SEAL for creating a non-executable memfd with its executable bit sealed against later addition. It also provides MFD_EXEC for an executable memfd, subject to kernel policy.

The PID-namespace-scoped vm.memfd_noexec setting can alter or enforce defaults for calls that omit explicit execution flags. Its policy is hierarchical across PID namespaces, with the most restrictive applicable setting taking effect during creation.

A design that uses memfd only as a data transport can therefore constrain execution separately from content mutation. Write seals do not by themselves express a non-execution policy, and a non-executable memfd does not by itself make the payload immutable.

Immutability is strongest when the handoff state is explicit

The useful security boundary is not the existence of a memfd descriptor; it is the state of the inode at the moment another component begins trusting its contents. Fixed size, blocked writes, frozen seal policy, and non-executable status each cover different mutation or use paths.

For an immutable data handoff, the receiver can treat the required seal mask as protocol metadata and reject objects that do not meet it. That keeps the invariant tied to kernel-visible state rather than process convention. It also keeps the limitation precise: seals can remove selected mutation authority from a shared file, but they do not replace authentication, payload validation, IPC authorization, or confinement of the processes that exchange it.