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>
This commit is contained in:
Eugeni Dodonov 2011-11-10 13:55:15 -02:00 committed by Keith Packard
parent 03d00ac53f
commit 4ed0b57745
2 changed files with 11 additions and 0 deletions

View File

@ -1454,6 +1454,14 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
diff1 = now - dev_priv->last_time1; diff1 = now - dev_priv->last_time1;
/* Prevent division-by-zero if we are asking too fast.
* Also, we don't get interesting results if we are polling
* faster than once in 10ms, so just return the saved value
* in such cases.
*/
if (diff1 <= 10)
return dev_priv->chipset_power;
count1 = I915_READ(DMIEC); count1 = I915_READ(DMIEC);
count2 = I915_READ(DDREC); count2 = I915_READ(DDREC);
count3 = I915_READ(CSIEC); count3 = I915_READ(CSIEC);
@ -1484,6 +1492,8 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
dev_priv->last_count1 = total_count; dev_priv->last_count1 = total_count;
dev_priv->last_time1 = now; dev_priv->last_time1 = now;
dev_priv->chipset_power = ret;
return ret; return ret;
} }

View File

@ -713,6 +713,7 @@ typedef struct drm_i915_private {
u64 last_count1; u64 last_count1;
unsigned long last_time1; unsigned long last_time1;
unsigned long chipset_power;
u64 last_count2; u64 last_count2;
struct timespec last_time2; struct timespec last_time2;
unsigned long gfx_power; unsigned long gfx_power;