* [PATCH v5 0/2] misc: ibmasm: Fix out-of-bounds MMIO accesses
@ 2026-07-17 15:09 Mingyu Wang
2026-07-17 15:09 ` [PATCH v5 1/2] misc: ibmasm: Fix static out-of-bounds MMIO access during probe Mingyu Wang
2026-07-17 15:09 ` [PATCH v5 2/2] misc: ibmasm: Fix dynamic out-of-bounds MMIO access via malicious MFA Mingyu Wang
0 siblings, 2 replies; 3+ messages in thread
From: Mingyu Wang @ 2026-07-17 15:09 UTC (permalink / raw)
To: gregkh, arnd; +Cc: kees, linux-kernel, stable, Mingyu Wang
This patch series fixes two distinct out-of-bounds (OOB) MMIO access
vectors in the ibmasm driver when exposed to malformed or fuzzed hardware
with an undersized BAR 0.
Patch 1 addresses the static OOB access during the probe phase.
Patch 2 addresses the dynamic OOB accesses via malicious hardware MFAs
during runtime interrupts.
Changes in v5:
- Patch 1: Extended static bounds check threshold to 0xAC1FC to correctly
cover display_depth() accesses identified by static analysis.
- Patch 2: Moved get_i2o_message() out of line into lowlevel.c to avoid
text bloat, as requested by Greg KH.
Changes in v4:
- Patch 1: Extended static bounds check to cover remote input device
registers (up to 0xAC000) that are unconditionally accessed
during probe.
- Patch 2: Added dynamic payload size to bounds calculation to prevent
trailing out-of-bounds memcpy_toio().
- Patch 2: Restored set_mfa_inbound() in the error path to prevent
hardware queue deadlocks, and used safe subtraction for dynamic bounds
checking to prevent integer overflow bypasses.
Changes in v3:
- Split the monolithic v2 patch into a 2-patch series to separate the
probe-time static checks from the runtime dynamic checks, as requested
by Greg KH.
Changes in v2:
- Added dynamic MFA bounds checking in get_i2o_message().
- Implemented hardware mailbox deadlock prevention.
- Fixed potential unsigned integer underflow in bounds check arithmetic.
Mingyu Wang (2):
misc: ibmasm: Fix static out-of-bounds MMIO access during probe
misc: ibmasm: Fix dynamic out-of-bounds MMIO access via malicious MFA
drivers/misc/ibmasm/ibmasm.h | 1 +
drivers/misc/ibmasm/lowlevel.c | 42 ++++++++++++++++++++++++++++++----
drivers/misc/ibmasm/lowlevel.h | 23 +++++++++++++++----
drivers/misc/ibmasm/module.c | 13 +++++++++++
4 files changed, 71 insertions(+), 8 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v5 1/2] misc: ibmasm: Fix static out-of-bounds MMIO access during probe
2026-07-17 15:09 [PATCH v5 0/2] misc: ibmasm: Fix out-of-bounds MMIO accesses Mingyu Wang
@ 2026-07-17 15:09 ` Mingyu Wang
2026-07-17 15:09 ` [PATCH v5 2/2] misc: ibmasm: Fix dynamic out-of-bounds MMIO access via malicious MFA Mingyu Wang
1 sibling, 0 replies; 3+ messages in thread
From: Mingyu Wang @ 2026-07-17 15:09 UTC (permalink / raw)
To: gregkh, arnd; +Cc: kees, linux-kernel, stable, Mingyu Wang
The ibmasm driver maps PCI BAR 0 without verifying if the hardware-provided
resource length is sufficient to cover statically accessed registers.
When evaluating the driver against emulated hardware or during virtual
device fuzzing, a malformed device may expose a significantly undersized
BAR 0. This leads to an out-of-bounds (OOB) access when reading or writing
to registers such as INTR_CONTROL_REGISTER (offset 0x13A4) or mouse
interrupt controls (offset 0xAC000) during probe. A page fault here
while holding the idempotent_init_module() lock causes a cascading global
soft lockup.
Fix this by storing the mapped size in 'struct service_processor' and
ensuring the BAR is at least large enough to cover the highest statically
accessed hardware register before calling pci_ioremap_bar(). This highest
static access (offset 0xAC1FC) occurs via the display_depth() macro
during ibmasmfs filesystem initialization.
Fixes: bdbeed75b288 ("pci: use pci_ioremap_bar() in drivers/misc")
Cc: stable@vger.kernel.org
Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>
---
drivers/misc/ibmasm/ibmasm.h | 1 +
drivers/misc/ibmasm/lowlevel.h | 3 +++
drivers/misc/ibmasm/module.c | 13 +++++++++++++
3 files changed, 17 insertions(+)
diff --git a/drivers/misc/ibmasm/ibmasm.h b/drivers/misc/ibmasm/ibmasm.h
index a5ced88ca923..8d69198bf10f 100644
--- a/drivers/misc/ibmasm/ibmasm.h
+++ b/drivers/misc/ibmasm/ibmasm.h
@@ -140,6 +140,7 @@ struct service_processor {
struct list_head node;
spinlock_t lock;
void __iomem *base_address;
+ resource_size_t mapped_size;
unsigned int irq;
struct command *current_command;
struct command *heartbeat;
diff --git a/drivers/misc/ibmasm/lowlevel.h b/drivers/misc/ibmasm/lowlevel.h
index 25f1ed07c3c5..970d30478c7b 100644
--- a/drivers/misc/ibmasm/lowlevel.h
+++ b/drivers/misc/ibmasm/lowlevel.h
@@ -33,6 +33,9 @@
#define INTR_STATUS_REGISTER 0x13A0
#define INTR_CONTROL_REGISTER 0x13A4
+/* Highest static MMIO offset accessed during probe (display_depth during fs init) */
+#define IBMASM_MAX_REG_OFFSET 0xAC1FC
+
#define SCOUT_COM_A_BASE 0x0000
#define SCOUT_COM_B_BASE 0x0100
#define SCOUT_COM_C_BASE 0x0200
diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c
index 4509c15a76a8..87d4d698a5ff 100644
--- a/drivers/misc/ibmasm/module.c
+++ b/drivers/misc/ibmasm/module.c
@@ -93,6 +93,19 @@ static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
}
sp->irq = pdev->irq;
+ sp->mapped_size = pci_resource_len(pdev, 0);
+
+ /*
+ * Ensure BAR 0 is large enough to cover the highest statically
+ * accessed hardware register (IBMASM_MAX_REG_OFFSET).
+ */
+ if (sp->mapped_size < IBMASM_MAX_REG_OFFSET + 4) {
+ dev_err(sp->dev, "PCI BAR0 too small, need at least %zu bytes\n",
+ (size_t)(IBMASM_MAX_REG_OFFSET + 4));
+ result = -ENODEV;
+ goto error_ioremap;
+ }
+
sp->base_address = pci_ioremap_bar(pdev, 0);
if (!sp->base_address) {
dev_err(sp->dev, "Failed to ioremap pci memory\n");
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v5 2/2] misc: ibmasm: Fix dynamic out-of-bounds MMIO access via malicious MFA
2026-07-17 15:09 [PATCH v5 0/2] misc: ibmasm: Fix out-of-bounds MMIO accesses Mingyu Wang
2026-07-17 15:09 ` [PATCH v5 1/2] misc: ibmasm: Fix static out-of-bounds MMIO access during probe Mingyu Wang
@ 2026-07-17 15:09 ` Mingyu Wang
1 sibling, 0 replies; 3+ messages in thread
From: Mingyu Wang @ 2026-07-17 15:09 UTC (permalink / raw)
To: gregkh, arnd; +Cc: kees, linux-kernel, stable, Mingyu Wang
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 target offset against the actual mapped size
before dereferencing. This validation strictly accounts for both the
i2o_header and the dynamic payload size using safe subtraction to prevent
integer overflow bypasses.
If the bounds check fails, the invalid MFA is released back to the
inbound queue via set_mfa_inbound() to prevent a hardware mailbox deadlock.
Fixes: bdbeed75b288 ("pci: use pci_ioremap_bar() in drivers/misc")
Cc: stable@vger.kernel.org
Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>
---
drivers/misc/ibmasm/lowlevel.c | 42 ++++++++++++++++++++++++++++++----
drivers/misc/ibmasm/lowlevel.h | 20 ++++++++++++----
2 files changed, 54 insertions(+), 8 deletions(-)
diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c
index 5313230f36ad..403fad3b1af0 100644
--- a/drivers/misc/ibmasm/lowlevel.c
+++ b/drivers/misc/ibmasm/lowlevel.c
@@ -9,12 +9,23 @@
#include "ibmasm.h"
#include "lowlevel.h"
-#include "i2o.h"
#include "dot_command.h"
#include "remote.h"
static struct i2o_header header = I2O_HEADER_TEMPLATE;
+struct i2o_message *get_i2o_message(void __iomem *base_address,
+ resource_size_t mapped_size,
+ u32 mfa, size_t msg_size)
+{
+ u32 offset = GET_MFA_ADDR(mfa);
+
+ /* Prevent read/write beyond the ioremap region and avoid integer underflow/overflow */
+ if (unlikely(offset > mapped_size || msg_size > mapped_size - offset))
+ return NULL;
+
+ return (struct i2o_message *)(offset + base_address);
+}
int ibmasm_send_i2o_message(struct service_processor *sp)
{
@@ -34,7 +45,13 @@ 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,
+ sizeof(struct i2o_header) + command_size);
+ if (!message) {
+ /* Release the allocated inbound MFA to prevent mailbox deadlock */
+ 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 +80,25 @@ 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,
+ sizeof(struct i2o_header));
+ if (msg) {
+ u32 data_size = incoming_data_size(msg);
+ u32 offset = GET_MFA_ADDR(mfa);
+
+ /*
+ * Secondary check for dynamic payload size.
+ * Use subtraction to perfectly prevent integer overflow.
+ */
+ if (unlikely(data_size > sp->mapped_size - offset -
+ sizeof(struct i2o_header)))
+ dbg("received mfa payload out of bounds\n");
+ else
+ ibmasm_receive_message(sp, &msg->data, data_size);
+ } else {
+ dbg("received mfa header 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 970d30478c7b..83b20b1d7021 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,18 @@ 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)
-{
- return (struct i2o_message *)(GET_MFA_ADDR(mfa) + base_address);
-}
+/**
+ * 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
+ * @msg_size: Required size of the message (header + payload)
+ *
+ * Returns NULL if the offset derived from @mfa does not fit within
+ * the mapped BAR (including the i2o_message header).
+ */
+struct i2o_message *get_i2o_message(void __iomem *base_address,
+ resource_size_t mapped_size,
+ u32 mfa, size_t msg_size);
#endif /* __IBMASM_CONDOR_H__ */
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-17 15:10 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-17 15:09 [PATCH v5 0/2] misc: ibmasm: Fix out-of-bounds MMIO accesses Mingyu Wang
2026-07-17 15:09 ` [PATCH v5 1/2] misc: ibmasm: Fix static out-of-bounds MMIO access during probe Mingyu Wang
2026-07-17 15:09 ` [PATCH v5 2/2] misc: ibmasm: Fix dynamic out-of-bounds MMIO access via malicious MFA Mingyu Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox