A memfd can begin as a writable anonymous file and later reject whole classes of mutation through kernel-enforced seals. The transition is attached to the inode, not to one descriptor, so a process cannot preserve an unrestricted duplicate descriptor and use it to bypass a seal added through another reference.
This makes sealing materially different from handing another component a descriptor opened with narrower access. Descriptor access mode constrains one open file description. A seal changes which operations the kernel permits against the file itself, including operations attempted through other descriptors that refer to the same inode.
The file exists without a normal pathname
memfd_create() returns a file descriptor for an anonymous file. The file has ordinary file-like properties that matter for IPC: it can be sized, read, written, mapped, duplicated, inherited, and passed over a Unix domain socket.
The supplied memfd name is diagnostic rather than a namespace entry used for normal pathname lookup. The file’s lifetime follows its references. When no descriptor, mapping, or other kernel reference keeps it alive, its storage can be reclaimed.
This combination is useful for data that needs file semantics without publication in a filesystem directory. A producer can construct a payload, map it for efficient access, then transfer a descriptor rather than copying the payload into a pathname-addressed temporary file.
Sealing is optional. A caller that wants to add ordinary seals creates the memfd with MFD_ALLOW_SEALING. Without that permission, the initial seal state prevents later addition of seals.
Seals belong to the inode
Linux exposes file seals through fcntl() operations. F_ADD_SEALS adds restrictions; F_GET_SEALS reports the current set.
The important identity boundary is the inode. If two descriptors refer to the same memfd inode, they observe the same seals. Passing the descriptor to another process does not create an unsealed copy of the file. Duplicating the descriptor does not create one either.
This property turns sealing into a shared-object contract. A producer can populate a memfd and then restrict future mutation before transferring it. A receiver can inspect the seal set and reason about operations the kernel will reject for that file.
The contract is monotonic. Seals can be added but not removed. F_SEAL_SEAL closes the seal set itself: after it is present, attempts to add further seals fail. Code that uses seals as a protocol therefore has to distinguish a partially restricted file from a file whose restriction set has also been finalized.
Size constraints and byte constraints are separate
F_SEAL_SHRINK prevents operations that reduce the file size. F_SEAL_GROW prevents operations that increase it. Neither seal, by itself, makes existing bytes immutable.
That separation matters for shared buffers with a fixed extent. A producer can freeze the size while still permitting writes within the existing range, or it can combine size seals with write restrictions when the payload must become immutable.
A fixed size is not equivalent to fixed contents. Without a write seal, an operation that overwrites bytes inside the existing extent can remain valid even though growth and shrinkage are blocked.
The reverse distinction also matters. A restriction on writes needs defined behavior for size-changing operations, because truncation and allocation interfaces can alter observable contents without looking like an ordinary byte write. The seal set exists as several orthogonal constraints rather than one generic read-only bit.
F_SEAL_WRITE has a mapping precondition
F_SEAL_WRITE blocks writes and prevents creation of new shared writable mappings. It also prevents size-changing operations covered by its semantics.
The seal cannot simply revoke an existing shared writable mapping. If such a mapping exists when a process tries to add F_SEAL_WRITE, the operation fails with EBUSY. The writable shared mapping must be removed before that seal can be established.
This is a critical boundary in protocols that publish a supposedly immutable buffer. The producer cannot keep a shared writable mapping, add F_SEAL_WRITE, and claim that the mapping has silently become read-only. Linux rejects that transition rather than changing the established mapping behind the process.
A compact publication sequence therefore has a visible state transition:
int fd = memfd_create("payload", MFD_ALLOW_SEALING | MFD_CLOEXEC);
/* size and populate fd */
munmap(shared_writer, length);
fcntl(fd, F_ADD_SEALS,
F_SEAL_WRITE | F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL);Production code must check every return value and handle partial setup. The example only shows the semantic boundary: existing shared writable mappings and a full write seal are incompatible at seal-installation time.
Future-write sealing preserves an existing writer
Linux also provides F_SEAL_FUTURE_WRITE. It rejects future write() operations and future attempts to create writable mappings, while existing shared writable mappings can continue modifying the file.
That gives it a different protocol shape from F_SEAL_WRITE. A process can retain a mapping that was writable before the seal and continue updating through that mapping, while later consumers are prevented from obtaining new writable mappings or writing through ordinary write operations.
The result is not general immutability. Existing writable shared mappings remain a mutation channel. Treating F_SEAL_FUTURE_WRITE as equivalent to F_SEAL_WRITE would therefore overstate the guarantee.
This distinction supports asymmetric ownership models: an established producer mapping can remain active while newly transferred references have a narrower mutation surface. The kernel enforces the boundary based on when the writable mapping was established.
Descriptor transfer does not transfer trust automatically
A sealed memfd can be passed between processes using Unix domain socket descriptor passing. The receiver obtains a descriptor for the same underlying file, including its seal state.
That makes seal inspection useful at an IPC boundary, but it does not authenticate the sender, validate payload structure, or prove that the receiver got the intended object. Those properties belong to the surrounding protocol.
The receiver also needs to check the actual seal set rather than infer it from convention. A protocol that requires immutable, fixed-size contents can reject a memfd unless the required seals are present. If finality matters, the protocol can also require F_SEAL_SEAL so no later restriction transition remains possible.
Seals express mutation constraints. They do not express schema validity, provenance, confidentiality, or authorization.
Memory mappings preserve file semantics
A memfd can be mapped because it is a file object, and sealing interacts directly with mapping permissions. This is one reason seals are stronger than a user-space flag carried beside a buffer.
If the kernel permits a shared writable mapping, modifications through that mapping affect the file contents. A seal that blocks new writable mappings closes that path for mappings created after the relevant restriction takes effect.
Private mappings have different semantics. MAP_PRIVATE modifications are copy-on-write changes to the mapping and do not write those private changes back into the file. A consumer can therefore create private writable memory from immutable file contents without gaining a channel to mutate the shared memfd object.
The distinction is between mutability of a process’s virtual memory and mutability of the shared file. Seals constrain the latter.
Sealing narrows races rather than replacing protocol state
A user-space protocol could send a message saying that a buffer is final, then rely on every participant to stop writing. That convention has a race surface: a stale writer can still mutate the object after another process accepts the final-state message.
A completed write seal moves that specific invariant into the kernel. Once the seal is successfully installed, later prohibited operations fail even if a component retains a descriptor it received earlier.
The guarantee still begins at a precise point. Before seal installation succeeds, the file remains subject to the mutation paths that are still permitted. A consumer that receives the descriptor before the producer finishes sealing can observe a different state from one that receives it afterward.
Ordering therefore remains part of the IPC design. Sealing provides a durable mutation boundary for the file object; the protocol decides when that boundary must exist relative to descriptor transfer and payload consumption.
Linux memfd seals are most useful when a shared file must move from construction into a restricted phase without depending solely on cooperative behavior. Their scope is exact: they constrain specified file mutations at the inode, propagate across references to that inode, and become irreversible once added. They do not turn arbitrary shared memory into immutable data, and they do not replace the identity, validation, or ordering rules around the file.