TOCTOU race leading to MAC address spoofing / unauthorized VF MAC configuration
Description
The commit fixes a real TOCTOU race and MAC address validation flaw in the ENETC PF/ VF mailbox path. Previously, enetc_msg_pf_set_vf_primary_mac_addr() read the VF-provided MAC directly from the DMA buffer, validated it, and then re-read the same DMA buffer to program hardware. Between validation and programming, a malicious VF could overwrite the DMA buffer, bypassing validation and causing the PF to program an arbitrary MAC (multicast, broadcast, or all-zero), enabling MAC spoofing. The patch introduces two mitigations:
- It snapshots the message content into a private kernel buffer (via kzalloc and memcpy) before processing, eliminating the TOCTOU window.
- It validates the MAC address with is_valid_ether_addr() on the snapshot and rejects invalid addresses, preventing spoofing vectors like all-zero or multicast MACs.
These changes apply to the ENETC PF MAC-set path and address the specific TOCTOU race reported by Sashiko. The fix thus hardens VF-to-PF MAC configuration against spoofing and race conditions.
Proof of Concept
Note: This PoC is conceptual and not a runnable exploit. It demonstrates how the TOCTOU race could be exploited on systems lacking the snapshot and validation fixes.
Conceptual PoC (TOCTOU race demonstration in the old code path):
Prerequisites:
- A system with ENETC PF driver and a configured VF capable of sending a mailbox/MAC set command.
- Access to the VF-PF mailbox memory (DMA buffer) shared between VF and PF.
Steps:
1) Craft a VF mailbox message to set the VF's primary MAC to a valid address, e.g., 00:11:22:33:44:55, and place it in the DMA buffer referenced by the PF (cmd->mac.sa_data).
2) In parallel, race a second thread that continuously overwrites the same DMA buffer's MAC field with a hostile value (e.g., 01:02:03:04:05:06 or 00:00:00:00:00:00) just after the PF validation check but before hardware programming.
3) If the timing aligns such that the PF validates the MAC from the DMA buffer and then uses the same buffer to program the hardware before the snapshot was taken (i.e., before the patch), the hardware could be programmed with the overwritten MAC value, effectively spoofing the MAC.
Mitigation (as implemented by the patch):
- enetc_msg_handle_rxmsg() allocates a local snapshot buffer and copies the entire message (via memcpy) before any processing.
- enetc_msg_pf_set_vf_primary_mac_addr() validates the MAC against is_valid_ether_addr() on the snapshot and then uses that validated, stable data to program hardware.
- This prevents the TOCTOU window and ensures only valid, non-spoofed MACs are programmed.
Outcome:
- With the patch applied, the race is eliminated and MAC programming is performed on a stable, validated snapshot, preventing VF MAC spoofing via DMA-buffer manipulation.
Commit Details
Author: Wei Fang
Date: 2026-05-20 06:44 UTC
Message:
net: enetc: fix TOCTOU race and validate VF MAC address
Sashiko reported that the PF driver accepts arbitrary MAC address from
from VF mailbox messages without proper validation, creating a security
vulnerability [1].
In enetc_msg_pf_set_vf_primary_mac_addr(), the MAC address is extracted
directly from the message buffer (cmd->mac.sa_data) and programmed into
hardware via pf->ops->set_si_primary_mac() without any validity checks.
A malicious VF can configure a multicast, broadcast, or all-zero MAC
address. Therefore, a validation to check the MAC address provided by VF
is required.
However, simply checking the MAC address is not enough, because it also
has the potential TOCTOU race [2]: The code reads the MAC address from
the DMA buffer to validate it via is_valid_ether_addr(), if validation
passes, reads the same DMA buffer a second time when calling
enetc_pf_set_primary_mac_addr() to program the hardware. A malicious VF
can exploit this window by overwriting the MAC address in the DMA buffer
between the validation check and the hardware programming, bypassing the
validation entirely.
Therefore, allocate a local buffer in enetc_msg_handle_rxmsg() and copy
the message content from the DMA buffer via memcpy() before processing.
This ensures the PF operates on a stable snapshot that the VF cannot
modify.
Link: https://sashiko.dev/#/patchset/20260511080805.2052495-1-wei.fang%40nxp.com #1
Link: https://sashiko.dev/#/patchset/20260513103021.2190593-1-wei.fang%40nxp.com #2
Fixes: beb74ac878c8 ("enetc: Add vf to pf messaging support")
Signed-off-by: Wei Fang <wei.fang@nxp.com>
Reviewed-by: Harshitha Ramamurthy <hramamurthy@google.com>
Link: https://patch.msgid.link/20260520064421.91569-5-wei.fang@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Triage Assessment
Vulnerability Type: MAC address spoofing / unauthorized VF MAC configuration (TOCTOU race)
Confidence: HIGH
Reasoning:
The patch validates the MAC address provided by a VF and mitigates a TOCTOU race by taking a stable snapshot of the message before processing. This directly prevents a malicious VF from configuring a multicast/broadcast/all-zero MAC or exploiting a window where the MAC could be changed between validation and programming the hardware.
Verification Assessment
Vulnerability Type: TOCTOU race leading to MAC address spoofing / unauthorized VF MAC configuration
Confidence: HIGH
Affected Versions: v7.0-rc6 and earlier (enetc PF driver; prior to this patch)
Code Diff
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index dea3a92c472228..09c64204089235 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -478,21 +478,24 @@ static void enetc_configure_port(struct enetc_pf *pf)
/* Messaging */
static u16 enetc_msg_pf_set_vf_primary_mac_addr(struct enetc_pf *pf,
- int vf_id)
+ int vf_id, void *msg)
{
struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
- struct enetc_msg_swbd *msg = &pf->rxmsg[vf_id];
- struct enetc_msg_cmd_set_primary_mac *cmd;
+ struct enetc_msg_cmd_set_primary_mac *cmd = msg;
struct device *dev = &pf->si->pdev->dev;
- u16 cmd_id;
+ u16 cmd_id = cmd->header.id;
char *addr;
- cmd = (struct enetc_msg_cmd_set_primary_mac *)msg->vaddr;
- cmd_id = cmd->header.id;
if (cmd_id != ENETC_MSG_CMD_MNG_ADD)
return ENETC_MSG_CMD_STATUS_FAIL;
addr = cmd->mac.sa_data;
+ if (!is_valid_ether_addr(addr)) {
+ dev_err_ratelimited(dev, "VF%d attempted to set invalid MAC\n",
+ vf_id);
+ return ENETC_MSG_CMD_STATUS_FAIL;
+ }
+
if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC) {
dev_err_ratelimited(dev,
"VF%d attempted to override PF set MAC\n",
@@ -507,17 +510,33 @@ static u16 enetc_msg_pf_set_vf_primary_mac_addr(struct enetc_pf *pf,
void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id, u16 *status)
{
- struct enetc_msg_swbd *msg = &pf->rxmsg[vf_id];
+ struct enetc_msg_swbd *msg_swbd = &pf->rxmsg[vf_id];
struct device *dev = &pf->si->pdev->dev;
struct enetc_msg_cmd_header *cmd_hdr;
u16 cmd_type;
+ u8 *msg;
- cmd_hdr = (struct enetc_msg_cmd_header *)msg->vaddr;
+ msg = kzalloc_objs(*msg, msg_swbd->size);
+ if (!msg) {
+ dev_err_ratelimited(dev,
+ "Failed to allocate message buffer\n");
+ *status = ENETC_MSG_CMD_STATUS_FAIL;
+ return;
+ }
+
+ /* Currently, only ENETC_MSG_CMD_MNG_MAC command is supported, so
+ * only sizeof(struct enetc_msg_cmd_set_primary_mac) bytes need to
+ * be copied. This data already includes the cmd_type field, so it
+ * can correctly return an error code.
+ */
+ memcpy(msg, msg_swbd->vaddr,
+ sizeof(struct enetc_msg_cmd_set_primary_mac));
+ cmd_hdr = (struct enetc_msg_cmd_header *)msg;
cmd_type = cmd_hdr->type;
switch (cmd_type) {
case ENETC_MSG_CMD_MNG_MAC:
- *status = enetc_msg_pf_set_vf_primary_mac_addr(pf, vf_id);
+ *status = enetc_msg_pf_set_vf_primary_mac_addr(pf, vf_id, msg);
break;
default:
*status = ENETC_MSG_CMD_STATUS_FAIL;
@@ -525,6 +544,8 @@ void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id, u16 *status)
"command not supported (cmd_type: 0x%x)\n",
cmd_type);
}
+
+ kfree(msg);
}
#ifdef CONFIG_PCI_IOV