Skip to content

Commit

Permalink
Feature - lduw (load upper word immediate) (#486)
Browse files Browse the repository at this point in the history
* Adds SBPFVersion::disable_lddw.

* Adds LD_UW_IMM.

* Adjusts the existing tests.

* Adds test_lduw.
  • Loading branch information
Lichtso authored Jul 26, 2023
1 parent 48f271f commit 5debf64
Show file tree
Hide file tree
Showing 16 changed files with 234 additions and 174 deletions.
13 changes: 8 additions & 5 deletions src/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use self::InstructionType::{
AluBinary, AluUnary, CallImm, CallReg, Endian, JumpConditional, JumpUnconditional, LoadAbs,
LoadImm, LoadInd, LoadReg, NoOperand, StoreImm, StoreReg, Syscall,
LoadDwImm, LoadInd, LoadReg, LoadUwImm, NoOperand, StoreImm, StoreReg, Syscall,
};
use crate::{
asm_parser::{
Expand All @@ -28,7 +28,8 @@ use std::{collections::HashMap, sync::Arc};
enum InstructionType {
AluBinary,
AluUnary,
LoadImm,
LoadUwImm,
LoadDwImm,
LoadAbs,
LoadInd,
LoadReg,
Expand Down Expand Up @@ -93,7 +94,8 @@ fn make_instruction_map() -> HashMap<String, (InstructionType, u8)> {
entry("syscall", Syscall, ebpf::CALL_IMM);
entry("call", CallImm, ebpf::CALL_IMM);
entry("callx", CallReg, ebpf::CALL_REG);
entry("lddw", LoadImm, ebpf::LD_DW_IMM);
entry("lduw", LoadUwImm, ebpf::LD_UW_IMM);
entry("lddw", LoadDwImm, ebpf::LD_DW_IMM);

// AluUnary.
entry("neg", AluUnary, ebpf::NEG64);
Expand Down Expand Up @@ -332,15 +334,16 @@ pub fn assemble<C: ContextObject>(
insn(opc, 0, 1, 0, target_pc as i64)
}
(Endian(size), [Register(dst)]) => insn(opc, *dst, 0, 0, size),
(LoadImm, [Register(dst), Integer(imm)]) => {
(LoadUwImm, [Register(dst), Integer(imm)]) => insn(opc, *dst, 0, 0, *imm),
(LoadDwImm, [Register(dst), Integer(imm)]) => {
insn(opc, *dst, 0, 0, (*imm << 32) >> 32)
}
_ => Err(format!("Unexpected operands: {operands:?}")),
}?;
insn.ptr = insn_ptr;
instructions.push(insn);
insn_ptr += 1;
if let LoadImm = inst_type {
if let LoadDwImm = inst_type {
if let Integer(imm) = operands[1] {
instructions.push(Insn {
ptr: insn_ptr,
Expand Down
1 change: 1 addition & 0 deletions src/disassembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ pub fn disassemble_instruction<C: ContextObject>(
ebpf::LD_IND_W => { name = "ldindw"; desc = ldind_str(name, insn); },
ebpf::LD_IND_DW => { name = "ldinddw"; desc = ldind_str(name, insn); },

ebpf::LD_UW_IMM => { name = "lduw"; desc = format!("{} r{:}, {:#x}", name, insn.dst, insn.imm); },
ebpf::LD_DW_IMM => { name = "lddw"; desc = format!("{} r{:}, {:#x}", name, insn.dst, insn.imm); },

// BPF_LDX class
Expand Down
2 changes: 2 additions & 0 deletions src/ebpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ pub const LD_IND_W: u8 = BPF_LD | BPF_IND | BPF_W;
/// BPF opcode: `ldinddw src, dst, imm`.
pub const LD_IND_DW: u8 = BPF_LD | BPF_IND | BPF_DW;

/// BPF opcode: `lduw dst, imm` /// `dst |= imm << 32`.
pub const LD_UW_IMM: u8 = BPF_LD | BPF_IMM | BPF_W;
/// BPF opcode: `lddw dst, imm` /// `dst = imm`.
pub const LD_DW_IMM: u8 = BPF_LD | BPF_IMM | BPF_DW;
/// BPF opcode: `ldxb dst, [src + off]` /// `dst = (src + off) as u8`.
Expand Down
5 changes: 5 additions & 0 deletions src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ pub enum SBPFVersion {
}

impl SBPFVersion {
/// Disable the only two slots long instruction: LD_DW_IMM
pub fn disable_lddw(&self) -> bool {
self != &SBPFVersion::V1
}

/// Enable native signed division
pub fn enable_sdiv(&self) -> bool {
self != &SBPFVersion::V1
Expand Down
3 changes: 3 additions & 0 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ impl<'a, 'b, V: Verifier, C: ContextObject> Interpreter<'a, 'b, V, C> {
self.vm.stack_pointer = self.vm.stack_pointer.overflowing_add(insn.imm as u64).0;
}

ebpf::LD_UW_IMM if self.executable.get_sbpf_version().disable_lddw() => {
self.reg[dst] |= (insn.imm as u64).wrapping_shl(32);
}
ebpf::LD_DW_IMM => {
ebpf::augment_lddw_unchecked(self.program, &mut insn);
instruction_width = 2;
Expand Down
19 changes: 13 additions & 6 deletions src/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,12 +329,16 @@ impl<'a, V: Verifier, C: ContextObject> JitCompiler<'a, V, C> {

// Scan through program to find actual number of instructions
let mut pc = 0;
while (pc + 1) * ebpf::INSN_SIZE <= program.len() {
let insn = ebpf::get_insn_unchecked(program, pc);
pc += match insn.opc {
ebpf::LD_DW_IMM => 2,
_ => 1,
};
if executable.get_sbpf_version().disable_lddw() {
pc = program.len() / ebpf::INSN_SIZE;
} else {
while (pc + 1) * ebpf::INSN_SIZE <= program.len() {
let insn = ebpf::get_insn_unchecked(program, pc);
pc += match insn.opc {
ebpf::LD_DW_IMM => 2,
_ => 1,
};
}
}

let mut code_length_estimate = MAX_EMPTY_PROGRAM_MACHINE_CODE_LENGTH + MAX_MACHINE_CODE_LENGTH_PER_INSTRUCTION * pc;
Expand Down Expand Up @@ -397,6 +401,9 @@ impl<'a, V: Verifier, C: ContextObject> JitCompiler<'a, V, C> {
self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x81, 0, RBP, insn.imm, Some(stack_ptr_access)));
}

ebpf::LD_UW_IMM => {
self.emit_sanitized_alu(OperandSize::S64, 0x09, 1, dst, (insn.imm as u64).wrapping_shl(32) as i64);
}
ebpf::LD_DW_IMM => {
self.emit_validate_and_profile_instruction_count(true, Some(self.pc + 2));
self.pc += 1;
Expand Down
2 changes: 1 addition & 1 deletion src/static_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,7 @@ impl<'a> Analysis<'a> {
bind(&mut state, insn, false, DataResource::Register(insn.src));
bind(&mut state, insn, true, DataResource::Register(0));
}
ebpf::LD_DW_IMM => {
ebpf::LD_UW_IMM | ebpf::LD_DW_IMM => {
bind(&mut state, insn, true, DataResource::Register(insn.dst));
}
ebpf::LD_B_REG | ebpf::LD_H_REG | ebpf::LD_W_REG | ebpf::LD_DW_REG => {
Expand Down
8 changes: 2 additions & 6 deletions src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,6 @@ impl Verifier for RequisiteVerifier {
if sbpf_version.static_syscalls() && function_iter.peek() == Some(&insn_ptr) {
function_range.start = function_iter.next().unwrap_or(0);
function_range.end = *function_iter.peek().unwrap_or(&program_range.end);
if insn.opc == 0 {
return Err(VerifierError::InvalidFunction(
function_range.start,
));
}
let insn = ebpf::get_insn(prog, function_range.end.saturating_sub(1));
match insn.opc {
ebpf::JA | ebpf::CALL_IMM | ebpf::CALL_REG | ebpf::EXIT => {},
Expand All @@ -250,7 +245,8 @@ impl Verifier for RequisiteVerifier {
}

match insn.opc {
ebpf::LD_DW_IMM => {
ebpf::LD_UW_IMM if sbpf_version.disable_lddw() => {},
ebpf::LD_DW_IMM if !sbpf_version.disable_lddw() => {
check_load_dw(prog, insn_ptr)?;
insn_ptr += 1;
},
Expand Down
Binary file modified tests/elfs/reloc_64_64.so
Binary file not shown.
Binary file modified tests/elfs/reloc_64_relative.so
Binary file not shown.
Binary file modified tests/elfs/reloc_64_relative_data.so
Binary file not shown.
Binary file modified tests/elfs/rodata_section.so
Binary file not shown.
Binary file modified tests/elfs/struct_func_pointer.so
Binary file not shown.
Binary file modified tests/elfs/syscall_static.so
Binary file not shown.
Loading

0 comments on commit 5debf64

Please sign in to comment.