From 40a751b0044114488e841f0223e630596c527c53 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 18 Nov 2025 10:21:18 -0800 Subject: [PATCH] linux/termios: test the kernel-side termios canonicalization MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Verify that the kernel side of the termios interface gets the various speed fields set according to our current canonicalization policy. [ v2.1: fix formatting - Adhemerval Netto ] [ v4: fix typo in patch description - Dan Horák ] Signed-off-by: H. Peter Anvin (Intel) Reviewed-by: Adhemerval Zanella (v2.1) Reviewed-by: H.J. Lu --- sysdeps/unix/sysv/linux/tst-termios-linux.c | 61 ++++++++++++++++++++- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/sysdeps/unix/sysv/linux/tst-termios-linux.c b/sysdeps/unix/sysv/linux/tst-termios-linux.c index e4b0c8bcd6..80ba9e8853 100644 --- a/sysdeps/unix/sysv/linux/tst-termios-linux.c +++ b/sysdeps/unix/sysv/linux/tst-termios-linux.c @@ -38,6 +38,8 @@ #include #include +#include /* Definitions for the raw ioctl interface */ + /* Evaluate an expression and make sure errno did not get set; return the value of the expression */ #define CHECKERR(expr) \ @@ -226,13 +228,66 @@ static void check_speeds_cf (const struct termios *tio_p, CHECKERR (cfgetibaud (tio_p)), 'i'); } -/* Use this after tc[gs]etattr () */ +/* Access the raw kernel interface and verify that the result is + canonicalized properly; this should be run after tcsetattr (). */ +static void +check_speeds_kernel (int fd, speed_t ospeed, speed_t ispeed) +{ + struct termios2 k_termios; + tcflag_t expect_cbaud = speed_to_cbaud (ospeed); + tcflag_t expect_cibaud; + + if (!ispeed) + ispeed = ospeed; + + /* If ospeed == ispeed, tcsetattr() should set the kernel CIBAUD to 0, + for compatibility with programs that use the direct ioctl interface + but fail to account for CIBAUD. c_ispeed should still be correct. */ + if (ospeed == ispeed) + expect_cibaud = 0; + else + expect_cibaud = speed_to_cbaud (ispeed); + + memset (&k_termios, 0xed, sizeof k_termios); /* Fill with nonsense */ + CHECKZERO (ioctl(fd, TCGETS2, &k_termios)); + + tcflag_t k_cbaud = k_termios.c_cflag & CBAUD; + tcflag_t k_cibaud = (k_termios.c_cflag >> IBSHIFT) & CBAUD; + + if (k_termios.c_ospeed != ospeed) + FAIL ("opeed %u ispeed %u: kernel c_ospeed = %u, expected %u", + ospeed, ispeed, + k_termios.c_ospeed, ospeed); + + if (k_cbaud != expect_cbaud) + FAIL ("ospeed %u ispeed %u: kernel CBAUD = %s (%06o), expected %s (%06o)", + ospeed, ispeed, + cbaud_name (k_cbaud), k_cbaud, + cbaud_name (expect_cbaud), expect_cbaud); + + if (k_termios.c_ispeed != ispeed) + FAIL ("ospeed %u ispeed %u: kernel c_ispeed == %u, expected %u", + ospeed, ispeed, + k_termios.c_ispeed, ispeed); + + if (k_cibaud != expect_cibaud) + FAIL ("ospeed %u ispeed %u: kernel CIBAUD = %s (%06o), expected %s (%06o)", + ospeed, ispeed, + cbaud_name (k_cibaud), k_cibaud, + cbaud_name (expect_cibaud), expect_cibaud); +} + +/* Use this after tcsetattr () */ static void check_speeds_tc (int fd, speed_t ospeed, speed_t ispeed) { struct termios tio; + if (!ispeed) + ispeed = ospeed; + CHECKZERO (tcgetattr (fd, &tio)); - check_speeds_cf (&tio, ospeed, ispeed ? ispeed : ospeed); + check_speeds_cf (&tio, ospeed, ispeed); + check_speeds_kernel (fd, ospeed, ispeed); } /* For search and replace convenience */ @@ -250,7 +305,7 @@ set_speeds (int fd, speed_t ospeed, speed_t ispeed) CHECKZERO (cfsetispeed (&tio, ispeed)); check_speeds_cf (&tio, ospeed, ispeed); CHECKZERO (tcsetattr (fd, TCSANOW, &tio)); - check_speeds_tc (fd, ospeed, ispeed ? ispeed : ospeed); + check_speeds_tc (fd, ospeed, ispeed); } /* Actual tests */ -- 2.47.3