Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/sys/termios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,18 +753,26 @@ cfg_if! {
/// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
///
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
///
/// On glibc 2.42+, returns `BaudRate::B0` if the actual baud rate cannot be determined.
pub fn cfgetispeed(termios: &Termios) -> BaudRate {
let inner_termios = termios.get_libc_termios();
unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
unsafe { libc::cfgetispeed(&*inner_termios) }
.try_into()
.unwrap_or(BaudRate::B0)
}

/// Get output baud rate (see
/// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
///
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
///
/// On glibc 2.42+, returns `BaudRate::B0` if the actual baud rate cannot be determined.
pub fn cfgetospeed(termios: &Termios) -> BaudRate {
let inner_termios = termios.get_libc_termios();
unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
unsafe { libc::cfgetospeed(&*inner_termios) }
.try_into()
.unwrap_or(BaudRate::B0)
}

/// Set input baud rate (see
Expand Down
71 changes: 71 additions & 0 deletions test/sys/test_termios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,74 @@ fn test_local_flags() {
let read = read(&pty.master, &mut buf).unwrap_err();
assert_eq!(read, Errno::EAGAIN);
}

// Test for glibc 2.42 compatibility - cfgetospeed should never panic
// Reproduces issue from nix-rust/nix#2672
#[test]
fn test_cfgetospeed_never_panics() {
use nix::sys::termios::{cfgetospeed, Termios};

// Test with zeroed termios (may be invalid on glibc 2.42)
let termios: Termios = unsafe { std::mem::zeroed() };

// This should not panic, even on glibc 2.42
// Before fix: panicked at src/sys/termios.rs:767 with EINVAL
let _speed = cfgetospeed(&termios);

// If we get here, no panic occurred - test passes
}

// Test for glibc 2.42 compatibility - cfgetispeed should never panic
#[test]
fn test_cfgetispeed_never_panics() {
use nix::sys::termios::{cfgetispeed, Termios};

// Test with zeroed termios (may be invalid on glibc 2.42)
let termios: Termios = unsafe { std::mem::zeroed() };

// This should not panic, even on glibc 2.42
let _speed = cfgetispeed(&termios);

// If we get here, no panic occurred - test passes
}

// Test exact reproducer from issue #2672
#[test]
fn test_issue_2672_cfgetospeed_with_pty() {
use nix::sys::termios::cfgetospeed;

// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();

let pty = openpty(None, None).expect("openpty failed");
let termios = tcgetattr(&pty.slave).expect("tcgetattr failed");

// This exact call panicked on glibc 2.42 before the fix
let speed = cfgetospeed(&termios);

// Should return some valid BaudRate (or B0 on error)
// The key is: NO PANIC
let _ = speed;
}

// Test baud rate roundtrip with actual pty
#[test]
fn test_baudrate_roundtrip_with_pty() {
use nix::sys::termios::{cfgetospeed, cfsetspeed};

// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();

let pty = openpty(None, None).expect("openpty failed");
let mut termios = tcgetattr(&pty.slave).expect("tcgetattr failed");

// Set a known baud rate
cfsetspeed(&mut termios, BaudRate::B9600).expect("cfsetspeed failed");

// Get it back - should not panic
let speed = cfgetospeed(&termios);

// On most systems this equals B9600, but on glibc 2.42 might be B0 if conversion fails
// Either way, NO PANIC is the requirement
let _ = speed;
}
Loading