drm/i915: Validate VBT header before trusting it
authorChris Wilson <chris@chris-wilson.co.uk>
Fri, 18 Apr 2014 21:04:22 +0000 (18:04 -0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Mon, 5 May 2014 07:08:59 +0000 (09:08 +0200)
Be we read and chase pointers from the VBT, it is prudent to make sure
that those accesses are wholly contained within the MMIO region, or else
we may cause a kernel panic during boot.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@gmail.com>
Reviewed-by: Shobhit Kumar <shobhit.kumar@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/intel_bios.c

index 148d8a786c8c68fa4e906cb62f07bf5bb856235b..2945f57c53ee9a56cb59007c8f5e891e4df52bee 100644 (file)
@@ -1105,6 +1105,46 @@ static const struct dmi_system_id intel_no_opregion_vbt[] = {
        { }
 };
 
+static struct bdb_header *validate_vbt(char *base, size_t size,
+                                      struct vbt_header *vbt,
+                                      const char *source)
+{
+       size_t offset;
+       struct bdb_header *bdb;
+
+       if (vbt == NULL) {
+               DRM_DEBUG_DRIVER("VBT signature missing\n");
+               return NULL;
+       }
+
+       offset = (char *)vbt - base;
+       if (offset + sizeof(struct vbt_header) > size) {
+               DRM_DEBUG_DRIVER("VBT header incomplete\n");
+               return NULL;
+       }
+
+       if (memcmp(vbt->signature, "$VBT", 4)) {
+               DRM_DEBUG_DRIVER("VBT invalid signature\n");
+               return NULL;
+       }
+
+       offset += vbt->bdb_offset;
+       if (offset + sizeof(struct bdb_header) > size) {
+               DRM_DEBUG_DRIVER("BDB header incomplete\n");
+               return NULL;
+       }
+
+       bdb = (struct bdb_header *)(base + offset);
+       if (offset + bdb->bdb_size > size) {
+               DRM_DEBUG_DRIVER("BDB incomplete\n");
+               return NULL;
+       }
+
+       DRM_DEBUG_KMS("Using VBT from %s: %20s\n",
+                     source, vbt->signature);
+       return bdb;
+}
+
 /**
  * intel_parse_bios - find VBT and initialize settings from the BIOS
  * @dev: DRM device
@@ -1128,20 +1168,13 @@ intel_parse_bios(struct drm_device *dev)
        init_vbt_defaults(dev_priv);
 
        /* XXX Should this validation be moved to intel_opregion.c? */
-       if (!dmi_check_system(intel_no_opregion_vbt) && dev_priv->opregion.vbt) {
-               struct vbt_header *vbt = dev_priv->opregion.vbt;
-               if (memcmp(vbt->signature, "$VBT", 4) == 0) {
-                       DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n",
-                                        vbt->signature);
-                       bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset);
-               } else
-                       dev_priv->opregion.vbt = NULL;
-       }
+       if (!dmi_check_system(intel_no_opregion_vbt) && dev_priv->opregion.vbt)
+               bdb = validate_vbt((char *)dev_priv->opregion.header, OPREGION_SIZE,
+                                  (struct vbt_header *)dev_priv->opregion.vbt,
+                                  "OpRegion");
 
        if (bdb == NULL) {
-               struct vbt_header *vbt = NULL;
-               size_t size;
-               int i;
+               size_t i, size;
 
                bios = pci_map_rom(pdev, &size);
                if (!bios)
@@ -1149,19 +1182,18 @@ intel_parse_bios(struct drm_device *dev)
 
                /* Scour memory looking for the VBT signature */
                for (i = 0; i + 4 < size; i++) {
-                       if (!memcmp(bios + i, "$VBT", 4)) {
-                               vbt = (struct vbt_header *)(bios + i);
+                       if (memcmp(bios + i, "$VBT", 4) == 0) {
+                               bdb = validate_vbt(bios, size,
+                                                  (struct vbt_header *)(bios + i),
+                                                  "PCI ROM");
                                break;
                        }
                }
 
-               if (!vbt) {
-                       DRM_DEBUG_DRIVER("VBT signature missing\n");
+               if (!bdb) {
                        pci_unmap_rom(pdev, bios);
                        return -1;
                }
-
-               bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
        }
 
        /* Grab useful general definitions */