use libc::{dup3, O_CLOEXEC};
use libseccomp::*;
use std::thread;
macro_rules! skip_if_not_supported {
() => {
if !check_api(6, ScmpVersion::from((2, 5, 0))).unwrap() {
println!("Skip tests for userspace notification");
return;
}
};
}
#[derive(Debug)]
struct TestData {
syscall: ScmpSyscall,
args: Vec<u64>,
arch: ScmpArch,
resp_val: i64,
resp_err: i32,
resp_flags: u32,
expected_val: i64,
}
#[test]
fn test_user_notification() {
skip_if_not_supported!();
let mut ctx = ScmpFilterContext::new(ScmpAction::Allow).unwrap();
let syscall = ScmpSyscall::from_name("dup3").unwrap();
let arch = ScmpArch::native();
ctx.add_arch(arch).unwrap();
ctx.add_rule(ScmpAction::Notify, syscall).unwrap();
let tests = &[
TestData {
syscall,
args: vec![0, 100, O_CLOEXEC as u64],
arch,
resp_val: 10,
resp_err: 0,
resp_flags: 0,
expected_val: 10,
},
TestData {
syscall,
args: vec![0, 100, O_CLOEXEC as u64],
arch,
resp_val: 0,
resp_err: -1,
resp_flags: 0,
expected_val: -1,
},
TestData {
syscall,
args: vec![0, 100, O_CLOEXEC as u64],
arch,
resp_val: 0,
resp_err: 0,
resp_flags: ScmpNotifRespFlags::CONTINUE.bits(),
expected_val: 100,
},
];
ctx.load().unwrap();
let fd = ctx.get_notify_fd().unwrap();
let mut handlers = vec![];
for test in tests.iter() {
let args: (i32, i32, i32) = (
test.args[0] as i32,
test.args[1] as i32,
test.args[2] as i32,
);
handlers.push(thread::spawn(move || unsafe {
dup3(args.0, args.1, args.2)
}));
let req = ScmpNotifReq::receive(fd).unwrap();
assert_eq!(req.data.arch, test.arch);
assert_eq!(req.data.syscall, test.syscall);
for (i, test_val) in test.args.iter().enumerate() {
assert_eq!(&req.data.args[i], test_val);
}
assert!(notify_id_valid(fd, req.id).is_ok());
let resp = ScmpNotifResp::new(req.id, test.resp_val, test.resp_err, test.resp_flags);
resp.respond(fd).unwrap();
}
for (i, handler) in handlers.into_iter().enumerate() {
let ret_val = handler.join().unwrap();
assert_eq!(tests[i].expected_val as i32, ret_val);
}
}
#[test]
fn test_resp_new() {
assert_eq!(
ScmpNotifResp::new_val(1234, 1, ScmpNotifRespFlags::empty()),
ScmpNotifResp::new(1234, 1, 0, 0),
);
assert_eq!(
ScmpNotifResp::new_error(1234, -2, ScmpNotifRespFlags::empty()),
ScmpNotifResp::new(1234, 0, -2, 0),
);
assert_eq!(
ScmpNotifResp::new_continue(1234, ScmpNotifRespFlags::empty()),
ScmpNotifResp::new(1234, 0, 0, ScmpNotifRespFlags::CONTINUE.bits()),
);
}
#[test]
fn test_error() {
skip_if_not_supported!();
let ctx = ScmpFilterContext::new(ScmpAction::Allow).unwrap();
let resp = ScmpNotifResp::new(0, 0, 0, 0);
assert!(ctx.get_notify_fd().is_err());
assert!(ScmpNotifReq::receive(0).is_err());
assert!(resp.respond(0).is_err());
assert!(notify_id_valid(0, 0).is_err());
}