AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/07 12:59 flow patch-triage
4m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "The patch adds a length check `skb_headlen(skb) \u003c ETH_HLEN` in `eth_type_trans()` to prevent processing packets with a linear part shorter than the Ethernet header (14 bytes). \n\nWithout this check, if a user injects a short linear packet (e.g., 10 bytes) via a TUN/TAP device or an AF_PACKET raw socket, `eth_type_trans()` calls `eth_skb_pull_mac()`. `eth_skb_pull_mac()` calls `skb_pull_inline(skb, ETH_HLEN)`. If `skb-\u003elen \u003c ETH_HLEN`, `skb_pull_inline()` fails and returns `NULL` without triggering any `BUG_ON` or crash. `eth_skb_pull_mac()` ignores this return value and returns the original `skb-\u003edata` pointer.\n\n`eth_type_trans()` then reads `eth-\u003eh_proto` at offset 12. Because the skb data buffer is typically allocated with a much larger size (e.g., via `kmalloc`), this read is well within the allocation bounds. Therefore, KASAN will NOT detect it as an out-of-bounds read.\n\nHowever, the memory beyond the user-provided 10 bytes is uninitialized. KMSAN will detect this as a read of uninitialized memory. Furthermore, this uninitialized value is stored in `skb-\u003eprotocol`. If the packet is later received by an AF_PACKET socket, `skb-\u003eprotocol` is copied to `sll_protocol` in the `sockaddr_ll` structure and returned to user space via `recvmsg()`. This constitutes a direct kernel-to-user info-leak of 2 bytes of uninitialized kernel memory.\n\nSince this issue does not cause a crash, out-of-bounds access, or trigger a `BUG_ON` for linear packets, it can ONLY be detected by KMSAN. Therefore, a dedicated KMSAN fuzzing session is highly justified.",
  "NeedsKMSAN": true,
  "Reasoning": "The patch adds a check to drop ethernet frames with a short linear part in eth_type_trans. This is a functional change in the core ethernet receive path, so it is worth fuzzing. Since eth_type_trans is a generic hot-path function, it is not added to FocusSymbols to avoid skewing test distributions.",
  "WorthFuzzing": true
}

1/1 2026/08/07 12:59 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 3ed552c0c6cdc6816780b1ca68f99c38c5aba7dd\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Aug 7 12:59:15 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/net/ethernet/eth.c b/net/ethernet/eth.c\nindex d9faadbe9b6c8..84914709ac061 100644\n--- a/net/ethernet/eth.c\n+++ b/net/ethernet/eth.c\n@@ -161,6 +161,12 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)\n \tskb-\u003edev = dev;\n \tskb_reset_mac_header(skb);\n \n+\tif (unlikely(skb_headlen(skb) \u003c ETH_HLEN)) {\n+\t\tnet_warn_ratelimited(\"%s: dropping frame with a short linear part from %s\\n\",\n+\t\t\t\t     __func__, dev-\u003ename);\n+\t\treturn 0;\n+\t}\n+\n \teth = eth_skb_pull_mac(skb);\n \teth_skb_pkt_type(skb, dev);\n \n"
}

2/1 2026/08/07 12:59 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch adds a check to drop ethernet frames with a short linear part in eth_type_trans. This is a functional change in the core ethernet receive path, so it is worth fuzzing. Since eth_type_trans is a generic hot-path function, it is not added to FocusSymbols to avoid skewing test distributions.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 3ed552c0c6cdc6816780b1ca68f99c38c5aba7dd
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 7 12:59:15 2026 +0000

    syz-cluster: applied patch under review

diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index d9faadbe9b6c8..84914709ac061 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -161,6 +161,12 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
 	skb->dev = dev;
 	skb_reset_mac_header(skb);
 
+	if (unlikely(skb_headlen(skb) < ETH_HLEN)) {
+		net_warn_ratelimited("%s: dropping frame with a short linear part from %s\n",
+				     __func__, dev->name);
+		return 0;
+	}
+
 	eth = eth_skb_pull_mac(skb);
 	eth_skb_pkt_type(skb, dev);
 

3/2 2026/08/07 12:59 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 2034
							output: 29
							thoughts: 311
						

Thoughts:
**Analyzing a Patch: `eth_type_trans` in `net/ethernet/eth.c`**

Okay, so there's a patch introducing a check within `eth_type_trans` in `net/ethernet/eth.c`.  Specifically, the code now looks for packets with a short linear part – less than `ETH_HLEN`.  The check is right there, dropping these short frames.  Now, `eth_type_trans`… that's *everywhere*. It's a fundamental function for Ethernet drivers, doing the critical work of setting packet type and pulling out the MAC header.  Definitely reachable, and my initial instinct says it's worth fuzzing given the functional change.

Wait a second…  `eth_type_trans` is a *hot-path* function. We need to be careful with those. If it's a hot path, listing it in `FocusSymbols` might skew the fuzzing distribution, which we want to avoid. The documentation says, "Avoid listing generic hot-path functions to prevent skewed test distributions."

So, I can still say the change makes it WorthFuzzing=true, but I need to make sure the `FocusSymbols` are empty.  We don't want to over-prioritize this function in the fuzzing process.

Let's quickly check the configuration.  `net/ethernet/eth.c` is compiled when `CONFIG_ETHERNET` is enabled, which is pretty much guaranteed in any basic setup. I'll double check the `net/ethernet/Makefile` just to be sure there's nothing else specific that might affect it.



4/2 2026/08/07 12:59 tool grepper
0m Args:
{
  "Expression": "eth.o",
  "PathPrefix": "net/ethernet/Makefile"
}

Results:
{
  "Output": "net/ethernet/Makefile-5-\nnet/ethernet/Makefile:6:obj-y\t\t\t\t\t+= eth.o\n"
}

5/2 2026/08/07 12:59 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 2766
							output: 99
							thoughts: 159
						

Thoughts:
**Fuzzing the Ethernet Receive Path**

Okay, so I'm looking at `obj-y += eth.o`. That means the `eth` module is always compiled into the kernel. Right, this makes it an interesting target for fuzzing. Given the description, the patch introduces a check to discard packets that are too short, and this sounds like it could uncover some vulnerabilities.

Now, my mind is already racing. Specifically, `eth_type_trans` gets called for *every* Ethernet packet received. That's a huge code surface! The initial thought was, should this be in `FocusSymbols`? However, that would almost certainly skew the fuzzing distribution heavily towards Ethernet, and that's not what I want. So, `FocusSymbols` needs to be empty in this case. Also, `EnableConfigs` should probably be empty too. The core of this logic is in the ethernet receive path which is generic.

So, the reasoning boils down to this: The change introduces a *functional* check in `eth_type_trans`. It's a fundamental change in how the kernel handles received Ethernet packets, so fuzzing is certainly warranted. But, because `eth_type_trans` is a generic, hot-path function, I need to be careful with how I configure the fuzzing process to avoid undue focus on it.



6/2 2026/08/07 12:59 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch adds a check to drop ethernet frames with a short linear part in eth_type_trans. This is a functional change in the core ethernet receive path, so it is worth fuzzing. Since eth_type_trans is a generic hot-path function, it is not added to FocusSymbols to avoid skewing test distributions.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch adds a check to drop ethernet frames with a short linear part in eth_type_trans. This is a functional change in the core ethernet receive path, so it is worth fuzzing. Since eth_type_trans is a generic hot-path function, it is not added to FocusSymbols to avoid skewing test distributions.",
  "WorthFuzzing": true
}

7/1 2026/08/07 12:59 action If
4m Args:
{
  "WorthFuzzing": true
}

Results:
null

8/2 2026/08/07 12:59 agent kmsan-evaluator
4m Model:
best-expensive

Args:
null

