Skip to content

Commit c6c8514

Browse files
committed
pty: Make forkpty() unsafe
After the child returns from a fork() of a multi-threaded process, it is undefined behaviour to call non-async-signal-safe functions according to POSIX. Since forkpty() is implemented in terms of fork(), those restrictions should apply to it too.
1 parent d0bba22 commit c6c8514

File tree

3 files changed

+25
-12
lines changed

3 files changed

+25
-12
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
55

66
## [Unreleased] - ReleaseDate
77
### Added
8+
89
### Changed
10+
- Made `forkpty` unsafe, like `fork`
11+
(#[1390](https://github.com/nix-rust/nix/pull/1390))
12+
913
### Fixed
1014
### Removed
1115

src/pty.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,19 @@ pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>
302302
/// If `winsize` is not `None`, the window size of the slave will be set to
303303
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
304304
/// terminal settings of the slave will be set to the values in `termios`.
305-
pub fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
305+
///
306+
/// # Safety
307+
///
308+
/// In a multithreaded program, only [async-signal-safe] functions like `pause`
309+
/// and `_exit` may be called by the child (the parent isn't restricted). Note
310+
/// that memory allocation may **not** be async-signal-safe and thus must be
311+
/// prevented.
312+
///
313+
/// Those functions are only a small subset of your operating system's API, so
314+
/// special care must be taken to only invoke code you can control and audit.
315+
///
316+
/// [async-signal-safe]: http://man7.org/linux/man-pages/man7/signal-safety.7.html
317+
pub unsafe fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
306318
winsize: T,
307319
termios: U,
308320
) -> Result<ForkptyResult> {
@@ -323,20 +335,15 @@ pub fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>
323335
.map(|ws| ws as *const Winsize as *mut _)
324336
.unwrap_or(ptr::null_mut());
325337

326-
let res = unsafe {
327-
libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win)
328-
};
338+
let res = libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win);
329339

330340
let fork_result = Errno::result(res).map(|res| match res {
331341
0 => ForkResult::Child,
332342
res => ForkResult::Parent { child: Pid::from_raw(res) },
333343
})?;
334344

335-
unsafe {
336-
Ok(ForkptyResult {
337-
master: master.assume_init(),
338-
fork_result,
339-
})
340-
}
345+
Ok(ForkptyResult {
346+
master: master.assume_init(),
347+
fork_result,
348+
})
341349
}
342-

test/test_pty.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,9 @@ fn test_forkpty() {
255255

256256
let string = "naninani\n";
257257
let echoed_string = "naninani\r\n";
258-
let pty = forkpty(None, None).unwrap();
258+
let pty = unsafe {
259+
forkpty(None, None).unwrap()
260+
};
259261
match pty.fork_result {
260262
Child => {
261263
write(STDOUT_FILENO, string.as_bytes()).unwrap();

0 commit comments

Comments
 (0)