A file created by memfd_create() can begin as mutable storage and later acquire kernel-enforced restrictions that apply to the underlying file rather than to one descriptor. With MFD_ALLOW_SEALING, a process can add seals through fcntl(F_ADD_SEALS) and make selected mutations unavailable to every holder of that file.

This creates a state transition that ordinary descriptor permissions do not express. A producer can populate bytes, fix the file’s size, and then publish the descriptor with restrictions that remain attached even after the descriptor crosses a process boundary.

Seals belong to the file, not a descriptor

memfd_create() returns a file descriptor for an anonymous file. The file behaves like a regular file for operations such as read(), write(), mmap(), ftruncate(), and descriptor duplication, subject to the flags and permissions relevant to each operation.

When sealing is enabled at creation, F_ADD_SEALS adds restrictions to the file. F_GET_SEALS reports the current set. Duplicating the descriptor or transferring it with SCM_RIGHTS does not create a private seal set for the receiver. All descriptors referring to that file observe the same sealing state.

This scope is central to the contract. Descriptor flags such as FD_CLOEXEC are per descriptor, while memfd seals constrain operations on the shared file object.

Size seals separate layout from byte mutability

F_SEAL_GROW prevents operations that increase file size. F_SEAL_SHRINK prevents operations that reduce it. They can be installed independently, so a file can forbid truncation while still permitting extension, or the reverse.

A producer that needs a fixed byte range can add both after establishing the intended length. Subsequent ftruncate() calls that conflict with those seals fail. Writes that would extend the file are also rejected when growth is sealed.

Neither size seal makes existing bytes immutable. A write wholly inside the current extent can still modify data unless a write-related seal blocks it. Fixed length and fixed content are separate properties.

F_SEAL_WRITE has mapping preconditions

F_SEAL_WRITE blocks writes to the file and prevents new shared writable mappings. Linux also refuses to add this seal while writable shared mappings already exist. The kernel cannot promise a no-write state while an existing MAP_SHARED writable mapping still grants a route to mutate file contents.

That condition makes sealing order observable. A producer that intends to publish immutable bytes must account for writable shared mappings before requesting F_SEAL_WRITE. Closing only the descriptor used to create a mapping does not remove the mapping; the mapping has its own lifetime.

F_SEAL_FUTURE_WRITE expresses a different boundary on Linux versions that support it. It blocks future write-style access while allowing writes through shared writable mappings that already existed when the seal was installed. It is therefore not interchangeable with F_SEAL_WRITE.

F_SEAL_SEAL closes the policy itself

Seals are additive. Once installed, they cannot be removed. F_SEAL_SEAL adds a further transition: after it is present, no additional seals can be added.

That makes the order of operations part of the interface. Installing F_SEAL_SEAL too early can permanently prevent the file from reaching a stricter intended state. A publication sequence can instead establish content restrictions first and add F_SEAL_SEAL only when the seal set itself should become final.

The kernel enforces this monotonic model. Cooperation among descriptor holders is not required for an already installed seal to remain effective.

Publication can transfer data and mutation policy together

A process can create a memfd, size it, populate it, add the required seals, and pass the descriptor to another process. The receiver gets access to the same file object with the seal state already attached.

This is materially different from sending a pathname plus an agreement that the receiver should open the file read-only. A pathname causes another lookup, and a read-only descriptor constrains that descriptor’s access mode. A sealed memfd carries restrictions on the file object itself.

The receiver should still inspect the contract it relies on. F_GET_SEALS can verify the current seal set, and application-level metadata still needs its own validation. Sealing constrains specified file mutations; it does not authenticate the producer or validate the byte format.

Sealing is not a general synchronization primitive

A successful seal changes which later operations the kernel accepts, but it does not turn earlier unsynchronized memory access into a valid concurrency protocol. Threads or processes using mappings still need synchronization appropriate to their language, runtime, and shared-memory design.

Likewise, seals do not snapshot external state. They govern the memfd object and the mutation classes defined by each seal. They do not freeze data structures elsewhere, establish message ordering, or provide application-level version agreement.

The precise design boundary is a monotonic file-state contract. A memfd can move from broadly mutable storage toward a restricted published object, and the kernel can enforce that transition across every descriptor that reaches the same file.