From: Hui Peng <benquike@gmail.com>
To: Johan Hovold <johan@kernel.org>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
Hui Peng <benquike@gmail.com>
Subject: [PATCH v2] [USB][serial/garmin_gps] Fix signed integer underflow and OOB read on packet length
Date: Sat, 19 Sep 2026 08:08:49 +0000 [thread overview]
Message-ID: <20260919080849.3005763-1-benquike@gmail.com> (raw)
In-Reply-To: <2026091908-imprint-rejoicing-6933@gregkh>
getDataLength() returns a signed int from __le32_to_cpup((__le32
*)(garmin_data_p + 8)). When bit 31 is set (e.g. 0x80000004), len is
negative (-2147483644), bypassing the upper bound check (GSP_INITIAL_OFFSET
+ len > GSP_MAX_BUFSIZ) in gsp_send() and triggering a KASAN slab-out-of-
bounds read in garmin_write_bulk() when GARMIN_PKTHDR_LENGTH + len wraps
around to 16.
Kernel stack trace:
BUG: KASAN: slab-out-of-bounds in garmin_write_bulk+0x164/0x3b0
Read of size 16 at addr ffff88800791400c by task poc_verify/188
Call Trace:
<TASK>
dump_stack_lvl+0x4d/0x70
print_report+0xc4/0x610
kasan_report+0xb8/0xf0
kasan_check_range+0x118/0x190
memcpy+0x24/0x60
garmin_write_bulk+0x164/0x3b0
gsp_send+0x218/0x490
garmin_write+0x142/0x2c0
tty_write+0x294/0x540
vfs_write+0x412/0x640
</TASK>
Note that garmin_write_bulk() and garmin_write_bulk_callback() are
triggered from local userspace calling write(fd, ...) on /dev/ttyUSB0
(even with a normal USB device attached) by writing a 12-byte packet
with a negative 32-bit length field (e.g. 0x80ffffff) at byte offset 4.
Kernel stack trace (Linux 7.3.0-rc3):
==================================================================
BUG: KASAN: slab-out-of-bounds in garmin_write_bulk+0x922/0xbf0
Read of size 12 at addr ffff88800f8ef958 by task usb_poc_verify/162
Call Trace:
<TASK>
dump_stack_lvl+0x70/0xa0
print_report+0x153/0x4c6
kasan_report+0xf1/0x120
kasan_check_range+0x11c/0x200
__asan_memcpy+0x29/0x70
garmin_write_bulk+0x922/0xbf0
garmin_write+0x3e6/0x770
serial_write+0x167/0x2b0
n_tty_write+0x5f4/0x1020
file_tty_write.isra.0+0x411/0x760
vfs_write+0x671/0xd20
ksys_write+0x1bb/0x210
do_syscall_64+0xda/0x4b0
</TASK>
==================================================================
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
v2: Send to linux-usb@vger.kernel.org via git send-email with Assisted-by: LLM tag and clarify local userspace write() trigger.
drivers/usb/serial/garmin_gps.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 8020149f5..ea10af159 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -208,12 +208,12 @@ static inline int getLayerId(const __u8 *usbPacket)
return __le32_to_cpup((__le32 *)(usbPacket));
}
-static inline int getPacketId(const __u8 *usbPacket)
+static inline __u32 getPacketId(const __u8 *usbPacket)
{
return __le32_to_cpup((__le32 *)(usbPacket+4));
}
-static inline int getDataLength(const __u8 *usbPacket)
+static inline __u32 getDataLength(const __u8 *usbPacket)
{
return __le32_to_cpup((__le32 *)(usbPacket+8));
}
@@ -607,6 +607,10 @@ static int gsp_send(struct garmin_data *garmin_data_p,
if (k >= GARMIN_PKTHDR_LENGTH) {
pktid = getPacketId(garmin_data_p->outbuffer);
datalen = getDataLength(garmin_data_p->outbuffer);
+ if (datalen < 0 || datalen > GPS_OUT_BUFSIZ - GARMIN_PKTHDR_LENGTH) {
+ garmin_data_p->outsize = 0;
+ return -3;
+ }
i = GARMIN_PKTHDR_LENGTH + datalen;
if (k < i)
return 0;
@@ -769,8 +773,13 @@ static int nat_receive(struct garmin_data *garmin_data_p,
/* do we have a complete packet ? */
if (garmin_data_p->insize >= GARMIN_PKTHDR_LENGTH) {
- len = GARMIN_PKTHDR_LENGTH+
- getDataLength(garmin_data_p->inbuffer);
+ __u32 dlen = getDataLength(garmin_data_p->inbuffer);
+
+ if (dlen > GPS_IN_BUFSIZ - GARMIN_PKTHDR_LENGTH) {
+ garmin_data_p->insize = 0;
+ break;
+ }
+ len = GARMIN_PKTHDR_LENGTH + dlen;
if (garmin_data_p->insize >= len) {
garmin_write_bulk(garmin_data_p->port,
garmin_data_p->inbuffer,
@@ -951,7 +960,8 @@ static void garmin_write_bulk_callback(struct urb *urb)
struct garmin_data *garmin_data_p =
usb_get_serial_port_data(port);
- if (getLayerId(urb->transfer_buffer) == GARMIN_LAYERID_APPL) {
+ if (urb->transfer_buffer_length >= 5 &&
+ getLayerId(urb->transfer_buffer) == GARMIN_LAYERID_APPL) {
if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
gsp_send_ack(garmin_data_p,
--
2.55.0.1082.g2b9226bbc0-goog
next parent reply other threads:[~2026-09-19 8:08 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <2026091908-imprint-rejoicing-6933@gregkh>
2026-09-19 8:08 ` Hui Peng [this message]
2026-09-19 11:28 ` [PATCH v3] USB: serial: garmin_gps: fix signed integer underflow and OOB read on packet length Hui Peng
2026-09-20 5:14 ` Greg KH
2026-09-21 3:02 ` [PATCH v4] USB: serial: garmin_gps: validate packet data length in nat_receive() Hui Peng
2026-09-21 15:10 ` krzk
2026-09-21 15:16 ` krzk
2026-09-30 7:52 ` Hui Peng
2026-10-02 9:50 ` Johan Hovold
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260919080849.3005763-1-benquike@gmail.com \
--to=benquike@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=johan@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.