Skip to content

Commit

Permalink
Also removes the pc field from EbpfError::AccessViolation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lichtso committed Sep 29, 2023
1 parent e2c048b commit 30fc37c
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 225 deletions.
13 changes: 5 additions & 8 deletions benches/memory_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ macro_rules! bench_gapped_randomized_access_with_1024_entries {
AccessType::Load,
0x100000000 + (prng.gen::<u64>() % frame_count * (frame_size * 2)),
1,
0,
)
.is_ok());
});
Expand Down Expand Up @@ -119,7 +118,6 @@ macro_rules! bench_randomized_access_with_0001_entry {
AccessType::Load,
0x100000000 + (prng.gen::<u64>() % content.len() as u64),
1,
0,
);
});
}
Expand Down Expand Up @@ -153,7 +151,6 @@ macro_rules! bench_randomized_access_with_n_entries {
AccessType::Load,
0x100000000 + (prng.gen::<u64>() % end_address),
1,
0,
);
});
}
Expand Down Expand Up @@ -199,7 +196,7 @@ macro_rules! bench_randomized_mapping_with_n_entries {
let config = Config::default();
let memory_mapping = $mem::new(memory_regions, &config, &SBPFVersion::V2).unwrap();
bencher.iter(|| {
let _ = memory_mapping.map(AccessType::Load, 0x100000000, 1, 0);
let _ = memory_mapping.map(AccessType::Load, 0x100000000, 1);
});
}
};
Expand Down Expand Up @@ -248,7 +245,7 @@ macro_rules! bench_mapping_with_n_entries {
let config = Config::default();
let memory_mapping = $mem::new(memory_regions, &config, &SBPFVersion::V2).unwrap();
bencher.iter(|| {
let _ = memory_mapping.map(AccessType::Load, 0x100000000, 1, 0);
let _ = memory_mapping.map(AccessType::Load, 0x100000000, 1);
});
}
};
Expand Down Expand Up @@ -310,13 +307,13 @@ fn do_bench_mapping_operation(bencher: &mut Bencher, op: MemoryOperation, vm_add

match op {
MemoryOperation::Map => bencher.iter(|| {
let _ = memory_mapping.map(AccessType::Load, vm_addr, 8, 0).unwrap();
let _ = memory_mapping.map(AccessType::Load, vm_addr, 8).unwrap();
}),
MemoryOperation::Load => bencher.iter(|| {
let _ = memory_mapping.load::<u64>(vm_addr, 0).unwrap();
let _ = memory_mapping.load::<u64>(vm_addr).unwrap();
}),
MemoryOperation::Store(val) => bencher.iter(|| {
let _ = memory_mapping.store(val, vm_addr, 0).unwrap();
let _ = memory_mapping.store(val, vm_addr).unwrap();
}),
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/debugger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ impl<'a, 'b, C: ContextObject> Target for Interpreter<'a, 'b, C> {
fn get_host_ptr<C: ContextObject>(
interpreter: &mut Interpreter<C>,
mut vm_addr: u64,
pc: usize,
) -> Result<*mut u8, EbpfError> {
if vm_addr < ebpf::MM_PROGRAM_START {
vm_addr += ebpf::MM_PROGRAM_START;
Expand All @@ -161,7 +160,6 @@ fn get_host_ptr<C: ContextObject>(
AccessType::Load,
vm_addr,
std::mem::size_of::<u8>() as u64,
pc + ebpf::ELF_INSN_DUMP_OFFSET,
) {
ProgramResult::Ok(host_addr) => Ok(host_addr as *mut u8),
ProgramResult::Err(err) => Err(err),
Expand Down Expand Up @@ -197,7 +195,7 @@ impl<'a, 'b, C: ContextObject> SingleThreadBase for Interpreter<'a, 'b, C> {

fn read_addrs(&mut self, start_addr: u64, data: &mut [u8]) -> TargetResult<(), Self> {
for (vm_addr, val) in (start_addr..).zip(data.iter_mut()) {
let host_ptr = match get_host_ptr(self, vm_addr, self.pc) {
let host_ptr = match get_host_ptr(self, vm_addr) {
Ok(host_ptr) => host_ptr,
// The debugger is sometimes requesting more data than we have access to, just skip these
_ => continue,
Expand Down
12 changes: 4 additions & 8 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,11 @@ pub enum EbpfError {
#[error("Invalid memory region at index {0}")]
InvalidMemoryRegion(usize),
/// Access violation (general)
#[error(
"Access violation in {4} section at address {2:#x} of size {3:?} at BPF instruction #{0}"
)]
AccessViolation(usize, AccessType, u64, u64, &'static str),
#[error("Access violation in {3} section at address {1:#x} of size {2:?}")]
AccessViolation(AccessType, u64, u64, &'static str),
/// Access violation (stack specific)
#[error(
"Access violation in stack frame {4} at address {2:#x} of size {3:?} at BPF instruction #{0}"
)]
StackAccessViolation(usize, AccessType, u64, u64, i64),
#[error("Access violation in stack frame {3} at address {1:#x} of size {2:?}")]
StackAccessViolation(AccessType, u64, u64, i64),
/// Invalid instruction
#[error("invalid BPF instruction")]
InvalidInstruction,
Expand Down
35 changes: 17 additions & 18 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ use std::convert::TryInto;

/// Virtual memory operation helper.
macro_rules! translate_memory_access {
(_impl, $self:ident, $op:ident, $vm_addr:ident, $pc:ident, $T:ty, $($rest:expr),*) => {
(_impl, $self:ident, $op:ident, $vm_addr:ident, $T:ty, $($rest:expr),*) => {
match $self.vm.memory_mapping.$op::<$T>(
$($rest,)*
$vm_addr,
$pc + ebpf::ELF_INSN_DUMP_OFFSET,
) {
ProgramResult::Ok(v) => v,
ProgramResult::Err(err) => {
Expand All @@ -37,13 +36,13 @@ macro_rules! translate_memory_access {
};

// MemoryMapping::load()
($self:ident, load, $vm_addr:ident, $pc:ident, $T:ty) => {
translate_memory_access!(_impl, $self, load, $vm_addr, $pc, $T,)
($self:ident, load, $vm_addr:ident, $T:ty) => {
translate_memory_access!(_impl, $self, load, $vm_addr, $T,)
};

// MemoryMapping::store()
($self:ident, store, $value:expr, $vm_addr:ident, $pc:ident, $T:ty) => {
translate_memory_access!(_impl, $self, store, $vm_addr, $pc, $T, ($value) as $T);
($self:ident, store, $value:expr, $vm_addr:ident, $T:ty) => {
translate_memory_access!(_impl, $self, store, $vm_addr, $T, ($value) as $T);
};
}

Expand Down Expand Up @@ -202,55 +201,55 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
// BPF_LDX class
ebpf::LD_B_REG => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, pc, u8);
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u8);
},
ebpf::LD_H_REG => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, pc, u16);
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u16);
},
ebpf::LD_W_REG => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, pc, u32);
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u32);
},
ebpf::LD_DW_REG => {
let vm_addr = (self.reg[src] as i64).wrapping_add(insn.off as i64) as u64;
self.reg[dst] = translate_memory_access!(self, load, vm_addr, pc, u64);
self.reg[dst] = translate_memory_access!(self, load, vm_addr, u64);
},

// BPF_ST class
ebpf::ST_B_IMM => {
let vm_addr = (self.reg[dst] as i64).wrapping_add( insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, pc, u8);
translate_memory_access!(self, store, insn.imm, vm_addr, u8);
},
ebpf::ST_H_IMM => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, pc, u16);
translate_memory_access!(self, store, insn.imm, vm_addr, u16);
},
ebpf::ST_W_IMM => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, pc, u32);
translate_memory_access!(self, store, insn.imm, vm_addr, u32);
},
ebpf::ST_DW_IMM => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, insn.imm, vm_addr, pc, u64);
translate_memory_access!(self, store, insn.imm, vm_addr, u64);
},

// BPF_STX class
ebpf::ST_B_REG => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, pc, u8);
translate_memory_access!(self, store, self.reg[src], vm_addr, u8);
},
ebpf::ST_H_REG => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, pc, u16);
translate_memory_access!(self, store, self.reg[src], vm_addr, u16);
},
ebpf::ST_W_REG => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, pc, u32);
translate_memory_access!(self, store, self.reg[src], vm_addr, u32);
},
ebpf::ST_DW_REG => {
let vm_addr = (self.reg[dst] as i64).wrapping_add(insn.off as i64) as u64;
translate_memory_access!(self, store, self.reg[src], vm_addr, pc, u64);
translate_memory_access!(self, store, self.reg[src], vm_addr, u64);
},

