mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/3] hwmon: fix jiffies wraparound in one-shot ready checks
@ 2026-09-25 18:23 Tom Verdonck
  2026-09-25 18:23 ` [PATCH 1/3] hwmon: (tmp102) Fix jiffies wraparound in conversion-ready check Tom Verdonck
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Tom Verdonck @ 2026-09-25 18:23 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Tom Verdonck

Several hwmon drivers store a one-shot jiffies deadline that is set once
and never refreshed, then compare it against the current time with
time_before() on every access. time_before() only interprets the signed
difference of two jiffies values correctly while they are within
LONG_MAX jiffies of each other. Because the deadline is frozen while
jiffies keeps advancing, the difference eventually flips sign: after
2^31 jiffies (~248.5 days of uptime on a 32-bit HZ=100 kernel) the check
inverts and stays wrong for the next ~248.5 days.

The consequences differ per driver:

  - tmp102, tmp108: every temperature read then returns -EAGAIN without
    ever touching the sensor, until the machine is rebooted. This has
    been observed in the field on 32-bit systems that had been up for
    ~248 days.
  - sht4x: the read path instead concludes the heater is still active
    and calls msleep() with a bogus, huge delta, blocking the read for a
    very long time.

Each is fixed the same minimal way: store the deadline in a u64 jiffies
value and compare it with get_jiffies_64()/time_before64(), which does
not wrap in any realistic uptime. No functional change on the hot path
other than removing the false-positive after the wrap point.

The other hwmon time_before(jiffies, ...) users I looked at are not
affected: they either re-stamp last_updated on every update (the usual
cache pattern, e.g. tmp421, tmp464, tc654, g762, ibmpex, w83792d) or use
a freshly computed local timeout (pmbus/ltc2978), so their two operands
never drift apart.

The failure itself takes ~248 days of uptime to reproduce and follows
directly from the time_before() semantics; it was traced from a field
report of a 32-bit board stuck returning -EAGAIN after ~248 days.

Build-tested on x86-64 and cross-compiled for 32-bit ARM (Cortex-A7,
arm-linux-gnueabi) with make W=1; all three objects build cleanly with
no new warnings. 32-bit ARM is the configuration in which the bug
actually manifests.

Tom Verdonck (3):
  hwmon: (tmp102) Fix jiffies wraparound in conversion-ready check
  hwmon: (tmp108) Fix jiffies wraparound in conversion-ready check
  hwmon: (sht4x) Fix jiffies wraparound in heater-ready check

 drivers/hwmon/sht4x.c  | 18 +++++++++---------
 drivers/hwmon/tmp102.c |  8 ++++----
 drivers/hwmon/tmp108.c |  8 ++++----
 3 files changed, 17 insertions(+), 17 deletions(-)

-- 
2.53.0


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

* [PATCH 1/3] hwmon: (tmp102) Fix jiffies wraparound in conversion-ready check
  2026-09-25 18:23 [PATCH 0/3] hwmon: fix jiffies wraparound in one-shot ready checks Tom Verdonck
@ 2026-09-25 18:23 ` Tom Verdonck
  2026-09-26  3:25   ` Guenter Roeck
  2026-09-25 18:23 ` [PATCH 2/3] hwmon: (tmp108) " Tom Verdonck
  2026-09-25 18:23 ` [PATCH 3/3] hwmon: (sht4x) Fix jiffies wraparound in heater-ready check Tom Verdonck
  2 siblings, 1 reply; 7+ messages in thread
From: Tom Verdonck @ 2026-09-25 18:23 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-hwmon, linux-kernel, Tom Verdonck, stable, Nishanth Menon

tmp102 records a one-shot deadline in ->ready_time at probe (and resume)
and, on every temperature read, refuses the read with -EAGAIN while
time_before(jiffies, ready_time) is true, in order to skip the first
~35 ms conversion.

->ready_time is an unsigned long compared with time_before(), whose
signed difference is only meaningful while the two values are within
LONG_MAX jiffies of each other. Because ->ready_time is set once and
never refreshed, jiffies keeps advancing away from it, and after 2^31
jiffies the difference flips sign. On a 32-bit HZ=100 kernel that
happens ~248.5 days after boot: time_before() then permanently reports
"not ready" and the driver returns -EAGAIN on every read, without ever
touching the sensor, until the next reboot.

Store the deadline as a 64-bit jiffies value and compare it with
get_jiffies_64()/time_before64(), which does not wrap in any practical
uptime.

Fixes: 3d8f7a89a197 ("hwmon: (tmp102) Improve handling of initial read delay")
Cc: stable@vger.kernel.org
Signed-off-by: Tom Verdonck <tom.verdonck@inspiral.be>
---
 drivers/hwmon/tmp102.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 50a8b050d8da..8c8463ddb6c4 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -58,7 +58,7 @@ struct tmp102 {
 	const char *label;
 	struct regmap *regmap;
 	u16 config_orig;
-	unsigned long ready_time;
+	u64 ready_time;
 	u16 sample_time;
 };
 
@@ -106,7 +106,7 @@ static int tmp102_read_temp(struct device *dev, u32 attr, long *val)
 	switch (attr) {
 	case hwmon_temp_input:
 		/* Is it too early to return a conversion ? */
-		if (time_before(jiffies, tmp102->ready_time)) {
+		if (time_before64(get_jiffies_64(), tmp102->ready_time)) {
 			dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__);
 			return -EAGAIN;
 		}
@@ -354,7 +354,7 @@ static int tmp102_probe(struct i2c_client *client)
 	 * Mark that we are not ready with data until the first
 	 * conversion is complete
 	 */
-	tmp102->ready_time = jiffies + msecs_to_jiffies(CONVERSION_TIME_MS);
+	tmp102->ready_time = get_jiffies_64() + msecs_to_jiffies(CONVERSION_TIME_MS);
 
 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
 							 tmp102,
@@ -387,7 +387,7 @@ static int tmp102_resume(struct device *dev)
 	err = regmap_update_bits(tmp102->regmap, TMP102_CONF_REG,
 				 TMP102_CONF_SD, 0);
 
-	tmp102->ready_time = jiffies + msecs_to_jiffies(CONVERSION_TIME_MS);
+	tmp102->ready_time = get_jiffies_64() + msecs_to_jiffies(CONVERSION_TIME_MS);
 
 	return err;
 }
-- 
2.53.0


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

* [PATCH 2/3] hwmon: (tmp108) Fix jiffies wraparound in conversion-ready check
  2026-09-25 18:23 [PATCH 0/3] hwmon: fix jiffies wraparound in one-shot ready checks Tom Verdonck
  2026-09-25 18:23 ` [PATCH 1/3] hwmon: (tmp102) Fix jiffies wraparound in conversion-ready check Tom Verdonck
