Skip to content

Commit a91348b

Browse files
committed
PtyMaster::drop should panic on EBADF
Fixes #659
1 parent da49f2a commit a91348b

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

src/pty.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,17 @@ impl IntoRawFd for PtyMaster {
4545

4646
impl Drop for PtyMaster {
4747
fn drop(&mut self) {
48-
// Errors when closing are ignored because we don't actually know if the file descriptor
49-
// was closed. If we retried, it's possible that descriptor was reallocated in the mean
50-
// time and the wrong file descriptor could be closed.
51-
let _ = ::unistd::close(self.0);
48+
// On drop, we ignore errors like EINTR and EIO because there's no clear
49+
// way to handle them, we can't return anything, and (on FreeBSD at
50+
// least) the file descriptor is deallocated in these cases. However,
51+
// we must panic on EBADF, because it is always an error to close an
52+
// invalid file descriptor. That frequently indicates a double-close
53+
// condition, which can cause confusing errors for future I/O
54+
// operations.
55+
let e = ::unistd::close(self.0);
56+
if e == Err(Error::Sys(Errno::EBADF)) {
57+
panic!("Closing an invalid file descriptor!");
58+
};
5259
}
5360
}
5461

test/test_pty.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,38 @@
1+
use std::io::Write;
12
use std::path::Path;
23
use std::os::unix::prelude::*;
4+
use tempfile::tempfile;
35

46
use nix::fcntl::{O_RDWR, open};
57
use nix::pty::*;
68
use nix::sys::stat;
79
use nix::sys::termios::*;
810
use nix::unistd::{write, close};
911

12+
/// Regression test for Issue #659
13+
/// PtyMaster should panic rather than double close the file descriptor
14+
#[test]
15+
#[should_panic(expected = "Closing an invalid file descriptor!")]
16+
fn test_double_close() {
17+
let m = posix_openpt(O_RDWR).unwrap();
18+
close(m.as_raw_fd()).unwrap();
19+
drop(m); // should panic here
20+
}
21+
22+
/// Regression test for Issue #659
23+
/// This is the correct way to explicitly close a PtyMaster
24+
#[test]
25+
fn test_explicit_close() {
26+
let mut f = {
27+
let m = posix_openpt(O_RDWR).unwrap();
28+
close(m.into_raw_fd()).unwrap();
29+
tempfile().unwrap()
30+
};
31+
// This should work. But if there's been a double close, then it will
32+
// return EBADF
33+
f.write(b"whatever").unwrap();
34+
}
35+
1036
/// Test equivalence of `ptsname` and `ptsname_r`
1137
#[test]
1238
#[cfg(any(target_os = "android", target_os = "linux"))]

0 commit comments

Comments
 (0)