lkdtm: Add CONFIG hints in errors where possible
authorKees Cook <keescook@chromium.org>
Wed, 23 Jun 2021 20:39:34 +0000 (13:39 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 24 Jun 2021 13:32:08 +0000 (15:32 +0200)
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>
Link: https://lore.kernel.org/r/20210623203936.3151093-8-keescook@chromium.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/lkdtm/bugs.c
drivers/misc/lkdtm/cfi.c
drivers/misc/lkdtm/core.c
drivers/misc/lkdtm/fortify.c
drivers/misc/lkdtm/heap.c
drivers/misc/lkdtm/lkdtm.h
drivers/misc/lkdtm/stackleak.c
drivers/misc/lkdtm/usercopy.c
tools/testing/selftests/lkdtm/stack-entropy.sh

index 9ff02bd..7c73355 100644 (file)
@@ -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 */
index e73ebdb..c9aedde 100644 (file)
@@ -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);
 }
index 2c89fc1..c185ae4 100644 (file)
@@ -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)
index faf29cf..0f51d31 100644 (file)
@@ -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);
 }
index 36be5e3..a3bb057 100644 (file)
@@ -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);
 }
index c6baf4f..e491bc5 100644 (file)
@@ -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);
index d1a5c07..00db21f 100644 (file)
@@ -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");
        }
index 15d220e..9161ce7 100644 (file)
@@ -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);
index b1b8a50..1b4d95d 100755 (executable)
@@ -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