// BPF_ALU class
Expand Down
50 changes: 21 additions & 29 deletions src/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,22 +181,21 @@ impl PartialEq for JitProgram {
// Used to define subroutines and then call them
// See JitCompiler::set_anchor() and JitCompiler::relative_to_anchor()
const ANCHOR_TRACE: usize = 0;
const ANCHOR_CALL_EXCEEDED_MAX_INSTRUCTIONS: usize = 1;
const ANCHOR_THROW_EXCEEDED_MAX_INSTRUCTIONS: usize = 1;
const ANCHOR_EPILOGUE: usize = 2;
const ANCHOR_THROW_EXCEPTION_UNCHECKED: usize = 3;
const ANCHOR_EXIT: usize = 4;
const ANCHOR_THROW_EXCEPTION: usize = 5;
const ANCHOR_ACCESS_VIOLATION: usize = 6;
const ANCHOR_CALL_DEPTH_EXCEEDED: usize = 7;
const ANCHOR_CALL_OUTSIDE_TEXT_SEGMENT: usize = 8;
const ANCHOR_DIV_BY_ZERO: usize = 9;
const ANCHOR_DIV_OVERFLOW: usize = 10;
const ANCHOR_CALL_UNSUPPORTED_INSTRUCTION: usize = 11;
const ANCHOR_EXTERNAL_FUNCTION_CALL: usize = 12;
const ANCHOR_ANCHOR_INTERNAL_FUNCTION_CALL_PROLOGUE: usize = 13;
const ANCHOR_ANCHOR_INTERNAL_FUNCTION_CALL_REG: usize = 14;
const ANCHOR_TRANSLATE_MEMORY_ADDRESS: usize = 22;
const ANCHOR_COUNT: usize = 31; // Update me when adding or removing anchors
const ANCHOR_CALL_DEPTH_EXCEEDED: usize = 6;
const ANCHOR_CALL_OUTSIDE_TEXT_SEGMENT: usize = 7;
const ANCHOR_DIV_BY_ZERO: usize = 8;
const ANCHOR_DIV_OVERFLOW: usize = 9;
const ANCHOR_CALL_UNSUPPORTED_INSTRUCTION: usize = 10;
const ANCHOR_EXTERNAL_FUNCTION_CALL: usize = 11;
const ANCHOR_ANCHOR_INTERNAL_FUNCTION_CALL_PROLOGUE: usize = 12;
const ANCHOR_ANCHOR_INTERNAL_FUNCTION_CALL_REG: usize = 13;
const ANCHOR_TRANSLATE_MEMORY_ADDRESS: usize = 21;
const ANCHOR_COUNT: usize = 30; // Update me when adding or removing anchors

const REGISTER_MAP: [u8; 11] = [
CALLER_SAVED_REGISTERS[0],
Expand All @@ -215,8 +214,8 @@ const REGISTER_MAP: [u8; 11] = [
// Special registers:
// ARGUMENT_REGISTERS[0] RDI BPF program counter limit (used by instruction meter)
// CALLER_SAVED_REGISTERS[8] R11 Scratch register
// CALLER_SAVED_REGISTERS[7] R10 Unused for the most part, scratch register for exception handling
// CALLEE_SAVED_REGISTERS[0] RBP Constant pointer to initial RSP - 8
// CALLER_SAVED_REGISTERS[7] R10 Scratch register
// CALLEE_SAVED_REGISTERS[0] RBP Constant pointer to vm object, see slot_on_environment_stack()

#[derive(Copy, Clone, Debug)]
pub enum OperandSize {
Expand Down Expand Up @@ -876,7 +875,7 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
} else {
self.emit_ins(X86Instruction::cmp(OperandSize::S64, R11, ARGUMENT_REGISTERS[0], None));
}
self.emit_ins(X86Instruction::conditional_jump_immediate(if exclusive { 0x82 } else { 0x86 }, self.relative_to_anchor(ANCHOR_CALL_EXCEEDED_MAX_INSTRUCTIONS, 6)));
self.emit_ins(X86Instruction::conditional_jump_immediate(if exclusive { 0x82 } else { 0x86 }, self.relative_to_anchor(ANCHOR_THROW_EXCEEDED_MAX_INSTRUCTIONS, 6)));
}

#[inline]
Expand Down Expand Up @@ -1285,9 +1284,9 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
fn emit_set_exception_kind(&mut self, err: EbpfError) {
let err_discriminant = ProgramResult::Err(EbpfError::JitNotCompiled).discriminant();
let err_kind = unsafe { *(&err as *const _ as *const u64) };
self.emit_ins(X86Instruction::lea(OperandSize::S64, RBP, R10, Some(X86IndirectAccess::Offset(self.slot_on_environment_stack(RuntimeEnvironmentSlot::ProgramResult) + std::mem::size_of::<u64>() as i32))));
self.emit_ins(X86Instruction::store_immediate(OperandSize::S64, R10, X86IndirectAccess::Offset(-(std::mem::size_of::<u64>() as i32)), err_discriminant as i64)); // result.discriminant = err_discriminant;
self.emit_ins(X86Instruction::store_immediate(OperandSize::S64, R10, X86IndirectAccess::Offset(0), err_kind as i64)); // err.kind = err_kind;
self.emit_ins(X86Instruction::lea(OperandSize::S64, RBP, R10, Some(X86IndirectAccess::Offset(self.slot_on_environment_stack(RuntimeEnvironmentSlot::ProgramResult)))));
self.emit_ins(X86Instruction::store_immediate(OperandSize::S64, R10, X86IndirectAccess::Offset(0), err_discriminant as i64)); // result.discriminant = err_discriminant;
self.emit_ins(X86Instruction::store_immediate(OperandSize::S64, R10, X86IndirectAccess::Offset(std::mem::size_of::<u64>() as i32), err_kind as i64)); // err.kind = err_kind;
}

fn emit_result_is_err(&mut self, destination: u8) {
Expand Down Expand Up @@ -1341,7 +1340,7 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
self.emit_ins(X86Instruction::return_near());

// Handler for EbpfError::ExceededMaxInstructions
self.set_anchor(ANCHOR_CALL_EXCEEDED_MAX_INSTRUCTIONS);
self.set_anchor(ANCHOR_THROW_EXCEEDED_MAX_INSTRUCTIONS);
self.emit_set_exception_kind(EbpfError::ExceededMaxInstructions);
self.emit_ins(X86Instruction::mov(OperandSize::S64, ARGUMENT_REGISTERS[0], R11)); // R11 = instruction_meter;
// Fall through
Expand All @@ -1354,7 +1353,7 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
self.set_anchor(ANCHOR_EXIT);
self.emit_validate_instruction_count(false, None);
self.emit_ins(X86Instruction::lea(OperandSize::S64, RBP, R10, Some(X86IndirectAccess::Offset(self.slot_on_environment_stack(RuntimeEnvironmentSlot::ProgramResult)))));
self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_MAP[0], R10, X86IndirectAccess::Offset(8))); // result.return_value = R0;
self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_MAP[0], R10, X86IndirectAccess::Offset(std::mem::size_of::<u64>() as i32))); // result.return_value = R0;
self.emit_ins(X86Instruction::load_immediate(OperandSize::S64, REGISTER_MAP[0], 0));
self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_EPILOGUE, 5)));

