From 73c63afb93c0af1bfd1969bf6e71c9edca586c77 Mon Sep 17 00:00:00 2001 From: Alan Coopersmith Date: Sun, 26 Jan 2014 10:54:41 -0800 Subject: [PATCH] Xi: unvalidated lengths in Xinput extension [CVE-2014-8095] Multiple functions in the Xinput extension handling of requests from clients failed to check that the length of the request sent by the client was large enough to perform all the required operations and thus could read or write to memory outside the bounds of the request buffer. This commit includes the creation of a new REQUEST_AT_LEAST_EXTRA_SIZE macro in include/dix.h for the common case of needing to ensure a request is large enough to include both the request itself and a minimum amount of extra data following the request header. Signed-off-by: Alan Coopersmith Reviewed-by: Peter Hutterer --- Xi/chgdctl.c | 8 ++++++-- Xi/chgfctl.c | 2 ++ Xi/sendexev.c | 3 +++ Xi/xiallowev.c | 2 ++ Xi/xichangecursor.c | 2 +- Xi/xichangehierarchy.c | 35 ++++++++++++++++++++++++++++++++--- Xi/xigetclientpointer.c | 1 + Xi/xigrabdev.c | 9 ++++++++- Xi/xipassivegrab.c | 12 ++++++++++-- Xi/xiproperty.c | 14 ++++++-------- Xi/xiquerydevice.c | 1 + Xi/xiquerypointer.c | 2 ++ Xi/xiselectev.c | 8 ++++++++ Xi/xisetclientpointer.c | 3 ++- Xi/xisetdevfocus.c | 4 ++++ Xi/xiwarppointer.c | 2 ++ include/dix.h | 4 ++++ 17 files changed, 94 insertions(+), 18 deletions(-) diff --git a/Xi/chgdctl.c b/Xi/chgdctl.c index d078aa248..b3ee867f0 100644 --- a/Xi/chgdctl.c +++ b/Xi/chgdctl.c @@ -78,7 +78,7 @@ SProcXChangeDeviceControl(ClientPtr client) REQUEST(xChangeDeviceControlReq); swaps(&stuff->length); - REQUEST_AT_LEAST_SIZE(xChangeDeviceControlReq); + REQUEST_AT_LEAST_EXTRA_SIZE(xChangeDeviceControlReq, sizeof(xDeviceCtl)); swaps(&stuff->control); ctl = (xDeviceCtl *) &stuff[1]; swaps(&ctl->control); @@ -115,7 +115,7 @@ ProcXChangeDeviceControl(ClientPtr client) xDeviceEnableCtl *e; REQUEST(xChangeDeviceControlReq); - REQUEST_AT_LEAST_SIZE(xChangeDeviceControlReq); + REQUEST_AT_LEAST_EXTRA_SIZE(xChangeDeviceControlReq, sizeof(xDeviceCtl)); len = stuff->length - bytes_to_int32(sizeof(xChangeDeviceControlReq)); ret = dixLookupDevice(&dev, stuff->deviceid, client, DixManageAccess); @@ -192,6 +192,10 @@ ProcXChangeDeviceControl(ClientPtr client) break; case DEVICE_ENABLE: e = (xDeviceEnableCtl *) &stuff[1]; + if ((len != bytes_to_int32(sizeof(xDeviceEnableCtl)))) { + ret = BadLength; + goto out; + } if (IsXTestDevice(dev, NULL)) status = !Success; diff --git a/Xi/chgfctl.c b/Xi/chgfctl.c index 6dcf60c66..224c2ba0a 100644 --- a/Xi/chgfctl.c +++ b/Xi/chgfctl.c @@ -467,6 +467,8 @@ ProcXChangeFeedbackControl(ClientPtr client) xStringFeedbackCtl *f = ((xStringFeedbackCtl *) &stuff[1]); if (client->swapped) { + if (len < bytes_to_int32(sizeof(xStringFeedbackCtl))) + return BadLength; swaps(&f->num_keysyms); } if (len != diff --git a/Xi/sendexev.c b/Xi/sendexev.c index 3c213864b..183f88dae 100644 --- a/Xi/sendexev.c +++ b/Xi/sendexev.c @@ -135,6 +135,9 @@ ProcXSendExtensionEvent(ClientPtr client) if (ret != Success) return ret; + if (stuff->num_events == 0) + return ret; + /* The client's event type must be one defined by an extension. */ first = ((xEvent *) &stuff[1]); diff --git a/Xi/xiallowev.c b/Xi/xiallowev.c index ebef23344..ca263ef1f 100644 --- a/Xi/xiallowev.c +++ b/Xi/xiallowev.c @@ -48,6 +48,7 @@ int SProcXIAllowEvents(ClientPtr client) { REQUEST(xXIAllowEventsReq); + REQUEST_AT_LEAST_SIZE(xXIAllowEventsReq); swaps(&stuff->length); swaps(&stuff->deviceid); @@ -55,6 +56,7 @@ SProcXIAllowEvents(ClientPtr client) if (stuff->length > 3) { xXI2_2AllowEventsReq *req_xi22 = (xXI2_2AllowEventsReq *) stuff; + REQUEST_AT_LEAST_SIZE(xXI2_2AllowEventsReq); swapl(&req_xi22->touchid); swapl(&req_xi22->grab_window); } diff --git a/Xi/xichangecursor.c b/Xi/xichangecursor.c index 7a1bb7a0d..8e6255b6e 100644 --- a/Xi/xichangecursor.c +++ b/Xi/xichangecursor.c @@ -57,11 +57,11 @@ int SProcXIChangeCursor(ClientPtr client) { REQUEST(xXIChangeCursorReq); + REQUEST_SIZE_MATCH(xXIChangeCursorReq); swaps(&stuff->length); swapl(&stuff->win); swapl(&stuff->cursor); swaps(&stuff->deviceid); - REQUEST_SIZE_MATCH(xXIChangeCursorReq); return (ProcXIChangeCursor(client)); } diff --git a/Xi/xichangehierarchy.c b/Xi/xichangehierarchy.c index 9e36354d1..27324452d 100644 --- a/Xi/xichangehierarchy.c +++ b/Xi/xichangehierarchy.c @@ -411,7 +411,7 @@ int ProcXIChangeHierarchy(ClientPtr client) { xXIAnyHierarchyChangeInfo *any; - int required_len = sizeof(xXIChangeHierarchyReq); + size_t len; /* length of data remaining in request */ int rc = Success; int flags[MAXDEVICES] = { 0 }; @@ -421,21 +421,46 @@ ProcXIChangeHierarchy(ClientPtr client) if (!stuff->num_changes) return rc; + if (stuff->length > (INT_MAX >> 2)) + return BadAlloc; + len = (stuff->length << 2) - sizeof(xXIAnyHierarchyChangeInfo); + any = (xXIAnyHierarchyChangeInfo *) &stuff[1]; while (stuff->num_changes--) { + if (len < sizeof(xXIAnyHierarchyChangeInfo)) { + rc = BadLength; + goto unwind; + } + SWAPIF(swaps(&any->type)); SWAPIF(swaps(&any->length)); - required_len += any->length; - if ((stuff->length * 4) < required_len) + if ((any->length > (INT_MAX >> 2)) || (len < (any->length << 2))) return BadLength; +#define CHANGE_SIZE_MATCH(type) \ + do { \ + if ((len < sizeof(type)) || (any->length != (sizeof(type) >> 2))) { \ + rc = BadLength; \ + goto unwind; \ + } \ + } while(0) + switch (any->type) { case XIAddMaster: { xXIAddMasterInfo *c = (xXIAddMasterInfo *) any; + /* Variable length, due to appended name string */ + if (len < sizeof(xXIAddMasterInfo)) { + rc = BadLength; + goto unwind; + } SWAPIF(swaps(&c->name_len)); + if (c->name_len > (len - sizeof(xXIAddMasterInfo))) { + rc = BadLength; + goto unwind; + } rc = add_master(client, c, flags); if (rc != Success) @@ -446,6 +471,7 @@ ProcXIChangeHierarchy(ClientPtr client) { xXIRemoveMasterInfo *r = (xXIRemoveMasterInfo *) any; + CHANGE_SIZE_MATCH(xXIRemoveMasterInfo); rc = remove_master(client, r, flags); if (rc != Success) goto unwind; @@ -455,6 +481,7 @@ ProcXIChangeHierarchy(ClientPtr client) { xXIDetachSlaveInfo *c = (xXIDetachSlaveInfo *) any; + CHANGE_SIZE_MATCH(xXIDetachSlaveInfo); rc = detach_slave(client, c, flags); if (rc != Success) goto unwind; @@ -464,6 +491,7 @@ ProcXIChangeHierarchy(ClientPtr client) { xXIAttachSlaveInfo *c = (xXIAttachSlaveInfo *) any; + CHANGE_SIZE_MATCH(xXIAttachSlaveInfo); rc = attach_slave(client, c, flags); if (rc != Success) goto unwind; @@ -471,6 +499,7 @@ ProcXIChangeHierarchy(ClientPtr client) break; } + len -= any->length * 4; any = (xXIAnyHierarchyChangeInfo *) ((char *) any + any->length * 4); } diff --git a/Xi/xigetclientpointer.c b/Xi/xigetclientpointer.c index 3c90d588d..306dd396b 100644 --- a/Xi/xigetclientpointer.c +++ b/Xi/xigetclientpointer.c @@ -50,6 +50,7 @@ int SProcXIGetClientPointer(ClientPtr client) { REQUEST(xXIGetClientPointerReq); + REQUEST_SIZE_MATCH(xXIGetClientPointerReq); swaps(&stuff->length); swapl(&stuff->win); diff --git a/Xi/xigrabdev.c b/Xi/xigrabdev.c index 63d95bc1c..e2a2ae333 100644 --- a/Xi/xigrabdev.c +++ b/Xi/xigrabdev.c @@ -47,6 +47,11 @@ int SProcXIGrabDevice(ClientPtr client) { REQUEST(xXIGrabDeviceReq); + /* + * Check here for at least the length of the struct we swap, then + * let ProcXIGrabDevice check the full size after we swap mask_len. + */ + REQUEST_AT_LEAST_SIZE(xXIGrabDeviceReq); swaps(&stuff->length); swaps(&stuff->deviceid); @@ -71,7 +76,7 @@ ProcXIGrabDevice(ClientPtr client) unsigned int pointer_mode; REQUEST(xXIGrabDeviceReq); - REQUEST_AT_LEAST_SIZE(xXIGrabDeviceReq); + REQUEST_FIXED_SIZE(xXIGrabDeviceReq, ((size_t) stuff->mask_len) * 4); ret = dixLookupDevice(&dev, stuff->deviceid, client, DixGrabAccess); if (ret != Success) @@ -131,6 +136,7 @@ int SProcXIUngrabDevice(ClientPtr client) { REQUEST(xXIUngrabDeviceReq); + REQUEST_SIZE_MATCH(xXIUngrabDeviceReq); swaps(&stuff->length); swaps(&stuff->deviceid); @@ -148,6 +154,7 @@ ProcXIUngrabDevice(ClientPtr client) TimeStamp time; REQUEST(xXIUngrabDeviceReq); + REQUEST_SIZE_MATCH(xXIUngrabDeviceReq); ret = dixLookupDevice(&dev, stuff->deviceid, client, DixGetAttrAccess); if (ret != Success) diff --git a/Xi/xipassivegrab.c b/Xi/xipassivegrab.c index 700622d38..9241ffdea 100644 --- a/Xi/xipassivegrab.c +++ b/Xi/xipassivegrab.c @@ -53,6 +53,7 @@ SProcXIPassiveGrabDevice(ClientPtr client) uint32_t *mods; REQUEST(xXIPassiveGrabDeviceReq); + REQUEST_AT_LEAST_SIZE(xXIPassiveGrabDeviceReq); swaps(&stuff->length); swaps(&stuff->deviceid); @@ -63,6 +64,8 @@ SProcXIPassiveGrabDevice(ClientPtr client) swaps(&stuff->mask_len); swaps(&stuff->num_modifiers); + REQUEST_FIXED_SIZE(xXIPassiveGrabDeviceReq, + ((uint32_t) stuff->mask_len + stuff->num_modifiers) *4); mods = (uint32_t *) &stuff[1] + stuff->mask_len; for (i = 0; i < stuff->num_modifiers; i++, mods++) { @@ -92,7 +95,8 @@ ProcXIPassiveGrabDevice(ClientPtr client) int mask_len; REQUEST(xXIPassiveGrabDeviceReq); - REQUEST_AT_LEAST_SIZE(xXIPassiveGrabDeviceReq); + REQUEST_FIXED_SIZE(xXIPassiveGrabDeviceReq, + ((uint32_t) stuff->mask_len + stuff->num_modifiers) * 4); if (stuff->deviceid == XIAllDevices) dev = inputInfo.all_devices; @@ -252,6 +256,7 @@ SProcXIPassiveUngrabDevice(ClientPtr client) uint32_t *modifiers; REQUEST(xXIPassiveUngrabDeviceReq); + REQUEST_AT_LEAST_SIZE(xXIPassiveUngrabDeviceReq); swaps(&stuff->length); swapl(&stuff->grab_window); @@ -259,6 +264,8 @@ SProcXIPassiveUngrabDevice(ClientPtr client) swapl(&stuff->detail); swaps(&stuff->num_modifiers); + REQUEST_FIXED_SIZE(xXIPassiveUngrabDeviceReq, + ((uint32_t) stuff->num_modifiers) << 2); modifiers = (uint32_t *) &stuff[1]; for (i = 0; i < stuff->num_modifiers; i++, modifiers++) @@ -277,7 +284,8 @@ ProcXIPassiveUngrabDevice(ClientPtr client) int i, rc; REQUEST(xXIPassiveUngrabDeviceReq); - REQUEST_AT_LEAST_SIZE(xXIPassiveUngrabDeviceReq); + REQUEST_FIXED_SIZE(xXIPassiveUngrabDeviceReq, + ((uint32_t) stuff->num_modifiers) << 2); if (stuff->deviceid == XIAllDevices) dev = inputInfo.all_devices; diff --git a/Xi/xiproperty.c b/Xi/xiproperty.c index 463607d33..8e8e4b061 100644 --- a/Xi/xiproperty.c +++ b/Xi/xiproperty.c @@ -1013,10 +1013,9 @@ int SProcXListDeviceProperties(ClientPtr client) { REQUEST(xListDevicePropertiesReq); + REQUEST_SIZE_MATCH(xListDevicePropertiesReq); swaps(&stuff->length); - - REQUEST_SIZE_MATCH(xListDevicePropertiesReq); return (ProcXListDeviceProperties(client)); } @@ -1037,10 +1036,10 @@ int SProcXDeleteDeviceProperty(ClientPtr client) { REQUEST(xDeleteDevicePropertyReq); + REQUEST_SIZE_MATCH(xDeleteDevicePropertyReq); swaps(&stuff->length); swapl(&stuff->property); - REQUEST_SIZE_MATCH(xDeleteDevicePropertyReq); return (ProcXDeleteDeviceProperty(client)); } @@ -1048,13 +1047,13 @@ int SProcXGetDeviceProperty(ClientPtr client) { REQUEST(xGetDevicePropertyReq); + REQUEST_SIZE_MATCH(xGetDevicePropertyReq); swaps(&stuff->length); swapl(&stuff->property); swapl(&stuff->type); swapl(&stuff->longOffset); swapl(&stuff->longLength); - REQUEST_SIZE_MATCH(xGetDevicePropertyReq); return (ProcXGetDeviceProperty(client)); } @@ -1253,11 +1252,10 @@ int SProcXIListProperties(ClientPtr client) { REQUEST(xXIListPropertiesReq); + REQUEST_SIZE_MATCH(xXIListPropertiesReq); swaps(&stuff->length); swaps(&stuff->deviceid); - - REQUEST_SIZE_MATCH(xXIListPropertiesReq); return (ProcXIListProperties(client)); } @@ -1279,11 +1277,11 @@ int SProcXIDeleteProperty(ClientPtr client) { REQUEST(xXIDeletePropertyReq); + REQUEST_SIZE_MATCH(xXIDeletePropertyReq); swaps(&stuff->length); swaps(&stuff->deviceid); swapl(&stuff->property); - REQUEST_SIZE_MATCH(xXIDeletePropertyReq); return (ProcXIDeleteProperty(client)); } @@ -1291,6 +1289,7 @@ int SProcXIGetProperty(ClientPtr client) { REQUEST(xXIGetPropertyReq); + REQUEST_SIZE_MATCH(xXIGetPropertyReq); swaps(&stuff->length); swaps(&stuff->deviceid); @@ -1298,7 +1297,6 @@ SProcXIGetProperty(ClientPtr client) swapl(&stuff->type); swapl(&stuff->offset); swapl(&stuff->len); - REQUEST_SIZE_MATCH(xXIGetPropertyReq); return (ProcXIGetProperty(client)); } diff --git a/Xi/xiquerydevice.c b/Xi/xiquerydevice.c index 4e544f0f5..67a9a4f3f 100644 --- a/Xi/xiquerydevice.c +++ b/Xi/xiquerydevice.c @@ -54,6 +54,7 @@ int SProcXIQueryDevice(ClientPtr client) { REQUEST(xXIQueryDeviceReq); + REQUEST_SIZE_MATCH(xXIQueryDeviceReq); swaps(&stuff->length); swaps(&stuff->deviceid); diff --git a/Xi/xiquerypointer.c b/Xi/xiquerypointer.c index e9bdd428d..7ec0c851d 100644 --- a/Xi/xiquerypointer.c +++ b/Xi/xiquerypointer.c @@ -63,6 +63,8 @@ int SProcXIQueryPointer(ClientPtr client) { REQUEST(xXIQueryPointerReq); + REQUEST_SIZE_MATCH(xXIQueryPointerReq); + swaps(&stuff->length); swaps(&stuff->deviceid); swapl(&stuff->win); diff --git a/Xi/xiselectev.c b/Xi/xiselectev.c index 45a996e4c..168579f5b 100644 --- a/Xi/xiselectev.c +++ b/Xi/xiselectev.c @@ -114,6 +114,7 @@ int SProcXISelectEvents(ClientPtr client) { int i; + int len; xXIEventMask *evmask; REQUEST(xXISelectEventsReq); @@ -122,10 +123,17 @@ SProcXISelectEvents(ClientPtr client) swapl(&stuff->win); swaps(&stuff->num_masks); + len = stuff->length - bytes_to_int32(sizeof(xXISelectEventsReq)); evmask = (xXIEventMask *) &stuff[1]; for (i = 0; i < stuff->num_masks; i++) { + if (len < bytes_to_int32(sizeof(xXIEventMask))) + return BadLength; + len -= bytes_to_int32(sizeof(xXIEventMask)); swaps(&evmask->deviceid); swaps(&evmask->mask_len); + if (len < evmask->mask_len) + return BadLength; + len -= evmask->mask_len; evmask = (xXIEventMask *) (((char *) &evmask[1]) + evmask->mask_len * 4); } diff --git a/Xi/xisetclientpointer.c b/Xi/xisetclientpointer.c index 38ff51e86..24d4a5379 100644 --- a/Xi/xisetclientpointer.c +++ b/Xi/xisetclientpointer.c @@ -51,10 +51,11 @@ int SProcXISetClientPointer(ClientPtr client) { REQUEST(xXISetClientPointerReq); + REQUEST_SIZE_MATCH(xXISetClientPointerReq); + swaps(&stuff->length); swapl(&stuff->win); swaps(&stuff->deviceid); - REQUEST_SIZE_MATCH(xXISetClientPointerReq); return (ProcXISetClientPointer(client)); } diff --git a/Xi/xisetdevfocus.c b/Xi/xisetdevfocus.c index 372ec248a..96a9a16e4 100644 --- a/Xi/xisetdevfocus.c +++ b/Xi/xisetdevfocus.c @@ -44,6 +44,8 @@ int SProcXISetFocus(ClientPtr client) { REQUEST(xXISetFocusReq); + REQUEST_AT_LEAST_SIZE(xXISetFocusReq); + swaps(&stuff->length); swaps(&stuff->deviceid); swapl(&stuff->focus); @@ -56,6 +58,8 @@ int SProcXIGetFocus(ClientPtr client) { REQUEST(xXIGetFocusReq); + REQUEST_AT_LEAST_SIZE(xXIGetFocusReq); + swaps(&stuff->length); swaps(&stuff->deviceid); diff --git a/Xi/xiwarppointer.c b/Xi/xiwarppointer.c index 3f051f759..780758a9e 100644 --- a/Xi/xiwarppointer.c +++ b/Xi/xiwarppointer.c @@ -56,6 +56,8 @@ int SProcXIWarpPointer(ClientPtr client) { REQUEST(xXIWarpPointerReq); + REQUEST_SIZE_MATCH(xXIWarpPointerReq); + swaps(&stuff->length); swapl(&stuff->src_win); swapl(&stuff->dst_win); diff --git a/include/dix.h b/include/dix.h index e0c6ed84f..21176a8c3 100644 --- a/include/dix.h +++ b/include/dix.h @@ -74,6 +74,10 @@ SOFTWARE. if ((sizeof(req) >> 2) > client->req_len )\ return(BadLength) +#define REQUEST_AT_LEAST_EXTRA_SIZE(req, extra) \ + if (((sizeof(req) + ((uint64_t) extra)) >> 2) > client->req_len ) \ + return(BadLength) + #define REQUEST_FIXED_SIZE(req, n)\ if (((sizeof(req) >> 2) > client->req_len) || \ ((n >> 2) >= client->req_len) || \