From c53da506f34d66e55c3a046328dc497a6da2b1e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Thu, 1 Dec 2022 20:15:28 +0100 Subject: [PATCH] Adds the memory management on windows part of the contribution from ndrewh (Trail of Bits). See https://github.com/solana-labs/rbpf/pull/359 --- Cargo.lock | 23 ++++++++++ Cargo.toml | 7 ++-- src/jit.rs | 121 +++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 136 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09728307..44f804fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -284,6 +284,7 @@ dependencies = [ "scroll", "test_utils", "thiserror", + "winapi", ] [[package]] @@ -351,3 +352,25 @@ name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 839f04b6..4cba7ed7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,20 +22,21 @@ include = [ arbitrary = { version = "1.0", optional = true, features = ["derive"] } byteorder = "1.2" combine = "3.8.1" +gdbstub = { version = "0.6.2", optional = true } goblin = "0.5.1" hash32 = "0.2.0" libc = { version = "0.2", optional = true } log = "0.4.2" rand = { version = "0.8.5", features = ["small_rng"]} +rustc-demangle = "0.1" scroll = "0.11" thiserror = "1.0.26" -rustc-demangle = "0.1" -gdbstub = { version = "0.6.2", optional = true } +winapi = { version = "0.3", features = ["memoryapi", "sysinfoapi", "winnt", "errhandlingapi"], optional = true } [features] default = ["jit"] fuzzer-not-safe-for-production = ["arbitrary"] -jit = ["libc"] +jit = ["libc", "winapi"] debugger = ["gdbstub"] [dev-dependencies] diff --git a/src/jit.rs b/src/jit.rs index 8f8adff2..bc18918a 100644 --- a/src/jit.rs +++ b/src/jit.rs @@ -10,8 +10,19 @@ // the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. +#[cfg(not(target_os = "windows"))] extern crate libc; +#[cfg(target_os = "windows")] +use winapi::{ + um::sysinfoapi::{GetSystemInfo, SYSTEM_INFO}, + um::memoryapi::{VirtualAlloc, VirtualFree, VirtualProtect}, + um::winnt, + um::errhandlingapi::{GetLastError}, + ctypes::c_void, + shared::minwindef, +}; + use rand::{rngs::SmallRng, Rng, SeedableRng}; use std::{fmt::Debug, marker::PhantomData, mem, ptr}; @@ -48,7 +59,7 @@ macro_rules! libc_error_guard { (succeeded?, $function:ident, $($arg:expr),*) => { libc::$function($($arg),*) == 0 }; - ($function:ident, $($arg:expr),*) => {{ + ($function:ident, $($arg:expr),* $(,)?) => {{ const RETRY_COUNT: usize = 3; for i in 0..RETRY_COUNT { if libc_error_guard!(succeeded?, $function, $($arg),*) { @@ -67,12 +78,31 @@ macro_rules! libc_error_guard { }}; } +#[cfg(target_os = "windows")] +macro_rules! winapi_error_guard { + (succeeded?, VirtualAlloc, $addr:expr, $($arg:expr),*) => {{ + *$addr = VirtualAlloc(*$addr, $($arg),*); + !(*$addr).is_null() + }}; + (succeeded?, $function:ident, $($arg:expr),*) => { + $function($($arg),*) != 0 + }; + ($function:ident, $($arg:expr),* $(,)?) => {{ + if !winapi_error_guard!(succeeded?, $function, $($arg),*) { + let args = vec![$(format!("{:?}", $arg)),*]; + let errno = GetLastError(); + return Err(EbpfError::WinapiInvocationFailed(stringify!($function), args, errno)); + } + }}; +} + fn round_to_page_size(value: usize, page_size: usize) -> usize { (value + page_size - 1) / page_size * page_size } impl JitProgram { fn new(pc: usize, code_size: usize) -> Result { + #[cfg(not(target_os = "windows"))] unsafe { let page_size = libc::sysconf(libc::_SC_PAGESIZE) as usize; let pc_loc_table_size = round_to_page_size(pc * 8, page_size); @@ -85,7 +115,7 @@ impl JitProgram { libc::PROT_READ | libc::PROT_WRITE, libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, 0, - 0 + 0, ); Ok(Self { page_size, @@ -97,18 +127,53 @@ impl JitProgram { _marker: PhantomData::default(), }) } + #[cfg(target_os = "windows")] + unsafe { + let mut system_info: SYSTEM_INFO = mem::zeroed(); + GetSystemInfo(&mut system_info); + let page_size = system_info.dwPageSize as usize; + let pc_loc_table_size = round_to_page_size(pc * 8, page_size); + let over_allocated_code_size = round_to_page_size(code_size, page_size); + let mut raw: *mut c_void = std::ptr::null_mut(); + winapi_error_guard!( + VirtualAlloc, + &mut raw, + pc_loc_table_size + over_allocated_code_size, + winnt::MEM_RESERVE | winnt::MEM_COMMIT, + winnt::PAGE_READWRITE, + ); + Ok(Self { + page_size: 0, + pc_section: &mut [], + text_section: &mut [], + page_size, + pc_section: std::slice::from_raw_parts_mut(raw as *mut usize, pc), + text_section: std::slice::from_raw_parts_mut( + (raw as *mut u8).add(pc_loc_table_size), + over_allocated_code_size, + ), + }) + } } fn seal(&mut self, text_section_usage: usize) -> Result<(), EbpfError> { if self.page_size == 0 { return Ok(()); } + let raw = self.pc_section.as_ptr() as *mut u8; + let pc_loc_table_size = round_to_page_size(self.pc_section.len() * 8, self.page_size); + let over_allocated_code_size = round_to_page_size(self.text_section.len(), self.page_size); + let code_size = round_to_page_size(text_section_usage, self.page_size); + unsafe { + // Fill with debugger traps + std::ptr::write_bytes( + raw.add(pc_loc_table_size).add(text_section_usage), + 0xcc, + code_size - text_section_usage, + ); + } + #[cfg(not(target_os = "windows"))] unsafe { - let raw = self.pc_section.as_ptr() as *mut u8; - let pc_loc_table_size = round_to_page_size(self.pc_section.len() * 8, self.page_size); - let over_allocated_code_size = - round_to_page_size(self.text_section.len(), self.page_size); - let code_size = round_to_page_size(text_section_usage, self.page_size); if over_allocated_code_size > code_size { libc_error_guard!( munmap, @@ -116,11 +181,6 @@ impl JitProgram { over_allocated_code_size - code_size ); } - std::ptr::write_bytes( - raw.add(pc_loc_table_size).add(text_section_usage), - 0xcc, - code_size - text_section_usage, - ); // Fill with debugger traps self.text_section = std::slice::from_raw_parts_mut(raw.add(pc_loc_table_size), text_section_usage); libc_error_guard!( @@ -136,6 +196,35 @@ impl JitProgram { libc::PROT_EXEC | libc::PROT_READ ); } + #[cfg(target_os = "windows")] + unsafe { + if over_allocated_code_size > code_size { + winapi_error_guard!( + VirtualFree, + raw.add(pc_loc_table_size).add(code_size) as *mut _, + over_allocated_code_size - code_size, + winnt::MEM_DECOMMIT, + ); + } + self.text_section = + std::slice::from_raw_parts_mut(raw.add(pc_loc_table_size), text_section_usage); + let mut old: minwindef::DWORD = 0; + let p2old: *mut minwindef::DWORD = &mut old; + winapi_error_guard!( + VirtualProtect, + self.pc_section.as_mut_ptr() as *mut _, + pc_loc_table_size, + winnt::PAGE_READONLY, + p2old, + ); + winapi_error_guard!( + VirtualProtect, + self.text_section.as_mut_ptr() as *mut _, + code_size, + winnt::PAGE_EXECUTE_READ, + p2old, + ); + } Ok(()) } @@ -207,6 +296,14 @@ impl Drop for JitProgram { pc_loc_table_size + code_size, ); } + #[cfg(target_os = "windows")] + unsafe { + VirtualFree( + self.pc_section.as_ptr() as *mut _, + pc_loc_table_size + code_size, + winnt::MEM_RELEASE, + ); + } } } }