]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - lib/test_kasan.c
Merge tag 'staging-5.4-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[linux.git] / lib / test_kasan.c
index e3c593c38eff6cfcdebe29bf1c9768ae9ad7331b..49cc4d570a40b7336ec98ec6e9d4ea0ce032f7e5 100644 (file)
@@ -7,16 +7,20 @@
 
 #define pr_fmt(fmt) "kasan test: %s " fmt, __func__
 
+#include <linux/bitops.h>
 #include <linux/delay.h>
+#include <linux/kasan.h>
 #include <linux/kernel.h>
-#include <linux/mman.h>
 #include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/module.h>
 #include <linux/printk.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/uaccess.h>
-#include <linux/module.h>
-#include <linux/kasan.h>
+#include <linux/io.h>
+
+#include <asm/page.h>
 
 /*
  * Note: test functions are marked noinline so that their names appear in
@@ -336,6 +340,42 @@ static noinline void __init kmalloc_uaf2(void)
        kfree(ptr2);
 }
 
+static noinline void __init kfree_via_page(void)
+{
+       char *ptr;
+       size_t size = 8;
+       struct page *page;
+       unsigned long offset;
+
+       pr_info("invalid-free false positive (via page)\n");
+       ptr = kmalloc(size, GFP_KERNEL);
+       if (!ptr) {
+               pr_err("Allocation failed\n");
+               return;
+       }
+
+       page = virt_to_page(ptr);
+       offset = offset_in_page(ptr);
+       kfree(page_address(page) + offset);
+}
+
+static noinline void __init kfree_via_phys(void)
+{
+       char *ptr;
+       size_t size = 8;
+       phys_addr_t phys;
+
+       pr_info("invalid-free false positive (via phys)\n");
+       ptr = kmalloc(size, GFP_KERNEL);
+       if (!ptr) {
+               pr_err("Allocation failed\n");
+               return;
+       }
+
+       phys = virt_to_phys(ptr);
+       kfree(phys_to_virt(phys));
+}
+
 static noinline void __init kmem_cache_oob(void)
 {
        char *p;
@@ -619,6 +659,95 @@ static noinline void __init kasan_strings(void)
        strnlen(ptr, 1);
 }
 
+static noinline void __init kasan_bitops(void)
+{
+       /*
+        * Allocate 1 more byte, which causes kzalloc to round up to 16-bytes;
+        * this way we do not actually corrupt other memory.
+        */
+       long *bits = kzalloc(sizeof(*bits) + 1, GFP_KERNEL);
+       if (!bits)
+               return;
+
+       /*
+        * Below calls try to access bit within allocated memory; however, the
+        * below accesses are still out-of-bounds, since bitops are defined to
+        * operate on the whole long the bit is in.
+        */
+       pr_info("out-of-bounds in set_bit\n");
+       set_bit(BITS_PER_LONG, bits);
+
+       pr_info("out-of-bounds in __set_bit\n");
+       __set_bit(BITS_PER_LONG, bits);
+
+       pr_info("out-of-bounds in clear_bit\n");
+       clear_bit(BITS_PER_LONG, bits);
+
+       pr_info("out-of-bounds in __clear_bit\n");
+       __clear_bit(BITS_PER_LONG, bits);
+
+       pr_info("out-of-bounds in clear_bit_unlock\n");
+       clear_bit_unlock(BITS_PER_LONG, bits);
+
+       pr_info("out-of-bounds in __clear_bit_unlock\n");
+       __clear_bit_unlock(BITS_PER_LONG, bits);
+
+       pr_info("out-of-bounds in change_bit\n");
+       change_bit(BITS_PER_LONG, bits);
+
+       pr_info("out-of-bounds in __change_bit\n");
+       __change_bit(BITS_PER_LONG, bits);
+
+       /*
+        * Below calls try to access bit beyond allocated memory.
+        */
+       pr_info("out-of-bounds in test_and_set_bit\n");
+       test_and_set_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+       pr_info("out-of-bounds in __test_and_set_bit\n");
+       __test_and_set_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+       pr_info("out-of-bounds in test_and_set_bit_lock\n");
+       test_and_set_bit_lock(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+       pr_info("out-of-bounds in test_and_clear_bit\n");
+       test_and_clear_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+       pr_info("out-of-bounds in __test_and_clear_bit\n");
+       __test_and_clear_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+       pr_info("out-of-bounds in test_and_change_bit\n");
+       test_and_change_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+       pr_info("out-of-bounds in __test_and_change_bit\n");
+       __test_and_change_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+       pr_info("out-of-bounds in test_bit\n");
+       (void)test_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+#if defined(clear_bit_unlock_is_negative_byte)
+       pr_info("out-of-bounds in clear_bit_unlock_is_negative_byte\n");
+       clear_bit_unlock_is_negative_byte(BITS_PER_LONG + BITS_PER_BYTE, bits);
+#endif
+       kfree(bits);
+}
+
+static noinline void __init kmalloc_double_kzfree(void)
+{
+       char *ptr;
+       size_t size = 16;
+
+       pr_info("double-free (kzfree)\n");
+       ptr = kmalloc(size, GFP_KERNEL);
+       if (!ptr) {
+               pr_err("Allocation failed\n");
+               return;
+       }
+
+       kzfree(ptr);
+       kzfree(ptr);
+}
+
 static int __init kmalloc_tests_init(void)
 {
        /*
@@ -647,6 +776,8 @@ static int __init kmalloc_tests_init(void)
        kmalloc_uaf();
        kmalloc_uaf_memset();
        kmalloc_uaf2();
+       kfree_via_page();
+       kfree_via_phys();
        kmem_cache_oob();
        memcg_accounted_kmem_cache();
        kasan_stack_oob();
@@ -660,6 +791,8 @@ static int __init kmalloc_tests_init(void)
        kasan_memchr();
        kasan_memcmp();
        kasan_strings();
+       kasan_bitops();
+       kmalloc_double_kzfree();
 
        kasan_restore_multi_shot(multishot);