From nobody Fri Oct 2 04:27:48 2026 Received: from mout-p-103.mailbox.org (mout-p-103.mailbox.org [80.241.56.161]) (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 561A94399C4 for ; Wed, 5 Aug 2026 11:48:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.161 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785930490; cv=none; b=L20KE6xv54Izj3shnSD1UEyxXfeFzm6iKQfV8NJSdkVI0aTmOWlCpHCHp1LLuRWlAXdiYW6JPi1VKCJ4ejpbTKSUBb6WHrVnDeBPvC+aj1MBn2/xuFHONBeMU8xzAV2J1oTDnx2PEoH13FEt0U+lsaJ+4Y1muIITYgfE5NIq9NE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785930490; c=relaxed/simple; bh=1gJf2lFd80Nj9pb252CalL3hqoV0imm+z9mSBbBJdDU=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=jrpw3J7R4p2xmdBaayvC9AvUx7s6zgxIvNPGRY/3dWgoaTJBVCyOlTvlzwn5smPa1q0uSbmC202rrePKbKZPFZuE2rO3Ov2HvkjnTcEUhUC7xx/XqQ8x1MO5s3VdDUOWXEmn2OnJMCFAIkWA4OEwFBrlkpXduEtXNoYlevsqh84= 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=N3K46WR6; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b=lqIfOQeB; arc=none smtp.client-ip=80.241.56.161 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="N3K46WR6"; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b="lqIfOQeB" 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-103.mailbox.org (Postfix) with ESMTPS id 4hFTF214grzKnPH; Wed, 05 Aug 2026 13:48:06 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785930486; 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=okQDqHUq1y9Af7kSUIwUnADtDHoS+SyrhpIlx1Ne9Sw=; b=N3K46WR6Cs7CwyfdzKmQLahpg3BFOVsCw/HWlpIOVkjcAleoPqNVgqJiqJWhWkKOT1oW8k AyBT4mr6DXr4lyG9iOn3PvbikbqYNP35AfrxHTlNoxv7AkA67wf4kMDzWWcIfksheuTJfY UOXgK93VXINEqvDx9BCW5L1RHCr1ZHs5RMJsZnMUZ6xhFs7P5ihiBGziopragrZiRz8dgd tlernC8Qjv2CDofrk/WIwDPsyEBOVy+A7H0TX/P8Q4x2Yr5PeIITIdUjFquqMoOBUB0Uz6 QwsfaldCJ2yYYMePlvuWYDE5E2ROZR39hUK71AeDwBNM5/L2zbJI/A01uuf43A== From: Qing Ming DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785930484; 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=okQDqHUq1y9Af7kSUIwUnADtDHoS+SyrhpIlx1Ne9Sw=; b=lqIfOQeBTez/QJG9+1lSyfpKnf7kz1UWMIVHn1rbURmHUxb4EP7mI8G3sbZsGAykhLQ/nv wvi/PNJd6r5wpsgbGYK4q/r9UuaQlhwzZUoJZeTX2c1fHIEz/CwwLAuOBMVPQGj4EEcBjI 3XtAIIlodGiXQorPsCzm9/scuu2q+xGiZdyPRC9f7Q9DAuYl2QAUcY2HXUEujw1PSuk194 mepdX585xMLWSeYcu+yQYHyjZ7eUtl2db1ukI858GjFdHz9mkugW5EZ1wdVXnhLbq0mA48 aNu4sGw3CAN+/qbKlKlp1Wnr3HEXfUWzPlmFvQH5tJHEv4Q20tsf4otb0c9coA== To: Corey Minyard Cc: Asmaa Mnebhi , vadimp@mellanox.com, openipmi-developer@lists.sourceforge.net, linux-kernel@vger.kernel.org, Qing Ming Subject: [PATCH] ipmi: ipmb: keep device alive across remove Date: Wed, 5 Aug 2026 19:47:35 +0800 Message-ID: <20260805114735.8123-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-META: a8a88fbtzkmwrwnebq8jkkc44rfiuayf X-MBO-RS-ID: 1eb1aed6dad27186821 Content-Type: text/plain; charset="utf-8" misc_deregister() prevents new opens but does not close existing files. ipmb_dev is allocated with devm_kzalloc(), so I2C device removal releases the object while an open /dev/ipmb-* file can still reach it through its file operations. Keeping /dev/ipmb-0 open across I2C removal and then polling the old file triggers: BUG: KASAN: slab-use-after-free in mutex_lock ipmb_poll Allocate ipmb_dev explicitly and hold a reference for each open file. Mark the device dead before deregistering the miscdevice, wake blocked readers, report a hangup to poll, and reject writes after removal. Serialize writes with removal so the I2C client is not used after it has been unregistered. Release queued requests and the device object after the last reference is dropped. Register the I2C target before exposing the miscdevice so a late probe failure cannot race an open file. Fixes: 51bd6f291583 ("Add support for IPMB driver") Cc: stable@vger.kernel.org Signed-off-by: Qing Ming --- drivers/char/ipmi/ipmb_dev_int.c | 121 +++++++++++++++++++++++++------ 1 file changed, 100 insertions(+), 21 deletions(-) diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_= int.c index e4c50d9ae3e1..7f63fa002f18 100644 --- a/drivers/char/ipmi/ipmb_dev_int.c +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ struct ipmb_request_elem { }; =20 struct ipmb_dev { + struct kref refcount; struct i2c_client *client; struct miscdevice miscdev; struct ipmb_msg request; @@ -64,11 +66,26 @@ struct ipmb_dev { wait_queue_head_t wait_queue; struct mutex file_mutex; bool is_i2c_protocol; + bool dead; }; =20 static inline struct ipmb_dev *to_ipmb_dev(struct file *file) { - return container_of(file->private_data, struct ipmb_dev, miscdev); + return file->private_data; +} + +static void ipmb_dev_release(struct kref *refcount) +{ + struct ipmb_dev *ipmb_dev; + struct ipmb_request_elem *elem, *tmp; + + ipmb_dev =3D container_of(refcount, struct ipmb_dev, refcount); + list_for_each_entry_safe(elem, tmp, &ipmb_dev->request_queue, list) { + list_del(&elem->list); + kfree(elem); + } + kfree(ipmb_dev->miscdev.name); + kfree(ipmb_dev); } =20 static ssize_t ipmb_read(struct file *file, char __user *buf, size_t count, @@ -84,12 +101,17 @@ static ssize_t ipmb_read(struct file *file, char __use= r *buf, size_t count, spin_lock_irq(&ipmb_dev->lock); =20 while (list_empty(&ipmb_dev->request_queue)) { + if (READ_ONCE(ipmb_dev->dead)) { + spin_unlock_irq(&ipmb_dev->lock); + return -ENODEV; + } spin_unlock_irq(&ipmb_dev->lock); =20 if (file->f_flags & O_NONBLOCK) return -EAGAIN; =20 ret =3D wait_event_interruptible(ipmb_dev->wait_queue, + READ_ONCE(ipmb_dev->dead) || !list_empty(&ipmb_dev->request_queue)); if (ret) return ret; @@ -150,6 +172,12 @@ static ssize_t ipmb_write(struct file *file, const cha= r __user *buf, if (msg[IPMB_MSG_LEN_IDX] < IPMB_REQUEST_LEN_MIN || count < (size_t)msg[IPMB_MSG_LEN_IDX] + 1) return -EINVAL; + if (mutex_lock_interruptible(&ipmb_dev->file_mutex)) + return -ERESTARTSYS; + if (ipmb_dev->dead) { + ret =3D -ENODEV; + goto out_unlock; + } =20 rq_sa =3D GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]); netf_rq_lun =3D msg[NETFN_LUN_IDX]; @@ -157,7 +185,8 @@ static ssize_t ipmb_write(struct file *file, const char= __user *buf, /* Check i2c block transfer vs smbus */ if (ipmb_dev->is_i2c_protocol) { ret =3D ipmb_i2c_write(ipmb_dev->client, msg, rq_sa); - return (ret =3D=3D 1) ? count : ret; + ret =3D (ret =3D=3D 1) ? count : ret; + goto out_unlock; } =20 /* @@ -166,8 +195,10 @@ static ssize_t ipmb_write(struct file *file, const cha= r __user *buf, */ msg_len =3D msg[IPMB_MSG_LEN_IDX] - SMBUS_MSG_HEADER_LENGTH; temp_client =3D kmemdup(ipmb_dev->client, sizeof(*temp_client), GFP_KERNE= L); - if (!temp_client) - return -ENOMEM; + if (!temp_client) { + ret =3D -ENOMEM; + goto out_unlock; + } =20 temp_client->addr =3D rq_sa; =20 @@ -175,7 +206,11 @@ static ssize_t ipmb_write(struct file *file, const cha= r __user *buf, msg + SMBUS_MSG_IDX_OFFSET); kfree(temp_client); =20 - return ret < 0 ? ret : count; + ret =3D ret < 0 ? ret : count; + +out_unlock: + mutex_unlock(&ipmb_dev->file_mutex); + return ret; } =20 static __poll_t ipmb_poll(struct file *file, poll_table *wait) @@ -184,6 +219,10 @@ static __poll_t ipmb_poll(struct file *file, poll_tabl= e *wait) __poll_t mask =3D EPOLLOUT; =20 mutex_lock(&ipmb_dev->file_mutex); + if (ipmb_dev->dead) { + mutex_unlock(&ipmb_dev->file_mutex); + return EPOLLERR | EPOLLHUP; + } poll_wait(file, &ipmb_dev->wait_queue, wait); =20 if (atomic_read(&ipmb_dev->request_queue_len)) @@ -193,11 +232,38 @@ static __poll_t ipmb_poll(struct file *file, poll_tab= le *wait) return mask; } =20 +static int ipmb_open(struct inode *inode, struct file *file) +{ + struct ipmb_dev *ipmb_dev; + int ret =3D 0; + + ipmb_dev =3D container_of(file->private_data, struct ipmb_dev, miscdev); + mutex_lock(&ipmb_dev->file_mutex); + if (ipmb_dev->dead) { + ret =3D -ENODEV; + } else { + kref_get(&ipmb_dev->refcount); + file->private_data =3D ipmb_dev; + } + mutex_unlock(&ipmb_dev->file_mutex); + return ret; +} + +static int ipmb_release(struct inode *inode, struct file *file) +{ + struct ipmb_dev *ipmb_dev =3D to_ipmb_dev(file); + + kref_put(&ipmb_dev->refcount, ipmb_dev_release); + return 0; +} + static const struct file_operations ipmb_fops =3D { .owner =3D THIS_MODULE, + .open =3D ipmb_open, .read =3D ipmb_read, .write =3D ipmb_write, .poll =3D ipmb_poll, + .release =3D ipmb_release, }; =20 /* Called with ipmb_dev->lock held. */ @@ -305,10 +371,10 @@ static int ipmb_probe(struct i2c_client *client) struct ipmb_dev *ipmb_dev; int ret; =20 - ipmb_dev =3D devm_kzalloc(&client->dev, sizeof(*ipmb_dev), - GFP_KERNEL); + ipmb_dev =3D kzalloc_obj(struct ipmb_dev); if (!ipmb_dev) return -ENOMEM; + kref_init(&ipmb_dev->refcount); =20 spin_lock_init(&ipmb_dev->lock); init_waitqueue_head(&ipmb_dev->wait_queue); @@ -319,38 +385,51 @@ static int ipmb_probe(struct i2c_client *client) =20 ipmb_dev->miscdev.minor =3D MISC_DYNAMIC_MINOR; =20 - ipmb_dev->miscdev.name =3D devm_kasprintf(&client->dev, GFP_KERNEL, - "%s%d", "ipmb-", - client->adapter->nr); - if (!ipmb_dev->miscdev.name) - return -ENOMEM; + ipmb_dev->miscdev.name =3D kasprintf(GFP_KERNEL, "%s%d", "ipmb-", + client->adapter->nr); + if (!ipmb_dev->miscdev.name) { + ret =3D -ENOMEM; + goto err_put; + } =20 ipmb_dev->miscdev.fops =3D &ipmb_fops; ipmb_dev->miscdev.parent =3D &client->dev; - ret =3D misc_register(&ipmb_dev->miscdev); - if (ret) - return ret; - ipmb_dev->is_i2c_protocol =3D device_property_read_bool(&client->dev, "i2c-protocol"); =20 ipmb_dev->client =3D client; i2c_set_clientdata(client, ipmb_dev); ret =3D i2c_slave_register(client, ipmb_slave_cb); - if (ret) { - misc_deregister(&ipmb_dev->miscdev); - return ret; - } + if (ret) + goto err_data; + + ret =3D misc_register(&ipmb_dev->miscdev); + if (ret) + goto err_slave; =20 return 0; + +err_slave: + i2c_slave_unregister(client); +err_data: + i2c_set_clientdata(client, NULL); +err_put: + kref_put(&ipmb_dev->refcount, ipmb_dev_release); + return ret; } =20 static void ipmb_remove(struct i2c_client *client) { struct ipmb_dev *ipmb_dev =3D i2c_get_clientdata(client); =20 - i2c_slave_unregister(client); + mutex_lock(&ipmb_dev->file_mutex); + WRITE_ONCE(ipmb_dev->dead, true); + mutex_unlock(&ipmb_dev->file_mutex); misc_deregister(&ipmb_dev->miscdev); + wake_up_all(&ipmb_dev->wait_queue); + i2c_slave_unregister(client); + i2c_set_clientdata(client, NULL); + kref_put(&ipmb_dev->refcount, ipmb_dev_release); } =20 static const struct i2c_device_id ipmb_id[] =3D { --=20 2.53.0