Results:
{
  "KMSANReasoning": "The patch adds a length check `skb_headlen(skb) \u003c ETH_HLEN` in `eth_type_trans()` to prevent processing packets with a linear part shorter than the Ethernet header (14 bytes). \n\nWithout this check, if a user injects a short linear packet (e.g., 10 bytes) via a TUN/TAP device or an AF_PACKET raw socket, `eth_type_trans()` calls `eth_skb_pull_mac()`. `eth_skb_pull_mac()` calls `skb_pull_inline(skb, ETH_HLEN)`. If `skb-\u003elen \u003c ETH_HLEN`, `skb_pull_inline()` fails and returns `NULL` without triggering any `BUG_ON` or crash. `eth_skb_pull_mac()` ignores this return value and returns the original `skb-\u003edata` pointer.\n\n`eth_type_trans()` then reads `eth-\u003eh_proto` at offset 12. Because the skb data buffer is typically allocated with a much larger size (e.g., via `kmalloc`), this read is well within the allocation bounds. Therefore, KASAN will NOT detect it as an out-of-bounds read.\n\nHowever, the memory beyond the user-provided 10 bytes is uninitialized. KMSAN will detect this as a read of uninitialized memory. Furthermore, this uninitialized value is stored in `skb-\u003eprotocol`. If the packet is later received by an AF_PACKET socket, `skb-\u003eprotocol` is copied to `sll_protocol` in the `sockaddr_ll` structure and returned to user space via `recvmsg()`. This constitutes a direct kernel-to-user info-leak of 2 bytes of uninitialized kernel memory.\n\nSince this issue does not cause a crash, out-of-bounds access, or trigger a `BUG_ON` for linear packets, it can ONLY be detected by KMSAN. Therefore, a dedicated KMSAN fuzzing session is highly justified.",
  "NeedsKMSAN": true
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 3ed552c0c6cdc6816780b1ca68f99c38c5aba7dd
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 7 12:59:15 2026 +0000

    syz-cluster: applied patch under review

diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index d9faadbe9b6c8..84914709ac061 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -161,6 +161,12 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
 	skb->dev = dev;
 	skb_reset_mac_header(skb);
 
+	if (unlikely(skb_headlen(skb) < ETH_HLEN)) {
+		net_warn_ratelimited("%s: dropping frame with a short linear part from %s\n",
+				     __func__, dev->name);
+		return 0;
+	}
+
 	eth = eth_skb_pull_mac(skb);
 	eth_skb_pkt_type(skb, dev);
 

9/3 2026/08/07 12:59 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 2031
							output: 36
							thoughts: 546
						

Thoughts:
**Analyzing the Ethernet Frame Length Check**

Okay, so I'm looking at this patch that adds a check in `eth_type_trans` for the head length of an `skb`. Specifically, `if (unlikely(skb_headlen(skb) < ETH_HLEN))`.  The idea is to drop the frame if the head length is smaller than `ETH_HLEN`.  My initial thought is, this is a bounds check, right?  Preventing an out-of-bounds access when `eth_skb_pull_mac(skb)` is called later.  We pull the MAC header, and if the buffer is too small, boom, OOB read.

I start to wonder about uninitialized memory, though.  If `skb_headlen(skb)` is less than `ETH_HLEN`, is there a risk that `eth_skb_pull_mac` might try to read uninitialized memory?  My first instinct is that if it's *that* small, it's an OOB read, and KASAN will flag that. However, maybe the buffer is allocated larger, but the actual packet length is smaller?  Could there be a scenario where it's within the allocated buffer but beyond `skb->len` and we end up reading uninitialized padding?  But wait, KMSAN is designed to catch uninitialized memory, right?  KASAN handles the OOB stuff. This patch seems to be about preventing reading beyond the allocated and initialized part of the `skb`'s head.

Let's dive into `eth_skb_pull_mac`.  Ah, it calls `skb_pull_inline(skb, ETH_HLEN)`.  If `skb->len` is less than `ETH_HLEN`, something *bad* might happen, or we end up reading `eth->h_proto` which is technically out-of-bounds of the linear portion. If it's *beyond* the allocated part, KASAN will catch that. But what if it's within the allocation, but past the actual packet data (`skb->len`)? KASAN might not catch that, unless its skb integration is more extensive. KMSAN, however, *should* catch uninitialized memory if we're reading past the valid data. The memory there is likely uninitialized.

So, is this patch primarily about preventing OOB reads or catching uninitialized memory? The commit message says "dropping frame with a short linear part". That's a classic OOB issue. `skb_headlen(skb) < ETH_HLEN` means the linear part is too short. It's an OOB read/write. I should really go back and check the `eth_type_trans` code itself in `net/ethernet/eth.c` to be completely sure.



10/3 2026/08/07 12:59 tool grepper
0m Args:
{
  "Expression": "eth_skb_pull_mac",
  "PathPrefix": "net/ethernet/eth.c"
}

Results:
{
  "Output": "net/ethernet/eth.c=155=__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)\n--\nnet/ethernet/eth.c-169-\nnet/ethernet/eth.c:170:\teth = eth_skb_pull_mac(skb);\nnet/ethernet/eth.c-171-\teth_skb_pkt_type(skb, dev);\n"
}

11/3 2026/08/07 12:59 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 3355
							output: 36
							thoughts: 22
						

Thoughts:
Here's my thought process, as if I were considering this code:

**Diving into the Ethernet Type Translator**

Alright, I need to understand how Ethernet types are being handled. Let's start by digging into `eth_type_trans` within `net/ethernet/eth.c`. That function's likely the key to figuring out how raw Ethernet frames are classified and processed, I'm thinking. I'm especially interested in how it maps EtherType values to protocol handlers; understanding that will be crucial for any in-depth analysis of Ethernet frame processing. I'll need to carefully examine the input arguments, return values, and any internal data structures it uses to make these crucial translation decisions. Let's get to it.



12/3 2026/08/07 12:59 tool read-file
0m Args:
{
  "File": "net/ethernet/eth.c",
  "FirstLine": 150,
  "LineCount": 50
}

Results:
{
  "Contents": " 150:\t *\n 151:\t * The rule here is that we\n 152:\t * assume 802.3 if the type field is short enough to be a length.\n 153:\t * This is normal practice and works for any 'now in use' protocol.\n 154:\t */\n 155:\t__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)\n 156:\t{\n 157:\t\tconst unsigned short *sap;\n 158:\t\tconst struct ethhdr *eth;\n 159:\t\t__be16 res;\n 160:\t\n 161:\t\tskb-\u003edev = dev;\n 162:\t\tskb_reset_mac_header(skb);\n 163:\t\n 164:\t\tif (unlikely(skb_headlen(skb) \u003c ETH_HLEN)) {\n 165:\t\t\tnet_warn_ratelimited(\"%s: dropping frame with a short linear part from %s\\n\",\n 166:\t\t\t\t\t     __func__, dev-\u003ename);\n 167:\t\t\treturn 0;\n 168:\t\t}\n 169:\t\n 170:\t\teth = eth_skb_pull_mac(skb);\n 171:\t\teth_skb_pkt_type(skb, dev);\n 172:\t\n 173:\t\t/*\n 174:\t\t * Some variants of DSA tagging don't have an ethertype field\n 175:\t\t * at all, so we check here whether one of those tagging\n 176:\t\t * variants has been configured on the receiving interface,\n 177:\t\t * and if so, set skb-\u003eprotocol without looking at the packet.\n 178:\t\t */\n 179:\t\tif (unlikely(netdev_uses_dsa(dev)))\n 180:\t\t\treturn htons(ETH_P_XDSA);\n 181:\t\n 182:\t\tif (likely(eth_proto_is_802_3(eth-\u003eh_proto)))\n 183:\t\t\treturn eth-\u003eh_proto;\n 184:\t\n 185:\t\t/*\n 186:\t\t *      This is a magic hack to spot IPX packets. Older Novell breaks\n 187:\t\t *      the protocol design and runs IPX over 802.3 without an 802.2 LLC\n 188:\t\t *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This\n 189:\t\t *      won't work for fault tolerant netware but does for the rest.\n 190:\t\t *\tWe use skb-\u003edev as temporary storage to not hit\n 191:\t\t *\tCONFIG_STACKPROTECTOR_STRONG=y costs on some platforms.\n 192:\t\t */\n 193:\t\tsap = skb_header_pointer(skb, 0, sizeof(*sap), \u0026skb-\u003edev);\n 194:\t\tres = (sap \u0026\u0026 *sap == 0xFFFF) ? htons(ETH_P_802_3) : htons(ETH_P_802_2);\n 195:\t\n 196:\t\t/* restore skb-\u003edev in case it was mangled by skb_header_pointer(). */\n 197:\t\tskb-\u003edev = dev;\n 198:\t\treturn res;\n 199:\t}\n"
}

13/3 2026/08/07 12:59 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 4449
							output: 26
							thoughts: 365
						

Thoughts:
**Analyzing a Potential Kernel Vulnerability**

Okay, so I'm looking at this patch and immediately see a potential issue with `eth_skb_pull_mac`. It seems like the function is guarded against pulling too much data, specifically more than what's already present in the `skb`'s head. However, if the head length is actually *less* than the Ethernet header length (`ETH_HLEN`), it will try to pull `ETH_HLEN` bytes anyway. This means `skb->data` would get advanced past the end of the valid linear data (or, at the very least, potentially past `skb->tail`). Then, this code attempts to access `eth->h_proto`. This field is at an offset of 12 bytes within the Ethernet header.

If the linear portion of the `skb` is shorter than 14 bytes, this access to `eth->h_proto` will cause an out-of-bounds read. Crucially, I'm thinking, this might *not* immediately crash. If the `skb`'s data buffer was allocated with some slack (and it usually is, given minimum allocation sizes), the out-of-bounds read might hit valid memory, not an unmapped page. So, we're not guaranteed a page fault.

But, would KASAN catch this? KASAN is supposed to track memory allocations. If the skb buffer was allocated using `kmalloc`, KASAN should know its size. I'm thinking though, `skb->data` might be within the allocated buffer, but the valid data only goes up to `skb_headlen(skb)`. The bytes beyond that, up to the end of the allocated buffer, are either uninitialized or contain old data. Ah, wait a minute, doesn't KASAN do some special handling for skbs? Doesn't it have annotations for skbs?

Yes! KASAN poisons the unused part of skb data. So, let's confirm whether KASAN poisons skb data. This is key to determine whether it would flag this potentially out of bounds read.



14/3 2026/08/07 12:59 tool grepper
0m Args:
{
  "Expression": "kasan_.*skb",
  "PathPrefix": ""
}

Results:
{
  "Output": "net/core/skbuff.c=284=static inline struct sk_buff *napi_skb_cache_get(bool alloc)\n--\nnet/core/skbuff.c-305-\tlocal_unlock_nested_bh(\u0026napi_alloc_cache.bh_lock);\nnet/core/skbuff.c:306:\tkasan_mempool_unpoison_object(skb, skbuff_cache_size);\nnet/core/skbuff.c-307-\n--\nnet/core/skbuff.c=344=u32 napi_skb_cache_get_bulk(void **skbs, u32 n)\n--\nnet/core/skbuff.c-378-\nnet/core/skbuff.c:379:\t\tkasan_mempool_unpoison_object(skbs[i], skbuff_cache_size);\nnet/core/skbuff.c-380-\t\tskbuff_clear(skbs[i]);\n--\nnet/core/skbuff.c=1464=static void napi_skb_cache_put(struct sk_buff *skb)\n--\nnet/core/skbuff.c-1467-\nnet/core/skbuff.c:1468:\tif (!kasan_mempool_poison_object(skb))\nnet/core/skbuff.c-1469-\t\treturn;\n--\nnet/core/skbuff.c-1477-\t\tfor (i = remaining; i \u003c NAPI_SKB_CACHE_SIZE; i++)\nnet/core/skbuff.c:1478:\t\t\tkasan_mempool_unpoison_object(nc-\u003eskb_cache[i],\nnet/core/skbuff.c-1479-\t\t\t\t\t\tskbuff_cache_size);\n"
}

15/3 2026/08/07 12:59 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 5750
							output: 32
							thoughts: 66
						

Thoughts:
**KASAN's Role in sk_buff Poisoning**

Okay, hold on a second. I need to clarify something about `kasan_mempool_poison_object`. My understanding is that this poisons the *`sk_buff` struct* itself when it's allocated from a mempool. But is KASAN also used to poison the *data buffer* that's attached to the `sk_buff`? That's the crucial point. I need to verify how memory corruption is handled with the data buffer.

Let me quickly dive into `net/core/skbuff.c` and hunt for calls to `kasan_poison` or anything similar that handles data poisoning within the `sk_buff`'s context. That's the most likely place to find the answer. It's a key detail for correctly assessing potential memory issues and vulnerabilities here. I'm especially interested if there's any distinction between mempool-allocated and other `skb` allocations with respect to KASAN poisoning of the data.



16/3 2026/08/07 12:59 tool grepper
0m Args:
{
  "Expression": "kasan.*poison",
  "PathPrefix": "net/core/skbuff.c"
}

Results:
{
  "Output": "net/core/skbuff.c=284=static inline struct sk_buff *napi_skb_cache_get(bool alloc)\n--\nnet/core/skbuff.c-305-\tlocal_unlock_nested_bh(\u0026napi_alloc_cache.bh_lock);\nnet/core/skbuff.c:306:\tkasan_mempool_unpoison_object(skb, skbuff_cache_size);\nnet/core/skbuff.c-307-\n--\nnet/core/skbuff.c=344=u32 napi_skb_cache_get_bulk(void **skbs, u32 n)\n--\nnet/core/skbuff.c-378-\nnet/core/skbuff.c:379:\t\tkasan_mempool_unpoison_object(skbs[i], skbuff_cache_size);\nnet/core/skbuff.c-380-\t\tskbuff_clear(skbs[i]);\n--\nnet/core/skbuff.c=1464=static void napi_skb_cache_put(struct sk_buff *skb)\n--\nnet/core/skbuff.c-1467-\nnet/core/skbuff.c:1468:\tif (!kasan_mempool_poison_object(skb))\nnet/core/skbuff.c-1469-\t\treturn;\n--\nnet/core/skbuff.c-1477-\t\tfor (i = remaining; i \u003c NAPI_SKB_CACHE_SIZE; i++)\nnet/core/skbuff.c:1478:\t\t\tkasan_mempool_unpoison_object(nc-\u003eskb_cache[i],\nnet/core/skbuff.c-1479-\t\t\t\t\t\tskbuff_cache_size);\n"
}

17/3 2026/08/07 12:59 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6523
							output: 33
							thoughts: 464
						

Thoughts:
**KMSAN's Potential Advantage in SKB Data Handling**

Okay, so I'm thinking about how KASAN and KMSAN interact with SKB data buffers, and something seems to be clicking. Initially, it seemed like KASAN might miss errors when reading past `skb->tail` within the allocated buffer, as it only checks the overall allocation size. But then I remembered KMSAN's role in detecting uninitialized memory.

If the `skb->data` buffer is allocated using `kmalloc` then KASAN will poison past the requested size, but the important point here is that when reading beyond `skb->tail`, we're potentially reading uninitialized memory, right? And KMSAN is specifically designed to catch that! If it's a new allocation, KMSAN should nail it, since the buffer will be uninitialized. Though, if the buffer is being reused, KMSAN *might* miss it since it only tracks initialization at allocation.

Thinking specifically about the receive path and how `eth_type_trans` fits in. The driver grabs a buffer, receives a packet, and sets `skb->len`. Anything beyond that is essentially "garbage" left over from the previous use, or padding not written by the device. If DMA is used, the device will write to the beginning of the buffer, and KMSAN will unpoison that memory, but the rest remains poisoned, hence uninitialized, right?

So, if `eth_type_trans` attempts to read, say, `eth->h_proto` beyond the received length (i.e. beyond `skb->len`), it should trigger a KMSAN warning, since it's an uninitialized read! That's a good find!

Now, is this a KMSAN-specific issue? Yes, because KASAN is likely not going to catch it, given the memory access is within the allocated bounds. KASAN doesn't have the same concept of 'logical' size, only the allocated size. It looks like it boils down to the use of uninitialized memory.

Let me just double-check, but I don't believe KASAN has the ability to poison SKB data, but there's a patch series called "kasan: add skb data poisoning". I'll have to check if that landed.



18/3 2026/08/07 13:00 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "kasan.*skb",
  "PathPrefix": "include/linux/skbuff.h"
}

