]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Input: touchwin - reset the packet index on every complete packet
authorBryam Vargas <hexlabsecurity@proton.me>
Sun, 14 Jun 2026 01:07:20 +0000 (20:07 -0500)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sun, 14 Jun 2026 20:58:16 +0000 (13:58 -0700)
tw_interrupt() accumulates each non-zero serial byte into a fixed
three-byte buffer with a running index that is only reset once a full
packet has been received *and* the device's two Y bytes agree:

tw->data[tw->idx++] = data;
if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
...
tw->idx = 0;
}

The reset is gated on tw->data[1] == tw->data[2], a value the device
controls.  A malicious, malfunctioning or counterfeit Touchwindow
peripheral can stream non-zero bytes whose 2nd and 3rd bytes differ: the
index reaches TW_LENGTH without the equality holding, is never reset, and
keeps growing, so tw->data[tw->idx++] walks off the end of the three-byte
array and the rest of the heap-allocated struct tw, one attacker-chosen
byte at a time -- an unbounded, device-driven heap out-of-bounds write.

Reset the index on every completed packet and report an event only when
the two Y bytes match, like the other serio touchscreen drivers do.

Fixes: 11ea3173d5f2 ("Input: add driver for Touchwin serial touchscreens")
Cc: stable@vger.kernel.org
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Link: https://patch.msgid.link/20260613-b4-disp-69921bfd-v1-1-82c036899959@proton.me
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/touchscreen/touchwin.c

index 099fd88e65d840b49ea7e9b2d5c6e62c2e91eca5..3ed33378dfcd8e3b67d30a332a388d75e5b80999 100644 (file)
@@ -63,12 +63,15 @@ static irqreturn_t tw_interrupt(struct serio *serio,
        if (data) {             /* touch */
                tw->touched = 1;
                tw->data[tw->idx++] = data;
-               /* verify length and that the two Y's are the same */
-               if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
-                       input_report_abs(dev, ABS_X, tw->data[0]);
-                       input_report_abs(dev, ABS_Y, tw->data[1]);
-                       input_report_key(dev, BTN_TOUCH, 1);
-                       input_sync(dev);
+               /* a full packet ends the accumulation, valid or not */
+               if (tw->idx == TW_LENGTH) {
+                       /* report only if the two Y's are the same */
+                       if (tw->data[1] == tw->data[2]) {
+                               input_report_abs(dev, ABS_X, tw->data[0]);
+                               input_report_abs(dev, ABS_Y, tw->data[1]);
+                               input_report_key(dev, BTN_TOUCH, 1);
+                               input_sync(dev);
+                       }
                        tw->idx = 0;
                }
        } else if (tw->touched) {       /* untouch */