aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorEugeni Dodonov <eugeni.dodonov@intel.com>2011-11-10 10:55:15 -0500
committerKeith Packard <keithp@keithp.com>2011-12-16 11:49:56 -0500
commit4ed0b577457eb6aeb7cdc7e7316576e63d15abb2 (patch)
tree9ecf2ddc9ff64ce9a10e3e2b8827a9e7e2b34f8f /drivers
parent03d00ac53f9bcde06ff7e33d6676083c18d569a4 (diff)
drm/i915: prevent division by zero when asking for chipset power
This prevents an in-kernel division by zero which happens when we are asking for i915_chipset_val too quickly, or within a race condition between the power monitoring thread and userspace accesses via debugfs. The issue can be reproduced easily via the following command: while ``; do cat /sys/kernel/debug/dri/0/i915_emon_status; done This is particularly dangerous because it can be triggered by a non-privileged user by just reading the debugfs entry. This issue was also found independently by Konstantin Belousov <kostikbel@gmail.com>, who proposed a similar patch. Reported-by: Konstantin Belousov <kostikbel@gmail.com> Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org> Acked-by: Keith Packard <keithp@keithp.com> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: <stable@vger.kernel.org> Signed-off-by: Eugeni Dodonov <eugeni.dodonov@intel.com> Signed-off-by: Keith Packard <keithp@keithp.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c10
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h1
2 files changed, 11 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index a9533c54c93c..a9ae374861e7 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1454,6 +1454,14 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
1454 1454
1455 diff1 = now - dev_priv->last_time1; 1455 diff1 = now - dev_priv->last_time1;
1456 1456
1457 /* Prevent division-by-zero if we are asking too fast.
1458 * Also, we don't get interesting results if we are polling
1459 * faster than once in 10ms, so just return the saved value
1460 * in such cases.
1461 */
1462 if (diff1 <= 10)
1463 return dev_priv->chipset_power;
1464
1457 count1 = I915_READ(DMIEC); 1465 count1 = I915_READ(DMIEC);
1458 count2 = I915_READ(DDREC); 1466 count2 = I915_READ(DDREC);
1459 count3 = I915_READ(CSIEC); 1467 count3 = I915_READ(CSIEC);
@@ -1484,6 +1492,8 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
1484 dev_priv->last_count1 = total_count; 1492 dev_priv->last_count1 = total_count;
1485 dev_priv->last_time1 = now; 1493 dev_priv->last_time1 = now;
1486 1494
1495 dev_priv->chipset_power = ret;
1496
1487 return ret; 1497 return ret;
1488} 1498}
1489 1499
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 8ba88cfc36de..39a72f642b33 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -713,6 +713,7 @@ typedef struct drm_i915_private {
713 713
714 u64 last_count1; 714 u64 last_count1;
715 unsigned long last_time1; 715 unsigned long last_time1;
716 unsigned long chipset_power;
716 u64 last_count2; 717 u64 last_count2;
717 struct timespec last_time2; 718 struct timespec last_time2;
718 unsigned long gfx_power; 719 unsigned long gfx_power;