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:
+11
-31
@@ -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])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user