mirror of
https://github.com/X11Libre/xserver.git
synced 2026-10-21 01:13:38 +00:00
Xi: add bounds check for barrier events in input_constrain_cursor
input_constrain_cursor() writes barrier hit and leave events into the
InputEventList buffer without checking how many slots remain.
The buffer is allocated by the DDX at GetMaximumEventsNum() (100) entries
and may contain one raw event already. A client can create more than 100
pointer barriers, pre-arm them as released via XIBarrierReleasePointer,
and then trigger a single pointer motion that crosses all barriers
simultaneously. The leave event loop iterates every hit barrier and
writes one event per barrier, overflowing the 100-slot buffer.
In an ideal world we'd change the InternalEvent* pointer to a struct
that contains allocated size, number of events and the pointer itself
but we're crossing public APIs here and that requires an ABI bump.
So we make do with cutting off the number of barrier events at 64 which
should be low enough to not trigger the issue. However, for the 100+
barrier case this means we only send BarierHit events, not BarrierRelease.
Potentially leaving a client stuck.
We mitigate that problem by simply refusing to create more than 32
barriers per screen (which on its own would also fix the issue, but hey,
'doppelt gemoppelt' as they say).
This vulnerability was discovered by:
Anonymous working with TrendAI Zero Day Initiative
ZDI-CAN-31938
CVE-2026-93519
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2341>
(cherry picked from commit 1f42cc1f00)
This commit is contained in:
parent
4cec8f8089
commit
a8192aa709
1 changed files with 28 additions and 2 deletions
|
|
@ -425,6 +425,18 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr pScreen,
|
|||
DeviceIntPtr master;
|
||||
bool can_emit = events && nevents;
|
||||
|
||||
/* We cannot know how many events are available in *events, but
|
||||
* we know it all DDX very protbably allocated them with
|
||||
* GetMaximumEventsNum() and fill_pointer_events() may have added one.
|
||||
*
|
||||
* Let's cap at a maximum of 64 barrier events which is way more than
|
||||
* we'll need but low enough that we shouldn't OOB on *events.
|
||||
*
|
||||
* Changing the signatures to get some sz_events in here is an ABI change
|
||||
* through miPointerSetPosition so... weeping face.
|
||||
*/
|
||||
const int MAX_BARRIER_EVENTS = 64;
|
||||
|
||||
if (nevents)
|
||||
*nevents = 0;
|
||||
|
||||
|
|
@ -491,7 +503,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr pScreen,
|
|||
|
||||
/* root x/y is filled in later */
|
||||
|
||||
if (can_emit) {
|
||||
if (can_emit && *nevents < MAX_BARRIER_EVENTS) {
|
||||
barrier_events->barrier_event = ev;
|
||||
barrier_events++;
|
||||
*nevents += 1;
|
||||
|
|
@ -530,7 +542,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr pScreen,
|
|||
|
||||
/* root x/y is filled in later */
|
||||
|
||||
if (can_emit) {
|
||||
if (can_emit && *nevents < MAX_BARRIER_EVENTS) {
|
||||
barrier_events->barrier_event = ev;
|
||||
barrier_events++;
|
||||
*nevents += 1;
|
||||
|
|
@ -588,6 +600,20 @@ CreatePointerBarrierClient(ClientPtr client,
|
|||
ScreenPtr pScreen = pWin->drawable.pScreen;
|
||||
cs = GetBarrierScreen(pScreen);
|
||||
|
||||
/* Only allow for a maximum of 32 barriers to be created. This
|
||||
* should be more than enough and prevents issues in
|
||||
* input_constrain_cursor, see MAX_BARRIER_EVENTS in that
|
||||
* function */
|
||||
struct PointerBarrierClient *counter;
|
||||
size_t nbarriers = 0;
|
||||
xorg_list_for_each_entry(counter, &cs->barriers, entry) {
|
||||
nbarriers++;
|
||||
if (nbarriers >= 32) {
|
||||
err = BadAlloc;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
ret->pScreen = pScreen;
|
||||
ret->window = stuff->window;
|
||||
ret->num_devices = stuff->num_devices;
|
||||
|
|
|
|||
Loading…
Reference in a new issue