Expand All @@ -1364,12 +1363,6 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
self.emit_validate_instruction_count(false, None);
self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_THROW_EXCEPTION_UNCHECKED, 5)));

// Handler for EbpfError::AccessViolation
self.set_anchor(ANCHOR_ACCESS_VIOLATION);
self.emit_ins(X86Instruction::store(OperandSize::S64, R11, R10, X86IndirectAccess::Offset(std::mem::size_of::<u64>() as i32 * 2))); // result.pc = self.pc;
self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x81, 0, R10, ebpf::ELF_INSN_DUMP_OFFSET as i64, Some(X86IndirectAccess::Offset(std::mem::size_of::<u64>() as i32 * 2)))); // result.pc += ebpf::ELF_INSN_DUMP_OFFSET;
self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_THROW_EXCEPTION, 5)));

// Handler for EbpfError::CallDepthExceeded
self.set_anchor(ANCHOR_CALL_DEPTH_EXCEEDED);
self.emit_set_exception_kind(EbpfError::CallDepthExceeded);
Expand Down Expand Up @@ -1547,11 +1540,10 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
self.emit_result_is_err(R11);
self.emit_ins(X86Instruction::pop(R11)); // R11 = self.pc
self.emit_ins(X86Instruction::xchg(OperandSize::S64, R11, RSP, Some(X86IndirectAccess::OffsetIndexShift(0, RSP, 0)))); // Swap return address and self.pc
self.emit_ins(X86Instruction::lea(OperandSize::S64, RBP, R10, Some(X86IndirectAccess::Offset(self.slot_on_environment_stack(RuntimeEnvironmentSlot::ProgramResult)))));
self.emit_ins(X86Instruction::conditional_jump_immediate(0x85, self.relative_to_anchor(ANCHOR_ACCESS_VIOLATION, 6)));
self.emit_ins(X86Instruction::conditional_jump_immediate(0x85, self.relative_to_anchor(ANCHOR_THROW_EXCEPTION, 6)));

// unwrap() the result into R11
self.emit_ins(X86Instruction::load(OperandSize::S64, R10, R11, X86IndirectAccess::Offset(8)));
self.emit_ins(X86Instruction::load(OperandSize::S64, RBP, R11, X86IndirectAccess::Offset(self.slot_on_environment_stack(RuntimeEnvironmentSlot::ProgramResult) + std::mem::size_of::<u64>() as i32)));

self.emit_ins(X86Instruction::return_near());
}
Expand Down
Loading

0 comments on commit 30fc37c

Please sign in to comment.