summaryrefslogtreecommitdiff
path: root/drivers/misc/lkdtm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-07-05 13:42:16 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2021-07-05 13:42:16 -0700
commiteed0218e8cae9fcd186c30e9fcf5fe46a87e056e (patch)
tree799a1360b947a56d05a60433fdf60a96bf3b3348 /drivers/misc/lkdtm
parent3f8b8e7dbd79086ad48fcff33de9399f3da66a69 (diff)
parent6f746d485fb9188dc67dce7de63d21f0c28a1f2e (diff)
Merge tag 'char-misc-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char / misc driver updates from Greg KH: "Here is the big set of char / misc and other driver subsystem updates for 5.14-rc1. Included in here are: - habanalabs driver updates - fsl-mc driver updates - comedi driver updates - fpga driver updates - extcon driver updates - interconnect driver updates - mei driver updates - nvmem driver updates - phy driver updates - pnp driver updates - soundwire driver updates - lots of other tiny driver updates for char and misc drivers This is looking more and more like the "various driver subsystems mushed together" tree... All of these have been in linux-next for a while with no reported issues" * tag 'char-misc-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (292 commits) mcb: Use DEFINE_RES_MEM() helper macro and fix the end address PNP: moved EXPORT_SYMBOL so that it immediately followed its function/variable bus: mhi: pci-generic: Add missing 'pci_disable_pcie_error_reporting()' calls bus: mhi: Wait for M2 state during system resume bus: mhi: core: Fix power down latency intel_th: Wait until port is in reset before programming it intel_th: msu: Make contiguous buffers uncached intel_th: Remove an unused exit point from intel_th_remove() stm class: Spelling fix nitro_enclaves: Set Bus Master for the NE PCI device misc: ibmasm: Modify matricies to matrices misc: vmw_vmci: return the correct errno code siox: Simplify error handling via dev_err_probe() fpga: machxo2-spi: Address warning about unused variable lkdtm/heap: Add init_on_alloc tests selftests/lkdtm: Enable various testable CONFIGs lkdtm: Add CONFIG hints in errors where possible lkdtm: Enable DOUBLE_FAULT on all architectures lkdtm/heap: Add vmalloc linear overflow test lkdtm/bugs: XFAIL UNALIGNED_LOAD_STORE_WRITE ...
Diffstat (limited to 'drivers/misc/lkdtm')
-rw-r--r--drivers/misc/lkdtm/bugs.c11
-rw-r--r--drivers/misc/lkdtm/cfi.c3
-rw-r--r--drivers/misc/lkdtm/core.c58
-rw-r--r--drivers/misc/lkdtm/fortify.c3
-rw-r--r--drivers/misc/lkdtm/heap.c97
-rw-r--r--drivers/misc/lkdtm/lkdtm.h46
-rw-r--r--drivers/misc/lkdtm/stackleak.c4
-rw-r--r--drivers/misc/lkdtm/usercopy.c7
8 files changed, 213 insertions, 16 deletions
diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c
index a164896dc6d4..88c218a9f8b3 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)
@@ -300,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)
@@ -325,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 8024b6a5cc7f..9dda87c6b54a 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
@@ -120,11 +121,14 @@ 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),
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),
@@ -177,9 +181,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
@@ -399,6 +401,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 1323bc16f113..3d9aae5821a0 100644
--- a/drivers/misc/lkdtm/heap.c
+++ b/drivers/misc/lkdtm/heap.c
@@ -5,6 +5,7 @@
*/
#include "lkdtm.h"
#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include <linux/sched.h>
static struct kmem_cache *double_free_cache;
@@ -12,17 +13,36 @@ 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);
}
@@ -89,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);
}
@@ -145,13 +166,79 @@ 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");
+ }
+
+ 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");
}
- pr_info("Buddy page was not poisoned\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 99f90d3e5e9c..6a30b60519f3 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);
@@ -39,11 +80,14 @@ 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);
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/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);