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 <ofourdan@redhat.com>
Assisted-by: AI
Closes: https://gitlab.freedesktop.org/xorg/xserver/-/work_items/1911
Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2270>
(cherry picked from commit cd083d9659)
This commit is contained in:
Olivier Fourdan 2026-08-06 11:26:48 +02:00 • committed by Enrico Weigelt
commit 84d53bf2f0

View file

@ -27,8 +27,10 @@
#include <unistd.h>
#include <X11/xshmfence.h>
#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 <setjmp.h>
#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;