From: netdev-bot+sashiko@kernel.org
To: james@codeconstruct.com.au
Cc: jk@codeconstruct.com.au, matt@codeconstruct.com.au,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, kees@kernel.org,
gustavoars@kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-hardening@vger.kernel.org
Subject: Re: [PATCH net-next v2 2/7] net: mctp: usblib: Add test for splits inside headers
Date: Wed, 09 Sep 2026 05:43:20 +0000 [thread overview]
Message-ID: <178893260008.219967.13546498074867789571@kernel.org> (raw)
In-Reply-To: <20260903-dev-mctp-usb-lib-test-v2-2-313cf15e0b56@codeconstruct.com.au>
Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 0 (1) · Low: 2
- [High] mctp_usblib_test_rx_split_header() releases the received sk_buff
with kfree(skb) instead of kfree_skb()/consume_skb()…
- [Low] mctp_usblib_test_rx_header_splits() declares `size_t i;`
(drivers/net/mctp/mctp-usblib-test.c:445) but never reads or writes it…
- [Low] Both mctp_usblib_test_rx_complete() calls in the new helper
discard the int return value (drivers/net/mctp/mctp-usblib-test.c:420…
Pre-existing issues:
- [Medium] The new helper reads dev->rx_pkts.qlen directly (lines 426 and
430) and removes entries with the lock-free __skb_dequeue() (line…
--
net: mctp: usblib: Add test for splits inside headers
This adds a KUnit case, mctp_usblib_test_rx_header_splits(), that feeds a
16-byte MCTP-over-USB packet in two transfers for every split offset from 1
to 8, and drops the three parameterized span cases that covered a subset of
those offsets.
> diff --git a/drivers/net/mctp/mctp-usblib-test.c b/drivers/net/mctp/mctp-usblib-test.c
> index 4c27307900fb..a2ca79f61910 100644
> --- a/drivers/net/mctp/mctp-usblib-test.c
> +++ b/drivers/net/mctp/mctp-usblib-test.c
[ ... ]
> @@ -378,12 +380,6 @@ static const struct mctp_usblib_test_pkt_span mctp_usblib_test_pkt_spans[] = {
> { "1p1x-complete", 1, { 8 }, 1, { 8 } },
> /* Two small packets combined within one transfer */
> { "2p1x-combined", 2, { 8, 8 }, 1, { 16 } },
> - /* A packet split over two transfers, at the MCTP payload */
> - { "1p2x-split-payload", 1, { 16 }, 2, { 8, 8 } },
> - /* A packet split over two transfers, at the USB transport header */
> - { "1p2x-split-usbhdr", 1, { 16 }, 2, { 2, 14 } },
> - /* A packet split over two transfers, at the MCTP header */
> - { "1p2x-split-mctphdr", 1, { 16 }, 2, { 6, 10 } },
> /* Single packet split over 3 transfers, middle entirely continuation */
> { "1p3x-split", 1, { 12 }, 3, { 4, 4, 4 } },
> /* A packet split over 5 transfers, splitting on and between each header. */
[ ... ]
> @@ -403,10 +399,73 @@ static const struct mctp_usblib_test_pkt_span mctp_usblib_test_pkt_spans[] = {
> KUNIT_ARRAY_PARAM(mctp_usblib_test_rx_pkt_span, mctp_usblib_test_pkt_spans,
> mctp_usblib_test_pkt_span_to_desc);
>
> +static void mctp_usblib_test_rx_split_header(struct kunit *test, size_t offset,
> + struct mctp_usblib_test_dev *dev,
> + struct mctp_usblib_rx *rx)
> +{
> + struct sk_buff *skb;
> + size_t buflen, len;
> + u8 packet[16];
> + void *buf;
> + int rc;
> +
> + len = sizeof(packet);
> + mctp_usblib_test_init_pkt(packet, len, len);
> +
> + rc = mctp_usblib_rx_prepare(dev->ndev, rx, &buf, &buflen, GFP_KERNEL);
> + KUNIT_ASSERT_EQ(test, rc, 0);
> + KUNIT_ASSERT_GE(test, buflen, len);
> +
> + memcpy(buf, packet, offset);
> + mctp_usblib_test_rx_complete(dev->ndev, rx, offset);
[Severity: Low]
Should the return value of mctp_usblib_test_rx_complete() be checked here,
as the other call sites in this file do?
mctp_usblib_test_rx_single() and mctp_usblib_test_rx_pkt_span() both follow
each completion with:
rc = mctp_usblib_test_rx_complete(dev->ndev, rx, len);
KUNIT_ASSERT_EQ(test, rc, 0);
mctp_usblib_rx_complete() has err_reset paths that return -ENOMSG/-EPROTO
and also tear down the RX context:
drivers/net/mctp/mctp-usblib.c:mctp_usblib_rx_complete() {
...
err_reset:
dev_kfree_skb_any(rx->skb);
rx->skb = NULL;
return rc;
}
If the first completion takes that path, the helper carries on: the second
mctp_usblib_rx_prepare() allocates a fresh skb, the mid-sequence
KUNIT_ASSERT_EQ(test, dev->rx_pkts.qlen, 0) still passes, the remainder of
the packet is fed at the wrong offset, and the failure only surfaces at the
later qlen check. Doesn't that work against the stated reason for
unrolling, "so stack traces point to the invocation with the failing
length"?
Related to that, the removed cases 1p2x-split-payload, 1p2x-split-usbhdr
and 1p2x-split-mctphdr did assert rc == 0 on every transfer, so is the
commit message wording "remove parameterized tests that are now covered"
fully accurate?
The new checks also only compare skb->len against len - HDR_LEN, and never
compare the delivered bytes against packet + sizeof(struct mctp_usb_hdr).
Would a regression that corrupts bytes across the split (say the MCTP
src/flags fields around offsets 6 to 8) while leaving dest routable and the
length unchanged still pass all of the new assertions?
> +
> + rc = mctp_usblib_rx_prepare(dev->ndev, rx, &buf, &buflen,
> + GFP_KERNEL);
> + KUNIT_ASSERT_EQ(test, rc, 0);
> + KUNIT_ASSERT_GE(test, buflen, len);
> + KUNIT_ASSERT_EQ(test, dev->rx_pkts.qlen, 0);
> +
> + memcpy(buf, packet + offset, len - offset);
> + mctp_usblib_test_rx_complete(dev->ndev, rx, len - offset);
> + KUNIT_EXPECT_EQ(test, dev->rx_pkts.qlen, 1);
> +
> + skb = __skb_dequeue(&dev->rx_pkts);
[Severity: Medium]
This isn't something introduced by this patch, but the reads of
dev->rx_pkts.qlen and the __skb_dequeue() here run with no lock held, while
the only producer, mctp_usblib_test_dst_output(), inserts with the locked
variant from the netif_rx/backlog delivery context:
skb_queue_tail(&dev->rx_pkts, skb);
The existing mctp_usblib_test_rx_single() and
mctp_usblib_test_rx_pkt_span() already do the same unlocked reads, so this
is pre-existing rather than new, and delivery does appear to be synchronous
inside the local_bh_disable()/local_bh_enable() window of
mctp_usblib_test_rx_complete() in both default and PREEMPT_RT
configurations. Would skb_dequeue() and skb_queue_len() be preferable here
anyway, so the helper does not depend on that assumption?
> + KUNIT_EXPECT_NOT_NULL(test, skb);
> + if (skb) {
> + KUNIT_EXPECT_EQ(test, skb->len, len - HDR_LEN);
> + kfree(skb);
^^^^^
[Severity: High]
Does this leak the skb data buffer? kfree() releases only the struct
sk_buff itself and skips skb_release_all()/skb_free_head(), so skb->head is
never freed.
The skb reaching this queue is the one allocated in
mctp_usblib_rx_prepare():
drivers/net/mctp/mctp-usblib.c:mctp_usblib_rx_prepare() {
...
len = rx->span ? roundup(MCTP_USB_1_1_PKTLEN_MAX, rx->ep_pktlen)
: MCTP_USB_1_0_XFER_SIZE;
if (!skb) {
skb = __netdev_alloc_skb(netdev, len, gfp);
...
}
and with span mode that is an 8 KiB head. On the second transfer
pkt_len == skb->len, so mctp_usblib_rx_complete() hands that same skb
onward:
drivers/net/mctp/mctp-usblib.c:mctp_usblib_rx_complete() {
...
/* we have (exactly) a complete packet, RX it directly */
if (pkt_len == skb->len) {
mctp_usblib_rx(netdev, skb);
rx->skb = NULL;
break;
}
...
}
That gives one leaked head buffer per call, so eight per run of
mctp_usblib_test_rx_header_splits(). Every other release site in this file
uses kfree_skb() - mctp_usblib_test_rx_single(),
mctp_usblib_test_rx_pkt_span() and mctp_usblib_dev_tx() - and kfree_skb() is
NULL safe, so the "if (skb)" guard around it could be dropped as well.
The v2 changelog says "Fix sk_buff leak"; with kfree() here, is the leak
actually closed? The same kfree() is still present at the end of the
series.
> + }
> +}
> +
> +static void mctp_usblib_test_rx_header_splits(struct kunit *test)
> +{
> + struct mctp_usblib_test_dev *dev;
> + struct mctp_usblib_test_ctx *ctx;
> + struct mctp_usblib_rx *rx;
> + size_t i;
^
[Severity: Low]
This isn't a bug, but i is never used once the invocations are unrolled, so
this emits a -Wunused-variable warning and fails the build under
CONFIG_WERROR with CONFIG_MCTP_TRANSPORT_USBLIB_TEST enabled. The
declaration is removed again by the next patch in the series, "net: mctp:
usblib: Add tests of invalid headers", so the final state of the series is
clean; could it be dropped in this patch instead to keep the series
bisectable?
> +
> + ctx = mctp_usblib_test_init(test);
> + rx = mctp_usblib_test_rx_init(test, true);
> + dev = ctx->dev;
> +
> + /* Unrolling here so stack traces point to the invocation with the
> + * failing length.
> + */
> + mctp_usblib_test_rx_split_header(test, 1, dev, rx);
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260903-dev-mctp-usb-lib-test-v2-0-313cf15e0b56%40codeconstruct.com.au
next prev parent reply other threads:[~2026-09-09 5:43 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-03 2:42 [PATCH net-next v2 0/7] net: mctp: usblib: Increase coverage of kunit testing James Lee
2026-09-03 2:42 ` [PATCH net-next v2 1/7] net: mctp: usblib: Add to parameterized kunit tests James Lee
2026-09-09 5:43 ` netdev-bot+sashiko
2026-09-03 2:42 ` [PATCH net-next v2 2/7] net: mctp: usblib: Add test for splits inside headers James Lee
2026-09-09 5:43 ` netdev-bot+sashiko [this message]
2026-09-09 7:12 ` Simon Horman
2026-09-03 2:42 ` [PATCH net-next v2 3/7] net: mctp: usblib: Add tests of invalid headers James Lee
2026-09-09 5:43 ` netdev-bot+sashiko
2026-09-03 2:42 ` [PATCH net-next v2 4/7] net: mctp: usblib: Complete rx tests James Lee
2026-09-09 5:43 ` netdev-bot+sashiko
2026-09-03 2:42 ` [PATCH net-next v2 5/7] net: mctp: usblib: Simplify allocation logic in mctp_usblib_test_rx_init James Lee
2026-09-03 2:42 ` [PATCH net-next v2 6/7] net: mctp: usblib: Add initial kunit tx tests James Lee
2026-09-09 5:43 ` netdev-bot+sashiko
2026-09-03 2:42 ` [PATCH net-next v2 7/7] net: mctp: usblib: Add test for failing append James Lee
2026-09-09 5:43 ` netdev-bot+sashiko
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=178893260008.219967.13546498074867789571@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=gustavoars@kernel.org \
--cc=james@codeconstruct.com.au \
--cc=jk@codeconstruct.com.au \
--cc=kees@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=matt@codeconstruct.com.au \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox