@@ -4,9 +4,50 @@
#define pr_fmt(fmt) "lkdtm: " fmt
#include <linux/kernel.h>
+#include <linux/fs.h>
+
+/*
+ * 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);
@@ -5,7 +5,9 @@
* test source files.
*/
#include "lkdtm.h"
+#include <linux/debugfs.h>
#include <linux/sched.h>
+#include <asm/local.h>
/*
* 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");
+ }
}
@@ -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];