From 74b4ca132fdc3c63f6554ec88903ac00a6c287b6 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 16 Apr 2026 10:29:26 +1000 Subject: [PATCH] tools/debug-tablet-pad: add bounds checks for array accesses A few not-really-an-issue fixes found by Claude: 1. ctx->buttons_down[number]: the 'number' value comes from libinput_event_tablet_pad_get_button_number() and is written into a fixed-size array of 32 elements without bounds checking. A crafted or malicious device reporting button numbers >= 32 causes a stack buffer overflow. 2. ctx->ring[number], ctx->strip[number], ctx->dial[number]: these are fixed-size arrays of 2 elements each. Ring/strip/dial numbers from libinput events are used as indices without bounds checking. Values >= 2 cause out-of-bounds writes. 3. assert()-based error handling for open() and libevdev_new_from_fd(): assert() is compiled to a no-op in release builds (NDEBUG). This means that in release builds, a failed open() returns fd=-1, and libevdev_new_from_fd() is called with an invalid fd. The result is undefined behavior. 4. Variable-length array (VLA) 'empty[termwidth]' in print_bar(): termwidth comes from an ioctl(TIOCGWINSZ) call and could be very large, causing a stack overflow. Replace with a fixed-size buffer. None of these really matter for a niche debugging tool. Co-Authored-by: Claude Code Part-of: --- tools/libinput-debug-tablet-pad.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tools/libinput-debug-tablet-pad.c b/tools/libinput-debug-tablet-pad.c index 51b678f6..92a6bb13 100644 --- a/tools/libinput-debug-tablet-pad.c +++ b/tools/libinput-debug-tablet-pad.c @@ -142,11 +142,11 @@ print_rel_wheel(struct context *ctx, unsigned int code, int value) static void print_bar(const char *header, double value, double normalized) { - char empty[termwidth]; + char empty[256]; bool oob = false; /* the bar is minimum 10 chars, otherwise 78 or whatever fits. 32 is the manually-added up length of the prefix + [|] */ - const int width = clamp(termwidth - 32, 10, 78); + const int width = clamp(termwidth - 32, 10, min(78, (int)sizeof(empty) - 1)); int left_pad, right_pad; memset(empty, '-', sizeof empty); @@ -259,8 +259,12 @@ handle_device_added(struct context *ctx, struct libinput_event *ev) devnode = udev_device_get_devnode(udev_device); if (devnode) { int fd = open(devnode, O_RDONLY | O_NONBLOCK); - assert(fd != -1); - assert(libevdev_new_from_fd(fd, &ctx->evdev) == 0); + if (fd == -1) + return; + if (libevdev_new_from_fd(fd, &ctx->evdev) != 0) { + close(fd); + return; + } } } @@ -304,26 +308,30 @@ handle_libinput_events(struct context *ctx) pev = libinput_event_get_tablet_pad_event(ev); number = libinput_event_tablet_pad_get_button_number(pev); state = libinput_event_tablet_pad_get_button_state(pev); - ctx->buttons_down[number] = - state == LIBINPUT_BUTTON_STATE_PRESSED ? 1 : 0; + if (number < ARRAY_LENGTH(ctx->buttons_down)) + ctx->buttons_down[number] = + state == LIBINPUT_BUTTON_STATE_PRESSED ? 1 : 0; break; case LIBINPUT_EVENT_TABLET_PAD_RING: pev = libinput_event_get_tablet_pad_event(ev); number = libinput_event_tablet_pad_get_ring_number(pev); value = libinput_event_tablet_pad_get_ring_position(pev); - ctx->ring[number] = value; + if (number < ARRAY_LENGTH(ctx->ring)) + ctx->ring[number] = value; break; case LIBINPUT_EVENT_TABLET_PAD_STRIP: pev = libinput_event_get_tablet_pad_event(ev); number = libinput_event_tablet_pad_get_strip_number(pev); value = libinput_event_tablet_pad_get_strip_position(pev); - ctx->strip[number] = value; + if (number < ARRAY_LENGTH(ctx->strip)) + ctx->strip[number] = value; break; case LIBINPUT_EVENT_TABLET_PAD_DIAL: { pev = libinput_event_get_tablet_pad_event(ev); number = libinput_event_tablet_pad_get_dial_number(pev); value = libinput_event_tablet_pad_get_dial_delta_v120(pev); - ctx->dial[number] = value; + if (number < ARRAY_LENGTH(ctx->dial)) + ctx->dial[number] = value; break; } case LIBINPUT_EVENT_TABLET_PAD_KEY: {