* [RFC] Saner typechecking for closures
@ 2024-04-23 4:38 Matthew Wilcox
2024-04-24 23:01 ` Kees Cook
0 siblings, 1 reply; 3+ messages in thread
From: Matthew Wilcox @ 2024-04-23 4:38 UTC (permalink / raw)
To: Al Viro; +Cc: linux-fsdevel, Kees Cook
What would you think to this?
+++ b/include/linux/delayed_call.h
@@ -14,13 +14,12 @@ struct delayed_call {
#define DEFINE_DELAYED_CALL(name) struct delayed_call name = {NULL, NULL}
-/* I really wish we had closures with sane typechecking... */
-static inline void set_delayed_call(struct delayed_call *call,
- void (*fn)(void *), void *arg)
-{
- call->fn = fn;
- call->arg = arg;
-}
+/* Typecheck the arg is appropriate for the function */
+#define set_delayed_call(call, _fn, _arg) do { \
+ (void)sizeof(_fn(_arg)); \
+ (call)->fn = (void (*)(void *))(_fn); \
+ (call)->arg = (_arg); \
+} while (0)
static inline void do_delayed_call(struct delayed_call *call)
{
That should give us the possibility of passing any pointer
to the function, but gets us away from void pointers. I did this as a
followup:
-extern void page_put_link(void *);
+extern void page_put_link(struct folio *);
...
-void page_put_link(void *arg)
+void page_put_link(struct folio *folio)
{
- put_page(arg);
+ folio_put(folio);
}
EXPORT_SYMBOL(page_put_link);
and similar changes to the three callers.
Or is there something newer and shinier we should be using instead of
delayed_call?
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFC] Saner typechecking for closures
2024-04-23 4:38 [RFC] Saner typechecking for closures Matthew Wilcox
@ 2024-04-24 23:01 ` Kees Cook
2024-04-25 3:19 ` Al Viro
0 siblings, 1 reply; 3+ messages in thread
From: Kees Cook @ 2024-04-24 23:01 UTC (permalink / raw)
To: Matthew Wilcox; +Cc: Al Viro, linux-fsdevel
On Tue, Apr 23, 2024 at 05:38:41AM +0100, Matthew Wilcox wrote:
> What would you think to this?
>
> +++ b/include/linux/delayed_call.h
> @@ -14,13 +14,12 @@ struct delayed_call {
>
> #define DEFINE_DELAYED_CALL(name) struct delayed_call name = {NULL, NULL}
>
> -/* I really wish we had closures with sane typechecking... */
> -static inline void set_delayed_call(struct delayed_call *call,
> - void (*fn)(void *), void *arg)
> -{
> - call->fn = fn;
> - call->arg = arg;
> -}
> +/* Typecheck the arg is appropriate for the function */
> +#define set_delayed_call(call, _fn, _arg) do { \
> + (void)sizeof(_fn(_arg)); \
> + (call)->fn = (void (*)(void *))(_fn); \
> + (call)->arg = (_arg); \
> +} while (0)
>
> static inline void do_delayed_call(struct delayed_call *call)
> {
>
>
> That should give us the possibility of passing any pointer
> to the function, but gets us away from void pointers. I did this as a
This just means KCFI will freak out now, since it will see a mismatch
between call->fn's type and the target function's type. :(
And instead of args like this, can't we use the regular container_of()
tricks to get at the pointer we want? i.e. make "call" a member of the
strut doing the delayed call?
-Kees
> followup:
>
> -extern void page_put_link(void *);
> +extern void page_put_link(struct folio *);
>
> ...
>
> -void page_put_link(void *arg)
> +void page_put_link(struct folio *folio)
> {
> - put_page(arg);
> + folio_put(folio);
> }
> EXPORT_SYMBOL(page_put_link);
>
> and similar changes to the three callers.
>
> Or is there something newer and shinier we should be using instead of
> delayed_call?
--
Kees Cook
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFC] Saner typechecking for closures
2024-04-24 23:01 ` Kees Cook
@ 2024-04-25 3:19 ` Al Viro
0 siblings, 0 replies; 3+ messages in thread
From: Al Viro @ 2024-04-25 3:19 UTC (permalink / raw)
To: Kees Cook; +Cc: Matthew Wilcox, linux-fsdevel
On Wed, Apr 24, 2024 at 04:01:45PM -0700, Kees Cook wrote:
> > That should give us the possibility of passing any pointer
> > to the function, but gets us away from void pointers. I did this as a
>
> This just means KCFI will freak out now, since it will see a mismatch
> between call->fn's type and the target function's type. :(
>
> And instead of args like this, can't we use the regular container_of()
> tricks to get at the pointer we want? i.e. make "call" a member of the
> strut doing the delayed call?
Huh? A typical situation is (kfree, void *) or (put_page, struct page *).
We are most certainly *not* embedding anything of that sort into struct
page...
What we want is (T -> void) x T : whenever is_pointer(T), but C doesn't
give that kind of polymorphism. We can do (void * -> void) x void *,
with callbacks converting their arguments to desired types, but that
means that type mismatches are on your head - compiler won't catch
them.
It could be done with lambdas as
(void)sizeof(f(p)), // p is a valid argument for f
call->fn = [](void *v){f((typeof(p))v);},
call->arg = p
but AFAICS we don't have that implemented sanely - neither in gcc nor in clang.
As the result, we have things like
set_delayed_call(callback, page_put_link, page);
...
void page_put_link(void *arg)
{
put_page(arg);
}
and there's nothing to catch you if 'page' above is not struct page *.
It shouldn't need any kind of runtime checks, executable stack, etc. -
everything here can be done statically at compile time. Oh, well...
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2024-04-25 3:19 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-04-23 4:38 [RFC] Saner typechecking for closures Matthew Wilcox
2024-04-24 23:01 ` Kees Cook
2024-04-25 3:19 ` Al Viro
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).