mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 02:24:24 +02:00
Merge branch 'net-enetc-sr-iov-robustness-and-security-fixes'
Wei Fang says:
====================
net: enetc: SR-IOV robustness and security fixes
This patch series addresses a number of robustness, security, and
correctness issues in the ENETC driver's SR-IOV subsystem, focusing
primarily on the VF-to-PF mailbox communication path.
The series can be grouped into the following categories:
1. DoS and security fixes:
- Prevent an unbounded loop DoS in the VF-to-PF message handler,
which could be triggered by a malicious or misbehaving VF.
- Fix a TOCTOU (Time-of-Check-Time-of-Use) race and add proper
validation of VF MAC addresses to prevent spoofing or invalid
configuration from being applied.
2. Race condition fixes:
- Fix a race condition in VF MAC address configuration that could
lead to inconsistent state between the VF request and PF
application.
- Fix a race condition during SR-IOV teardown that could cause
VF->PF mailbox operations to time out, resulting in unnecessary
errors during shutdown.
3. Memory safety fixes:
- Fix a DMA write to freed memory in enetc_msg_free_mbx(), which
could cause silent memory corruption or system instability.
4. Error handling and initialization fixes:
- Fix missing error code propagation when pf->vf_state allocation
fails, ensuring callers receive a proper errno instead of
succeeding silently.
- Fix incorrect mailbox message status values returned to VFs,
which could cause VFs to misinterpret PF responses.
- Fix initialization order to prevent the use of uninitialized
resources during driver probe, which could cause undefined
behavior on certain configurations.
5. Diagnostics improvement:
- Add rate limiting to VF mailbox error messages to prevent log
flooding in the presence of a misbehaving VF.
These fixes improve the overall stability and security of the ENETC
SR-IOV implementation, particularly in multi-tenant environments where
VFs may be assigned to untrusted guests.
====================
Link: https://patch.msgid.link/20260520064421.91569-1-wei.fang@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
c33f944a33
|
|
@ -56,11 +56,21 @@ static inline u32 enetc_vsi_set_msize(u32 size)
|
|||
}
|
||||
|
||||
#define ENETC_PSIMSGRR 0x204
|
||||
#define ENETC_PSIMSGRR_MR_MASK GENMASK(2, 1)
|
||||
#define ENETC_PSIMSGRR_MR(n) BIT((n) + 1) /* n = VSI index */
|
||||
#define ENETC_PSIVMSGRCVAR0(n) (0x210 + (n) * 0x8) /* n = VSI index */
|
||||
#define ENETC_PSIVMSGRCVAR1(n) (0x214 + (n) * 0x8)
|
||||
|
||||
/* Message received mask, n is the active number of VSIs.
|
||||
* It is available for ENETC_PSIMSGRR, ENETC_PSIIER, and
|
||||
* ENETC_PSIIDR registers.
|
||||
*/
|
||||
#define ENETC_PSIMR_MASK(n) \
|
||||
({ typeof(n) _n = (n); (_n) ? GENMASK((_n), 1) : 0; })
|
||||
|
||||
/* Message received bit, n is VSI index. It is available for
|
||||
* ENETC_PSIMSGRR, ENETC_PSIIER, and ENETC_PSIIDR registers.
|
||||
*/
|
||||
#define ENETC_PSIMR_BIT(n) BIT((n) + 1)
|
||||
|
||||
#define ENETC_VSIMSGSR 0x204 /* RO */
|
||||
#define ENETC_VSIMSGSR_MB BIT(0)
|
||||
#define ENETC_VSIMSGSR_MS BIT(1)
|
||||
|
|
@ -94,7 +104,6 @@ static inline u32 enetc_vsi_set_msize(u32 size)
|
|||
#define ENETC_SICAPR1 0x904
|
||||
|
||||
#define ENETC_PSIIER 0xa00
|
||||
#define ENETC_PSIIER_MR_MASK GENMASK(2, 1)
|
||||
#define ENETC_PSIIDR 0xa08
|
||||
#define ENETC_SITXIDR 0xa18
|
||||
#define ENETC_SIRXIDR 0xa28
|
||||
|
|
|
|||
|
|
@ -3,18 +3,25 @@
|
|||
|
||||
#include "enetc_pf.h"
|
||||
|
||||
static void enetc_msg_disable_mr_int(struct enetc_hw *hw)
|
||||
static void enetc_msg_disable_mr_int(struct enetc_pf *pf)
|
||||
{
|
||||
u32 psiier = enetc_rd(hw, ENETC_PSIIER);
|
||||
struct enetc_hw *hw = &pf->si->hw;
|
||||
u32 psiier;
|
||||
|
||||
psiier = enetc_rd(hw, ENETC_PSIIER) & ~ENETC_PSIMR_MASK(pf->num_vfs);
|
||||
|
||||
/* disable MR int source(s) */
|
||||
enetc_wr(hw, ENETC_PSIIER, psiier & ~ENETC_PSIIER_MR_MASK);
|
||||
enetc_wr(hw, ENETC_PSIIER, psiier);
|
||||
}
|
||||
|
||||
static void enetc_msg_enable_mr_int(struct enetc_hw *hw)
|
||||
static void enetc_msg_enable_mr_int(struct enetc_pf *pf)
|
||||
{
|
||||
u32 psiier = enetc_rd(hw, ENETC_PSIIER);
|
||||
struct enetc_hw *hw = &pf->si->hw;
|
||||
u32 psiier;
|
||||
|
||||
enetc_wr(hw, ENETC_PSIIER, psiier | ENETC_PSIIER_MR_MASK);
|
||||
psiier = enetc_rd(hw, ENETC_PSIIER) | ENETC_PSIMR_MASK(pf->num_vfs);
|
||||
|
||||
enetc_wr(hw, ENETC_PSIIER, psiier);
|
||||
}
|
||||
|
||||
static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
|
||||
|
|
@ -22,7 +29,7 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
|
|||
struct enetc_si *si = (struct enetc_si *)data;
|
||||
struct enetc_pf *pf = enetc_si_priv(si);
|
||||
|
||||
enetc_msg_disable_mr_int(&si->hw);
|
||||
enetc_msg_disable_mr_int(pf);
|
||||
schedule_work(&pf->msg_task);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
|
@ -31,33 +38,35 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
|
|||
static void enetc_msg_task(struct work_struct *work)
|
||||
{
|
||||
struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task);
|
||||
u32 mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
|
||||
struct enetc_hw *hw = &pf->si->hw;
|
||||
unsigned long mr_mask;
|
||||
u32 mr_status;
|
||||
int i;
|
||||
|
||||
for (;;) {
|
||||
mr_mask = enetc_rd(hw, ENETC_PSIMSGRR) & ENETC_PSIMSGRR_MR_MASK;
|
||||
if (!mr_mask) {
|
||||
/* re-arm MR interrupts, w1c the IDR reg */
|
||||
enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIIER_MR_MASK);
|
||||
enetc_msg_enable_mr_int(hw);
|
||||
return;
|
||||
}
|
||||
mr_status = (enetc_rd(hw, ENETC_PSIMSGRR) & mr_mask) |
|
||||
(enetc_rd(hw, ENETC_PSIIDR) & mr_mask);
|
||||
if (!mr_status)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < pf->num_vfs; i++) {
|
||||
u32 psimsgrr;
|
||||
u16 msg_code;
|
||||
for (i = 0; i < pf->num_vfs; i++) {
|
||||
u32 psimsgrr;
|
||||
u16 msg_code;
|
||||
|
||||
if (!(ENETC_PSIMSGRR_MR(i) & mr_mask))
|
||||
continue;
|
||||
if (!(ENETC_PSIMR_BIT(i) & mr_status))
|
||||
continue;
|
||||
|
||||
enetc_msg_handle_rxmsg(pf, i, &msg_code);
|
||||
enetc_msg_handle_rxmsg(pf, i, &msg_code);
|
||||
|
||||
psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code);
|
||||
psimsgrr |= ENETC_PSIMSGRR_MR(i); /* w1c */
|
||||
enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr);
|
||||
}
|
||||
/* w1c to clear the corresponding VF MR bit */
|
||||
enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIMR_BIT(i));
|
||||
|
||||
psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code);
|
||||
psimsgrr |= ENETC_PSIMR_BIT(i); /* w1c */
|
||||
enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr);
|
||||
}
|
||||
|
||||
out:
|
||||
enetc_msg_enable_mr_int(pf);
|
||||
}
|
||||
|
||||
/* Init */
|
||||
|
|
@ -96,12 +105,12 @@ static void enetc_msg_free_mbx(struct enetc_si *si, int idx)
|
|||
struct enetc_hw *hw = &si->hw;
|
||||
struct enetc_msg_swbd *msg;
|
||||
|
||||
enetc_wr(hw, ENETC_PSIVMSGRCVAR0(idx), 0);
|
||||
enetc_wr(hw, ENETC_PSIVMSGRCVAR1(idx), 0);
|
||||
|
||||
msg = &pf->rxmsg[idx];
|
||||
dma_free_coherent(&si->pdev->dev, msg->size, msg->vaddr, msg->dma);
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
enetc_wr(hw, ENETC_PSIVMSGRCVAR0(idx), 0);
|
||||
enetc_wr(hw, ENETC_PSIVMSGRCVAR1(idx), 0);
|
||||
}
|
||||
|
||||
int enetc_msg_psi_init(struct enetc_pf *pf)
|
||||
|
|
@ -109,6 +118,15 @@ int enetc_msg_psi_init(struct enetc_pf *pf)
|
|||
struct enetc_si *si = pf->si;
|
||||
int vector, i, err;
|
||||
|
||||
for (i = 0; i < pf->num_vfs; i++) {
|
||||
err = enetc_msg_alloc_mbx(si, i);
|
||||
if (err)
|
||||
goto free_mbx;
|
||||
}
|
||||
|
||||
/* initialize PSI mailbox */
|
||||
INIT_WORK(&pf->msg_task, enetc_msg_task);
|
||||
|
||||
/* register message passing interrupt handler */
|
||||
snprintf(pf->msg_int_name, sizeof(pf->msg_int_name), "%s-vfmsg",
|
||||
si->ndev->name);
|
||||
|
|
@ -117,32 +135,21 @@ int enetc_msg_psi_init(struct enetc_pf *pf)
|
|||
if (err) {
|
||||
dev_err(&si->pdev->dev,
|
||||
"PSI messaging: request_irq() failed!\n");
|
||||
return err;
|
||||
goto free_mbx;
|
||||
}
|
||||
|
||||
/* set one IRQ entry for PSI message receive notification (SI int) */
|
||||
enetc_wr(&si->hw, ENETC_SIMSIVR, ENETC_SI_INT_IDX);
|
||||
|
||||
/* initialize PSI mailbox */
|
||||
INIT_WORK(&pf->msg_task, enetc_msg_task);
|
||||
|
||||
for (i = 0; i < pf->num_vfs; i++) {
|
||||
err = enetc_msg_alloc_mbx(si, i);
|
||||
if (err)
|
||||
goto err_init_mbx;
|
||||
}
|
||||
|
||||
/* enable MR interrupts */
|
||||
enetc_msg_enable_mr_int(&si->hw);
|
||||
enetc_msg_enable_mr_int(pf);
|
||||
|
||||
return 0;
|
||||
|
||||
err_init_mbx:
|
||||
free_mbx:
|
||||
for (i--; i >= 0; i--)
|
||||
enetc_msg_free_mbx(si, i);
|
||||
|
||||
free_irq(vector, si);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -151,14 +158,17 @@ void enetc_msg_psi_free(struct enetc_pf *pf)
|
|||
struct enetc_si *si = pf->si;
|
||||
int i;
|
||||
|
||||
cancel_work_sync(&pf->msg_task);
|
||||
|
||||
/* disable MR interrupts */
|
||||
enetc_msg_disable_mr_int(&si->hw);
|
||||
|
||||
for (i = 0; i < pf->num_vfs; i++)
|
||||
enetc_msg_free_mbx(si, i);
|
||||
enetc_msg_disable_mr_int(pf);
|
||||
|
||||
/* de-register message passing interrupt handler */
|
||||
free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si);
|
||||
|
||||
cancel_work_sync(&pf->msg_task);
|
||||
|
||||
/* MR interrupts may be re-enabled by workqueue */
|
||||
enetc_msg_disable_mr_int(pf);
|
||||
|
||||
for (i = 0; i < pf->num_vfs; i++)
|
||||
enetc_msg_free_mbx(si, i);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,8 +252,12 @@ static int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac)
|
|||
return -EADDRNOTAVAIL;
|
||||
|
||||
vf_state = &pf->vf_state[vf];
|
||||
|
||||
mutex_lock(&vf_state->lock);
|
||||
vf_state->flags |= ENETC_VF_FLAG_PF_SET_MAC;
|
||||
enetc_pf_set_primary_mac_addr(&priv->si->hw, vf + 1, mac);
|
||||
mutex_unlock(&vf_state->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -478,49 +482,77 @@ 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 (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC)
|
||||
dev_warn(dev, "Attempt to override PF set mac addr for VF%d\n",
|
||||
vf_id);
|
||||
else
|
||||
enetc_pf_set_primary_mac_addr(&pf->si->hw, vf_id + 1, addr);
|
||||
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;
|
||||
}
|
||||
|
||||
mutex_lock(&vf_state->lock);
|
||||
if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC) {
|
||||
mutex_unlock(&vf_state->lock);
|
||||
dev_err_ratelimited(dev,
|
||||
"VF%d attempted to override PF set MAC\n",
|
||||
vf_id);
|
||||
return ENETC_MSG_CMD_STATUS_FAIL;
|
||||
}
|
||||
|
||||
enetc_pf_set_primary_mac_addr(&pf->si->hw, vf_id + 1, addr);
|
||||
mutex_unlock(&vf_state->lock);
|
||||
|
||||
return ENETC_MSG_CMD_STATUS_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
*status = ENETC_MSG_CMD_STATUS_OK;
|
||||
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:
|
||||
dev_err(dev, "command not supported (cmd_type: 0x%x)\n",
|
||||
cmd_type);
|
||||
*status = ENETC_MSG_CMD_STATUS_FAIL;
|
||||
dev_err_ratelimited(dev,
|
||||
"command not supported (cmd_type: 0x%x)\n",
|
||||
cmd_type);
|
||||
}
|
||||
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI_IOV
|
||||
|
|
@ -531,9 +563,9 @@ static int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
|
|||
int err;
|
||||
|
||||
if (!num_vfs) {
|
||||
pci_disable_sriov(pdev);
|
||||
enetc_msg_psi_free(pf);
|
||||
pf->num_vfs = 0;
|
||||
pci_disable_sriov(pdev);
|
||||
} else {
|
||||
pf->num_vfs = num_vfs;
|
||||
|
||||
|
|
@ -960,8 +992,13 @@ static int enetc_pf_probe(struct pci_dev *pdev,
|
|||
if (pf->total_vfs) {
|
||||
pf->vf_state = kzalloc_objs(struct enetc_vf_state,
|
||||
pf->total_vfs);
|
||||
if (!pf->vf_state)
|
||||
if (!pf->vf_state) {
|
||||
err = -ENOMEM;
|
||||
goto err_alloc_vf_state;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pf->total_vfs; i++)
|
||||
mutex_init(&pf->vf_state[i].lock);
|
||||
}
|
||||
|
||||
err = enetc_setup_mac_addresses(node, pf);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ enum enetc_vf_flags {
|
|||
};
|
||||
|
||||
struct enetc_vf_state {
|
||||
struct mutex lock; /* Prevent concurrent access */
|
||||
enum enetc_vf_flags flags;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user