From patchwork Fri Oct 28 19:48:19 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Reshetova, Elena" X-Patchwork-Id: 9402619 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 F1EA660588 for ; Fri, 28 Oct 2016 19:48:42 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DF7012A914 for ; Fri, 28 Oct 2016 19:48:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D1DDE2A918; Fri, 28 Oct 2016 19:48:42 +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.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED 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 EE7722A914 for ; Fri, 28 Oct 2016 19:48:40 +0000 (UTC) Received: (qmail 32397 invoked by uid 550); 28 Oct 2016 19:48:38 -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: Reply-To: kernel-hardening@lists.openwall.com Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 32353 invoked from network); 28 Oct 2016 19:48:36 -0000 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.31,411,1473145200"; d="scan'208";a="25285404" From: "Reshetova, Elena" To: Kees Cook CC: Hans Liljestrand , AKASHI Takahiro , David Windsor , "kernel-hardening@lists.openwall.com" Thread-Topic: [RFC v2 PATCH 13.2/13] lkdtm: add tests for atomic over-/underflow Thread-Index: AQHSMUnKIBGaL7PQo0eiZHCG0Ftwq6C+RSRg Date: Fri, 28 Oct 2016 19:48:19 +0000 Message-ID: <2236FBA76BA1254E88B949DDB74E612B41BF9D0A@IRSMSX102.ger.corp.intel.com> References: <20161028183321.GA41484@beast> In-Reply-To: <20161028183321.GA41484@beast> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [163.33.239.181] MIME-Version: 1.0 Subject: [kernel-hardening] RE: [RFC v2 PATCH 13.2/13] lkdtm: add tests for atomic over-/underflow X-Virus-Scanned: ClamAV using ClamSMTP Thank you Kees! Hans actually did the remaining tests today and we even run them already and found an issue, which we fixed. So they are certainly useful :) But he didn't have the debugfs part. I will take it from here! I will compile the fresh rfc tomorrow morning my time and send it out. I think we have now all needed parts in place. Best Regards, Elena. -----Original Message----- From: Kees Cook [mailto:keescook@chromium.org] Sent: Friday, October 28, 2016 9:33 PM To: Reshetova, Elena Cc: Hans Liljestrand ; AKASHI Takahiro ; David Windsor ; kernel-hardening@lists.openwall.com Subject: [RFC v2 PATCH 13.2/13] lkdtm: add tests for atomic over-/underflow This adds additional tests for the remaining atomic functions. Since the bulk of the logic is identical, the functions are generated with macros. Also adds debugfs entries for the manipulated atomic values, so that the saturation values can be validated after a test is executed. Based on work by Hans Liljestrand and AKASHI Takahiro. Signed-off-by: Kees Cook --- Here's an updated version that does everything with macros and adds Takahiro's debugfs idea. --- drivers/misc/lkdtm.h | 44 ++++++++++++++-- drivers/misc/lkdtm_bugs.c | 128 +++++++++++++++++++++++++++++++++++----------- drivers/misc/lkdtm_core.c | 20 +++++--- 3 files changed, 152 insertions(+), 40 deletions(-) CRASHTYPE(WRITE_RO), CRASHTYPE(WRITE_RO_AFTER_INIT), CRASHTYPE(WRITE_KERN), - CRASHTYPE(ATOMIC_UNDERFLOW), - CRASHTYPE(ATOMIC_OVERFLOW), CRASHTYPE(USERCOPY_HEAP_SIZE_TO), CRASHTYPE(USERCOPY_HEAP_SIZE_FROM), CRASHTYPE(USERCOPY_HEAP_FLAG_TO), @@ -228,6 +233,7 @@ struct crashtype crashtypes[] = { CRASHTYPE(USERCOPY_STACK_FRAME_FROM), CRASHTYPE(USERCOPY_STACK_BEYOND), CRASHTYPE(USERCOPY_KERNEL), + LKDTM_ATOMIC_OPERATIONS(CRASHTYPE_ATOMICS) }; @@ -481,11 +487,6 @@ static int __init lkdtm_module_init(void) crash_count = cpoint_count; #endif - /* Handle test-specific initialization. */ - lkdtm_bugs_init(&recur_count); - lkdtm_perms_init(); - lkdtm_usercopy_init(); - /* Register debugfs interface */ lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL); if (!lkdtm_debugfs_root) { @@ -493,6 +494,11 @@ static int __init lkdtm_module_init(void) return -ENODEV; } + /* Handle test-specific initialization. */ + lkdtm_bugs_init(&recur_count, lkdtm_debugfs_root); + lkdtm_perms_init(); + lkdtm_usercopy_init(); + /* Install debugfs trigger files. */ for (i = 0; i < ARRAY_SIZE(crashpoints); i++) { struct crashpoint *cur = &crashpoints[i]; -- 2.7.4 diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h index fdf954c2107f..4eba6135394c 100644 --- a/drivers/misc/lkdtm.h +++ b/drivers/misc/lkdtm.h @@ -3,10 +3,49 @@ #define pr_fmt(fmt) "lkdtm: " fmt +#include #include +/* + * Handle the various atomic function prototypes (potentially ignoring + * return values). + */ +#define ATOMIC_ARG_X(func, x) func(x) +#define ATOMIC_ARG_1_X(func, x) func(1, x) +#define ATOMIC_RET_ARG_X(func, x) if (func(x)) ; +#define ATOMIC_RET_ARG_1_X(func, x) if (func(1, x)) ; +#define ATOMIC_RET_ARG_X_1_0(func, x) if (func(x, 1, 0)) ; + +/* The list of all tested atomic operations. */ +#define LKDTM_ATOMIC_OPERATIONS(macro) \ + macro(UNDERFLOW, dec, ATOMIC_ARG_X) \ + macro(UNDERFLOW, dec_return, ATOMIC_RET_ARG_X) \ + macro(UNDERFLOW, sub, ATOMIC_ARG_1_X) \ + macro(UNDERFLOW, sub_return, ATOMIC_RET_ARG_1_X) \ + macro(UNDERFLOW, sub_and_test, ATOMIC_RET_ARG_1_X) \ + macro(OVERFLOW, inc, ATOMIC_ARG_X) \ + macro(OVERFLOW, inc_return, ATOMIC_RET_ARG_X) \ + macro(OVERFLOW, add, ATOMIC_ARG_1_X) \ + macro(OVERFLOW, add_return, ATOMIC_RET_ARG_1_X) \ + macro(OVERFLOW, add_unless, ATOMIC_RET_ARG_X_1_0) \ + macro(OVERFLOW, inc_and_test, ATOMIC_RET_ARG_X) + +/* The list of all tested atomic types. */ +#define LKDTM_ATOMIC_TYPES(macro, name, operation) \ + macro(name, atomic, operation) \ + macro(name, atomic_long, operation) \ + macro(name, local, operation) \ + macro(name, atomic64, operation) + +/* Generate function prototypes for all atomic operation/type combinations. */ +#define LKDTM_ATOMIC_FUNCDECL(name, atomic_type, operation) \ + void lkdtm_##name##_##atomic_type##_##operation(void); + +#define LKDTM_ATOMIC_FUNCDECLS(name, operation, ignored) \ + LKDTM_ATOMIC_TYPES(LKDTM_ATOMIC_FUNCDECL, name, operation) + /* lkdtm_bugs.c */ -void __init lkdtm_bugs_init(int *recur_param); +void __init lkdtm_bugs_init(int *recur_param, struct dentry *parent); void lkdtm_PANIC(void); void lkdtm_BUG(void); void lkdtm_WARNING(void); @@ -19,8 +58,7 @@ void lkdtm_SOFTLOCKUP(void); void lkdtm_HARDLOCKUP(void); void lkdtm_SPINLOCKUP(void); void lkdtm_HUNG_TASK(void); -void lkdtm_ATOMIC_UNDERFLOW(void); -void lkdtm_ATOMIC_OVERFLOW(void); +LKDTM_ATOMIC_OPERATIONS(LKDTM_ATOMIC_FUNCDECLS); /* lkdtm_heap.c */ void lkdtm_OVERWRITE_ALLOCATION(void); diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index 182ae1894b32..39cbd0effcf2 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c @@ -5,7 +5,9 @@ * test source files. */ #include "lkdtm.h" +#include #include +#include /* * Make sure our attempts to over run the kernel stack doesn't trigger @@ -35,15 +37,6 @@ static int recursive_loop(int remaining) return recursive_loop(remaining - 1); } -/* If the depth is negative, use the default, otherwise keep parameter. */ -void __init lkdtm_bugs_init(int *recur_param) -{ - if (*recur_param < 0) - *recur_param = recur_count; - else - recur_count = *recur_param; -} - void lkdtm_PANIC(void) { panic("dumptest"); @@ -123,26 +116,101 @@ void lkdtm_HUNG_TASK(void) schedule(); } -void lkdtm_ATOMIC_UNDERFLOW(void) -{ - atomic_t under = ATOMIC_INIT(INT_MIN); - - pr_info("attempting good atomic increment\n"); - atomic_inc(&under); - atomic_dec(&under); - - pr_info("attempting bad atomic underflow\n"); - atomic_dec(&under); -} - -void lkdtm_ATOMIC_OVERFLOW(void) -{ - atomic_t over = ATOMIC_INIT(INT_MAX); - - pr_info("attempting good atomic decrement\n"); - atomic_dec(&over); - atomic_inc(&over); +#define GENERATE_LKDTM_ATOMIC_TYPE(ignore1, atomic_name, ignore2) \ + \ +static atomic_name##_t atomic_name##_var; \ + \ +static int debugfs_##atomic_name##_get(void *data, u64 *val) \ +{ \ + *val = atomic_name##_read((atomic_name##_t *)data); \ + return 0; \ +} \ + \ +DEFINE_DEBUGFS_ATTRIBUTE(fops_##atomic_name, \ + debugfs_##atomic_name##_get, \ + NULL, "%lld\n"); \ + \ +static struct dentry *debugfs_create_##atomic_name(const char *item, \ + umode_t mode, \ + struct dentry *parent, \ + atomic_name##_t *value) \ +{ \ + return debugfs_create_file_unsafe(item, mode, parent, value, \ + &fops_##atomic_name); \ +} + +LKDTM_ATOMIC_TYPES(GENERATE_LKDTM_ATOMIC_TYPE, 0, 0) + +#define ATOMIC_TEST(name, atomic_name, init_func, start, safe_func, \ + test_func_proto, testfunc) \ + \ +void lkdtm_##name##_##testfunc(void) \ +{ \ + atomic_name##_set(&atomic_name##_var, start); \ + \ + pr_info("attempting good " #testfunc "\n"); \ + safe_func(&atomic_name##_var); \ + test_func_proto(testfunc, &atomic_name##_var); \ + \ + pr_info("attempting bad " #testfunc "\n"); \ + test_func_proto(testfunc, &atomic_name##_var); \ +} + +/* Declare underflow test functions for atomic_t and atomic_long_t types. */ +#define LKDTM_ATOMIC_UNDERFLOW(operation, test_func_proto) \ + ATOMIC_TEST(UNDERFLOW, atomic, ATOMIC_INIT, INT_MIN, \ + atomic_inc, test_func_proto, atomic_##operation) \ + ATOMIC_TEST(UNDERFLOW, atomic_long, ATOMIC_LONG_INIT, \ + LONG_MIN, atomic_long_inc, test_func_proto, \ + atomic_long_##operation) \ + ATOMIC_TEST(UNDERFLOW, atomic64, ATOMIC64_INIT, \ + S64_MIN, atomic64_inc, test_func_proto, \ + atomic64_##operation) \ + ATOMIC_TEST(UNDERFLOW, local, LOCAL_INIT, \ + LONG_MIN, local_inc, test_func_proto, \ + local_##operation) + +/* Declare overflow test functions for atomic_t and atomic_long_t types. */ +#define LKDTM_ATOMIC_OVERFLOW(operation, test_func_proto) \ + ATOMIC_TEST(OVERFLOW, atomic, ATOMIC_INIT, INT_MAX, \ + atomic_dec, test_func_proto, atomic_##operation) \ + ATOMIC_TEST(OVERFLOW, atomic_long, ATOMIC_LONG_INIT, \ + LONG_MAX, atomic_long_dec, test_func_proto, \ + atomic_long_##operation) \ + ATOMIC_TEST(OVERFLOW, atomic64, ATOMIC64_INIT, \ + S64_MAX, atomic64_dec, test_func_proto, \ + atomic64_##operation) \ + ATOMIC_TEST(OVERFLOW, local, LOCAL_INIT, \ + LONG_MAX, local_dec, test_func_proto, \ + local_##operation) + +#define GENERATE_LKDTM_ATOMIC_TEST(name, operation, test_func_proto) \ + LKDTM_ATOMIC_##name(operation, test_func_proto) + +LKDTM_ATOMIC_OPERATIONS(GENERATE_LKDTM_ATOMIC_TEST) + +#define GENERATE_LKDTM_ATOMIC_DEBUGFS(ignore1, atomic_name, ignore2) \ + de = debugfs_create_##atomic_name(#atomic_name, 0644, \ + atomic_dir, \ + &atomic_name##_var); \ + if (de == NULL) \ + pr_err("could not create " #atomic_name " in debugfs\n"); + +void __init lkdtm_bugs_init(int *recur_param, struct dentry *parent) { + struct dentry *atomic_dir, *de; + + /* If the depth is negative, use default, otherwise keep parameter. */ + if (*recur_param < 0) + *recur_param = recur_count; + else + recur_count = *recur_param; - pr_info("attempting bad atomic overflow\n"); - atomic_inc(&over); + /* Don't treat failures to create the atomic value tree as fatal. */ + atomic_dir = debugfs_create_dir("atomic", parent); + if (atomic_dir) { + LKDTM_ATOMIC_TYPES(GENERATE_LKDTM_ATOMIC_DEBUGFS, 0, 0); + } else { + pr_err("creating atomic dir failed\n"); + } } diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c index f9154b8d67f6..defa1af01e52 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm_core.c @@ -189,6 +189,13 @@ struct crashtype { .func = lkdtm_ ## _name, \ } +/* Generate crashtype entries for all atomic operation/type combinations. */ +#define CRASHTYPE_ATOMIC(name, atomic_name, operation) \ + CRASHTYPE(name##_##atomic_name##_##operation), \ + +#define CRASHTYPE_ATOMICS(name, operation, ignored) \ + LKDTM_ATOMIC_TYPES(CRASHTYPE_ATOMIC, name, operation) + /* Define the possible types of crashes that can be triggered. */ struct crashtype crashtypes[] = { CRASHTYPE(PANIC), @@ -218,8 +225,6 @@ struct crashtype crashtypes[] = {