From: Greg Kroah-Hartman Date: Thu, 5 Jul 2018 16:15:02 +0000 (+0200) Subject: 4.4-stable patches X-Git-Tag: v4.14.54~17 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e3e2f4c7e59ea92df5dd87fe8e1fbd3b096ad70b;p=thirdparty%2Fkernel%2Fstable-queue.git 4.4-stable patches added patches: 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/n_tty-access-echo_-variables-carefully.patch b/queue-4.4/n_tty-access-echo_-variables-carefully.patch new file mode 100644 index 00000000000..4f866eda10c --- /dev/null +++ b/queue-4.4/n_tty-access-echo_-variables-carefully.patch @@ -0,0 +1,179 @@ +From ebec3f8f5271139df618ebdf8427e24ba102ba94 Mon Sep 17 00:00:00 2001 +From: Tetsuo Handa +Date: Sat, 26 May 2018 09:53:14 +0900 +Subject: n_tty: Access echo_* variables carefully. + +From: Tetsuo Handa + +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 +Reported-by: syzbot +Cc: Peter Hurley +Cc: stable +Signed-off-by: Greg Kroah-Hartman + +--- + 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) diff --git a/queue-4.4/series b/queue-4.4/series index 5c3b508e626..c6b076b35fb 100644 --- a/queue-4.4/series +++ b/queue-4.4/series @@ -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 index 00000000000..39ca64ffdfd --- /dev/null +++ b/queue-4.4/x86-boot-fix-early-command-line-parsing-when-matching-at-end.patch @@ -0,0 +1,124 @@ +From 02afeaae9843733a39cd9b11053748b2d1dc5ae7 Mon Sep 17 00:00:00 2001 +From: Dave Hansen +Date: Tue, 22 Dec 2015 14:52:38 -0800 +Subject: x86/boot: Fix early command-line parsing when matching at end + +From: Dave Hansen + +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 +Signed-off-by: Borislav Petkov +Cc: Andy Lutomirski +Cc: Borislav Petkov +Cc: Brian Gerst +Cc: Denys Vlasenko +Cc: H. Peter Anvin +Cc: Linus Torvalds +Cc: Peter Zijlstra +Cc: Thomas Gleixner +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 +Cc: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman + +--- + 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: