All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kees Cook <keescook@chromium.org>
To: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	"Rafael J. Wysocki" <rafael@kernel.org>,
	linux-kernel@vger.kernel.org,
	Sami Tolvanen <samitolvanen@google.com>,
	Nathan Chancellor <nathan@kernel.org>,
	Nick Desaulniers <ndesaulniers@google.com>
Subject: Re: [RFC PATCH] devres: better type safety with devm_*_action()
Date: Thu, 18 Mar 2021 14:58:09 -0700	[thread overview]
Message-ID: <202103181455.E6FE393@keescook> (raw)
In-Reply-To: <871c73f5-70a5-c970-137b-ccb905f406fa@rasmusvillemoes.dk>

On Thu, Mar 18, 2021 at 09:30:12PM +0100, Rasmus Villemoes wrote:
> On 10/03/2021 00.59, Rasmus Villemoes wrote:
> 
> [quoting in full for context to the new CCs]
> 
> > With a little MacroMagic(tm), we can allow users to pass a pointer to
> > a function that actually takes the type of the data argument, instead
> > of forcing the function to have prototype void (*)(void *). Of course,
> > we must still accept such functions.
> > 
> > This can provide a little more type safety in that we get fewer
> > implicit casts to and from void* - as a random example,
> > gpio_mockup_dispose_mappings in drivers/gpio/gpio-mockup.c could take
> > a "struct gpio_mockup_chip *chip" directly.
> > 
> > Moreover, when the action is some "external" API, there will in many
> > cases no longer be a need for a trivial local wrapper -
> > e.g. drivers/watchdog/cadence_wdt.c could just use
> > clk_disable_unprepare directly and avoid defining
> > cdns_clk_disable_unprepare.
> > 
> > Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
> > ---
> >  drivers/base/devres.c  | 32 +++++++++++++++++++++++---------
> >  include/linux/device.h | 36 ++++++++++++++++++++++++++++++++----
> >  2 files changed, 55 insertions(+), 13 deletions(-)
> > 
> > diff --git a/drivers/base/devres.c b/drivers/base/devres.c
> > index fb9d5289a620..97ebd26bc44a 100644
> > --- a/drivers/base/devres.c
> > +++ b/drivers/base/devres.c
> > @@ -728,15 +728,25 @@ static void devm_action_release(struct device *dev, void *res)
> >  }
> >  
> >  /**
> > - * devm_add_action() - add a custom action to list of managed resources
> > + * __devm_add_action() - add a custom action to list of managed resources
> >   * @dev: Device that owns the action
> >   * @action: Function that should be called
> >   * @data: Pointer to data passed to @action implementation
> >   *
> >   * This adds a custom action to the list of managed resources so that
> >   * it gets executed as part of standard resource unwinding.
> > + *
> > + * Do not call directly, but use the the macro wrapper
> > + * devm_add_action, whose "prototype" is
> > + *
> > + * devm_add_action(struct device *dev, void (*action)(T *), T *data)
> > + *
> > + * This allows use of type-correct callbacks and can avoid wrapping
> > + * external APIs. For example, if the data item is a "struct clk *", one
> > + * can use clk_disable_unprepare directly as the action instead of
> > + * creating a local wrapper taking a "void *" argument.
> >   */
> > -int devm_add_action(struct device *dev, void (*action)(void *), void *data)
> > +int __devm_add_action(struct device *dev, void (*action)(void *), void *data)
> >  {
> >  	struct action_devres *devres;
> >  
> > @@ -751,18 +761,20 @@ int devm_add_action(struct device *dev, void (*action)(void *), void *data)
> >  	devres_add(dev, devres);
> >  	return 0;
> >  }
> > -EXPORT_SYMBOL_GPL(devm_add_action);
> > +EXPORT_SYMBOL_GPL(__devm_add_action);
> >  
> >  /**
> > - * devm_remove_action() - removes previously added custom action
> > + * __devm_remove_action() - removes previously added custom action
> >   * @dev: Device that owns the action
> >   * @action: Function implementing the action
> >   * @data: Pointer to data passed to @action implementation
> >   *
> >   * Removes instance of @action previously added by devm_add_action().
> >   * Both action and data should match one of the existing entries.
> > + *
> > + * Use the macro wrapper devm_remove_action, see __devm_add_action for details.
> >   */
> > -void devm_remove_action(struct device *dev, void (*action)(void *), void *data)
> > +void __devm_remove_action(struct device *dev, void (*action)(void *), void *data)
> >  {
> >  	struct action_devres devres = {
> >  		.data = data,
> > @@ -772,10 +784,10 @@ void devm_remove_action(struct device *dev, void (*action)(void *), void *data)
> >  	WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match,
> >  			       &devres));
> >  }
> > -EXPORT_SYMBOL_GPL(devm_remove_action);
> > +EXPORT_SYMBOL_GPL(__devm_remove_action);
> >  
> >  /**
> > - * devm_release_action() - release previously added custom action
> > + * __devm_release_action() - release previously added custom action
> >   * @dev: Device that owns the action
> >   * @action: Function implementing the action
> >   * @data: Pointer to data passed to @action implementation
> > @@ -783,8 +795,10 @@ EXPORT_SYMBOL_GPL(devm_remove_action);
> >   * Releases and removes instance of @action previously added by
> >   * devm_add_action().  Both action and data should match one of the
> >   * existing entries.
> > + *
> > + * Use the macro wrapper devm_release_action, see __devm_add_action for details.
> >   */
> > -void devm_release_action(struct device *dev, void (*action)(void *), void *data)
> > +void __devm_release_action(struct device *dev, void (*action)(void *), void *data)
> >  {
> >  	struct action_devres devres = {
> >  		.data = data,
> > @@ -795,7 +809,7 @@ void devm_release_action(struct device *dev, void (*action)(void *), void *data)
> >  			       &devres));
> >  
> >  }
> > -EXPORT_SYMBOL_GPL(devm_release_action);
> > +EXPORT_SYMBOL_GPL(__devm_release_action);
> >  
> >  /*
> >   * Managed kmalloc/kfree
> > diff --git a/include/linux/device.h b/include/linux/device.h
> > index ba660731bd25..c924612bfefd 100644
> > --- a/include/linux/device.h
> > +++ b/include/linux/device.h
> > @@ -250,11 +250,39 @@ void __iomem *devm_of_iomap(struct device *dev,
> >  			    resource_size_t *size);
> >  
> >  /* allows to add/remove a custom action to devres stack */
> > -int devm_add_action(struct device *dev, void (*action)(void *), void *data);
> > -void devm_remove_action(struct device *dev, void (*action)(void *), void *data);
> > -void devm_release_action(struct device *dev, void (*action)(void *), void *data);
> >  
> > -static inline int devm_add_action_or_reset(struct device *dev,
> > +/*
> > + * +0 forces the expression to decay to a function pointer. We cannot
> > + * just put an & in front as callers may already include that.
> > + */
> > +#define devm_action_typecheck(action, data)				\
> > +	static_assert(__same_type(action + 0, void (*)(void *)) ||	\
> > +		      __same_type(action + 0, void (*)(typeof(data))))
> > +
> > +#define devm_add_action(dev, action, data) ({				\
> > +	devm_action_typecheck(action, data);				\
> > +	__devm_add_action(dev, (void (*)(void *))action, data);		\
> > +})
> > +#define devm_remove_action(dev, action, data) ({			\
> > +	devm_action_typecheck(action, data);				\
> > +	__devm_remove_action(dev, (void (*)(void *))action, data);	\
> > +})
> > +#define devm_release_action(dev, action, data) ({			\
> > +	devm_action_typecheck(action, data);				\
> > +	__devm_release_action(dev, (void (*)(void *))action, data);	\
> > +})
> > +
> > +
> > +int __devm_add_action(struct device *dev, void (*action)(void *), void *data);
> > +void __devm_remove_action(struct device *dev, void (*action)(void *), void *data);
> > +void __devm_release_action(struct device *dev, void (*action)(void *), void *data);
> > +
> > +#define devm_add_action_or_reset(dev, action, data) ({			\
> > +	devm_action_typecheck(action, data);				\
> > +	__devm_add_action_or_reset(dev, (void (*)(void *))action, data); \
> > +})
> > +
> > +static inline int __devm_add_action_or_reset(struct device *dev,
> >  					   void (*action)(void *), void *data)
> >  {
> >  	int ret;
> > 
> 
> So, this would likely crash and burn under CFI if I understand
> correctly. Is there any way to make such "polymorphic" callbacks with
> type-checking done via macros coexist with CFI? I mean, it's a bit sad
> that in order to have the sanity checks done by CFI, one has to force
> everything through functions that take void* instead of the type that
> they really act on.

Yeah, that'll light up CFI. ;) Why not stick with the existing standard
of callbacks, which is to pass the object pointer the callback is
attached to (as done with timer_struct, etc)?

As in the prototype should be just

	void callback(struct device *dev);

or if we absolutely must have a "data" argument (it'd be better to have
the data directly associated with the struct device):

	void callback(struct device *dev, void *data);

-- 
Kees Cook

  reply	other threads:[~2021-03-18 21:59 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-09 23:59 [RFC PATCH] devres: better type safety with devm_*_action() Rasmus Villemoes
2021-03-18 20:30 ` Rasmus Villemoes
2021-03-18 21:58   ` Kees Cook [this message]
  -- strict thread matches above, loose matches on Subject: below --
2021-03-10  3:53 kernel test robot
2021-03-10  7:00 kernel test robot
2021-03-10  7:37 kernel test robot
2021-03-10  7:52 kernel test robot
2021-03-10  8:58 kernel test robot
2021-03-10  9:19 kernel test robot
2021-03-10 10:46 kernel test robot
2021-03-10 11:17 kernel test robot
2021-03-10 14:08 kernel test robot
2021-03-10 15:38 kernel test robot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=202103181455.E6FE393@keescook \
    --to=keescook@chromium.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@rasmusvillemoes.dk \
    --cc=nathan@kernel.org \
    --cc=ndesaulniers@google.com \
    --cc=rafael@kernel.org \
    --cc=samitolvanen@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.