]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 5 Jul 2018 16:15:02 +0000 (18:15 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 5 Jul 2018 16:15:02 +0000 (18:15 +0200)
added patches:
n_tty-access-echo_-variables-carefully.patch
x86-boot-fix-early-command-line-parsing-when-matching-at-end.patch

queue-4.4/n_tty-access-echo_-variables-carefully.patch [new file with mode: 0644]
queue-4.4/series
queue-4.4/x86-boot-fix-early-command-line-parsing-when-matching-at-end.patch [new file with mode: 0644]

diff --git a/queue-4.4/n_tty-access-echo_-variables-carefully.patch b/queue-4.4/n_tty-access-echo_-variables-carefully.patch
new file mode 100644 (file)
index 0000000..4f866ed
--- /dev/null
@@ -0,0 +1,179 @@
+From ebec3f8f5271139df618ebdf8427e24ba102ba94 Mon Sep 17 00:00:00 2001
+From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+Date: Sat, 26 May 2018 09:53:14 +0900
+Subject: n_tty: Access echo_* variables carefully.
+
+From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+
+commit ebec3f8f5271139df618ebdf8427e24ba102ba94 upstream.
+
+syzbot is reporting stalls at __process_echoes() [1]. This is because
+since ldata->echo_commit < ldata->echo_tail becomes true for some reason,
+the discard loop is serving as almost infinite loop. This patch tries to
+avoid falling into ldata->echo_commit < ldata->echo_tail situation by
+making access to echo_* variables more carefully.
+
+Since reset_buffer_flags() is called without output_lock held, it should
+not touch echo_* variables. And omit a call to reset_buffer_flags() from
+n_tty_open() by using vzalloc().
+
+Since add_echo_byte() is called without output_lock held, it needs memory
+barrier between storing into echo_buf[] and incrementing echo_head counter.
+echo_buf() needs corresponding memory barrier before reading echo_buf[].
+Lack of handling the possibility of not-yet-stored multi-byte operation
+might be the reason of falling into ldata->echo_commit < ldata->echo_tail
+situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to
+echo_buf(ldata, tail + 1), the WARN_ON() fires.
+
+Also, explicitly masking with buffer for the former "while" loop, and
+use ldata->echo_commit > tail for the latter "while" loop.
+
+[1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40
+
+Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com>
+Cc: Peter Hurley <peter@hurleysoftware.com>
+Cc: stable <stable@vger.kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/tty/n_tty.c |   42 ++++++++++++++++++++++++------------------
+ 1 file changed, 24 insertions(+), 18 deletions(-)
+
+--- a/drivers/tty/n_tty.c
++++ b/drivers/tty/n_tty.c
+@@ -147,6 +147,7 @@ static inline unsigned char *read_buf_ad
+ 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)];
+ }
+@@ -324,9 +325,7 @@ static inline void put_tty_queue(unsigne
+ static void reset_buffer_flags(struct n_tty_data *ldata)
+ {
+       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->echo_mark = 0;
+       ldata->line_start = 0;
+       ldata->erasing = 0;
+@@ -647,13 +646,20 @@ static size_t __process_echoes(struct tt
+       old_space = space = tty_write_room(tty);
+       tail = ldata->echo_tail;
+-      while (ldata->echo_commit != tail) {
++      while (MASK(ldata->echo_commit) != MASK(tail)) {
+               c = echo_buf(ldata, tail);
+               if (c == ECHO_OP_START) {
+                       unsigned char op;
+                       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
+                        * operation, get the next byte, which is either the
+                        * op code or a control character value.
+@@ -664,6 +670,8 @@ static size_t __process_echoes(struct tt
+                               unsigned int num_chars, num_bs;
+                       case ECHO_OP_ERASE_TAB:
++                              if (MASK(ldata->echo_commit) == MASK(tail + 2))
++                                      goto not_yet_stored;
+                               num_chars = echo_buf(ldata, tail + 2);
+                               /*
+@@ -758,7 +766,8 @@ static size_t __process_echoes(struct tt
+       /* If the echo buffer is nearly full (so that the possibility exists
+        * of echo overrun before the next commit), then discard enough
+        * 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 + 1) == ECHO_OP_ERASE_TAB)
+                               tail += 3;
+@@ -768,6 +777,7 @@ static size_t __process_echoes(struct tt
+                       tail++;
+       }
++ not_yet_stored:
+       ldata->echo_tail = tail;
+       return old_space - space;
+ }
+@@ -778,6 +788,7 @@ static void commit_echoes(struct tty_str
+       size_t nr, old, echoed;
+       size_t head;
++      mutex_lock(&ldata->output_lock);
+       head = ldata->echo_head;
+       ldata->echo_mark = head;
+       old = ldata->echo_commit - ldata->echo_tail;
+@@ -786,10 +797,12 @@ static void commit_echoes(struct tty_str
+        * is over the threshold (and try again each time another
+        * block is accumulated) */
+       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;
++      }
+-      mutex_lock(&ldata->output_lock);
+       ldata->echo_commit = head;
+       echoed = __process_echoes(tty);
+       mutex_unlock(&ldata->output_lock);
+@@ -840,7 +853,9 @@ static void flush_echoes(struct tty_stru
+ 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++;
+ }
+ /**
+@@ -1920,31 +1935,22 @@ static int n_tty_open(struct tty_struct
+       struct n_tty_data *ldata;
+       /* Currently a malloc failure here can panic */
+-      ldata = vmalloc(sizeof(*ldata));
++      ldata = vzalloc(sizeof(*ldata));
+       if (!ldata)
+-              goto err;
++              return -ENOMEM;
+       ldata->overrun_time = jiffies;
+       mutex_init(&ldata->atomic_read_lock);
+       mutex_init(&ldata->output_lock);
+       tty->disc_data = ldata;
+-      reset_buffer_flags(tty->disc_data);
+-      ldata->column = 0;
+-      ldata->canon_column = 0;
+       ldata->minimum_to_wake = 1;
+-      ldata->num_overrun = 0;
+-      ldata->no_room = 0;
+-      ldata->lnext = 0;
+       tty->closing = 0;
+       /* indicate buffer work may resume */
+       clear_bit(TTY_LDISC_HALTED, &tty->flags);
+       n_tty_set_termios(tty, NULL);
+       tty_unthrottle(tty);
+-
+       return 0;
+-err:
+-      return -ENOMEM;
+ }
+ static inline int input_available_p(struct tty_struct *tty, int poll)
index 5c3b508e62685ae852efc17f3aa78376d1362f75..c6b076b35fb66ee57b5185f2ea9ebd047004f4ef 100644 (file)
@@ -3,3 +3,5 @@ usb-serial-cp210x-add-cesinel-device-ids.patch
 usb-serial-cp210x-add-silicon-labs-ids-for-windows-update.patch
 n_tty-fix-stall-at-n_tty_receive_char_special.patch
 staging-android-ion-return-an-err_ptr-in-ion_map_kernel.patch
+n_tty-access-echo_-variables-carefully.patch
+x86-boot-fix-early-command-line-parsing-when-matching-at-end.patch
diff --git a/queue-4.4/x86-boot-fix-early-command-line-parsing-when-matching-at-end.patch b/queue-4.4/x86-boot-fix-early-command-line-parsing-when-matching-at-end.patch
new file mode 100644 (file)
index 0000000..39ca64f
--- /dev/null
@@ -0,0 +1,124 @@
+From 02afeaae9843733a39cd9b11053748b2d1dc5ae7 Mon Sep 17 00:00:00 2001
+From: Dave Hansen <dave.hansen@linux.intel.com>
+Date: Tue, 22 Dec 2015 14:52:38 -0800
+Subject: x86/boot: Fix early command-line parsing when matching at end
+
+From: Dave Hansen <dave.hansen@linux.intel.com>
+
+commit 02afeaae9843733a39cd9b11053748b2d1dc5ae7 upstream.
+
+The x86 early command line parsing in cmdline_find_option_bool() is
+buggy. If it matches a specified 'option' all the way to the end of the
+command-line, it will consider it a match.
+
+For instance,
+
+  cmdline = "foo";
+  cmdline_find_option_bool(cmdline, "fool");
+
+will return 1. This is particularly annoying since we have actual FPU
+options like "noxsave" and "noxsaves" So, command-line "foo bar noxsave"
+will match *BOTH* a "noxsave" and "noxsaves". (This turns out not to be
+an actual problem because "noxsave" implies "noxsaves", but it's still
+confusing.)
+
+To fix this, we simplify the code and stop tracking 'len'. 'len'
+was trying to indicate either the NULL terminator *OR* the end of a
+non-NULL-terminated command line at 'COMMAND_LINE_SIZE'. But, each of the
+three states is *already* checking 'cmdline' for a NULL terminator.
+
+We _only_ need to check if we have overrun 'COMMAND_LINE_SIZE', and that
+we can do without keeping 'len' around.
+
+Also add some commends to clarify what is going on.
+
+Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
+Signed-off-by: Borislav Petkov <bp@suse.de>
+Cc: Andy Lutomirski <luto@amacapital.net>
+Cc: Borislav Petkov <bp@alien8.de>
+Cc: Brian Gerst <brgerst@gmail.com>
+Cc: Denys Vlasenko <dvlasenk@redhat.com>
+Cc: H. Peter Anvin <hpa@zytor.com>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+Cc: fenghua.yu@intel.com
+Cc: yu-cheng.yu@intel.com
+Link: http://lkml.kernel.org/r/20151222225238.9AEB560C@viggo.jf.intel.com
+Signed-off-by: Ingo Molnar <mingo@kernel.org>
+Cc: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ arch/x86/lib/cmdline.c |   34 ++++++++++++++++++++++++----------
+ 1 file changed, 24 insertions(+), 10 deletions(-)
+
+--- a/arch/x86/lib/cmdline.c
++++ b/arch/x86/lib/cmdline.c
+@@ -21,12 +21,14 @@ static inline int myisspace(u8 c)
+  * @option: option string to look for
+  *
+  * Returns the position of that @option (starts counting with 1)
+- * or 0 on not found.
++ * or 0 on not found.  @option will only be found if it is found
++ * as an entire word in @cmdline.  For instance, if @option="car"
++ * then a cmdline which contains "cart" will not match.
+  */
+ int cmdline_find_option_bool(const char *cmdline, const char *option)
+ {
+       char c;
+-      int len, pos = 0, wstart = 0;
++      int pos = 0, wstart = 0;
+       const char *opptr = NULL;
+       enum {
+               st_wordstart = 0,       /* Start of word/after whitespace */
+@@ -37,11 +39,14 @@ int cmdline_find_option_bool(const char
+       if (!cmdline)
+               return -1;      /* No command line */
+-      len = min_t(int, strlen(cmdline), COMMAND_LINE_SIZE);
+-      if (!len)
++      if (!strlen(cmdline))
+               return 0;
+-      while (len--) {
++      /*
++       * This 'pos' check ensures we do not overrun
++       * a non-NULL-terminated 'cmdline'
++       */
++      while (pos < COMMAND_LINE_SIZE) {
+               c = *(char *)cmdline++;
+               pos++;
+@@ -58,17 +63,26 @@ int cmdline_find_option_bool(const char
+                       /* fall through */
+               case st_wordcmp:
+-                      if (!*opptr)
++                      if (!*opptr) {
++                              /*
++                               * We matched all the way to the end of the
++                               * option we were looking for.  If the
++                               * command-line has a space _or_ ends, then
++                               * we matched!
++                               */
+                               if (!c || myisspace(c))
+                                       return wstart;
+                               else
+                                       state = st_wordskip;
+-                      else if (!c)
++                      } else if (!c) {
++                              /*
++                               * Hit the NULL terminator on the end of
++                               * cmdline.
++                               */
+                               return 0;
+-                      else if (c != *opptr++)
++                      } else if (c != *opptr++) {
+                               state = st_wordskip;
+-                      else if (!len)          /* last word and is matching */
+-                              return wstart;
++                      }
+                       break;
+               case st_wordskip: