It seems like we should be able to add a niche optimization to the FileDesc
type internal. With this optimization, an Option<File>
will become the same size as a regular File
.
diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs
index c481ca8961f..090d9f7804a 100644
--- a/src/libstd/sys/unix/fd.rs
+++ b/src/libstd/sys/unix/fd.rs
@@ -10,6 +10,7 @@ use crate::sys_common::AsInner;
use libc::{c_int, c_void, ssize_t};
#[derive(Debug)]
+#[rustc_layout_scalar_valid_range_start(0)]
pub struct FileDesc {
fd: c_int,
}
@@ -28,7 +29,8 @@ fn max_len() -> usize {
impl FileDesc {
pub fn new(fd: c_int) -> FileDesc {
- FileDesc { fd }
+ assert!(fd >= 0);
+ unsafe { FileDesc { fd } }
}
pub fn raw(&self) -> c_int {
Can a valid file descriptor be negative?
On Linux, the answer is pretty obviously no. Linux file descriptors are stored in an array of structs that has its capacity bounded to INT_MAX
, so any negative int
would either be considered nonsense (if treated as negative) or be higher than INT_MAX
(if it was bit-reinterpreted as an unsigned value). Also, notice how dup2
returns a negative error code while also returning a positive file descriptor directly, as if they couldn't possibly overlap.
The Single UNIX Specification explicitly says that open
can't return a negative file descriptor, and says in the page for dup2
that you should get EBADF if you try to claim a negative file descriptor.
Unfortunately, their description of file descriptor allocation never explicitly says that the negative range is out of bounds, but open
references this algorithm while simultaneously claiming that it never gives a negative result.
Also, Wikipedia says it can't be negative, but they don't give a source on that particular claim (urgh!).
Does Rust's existing API allow it?
Here's what FromRawFd
says about it.
Constructs a new instance of
Self
from the given raw file descriptor.This function consumes ownership of the specified file descriptor. The returned object will take responsibility for closing it when the object goes out of scope.
This function is also unsafe as the primitives currently returned have the contract that they are the sole owner of the file descriptor they are wrapping. Usage of this function could accidentally allow violating this contract which can cause memory unsafety in code that relies on it being true.
It certainly cannot "claim exclusive ownership" of a resource that does not exist, so presumably an invalid file descriptor must not be supplied to this function. Also, it's unsafe, so triggering memory problems should not be out of the question here.
But while it says that it "can cause memory unsafety," (1) that's not the same thing as being insta-UB (2) it doesn't actually say when, presumably because it might be platform-dependent.
Is this one of those cases where "it's not worth the possibility that someone, somewhere, might be relying on this," even though it's not clearly allowed here? Or am I just twisting the language to try to support my pet project, and stuffing a number that isn't a file descriptor into a File is fine as long as you forget
it before it tries to call close
?