Skip to content

Commit

Permalink
iio: adc: cf_axi_adc_core: fix segfault on unbind
Browse files Browse the repository at this point in the history
If the converter driver is removed before us (note that can only happen
if someone does the unbind through sysfs), we will access a NULL
pointer when calling module_put(). To fix that, let's just save the
module owner field so that we can unconditionally call module_put(). In
order to do that, we now dynamically allocate 'struct axiadc_spidev'
object (which now holds a pointer to the owner field) so that we can
pass it to devm_add_action_or_reset().

Signed-off-by: Nuno Sa <[email protected]>
  • Loading branch information
nunojsa committed May 3, 2023
1 parent 136349d commit 5d8ee95
Showing 1 changed file with 18 additions and 12 deletions.
30 changes: 18 additions & 12 deletions drivers/iio/adc/cf_axi_adc_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,7 @@ static const struct iio_info axiadc_info = {
struct axiadc_spidev {
struct device_node *of_nspi;
struct device *dev_spi;
struct module *owner;
};

static int axiadc_attach_spi_client(struct device *dev, void *data)
Expand All @@ -867,6 +868,7 @@ static int axiadc_attach_spi_client(struct device *dev, void *data)
device_lock(dev);
if ((axiadc_spidev->of_nspi == dev->of_node) && dev->driver) {
axiadc_spidev->dev_spi = dev;
axiadc_spidev->owner = dev->driver->owner;
ret = 1;
}
device_unlock(dev);
Expand Down Expand Up @@ -1056,10 +1058,10 @@ int axiadc_append_attrs(struct iio_dev *indio_dev,

static void axiadc_release_converter(void *conv)
{
struct device *dev = conv;
struct axiadc_spidev *axiadc_spidev = conv;

put_device(dev);
module_put(dev->driver->owner);
put_device(axiadc_spidev->dev_spi);
module_put(axiadc_spidev->owner);
}

/**
Expand All @@ -1079,7 +1081,7 @@ static int axiadc_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct axiadc_state *st;
struct resource *mem;
struct axiadc_spidev axiadc_spidev;
struct axiadc_spidev *axiadc_spidev;
struct axiadc_converter *conv;
unsigned int config, skip = 1;
int ret;
Expand All @@ -1093,28 +1095,32 @@ static int axiadc_probe(struct platform_device *pdev)

info = id->data;

axiadc_spidev = devm_kzalloc(&pdev->dev, sizeof(*axiadc_spidev), GFP_KERNEL);
if (!axiadc_spidev)
return -ENOMEM;

/* Defer driver probe until matching spi
* converter driver is registered
*/
axiadc_spidev.of_nspi = of_parse_phandle(pdev->dev.of_node,
"spibus-connected", 0);
if (!axiadc_spidev.of_nspi) {
axiadc_spidev->of_nspi = of_parse_phandle(pdev->dev.of_node,
"spibus-connected", 0);
if (!axiadc_spidev->of_nspi) {
dev_err(&pdev->dev, "could not find spi node\n");
return -ENODEV;
}

ret = bus_for_each_dev(&spi_bus_type, NULL, &axiadc_spidev,
ret = bus_for_each_dev(&spi_bus_type, NULL, axiadc_spidev,
axiadc_attach_spi_client);
of_node_put(axiadc_spidev->of_nspi);
if (ret == 0)
return -EPROBE_DEFER;

if (!try_module_get(axiadc_spidev.dev_spi->driver->owner))
if (!try_module_get(axiadc_spidev->owner))
return -ENODEV;

get_device(axiadc_spidev.dev_spi);
get_device(axiadc_spidev->dev_spi);

ret = devm_add_action_or_reset(&pdev->dev, axiadc_release_converter, axiadc_spidev.dev_spi);
ret = devm_add_action_or_reset(&pdev->dev, axiadc_release_converter, axiadc_spidev);
if (ret)
return ret;

Expand All @@ -1134,7 +1140,7 @@ static int axiadc_probe(struct platform_device *pdev)
if (IS_ERR(st->regs))
return PTR_ERR(st->regs);

st->dev_spi = axiadc_spidev.dev_spi;
st->dev_spi = axiadc_spidev->dev_spi;

platform_set_drvdata(pdev, indio_dev);

Expand Down

0 comments on commit 5d8ee95

Please sign in to comment.