* [PATCH v1 1/8] net/zxdh: reject out-of-range VLAN ID from VF
2026-11-07 3:04 [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Junlong Wang
@ 2026-11-07 3:04 ` Junlong Wang
2026-11-07 3:04 ` [PATCH v1 2/8] net/zxdh: initialise DTB entry in flow query Junlong Wang
` (8 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-07 3:04 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1548 bytes --]
The PF handler for a VF's ZXDH_VLAN_FILTER_ADD/DEL message passes the
16-bit vlan_id from the message body to zxdh_vlan_filter_table_set()
without a range check. An id above 4095 becomes a row index outside
the VLAN filter groups the PF sets up for the function, and the NP
layer below only checks it against the end of the whole ERAM, so one
request from a guest reads, modifies and writes back unrelated table
rows.
Reject ids above RTE_ETHER_MAX_VLAN_ID where the value is taken out of
the message. Valid ids behave as before.
Fixes: b4f996b7fcf6 ("net/zxdh: optimize VLAN filter and offload")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index 4b01daf37a..f0f0522817 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -1552,7 +1552,12 @@ zxdh_vf_vlan_filter_table_process(struct zxdh_hw *hw, uint16_t vport,
void *reply_data_addr = ZXDH_ADDR_OF(msg_reply_body, res_info, reply_data);
int ret = 0;
- ret = zxdh_vlan_filter_table_set(hw, vport, vlan_id, enable);
+ if (vlan_id > RTE_ETHER_MAX_VLAN_ID) {
+ PMD_DRV_LOG(ERR, "vlan id %d is invalid", vlan_id);
+ ret = -EINVAL;
+ } else {
+ ret = zxdh_vlan_filter_table_set(hw, vport, vlan_id, enable);
+ }
if (ret)
sprintf(str, "vlan filter op-code[%d] vlan id:%d failed, code:%d\n",
enable, vlan_id, ret);
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 3001 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v1 2/8] net/zxdh: initialise DTB entry in flow query
2026-11-07 3:04 [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Junlong Wang
2026-11-07 3:04 ` [PATCH v1 1/8] net/zxdh: reject out-of-range VLAN ID from VF Junlong Wang
@ 2026-11-07 3:04 ` Junlong Wang
2026-11-07 3:04 ` [PATCH v1 3/8] net/zxdh: refuse PF mailbox requests not naming a VF of this PF Junlong Wang
` (7 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-07 3:04 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1638 bytes --]
zxdh_hw_flow_query() declares its ZXDH_DTB_USER_ENTRY_T without an
initialiser, unlike the sibling declarations in
zxdh_hw_flow_insert() and zxdh_hw_flow_del() which use = {0}. When
the flow index is ZXDH_MAX_FLOW_NUM or more,
zxdh_fd_flow_to_dtbentry() returns before writing the entry, and the
free_res path then calls rte_free() on the uninitialised
p_entry_data pointer. The index comes from a VF's ZXDH_FLOW_HW_GET
mailbox message, and pf_fd_hw_query_count() only checks it after the
query, so a single request from a guest frees garbage and kills the
process driving the PF in a release build.
Initialise the entry the way the siblings do: both error paths
(out-of-range index and rte_zmalloc() failure) then end in
rte_free(NULL), a no-op, and the VF gets the existing error reply.
Valid indices behave as before.
Fixes: d0af48c1b952 ("net/zxdh: support flow API")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_flow.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_flow.c b/drivers/net/zxdh/zxdh_flow.c
index 56ac34c6e3..a51ce9ef77 100644
--- a/drivers/net/zxdh/zxdh_flow.c
+++ b/drivers/net/zxdh/zxdh_flow.c
@@ -1137,7 +1137,7 @@ zxdh_hw_flow_query(struct rte_eth_dev *dev, struct zxdh_flow *dh_flow,
struct zxdh_hw *hw = dev->data->dev_private;
int ret = 0;
struct zxdh_flow_info *flow = &dh_flow->flowentry;
- ZXDH_DTB_USER_ENTRY_T dtb_entry;
+ ZXDH_DTB_USER_ENTRY_T dtb_entry = {0};
uint16_t handle_idx;
handle_idx = zxdh_fd_flow_to_dtbentry(hw, flow, &dtb_entry);
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 2832 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v1 3/8] net/zxdh: refuse PF mailbox requests not naming a VF of this PF
2026-11-07 3:04 [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Junlong Wang
2026-11-07 3:04 ` [PATCH v1 1/8] net/zxdh: reject out-of-range VLAN ID from VF Junlong Wang
2026-11-07 3:04 ` [PATCH v1 2/8] net/zxdh: initialise DTB entry in flow query Junlong Wang
@ 2026-11-07 3:04 ` Junlong Wang
2026-11-07 3:04 ` [PATCH v1 4/8] net/zxdh: initialise multicast delete flag Junlong Wang
` (6 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-07 3:04 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 3055 bytes --]
The PF dispatcher zxdh_config_process_callback() hands the
msghead->vport and msghead->pcieid of a request, both written by the
VF into its BAR0 mailbox window, straight to the handlers, and
nothing on the receive path compares them with the sender. One
message can therefore be aimed at any port: it rewrites or deletes
the port attribute entry of the PF itself or of another VF.
Add zxdh_msg_head_is_own_vf() before the dispatch: pcieid must be a
VF pcieid whose PF is this PF, and vport must carry vf_flag with this
PF's epid and pfid. The vport vfid is deliberately not compared with
the pcieid function number: vfid is allocated sequentially across all
PFs of an epid while the function number restarts at 0 for each PF.
Anything else is answered ZXDH_REPS_INVALID and no handler runs;
messages built by zxdh_msg_head_build() pass unchanged.
A VF that forges both pcieid and vport of a sibling VF of the same PF
still cannot be told apart by the driver; protection against that
case is planned on the device side. This patch closes the PF-port and
cross-PF cases.
Fixes: 2e22ff2528db ("net/zxdh: provide PF/VF message interrupt callback")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index f0f0522817..6aafefe289 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -2397,6 +2397,24 @@ static const zxdh_msg_process_callback zxdh_proc_cb[] = {
[ZXDH_FLOW_HW_FLUSH] = zxdh_vf_flow_hw_flush,
};
+/*
+ * The msg head is written by the VF driver: the vport and pcieid it carries
+ * must name one VF of this PF, not the PF itself or a port of another PF.
+ */
+static bool
+zxdh_msg_head_is_own_vf(struct zxdh_hw *hw, struct zxdh_msg_head *msghead)
+{
+ union zxdh_virport_num vport = {.vport = msghead->vport};
+
+ if ((msghead->pcieid & ZXDH_PCIEID_IS_PF_MASK) ||
+ ZXDH_PF_PCIE_ID(msghead->pcieid) != hw->pcie_id)
+ return false;
+
+ return vport.vf_flag &&
+ vport.epid == hw->vport.epid &&
+ vport.pfid == hw->vport.pfid;
+}
+
static inline int
zxdh_config_process_callback(struct zxdh_hw *hw, struct zxdh_msg_info *msg_info,
void *res, uint16_t *res_len)
@@ -2408,7 +2426,12 @@ zxdh_config_process_callback(struct zxdh_hw *hw, struct zxdh_msg_info *msg_info,
PMD_DRV_LOG(ERR, " invalid param");
return -1;
}
- if (zxdh_proc_cb[msghead->msg_type]) {
+
+ if (!zxdh_msg_head_is_own_vf(hw, msghead)) {
+ PMD_DRV_LOG(ERR, "msg_type %d vport 0x%x pcieid 0x%x is not a VF of PF pcieid 0x%x",
+ msghead->msg_type, msghead->vport, msghead->pcieid, hw->pcie_id);
+ ZXDH_SET(msg_reply_body, res, flag, ZXDH_REPS_INVALID);
+ } else if (zxdh_proc_cb[msghead->msg_type]) {
ret = zxdh_proc_cb[msghead->msg_type](hw, msghead->vport, msghead->pcieid,
(void *)&msg_info->data, res, res_len);
if (!ret)
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 6027 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v1 4/8] net/zxdh: initialise multicast delete flag
2026-11-07 3:04 [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Junlong Wang
` (2 preceding siblings ...)
2026-11-07 3:04 ` [PATCH v1 3/8] net/zxdh: refuse PF mailbox requests not naming a VF of this PF Junlong Wang
@ 2026-11-07 3:04 ` Junlong Wang
2026-11-07 3:04 ` [PATCH v1 5/8] net/zxdh: free receive buffer on the async no-ack exit Junlong Wang
` (5 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-07 3:04 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1617 bytes --]
zxdh_del_mac_table() decides whether a multicast address still has
subscribers with del_flag, but the flag has no initialiser and is
only ever assigned 1. The loop that walks the group entries breaks
out as soon as it finds one that still has members, leaving del_flag
unwritten; at -O1 and above, including the default -O2 build, the
compiler resolves the subsequent test to true, so the delete loop
always runs.
Whenever one port therefore leaves a shared multicast address, the
entries holding the PF's and every other VF's membership are deleted
too: the PF and sibling VFs silently lose their multicast reception,
with no attacker involved.
Restore the missing initialiser, so the entry is deleted only when
the last member leaves.
Fixes: b64747ef2951 ("net/zxdh: fix MAC configuration failure")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_tables.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_tables.c b/drivers/net/zxdh/zxdh_tables.c
index 233d53a7e8..e5a5cc63da 100644
--- a/drivers/net/zxdh/zxdh_tables.c
+++ b/drivers/net/zxdh/zxdh_tables.c
@@ -349,7 +349,7 @@ zxdh_del_mac_table(struct zxdh_hw *hw, uint16_t vport, struct rte_ether_addr *ad
struct zxdh_mac_multicast_table multicast_table = {0};
union zxdh_virport_num vport_num = (union zxdh_virport_num)vport;
uint16_t vfid = zxdh_vport_to_vfid(vport_num);
- uint16_t del_flag, group_id = 0;
+ uint16_t del_flag = 0, group_id = 0;
int32_t ret = 0;
if (rte_is_unicast_ether_addr(addr)) {
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 2865 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v1 5/8] net/zxdh: free receive buffer on the async no-ack exit
2026-11-07 3:04 [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Junlong Wang
` (3 preceding siblings ...)
2026-11-07 3:04 ` [PATCH v1 4/8] net/zxdh: initialise multicast delete flag Junlong Wang
@ 2026-11-07 3:04 ` Junlong Wang
2026-11-07 3:04 ` [PATCH v1 6/8] net/zxdh: allocate a zeroed full-size payload buffer Junlong Wang
` (4 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-07 3:04 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1626 bytes --]
zxdh_bar_irq_recv() allocates a buffer for the payload of every
message it takes off the BAR channel and frees it on two of its three
exits. The third exit, taken when the header is marked asynchronous
and not an ack, returns with the buffer still allocated. Those two
header bits are written by the VF itself, so a guest can pin about
2176 bytes of the process-wide rte_malloc heap per message, with no
per-VF accounting, until the heap is full: the PF then stops
answering mailbox requests from every VF and its own table updates
fail until the process is restarted.
Route that path through the existing exit label so the buffer is
freed like on the other two paths. The return value is unchanged
(ZXDH_BAR_MSG_OK is 0) and synchronous and acked messages are
untouched.
Fixes: 3630ac8bd81f ("net/zxdh: configure interrupt")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index 6aafefe289..c7a3281987 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -1023,11 +1023,8 @@ zxdh_bar_irq_recv(uint8_t src, uint8_t dst, uint64_t virt_addr, void *dev)
goto exit;
}
zxdh_bar_chan_msg_valid_set(recv_addr, ZXDH_BAR_MSG_CHAN_USABLE);
- if (msg_header.ack == ZXDH_BAR_CHAN_MSG_ACK) {
+ if (msg_header.ack == ZXDH_BAR_CHAN_MSG_ACK)
zxdh_bar_msg_ack_async_msg_proc(&msg_header, recved_msg);
- goto exit;
- }
- return 0;
exit:
rte_free(recved_msg);
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 3012 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v1 6/8] net/zxdh: allocate a zeroed full-size payload buffer
2026-11-07 3:04 [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Junlong Wang
` (4 preceding siblings ...)
2026-11-07 3:04 ` [PATCH v1 5/8] net/zxdh: free receive buffer on the async no-ack exit Junlong Wang
@ 2026-11-07 3:04 ` Junlong Wang
2026-11-07 3:04 ` [PATCH v1 7/8] net/zxdh: check full 16-bit module id of received messages Junlong Wang
` (3 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-07 3:04 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1997 bytes --]
zxdh_bar_irq_recv() allocates the request buffer with
rte_malloc(NULL, msg_header.len, 0), where len is written by the
sender into the shared window and is only checked against the 2036
byte maximum. pf_recv_bar_msg() then casts the buffer to a full
struct zxdh_msg_info and dispatches it through zxdh_proc_cb[] without
passing the length on, so no handler can check it.
A short request therefore reads past the allocation, and a
ZXDH_FLOW_HW_GET request with len = 180 writes the 16-byte result
field at offset 180 entirely outside the 180-byte buffer, corrupting
the EAL heap. The dispatcher itself already over-reads for len < 7.
Allocate the maximum payload size, zeroed, and still copy in only
len bytes: ZXDH_BAR_MSG_PAYLOAD_MAX_LEN equals
sizeof(struct zxdh_msg_info), which the header check already
guarantees len does not exceed, so every handler access stays inside
the buffer and a short head decodes as zeroed instead of reading past
the end. The reply path already allocates the maximum for its buffer.
Fixes: 3630ac8bd81f ("net/zxdh: configure interrupt")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index c7a3281987..95b2f5a60b 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -1009,7 +1009,12 @@ zxdh_bar_irq_recv(uint8_t src, uint8_t dst, uint64_t virt_addr, void *dev)
return -1;
}
- recved_msg = rte_malloc(NULL, msg_header.len, 0);
+ /*
+ * The sender chooses the length, but the receive callbacks access the
+ * payload as a full struct zxdh_msg_info: always give them a zeroed
+ * buffer of the maximum payload size.
+ */
+ recved_msg = rte_zmalloc(NULL, ZXDH_BAR_MSG_PAYLOAD_MAX_LEN, 0);
if (recved_msg == NULL) {
PMD_MSG_LOG(ERR, "malloc temp buff failed");
return -1;
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 3734 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v1 7/8] net/zxdh: check full 16-bit module id of received messages
2026-11-07 3:04 [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Junlong Wang
` (5 preceding siblings ...)
2026-11-07 3:04 ` [PATCH v1 6/8] net/zxdh: allocate a zeroed full-size payload buffer Junlong Wang
@ 2026-11-07 3:04 ` Junlong Wang
2026-11-07 3:04 ` [PATCH v1 8/8] net/zxdh: check msg id before indexing the seqid table Junlong Wang
` (2 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-07 3:04 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 2014 bytes --]
The header check of a received BAR channel message copies the 16-bit
module_id field into a uint8_t local before comparing it with the
handler table size, and the callers then index
zxdh_msg_recv_func_tbl[] with the full untruncated value. Any
module_id whose low byte is below ZXDH_BAR_MSG_MODULE_NUM passes the
check, so a sender can select among about 25,500 slots past the
100-entry table, and a synchronous message makes the PF call whatever
word it finds there. With the default gcc build the reachable slots
inside g_seqid_ring hold small integers set at init, so one forged
message from a VF jumps to a low address and kills the PF process.
Widen the local to uint16_t and drop the (uint8_t) cast so the check
compares the same value the callers use as the index. This is the
only place a received module_id is validated; the send side already
compares the full value. Valid ids 0-99 are unaffected.
Fixes: 3630ac8bd81f ("net/zxdh: configure interrupt")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index 95b2f5a60b..77b8df2f12 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -960,7 +960,7 @@ static uint16_t
zxdh_bar_chan_msg_header_check(struct zxdh_bar_msg_header *msg_header)
{
uint16_t len = 0;
- uint8_t module_id = 0;
+ uint16_t module_id = 0;
if (msg_header->valid != ZXDH_BAR_MSG_CHAN_USED) {
PMD_MSG_LOG(ERR, "recv header ERR: valid label is not used");
@@ -968,7 +968,7 @@ zxdh_bar_chan_msg_header_check(struct zxdh_bar_msg_header *msg_header)
}
module_id = msg_header->module_id;
- if (module_id >= (uint8_t)ZXDH_BAR_MSG_MODULE_NUM) {
+ if (module_id >= ZXDH_BAR_MSG_MODULE_NUM) {
PMD_MSG_LOG(ERR, "recv header ERR: invalid module_id: %u", module_id);
return ZXDH_BAR_MSG_ERR_MODULE;
}
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 3759 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v1 8/8] net/zxdh: check msg id before indexing the seqid table
2026-11-07 3:04 [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Junlong Wang
` (6 preceding siblings ...)
2026-11-07 3:04 ` [PATCH v1 7/8] net/zxdh: check full 16-bit module id of received messages Junlong Wang
@ 2026-11-07 3:04 ` Junlong Wang
2026-11-08 16:58 ` [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Stephen Hemminger
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
9 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-07 3:04 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1735 bytes --]
zxdh_bar_msg_ack_async_msg_proc() indexes the 256-entry
g_seqid_ring.reps_info_tbl[] with the 16-bit msg_id taken straight
from the message header a VF writes into the PF/VF window. Nothing on
the receive path compares it with ZXDH_BAR_SEQID_NUM_MAX, so a forged
asynchronous ack with an out-of-range msg_id makes the PF read, and
under some conditions write, up to 1 MiB past the table, crashing the
process that drives the PF.
Reject msg_id >= ZXDH_BAR_SEQID_NUM_MAX before the slot pointer is
formed: the id is logged as invalid and the message dropped, like an
id that is already released. The driver only ever allocates ids below
256, so a well-formed peer is unaffected.
Apply the same comparison in zxdh_bar_chan_sync_msg_reps_get(), which
indexes the same table with the msg_id of a reply; that path is not
reachable from a VF today, the check is hardening.
Fixes: 3630ac8bd81f ("net/zxdh: configure interrupt")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index 77b8df2f12..5e4085e4fa 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -703,6 +703,11 @@ zxdh_bar_chan_sync_msg_reps_get(uint64_t subchan_addr,
zxdh_bar_chan_msg_header_get(subchan_addr, &msg_header);
msg_id = msg_header.msg_id;
+ if (msg_id >= ZXDH_BAR_SEQID_NUM_MAX) {
+ PMD_MSG_LOG(ERR, "msg_id %u is invalid", msg_id);
+ return ZXDH_BAR_MSG_ERR_REPLY;
+ }
+
struct zxdh_seqid_item *reps_info = &g_seqid_ring.reps_info_tbl[msg_id];
if (reps_info->flag != ZXDH_REPS_INFO_FLAG_USED) {
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 3076 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner
2026-11-07 3:04 [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Junlong Wang
` (7 preceding siblings ...)
2026-11-07 3:04 ` [PATCH v1 8/8] net/zxdh: check msg id before indexing the seqid table Junlong Wang
@ 2026-11-08 16:58 ` Stephen Hemminger
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
9 siblings, 0 replies; 20+ messages in thread
From: Stephen Hemminger @ 2026-11-08 16:58 UTC (permalink / raw)
To: Junlong Wang; +Cc: dev
On Sat, 7 Nov 2026 11:04:40 +0800
Junlong Wang <wang.junlong1@zte.com.cn> wrote:
> v1:
> This series fixes issues in the zxdh PF/VF mailbox handling that were
> found by a code scan of the driver, reported to us by the DPDK
> community. All of them are on the PF receive path, where fields of a
> message written by a VF into the shared BAR window are trusted more
> than they should be, plus two ordinary local bugs the scan found on
> the way:
>
> - The dispatcher hands msg_head vport/pcieid to the handlers without
> comparing them with the sender, so a request can be aimed at any
> port (patch 3).
> - The header module_id is checked as 8-bit but indexes a table as
> 16-bit, and the async ack msg_id indexes the 256-entry seqid table
> without a bound check; both are out-of-bounds accesses up to a
> function call through a wild pointer (patches 7, 8).
> - The receive buffer is allocated with the sender-declared length but
> accessed as a full struct, corrupting the heap; and the async
> no-ack exit leaks the buffer, allowing a guest to exhaust the PF
> heap (patches 5, 6).
> - An uninitialised DTB entry is freed on a flow query error path,
> and an uninitialised multicast delete flag makes the PF delete the
> multicast entries of every port whenever one port leaves a group,
> with no attacker involved; the latter is a v25.11 regression
> (patches 2, 4).
> - A VLAN id above 4095 from a VF message turns into an out-of-range
> ERAM row index (patch 1).
>
> The sender check of patch 3 was verified on hardware; it stops at
> binding the request to a VF of the PF, a VF forging the identity of a
> sibling VF of the same PF is left to device-side protection that is
> planned. Each patch has been checked to apply on the current main
> branch.
Overall looks good. Running AI scan locally had a couple of things
you probably should look at
Subject: Re: [PATCH v1 0/8] net/zxdh: PF mailbox hardening fixes
Applied to main (v26.11-rc2), each commit builds with -Dwerror=true.
Fixes: tags check out against history; all are in 24.11..25.11, so
Cc: stable is correct. sizeof(struct zxdh_msg_info) ==
ZXDH_BAR_MSG_PAYLOAD_MAX_LEN (2036) confirmed with a static assert,
so the premise of 6/8 holds.
One real problem: 8/8 is missing the hunk that is its main fix.
[PATCH v1 8/8] net/zxdh: check msg id before indexing the seqid table
Error: the commit message says the check is added to
zxdh_bar_msg_ack_async_msg_proc() and that the reply-path check is
only hardening, but the diff (5 insertions, one hunk) touches only
zxdh_bar_chan_sync_msg_reps_get(). After the series the VF-reachable
path is unchanged:
static void
zxdh_bar_msg_ack_async_msg_proc(struct zxdh_bar_msg_header *msg_header,
uint8_t *receiver_buff)
{
struct zxdh_seqid_item *reps_info =
&g_seqid_ring.reps_info_tbl[msg_header->msg_id];
zxdh_bar_irq_recv() still reaches this with a VF-written msg_id for
any async ack, and the free_id path then calls
zxdh_bar_chan_msgid_free() with the same unchecked index. Add the
check before the pointer is formed:
static void
zxdh_bar_msg_ack_async_msg_proc(struct zxdh_bar_msg_header *msg_header,
uint8_t *receiver_buff)
{
struct zxdh_seqid_item *reps_info;
if (msg_header->msg_id >= ZXDH_BAR_SEQID_NUM_MAX) {
PMD_MSG_LOG(ERR, "msg_id %u is invalid", msg_header->msg_id);
return;
}
reps_info = &g_seqid_ring.reps_info_tbl[msg_header->msg_id];
Looks like the hunk was dropped when the series was rebased onto 5/8,
which rewrote the caller.
[PATCH v1 4/8] net/zxdh: initialise multicast delete flag
Info: the paragraph about what the compiler does at -O1 and above is
a claim about codegen for undefined behaviour, not something the fix
depends on. Drop it and just say del_flag is read uninitialized when
the group loop breaks early, so the entry can be deleted while other
members remain.
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH v2 0/8] net/zxdh: fix some issues reported by OSS scanner
2026-11-07 3:04 [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Junlong Wang
` (8 preceding siblings ...)
2026-11-08 16:58 ` [PATCH v1 0/8] net/zxdh: fix some issues reported by OSS scanner Stephen Hemminger
@ 2026-11-12 10:26 ` Junlong Wang
2026-11-12 10:26 ` [PATCH v2 1/8] net/zxdh: reject out-of-range VLAN ID from VF Junlong Wang
` (8 more replies)
9 siblings, 9 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-12 10:26 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang
[-- Attachment #1.1.1: Type: text/plain, Size: 3008 bytes --]
v2:
- Patch 8/8: add the missing check in zxdh_bar_msg_ack_async_msg_proc(),
the hunk described by the commit message was dropped when the v1
changes were carried into the tree, only the reply-path check in
zxdh_bar_chan_sync_msg_reps_get() was actually sent, so the
VF-reachable path was unchanged (reported by maintainer review).
- Patch 4/8: drop the paragraph claiming what the compiler does with
the uninitialised read at -O1 and above; the fix does not depend
on codegen for undefined behaviour, and just state that del_flag
is read uninitialized when the group loop breaks early.
v1:
This series fixes issues in the zxdh PF/VF mailbox handling that were
found by a code scan of the driver, reported to us by the DPDK
community. All of them are on the PF receive path, where fields of a
message written by a VF into the shared BAR window are trusted more
than they should be, plus two ordinary local bugs the scan found on
the way:
- The dispatcher hands msg_head vport/pcieid to the handlers without
comparing them with the sender, so a request can be aimed at any
port (patch 3).
- The header module_id is checked as 8-bit but indexes a table as
16-bit, and the async ack msg_id indexes the 256-entry seqid table
without a bound check; both are out-of-bounds accesses up to a
function call through a wild pointer (patches 7, 8).
- The receive buffer is allocated with the sender-declared length but
accessed as a full struct, corrupting the heap; and the async
no-ack exit leaks the buffer, allowing a guest to exhaust the PF
heap (patches 5, 6).
- An uninitialised DTB entry is freed on a flow query error path,
and an uninitialised multicast delete flag makes the PF delete the
multicast entries of every port whenever one port leaves a group,
with no attacker involved; the latter is a v25.11 regression
(patches 2, 4).
- A VLAN id above 4095 from a VF message turns into an out-of-range
ERAM row index (patch 1).
The sender check of patch 3 was verified on hardware; it stops at
binding the request to a VF of the PF, a VF forging the identity of a
sibling VF of the same PF is left to device-side protection that is
planned. Each patch has been checked to apply on the current main
branch.
Junlong Wang (8):
net/zxdh: reject out-of-range VLAN ID from VF
net/zxdh: initialise DTB entry in flow query
net/zxdh: refuse PF mailbox requests not naming a VF of this PF
net/zxdh: initialise multicast delete flag
net/zxdh: free receive buffer on the async no-ack exit
net/zxdh: allocate a zeroed full-size payload buffer
net/zxdh: check full 16-bit module id of recv msg
net/zxdh: check msg id before indexing the seqid table
drivers/net/zxdh/zxdh_flow.c | 2 +-
drivers/net/zxdh/zxdh_msg.c | 61 ++++++++++++++++++++++++++++------
drivers/net/zxdh/zxdh_tables.c | 2 +-
3 files changed, 53 insertions(+), 12 deletions(-)
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 5916 bytes --]
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH v2 1/8] net/zxdh: reject out-of-range VLAN ID from VF
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
@ 2026-11-12 10:26 ` Junlong Wang
2026-11-12 10:26 ` [PATCH v2 2/8] net/zxdh: initialise DTB entry in flow query Junlong Wang
` (7 subsequent siblings)
8 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-12 10:26 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1548 bytes --]
The PF handler for a VF's ZXDH_VLAN_FILTER_ADD/DEL message passes the
16-bit vlan_id from the message body to zxdh_vlan_filter_table_set()
without a range check. An id above 4095 becomes a row index outside
the VLAN filter groups the PF sets up for the function, and the NP
layer below only checks it against the end of the whole ERAM, so one
request from a guest reads, modifies and writes back unrelated table
rows.
Reject ids above RTE_ETHER_MAX_VLAN_ID where the value is taken out of
the message. Valid ids behave as before.
Fixes: b4f996b7fcf6 ("net/zxdh: optimize VLAN filter and offload")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index 4b01daf37a..f0f0522817 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -1552,7 +1552,12 @@ zxdh_vf_vlan_filter_table_process(struct zxdh_hw *hw, uint16_t vport,
void *reply_data_addr = ZXDH_ADDR_OF(msg_reply_body, res_info, reply_data);
int ret = 0;
- ret = zxdh_vlan_filter_table_set(hw, vport, vlan_id, enable);
+ if (vlan_id > RTE_ETHER_MAX_VLAN_ID) {
+ PMD_DRV_LOG(ERR, "vlan id %d is invalid", vlan_id);
+ ret = -EINVAL;
+ } else {
+ ret = zxdh_vlan_filter_table_set(hw, vport, vlan_id, enable);
+ }
if (ret)
sprintf(str, "vlan filter op-code[%d] vlan id:%d failed, code:%d\n",
enable, vlan_id, ret);
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 3001 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v2 2/8] net/zxdh: initialise DTB entry in flow query
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
2026-11-12 10:26 ` [PATCH v2 1/8] net/zxdh: reject out-of-range VLAN ID from VF Junlong Wang
@ 2026-11-12 10:26 ` Junlong Wang
2026-11-12 10:26 ` [PATCH v2 3/8] net/zxdh: refuse PF mailbox requests not naming a VF of this PF Junlong Wang
` (6 subsequent siblings)
8 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-12 10:26 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1638 bytes --]
zxdh_hw_flow_query() declares its ZXDH_DTB_USER_ENTRY_T without an
initialiser, unlike the sibling declarations in
zxdh_hw_flow_insert() and zxdh_hw_flow_del() which use = {0}. When
the flow index is ZXDH_MAX_FLOW_NUM or more,
zxdh_fd_flow_to_dtbentry() returns before writing the entry, and the
free_res path then calls rte_free() on the uninitialised
p_entry_data pointer. The index comes from a VF's ZXDH_FLOW_HW_GET
mailbox message, and pf_fd_hw_query_count() only checks it after the
query, so a single request from a guest frees garbage and kills the
process driving the PF in a release build.
Initialise the entry the way the siblings do: both error paths
(out-of-range index and rte_zmalloc() failure) then end in
rte_free(NULL), a no-op, and the VF gets the existing error reply.
Valid indices behave as before.
Fixes: d0af48c1b952 ("net/zxdh: support flow API")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_flow.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_flow.c b/drivers/net/zxdh/zxdh_flow.c
index 56ac34c6e3..a51ce9ef77 100644
--- a/drivers/net/zxdh/zxdh_flow.c
+++ b/drivers/net/zxdh/zxdh_flow.c
@@ -1137,7 +1137,7 @@ zxdh_hw_flow_query(struct rte_eth_dev *dev, struct zxdh_flow *dh_flow,
struct zxdh_hw *hw = dev->data->dev_private;
int ret = 0;
struct zxdh_flow_info *flow = &dh_flow->flowentry;
- ZXDH_DTB_USER_ENTRY_T dtb_entry;
+ ZXDH_DTB_USER_ENTRY_T dtb_entry = {0};
uint16_t handle_idx;
handle_idx = zxdh_fd_flow_to_dtbentry(hw, flow, &dtb_entry);
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 2832 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v2 3/8] net/zxdh: refuse PF mailbox requests not naming a VF of this PF
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
2026-11-12 10:26 ` [PATCH v2 1/8] net/zxdh: reject out-of-range VLAN ID from VF Junlong Wang
2026-11-12 10:26 ` [PATCH v2 2/8] net/zxdh: initialise DTB entry in flow query Junlong Wang
@ 2026-11-12 10:26 ` Junlong Wang
2026-11-12 10:26 ` [PATCH v2 4/8] net/zxdh: initialise multicast delete flag Junlong Wang
` (5 subsequent siblings)
8 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-12 10:26 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 3055 bytes --]
The PF dispatcher zxdh_config_process_callback() hands the
msghead->vport and msghead->pcieid of a request, both written by the
VF into its BAR0 mailbox window, straight to the handlers, and
nothing on the receive path compares them with the sender. One
message can therefore be aimed at any port: it rewrites or deletes
the port attribute entry of the PF itself or of another VF.
Add zxdh_msg_head_is_own_vf() before the dispatch: pcieid must be a
VF pcieid whose PF is this PF, and vport must carry vf_flag with this
PF's epid and pfid. The vport vfid is deliberately not compared with
the pcieid function number: vfid is allocated sequentially across all
PFs of an epid while the function number restarts at 0 for each PF.
Anything else is answered ZXDH_REPS_INVALID and no handler runs;
messages built by zxdh_msg_head_build() pass unchanged.
A VF that forges both pcieid and vport of a sibling VF of the same PF
still cannot be told apart by the driver; protection against that
case is planned on the device side. This patch closes the PF-port and
cross-PF cases.
Fixes: 2e22ff2528db ("net/zxdh: provide PF/VF message interrupt callback")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index f0f0522817..6aafefe289 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -2397,6 +2397,24 @@ static const zxdh_msg_process_callback zxdh_proc_cb[] = {
[ZXDH_FLOW_HW_FLUSH] = zxdh_vf_flow_hw_flush,
};
+/*
+ * The msg head is written by the VF driver: the vport and pcieid it carries
+ * must name one VF of this PF, not the PF itself or a port of another PF.
+ */
+static bool
+zxdh_msg_head_is_own_vf(struct zxdh_hw *hw, struct zxdh_msg_head *msghead)
+{
+ union zxdh_virport_num vport = {.vport = msghead->vport};
+
+ if ((msghead->pcieid & ZXDH_PCIEID_IS_PF_MASK) ||
+ ZXDH_PF_PCIE_ID(msghead->pcieid) != hw->pcie_id)
+ return false;
+
+ return vport.vf_flag &&
+ vport.epid == hw->vport.epid &&
+ vport.pfid == hw->vport.pfid;
+}
+
static inline int
zxdh_config_process_callback(struct zxdh_hw *hw, struct zxdh_msg_info *msg_info,
void *res, uint16_t *res_len)
@@ -2408,7 +2426,12 @@ zxdh_config_process_callback(struct zxdh_hw *hw, struct zxdh_msg_info *msg_info,
PMD_DRV_LOG(ERR, " invalid param");
return -1;
}
- if (zxdh_proc_cb[msghead->msg_type]) {
+
+ if (!zxdh_msg_head_is_own_vf(hw, msghead)) {
+ PMD_DRV_LOG(ERR, "msg_type %d vport 0x%x pcieid 0x%x is not a VF of PF pcieid 0x%x",
+ msghead->msg_type, msghead->vport, msghead->pcieid, hw->pcie_id);
+ ZXDH_SET(msg_reply_body, res, flag, ZXDH_REPS_INVALID);
+ } else if (zxdh_proc_cb[msghead->msg_type]) {
ret = zxdh_proc_cb[msghead->msg_type](hw, msghead->vport, msghead->pcieid,
(void *)&msg_info->data, res, res_len);
if (!ret)
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 6027 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v2 4/8] net/zxdh: initialise multicast delete flag
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
` (2 preceding siblings ...)
2026-11-12 10:26 ` [PATCH v2 3/8] net/zxdh: refuse PF mailbox requests not naming a VF of this PF Junlong Wang
@ 2026-11-12 10:26 ` Junlong Wang
2026-11-12 10:26 ` [PATCH v2 5/8] net/zxdh: free receive buffer on the async no-ack exit Junlong Wang
` (4 subsequent siblings)
8 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-12 10:26 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1547 bytes --]
zxdh_del_mac_table() decides whether a multicast address still has
subscribers with del_flag, but the flag has no initialiser and is
only ever assigned 1. The loop that walks the group entries breaks
out as soon as it finds one that still has members, leaving del_flag
read uninitialized, so the entry can be deleted while other members
remain.
Whenever one port therefore leaves a shared multicast address, the
entries holding the PF's and every other VF's membership are deleted
too: the PF and sibling VFs silently lose their multicast reception,
with no attacker involved.
Restore the missing initialiser, so the entry is deleted only when
the last member leaves.
Fixes: b64747ef2951 ("net/zxdh: fix MAC configuration failure")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_tables.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_tables.c b/drivers/net/zxdh/zxdh_tables.c
index 233d53a7e8..e5a5cc63da 100644
--- a/drivers/net/zxdh/zxdh_tables.c
+++ b/drivers/net/zxdh/zxdh_tables.c
@@ -349,7 +349,7 @@ zxdh_del_mac_table(struct zxdh_hw *hw, uint16_t vport, struct rte_ether_addr *ad
struct zxdh_mac_multicast_table multicast_table = {0};
union zxdh_virport_num vport_num = (union zxdh_virport_num)vport;
uint16_t vfid = zxdh_vport_to_vfid(vport_num);
- uint16_t del_flag, group_id = 0;
+ uint16_t del_flag = 0, group_id = 0;
int32_t ret = 0;
if (rte_is_unicast_ether_addr(addr)) {
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 2736 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v2 5/8] net/zxdh: free receive buffer on the async no-ack exit
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
` (3 preceding siblings ...)
2026-11-12 10:26 ` [PATCH v2 4/8] net/zxdh: initialise multicast delete flag Junlong Wang
@ 2026-11-12 10:26 ` Junlong Wang
2026-11-12 10:26 ` [PATCH v2 6/8] net/zxdh: allocate a zeroed full-size payload buffer Junlong Wang
` (3 subsequent siblings)
8 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-12 10:26 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1626 bytes --]
zxdh_bar_irq_recv() allocates a buffer for the payload of every
message it takes off the BAR channel and frees it on two of its three
exits. The third exit, taken when the header is marked asynchronous
and not an ack, returns with the buffer still allocated. Those two
header bits are written by the VF itself, so a guest can pin about
2176 bytes of the process-wide rte_malloc heap per message, with no
per-VF accounting, until the heap is full: the PF then stops
answering mailbox requests from every VF and its own table updates
fail until the process is restarted.
Route that path through the existing exit label so the buffer is
freed like on the other two paths. The return value is unchanged
(ZXDH_BAR_MSG_OK is 0) and synchronous and acked messages are
untouched.
Fixes: 3630ac8bd81f ("net/zxdh: configure interrupt")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index 6aafefe289..c7a3281987 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -1023,11 +1023,8 @@ zxdh_bar_irq_recv(uint8_t src, uint8_t dst, uint64_t virt_addr, void *dev)
goto exit;
}
zxdh_bar_chan_msg_valid_set(recv_addr, ZXDH_BAR_MSG_CHAN_USABLE);
- if (msg_header.ack == ZXDH_BAR_CHAN_MSG_ACK) {
+ if (msg_header.ack == ZXDH_BAR_CHAN_MSG_ACK)
zxdh_bar_msg_ack_async_msg_proc(&msg_header, recved_msg);
- goto exit;
- }
- return 0;
exit:
rte_free(recved_msg);
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 3012 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v2 6/8] net/zxdh: allocate a zeroed full-size payload buffer
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
` (4 preceding siblings ...)
2026-11-12 10:26 ` [PATCH v2 5/8] net/zxdh: free receive buffer on the async no-ack exit Junlong Wang
@ 2026-11-12 10:26 ` Junlong Wang
2026-11-12 10:26 ` [PATCH v2 7/8] net/zxdh: check full 16-bit module id of recv msg Junlong Wang
` (2 subsequent siblings)
8 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-12 10:26 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 1997 bytes --]
zxdh_bar_irq_recv() allocates the request buffer with
rte_malloc(NULL, msg_header.len, 0), where len is written by the
sender into the shared window and is only checked against the 2036
byte maximum. pf_recv_bar_msg() then casts the buffer to a full
struct zxdh_msg_info and dispatches it through zxdh_proc_cb[] without
passing the length on, so no handler can check it.
A short request therefore reads past the allocation, and a
ZXDH_FLOW_HW_GET request with len = 180 writes the 16-byte result
field at offset 180 entirely outside the 180-byte buffer, corrupting
the EAL heap. The dispatcher itself already over-reads for len < 7.
Allocate the maximum payload size, zeroed, and still copy in only
len bytes: ZXDH_BAR_MSG_PAYLOAD_MAX_LEN equals
sizeof(struct zxdh_msg_info), which the header check already
guarantees len does not exceed, so every handler access stays inside
the buffer and a short head decodes as zeroed instead of reading past
the end. The reply path already allocates the maximum for its buffer.
Fixes: 3630ac8bd81f ("net/zxdh: configure interrupt")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index c7a3281987..95b2f5a60b 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -1009,7 +1009,12 @@ zxdh_bar_irq_recv(uint8_t src, uint8_t dst, uint64_t virt_addr, void *dev)
return -1;
}
- recved_msg = rte_malloc(NULL, msg_header.len, 0);
+ /*
+ * The sender chooses the length, but the receive callbacks access the
+ * payload as a full struct zxdh_msg_info: always give them a zeroed
+ * buffer of the maximum payload size.
+ */
+ recved_msg = rte_zmalloc(NULL, ZXDH_BAR_MSG_PAYLOAD_MAX_LEN, 0);
if (recved_msg == NULL) {
PMD_MSG_LOG(ERR, "malloc temp buff failed");
return -1;
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 3734 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v2 7/8] net/zxdh: check full 16-bit module id of recv msg
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
` (5 preceding siblings ...)
2026-11-12 10:26 ` [PATCH v2 6/8] net/zxdh: allocate a zeroed full-size payload buffer Junlong Wang
@ 2026-11-12 10:26 ` Junlong Wang
2026-11-12 10:26 ` [PATCH v2 8/8] net/zxdh: check msg id before indexing the seqid table Junlong Wang
2026-11-12 23:32 ` [PATCH v2 0/8] net/zxdh: fix some issues reported by OSS scanner Stephen Hemminger
8 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-12 10:26 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 2014 bytes --]
The header check of a received BAR channel message copies the 16-bit
module_id field into a uint8_t local before comparing it with the
handler table size, and the callers then index
zxdh_msg_recv_func_tbl[] with the full untruncated value. Any
module_id whose low byte is below ZXDH_BAR_MSG_MODULE_NUM passes the
check, so a sender can select among about 25,500 slots past the
100-entry table, and a synchronous message makes the PF call whatever
word it finds there. With the default gcc build the reachable slots
inside g_seqid_ring hold small integers set at init, so one forged
message from a VF jumps to a low address and kills the PF process.
Widen the local to uint16_t and drop the (uint8_t) cast so the check
compares the same value the callers use as the index. This is the
only place a received module_id is validated; the send side already
compares the full value. Valid ids 0-99 are unaffected.
Fixes: 3630ac8bd81f ("net/zxdh: configure interrupt")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index 95b2f5a60b..77b8df2f12 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -960,7 +960,7 @@ static uint16_t
zxdh_bar_chan_msg_header_check(struct zxdh_bar_msg_header *msg_header)
{
uint16_t len = 0;
- uint8_t module_id = 0;
+ uint16_t module_id = 0;
if (msg_header->valid != ZXDH_BAR_MSG_CHAN_USED) {
PMD_MSG_LOG(ERR, "recv header ERR: valid label is not used");
@@ -968,7 +968,7 @@ zxdh_bar_chan_msg_header_check(struct zxdh_bar_msg_header *msg_header)
}
module_id = msg_header->module_id;
- if (module_id >= (uint8_t)ZXDH_BAR_MSG_MODULE_NUM) {
+ if (module_id >= ZXDH_BAR_MSG_MODULE_NUM) {
PMD_MSG_LOG(ERR, "recv header ERR: invalid module_id: %u", module_id);
return ZXDH_BAR_MSG_ERR_MODULE;
}
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 3759 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* [PATCH v2 8/8] net/zxdh: check msg id before indexing the seqid table
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
` (6 preceding siblings ...)
2026-11-12 10:26 ` [PATCH v2 7/8] net/zxdh: check full 16-bit module id of recv msg Junlong Wang
@ 2026-11-12 10:26 ` Junlong Wang
2026-11-12 23:32 ` [PATCH v2 0/8] net/zxdh: fix some issues reported by OSS scanner Stephen Hemminger
8 siblings, 0 replies; 20+ messages in thread
From: Junlong Wang @ 2026-11-12 10:26 UTC (permalink / raw)
To: stephen; +Cc: dev, Junlong Wang, stable
[-- Attachment #1.1.1: Type: text/plain, Size: 2357 bytes --]
zxdh_bar_msg_ack_async_msg_proc() indexes the 256-entry
g_seqid_ring.reps_info_tbl[] with the 16-bit msg_id taken straight
from the message header a VF writes into the PF/VF window. Nothing on
the receive path compares it with ZXDH_BAR_SEQID_NUM_MAX, so a forged
asynchronous ack with an out-of-range msg_id makes the PF read, and
under some conditions write, up to 1 MiB past the table, crashing the
process that drives the PF.
Reject msg_id >= ZXDH_BAR_SEQID_NUM_MAX before the slot pointer is
formed: the id is logged as invalid and the message dropped, like an
id that is already released. The driver only ever allocates ids below
256, so a well-formed peer is unaffected.
Apply the same comparison in zxdh_bar_chan_sync_msg_reps_get(), which
indexes the same table with the msg_id of a reply; that path is not
reachable from a VF today, the check is hardening.
Fixes: 3630ac8bd81f ("net/zxdh: configure interrupt")
Cc: stable@dpdk.org
Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn>
---
drivers/net/zxdh/zxdh_msg.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/net/zxdh/zxdh_msg.c b/drivers/net/zxdh/zxdh_msg.c
index 77b8df2f12..4d79d50ab6 100644
--- a/drivers/net/zxdh/zxdh_msg.c
+++ b/drivers/net/zxdh/zxdh_msg.c
@@ -703,6 +703,11 @@ zxdh_bar_chan_sync_msg_reps_get(uint64_t subchan_addr,
zxdh_bar_chan_msg_header_get(subchan_addr, &msg_header);
msg_id = msg_header.msg_id;
+ if (msg_id >= ZXDH_BAR_SEQID_NUM_MAX) {
+ PMD_MSG_LOG(ERR, "msg_id %u is invalid", msg_id);
+ return ZXDH_BAR_MSG_ERR_REPLY;
+ }
+
struct zxdh_seqid_item *reps_info = &g_seqid_ring.reps_info_tbl[msg_id];
if (reps_info->flag != ZXDH_REPS_INFO_FLAG_USED) {
@@ -886,7 +891,13 @@ static void
zxdh_bar_msg_ack_async_msg_proc(struct zxdh_bar_msg_header *msg_header,
uint8_t *receiver_buff)
{
- struct zxdh_seqid_item *reps_info = &g_seqid_ring.reps_info_tbl[msg_header->msg_id];
+ struct zxdh_seqid_item *reps_info;
+
+ if (msg_header->msg_id >= ZXDH_BAR_SEQID_NUM_MAX) {
+ PMD_MSG_LOG(ERR, "msg_id: %u is invalid", msg_header->msg_id);
+ return;
+ }
+ reps_info = &g_seqid_ring.reps_info_tbl[msg_header->msg_id];
if (reps_info->flag != ZXDH_REPS_INFO_FLAG_USED) {
PMD_MSG_LOG(ERR, "msg_id: %u is released", msg_header->msg_id);
--
2.27.0
[-- Attachment #1.1.2: Type: text/html , Size: 4340 bytes --]
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [PATCH v2 0/8] net/zxdh: fix some issues reported by OSS scanner
2026-11-12 10:26 ` [PATCH v2 " Junlong Wang
` (7 preceding siblings ...)
2026-11-12 10:26 ` [PATCH v2 8/8] net/zxdh: check msg id before indexing the seqid table Junlong Wang
@ 2026-11-12 23:32 ` Stephen Hemminger
8 siblings, 0 replies; 20+ messages in thread
From: Stephen Hemminger @ 2026-11-12 23:32 UTC (permalink / raw)
To: Junlong Wang; +Cc: dev
On Thu, 12 Nov 2026 18:26:42 +0800
Junlong Wang <wang.junlong1@zte.com.cn> wrote:
> v2:
> - Patch 8/8: add the missing check in zxdh_bar_msg_ack_async_msg_proc(),
> the hunk described by the commit message was dropped when the v1
> changes were carried into the tree, only the reply-path check in
> zxdh_bar_chan_sync_msg_reps_get() was actually sent, so the
> VF-reachable path was unchanged (reported by maintainer review).
> - Patch 4/8: drop the paragraph claiming what the compiler does with
> the uninitialised read at -O1 and above; the fix does not depend
> on codegen for undefined behaviour, and just state that del_flag
> is read uninitialized when the group loop breaks early.
Looks good but still a couple of AI review items.
Subject: Re: [PATCH v2 0/8] net/zxdh: PF mailbox hardening
Series applies cleanly to main (26.11-rc3) and every commit builds
with -Dwerror=true. Fixes: hashes resolve and all are in released
versions, so Cc: stable is correct.
Patches 1, 2, 4, 5, 6, 7 and 8 look correct. One problem in patch 3.
[PATCH v2 3/8] net/zxdh: refuse PF mailbox requests not naming a VF
of this PF
Warning: the pfid comparison does not keep a VF on its own PF, so the
"closes the cross-PF case" claim in the commit message is wrong.
Every vport-indexed handler maps the VF vport to a table index with
zxdh_vport_to_vfid(), which for a VF is:
return v.epid * 256 + v.vfid;
pfid is not part of the index. As the commit message itself says,
vfid is allocated sequentially across all PFs of an epid. A VF can
therefore send its own, correct pcieid together with
vport = { .vf_flag = 1, .epid = <own>, .pfid = <own PF>,
.vfid = <vfid of a VF under another PF> }
zxdh_msg_head_is_own_vf() accepts it, and the handler rewrites the
port attr, MAC, VLAN and flow entries of a VF that belongs to a
different PF on the same epid.
What the check does close: requests naming a PF port (vf_flag == 0),
and requests naming any port on another epid. The residual is any VF
on the same epid, not just siblings under the same PF.
The driver has no trusted record of which vfids belong to this PF
(vfinfo[].vport is filled from the VF's own ZXDH_VF_PORT_INIT
message, so it cannot be used as the reference). Unless the PF can
obtain its vfid range from firmware, fix the commit message to
match what the code does:
A VF that forges the vport of another VF on the same epid still
cannot be told apart by the driver, since zxdh_vport_to_vfid()
does not use pfid for VF ports; protection against that case is
planned on the device side. This patch closes the PF-port and
cross-epid cases.
If firmware can report the vfid range of the PF, check
vport.vfid against that range here instead.
[PATCH v2 6/8] net/zxdh: allocate a zeroed full-size payload buffer
Info: the fix is correct only because ZXDH_BAR_MSG_PAYLOAD_MAX_LEN
equals sizeof(struct zxdh_msg_info) (both 2036, checked by compile).
Nothing enforces that. Suggest pinning it in zxdh_msg.h after the
struct definition:
static_assert(sizeof(struct zxdh_msg_info) <=
ZXDH_BAR_MSG_PAYLOAD_MAX_LEN,
"receive buffer smaller than struct zxdh_msg_info");
^ permalink raw reply [flat|nested] 20+ messages in thread