| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/07 15:53 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"BPF_SYSCALL",
"NET"
],
"FocusSymbols": [
"bpf_skb_change_tail",
"sk_skb_change_tail",
"bpf_skb_trim_rcsum",
"__bpf_skb_min_len"
],
"KMSANReasoning": "The patch modifies BPF skb tail adjustment logic (`bpf_skb_change_tail` / `bpf_skb_trim_rcsum`) by relaxing the minimum length calculation in `__bpf_skb_min_len` and resetting `skb-\u003eip_summed` to `CHECKSUM_NONE` when an skb is trimmed past the partial checksum offset.\n\nThis change does not introduce or expose uninitialized memory risks:\n1. It does not allocate uninitialized memory or alter data buffers in a way that exposes uninitialized bytes (when growing, memory is explicitly zeroed via `memset`; trimming reduces buffer length).\n2. It does not copy uninitialized kernel structures to userspace or read uninitialized struct fields/padding.\n3. Any potential buffer length errors or out-of-bounds access would be caught by KASAN.\n\nTherefore, a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies BPF skb trimming semantics in net/core/filter.c. Specifically, it removes the minimum length constraint based on partial checksum offsets in __bpf_skb_min_len and instead resets skb-\u003eip_summed to CHECKSUM_NONE when trimming packets below the checksum start/offset in bpf_skb_trim_rcsum. This changes executable logic in reachable BPF helper functions (bpf_skb_change_tail and sk_skb_change_tail) and affects network checksum processing, making it an important target for fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/07 15:53 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 4c12eab9f4ec4a41a1dc8e9a4e48ca82de578cb1\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Sep 7 15:53:04 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/net/core/filter.c b/net/core/filter.c\nindex 61940e7535523..8513167a858a8 100644\n--- a/net/core/filter.c\n+++ b/net/core/filter.c\n@@ -3961,12 +3961,6 @@ static u32 __bpf_skb_min_len(const struct sk_buff *skb)\n \t\tif (offset \u003e 0)\n \t\t\tmin_len = offset;\n \t}\n-\tif (skb-\u003eip_summed == CHECKSUM_PARTIAL) {\n-\t\toffset = skb_checksum_start_offset(skb) +\n-\t\t\t skb-\u003ecsum_offset + sizeof(__sum16);\n-\t\tif (offset \u003e 0)\n-\t\t\tmin_len = offset;\n-\t}\n \treturn min_len;\n }\n \n@@ -3983,6 +3977,11 @@ static int bpf_skb_grow_rcsum(struct sk_buff *skb, unsigned int new_len)\n \n static int bpf_skb_trim_rcsum(struct sk_buff *skb, unsigned int new_len)\n {\n+\tif (skb-\u003eip_summed == CHECKSUM_PARTIAL \u0026\u0026\n+\t new_len \u003c skb_checksum_start_offset(skb) + skb-\u003ecsum_offset +\n+\t\t sizeof(__sum16))\n+\t\tskb-\u003eip_summed = CHECKSUM_NONE;\n+\n \treturn __skb_trim_rcsum(skb, new_len);\n }\n \ndiff --git a/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c b/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c\nnew file mode 100644\nindex 0000000000000..7acdbd5757a98\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c\n@@ -0,0 +1,125 @@\n+// SPDX-License-Identifier: GPL-2.0\n+\n+#include \u003cnetinet/tcp.h\u003e\n+\n+#include \"test_progs.h\"\n+#include \"network_helpers.h\"\n+#include \"test_tc_change_tail_pmtu.skel.h\"\n+\n+#define CLIENT_NS\t\"tc-change-tail-cli-ns\"\n+#define SERVER_NS\t\"tc-change-tail-srv-ns\"\n+#define CLIENT_IP\t\"192.168.1.1\"\n+#define SERVER_IP\t\"192.168.1.2\"\n+\n+#define TEST_PMTU\t1000\n+#define TEST_MSS_MAX\t(TEST_PMTU - 20 - 20)\n+#define TIMEOUT_MS\t3000\n+#define XFER_BYTES\t8192\n+\n+void test_tc_change_tail_pmtu(void)\n+{\n+\tLIBBPF_OPTS(bpf_tcx_opts, tcx_opts);\n+\tint mss_before = 0, mss_after = 0, ifindex, port;\n+\tint srv_fd = -1, srv_conn_fd = -1, cli_fd = -1;\n+\tstruct test_tc_change_tail_pmtu *skel = NULL;\n+\tstruct nstoken *nstoken = NULL;\n+\tstatic char buf[XFER_BYTES];\n+\tsocklen_t optlen;\n+\tssize_t bytes;\n+\tsize_t total;\n+\n+\tif (!ASSERT_OK(make_netns(CLIENT_NS), \"make client ns\"))\n+\t\treturn;\n+\tif (!ASSERT_OK(make_netns(SERVER_NS), \"make server ns\"))\n+\t\tgoto out_client_ns;\n+\n+\tnstoken = open_netns(CLIENT_NS);\n+\tif (!ASSERT_OK_PTR(nstoken, \"open client ns\"))\n+\t\tgoto out;\n+\tSYS(out, \"ip link add veth1 type veth peer name veth2 netns \" SERVER_NS);\n+\tSYS(out, \"ip -4 addr add \" CLIENT_IP \"/24 dev veth1\");\n+\tSYS(out, \"ip link set veth1 up\");\n+\tifindex = if_nametoindex(\"veth1\");\n+\tif (!ASSERT_NEQ(ifindex, 0, \"if_nametoindex\"))\n+\t\tgoto out;\n+\tclose_netns(nstoken);\n+\tnstoken = NULL;\n+\n+\tnstoken = open_netns(SERVER_NS);\n+\tif (!ASSERT_OK_PTR(nstoken, \"open server ns\"))\n+\t\tgoto out;\n+\tSYS(out, \"ip -4 addr add \" SERVER_IP \"/24 dev veth2\");\n+\tSYS(out, \"ip link set veth2 up\");\n+\tsrv_fd = start_server(AF_INET, SOCK_STREAM, SERVER_IP, 0, TIMEOUT_MS);\n+\tif (!ASSERT_OK_FD(srv_fd, \"start server\"))\n+\t\tgoto out;\n+\tclose_netns(nstoken);\n+\tnstoken = NULL;\n+\n+\tskel = test_tc_change_tail_pmtu__open_and_load();\n+\tif (!ASSERT_OK_PTR(skel, \"open and load skeleton\"))\n+\t\tgoto out;\n+\n+\tport = get_socket_local_port(srv_fd);\n+\tif (!ASSERT_GE(port, 0, \"get server port\"))\n+\t\tgoto out;\n+\n+\tskel-\u003ebss-\u003eserver_port = port;\n+\tskel-\u003ebss-\u003epmtu = TEST_PMTU;\n+\n+\tnstoken = open_netns(CLIENT_NS);\n+\tif (!ASSERT_OK_PTR(nstoken, \"open client ns\"))\n+\t\tgoto out;\n+\n+\tskel-\u003elinks.change_tail_icmp =\n+\t\tbpf_program__attach_tcx(skel-\u003eprogs.change_tail_icmp, ifindex,\n+\t\t\t\t\t\u0026tcx_opts);\n+\tif (!ASSERT_OK_PTR(skel-\u003elinks.change_tail_icmp, \"attach tcx\"))\n+\t\tgoto out;\n+\n+\tcli_fd = connect_to_fd(srv_fd, TIMEOUT_MS);\n+\tif (!ASSERT_OK_FD(cli_fd, \"connect to server\"))\n+\t\tgoto out;\n+\tsrv_conn_fd = accept(srv_fd, NULL, NULL);\n+\tif (!ASSERT_OK_FD(srv_conn_fd, \"accept connection\"))\n+\t\tgoto out;\n+\tif (!ASSERT_OK(settimeo(srv_conn_fd, TIMEOUT_MS), \"set server timeout\"))\n+\t\tgoto out;\n+\n+\toptlen = sizeof(mss_before);\n+\tif (!ASSERT_OK(getsockopt(cli_fd, IPPROTO_TCP, TCP_MAXSEG, \u0026mss_before,\n+\t\t\t\t \u0026optlen), \"get mss before\"))\n+\t\tgoto out;\n+\n+\tbytes = send(cli_fd, buf, sizeof(buf), 0);\n+\tif (!ASSERT_EQ(bytes, (ssize_t)sizeof(buf), \"send data\"))\n+\t\tgoto out;\n+\n+\tfor (total = 0; total \u003c sizeof(buf); total += bytes) {\n+\t\tbytes = recv(srv_conn_fd, buf, sizeof(buf), 0);\n+\t\tif (bytes \u003c= 0)\n+\t\t\tbreak;\n+\t}\n+\n+\tASSERT_EQ(total, sizeof(buf), \"receive data\");\n+\tASSERT_OK(skel-\u003edata-\u003echange_tail_ret, \"change tail\");\n+\tASSERT_OK(skel-\u003ebss-\u003eadjust_room_ret, \"adjust room\");\n+\tASSERT_TRUE(skel-\u003ebss-\u003eicmp_sent, \"icmp sent\");\n+\n+\toptlen = sizeof(mss_after);\n+\tif (!ASSERT_OK(getsockopt(cli_fd, IPPROTO_TCP, TCP_MAXSEG, \u0026mss_after,\n+\t\t\t\t \u0026optlen), \"get mss after\"))\n+\t\tgoto out;\n+\n+\tASSERT_LT(mss_after, mss_before, \"mss reduced\");\n+\tASSERT_LE(mss_after, TEST_MSS_MAX, \"mss below pmtu\");\n+out:\n+\tclose(srv_conn_fd);\n+\tclose(cli_fd);\n+\tclose(srv_fd);\n+\ttest_tc_change_tail_pmtu__destroy(skel);\n+\tclose_netns(nstoken);\n+\tremove_netns(SERVER_NS);\n+out_client_ns:\n+\tremove_netns(CLIENT_NS);\n+}\ndiff --git a/tools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c b/tools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c\nnew file mode 100644\nindex 0000000000000..5c4c07545bc9f\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c\n@@ -0,0 +1,129 @@\n+// SPDX-License-Identifier: GPL-2.0\n+\n+#include \u003cstdbool.h\u003e\n+#include \u003cstddef.h\u003e\n+\n+#include \u003clinux/bpf.h\u003e\n+#include \u003clinux/icmp.h\u003e\n+#include \u003clinux/if_ether.h\u003e\n+#include \u003clinux/in.h\u003e\n+#include \u003clinux/ip.h\u003e\n+#include \u003clinux/tcp.h\u003e\n+\n+#include \u003cbpf/bpf_helpers.h\u003e\n+#include \u003cbpf/bpf_endian.h\u003e\n+\n+#define ICMP_SAMPLE_LEN\t(sizeof(struct iphdr) + 8)\n+#define ICMP_HDRS_LEN\t(sizeof(struct iphdr) + sizeof(struct icmphdr))\n+\n+__be16 server_port = 0;\n+__u16 pmtu = 0;\n+\n+long change_tail_ret = 1;\n+long adjust_room_ret = 0;\n+bool icmp_sent = false;\n+bool icmp_err = false;\n+\n+static __always_inline __sum16 csum_fold(__wsum csum)\n+{\n+\tcsum = (csum \u0026 0xffff) + (csum \u003e\u003e 16);\n+\tcsum = (csum \u0026 0xffff) + (csum \u003e\u003e 16);\n+\n+\treturn (__sum16)~csum;\n+}\n+\n+SEC(\"tc/egress\")\n+int change_tail_icmp(struct __sk_buff *skb)\n+{\n+\t__u8 smac[ETH_ALEN], dmac[ETH_ALEN];\n+\tvoid *data, *data_end;\n+\tstruct icmphdr *icmp;\n+\tstruct ethhdr *eth;\n+\tstruct tcphdr *tcp;\n+\t__be32 saddr, daddr;\n+\tstruct iphdr *ip;\n+\t__wsum csum;\n+\n+\tif (icmp_sent || icmp_err)\n+\t\treturn TCX_PASS;\n+\n+\tdata = (void *)(long)skb-\u003edata;\n+\tdata_end = (void *)(long)skb-\u003edata_end;\n+\n+\teth = data;\n+\tif ((void *)(eth + 1) \u003e data_end)\n+\t\treturn TCX_PASS;\n+\tif (eth-\u003eh_proto != bpf_htons(ETH_P_IP))\n+\t\treturn TCX_PASS;\n+\n+\tip = (void *)(eth + 1);\n+\tif ((void *)(ip + 1) \u003e data_end)\n+\t\treturn TCX_PASS;\n+\tif (ip-\u003eihl != 5 || ip-\u003eprotocol != IPPROTO_TCP)\n+\t\treturn TCX_PASS;\n+\n+\ttcp = (void *)(ip + 1);\n+\tif ((void *)(tcp + 1) \u003e data_end)\n+\t\treturn TCX_PASS;\n+\tif (tcp-\u003edest != server_port)\n+\t\treturn TCX_PASS;\n+\tif (bpf_ntohs(ip-\u003etot_len) \u003c= sizeof(*ip) + tcp-\u003edoff * 4)\n+\t\treturn TCX_PASS;\n+\n+\t__builtin_memcpy(smac, eth-\u003eh_source, ETH_ALEN);\n+\t__builtin_memcpy(dmac, eth-\u003eh_dest, ETH_ALEN);\n+\tsaddr = ip-\u003esaddr;\n+\tdaddr = ip-\u003edaddr;\n+\n+\tchange_tail_ret = bpf_skb_change_tail(skb, ETH_HLEN + ICMP_SAMPLE_LEN, 0);\n+\tif (change_tail_ret) {\n+\t\ticmp_err = true;\n+\t\treturn TCX_PASS;\n+\t}\n+\n+\tadjust_room_ret = bpf_skb_adjust_room(skb, ICMP_HDRS_LEN,\n+\t\t\t\t\t BPF_ADJ_ROOM_MAC,\n+\t\t\t\t\t BPF_F_ADJ_ROOM_NO_CSUM_RESET);\n+\tif (adjust_room_ret) {\n+\t\ticmp_err = true;\n+\t\treturn TCX_DROP;\n+\t}\n+\n+\tdata = (void *)(long)skb-\u003edata;\n+\tdata_end = (void *)(long)skb-\u003edata_end;\n+\n+\teth = data;\n+\tip = (void *)(eth + 1);\n+\ticmp = (void *)(ip + 1);\n+\tif ((void *)icmp + sizeof(*icmp) + ICMP_SAMPLE_LEN \u003e data_end) {\n+\t\ticmp_err = true;\n+\t\treturn TCX_DROP;\n+\t}\n+\n+\t__builtin_memcpy(eth-\u003eh_dest, smac, ETH_ALEN);\n+\t__builtin_memcpy(eth-\u003eh_source, dmac, ETH_ALEN);\n+\n+\t__builtin_memset(icmp, 0, sizeof(*icmp));\n+\ticmp-\u003etype = ICMP_DEST_UNREACH;\n+\ticmp-\u003ecode = ICMP_FRAG_NEEDED;\n+\ticmp-\u003eun.frag.mtu = bpf_htons(pmtu);\n+\n+\t__builtin_memset(ip, 0, sizeof(*ip));\n+\tip-\u003eversion = 4;\n+\tip-\u003eihl = 5;\n+\tip-\u003ettl = 64;\n+\tip-\u003eprotocol = IPPROTO_ICMP;\n+\tip-\u003etot_len = bpf_htons(ICMP_HDRS_LEN + ICMP_SAMPLE_LEN);\n+\tip-\u003esaddr = daddr;\n+\tip-\u003edaddr = saddr;\n+\n+\tcsum = bpf_csum_diff(NULL, 0, (__be32 *)icmp,\n+\t\t\t sizeof(*icmp) + ICMP_SAMPLE_LEN, 0);\n+\ticmp-\u003echecksum = csum_fold(csum);\n+\tcsum = bpf_csum_diff(NULL, 0, (__be32 *)ip, sizeof(*ip), 0);\n+\tip-\u003echeck = csum_fold(csum);\n+\ticmp_sent = true;\n+\treturn bpf_redirect(skb-\u003eifindex, BPF_F_INGRESS);\n+}\n+\n+char _license[] SEC(\"license\") = \"GPL\";\n"
} |
| 2/1 | 2026/09/07 15:53 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": [
"BPF_SYSCALL",
"NET"
],
"FocusSymbols": [
"bpf_skb_change_tail",
"sk_skb_change_tail",
"bpf_skb_trim_rcsum",
"__bpf_skb_min_len"
],
"Reasoning": "The patch modifies BPF skb trimming semantics in net/core/filter.c. Specifically, it removes the minimum length constraint based on partial checksum offsets in __bpf_skb_min_len and instead resets skb-\u003eip_summed to CHECKSUM_NONE when trimming packets below the checksum start/offset in bpf_skb_trim_rcsum. This changes executable logic in reachable BPF helper functions (bpf_skb_change_tail and sk_skb_change_tail) and affects network checksum processing, making it an important target for fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any '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: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 4c12eab9f4ec4a41a1dc8e9a4e48ca82de578cb1
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Sep 7 15:53:04 2026 +0000
syz-cluster: applied patch under review
diff --git a/net/core/filter.c b/net/core/filter.c
index 61940e7535523..8513167a858a8 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -3961,12 +3961,6 @@ static u32 __bpf_skb_min_len(const struct sk_buff *skb)
if (offset > 0)
min_len = offset;
}
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- offset = skb_checksum_start_offset(skb) +
- skb->csum_offset + sizeof(__sum16);
- if (offset > 0)
- min_len = offset;
- }
return min_len;
}
@@ -3983,6 +3977,11 @@ static int bpf_skb_grow_rcsum(struct sk_buff *skb, unsigned int new_len)
static int bpf_skb_trim_rcsum(struct sk_buff *skb, unsigned int new_len)
{
+ if (skb->ip_summed == CHECKSUM_PARTIAL &&
+ new_len < skb_checksum_start_offset(skb) + skb->csum_offset +
+ sizeof(__sum16))
+ skb->ip_summed = CHECKSUM_NONE;
+
return __skb_trim_rcsum(skb, new_len);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c b/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c
new file mode 100644
index 0000000000000..7acdbd5757a98
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <netinet/tcp.h>
+
+#include "test_progs.h"
+#include "network_helpers.h"
+#include "test_tc_change_tail_pmtu.skel.h"
+
+#define CLIENT_NS "tc-change-tail-cli-ns"
+#define SERVER_NS "tc-change-tail-srv-ns"
+#define CLIENT_IP "192.168.1.1"
+#define SERVER_IP "192.168.1.2"
+
+#define TEST_PMTU 1000
+#define TEST_MSS_MAX (TEST_PMTU - 20 - 20)
+#define TIMEOUT_MS 3000
+#define XFER_BYTES 8192
+
+void test_tc_change_tail_pmtu(void)
+{
+ LIBBPF_OPTS(bpf_tcx_opts, tcx_opts);
+ int mss_before = 0, mss_after = 0, ifindex, port;
+ int srv_fd = -1, srv_conn_fd = -1, cli_fd = -1;
+ struct test_tc_change_tail_pmtu *skel = NULL;
+ struct nstoken *nstoken = NULL;
+ static char buf[XFER_BYTES];
+ socklen_t optlen;
+ ssize_t bytes;
+ size_t total;
+
+ if (!ASSERT_OK(make_netns(CLIENT_NS), "make client ns"))
+ return;
+ if (!ASSERT_OK(make_netns(SERVER_NS), "make server ns"))
+ goto out_client_ns;
+
+ nstoken = open_netns(CLIENT_NS);
+ if (!ASSERT_OK_PTR(nstoken, "open client ns"))
+ goto out;
+ SYS(out, "ip link add veth1 type veth peer name veth2 netns " SERVER_NS);
+ SYS(out, "ip -4 addr add " CLIENT_IP "/24 dev veth1");
+ SYS(out, "ip link set veth1 up");
+ ifindex = if_nametoindex("veth1");
+ if (!ASSERT_NEQ(ifindex, 0, "if_nametoindex"))
+ goto out;
+ close_netns(nstoken);
+ nstoken = NULL;
+
+ nstoken = open_netns(SERVER_NS);
+ if (!ASSERT_OK_PTR(nstoken, "open server ns"))
+ goto out;
+ SYS(out, "ip -4 addr add " SERVER_IP "/24 dev veth2");
+ SYS(out, "ip link set veth2 up");
+ srv_fd = start_server(AF_INET, SOCK_STREAM, SERVER_IP, 0, TIMEOUT_MS);
+ if (!ASSERT_OK_FD(srv_fd, "start server"))
+ goto out;
+ close_netns(nstoken);
+ nstoken = NULL;
+
+ skel = test_tc_change_tail_pmtu__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open and load skeleton"))
+ goto out;
+
+ port = get_socket_local_port(srv_fd);
+ if (!ASSERT_GE(port, 0, "get server port"))
+ goto out;
+
+ skel->bss->server_port = port;
+ skel->bss->pmtu = TEST_PMTU;
+
+ nstoken = open_netns(CLIENT_NS);
+ if (!ASSERT_OK_PTR(nstoken, "open client ns"))
+ goto out;
+
+ skel->links.change_tail_icmp =
+ bpf_program__attach_tcx(skel->progs.change_tail_icmp, ifindex,
+ &tcx_opts);
+ if (!ASSERT_OK_PTR(skel->links.change_tail_icmp, "attach tcx"))
+ goto out;
+
+ cli_fd = connect_to_fd(srv_fd, TIMEOUT_MS);
+ if (!ASSERT_OK_FD(cli_fd, "connect to server"))
+ goto out;
+ srv_conn_fd = accept(srv_fd, NULL, NULL);
+ if (!ASSERT_OK_FD(srv_conn_fd, "accept connection"))
+ goto out;
+ if (!ASSERT_OK(settimeo(srv_conn_fd, TIMEOUT_MS), "set server timeout"))
+ goto out;
+
+ optlen = sizeof(mss_before);
+ if (!ASSERT_OK(getsockopt(cli_fd, IPPROTO_TCP, TCP_MAXSEG, &mss_before,
+ &optlen), "get mss before"))
+ goto out;
+
+ bytes = send(cli_fd, buf, sizeof(buf), 0);
+ if (!ASSERT_EQ(bytes, (ssize_t)sizeof(buf), "send data"))
+ goto out;
+
+ for (total = 0; total < sizeof(buf); total += bytes) {
+ bytes = recv(srv_conn_fd, buf, sizeof(buf), 0);
+ if (bytes <= 0)
+ break;
+ }
+
+ ASSERT_EQ(total, sizeof(buf), "receive data");
+ ASSERT_OK(skel->data->change_tail_ret, "change tail");
+ ASSERT_OK(skel->bss->adjust_room_ret, "adjust room");
+ ASSERT_TRUE(skel->bss->icmp_sent, "icmp sent");
+
+ optlen = sizeof(mss_after);
+ if (!ASSERT_OK(getsockopt(cli_fd, IPPROTO_TCP, TCP_MAXSEG, &mss_after,
+ &optlen), "get mss after"))
+ goto out;
+
+ ASSERT_LT(mss_after, mss_before, "mss reduced");
+ ASSERT_LE(mss_after, TEST_MSS_MAX, "mss below pmtu");
+out:
+ close(srv_conn_fd);
+ close(cli_fd);
+ close(srv_fd);
+ test_tc_change_tail_pmtu__destroy(skel);
+ close_netns(nstoken);
+ remove_netns(SERVER_NS);
+out_client_ns:
+ remove_netns(CLIENT_NS);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c b/tools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c
new file mode 100644
index 0000000000000..5c4c07545bc9f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <linux/bpf.h>
+#include <linux/icmp.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+#define ICMP_SAMPLE_LEN (sizeof(struct iphdr) + 8)
+#define ICMP_HDRS_LEN (sizeof(struct iphdr) + sizeof(struct icmphdr))
+
+__be16 server_port = 0;
+__u16 pmtu = 0;
+
+long change_tail_ret = 1;
+long adjust_room_ret = 0;
+bool icmp_sent = false;
+bool icmp_err = false;
+
+static __always_inline __sum16 csum_fold(__wsum csum)
+{
+ csum = (csum & 0xffff) + (csum >> 16);
+ csum = (csum & 0xffff) + (csum >> 16);
+
+ return (__sum16)~csum;
+}
+
+SEC("tc/egress")
+int change_tail_icmp(struct __sk_buff *skb)
+{
+ __u8 smac[ETH_ALEN], dmac[ETH_ALEN];
+ void *data, *data_end;
+ struct icmphdr *icmp;
+ struct ethhdr *eth;
+ struct tcphdr *tcp;
+ __be32 saddr, daddr;
+ struct iphdr *ip;
+ __wsum csum;
+
+ if (icmp_sent || icmp_err)
+ return TCX_PASS;
+
+ data = (void *)(long)skb->data;
+ data_end = (void *)(long)skb->data_end;
+
+ eth = data;
+ if ((void *)(eth + 1) > data_end)
+ return TCX_PASS;
+ if (eth->h_proto != bpf_htons(ETH_P_IP))
+ return TCX_PASS;
+
+ ip = (void *)(eth + 1);
+ if ((void *)(ip + 1) > data_end)
+ return TCX_PASS;
+ if (ip->ihl != 5 || ip->protocol != IPPROTO_TCP)
+ return TCX_PASS;
+
+ tcp = (void *)(ip + 1);
+ if ((void *)(tcp + 1) > data_end)
+ return TCX_PASS;
+ if (tcp->dest != server_port)
+ return TCX_PASS;
+ if (bpf_ntohs(ip->tot_len) <= sizeof(*ip) + tcp->doff * 4)
+ return TCX_PASS;
+
+ __builtin_memcpy(smac, eth->h_source, ETH_ALEN);
+ __builtin_memcpy(dmac, eth->h_dest, ETH_ALEN);
+ saddr = ip->saddr;
+ daddr = ip->daddr;
+
+ change_tail_ret = bpf_skb_change_tail(skb, ETH_HLEN + ICMP_SAMPLE_LEN, 0);
+ if (change_tail_ret) {
+ icmp_err = true;
+ return TCX_PASS;
+ }
+
+ adjust_room_ret = bpf_skb_adjust_room(skb, ICMP_HDRS_LEN,
+ BPF_ADJ_ROOM_MAC,
+ BPF_F_ADJ_ROOM_NO_CSUM_RESET);
+ if (adjust_room_ret) {
+ icmp_err = true;
+ return TCX_DROP;
+ }
+
+ data = (void *)(long)skb->data;
+ data_end = (void *)(long)skb->data_end;
+
+ eth = data;
+ ip = (void *)(eth + 1);
+ icmp = (void *)(ip + 1);
+ if ((void *)icmp + sizeof(*icmp) + ICMP_SAMPLE_LEN > data_end) {
+ icmp_err = true;
+ return TCX_DROP;
+ }
+
+ __builtin_memcpy(eth->h_dest, smac, ETH_ALEN);
+ __builtin_memcpy(eth->h_source, dmac, ETH_ALEN);
+
+ __builtin_memset(icmp, 0, sizeof(*icmp));
+ icmp->type = ICMP_DEST_UNREACH;
+ icmp->code = ICMP_FRAG_NEEDED;
+ icmp->un.frag.mtu = bpf_htons(pmtu);
+
+ __builtin_memset(ip, 0, sizeof(*ip));
+ ip->version = 4;
+ ip->ihl = 5;
+ ip->ttl = 64;
+ ip->protocol = IPPROTO_ICMP;
+ ip->tot_len = bpf_htons(ICMP_HDRS_LEN + ICMP_SAMPLE_LEN);
+ ip->saddr = daddr;
+ ip->daddr = saddr;
+
+ csum = bpf_csum_diff(NULL, 0, (__be32 *)icmp,
+ sizeof(*icmp) + ICMP_SAMPLE_LEN, 0);
+ icmp->checksum = csum_fold(csum);
+ csum = bpf_csum_diff(NULL, 0, (__be32 *)ip, sizeof(*ip), 0);
+ ip->check = csum_fold(csum);
+ icmp_sent = true;
+ return bpf_redirect(skb->ifindex, BPF_F_INGRESS);
+}
+
+char _license[] SEC("license") = "GPL";
|
| 3/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 6524 output: 36 thoughts: 336 Thoughts: **Analyzing the Patch: Network Filter Optimization** Okay, let's dive into this patch. My immediate thought is to understand the implications of these changes, particularly how they affect the BPF filtering logic. Let's see what's being tweaked in `net/core/filter.c`. First, it looks like a section has been removed from `__bpf_skb_min_len`. The original code segment was checking for `CHECKSUM_PARTIAL` and calculating a minimum length based on checksum offset. Seems like it was related to partially checksummed packets, which is an important optimization consideration. I'll need to figure out why this check was deemed unnecessary. Perhaps this logic is handled elsewhere now, or maybe it's being simplified. Then, in `bpf_skb_trim_rcsum`, we see the addition of a conditional check. This code also appears related to `CHECKSUM_PARTIAL`. If the new length of the SKB is smaller than some checksum-related offset, then `skb->ip_summed` is set to `CHECKSUM_NONE`. This suggests the patch is handling the checksum update better than before or perhaps setting it to none, or perhaps doing some form of recomputation. This is an important consideration as this could add latency or result in incorrect packets. To fully grasp the intent, I should look at the surrounding code to grasp the intent here. I'll need to use `read-file` or `grepper` to find the callers of these functions and understand how they interact in the overall BPF execution path. I need to understand the full flow to determine the impact. I must also check the added selftests in `tools/testing/selftests/bpf/`. I'll want to see what these tests cover. Are they validating the trimming and checksum handling in various scenarios? Are they looking for potential regressions? They should be the first place I start. Overall, it seems this is aimed at optimizing the handling of checksums within the BPF filtering pipeline, and I'll need to thoroughly examine the context and tests to determine the impact on performance and correctness. |
| 4/2 | 2026/09/07 15:53 | tool | read-file |
0mArgs:{
"File": "net/core/filter.c",
"FirstLine": 3950,
"LineCount": 70
}Results: {
"Contents": "3950:\t};\n3951:\t\n3952:\tstatic u32 __bpf_skb_min_len(const struct sk_buff *skb)\n3953:\t{\n3954:\t\tint offset = skb_network_offset(skb);\n3955:\t\tu32 min_len = 0;\n3956:\t\n3957:\t\tif (offset \u003e 0)\n3958:\t\t\tmin_len = offset;\n3959:\t\tif (skb_transport_header_was_set(skb)) {\n3960:\t\t\toffset = skb_transport_offset(skb);\n3961:\t\t\tif (offset \u003e 0)\n3962:\t\t\t\tmin_len = offset;\n3963:\t\t}\n3964:\t\treturn min_len;\n3965:\t}\n3966:\t\n3967:\tstatic int bpf_skb_grow_rcsum(struct sk_buff *skb, unsigned int new_len)\n3968:\t{\n3969:\t\tunsigned int old_len = skb-\u003elen;\n3970:\t\tint ret;\n3971:\t\n3972:\t\tret = __skb_grow_rcsum(skb, new_len);\n3973:\t\tif (!ret)\n3974:\t\t\tmemset(skb-\u003edata + old_len, 0, new_len - old_len);\n3975:\t\treturn ret;\n3976:\t}\n3977:\t\n3978:\tstatic int bpf_skb_trim_rcsum(struct sk_buff *skb, unsigned int new_len)\n3979:\t{\n3980:\t\tif (skb-\u003eip_summed == CHECKSUM_PARTIAL \u0026\u0026\n3981:\t\t new_len \u003c skb_checksum_start_offset(skb) + skb-\u003ecsum_offset +\n3982:\t\t\t sizeof(__sum16))\n3983:\t\t\tskb-\u003eip_summed = CHECKSUM_NONE;\n3984:\t\n3985:\t\treturn __skb_trim_rcsum(skb, new_len);\n3986:\t}\n3987:\t\n3988:\tstatic inline int __bpf_skb_change_tail(struct sk_buff *skb, u32 new_len,\n3989:\t\t\t\t\t\tu64 flags)\n3990:\t{\n3991:\t\tu32 max_len = BPF_SKB_MAX_LEN;\n3992:\t\tu32 min_len = __bpf_skb_min_len(skb);\n3993:\t\tint ret;\n3994:\t\n3995:\t\tif (unlikely(flags || new_len \u003e max_len || new_len \u003c min_len))\n3996:\t\t\treturn -EINVAL;\n3997:\t\tif (skb-\u003eencapsulation)\n3998:\t\t\treturn -ENOTSUPP;\n3999:\t\n4000:\t\t/* The basic idea of this helper is that it's performing the\n4001:\t\t * needed work to either grow or trim an skb, and eBPF program\n4002:\t\t * rewrites the rest via helpers like bpf_skb_store_bytes(),\n4003:\t\t * bpf_lX_csum_replace() and others rather than passing a raw\n4004:\t\t * buffer here. This one is a slow path helper and intended\n4005:\t\t * for replies with control messages.\n4006:\t\t *\n4007:\t\t * Like in bpf_skb_change_proto(), we want to keep this rather\n4008:\t\t * minimal and without protocol specifics so that we are able\n4009:\t\t * to separate concerns as in bpf_skb_store_bytes() should only\n4010:\t\t * be the one responsible for writing buffers.\n4011:\t\t *\n4012:\t\t * It's really expected to be a slow path operation here for\n4013:\t\t * control message replies, so we're implicitly linearizing,\n4014:\t\t * uncloning and drop offloads from the skb by this.\n4015:\t\t */\n4016:\t\tret = __bpf_try_make_writable(skb, skb-\u003elen);\n4017:\t\tif (!ret) {\n4018:\t\t\tif (new_len \u003e skb-\u003elen)\n4019:\t\t\t\tret = bpf_skb_grow_rcsum(skb, new_len);\n"
} |
| 5/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 8527 output: 36 thoughts: 33 Thoughts: Here's my take on the thought process, as requested: **Diving Deeper into Tail Changes** Alright, I'm at the point where I need to get a comprehensive view of the `__bpf_skb_change_tail` and `bpf_skb_change_tail` definitions. I can't just operate in a vacuum here. To fully grasp their behavior and implications, especially in the context of advanced BPF programs, I need to see the complete picture. I need to understand how these functions are defined, what they are actually doing under the hood, and how they interact with the overall SKB manipulation process within the kernel. Time to dig in! Let's see how these crucial functions are implemented, and how they relate. |
| 6/2 | 2026/09/07 15:53 | tool | read-file |
0mArgs:{
"File": "net/core/filter.c",
"FirstLine": 4018,
"LineCount": 40
}Results: {
"Contents": "4018:\t\t\tif (new_len \u003e skb-\u003elen)\n4019:\t\t\t\tret = bpf_skb_grow_rcsum(skb, new_len);\n4020:\t\t\telse if (new_len \u003c skb-\u003elen)\n4021:\t\t\t\tret = bpf_skb_trim_rcsum(skb, new_len);\n4022:\t\t\tif (!ret \u0026\u0026 skb_is_gso(skb))\n4023:\t\t\t\tskb_gso_reset(skb);\n4024:\t\t}\n4025:\t\treturn ret;\n4026:\t}\n4027:\t\n4028:\tBPF_CALL_3(bpf_skb_change_tail, struct sk_buff *, skb, u32, new_len,\n4029:\t\t u64, flags)\n4030:\t{\n4031:\t\tint ret = __bpf_skb_change_tail(skb, new_len, flags);\n4032:\t\n4033:\t\tbpf_compute_data_pointers(skb);\n4034:\t\treturn ret;\n4035:\t}\n4036:\t\n4037:\tstatic const struct bpf_func_proto bpf_skb_change_tail_proto = {\n4038:\t\t.func\t\t= bpf_skb_change_tail,\n4039:\t\t.gpl_only\t= false,\n4040:\t\t.ret_type\t= RET_INTEGER,\n4041:\t\t.arg1_type\t= ARG_PTR_TO_CTX,\n4042:\t\t.arg2_type\t= ARG_ANYTHING,\n4043:\t\t.arg3_type\t= ARG_ANYTHING,\n4044:\t};\n4045:\t\n4046:\tBPF_CALL_3(sk_skb_change_tail, struct sk_buff *, skb, u32, new_len,\n4047:\t\t u64, flags)\n4048:\t{\n4049:\t\treturn __bpf_skb_change_tail(skb, new_len, flags);\n4050:\t}\n4051:\t\n4052:\tstatic const struct bpf_func_proto sk_skb_change_tail_proto = {\n4053:\t\t.func\t\t= sk_skb_change_tail,\n4054:\t\t.gpl_only\t= false,\n4055:\t\t.ret_type\t= RET_INTEGER,\n4056:\t\t.arg1_type\t= ARG_PTR_TO_CTX,\n4057:\t\t.arg2_type\t= ARG_ANYTHING,\n"
} |
| 7/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 9461 output: 24 thoughts: 449 Thoughts: **WorthFuzzing Analysis: bpf_skb_change_tail and Related Functions** Okay, let's break this down. `bpf_skb_change_tail` is a BPF helper. I know it's callable from BPF programs that attach to things like `tc` or `sk_skb`. This immediately puts it in my "potentially interesting" category because BPF programs are a known attack surface and that area has seen its fair share of vulnerabilities. It manipulates `__bpf_skb_min_len` and `bpf_skb_trim_rcsum`. These are core networking functions, specifically within `net/core/filter.c`, which makes it reachable code and, importantly, code that's already within the scope of standard kernel and syzkaller fuzzing. Now, to determine if it's "WorthFuzzing," let's evaluate: * **Core Subsystem:** It's absolutely core. We're talking BPF helper functions, core networking, and SKB manipulation. Specifically, `bpf_skb_change_tail`, `sk_skb_change_tail`, and `bpf_skb_trim_rcsum`. * **Reachable:** Clearly reachable via BPF programs attached to `tc`, sockets, etc. That's a huge positive. * **Semantic Changes:** This modifies how SKB checksumming (`skb->ip_summed`) is handled. Specifically, it has to do with trimming an SKB with `CHECKSUM_PARTIAL` below the checksum offset, and removes the minimum length restriction that previously prevented trimming packets with `CHECKSUM_PARTIAL` past the checksum offset. * **Impact:** This is significant! We're changing packet manipulation semantics and SKB state. The fact that it directly interacts with checksumming and subsequent network stack processing is a huge red flag that indicates itβs WorthFuzzing. Therefore, WorthFuzzing is definitely true, this is important and needs attention. Next step: Let's identify the specific symbols to focus on. What are the key entry points? * `bpf_skb_change_tail` / `__bpf_skb_change_tail`. I need to remember that `__bpf_skb_change_tail` is static inline, which means `bpf_skb_change_tail` and `sk_skb_change_tail` are the non-static functions and that is what I need to focus on. * `bpf_skb_trim_rcsum`. Wait a minute, that is a static function inside `filter.c`. I should probably check if `bpf_skb_trim_rcsum` is static and where else it might be used. I'll need to use grepper to see how it's called and by what. This could lead to a good fuzzing path. |
| 8/2 | 2026/09/07 15:53 | tool | grepper |
0mArgs:{
"Expression": "bpf_skb_trim_rcsum"
}Results: {
"Output": "net/core/filter.c=3967=static int bpf_skb_grow_rcsum(struct sk_buff *skb, unsigned int new_len)\n--\nnet/core/filter.c-3977-\nnet/core/filter.c:3978:static int bpf_skb_trim_rcsum(struct sk_buff *skb, unsigned int new_len)\nnet/core/filter.c-3979-{\n--\nnet/core/filter.c=3988=static inline int __bpf_skb_change_tail(struct sk_buff *skb, u32 new_len,\n--\nnet/core/filter.c-4020-\t\telse if (new_len \u003c skb-\u003elen)\nnet/core/filter.c:4021:\t\t\tret = bpf_skb_trim_rcsum(skb, new_len);\nnet/core/filter.c-4022-\t\tif (!ret \u0026\u0026 skb_is_gso(skb))\n"
} |
| 9/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 10828 output: 23 thoughts: 107 Thoughts: **Examining the Tail-Changing Mechanisms in BPF** Okay, let's dive into this. I need to understand how `bpf_skb_change_tail` and `sk_skb_change_tail` are defined, especially in the context of `BPF_CALL`. My immediate thought is to trace the definition of the `bpf_skb_change_tail` function symbol, or its potential aliases like `____bpf_skb_change_tail` or just `bpf_skb_change_tail` depending on the exact implementation details. `BPF_CALL_3(bpf_skb_change_tail, ...)` should give me a solid starting point for this analysis. From there, I'll need to look at other callers of this function. I need to ensure that I understand all related symbols and what they do. I know that looking at the callers will give me a better understanding of the design and use case for changing the tail of an SKB from within a BPF program. |
| 10/2 | 2026/09/07 15:53 | tool | grepper |
0mArgs:{
"Expression": "bpf_skb_change_tail"
}Results: {
"Output": "include/uapi/linux/bpf.h=1527=union bpf_attr {\n--\ninclude/uapi/linux/bpf.h-2763- *\ninclude/uapi/linux/bpf.h:2764: * long bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags)\ninclude/uapi/linux/bpf.h-2765- * \tDescription\n--\nnet/core/filter.c=3978=static int bpf_skb_trim_rcsum(struct sk_buff *skb, unsigned int new_len)\n--\nnet/core/filter.c-3987-\nnet/core/filter.c:3988:static inline int __bpf_skb_change_tail(struct sk_buff *skb, u32 new_len,\nnet/core/filter.c-3989-\t\t\t\t\tu64 flags)\n--\nnet/core/filter.c-4027-\nnet/core/filter.c:4028:BPF_CALL_3(bpf_skb_change_tail, struct sk_buff *, skb, u32, new_len,\nnet/core/filter.c-4029-\t u64, flags)\nnet/core/filter.c-4030-{\nnet/core/filter.c:4031:\tint ret = __bpf_skb_change_tail(skb, new_len, flags);\nnet/core/filter.c-4032-\n--\nnet/core/filter.c-4036-\nnet/core/filter.c:4037:static const struct bpf_func_proto bpf_skb_change_tail_proto = {\nnet/core/filter.c:4038:\t.func\t\t= bpf_skb_change_tail,\nnet/core/filter.c-4039-\t.gpl_only\t= false,\n--\nnet/core/filter.c=4046=BPF_CALL_3(sk_skb_change_tail, struct sk_buff *, skb, u32, new_len,\n--\nnet/core/filter.c-4048-{\nnet/core/filter.c:4049:\treturn __bpf_skb_change_tail(skb, new_len, flags);\nnet/core/filter.c-4050-}\n--\nnet/core/filter.c=4061=static inline int __bpf_skb_change_head(struct sk_buff *skb, u32 head_room,\n--\nnet/core/filter.c-4078-\t\t * skb-\u003eprotocol network header, etc, stay as is.\nnet/core/filter.c:4079:\t\t * Compared to bpf_skb_change_tail(), we're more\nnet/core/filter.c-4080-\t\t * flexible due to not needing to linearize or\n--\nnet/core/filter.c=8621=tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-8656-\tcase BPF_FUNC_skb_change_tail:\nnet/core/filter.c:8657:\t\treturn \u0026bpf_skb_change_tail_proto;\nnet/core/filter.c-8658-\tcase BPF_FUNC_skb_change_head:\n--\nnet/core/filter.c=9005=lwt_xmit_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-9020-\tcase BPF_FUNC_skb_change_tail:\nnet/core/filter.c:9021:\t\treturn \u0026bpf_skb_change_tail_proto;\nnet/core/filter.c-9022-\tcase BPF_FUNC_skb_change_head:\n--\ntools/include/uapi/linux/bpf.h=1527=union bpf_attr {\n--\ntools/include/uapi/linux/bpf.h-2763- *\ntools/include/uapi/linux/bpf.h:2764: * long bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags)\ntools/include/uapi/linux/bpf.h-2765- * \tDescription\n--\ntools/testing/selftests/bpf/progs/test_sockmap_change_tail.c=21=int prog_skb_verdict(struct __sk_buff *skb)\n--\ntools/testing/selftests/bpf/progs/test_sockmap_change_tail.c-32-\tif (data[0] == 'T') { /* Trim the packet */\ntools/testing/selftests/bpf/progs/test_sockmap_change_tail.c:33:\t\tchange_tail_ret = bpf_skb_change_tail(skb, skb-\u003elen - 1, 0);\ntools/testing/selftests/bpf/progs/test_sockmap_change_tail.c-34-\t\treturn SK_PASS;\ntools/testing/selftests/bpf/progs/test_sockmap_change_tail.c-35-\t} else if (data[0] == 'G') { /* Grow the packet */\ntools/testing/selftests/bpf/progs/test_sockmap_change_tail.c:36:\t\tchange_tail_ret = bpf_skb_change_tail(skb, skb-\u003elen + 1, 0);\ntools/testing/selftests/bpf/progs/test_sockmap_change_tail.c-37-\t\treturn SK_PASS;\ntools/testing/selftests/bpf/progs/test_sockmap_change_tail.c-38-\t} else if (data[0] == 'E') { /* Error */\ntools/testing/selftests/bpf/progs/test_sockmap_change_tail.c:39:\t\tchange_tail_ret = bpf_skb_change_tail(skb, BPF_SKB_MAX_LEN, 0);\ntools/testing/selftests/bpf/progs/test_sockmap_change_tail.c-40-\t\treturn SK_PASS;\n--\ntools/testing/selftests/bpf/progs/test_sockmap_strp.c=54=int prog_skb_parser_resize(struct __sk_buff *skb)\ntools/testing/selftests/bpf/progs/test_sockmap_strp.c-55-{\ntools/testing/selftests/bpf/progs/test_sockmap_strp.c:56:\tbpf_skb_change_tail(skb, skb-\u003elen, 0);\ntools/testing/selftests/bpf/progs/test_sockmap_strp.c-57-\treturn skb-\u003elen;\n--\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c=59=int change_tail(struct __sk_buff *skb)\n--\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c-86-\tif (payload[0] == 'T') { /* Trim the packet */\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c:87:\t\tchange_tail_ret = bpf_skb_change_tail(skb, len - 1, 0);\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c-88-\t\tif (!change_tail_ret)\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c:89:\t\t\tbpf_skb_change_tail(skb, len, 0);\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c-90-\t\treturn TCX_PASS;\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c-91-\t} else if (payload[0] == 'G') { /* Grow the packet */\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c:92:\t\tchange_tail_ret = bpf_skb_change_tail(skb, len + 1, 0);\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c-93-\t\tif (!change_tail_ret)\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c:94:\t\t\tbpf_skb_change_tail(skb, len, 0);\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c-95-\t\treturn TCX_PASS;\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c-96-\t} else if (payload[0] == 'E') { /* Error */\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c:97:\t\tchange_tail_ret = bpf_skb_change_tail(skb, BPF_SKB_MAX_LEN, 0);\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c-98-\t\treturn TCX_PASS;\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c-99-\t} else if (payload[0] == 'Z') { /* Zero */\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c:100:\t\tchange_tail_ret = bpf_skb_change_tail(skb, 0, 0);\ntools/testing/selftests/bpf/progs/test_tc_change_tail.c-101-\t\treturn TCX_PASS;\n--\ntools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c=36=int change_tail_icmp(struct __sk_buff *skb)\n--\ntools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c-77-\ntools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c:78:\tchange_tail_ret = bpf_skb_change_tail(skb, ETH_HLEN + ICMP_SAMPLE_LEN, 0);\ntools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c-79-\tif (change_tail_ret) {\n--\ntools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c=126=static int tcp_reload_headers(struct tcp_syncookie *ctx)\n--\ntools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c-136-\t/* Needed to calculate csum and parse TCP options. */\ntools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c:137:\tif (bpf_skb_change_tail(ctx-\u003eskb, data_len + 60 - ctx-\u003etcp-\u003edoff * 4, 0))\ntools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c-138-\t\tgoto err;\n--\ntools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c=401=static int tcp_handle_syn(struct tcp_syncookie *ctx)\n--\ntools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c-455-\ntools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c:456:\tif (bpf_skb_change_tail(ctx-\u003eskb, (long)ctx-\u003eptr32 - (long)ctx-\u003eeth, 0))\ntools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c-457-\t\tgoto err;\n--\ntools/testing/selftests/bpf/progs/test_xdp_meta.c=635=int helper_skb_change_head_tail(struct __sk_buff *ctx)\n--\ntools/testing/selftests/bpf/progs/test_xdp_meta.c-655-\t/* Reserve 4k extra bytes in the back to trigger head reallocation */\ntools/testing/selftests/bpf/progs/test_xdp_meta.c:656:\terr = bpf_skb_change_tail(ctx, ctx-\u003elen + 4096, 0);\ntools/testing/selftests/bpf/progs/test_xdp_meta.c-657-\tif (err)\n--\ntools/testing/selftests/bpf/progs/xdp_synproxy_kern.c=579=static __always_inline int syncookie_handle_syn(struct header_pointers *hdr,\n--\ntools/testing/selftests/bpf/progs/xdp_synproxy_kern.c-721-\t} else {\ntools/testing/selftests/bpf/progs/xdp_synproxy_kern.c:722:\t\tif (bpf_skb_change_tail(ctx, new_pkt_size, 0))\ntools/testing/selftests/bpf/progs/xdp_synproxy_kern.c-723-\t\t\treturn XDP_ABORTED;\n--\ntools/testing/selftests/bpf/progs/xdp_synproxy_kern.c=750=static __always_inline int syncookie_part1(void *ctx, void *data, void *data_end,\n--\ntools/testing/selftests/bpf/progs/xdp_synproxy_kern.c-779-\ntools/testing/selftests/bpf/progs/xdp_synproxy_kern.c:780:\t\tif (bpf_skb_change_tail(ctx, old_len + TCP_MAXLEN - hdr-\u003etcp_len, 0))\ntools/testing/selftests/bpf/progs/xdp_synproxy_kern.c-781-\t\t\treturn XDP_ABORTED;\n"
} |
| 11/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 14843 output: 27 thoughts: 78 Thoughts: Here's my thought process, as it stands: **Examining Configuration Dependencies for `bpf_skb_change_tail`** Okay, so I need to nail down the configuration prerequisites for `net/core/filter.c` and specifically for the `bpf_skb_change_tail` and `sk_skb_change_tail` functions. The goal here is to determine what kernel config options are *required* to enable or build these functionalities. I'm thinking the logical places to start poking around for this info are the makefiles or Kconfig files related to the networking core. I'll need to check `net/core/Makefile` first, that's almost always a reliable place to get clues about dependencies. If I don't find the answers immediately there, I should check the corresponding Kconfig files, such as `net/Kconfig`, `init/Kconfig`, or potentially even `kernel/bpf/Kconfig`, since BPF is, after all, fundamentally involved. It's a systematic approach - look for the obvious and then drill down as needed. This should allow me to accurately determine the config requirements. |
| 12/2 | 2026/09/07 15:53 | tool | grepper |
0mArgs:{
"Expression": "filter\\.o",
"PathPrefix": "net/core/"
}Results: {
"Output": "net/core/Makefile=12=obj-y\t\t += dev.o dev_api.o dev_addr_lists.o dst.o netevent.o \\\nnet/core/Makefile:13:\t\t\tneighbour.o rtnetlink.o utils.o link_watch.o filter.o \\\nnet/core/Makefile-14-\t\t\tsock_diag.o dev_ioctl.o tso.o sock_reuseport.o \\\n--\nnet/core/pktgen.c-87- *\nnet/core/pktgen.c:88: * Remove if fix from added Harald Welte \u003claforge@netfilter.org\u003e 040419\nnet/core/pktgen.c-89- * ia64 compilation fix from Aron Griffis \u003caron@hp.com\u003e 040604\n"
} |
| 13/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 15400 output: 29 thoughts: 98 Thoughts: **My Analysis of Network Filtering Configuration** Okay, so I'm looking at how the kernel builds the network filtering object, specifically `net/core/filter.o`. The Makefile indicates that it's included using `obj-y` within `net/core/Makefile`, which means it's unconditionally built if the parent directory is included. That parent directory is conditioned on `CONFIG_NET`. Got it. Now, I'm thinking about BPF syscall support and how it relates to this. I need to figure out the dependencies. Is it conditional based on `CONFIG_BPF_SYSCALL` directly, or does it also depend on the overarching `CONFIG_NET` configuration? I'll need to check the build configurations to know for sure. It's likely that it will be `CONFIG_BPF_SYSCALL`, but I need to make sure. Furthermore, I should investigate any potential configuration dependencies within the `net/core/filter.c` source code itself. I want to check for conditional compilation blocks, like `#ifdef CONFIG_...`, specifically around the `bpf_skb_change_tail` function, and any others that might be relevant. This will tell me how the BPF support or even the underlying network stack might change the behaviour of this file. |
| 14/2 | 2026/09/07 15:53 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_",
"PathPrefix": "net/core/filter.c"
}Results: {
"Output": "net/core/filter.c=495=static bool convert_bpf_ld_abs(struct sock_filter *fp, struct bpf_insn **insnp)\nnet/core/filter.c-496-{\nnet/core/filter.c:497:\tconst bool unaligned_ok = IS_BUILTIN(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS);\nnet/core/filter.c-498-\tint size = bpf_size_to_bytes(BPF_SIZE(fp-\u003ecode));\n--\nnet/core/filter.c=1681=static inline int __bpf_try_make_writable(struct sk_buff *skb,\n--\nnet/core/filter.c-1683-{\nnet/core/filter.c:1684:#ifdef CONFIG_DEBUG_NET\nnet/core/filter.c-1685-\t/* Avoid a splat in pskb_may_pull_reason() */\n--\nnet/core/filter.c=2207=static int __bpf_redirect(struct sk_buff *skb, struct net_device *dev,\n--\nnet/core/filter.c-2215-\nnet/core/filter.c:2216:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-2217-static int bpf_out_neigh_v6(struct net *net, struct sk_buff *skb,\n--\nnet/core/filter.c=2313=static int __bpf_redirect_neigh_v6(struct sk_buff *skb, struct net_device *dev,\n--\nnet/core/filter.c-2318-}\nnet/core/filter.c:2319:#endif /* CONFIG_IPV6 */\nnet/core/filter.c-2320-\nnet/core/filter.c:2321:#if IS_ENABLED(CONFIG_INET)\nnet/core/filter.c-2322-static int bpf_out_neigh_v4(struct net *net, struct sk_buff *skb,\n--\nnet/core/filter.c=2424=static int __bpf_redirect_neigh_v4(struct sk_buff *skb, struct net_device *dev,\n--\nnet/core/filter.c-2429-}\nnet/core/filter.c:2430:#endif /* CONFIG_INET */\nnet/core/filter.c-2431-\n--\nnet/core/filter.c=3183=static const struct bpf_func_proto bpf_msg_pop_data_proto = {\n--\nnet/core/filter.c-3192-\nnet/core/filter.c:3193:#ifdef CONFIG_CGROUP_NET_CLASSID\nnet/core/filter.c-3194-BPF_CALL_0(bpf_get_cgroup_classid_curr)\n--\nnet/core/filter.c=4533=EXPORT_SYMBOL_GPL(xdp_do_flush);\nnet/core/filter.c-4534-\nnet/core/filter.c:4535:#if defined(CONFIG_DEBUG_NET) \u0026\u0026 defined(CONFIG_BPF_SYSCALL)\nnet/core/filter.c-4536-void xdp_do_check_flushed(struct napi_struct *napi)\n--\nnet/core/filter.c=5171=static const struct bpf_func_proto bpf_skb_under_cgroup_proto = {\n--\nnet/core/filter.c-5179-\nnet/core/filter.c:5180:#ifdef CONFIG_SOCK_CGROUP_DATA\nnet/core/filter.c-5181-static inline u64 __bpf_sk_cgroup_id(struct sock *sk)\n--\nnet/core/filter.c=5776=static int __bpf_setsockopt(struct sock *sk, int level, int optname,\n--\nnet/core/filter.c-5783-\t\treturn sol_socket_sockopt(sk, optname, optval, \u0026optlen, false);\nnet/core/filter.c:5784:\telse if (IS_ENABLED(CONFIG_INET) \u0026\u0026 level == SOL_IP)\nnet/core/filter.c-5785-\t\treturn sol_ip_sockopt(sk, optname, optval, \u0026optlen, false);\nnet/core/filter.c:5786:\telse if (IS_ENABLED(CONFIG_IPV6) \u0026\u0026 level == SOL_IPV6)\nnet/core/filter.c-5787-\t\treturn sol_ipv6_sockopt(sk, optname, optval, \u0026optlen, false);\nnet/core/filter.c:5788:\telse if (IS_ENABLED(CONFIG_INET) \u0026\u0026 level == SOL_TCP)\nnet/core/filter.c-5789-\t\treturn sol_tcp_sockopt(sk, optname, optval, \u0026optlen, false);\n--\nnet/core/filter.c=5807=static int __bpf_getsockopt(struct sock *sk, int level, int optname,\n--\nnet/core/filter.c-5818-\t\terr = sol_socket_sockopt(sk, optname, optval, \u0026optlen, true);\nnet/core/filter.c:5819:\telse if (IS_ENABLED(CONFIG_INET) \u0026\u0026 level == SOL_TCP)\nnet/core/filter.c-5820-\t\terr = sol_tcp_sockopt(sk, optname, optval, \u0026optlen, true);\nnet/core/filter.c:5821:\telse if (IS_ENABLED(CONFIG_INET) \u0026\u0026 level == SOL_IP)\nnet/core/filter.c-5822-\t\terr = sol_ip_sockopt(sk, optname, optval, \u0026optlen, true);\nnet/core/filter.c:5823:\telse if (IS_ENABLED(CONFIG_IPV6) \u0026\u0026 level == SOL_IPV6)\nnet/core/filter.c-5824-\t\terr = sol_ipv6_sockopt(sk, optname, optval, \u0026optlen, true);\n--\nnet/core/filter.c=6135=BPF_CALL_5(bpf_sock_ops_getsockopt, struct bpf_sock_ops_kern *, bpf_sock,\n--\nnet/core/filter.c-6140-\nnet/core/filter.c:6141:\tif (IS_ENABLED(CONFIG_INET) \u0026\u0026 level == SOL_TCP \u0026\u0026\nnet/core/filter.c-6142-\t optname \u003e= TCP_BPF_SYN \u0026\u0026 optname \u003c= TCP_BPF_SYN_MAC) {\n--\nnet/core/filter.c=6177=BPF_CALL_2(bpf_sock_ops_cb_flags_set, struct bpf_sock_ops_kern *, bpf_sock,\n--\nnet/core/filter.c-6185-\nnet/core/filter.c:6186:\tif (!IS_ENABLED(CONFIG_INET) || !sk_fullsock(sk))\nnet/core/filter.c-6187-\t\treturn -EINVAL;\n--\nnet/core/filter.c=6202=BPF_CALL_3(bpf_bind, struct bpf_sock_addr_kern *, ctx, struct sockaddr *, addr,\n--\nnet/core/filter.c-6204-{\nnet/core/filter.c:6205:#ifdef CONFIG_INET\nnet/core/filter.c-6206-\tstruct sock *sk = ctx-\u003esk;\n--\nnet/core/filter.c-6218-\t\treturn __inet_bind(sk, (struct sockaddr_unsized *)addr, addr_len, flags);\nnet/core/filter.c:6219:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-6220-\t} else if (addr-\u003esa_family == AF_INET6) {\n--\nnet/core/filter.c-6227-\t\t\t\t addr_len, flags);\nnet/core/filter.c:6228:#endif /* CONFIG_IPV6 */\nnet/core/filter.c-6229-\t}\nnet/core/filter.c:6230:#endif /* CONFIG_INET */\nnet/core/filter.c-6231-\n--\nnet/core/filter.c=6235=static const struct bpf_func_proto bpf_bind_proto = {\n--\nnet/core/filter.c-6243-\nnet/core/filter.c:6244:#ifdef CONFIG_XFRM\nnet/core/filter.c-6245-\nnet/core/filter.c:6246:#if (IS_BUILTIN(CONFIG_XFRM_INTERFACE) \u0026\u0026 IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \\\nnet/core/filter.c:6247: (IS_MODULE(CONFIG_XFRM_INTERFACE) \u0026\u0026 IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES))\nnet/core/filter.c-6248-\n--\nnet/core/filter.c=6287=static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = {\n--\nnet/core/filter.c-6298-\nnet/core/filter.c:6299:#if IS_ENABLED(CONFIG_INET) || IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-6300-static int bpf_fib_set_fwd_params(struct net_device *dev,\n--\nnet/core/filter.c-6306-\nnet/core/filter.c:6307:#if IS_ENABLED(CONFIG_VLAN_8021Q)\nnet/core/filter.c-6308-\tif ((flags \u0026 BPF_FIB_LOOKUP_VLAN) \u0026\u0026 is_vlan_dev(dev)) {\n--\nnet/core/filter.c=6329=static struct net_device *bpf_fib_vlan_input_dev(struct net_device *dev,\n--\nnet/core/filter.c-6348-\nnet/core/filter.c:6349:#if IS_ENABLED(CONFIG_INET)\nnet/core/filter.c-6350-static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,\n--\nnet/core/filter.c-6487-\t\t\t\t\t\t (__force u32)params-\u003eipv4_dst);\nnet/core/filter.c:6488:\telse if (IS_ENABLED(CONFIG_IPV6))\nnet/core/filter.c-6489-\t\tneigh = __ipv6_neigh_lookup_noref(dev, params-\u003eipv6_dst);\n--\nnet/core/filter.c-6500-\nnet/core/filter.c:6501:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-6502-static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,\n--\nnet/core/filter.c=6671=BPF_CALL_4(bpf_xdp_fib_lookup, struct xdp_buff *, ctx,\n--\nnet/core/filter.c-6680-\tswitch (params-\u003efamily) {\nnet/core/filter.c:6681:#if IS_ENABLED(CONFIG_INET)\nnet/core/filter.c-6682-\tcase AF_INET:\n--\nnet/core/filter.c-6685-#endif\nnet/core/filter.c:6686:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-6687-\tcase AF_INET6:\n--\nnet/core/filter.c=6705=BPF_CALL_4(bpf_skb_fib_lookup, struct sk_buff *, skb,\n--\nnet/core/filter.c-6724-\tswitch (params-\u003efamily) {\nnet/core/filter.c:6725:#if IS_ENABLED(CONFIG_INET)\nnet/core/filter.c-6726-\tcase AF_INET:\n--\nnet/core/filter.c-6729-#endif\nnet/core/filter.c:6730:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-6731-\tcase AF_INET6:\n--\nnet/core/filter.c=6866=static const struct bpf_func_proto bpf_xdp_check_mtu_proto = {\n--\nnet/core/filter.c-6877-\nnet/core/filter.c:6878:#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)\nnet/core/filter.c-6879-static int bpf_push_seg6_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len)\n--\nnet/core/filter.c-6910-}\nnet/core/filter.c:6911:#endif /* CONFIG_IPV6_SEG6_BPF */\nnet/core/filter.c-6912-\nnet/core/filter.c:6913:#if IS_ENABLED(CONFIG_LWTUNNEL_BPF)\nnet/core/filter.c-6914-static int bpf_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len,\n--\nnet/core/filter.c=6921=BPF_CALL_4(bpf_lwt_in_push_encap, struct sk_buff *, skb, u32, type, void *, hdr,\n--\nnet/core/filter.c-6924-\tswitch (type) {\nnet/core/filter.c:6925:#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)\nnet/core/filter.c-6926-\tcase BPF_LWT_ENCAP_SEG6:\n--\nnet/core/filter.c-6929-#endif\nnet/core/filter.c:6930:#if IS_ENABLED(CONFIG_LWTUNNEL_BPF)\nnet/core/filter.c-6931-\tcase BPF_LWT_ENCAP_IP:\n--\nnet/core/filter.c=6939=BPF_CALL_4(bpf_lwt_xmit_push_encap, struct sk_buff *, skb, u32, type,\n--\nnet/core/filter.c-6942-\tswitch (type) {\nnet/core/filter.c:6943:#if IS_ENABLED(CONFIG_LWTUNNEL_BPF)\nnet/core/filter.c-6944-\tcase BPF_LWT_ENCAP_IP:\n--\nnet/core/filter.c=6962=static const struct bpf_func_proto bpf_lwt_xmit_push_encap_proto = {\n--\nnet/core/filter.c-6971-\nnet/core/filter.c:6972:#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)\nnet/core/filter.c-6973-BPF_CALL_4(bpf_lwt_seg6_store_bytes, struct sk_buff *, skb, u32, offset,\n--\nnet/core/filter.c=7156=static const struct bpf_func_proto bpf_lwt_seg6_adjust_srh_proto = {\n--\nnet/core/filter.c-7163-};\nnet/core/filter.c:7164:#endif /* CONFIG_IPV6_SEG6_BPF */\nnet/core/filter.c-7165-\nnet/core/filter.c:7166:#ifdef CONFIG_INET\nnet/core/filter.c-7167-static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple,\n--\nnet/core/filter.c-7185-\t\t\t\t\t dif, sdif, NULL);\nnet/core/filter.c:7186:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-7187-\t} else {\n--\nnet/core/filter.c=7839=BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len,\n--\nnet/core/filter.c-7841-{\nnet/core/filter.c:7842:#ifdef CONFIG_SYN_COOKIES\nnet/core/filter.c-7843-\tint ret;\n--\nnet/core/filter.c-7874-\nnet/core/filter.c:7875:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-7876-\tcase 6:\n--\nnet/core/filter.c-7884-\t\tbreak;\nnet/core/filter.c:7885:#endif /* CONFIG_IPV6 */\nnet/core/filter.c-7886-\n--\nnet/core/filter.c=7912=BPF_CALL_5(bpf_tcp_gen_syncookie, struct sock *, sk, void *, iph, u32, iph_len,\n--\nnet/core/filter.c-7914-{\nnet/core/filter.c:7915:#ifdef CONFIG_SYN_COOKIES\nnet/core/filter.c-7916-\tu32 cookie;\n--\nnet/core/filter.c-7944-\nnet/core/filter.c:7945:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-7946-\tcase 6:\n--\nnet/core/filter.c-7954-\t\tbreak;\nnet/core/filter.c:7955:#endif /* CONFIG_IPV6 */\nnet/core/filter.c-7956-\n--\nnet/core/filter.c-7965-\treturn -EOPNOTSUPP;\nnet/core/filter.c:7966:#endif /* CONFIG_SYN_COOKIES */\nnet/core/filter.c-7967-}\n--\nnet/core/filter.c=8272=static const struct bpf_func_proto bpf_skb_set_tstamp_proto = {\n--\nnet/core/filter.c-8280-\nnet/core/filter.c:8281:#ifdef CONFIG_SYN_COOKIES\nnet/core/filter.c-8282-BPF_CALL_3(bpf_tcp_raw_gen_syncookie_ipv4, struct iphdr *, iph,\n--\nnet/core/filter.c=8308=BPF_CALL_3(bpf_tcp_raw_gen_syncookie_ipv6, struct ipv6hdr *, iph,\n--\nnet/core/filter.c-8310-{\nnet/core/filter.c:8311:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-8312-\tconst u16 mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) -\n--\nnet/core/filter.c=8360=BPF_CALL_2(bpf_tcp_raw_check_syncookie_ipv6, struct ipv6hdr *, iph,\n--\nnet/core/filter.c-8362-{\nnet/core/filter.c:8363:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-8364-\tif (__cookie_v6_check(iph, th) \u003e 0)\n--\nnet/core/filter.c=8373=static const struct bpf_func_proto bpf_tcp_raw_check_syncookie_ipv6_proto = {\n--\nnet/core/filter.c-8382-};\nnet/core/filter.c:8383:#endif /* CONFIG_SYN_COOKIES */\nnet/core/filter.c-8384-\nnet/core/filter.c:8385:#endif /* CONFIG_INET */\nnet/core/filter.c-8386-\n--\nnet/core/filter.c=8463=sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-8485-\t\treturn \u0026bpf_event_output_data_proto;\nnet/core/filter.c:8486:#ifdef CONFIG_INET\nnet/core/filter.c-8487-\tcase BPF_FUNC_sk_lookup_tcp:\n--\nnet/core/filter.c-8494-\t\treturn \u0026bpf_sock_addr_skc_lookup_tcp_proto;\nnet/core/filter.c:8495:#endif /* CONFIG_INET */\nnet/core/filter.c-8496-\tcase BPF_FUNC_sk_storage_get:\n--\nnet/core/filter.c=8572=cg_skb_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-8588-\t\treturn \u0026bpf_skb_event_output_proto;\nnet/core/filter.c:8589:#ifdef CONFIG_SOCK_CGROUP_DATA\nnet/core/filter.c-8590-\tcase BPF_FUNC_skb_cgroup_id:\n--\nnet/core/filter.c-8598-#endif\nnet/core/filter.c:8599:#ifdef CONFIG_INET\nnet/core/filter.c-8600-\tcase BPF_FUNC_sk_lookup_tcp:\n--\nnet/core/filter.c=8621=tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-8703-\t\treturn \u0026bpf_sk_storage_delete_proto;\nnet/core/filter.c:8704:#ifdef CONFIG_XFRM\nnet/core/filter.c-8705-\tcase BPF_FUNC_skb_get_xfrm_state:\n--\nnet/core/filter.c-8707-#endif\nnet/core/filter.c:8708:#ifdef CONFIG_CGROUP_NET_CLASSID\nnet/core/filter.c-8709-\tcase BPF_FUNC_skb_cgroup_classid:\n--\nnet/core/filter.c-8711-#endif\nnet/core/filter.c:8712:#ifdef CONFIG_SOCK_CGROUP_DATA\nnet/core/filter.c-8713-\tcase BPF_FUNC_skb_cgroup_id:\n--\nnet/core/filter.c-8717-#endif\nnet/core/filter.c:8718:#ifdef CONFIG_INET\nnet/core/filter.c-8719-\tcase BPF_FUNC_sk_lookup_tcp:\n--\nnet/core/filter.c-8740-\t\treturn \u0026bpf_skb_set_tstamp_proto;\nnet/core/filter.c:8741:#ifdef CONFIG_SYN_COOKIES\nnet/core/filter.c-8742-\tcase BPF_FUNC_tcp_raw_gen_syncookie_ipv4:\n--\nnet/core/filter.c=8758=xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-8786-\t\treturn \u0026bpf_xdp_check_mtu_proto;\nnet/core/filter.c:8787:#ifdef CONFIG_INET\nnet/core/filter.c-8788-\tcase BPF_FUNC_sk_lookup_udp:\n--\nnet/core/filter.c-8799-\t\treturn \u0026bpf_tcp_gen_syncookie_proto;\nnet/core/filter.c:8800:#ifdef CONFIG_SYN_COOKIES\nnet/core/filter.c-8801-\tcase BPF_FUNC_tcp_raw_gen_syncookie_ipv4:\n--\nnet/core/filter.c-8814-\nnet/core/filter.c:8815:#if IS_MODULE(CONFIG_NF_CONNTRACK) \u0026\u0026 IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)\nnet/core/filter.c-8816-\t/* The nf_conn___init type is used in the NF_CONNTRACK kfuncs. The\n--\nnet/core/filter.c=8833=sock_ops_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-8861-\t\treturn \u0026bpf_get_netns_cookie_sock_ops_proto;\nnet/core/filter.c:8862:#ifdef CONFIG_INET\nnet/core/filter.c-8863-\tcase BPF_FUNC_load_hdr_opt:\n--\nnet/core/filter.c-8870-\t\treturn \u0026bpf_tcp_sock_proto;\nnet/core/filter.c:8871:#endif /* CONFIG_INET */\nnet/core/filter.c-8872-\tdefault:\n--\nnet/core/filter.c=8915=sk_skb_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-8939-\t\treturn \u0026bpf_skb_event_output_proto;\nnet/core/filter.c:8940:#ifdef CONFIG_INET\nnet/core/filter.c-8941-\tcase BPF_FUNC_sk_lookup_tcp:\n--\nnet/core/filter.c=9044=lwt_seg6local_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-9046-\tswitch (func_id) {\nnet/core/filter.c:9047:#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)\nnet/core/filter.c-9048-\tcase BPF_FUNC_lwt_seg6_store_bytes:\n--\nnet/core/filter.c=9964=static struct bpf_insn *bpf_convert_tstamp_read(const struct bpf_prog *prog,\n--\nnet/core/filter.c-9970-\nnet/core/filter.c:9971:#ifdef CONFIG_NET_XGRESS\nnet/core/filter.c-9972-\t/* If the tstamp_type is read,\n--\nnet/core/filter.c=9999=static struct bpf_insn *bpf_convert_tstamp_write(const struct bpf_prog *prog,\n--\nnet/core/filter.c-10005-\nnet/core/filter.c:10006:#ifdef CONFIG_NET_XGRESS\nnet/core/filter.c-10007-\t/* If the tstamp_type is read,\n--\nnet/core/filter.c=10037=static u32 bpf_convert_ctx_access(enum bpf_access_type type,\n--\nnet/core/filter.c-10210-\tcase offsetof(struct __sk_buff, tc_index):\nnet/core/filter.c:10211:#ifdef CONFIG_NET_SCHED\nnet/core/filter.c-10212-\t\tif (type == BPF_WRITE)\n--\nnet/core/filter.c-10229-\tcase offsetof(struct __sk_buff, napi_id):\nnet/core/filter.c:10230:#if defined(CONFIG_NET_RX_BUSY_POLL)\nnet/core/filter.c-10231-\t\t*insn++ = BPF_LDX_MEM(BPF_W, si-\u003edst_reg, si-\u003esrc_reg,\n--\nnet/core/filter.c-10276-\t offsetof(struct __sk_buff, remote_ip6[3]):\nnet/core/filter.c:10277:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-10278-\t\tBUILD_BUG_ON(sizeof_field(struct sock_common,\n--\nnet/core/filter.c-10296-\t offsetof(struct __sk_buff, local_ip6[3]):\nnet/core/filter.c:10297:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-10298-\t\tBUILD_BUG_ON(sizeof_field(struct sock_common,\n--\nnet/core/filter.c=10403=u32 bpf_sock_convert_ctx_access(enum bpf_access_type type,\n--\nnet/core/filter.c-10492-\tcase bpf_ctx_range_till(struct bpf_sock, src_ip6[0], src_ip6[3]):\nnet/core/filter.c:10493:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-10494-\t\toff = si-\u003eoff;\n--\nnet/core/filter.c-10510-\tcase bpf_ctx_range_till(struct bpf_sock, dst_ip6[0], dst_ip6[3]):\nnet/core/filter.c:10511:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-10512-\t\toff = si-\u003eoff;\n--\nnet/core/filter.c-10556-\tcase offsetof(struct bpf_sock, rx_queue_mapping):\nnet/core/filter.c:10557:#ifdef CONFIG_SOCK_RX_QUEUE_MAPPING\nnet/core/filter.c-10558-\t\t*insn++ = BPF_LDX_MEM(\n--\nnet/core/filter.c=10815=static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,\n--\nnet/core/filter.c-11020-\t offsetof(struct bpf_sock_ops, remote_ip6[3]):\nnet/core/filter.c:11021:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-11022-\t\tBUILD_BUG_ON(sizeof_field(struct sock_common,\n--\nnet/core/filter.c-11041-\t offsetof(struct bpf_sock_ops, local_ip6[3]):\nnet/core/filter.c:11042:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-11043-\t\tBUILD_BUG_ON(sizeof_field(struct sock_common,\n--\nnet/core/filter.c=11350=static u32 sk_msg_convert_ctx_access(enum bpf_access_type type,\n--\nnet/core/filter.c-11355-\tstruct bpf_insn *insn = insn_buf;\nnet/core/filter.c:11356:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-11357-\tint off;\n--\nnet/core/filter.c-11410-\t offsetof(struct sk_msg_md, remote_ip6[3]):\nnet/core/filter.c:11411:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-11412-\t\tBUILD_BUG_ON(sizeof_field(struct sock_common,\n--\nnet/core/filter.c-11431-\t offsetof(struct sk_msg_md, local_ip6[3]):\nnet/core/filter.c:11432:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-11433-\t\tBUILD_BUG_ON(sizeof_field(struct sock_common,\n--\nnet/core/filter.c=11655=int sk_get_filter(struct sock *sk, sockptr_t optval, unsigned int len)\n--\nnet/core/filter.c-11697-\nnet/core/filter.c:11698:#ifdef CONFIG_INET\nnet/core/filter.c-11699-static void bpf_init_reuseport_kern(struct sk_reuseport_kern *reuse_kern,\n--\nnet/core/filter.c=12076=static u32 sk_lookup_convert_ctx_access(enum bpf_access_type type,\n--\nnet/core/filter.c-12115-\t\t\t\tremote_ip6[0], remote_ip6[3]): {\nnet/core/filter.c:12116:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-12117-\t\tint off = si-\u003eoff;\n--\nnet/core/filter.c-12131-\t\t\t\tlocal_ip6[0], local_ip6[3]): {\nnet/core/filter.c:12132:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-12133-\t\tint off = si-\u003eoff;\n--\nnet/core/filter.c=12177=const struct bpf_verifier_ops sk_lookup_verifier_ops = {\n--\nnet/core/filter.c-12182-\nnet/core/filter.c:12183:#endif /* CONFIG_INET */\nnet/core/filter.c-12184-\n--\nnet/core/filter.c=12234=BPF_CALL_1(bpf_skc_to_tcp_timewait_sock, struct sock *, sk)\n--\nnet/core/filter.c-12236-\t/* BTF types for tcp_timewait_sock and inet_timewait_sock are not\nnet/core/filter.c:12237:\t * generated if CONFIG_INET=n. Trigger an explicit generation here.\nnet/core/filter.c-12238-\t */\n--\nnet/core/filter.c-12241-\nnet/core/filter.c:12242:#ifdef CONFIG_INET\nnet/core/filter.c-12243-\tif (sk \u0026\u0026 sk-\u003esk_prot == \u0026tcp_prot \u0026\u0026 sk-\u003esk_state == TCP_TIME_WAIT)\n--\nnet/core/filter.c-12246-\nnet/core/filter.c:12247:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-12248-\tif (sk \u0026\u0026 sk-\u003esk_prot == \u0026tcpv6_prot \u0026\u0026 sk-\u003esk_state == TCP_TIME_WAIT)\n--\nnet/core/filter.c=12263=BPF_CALL_1(bpf_skc_to_tcp_request_sock, struct sock *, sk)\nnet/core/filter.c-12264-{\nnet/core/filter.c:12265:#ifdef CONFIG_INET\nnet/core/filter.c-12266-\tif (sk \u0026\u0026 sk-\u003esk_prot == \u0026tcp_prot \u0026\u0026 sk-\u003esk_state == TCP_NEW_SYN_RECV)\n--\nnet/core/filter.c-12269-\nnet/core/filter.c:12270:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-12271-\tif (sk \u0026\u0026 sk-\u003esk_prot == \u0026tcpv6_prot \u0026\u0026 sk-\u003esk_state == TCP_NEW_SYN_RECV)\n--\nnet/core/filter.c=12504=__bpf_kfunc int bpf_sk_assign_tcp_reqsk(struct __sk_buff *s, struct sock *sk,\n--\nnet/core/filter.c-12506-{\nnet/core/filter.c:12507:#if IS_ENABLED(CONFIG_SYN_COOKIES)\nnet/core/filter.c-12508-\tstruct sk_buff *skb = (struct sk_buff *)s;\n--\nnet/core/filter.c-12532-\t\tbreak;\nnet/core/filter.c:12533:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-12534-\tcase htons(ETH_P_IPV6):\n--\nnet/core/filter.c=12733=__bpf_kfunc int bpf_icmp_send(struct __sk_buff *skb_ctx, int type, int code)\n--\nnet/core/filter.c-12746-\tswitch (skb-\u003eprotocol) {\nnet/core/filter.c:12747:#if IS_ENABLED(CONFIG_INET)\nnet/core/filter.c-12748-\tcase htons(ETH_P_IP): {\n--\nnet/core/filter.c-12766-#endif\nnet/core/filter.c:12767:#if IS_ENABLED(CONFIG_IPV6)\nnet/core/filter.c-12768-\tcase htons(ETH_P_IPV6): {\n"
} |
| 15/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 25198 output: 49 thoughts: 172 Thoughts: **My Assessment of eBPF Function Visibility** Okay, so I'm looking at how `bpf_skb_change_tail` and `sk_skb_change_tail` are made available within the eBPF ecosystem. First, `bpf_skb_change_tail` is accessible through `tc_cls_act_func_proto` and `lwt_xmit_func_proto`, which means the relevant configurations are `CONFIG_NET_CLS_ACT` and `CONFIG_LWTUNNEL_BPF`, respectively. Got it. That makes sense for traffic control and lightweight tunneling, so far so good. Then we have `sk_skb_change_tail`, which is exposed via `sk_skb_func_proto`. This one requires `CONFIG_BPF_STREAM_PARSER`, and the more general `CONFIG_INET` and `CONFIG_NET`. It's also exposed with `BPF_SYSCALL` and `NET`, which is pretty broad. Now I need to drill down, so let's cross-reference those function protos in `net/core/filter.c` to see how those are set up. |
| 16/2 | 2026/09/07 15:53 | tool | grepper |
0mArgs:{
"Expression": "bpf_skb_change_tail_proto|sk_skb_change_tail_proto",
"PathPrefix": "net/core/filter.c"
}Results: {
"Output": "net/core/filter.c=4028=BPF_CALL_3(bpf_skb_change_tail, struct sk_buff *, skb, u32, new_len,\n--\nnet/core/filter.c-4036-\nnet/core/filter.c:4037:static const struct bpf_func_proto bpf_skb_change_tail_proto = {\nnet/core/filter.c-4038-\t.func\t\t= bpf_skb_change_tail,\n--\nnet/core/filter.c=4046=BPF_CALL_3(sk_skb_change_tail, struct sk_buff *, skb, u32, new_len,\n--\nnet/core/filter.c-4051-\nnet/core/filter.c:4052:static const struct bpf_func_proto sk_skb_change_tail_proto = {\nnet/core/filter.c-4053-\t.func\t\t= sk_skb_change_tail,\n--\nnet/core/filter.c=8621=tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-8656-\tcase BPF_FUNC_skb_change_tail:\nnet/core/filter.c:8657:\t\treturn \u0026bpf_skb_change_tail_proto;\nnet/core/filter.c-8658-\tcase BPF_FUNC_skb_change_head:\n--\nnet/core/filter.c=8915=sk_skb_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-8924-\tcase BPF_FUNC_skb_change_tail:\nnet/core/filter.c:8925:\t\treturn \u0026sk_skb_change_tail_proto;\nnet/core/filter.c-8926-\tcase BPF_FUNC_skb_change_head:\n--\nnet/core/filter.c=9005=lwt_xmit_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nnet/core/filter.c-9020-\tcase BPF_FUNC_skb_change_tail:\nnet/core/filter.c:9021:\t\treturn \u0026bpf_skb_change_tail_proto;\nnet/core/filter.c-9022-\tcase BPF_FUNC_skb_change_head:\n"
} |
| 17/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 26336 output: 36 thoughts: 43 Thoughts: Here's my take on the thought process: **Diving into Traffic Control Function Prototypes** Okay, so I'm trying to figure something out with `tc_cls_act_func_proto`, and I suspect there might be some conditional compilation involved. My gut feeling is telling me to check for some `ifdef` directives that could be affecting its behavior. The best place to start is around line 8620 in the `net/core/filter.c` file. That seems like a reasonable location, considering the function name and its likely role in traffic control. Let's see if we can spot any compile-time flags affecting it. I'm hoping to find clues there that will shed light on what I'm dealing with. |
| 18/2 | 2026/09/07 15:53 | tool | read-file |
0mArgs:{
"File": "net/core/filter.c",
"FirstLine": 8615,
"LineCount": 20
}Results: {
"Contents": "8615:\t\tdefault:\n8616:\t\t\treturn sk_filter_func_proto(func_id, prog);\n8617:\t\t}\n8618:\t}\n8619:\t\n8620:\tstatic const struct bpf_func_proto *\n8621:\ttc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n8622:\t{\n8623:\t\tswitch (func_id) {\n8624:\t\tcase BPF_FUNC_skb_store_bytes:\n8625:\t\t\treturn \u0026bpf_skb_store_bytes_proto;\n8626:\t\tcase BPF_FUNC_skb_load_bytes:\n8627:\t\t\treturn \u0026bpf_skb_load_bytes_proto;\n8628:\t\tcase BPF_FUNC_skb_load_bytes_relative:\n8629:\t\t\treturn \u0026bpf_skb_load_bytes_relative_proto;\n8630:\t\tcase BPF_FUNC_skb_pull_data:\n8631:\t\t\treturn \u0026bpf_skb_pull_data_proto;\n8632:\t\tcase BPF_FUNC_csum_diff:\n8633:\t\t\treturn \u0026bpf_csum_diff_proto;\n8634:\t\tcase BPF_FUNC_csum_update:\n"
} |
| 19/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 26940 output: 36 thoughts: 19 Thoughts: Here's my take on those thoughts: **Tracing the Usage of `tc_cls_act_func_proto`** Okay, so I need to understand the implications of `tc_cls_act_func_proto`. The most logical first step is to see *where* this thing is actually being used. Understanding the call sites is crucial. Knowing the context in which `tc_cls_act_func_proto` is employed will tell me everything from its purpose to its potential impact on the system. Time to fire up the tools and trace those references. It's the only way to get a solid grasp on what's going on. |
| 20/2 | 2026/09/07 15:53 | tool | grepper |
0mArgs:{
"Expression": "tc_cls_act_func_proto",
"PathPrefix": "net/core/filter.c"
}Results: {
"Output": "net/core/filter.c=8620=static const struct bpf_func_proto *\nnet/core/filter.c:8621:tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\nnet/core/filter.c-8622-{\n--\nnet/core/filter.c=11503=const struct bpf_verifier_ops tc_cls_act_verifier_ops = {\nnet/core/filter.c:11504:\t.get_func_proto\t\t= tc_cls_act_func_proto,\nnet/core/filter.c-11505-\t.is_valid_access\t= tc_cls_act_is_valid_access,\n"
} |
| 21/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 27332 output: 35 thoughts: 153 Thoughts: **My Analysis of tc_cls_act Verification** Okay, so I'm digging into how the `tc_cls_act` verifier operates, specifically looking at how the BPF program types are defined. First, I need to check the definitions within `include/linux/bpf_types.h`. I'm expecting to find the key program types, and there they are: `BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_CLS, ...)` and `BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_ACT, ...)` specifically. Now I need to understand the dependencies. Based on my existing knowledge, these types should require some kernel configurations to be enabled. I see that `CONFIG_NET_CLS_ACT` and either `CONFIG_NET_SCHED` or `CONFIG_BPF_SYSCALL` (or possibly both) are what I'm looking for. Now that I have the basic lay of the land, it's back to `include/linux/bpf_types.h`. I need to confirm the exact dependencies on those configurations for these `tc_cls_act` program types. I need to be exact here, there may be some subtlety in the definitions I need to be aware of. |
| 22/2 | 2026/09/07 15:53 | tool | grepper |
0mArgs:{
"Expression": "tc_cls_act",
"PathPrefix": "include/linux/bpf_types.h"
}Results: {
"Output": "include/linux/bpf_types.h=5=BPF_PROG_TYPE(BPF_PROG_TYPE_SOCKET_FILTER, sk_filter,\ninclude/linux/bpf_types.h-6-\t struct __sk_buff, struct sk_buff)\ninclude/linux/bpf_types.h:7:BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_CLS, tc_cls_act,\ninclude/linux/bpf_types.h-8-\t struct __sk_buff, struct sk_buff)\ninclude/linux/bpf_types.h:9:BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_ACT, tc_cls_act,\ninclude/linux/bpf_types.h-10-\t struct __sk_buff, struct sk_buff)\n"
} |
| 23/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 28013 output: 36 thoughts: 35 Thoughts: Okay, here's my interpretation of that thought process, delivered in the first person, as if these were my internal considerations: **BPF Type Exploration: `NET_CLS_ACT`** Alright, I need to get a handle on `NET_CLS_ACT`. My first step, as always, is to pinpoint its declaration within the kernel's source code. Given my experience, I'm pretty sure it's defined in one of the core BPF header files. Specifically, I'm betting it'll be in `include/linux/bpf_types.h`. That's where a lot of the fundamental BPF data structures and type definitions reside. Let me zero in on the area around line 7. That's a reasonable starting point, given my understanding of the typical layout and how these types are often grouped. I'm expecting something relatively concise - probably a macro or an enumeration - defining this particular BPF classifier action. I'll be looking for clues about its purpose, its expected usage in the broader networking context, and any associated data types or constants. With my understanding of eBPF internals, I should be able to quickly determine its role and how it relates to other BPF components. Now let's see what's actually there! |
| 24/2 | 2026/09/07 15:53 | tool | read-file |
0mArgs:{
"File": "include/linux/bpf_types.h",
"FirstLine": 1,
"LineCount": 25
}Results: {
"Contents": " 1:\t/* SPDX-License-Identifier: GPL-2.0 */\n 2:\t/* internal file - do not include directly */\n 3:\t\n 4:\t#ifdef CONFIG_NET\n 5:\tBPF_PROG_TYPE(BPF_PROG_TYPE_SOCKET_FILTER, sk_filter,\n 6:\t\t struct __sk_buff, struct sk_buff)\n 7:\tBPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_CLS, tc_cls_act,\n 8:\t\t struct __sk_buff, struct sk_buff)\n 9:\tBPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_ACT, tc_cls_act,\n 10:\t\t struct __sk_buff, struct sk_buff)\n 11:\tBPF_PROG_TYPE(BPF_PROG_TYPE_XDP, xdp,\n 12:\t\t struct xdp_md, struct xdp_buff)\n 13:\t#ifdef CONFIG_CGROUP_BPF\n 14:\tBPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SKB, cg_skb,\n 15:\t\t struct __sk_buff, struct sk_buff)\n 16:\tBPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK, cg_sock,\n 17:\t\t struct bpf_sock, struct sock)\n 18:\tBPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, cg_sock_addr,\n 19:\t\t struct bpf_sock_addr, struct bpf_sock_addr_kern)\n 20:\t#endif\n 21:\tBPF_PROG_TYPE(BPF_PROG_TYPE_LWT_IN, lwt_in,\n 22:\t\t struct __sk_buff, struct sk_buff)\n 23:\tBPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_out,\n 24:\t\t struct __sk_buff, struct sk_buff)\n 25:\tBPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit,\n"
} |
| 25/2 | 2026/09/07 15:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 28925 output: 216 thoughts: 537 Thoughts: **My Assessment of the Code Change** Okay, so looking at these configurations, `CONFIG_NET`, `CONFIG_BPF_SYSCALL`, and of course `CONFIG_BPF` are all active, meaning that we have the necessary foundation for BPF programs related to traffic control (`BPF_PROG_TYPE_SCHED_CLS` / `BPF_PROG_TYPE_SCHED_ACT`) available. Now, let's dive into the core of this patch. My initial scan tells me the key functions affected are `bpf_skb_change_tail`, `sk_skb_change_tail`, `bpf_skb_trim_rcsum`, and `__bpf_skb_min_len`. I need to confirm if those last two might be inlined. Ah, yes, `__bpf_skb_min_len` is a static `u32` β potentially inlined β and `bpf_skb_trim_rcsum` is a static `int`, so probably inlined too. The others are non-static helper functions. I should include all those symbols for good measure. Now, a final verification of the `set-results` data: * **WorthFuzzing**: Absolutely! * **Reasoning**: This patch is touching core SKB (Socket Buffer) trimming logic, namely `bpf_skb_trim_rcsum` and `__bpf_skb_min_len`. The changes specifically involve resetting `skb->ip_summed` to `CHECKSUM_NONE` when trimming past the checksum offset for partial checksum packets and removes the checksum offset restriction. This has a direct impact on how BPF helpers manipulate SKBs (`bpf_skb_change_tail`, `sk_skb_change_tail`) and the handling of checksums within the networking stack. Given the reachability and potential for subtle interactions, it's definitely worth fuzzing. * **FocusSymbols**: `bpf_skb_change_tail`, `sk_skb_change_tail`, `bpf_skb_trim_rcsum`, `__bpf_skb_min_len` - confirmed. * **EnableConfigs**: `BPF_SYSCALL`, `NET` - correct. Okay, everything aligns. Let's send the `set-results` call with confidence. This patch is definitely a good candidate for thorough fuzzing. |
| 26/2 | 2026/09/07 15:54 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BPF_SYSCALL",
"NET"
],
"FocusSymbols": [
"bpf_skb_change_tail",
"sk_skb_change_tail",
"bpf_skb_trim_rcsum",
"__bpf_skb_min_len"
],
"Reasoning": "The patch modifies BPF skb trimming semantics in net/core/filter.c. Specifically, it removes the minimum length constraint based on partial checksum offsets in __bpf_skb_min_len and instead resets skb-\u003eip_summed to CHECKSUM_NONE when trimming packets below the checksum start/offset in bpf_skb_trim_rcsum. This changes executable logic in reachable BPF helper functions (bpf_skb_change_tail and sk_skb_change_tail) and affects network checksum processing, making it an important target for fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BPF_SYSCALL",
"NET"
],
"FocusSymbols": [
"bpf_skb_change_tail",
"sk_skb_change_tail",
"bpf_skb_trim_rcsum",
"__bpf_skb_min_len"
],
"Reasoning": "The patch modifies BPF skb trimming semantics in net/core/filter.c. Specifically, it removes the minimum length constraint based on partial checksum offsets in __bpf_skb_min_len and instead resets skb-\u003eip_summed to CHECKSUM_NONE when trimming packets below the checksum start/offset in bpf_skb_trim_rcsum. This changes executable logic in reachable BPF helper functions (bpf_skb_change_tail and sk_skb_change_tail) and affects network checksum processing, making it an important target for fuzzing.",
"WorthFuzzing": true
} |
| 27/1 | 2026/09/07 15:54 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 28/2 | 2026/09/07 15:54 | agent | kmsan-evaluator |
0mModel:core Args: null Results: {
"KMSANReasoning": "The patch modifies BPF skb tail adjustment logic (`bpf_skb_change_tail` / `bpf_skb_trim_rcsum`) by relaxing the minimum length calculation in `__bpf_skb_min_len` and resetting `skb-\u003eip_summed` to `CHECKSUM_NONE` when an skb is trimmed past the partial checksum offset.\n\nThis change does not introduce or expose uninitialized memory risks:\n1. It does not allocate uninitialized memory or alter data buffers in a way that exposes uninitialized bytes (when growing, memory is explicitly zeroed via `memset`; trimming reduces buffer length).\n2. It does not copy uninitialized kernel structures to userspace or read uninitialized struct fields/padding.\n3. Any potential buffer length errors or out-of-bounds access would be caught by KASAN.\n\nTherefore, a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false
}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: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 4c12eab9f4ec4a41a1dc8e9a4e48ca82de578cb1
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Sep 7 15:53:04 2026 +0000
syz-cluster: applied patch under review
diff --git a/net/core/filter.c b/net/core/filter.c
index 61940e7535523..8513167a858a8 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -3961,12 +3961,6 @@ static u32 __bpf_skb_min_len(const struct sk_buff *skb)
if (offset > 0)
min_len = offset;
}
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- offset = skb_checksum_start_offset(skb) +
- skb->csum_offset + sizeof(__sum16);
- if (offset > 0)
- min_len = offset;
- }
return min_len;
}
@@ -3983,6 +3977,11 @@ static int bpf_skb_grow_rcsum(struct sk_buff *skb, unsigned int new_len)
static int bpf_skb_trim_rcsum(struct sk_buff *skb, unsigned int new_len)
{
+ if (skb->ip_summed == CHECKSUM_PARTIAL &&
+ new_len < skb_checksum_start_offset(skb) + skb->csum_offset +
+ sizeof(__sum16))
+ skb->ip_summed = CHECKSUM_NONE;
+
return __skb_trim_rcsum(skb, new_len);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c b/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c
new file mode 100644
index 0000000000000..7acdbd5757a98
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/tc_change_tail_pmtu.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <netinet/tcp.h>
+
+#include "test_progs.h"
+#include "network_helpers.h"
+#include "test_tc_change_tail_pmtu.skel.h"
+
+#define CLIENT_NS "tc-change-tail-cli-ns"
+#define SERVER_NS "tc-change-tail-srv-ns"
+#define CLIENT_IP "192.168.1.1"
+#define SERVER_IP "192.168.1.2"
+
+#define TEST_PMTU 1000
+#define TEST_MSS_MAX (TEST_PMTU - 20 - 20)
+#define TIMEOUT_MS 3000
+#define XFER_BYTES 8192
+
+void test_tc_change_tail_pmtu(void)
+{
+ LIBBPF_OPTS(bpf_tcx_opts, tcx_opts);
+ int mss_before = 0, mss_after = 0, ifindex, port;
+ int srv_fd = -1, srv_conn_fd = -1, cli_fd = -1;
+ struct test_tc_change_tail_pmtu *skel = NULL;
+ struct nstoken *nstoken = NULL;
+ static char buf[XFER_BYTES];
+ socklen_t optlen;
+ ssize_t bytes;
+ size_t total;
+
+ if (!ASSERT_OK(make_netns(CLIENT_NS), "make client ns"))
+ return;
+ if (!ASSERT_OK(make_netns(SERVER_NS), "make server ns"))
+ goto out_client_ns;
+
+ nstoken = open_netns(CLIENT_NS);
+ if (!ASSERT_OK_PTR(nstoken, "open client ns"))
+ goto out;
+ SYS(out, "ip link add veth1 type veth peer name veth2 netns " SERVER_NS);
+ SYS(out, "ip -4 addr add " CLIENT_IP "/24 dev veth1");
+ SYS(out, "ip link set veth1 up");
+ ifindex = if_nametoindex("veth1");
+ if (!ASSERT_NEQ(ifindex, 0, "if_nametoindex"))
+ goto out;
+ close_netns(nstoken);
+ nstoken = NULL;
+
+ nstoken = open_netns(SERVER_NS);
+ if (!ASSERT_OK_PTR(nstoken, "open server ns"))
+ goto out;
+ SYS(out, "ip -4 addr add " SERVER_IP "/24 dev veth2");
+ SYS(out, "ip link set veth2 up");
+ srv_fd = start_server(AF_INET, SOCK_STREAM, SERVER_IP, 0, TIMEOUT_MS);
+ if (!ASSERT_OK_FD(srv_fd, "start server"))
+ goto out;
+ close_netns(nstoken);
+ nstoken = NULL;
+
+ skel = test_tc_change_tail_pmtu__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open and load skeleton"))
+ goto out;
+
+ port = get_socket_local_port(srv_fd);
+ if (!ASSERT_GE(port, 0, "get server port"))
+ goto out;
+
+ skel->bss->server_port = port;
+ skel->bss->pmtu = TEST_PMTU;
+
+ nstoken = open_netns(CLIENT_NS);
+ if (!ASSERT_OK_PTR(nstoken, "open client ns"))
+ goto out;
+
+ skel->links.change_tail_icmp =
+ bpf_program__attach_tcx(skel->progs.change_tail_icmp, ifindex,
+ &tcx_opts);
+ if (!ASSERT_OK_PTR(skel->links.change_tail_icmp, "attach tcx"))
+ goto out;
+
+ cli_fd = connect_to_fd(srv_fd, TIMEOUT_MS);
+ if (!ASSERT_OK_FD(cli_fd, "connect to server"))
+ goto out;
+ srv_conn_fd = accept(srv_fd, NULL, NULL);
+ if (!ASSERT_OK_FD(srv_conn_fd, "accept connection"))
+ goto out;
+ if (!ASSERT_OK(settimeo(srv_conn_fd, TIMEOUT_MS), "set server timeout"))
+ goto out;
+
+ optlen = sizeof(mss_before);
+ if (!ASSERT_OK(getsockopt(cli_fd, IPPROTO_TCP, TCP_MAXSEG, &mss_before,
+ &optlen), "get mss before"))
+ goto out;
+
+ bytes = send(cli_fd, buf, sizeof(buf), 0);
+ if (!ASSERT_EQ(bytes, (ssize_t)sizeof(buf), "send data"))
+ goto out;
+
+ for (total = 0; total < sizeof(buf); total += bytes) {
+ bytes = recv(srv_conn_fd, buf, sizeof(buf), 0);
+ if (bytes <= 0)
+ break;
+ }
+
+ ASSERT_EQ(total, sizeof(buf), "receive data");
+ ASSERT_OK(skel->data->change_tail_ret, "change tail");
+ ASSERT_OK(skel->bss->adjust_room_ret, "adjust room");
+ ASSERT_TRUE(skel->bss->icmp_sent, "icmp sent");
+
+ optlen = sizeof(mss_after);
+ if (!ASSERT_OK(getsockopt(cli_fd, IPPROTO_TCP, TCP_MAXSEG, &mss_after,
+ &optlen), "get mss after"))
+ goto out;
+
+ ASSERT_LT(mss_after, mss_before, "mss reduced");
+ ASSERT_LE(mss_after, TEST_MSS_MAX, "mss below pmtu");
+out:
+ close(srv_conn_fd);
+ close(cli_fd);
+ close(srv_fd);
+ test_tc_change_tail_pmtu__destroy(skel);
+ close_netns(nstoken);
+ remove_netns(SERVER_NS);
+out_client_ns:
+ remove_netns(CLIENT_NS);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c b/tools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c
new file mode 100644
index 0000000000000..5c4c07545bc9f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_tc_change_tail_pmtu.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <linux/bpf.h>
+#include <linux/icmp.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+#define ICMP_SAMPLE_LEN (sizeof(struct iphdr) + 8)
+#define ICMP_HDRS_LEN (sizeof(struct iphdr) + sizeof(struct icmphdr))
+
+__be16 server_port = 0;
+__u16 pmtu = 0;
+
+long change_tail_ret = 1;
+long adjust_room_ret = 0;
+bool icmp_sent = false;
+bool icmp_err = false;
+
+static __always_inline __sum16 csum_fold(__wsum csum)
+{
+ csum = (csum & 0xffff) + (csum >> 16);
+ csum = (csum & 0xffff) + (csum >> 16);
+
+ return (__sum16)~csum;
+}
+
+SEC("tc/egress")
+int change_tail_icmp(struct __sk_buff *skb)
+{
+ __u8 smac[ETH_ALEN], dmac[ETH_ALEN];
+ void *data, *data_end;
+ struct icmphdr *icmp;
+ struct ethhdr *eth;
+ struct tcphdr *tcp;
+ __be32 saddr, daddr;
+ struct iphdr *ip;
+ __wsum csum;
+
+ if (icmp_sent || icmp_err)
+ return TCX_PASS;
+
+ data = (void *)(long)skb->data;
+ data_end = (void *)(long)skb->data_end;
+
+ eth = data;
+ if ((void *)(eth + 1) > data_end)
+ return TCX_PASS;
+ if (eth->h_proto != bpf_htons(ETH_P_IP))
+ return TCX_PASS;
+
+ ip = (void *)(eth + 1);
+ if ((void *)(ip + 1) > data_end)
+ return TCX_PASS;
+ if (ip->ihl != 5 || ip->protocol != IPPROTO_TCP)
+ return TCX_PASS;
+
+ tcp = (void *)(ip + 1);
+ if ((void *)(tcp + 1) > data_end)
+ return TCX_PASS;
+ if (tcp->dest != server_port)
+ return TCX_PASS;
+ if (bpf_ntohs(ip->tot_len) <= sizeof(*ip) + tcp->doff * 4)
+ return TCX_PASS;
+
+ __builtin_memcpy(smac, eth->h_source, ETH_ALEN);
+ __builtin_memcpy(dmac, eth->h_dest, ETH_ALEN);
+ saddr = ip->saddr;
+ daddr = ip->daddr;
+
+ change_tail_ret = bpf_skb_change_tail(skb, ETH_HLEN + ICMP_SAMPLE_LEN, 0);
+ if (change_tail_ret) {
+ icmp_err = true;
+ return TCX_PASS;
+ }
+
+ adjust_room_ret = bpf_skb_adjust_room(skb, ICMP_HDRS_LEN,
+ BPF_ADJ_ROOM_MAC,
+ BPF_F_ADJ_ROOM_NO_CSUM_RESET);
+ if (adjust_room_ret) {
+ icmp_err = true;
+ return TCX_DROP;
+ }
+
+ data = (void *)(long)skb->data;
+ data_end = (void *)(long)skb->data_end;
+
+ eth = data;
+ ip = (void *)(eth + 1);
+ icmp = (void *)(ip + 1);
+ if ((void *)icmp + sizeof(*icmp) + ICMP_SAMPLE_LEN > data_end) {
+ icmp_err = true;
+ return TCX_DROP;
+ }
+
+ __builtin_memcpy(eth->h_dest, smac, ETH_ALEN);
+ __builtin_memcpy(eth->h_source, dmac, ETH_ALEN);
+
+ __builtin_memset(icmp, 0, sizeof(*icmp));
+ icmp->type = ICMP_DEST_UNREACH;
+ icmp->code = ICMP_FRAG_NEEDED;
+ icmp->un.frag.mtu = bpf_htons(pmtu);
+
+ __builtin_memset(ip, 0, sizeof(*ip));
+ ip->version = 4;
+ ip->ihl = 5;
+ ip->ttl = 64;
+ ip->protocol = IPPROTO_ICMP;
+ ip->tot_len = bpf_htons(ICMP_HDRS_LEN + ICMP_SAMPLE_LEN);
+ ip->saddr = daddr;
+ ip->daddr = saddr;
+
+ csum = bpf_csum_diff(NULL, 0, (__be32 *)icmp,
+ sizeof(*icmp) + ICMP_SAMPLE_LEN, 0);
+ icmp->checksum = csum_fold(csum);
+ csum = bpf_csum_diff(NULL, 0, (__be32 *)ip, sizeof(*ip), 0);
+ ip->check = csum_fold(csum);
+ icmp_sent = true;
+ return bpf_redirect(skb->ifindex, BPF_F_INGRESS);
+}
+
+char _license[] SEC("license") = "GPL";
|
| 29/3 | 2026/09/07 15:54 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 5736 output: 36 thoughts: 520 Thoughts: **KMSAN Fuzzing Session Justification**
Okay, so the question is, does this patch series warrant firing up a dedicated KMSAN fuzzing session? Let's break this down.
First, I need to analyze the diff itself. Ah, it modifies `net/core/filter.c` and adds BPF selftests. That's a good starting point. I always start with the actual code changes.
Let's dive into `net/core/filter.c` and see what's going on... Okay, line 3960 and around there...
```diff
@@ -3961,12 +3961,6 @@ static u32 __bpf_skb_min_len(const struct sk_buff *skb)
if (offset > 0)
min_len = offset;
}
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- offset = skb_checksum_start_offset(skb) +
- skb->csum_offset + sizeof(__sum16);
- if (offset > 0)
- min_len = offset;
- }
return min_len;
}
@@ -3983,6 +3977,11 @@ static int bpf_skb_grow_rcsum(struct sk_buff *skb, unsigned int new_len)
static int bpf_skb_trim_rcsum(struct sk_buff *skb, unsigned int new_len)
{
+ if (skb->ip_summed == CHECKSUM_PARTIAL &&
+ new_len < skb_checksum_start_offset(skb) + skb->csum_offset +
+ sizeof(__sum16))
+ skb->ip_summed = CHECKSUM_NONE;
+
return __skb_trim_rcsum(skb, new_len);
}
```
Right, so it's removing the checksum offset check in `__bpf_skb_min_len` and changing `bpf_skb_trim_rcsum` to clear the checksum when trimming. The removed check is there to prevent OOB access when dealing with `CHECKSUM_PARTIAL` skbs. The new code in `bpf_skb_trim_rcsum` seems to be intended to deal with checksums correctly when trimming.
So, potentially we have a change in checksum handling and bpf selftests. The removal of the check in `__bpf_skb_min_len` and the logic in `bpf_skb_trim_rcsum` might have some subtle corner cases that a KMSAN run might expose. This *could* be significant because it involves network packet manipulation and checksums - a classic area for memory corruption bugs. Now I need to decide if the risk outweighs the effort to setup KMSAN. This looks pretty good for a KMSAN session.
|
| 30/3 | 2026/09/07 15:54 | tool | read-file |
0mArgs:{
"File": "net/core/filter.c",
"FirstLine": 3940,
"LineCount": 60
}Results: {
"Contents": "3940:\t}\n3941:\t\n3942:\tstatic const struct bpf_func_proto bpf_skb_adjust_room_proto = {\n3943:\t\t.func\t\t= bpf_skb_adjust_room,\n3944:\t\t.gpl_only\t= false,\n3945:\t\t.ret_type\t= RET_INTEGER,\n3946:\t\t.arg1_type\t= ARG_PTR_TO_CTX,\n3947:\t\t.arg2_type\t= ARG_ANYTHING,\n3948:\t\t.arg3_type\t= ARG_ANYTHING,\n3949:\t\t.arg4_type\t= ARG_ANYTHING,\n3950:\t};\n3951:\t\n3952:\tstatic u32 __bpf_skb_min_len(const struct sk_buff *skb)\n3953:\t{\n3954:\t\tint offset = skb_network_offset(skb);\n3955:\t\tu32 min_len = 0;\n3956:\t\n3957:\t\tif (offset \u003e 0)\n3958:\t\t\tmin_len = offset;\n3959:\t\tif (skb_transport_header_was_set(skb)) {\n3960:\t\t\toffset = skb_transport_offset(skb);\n3961:\t\t\tif (offset \u003e 0)\n3962:\t\t\t\tmin_len = offset;\n3963:\t\t}\n3964:\t\treturn min_len;\n3965:\t}\n3966:\t\n3967:\tstatic int bpf_skb_grow_rcsum(struct sk_buff *skb, unsigned int new_len)\n3968:\t{\n3969:\t\tunsigned int old_len = skb-\u003elen;\n3970:\t\tint ret;\n3971:\t\n3972:\t\tret = __skb_grow_rcsum(skb, new_len);\n3973:\t\tif (!ret)\n3974:\t\t\tmemset(skb-\u003edata + old_len, 0, new_len - old_len);\n3975:\t\treturn ret;\n3976:\t}\n3977:\t\n3978:\tstatic int bpf_skb_trim_rcsum(struct sk_buff *skb, unsigned int new_len)\n3979:\t{\n3980:\t\tif (skb-\u003eip_summed == CHECKSUM_PARTIAL \u0026\u0026\n3981:\t\t new_len \u003c skb_checksum_start_offset(skb) + skb-\u003ecsum_offset +\n3982:\t\t\t sizeof(__sum16))\n3983:\t\t\tskb-\u003eip_summed = CHECKSUM_NONE;\n3984:\t\n3985:\t\treturn __skb_trim_rcsum(skb, new_len);\n3986:\t}\n3987:\t\n3988:\tstatic inline int __bpf_skb_change_tail(struct sk_buff *skb, u32 new_len,\n3989:\t\t\t\t\t\tu64 flags)\n3990:\t{\n3991:\t\tu32 max_len = BPF_SKB_MAX_LEN;\n3992:\t\tu32 min_len = __bpf_skb_min_len(skb);\n3993:\t\tint ret;\n3994:\t\n3995:\t\tif (unlikely(flags || new_len \u003e max_len || new_len \u003c min_len))\n3996:\t\t\treturn -EINVAL;\n3997:\t\tif (skb-\u003eencapsulation)\n3998:\t\t\treturn -ENOTSUPP;\n3999:\t\n"
} |
| 31/3 | 2026/09/07 15:54 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 7946 output: 22 thoughts: 179 Thoughts: **Analyzing this Commit's Logic for SKB Trimming** Okay, let's dive into this commit and understand its implications. My initial thought is to figure out the reasoning behind this change. Previously, the `__bpf_skb_min_len` check was preventing `bpf_skb_change_tail` from shrinking an SKB (Socket Buffer) beyond a certain point β specifically, the checksum-related offset calculated using `skb_checksum_start_offset(skb) + skb->csum_offset + sizeof(__sum16)`. That makes sense; we don't want to mess up the checksum calculations by arbitrarily truncating the buffer. Now, however, it looks like they're modifying the `bpf_skb_trim_rcsum` process. Instead of rejecting the trim in the `__bpf_skb_min_len` check if the intended `new_len` is less than the partial checksum offset, the code *allows* the trim to happen. The significant consequence is that it also *resets* `skb->ip_summed` to `CHECKSUM_NONE`. This is the crux of the change, and it demands further investigation. My next step is to examine `__skb_trim_rcsum` or related functions to determine what specific actions are taken when an SKB is trimmed in this new paradigm. I need to understand the side effects of effectively disabling checksumming after a potentially aggressive trim. Is this expected behavior? Are there other parts of the code that need to be aware of this change? I'll probably also want to cross-reference this with the checksum offload capabilities we are employing to ensure it doesn't cause unexpected interactions. I'm keen to uncover the rationale behind this modification and ensure its correctness and safety. |
| 32/3 | 2026/09/07 15:54 | tool | grepper |
0mArgs:{
"Expression": "__skb_trim_rcsum"
}Results: {
"Output": "include/linux/skbuff.h=4159=static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len)\n--\ninclude/linux/skbuff.h-4166-\ninclude/linux/skbuff.h:4167:static inline int __skb_trim_rcsum(struct sk_buff *skb, unsigned int len)\ninclude/linux/skbuff.h-4168-{\n--\nnet/core/filter.c=3978=static int bpf_skb_trim_rcsum(struct sk_buff *skb, unsigned int new_len)\n--\nnet/core/filter.c-3984-\nnet/core/filter.c:3985:\treturn __skb_trim_rcsum(skb, new_len);\nnet/core/filter.c-3986-}\n"
} |
| 33/3 | 2026/09/07 15:54 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 8758 output: 37 thoughts: 26 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into the skb_trim_rcsum Implementation** Okay, so I need to understand `__skb_trim_rcsum`. Where's the authoritative definition? Right, it should be in `include/linux/skbuff.h`. Time to pull up that header and refresh my memory on the specifics of how this function is intended to work. I need to understand the implications of trimming on the checksum calculations. Is it just a matter of adjusting the `csum` field? Are there more complex scenarios to consider, like fragmented SKBs or hardware checksum offload? I want to make sure I grasp the core logic before I get distracted by any particular use cases. Let's see how the internal workings relate to the `skb` data structure. |
| 34/3 | 2026/09/07 15:54 | tool | read-file |
0mArgs:{
"File": "include/linux/skbuff.h",
"FirstLine": 4165,
"LineCount": 25
}Results: {
"Contents": "4165:\t}\n4166:\t\n4167:\tstatic inline int __skb_trim_rcsum(struct sk_buff *skb, unsigned int len)\n4168:\t{\n4169:\t\tif (skb-\u003eip_summed == CHECKSUM_COMPLETE)\n4170:\t\t\tskb-\u003eip_summed = CHECKSUM_NONE;\n4171:\t\t__skb_trim(skb, len);\n4172:\t\treturn 0;\n4173:\t}\n4174:\t\n4175:\tstatic inline int __skb_grow_rcsum(struct sk_buff *skb, unsigned int len)\n4176:\t{\n4177:\t\tif (skb-\u003eip_summed == CHECKSUM_COMPLETE)\n4178:\t\t\tskb-\u003eip_summed = CHECKSUM_NONE;\n4179:\t\treturn __skb_grow(skb, len);\n4180:\t}\n4181:\t\n4182:\t#define rb_to_skb(rb) rb_entry_safe(rb, struct sk_buff, rbnode)\n4183:\t#define skb_rb_first(root) rb_to_skb(rb_first(root))\n4184:\t#define skb_rb_last(root) rb_to_skb(rb_last(root))\n4185:\t#define skb_rb_next(skb) rb_to_skb(rb_next(\u0026(skb)-\u003erbnode))\n4186:\t#define skb_rb_prev(skb) rb_to_skb(rb_prev(\u0026(skb)-\u003erbnode))\n4187:\t\n4188:\t#define skb_queue_walk(queue, skb) \\\n4189:\t\t\tfor (skb = (queue)-\u003enext;\t\t\t\t\t\\\n"
} |
| 35/3 | 2026/09/07 15:54 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 9495 output: 36 thoughts: 39 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into `__bpf_skb_change_tail` in `net/core/filter.c`** Alright, let's zero in on a critical section, `__bpf_skb_change_tail`, specifically around lines 3980 to 4030 within `net/core/filter.c`. I need to refresh my understanding of how this function interacts with the BPF subsystem, especially considering its implications for tail modifications on SKBs. My focus will be on the edge cases and potential race conditions associated with changing the tail pointer. I want to look closely at the memory barrier implementations within this function too. I'll need to remember to focus on the interaction with any locking primitives or atomic operations that could impact performance. |
| 36/3 | 2026/09/07 15:54 | tool | read-file |
0mArgs:{
"File": "net/core/filter.c",
"FirstLine": 3985,
"LineCount": 50
}Results: {
"Contents": "3985:\t\treturn __skb_trim_rcsum(skb, new_len);\n3986:\t}\n3987:\t\n3988:\tstatic inline int __bpf_skb_change_tail(struct sk_buff *skb, u32 new_len,\n3989:\t\t\t\t\t\tu64 flags)\n3990:\t{\n3991:\t\tu32 max_len = BPF_SKB_MAX_LEN;\n3992:\t\tu32 min_len = __bpf_skb_min_len(skb);\n3993:\t\tint ret;\n3994:\t\n3995:\t\tif (unlikely(flags || new_len \u003e max_len || new_len \u003c min_len))\n3996:\t\t\treturn -EINVAL;\n3997:\t\tif (skb-\u003eencapsulation)\n3998:\t\t\treturn -ENOTSUPP;\n3999:\t\n4000:\t\t/* The basic idea of this helper is that it's performing the\n4001:\t\t * needed work to either grow or trim an skb, and eBPF program\n4002:\t\t * rewrites the rest via helpers like bpf_skb_store_bytes(),\n4003:\t\t * bpf_lX_csum_replace() and others rather than passing a raw\n4004:\t\t * buffer here. This one is a slow path helper and intended\n4005:\t\t * for replies with control messages.\n4006:\t\t *\n4007:\t\t * Like in bpf_skb_change_proto(), we want to keep this rather\n4008:\t\t * minimal and without protocol specifics so that we are able\n4009:\t\t * to separate concerns as in bpf_skb_store_bytes() should only\n4010:\t\t * be the one responsible for writing buffers.\n4011:\t\t *\n4012:\t\t * It's really expected to be a slow path operation here for\n4013:\t\t * control message replies, so we're implicitly linearizing,\n4014:\t\t * uncloning and drop offloads from the skb by this.\n4015:\t\t */\n4016:\t\tret = __bpf_try_make_writable(skb, skb-\u003elen);\n4017:\t\tif (!ret) {\n4018:\t\t\tif (new_len \u003e skb-\u003elen)\n4019:\t\t\t\tret = bpf_skb_grow_rcsum(skb, new_len);\n4020:\t\t\telse if (new_len \u003c skb-\u003elen)\n4021:\t\t\t\tret = bpf_skb_trim_rcsum(skb, new_len);\n4022:\t\t\tif (!ret \u0026\u0026 skb_is_gso(skb))\n4023:\t\t\t\tskb_gso_reset(skb);\n4024:\t\t}\n4025:\t\treturn ret;\n4026:\t}\n4027:\t\n4028:\tBPF_CALL_3(bpf_skb_change_tail, struct sk_buff *, skb, u32, new_len,\n4029:\t\t u64, flags)\n4030:\t{\n4031:\t\tint ret = __bpf_skb_change_tail(skb, new_len, flags);\n4032:\t\n4033:\t\tbpf_compute_data_pointers(skb);\n4034:\t\treturn ret;\n"
} |
| 37/3 | 2026/09/07 15:54 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 10638 output: 222 thoughts: 519 Thoughts: **KMSAN Assessment: SKB Trimming in BPF** Okay, let's break this down. `bpf_skb_change_tail` is the function at play here, and it's responsible for both growing and trimming skbs. The key is to assess this change for KMSAN implications, specifically related to uninitialized memory. First, growing. Growing is handled with `bpf_skb_grow_rcsum` which *explicitly* zeroes out the newly allocated memory using `memset`. No concerns there. Now, trimming. The patch allows for trimming the skb to a shorter length, which was previously restricted. If the trim happens to cut past the partial checksum offset, we correctly reset `skb->ip_summed = CHECKSUM_NONE`. This is critical. We're proactively ensuring that the checksum calculation won't read past the new, reduced packet boundary. So, let's run through the KMSAN criteria: 1. **Kernel structures exposed to user space:** Nothing here. No data is being sent out, modified, or otherwise exposed in this way. 2. **Conditional logic based on uninitialized data:** No. There are no logic branches being determined by uninitialized variables. 3. **Complex data structure allocation/initialization:** Nope. The existing zero-ing logic already covers growing. The concern here is about shrinking, and we are setting the checksum flag correctly, or keeping it correct when shrinking. 4. **Bounds checks and lengths:** Ah, here's where it matters. Trimming *reduces* the buffer size. This means the allocated buffer will never be read past the boundaries. With the csum adjustment as well, this is not an issue. We are clearing out the ip\_summed setting when trimming. Therefore, *no* uninitialized memory reads or information leaks are being introduced. KASAN and other sanitizers cover the boundary conditions, as expected. This change is KMSAN-safe. I'm satisfied. Let's make sure the tool call goes through smoothly. |
| 38/3 | 2026/09/07 15:54 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch modifies BPF skb tail adjustment logic (`bpf_skb_change_tail` / `bpf_skb_trim_rcsum`) by relaxing the minimum length calculation in `__bpf_skb_min_len` and resetting `skb-\u003eip_summed` to `CHECKSUM_NONE` when an skb is trimmed past the partial checksum offset.\n\nThis change does not introduce or expose uninitialized memory risks:\n1. It does not allocate uninitialized memory or alter data buffers in a way that exposes uninitialized bytes (when growing, memory is explicitly zeroed via `memset`; trimming reduces buffer length).\n2. It does not copy uninitialized kernel structures to userspace or read uninitialized struct fields/padding.\n3. Any potential buffer length errors or out-of-bounds access would be caught by KASAN.\n\nTherefore, a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch modifies BPF skb tail adjustment logic (`bpf_skb_change_tail` / `bpf_skb_trim_rcsum`) by relaxing the minimum length calculation in `__bpf_skb_min_len` and resetting `skb-\u003eip_summed` to `CHECKSUM_NONE` when an skb is trimmed past the partial checksum offset.\n\nThis change does not introduce or expose uninitialized memory risks:\n1. It does not allocate uninitialized memory or alter data buffers in a way that exposes uninitialized bytes (when growing, memory is explicitly zeroed via `memset`; trimming reduces buffer length).\n2. It does not copy uninitialized kernel structures to userspace or read uninitialized struct fields/padding.\n3. Any potential buffer length errors or out-of-bounds access would be caught by KASAN.\n\nTherefore, a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|