Firmware chooses where a mailbox channel's head and tail registers live and reports them to the driver as device addresses: in the management mailbox block it writes into the SRAM BAR, read by aie2_get_mgmt_chann_info(), and in the CREATE_CONTEXT response, read by aie2_create_context() for every hardware context. AIE2_MBOX_OFF() turns each one into a raw byte offset into the mailbox mapping, and both aie2_hw_start() and aie2_create_context() derive the mailbox interrupt register from one of them by adding 4. On AIE4, aie4_mailbox_start() takes the same four offsets straight out of the mailbox_info block.
None of them is checked. mailbox_reg_read() and mailbox_reg_write() add the offset to xdna_mailbox_res::mbox_base and hand the result to readl()/writel(), so an offset past the end of that mapping is an MMIO access outside it, at a kernel virtual address the driver has no claim to. AIE2_MBOX_OFF() is an unsigned 32-bit subtraction: a reported address below the aperture base does not yield an obviously invalid small offset but wraps to a very large one, and the + 4 for the interrupt register can wrap independently of the value it derives from. The bound is already there. xdna_mailbox_res::mbox_size is the exact length of the mapping pcim_iomap() produced - the device's mailbox window size, or the BAR length when the device does not override it - and it sits in the same struct as mbox_base. The driver stores it and never reads it. Check the four register offsets and the interrupt register against mbox_size in xdna_mailbox_start_channel(). Every mailbox register access reads mb_chann->res[] or mb_chann->iohub_int_addr, and that function is the only writer of either, so it is the one point every firmware-supplied offset passes through, for the management channel, for a hardware context and for AIE4 alike. It is also where the derived interrupt register arrives, as a parameter, which a check at the producing sites would not cover. A zero interrupt register keeps its existing meaning of "this platform has no such register". Failing there rejects the channel before any offset reaches readl() or writel(). aie2_hw_start() and aie4_mailbox_start() unwind and fail the probe or resume; aie2_create_context() frees the channel and destroys the firmware context, so AMDXDNA_CREATE_HWCTX returns an error to userspace instead of leaving a channel that accesses outside its mapping. This is the firmware-to-driver trust boundary. Whether the part can report a register outside the mailbox aperture is not established and there is no reproducer. The offsets are simply the one firmware-supplied value this driver leaves unbounded, against a limit it already computes and stores. Signed-off-by: Eva Crystal <[email protected]> --- drivers/accel/amdxdna/amdxdna_mailbox.c | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.c b/drivers/accel/amdxdna/amdxdna_mailbox.c index cc8865f4e79c..0c4cb8beb26e 100644 --- a/drivers/accel/amdxdna/amdxdna_mailbox.c +++ b/drivers/accel/amdxdna/amdxdna_mailbox.c @@ -112,6 +112,28 @@ static u32 mailbox_reg_read(struct mailbox_channel *mb_chann, u32 mbox_reg) return readl(ringbuf_addr); } +/* + * Firmware describes where a channel's head and tail registers live, as raw + * offsets into the mailbox mapping: in the management mailbox block it writes + * into SRAM for the management channel, and in the CREATE_CONTEXT response for + * a hardware context. Both helpers above add such an offset straight to + * mbox_base, so bound it against the size of that mapping first. + */ +static bool mailbox_reg_in_range(struct mailbox_channel *mb_chann, u32 mbox_reg) +{ + struct xdna_mailbox_res *mb_res = &mb_chann->mb->res; + + /* Every access through the two helpers above is 32 bits wide. */ + return (u64)mbox_reg + sizeof(u32) <= mb_res->mbox_size; +} + +static bool mailbox_chann_res_in_range(struct mailbox_channel *mb_chann, + const struct xdna_mailbox_chann_res *res) +{ + return mailbox_reg_in_range(mb_chann, res->mb_head_ptr_reg) && + mailbox_reg_in_range(mb_chann, res->mb_tail_ptr_reg); +} + static inline void mailbox_irq_acknowledge(struct mailbox_channel *mb_chann) { if (mb_chann->iohub_int_addr) @@ -518,6 +540,16 @@ xdna_mailbox_start_channel(struct mailbox_channel *mb_chann, return -EINVAL; } + /* A zero iohub_int_addr means the platform has no such register. */ + if (!mailbox_chann_res_in_range(mb_chann, x2i) || + !mailbox_chann_res_in_range(mb_chann, i2x) || + (iohub_int_addr && !mailbox_reg_in_range(mb_chann, iohub_int_addr))) { + dev_err(mb_chann->mb->dev, + "Mailbox register offset outside the %zu byte mailbox mapping\n", + mb_chann->mb->res.mbox_size); + return -EINVAL; + } + mb_chann->msix_irq = mb_irq; mb_chann->iohub_int_addr = iohub_int_addr; memcpy(&mb_chann->res[CHAN_RES_X2I], x2i, sizeof(*x2i)); -- 2.53.0
