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. See71a174b39f
(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:71a174b39f
(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>
46 lines
1.6 KiB
C
46 lines
1.6 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_TTY_FLIP_H
|
|
#define _LINUX_TTY_FLIP_H
|
|
|
|
extern int tty_buffer_set_limit(struct tty_port *port, int limit);
|
|
extern int tty_buffer_space_avail(struct tty_port *port);
|
|
extern int tty_buffer_request_room(struct tty_port *port, size_t size);
|
|
extern int tty_insert_flip_string_flags(struct tty_port *port,
|
|
const unsigned char *chars, const char *flags, size_t size);
|
|
extern int tty_insert_flip_string_fixed_flag(struct tty_port *port,
|
|
const unsigned char *chars, char flag, size_t size);
|
|
extern int tty_prepare_flip_string(struct tty_port *port,
|
|
unsigned char **chars, size_t size);
|
|
extern void tty_flip_buffer_push(struct tty_port *port);
|
|
int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag);
|
|
|
|
static inline int tty_insert_flip_char(struct tty_port *port,
|
|
unsigned char ch, char flag)
|
|
{
|
|
struct tty_buffer *tb = port->buf.tail;
|
|
int change;
|
|
|
|
change = (tb->flags & TTYB_NORMAL) && (flag != TTY_NORMAL);
|
|
if (!change && tb->used < tb->size) {
|
|
if (~tb->flags & TTYB_NORMAL)
|
|
*flag_buf_ptr(tb, tb->used) = flag;
|
|
*char_buf_ptr(tb, tb->used++) = ch;
|
|
return 1;
|
|
}
|
|
return __tty_insert_flip_char(port, ch, flag);
|
|
}
|
|
|
|
static inline int tty_insert_flip_string(struct tty_port *port,
|
|
const unsigned char *chars, size_t size)
|
|
{
|
|
return tty_insert_flip_string_fixed_flag(port, chars, TTY_NORMAL, size);
|
|
}
|
|
|
|
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 */
|