[PATCH] mgag200: Fix NULL dereference on backout due to VRAMreservation failure

From: Don Morris
Date: Fri Oct 19 2012 - 10:27:33 EST


Based on code flow analysis and the reported Oops, the mgag200 driver
will attempt to call drm_mode_config_cleanup() as part of using the
unload function for general cleanup. However, if an error is encountered
during driver load before drm_mode_config_init() is done, there's nothing
to cleanup.

This fix introduces a core cleanup method which can be used to skip
cleanup paths which have not yet been done but which can still be called
from the general unload function.

Reported by: Pawel Sikora <pawel.sikora@xxxxxxxx>

Signed-off-by: Don Morris <don.morris@xxxxxx>
---
drivers/gpu/drm/mgag200/mgag200_main.c | 42 ++++++++++++++++++++++++++-----
1 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
index d6a1aae..022e9e5 100644
--- a/drivers/gpu/drm/mgag200/mgag200_main.c
+++ b/drivers/gpu/drm/mgag200/mgag200_main.c
@@ -210,16 +210,41 @@ void mgag200_device_fini(struct mga_device *mdev)
mga_vram_fini(mdev);
}

+enum mgag200_init_state {
+ MGAG200_CLEAR = 0x0,
+ MGAG200_DEV_INIT = 0x1,
+ MGAG200_MM_INIT = 0x2
+};
+
+static void mgag200_driver_cleanup(struct drm_device *dev,
+ enum mgag200_init_state state)
+{
+ struct mga_device *mdev = dev->dev_private;
+
+ BUG_ON(mdev == NULL);
+
+ if (state & MGAG200_MM_INIT)
+ mgag200_mm_fini(mdev);
+
+ if (state & MGAG200_DEV_INIT)
+ mgag200_device_fini(mdev);
+
+ kfree(mdev);
+ dev->dev_private = NULL;
+
+ return;
+}
+
/*
* Functions here will be called by the core once it's bound the driver to
* a PCI device
*/

-
int mgag200_driver_load(struct drm_device *dev, unsigned long flags)
{
struct mga_device *mdev;
int r;
+ enum mgag200_init_state state = MGAG200_CLEAR;

mdev = kzalloc(sizeof(struct mga_device), GFP_KERNEL);
if (mdev == NULL)
@@ -232,10 +257,12 @@ int mgag200_driver_load(struct drm_device *dev, unsigned long flags)
dev_err(&dev->pdev->dev, "Fatal error during GPU init: %d\n", r);
goto out;
}
+ state = MGAG200_DEV_INIT;
r = mgag200_mm_init(mdev);
if (r)
goto out;

+ state = (state | MGAG200_MM_INIT);
drm_mode_config_init(dev);
dev->mode_config.funcs = (void *)&mga_mode_funcs;
dev->mode_config.min_width = 0;
@@ -247,8 +274,12 @@ int mgag200_driver_load(struct drm_device *dev, unsigned long flags)
if (r)
dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r);
out:
- if (r)
- mgag200_driver_unload(dev);
+ if (r) {
+ if (state & (MGAG200_MM_INIT|MGAG200_DEV_INIT))
+ mgag200_driver_unload(dev);
+ else
+ mgag200_driver_cleanup(dev, state);
+ }
return r;
}

@@ -261,10 +292,7 @@ int mgag200_driver_unload(struct drm_device *dev)
mgag200_modeset_fini(mdev);
mgag200_fbdev_fini(mdev);
drm_mode_config_cleanup(dev);
- mgag200_mm_fini(mdev);
- mgag200_device_fini(mdev);
- kfree(mdev);
- dev->dev_private = NULL;
+ mgag200_driver_cleanup(dev, MGAG200_MM_INIT|MGAG200_DEV_INIT);
return 0;
}

--
1.7.9.111.gf3fb0.dirty


--------------050301060807020404060605--
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/