>From bae2005b42747ce1d1bac7f34ab0a0458cf61acc Mon Sep 17 00:00:00 2001 From: Don Morris Date: Fri, 19 Oct 2012 07:27:33 -0700 Subject: [PATCH] mgag200: Fix NULL dereference on backout due to VRAM reservation failure To: don.morris@hp.com 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 Signed-off-by: Don Morris --- 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