@ 2026-09-25 18:23 ` Tom Verdonck
  2026-09-26  3:26   ` Guenter Roeck
  2026-09-25 18:23 ` [PATCH 3/3] hwmon: (sht4x) Fix jiffies wraparound in heater-ready check Tom Verdonck
  2 siblings, 1 reply; 7+ messages in thread
From: Tom Verdonck @ 2026-09-25 18:23 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Tom Verdonck, stable, John Muir

tmp108 records a one-shot deadline in ->ready_time at probe (and resume)
and, on every temperature read, refuses the read with -EAGAIN while
time_before(jiffies, ready_time) is true, in order to skip the initial
conversion.

->ready_time is an unsigned long compared with time_before(), whose
signed difference is only meaningful while the two values are within
LONG_MAX jiffies of each other. Because ->ready_time is set once and
never refreshed, jiffies keeps advancing away from it, and after 2^31
jiffies the difference flips sign. On a 32-bit HZ=100 kernel that
happens ~248.5 days after boot: time_before() then permanently reports
"not ready" and the driver returns -EAGAIN on every read, without ever
touching the sensor, until the next reboot. The continuous-mode path,
which sets ->ready_time to jiffies with no added conversion delay, wraps
the same way.

Store the deadline as a 64-bit jiffies value and compare it with
get_jiffies_64()/time_before64(), which does not wrap in any practical
uptime.

Fixes: 66e1c9171339 ("hwmon: Add Texas Instruments TMP108 temperature sensor driver.")
Cc: stable@vger.kernel.org
Signed-off-by: Tom Verdonck <tom.verdonck@inspiral.be>
---
 drivers/hwmon/tmp108.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c
index 9fa31bd66ff6..91d20a1ce112 100644
--- a/drivers/hwmon/tmp108.c
+++ b/drivers/hwmon/tmp108.c
@@ -78,7 +78,7 @@
 struct tmp108 {
 	struct regmap *regmap;
 	u16 orig_config;
-	unsigned long ready_time;
+	u64 ready_time;
 	const struct tmp108_params *params;
 };
 
@@ -138,7 +138,7 @@ static int tmp108_read(struct device *dev, enum hwmon_sensor_types type,
 	switch (attr) {
 	case hwmon_temp_input:
 		/* Is it too early to return a conversion ? */
-		if (time_before(jiffies, tmp108->ready_time)) {
+		if (time_before64(get_jiffies_64(), tmp108->ready_time)) {
 			dev_dbg(dev, "%s: Conversion not ready yet..\n",
 				__func__);
 			return -EAGAIN;
@@ -477,7 +477,7 @@ static int tmp108_common_probe(struct device *dev, struct regmap *regmap, char *
 		return err;
 	}
 
-	tmp108->ready_time = jiffies;
+	tmp108->ready_time = get_jiffies_64();
 	if ((tmp108->orig_config & TMP108_CONF_MODE_MASK) ==
 	    TMP108_MODE_SHUTDOWN)
 		tmp108->ready_time +=
@@ -528,7 +528,7 @@ static int tmp108_resume(struct device *dev)
 
 	err = regmap_update_bits(tmp108->regmap, TMP108_REG_CONF,
 				 TMP108_CONF_MODE_MASK, TMP108_MODE_CONTINUOUS);
-	tmp108->ready_time = jiffies +
+	tmp108->ready_time = get_jiffies_64() +
 			     msecs_to_jiffies(TMP108_CONVERSION_TIME_MS);
 	return err;
 }
-- 
2.53.0


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

* [PATCH 3/3] hwmon: (sht4x) Fix jiffies wraparound in heater-ready check
  2026-09-25 18:23 [PATCH 0/3] hwmon: fix jiffies wraparound in one-shot ready checks Tom Verdonck
  2026-09-25 18:23 ` [PATCH 1/3] hwmon: (tmp102) Fix jiffies wraparound in conversion-ready check Tom Verdonck
  2026-09-25 18:23 ` [PATCH 2/3] hwmon: (tmp108) " Tom Verdonck
@ 2026-09-25 18:23 ` Tom Verdonck
  2026-09-26  3:27   ` Guenter Roeck
  2 siblings, 1 reply; 7+ messages in thread
From: Tom Verdonck @ 2026-09-25 18:23 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-hwmon, linux-kernel, Tom Verdonck, stable, Antoni Pokusinski

sht4x stores in ->heating_complete the jiffies deadline at which the
current heater pulse finishes. It is set once at probe (to jiffies) and
only updated when the heater is explicitly enabled via sysfs, so in the
common case where the heater is never used it stays frozen at its probe
value.

->heating_complete is an unsigned long compared with time_before(),
whose signed difference is only meaningful while the two values are
within LONG_MAX jiffies of each other. Because the deadline is frozen,
jiffies keeps advancing away from it, and after 2^31 jiffies the
difference flips sign. On a 32-bit HZ=100 kernel that happens ~248.5
days after boot. time_before() in sht4x_read_values() then wrongly
reports that heating is still in progress and the read path executes

	msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies));

with a bogus, huge delta, blocking the temperature read for a very long
time instead of returning data. The heater_enable sysfs attributes are
affected the same way (spurious -EBUSY and "1" readback).

Store the deadline as a 64-bit jiffies value and compare it with
get_jiffies_64()/time_before64(), which does not wrap in any practical
uptime.

Fixes: 0eed6fc3d2b9 ("hwmon: (sht4x): add heater support")
Cc: stable@vger.kernel.org
Signed-off-by: Tom Verdonck <tom.verdonck@inspiral.be>
---
 drivers/hwmon/sht4x.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c
index a97dda9e92dc..da2b6130fca7 100644
--- a/drivers/hwmon/sht4x.c
+++ b/drivers/hwmon/sht4x.c
@@ -67,7 +67,7 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);
  */
 struct sht4x_data {
 	struct i2c_client	*client;
-	unsigned long		heating_complete;	/* in jiffies */
+	u64			heating_complete;	/* in jiffies */
 	bool			data_pending;
 	u32			heater_power;	/* in milli-watts */
 	u32			heater_time;	/* in milli-seconds */
@@ -92,14 +92,14 @@ static int sht4x_read_values(struct sht4x_data *data)
 	u8 crc;
 	u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM};
 	u8 raw_data[SHT4X_RESPONSE_LENGTH];
-	unsigned long curr_jiffies;
+	u64 curr_jiffies;
 
-	curr_jiffies = jiffies;
-	if (time_before(curr_jiffies, data->heating_complete))
+	curr_jiffies = get_jiffies_64();
+	if (time_before64(curr_jiffies, data->heating_complete))
 		msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies));
 
 	if (data->data_pending &&
-	    time_before(jiffies, data->heating_complete + data->update_interval)) {
+	    time_before64(get_jiffies_64(), data->heating_complete + data->update_interval)) {
 		data->data_pending = false;
 	} else {
 		next_update = data->last_updated +
@@ -237,7 +237,7 @@ static ssize_t heater_enable_show(struct device *dev,
 {
 	struct sht4x_data *data = dev_get_drvdata(dev);
 
-	return sysfs_emit(buf, "%u\n", time_before(jiffies, data->heating_complete));
+	return sysfs_emit(buf, "%u\n", time_before64(get_jiffies_64(), data->heating_complete));
 }
 
 static ssize_t heater_enable_store(struct device *dev,
@@ -279,14 +279,14 @@ static ssize_t heater_enable_store(struct device *dev,
 
 	guard(hwmon_lock)(dev);
 
-	if (time_before(jiffies, data->heating_complete))
+	if (time_before64(get_jiffies_64(), data->heating_complete))
 		return -EBUSY;
 
 	ret = i2c_master_send(data->client, &cmd, SHT4X_CMD_LEN);
 	if (ret < 0)
 		return ret;
 
-	data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound);
+	data->heating_complete = get_jiffies_64() + msecs_to_jiffies(heating_time_bound);
 	data->data_pending = true;
 	return count;
 }
@@ -410,7 +410,7 @@ static int sht4x_probe(struct i2c_client *client)
 	data->client = client;
 	data->heater_power = 200;
 	data->heater_time = 1000;
-	data->heating_complete = jiffies;
+	data->heating_complete = get_jiffies_64();
 
 	crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL);
 
-- 
2.53.0


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

* Re: [PATCH 1/3] hwmon: (tmp102) Fix jiffies wraparound in conversion-ready check
  2026-09-25 18:23 ` [PATCH 1/3] hwmon: (tmp102) Fix jiffies wraparound in conversion-ready check Tom Verdonck
