History

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.

Eugeni Dodonov committed 15 years ago

Commit 4ed0b571 parent 03d00ac2 files · +11 −0Browse files

Unified · Split

2 files
  1. drivers/gpu/drm/i915/i915_dma.c +10 −0
  2. drivers/gpu/drm/i915/i915_drv.h +1 −0
drivers/gpu/drm/i915/i915_dma.c+10−0
@@ -1454,6 +1454,14 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) · 1453 unmodified lines
14541454
14551455 diff1 = now - dev_priv->last_time1;
14561456
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+
14571465 count1 = I915_READ(DMIEC);
14581466 count2 = I915_READ(DDREC);
14591467 count3 = I915_READ(CSIEC);
@@ -1484,6 +1492,8 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) · 24 unmodified lines
14841492 dev_priv->last_count1 = total_count;
14851493 dev_priv->last_time1 = now;
14861494
1495+ dev_priv->chipset_power = ret;
1496+
14871497 return ret;
14881498 }
14891499
drivers/gpu/drm/i915/i915_drv.h+1−0
@@ -713,6 +713,7 @@ typedef struct drm_i915_private { · 712 unmodified lines
713713
714714 u64 last_count1;
715715 unsigned long last_time1;
716+ unsigned long chipset_power;
716717 u64 last_count2;
717718 struct timespec last_time2;
718719 unsigned long gfx_power;