Skip to content

Commit

Permalink
Include new syscall instruction in the (dis)assembler (#611)
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasSte authored Oct 11, 2024
1 parent c168a87 commit cd19a25
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 17 deletions.
11 changes: 10 additions & 1 deletion src/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,15 @@ fn make_instruction_map(sbpf_version: SBPFVersion) -> HashMap<String, (Instructi

// Miscellaneous.
entry("ja", JumpUnconditional, ebpf::JA);
entry("syscall", Syscall, ebpf::CALL_IMM);
entry(
"syscall",
Syscall,
if sbpf_version == SBPFVersion::V1 {
ebpf::CALL_IMM
} else {
ebpf::SYSCALL
},
);
entry("call", CallImm, ebpf::CALL_IMM);
entry("callx", CallReg, ebpf::CALL_REG);
entry("lddw", LoadDwImm, ebpf::LD_DW_IMM);
Expand Down Expand Up @@ -450,6 +458,7 @@ pub fn assemble<C: ContextObject>(
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
Expand Down
15 changes: 4 additions & 11 deletions src/disassembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,7 @@ pub fn disassemble_instruction<C: ContextObject>(
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
Expand All @@ -284,9 +277,9 @@ pub fn disassemble_instruction<C: ContextObject>(
},
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); },
};
Expand Down
6 changes: 5 additions & 1 deletion src/ebpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion src/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
13 changes: 13 additions & 0 deletions tests/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
9 changes: 9 additions & 0 deletions tests/disassembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
4 changes: 2 additions & 2 deletions tests/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
];

Expand All @@ -444,7 +444,7 @@ fn return_instr() {
.unwrap();
let result = executable.verify::<RequisiteVerifier>();
if sbpf_version == SBPFVersion::V2 {
assert_error!(result, "VerifierError(UnknownOpCode(149, 1))");
assert!(result.is_ok());
} else {
assert_error!(result, "VerifierError(UnknownOpCode(157, 2))");
}
Expand Down

0 comments on commit cd19a25

Please sign in to comment.