* [RFC PATCH] kunit: Add a macro to wrap a deferred action function
@ 2023-09-15 5:01 David Gow
2023-09-15 19:05 ` Sami Tolvanen
2023-09-19 7:57 ` Maxime Ripard
0 siblings, 2 replies; 3+ messages in thread
From: David Gow @ 2023-09-15 5:01 UTC (permalink / raw)
To: Nathan Chancellor, Kees Cook, Brendan Higgins, Rae Moar, dlatypov
Cc: David Gow, Benjamin Berg, Maxime Ripard, Richard Fitzgerald, llvm,
linux-kernel, kunit-dev, linux-kselftest, linux-hardening,
Nick Desaulniers, Tom Rix
KUnit's deferred action API accepts a void(*)(void *) function pointer
which is called when the test is exited. However, we very frequently
want to use existing functions which accept a single pointer, but which
may not be of type void*. While this is probably dodgy enough to be on
the wrong side of the C standard, it's been often used for similar
callbacks, and gcc's -Wcast-function-type seems to ignore cases where
the only difference is the type of the argument, assuming it's
compatible (i.e., they're both pointers to data).
However, clang 16 has introduced -Wcast-function-type-strict, which no
longer permits any deviation in function pointer type. This seems to be
because it'd break CFI, which validates the type of function calls.
This rather ruins our attempts to cast functions to defer them, and
leaves us with a few options:
1. Stick our fingers in our ears an ignore the warning. (It's worked so
far, but probably isn't the right thing to do.)
2. Find some horrible way of casting which fools the compiler into
letting us do the cast. (It'd still break CFI, though.)
3. Disable the warning, and CFI for this function. This isn't optimal,
but may make sense for test-only code. However, I think we'd have to
do this for every function called, not just the caller, so maybe it's
not practical.
4. Manually write wrappers around any such functions. This is ugly (do
we really want two copies of each function, one of which has no type
info and just forwards to the other). It could get repetitive.
5. Generate these wrappers with a macro. That's what this patch does.
I'm broadly okay with any of the options above, though whatever we go
with will no doubt require some bikeshedding of details (should these
wrappers be public, do we dedupe them, etc).
Thoughts?
Link: https://github.com/ClangBuiltLinux/linux/issues/1750
Signed-off-by: David Gow <davidgow@google.com>
---
I finally got around to setting up clang 16 to look into these warnings:
lib/kunit/test.c:764:38: warning: cast from 'void (*)(const void *)' to 'kunit_action_t *' (aka 'void (*)(void *)') converts to incompatible function type [-Wcast-function-type-strict]
if (kunit_add_action_or_reset(test, (kunit_action_t *)kfree, data) != 0)
^~~~~~~~~~~~~~~~~~~~~~~
lib/kunit/test.c:776:29: warning: cast from 'void (*)(const void *)' to 'kunit_action_t *' (aka 'void (*)(void *)') converts to incompatible function type [-Wcast-function-type-strict]
kunit_release_action(test, (kunit_action_t *)kfree, (void *)ptr);
^~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
It's probably something which needs fixing with wrappers, not with the
"just keep casting things until the compiler forgets" strategy.
There are few enough uses of kunit_add_action() that now's the time to
change things if we want to fix these warnings (and, I guess, work with
CFI). This patch uses an ugly macro, but we're definitely still at the
point where doing this by hand might make more sense.
Don't take this exact patch too seriously: it's mostly a discussion
starter so we can decide on a plan.
Cheers,
-- David
---
include/kunit/resource.h | 9 +++++++++
lib/kunit/executor_test.c | 7 +++----
lib/kunit/test.c | 6 ++++--
3 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/include/kunit/resource.h b/include/kunit/resource.h
index c7383e90f5c9..4110e13970dc 100644
--- a/include/kunit/resource.h
+++ b/include/kunit/resource.h
@@ -390,6 +390,15 @@ void kunit_remove_resource(struct kunit *test, struct kunit_resource *res);
/* A 'deferred action' function to be used with kunit_add_action. */
typedef void (kunit_action_t)(void *);
+/* We can't cast function pointers to kunit_action_t if CFI is enabled. */
+#define KUNIT_DEFINE_ACTION_WRAPPER(wrapper, orig, arg_type) \
+ static void wrapper(void *in) \
+ { \
+ arg_type arg = (arg_type)in; \
+ orig(arg); \
+ }
+
+
/**
* kunit_add_action() - Call a function when the test ends.
* @test: Test case to associate the action with.
diff --git a/lib/kunit/executor_test.c b/lib/kunit/executor_test.c
index b4f6f96b2844..14ac64f4f71b 100644
--- a/lib/kunit/executor_test.c
+++ b/lib/kunit/executor_test.c
@@ -256,9 +256,8 @@ kunit_test_suites(&executor_test_suite);
/* Test helpers */
-/* Use the resource API to register a call to kfree(to_free).
- * Since we never actually use the resource, it's safe to use on const data.
- */
+KUNIT_DEFINE_ACTION_WRAPPER(kfree_action_wrapper, kfree, const void *)
+/* Use the resource API to register a call to kfree(to_free). */
static void kfree_at_end(struct kunit *test, const void *to_free)
{
/* kfree() handles NULL already, but avoid allocating a no-op cleanup. */
@@ -266,7 +265,7 @@ static void kfree_at_end(struct kunit *test, const void *to_free)
return;
kunit_add_action(test,
- (kunit_action_t *)kfree,
+ kfree_action_wrapper,
(void *)to_free);
}
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 421f13981412..41b7d9a090fb 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -804,6 +804,8 @@ static struct notifier_block kunit_mod_nb = {
};
#endif
+KUNIT_DEFINE_ACTION_WRAPPER(kfree_action_wrapper, kfree, const void *)
+
void *kunit_kmalloc_array(struct kunit *test, size_t n, size_t size, gfp_t gfp)
{
void *data;
@@ -813,7 +815,7 @@ void *kunit_kmalloc_array(struct kunit *test, size_t n, size_t size, gfp_t gfp)
if (!data)
return NULL;
- if (kunit_add_action_or_reset(test, (kunit_action_t *)kfree, data) != 0)
+ if (kunit_add_action_or_reset(test, kfree_action_wrapper, data) != 0)
return NULL;
return data;
@@ -825,7 +827,7 @@ void kunit_kfree(struct kunit *test, const void *ptr)
if (!ptr)
return;
- kunit_release_action(test, (kunit_action_t *)kfree, (void *)ptr);
+ kunit_release_action(test, kfree_action_wrapper, (void *)ptr);
}
EXPORT_SYMBOL_GPL(kunit_kfree);
--
2.42.0.459.ge4e396fd5e-goog
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [RFC PATCH] kunit: Add a macro to wrap a deferred action function
2023-09-15 5:01 [RFC PATCH] kunit: Add a macro to wrap a deferred action function David Gow
@ 2023-09-15 19:05 ` Sami Tolvanen
2023-09-19 7:57 ` Maxime Ripard
1 sibling, 0 replies; 3+ messages in thread
From: Sami Tolvanen @ 2023-09-15 19:05 UTC (permalink / raw)
To: David Gow
Cc: Nathan Chancellor, Kees Cook, Brendan Higgins, Rae Moar, dlatypov,
Benjamin Berg, Maxime Ripard, Richard Fitzgerald, llvm,
linux-kernel, kunit-dev, linux-kselftest, linux-hardening,
Nick Desaulniers, Tom Rix
Hi David,
On Thu, Sep 14, 2023 at 10:01 PM David Gow <davidgow@google.com> wrote:
>
> KUnit's deferred action API accepts a void(*)(void *) function pointer
> which is called when the test is exited. However, we very frequently
> want to use existing functions which accept a single pointer, but which
> may not be of type void*. While this is probably dodgy enough to be on
> the wrong side of the C standard, it's been often used for similar
> callbacks, and gcc's -Wcast-function-type seems to ignore cases where
> the only difference is the type of the argument, assuming it's
> compatible (i.e., they're both pointers to data).
>
> However, clang 16 has introduced -Wcast-function-type-strict, which no
> longer permits any deviation in function pointer type. This seems to be
> because it'd break CFI, which validates the type of function calls.
>
> This rather ruins our attempts to cast functions to defer them, and
> leaves us with a few options:
> 1. Stick our fingers in our ears an ignore the warning. (It's worked so
> far, but probably isn't the right thing to do.)
> 2. Find some horrible way of casting which fools the compiler into
> letting us do the cast. (It'd still break CFI, though.)
> 3. Disable the warning, and CFI for this function. This isn't optimal,
> but may make sense for test-only code. However, I think we'd have to
> do this for every function called, not just the caller, so maybe it's
> not practical.
> 4. Manually write wrappers around any such functions. This is ugly (do
> we really want two copies of each function, one of which has no type
> info and just forwards to the other). It could get repetitive.
> 5. Generate these wrappers with a macro. That's what this patch does.
>
> I'm broadly okay with any of the options above, though whatever we go
> with will no doubt require some bikeshedding of details (should these
> wrappers be public, do we dedupe them, etc).
>
> Thoughts?
Using a macro to generate a wrapper is a reasonable approach IMO, and
we've used it before in the kernel to fix type mismatches in
indirectly called functions (v4l2-ioctl and cfg80211 come to mind at
least).
Sami
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFC PATCH] kunit: Add a macro to wrap a deferred action function
2023-09-15 5:01 [RFC PATCH] kunit: Add a macro to wrap a deferred action function David Gow
2023-09-15 19:05 ` Sami Tolvanen
@ 2023-09-19 7:57 ` Maxime Ripard
1 sibling, 0 replies; 3+ messages in thread
From: Maxime Ripard @ 2023-09-19 7:57 UTC (permalink / raw)
To: David Gow
Cc: Nathan Chancellor, Kees Cook, Brendan Higgins, Rae Moar, dlatypov,
Benjamin Berg, Richard Fitzgerald, llvm, linux-kernel, kunit-dev,
linux-kselftest, linux-hardening, Nick Desaulniers, Tom Rix
[-- Attachment #1: Type: text/plain, Size: 2087 bytes --]
On Fri, Sep 15, 2023 at 01:01:23PM +0800, David Gow wrote:
> KUnit's deferred action API accepts a void(*)(void *) function pointer
> which is called when the test is exited. However, we very frequently
> want to use existing functions which accept a single pointer, but which
> may not be of type void*. While this is probably dodgy enough to be on
> the wrong side of the C standard, it's been often used for similar
> callbacks, and gcc's -Wcast-function-type seems to ignore cases where
> the only difference is the type of the argument, assuming it's
> compatible (i.e., they're both pointers to data).
>
> However, clang 16 has introduced -Wcast-function-type-strict, which no
> longer permits any deviation in function pointer type. This seems to be
> because it'd break CFI, which validates the type of function calls.
>
> This rather ruins our attempts to cast functions to defer them, and
> leaves us with a few options:
> 1. Stick our fingers in our ears an ignore the warning. (It's worked so
> far, but probably isn't the right thing to do.)
> 2. Find some horrible way of casting which fools the compiler into
> letting us do the cast. (It'd still break CFI, though.)
> 3. Disable the warning, and CFI for this function. This isn't optimal,
> but may make sense for test-only code. However, I think we'd have to
> do this for every function called, not just the caller, so maybe it's
> not practical.
> 4. Manually write wrappers around any such functions. This is ugly (do
> we really want two copies of each function, one of which has no type
> info and just forwards to the other). It could get repetitive.
> 5. Generate these wrappers with a macro. That's what this patch does.
>
> I'm broadly okay with any of the options above, though whatever we go
> with will no doubt require some bikeshedding of details (should these
> wrappers be public, do we dedupe them, etc).
>
> Thoughts?
Looks awesome :)
We ended up using a wrapper in KMS to workaround this issue and would
benefit from it too.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2023-09-19 7:57 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-15 5:01 [RFC PATCH] kunit: Add a macro to wrap a deferred action function David Gow
2023-09-15 19:05 ` Sami Tolvanen
2023-09-19 7:57 ` Maxime Ripard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox