From 53637506884dbd5c91a89b1a3547d99d80f8ed2c Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 24 Jun 2026 19:53:53 +0200 Subject: [PATCH 1/8] ipmi: ipmb: validate write message length ipmb_write() read message fields before validating the length byte. A zero or short write can read uninitialized stack bytes. A length smaller than the SMBus header underflows the block write length. Require a non-empty buffer and the minimum IPMB request length. Also require the length byte plus payload before parsing the message. Fixes: 51bd6f291583 ("Add support for IPMB driver") Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Message-ID: <20260624175353.8592-1-alhouseenyousef@gmail.com> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmb_dev_int.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c index 680ff15c30ab..e4c50d9ae3e1 100644 --- a/drivers/char/ipmi/ipmb_dev_int.c +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -141,13 +141,14 @@ static ssize_t ipmb_write(struct file *file, const char __user *buf, u8 msg[MAX_MSG_LEN]; ssize_t ret; - if (count > sizeof(msg)) + if (!count || count > sizeof(msg)) return -EINVAL; if (copy_from_user(&msg, buf, count)) return -EFAULT; - if (count < msg[0]) + if (msg[IPMB_MSG_LEN_IDX] < IPMB_REQUEST_LEN_MIN || + count < (size_t)msg[IPMB_MSG_LEN_IDX] + 1) return -EINVAL; rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]); From 6d920a75df9a83ab096b3cde7a643b656e4fdfeb Mon Sep 17 00:00:00 2001 From: Seiji Nishikawa Date: Wed, 1 Jul 2026 02:43:48 +0900 Subject: [PATCH 2/8] ipmi: si: Fix NULL pointer dereference after failed registration try_smi_init() allocates new_smi->si_sm and later calls ipmi_register_smi_mod(), which maps to ipmi_add_smi(). During ipmi_add_smi(), the upper IPMI message handler obtains the initial BMC device information through __bmc_get_device_id(). This can fail if the BMC does not return a successful response to the Get Device ID command. When the BMC returns a nonzero completion code, the device-id helper retries the command and eventually returns -EIO if the device ID still cannot be fetched. On this failure path, ipmi_add_smi() logs "Unable to get the device id" and goes to out_err_started, where it invokes the lower driver's shutdown callback. try_smi_init() then logs the returned registration failure: ipmi_si IPI0001:00: IPMI message handler: Unable to get the device id: -5 ipmi_si IPI0001:00: Unable to register device: error -5 For ipmi_si, the shutdown callback is shutdown_smi(), which cleans up the SI state machine data, frees smi_info->si_sm, and sets smi_info->si_sm and smi_info->intf to NULL. However, intf->in_shutdown is not set on this failed-registration rollback path. Therefore, the asynchronous redo_bmc_reg work item can still retry BMC device-id probing after the lower driver has already cleared its SI state machine data. In the observed case, that retry path reached start_next_msg(), which passed the NULL smi_info->si_sm pointer to the selected KCS state machine handler: BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 Workqueue: events redo_bmc_reg [ipmi_msghandler] RIP: start_kcs_transaction+0x2c/0x190 [ipmi_si] Call Trace: start_next_msg+0x50/0x80 [ipmi_si] check_start_timer_thread.part.9+0x3b/0x50 [ipmi_si] sender+0x69/0x80 [ipmi_si] i_ipmi_request+0x2ac/0x9d0 [ipmi_msghandler] __get_device_id.isra.29+0xaa/0x180 [ipmi_msghandler] __bmc_get_device_id+0xef/0x950 [ipmi_msghandler] redo_bmc_reg+0x52/0x60 [ipmi_msghandler] process_one_work+0x1a7/0x360 Set intf->in_shutdown on the out_err_started path before invoking the lower driver's shutdown callback. This prevents later redo_bmc_reg retries from using an interface whose lower driver state has been cleaned up, and applies the same shutdown state to other IPMI interfaces as well. Fixes: 2512e40e48d2 ("ipmi: Rework SMI registration failure") Cc: stable@vger.kernel.org Signed-off-by: Seiji Nishikawa Message-ID: <20260630174348.1483814-1-snishika@redhat.com> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index ab4c85f3d6fe..8d9f2e647d9b 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -3757,6 +3757,7 @@ int ipmi_add_smi(struct module *owner, out_err_bmc_reg: ipmi_bmc_unregister(intf); out_err_started: + intf->in_shutdown = true; if (intf->handlers->shutdown) intf->handlers->shutdown(intf->send_info); out_err: From b6c46ab0bdee90c238e96ea4a74972118c97900d Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Sun, 2 Aug 2026 21:55:50 -0400 Subject: [PATCH 3/8] ipmi: Remove all sysfs files on registration failure ipmi_add_smi() creates the nr_users and nr_msgs files before trying to create the maintenance_mode file. If that last creation fails, the error path removes only nr_users before dropping the final reference to the interface. Remove nr_msgs as well so no sysfs attribute embedded in the freed interface remains registered. Fixes: 627118470fcc ("ipmi: Add a maintenance mode sysfs file") Cc: stable@vger.kernel.org # 6.18 Signed-off-by: Yuho Choi Message-ID: <20260803015550.618808-1-dbgh9129@gmail.com> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 8d9f2e647d9b..6ff9a15cced8 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -3740,6 +3740,7 @@ int ipmi_add_smi(struct module *owner, sysfs_attr_init(&intf->maintenance_mode_devattr.attr); rv = device_create_file(intf->si_dev, &intf->maintenance_mode_devattr); if (rv) { + device_remove_file(intf->si_dev, &intf->nr_msgs_devattr); device_remove_file(intf->si_dev, &intf->nr_users_devattr); goto out_err_bmc_reg; } From 18e633dd9674c8bf118ea8583bb5a531edc2728a Mon Sep 17 00:00:00 2001 From: Michail Tatas Date: Fri, 7 Aug 2026 01:30:20 +0300 Subject: [PATCH 4/8] ipmi: Fix leak in __ipmi_bmc_register In case that ida_alloc(&ipmi_bmc_ida,...) succeeds and then platform_device_register() fails, ipmi_bmc_ida is leaked. Fix by freeing the error path Signed-off-by: Michail Tatas Message-ID: Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 6ff9a15cced8..7634dff99f41 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -3301,6 +3301,7 @@ static int __ipmi_bmc_register(struct ipmi_smi *intf, list_del(&intf->bmc_link); mutex_unlock(&bmc->dyn_mutex); intf->bmc = &intf->tmp_bmc; + ida_free(&ipmi_bmc_ida, bmc->pdev.id); put_device(&bmc->pdev.dev); goto out; } From eee1ea58c81ecb4d05c7267c05da73544386f3d9 Mon Sep 17 00:00:00 2001 From: "Pawel Zalewski (The Capable Hub)" Date: Fri, 7 Aug 2026 12:26:10 +0100 Subject: [PATCH 5/8] char: ipmi: use named initializers for acpi_device_id Use a named initializer for the acpi_device_id fields which makes the code more readable and consistent with how lists are initialized in the rest of the kernel code base. Also drop explicitly setting fields to 0 where it is redundant. While we are at it - unify the list terminator to have a single space between the brackets and no trailing comma. Signed-off-by: Pawel Zalewski (The Capable Hub) Message-ID: <20260807-acpi-char-v1-3-742c450254dd@thegoodpenguin.co.uk> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmb_dev_int.c | 4 ++-- drivers/char/ipmi/ipmi_si_platform.c | 4 ++-- drivers/char/ipmi/ipmi_ssif.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c index e4c50d9ae3e1..eae31ec92382 100644 --- a/drivers/char/ipmi/ipmb_dev_int.c +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -361,8 +361,8 @@ MODULE_DEVICE_TABLE(i2c, ipmb_id); #ifdef CONFIG_ACPI static const struct acpi_device_id acpi_ipmb_id[] = { - { "IPMB0001", 0 }, - {}, + { .id = "IPMB0001" }, + { } }; MODULE_DEVICE_TABLE(acpi, acpi_ipmb_id); #endif diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index bdc481ce1302..fa221cbb4b3b 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -387,8 +387,8 @@ static int acpi_ipmi_probe(struct platform_device *pdev) } static const struct acpi_device_id acpi_ipmi_match[] = { - { "IPI0001", 0 }, - { }, + { .id = "IPI0001" }, + { } }; MODULE_DEVICE_TABLE(acpi, acpi_ipmi_match); #else diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 07f1d2327bb7..2361103c5edc 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -2057,8 +2057,8 @@ static unsigned short *ssif_address_list(void) #ifdef CONFIG_ACPI static const struct acpi_device_id ssif_acpi_match[] = { - { "IPI0001", 0 }, - { }, + { .id = "IPI0001" }, + { } }; MODULE_DEVICE_TABLE(acpi, ssif_acpi_match); #endif From ed98f8e27a93dae225a99ac41ad48f967fd19842 Mon Sep 17 00:00:00 2001 From: Michal Clapinski Date: Mon, 10 Aug 2026 09:48:51 +0200 Subject: [PATCH 6/8] ipmi:si: Add async init to ipmi_si Added a new config option to allow offloading individual calls to try_smi_init() using workqueue. Saves 100ms on my system. Signed-off-by: Michal Clapinski Message-ID: <20260810074851.306979-1-mclapinski@google.com> Signed-off-by: Corey Minyard --- drivers/char/ipmi/Kconfig | 9 ++++ drivers/char/ipmi/ipmi_si_intf.c | 79 +++++++++++++++++++++++++------- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 669f76000197..538a7d3c65bf 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -67,6 +67,15 @@ config IPMI_SI Currently, only KCS and SMIC are supported. If you are using IPMI, you should probably say "y" here. +config IPMI_SI_ASYNC_INIT + bool 'Asynchronous initialization of IPMI System Interface' + depends on IPMI_SI + default n + help + Offloads individual SMI inits. It speeds up the boot time. + It also introduces a very small risk that something else might fail + if it depends on synchronous IPMI init. + config IPMI_SSIF tristate 'IPMI SMBus handler (SSIF)' depends on I2C diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 9a9d12be9bf7..6b95a7581328 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "ipmi_si.h" #include "ipmi_si_sm.h" #include @@ -252,6 +253,8 @@ struct smi_info { struct task_struct *thread; + struct work_struct init_work; + struct list_head link; }; @@ -272,6 +275,7 @@ static bool unload_when_empty = true; static int try_smi_init(struct smi_info *smi); static void cleanup_one_si(struct smi_info *smi_info); static void cleanup_ipmi_si(void); +static void smi_init_work_fn(struct work_struct *work); #ifdef DEBUG_TIMING void debug_timestamp(struct smi_info *smi_info, char *msg) @@ -1970,6 +1974,7 @@ int ipmi_si_add_smi(struct si_sm_io *io) if (!new_smi) return -ENOMEM; spin_lock_init(&new_smi->si_lock); + INIT_WORK(&new_smi->init_work, smi_init_work_fn); new_smi->io = *io; @@ -1982,7 +1987,12 @@ int ipmi_si_add_smi(struct si_sm_io *io) dev_info(dup->io.dev, "Removing SMBIOS-specified %s state machine in favor of ACPI\n", si_to_str[new_smi->io.si_info->type]); + list_del(&dup->link); + mutex_unlock(&smi_infos_lock); + cleanup_one_si(dup); + + mutex_lock(&smi_infos_lock); } else { dev_info(new_smi->io.dev, "%s-specified %s state machine: duplicate\n", @@ -2000,8 +2010,12 @@ int ipmi_si_add_smi(struct si_sm_io *io) list_add_tail(&new_smi->link, &smi_infos); - if (initialized) - rv = try_smi_init(new_smi); + if (initialized) { + if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT)) + queue_work(system_dfl_wq, &new_smi->init_work); + else + rv = try_smi_init(new_smi); + } out_err: mutex_unlock(&smi_infos_lock); return rv; @@ -2174,6 +2188,15 @@ static bool __init ipmi_smi_info_same(struct smi_info *e1, struct smi_info *e2) e1->io.addr_data == e2->io.addr_data); } +static void smi_init_work_fn(struct work_struct *work) +{ + struct smi_info *smi = container_of(work, struct smi_info, init_work); + + mutex_lock(&smi_infos_lock); + try_smi_init(smi); + mutex_unlock(&smi_infos_lock); +} + static int __init init_ipmi_si(void) { struct smi_info *e, *e2; @@ -2219,8 +2242,12 @@ static int __init init_ipmi_si(void) break; } } - if (!dup) - try_smi_init(e); + if (!dup) { + if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT)) + queue_work(system_unbound_wq, &e->init_work); + else + try_smi_init(e); + } } /* @@ -2253,8 +2280,12 @@ static int __init init_ipmi_si(void) break; } } - if (!dup) - try_smi_init(e); + if (!dup) { + if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT)) + queue_work(system_unbound_wq, &e->init_work); + else + try_smi_init(e); + } } initialized = true; @@ -2344,31 +2375,36 @@ static void shutdown_smi(void *send_info) } /* - * Must be called with smi_infos_lock held, to serialize the - * smi_info->intf check. + * Must be called with smi_info unlinked from smi_infos and smi_infos_lock released. */ static void cleanup_one_si(struct smi_info *smi_info) { if (!smi_info) return; - list_del(&smi_info->link); + if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT)) + cancel_work_sync(&smi_info->init_work); + ipmi_unregister_smi(smi_info->intf); kfree(smi_info); } void ipmi_si_remove_by_dev(struct device *dev) { - struct smi_info *e; + struct smi_info *e = NULL, *tmp; mutex_lock(&smi_infos_lock); - list_for_each_entry(e, &smi_infos, link) { - if (e->io.dev == dev) { - cleanup_one_si(e); + list_for_each_entry(tmp, &smi_infos, link) { + if (tmp->io.dev == dev) { + e = tmp; + list_del(&e->link); break; } } mutex_unlock(&smi_infos_lock); + + if (e) + cleanup_one_si(e); } struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type, @@ -2377,6 +2413,7 @@ struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type, /* remove */ struct smi_info *e, *tmp_e; struct device *dev = NULL; + LIST_HEAD(to_clean); mutex_lock(&smi_infos_lock); list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { @@ -2386,17 +2423,23 @@ struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type, continue; if (e->io.addr_data == addr) { dev = get_device(e->io.dev); - cleanup_one_si(e); + list_move_tail(&e->link, &to_clean); } } mutex_unlock(&smi_infos_lock); + list_for_each_entry_safe(e, tmp_e, &to_clean, link) { + list_del(&e->link); + cleanup_one_si(e); + } + return dev; } static void cleanup_ipmi_si(void) { struct smi_info *e, *tmp_e; + LIST_HEAD(to_clean); if (!initialized) return; @@ -2410,10 +2453,14 @@ static void cleanup_ipmi_si(void) ipmi_si_platform_shutdown(); mutex_lock(&smi_infos_lock); - list_for_each_entry_safe(e, tmp_e, &smi_infos, link) - cleanup_one_si(e); + list_splice_init(&smi_infos, &to_clean); mutex_unlock(&smi_infos_lock); + list_for_each_entry_safe(e, tmp_e, &to_clean, link) { + list_del(&e->link); + cleanup_one_si(e); + } + ipmi_si_hardcode_exit(); ipmi_si_hotmod_exit(); } From ae84a2536577057e97f23f75a202e26d0e86cf01 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 18 Aug 2026 12:49:52 -0500 Subject: [PATCH 7/8] ipmi:msghandler: Cancel work cleanly on an error If an error occurs during startup of an IPMI interface, it may have scheduled work to run. The work needs to be canceled before the interface can be freed. Reported-by: Nilay Shroff Closes: https://sourceforge.net/p/openipmi/mailman/message/59375605/ Fixes: 62cd145453d5 ("ipmi:msghandler: Handle error returns from the SMI sender") Cc: stable@vger.kernel.org # 7.0 Tested-by: Nilay Shroff Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 7634dff99f41..c73e9def59a6 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -3766,6 +3766,7 @@ int ipmi_add_smi(struct module *owner, list_del(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); mutex_unlock(&smi_watchers_mutex); + cancel_work_sync(&intf->smi_work); kref_put(&intf->refcount, intf_free); return rv; From 05ec76cfbce653e07cec19b9b8b20e33449d5d87 Mon Sep 17 00:00:00 2001 From: Yifei Gao Date: Tue, 25 Aug 2026 23:46:29 +0000 Subject: [PATCH 8/8] ipmi: Fix use-after-free of cmd_rcvr in _ipmi_destroy_user() Commit 9e91f8a6c868 ("ipmi:msghandler: Remove srcu for the ipmi_interfaces list") dropped the synchronize_rcu() between unlinking the command receivers from intf->cmd_rcvrs and freeing them, updating only the comment that explains why the barrier is needed. The cmd_rcvrs list is still traversed under plain RCU: find_cmd_rcvr() walks it inside rcu_read_lock(), and handle_ipmb_get_msg_cmd() borrows rcvr->user from that lookup within the same read-side section. Without the grace period, _ipmi_destroy_user() can kfree() a cmd_rcvr while a reader still holds a pointer to it, causing a use-after-free. The rework only made srcu unnecessary for the interfaces list; the cmd_rcvrs list still relies on plain RCU. Restore the synchronize_rcu() before freeing the receivers. Fixes: 9e91f8a6c868 ("ipmi:msghandler: Remove srcu for the ipmi_interfaces list") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Yifei Gao Message-ID: <20260825234630.1196170-1-gyf161023@gmail.com> Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index c73e9def59a6..152dd6b5bb27 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -1391,6 +1391,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user) } } mutex_unlock(&intf->cmd_rcvrs_mutex); + synchronize_rcu(); while (rcvrs) { rcvr = rcvrs; rcvrs = rcvr->next;