mirror of
https://github.com/X11Libre/xserver.git
synced 2026-10-28 02:21:08 +00:00
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 <peter.hutterer@who-t.net> Assisted-by: Claude:claude-opus-4-6 Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2341>
This commit is contained in:
parent
5dc9efd5a1
commit
37a1847a60
2 changed files with 63 additions and 0 deletions
|
|
@ -46,6 +46,10 @@
|
|||
#include "misc.h"
|
||||
#include "inpututils.h"
|
||||
|
||||
#define AllModifiersMask ( \
|
||||
ShiftMask | LockMask | ControlMask | Mod1Mask | Mod2Mask | \
|
||||
Mod3Mask | Mod4Mask | Mod5Mask )
|
||||
|
||||
int _X_COLD
|
||||
SProcXIPassiveGrabDevice(ClientPtr client)
|
||||
{
|
||||
|
|
@ -350,6 +354,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 = (IsFloating(dev)) ? dev : GetMaster(dev, MASTER_KEYBOARD);
|
||||
|
||||
tempGrab = AllocGrab(NULL);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
# Security tests for XI/XI2 (XInput) extension vulnerabilities.
|
||||
|
||||
import struct
|
||||
import time
|
||||
|
||||
import pytest
|
||||
from proto import x11, xi
|
||||
|
|
@ -119,6 +120,56 @@ class TestXIPassiveGrab:
|
|||
f"Expected BadValue ({x11.BadValue}), got error code {resp.error_code}"
|
||||
)
|
||||
|
||||
@pytest.mark.asan
|
||||
def test_passive_ungrab_modifier_oob_write(self, xserver, xi_xclient):
|
||||
"""
|
||||
ZDI-CAN-32366: ProcXIPassiveUngrabDevice does not validate modifier
|
||||
values. The grab path (ProcXIPassiveGrabDevice) validates modifiers
|
||||
via CheckGrabValues(), but the ungrab path passes them directly to
|
||||
DeletePassiveGrabFromList(). BITCLEAR(mask, modifier) with
|
||||
modifier > 255 indexes mask[modifier>>5] past the 8-word (32-byte)
|
||||
mask allocation, causing a controlled single-bit-clear at an
|
||||
attacker-chosen heap offset.
|
||||
|
||||
The fix adds the same modifier validation to the ungrab path.
|
||||
"""
|
||||
opcode = xi_xclient.query_extension(Extension.XI).opcode
|
||||
|
||||
wid = xi_xclient.create_window()
|
||||
|
||||
# First create a valid grab so DeletePassiveGrabFromList has
|
||||
# something to match against
|
||||
grab_req = xi.XIPassiveGrabDeviceRequest(
|
||||
opcode=opcode,
|
||||
grab_window=wid,
|
||||
detail=1,
|
||||
grab_type=xi.XIGrabtypeButton,
|
||||
)
|
||||
xi_xclient.send_request(grab_req)
|
||||
xi_xclient.recv_response(timeout=2.0)
|
||||
|
||||
# Now try to ungrab with an oversized modifier (0x10000)
|
||||
# This would cause BITCLEAR to write past the mask allocation
|
||||
req = xi.XIPassiveUngrabDeviceRequest(
|
||||
opcode=opcode,
|
||||
grab_window=wid,
|
||||
detail=1,
|
||||
grab_type=xi.XIGrabtypeButton,
|
||||
modifiers=[0x10000], # > 255: OOB write via BITCLEAR
|
||||
)
|
||||
xi_xclient.send_request(req)
|
||||
resp = xi_xclient.recv_response(timeout=2.0)
|
||||
time.sleep(0.5)
|
||||
|
||||
assert xserver.is_alive, (
|
||||
"Server crashed - XIPassiveUngrabDevice modifier OOB write: "
|
||||
"BITCLEAR(mask, modifier) with modifier > 255"
|
||||
)
|
||||
assert isinstance(resp, X11Error), f"Expected an error, got {resp}"
|
||||
assert resp.error_code == x11.BadValue, (
|
||||
f"Expected BadValue ({x11.BadValue}), got error code {resp.error_code}"
|
||||
)
|
||||
|
||||
|
||||
class TestXIChangeProperty:
|
||||
"""Tests for XIChangeProperty vulnerabilities."""
|
||||
|
|
|
|||
Loading…
Reference in a new issue