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 1/3] 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, + ); + } } } } From 19cea006b185cdedaaedf043807ac65f7eafc84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Thu, 1 Dec 2022 20:46:41 +0100 Subject: [PATCH 2/3] Factors out common code. --- src/jit.rs | 213 +++++++++++++++++++++++++++-------------------------- 1 file changed, 108 insertions(+), 105 deletions(-) diff --git a/src/jit.rs b/src/jit.rs index bc18918a..e49379c2 100644 --- a/src/jit.rs +++ b/src/jit.rs @@ -12,15 +12,17 @@ #[cfg(not(target_os = "windows"))] extern crate libc; +#[cfg(not(target_os = "windows"))] +use libc::c_void; #[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, + um::errhandlingapi::GetLastError, + um::memoryapi::{VirtualAlloc, VirtualFree, VirtualProtect}, + um::sysinfoapi::{GetSystemInfo, SYSTEM_INFO}, + um::winnt, }; use rand::{rngs::SmallRng, Rng, SeedableRng}; @@ -96,62 +98,111 @@ macro_rules! winapi_error_guard { }}; } -fn round_to_page_size(value: usize, page_size: usize) -> usize { +pub fn get_system_page_size() -> usize { + #[cfg(not(target_os = "windows"))] + unsafe { + libc::sysconf(libc::_SC_PAGESIZE) as usize + } + #[cfg(target_os = "windows")] + unsafe { + let mut system_info: SYSTEM_INFO = mem::zeroed(); + GetSystemInfo(&mut system_info); + system_info.dwPageSize as usize + } +} + +pub fn round_to_page_size(value: usize, page_size: usize) -> usize { (value + page_size - 1) / page_size * page_size } +pub unsafe fn allocate_pages(size_in_bytes: usize) -> Result<*mut u8, EbpfError> { + let mut raw: *mut c_void = std::ptr::null_mut(); + #[cfg(not(target_os = "windows"))] + libc_error_guard!( + mmap, + &mut raw, + size_in_bytes, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, + 0, + 0, + ); + #[cfg(target_os = "windows")] + winapi_error_guard!( + VirtualAlloc, + &mut raw, + size_in_bytes, + winnt::MEM_RESERVE | winnt::MEM_COMMIT, + winnt::PAGE_READWRITE, + ); + Ok(raw as *mut u8) +} + +pub unsafe fn free_pages(raw: *mut u8, size_in_bytes: usize) -> Result<(), EbpfError> { + #[cfg(not(target_os = "windows"))] + libc_error_guard!(munmap, raw as *mut _, size_in_bytes); + #[cfg(target_os = "windows")] + winapi_error_guard!( + VirtualFree, + raw as *mut _, + size_in_bytes, + winnt::MEM_RELEASE, // winnt::MEM_DECOMMIT + ); + Ok(()) +} + +pub unsafe fn protect_pages( + raw: *mut u8, + size_in_bytes: usize, + executable_flag: bool, +) -> Result<(), EbpfError> { + #[cfg(not(target_os = "windows"))] + { + libc_error_guard!( + mprotect, + raw as *mut _, + size_in_bytes, + if executable_flag { + libc::PROT_EXEC | libc::PROT_READ + } else { + libc::PROT_READ + }, + ); + } + #[cfg(target_os = "windows")] + { + let mut old: minwindef::DWORD = 0; + let ptr_old: *mut minwindef::DWORD = &mut old; + winapi_error_guard!( + VirtualProtect, + raw as *mut _, + size_in_bytes, + if executable_flag { + winnt::PAGE_EXECUTE_READ + } else { + winnt::PAGE_READONLY + }, + ptr_old, + ); + } + Ok(()) +} + impl JitProgram { fn new(pc: usize, code_size: usize) -> Result { - #[cfg(not(target_os = "windows"))] + let page_size = get_system_page_size(); + 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); unsafe { - let page_size = libc::sysconf(libc::_SC_PAGESIZE) 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 libc::c_void = std::ptr::null_mut(); - libc_error_guard!( - mmap, - &mut raw, - pc_loc_table_size + over_allocated_code_size, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, - 0, - 0, - ); + let raw = allocate_pages(pc_loc_table_size + over_allocated_code_size)?; Ok(Self { - page_size, - pc_section: std::slice::from_raw_parts_mut(raw as *mut usize, pc), - text_section: std::slice::from_raw_parts_mut( - raw.add(pc_loc_table_size) as *mut u8, - over_allocated_code_size, - ), - _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, ), + _marker: PhantomData::default(), }) } } @@ -171,59 +222,20 @@ impl JitProgram { 0xcc, code_size - text_section_usage, ); - } - #[cfg(not(target_os = "windows"))] - unsafe { if over_allocated_code_size > code_size { - libc_error_guard!( - munmap, - raw.add(pc_loc_table_size).add(code_size) as *mut _, - over_allocated_code_size - code_size - ); - } - self.text_section = - std::slice::from_raw_parts_mut(raw.add(pc_loc_table_size), text_section_usage); - libc_error_guard!( - mprotect, - self.pc_section.as_mut_ptr() as *mut _, - pc_loc_table_size, - libc::PROT_READ - ); - libc_error_guard!( - mprotect, - self.text_section.as_mut_ptr() as *mut _, - code_size, - 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 _, + free_pages( + raw.add(pc_loc_table_size).add(code_size), 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 _, + protect_pages( + self.pc_section.as_mut_ptr() as *mut u8, 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, - ); + false, + )?; + protect_pages(self.text_section.as_mut_ptr(), code_size, true)?; } Ok(()) } @@ -289,19 +301,10 @@ impl Drop for JitProgram { let pc_loc_table_size = round_to_page_size(self.pc_section.len() * 8, self.page_size); let code_size = round_to_page_size(self.text_section.len(), self.page_size); if pc_loc_table_size + code_size > 0 { - #[cfg(not(target_os = "windows"))] - unsafe { - libc::munmap( - self.pc_section.as_ptr() as *mut _, - pc_loc_table_size + code_size, - ); - } - #[cfg(target_os = "windows")] unsafe { - VirtualFree( - self.pc_section.as_ptr() as *mut _, + let _ = free_pages( + self.pc_section.as_ptr() as *mut u8, pc_loc_table_size + code_size, - winnt::MEM_RELEASE, ); } } From 035556c84dd46e75f2a4a3e6e22481b560e56ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Thu, 1 Dec 2022 20:57:19 +0100 Subject: [PATCH 3/3] Moves memory_management into its own file. --- src/jit.rs | 154 +----------------------------------- src/lib.rs | 2 + src/memory_management.rs | 166 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 151 deletions(-) create mode 100644 src/memory_management.rs diff --git a/src/jit.rs b/src/jit.rs index e49379c2..b560128c 100644 --- a/src/jit.rs +++ b/src/jit.rs @@ -10,21 +10,6 @@ // 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(not(target_os = "windows"))] -use libc::c_void; - -#[cfg(target_os = "windows")] -use winapi::{ - ctypes::c_void, - shared::minwindef, - um::errhandlingapi::GetLastError, - um::memoryapi::{VirtualAlloc, VirtualFree, VirtualProtect}, - um::sysinfoapi::{GetSystemInfo, SYSTEM_INFO}, - um::winnt, -}; - use rand::{rngs::SmallRng, Rng, SeedableRng}; use std::{fmt::Debug, marker::PhantomData, mem, ptr}; @@ -32,6 +17,9 @@ use crate::{ ebpf::{self, FIRST_SCRATCH_REG, FRAME_PTR_REG, INSN_SIZE, SCRATCH_REGS, STACK_PTR_REG}, elf::Executable, error::EbpfError, + memory_management::{ + allocate_pages, free_pages, get_system_page_size, protect_pages, round_to_page_size, + }, memory_region::{AccessType, MemoryMapping}, vm::{Config, ContextObject, ProgramResult, RuntimeEnvironment, SyscallFunction}, x86::*, @@ -52,142 +40,6 @@ pub struct JitProgram { _marker: PhantomData, } -#[cfg(not(target_os = "windows"))] -macro_rules! libc_error_guard { - (succeeded?, mmap, $addr:expr, $($arg:expr),*) => {{ - *$addr = libc::mmap(*$addr, $($arg),*); - *$addr != libc::MAP_FAILED - }}; - (succeeded?, $function:ident, $($arg:expr),*) => { - libc::$function($($arg),*) == 0 - }; - ($function:ident, $($arg:expr),* $(,)?) => {{ - const RETRY_COUNT: usize = 3; - for i in 0..RETRY_COUNT { - if libc_error_guard!(succeeded?, $function, $($arg),*) { - break; - } else if i + 1 == RETRY_COUNT { - let args = vec![$(format!("{:?}", $arg)),*]; - #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] - let errno = *libc::__error(); - #[cfg(any(target_os = "android", target_os = "netbsd", target_os = "openbsd"))] - let errno = *libc::__errno(); - #[cfg(target_os = "linux")] - let errno = *libc::__errno_location(); - return Err(EbpfError::LibcInvocationFailed(stringify!($function), args, errno)); - } - } - }}; -} - -#[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)); - } - }}; -} - -pub fn get_system_page_size() -> usize { - #[cfg(not(target_os = "windows"))] - unsafe { - libc::sysconf(libc::_SC_PAGESIZE) as usize - } - #[cfg(target_os = "windows")] - unsafe { - let mut system_info: SYSTEM_INFO = mem::zeroed(); - GetSystemInfo(&mut system_info); - system_info.dwPageSize as usize - } -} - -pub fn round_to_page_size(value: usize, page_size: usize) -> usize { - (value + page_size - 1) / page_size * page_size -} - -pub unsafe fn allocate_pages(size_in_bytes: usize) -> Result<*mut u8, EbpfError> { - let mut raw: *mut c_void = std::ptr::null_mut(); - #[cfg(not(target_os = "windows"))] - libc_error_guard!( - mmap, - &mut raw, - size_in_bytes, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, - 0, - 0, - ); - #[cfg(target_os = "windows")] - winapi_error_guard!( - VirtualAlloc, - &mut raw, - size_in_bytes, - winnt::MEM_RESERVE | winnt::MEM_COMMIT, - winnt::PAGE_READWRITE, - ); - Ok(raw as *mut u8) -} - -pub unsafe fn free_pages(raw: *mut u8, size_in_bytes: usize) -> Result<(), EbpfError> { - #[cfg(not(target_os = "windows"))] - libc_error_guard!(munmap, raw as *mut _, size_in_bytes); - #[cfg(target_os = "windows")] - winapi_error_guard!( - VirtualFree, - raw as *mut _, - size_in_bytes, - winnt::MEM_RELEASE, // winnt::MEM_DECOMMIT - ); - Ok(()) -} - -pub unsafe fn protect_pages( - raw: *mut u8, - size_in_bytes: usize, - executable_flag: bool, -) -> Result<(), EbpfError> { - #[cfg(not(target_os = "windows"))] - { - libc_error_guard!( - mprotect, - raw as *mut _, - size_in_bytes, - if executable_flag { - libc::PROT_EXEC | libc::PROT_READ - } else { - libc::PROT_READ - }, - ); - } - #[cfg(target_os = "windows")] - { - let mut old: minwindef::DWORD = 0; - let ptr_old: *mut minwindef::DWORD = &mut old; - winapi_error_guard!( - VirtualProtect, - raw as *mut _, - size_in_bytes, - if executable_flag { - winnt::PAGE_EXECUTE_READ - } else { - winnt::PAGE_READONLY - }, - ptr_old, - ); - } - Ok(()) -} - impl JitProgram { fn new(pc: usize, code_size: usize) -> Result { let page_size = get_system_page_size(); diff --git a/src/lib.rs b/src/lib.rs index b52caa14..bd1b3069 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,8 @@ pub mod insn_builder; pub mod interpreter; #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))] mod jit; +#[cfg(feature = "jit")] +mod memory_management; pub mod memory_region; pub mod static_analysis; pub mod syscalls; diff --git a/src/memory_management.rs b/src/memory_management.rs new file mode 100644 index 00000000..76ff359f --- /dev/null +++ b/src/memory_management.rs @@ -0,0 +1,166 @@ +// Copyright 2022 Solana Maintainers +// +// Licensed under the Apache License, Version 2.0 or +// the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +#![cfg_attr(target_os = "windows", allow(dead_code))] + +use crate::error::EbpfError; + +#[cfg(not(target_os = "windows"))] +extern crate libc; +#[cfg(not(target_os = "windows"))] +use libc::c_void; + +#[cfg(target_os = "windows")] +use winapi::{ + ctypes::c_void, + shared::minwindef, + um::{ + errhandlingapi::GetLastError, + memoryapi::{VirtualAlloc, VirtualFree, VirtualProtect}, + sysinfoapi::{GetSystemInfo, SYSTEM_INFO}, + winnt, + }, +}; + +#[cfg(not(target_os = "windows"))] +macro_rules! libc_error_guard { + (succeeded?, mmap, $addr:expr, $($arg:expr),*) => {{ + *$addr = libc::mmap(*$addr, $($arg),*); + *$addr != libc::MAP_FAILED + }}; + (succeeded?, $function:ident, $($arg:expr),*) => { + libc::$function($($arg),*) == 0 + }; + ($function:ident, $($arg:expr),* $(,)?) => {{ + const RETRY_COUNT: usize = 3; + for i in 0..RETRY_COUNT { + if libc_error_guard!(succeeded?, $function, $($arg),*) { + break; + } else if i.saturating_add(1) == RETRY_COUNT { + let args = vec![$(format!("{:?}", $arg)),*]; + #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] + let errno = *libc::__error(); + #[cfg(any(target_os = "android", target_os = "netbsd", target_os = "openbsd"))] + let errno = *libc::__errno(); + #[cfg(target_os = "linux")] + let errno = *libc::__errno_location(); + return Err(EbpfError::LibcInvocationFailed(stringify!($function), args, errno)); + } + } + }}; +} + +#[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::LibcInvocationFailed(stringify!($function), args, errno as i32)); + } + }}; +} + +pub fn get_system_page_size() -> usize { + #[cfg(not(target_os = "windows"))] + unsafe { + libc::sysconf(libc::_SC_PAGESIZE) as usize + } + #[cfg(target_os = "windows")] + unsafe { + let mut system_info: SYSTEM_INFO = std::mem::zeroed(); + GetSystemInfo(&mut system_info); + system_info.dwPageSize as usize + } +} + +pub fn round_to_page_size(value: usize, page_size: usize) -> usize { + value + .saturating_add(page_size) + .saturating_sub(1) + .saturating_div(page_size) + .saturating_mul(page_size) +} + +pub unsafe fn allocate_pages(size_in_bytes: usize) -> Result<*mut u8, EbpfError> { + let mut raw: *mut c_void = std::ptr::null_mut(); + #[cfg(not(target_os = "windows"))] + libc_error_guard!( + mmap, + &mut raw, + size_in_bytes, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, + 0, + 0, + ); + #[cfg(target_os = "windows")] + winapi_error_guard!( + VirtualAlloc, + &mut raw, + size_in_bytes, + winnt::MEM_RESERVE | winnt::MEM_COMMIT, + winnt::PAGE_READWRITE, + ); + Ok(raw as *mut u8) +} + +pub unsafe fn free_pages(raw: *mut u8, size_in_bytes: usize) -> Result<(), EbpfError> { + #[cfg(not(target_os = "windows"))] + libc_error_guard!(munmap, raw as *mut _, size_in_bytes); + #[cfg(target_os = "windows")] + winapi_error_guard!( + VirtualFree, + raw as *mut _, + size_in_bytes, + winnt::MEM_RELEASE, // winnt::MEM_DECOMMIT + ); + Ok(()) +} + +pub unsafe fn protect_pages( + raw: *mut u8, + size_in_bytes: usize, + executable_flag: bool, +) -> Result<(), EbpfError> { + #[cfg(not(target_os = "windows"))] + { + libc_error_guard!( + mprotect, + raw as *mut _, + size_in_bytes, + if executable_flag { + libc::PROT_EXEC | libc::PROT_READ + } else { + libc::PROT_READ + }, + ); + } + #[cfg(target_os = "windows")] + { + let mut old: minwindef::DWORD = 0; + let ptr_old: *mut minwindef::DWORD = &mut old; + winapi_error_guard!( + VirtualProtect, + raw as *mut _, + size_in_bytes, + if executable_flag { + winnt::PAGE_EXECUTE_READ + } else { + winnt::PAGE_READONLY + }, + ptr_old, + ); + } + Ok(()) +}