diff options
author | Gaurav Kohli <gkohli@codeaurora.org> | 2018-01-23 02:46:34 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-01-23 02:57:37 -0500 |
commit | b027e2298bd588d6fa36ed2eda97447fb3eac078 (patch) | |
tree | 3b90cee88ac74c2828b90b67d67cf7adf6e9e310 | |
parent | 09df0b3464e528c6a4ca2c48d9ff6d2fd7cbd775 (diff) |
tty: fix data race between tty_init_dev and flush of buf
There can be a race, if receive_buf call comes before
tty initialization completes in n_tty_open and tty->disc_data
may be NULL.
CPU0 CPU1
---- ----
000|n_tty_receive_buf_common() n_tty_open()
-001|n_tty_receive_buf2() tty_ldisc_open.isra.3()
-002|tty_ldisc_receive_buf(inline) tty_ldisc_setup()
Using ldisc semaphore lock in tty_init_dev till disc_data
initializes completely.
Signed-off-by: Gaurav Kohli <gkohli@codeaurora.org>
Reviewed-by: Alan Cox <alan@linux.intel.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/tty/tty_io.c | 8 | ||||
-rw-r--r-- | drivers/tty/tty_ldisc.c | 4 | ||||
-rw-r--r-- | include/linux/tty.h | 2 |
3 files changed, 11 insertions, 3 deletions
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index a6ca634411e1..688bd25aa6b0 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c | |||
@@ -1323,6 +1323,9 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) | |||
1323 | "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n", | 1323 | "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n", |
1324 | __func__, tty->driver->name); | 1324 | __func__, tty->driver->name); |
1325 | 1325 | ||
1326 | retval = tty_ldisc_lock(tty, 5 * HZ); | ||
1327 | if (retval) | ||
1328 | goto err_release_lock; | ||
1326 | tty->port->itty = tty; | 1329 | tty->port->itty = tty; |
1327 | 1330 | ||
1328 | /* | 1331 | /* |
@@ -1333,6 +1336,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) | |||
1333 | retval = tty_ldisc_setup(tty, tty->link); | 1336 | retval = tty_ldisc_setup(tty, tty->link); |
1334 | if (retval) | 1337 | if (retval) |
1335 | goto err_release_tty; | 1338 | goto err_release_tty; |
1339 | tty_ldisc_unlock(tty); | ||
1336 | /* Return the tty locked so that it cannot vanish under the caller */ | 1340 | /* Return the tty locked so that it cannot vanish under the caller */ |
1337 | return tty; | 1341 | return tty; |
1338 | 1342 | ||
@@ -1345,9 +1349,11 @@ err_module_put: | |||
1345 | 1349 | ||
1346 | /* call the tty release_tty routine to clean out this slot */ | 1350 | /* call the tty release_tty routine to clean out this slot */ |
1347 | err_release_tty: | 1351 | err_release_tty: |
1348 | tty_unlock(tty); | 1352 | tty_ldisc_unlock(tty); |
1349 | tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n", | 1353 | tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n", |
1350 | retval, idx); | 1354 | retval, idx); |
1355 | err_release_lock: | ||
1356 | tty_unlock(tty); | ||
1351 | release_tty(tty, idx); | 1357 | release_tty(tty, idx); |
1352 | return ERR_PTR(retval); | 1358 | return ERR_PTR(retval); |
1353 | } | 1359 | } |
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 24ec5c7e6b20..4e7946c0484b 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c | |||
@@ -337,7 +337,7 @@ static inline void __tty_ldisc_unlock(struct tty_struct *tty) | |||
337 | ldsem_up_write(&tty->ldisc_sem); | 337 | ldsem_up_write(&tty->ldisc_sem); |
338 | } | 338 | } |
339 | 339 | ||
340 | static int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) | 340 | int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) |
341 | { | 341 | { |
342 | int ret; | 342 | int ret; |
343 | 343 | ||
@@ -348,7 +348,7 @@ static int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) | |||
348 | return 0; | 348 | return 0; |
349 | } | 349 | } |
350 | 350 | ||
351 | static void tty_ldisc_unlock(struct tty_struct *tty) | 351 | void tty_ldisc_unlock(struct tty_struct *tty) |
352 | { | 352 | { |
353 | clear_bit(TTY_LDISC_HALTED, &tty->flags); | 353 | clear_bit(TTY_LDISC_HALTED, &tty->flags); |
354 | __tty_ldisc_unlock(tty); | 354 | __tty_ldisc_unlock(tty); |
diff --git a/include/linux/tty.h b/include/linux/tty.h index 7ac8ba208b1f..0a6c71e0ad01 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
@@ -405,6 +405,8 @@ extern const char *tty_name(const struct tty_struct *tty); | |||
405 | extern struct tty_struct *tty_kopen(dev_t device); | 405 | extern struct tty_struct *tty_kopen(dev_t device); |
406 | extern void tty_kclose(struct tty_struct *tty); | 406 | extern void tty_kclose(struct tty_struct *tty); |
407 | extern int tty_dev_name_to_number(const char *name, dev_t *number); | 407 | extern int tty_dev_name_to_number(const char *name, dev_t *number); |
408 | extern int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout); | ||
409 | extern void tty_ldisc_unlock(struct tty_struct *tty); | ||
408 | #else | 410 | #else |
409 | static inline void tty_kref_put(struct tty_struct *tty) | 411 | static inline void tty_kref_put(struct tty_struct *tty) |
410 | { } | 412 | { } |