]> Git Repo - linux.git/commitdiff
kunit: Device wrappers should also manage driver name
authorDavid Gow <[email protected]>
Fri, 16 Aug 2024 04:51:22 +0000 (12:51 +0800)
committerShuah Khan <[email protected]>
Mon, 26 Aug 2024 13:03:46 +0000 (07:03 -0600)
kunit_driver_create() accepts a name for the driver, but does not copy
it, so if that name is either on the stack, or otherwise freed, we end
up with a use-after-free when the driver is cleaned up.

Instead, strdup() the name, and manage it as another KUnit allocation.
As there was no existing kunit_kstrdup(), we add one. Further, add a
kunit_ variant of strdup_const() and kfree_const(), so we don't need to
allocate and manage the string in the majority of cases where it's a
constant.

However, these are inline functions, and is_kernel_rodata() only works
for built-in code. This causes problems in two cases:
- If kunit is built as a module, __{start,end}_rodata is not defined.
- If a kunit test using these functions is built as a module, it will
  suffer the same fate.

This fixes a KASAN splat with overflow.overflow_allocation_test, when
built as a module.

Restrict the is_kernel_rodata() case to when KUnit is built as a module,
which fixes the first case, at the cost of losing the optimisation.

Also, make kunit_{kstrdup,kfree}_const non-inline, so that other modules
using them will not accidentally depend on is_kernel_rodata(). If KUnit
is built-in, they'll benefit from the optimisation, if KUnit is not,
they won't, but the string will be properly duplicated.

Fixes: d03c720e03bd ("kunit: Add APIs for managing devices")
Reported-by: Nico Pache <[email protected]>
Closes: https://groups.google.com/g/kunit-dev/c/81V9b9QYON0
Reviewed-by: Kees Cook <[email protected]>
Reviewed-by: Maxime Ripard <[email protected]>
Reviewed-by: Rae Moar <[email protected]>
Signed-off-by: David Gow <[email protected]>
Tested-by: Rae Moar <[email protected]>
Signed-off-by: Shuah Khan <[email protected]>
include/kunit/test.h
lib/kunit/device.c
lib/kunit/test.c

index e2a1f0928e8b1382562b7b4c947e36b56f2c14b6..5ac237c949a08e6d9ab2443b4622e3b276b9e966 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/types.h>
 
 #include <asm/rwonce.h>
+#include <asm/sections.h>
 
 /* Static key: true if any KUnit tests are currently running */
 DECLARE_STATIC_KEY_FALSE(kunit_running);
@@ -480,6 +481,53 @@ static inline void *kunit_kcalloc(struct kunit *test, size_t n, size_t size, gfp
        return kunit_kmalloc_array(test, n, size, gfp | __GFP_ZERO);
 }
 
+
+/**
+ * kunit_kfree_const() - conditionally free test managed memory
+ * @x: pointer to the memory
+ *
+ * Calls kunit_kfree() only if @x is not in .rodata section.
+ * See kunit_kstrdup_const() for more information.
+ */
+void kunit_kfree_const(struct kunit *test, const void *x);
+
+/**
+ * kunit_kstrdup() - Duplicates a string into a test managed allocation.
+ *
+ * @test: The test context object.
+ * @str: The NULL-terminated string to duplicate.
+ * @gfp: flags passed to underlying kmalloc().
+ *
+ * See kstrdup() and kunit_kmalloc_array() for more information.
+ */
+static inline char *kunit_kstrdup(struct kunit *test, const char *str, gfp_t gfp)
+{
+       size_t len;
+       char *buf;
+
+       if (!str)
+               return NULL;
+
+       len = strlen(str) + 1;
+       buf = kunit_kmalloc(test, len, gfp);
+       if (buf)
+               memcpy(buf, str, len);
+       return buf;
+}
+
+/**
+ * kunit_kstrdup_const() - Conditionally duplicates a string into a test managed allocation.
+ *
+ * @test: The test context object.
+ * @str: The NULL-terminated string to duplicate.
+ * @gfp: flags passed to underlying kmalloc().
+ *
+ * Calls kunit_kstrdup() only if @str is not in the rodata section. Must be freed with
+ * kunit_kfree_const() -- not kunit_kfree().
+ * See kstrdup_const() and kunit_kmalloc_array() for more information.
+ */
+const char *kunit_kstrdup_const(struct kunit *test, const char *str, gfp_t gfp);
+
 /**
  * kunit_vm_mmap() - Allocate KUnit-tracked vm_mmap() area
  * @test: The test context object.
index 25c81ed465fb77cccfc4bb0c0d4e3fdb548a4dd4..520c1fccee8a541badef3a8ee8cdb1113471071b 100644 (file)
@@ -89,7 +89,7 @@ struct device_driver *kunit_driver_create(struct kunit *test, const char *name)
        if (!driver)
                return ERR_PTR(err);
 
-       driver->name = name;
+       driver->name = kunit_kstrdup_const(test, name, GFP_KERNEL);
        driver->bus = &kunit_bus_type;
        driver->owner = THIS_MODULE;
 
@@ -192,8 +192,11 @@ void kunit_device_unregister(struct kunit *test, struct device *dev)
        const struct device_driver *driver = to_kunit_device(dev)->driver;
 
        kunit_release_action(test, device_unregister_wrapper, dev);
-       if (driver)
+       if (driver) {
+               const char *driver_name = driver->name;
                kunit_release_action(test, driver_unregister_wrapper, (void *)driver);
+               kunit_kfree_const(test, driver_name);
+       }
 }
 EXPORT_SYMBOL_GPL(kunit_device_unregister);
 
index e8b1b52a19abb3d520e9e0be3ef635c4a93ba5a9..089c832e3cdbd5f0a1b8318106691b249007d15d 100644 (file)
@@ -874,6 +874,25 @@ void kunit_kfree(struct kunit *test, const void *ptr)
 }
 EXPORT_SYMBOL_GPL(kunit_kfree);
 
+void kunit_kfree_const(struct kunit *test, const void *x)
+{
+#if !IS_MODULE(CONFIG_KUNIT)
+       if (!is_kernel_rodata((unsigned long)x))
+#endif
+               kunit_kfree(test, x);
+}
+EXPORT_SYMBOL_GPL(kunit_kfree_const);
+
+const char *kunit_kstrdup_const(struct kunit *test, const char *str, gfp_t gfp)
+{
+#if !IS_MODULE(CONFIG_KUNIT)
+       if (is_kernel_rodata((unsigned long)str))
+               return str;
+#endif
+       return kunit_kstrdup(test, str, gfp);
+}
+EXPORT_SYMBOL_GPL(kunit_kstrdup_const);
+
 void kunit_cleanup(struct kunit *test)
 {
        struct kunit_resource *res;
This page took 0.05871 seconds and 4 git commands to generate.