wasmtime/runtime/vm/mpk/sys.rs
1//! Expose the `pkey_*` Linux system calls. See the kernel documentation for
2//! more information:
3//! - [`pkeys`] overview
4//! - [`pkey_alloc`] (with `pkey_free`)
5//! - [`pkey_mprotect`]
6//! - `pkey_set` is implemented directly in assembly.
7//!
8//! [`pkey_alloc`]: https://man7.org/linux/man-pages/man2/pkey_alloc.2.html
9//! [`pkey_mprotect`]: https://man7.org/linux/man-pages/man2/pkey_mprotect.2.html
10//! [`pkeys`]: https://man7.org/linux/man-pages/man7/pkeys.7.html
11
12use crate::prelude::*;
13use crate::runtime::vm::host_page_size;
14use std::io::Error;
15
16/// Protection mask disallowing reads and writes of pkey-protected memory (see
17/// `prot` in [`pkey_mprotect`]); in Wasmtime we expect all MPK-protected memory
18/// to start as `PROT_NONE`.
19pub const PROT_NONE: u32 = libc::PROT_NONE as u32; // == 0b0000;
20
21/// Protection mask allowing reads of pkey-protected memory (see `prot` in
22/// [`pkey_mprotect`]).
23pub const PROT_READ: u32 = libc::PROT_READ as u32; // == 0b0001;
24
25/// Protection mask allowing writes of pkey-protected memory (see `prot` in
26/// [`pkey_mprotect`]).
27pub const PROT_WRITE: u32 = libc::PROT_WRITE as u32; // == 0b0010;
28
29/// Allocate a new protection key in the Linux kernel ([docs]); returns the
30/// key ID.
31///
32/// [docs]: https://man7.org/linux/man-pages/man2/pkey_alloc.2.html
33///
34/// Each process has its own separate pkey index; e.g., if process `m`
35/// allocates key 1, process `n` can as well.
36pub fn pkey_alloc(flags: u32, access_rights: u32) -> Result<u32> {
37 assert_eq!(flags, 0); // reserved for future use--must be 0.
38 let result = unsafe { libc::syscall(libc::SYS_pkey_alloc, flags, access_rights) };
39 if result >= 0 {
40 Ok(result
41 .try_into()
42 .expect("only pkey IDs between 0 and 15 are expected"))
43 } else {
44 debug_assert_eq!(result, -1); // only this error result is expected.
45 Err(Error::last_os_error().into())
46 }
47}
48
49/// Free a kernel protection key ([docs]).
50///
51/// [docs]: https://man7.org/linux/man-pages/man2/pkey_alloc.2.html
52#[cfg(test)]
53pub fn pkey_free(key: u32) -> Result<()> {
54 let result = unsafe { libc::syscall(libc::SYS_pkey_free, key) };
55 if result == 0 {
56 Ok(())
57 } else {
58 debug_assert_eq!(result, -1); // only this error result is expected.
59 Err(Error::last_os_error().into())
60 }
61}
62
63/// Change the access protections for a page-aligned memory region ([docs]).
64///
65/// [docs]: https://man7.org/linux/man-pages/man2/pkey_mprotect.2.html
66pub fn pkey_mprotect(addr: usize, len: usize, prot: u32, key: u32) -> Result<()> {
67 let page_size = host_page_size();
68 if addr % page_size != 0 {
69 log::warn!(
70 "memory must be page-aligned for MPK (addr = {addr:#x}, page size = {page_size}"
71 );
72 }
73 let result = unsafe { libc::syscall(libc::SYS_pkey_mprotect, addr, len, prot, key) };
74 if result == 0 {
75 Ok(())
76 } else {
77 debug_assert_eq!(result, -1); // only this error result is expected.
78 Err(Error::last_os_error().into())
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[ignore = "cannot be run when keys() has already allocated all keys"]
87 #[test]
88 fn check_allocate_and_free() {
89 let key = pkey_alloc(0, 0).unwrap();
90 assert_eq!(key, 1);
91 // It may seem strange to assert the key ID here, but we already
92 // make some assumptions:
93 // 1. we are running on Linux with `pku` enabled
94 // 2. Linux will allocate key 0 for itself
95 // 3. we are running this test in non-MPK mode and no one else is
96 // using pkeys
97 // If these assumptions are incorrect, this test can be removed.
98 pkey_free(key).unwrap()
99 }
100
101 #[test]
102 fn check_invalid_free() {
103 let result = pkey_free(42);
104 assert!(result.is_err());
105 assert_eq!(
106 result.unwrap_err().to_string(),
107 "Invalid argument (os error 22)"
108 );
109 }
110
111 #[test]
112 #[should_panic]
113 fn check_invalid_alloc_flags() {
114 let _ = pkey_alloc(42, 0);
115 }
116
117 #[test]
118 fn check_invalid_alloc_rights() {
119 assert!(pkey_alloc(0, 42).is_err());
120 }
121}