Results:
{
  "error": "no matches"
}

19/3 2026/08/07 13:00 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7525
							output: 27
							thoughts: 39
						

Thoughts:
Okay, here's my interpretation of those thoughts, summarized from my perspective as a seasoned professional:

**Digging into Potential KASAN Issues with SKBs**

Alright, the initial scan for `kasan.*skb` in `include/linux/skbuff.h` didn't yield any direct hits. That's not entirely unexpected, sometimes these things are subtly named or implemented across multiple files. So, the next logical step is to dive deeper and see if KASAN (Kernel Address Sanitizer) is being utilized to detect potential memory corruption problems specifically within the data buffers associated with `skb` (socket buffer) structures. I need to understand if there is any KASAN poisoning being applied to the skb data. I'm going to look for any clues there to determine whether KASAN is being actively leveraged to ensure the integrity of this critical networking data.



20/3 2026/08/07 13:00 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "kasan_poison.*skb",
  "PathPrefix": ""
}

Results:
{
  "error": "no matches"
}

21/3 2026/08/07 13:00 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7790
							output: 36
							thoughts: 583
						

Thoughts:
**My Analysis of Uninitialized Memory in Network Packet Processing**

Okay, so I'm digging into how network drivers handle packet data and how memory sanitizers come into play. It seems KASAN isn't the silver bullet here, which is interesting. It doesn't catch reads beyond `skb->tail` or `skb_headlen` but *within* the allocated buffer. KMSAN, however, *does* catch it, because the memory beyond the actual packet data is uninitialized.

The key is this: if a driver allocates a page for a packet, that entire page starts out uninitialized. DMA writes the actual packet data, and KMSAN specifically unpoisons that written portion. The rest, however, remains untouched, hence uninitialized.

Now, take `eth_type_trans` for example, which reads `eth->h_proto` (2 bytes). If the packet is smaller than the Ethernet header length (`ETH_HLEN`), this read could be beyond `skb_headlen(skb)`. Boom, uninitialized memory!

That `eth->h_proto` value is then used to determine the protocol. This value could end up in `skb->protocol`. Later, this `skb->protocol` is used for routing or filtering, and that could easily lead to conditional branches based on that uninitialized data. That's a perfect scenario for KMSAN to flag as a "use of uninitialized value" error.

I confirmed my hypothesis by looking at the code, specifically the `eth_type_trans` function:
```c
	eth = eth_skb_pull_mac(skb);
	eth_skb_pkt_type(skb, dev);

	/* ... */
	if (likely(eth_proto_is_802_3(eth->h_proto)))
		return eth->h_proto;
```
If `skb_headlen(skb)` is less than `ETH_HLEN`, then reading `eth->h_proto` is going to be from uninitialized memory. That `eth_proto_is_802_3` function checks if the protocol is greater than or equal to 1536. Conditional branch on uninitialized data! KMSAN will *definitely* catch that.

My next question is, will KASAN catch this? No, it won't. The read is within the allocation bounds. Usually, network drivers allocate at least a page (4096 bytes) or a buffer large enough to receive packets. A packet of less than 14 bytes (Ethernet header length) easily fits within that allocation. So the `eth->h_proto` read happens within the allocation. It's KMSAN that shines here because the memory beyond the packet data is untouched and therefore uninitialized.

Now, I wonder if another tool catches this. Maybe there is a `BUG_ON` in `skb_pull`? I should check `eth_skb_pull_mac`.



22/3 2026/08/07 13:00 tool grepper
0m Args:
{
  "Expression": "eth_skb_pull_mac",
  "PathPrefix": "include/linux/etherdevice.h"
}

Results:
{
  "Output": "include/linux/etherdevice.h=623=static inline void eth_skb_pkt_type(struct sk_buff *skb,\n--\ninclude/linux/etherdevice.h-639-\ninclude/linux/etherdevice.h:640:static inline struct ethhdr *eth_skb_pull_mac(struct sk_buff *skb)\ninclude/linux/etherdevice.h-641-{\n"
}

23/3 2026/08/07 13:00 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9166
							output: 36
							thoughts: 14
						

Thoughts:
Okay, here's my interpretation of that thought process, assuming I'm well-versed in kernel networking internals:

**Diving into `eth_skb_pull_mac`**

Alright, let's pull up the source code for `eth_skb_pull_mac`. This function name immediately screams "Ethernet and SKB manipulation," so it's likely a key player in the data link layer within the kernel's network stack. I'm guessing it's responsible for, at a minimum, handling the pulling or advancement of the Secure Kernel Buffer (SKB) pointer to account for the Ethernet MAC header. This is the stage where the kernel is processing a received Ethernet frame, and needs to move past the destination MAC, source MAC, EtherType, and potentially VLAN tags (if present) to get to the payload. It's the essential bridging step between the raw frame data and the IP layer or higher protocols.

I'll be looking to understand:

*   **How it handles variable-length headers:** Does it dynamically adjust the SKB's data pointer based on the presence of VLAN tags or other extensions in the Ethernet header? Are there any checks for minimum frame sizes or fragmented frames?
*   **Error handling:** What happens if the SKB is too small to contain a valid Ethernet header? Are there any sanity checks on the EtherType field to ensure it's a known protocol?
*   **Performance considerations:** Is this a hot path? Are there optimizations in place like potentially inlining MAC header offsets to avoid branch conditions? Does it use any techniques like prefetching to improve cache behavior?
*   **Interaction with other functions:** How does it tie into the overall receive path? Is it called directly from the NIC driver, or through some intermediate layer? Does it update any other SKB metadata that downstream layers rely on?

I want to see how this function handles the raw incoming Ethernet frames and translates them into a usable format for higher-level network protocols. It's fundamental to the whole networking process! Let's dig in.



24/3 2026/08/07 13:00 tool read-file
0m Args:
{
  "File": "include/linux/etherdevice.h",
  "FirstLine": 635,
  "LineCount": 20
}

Results:
{
  "Contents": " 635:\t\t\t\tskb-\u003epkt_type = PACKET_OTHERHOST;\n 636:\t\t\t}\n 637:\t\t}\n 638:\t}\n 639:\t\n 640:\tstatic inline struct ethhdr *eth_skb_pull_mac(struct sk_buff *skb)\n 641:\t{\n 642:\t\tstruct ethhdr *eth = (struct ethhdr *)skb-\u003edata;\n 643:\t\n 644:\t\tskb_pull_inline(skb, ETH_HLEN);\n 645:\t\treturn eth;\n 646:\t}\n 647:\t\n 648:\t/**\n 649:\t * eth_skb_pad - Pad buffer to minimum number of octets for Ethernet frame\n 650:\t * @skb: Buffer to pad\n 651:\t *\n 652:\t * An Ethernet frame should have a minimum size of 60 bytes.  This function\n 653:\t * takes short frames and pads them with zeros up to the 60 byte limit.\n 654:\t */\n"
}

25/3 2026/08/07 13:00 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9943
							output: 34
							thoughts: 41
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Analyzing the Network Packet Pull Operation**

Right, `eth_skb_pull_mac`... that's where we're yanking the Ethernet header off a packet, isn't it? The key line there, as I recall, is the call to `skb_pull_inline(skb, ETH_HLEN)`. Okay, so `skb_pull_inline` is clearly the workhorse here. We're expecting `ETH_HLEN`, which is that standard Ethernet header length... 14 or 15 bytes, depending on the VLAN situation. So, the kernel is likely managing a shared network buffer, right? We're taking the memory at the head of the `skb` and advancing the `data` pointer to skip the Ethernet header. Let's dig deeper into the intricacies of what `skb_pull_inline` is doing. Specifically, how it's being handled here. I'm keen to examine how it is working here, since we are handling a buffer inline. Let's see how they do it.



26/3 2026/08/07 13:00 tool grepper
0m Args:
{
  "Expression": "skb_pull_inline",
  "PathPrefix": "include/linux/skbuff.h"
}

Results:
{
  "Output": "include/linux/skbuff.h=2836=static __always_inline void *__skb_pull(struct sk_buff *skb, unsigned int len)\n--\ninclude/linux/skbuff.h-2851-\ninclude/linux/skbuff.h:2852:static inline void *skb_pull_inline(struct sk_buff *skb, unsigned int len)\ninclude/linux/skbuff.h-2853-{\n"
}

27/3 2026/08/07 13:00 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10393
							output: 37
							thoughts: 12
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Diving into `skb_pull_inline`**

