| From c2730a40997cf5fdb992a6b0b05d1a472cd703a8 Mon Sep 17 00:00:00 2001 |
| From: Anthony Mallet <anthony.mallet@laas.fr> |
| Date: Thu, 12 Mar 2020 14:31:01 +0100 |
| Subject: [PATCH] USB: cdc-acm: fix rounding error in TIOCSSERIAL |
| |
| commit b401f8c4f492cbf74f3f59c9141e5be3071071bb upstream. |
| |
| By default, tty_port_init() initializes those parameters to a multiple |
| of HZ. For instance in line 69 of tty_port.c: |
| port->close_delay = (50 * HZ) / 100; |
| https://github.com/torvalds/linux/blob/master/drivers/tty/tty_port.c#L69 |
| |
| With e.g. CONFIG_HZ = 250 (as this is the case for Ubuntu 18.04 |
| linux-image-4.15.0-37-generic), the default setting for close_delay is |
| thus 125. |
| |
| When ioctl(fd, TIOCGSERIAL, &s) is executed, the setting returned in |
| user space is '12' (125/10). When ioctl(fd, TIOCSSERIAL, &s) is then |
| executed with the same setting '12', the value is interpreted as '120' |
| which is different from the current setting and a EPERM error may be |
| raised by set_serial_info() if !CAP_SYS_ADMIN. |
| https://github.com/torvalds/linux/blob/master/drivers/usb/class/cdc-acm.c#L919 |
| |
| Fixes: ba2d8ce9db0a6 ("cdc-acm: implement TIOCSSERIAL to avoid blocking close(2)") |
| Signed-off-by: Anthony Mallet <anthony.mallet@laas.fr> |
| Cc: stable <stable@vger.kernel.org> |
| Link: https://lore.kernel.org/r/20200312133101.7096-2-anthony.mallet@laas.fr |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c |
| index da619176deca..47f09a6ce7bd 100644 |
| --- a/drivers/usb/class/cdc-acm.c |
| +++ b/drivers/usb/class/cdc-acm.c |
| @@ -907,6 +907,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) |
| { |
| struct acm *acm = tty->driver_data; |
| unsigned int closing_wait, close_delay; |
| + unsigned int old_closing_wait, old_close_delay; |
| int retval = 0; |
| |
| close_delay = msecs_to_jiffies(ss->close_delay * 10); |
| @@ -914,18 +915,24 @@ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) |
| ASYNC_CLOSING_WAIT_NONE : |
| msecs_to_jiffies(ss->closing_wait * 10); |
| |
| + /* we must redo the rounding here, so that the values match */ |
| + old_close_delay = jiffies_to_msecs(acm->port.close_delay) / 10; |
| + old_closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? |
| + ASYNC_CLOSING_WAIT_NONE : |
| + jiffies_to_msecs(acm->port.closing_wait) / 10; |
| + |
| mutex_lock(&acm->port.mutex); |
| |
| - if (!capable(CAP_SYS_ADMIN)) { |
| - if ((close_delay != acm->port.close_delay) || |
| - (closing_wait != acm->port.closing_wait)) |
| + if ((ss->close_delay != old_close_delay) || |
| + (ss->closing_wait != old_closing_wait)) { |
| + if (!capable(CAP_SYS_ADMIN)) |
| retval = -EPERM; |
| - else |
| - retval = -EOPNOTSUPP; |
| - } else { |
| - acm->port.close_delay = close_delay; |
| - acm->port.closing_wait = closing_wait; |
| - } |
| + else { |
| + acm->port.close_delay = close_delay; |
| + acm->port.closing_wait = closing_wait; |
| + } |
| + } else |
| + retval = -EOPNOTSUPP; |
| |
| mutex_unlock(&acm->port.mutex); |
| return retval; |
| -- |
| 2.7.4 |
| |