From patchwork Thu Nov 10 20:24:48 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Reshetova, Elena" X-Patchwork-Id: 9422065 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 9B7C5601C0 for ; Thu, 10 Nov 2016 20:28:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9497D29845 for ; Thu, 10 Nov 2016 20:28:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 861F629844; Thu, 10 Nov 2016 20:28:46 +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 3142529844 for ; Thu, 10 Nov 2016 20:28:45 +0000 (UTC) Received: (qmail 7854 invoked by uid 550); 10 Nov 2016 20:26:14 -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 7805 invoked from network); 10 Nov 2016 20:26:13 -0000 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.31,619,1473145200"; d="scan'208";a="29871911" From: Elena Reshetova To: kernel-hardening@lists.openwall.com Cc: keescook@chromium.org, arnd@arndb.de, tglx@linutronix.de, mingo@redhat.com, h.peter.anvin@intel.com, peterz@infradead.org, will.deacon@arm.com, Elena Reshetova , Hans Liljestrand Date: Thu, 10 Nov 2016 22:24:48 +0200 Message-Id: <1478809488-18303-14-git-send-email-elena.reshetova@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1478809488-18303-1-git-send-email-elena.reshetova@intel.com> References: <1478809488-18303-1-git-send-email-elena.reshetova@intel.com> Subject: [kernel-hardening] [RFC v4 PATCH 13/13] lkdtm: add tests for atomic over-/underflow X-Virus-Scanned: ClamAV using ClamSMTP 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 Signed-off-by: Hans Liljestrand --- drivers/misc/lkdtm.h | 46 +++++++++++++++-- drivers/misc/lkdtm_bugs.c | 127 +++++++++++++++++++++++++++++++++++----------- drivers/misc/lkdtm_core.c | 20 +++++--- 3 files changed, 153 insertions(+), 40 deletions(-) diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h index fdf954c..ce73dcc 100644 --- a/drivers/misc/lkdtm.h +++ b/drivers/misc/lkdtm.h @@ -4,9 +4,50 @@ #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, dec_and_test, 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, inc_and_test, 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, add_negative, ATOMIC_RET_ARG_1_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 +60,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 182ae18..b1da9c5 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,100 @@ 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 f9154b8..defa1af 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[] = { 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];