lkml.org 
[lkml]   [2020]   [Jun]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.4 23/34] tty: hvc_console, fix crashes on parallel open/close
    Date
    From: Jiri Slaby <jslaby@suse.cz>

    commit 24eb2377f977fe06d84fca558f891f95bc28a449 upstream.

    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 <jslaby@suse.cz>
    Cc: stable <stable@vger.kernel.org>
    Reported-and-tested-by: Raghavendra <rananta@codeaurora.org>
    Link: https://lore.kernel.org/r/20200526145632.13879-1-jslaby@suse.cz
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    drivers/tty/hvc/hvc_console.c | 23 ++++++++---------------
    1 file changed, 8 insertions(+), 15 deletions(-)

    --- a/drivers/tty/hvc/hvc_console.c
    +++ b/drivers/tty/hvc/hvc_console.c
    @@ -371,15 +371,14 @@ static int hvc_open(struct tty_struct *t
    * tty fields and return the kref reference.
    */
    if (rc) {
    - tty_port_tty_set(&hp->port, NULL);
    - tty->driver_data = NULL;
    - tty_port_put(&hp->port);
    printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);
    - } else
    + } else {
    /* We are ready... raise DTR/RTS */
    if (C_BAUD(tty))
    if (hp->ops->dtr_rts)
    hp->ops->dtr_rts(hp, 1);
    + tty_port_set_initialized(&hp->port, true);
    + }

    /* Force wakeup of the polling thread */
    hvc_kick();
    @@ -389,22 +388,12 @@ static int hvc_open(struct tty_struct *t

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

    if (tty_hung_up_p(filp))
    return;

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

    if (--hp->port.count == 0) {
    @@ -412,6 +401,9 @@ static void hvc_close(struct tty_struct
    /* We are done with the tty pointer now. */
    tty_port_tty_set(&hp->port, NULL);

    + if (!tty_port_initialized(&hp->port))
    + return;
    +
    if (C_HUPCL(tty))
    if (hp->ops->dtr_rts)
    hp->ops->dtr_rts(hp, 0);
    @@ -428,6 +420,7 @@ static void hvc_close(struct tty_struct
    * waking periodically to check chars_in_buffer().
    */
    tty_wait_until_sent(tty, HVC_CLOSE_WAIT);
    + tty_port_set_initialized(&hp->port, false);
    } else {
    if (hp->port.count < 0)
    printk(KERN_ERR "hvc_close %X: oops, count is %d\n",

    \
     
     \ /
      Last update: 2020-06-09 20:00    [W:4.121 / U:0.076 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site