]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
mm: Make ksize() a reporting-only function
authorKees Cook <keescook@chromium.org>
Thu, 22 Sep 2022 20:08:16 +0000 (13:08 -0700)
committerKees Cook <keescook@chromium.org>
Thu, 1 Dec 2022 16:50:30 +0000 (08:50 -0800)
With all "silently resizing" callers of ksize() refactored, remove the
logic in ksize() that would allow it to be used to effectively change
the size of an allocation (bypassing __alloc_size hints, etc). Users
wanting this feature need to either use kmalloc_size_roundup() before an
allocation, or use krealloc() directly.

For kfree_sensitive(), move the unpoisoning logic inline. Replace the
some of the partially open-coded ksize() in __do_krealloc with ksize()
now that it doesn't perform unpoisoning.

Adjust the KUnit tests to match the new ksize() behavior. Execution
tested with:

$ ./tools/testing/kunit/kunit.py run \
--kconfig_add CONFIG_KASAN=y \
--kconfig_add CONFIG_KASAN_GENERIC=y \
--arch x86_64 kasan

Cc: Christoph Lameter <cl@linux.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Hyeonggon Yoo <42.hyeyoo@gmail.com>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Cc: linux-mm@kvack.org
Cc: kasan-dev@googlegroups.com
Acked-by: Vlastimil Babka <vbabka@suse.cz>
Acked-by: David Rientjes <rientjes@google.com>
Enhanced-by: Andrey Konovalov <andreyknvl@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
mm/kasan/kasan_test.c
mm/slab_common.c

index 0d59098f087613d11ef37cdd02c50894eb67e9e1..73684642c42d825ae5e61b430283c4a918038aa1 100644 (file)
@@ -783,23 +783,30 @@ static void kasan_global_oob_left(struct kunit *test)
        KUNIT_EXPECT_KASAN_FAIL(test, *(volatile char *)p);
 }
 
-/* Check that ksize() makes the whole object accessible. */
+/* Check that ksize() does NOT unpoison whole object. */
 static void ksize_unpoisons_memory(struct kunit *test)
 {
        char *ptr;
-       size_t size = 123, real_size;
+       size_t size = 128 - KASAN_GRANULE_SIZE - 5;
+       size_t real_size;
 
        ptr = kmalloc(size, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
+
        real_size = ksize(ptr);
+       KUNIT_EXPECT_GT(test, real_size, size);
 
        OPTIMIZER_HIDE_VAR(ptr);
 
-       /* This access shouldn't trigger a KASAN report. */
-       ptr[size] = 'x';
+       /* These accesses shouldn't trigger a KASAN report. */
+       ptr[0] = 'x';
+       ptr[size - 1] = 'x';
 
-       /* This one must. */
-       KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[real_size]);
+       /* These must trigger a KASAN report. */
+       if (IS_ENABLED(CONFIG_KASAN_GENERIC))
+               KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[size]);
+       KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[size + 5]);
+       KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[real_size - 1]);
 
        kfree(ptr);
 }
index 33b1886b06ebfd4a137c91ae7d4ad22d1e4265f7..7e96abf1bd7d18192a3b0a73d6ec83ea731d59c6 100644 (file)
@@ -1333,11 +1333,11 @@ __do_krealloc(const void *p, size_t new_size, gfp_t flags)
        void *ret;
        size_t ks;
 
-       /* Don't use instrumented ksize to allow precise KASAN poisoning. */
+       /* Check for double-free before calling ksize. */
        if (likely(!ZERO_OR_NULL_PTR(p))) {
                if (!kasan_check_byte(p))
                        return NULL;
-               ks = kfence_ksize(p) ?: __ksize(p);
+               ks = ksize(p);
        } else
                ks = 0;
 
@@ -1405,8 +1405,10 @@ void kfree_sensitive(const void *p)
        void *mem = (void *)p;
 
        ks = ksize(mem);
-       if (ks)
+       if (ks) {
+               kasan_unpoison_range(mem, ks);
                memzero_explicit(mem, ks);
+       }
        kfree(mem);
 }
 EXPORT_SYMBOL(kfree_sensitive);
@@ -1427,13 +1429,11 @@ EXPORT_SYMBOL(kfree_sensitive);
  */
 size_t ksize(const void *objp)
 {
-       size_t size;
-
        /*
-        * We need to first check that the pointer to the object is valid, and
-        * only then unpoison the memory. The report printed from ksize() is
-        * more useful, then when it's printed later when the behaviour could
-        * be undefined due to a potential use-after-free or double-free.
+        * We need to first check that the pointer to the object is valid.
+        * The KASAN report printed from ksize() is more useful, then when
+        * it's printed later when the behaviour could be undefined due to
+        * a potential use-after-free or double-free.
         *
         * We use kasan_check_byte(), which is supported for the hardware
         * tag-based KASAN mode, unlike kasan_check_read/write().
@@ -1447,13 +1447,7 @@ size_t ksize(const void *objp)
        if (unlikely(ZERO_OR_NULL_PTR(objp)) || !kasan_check_byte(objp))
                return 0;
 
-       size = kfence_ksize(objp) ?: __ksize(objp);
-       /*
-        * We assume that ksize callers could use whole allocated area,
-        * so we need to unpoison this area.
-        */
-       kasan_unpoison_range(objp, size);
-       return size;
+       return kfence_ksize(objp) ?: __ksize(objp);
 }
 EXPORT_SYMBOL(ksize);