A process prepares a binary payload in memory, passes a file descriptor to another process, and expects the bytes to remain stable after validation. A plain descriptor does not create that guarantee. If some holder still has write authority, the object can change after a consumer has inspected it, and pathname permissions offer no useful boundary when the object has no ordinary filesystem name.

Linux memfd_create() provides an anonymous file backed by memory-like filesystem storage, and file seals can constrain later changes to that file. The useful security property is not anonymity by itself. It is the ability to construct a mutable object, apply irreversible restrictions to that object, then hand out descriptors whose backing file can no longer be changed in the prohibited ways.

Sealing must be enabled when the memfd is created

A caller that intends to add seals creates the object with MFD_ALLOW_SEALING. Without that flag, the new file starts with F_SEAL_SEAL, which prevents adding further seals. This default makes sealing capability an explicit creation-time choice.

Seals are manipulated with fcntl(). F_ADD_SEALS adds restrictions, while F_GET_SEALS reports the current seal mask. Added seals are not removable. The kernel therefore treats the transition as monotonic: a process can narrow mutation rights on the file but cannot later restore a prohibited operation by deleting a seal.

F_SEAL_SEAL closes the policy itself. Once present, no additional seals can be added. A producer that wants a final immutable policy normally adds the mutation seals it requires before adding F_SEAL_SEAL; applying the policy seal too early freezes an incomplete mask.

Size and content are separate mutation surfaces

F_SEAL_GROW prevents increasing the file size. F_SEAL_SHRINK prevents reducing it. These restrictions matter independently because changing length can alter the consumer-visible object even without replacing bytes in the retained range.

F_SEAL_WRITE targets content mutation more strongly. Once successfully applied, write operations that modify the file are rejected, and new shared writable mappings cannot be created. The kernel will not add this seal while writable shared mappings of the file already exist. That condition prevents a producer from claiming the strong write seal while another mapping still carries direct mutation authority.

The distinction between file size and file content matters for protocol design. A producer that requires a fixed byte sequence generally needs to constrain both writes and size changes. A seal mask that blocks writes but permits truncation does not express the same invariant.

Existing mappings define the hard edge of F_SEAL_WRITE

Memory mappings make the sealing boundary more precise than a simple “make read-only” operation. A writable MAP_SHARED mapping can modify the underlying file without using write(). For that reason, F_SEAL_WRITE cannot be installed until such mappings are gone.

Linux also provides F_SEAL_FUTURE_WRITE. It blocks later file writes and later creation of shared writable mappings, but shared writable mappings that already exist may continue modifying the file. This seal supports a different transition: stop distributing new write authority without revoking authority already represented by existing mappings.

That difference must remain visible in a security claim. F_SEAL_FUTURE_WRITE is not interchangeable with F_SEAL_WRITE when a consumer requires bytes that cannot change after handoff. The producer must account for existing writable mappings and select the seal whose kernel semantics match the required invariant.

Descriptor transfer does not remove the seals

Seals belong to the file object rather than to one descriptor number. Duplicating the descriptor or passing it over a Unix domain socket does not create an unsealed copy of the same file. A receiver observes the restrictions attached to the backing object.

This property makes sealed memfds useful at IPC boundaries. A producer can populate data, validate its own final representation, apply the required seals, and only then transfer the descriptor. The receiver can query F_GET_SEALS if its protocol requires a particular mask instead of trusting a message that merely states that sealing occurred.

The receiver still has to define what it accepts. A descriptor being a memfd does not imply that useful seals are present, and a nonzero seal mask does not imply that the exact required combination is active. The protocol boundary is the verified set of object properties, not the API name used to create the object.

Seals constrain mutation, not every property of data use

File sealing does not authenticate the producer, validate the bytes, encrypt the contents, or decide which process may receive a duplicated descriptor. Those controls belong to the surrounding IPC, identity, and data-validation design.

Seals also do not convert the object into a universal immutable storage primitive. Their guarantees are the operations defined by the kernel for each seal and the state of relevant mappings when seals are installed. Consumers that need provenance or persistence across a broader storage boundary require separate mechanisms.

The architectural value is narrower and concrete: sealing lets a process change an anonymous file from a construction object into a restricted handoff object. When the producer verifies that conflicting writable mappings are absent, applies the required write and size seals, and the receiver verifies the resulting mask, mutability becomes an explicit property of the descriptor-transfer protocol rather than an assumption about process behavior.