Commit 652788a9 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'tty-4.18-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial fixes from Greg KH:
 "Here are five fixes for the tty core and some serial drivers.

  The tty core ones fix some security and other issues reported by the
  syzbot that I have taken too long in responding to (sorry Tetsuo!).

  The 8350 serial driver fix resolves an issue of devices that used to
  work properly stopping working as they shouldn't have been added to a
  blacklist.

  All of these have been in linux-next for a few days with no reported
  issues"

* tag 'tty-4.18-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  vt: prevent leaking uninitialized data to userspace via /dev/vcs*
  serdev: fix memleak on module unload
  serial: 8250_pci: Remove stalled entries in blacklist
  n_tty: Access echo_* variables carefully.
  n_tty: Fix stall at n_tty_receive_char_special().
parents c2aee376 21eff69a
...@@ -124,6 +124,8 @@ struct n_tty_data { ...@@ -124,6 +124,8 @@ struct n_tty_data {
struct mutex output_lock; struct mutex output_lock;
}; };
#define MASK(x) ((x) & (N_TTY_BUF_SIZE - 1))
static inline size_t read_cnt(struct n_tty_data *ldata) static inline size_t read_cnt(struct n_tty_data *ldata)
{ {
return ldata->read_head - ldata->read_tail; return ldata->read_head - ldata->read_tail;
...@@ -141,6 +143,7 @@ static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i) ...@@ -141,6 +143,7 @@ static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i)
static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i) static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i)
{ {
smp_rmb(); /* Matches smp_wmb() in add_echo_byte(). */
return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)];
} }
...@@ -316,9 +319,7 @@ static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata) ...@@ -316,9 +319,7 @@ static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
static void reset_buffer_flags(struct n_tty_data *ldata) static void reset_buffer_flags(struct n_tty_data *ldata)
{ {
ldata->read_head = ldata->canon_head = ldata->read_tail = 0; ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
ldata->commit_head = 0; ldata->commit_head = 0;
ldata->echo_mark = 0;
ldata->line_start = 0; ldata->line_start = 0;
ldata->erasing = 0; ldata->erasing = 0;
...@@ -617,12 +618,19 @@ static size_t __process_echoes(struct tty_struct *tty) ...@@ -617,12 +618,19 @@ static size_t __process_echoes(struct tty_struct *tty)
old_space = space = tty_write_room(tty); old_space = space = tty_write_room(tty);
tail = ldata->echo_tail; tail = ldata->echo_tail;
while (ldata->echo_commit != tail) { while (MASK(ldata->echo_commit) != MASK(tail)) {
c = echo_buf(ldata, tail); c = echo_buf(ldata, tail);
if (c == ECHO_OP_START) { if (c == ECHO_OP_START) {
unsigned char op; unsigned char op;
int no_space_left = 0; int no_space_left = 0;
/*
* Since add_echo_byte() is called without holding
* output_lock, we might see only portion of multi-byte
* operation.
*/
if (MASK(ldata->echo_commit) == MASK(tail + 1))
goto not_yet_stored;
/* /*
* If the buffer byte is the start of a multi-byte * If the buffer byte is the start of a multi-byte
* operation, get the next byte, which is either the * operation, get the next byte, which is either the
...@@ -634,6 +642,8 @@ static size_t __process_echoes(struct tty_struct *tty) ...@@ -634,6 +642,8 @@ static size_t __process_echoes(struct tty_struct *tty)
unsigned int num_chars, num_bs; unsigned int num_chars, num_bs;
case ECHO_OP_ERASE_TAB: case ECHO_OP_ERASE_TAB:
if (MASK(ldata->echo_commit) == MASK(tail + 2))
goto not_yet_stored;
num_chars = echo_buf(ldata, tail + 2); num_chars = echo_buf(ldata, tail + 2);
/* /*
...@@ -728,7 +738,8 @@ static size_t __process_echoes(struct tty_struct *tty) ...@@ -728,7 +738,8 @@ static size_t __process_echoes(struct tty_struct *tty)
/* If the echo buffer is nearly full (so that the possibility exists /* If the echo buffer is nearly full (so that the possibility exists
* of echo overrun before the next commit), then discard enough * of echo overrun before the next commit), then discard enough
* data at the tail to prevent a subsequent overrun */ * data at the tail to prevent a subsequent overrun */
while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { while (ldata->echo_commit > tail &&
ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) {
if (echo_buf(ldata, tail) == ECHO_OP_START) { if (echo_buf(ldata, tail) == ECHO_OP_START) {
if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB) if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB)
tail += 3; tail += 3;
...@@ -738,6 +749,7 @@ static size_t __process_echoes(struct tty_struct *tty) ...@@ -738,6 +749,7 @@ static size_t __process_echoes(struct tty_struct *tty)
tail++; tail++;
} }
not_yet_stored:
ldata->echo_tail = tail; ldata->echo_tail = tail;
return old_space - space; return old_space - space;
} }
...@@ -748,6 +760,7 @@ static void commit_echoes(struct tty_struct *tty) ...@@ -748,6 +760,7 @@ static void commit_echoes(struct tty_struct *tty)
size_t nr, old, echoed; size_t nr, old, echoed;
size_t head; size_t head;
mutex_lock(&ldata->output_lock);
head = ldata->echo_head; head = ldata->echo_head;
ldata->echo_mark = head; ldata->echo_mark = head;
old = ldata->echo_commit - ldata->echo_tail; old = ldata->echo_commit - ldata->echo_tail;
...@@ -756,10 +769,12 @@ static void commit_echoes(struct tty_struct *tty) ...@@ -756,10 +769,12 @@ static void commit_echoes(struct tty_struct *tty)
* is over the threshold (and try again each time another * is over the threshold (and try again each time another
* block is accumulated) */ * block is accumulated) */
nr = head - ldata->echo_tail; nr = head - ldata->echo_tail;
if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK)) if (nr < ECHO_COMMIT_WATERMARK ||
(nr % ECHO_BLOCK > old % ECHO_BLOCK)) {
mutex_unlock(&ldata->output_lock);
return; return;
}
mutex_lock(&ldata->output_lock);
ldata->echo_commit = head; ldata->echo_commit = head;
echoed = __process_echoes(tty); echoed = __process_echoes(tty);
mutex_unlock(&ldata->output_lock); mutex_unlock(&ldata->output_lock);
...@@ -810,7 +825,9 @@ static void flush_echoes(struct tty_struct *tty) ...@@ -810,7 +825,9 @@ static void flush_echoes(struct tty_struct *tty)
static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata) static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
{ {
*echo_buf_addr(ldata, ldata->echo_head++) = c; *echo_buf_addr(ldata, ldata->echo_head) = c;
smp_wmb(); /* Matches smp_rmb() in echo_buf(). */
ldata->echo_head++;
} }
/** /**
...@@ -978,14 +995,15 @@ static void eraser(unsigned char c, struct tty_struct *tty) ...@@ -978,14 +995,15 @@ static void eraser(unsigned char c, struct tty_struct *tty)
} }
seen_alnums = 0; seen_alnums = 0;
while (ldata->read_head != ldata->canon_head) { while (MASK(ldata->read_head) != MASK(ldata->canon_head)) {
head = ldata->read_head; head = ldata->read_head;
/* erase a single possibly multibyte character */ /* erase a single possibly multibyte character */
do { do {
head--; head--;
c = read_buf(ldata, head); c = read_buf(ldata, head);
} while (is_continuation(c, tty) && head != ldata->canon_head); } while (is_continuation(c, tty) &&
MASK(head) != MASK(ldata->canon_head));
/* do not partially erase */ /* do not partially erase */
if (is_continuation(c, tty)) if (is_continuation(c, tty))
...@@ -1027,7 +1045,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) ...@@ -1027,7 +1045,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
* This info is used to go back the correct * This info is used to go back the correct
* number of columns. * number of columns.
*/ */
while (tail != ldata->canon_head) { while (MASK(tail) != MASK(ldata->canon_head)) {
tail--; tail--;
c = read_buf(ldata, tail); c = read_buf(ldata, tail);
if (c == '\t') { if (c == '\t') {
...@@ -1302,7 +1320,7 @@ n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) ...@@ -1302,7 +1320,7 @@ n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
finish_erasing(ldata); finish_erasing(ldata);
echo_char(c, tty); echo_char(c, tty);
echo_char_raw('\n', ldata); echo_char_raw('\n', ldata);
while (tail != ldata->read_head) { while (MASK(tail) != MASK(ldata->read_head)) {
echo_char(read_buf(ldata, tail), tty); echo_char(read_buf(ldata, tail), tty);
tail++; tail++;
} }
...@@ -1878,30 +1896,21 @@ static int n_tty_open(struct tty_struct *tty) ...@@ -1878,30 +1896,21 @@ static int n_tty_open(struct tty_struct *tty)
struct n_tty_data *ldata; struct n_tty_data *ldata;
/* Currently a malloc failure here can panic */ /* Currently a malloc failure here can panic */
ldata = vmalloc(sizeof(*ldata)); ldata = vzalloc(sizeof(*ldata));
if (!ldata) if (!ldata)
goto err; return -ENOMEM;
ldata->overrun_time = jiffies; ldata->overrun_time = jiffies;
mutex_init(&ldata->atomic_read_lock); mutex_init(&ldata->atomic_read_lock);
mutex_init(&ldata->output_lock); mutex_init(&ldata->output_lock);
tty->disc_data = ldata; tty->disc_data = ldata;
reset_buffer_flags(tty->disc_data);
ldata->column = 0;
ldata->canon_column = 0;
ldata->num_overrun = 0;
ldata->no_room = 0;
ldata->lnext = 0;
tty->closing = 0; tty->closing = 0;
/* indicate buffer work may resume */ /* indicate buffer work may resume */
clear_bit(TTY_LDISC_HALTED, &tty->flags); clear_bit(TTY_LDISC_HALTED, &tty->flags);
n_tty_set_termios(tty, NULL); n_tty_set_termios(tty, NULL);
tty_unthrottle(tty); tty_unthrottle(tty);
return 0; return 0;
err:
return -ENOMEM;
} }
static inline int input_available_p(struct tty_struct *tty, int poll) static inline int input_available_p(struct tty_struct *tty, int poll)
...@@ -2411,7 +2420,7 @@ static unsigned long inq_canon(struct n_tty_data *ldata) ...@@ -2411,7 +2420,7 @@ static unsigned long inq_canon(struct n_tty_data *ldata)
tail = ldata->read_tail; tail = ldata->read_tail;
nr = head - tail; nr = head - tail;
/* Skip EOF-chars.. */ /* Skip EOF-chars.. */
while (head != tail) { while (MASK(head) != MASK(tail)) {
if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) && if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) &&
read_buf(ldata, tail) == __DISABLED_CHAR) read_buf(ldata, tail) == __DISABLED_CHAR)
nr--; nr--;
......
...@@ -617,6 +617,7 @@ EXPORT_SYMBOL_GPL(__serdev_device_driver_register); ...@@ -617,6 +617,7 @@ EXPORT_SYMBOL_GPL(__serdev_device_driver_register);
static void __exit serdev_exit(void) static void __exit serdev_exit(void)
{ {
bus_unregister(&serdev_bus_type); bus_unregister(&serdev_bus_type);
ida_destroy(&ctrl_ida);
} }
module_exit(serdev_exit); module_exit(serdev_exit);
......
...@@ -3339,9 +3339,7 @@ static const struct pci_device_id blacklist[] = { ...@@ -3339,9 +3339,7 @@ static const struct pci_device_id blacklist[] = {
/* multi-io cards handled by parport_serial */ /* multi-io cards handled by parport_serial */
{ PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */ { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */
{ PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */ { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */
{ PCI_DEVICE(0x4348, 0x7173), }, /* WCH CH355 4S */
{ PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */ { PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */
{ PCI_DEVICE(0x1c00, 0x3470), }, /* WCH CH384 4S */
/* Moxa Smartio MUE boards handled by 8250_moxa */ /* Moxa Smartio MUE boards handled by 8250_moxa */
{ PCI_VDEVICE(MOXA, 0x1024), }, { PCI_VDEVICE(MOXA, 0x1024), },
......
...@@ -784,7 +784,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ ...@@ -784,7 +784,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
if (!*vc->vc_uni_pagedir_loc) if (!*vc->vc_uni_pagedir_loc)
con_set_default_unimap(vc); con_set_default_unimap(vc);
vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_KERNEL);
if (!vc->vc_screenbuf) if (!vc->vc_screenbuf)
goto err_free; goto err_free;
...@@ -871,7 +871,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, ...@@ -871,7 +871,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
if (new_screen_size > (4 << 20)) if (new_screen_size > (4 << 20))
return -EINVAL; return -EINVAL;
newscreen = kmalloc(new_screen_size, GFP_USER); newscreen = kzalloc(new_screen_size, GFP_USER);
if (!newscreen) if (!newscreen)
return -ENOMEM; return -ENOMEM;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment