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;
         }
