Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions arch/x86/kernel/vmlinux.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,18 @@ ASSERT(__relocate_kernel_end - __relocate_kernel_start <= KEXEC_CONTROL_CODE_MAX
#endif

#define KFTF_TABLE \
. = ALIGN(0X100); \
__kftf_start = .; \
KEEP(*(.kftf)); /* keep everything referencing section */ \
__kftf_end = .;
. = ALIGN(PAGE_SIZE); \
__kftf_test_case_start = .; \
KEEP(*(.kftf_test)); \
__kftf_test_case_end = .; \
. = ALIGN(PAGE_SIZE); \
__kftf_constraint_start = .; \
KEEP(*(.kftf_constraint)); \
__kftf_constraint_end = .; \
. = ALIGN(PAGE_SIZE); \
__kftf_annotation_start = .; \
KEEP(*(.kftf_annotation)); \
__kftf_annotation_end = .; \

PHDRS {
text PT_LOAD FLAGS(5); /* R_E */
Expand Down
149 changes: 145 additions & 4 deletions include/linux/kftf.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ struct kftf_test_case {
loff_t *);
};

static int write_input_cb_common(struct file *filp, const char __user *buf,
size_t len, loff_t *off, void *arg,
size_t arg_size)
// XXX: why can't we use without the attribute unused anymore??

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe because this function is inlined at every callsite?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah could be!

__attribute__((unused)) static int
write_input_cb_common(struct file *filp, const char __user *buf, size_t len,
loff_t *off, void *arg, size_t arg_size)
{
if (len != arg_size) {
return -EINVAL;
Expand Down Expand Up @@ -90,7 +91,7 @@ static int write_input_cb_common(struct file *filp, const char __user *buf,
static void _fuzz_test_logic_##func(func_arg_type arg); \
/* test case struct initialization */ \
const struct kftf_test_case __fuzz_test__##func \
__attribute__((__section__(".kftf"), __used__)) = { \
__attribute__((__section__(".kftf_test"), __used__)) = { \
.name = #func, \
.arg_type_name = #func_arg_type, \
.write_input_cb = _write_callback_##func, \
Expand Down Expand Up @@ -125,4 +126,144 @@ static int write_input_cb_common(struct file *filp, const char __user *buf,
} \
static void _fuzz_test_logic_##func(func_arg_type arg)

/**
* Reports a bug with a predictable prefix so that it can be parsed by a
* fuzzing driver.
*/
#define KFTF_REPORT_BUG(msg, fmt) pr_warn("bug: " #msg, fmt)

/**
* struct kftf_constraint_type defines a type of constraint. The fuzzing driver
* should be aware of these.
*/
enum kftf_constraint_type : uint8_t {
EXPECT_EQ = 0,
EXPECT_NE,
EXPECT_LE,
EXPECT_GT,
EXPECT_IN_RANGE,
};

/**
* ktft_constraint defines a domain constraint for a struct variable that is
* taken as input for a FUZZ_TEST
*
* @input_type: the name of the input (a struct name)
* @field_name: the name of the field that this domain constraint applies to
* @value1: used in all comparisons
* @value2: only used in comparisons that require multiple values, e.g. range
* constraints
* @type: the type of the constraint, enumerated above
*
* Note: if this struct is not a multiple of 64 bytes, everything breaks and
* we get corrupted data and occasional kernel panics. To avoid this happening,
* we enforce 64 Byte alignment and statically assert that this struct has size

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am afraid we'll have bigger structs in the future (e.g. to list a set of possible values).
Why does everything break?
Maybe we can put the struct length at its beginning?

* 64 Bytes.
*/
struct kftf_constraint {
const char *input_type;
const char *field_name;
uintptr_t value1;
uintptr_t value2;
enum kftf_constraint_type type;
} __attribute__((aligned(64)));

static_assert(sizeof(struct kftf_constraint) == 64,
"struct kftf_constraint should have size 64");

/**
* __KFTF_DEFINE_CONSTRAINT - defines a fuzz test constraint linked to a given

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: you normally don't write doc comments for internal macros/functions.

* argument type belonging to a fuzz test. See FUZZ_TEST above.
*
* @arg_type: the type of argument (a struct) without the leading "struct" in
* its name, which will be prepended.
* @field: the field on which the constraint is defined.
* @val: used for comparison constraints such as KFTF_EXPECT_NE
* @tpe: the type of constaint that this defines
*
* This macro is intended for internal use. A user should opt for
* KFTF_EXPECT_* instead when defining fuzz test constraints.
*/
#define __KFTF_DEFINE_CONSTRAINT(arg_type, field, val1, val2, tpe) \
static struct kftf_constraint __constraint_##arg_type##_##field \
__attribute__((__section__(".kftf_constraint"), __used__)) = { \
.input_type = "struct " #arg_type, \
.field_name = #field, \
.value1 = (uintptr_t)val1, \
.value2 = (uintptr_t)val2, \
.type = tpe, \
};

#define KFTF_EXPECT_EQ(arg_type, field, val) \
if (arg.field != val) \
return; \
__KFTF_DEFINE_CONSTRAINT(arg_type, field, val, 0x0, EXPECT_EQ)

#define KFTF_EXPECT_NE(arg_type, field, val) \
if (arg.field == val) \
return; \
__KFTF_DEFINE_CONSTRAINT(arg_type, field, val, 0x0, EXPECT_NE)

#define KFTF_EXPECT_LE(arg_type, field, val) \
if (arg.field > val) \
return; \
__KFTF_DEFINE_CONSTRAINT(arg_type, field, val, 0x0, EXPECT_LE)

#define KFTF_EXPECT_GT(arg_type, field, val) \
if (arg.field <= val) \
return; \
__KFTF_DEFINE_CONSTRAINT(arg_type, field, val, 0x0, EXPECT_GT)

#define KFTF_EXPECT_NOT_NULL(arg_type, field) \
KFTF_EXPECT_NE(arg_type, field, 0x0)

#define KFTF_EXPECT_IN_RANGE(arg_type, field, lower_bound, upper_bound) \
if (arg.field < lower_bound || arg.field > upper_bound) \
return; \
__KFTF_DEFINE_CONSTRAINT(arg_type, field, lower_bound, upper_bound, \
EXPECT_IN_RANGE)

enum kftf_annotation_attribute : uint8_t {
ATTRIBUTE_LEN = 0,
ATTRIBUTE_STRING,
ATTRIBUTE_ARRAY,
};

struct kftf_annotation {
const char *input_type;
const char *field_name;
const char *linked_field_name;
enum kftf_annotation_attribute attrib;
} __attribute__((aligned(32)));

#define __KFTF_ANNOTATE(arg_type, field, linked_field, attribute) \
static struct kftf_annotation __annotation_##arg_type##_##field \
__attribute__((__section__(".kftf_annotation"), __used__)) = { \
.input_type = "struct " #arg_type, \
.field_name = #field, \
.linked_field_name = #linked_field, \
.attrib = attribute, \
};

/**
* Annotates arg_type.field as a string
*/
#define KFTF_ANNOTATE_STRING(arg_type, field) \
__KFTF_ANNOTATE(arg_type, field, , ATTRIBUTE_STRING)

/**
* Annotates arg_type.field as an arrray. For example, assume that
* arg_type.field is of type `unsigned long *`, which could either represent
* a pointer to a single value or an array. Using this annotation removes that
* ambiguity.
*/
#define KFTF_ANNOTEATE_ARRAY(arg_type, field) \
__KFTF_ANNOTATE(arg_type, field, , ATTRIBUTE_ARRAY)

/**
* Annotates arg_type.field as the length of arg_type.linked_field
*/
#define KFTF_ANNOTATE_LEN(arg_type, field, linked_field) \
__KFTF_ANNOTATE(arg_type, field, linked_field, ATTRIBUTE_LEN)

#endif /* KFTF_H */
27 changes: 23 additions & 4 deletions lib/kftf/kftf_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@
#include <linux/kftf.h>
#include <linux/printk.h>

extern const struct kftf_test_case __kftf_start[];
extern const struct kftf_test_case __kftf_end[];
#include "kftf_tests.h"

extern const struct kftf_test_case __kftf_test_case_start[];
extern const struct kftf_test_case __kftf_test_case_end[];
extern const struct kftf_constraint __kftf_constraint_start[];
extern const struct kftf_constraint __kftf_constraint_end[];

/**
* struct kftf_dentry - A container for a debugfs dentry and its fops.
Expand Down Expand Up @@ -87,11 +91,12 @@ const umode_t kftf_flags_r = 0444;
static int __init kftf_init(void)
{
const struct kftf_test_case *test;
const struct kftf_constraint *constraint;
int ret = 0;
int i = 0;
size_t num_test_cases;

num_test_cases = __kftf_end - __kftf_start;
num_test_cases = __kftf_test_case_end - __kftf_test_case_start;

st.debugfs_state = kmalloc(
num_test_cases * sizeof(struct kftf_debugfs_state), GFP_KERNEL);
Expand All @@ -111,7 +116,8 @@ static int __init kftf_init(void)
}

/* iterate over all discovered test cases and set up debugfs entries */
for (test = __kftf_start; test < __kftf_end; test++, i++) {
for (test = __kftf_test_case_start; test < __kftf_test_case_end;
test++, i++) {
/* create a directory for the discovered test case */
st.debugfs_state[i].test_dir =
debugfs_create_dir(test->name, st.kftf_dir);
Expand Down Expand Up @@ -165,6 +171,19 @@ static int __init kftf_init(void)
pr_info("kftf: registered %s\n", test->name);
}

// TODO: make debugfs entries for these constraints
size_t num_constraints = 0;
for (constraint = __kftf_constraint_start;
constraint < __kftf_constraint_end; constraint++) {
pr_info("kftf: addr = 0x%lX\n", (size_t)constraint);
pr_info("input type: %s\n", constraint->input_type);
pr_info("field name: %s\n", constraint->field_name);
pr_info("value1: %lx\n", constraint->value1);
pr_info("value2: %lx\n", constraint->value2);
pr_info("type: %d\n", constraint->type);
num_constraints++;
}

return 0;

cleanup_failure:
Expand Down
3 changes: 3 additions & 0 deletions lib/kftf/kftf_tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ static void kftf_fuzzable(char first, char second, char third)

FUZZ_TEST(kftf_fuzzable, struct kftf_simple_arg)
{
KFTF_EXPECT_NOT_NULL(kftf_simple_arg, first);
KFTF_EXPECT_IN_RANGE(kftf_simple_arg, second, 'a', 'z');
KFTF_EXPECT_IN_RANGE(kftf_simple_arg, third, 'a', 'z');
kftf_fuzzable(arg.first, arg.second, arg.third);
}

Expand Down