From nobody Fri Oct 2 04:27:51 2026 Received: from mout-p-102.mailbox.org (mout-p-102.mailbox.org [80.241.56.152]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 38B5228B7DA for ; Wed, 5 Aug 2026 13:00:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.152 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785934847; cv=none; b=Vzf+HA8QaV/pYlbbyOE2rgCjPDovPYXI65fB2T440fohBT838yp8WQZ+hrS8rXeR1IAcZLDq6NDm2qEw0FDxyDZTDIORbe40SMDGU1YWiw4EGy2/mX7dQV8/E7xP84+L8v+i+DTeUyqV9f/k52piMypBpQbuiKXdanOpFrDbfzM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785934847; c=relaxed/simple; bh=AmhZD8PVyZOexQqWD5goIysCbYLolEaxSDteJ4d4W3Q=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=CRtgk9XfLgwacy4yR/IUZMXQJ8XoHDyhoR3W3PkQ+LAiFxwCW8oEAtg1VxSqfVTal3RCh7h2+wYt7o0LI4jD27qGQFFjk/XVLKhMfrnO97ysfMkJkswiZYj6GrQeZBOeCkGaPMEo1grEo9OLeUiNeAWe3V3XpQyQ/NtdXR973PU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mailbox.org; spf=pass smtp.mailfrom=mailbox.org; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b=PxhNDz5N; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b=lsFYqCVb; arc=none smtp.client-ip=80.241.56.152 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mailbox.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mailbox.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b="PxhNDz5N"; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b="lsFYqCVb" Received: from smtp2.mailbox.org (smtp2.mailbox.org [10.196.197.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-102.mailbox.org (Postfix) with ESMTPS id 4hFVrn6dCKzKw20; Wed, 05 Aug 2026 15:00:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785934841; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=trGjHvxlJc+mkL+GsMH7dHkXn6aLOWp+v3b1oo6Jq7Q=; b=PxhNDz5N4zFFjp4ogJg84/xxwWX2pN97cHbeVOll2un/RNQdpht2mm8o3ARPzFfFEdfoMa uHHI80v4CQsxr8MxMkva4Up/lLtNQ5+2RXwkzpfCKXYXzzJqO73ATIaVKkdqdlFbdStkEx Jrc++ZgfoobZhkMbft0Vzt4b7QfD5tzWZrpYLI3t8Leum8YlYgAJa+Uq4aWcXmbpS8YKgm 417wuvzfDE1A4yZ63FddzBJ7IPoCWHA7RFla4h/qB+gLwQLHYbxABBr9a4Suz7YYMoZVop ePPfesbYECyoJVfjQ+d0HhePGdQ5AFTeMkVH8BtctulVOSVxAuBxRzxY1zLvEg== From: Qing Ming DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785934840; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=trGjHvxlJc+mkL+GsMH7dHkXn6aLOWp+v3b1oo6Jq7Q=; b=lsFYqCVbc5JNt2HfJS+dYORMbqDM9crA3jv3do0Wk+xy2hBmZpWbFbqmW1S9kRYM9JTHNG wKzUONfEE+bbLgHUIu8/i/Mg1VWGAPIfXiBIdTRDnf7Gj4VxWrpibW+m8NowzXGQhpqtS0 ha11f+54LUZOAdqu57V5RAqAKzUIUpGi5XlC85RuwT9PgzKW/vcw3rFSTPKVqggLhb5Zpw j1q1Wx73Z0ntye006P/u8gd4zqlYSCwFzUDy0+O56yGPtPR5AaMOPiF0cPqmngzCQRpqhI h4TPJRteYyXRlGTZzpGeQAr9U1TPlNz7P8AhMu4Bdp+uUf/jGEOgOXkc0qEZTw== To: Corey Minyard Cc: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= , Jeremy Kerr , Rob Herring , Alistair Popple , Joel Stanley , Arnd Bergmann , openipmi-developer@lists.sourceforge.net, linux-kernel@vger.kernel.org, Qing Ming Subject: [PATCH] ipmi: bt-bmc: keep device alive across remove Date: Wed, 5 Aug 2026 21:00:09 +0800 Message-ID: <20260805130009.15600-1-a0yami@mailbox.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MBO-RS-ID: aa0d2f5a8840eb52099 X-MBO-RS-META: m61if7jimbzxzpykh94nmsb7r57uyawi Content-Type: text/plain; charset="utf-8" misc_deregister() prevents new opens but does not close existing files. bt_bmc and its MMIO mapping are devm-managed, so platform device removal releases them while an open /dev/ipmi-bt-host file can still reach them through poll and release. Keeping /dev/ipmi-bt-host open across platform removal and then polling the old file triggers: BUG: KASAN: slab-use-after-free in bt_bmc_poll Allocate bt_bmc explicitly and hold a reference for the successful open. Mark the device dead before deregistering the miscdevice, wake blocked operations, and reject old-file MMIO access after removal. Serialize process-context MMIO with the disconnect state and use a spinlock for wait conditions and the IRQ handler. Shut down the self-rearming poll timer, or release and synchronize the IRQ, before dropping the probe reference. Leave the final file release to free the software context when necessary. Fixes: 54f9c4d0778b ("ipmi: add an Aspeed BT IPMI BMC driver") Cc: stable@vger.kernel.org Signed-off-by: Qing Ming --- drivers/char/ipmi/bt-bmc.c | 141 ++++++++++++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 19 deletions(-) diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index a179d4797011..481c83875908 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -8,12 +8,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include =20 /* @@ -55,6 +57,7 @@ #define BT_BMC_BUFFER_SIZE 256 =20 struct bt_bmc { + struct kref refcount; struct device dev; struct miscdevice miscdev; void __iomem *base; @@ -62,6 +65,9 @@ struct bt_bmc { wait_queue_head_t queue; struct timer_list poll_timer; struct mutex mutex; + /* Protects the disconnect state against MMIO access. */ + spinlock_t state_lock; + bool dead; }; =20 static atomic_t open_count =3D ATOMIC_INIT(0); @@ -143,20 +149,61 @@ static void set_sms_atn(struct bt_bmc *bt_bmc) =20 static struct bt_bmc *file_bt_bmc(struct file *file) { - return container_of(file->private_data, struct bt_bmc, miscdev); + return file->private_data; +} + +static void bt_bmc_free(struct kref *refcount) +{ + struct bt_bmc *bt_bmc; + + bt_bmc =3D container_of(refcount, struct bt_bmc, refcount); + kfree(bt_bmc); +} + +static bool bt_bmc_request_ready(struct bt_bmc *bt_bmc) +{ + unsigned long flags; + bool ready; + + spin_lock_irqsave(&bt_bmc->state_lock, flags); + ready =3D bt_bmc->dead || + (bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_H2B_ATN); + spin_unlock_irqrestore(&bt_bmc->state_lock, flags); + return ready; +} + +static bool bt_bmc_response_ready(struct bt_bmc *bt_bmc) +{ + unsigned long flags; + bool ready; + + spin_lock_irqsave(&bt_bmc->state_lock, flags); + ready =3D bt_bmc->dead || + !(bt_inb(bt_bmc, BT_CTRL) & + (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN)); + spin_unlock_irqrestore(&bt_bmc->state_lock, flags); + return ready; } =20 static int bt_bmc_open(struct inode *inode, struct file *file) { - struct bt_bmc *bt_bmc =3D file_bt_bmc(file); + struct bt_bmc *bt_bmc; + int ret =3D 0; =20 - if (atomic_inc_return(&open_count) =3D=3D 1) { + bt_bmc =3D container_of(file->private_data, struct bt_bmc, miscdev); + mutex_lock(&bt_bmc->mutex); + if (bt_bmc->dead) { + ret =3D -ENODEV; + } else if (atomic_inc_return(&open_count) =3D=3D 1) { + kref_get(&bt_bmc->refcount); + file->private_data =3D bt_bmc; clr_b_busy(bt_bmc); - return 0; + } else { + atomic_dec(&open_count); + ret =3D -EBUSY; } - - atomic_dec(&open_count); - return -EBUSY; + mutex_unlock(&bt_bmc->mutex); + return ret; } =20 /* @@ -185,10 +232,14 @@ static ssize_t bt_bmc_read(struct file *file, char __= user *buf, WARN_ON(*ppos); =20 if (wait_event_interruptible(bt_bmc->queue, - bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_H2B_ATN)) + bt_bmc_request_ready(bt_bmc))) return -ERESTARTSYS; =20 mutex_lock(&bt_bmc->mutex); + if (bt_bmc->dead) { + ret =3D -ENODEV; + goto out_unlock; + } =20 if (unlikely(!(bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_H2B_ATN))) { ret =3D -EIO; @@ -259,11 +310,14 @@ static ssize_t bt_bmc_write(struct file *file, const = char __user *buf, * poll */ if (wait_event_interruptible(bt_bmc->queue, - !(bt_inb(bt_bmc, BT_CTRL) & - (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN)))) + bt_bmc_response_ready(bt_bmc))) return -ERESTARTSYS; =20 mutex_lock(&bt_bmc->mutex); + if (bt_bmc->dead) { + ret =3D -ENODEV; + goto out_unlock; + } =20 if (unlikely(bt_inb(bt_bmc, BT_CTRL) & (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN))) { @@ -298,21 +352,35 @@ static long bt_bmc_ioctl(struct file *file, unsigned = int cmd, unsigned long param) { struct bt_bmc *bt_bmc =3D file_bt_bmc(file); + long ret =3D -EINVAL; =20 + mutex_lock(&bt_bmc->mutex); + if (bt_bmc->dead) { + ret =3D -ENODEV; + goto out_unlock; + } switch (cmd) { case BT_BMC_IOCTL_SMS_ATN: set_sms_atn(bt_bmc); - return 0; + ret =3D 0; + break; } - return -EINVAL; + +out_unlock: + mutex_unlock(&bt_bmc->mutex); + return ret; } =20 static int bt_bmc_release(struct inode *inode, struct file *file) { struct bt_bmc *bt_bmc =3D file_bt_bmc(file); =20 + mutex_lock(&bt_bmc->mutex); atomic_dec(&open_count); - set_b_busy(bt_bmc); + if (!bt_bmc->dead) + set_b_busy(bt_bmc); + mutex_unlock(&bt_bmc->mutex); + kref_put(&bt_bmc->refcount, bt_bmc_free); return 0; } =20 @@ -324,6 +392,11 @@ static __poll_t bt_bmc_poll(struct file *file, poll_ta= ble *wait) =20 poll_wait(file, &bt_bmc->queue, wait); =20 + mutex_lock(&bt_bmc->mutex); + if (bt_bmc->dead) { + mutex_unlock(&bt_bmc->mutex); + return EPOLLERR | EPOLLHUP; + } ctrl =3D bt_inb(bt_bmc, BT_CTRL); =20 if (ctrl & BT_CTRL_H2B_ATN) @@ -331,6 +404,7 @@ static __poll_t bt_bmc_poll(struct file *file, poll_tab= le *wait) =20 if (!(ctrl & (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN))) mask |=3D EPOLLOUT; + mutex_unlock(&bt_bmc->mutex); =20 return mask; } @@ -357,16 +431,25 @@ static void poll_timer(struct timer_list *t) static irqreturn_t bt_bmc_irq(int irq, void *arg) { struct bt_bmc *bt_bmc =3D arg; + unsigned long flags; u32 reg; =20 + spin_lock_irqsave(&bt_bmc->state_lock, flags); + if (bt_bmc->dead) { + spin_unlock_irqrestore(&bt_bmc->state_lock, flags); + return IRQ_NONE; + } reg =3D readl(bt_bmc->base + BT_CR2); =20 reg &=3D BT_CR2_IRQ_H2B | BT_CR2_IRQ_HBUSY; - if (!reg) + if (!reg) { + spin_unlock_irqrestore(&bt_bmc->state_lock, flags); return IRQ_NONE; + } =20 /* ack pending IRQs */ writel(reg, bt_bmc->base + BT_CR2); + spin_unlock_irqrestore(&bt_bmc->state_lock, flags); =20 wake_up(&bt_bmc->queue); return IRQ_HANDLED; @@ -413,17 +496,21 @@ static int bt_bmc_probe(struct platform_device *pdev) dev =3D &pdev->dev; dev_info(dev, "Found bt bmc device\n"); =20 - bt_bmc =3D devm_kzalloc(dev, sizeof(*bt_bmc), GFP_KERNEL); + bt_bmc =3D kzalloc_obj(struct bt_bmc); if (!bt_bmc) return -ENOMEM; + kref_init(&bt_bmc->refcount); =20 dev_set_drvdata(&pdev->dev, bt_bmc); =20 bt_bmc->base =3D devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(bt_bmc->base)) - return PTR_ERR(bt_bmc->base); + if (IS_ERR(bt_bmc->base)) { + rc =3D PTR_ERR(bt_bmc->base); + goto err_free; + } =20 mutex_init(&bt_bmc->mutex); + spin_lock_init(&bt_bmc->state_lock); init_waitqueue_head(&bt_bmc->queue); =20 bt_bmc->miscdev.minor =3D MISC_DYNAMIC_MINOR; @@ -433,7 +520,7 @@ static int bt_bmc_probe(struct platform_device *pdev) rc =3D misc_register(&bt_bmc->miscdev); if (rc) { dev_err(dev, "Unable to register misc device\n"); - return rc; + goto err_free; } =20 bt_bmc_config_irq(bt_bmc, pdev); @@ -457,15 +544,31 @@ static int bt_bmc_probe(struct platform_device *pdev) clr_b_busy(bt_bmc); =20 return 0; + +err_free: + dev_set_drvdata(dev, NULL); + kref_put(&bt_bmc->refcount, bt_bmc_free); + return rc; } =20 static void bt_bmc_remove(struct platform_device *pdev) { struct bt_bmc *bt_bmc =3D dev_get_drvdata(&pdev->dev); + unsigned long flags; =20 + mutex_lock(&bt_bmc->mutex); + spin_lock_irqsave(&bt_bmc->state_lock, flags); + bt_bmc->dead =3D true; + spin_unlock_irqrestore(&bt_bmc->state_lock, flags); + mutex_unlock(&bt_bmc->mutex); misc_deregister(&bt_bmc->miscdev); + wake_up_all(&bt_bmc->queue); if (bt_bmc->irq < 0) - timer_delete_sync(&bt_bmc->poll_timer); + timer_shutdown_sync(&bt_bmc->poll_timer); + else + devm_free_irq(&pdev->dev, bt_bmc->irq, bt_bmc); + dev_set_drvdata(&pdev->dev, NULL); + kref_put(&bt_bmc->refcount, bt_bmc_free); } =20 static const struct of_device_id bt_bmc_match[] =3D { --=20 2.53.0