Skip to content

Commit

Permalink
tunable stack depth and frame size (#102)
Browse files Browse the repository at this point in the history
* tunable stack depth and frame size

* fix windows clippy
  • Loading branch information
jackcmay authored Oct 8, 2020
1 parent 076860e commit 61b76ef
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 189 deletions.
10 changes: 7 additions & 3 deletions examples/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
// copied, modified, or distributed except according to those terms.

extern crate solana_rbpf;
use solana_rbpf::{syscalls, user_error::UserError, vm::EbpfVm};
use solana_rbpf::{
syscalls,
user_error::UserError,
vm::{Config, EbpfVm},
};

// The main objectives of this example is to show:
//
Expand Down Expand Up @@ -39,7 +43,7 @@ fn main() {

// Create a VM: this one takes no data. Load prog1 in it.
let executable = EbpfVm::<UserError>::create_executable_from_text_bytes(prog1, None).unwrap();
let mut vm = EbpfVm::<UserError>::new(executable.as_ref()).unwrap();
let mut vm = EbpfVm::<UserError>::new(executable.as_ref(), Config::default()).unwrap();
// Execute prog1.
assert_eq!(vm.execute_program(&[], &[], &[]).unwrap(), 0x3);

Expand All @@ -51,7 +55,7 @@ fn main() {
// reimplement uptime in eBPF, in Rust. Because why not.

let executable = EbpfVm::<UserError>::create_executable_from_text_bytes(prog2, None).unwrap();
let mut vm = EbpfVm::<UserError>::new(executable.as_ref()).unwrap();
let mut vm = EbpfVm::<UserError>::new(executable.as_ref(), Config::default()).unwrap();
vm.register_syscall(syscalls::BPF_KTIME_GETNS_IDX, syscalls::bpf_time_getns)
.unwrap();

Expand Down
10 changes: 0 additions & 10 deletions src/call_frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ use crate::{
memory_region::MemoryRegion,
};

/// Stack for the eBPF stack, in bytes.
pub const CALL_FRAME_SIZE: usize = 4_096; // !! Warning: if you change stack size here also change warning in llvm (BPF_RegisterInfo.cpp)
/// Max BPF to BPF call depth
pub const MAX_CALL_DEPTH: usize = 20;

/// One call frame
#[derive(Clone, Debug)]
struct CallFrame {
Expand All @@ -29,11 +24,6 @@ pub struct CallFrames {
max_frame: usize,
frames: Vec<CallFrame>,
}
impl Default for CallFrames {
fn default() -> Self {
CallFrames::new(MAX_CALL_DEPTH, CALL_FRAME_SIZE)
}
}
impl CallFrames {
/// New call frame, depth indicates maximum call depth
pub fn new(depth: usize, size: usize) -> Self {
Expand Down
14 changes: 8 additions & 6 deletions src/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::fmt::Formatter;
use std::fmt::Error as FormatterError;
use std::ops::{Index, IndexMut};

use crate::{vm::JitProgram, error::UserDefinedError, vm::Syscall, call_frames::CALL_FRAME_SIZE, ebpf::{self}};
use crate::{error::UserDefinedError, vm::{Config, JitProgram, Syscall}, ebpf::{self}};
use thiserror::Error;

extern crate libc;
Expand Down Expand Up @@ -438,10 +438,11 @@ struct JitMemory<'a> {
pc_locs: Vec<usize>,
special_targets: HashMap<isize, usize>,
jumps: Vec<Jump>,
config: Config,
}

impl<'a> JitMemory<'a> {
fn new(_num_pages: usize) -> JitMemory<'a> {
fn new(_num_pages: usize, _config: Config) -> JitMemory<'a> {
#[cfg(windows)]
{
panic!("JIT not supported on windows");
Expand All @@ -464,6 +465,7 @@ impl<'a> JitMemory<'a> {
pc_locs: vec![],
jumps: vec![],
special_targets: HashMap::new(),
config: _config,
}
}

Expand Down Expand Up @@ -492,7 +494,7 @@ impl<'a> JitMemory<'a> {
emit_mov(self, RSP, map_register(10));

// Allocate stack space
emit_alu64_imm32(self, 0x81, 5, RSP, CALL_FRAME_SIZE as i32);
emit_alu64_imm32(self, 0x81, 5, RSP, self.config.stack_frame_size as i32);

self.pc_locs = vec![0; prog.len() / ebpf::INSN_SIZE + 1];

Expand Down Expand Up @@ -799,7 +801,7 @@ impl<'a> JitMemory<'a> {
}

// Deallocate stack space
emit_alu64_imm32(self, 0x81, 0, RSP, CALL_FRAME_SIZE as i32);
emit_alu64_imm32(self, 0x81, 0, RSP, self.config.stack_frame_size as i32);

emit_pop(self, R15);
emit_pop(self, R14);
Expand Down Expand Up @@ -885,12 +887,12 @@ impl<'a> std::fmt::Debug for JitMemory<'a> {
}

// In the end, this is the only thing we export
pub fn compile<'a, E: UserDefinedError>(prog: &'a [u8], syscalls: &HashMap<u32, Syscall<'a, E>>)
pub fn compile<'a, E: UserDefinedError>(prog: &'a [u8], syscalls: &HashMap<u32, Syscall<'a, E>>, config: Config)
-> Result<JitProgram, JITError> {

// TODO: check how long the page must be to be sure to support an eBPF program of maximum
// possible length
let mut jit = JitMemory::new(1);
let mut jit = JitMemory::new(1, config);
jit.jit_compile(prog, syscalls)?;
jit.resolve_jumps()?;

Expand Down
52 changes: 37 additions & 15 deletions src/vm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Derived from uBPF <https://github.com/iovisor/ubpf>
// Ddeerived from uBPF <https://github.com/iovisor/ubpf>
// Copyright 2015 Big Switch Networks, Inc
// (uBPF: VM architecture, parts of the interpreter, originally in C)
// Copyright 2016 6WIND S.A. <[email protected]>
Expand Down Expand Up @@ -91,12 +91,29 @@ impl InstructionMeter for DefaultInstructionMeter {
}
}

/// VM configuration settings
#[derive(Clone, Copy)]
pub struct Config {
/// Maximum call depth
pub max_call_depth: usize,
/// Size of a stack frame in bytes, must match the size specified in the LLVM BPF backend
pub stack_frame_size: usize,
}
impl Default for Config {
fn default() -> Self {
Self {
max_call_depth: 20,
stack_frame_size: 4_096,
}
}
}

/// A virtual machine to run eBPF program.
///
/// # Examples
///
/// ```
/// use solana_rbpf::{vm::EbpfVm, user_error::UserError};
/// use solana_rbpf::{vm::{EbpfVm, Config}, user_error::UserError};
///
/// let prog = &[
/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
Expand All @@ -107,7 +124,7 @@ impl InstructionMeter for DefaultInstructionMeter {
///
/// // Instantiate a VM.
/// let executable = EbpfVm::<UserError>::create_executable_from_text_bytes(prog, None).unwrap();
/// let mut vm = EbpfVm::<UserError>::new(executable.as_ref()).unwrap();
/// let mut vm = EbpfVm::<UserError>::new(executable.as_ref(), Config::default()).unwrap();
///
/// // Provide a reference to the packet data.
/// let res = vm.execute_program(mem, &[], &[]).unwrap();
Expand All @@ -119,6 +136,7 @@ pub struct EbpfVm<'a, E: UserDefinedError> {
syscalls: HashMap<u32, Syscall<'a, E>>,
last_insn_count: u64,
total_insn_count: u64,
config: Config,
}

impl<'a, E: UserDefinedError> EbpfVm<'a, E> {
Expand All @@ -128,23 +146,27 @@ impl<'a, E: UserDefinedError> EbpfVm<'a, E> {
/// # Examples
///
/// ```
/// use solana_rbpf::{vm::EbpfVm, user_error::UserError};
/// use solana_rbpf::{vm::{EbpfVm, Config}, user_error::UserError};
///
/// let prog = &[
/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
/// ];
///
/// // Instantiate a VM.
/// let executable = EbpfVm::<UserError>::create_executable_from_text_bytes(prog, None).unwrap();
/// let mut vm = EbpfVm::<UserError>::new(executable.as_ref()).unwrap();
/// let mut vm = EbpfVm::<UserError>::new(executable.as_ref(), Config::default()).unwrap();
/// ```
pub fn new(executable: &'a dyn Executable<E>) -> Result<EbpfVm<'a, E>, EbpfError<E>> {
pub fn new(
executable: &'a dyn Executable<E>,
config: Config,
) -> Result<EbpfVm<'a, E>, EbpfError<E>> {
Ok(EbpfVm {
executable,
jit: None,
syscalls: HashMap::new(),
last_insn_count: 0,
total_insn_count: 0,
config,
})
}

Expand Down Expand Up @@ -187,7 +209,7 @@ impl<'a, E: UserDefinedError> EbpfVm<'a, E> {
/// # Examples
///
/// ```
/// use solana_rbpf::{vm::EbpfVm, syscalls::bpf_trace_printf, user_error::UserError};
/// use solana_rbpf::{vm::{EbpfVm, Config}, syscalls::bpf_trace_printf, user_error::UserError};
///
/// // This program was compiled with clang, from a C program containing the following single
/// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);`
Expand All @@ -206,7 +228,7 @@ impl<'a, E: UserDefinedError> EbpfVm<'a, E> {
///
/// // Instantiate a VM.
/// let executable = EbpfVm::<UserError>::create_executable_from_text_bytes(prog, None).unwrap();
/// let mut vm = EbpfVm::<UserError>::new(executable.as_ref()).unwrap();
/// let mut vm = EbpfVm::<UserError>::new(executable.as_ref(), Config::default()).unwrap();
///
/// // Register a syscall.
/// // On running the program this syscall will print the content of registers r3, r4 and r5 to
Expand Down Expand Up @@ -265,7 +287,7 @@ impl<'a, E: UserDefinedError> EbpfVm<'a, E> {
/// # Examples
///
/// ```
/// use solana_rbpf::{vm::EbpfVm, user_error::UserError};
/// use solana_rbpf::{vm::{EbpfVm, Config}, user_error::UserError};
///
/// let prog = &[
/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
Expand All @@ -276,7 +298,7 @@ impl<'a, E: UserDefinedError> EbpfVm<'a, E> {
///
/// // Instantiate a VM.
/// let executable = EbpfVm::<UserError>::create_executable_from_text_bytes(prog, None).unwrap();
/// let mut vm = EbpfVm::<UserError>::new(executable.as_ref()).unwrap();
/// let mut vm = EbpfVm::<UserError>::new(executable.as_ref(), Config::default()).unwrap();
///
/// // Provide a reference to the packet data.
/// let res = vm.execute_program(mem, &[], &[]).unwrap();
Expand Down Expand Up @@ -324,7 +346,7 @@ impl<'a, E: UserDefinedError> EbpfVm<'a, E> {
) -> Result<u64, EbpfError<E>> {
const U32MAX: u64 = u32::MAX as u64;

let mut frames = CallFrames::default();
let mut frames = CallFrames::new(self.config.max_call_depth, self.config.stack_frame_size);
let mut ro_regions = Vec::new();
let mut rw_regions = Vec::new();
ro_regions.extend_from_slice(granted_ro_regions);
Expand Down Expand Up @@ -704,7 +726,7 @@ impl<'a, E: UserDefinedError> EbpfVm<'a, E> {
_ => {
debug!("BPF instructions executed: {:?}", self.total_insn_count);
debug!(
"Max frame depth reached: {:?}",
"Max call depth reached: {:?}",
frames.get_max_frame_index()
);
return Ok(reg[0]);
Expand Down Expand Up @@ -741,15 +763,15 @@ impl<'a, E: UserDefinedError> EbpfVm<'a, E> {
/// # Examples
///
/// ```
/// use solana_rbpf::{vm::EbpfVm, user_error::UserError};
/// use solana_rbpf::{vm::{EbpfVm, Config}, user_error::UserError};
///
/// let prog = &[
/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
/// ];
///
/// // Instantiate a VM.
/// let executable = EbpfVm::<UserError>::create_executable_from_text_bytes(prog, None).unwrap();
/// let mut vm = EbpfVm::<UserError>::new(executable.as_ref()).unwrap();
/// let mut vm = EbpfVm::<UserError>::new(executable.as_ref(), Config::default()).unwrap();
///
/// # #[cfg(not(windows))]
/// vm.jit_compile();
Expand All @@ -763,7 +785,7 @@ impl<'a, E: UserDefinedError> EbpfVm<'a, E> {
bytes
};

self.jit = Some(jit::compile(prog, &self.syscalls)?);
self.jit = Some(jit::compile(prog, &self.syscalls, self.config)?);
Ok(())
}

Expand Down
Loading

0 comments on commit 61b76ef

Please sign in to comment.