Commit 5662b1f
authored
Fix soundness issues in
* Fix `dup2_stdout` et al to handle incorrect `AsFd` implementations
Without this change, an incorrect `AsFd` implementation could result to
the stdio file descriptors being closed in safe code, violating I/O safety.
The old `dup2_std*` implementations were thus unsound.
Consider this old version of the code:
```rust
if fd.as_fd().as_raw_fd() != c::STDOUT_FILENO {
// SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't
// dropped.
let mut target = unsafe { take_stdout() };
backend::io::syscalls::dup2(fd.as_fd(), &mut target)?;
forget(target);
}
```
The safety of the `take_stdout()` call relies on two things:
1. In the call to `syscalls::dup2`, `fd` and `target` don't alias (#1069).
2. `forget(target)` is called to not drop `target`.
If one of these conditions is violated, then stdout of the process can get
closed. Violating both of them is possible in entirely safe code, because
`AsFd` is not an unsafe trait. Consider a user defined type that (safely)
implements `AsFd` and
1. Returns `stdin()` on the first `as_fd()` call, but `stdout()` on the
second time `as_fd()` is called. This would bypass the aliasing check,
because the `dup2_stdout` implementation calls `as_fd()` twice, and the
first result is used in the check and second result in the actual `dup2`
call.
2. Or returns `stdin()` on the first `as_fd()` call, but panics on the
second call. The panic would happen on the same line as the `dup2` call,
so `forget(target)` after it isn't called, which results in `target`
getting dropped which closes stdout. The panic could be catched with
`catch_unwind` that would let other code observe the missing stdout.
This violates exception safety.
This patch fixes both of these issues by calling `as_fd()` only once and
storing the `BorrowedFd` into a variable. This ensures that the file
descriptor in the alias check and the `dup2` call are the same, and a panic
in `as_fd()` would happen before the unsafe `take_stdout` call, so there is
no concern of `target` getting dropped because of a panic.
* Fix `dup2_stdout` et al to not close the stdio file descriptor on error
The `dup2_std*` functions use the `?` operator to return errors from the
`dup2` system call. However, this means that the code after it
(`forget(target)`) is not run on errors, so `target` gets dropped closing
the file descriptor which violates I/O safety.
Fix that by using `ManuallyDrop` instead of `forget`.dup2_stdout et al (#1080)1 parent 81f500a commit 5662b1f
1 file changed
+19
-22
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
| 20 | + | |
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| |||
477 | 477 | | |
478 | 478 | | |
479 | 479 | | |
480 | | - | |
481 | 480 | | |
482 | 481 | | |
483 | | - | |
484 | | - | |
485 | | - | |
486 | | - | |
487 | | - | |
488 | | - | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
489 | 488 | | |
490 | 489 | | |
491 | 490 | | |
492 | 491 | | |
493 | 492 | | |
494 | 493 | | |
495 | | - | |
496 | 494 | | |
497 | 495 | | |
498 | | - | |
499 | | - | |
500 | | - | |
501 | | - | |
502 | | - | |
503 | | - | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
504 | 502 | | |
505 | 503 | | |
506 | 504 | | |
507 | 505 | | |
508 | 506 | | |
509 | 507 | | |
510 | | - | |
511 | 508 | | |
512 | 509 | | |
513 | | - | |
514 | | - | |
515 | | - | |
516 | | - | |
517 | | - | |
518 | | - | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
519 | 516 | | |
520 | 517 | | |
521 | 518 | | |
0 commit comments