[PATCH v3 2/2] misc: ibmasm: Fix dynamic out-of-bounds MMIO access
From: w15303746062
Date: Tue Jun 23 2026 - 08:43:48 EST
From: Mingyu Wang <25181214217@xxxxxxxxxxxxxxxxx>
The ibmasm driver reads dynamic Message Frame Addresses (MFA) from
hardware queues and uses them directly as offsets to dereference I2O
messages via get_i2o_message().
If a malformed or fuzzed device provides a malicious MFA, it can cause
the driver to access memory far beyond the mapped BAR, leading to an
out-of-bounds (OOB) access and potential kernel panic during runtime.
Fix this by validating the offset against the actual mapped size before
dereferencing. If the check fails, release the MFA back to the inbound
queue to prevent deadlocking the hardware mailbox, and discard the message.
Fixes: bdbeed75b288 ("pci: use pci_ioremap_bar() in drivers/misc")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Mingyu Wang <25181214217@xxxxxxxxxxxxxxxxx>
---
drivers/misc/ibmasm/lowlevel.c | 19 +++++++++++++++----
drivers/misc/ibmasm/lowlevel.h | 24 ++++++++++++++++++++++--
2 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c
index 5313230f36ad..db84e5f827cb 100644
--- a/drivers/misc/ibmasm/lowlevel.c
+++ b/drivers/misc/ibmasm/lowlevel.c
@@ -9,7 +9,6 @@
#include "ibmasm.h"
#include "lowlevel.h"
-#include "i2o.h"
#include "dot_command.h"
#include "remote.h"
@@ -34,7 +33,14 @@ int ibmasm_send_i2o_message(struct service_processor *sp)
return 1;
header.message_size = outgoing_message_size((unsigned int)command_size);
- message = get_i2o_message(sp->base_address, mfa);
+ message = get_i2o_message(sp->base_address, sp->mapped_size, mfa);
+ if (!message) {
+ /* MFA was reserved for us; must release it to avoid
+ * deadlocking the hardware mailbox.
+ */
+ set_mfa_inbound(sp->base_address, mfa);
+ return 1;
+ }
memcpy_toio(&message->header, &header, sizeof(struct i2o_header));
memcpy_toio(&message->data, command->buffer, command_size);
@@ -63,8 +69,13 @@ irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id)
mfa = get_mfa_outbound(base_address);
if (valid_mfa(mfa)) {
- struct i2o_message *msg = get_i2o_message(base_address, mfa);
- ibmasm_receive_message(sp, &msg->data, incoming_data_size(msg));
+ struct i2o_message *msg = get_i2o_message(base_address,
+ sp->mapped_size, mfa);
+ if (msg)
+ ibmasm_receive_message(sp, &msg->data,
+ incoming_data_size(msg));
+ else
+ dbg("received mfa out of bounds\n");
} else
dbg("didn't get a valid MFA\n");
diff --git a/drivers/misc/ibmasm/lowlevel.h b/drivers/misc/ibmasm/lowlevel.h
index c2d2e96ec4e9..8c9700fc70bb 100644
--- a/drivers/misc/ibmasm/lowlevel.h
+++ b/drivers/misc/ibmasm/lowlevel.h
@@ -13,6 +13,9 @@
#define __IBMASM_CONDOR_H__
#include <asm/io.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include "i2o.h"
#define VENDORID_IBM 0x1014
#define DEVICEID_RSA 0x010F
@@ -118,9 +121,26 @@ static inline void set_mfa_inbound(void __iomem *base_address, u32 mfa)
writel(mfa, base_address + INBOUND_QUEUE_PORT);
}
-static inline struct i2o_message *get_i2o_message(void __iomem *base_address, u32 mfa)
+/**
+ * get_i2o_message - Convert MFA to i2o_message pointer with bounds check
+ * @base_address: BAR 0 virtual address
+ * @mapped_size: actual size of BAR 0 mapping
+ * @mfa: Message Frame Address from hardware
+ *
+ * Returns NULL if the offset derived from @mfa does not fit within
+ * the mapped BAR (including the i2o_message header).
+ */
+static inline struct i2o_message *get_i2o_message(void __iomem *base_address,
+ resource_size_t mapped_size,
+ u32 mfa)
{
- return (struct i2o_message *)(GET_MFA_ADDR(mfa) + base_address);
+ u32 offset = GET_MFA_ADDR(mfa);
+
+ /* Prevent read/write beyond the ioremap region */
+ if (unlikely(offset + sizeof(struct i2o_message) > mapped_size))
+ return NULL;
+
+ return (struct i2o_message *)(offset + base_address);
}
#endif /* __IBMASM_CONDOR_H__ */
--
2.34.1