@ 2026-09-26  3:25   ` Guenter Roeck
  0 siblings, 0 replies; 7+ messages in thread
From: Guenter Roeck @ 2026-09-26  3:25 UTC (permalink / raw)
  To: Tom Verdonck; +Cc: linux-hwmon, linux-kernel, stable, Nishanth Menon

On Fri, Sep 25, 2026 at 08:23:22PM +0200, Tom Verdonck wrote:
> tmp102 records a one-shot deadline in ->ready_time at probe (and resume)
> and, on every temperature read, refuses the read with -EAGAIN while
> time_before(jiffies, ready_time) is true, in order to skip the first
> ~35 ms conversion.
> 
> ->ready_time is an unsigned long compared with time_before(), whose
> signed difference is only meaningful while the two values are within
> LONG_MAX jiffies of each other. Because ->ready_time is set once and
> never refreshed, jiffies keeps advancing away from it, and after 2^31
> jiffies the difference flips sign. On a 32-bit HZ=100 kernel that
> happens ~248.5 days after boot: time_before() then permanently reports
> "not ready" and the driver returns -EAGAIN on every read, without ever
> touching the sensor, until the next reboot.
> 
> Store the deadline as a 64-bit jiffies value and compare it with
> get_jiffies_64()/time_before64(), which does not wrap in any practical
> uptime.
> 
> Fixes: 3d8f7a89a197 ("hwmon: (tmp102) Improve handling of initial read delay")
> Cc: stable@vger.kernel.org
> Signed-off-by: Tom Verdonck <tom.verdonck@inspiral.be>

Applied.

Thanks,
Guenter

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

* Re: [PATCH 2/3] hwmon: (tmp108) Fix jiffies wraparound in conversion-ready check
  2026-09-25 18:23 ` [PATCH 2/3] hwmon: (tmp108) " Tom Verdonck
