From 84d53bf2f02c7ed8bfd2a0cb214614fec7deedc3 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Thu, 6 Aug 2026 11:26:48 +0200 Subject: [PATCH] miext/sync: Handle SIGBUS from truncated shm fences A client can truncate the shared memory backing a DRI3/xshmfence sync fence and cause SIGBUS when the server accesses it. Register the mapping with busfault and wrap xshmfence accesses with sigsetjmp so truncation unwinds safely, then discard the mapping while keeping the fence XID. Signed-off-by: Olivier Fourdan Assisted-by: AI Closes: https://gitlab.freedesktop.org/xorg/xserver/-/work_items/1911 Part-of: (cherry picked from commit cd083d96593a903edd4b6bc5adf270a23c674cfe) --- miext/sync/misyncshm.c | 128 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/miext/sync/misyncshm.c b/miext/sync/misyncshm.c index f48533dd1..e74ded7e9 100644 --- a/miext/sync/misyncshm.c +++ b/miext/sync/misyncshm.c @@ -27,8 +27,10 @@ #include #include +#include "os/busfault.h" #include "os/osdep.h" +#include "os.h" #include "scrnintstr.h" #include "misync_priv.h" #include "misyncstr.h" @@ -41,18 +43,106 @@ static DevPrivateKeyRec syncShmFencePrivateKey; typedef struct _SyncShmFencePrivate { struct xshmfence *fence; int fd; +#ifdef BUSFAULT + struct busfault *busfault; +#endif } SyncShmFencePrivateRec, *SyncShmFencePrivatePtr; #define SYNC_FENCE_PRIV(pFence) \ (SyncShmFencePrivatePtr) dixLookupPrivate(&(pFence)->devPrivates, &syncShmFencePrivateKey) +#ifdef BUSFAULT + +#include + +#define SYNC_SHM_FENCE_ACCESS(pFence, pPriv, call) \ + do { \ + if ((pPriv)->fence && (pPriv)->busfault) { \ + sigjmp_buf jmp; \ + if (sigsetjmp(jmp, 1) == 0) { \ + busfault_set_jmp((pPriv)->busfault, &jmp); \ + call; \ + busfault_set_jmp((pPriv)->busfault, NULL); \ + } else { \ + ErrorF("shared memory fence 0x%x truncated by client\n",\ + (unsigned int) (pFence)->sync.id); \ + miSyncShmFenceDiscard(pFence); \ + } \ + } else if ((pPriv)->fence) { \ + call; \ + } \ + } while (0) + + +/* Cover the full mmap VMA; xshmfence maps a small object which the + * kernel rounds up to a page. */ +static unsigned int pagesize; + +static void +initPageSize(void) +{ + if (!pagesize) { +#ifdef _SC_PAGESIZE + pagesize = sysconf(_SC_PAGESIZE); +#else + pagesize = getpagesize(); +#endif + } +} + +static void +miSyncShmFenceDiscard(SyncFence *pFence) +{ + SyncShmFencePrivatePtr pPriv = SYNC_FENCE_PRIV(pFence); + + if (pPriv->busfault) { + busfault_unregister(pPriv->busfault); + pPriv->busfault = NULL; + } + if (pPriv->fence) { + xshmfence_unmap_shm(pPriv->fence); + pPriv->fence = NULL; + close(pPriv->fd); + } +} + +static void +miSyncShmFenceBusfaultNotify(void *context) +{ + SyncFence *pFence = context; + + ErrorF("shared memory fence 0x%x truncated by client\n", + (unsigned int) pFence->sync.id); + /* Drop the mapping; the fence XID remains and further ops use + * the in-server SyncFence state only. */ + miSyncShmFenceDiscard(pFence); +} + +static void +miSyncShmFenceRegisterBusfault(SyncFence *pFence) +{ + SyncShmFencePrivatePtr pPriv = SYNC_FENCE_PRIV(pFence); + + pPriv->busfault = busfault_register_mmap(pPriv->fence, pagesize, + miSyncShmFenceBusfaultNotify, + pFence); + if (!pPriv->busfault) + ErrorF("shared memory fence 0x%x could not register for truncation detection\n", + (unsigned int) pFence->sync.id); +} +#endif /* BUSFAULT */ + static void miSyncShmFenceSetTriggered(SyncFence * pFence) { SyncShmFencePrivatePtr pPriv = SYNC_FENCE_PRIV(pFence); +#ifdef BUSFAULT + SYNC_SHM_FENCE_ACCESS(pFence, pPriv, xshmfence_trigger(pPriv->fence)); +#else if (pPriv->fence) xshmfence_trigger(pPriv->fence); +#endif miSyncFenceSetTriggered(pFence); } @@ -61,8 +151,12 @@ miSyncShmFenceReset(SyncFence * pFence) { SyncShmFencePrivatePtr pPriv = SYNC_FENCE_PRIV(pFence); +#ifdef BUSFAULT + SYNC_SHM_FENCE_ACCESS(pFence, pPriv, xshmfence_reset(pPriv->fence)); +#else if (pPriv->fence) xshmfence_reset(pPriv->fence); +#endif miSyncFenceReset(pFence); } @@ -71,6 +165,24 @@ miSyncShmFenceCheckTriggered(SyncFence * pFence) { SyncShmFencePrivatePtr pPriv = SYNC_FENCE_PRIV(pFence); +#ifdef BUSFAULT + if (pPriv->fence && pPriv->busfault) { + sigjmp_buf jmp; + if (sigsetjmp(jmp, 1) == 0) { + Bool result; + + busfault_set_jmp(pPriv->busfault, &jmp); + result = xshmfence_query(pPriv->fence); + busfault_set_jmp(pPriv->busfault, NULL); + return result; + } else { + ErrorF("shared memory fence 0x%x truncated by client\n", + (unsigned int) pFence->sync.id); + miSyncShmFenceDiscard(pFence); + return miSyncFenceCheckTriggered(pFence); + } + } +#endif if (pPriv->fence) return xshmfence_query(pPriv->fence); else @@ -114,9 +226,15 @@ miSyncShmScreenDestroyFence(ScreenPtr pScreen, SyncFence * pFence) SyncShmFencePrivatePtr pPriv = SYNC_FENCE_PRIV(pFence); if (pPriv->fence) { +#ifdef BUSFAULT + /* Wake waiters if possible; SIGBUS discards via longjmp. */ + SYNC_SHM_FENCE_ACCESS(pFence, pPriv, xshmfence_trigger(pPriv->fence)); + miSyncShmFenceDiscard(pFence); +#else xshmfence_trigger(pPriv->fence); xshmfence_unmap_shm(pPriv->fence); close(pPriv->fd); +#endif } miSyncScreenDestroyFence(pScreen, pFence); } @@ -131,6 +249,9 @@ miSyncShmCreateFenceFromFd(ScreenPtr pScreen, SyncFence *pFence, int fd, Bool in fd = os_move_fd(fd); pPriv->fence = xshmfence_map_shm(fd); if (pPriv->fence) { +#ifdef BUSFAULT + miSyncShmFenceRegisterBusfault(pFence); +#endif pPriv->fd = fd; return Success; } @@ -154,6 +275,9 @@ miSyncShmGetFenceFd(ScreenPtr pScreen, SyncFence *pFence) close (pPriv->fd); return -1; } +#ifdef BUSFAULT + miSyncShmFenceRegisterBusfault(pFence); +#endif } return pPriv->fd; } @@ -177,6 +301,10 @@ Bool miSyncShmScreenInit(ScreenPtr pScreen) return FALSE; } +#ifdef BUSFAULT + initPageSize(); +#endif + funcs = miSyncGetScreenFuncs(pScreen); funcs->CreateFence = miSyncShmScreenCreateFence;