relibc: fix pipe2 to use raw syscalls + register_external_fd

The previous pipe2 implementation used redox_rt::sys::open() which goes
through the FILETABLE's insert_upper mechanism. This can collide with
the untracked namespace fd (ns_fd) stored in DYNAMIC_PROC_INFO, causing
the kernel to overwrite the namespace fd entry with a pipe scheme root
entry. Subsequent open() calls then resolve to the wrong scheme, and
kdup fails with EBADF because it receives a write-end (odd) ID instead
of a read-end (even) ID.

Fix: bypass the open() function entirely and use raw syscall::openat +
syscall::dup, then register both fds with register_external_fd so the
FILETABLE tracks them correctly.
This commit is contained in:
Red Bear OS
2026-07-13 22:11:15 +03:00
parent 9ada7b78bb
commit eac112e919
+11 -31
View File
@@ -4,9 +4,7 @@ use crate::{
error::{Errno, ResultExt},
platform::types::*,
};
use syscall::{F_SETFD, F_SETFL, O_CLOEXEC, O_RDONLY, O_WRONLY, error::*};
pub use redox_rt::proc::FdGuard;
use syscall::{F_SETFD, F_SETFL, O_CLOEXEC, O_RDONLY, O_WRONLY};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_fpath(fd: c_int, buf: *mut c_void, count: size_t) -> ssize_t {
@@ -21,37 +19,19 @@ pub unsafe extern "C" fn redox_fpath(fd: c_int, buf: *mut c_void, count: size_t)
pub fn pipe2(flags: usize) -> syscall::error::Result<[c_int; 2]> {
let read_flags = flags | O_RDONLY;
let write_flags = flags | O_WRONLY;
let read_fd_raw = redox_rt::sys::open("/scheme/pipe", read_flags).map_err(|e| {
eprintln!("PIPE2_DIAG: open(/scheme/pipe) failed: errno={}", e.errno);
e
})?;
eprintln!("PIPE2_DIAG: open succeeded, read_fd_raw={}", read_fd_raw);
let mut stat: syscall::Stat = unsafe { core::mem::zeroed() };
let fstat_res = syscall::fstat(read_fd_raw, &mut stat);
eprintln!("PIPE2_DIAG: fstat(read_fd={}) => {:?}", read_fd_raw, fstat_res.as_ref().map(|_| ()).map_err(|e| e.errno));
let ns_fd = redox_rt::current_namespace_fd()?;
let read_fd = FdGuard::new(read_fd_raw);
let write_fd = read_fd.dup(b"write").map_err(|e| {
eprintln!("PIPE2_DIAG: dup(write) failed: errno={}, read_fd={}", e.errno, read_fd.as_raw_fd());
e
})?;
write_fd.fcntl(F_SETFL, write_flags).map_err(|e| {
eprintln!("PIPE2_DIAG: F_SETFL failed: errno={}", e.errno);
e
})?;
write_fd.fcntl(F_SETFD, flags & O_CLOEXEC).map_err(|e| {
eprintln!("PIPE2_DIAG: F_SETFD failed: errno={}", e.errno);
e
})?;
let read_fd = syscall::openat(ns_fd, "/scheme/pipe", read_flags, 0)?;
redox_rt::sys::register_external_fd(read_fd)?;
let fds = [
c_int::try_from(read_fd.as_raw_fd()).map_err(|_| Error::new(EMFILE))?,
c_int::try_from(write_fd.as_raw_fd()).map_err(|_| Error::new(EMFILE))?,
];
let write_fd = syscall::dup(read_fd, b"write")?;
redox_rt::sys::register_external_fd(write_fd)?;
read_fd.take();
write_fd.take();
if flags & O_CLOEXEC != 0 {
let _ = syscall::fcntl(write_fd, F_SETFD, O_CLOEXEC);
}
let _ = syscall::fcntl(write_fd, F_SETFL, write_flags);
Ok(fds)
Ok([read_fd as c_int, write_fd as c_int])
}