From nobody Sat Oct 3 04:37:45 2026 Received: from mout-p-201.mailbox.org (mout-p-201.mailbox.org [80.241.56.171]) (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 B4627355F46 for ; Wed, 5 Aug 2026 12:33:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785933209; cv=none; b=eGlyb+8hU6McpI0F52D13nNFvuDkSM5IwMSC6MqJzWNx3jUomDgmSvZHchp6BtcEqoJSsCUlW3yd5LmU982D23fch8plv+LSDxqcZavq7D/Q5FYdlAi3B5qWRdZzzVa/zSD8tAmb9CIzpuvzSWE1DffCSVF3wEPxHZqJoR8xt/0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785933209; c=relaxed/simple; bh=KRnob1h9gSVSN38x92Mp3+Q7CKc9wQ73pJpK/IWCx1E=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=Eh0eGKBd0hhUUdsPgPnALvuRf9lZKAvk0qFsxX3tDUNZY44oLcuFjoaQvUUpnHJ82+qXxkyLZjQlX9SD16c8ieVQsiQ6qkI60LJWEWvPJCOgvWpb+1x5VvnYa0uidpzt+dPF5fLIcMUaqXlG2j45b1yCvuffoe9UX2qpNmtsS6c= 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=RHq9iuMT; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b=ES7TCLNO; arc=none smtp.client-ip=80.241.56.171 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="RHq9iuMT"; dkim=pass (2048-bit key) header.d=mailbox.org header.i=@mailbox.org header.b="ES7TCLNO" Received: from smtp1.mailbox.org (smtp1.mailbox.org [10.196.197.1]) (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-201.mailbox.org (Postfix) with ESMTPS id 4hFVFK4YrvzMlKl; Wed, 05 Aug 2026 14:33:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785933205; 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=6D4I8tcqGADshgwuYo4EcFg0oqeNraupU72tCm7Wlww=; b=RHq9iuMTLDGUWSX42QR1qnTWBhvk8FkGK3gXEClhEkA5J5aaXiGAiuyQCf9q0Ckg1ot6iX zCK7PGxt2jt1d/YI+H67MFnzYEURirGc/8wYwy5dGwq6wL2IWRIRWk0ywzO+ULxQcxpmNX lUQy9YPUBdjZumg81clkVLimvA0JYD/GZz7RrFU5rJ/sNHqEcOv4FC4thS1iYOWXKKiFwh ssJ8Wj8ef84DgjTb8BOWw2lY4W7Yu7EhwqWGlZlqlTnLkJgvDbyRENB3+abrVDNYnXqCdb +LFsCRcnvB8Hlc9RpRcg+AejpO/Gosk4nc62mqEbzfy5jUqi/vSsK49wHbkjsw== From: Qing Ming DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mailbox.org; s=mail20150812; t=1785933204; 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=6D4I8tcqGADshgwuYo4EcFg0oqeNraupU72tCm7Wlww=; b=ES7TCLNO9AXTV0TiAo//wan26I7bM71kEMurKZSmgEGGfQe9ULogkPx0+Pvf++1d4TX9XW BgmZeSzPRy7dTX5V491sBatpb4aK3b+O1zq5NAOtvz4rVENTt1dXeQi7qdtIZ1skdLvBSf d+8BxKCc3QwXTs7ER+iowj89ffur8/1/MGEtMydwWedtUyh5dJw/o1eg+X0v1DUCbLsDx5 aW3f50d3dgeqdZ6xSyAuxHU/UrRnv5g0KhpwJgyayziQNH/Y4SH+uNiJXGMjEj72cRBEH6 ispGQbc/hV9e9XMODQyuGL7hBBl6sy8gQYhRSPCh+ED66O5sChHCY5x490yVNA== To: Corey Minyard Cc: Quan Nguyen , openipmi-developer@lists.sourceforge.net, linux-kernel@vger.kernel.org, Qing Ming Subject: [PATCH] ipmi: ssif_bmc: keep device alive across remove Date: Wed, 5 Aug 2026 20:33:04 +0800 Message-ID: <20260805123304.12652-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: he931f9jhjrghwdn4u7gdo78d7t9b7i5 X-MBO-RS-ID: c6ad2cb2ad39b80b48e Content-Type: text/plain; charset="utf-8" misc_deregister() prevents new opens but does not close existing files. ssif_bmc_ctx is allocated with devm_kzalloc(), so I2C device removal releases the object while an open /dev/ipmi-ssif-host file can still reach it through poll and release. Keeping /dev/ipmi-ssif-host open across I2C removal and then polling the old file triggers: BUG: KASAN: slab-use-after-free in do_raw_spin_lock ssif_bmc_poll Allocate ssif_bmc_ctx explicitly and hold a reference for each successful open. Mark the device dead before deregistering the miscdevice, wake blocked operations, report a hangup to poll, and make empty reads and new writes return -ENODEV after removal. Unregister the I2C target before deleting the response timer so a target callback cannot rearm the timer during teardown. If I2C target registration fails after the miscdevice is visible, mark the device dead and deregister it before dropping the probe reference. This keeps any file that raced with the probe failure valid until its release. Fixes: dd2bc5cc9e25 ("ipmi: ssif_bmc: Add SSIF BMC driver") Cc: stable@vger.kernel.org Signed-off-by: Qing Ming --- drivers/char/ipmi/ssif_bmc.c | 77 ++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/drivers/char/ipmi/ssif_bmc.c b/drivers/char/ipmi/ssif_bmc.c index 6036897725f3..84616fa2c8fd 100644 --- a/drivers/char/ipmi/ssif_bmc.c +++ b/drivers/char/ipmi/ssif_bmc.c @@ -7,12 +7,14 @@ */ =20 #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -78,6 +80,7 @@ enum ssif_state { }; =20 struct ssif_bmc_ctx { + struct kref refcount; struct i2c_client *client; struct miscdevice miscdev; int msg_idx; @@ -101,6 +104,7 @@ struct ssif_bmc_ctx { bool response_in_progress; bool busy; bool aborting; + bool dead; /* Buffer for SSIF Transaction part*/ struct ssif_part_buffer part_buf; struct ipmi_ssif_msg response; @@ -109,7 +113,15 @@ struct ssif_bmc_ctx { =20 static inline struct ssif_bmc_ctx *to_ssif_bmc(struct file *file) { - return container_of(file->private_data, struct ssif_bmc_ctx, miscdev); + return file->private_data; +} + +static void ssif_bmc_free(struct kref *refcount) +{ + struct ssif_bmc_ctx *ssif_bmc; + + ssif_bmc =3D container_of(refcount, struct ssif_bmc_ctx, refcount); + kfree(ssif_bmc); } =20 static const char *state_to_string(enum ssif_state state) @@ -142,10 +154,15 @@ static ssize_t ssif_bmc_read(struct file *file, char = __user *buf, size_t count, =20 spin_lock_irqsave(&ssif_bmc->lock, flags); while (!ssif_bmc->request_available) { + if (ssif_bmc->dead) { + spin_unlock_irqrestore(&ssif_bmc->lock, flags); + return -ENODEV; + } spin_unlock_irqrestore(&ssif_bmc->lock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; ret =3D wait_event_interruptible(ssif_bmc->wait_queue, + READ_ONCE(ssif_bmc->dead) || ssif_bmc->request_available); if (ret) return ret; @@ -195,15 +212,24 @@ static ssize_t ssif_bmc_write(struct file *file, cons= t char __user *buf, size_t =20 spin_lock_irqsave(&ssif_bmc->lock, flags); while (ssif_bmc->response_in_progress) { + if (ssif_bmc->dead) { + ret =3D -ENODEV; + goto exit; + } spin_unlock_irqrestore(&ssif_bmc->lock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; ret =3D wait_event_interruptible(ssif_bmc->wait_queue, + READ_ONCE(ssif_bmc->dead) || !ssif_bmc->response_in_progress); if (ret) return ret; spin_lock_irqsave(&ssif_bmc->lock, flags); } + if (ssif_bmc->dead) { + ret =3D -ENODEV; + goto exit; + } =20 /* * The write must complete before the response timeout fired, otherwise @@ -235,14 +261,21 @@ static ssize_t ssif_bmc_write(struct file *file, cons= t char __user *buf, size_t =20 static int ssif_bmc_open(struct inode *inode, struct file *file) { - struct ssif_bmc_ctx *ssif_bmc =3D to_ssif_bmc(file); + struct ssif_bmc_ctx *ssif_bmc; int ret =3D 0; =20 + ssif_bmc =3D container_of(file->private_data, struct ssif_bmc_ctx, + miscdev); spin_lock_irq(&ssif_bmc->lock); - if (!ssif_bmc->running) + if (ssif_bmc->dead) { + ret =3D -ENODEV; + } else if (!ssif_bmc->running) { ssif_bmc->running =3D 1; - else + kref_get(&ssif_bmc->refcount); + file->private_data =3D ssif_bmc; + } else { ret =3D -EBUSY; + } spin_unlock_irq(&ssif_bmc->lock); =20 return ret; @@ -259,6 +292,8 @@ static __poll_t ssif_bmc_poll(struct file *file, poll_t= able *wait) /* The request is available, userspace application can get the request */ if (ssif_bmc->request_available) mask |=3D EPOLLIN; + if (ssif_bmc->dead) + mask |=3D EPOLLERR | EPOLLHUP; =20 spin_unlock_irq(&ssif_bmc->lock); =20 @@ -272,6 +307,7 @@ static int ssif_bmc_release(struct inode *inode, struct= file *file) spin_lock_irq(&ssif_bmc->lock); ssif_bmc->running =3D 0; spin_unlock_irq(&ssif_bmc->lock); + kref_put(&ssif_bmc->refcount, ssif_bmc_free); =20 return 0; } @@ -825,9 +861,10 @@ static int ssif_bmc_probe(struct i2c_client *client) struct ssif_bmc_ctx *ssif_bmc; int ret; =20 - ssif_bmc =3D devm_kzalloc(&client->dev, sizeof(*ssif_bmc), GFP_KERNEL); + ssif_bmc =3D kzalloc_obj(struct ssif_bmc_ctx); if (!ssif_bmc) return -ENOMEM; + kref_init(&ssif_bmc->refcount); =20 spin_lock_init(&ssif_bmc->lock); =20 @@ -837,6 +874,8 @@ static int ssif_bmc_probe(struct i2c_client *client) ssif_bmc->busy =3D false; ssif_bmc->response_timer_inited =3D false; =20 + ssif_bmc->client =3D client; + ssif_bmc->client->flags |=3D I2C_CLIENT_SLAVE; /* Register misc device interface */ ssif_bmc->miscdev.minor =3D MISC_DYNAMIC_MINOR; ssif_bmc->miscdev.name =3D DEVICE_NAME; @@ -844,17 +883,25 @@ static int ssif_bmc_probe(struct i2c_client *client) ssif_bmc->miscdev.parent =3D &client->dev; ret =3D misc_register(&ssif_bmc->miscdev); if (ret) - return ret; - - ssif_bmc->client =3D client; - ssif_bmc->client->flags |=3D I2C_CLIENT_SLAVE; + goto err_put; =20 /* Register I2C slave */ i2c_set_clientdata(client, ssif_bmc); ret =3D i2c_slave_register(client, ssif_bmc_cb); if (ret) - misc_deregister(&ssif_bmc->miscdev); + goto err_misc; + + return 0; =20 +err_misc: + spin_lock_irq(&ssif_bmc->lock); + ssif_bmc->dead =3D true; + spin_unlock_irq(&ssif_bmc->lock); + misc_deregister(&ssif_bmc->miscdev); + wake_up_all(&ssif_bmc->wait_queue); + i2c_set_clientdata(client, NULL); +err_put: + kref_put(&ssif_bmc->refcount, ssif_bmc_free); return ret; } =20 @@ -862,9 +909,15 @@ static void ssif_bmc_remove(struct i2c_client *client) { struct ssif_bmc_ctx *ssif_bmc =3D i2c_get_clientdata(client); =20 - timer_delete_sync(&ssif_bmc->response_timer); - i2c_slave_unregister(client); + spin_lock_irq(&ssif_bmc->lock); + ssif_bmc->dead =3D true; + spin_unlock_irq(&ssif_bmc->lock); misc_deregister(&ssif_bmc->miscdev); + wake_up_all(&ssif_bmc->wait_queue); + i2c_slave_unregister(client); + timer_delete_sync(&ssif_bmc->response_timer); + i2c_set_clientdata(client, NULL); + kref_put(&ssif_bmc->refcount, ssif_bmc_free); } =20 static const struct of_device_id ssif_bmc_match[] =3D { --=20 2.53.0