Re: KMSAN: uninit-value in eth_type_trans

From: Helen Koike

Date: Fri Mar 20 2026 - 13:38:24 EST


Hi all,

On 10/13/25 4:31 AM, Marek Szyprowski wrote:
On 08.10.2025 10:43, Alexander Potapenko wrote:
On Tue, Oct 7, 2025 at 8:51 AM Alexander Potapenko <glider@xxxxxxxxxx> wrote:
Folks, as far as I understand, dma_direct_sync_single_for_cpu() and
dma_direct_sync_single_for_device() are the places where we send data
to or from the device.
Should we add KMSAN annotations to those functions to catch infoleaks
and mark data from devices as initialized?
Something along the lines of:

======================================
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 0d37da3d95b65..7f59de19c1c87 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -42,6 +42,7 @@
#include <linux/string.h>
#include <linux/swiotlb.h>
#include <linux/types.h>
+#include <linux/kmsan-checks.h>
#ifdef CONFIG_DMA_RESTRICTED_POOL
#include <linux/of.h>
#include <linux/of_fdt.h>
@@ -903,10 +904,13 @@ static void swiotlb_bounce(struct device *dev,
phys_addr_t tlb_addr, size_t size

local_irq_save(flags);
page = pfn_to_page(pfn);
- if (dir == DMA_TO_DEVICE)
+ if (dir == DMA_TO_DEVICE) {
+ kmsan_check_highmem_page(page, offset, sz);
memcpy_from_page(vaddr, page, offset, sz);
- else
+ } else {
+ kmsan_unpoison_memory(vaddr, sz);
memcpy_to_page(page, offset, vaddr, sz);
+ }
local_irq_restore(flags);

size -= sz;
@@ -915,8 +919,10 @@ static void swiotlb_bounce(struct device *dev,
phys_addr_t tlb_addr, size_t size
offset = 0;
}
} else if (dir == DMA_TO_DEVICE) {
+ kmsan_check_memory(phys_to_virt(orig_addr), size);
memcpy(vaddr, phys_to_virt(orig_addr), size);
} else {
+ kmsan_unpoison_memory(vaddr, size);
memcpy(phys_to_virt(orig_addr), vaddr, size);
}
}
======================================

should be conceptually right, but according to the comment in
swiotlb_tbl_map_single()
(https://protect2.fireeye.com/v1/url?k=837a6d67-dce15478-837be628-000babdfecba-aa25926458f9fd30&q=1&e=a3963b1b-328b-4f69-8ca5-ffd6fc777dd7&u=https%3A%2F%2Felixir.bootlin.com%2Flinux%2Fv6.17.1%2Fsource%2Fkernel%2Fdma%2Fswiotlb.c%23L1431),
that function is deliberately copying the buffer to the device, even
when it is uninitialized - and KMSAN actually started reporting that
when I applied the above patch.

How should we handle this case?
Not adding the kmsan_check_memory() calls will solve the problem, but
there might be real infoleaks that we won't detect.
We could unpoison the buffer before passing it to
swiotlb_tbl_map_single() to ignore just the first infoleak on the
buffer.
Alternatively, we could require callers to always initialize the
buffer passed to swiotlb_tbl_map_single().

Well, I didn't consider swiotlb a special case so far. I did a simple
test with my PoC patch mentioned earlier in this thread with
'swiotlb=force' kernel parameter and I didn't observe any kmsan issues,
but I admin that this wasn't exhaustive test.

Best regards


I just hit this issue and found this thread. So I'm sharing below my report in case it is useful (if not, please just ignore):

I'm easily reproducing it when trying to reproduce another unrelated issue[1] by syzbot on qemu[2] with torvalds/master branch, commit 0e4f8f1a3d08 .

[1] https://syzkaller.appspot.com/bug?extid=ba80855313e6fa65717a
[2] https://github.com/google/syzkaller/blob/master/docs/reproducing_crashes.md#using-a-c-reproducer


To momentarily fix it (and allow me o reach my syzbot issue), I just did the following:

@@ -916,6 +917,12 @@ static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size
memcpy(vaddr, phys_to_virt(orig_addr), size);
} else {
memcpy(phys_to_virt(orig_addr), vaddr, size);
+ kmsan_unpoison_memory(phys_to_virt(orig_addr), size);
}
}

Which, from the discussions here is obviously not the right solution.


In case it helps, see below what I'm executing:
(copy and pasted from my scripts)

* Config: https://syzkaller.appspot.com/text?tag=KernelConfig&x=963de479f54c6dbb
* Disk image: https://storage.googleapis.com/syzbot-assets/bef78207d3f7/disk-2d1373e4.raw.xz


$ gcc \
repro.c \
-lpthread \
-static \
-o repro

Running make olddefconfig …

$ make O=/$BUILD_DIR CC=clang LD=ld.lld olddefconfig

Building kernel …

$ make O=$BUILD_DIR CC=clang LD=ld.lld -j$NPROC

Starting QEMU with disk image …
SSH port: 35053

$ qemu-system-x86_64 \
-m 8G \
-smp 2,sockets=2,cores=1 \
-drive file=disk.raw,format=raw \
-net nic,model=e1000 \
-net user,host=10.0.2.10,hostfwd=tcp::10022-:22 \
-kernel $BUILD_DIR/arch/x86/boot/bzImage \
-append 'root=/dev/sda1 console=ttyS0 panic_on_warn=1 earlyprintk=serial net.ifnames=0 vsyscall=native numa=fake=2' \
-enable-kvm \
-nographic \
-snapshot \
-machine pc-q35-7.1 \
-cpu host

$ scp \
-P 10022 \
repro \
root@127.0.0.1:/root/

$ ssh -p 10022 root@127.0.0.1 'chmod +x ./repro && ./repro'


Regards,
Helen