Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

serial: Fix locking for uart driver set_termios() method

The low-level uart driver may modify termios settings to override
settings that are not compatible with the uart, such as CRTSCTS.
Thus, callers of the low-level uart driver's set_termios() method must
hold termios_rwsem write lock to prevent concurrent access to termios,
in case such override occurs.

The termios_rwsem lock requirement does not extend to console setup
(ie., uart_set_options), as console setup cannot race with tty
operations. Nor does this lock requirement extend to functions which
cannot be concurrent with tty ioctls (ie., uart_port_startup() and
uart_resume_port()).

Further, always claim the port mutex to protect hardware
re-reprogramming in the set_termios() uart driver method. Note this
is unnecessary for console initialization in uart_set_options()
which cannot be concurrent with other uart operations.

Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Peter Hurley and committed by
Greg Kroah-Hartman
7c8ab967 2e758910

+11 -3
+4 -2
Documentation/serial/driver
··· 59 59 access to the info->tmpbuf bouncebuffer used for port writes. 60 60 61 61 The port_sem semaphore is used to protect against ports being added/ 62 - removed or reconfigured at inappropriate times. 62 + removed or reconfigured at inappropriate times. Since v2.6.27, this 63 + semaphore has been the 'mutex' member of the tty_port struct, and 64 + commonly referred to as the port mutex (or port->mutex). 63 65 64 66 65 67 uart_ops ··· 250 248 Other flags may be used (eg, xon/xoff characters) if your 251 249 hardware supports hardware "soft" flow control. 252 250 253 - Locking: none. 251 + Locking: caller holds port->mutex 254 252 Interrupts: caller dependent. 255 253 This call must not sleep 256 254
+7 -1
drivers/tty/serial/serial_core.c
··· 436 436 437 437 EXPORT_SYMBOL(uart_get_divisor); 438 438 439 - /* FIXME: Consistent locking policy */ 439 + /* Caller holds port mutex */ 440 440 static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, 441 441 struct ktermios *old_termios) 442 442 { ··· 1173 1173 break; 1174 1174 1175 1175 case TIOCSSERIAL: 1176 + down_write(&tty->termios_rwsem); 1176 1177 ret = uart_set_info_user(tty, state, uarg); 1178 + up_write(&tty->termios_rwsem); 1177 1179 break; 1178 1180 1179 1181 case TIOCSERCONFIG: 1182 + down_write(&tty->termios_rwsem); 1180 1183 ret = uart_do_autoconfig(tty, state); 1184 + up_write(&tty->termios_rwsem); 1181 1185 break; 1182 1186 1183 1187 case TIOCSERGWILD: /* obsolete */ ··· 1282 1278 return; 1283 1279 } 1284 1280 1281 + mutex_lock(&state->port.mutex); 1285 1282 uart_change_speed(tty, state, old_termios); 1283 + mutex_unlock(&state->port.mutex); 1286 1284 /* reload cflag from termios; port driver may have overriden flags */ 1287 1285 cflag = tty->termios.c_cflag; 1288 1286