diff --git a/src/sys/termios.rs b/src/sys/termios.rs index bff981fad5..288860336f 100644 --- a/src/sys/termios.rs +++ b/src/sys/termios.rs @@ -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 diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs index 98de86bc74..2a9754bb13 100644 --- a/test/sys/test_termios.rs +++ b/test/sys/test_termios.rs @@ -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; +}