@ 2026-09-26  3:26   ` Guenter Roeck
  0 siblings, 0 replies; 7+ messages in thread
From: Guenter Roeck @ 2026-09-26  3:26 UTC (permalink / raw)
  To: Tom Verdonck; +Cc: linux-hwmon, linux-kernel, stable, John Muir

On Fri, Sep 25, 2026 at 08:23:23PM +0200, Tom Verdonck wrote:
> tmp108 records a one-shot deadline in ->ready_time at probe (and resume)
> and, on every temperature read, refuses the read with -EAGAIN while
> time_before(jiffies, ready_time) is true, in order to skip the initial
> conversion.
> 
> ->ready_time is an unsigned long compared with time_before(), whose
> signed difference is only meaningful while the two values are within
> LONG_MAX jiffies of each other. Because ->ready_time is set once and
> never refreshed, jiffies keeps advancing away from it, and after 2^31
> jiffies the difference flips sign. On a 32-bit HZ=100 kernel that
> happens ~248.5 days after boot: time_before() then permanently reports
> "not ready" and the driver returns -EAGAIN on every read, without ever
> touching the sensor, until the next reboot. The continuous-mode path,
> which sets ->ready_time to jiffies with no added conversion delay, wraps
> the same way.
> 
> Store the deadline as a 64-bit jiffies value and compare it with
> get_jiffies_64()/time_before64(), which does not wrap in any practical
> uptime.
> 
> Fixes: 66e1c9171339 ("hwmon: Add Texas Instruments TMP108 temperature sensor driver.")
> Cc: stable@vger.kernel.org
> Signed-off-by: Tom Verdonck <tom.verdonck@inspiral.be>

Applied.

Thanks,
Guenter

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

* Re: [PATCH 3/3] hwmon: (sht4x) Fix jiffies wraparound in heater-ready check
  2026-09-25 18:23 ` [PATCH 3/3] hwmon: (sht4x) Fix jiffies wraparound in heater-ready check Tom Verdonck
