dd: remove unnecessary fcntl dependency for stdout#12208
Conversation
Replace OwnedFileDescriptorOrHandle::from() with borrowed File pattern using ManuallyDrop and from_raw_fd/from_raw_handle. This eliminates the unnecessary fcntl syscall from try_clone_to_owned() when dd writes to stdout, fixing failures in restricted containers where fcntl is unavailable. Fixes uutils#12157
|
Can we do same things without unsafe at least on unix? |
| let file = ManuallyDrop::new(unsafe { File::from_raw_handle(io::stdout().as_raw_handle()) }); | ||
|
|
||
| Self::prepare_file(fx.into_file(), settings) | ||
| Self::prepare_file(ManuallyDrop::into_inner(file), settings) |
There was a problem hiding this comment.
This is potentially unsound, we must guarantee stdout is never dropped.
There was a problem hiding this comment.
You're absolutely correct. The previous version created a File from stdout's raw fd without duplicating it, which meant when the File was dropped, it would close stdout's fd and potentially lose buffered data.
I've fixed this by:
- Changing
Dest::Stdoutto holdManuallyDrop<File>instead ofFile - Adding a new
Dest::FileFromStdoutvariant for when stdout is redirected to a seekable file - Both variants now guarantee stdout's file descriptor is never closed when dropped
This preserves the optimization of avoiding the fcntl syscall while maintaining soundness. All tests pass.
Thanks for catching this!
I don't think so, but we can use a safe wrapper: #12210 |
|
|
[Edit] this spreads |
The previous implementation created a File from stdout's raw fd without duplicating it, then stored it in Dest::Stdout(File) or Dest::File(File, ..). When dropped, this would close the original stdout fd, potentially losing buffered data. Changes: - Dest::Stdout now uses ManuallyDrop<File> to prevent drop - Added Dest::FileFromStdout variant for seekable stdout redirects - Both variants guarantee stdout's fd is never closed - All match arms updated to handle ManuallyDrop deref Fixes soundness issue raised by @xtqqczze.
I think the unsafe usage in |
|
We can concentrate rustix to uucore too. |
|
If we tried adding platforms without FD, it would be hard to support it (without |
|
Is Windows the only platform (outside of no_std) that doesn't support file descriptors? |
|
How about UEFI? |
|
|
|
I prefer using backport crate by core member of Rust |
|
What syscall does Windows call with previous code? Does it fail with common usecases? I think we can still clone on Windows. |
|
On Windows, The Windows path still benefits from avoiding the clone though. it's an unnecessary handle duplication. The ManuallyDrop approach is simpler on both platforms: borrow the fd/handle instead of cloning it. Re: backport crate, happy to look at that if you have a specific one in mind. The ManuallyDrop pattern here is pretty standard for borrowing file descriptors (rustix uses the same approach internally), but open to alternatives. |
|
I think I'm gonna put it back in drafts, this one needs to cook some more |
|
We need to wait maintainer's response about #12210 . |
|
Coming back to this with answers to each thread, after testing more carefully and reading #12210. Re soundness (xtqqczze, inline R1): addressed in 46cec8b. Re Re #12210 (xtqqczze, R3): this is the strictly more general version of what 12208 does — Re Windows clone (oech3, R10): confirmed — Windows uses Re backport crate (oech3, R9): I searched and didn't find one. Re no-FD platforms (R5/R6/R7/R8): I read the resolution that UEFI is Side note — the comment in I tested locally on macOS only. If the Windows symmetry is in question, I have a Windows machine and can verify the Leaving in draft until the layer question is settled. |
Summary
ddfails in restricted containers wherefcntlreturnsENOSYSbecausetry_clone_to_owned()internally callsfcntl. This was introduced in PR #10235.OwnedFileDescriptorOrHandle::from(io::stdout())with aManuallyDrop<File>wrapper that borrows the fd without cloningfcntlsyscallfrom_raw_fd) and Windows (from_raw_handle)Test plan
echo "test" | dd bs=1)Fixes #12157