> From: Arnav Kapoor <
kapoor...@gmail.com>
> Date: Sun, 12 Jan 2026 16:45:00 +0000
> Subject: [PATCH] mm/zswap: clear folio before decompression to prevent
> infoleak
>
> #syz test
>
> KMSAN reported an infoleak in anon_pipe_read() where uninitialized memory
> from a decompressed zswap page was copied to userspace:
>
> BUG: KMSAN: kernel-infoleak-after-free in anon_pipe_read+0x769/0x1e80
> Uninit was stored to memory at:
> zswap_decompress+0x2bd/0x1000 mm/zswap.c:946
> Bytes 0-1023 of 1024 are uninitialized
>
> The issue occurs when zswap decompresses a page but the decompression
> doesn't write a full PAGE_SIZE of data. The function zswap_decompress()
> writes decompressed data directly into the folio without first clearing
> it. If the decompressed size is less than PAGE_SIZE (which can happen
> with certain compression algorithms or corrupted data), or if
> decompression fails partway through, the remainder of the page contains
> uninitialized memory.
>
> This uninitialized data can then:
> 1. Be placed in pipe buffers when the page is used in splice operations
> 2. Get copied to userspace via read() on the pipe
> 3. Leak kernel memory contents to userspace
>
> The call chain observed:
> zswap_load()
> -> zswap_decompress() [writes partial data to folio]
> -> [folio ends up in pipe buffer via shmem_file_splice_read]
> -> anon_pipe_read()
> -> copy_page_to_iter() [copies uninitialized tail to userspace]
>
> Fix this by clearing the entire folio before decompression. This ensures
> that even if decompression writes less than PAGE_SIZE, or fails after
> partial write, the remainder of the page contains zeros rather than
> uninitialized memory.
>
> We use folio_zero_range() which is efficient as it can use optimized
> memset implementations and handles compound pages correctly.
>
> Alternative approaches considered:
> 1. Zero only the tail after decompression - more complex, requires
> tracking actual bytes written, and doesn't help if decompression fails
> 2. Check dlen and zero tail only when dlen < PAGE_SIZE - still leaves
> a race if decompression fails after partial write
>
> The performance impact is minimal since:
> - This is a relatively rare path (swap-in from zswap)
> - Modern CPUs have efficient memory zeroing (often using cache-bypassing
> instructions)
> - The cost is much less than the decompression itself
>
> Reported-by:
syzbot+175753...@syzkaller.appspotmail.com
> Closes:
https://syzkaller.appspot.com/bug?extid=175753aff92b396b1f30
> Fixes: 0ab0abcf5115 ("mm/zswap: remove the memcpy if acomp is not
> sleepable")
> Signed-off-by: Arnav Kapoor <
kapoor...@gmail.com>
> ---
> mm/zswap.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/mm/zswap.c b/mm/zswap.c
> index 000000000000..111111111111 100644
> --- a/mm/zswap.c
> +++ b/mm/zswap.c
> @@ -932,6 +932,13 @@ static bool zswap_decompress(struct zswap_entry
> *entry, struct folio *folio)
> int decomp_ret = 0, dlen = PAGE_SIZE;
> u8 *src, *obj;
> + /*
> + * Clear the folio before decompression to prevent leaking uninitialized
> + * memory to userspace if decompression writes less than PAGE_SIZE or
> + * fails partway through.
> + */
> + folio_zero_range(folio, 0, folio_size(folio));
> +
> acomp_ctx = acomp_ctx_get_cpu_lock(pool);
> obj = zs_obj_read_begin(pool->zs_pool, entry->handle, acomp_ctx->buffer);
> --
> 2.43.0
>
>
>
>
> On Monday, 12 January 2026 at 14:05:27 UTC+5:30 syzbot wrote:
>
> Hello,
>
> syzbot found the following issue on:
>
> HEAD commit: 7f98ab9da046 Merge tag 'for-6.19-rc4-tag' of git://git.ker..
> git tree: upstream
> console output:
https://syzkaller.appspot.com/x/log.txt?x=1334af92580000
> kernel config:
https://syzkaller.appspot.com/x/.config?x=b3903bdf68407a14
> dashboard link:
https://syzkaller.appspot.com/bug?extid=175753aff92b396b1f30
> compiler: Debian clang version 20.1.8
> (++20250708063551+0c9f909b7976-1~exp1~20250708183702.136), Debian LLD
> 20.1.8
> userspace arch: i386
>
> Unfortunately, I don't have any reproducer for this issue yet.
>
> Downloadable assets:
> disk image:
>
https://storage.googleapis.com/syzbot-assets/c40942ffda39/disk-7f98ab9d.raw.xz
> vmlinux:
>
https://storage.googleapis.com/syzbot-assets/63f72388d8d6/vmlinux-7f98ab9d.xz
> kernel image:
>
https://storage.googleapis.com/syzbot-assets/751895685a54/bzImage-7f98ab9d.xz
>
> IMPORTANT: if you fix the issue, please add the following tag to the
> commit:
> Reported-by:
syzbot+175753...@syzkaller.appspotmail.com
>
> =====================================================
> BUG: KMSAN: kernel-infoleak-after-free in instrument_copy_to_user
> include/linux/instrumented.h:114 [inline]
> BUG: KMSAN: kernel-infoleak-after-free in copy_to_user_iter
> lib/iov_iter.c:24 [inline]
> BUG: KMSAN: kernel-infoleak-after-free in iterate_ubuf
> include/linux/iov_iter.h:30 [inline]
> BUG: KMSAN: kernel-infoleak-after-free in iterate_and_advance2
> include/linux/iov_iter.h:302 [inline]
> BUG: KMSAN: kernel-infoleak-after-free in iterate_and_advance
> include/linux/iov_iter.h:330 [inline]
> BUG: KMSAN: kernel-infoleak-after-free in _copy_to_iter+0xef3/0x33f0
> lib/iov_iter.c:197
> instrument_copy_to_user include/linux/instrumented.h:114 [inline]
> copy_to_user_iter lib/iov_iter.c:24 [inline]
> iterate_ubuf include/linux/iov_iter.h:30 [inline]
> iterate_and_advance2 include/linux/iov_iter.h:302 [inline]
> iterate_and_advance include/linux/iov_iter.h:330 [inline]
> _copy_to_iter+0xef3/0x33f0 lib/iov_iter.c:197
> copy_page_to_iter+0x482/0x910 lib/iov_iter.c:374
> anon_pipe_read+0x769/0x1e80 fs/pipe.c:343
> new_sync_read fs/read_write.c:491 [inline]
> vfs_read+0x8ed/0xf90 fs/read_write.c:572
> ksys_read fs/read_write.c:715 [inline]
> __do_sys_read fs/read_write.c:724 [inline]
> __se_sys_read fs/read_write.c:722 [inline]
> __ia32_sys_read+0x1f9/0x4d0 fs/read_write.c:722
> ia32_sys_call+0x191f/0x4340 arch/x86/include/generated/asm/syscalls_32.h:4
> do_syscall_32_irqs_on arch/x86/entry/syscall_32.c:83 [inline]
> __do_fast_syscall_32+0x154/0x320 arch/x86/entry/syscall_32.c:307
> do_fast_syscall_32+0x38/0x80 arch/x86/entry/syscall_32.c:332
> do_SYSENTER_32+0x1f/0x30 arch/x86/entry/syscall_32.c:370
> entry_SYSENTER_compat_after_hwframe+0x84/0x8e
>
> Uninit was stored to memory at:
> memcpy_to_folio include/linux/highmem.h:518 [inline]
> zswap_decompress+0x2bd/0x1000 mm/zswap.c:946
> zswap_load+0x262/0x570 mm/zswap.c:1627
> swap_read_folio+0x662/0x3050 mm/page_io.c:637
> swap_cluster_readahead+0x725/0xb20 mm/swap_state.c:652
> shmem_swapin_cluster mm/shmem.c:1745 [inline]
> shmem_swapin_folio+0x1fd9/0x3ee0 mm/shmem.c:2329
> shmem_get_folio_gfp+0x92a/0x1fc0 mm/shmem.c:2489
> shmem_get_folio mm/shmem.c:2662 [inline]
> shmem_file_splice_read+0x350/0x11e0 mm/shmem.c:3567
> do_splice_read fs/splice.c:982 [inline]
> splice_file_to_pipe+0x5b4/0x8f0 fs/splice.c:1292
> do_splice+0x29d8/0x30d0 fs/splice.c:1376
> __do_splice fs/splice.c:1433 [inline]
> __do_sys_splice fs/splice.c:1636 [inline]
> __se_sys_splice+0x549/0x8c0 fs/splice.c:1618
> __ia32_sys_splice+0x112/0x1a0 fs/splice.c:1618
> ia32_sys_call+0x31a6/0x4340
> arch/x86/include/generated/asm/syscalls_32.h:314
> do_syscall_32_irqs_on arch/x86/entry/syscall_32.c:83 [inline]
> __do_fast_syscall_32+0x154/0x320 arch/x86/entry/syscall_32.c:307
> do_fast_syscall_32+0x38/0x80 arch/x86/entry/syscall_32.c:332
> do_SYSENTER_32+0x1f/0x30 arch/x86/entry/syscall_32.c:370
> entry_SYSENTER_compat_after_hwframe+0x84/0x8e
>
> Uninit was created at:
> free_pages_prepare mm/page_alloc.c:1328 [inline]
> free_unref_folios+0x26a/0x29a0 mm/page_alloc.c:3000
> folios_put_refs+0xaac/0xb10 mm/swap.c:1002
> folios_put include/linux/mm.h:1671 [inline]
> __folio_batch_release+0xe1/0x100 mm/swap.c:1062
> folio_batch_release include/linux/pagevec.h:101 [inline]
> shmem_undo_range+0x929/0x20c0 mm/shmem.c:1137
> shmem_truncate_range mm/shmem.c:1249 [inline]
> shmem_evict_inode+0x22c/0xed0 mm/shmem.c:1379
> evict+0x6a9/0xca0 fs/inode.c:837
> iput_final fs/inode.c:1951 [inline]
> iput+0xc6f/0x1070 fs/inode.c:2003
> do_unlinkat+0x58a/0xd80 fs/namei.c:5443
> __do_sys_unlink fs/namei.c:5474 [inline]
> __se_sys_unlink fs/namei.c:5472 [inline]
> __ia32_sys_unlink+0x70/0xa0 fs/namei.c:5472
> ia32_sys_call+0x1e4a/0x4340 arch/x86/include/generated/asm/syscalls_32.h:11
> do_syscall_32_irqs_on arch/x86/entry/syscall_32.c:83 [inline]
> __do_fast_syscall_32+0x154/0x320 arch/x86/entry/syscall_32.c:307
> do_fast_syscall_32+0x38/0x80 arch/x86/entry/syscall_32.c:332
> do_SYSENTER_32+0x1f/0x30 arch/x86/entry/syscall_32.c:370
> entry_SYSENTER_compat_after_hwframe+0x84/0x8e
>
> Bytes 0-1023 of 1024 are uninitialized
> Memory access of size 1024 starts at ffff8880731d2000
> Data copied to user address 0000000056912be0
>
> CPU: 1 UID: 0 PID: 5779 Comm: syz-executor Tainted: G W syzkaller #0
> PREEMPT(none)
> Tainted: [W]=WARN
> Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS
> Google 10/25/2025
> =====================================================
>
>
> ---
> This report is generated by a bot. It may contain errors.
> See
https://goo.gl/tpsmEJ for more information about syzbot.
> syzbot engineers can be reached at
syzk...@googlegroups.com.
>
> syzbot will keep track of this issue. See:
>
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
>
> If the report is already addressed, let syzbot know by replying with:
> #syz fix: exact-commit-title
>
> If you want to overwrite report's subsystems, reply with:
> #syz set subsystems: new-subsystem
> (See the list of subsystem names on the web dashboard)
>
> If the report is a duplicate of another one, reply with:
> #syz dup: exact-subject-of-another-report
>
> If you want to undo deduplication, reply with:
> #syz undup
>
Command #1:
This crash does not have a reproducer. I cannot test it.