[PATCH] staging: octeon: check preamble strip lengths in cvm_oct_check_rcv_error()

From: Noah Weinberger

Date: Sun Nov 08 2026 - 18:50:52 EST


Hello,

cvm_oct_check_rcv_error() strips a non-standard preamble from
received error frames by counting leading 0x55 bytes and subtracting
that count (plus the SFD/preamble bytes) from the 16-bit
work->word1.len field, without any lower bound:

while (i < work->word1.len - 1) { ... }
if (*ptr == 0xd5) {
work->packet_ptr.s.addr += i + 1;
work->word1.len -= i + 5; /* can be -4 */
} else if ((*ptr & 0xf) == 0xd) {
work->packet_ptr.s.addr += i;
work->word1.len -= i + 4; /* can be -3 */
for (i = 0; i < work->word1.len; i++) { ... }
}

The walk allows i = len - 1, so the subtraction can underflow to
0xFFFC / 0xFFFD in the 16-bit field, and nothing revalidates the
length after the helper returns 0. An attacker on the segment can
force this by sending a frame that is almost entirely 0x55 preamble
ending in 0xd5 (or any byte with low nibble 0xd) with a bad FCS or
alignment error, on a port running with PRE_CHK off. The zero-copy
receive path then sets skb->len = 65532 over a small pool buffer
(skb_set_tail_pointer past skb->end), and the 0xd variant also runs
its nibble-shift loop for ~64K iterations past the buffer end - an
out-of-bounds read and write walk.

word1.len == 0 additionally makes the walk condition
work->word1.len - 1 wrap in the u64 bitfield container, so the 0x55
scan itself runs away.

The patch drops the work entry when the subtraction would
underflow or the frame is too short to contain a preamble plus SFD,
following the existing drop convention in the same function.

Affected version/commit: mainline 8a65162514fbad21a2c58ffed65f42b27e92b163 (v7.4-rc2 window, 2026-11-08), drivers/staging/octeon/ethernet-rx.c:99 (while (i < work->word1.len - 1) - len is u64:16, so len == 0 wraps to ~64K), :106-110 (word1.len -= i + 5 with i up to len-1: underflow to 0xFFFC), :113-122 (word1.len -= i + 4 then a nibble-shift loop over the underflowed len: ~64K OOB read/write walk), :286-287 (skb->len = work->word1.len; skb_set_tail_pointer past skb->end), :294, :317
Longstanding code (OCTEON SDK heritage), unchanged in the reviewed window.

This was found during an assisted (AI-supported) static source review of
mainline 8a65162514fbad21a2c58ffed65f42b27e92b163 (v7.4-rc2 window, 2026-11-08). The patch below has NOT been compiled and has
NOT been runtime tested, and no reproducer was produced - the triggering
conditions are described above instead. Please treat this as a report for
verification rather than a battle-tested fix.

---
diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c
index 5be43bbf6a..bb02bcbfbe 100644
--- a/drivers/staging/octeon/ethernet-rx.c
+++ b/drivers/staging/octeon/ethernet-rx.c
@@ -96,6 +96,12 @@ static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work)
cvmx_phys_to_ptr(work->packet_ptr.s.addr);
int i = 0;

+ if (work->word1.len < 2) {
+ printk_ratelimited("Port %d short frame, dropped\n",
+ port);
+ cvm_oct_free_work(work);
+ return 1;
+ }
while (i < work->word1.len - 1) {
if (*ptr != 0x55)
break;
@@ -105,6 +111,12 @@ static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work)

if (*ptr == 0xd5) {
/* Port received 0xd5 preamble */
+ if (work->word1.len < i + 5) {
+ printk_ratelimited("Port %d malformed preamble, dropped\n",
+ port);
+ cvm_oct_free_work(work);
+ return 1;
+ }
work->packet_ptr.s.addr += i + 1;
work->word1.len -= i + 5;
return 0;
@@ -112,6 +124,12 @@ static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work)

if ((*ptr & 0xf) == 0xd) {
/* Port received 0xd preamble */
+ if (work->word1.len < i + 4) {
+ printk_ratelimited("Port %d malformed preamble, dropped\n",
+ port);
+ cvm_oct_free_work(work);
+ return 1;
+ }
work->packet_ptr.s.addr += i;
work->word1.len -= i + 4;
for (i = 0; i < work->word1.len; i++) {