Skip to content

Commit 11e2610

Browse files
sat0kenCopilot
andauthored
Extend experiment seccomp program (#3464)
* update seccomp program of update method and add function - add default error return code to InstructionData - add action to Rule - add action to fn new of Rule and fix test code - add seccomp compare op code to const - ported function from libcontainer of seccomp - update Cargo.toml and lock - add const of seccomp flags - add flags to InstructionData - add derive - improve implementation to generate filter from LinuxSeccomp - update main.rs to use oci_spec - fix format Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * implementation From trait Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * implement seccomp flags if config.json define Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * add BPF Instruction to validate Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * add fn to get nr offset from seccomp_data Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * modify generate BPF program order Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * implement check args of seccomp program and add test code - modify systemcall of args check logic - add for test code and add serde to use json - update gen_validate - update seccomp_data_args_offset to get args index - add file for test - update check argument code - update check argument code - fix test code - remove unusual args from fn to_instruction_with_args - add test code - add test case with args - add test for arm64 Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * move test json file to tests Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * change to return Result Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * change fn seccomp_data_args_offset to return Result Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * to change return Result, add unwrap Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * run cargo fmt Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * fix offset size Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * add design pattern to rule Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * change method to return Result Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * change method name Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * improve multipul variable set to use is_none method Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * fix test code Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * insert BPF_JMP to new to omit code Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * omit BPM_JMP from code Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * add check argument count Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * fix if value is 0, set the value Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * fix for cargo clippy Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * change argument string to Path Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * change to return Result and remove unwrap Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * change struct name InstructionData to SeccompProgramPlan Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * implement TryFrom to SeccompProgramPlan Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * update oci-spec-rs Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * errno correctly set to the value of action Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * refactoring testutils.rs to use oci-spec-rs to parse json Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * refactor fn try_from to separate logic Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * fix test code and refactor Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * change type of syscall Vec<String> to Vec<u64> to sort Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * add const to jump_num Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * cargo fmt Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * minor update for test Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * create example dir and move main.rs Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * fix typo Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * update package and toolchain Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * cargo fmt Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * remove unused package Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * organize tests dir Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * update Cargo.toml Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * add readjson file by libseccomp Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * update README Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * cargo fmt fix warning Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> * fix typo Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> --------- Signed-off-by: sat0ken <15720506+sat0ken@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent ddd6abb commit 11e2610

File tree

15 files changed

+3766
-161
lines changed

15 files changed

+3766
-161
lines changed

experiment/seccomp/Cargo.lock

Lines changed: 611 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

experiment/seccomp/Cargo.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ nix = { version = "0.29.0", features = [
2222
thiserror = "1.0.57"
2323
prctl = "1.0.0"
2424
anyhow = "1.0"
25-
tokio = { version = "1", features = ["full"] }
2625
syscall-numbers = "3.1.1"
27-
syscalls = { version = "0.6.18", features = ["std", "serde", "aarch64", "x86_64"]}
26+
syscalls = { version = "0.6.18", features = ["std", "serde", "aarch64", "x86_64"]}
27+
oci-spec = { version = "0.9.0", features = ["runtime"] }
28+
serde_json = "1.0.138"
29+
derive_builder = "0.20.2"
30+
31+
[dev-dependencies]
32+
tokio = { version = "1", features = ["full"] }
33+
libseccomp = "0.4.0"
34+
tempfile = "3.27.0"

experiment/seccomp/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,9 @@ This is an experimental project in order to get away from libseccomp.
22
Ref: https://github.com/youki-dev/youki/issues/2724
33

44
```console
5-
$ cargo run
5+
# apply sample seccomp filter
6+
$ cargo test --test filter -- --show-output
7+
8+
# output bpf instruction
9+
$ cargo test --test readjson -- --show-output
610
```
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[toolchain]
22
profile="default"
3-
channel="1.77.1"
3+
channel="1.92.0"
Lines changed: 87 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,55 @@
1-
use crate::instruction::Instruction;
2-
use crate::instruction::*;
1+
use std::os::raw::c_uchar;
32

4-
#[derive(PartialEq, Debug)]
3+
use nix::errno::Errno::ENOSYS;
4+
5+
use crate::instruction::{Instruction, *};
6+
7+
#[derive(PartialEq, Debug, Default)]
58
pub enum Arch {
6-
X86,AArch64
9+
#[default]
10+
X86,
11+
AArch64,
712
}
813

9-
pub fn gen_validate(arc: &Arch) -> Vec<Instruction> {
14+
pub fn gen_validate(arc: &Arch, def_action: u32, jump_num: usize) -> Vec<Instruction> {
1015
let arch = match arc {
1116
Arch::X86 => AUDIT_ARCH_X86_64,
12-
Arch::AArch64 => AUDIT_ARCH_AARCH64
17+
Arch::AArch64 => AUDIT_ARCH_AARCH64,
1318
};
1419

15-
vec![
16-
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_arch_offset() as u32),
17-
Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, arch),
18-
Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
19-
]
20+
if jump_num <= BPF_JMP_MAX {
21+
vec![
22+
// load offset architecture
23+
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_arch_offset() as u32),
24+
// if not match architecture, jump to default action
25+
Instruction::jump(
26+
BPF_JMP | BPF_JEQ | BPF_K,
27+
0,
28+
(jump_num + 3) as c_uchar,
29+
arch,
30+
),
31+
// load offset system call number
32+
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_nr_offset() as u32),
33+
// check system call is not using 32bit ABI
34+
// see https://github.com/elastic/go-seccomp-bpf/blob/main/filter.go#L231
35+
Instruction::jump(BPF_JMP | BPF_JGE | BPF_K, 0, 1, X32_SYSCALL_BIT),
36+
Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS as u32),
37+
]
38+
} else {
39+
vec![
40+
// load offset architecture
41+
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_arch_offset() as u32),
42+
// if not match architecture, jump to default action
43+
Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 1, 0 as c_uchar, arch),
44+
Instruction::stmt(BPF_RET | BPF_K, def_action),
45+
// load offset system call number
46+
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_nr_offset() as u32),
47+
// check system call is not using 32bit ABI
48+
// see https://github.com/elastic/go-seccomp-bpf/blob/main/filter.go#L231
49+
Instruction::jump(BPF_JMP | BPF_JGE | BPF_K, 0, 1, X32_SYSCALL_BIT),
50+
Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS as u32),
51+
]
52+
}
2053
}
2154

2255
#[cfg(test)]
@@ -25,17 +58,51 @@ mod tests {
2558

2659
#[test]
2760
fn test_gen_validate_x86() {
28-
let bpf_prog = gen_validate(&Arch::X86);
29-
assert_eq!(bpf_prog[0], Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_arch_offset() as u32));
30-
assert_eq!(bpf_prog[1], Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, AUDIT_ARCH_X86_64));
31-
assert_eq!(bpf_prog[2], Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS));
61+
let bpf_prog = gen_validate(&Arch::X86, SECCOMP_RET_KILL_PROCESS, 3);
62+
assert_eq!(
63+
bpf_prog[0],
64+
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_arch_offset() as u32)
65+
);
66+
assert_eq!(
67+
bpf_prog[1],
68+
Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 6, AUDIT_ARCH_X86_64)
69+
);
70+
assert_eq!(
71+
bpf_prog[2],
72+
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_nr_offset() as u32)
73+
);
74+
assert_eq!(
75+
bpf_prog[3],
76+
Instruction::jump(BPF_JMP | BPF_JGE | BPF_K, 0, 1, X32_SYSCALL_BIT)
77+
);
78+
assert_eq!(
79+
bpf_prog[4],
80+
Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS as u32)
81+
);
3282
}
3383

3484
#[test]
3585
fn test_gen_validate_aarch64() {
36-
let bpf_prog = gen_validate(&Arch::AArch64);
37-
assert_eq!(bpf_prog[0], Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_arch_offset() as u32));
38-
assert_eq!(bpf_prog[1], Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, AUDIT_ARCH_AARCH64));
39-
assert_eq!(bpf_prog[2], Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS));
86+
let bpf_prog = gen_validate(&Arch::AArch64, SECCOMP_RET_KILL_PROCESS, 3);
87+
assert_eq!(
88+
bpf_prog[0],
89+
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_arch_offset() as u32)
90+
);
91+
assert_eq!(
92+
bpf_prog[1],
93+
Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 0, 6, AUDIT_ARCH_AARCH64)
94+
);
95+
assert_eq!(
96+
bpf_prog[2],
97+
Instruction::stmt(BPF_LD | BPF_W | BPF_ABS, seccomp_data_nr_offset() as u32)
98+
);
99+
assert_eq!(
100+
bpf_prog[3],
101+
Instruction::jump(BPF_JMP | BPF_JGE | BPF_K, 0, 1, X32_SYSCALL_BIT)
102+
);
103+
assert_eq!(
104+
bpf_prog[4],
105+
Instruction::stmt(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS as u32)
106+
);
40107
}
41-
}
108+
}

experiment/seccomp/src/instruction/consts.rs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::{mem::offset_of, os::raw::c_int};
1+
use std::mem::offset_of;
2+
use std::os::raw::c_int;
3+
4+
use crate::seccomp::SeccompError;
25

36
// BPF Instruction classes.
47
// See /usr/include/linux/bpf_common.h .
@@ -30,9 +33,14 @@ pub const BPF_JA: u16 = 0x00;
3033
pub const BPF_JEQ: u16 = 0x10;
3134
pub const BPF_JGT: u16 = 0x20;
3235
pub const BPF_JGE: u16 = 0x30;
36+
pub const BPF_JSET: u16 = 0x40;
3337
// Test against the value in the K register.
3438
pub const BPF_K: u16 = 0x00;
3539

