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

tempfile: Add support for TempFile #239

Merged
merged 1 commit into from Apr 29, 2022

Conversation

cgwalters
Copy link
Contributor

The cap-tempfile crate ironically only supports temporary
directories right now.

This adapts code I wrote as part of https://github.com/coreos/cap-std-ext/
to add support for temporary files that can also be atomically
linked into place.

@cgwalters
Copy link
Contributor Author

Side note...I have kind of lost track of how many times I have implemented Linux O_TMPFILE stuff. A long time ago I wrote a C implementation of this in ostree, then cleaned it up and factored it out into https://gitlab.gnome.org/GNOME/libglnx where I also did fd-relative i.e. C openat() stuff (but still not capability/openat2 based).

Then...Rust came along, I eventually decided to build on openat and reimplement it again in openat-ext.

So this is at least the 4th time, but I am pretty sure I also did some stuff around this in glib but at this point that's getting to be a distant memory.

But anyways, I for one am looking forward to going into the second half of my career mostly writing Rust code, and also hopefully not writing basic code to manipulate temporary files again...

@cgwalters
Copy link
Contributor Author

OK I belatedly realized that having the API pass in Permissions was really just a workaround for a long ago fixed glibc bug. I think we can relatively safely rely on that being fixed - if some user shows up bitten by it, we could have rustix do the direct syscall path, or perhaps try to detect it and fall back to not using O_TMPFILE.

(The latter path will want the "compute current process umask" code that lives in the test suite for now)

Copy link
Member

@sunfishcode sunfishcode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I belatedly realized that having the API pass in Permissions was really just a workaround for a long ago fixed glibc bug. I think we can relatively safely rely on that being fixed - if some user shows up bitten by it, we could have rustix do the direct syscall path, or perhaps try to detect it and fall back to not using O_TMPFILE.

According to the linked bug report, the bug is present in glibc 2.20, however Rust itself is only now updating its minimum glibc version to 2.17. Unless it's a major obstacle, I'd prefer to have cap-std continue to support the versions of glibc supported by Rust.

cap-tempfile/src/lib.rs Outdated Show resolved Hide resolved
cap-tempfile/src/tempfile.rs Outdated Show resolved Hide resolved
cap-tempfile/src/tempfile.rs Outdated Show resolved Hide resolved
cap-tempfile/src/tempfile.rs Outdated Show resolved Hide resolved
cap-tempfile/src/tempfile.rs Show resolved Hide resolved
cap-tempfile/src/tempfile.rs Show resolved Hide resolved
@cgwalters cgwalters force-pushed the tempfile-prep branch 2 times, most recently from 0c99e11 to 1f5f2a5 Compare March 29, 2022 14:01
@cgwalters
Copy link
Contributor Author

OK, MacOS and Windows should compile at least now.

@cgwalters
Copy link
Contributor Author

cgwalters commented Mar 29, 2022

According to the linked bug report, the bug is present in glibc 2.20, however Rust itself is only now rust-lang/rust#95026. Unless it's a major obstacle, I'd prefer to have cap-std continue to support the versions of glibc supported by Rust.

Yeah, fair. Though it should be noted, it looks like the tempfile crate uses this unconditionally today.

(We need to match the same errno fallbacks too, will look at that) done

Perhaps one possible trick here could be to use a weak symbol to detect if we have a new enough glibc? I'd guess rustix could expose some API for this, given that it's effectively doing it internally; for example if we have getrandom we know we have a new enough glibc.

The `cap-tempfile` crate ironically only supports temporary
directories right now.

This adapts code I wrote as part of https://github.com/coreos/cap-std-ext/
to add support for temporary files that can also be atomically
linked into place.
@cgwalters
Copy link
Contributor Author

Perhaps one possible trick here could be to use a weak symbol to detect if we have a new enough glibc?

Or we could use openat2 - we already have all the infrastructure for that. The downside though is this would exclude systems that have new enough Linux for O_TMPFILE but not openat2. But OTOH people using cap-std on such old systems are already going to be paying a high price and this would only be another small hit.

@cgwalters
Copy link
Contributor Author

Or we could use openat2

I think to do that nicely we'd want to make open_beneath from cap-primitives be pub or so? There are important things like handling android and seccomp there.

@sunfishcode
Copy link
Member

