diff --git a/miext/damage/damage.c b/miext/damage/damage.c index 48e6af6734..498ac910e1 100644 --- a/miext/damage/damage.c +++ b/miext/damage/damage.c @@ -1457,6 +1457,7 @@ damageRemoveDamage(DamagePtr * pPrev, DamagePtr pDamage) while (*pPrev) { if (*pPrev == pDamage) { *pPrev = pDamage->pNext; + pDamage->pListHead = NULL; return; } pPrev = &(*pPrev)->pNext; @@ -1467,6 +1468,16 @@ damageRemoveDamage(DamagePtr * pPrev, DamagePtr pDamage) #endif } +/* + * Link pDamage onto the list headed by pPrev, remembering that list so that damageRemoveDamage() can later unlink it + * from the same place. + * + * The list a window's damage belongs on is chosen by getDrawableDamageRef() and depends on the window pixmap, which can + * change while the damage is registered (Composite redirection, rootless drawing and Present flips all swap it). + * Re-deriving the list at removal time can therefore consult a different list than the damage was inserted onto, in + * which case the removal silently does nothing and leaves an unregistered -- possibly freed -- damage linked where + * damageRegionAppend() will dereference its NULL pDrawable. + */ static void damageInsertDamage(DamagePtr * pPrev, DamagePtr pDamage) { @@ -1481,6 +1492,7 @@ damageInsertDamage(DamagePtr * pPrev, DamagePtr pDamage) #endif pDamage->pNext = *pPrev; *pPrev = pDamage; + pDamage->pListHead = pPrev; } static Bool @@ -1553,11 +1565,9 @@ damageSetWindowPixmap(WindowPtr pWindow, PixmapPtr pPixmap) damageScrPriv(pScreen); if ((pDamage = damageGetWinPriv(pWindow))) { - PixmapPtr pOldPixmap = (*pScreen->GetWindowPixmap) (pWindow); - DamagePtr *pPrev = getPixmapDamageRef(pOldPixmap); - while (pDamage) { - damageRemoveDamage(pPrev, pDamage); + if (pDamage->pListHead) + damageRemoveDamage(pDamage->pListHead, pDamage); pDamage = pDamage->pNextWin; } } @@ -1721,6 +1731,7 @@ DamageCreate(DamageReportFunc damageReport, return 0; pDamage->pNext = 0; pDamage->pNextWin = 0; + pDamage->pListHead = NULL; RegionNull(&pDamage->damage); RegionNull(&pDamage->pendingDamage); @@ -1823,7 +1834,8 @@ DamageUnregister(DamagePtr pDamage) #endif } pDamage->pDrawable = 0; - damageRemoveDamage(getDrawableDamageRef(pDrawable), pDamage); + if (pDamage->pListHead) + damageRemoveDamage(pDamage->pListHead, pDamage); } void diff --git a/miext/damage/damagestr.h b/miext/damage/damagestr.h index 0d07872e56..19624192eb 100644 --- a/miext/damage/damagestr.h +++ b/miext/damage/damagestr.h @@ -35,6 +35,12 @@ typedef struct _damage { DamagePtr pNext; DamagePtr pNextWin; + /* + * Head of the pNext list this damage is currently linked on, or NULL if it is not linked on any list. Recorded at + * insertion time so that removal always unlinks from the list the damage actually went onto; see + * damageInsertDamage(). + */ + DamagePtr *pListHead; RegionRec damage; DamageReportLevel damageLevel;