Re: [PATCH v7 11/12] lib: add kasan test module

From: Dmitry Chernenkov
Date: Tue Nov 25 2014 - 06:15:06 EST


I have a bit of concern about tests.
A) they are not fully automated, there is no checking whether they
pass or not. This is implemented in our repository using special tags
in the log (https://github.com/google/kasan/commit/33b267553e7ffe66d5207152a3294112361b75fe;
don't mmind the TODOs, they weren't broken to begin with), and a
parser script (https://code.google.com/p/address-sanitizer/source/browse/trunk/tools/kernel_test_parse.py)
to feed the kernel log to.

B) They are not thorough enough - they don't check false negatives,
accesses more than 1 byte away etc.

C) (more of general concern for current Kasan realiability) - when
running multiple times, some tests are flaky, specificially oob_right
and uaf2. The latter needs quarantine to work reliably (I know
Konstantin is working on it). oob_right needs redzones in the
beginning of the slabs.

I know all of these may seem like long shots, but if we want a
reliable solution (also a backportable solution), we need to at least
consider them.

Otherwise, LGTM

On Mon, Nov 24, 2014 at 9:02 PM, Andrey Ryabinin <a.ryabinin@xxxxxxxxxxx> wrote:
> This is a test module doing various nasty things like
> out of bounds accesses, use after free. It is useful for testing
> kernel debugging features like kernel address sanitizer.
>
> It mostly concentrates on testing of slab allocator, but we
> might want to add more different stuff here in future (like
> stack/global variables out of bounds accesses and so on).
>
> Signed-off-by: Andrey Ryabinin <a.ryabinin@xxxxxxxxxxx>
> ---
> lib/Kconfig.kasan | 8 ++
> lib/Makefile | 1 +
> lib/test_kasan.c | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 263 insertions(+)
> create mode 100644 lib/test_kasan.c
>
> diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
> index 1fa4fe8..8548646 100644
> --- a/lib/Kconfig.kasan
> +++ b/lib/Kconfig.kasan
> @@ -43,4 +43,12 @@ config KASAN_INLINE
>
> endchoice
>
> +config TEST_KASAN
> + tristate "Module for testing kasan for bug detection"
> + depends on m && KASAN
> + help
> + This is a test module doing various nasty things like
> + out of bounds accesses, use after free. It is useful for testing
> + kernel debugging features like kernel address sanitizer.
> +
> endif
> diff --git a/lib/Makefile b/lib/Makefile
> index 750617c..1d8211a 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_TEST_LKM) += test_module.o
> obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
> obj-$(CONFIG_TEST_BPF) += test_bpf.o
> obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o
> +obj-$(CONFIG_TEST_KASAN) += test_kasan.o
>
> ifeq ($(CONFIG_DEBUG_KOBJECT),y)
> CFLAGS_kobject.o += -DDEBUG
> diff --git a/lib/test_kasan.c b/lib/test_kasan.c
> new file mode 100644
> index 0000000..896dee5
> --- /dev/null
> +++ b/lib/test_kasan.c
> @@ -0,0 +1,254 @@
> +/*
> + *
> + * Copyright (c) 2014 Samsung Electronics Co., Ltd.
> + * Author: Andrey Ryabinin <a.ryabinin@xxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#define pr_fmt(fmt) "kasan test: %s " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/module.h>
> +
> +static noinline void __init kmalloc_oob_right(void)
> +{
> + char *ptr;
> + size_t size = 123;
> +
> + pr_info("out-of-bounds to right\n");
> + ptr = kmalloc(size, GFP_KERNEL);
> + if (!ptr) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> +
> + ptr[size] = 'x';
> + kfree(ptr);
> +}
> +
> +static noinline void __init kmalloc_oob_left(void)
> +{
> + char *ptr;
> + size_t size = 15;
> +
> + pr_info("out-of-bounds to left\n");
> + ptr = kmalloc(size, GFP_KERNEL);
> + if (!ptr) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> +
> + *ptr = *(ptr - 1);
> + kfree(ptr);
> +}
> +
> +static noinline void __init kmalloc_node_oob_right(void)
> +{
> + char *ptr;
> + size_t size = 4096;
> +
> + pr_info("kmalloc_node(): out-of-bounds to right\n");
> + ptr = kmalloc_node(size, GFP_KERNEL, 0);
> + if (!ptr) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> +
> + ptr[size] = 0;
> + kfree(ptr);
> +}
> +
> +static noinline void __init kmalloc_large_oob_rigth(void)
> +{
> + char *ptr;
> + size_t size = KMALLOC_MAX_CACHE_SIZE + 10;
> +
> + pr_info("kmalloc large allocation: out-of-bounds to right\n");
> + ptr = kmalloc(size, GFP_KERNEL);
> + if (!ptr) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> +
> + ptr[size] = 0;
> + kfree(ptr);
> +}
> +
> +static noinline void __init kmalloc_oob_krealloc_more(void)
> +{
> + char *ptr1, *ptr2;
> + size_t size1 = 17;
> + size_t size2 = 19;
> +
> + pr_info("out-of-bounds after krealloc more\n");
> + ptr1 = kmalloc(size1, GFP_KERNEL);
> + ptr2 = krealloc(ptr1, size2, GFP_KERNEL);
> + if (!ptr1 || !ptr2) {
> + pr_err("Allocation failed\n");
> + kfree(ptr1);
> + return;
> + }
> +
> + ptr2[size2] = 'x';
> + kfree(ptr2);
> +}
> +
> +static noinline void __init kmalloc_oob_krealloc_less(void)
> +{
> + char *ptr1, *ptr2;
> + size_t size1 = 17;
> + size_t size2 = 15;
> +
> + pr_info("out-of-bounds after krealloc less\n");
> + ptr1 = kmalloc(size1, GFP_KERNEL);
> + ptr2 = krealloc(ptr1, size2, GFP_KERNEL);
> + if (!ptr1 || !ptr2) {
> + pr_err("Allocation failed\n");
> + kfree(ptr1);
> + return;
> + }
> + ptr2[size1] = 'x';
> + kfree(ptr2);
> +}
> +
> +static noinline void __init kmalloc_oob_16(void)
> +{
> + struct {
> + u64 words[2];
> + } *ptr1, *ptr2;
> +
> + pr_info("kmalloc out-of-bounds for 16-bytes access\n");
> + ptr1 = kmalloc(sizeof(*ptr1) - 3, GFP_KERNEL);
> + ptr2 = kmalloc(sizeof(*ptr2), GFP_KERNEL);
> + if (!ptr1 || !ptr2) {
> + pr_err("Allocation failed\n");
> + kfree(ptr1);
> + kfree(ptr2);
> + return;
> + }
> + *ptr1 = *ptr2;
> + kfree(ptr1);
> + kfree(ptr2);
> +}
> +
> +static noinline void __init kmalloc_oob_in_memset(void)
> +{
> + char *ptr;
> + size_t size = 666;
> +
> + pr_info("out-of-bounds in memset\n");
> + ptr = kmalloc(size, GFP_KERNEL);
> + if (!ptr) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> +
> + memset(ptr, 0, size+5);
> + kfree(ptr);
> +}
> +
> +static noinline void __init kmalloc_uaf(void)
> +{
> + char *ptr;
> + size_t size = 10;
> +
> + pr_info("use-after-free\n");
> + ptr = kmalloc(size, GFP_KERNEL);
> + if (!ptr) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> +
> + kfree(ptr);
> + *(ptr + 8) = 'x';
> +}
> +
> +static noinline void __init kmalloc_uaf_memset(void)
> +{
> + char *ptr;
> + size_t size = 33;
> +
> + pr_info("use-after-free in memset\n");
> + ptr = kmalloc(size, GFP_KERNEL);
> + if (!ptr) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> +
> + kfree(ptr);
> + memset(ptr, 0, size);
> +}
> +
> +static noinline void __init kmalloc_uaf2(void)
> +{
> + char *ptr1, *ptr2;
> + size_t size = 43;
> +
> + pr_info("use-after-free after another kmalloc\n");
> + ptr1 = kmalloc(size, GFP_KERNEL);
> + if (!ptr1) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> +
> + kfree(ptr1);
> + ptr2 = kmalloc(size, GFP_KERNEL);
> + if (!ptr2) {
> + pr_err("Allocation failed\n");
> + return;
> + }
> +
> + ptr1[40] = 'x';
> + kfree(ptr2);
> +}
> +
> +static noinline void __init kmem_cache_oob(void)
> +{
> + char *p;
> + size_t size = 200;
> + struct kmem_cache *cache = kmem_cache_create("test_cache",
> + size, 0,
> + 0, NULL);
> + if (!cache) {
> + pr_err("Cache allocation failed\n");
> + return;
> + }
> + pr_info("out-of-bounds in kmem_cache_alloc\n");
> + p = kmem_cache_alloc(cache, GFP_KERNEL);
> + if (!p) {
> + pr_err("Allocation failed\n");
> + kmem_cache_destroy(cache);
> + return;
> + }
> +
> + *p = p[size];
> + kmem_cache_free(cache, p);
> + kmem_cache_destroy(cache);
> +}
> +
> +int __init kmalloc_tests_init(void)
> +{
> + kmalloc_oob_right();
> + kmalloc_oob_left();
> + kmalloc_node_oob_right();
> + kmalloc_large_oob_rigth();
> + kmalloc_oob_krealloc_more();
> + kmalloc_oob_krealloc_less();
> + kmalloc_oob_16();
> + kmalloc_oob_in_memset();
> + kmalloc_uaf();
> + kmalloc_uaf_memset();
> + kmalloc_uaf2();
> + kmem_cache_oob();
> + return -EAGAIN;
> +}
> +
> +module_init(kmalloc_tests_init);
> +MODULE_LICENSE("GPL");
> --
> 2.1.3
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/