Merge branch 'wip/zdi-fixes-server-21.1-branch' into 'server-21.1-branch'

[server 21.1] Backport the latest batch of security issues

See merge request xorg/xserver!2229
This commit is contained in:
Peter Hutterer 2026-06-02 09:57:35 +10:00
commit 56febfe786
9 changed files with 122 additions and 62 deletions

View file

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

View file

@ -720,8 +720,29 @@ SyncChangeCounter(SyncCounter * pCounter, int64_t newval)
/* 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;

View file

@ -670,6 +670,10 @@ doListFontsAndAliases(ClientPtr client, LFclosurePtr c)
* 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, LFWIclosurePtr c)
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;
c->current.max_names = 1;

View file

@ -1144,8 +1144,7 @@ __glXDisp_GetFBConfigsSGIX(__GLXclientState * cl, GLbyte * pc)
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 * cl, GLbyte * pc)
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(__GLXclientState * cl, GLbyte * pc)
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 * cl, GLbyte * pc)
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(__GLXclientState * cl, GLbyte * pc)
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);
}

View file

@ -235,7 +235,7 @@ __glXDispSwap_GetFBConfigsSGIX(__GLXclientState * cl, GLbyte * pc)
__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(__GLXclientState * cl, GLbyte * pc)
__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(__GLXclientState * cl, GLbyte * pc)
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(__GLXclientState * cl, GLbyte * pc)
__GLX_DECLARE_SWAP_VARIABLES;
REQUEST_AT_LEAST_SIZE(xGLXDestroyWindowReq);
REQUEST_SIZE_MATCH(xGLXDestroyWindowReq);
__GLX_SWAP_INT(&req->glxwindow);
@ -723,7 +721,7 @@ __glXDispSwap_GetDrawableAttributes(__GLXclientState * cl, GLbyte * pc)
__GLX_DECLARE_SWAP_VARIABLES;
REQUEST_AT_LEAST_SIZE(xGLXGetDrawableAttributesReq);
REQUEST_SIZE_MATCH(xGLXGetDrawableAttributesReq);
__GLX_SWAP_SHORT(&req->length);
__GLX_SWAP_INT(&req->drawable);

View file

@ -563,15 +563,16 @@ do_get_buffers(DrawablePtr pDraw, int *width, int *height,
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 *width, int *height,
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 *width, int *height,
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 *width, int *height,
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 *width, int *height,
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 *width, int *height,
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 *width, int *height,
* 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;

View file

@ -57,7 +57,12 @@ 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;

View file

@ -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 */
}

View file

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