embive

Crates.ioembive
lib.rsembive
version0.1.0
sourcesrc
created_at2024-11-17 03:30:11.649456
updated_at2024-11-22 23:33:01.759486
descriptionA low-level sandboxing library for RISC-V bytecode
homepage
repositoryhttps://github.com/embive/embive
max_upload_size
id1450874
size430,828
Daniel Stuart (danielstuart14)

documentation

https://docs.rs/embive

README

Embive (Embedded RISC-V) Latest Version docs msrv

Embive is a low-level sandboxing library focused on the embedding of untrusted code for constrained environments.
As it interprets RISC-V bytecode, multiple languages are supported out of the box by Embive (Rust, C, C++, Zig, TinyGo, etc.).
By default, it doesn’t require external crates, dynamic memory allocation or the standard library (no_std & no_alloc).

Embive is designed for any error during execution to be recoverable, allowing the host to handle it as needed. As so, no panics should occur on release builds, despite the bytecode being executed.

Currently, it supports the RV32I[M] unprivileged instruction set (M extension enabled by default).

Templates

The following templates are available for programs that run inside Embive:

Example

use embive::{engine::{Engine, Config, SYSCALL_ARGS}, memory::Memory, register::Register};

/// A simple syscall example. Check [`engine::SyscallFn`] for more information.
fn syscall(nr: i32, args: &[i32; SYSCALL_ARGS], memory: &mut Memory) -> Result<i32, i32> {
    println!("Syscall nr: {}, Args: {:?}", nr, args);
    match nr {
        1 => Ok(args[0] + args[1]), // Add two numbers (arg[0] + arg[1])
        2 => match memory.load(args[0] as u32) { // Load from RAM (arg[0])
            Ok(val) => Ok(i32::from_le_bytes(val)), // RISC-V is little endian
            Err(_) => Err(1),
        },
        _ => Err(2),
    }
}

fn main() {
    // "10 + 20" using syscalls (load from ram and add two numbers)
    let code = &[
        0x93, 0x08, 0x20, 0x00, // li   a7, 2      (Syscall nr = 2)
        0x13, 0x05, 0x10, 0x00, // li   a0, 1      (a0 = 1)
        0x13, 0x15, 0xf5, 0x01, // slli a0, a0, 31 (a0 << 31) (0x80000000)
        0x73, 0x00, 0x00, 0x00, // ecall           (Syscall, load from arg0)
        0x93, 0x08, 0x10, 0x00, // li   a7, 1      (Syscall nr = 1)
        0x13, 0x05, 0x40, 0x01, // li   a0,20      (a0 = 20)
        0x73, 0x00, 0x00, 0x00, // ecall           (Syscall, add two args)
        0x73, 0x00, 0x10, 0x00  // ebreak          (Halt)
    ];
    let mut ram = [0; 1024];
    // Store value 10 at RAM address 0 (0x80000000)
    ram[..4].copy_from_slice(&u32::to_le_bytes(10));

    // Create engine config
    let config = Config {
        syscall_fn: Some(syscall),
        ..Default::default()
    };

    // Create engine
    let mut engine = Engine::new(code, &mut ram, config).unwrap();

    // Run it
    engine.run().unwrap();

    // Check the result
    assert_eq!(engine.registers().get(Register::A0 as usize).unwrap(), 0);
    assert_eq!(engine.registers().get(Register::A1 as usize).unwrap(), 30);
}

Roadmap

  • Fully support RV32G (RV32IMAFDZicsr_Zifencei)
    • RV32I Base Integer Instruction Set
    • M Extension (Multiplication and Division Instructions)
    • Zifencei
      • Implemented as a no-operation as it isn't applicable (Single HART, no cache, no memory-mapped devices, etc.).
    • Zicsr
      • At least the unprivileged CSRs
    • F Extension (Single-Precision Floating-Point Instructions)
    • D Extension (Double-Precision Floating-Point Instructions)
    • A Extension (Atomic Instructions)
  • System Calls
    • Function calls from interpreted to native code
  • Resource limiter
    • Yield the engine after a configurable amount of instructions are executed.
  • CI/CD
    • Incorporate more tests into the repository and create test automations for PRs
  • Bytecode optimization (AOT and JIT)
    • Allow in-place JIT and AOT compilation to a format easier to parse.
      • Less bit-shifting, faster instruction matching, etc.
    • Should be kept as close as possible to native RISC-V bytecode.
  • Callbacks
    • Function calls from native to interpreted code.
  • Macros for converting native functions to system calls / callbacks
    • Use Rust type-system instead of only allowing i32 arguments / results
  • Support C Extension (Compressed Instructions)
    • This is a maybe, but good to keep in mind while developing other features (especially AOT/JIT).

Minimum supported Rust version (MSRV)

Embive is guaranteed to compile on stable Rust 1.81 and up.

License

Embive is licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Commit count: 18

cargo fmt