diff --git a/judge-core/Cargo.toml b/judge-core/Cargo.toml index 10df489..0e042f4 100644 --- a/judge-core/Cargo.toml +++ b/judge-core/Cargo.toml @@ -10,7 +10,7 @@ description = "A judge library for online judge system" [dependencies] libc = "0.2" libseccomp = "0.3" -nix = { version = "0.27", features = ["event", "fs", "process", "resource"] } +nix = { version = "0.28", features = ["event", "fs", "process", "resource"] } log = "0.4" anyhow = "1.0" serde = "1" diff --git a/judge-core/src/judge/interact.rs b/judge-core/src/judge/interact.rs index 244360d..4ff051b 100644 --- a/judge-core/src/judge/interact.rs +++ b/judge-core/src/judge/interact.rs @@ -9,7 +9,7 @@ use crate::utils::get_pathbuf_str; use nix::errno::Errno; use nix::fcntl::{fcntl, FcntlArg, OFlag}; -use nix::sys::epoll::{Epoll, EpollCreateFlags, EpollEvent, EpollFlags}; +use nix::sys::epoll::{Epoll, EpollCreateFlags, EpollEvent, EpollFlags, EpollTimeout}; use nix::unistd::{pipe, read, write}; use std::fs::File; use std::os::fd::BorrowedFd; @@ -31,6 +31,7 @@ fn set_fd_non_blocking(fd: RawFd) -> Result { /// write the content of `from` to `to`, record to output. /// `from` will be set to non-blocking mode. fn pump_proxy_pipe(from: RawFd, to: RawFd, output: RawFd) -> Result<(), JudgeCoreError> { + log::debug!("Pumping from {} to {} with output {}", from, to, output); set_fd_non_blocking(from)?; let mut buf = [0; 1024]; @@ -38,8 +39,10 @@ fn pump_proxy_pipe(from: RawFd, to: RawFd, output: RawFd) -> Result<(), JudgeCor match read(from, &mut buf) { Ok(nread) => { log::debug!("{} read. {} -> {}", nread, from, to); - write(to, &buf[..nread])?; - write(output, &buf[..nread])?; + // We should be really careful here + // not using OwnedFd here because it will close the fd + write(unsafe { BorrowedFd::borrow_raw(to) }, &buf[..nread])?; + write(unsafe { BorrowedFd::borrow_raw(output) }, &buf[..nread])?; } Err(e) => { if e == Errno::EAGAIN || e == Errno::EWOULDBLOCK { @@ -104,19 +107,19 @@ pub fn run_interact( let (interactor_read_proxy, proxy_write_interactor) = pipe()?; log::debug!("Adding read proxy fds to epoll"); - add_epoll_fd(&epoll, proxy_read_user)?; - add_epoll_fd(&epoll, proxy_read_interactor)?; + add_epoll_fd(&epoll, proxy_read_user.as_raw_fd())?; + add_epoll_fd(&epoll, proxy_read_interactor.as_raw_fd())?; log::debug!("Creating exit report pipes with epoll"); let (user_exit_read, user_exit_write) = pipe()?; let (interactor_exit_read, interactor_exit_write) = pipe()?; - add_epoll_fd(&epoll, user_exit_read)?; - add_epoll_fd(&epoll, interactor_exit_read)?; + add_epoll_fd(&epoll, user_exit_read.as_raw_fd())?; + add_epoll_fd(&epoll, interactor_exit_read.as_raw_fd())?; let mut user_listener = ProcessListener::new()?; let mut interact_listener = ProcessListener::new()?; - user_listener.setup_exit_report(user_exit_write, USER_EXIT_SIGNAL); - interact_listener.setup_exit_report(interactor_exit_write, INTERACTOR_EXIT_SIGNAL); + user_listener.setup_exit_report(user_exit_write.as_raw_fd(), USER_EXIT_SIGNAL); + interact_listener.setup_exit_report(interactor_exit_write.as_raw_fd(), INTERACTOR_EXIT_SIGNAL); if !PathBuf::from(&output_path).exists() { File::create(output_path)?; @@ -130,8 +133,8 @@ pub fn run_interact( let mut user_sandbox = Sandbox::new( config.program.executor.clone(), config.runtime.rlimit_configs.clone(), - Some(user_read_proxy), - Some(user_write_proxy), + Some(user_read_proxy.as_raw_fd()), + Some(user_write_proxy.as_raw_fd()), true, )?; user_listener.spawn_with_sandbox(&mut user_sandbox)?; @@ -147,8 +150,8 @@ pub fn run_interact( let mut interact_sandbox = Sandbox::new( interactor_executor, SCRIPT_LIMIT_CONFIG.clone(), - Some(interactor_read_proxy), - Some(interactor_write_proxy), + Some(interactor_read_proxy.as_raw_fd()), + Some(interactor_write_proxy.as_raw_fd()), false, )?; interact_listener.spawn_with_sandbox(&mut interact_sandbox)?; @@ -159,30 +162,38 @@ pub fn run_interact( let mut interactor_exited = false; let mut option_user_result: Option = None; loop { - let num_events = epoll.wait(&mut events, -1)?; + let num_events = epoll.wait(&mut events, EpollTimeout::NONE)?; log::debug!("{} events found!", num_events); for event in events.iter().take(num_events) { log::debug!("Event: {:?}", event); let fd = event.data() as RawFd; - if fd == user_exit_read { + if fd == user_exit_read.as_raw_fd() { log::debug!("{:?} user fd exited", fd); user_exited = true; let exit_msg = read_msg_from_fd(fd)?; option_user_result = exit_msg.option_run_result; } - if fd == interactor_exit_read { + if fd == interactor_exit_read.as_raw_fd() { log::debug!("{:?} interactor fd exited", fd); interactor_exited = true; let _interactor_result: ProcessExitMessage = read_msg_from_fd(fd)?; } - if fd == proxy_read_user { + if fd == proxy_read_user.as_raw_fd() { log::debug!("proxy_read_user {} fd read", fd); - pump_proxy_pipe(proxy_read_user, proxy_write_interactor, output_raw_fd)?; + pump_proxy_pipe( + proxy_read_user.as_raw_fd(), + proxy_write_interactor.as_raw_fd(), + output_raw_fd.as_raw_fd(), + )?; } - if fd == proxy_read_interactor { + if fd == proxy_read_interactor.as_raw_fd() { log::debug!("proxy_read_interactor {} fd read", fd); - pump_proxy_pipe(proxy_read_interactor, proxy_write_user, output_raw_fd)?; + pump_proxy_pipe( + proxy_read_interactor.as_raw_fd(), + proxy_write_user.as_raw_fd(), + output_raw_fd.as_raw_fd(), + )?; } } if user_exited && interactor_exited { diff --git a/judge-core/src/judge/result.rs b/judge-core/src/judge/result.rs index 8e261fe..0309dc8 100644 --- a/judge-core/src/judge/result.rs +++ b/judge-core/src/judge/result.rs @@ -49,7 +49,6 @@ pub fn check_user_result( ) -> Option { if let Some(time_limit) = config.runtime.rlimit_configs.get_cpu_limit_duration() { let run_time = get_run_time(raw_info); - // run_time is a little bit shorter than time_limit if run_time > time_limit { log::debug!("User program run time: {:?}", run_time); log::debug!("Time limit: {:?}", time_limit); diff --git a/judge-core/src/run/process_listener.rs b/judge-core/src/run/process_listener.rs index fec6097..5a23a22 100644 --- a/judge-core/src/run/process_listener.rs +++ b/judge-core/src/run/process_listener.rs @@ -2,7 +2,7 @@ use super::sandbox::{RawRunResultInfo, Sandbox}; use crate::error::JudgeCoreError; use nix::unistd::{fork, write, ForkResult}; use serde_derive::{Deserialize, Serialize}; -use std::os::unix::io::RawFd; +use std::os::{fd::BorrowedFd, unix::io::RawFd}; pub struct ProcessListener { child_exit_fd: i32, @@ -31,7 +31,9 @@ impl ProcessListener { option_run_result, }; let buf = serde_json::to_vec(&msg).expect("Serialize failed."); - write(self.child_exit_fd, &buf).unwrap(); + // We should be really careful here + // not using OwnedFd here because it will close the fd + write(unsafe { BorrowedFd::borrow_raw(self.child_exit_fd) }, &buf).unwrap(); } }