Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiroslav Benes <mbenes@suse.cz>2018-11-01 14:15:58 +0100
committerMiroslav Benes <mbenes@suse.cz>2018-11-01 14:15:58 +0100
commit830e90a77059c673c0c3b181428205cbf0839bdc (patch)
tree0e0d36fd7ca64d96cc7279e33f3426d51b1bf9f3
parent915e043a8afff05cc704bfb1b2dba0190bafc591 (diff)
parent2a3f72509892821ad813a0a4139ae8d26394bf1e (diff)
Merge branch 'bsc#1112039_15' into SLE15_Update_0
-rw-r--r--bsc1112039/livepatch_bsc1112039.c257
-rw-r--r--bsc1112039/livepatch_bsc1112039.h16
-rw-r--r--bsc1112039/patched_funcs.csv2
3 files changed, 275 insertions, 0 deletions
diff --git a/bsc1112039/livepatch_bsc1112039.c b/bsc1112039/livepatch_bsc1112039.c
new file mode 100644
index 0000000..7b77348
--- /dev/null
+++ b/bsc1112039/livepatch_bsc1112039.c
@@ -0,0 +1,257 @@
+/*
+ * livepatch_bsc1112039
+ *
+ * Fix for CVE-2018-18386, bsc#1112039
+ *
+ * Upstream commit:
+ * 966031f34018 ("n_tty: fix EXTPROC vs ICANON interaction with
+ * TIOCINQ (aka FIONREAD)")
+ *
+ * SLE12(-SP1) commit:
+ * aa69dcda7cafc0426b33684673756ae09d4af8db
+ *
+ * SLE12-SP2 commits:
+ * b763bd0e6ca956c7794437b255fecb6d2435acac ("Linux 4.4.109")
+ * 7d3c6375af00e4d84fb5b082fc47cc097f5d086c (merged trivially)
+ *
+ * SLE12-SP3 commits:
+ * 39b69ee3f2767910b8c7eebe036fe0c055f806c6 ("Linux 4.4.109")
+ * 7d3c6375af00e4d84fb5b082fc47cc097f5d086c (merged trivially)
+ *
+ * SLE15 commit:
+ * f8b3f759ae53dfb7ccfefdc6c587f916f1533522
+ *
+ *
+ * Copyright (c) 2018 SUSE
+ * Author: Nicolai Stange <nstange@suse.de>
+ *
+ * Based on the original Linux kernel code. Other copyrights apply.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/tty.h>
+#include <linux/bitmap.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include "livepatch_bsc1112039.h"
+#include "kallsyms_relocs.h"
+
+
+static void (*klp_process_echoes)(struct tty_struct *tty);
+
+static struct klp_kallsyms_reloc klp_funcs[] = {
+ { "process_echoes", (void *)&klp_process_echoes },
+};
+
+
+/* from drivers/tty/n_tty.c */
+struct n_tty_data {
+ /* producer-published */
+ size_t read_head;
+ size_t commit_head;
+ size_t canon_head;
+ size_t echo_head;
+ size_t echo_commit;
+ size_t echo_mark;
+ DECLARE_BITMAP(char_map, 256);
+
+ /* private to n_tty_receive_overrun (single-threaded) */
+ unsigned long overrun_time;
+ int num_overrun;
+
+ /* non-atomic */
+ bool no_room;
+
+ /* must hold exclusive termios_rwsem to reset these */
+ unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
+ unsigned char push:1;
+
+ /* shared by producer and consumer */
+ char read_buf[N_TTY_BUF_SIZE];
+ DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE);
+ unsigned char echo_buf[N_TTY_BUF_SIZE];
+
+ /* consumer-published */
+ size_t read_tail;
+ size_t line_start;
+
+ /* protected by output lock */
+ unsigned int column;
+ unsigned int canon_column;
+ size_t echo_tail;
+
+ struct mutex atomic_read_lock;
+ struct mutex output_lock;
+};
+
+/* inlined */
+static inline size_t klp_read_cnt(struct n_tty_data *ldata)
+{
+ return ldata->read_head - ldata->read_tail;
+}
+
+/* inlined */
+static inline unsigned char klp_read_buf(struct n_tty_data *ldata, size_t i)
+{
+ return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
+}
+
+/* inlined */
+static unsigned long klp_inq_canon(struct n_tty_data *ldata)
+{
+ size_t nr, head, tail;
+
+ if (ldata->canon_head == ldata->read_tail)
+ return 0;
+ head = ldata->canon_head;
+ tail = ldata->read_tail;
+ nr = head - tail;
+ /* Skip EOF-chars.. */
+ while (head != tail) {
+ if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) &&
+ klp_read_buf(ldata, tail) == __DISABLED_CHAR)
+ nr--;
+ tail++;
+ }
+ return nr;
+}
+
+
+
+/* patched */
+void klp_n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ /*
+ * Fix CVE-2018-18386
+ * -1 line, +1 line
+ */
+ if (!old || (old->c_lflag ^ tty->termios.c_lflag) & (ICANON | EXTPROC)) {
+ bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
+ ldata->line_start = ldata->read_tail;
+ if (!L_ICANON(tty) || !klp_read_cnt(ldata)) {
+ ldata->canon_head = ldata->read_tail;
+ ldata->push = 0;
+ } else {
+ set_bit((ldata->read_head - 1) & (N_TTY_BUF_SIZE - 1),
+ ldata->read_flags);
+ ldata->canon_head = ldata->read_head;
+ ldata->push = 1;
+ }
+ ldata->commit_head = ldata->read_head;
+ ldata->erasing = 0;
+ ldata->lnext = 0;
+ }
+
+ ldata->icanon = (L_ICANON(tty) != 0);
+
+ if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
+ I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
+ I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
+ I_PARMRK(tty)) {
+ bitmap_zero(ldata->char_map, 256);
+
+ if (I_IGNCR(tty) || I_ICRNL(tty))
+ set_bit('\r', ldata->char_map);
+ if (I_INLCR(tty))
+ set_bit('\n', ldata->char_map);
+
+ if (L_ICANON(tty)) {
+ set_bit(ERASE_CHAR(tty), ldata->char_map);
+ set_bit(KILL_CHAR(tty), ldata->char_map);
+ set_bit(EOF_CHAR(tty), ldata->char_map);
+ set_bit('\n', ldata->char_map);
+ set_bit(EOL_CHAR(tty), ldata->char_map);
+ if (L_IEXTEN(tty)) {
+ set_bit(WERASE_CHAR(tty), ldata->char_map);
+ set_bit(LNEXT_CHAR(tty), ldata->char_map);
+ set_bit(EOL2_CHAR(tty), ldata->char_map);
+ if (L_ECHO(tty))
+ set_bit(REPRINT_CHAR(tty),
+ ldata->char_map);
+ }
+ }
+ if (I_IXON(tty)) {
+ set_bit(START_CHAR(tty), ldata->char_map);
+ set_bit(STOP_CHAR(tty), ldata->char_map);
+ }
+ if (L_ISIG(tty)) {
+ set_bit(INTR_CHAR(tty), ldata->char_map);
+ set_bit(QUIT_CHAR(tty), ldata->char_map);
+ set_bit(SUSP_CHAR(tty), ldata->char_map);
+ }
+ clear_bit(__DISABLED_CHAR, ldata->char_map);
+ ldata->raw = 0;
+ ldata->real_raw = 0;
+ } else {
+ ldata->raw = 1;
+ if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
+ (I_IGNPAR(tty) || !I_INPCK(tty)) &&
+ (tty->driver->flags & TTY_DRIVER_REAL_RAW))
+ ldata->real_raw = 1;
+ else
+ ldata->real_raw = 0;
+ }
+ /*
+ * Fix tty hang when I_IXON(tty) is cleared, but the tty
+ * been stopped by STOP_CHAR(tty) before it.
+ */
+ if (!I_IXON(tty) && old && (old->c_iflag & IXON) && !tty->flow_stopped) {
+ start_tty(tty);
+ klp_process_echoes(tty);
+ }
+
+ /* The termios change make the tty ready for I/O */
+ wake_up_interruptible(&tty->write_wait);
+ wake_up_interruptible(&tty->read_wait);
+}
+
+/* patched */
+int klp_n_tty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ int retval;
+
+ switch (cmd) {
+ case TIOCOUTQ:
+ return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
+ case TIOCINQ:
+ down_write(&tty->termios_rwsem);
+ /*
+ * Fix CVE-2018-18386
+ * -1 line, +1 line
+ */
+ if (L_ICANON(tty) && !L_EXTPROC(tty))
+ retval = klp_inq_canon(ldata);
+ else
+ retval = klp_read_cnt(ldata);
+ up_write(&tty->termios_rwsem);
+ return put_user(retval, (unsigned int __user *) arg);
+ default:
+ return n_tty_ioctl_helper(tty, file, cmd, arg);
+ }
+}
+
+
+
+int livepatch_bsc1112039_init(void)
+{
+ return __klp_resolve_kallsyms_relocs(klp_funcs, ARRAY_SIZE(klp_funcs));
+}
diff --git a/bsc1112039/livepatch_bsc1112039.h b/bsc1112039/livepatch_bsc1112039.h
new file mode 100644
index 0000000..42bf248
--- /dev/null
+++ b/bsc1112039/livepatch_bsc1112039.h
@@ -0,0 +1,16 @@
+#ifndef _LIVEPATCH_BSC1112039_H
+#define _LIVEPATCH_BSC1112039_H
+
+int livepatch_bsc1112039_init(void);
+static inline void livepatch_bsc1112039_cleanup(void) {}
+
+
+struct tty_struct;
+struct ktermios;
+struct file;
+
+void klp_n_tty_set_termios(struct tty_struct *tty, struct ktermios *old);
+int klp_n_tty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+#endif /* _LIVEPATCH_BSC1112039_H */
diff --git a/bsc1112039/patched_funcs.csv b/bsc1112039/patched_funcs.csv
new file mode 100644
index 0000000..ed95702
--- /dev/null
+++ b/bsc1112039/patched_funcs.csv
@@ -0,0 +1,2 @@
+vmlinux n_tty_set_termios klp_n_tty_set_termios
+vmlinux n_tty_ioctl klp_n_tty_ioctl