Alright, let's get down to it. Time to dissect `skb_pull_inline`. I know this function intimately, but it's always worth a fresh look. I'm going to quickly refresh the mental model of how it operates on an `sk_buff`. I'm going to pay close attention to the implications of inlining, especially around potential performance bottlenecks or memory alignment issues. I'll need to remember the typical use cases and how this function fits into the overall network stack – what layers rely on it, and what assumptions are made about the data structures involved. I'm particularly interested in any recent changes or discussions I've missed – perhaps new architectures or optimizations that have impacted its behavior. It's time to confirm my assumptions and make sure my understanding is completely sharp!



28/3 2026/08/07 13:00 tool read-file
0m Args:
{
  "File": "include/linux/skbuff.h",
  "FirstLine": 2845,
  "LineCount": 20
}

Results:
{
  "Contents": "2845:\t\t\tskb_dump(KERN_ERR, skb, false);\n2846:\t#endif\n2847:\t\t\tBUG();\n2848:\t\t}\n2849:\t\treturn skb-\u003edata += len;\n2850:\t}\n2851:\t\n2852:\tstatic inline void *skb_pull_inline(struct sk_buff *skb, unsigned int len)\n2853:\t{\n2854:\t\treturn unlikely(len \u003e skb-\u003elen) ? NULL : __skb_pull(skb, len);\n2855:\t}\n2856:\t\n2857:\tvoid *skb_pull_data(struct sk_buff *skb, size_t len);\n2858:\t\n2859:\tvoid *__pskb_pull_tail(struct sk_buff *skb, int delta);\n2860:\t\n2861:\tstatic __always_inline enum skb_drop_reason\n2862:\tpskb_may_pull_reason(struct sk_buff *skb, unsigned int len)\n2863:\t{\n2864:\t\tDEBUG_NET_WARN_ON_ONCE(len \u003e INT_MAX);\n"
}

29/3 2026/08/07 13:00 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10964
							output: 33
							thoughts: 687
						

Thoughts:
**Analyzing a Potential Kernel Vulnerability**

Okay, so `skb_pull_inline` returning `NULL` when the requested length (`len`) is greater than the skb's current length (`skb->len`) immediately jumps out at me. And here's the kicker: `eth_skb_pull_mac` blithely *ignores* that `NULL` return! That's a red flag. Specifically, this code:

```c
static inline struct ethhdr *eth_skb_pull_mac(struct sk_buff *skb)
{
	struct ethhdr *eth = (struct ethhdr *)skb->data;

	skb_pull_inline(skb, ETH_HLEN);
	return eth;
}
```

If `ETH_HLEN` (14 bytes for an Ethernet header) is greater than `skb->len`, `skb_pull_inline` returns `NULL` and *doesn't* advance `skb->data`. But `eth_skb_pull_mac` still returns the original, now potentially invalid, pointer `eth`. Then, `eth_type_trans` happily goes ahead and tries to use `eth->h_proto`. This is where things get interesting.

If `ETH_HLEN > skb->len`, does this necessarily mean `skb->len` is less than 14? Absolutely. That means that `eth->h_proto`, which is at offset 12 in the `ethhdr` structure, falls *outside* the valid data region of the skb. If `skb->len` is, say, 10 bytes, then `eth->h_proto` at offset 12 is accessing memory that's beyond the data's boundary.

The question then is, will this be caught? KASAN (Kernel Address Sanitizer) likely *won't* flag this. The allocated buffer is usually much larger than the current data length. However, KMSAN (Kernel Memory Sanitizer) *will* likely catch it because the memory at offset 12 might very well be uninitialized.

Now, what if `skb_headlen(skb)` is less than `ETH_HLEN` *but* `skb->len` is greater than or equal to `ETH_HLEN`? Ah, this means the packet is fragmented, and the linear part is shorter than 14 bytes. In this case, `skb_pull_inline` will call `__skb_pull(skb, ETH_HLEN)`. Let's refresh our memory of what `__skb_pull` does:

```c
static __always_inline void *__skb_pull(struct sk_buff *skb, unsigned int len)
{
	skb->len -= len;
	BUG_ON(skb->len < skb->data_len);
	return skb->data += len;
}
```

If `skb_headlen(skb) < ETH_HLEN`, then `skb->len - skb->data_len < ETH_HLEN`. So, `skb->len - ETH_HLEN < skb->data_len`. The `BUG_ON(skb->len < skb->data_len)` will trigger, preventing further problems.

So, *that* `BUG_ON` will be hit if `skb_headlen(skb) < ETH_HLEN` and `skb->len >= ETH_HLEN`? I need to double-check my understanding of how `skb_headlen` behaves...



30/3 2026/08/07 13:00 tool grepper
0m Args:
{
  "Expression": "skb_headlen",
  "PathPrefix": "include/linux/skbuff.h"
}

Results:
{
  "Output": "include/linux/skbuff.h=2527=static inline bool skb_is_nonlinear(const struct sk_buff *skb)\n--\ninclude/linux/skbuff.h-2531-\ninclude/linux/skbuff.h:2532:static inline unsigned int skb_headlen(const struct sk_buff *skb)\ninclude/linux/skbuff.h-2533-{\n--\ninclude/linux/skbuff.h=2546=static inline unsigned int skb_pagelen(const struct sk_buff *skb)\ninclude/linux/skbuff.h-2547-{\ninclude/linux/skbuff.h:2548:\treturn skb_headlen(skb) + __skb_pagelen(skb);\ninclude/linux/skbuff.h-2549-}\n--\ninclude/linux/skbuff.h=2862=pskb_may_pull_reason(struct sk_buff *skb, unsigned int len)\n--\ninclude/linux/skbuff.h-2866-\ninclude/linux/skbuff.h:2867:\tif (likely(len \u003c= skb_headlen(skb)))\ninclude/linux/skbuff.h-2868-\t\treturn SKB_NOT_DROPPED_YET;\n--\ninclude/linux/skbuff.h-2872-\ninclude/linux/skbuff.h:2873:\tif (unlikely(!__pskb_pull_tail(skb, len - skb_headlen(skb))))\ninclude/linux/skbuff.h-2874-\t\treturn SKB_DROP_REASON_NOMEM;\n--\ninclude/linux/skbuff.h=4325=skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer)\n--\ninclude/linux/skbuff.h-4327-\treturn __skb_header_pointer(skb, offset, len, skb-\u003edata,\ninclude/linux/skbuff.h:4328:\t\t\t\t    skb_headlen(skb), buffer);\ninclude/linux/skbuff.h-4329-}\n--\ninclude/linux/skbuff.h=4344=skb_pointer_if_linear(const struct sk_buff *skb, int offset, int len)\ninclude/linux/skbuff.h-4345-{\ninclude/linux/skbuff.h:4346:\tif (likely(skb_headlen(skb) - offset \u003e= len))\ninclude/linux/skbuff.h-4347-\t\treturn skb-\u003edata + offset;\n"
}

31/3 2026/08/07 13:00 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13130
							output: 37
							thoughts: 11
						

Thoughts:
Okay, let's dive into this. Here's how I'd approach this, as though these are *my* thoughts:

**Analyzing `skb_headlen`**

Alright, time to get into the details. My initial thought process would be something like this:

"Okay, so we're looking at `skb_headlen`. Straight away, I'm thinking, this must relate to the Linux kernel's `sk_buff` structure, the workhorse for network packet handling.  `headlen` suggests we're dealing with the size of the header portion of an `skb`. That's the most *likely* interpretation, given my past experience and the common naming conventions. Let's delve in and see if this intuition is correct."



32/3 2026/08/07 13:00 tool read-file
0m Args:
{
  "File": "include/linux/skbuff.h",
  "FirstLine": 2525,
  "LineCount": 15
}

Results:
{
  "Contents": "2525:\t\n2526:\t\n2527:\tstatic inline bool skb_is_nonlinear(const struct sk_buff *skb)\n2528:\t{\n2529:\t\treturn skb-\u003edata_len;\n2530:\t}\n2531:\t\n2532:\tstatic inline unsigned int skb_headlen(const struct sk_buff *skb)\n2533:\t{\n2534:\t\treturn skb-\u003elen - skb-\u003edata_len;\n2535:\t}\n2536:\t\n2537:\tstatic inline unsigned int __skb_pagelen(const struct sk_buff *skb)\n2538:\t{\n2539:\t\tunsigned int i, len = 0;\n"
}

33/3 2026/08/07 13:00 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13554
							output: 32
							thoughts: 1699
						

Thoughts:
**Analyzing the Network Packet Processing Vulnerability**

Okay, so I've been digging into this kernel issue related to Ethernet header handling and it's quite interesting. It seems the core problem arises when dealing with network packets where the linear part of the SKB (Socket Buffer) is shorter than the Ethernet header length (`ETH_HLEN`). Let's break this down:

First, `skb_headlen(skb)` is essentially `skb->len - skb->data_len`. The vulnerability pops up if this value is less than `ETH_HLEN`. If we have `skb->len >= ETH_HLEN`, then `skb_pull_inline` gets called which ultimately calls `__skb_pull`. However, if `skb->len - skb->data_len < ETH_HLEN`, then `__skb_pull` triggers a `BUG_ON` because the length calculation becomes invalid (`skb->len` would be less than `skb->data_len` after the subtraction). Standard fuzzing, specifically syzkaller (without KMSAN), should catch this `BUG_ON`.

But, what if `skb->len < ETH_HLEN`? In this scenario, `skb_pull_inline` returns `NULL`, bypassing `__skb_pull`. Crucially, this avoids the `BUG_ON`. Here, `eth_skb_pull_mac` returns `skb->data`, and `eth_type_trans` attempts to read `eth->h_proto`. The key is that since `skb->len < ETH_HLEN`, `eth->h_proto` is beyond the end of what's considered the "valid" packet data.

Initially, I was worried about an out-of-bounds read, but the data is still within the allocated buffer, just beyond the reported `skb->len`. KASAN won't catch this, because it's not truly out-of-bounds. However, KMSAN *will* catch it because the memory beyond `skb->len` is uninitialized. The kernel is reading uninitialized memory.

This read of the uninitialized value becomes the `skb->protocol`. It then gets used in a later step and becomes a reportable KMSAN issue. This seems to be the primary way to catch it because KASAN doesn't track uninitialized memory, and there's no actual out-of-bounds access.

The consequences could be interesting. If `eth->h_proto` is uninitialized, the packet might get routed to a random protocol handler, potentially leading to packet drops by handlers such as IPv4 that check `pskb_may_pull`. The packet could be dropped due to an invalid header length. No memory corruption, just a read of uninitialized memory, but only KMSAN can catch this.

Now, let's revisit the `BUG_ON` scenario. It's triggered when `skb->len >= ETH_HLEN` but `skb_headlen(skb) < ETH_HLEN`. This usually happens when a MAC header is split across the linear part and the fragments of the packet. Syzkaller catches this. But if `skb->len < ETH_HLEN`, then the `BUG_ON` is avoided, exposing the uninitialized memory. The proposed patch mitigates *both* cases by checking if `skb_headlen(skb) < ETH_HLEN`, effectively preventing both the `BUG_ON` and the uninitialized memory read.

