The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH] i2c: i801: fix hardware state machine corruption in error path
@ 2026-05-07 11:43 w15303746062
  2026-05-12  8:38 ` Jean Delvare
  0 siblings, 1 reply; 2+ messages in thread
From: w15303746062 @ 2026-05-07 11:43 UTC (permalink / raw)
  To: jdelvare, andi.shyti; +Cc: linux-i2c, linux-kernel, Mingyu Wang

From: Mingyu Wang <25181214217@stu.xidian.edu.cn>

A severe livelock and subsequent Hung Task panic were observed in the
i2c-i801 driver during concurrent Fuzzing. The crash is caused by an
unconditional hardware register cleanup in the error handling path of
i801_access().

When i801_check_pre() fails (e.g., returning -EBUSY because the SMBus
controller is actively used by BIOS/ACPI or another thread), the kernel
does not actually acquire the hardware ownership. However, the code jumps
to the 'out' label and executes:

    iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv));

This forcefully clears the INUSE_STS lock and resets the hardware status
flags without owning the controller. Doing so interrupts ongoing BIOS/ACPI
transactions and totally corrupts the SMBus hardware state machine.

Consequently, all subsequent i801_access() calls fail at the pre-check
stage, triggering an endless stream of "SMBus is busy, can't use it!"
error logs. Over a slow serial console, this printk flood monopolizes
the CPU (Console Livelock), starving other processes trying to acquire
the mmap_lock down_read semaphore, ultimately triggering the hung task
watchdog.

Fix this by introducing an 'out_err' label. If i801_check_pre() fails,
we safely bypass the hardware register cleanup and only release the
software locks (pm_runtime and mutex), strictly adhering to the rule of
not releasing resources that were never acquired.

Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>
---
 drivers/i2c/busses/i2c-i801.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 32a3cef02c7b..068b9ffb234f 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -905,7 +905,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
 
 	ret = i801_check_pre(priv);
 	if (ret)
-		goto out;
+		goto out_err;
 
 	hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
 		&& size != I2C_SMBUS_QUICK
@@ -938,6 +938,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
 	 */
 	iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv));
 
+out_err:
 	pm_runtime_put_autosuspend(&priv->pci_dev->dev);
 	mutex_unlock(&priv->acpi_lock);
 	return ret;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] i2c: i801: fix hardware state machine corruption in error path
  2026-05-07 11:43 [PATCH] i2c: i801: fix hardware state machine corruption in error path w15303746062
@ 2026-05-12  8:38 ` Jean Delvare
  0 siblings, 0 replies; 2+ messages in thread
From: Jean Delvare @ 2026-05-12  8:38 UTC (permalink / raw)
  To: w15303746062; +Cc: andi.shyti, linux-i2c, linux-kernel, Mingyu Wang

Hi Wang,

On Thu,  7 May 2026 19:43:56 +0800, w15303746062@163.com wrote:
> From: Mingyu Wang <25181214217@stu.xidian.edu.cn>
> 
> A severe livelock and subsequent Hung Task panic were observed in the
> i2c-i801 driver during concurrent Fuzzing. The crash is caused by an
> unconditional hardware register cleanup in the error handling path of
> i801_access().
> 
> When i801_check_pre() fails (e.g., returning -EBUSY because the SMBus
> controller is actively used by BIOS/ACPI or another thread), the kernel

This can't be "another thread", as calls to i801_access() are
serialized.

> does not actually acquire the hardware ownership. However, the code jumps
> to the 'out' label and executes:
> 
>     iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv));
> 
> This forcefully clears the INUSE_STS lock and resets the hardware status
> flags without owning the controller. Doing so interrupts ongoing BIOS/ACPI
> transactions and totally corrupts the SMBus hardware state machine.
> 
> Consequently, all subsequent i801_access() calls fail at the pre-check
> stage, triggering an endless stream of "SMBus is busy, can't use it!"
> error logs. Over a slow serial console, this printk flood monopolizes
> the CPU (Console Livelock), starving other processes trying to acquire
> the mmap_lock down_read semaphore, ultimately triggering the hung task
> watchdog.

