1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
|
# 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 <licquia@progeny.com>
# DP: Upstream status: [In CVS | Debian-Specific | Pending | Not submitted ]
# DP: Status Details:
# DP: Date: 2003-10-29
---
sysdeps/unix/sysv/linux/tcsetattr.c | 55 +++++++++++++++++++++++++++++++++++-
1 file changed, 54 insertions(+), 1 deletion(-)
--- a/sysdeps/unix/sysv/linux/tcsetattr.c
+++ b/sysdeps/unix/sysv/linux/tcsetattr.c
@@ -44,7 +44,12 @@
__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 @@
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)
|