]> Pileus Git - ~andy/linux/blobdiff - drivers/gpu/drm/drm_pci.c
drm: Fix use-after-free in the shadow-attache exit code
[~andy/linux] / drivers / gpu / drm / drm_pci.c
index 02679793c9e2e73d3d175136bbfb638a7cd0784f..f7af69bcf3f452aff52647d830e3f1497ad21dca 100644 (file)
@@ -262,16 +262,11 @@ static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
        return 0;
 }
 
-static int drm_pci_agp_init(struct drm_device *dev)
+static void drm_pci_agp_init(struct drm_device *dev)
 {
-       if (drm_core_has_AGP(dev)) {
+       if (drm_core_check_feature(dev, DRIVER_USE_AGP)) {
                if (drm_pci_device_is_agp(dev))
                        dev->agp = drm_agp_init(dev);
-               if (drm_core_check_feature(dev, DRIVER_REQUIRE_AGP)
-                   && (dev->agp == NULL)) {
-                       DRM_ERROR("Cannot initialize the agpgart module.\n");
-                       return -EINVAL;
-               }
                if (dev->agp) {
                        dev->agp->agp_mtrr = arch_phys_wc_add(
                                dev->agp->agp_info.aper_base,
@@ -279,15 +274,14 @@ static int drm_pci_agp_init(struct drm_device *dev)
                                1024 * 1024);
                }
        }
-       return 0;
 }
 
-static void drm_pci_agp_destroy(struct drm_device *dev)
+void drm_pci_agp_destroy(struct drm_device *dev)
 {
-       if (drm_core_has_AGP(dev) && dev->agp) {
+       if (dev->agp) {
                arch_phys_wc_del(dev->agp->agp_mtrr);
                drm_agp_clear(dev);
-               drm_agp_destroy(dev->agp);
+               kfree(dev->agp);
                dev->agp = NULL;
        }
 }
@@ -299,8 +293,6 @@ static struct drm_bus drm_pci_bus = {
        .set_busid = drm_pci_set_busid,
        .set_unique = drm_pci_set_unique,
        .irq_by_busid = drm_pci_irq_by_busid,
-       .agp_init = drm_pci_agp_init,
-       .agp_destroy = drm_pci_agp_destroy,
 };
 
 /**
@@ -338,17 +330,25 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                pci_set_drvdata(pdev, dev);
 
+       drm_pci_agp_init(dev);
+
        ret = drm_dev_register(dev, ent->driver_data);
        if (ret)
-               goto err_pci;
+               goto err_agp;
 
        DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
                 driver->name, driver->major, driver->minor, driver->patchlevel,
                 driver->date, pci_name(pdev), dev->primary->index);
 
+       /* No locking needed since shadow-attach is single-threaded since it may
+        * only be called from the per-driver module init hook. */
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               list_add_tail(&dev->legacy_dev_list, &driver->legacy_dev_list);
+
        return 0;
 
-err_pci:
+err_agp:
+       drm_pci_agp_destroy(dev);
        pci_disable_device(pdev);
 err_free:
        drm_dev_free(dev);
@@ -375,7 +375,6 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
 
        DRM_DEBUG("\n");
 
-       INIT_LIST_HEAD(&driver->device_list);
        driver->kdriver.pci = pdriver;
        driver->bus = &drm_pci_bus;
 
@@ -383,6 +382,7 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
                return pci_register_driver(pdriver);
 
        /* If not using KMS, fall back to stealth mode manual scanning. */
+       INIT_LIST_HEAD(&driver->legacy_dev_list);
        for (i = 0; pdriver->id_table[i].vendor != 0; i++) {
                pid = &pdriver->id_table[i];
 
@@ -452,6 +452,7 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
        return -1;
 }
 
+void drm_pci_agp_destroy(struct drm_device *dev) {}
 #endif
 
 EXPORT_SYMBOL(drm_pci_init);
@@ -465,8 +466,11 @@ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
        if (driver->driver_features & DRIVER_MODESET) {
                pci_unregister_driver(pdriver);
        } else {
-               list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item)
+               list_for_each_entry_safe(dev, tmp, &driver->legacy_dev_list,
+                                        legacy_dev_list) {
+                       list_del(&dev->legacy_dev_list);
                        drm_put_dev(dev);
+               }
        }
        DRM_INFO("Module unloaded\n");
 }