From: GNU Libc Maintainers Date: Tue, 5 Jan 2021 05:47:42 +0000 (+0000) Subject: local-tcsetaddr X-Git-Tag: archive/raspbian/2.31-9+rpi1^2~17 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=48bbd714128731e846bff6b233a5c8f7c7041b33;p=glibc.git local-tcsetaddr # All lines beginning with `# DP:' are a description of the patch. # DP: Description: tcsetattr sanity check on PARENB/CREAD/CSIZE for ptys # DP: Related bugs: 218131 # DP: Author: Jeff Licquia # DP: Upstream status: [In CVS | Debian-Specific | Pending | Not submitted ] # DP: Status Details: # DP: Date: 2003-10-29 # All lines beginning with `# DP:' are a description of the patch. # DP: Description: tcsetattr sanity check on PARENB/CREAD/CSIZE for ptys # DP: Related bugs: 218131 # DP: Author: Jeff Licquia # DP: Upstream status: [In CVS | Debian-Specific | Pending | Not submitted ] # DP: Status Details: # DP: Date: 2003-10-29 Gbp-Pq: Topic any Gbp-Pq: Name local-tcsetaddr.diff --- diff --git a/sysdeps/unix/sysv/linux/tcsetattr.c b/sysdeps/unix/sysv/linux/tcsetattr.c index 50b2b0af8..4a83c1c5d 100644 --- a/sysdeps/unix/sysv/linux/tcsetattr.c +++ b/sysdeps/unix/sysv/linux/tcsetattr.c @@ -44,7 +44,12 @@ int __tcsetattr (int fd, int optional_actions, const struct termios *termios_p) { struct __kernel_termios k_termios; + struct __kernel_termios k_termios_old; unsigned long int cmd; + int retval, old_retval; + + /* Preserve the previous termios state if we can. */ + old_retval = INLINE_SYSCALL (ioctl, 3, fd, TCGETS, &k_termios_old); switch (optional_actions) { @@ -75,7 +80,55 @@ __tcsetattr (int fd, int optional_actions, const struct termios *termios_p) 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); + + /* The Linux kernel silently ignores the invalid c_cflag on pty. + We have to check it here, and return an error. But if some other + setting was successfully changed, POSIX requires us to report + success. */ + if ((retval == 0) && (old_retval == 0)) + { + 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 ((k_termios_old.c_oflag != k_termios.c_oflag) || + (k_termios_old.c_lflag != k_termios.c_lflag) || + (k_termios_old.c_line != k_termios.c_line) || + ((k_termios_old.c_iflag | IBAUD0) != (k_termios.c_iflag | IBAUD0))) + { + /* Some other setting was successfully changed, which + means we should not return an error. */ + __set_errno (save); + retval = 0; + } + else if ((k_termios_old.c_cflag | (PARENB & CREAD & CSIZE)) != + (k_termios.c_cflag | (PARENB & CREAD & CSIZE))) + { + /* Some other c_cflag setting was successfully changed, which + means we should not 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)