diff --git a/crates/host_env/src/fileutils.rs b/crates/host_env/src/fileutils.rs index 8db9af895..1674c9074 100644 --- a/crates/host_env/src/fileutils.rs +++ b/crates/host_env/src/fileutils.rs @@ -11,6 +11,10 @@ pub use windows::{StatStruct, fstat}; #[cfg(not(windows))] pub fn fstat(fd: crate::crt_fd::Borrowed<'_>) -> std::io::Result { + // Unlike a directory fd, fstat accepts no negative descriptor sentinels. + if fd.as_raw() < 0 { + return Err(std::io::Error::from_raw_os_error(libc::EBADF)); + } rustix::fs::fstat(fd).map_err(Into::into) } diff --git a/extra_tests/snippets/stdlib_os.py b/extra_tests/snippets/stdlib_os.py index a1f40ef4c..2c673e3de 100644 --- a/extra_tests/snippets/stdlib_os.py +++ b/extra_tests/snippets/stdlib_os.py @@ -8,6 +8,26 @@ from testutils import assert_raises assert os.name == "posix" or os.name == "nt" + +def stat_rejects_negative_file_descriptors(): + import errno + import os + + for stat in (os.stat, os.fstat): + for fd in (-1, -5, -9, -100, -(2**31)): + try: + stat(fd) + except OSError as exc: + assert exc.errno == errno.EBADF, (stat, fd, exc) + else: + raise AssertionError((stat, fd, "accepted a negative file descriptor")) + + +# Keep the original RustPython-only coverage: CPython treats -1 as a path sentinel. +if os.name == "posix" and sys.implementation.name == "rustpython": + stat_rejects_negative_file_descriptors() + + fd = os.open("README.md", os.O_RDONLY) assert fd > 0