lkml.org 
[lkml]   [2021]   [Dec]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.15 116/177] tty: n_hdlc: make n_hdlc_tty_wakeup() asynchronous
    Date
    From: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>

    commit 1ee33b1ca2b8dabfcc17198ffd049a6b55674a86 upstream.

    syzbot is reporting that an unprivileged user who logged in from tty
    console can crash the system using a reproducer shown below [1], for
    n_hdlc_tty_wakeup() is synchronously calling n_hdlc_send_frames().

    ----------
    #include <sys/ioctl.h>
    #include <unistd.h>

    int main(int argc, char *argv[])
    {
    const int disc = 0xd;

    ioctl(1, TIOCSETD, &disc);
    while (1) {
    ioctl(1, TCXONC, 0);
    write(1, "", 1);
    ioctl(1, TCXONC, 1); /* Kernel panic - not syncing: scheduling while atomic */
    }
    }
    ----------

    Linus suspected that "struct tty_ldisc"->ops->write_wakeup() must not
    sleep, and Jiri confirmed it from include/linux/tty_ldisc.h. Thus, defer
    n_hdlc_send_frames() from n_hdlc_tty_wakeup() to a WQ context like
    net/nfc/nci/uart.c does.

    Link: https://syzkaller.appspot.com/bug?extid=5f47a8cea6a12b77a876 [1]
    Reported-by: syzbot <syzbot+5f47a8cea6a12b77a876@syzkaller.appspotmail.com>
    Cc: stable <stable@vger.kernel.org>
    Analyzed-by: Fabio M. De Francesco <fmdefrancesco@gmail.com>
    Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
    Confirmed-by: Jiri Slaby <jirislaby@kernel.org>
    Reviewed-by: Fabio M. De Francesco <fmdefrancesco@gmail.com>
    Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
    Link: https://lore.kernel.org/r/40de8b7e-a3be-4486-4e33-1b1d1da452f8@i-love.sakura.ne.jp
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    drivers/tty/n_hdlc.c | 23 ++++++++++++++++++++++-
    1 file changed, 22 insertions(+), 1 deletion(-)

    --- a/drivers/tty/n_hdlc.c
    +++ b/drivers/tty/n_hdlc.c
    @@ -140,6 +140,8 @@ struct n_hdlc {
    struct n_hdlc_buf_list rx_buf_list;
    struct n_hdlc_buf_list tx_free_buf_list;
    struct n_hdlc_buf_list rx_free_buf_list;
    + struct work_struct write_work;
    + struct tty_struct *tty_for_write_work;
    };

    /*
    @@ -154,6 +156,7 @@ static struct n_hdlc_buf *n_hdlc_buf_get
    /* Local functions */

    static struct n_hdlc *n_hdlc_alloc(void);
    +static void n_hdlc_tty_write_work(struct work_struct *work);

    /* max frame size for memory allocations */
    static int maxframe = 4096;
    @@ -210,6 +213,8 @@ static void n_hdlc_tty_close(struct tty_
    wake_up_interruptible(&tty->read_wait);
    wake_up_interruptible(&tty->write_wait);

    + cancel_work_sync(&n_hdlc->write_work);
    +
    n_hdlc_free_buf_list(&n_hdlc->rx_free_buf_list);
    n_hdlc_free_buf_list(&n_hdlc->tx_free_buf_list);
    n_hdlc_free_buf_list(&n_hdlc->rx_buf_list);
    @@ -241,6 +246,8 @@ static int n_hdlc_tty_open(struct tty_st
    return -ENFILE;
    }

    + INIT_WORK(&n_hdlc->write_work, n_hdlc_tty_write_work);
    + n_hdlc->tty_for_write_work = tty;
    tty->disc_data = n_hdlc;
    tty->receive_room = 65536;

    @@ -335,6 +342,20 @@ check_again:
    } /* end of n_hdlc_send_frames() */

    /**
    + * n_hdlc_tty_write_work - Asynchronous callback for transmit wakeup
    + * @work: pointer to work_struct
    + *
    + * Called when low level device driver can accept more send data.
    + */
    +static void n_hdlc_tty_write_work(struct work_struct *work)
    +{
    + struct n_hdlc *n_hdlc = container_of(work, struct n_hdlc, write_work);
    + struct tty_struct *tty = n_hdlc->tty_for_write_work;
    +
    + n_hdlc_send_frames(n_hdlc, tty);
    +} /* end of n_hdlc_tty_write_work() */
    +
    +/**
    * n_hdlc_tty_wakeup - Callback for transmit wakeup
    * @tty: pointer to associated tty instance data
    *
    @@ -344,7 +365,7 @@ static void n_hdlc_tty_wakeup(struct tty
    {
    struct n_hdlc *n_hdlc = tty->disc_data;

    - n_hdlc_send_frames(n_hdlc, tty);
    + schedule_work(&n_hdlc->write_work);
    } /* end of n_hdlc_tty_wakeup() */

    /**

    \
     
     \ /
      Last update: 2021-12-20 16:15    [W:4.084 / U:0.272 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site