aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Barman <ludovicb@google.com>2024-01-05 11:20:59 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2024-01-05 11:20:59 +0000
commit26a65d048fce377a7160653f408988ed455dbf76 (patch)
tree7c3c342073022878b3bd37e25a235d7dfb0bd488
parent499e4718b98512eaa9309c40bb5da0eab288d01f (diff)
parent91342e8f170110f827df7603c7de9778ee888150 (diff)
downloaduserfaultfd-master.tar.gz
Upgrade userfaultfd to 0.7.0 am: 2b6ee30183 am: e3d9fe3555 am: 91342e8f17HEADmastermain
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/userfaultfd/+/2878120 Change-Id: I27e96e5ccd1f3e4411592c84fcd091ae807f88fc Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/dependabot.yml16
-rw-r--r--.github/workflows/rust.yml36
-rw-r--r--Android.bp4
-rwxr-xr-xCHANGELOG.md13
-rw-r--r--Cargo.toml22
-rw-r--r--Cargo.toml.orig13
-rw-r--r--METADATA21
-rw-r--r--cargo_embargo.json3
-rw-r--r--examples/manpage.rs7
-rw-r--r--src/builder.rs62
-rw-r--r--src/error.rs6
-rw-r--r--src/lib.rs9
-rw-r--r--src/raw.rs15
14 files changed, 179 insertions, 50 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index c054044..054ab4e 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "cb2cd6d359b715e24144242e4580679897268ffd"
+ "sha1": "afcbd90872e7fa0fca2c416e786e4d62e948bced"
},
"path_in_vcs": ""
} \ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..8923421
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,16 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "cargo" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ - package-ecosystem: "cargo" # See documentation for possible values
+ directory: "/userfaultfd-sys" # Location of package manifests
+ schedule:
+ interval: "weekly"
+
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 7631965..1e212ec 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -9,24 +9,50 @@ on:
jobs:
build:
- runs-on: ubuntu-latest
+ # ubuntu-latest runs a recent kernel with /dev/userfaultfd support whereas
+ # ubuntu-20.04 has a 5.15 kernel. We run the job in both, so we can test
+ # both paths for creating the file descriptor, i.e. /dev/userfaultfd ioctl
+ # and userfaultfd syscall.
+ runs-on: ${{ matrix.runner }}
+ strategy:
+ matrix:
+ runner: [ ubuntu-latest, ubuntu-20.04 ]
steps:
- uses: actions/checkout@v2
+
+ # Keep this step, so that we can check that the Linux kernel is the one we
+ # expect, depending on the runner kernel.
+ - name: Check Linux version
+ run: uname -r
+
+ # /dev/userfaultfd is only present on ubuntu-latest.
+ - name: Setup access to /dev/userfaultfd
+ if: ${{ matrix.runner == 'ubuntu-latest' }}
+ run: sudo setfacl -m u:${USER}:rw /dev/userfaultfd
+
- name: Build
run: cargo build --verbose
- # The github ubuntu-latest is now on linux 5.11 kernel,
- # so we can test the crate with support for each of the
- # kernel featuresets:
-
- name: Run tests (Linux 4.11 support)
run: cargo test --verbose
- name: Run tests (Linux 4.14 support)
run: cargo test --verbose --features linux4_14
+
- name: Run tests (Linux 5.7 support)
+ if: ${{ matrix.runner == 'ubuntu-latest' }}
run: cargo test --verbose --features linux5_7
+ # On ubuntu-20.04 runner we need to make sure we have the proper kernel
+ # headers for building the correct bindings
+ - name: Run tests (Linux 5.7 support)
+ if: ${{ matrix.runner == 'ubuntu-20.04' }}
+ run:
+ sudo apt update &&
+ sudo apt install -y linux-headers-5.11.0-25-generic &&
+ export LINUX_HEADERS=/usr/src/linux-headers-5.11.0-25-generic &&
+ cargo test --verbose --features linux5_7
+
audit:
runs-on: ubuntu-latest
diff --git a/Android.bp b/Android.bp
index f497e56..618a202 100644
--- a/Android.bp
+++ b/Android.bp
@@ -29,12 +29,12 @@ rust_library {
host_supported: true,
crate_name: "userfaultfd",
cargo_env_compat: true,
- cargo_pkg_version: "0.5.1",
+ cargo_pkg_version: "0.7.0",
srcs: ["src/lib.rs"],
edition: "2018",
features: ["default"],
rustlibs: [
- "libbitflags-1.3.2",
+ "libbitflags",
"libcfg_if",
"liblibc",
"libnix",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b2621e9..b0797ad 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,19 @@
### Unreleased
- Added `Uffd::read_events` that can read multiple events from the userfaultfd file descriptor.
+- Updated `bitflags` dependency to `2.2.1`.
+- Use `/dev/userfaultfd` as the default API for creating userfaultfd file descriptors.
+
+ Since Linux 5.11 a process can select if it wants to handle page faults triggered in kernel space
+ or not. Under this mechanism, processes that wish to handle those, need to have `CAP_SYS_PTRACE`
+ capability. `CAP_SYS_PTRACE` allows a process to do much more than create userfault fds, so with
+ 6.1 Linux introduces `/dev/userfaultfd`, a special character device that allows creating
+ userfault file descriptors using the `USERFAULTFD_IOC_NEW` `ioctl`. Access to this device is
+ granted via file system permissions and does not require `CAP_SYS_PTRACE` to handle kernel
+ triggered page faults.
+
+ We now default to using `/dev/userfaultfd` for creating the descriptors and only if that file is
+ not present, we fall back to using the syscall.
### 0.3.1 (2021-02-17)
diff --git a/Cargo.toml b/Cargo.toml
index c2f042a..a43c013 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "userfaultfd"
-version = "0.5.1"
+version = "0.7.0"
authors = ["The Wasmtime Project Developers"]
description = "Rust bindings for the Linux userfaultfd functionality"
readme = "README.md"
@@ -20,7 +20,7 @@ license = "MIT OR Apache-2.0"
repository = "https://github.com/bytecodealliance/userfaultfd-rs"
[dependencies.bitflags]
-version = "1.0"
+version = "2.2.1"
[dependencies.cfg-if]
version = "^1.0.0"
@@ -29,15 +29,27 @@ version = "^1.0.0"
version = "0.2.65"
[dependencies.nix]
-version = "0.26"
+version = "0.27"
+features = ["ioctl"]
[dependencies.thiserror]
version = "1.0.4"
[dependencies.userfaultfd-sys]
-version = "^0.4.0"
+version = "^0.5.0"
+
+[dev-dependencies.nix]
+version = "0.27"
+features = [
+ "poll",
+ "mman",
+ "feature",
+]
[features]
default = []
-linux4_14 = ["userfaultfd-sys/linux4_14"]
+linux4_14 = [
+ "userfaultfd-sys/linux4_14",
+ "nix/process",
+]
linux5_7 = ["userfaultfd-sys/linux5_7"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 04c8ca4..aa3d40c 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "userfaultfd"
-version = "0.5.1"
+version = "0.7.0"
authors = ["The Wasmtime Project Developers"]
edition = "2018"
license = "MIT OR Apache-2.0"
@@ -9,14 +9,17 @@ repository = "https://github.com/bytecodealliance/userfaultfd-rs"
readme = "README.md"
[dependencies]
-bitflags = "1.0"
+bitflags = "2.2.1"
cfg-if = "^1.0.0"
libc = "0.2.65"
-nix = "0.26"
+nix = { version = "0.27", features = ["ioctl"] }
thiserror = "1.0.4"
-userfaultfd-sys = { path = "userfaultfd-sys", version = "^0.4.0" }
+userfaultfd-sys = { path = "userfaultfd-sys", version = "^0.5.0" }
+
+[dev-dependencies]
+nix = { version = "0.27", features = ["poll", "mman", "feature"] }
[features]
default = []
-linux4_14 = ["userfaultfd-sys/linux4_14"]
+linux4_14 = ["userfaultfd-sys/linux4_14", "nix/process"]
linux5_7 = ["userfaultfd-sys/linux5_7"]
diff --git a/METADATA b/METADATA
index e2181c8..08e2200 100644
--- a/METADATA
+++ b/METADATA
@@ -1,23 +1,20 @@
# This project was upgraded with external_updater.
# Usage: tools/external_updater/updater.sh update rust/crates/userfaultfd
-# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
name: "userfaultfd"
description: "Rust bindings for the Linux userfaultfd functionality"
third_party {
- url {
- type: HOMEPAGE
- value: "https://crates.io/crates/userfaultfd"
- }
- url {
- type: ARCHIVE
- value: "https://static.crates.io/crates/userfaultfd/userfaultfd-0.5.1.crate"
- }
- version: "0.5.1"
license_type: NOTICE
last_upgrade_date {
year: 2023
- month: 3
- day: 30
+ month: 12
+ day: 16
+ }
+ homepage: "https://crates.io/crates/userfaultfd"
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/userfaultfd/userfaultfd-0.7.0.crate"
+ version: "0.7.0"
}
}
diff --git a/cargo_embargo.json b/cargo_embargo.json
index 99df43f..cb908d7 100644
--- a/cargo_embargo.json
+++ b/cargo_embargo.json
@@ -1,6 +1,3 @@
{
- "module_name_overrides": {
- "libbitflags": "libbitflags-1.3.2"
- },
"run_cargo": false
}
diff --git a/examples/manpage.rs b/examples/manpage.rs
index 29cccd0..1de593e 100644
--- a/examples/manpage.rs
+++ b/examples/manpage.rs
@@ -3,7 +3,6 @@ use libc::{self, c_void};
use nix::poll::{poll, PollFd, PollFlags};
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
use nix::unistd::{sysconf, SysconfVar};
-use std::os::unix::io::AsRawFd;
use std::{convert::TryInto, env};
use userfaultfd::{Event, Uffd, UffdBuilder};
@@ -18,7 +17,7 @@ fn fault_handler_thread(uffd: Uffd) {
page_size.try_into().unwrap(),
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
- -1,
+ None::<std::os::fd::BorrowedFd>,
0,
)
.expect("mmap")
@@ -30,7 +29,7 @@ fn fault_handler_thread(uffd: Uffd) {
loop {
// See what poll() tells us about the userfaultfd
- let pollfd = PollFd::new(uffd.as_raw_fd(), PollFlags::POLLIN);
+ let pollfd = PollFd::new(&uffd, PollFlags::POLLIN);
let nready = poll(&mut [pollfd], -1).expect("poll");
println!("\nfault_handler_thread():");
@@ -101,7 +100,7 @@ fn main() {
len.try_into().unwrap(),
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
- -1,
+ None::<std::os::fd::BorrowedFd>,
0,
)
.expect("mmap")
diff --git a/src/builder.rs b/src/builder.rs
index b89efb4..a200148 100644
--- a/src/builder.rs
+++ b/src/builder.rs
@@ -3,11 +3,17 @@ use crate::raw;
use crate::{IoctlFlags, Uffd};
use bitflags::bitflags;
use nix::errno::Errno;
+use std::fs::{File, OpenOptions};
+use std::io::ErrorKind;
+use std::os::fd::AsRawFd;
+
+const UFFD_DEVICE_PATH: &str = "/dev/userfaultfd";
cfg_if::cfg_if! {
if #[cfg(any(feature = "linux5_7", feature = "linux4_14"))] {
bitflags! {
/// Used with `UffdBuilder` to determine which features are available in the current kernel.
+ #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FeatureFlags: u64 {
const PAGEFAULT_FLAG_WP = raw::UFFD_FEATURE_PAGEFAULT_FLAG_WP;
const EVENT_FORK = raw::UFFD_FEATURE_EVENT_FORK;
@@ -23,6 +29,7 @@ cfg_if::cfg_if! {
} else {
bitflags! {
/// Used with `UffdBuilder` to determine which features are available in the current kernel.
+ #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FeatureFlags: u64 {
const PAGEFAULT_FLAG_WP = raw::UFFD_FEATURE_PAGEFAULT_FLAG_WP;
const EVENT_FORK = raw::UFFD_FEATURE_EVENT_FORK;
@@ -113,6 +120,47 @@ impl UffdBuilder {
self
}
+ fn uffd_from_dev(&self, file: &mut File, flags: i32) -> Result<Uffd> {
+ match unsafe { raw::new_uffd(file.as_raw_fd(), flags) } {
+ Err(err) => Err(err.into()),
+ Ok(fd) => Ok(Uffd { fd }),
+ }
+ }
+
+ fn uffd_from_syscall(&self, flags: i32) -> Result<Uffd> {
+ let fd = match Errno::result(unsafe { raw::userfaultfd(flags) }) {
+ Ok(fd) => fd,
+ // setting the USER_MODE_ONLY flag on kernel pre-5.11 causes it to return EINVAL.
+ // If the user asks for the flag, we first try with it set, and if kernel gives
+ // EINVAL we try again without the flag set.
+ Err(Errno::EINVAL) if self.user_mode_only => Errno::result(unsafe {
+ raw::userfaultfd(flags & !raw::UFFD_USER_MODE_ONLY as i32)
+ })?,
+ Err(e) => return Err(e.into()),
+ };
+
+ // Wrap the fd up so that a failure in this function body closes it with the drop.
+ Ok(Uffd { fd })
+ }
+
+ // Try to get a UFFD file descriptor using `/dev/userfaultfd`. If that fails
+ // fall back to calling the system call.
+ fn open_file_descriptor(&self, flags: i32) -> Result<Uffd> {
+ // If `/dev/userfaultfd` exists we'll try to get the file descriptor from it. If the file
+ // doesn't exist we will fall back to calling the system call. This means, that if the
+ // device exists but the calling process does not have access rights to it, this will fail,
+ // i.e. we will not fall back to calling the system call.
+ match OpenOptions::new()
+ .read(true)
+ .write(true)
+ .open(UFFD_DEVICE_PATH)
+ {
+ Ok(mut file) => self.uffd_from_dev(&mut file, flags),
+ Err(err) if err.kind() == ErrorKind::NotFound => self.uffd_from_syscall(flags),
+ Err(err) => Err(Error::OpenDevUserfaultfd(err)),
+ }
+ }
+
/// Create a `Uffd` object with the current settings of this builder.
pub fn create(&self) -> Result<Uffd> {
// first do the syscall to get the file descriptor
@@ -128,19 +176,7 @@ impl UffdBuilder {
flags |= raw::UFFD_USER_MODE_ONLY as i32;
}
- let fd = match Errno::result(unsafe { raw::userfaultfd(flags) }) {
- Ok(fd) => fd,
- // setting the USER_MODE_ONLY flag on kernel pre-5.11 causes it to return EINVAL.
- // If the user asks for the flag, we first try with it set, and if kernel gives
- // EINVAL we try again without the flag set.
- Err(Errno::EINVAL) if self.user_mode_only => Errno::result(unsafe {
- raw::userfaultfd(flags & !raw::UFFD_USER_MODE_ONLY as i32)
- })?,
- Err(e) => return Err(e.into()),
- };
-
- // Wrap the fd up so that a failure in this function body closes it with the drop.
- let uffd = Uffd { fd };
+ let uffd = self.open_file_descriptor(flags)?;
// then do the UFFDIO_API ioctl to set up and ensure features and other ioctls are available
let mut api = raw::uffdio_api {
diff --git a/src/error.rs b/src/error.rs
index 5cd8926..f66806a 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,3 +1,5 @@
+use std::io;
+
use crate::IoctlFlags;
use nix::errno::Errno;
use thiserror::Error;
@@ -47,6 +49,10 @@ pub enum Error {
/// Zeropage ioctl failure with `errno` value.
#[error("Zeropage failed: {0}")]
ZeropageFailed(Errno),
+
+ /// Could not open /dev/userfaultfd even though it exists
+ #[error("Error accessing /dev/userfaultfd: {0}")]
+ OpenDevUserfaultfd(io::Error),
}
impl From<nix::Error> for Error {
diff --git a/src/lib.rs b/src/lib.rs
index 9a7641b..e18ef3c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,6 +21,7 @@ use libc::{self, c_void};
use nix::errno::Errno;
use nix::unistd::read;
use std::mem;
+use std::os::fd::{AsFd, BorrowedFd};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
/// Represents an opaque buffer where userfaultfd events are stored.
@@ -53,6 +54,12 @@ impl Drop for Uffd {
}
}
+impl AsFd for Uffd {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
+ }
+}
+
impl AsRawFd for Uffd {
fn as_raw_fd(&self) -> RawFd {
self.fd
@@ -73,6 +80,7 @@ impl FromRawFd for Uffd {
bitflags! {
/// The registration mode used when registering an address range with `Uffd`.
+ #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RegisterMode: u64 {
/// Registers the range for missing page faults.
const MISSING = raw::UFFDIO_REGISTER_MODE_MISSING;
@@ -359,6 +367,7 @@ impl Uffd {
bitflags! {
/// Used with `UffdBuilder` and `Uffd::register()` to determine which operations are available.
+ #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct IoctlFlags: u64 {
const REGISTER = 1 << raw::_UFFDIO_REGISTER;
const UNREGISTER = 1 << raw::_UFFDIO_UNREGISTER;
diff --git a/src/raw.rs b/src/raw.rs
index 332c459..28039c7 100644
--- a/src/raw.rs
+++ b/src/raw.rs
@@ -23,3 +23,18 @@ nix::ioctl_readwrite!(
_UFFDIO_WRITEPROTECT,
uffdio_writeprotect
);
+
+// ioctls for /dev/userfaultfd
+
+// This is the `/dev/userfaultfd` ioctl() from creating a new userfault file descriptor.
+// It is a "bad" ioctl in the sense that it is defined as an _IOC:
+// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/userfaultfd.h#L17,
+// aka `nix::ioctl_none`, however it does receive an integer argument:
+// https://elixir.bootlin.com/linux/latest/source/fs/userfaultfd.c#L2186. That is the same argument
+// that the userfaultfd() system call receives.
+nix::ioctl_write_int_bad!(
+ /// Create a new userfault file descriptor from the `/dev/userfaultfd`
+ /// device. This receives the same arguments as the userfaultfd system call.
+ new_uffd,
+ nix::request_code_none!(USERFAULTFD_IOC, 0x00)
+);