If standard fuzzing found the issue via `BUG_ON`, that means we don't necessarily need KMSAN. Wait! Is that right?

The patch itself indicates this: `if (unlikely(skb_headlen(skb) < ETH_HLEN))`. This will warn, and return zero.

So, if syzkaller found it as a `BUG_ON`, it would be reported like `kernel BUG at include/linux/skbuff.h:2847!`, which is in `__skb_pull`. But that `BUG_ON` is only triggered if `skb->len >= ETH_HLEN` and `skb_headlen(skb) < ETH_HLEN`. Can a device realistically *receive* such a packet before `eth_type_trans`?

Usually, devices receive the MAC header in the linear part. However, a virtual device like tun/tap or some other virtual device *could* inject a packet with a short linear part and fragments, thus triggering the `BUG_ON`.

Conversely, if a device injects a packet with `skb->len < ETH_HLEN` and no fragments, it *won't* trigger the `BUG_ON`, but it *will* trigger


34/3 2026/08/07 13:01 tool grepper
0m Args:
{
  "Expression": "eth_type_trans",
  "PathPrefix": "drivers/net/tun.c"
}

Results:
{
  "Output": "drivers/net/tun.c=1695=static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,\n--\ndrivers/net/tun.c-1849-\t\t}\ndrivers/net/tun.c:1850:\t\tskb-\u003eprotocol = eth_type_trans(skb, tun-\u003edev);\ndrivers/net/tun.c-1851-\t\tbreak;\n--\ndrivers/net/tun.c=2383=static int tun_xdp_one(struct tun_struct *tun,\n--\ndrivers/net/tun.c-2471-\ndrivers/net/tun.c:2472:\tskb-\u003eprotocol = eth_type_trans(skb, tun-\u003edev);\ndrivers/net/tun.c-2473-\tskb_reset_network_header(skb);\n"
}

35/3 2026/08/07 13:01 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 16537
							output: 36
							thoughts: 37
						

Thoughts:
Okay, let's dive into this. Here's how I'd approach that kernel code:

**Checking `tun_get_user` for Minimum Frame Size Violations**

Alright, I need to verify how `tun_get_user` in `drivers/net/tun.c` handles packets with lengths shorter than the standard Ethernet header length (`ETH_HLEN`). I know that Ethernet frames *must* have a minimum size, and if we're seeing an issue, it's possible that `tun_get_user` is allowing or misinterpreting short frames coming from userspace. I'll need to look carefully at how the function calculates buffer sizes, copies data, and performs any length checks. Specifically, I'll be looking for comparisons against `ETH_HLEN` or other size constraints. It's also important to consider if any other layers are relied on to handle the size constraints. I need to make sure that no exploitable condition exists due to an undersized frame being passed through. My goal is to determine if a vulnerability exists or not, and how it can be mitigated.



36/3 2026/08/07 13:01 tool read-file
0m Args:
{
  "File": "drivers/net/tun.c",
  "FirstLine": 1840,
  "LineCount": 20
}

Results:
{
  "Contents": "1840:\t\t\tskb_reset_mac_header(skb);\n1841:\t\t\tskb-\u003eprotocol = pi.proto;\n1842:\t\t\tskb-\u003edev = tun-\u003edev;\n1843:\t\t\tbreak;\n1844:\t\tcase IFF_TAP:\n1845:\t\t\tif (frags \u0026\u0026 !pskb_may_pull(skb, ETH_HLEN)) {\n1846:\t\t\t\terr = -ENOMEM;\n1847:\t\t\t\tdrop_reason = SKB_DROP_REASON_HDR_TRUNC;\n1848:\t\t\t\tgoto drop;\n1849:\t\t\t}\n1850:\t\t\tskb-\u003eprotocol = eth_type_trans(skb, tun-\u003edev);\n1851:\t\t\tbreak;\n1852:\t\t}\n1853:\t\n1854:\t\t/* copy skb_ubuf_info for callback when skb has no error */\n1855:\t\tif (zerocopy) {\n1856:\t\t\tskb_zcopy_init(skb, msg_control);\n1857:\t\t} else if (msg_control) {\n1858:\t\t\tstruct ubuf_info *uarg = msg_control;\n1859:\t\t\tuarg-\u003eops-\u003ecomplete(NULL, uarg, false);\n"
}

37/3 2026/08/07 13:01 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17181
							output: 33
							thoughts: 431
						

Thoughts:
**Vulnerability Discovered in `tun_get_user`**

Okay, let's break this down. I'm looking at `tun_get_user` and specifically this section:

```c
		case IFF_TAP:
			if (frags && !pskb_may_pull(skb, ETH_HLEN)) {
				err = -ENOMEM;
				drop_reason = SKB_DROP_REASON_HDR_TRUNC;
				goto drop;
			}
			skb->protocol = eth_type_trans(skb, tun->dev);
			break;
```

Interesting. It appears that the `pskb_may_pull` check is *only* performed if the `frags` flag is set. That means, if `frags` is false, and we're dealing with a linear skb (meaning no fragmentation), there's *no* length check against `ETH_HLEN` (which is 14 bytes) for the incoming packet.

So, if a malicious user injects a *linear* packet with a length of less than 14 bytes into the TAP interface, the code will happily continue and call `eth_type_trans`. The critical issue here is that since `skb->len` and `skb_headlen(skb)` are both less than `ETH_HLEN`, the `eth_type_trans` function will attempt to read beyond the provided data in the skb.

My initial thought was, will this trigger a `BUG_ON` in `skb_pull_inline`? No, because `skb_pull_inline` returns NULL if you try to pull more data than available which protects the code from the bug.

I then realized that `eth_skb_pull_mac` simply returns `skb->data` and then `eth_type_trans` attempts to read `eth->h_proto` which is uninitialized!

Could KASAN catch this? I initially thought, probably not. The linear buffer allocated by `tun_get_user` is usually quite large – a few hundred bytes at least, or maybe a full page. So, even if the user provides less than 14 bytes, the "extra" bytes we are reading would likely be within the bounds of the allocated buffer. I'd expect any read at offset 12 to be within the buffer.

However, after a second pass, I think KMSAN might catch this! The user only provided a small amount of data (less than 14 bytes). The rest of the buffer is uninitialized. We need to confirm whether `tun_alloc_skb` zeroes the buffer (which would make this a read of zeroes rather than uninitialized memory) but either way, since the provided data isn't enough, we are reading out-of-bounds.



38/3 2026/08/07 13:01 tool grepper
0m Args:
{
  "Expression": "tun_alloc_skb",
  "PathPrefix": "drivers/net/tun.c"
}

Results:
{
  "Output": "drivers/net/tun.c=1392=static struct sk_buff *tun_napi_alloc_frags(struct tun_file *tfile,\n--\ndrivers/net/tun.c-1448- * linear is a hint as to how much to copy (usually headers). */\ndrivers/net/tun.c:1449:static struct sk_buff *tun_alloc_skb(struct tun_file *tfile,\ndrivers/net/tun.c-1450-\t\t\t\t     size_t prepad, size_t len,\n--\ndrivers/net/tun.c=1695=static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,\n--\ndrivers/net/tun.c-1795-\ndrivers/net/tun.c:1796:\t\t\tskb = tun_alloc_skb(tfile, align, copylen, linear,\ndrivers/net/tun.c-1797-\t\t\t\t\t    noblock);\n"
}

39/3 2026/08/07 13:01 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 18515
							output: 36
							thoughts: 12
						

Thoughts:
Okay, here's my interpretation of that thought process, assuming I'm deeply familiar with networking and kernel internals, and especially the TUN/TAP driver:

**Checking `tun_alloc_skb`**

Right, let's dive right in. The phrase "Let's check `tun_alloc_skb`" immediately signals a specific area of interest: the allocation of SKBs (Socket Buffers) within the TUN driver. This is a critical point. `tun_alloc_skb` is the function responsible for creating the memory structures that will hold the network packets being sent to and received from the user-space application via the TUN interface. A deep understanding of this is paramount when dealing with network performance, potential memory leaks, or any odd behavior in a TUN-based setup.

My immediate thoughts are, where exactly do I want to "check" this function? Is this about the implementation itself? Perhaps I'm looking at potential performance bottlenecks within that function. Are there any contention issues? Do I want to check its interaction with other kernel components such as DMA or NUMA? Is there a memory allocation strategy I should examine closely for possible improvements or misconfigurations?

I probably have a specific concern in mind. Is there a performance problem with a particular workload? Are packets being dropped? Are there any reports of memory leaks? Is it about overall throughput, latency, or specific error messages related to buffer allocation? The context of the check would influence where to begin the inspection. I'd need to consider the system's hardware configuration, the user-space application, and any other relevant factors to formulate a useful hypothesis about `tun_alloc_skb`.

Ultimately, it would depend on where I would like to look, be it the memory allocation strategy, buffer alignment issues, or concurrent access to data structures. Whatever the concern, `tun_alloc_skb` is the place to start.



40/3 2026/08/07 13:01 tool read-file
0m Args:
{
  "File": "drivers/net/tun.c",
  "FirstLine": 1449,
  "LineCount": 30
}

Results:
{
  "Contents": "1449:\tstatic struct sk_buff *tun_alloc_skb(struct tun_file *tfile,\n1450:\t\t\t\t\t     size_t prepad, size_t len,\n1451:\t\t\t\t\t     size_t linear, int noblock)\n1452:\t{\n1453:\t\tstruct sock *sk = tfile-\u003esocket.sk;\n1454:\t\tstruct sk_buff *skb;\n1455:\t\tint err;\n1456:\t\n1457:\t\t/* Under a page?  Don't bother with paged skb. */\n1458:\t\tif (prepad + len \u003c PAGE_SIZE)\n1459:\t\t\tlinear = len;\n1460:\t\n1461:\t\tif (len - linear \u003e MAX_SKB_FRAGS * (PAGE_SIZE \u003c\u003c PAGE_ALLOC_COSTLY_ORDER))\n1462:\t\t\tlinear = len - MAX_SKB_FRAGS * (PAGE_SIZE \u003c\u003c PAGE_ALLOC_COSTLY_ORDER);\n1463:\t\tskb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock,\n1464:\t\t\t\t\t   \u0026err, PAGE_ALLOC_COSTLY_ORDER);\n1465:\t\tif (!skb)\n1466:\t\t\treturn ERR_PTR(err);\n1467:\t\n1468:\t\tskb_reserve(skb, prepad);\n1469:\t\tskb_put(skb, linear);\n1470:\t\tskb-\u003edata_len = len - linear;\n1471:\t\tskb-\u003elen += len - linear;\n1472:\t\n1473:\t\treturn skb;\n1474:\t}\n1475:\t\n1476:\tstatic void tun_rx_batched(struct tun_struct *tun, struct tun_file *tfile,\n1477:\t\t\t\t   struct sk_buff *skb, int more)\n1478:\t{\n"
}

