tty: use new tty_insert_flip_string_and_push_buffer() in pty_write()
authorJiri Slaby <jslaby@suse.cz>
Thu, 7 Jul 2022 08:25:58 +0000 (10:25 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 29 Jul 2022 15:25:32 +0000 (17:25 +0200)
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
drivers/tty/tty.h
drivers/tty/tty_buffer.c

index 74bfabe5b45381e62214c353d7461ed5a08f2179..752dab3356d72b6e9c7f3d25ad91eae31ae8fc83 100644 (file)
@@ -111,21 +111,11 @@ static void pty_unthrottle(struct tty_struct *tty)
 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->flow.stopped)
+       if (tty->flow.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);
 }
 
 /**
index b710c5ef89ab2f5939869dacaa73f383628d60e1..f310a8274df1539a9a71c2220fbd7fe2b9067f17 100644 (file)
@@ -111,4 +111,7 @@ static inline void tty_audit_tiocsti(struct tty_struct *tty, char ch)
 
 ssize_t redirected_tty_write(struct kiocb *, struct iov_iter *);
 
+int tty_insert_flip_string_and_push_buffer(struct tty_port *port,
+               const unsigned char *chars, size_t cnt);
+
 #endif
index 76fb600c1f7933777fcd3eda77a272904b3ee68d..f3143ae4bf7fe82a58176b0faffeef10e7e5b74a 100644 (file)
@@ -562,6 +562,37 @@ void tty_flip_buffer_push(struct tty_port *port)
 }
 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
  *     @port: tty port to initialise