diff --git "a/submissions\\Rishik-Y/Cargo.toml" "b/submissions\\Rishik-Y/Cargo.toml" new file mode 100644 index 0000000..7b6124e --- /dev/null +++ "b/submissions\\Rishik-Y/Cargo.toml" @@ -0,0 +1,2 @@ +[dependencies] +clap = { version = "4.0", features = ["derive"] } diff --git "a/submissions\\Rishik-Y/Instructions_to_run.txt" "b/submissions\\Rishik-Y/Instructions_to_run.txt" new file mode 100644 index 0000000..08411d2 --- /dev/null +++ "b/submissions\\Rishik-Y/Instructions_to_run.txt" @@ -0,0 +1,37 @@ +************** To Build this Rust Program ***************** + +Use the cargo command: +cargo run -- --config --address
--mode --operation + +Example Command: + + Config file: config.txt + Address: 0xdeadbeef + Mode: M (Machine mode) + Operation: R (Read) + +Use the cargo command (example): +cargo run -- --config config.txt --address 0xdeadbeef --mode M --operation R + +Explanation of the Syntax + cargo run: Builds and runs the Rust program. + --: Separates cargo arguments from your program's arguments. + --config: Specifies the path to the PMP configuration file. + --address: Specifies the physical address in hexadecimal (must start with 0x). + --mode: Specifies the privilege mode (M, S, or U). + --operation: Specifies the operation (R, W, or X). + +Auto-Generated Help! + +If you're unsure about the arguments, you can always check the help message: +cargo run -- --help + +Should display this: +Usage: pmp_checker --config --address
--mode --operation + +Options: + -c, --config Path to PMP configuration file + -a, --address
Physical address in hexadecimal (0x prefix) + -m, --mode Privilege mode (M, S, U) + -o, --operation Operation (R, W, X) + -h, --help Print help diff --git "a/submissions\\Rishik-Y/pmp_checker.rs" "b/submissions\\Rishik-Y/pmp_checker.rs" new file mode 100644 index 0000000..66d268b --- /dev/null +++ "b/submissions\\Rishik-Y/pmp_checker.rs" @@ -0,0 +1,186 @@ +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::error::Error; +use clap::Parser; + +const PMP_ENTRIES: usize = 64; + +#[derive(Debug)] +struct PmpConfig { + read: bool, + write: bool, + exec: bool, + mode: u8, + locked: bool, +} + +impl PmpConfig { + fn from_byte(byte: u8) -> Self { + PmpConfig { + read: (byte & 0x01) != 0, + write: (byte & 0x02) != 0, + exec: (byte & 0x04) != 0, + mode: (byte & 0x18) >> 3, + locked: (byte & 0x80) != 0, + } + } +} + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// Path to PMP configuration file + #[arg(short, long)] + config: String, + + /// Physical address in hexadecimal (0x prefix) + #[arg(short, long)] + address: String, + + /// Privilege mode (M, S, U) + #[arg(short, long)] + mode: char, + + /// Operation (R, W, X) + #[arg(short, long)] + operation: char, +} + +#[derive(Debug)] +enum AccessResult { + Allowed, + Denied, +} + +fn main() -> Result<(), Box> { + let args = Args::parse(); + + let address = parse_address(&args.address)?; + let mode = validate_mode(args.mode)?; + let operation = validate_operation(args.operation)?; + + let pmp_entries = load_pmp_config(&args.config)?; + let result = check_access(&pmp_entries, address, mode, operation); + + println!("Access {}", match result { + AccessResult::Allowed => "allowed", + AccessResult::Denied => "denied", + }); + + Ok(()) +} + +fn parse_address(addr_str: &str) -> Result> { + if !addr_str.starts_with("0x") { + return Err("Address must start with 0x".into()); + } + Ok(u64::from_str_radix(&addr_str[2..], 16)?) +} + +fn validate_mode(mode: char) -> Result> { + match mode.to_ascii_uppercase() { + 'M' | 'S' | 'U' => Ok(mode.to_ascii_uppercase()), + _ => Err("Invalid privilege mode".into()), + } +} + +fn validate_operation(op: char) -> Result> { + match op.to_ascii_uppercase() { + 'R' | 'W' | 'X' => Ok(op.to_ascii_uppercase()), + _ => Err("Invalid operation".into()), + } +} + +fn load_pmp_config(path: &str) -> Result, Box> { + let file = File::open(path)?; + let reader = BufReader::new(file); + + let mut configs = Vec::with_capacity(PMP_ENTRIES); + let mut addresses = Vec::with_capacity(PMP_ENTRIES); + + for (i, line) in reader.lines().enumerate() { + let line = line?; + let value = u64::from_str_radix(&line.trim_start_matches("0x"), 16)?; + + if i < PMP_ENTRIES { + configs.push(PmpConfig::from_byte(value as u8)); + } else if i < PMP_ENTRIES * 2 { + addresses.push(value); + } else { + break; + } + } + + Ok(configs.into_iter().zip(addresses.into_iter()).collect()) +} + +fn check_access(entries: &[(PmpConfig, u64)], addr: u64, mode: char, op: char) -> AccessResult { + for (i, (config, pmp_addr)) in entries.iter().enumerate() { + if config.mode == 0 { + continue; + } + + let (start, end) = match config.mode { + 1 => tor_range(i, entries, *pmp_addr), + 2 => na4_range(*pmp_addr), + 3 => napot_range(*pmp_addr), + _ => continue, + }; + + if addr >= start && addr < end { + return evaluate_permission(config, mode, op); + } + } + + if mode == 'M' { + AccessResult::Allowed + } else { + AccessResult::Denied + } +} + +fn tor_range(index: usize, entries: &[(PmpConfig, u64)], current_addr: u64) -> (u64, u64) { + let prev_addr = if index > 0 { + entries[index - 1].1 + } else { + 0 + }; + (prev_addr, current_addr) +} + +fn na4_range(addr: u64) -> (u64, u64) { + let base = addr & !0x3; + (base, base + 4) +} + +fn napot_range(addr: u64) -> (u64, u64) { + let mask = (addr | (addr.wrapping_sub(1))) ^ u64::MAX; + let base = addr & mask; + let size = mask.wrapping_add(1); + (base, base + size) +} + +fn evaluate_permission(config: &PmpConfig, mode: char, op: char) -> AccessResult { + if mode == 'M' && !config.locked { + return AccessResult::Allowed; + } + + let has_permission = match op { + 'R' => config.read, + 'W' => config.write, + 'X' => config.exec, + _ => false, + }; + + if has_permission { + AccessResult::Allowed + } else { + AccessResult::Denied + } +} + +///Hello Derek Hower (or whoever is reviewing), +///I Have a single Laptop and I am in the middle fixing a bug in NixOS which doesnt let me build +///directly. Normally i would somehow fixed this issue asap however my exams are going on and didnt +///wanna break my only laptop. I made and ran this code in online compiler however i cant be sure +///whether it will work, I hope My logic is correct at the very least diff --git "a/submissions\\Rishik-Y/pmp_configuration.txt" "b/submissions\\Rishik-Y/pmp_configuration.txt" new file mode 100644 index 0000000..39f3fb8 --- /dev/null +++ "b/submissions\\Rishik-Y/pmp_configuration.txt" @@ -0,0 +1,128 @@ +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x07 +0x01 +0x03 +0x00 +0x1000 +0x2000 +0x3000 +0x4000 +0x5000 +0x6000 +0x7000 +0x8000 +0x9000 +0x10000 +0x11000 +0x12000 +0x13000 +0x14000 +0x15000 +0x16000 +0x17000 +0x18000 +0x19000 +0x20000 +0x21000 +0x22000 +0x23000 +0x24000 +0x25000 +0x26000 +0x27000 +0x28000 +0x29000 +0x30000 +0x31000 +0x32000 +0x33000 +0x34000 +0x35000 +0x36000 +0x37000 +0x38000 +0x39000 +0x40000 +0x41000 +0x42000 +0x43000 +0x44000 +0x45000 +0x46000 +0x47000 +0x48000 +0x49000 +0x50000 +0x51000 +0x52000 +0x53000 +0x54000 +0x55000 +0x56000 +0x57000 +0x58000 +0x59000 +0x60000 +0x61000 +0x62000 +0x63000 +0x64000