41/3 2026/08/07 13:01 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19472
							output: 34
							thoughts: 393
						

Thoughts:
**Analyzing a Potential KMSAN Bug**

Okay, so here's what I'm thinking. This `sock_alloc_send_pskb` function is allocating the socket buffer (skb), but it *doesn't* zero out the data buffer. That means everything beyond the initial 'linear' data, which is our 'len', is just uninitialized memory. Now, `tun_get_user` then copies 'len' bytes from user space into this buffer. The bytes beyond 'len' *remain* uninitialized. That's the key.

Then, we have `eth_type_trans` reading `eth->h_proto` at offset 12. If our 'len' is less than 14 bytes, it's reading uninitialized memory! This *screams* KMSAN bug, a classic one.

Wait, did syzkaller actually find this one with KASAN? If KASAN isn't catching it, and that `BUG_ON` isn't firing, then the only way syzkaller would have found it would be through KMSAN. That's interesting.

Now, wait, what if `frags` is true? If `frags` is true, then `tun_get_user` calls `pskb_may_pull(skb, ETH_HLEN)`. If that fails, the packet gets dropped, no harm. If it *succeeds*, we know that `skb->len >= ETH_HLEN` and `skb_headlen(skb) >= ETH_HLEN`. So, even if `frags` is true, the `BUG_ON` isn't triggered in `tun_get_user`!

Wait, wait. If `frags` is true, `pskb_may_pull` ensures `skb_headlen(skb) >= ETH_HLEN`. That's the real point. So `BUG_ON` is never triggered from `tun_get_user`.

Okay, is there another caller of `eth_type_trans` that could be triggered this way? What about `packet_snd` in `net/packet/af_packet.c`? That seems like a logical next step to examine.



42/3 2026/08/07 13:01 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "eth_type_trans",
  "PathPrefix": "net/packet/af_packet.c"
}

Results:
{
  "error": "no matches"
}

43/3 2026/08/07 13:01 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 20394
							output: 35
							thoughts: 41
						

Thoughts:
Okay, let's see. My mind is jumping around a bit here, trying to piece this together...

**Investigating Packet Reception Flow**

Hold on a second... I was under the impression that `packet_snd` was the primary entry point for outbound packet processing, but now it occurs to me that it **doesn't** actually call `eth_type_trans` directly, which is where the Ethernet type determination usually happens. That's a key piece missing from my mental model! Where *does* that happen in the typical path?

Alright, let's back up and rethink this. Maybe it's not the transmission side I need to focus on right now. Perhaps my attention should shift to the virtual network device side for reception. I need to refresh my memory on what function is used for incoming packet processing. I'm going to look at `drivers/net/virtio_net.c`, specifically `virtnet_receive`. It feels like that's the more relevant area given the flow of traffic I'm expecting. Hopefully, `virtnet_receive` is where the packet type is determined.



44/3 2026/08/07 13:01 tool grepper
0m Args:
{
  "Expression": "eth_type_trans",
  "PathPrefix": "drivers/net/virtio_net.c"
}

Results:
{
  "Output": "drivers/net/virtio_net.c=2030=static void mergeable_buf_free(struct receive_queue *rq, int num_buf,\n--\ndrivers/net/virtio_net.c-2056- *  2. xdp_build_skb_from_frame() does more checks that we don't need,\ndrivers/net/virtio_net.c:2057: *     like eth_type_trans() (which virtio-net does in receive_buf()).\ndrivers/net/virtio_net.c-2058- */\n--\ndrivers/net/virtio_net.c=2500=static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue *rq,\n--\ndrivers/net/virtio_net.c-2528-\tskb_record_rx_queue(skb, vq2rxq(rq-\u003evq));\ndrivers/net/virtio_net.c:2529:\tskb-\u003eprotocol = eth_type_trans(skb, dev);\ndrivers/net/virtio_net.c-2530-\tpr_debug(\"Receiving skb proto 0x%04x len %i type %i\\n\",\n"
}

45/3 2026/08/07 13:01 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21018
							output: 39
							thoughts: 18
						

Thoughts:
Okay, here's my interpretation of that thought process, from an expert's perspective:

**Checking the Foundation: Receive Completion Indicators**

Right, before diving deeper, I need to make sure I'm grounded. My immediate thought is to zero in on the fundamental indicators of receive completion. Specifically, I need to check two critical elements that likely signal whether a receive operation has finalized. This could be checking the state of a data buffer (`receive_buf`) itself. Or a dedicated flag or variable indicating that a reception is `virtnet_receive_done`. Essentially I need to confirm whether data is available or if the receiver is finished for a new incoming data. Without confirming either the buffer status or reception status, there's no sense in proceeding with any deeper analysis of the network interaction. This preliminary step is crucial to avoid chasing ghosts or drawing conclusions based on incomplete data.



46/3 2026/08/07 13:02 tool read-file
0m Args:
{
  "File": "drivers/net/virtio_net.c",
  "FirstLine": 2515,
  "LineCount": 20
}

Results:
{
  "Contents": "2515:\t\t\tgoto frame_err;\n2516:\t\t}\n2517:\t\n2518:\t\tif (virtio_net_hdr_tnl_to_skb(skb, \u0026hdr-\u003etnl_hdr, vi-\u003erx_tnl,\n2519:\t\t\t\t\t      vi-\u003erx_tnl_csum,\n2520:\t\t\t\t\t      virtio_is_little_endian(vi-\u003evdev))) {\n2521:\t\t\tnet_warn_ratelimited(\"%s: bad gso: type: %x, size: %u, flags %x tunnel %d tnl csum %d\\n\",\n2522:\t\t\t\t\t     dev-\u003ename, hdr-\u003ehdr.gso_type,\n2523:\t\t\t\t\t     hdr-\u003ehdr.gso_size, hdr-\u003ehdr.flags,\n2524:\t\t\t\t\t     vi-\u003erx_tnl, vi-\u003erx_tnl_csum);\n2525:\t\t\tgoto frame_err;\n2526:\t\t}\n2527:\t\n2528:\t\tskb_record_rx_queue(skb, vq2rxq(rq-\u003evq));\n2529:\t\tskb-\u003eprotocol = eth_type_trans(skb, dev);\n2530:\t\tpr_debug(\"Receiving skb proto 0x%04x len %i type %i\\n\",\n2531:\t\t\t ntohs(skb-\u003eprotocol), skb-\u003elen, skb-\u003epkt_type);\n2532:\t\n2533:\t\tnapi_gro_receive(\u0026rq-\u003enapi, skb);\n2534:\t\treturn;\n"
}

47/3 2026/08/07 13:02 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21670
							output: 34
							thoughts: 149
						

Thoughts:
**Analyzing a Virtio-Net Packet Vulnerability**

Okay, so I'm digging into how virtio-net handles incoming packets, specifically those that might be malformed. My initial thought is about what happens when virtio-net gets a packet shorter than 14 bytes. It jumps to `eth_type_trans`, right? The question is, will that trigger a `BUG_ON`? I'm trying to be very precise here and avoid uninitialized memory reads.

I'm considering a linear packet now. If the packet is linear, I think it's likely that `skb->len` would be less than `ETH_HLEN`, which is 14. Also `skb_headlen(skb)` will be less than `ETH_HLEN`. So, in this scenario, I'm pretty sure that it won't trigger a `BUG_ON`. However, it WILL lead to uninitialized memory being read, which is definitely something I need to keep in mind.

Now I'm focusing on fragmented packets. With fragmentation, `receive_mergeable` comes into play. Could `receive_mergeable` possibly build an skb where `skb->len` is greater than or equal to `ETH_HLEN` but `skb_headlen(skb)` is less than `ETH_HLEN`? That seems like a potential vulnerability. I need to investigate that function to make sure it's handled. So, that's what I'm going to look at next – can this function create this condition?



48/3 2026/08/07 13:02 tool grepper
0m Args:
{
  "Expression": "receive_mergeable",
  "PathPrefix": "drivers/net/virtio_net.c"
}

Results:
{
  "Output": "drivers/net/virtio_net.c=2186=static void *mergeable_xdp_get_buf(struct virtnet_info *vi,\n--\ndrivers/net/virtio_net.c-2263-\ndrivers/net/virtio_net.c:2264:static struct sk_buff *receive_mergeable_xdp(struct net_device *dev,\ndrivers/net/virtio_net.c-2265-\t\t\t\t\t     struct virtnet_info *vi,\n--\ndrivers/net/virtio_net.c=2326=static struct sk_buff *virtnet_skb_append_frag(struct receive_queue *rq,\n--\ndrivers/net/virtio_net.c-2375-\ndrivers/net/virtio_net.c:2376:static struct sk_buff *receive_mergeable(struct net_device *dev,\ndrivers/net/virtio_net.c-2377-\t\t\t\t\t struct virtnet_info *vi,\n--\ndrivers/net/virtio_net.c-2405-\t\tif (xdp_prog) {\ndrivers/net/virtio_net.c:2406:\t\t\thead_skb = receive_mergeable_xdp(dev, vi, rq, xdp_prog, buf, ctx,\ndrivers/net/virtio_net.c-2407-\t\t\t\t\t\t\t len, xdp_xmit, stats);\n--\ndrivers/net/virtio_net.c=2541=static void receive_buf(struct virtnet_info *vi, struct receive_queue *rq,\n--\ndrivers/net/virtio_net.c-2577-\t\tflags = ((struct virtio_net_common_hdr *)buf)-\u003ehdr.flags;\ndrivers/net/virtio_net.c:2578:\t\tskb = receive_mergeable(dev, vi, rq, buf, ctx, len, xdp_xmit,\ndrivers/net/virtio_net.c-2579-\t\t\t\t\tstats);\n"
}

49/3 2026/08/07 13:02 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22686
							output: 33
							thoughts: 1555
						

Thoughts:
**Analyzing the Implications of a Network Packet Vulnerability**

Okay, so here's the situation as I see it. It looks like with virtio-net, if a packet arrives fragmented and the first buffer is tiny, then `receive_mergeable` builds an SKB where the header is small, but the total length is larger.  This then gets passed to `eth_type_trans`. The problem surfaces when `skb_pull_inline` calls `__skb_pull`.  `__skb_pull` subtracts the Ethernet header length from `skb->len`.  If the header length is greater than the initial linear data, we hit a `BUG_ON` because the new `skb->len` would be less than `skb->data_len`, indicating the packet's length is smaller than its data.

Now, syzkaller *can* find this `BUG_ON` scenario without KMSAN, which is interesting. But, I see that a patch exists which checks `skb_headlen(skb)` against `ETH_HLEN`, preventing both the `BUG_ON` when `skb->len >= ETH_HLEN` and the uninitialized memory read when `skb->len < ETH_HLEN`.

So the question is, did syzkaller discover the `BUG_ON` or the KMSAN issue? If it was the `BUG_ON`, we don't *strictly* need KMSAN, right? Standard fuzzing tools would likely catch that. But, wait, is there a case where `skb->len < ETH_HLEN` is the *only* way to trigger a bug? Ah, yes! If a device only handles linear packets, that condition is the *only* way.

Take `tun_get_user` with `IFF_TAP` and `frags == false` for example, which creates a linear SKB with `skb->len < ETH_HLEN`.  No `BUG_ON` here. It calls `eth_type_trans` and proceeds to read uninitialized memory. KMSAN catches that. Syzkaller, without KMSAN, won't find *that* bug because it might branch on the uninitialized value, but no crash. But, if `eth_type_trans` is common, and the fuzzer found a `BUG_ON` through virtio-net or another driver creating fragmented SKBs with short linear parts, then it essentially found the underlying vulnerability through another driver, and we don't need KMSAN to find that.

Okay, back to `tun_get_user`. If `frags == true`, it *does* check `pskb_may_pull`, so we're safe there. But with `frags == false`, it creates a linear SKB, `skb->len < ETH_HLEN`, calls `eth_type_trans`, and the bug is that the system now reads uninitialized memory. This *only* gets caught by KMSAN, which is important.

Are there other drivers at risk? Many drivers assume packets smaller than 14 bytes are dropped. Virtual drivers like `veth`, `loopback`, and `wireguard` *might* not. The patch fixes this. So, if a driver creates a linear SKB with `skb->len < ETH_HLEN`, KMSAN catches it. Fragmented SKBs with a short linear part? `BUG_ON`.

The core question is this patch and uninitialized memory caught *only* by KMSAN. Yes. Reading uninitialized memory into `skb->protocol` (allocated by `kmalloc` or page allocator and not zeroed) can lead to routing the packet to an arbitrary protocol handler. This is a use-of-uninitialized value! KMSAN is *designed* to find this.

And wait a second. If `skb->protocol` is uninitialized, it could go to a packet socket (AF_PACKET). The user *could* then receive the packet and read the uninitialized value from `sll_protocol` in the `sockaddr_ll` returned by `recvfrom`. That is a direct kernel-to-user info-leak of 2 bytes! Wow. I need to verify this... time to check `packet_rcv` in `net/packet/af_packet.c`.



50/3 2026/08/07 13:02 tool grepper
0m Args:
{
  "Expression": "sll_protocol",
  "PathPrefix": "net/packet/af_packet.c"
}

Results:
{
  "Output": "net/packet/af_packet.c=2133=static int packet_rcv(struct sk_buff *skb, struct net_device *dev,\n--\nnet/packet/af_packet.c-2206-\nnet/packet/af_packet.c:2207:\t/* sll-\u003esll_family and sll-\u003esll_protocol are set in packet_recvmsg().\nnet/packet/af_packet.c-2208-\t * Use their space for storing the original skb length.\n--\nnet/packet/af_packet.c=2246=static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,\n--\nnet/packet/af_packet.c-2475-\tsll-\u003esll_hatype = dev-\u003etype;\nnet/packet/af_packet.c:2476:\tsll-\u003esll_protocol = (sk-\u003esk_type == SOCK_DGRAM) ?\nnet/packet/af_packet.c-2477-\t\tvlan_get_protocol_dgram(skb) : skb-\u003eprotocol;\n--\nnet/packet/af_packet.c=2736=static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)\n--\nnet/packet/af_packet.c-2776-\t\t\tgoto out;\nnet/packet/af_packet.c:2777:\t\tproto\t= saddr-\u003esll_protocol;\nnet/packet/af_packet.c-2778-\t\tdev = dev_get_by_index(sock_net(\u0026po-\u003esk), saddr-\u003esll_ifindex);\n--\nnet/packet/af_packet.c=2966=static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)\n--\nnet/packet/af_packet.c-2995-\t\t\tgoto out;\nnet/packet/af_packet.c:2996:\t\tproto\t= saddr-\u003esll_protocol;\nnet/packet/af_packet.c-2997-\t\tdev = dev_get_by_index(sock_net(sk), saddr-\u003esll_ifindex);\n--\nnet/packet/af_packet.c=3333=static int packet_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len)\n--\nnet/packet/af_packet.c-3346-\nnet/packet/af_packet.c:3347:\treturn packet_do_bind(sk, NULL, sll-\u003esll_ifindex, sll-\u003esll_protocol);\nnet/packet/af_packet.c-3348-}\n--\nnet/packet/af_packet.c=3438=static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,\n--\nnet/packet/af_packet.c-3510-\t\tsll-\u003esll_family = AF_PACKET;\nnet/packet/af_packet.c:3511:\t\tsll-\u003esll_protocol = (sock-\u003etype == SOCK_DGRAM) ?\nnet/packet/af_packet.c-3512-\t\t\tvlan_get_protocol_dgram(skb) : skb-\u003eprotocol;\n--\nnet/packet/af_packet.c=3623=static int packet_getname(struct socket *sock, struct sockaddr *uaddr,\n--\nnet/packet/af_packet.c-3637-\tsll-\u003esll_ifindex = ifindex;\nnet/packet/af_packet.c:3638:\tsll-\u003esll_protocol = READ_ONCE(po-\u003enum);\nnet/packet/af_packet.c-3639-\tsll-\u003esll_pkttype = 0;\n"
}

51/3 2026/08/07 13:02 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26149
							output: 33
							thoughts: 957
						

Thoughts:
**A Deep Dive into a Kernel Information Leak**

Okay, so I've stumbled upon something interesting in `packet_recvmsg`. If `skb->protocol` isn't initialized, it's copied directly into `sll->sll_protocol` and sent straight to user space via `msg->msg_name`—specifically, the `sockaddr_ll` structure. This looks like a straightforward kernel-to-user information leak, and the kicker? It's uninitialized kernel memory, two bytes of it! That means KASAN won't catch it. It's KMSAN's territory!

But, hold on a second... is the memory *actually* uninitialized? Let's trace it back. When `tun_alloc_skb` allocates the `skb`, it goes through `sock_alloc_send_pskb`, which uses `alloc_skb_with_frags`. This, in turn, uses `kmem_cache_alloc_node` for the `skb` itself and `kmalloc_reserve` for the data buffer. And the crucial bit? `kmalloc_reserve` uses `kmalloc_node_track_caller` (or something similar), which *doesn't* zero the memory unless `__GFP_ZERO` is passed. And, guess what? `sock_alloc_send_pskb` *doesn't* pass `__GFP_ZERO`. So, yes, the data buffer is uninitialized.

Now, `tun_get_user` copies `len` bytes from user space using `skb_copy_datagram_from_iter`. The bytes beyond `len` remain untouched—still uninitialized. Then, `eth_type_trans` comes into play, reading `eth->h_proto` at offset 12. If `len < 14`, it's reading uninitialized memory. That uninitialized value gets assigned to `skb->protocol`. The packet then passes through `netif_rx` or `netif_receive_skb`.

Here's where it all connects: If there's an AF_PACKET socket sniffing the interface, it receives this packet. The user then reads from the AF_PACKET socket and gets those juicy, uninitialized 2 bytes in `sll_protocol`. Bingo! This is a completely legitimate info-leak, and it's solely caught by KMSAN.

Let's do some more digging. Can `tun_get_user` actually send a packet with `len < 14` to `eth_type_trans`?

Looking at `tun_get_user`: I can see the conditional:
```c
	if (tun->flags & IFF_VNET_HDR) {
		// reads vnet header
	}

	// ...
	skb = tun_alloc_skb(tfile, align, copylen, linear, noblock);
	// ...
	err = skb_copy_datagram_from_iter(skb, 0, from, len);
	// ...
	switch (tun->flags & TUN_TYPE_MASK) {
	case IFF_TUN:
		// ...
		break;
	case IFF_TAP:
		if (frags && !pskb_may_pull(skb, ETH_HLEN)) {
			// ...
		}
		skb->protocol = eth_type_trans(skb, tun->dev);
		break;
	}
```

Yes, it looks like if `tun->flags & TUN_TYPE_MASK` is set to `IFF_TAP`, and `frags` is false (which is the case if the packet fits within the linear portion, meaning if its less than 14 bytes), then it calls `eth_type_trans(skb, tun->dev)` without a check if `skb->len >= ETH_HLEN`.

So, a user can write, say, 10 bytes to a TAP device. The TAP device will allocate an `skb`, copy 10 bytes, and then call `eth_type_trans`. The `eth_type_trans` function reads those 2 bytes of uninitialized memory at offset 12. Then, the packet gets injected into the network stack. An AF_PACKET socket can receive it and leak the 2 bytes. This is, indeed, a perfect KMSAN bug.

Okay, now, are there any other calls to `eth_type_trans` with `skb->len < ETH_HLEN`? I need to check. Let's look at `loopback_xmit` in `drivers/net/loopback.c`.



52/3 2026/08/07 13:02 tool grepper
0m Args:
{
  "Expression": "eth_type_trans",
  "PathPrefix": "drivers/net/loopback.c"
}

Results:
{
  "Output": "drivers/net/loopback.c=70=static netdev_tx_t loopback_xmit(struct sk_buff *skb,\n--\ndrivers/net/loopback.c-86-\ndrivers/net/loopback.c:87:\tskb-\u003eprotocol = eth_type_trans(skb, dev);\ndrivers/net/loopback.c-88-\n"
}

53/3 2026/08/07 13:02 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28259
							output: 36
							thoughts: 332
						

Thoughts:
**Analyzing the Loopback Transmission Path**

Okay, so I'm looking at the `loopback_xmit` function, and my initial thoughts are around how it handles incoming packets. First, it timestamps the `skb` and clears the timestamp to avoid issues with different clock bases. It then orphans the `skb` and ensures the destination is refcounted, which is pretty standard. The key part is that `eth_type_trans` is called to determine the protocol.

My concern here is regarding packets with unusual sizes. I know that if a packet with a length less than `ETH_HLEN` is sent over loopback, `eth_type_trans` will be called. That's fine in most scenarios because packets generated by the kernel are usually well-formed. However, I'm thinking about the edge cases. What if a raw packet is sent using an AF_PACKET socket with a short length, less than the minimum Ethernet frame size, which is 14 bytes? In that case, `loopback_xmit` would be called with an invalid packet.

The question then becomes, how does that even happen? Does the packet socket code prevent it from happening? Does it filter out these shorter packets? I seem to recall that packet send, `packet_snd`, does some length checks. Let me quickly verify if the packet send path checks whether the length is at least `dev->hard_header_len` before it gets to this point. That would explain it. I need to refresh my memory and check `packet_snd` to confirm the behavior.



54/3 2026/08/07 13:02 tool grepper
0m Args:
{
  "Expression": "dev-\u003ehard_header_len",
  "PathPrefix": "net/packet/af_packet.c"
}

