Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve memfd-secret guard page allocation #18

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "memsec"
version = "0.7.0"
version = "0.7.1"
authors = ["quininer kel <[email protected]>"]
description = "Rust implementation `libsodium/utils`."
repository = "https://github.com/quininer/memsec"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ ref
* [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)
* [memfd_secret](https://man.archlinux.org/man/memfd_secret.2.en)
108 changes: 85 additions & 23 deletions src/alloc/allocext/linux.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
extern crate std;
use libc::{MAP_ANONYMOUS, MAP_FIXED, MAP_NORESERVE, MAP_SHARED, PROT_NONE};

use self::std::process::abort;
use crate::{alloc::*, Prot};
use core::mem::{self, size_of};
Expand All @@ -8,32 +10,44 @@ use core::slice;
use self::memfd_secret_alloc::*;

mod memfd_secret_alloc {
use libc::{MAP_LOCKED, MAP_POPULATE};

use super::*;
use core::convert::TryInto;

/// Allocate memfd_secret with given size at given address ptr
/// Returns tuple of ptr to memory and file descriptor of memfd_secret
#[inline]
pub unsafe fn alloc_memfd_secret(size: usize) -> Option<(NonNull<u8>, libc::c_int)> {
pub unsafe fn alloc_memfd_secret_at_ptr(
size: usize,
ptr: *mut libc::c_void,
) -> Option<(NonNull<u8>, libc::c_int)> {
let fd: Result<libc::c_int, _> = 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(),
let ptr_out = libc::mmap(
ptr,
size,
Prot::ReadWrite,
libc::MAP_SHARED,
libc::MAP_SHARED | libc::MAP_FIXED | MAP_LOCKED | MAP_POPULATE,
fd,
0,
);

if ptr == libc::MAP_FAILED {
if ptr_out == libc::MAP_FAILED {
return None;
}

if ptr_out != ptr {
libc::munmap(ptr_out, size);
return None;
}

NonNull::new(ptr as *mut u8).map(|ptr| (ptr, fd))
NonNull::new(ptr_out as *mut u8).map(|ptr| (ptr, fd))
}
}

Expand All @@ -47,29 +61,60 @@ unsafe fn _memfd_secret(size: usize) -> Option<*mut u8> {
return None;
}

// aligned alloc ptr
let size_with_canary = CANARY_SIZE + size;
let front_guard_size = PAGE_SIZE + PAGE_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::<usize>());
let unprotected_ptr = base_ptr.add(PAGE_SIZE * 2);
let back_guard_size = PAGE_SIZE;
let total_size = front_guard_size + unprotected_size + back_guard_size;

let base_ptr = libc::mmap(
ptr::null_mut(),
total_size,
PROT_NONE,
MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE,
-1,
0,
);

// 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),
if base_ptr == libc::MAP_FAILED {
return None;
}
let base_ptr = base_ptr as *mut u8;

let base_ptr_stored = libc::mmap(
base_ptr as *mut libc::c_void,
PAGE_SIZE,
Prot::NoAccess,
);
Prot::ReadWrite,
MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED,
-1,
0,
) as *mut u8;

if base_ptr_stored == libc::MAP_FAILED as *mut u8 || base_ptr_stored != base_ptr {
libc::munmap(base_ptr as *mut libc::c_void, total_size);
return None;
}

let unprotected_ptr = base_ptr.add(front_guard_size);
let Some((unprotected_ptr_val, fd)) =
alloc_memfd_secret_at_ptr(unprotected_size, unprotected_ptr as *mut libc::c_void)
else {
libc::munmap(base_ptr_stored as *mut libc::c_void, PAGE_SIZE);
libc::munmap(base_ptr as *mut libc::c_void, total_size);
return None;
};
assert_eq!(unprotected_ptr_val.as_ptr(), unprotected_ptr as *mut u8);

let fd_ptr = base_ptr.add(size_of::<usize>());

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 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, PAGE_SIZE, Prot::ReadOnly);

assert_eq!(unprotected_ptr_from_user_ptr(user_ptr), unprotected_ptr);
Expand Down Expand Up @@ -109,20 +154,37 @@ pub unsafe fn free_memfd_secret<T: ?Sized>(memptr: NonNull<T>) {
let base_ptr = unprotected_ptr.sub(PAGE_SIZE * 2);
let fd_ptr = base_ptr.add(size_of::<usize>()) as *mut libc::c_int;
let unprotected_size = ptr::read(base_ptr as *const usize);
let back_guard_ptr = unprotected_ptr.add(unprotected_size);
let fd = ptr::read(fd_ptr);

// check
// check canary value is same
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);
let front_guard_size = PAGE_SIZE + PAGE_SIZE;
let back_guard_size = PAGE_SIZE;

_mprotect(base_ptr.add(PAGE_SIZE), front_guard_size, Prot::ReadWrite);
_mprotect(back_guard_ptr, back_guard_size, Prot::ReadWrite);

crate::memzero(unprotected_ptr, unprotected_size);

let res = libc::munmap(base_ptr as *mut c_void, total_size);
// Unmap memfd_secret mapping
let res = libc::munmap(unprotected_ptr as *mut c_void, unprotected_size);
if res < 0 {
abort();
}

// Unmap header mapping
let res = libc::munmap(base_ptr as *mut c_void, PAGE_SIZE);
if res < 0 {
abort();
}

// Unmap reserved space mapping
let res = libc::munmap(base_ptr as *mut c_void, PAGE_SIZE);
if res < 0 {
abort();
}
Expand Down
Loading