xserver/Xext
Peter Hutterer 182c23f780 saver: re-fetch screen private after CheckScreenPrivate in CreateSaverWindow
CreateSaverWindow stores pPriv (the ScreenSaverScreenPrivatePtr) in a local
variable via the SetupScreen macro at function entry. When an existing saver
window is being replaced, the function sets pPriv->hasWindow = FALSE and
calls CheckScreenPrivate(). If at this point pPriv->attr is NULL (cleared
by a prior UnsetAttributes call), pPriv->events is NULL, and
pPriv->installedMap is None, then CheckScreenPrivate determines the screen
private is unused, frees it, and sets the screen private pointer to NULL.

The function then continues to dereference the now-freed pPriv on the very
next line (pPriv->attr), resulting in a use-after-free. On glibc 2.34+,
the tcache key at offset 8 within the freed block makes pPriv->attr appear
non-NULL, causing the function to continue operating on garbage data and
eventually crash.

The attack sequence is:
  1. SetAttributes (creates pPriv with pPriv->attr set)
  2. ForceScreenSaver(Active) (creates saver window, pPriv->hasWindow=TRUE)
  3. UnsetAttributes (sets pPriv->attr = NULL)
  4. ForceScreenSaver(Active) (re-enters CreateSaverWindow → UAF)

Fix by re-fetching pPriv from the screen private after CheckScreenPrivate
returns, so the subsequent NULL check correctly detects the freed state.

ScreenSaverFreeAttr has the same pattern, force pPriv to NULL there too
even though it has no real effect.

This vulnerability was discovered by:
Anonymous working with TrendAI Zero Day Initiative

ZDI-CAN-30168

Assisted-by: Claude:claude-opus-4-6
(cherry picked from commit ecc634f1b2)

Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2229>
2026-06-02 09:47:24 +10:00
..
bigreq.c Move extension initialisation prototypes into extinit.h 2012-07-09 23:06:41 -07:00
dpms.c Change the DPMS initialization to be conditional on not set from config 2018-06-26 17:14:34 -07:00
dpmsproc.h dpms: Consolidate a bunch of stuff into Xext/dpms.c 2017-03-27 15:59:47 -04:00
geext.c More missing version checks in SProcs 2021-08-08 12:43:01 +00:00
geext.h Move extension initialisation prototypes into extinit.h 2012-07-09 23:06:41 -07:00
geint.h xge: Hide some implementation details 2015-07-08 16:40:58 -04:00
hashtable.c dix: Fix undefined shift in ht_generic_hash 2019-10-15 14:06:30 -04:00
hashtable.h Fix spelling/wording issues 2020-07-05 13:07:33 -07:00
Makefile.am configure: Build hashtable for Xres and glvnd 2020-11-09 09:38:46 +00:00
meson.build meson: hide C API if Xorg is disabled (like autotools) 2021-03-11 00:22:36 +00:00
panoramiX.c panoramix: avoid null dereference in PanoramiXConsolidate() 2025-10-08 17:54:33 +02:00
panoramiX.h Drop trailing whitespaces 2014-11-12 10:25:00 +10:00
panoramiXh.h Introduce a consistent coding style 2012-03-21 13:54:42 -07:00
panoramiXprocs.c dix: Call SourceValidate before GetImage 2019-10-30 16:26:01 +00:00
panoramiXsrv.h Replace 'pointer' type with 'void *' 2014-01-12 10:24:11 -08:00
panoramiXSwap.c dispatch: Mark swapped dispatch as _X_COLD 2017-03-01 10:16:20 -05:00
saver.c saver: re-fetch screen private after CheckScreenPrivate in CreateSaverWindow 2026-06-02 09:47:24 +10:00
security.c dispatch: Mark swapped dispatch as _X_COLD 2017-03-01 10:16:20 -05:00
securitysrv.h Introduce a consistent coding style 2012-03-21 13:54:42 -07:00
shape.c dispatch: Mark swapped dispatch as _X_COLD 2017-03-01 10:16:20 -05:00
shm.c Xext/shm: avoid null dereference in ShmInitScreenPriv() 2025-10-08 17:54:33 +02:00
shmint.h xext: Fix shmint.h to not use headers outside of sdk_HEADERS 2013-11-14 10:22:15 +09:00
sleepuntil.c os: Don't crash in AttendClient if the client is gone 2019-11-19 10:15:05 -08:00
sleepuntil.h Replace 'pointer' type with 'void *' 2014-01-12 10:24:11 -08:00
sync.c sync: restart trigger list iteration in SyncChangeCounter after TriggerFired 2026-06-02 09:47:19 +10:00
syncsdk.h xsync: Add resource inside of SyncCreate, export SyncCreate 2019-04-17 14:01:17 -07:00
syncsrv.h sync: Convert from "CARD64" to int64_t. 2017-09-20 13:19:27 -04:00
vidmode.c Xext/vidmode: avoid null dereference if VidModeCreateMode() allocation fails 2025-10-08 17:54:33 +02:00
xace.c xace: Don't censor window borders 2016-09-28 15:25:07 -04:00
xace.h xace: Remove the audit hooks and tune dispatch 2016-06-10 13:26:19 -04:00
xacestr.h Replace 'pointer' type with 'void *' 2014-01-12 10:24:11 -08:00
xcmisc.c dispatch: Mark swapped dispatch as _X_COLD 2017-03-01 10:16:20 -05:00
xf86bigfont.c xf86bigfont: fix -Wimplicit-function-declaration error 2026-03-28 16:40:00 +00:00
xf86bigfontsrv.h Move extension initialisation prototypes into extinit.h 2012-07-09 23:06:41 -07:00
xres.c Xext/xres: avoid null dereference in ProcXResQueryClients() 2025-10-08 17:54:33 +02:00
xselinux.h Introduce a consistent coding style 2012-03-21 13:54:42 -07:00
xselinux_ext.c Xext/xselinux: add fast path to ProcSELinuxListSelections() 2025-10-08 17:54:33 +02:00
xselinux_hooks.c selinux: Stop using security_context_t 2021-08-17 16:02:39 -04:00
xselinux_label.c Xext/xselinux: avoid memory leak in SELinuxAtomToSID() 2025-10-08 17:54:33 +02:00
xselinuxint.h selinux: Stop using security_context_t 2021-08-17 16:02:39 -04:00
xtest.c Xext/xtest: avoid null dereference in ProcXTestFakeInput() 2025-10-08 17:54:33 +02:00
xvdisp.c Unvalidated lengths 2017-10-10 23:33:34 +02:00
xvdisp.h Fix swapped Xv dispatch under Xinerama. 2007-12-02 14:15:36 -05:00
xvdix.h Drop trailing whitespaces 2014-11-12 10:25:00 +10:00
xvmain.c Xext: free the XvRTVideoNotify when turning off from the same client 2022-12-14 11:24:41 +10:00
xvmc.c dispatch: Mark swapped dispatch as _X_COLD 2017-03-01 10:16:20 -05:00
xvmcext.h Replace 'pointer' type with 'void *' 2014-01-12 10:24:11 -08:00