diff --git a/sys/dev/virtual/amazon/ena/ena-com/ena_com.c b/sys/dev/virtual/amazon/ena/ena-com/ena_com.c index 4314b31..7197cad 100644 --- a/sys/dev/virtual/amazon/ena/ena-com/ena_com.c +++ b/sys/dev/virtual/amazon/ena/ena-com/ena_com.c @@ -1153,6 +1153,17 @@ static int ena_com_create_io_sq(struct ena_com_dev *ena_dev, return ret; } + /* DF-3797: the device supplies this register offset; it must land + * inside the register BAR or a malicious device turns every TX + * doorbell into an arbitrary-offset kernel write. + */ + if (unlikely(cmd_completion.sq_doorbell_offset >= ena_dev->reg_bar_len)) { + ena_trc_err("Invalid SQ doorbell offset %u (reg BAR len %u)\n", + cmd_completion.sq_doorbell_offset, + ena_dev->reg_bar_len); + return ENA_COM_INVAL; + } + io_sq->idx = cmd_completion.sq_idx; io_sq->db_addr = (u32 __iomem *)((uintptr_t)ena_dev->reg_bar + @@ -1330,6 +1341,25 @@ int ena_com_create_io_cq(struct ena_com_dev *ena_dev, return ret; } + /* DF-3797: all three offsets are device-supplied; validate them + * against the register BAR extent before caching them as write + * targets (unmask_reg / cq_head_db_reg / numa_node_cfg_reg are + * written by the interrupt handlers on every event). + */ + if (unlikely((cmd_completion.cq_interrupt_unmask_register_offset >= + ena_dev->reg_bar_len) || + (cmd_completion.cq_head_db_register_offset >= + ena_dev->reg_bar_len) || + (cmd_completion.numa_node_register_offset >= + ena_dev->reg_bar_len))) { + ena_trc_err("Invalid CQ register offset (unmask %u, head db %u, numa %u; reg BAR len %u)\n", + cmd_completion.cq_interrupt_unmask_register_offset, + cmd_completion.cq_head_db_register_offset, + cmd_completion.numa_node_register_offset, + ena_dev->reg_bar_len); + return ENA_COM_INVAL; + } + io_cq->idx = cmd_completion.cq_idx; io_cq->unmask_reg = (u32 __iomem *)((uintptr_t)ena_dev->reg_bar + diff --git a/sys/dev/virtual/amazon/ena/ena-com/ena_com.h b/sys/dev/virtual/amazon/ena/ena-com/ena_com.h index 9425205..9dac11f 100644 --- a/sys/dev/virtual/amazon/ena/ena-com/ena_com.h +++ b/sys/dev/virtual/amazon/ena/ena-com/ena_com.h @@ -345,6 +345,10 @@ struct ena_com_dev { struct ena_com_io_cq io_cq_queues[ENA_TOTAL_NUM_QUEUES]; struct ena_com_io_sq io_sq_queues[ENA_TOTAL_NUM_QUEUES]; u8 __iomem *reg_bar; + + /* Extent of the register BAR, used to validate device-supplied + * register offsets (DF-3797). */ + u32 reg_bar_len; void __iomem *mem_bar; void *dmadev; void *bus; diff --git a/sys/dev/virtual/amazon/ena/ena.c b/sys/dev/virtual/amazon/ena/ena.c index 97aed1c..fa908fc 100644 --- a/sys/dev/virtual/amazon/ena/ena.c +++ b/sys/dev/virtual/amazon/ena/ena.c @@ -3673,6 +3673,7 @@ ena_attach(device_t pdev) rman_get_bustag(adapter->registers); ((struct ena_bus*)(ena_dev->bus))->reg_bar_h = rman_get_bushandle(adapter->registers); + ena_dev->reg_bar_len = (u32)rman_get_size(adapter->registers); if (unlikely(((struct ena_bus*)(ena_dev->bus))->reg_bar_h == 0)) { device_printf(pdev, "failed to pmap registers bar\n");