mirror of
https://github.com/MeiK2333/river.git
synced 2025-11-04 14:49:40 +08:00
cargo fmt
This commit is contained in:
parent
211050a434
commit
5289506886
@ -8,60 +8,60 @@ use tempfile::tempdir_in;
|
|||||||
use std::println as debug;
|
use std::println as debug;
|
||||||
|
|
||||||
pub struct CGroupOptions {
|
pub struct CGroupOptions {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CGroupOptions {
|
impl CGroupOptions {
|
||||||
pub fn new(base_path: &str) -> Result<CGroupOptions> {
|
pub fn new(base_path: &str) -> Result<CGroupOptions> {
|
||||||
let pwd = try_io!(tempdir_in(base_path));
|
let pwd = try_io!(tempdir_in(base_path));
|
||||||
debug!("register cgroup {:?}", pwd);
|
debug!("register cgroup {:?}", pwd);
|
||||||
Ok(CGroupOptions {
|
Ok(CGroupOptions {
|
||||||
path: pwd.path().to_path_buf(),
|
path: pwd.path().to_path_buf(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 将指定进程加入该 cgroup 组
|
/// 将指定进程加入该 cgroup 组
|
||||||
pub fn apply(&self, pid: i32) -> Result<()> {
|
pub fn apply(&self, pid: i32) -> Result<()> {
|
||||||
debug!("add {} to cgroup {:?}", pid, self.path);
|
debug!("add {} to cgroup {:?}", pid, self.path);
|
||||||
self.set("cgroup.procs", &format!("{}", pid))
|
self.set("cgroup.procs", &format!("{}", pid))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// e.g `set("memory.limit_in_bytes", "67108864")`
|
/// e.g `set("memory.limit_in_bytes", "67108864")`
|
||||||
pub fn set(&self, key: &str, value: &str) -> Result<()> {
|
pub fn set(&self, key: &str, value: &str) -> Result<()> {
|
||||||
debug!("set {} values {}", key, value);
|
debug!("set {} values {}", key, value);
|
||||||
try_io!(fs::write(self.path.join(key), value));
|
try_io!(fs::write(self.path.join(key), value));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// e.g `get("memory.max_usage_in_bytes")`
|
/// e.g `get("memory.max_usage_in_bytes")`
|
||||||
pub fn get(&self, key: &str) -> Result<String> {
|
pub fn get(&self, key: &str) -> Result<String> {
|
||||||
Ok(try_io!(read_to_string(self.path.join(key))))
|
Ok(try_io!(read_to_string(self.path.join(key))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for CGroupOptions {
|
impl Drop for CGroupOptions {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
debug!("remove cgroup {:?}", self.path);
|
debug!("remove cgroup {:?}", self.path);
|
||||||
remove_dir(&self.path).unwrap();
|
remove_dir(&self.path).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// cgroup v1
|
/// cgroup v1
|
||||||
pub struct CGroupSet {
|
pub struct CGroupSet {
|
||||||
// pub cpuset: CGroupOptions,
|
// pub cpuset: CGroupOptions,
|
||||||
pub memory: CGroupOptions,
|
pub memory: CGroupOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CGroupSet {
|
impl CGroupSet {
|
||||||
pub fn new() -> Result<CGroupSet> {
|
pub fn new() -> Result<CGroupSet> {
|
||||||
Ok(CGroupSet {
|
Ok(CGroupSet {
|
||||||
// cpuset: CGroupOptions::new("/sys/fs/cgroup/cpuset")?,
|
// cpuset: CGroupOptions::new("/sys/fs/cgroup/cpuset")?,
|
||||||
memory: CGroupOptions::new("/sys/fs/cgroup/memory")?,
|
memory: CGroupOptions::new("/sys/fs/cgroup/memory")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn apply(&self, pid: i32) -> Result<()> {
|
pub fn apply(&self, pid: i32) -> Result<()> {
|
||||||
// self.cpuset.apply(pid)?;
|
// self.cpuset.apply(pid)?;
|
||||||
self.memory.apply(pid)?;
|
self.memory.apply(pid)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
src/error.rs
42
src/error.rs
@ -10,10 +10,10 @@ use std::result;
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
IOError(io::Error),
|
IOError(io::Error),
|
||||||
StringToCStringError(NulError),
|
StringToCStringError(NulError),
|
||||||
ParseIntError(std::num::ParseIntError),
|
ParseIntError(std::num::ParseIntError),
|
||||||
CreateTempDirError(io::Error),
|
CreateTempDirError(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
@ -21,30 +21,30 @@ pub type Result<T> = result::Result<T, Error>;
|
|||||||
// 创建一个简单的包装
|
// 创建一个简单的包装
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! try_io {
|
macro_rules! try_io {
|
||||||
($expression:expr) => {
|
($expression:expr) => {
|
||||||
match $expression {
|
match $expression {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(e) => return Err(Error::IOError(e)),
|
Err(e) => return Err(Error::IOError(e)),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::IOError(ref e) => write!(f, "IOError: {}", errno_str(e.raw_os_error())),
|
Error::IOError(ref e) => write!(f, "IOError: {}", errno_str(e.raw_os_error())),
|
||||||
_ => write!(f, "{:?}", self),
|
_ => write!(f, "{:?}", self),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn errno_str(errno: Option<i32>) -> String {
|
pub fn errno_str(errno: Option<i32>) -> String {
|
||||||
match errno {
|
match errno {
|
||||||
Some(no) => {
|
Some(no) => {
|
||||||
let stre = unsafe { strerror(no) };
|
let stre = unsafe { strerror(no) };
|
||||||
let c_str: &CStr = unsafe { CStr::from_ptr(stre) };
|
let c_str: &CStr = unsafe { CStr::from_ptr(stre) };
|
||||||
c_str.to_str().unwrap().to_string()
|
c_str.to_str().unwrap().to_string()
|
||||||
|
}
|
||||||
|
_ => "Unknown Error!".to_string(),
|
||||||
}
|
}
|
||||||
_ => "Unknown Error!".to_string(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,46 +7,46 @@ use std::ptr;
|
|||||||
use std::println as debug;
|
use std::println as debug;
|
||||||
|
|
||||||
pub struct ExecArgs {
|
pub struct ExecArgs {
|
||||||
pub pathname: *const libc::c_char,
|
pub pathname: *const libc::c_char,
|
||||||
pub argv: *const *const libc::c_char,
|
pub argv: *const *const libc::c_char,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecArgs {
|
impl ExecArgs {
|
||||||
pub fn build(cmd: &String) -> Result<ExecArgs> {
|
pub fn build(cmd: &String) -> Result<ExecArgs> {
|
||||||
let cmds: Vec<&str> = cmd.split_whitespace().collect();
|
let cmds: Vec<&str> = cmd.split_whitespace().collect();
|
||||||
let pathname = cmds[0].clone();
|
let pathname = cmds[0].clone();
|
||||||
let pathname_str = match CString::new(pathname) {
|
let pathname_str = match CString::new(pathname) {
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(err) => return Err(Error::StringToCStringError(err)),
|
Err(err) => return Err(Error::StringToCStringError(err)),
|
||||||
};
|
};
|
||||||
let pathname = pathname_str.as_ptr();
|
let pathname = pathname_str.as_ptr();
|
||||||
|
|
||||||
let mut argv_vec: Vec<*const libc::c_char> = vec![];
|
let mut argv_vec: Vec<*const libc::c_char> = vec![];
|
||||||
for item in cmds.iter() {
|
for item in cmds.iter() {
|
||||||
let cstr = match CString::new(item.clone()) {
|
let cstr = match CString::new(item.clone()) {
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(err) => return Err(Error::StringToCStringError(err)),
|
Err(err) => return Err(Error::StringToCStringError(err)),
|
||||||
};
|
};
|
||||||
let cptr = cstr.as_ptr();
|
let cptr = cstr.as_ptr();
|
||||||
// 需要使用 mem::forget 来标记
|
// 需要使用 mem::forget 来标记
|
||||||
// 否则在此次循环结束后,cstr 就会被回收,后续 exec 函数无法通过指针获取到字符串内容
|
// 否则在此次循环结束后,cstr 就会被回收,后续 exec 函数无法通过指针获取到字符串内容
|
||||||
mem::forget(cstr);
|
mem::forget(cstr);
|
||||||
argv_vec.push(cptr);
|
argv_vec.push(cptr);
|
||||||
|
}
|
||||||
|
// argv 与 envp 的参数需要使用 NULL 来标记结束
|
||||||
|
argv_vec.push(ptr::null());
|
||||||
|
let argv: *const *const libc::c_char = argv_vec.as_ptr() as *const *const libc::c_char;
|
||||||
|
|
||||||
|
mem::forget(pathname_str);
|
||||||
|
mem::forget(argv_vec);
|
||||||
|
Ok(ExecArgs { pathname, argv })
|
||||||
}
|
}
|
||||||
// argv 与 envp 的参数需要使用 NULL 来标记结束
|
|
||||||
argv_vec.push(ptr::null());
|
|
||||||
let argv: *const *const libc::c_char = argv_vec.as_ptr() as *const *const libc::c_char;
|
|
||||||
|
|
||||||
mem::forget(pathname_str);
|
|
||||||
mem::forget(argv_vec);
|
|
||||||
Ok(ExecArgs { pathname, argv })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ExecArgs {
|
impl Drop for ExecArgs {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// TODO: 将不安全的指针类型转换回内置类型,以便由 Rust 自动回收资源
|
// TODO: 将不安全的指针类型转换回内置类型,以便由 Rust 自动回收资源
|
||||||
// TODO: 优先级较低,因为目前只在子进程里进行这个操作,且操作后会很快 exec,操作系统会回收这些内存
|
// TODO: 优先级较低,因为目前只在子进程里进行这个操作,且操作后会很快 exec,操作系统会回收这些内存
|
||||||
debug!("TODO");
|
debug!("TODO");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/judger.rs
Normal file
1
src/judger.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
||||||
123
src/main.rs
123
src/main.rs
@ -18,11 +18,12 @@ mod error;
|
|||||||
|
|
||||||
mod cgroup;
|
mod cgroup;
|
||||||
mod exec_args;
|
mod exec_args;
|
||||||
|
mod judger;
|
||||||
mod process;
|
mod process;
|
||||||
mod seccomp;
|
mod seccomp;
|
||||||
|
|
||||||
pub mod river {
|
pub mod river {
|
||||||
tonic::include_proto!("river");
|
tonic::include_proto!("river");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -30,76 +31,78 @@ pub struct RiverService {}
|
|||||||
|
|
||||||
#[tonic::async_trait]
|
#[tonic::async_trait]
|
||||||
impl River for RiverService {
|
impl River for RiverService {
|
||||||
type JudgeStream =
|
type JudgeStream =
|
||||||
Pin<Box<dyn Stream<Item = Result<JudgeResponse, Status>> + Send + Sync + 'static>>;
|
Pin<Box<dyn Stream<Item = Result<JudgeResponse, Status>> + Send + Sync + 'static>>;
|
||||||
|
|
||||||
async fn judge(
|
async fn judge(
|
||||||
&self,
|
&self,
|
||||||
request: Request<tonic::Streaming<JudgeRequest>>,
|
request: Request<tonic::Streaming<JudgeRequest>>,
|
||||||
) -> Result<Response<Self::JudgeStream>, Status> {
|
) -> Result<Response<Self::JudgeStream>, Status> {
|
||||||
let mut stream = request.into_inner();
|
let mut stream = request.into_inner();
|
||||||
|
|
||||||
// 此处编译很慢
|
// 此处编译很慢
|
||||||
// 为啥,是 try_stream! 这个宏导致的吗?
|
// 为啥,是 try_stream! 这个宏导致的吗?
|
||||||
let output = async_stream::try_stream! {
|
// 同时内部代码无法被 cargo fmt 格式化
|
||||||
while let Some(req) = stream.next().await {
|
// why?
|
||||||
let req = req?;
|
let output = async_stream::try_stream! {
|
||||||
// TODO
|
while let Some(req) = stream.next().await {
|
||||||
let pwd = tempdir_in("/tmp").unwrap();
|
let req = req?;
|
||||||
match &req.data {
|
// TODO
|
||||||
Some(Data::CompileData(data)) => {
|
let pwd = tempdir_in("/tmp").unwrap();
|
||||||
debug!("compile request");
|
match &req.data {
|
||||||
let language = &data.language;
|
Some(Data::CompileData(data)) => {
|
||||||
let code = &data.code;
|
debug!("compile request");
|
||||||
debug!("language: {}", language);
|
let language = &data.language;
|
||||||
debug!("code: {}", code);
|
let code = &data.code;
|
||||||
break;
|
debug!("language: {}", language);
|
||||||
},
|
debug!("code: {}", code);
|
||||||
Some(Data::JudgeData(data)) => {
|
break;
|
||||||
debug!("judge request");
|
},
|
||||||
let in_file = &data.in_file;
|
Some(Data::JudgeData(data)) => {
|
||||||
let out_file = &data.out_file;
|
debug!("judge request");
|
||||||
let time_limit = &data.time_limit;
|
let in_file = &data.in_file;
|
||||||
let memory_limit = &data.memory_limit;
|
let out_file = &data.out_file;
|
||||||
let judge_type = &data.judge_type;
|
let time_limit = &data.time_limit;
|
||||||
debug!("in_file: {}", in_file);
|
let memory_limit = &data.memory_limit;
|
||||||
debug!("out_file: {}", out_file);
|
let judge_type = &data.judge_type;
|
||||||
debug!("time_limit: {}", time_limit);
|
debug!("in_file: {}", in_file);
|
||||||
debug!("memory_limit: {}", memory_limit);
|
debug!("out_file: {}", out_file);
|
||||||
debug!("judge_type: {}", judge_type);
|
debug!("time_limit: {}", time_limit);
|
||||||
break;
|
debug!("memory_limit: {}", memory_limit);
|
||||||
},
|
debug!("judge_type: {}", judge_type);
|
||||||
None => break,
|
break;
|
||||||
_ => break,
|
},
|
||||||
|
None => break,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
yield JudgeResponse {
|
||||||
|
state: Some(river::judge_response::State::Status(river::JudgeStatus::Pending as i32))
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
|
||||||
yield JudgeResponse {
|
|
||||||
state: Some(river::judge_response::State::Status(river::JudgeStatus::Pending as i32))
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Response::new(Box::pin(output) as Self::JudgeStream))
|
Ok(Response::new(Box::pin(output) as Self::JudgeStream))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let env = Env::default()
|
let env = Env::default()
|
||||||
.filter_or("LOG_LEVEL", "debug,h2=info,hyper=info")
|
.filter_or("LOG_LEVEL", "debug,h2=info,hyper=info")
|
||||||
.write_style_or("LOG_STYLE", "always");
|
.write_style_or("LOG_STYLE", "always");
|
||||||
|
|
||||||
env_logger::init_from_env(env);
|
env_logger::init_from_env(env);
|
||||||
|
|
||||||
let addr = "0.0.0.0:4003".parse()?;
|
let addr = "0.0.0.0:4003".parse()?;
|
||||||
let river = RiverService::default();
|
let river = RiverService::default();
|
||||||
|
|
||||||
info!("listen on: {}", addr);
|
info!("listen on: {}", addr);
|
||||||
|
|
||||||
Server::builder()
|
Server::builder()
|
||||||
.concurrency_limit_per_connection(5)
|
.concurrency_limit_per_connection(5)
|
||||||
.add_service(RiverServer::new(river))
|
.add_service(RiverServer::new(river))
|
||||||
.serve(addr)
|
.serve(addr)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
764
src/process.rs
764
src/process.rs
@ -23,233 +23,233 @@ use std::println as debug;
|
|||||||
const STACK_SIZE: usize = 1024 * 1024;
|
const STACK_SIZE: usize = 1024 * 1024;
|
||||||
|
|
||||||
macro_rules! syscall_or_panic {
|
macro_rules! syscall_or_panic {
|
||||||
($expression:expr) => {
|
($expression:expr) => {
|
||||||
if $expression < 0 {
|
if $expression < 0 {
|
||||||
let err = io::Error::last_os_error().raw_os_error();
|
let err = io::Error::last_os_error().raw_os_error();
|
||||||
panic!(errno_str(err));
|
panic!(errno_str(err));
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! c_str_ptr {
|
macro_rules! c_str_ptr {
|
||||||
($expression:expr) => {
|
($expression:expr) => {
|
||||||
CString::new($expression).unwrap().as_ptr()
|
CString::new($expression).unwrap().as_ptr()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Process {
|
pub struct Process {
|
||||||
pub cmd: String,
|
pub cmd: String,
|
||||||
pub time_limit: i32,
|
pub time_limit: i32,
|
||||||
pub memory_limit: i32,
|
pub memory_limit: i32,
|
||||||
pub workdir: PathBuf,
|
pub workdir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RunnerStatus {
|
pub struct RunnerStatus {
|
||||||
pub rusage: libc::rusage,
|
pub rusage: libc::rusage,
|
||||||
pub exit_code: i32,
|
pub exit_code: i32,
|
||||||
pub status: i32,
|
pub status: i32,
|
||||||
pub signal: i32,
|
pub signal: i32,
|
||||||
pub time_used: i64,
|
pub time_used: i64,
|
||||||
pub real_time_used: u128,
|
pub real_time_used: u128,
|
||||||
pub memory_used: i64,
|
pub memory_used: i64,
|
||||||
pub cgroup_memory_used: i64,
|
pub cgroup_memory_used: i64,
|
||||||
pub errmsg: String,
|
pub errmsg: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
pub fn new(cmd: String, time_limit: i32, memory_limit: i32, workdir: PathBuf) -> Process {
|
pub fn new(cmd: String, time_limit: i32, memory_limit: i32, workdir: PathBuf) -> Process {
|
||||||
let runner = Process {
|
let runner = Process {
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
time_limit: time_limit,
|
time_limit: time_limit,
|
||||||
memory_limit: memory_limit,
|
memory_limit: memory_limit,
|
||||||
workdir: workdir,
|
workdir: workdir,
|
||||||
};
|
};
|
||||||
runner
|
runner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Runner {
|
struct Runner {
|
||||||
process: Process,
|
process: Process,
|
||||||
pid: i32,
|
pid: i32,
|
||||||
tx: Arc<Mutex<mpsc::Sender<RunnerStatus>>>,
|
tx: Arc<Mutex<mpsc::Sender<RunnerStatus>>>,
|
||||||
rx: Arc<Mutex<mpsc::Receiver<RunnerStatus>>>,
|
rx: Arc<Mutex<mpsc::Receiver<RunnerStatus>>>,
|
||||||
stack: *mut libc::c_void,
|
stack: *mut libc::c_void,
|
||||||
cgroup_set: CGroupSet,
|
cgroup_set: CGroupSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Runner {
|
impl Runner {
|
||||||
pub fn from(process: Process) -> Result<Runner> {
|
pub fn from(process: Process) -> Result<Runner> {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
let stack = unsafe {
|
let stack = unsafe {
|
||||||
libc::mmap(
|
libc::mmap(
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
STACK_SIZE,
|
STACK_SIZE,
|
||||||
libc::PROT_READ | libc::PROT_WRITE,
|
libc::PROT_READ | libc::PROT_WRITE,
|
||||||
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_STACK,
|
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_STACK,
|
||||||
-1,
|
-1,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
Ok(Runner {
|
Ok(Runner {
|
||||||
process: process,
|
process: process,
|
||||||
pid: -1,
|
pid: -1,
|
||||||
tx: Arc::new(Mutex::new(tx)),
|
tx: Arc::new(Mutex::new(tx)),
|
||||||
rx: Arc::new(Mutex::new(rx)),
|
rx: Arc::new(Mutex::new(rx)),
|
||||||
stack: stack,
|
stack: stack,
|
||||||
cgroup_set: CGroupSet::new()?,
|
cgroup_set: CGroupSet::new()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Runner {
|
impl Drop for Runner {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
debug!("dropping");
|
debug!("dropping");
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::munmap(self.stack, STACK_SIZE);
|
libc::munmap(self.stack, STACK_SIZE);
|
||||||
|
|
||||||
let mut status = 0;
|
let mut status = 0;
|
||||||
let pid = libc::waitpid(self.pid, &mut status, libc::WNOHANG);
|
let pid = libc::waitpid(self.pid, &mut status, libc::WNOHANG);
|
||||||
|
|
||||||
// > 0: 对应子进程退出但未回收资源
|
// > 0: 对应子进程退出但未回收资源
|
||||||
// = 0: 对应子进程存在但未退出
|
// = 0: 对应子进程存在但未退出
|
||||||
// 如果在运行过程中上层异常中断,则需要 kill 子进程并回收资源
|
// 如果在运行过程中上层异常中断,则需要 kill 子进程并回收资源
|
||||||
if pid >= 0 {
|
if pid >= 0 {
|
||||||
libc::kill(self.pid, 9);
|
libc::kill(self.pid, 9);
|
||||||
libc::waitpid(self.pid, &mut status, 0);
|
libc::waitpid(self.pid, &mut status, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Future for Runner {
|
impl Future for Runner {
|
||||||
type Output = Result<RunnerStatus>;
|
type Output = Result<RunnerStatus>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<RunnerStatus>> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<RunnerStatus>> {
|
||||||
let runner = Pin::into_inner(self);
|
let runner = Pin::into_inner(self);
|
||||||
// 如果 pid == -1,则说明子进程还没有运行
|
// 如果 pid == -1,则说明子进程还没有运行
|
||||||
if runner.pid == -1 {
|
if runner.pid == -1 {
|
||||||
let waker = cx.waker().clone();
|
let waker = cx.waker().clone();
|
||||||
let pid = unsafe {
|
let pid = unsafe {
|
||||||
libc::clone(
|
libc::clone(
|
||||||
runit,
|
runit,
|
||||||
(runner.stack as usize + STACK_SIZE) as *mut libc::c_void,
|
(runner.stack as usize + STACK_SIZE) as *mut libc::c_void,
|
||||||
libc::SIGCHLD
|
libc::SIGCHLD
|
||||||
| libc::CLONE_NEWUTS // 设置新的 UTS 名称空间(主机名、网络名等)
|
| libc::CLONE_NEWUTS // 设置新的 UTS 名称空间(主机名、网络名等)
|
||||||
| libc::CLONE_NEWNET // 设置新的网络空间,如果没有配置网络,则该沙盒内部将无法联网
|
| libc::CLONE_NEWNET // 设置新的网络空间,如果没有配置网络,则该沙盒内部将无法联网
|
||||||
| libc::CLONE_NEWNS // 为沙盒内部设置新的 namespaces 空间
|
| libc::CLONE_NEWNS // 为沙盒内部设置新的 namespaces 空间
|
||||||
| libc::CLONE_NEWIPC // IPC 隔离
|
| libc::CLONE_NEWIPC // IPC 隔离
|
||||||
| libc::CLONE_NEWCGROUP // 在新的 CGROUP 中创建沙盒
|
| libc::CLONE_NEWCGROUP // 在新的 CGROUP 中创建沙盒
|
||||||
| libc::CLONE_NEWPID, // 外部进程对沙盒不可见
|
| libc::CLONE_NEWPID, // 外部进程对沙盒不可见
|
||||||
&mut runner.process as *mut _ as *mut libc::c_void,
|
&mut runner.process as *mut _ as *mut libc::c_void,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
debug!("pid = {}", pid);
|
debug!("pid = {}", pid);
|
||||||
runner.pid = pid;
|
runner.pid = pid;
|
||||||
// 设置 cgroup 限制
|
// 设置 cgroup 限制
|
||||||
// 此处为父进程做的策略,所以没有与子进程的安全策略放一块
|
// 此处为父进程做的策略,所以没有与子进程的安全策略放一块
|
||||||
runner.cgroup_set.apply(pid).unwrap();
|
runner.cgroup_set.apply(pid).unwrap();
|
||||||
runner.cgroup_set.memory.set(
|
runner.cgroup_set.memory.set(
|
||||||
"memory.limit_in_bytes",
|
"memory.limit_in_bytes",
|
||||||
&format!("{}", runner.process.memory_limit as i64 * 1024),
|
&format!("{}", runner.process.memory_limit as i64 * 1024),
|
||||||
)?;
|
)?;
|
||||||
let tx = runner.tx.clone();
|
let tx = runner.tx.clone();
|
||||||
|
|
||||||
// 因为 wait 会阻塞等待结果,因此此处使用 thread::spawn 来 wait,以防止主流程被阻塞
|
// 因为 wait 会阻塞等待结果,因此此处使用 thread::spawn 来 wait,以防止主流程被阻塞
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let status = Runner::waitpid(pid);
|
let status = Runner::waitpid(pid);
|
||||||
// 子流程运行结束后通知主流程
|
// 子流程运行结束后通知主流程
|
||||||
let status_tx = tx.lock().unwrap();
|
let status_tx = tx.lock().unwrap();
|
||||||
status_tx.send(status).unwrap();
|
status_tx.send(status).unwrap();
|
||||||
waker.wake();
|
waker.wake();
|
||||||
});
|
});
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
} else {
|
} else {
|
||||||
let mut status = runner.rx.lock().unwrap().recv().unwrap();
|
let mut status = runner.rx.lock().unwrap().recv().unwrap();
|
||||||
let mem_used = match runner
|
let mem_used = match runner
|
||||||
.cgroup_set
|
.cgroup_set
|
||||||
.memory
|
.memory
|
||||||
.get("memory.max_usage_in_bytes")?
|
.get("memory.max_usage_in_bytes")?
|
||||||
.trim()
|
.trim()
|
||||||
.parse::<i64>()
|
.parse::<i64>()
|
||||||
{
|
{
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(e) => return Poll::Ready(Err(Error::ParseIntError(e))),
|
Err(e) => return Poll::Ready(Err(Error::ParseIntError(e))),
|
||||||
};
|
};
|
||||||
status.cgroup_memory_used = mem_used / 1024;
|
status.cgroup_memory_used = mem_used / 1024;
|
||||||
debug!("cpu time used: {} ms", status.time_used);
|
debug!("cpu time used: {} ms", status.time_used);
|
||||||
debug!("real time used: {} ms", status.real_time_used);
|
debug!("real time used: {} ms", status.real_time_used);
|
||||||
debug!("cgroup memory used: {} KiB", status.cgroup_memory_used);
|
debug!("cgroup memory used: {} KiB", status.cgroup_memory_used);
|
||||||
debug!("rusage memory used: {} KiB", status.memory_used);
|
debug!("rusage memory used: {} KiB", status.memory_used);
|
||||||
return Poll::Ready(Ok(status));
|
return Poll::Ready(Ok(status));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Runner {
|
impl Runner {
|
||||||
fn waitpid(pid: i32) -> RunnerStatus {
|
fn waitpid(pid: i32) -> RunnerStatus {
|
||||||
let mut status: i32 = 0;
|
let mut status: i32 = 0;
|
||||||
let mut rusage = new_rusage();
|
let mut rusage = new_rusage();
|
||||||
let start = SystemTime::now();
|
let start = SystemTime::now();
|
||||||
unsafe {
|
unsafe {
|
||||||
if libc::wait4(pid, &mut status, 0, &mut rusage) < 0 {
|
if libc::wait4(pid, &mut status, 0, &mut rusage) < 0 {
|
||||||
return judge_system_error(String::from("wait4 failure!"));
|
return judge_system_error(String::from("wait4 failure!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let real_time_used = match start.elapsed() {
|
let real_time_used = match start.elapsed() {
|
||||||
Ok(elapsed) => elapsed.as_millis(),
|
Ok(elapsed) => elapsed.as_millis(),
|
||||||
Err(_) => return judge_system_error(String::from("real time elapsed failure!")),
|
Err(_) => return judge_system_error(String::from("real time elapsed failure!")),
|
||||||
};
|
};
|
||||||
let mut exit_code = 0;
|
let mut exit_code = 0;
|
||||||
let exited = libc::WIFEXITED(status);
|
let exited = libc::WIFEXITED(status);
|
||||||
if exited {
|
if exited {
|
||||||
exit_code = libc::WEXITSTATUS(status);
|
exit_code = libc::WEXITSTATUS(status);
|
||||||
}
|
}
|
||||||
let signal = if libc::WIFSIGNALED(status) {
|
let signal = if libc::WIFSIGNALED(status) {
|
||||||
libc::WTERMSIG(status)
|
libc::WTERMSIG(status)
|
||||||
} else if libc::WIFSTOPPED(status) {
|
} else if libc::WIFSTOPPED(status) {
|
||||||
libc::WSTOPSIG(status)
|
libc::WSTOPSIG(status)
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
let time_used = rusage.ru_utime.tv_sec * 1000
|
let time_used = rusage.ru_utime.tv_sec * 1000
|
||||||
+ i64::from(rusage.ru_utime.tv_usec) / 1000
|
+ i64::from(rusage.ru_utime.tv_usec) / 1000
|
||||||
+ rusage.ru_stime.tv_sec * 1000
|
+ rusage.ru_stime.tv_sec * 1000
|
||||||
+ i64::from(rusage.ru_stime.tv_usec) / 1000;
|
+ i64::from(rusage.ru_stime.tv_usec) / 1000;
|
||||||
let memory_used = rusage.ru_maxrss;
|
let memory_used = rusage.ru_maxrss;
|
||||||
|
|
||||||
RunnerStatus {
|
RunnerStatus {
|
||||||
errmsg: String::from(""),
|
errmsg: String::from(""),
|
||||||
memory_used: memory_used,
|
memory_used: memory_used,
|
||||||
cgroup_memory_used: -1,
|
cgroup_memory_used: -1,
|
||||||
time_used: time_used,
|
time_used: time_used,
|
||||||
real_time_used: real_time_used,
|
real_time_used: real_time_used,
|
||||||
exit_code: exit_code,
|
exit_code: exit_code,
|
||||||
signal: signal,
|
signal: signal,
|
||||||
status: status,
|
status: status,
|
||||||
rusage: rusage,
|
rusage: rusage,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn runit(process: *mut libc::c_void) -> i32 {
|
extern "C" fn runit(process: *mut libc::c_void) -> i32 {
|
||||||
let process = unsafe { &mut *(process as *mut Process) };
|
let process = unsafe { &mut *(process as *mut Process) };
|
||||||
debug!("cmd = {}", process.cmd);
|
debug!("cmd = {}", process.cmd);
|
||||||
let exec_args = ExecArgs::build(&process.cmd).unwrap();
|
let exec_args = ExecArgs::build(&process.cmd).unwrap();
|
||||||
unsafe {
|
unsafe {
|
||||||
security(&process);
|
security(&process);
|
||||||
fd_dup();
|
fd_dup();
|
||||||
syscall_or_panic!(libc::execve(
|
syscall_or_panic!(libc::execve(
|
||||||
exec_args.pathname,
|
exec_args.pathname,
|
||||||
exec_args.argv,
|
exec_args.argv,
|
||||||
// 因为运行是在隔离的环境内,原有的环境变量已经没啥用了,因此这里直接传 null
|
// 因为运行是在隔离的环境内,原有的环境变量已经没啥用了,因此这里直接传 null
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
));
|
));
|
||||||
// 理论上并不会到这里,因此如果到这里,直接 kill 掉
|
// 理论上并不会到这里,因此如果到这里,直接 kill 掉
|
||||||
syscall_or_panic!(libc::kill(libc::getpid(), libc::SIGKILL));
|
syscall_or_panic!(libc::kill(libc::getpid(), libc::SIGKILL));
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 为评测沙盒提供安全保障
|
/// 为评测沙盒提供安全保障
|
||||||
@ -264,181 +264,181 @@ extern "C" fn runit(process: *mut libc::c_void) -> i32 {
|
|||||||
/// - `CLONE_NEWNET` 禁止沙盒内部连接网络
|
/// - `CLONE_NEWNET` 禁止沙盒内部连接网络
|
||||||
/// - `CLONE_NEWPID` 隔离内外进程空间
|
/// - `CLONE_NEWPID` 隔离内外进程空间
|
||||||
unsafe fn security(process: &Process) {
|
unsafe fn security(process: &Process) {
|
||||||
// 全局默认权限 755,为运行目录设置特权
|
// 全局默认权限 755,为运行目录设置特权
|
||||||
syscall_or_panic!(libc::chmod(
|
syscall_or_panic!(libc::chmod(
|
||||||
c_str_ptr!(process.workdir.to_str().unwrap()),
|
c_str_ptr!(process.workdir.to_str().unwrap()),
|
||||||
0o777,
|
0o777,
|
||||||
));
|
));
|
||||||
|
|
||||||
// 等同于 mount --make-rprivate /
|
// 等同于 mount --make-rprivate /
|
||||||
// 不将挂载传播到其他空间,以免造成挂载混淆
|
// 不将挂载传播到其他空间,以免造成挂载混淆
|
||||||
syscall_or_panic!(libc::mount(
|
syscall_or_panic!(libc::mount(
|
||||||
c_str_ptr!(""),
|
c_str_ptr!(""),
|
||||||
c_str_ptr!("/"),
|
c_str_ptr!("/"),
|
||||||
c_str_ptr!(""),
|
c_str_ptr!(""),
|
||||||
libc::MS_PRIVATE | libc::MS_REC,
|
libc::MS_PRIVATE | libc::MS_REC,
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
));
|
));
|
||||||
|
|
||||||
// 挂载运行文件夹,除此目录外程序没有其他目录的写权限
|
// 挂载运行文件夹,除此目录外程序没有其他目录的写权限
|
||||||
syscall_or_panic!(libc::mount(
|
syscall_or_panic!(libc::mount(
|
||||||
c_str_ptr!(process.workdir.to_str().unwrap()),
|
c_str_ptr!(process.workdir.to_str().unwrap()),
|
||||||
c_str_ptr!("runtime/rootfs/tmp"),
|
c_str_ptr!("runtime/rootfs/tmp"),
|
||||||
c_str_ptr!("none"),
|
c_str_ptr!("none"),
|
||||||
libc::MS_BIND | libc::MS_PRIVATE,
|
libc::MS_BIND | libc::MS_PRIVATE,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// chdir && chroot,隔离文件系统
|
// chdir && chroot,隔离文件系统
|
||||||
syscall_or_panic!(libc::chdir(c_str_ptr!("runtime/rootfs")));
|
syscall_or_panic!(libc::chdir(c_str_ptr!("runtime/rootfs")));
|
||||||
syscall_or_panic!(libc::chroot(c_str_ptr!(".")));
|
syscall_or_panic!(libc::chroot(c_str_ptr!(".")));
|
||||||
syscall_or_panic!(libc::chdir(c_str_ptr!("/tmp")));
|
syscall_or_panic!(libc::chdir(c_str_ptr!("/tmp")));
|
||||||
|
|
||||||
// 设置主机名
|
// 设置主机名
|
||||||
syscall_or_panic!(libc::sethostname(c_str_ptr!("river"), 5));
|
syscall_or_panic!(libc::sethostname(c_str_ptr!("river"), 5));
|
||||||
syscall_or_panic!(libc::setdomainname(c_str_ptr!("river"), 5));
|
syscall_or_panic!(libc::setdomainname(c_str_ptr!("river"), 5));
|
||||||
|
|
||||||
// 修改用户为 nobody
|
// 修改用户为 nobody
|
||||||
syscall_or_panic!(libc::setgid(65534));
|
syscall_or_panic!(libc::setgid(65534));
|
||||||
syscall_or_panic!(libc::setuid(65534));
|
syscall_or_panic!(libc::setuid(65534));
|
||||||
|
|
||||||
let filter = seccomp::SeccompFilter::new(
|
let filter = seccomp::SeccompFilter::new(
|
||||||
deny_syscalls().into_iter().collect(),
|
deny_syscalls().into_iter().collect(),
|
||||||
seccomp::SeccompAction::Allow,
|
seccomp::SeccompAction::Allow,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
seccomp::SeccompFilter::apply(filter.try_into().unwrap()).unwrap();
|
seccomp::SeccompFilter::apply(filter.try_into().unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 重定向 `stdin`、`stdout`、`stderr`
|
/// 重定向 `stdin`、`stdout`、`stderr`
|
||||||
unsafe fn fd_dup() {
|
unsafe fn fd_dup() {
|
||||||
// 重定向文件描述符
|
// 重定向文件描述符
|
||||||
if Path::new(STDIN_FILENAME).exists() {
|
if Path::new(STDIN_FILENAME).exists() {
|
||||||
dup(STDIN_FILENAME, libc::STDIN_FILENO, libc::O_RDONLY, 0o644);
|
dup(STDIN_FILENAME, libc::STDIN_FILENO, libc::O_RDONLY, 0o644);
|
||||||
}
|
}
|
||||||
if Path::new(STDOUT_FILENAME).exists() {
|
if Path::new(STDOUT_FILENAME).exists() {
|
||||||
remove_file(STDOUT_FILENAME).unwrap();
|
remove_file(STDOUT_FILENAME).unwrap();
|
||||||
}
|
}
|
||||||
dup(
|
dup(
|
||||||
STDOUT_FILENAME,
|
STDOUT_FILENAME,
|
||||||
libc::STDOUT_FILENO,
|
libc::STDOUT_FILENO,
|
||||||
libc::O_CREAT | libc::O_RDWR,
|
libc::O_CREAT | libc::O_RDWR,
|
||||||
0o644,
|
0o644,
|
||||||
);
|
);
|
||||||
if Path::new(STDERR_FILENAME).exists() {
|
if Path::new(STDERR_FILENAME).exists() {
|
||||||
remove_file(STDERR_FILENAME).unwrap();
|
remove_file(STDERR_FILENAME).unwrap();
|
||||||
}
|
}
|
||||||
dup(
|
dup(
|
||||||
STDERR_FILENAME,
|
STDERR_FILENAME,
|
||||||
libc::STDERR_FILENO,
|
libc::STDERR_FILENO,
|
||||||
libc::O_CREAT | libc::O_RDWR,
|
libc::O_CREAT | libc::O_RDWR,
|
||||||
0o644,
|
0o644,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn dup(filename: &str, to: libc::c_int, flag: libc::c_int, mode: libc::c_int) {
|
unsafe fn dup(filename: &str, to: libc::c_int, flag: libc::c_int, mode: libc::c_int) {
|
||||||
let filename_str = CString::new(filename).unwrap();
|
let filename_str = CString::new(filename).unwrap();
|
||||||
let filename = filename_str.as_ptr();
|
let filename = filename_str.as_ptr();
|
||||||
let fd = libc::open(filename, flag, mode);
|
let fd = libc::open(filename, flag, mode);
|
||||||
if fd < 0 {
|
if fd < 0 {
|
||||||
let err = io::Error::last_os_error().raw_os_error();
|
let err = io::Error::last_os_error().raw_os_error();
|
||||||
panic!(errno_str(err));
|
panic!(errno_str(err));
|
||||||
}
|
}
|
||||||
syscall_or_panic!(libc::dup2(fd, to));
|
syscall_or_panic!(libc::dup2(fd, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 阻止危险的系统调用
|
/// 阻止危险的系统调用
|
||||||
///
|
///
|
||||||
/// 参照 Docker 文档 [significant-syscalls-blocked-by-the-default-profile](https://docs.docker.com/engine/security/seccomp/#significant-syscalls-blocked-by-the-default-profile) 一节
|
/// 参照 Docker 文档 [significant-syscalls-blocked-by-the-default-profile](https://docs.docker.com/engine/security/seccomp/#significant-syscalls-blocked-by-the-default-profile) 一节
|
||||||
fn deny_syscalls() -> Vec<seccomp::SyscallRuleSet> {
|
fn deny_syscalls() -> Vec<seccomp::SyscallRuleSet> {
|
||||||
vec![
|
vec![
|
||||||
deny_syscall(libc::SYS_acct),
|
deny_syscall(libc::SYS_acct),
|
||||||
deny_syscall(libc::SYS_add_key),
|
deny_syscall(libc::SYS_add_key),
|
||||||
deny_syscall(libc::SYS_bpf),
|
deny_syscall(libc::SYS_bpf),
|
||||||
deny_syscall(libc::SYS_clock_adjtime),
|
deny_syscall(libc::SYS_clock_adjtime),
|
||||||
deny_syscall(libc::SYS_clock_settime),
|
deny_syscall(libc::SYS_clock_settime),
|
||||||
deny_syscall(libc::SYS_create_module),
|
deny_syscall(libc::SYS_create_module),
|
||||||
deny_syscall(libc::SYS_delete_module),
|
deny_syscall(libc::SYS_delete_module),
|
||||||
deny_syscall(libc::SYS_finit_module),
|
deny_syscall(libc::SYS_finit_module),
|
||||||
deny_syscall(libc::SYS_get_kernel_syms),
|
deny_syscall(libc::SYS_get_kernel_syms),
|
||||||
deny_syscall(libc::SYS_get_mempolicy),
|
deny_syscall(libc::SYS_get_mempolicy),
|
||||||
deny_syscall(libc::SYS_init_module),
|
deny_syscall(libc::SYS_init_module),
|
||||||
deny_syscall(libc::SYS_ioperm),
|
deny_syscall(libc::SYS_ioperm),
|
||||||
deny_syscall(libc::SYS_iopl),
|
deny_syscall(libc::SYS_iopl),
|
||||||
deny_syscall(libc::SYS_kcmp),
|
deny_syscall(libc::SYS_kcmp),
|
||||||
deny_syscall(libc::SYS_kexec_file_load),
|
deny_syscall(libc::SYS_kexec_file_load),
|
||||||
deny_syscall(libc::SYS_kexec_load),
|
deny_syscall(libc::SYS_kexec_load),
|
||||||
deny_syscall(libc::SYS_keyctl),
|
deny_syscall(libc::SYS_keyctl),
|
||||||
deny_syscall(libc::SYS_lookup_dcookie),
|
deny_syscall(libc::SYS_lookup_dcookie),
|
||||||
deny_syscall(libc::SYS_mbind),
|
deny_syscall(libc::SYS_mbind),
|
||||||
deny_syscall(libc::SYS_mount),
|
deny_syscall(libc::SYS_mount),
|
||||||
deny_syscall(libc::SYS_move_pages),
|
deny_syscall(libc::SYS_move_pages),
|
||||||
deny_syscall(libc::SYS_name_to_handle_at),
|
deny_syscall(libc::SYS_name_to_handle_at),
|
||||||
deny_syscall(libc::SYS_nfsservctl),
|
deny_syscall(libc::SYS_nfsservctl),
|
||||||
deny_syscall(libc::SYS_open_by_handle_at),
|
deny_syscall(libc::SYS_open_by_handle_at),
|
||||||
deny_syscall(libc::SYS_perf_event_open),
|
deny_syscall(libc::SYS_perf_event_open),
|
||||||
deny_syscall(libc::SYS_personality),
|
deny_syscall(libc::SYS_personality),
|
||||||
deny_syscall(libc::SYS_pivot_root),
|
deny_syscall(libc::SYS_pivot_root),
|
||||||
deny_syscall(libc::SYS_process_vm_readv),
|
deny_syscall(libc::SYS_process_vm_readv),
|
||||||
deny_syscall(libc::SYS_process_vm_writev),
|
deny_syscall(libc::SYS_process_vm_writev),
|
||||||
deny_syscall(libc::SYS_ptrace),
|
deny_syscall(libc::SYS_ptrace),
|
||||||
deny_syscall(libc::SYS_query_module),
|
deny_syscall(libc::SYS_query_module),
|
||||||
deny_syscall(libc::SYS_quotactl),
|
deny_syscall(libc::SYS_quotactl),
|
||||||
deny_syscall(libc::SYS_reboot),
|
deny_syscall(libc::SYS_reboot),
|
||||||
deny_syscall(libc::SYS_request_key),
|
deny_syscall(libc::SYS_request_key),
|
||||||
deny_syscall(libc::SYS_set_mempolicy),
|
deny_syscall(libc::SYS_set_mempolicy),
|
||||||
deny_syscall(libc::SYS_setns),
|
deny_syscall(libc::SYS_setns),
|
||||||
deny_syscall(libc::SYS_settimeofday),
|
deny_syscall(libc::SYS_settimeofday),
|
||||||
deny_syscall(libc::SYS_swapon),
|
deny_syscall(libc::SYS_swapon),
|
||||||
deny_syscall(libc::SYS_swapoff),
|
deny_syscall(libc::SYS_swapoff),
|
||||||
deny_syscall(libc::SYS_sysfs),
|
deny_syscall(libc::SYS_sysfs),
|
||||||
deny_syscall(libc::SYS__sysctl),
|
deny_syscall(libc::SYS__sysctl),
|
||||||
deny_syscall(libc::SYS_umount2),
|
deny_syscall(libc::SYS_umount2),
|
||||||
deny_syscall(libc::SYS_unshare),
|
deny_syscall(libc::SYS_unshare),
|
||||||
deny_syscall(libc::SYS_uselib),
|
deny_syscall(libc::SYS_uselib),
|
||||||
deny_syscall(libc::SYS_userfaultfd),
|
deny_syscall(libc::SYS_userfaultfd),
|
||||||
deny_syscall(libc::SYS_ustat),
|
deny_syscall(libc::SYS_ustat),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deny_syscall(syscall_number: i64) -> seccomp::SyscallRuleSet {
|
fn deny_syscall(syscall_number: i64) -> seccomp::SyscallRuleSet {
|
||||||
(
|
(
|
||||||
syscall_number,
|
syscall_number,
|
||||||
vec![seccomp::SeccompRule::new(
|
vec![seccomp::SeccompRule::new(
|
||||||
vec![],
|
vec![],
|
||||||
seccomp::SeccompAction::Kill,
|
seccomp::SeccompAction::Kill,
|
||||||
)],
|
)],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 一个全为 `0` 的 `rusage`
|
/// 一个全为 `0` 的 `rusage`
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn new_rusage() -> libc::rusage {
|
fn new_rusage() -> libc::rusage {
|
||||||
libc::rusage {
|
libc::rusage {
|
||||||
ru_utime: libc::timeval {
|
ru_utime: libc::timeval {
|
||||||
tv_sec: 0 as libc::time_t,
|
tv_sec: 0 as libc::time_t,
|
||||||
tv_usec: 0 as libc::suseconds_t,
|
tv_usec: 0 as libc::suseconds_t,
|
||||||
},
|
},
|
||||||
ru_stime: libc::timeval {
|
ru_stime: libc::timeval {
|
||||||
tv_sec: 0 as libc::time_t,
|
tv_sec: 0 as libc::time_t,
|
||||||
tv_usec: 0 as libc::suseconds_t,
|
tv_usec: 0 as libc::suseconds_t,
|
||||||
},
|
},
|
||||||
ru_maxrss: 0 as libc::c_long,
|
ru_maxrss: 0 as libc::c_long,
|
||||||
ru_ixrss: 0 as libc::c_long,
|
ru_ixrss: 0 as libc::c_long,
|
||||||
ru_idrss: 0 as libc::c_long,
|
ru_idrss: 0 as libc::c_long,
|
||||||
ru_isrss: 0 as libc::c_long,
|
ru_isrss: 0 as libc::c_long,
|
||||||
ru_minflt: 0 as libc::c_long,
|
ru_minflt: 0 as libc::c_long,
|
||||||
ru_majflt: 0 as libc::c_long,
|
ru_majflt: 0 as libc::c_long,
|
||||||
ru_nswap: 0 as libc::c_long,
|
ru_nswap: 0 as libc::c_long,
|
||||||
ru_inblock: 0 as libc::c_long,
|
ru_inblock: 0 as libc::c_long,
|
||||||
ru_oublock: 0 as libc::c_long,
|
ru_oublock: 0 as libc::c_long,
|
||||||
ru_msgsnd: 0 as libc::c_long,
|
ru_msgsnd: 0 as libc::c_long,
|
||||||
ru_msgrcv: 0 as libc::c_long,
|
ru_msgrcv: 0 as libc::c_long,
|
||||||
ru_nsignals: 0 as libc::c_long,
|
ru_nsignals: 0 as libc::c_long,
|
||||||
ru_nvcsw: 0 as libc::c_long,
|
ru_nvcsw: 0 as libc::c_long,
|
||||||
ru_nivcsw: 0 as libc::c_long,
|
ru_nivcsw: 0 as libc::c_long,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 由于评测系统本身异常而产生的错误
|
/// 由于评测系统本身异常而产生的错误
|
||||||
@ -446,65 +446,65 @@ fn new_rusage() -> libc::rusage {
|
|||||||
/// 因为正常程序返回 `signal` 不能为负数,因此此处使用负数的 `signal` 标识系统错误
|
/// 因为正常程序返回 `signal` 不能为负数,因此此处使用负数的 `signal` 标识系统错误
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn judge_system_error(errmsg: String) -> RunnerStatus {
|
fn judge_system_error(errmsg: String) -> RunnerStatus {
|
||||||
RunnerStatus {
|
RunnerStatus {
|
||||||
rusage: new_rusage(),
|
rusage: new_rusage(),
|
||||||
exit_code: -1,
|
exit_code: -1,
|
||||||
status: -1,
|
status: -1,
|
||||||
signal: -1,
|
signal: -1,
|
||||||
time_used: -1,
|
time_used: -1,
|
||||||
memory_used: -1,
|
memory_used: -1,
|
||||||
cgroup_memory_used: -1,
|
cgroup_memory_used: -1,
|
||||||
real_time_used: 0,
|
real_time_used: 0,
|
||||||
errmsg: errmsg,
|
errmsg: errmsg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use tempfile::tempdir_in;
|
use tempfile::tempdir_in;
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_echo() {
|
async fn test_echo() {
|
||||||
let pwd = tempdir_in("/tmp").unwrap();
|
let pwd = tempdir_in("/tmp").unwrap();
|
||||||
println!("{:?}", pwd);
|
println!("{:?}", pwd);
|
||||||
let process = Process::new(
|
let process = Process::new(
|
||||||
String::from("/bin/echo Hello World!"),
|
String::from("/bin/echo Hello World!"),
|
||||||
1000,
|
1000,
|
||||||
65535,
|
65535,
|
||||||
pwd.path().to_path_buf(),
|
pwd.path().to_path_buf(),
|
||||||
);
|
);
|
||||||
let result = Runner::from(process).unwrap().await.unwrap();
|
let result = Runner::from(process).unwrap().await.unwrap();
|
||||||
assert_eq!(result.exit_code, 0);
|
assert_eq!(result.exit_code, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_sleep() {
|
async fn test_sleep() {
|
||||||
let pwd = tempdir_in("/tmp").unwrap();
|
let pwd = tempdir_in("/tmp").unwrap();
|
||||||
println!("{:?}", pwd);
|
println!("{:?}", pwd);
|
||||||
let process = Process::new(
|
let process = Process::new(
|
||||||
String::from("/bin/sleep 1"),
|
String::from("/bin/sleep 1"),
|
||||||
2000,
|
2000,
|
||||||
65535,
|
65535,
|
||||||
pwd.path().to_path_buf(),
|
pwd.path().to_path_buf(),
|
||||||
);
|
);
|
||||||
let result = Runner::from(process).unwrap().await.unwrap();
|
let result = Runner::from(process).unwrap().await.unwrap();
|
||||||
assert!(result.real_time_used > 1000);
|
assert!(result.real_time_used > 1000);
|
||||||
assert!(result.real_time_used < 1500);
|
assert!(result.real_time_used < 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_output() {
|
async fn test_output() {
|
||||||
let pwd = tempdir_in("/tmp").unwrap();
|
let pwd = tempdir_in("/tmp").unwrap();
|
||||||
println!("{:?}", pwd);
|
println!("{:?}", pwd);
|
||||||
let process = Process::new(
|
let process = Process::new(
|
||||||
String::from("/bin/echo Hello World!"),
|
String::from("/bin/echo Hello World!"),
|
||||||
1000,
|
1000,
|
||||||
65535,
|
65535,
|
||||||
pwd.path().to_path_buf(),
|
pwd.path().to_path_buf(),
|
||||||
);
|
);
|
||||||
let runner = Runner::from(process).unwrap();
|
let runner = Runner::from(process).unwrap();
|
||||||
let _ = runner.await.unwrap();
|
let _ = runner.await.unwrap();
|
||||||
let out = std::fs::read_to_string(pwd.path().join(STDOUT_FILENAME)).unwrap();
|
let out = std::fs::read_to_string(pwd.path().join(STDOUT_FILENAME)).unwrap();
|
||||||
assert_eq!(out, "Hello World!\n");
|
assert_eq!(out, "Hello World!\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1315
src/seccomp.rs
1315
src/seccomp.rs
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user