Short description: Fedora-specific workaround for kernel pty bug.
Author(s): Fedora glibc team <glibc@lists.fedoraproject.org>
Origin: PATCH
Upstream status: not-submitted

This is a Fedora-specific workaround for a kernel bug where calling
ioctl on a pty will silently ignore the invalid c_cflag. The
workaround is to use TCGETS to verify the setting matches. This is
not upstream and needs to either be removed or submitted upstream
after analysis.

Index: b/sysdeps/unix/sysv/linux/tcsetattr.c
===================================================================
--- a/sysdeps/unix/sysv/linux/tcsetattr.c
+++ b/sysdeps/unix/sysv/linux/tcsetattr.c
@@ -45,6 +45,7 @@ __tcsetattr (int fd, int optional_action
 {
   struct __kernel_termios k_termios;
   unsigned long int cmd;
+  int retval;
 
   switch (optional_actions)
     {
@@ -75,7 +76,36 @@ __tcsetattr (int fd, int optional_action
   memcpy (&k_termios.c_cc[0], &termios_p->c_cc[0],
 	  __KERNEL_NCCS * sizeof (cc_t));
 
-  return INLINE_SYSCALL (ioctl, 3, fd, cmd, &k_termios);
+  retval = INLINE_SYSCALL (ioctl, 3, fd, cmd, &k_termios);
+
+  if (retval == 0 && cmd == TCSETS)
+    {
+      /* The Linux kernel has a bug which silently ignore the invalid
+        c_cflag on pty. We have to check it here. */
+      int save = errno;
+      retval = INLINE_SYSCALL (ioctl, 3, fd, TCGETS, &k_termios);
+      if (retval)
+       {
+         /* We cannot verify if the setting is ok. We don't return
+            an error (?). */
+         __set_errno (save);
+         retval = 0;
+       }
+      else if ((termios_p->c_cflag & (PARENB | CREAD))
+              != (k_termios.c_cflag & (PARENB | CREAD))
+              || ((termios_p->c_cflag & CSIZE)
+                  && ((termios_p->c_cflag & CSIZE)
+                      != (k_termios.c_cflag & CSIZE))))
+       {
+         /* It looks like the Linux kernel silently changed the
+            PARENB/CREAD/CSIZE bits in c_cflag. Report it as an
+            error. */
+         __set_errno (EINVAL);
+         retval = -1;
+       }
+    }
+
+  return retval;
 }
 weak_alias (__tcsetattr, tcsetattr)
 libc_hidden_def (tcsetattr)
