Commit ae2e1aad authored by Kees Cook's avatar Kees Cook Committed by Linus Torvalds
Browse files

drivers/misc/lkdtm/bugs.c: add arithmetic overflow and array bounds checks



Adds LKDTM tests for arithmetic overflow (both signed and unsigned), as
well as array bounds checking.

Signed-off-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Acked-by: default avatarDmitry Vyukov <dvyukov@google.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Konovalov <andreyknvl@google.com>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Cc: Elena Petrova <lenaptr@google.com>
Cc: "Gustavo A. R. Silva" <gustavo@embeddedor.com>
Link: http://lkml.kernel.org/r/20200227193516.32566-4-keescook@chromium.org


Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 277a1085
Loading
Loading
Loading
Loading
+75 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/sched/signal.h>
#include <linux/sched/task_stack.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

#ifdef CONFIG_X86_32
#include <asm/desc.h>
@@ -175,6 +176,80 @@ void lkdtm_HUNG_TASK(void)
	schedule();
}

volatile unsigned int huge = INT_MAX - 2;
volatile unsigned int ignored;

void lkdtm_OVERFLOW_SIGNED(void)
{
	int value;

	value = huge;
	pr_info("Normal signed addition ...\n");
	value += 1;
	ignored = value;

	pr_info("Overflowing signed addition ...\n");
	value += 4;
	ignored = value;
}


void lkdtm_OVERFLOW_UNSIGNED(void)
{
	unsigned int value;

	value = huge;
	pr_info("Normal unsigned addition ...\n");
	value += 1;
	ignored = value;

	pr_info("Overflowing unsigned addition ...\n");
	value += 4;
	ignored = value;
}

/* Intentially using old-style flex array definition of 1 byte. */
struct array_bounds_flex_array {
	int one;
	int two;
	char data[1];
};

struct array_bounds {
	int one;
	int two;
	char data[8];
	int three;
};

void lkdtm_ARRAY_BOUNDS(void)
{
	struct array_bounds_flex_array *not_checked;
	struct array_bounds *checked;
	volatile int i;

	not_checked = kmalloc(sizeof(*not_checked) * 2, GFP_KERNEL);
	checked = kmalloc(sizeof(*checked) * 2, GFP_KERNEL);

	pr_info("Array access within bounds ...\n");
	/* For both, touch all bytes in the actual member size. */
	for (i = 0; i < sizeof(checked->data); i++)
		checked->data[i] = 'A';
	/*
	 * For the uninstrumented flex array member, also touch 1 byte
	 * beyond to verify it is correctly uninstrumented.
	 */
	for (i = 0; i < sizeof(not_checked->data) + 1; i++)
		not_checked->data[i] = 'A';

	pr_info("Array access beyond bounds ...\n");
	for (i = 0; i < sizeof(checked->data) + 1; i++)
		checked->data[i] = 'B';

	kfree(not_checked);
	kfree(checked);
}

void lkdtm_CORRUPT_LIST_ADD(void)
{
	/*
+3 −0
Original line number Diff line number Diff line
@@ -130,6 +130,9 @@ static const struct crashtype crashtypes[] = {
	CRASHTYPE(HARDLOCKUP),
	CRASHTYPE(SPINLOCKUP),
	CRASHTYPE(HUNG_TASK),
	CRASHTYPE(OVERFLOW_SIGNED),
	CRASHTYPE(OVERFLOW_UNSIGNED),
	CRASHTYPE(ARRAY_BOUNDS),
	CRASHTYPE(EXEC_DATA),
	CRASHTYPE(EXEC_STACK),
	CRASHTYPE(EXEC_KMALLOC),
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@ void lkdtm_SOFTLOCKUP(void);
void lkdtm_HARDLOCKUP(void);
void lkdtm_SPINLOCKUP(void);
void lkdtm_HUNG_TASK(void);
void lkdtm_OVERFLOW_SIGNED(void);
void lkdtm_OVERFLOW_UNSIGNED(void);
void lkdtm_ARRAY_BOUNDS(void);
void lkdtm_CORRUPT_LIST_ADD(void);
void lkdtm_CORRUPT_LIST_DEL(void);
void lkdtm_CORRUPT_USER_DS(void);