⬢ DragonFlyBSD Kernel Audit
DF-3797 / fix.diff
← back to finding ↓ download raw
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");