Commit d96938da authored by Daniel Axtens's avatar Daniel Axtens Committed by Linus Torvalds
Browse files

lkdtm: tests for FORTIFY_SOURCE

Add code to test both:

 - runtime detection of the overrun of a structure. This covers the
   __builtin_object_size(x, 0) case. This test is called FORTIFY_OBJECT.

 - runtime detection of the overrun of a char array within a structure.
   This covers the __builtin_object_size(x, 1) case which can be used
   for some string functions. This test is called FORTIFY_SUBOBJECT.

Link: https://lkml.kernel.org/r/20201122162451.27551-3-laniel_francis@privacyrequired.com


Signed-off-by: default avatarDaniel Axtens <dja@axtens.net>
Signed-off-by: default avatarFrancis Laniel <laniel_francis@privacyrequired.com>
Suggested-by: default avatarKees Cook <keescook@chromium.org>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Cc: Daniel Micay <danielmicay@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 6a39e62a
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
@@ -482,3 +482,53 @@ noinline void lkdtm_CORRUPT_PAC(void)
	pr_err("XFAIL: this test is arm64-only\n");
#endif
}

void lkdtm_FORTIFY_OBJECT(void)
{
	struct target {
		char a[10];
	} target[2] = {};
	int result;

	/*
	 * Using volatile prevents the compiler from determining the value of
	 * 'size' at compile time. Without that, we would get a compile error
	 * rather than a runtime error.
	 */
	volatile int size = 11;

	pr_info("trying to read past the end of a struct\n");

	result = memcmp(&target[0], &target[1], size);

	/* Print result to prevent the code from being eliminated */
	pr_err("FAIL: fortify did not catch an object overread!\n"
	       "\"%d\" was the memcmp result.\n", result);
}

void lkdtm_FORTIFY_SUBOBJECT(void)
{
	struct target {
		char a[10];
		char b[10];
	} target;
	char *src;

	src = kmalloc(20, GFP_KERNEL);
	strscpy(src, "over ten bytes", 20);

	pr_info("trying to strcpy past the end of a member of a struct\n");

	/*
	 * strncpy(target.a, src, 20); will hit a compile error because the
	 * compiler knows at build time that target.a < 20 bytes. Use strcpy()
	 * to force a runtime error.
	 */
	strcpy(target.a, src);

	/* Use target.a to prevent the code from being eliminated */
	pr_err("FAIL: fortify did not catch an sub-object overrun!\n"
	       "\"%s\" was copied.\n", target.a);

	kfree(src);
}
+2 −0
Original line number Diff line number Diff line
@@ -117,6 +117,8 @@ static const struct crashtype crashtypes[] = {
	CRASHTYPE(UNSET_SMEP),
	CRASHTYPE(CORRUPT_PAC),
	CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE),
	CRASHTYPE(FORTIFY_OBJECT),
	CRASHTYPE(FORTIFY_SUBOBJECT),
	CRASHTYPE(OVERWRITE_ALLOCATION),
	CRASHTYPE(WRITE_AFTER_FREE),
	CRASHTYPE(READ_AFTER_FREE),
+2 −0
Original line number Diff line number Diff line
@@ -32,6 +32,8 @@ void lkdtm_STACK_GUARD_PAGE_TRAILING(void);
void lkdtm_UNSET_SMEP(void);
void lkdtm_DOUBLE_FAULT(void);
void lkdtm_CORRUPT_PAC(void);
void lkdtm_FORTIFY_OBJECT(void);
void lkdtm_FORTIFY_SUBOBJECT(void);

/* lkdtm_heap.c */
void __init lkdtm_heap_init(void);