fcntl: add the file-sealing ABI, and refuse seals honestly
Adds F_ADD_SEALS (1033), F_GET_SEALS (1034) and the six F_SEAL_* flags at their Linux ABI values, so cbindgen emits them into fcntl.h. Consumers that reference them now compile; kwin's utils/ramfile.cpp was failing on F_SEAL_SHRINK/GROW/SEAL/WRITE and F_ADD_SEALS/F_GET_SEALS. F_ADD_SEALS takes an argument, so it is added to the va_list match in fcntl() -- without that it would have read whatever happened to be next. Sys::fcntl (redox) now rejects both commands explicitly with EINVAL. They previously fell through to redox_rt::sys::fcntl, where command 1033 has no defined meaning; refusing deliberately is safer than relying on the kernel to reject a command it does not know. EINVAL is deliberate and is not a placeholder. Seals are a property of the underlying object, enforced against every holder of the descriptor across dup() and across processes. Redox has no such object: there is no memfd_create, and no scheme services a seal mask. Linux returns EINVAL for F_ADD_SEALS on any fd whose backing object cannot seal, so callers that already cope with unsealable backing stores work unchanged -- kwin notes "This can fail for QTemporaryFile based on the underlying file system" and degrades to an unsealed mapping. These must not be made to return success without enforcement. A caller that seals a buffer before sharing it with a less-trusted process depends on the seal holding; reporting success without enforcing turns an honest failure into a silent one. Implementing them for real means giving Redox a sealable object. On a microkernel that belongs in a userspace scheme rather than the kernel: a `memfd:` scheme owning the memory and checking a per-object monotonic seal mask in its write/ftruncate/fmap handlers, with F_ADD_SEALS/F_GET_SEALS routed to it from Sys::fcntl. memfd_create would be built on the same object. Both are left unimplemented rather than faked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+41
-1
@@ -51,6 +51,46 @@ pub const F_OFD_SETLKW: c_int = 38;
|
||||
/// Duplicate file descriptor with the close-on-exec flag `FD_CLOEXEC` set.
|
||||
pub const F_DUPFD_CLOEXEC: c_int = 1030;
|
||||
|
||||
// File sealing (Linux ABI values). Seals are a property of the underlying
|
||||
// memory object, enforced against every holder of the descriptor across dup()
|
||||
// and across processes. Redox has no such object yet: there is no
|
||||
// memfd_create, and no scheme services a seal mask, so `fcntl` reports these
|
||||
// as unsupported (EINVAL) rather than accepting them.
|
||||
//
|
||||
// That refusal is the correct, Linux-compatible behaviour -- Linux likewise
|
||||
// returns EINVAL for F_ADD_SEALS on any fd whose backing object cannot seal.
|
||||
// Callers are expected to cope; kwin's utils/ramfile.cpp, for instance, notes
|
||||
// "This can fail for QTemporaryFile based on the underlying file system" and
|
||||
// degrades to an unsealed mapping.
|
||||
//
|
||||
// DO NOT make these succeed without enforcement. A caller that seals a buffer
|
||||
// it then shares with a less-trusted process is relying on the seal to hold;
|
||||
// reporting success without enforcing it converts an honest failure into a
|
||||
// silent one.
|
||||
//
|
||||
// Implementing them for real means giving Redox a sealable object. On a
|
||||
// microkernel that belongs in a userspace scheme, not the kernel: a `memfd:`
|
||||
// scheme owning the memory and checking a per-object monotonic seal mask in
|
||||
// its write/ftruncate/fmap handlers, with F_ADD_SEALS/F_GET_SEALS routed to it
|
||||
// from Sys::fcntl. See local/docs/ for the workstream note.
|
||||
/// Add seals to the underlying memory object. Unsupported on Redox: EINVAL.
|
||||
pub const F_ADD_SEALS: c_int = 1033;
|
||||
/// Query the seals set on the underlying memory object. Unsupported: EINVAL.
|
||||
pub const F_GET_SEALS: c_int = 1034;
|
||||
|
||||
/// Prevent any further seals from being set.
|
||||
pub const F_SEAL_SEAL: c_int = 0x0001;
|
||||
/// Prevent the object from being shrunk.
|
||||
pub const F_SEAL_SHRINK: c_int = 0x0002;
|
||||
/// Prevent the object from being grown.
|
||||
pub const F_SEAL_GROW: c_int = 0x0004;
|
||||
/// Prevent writes to the object.
|
||||
pub const F_SEAL_WRITE: c_int = 0x0008;
|
||||
/// Prevent future writes while leaving existing mappings writable.
|
||||
pub const F_SEAL_FUTURE_WRITE: c_int = 0x0010;
|
||||
/// Prevent the object from being mapped executable.
|
||||
pub const F_SEAL_EXEC: c_int = 0x0020;
|
||||
|
||||
// Used for `l_type` to describe the type of lock {
|
||||
/// Shared or read lock.
|
||||
pub const F_RDLCK: c_int = 0;
|
||||
@@ -116,7 +156,7 @@ pub unsafe extern "C" fn fcntl(fildes: c_int, cmd: c_int, mut __valist: ...) ->
|
||||
// c_ulonglong
|
||||
let arg = match cmd {
|
||||
F_DUPFD | F_SETFD | F_SETFL | F_GETLK | F_SETLK | F_SETLKW | F_OFD_GETLK | F_OFD_SETLK
|
||||
| F_OFD_SETLKW | F_DUPFD_CLOEXEC => unsafe { __valist.next_arg::<c_ulonglong>() },
|
||||
| F_OFD_SETLKW | F_DUPFD_CLOEXEC | F_ADD_SEALS => unsafe { __valist.next_arg::<c_ulonglong>() },
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
|
||||
@@ -569,6 +569,18 @@ unsafe { &mut *(args as *mut flock) };
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
fcntl::F_ADD_SEALS | fcntl::F_GET_SEALS => {
|
||||
// File sealing needs an object that enforces the seal mask against
|
||||
// every holder of the fd, in every process. Redox has none yet, so
|
||||
// refuse explicitly instead of letting an unrecognised command
|
||||
// reach the kernel, where its meaning is undefined.
|
||||
//
|
||||
// EINVAL is what Linux returns for a non-sealable object, so callers
|
||||
// that already handle unsealable backing stores work unchanged.
|
||||
// Do not turn this into Ok(0) -- see the note in header/fcntl/mod.rs.
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user