TOCTOU race leading to MAC address spoofing / unauthorized VF MAC configuration

HIGH
torvalds/linux
Commit: c666fa632fe6
Affected: v7.0-rc6 and earlier (enetc PF driver; prior to this patch)
2026-05-25 23:44 UTC

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
← Back to Alerts View on GitHub →