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

tty: serial: kgdboc: fix mutex locking order for configure_kgdboc()

Several mutexes are taken while setting up console serial ports. In
particular, the tty_port->mutex and @console_mutex are taken:

serial_pnp_probe
serial8250_register_8250_port
uart_add_one_port (locks tty_port->mutex)
uart_configure_port
register_console (locks @console_mutex)

In order to synchronize kgdb's tty_find_polling_driver() with
register_console(), commit 6193bc90849a ("tty: serial: kgdboc:
synchronize tty_find_polling_driver() and register_console()") takes
the @console_mutex. However, this leads to the following call chain
(with locking):

platform_probe
kgdboc_probe
configure_kgdboc (locks @console_mutex)
tty_find_polling_driver
uart_poll_init (locks tty_port->mutex)
uart_set_options

This is clearly deadlock potential due to the reverse lock ordering.

Since uart_set_options() requires holding @console_mutex in order to
serialize early initialization of the serial-console lock, take the
@console_mutex in uart_poll_init() instead of configure_kgdboc().

Since configure_kgdboc() was using @console_mutex for safe traversal
of the console list, change it to use the SRCU iterator instead.

Add comments to uart_set_options() kerneldoc mentioning that it
requires holding @console_mutex (aka the console_list_lock).

Fixes: 6193bc90849a ("tty: serial: kgdboc: synchronize tty_find_polling_driver() and register_console()")
Signed-off-by: John Ogness <john.ogness@linutronix.de>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Reviewed-by: Petr Mladek <pmladek@suse.com>
[pmladek@suse.com: Export console_srcu_read_lock_is_held() to fix build kgdboc as a module.]
Signed-off-by: Petr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20230112161213.1434854-1-john.ogness@linutronix.de

authored by

John Ogness and committed by
Petr Mladek
3ef5abd9 5074ffbe

+11 -15
+5 -15
drivers/tty/serial/kgdboc.c
··· 171 171 int err = -ENODEV; 172 172 char *cptr = config; 173 173 struct console *cons; 174 + int cookie; 174 175 175 176 if (!strlen(config) || isspace(config[0])) { 176 177 err = 0; ··· 190 189 if (kgdboc_register_kbd(&cptr)) 191 190 goto do_register; 192 191 193 - /* 194 - * tty_find_polling_driver() can call uart_set_options() 195 - * (via poll_init) to configure the uart. Take the console_list_lock 196 - * in order to synchronize against register_console(), which can also 197 - * configure the uart via uart_set_options(). This also allows safe 198 - * traversal of the console list. 199 - */ 200 - console_list_lock(); 201 - 202 192 p = tty_find_polling_driver(cptr, &tty_line); 203 - if (!p) { 204 - console_list_unlock(); 193 + if (!p) 205 194 goto noconfig; 206 - } 207 195 208 196 /* 209 197 * Take console_lock to serialize device() callback with ··· 201 211 */ 202 212 console_lock(); 203 213 204 - for_each_console(cons) { 214 + cookie = console_srcu_read_lock(); 215 + for_each_console_srcu(cons) { 205 216 int idx; 206 217 if (cons->device && cons->device(cons, &idx) == p && 207 218 idx == tty_line) { ··· 210 219 break; 211 220 } 212 221 } 222 + console_srcu_read_unlock(cookie); 213 223 214 224 console_unlock(); 215 - 216 - console_list_unlock(); 217 225 218 226 kgdb_tty_driver = p; 219 227 kgdb_tty_line = tty_line;
+5
drivers/tty/serial/serial_core.c
··· 2212 2212 * @parity: parity character - 'n' (none), 'o' (odd), 'e' (even) 2213 2213 * @bits: number of data bits 2214 2214 * @flow: flow control character - 'r' (rts) 2215 + * 2216 + * Locking: Caller must hold console_list_lock in order to serialize 2217 + * early initialization of the serial-console lock. 2215 2218 */ 2216 2219 int 2217 2220 uart_set_options(struct uart_port *port, struct console *co, ··· 2622 2619 2623 2620 if (!ret && options) { 2624 2621 uart_parse_options(options, &baud, &parity, &bits, &flow); 2622 + console_list_lock(); 2625 2623 ret = uart_set_options(port, NULL, baud, parity, bits, flow); 2624 + console_list_unlock(); 2626 2625 } 2627 2626 out: 2628 2627 mutex_unlock(&tport->mutex);
+1
kernel/printk/printk.c
··· 123 123 { 124 124 return srcu_read_lock_held(&console_srcu); 125 125 } 126 + EXPORT_SYMBOL(console_srcu_read_lock_is_held); 126 127 #endif 127 128 128 129 enum devkmsg_log_bits {