(Don't worry about the windows-2016 failure btw.)

I think this PR is in good shape; I just want to figure out the O_TMPFILE situation here.

According to the linked bug report, the bug is present in glibc 2.20, however Rust itself is only now rust-lang/rust#95026. Unless it's a major obstacle, I'd prefer to have cap-std continue to support the versions of glibc supported by Rust.

Yeah, fair. Though it should be noted, it looks like the tempfile crate uses this unconditionally today.

If I understand correctly, that code in the tempfile is using open rather than openat, so on x86_64 and possibly others the bug is hidden in practice (according to the glibc bug linked above).

Perhaps one possible trick here could be to use a weak symbol to detect if we have a new enough glibc? I'd guess rustix could expose some API for this, given that it's effectively doing it internally; for example if we have getrandom we know we have a new enough glibc.

My sense here is that this is something that would be better to handle internally in rustix, rather than exposing an API. Detecting it by using the weak! macro looking for getrandom sounds reasonable to me, and for a fallback when we don't have a new enough glibc, rustix could use libc::syscall(SYS_openat, ...) , which avoids the bug. Does that sound workable?

@cgwalters
Copy link
Contributor Author

If I understand correctly, that code in the tempfile is using open rather than openat, so on x86_64 and possibly others the bug is hidden in practice (according to the glibc bug linked above).

Ah yes, I think you're right.

My sense here is that this is something that would be better to handle internally in rustix, rather than exposing an API. Detecting it by using the weak! macro looking for getrandom sounds reasonable to me, and for a fallback when we don't have a new enough glibc, rustix could use libc::syscall(SYS_openat, ...) , which avoids the bug. Does that sound workable?

Sounds good to me!

@cgwalters
Copy link
Contributor Author

Detecting it by using the weak! macro looking for getrandom sounds reasonable to me

I briefly looked at this, and today the weak! is wrapped up in weak_or_syscall!, and I couldn't figure out an elegant way to make the weak! inside there conditionally pub(crate) or so.

Here's my hack to duplicate it:

From 84dfe8d0600a39c907a887ae7237a5f24a6410d5 Mon Sep 17 00:00:00 2001
From: Colin Walters <walters@verbum.org>
Date: Fri, 1 Apr 2022 08:45:09 -0400
Subject: [PATCH] wip

---
 src/imp/libc/mod.rs           | 6 ++++++
 src/imp/libc/rand/syscalls.rs | 3 +++
 src/imp/libc/weak.rs          | 4 ++--
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/imp/libc/mod.rs b/src/imp/libc/mod.rs
index 96a040b..1fbcbbd 100644
--- a/src/imp/libc/mod.rs
+++ b/src/imp/libc/mod.rs
@@ -62,3 +62,9 @@ pub(crate) mod rand;
 pub(crate) mod thread;
 #[cfg(not(windows))]
 pub(crate) mod time;
+
+/// If the host libc is glibc, detect if it's at least version 2.25.
+#[cfg(not(windows))]
+pub(crate) fn is_atleast_glibc_2_25() -> bool {
+    rand::syscalls::libc_getrandom_weak.get().is_some()
+}
diff --git a/src/imp/libc/rand/syscalls.rs b/src/imp/libc/rand/syscalls.rs
index e860f40..380f643 100644
--- a/src/imp/libc/rand/syscalls.rs
+++ b/src/imp/libc/rand/syscalls.rs
@@ -5,6 +5,9 @@
 #[cfg(target_os = "linux")]
 use {super::super::c, super::super::conv::ret_ssize_t, crate::io, crate::rand::GetRandomFlags};
 
+// A duplicate weak symbol because we use this to detect the glibc version.
+weak! { pub(crate) fn libc_getrandom_weak(*mut c::c_void, c::size_t, c::c_uint) -> c::ssize_t }
+
 #[cfg(target_os = "linux")]
 pub(crate) fn getrandom(buf: &mut [u8], flags: GetRandomFlags) -> io::Result<usize> {
     // `getrandom` wasn't supported in glibc until 2.25.
diff --git a/src/imp/libc/weak.rs b/src/imp/libc/weak.rs
index aecc081..203969f 100644
--- a/src/imp/libc/weak.rs
+++ b/src/imp/libc/weak.rs
@@ -36,9 +36,9 @@ const NULL: *mut c_void = null_mut();
 const INVALID: *mut c_void = 1 as *mut c_void;
 
 macro_rules! weak {
-    (fn $name:ident($($t:ty),*) -> $ret:ty) => (
+    ($vis:vis fn $name:ident($($t:ty),*) -> $ret:ty) => (
         #[allow(non_upper_case_globals)]
-        static $name: $crate::imp::weak::Weak<unsafe extern fn($($t),*) -> $ret> =
+        $vis static $name: $crate::imp::weak::Weak<unsafe extern fn($($t),*) -> $ret> =
             $crate::imp::weak::Weak::new(concat!(stringify!($name), '\0'));
     )
 }
-- 
2.35.1

cgwalters added a commit to cgwalters/rustix that referenced this pull request Apr 1, 2022
This works around https://sourceware.org/bugzilla/show_bug.cgi?id=17523
Where the `openat` wrapper is buggy if `O_TMPFILE` is provided
on (now quite old) glibc versions.

Use the presence of the `getrandom` symbol to detect if we have
a fixed glibc or not.

Will be used by bytecodealliance/cap-std#239
which is wiring up use of `O_TMPFILE` in cap-std.
@cgwalters
Copy link
Contributor Author

OK moving that bit to bytecodealliance/rustix#268

cgwalters added a commit to cgwalters/rustix that referenced this pull request Apr 1, 2022
This works around https://sourceware.org/bugzilla/show_bug.cgi?id=17523
Where the `openat` wrapper is buggy if `O_TMPFILE` is provided
on (now quite old) glibc versions.

Use the presence of the `getrandom` symbol to detect if we have
a fixed glibc or not.

Will be used by bytecodealliance/cap-std#239
which is wiring up use of `O_TMPFILE` in cap-std.
cgwalters added a commit to cgwalters/rustix that referenced this pull request Apr 1, 2022
This works around https://sourceware.org/bugzilla/show_bug.cgi?id=17523
Where the `openat` wrapper is buggy if `O_TMPFILE` is provided
on (now quite old) glibc versions.

Use the presence of the `getrandom` symbol to detect if we have
a fixed glibc or not.

Will be used by bytecodealliance/cap-std#239
which is wiring up use of `O_TMPFILE` in cap-std.
cgwalters added a commit to cgwalters/rustix that referenced this pull request Apr 2, 2022
This works around https://sourceware.org/bugzilla/show_bug.cgi?id=17523
Where the `openat` wrapper is buggy if `O_TMPFILE` is provided
on (now quite old) glibc versions.

Use the presence of the `getrandom` symbol to detect if we have
a fixed glibc or not.

Will be used by bytecodealliance/cap-std#239
which is wiring up use of `O_TMPFILE` in cap-std.
cgwalters added a commit to cgwalters/rustix that referenced this pull request Apr 5, 2022
This works around https://sourceware.org/bugzilla/show_bug.cgi?id=17523
Where the `openat` wrapper is buggy if `O_TMPFILE` is provided
on (now quite old) glibc versions.

Use the presence of the `getrandom` symbol to detect if we have
a fixed glibc or not.

Will be used by bytecodealliance/cap-std#239
which is wiring up use of `O_TMPFILE` in cap-std.
cgwalters added a commit to cgwalters/rustix that referenced this pull request Apr 5, 2022
This works around https://sourceware.org/bugzilla/show_bug.cgi?id=17523
Where the `openat` wrapper is buggy if `O_TMPFILE` is provided
on (now quite old) glibc versions.

Use the presence of the `getrandom` symbol to detect if we have
a fixed glibc or not.

Will be used by bytecodealliance/cap-std#239
which is wiring up use of `O_TMPFILE` in cap-std.
sunfishcode pushed a commit to bytecodealliance/rustix that referenced this pull request Apr 5, 2022
This works around https://sourceware.org/bugzilla/show_bug.cgi?id=17523
Where the `openat` wrapper is buggy if `O_TMPFILE` is provided
on (now quite old) glibc versions.

Use the presence of the `getrandom` symbol to detect if we have
a fixed glibc or not.

Will be used by bytecodealliance/cap-std#239
which is wiring up use of `O_TMPFILE` in cap-std.
sunfishcode pushed a commit to bytecodealliance/rustix that referenced this pull request Apr 13, 2022
This works around https://sourceware.org/bugzilla/show_bug.cgi?id=17523
Where the `openat` wrapper is buggy if `O_TMPFILE` is provided
on (now quite old) glibc versions.

Use the presence of the `getrandom` symbol to detect if we have
a fixed glibc or not.

Will be used by bytecodealliance/cap-std#239
which is wiring up use of `O_TMPFILE` in cap-std.
@sunfishcode
Copy link
Member

bytecodealliance/rustix#268 is now released, in rustix 0.33.6.

@cgwalters
Copy link
Contributor Author

Anything else that needs doing from my side for this?

@sunfishcode
Copy link
Member

Ah, thanks for the ping. Looks like this is ready to go, the CI failure is just windows-2016 which is fixed on main.

@sunfishcode sunfishcode merged commit a521444 into bytecodealliance:main Apr 29, 2022
cgwalters added a commit to cgwalters/cap-std-ext that referenced this pull request May 2, 2022
Now that all the core logic has been drained into
`cap_tempfile::TempFile` in bytecodealliance/cap-std#239
we can just use it directly.

NOTE: This changes the semantics of some of the APIs; the previous
`replace()` API tried to do "reuse previous file mode if it existed"
but I think that's just a confusing trap.  Anything writing the file
in the general case needs to know the permissions it wants to use.
@cgwalters
Copy link
Contributor Author

Follow up to this in coreos/cap-std-ext#12

cgwalters added a commit to cgwalters/cap-std-ext that referenced this pull request May 2, 2022
Now that all the core logic has been drained into
`cap_tempfile::TempFile` in bytecodealliance/cap-std#239
we can just use it directly.

NOTE: This changes the semantics of some of the APIs; the previous
`replace()` API tried to do "reuse previous file mode if it existed"
but I think that's just a confusing trap.  Anything writing the file
in the general case needs to know the permissions it wants to use.
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.

None yet

2 participants