Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Trace 32bit execs in ptrace backend #45

Merged
merged 6 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.80"
toolchain: "1.82"
targets: ${{ matrix.target }}
components: rustfmt
- name: Install cross-compilation tools
Expand Down
11 changes: 11 additions & 0 deletions src/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,14 @@ cfg_if! {
compile_error!("unsupported architecture");
}
}

#[allow(unused)]
mod audit {
include!("arch/audit.rs");
}

pub use audit::*;

pub trait RegsExt {
fn syscall_arg(&self, idx: usize, is_32bit: bool) -> usize;
}
54 changes: 20 additions & 34 deletions src/arch/aarch64.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,26 @@
use nix::libc::user_regs_struct;
use super::RegsExt;

pub type PtraceRegisters = user_regs_struct;
pub const NATIVE_AUDIT_ARCH: u32 = super::AUDIT_ARCH_AARCH64;
pub const HAS_32BIT: bool = false;

macro_rules! syscall_no_from_regs {
($regs:ident) => {
$regs.regs[8] as i64
};
pub type Regs = user_regs_struct;
pub type RegsPayload = Regs;
#[repr(transparent)]
pub struct RegsRepr {
pub payload: RegsPayload,
}

macro_rules! syscall_res_from_regs {
($regs:ident) => {
$regs.regs[0] as i64
};
impl RegsExt for Regs {
fn syscall_arg(&self, idx: usize, _is_32bit: bool) -> usize {
(match idx {
0 => self.regs[0],
1 => self.regs[1],
2 => self.regs[2],
3 => self.regs[3],
4 => self.regs[4],
5 => self.regs[5],
_ => unimplemented!(),
} as usize)
}
}

macro_rules! syscall_arg {
($regs:ident, 0) => {
$regs.regs[0]
};
($regs:ident, 1) => {
$regs.regs[1]
};
($regs:ident, 2) => {
$regs.regs[2]
};
($regs:ident, 3) => {
$regs.regs[3]
};
($regs:ident, 4) => {
$regs.regs[4]
};
($regs:ident, 5) => {
$regs.regs[5]
};
}

pub(crate) use syscall_arg;
pub(crate) use syscall_no_from_regs;
pub(crate) use syscall_res_from_regs;
55 changes: 55 additions & 0 deletions src/arch/audit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* automatically generated by rust-bindgen 0.70.1 */

pub const AUDIT_ARCH_AARCH64: u32 = 3221225655;
pub const AUDIT_ARCH_ALPHA: u32 = 3221262374;
pub const AUDIT_ARCH_ARCOMPACT: u32 = 1073741917;
pub const AUDIT_ARCH_ARCOMPACTBE: u32 = 93;
pub const AUDIT_ARCH_ARCV2: u32 = 1073742019;
pub const AUDIT_ARCH_ARCV2BE: u32 = 195;
pub const AUDIT_ARCH_ARM: u32 = 1073741864;
pub const AUDIT_ARCH_ARMEB: u32 = 40;
pub const AUDIT_ARCH_C6X: u32 = 1073741964;
pub const AUDIT_ARCH_C6XBE: u32 = 140;
pub const AUDIT_ARCH_CRIS: u32 = 1073741900;
pub const AUDIT_ARCH_CSKY: u32 = 1073742076;
pub const AUDIT_ARCH_FRV: u32 = 21569;
pub const AUDIT_ARCH_H8300: u32 = 46;
pub const AUDIT_ARCH_HEXAGON: u32 = 164;
pub const AUDIT_ARCH_I386: u32 = 1073741827;
pub const AUDIT_ARCH_IA64: u32 = 3221225522;
pub const AUDIT_ARCH_M32R: u32 = 88;
pub const AUDIT_ARCH_M68K: u32 = 4;
pub const AUDIT_ARCH_MICROBLAZE: u32 = 189;
pub const AUDIT_ARCH_MIPS: u32 = 8;
pub const AUDIT_ARCH_MIPSEL: u32 = 1073741832;
pub const AUDIT_ARCH_MIPS64: u32 = 2147483656;
pub const AUDIT_ARCH_MIPS64N32: u32 = 2684354568;
pub const AUDIT_ARCH_MIPSEL64: u32 = 3221225480;
pub const AUDIT_ARCH_MIPSEL64N32: u32 = 3758096392;
pub const AUDIT_ARCH_NDS32: u32 = 1073741991;
pub const AUDIT_ARCH_NDS32BE: u32 = 167;
pub const AUDIT_ARCH_NIOS2: u32 = 1073741937;
pub const AUDIT_ARCH_OPENRISC: u32 = 92;
pub const AUDIT_ARCH_PARISC: u32 = 15;
pub const AUDIT_ARCH_PARISC64: u32 = 2147483663;
pub const AUDIT_ARCH_PPC: u32 = 20;
pub const AUDIT_ARCH_PPC64: u32 = 2147483669;
pub const AUDIT_ARCH_PPC64LE: u32 = 3221225493;
pub const AUDIT_ARCH_RISCV32: u32 = 1073742067;
pub const AUDIT_ARCH_RISCV64: u32 = 3221225715;
pub const AUDIT_ARCH_S390: u32 = 22;
pub const AUDIT_ARCH_S390X: u32 = 2147483670;
pub const AUDIT_ARCH_SH: u32 = 42;
pub const AUDIT_ARCH_SHEL: u32 = 1073741866;
pub const AUDIT_ARCH_SH64: u32 = 2147483690;
pub const AUDIT_ARCH_SHEL64: u32 = 3221225514;
pub const AUDIT_ARCH_SPARC: u32 = 2;
pub const AUDIT_ARCH_SPARC64: u32 = 2147483691;
pub const AUDIT_ARCH_TILEGX: u32 = 3221225663;
pub const AUDIT_ARCH_TILEGX32: u32 = 1073742015;
pub const AUDIT_ARCH_TILEPRO: u32 = 1073742012;
pub const AUDIT_ARCH_UNICORE: u32 = 1073741934;
pub const AUDIT_ARCH_X86_64: u32 = 3221225534;
pub const AUDIT_ARCH_XTENSA: u32 = 94;
pub const AUDIT_ARCH_LOONGARCH32: u32 = 1073742082;
pub const AUDIT_ARCH_LOONGARCH64: u32 = 3221225730;
2 changes: 2 additions & 0 deletions src/arch/generate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
bindgen --allowlist-var 'AUDIT_ARCH_.*' /usr/include/linux/audit.h > audit.rs
54 changes: 20 additions & 34 deletions src/arch/riscv64.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,26 @@
use super::RegsExt;
use nix::libc::user_regs_struct;

pub type PtraceRegisters = user_regs_struct;
pub const NATIVE_AUDIT_ARCH: u32 = super::AUDIT_ARCH_RISCV64;
pub const HAS_32BIT: bool = false;

macro_rules! syscall_no_from_regs {
($regs:ident) => {
$regs.a7 as i64
};
pub type Regs = user_regs_struct;
pub type RegsPayload = Regs;
#[repr(transparent)]
pub struct RegsRepr {
pub payload: RegsPayload,
}

macro_rules! syscall_res_from_regs {
($regs:ident) => {
$regs.a0 as i64
};
impl RegsExt for Regs {
fn syscall_arg(&self, idx: usize, _is_32bit: bool) -> usize {
(match idx {
0 => self.a0,
1 => self.a1,
2 => self.a2,
3 => self.a3,
4 => self.a4,
5 => self.a5,
_ => unimplemented!(),
} as usize)
}
}

macro_rules! syscall_arg {
($regs:ident, 0) => {
$regs.a0
};
($regs:ident, 1) => {
$regs.a1
};
($regs:ident, 2) => {
$regs.a2
};
($regs:ident, 3) => {
$regs.a3
};
($regs:ident, 4) => {
$regs.a4
};
($regs:ident, 5) => {
$regs.a5
};
}

pub(crate) use syscall_arg;
pub(crate) use syscall_no_from_regs;
pub(crate) use syscall_res_from_regs;
123 changes: 92 additions & 31 deletions src/arch/x86_64.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,101 @@
use nix::libc::user_regs_struct;

pub type PtraceRegisters = user_regs_struct;
pub const NATIVE_AUDIT_ARCH: u32 = super::AUDIT_ARCH_X86_64;
pub const SYS_EXECVE_32: i32 = 11;
pub const SYS_EXECVEAT_32: i32 = 358;
pub const HAS_32BIT: bool = true;

macro_rules! syscall_no_from_regs {
($regs:ident) => {
$regs.orig_rax as i64
};
// https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md
#[repr(C, u32)]
#[derive(Debug)]
#[expect(dead_code)] // Variants are constructed in unsafe code
pub enum Regs {
X86(PtraceRegisters32),
X64(PtraceRegisters64),
}

macro_rules! syscall_res_from_regs {
($regs:ident) => {
$regs.rax as i64
};
#[repr(u32)]
pub enum RegsTag {
X86,
X64,
}

macro_rules! syscall_arg {
($regs:ident, 0) => {
$regs.rdi
};
($regs:ident, 1) => {
$regs.rsi
};
($regs:ident, 2) => {
$regs.rdx
};
($regs:ident, 3) => {
$regs.r10
};
($regs:ident, 4) => {
$regs.r8
};
($regs:ident, 5) => {
$regs.r9
};
#[repr(C)]
pub union RegsPayload {
x86: PtraceRegisters32,
x64: PtraceRegisters64,
}

pub(crate) use syscall_arg;
pub(crate) use syscall_no_from_regs;
pub(crate) use syscall_res_from_regs;
#[repr(C)]
pub struct RegsRepr {
pub tag: RegsTag,
pub payload: RegsPayload,
}

pub type PtraceRegisters64 = user_regs_struct;

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct PtraceRegisters32 {
ebx: u32,
ecx: u32,
edx: u32,
esi: u32,
edi: u32,
ebp: u32,
eax: u32,
xds: u32,
xes: u32,
xfs: u32,
xgs: u32,
orig_eax: u32,
eip: u32,
xcs: u32,
eflags: u32,
esp: u32,
xss: u32,
}

use super::RegsExt;

impl RegsExt for Regs {
fn syscall_arg(&self, idx: usize, is_32bit: bool) -> usize {
match self {
Self::X86(regs) => {
debug_assert!(is_32bit);
(match idx {
0 => regs.ebx,
1 => regs.ecx,
2 => regs.edx,
3 => regs.esi,
4 => regs.edi,
5 => unimplemented!(),
_ => unreachable!(),
} as usize)
}
Self::X64(regs) => {
if is_32bit {
(match idx {
0 => regs.rbx,
1 => regs.rcx,
2 => regs.rdx,
3 => regs.rsi,
4 => regs.rdi,
5 => unimplemented!(),
_ => unreachable!(),
} as u32 as usize)
} else {
(match idx {
0 => regs.rdi,
1 => regs.rsi,
2 => regs.rdx,
3 => regs.r10,
4 => regs.r8,
5 => regs.r9,
_ => unreachable!(),
} as usize)
}
}
}
}
}
13 changes: 9 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,19 @@ async fn main() -> color_eyre::Result<()> {
} else {
None
};
// Seccomp-bpf ptrace behavior is changed on 4.8. I haven't tested on older kernels.
let min_support_kver = (4, 8);
if !is_current_kernel_greater_than(min_support_kver)? {
// PTRACE_GET_SYSCALL_INFO requires at least linux 5.3.
let min_support_kver = (5, 3);
if !is_current_kernel_ge(min_support_kver)? {
log::warn!(
"Current kernel version is not supported! Minimum supported kernel version is {}.{}.",
min_support_kver.0,
min_support_kver.1
);
eprintln!(
"Current kernel version is not supported! Minimum supported kernel version is {}.{}.",
min_support_kver.0,
min_support_kver.1
);
}
if !cli.no_profile {
match Config::load(cli.profile.clone()) {
Expand Down Expand Up @@ -353,7 +358,7 @@ async fn main() -> color_eyre::Result<()> {
Ok(())
}

fn is_current_kernel_greater_than(min_support: (u32, u32)) -> color_eyre::Result<bool> {
fn is_current_kernel_ge(min_support: (u32, u32)) -> color_eyre::Result<bool> {
let utsname = nix::sys::utsname::uname()?;
let kstr = utsname.release().as_bytes();
let pos = kstr.iter().position(|&c| c != b'.' && !c.is_ascii_digit());
Expand Down
18 changes: 15 additions & 3 deletions src/seccomp.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
use libseccomp::{ScmpAction, ScmpFilterContext};
use libseccomp::{ScmpAction, ScmpArch, ScmpFilterContext};

pub fn create_seccomp_filters() -> color_eyre::Result<ScmpFilterContext> {

pub fn load_seccomp_filters() -> color_eyre::Result<()> {
libseccomp::reset_global_state()?;
let mut filter = ScmpFilterContext::new_filter(ScmpAction::Allow)?;
filter.add_rule(ScmpAction::Trace(0), nix::libc::SYS_execve as i32)?;
filter.add_rule(ScmpAction::Trace(0), nix::libc::SYS_execveat as i32)?;
Ok(filter)
if cfg!(target_arch = "x86_64") {
let mut filter32 = ScmpFilterContext::new_filter(ScmpAction::Allow)?;
filter32.remove_arch(ScmpArch::native())?;
filter32.add_arch(ScmpArch::X86)?;
// libseccomp translates the syscall number for us.
filter32.add_rule(ScmpAction::Trace(0), nix::libc::SYS_execve as i32)?;
filter32.add_rule(ScmpAction::Trace(0), nix::libc::SYS_execveat as i32)?;
filter.merge(filter32)?;
}
filter.load()?;
Ok(())
}
Loading