Skip to content

Commit

Permalink
Add syscall instruction to verifier
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasSte committed Oct 18, 2024
1 parent b3d4cdf commit 9bd8ab6
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ impl<C: ContextObject> Executable<C> {
self.get_config(),
self.get_sbpf_version(),
self.get_function_registry(),
self.loader.get_sparse_function_registry(),
self.loader.get_dense_function_registry(),
)?;
Ok(())
}
Expand Down
7 changes: 6 additions & 1 deletion src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,16 @@ impl<C: ContextObject> BuiltinProgram<C> {
self.config.as_ref().unwrap()
}

/// Get the function registry
/// Get the sparse function registry
pub fn get_sparse_function_registry(&self) -> &FunctionRegistry<BuiltinFunction<C>> {
&self.sparse_registry
}

/// Get the dense function registry
pub fn get_dense_function_registry(&self) -> &FunctionRegistry<BuiltinFunction<C>> {
&self.dense_registry
}

/// Calculate memory size
pub fn mem_size(&self) -> usize {
std::mem::size_of::<Self>()
Expand Down
22 changes: 18 additions & 4 deletions src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ pub enum VerifierError {
/// Invalid function
#[error("Invalid function at instruction {0}")]
InvalidFunction(usize),
/// Invalid syscall
#[error("Invalid syscall code {0}")]
InvalidSyscall(u32),
}

/// eBPF Verifier
Expand Down Expand Up @@ -157,6 +160,7 @@ fn check_jmp_offset(
fn check_call_target<T>(
key: u32,
function_registry: &FunctionRegistry<T>,
error: VerifierError,
) -> Result<(), VerifierError>
where
T: Copy,
Expand All @@ -165,7 +169,7 @@ where
function_registry
.lookup_by_key(key)
.map(|_| ())
.ok_or(VerifierError::InvalidFunction(key as usize))
.ok_or(error)
}

fn check_registers(
Expand Down Expand Up @@ -386,13 +390,23 @@ impl Verifier for RequisiteVerifier {
ebpf::JSLT_REG => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
ebpf::JSLE_IMM => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
ebpf::JSLE_REG => { check_jmp_offset(prog, insn_ptr, &function_range)?; },
ebpf::CALL_IMM if sbpf_version.static_syscalls() && insn.src != 0 => { check_call_target(insn.imm as u32, function_registry)?; },
ebpf::CALL_IMM if sbpf_version.static_syscalls() && insn.src == 0 => { check_call_target(insn.imm as u32, syscall_registry)?; },
ebpf::CALL_IMM if sbpf_version.static_syscalls() => {
check_call_target(
insn.imm as u32,
function_registry,
VerifierError::InvalidFunction(insn.imm as usize)
)?;
},
ebpf::CALL_IMM => {},
ebpf::CALL_REG => { check_callx_register(&insn, insn_ptr, sbpf_version)?; },
ebpf::EXIT if !sbpf_version.static_syscalls() => {},
ebpf::RETURN if sbpf_version.static_syscalls() => {},
ebpf::SYSCALL if sbpf_version.static_syscalls() => {},
ebpf::SYSCALL if sbpf_version.static_syscalls() => {
check_call_target(
insn.imm as u32,
syscall_registry,
VerifierError::InvalidSyscall(insn.imm as u32))?;
},

_ => {
return Err(VerifierError::UnknownOpCode(insn.opc, insn_ptr));
Expand Down
67 changes: 66 additions & 1 deletion tests/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1999,6 +1999,10 @@ fn test_stack1() {

#[test]
fn test_stack2() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
stb [r10-4], 0x01
Expand All @@ -2017,6 +2021,7 @@ fn test_stack2() {
syscall bpf_gather_bytes
xor r0, 0x2a2a2a2a
exit",
config,
[],
(
"bpf_mem_frob" => syscalls::SyscallMemFrob::vm,
Expand All @@ -2029,6 +2034,10 @@ fn test_stack2() {

#[test]
fn test_string_stack() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov r1, 0x78636261
Expand Down Expand Up @@ -2059,6 +2068,7 @@ fn test_string_stack() {
jeq r1, r6, +1
mov r0, 0x0
exit",
config,
[],
(
"bpf_str_cmp" => syscalls::SyscallStrCmp::vm,
Expand Down Expand Up @@ -2367,6 +2377,10 @@ fn test_bpf_to_bpf_scratch_registers() {

#[test]
fn test_syscall_parameter_on_stack() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov64 r1, r10
Expand All @@ -2375,6 +2389,7 @@ fn test_syscall_parameter_on_stack() {
syscall bpf_syscall_string
mov64 r0, 0x0
exit",
config,
[],
(
"bpf_syscall_string" => syscalls::SyscallString::vm,
Expand Down Expand Up @@ -2559,12 +2574,17 @@ fn test_call_save() {

#[test]
fn test_err_syscall_string() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov64 r1, 0x0
syscall bpf_syscall_string
mov64 r0, 0x0
exit",
config,
[72, 101, 108, 108, 111],
(
"bpf_syscall_string" => syscalls::SyscallString::vm,
Expand All @@ -2576,12 +2596,17 @@ fn test_err_syscall_string() {

#[test]
fn test_syscall_string() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov64 r2, 0x5
syscall bpf_syscall_string
mov64 r0, 0x0
exit",
config,
[72, 101, 108, 108, 111],
(
"bpf_syscall_string" => syscalls::SyscallString::vm,
Expand All @@ -2593,6 +2618,10 @@ fn test_syscall_string() {

#[test]
fn test_syscall() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov64 r1, 0xAA
Expand All @@ -2603,6 +2632,7 @@ fn test_syscall() {
syscall bpf_syscall_u64
mov64 r0, 0x0
exit",
config,
[],
(
"bpf_syscall_u64" => syscalls::SyscallU64::vm,
Expand All @@ -2614,6 +2644,10 @@ fn test_syscall() {

#[test]
fn test_call_gather_bytes() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov r1, 1
Expand All @@ -2623,6 +2657,7 @@ fn test_call_gather_bytes() {
mov r5, 5
syscall bpf_gather_bytes
exit",
config,
[],
(
"bpf_gather_bytes" => syscalls::SyscallGatherBytes::vm,
Expand All @@ -2634,6 +2669,10 @@ fn test_call_gather_bytes() {

#[test]
fn test_call_memfrob() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov r6, r1
Expand All @@ -2643,6 +2682,7 @@ fn test_call_memfrob() {
ldxdw r0, [r6]
be64 r0
exit",
config,
[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, //
],
Expand Down Expand Up @@ -2684,7 +2724,11 @@ declare_builtin_function!(
function_registry
.register_function_hashed(*b"nested_vm_syscall", SyscallNestedVm::vm)
.unwrap();
let loader = BuiltinProgram::new_loader(Config::default(), function_registry);
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
let loader = BuiltinProgram::new_loader(config, function_registry);
let mem = [depth as u8 - 1, throw as u8];
let mut executable = assemble::<TestContextObject>(
"
Expand Down Expand Up @@ -2780,12 +2824,17 @@ fn test_tight_infinite_recursion_callx() {

#[test]
fn test_instruction_count_syscall() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov64 r2, 0x5
syscall bpf_syscall_string
mov64 r0, 0x0
exit",
config,
[72, 101, 108, 108, 111],
(
"bpf_syscall_string" => syscalls::SyscallString::vm,
Expand All @@ -2797,12 +2846,17 @@ fn test_instruction_count_syscall() {

#[test]
fn test_err_instruction_count_syscall_capped() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov64 r2, 0x5
syscall bpf_syscall_string
mov64 r0, 0x0
exit",
config,
[72, 101, 108, 108, 111],
(
"bpf_syscall_string" => syscalls::SyscallString::vm,
Expand All @@ -2814,6 +2868,10 @@ fn test_err_instruction_count_syscall_capped() {

#[test]
fn test_err_non_terminate_capped() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov64 r6, 0x0
Expand All @@ -2826,6 +2884,7 @@ fn test_err_non_terminate_capped() {
add64 r6, 0x1
ja -0x8
exit",
config.clone(),
[],
(
"bpf_trace_printf" => syscalls::SyscallTracePrintf::vm,
Expand All @@ -2845,6 +2904,7 @@ fn test_err_non_terminate_capped() {
add64 r6, 0x1
ja -0x8
exit",
config,
[],
(
"bpf_trace_printf" => syscalls::SyscallTracePrintf::vm,
Expand Down Expand Up @@ -2952,6 +3012,10 @@ fn test_far_jumps() {

#[test]
fn test_symbol_relocation() {
let config = Config {
enabled_sbpf_versions: SBPFVersion::V1..=SBPFVersion::V1,
..Config::default()
};
test_interpreter_and_jit_asm!(
"
mov64 r1, r10
Expand All @@ -2960,6 +3024,7 @@ fn test_symbol_relocation() {
syscall bpf_syscall_string
mov64 r0, 0x0
exit",
config,
[72, 101, 108, 108, 111],
(
"bpf_syscall_string" => syscalls::SyscallString::vm,
Expand Down
16 changes: 7 additions & 9 deletions tests/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,19 +367,17 @@ fn test_verifier_unknown_sycall() {
#[test]
fn test_verifier_known_syscall() {
let prog = &[
0x85, 0x00, 0x00, 0x00, 0xfe, 0xc3, 0xf5, 0x6b, // call 0x6bf5c3fe
0x95, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // syscall 2
0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // return
];
let mut function_registry = FunctionRegistry::<BuiltinFunction<TestContextObject>>::default();
function_registry
.register_function(0x6bf5c3fe, b"my_syscall", syscalls::SyscallString::vm)

let mut loader = BuiltinProgram::new_loader_with_dense_registration(Config::default());
loader
.register_function("my_syscall", syscalls::SyscallString::vm, 2)
.unwrap();
let executable = Executable::<TestContextObject>::from_text_bytes(
prog,
Arc::new(BuiltinProgram::new_loader(
Config::default(),
function_registry,
)),
Arc::new(loader),
SBPFVersion::V2,
FunctionRegistry::default(),
)
Expand Down Expand Up @@ -489,7 +487,7 @@ fn return_instr() {
.unwrap();
let result = executable.verify::<RequisiteVerifier>();
if sbpf_version == SBPFVersion::V2 {
assert!(result.is_ok());
assert_error!(result, "VerifierError(InvalidSyscall(0))");
} else {
assert_error!(result, "VerifierError(UnknownOpCode(157, 2))");
}
Expand Down

0 comments on commit 9bd8ab6

Please sign in to comment.