#![allow(clippy::deprecated_cfg_attr)]
#![cfg_attr(rustfmt, rustfmt_skip)]
use crate::{elf::ELFError, jit::JITError, memory_region::MemoryRegion};
use std::fmt;
use std::error::Error;
use byteorder::{ByteOrder, LittleEndian};
use hash32::{Hash, Hasher, Murmur3Hasher};
use thiserror::Error as ThisError;
pub const PROG_MAX_INSNS: usize = 65536;
pub const INSN_SIZE: usize = 8;
pub const PROG_MAX_SIZE: usize = PROG_MAX_INSNS * INSN_SIZE;
pub const STACK_FRAME_SIZE: usize = 4096; pub const STACK_REG: usize = 10;
pub const FIRST_SCRATCH_REG: usize = 6;
pub const SCRATCH_REGS: usize = 4;
pub const MAX_CALL_DEPTH: usize = 20;
pub const ELF_INSN_DUMP_OFFSET: usize = 29;
pub const MM_PROGRAM_START: u64 = 0x100000000;
pub const MM_STACK_START: u64 = 0x200000000;
pub const MM_HEAP_START: u64 = 0x300000000;
pub const MM_INPUT_START: u64 = 0x400000000;
pub const BPF_LD : u8 = 0x00;
pub const BPF_LDX : u8 = 0x01;
pub const BPF_ST : u8 = 0x02;
pub const BPF_STX : u8 = 0x03;
pub const BPF_ALU : u8 = 0x04;
pub const BPF_JMP : u8 = 0x05;
pub const BPF_ALU64 : u8 = 0x07;
pub const BPF_W : u8 = 0x00;
pub const BPF_H : u8 = 0x08;
pub const BPF_B : u8 = 0x10;
pub const BPF_DW : u8 = 0x18;
pub const BPF_IMM : u8 = 0x00;
pub const BPF_ABS : u8 = 0x20;
pub const BPF_IND : u8 = 0x40;
pub const BPF_MEM : u8 = 0x60;
pub const BPF_XADD : u8 = 0xc0;
pub const BPF_K : u8 = 0x00;
pub const BPF_X : u8 = 0x08;
pub const BPF_ADD : u8 = 0x00;
pub const BPF_SUB : u8 = 0x10;
pub const BPF_MUL : u8 = 0x20;
pub const BPF_DIV : u8 = 0x30;
pub const BPF_OR : u8 = 0x40;
pub const BPF_AND : u8 = 0x50;
pub const BPF_LSH : u8 = 0x60;
pub const BPF_RSH : u8 = 0x70;
pub const BPF_NEG : u8 = 0x80;
pub const BPF_MOD : u8 = 0x90;
pub const BPF_XOR : u8 = 0xa0;
pub const BPF_MOV : u8 = 0xb0;
pub const BPF_ARSH : u8 = 0xc0;
pub const BPF_END : u8 = 0xd0;
pub const BPF_JA : u8 = 0x00;
pub const BPF_JEQ : u8 = 0x10;
pub const BPF_JGT : u8 = 0x20;
pub const BPF_JGE : u8 = 0x30;
pub const BPF_JSET : u8 = 0x40;
pub const BPF_JNE : u8 = 0x50;
pub const BPF_JSGT : u8 = 0x60;
pub const BPF_JSGE : u8 = 0x70;
pub const BPF_CALL : u8 = 0x80;
pub const BPF_EXIT : u8 = 0x90;
pub const BPF_JLT : u8 = 0xa0;
pub const BPF_JLE : u8 = 0xb0;
pub const BPF_JSLT : u8 = 0xc0;
pub const BPF_JSLE : u8 = 0xd0;
pub const LD_ABS_B : u8 = BPF_LD | BPF_ABS | BPF_B;
pub const LD_ABS_H : u8 = BPF_LD | BPF_ABS | BPF_H;
pub const LD_ABS_W : u8 = BPF_LD | BPF_ABS | BPF_W;
pub const LD_ABS_DW : u8 = BPF_LD | BPF_ABS | BPF_DW;
pub const LD_IND_B : u8 = BPF_LD | BPF_IND | BPF_B;
pub const LD_IND_H : u8 = BPF_LD | BPF_IND | BPF_H;
pub const LD_IND_W : u8 = BPF_LD | BPF_IND | BPF_W;
pub const LD_IND_DW : u8 = BPF_LD | BPF_IND | BPF_DW;
#[allow(unknown_lints)]
#[allow(clippy::eq_op)]
pub const LD_DW_IMM : u8 = BPF_LD | BPF_IMM | BPF_DW;
pub const LD_B_REG : u8 = BPF_LDX | BPF_MEM | BPF_B;
pub const LD_H_REG : u8 = BPF_LDX | BPF_MEM | BPF_H;
pub const LD_W_REG : u8 = BPF_LDX | BPF_MEM | BPF_W;
pub const LD_DW_REG : u8 = BPF_LDX | BPF_MEM | BPF_DW;
pub const ST_B_IMM : u8 = BPF_ST | BPF_MEM | BPF_B;
pub const ST_H_IMM : u8 = BPF_ST | BPF_MEM | BPF_H;
pub const ST_W_IMM : u8 = BPF_ST | BPF_MEM | BPF_W;
pub const ST_DW_IMM : u8 = BPF_ST | BPF_MEM | BPF_DW;
pub const ST_B_REG : u8 = BPF_STX | BPF_MEM | BPF_B;
pub const ST_H_REG : u8 = BPF_STX | BPF_MEM | BPF_H;
pub const ST_W_REG : u8 = BPF_STX | BPF_MEM | BPF_W;
pub const ST_DW_REG : u8 = BPF_STX | BPF_MEM | BPF_DW;
pub const ST_W_XADD : u8 = BPF_STX | BPF_XADD | BPF_W;
pub const ST_DW_XADD : u8 = BPF_STX | BPF_XADD | BPF_DW;
pub const ADD32_IMM : u8 = BPF_ALU | BPF_K | BPF_ADD;
pub const ADD32_REG : u8 = BPF_ALU | BPF_X | BPF_ADD;
pub const SUB32_IMM : u8 = BPF_ALU | BPF_K | BPF_SUB;
pub const SUB32_REG : u8 = BPF_ALU | BPF_X | BPF_SUB;
pub const MUL32_IMM : u8 = BPF_ALU | BPF_K | BPF_MUL;
pub const MUL32_REG : u8 = BPF_ALU | BPF_X | BPF_MUL;
pub const DIV32_IMM : u8 = BPF_ALU | BPF_K | BPF_DIV;
pub const DIV32_REG : u8 = BPF_ALU | BPF_X | BPF_DIV;
pub const OR32_IMM : u8 = BPF_ALU | BPF_K | BPF_OR;
pub const OR32_REG : u8 = BPF_ALU | BPF_X | BPF_OR;
pub const AND32_IMM : u8 = BPF_ALU | BPF_K | BPF_AND;
pub const AND32_REG : u8 = BPF_ALU | BPF_X | BPF_AND;
pub const LSH32_IMM : u8 = BPF_ALU | BPF_K | BPF_LSH;
pub const LSH32_REG : u8 = BPF_ALU | BPF_X | BPF_LSH;
pub const RSH32_IMM : u8 = BPF_ALU | BPF_K | BPF_RSH;
pub const RSH32_REG : u8 = BPF_ALU | BPF_X | BPF_RSH;
pub const NEG32 : u8 = BPF_ALU | BPF_NEG;
pub const MOD32_IMM : u8 = BPF_ALU | BPF_K | BPF_MOD;
pub const MOD32_REG : u8 = BPF_ALU | BPF_X | BPF_MOD;
pub const XOR32_IMM : u8 = BPF_ALU | BPF_K | BPF_XOR;
pub const XOR32_REG : u8 = BPF_ALU | BPF_X | BPF_XOR;
pub const MOV32_IMM : u8 = BPF_ALU | BPF_K | BPF_MOV;
pub const MOV32_REG : u8 = BPF_ALU | BPF_X | BPF_MOV;
pub const ARSH32_IMM : u8 = BPF_ALU | BPF_K | BPF_ARSH;
pub const ARSH32_REG : u8 = BPF_ALU | BPF_X | BPF_ARSH;
pub const LE : u8 = BPF_ALU | BPF_K | BPF_END;
pub const BE : u8 = BPF_ALU | BPF_X | BPF_END;
pub const ADD64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_ADD;
pub const ADD64_REG : u8 = BPF_ALU64 | BPF_X | BPF_ADD;
pub const SUB64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_SUB;
pub const SUB64_REG : u8 = BPF_ALU64 | BPF_X | BPF_SUB;
pub const MUL64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_MUL;
pub const MUL64_REG : u8 = BPF_ALU64 | BPF_X | BPF_MUL;
pub const DIV64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_DIV;
pub const DIV64_REG : u8 = BPF_ALU64 | BPF_X | BPF_DIV;
pub const OR64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_OR;
pub const OR64_REG : u8 = BPF_ALU64 | BPF_X | BPF_OR;
pub const AND64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_AND;
pub const AND64_REG : u8 = BPF_ALU64 | BPF_X | BPF_AND;
pub const LSH64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_LSH;
pub const LSH64_REG : u8 = BPF_ALU64 | BPF_X | BPF_LSH;
pub const RSH64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_RSH;
pub const RSH64_REG : u8 = BPF_ALU64 | BPF_X | BPF_RSH;
pub const NEG64 : u8 = BPF_ALU64 | BPF_NEG;
pub const MOD64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_MOD;
pub const MOD64_REG : u8 = BPF_ALU64 | BPF_X | BPF_MOD;
pub const XOR64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_XOR;
pub const XOR64_REG : u8 = BPF_ALU64 | BPF_X | BPF_XOR;
pub const MOV64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_MOV;
pub const MOV64_REG : u8 = BPF_ALU64 | BPF_X | BPF_MOV;
pub const ARSH64_IMM : u8 = BPF_ALU64 | BPF_K | BPF_ARSH;
pub const ARSH64_REG : u8 = BPF_ALU64 | BPF_X | BPF_ARSH;
pub const JA : u8 = BPF_JMP | BPF_JA;
pub const JEQ_IMM : u8 = BPF_JMP | BPF_K | BPF_JEQ;
pub const JEQ_REG : u8 = BPF_JMP | BPF_X | BPF_JEQ;
pub const JGT_IMM : u8 = BPF_JMP | BPF_K | BPF_JGT;
pub const JGT_REG : u8 = BPF_JMP | BPF_X | BPF_JGT;
pub const JGE_IMM : u8 = BPF_JMP | BPF_K | BPF_JGE;
pub const JGE_REG : u8 = BPF_JMP | BPF_X | BPF_JGE;
pub const JLT_IMM : u8 = BPF_JMP | BPF_K | BPF_JLT;
pub const JLT_REG : u8 = BPF_JMP | BPF_X | BPF_JLT;
pub const JLE_IMM : u8 = BPF_JMP | BPF_K | BPF_JLE;
pub const JLE_REG : u8 = BPF_JMP | BPF_X | BPF_JLE;
pub const JSET_IMM : u8 = BPF_JMP | BPF_K | BPF_JSET;
pub const JSET_REG : u8 = BPF_JMP | BPF_X | BPF_JSET;
pub const JNE_IMM : u8 = BPF_JMP | BPF_K | BPF_JNE;
pub const JNE_REG : u8 = BPF_JMP | BPF_X | BPF_JNE;
pub const JSGT_IMM : u8 = BPF_JMP | BPF_K | BPF_JSGT;
pub const JSGT_REG : u8 = BPF_JMP | BPF_X | BPF_JSGT;
pub const JSGE_IMM : u8 = BPF_JMP | BPF_K | BPF_JSGE;
pub const JSGE_REG : u8 = BPF_JMP | BPF_X | BPF_JSGE;
pub const JSLT_IMM : u8 = BPF_JMP | BPF_K | BPF_JSLT;
pub const JSLT_REG : u8 = BPF_JMP | BPF_X | BPF_JSLT;
pub const JSLE_IMM : u8 = BPF_JMP | BPF_K | BPF_JSLE;
pub const JSLE_REG : u8 = BPF_JMP | BPF_X | BPF_JSLE;
pub const CALL_IMM : u8 = BPF_JMP | BPF_CALL;
pub const CALL_REG : u8 = BPF_JMP | BPF_X | BPF_CALL;
pub const EXIT : u8 = BPF_JMP | BPF_EXIT;
pub trait UserDefinedError: 'static + Error {}
#[derive(Debug, ThisError)]
pub enum EbpfError<E: UserDefinedError> {
#[error("{0}")]
UserError(
#[from]
E
),
#[error("ELF error: {0}")]
ELFError(
#[from]
ELFError
),
#[error("JIT error: {0}")]
JITError(
#[from]
JITError
),
#[error("no program or ELF set")]
NothingToExecute,
#[error("exceeded max BPF to BPF call depth of {0}")]
CallDepthExceeded(usize),
#[error("attempted to exit root call frame")]
ExitRootCallFrame,
#[error("devide by zero at instruction {0}")]
DivideByZero(usize),
#[error("attempted to execute past the end of the text segment at instruction #{0}")]
ExecutionOverrun(usize),
#[error("callx at instruction {0} attempted to call outside of the text segment to addr 0x{1:x}")]
CallOutsideTextSegment(usize, u64),
#[error("unresolved symbol at instruction #{0}")]
UnresolvedSymbol(usize),
#[error("exceeded maximum number of instructions allowed ({0})")]
ExceededMaxInstructions(u64),
#[error("JIT does not support read only data")]
ReadOnlyDataUnsupported,
#[error("program has not been JIT-compiled")]
JITNotCompiled,
#[error("invalid virtual address {0:x?}")]
InvalidVirtualAddress(u64),
#[error("out of bounds memory {0} (insn #{1}), addr {2:#x}/{3:?} \n{4}")]
AccessViolation(
String,
usize,
u64,
usize,
String),
#[error("Invalid instruction at {0}")]
InvalidInstruction(usize),
#[error("Unsupported instruction at instruction {0}")]
UnsupportedInstruction(usize),
#[error("Shift with overflow at instruction {0}")]
ShiftWithOverflow(usize),
}
pub const BPF_CLS_MASK : u8 = 0x07;
pub const BPF_ALU_OP_MASK : u8 = 0xf0;
pub type SyscallFunction<E> = fn(
u64,
u64,
u64,
u64,
u64,
&[MemoryRegion],
&[MemoryRegion],
) -> Result<u64, EbpfError<E>>;
pub trait SyscallObject<E: UserDefinedError> {
#[allow(clippy::too_many_arguments)]
fn call(
&mut self,
u64,
u64,
u64,
u64,
u64,
&[MemoryRegion],
&[MemoryRegion],
) -> Result<u64, EbpfError<E>>;
}
pub enum Syscall<'a, E: UserDefinedError> {
Function(SyscallFunction<E>),
Object(Box<dyn SyscallObject<E> + 'a>)
}
#[derive(PartialEq, Clone)]
pub struct Insn {
pub opc: u8,
pub dst: u8,
pub src: u8,
pub off: i16,
pub imm: i32,
}
impl fmt::Debug for Insn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Insn {{ opc: 0x{:02x?}, dst: {}, src: {}, off: 0x{:04x?}, imm: 0x{:08x?} }}",
self.opc, self.dst, self.src, self.off, self.imm)
}
}
impl Insn {
pub fn to_array(&self) -> [u8;INSN_SIZE] {
[
self.opc,
self.src.wrapping_shl(4) | self.dst,
(self.off & 0xff) as u8,
self.off.wrapping_shr(8) as u8,
(self.imm & 0xff) as u8,
(self.imm & 0xff_00).wrapping_shr(8) as u8,
(self.imm as u32 & 0xff_00_00).wrapping_shr(16) as u8,
(self.imm as u32 & 0xff_00_00_00).wrapping_shr(24) as u8,
]
}
pub fn to_vec(&self) -> Vec<u8> {
vec![
self.opc,
self.src.wrapping_shl(4) | self.dst,
(self.off & 0xff) as u8,
self.off.wrapping_shr(8) as u8,
(self.imm & 0xff) as u8,
(self.imm & 0xff_00).wrapping_shr(8) as u8,
(self.imm as u32 & 0xff_00_00).wrapping_shr(16) as u8,
(self.imm as u32 & 0xff_00_00_00).wrapping_shr(24) as u8,
]
}
}
pub fn get_insn(prog: &[u8], idx: usize) -> Insn {
if (idx + 1) * INSN_SIZE > prog.len() {
panic!("Error: cannot reach instruction at index {:?} in program containing {:?} bytes",
idx, prog.len());
}
get_insn_unchecked(prog, idx)
}
pub fn get_insn_unchecked(prog: &[u8], idx: usize) -> Insn {
Insn {
opc: prog[INSN_SIZE * idx],
dst: prog[INSN_SIZE * idx + 1] & 0x0f,
src: (prog[INSN_SIZE * idx + 1] & 0xf0) >> 4,
off: LittleEndian::read_i16(&prog[(INSN_SIZE * idx + 2) .. ]),
imm: LittleEndian::read_i32(&prog[(INSN_SIZE * idx + 4) .. ]),
}
}
pub fn to_insn_vec(prog: &[u8]) -> Vec<Insn> {
if prog.len() % INSN_SIZE != 0 {
panic!("Error: eBPF program length must be a multiple of {:?} octets",
INSN_SIZE);
}
let mut res = vec![];
let mut insn_ptr:usize = 0;
while insn_ptr * INSN_SIZE < prog.len() {
let insn = get_insn(prog, insn_ptr);
res.push(insn);
insn_ptr += 1;
};
res
}
pub fn hash_symbol_name(name: &[u8]) -> u32 {
let mut hasher = Murmur3Hasher::default();
Hash::hash_slice(name, &mut hasher);
hasher.finish()
}