⬢ DragonFlyBSD Kernel Audit
DF-4902 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/amd/amdgpu/iceland_ih.c
+++ b/sys/dev/drm/amd/amdgpu/iceland_ih.c
@@ -185,9 +185,18 @@
 static u32 iceland_ih_get_wptr(struct amdgpu_device *adev)
 {
 	u32 wptr, tmp;
 
 	wptr = le32_to_cpu(adev->wb.wb[adev->irq.ih.wptr_offs]);
+	/* IV entries are 16 bytes: a wptr that is not 16-byte aligned comes
+	 * from a hostile or faulting device and would permanently spin the
+	 * interrupt walk (the 16-byte-stepping rptr can never equal it).
+	 * Normalize with a warn.
+	 */
+	if ((wptr & ~IH_RB_WPTR__RB_OVERFLOW_MASK) & 0xF)
+		dev_warn(adev->dev, "IH wptr not 16-byte aligned: 0x%08X\n", wptr);
+	wptr &= ~0xF;
 
 	if (wptr & IH_RB_WPTR__RB_OVERFLOW_MASK) {
 		wptr &= ~IH_RB_WPTR__RB_OVERFLOW_MASK;
@@ -196,6 +205,10 @@
 		 * from the last not overwritten vector (wptr + 16). Hopefully
 		 * this should allow us to catchup.
 		 */
+		/* derive rptr from the aligned value so a 4-granular wptr
+		 * phase cannot park rptr within 12 bytes of the ring end
+		 * (out-of-bounds decode read)
+		 */
+		wptr &= ~0xF;
 		dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
 			wptr, adev->irq.ih.rptr, (wptr + 16) & adev->irq.ih.ptr_mask);
 		adev->irq.ih.rptr = (wptr + 16) & adev->irq.ih.ptr_mask;
(The bounded-walk half of the family fix lives in amdgpu_ih.c — see
findings/poc/DF-4887/fix.diff, which already patches the shared walker; the
two halves together close the family.)