use core::any::Any;
use crate::{
ebpf::{self, LOCAL_FUNCTION_STACK_SIZE},
lib::*,
StackUsageCalculator,
};
#[derive(Debug, Copy, Clone)]
pub struct StackFrame {
return_address: usize,
saved_registers: [u64; 4],
stack_usage: StackUsageType,
}
impl StackFrame {
pub const fn new() -> Self {
Self {
return_address: 0,
saved_registers: [0; 4],
stack_usage: StackUsageType::Default,
}
}
pub fn save_registers(&mut self, regs: &[u64]) {
self.saved_registers.copy_from_slice(regs);
}
pub fn get_registers(&self) -> [u64; 4] {
self.saved_registers
}
pub fn save_return_address(&mut self, address: usize) {
self.return_address = address;
}
pub fn get_return_address(&self) -> usize {
self.return_address
}
pub fn set_stack_usage(&mut self, usage: StackUsageType) {
self.stack_usage = usage;
}
pub fn get_stack_usage(&self) -> StackUsageType {
self.stack_usage
}
}
#[derive(Debug, Copy, Clone)]
pub enum StackUsageType {
Default,
Custom(u16),
}
impl StackUsageType {
pub fn stack_usage(&self) -> u16 {
match self {
StackUsageType::Default => LOCAL_FUNCTION_STACK_SIZE,
StackUsageType::Custom(size) => *size,
}
}
}
pub struct StackVerifier {
calculator: Option<StackUsageCalculator>,
data: Option<Box<dyn Any>>,
}
impl StackVerifier {
pub fn new(
stack_usage_calculator: Option<StackUsageCalculator>,
data: Option<Box<dyn Any>>,
) -> Self {
Self {
calculator: stack_usage_calculator,
data,
}
}
pub fn stack_validate(&mut self, prog: &[u8]) -> Result<StackUsage, Error> {
let mut stack_usage = HashMap::new();
let ty = self.calculate_stack_usage_for_local_func(prog, 0)?;
stack_usage.insert(0, ty);
for idx in 0..prog.len() / ebpf::INSN_SIZE {
let insn = ebpf::get_insn(prog, idx);
if insn.opc == ebpf::CALL {
let dst_insn_ptr = idx as isize + 1 + insn.imm as isize;
let ty = self.calculate_stack_usage_for_local_func(prog, dst_insn_ptr as usize)?;
stack_usage.insert(dst_insn_ptr as usize, ty);
}
}
Ok(StackUsage(stack_usage))
}
fn calculate_stack_usage_for_local_func(
&mut self,
prog: &[u8],
pc: usize,
) -> Result<StackUsageType, Error> {
let mut ty = StackUsageType::Default;
match self.calculator {
Some(calculator) => {
ty = StackUsageType::Custom(calculator(prog, pc, self.data.as_mut().unwrap()));
}
None => return Ok(ty),
}
Ok(ty)
}
}
pub struct StackUsage(HashMap<usize, StackUsageType>);
impl StackUsage {
pub fn stack_usage_for_local_func(&self, pc: usize) -> Option<StackUsageType> {
self.0.get(&pc).cloned()
}
}