40+
// Limitation on the number of jumps for the bpf instruction
41+
// https://github.com/seccomp/libseccomp/blob/main/src/gen_bpf.c#L104
42+
pub const BPF_JMP_MAX: usize = 255;
43+
3644
// Return codes for BPF programs.
3745
// See /usr/include/linux/seccomp.h .
3846
pub const SECCOMP_RET_ALLOW: u32 = 0x7fff_0000;
@@ -50,6 +58,22 @@ pub const SECCOMP_RET_USER_NOTIF: u32 = 0x7fc00000;
5058
pub const AUDIT_ARCH_X86_64: u32 = 62 | 0x8000_0000 | 0x4000_0000;
5159
pub const AUDIT_ARCH_AARCH64: u32 = 183 | 0x8000_0000 | 0x4000_0000;
5260

61+
// See /arch/x86/include/uapi/asm/unistd.h
62+
pub const X32_SYSCALL_BIT: u32 = 0x4000_0000;
63+
64+
// Comparison operators
65+
// See libseccomp/include/seccomp.h.in
66+
#[derive(Debug, PartialEq, Clone)]
67+
pub enum SeccompCompareOp {
68+
NotEqual = 1,
69+
LessThan,
70+
LessOrEqual,
71+
Equal,
72+
GreaterOrEqual,
73+
GreaterThan,
74+
MaskedEqual,
75+
}
76+
5377
// ```c
5478
// struct seccomp_data {
5579
// int nr;
@@ -67,6 +91,10 @@ struct SeccompData {
6791
args: [u64; 6],
6892
}
6993

94+
pub const fn seccomp_data_nr_offset() -> u8 {
95+
offset_of!(SeccompData, nr) as u8
96+
}
97+
7098
pub const fn seccomp_data_arch_offset() -> u8 {
7199
offset_of!(SeccompData, arch) as u8
72100
}
@@ -75,8 +103,11 @@ pub const fn seccomp_data_arg_size() -> u8 {
75103
8
76104
}
77105

78-
pub const fn seccomp_data_args_offset() -> u8 {
79-
offset_of!(SeccompData, args) as u8
106+
pub const fn seccomp_data_args_offset(index: u8) -> Result<u8, SeccompError> {
107+
match index {
108+
0..=5 => Ok((offset_of!(SeccompData, args) as u8) + (index * 8)),
109+
_ => Err(SeccompError::InvalidArgumentSize),
110+
}
80111
}
81112

82113
pub const SECCOMP_IOC_MAGIC: u8 = b'!';
@@ -102,7 +133,12 @@ mod tests {
102133
#[test]
103134
fn test_seccomp_data_args_offset() {
104135
if cfg!(target_arch = "x86_64") {
105-
assert_eq!(seccomp_data_args_offset(), 16);
136+
assert_eq!(seccomp_data_args_offset(0).unwrap(), 16);
137+
assert_eq!(seccomp_data_args_offset(1).unwrap(), 16 + 8);
138+
assert_eq!(seccomp_data_args_offset(2).unwrap(), 16 + 16);
139+
assert_eq!(seccomp_data_args_offset(3).unwrap(), 16 + 24);
140+
assert_eq!(seccomp_data_args_offset(4).unwrap(), 16 + 32);
141+
assert_eq!(seccomp_data_args_offset(5).unwrap(), 16 + 40);
106142
}
107143
}
108144
}

experiment/seccomp/src/instruction/inst.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::os::raw::{c_uchar, c_uint, c_ushort};
22

3+
use crate::instruction::BPF_JMP;
4+
35
// https://docs.kernel.org/networking/filter.html#structure
46
// <linux/filter.h>: sock_filter
57
#[repr(C)]
@@ -32,7 +34,7 @@ impl Instruction {
3234
jump_false: c_uchar,
3335
multiuse_field: c_uint,
3436
) -> Self {
35-
Self::new(code, jump_true, jump_false, multiuse_field)
37+
Self::new(BPF_JMP | code, jump_true, jump_false, multiuse_field)
3638
}
3739

3840
pub fn stmt(code: c_ushort, k: c_uint) -> Self {
@@ -57,12 +59,12 @@ mod tests {
5759
}
5860
);
5961
assert_eq!(
60-
Instruction::jump(BPF_JMP | BPF_JEQ | BPF_K, 10, 2, 5),
62+
Instruction::jump(BPF_JEQ | BPF_K, 10, 2, 5),
6163
Instruction {
6264
code: 0x15,
63-
offset_jump_true: 2,
64-
offset_jump_false: 5,
65-
multiuse_field: 10,
65+
offset_jump_true: 10,
66+
offset_jump_false: 2,
67+
multiuse_field: 5,
6668
}
6769
);
6870
}

experiment/seccomp/src/instruction/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ mod arch;
22
mod consts;
33
mod inst;
44

5-
pub use arch::{gen_validate, Arch};
5+
pub use arch::{Arch, gen_validate};
66
pub use consts::*;
77
pub use inst::Instruction;

0 commit comments

Comments
 (0)