Re: [PATCH v4 3/7] regulator: IRQ based event/error notification helpers

From: Matti Vaittinen
Date: Wed Apr 07 2021 - 01:02:21 EST


Morning Andy,

Thanks for the review! By the way, is it me or did your mail-client
spill this out using HTML?

On Wed, 2021-04-07 at 01:44 +0300, Andy Shevchenko wrote:
> On Tuesday, April 6, 2021, Matti Vaittinen <
> matti.vaittinen@xxxxxxxxxxxxxxxxx> wrote:

> > +static void die_loudly(const char *msg)
> > +{
> > + pr_emerg(msg);
>
> Oh là là, besides build bot complaints, this has serious security
> implications. Never do like this.

I'm not even trying to claim that was correct. And I did send a fixup -
sorry for this. I don't intend to do this again.

Now, when this is said - If you have a minute, please educate me.
Assuming we know all the callers and that all the callers use this as

die_loudly("foobarfoo\n");
- what is the exploit mechanism?

> > + BUG();
> > +}
> > +


> > +/**
> > + * regulator_irq_helper - register IRQ based regulator event/error
> > notifier
> > + *
> > + * @dev: device to which lifetime the helper's
> > lifetime is
> > + * bound.
> > + * @d: IRQ helper descriptor.
> > + * @irq: IRQ used to inform events/errors to be
> > notified.
> > + * @irq_flags: Extra IRQ flags to be OR's with the default
> > IRQF_ONESHOT
> > + * when requesting the (threaded) irq.
> > + * @common_errs: Errors which can be flagged by this IRQ for
> > all rdevs.
> > + * When IRQ is re-enabled these errors will be
> > cleared
> > + * from all associated regulators
> > + * @per_rdev_errs: Optional error flag array describing errors
> > specific
> > + * for only some of the regulators. These
> > errors will be
> > + * or'ed with common erros. If this is given
> > the array
> > + * should contain rdev_amount flags. Can be
> > set to NULL
> > + * if there is no regulator specific error
> > flags for this
> > + * IRQ.
> > + * @rdev: Array of regulators associated with this
> > IRQ.
> > + * @rdev_amount: Amount of regulators associated wit this
> > IRQ.
> > + */
> > +void *regulator_irq_helper(struct device *dev,
> > + const struct regulator_irq_desc *d, int
> > irq,
> > + int irq_flags, int common_errs, int
> > *per_rdev_errs,
> > + struct regulator_dev **rdev, int
> > rdev_amount)
> > +{
> > + struct regulator_irq *h;
> > + int ret;
> > +
> > + if (!rdev_amount || !d || !d->map_event || !d->name)
> > + return ERR_PTR(-EINVAL);
> > +
> > + if (irq <= 0) {
> > + dev_err(dev, "No IRQ\n");
> > + return ERR_PTR(-EINVAL);
>
> Why shadowing error code? Negative IRQ is anything but “no IRQ”.

This was a good point. The irq is passed here as parameter. From this
function's perspective the negative irq is invalid parameter - we don't
know how the caller has obtained it. Print could show the value
contained in irq though.

Now that you pointed this out I am unsure if this check is needed here.
If we check it, then I still think we should report -EINVAL for invalid
parameter. Other option is to just call the request_threaded_irq() -
log the IRQ request failure and return what request_threaded_irq()
returns. Do you think that would make sense?

> > +
> > +/**
> > + * regulator_irq_helper_cancel - drop IRQ based regulator
> > event/error notifier
> > + *
> > + * @handle: Pointer to handle returned by a successful
> > call to
> > + * regulator_irq_helper(). Will be NULLed upon
> > return.
> > + *
> > + * The associated IRQ is released and work is cancelled when the
> > function
> > + * returns.
> > + */
> > +void regulator_irq_helper_cancel(void **handle)
> > +{
> > + if (handle && *handle) {
>
> Can handle ever be NULL here ? (Yes, I understand that you export
> this)

To tell the truth - I am not sure. I *guess* that if we allow this to
be NULL, then one *could* implement a driver for IC where IRQs are
optional, in a way that when IRQs are supported the pointer to handle
is valid, when IRQs aren't supported the pointer is NULL. (Why) do you
think we should skip the check?

>
> > + struct regulator_irq *h = *handle;
> > +
> > + free_irq(h->irq, h);
> > + if (h->desc.irq_off_ms)
> > + cancel_delayed_work_sync(&h->isr_work);
> > +
> > + h = NULL;
> > + }
> > +}
> > +EXPORT_SYMBOL_GPL(regulator_irq_helper_cancel);
> > +
> > +static void regulator_irq_helper_drop(struct device *dev, void
> > *res)
> > +{
> > + regulator_irq_helper_cancel(res);
> > +}
> > +
> > +void *devm_regulator_irq_helper(struct device *dev,
> > + const struct regulator_irq_desc
> > *d, int irq,
> > + int irq_flags, int common_errs,
> > + int *per_rdev_errs,
> > + struct regulator_dev **rdev, int
> > rdev_amount)
> > +{
> > + void **ptr;
> > +
> > + ptr = devres_alloc(regulator_irq_helper_drop, sizeof(*ptr),
> > GFP_KERNEL);
> > + if (!ptr)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + *ptr = regulator_irq_helper(dev, d, irq, irq_flags,
> > common_errs,
> > + per_rdev_errs, rdev,
> > rdev_amount);
> > +
> > + if (IS_ERR(*ptr))
> > + devres_free(ptr);
> > + else
> > + devres_add(dev, ptr);
> > +
> > + return *ptr;
>
> Why not to use devm_add_action{_or_reset}()?

I just followed the same approach that has been used in other regulator
functions. (drivers/regulator/devres.c)
OTOH, the devm_add_action makes this little bit simpler so I'll convert
to use it.

Mark, do you have a reason of not using devm_add_action() in devres.c?
Should devm_add_action() be used in some other functions there? And
should this be moved to devres.c?

Best Regards
Matti Vaittinen