memsec-0.7.0/.cargo_vcs_info.json0000644000000001360000000000100123110ustar { "git": { "sha1": "7fae5005f3233976e03507c49505858a4f2b03f4" }, "path_in_vcs": "" }memsec-0.7.0/.github/workflows/ci.yml000064400000000000000000000013161046102023000156150ustar 00000000000000name: CI on: push: branches: - master - switch-to-windows-sys pull_request: {} jobs: test: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} env: RUSTFLAGS: "-D warnings" steps: - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: nightly override: true - uses: actions/checkout@master - name: minimum feature run: | cd memsec-test cargo test --no-default-features - name: all feature run: | cd memsec-test cargo test - name: nightly run: | cd memsec-test cargo test --features nightly memsec-0.7.0/.gitignore000064400000000000000000000000221046102023000130630ustar 00000000000000target Cargo.lock memsec-0.7.0/.travis.yml000064400000000000000000000014231046102023000132120ustar 00000000000000language: rust rust: - nightly - stable cache: cargo os: - linux - osx install: - curl -L -O https://github.com/jedisct1/libsodium/releases/download/1.0.18-RELEASE/libsodium-1.0.18.tar.gz - tar xvfz libsodium-1.0.18.tar.gz - cd libsodium-1.0.18 && ./configure --prefix=$HOME/installed_libsodium && make && make install && cd .. - export PKG_CONFIG_PATH=$HOME/installed_libsodium/lib/pkgconfig:$PKG_CONFIG_PATH - export LD_LIBRARY_PATH=$HOME/installed_libsodium/lib:$LD_LIBRARY_PATH script: - cd memsec-test - cargo test --no-default-features - cargo test - if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then cargo test --features nightly; fi - if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then cargo bench --features nightly; fi memsec-0.7.0/Cargo.toml0000644000000032010000000000100103030ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "memsec" version = "0.7.0" authors = ["quininer kel "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Rust implementation `libsodium/utils`." documentation = "https://docs.rs/memsec/" readme = "README.md" keywords = [ "protection", "memory", "secure", ] categories = [ "no-std", "memory-management", ] license = "MIT" repository = "https://github.com/quininer/memsec" [lib] name = "memsec" path = "src/lib.rs" [dependencies.getrandom] version = "0.2" optional = true [features] alloc = [ "getrandom", "use_os", ] alloc_ext = ["alloc"] default = [ "use_os", "alloc", ] nightly = [] use_os = [ "libc", "windows-sys", ] [target."cfg(unix)".dependencies.libc] version = "0.2" optional = true [target."cfg(windows)".dependencies.windows-sys] version = "0.45" features = [ "Win32_System_SystemInformation", "Win32_System_Memory", "Win32_Foundation", "Win32_System_Diagnostics_Debug", ] optional = true default-features = false [badges.appveyor] repository = "quininer/memsec" [badges.travis-ci] repository = "quininer/memsec" memsec-0.7.0/Cargo.toml.orig000064400000000000000000000017621046102023000137760ustar 00000000000000[package] name = "memsec" version = "0.7.0" authors = ["quininer kel "] description = "Rust implementation `libsodium/utils`." repository = "https://github.com/quininer/memsec" keywords = ["protection", "memory", "secure"] documentation = "https://docs.rs/memsec/" license = "MIT" categories = ["no-std", "memory-management"] edition = "2018" [badges] travis-ci = { repository = "quininer/memsec" } appveyor = { repository = "quininer/memsec" } [dependencies] getrandom = { version = "0.2", optional = true } [target.'cfg(unix)'.dependencies] libc = { version = "0.2", optional = true } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.45", default-features = false, features = [ "Win32_System_SystemInformation", "Win32_System_Memory", "Win32_Foundation", "Win32_System_Diagnostics_Debug", ], optional = true } [features] default = ["use_os", "alloc"] nightly = [] use_os = ["libc", "windows-sys"] alloc = ["getrandom", "use_os"] alloc_ext = ["alloc"] memsec-0.7.0/LICENSE000064400000000000000000000020611046102023000121050ustar 00000000000000MIT License Copyright (c) 2016 quininer@live.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. memsec-0.7.0/README.md000064400000000000000000000022461046102023000123640ustar 00000000000000# memsec [![travis-ci](https://travis-ci.org/quininer/memsec.svg?branch=master)](https://travis-ci.org/quininer/memsec) [![appveyor](https://ci.appveyor.com/api/projects/status/1w0qtl0grjfu0uac?svg=true)](https://ci.appveyor.com/project/quininer/memsec) [![crates](https://img.shields.io/crates/v/memsec.svg)](https://crates.io/crates/memsec) [![license](https://img.shields.io/github/license/quininer/memsec.svg)](https://github.com/quininer/memsec/blob/master/LICENSE) [![docs.rs](https://docs.rs/memsec/badge.svg)](https://docs.rs/memsec/) Rust implementation `libsodium/utils`. * [x] `memeq`/`memcmp` * [x] `memset`/`memzero` * [x] `mlock`/`munlock` * [x] `alloc`/`free`/`mprotect` * [x] Linux only: `alloc_memfd_secret`/`free_memfd_secret` functions similar to `alloc`/`free` implementation backed by `memfd_secret` ref --- * [Securing memory allocations](https://download.libsodium.org/doc/helpers/memory_management.html) * [rlibc](https://github.com/alexcrichton/rlibc) * [aligned\_alloc.rs](https://github.com/jonas-schievink/aligned_alloc.rs) * [cst\_time\_memcmp](https://github.com/chmike/cst_time_memcmp) * [memfd_secret] (https://man.archlinux.org/man/memfd_secret.2.en) memsec-0.7.0/src/alloc/allocext/linux.rs000064400000000000000000000102631046102023000163040ustar 00000000000000extern crate std; use self::std::process::abort; use crate::{alloc::*, Prot}; use core::mem::{self, size_of}; use core::ptr::{self, NonNull}; use core::slice; use self::memfd_secret_alloc::*; mod memfd_secret_alloc { use super::*; use core::convert::TryInto; #[inline] pub unsafe fn alloc_memfd_secret(size: usize) -> Option<(NonNull, libc::c_int)> { let fd: Result = libc::syscall(libc::SYS_memfd_secret, 0).try_into(); let fd = fd.ok().filter(|&fd| fd >= 0)?; // File size is set using ftruncate let _ = libc::ftruncate(fd, size as libc::off_t); let ptr = libc::mmap( ptr::null_mut(), size, Prot::ReadWrite, libc::MAP_SHARED, fd, 0, ); if ptr == libc::MAP_FAILED { return None; } NonNull::new(ptr as *mut u8).map(|ptr| (ptr, fd)) } } unsafe fn _memfd_secret(size: usize) -> Option<*mut u8> { ALLOC_INIT.call_once(|| alloc_init()); //Assert size of unprotected_size (usize) and fd (i32) is less than PAGE_SIZE before allocating memory assert!(size_of::() + size_of::() <= PAGE_SIZE); if size >= ::core::usize::MAX - PAGE_SIZE * 4 { return None; } // aligned alloc ptr let size_with_canary = CANARY_SIZE + size; let unprotected_size = page_round(size_with_canary); let total_size = PAGE_SIZE + PAGE_SIZE + unprotected_size + PAGE_SIZE; let (base_ptr, fd) = alloc_memfd_secret(total_size)?; let base_ptr = base_ptr.as_ptr(); let fd_ptr = base_ptr.add(size_of::()); let unprotected_ptr = base_ptr.add(PAGE_SIZE * 2); // mprotect can be used to change protection flag after mmap setup // https://www.gnu.org/software/libc/manual/html_node/Memory-Protection.html#index-mprotect _mprotect(base_ptr.add(PAGE_SIZE), PAGE_SIZE, Prot::NoAccess); _mprotect( unprotected_ptr.add(unprotected_size), PAGE_SIZE, Prot::NoAccess, ); let canary_ptr = unprotected_ptr.add(unprotected_size - size_with_canary); let user_ptr = canary_ptr.add(CANARY_SIZE); ptr::copy_nonoverlapping(CANARY.as_ptr(), canary_ptr, CANARY_SIZE); ptr::write_unaligned(base_ptr as *mut usize, unprotected_size); ptr::write_unaligned(fd_ptr as *mut libc::c_int, fd); _mprotect(base_ptr, PAGE_SIZE, Prot::ReadOnly); assert_eq!(unprotected_ptr_from_user_ptr(user_ptr), unprotected_ptr); Some(user_ptr) } /// Linux specific `memfd_secret` backed allocation #[inline] pub unsafe fn memfd_secret() -> Option> { _memfd_secret(mem::size_of::()).map(|memptr| { ptr::write_bytes(memptr, GARBAGE_VALUE, mem::size_of::()); NonNull::new_unchecked(memptr as *mut T) }) } /// Linux specific `memfd_secret` backed `sized` allocation #[inline] pub unsafe fn memfd_secret_sized(size: usize) -> Option> { _memfd_secret(size).map(|memptr| { ptr::write_bytes(memptr, GARBAGE_VALUE, size); NonNull::new_unchecked(slice::from_raw_parts_mut(memptr, size)) }) } /// Secure `free` for memfd_secret allocations, /// i.e. provides read write access back to mprotect guard pages /// and unmaps mmaped secrets pub unsafe fn free_memfd_secret(memptr: NonNull) { use libc::c_void; let memptr = memptr.as_ptr() as *mut u8; // get unprotected ptr let canary_ptr = memptr.sub(CANARY_SIZE); let unprotected_ptr = unprotected_ptr_from_user_ptr(memptr); let base_ptr = unprotected_ptr.sub(PAGE_SIZE * 2); let fd_ptr = base_ptr.add(size_of::()) as *mut libc::c_int; let unprotected_size = ptr::read(base_ptr as *const usize); let fd = ptr::read(fd_ptr); // check if !crate::memeq(canary_ptr as *const u8, CANARY.as_ptr(), CANARY_SIZE) { abort(); } // free let total_size = PAGE_SIZE + PAGE_SIZE + unprotected_size + PAGE_SIZE; _mprotect(base_ptr, total_size, Prot::ReadWrite); crate::memzero(unprotected_ptr, unprotected_size); let res = libc::munmap(base_ptr as *mut c_void, total_size); if res < 0 { abort(); } let res = libc::close(fd); if res < 0 { abort(); } } memsec-0.7.0/src/alloc/allocext/mod.rs000064400000000000000000000002541046102023000157230ustar 00000000000000//! allocext //! OS Specific allocation //! //! #![cfg(feature = "alloc_ext")] #[cfg(target_os = "linux")] mod linux; #[cfg(target_os = "linux")] pub use self::linux::*; memsec-0.7.0/src/alloc/mod.rs000064400000000000000000000162221046102023000141120ustar 00000000000000//! alloc #![cfg(feature = "alloc")] pub mod allocext; extern crate std; use self::raw_alloc::*; use self::std::process::abort; use self::std::sync::Once; use core::mem; use core::ptr::{self, NonNull}; use core::slice; use getrandom::getrandom; const GARBAGE_VALUE: u8 = 0xd0; const CANARY_SIZE: usize = 16; static ALLOC_INIT: Once = Once::new(); static mut PAGE_SIZE: usize = 0; static mut PAGE_MASK: usize = 0; static mut CANARY: [u8; CANARY_SIZE] = [0; CANARY_SIZE]; // -- alloc init -- #[inline] unsafe fn alloc_init() { #[cfg(unix)] { PAGE_SIZE = libc::sysconf(libc::_SC_PAGESIZE) as usize; } #[cfg(windows)] { let mut si = mem::MaybeUninit::uninit(); windows_sys::Win32::System::SystemInformation::GetSystemInfo(si.as_mut_ptr()); PAGE_SIZE = (*si.as_ptr()).dwPageSize as usize; } if PAGE_SIZE < CANARY_SIZE || PAGE_SIZE < mem::size_of::() { panic!("page size too small"); } PAGE_MASK = PAGE_SIZE - 1; #[allow(static_mut_refs)] getrandom(&mut CANARY).unwrap(); } // -- aligned alloc / aligned free -- mod raw_alloc { use super::std::alloc::{alloc, dealloc, Layout}; use super::*; #[inline] pub unsafe fn alloc_aligned(size: usize) -> Option> { let layout = Layout::from_size_align_unchecked(size, PAGE_SIZE); NonNull::new(alloc(layout)) } #[inline] pub unsafe fn free_aligned(memptr: *mut u8, size: usize) { let layout = Layout::from_size_align_unchecked(size, PAGE_SIZE); dealloc(memptr, layout); } } // -- mprotect -- /// Prot enum. #[cfg(unix)] #[allow(non_snake_case, non_upper_case_globals)] pub mod Prot { pub use libc::c_int as Ty; pub const NoAccess: Ty = libc::PROT_NONE; pub const ReadOnly: Ty = libc::PROT_READ; pub const WriteOnly: Ty = libc::PROT_WRITE; pub const ReadWrite: Ty = libc::PROT_READ | libc::PROT_WRITE; pub const Execute: Ty = libc::PROT_EXEC; pub const ReadExec: Ty = libc::PROT_READ | libc::PROT_EXEC; pub const WriteExec: Ty = libc::PROT_WRITE | libc::PROT_EXEC; pub const ReadWriteExec: Ty = libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC; } /// Prot enum. #[cfg(windows)] #[allow(non_snake_case, non_upper_case_globals)] pub mod Prot { pub use windows_sys::Win32::System::Memory::PAGE_PROTECTION_FLAGS as Ty; pub const NoAccess: Ty = windows_sys::Win32::System::Memory::PAGE_NOACCESS; pub const ReadOnly: Ty = windows_sys::Win32::System::Memory::PAGE_READONLY; pub const ReadWrite: Ty = windows_sys::Win32::System::Memory::PAGE_READWRITE; pub const WriteCopy: Ty = windows_sys::Win32::System::Memory::PAGE_WRITECOPY; pub const Execute: Ty = windows_sys::Win32::System::Memory::PAGE_EXECUTE; pub const ReadExec: Ty = windows_sys::Win32::System::Memory::PAGE_EXECUTE_READ; pub const ReadWriteExec: Ty = windows_sys::Win32::System::Memory::PAGE_EXECUTE_READWRITE; pub const WriteCopyExec: Ty = windows_sys::Win32::System::Memory::PAGE_EXECUTE_WRITECOPY; pub const Guard: Ty = windows_sys::Win32::System::Memory::PAGE_GUARD; pub const NoCache: Ty = windows_sys::Win32::System::Memory::PAGE_NOCACHE; pub const WriteCombine: Ty = windows_sys::Win32::System::Memory::PAGE_WRITECOMBINE; pub const RevertToFileMap: Ty = windows_sys::Win32::System::Memory::PAGE_REVERT_TO_FILE_MAP; pub const TargetsInvalid: Ty = windows_sys::Win32::System::Memory::PAGE_TARGETS_INVALID; pub const TargetsNoUpdate: Ty = windows_sys::Win32::System::Memory::PAGE_TARGETS_NO_UPDATE; } /// Unix `mprotect`. #[cfg(unix)] #[inline] pub unsafe fn _mprotect(ptr: *mut u8, len: usize, prot: Prot::Ty) -> bool { libc::mprotect(ptr as *mut libc::c_void, len, prot as libc::c_int) == 0 } /// Windows `VirtualProtect`. #[cfg(windows)] #[inline] pub unsafe fn _mprotect(ptr: *mut u8, len: usize, prot: Prot::Ty) -> bool { let mut old = mem::MaybeUninit::uninit(); windows_sys::Win32::System::Memory::VirtualProtect(ptr.cast(), len, prot, old.as_mut_ptr()) != 0 } /// Secure `mprotect`. #[cfg(any(unix, windows))] pub unsafe fn mprotect(memptr: NonNull, prot: Prot::Ty) -> bool { let memptr = memptr.as_ptr() as *mut u8; let unprotected_ptr = unprotected_ptr_from_user_ptr(memptr); let base_ptr = unprotected_ptr.sub(PAGE_SIZE * 2); let unprotected_size = ptr::read(base_ptr as *const usize); _mprotect(unprotected_ptr, unprotected_size, prot) } // -- malloc / free -- #[inline] unsafe fn page_round(size: usize) -> usize { (size + PAGE_MASK) & !PAGE_MASK } #[inline] unsafe fn unprotected_ptr_from_user_ptr(memptr: *const u8) -> *mut u8 { let canary_ptr = memptr.sub(CANARY_SIZE); let unprotected_ptr_u = canary_ptr as usize & !PAGE_MASK; if unprotected_ptr_u <= PAGE_SIZE * 2 { abort(); } unprotected_ptr_u as *mut u8 } unsafe fn _malloc(size: usize) -> Option<*mut u8> { ALLOC_INIT.call_once(|| alloc_init()); if size >= ::core::usize::MAX - PAGE_SIZE * 4 { return None; } // aligned alloc ptr let size_with_canary = CANARY_SIZE + size; let unprotected_size = page_round(size_with_canary); let total_size = PAGE_SIZE + PAGE_SIZE + unprotected_size + PAGE_SIZE; let base_ptr = alloc_aligned(total_size)?.as_ptr(); let unprotected_ptr = base_ptr.add(PAGE_SIZE * 2); // mprotect ptr _mprotect(base_ptr.add(PAGE_SIZE), PAGE_SIZE, Prot::NoAccess); _mprotect( unprotected_ptr.add(unprotected_size), PAGE_SIZE, Prot::NoAccess, ); crate::mlock(unprotected_ptr, unprotected_size); let canary_ptr = unprotected_ptr.add(unprotected_size - size_with_canary); let user_ptr = canary_ptr.add(CANARY_SIZE); ptr::copy_nonoverlapping(CANARY.as_ptr(), canary_ptr, CANARY_SIZE); ptr::write_unaligned(base_ptr as *mut usize, unprotected_size); _mprotect(base_ptr, PAGE_SIZE, Prot::ReadOnly); assert_eq!(unprotected_ptr_from_user_ptr(user_ptr), unprotected_ptr); Some(user_ptr) } /// Secure `malloc`. #[inline] pub unsafe fn malloc() -> Option> { _malloc(mem::size_of::()).map(|memptr| { ptr::write_bytes(memptr, GARBAGE_VALUE, mem::size_of::()); NonNull::new_unchecked(memptr as *mut T) }) } /// Secure `malloc_sized`. #[inline] pub unsafe fn malloc_sized(size: usize) -> Option> { _malloc(size).map(|memptr| { ptr::write_bytes(memptr, GARBAGE_VALUE, size); NonNull::new_unchecked(slice::from_raw_parts_mut(memptr, size)) }) } /// Secure `free`. pub unsafe fn free(memptr: NonNull) { let memptr = memptr.as_ptr() as *mut u8; // get unprotected ptr let canary_ptr = memptr.sub(CANARY_SIZE); let unprotected_ptr = unprotected_ptr_from_user_ptr(memptr); let base_ptr = unprotected_ptr.sub(PAGE_SIZE * 2); let unprotected_size = ptr::read(base_ptr as *const usize); // check if !crate::memeq(canary_ptr as *const u8, CANARY.as_ptr(), CANARY_SIZE) { abort(); } // free let total_size = PAGE_SIZE + PAGE_SIZE + unprotected_size + PAGE_SIZE; _mprotect(base_ptr, total_size, Prot::ReadWrite); crate::munlock(unprotected_ptr, unprotected_size); free_aligned(base_ptr, total_size); } memsec-0.7.0/src/lib.rs000064400000000000000000000033521046102023000130070ustar 00000000000000#![no_std] #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", feature(core_intrinsics))] #![allow(clippy::missing_safety_doc)] mod alloc; mod mlock; use core::ptr; #[cfg(feature = "use_os")] pub use mlock::{mlock, munlock}; #[cfg(feature = "alloc")] pub use alloc::{free, malloc, malloc_sized, mprotect, Prot}; #[cfg(feature = "alloc_ext")] #[cfg(target_os = "linux")] pub use alloc::allocext::{free_memfd_secret, memfd_secret, memfd_secret_sized}; // -- memcmp -- /// Secure `memeq`. #[inline(never)] pub unsafe fn memeq(b1: *const u8, b2: *const u8, len: usize) -> bool { (0..len) .map(|i| ptr::read_volatile(b1.add(i)) ^ ptr::read_volatile(b2.add(i))) .fold(0, |sum, next| sum | next) .eq(&0) } /// Secure `memcmp`. #[inline(never)] pub unsafe fn memcmp(b1: *const u8, b2: *const u8, len: usize) -> i32 { let mut res = 0; for i in (0..len).rev() { let diff = i32::from(ptr::read_volatile(b1.add(i))) - i32::from(ptr::read_volatile(b2.add(i))); res = (res & (((diff - 1) & !diff) >> 8)) | diff; } ((res - 1) >> 8) + (res >> 8) + 1 } // -- memset / memzero -- /// General `memset`. #[inline(never)] pub unsafe fn memset(s: *mut u8, c: u8, n: usize) { #[cfg(feature = "nightly")] { core::intrinsics::volatile_set_memory(s, c, n); } #[cfg(not(feature = "nightly"))] { let s = ptr::read_volatile(&s); let c = ptr::read_volatile(&c); let n = ptr::read_volatile(&n); for i in 0..n { ptr::write(s.add(i), c); } let _ = ptr::read_volatile(&s); } } /// General `memzero`. #[inline] pub unsafe fn memzero(dest: *mut u8, n: usize) { memset(dest, 0, n); } memsec-0.7.0/src/mlock.rs000064400000000000000000000023771046102023000133540ustar 00000000000000//! mlock / munlock #![cfg(feature = "use_os")] /// Cross-platform `mlock`. /// /// * Unix `mlock`. /// * Windows `VirtualLock`. pub unsafe fn mlock(addr: *mut u8, len: usize) -> bool { #[cfg(unix)] { #[cfg(target_os = "linux")] libc::madvise(addr as *mut libc::c_void, len, libc::MADV_DONTDUMP); #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] libc::madvise(addr as *mut libc::c_void, len, libc::MADV_NOCORE); libc::mlock(addr as *mut libc::c_void, len) == 0 } #[cfg(windows)] { windows_sys::Win32::System::Memory::VirtualLock(addr.cast(), len) != 0 } } /// Cross-platform `munlock`. /// /// * Unix `munlock`. /// * Windows `VirtualUnlock`. pub unsafe fn munlock(addr: *mut u8, len: usize) -> bool { crate::memzero(addr, len); #[cfg(unix)] { #[cfg(target_os = "linux")] libc::madvise(addr as *mut libc::c_void, len, libc::MADV_DODUMP); #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] libc::madvise(addr as *mut libc::c_void, len, libc::MADV_CORE); libc::munlock(addr as *mut libc::c_void, len) == 0 } #[cfg(windows)] { windows_sys::Win32::System::Memory::VirtualUnlock(addr.cast(), len) != 0 } }