This is a bunch of LKDTM clean-ups to improve and expand testing, given feedback from testing at kernelci.org. Adds a few new tests as well.
(If a pull-request is preferred, please let me know.)
Thanks!
-Kees
Kees Cook (9): selftests/lkdtm: Avoid needing explicit sub-shell selftests/lkdtm: Fix expected text for CR4 pinning selftests/lkdtm: Fix expected text for free poison lkdtm/bugs: XFAIL UNALIGNED_LOAD_STORE_WRITE lkdtm/heap: Add vmalloc linear overflow test lkdtm: Enable DOUBLE_FAULT on all architectures lkdtm: Add CONFIG hints in errors where possible selftests/lkdtm: Enable various testable CONFIGs lkdtm/heap: Add init_on_alloc tests
drivers/misc/lkdtm/bugs.c | 11 ++- drivers/misc/lkdtm/cfi.c | 3 +- drivers/misc/lkdtm/core.c | 58 ++++++++++- drivers/misc/lkdtm/fortify.c | 3 +- drivers/misc/lkdtm/heap.c | 97 ++++++++++++++++++- drivers/misc/lkdtm/lkdtm.h | 46 ++++++++- drivers/misc/lkdtm/stackleak.c | 4 +- drivers/misc/lkdtm/usercopy.c | 7 +- tools/testing/selftests/lkdtm/config | 7 ++ tools/testing/selftests/lkdtm/run.sh | 12 ++- .../testing/selftests/lkdtm/stack-entropy.sh | 1 + tools/testing/selftests/lkdtm/tests.txt | 11 ++- 12 files changed, 236 insertions(+), 24 deletions(-)
Some environments do not set $SHELL when running tests. There's no need to use $SHELL here anyway, since "cat" can be used to receive any delivered signals from the kernel. Additionally avoid using bash-isms in the command, and record stderr for posterity.
Suggested-by: Guillaume Tucker guillaume.tucker@collabora.com Suggested-by: David Laight David.Laight@ACULAB.COM Fixes: 46d1a0f03d66 ("selftests/lkdtm: Add tests for LKDTM targets") Cc: stable@vger.kernel.org Signed-off-by: Kees Cook keescook@chromium.org --- tools/testing/selftests/lkdtm/run.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/lkdtm/run.sh b/tools/testing/selftests/lkdtm/run.sh index bb7a1775307b..e95e79bd3126 100755 --- a/tools/testing/selftests/lkdtm/run.sh +++ b/tools/testing/selftests/lkdtm/run.sh @@ -76,10 +76,14 @@ fi # Save existing dmesg so we can detect new content below dmesg > "$DMESG"
-# Most shells yell about signals and we're expecting the "cat" process -# to usually be killed by the kernel. So we have to run it in a sub-shell -# and silence errors. -($SHELL -c 'cat <(echo '"$test"') >'"$TRIGGER" 2>/dev/null) || true +# Since the kernel is likely killing the process writing to the trigger +# file, it must not be the script's shell itself. i.e. we cannot do: +# echo "$test" >"$TRIGGER" +# Instead, use "cat" to take the signal. Since the shell will yell about +# the signal that killed the subprocess, we must ignore the failure and +# continue. However we don't silence stderr since there might be other +# useful details reported there in the case of other unexpected conditions. +echo "$test" | cat >"$TRIGGER" || true
# Record and dump the results dmesg | comm --nocheck-order -13 "$DMESG" - > "$LOG" || true
The error text for CR4 pinning changed. Update the test to match.
Fixes: a13b9d0b9721 ("x86/cpu: Use pinning mask for CR4 bits needing to be 0") Cc: stable@vger.kernel.org Signed-off-by: Kees Cook keescook@chromium.org --- tools/testing/selftests/lkdtm/tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index 11ef159be0fd..a5fce7fd4520 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -11,7 +11,7 @@ CORRUPT_LIST_ADD list_add corruption CORRUPT_LIST_DEL list_del corruption STACK_GUARD_PAGE_LEADING STACK_GUARD_PAGE_TRAILING -UNSET_SMEP CR4 bits went missing +UNSET_SMEP pinned CR4 bits changed: DOUBLE_FAULT CORRUPT_PAC UNALIGNED_LOAD_STORE_WRITE
Freed memory poisoning can be tested a few ways, so update the expected text to reflect the non-Oopsing alternative.
Signed-off-by: Kees Cook keescook@chromium.org --- tools/testing/selftests/lkdtm/tests.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index a5fce7fd4520..a94d4d8eeb5c 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -17,9 +17,9 @@ CORRUPT_PAC UNALIGNED_LOAD_STORE_WRITE #OVERWRITE_ALLOCATION Corrupts memory on failure #WRITE_AFTER_FREE Corrupts memory on failure -READ_AFTER_FREE +READ_AFTER_FREE call trace:|Memory correctly poisoned #WRITE_BUDDY_AFTER_FREE Corrupts memory on failure -READ_BUDDY_AFTER_FREE +READ_BUDDY_AFTER_FREE call trace:|Memory correctly poisoned SLAB_FREE_DOUBLE SLAB_FREE_CROSS SLAB_FREE_PAGE
When built under CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS, this test is expected to fail (i.e. not trip an exception).
Fixes: 46d1a0f03d66 ("selftests/lkdtm: Add tests for LKDTM targets") Cc: stable@vger.kernel.org Signed-off-by: Kees Cook keescook@chromium.org --- drivers/misc/lkdtm/bugs.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index 0e8254d0cf0b..9ff02bdf3153 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -161,6 +161,9 @@ void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) if (*p == 0) val = 0x87654321; *p = val; + + if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) + pr_err("XFAIL: arch has CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS\n"); }
void lkdtm_SOFTLOCKUP(void)
Similar to the existing slab overflow and stack exhaustion tests, add VMALLOC_LINEAR_OVERFLOW (and rename the slab test SLAB_LINEAR_OVERFLOW). Additionally unmarks the test as destructive. (It should be safe in the face of misbehavior.)
Signed-off-by: Kees Cook keescook@chromium.org --- drivers/misc/lkdtm/core.c | 3 ++- drivers/misc/lkdtm/heap.c | 22 +++++++++++++++++++++- drivers/misc/lkdtm/lkdtm.h | 3 ++- tools/testing/selftests/lkdtm/tests.txt | 3 ++- 4 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index 8024b6a5cc7f..645b31e98c77 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -120,7 +120,8 @@ static const struct crashtype crashtypes[] = { CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), CRASHTYPE(FORTIFY_OBJECT), CRASHTYPE(FORTIFY_SUBOBJECT), - CRASHTYPE(OVERWRITE_ALLOCATION), + CRASHTYPE(SLAB_LINEAR_OVERFLOW), + CRASHTYPE(VMALLOC_LINEAR_OVERFLOW), CRASHTYPE(WRITE_AFTER_FREE), CRASHTYPE(READ_AFTER_FREE), CRASHTYPE(WRITE_BUDDY_AFTER_FREE), diff --git a/drivers/misc/lkdtm/heap.c b/drivers/misc/lkdtm/heap.c index 1323bc16f113..36be5e353cd0 100644 --- a/drivers/misc/lkdtm/heap.c +++ b/drivers/misc/lkdtm/heap.c @@ -5,24 +5,44 @@ */ #include "lkdtm.h" #include <linux/slab.h> +#include <linux/vmalloc.h> #include <linux/sched.h>
static struct kmem_cache *double_free_cache; static struct kmem_cache *a_cache; static struct kmem_cache *b_cache;
+/* + * If there aren't guard pages, it's likely that a consecutive allocation will + * let us overflow into the second allocation without overwriting something real. + */ +void lkdtm_VMALLOC_LINEAR_OVERFLOW(void) +{ + char *one, *two; + + one = vzalloc(PAGE_SIZE); + two = vzalloc(PAGE_SIZE); + + pr_info("Attempting vmalloc linear overflow ...\n"); + memset(one, 0xAA, PAGE_SIZE + 1); + + vfree(two); + vfree(one); +} + /* * This tries to stay within the next largest power-of-2 kmalloc cache * to avoid actually overwriting anything important if it's not detected * correctly. */ -void lkdtm_OVERWRITE_ALLOCATION(void) +void lkdtm_SLAB_LINEAR_OVERFLOW(void) { size_t len = 1020; u32 *data = kmalloc(len, GFP_KERNEL); if (!data) return;
+ pr_info("Attempting slab linear overflow ...\n"); data[1024 / sizeof(u32)] = 0x12345678; kfree(data); } diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index 99f90d3e5e9c..c6baf4f1e1db 100644 --- a/drivers/misc/lkdtm/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h @@ -39,7 +39,8 @@ void lkdtm_FORTIFY_SUBOBJECT(void); /* heap.c */ void __init lkdtm_heap_init(void); void __exit lkdtm_heap_exit(void); -void lkdtm_OVERWRITE_ALLOCATION(void); +void lkdtm_VMALLOC_LINEAR_OVERFLOW(void); +void lkdtm_SLAB_LINEAR_OVERFLOW(void); void lkdtm_WRITE_AFTER_FREE(void); void lkdtm_READ_AFTER_FREE(void); void lkdtm_WRITE_BUDDY_AFTER_FREE(void); diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index a94d4d8eeb5c..30080cc15623 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -15,7 +15,8 @@ UNSET_SMEP pinned CR4 bits changed: DOUBLE_FAULT CORRUPT_PAC UNALIGNED_LOAD_STORE_WRITE -#OVERWRITE_ALLOCATION Corrupts memory on failure +SLAB_LINEAR_OVERFLOW +VMALLOC_LINEAR_OVERFLOW #WRITE_AFTER_FREE Corrupts memory on failure READ_AFTER_FREE call trace:|Memory correctly poisoned #WRITE_BUDDY_AFTER_FREE Corrupts memory on failure
Where feasible, I prefer to have all tests visible on all architectures, but to have them wired to XFAIL. DOUBLE_FAIL was set up to XFAIL, but wasn't actually being added to the test list.
Fixes: cea23efb4de2 ("lkdtm/bugs: Make double-fault test always available") Cc: stable@vger.kernel.org Signed-off-by: Kees Cook keescook@chromium.org --- drivers/misc/lkdtm/core.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index 645b31e98c77..2c89fc18669f 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -178,9 +178,7 @@ static const struct crashtype crashtypes[] = { CRASHTYPE(STACKLEAK_ERASING), CRASHTYPE(CFI_FORWARD_PROTO), CRASHTYPE(FORTIFIED_STRSCPY), -#ifdef CONFIG_X86_32 CRASHTYPE(DOUBLE_FAULT), -#endif #ifdef CONFIG_PPC_BOOK3S_64 CRASHTYPE(PPC_SLB_MULTIHIT), #endif
For various failure conditions, try to include some details about where to look for reasons about the failure.
Signed-off-by: Kees Cook keescook@chromium.org --- drivers/misc/lkdtm/bugs.c | 8 ++- drivers/misc/lkdtm/cfi.c | 3 +- drivers/misc/lkdtm/core.c | 51 +++++++++++++++++++ drivers/misc/lkdtm/fortify.c | 3 +- drivers/misc/lkdtm/heap.c | 10 ++-- drivers/misc/lkdtm/lkdtm.h | 41 +++++++++++++++ drivers/misc/lkdtm/stackleak.c | 4 +- drivers/misc/lkdtm/usercopy.c | 7 ++- .../testing/selftests/lkdtm/stack-entropy.sh | 1 + 9 files changed, 117 insertions(+), 11 deletions(-)
diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index 9ff02bdf3153..7c7335506c45 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -303,8 +303,10 @@ void lkdtm_CORRUPT_LIST_ADD(void)
if (target[0] == NULL && target[1] == NULL) pr_err("Overwrite did not happen, but no BUG?!\n"); - else + else { pr_err("list_add() corruption not detected!\n"); + pr_expected_config(CONFIG_DEBUG_LIST); + } }
void lkdtm_CORRUPT_LIST_DEL(void) @@ -328,8 +330,10 @@ void lkdtm_CORRUPT_LIST_DEL(void)
if (target[0] == NULL && target[1] == NULL) pr_err("Overwrite did not happen, but no BUG?!\n"); - else + else { pr_err("list_del() corruption not detected!\n"); + pr_expected_config(CONFIG_DEBUG_LIST); + } }
/* Test that VMAP_STACK is actually allocating with a leading guard page */ diff --git a/drivers/misc/lkdtm/cfi.c b/drivers/misc/lkdtm/cfi.c index e73ebdbfa806..c9aeddef1044 100644 --- a/drivers/misc/lkdtm/cfi.c +++ b/drivers/misc/lkdtm/cfi.c @@ -38,5 +38,6 @@ void lkdtm_CFI_FORWARD_PROTO(void) func = (void *)lkdtm_increment_int; func(&called_count);
- pr_info("Fail: survived mismatched prototype function call!\n"); + pr_err("FAIL: survived mismatched prototype function call!\n"); + pr_expected_config(CONFIG_CFI_CLANG); } diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index 2c89fc18669f..c185ae4719c3 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -26,6 +26,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/debugfs.h> +#include <linux/init.h>
#define DEFAULT_COUNT 10
@@ -398,6 +399,56 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf, return count; }
+#ifndef MODULE +/* + * To avoid needing to export parse_args(), just don't use this code + * when LKDTM is built as a module. + */ +struct check_cmdline_args { + const char *param; + int value; +}; + +static int lkdtm_parse_one(char *param, char *val, + const char *unused, void *arg) +{ + struct check_cmdline_args *args = arg; + + /* short circuit if we already found a value. */ + if (args->value != -ESRCH) + return 0; + if (strncmp(param, args->param, strlen(args->param)) == 0) { + bool bool_result; + int ret; + + ret = kstrtobool(val, &bool_result); + if (ret == 0) + args->value = bool_result; + } + return 0; +} + +int lkdtm_check_bool_cmdline(const char *param) +{ + char *command_line; + struct check_cmdline_args args = { + .param = param, + .value = -ESRCH, + }; + + command_line = kstrdup(saved_command_line, GFP_KERNEL); + if (!command_line) + return -ENOMEM; + + parse_args("Setting sysctl args", command_line, + NULL, 0, -1, -1, &args, lkdtm_parse_one); + + kfree(command_line); + + return args.value; +} +#endif + static struct dentry *lkdtm_debugfs_root;
static int __init lkdtm_module_init(void) diff --git a/drivers/misc/lkdtm/fortify.c b/drivers/misc/lkdtm/fortify.c index faf29cf04baa..0f51d31b57ca 100644 --- a/drivers/misc/lkdtm/fortify.c +++ b/drivers/misc/lkdtm/fortify.c @@ -76,7 +76,8 @@ void lkdtm_FORTIFIED_STRSCPY(void) */ strscpy(dst, src, strlen(src));
- pr_warn("FAIL: No overflow in above strscpy()\n"); + pr_err("FAIL: strscpy() overflow not detected!\n"); + pr_expected_config(CONFIG_FORTIFY_SOURCE);
kfree(src); } diff --git a/drivers/misc/lkdtm/heap.c b/drivers/misc/lkdtm/heap.c index 36be5e353cd0..a3bb0577ed8b 100644 --- a/drivers/misc/lkdtm/heap.c +++ b/drivers/misc/lkdtm/heap.c @@ -109,9 +109,10 @@ void lkdtm_READ_AFTER_FREE(void) if (saw != *val) { /* Good! Poisoning happened, so declare a win. */ pr_info("Memory correctly poisoned (%x)\n", saw); - BUG(); + } else { + pr_err("FAIL: Memory was not poisoned!\n"); + pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free"); } - pr_info("Memory was not poisoned\n");
kfree(val); } @@ -165,9 +166,10 @@ void lkdtm_READ_BUDDY_AFTER_FREE(void) if (saw != *val) { /* Good! Poisoning happened, so declare a win. */ pr_info("Memory correctly poisoned (%x)\n", saw); - BUG(); + } else { + pr_err("FAIL: Buddy page was not poisoned!\n"); + pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free"); } - pr_info("Buddy page was not poisoned\n");
kfree(val); } diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index c6baf4f1e1db..e491bc571808 100644 --- a/drivers/misc/lkdtm/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h @@ -6,6 +6,47 @@
#include <linux/kernel.h>
+#define pr_expected_config(kconfig) \ +{ \ + if (IS_ENABLED(kconfig)) \ + pr_err("Unexpected! This kernel was built with " #kconfig "=y\n"); \ + else \ + pr_warn("This is probably expected, since this kernel was built *without* " #kconfig "=y\n"); \ +} + +#ifndef MODULE +int lkdtm_check_bool_cmdline(const char *param); +#define pr_expected_config_param(kconfig, param) \ +{ \ + if (IS_ENABLED(kconfig)) { \ + switch (lkdtm_check_bool_cmdline(param)) { \ + case 0: \ + pr_warn("This is probably expected, since this kernel was built with " #kconfig "=y but booted with '" param "=N'\n"); \ + break; \ + case 1: \ + pr_err("Unexpected! This kernel was built with " #kconfig "=y and booted with '" param "=Y'\n"); \ + break; \ + default: \ + pr_err("Unexpected! This kernel was built with " #kconfig "=y (and booted without '" param "' specified)\n"); \ + } \ + } else { \ + switch (lkdtm_check_bool_cmdline(param)) { \ + case 0: \ + pr_warn("This is probably expected, as kernel was built *without* " #kconfig "=y and booted with '" param "=N'\n"); \ + break; \ + case 1: \ + pr_err("Unexpected! This kernel was built *without* " #kconfig "=y but booted with '" param "=Y'\n"); \ + break; \ + default: \ + pr_err("This is probably expected, since this kernel was built *without* " #kconfig "=y (and booted without '" param "' specified)\n"); \ + break; \ + } \ + } \ +} +#else +#define pr_expected_config_param(kconfig, param) pr_expected_config(kconfig) +#endif + /* bugs.c */ void __init lkdtm_bugs_init(int *recur_param); void lkdtm_PANIC(void); diff --git a/drivers/misc/lkdtm/stackleak.c b/drivers/misc/lkdtm/stackleak.c index d1a5c0705be3..00db21ff115e 100644 --- a/drivers/misc/lkdtm/stackleak.c +++ b/drivers/misc/lkdtm/stackleak.c @@ -74,8 +74,8 @@ void lkdtm_STACKLEAK_ERASING(void)
end: if (test_failed) { - pr_err("FAIL: the thread stack is NOT properly erased\n"); - dump_stack(); + pr_err("FAIL: the thread stack is NOT properly erased!\n"); + pr_expected_config(CONFIG_GCC_PLUGIN_STACKLEAK); } else { pr_info("OK: the rest of the thread stack is properly erased\n"); } diff --git a/drivers/misc/lkdtm/usercopy.c b/drivers/misc/lkdtm/usercopy.c index 15d220ef35a5..9161ce7ed47a 100644 --- a/drivers/misc/lkdtm/usercopy.c +++ b/drivers/misc/lkdtm/usercopy.c @@ -173,6 +173,8 @@ static void do_usercopy_heap_size(bool to_user) goto free_user; } } + pr_err("FAIL: bad usercopy not detected!\n"); + pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");
free_user: vm_munmap(user_addr, PAGE_SIZE); @@ -248,6 +250,8 @@ static void do_usercopy_heap_whitelist(bool to_user) goto free_user; } } + pr_err("FAIL: bad usercopy not detected!\n"); + pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");
free_user: vm_munmap(user_alloc, PAGE_SIZE); @@ -319,7 +323,8 @@ void lkdtm_USERCOPY_KERNEL(void) pr_warn("copy_to_user failed, but lacked Oops\n"); goto free_user; } - pr_err("FAIL: survived bad copy_to_user()\n"); + pr_err("FAIL: bad copy_to_user() not detected!\n"); + pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy");
free_user: vm_munmap(user_addr, PAGE_SIZE); diff --git a/tools/testing/selftests/lkdtm/stack-entropy.sh b/tools/testing/selftests/lkdtm/stack-entropy.sh index b1b8a5097cbb..1b4d95d575f8 100755 --- a/tools/testing/selftests/lkdtm/stack-entropy.sh +++ b/tools/testing/selftests/lkdtm/stack-entropy.sh @@ -30,6 +30,7 @@ rm -f "$log"
# We would expect any functional stack randomization to be at least 5 bits. if [ "$bits" -lt 5 ]; then + echo "Stack entropy is low! Booted without 'randomize_kstack_offset=y'?" exit 1 else exit 0
Add a handful of LKDTM-testable features that depend on certain CONFIGs so that they are visible in logs for CI systems that run the selftests. Others could be added, but may be seen as having too high a trade-off for general testing.
Cc: kernelci@groups.io Suggested-by: Guillaume Tucker guillaume.tucker@collabora.com Signed-off-by: Kees Cook keescook@chromium.org --- tools/testing/selftests/lkdtm/config | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/tools/testing/selftests/lkdtm/config b/tools/testing/selftests/lkdtm/config index d874990e442b..849799bcfa95 100644 --- a/tools/testing/selftests/lkdtm/config +++ b/tools/testing/selftests/lkdtm/config @@ -1 +1,7 @@ CONFIG_LKDTM=y +CONFIG_DEBUG_LIST=y +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_FORTIFY_SOURCE=y +CONFIG_HARDENED_USERCOPY=y +# CONFIG_HARDENED_USERCOPY_FALLBACK is not set +CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y
Add SLAB and page allocator tests for init_on_alloc. Testing for init_on_free was already happening via the poisoning tests.
Signed-off-by: Kees Cook keescook@chromium.org --- drivers/misc/lkdtm/core.c | 2 + drivers/misc/lkdtm/heap.c | 65 +++++++++++++++++++++++++ drivers/misc/lkdtm/lkdtm.h | 2 + tools/testing/selftests/lkdtm/config | 1 + tools/testing/selftests/lkdtm/tests.txt | 2 + 5 files changed, 72 insertions(+)
diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index c185ae4719c3..9dda87c6b54a 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -127,6 +127,8 @@ static const struct crashtype crashtypes[] = { CRASHTYPE(READ_AFTER_FREE), CRASHTYPE(WRITE_BUDDY_AFTER_FREE), CRASHTYPE(READ_BUDDY_AFTER_FREE), + CRASHTYPE(SLAB_INIT_ON_ALLOC), + CRASHTYPE(BUDDY_INIT_ON_ALLOC), CRASHTYPE(SLAB_FREE_DOUBLE), CRASHTYPE(SLAB_FREE_CROSS), CRASHTYPE(SLAB_FREE_PAGE), diff --git a/drivers/misc/lkdtm/heap.c b/drivers/misc/lkdtm/heap.c index a3bb0577ed8b..3d9aae5821a0 100644 --- a/drivers/misc/lkdtm/heap.c +++ b/drivers/misc/lkdtm/heap.c @@ -174,6 +174,71 @@ void lkdtm_READ_BUDDY_AFTER_FREE(void) kfree(val); }
+void lkdtm_SLAB_INIT_ON_ALLOC(void) +{ + u8 *first; + u8 *val; + + first = kmalloc(512, GFP_KERNEL); + if (!first) { + pr_info("Unable to allocate 512 bytes the first time.\n"); + return; + } + + memset(first, 0xAB, 512); + kfree(first); + + val = kmalloc(512, GFP_KERNEL); + if (!val) { + pr_info("Unable to allocate 512 bytes the second time.\n"); + return; + } + if (val != first) { + pr_warn("Reallocation missed clobbered memory.\n"); + } + + if (memchr(val, 0xAB, 512) == NULL) { + pr_info("Memory appears initialized (%x, no earlier values)\n", *val); + } else { + pr_err("FAIL: Slab was not initialized\n"); + pr_expected_config_param(CONFIG_INIT_ON_ALLOC_DEFAULT_ON, "init_on_alloc"); + } + kfree(val); +} + +void lkdtm_BUDDY_INIT_ON_ALLOC(void) +{ + u8 *first; + u8 *val; + + first = (u8 *)__get_free_page(GFP_KERNEL); + if (!first) { + pr_info("Unable to allocate first free page\n"); + return; + } + + memset(first, 0xAB, PAGE_SIZE); + free_page((unsigned long)first); + + val = (u8 *)__get_free_page(GFP_KERNEL); + if (!val) { + pr_info("Unable to allocate second free page\n"); + return; + } + + if (val != first) { + pr_warn("Reallocation missed clobbered memory.\n"); + } + + if (memchr(val, 0xAB, PAGE_SIZE) == NULL) { + pr_info("Memory appears initialized (%x, no earlier values)\n", *val); + } else { + pr_err("FAIL: Slab was not initialized\n"); + pr_expected_config_param(CONFIG_INIT_ON_ALLOC_DEFAULT_ON, "init_on_alloc"); + } + free_page((unsigned long)val); +} + void lkdtm_SLAB_FREE_DOUBLE(void) { int *val; diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index e491bc571808..6a30b60519f3 100644 --- a/drivers/misc/lkdtm/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h @@ -86,6 +86,8 @@ void lkdtm_WRITE_AFTER_FREE(void); void lkdtm_READ_AFTER_FREE(void); void lkdtm_WRITE_BUDDY_AFTER_FREE(void); void lkdtm_READ_BUDDY_AFTER_FREE(void); +void lkdtm_SLAB_INIT_ON_ALLOC(void); +void lkdtm_BUDDY_INIT_ON_ALLOC(void); void lkdtm_SLAB_FREE_DOUBLE(void); void lkdtm_SLAB_FREE_CROSS(void); void lkdtm_SLAB_FREE_PAGE(void); diff --git a/tools/testing/selftests/lkdtm/config b/tools/testing/selftests/lkdtm/config index 849799bcfa95..013446e87f1f 100644 --- a/tools/testing/selftests/lkdtm/config +++ b/tools/testing/selftests/lkdtm/config @@ -5,3 +5,4 @@ CONFIG_FORTIFY_SOURCE=y CONFIG_HARDENED_USERCOPY=y # CONFIG_HARDENED_USERCOPY_FALLBACK is not set CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y +CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index 30080cc15623..846cfd508d3c 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -21,6 +21,8 @@ VMALLOC_LINEAR_OVERFLOW READ_AFTER_FREE call trace:|Memory correctly poisoned #WRITE_BUDDY_AFTER_FREE Corrupts memory on failure READ_BUDDY_AFTER_FREE call trace:|Memory correctly poisoned +SLAB_INIT_ON_ALLOC Memory appears initialized +BUDDY_INIT_ON_ALLOC Memory appears initialized SLAB_FREE_DOUBLE SLAB_FREE_CROSS SLAB_FREE_PAGE
On Wed, Jun 23, 2021 at 01:39:27PM -0700, Kees Cook wrote:
This is a bunch of LKDTM clean-ups to improve and expand testing, given feedback from testing at kernelci.org. Adds a few new tests as well.
(If a pull-request is preferred, please let me know.)
This works, thanks!
greg k-h
On 23/06/2021 21:39, Kees Cook wrote:
This is a bunch of LKDTM clean-ups to improve and expand testing, given feedback from testing at kernelci.org. Adds a few new tests as well.
(If a pull-request is preferred, please let me know.)
Thanks!
-Kees
Kees Cook (9): selftests/lkdtm: Avoid needing explicit sub-shell selftests/lkdtm: Fix expected text for CR4 pinning selftests/lkdtm: Fix expected text for free poison lkdtm/bugs: XFAIL UNALIGNED_LOAD_STORE_WRITE lkdtm/heap: Add vmalloc linear overflow test lkdtm: Enable DOUBLE_FAULT on all architectures lkdtm: Add CONFIG hints in errors where possible selftests/lkdtm: Enable various testable CONFIGs lkdtm/heap: Add init_on_alloc tests
Tested-by: "kernelci.org bot" bot@kernelci.org
This whole series was tested on top of next-20210623, here are a couple of sample results from KernelCI staging:
Clang 13 on AMD x86_64: https://staging.kernelci.org/test/plan/id/60d4a5ce3d2bc535d924bf25/
GCC 8 on Intel x86_64: https://staging.kernelci.org/test/plan/id/60d570711a3d6beefe24bf26/
And this is where the series was applied: https://github.com/kernelci/linux/commits/a9f4387015268e426c77fd51ed846c9756...
Thanks, Guillaume
On Fri, Jun 25, 2021 at 07:22:01AM +0100, Guillaume Tucker wrote:
On 23/06/2021 21:39, Kees Cook wrote:
This is a bunch of LKDTM clean-ups to improve and expand testing, given feedback from testing at kernelci.org. Adds a few new tests as well.
(If a pull-request is preferred, please let me know.)
Thanks!
-Kees
Kees Cook (9): selftests/lkdtm: Avoid needing explicit sub-shell selftests/lkdtm: Fix expected text for CR4 pinning selftests/lkdtm: Fix expected text for free poison lkdtm/bugs: XFAIL UNALIGNED_LOAD_STORE_WRITE lkdtm/heap: Add vmalloc linear overflow test lkdtm: Enable DOUBLE_FAULT on all architectures lkdtm: Add CONFIG hints in errors where possible selftests/lkdtm: Enable various testable CONFIGs lkdtm/heap: Add init_on_alloc tests
Tested-by: "kernelci.org bot" bot@kernelci.org
This whole series was tested on top of next-20210623, here are a couple of sample results from KernelCI staging:
Clang 13 on AMD x86_64: https://staging.kernelci.org/test/plan/id/60d4a5ce3d2bc535d924bf25/
GCC 8 on Intel x86_64: https://staging.kernelci.org/test/plan/id/60d570711a3d6beefe24bf26/
And this is where the series was applied: https://github.com/kernelci/linux/commits/a9f4387015268e426c77fd51ed846c9756...
Fantastic! Thanks; this looks great. :)
linux-kselftest-mirror@lists.linaro.org