untrusted comment: verify with openbsd-79-base.pub RWTSdNN9A3yvWIh1g3Kwp0Cho4VLmkoy3tsSp+KLNBbeElly0W2xAvtdO7tE7ar15kj8H6Ygnpbin1yZHD2daCRdhbNSn9rffAE= OpenBSD 7.9 errata 001, June 2, 2026: Multiple vulnerabilites in the X server dri2, sync, saver and Xkb extensions. Apply by doing: signify -Vep /etc/signify/openbsd-79-base.pub -x 001_xserver.patch.sig \ -m - | (cd /usr/xenocara && patch -p0) And then rebuild and install the X server: cd /usr/xenocara/xserver make -f Makefile.bsd-wrapper obj make -f Makefile.bsd-wrapper build Index: xserver/Xext/saver.c =================================================================== RCS file: /cvs/xenocara/xserver/Xext/saver.c,v diff -u -p -r1.22 saver.c --- xserver/Xext/saver.c 22 Jan 2023 09:44:41 -0000 1.22 +++ xserver/Xext/saver.c 26 May 2026 16:10:00 -0000 @@ -348,6 +348,9 @@ ScreenSaverFreeAttr(void *value, XID id) dixSaveScreens(serverClient, SCREEN_SAVER_FORCER, ScreenSaverActive); } CheckScreenPrivate(pScreen); + /* CheckScreenPrivate may have freed pPriv (same pattern as + * CreateSaverWindow fix for ZDI-CAN-30168). */ + pPriv = NULL; return TRUE; } @@ -479,6 +482,8 @@ CreateSaverWindow(ScreenPtr pScreen) UninstallSaverColormap(pScreen); pPriv->hasWindow = FALSE; CheckScreenPrivate(pScreen); + /* Re-fetch pPriv since CheckScreenPrivate may have freed it */ + pPriv = GetScreenPrivate(pScreen); } } Index: xserver/Xext/sync.c =================================================================== RCS file: /cvs/xenocara/xserver/Xext/sync.c,v diff -u -p -r1.24 sync.c --- xserver/Xext/sync.c 3 Nov 2025 09:57:22 -0000 1.24 +++ xserver/Xext/sync.c 26 May 2026 16:10:01 -0000 @@ -720,8 +720,29 @@ SyncChangeCounter(SyncCounter * pCounter /* run through triggers to see if any become true */ for (ptl = pCounter->sync.pTriglist; ptl; ptl = pnext) { pnext = ptl->next; - if ((*ptl->pTrigger->CheckTrigger) (ptl->pTrigger, oldval)) + if ((*ptl->pTrigger->CheckTrigger) (ptl->pTrigger, oldval)) { (*ptl->pTrigger->TriggerFired) (ptl->pTrigger); + /* TriggerFired may have called SyncDeleteTriggerFromSyncObject + * for sibling triggers in the same Await group, freeing their + * trigger list nodes - potentially including pnext. Verify + * pnext is still on the counter's trigger list; if not, + * restart from the list head. + * + * Unlike miSyncTriggerFence() we cannot use a do/while + * restart loop here: counter trigger lists may contain alarm + * triggers which are not removed after firing and would cause + * an infinite loop when delta is 0. + */ + if (pnext) { + SyncTriggerList *tmp; + for (tmp = pCounter->sync.pTriglist; tmp; tmp = tmp->next) { + if (tmp == pnext) + break; + } + if (!tmp) + pnext = pCounter->sync.pTriglist; + } + } } if (IsSystemCounter(pCounter)) { @@ -1162,9 +1183,12 @@ FreeCounter(void *env, XID id) SyncTriggerList *ptl, *pnext; /* tell all the counter's triggers that counter has been destroyed */ - for (ptl = pCounter->sync.pTriglist; ptl; ptl = pnext) { - (*ptl->pTrigger->CounterDestroyed) (ptl->pTrigger); - pnext = ptl->next; + nt_list_for_each_entry_safe(ptl, pnext, pCounter->sync.pTriglist, next) { + /* Remove it from the list first so CounterDestroyed + * callbacks have a valid list to iterate */ + pCounter->sync.pTriglist = pnext; + if (ptl->pTrigger) + (*ptl->pTrigger->CounterDestroyed) (ptl->pTrigger); free(ptl); /* destroy the trigger list as we go */ } if (IsSystemCounter(pCounter)) { @@ -1196,13 +1220,28 @@ FreeAwait(void *addr, XID id) for (numwaits = pAwaitUnion->header.num_waitconditions; numwaits; numwaits--, pAwait++) { - /* If the counter is being destroyed, FreeCounter will delete - * the trigger list itself, so don't do it here. + /* If the counter is being destroyed, FreeCounter/miSyncDestroyFence + * will delete the trigger list itself, so don't do it here. + * However, we must NULL out the pTrigger pointer in the trigger list + * node so the destroy loop knows not to dereference it - the backing + * SyncAwait memory is about to be freed below. */ SyncObject *pSync = pAwait->trigger.pSync; - if (pSync && !pSync->beingDestroyed) - SyncDeleteTriggerFromSyncObject(&pAwait->trigger); + if (pSync) { + if (!pSync->beingDestroyed) { + SyncDeleteTriggerFromSyncObject(&pAwait->trigger); + } else { + SyncTriggerList *ptl; + + nt_list_for_each_entry(ptl, pSync->pTriglist, next) { + if (ptl->pTrigger == &pAwait->trigger) { + ptl->pTrigger = NULL; + break; + } + } + } + } } free(pAwaitUnion); return Success; Index: xserver/dix/dixfonts.c =================================================================== RCS file: /cvs/xenocara/xserver/dix/dixfonts.c,v diff -u -p -r1.21 dixfonts.c --- xserver/dix/dixfonts.c 3 Nov 2025 09:57:23 -0000 1.21 +++ xserver/dix/dixfonts.c 26 May 2026 16:10:01 -0000 @@ -671,6 +671,10 @@ doListFontsAndAliases(ClientPtr client, * is BadFontName, indicating the alias resolution * is complete. */ + if (resolvedlen > XLFDMAXFONTNAMELEN) { + err = BadFontName; + goto ContBadFontName; + } memmove(tmp_pattern, resolved, resolvedlen); if (c->haveSaved) { char *tmpname; @@ -932,6 +936,10 @@ doListFontsWithInfo(ClientPtr client, LF c->savedName = XNFalloc(namelen + 1); memcpy(c->savedName, name, namelen + 1); aliascount = 20; + } + if (namelen > XLFDMAXFONTNAMELEN) { + err = BadFontName; + goto ContBadFontName; } memmove(c->current.pattern, name, namelen); c->current.patlen = namelen; Index: xserver/glx/glxcmds.c =================================================================== RCS file: /cvs/xenocara/xserver/glx/glxcmds.c,v diff -u -p -r1.21 glxcmds.c --- xserver/glx/glxcmds.c 16 Jan 2024 12:34:23 -0000 1.21 +++ xserver/glx/glxcmds.c 26 May 2026 16:10:01 -0000 @@ -1144,8 +1144,7 @@ __glXDisp_GetFBConfigsSGIX(__GLXclientSt ClientPtr client = cl->client; xGLXGetFBConfigsSGIXReq *req = (xGLXGetFBConfigsSGIXReq *) pc; - /* work around mesa bug, don't use REQUEST_SIZE_MATCH */ - REQUEST_AT_LEAST_SIZE(xGLXGetFBConfigsSGIXReq); + REQUEST_SIZE_MATCH(xGLXGetFBConfigsSGIXReq); return DoGetFBConfigs(cl, req->screen); } @@ -1366,9 +1365,7 @@ __glXDisp_DestroyPixmap(__GLXclientState ClientPtr client = cl->client; xGLXDestroyPixmapReq *req = (xGLXDestroyPixmapReq *) pc; - /* should be REQUEST_SIZE_MATCH, but mesa's glXDestroyPixmap used to set - * length to 3 instead of 2 */ - REQUEST_AT_LEAST_SIZE(xGLXDestroyPixmapReq); + REQUEST_SIZE_MATCH(xGLXDestroyPixmapReq); return DoDestroyDrawable(cl, req->glxpixmap, GLX_DRAWABLE_PIXMAP); } @@ -1524,14 +1521,8 @@ __glXDisp_ChangeDrawableAttributes(__GLX client->errorValue = req->numAttribs; return BadValue; } -#if 0 - /* mesa sends an additional 8 bytes */ + REQUEST_FIXED_SIZE(xGLXChangeDrawableAttributesReq, req->numAttribs << 3); -#else - if (((sizeof(xGLXChangeDrawableAttributesReq) + - (req->numAttribs << 3)) >> 2) < client->req_len) - return BadLength; -#endif return DoChangeDrawableAttributes(cl->client, req->drawable, req->numAttribs, (CARD32 *) (req + 1)); @@ -1598,8 +1589,7 @@ __glXDisp_DestroyWindow(__GLXclientState ClientPtr client = cl->client; xGLXDestroyWindowReq *req = (xGLXDestroyWindowReq *) pc; - /* mesa's glXDestroyWindow used to set length to 3 instead of 2 */ - REQUEST_AT_LEAST_SIZE(xGLXDestroyWindowReq); + REQUEST_SIZE_MATCH(xGLXDestroyWindowReq); return DoDestroyDrawable(cl, req->glxwindow, GLX_DRAWABLE_WINDOW); } @@ -1960,8 +1950,7 @@ __glXDisp_GetDrawableAttributes(__GLXcli ClientPtr client = cl->client; xGLXGetDrawableAttributesReq *req = (xGLXGetDrawableAttributesReq *) pc; - /* this should be REQUEST_SIZE_MATCH, but mesa sends an additional 4 bytes */ - REQUEST_AT_LEAST_SIZE(xGLXGetDrawableAttributesReq); + REQUEST_SIZE_MATCH(xGLXGetDrawableAttributesReq); return DoGetDrawableAttributes(cl, req->drawable); } Index: xserver/glx/glxcmdsswap.c =================================================================== RCS file: /cvs/xenocara/xserver/glx/glxcmdsswap.c,v diff -u -p -r1.9 glxcmdsswap.c --- xserver/glx/glxcmdsswap.c 27 Jul 2019 07:57:10 -0000 1.9 +++ xserver/glx/glxcmdsswap.c 26 May 2026 16:10:02 -0000 @@ -235,7 +235,7 @@ __glXDispSwap_GetFBConfigsSGIX(__GLXclie __GLX_DECLARE_SWAP_VARIABLES; - REQUEST_AT_LEAST_SIZE(xGLXGetFBConfigsSGIXReq); + REQUEST_SIZE_MATCH(xGLXGetFBConfigsSGIXReq); __GLX_SWAP_INT(&req->screen); return __glXDisp_GetFBConfigsSGIX(cl, pc); @@ -327,7 +327,7 @@ __glXDispSwap_DestroyPixmap(__GLXclientS __GLX_DECLARE_SWAP_VARIABLES; - REQUEST_AT_LEAST_SIZE(xGLXDestroyGLXPixmapReq); + REQUEST_SIZE_MATCH(xGLXDestroyGLXPixmapReq); __GLX_SWAP_SHORT(&req->length); __GLX_SWAP_INT(&req->glxpixmap); @@ -440,9 +440,7 @@ __glXDispSwap_ChangeDrawableAttributes(_ client->errorValue = req->numAttribs; return BadValue; } - if (((sizeof(xGLXChangeDrawableAttributesReq) + - (req->numAttribs << 3)) >> 2) < client->req_len) - return BadLength; + REQUEST_FIXED_SIZE(xGLXChangeDrawableAttributesReq, req->numAttribs << 3); attribs = (CARD32 *) (req + 1); __GLX_SWAP_INT_ARRAY(attribs, req->numAttribs << 1); @@ -514,7 +512,7 @@ __glXDispSwap_DestroyWindow(__GLXclientS __GLX_DECLARE_SWAP_VARIABLES; - REQUEST_AT_LEAST_SIZE(xGLXDestroyWindowReq); + REQUEST_SIZE_MATCH(xGLXDestroyWindowReq); __GLX_SWAP_INT(&req->glxwindow); @@ -723,7 +721,7 @@ __glXDispSwap_GetDrawableAttributes(__GL __GLX_DECLARE_SWAP_VARIABLES; - REQUEST_AT_LEAST_SIZE(xGLXGetDrawableAttributesReq); + REQUEST_SIZE_MATCH(xGLXGetDrawableAttributesReq); __GLX_SWAP_SHORT(&req->length); __GLX_SWAP_INT(&req->drawable); Index: xserver/hw/xfree86/dri2/dri2.c =================================================================== RCS file: /cvs/xenocara/xserver/hw/xfree86/dri2/dri2.c,v diff -u -p -r1.20 dri2.c --- xserver/hw/xfree86/dri2/dri2.c 2 Mar 2025 09:09:28 -0000 1.20 +++ xserver/hw/xfree86/dri2/dri2.c 26 May 2026 16:10:02 -0000 @@ -563,15 +563,16 @@ do_get_buffers(DrawablePtr pDraw, int *w DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw); DRI2ScreenPtr ds; DRI2BufferPtr *buffers; - int need_real_front = 0; - int need_fake_front = 0; - int have_fake_front = 0; + unsigned attachments_bitset = 0; + Bool need_real_front = FALSE; + Bool need_fake_front = FALSE; int front_format = 0; int dimensions_match; int buffers_changed = 0; int i; - if (!pPriv) { + if (!pPriv || + count > DRI2BufferHiz + 1) { *width = pDraw->width; *height = pDraw->height; *out_count = 0; @@ -583,7 +584,10 @@ do_get_buffers(DrawablePtr pDraw, int *w dimensions_match = (pDraw->width == pPriv->width) && (pDraw->height == pPriv->height); - buffers = calloc((count + 1), sizeof(buffers[0])); + /* Since we deduplicate attachments in the buffers array, there cannot be + * more entries than there are attachments. + */ + buffers = calloc((min(count, DRI2BufferHiz) + 1), sizeof(buffers[0])); if (!buffers) goto err_out; @@ -591,6 +595,14 @@ do_get_buffers(DrawablePtr pDraw, int *w const unsigned attachment = *(attachments++); const unsigned format = (has_format) ? *(attachments++) : 0; + if (attachment > DRI2BufferHiz) + goto err_out; + + if (attachments_bitset & (1u << attachment)) + continue; + + attachments_bitset |= 1u << attachment; + if (allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment, format, dimensions_match, &buffers[i])) buffers_changed = 1; @@ -598,34 +610,27 @@ do_get_buffers(DrawablePtr pDraw, int *w if (buffers[i] == NULL) goto err_out; - /* If the drawable is a window and the front-buffer is requested, - * silently add the fake front-buffer to the list of requested - * attachments. The counting logic in the loop accounts for the case - * where the client requests both the fake and real front-buffer. + /* In certain cases the (fake) front buffer is always needed, so return + * it even if the client failed to request it. + * The logic in & after the loop accounts for the case where the client + * does request the (fake) front buffer, to avoid returning it multiple + * times. */ if (attachment == DRI2BufferBackLeft) { - need_real_front++; + need_real_front = TRUE; front_format = format; } if (attachment == DRI2BufferFrontLeft) { - need_real_front--; front_format = format; - if (pDraw->type == DRAWABLE_WINDOW) { - need_fake_front++; - } - } - - if (pDraw->type == DRAWABLE_WINDOW) { - if (attachment == DRI2BufferFakeFrontLeft) { - need_fake_front--; - have_fake_front = 1; - } + if (pDraw->type == DRAWABLE_WINDOW) + need_fake_front = TRUE; } } - if (need_real_front > 0) { + if (need_real_front && + !(attachments_bitset & (1u << DRI2BufferFrontLeft))) { if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFrontLeft, front_format, dimensions_match, &buffers[i])) @@ -636,7 +641,8 @@ do_get_buffers(DrawablePtr pDraw, int *w i++; } - if (need_fake_front > 0) { + if (need_fake_front && + !(attachments_bitset & (1u << DRI2BufferFakeFrontLeft))) { if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFakeFrontLeft, front_format, dimensions_match, &buffers[i])) @@ -646,7 +652,7 @@ do_get_buffers(DrawablePtr pDraw, int *w goto err_out; i++; - have_fake_front = 1; + attachments_bitset |= 1u << DRI2BufferFakeFrontLeft; } *out_count = i; @@ -658,7 +664,8 @@ do_get_buffers(DrawablePtr pDraw, int *w * contents of the real front-buffer. This ensures correct operation of * applications that call glXWaitX before calling glDrawBuffer. */ - if (have_fake_front && buffers_changed) { + if (buffers_changed && + (attachments_bitset & (1u << DRI2BufferFakeFrontLeft))) { BoxRec box; RegionRec region; Index: xserver/include/closestr.h =================================================================== RCS file: /cvs/xenocara/xserver/include/closestr.h,v diff -u -p -r1.6 closestr.h --- xserver/include/closestr.h 27 Sep 2014 17:53:04 -0000 1.6 +++ xserver/include/closestr.h 26 May 2026 16:10:02 -0000 @@ -57,7 +57,13 @@ typedef struct _OFclosure { /* ListFontsWithInfo */ -#define XLFDMAXFONTNAMELEN 256 +/* libXfont2 allows font names/aliases up to MAXFONTNAMELEN (1024) bytes in + * fonts.alias files. The server's pattern buffers must be large enough to + * hold resolved alias targets returned by the font library. + * ZDI-CAN-30136 + */ +#define XLFDMAXFONTNAMELEN 1024 + typedef struct _LFWIstate { char pattern[XLFDMAXFONTNAMELEN]; int patlen; Index: xserver/miext/sync/misync.c =================================================================== RCS file: /cvs/xenocara/xserver/miext/sync/misync.c,v diff -u -p -r1.7 misync.c --- xserver/miext/sync/misync.c 14 Apr 2026 13:52:41 -0000 1.7 +++ xserver/miext/sync/misync.c 26 May 2026 16:10:02 -0000 @@ -115,10 +115,14 @@ miSyncDestroyFence(SyncFence * pFence) SyncScreenPrivPtr pScreenPriv = SYNC_SCREEN_PRIV(pScreen); SyncTriggerList *ptl, *pNext; - /* tell all the fence's triggers that the counter has been destroyed */ - for (ptl = pFence->sync.pTriglist; ptl; ptl = pNext) { - (*ptl->pTrigger->CounterDestroyed) (ptl->pTrigger); - pNext = ptl->next; + /* tell all the fence's triggers that the fence has been destroyed. + * Update pTriglist before each callback and free so that FreeAwait + * sees a valid list head when scanning for triggers to NULL out. + */ + nt_list_for_each_entry_safe(ptl, pNext, pFence->sync.pTriglist, next) { + pFence->sync.pTriglist = pNext; + if (ptl->pTrigger) + (*ptl->pTrigger->CounterDestroyed) (ptl->pTrigger); free(ptl); /* destroy the trigger list as we go */ } Index: xserver/xkb/xkb.c =================================================================== RCS file: /cvs/xenocara/xserver/xkb/xkb.c,v diff -u -p -r1.28 xkb.c --- xserver/xkb/xkb.c 14 Apr 2026 13:52:41 -0000 1.28 +++ xserver/xkb/xkb.c 26 May 2026 16:10:02 -0000 @@ -1617,6 +1617,11 @@ CheckKeyTypes(ClientPtr client, *nMapsRtrn = _XkbErrCode4(0x02, req->firstType, req->nTypes, 4); return 0; } + if (nMaps > XkbMaxLegalKeyCode + 1) { + *nMapsRtrn = _XkbErrCode4(0x02, req->firstType, req->nTypes, + XkbMaxLegalKeyCode + 1); + return 0; + } } else if (req->present & XkbKeyTypesMask) { nMaps = xkb->map->num_types; @@ -1648,7 +1653,7 @@ CheckKeyTypes(ClientPtr client, } n = i + req->firstType; width = wire->numLevels; - if (width < 1) { + if (width < 1 || width > XkbMaxShiftLevel) { *nMapsRtrn = _XkbErrCode3(0x04, n, width); return 0; }