Analysis looks good. We indeed should not write to a register if we do
not own the device.

> Fix this by introducing an 'out_err' label. If i801_check_pre() fails,
> we safely bypass the hardware register cleanup and only release the
> software locks (pm_runtime and mutex), strictly adhering to the rule of
> not releasing resources that were never acquired.

This fix introduces a build-time warning:

drivers/i2c/busses/i2c-i801.c: In function ‘i801_access’:
drivers/i2c/busses/i2c-i801.c:930:1: warning: label ‘out’ defined but not used [-Wunused-label]
  930 | out:
      | ^~~
drivers/i2c/busses/i2c-i801.c:930:1: warning: unused label 'out'

Am I missing another driver change? Or did you not test-build your
patch?

There's only one goto in i801_access(), so you don't need 2 labels.
Instead of introducing a new label you should move the existing one.

> Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>

I looked into the driver history to try and figure out when the bug was
introduced.

The label you are moving was re-introduced when the call to
i801_check_pre() was moved to i801_access() in v6.3 by:

commit 1f760b87e54cf56a25ab68f8dc625e339f6e46d5
Author: Heiner Kallweit
Date:   Thu Feb 16 17:14:51 2023 +0100

    i2c: i801: Call i801_check_pre() from i801_access()

However it existed already in earlier driver versions, until
a3989dc0b059 ("i2c: i801: Centralize configuring block commands in
i801_block_transaction") also in v6.3. As i801_check_pre() was called
later back then, we already wrote to device registers many times before
the check, so fixing this bug in older versions of the driver would be
much harder.

So I think your fix should be backported to stable branches v6.3+, and
if anyone wants the fix in an older kernel, they will have to backport
1f760b87e54c ("i2c: i801: Call i801_check_pre() from i801_access()")
first.

The first relevant commit in the driver history seems to be:

ommit 065b6211a87746e196b56759a70c7851418dd741
Author: Heiner Kallweit
Date:   Sun Jun 6 15:55:55 2021 +0200

    i2c: i801: Ensure that SMBHSTSTS_INUSE_STS is cleared when leaving i801_access

Before that, we did not touch SMBHSTSTS at the end of i801_access(), so
the fix does not apply (although the bug was present in another form, as
we were writing to device registers before the busy check back then).

The status flag clearing was added by:

commit 4f7275fc7e570dfc46f733ff8ae131cb128a4758
Author: Heiner Kallweit
Date:   Sat Dec 4 21:04:40 2021 +0100

    i2c: i801: Don't clear status flags twice in interrupt mode

but then again it's only moving the execution around, not introducing
the bug.

Anyway, this was for information only, I do not think the issue is
worth fixing in older kernels where the driver code is very different.
Concurrent access to the SMBus controller by the native Linux driver
and the firmware was best effort originally and we are well aware that
it was not 100% reliable back then. Anyone who cares about this
scenario would have to use a newer version of the i2c-i801 driver.

> ---
>  drivers/i2c/busses/i2c-i801.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
> index 32a3cef02c7b..068b9ffb234f 100644
> --- a/drivers/i2c/busses/i2c-i801.c
> +++ b/drivers/i2c/busses/i2c-i801.c
> @@ -905,7 +905,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>  
>  	ret = i801_check_pre(priv);
>  	if (ret)
> -		goto out;
> +		goto out_err;
>  
>  	hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
>  		&& size != I2C_SMBUS_QUICK
> @@ -938,6 +938,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>  	 */
>  	iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv));
>  
> +out_err:
>  	pm_runtime_put_autosuspend(&priv->pci_dev->dev);
>  	mutex_unlock(&priv->acpi_lock);
>  	return ret;


-- 
Jean Delvare
SUSE L3 Support

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-05-12  8:38 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 11:43 [PATCH] i2c: i801: fix hardware state machine corruption in error path w15303746062
2026-05-12  8:38 ` Jean Delvare

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox