Commit 43acb728 authored by Johan Hovold's avatar Johan Hovold Committed by Mauro Carvalho Chehab

media: davinci: vpif: fix use-after-free on driver unbind

The driver allocates and registers two platform device structures during
probe, but the devices were never deregistered on driver unbind.

This results in a use-after-free on driver unbind as the device
structures were allocated using devres and would be freed by driver
core when remove() returns.

Fix this by adding the missing deregistration calls to the remove()
callback and failing probe on registration errors.

Note that the platform device structures must be freed using a proper
release callback to avoid leaking associated resources like device
names.

Fixes: 479f7a11 ("[media] davinci: vpif: adaptions for DT support")
Cc: stable@vger.kernel.org      # 4.12
Cc: Kevin Hilman <khilman@baylibre.com>
Signed-off-by: default avatarJohan Hovold <johan@kernel.org>
Reviewed-by: default avatarLad Prabhakar <prabhakar.csengg@gmail.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent d42b3ad1
...@@ -41,6 +41,11 @@ MODULE_ALIAS("platform:" VPIF_DRIVER_NAME); ...@@ -41,6 +41,11 @@ MODULE_ALIAS("platform:" VPIF_DRIVER_NAME);
#define VPIF_CH2_MAX_MODES 15 #define VPIF_CH2_MAX_MODES 15
#define VPIF_CH3_MAX_MODES 2 #define VPIF_CH3_MAX_MODES 2
struct vpif_data {
struct platform_device *capture;
struct platform_device *display;
};
DEFINE_SPINLOCK(vpif_lock); DEFINE_SPINLOCK(vpif_lock);
EXPORT_SYMBOL_GPL(vpif_lock); EXPORT_SYMBOL_GPL(vpif_lock);
...@@ -423,17 +428,31 @@ int vpif_channel_getfid(u8 channel_id) ...@@ -423,17 +428,31 @@ int vpif_channel_getfid(u8 channel_id)
} }
EXPORT_SYMBOL(vpif_channel_getfid); EXPORT_SYMBOL(vpif_channel_getfid);
static void vpif_pdev_release(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
kfree(pdev);
}
static int vpif_probe(struct platform_device *pdev) static int vpif_probe(struct platform_device *pdev)
{ {
static struct resource *res_irq; static struct resource *res_irq;
struct platform_device *pdev_capture, *pdev_display; struct platform_device *pdev_capture, *pdev_display;
struct device_node *endpoint = NULL; struct device_node *endpoint = NULL;
struct vpif_data *data;
int ret; int ret;
vpif_base = devm_platform_ioremap_resource(pdev, 0); vpif_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(vpif_base)) if (IS_ERR(vpif_base))
return PTR_ERR(vpif_base); return PTR_ERR(vpif_base);
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
platform_set_drvdata(pdev, data);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
pm_runtime_get(&pdev->dev); pm_runtime_get(&pdev->dev);
...@@ -461,49 +480,75 @@ static int vpif_probe(struct platform_device *pdev) ...@@ -461,49 +480,75 @@ static int vpif_probe(struct platform_device *pdev)
goto err_put_rpm; goto err_put_rpm;
} }
pdev_capture = devm_kzalloc(&pdev->dev, sizeof(*pdev_capture), pdev_capture = kzalloc(sizeof(*pdev_capture), GFP_KERNEL);
GFP_KERNEL); if (!pdev_capture) {
if (pdev_capture) { ret = -ENOMEM;
pdev_capture->name = "vpif_capture"; goto err_put_rpm;
pdev_capture->id = -1;
pdev_capture->resource = res_irq;
pdev_capture->num_resources = 1;
pdev_capture->dev.dma_mask = pdev->dev.dma_mask;
pdev_capture->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
pdev_capture->dev.parent = &pdev->dev;
platform_device_register(pdev_capture);
} else {
dev_warn(&pdev->dev, "Unable to allocate memory for pdev_capture.\n");
} }
pdev_display = devm_kzalloc(&pdev->dev, sizeof(*pdev_display), pdev_capture->name = "vpif_capture";
GFP_KERNEL); pdev_capture->id = -1;
if (pdev_display) { pdev_capture->resource = res_irq;
pdev_display->name = "vpif_display"; pdev_capture->num_resources = 1;
pdev_display->id = -1; pdev_capture->dev.dma_mask = pdev->dev.dma_mask;
pdev_display->resource = res_irq; pdev_capture->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
pdev_display->num_resources = 1; pdev_capture->dev.parent = &pdev->dev;
pdev_display->dev.dma_mask = pdev->dev.dma_mask; pdev_capture->dev.release = vpif_pdev_release;
pdev_display->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
pdev_display->dev.parent = &pdev->dev; ret = platform_device_register(pdev_capture);
platform_device_register(pdev_display); if (ret)
} else { goto err_put_pdev_capture;
dev_warn(&pdev->dev, "Unable to allocate memory for pdev_display.\n");
pdev_display = kzalloc(sizeof(*pdev_display), GFP_KERNEL);
if (!pdev_display) {
ret = -ENOMEM;
goto err_put_pdev_capture;
} }
pdev_display->name = "vpif_display";
pdev_display->id = -1;
pdev_display->resource = res_irq;
pdev_display->num_resources = 1;
pdev_display->dev.dma_mask = pdev->dev.dma_mask;
pdev_display->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
pdev_display->dev.parent = &pdev->dev;
pdev_display->dev.release = vpif_pdev_release;
ret = platform_device_register(pdev_display);
if (ret)
goto err_put_pdev_display;
data->capture = pdev_capture;
data->display = pdev_display;
return 0; return 0;
err_put_pdev_display:
platform_device_put(pdev_display);
err_put_pdev_capture:
platform_device_put(pdev_capture);
err_put_rpm: err_put_rpm:
pm_runtime_put(&pdev->dev); pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
kfree(data);
return ret; return ret;
} }
static int vpif_remove(struct platform_device *pdev) static int vpif_remove(struct platform_device *pdev)
{ {
struct vpif_data *data = platform_get_drvdata(pdev);
if (data->capture)
platform_device_unregister(data->capture);
if (data->display)
platform_device_unregister(data->display);
pm_runtime_put(&pdev->dev); pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
kfree(data);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment