From cd19a25c17ec474e6fa01a3cc3efa325f44cd111 Mon Sep 17 00:00:00 2001 From: Lucas Ste <38472950+LucasSte@users.noreply.github.com> Date: Fri, 11 Oct 2024 19:33:00 -0300 Subject: [PATCH] Include new `syscall` instruction in the (dis)assembler (#611) --- src/assembler.rs | 11 ++++++++++- src/disassembler.rs | 15 ++++----------- src/ebpf.rs | 6 +++++- src/interpreter.rs | 3 ++- src/jit.rs | 3 ++- src/verifier.rs | 1 + tests/assembler.rs | 13 +++++++++++++ tests/disassembler.rs | 9 +++++++++ tests/verifier.rs | 4 ++-- 9 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/assembler.rs b/src/assembler.rs index 46a88e65..ffdb3508 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -122,7 +122,15 @@ fn make_instruction_map(sbpf_version: SBPFVersion) -> HashMap( 0, ebpf::hash_symbol_name(label.as_bytes()) as i32 as i64, ), + (Syscall, [Integer(imm)]) => insn(opc, 0, 0, 0, *imm), (CallImm, [Label(label)]) => { let label: &str = label; let target_pc = *labels diff --git a/src/disassembler.rs b/src/disassembler.rs index 94a7695c..b500b0a5 100644 --- a/src/disassembler.rs +++ b/src/disassembler.rs @@ -265,14 +265,7 @@ pub fn disassemble_instruction( ebpf::JSLE_IMM => { name = "jsle"; desc = jmp_imm_str(name, insn, cfg_nodes); }, ebpf::JSLE_REG => { name = "jsle"; desc = jmp_reg_str(name, insn, cfg_nodes); }, ebpf::CALL_IMM => { - let mut function_name = None; - if sbpf_version.static_syscalls() { - if insn.src != 0 { - function_name = Some(resolve_label(cfg_nodes, insn.imm as usize).to_string()); - } - } else { - function_name = function_registry.lookup_by_key(insn.imm as u32).map(|(function_name, _)| String::from_utf8_lossy(function_name).to_string()); - } + let function_name = function_registry.lookup_by_key(insn.imm as u32).map(|(function_name, _)| String::from_utf8_lossy(function_name).to_string()); let function_name = if let Some(function_name) = function_name { name = "call"; function_name @@ -284,9 +277,9 @@ pub fn disassemble_instruction( }, ebpf::CALL_REG => { name = "callx"; desc = format!("{} r{}", name, if sbpf_version.callx_uses_src_reg() { insn.src } else { insn.imm as u8 }); }, ebpf::EXIT - | ebpf::RETURN if !sbpf_version.static_syscalls() => { name = "exit"; desc = name.to_string(); }, - ebpf::EXIT - | ebpf::RETURN if sbpf_version.static_syscalls() => { name = "return"; desc = name.to_string(); }, + | ebpf::RETURN if !sbpf_version.static_syscalls() => { name = "exit"; desc = name.to_string(); }, + ebpf::RETURN if sbpf_version.static_syscalls() => { name = "return"; desc = name.to_string(); }, + ebpf::SYSCALL if sbpf_version.static_syscalls() => { desc = format!("syscall {}", insn.imm); }, _ => { name = "unknown"; desc = format!("{} opcode={:#x}", name, insn.opc); }, }; diff --git a/src/ebpf.rs b/src/ebpf.rs index 6608452b..9c443092 100644 --- a/src/ebpf.rs +++ b/src/ebpf.rs @@ -195,8 +195,10 @@ pub const BPF_JSGT: u8 = 0x60; pub const BPF_JSGE: u8 = 0x70; /// BPF JMP operation code: syscall function call. pub const BPF_CALL: u8 = 0x80; -/// BPF JMP operation code: return from program. +/// BPF JMP operation code: return from program (V1). pub const BPF_EXIT: u8 = 0x90; +/// BPF JMP operation code: static syscall (V2). +pub const BPF_SYSCALL: u8 = 0x90; /// BPF JMP operation code: jump if lower than. pub const BPF_JLT: u8 = 0xa0; /// BPF JMP operation code: jump if lower or equal. @@ -484,6 +486,8 @@ pub const CALL_REG: u8 = BPF_JMP | BPF_X | BPF_CALL; pub const EXIT: u8 = BPF_JMP | BPF_EXIT; /// BPF opcode: `return` /// `return r0`. /// Valid only for SBPFv2 pub const RETURN: u8 = BPF_JMP | BPF_X | BPF_EXIT; +/// BPF opcode: `syscall` /// `syscall imm`. /// Valid only for SBPFv2 +pub const SYSCALL: u8 = BPF_JMP | BPF_SYSCALL; // Used in JIT /// Mask to extract the operation class from an operation code. diff --git a/src/interpreter.rs b/src/interpreter.rs index adc2ab79..34ea6dd8 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -526,7 +526,8 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> { // Do not delegate the check to the verifier, since self.registered functions can be // changed after the program has been verified. - ebpf::CALL_IMM => { + ebpf::CALL_IMM + | ebpf::SYSCALL if insn.opc == ebpf::CALL_IMM || self.executable.get_sbpf_version().static_syscalls() => { let mut resolved = false; let (external, internal) = if self.executable.get_sbpf_version().static_syscalls() { (insn.src == 0, insn.src != 0) diff --git a/src/jit.rs b/src/jit.rs index cf1fd63e..d17abd19 100644 --- a/src/jit.rs +++ b/src/jit.rs @@ -705,7 +705,8 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> { ebpf::JSLT_REG => self.emit_conditional_branch_reg(0x8c, false, src, dst, target_pc), ebpf::JSLE_IMM => self.emit_conditional_branch_imm(0x8e, false, insn.imm, dst, target_pc), ebpf::JSLE_REG => self.emit_conditional_branch_reg(0x8e, false, src, dst, target_pc), - ebpf::CALL_IMM => { + ebpf::CALL_IMM | ebpf::SYSCALL + if insn.opc == ebpf::CALL_IMM || self.executable.get_sbpf_version().static_syscalls() => { // For JIT, external functions MUST be registered at compile time. let mut resolved = false; diff --git a/src/verifier.rs b/src/verifier.rs index e9a28024..6f46a0a3 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -392,6 +392,7 @@ impl Verifier for RequisiteVerifier { 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() => {}, _ => { return Err(VerifierError::UnknownOpCode(insn.opc, insn_ptr)); diff --git a/tests/assembler.rs b/tests/assembler.rs index d94c2155..66df864d 100644 --- a/tests/assembler.rs +++ b/tests/assembler.rs @@ -71,6 +71,19 @@ fn test_exit() { ); } +#[test] +fn test_static_syscall() { + let config = Config { + enabled_sbpf_versions: SBPFVersion::V2..=SBPFVersion::V2, + ..Config::default() + }; + + assert_eq!( + asm_with_config("syscall 3", config), + Ok(vec![insn(0, ebpf::SYSCALL, 0, 0, 0, 3)]) + ); +} + #[test] fn test_return() { let config = Config { diff --git a/tests/disassembler.rs b/tests/disassembler.rs index 0740787e..e77d274f 100644 --- a/tests/disassembler.rs +++ b/tests/disassembler.rs @@ -60,6 +60,15 @@ fn test_return() { disasm!("entrypoint:\n return\n", config); } +#[test] +fn test_static_syscall() { + let config = Config { + enabled_sbpf_versions: SBPFVersion::V2..=SBPFVersion::V2, + ..Config::default() + }; + disasm!("entrypoint:\n syscall 5\n", config); +} + // Example for InstructionType::AluBinary. #[test] fn test_add64() { diff --git a/tests/verifier.rs b/tests/verifier.rs index a7cf9421..5fa54218 100644 --- a/tests/verifier.rs +++ b/tests/verifier.rs @@ -431,7 +431,7 @@ fn return_instr() { for sbpf_version in [SBPFVersion::V1, SBPFVersion::V2] { let prog = &[ 0xbf, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov64 r0, 2 - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit + 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit (v1), syscall (v2) 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // return ]; @@ -444,7 +444,7 @@ fn return_instr() { .unwrap(); let result = executable.verify::(); if sbpf_version == SBPFVersion::V2 { - assert_error!(result, "VerifierError(UnknownOpCode(149, 1))"); + assert!(result.is_ok()); } else { assert_error!(result, "VerifierError(UnknownOpCode(157, 2))"); }