From e81102be19d3456650e1d7cd55f78d67003f961a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 20 Oct 2026 19:46:54 +0200 Subject: [PATCH] Xi: validate modifier values in ProcXIPassiveUngrabDevice ProcXIPassiveUngrabDevice() passes client-supplied modifier values directly to DeletePassiveGrabFromList() without validating them. DeletePassiveGrabFromList() may reach DeleteDetailFromMask() which uses BITCLEAR(mask, modifier). The mask is allocated as 8 Mask words (256 bits). A modifier value > 255 causes BITCLEAR to index mask[modifier>>5] past the allocation, resulting in a controlled single-bit-clear at an attacker-chosen heap offset. Add the same modifier validation that CheckGrabValues() uses: reject modifiers that are not XIAnyModifier and have bits set outside AllModifiersMask. This vulnerability was discovered by: Anonymous working with TrendAI Zero Day Initiative ZDI-CAN-32366 CVE-2026-93523 Signed-off-by: Peter Hutterer Part-of: (cherry picked from commit 37a1847a60c6799aecf163a5cfa40077a9002675) --- Xext/xinput/xipassivegrab.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Xext/xinput/xipassivegrab.c b/Xext/xinput/xipassivegrab.c index 8267a05a9b..9f66808524 100644 --- a/Xext/xinput/xipassivegrab.c +++ b/Xext/xinput/xipassivegrab.c @@ -49,6 +49,10 @@ #include "swaprep.h" #include "exglobals.h" /* BadDevice */ +#define AllModifiersMask ( \ + ShiftMask | LockMask | ControlMask | Mod1Mask | Mod2Mask | \ + Mod3Mask | Mod4Mask | Mod5Mask ) + int ProcXIPassiveGrabDevice(ClientPtr client) { @@ -294,6 +298,14 @@ ProcXIPassiveUngrabDevice(ClientPtr client) if (rc != Success) return rc; + modifiers = (uint32_t *) &stuff[1]; + for (i = 0; i < stuff->num_modifiers; i++, modifiers++) { + if (*modifiers != XIAnyModifier && (*modifiers & ~AllModifiersMask)) { + client->errorValue = *modifiers; + return BadValue; + } + } + mod_dev = (InputDevIsFloating(dev)) ? dev : GetMaster(dev, MASTER_KEYBOARD); tempGrab = AllocGrab(NULL);