[PATCH 1/1] linux/termios: clear c_cflag & CIBAUD for non-split speed

H. Peter Anvin hpa@zytor.com
Mon Sep 8 15:34:17 GMT 2025


After getting more experience with the various broken direct-to-ioctl
termios2 hacks using Fedora 43 beta, I have found a fair number of
cases where the software would fail to set, or clear CIBAUD for
non-split-speed operation.

Therefore, I think it will help improve compatibility to clear the
CIBAUD flags for non-split-speed operation, rather than having it
equal CIBAUD.

This change is in tcsetattr() rather than
___termios2_canonicalize_speeds(), as it should not be run for
tcgetattr(); that would break split speed support for the legacy
interface versions of cfgetispeed() and cfsetispeed().

When writing the code that went into glibc 2.42 I had considered this
issue, and had to make an educated guess which way would be more
likely to break fewer things. Unfortunately, it appears I guessed
wrong.

Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
---
 sysdeps/unix/sysv/linux/tcsetattr.c | 39 ++++++++++++++++-------------
 1 file changed, 22 insertions(+), 17 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/tcsetattr.c b/sysdeps/unix/sysv/linux/tcsetattr.c
index 4f07a03e6657..d61c30d5ae9a 100644
--- a/sysdeps/unix/sysv/linux/tcsetattr.c
+++ b/sysdeps/unix/sysv/linux/tcsetattr.c
@@ -41,16 +41,6 @@ __tcsetattr (int fd, int optional_actions, const struct termios *termios_p)
 
   copy_c_cc (k_termios.c_cc, _TERMIOS2_NCCS, termios_p->c_cc, NCCS);
 
-  /*
-   * Choose the proper ioctl number to invoke.
-   *
-   * Alpha got TCSETS2 late (Linux 4.20), but has the same structure
-   * format, and it only needs TCSETS2 if either it needs to use
-   * __BOTHER or split speed.  All other architectures have TCSETS2 as
-   * far back as the current glibc supports.  Calling TCSETS with
-   * __BOTHER causes unpredictable results on old Alpha kernels and
-   * could even crash them.
-   */
   static_assert_equal(TCSADRAIN, TCSANOW + 1);
   static_assert_equal(TCSAFLUSH, TCSANOW + 2);
   static_assert_equal(TCSETSW2,  TCSETS2 + 1);
@@ -62,17 +52,32 @@ __tcsetattr (int fd, int optional_actions, const struct termios *termios_p)
   if (cmd > 2)
     return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
 
+  /*
+   * For compatibility with broken workaround hacks for the lack
+   * of arbitrary speed support in previous versions of glibc,
+   * clear CIBAUD if only one speed is used.
+   *
+   * This is also necessary for the Alpha compatibility hack below.
+   */
+  if (k_termios.c_ospeed == k_termios.c_ispeed)
+    k_termios.c_cflag &= ~CIBAUD;
+
+  /*
+   * Choose the proper ioctl number to invoke.
+   *
+   * Alpha got TCSETS2 late (Linux 4.20), but has the same structure
+   * format, and it only needs TCSETS2 if either it needs to use
+   * __BOTHER or split speed.  All other architectures have TCSETS2 as
+   * far back as the current glibc supports.  Calling TCSETS with
+   * __BOTHER causes unpredictable results on old Alpha kernels and
+   * could even crash them.
+   */
   if (__ASSUME_TERMIOS2 ||
       k_termios.c_ospeed != k_termios.c_ispeed ||
       cbaud (k_termios.c_cflag) == __BOTHER)
-    {
-      cmd += TCSETS2;
-    }
+    cmd += TCSETS2;
   else
-    {
-      cmd += TCSETS;
-      k_termios.c_cflag &= ~CIBAUD;
-    }
+    cmd += TCSETS;
 
   return INLINE_SYSCALL_CALL (ioctl, fd, cmd, &k_termios);
 }
-- 
2.51.0



More information about the Libc-alpha mailing list