Skip to content

Commit 74ed22c

Browse files
committed
feat: split syscall filter
1 parent 2feac01 commit 74ed22c

File tree

5 files changed

+157
-51
lines changed

5 files changed

+157
-51
lines changed

src/context/builder.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::context::{CatBoxCompileContext, CatBoxContext, CatBoxJudgeContext, CatBoxRunContext};
2-
use crate::syscall::SyscallFilter;
2+
use crate::syscall::{RestrictedSyscall, SyscallFilter};
33
use crate::utils::mount::MountPoint;
44
use crate::utils::{into_c_string, parse_env, GidType, MemoryLimitType, TimeLimitType, UidType};
55
use crate::{CatBox, CatBoxError, CatBoxOption};
@@ -22,6 +22,7 @@ pub struct CatBoxBuilder {
2222
memory_limit: Option<MemoryLimitType>,
2323
uid: Option<UidType>,
2424
gid: Option<GidType>,
25+
cwd: Option<PathBuf>,
2526
}
2627

2728
/// Build CatBox running option
@@ -42,6 +43,7 @@ impl CatBoxBuilder {
4243
memory_limit: None,
4344
uid: None,
4445
gid: None,
46+
cwd: None,
4547
}
4648
}
4749

@@ -93,6 +95,10 @@ impl CatBoxBuilder {
9395
if let Some(gid) = self.gid {
9496
option.gid = Gid::from(gid);
9597
}
98+
// Set default cwd
99+
if let Some(cwd) = &self.cwd {
100+
option.cwd = cwd.clone();
101+
}
96102
// Set default env
97103
for env_pair in self.env.iter() {
98104
option.env.push(env_pair.clone());
@@ -125,8 +131,8 @@ impl CatBoxBuilder {
125131
}
126132

127133
/// Set default force mode
128-
pub fn set_default_force(mut self, flag: Option<bool>) -> Self {
129-
self.force = flag;
134+
pub fn set_default_force(mut self, flag: bool) -> Self {
135+
self.force = Some(flag);
130136
self
131137
}
132138

@@ -152,6 +158,12 @@ impl CatBoxBuilder {
152158
self
153159
}
154160

161+
/// Set default cwd
162+
pub fn set_default_cwd(mut self, path: Option<PathBuf>) -> Self {
163+
self.cwd = path;
164+
self
165+
}
166+
155167
/// Parse default env list
156168
pub fn parse_env_list(mut self, list: Vec<String>) -> Result<Self, CatBoxError> {
157169
for env_var in list {
@@ -273,13 +285,29 @@ impl CatBoxOptionBuilder {
273285
}
274286
}
275287

276-
/// Set ptrace syscall filter
277-
pub fn set_ptrace(mut self, flag: bool) -> Self {
278-
if flag {
279-
self.option.ptrace = Some(SyscallFilter::default());
280-
} else {
281-
self.option.ptrace = None;
288+
/// Parse ptrace syscall filter
289+
pub fn parse_ptrace_presets(mut self, presets: Option<Vec<String>>) -> Result<Self, CatBoxError> {
290+
if let Some(presets) = presets {
291+
self.option.ptrace = SyscallFilter::parse_presets(presets)?;
282292
}
293+
Ok(self)
294+
}
295+
296+
/// Set ptrace feature
297+
pub fn ptrace(mut self, preset: RestrictedSyscall) -> Self {
298+
let mut filter = self
299+
.option
300+
.ptrace
301+
.get_or_insert(SyscallFilter::new())
302+
.to_owned();
303+
filter.enable(preset);
304+
self.option.ptrace = Some(filter);
305+
self
306+
}
307+
308+
/// Disable ptrace
309+
pub fn disable_ptrace(mut self) -> Self {
310+
self.option.ptrace = None;
283311
self
284312
}
285313

@@ -291,6 +319,14 @@ impl CatBoxOptionBuilder {
291319
self
292320
}
293321

322+
/// Set work directory in chroot or not
323+
pub fn set_cwd(mut self, path: Option<PathBuf>) -> Self {
324+
if let Some(path) = path {
325+
self.option.cwd = path;
326+
}
327+
self
328+
}
329+
294330
/// Set work directory in chroot
295331
pub fn cwd<P: Into<PathBuf>>(mut self, path: P) -> Self {
296332
self.option.cwd = path.into();

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
pub use catbox::run;
3737
pub use context::{CatBox, CatBoxBuilder, CatBoxOption, CatBoxOptionBuilder, CatBoxResult};
3838
pub use error::CatBoxError;
39+
pub use syscall::{RestrictedSyscall, SyscallFilter};
3940

4041
mod catbox;
4142
mod cgroup;

src/main.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::catbox::run;
1111
use crate::context::{CatBox, CatBoxBuilder, CatBoxOption};
1212
use crate::error::{CatBoxError, CatBoxExit};
1313
// use crate::preset::make_compile_params;
14-
use crate::utils::{default_format, MemoryLimitType, TimeLimitType};
14+
use crate::utils::{default_format, GidType, MemoryLimitType, TimeLimitType, UidType};
1515

1616
mod catbox;
1717
mod cgroup;
@@ -30,26 +30,29 @@ struct Cli {
3030
#[arg(long, requires = "report", help = "Output JSON format report")]
3131
json: bool,
3232

33-
#[arg(short, long, help = "Time limit (unit: ms)")]
33+
#[arg(short, long, help = "Time limit (unit: ms) [default: 1000]")]
3434
time: Option<TimeLimitType>,
3535

36-
#[arg(short, long, help = "Memory limit (unit: KB)")]
36+
#[arg(short, long, help = "Memory limit (unit: KB) [default: 262144]")]
3737
memory: Option<MemoryLimitType>,
3838

3939
#[arg(long, value_name = "KEY=VALUE", help = "Pass environment variables")]
4040
env: Vec<String>,
4141

42+
#[arg(long, help = "Current working directory [default: ./]")]
43+
cwd: Option<PathBuf>,
44+
4245
#[arg(long, help = "Child process uid")]
43-
uid: Option<u32>,
46+
uid: Option<UidType>,
4447

4548
#[arg(long, help = "Child process gid")]
46-
gid: Option<u32>,
49+
gid: Option<GidType>,
4750

48-
#[arg(long, help = "Run in current user")]
51+
#[arg(long, help = "Run in current user [default: false]")]
4952
user: bool,
5053

51-
#[arg(short, long, help = "Force security control")]
52-
force: Option<bool>,
54+
#[arg(short, long, help = "Force security control [default: false]")]
55+
force: bool,
5356

5457
#[structopt(subcommand)]
5558
command: Commands,
@@ -83,11 +86,15 @@ enum Commands {
8386
#[arg(long, help = "The number of processes")]
8487
process: Option<u64>,
8588

89+
#[arg(
90+
long,
91+
value_name = "PRESET",
92+
help = "Enable ptrace presets [support: none|net|process|all]"
93+
)]
94+
ptrace: Option<Vec<String>>,
95+
8696
#[arg(long, help = "Disable chroot")]
8797
no_chroot: bool,
88-
89-
#[arg(long, help = "Disable ptrace")]
90-
no_ptrace: bool,
9198
},
9299

93100
#[command(about = "Compile user code")]
@@ -139,6 +146,7 @@ impl Cli {
139146
.set_current_user(self.user)
140147
.set_default_uid(self.uid)
141148
.set_default_gid(self.gid)
149+
.set_default_cwd(self.cwd)
142150
.parse_env_list(self.env)?;
143151

144152
let catbox = match self.command {
@@ -151,16 +159,16 @@ impl Cli {
151159
read,
152160
write,
153161
process,
162+
ptrace,
154163
no_chroot,
155-
no_ptrace,
156164
} => builder
157165
.command(program, arguments)
158166
.set_process(process)
159167
.set_stdin(stdin)
160168
.set_stdout(stdout)
161169
.set_stderr(stderr)
162-
.set_ptrace(!no_ptrace)
163170
.set_chroot(!no_chroot)
171+
.parse_ptrace_presets(ptrace)?
164172
.parse_mount_read(read)?
165173
.parse_mount_write(write)?
166174
.done(),

src/syscall.rs

Lines changed: 88 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,60 +3,121 @@ use std::collections::HashMap;
33
use std::ffi::{c_long, c_ulonglong};
44
use std::fmt::{Debug, Formatter};
55

6+
use crate::CatBoxError;
67
use nix::libc::{
7-
user_regs_struct, SYS_accept, SYS_accept4, SYS_bind, SYS_clone, SYS_connect, SYS_execve,
8-
SYS_execveat, SYS_fork, SYS_getpeername, SYS_getsockname, SYS_getsockopt, SYS_listen,
8+
user_regs_struct, SYS_accept, SYS_accept4, SYS_bind, SYS_clone, SYS_clone3, SYS_connect,
9+
SYS_execve, SYS_execveat, SYS_fork, SYS_getpeername, SYS_getsockname, SYS_getsockopt, SYS_listen,
910
SYS_setsockopt, SYS_shutdown, SYS_socket, SYS_socketpair, SYS_vfork,
1011
};
1112
use nix::unistd::Pid;
1213

1314
type SyscallId = c_ulonglong;
1415

15-
/// 禁止系统调用
16-
/// 允许有限次系统调用
16+
/// Syscall permission
1717
#[derive(Clone)]
1818
pub enum SyscallPerm {
19+
/// Forbid all
1920
Forbid,
21+
/// Use a filter function to check whether it is ok
2022
FilterFn(fn(pid: &Pid, regs: &user_regs_struct) -> bool),
23+
/// Allow a few times
2124
Allow(i32),
2225
}
2326

24-
/// 系统调用过滤器
25-
/// 黑名单过滤,若不在映射内,则允许;否则,禁止或者允许有限次
27+
/// Syscall filter
28+
/// It is a black list filter, and it supports forbid syscall or allow a few times
2629
#[derive(Debug, Clone)]
2730
pub struct SyscallFilter {
2831
map: HashMap<SyscallId, SyscallPerm>,
2932
}
3033

34+
/// Syscall filter preset category
35+
#[derive(Debug, Copy, Clone)]
36+
pub enum RestrictedSyscall {
37+
Net,
38+
Process,
39+
Thread,
40+
}
41+
3142
impl SyscallFilter {
32-
pub fn default() -> Self {
33-
let mut filter = SyscallFilter {
43+
/// Create an empty syscall filter
44+
pub fn new() -> Self {
45+
let filter = SyscallFilter {
3446
map: HashMap::new(),
3547
};
36-
// 禁用网络
3748
filter
38-
.forbid(SYS_socket)
39-
.forbid(SYS_socketpair)
40-
.forbid(SYS_setsockopt)
41-
.forbid(SYS_getsockopt)
42-
.forbid(SYS_getsockname)
43-
.forbid(SYS_getpeername)
44-
.forbid(SYS_bind)
45-
.forbid(SYS_listen)
46-
.forbid(SYS_accept)
47-
.forbid(SYS_accept4)
48-
.forbid(SYS_connect)
49-
.forbid(SYS_shutdown);
50-
// 禁用进程相关
49+
}
50+
51+
/// Create a default syscall filter with all the presets open
52+
pub fn default() -> Self {
53+
let mut filter = Self::new();
5154
filter
52-
.allow(SYS_execve, 1)
53-
.allow(SYS_execveat, 1)
54-
.forbid(SYS_fork)
55-
.forbid(SYS_vfork)
56-
.forbid(SYS_clone);
55+
.enable(RestrictedSyscall::Net)
56+
.enable(RestrictedSyscall::Process);
5757
filter
5858
}
5959

60+
/// Enable preset
61+
pub fn enable(self: &mut Self, feature: RestrictedSyscall) -> &mut Self {
62+
match feature {
63+
RestrictedSyscall::Net => {
64+
self
65+
.forbid(SYS_socket)
66+
.forbid(SYS_socketpair)
67+
.forbid(SYS_setsockopt)
68+
.forbid(SYS_getsockopt)
69+
.forbid(SYS_getsockname)
70+
.forbid(SYS_getpeername)
71+
.forbid(SYS_bind)
72+
.forbid(SYS_listen)
73+
.forbid(SYS_accept)
74+
.forbid(SYS_accept4)
75+
.forbid(SYS_connect)
76+
.forbid(SYS_shutdown);
77+
}
78+
RestrictedSyscall::Process => {
79+
self
80+
.allow(SYS_execve, 1)
81+
.allow(SYS_execveat, 1)
82+
.forbid(SYS_fork)
83+
.forbid(SYS_vfork)
84+
.forbid(SYS_clone)
85+
.forbid(SYS_clone3);
86+
}
87+
RestrictedSyscall::Thread => {}
88+
};
89+
self
90+
}
91+
92+
/// Try parsing presets string
93+
pub fn parse_presets(presets: Vec<String>) -> Result<Option<Self>, CatBoxError> {
94+
let mut filter = Self::new();
95+
let presets = presets
96+
.into_iter()
97+
.flat_map(|str| str.split(" ").map(str::to_owned).collect::<Vec<_>>())
98+
.map(|p| p.trim().to_ascii_lowercase())
99+
.filter(|p| p.len() > 0)
100+
.collect::<Vec<String>>();
101+
for preset in presets {
102+
match preset.as_str() {
103+
"none" => return Ok(None),
104+
"net" | "network" => {
105+
filter.enable(RestrictedSyscall::Net);
106+
}
107+
"process" => {
108+
filter.enable(RestrictedSyscall::Process);
109+
}
110+
"all" => {
111+
filter
112+
.enable(RestrictedSyscall::Net)
113+
.enable(RestrictedSyscall::Process);
114+
}
115+
_ => return Err(CatBoxError::cli("Parse ptrace syscall filter string fails")),
116+
};
117+
}
118+
Ok(Some(filter))
119+
}
120+
60121
pub fn forbid(self: &mut Self, id: c_long) -> &mut Self {
61122
self.map.insert(id as SyscallId, SyscallPerm::forbid());
62123
self

tests/aplusb.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use catj::{run, CatBoxBuilder, CatBoxResult};
1+
use catj::{run, CatBoxBuilder, CatBoxResult, RestrictedSyscall};
22
use log::info;
33
use nix::sys::signal::Signal;
44
use std::env::current_dir;
@@ -35,7 +35,7 @@ fn compile_cpp(dir: &PathBuf, file: &String) -> String {
3535
.stdout("/dev/null")
3636
// .stderr("/dev/null")
3737
.current_user()
38-
.set_ptrace(false)
38+
.disable_ptrace()
3939
.process(10)
4040
.chroot()
4141
.cwd(current_dir().unwrap())

0 commit comments

Comments
 (0)