lkdtm: Add CONFIG hints in errors where possible

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>
This commit is contained in:
Kees Cook 2021-06-23 13:39:34 -07:00 committed by Greg Kroah-Hartman
parent f123c42bbe
commit 5b777131bd
9 changed files with 117 additions and 11 deletions

View 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 */

View 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);
}

View 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)

View 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);
}

View 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);
}

View 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);

View 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");
}

View 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);

View File

@ -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