memsec-0.6.3/.cargo_vcs_info.json0000644000000001360000000000100123130ustar { "git": { "sha1": "bbc647967ff6d20d6dccf1c85f5d9037fcadd3b0" }, "path_in_vcs": "" }memsec-0.6.3/.github/workflows/ci.yml000064400000000000000000000013161046102023000156170ustar 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.6.3/.gitignore000064400000000000000000000000221046102023000130650ustar 00000000000000target Cargo.lock memsec-0.6.3/.travis.yml000064400000000000000000000014231046102023000132140ustar 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.6.3/Cargo.toml0000644000000027460000000000100103220ustar # 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.6.3" authors = ["quininer kel "] 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" [dependencies.getrandom] version = "0.2" optional = true [features] alloc = [ "getrandom", "use_os", ] 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.6.3/Cargo.toml.orig000064400000000000000000000017311046102023000137740ustar 00000000000000[package] name = "memsec" version = "0.6.3" 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" ] memsec-0.6.3/LICENSE000064400000000000000000000020611046102023000121070ustar 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.6.3/README.md000064400000000000000000000017321046102023000123650ustar 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` 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) memsec-0.6.3/src/alloc.rs000064400000000000000000000161601046102023000133360ustar 00000000000000//! alloc #![cfg(feature = "alloc")] extern crate std; use core::mem; use core::ptr::{ self, NonNull }; use core::slice; use getrandom::getrandom; use self::std::sync::Once; use self::std::process::abort; use self::raw_alloc::*; 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; 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.6.3/src/lib.rs000064400000000000000000000030431046102023000130060ustar 00000000000000#![no_std] #![cfg_attr(feature = "nightly", feature(core_intrinsics))] #![allow(clippy::missing_safety_doc)] mod mlock; mod alloc; use core::ptr; #[cfg(feature = "use_os")] pub use mlock::{ mlock, munlock }; #[cfg(feature = "alloc")] pub use alloc::{ Prot, mprotect, malloc, malloc_sized, free }; // -- 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.6.3/src/mlock.rs000064400000000000000000000023601046102023000133460ustar 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 } }