diff --git a/test/xi2/Makefile.am b/test/xi2/Makefile.am index 58556cb8f..86992994f 100644 --- a/test/xi2/Makefile.am +++ b/test/xi2/Makefile.am @@ -1,7 +1,8 @@ if UNITTESTS check_PROGRAMS = \ protocol-xiqueryversion \ - protocol-xiquerydevice + protocol-xiquerydevice \ + protocol-xiselectevents TESTS=$(check_PROGRAMS) @@ -12,10 +13,13 @@ COMMON_SOURCES=protocol-common.h protocol-common.c protocol_xiqueryversion_LDADD=$(TEST_LDADD) protocol_xiquerydevice_LDADD=$(TEST_LDADD) +protocol_xiselectevents_LDADD=$(TEST_LDADD) protocol_xiqueryversion_CFLAGS=$(AM_CFLAGS) -Wl,-wrap,WriteToClient protocol_xiquerydevice_CFLAGS=$(AM_CFLAGS) -Wl,-wrap,WriteToClient +protocol_xiselectevents_CFLAGS=$(AM_CFLAGS) -Wl,-wrap,dixLookupWindow -Wl,-wrap,XISetEventMask protocol_xiqueryversion_SOURCES=$(COMMON_SOURCES) protocol-xiqueryversion.c protocol_xiquerydevice_SOURCES=$(COMMON_SOURCES) protocol-xiquerydevice.c +protocol_xiselectevents_SOURCES=$(COMMON_SOURCES) protocol-xiselectevents.c endif diff --git a/test/xi2/protocol-xiselectevents.c b/test/xi2/protocol-xiselectevents.c new file mode 100644 index 000000000..825eeb628 --- /dev/null +++ b/test/xi2/protocol-xiselectevents.c @@ -0,0 +1,321 @@ +/** + * Copyright © 2009 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +/* + * Protocol testing for XISelectEvents request. + * + * Test approach: + * + * Wrap XISetEventMask to intercept when the server tries to apply the event + * mask. Ensure that the mask passed in is equivalent to the one supplied by + * the client. Ensure that invalid devices and invalid masks return errors + * as appropriate. + * + * Tests included: + * BadValue for num_masks < 0 + * BadWindow for invalid windows + * BadDevice for non-existing devices + * BadImplemenation for devices >= 0xFF + * BadValue if HierarchyChanged bit is set for devices other than + * XIAllDevices + * BadValue for invalid mask bits + * Sucecss for excessive mask lengths + * + */ + +#include +#include +#include +#include +#include "inputstr.h" +#include "windowstr.h" +#include "extinit.h" /* for XInputExtensionInit */ +#include "scrnintstr.h" +#include "xiselectev.h" + +#include "protocol-common.h" +#include + +static unsigned char *data[4096 * 16]; /* the request data buffer */ + +void __wrap_XISetEventMask(DeviceIntPtr dev, WindowPtr win, int len, unsigned char* mask) +{ +} + +/* dixLookupWindow requires a lot of setup not necessary for this test. + * Simple wrapper that returns either one of the fake root window or the + * fake client window. If the requested ID is neither of those wanted, + * return whatever the real dixLookupWindow does. + */ +int __wrap_dixLookupWindow(WindowPtr *win, XID id, ClientPtr client, Mask access) +{ + if (id == root.drawable.id) + { + *win = &root; + return Success; + } else if (id == window.drawable.id) + { + *win = &window; + return Success; + } + + return __real_dixLookupWindow(win, id, client, access); +} + + +static void request_XISelectEvent(xXISelectEventsReq *req, int error) +{ + char n; + int i; + int rc; + ClientRec client; + xXIEventMask *mask, *next; + + req->length = (sz_xXISelectEventsReq/4) + req->num_masks; + client = init_client(req->length, req); + + rc = ProcXISelectEvents(&client); + g_assert(rc == error); + + client.swapped = TRUE; + + mask = (xXIEventMask*)&req[1]; + for (i = 0; i < req->num_masks; i++) + { + next = (xXIEventMask*)((char*)mask) + mask->mask_len; + swaps(&mask->deviceid, n); + swaps(&mask->mask_len, n); + mask = next; + } + + swapl(&req->win, n); + swaps(&req->length, n); + swaps(&req->num_masks, n); + rc = SProcXISelectEvents(&client); + g_assert(rc == error); +} + +static void request_XISelectEvents_masks(xXISelectEventsReq *req) +{ + int i, j; + xXIEventMask *mask; + int nmasks = (XI_LASTEVENT + 7)/8; + unsigned char *bits; + + mask = (xXIEventMask*)&req[1]; + req->win = ROOT_WINDOW_ID; + + /* if a clients submits more than 100 masks, consider it insane and untested */ + for (i = 1; i <= 1000; i++) + { + req->num_masks = i; + mask->deviceid = XIAllDevices; + + /* Test 0: + * mask_len is 0 -> Success + */ + mask->mask_len = 0; + request_XISelectEvent(req, Success); + + /* Test 1: + * mask may be larger than needed for XI_LASTEVENT. + * Test setting each valid mask bit, while leaving unneeded bits 0. + * -> Success + */ + bits = (unsigned char*)&mask[1]; + mask->mask_len = (nmasks + 3)/4 * 10; + memset(bits, 0, mask->mask_len * 4); + for (j = 0; j <= XI_LASTEVENT; j++) + { + SetBit(bits, j); + request_XISelectEvent(req, Success); + ClearBit(bits, j); + } + + /* Test 2: + * mask may be larger than needed for XI_LASTEVENT. + * Test setting all valid mask bits, while leaving unneeded bits 0. + * -> Success + */ + bits = (unsigned char*)&mask[1]; + mask->mask_len = (nmasks + 3)/4 * 10; + memset(bits, 0, mask->mask_len * 4); + + for (j = 0; j <= XI_LASTEVENT; j++) + { + SetBit(bits, j); + request_XISelectEvent(req, Success); + } + + /* Test 3: + * mask is larger than needed for XI_LASTEVENT. If any unneeded bit + * is set -> BadValue + */ + bits = (unsigned char*)&mask[1]; + mask->mask_len = (nmasks + 3)/4 * 10; + memset(bits, 0, mask->mask_len * 4); + + for (j = XI_LASTEVENT + 1; j < mask->mask_len * 4; j++) + { + SetBit(bits, j); + request_XISelectEvent(req, BadValue); + ClearBit(bits, j); + } + + /* Test 4: + * Mask len is a sensible length, only valid bits are set -> Success + */ + bits = (unsigned char*)&mask[1]; + mask->mask_len = (nmasks + 3)/4; + memset(bits, 0, mask->mask_len * 4); + for (j = 0; j <= XI_LASTEVENT; j++) + { + SetBit(bits, j); + request_XISelectEvent(req, Success); + } + + /* Test 5: + * HierarchyChanged bit is BadValue for devices other than + * XIAllDevices + */ + bits = (unsigned char*)&mask[1]; + mask->mask_len = (nmasks + 3)/4; + memset(bits, 0, mask->mask_len * 4); + SetBit(bits, XI_HierarchyChanged); + mask->deviceid = XIAllDevices; + request_XISelectEvent(req, Success); + for (j = 1; j < devices.num_devices; j++) + { + mask->deviceid = j; + request_XISelectEvent(req, BadValue); + } + + /* Test 6: + * All bits set minus hierarchy changed bit -> Success + */ + bits = (unsigned char*)&mask[1]; + mask->mask_len = (nmasks + 3)/4; + memset(bits, 0, mask->mask_len * 4); + for (j = 0; j <= XI_LASTEVENT; j++) + SetBit(bits, j); + ClearBit(bits, XI_HierarchyChanged); + for (j = 1; j < 6; j++) + { + mask->deviceid = j; + request_XISelectEvent(req, Success); + } + + mask = (xXIEventMask*)((char*)mask + sizeof(xXIEventMask) + mask->mask_len * 4); + } +} + +static void test_XISelectEvents(void) +{ + int i; + xXIEventMask *mask; + xXISelectEventsReq *req; + req = (xXISelectEventsReq*)data; + + request_init(req, XISelectEvents); + + g_test_message("Testing for BadValue on zero-length masks"); + /* zero masks are BadValue, regardless of the window */ + req->num_masks = 0; + + req->win = None; + request_XISelectEvent(req, BadValue); + + req->win = ROOT_WINDOW_ID; + request_XISelectEvent(req, BadValue); + + req->win = CLIENT_WINDOW_ID; + request_XISelectEvent(req, BadValue); + + g_test_message("Testing for BadWindow."); + /* None window is BadWindow, regardless of the masks. + * We don't actually need to set the masks here, BadWindow must occur + * before checking the masks. + */ + req->win = None; + req->num_masks = 1; + request_XISelectEvent(req, BadWindow); + + req->num_masks = 2; + request_XISelectEvent(req, BadWindow); + + req->num_masks = 0xFF; + request_XISelectEvent(req, BadWindow); + + /* request size is 3, so 0xFFFC is the highest num_mask that doesn't + * overflow req->length */ + req->num_masks = 0xFFFC; + request_XISelectEvent(req, BadWindow); + + g_test_message("Triggering num_masks/length overflow"); + /* Integer overflow - req->length can't hold that much */ + req->num_masks = 0xFFFF; + request_XISelectEvent(req, BadLength); + + /* testing various device ids */ + req->win = ROOT_WINDOW_ID; + req->num_masks = 1; + + g_test_message("Testing existing device ids."); + for (i = 0; i < 6; i++) + { + mask = (xXIEventMask*)&req[1]; + mask->deviceid = i; + mask->mask_len = 1; + request_XISelectEvent(req, Success); + } + + g_test_message("Testing non-existing device ids."); + for (i = 6; i <= 0xFFFF; i++) + { + req->win = ROOT_WINDOW_ID; + req->num_masks = 1; + mask = (xXIEventMask*)&req[1]; + mask->deviceid = i; + mask->mask_len = 1; + request_XISelectEvent(req, BadDevice); + } + + request_XISelectEvents_masks(req); +} + +int main(int argc, char** argv) +{ + g_test_init(&argc, &argv,NULL); + g_test_bug_base("https://bugzilla.freedesktop.org/show_bug.cgi?id="); + + init_simple(); + + g_test_add_func("/xi2/protocol/XISelectEvents", test_XISelectEvents); + + return g_test_run(); +} +