Hi,
On Tue, Apr 12, 2022 at 11:31:40AM +0200, Takashi Iwai wrote:
This is a small helper function to handle the error path more easily when an error happens during the probe for the device with the device-managed card. Since devres releases in the reverser order of the creations, usually snd_card_free() gets called at the last in the probe error path unless it already reached snd_card_register() calls. Due to this nature, when a driver expects the resource releases in card->private_free, this might be called too lately.
As a workaround, one should call the probe like:
static int __some_probe(...) { // do real probe.... }
static int some_probe(...) { return snd_card_free_on_error(dev, __some_probe(dev, ...)); }
so that the snd_card_free() is called explicitly at the beginning of the error path from the probe.
This function will be used in the upcoming fixes to address the regressions by devres usages.
Fixes: e8ad415b7a55 ("ALSA: core: Add managed card creation") Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de
include/sound/core.h | 1 + sound/core/init.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+)
diff --git a/include/sound/core.h b/include/sound/core.h index b7e9b58d3c78..6d4cc49584c6 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -284,6 +284,7 @@ int snd_card_disconnect(struct snd_card *card); void snd_card_disconnect_sync(struct snd_card *card); int snd_card_free(struct snd_card *card); int snd_card_free_when_closed(struct snd_card *card); +int snd_card_free_on_error(struct device *dev, int ret); void snd_card_set_id(struct snd_card *card, const char *id); int snd_card_register(struct snd_card *card); int snd_card_info_init(void); diff --git a/sound/core/init.c b/sound/core/init.c index 31ba7024e3ad..726a8353201f 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -209,6 +209,12 @@ static void __snd_card_release(struct device *dev, void *data)
- snd_card_register(), the very first devres action to call snd_card_free()
- is added automatically. In that way, the resource disconnection is assured
- at first, then released in the expected order.
- If an error happens at the probe before snd_card_register() is called and
- there have been other devres resources, you'd need to free the card manually
- via snd_card_free() call in the error; otherwise it may lead to UAF due to
- devres call orders. You can use snd_card_free_on_error() helper for
*/
- handling it more easily.
int snd_devm_card_new(struct device *parent, int idx, const char *xid, struct module *module, size_t extra_size, @@ -235,6 +241,28 @@ int snd_devm_card_new(struct device *parent, int idx, const char *xid, } EXPORT_SYMBOL_GPL(snd_devm_card_new);
+/**
- snd_card_free_on_error - a small helper for handling devm probe errors
- @dev: the managed device object
- @ret: the return code from the probe callback
- This function handles the explicit snd_card_free() call at the error from
- the probe callback. It's just a small helper for simplifying the error
- handling with the managed devices.
- */
+int snd_card_free_on_error(struct device *dev, int ret) +{
- struct snd_card *card;
- if (!ret)
return 0;
- card = devres_find(dev, __snd_card_release, NULL, NULL);
- if (card)
snd_card_free(card);
- return ret;
+} +EXPORT_SYMBOL_GPL(snd_card_free_on_error);
static int snd_card_init(struct snd_card *card, struct device *parent, int idx, const char *xid, struct module *module, size_t extra_size) -- 2.31.1
The idea looks good itself to me. On the other hand, the name 'snd_card_free_on_error()' is not so suitable since it assumes that 'snd_devm_card_new()' is called in advance, while we have another function, 'snd_card_new()'.
I think it better to use 'snd_devm_card_free_on_error()' instead since the function doesn't work as expected in the case of 'snd_card_new()' (the snd_card_free() is not called because nothing found in devres).
Regards
Takashi Sakamoto