lkml.org 
[lkml]   [2022]   [Jul]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.9 25/26] tty: use new tty_insert_flip_string_and_push_buffer() in pty_write()
    Date
    From: Jiri Slaby <jslaby@suse.cz>

    commit a501ab75e7624d133a5a3c7ec010687c8b961d23 upstream.

    There is a race in pty_write(). pty_write() can be called in parallel
    with e.g. ioctl(TIOCSTI) or ioctl(TCXONC) which also inserts chars to
    the buffer. Provided, tty_flip_buffer_push() in pty_write() is called
    outside the lock, it can commit inconsistent tail. This can lead to out
    of bounds writes and other issues. See the Link below.

    To fix this, we have to introduce a new helper called
    tty_insert_flip_string_and_push_buffer(). It does both
    tty_insert_flip_string() and tty_flip_buffer_commit() under the port
    lock. It also calls queue_work(), but outside the lock. See
    71a174b39f10 (pty: do tty_flip_buffer_push without port->lock in
    pty_write) for the reasons.

    Keep the helper internal-only (in drivers' tty.h). It is not intended to
    be used widely.

    Link: https://seclists.org/oss-sec/2022/q2/155
    Fixes: 71a174b39f10 (pty: do tty_flip_buffer_push without port->lock in pty_write)
    Cc: 一只狗 <chennbnbnb@gmail.com>
    Cc: Dan Carpenter <dan.carpenter@oracle.com>
    Suggested-by: Hillf Danton <hdanton@sina.com>
    Signed-off-by: Jiri Slaby <jslaby@suse.cz>
    Link: https://lore.kernel.org/r/20220707082558.9250-2-jslaby@suse.cz
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    drivers/tty/pty.c | 14 ++------------
    drivers/tty/tty_buffer.c | 31 +++++++++++++++++++++++++++++++
    include/linux/tty_flip.h | 3 +++
    3 files changed, 36 insertions(+), 12 deletions(-)

    --- a/drivers/tty/pty.c
    +++ b/drivers/tty/pty.c
    @@ -106,21 +106,11 @@ static void pty_unthrottle(struct tty_st
    static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
    {
    struct tty_struct *to = tty->link;
    - unsigned long flags;

    - if (tty->stopped)
    + if (tty->stopped || !c)
    return 0;

    - if (c > 0) {
    - spin_lock_irqsave(&to->port->lock, flags);
    - /* Stuff the data into the input queue of the other end */
    - c = tty_insert_flip_string(to->port, buf, c);
    - spin_unlock_irqrestore(&to->port->lock, flags);
    - /* And shovel */
    - if (c)
    - tty_flip_buffer_push(to->port);
    - }
    - return c;
    + return tty_insert_flip_string_and_push_buffer(to->port, buf, c);
    }

    /**
    --- a/drivers/tty/tty_buffer.c
    +++ b/drivers/tty/tty_buffer.c
    @@ -556,6 +556,37 @@ void tty_flip_buffer_push(struct tty_por
    EXPORT_SYMBOL(tty_flip_buffer_push);

    /**
    + * tty_insert_flip_string_and_push_buffer - add characters to the tty buffer and
    + * push
    + * @port: tty port
    + * @chars: characters
    + * @size: size
    + *
    + * The function combines tty_insert_flip_string() and tty_flip_buffer_push()
    + * with the exception of properly holding the @port->lock.
    + *
    + * To be used only internally (by pty currently).
    + *
    + * Returns: the number added.
    + */
    +int tty_insert_flip_string_and_push_buffer(struct tty_port *port,
    + const unsigned char *chars, size_t size)
    +{
    + struct tty_bufhead *buf = &port->buf;
    + unsigned long flags;
    +
    + spin_lock_irqsave(&port->lock, flags);
    + size = tty_insert_flip_string(port, chars, size);
    + if (size)
    + tty_flip_buffer_commit(buf->tail);
    + spin_unlock_irqrestore(&port->lock, flags);
    +
    + queue_work(system_unbound_wq, &buf->work);
    +
    + return size;
    +}
    +
    +/**
    * tty_buffer_init - prepare a tty buffer structure
    * @tty: tty to initialise
    *
    --- a/include/linux/tty_flip.h
    +++ b/include/linux/tty_flip.h
    @@ -38,4 +38,7 @@ static inline int tty_insert_flip_string
    extern void tty_buffer_lock_exclusive(struct tty_port *port);
    extern void tty_buffer_unlock_exclusive(struct tty_port *port);

    +int tty_insert_flip_string_and_push_buffer(struct tty_port *port,
    + const unsigned char *chars, size_t cnt);
    +
    #endif /* _LINUX_TTY_FLIP_H */

    \
     
     \ /
      Last update: 2022-07-27 18:25    [W:4.179 / U:0.068 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site