Skip to content

Commit 24eb237

Browse files
Jiri Slabygregkh
authored andcommitted
tty: hvc_console, fix crashes on parallel open/close
hvc_open sets tty->driver_data to NULL when open fails at some point. Typically, the failure happens in hp->ops->notifier_add(). If there is a racing process which tries to open such mangled tty, which was not closed yet, the process will crash in hvc_open as tty->driver_data is NULL. All this happens because close wants to know whether open failed or not. But ->open should not NULL this and other tty fields for ->close to be happy. ->open should call tty_port_set_initialized(true) and close should check by tty_port_initialized() instead. So do this properly in this driver. So this patch removes these from ->open: * tty_port_tty_set(&hp->port, NULL). This happens on last close. * tty->driver_data = NULL. Dtto. * tty_port_put(&hp->port). This happens in shutdown and until now, this must have been causing a reference underflow, if I am not missing something. Signed-off-by: Jiri Slaby <[email protected]> Cc: stable <[email protected]> Reported-and-tested-by: Raghavendra <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 8f065ac commit 24eb237

File tree

1 file changed

+8
-15
lines changed

1 file changed

+8
-15
lines changed

drivers/tty/hvc/hvc_console.c

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -371,15 +371,14 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
371371
* tty fields and return the kref reference.
372372
*/
373373
if (rc) {
374-
tty_port_tty_set(&hp->port, NULL);
375-
tty->driver_data = NULL;
376-
tty_port_put(&hp->port);
377374
printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);
378-
} else
375+
} else {
379376
/* We are ready... raise DTR/RTS */
380377
if (C_BAUD(tty))
381378
if (hp->ops->dtr_rts)
382379
hp->ops->dtr_rts(hp, 1);
380+
tty_port_set_initialized(&hp->port, true);
381+
}
383382

384383
/* Force wakeup of the polling thread */
385384
hvc_kick();
@@ -389,29 +388,22 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
389388

390389
static void hvc_close(struct tty_struct *tty, struct file * filp)
391390
{
392-
struct hvc_struct *hp;
391+
struct hvc_struct *hp = tty->driver_data;
393392
unsigned long flags;
394393

395394
if (tty_hung_up_p(filp))
396395
return;
397396

398-
/*
399-
* No driver_data means that this close was issued after a failed
400-
* hvc_open by the tty layer's release_dev() function and we can just
401-
* exit cleanly because the kref reference wasn't made.
402-
*/
403-
if (!tty->driver_data)
404-
return;
405-
406-
hp = tty->driver_data;
407-
408397
spin_lock_irqsave(&hp->port.lock, flags);
409398

410399
if (--hp->port.count == 0) {
411400
spin_unlock_irqrestore(&hp->port.lock, flags);
412401
/* We are done with the tty pointer now. */
413402
tty_port_tty_set(&hp->port, NULL);
414403

404+
if (!tty_port_initialized(&hp->port))
405+
return;
406+
415407
if (C_HUPCL(tty))
416408
if (hp->ops->dtr_rts)
417409
hp->ops->dtr_rts(hp, 0);
@@ -428,6 +420,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
428420
* waking periodically to check chars_in_buffer().
429421
*/
430422
tty_wait_until_sent(tty, HVC_CLOSE_WAIT);
423+
tty_port_set_initialized(&hp->port, false);
431424
} else {
432425
if (hp->port.count < 0)
433426
printk(KERN_ERR "hvc_close %X: oops, count is %d\n",

0 commit comments

Comments
 (0)