From patchwork Mon Jul 24 13:38:24 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans Liljestrand X-Patchwork-Id: 9860091 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 805C2601A1 for ; Mon, 24 Jul 2017 18:57:31 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7340B284B2 for ; Mon, 24 Jul 2017 18:57:31 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 680142852B; Mon, 24 Jul 2017 18:57:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id 1D1D4284B2 for ; Mon, 24 Jul 2017 18:57:29 +0000 (UTC) Received: (qmail 19641 invoked by uid 550); 24 Jul 2017 18:57:24 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Delivered-To: moderator for kernel-hardening@lists.openwall.com Received: (qmail 29752 invoked from network); 24 Jul 2017 13:39:58 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=kgiP5pAquxlTTIlHFthHDPPSauZXspxx0Oa26eKz3jo=; b=X1g38y7Vrn8fjv1wJLXv9ErMhkWid4Wd8RzbFiUGa3Z5tkwgE+T/MmKRzIWnZhQ7Lz bpE0yLc+r75ucwr14f2Dw1+m4myxjPyxYQEiIff9yvj1crJHjbdQ1KwAToUUKVY+N6pf 5P2qbe9EQ1yDPuQk7Hp53RmMEVZ8tojWbrPVz7YriUt2lvCMs6tADGYCyh+RI5KSpFvH Vjud4UgAM0I12aiuzRvaOUGv5G3CvjvPXlAckcVkXk0rMZ2FXf75wcx4wLrweqOnHYAh YxjaixSGw4JChxQfTPOaXIdR5Zvb31S2sXzijZ9iFcbmbnV7I6vfXigoSEotAi93K0L9 PKBg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=kgiP5pAquxlTTIlHFthHDPPSauZXspxx0Oa26eKz3jo=; b=e8omsUvG0wh+TebEuY7z6IF7Q4YcM4lAPPvohZ2Av+6uMWd2dobQfeG/IrlDJSXNhf ZebStSb438xEDpA6IW9bAr9Wsht/K8frb6iIR3/7a1+SFh3MHPqcpJyOjaZqFmUbo72f LIP+KkUGiZm0DUZcT1ptqvtPee9A52cyVtUfwLvan451KtkLknYpD0+ySim1+KcY/PcA jft0Fr+jkiyIn6NLtkntBLxv4vQzVwQHB7Rldxp8Mm6A9FALrGn+oSEaA58VG/uwkk6N Ljdzdc1szdw/eirWHiqZpFYhFYa4V49bl9kRpP31LS0abTgtrEgLLjpfj4mxfc9WgH2b qmww== X-Gm-Message-State: AIVw111SfTuiLAufKzh8iPC5bc0++lbFAZYFQy5rJ3HTU4x/bdQat642 Zln4OWfAu+TPIlcNZahxVA== X-Received: by 10.25.145.65 with SMTP id y1mr4441291lfj.233.1500903586364; Mon, 24 Jul 2017 06:39:46 -0700 (PDT) Sender: Hans Liljestrand From: Hans Liljestrand X-Google-Original-From: Hans Liljestrand To: kernel-hardening@lists.openwall.com Cc: elena.reshetova@intel.com, dave.hansen@intel.com, keescook@chromium.org, hpa@zytor.com, Hans Liljestrand Date: Mon, 24 Jul 2017 16:38:24 +0300 Message-Id: <20170724133824.27223-6-LiljestrandH@gmail.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170724133824.27223-1-LiljestrandH@gmail.com> References: <20170724133824.27223-1-LiljestrandH@gmail.com> Subject: [kernel-hardening] [RFC PATCH 5/5] lkdtm: Add kernel MPX testing X-Virus-Scanned: ClamAV using ClamSMTP Tests MPXK functionality via lkdtm test cases. Currently tests basic bound propagation and instrumentation for memcpy and kmalloc. Signed-off-by: Hans Liljestrand Signed-off-by: Elena Reshetova --- 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 +#include +#include +#include +#include +#include + +/** 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 + +/* #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 + +/** + * 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 +}