@ 2026-09-26  3:27   ` Guenter Roeck
  0 siblings, 0 replies; 7+ messages in thread
From: Guenter Roeck @ 2026-09-26  3:27 UTC (permalink / raw)
  To: Tom Verdonck; +Cc: linux-hwmon, linux-kernel, stable, Antoni Pokusinski

On Fri, Sep 25, 2026 at 08:23:24PM +0200, Tom Verdonck wrote:
> sht4x stores in ->heating_complete the jiffies deadline at which the
> current heater pulse finishes. It is set once at probe (to jiffies) and
> only updated when the heater is explicitly enabled via sysfs, so in the
> common case where the heater is never used it stays frozen at its probe
> value.
> 
> ->heating_complete is an unsigned long compared with time_before(),
> whose signed difference is only meaningful while the two values are
> within LONG_MAX jiffies of each other. Because the deadline is frozen,
> jiffies keeps advancing away from it, and after 2^31 jiffies the
> difference flips sign. On a 32-bit HZ=100 kernel that happens ~248.5
> days after boot. time_before() in sht4x_read_values() then wrongly
> reports that heating is still in progress and the read path executes
> 
> 	msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies));
> 
> with a bogus, huge delta, blocking the temperature read for a very long
> time instead of returning data. The heater_enable sysfs attributes are
> affected the same way (spurious -EBUSY and "1" readback).
> 
> Store the deadline as a 64-bit jiffies value and compare it with
> get_jiffies_64()/time_before64(), which does not wrap in any practical
> uptime.
> 
> Fixes: 0eed6fc3d2b9 ("hwmon: (sht4x): add heater support")
> Cc: stable@vger.kernel.org
> Signed-off-by: Tom Verdonck <tom.verdonck@inspiral.be>

Applied.

Thanks,
Guenter

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

end of thread, other threads:[~2026-09-26  3:27 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-25 18:23 [PATCH 0/3] hwmon: fix jiffies wraparound in one-shot ready checks Tom Verdonck
2026-09-25 18:23 ` [PATCH 1/3] hwmon: (tmp102) Fix jiffies wraparound in conversion-ready check Tom Verdonck
2026-09-26  3:25   ` Guenter Roeck
2026-09-25 18:23 ` [PATCH 2/3] hwmon: (tmp108) " Tom Verdonck
2026-09-26  3:26   ` Guenter Roeck
2026-09-25 18:23 ` [PATCH 3/3] hwmon: (sht4x) Fix jiffies wraparound in heater-ready check Tom Verdonck
2026-09-26  3:27   ` Guenter Roeck

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

all inboxes | Powered by JetHome®