From e7ad4f3a54d7f4aaa2fa1d7f828506315d6b4e2b Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 25 Jun 2025 14:45:16 +0100 Subject: [PATCH 1/4] fix: remove UFFDIO_WRITEPROTECT from linux4_11/4_14 modules This ioctl was only added in linux 5.7 [1], so remove the bindings for the ioctl number from the 4_11 and 4_14 features. The actual writeprotect functionality was already gated behind the linux5_7 feature. [1]: https://man7.org/linux/man-pages/man2/UFFDIO_WRITEPROTECT.2const.html Signed-off-by: Patrick Roy --- userfaultfd-sys/src/linux4_11.rs | 6 ------ userfaultfd-sys/src/linux4_14.rs | 6 ------ 2 files changed, 12 deletions(-) diff --git a/userfaultfd-sys/src/linux4_11.rs b/userfaultfd-sys/src/linux4_11.rs index fab13a2..5192450 100644 --- a/userfaultfd-sys/src/linux4_11.rs +++ b/userfaultfd-sys/src/linux4_11.rs @@ -29,7 +29,6 @@ pub const UFFDIO_UNREGISTER: u32 = 0x8010aa01; pub const UFFDIO_WAKE: u32 = 0x8010aa02; pub const UFFDIO_COPY: u32 = 0xc028aa03; pub const UFFDIO_ZEROPAGE: u32 = 0xc020aa04; -pub const UFFDIO_WRITEPROTECT: u32 = 0xc018aa06; #[cfg(test)] mod const_tests { @@ -51,7 +50,6 @@ mod const_tests { static _const_UFFDIO_WAKE: u32; static _const_UFFDIO_COPY: u32; static _const_UFFDIO_ZEROPAGE: u32; - static _const_UFFDIO_WRITEPROTECT: u32; } #[test] @@ -96,10 +94,6 @@ mod const_tests { assert_eq!(UFFDIO_WAKE, _const_UFFDIO_WAKE, "UFFDIO_WAKE"); assert_eq!(UFFDIO_COPY, _const_UFFDIO_COPY, "UFFDIO_COPY"); assert_eq!(UFFDIO_ZEROPAGE, _const_UFFDIO_ZEROPAGE, "UFFDIO_ZEROPAGE"); - assert_eq!( - UFFDIO_WRITEPROTECT, _const_UFFDIO_WRITEPROTECT, - "UFFDIO_WRITEPROTECT" - ); } } } diff --git a/userfaultfd-sys/src/linux4_14.rs b/userfaultfd-sys/src/linux4_14.rs index d485171..c799d65 100644 --- a/userfaultfd-sys/src/linux4_14.rs +++ b/userfaultfd-sys/src/linux4_14.rs @@ -32,7 +32,6 @@ pub const UFFDIO_UNREGISTER: u32 = 0x8010aa01; pub const UFFDIO_WAKE: u32 = 0x8010aa02; pub const UFFDIO_COPY: u32 = 0xc028aa03; pub const UFFDIO_ZEROPAGE: u32 = 0xc020aa04; -pub const UFFDIO_WRITEPROTECT: u32 = 0xc018aa06; #[cfg(test)] mod const_tests { @@ -55,7 +54,6 @@ mod const_tests { static _const_UFFDIO_WAKE: u32; static _const_UFFDIO_COPY: u32; static _const_UFFDIO_ZEROPAGE: u32; - static _const_UFFDIO_WRITEPROTECT: u32; } #[test] @@ -104,10 +102,6 @@ mod const_tests { assert_eq!(UFFDIO_WAKE, _const_UFFDIO_WAKE, "UFFDIO_WAKE"); assert_eq!(UFFDIO_COPY, _const_UFFDIO_COPY, "UFFDIO_COPY"); assert_eq!(UFFDIO_ZEROPAGE, _const_UFFDIO_ZEROPAGE, "UFFDIO_ZEROPAGE"); - assert_eq!( - UFFDIO_WRITEPROTECT, _const_UFFDIO_WRITEPROTECT, - "UFFDIO_WRITEPROTECT" - ); } } } From 0975fe42c93f3fd345b853e1c5584e767d625576 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 25 Jun 2025 14:51:23 +0100 Subject: [PATCH 2/4] fix: add UFFDIO_WRITEPROTECT related consts to tests Extend the "constants are correct" tests to also cover the writeprotect related constants. Signed-off-by: Patrick Roy --- userfaultfd-sys/src/consts.c | 8 ++++++++ userfaultfd-sys/src/linux5_7.rs | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/userfaultfd-sys/src/consts.c b/userfaultfd-sys/src/consts.c index df78dbb..a380edd 100644 --- a/userfaultfd-sys/src/consts.c +++ b/userfaultfd-sys/src/consts.c @@ -42,6 +42,14 @@ const __u64 _const_UFFDIO_COPY_MODE_WP = UFFDIO_COPY_MODE_WP; const __u64 _const_UFFDIO_ZEROPAGE_MODE_DONTWAKE = UFFDIO_ZEROPAGE_MODE_DONTWAKE; #endif +#ifdef UFFDIO_WRITEPROTECT_MODE_WP +const __u64 _const_UFFDIO_WRITEPROTECT_MODE_WP = UFFDIO_WRITEPROTECT_MODE_WP; +#endif + +#ifdef UFFDIO_WRITEPROTECT_MODE_DONTWAKE +const __u64 _const_UFFDIO_WRITEPROTECT_MODE_DONTWAKE = UFFDIO_WRITEPROTECT_MODE_DONTWAKE; +#endif + #ifdef UFFDIO_API const __u32 _const_UFFDIO_API = UFFDIO_API; #endif diff --git a/userfaultfd-sys/src/linux5_7.rs b/userfaultfd-sys/src/linux5_7.rs index 05e43fc..d25db2f 100644 --- a/userfaultfd-sys/src/linux5_7.rs +++ b/userfaultfd-sys/src/linux5_7.rs @@ -52,6 +52,8 @@ mod const_tests { static _const_UFFDIO_COPY_MODE_DONTWAKE: u64; static _const_UFFDIO_COPY_MODE_WP: u64; static _const_UFFDIO_ZEROPAGE_MODE_DONTWAKE: u64; + static _const_UFFDIO_WRITEPROTECT_MODE_WP: u64; + static _const_UFFDIO_WRITEPROTECT_MODE_DONTWAKE: u64; static _const_UFFDIO_API: u32; static _const_UFFDIO_REGISTER: u32; static _const_UFFDIO_UNREGISTER: u32; @@ -98,6 +100,14 @@ mod const_tests { UFFDIO_ZEROPAGE_MODE_DONTWAKE, _const_UFFDIO_ZEROPAGE_MODE_DONTWAKE, "UFFDIO_ZEROPAGE_MODE_DONTWAKE" ); + assert_eq!( + UFFDIO_WRITEPROTECT_MODE_WP, _const_UFFDIO_WRITEPROTECT_MODE_WP, + "UFFDIO_WRITEPROTECT_MODE_WP" + ); + assert_eq!( + UFFDIO_WRITEPROTECT_MODE_DONTWAKE, _const_UFFDIO_WRITEPROTECT_MODE_DONTWAKE, + "UFFDIO_WRITEPROTECT_MODE_DONTWAKE" + ); assert_eq!(UFFDIO_API, _const_UFFDIO_API, "UFFDIO_API"); assert_eq!(UFFDIO_REGISTER, _const_UFFDIO_REGISTER, "UFFDIO_REGISTER"); assert_eq!( From 43f1f6c9a92ebff93ef975c38eeda58b3022701a Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 25 Jun 2025 14:56:30 +0100 Subject: [PATCH 3/4] deduplicate constant definitions Change the linux{major}_{minor} files that contain handwritten bindings for constants that bindgen doesnt understand to be "incremental" (e.g. linux4_14 only contains constants that are new to 4_14 and just reexports the 4_11 constants for old ones), to avoid redefining an ever increasing set of constants with each version. Also adjust the test that checks the api features and available ioctl constants to only verify that the rust-defined set of features is a subset of the kernel available features. For example running the test on a 6.8 host kernel with the 5.7 feature means the kernel has more features than the uffd crate supports, but this does not mean that the crate definitions are _wrong_, just that they are incomplete. Signed-off-by: Patrick Roy --- userfaultfd-sys/Cargo.toml | 2 +- userfaultfd-sys/src/lib.rs | 44 ++++++++++-- userfaultfd-sys/src/linux4_11.rs | 10 +-- userfaultfd-sys/src/linux4_14.rs | 113 +++---------------------------- userfaultfd-sys/src/linux5_7.rs | 95 +++----------------------- 5 files changed, 60 insertions(+), 204 deletions(-) diff --git a/userfaultfd-sys/Cargo.toml b/userfaultfd-sys/Cargo.toml index 9062ce7..b4c5034 100644 --- a/userfaultfd-sys/Cargo.toml +++ b/userfaultfd-sys/Cargo.toml @@ -19,4 +19,4 @@ cc = "1.0" [features] default = [] linux4_14 = [] -linux5_7 = [] +linux5_7 = ["linux4_14"] diff --git a/userfaultfd-sys/src/lib.rs b/userfaultfd-sys/src/lib.rs index 7164db2..4239150 100644 --- a/userfaultfd-sys/src/lib.rs +++ b/userfaultfd-sys/src/lib.rs @@ -9,18 +9,52 @@ use cfg_if::cfg_if; +mod linux4_11; +#[cfg(feature = "linux4_14")] +mod linux4_14; +#[cfg(feature = "linux5_7")] +mod linux5_7; + cfg_if! { if #[cfg(feature = "linux5_7")] { - mod linux5_7; pub use crate::linux5_7::*; - } - else if #[cfg(feature = "linux4_14")] { - mod linux4_14; + } else if #[cfg(feature = "linux4_14")] { pub use crate::linux4_14::*; } else { - mod linux4_11; pub use crate::linux4_11::*; } } include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +#[cfg(test)] +mod const_tests { + use super::*; + + extern "C" { + static _const_UFFD_API_FEATURES: u64; + static _const_UFFD_API_RANGE_IOCTLS: u64; + static _const_UFFD_API_RANGE_IOCTLS_BASIC: u64; + } + + #[test] + fn consts_correct() { + unsafe { + assert_eq!( + UFFD_API_FEATURES & _const_UFFD_API_FEATURES, + UFFD_API_FEATURES, + "UFFD_API_FEATURES" + ); + assert_eq!( + UFFD_API_RANGE_IOCTLS & _const_UFFD_API_RANGE_IOCTLS, + UFFD_API_RANGE_IOCTLS, + "UFFD_API_RANGE_IOCTLS" + ); + assert_eq!( + UFFD_API_RANGE_IOCTLS_BASIC & _const_UFFD_API_RANGE_IOCTLS_BASIC, + UFFD_API_RANGE_IOCTLS_BASIC, + "UFFD_API_RANGE_IOCTLS_BASIC" + ); + } + } +} diff --git a/userfaultfd-sys/src/linux4_11.rs b/userfaultfd-sys/src/linux4_11.rs index 5192450..2df0cf5 100644 --- a/userfaultfd-sys/src/linux4_11.rs +++ b/userfaultfd-sys/src/linux4_11.rs @@ -14,6 +14,7 @@ pub const UFFD_API_FEATURES: u64 = UFFD_FEATURE_EVENT_FORK pub const UFFD_API_IOCTLS: u64 = 1 << _UFFDIO_REGISTER | 1 << _UFFDIO_UNREGISTER | 1 << _UFFDIO_API; pub const UFFD_API_RANGE_IOCTLS: u64 = 1 << _UFFDIO_WAKE | 1 << _UFFDIO_COPY | 1 << _UFFDIO_ZEROPAGE; +pub const UFFD_API_RANGE_IOCTLS_BASIC: u64 = 0; pub const UFFDIO_REGISTER_MODE_MISSING: u64 = 1 << 0; pub const UFFDIO_REGISTER_MODE_WP: u64 = 1 << 1; @@ -36,7 +37,6 @@ mod const_tests { extern "C" { static _const_UFFD_API: u64; - static _const_UFFD_API_FEATURES: u64; static _const_UFFD_API_IOCTLS: u64; static _const_UFFD_API_RANGE_IOCTLS: u64; static _const_UFFDIO_REGISTER_MODE_MISSING: u64; @@ -56,15 +56,7 @@ mod const_tests { fn consts_correct() { unsafe { assert_eq!(UFFD_API, _const_UFFD_API, "UFFD_API"); - assert_eq!( - UFFD_API_FEATURES, _const_UFFD_API_FEATURES, - "UFFD_API_FEATURES" - ); assert_eq!(UFFD_API_IOCTLS, _const_UFFD_API_IOCTLS, "UFFD_API_IOCTLS"); - assert_eq!( - UFFD_API_RANGE_IOCTLS, _const_UFFD_API_RANGE_IOCTLS, - "UFFD_API_RANGE_IOCTLS" - ); assert_eq!( UFFDIO_REGISTER_MODE_MISSING, _const_UFFDIO_REGISTER_MODE_MISSING, "UFFDIO_REGISTER_MODE_MISSING" diff --git a/userfaultfd-sys/src/linux4_14.rs b/userfaultfd-sys/src/linux4_14.rs index c799d65..be5d331 100644 --- a/userfaultfd-sys/src/linux4_14.rs +++ b/userfaultfd-sys/src/linux4_14.rs @@ -1,107 +1,16 @@ use super::*; +pub use linux4_11::{ + UFFDIO_API, UFFDIO_COPY, UFFDIO_COPY_MODE_DONTWAKE, UFFDIO_COPY_MODE_WP, UFFDIO_REGISTER, + UFFDIO_REGISTER_MODE_MISSING, UFFDIO_REGISTER_MODE_WP, UFFDIO_UNREGISTER, UFFDIO_WAKE, + UFFDIO_ZEROPAGE, UFFDIO_ZEROPAGE_MODE_DONTWAKE, UFFD_API, UFFD_API_IOCTLS, + UFFD_API_RANGE_IOCTLS, +}; + // The following are preprocessor constants that bindgen can't figure out, so we enter them manually // from , and have tests to make sure they're accurate. -pub const UFFD_API: u64 = 0xAA; - -pub const UFFD_API_FEATURES: u64 = UFFD_FEATURE_EVENT_FORK - | UFFD_FEATURE_EVENT_REMAP - | UFFD_FEATURE_EVENT_REMOVE - | UFFD_FEATURE_EVENT_UNMAP - | UFFD_FEATURE_MISSING_HUGETLBFS - | UFFD_FEATURE_MISSING_SHMEM - | UFFD_FEATURE_SIGBUS - | UFFD_FEATURE_THREAD_ID; -pub const UFFD_API_IOCTLS: u64 = 1 << _UFFDIO_REGISTER | 1 << _UFFDIO_UNREGISTER | 1 << _UFFDIO_API; -pub const UFFD_API_RANGE_IOCTLS: u64 = - 1 << _UFFDIO_WAKE | 1 << _UFFDIO_COPY | 1 << _UFFDIO_ZEROPAGE; -pub const UFFD_API_RANGE_IOCTLS_BASIC: u64 = 1 << _UFFDIO_WAKE | 1 << _UFFDIO_COPY; - -pub const UFFDIO_REGISTER_MODE_MISSING: u64 = 1 << 0; -pub const UFFDIO_REGISTER_MODE_WP: u64 = 1 << 1; - -pub const UFFDIO_COPY_MODE_DONTWAKE: u64 = 1 << 0; -pub const UFFDIO_COPY_MODE_WP: u64 = 1 << 1; - -pub const UFFDIO_ZEROPAGE_MODE_DONTWAKE: u64 = 1 << 0; - -pub const UFFDIO_API: u32 = 0xc018aa3f; -pub const UFFDIO_REGISTER: u32 = 0xc020aa00; -pub const UFFDIO_UNREGISTER: u32 = 0x8010aa01; -pub const UFFDIO_WAKE: u32 = 0x8010aa02; -pub const UFFDIO_COPY: u32 = 0xc028aa03; -pub const UFFDIO_ZEROPAGE: u32 = 0xc020aa04; - -#[cfg(test)] -mod const_tests { - use super::*; - - extern "C" { - static _const_UFFD_API: u64; - static _const_UFFD_API_FEATURES: u64; - static _const_UFFD_API_IOCTLS: u64; - static _const_UFFD_API_RANGE_IOCTLS: u64; - static _const_UFFD_API_RANGE_IOCTLS_BASIC: u64; - static _const_UFFDIO_REGISTER_MODE_MISSING: u64; - static _const_UFFDIO_REGISTER_MODE_WP: u64; - static _const_UFFDIO_COPY_MODE_DONTWAKE: u64; - static _const_UFFDIO_COPY_MODE_WP: u64; - static _const_UFFDIO_ZEROPAGE_MODE_DONTWAKE: u64; - static _const_UFFDIO_API: u32; - static _const_UFFDIO_REGISTER: u32; - static _const_UFFDIO_UNREGISTER: u32; - static _const_UFFDIO_WAKE: u32; - static _const_UFFDIO_COPY: u32; - static _const_UFFDIO_ZEROPAGE: u32; - } - - #[test] - fn consts_correct() { - unsafe { - assert_eq!(UFFD_API, _const_UFFD_API, "UFFD_API"); - assert_eq!( - UFFD_API_FEATURES, _const_UFFD_API_FEATURES, - "UFFD_API_FEATURES" - ); - assert_eq!(UFFD_API_IOCTLS, _const_UFFD_API_IOCTLS, "UFFD_API_IOCTLS"); - assert_eq!( - UFFD_API_RANGE_IOCTLS, _const_UFFD_API_RANGE_IOCTLS, - "UFFD_API_RANGE_IOCTLS" - ); - assert_eq!( - UFFD_API_RANGE_IOCTLS_BASIC, _const_UFFD_API_RANGE_IOCTLS_BASIC, - "UFFD_API_RANGE_IOCTLS_BASIC" - ); - assert_eq!( - UFFDIO_REGISTER_MODE_MISSING, _const_UFFDIO_REGISTER_MODE_MISSING, - "UFFDIO_REGISTER_MODE_MISSING" - ); - assert_eq!( - UFFDIO_REGISTER_MODE_WP, _const_UFFDIO_REGISTER_MODE_WP, - "UFFDIO_REGISTER_MODE_WP" - ); - assert_eq!( - UFFDIO_COPY_MODE_DONTWAKE, _const_UFFDIO_COPY_MODE_DONTWAKE, - "UFFDIO_COPY_MODE_DONTWAKE" - ); - assert_eq!( - UFFDIO_COPY_MODE_WP, _const_UFFDIO_COPY_MODE_WP, - "UFFDIO_COPY_MODE_WP" - ); - assert_eq!( - UFFDIO_ZEROPAGE_MODE_DONTWAKE, _const_UFFDIO_ZEROPAGE_MODE_DONTWAKE, - "UFFDIO_ZEROPAGE_MODE_DONTWAKE" - ); - assert_eq!(UFFDIO_API, _const_UFFDIO_API, "UFFDIO_API"); - assert_eq!(UFFDIO_REGISTER, _const_UFFDIO_REGISTER, "UFFDIO_REGISTER"); - assert_eq!( - UFFDIO_UNREGISTER, _const_UFFDIO_UNREGISTER, - "UFFDIO_UNREGISTER" - ); - assert_eq!(UFFDIO_WAKE, _const_UFFDIO_WAKE, "UFFDIO_WAKE"); - assert_eq!(UFFDIO_COPY, _const_UFFDIO_COPY, "UFFDIO_COPY"); - assert_eq!(UFFDIO_ZEROPAGE, _const_UFFDIO_ZEROPAGE, "UFFDIO_ZEROPAGE"); - } - } -} +pub const UFFD_API_FEATURES: u64 = + linux4_11::UFFD_API_FEATURES | UFFD_FEATURE_SIGBUS | UFFD_FEATURE_THREAD_ID; +pub const UFFD_API_RANGE_IOCTLS_BASIC: u64 = + linux4_11::UFFD_API_RANGE_IOCTLS_BASIC | 1 << _UFFDIO_WAKE | 1 << _UFFDIO_COPY; diff --git a/userfaultfd-sys/src/linux5_7.rs b/userfaultfd-sys/src/linux5_7.rs index d25db2f..5cac710 100644 --- a/userfaultfd-sys/src/linux5_7.rs +++ b/userfaultfd-sys/src/linux5_7.rs @@ -1,40 +1,20 @@ use super::*; +pub use linux4_14::{ + UFFDIO_API, UFFDIO_COPY, UFFDIO_COPY_MODE_DONTWAKE, UFFDIO_COPY_MODE_WP, UFFDIO_REGISTER, + UFFDIO_REGISTER_MODE_MISSING, UFFDIO_REGISTER_MODE_WP, UFFDIO_UNREGISTER, UFFDIO_WAKE, + UFFDIO_ZEROPAGE, UFFDIO_ZEROPAGE_MODE_DONTWAKE, UFFD_API, UFFD_API_FEATURES, UFFD_API_IOCTLS, + UFFD_API_RANGE_IOCTLS_BASIC, +}; + // The following are preprocessor constants that bindgen can't figure out, so we enter them manually // from , and have tests to make sure they're accurate. -pub const UFFD_API: u64 = 0xAA; - -pub const UFFD_API_FEATURES: u64 = UFFD_FEATURE_PAGEFAULT_FLAG_WP - | UFFD_FEATURE_EVENT_FORK - | UFFD_FEATURE_EVENT_REMAP - | UFFD_FEATURE_EVENT_REMOVE - | UFFD_FEATURE_EVENT_UNMAP - | UFFD_FEATURE_MISSING_HUGETLBFS - | UFFD_FEATURE_MISSING_SHMEM - | UFFD_FEATURE_SIGBUS - | UFFD_FEATURE_THREAD_ID; -pub const UFFD_API_IOCTLS: u64 = 1 << _UFFDIO_REGISTER | 1 << _UFFDIO_UNREGISTER | 1 << _UFFDIO_API; - -pub const UFFD_API_RANGE_IOCTLS: u64 = - 1 << _UFFDIO_WAKE | 1 << _UFFDIO_COPY | 1 << _UFFDIO_ZEROPAGE | 1 << _UFFDIO_WRITEPROTECT; +pub const UFFD_API_RANGE_IOCTLS: u64 = linux4_14::UFFD_API_RANGE_IOCTLS | 1 << _UFFDIO_WRITEPROTECT; -pub const UFFD_API_RANGE_IOCTLS_BASIC: u64 = 1 << _UFFDIO_WAKE | 1 << _UFFDIO_COPY; - -pub const UFFDIO_REGISTER_MODE_MISSING: u64 = 1 << 0; -pub const UFFDIO_REGISTER_MODE_WP: u64 = 1 << 1; -pub const UFFDIO_COPY_MODE_DONTWAKE: u64 = 1 << 0; -pub const UFFDIO_COPY_MODE_WP: u64 = 1 << 1; -pub const UFFDIO_ZEROPAGE_MODE_DONTWAKE: u64 = 1 << 0; pub const UFFDIO_WRITEPROTECT_MODE_WP: u64 = 1 << 0; pub const UFFDIO_WRITEPROTECT_MODE_DONTWAKE: u64 = 1 << 1; -pub const UFFDIO_API: u32 = 0xc018aa3f; -pub const UFFDIO_REGISTER: u32 = 0xc020aa00; -pub const UFFDIO_UNREGISTER: u32 = 0x8010aa01; -pub const UFFDIO_WAKE: u32 = 0x8010aa02; -pub const UFFDIO_COPY: u32 = 0xc028aa03; -pub const UFFDIO_ZEROPAGE: u32 = 0xc020aa04; pub const UFFDIO_WRITEPROTECT: u32 = 0xc018aa06; #[cfg(test)] @@ -42,64 +22,14 @@ mod const_tests { use super::*; extern "C" { - static _const_UFFD_API: u64; - static _const_UFFD_API_FEATURES: u64; - static _const_UFFD_API_IOCTLS: u64; - static _const_UFFD_API_RANGE_IOCTLS: u64; - static _const_UFFD_API_RANGE_IOCTLS_BASIC: u64; - static _const_UFFDIO_REGISTER_MODE_MISSING: u64; - static _const_UFFDIO_REGISTER_MODE_WP: u64; - static _const_UFFDIO_COPY_MODE_DONTWAKE: u64; - static _const_UFFDIO_COPY_MODE_WP: u64; - static _const_UFFDIO_ZEROPAGE_MODE_DONTWAKE: u64; static _const_UFFDIO_WRITEPROTECT_MODE_WP: u64; static _const_UFFDIO_WRITEPROTECT_MODE_DONTWAKE: u64; - static _const_UFFDIO_API: u32; - static _const_UFFDIO_REGISTER: u32; - static _const_UFFDIO_UNREGISTER: u32; - static _const_UFFDIO_WAKE: u32; - static _const_UFFDIO_COPY: u32; - static _const_UFFDIO_ZEROPAGE: u32; static _const_UFFDIO_WRITEPROTECT: u32; } #[test] fn consts_correct() { unsafe { - assert_eq!(UFFD_API, _const_UFFD_API, "UFFD_API"); - assert_eq!( - UFFD_API_FEATURES, _const_UFFD_API_FEATURES, - "UFFD_API_FEATURES" - ); - assert_eq!(UFFD_API_IOCTLS, _const_UFFD_API_IOCTLS, "UFFD_API_IOCTLS"); - assert_eq!( - UFFD_API_RANGE_IOCTLS, _const_UFFD_API_RANGE_IOCTLS, - "UFFD_API_RANGE_IOCTLS" - ); - assert_eq!( - UFFD_API_RANGE_IOCTLS_BASIC, _const_UFFD_API_RANGE_IOCTLS_BASIC, - "UFFD_API_RANGE_IOCTLS_BASIC" - ); - assert_eq!( - UFFDIO_REGISTER_MODE_MISSING, _const_UFFDIO_REGISTER_MODE_MISSING, - "UFFDIO_REGISTER_MODE_MISSING" - ); - assert_eq!( - UFFDIO_REGISTER_MODE_WP, _const_UFFDIO_REGISTER_MODE_WP, - "UFFDIO_REGISTER_MODE_WP" - ); - assert_eq!( - UFFDIO_COPY_MODE_DONTWAKE, _const_UFFDIO_COPY_MODE_DONTWAKE, - "UFFDIO_COPY_MODE_DONTWAKE" - ); - assert_eq!( - UFFDIO_COPY_MODE_WP, _const_UFFDIO_COPY_MODE_WP, - "UFFDIO_COPY_MODE_WP" - ); - assert_eq!( - UFFDIO_ZEROPAGE_MODE_DONTWAKE, _const_UFFDIO_ZEROPAGE_MODE_DONTWAKE, - "UFFDIO_ZEROPAGE_MODE_DONTWAKE" - ); assert_eq!( UFFDIO_WRITEPROTECT_MODE_WP, _const_UFFDIO_WRITEPROTECT_MODE_WP, "UFFDIO_WRITEPROTECT_MODE_WP" @@ -108,15 +38,6 @@ mod const_tests { UFFDIO_WRITEPROTECT_MODE_DONTWAKE, _const_UFFDIO_WRITEPROTECT_MODE_DONTWAKE, "UFFDIO_WRITEPROTECT_MODE_DONTWAKE" ); - assert_eq!(UFFDIO_API, _const_UFFDIO_API, "UFFDIO_API"); - assert_eq!(UFFDIO_REGISTER, _const_UFFDIO_REGISTER, "UFFDIO_REGISTER"); - assert_eq!( - UFFDIO_UNREGISTER, _const_UFFDIO_UNREGISTER, - "UFFDIO_UNREGISTER" - ); - assert_eq!(UFFDIO_WAKE, _const_UFFDIO_WAKE, "UFFDIO_WAKE"); - assert_eq!(UFFDIO_COPY, _const_UFFDIO_COPY, "UFFDIO_COPY"); - assert_eq!(UFFDIO_ZEROPAGE, _const_UFFDIO_ZEROPAGE, "UFFDIO_ZEROPAGE"); assert_eq!( UFFDIO_WRITEPROTECT, _const_UFFDIO_WRITEPROTECT, "UFFDIO_WRITEPROTECT" From 6902c2719955bfa4bf3d0fcae922b80b3bfc7ff3 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 25 Jun 2025 15:33:18 +0100 Subject: [PATCH 4/4] Add support for UFFDIO_CONTINUE / MODE_MINOR With Linux 5.13, uffd got support for minor page fault notifications (e.g. a page was already in the page cache, but not present in the userspace page tables). Notifications are enabled by registering with UFFDIO_REGISTER_MODE_MINOR, and resolved via the UFFDIO_CONTINUE ioctl. Add support for these in userfaultfd-sys, as well as utility wrappers in the main crate. Signed-off-by: Patrick Roy --- Cargo.toml | 1 + src/event.rs | 30 ++++++++++++++-------- src/lib.rs | 35 ++++++++++++++++++++++++++ src/raw.rs | 2 ++ userfaultfd-sys/Cargo.toml | 1 + userfaultfd-sys/src/consts.c | 12 +++++++++ userfaultfd-sys/src/lib.rs | 7 +++++- userfaultfd-sys/src/linux5_13.rs | 43 ++++++++++++++++++++++++++++++++ 8 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 userfaultfd-sys/src/linux5_13.rs diff --git a/Cargo.toml b/Cargo.toml index ec56fd4..134c7a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ nix = { version = "0.27", features = ["poll", "mman", "feature"] } default = [] linux4_14 = ["userfaultfd-sys/linux4_14", "nix/process"] linux5_7 = ["userfaultfd-sys/linux5_7"] +linux5_13 = ["userfaultfd-sys/linux5_13"] \ No newline at end of file diff --git a/src/event.rs b/src/event.rs index 8ea75f2..bdc3560 100644 --- a/src/event.rs +++ b/src/event.rs @@ -21,6 +21,10 @@ pub enum FaultKind { /// The fault was a write on a write-protected page. #[cfg(feature = "linux5_7")] WriteProtected, + // The fault was a minor page fault, meaning the page was present in the page cache, + // but the userspace page table entry was missing. + #[cfg(feature = "linux5_13")] + Minor, } /// Events from the userfaultfd object that are read by `Uffd::read_event()`. @@ -82,17 +86,21 @@ impl Event { match msg.event { raw::UFFD_EVENT_PAGEFAULT => { let pagefault = unsafe { msg.arg.pagefault }; - cfg_if::cfg_if!( - if #[cfg(feature = "linux5_7")] { - let kind = if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WP != 0 { - FaultKind::WriteProtected - } else { - FaultKind::Missing - }; - } else { - let kind = FaultKind::Missing; - } - ); + + #[allow(unused_mut)] + let mut kind = FaultKind::Missing; + + // The below two flags are mutually exclusive (it does not make sense + // to have a minor fault that is a write-protect fault at the same time. + #[cfg(feature = "linux5_7")] + if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WP != 0 { + kind = FaultKind::WriteProtected; + } + + #[cfg(feature = "linux5_13")] + if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_MINOR != 0 { + kind = FaultKind::Minor + } let rw = if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WRITE == 0 { ReadWrite::Read diff --git a/src/lib.rs b/src/lib.rs index af0e530..3ce9bb5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,6 +87,9 @@ bitflags! { /// Registers the range for write faults. #[cfg(feature = "linux5_7")] const WRITE_PROTECT = raw::UFFDIO_REGISTER_MODE_WP; + // Registers the range for minor faults. + #[cfg(feature = "linux5_13")] + const MINOR = raw::UFFDIO_REGISTER_MODE_MINOR; } } @@ -269,6 +272,38 @@ impl Uffd { Ok(()) } + /// Resolves minor faults for a range. + /// + /// If `wake` is `true`, wake up the thread waiting for page fault resolution on the memory + /// address range. + /// + /// Returns the number of bytes actually mapped. If this differs from `len`, then the ioctl + /// returned EAGAIN. + #[cfg(feature = "linux5_13")] + pub fn r#continue(&self, start: *mut c_void, len: usize, wake: bool) -> Result { + let mut ioctl = raw::uffdio_continue { + range: raw::uffdio_range { + start: start as u64, + len: len as u64, + }, + mode: if wake { + 0 + } else { + raw::UFFDIO_CONTINUE_MODE_DONTWAKE + }, + mapped: 0, + }; + + let r = + unsafe { raw::r#continue(self.as_raw_fd(), &mut ioctl as *mut raw::uffdio_continue) }; + + match r { + Err(Errno::EAGAIN) if ioctl.mapped > 0 => Ok(ioctl.mapped as u64), + Err(err) => Err(err.into()), + Ok(_) => Ok(ioctl.mapped as u64), + } + } + /// Read an `Event` from the userfaultfd object. /// /// If the `Uffd` object was created with `non_blocking` set to `false`, this will block until diff --git a/src/raw.rs b/src/raw.rs index 28039c7..385b1f2 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -23,6 +23,8 @@ nix::ioctl_readwrite!( _UFFDIO_WRITEPROTECT, uffdio_writeprotect ); +#[cfg(feature = "linux5_13")] +nix::ioctl_readwrite!(r#continue, UFFDIO, _UFFDIO_CONTINUE, uffdio_continue); // ioctls for /dev/userfaultfd diff --git a/userfaultfd-sys/Cargo.toml b/userfaultfd-sys/Cargo.toml index b4c5034..77be582 100644 --- a/userfaultfd-sys/Cargo.toml +++ b/userfaultfd-sys/Cargo.toml @@ -20,3 +20,4 @@ cc = "1.0" default = [] linux4_14 = [] linux5_7 = ["linux4_14"] +linux5_13 = ["linux5_7"] diff --git a/userfaultfd-sys/src/consts.c b/userfaultfd-sys/src/consts.c index a380edd..7969a56 100644 --- a/userfaultfd-sys/src/consts.c +++ b/userfaultfd-sys/src/consts.c @@ -30,6 +30,10 @@ const __u64 _const_UFFDIO_REGISTER_MODE_MISSING = UFFDIO_REGISTER_MODE_MISSING; const __u64 _const_UFFDIO_REGISTER_MODE_WP = UFFDIO_REGISTER_MODE_WP; #endif +#ifdef UFFDIO_REGISTER_MODE_MINOR +const __u64 _const_UFFDIO_REGISTER_MODE_MINOR = UFFDIO_REGISTER_MODE_MINOR; +#endif + #ifdef UFFDIO_COPY_MODE_DONTWAKE const __u64 _const_UFFDIO_COPY_MODE_DONTWAKE = UFFDIO_COPY_MODE_DONTWAKE; #endif @@ -46,6 +50,10 @@ const __u64 _const_UFFDIO_ZEROPAGE_MODE_DONTWAKE = UFFDIO_ZEROPAGE_MODE_DONTWAKE const __u64 _const_UFFDIO_WRITEPROTECT_MODE_WP = UFFDIO_WRITEPROTECT_MODE_WP; #endif +#ifdef UFFDIO_CONTINUE_MODE_DONTWAKE +const __u64 _const_UFFDIO_CONTINUE_MODE_DONTWAKE = UFFDIO_CONTINUE_MODE_DONTWAKE; +#endif + #ifdef UFFDIO_WRITEPROTECT_MODE_DONTWAKE const __u64 _const_UFFDIO_WRITEPROTECT_MODE_DONTWAKE = UFFDIO_WRITEPROTECT_MODE_DONTWAKE; #endif @@ -78,6 +86,10 @@ const __u32 _const_UFFDIO_ZEROPAGE = UFFDIO_ZEROPAGE; const __u32 _const_UFFDIO_WRITEPROTECT = UFFDIO_WRITEPROTECT; #endif +#ifdef UFFDIO_CONTINUE +const __u32 _const_UFFDIO_CONTINUE = UFFDIO_CONTINUE; +#endif + #ifdef USERFAULTFD_IOC const __u32 _const_USERFAULTFD_IOC = USERFAULTFD_IOC; #endif diff --git a/userfaultfd-sys/src/lib.rs b/userfaultfd-sys/src/lib.rs index 4239150..37d80ed 100644 --- a/userfaultfd-sys/src/lib.rs +++ b/userfaultfd-sys/src/lib.rs @@ -15,8 +15,13 @@ mod linux4_14; #[cfg(feature = "linux5_7")] mod linux5_7; +#[cfg(feature = "linux5_13")] +mod linux5_13; + cfg_if! { - if #[cfg(feature = "linux5_7")] { + if #[cfg(feature = "linux5_13")] { + pub use crate::linux5_13::*; + } else if #[cfg(feature = "linux5_7")] { pub use crate::linux5_7::*; } else if #[cfg(feature = "linux4_14")] { pub use crate::linux4_14::*; diff --git a/userfaultfd-sys/src/linux5_13.rs b/userfaultfd-sys/src/linux5_13.rs new file mode 100644 index 0000000..23ac209 --- /dev/null +++ b/userfaultfd-sys/src/linux5_13.rs @@ -0,0 +1,43 @@ +use super::*; + +pub use linux5_7::{ + UFFDIO_API, UFFDIO_COPY, UFFDIO_COPY_MODE_DONTWAKE, UFFDIO_COPY_MODE_WP, UFFDIO_REGISTER, + UFFDIO_REGISTER_MODE_MISSING, UFFDIO_REGISTER_MODE_WP, UFFDIO_UNREGISTER, UFFDIO_WAKE, + UFFDIO_WRITEPROTECT, UFFDIO_WRITEPROTECT_MODE_DONTWAKE, UFFDIO_WRITEPROTECT_MODE_WP, + UFFDIO_ZEROPAGE, UFFDIO_ZEROPAGE_MODE_DONTWAKE, UFFD_API, UFFD_API_FEATURES, UFFD_API_IOCTLS, + UFFD_API_RANGE_IOCTLS_BASIC, +}; + +pub const UFFD_API_RANGE_IOCTLS: u64 = linux5_7::UFFD_API_RANGE_IOCTLS | 1 << _UFFDIO_CONTINUE; + +pub const UFFDIO_REGISTER_MODE_MINOR: u64 = 1 << 2; + +pub const UFFDIO_CONTINUE_MODE_DONTWAKE: u64 = 1 << 0; + +pub const UFFDIO_CONTINUE: u32 = 0xc020aa07; + +#[cfg(test)] +mod const_tests { + use super::*; + + extern "C" { + static _const_UFFDIO_REGISTER_MODE_MINOR: u64; + static _const_UFFDIO_CONTINUE_MODE_DONTWAKE: u64; + static _const_UFFDIO_CONTINUE: u32; + } + + #[test] + fn consts_correct() { + unsafe { + assert_eq!( + UFFDIO_REGISTER_MODE_MINOR, _const_UFFDIO_REGISTER_MODE_MINOR, + "UFFDIO_REGISTER_MODE_MINOR" + ); + assert_eq!( + UFFDIO_CONTINUE_MODE_DONTWAKE, _const_UFFDIO_CONTINUE_MODE_DONTWAKE, + "UFFDIO_CONTINUE_MODE_DONTWAKE" + ); + assert_eq!(UFFDIO_CONTINUE, _const_UFFDIO_CONTINUE, "UFFDIO_CONTINUE"); + } + } +}