From: Takashi Iwai <tiwai@suse.de>
To: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Cc: yong.zhi@intel.com, alsa-devel@alsa-project.org,
broonie@kernel.org,
Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>,
tiwai@suse.com
Subject: Re: [PATCH] ALSA: hda: fix NULL pointer dereference during suspend
Date: Thu, 30 Jul 2020 15:07:15 +0200 [thread overview]
Message-ID: <s5hd04dcfss.wl-tiwai@suse.de> (raw)
In-Reply-To: <s5hft9ae266.wl-tiwai@suse.de>
On Wed, 29 Jul 2020 18:06:25 +0200,
Takashi Iwai wrote:
>
> On Wed, 29 Jul 2020 17:03:22 +0200,
> Ranjani Sridharan wrote:
> >
> > On Wed, 2020-07-29 at 09:48 +0200, Takashi Iwai wrote:
> > > On Wed, 29 Jul 2020 01:10:11 +0200,
> > > Ranjani Sridharan wrote:
> > > > When the ASoC card registration fails and the codec component
> > > > driver
> > > > never probes, the codec device is not initialized and therefore
> > > > memory for codec->wcaps is not allocated. This results in a NULL
> > > > pointer
> > > > dereference when the codec driver suspend callback is invoked
> > > > during
> > > > system suspend. Fix this by returning without performing any
> > > > actions
> > > > during codec suspend/resume if the card was not registered
> > > > successfully.
> > > >
> > > > Reviewed-by: Pierre-Louis Bossart <
> > > > pierre-louis.bossart@linux.intel.com>
> > > > Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com
> > > > >
> > >
> > > The code changes look OK to apply, but I still wonder how the runtime
> > > PM gets invoked even if the device is not instantiated properly?
> > Hi Takashi,
> >
> > Its not runtime PM suspend but rather the system PM suspend callback
> > that is invoked when the system is suspended that ends up callling the
> > the runtime PM callback. So, the sequence is:
> > hda_codec_pm_suspend()
> > -> pm_runtime_force_suspend()
> > -> hda_codec_runtime_suspend()
>
> OK, but the problem is still same. The basic problem is that the
> hda_codec_driver_probe() is called for the hda_codec object that
> hasn't been initialized and bypasses to ext_ops.hdev_attach.
>
> So, we can factor out the fundamental part of
> snd_hda_codec_device_new() that is irrelevant with the card object and
> call it in hdac_hda_dev_probe().
I meant something like below (totally untested)
Takashi
---
diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h
index f4cc364d837f..1f01e4d6b923 100644
--- a/include/sound/hda_codec.h
+++ b/include/sound/hda_codec.h
@@ -303,10 +303,11 @@ struct hda_codec {
/*
* constructors
*/
-int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
- unsigned int codec_addr, struct hda_codec **codecp);
-int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
- unsigned int codec_addr, struct hda_codec *codec);
+int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+ struct hda_codec **codecp);
+int snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr,
+ struct hda_codec *codec);
+int snd_hda_codec_assign_card(struct hda_codec *codec);
int snd_hda_codec_configure(struct hda_codec *codec);
int snd_hda_codec_update_widgets(struct hda_codec *codec);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 40f3c175954d..3079d32ba64d 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -867,15 +867,13 @@ static void snd_hda_codec_dev_release(struct device *dev)
#define DEV_NAME_LEN 31
-static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card,
- unsigned int codec_addr, struct hda_codec **codecp)
+static int hda_codec_new(struct hda_bus *bus, unsigned int card_number,
+ unsigned int codec_addr, struct hda_codec **codecp)
{
char name[DEV_NAME_LEN];
struct hda_codec *codec;
int err;
- dev_dbg(card->dev, "%s: entry\n", __func__);
-
if (snd_BUG_ON(!bus))
return -EINVAL;
if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
@@ -885,7 +883,7 @@ static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card,
if (!codec)
return -ENOMEM;
- sprintf(name, "hdaudioC%dD%d", card->number, codec_addr);
+ sprintf(name, "hdaudioC%dD%d", card_number, codec_addr);
err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr);
if (err < 0) {
kfree(codec);
@@ -901,37 +899,41 @@ static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card,
/**
* snd_hda_codec_new - create a HDA codec
* @bus: the bus to assign
- * @card: card for this codec
* @codec_addr: the codec address
* @codecp: the pointer to store the generated codec
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
- unsigned int codec_addr, struct hda_codec **codecp)
+int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+ struct hda_codec **codecp)
{
int ret;
- ret = snd_hda_codec_device_init(bus, card, codec_addr, codecp);
+ ret = hda_codec_new(bus, bus->card->number, codec_addr, codecp);
if (ret < 0)
return ret;
- return snd_hda_codec_device_new(bus, card, codec_addr, *codecp);
+ ret = snd_hda_codec_device_init(bus, codec_addr, *codecp);
+ if (ret < 0)
+ goto error;
+
+ ret = snd_hda_codec_assign_card(*codecp);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+ error:
+ put_device(hda_codec_dev(*codecp));
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_hda_codec_new);
-int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
- unsigned int codec_addr, struct hda_codec *codec)
+int snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr,
+ struct hda_codec *codec)
{
- char component[31];
hda_nid_t fg;
int err;
- static const struct snd_device_ops dev_ops = {
- .dev_register = snd_hda_codec_dev_register,
- .dev_free = snd_hda_codec_dev_free,
- };
-
- dev_dbg(card->dev, "%s: entry\n", __func__);
if (snd_BUG_ON(!bus))
return -EINVAL;
@@ -942,7 +944,6 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
codec->core.exec_verb = codec_exec_verb;
codec->bus = bus;
- codec->card = card;
codec->addr = codec_addr;
mutex_init(&codec->spdif_mutex);
mutex_init(&codec->control_mutex);
@@ -965,47 +966,50 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
codec->power_jiffies = jiffies;
#endif
- snd_hda_sysfs_init(codec);
-
if (codec->bus->modelname) {
codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
- if (!codec->modelname) {
- err = -ENOMEM;
- goto error;
- }
+ if (!codec->modelname)
+ return -ENOMEM;
}
fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
err = read_widget_caps(codec, fg);
if (err < 0)
- goto error;
+ return err;
err = read_pin_defaults(codec);
if (err < 0)
- goto error;
+ return err;
/* power-up all before initialization */
hda_set_power_state(codec, AC_PWRST_D0);
codec->core.dev.power.power_state = PMSG_ON;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_device_init);
+
+int snd_hda_codec_assign_card(struct hda_codec *codec)
+{
+ static const struct snd_device_ops dev_ops = {
+ .dev_register = snd_hda_codec_dev_register,
+ .dev_free = snd_hda_codec_dev_free,
+ };
+ char component[31];
+
+ codec->card = codec->bus->card;
snd_hda_codec_proc_new(codec);
+ snd_hda_sysfs_init(codec);
+
snd_hda_create_hwdep(codec);
sprintf(component, "HDA:%08x,%08x,%08x", codec->core.vendor_id,
codec->core.subsystem_id, codec->core.revision_id);
- snd_component_add(card, component);
-
- err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops);
- if (err < 0)
- goto error;
-
- return 0;
+ snd_component_add(codec->card, component);
- error:
- put_device(hda_codec_dev(codec));
- return err;
+ return snd_device_new(codec->card, SNDRV_DEV_CODEC, codec, &dev_ops);
}
-EXPORT_SYMBOL_GPL(snd_hda_codec_device_new);
+EXPORT_SYMBOL_GPL(snd_hda_codec_assign_card);
/**
* snd_hda_codec_update_widgets - Refresh widget caps and pin defaults
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 80016b7b6849..e68ca57be30e 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -1213,6 +1213,7 @@ EXPORT_SYMBOL_GPL(azx_bus_init);
int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
{
struct hdac_bus *bus = azx_bus(chip);
+ struct hda_codec *codec;
int c, codecs, err;
codecs = 0;
@@ -1245,10 +1246,10 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
/* Then create codec instances */
for (c = 0; c < max_slots; c++) {
if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
- struct hda_codec *codec;
- err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec);
+ err = snd_hda_codec_new(&chip->bus, c, &codec);
if (err < 0)
continue;
+
codec->jackpoll_interval = chip->jackpoll_interval;
codec->beep_mode = chip->beep_mode;
codecs++;
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index 473efe9ef998..298d9c46d85d 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -417,18 +417,12 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
snd_hdac_display_power(hdev->bus,
HDA_CODEC_IDX_CONTROLLER, true);
- ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card,
- hdev->addr, hcodec);
+ hcodec->bus->card = dapm->card->snd_card;
+ ret = snd_hda_codec_assign_card(hcodec);
if (ret < 0) {
dev_err(&hdev->dev, "failed to create hda codec %d\n", ret);
goto error_no_pm;
}
- /*
- * Overwrite type to HDA_DEV_ASOC since it is a ASoC driver
- * hda_codec.c will check this flag to determine if unregister
- * device is needed.
- */
- hdev->type = HDA_DEV_ASOC;
/*
* snd_hda_codec_device_new decrements the usage count so call get pm
@@ -436,8 +430,6 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
*/
pm_runtime_get_noresume(&hdev->dev);
- hcodec->bus->card = dapm->card->snd_card;
-
ret = snd_hda_codec_set_name(hcodec, hcodec->preset->name);
if (ret < 0) {
dev_err(&hdev->dev, "name failed %s\n", hcodec->preset->name);
@@ -574,6 +566,7 @@ static int hdac_hda_dev_probe(struct hdac_device *hdev)
{
struct hdac_ext_link *hlink;
struct hdac_hda_priv *hda_pvt;
+ struct hda_codec *hcodec;
int ret;
/* hold the ref while we probe */
@@ -588,6 +581,18 @@ static int hdac_hda_dev_probe(struct hdac_device *hdev)
if (!hda_pvt)
return -ENOMEM;
+ ret = snd_hda_codec_device_init(hcodec->bus, hdev->addr,
+ &hda_pvt->codec);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Overwrite type to HDA_DEV_ASOC since it is a ASoC driver
+ * hda_codec.c will check this flag to determine if unregister
+ * device is needed.
+ */
+ hdev->type = HDA_DEV_ASOC;
+
/* ASoC specific initialization */
ret = devm_snd_soc_register_component(&hdev->dev,
&hdac_hda_codec, hdac_hda_dais,
next prev parent reply other threads:[~2020-07-30 13:08 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-07-28 23:10 [PATCH] ALSA: hda: fix NULL pointer dereference during suspend Ranjani Sridharan
2020-07-29 7:48 ` Takashi Iwai
2020-07-29 9:39 ` Mark Brown
2020-07-29 15:03 ` Ranjani Sridharan
2020-07-29 16:06 ` Takashi Iwai
2020-07-30 13:07 ` Takashi Iwai [this message]
2020-07-30 17:33 ` Ranjani Sridharan
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=s5hd04dcfss.wl-tiwai@suse.de \
--to=tiwai@suse.de \
--cc=alsa-devel@alsa-project.org \
--cc=broonie@kernel.org \
--cc=pierre-louis.bossart@linux.intel.com \
--cc=ranjani.sridharan@linux.intel.com \
--cc=tiwai@suse.com \
--cc=yong.zhi@intel.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.