* [PATCH net v2] octeontx2-af: Fix BPID leak in nix_bp_enable()
@ 2026-09-10 3:16 nshettyj
2026-09-15 13:17 ` Paolo Abeni
0 siblings, 1 reply; 3+ messages in thread
From: nshettyj @ 2026-09-10 3:16 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: Rakesh Kudurumalla, Nitin Shetty J, Sunil Goutham,
Ratheesh Kannoth, Geetha sowjanya, Subbaraya Sundeep,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
From: Rakesh Kudurumalla <rkudurumalla@marvell.com>
For LBK interfaces, rvu_nix_get_bpid() allocates a BPID from the free
pool on every call. nix_bp_enable() called it unconditionally before
the loop, and again after the last channel was programmed, leaking a
BPID whenever that extra call's result went unused. With
req->chan_cnt == 0, the pre-loop call leaked a BPID on every call.
Move the allocation into the loop body so it runs exactly once per
channel actually programmed, and reject req->chan_cnt == 0 upfront.
Fixes: d6212d2e41a0 ("octeontx2-af: Create BPIDs free pool")
Signed-off-by: Nitin Shetty J <nshettyj@marvell.com>
Signed-off-by: Rakesh Kudurumalla <rkudurumalla@marvell.com>
---
changes in v2:
- Move the rvu_nix_get_bpid() call for LBK BPID allocation from before
the loop into the loop body.
- Validate req->chan_cnt before allocating BPIDs.
- updated commit message and fix tag
---
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index 153eb57bad06..3a43432d29c1 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -764,19 +764,25 @@ static int nix_bp_enable(struct rvu *rvu,
if (cpt_link && !rvu->hw->cpt_links)
return 0;
+ if (!req->chan_cnt)
+ return NIX_AF_ERR_INVALID_BPID_REQ;
+
pfvf = rvu_get_pfvf(rvu, pcifunc);
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
- bpid_base = rvu_nix_get_bpid(rvu, req, type, chan_id);
chan_base = pfvf->rx_chan_base + req->chan_base;
- bpid = bpid_base;
+ bpid_base = -1;
for (chan = chan_base; chan < (chan_base + req->chan_cnt); chan++) {
+ bpid = rvu_nix_get_bpid(rvu, req, type, chan_id);
if (bpid < 0) {
dev_warn(rvu->dev, "Fail to enable backpressure\n");
return -EINVAL;
}
+ if (bpid_base < 0)
+ bpid_base = bpid;
+
chan_v = nix_get_channel(chan, cpt_link);
cfg = rvu_read64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan_v));
@@ -784,7 +790,6 @@ static int nix_bp_enable(struct rvu *rvu,
rvu_write64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan_v),
cfg | (bpid & GENMASK_ULL(8, 0)) | BIT_ULL(16));
chan_id++;
- bpid = rvu_nix_get_bpid(rvu, req, type, chan_id);
}
for (chan = 0; chan < req->chan_cnt; chan++) {
--
2.48.1
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH net v2] octeontx2-af: Fix BPID leak in nix_bp_enable() 2026-09-10 3:16 [PATCH net v2] octeontx2-af: Fix BPID leak in nix_bp_enable() nshettyj @ 2026-09-15 13:17 ` Paolo Abeni 2026-09-28 6:45 ` Nitin Shetty J 0 siblings, 1 reply; 3+ messages in thread From: Paolo Abeni @ 2026-09-15 13:17 UTC (permalink / raw) To: nshettyj Cc: netdev, linux-kernel, rkudurumalla, sgoutham, rkannoth, gakula, sbhatta, andrew+netdev, davem, edumazet, kuba, pabeni, horms This is an AI-generated review of your patch. The human sending this email has considered the AI review valid, or at least plausible. Full review at: https://netdev-ai.bots.linux.dev/sashiko/ --- octeontx2-af: Fix BPID leak in nix_bp_enable() For LBK interfaces rvu_nix_get_bpid() takes a BPID from the free pool on every call, and nix_bp_enable() used to call it once before the loop and once more after the last channel was programmed. The patch moves the allocation into the loop body and rejects req->chan_cnt == 0 upfront. > diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c > index 153eb57bad067..3a43432d29c15 100644 > --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c > +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c > @@ -764,19 +764,25 @@ static int nix_bp_enable(struct rvu *rvu, > if (cpt_link && !rvu->hw->cpt_links) > return 0; > > + if (!req->chan_cnt) > + return NIX_AF_ERR_INVALID_BPID_REQ; [Severity: High] This isn't a bug introduced by this patch, but since the change adds request validation here, is the companion bound on req->chan_base / req->chan_cnt also needed for LBK? rvu_nix_get_bpid() range-checks the channel interval only for the CGX and SDP cases: case NIX_INTF_TYPE_CGX: if ((req->chan_base + req->chan_cnt) > NIX_BPIDS_PER_LMAC) return NIX_AF_ERR_INVALID_BPID_REQ; ... case NIX_INTF_TYPE_SDP: if ((req->chan_base + req->chan_cnt) > bp->sdp_bpid_cnt) return NIX_AF_ERR_INVALID_BPID_REQ; The NIX_INTF_TYPE_LBK case has no such check, and nix_interface_init() gives an LBK VF exactly one RX channel (pfvf->rx_chan_cnt = 1), while req->chan_base is a u16 copied straight out of the mailbox region. So chan_base = pfvf->rx_chan_base + req->chan_base can be any 16-bit value, and each iteration does a read-modify-write of the AF-global register NIX_AF_RX_CHANX_CFG(chan_v), which expands to 0x1A30 | (a) << 15. With req->chan_base near 0xFFF0 the offset reaches roughly 0x7FFF8000, well past the 1 << 28 per-block stride assumed by rvu_write64(): writeq(val, rvu->afreg_base + ((block << 28) | offset)); Can an LBK VF therefore reprogram the backpressure configuration of channels owned by other PF/VFs (arming them against a foreign BPID with BIT_ULL(16) set), or push the access outside the intended NIX register window? > pfvf = rvu_get_pfvf(rvu, pcifunc); > blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); > > - bpid_base = rvu_nix_get_bpid(rvu, req, type, chan_id); > chan_base = pfvf->rx_chan_base + req->chan_base; > - bpid = bpid_base; > + bpid_base = -1; > > for (chan = chan_base; chan < (chan_base + req->chan_cnt); chan++) { > + bpid = rvu_nix_get_bpid(rvu, req, type, chan_id); > if (bpid < 0) { > dev_warn(rvu->dev, "Fail to enable backpressure\n"); > return -EINVAL; > } [Severity: High] Now that the allocation happens per iteration, does this bare return leak the BPIDs already taken for the earlier channels? For LBK, each call consumes one entry of the finite free pool and records ownership: case NIX_INTF_TYPE_LBK: /* Alloc bpid from the free pool */ mutex_lock(&rvu->rsrc_lock); bpid = rvu_alloc_rsrc(&bp->bpids); if (bpid < 0) { mutex_unlock(&rvu->rsrc_lock); return NIX_AF_ERR_INVALID_BPID; } bp->fn_map[bpid] = req->hdr.pcifunc; bp->ref_cnt[bpid]++; A request with req->chan_cnt larger than the number of free pool entries fails deterministically once rvu_alloc_rsrc() returns -ENOSPC. At that point iterations 0..i-1 have already allocated BPIDs and written them into NIX_AF_RX_CHANX_CFG with the enable bit set, and this path returns without freeing them or clearing the enable bit. The response is also left zeroed (otx2_mbox_alloc_msg_rsp() memsets the msg region and rsp->chan_cnt is only set at the end of the function), so the requester learns no channel/BPID mapping and will not send a matching NIX_BP_DISABLE. Does that leave the BPIDs marked used with fn_map/ref_cnt set until FLR, and the affected RX channels armed against BPIDs that no aura maps? Would an unwind loop that frees the BPIDs and clears BIT_ULL(16) for the channels programmed so far be appropriate here? > > + if (bpid_base < 0) > + bpid_base = bpid; > + [Severity: Medium] Only the first allocation is remembered here, but the response is built from it arithmetically: for (chan = 0; chan < req->chan_cnt; chan++) { /* Map channel and bpid assign to it */ rsp->chan_bpid[chan] = ((req->chan_base + chan) & 0x7F) << 10 | (bpid_base & 0x3FF); if (req->bpid_per_chan) bpid_base++; } Does this diverge from what was actually programmed for LBK? The mailbox contract in mbox.h says: u8 bpid_per_chan; /* bpid_per_chan = 0 assigns single bp id for range of channels */ /* bpid_per_chan = 1 assigns separate bp id for each channel */ The LBK branch of rvu_nix_get_bpid() ignores req->bpid_per_chan and always allocates a fresh pool entry. With bpid_per_chan == 0 and chan_cnt > 1, each NIX_AF_RX_CHANX_CFG gets a different BPID while rsp->chan_bpid[] reports bpid_base for every channel. The requester then wires up only bpid_base: drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:mbox_handler_nix_bp_enable() { for (chan = 0; chan < rsp->chan_cnt; chan++) { chan_id = ((rsp->chan_bpid[chan] >> 10) & 0x7F); pfvf->bpid[chan_id] = rsp->chan_bpid[chan] & 0x3FF; } } With bpid_per_chan == 1 the response assumes bpid_base, bpid_base + 1, ..., but rvu_alloc_rsrc() returns the first free bit of a shared bitmap: id = find_first_zero_bit(rsrc->bmap, rsrc->max); if (id >= rsrc->max) return -ENOSPC; __set_bit(id, rsrc->bmap); and rvu_nix_get_bpid() takes and drops rvu->rsrc_lock once per channel, so a fragmented pool or an interleaved allocation by another LBK function gives non-contiguous BPIDs. Can the reported map then name a BPID owned by a different pcifunc? For bpid_per_chan == 0, would allocating once per request (and reporting the BPIDs actually written) match the documented behaviour better than "exactly once per channel actually programmed"? > chan_v = nix_get_channel(chan, cpt_link); > > cfg = rvu_read64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan_v)); > @@ -784,7 +790,6 @@ static int nix_bp_enable(struct rvu *rvu, > rvu_write64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan_v), > cfg | (bpid & GENMASK_ULL(8, 0)) | BIT_ULL(16)); > chan_id++; > - bpid = rvu_nix_get_bpid(rvu, req, type, chan_id); > } > > for (chan = 0; chan < req->chan_cnt; chan++) { [Severity: Critical] This is a pre-existing issue, but it interacts with the new mid-loop error return above, so it seems worth raising here. nix_bp_disable() recovers the BPID to release from the channel register: if (type == NIX_INTF_TYPE_LBK) { bpid = cfg & GENMASK(8, 0); mutex_lock(&rvu->rsrc_lock); rvu_free_rsrc(&bp->bpids, bpid - bp->free_pool_base); nix_setup_bpids() makes free_pool_base strictly positive: bp->free_pool_base = bp->cgx_bpid_cnt + bp->sdp_bpid_cnt + NIX_BPIDS_PER_CPT; So for any channel whose CFG BPID field is below free_pool_base - the reset value 0 on a channel that was never enabled, the tail channels left unprogrammed by the new error return in nix_bp_enable(), or a CGX-range BPID belonging to another function - the second argument is negative. rvu_free_rsrc() does not check it: void rvu_free_rsrc(struct rsrc_bmap *rsrc, int id) { if (!rsrc->bmap) return; __clear_bit(id, rsrc->bmap); } and generic___clear_bit() takes an unsigned long: unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); *p &= ~mask; Can this write outside bp->bpids.bmap? Since nothing bounds chan_base / chan_cnt for LBK, a VF sending NIX_BP_DISABLE over channels it never enabled appears to reach this with bpid == 0. Should nix_bp_disable() validate that the register-derived BPID falls inside the free pool range before converting it to a pool index? -- This is an AI-generated review. ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net v2] octeontx2-af: Fix BPID leak in nix_bp_enable() 2026-09-15 13:17 ` Paolo Abeni @ 2026-09-28 6:45 ` Nitin Shetty J 0 siblings, 0 replies; 3+ messages in thread From: Nitin Shetty J @ 2026-09-28 6:45 UTC (permalink / raw) To: Paolo Abeni Cc: netdev, linux-kernel, rkudurumalla, sgoutham, rkannoth, gakula, sbhatta, andrew+netdev, davem, edumazet, kuba, horms, nshettyj On 2026-09-15 at 18:47:14, Paolo Abeni (pabeni@redhat.com) wrote: > This is an AI-generated review of your patch. The human sending this > email has considered the AI review valid, or at least plausible. > Full review at: https://netdev-ai.bots.linux.dev/sashiko/ > --- > octeontx2-af: Fix BPID leak in nix_bp_enable() > > For LBK interfaces rvu_nix_get_bpid() takes a BPID from the free pool on > every call, and nix_bp_enable() used to call it once before the loop and > once more after the last channel was programmed. The patch moves the > allocation into the loop body and rejects req->chan_cnt == 0 upfront. > > > diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c > > index 153eb57bad067..3a43432d29c15 100644 > > --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c > > +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c > > @@ -764,19 +764,25 @@ static int nix_bp_enable(struct rvu *rvu, > > if (cpt_link && !rvu->hw->cpt_links) > > return 0; > > > > + if (!req->chan_cnt) > > + return NIX_AF_ERR_INVALID_BPID_REQ; > > [Severity: High] > This isn't a bug introduced by this patch, but since the change adds request > validation here, is the companion bound on req->chan_base / req->chan_cnt > also needed for LBK? > > rvu_nix_get_bpid() range-checks the channel interval only for the CGX and > SDP cases: > > case NIX_INTF_TYPE_CGX: > if ((req->chan_base + req->chan_cnt) > NIX_BPIDS_PER_LMAC) > return NIX_AF_ERR_INVALID_BPID_REQ; > ... > case NIX_INTF_TYPE_SDP: > if ((req->chan_base + req->chan_cnt) > bp->sdp_bpid_cnt) > return NIX_AF_ERR_INVALID_BPID_REQ; > > The NIX_INTF_TYPE_LBK case has no such check, and nix_interface_init() gives > an LBK VF exactly one RX channel (pfvf->rx_chan_cnt = 1), while req->chan_base > is a u16 copied straight out of the mailbox region. > > So chan_base = pfvf->rx_chan_base + req->chan_base can be any 16-bit value, > and each iteration does a read-modify-write of the AF-global register > NIX_AF_RX_CHANX_CFG(chan_v), which expands to 0x1A30 | (a) << 15. With > req->chan_base near 0xFFF0 the offset reaches roughly 0x7FFF8000, well past > the 1 << 28 per-block stride assumed by rvu_write64(): > > writeq(val, rvu->afreg_base + ((block << 28) | offset)); > > Can an LBK VF therefore reprogram the backpressure configuration of channels > owned by other PF/VFs (arming them against a foreign BPID with BIT_ULL(16) > set), or push the access outside the intended NIX register window? > Since it is exisiting issue will submit separate patch. > > pfvf = rvu_get_pfvf(rvu, pcifunc); > > blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); > > > > - bpid_base = rvu_nix_get_bpid(rvu, req, type, chan_id); > > chan_base = pfvf->rx_chan_base + req->chan_base; > > - bpid = bpid_base; > > + bpid_base = -1; > > > > for (chan = chan_base; chan < (chan_base + req->chan_cnt); chan++) { > > + bpid = rvu_nix_get_bpid(rvu, req, type, chan_id); > > if (bpid < 0) { > > dev_warn(rvu->dev, "Fail to enable backpressure\n"); > > return -EINVAL; > > } > > [Severity: High] > Now that the allocation happens per iteration, does this bare return leak the > BPIDs already taken for the earlier channels? > > For LBK, each call consumes one entry of the finite free pool and records > ownership: > > case NIX_INTF_TYPE_LBK: > /* Alloc bpid from the free pool */ > mutex_lock(&rvu->rsrc_lock); > bpid = rvu_alloc_rsrc(&bp->bpids); > if (bpid < 0) { > mutex_unlock(&rvu->rsrc_lock); > return NIX_AF_ERR_INVALID_BPID; > } > bp->fn_map[bpid] = req->hdr.pcifunc; > bp->ref_cnt[bpid]++; > > A request with req->chan_cnt larger than the number of free pool entries > fails deterministically once rvu_alloc_rsrc() returns -ENOSPC. At that point > iterations 0..i-1 have already allocated BPIDs and written them into > NIX_AF_RX_CHANX_CFG with the enable bit set, and this path returns without > freeing them or clearing the enable bit. > > The response is also left zeroed (otx2_mbox_alloc_msg_rsp() memsets the msg > region and rsp->chan_cnt is only set at the end of the function), so the > requester learns no channel/BPID mapping and will not send a matching > NIX_BP_DISABLE. Does that leave the BPIDs marked used with fn_map/ref_cnt > set until FLR, and the affected RX channels armed against BPIDs that no aura > maps? > > Would an unwind loop that frees the BPIDs and clears BIT_ULL(16) for the > channels programmed so far be appropriate here? > will fix it in next patch version > > > > + if (bpid_base < 0) > > + bpid_base = bpid; > > + > > [Severity: Medium] > Only the first allocation is remembered here, but the response is built from > it arithmetically: > > for (chan = 0; chan < req->chan_cnt; chan++) { > /* Map channel and bpid assign to it */ > rsp->chan_bpid[chan] = ((req->chan_base + chan) & 0x7F) << 10 | > (bpid_base & 0x3FF); > if (req->bpid_per_chan) > bpid_base++; > } > > Does this diverge from what was actually programmed for LBK? > > The mailbox contract in mbox.h says: > > u8 bpid_per_chan; > /* bpid_per_chan = 0 assigns single bp id for range of channels */ > /* bpid_per_chan = 1 assigns separate bp id for each channel */ > > The LBK branch of rvu_nix_get_bpid() ignores req->bpid_per_chan and always > allocates a fresh pool entry. With bpid_per_chan == 0 and chan_cnt > 1, each > NIX_AF_RX_CHANX_CFG gets a different BPID while rsp->chan_bpid[] reports > bpid_base for every channel. The requester then wires up only bpid_base: > > drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c:mbox_handler_nix_bp_enable() { > for (chan = 0; chan < rsp->chan_cnt; chan++) { > chan_id = ((rsp->chan_bpid[chan] >> 10) & 0x7F); > pfvf->bpid[chan_id] = rsp->chan_bpid[chan] & 0x3FF; > } > } > > With bpid_per_chan == 1 the response assumes bpid_base, bpid_base + 1, ..., > but rvu_alloc_rsrc() returns the first free bit of a shared bitmap: > > id = find_first_zero_bit(rsrc->bmap, rsrc->max); > if (id >= rsrc->max) > return -ENOSPC; > __set_bit(id, rsrc->bmap); > > and rvu_nix_get_bpid() takes and drops rvu->rsrc_lock once per channel, so a > fragmented pool or an interleaved allocation by another LBK function gives > non-contiguous BPIDs. Can the reported map then name a BPID owned by a > different pcifunc? > > For bpid_per_chan == 0, would allocating once per request (and reporting the > BPIDs actually written) match the documented behaviour better than "exactly > once per channel actually programmed"? > will fix it in next patch version > > chan_v = nix_get_channel(chan, cpt_link); > > > > cfg = rvu_read64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan_v)); > > @@ -784,7 +790,6 @@ static int nix_bp_enable(struct rvu *rvu, > > rvu_write64(rvu, blkaddr, NIX_AF_RX_CHANX_CFG(chan_v), > > cfg | (bpid & GENMASK_ULL(8, 0)) | BIT_ULL(16)); > > chan_id++; > > - bpid = rvu_nix_get_bpid(rvu, req, type, chan_id); > > } > > > > for (chan = 0; chan < req->chan_cnt; chan++) { > > [Severity: Critical] > This is a pre-existing issue, but it interacts with the new mid-loop error > return above, so it seems worth raising here. > > nix_bp_disable() recovers the BPID to release from the channel register: > > if (type == NIX_INTF_TYPE_LBK) { > bpid = cfg & GENMASK(8, 0); > mutex_lock(&rvu->rsrc_lock); > rvu_free_rsrc(&bp->bpids, bpid - bp->free_pool_base); > > nix_setup_bpids() makes free_pool_base strictly positive: > > bp->free_pool_base = bp->cgx_bpid_cnt + bp->sdp_bpid_cnt + > NIX_BPIDS_PER_CPT; > > So for any channel whose CFG BPID field is below free_pool_base - the reset > value 0 on a channel that was never enabled, the tail channels left > unprogrammed by the new error return in nix_bp_enable(), or a CGX-range BPID > belonging to another function - the second argument is negative. > > rvu_free_rsrc() does not check it: > > void rvu_free_rsrc(struct rsrc_bmap *rsrc, int id) > { > if (!rsrc->bmap) > return; > > __clear_bit(id, rsrc->bmap); > } > > and generic___clear_bit() takes an unsigned long: > > unsigned long mask = BIT_MASK(nr); > unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); > > *p &= ~mask; > > Can this write outside bp->bpids.bmap? Since nothing bounds chan_base / > chan_cnt for LBK, a VF sending NIX_BP_DISABLE over channels it never enabled > appears to reach this with bpid == 0. Should nix_bp_disable() validate that > the register-derived BPID falls inside the free pool range before converting > it to a pool index? will fix it in next patch version > -- > This is an AI-generated review. > > ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-28 6:46 UTC | newest] Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-10 3:16 [PATCH net v2] octeontx2-af: Fix BPID leak in nix_bp_enable() nshettyj 2026-09-15 13:17 ` Paolo Abeni 2026-09-28 6:45 ` Nitin Shetty J
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®