Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Mon, 24 Jul 2017 16:38:24 +0300
From: Hans Liljestrand <liljestrandh@...il.com>
To: kernel-hardening@...ts.openwall.com
Cc: elena.reshetova@...el.com,
	dave.hansen@...el.com,
	keescook@...omium.org,
	hpa@...or.com,
	Hans Liljestrand <LiljestrandH@...il.com>
Subject: [RFC PATCH 5/5] lkdtm: Add kernel MPX testing

Tests MPXK functionality via lkdtm test cases. Currently tests basic
bound propagation and instrumentation for memcpy and kmalloc.

Signed-off-by: Hans Liljestrand <LiljestrandH@...il.com>
Signed-off-by: Elena Reshetova <elena.reshetova@...el.com>
---
 drivers/misc/Makefile          |   7 +++
 drivers/misc/lkdtm.h           |   7 +++
 drivers/misc/lkdtm_core.c      |   6 +++
 drivers/misc/lkdtm_mpxk.c      | 115 +++++++++++++++++++++++++++++++++++++++++
 drivers/misc/lkdtm_mpxk.h      |  11 ++++
 drivers/misc/lkdtm_mpxk_base.c |  65 +++++++++++++++++++++++
 6 files changed, 211 insertions(+)
 create mode 100644 drivers/misc/lkdtm_mpxk.c
 create mode 100644 drivers/misc/lkdtm_mpxk.h
 create mode 100644 drivers/misc/lkdtm_mpxk_base.c

diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 81ef3e67acc9..58d9ba43e081 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -64,6 +64,13 @@ lkdtm-$(CONFIG_LKDTM)		+= lkdtm_usercopy.o
 
 KCOV_INSTRUMENT_lkdtm_rodata.o	:= n
 
+ifdef CONFIG_X86_INTEL_MPX_KERNEL
+	lkdtm-$(CONFIG_LKDTM)		+= lkdtm_mpxk.o
+	lkdtm-$(CONFIG_LKDTM)		+= lkdtm_mpxk_base.o
+	CFLAGS_lkdtm_mpxk.o		+= $(MPXK_CFLAGS)
+	CFLAGS_lkdtm_mpxk_base.o	+= $(MPXK_CFLAGS)
+endif
+
 OBJCOPYFLAGS :=
 OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \
 			--set-section-flags .text=alloc,readonly \
diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h
index 3b4976396ec4..46cecd01db92 100644
--- a/drivers/misc/lkdtm.h
+++ b/drivers/misc/lkdtm.h
@@ -29,6 +29,13 @@ void lkdtm_CORRUPT_LIST_ADD(void);
 void lkdtm_CORRUPT_LIST_DEL(void);
 void lkdtm_CORRUPT_USER_DS(void);
 
+#ifdef CONFIG_X86_INTEL_MPX_KERNEL
+void lkdtm_MPXK_LOAD_BOUNDS(void);
+void lkdtm_MPXK_FUNCTION_ARGS(void);
+void lkdtm_MPXK_KMALLOC(void);
+void lkdtm_MPXK_MEMCPY(void);
+#endif
+
 /* lkdtm_heap.c */
 void lkdtm_OVERWRITE_ALLOCATION(void);
 void lkdtm_WRITE_AFTER_FREE(void);
diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c
index 42d2b8e31e6b..74e258ddc5fe 100644
--- a/drivers/misc/lkdtm_core.c
+++ b/drivers/misc/lkdtm_core.c
@@ -235,6 +235,12 @@ struct crashtype crashtypes[] = {
 	CRASHTYPE(USERCOPY_STACK_FRAME_FROM),
 	CRASHTYPE(USERCOPY_STACK_BEYOND),
 	CRASHTYPE(USERCOPY_KERNEL),
+#ifdef CONFIG_X86_INTEL_MPX_KERNEL
+	CRASHTYPE(MPXK_LOAD_BOUNDS),
+	CRASHTYPE(MPXK_FUNCTION_ARGS),
+	CRASHTYPE(MPXK_KMALLOC),
+	CRASHTYPE(MPXK_MEMCPY)
+#endif
 };
 
 
diff --git a/drivers/misc/lkdtm_mpxk.c b/drivers/misc/lkdtm_mpxk.c
new file mode 100644
index 000000000000..b957d3641378
--- /dev/null
+++ b/drivers/misc/lkdtm_mpxk.c
@@ -0,0 +1,115 @@
+#undef pr_fmt
+#include "lkdtm_mpxk.h"
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/printk.h>
+#include <asm/mpxk.h>
+
+/** lkdtm_MPXK_LOAD_BOUNDS - test mpxk_bound_load
+ *
+ * Tests mpxk_load_bounds function by passing pointers into function via an
+ * array. The bounds for the array itself are passed via the bnd0 register, but
+ * MPX cannot do that for the internal pointers, hence it uses BNDSTX+BNDLDX.
+ * MPXK therefore must use mpxk_load_bounds to retrieve the bounds inside the
+ * called function.
+ */
+void lkdtm_MPXK_LOAD_BOUNDS(void)
+{
+	int i;
+	char *arr[10];
+
+	for (i = 0; i < 10; i++)
+		arr[i] = kmalloc(16, GFP_KERNEL);
+
+	pr_info("attempting good ptr write\n");
+	mpxk_write_arr_i(arr, 2, 0);
+
+	/* This could succeed because mpxk_load_bounds retrieved the size based
+	 * on the pointer value via ksize, which in turn doesn't necessarily
+	 * return the exact size that was passed into kmalloc. The size is none
+	 * the less guaranteed to be "safe" in that it will not be reserved
+	 * elsewhere.
+	 */
+	pr_info("attempting exact (+1) bad ptr write (can succeed)");
+	mpxk_write_arr_i(arr, 4, 16);
+
+	pr_info("attempting real bad ptr write (should be caught)\n");
+	mpxk_write_arr_i(arr, 5, 1024);
+
+	for (i = 0; i < 10; i++)
+		kfree(arr[i]);
+}
+
+/** lkdtm_MPXK_FUNCTION_ARGS - test function argument bound propagation
+ *
+ * Note that the four first pointers will have their bounds passed into the
+ * function via the bnd0-bnd3 registers. The rest are in vanilla MPX passed in
+ * via BNDSTX+BNDLDX, but in the case of MPXK they are simply loaded inside the
+ * called function using mpxk_load_bounds.
+ */
+void lkdtm_MPXK_FUNCTION_ARGS(void)
+{
+	int i;
+	char *arr[10];
+
+	for (i = 0; i < 10; i++)
+		arr[i] = kmalloc(16, GFP_KERNEL);
+
+	pr_info("attempting good ptr write\n");
+	mpxk_write_10_i(8, 0,
+			arr[0], arr[1], arr[2], arr[3], arr[4],
+			arr[5], arr[6], arr[7], arr[8], arr[9]);
+
+	pr_info("attempting exact bad ptr write\n");
+	mpxk_write_10_i(9, 2,
+			arr[0], arr[1], arr[2], arr[3], arr[4],
+			arr[5], arr[6], arr[7], arr[8], arr[9]);
+
+	pr_info("attempting real bad ptr write\n");
+	mpxk_write_10_i(7, 1024,
+			arr[0], arr[1], arr[2], arr[3], arr[4],
+			arr[5], arr[6], arr[7], arr[8], arr[9]);
+
+	for (i = 0; i < 10; i++)
+		kfree(arr[i]);
+}
+
+/** lkdtm_MPXK_KMALLOC
+ *
+ * Make suer kmalloc is properly instrumented, i.e. it returns proper pointer
+ * bounds on allocation.
+ */
+void lkdtm_MPXK_KMALLOC(void)
+{
+	void *ptr = kmalloc(10, GFP_KERNEL);
+
+	pr_info("attempting good write\n");
+	try_write(ptr, 1);
+
+	pr_info("attempting bad write\n");
+	try_write(ptr, 11);
+
+	kfree(ptr);
+}
+
+
+/** lkdtm_MPXK_MEMCPY - test memcpy instrumentation
+ *
+ * Test memcpy instrumentation, which should check that both target and source
+ * are within bounds (this exercises only destination bounds).
+ */
+void lkdtm_MPXK_MEMCPY(void)
+{
+	char *s  = "123456789";
+	char *s_big = "12345678901234567890123456789012";
+	char *d = kmalloc(4 * sizeof(char), GFP_KERNEL);
+
+	pr_info("performing okay memcpy\n");
+	memcpy(d, s, 1);
+
+	/* The source is okay, but target is too small. */
+	pr_info("performing bad memcpy\n");
+	memcpy(d, s_big, 32 * sizeof(char));
+}
diff --git a/drivers/misc/lkdtm_mpxk.h b/drivers/misc/lkdtm_mpxk.h
new file mode 100644
index 000000000000..197bdca2c10c
--- /dev/null
+++ b/drivers/misc/lkdtm_mpxk.h
@@ -0,0 +1,11 @@
+#undef pr_fmt
+#include "lkdtm.h"
+#include <asm/mpxk.h>
+
+/* #define SOFT_TEST */
+
+void try_write(void *ptr, int i);
+void mpxk_write_arr_i(char **arr, int i, int j);
+noinline void mpxk_write_10_i(int i, int j,
+		void *s0, void *s1, void *s2, void *s3, void *s4,
+		void *s5, void *s6, void *s7, void *s8, void *s9);
diff --git a/drivers/misc/lkdtm_mpxk_base.c b/drivers/misc/lkdtm_mpxk_base.c
new file mode 100644
index 000000000000..938aa5c78211
--- /dev/null
+++ b/drivers/misc/lkdtm_mpxk_base.c
@@ -0,0 +1,65 @@
+#undef pr_fmt
+#include "lkdtm_mpxk.h"
+#include <linux/printk.h>
+
+/**
+ * try_write - Attempt write at pointed memory
+ *
+ * On bad writes this will either cause a bound violation, or
+ * when SOFT_TEST is set pritn out "fail".
+ */
+noinline void try_write(void *ptr, int i)
+{
+#ifdef SOFT_TEST
+	const void *ubound = __bnd_get_ptr_ubound(ptr);
+	const void *lbound = __bnd_get_ptr_lbound(ptr);
+
+	if (ptr < lbound || (ptr+i) > ubound)
+		pr_info("fail\n");
+	else
+		pr_info("ok\n");
+#else
+	((char *)ptr)[i] = '\0';
+#endif /* SOFT_TEST */
+}
+
+/**
+ * mpxk_write_arr_i - Test function that writes to array.
+ *
+ * The boudns for the inner array cannot be passed in via stack/registers and
+ * are therefore loaded with mpxk_load_bounds (and would have been passed in
+ * vanilla MPX with BNDSTX+BNDLDX).
+ */
+noinline void mpxk_write_arr_i(char **arr, int i, int j)
+{
+	try_write(arr[i], j);
+}
+
+
+/**
+ * mpxk_write_10_i - Test function that writes to function arg strings.
+ *
+ * Because bounds cannot be passed beyond the sixth argument (or the fourth
+ * bound) this forces MPXK to use mpxk_load_bounds for the latter pointers.
+ */
+noinline void mpxk_write_10_i(int i, int j,
+		void *s0, void *s1, void *s2, void *s3, void *s4,
+		void *s5, void *s6, void *s7, void *s8, void *s9)
+{
+
+#define mpxk_func_case(x) do {		\
+	if (i == x)			\
+		try_write(s##x, j);	\
+} while (0)
+	mpxk_func_case(0);
+	mpxk_func_case(1);
+	mpxk_func_case(2);
+	mpxk_func_case(3);
+	mpxk_func_case(4);
+	mpxk_func_case(5);
+	mpxk_func_case(6);
+	mpxk_func_case(7);
+	mpxk_func_case(8);
+	mpxk_func_case(9);
+#undef mpxk_func_case
+}
-- 
2.11.0

Powered by blists - more mailing lists

Your e-mail address:

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.