Skip to content

fix: fall back to userfaultfd(2) when /dev/userfaultfd is unreadable#84

Open
ShivanshVij wants to merge 1 commit intobytecodealliance:mainfrom
ShivanshVij:shiv/fix-device-fallback
Open

fix: fall back to userfaultfd(2) when /dev/userfaultfd is unreadable#84
ShivanshVij wants to merge 1 commit intobytecodealliance:mainfrom
ShivanshVij:shiv/fix-device-fallback

Conversation

@ShivanshVij
Copy link
Copy Markdown

Current Implementation

In the current implementation of UffdBuilder::create(), we only fall back to the userfaultfd(2) syscall when /dev/userfaultfd doesn't exist. If the device node is present but the calling process lacks permission to open it (EACCES/EPERM), the builder returns Error::OpenDevUserfaultfd and never tries the syscall. This is documented in the current implementation, however the linux kernel accepts either entry point and the process is allowed to hold the capabilities (e.g. CAP_SYS_PTRACE) needed for the syscall path without having access to the /dev/userfaultfd path.

One way to enable this is by running sysctl vm.unprivileged_userfaultfd=1 - this does not grant the process or user acess to /dev/userfaultfd but it does allow the userfaultd(2) approach to work.

Right now downstream consumers are forced to bypass the builder entirely to support this use case.

Fix

The fix is simple. We've just extended the fallback arm in open_file_descriptor to also match ErrorKind::PermissionDenied (which covers both EACCES and EPERM), and fall through to trying userfaultd(2). Any real errors (ENOMEM, ENOTDIR, etc.) still surface as Error::OpenDevUserfaultfd, so existing users should not be affected unless they rely on this specific scenario (which is very unlikely).

Test

We've also added a unit test that creates a chmod 0o000 stand-in file, points the builder at it, and asserts the syscall fallback succeeds. The test self-skips under root (where mode 000 is bypassed). To make the helper drivable from a test, open_file_descriptor now takes the device path as a parameter; its sole caller in create() passes Path::new(UFFD_DEVICE_PATH). This does not result in a public API change.

I've validated the test fails without the fix, and does not fail with the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant