test/pyxtest: add test for ScreenSaverFreeAttr stale pPriv code path

Add TestScreenSaverFreeAttr which exercises the ScreenSaverFreeAttr
code path by setting attributes, activating the screen saver, then
closing the client connection (triggering resource cleanup).

While ScreenSaverFreeAttr currently does not dereference pPriv after
CheckScreenPrivate, this test verifies the code path is safe and
would catch regressions if future code changes introduced a stale
pointer dereference (same pattern as ZDI-CAN-30168).

Assisted-by: Claude:claude-opus-4-6
Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2228>
This commit is contained in:
Peter Hutterer 2026-04-29 06:07:30 +00:00
parent 3568302483
commit 4809a5e4b8

View file

@ -120,3 +120,50 @@ class TestCreateSaverWindow:
assert xserver.is_alive, (
"Server crashed - CreateSaverWindow UAF (ZDI-CAN-30168)"
)
class TestScreenSaverFreeAttr:
"""Tests for ScreenSaverFreeAttr stale pPriv after CheckScreenPrivate."""
@pytest.mark.valgrind
def test_free_attr_with_active_saver(self, xserver, screensaver_xclient):
"""
ScreenSaverFreeAttr calls CheckScreenPrivate() which may free
pPriv. While the function currently returns immediately after
and does not dereference pPriv, this exercises the code path
to verify the pattern is safe.
This test triggers ScreenSaverFreeAttr by closing the client
that called SetAttributes (resource cleanup frees the attr).
1. Client A: SetAttributes (creates pPriv with attr)
2. Client A: ForceScreenSaver(Active) (creates saver window,
pPriv->hasWindow=TRUE)
3. Close Client A ScreenSaverFreeAttr frees attr, calls
CheckScreenPrivate with pPriv->hasWindow still TRUE.
"""
conn, opcode = screensaver_xclient
# SetAttributes on root
req = screensaver.SetAttributesRequest(
opcode=opcode,
drawable=conn.root_window,
width=100,
height=100,
mask=0,
)
conn.send_request(req.to_bytes())
conn.flush_responses(timeout=0.5)
# ForceScreenSaver(Active) to create the saver window
req = x11.ForceScreenSaver(mode=x11.ScreenSaverActive)
conn.send_request(req.to_bytes())
conn.flush_responses(timeout=0.5)
time.sleep(0.2)
# Close the connection - triggers ScreenSaverFreeAttr via
# resource cleanup of the SetAttributes resource
conn.close()
time.sleep(0.5)
assert xserver.is_alive, "Server crashed - ScreenSaverFreeAttr stale pPriv"