|
| 1 | +From 7b437ebf64aa7cce60f4260aa506c1cd0a2ff725 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Olivier Fourdan <ofourdan@redhat.com> |
| 3 | +Date: Wed, 18 Feb 2026 16:23:23 +0100 |
| 4 | +Subject: [PATCH] miext/sync: Fix use-after-free in miSyncTriggerFence() |
| 5 | + |
| 6 | +As reported by valgrind: |
| 7 | + |
| 8 | + == Invalid read of size 8 |
| 9 | + == at 0x568C14: miSyncTriggerFence (misync.c:140) |
| 10 | + == by 0x540688: ProcSyncTriggerFence (sync.c:1957) |
| 11 | + == by 0x540CCC: ProcSyncDispatch (sync.c:2152) |
| 12 | + == by 0x4A28C5: Dispatch (dispatch.c:553) |
| 13 | + == by 0x4B0B24: dix_main (main.c:274) |
| 14 | + == by 0x42915E: main (stubmain.c:34) |
| 15 | + == Address 0x17e35488 is 8 bytes inside a block of size 16 free'd |
| 16 | + == at 0x4843E43: free (vg_replace_malloc.c:990) |
| 17 | + == by 0x53D683: SyncDeleteTriggerFromSyncObject (sync.c:169) |
| 18 | + == by 0x53F14D: FreeAwait (sync.c:1208) |
| 19 | + == by 0x4DFB06: doFreeResource (resource.c:888) |
| 20 | + == by 0x4DFC59: FreeResource (resource.c:918) |
| 21 | + == by 0x53E349: SyncAwaitTriggerFired (sync.c:701) |
| 22 | + == by 0x568C52: miSyncTriggerFence (misync.c:142) |
| 23 | + == by 0x540688: ProcSyncTriggerFence (sync.c:1957) |
| 24 | + == by 0x540CCC: ProcSyncDispatch (sync.c:2152) |
| 25 | + == by 0x4A28C5: Dispatch (dispatch.c:553) |
| 26 | + == by 0x4B0B24: dix_main (main.c:274) |
| 27 | + == by 0x42915E: main (stubmain.c:34) |
| 28 | + == Block was alloc'd at |
| 29 | + == at 0x4840B26: malloc (vg_replace_malloc.c:447) |
| 30 | + == by 0x5E50E1: XNFalloc (utils.c:1129) |
| 31 | + == by 0x53D772: SyncAddTriggerToSyncObject (sync.c:206) |
| 32 | + == by 0x53DCA8: SyncInitTrigger (sync.c:414) |
| 33 | + == by 0x5409C7: ProcSyncAwaitFence (sync.c:2089) |
| 34 | + == by 0x540D04: ProcSyncDispatch (sync.c:2160) |
| 35 | + == by 0x4A28C5: Dispatch (dispatch.c:553) |
| 36 | + == by 0x4B0B24: dix_main (main.c:274) |
| 37 | + == by 0x42915E: main (stubmain.c:34) |
| 38 | + |
| 39 | +When walking the list of fences to trigger, miSyncTriggerFence() may |
| 40 | +call TriggerFence() for the current trigger, which end up calling the |
| 41 | +function SyncAwaitTriggerFired(). |
| 42 | + |
| 43 | +SyncAwaitTriggerFired() frees the entire await resource, which removes |
| 44 | +all triggers from that await - including pNext which may be another |
| 45 | +trigger from the same await attached to the same fence. |
| 46 | + |
| 47 | +On the next iteration, ptl = pNext points to freed memory... |
| 48 | + |
| 49 | +To avoid the issue, we need to restart the iteration from the beginning |
| 50 | +of the list each time a trigger fires, since the callback can modify the |
| 51 | +list. |
| 52 | + |
| 53 | +CVE-2026-34001, ZDI-CAN-28706 |
| 54 | + |
| 55 | +This vulnerability was discovered by: |
| 56 | +Jan-Niklas Sohn working with TrendAI Zero Day Initiative |
| 57 | + |
| 58 | +Signed-off-by: Olivier Fourdan <ofourdan@redhat.com> |
| 59 | +Acked-by: Peter Hutterer <peter.hutterer@who-t.net> |
| 60 | +Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2176> |
| 61 | +Signed-off-by: rpm-build <rpm-build> |
| 62 | +Upstream-reference: https://gitlab.freedesktop.org/xorg/xserver/-/commit/f19ab94ba9c891d801231654267556dc7f32b5e0.patch |
| 63 | +--- |
| 64 | + miext/sync/misync.c | 18 ++++++++++++------ |
| 65 | + 1 file changed, 12 insertions(+), 6 deletions(-) |
| 66 | + |
| 67 | +diff --git a/miext/sync/misync.c b/miext/sync/misync.c |
| 68 | +index 48234ef..77b4659 100644 |
| 69 | +--- a/miext/sync/misync.c |
| 70 | ++++ b/miext/sync/misync.c |
| 71 | +@@ -131,16 +131,22 @@ miSyncDestroyFence(SyncFence * pFence) |
| 72 | + void |
| 73 | + miSyncTriggerFence(SyncFence * pFence) |
| 74 | + { |
| 75 | +- SyncTriggerList *ptl, *pNext; |
| 76 | ++ SyncTriggerList *ptl; |
| 77 | ++ Bool triggered; |
| 78 | + |
| 79 | + pFence->funcs.SetTriggered(pFence); |
| 80 | + |
| 81 | + /* run through triggers to see if any fired */ |
| 82 | +- for (ptl = pFence->sync.pTriglist; ptl; ptl = pNext) { |
| 83 | +- pNext = ptl->next; |
| 84 | +- if ((*ptl->pTrigger->CheckTrigger) (ptl->pTrigger, 0)) |
| 85 | +- (*ptl->pTrigger->TriggerFired) (ptl->pTrigger); |
| 86 | +- } |
| 87 | ++ do { |
| 88 | ++ triggered = FALSE; |
| 89 | ++ for (ptl = pFence->sync.pTriglist; ptl; ptl = ptl->next) { |
| 90 | ++ if ((*ptl->pTrigger->CheckTrigger) (ptl->pTrigger, 0)) { |
| 91 | ++ (*ptl->pTrigger->TriggerFired) (ptl->pTrigger); |
| 92 | ++ triggered = TRUE; |
| 93 | ++ break; |
| 94 | ++ } |
| 95 | ++ } |
| 96 | ++ } while (triggered); |
| 97 | + } |
| 98 | + |
| 99 | + SyncScreenFuncsPtr |
| 100 | +-- |
| 101 | +2.45.4 |
| 102 | + |
0 commit comments