[PATCH] kunit/fortify: Validate __alloc_size attribute results
From: Kees Cook
Date: Tue Oct 18 2022 - 04:28:45 EST
Validate the effect of the __alloc_size attribute on allocators. If the
compiler doesn't support __builtin_dynamic_object_size(), skip the test.
Cc: linux-hardening@xxxxxxxxxxxxxxx
Signed-off-by: Kees Cook <keescook@xxxxxxxxxxxx>
---
To pass this depends on the following patches:
https://lore.kernel.org/lkml/20221018073430.never.551-kees@xxxxxxxxxx/
https://lore.kernel.org/lkml/20221018082232.never.213-kees@xxxxxxxxxx/
To not be skipped, either GCC 12 or Clang is needed.
---
lib/fortify_kunit.c | 92 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 92 insertions(+)
diff --git a/lib/fortify_kunit.c b/lib/fortify_kunit.c
index 409af07f340a..5076ba11adfd 100644
--- a/lib/fortify_kunit.c
+++ b/lib/fortify_kunit.c
@@ -16,7 +16,10 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <kunit/test.h>
+#include <linux/device.h>
#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
static const char array_of_10[] = "this is 10";
static const char *ptr_of_11 = "this is 11!";
@@ -60,9 +63,98 @@ static void control_flow_split_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, want_minus_one(pick), SIZE_MAX);
}
+#define check_alloc(alloc, free) do { \
+ size_t expected = size; \
+ void *p = alloc; \
+ KUNIT_EXPECT_TRUE_MSG(test, p != NULL, #alloc " failed?!\n"); \
+ KUNIT_EXPECT_EQ_MSG(test, __builtin_dynamic_object_size(p, 1), \
+ expected, \
+ "__alloc_size() not working with " #alloc "\n"); \
+ free; \
+} while (0)
+
+static volatile size_t unknown_size = 50;
+
+static void alloc_size_test(struct kunit *test)
+{
+#if !__has_builtin(__builtin_dynamic_object_size)
+ kunit_skip(test, "Compiler is missing __builtin_dynamic_object_size() support\n");
+#else
+ const char device_name[] = "fortify-test";
+ struct device *dev;
+ gfp_t gfp = GFP_KERNEL | __GFP_NOWARN;
+ size_t size = unknown_size, prev_size;
+ void *orig;
+
+ /* kmalloc()-family */
+ check_alloc(kmalloc(size++, gfp), kfree(p));
+ check_alloc(kmalloc_node(size++, gfp, NUMA_NO_NODE), kfree(p));
+ check_alloc(kzalloc(size++, gfp), kfree(p));
+ check_alloc(kzalloc_node(size++, gfp, NUMA_NO_NODE), kfree(p));
+ check_alloc(kcalloc(1, size++, gfp), kfree(p));
+ check_alloc(kcalloc_node(1, size++, gfp, NUMA_NO_NODE), kfree(p));
+ check_alloc(kmalloc_array(1, size++, gfp), kfree(p));
+ check_alloc(kmalloc_array_node(1, size++, gfp, NUMA_NO_NODE), kfree(p));
+ check_alloc(__kmalloc(size++, gfp), kfree(p));
+ check_alloc(__kmalloc_node(size++, gfp, NUMA_NO_NODE), kfree(p));
+
+ /* kmemdup() */
+ prev_size = size;
+ size = 11;
+ check_alloc(kmemdup("hello there", size, gfp), kfree(p));
+ size = prev_size + 1;
+
+ /* krealloc()-family */
+ orig = kmalloc(size++, gfp);
+ check_alloc(krealloc(orig, size++, gfp), kfree(p));
+ orig = kmalloc(size++, gfp);
+ check_alloc(krealloc_array(orig, 1, size++, gfp), kfree(p));
+
+ /* vmalloc()-family */
+ check_alloc(vmalloc(size++), vfree(p));
+ check_alloc(vzalloc(size++), vfree(p));
+ check_alloc(__vmalloc(size++, gfp), vfree(p));
+
+ /* kvalloc()-family */
+ check_alloc(kvmalloc(size++, gfp), kvfree(p));
+ check_alloc(kvmalloc_node(size++, gfp, NUMA_NO_NODE), kvfree(p));
+ check_alloc(kvzalloc(size++, gfp), kvfree(p));
+ check_alloc(kvzalloc_node(size++, gfp, NUMA_NO_NODE), kvfree(p));
+ check_alloc(kvcalloc(1, size++, gfp), kvfree(p));
+ check_alloc(kvmalloc_array(1, size++, gfp), kvfree(p));
+
+ /* kvrealloc() */
+ prev_size = size++;
+ orig = kvmalloc(prev_size, gfp);
+ check_alloc(kvrealloc(orig, prev_size, size++, gfp), kfree(p));
+
+ /* Create dummy device for devm_kmalloc()-family tests. */
+ dev = root_device_register(device_name);
+ KUNIT_ASSERT_FALSE_MSG(test, IS_ERR(dev),
+ "Cannot register test device\n");
+
+ /* devm_kmalloc()-family */
+ check_alloc(devm_kmalloc(dev, size++, gfp), devm_kfree(dev, p));
+ check_alloc(devm_kzalloc(dev, size++, gfp), devm_kfree(dev, p));
+
+ /* devm_kmemdup() */
+ prev_size = size;
+ size = 4;
+ check_alloc(devm_kmemdup(dev, "Ohai", size, gfp), devm_kfree(dev, p));
+ size = prev_size + 1;
+
+ /* devm_kremalloc() */
+ orig = devm_kmalloc(dev, size++, gfp);
+ check_alloc(devm_krealloc(dev, orig, size++, gfp), devm_kfree(dev, p));
+
+ device_unregister(dev);
+#endif
+}
+
static struct kunit_case fortify_test_cases[] = {
KUNIT_CASE(known_sizes_test),
KUNIT_CASE(control_flow_split_test),
+ KUNIT_CASE(alloc_size_test),
{}
};
--
2.34.1