Results:
{
  "Output": "net/packet/af_packet.c=541=static u16 vlan_get_tci(const struct sk_buff *skb, struct net_device *dev)\n--\nnet/packet/af_packet.c-555-\t */\nnet/packet/af_packet.c:556:\tif (dev-\u003emin_header_len == dev-\u003ehard_header_len)\nnet/packet/af_packet.c:557:\t\theader_len = dev-\u003ehard_header_len;\nnet/packet/af_packet.c-558-\telse if (is_vlan_dev(dev))\n--\nnet/packet/af_packet.c=1960=static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,\n--\nnet/packet/af_packet.c-2015-\tif (!skb)\nnet/packet/af_packet.c:2016:\t\thard_header_len = READ_ONCE(dev-\u003ehard_header_len);\nnet/packet/af_packet.c-2017-\n--\nnet/packet/af_packet.c=2736=static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)\n--\nnet/packet/af_packet.c-2800-\nnet/packet/af_packet.c:2801:\thard_header_len = READ_ONCE(dev-\u003ehard_header_len);\nnet/packet/af_packet.c-2802-\tif (po-\u003esk.sk_socket-\u003etype == SOCK_RAW)\n--\nnet/packet/af_packet.c=2966=static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)\n--\nnet/packet/af_packet.c-3019-\nnet/packet/af_packet.c:3020:\thard_header_len = READ_ONCE(dev-\u003ehard_header_len);\nnet/packet/af_packet.c-3021-\tif (sock-\u003etype == SOCK_RAW)\n"
}

55/3 2026/08/07 13:02 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29487
							output: 38
							thoughts: 91
						

Thoughts:
**Analyzing Packet Transmission and Header Space**

Okay, so I'm looking at this `packet_snd` function, specifically how it handles reserving space within a socket buffer before transmission. My immediate concern is this: it calculates the reserved space (`reserve`) based on the socket type. Raw sockets get a simple `hard_header_len`, while other socket types also consider the device's tailroom. That's fine – standard practice to accommodate for headers.

But the question is, does this space reservation *enforce* a minimum length for the packet? Does it ensure that the data length (`len`) is at least as large as the `hard_header_len`? A packet with a data length shorter than the minimum header length would be...problematic, to say the least. It's a fundamental requirement. So, I need to go back and thoroughly examine the `packet_snd` function to see if there are any explicit length checks in place to prevent this potentially disastrous scenario. Let's dig in and see.



56/3 2026/08/07 13:02 tool read-file
0m Args:
{
  "File": "net/packet/af_packet.c",
  "FirstLine": 3010,
  "LineCount": 30
}

Results:
{
  "Contents": "3010:\t\tif (unlikely(!(dev-\u003eflags \u0026 IFF_UP)))\n3011:\t\t\tgoto out_unlock;\n3012:\t\n3013:\t\tsockcm_init(\u0026sockc, sk);\n3014:\t\tif (msg-\u003emsg_controllen) {\n3015:\t\t\terr = sock_cmsg_send(sk, msg, \u0026sockc);\n3016:\t\t\tif (unlikely(err))\n3017:\t\t\t\tgoto out_unlock;\n3018:\t\t}\n3019:\t\n3020:\t\thard_header_len = READ_ONCE(dev-\u003ehard_header_len);\n3021:\t\tif (sock-\u003etype == SOCK_RAW)\n3022:\t\t\treserve = hard_header_len;\n3023:\t\tif (vnet_hdr_sz) {\n3024:\t\t\terr = packet_snd_vnet_parse(msg, \u0026len, \u0026vnet_hdr, vnet_hdr_sz);\n3025:\t\t\tif (err)\n3026:\t\t\t\tgoto out_unlock;\n3027:\t\t}\n3028:\t\n3029:\t\tif (unlikely(sock_flag(sk, SOCK_NOFCS))) {\n3030:\t\t\tif (!netif_supports_nofcs(dev)) {\n3031:\t\t\t\terr = -EPROTONOSUPPORT;\n3032:\t\t\t\tgoto out_unlock;\n3033:\t\t\t}\n3034:\t\t\textra_len = 4; /* We're doing our own CRC */\n3035:\t\t}\n3036:\t\n3037:\t\terr = -EMSGSIZE;\n3038:\t\tif (!vnet_hdr.gso_type \u0026\u0026\n3039:\t\t    (len \u003e dev-\u003emtu + reserve + VLAN_HLEN + extra_len))\n"
}

57/3 2026/08/07 13:02 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 30311
							output: 466
							thoughts: 2145
						

Thoughts:
**My Analysis of the KMSAN Necessity**

Okay, here's the breakdown, as I see it. There's a critical oversight in the packet reception path, specifically concerning packets with linear data lengths shorter than the Ethernet header size (14 bytes). My investigation reveals a clear information leak that's uniquely detectable by KMSAN.

Here's my thought process: Initially, I noticed that `packet_snd` doesn't explicitly check for packets with a length less than the hard header length when using `SOCK_RAW` on `AF_PACKET`. This opens the door for users to craft very short packets – even zero-byte ones – which are then passed downstream to `dev_queue_xmit`. In the `loopback` device case, these packets ultimately reach `eth_type_trans` via `loopback_xmit`.

The core issue lies in how `eth_type_trans` handles these malformed packets. It reads `eth->h_proto` without proper length validation. Since the kernel allocates the SKB with `kmalloc` (without `__GFP_ZERO`), the memory immediately following the user-provided data is uninitialized. This is the crux of the problem.

`eth_skb_pull_mac` calls `skb_pull_inline`. It is possible that the size of the SKB is less than `ETH_HLEN`, in which case a read past the allocated buffer occurs. However, this doesn't trigger `BUG_ON` or KASAN, because the data isn't out of bounds – it's just uninitialized within the allocated SKB buffer. The key is that `skb_pull_inline` doesn't return an error here, it continues.

This uninitialized `h_proto` value gets silently propagated. `eth_type_trans` reads the 2 uninitialized bytes that are part of `h_proto` and then stores it into `skb->protocol`. This `skb->protocol` value is then leaked to user space via `packet_recvmsg`.

The crucial thing is this leak *isn't* detected by standard fuzzing or KASAN because it doesn't involve any out-of-bounds reads or crashes. KASAN only tracks out-of-bounds memory accesses, so reading uninitialized memory *within* a valid allocation isn't caught. KMSAN, however, *does* track uninitialized memory reads.

This is a textbook case where KMSAN is essential. It's the only tool that reliably identifies this vulnerability. Standard fuzzing, or fuzzing with KASAN, will miss this, leading to a silent kernel info-leak.

Now I am going to summarize the process of this information leak:

1.  A short packet (e.g., 10 bytes) is crafted by a user.
2.  The packet bypasses a length check.
3.  The packet calls `eth_type_trans`, where it is handled without sufficient length validation.
4.  The packets end up calling `skb_pull_inline`.
5.  Uninitialized memory is read from the `SKB` data buffer when `eth->h_proto` is read.
6.  The uninitialized memory is then copied to the `skb->protocol` field.
7.  The packet is received by another `AF_PACKET` socket.
8.  The `skb->protocol` field is copied to `sll->sll_protocol`.
9.  The `sll->sll_protocol` field is leaked to userspace.
10. The result is a kernel-to-user info-leak of uninitialized memory.

This patch, specifically by dropping the short linear packet, fixes a serious issue.

I can confirm that the `skb->protocol` value *is* indeed leaked to user space, providing a reproducible and exploitable kernel info-leak. The `sockaddr_ll` structure and its contents, including the now-compromised `sll->sll_protocol`, are copied to user space.

In summary: I see `NeedsKMSAN = true`. This patch fixes a vulnerability that is only detected by KMSAN.



58/3 2026/08/07 13:03 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch adds a length check `skb_headlen(skb) \u003c ETH_HLEN` in `eth_type_trans()` to prevent processing packets with a linear part shorter than the Ethernet header (14 bytes). \n\nWithout this check, if a user injects a short linear packet (e.g., 10 bytes) via a TUN/TAP device or an AF_PACKET raw socket, `eth_type_trans()` calls `eth_skb_pull_mac()`. `eth_skb_pull_mac()` calls `skb_pull_inline(skb, ETH_HLEN)`. If `skb-\u003elen \u003c ETH_HLEN`, `skb_pull_inline()` fails and returns `NULL` without triggering any `BUG_ON` or crash. `eth_skb_pull_mac()` ignores this return value and returns the original `skb-\u003edata` pointer.\n\n`eth_type_trans()` then reads `eth-\u003eh_proto` at offset 12. Because the skb data buffer is typically allocated with a much larger size (e.g., via `kmalloc`), this read is well within the allocation bounds. Therefore, KASAN will NOT detect it as an out-of-bounds read.\n\nHowever, the memory beyond the user-provided 10 bytes is uninitialized. KMSAN will detect this as a read of uninitialized memory. Furthermore, this uninitialized value is stored in `skb-\u003eprotocol`. If the packet is later received by an AF_PACKET socket, `skb-\u003eprotocol` is copied to `sll_protocol` in the `sockaddr_ll` structure and returned to user space via `recvmsg()`. This constitutes a direct kernel-to-user info-leak of 2 bytes of uninitialized kernel memory.\n\nSince this issue does not cause a crash, out-of-bounds access, or trigger a `BUG_ON` for linear packets, it can ONLY be detected by KMSAN. Therefore, a dedicated KMSAN fuzzing session is highly justified.",
  "NeedsKMSAN": true
}

Results:
{
  "KMSANReasoning": "The patch adds a length check `skb_headlen(skb) \u003c ETH_HLEN` in `eth_type_trans()` to prevent processing packets with a linear part shorter than the Ethernet header (14 bytes). \n\nWithout this check, if a user injects a short linear packet (e.g., 10 bytes) via a TUN/TAP device or an AF_PACKET raw socket, `eth_type_trans()` calls `eth_skb_pull_mac()`. `eth_skb_pull_mac()` calls `skb_pull_inline(skb, ETH_HLEN)`. If `skb-\u003elen \u003c ETH_HLEN`, `skb_pull_inline()` fails and returns `NULL` without triggering any `BUG_ON` or crash. `eth_skb_pull_mac()` ignores this return value and returns the original `skb-\u003edata` pointer.\n\n`eth_type_trans()` then reads `eth-\u003eh_proto` at offset 12. Because the skb data buffer is typically allocated with a much larger size (e.g., via `kmalloc`), this read is well within the allocation bounds. Therefore, KASAN will NOT detect it as an out-of-bounds read.\n\nHowever, the memory beyond the user-provided 10 bytes is uninitialized. KMSAN will detect this as a read of uninitialized memory. Furthermore, this uninitialized value is stored in `skb-\u003eprotocol`. If the packet is later received by an AF_PACKET socket, `skb-\u003eprotocol` is copied to `sll_protocol` in the `sockaddr_ll` structure and returned to user space via `recvmsg()`. This constitutes a direct kernel-to-user info-leak of 2 bytes of uninitialized kernel memory.\n\nSince this issue does not cause a crash, out-of-bounds access, or trigger a `BUG_ON` for linear packets, it can ONLY be detected by KMSAN. Therefore, a dedicated KMSAN fuzzing session is highly justified.",
  "NeedsKMSAN": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)