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: Daniel Axtens <dja@axtens.net>
Signed-off-by: Francis Laniel <laniel_francis@privacyrequired.com>
Suggested-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Kees Cook <keescook@chromium.org>
Cc: Daniel Micay <danielmicay@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Daniel Axtens 2020-12-15 20:43:47 -08:00 committed by Linus Torvalds
parent 6a39e62abb
commit d96938daae
3 changed files with 54 additions and 0 deletions

View file

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

View file

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

View file

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