AI Trajectory


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

Results:
{
  "EnableConfigs": [
    "INET",
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_ksock_create",
    "bpf_ksock_connect",
    "bpf_ksock_acquire",
    "bpf_ksock_release",
    "bpf_ksock_send",
    "connect_socket"
  ],
  "KMSANReasoning": "The patch introduces a new BPF kfunc API for creating, connecting, and sending data over kernel sockets (`bpf_ksock`). \n\nFrom an uninitialized memory perspective:\n1. All newly introduced kernel structures and stack variables are properly initialized:\n   - `struct bpf_ksock` is allocated using `kzalloc_obj`, ensuring all fields are zeroed.\n   - `struct msghdr msg` and `struct kvec iov` in `bpf_ksock_send` are initialized using designated initializers (`= { ... }`), which guarantees that all unspecified fields are zero-initialized by the compiler.\n   - `struct sockaddr_storage sa` in `bpf_ksock_connect` is partially initialized via `memcpy` from the BPF-provided address (up to 28 bytes). The uninitialized portion is never read, as `connect_socket` only reads up to `addrlen` (16 or 28 bytes). This exactly mirrors the behavior of the standard `connect()` syscall (which leaves the tail of `sockaddr_storage` uninitialized via `move_addr_to_kernel`), so it does not introduce a new info-leak risk.\n   - `opts_copy` in `bpf_ksock_create` is fully initialized by reading the 4 byte fields explicitly.\n2. The data sent over the socket in `bpf_ksock_send` is provided by the BPF program. The BPF verifier ensures that memory passed to kfuncs is within bounds and initialized (for stack variables). While BPF map values might contain uninitialized padding, this is a known property of the BPF subsystem and not a bug in the `bpf_ksock` C implementation.\n3. The primary risks introduced by this patch are related to object lifetime (use-after-free of `bpf_ksock` or `socket`), reference counting, and out-of-bounds accesses. These are perfectly covered by KASAN and standard bug detectors.\n\nThere are no uninitialized memory info-leaks or uses of uninitialized memory in conditional logic introduced by this patch. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch introduces a new BPF kfunc feature that allows BPF programs to create, connect, and send data through kernel sockets. This adds new reachable code paths in the BPF subsystem and networking core, which should be fuzzed.",
  "WorthFuzzing": true
}

1/1 2026/08/07 20:19 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 2271d840dc63021eef44ce76b20d82c0bea281f6\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Aug 7 20:19:11 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/include/linux/bpf_ksock.h b/include/linux/bpf_ksock.h\nnew file mode 100644\nindex 0000000000000..cb387fb75e43b\n--- /dev/null\n+++ b/include/linux/bpf_ksock.h\n@@ -0,0 +1,36 @@\n+/* SPDX-License-Identifier: GPL-2.0-only */\n+/* Copyright (c) 2026 Isovalent */\n+\n+#ifndef _BPF_KSOCK_H\n+#define _BPF_KSOCK_H\n+\n+#include \u003clinux/types.h\u003e\n+#include \u003clinux/in.h\u003e\n+#include \u003clinux/in6.h\u003e\n+\n+/**\n+ * struct bpf_ksock_create_opts - BPF kernel socket creation parameters\n+ * @family:\tAddress family: AF_INET or AF_INET6.\n+ * @type:\tSocket type: only SOCK_DGRAM supported for now.\n+ * @protocol:\tProtocol number (e.g. IPPROTO_UDP), or 0 for the default protocol\n+ *\t\tof the given type.\n+ * @reserved:\tMust be zero. Reserved for future use.\n+ */\n+struct bpf_ksock_create_opts {\n+\t__u8 family;\n+\t__u8 type;\n+\t__u8 protocol;\n+\t__u8 reserved;\n+};\n+\n+/**\n+ * union bpf_ksock_addr - IPv4 or IPv6 socket address\n+ * @sin: IPv4 socket address.\n+ * @sin6: IPv6 socket address.\n+ */\n+union bpf_ksock_addr {\n+\tstruct sockaddr_in sin;\n+\tstruct sockaddr_in6 sin6;\n+};\n+\n+#endif /* _BPF_KSOCK_H */\ndiff --git a/include/linux/socket.h b/include/linux/socket.h\nindex 2a8d7b14f1d11..5a5eb12501032 100644\n--- a/include/linux/socket.h\n+++ b/include/linux/socket.h\n@@ -461,6 +461,8 @@ extern struct file *__sys_socket_file(int family, int type, int protocol);\n extern int __sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen);\n extern int __sys_bind_socket(struct socket *sock, struct sockaddr_storage *address,\n \t\t\t     int addrlen);\n+int connect_socket(struct socket *sock, struct sockaddr_storage *addr,\n+\t\t   int addrlen, int flags);\n extern int __sys_connect_file(struct file *file, struct sockaddr_storage *addr,\n \t\t\t      int addrlen, int file_flags);\n extern int __sys_connect(int fd, struct sockaddr __user *uservaddr,\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex e6233c0081d10..037df7232f426 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -4473,6 +4473,9 @@ BTF_ID(struct, task_struct)\n #ifdef CONFIG_CRYPTO\n BTF_ID(struct, bpf_crypto_ctx)\n #endif\n+#ifdef CONFIG_INET\n+BTF_ID(struct, bpf_ksock)\n+#endif\n BTF_SET_END(rcu_protected_types)\n \n static bool rcu_protected_object(const struct btf *btf, u32 btf_id)\ndiff --git a/net/core/Makefile b/net/core/Makefile\nindex b3fdcb4e355fa..a9295b7859010 100644\n--- a/net/core/Makefile\n+++ b/net/core/Makefile\n@@ -44,6 +44,9 @@ obj-$(CONFIG_FAILOVER) += failover.o\n obj-$(CONFIG_NET_SOCK_MSG) += skmsg.o\n obj-$(CONFIG_BPF_SYSCALL) += sock_map.o\n obj-$(CONFIG_BPF_SYSCALL) += bpf_sk_storage.o\n+ifneq ($(CONFIG_INET),)\n+obj-$(CONFIG_BPF_SYSCALL) += bpf_ksock.o\n+endif\n obj-$(CONFIG_OF)\t+= of_net.o\n obj-$(CONFIG_NET_TEST) += net_test.o\n obj-$(CONFIG_NET_DEVMEM) += devmem.o\ndiff --git a/net/core/bpf_ksock.c b/net/core/bpf_ksock.c\nnew file mode 100644\nindex 0000000000000..8a4f0150a0a41\n--- /dev/null\n+++ b/net/core/bpf_ksock.c\n@@ -0,0 +1,335 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/* Copyright (c) 2026 Isovalent */\n+\n+#include \u003clinux/bpf.h\u003e\n+#include \u003clinux/bpf_ksock.h\u003e\n+#include \u003clinux/btf.h\u003e\n+#include \u003clinux/btf_ids.h\u003e\n+#include \u003clinux/in.h\u003e\n+#include \u003clinux/in6.h\u003e\n+#include \u003clinux/net.h\u003e\n+#include \u003clinux/refcount.h\u003e\n+#include \u003clinux/sched.h\u003e\n+#include \u003clinux/slab.h\u003e\n+#include \u003clinux/socket.h\u003e\n+#include \u003clinux/unaligned.h\u003e\n+#include \u003clinux/workqueue.h\u003e\n+#include \u003clinux/ip.h\u003e\n+#include \u003cnet/sock.h\u003e\n+\n+/**\n+ * struct bpf_ksock - refcounted BPF kernel socket context\n+ * @sock:\tThe underlying kernel socket.\n+ * @usage:\tReference counter.\n+ * @rwork:\tRCU work for deferred cleanup (sock_release may sleep).\n+ */\n+struct bpf_ksock {\n+\tstruct socket *sock;\n+\trefcount_t usage;\n+\tstruct rcu_work rwork;\n+};\n+\n+static void ksock_release_work_fn(struct work_struct *work)\n+{\n+\tstruct bpf_ksock *ks =\n+\t\tcontainer_of(to_rcu_work(work), struct bpf_ksock, rwork);\n+\n+\tsock_release(ks-\u003esock);\n+\tkfree(ks);\n+}\n+\n+static bool bpf_ksock_has_user_task_context(void)\n+{\n+\t/*\n+\t * Task work can run from do_exit() after exit_nsproxy_namespaces()\n+\t * cleared current-\u003ensproxy, while current is still not a kthread.\n+\t */\n+\treturn !(current-\u003eflags \u0026 PF_KTHREAD) \u0026\u0026 current-\u003ensproxy;\n+}\n+\n+__bpf_kfunc_start_defs();\n+\n+/**\n+ * bpf_ksock_create() - Create a BPF kernel socket.\n+ *\n+ * Allocates and creates a kernel socket.\n+ *\n+ * The returned context must either be stored in a map as a kptr, or\n+ * freed with bpf_ksock_release().\n+ *\n+ * This function may sleep (sock_create), so it can only be used\n+ * in sleepable BPF programs (SYSCALL).\n+ * It cannot be called from a BPF workqueue callback because that callback\n+ * does not retain the invoking task's namespace or security context.\n+ *\n+ * @opts:\tPointer to struct bpf_ksock_create_opts with socket parameters.\n+ * @opts__sz:\tSize of the opts struct.\n+ * @err__uninit:\tInteger to store error code when NULL is returned.\n+ */\n+__bpf_kfunc struct bpf_ksock *\n+bpf_ksock_create(const struct bpf_ksock_create_opts *opts, u32 opts__sz,\n+\t\t int *err__uninit)\n+{\n+\tstruct bpf_ksock_create_opts opts_copy;\n+\tstruct bpf_ksock *ks;\n+\tint err;\n+\n+\t/*\n+\t * sock_create() derives the network namespace, credentials, and cgroup\n+\t * from current. Kernel threads, including BPF workqueue callbacks, do\n+\t * not carry the context of the task that invoked the BPF program.\n+\t */\n+\tif (!bpf_ksock_has_user_task_context()) {\n+\t\terr = -EOPNOTSUPP;\n+\t\tgoto err_out;\n+\t}\n+\n+\tif (!opts || opts__sz != sizeof(struct bpf_ksock_create_opts)) {\n+\t\terr = -EINVAL;\n+\t\tgoto err_out;\n+\t}\n+\n+\topts_copy = (struct bpf_ksock_create_opts){\n+\t\t.family = READ_ONCE(opts-\u003efamily),\n+\t\t.type = READ_ONCE(opts-\u003etype),\n+\t\t.protocol = READ_ONCE(opts-\u003eprotocol),\n+\t\t.reserved = READ_ONCE(opts-\u003ereserved),\n+\t};\n+\n+\tif (opts_copy.reserved) {\n+\t\terr = -EINVAL;\n+\t\tgoto err_out;\n+\t}\n+\n+\tif (opts_copy.family != AF_INET \u0026\u0026 opts_copy.family != AF_INET6) {\n+\t\terr = -EAFNOSUPPORT;\n+\t\tgoto err_out;\n+\t}\n+\n+\tif (opts_copy.type != SOCK_DGRAM) {\n+\t\terr = -EPROTONOSUPPORT;\n+\t\tgoto err_out;\n+\t}\n+\n+\tif (opts_copy.protocol != IPPROTO_UDP \u0026\u0026 opts_copy.protocol != 0) {\n+\t\terr = -EPROTONOSUPPORT;\n+\t\tgoto err_out;\n+\t}\n+\n+\tks = kzalloc_obj(*ks);\n+\tif (!ks) {\n+\t\terr = -ENOMEM;\n+\t\tgoto err_out;\n+\t}\n+\n+\t/*\n+\t * Use the normal current-task socket path so LSM/cgroup policy,\n+\t * socket labels, and the active netns reference match a socket(2)\n+\t * created by the BPF program's caller.\n+\t */\n+\terr = sock_create(opts_copy.family, opts_copy.type, opts_copy.protocol,\n+\t\t\t  \u0026ks-\u003esock);\n+\tif (err)\n+\t\tgoto err_free;\n+\n+\tks-\u003esock-\u003esk-\u003esk_rcvbuf = SOCK_MIN_RCVBUF;\n+\tks-\u003esock-\u003esk-\u003esk_userlocks |= SOCK_RCVBUF_LOCK;\n+\n+\trefcount_set(\u0026ks-\u003eusage, 1);\n+\tput_unaligned(0, err__uninit);\n+\treturn ks;\n+\n+err_free:\n+\tkfree(ks);\n+err_out:\n+\tput_unaligned(err, err__uninit);\n+\treturn NULL;\n+}\n+\n+/**\n+ * bpf_ksock_connect() - Connect a BPF kernel socket to a remote address.\n+ * @ks:\t\tThe BPF kernel socket context.\n+ * @addr:\tPointer to an IPv4 or IPv6 socket address.\n+ * @addr__sz:\tSize of the address union.\n+ *\n+ * Connects the socket to the specified remote address and port.\n+ *\n+ * This function may sleep while connecting the socket, so it can only be used\n+ * in sleepable BPF programs (SYSCALL).\n+ *\n+ * Return: 0 on success, negative errno on error.\n+ */\n+__bpf_kfunc int bpf_ksock_connect(struct bpf_ksock *ks,\n+\t\t\t\t  const union bpf_ksock_addr *addr,\n+\t\t\t\t  u32 addr__sz)\n+{\n+\tstruct sockaddr_storage sa;\n+\tint addrlen;\n+\n+\tif (!bpf_ksock_has_user_task_context())\n+\t\treturn -EOPNOTSUPP;\n+\n+\tif (!addr || addr__sz != sizeof(*addr))\n+\t\treturn -EINVAL;\n+\n+\t/* Kfunc memory arguments may be unaligned. */\n+\tmemcpy(\u0026sa, addr, sizeof(*addr));\n+\n+\tswitch (sa.ss_family) {\n+\tcase AF_INET:\n+\t\taddrlen = sizeof(struct sockaddr_in);\n+\t\tbreak;\n+#if IS_ENABLED(CONFIG_IPV6)\n+\tcase AF_INET6:\n+\t\taddrlen = sizeof(struct sockaddr_in6);\n+\t\tbreak;\n+#endif\n+\tdefault:\n+\t\treturn -EAFNOSUPPORT;\n+\t}\n+\n+\treturn connect_socket(ks-\u003esock, \u0026sa, addrlen, 0);\n+}\n+\n+/**\n+ * bpf_ksock_acquire() - Acquire a reference to a BPF kernel socket.\n+ * @ks:\tThe BPF kernel socket context to acquire. Must be a\n+ *\ttrusted pointer (e.g. RCU-protected kptr from a map).\n+ *\n+ * The acquired context must either be stored in a map as a kptr, or\n+ * freed with bpf_ksock_release().\n+ */\n+__bpf_kfunc struct bpf_ksock *bpf_ksock_acquire(struct bpf_ksock *ks)\n+{\n+\tif (!refcount_inc_not_zero(\u0026ks-\u003eusage))\n+\t\treturn NULL;\n+\treturn ks;\n+}\n+\n+/**\n+ * bpf_ksock_release() - Release a BPF kernel socket.\n+ * @ks:\tThe BPF kernel socket context to release.\n+ *\n+ * When the final reference is released, the socket is cleaned up via\n+ * queue_rcu_work() (since sock_release may sleep).\n+ */\n+__bpf_kfunc void bpf_ksock_release(struct bpf_ksock *ks)\n+{\n+\tif (refcount_dec_and_test(\u0026ks-\u003eusage)) {\n+\t\tINIT_RCU_WORK(\u0026ks-\u003erwork, ksock_release_work_fn);\n+\t\tqueue_rcu_work(system_dfl_wq, \u0026ks-\u003erwork);\n+\t}\n+}\n+\n+__bpf_kfunc void bpf_ksock_release_dtor(void *ks)\n+{\n+\tbpf_ksock_release(ks);\n+}\n+CFI_NOSEAL(bpf_ksock_release_dtor);\n+\n+/**\n+ * bpf_ksock_send() - Send data through a BPF kernel socket.\n+ * @ks:\t\tThe BPF kernel socket context. Must be an acquired reference.\n+ * @data:\tPointer to the data to send.\n+ * @data__sz:\tSize of the data to send (max 65535 bytes).\n+ *\n+ * Sends data on a connected socket, best-effort and nonblocking. This may sleep\n+ * (kernel_sendmsg), so it can only be called from sleepable BPF programs.\n+ *\n+ * Return: Number of bytes sent on success, negative errno on error.\n+ */\n+__bpf_kfunc int bpf_ksock_send(struct bpf_ksock *ks, const void *data,\n+\t\t\t       u32 data__sz)\n+{\n+\tstruct msghdr msg = {\n+\t\t.msg_flags = MSG_DONTWAIT,\n+\t};\n+\tstruct kvec iov = {\n+\t\t.iov_base = (void *)data,\n+\t\t.iov_len = data__sz,\n+\t};\n+\tint ret;\n+\n+\tif (!bpf_ksock_has_user_task_context())\n+\t\treturn -EOPNOTSUPP;\n+\n+\t/* Early check for UDP. Exact limits enforced by kernel_sendmsg(). */\n+\tif (data__sz \u003e IP_MAX_MTU)\n+\t\treturn -EMSGSIZE;\n+\n+\tret = kernel_sendmsg(ks-\u003esock, \u0026msg, \u0026iov, 1, data__sz);\n+\n+\treturn ret;\n+}\n+\n+__bpf_kfunc_end_defs();\n+\n+BTF_KFUNCS_START(ksock_init_kfunc_btf_ids)\n+BTF_ID_FLAGS(func, bpf_ksock_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)\n+BTF_ID_FLAGS(func, bpf_ksock_connect, KF_SLEEPABLE)\n+BTF_KFUNCS_END(ksock_init_kfunc_btf_ids)\n+\n+static const struct btf_kfunc_id_set ksock_init_kfunc_set = {\n+\t.owner = THIS_MODULE,\n+\t.set = \u0026ksock_init_kfunc_btf_ids,\n+};\n+\n+BTF_KFUNCS_START(ksock_kfunc_btf_ids)\n+BTF_ID_FLAGS(func, bpf_ksock_release, KF_RELEASE)\n+BTF_ID_FLAGS(func, bpf_ksock_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)\n+BTF_ID_FLAGS(func, bpf_ksock_send, KF_SLEEPABLE)\n+BTF_KFUNCS_END(ksock_kfunc_btf_ids)\n+\n+#ifdef CONFIG_BPF_LSM\n+BTF_ID_LIST_SINGLE(bpf_lsm_socket_sendmsg_id, func, bpf_lsm_socket_sendmsg)\n+#endif\n+\n+static int bpf_ksock_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id)\n+{\n+\tif (!btf_id_set8_contains(\u0026ksock_kfunc_btf_ids, kfunc_id))\n+\t\treturn 0;\n+\n+\tif (prog-\u003etype == BPF_PROG_TYPE_SYSCALL)\n+\t\treturn 0;\n+\n+#ifdef CONFIG_BPF_LSM\n+\tif (prog-\u003etype == BPF_PROG_TYPE_LSM \u0026\u0026\n+\t    prog-\u003eaux-\u003eattach_btf_id != bpf_lsm_socket_sendmsg_id[0])\n+\t\treturn 0;\n+#endif\n+\n+\treturn -EACCES;\n+}\n+\n+static const struct btf_kfunc_id_set ksock_kfunc_set = {\n+\t.owner = THIS_MODULE,\n+\t.set = \u0026ksock_kfunc_btf_ids,\n+\t.filter = bpf_ksock_kfunc_filter,\n+};\n+\n+BTF_ID_LIST(bpf_ksock_dtor_ids)\n+BTF_ID(struct, bpf_ksock)\n+BTF_ID(func, bpf_ksock_release_dtor)\n+\n+static int __init bpf_ksock_kfunc_init(void)\n+{\n+\tint ret;\n+\tconst struct btf_id_dtor_kfunc bpf_ksock_dtors[] = {\n+\t\t{\n+\t\t\t.btf_id = bpf_ksock_dtor_ids[0],\n+\t\t\t.kfunc_btf_id = bpf_ksock_dtor_ids[1],\n+\t\t},\n+\t};\n+\n+\tret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL,\n+\t\t\t\t\t\u0026ksock_init_kfunc_set);\n+\tret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL,\n+\t\t\t\t\t       \u0026ksock_kfunc_set);\n+\tret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM,\n+\t\t\t\t\t       \u0026ksock_kfunc_set);\n+\treturn ret ?: register_btf_id_dtor_kfuncs(bpf_ksock_dtors,\n+\t\t\t\t\t\t  ARRAY_SIZE(bpf_ksock_dtors),\n+\t\t\t\t\t\t  THIS_MODULE);\n+}\n+\n+late_initcall(bpf_ksock_kfunc_init);\ndiff --git a/net/socket.c b/net/socket.c\nindex 63c69a0fa74e1..8f18f124d92a7 100644\n--- a/net/socket.c\n+++ b/net/socket.c\n@@ -2103,6 +2103,20 @@ SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,\n \treturn __sys_accept4(fd, upeer_sockaddr, upeer_addrlen, 0);\n }\n \n+int connect_socket(struct socket *sock, struct sockaddr_storage *address,\n+\t\t   int addrlen, int flags)\n+{\n+\tint err;\n+\n+\terr = security_socket_connect(sock, (struct sockaddr *)address, addrlen);\n+\tif (err)\n+\t\treturn err;\n+\n+\treturn READ_ONCE(sock-\u003eops)-\u003econnect(sock,\n+\t\t\t\t    (struct sockaddr_unsized *)address,\n+\t\t\t\t    addrlen, flags);\n+}\n+\n /*\n  *\tAttempt to connect to a socket with the server address.  The address\n  *\tis in user space so we verify it is OK and move it to kernel space.\n@@ -2119,23 +2133,13 @@ int __sys_connect_file(struct file *file, struct sockaddr_storage *address,\n \t\t       int addrlen, int file_flags)\n {\n \tstruct socket *sock;\n-\tint err;\n \n \tsock = sock_from_file(file);\n-\tif (!sock) {\n-\t\terr = -ENOTSOCK;\n-\t\tgoto out;\n-\t}\n-\n-\terr =\n-\t    security_socket_connect(sock, (struct sockaddr *)address, addrlen);\n-\tif (err)\n-\t\tgoto out;\n+\tif (!sock)\n+\t\treturn -ENOTSOCK;\n \n-\terr = READ_ONCE(sock-\u003eops)-\u003econnect(sock, (struct sockaddr_unsized *)address,\n-\t\t\t\t\t    addrlen, sock-\u003efile-\u003ef_flags | file_flags);\n-out:\n-\treturn err;\n+\treturn connect_socket(sock, address, addrlen,\n+\t\t\t\t    sock-\u003efile-\u003ef_flags | file_flags);\n }\n \n int __sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen)\ndiff --git a/tools/testing/selftests/bpf/prog_tests/ksock.c b/tools/testing/selftests/bpf/prog_tests/ksock.c\nnew file mode 100644\nindex 0000000000000..fb1a332eb3da1\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/prog_tests/ksock.c\n@@ -0,0 +1,133 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 Isovalent */\n+\n+#include \u003carpa/inet.h\u003e\n+\n+#include \"test_progs.h\"\n+#include \"network_helpers.h\"\n+#include \"ksock_lsm.skel.h\"\n+#include \"ksock_lsm_verifier.skel.h\"\n+\n+#define NS_TEST \"ksock_lsm_ns\"\n+#define RECV_PORT 7777\n+#define RECV_TIMEOUT_SEC 5\n+\n+struct ksock_test_env {\n+\tbool netns_created;\n+\tstruct nstoken *nstoken;\n+\tint rfd;\n+};\n+\n+static bool ksock_test_env_setup(struct ksock_test_env *env)\n+{\n+\tstruct sockaddr_in addr = {\n+\t\t.sin_family = AF_INET,\n+\t\t.sin_addr.s_addr = htonl(INADDR_LOOPBACK),\n+\t\t.sin_port = htons(RECV_PORT),\n+\t};\n+\tstruct timeval tv = { .tv_sec = RECV_TIMEOUT_SEC };\n+\tint err;\n+\n+\tmemset(env, 0, sizeof(*env));\n+\tenv-\u003erfd = -1;\n+\n+\tSYS(fail, \"ip netns add %s\", NS_TEST);\n+\tenv-\u003enetns_created = true;\n+\tSYS(fail, \"ip -net %s link set lo up\", NS_TEST);\n+\n+\tenv-\u003enstoken = open_netns(NS_TEST);\n+\tif (!ASSERT_OK_PTR(env-\u003enstoken, \"open_netns\"))\n+\t\tgoto fail;\n+\n+\tenv-\u003erfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);\n+\tif (!ASSERT_OK_FD(env-\u003erfd, \"receiver socket\"))\n+\t\tgoto fail;\n+\n+\terr = bind(env-\u003erfd, (struct sockaddr *)\u0026addr, sizeof(addr));\n+\tif (!ASSERT_OK(err, \"bind receiver\"))\n+\t\tgoto fail;\n+\n+\terr = setsockopt(env-\u003erfd, SOL_SOCKET, SO_RCVTIMEO, \u0026tv, sizeof(tv));\n+\tif (!ASSERT_OK(err, \"set rcvtimeo\"))\n+\t\tgoto fail;\n+\n+\treturn true;\n+\n+fail:\n+\treturn false;\n+}\n+\n+void test_ksock_lsm(void)\n+{\n+\tLIBBPF_OPTS(bpf_test_run_opts, opts);\n+\tstruct ksock_test_env env;\n+\tstruct sockaddr_in trigger_addr = {\n+\t\t.sin_family = AF_INET,\n+\t\t.sin_addr.s_addr = htonl(INADDR_LOOPBACK),\n+\t};\n+\tstruct ksock_lsm *skel;\n+\tchar recv_data[sizeof(skel-\u003edata-\u003esend_data)] = {};\n+\tssize_t n;\n+\tint tfd = -1;\n+\tint err;\n+\n+\tskel = ksock_lsm__open_and_load();\n+\tif (!ASSERT_OK_PTR(skel, \"skel open_and_load\"))\n+\t\treturn;\n+\n+\tif (!ksock_test_env_setup(\u0026env))\n+\t\tgoto fail;\n+\n+\t/* Step 1: Run the setup SYSCALL prog to create the ksock */\n+\tskel-\u003ebss-\u003eipv4_remote = htonl(INADDR_LOOPBACK);\n+\tskel-\u003ebss-\u003eremote_port = RECV_PORT;\n+\terr = bpf_prog_test_run_opts(bpf_program__fd(skel-\u003eprogs.ksock_setup),\n+\t\t\t\t     \u0026opts);\n+\tif (!ASSERT_OK(err, \"ksock_setup run\"))\n+\t\tgoto fail;\n+\tif (!ASSERT_OK(opts.retval, \"ksock_setup retval\"))\n+\t\tgoto fail;\n+\n+\t/* Step 2: Attach LSM prog and trigger socket_bind from userspace */\n+\tskel-\u003elinks.ksock_socket_bind =\n+\t\tbpf_program__attach_lsm(skel-\u003eprogs.ksock_socket_bind);\n+\tif (!ASSERT_OK_PTR(skel-\u003elinks.ksock_socket_bind,\n+\t\t\t   \"attach socket_bind lsm\"))\n+\t\tgoto fail;\n+\n+\ttfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);\n+\tif (!ASSERT_OK_FD(tfd, \"trigger socket\"))\n+\t\tgoto fail;\n+\n+\tskel-\u003ebss-\u003etarget_pid = getpid();\n+\terr = bind(tfd, (struct sockaddr *)\u0026trigger_addr, sizeof(trigger_addr));\n+\tskel-\u003ebss-\u003etarget_pid = 0;\n+\tif (!ASSERT_OK(err, \"trigger bind\"))\n+\t\tgoto fail;\n+\n+\t/* Step 3: Verify the LSM hook sent the notification */\n+\tif (!ASSERT_EQ(skel-\u003edata-\u003esend_ret, sizeof(skel-\u003edata-\u003esend_data),\n+\t\t       \"LSM send bytes\"))\n+\t\tgoto fail;\n+\n+\tn = recvfrom(env.rfd, recv_data, sizeof(recv_data), 0, NULL, NULL);\n+\tif (ASSERT_EQ(n, sizeof(recv_data), \"recvfrom len\"))\n+\t\tASSERT_MEMEQ(recv_data, skel-\u003edata-\u003esend_data, sizeof(recv_data),\n+\t\t\t     \"payload match\");\n+\n+fail:\n+\tif (tfd \u003e= 0)\n+\t\tclose(tfd);\n+\tif (env.rfd \u003e= 0)\n+\t\tclose(env.rfd);\n+\tif (env.nstoken)\n+\t\tclose_netns(env.nstoken);\n+\tif (env.netns_created)\n+\t\tSYS_NOFAIL(\"ip netns del %s \u003e/dev/null 2\u003e\u00261\", NS_TEST);\n+\tksock_lsm__destroy(skel);\n+}\n+\n+void test_ksock_lsm_verifier(void)\n+{\n+\tRUN_TESTS(ksock_lsm_verifier);\n+}\ndiff --git a/tools/testing/selftests/bpf/prog_tests/ksock_wq.c b/tools/testing/selftests/bpf/prog_tests/ksock_wq.c\nnew file mode 100644\nindex 0000000000000..5b9c6cc303922\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/prog_tests/ksock_wq.c\n@@ -0,0 +1,34 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 Isovalent */\n+\n+#include \u003cunistd.h\u003e\n+\n+#include \"test_progs.h\"\n+#include \"ksock_wq.skel.h\"\n+\n+void test_ksock_wq(void)\n+{\n+\tLIBBPF_OPTS(bpf_test_run_opts, opts);\n+\tstruct ksock_wq *skel;\n+\tint err;\n+\n+\tskel = ksock_wq__open_and_load();\n+\tif (!ASSERT_OK_PTR(skel, \"ksock_wq open and load\"))\n+\t\treturn;\n+\n+\terr = bpf_prog_test_run_opts(\n+\t\tbpf_program__fd(skel-\u003eprogs.ksock_wq_start), \u0026opts);\n+\tif (!ASSERT_OK(err, \"run ksock_wq_start\"))\n+\t\tgoto out;\n+\tif (!ASSERT_OK(opts.retval, \"ksock_wq_start retval\"))\n+\t\tgoto out;\n+\n+\twhile (!__atomic_load_n(\u0026skel-\u003ebss-\u003ecallback_done, __ATOMIC_ACQUIRE))\n+\t\tusleep(1000);\n+\n+\tASSERT_EQ(skel-\u003ebss-\u003ecreate_err, -EOPNOTSUPP,\n+\t\t  \"workqueue create rejected\");\n+\n+out:\n+\tksock_wq__destroy(skel);\n+}\ndiff --git a/tools/testing/selftests/bpf/progs/ksock_common.h b/tools/testing/selftests/bpf/progs/ksock_common.h\nnew file mode 100644\nindex 0000000000000..01edaeb9fdd4a\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/ksock_common.h\n@@ -0,0 +1,78 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Copyright (c) 2026 Isovalent */\n+\n+#ifndef _KSOCK_COMMON_H\n+#define _KSOCK_COMMON_H\n+\n+#include \"errno.h\"\n+\n+#define SOCK_DGRAM\t2\n+#define IPPROTO_UDP\t17\n+\n+struct bpf_ksock *bpf_ksock_create(const struct bpf_ksock_create_opts *opts,\n+\t\t\t\t   u32 opts__sz, int *err__uninit) __ksym;\n+int bpf_ksock_connect(struct bpf_ksock *ks, const union bpf_ksock_addr *addr,\n+\t\t      u32 addr__sz) __ksym;\n+struct bpf_ksock *bpf_ksock_acquire(struct bpf_ksock *ks) __ksym;\n+void bpf_ksock_release(struct bpf_ksock *ks) __ksym;\n+int bpf_ksock_send(struct bpf_ksock *ks, const void *data, u32 data__sz) __ksym;\n+void bpf_rcu_read_lock(void) __ksym;\n+void bpf_rcu_read_unlock(void) __ksym;\n+\n+struct __ksock_ctx_value {\n+\tstruct bpf_ksock __kptr * ctx;\n+};\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_ARRAY);\n+\t__type(key, int);\n+\t__type(value, struct __ksock_ctx_value);\n+\t__uint(max_entries, 1);\n+} __ksock_ctx_map SEC(\".maps\");\n+\n+static inline struct __ksock_ctx_value *ksock_ctx_value_lookup(void)\n+{\n+\tu32 key = 0;\n+\n+\treturn bpf_map_lookup_elem(\u0026__ksock_ctx_map, \u0026key);\n+}\n+\n+static inline struct bpf_ksock *ksock_ctx_get(void)\n+{\n+\tstruct __ksock_ctx_value *v;\n+\tstruct bpf_ksock *ks = NULL, *tmp;\n+\n+\tv = ksock_ctx_value_lookup();\n+\tif (!v)\n+\t\treturn NULL;\n+\n+\tbpf_rcu_read_lock();\n+\ttmp = v-\u003ectx;\n+\tif (tmp)\n+\t\tks = bpf_ksock_acquire(tmp);\n+\tbpf_rcu_read_unlock();\n+\n+\treturn ks;\n+}\n+\n+static inline int ksock_ctx_insert(struct bpf_ksock *ctx)\n+{\n+\tstruct __ksock_ctx_value *v;\n+\tstruct bpf_ksock *old;\n+\n+\tv = ksock_ctx_value_lookup();\n+\tif (!v) {\n+\t\tbpf_ksock_release(ctx);\n+\t\treturn -ENOENT;\n+\t}\n+\n+\told = bpf_kptr_xchg(\u0026v-\u003ectx, ctx);\n+\tif (old) {\n+\t\tbpf_ksock_release(old);\n+\t\treturn -EEXIST;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+#endif /* _KSOCK_COMMON_H */\ndiff --git a/tools/testing/selftests/bpf/progs/ksock_lsm.c b/tools/testing/selftests/bpf/progs/ksock_lsm.c\nnew file mode 100644\nindex 0000000000000..9808451098efb\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/ksock_lsm.c\n@@ -0,0 +1,72 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 Isovalent */\n+\n+#include \"vmlinux.h\"\n+#include \u003cbpf/bpf_helpers.h\u003e\n+#include \u003cbpf/bpf_tracing.h\u003e\n+#include \u003cbpf/bpf_endian.h\u003e\n+#include \"bpf_tracing_net.h\"\n+#include \"ksock_common.h\"\n+\n+char send_data[32] = \"hello from bpf ksock\";\n+\n+__be32 ipv4_remote;\n+__u16 remote_port;\n+int target_pid;\n+int send_ret = -1;\n+\n+SEC(\"syscall\")\n+int ksock_setup(void *ctx)\n+{\n+\tstruct bpf_ksock_create_opts create_opts = {};\n+\tunion bpf_ksock_addr addr = {};\n+\tstruct bpf_ksock *ks;\n+\tint err = 0;\n+\n+\tcreate_opts.family = AF_INET;\n+\tcreate_opts.type = SOCK_DGRAM;\n+\tcreate_opts.protocol = IPPROTO_UDP;\n+\n+\tks = bpf_ksock_create(\u0026create_opts, sizeof(create_opts), \u0026err);\n+\tif (!ks)\n+\t\treturn err;\n+\n+\taddr.sin.sin_family = AF_INET;\n+\taddr.sin.sin_port = bpf_htons(remote_port);\n+\taddr.sin.sin_addr.s_addr = ipv4_remote;\n+\n+\terr = bpf_ksock_connect(ks, \u0026addr, sizeof(addr));\n+\tif (err) {\n+\t\tbpf_ksock_release(ks);\n+\t\treturn err;\n+\t}\n+\n+\terr = ksock_ctx_insert(ks);\n+\tif (err \u0026\u0026 err != -EEXIST)\n+\t\treturn err;\n+\treturn 0;\n+}\n+\n+SEC(\"lsm.s/socket_bind\")\n+int BPF_PROG(ksock_socket_bind, struct socket *sock, struct sockaddr *address,\n+\t     int addrlen, int ret)\n+{\n+\tstruct bpf_ksock *ks;\n+\tu32 pid = bpf_get_current_pid_tgid() \u003e\u003e 32;\n+\n+\tif (ret || pid != target_pid)\n+\t\treturn ret;\n+\n+\tks = ksock_ctx_get();\n+\tif (!ks) {\n+\t\tsend_ret = -ENOENT;\n+\t\treturn ret;\n+\t}\n+\n+\tsend_ret = bpf_ksock_send(ks, send_data, sizeof(send_data));\n+\tbpf_ksock_release(ks);\n+\n+\treturn ret;\n+}\n+\n+char __license[] SEC(\"license\") = \"GPL\";\ndiff --git a/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c b/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c\nnew file mode 100644\nindex 0000000000000..5b969e03b6d69\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c\n@@ -0,0 +1,36 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 Isovalent */\n+\n+#include \"vmlinux.h\"\n+#include \u003cbpf/bpf_helpers.h\u003e\n+#include \u003cbpf/bpf_tracing.h\u003e\n+#include \"bpf_misc.h\"\n+#include \"bpf_tracing_net.h\"\n+#include \"ksock_common.h\"\n+\n+char send_data[11] = \"dummy data\";\n+\n+SEC(\"lsm.s/socket_sendmsg\")\n+__description(\"bpf_ksock_send is rejected from socket_sendmsg LSM hook\")\n+__failure __msg(\"calling kernel function bpf_ksock_send is not allowed\")\n+int BPF_PROG(ksock_socket_sendmsg, struct socket *sock, struct msghdr *msg,\n+\t     int size, int ret)\n+{\n+\tstruct __ksock_ctx_value *v;\n+\tstruct bpf_ksock *ks;\n+\n+\tv = ksock_ctx_value_lookup();\n+\tif (!v)\n+\t\treturn ret;\n+\n+\tks = bpf_kptr_xchg(\u0026v-\u003ectx, NULL);\n+\tif (!ks)\n+\t\treturn ret;\n+\n+\tbpf_ksock_send(ks, send_data, sizeof(send_data));\n+\tbpf_ksock_release(ks);\n+\n+\treturn ret;\n+}\n+\n+char __license[] SEC(\"license\") = \"GPL\";\ndiff --git a/tools/testing/selftests/bpf/progs/ksock_wq.c b/tools/testing/selftests/bpf/progs/ksock_wq.c\nnew file mode 100644\nindex 0000000000000..16a1873d132ed\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/ksock_wq.c\n@@ -0,0 +1,62 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 Isovalent */\n+\n+#include \"vmlinux.h\"\n+#include \u003cbpf/bpf_helpers.h\u003e\n+#include \"bpf_experimental.h\"\n+#include \"bpf_tracing_net.h\"\n+#include \"errno.h\"\n+#include \"ksock_common.h\"\n+\n+struct ksock_wq_value {\n+\tstruct bpf_wq work;\n+};\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_ARRAY);\n+\t__uint(max_entries, 1);\n+\t__type(key, u32);\n+\t__type(value, struct ksock_wq_value);\n+} work_map SEC(\".maps\");\n+\n+int create_err;\n+u32 callback_done;\n+\n+static int ksock_wq_callback(void *map, int *key, void *value)\n+{\n+\tstruct bpf_ksock_create_opts opts = {\n+\t\t.family = AF_INET,\n+\t\t.type = SOCK_DGRAM,\n+\t\t.protocol = IPPROTO_UDP,\n+\t};\n+\tstruct bpf_ksock *ks;\n+\tint err = 0;\n+\n+\tks = bpf_ksock_create(\u0026opts, sizeof(opts), \u0026err);\n+\tif (ks)\n+\t\tbpf_ksock_release(ks);\n+\tcreate_err = err;\n+\t__sync_fetch_and_add(\u0026callback_done, 1);\n+\treturn 0;\n+}\n+\n+SEC(\"syscall\")\n+int ksock_wq_start(void *ctx)\n+{\n+\tstruct ksock_wq_value *value;\n+\tu32 key = 0;\n+\tint err;\n+\n+\tvalue = bpf_map_lookup_elem(\u0026work_map, \u0026key);\n+\tif (!value)\n+\t\treturn -ENOENT;\n+\terr = bpf_wq_init(\u0026value-\u003ework, \u0026work_map, 0);\n+\tif (err)\n+\t\treturn err;\n+\terr = bpf_wq_set_callback(\u0026value-\u003ework, ksock_wq_callback, 0);\n+\tif (err)\n+\t\treturn err;\n+\treturn bpf_wq_start(\u0026value-\u003ework, 0);\n+}\n+\n+char __license[] SEC(\"license\") = \"GPL\";\n"
}

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

Args:
null

Results:
{
  "EnableConfigs": [
    "INET",
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_ksock_create",
    "bpf_ksock_connect",
    "bpf_ksock_acquire",
    "bpf_ksock_release",
    "bpf_ksock_send",
    "connect_socket"
  ],
  "Reasoning": "The patch introduces a new BPF kfunc feature that allows BPF programs to create, connect, and send data through kernel sockets. This adds new reachable code paths in the BPF subsystem and networking core, which should be fuzzed.",
  "WorthFuzzing": true
}

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

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

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

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

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


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

Prompt:
For your convenience, here is the diff of the changes:
commit 2271d840dc63021eef44ce76b20d82c0bea281f6
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 7 20:19:11 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/bpf_ksock.h b/include/linux/bpf_ksock.h
new file mode 100644
index 0000000000000..cb387fb75e43b
--- /dev/null
+++ b/include/linux/bpf_ksock.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2026 Isovalent */
+
+#ifndef _BPF_KSOCK_H
+#define _BPF_KSOCK_H
+
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+
+/**
+ * struct bpf_ksock_create_opts - BPF kernel socket creation parameters
+ * @family:	Address family: AF_INET or AF_INET6.
+ * @type:	Socket type: only SOCK_DGRAM supported for now.
+ * @protocol:	Protocol number (e.g. IPPROTO_UDP), or 0 for the default protocol
+ *		of the given type.
+ * @reserved:	Must be zero. Reserved for future use.
+ */
+struct bpf_ksock_create_opts {
+	__u8 family;
+	__u8 type;
+	__u8 protocol;
+	__u8 reserved;
+};
+
+/**
+ * union bpf_ksock_addr - IPv4 or IPv6 socket address
+ * @sin: IPv4 socket address.
+ * @sin6: IPv6 socket address.
+ */
+union bpf_ksock_addr {
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+};
+
+#endif /* _BPF_KSOCK_H */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 2a8d7b14f1d11..5a5eb12501032 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -461,6 +461,8 @@ extern struct file *__sys_socket_file(int family, int type, int protocol);
 extern int __sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen);
 extern int __sys_bind_socket(struct socket *sock, struct sockaddr_storage *address,
 			     int addrlen);
+int connect_socket(struct socket *sock, struct sockaddr_storage *addr,
+		   int addrlen, int flags);
 extern int __sys_connect_file(struct file *file, struct sockaddr_storage *addr,
 			      int addrlen, int file_flags);
 extern int __sys_connect(int fd, struct sockaddr __user *uservaddr,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e6233c0081d10..037df7232f426 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4473,6 +4473,9 @@ BTF_ID(struct, task_struct)
 #ifdef CONFIG_CRYPTO
 BTF_ID(struct, bpf_crypto_ctx)
 #endif
+#ifdef CONFIG_INET
+BTF_ID(struct, bpf_ksock)
+#endif
 BTF_SET_END(rcu_protected_types)
 
 static bool rcu_protected_object(const struct btf *btf, u32 btf_id)
diff --git a/net/core/Makefile b/net/core/Makefile
index b3fdcb4e355fa..a9295b7859010 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -44,6 +44,9 @@ obj-$(CONFIG_FAILOVER) += failover.o
 obj-$(CONFIG_NET_SOCK_MSG) += skmsg.o
 obj-$(CONFIG_BPF_SYSCALL) += sock_map.o
 obj-$(CONFIG_BPF_SYSCALL) += bpf_sk_storage.o
+ifneq ($(CONFIG_INET),)
+obj-$(CONFIG_BPF_SYSCALL) += bpf_ksock.o
+endif
 obj-$(CONFIG_OF)	+= of_net.o
 obj-$(CONFIG_NET_TEST) += net_test.o
 obj-$(CONFIG_NET_DEVMEM) += devmem.o
diff --git a/net/core/bpf_ksock.c b/net/core/bpf_ksock.c
new file mode 100644
index 0000000000000..8a4f0150a0a41
--- /dev/null
+++ b/net/core/bpf_ksock.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2026 Isovalent */
+
+#include <linux/bpf.h>
+#include <linux/bpf_ksock.h>
+#include <linux/btf.h>
+#include <linux/btf_ids.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/net.h>
+#include <linux/refcount.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/unaligned.h>
+#include <linux/workqueue.h>
+#include <linux/ip.h>
+#include <net/sock.h>
+
+/**
+ * struct bpf_ksock - refcounted BPF kernel socket context
+ * @sock:	The underlying kernel socket.
+ * @usage:	Reference counter.
+ * @rwork:	RCU work for deferred cleanup (sock_release may sleep).
+ */
+struct bpf_ksock {
+	struct socket *sock;
+	refcount_t usage;
+	struct rcu_work rwork;
+};
+
+static void ksock_release_work_fn(struct work_struct *work)
+{
+	struct bpf_ksock *ks =
+		container_of(to_rcu_work(work), struct bpf_ksock, rwork);
+
+	sock_release(ks->sock);
+	kfree(ks);
+}
+
+static bool bpf_ksock_has_user_task_context(void)
+{
+	/*
+	 * Task work can run from do_exit() after exit_nsproxy_namespaces()
+	 * cleared current->nsproxy, while current is still not a kthread.
+	 */
+	return !(current->flags & PF_KTHREAD) && current->nsproxy;
+}
+
+__bpf_kfunc_start_defs();
+
+/**
+ * bpf_ksock_create() - Create a BPF kernel socket.
+ *
+ * Allocates and creates a kernel socket.
+ *
+ * The returned context must either be stored in a map as a kptr, or
+ * freed with bpf_ksock_release().
+ *
+ * This function may sleep (sock_create), so it can only be used
+ * in sleepable BPF programs (SYSCALL).
+ * It cannot be called from a BPF workqueue callback because that callback
+ * does not retain the invoking task's namespace or security context.
+ *
+ * @opts:	Pointer to struct bpf_ksock_create_opts with socket parameters.
+ * @opts__sz:	Size of the opts struct.
+ * @err__uninit:	Integer to store error code when NULL is returned.
+ */
+__bpf_kfunc struct bpf_ksock *
+bpf_ksock_create(const struct bpf_ksock_create_opts *opts, u32 opts__sz,
+		 int *err__uninit)
+{
+	struct bpf_ksock_create_opts opts_copy;
+	struct bpf_ksock *ks;
+	int err;
+
+	/*
+	 * sock_create() derives the network namespace, credentials, and cgroup
+	 * from current. Kernel threads, including BPF workqueue callbacks, do
+	 * not carry the context of the task that invoked the BPF program.
+	 */
+	if (!bpf_ksock_has_user_task_context()) {
+		err = -EOPNOTSUPP;
+		goto err_out;
+	}
+
+	if (!opts || opts__sz != sizeof(struct bpf_ksock_create_opts)) {
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	opts_copy = (struct bpf_ksock_create_opts){
+		.family = READ_ONCE(opts->family),
+		.type = READ_ONCE(opts->type),
+		.protocol = READ_ONCE(opts->protocol),
+		.reserved = READ_ONCE(opts->reserved),
+	};
+
+	if (opts_copy.reserved) {
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	if (opts_copy.family != AF_INET && opts_copy.family != AF_INET6) {
+		err = -EAFNOSUPPORT;
+		goto err_out;
+	}
+
+	if (opts_copy.type != SOCK_DGRAM) {
+		err = -EPROTONOSUPPORT;
+		goto err_out;
+	}
+
+	if (opts_copy.protocol != IPPROTO_UDP && opts_copy.protocol != 0) {
+		err = -EPROTONOSUPPORT;
+		goto err_out;
+	}
+
+	ks = kzalloc_obj(*ks);
+	if (!ks) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	/*
+	 * Use the normal current-task socket path so LSM/cgroup policy,
+	 * socket labels, and the active netns reference match a socket(2)
+	 * created by the BPF program's caller.
+	 */
+	err = sock_create(opts_copy.family, opts_copy.type, opts_copy.protocol,
+			  &ks->sock);
+	if (err)
+		goto err_free;
+
+	ks->sock->sk->sk_rcvbuf = SOCK_MIN_RCVBUF;
+	ks->sock->sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
+
+	refcount_set(&ks->usage, 1);
+	put_unaligned(0, err__uninit);
+	return ks;
+
+err_free:
+	kfree(ks);
+err_out:
+	put_unaligned(err, err__uninit);
+	return NULL;
+}
+
+/**
+ * bpf_ksock_connect() - Connect a BPF kernel socket to a remote address.
+ * @ks:		The BPF kernel socket context.
+ * @addr:	Pointer to an IPv4 or IPv6 socket address.
+ * @addr__sz:	Size of the address union.
+ *
+ * Connects the socket to the specified remote address and port.
+ *
+ * This function may sleep while connecting the socket, so it can only be used
+ * in sleepable BPF programs (SYSCALL).
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+__bpf_kfunc int bpf_ksock_connect(struct bpf_ksock *ks,
+				  const union bpf_ksock_addr *addr,
+				  u32 addr__sz)
+{
+	struct sockaddr_storage sa;
+	int addrlen;
+
+	if (!bpf_ksock_has_user_task_context())
+		return -EOPNOTSUPP;
+
+	if (!addr || addr__sz != sizeof(*addr))
+		return -EINVAL;
+
+	/* Kfunc memory arguments may be unaligned. */
+	memcpy(&sa, addr, sizeof(*addr));
+
+	switch (sa.ss_family) {
+	case AF_INET:
+		addrlen = sizeof(struct sockaddr_in);
+		break;
+#if IS_ENABLED(CONFIG_IPV6)
+	case AF_INET6:
+		addrlen = sizeof(struct sockaddr_in6);
+		break;
+#endif
+	default:
+		return -EAFNOSUPPORT;
+	}
+
+	return connect_socket(ks->sock, &sa, addrlen, 0);
+}
+
+/**
+ * bpf_ksock_acquire() - Acquire a reference to a BPF kernel socket.
+ * @ks:	The BPF kernel socket context to acquire. Must be a
+ *	trusted pointer (e.g. RCU-protected kptr from a map).
+ *
+ * The acquired context must either be stored in a map as a kptr, or
+ * freed with bpf_ksock_release().
+ */
+__bpf_kfunc struct bpf_ksock *bpf_ksock_acquire(struct bpf_ksock *ks)
+{
+	if (!refcount_inc_not_zero(&ks->usage))
+		return NULL;
+	return ks;
+}
+
+/**
+ * bpf_ksock_release() - Release a BPF kernel socket.
+ * @ks:	The BPF kernel socket context to release.
+ *
+ * When the final reference is released, the socket is cleaned up via
+ * queue_rcu_work() (since sock_release may sleep).
+ */
+__bpf_kfunc void bpf_ksock_release(struct bpf_ksock *ks)
+{
+	if (refcount_dec_and_test(&ks->usage)) {
+		INIT_RCU_WORK(&ks->rwork, ksock_release_work_fn);
+		queue_rcu_work(system_dfl_wq, &ks->rwork);
+	}
+}
+
+__bpf_kfunc void bpf_ksock_release_dtor(void *ks)
+{
+	bpf_ksock_release(ks);
+}
+CFI_NOSEAL(bpf_ksock_release_dtor);
+
+/**
+ * bpf_ksock_send() - Send data through a BPF kernel socket.
+ * @ks:		The BPF kernel socket context. Must be an acquired reference.
+ * @data:	Pointer to the data to send.
+ * @data__sz:	Size of the data to send (max 65535 bytes).
+ *
+ * Sends data on a connected socket, best-effort and nonblocking. This may sleep
+ * (kernel_sendmsg), so it can only be called from sleepable BPF programs.
+ *
+ * Return: Number of bytes sent on success, negative errno on error.
+ */
+__bpf_kfunc int bpf_ksock_send(struct bpf_ksock *ks, const void *data,
+			       u32 data__sz)
+{
+	struct msghdr msg = {
+		.msg_flags = MSG_DONTWAIT,
+	};
+	struct kvec iov = {
+		.iov_base = (void *)data,
+		.iov_len = data__sz,
+	};
+	int ret;
+
+	if (!bpf_ksock_has_user_task_context())
+		return -EOPNOTSUPP;
+
+	/* Early check for UDP. Exact limits enforced by kernel_sendmsg(). */
+	if (data__sz > IP_MAX_MTU)
+		return -EMSGSIZE;
+
+	ret = kernel_sendmsg(ks->sock, &msg, &iov, 1, data__sz);
+
+	return ret;
+}
+
+__bpf_kfunc_end_defs();
+
+BTF_KFUNCS_START(ksock_init_kfunc_btf_ids)
+BTF_ID_FLAGS(func, bpf_ksock_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_ksock_connect, KF_SLEEPABLE)
+BTF_KFUNCS_END(ksock_init_kfunc_btf_ids)
+
+static const struct btf_kfunc_id_set ksock_init_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set = &ksock_init_kfunc_btf_ids,
+};
+
+BTF_KFUNCS_START(ksock_kfunc_btf_ids)
+BTF_ID_FLAGS(func, bpf_ksock_release, KF_RELEASE)
+BTF_ID_FLAGS(func, bpf_ksock_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
+BTF_ID_FLAGS(func, bpf_ksock_send, KF_SLEEPABLE)
+BTF_KFUNCS_END(ksock_kfunc_btf_ids)
+
+#ifdef CONFIG_BPF_LSM
+BTF_ID_LIST_SINGLE(bpf_lsm_socket_sendmsg_id, func, bpf_lsm_socket_sendmsg)
+#endif
+
+static int bpf_ksock_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id)
+{
+	if (!btf_id_set8_contains(&ksock_kfunc_btf_ids, kfunc_id))
+		return 0;
+
+	if (prog->type == BPF_PROG_TYPE_SYSCALL)
+		return 0;
+
+#ifdef CONFIG_BPF_LSM
+	if (prog->type == BPF_PROG_TYPE_LSM &&
+	    prog->aux->attach_btf_id != bpf_lsm_socket_sendmsg_id[0])
+		return 0;
+#endif
+
+	return -EACCES;
+}
+
+static const struct btf_kfunc_id_set ksock_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set = &ksock_kfunc_btf_ids,
+	.filter = bpf_ksock_kfunc_filter,
+};
+
+BTF_ID_LIST(bpf_ksock_dtor_ids)
+BTF_ID(struct, bpf_ksock)
+BTF_ID(func, bpf_ksock_release_dtor)
+
+static int __init bpf_ksock_kfunc_init(void)
+{
+	int ret;
+	const struct btf_id_dtor_kfunc bpf_ksock_dtors[] = {
+		{
+			.btf_id = bpf_ksock_dtor_ids[0],
+			.kfunc_btf_id = bpf_ksock_dtor_ids[1],
+		},
+	};
+
+	ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL,
+					&ksock_init_kfunc_set);
+	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL,
+					       &ksock_kfunc_set);
+	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM,
+					       &ksock_kfunc_set);
+	return ret ?: register_btf_id_dtor_kfuncs(bpf_ksock_dtors,
+						  ARRAY_SIZE(bpf_ksock_dtors),
+						  THIS_MODULE);
+}
+
+late_initcall(bpf_ksock_kfunc_init);
diff --git a/net/socket.c b/net/socket.c
index 63c69a0fa74e1..8f18f124d92a7 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2103,6 +2103,20 @@ SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,
 	return __sys_accept4(fd, upeer_sockaddr, upeer_addrlen, 0);
 }
 
+int connect_socket(struct socket *sock, struct sockaddr_storage *address,
+		   int addrlen, int flags)
+{
+	int err;
+
+	err = security_socket_connect(sock, (struct sockaddr *)address, addrlen);
+	if (err)
+		return err;
+
+	return READ_ONCE(sock->ops)->connect(sock,
+				    (struct sockaddr_unsized *)address,
+				    addrlen, flags);
+}
+
 /*
  *	Attempt to connect to a socket with the server address.  The address
  *	is in user space so we verify it is OK and move it to kernel space.
@@ -2119,23 +2133,13 @@ int __sys_connect_file(struct file *file, struct sockaddr_storage *address,
 		       int addrlen, int file_flags)
 {
 	struct socket *sock;
-	int err;
 
 	sock = sock_from_file(file);
-	if (!sock) {
-		err = -ENOTSOCK;
-		goto out;
-	}
-
-	err =
-	    security_socket_connect(sock, (struct sockaddr *)address, addrlen);
-	if (err)
-		goto out;
+	if (!sock)
+		return -ENOTSOCK;
 
-	err = READ_ONCE(sock->ops)->connect(sock, (struct sockaddr_unsized *)address,
-					    addrlen, sock->file->f_flags | file_flags);
-out:
-	return err;
+	return connect_socket(sock, address, addrlen,
+				    sock->file->f_flags | file_flags);
 }
 
 int __sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen)
diff --git a/tools/testing/selftests/bpf/prog_tests/ksock.c b/tools/testing/selftests/bpf/prog_tests/ksock.c
new file mode 100644
index 0000000000000..fb1a332eb3da1
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/ksock.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Isovalent */
+
+#include <arpa/inet.h>
+
+#include "test_progs.h"
+#include "network_helpers.h"
+#include "ksock_lsm.skel.h"
+#include "ksock_lsm_verifier.skel.h"
+
+#define NS_TEST "ksock_lsm_ns"
+#define RECV_PORT 7777
+#define RECV_TIMEOUT_SEC 5
+
+struct ksock_test_env {
+	bool netns_created;
+	struct nstoken *nstoken;
+	int rfd;
+};
+
+static bool ksock_test_env_setup(struct ksock_test_env *env)
+{
+	struct sockaddr_in addr = {
+		.sin_family = AF_INET,
+		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+		.sin_port = htons(RECV_PORT),
+	};
+	struct timeval tv = { .tv_sec = RECV_TIMEOUT_SEC };
+	int err;
+
+	memset(env, 0, sizeof(*env));
+	env->rfd = -1;
+
+	SYS(fail, "ip netns add %s", NS_TEST);
+	env->netns_created = true;
+	SYS(fail, "ip -net %s link set lo up", NS_TEST);
+
+	env->nstoken = open_netns(NS_TEST);
+	if (!ASSERT_OK_PTR(env->nstoken, "open_netns"))
+		goto fail;
+
+	env->rfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (!ASSERT_OK_FD(env->rfd, "receiver socket"))
+		goto fail;
+
+	err = bind(env->rfd, (struct sockaddr *)&addr, sizeof(addr));
+	if (!ASSERT_OK(err, "bind receiver"))
+		goto fail;
+
+	err = setsockopt(env->rfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+	if (!ASSERT_OK(err, "set rcvtimeo"))
+		goto fail;
+
+	return true;
+
+fail:
+	return false;
+}
+
+void test_ksock_lsm(void)
+{
+	LIBBPF_OPTS(bpf_test_run_opts, opts);
+	struct ksock_test_env env;
+	struct sockaddr_in trigger_addr = {
+		.sin_family = AF_INET,
+		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+	};
+	struct ksock_lsm *skel;
+	char recv_data[sizeof(skel->data->send_data)] = {};
+	ssize_t n;
+	int tfd = -1;
+	int err;
+
+	skel = ksock_lsm__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "skel open_and_load"))
+		return;
+
+	if (!ksock_test_env_setup(&env))
+		goto fail;
+
+	/* Step 1: Run the setup SYSCALL prog to create the ksock */
+	skel->bss->ipv4_remote = htonl(INADDR_LOOPBACK);
+	skel->bss->remote_port = RECV_PORT;
+	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.ksock_setup),
+				     &opts);
+	if (!ASSERT_OK(err, "ksock_setup run"))
+		goto fail;
+	if (!ASSERT_OK(opts.retval, "ksock_setup retval"))
+		goto fail;
+
+	/* Step 2: Attach LSM prog and trigger socket_bind from userspace */
+	skel->links.ksock_socket_bind =
+		bpf_program__attach_lsm(skel->progs.ksock_socket_bind);
+	if (!ASSERT_OK_PTR(skel->links.ksock_socket_bind,
+			   "attach socket_bind lsm"))
+		goto fail;
+
+	tfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (!ASSERT_OK_FD(tfd, "trigger socket"))
+		goto fail;
+
+	skel->bss->target_pid = getpid();
+	err = bind(tfd, (struct sockaddr *)&trigger_addr, sizeof(trigger_addr));
+	skel->bss->target_pid = 0;
+	if (!ASSERT_OK(err, "trigger bind"))
+		goto fail;
+
+	/* Step 3: Verify the LSM hook sent the notification */
+	if (!ASSERT_EQ(skel->data->send_ret, sizeof(skel->data->send_data),
+		       "LSM send bytes"))
+		goto fail;
+
+	n = recvfrom(env.rfd, recv_data, sizeof(recv_data), 0, NULL, NULL);
+	if (ASSERT_EQ(n, sizeof(recv_data), "recvfrom len"))
+		ASSERT_MEMEQ(recv_data, skel->data->send_data, sizeof(recv_data),
+			     "payload match");
+
+fail:
+	if (tfd >= 0)
+		close(tfd);
+	if (env.rfd >= 0)
+		close(env.rfd);
+	if (env.nstoken)
+		close_netns(env.nstoken);
+	if (env.netns_created)
+		SYS_NOFAIL("ip netns del %s >/dev/null 2>&1", NS_TEST);
+	ksock_lsm__destroy(skel);
+}
+
+void test_ksock_lsm_verifier(void)
+{
+	RUN_TESTS(ksock_lsm_verifier);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/ksock_wq.c b/tools/testing/selftests/bpf/prog_tests/ksock_wq.c
new file mode 100644
index 0000000000000..5b9c6cc303922
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/ksock_wq.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Isovalent */
+
+#include <unistd.h>
+
+#include "test_progs.h"
+#include "ksock_wq.skel.h"
+
+void test_ksock_wq(void)
+{
+	LIBBPF_OPTS(bpf_test_run_opts, opts);
+	struct ksock_wq *skel;
+	int err;
+
+	skel = ksock_wq__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "ksock_wq open and load"))
+		return;
+
+	err = bpf_prog_test_run_opts(
+		bpf_program__fd(skel->progs.ksock_wq_start), &opts);
+	if (!ASSERT_OK(err, "run ksock_wq_start"))
+		goto out;
+	if (!ASSERT_OK(opts.retval, "ksock_wq_start retval"))
+		goto out;
+
+	while (!__atomic_load_n(&skel->bss->callback_done, __ATOMIC_ACQUIRE))
+		usleep(1000);
+
+	ASSERT_EQ(skel->bss->create_err, -EOPNOTSUPP,
+		  "workqueue create rejected");
+
+out:
+	ksock_wq__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/ksock_common.h b/tools/testing/selftests/bpf/progs/ksock_common.h
new file mode 100644
index 0000000000000..01edaeb9fdd4a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/ksock_common.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2026 Isovalent */
+
+#ifndef _KSOCK_COMMON_H
+#define _KSOCK_COMMON_H
+
+#include "errno.h"
+
+#define SOCK_DGRAM	2
+#define IPPROTO_UDP	17
+
+struct bpf_ksock *bpf_ksock_create(const struct bpf_ksock_create_opts *opts,
+				   u32 opts__sz, int *err__uninit) __ksym;
+int bpf_ksock_connect(struct bpf_ksock *ks, const union bpf_ksock_addr *addr,
+		      u32 addr__sz) __ksym;
+struct bpf_ksock *bpf_ksock_acquire(struct bpf_ksock *ks) __ksym;
+void bpf_ksock_release(struct bpf_ksock *ks) __ksym;
+int bpf_ksock_send(struct bpf_ksock *ks, const void *data, u32 data__sz) __ksym;
+void bpf_rcu_read_lock(void) __ksym;
+void bpf_rcu_read_unlock(void) __ksym;
+
+struct __ksock_ctx_value {
+	struct bpf_ksock __kptr * ctx;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__type(key, int);
+	__type(value, struct __ksock_ctx_value);
+	__uint(max_entries, 1);
+} __ksock_ctx_map SEC(".maps");
+
+static inline struct __ksock_ctx_value *ksock_ctx_value_lookup(void)
+{
+	u32 key = 0;
+
+	return bpf_map_lookup_elem(&__ksock_ctx_map, &key);
+}
+
+static inline struct bpf_ksock *ksock_ctx_get(void)
+{
+	struct __ksock_ctx_value *v;
+	struct bpf_ksock *ks = NULL, *tmp;
+
+	v = ksock_ctx_value_lookup();
+	if (!v)
+		return NULL;
+
+	bpf_rcu_read_lock();
+	tmp = v->ctx;
+	if (tmp)
+		ks = bpf_ksock_acquire(tmp);
+	bpf_rcu_read_unlock();
+
+	return ks;
+}
+
+static inline int ksock_ctx_insert(struct bpf_ksock *ctx)
+{
+	struct __ksock_ctx_value *v;
+	struct bpf_ksock *old;
+
+	v = ksock_ctx_value_lookup();
+	if (!v) {
+		bpf_ksock_release(ctx);
+		return -ENOENT;
+	}
+
+	old = bpf_kptr_xchg(&v->ctx, ctx);
+	if (old) {
+		bpf_ksock_release(old);
+		return -EEXIST;
+	}
+
+	return 0;
+}
+
+#endif /* _KSOCK_COMMON_H */
diff --git a/tools/testing/selftests/bpf/progs/ksock_lsm.c b/tools/testing/selftests/bpf/progs/ksock_lsm.c
new file mode 100644
index 0000000000000..9808451098efb
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/ksock_lsm.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Isovalent */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_endian.h>
+#include "bpf_tracing_net.h"
+#include "ksock_common.h"
+
+char send_data[32] = "hello from bpf ksock";
+
+__be32 ipv4_remote;
+__u16 remote_port;
+int target_pid;
+int send_ret = -1;
+
+SEC("syscall")
+int ksock_setup(void *ctx)
+{
+	struct bpf_ksock_create_opts create_opts = {};
+	union bpf_ksock_addr addr = {};
+	struct bpf_ksock *ks;
+	int err = 0;
+
+	create_opts.family = AF_INET;
+	create_opts.type = SOCK_DGRAM;
+	create_opts.protocol = IPPROTO_UDP;
+
+	ks = bpf_ksock_create(&create_opts, sizeof(create_opts), &err);
+	if (!ks)
+		return err;
+
+	addr.sin.sin_family = AF_INET;
+	addr.sin.sin_port = bpf_htons(remote_port);
+	addr.sin.sin_addr.s_addr = ipv4_remote;
+
+	err = bpf_ksock_connect(ks, &addr, sizeof(addr));
+	if (err) {
+		bpf_ksock_release(ks);
+		return err;
+	}
+
+	err = ksock_ctx_insert(ks);
+	if (err && err != -EEXIST)
+		return err;
+	return 0;
+}
+
+SEC("lsm.s/socket_bind")
+int BPF_PROG(ksock_socket_bind, struct socket *sock, struct sockaddr *address,
+	     int addrlen, int ret)
+{
+	struct bpf_ksock *ks;
+	u32 pid = bpf_get_current_pid_tgid() >> 32;
+
+	if (ret || pid != target_pid)
+		return ret;
+
+	ks = ksock_ctx_get();
+	if (!ks) {
+		send_ret = -ENOENT;
+		return ret;
+	}
+
+	send_ret = bpf_ksock_send(ks, send_data, sizeof(send_data));
+	bpf_ksock_release(ks);
+
+	return ret;
+}
+
+char __license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c b/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c
new file mode 100644
index 0000000000000..5b969e03b6d69
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Isovalent */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+#include "bpf_tracing_net.h"
+#include "ksock_common.h"
+
+char send_data[11] = "dummy data";
+
+SEC("lsm.s/socket_sendmsg")
+__description("bpf_ksock_send is rejected from socket_sendmsg LSM hook")
+__failure __msg("calling kernel function bpf_ksock_send is not allowed")
+int BPF_PROG(ksock_socket_sendmsg, struct socket *sock, struct msghdr *msg,
+	     int size, int ret)
+{
+	struct __ksock_ctx_value *v;
+	struct bpf_ksock *ks;
+
+	v = ksock_ctx_value_lookup();
+	if (!v)
+		return ret;
+
+	ks = bpf_kptr_xchg(&v->ctx, NULL);
+	if (!ks)
+		return ret;
+
+	bpf_ksock_send(ks, send_data, sizeof(send_data));
+	bpf_ksock_release(ks);
+
+	return ret;
+}
+
+char __license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/ksock_wq.c b/tools/testing/selftests/bpf/progs/ksock_wq.c
new file mode 100644
index 0000000000000..16a1873d132ed
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/ksock_wq.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Isovalent */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include "bpf_experimental.h"
+#include "bpf_tracing_net.h"
+#include "errno.h"
+#include "ksock_common.h"
+
+struct ksock_wq_value {
+	struct bpf_wq work;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 1);
+	__type(key, u32);
+	__type(value, struct ksock_wq_value);
+} work_map SEC(".maps");
+
+int create_err;
+u32 callback_done;
+
+static int ksock_wq_callback(void *map, int *key, void *value)
+{
+	struct bpf_ksock_create_opts opts = {
+		.family = AF_INET,
+		.type = SOCK_DGRAM,
+		.protocol = IPPROTO_UDP,
+	};
+	struct bpf_ksock *ks;
+	int err = 0;
+
+	ks = bpf_ksock_create(&opts, sizeof(opts), &err);
+	if (ks)
+		bpf_ksock_release(ks);
+	create_err = err;
+	__sync_fetch_and_add(&callback_done, 1);
+	return 0;
+}
+
+SEC("syscall")
+int ksock_wq_start(void *ctx)
+{
+	struct ksock_wq_value *value;
+	u32 key = 0;
+	int err;
+
+	value = bpf_map_lookup_elem(&work_map, &key);
+	if (!value)
+		return -ENOENT;
+	err = bpf_wq_init(&value->work, &work_map, 0);
+	if (err)
+		return err;
+	err = bpf_wq_set_callback(&value->work, ksock_wq_callback, 0);
+	if (err)
+		return err;
+	return bpf_wq_start(&value->work, 0);
+}
+
+char __license[] SEC("license") = "GPL";

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

Args:
null

Results:
null

Tokens:
							input: 12554
							output: 19
							thoughts: 1326
						

Thoughts:
**Analyzing the New BPF Kernel Socket Feature**

Okay, so we have a new BPF kfunc feature, `bpf_ksock`, which is interesting. It allows BPF programs, especially sleepable ones like SYSCALL and LSM, to create kernel sockets, connect them, and send data. My initial thought is, this warrants a closer look, especially for fuzzing potential. It’s a new feature, deeply integrated into the kernel's BPF subsystem, opening up new attack surfaces if not properly handled.

I see the main changes are spread across a few files: `include/linux/bpf_ksock.h`, `net/core/bpf_ksock.c`, `net/socket.c` (where `connect_socket` is being exposed), `kernel/bpf/verifier.c`, and `net/core/Makefile`. The new kfuncs are `bpf_ksock_create`, `bpf_ksock_connect`, `bpf_ksock_acquire`, `bpf_ksock_release`, `bpf_ksock_release_dtor`, and `bpf_ksock_send`. These are clearly reachable from BPF programs.

The question of whether to fuzz is immediately answered with a resounding "yes". This is ripe for exploitation if there are any issues. Now, to prepare for fuzzing, I need to know the required configurations. I can see that `CONFIG_BPF_SYSCALL` is essential, naturally, for BPF support, and `CONFIG_INET` is needed for `bpf_ksock.o` to be compiled. The `net/core/Makefile` makes this requirement explicit: `ifneq ($(CONFIG_INET),) obj-$(CONFIG_BPF_SYSCALL) += bpf_ksock.o endif`.

I'm noting that `CONFIG_BPF_LSM` is mentioned in `net/core/bpf_ksock.c`, but the core functionality should be available even for `BPF_PROG_TYPE_SYSCALL`, which is important for wider applicability.

Let me extract the key symbols, as I'll need these for the fuzzer: `bpf_ksock_create`, `bpf_ksock_connect`, `bpf_ksock_acquire`, `bpf_ksock_release`, and `bpf_ksock_send`. They are indeed kfuncs, so they are not `EXPORT_SYMBOL`ed, but registered via `register_btf_kfunc_id_set`. Syzkaller can fuzz these directly, or rather, the BPF programs that call them. I need to ensure these are included in the `FocusSymbols` list for the fuzzer.

I'm double-checking the kfunc definitions in `bpf_ksock.c` to be sure: `bpf_ksock_create`, `bpf_ksock_connect`, `bpf_ksock_acquire`, `bpf_ksock_release`, `bpf_ksock_release_dtor`, `bpf_ksock_send`, and noting the changes to `connect_socket` in `net/socket.c` as a side effect. This looks like a refactoring.

After reviewing these files, I now believe the crucial symbols for fuzzing are: `bpf_ksock_create`, `bpf_ksock_connect`, `bpf_ksock_acquire`, `bpf_ksock_release`, `bpf_ksock_send`, and `connect_socket`.

I'll quickly do a final search to ensure no other configurations are required.



4/2 2026/08/07 20:19 tool grepper
0m Args:
{
  "Expression": "bpf_ksock"
}

Results:
{
  "Output": "include/linux/bpf_ksock.h-11-/**\ninclude/linux/bpf_ksock.h:12: * struct bpf_ksock_create_opts - BPF kernel socket creation parameters\ninclude/linux/bpf_ksock.h-13- * @family:\tAddress family: AF_INET or AF_INET6.\n--\ninclude/linux/bpf_ksock.h-18- */\ninclude/linux/bpf_ksock.h:19:struct bpf_ksock_create_opts {\ninclude/linux/bpf_ksock.h-20-\t__u8 family;\n--\ninclude/linux/bpf_ksock.h-26-/**\ninclude/linux/bpf_ksock.h:27: * union bpf_ksock_addr - IPv4 or IPv6 socket address\ninclude/linux/bpf_ksock.h-28- * @sin: IPv4 socket address.\n--\ninclude/linux/bpf_ksock.h-30- */\ninclude/linux/bpf_ksock.h:31:union bpf_ksock_addr {\ninclude/linux/bpf_ksock.h-32-\tstruct sockaddr_in sin;\n--\nkernel/bpf/verifier.c=4474=BTF_ID(struct, bpf_crypto_ctx)\n--\nkernel/bpf/verifier.c-4476-#ifdef CONFIG_INET\nkernel/bpf/verifier.c:4477:BTF_ID(struct, bpf_ksock)\nkernel/bpf/verifier.c-4478-#endif\n--\nnet/core/Makefile=47=ifneq ($(CONFIG_INET),)\nnet/core/Makefile:48:obj-$(CONFIG_BPF_SYSCALL) += bpf_ksock.o\nnet/core/Makefile-49-endif\n--\nnet/core/bpf_ksock.c-4-#include \u003clinux/bpf.h\u003e\nnet/core/bpf_ksock.c:5:#include \u003clinux/bpf_ksock.h\u003e\nnet/core/bpf_ksock.c-6-#include \u003clinux/btf.h\u003e\n--\nnet/core/bpf_ksock.c-20-/**\nnet/core/bpf_ksock.c:21: * struct bpf_ksock - refcounted BPF kernel socket context\nnet/core/bpf_ksock.c-22- * @sock:\tThe underlying kernel socket.\n--\nnet/core/bpf_ksock.c-25- */\nnet/core/bpf_ksock.c:26:struct bpf_ksock {\nnet/core/bpf_ksock.c-27-\tstruct socket *sock;\n--\nnet/core/bpf_ksock.c=32=static void ksock_release_work_fn(struct work_struct *work)\nnet/core/bpf_ksock.c-33-{\nnet/core/bpf_ksock.c:34:\tstruct bpf_ksock *ks =\nnet/core/bpf_ksock.c:35:\t\tcontainer_of(to_rcu_work(work), struct bpf_ksock, rwork);\nnet/core/bpf_ksock.c-36-\n--\nnet/core/bpf_ksock.c-40-\nnet/core/bpf_ksock.c:41:static bool bpf_ksock_has_user_task_context(void)\nnet/core/bpf_ksock.c-42-{\n--\nnet/core/bpf_ksock.c=50=__bpf_kfunc_start_defs();\n--\nnet/core/bpf_ksock.c-52-/**\nnet/core/bpf_ksock.c:53: * bpf_ksock_create() - Create a BPF kernel socket.\nnet/core/bpf_ksock.c-54- *\n--\nnet/core/bpf_ksock.c-57- * The returned context must either be stored in a map as a kptr, or\nnet/core/bpf_ksock.c:58: * freed with bpf_ksock_release().\nnet/core/bpf_ksock.c-59- *\n--\nnet/core/bpf_ksock.c-64- *\nnet/core/bpf_ksock.c:65: * @opts:\tPointer to struct bpf_ksock_create_opts with socket parameters.\nnet/core/bpf_ksock.c-66- * @opts__sz:\tSize of the opts struct.\n--\nnet/core/bpf_ksock.c-68- */\nnet/core/bpf_ksock.c:69:__bpf_kfunc struct bpf_ksock *\nnet/core/bpf_ksock.c:70:bpf_ksock_create(const struct bpf_ksock_create_opts *opts, u32 opts__sz,\nnet/core/bpf_ksock.c-71-\t\t int *err__uninit)\nnet/core/bpf_ksock.c-72-{\nnet/core/bpf_ksock.c:73:\tstruct bpf_ksock_create_opts opts_copy;\nnet/core/bpf_ksock.c:74:\tstruct bpf_ksock *ks;\nnet/core/bpf_ksock.c-75-\tint err;\n--\nnet/core/bpf_ksock.c-81-\t */\nnet/core/bpf_ksock.c:82:\tif (!bpf_ksock_has_user_task_context()) {\nnet/core/bpf_ksock.c-83-\t\terr = -EOPNOTSUPP;\n--\nnet/core/bpf_ksock.c-86-\nnet/core/bpf_ksock.c:87:\tif (!opts || opts__sz != sizeof(struct bpf_ksock_create_opts)) {\nnet/core/bpf_ksock.c-88-\t\terr = -EINVAL;\n--\nnet/core/bpf_ksock.c-91-\nnet/core/bpf_ksock.c:92:\topts_copy = (struct bpf_ksock_create_opts){\nnet/core/bpf_ksock.c-93-\t\t.family = READ_ONCE(opts-\u003efamily),\n--\nnet/core/bpf_ksock.c-149-/**\nnet/core/bpf_ksock.c:150: * bpf_ksock_connect() - Connect a BPF kernel socket to a remote address.\nnet/core/bpf_ksock.c-151- * @ks:\t\tThe BPF kernel socket context.\n--\nnet/core/bpf_ksock.c-161- */\nnet/core/bpf_ksock.c:162:__bpf_kfunc int bpf_ksock_connect(struct bpf_ksock *ks,\nnet/core/bpf_ksock.c:163:\t\t\t\t  const union bpf_ksock_addr *addr,\nnet/core/bpf_ksock.c-164-\t\t\t\t  u32 addr__sz)\n--\nnet/core/bpf_ksock.c-168-\nnet/core/bpf_ksock.c:169:\tif (!bpf_ksock_has_user_task_context())\nnet/core/bpf_ksock.c-170-\t\treturn -EOPNOTSUPP;\n--\nnet/core/bpf_ksock.c-194-/**\nnet/core/bpf_ksock.c:195: * bpf_ksock_acquire() - Acquire a reference to a BPF kernel socket.\nnet/core/bpf_ksock.c-196- * @ks:\tThe BPF kernel socket context to acquire. Must be a\n--\nnet/core/bpf_ksock.c-199- * The acquired context must either be stored in a map as a kptr, or\nnet/core/bpf_ksock.c:200: * freed with bpf_ksock_release().\nnet/core/bpf_ksock.c-201- */\nnet/core/bpf_ksock.c:202:__bpf_kfunc struct bpf_ksock *bpf_ksock_acquire(struct bpf_ksock *ks)\nnet/core/bpf_ksock.c-203-{\n--\nnet/core/bpf_ksock.c-209-/**\nnet/core/bpf_ksock.c:210: * bpf_ksock_release() - Release a BPF kernel socket.\nnet/core/bpf_ksock.c-211- * @ks:\tThe BPF kernel socket context to release.\n--\nnet/core/bpf_ksock.c-215- */\nnet/core/bpf_ksock.c:216:__bpf_kfunc void bpf_ksock_release(struct bpf_ksock *ks)\nnet/core/bpf_ksock.c-217-{\n--\nnet/core/bpf_ksock.c-223-\nnet/core/bpf_ksock.c:224:__bpf_kfunc void bpf_ksock_release_dtor(void *ks)\nnet/core/bpf_ksock.c-225-{\nnet/core/bpf_ksock.c:226:\tbpf_ksock_release(ks);\nnet/core/bpf_ksock.c-227-}\nnet/core/bpf_ksock.c:228:CFI_NOSEAL(bpf_ksock_release_dtor);\nnet/core/bpf_ksock.c-229-\nnet/core/bpf_ksock.c-230-/**\nnet/core/bpf_ksock.c:231: * bpf_ksock_send() - Send data through a BPF kernel socket.\nnet/core/bpf_ksock.c-232- * @ks:\t\tThe BPF kernel socket context. Must be an acquired reference.\n--\nnet/core/bpf_ksock.c-240- */\nnet/core/bpf_ksock.c:241:__bpf_kfunc int bpf_ksock_send(struct bpf_ksock *ks, const void *data,\nnet/core/bpf_ksock.c-242-\t\t\t       u32 data__sz)\n--\nnet/core/bpf_ksock.c-252-\nnet/core/bpf_ksock.c:253:\tif (!bpf_ksock_has_user_task_context())\nnet/core/bpf_ksock.c-254-\t\treturn -EOPNOTSUPP;\n--\nnet/core/bpf_ksock.c=267=BTF_KFUNCS_START(ksock_init_kfunc_btf_ids)\nnet/core/bpf_ksock.c:268:BTF_ID_FLAGS(func, bpf_ksock_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)\nnet/core/bpf_ksock.c:269:BTF_ID_FLAGS(func, bpf_ksock_connect, KF_SLEEPABLE)\nnet/core/bpf_ksock.c-270-BTF_KFUNCS_END(ksock_init_kfunc_btf_ids)\n--\nnet/core/bpf_ksock.c=277=BTF_KFUNCS_START(ksock_kfunc_btf_ids)\nnet/core/bpf_ksock.c:278:BTF_ID_FLAGS(func, bpf_ksock_release, KF_RELEASE)\nnet/core/bpf_ksock.c:279:BTF_ID_FLAGS(func, bpf_ksock_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)\nnet/core/bpf_ksock.c:280:BTF_ID_FLAGS(func, bpf_ksock_send, KF_SLEEPABLE)\nnet/core/bpf_ksock.c-281-BTF_KFUNCS_END(ksock_kfunc_btf_ids)\n--\nnet/core/bpf_ksock.c=284=BTF_ID_LIST_SINGLE(bpf_lsm_socket_sendmsg_id, func, bpf_lsm_socket_sendmsg)\n--\nnet/core/bpf_ksock.c-286-\nnet/core/bpf_ksock.c:287:static int bpf_ksock_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id)\nnet/core/bpf_ksock.c-288-{\n--\nnet/core/bpf_ksock.c=304=static const struct btf_kfunc_id_set ksock_kfunc_set = {\n--\nnet/core/bpf_ksock.c-306-\t.set = \u0026ksock_kfunc_btf_ids,\nnet/core/bpf_ksock.c:307:\t.filter = bpf_ksock_kfunc_filter,\nnet/core/bpf_ksock.c-308-};\nnet/core/bpf_ksock.c-309-\nnet/core/bpf_ksock.c:310:BTF_ID_LIST(bpf_ksock_dtor_ids)\nnet/core/bpf_ksock.c:311:BTF_ID(struct, bpf_ksock)\nnet/core/bpf_ksock.c:312:BTF_ID(func, bpf_ksock_release_dtor)\nnet/core/bpf_ksock.c-313-\nnet/core/bpf_ksock.c:314:static int __init bpf_ksock_kfunc_init(void)\nnet/core/bpf_ksock.c-315-{\nnet/core/bpf_ksock.c-316-\tint ret;\nnet/core/bpf_ksock.c:317:\tconst struct btf_id_dtor_kfunc bpf_ksock_dtors[] = {\nnet/core/bpf_ksock.c-318-\t\t{\nnet/core/bpf_ksock.c:319:\t\t\t.btf_id = bpf_ksock_dtor_ids[0],\nnet/core/bpf_ksock.c:320:\t\t\t.kfunc_btf_id = bpf_ksock_dtor_ids[1],\nnet/core/bpf_ksock.c-321-\t\t},\n--\nnet/core/bpf_ksock.c-329-\t\t\t\t\t       \u0026ksock_kfunc_set);\nnet/core/bpf_ksock.c:330:\treturn ret ?: register_btf_id_dtor_kfuncs(bpf_ksock_dtors,\nnet/core/bpf_ksock.c:331:\t\t\t\t\t\t  ARRAY_SIZE(bpf_ksock_dtors),\nnet/core/bpf_ksock.c-332-\t\t\t\t\t\t  THIS_MODULE);\n--\nnet/core/bpf_ksock.c-334-\nnet/core/bpf_ksock.c:335:late_initcall(bpf_ksock_kfunc_init);\n--\ntools/testing/selftests/bpf/progs/ksock_common.h-11-\ntools/testing/selftests/bpf/progs/ksock_common.h:12:struct bpf_ksock *bpf_ksock_create(const struct bpf_ksock_create_opts *opts,\ntools/testing/selftests/bpf/progs/ksock_common.h-13-\t\t\t\t   u32 opts__sz, int *err__uninit) __ksym;\ntools/testing/selftests/bpf/progs/ksock_common.h:14:int bpf_ksock_connect(struct bpf_ksock *ks, const union bpf_ksock_addr *addr,\ntools/testing/selftests/bpf/progs/ksock_common.h-15-\t\t      u32 addr__sz) __ksym;\ntools/testing/selftests/bpf/progs/ksock_common.h:16:struct bpf_ksock *bpf_ksock_acquire(struct bpf_ksock *ks) __ksym;\ntools/testing/selftests/bpf/progs/ksock_common.h:17:void bpf_ksock_release(struct bpf_ksock *ks) __ksym;\ntools/testing/selftests/bpf/progs/ksock_common.h:18:int bpf_ksock_send(struct bpf_ksock *ks, const void *data, u32 data__sz) __ksym;\ntools/testing/selftests/bpf/progs/ksock_common.h-19-void bpf_rcu_read_lock(void) __ksym;\n--\ntools/testing/selftests/bpf/progs/ksock_common.h=22=struct __ksock_ctx_value {\ntools/testing/selftests/bpf/progs/ksock_common.h:23:\tstruct bpf_ksock __kptr * ctx;\ntools/testing/selftests/bpf/progs/ksock_common.h-24-};\n--\ntools/testing/selftests/bpf/progs/ksock_common.h=33=static inline struct __ksock_ctx_value *ksock_ctx_value_lookup(void)\n--\ntools/testing/selftests/bpf/progs/ksock_common.h-39-\ntools/testing/selftests/bpf/progs/ksock_common.h:40:static inline struct bpf_ksock *ksock_ctx_get(void)\ntools/testing/selftests/bpf/progs/ksock_common.h-41-{\ntools/testing/selftests/bpf/progs/ksock_common.h-42-\tstruct __ksock_ctx_value *v;\ntools/testing/selftests/bpf/progs/ksock_common.h:43:\tstruct bpf_ksock *ks = NULL, *tmp;\ntools/testing/selftests/bpf/progs/ksock_common.h-44-\n--\ntools/testing/selftests/bpf/progs/ksock_common.h-51-\tif (tmp)\ntools/testing/selftests/bpf/progs/ksock_common.h:52:\t\tks = bpf_ksock_acquire(tmp);\ntools/testing/selftests/bpf/progs/ksock_common.h-53-\tbpf_rcu_read_unlock();\n--\ntools/testing/selftests/bpf/progs/ksock_common.h-57-\ntools/testing/selftests/bpf/progs/ksock_common.h:58:static inline int ksock_ctx_insert(struct bpf_ksock *ctx)\ntools/testing/selftests/bpf/progs/ksock_common.h-59-{\ntools/testing/selftests/bpf/progs/ksock_common.h-60-\tstruct __ksock_ctx_value *v;\ntools/testing/selftests/bpf/progs/ksock_common.h:61:\tstruct bpf_ksock *old;\ntools/testing/selftests/bpf/progs/ksock_common.h-62-\n--\ntools/testing/selftests/bpf/progs/ksock_common.h-64-\tif (!v) {\ntools/testing/selftests/bpf/progs/ksock_common.h:65:\t\tbpf_ksock_release(ctx);\ntools/testing/selftests/bpf/progs/ksock_common.h-66-\t\treturn -ENOENT;\n--\ntools/testing/selftests/bpf/progs/ksock_common.h-70-\tif (old) {\ntools/testing/selftests/bpf/progs/ksock_common.h:71:\t\tbpf_ksock_release(old);\ntools/testing/selftests/bpf/progs/ksock_common.h-72-\t\treturn -EEXIST;\n--\ntools/testing/selftests/bpf/progs/ksock_lsm.c=19=int ksock_setup(void *ctx)\ntools/testing/selftests/bpf/progs/ksock_lsm.c-20-{\ntools/testing/selftests/bpf/progs/ksock_lsm.c:21:\tstruct bpf_ksock_create_opts create_opts = {};\ntools/testing/selftests/bpf/progs/ksock_lsm.c:22:\tunion bpf_ksock_addr addr = {};\ntools/testing/selftests/bpf/progs/ksock_lsm.c:23:\tstruct bpf_ksock *ks;\ntools/testing/selftests/bpf/progs/ksock_lsm.c-24-\tint err = 0;\n--\ntools/testing/selftests/bpf/progs/ksock_lsm.c-29-\ntools/testing/selftests/bpf/progs/ksock_lsm.c:30:\tks = bpf_ksock_create(\u0026create_opts, sizeof(create_opts), \u0026err);\ntools/testing/selftests/bpf/progs/ksock_lsm.c-31-\tif (!ks)\n--\ntools/testing/selftests/bpf/progs/ksock_lsm.c-37-\ntools/testing/selftests/bpf/progs/ksock_lsm.c:38:\terr = bpf_ksock_connect(ks, \u0026addr, sizeof(addr));\ntools/testing/selftests/bpf/progs/ksock_lsm.c-39-\tif (err) {\ntools/testing/selftests/bpf/progs/ksock_lsm.c:40:\t\tbpf_ksock_release(ks);\ntools/testing/selftests/bpf/progs/ksock_lsm.c-41-\t\treturn err;\n--\ntools/testing/selftests/bpf/progs/ksock_lsm.c=51=int BPF_PROG(ksock_socket_bind, struct socket *sock, struct sockaddr *address,\n--\ntools/testing/selftests/bpf/progs/ksock_lsm.c-53-{\ntools/testing/selftests/bpf/progs/ksock_lsm.c:54:\tstruct bpf_ksock *ks;\ntools/testing/selftests/bpf/progs/ksock_lsm.c-55-\tu32 pid = bpf_get_current_pid_tgid() \u003e\u003e 32;\n--\ntools/testing/selftests/bpf/progs/ksock_lsm.c-65-\ntools/testing/selftests/bpf/progs/ksock_lsm.c:66:\tsend_ret = bpf_ksock_send(ks, send_data, sizeof(send_data));\ntools/testing/selftests/bpf/progs/ksock_lsm.c:67:\tbpf_ksock_release(ks);\ntools/testing/selftests/bpf/progs/ksock_lsm.c-68-\n--\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c=13=SEC(\"lsm.s/socket_sendmsg\")\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c:14:__description(\"bpf_ksock_send is rejected from socket_sendmsg LSM hook\")\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c:15:__failure __msg(\"calling kernel function bpf_ksock_send is not allowed\")\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c-16-int BPF_PROG(ksock_socket_sendmsg, struct socket *sock, struct msghdr *msg,\n--\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c-19-\tstruct __ksock_ctx_value *v;\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c:20:\tstruct bpf_ksock *ks;\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c-21-\n--\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c-29-\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c:30:\tbpf_ksock_send(ks, send_data, sizeof(send_data));\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c:31:\tbpf_ksock_release(ks);\ntools/testing/selftests/bpf/progs/ksock_lsm_verifier.c-32-\n--\ntools/testing/selftests/bpf/progs/ksock_wq.c=25=static int ksock_wq_callback(void *map, int *key, void *value)\ntools/testing/selftests/bpf/progs/ksock_wq.c-26-{\ntools/testing/selftests/bpf/progs/ksock_wq.c:27:\tstruct bpf_ksock_create_opts opts = {\ntools/testing/selftests/bpf/progs/ksock_wq.c-28-\t\t.family = AF_INET,\n--\ntools/testing/selftests/bpf/progs/ksock_wq.c-31-\t};\ntools/testing/selftests/bpf/progs/ksock_wq.c:32:\tstruct bpf_ksock *ks;\ntools/testing/selftests/bpf/progs/ksock_wq.c-33-\tint err = 0;\ntools/testing/selftests/bpf/progs/ksock_wq.c-34-\ntools/testing/selftests/bpf/progs/ksock_wq.c:35:\tks = bpf_ksock_create(\u0026opts, sizeof(opts), \u0026err);\ntools/testing/selftests/bpf/progs/ksock_wq.c-36-\tif (ks)\ntools/testing/selftests/bpf/progs/ksock_wq.c:37:\t\tbpf_ksock_release(ks);\ntools/testing/selftests/bpf/progs/ksock_wq.c-38-\tcreate_err = err;\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 21499
							output: 147
							thoughts: 354
						

Thoughts:
**My Analysis of Key BPF Ksock Functions and Configurations**

Okay, so the initial investigation has yielded some critical findings. It's clear that the core functionality revolves around the `bpf_ksock_create`, `bpf_ksock_connect`, `bpf_ksock_acquire`, `bpf_ksock_release`, and `bpf_ksock_send` functions, along with `connect_socket`. This is the fundamental building block.

Now, let's nail down the configurations. The `INET` and `BPF_SYSCALL` configurations are required. A quick verification of `connect_socket` is next to ensure it's accessible. It looks like it's reachable from `__sys_connect_file` and `__sys_connect`, which makes sense. Of course, `bpf_ksock_connect` also calls it.

The definition looks like this:

```c
int connect_socket(struct socket *sock, struct sockaddr_storage *address,
		   int addrlen, int flags)
```

I'll add `connect_socket` and the `bpf_ksock_*` functions to the `FocusSymbols` list. The `INET` and `BPF_SYSCALL` configs will be listed in `EnableConfigs`.

A quick check to confirm if `connect_socket` is static is prudent. Navigating to `net/socket.c`, I see:

```c
int connect_socket(struct socket *sock, struct sockaddr_storage *address,
		   int addrlen, int flags)
{
...
}
```

And it's not static. It's also declared in `include/linux/socket.h`, so it's clearly a valid symbol. Excellent.

Now, I'll use the `set-results` tool to finalize and provide this information for the next steps.



6/2 2026/08/07 20:19 tool set-results
0m Args:
{
  "EnableConfigs": [
    "INET",
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_ksock_create",
    "bpf_ksock_connect",
    "bpf_ksock_acquire",
    "bpf_ksock_release",
    "bpf_ksock_send",
    "connect_socket"
  ],
  "Reasoning": "The patch introduces a new BPF kfunc feature that allows BPF programs to create, connect, and send data through kernel sockets. This adds new reachable code paths in the BPF subsystem and networking core, which should be fuzzed.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "INET",
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_ksock_create",
    "bpf_ksock_connect",
    "bpf_ksock_acquire",
    "bpf_ksock_release",
    "bpf_ksock_send",
    "connect_socket"
  ],
  "Reasoning": "The patch introduces a new BPF kfunc feature that allows BPF programs to create, connect, and send data through kernel sockets. This adds new reachable code paths in the BPF subsystem and networking core, which should be fuzzed.",
  "WorthFuzzing": true
}

7/1 2026/08/07 20:19 action If
3m Args:
{
  "WorthFuzzing": true
}

Results:
null

8/2 2026/08/07 20:19 agent kmsan-evaluator
3m Model:
best-expensive

Args:
null

Results:
{
  "KMSANReasoning": "The patch introduces a new BPF kfunc API for creating, connecting, and sending data over kernel sockets (`bpf_ksock`). \n\nFrom an uninitialized memory perspective:\n1. All newly introduced kernel structures and stack variables are properly initialized:\n   - `struct bpf_ksock` is allocated using `kzalloc_obj`, ensuring all fields are zeroed.\n   - `struct msghdr msg` and `struct kvec iov` in `bpf_ksock_send` are initialized using designated initializers (`= { ... }`), which guarantees that all unspecified fields are zero-initialized by the compiler.\n   - `struct sockaddr_storage sa` in `bpf_ksock_connect` is partially initialized via `memcpy` from the BPF-provided address (up to 28 bytes). The uninitialized portion is never read, as `connect_socket` only reads up to `addrlen` (16 or 28 bytes). This exactly mirrors the behavior of the standard `connect()` syscall (which leaves the tail of `sockaddr_storage` uninitialized via `move_addr_to_kernel`), so it does not introduce a new info-leak risk.\n   - `opts_copy` in `bpf_ksock_create` is fully initialized by reading the 4 byte fields explicitly.\n2. The data sent over the socket in `bpf_ksock_send` is provided by the BPF program. The BPF verifier ensures that memory passed to kfuncs is within bounds and initialized (for stack variables). While BPF map values might contain uninitialized padding, this is a known property of the BPF subsystem and not a bug in the `bpf_ksock` C implementation.\n3. The primary risks introduced by this patch are related to object lifetime (use-after-free of `bpf_ksock` or `socket`), reference counting, and out-of-bounds accesses. These are perfectly covered by KASAN and standard bug detectors.\n\nThere are no uninitialized memory info-leaks or uses of uninitialized memory in conditional logic introduced by this patch. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "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:
For your convenience, here is the diff of the changes:
commit 2271d840dc63021eef44ce76b20d82c0bea281f6
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 7 20:19:11 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/bpf_ksock.h b/include/linux/bpf_ksock.h
new file mode 100644
index 0000000000000..cb387fb75e43b
--- /dev/null
+++ b/include/linux/bpf_ksock.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2026 Isovalent */
+
+#ifndef _BPF_KSOCK_H
+#define _BPF_KSOCK_H
+
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+
+/**
+ * struct bpf_ksock_create_opts - BPF kernel socket creation parameters
+ * @family:	Address family: AF_INET or AF_INET6.
+ * @type:	Socket type: only SOCK_DGRAM supported for now.
+ * @protocol:	Protocol number (e.g. IPPROTO_UDP), or 0 for the default protocol
+ *		of the given type.
+ * @reserved:	Must be zero. Reserved for future use.
+ */
+struct bpf_ksock_create_opts {
+	__u8 family;
+	__u8 type;
+	__u8 protocol;
+	__u8 reserved;
+};
+
+/**
+ * union bpf_ksock_addr - IPv4 or IPv6 socket address
+ * @sin: IPv4 socket address.
+ * @sin6: IPv6 socket address.
+ */
+union bpf_ksock_addr {
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+};
+
+#endif /* _BPF_KSOCK_H */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 2a8d7b14f1d11..5a5eb12501032 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -461,6 +461,8 @@ extern struct file *__sys_socket_file(int family, int type, int protocol);
 extern int __sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen);
 extern int __sys_bind_socket(struct socket *sock, struct sockaddr_storage *address,
 			     int addrlen);
+int connect_socket(struct socket *sock, struct sockaddr_storage *addr,
+		   int addrlen, int flags);
 extern int __sys_connect_file(struct file *file, struct sockaddr_storage *addr,
 			      int addrlen, int file_flags);
 extern int __sys_connect(int fd, struct sockaddr __user *uservaddr,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e6233c0081d10..037df7232f426 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4473,6 +4473,9 @@ BTF_ID(struct, task_struct)
 #ifdef CONFIG_CRYPTO
 BTF_ID(struct, bpf_crypto_ctx)
 #endif
+#ifdef CONFIG_INET
+BTF_ID(struct, bpf_ksock)
+#endif
 BTF_SET_END(rcu_protected_types)
 
 static bool rcu_protected_object(const struct btf *btf, u32 btf_id)
diff --git a/net/core/Makefile b/net/core/Makefile
index b3fdcb4e355fa..a9295b7859010 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -44,6 +44,9 @@ obj-$(CONFIG_FAILOVER) += failover.o
 obj-$(CONFIG_NET_SOCK_MSG) += skmsg.o
 obj-$(CONFIG_BPF_SYSCALL) += sock_map.o
 obj-$(CONFIG_BPF_SYSCALL) += bpf_sk_storage.o
+ifneq ($(CONFIG_INET),)
+obj-$(CONFIG_BPF_SYSCALL) += bpf_ksock.o
+endif
 obj-$(CONFIG_OF)	+= of_net.o
 obj-$(CONFIG_NET_TEST) += net_test.o
 obj-$(CONFIG_NET_DEVMEM) += devmem.o
diff --git a/net/core/bpf_ksock.c b/net/core/bpf_ksock.c
new file mode 100644
index 0000000000000..8a4f0150a0a41
--- /dev/null
+++ b/net/core/bpf_ksock.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2026 Isovalent */
+
+#include <linux/bpf.h>
+#include <linux/bpf_ksock.h>
+#include <linux/btf.h>
+#include <linux/btf_ids.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/net.h>
+#include <linux/refcount.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/unaligned.h>
+#include <linux/workqueue.h>
+#include <linux/ip.h>
+#include <net/sock.h>
+
+/**
+ * struct bpf_ksock - refcounted BPF kernel socket context
+ * @sock:	The underlying kernel socket.
+ * @usage:	Reference counter.
+ * @rwork:	RCU work for deferred cleanup (sock_release may sleep).
+ */
+struct bpf_ksock {
+	struct socket *sock;
+	refcount_t usage;
+	struct rcu_work rwork;
+};
+
+static void ksock_release_work_fn(struct work_struct *work)
+{
+	struct bpf_ksock *ks =
+		container_of(to_rcu_work(work), struct bpf_ksock, rwork);
+
+	sock_release(ks->sock);
+	kfree(ks);
+}
+
+static bool bpf_ksock_has_user_task_context(void)
+{
+	/*
+	 * Task work can run from do_exit() after exit_nsproxy_namespaces()
+	 * cleared current->nsproxy, while current is still not a kthread.
+	 */
+	return !(current->flags & PF_KTHREAD) && current->nsproxy;
+}
+
+__bpf_kfunc_start_defs();
+
+/**
+ * bpf_ksock_create() - Create a BPF kernel socket.
+ *
+ * Allocates and creates a kernel socket.
+ *
+ * The returned context must either be stored in a map as a kptr, or
+ * freed with bpf_ksock_release().
+ *
+ * This function may sleep (sock_create), so it can only be used
+ * in sleepable BPF programs (SYSCALL).
+ * It cannot be called from a BPF workqueue callback because that callback
+ * does not retain the invoking task's namespace or security context.
+ *
+ * @opts:	Pointer to struct bpf_ksock_create_opts with socket parameters.
+ * @opts__sz:	Size of the opts struct.
+ * @err__uninit:	Integer to store error code when NULL is returned.
+ */
+__bpf_kfunc struct bpf_ksock *
+bpf_ksock_create(const struct bpf_ksock_create_opts *opts, u32 opts__sz,
+		 int *err__uninit)
+{
+	struct bpf_ksock_create_opts opts_copy;
+	struct bpf_ksock *ks;
+	int err;
+
+	/*
+	 * sock_create() derives the network namespace, credentials, and cgroup
+	 * from current. Kernel threads, including BPF workqueue callbacks, do
+	 * not carry the context of the task that invoked the BPF program.
+	 */
+	if (!bpf_ksock_has_user_task_context()) {
+		err = -EOPNOTSUPP;
+		goto err_out;
+	}
+
+	if (!opts || opts__sz != sizeof(struct bpf_ksock_create_opts)) {
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	opts_copy = (struct bpf_ksock_create_opts){
+		.family = READ_ONCE(opts->family),
+		.type = READ_ONCE(opts->type),
+		.protocol = READ_ONCE(opts->protocol),
+		.reserved = READ_ONCE(opts->reserved),
+	};
+
+	if (opts_copy.reserved) {
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	if (opts_copy.family != AF_INET && opts_copy.family != AF_INET6) {
+		err = -EAFNOSUPPORT;
+		goto err_out;
+	}
+
+	if (opts_copy.type != SOCK_DGRAM) {
+		err = -EPROTONOSUPPORT;
+		goto err_out;
+	}
+
+	if (opts_copy.protocol != IPPROTO_UDP && opts_copy.protocol != 0) {
+		err = -EPROTONOSUPPORT;
+		goto err_out;
+	}
+
+	ks = kzalloc_obj(*ks);
+	if (!ks) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	/*
+	 * Use the normal current-task socket path so LSM/cgroup policy,
+	 * socket labels, and the active netns reference match a socket(2)
+	 * created by the BPF program's caller.
+	 */
+	err = sock_create(opts_copy.family, opts_copy.type, opts_copy.protocol,
+			  &ks->sock);
+	if (err)
+		goto err_free;
+
+	ks->sock->sk->sk_rcvbuf = SOCK_MIN_RCVBUF;
+	ks->sock->sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
+
+	refcount_set(&ks->usage, 1);
+	put_unaligned(0, err__uninit);
+	return ks;
+
+err_free:
+	kfree(ks);
+err_out:
+	put_unaligned(err, err__uninit);
+	return NULL;
+}
+
+/**
+ * bpf_ksock_connect() - Connect a BPF kernel socket to a remote address.
+ * @ks:		The BPF kernel socket context.
+ * @addr:	Pointer to an IPv4 or IPv6 socket address.
+ * @addr__sz:	Size of the address union.
+ *
+ * Connects the socket to the specified remote address and port.
+ *
+ * This function may sleep while connecting the socket, so it can only be used
+ * in sleepable BPF programs (SYSCALL).
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+__bpf_kfunc int bpf_ksock_connect(struct bpf_ksock *ks,
+				  const union bpf_ksock_addr *addr,
+				  u32 addr__sz)
+{
+	struct sockaddr_storage sa;
+	int addrlen;
+
+	if (!bpf_ksock_has_user_task_context())
+		return -EOPNOTSUPP;
+
+	if (!addr || addr__sz != sizeof(*addr))
+		return -EINVAL;
+
+	/* Kfunc memory arguments may be unaligned. */
+	memcpy(&sa, addr, sizeof(*addr));
+
+	switch (sa.ss_family) {
+	case AF_INET:
+		addrlen = sizeof(struct sockaddr_in);
+		break;
+#if IS_ENABLED(CONFIG_IPV6)
+	case AF_INET6:
+		addrlen = sizeof(struct sockaddr_in6);
+		break;
+#endif
+	default:
+		return -EAFNOSUPPORT;
+	}
+
+	return connect_socket(ks->sock, &sa, addrlen, 0);
+}
+
+/**
+ * bpf_ksock_acquire() - Acquire a reference to a BPF kernel socket.
+ * @ks:	The BPF kernel socket context to acquire. Must be a
+ *	trusted pointer (e.g. RCU-protected kptr from a map).
+ *
+ * The acquired context must either be stored in a map as a kptr, or
+ * freed with bpf_ksock_release().
+ */
+__bpf_kfunc struct bpf_ksock *bpf_ksock_acquire(struct bpf_ksock *ks)
+{
+	if (!refcount_inc_not_zero(&ks->usage))
+		return NULL;
+	return ks;
+}
+
+/**
+ * bpf_ksock_release() - Release a BPF kernel socket.
+ * @ks:	The BPF kernel socket context to release.
+ *
+ * When the final reference is released, the socket is cleaned up via
+ * queue_rcu_work() (since sock_release may sleep).
+ */
+__bpf_kfunc void bpf_ksock_release(struct bpf_ksock *ks)
+{
+	if (refcount_dec_and_test(&ks->usage)) {
+		INIT_RCU_WORK(&ks->rwork, ksock_release_work_fn);
+		queue_rcu_work(system_dfl_wq, &ks->rwork);
+	}
+}
+
+__bpf_kfunc void bpf_ksock_release_dtor(void *ks)
+{
+	bpf_ksock_release(ks);
+}
+CFI_NOSEAL(bpf_ksock_release_dtor);
+
+/**
+ * bpf_ksock_send() - Send data through a BPF kernel socket.
+ * @ks:		The BPF kernel socket context. Must be an acquired reference.
+ * @data:	Pointer to the data to send.
+ * @data__sz:	Size of the data to send (max 65535 bytes).
+ *
+ * Sends data on a connected socket, best-effort and nonblocking. This may sleep
+ * (kernel_sendmsg), so it can only be called from sleepable BPF programs.
+ *
+ * Return: Number of bytes sent on success, negative errno on error.
+ */
+__bpf_kfunc int bpf_ksock_send(struct bpf_ksock *ks, const void *data,
+			       u32 data__sz)
+{
+	struct msghdr msg = {
+		.msg_flags = MSG_DONTWAIT,
+	};
+	struct kvec iov = {
+		.iov_base = (void *)data,
+		.iov_len = data__sz,
+	};
+	int ret;
+
+	if (!bpf_ksock_has_user_task_context())
+		return -EOPNOTSUPP;
+
+	/* Early check for UDP. Exact limits enforced by kernel_sendmsg(). */
+	if (data__sz > IP_MAX_MTU)
+		return -EMSGSIZE;
+
+	ret = kernel_sendmsg(ks->sock, &msg, &iov, 1, data__sz);
+
+	return ret;
+}
+
+__bpf_kfunc_end_defs();
+
+BTF_KFUNCS_START(ksock_init_kfunc_btf_ids)
+BTF_ID_FLAGS(func, bpf_ksock_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_ksock_connect, KF_SLEEPABLE)
+BTF_KFUNCS_END(ksock_init_kfunc_btf_ids)
+
+static const struct btf_kfunc_id_set ksock_init_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set = &ksock_init_kfunc_btf_ids,
+};
+
+BTF_KFUNCS_START(ksock_kfunc_btf_ids)
+BTF_ID_FLAGS(func, bpf_ksock_release, KF_RELEASE)
+BTF_ID_FLAGS(func, bpf_ksock_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
+BTF_ID_FLAGS(func, bpf_ksock_send, KF_SLEEPABLE)
+BTF_KFUNCS_END(ksock_kfunc_btf_ids)
+
+#ifdef CONFIG_BPF_LSM
+BTF_ID_LIST_SINGLE(bpf_lsm_socket_sendmsg_id, func, bpf_lsm_socket_sendmsg)
+#endif
+
+static int bpf_ksock_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id)
+{
+	if (!btf_id_set8_contains(&ksock_kfunc_btf_ids, kfunc_id))
+		return 0;
+
+	if (prog->type == BPF_PROG_TYPE_SYSCALL)
+		return 0;
+
+#ifdef CONFIG_BPF_LSM
+	if (prog->type == BPF_PROG_TYPE_LSM &&
+	    prog->aux->attach_btf_id != bpf_lsm_socket_sendmsg_id[0])
+		return 0;
+#endif
+
+	return -EACCES;
+}
+
+static const struct btf_kfunc_id_set ksock_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set = &ksock_kfunc_btf_ids,
+	.filter = bpf_ksock_kfunc_filter,
+};
+
+BTF_ID_LIST(bpf_ksock_dtor_ids)
+BTF_ID(struct, bpf_ksock)
+BTF_ID(func, bpf_ksock_release_dtor)
+
+static int __init bpf_ksock_kfunc_init(void)
+{
+	int ret;
+	const struct btf_id_dtor_kfunc bpf_ksock_dtors[] = {
+		{
+			.btf_id = bpf_ksock_dtor_ids[0],
+			.kfunc_btf_id = bpf_ksock_dtor_ids[1],
+		},
+	};
+
+	ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL,
+					&ksock_init_kfunc_set);
+	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL,
+					       &ksock_kfunc_set);
+	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM,
+					       &ksock_kfunc_set);
+	return ret ?: register_btf_id_dtor_kfuncs(bpf_ksock_dtors,
+						  ARRAY_SIZE(bpf_ksock_dtors),
+						  THIS_MODULE);
+}
+
+late_initcall(bpf_ksock_kfunc_init);
diff --git a/net/socket.c b/net/socket.c
index 63c69a0fa74e1..8f18f124d92a7 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2103,6 +2103,20 @@ SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,
 	return __sys_accept4(fd, upeer_sockaddr, upeer_addrlen, 0);
 }
 
+int connect_socket(struct socket *sock, struct sockaddr_storage *address,
+		   int addrlen, int flags)
+{
+	int err;
+
+	err = security_socket_connect(sock, (struct sockaddr *)address, addrlen);
+	if (err)
+		return err;
+
+	return READ_ONCE(sock->ops)->connect(sock,
+				    (struct sockaddr_unsized *)address,
+				    addrlen, flags);
+}
+
 /*
  *	Attempt to connect to a socket with the server address.  The address
  *	is in user space so we verify it is OK and move it to kernel space.
@@ -2119,23 +2133,13 @@ int __sys_connect_file(struct file *file, struct sockaddr_storage *address,
 		       int addrlen, int file_flags)
 {
 	struct socket *sock;
-	int err;
 
 	sock = sock_from_file(file);
-	if (!sock) {
-		err = -ENOTSOCK;
-		goto out;
-	}
-
-	err =
-	    security_socket_connect(sock, (struct sockaddr *)address, addrlen);
-	if (err)
-		goto out;
+	if (!sock)
+		return -ENOTSOCK;
 
-	err = READ_ONCE(sock->ops)->connect(sock, (struct sockaddr_unsized *)address,
-					    addrlen, sock->file->f_flags | file_flags);
-out:
-	return err;
+	return connect_socket(sock, address, addrlen,
+				    sock->file->f_flags | file_flags);
 }
 
 int __sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen)
diff --git a/tools/testing/selftests/bpf/prog_tests/ksock.c b/tools/testing/selftests/bpf/prog_tests/ksock.c
new file mode 100644
index 0000000000000..fb1a332eb3da1
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/ksock.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Isovalent */
+
+#include <arpa/inet.h>
+
+#include "test_progs.h"
+#include "network_helpers.h"
+#include "ksock_lsm.skel.h"
+#include "ksock_lsm_verifier.skel.h"
+
+#define NS_TEST "ksock_lsm_ns"
+#define RECV_PORT 7777
+#define RECV_TIMEOUT_SEC 5
+
+struct ksock_test_env {
+	bool netns_created;
+	struct nstoken *nstoken;
+	int rfd;
+};
+
+static bool ksock_test_env_setup(struct ksock_test_env *env)
+{
+	struct sockaddr_in addr = {
+		.sin_family = AF_INET,
+		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+		.sin_port = htons(RECV_PORT),
+	};
+	struct timeval tv = { .tv_sec = RECV_TIMEOUT_SEC };
+	int err;
+
+	memset(env, 0, sizeof(*env));
+	env->rfd = -1;
+
+	SYS(fail, "ip netns add %s", NS_TEST);
+	env->netns_created = true;
+	SYS(fail, "ip -net %s link set lo up", NS_TEST);
+
+	env->nstoken = open_netns(NS_TEST);
+	if (!ASSERT_OK_PTR(env->nstoken, "open_netns"))
+		goto fail;
+
+	env->rfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (!ASSERT_OK_FD(env->rfd, "receiver socket"))
+		goto fail;
+
+	err = bind(env->rfd, (struct sockaddr *)&addr, sizeof(addr));
+	if (!ASSERT_OK(err, "bind receiver"))
+		goto fail;
+
+	err = setsockopt(env->rfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+	if (!ASSERT_OK(err, "set rcvtimeo"))
+		goto fail;
+
+	return true;
+
+fail:
+	return false;
+}
+
+void test_ksock_lsm(void)
+{
+	LIBBPF_OPTS(bpf_test_run_opts, opts);
+	struct ksock_test_env env;
+	struct sockaddr_in trigger_addr = {
+		.sin_family = AF_INET,
+		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+	};
+	struct ksock_lsm *skel;
+	char recv_data[sizeof(skel->data->send_data)] = {};
+	ssize_t n;
+	int tfd = -1;
+	int err;
+
+	skel = ksock_lsm__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "skel open_and_load"))
+		return;
+
+	if (!ksock_test_env_setup(&env))
+		goto fail;
+
+	/* Step 1: Run the setup SYSCALL prog to create the ksock */
+	skel->bss->ipv4_remote = htonl(INADDR_LOOPBACK);
+	skel->bss->remote_port = RECV_PORT;
+	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.ksock_setup),
+				     &opts);
+	if (!ASSERT_OK(err, "ksock_setup run"))
+		goto fail;
+	if (!ASSERT_OK(opts.retval, "ksock_setup retval"))
+		goto fail;
+
+	/* Step 2: Attach LSM prog and trigger socket_bind from userspace */
+	skel->links.ksock_socket_bind =
+		bpf_program__attach_lsm(skel->progs.ksock_socket_bind);
+	if (!ASSERT_OK_PTR(skel->links.ksock_socket_bind,
+			   "attach socket_bind lsm"))
+		goto fail;
+
+	tfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (!ASSERT_OK_FD(tfd, "trigger socket"))
+		goto fail;
+
+	skel->bss->target_pid = getpid();
+	err = bind(tfd, (struct sockaddr *)&trigger_addr, sizeof(trigger_addr));
+	skel->bss->target_pid = 0;
+	if (!ASSERT_OK(err, "trigger bind"))
+		goto fail;
+
+	/* Step 3: Verify the LSM hook sent the notification */
+	if (!ASSERT_EQ(skel->data->send_ret, sizeof(skel->data->send_data),
+		       "LSM send bytes"))
+		goto fail;
+
+	n = recvfrom(env.rfd, recv_data, sizeof(recv_data), 0, NULL, NULL);
+	if (ASSERT_EQ(n, sizeof(recv_data), "recvfrom len"))
+		ASSERT_MEMEQ(recv_data, skel->data->send_data, sizeof(recv_data),
+			     "payload match");
+
+fail:
+	if (tfd >= 0)
+		close(tfd);
+	if (env.rfd >= 0)
+		close(env.rfd);
+	if (env.nstoken)
+		close_netns(env.nstoken);
+	if (env.netns_created)
+		SYS_NOFAIL("ip netns del %s >/dev/null 2>&1", NS_TEST);
+	ksock_lsm__destroy(skel);
+}
+
+void test_ksock_lsm_verifier(void)
+{
+	RUN_TESTS(ksock_lsm_verifier);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/ksock_wq.c b/tools/testing/selftests/bpf/prog_tests/ksock_wq.c
new file mode 100644
index 0000000000000..5b9c6cc303922
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/ksock_wq.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Isovalent */
+
+#include <unistd.h>
+
+#include "test_progs.h"
+#include "ksock_wq.skel.h"
+
+void test_ksock_wq(void)
+{
+	LIBBPF_OPTS(bpf_test_run_opts, opts);
+	struct ksock_wq *skel;
+	int err;
+
+	skel = ksock_wq__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "ksock_wq open and load"))
+		return;
+
+	err = bpf_prog_test_run_opts(
+		bpf_program__fd(skel->progs.ksock_wq_start), &opts);
+	if (!ASSERT_OK(err, "run ksock_wq_start"))
+		goto out;
+	if (!ASSERT_OK(opts.retval, "ksock_wq_start retval"))
+		goto out;
+
+	while (!__atomic_load_n(&skel->bss->callback_done, __ATOMIC_ACQUIRE))
+		usleep(1000);
+
+	ASSERT_EQ(skel->bss->create_err, -EOPNOTSUPP,
+		  "workqueue create rejected");
+
+out:
+	ksock_wq__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/ksock_common.h b/tools/testing/selftests/bpf/progs/ksock_common.h
new file mode 100644
index 0000000000000..01edaeb9fdd4a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/ksock_common.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2026 Isovalent */
+
+#ifndef _KSOCK_COMMON_H
+#define _KSOCK_COMMON_H
+
+#include "errno.h"
+
+#define SOCK_DGRAM	2
+#define IPPROTO_UDP	17
+
+struct bpf_ksock *bpf_ksock_create(const struct bpf_ksock_create_opts *opts,
+				   u32 opts__sz, int *err__uninit) __ksym;
+int bpf_ksock_connect(struct bpf_ksock *ks, const union bpf_ksock_addr *addr,
+		      u32 addr__sz) __ksym;
+struct bpf_ksock *bpf_ksock_acquire(struct bpf_ksock *ks) __ksym;
+void bpf_ksock_release(struct bpf_ksock *ks) __ksym;
+int bpf_ksock_send(struct bpf_ksock *ks, const void *data, u32 data__sz) __ksym;
+void bpf_rcu_read_lock(void) __ksym;
+void bpf_rcu_read_unlock(void) __ksym;
+
+struct __ksock_ctx_value {
+	struct bpf_ksock __kptr * ctx;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__type(key, int);
+	__type(value, struct __ksock_ctx_value);
+	__uint(max_entries, 1);
+} __ksock_ctx_map SEC(".maps");
+
+static inline struct __ksock_ctx_value *ksock_ctx_value_lookup(void)
+{
+	u32 key = 0;
+
+	return bpf_map_lookup_elem(&__ksock_ctx_map, &key);
+}
+
+static inline struct bpf_ksock *ksock_ctx_get(void)
+{
+	struct __ksock_ctx_value *v;
+	struct bpf_ksock *ks = NULL, *tmp;
+
+	v = ksock_ctx_value_lookup();
+	if (!v)
+		return NULL;
+
+	bpf_rcu_read_lock();
+	tmp = v->ctx;
+	if (tmp)
+		ks = bpf_ksock_acquire(tmp);
+	bpf_rcu_read_unlock();
+
+	return ks;
+}
+
+static inline int ksock_ctx_insert(struct bpf_ksock *ctx)
+{
+	struct __ksock_ctx_value *v;
+	struct bpf_ksock *old;
+
+	v = ksock_ctx_value_lookup();
+	if (!v) {
+		bpf_ksock_release(ctx);
+		return -ENOENT;
+	}
+
+	old = bpf_kptr_xchg(&v->ctx, ctx);
+	if (old) {
+		bpf_ksock_release(old);
+		return -EEXIST;
+	}
+
+	return 0;
+}
+
+#endif /* _KSOCK_COMMON_H */
diff --git a/tools/testing/selftests/bpf/progs/ksock_lsm.c b/tools/testing/selftests/bpf/progs/ksock_lsm.c
new file mode 100644
index 0000000000000..9808451098efb
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/ksock_lsm.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Isovalent */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_endian.h>
+#include "bpf_tracing_net.h"
+#include "ksock_common.h"
+
+char send_data[32] = "hello from bpf ksock";
+
+__be32 ipv4_remote;
+__u16 remote_port;
+int target_pid;
+int send_ret = -1;
+
+SEC("syscall")
+int ksock_setup(void *ctx)
+{
+	struct bpf_ksock_create_opts create_opts = {};
+	union bpf_ksock_addr addr = {};
+	struct bpf_ksock *ks;
+	int err = 0;
+
+	create_opts.family = AF_INET;
+	create_opts.type = SOCK_DGRAM;
+	create_opts.protocol = IPPROTO_UDP;
+
+	ks = bpf_ksock_create(&create_opts, sizeof(create_opts), &err);
+	if (!ks)
+		return err;
+
+	addr.sin.sin_family = AF_INET;
+	addr.sin.sin_port = bpf_htons(remote_port);
+	addr.sin.sin_addr.s_addr = ipv4_remote;
+
+	err = bpf_ksock_connect(ks, &addr, sizeof(addr));
+	if (err) {
+		bpf_ksock_release(ks);
+		return err;
+	}
+
+	err = ksock_ctx_insert(ks);
+	if (err && err != -EEXIST)
+		return err;
+	return 0;
+}
+
+SEC("lsm.s/socket_bind")
+int BPF_PROG(ksock_socket_bind, struct socket *sock, struct sockaddr *address,
+	     int addrlen, int ret)
+{
+	struct bpf_ksock *ks;
+	u32 pid = bpf_get_current_pid_tgid() >> 32;
+
+	if (ret || pid != target_pid)
+		return ret;
+
+	ks = ksock_ctx_get();
+	if (!ks) {
+		send_ret = -ENOENT;
+		return ret;
+	}
+
+	send_ret = bpf_ksock_send(ks, send_data, sizeof(send_data));
+	bpf_ksock_release(ks);
+
+	return ret;
+}
+
+char __license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c b/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c
new file mode 100644
index 0000000000000..5b969e03b6d69
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Isovalent */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+#include "bpf_tracing_net.h"
+#include "ksock_common.h"
+
+char send_data[11] = "dummy data";
+
+SEC("lsm.s/socket_sendmsg")
+__description("bpf_ksock_send is rejected from socket_sendmsg LSM hook")
+__failure __msg("calling kernel function bpf_ksock_send is not allowed")
+int BPF_PROG(ksock_socket_sendmsg, struct socket *sock, struct msghdr *msg,
+	     int size, int ret)
+{
+	struct __ksock_ctx_value *v;
+	struct bpf_ksock *ks;
+
+	v = ksock_ctx_value_lookup();
+	if (!v)
+		return ret;
+
+	ks = bpf_kptr_xchg(&v->ctx, NULL);
+	if (!ks)
+		return ret;
+
+	bpf_ksock_send(ks, send_data, sizeof(send_data));
+	bpf_ksock_release(ks);
+
+	return ret;
+}
+
+char __license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/ksock_wq.c b/tools/testing/selftests/bpf/progs/ksock_wq.c
new file mode 100644
index 0000000000000..16a1873d132ed
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/ksock_wq.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Isovalent */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include "bpf_experimental.h"
+#include "bpf_tracing_net.h"
+#include "errno.h"
+#include "ksock_common.h"
+
+struct ksock_wq_value {
+	struct bpf_wq work;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 1);
+	__type(key, u32);
+	__type(value, struct ksock_wq_value);
+} work_map SEC(".maps");
+
+int create_err;
+u32 callback_done;
+
+static int ksock_wq_callback(void *map, int *key, void *value)
+{
+	struct bpf_ksock_create_opts opts = {
+		.family = AF_INET,
+		.type = SOCK_DGRAM,
+		.protocol = IPPROTO_UDP,
+	};
+	struct bpf_ksock *ks;
+	int err = 0;
+
+	ks = bpf_ksock_create(&opts, sizeof(opts), &err);
+	if (ks)
+		bpf_ksock_release(ks);
+	create_err = err;
+	__sync_fetch_and_add(&callback_done, 1);
+	return 0;
+}
+
+SEC("syscall")
+int ksock_wq_start(void *ctx)
+{
+	struct ksock_wq_value *value;
+	u32 key = 0;
+	int err;
+
+	value = bpf_map_lookup_elem(&work_map, &key);
+	if (!value)
+		return -ENOENT;
+	err = bpf_wq_init(&value->work, &work_map, 0);
+	if (err)
+		return err;
+	err = bpf_wq_set_callback(&value->work, ksock_wq_callback, 0);
+	if (err)
+		return err;
+	return bpf_wq_start(&value->work, 0);
+}
+
+char __license[] SEC("license") = "GPL";

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

Args:
null

Results:
null

Tokens:
							input: 12551
							output: 19
							thoughts: 3644
						

Thoughts:
**BPF Ksocket API: Uninitialized Memory Analysis**

Okay, so this patch introduces a new BPF kernel socket API. The core of it involves BPF kfuncs for socket creation, connection, and data transfer (`bpf_ksock_create`, `bpf_ksock_connect`, `bpf_ksock_send`). My immediate focus is on memory safety, particularly uninitialized memory.

First, `bpf_ksock_create` seems straightforward. It copies relevant fields from a `bpf_ksock_create_opts` structure, which comes from BPF program memory. The verifier is supposed to ensure initialization, so no immediate concerns there. The critical part is checking that the error pointer `err__uninit` gets correctly initialized as expected.

`bpf_ksock_connect` is where things get interesting. It copies a `union bpf_ksock_addr` from BPF memory into a `struct sockaddr_storage` named `sa`. The potential issue is that `sa` is *much* larger than the union, so I've got to check that `memcpy` doesn't leave uninitialized data in `sa`. The code only initializes 28 bytes of the `sa` struct, while it is 128 bytes in total. The `connect_socket` is called by `bpf_ksock_connect`, and I need to be *absolutely* sure that `connect_socket`, or anything it calls, doesn't read beyond the copied data.

The code then uses `sa.ss_family` to determine the address family and sets `addrlen` accordingly. The subsequent `connect_socket` uses `sa` and `addrlen`. I need to confirm that `connect_socket`, `security_socket_connect`, and finally the `connect` operation from `sock->ops` only read the specified `addrlen` bytes. The concern arises that some LSM hook or socket implementation might cast `address` to a wider struct or copy it.

I checked `connect_socket` and traced the calls down, and as it stands now, it only seems to use the `addrlen` part of `address`. So as far as the current implementation goes, there should be no uninitialized memory leaks.
The use of the helper `connect_socket` is a nice refactoring step to reuse code, and it does not introduce any issues.

Finally, `bpf_ksock_send` uses `kernel_sendmsg` to send data from BPF memory. The BPF verifier is supposed to ensure the data is initialized up to `data__sz`, and so there are no apparent problems.

So, the key concerns are centered on `bpf_ksock_connect` and the incomplete initialization of `sa`. However, I believe that the existing code protects against uninitialized memory issues.
I need to be very diligent when checking `security_socket_connect` and `sock->ops->connect` to ensure they don't read beyond the intended range.



10/3 2026/08/07 20:20 tool grepper
0m Args:
{
  "Expression": "security_socket_connect"
}

Results:
{
  "Output": "include/linux/security.h=1672=int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen);\ninclude/linux/security.h:1673:int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen);\ninclude/linux/security.h-1674-int security_socket_listen(struct socket *sock, int backlog);\n--\ninclude/linux/security.h=1759=static inline int security_socket_bind(struct socket *sock,\n--\ninclude/linux/security.h-1765-\ninclude/linux/security.h:1766:static inline int security_socket_connect(struct socket *sock,\ninclude/linux/security.h-1767-\t\t\t\t\t  struct sockaddr *address,\n--\nnet/socket.c=2106=int connect_socket(struct socket *sock, struct sockaddr_storage *address,\n--\nnet/socket.c-2110-\nnet/socket.c:2111:\terr = security_socket_connect(sock, (struct sockaddr *)address, addrlen);\nnet/socket.c-2112-\tif (err)\n--\nsecurity/security.c=4248=int security_socket_bind(struct socket *sock,\n--\nsecurity/security.c-4254-/**\nsecurity/security.c:4255: * security_socket_connect() - Check if a socket connect operation is allowed\nsecurity/security.c-4256- * @sock: socket\n--\nsecurity/security.c-4264- */\nsecurity/security.c:4265:int security_socket_connect(struct socket *sock,\nsecurity/security.c-4266-\t\t\t    struct sockaddr *address, int addrlen)\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 17301
							output: 20
							thoughts: 19
						

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

**Diving into `security_socket_connect`**

Alright, let's zero in on `security_socket_connect` within `security/security.c`. I need to refresh my memory on the specifics of how this function handles connection establishment from a security perspective. I want to understand its role in authentication, authorization, and any checks it performs to ensure a secure channel is established. I'm particularly interested in any potential vulnerabilities or attack surfaces related to the socket connection process itself - think about handshake vulnerabilities, man-in-the-middle attack prevention, potential for credential leaks, or even race conditions in the authentication flow. I'll need to analyze the code carefully to confirm that this function adheres to the latest best practices and any relevant security policies. This is a critical point in the system, so I'm expecting to delve into details like key exchange, certificate validation, and any mitigation strategies implemented to protect against various types of connection hijacking. I'll be keeping an eye out for any unusual or overly complex logic that might introduce subtle weaknesses, or poorly handled error conditions that could be exploited.



12/3 2026/08/07 20:20 tool grepper
0m Args:
{
  "Expression": "int security_socket_connect"
}

Results:
{
  "Output": "include/linux/security.h=1672=int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen);\ninclude/linux/security.h:1673:int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen);\ninclude/linux/security.h-1674-int security_socket_listen(struct socket *sock, int backlog);\n--\ninclude/linux/security.h=1759=static inline int security_socket_bind(struct socket *sock,\n--\ninclude/linux/security.h-1765-\ninclude/linux/security.h:1766:static inline int security_socket_connect(struct socket *sock,\ninclude/linux/security.h-1767-\t\t\t\t\t  struct sockaddr *address,\n--\nsecurity/security.c=4248=int security_socket_bind(struct socket *sock,\n--\nsecurity/security.c-4264- */\nsecurity/security.c:4265:int security_socket_connect(struct socket *sock,\nsecurity/security.c-4266-\t\t\t    struct sockaddr *address, int addrlen)\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 17877
							output: 34
							thoughts: 19
						

Thoughts:
Here's a summary of my thought process, as if I were the one thinking it:

**Diving into the `security_socket_connect` Function**

Alright, time to dig into the heart of the matter. My first instinct is to open up `security_socket_connect`, specifically in the `security/security.c` file. I need to understand exactly how this crucial function works. It’s the cornerstone of handling socket connections from a security perspective. I want to see how it’s designed to manage authentication, authorization, any potential vulnerabilities, and ensure everything is buttoned up securely. I'm looking for the nitty-gritty details here.



14/3 2026/08/07 20:20 tool read-file
0m Args:
{
  "File": "security/security.c",
  "FirstLine": 4265,
  "LineCount": 20
}

Results:
{
  "Contents": "4265:\tint security_socket_connect(struct socket *sock,\n4266:\t\t\t\t    struct sockaddr *address, int addrlen)\n4267:\t{\n4268:\t\treturn call_int_hook(socket_connect, sock, address, addrlen);\n4269:\t}\n4270:\t\n4271:\t/**\n4272:\t * security_socket_listen() - Check if a socket is allowed to listen\n4273:\t * @sock: socket\n4274:\t * @backlog: connection queue size\n4275:\t *\n4276:\t * Check permission before socket protocol layer listen operation.\n4277:\t *\n4278:\t * Return: Returns 0 if permission is granted.\n4279:\t */\n4280:\tint security_socket_listen(struct socket *sock, int backlog)\n4281:\t{\n4282:\t\treturn call_int_hook(socket_listen, sock, backlog);\n4283:\t}\n4284:\t\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 18352
							output: 20
							thoughts: 39
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:

**Kernel Hook Exploration**

Alright, so I'm thinking about how this connection process works, and my initial thought is that it's just calling the relevant LSM hook, which is, of course, the primary point of intervention. The question then becomes where this hook is called.

But what about the specifics? We're talking about the low-level details of connection establishment. Surely there's a more granular hook point than simply the top level LSM call? So, my mind immediately jumps to `sock->ops->connect`. This must be where the heavy lifting of the connection happens, so likely the best place to find details.

And, following this line of thought, if we're dealing with `AF_INET` and, specifically, UDP sockets, that `connect` operation will likely resolve to `inet_dgram_connect`. It will be useful to review this code for the relevant connection details.



16/3 2026/08/07 20:20 tool grepper
0m Args:
{
  "Expression": "inet_dgram_connect"
}

Results:
{
  "Output": "include/net/inet_common.h=27=int __inet_stream_connect(struct socket *sock, struct sockaddr_unsized *uaddr,\ninclude/net/inet_common.h-28-\t\t\t  int addr_len, int flags, int is_sendmsg);\ninclude/net/inet_common.h:29:int inet_dgram_connect(struct socket *sock, struct sockaddr_unsized *uaddr,\ninclude/net/inet_common.h-30-\t\t       int addr_len, int flags);\n--\nnet/ipv4/af_inet.c=478=int __inet_bind(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len,\n--\nnet/ipv4/af_inet.c-574-\nnet/ipv4/af_inet.c:575:int inet_dgram_connect(struct socket *sock, struct sockaddr_unsized *uaddr,\nnet/ipv4/af_inet.c-576-\t\t       int addr_len, int flags)\n--\nnet/ipv4/af_inet.c-600-}\nnet/ipv4/af_inet.c:601:EXPORT_SYMBOL(inet_dgram_connect);\nnet/ipv4/af_inet.c-602-\n--\nnet/ipv4/af_inet.c=1096=const struct proto_ops inet_dgram_ops = {\n--\nnet/ipv4/af_inet.c-1100-\t.bind\t\t   = inet_bind,\nnet/ipv4/af_inet.c:1101:\t.connect\t   = inet_dgram_connect,\nnet/ipv4/af_inet.c-1102-\t.socketpair\t   = sock_no_socketpair,\n--\nnet/ipv4/af_inet.c=1128=static const struct proto_ops inet_sockraw_ops = {\n--\nnet/ipv4/af_inet.c-1132-\t.bind\t\t   = inet_bind,\nnet/ipv4/af_inet.c:1133:\t.connect\t   = inet_dgram_connect,\nnet/ipv4/af_inet.c-1134-\t.socketpair\t   = sock_no_socketpair,\n--\nnet/ipv6/af_inet6.c=696=const struct proto_ops inet6_dgram_ops = {\n--\nnet/ipv6/af_inet6.c-700-\t.bind\t\t   = inet6_bind,\nnet/ipv6/af_inet6.c:701:\t.connect\t   = inet_dgram_connect,\t/* ok\t\t*/\nnet/ipv6/af_inet6.c-702-\t.socketpair\t   = sock_no_socketpair,\t/* a do nothing\t*/\n--\nnet/ipv6/raw.c=1276=const struct proto_ops inet6_sockraw_ops = {\n--\nnet/ipv6/raw.c-1280-\t.bind\t\t   = inet6_bind,\nnet/ipv6/raw.c:1281:\t.connect\t   = inet_dgram_connect,\t/* ok\t\t*/\nnet/ipv6/raw.c-1282-\t.socketpair\t   = sock_no_socketpair,\t/* a do nothing\t*/\n--\nnet/l2tp/l2tp_ip.c=628=static const struct proto_ops l2tp_ip_ops = {\n--\nnet/l2tp/l2tp_ip.c-632-\t.bind\t\t   = inet_bind,\nnet/l2tp/l2tp_ip.c:633:\t.connect\t   = inet_dgram_connect,\nnet/l2tp/l2tp_ip.c-634-\t.socketpair\t   = sock_no_socketpair,\n--\nnet/l2tp/l2tp_ip6.c=757=static const struct proto_ops l2tp_ip6_ops = {\n--\nnet/l2tp/l2tp_ip6.c-761-\t.bind\t\t   = inet6_bind,\nnet/l2tp/l2tp_ip6.c:762:\t.connect\t   = inet_dgram_connect,\nnet/l2tp/l2tp_ip6.c-763-\t.socketpair\t   = sock_no_socketpair,\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 19728
							output: 38
							thoughts: 12
						

Thoughts:
Okay, here's my interpretation of that request, formatted as requested:

**Deconstructing `inet_dgram_connect`**

Alright, let's dive into this. The instruction is to examine the `inet_dgram_connect` function. I'm assuming, given the context, this is related to networking and specifically, probably, the connection establishment within datagram sockets within the Internet protocol family.

My initial thoughts are to understand its purpose. What is this function *doing* at a fundamental level? Is it really establishing a "connection" in the same sense as with TCP, or is it a misnomer for a function that associates a datagram socket with a specific peer address? I suspect the latter. I'll need to look at how it interacts with the underlying system calls, probably `connect` or something similar, and understand the implications for things like routing, error handling (if the peer isn't reachable), and the overall state of the socket. I need to see how the kernel handles the binding of the local socket with a remote address in the context of connectionless communication. How does this affect things like `sendto()` and `recvfrom()`? Will this impact the way I handle source and destination addresses?

I'm anticipating that reading this will also reveal details about socket options and any nuances specific to UDP (or whatever other datagram protocol is in play) regarding this "connected" state. I will also be particularly interested in race conditions or security considerations that arise from establishing a "connected" state on a datagram socket. Time to get into the details.



18/3 2026/08/07 20:20 tool read-file
0m Args:
{
  "File": "net/ipv4/af_inet.c",
  "FirstLine": 575,
  "LineCount": 26
}

Results:
{
  "Contents": " 575:\tint inet_dgram_connect(struct socket *sock, struct sockaddr_unsized *uaddr,\n 576:\t\t\t       int addr_len, int flags)\n 577:\t{\n 578:\t\tstruct sock *sk = sock-\u003esk;\n 579:\t\tconst struct proto *prot;\n 580:\t\tint err;\n 581:\t\n 582:\t\tif (addr_len \u003c sizeof(uaddr-\u003esa_family))\n 583:\t\t\treturn -EINVAL;\n 584:\t\n 585:\t\t/* IPV6_ADDRFORM can change sk-\u003esk_prot under us. */\n 586:\t\tprot = READ_ONCE(sk-\u003esk_prot);\n 587:\t\n 588:\t\tif (uaddr-\u003esa_family == AF_UNSPEC)\n 589:\t\t\treturn prot-\u003edisconnect(sk, flags);\n 590:\t\n 591:\t\tif (BPF_CGROUP_PRE_CONNECT_ENABLED(sk)) {\n 592:\t\t\terr = prot-\u003epre_connect(sk, uaddr, addr_len);\n 593:\t\t\tif (err)\n 594:\t\t\t\treturn err;\n 595:\t\t}\n 596:\t\n 597:\t\tif (data_race(!inet_sk(sk)-\u003einet_num) \u0026\u0026 inet_autobind(sk))\n 598:\t\t\treturn -EAGAIN;\n 599:\t\treturn prot-\u003econnect(sk, uaddr, addr_len);\n 600:\t}\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 20517
							output: 21
							thoughts: 61
						

Thoughts:
**Connecting the Dots: Understanding the Socket Connection Process**

Okay, so I'm looking at this connection process, and it seems pretty straightforward at the core. It's calling `prot->connect(sk, uaddr, addr_len)`. The key, as always, is what `prot` actually *is*. This determines the specific connection method.

Ah, yes, for UDP, and only UDP, the `prot->connect` call resolves to one of the following:  `udp_connect`, `udp_v6_connect`, `ip4_datagram_connect`, or `ip6_datagram_connect`. I'd have to trace through to see which one is chosen based on the specifics of the socket (`sk`), the user address (`uaddr`), and the address length (`addr_len`). The address family will, most likely, be the key deciding factor between IPv4 and IPv6 implementations. I'll need to dig deeper into the code that selects the right function pointer, but this provides a good start for tracing the datagram connection process.



20/3 2026/08/07 20:20 tool grepper
0m Args:
{
  "Expression": "ip4_datagram_connect"
}

Results:
{
  "Output": "include/net/ip.h=251=static inline u8 ip_sendmsg_scope(const struct inet_sock *inet,\n--\ninclude/net/ip.h-263-/* datagram.c */\ninclude/net/ip.h:264:int __ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len);\ninclude/net/ip.h:265:int ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len);\ninclude/net/ip.h-266-\n--\nnet/ipv4/datagram.c-18-\nnet/ipv4/datagram.c:19:int __ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len)\nnet/ipv4/datagram.c-20-{\n--\nnet/ipv4/datagram.c-84-}\nnet/ipv4/datagram.c:85:EXPORT_SYMBOL(__ip4_datagram_connect);\nnet/ipv4/datagram.c-86-\nnet/ipv4/datagram.c:87:int ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len)\nnet/ipv4/datagram.c-88-{\n--\nnet/ipv4/datagram.c-91-\tlock_sock(sk);\nnet/ipv4/datagram.c:92:\tres = __ip4_datagram_connect(sk, uaddr, addr_len);\nnet/ipv4/datagram.c-93-\trelease_sock(sk);\n--\nnet/ipv4/datagram.c-95-}\nnet/ipv4/datagram.c:96:EXPORT_SYMBOL(ip4_datagram_connect);\nnet/ipv4/datagram.c-97-\n--\nnet/ipv4/ping.c=288=static int ping_pre_connect(struct sock *sk, struct sockaddr_unsized *uaddr,\n--\nnet/ipv4/ping.c-290-{\nnet/ipv4/ping.c:291:\t/* This check is replicated from __ip4_datagram_connect() and\nnet/ipv4/ping.c-292-\t * intended to prevent BPF program called below from accessing bytes\n--\nnet/ipv4/ping.c=979=struct proto ping_prot = {\n--\nnet/ipv4/ping.c-984-\t.pre_connect =\tping_pre_connect,\nnet/ipv4/ping.c:985:\t.connect =\tip4_datagram_connect,\nnet/ipv4/ping.c-986-\t.disconnect =\t__udp_disconnect,\n--\nnet/ipv4/raw.c=939=struct proto raw_prot = {\n--\nnet/ipv4/raw.c-943-\t.destroy\t   = raw_destroy,\nnet/ipv4/raw.c:944:\t.connect\t   = ip4_datagram_connect,\nnet/ipv4/raw.c-945-\t.disconnect\t   = __udp_disconnect,\n--\nnet/ipv4/udp.c=667=struct sock *__udp4_lib_lookup(const struct net *net, __be32 saddr,\n--\nnet/ipv4/udp.c-719-\t/* Primary hash (destination port) lookup as fallback for this race:\nnet/ipv4/udp.c:720:\t *   1. __ip4_datagram_connect() sets sk_rcv_saddr\nnet/ipv4/udp.c-721-\t *   2. lookup (this function): new sk_rcv_saddr, hashes not updated yet\n--\nnet/ipv4/udp.c=2133=int udp_pre_connect(struct sock *sk, struct sockaddr_unsized *uaddr,\n--\nnet/ipv4/udp.c-2135-{\nnet/ipv4/udp.c:2136:\t/* This check is replicated from __ip4_datagram_connect() and\nnet/ipv4/udp.c-2137-\t * intended to prevent BPF program called below from accessing bytes\n--\nnet/ipv4/udp.c=2146=static int udp_connect(struct sock *sk, struct sockaddr_unsized *uaddr,\n--\nnet/ipv4/udp.c-2151-\tlock_sock(sk);\nnet/ipv4/udp.c:2152:\tres = __ip4_datagram_connect(sk, uaddr, addr_len);\nnet/ipv4/udp.c-2153-\tif (!res)\n--\nnet/ipv6/datagram.c=142=int __ip6_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr,\n--\nnet/ipv6/datagram.c-157-\t\t\treturn -EAFNOSUPPORT;\nnet/ipv6/datagram.c:158:\t\terr = __ip4_datagram_connect(sk, uaddr, addr_len);\nnet/ipv6/datagram.c-159-\t\tgoto ipv4_connected;\n--\nnet/ipv6/datagram.c-196-\nnet/ipv6/datagram.c:197:\t\terr = __ip4_datagram_connect(sk,\nnet/ipv6/datagram.c-198-\t\t\t\t\t     (struct sockaddr_unsized *)\u0026sin,\n--\nnet/l2tp/l2tp_ip.c=332=static int l2tp_ip_connect(struct sock *sk, struct sockaddr_unsized *uaddr,\n--\nnet/l2tp/l2tp_ip.c-352-\nnet/l2tp/l2tp_ip.c:353:\trc = __ip4_datagram_connect(sk, uaddr, addr_len);\nnet/l2tp/l2tp_ip.c-354-\tif (rc \u003c 0)\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 22328
							output: 36
							thoughts: 26
						

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

**Diving into IPv4 Datagram Connection**

Alright, time to dig into the guts of this IPv4 datagram connection process. Let's see... the function in question should be `__ip4_datagram_connect`. I'm going to jump straight into `net/ipv4/datagram.c`. That's where I'd expect to find the core logic for setting up a connectionless socket in the IPv4 context. I'm assuming we are looking at the *internal* function, that is, the one prefixed with `__`. This gives us the best chance of tracking all the internal logic. This is where I can get to the meat of the connection process for datagram sockets, bypassing any potential wrappers or user-space interaction nuances. Let's see what interesting things are happening there... probably socket address validation, routing table lookups, perhaps some interface selection... I'll need to keep an eye out for how it interacts with the routing subsystem and any potential resource allocation within the core network stack. Let's get to work.



22/3 2026/08/07 20:20 tool read-file
0m Args:
{
  "File": "net/ipv4/datagram.c",
  "FirstLine": 19,
  "LineCount": 20
}

Results:
{
  "Contents": "  19:\tint __ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len)\n  20:\t{\n  21:\t\tstruct inet_sock *inet = inet_sk(sk);\n  22:\t\tstruct sockaddr_in *usin = (struct sockaddr_in *) uaddr;\n  23:\t\tstruct flowi4 *fl4;\n  24:\t\tstruct rtable *rt;\n  25:\t\t__be32 saddr;\n  26:\t\tint oif;\n  27:\t\tint err;\n  28:\t\n  29:\t\n  30:\t\tif (addr_len \u003c sizeof(*usin))\n  31:\t\t\treturn -EINVAL;\n  32:\t\n  33:\t\tif (usin-\u003esin_family != AF_INET)\n  34:\t\t\treturn -EAFNOSUPPORT;\n  35:\t\n  36:\t\tsk_dst_reset(sk);\n  37:\t\n  38:\t\toif = sk-\u003esk_bound_dev_if;\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 22906
							output: 20
							thoughts: 1194
						

Thoughts:
**Uninitialized Memory in BPF Context: A Deep Dive**

Okay, let's break this down. First, the initial checks for address lengths in the socket connection process, focusing on `struct sockaddr_in` (16 bytes) and `struct sockaddr_in6` (28 bytes). The code explicitly avoids reading beyond these defined sizes, so the uninitialized portion of the `sa` struct, extending up to 128 bytes, shouldn't be touched during the connection setup. That part seems safe.

Now, the BPF programs. The connection setup involves `BPF_CGROUP_PRE_CONNECT_ENABLED`, leading to `udp_pre_connect` and the eventual BPF program execution with `uaddr` and `addr_len`. The crucial point is that the BPF verifier enforces that the program only accesses memory up to `addr_len`, which is 16 or 28 bytes. So far, so good – no leakage here either.

Let's not forget `bpf_ksock_create_opts`. The `opts` structure is provided by the BPF program, and the verifier ensures initialization. That's a 4-byte structure, no issues.

Now, things get interesting with `bpf_ksock_send`. Here, a BPF program can send data directly to a socket. The data comes from the `data` pointer, and the verifier ensures it is initialized. But wait... what if `data` points to a map value, and that map value contains uninitialized padding? Ah, that's the crux of it! The verifier allows map values with uninitialized padding. And, *that* could be sent to the network, potentially leaking information.

Wait, isn't this already happening in other BPF calls that send network data, like `bpf_skb_store_bytes`? Yes, but `bpf_ksock_send` is different because it directly sends data from the BPF program to a socket.

The fundamental issue is that a map value, with uninitialized memory, could be passed to `bpf_ksock_send`. `kernel_sendmsg` would then read that uninitialized memory and send it out. KMSAN should catch this – it tracks uninitialized memory. So, this feels like a legitimate KMSAN concern.

But hold on! For kfuncs with arguments like `const void *data, u32 data__sz`, the verifier checks that memory is initialized. Okay, so we need to verify how `bpf_ksock_send` interacts with the verifier, but it seems like we *should* be protected, as the kernel verifier should guarantee that initialized memory is being passed.

Now to verify this assertion, and see if it's correct.



24/3 2026/08/07 20:20 tool grepper
0m Args:
{
  "Expression": "check_kfunc_args"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=12077=static bool check_css_task_iter_allowlist(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-12092-\nkernel/bpf/verifier.c:12093:static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,\nkernel/bpf/verifier.c-12094-\t\t\t    int insn_idx)\n--\nkernel/bpf/verifier.c=13021=static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-13105-\t/* Check the arguments */\nkernel/bpf/verifier.c:13106:\terr = check_kfunc_args(env, \u0026meta, insn_idx);\nkernel/bpf/verifier.c-13107-\tif (err \u003c 0)\n--\nkernel/bpf/verifier.c-13394-\t\telse\nkernel/bpf/verifier.c:13395:\t\t\t/* scalar. ensured by check_kfunc_args() */\nkernel/bpf/verifier.c-13396-\t\t\tmark_btf_func_reg_size(env, regno, t-\u003esize);\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 25070
							output: 38
							thoughts: 27
						

Thoughts:
Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:

**Analyzing `check_kfunc_args` and Data Size**

Alright, let's get into the nitty-gritty. My immediate thought is to examine how the `check_kfunc_args` function manages those arguments: `const void *data, u32 data__sz`. I want to really understand how it's handling that `const void *data` pointer and, crucially, the associated size `u32 data__sz`. I'm going to be looking for potential vulnerabilities, inefficiencies, or anything that might lead to unexpected behavior. I'm thinking about potential integer overflows when calculating offsets or sizes, memory access issues if the size is incorrect, and maybe even if it's properly sanitizing the size input. Basically, I want to see how robust and secure this function is in practice. I'll probably be thinking about edge cases and boundary conditions as I analyze this section.



26/3 2026/08/07 20:20 tool read-file
0m Args:
{
  "File": "kernel/bpf/verifier.c",
  "FirstLine": 12093,
  "LineCount": 50
}

Results:
{
  "Contents": "12093:\tstatic int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,\n12094:\t\t\t\t    int insn_idx)\n12095:\t{\n12096:\t\tconst char *func_name = meta-\u003efunc_name, *ref_tname;\n12097:\t\tstruct bpf_func_state *caller = cur_func(env);\n12098:\t\tstruct bpf_reg_state *regs = cur_regs(env);\n12099:\t\tconst struct btf *btf = meta-\u003ebtf;\n12100:\t\tconst struct btf_param *args;\n12101:\t\tstruct btf_record *rec;\n12102:\t\tu32 i, nargs;\n12103:\t\tint ret;\n12104:\t\n12105:\t\targs = (const struct btf_param *)(meta-\u003efunc_proto + 1);\n12106:\t\tnargs = btf_type_vlen(meta-\u003efunc_proto);\n12107:\t\n12108:\t\tret = check_outgoing_stack_args(env, caller, nargs);\n12109:\t\tif (ret)\n12110:\t\t\treturn ret;\n12111:\t\n12112:\t\t/* Check that BTF function arguments match actual types that the\n12113:\t\t * verifier sees.\n12114:\t\t */\n12115:\t\tfor (i = 0; i \u003c nargs; i++) {\n12116:\t\t\tstruct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);\n12117:\t\t\tconst struct btf_type *t, *ref_t, *resolve_ret;\n12118:\t\t\tenum bpf_arg_type arg_type = ARG_DONTCARE;\n12119:\t\t\targno_t argno = argno_from_arg(i + 1);\n12120:\t\t\tint regno = reg_from_argno(argno);\n12121:\t\t\tbool btf_id_fixed_off_ok = true;\n12122:\t\t\tu32 ref_id, type_size;\n12123:\t\t\tint kf_arg_type = meta-\u003efn-\u003earg_type[i];\n12124:\t\n12125:\t\t\tif (is_kfunc_arg_prog_aux(btf, \u0026args[i])) {\n12126:\t\t\t\t/* Reject repeated use bpf_prog_aux */\n12127:\t\t\t\tif (meta-\u003earg_prog) {\n12128:\t\t\t\t\tverifier_bug(env, \"Only 1 prog-\u003eaux argument supported per-kfunc\");\n12129:\t\t\t\t\treturn -EFAULT;\n12130:\t\t\t\t}\n12131:\t\t\t\tif (regno \u003c 0) {\n12132:\t\t\t\t\tverbose(env, \"%s prog-\u003eaux cannot be a stack argument\\n\",\n12133:\t\t\t\t\t\treg_arg_name(env, argno));\n12134:\t\t\t\t\treturn -EINVAL;\n12135:\t\t\t\t}\n12136:\t\t\t\tmeta-\u003earg_prog = true;\n12137:\t\t\t\tcur_aux(env)-\u003earg_prog = regno;\n12138:\t\t\t\tcontinue;\n12139:\t\t\t}\n12140:\t\n12141:\t\t\tif (is_kfunc_arg_ignore(btf, \u0026args[i]) || is_kfunc_arg_implicit(meta, i))\n12142:\t\t\t\tcontinue;\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 26289
							output: 23
							thoughts: 33
						

Thoughts:
Okay, let's dive into this. Here's my thought process on dissecting how those specific arguments are managed:

**Investigating `KF_ARG_PTR_TO_MEM` and `KF_ARG_PTR_TO_MEM_SIZE`**

Alright, I need to get a clear picture of how the system handles `KF_ARG_PTR_TO_MEM` and `KF_ARG_PTR_TO_MEM_SIZE`. I need to understand the nuances of how these specific argument types are interpreted. I'll need to trace the code path for each, looking at how they're processed during argument parsing and validation. I will need to understand the implications of using each one of these arguments. Are there subtle differences in memory management, error handling, or security implications between them? I'm thinking I will need to look at both the kernel-side and user-space interactions. I'll focus on potential vulnerabilities related to buffer overflows or incorrect size calculations that might be present. Let's see if the documentation is helpful or if I need to dig into the assembly. Hopefully the debugging symbols will be sufficient. I really need to get a firm grasp on the underlying mechanisms to make sure they're being utilized safely and effectively.



28/3 2026/08/07 20:20 tool grepper
0m Args:
{
  "Expression": "KF_ARG_PTR_TO_MEM"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=11113=enum kfunc_ptr_arg_type {\n--\nkernel/bpf/verifier.c-11126-\tKF_ARG_PTR_TO_BTF_ID,\t       /* Also covers reg2btf_ids conversions */\nkernel/bpf/verifier.c:11127:\tKF_ARG_PTR_TO_MEM,\nkernel/bpf/verifier.c-11128-\tKF_ARG_PTR_TO_CALLBACK,\n--\nkernel/bpf/verifier.c=11394=get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,\n--\nkernel/bpf/verifier.c-11480-\t\t}\nkernel/bpf/verifier.c:11481:\t\targ_type = KF_ARG_PTR_TO_MEM;\nkernel/bpf/verifier.c-11482-\t} else if (btf_type_is_struct(ref_t))\n--\nkernel/bpf/verifier.c-11496-\t\t}\nkernel/bpf/verifier.c:11497:\t\targ_type = KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;\nkernel/bpf/verifier.c-11498-\t}\n--\nkernel/bpf/verifier.c=12093=static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,\n--\nkernel/bpf/verifier.c-12191-\t\tcase KF_ARG_PTR_TO_RB_NODE:\nkernel/bpf/verifier.c:12192:\t\tcase KF_ARG_PTR_TO_MEM:\nkernel/bpf/verifier.c-12193-\t\tcase KF_ARG_PTR_TO_CALLBACK:\n--\nkernel/bpf/verifier.c-12470-\t\t\t */\nkernel/bpf/verifier.c:12471:\t\t\tkf_arg_type = KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;\nkernel/bpf/verifier.c-12472-\t\t\tfallthrough;\nkernel/bpf/verifier.c:12473:\t\tcase KF_ARG_PTR_TO_MEM:\nkernel/bpf/verifier.c-12474-\t\t\tif (kf_arg_type \u0026 MEM_FIXED_SIZE) {\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 27230
							output: 38
							thoughts: 23
						

Thoughts:
Okay, let's dive into this. Here's my take on those lines of code, presented as a summary of my immediate thought process:

**Diving Deep into the BPF Verifier**

Alright, time to crack open `kernel/bpf/verifier.c` and zero in on roughly line 12473. My mental gears are already starting to turn. I know this verifier is the linchpin of BPF security, so anything in this area is critical. I'm expecting complex logic – likely involving state management, instruction analysis, and certainly checks for potential exploits. I'm assuming that this area contains code relating to some level of optimization, or perhaps mitigation for a security issue which requires more careful validation, as I have familiarity with the code base. I'm prepared to trace function calls and understand how this particular section integrates within the whole verifier system. My knowledge of BPF bytecode is paramount here; I need to quickly relate the C code to the underlying instructions it's inspecting. Let's see what interesting tidbits are hiding in there!



30/3 2026/08/07 20:21 tool read-file
0m Args:
{
  "File": "kernel/bpf/verifier.c",
  "FirstLine": 12465,
  "LineCount": 50
}

Results:
{
  "Contents": "12465:\t\t\t\t}\n12466:\t\n12467:\t\t\t\t/*\n12468:\t\t\t\t * If the register does not contain btf id but the argument type is a pointer to\n12469:\t\t\t\t * scalar-only struct, allow verifying it as a fixed size memory.\n12470:\t\t\t\t */\n12471:\t\t\t\tkf_arg_type = KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;\n12472:\t\t\t\tfallthrough;\n12473:\t\t\tcase KF_ARG_PTR_TO_MEM:\n12474:\t\t\t\tif (kf_arg_type \u0026 MEM_FIXED_SIZE) {\n12475:\t\t\t\t\tresolve_ret = btf_resolve_size(btf, ref_t, \u0026type_size);\n12476:\t\t\t\t\tif (IS_ERR(resolve_ret)) {\n12477:\t\t\t\t\t\tverbose(env, \"%s reference type('%s %s') size cannot be determined: %ld\\n\",\n12478:\t\t\t\t\t\t\treg_arg_name(env, argno), btf_type_str(ref_t),\n12479:\t\t\t\t\t\t\tref_tname, PTR_ERR(resolve_ret));\n12480:\t\t\t\t\t\treturn -EINVAL;\n12481:\t\t\t\t\t}\n12482:\t\t\t\t\tret = check_mem_reg(env, reg, argno, type_size, BPF_READ | BPF_WRITE, meta);\n12483:\t\t\t\t\tif (ret \u003c 0)\n12484:\t\t\t\t\t\treturn ret;\n12485:\t\t\t\t}\n12486:\t\t\t\tbreak;\n12487:\t\t\tcase KF_ARG_CONST_MEM_SIZE:\n12488:\t\t\t\tret = process_const_arg(env, reg, argno, meta);\n12489:\t\t\t\tif (ret \u003c 0)\n12490:\t\t\t\t\treturn ret;\n12491:\t\t\t\tfallthrough;\n12492:\t\t\tcase KF_ARG_MEM_SIZE:\n12493:\t\t\t{\n12494:\t\t\t\tstruct bpf_reg_state *buff_reg = get_func_arg_reg(caller, regs, i - 1);\n12495:\t\t\t\tstruct bpf_reg_state *size_reg = reg;\n12496:\t\t\t\targno_t buff_argno = argno_from_arg(i);\n12497:\t\n12498:\t\t\t\tif (reg-\u003etype != SCALAR_VALUE) {\n12499:\t\t\t\t\tverbose(env, \"%s is not a scalar\\n\", reg_arg_name(env, argno));\n12500:\t\t\t\t\treturn -EINVAL;\n12501:\t\t\t\t}\n12502:\t\n12503:\t\t\t\tif (bpf_register_is_null(buff_reg))\n12504:\t\t\t\t\tbreak;\n12505:\t\n12506:\t\t\t\tret = check_mem_size_reg(env, buff_reg, size_reg, buff_argno, argno,\n12507:\t\t\t\t\t\t\t BPF_READ | BPF_WRITE, true, meta);\n12508:\t\t\t\tif (ret \u003c 0) {\n12509:\t\t\t\t\tverbose(env, \"%s and \", reg_arg_name(env, buff_argno));\n12510:\t\t\t\t\tverbose(env, \"%s memory, len pair leads to invalid memory access\\n\",\n12511:\t\t\t\t\t\treg_arg_name(env, argno));\n12512:\t\t\t\t\treturn ret;\n12513:\t\t\t\t}\n12514:\t\t\t\tbreak;\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 28439
							output: 21
							thoughts: 105
						

Thoughts:
**Analyzing Memory Access in BPF Programs**

Okay, so I'm looking at this function, `check_mem_size_reg`, and it's being called with `BPF_READ | BPF_WRITE`. Alright, that immediately tells me that whatever memory region this is referencing, it's going to be both read from and written to by the BPF program. That's the basic interpretation of those flags. But hold on a second... since we are reading and writing, doesn't that inherently imply something about the initialization state of the memory? Do these operations have implications for uninitialized memory? 

Let me trace further: `check_mem_size_reg` calls `check_helper_mem_access`, which sounds like the logical place to delve deeper into these checks. I should understand what's really happening during these memory accesses, including how memory initialization is handled or validated. I'll need to examine `check_helper_mem_access` now to get a clearer picture of the constraints and safety mechanisms in place.



32/3 2026/08/07 20:21 tool grepper
0m Args:
{
  "Expression": "check_helper_mem_access"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=6701=static int check_stack_range_initialized(\n--\nkernel/bpf/verifier.c-6821-\nkernel/bpf/verifier.c:6822:static int check_helper_mem_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\nkernel/bpf/verifier.c-6823-\t\t\t\t   int access_size, enum bpf_access_type access_type,\n--\nkernel/bpf/verifier.c=6912=static int check_mem_size_reg(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-6957-\tif (access_type \u0026 BPF_READ)\nkernel/bpf/verifier.c:6958:\t\terr = check_helper_mem_access(env, mem_reg, mem_argno, reg_umax(size_reg),\nkernel/bpf/verifier.c-6959-\t\t\t\t\t      BPF_READ, zero_size_allowed, meta);\nkernel/bpf/verifier.c-6960-\tif (!err \u0026\u0026 access_type \u0026 BPF_WRITE)\nkernel/bpf/verifier.c:6961:\t\terr = check_helper_mem_access(env, mem_reg, mem_argno, reg_umax(size_reg),\nkernel/bpf/verifier.c-6962-\t\t\t\t\t      BPF_WRITE, zero_size_allowed, meta);\n--\nkernel/bpf/verifier.c=6976=static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\n--\nkernel/bpf/verifier.c-6997-\tif (access_type \u0026 BPF_READ)\nkernel/bpf/verifier.c:6998:\t\terr = check_helper_mem_access(env, reg, argno, size, BPF_READ, true, meta);\nkernel/bpf/verifier.c-6999-\tif (!err \u0026\u0026 (access_type \u0026 BPF_WRITE))\nkernel/bpf/verifier.c:7000:\t\terr = check_helper_mem_access(env, reg, argno, size, BPF_WRITE, true, meta);\nkernel/bpf/verifier.c-7001-\n--\nkernel/bpf/verifier.c=8364=static int check_func_arg(struct bpf_verifier_env *env, u32 arg,\n--\nkernel/bpf/verifier.c-8457-\t\tkey_size = meta-\u003emap.ptr-\u003ekey_size;\nkernel/bpf/verifier.c:8458:\t\terr = check_helper_mem_access(env, reg, argno, key_size, BPF_READ, false, NULL);\nkernel/bpf/verifier.c-8459-\t\tif (err)\n--\nkernel/bpf/verifier.c-8492-\nkernel/bpf/verifier.c:8493:\t\terr = check_helper_mem_access(env, reg, argno, meta-\u003emap.ptr-\u003evalue_size,\nkernel/bpf/verifier.c-8494-\t\t\t\t\t      arg_type \u0026 MEM_WRITE ? BPF_WRITE : BPF_READ,\n--\nkernel/bpf/verifier.c=11394=get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,\n--\nkernel/bpf/verifier.c-11487-\t\t * Otherwise this is a fixed-size memory buffer supported by\nkernel/bpf/verifier.c:11488:\t\t * check_helper_mem_access(): a pointer to a scalar or a struct of\nkernel/bpf/verifier.c-11489-\t\t * scalars. The access size is derived from the pointed-to BTF type.\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 29760
							output: 37
							thoughts: 299
						

Thoughts:
**Analyzing Memory Access Checks in eBPF KFuncs**

Okay, let's break this down. In `check_mem_size_reg`, I see a call to `check_helper_mem_access` with `BPF_READ` and `BPF_WRITE` flags. Now, I'm thinking about `bpf_ksock_send`, and that `const void *data` argument. Does the verifier actually *understand* that this is `const void *` and effectively only check for `BPF_READ` access, and then what is being read is initialized?

Digging deeper into `check_kfunc_args`, I see this in the `KF_ARG_MEM_SIZE` case. I'm seeing it passes `BPF_READ | BPF_WRITE` unconditionally to `check_mem_size_reg`! That seems odd. If it is passing both `BPF_READ | BPF_WRITE`, then the verifier *is* checking for both read and write access.

If it's actually checking for `BPF_WRITE`, then that implies the kfunc *is* allowed to write to that memory. But... `data` is declared as `const void *`. That's a contradiction.

So, the key here is how `check_helper_mem_access` handles the `BPF_READ` flag. Does it *only* check for initialization, effectively ignoring write permissions for a `const` pointer, or is there a bug? Yes, when `BPF_READ` is set, it checks that the memory is initialized. This is a common and important check.

Time to look into `check_helper_mem_access` and understand the initialization check.



34/3 2026/08/07 20:21 tool read-file
0m Args:
{
  "File": "kernel/bpf/verifier.c",
  "FirstLine": 6822,
  "LineCount": 50
}

Results:
{
  "Contents": "6822:\tstatic int check_helper_mem_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\n6823:\t\t\t\t\t   int access_size, enum bpf_access_type access_type,\n6824:\t\t\t\t\t   bool zero_size_allowed,\n6825:\t\t\t\t\t   struct bpf_call_arg_meta *meta)\n6826:\t{\n6827:\t\tstruct bpf_reg_state *regs = cur_regs(env);\n6828:\t\tu32 *max_access;\n6829:\t\n6830:\t\tswitch (base_type(reg-\u003etype)) {\n6831:\t\tcase PTR_TO_PACKET:\n6832:\t\tcase PTR_TO_PACKET_META:\n6833:\t\t\treturn check_packet_access(env, reg, argno, 0, access_size,\n6834:\t\t\t\t\t\t   zero_size_allowed);\n6835:\t\tcase PTR_TO_MAP_KEY:\n6836:\t\t\tif (access_type == BPF_WRITE) {\n6837:\t\t\t\tverbose(env, \"%s cannot write into %s\\n\",\n6838:\t\t\t\t\treg_arg_name(env, argno), reg_type_str(env, reg-\u003etype));\n6839:\t\t\t\treturn -EACCES;\n6840:\t\t\t}\n6841:\t\t\treturn check_mem_region_access(env, reg, argno, 0, access_size,\n6842:\t\t\t\t\t\t       reg-\u003emap_ptr-\u003ekey_size, false);\n6843:\t\tcase PTR_TO_MAP_VALUE:\n6844:\t\t\tif (check_map_access_type(env, reg, 0, access_size, access_type))\n6845:\t\t\t\treturn -EACCES;\n6846:\t\t\treturn check_map_access(env, reg, argno, 0, access_size,\n6847:\t\t\t\t\t\tzero_size_allowed, ACCESS_HELPER);\n6848:\t\tcase PTR_TO_MEM:\n6849:\t\t\tif (type_is_rdonly_mem(reg-\u003etype)) {\n6850:\t\t\t\tif (access_type == BPF_WRITE) {\n6851:\t\t\t\t\tverbose(env, \"%s cannot write into %s\\n\",\n6852:\t\t\t\t\t\treg_arg_name(env, argno), reg_type_str(env, reg-\u003etype));\n6853:\t\t\t\t\treturn -EACCES;\n6854:\t\t\t\t}\n6855:\t\t\t}\n6856:\t\t\treturn check_mem_region_access(env, reg, argno, 0,\n6857:\t\t\t\t\t\t       access_size, reg-\u003emem_size,\n6858:\t\t\t\t\t\t       zero_size_allowed);\n6859:\t\tcase PTR_TO_BUF:\n6860:\t\t\tif (type_is_rdonly_mem(reg-\u003etype)) {\n6861:\t\t\t\tif (access_type == BPF_WRITE) {\n6862:\t\t\t\t\tverbose(env, \"%s cannot write into %s\\n\",\n6863:\t\t\t\t\t\treg_arg_name(env, argno), reg_type_str(env, reg-\u003etype));\n6864:\t\t\t\t\treturn -EACCES;\n6865:\t\t\t\t}\n6866:\t\n6867:\t\t\t\tmax_access = \u0026env-\u003eprog-\u003eaux-\u003emax_rdonly_access;\n6868:\t\t\t} else {\n6869:\t\t\t\tmax_access = \u0026env-\u003eprog-\u003eaux-\u003emax_rdwr_access;\n6870:\t\t\t}\n6871:\t\t\treturn check_buffer_access(env, reg, argno, 0,\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 31413
							output: 19
							thoughts: 35
						

Thoughts:
Okay, let's see. My first thought is: **Hold on a second, that `check_map_access` function... doesn't it *only* handle access after the map is initialized? Doesn't it skip that crucial first step?**

I need to confirm that. My next step has to be to take a very close look at the implementation of `check_map_access`. I need to understand its behavior thoroughly to confirm my initial thought.



36/3 2026/08/07 20:21 tool grepper
0m Args:
{
  "Expression": "check_map_access"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=4205=static struct bpf_reg_state *get_func_arg_reg(struct bpf_func_state *caller,\n--\nkernel/bpf/verifier.c-4213-\nkernel/bpf/verifier.c:4214:static int check_map_access_type(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\nkernel/bpf/verifier.c-4215-\t\t\t\t int off, int size, enum bpf_access_type type)\n--\nkernel/bpf/verifier.c=4548=static int check_map_kptr_access(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-4556-\nkernel/bpf/verifier.c:4557:\t/* Things we already checked for in check_map_access and caller:\nkernel/bpf/verifier.c-4558-\t *  - Reject cases where variable offset may touch kptr\n--\nkernel/bpf/verifier.c=4616=static u32 map_mem_size(const struct bpf_map *map)\n--\nkernel/bpf/verifier.c-4624-/* check read/write into a map element with possible variable offset */\nkernel/bpf/verifier.c:4625:static int check_map_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\nkernel/bpf/verifier.c-4626-\t\t\t    int off, int size, bool zero_size_allowed,\n--\nkernel/bpf/verifier.c=6172=static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct bpf_reg_state *reg, argno_t argno,\n--\nkernel/bpf/verifier.c-6207-\t\t}\nkernel/bpf/verifier.c:6208:\t\terr = check_map_access_type(env, reg, off, size, t);\nkernel/bpf/verifier.c-6209-\t\tif (err)\nkernel/bpf/verifier.c-6210-\t\t\treturn err;\nkernel/bpf/verifier.c:6211:\t\terr = check_map_access(env, reg, argno, off, size, false, ACCESS_DIRECT);\nkernel/bpf/verifier.c-6212-\t\tif (err)\n--\nkernel/bpf/verifier.c=6822=static int check_helper_mem_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\n--\nkernel/bpf/verifier.c-6843-\tcase PTR_TO_MAP_VALUE:\nkernel/bpf/verifier.c:6844:\t\tif (check_map_access_type(env, reg, 0, access_size, access_type))\nkernel/bpf/verifier.c-6845-\t\t\treturn -EACCES;\nkernel/bpf/verifier.c:6846:\t\treturn check_map_access(env, reg, argno, 0, access_size,\nkernel/bpf/verifier.c-6847-\t\t\t\t\tzero_size_allowed, ACCESS_HELPER);\n--\nkernel/bpf/verifier.c=8209=static int check_arg_const_str(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-8241-\nkernel/bpf/verifier.c:8242:\terr = check_map_access(env, reg, argno, 0,\nkernel/bpf/verifier.c-8243-\t\t\t       map-\u003evalue_size - reg-\u003evar_off.value, false,\n--\nkernel/bpf/verifier.c=10298=static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-10565-\t\tmark_reg_known_zero(env, regs, BPF_REG_0);\nkernel/bpf/verifier.c:10566:\t\t/* remember map_ptr, so that check_map_access()\nkernel/bpf/verifier.c-10567-\t\t * can check 'value_size' boundary of memory access\n--\nkernel/bpf/verifier.c=13747=static int sanitize_check_bounds(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-13765-\tcase PTR_TO_MAP_VALUE:\nkernel/bpf/verifier.c:13766:\t\tif (check_map_access(env, dst_reg, argno_from_reg(dst), 0, 1, false, ACCESS_HELPER)) {\nkernel/bpf/verifier.c-13767-\t\t\tverbose(env, \"R%d pointer arithmetic of map value goes out of range, \"\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 32813
							output: 21
							thoughts: 351
						

Thoughts:
**Uninitialized Memory Leak in BPF Programs**

Okay, so I'm realizing that `check_map_access` doesn't account for whether the *contents* of a map value are initialized, just that the map itself has been allocated. That's a problem. If a BPF program creates a map value, maybe with some fields left uninitialized, and then passes it to `bpf_ksock_send`, the kernel is going to read those potentially garbage bytes and send them over the network. That's a clear information leak.

Is this limited to `bpf_ksock_send`? I immediately think of `bpf_skb_store_bytes`. It also copies data, writes directly to an SKB. Ah, but that one takes `const void *from, u32 len`. So, if `from` is a map value, it could absolutely leak uninitialized memory through that function as well.

But, focusing on `bpf_ksock_send`, that's a new kfunc for sending data over a socket. It seems like the directness of it makes this leak more concerning, right? The question is, how do we catch this?

This is where KMSAN comes in. KMSAN is *designed* to find exactly this type of uninitialized memory read. If `bpf_ksock_send` is actually allowing us to send uninitialized data from the stack or map values, KMSAN will nail it.

Now, does the verifier offer any protection? Wait, yes, the verifier *does* track stack initialization. If a stack variable is passed to `bpf_ksock_send`, the verifier checks it has been initialized. But what about *padding* within a stack variable? The verifier expects the entire stack range passed to a helper/kfunc to be fully initialized. Okay, let's go check `check_stack_range_initialized` to verify this.



38/3 2026/08/07 20:21 tool grepper
0m Args:
{
  "Expression": "check_stack_range_initialized"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=3967=enum bpf_access_src {\n--\nkernel/bpf/verifier.c-3971-\nkernel/bpf/verifier.c:3972:static int check_stack_range_initialized(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\nkernel/bpf/verifier.c-3973-\t\t\t\t\t argno_t argno, int off, int access_size,\n--\nkernel/bpf/verifier.c=3996=static int check_stack_read_var_off(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\n--\nkernel/bpf/verifier.c-4004-\t */\nkernel/bpf/verifier.c:4005:\terr = check_stack_range_initialized(env, reg, ptr_argno, off, size,\nkernel/bpf/verifier.c-4006-\t\t\t\t\t    false, BPF_READ, NULL);\n--\nkernel/bpf/verifier.c=6658=static int check_atomic(struct bpf_verifier_env *env, struct bpf_insn *insn)\n--\nkernel/bpf/verifier.c-6700- */\nkernel/bpf/verifier.c:6701:static int check_stack_range_initialized(\nkernel/bpf/verifier.c-6702-\t\tstruct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno, int off,\n--\nkernel/bpf/verifier.c=6822=static int check_helper_mem_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\n--\nkernel/bpf/verifier.c-6874-\tcase PTR_TO_STACK:\nkernel/bpf/verifier.c:6875:\t\treturn check_stack_range_initialized(\nkernel/bpf/verifier.c-6876-\t\t\t\tenv, reg,\n--\nkernel/bpf/verifier.c=7782=static bool arg_type_is_raw_mem(enum bpf_arg_type type)\n--\nkernel/bpf/verifier.c-7786-\t * (uninitialized) memory argument, and like ARG_PTR_TO_MEM it may be\nkernel/bpf/verifier.c:7787:\t * passed as a PTR_TO_STACK that reaches check_stack_range_initialized().\nkernel/bpf/verifier.c-7788-\t */\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 34242
							output: 17
							thoughts: 755
						

Thoughts:
**Uninitialized Memory in `bpf_ksock_connect`**

Okay, I've confirmed that `check_stack_range_initialized` handles stack memory initialization, generally. But I'm now looking closely at `bpf_ksock_create` specifically, and the `err__uninit` argument. It looks like it's an uninitialized memory argument, but it's only *written* to, so no issues there.

Now, on to `bpf_ksock_connect`. Here, we have a similar situation, but the potential for a deeper issue. `addr` is a `const union bpf_ksock_addr *`, and we `memcpy` it into the stack variable `sa`, a `struct sockaddr_storage`. This copies 28 bytes into a 128-byte struct. The crucial point is that the remaining 100 bytes of `sa` are *uninitialized*.

The flow is: we copy the `addr` data into `sa`, determine `addrlen` based on `sa.ss_family` (either 16 for IPv4 or a larger value for IPv6), and then call `connect_socket`. This calls `security_socket_connect` and the socket's `connect` operation, both with `&sa` and `addrlen`.

My concern is: do these functions, particularly the security checks, read *beyond* the specified `addrlen`? Ordinarily, they shouldn't. However, what if a Loadable Kernel Module (LSM) hook, for example, casts the `struct sockaddr *` to a `struct sockaddr_storage *` and reads the *entire* struct? This would expose the uninitialized memory on the stack! The security hook is passed the full address and the length.

So, the critical question is: are there any LSM hooks that potentially do this and might therefore be vulnerable to reading the uninitialized stack memory? I'll need to check the code for any LSM hooks that might access beyond the specified `addrlen` when handling `socket_connect`.



40/3 2026/08/07 20:21 tool grepper
0m Args:
{
  "Expression": "socket_connect"
}

Results:
{
  "Output": "include/linux/lsm_hook_defs.h=338=LSM_HOOK(int, 0, socket_bind, struct socket *sock, struct sockaddr *address,\ninclude/linux/lsm_hook_defs.h-339-\t int addrlen)\ninclude/linux/lsm_hook_defs.h:340:LSM_HOOK(int, 0, socket_connect, struct socket *sock, struct sockaddr *address,\ninclude/linux/lsm_hook_defs.h-341-\t int addrlen)\n--\ninclude/linux/security.h=1672=int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen);\ninclude/linux/security.h:1673:int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen);\ninclude/linux/security.h-1674-int security_socket_listen(struct socket *sock, int backlog);\n--\ninclude/linux/security.h=1759=static inline int security_socket_bind(struct socket *sock,\n--\ninclude/linux/security.h-1765-\ninclude/linux/security.h:1766:static inline int security_socket_connect(struct socket *sock,\ninclude/linux/security.h-1767-\t\t\t\t\t  struct sockaddr *address,\n--\ninclude/trace/events/sunrpc.h=950=DEFINE_RPC_SOCKET_EVENT(rpc_socket_state_change);\ninclude/trace/events/sunrpc.h:951:DEFINE_RPC_SOCKET_EVENT_DONE(rpc_socket_connect);\ninclude/trace/events/sunrpc.h-952-DEFINE_RPC_SOCKET_EVENT_DONE(rpc_socket_error);\n--\nkernel/bpf/bpf_lsm.c=365=BTF_ID(func, bpf_lsm_socket_bind)\nkernel/bpf/bpf_lsm.c:366:BTF_ID(func, bpf_lsm_socket_connect)\nkernel/bpf/bpf_lsm.c-367-BTF_ID(func, bpf_lsm_socket_create)\n--\nnet/phonet/socket.c=202=static int pn_socket_autobind(struct socket *sock)\n--\nnet/phonet/socket.c-222-\nnet/phonet/socket.c:223:static int pn_socket_connect(struct socket *sock, struct sockaddr_unsized *addr,\nnet/phonet/socket.c-224-\t\t\t     int len, int flags)\n--\nnet/phonet/socket.c=451=const struct proto_ops phonet_stream_ops = {\n--\nnet/phonet/socket.c-455-\t.bind\t\t= pn_socket_bind,\nnet/phonet/socket.c:456:\t.connect\t= pn_socket_connect,\nnet/phonet/socket.c-457-\t.socketpair\t= sock_no_socketpair,\n--\nnet/socket.c=2106=int connect_socket(struct socket *sock, struct sockaddr_storage *address,\n--\nnet/socket.c-2110-\nnet/socket.c:2111:\terr = security_socket_connect(sock, (struct sockaddr *)address, addrlen);\nnet/socket.c-2112-\tif (err)\n--\nnet/sunrpc/xprtsock.c=2015=static int xs_local_setup_socket(struct sock_xprt *transport)\n--\nnet/sunrpc/xprtsock.c-2041-\tstatus = xs_local_finish_connecting(xprt, sock);\nnet/sunrpc/xprtsock.c:2042:\ttrace_rpc_socket_connect(xprt, sock, status);\nnet/sunrpc/xprtsock.c-2043-\tswitch (status) {\n--\nnet/sunrpc/xprtsock.c=2213=static void xs_udp_setup_socket(struct work_struct *work)\n--\nnet/sunrpc/xprtsock.c-2236-\txs_udp_finish_connecting(xprt, sock);\nnet/sunrpc/xprtsock.c:2237:\ttrace_rpc_socket_connect(xprt, sock, 0);\nnet/sunrpc/xprtsock.c-2238-\tstatus = 0;\n--\nnet/sunrpc/xprtsock.c=2418=static void xs_tcp_setup_socket(struct work_struct *work)\n--\nnet/sunrpc/xprtsock.c-2450-\tstatus = xs_tcp_finish_connecting(xprt, sock);\nnet/sunrpc/xprtsock.c:2451:\ttrace_rpc_socket_connect(xprt, sock, status);\nnet/sunrpc/xprtsock.c-2452-\tdprintk(\"RPC:       %p connect status %d connected %d sock state %d\\n\",\n--\nnet/sunrpc/xprtsock.c=2686=static void xs_tcp_tls_setup_socket(struct work_struct *work)\n--\nnet/sunrpc/xprtsock.c-2752-\txprt_release_write(lower_xprt, NULL);\nnet/sunrpc/xprtsock.c:2753:\ttrace_rpc_socket_connect(upper_xprt, upper_transport-\u003esock, 0);\nnet/sunrpc/xprtsock.c-2754-\trpc_shutdown_client(lower_clnt);\n--\nsecurity/apparmor/lsm.c=1354=static int apparmor_socket_bind(struct socket *sock,\n--\nsecurity/apparmor/lsm.c-1366-\nsecurity/apparmor/lsm.c:1367:static int apparmor_socket_connect(struct socket *sock,\nsecurity/apparmor/lsm.c-1368-\t\t\t\t   struct sockaddr *address, int addrlen)\n--\nsecurity/apparmor/lsm.c=1670=static struct security_hook_list apparmor_hooks[] __ro_after_init = {\n--\nsecurity/apparmor/lsm.c-1718-\tLSM_HOOK_INIT(socket_bind, apparmor_socket_bind),\nsecurity/apparmor/lsm.c:1719:\tLSM_HOOK_INIT(socket_connect, apparmor_socket_connect),\nsecurity/apparmor/lsm.c-1720-\tLSM_HOOK_INIT(socket_listen, apparmor_socket_listen),\n--\nsecurity/landlock/net.c=301=static int hook_socket_bind(struct socket *const sock,\n--\nsecurity/landlock/net.c-316-\nsecurity/landlock/net.c:317:static int hook_socket_connect(struct socket *const sock,\nsecurity/landlock/net.c-318-\t\t\t       struct sockaddr *const address,\n--\nsecurity/landlock/net.c=377=static struct security_hook_list landlock_hooks[] __ro_after_init = {\nsecurity/landlock/net.c-378-\tLSM_HOOK_INIT(socket_bind, hook_socket_bind),\nsecurity/landlock/net.c:379:\tLSM_HOOK_INIT(socket_connect, hook_socket_connect),\nsecurity/landlock/net.c-380-\tLSM_HOOK_INIT(socket_sendmsg, hook_socket_sendmsg),\n--\nsecurity/security.c=4248=int security_socket_bind(struct socket *sock,\n--\nsecurity/security.c-4254-/**\nsecurity/security.c:4255: * security_socket_connect() - Check if a socket connect operation is allowed\nsecurity/security.c-4256- * @sock: socket\n--\nsecurity/security.c-4264- */\nsecurity/security.c:4265:int security_socket_connect(struct socket *sock,\nsecurity/security.c-4266-\t\t\t    struct sockaddr *address, int addrlen)\nsecurity/security.c-4267-{\nsecurity/security.c:4268:\treturn call_int_hook(socket_connect, sock, address, addrlen);\nsecurity/security.c-4269-}\n--\nsecurity/selinux/hooks.c=5134=static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)\n--\nsecurity/selinux/hooks.c-5141- */\nsecurity/selinux/hooks.c:5142:static int selinux_socket_connect_helper(struct sock *sk,\nsecurity/selinux/hooks.c-5143-\t\t\t\t\t struct sockaddr *address, int addrlen)\n--\nsecurity/selinux/hooks.c-5225-\nsecurity/selinux/hooks.c:5226:/* Supports connect(2), see comments in selinux_socket_connect_helper() */\nsecurity/selinux/hooks.c:5227:static int selinux_socket_connect(struct socket *sock,\nsecurity/selinux/hooks.c-5228-\t\t\t\t  struct sockaddr *address, int addrlen)\n--\nsecurity/selinux/hooks.c-5232-\nsecurity/selinux/hooks.c:5233:\terr = selinux_socket_connect_helper(sk, address, addrlen);\nsecurity/selinux/hooks.c-5234-\tif (err)\n--\nsecurity/selinux/hooks.c-5236-\nsecurity/selinux/hooks.c:5237:\treturn selinux_netlbl_socket_connect(sk, address);\nsecurity/selinux/hooks.c-5238-}\n--\nsecurity/selinux/hooks.c=5271=static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,\n--\nsecurity/selinux/hooks.c-5285-\t      sock-\u003esk-\u003esk_protocol == IPPROTO_MPTCP))) {\nsecurity/selinux/hooks.c:5286:\t\trc = selinux_socket_connect(sock, addr, addrlen);\nsecurity/selinux/hooks.c-5287-\t\tif (rc)\n--\nsecurity/selinux/hooks.c=5728=static int selinux_sctp_bind_connect(struct sock *sk, int optname,\n--\nsecurity/selinux/hooks.c-5774-\t\tcase SCTP_SENDMSG_CONNECT:\nsecurity/selinux/hooks.c:5775:\t\t\terr = selinux_socket_connect_helper(sk, addr, len);\nsecurity/selinux/hooks.c-5776-\t\t\tif (err)\n--\nsecurity/selinux/hooks.c-5780-\t\t\t * SCTP protocol layer, the socket is already locked,\nsecurity/selinux/hooks.c:5781:\t\t\t * therefore selinux_netlbl_socket_connect_locked()\nsecurity/selinux/hooks.c-5782-\t\t\t * is called here. The situations handled are:\n--\nsecurity/selinux/hooks.c-5787-\t\t\t * the SCTP protocol layer and is handled via\nsecurity/selinux/hooks.c:5788:\t\t\t * selinux_socket_connect().\nsecurity/selinux/hooks.c-5789-\t\t\t */\nsecurity/selinux/hooks.c:5790:\t\t\terr = selinux_netlbl_socket_connect_locked(sk, addr);\nsecurity/selinux/hooks.c-5791-\t\t\tbreak;\n--\nsecurity/selinux/hooks.c=7553=static struct security_hook_list selinux_hooks[] __ro_after_init = {\n--\nsecurity/selinux/hooks.c-7708-\tLSM_HOOK_INIT(socket_bind, selinux_socket_bind),\nsecurity/selinux/hooks.c:7709:\tLSM_HOOK_INIT(socket_connect, selinux_socket_connect),\nsecurity/selinux/hooks.c-7710-\tLSM_HOOK_INIT(socket_listen, selinux_socket_listen),\n--\nsecurity/selinux/include/netlabel.h=47=int selinux_netlbl_socket_setsockopt(struct socket *sock, int level,\nsecurity/selinux/include/netlabel.h-48-\t\t\t\t     int optname);\nsecurity/selinux/include/netlabel.h:49:int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr);\nsecurity/selinux/include/netlabel.h:50:int selinux_netlbl_socket_connect_locked(struct sock *sk,\nsecurity/selinux/include/netlabel.h-51-\t\t\t\t\t struct sockaddr *addr);\n--\nsecurity/selinux/include/netlabel.h=120=static inline int selinux_netlbl_socket_setsockopt(struct socket *sock,\n--\nsecurity/selinux/include/netlabel.h-124-}\nsecurity/selinux/include/netlabel.h:125:static inline int selinux_netlbl_socket_connect(struct sock *sk,\nsecurity/selinux/include/netlabel.h-126-\t\t\t\t\t\tstruct sockaddr *addr)\n--\nsecurity/selinux/include/netlabel.h-129-}\nsecurity/selinux/include/netlabel.h:130:static inline int selinux_netlbl_socket_connect_locked(struct sock *sk,\nsecurity/selinux/include/netlabel.h-131-\t\t\t\t\t\t       struct sockaddr *addr)\n--\nsecurity/selinux/netlabel.c=509=int selinux_netlbl_socket_setsockopt(struct socket *sock,\n--\nsecurity/selinux/netlabel.c-538-/**\nsecurity/selinux/netlabel.c:539: * selinux_netlbl_socket_connect_helper - Help label a client-side socket on\nsecurity/selinux/netlabel.c-540- * connect\n--\nsecurity/selinux/netlabel.c-548- */\nsecurity/selinux/netlabel.c:549:static int selinux_netlbl_socket_connect_helper(struct sock *sk,\nsecurity/selinux/netlabel.c-550-\t\t\t\t\t\tstruct sockaddr *addr)\n--\nsecurity/selinux/netlabel.c-576-/**\nsecurity/selinux/netlabel.c:577: * selinux_netlbl_socket_connect_locked - Label a client-side socket on\nsecurity/selinux/netlabel.c-578- * connect\n--\nsecurity/selinux/netlabel.c-587- */\nsecurity/selinux/netlabel.c:588:int selinux_netlbl_socket_connect_locked(struct sock *sk,\nsecurity/selinux/netlabel.c-589-\t\t\t\t\t struct sockaddr *addr)\n--\nsecurity/selinux/netlabel.c-596-\nsecurity/selinux/netlabel.c:597:\treturn selinux_netlbl_socket_connect_helper(sk, addr);\nsecurity/selinux/netlabel.c-598-}\n--\nsecurity/selinux/netlabel.c-600-/**\nsecurity/selinux/netlabel.c:601: * selinux_netlbl_socket_connect - Label a client-side socket on connect\nsecurity/selinux/netlabel.c-602- * @sk: the socket to label\n--\nsecurity/selinux/netlabel.c-609- */\nsecurity/selinux/netlabel.c:610:int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)\nsecurity/selinux/netlabel.c-611-{\n--\nsecurity/selinux/netlabel.c-614-\tlock_sock(sk);\nsecurity/selinux/netlabel.c:615:\trc = selinux_netlbl_socket_connect_locked(sk, addr);\nsecurity/selinux/netlabel.c-616-\trelease_sock(sk);\n--\nsecurity/smack/smack_lsm.c=3040=static int smack_socket_bind(struct socket *sock, struct sockaddr *address,\n--\nsecurity/smack/smack_lsm.c-3053-/**\nsecurity/smack/smack_lsm.c:3054: * smack_socket_connect - connect access check\nsecurity/smack/smack_lsm.c-3055- * @sock: the socket\n--\nsecurity/smack/smack_lsm.c-3062- */\nsecurity/smack/smack_lsm.c:3063:static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,\nsecurity/smack/smack_lsm.c-3064-\t\t\t\tint addrlen)\n--\nsecurity/smack/smack_lsm.c=5124=static struct security_hook_list smack_hooks[] __ro_after_init = {\n--\nsecurity/smack/smack_lsm.c-5233-#endif\nsecurity/smack/smack_lsm.c:5234:\tLSM_HOOK_INIT(socket_connect, smack_socket_connect),\nsecurity/smack/smack_lsm.c-5235-\tLSM_HOOK_INIT(socket_sendmsg, smack_socket_sendmsg),\n--\nsecurity/tomoyo/common.h=1026=int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,\nsecurity/tomoyo/common.h-1027-\t\t\t\t  int addr_len);\nsecurity/tomoyo/common.h:1028:int tomoyo_socket_connect_permission(struct socket *sock,\nsecurity/tomoyo/common.h-1029-\t\t\t\t     struct sockaddr *addr, int addr_len);\n--\nsecurity/tomoyo/network.c=653=int tomoyo_socket_listen_permission(struct socket *sock)\n--\nsecurity/tomoyo/network.c-680-/**\nsecurity/tomoyo/network.c:681: * tomoyo_socket_connect_permission - Check permission for setting the remote address of a socket.\nsecurity/tomoyo/network.c-682- *\n--\nsecurity/tomoyo/network.c-688- */\nsecurity/tomoyo/network.c:689:int tomoyo_socket_connect_permission(struct socket *sock,\nsecurity/tomoyo/network.c-690-\t\t\t\t     struct sockaddr *addr, int addr_len)\n--\nsecurity/tomoyo/tomoyo.c=454=static int tomoyo_socket_listen(struct socket *sock, int backlog)\n--\nsecurity/tomoyo/tomoyo.c-459-/**\nsecurity/tomoyo/tomoyo.c:460: * tomoyo_socket_connect - Check permission for connect().\nsecurity/tomoyo/tomoyo.c-461- *\n--\nsecurity/tomoyo/tomoyo.c-467- */\nsecurity/tomoyo/tomoyo.c:468:static int tomoyo_socket_connect(struct socket *sock, struct sockaddr *addr,\nsecurity/tomoyo/tomoyo.c-469-\t\t\t\t int addr_len)\nsecurity/tomoyo/tomoyo.c-470-{\nsecurity/tomoyo/tomoyo.c:471:\treturn tomoyo_socket_connect_permission(sock, addr, addr_len);\nsecurity/tomoyo/tomoyo.c-472-}\n--\nsecurity/tomoyo/tomoyo.c=553=static struct security_hook_list tomoyo_hooks[] __ro_after_init = {\n--\nsecurity/tomoyo/tomoyo.c-582-\tLSM_HOOK_INIT(socket_bind, tomoyo_socket_bind),\nsecurity/tomoyo/tomoyo.c:583:\tLSM_HOOK_INIT(socket_connect, tomoyo_socket_connect),\nsecurity/tomoyo/tomoyo.c-584-\tLSM_HOOK_INIT(socket_listen, tomoyo_socket_listen),\n--\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c=15=static __always_inline void read_xattr(struct cgroup *cgroup)\n--\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-23-\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c:24:SEC(\"lsm.s/socket_connect\")\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-25-__success\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c=26=int BPF_PROG(trusted_cgroup_ptr_sleepable)\n--\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-39-\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c:40:SEC(\"lsm/socket_connect\")\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-41-__success\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c=42=int BPF_PROG(trusted_cgroup_ptr_non_sleepable)\n--\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-55-\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c:56:SEC(\"lsm/socket_connect\")\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-57-__success\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c=58=int BPF_PROG(use_css_iter_non_sleepable)\n--\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-74-\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c:75:SEC(\"lsm.s/socket_connect\")\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-76-__failure __msg(\"kernel func bpf_iter_css_new requires RCU critical section protection\")\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c=77=int BPF_PROG(use_css_iter_sleepable_missing_rcu_lock)\n--\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-93-\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c:94:SEC(\"lsm.s/socket_connect\")\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-95-__success\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c=96=int BPF_PROG(use_css_iter_sleepable_with_rcu_lock)\n--\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-115-\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c:116:SEC(\"lsm/socket_connect\")\ntools/testing/selftests/bpf/progs/cgroup_read_xattr.c-117-__success\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c=228=__naked void bpf_cond_op_not_r10(void)\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c-240-\ntools/testing/selftests/bpf/progs/verifier_precision.c:241:SEC(\"lsm.s/socket_connect\")\ntools/testing/selftests/bpf/progs/verifier_precision.c-242-__success __log_level(2)\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c=248=__naked int bpf_neg_2(void)\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c-250-\t/*\ntools/testing/selftests/bpf/progs/verifier_precision.c:251:\t * lsm.s/socket_connect requires a return value within [-4095, 0].\ntools/testing/selftests/bpf/progs/verifier_precision.c-252-\t * Returning -1 is allowed\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c-260-\ntools/testing/selftests/bpf/progs/verifier_precision.c:261:SEC(\"lsm.s/socket_connect\")\ntools/testing/selftests/bpf/progs/verifier_precision.c-262-__failure __msg(\"At program exit the register R0 has\")\ntools/testing/selftests/bpf/progs/verifier_precision.c=263=__naked int bpf_neg_3(void)\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c-265-\t/*\ntools/testing/selftests/bpf/progs/verifier_precision.c:266:\t * lsm.s/socket_connect requires a return value within [-4095, 0].\ntools/testing/selftests/bpf/progs/verifier_precision.c-267-\t * Returning -10000 is not allowed.\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c-275-\ntools/testing/selftests/bpf/progs/verifier_precision.c:276:SEC(\"lsm.s/socket_connect\")\ntools/testing/selftests/bpf/progs/verifier_precision.c-277-__success __log_level(2)\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c=283=__naked int bpf_neg_4(void)\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c-285-\t/*\ntools/testing/selftests/bpf/progs/verifier_precision.c:286:\t * lsm.s/socket_connect requires a return value within [-4095, 0].\ntools/testing/selftests/bpf/progs/verifier_precision.c-287-\t * Returning -1 is allowed\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c-295-\ntools/testing/selftests/bpf/progs/verifier_precision.c:296:SEC(\"lsm.s/socket_connect\")\ntools/testing/selftests/bpf/progs/verifier_precision.c-297-__failure __msg(\"At program exit the register R0 has\")\ntools/testing/selftests/bpf/progs/verifier_precision.c=298=__naked int bpf_neg_5(void)\n--\ntools/testing/selftests/bpf/progs/verifier_precision.c-300-\t/*\ntools/testing/selftests/bpf/progs/verifier_precision.c:301:\t * lsm.s/socket_connect requires a return value within [-4095, 0].\ntools/testing/selftests/bpf/progs/verifier_precision.c-302-\t * Returning -10000 is not allowed.\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 42495
							output: 21
							thoughts: 2093
						

Thoughts:
**Uninitialized Memory and BPF ksock_send: A Deep Dive**

Okay, let's break this down. My initial thought was about `LSM` hooks and potential reads of uninitialized memory with hooks that read up to `sizeof(struct sockaddr_storage)`. The key, though, is `addrlen`. The LSM hook should only read up to `addrlen`. If it reads beyond, that's a hook bug, but it's *unlikely* any of those common LSMs (AppArmor, SELinux, etc.) are doing that. They usually check `sa_family` and then read the correct `struct sockaddr_in` or `struct sockaddr_in6` which is safe, and within `addrlen`. So, no immediate issues there.

But then, `bpf_ksock_send` came into focus. `kernel_sendmsg` takes a `data` pointer. If that `data` points to a map value, and that map value contains *uninitialized padding*, that padding will be sent over the network! That's a classic info-leak. And KMSAN *would* catch this.

The question then shifts to "can BPF programs already do this?". Yes, `bpf_skb_store_bytes`, etc. already could. `bpf_ksock_send` is different because it's *direct* to the socket. It introduces a *new* way to leak uninitialized map data. So, the risk is *new* here. A BPF program *could* pass uninitialized map padding, and it *will* be sent over the network. KMSAN would nail this.

Next, I looked at the initialization of the `bpf_ksock_create` options. That struct is only 4 bytes. There's no padding, so no uninitialized memory could be there.

Moving on to `bpf_ksock_connect`, the focus is on `memcpy(&sa, addr, sizeof(*addr))`. If the BPF program initializes `sockaddr_in` partially, that uninitialized padding can be copied into `sa`. But, it seems `connect_socket` is safe. `inet_dgram_connect` calls `ip4_datagram_connect` or `ip6_datagram_connect`. And, based on the code analysis, this seems safe. If the address family is `AF_INET` and the BPF program initializes only the first 16 bytes of the address, the next 12 bytes copied would be uninitialized. However, the kernel only reads `addrlen = 16`, so the other 12 bytes are not read.

The main concern remains `bpf_ksock_send`. If a BPF program sends a map value with uninitialized padding, it will leak. Is this a kernel bug, or a BPF program bug? BPF programs are trusted... but the *verifier* doesn't track initialization of map values. So, it's a *kernel* info-leak, even if the origin is a "trusted" BPF program leaking its own map. The critical point is that KMSAN reports this as an info-leak!

Wait a minute, what about the fact that BPF map values are zero-allocated? This means the padding starts as zero! So, if the BPF program *only writes to fields*, the padding *stays* zero. If it updates an existing map entry, the padding remains initialized to zero. And stack variables are verified to be fully initialized before use. The verifier in BPF enforces initialization, including the padding, before a stack variable is used. This is confirmed by the check `check_stack_range_initialized`, so stack variables are safe.

This is the key. Since `bpf_map_update_elem` requires full initialization of stack variables *or* retains the zero-initialization of map values, the situation is *not* as bad as it seemed at first.

Unless the BPF program is using a `bpf_ringbuf_reserve`! That could return uninitialized memory. That needs to be investigated...



42/3 2026/08/07 20:22 tool grepper
0m Args:
{
  "Expression": "bpf_ringbuf_reserve"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 641 lines.\nUse more precise expression if possible.\n\nDocumentation/bpf/ringbuf.rst=84=BPF ringbuf provides two sets of APIs to BPF programs:\n--\nDocumentation/bpf/ringbuf.rst-87-  buffer, similarly to ``bpf_perf_event_output()``;\nDocumentation/bpf/ringbuf.rst:88:- ``bpf_ringbuf_reserve()``/``bpf_ringbuf_commit()``/``bpf_ringbuf_discard()``\nDocumentation/bpf/ringbuf.rst-89-  APIs split the whole process into two steps. First, a fixed amount of space\n--\nDocumentation/bpf/ringbuf.rst=100=significantly.\nDocumentation/bpf/ringbuf.rst-101-\nDocumentation/bpf/ringbuf.rst:102:``bpf_ringbuf_reserve()`` avoids the extra copy of memory by providing a memory\nDocumentation/bpf/ringbuf.rst-103-pointer directly to ring buffer memory. In a lot of cases records are larger\nDocumentation/bpf/ringbuf.rst=104=than BPF stack space allows, so many programs have use extra per-CPU array as\nDocumentation/bpf/ringbuf.rst:105:a temporary heap for preparing sample. bpf_ringbuf_reserve() avoid this needs\nDocumentation/bpf/ringbuf.rst-106-completely. But in exchange, it only allows a known constant size of memory to\n--\nDocumentation/bpf/ringbuf.rst=109=due to extra memory copy, covers some use cases that are not suitable for\nDocumentation/bpf/ringbuf.rst:110:``bpf_ringbuf_reserve()``.\nDocumentation/bpf/ringbuf.rst-111-\n--\nDocumentation/bpf/ringbuf.rst=152=applies to NMI context as well, except that due to using a spinlock during\nDocumentation/bpf/ringbuf.rst:153:reservation, in NMI context, ``bpf_ringbuf_reserve()`` might fail to get\nDocumentation/bpf/ringbuf.rst-154-a lock, in which case reservation will fail even if ring buffer is not full.\n--\nDocumentation/bpf/signing.rst=261=auditable. (Illustrative - error checking elided.)\n--\nDocumentation/bpf/signing.rst-299-\nDocumentation/bpf/signing.rst:300:            d = bpf_ringbuf_reserve(\u0026audit, sizeof(*d), 0);\nDocumentation/bpf/signing.rst-301-            if (d) {\n--\nDocumentation/hid/hid-bpf.rst=370=For that, we can create a basic skeleton for our BPF program::\n--\nDocumentation/hid/hid-bpf.rst-400-\tif (current_value != data[152]) {\nDocumentation/hid/hid-bpf.rst:401:\t\tbuf = bpf_ringbuf_reserve(\u0026ringbuf, 1, 0);\nDocumentation/hid/hid-bpf.rst-402-\t\tif (!buf)\n--\ninclude/linux/bpf.h=3904=extern const struct bpf_func_proto bpf_ringbuf_output_proto;\ninclude/linux/bpf.h:3905:extern const struct bpf_func_proto bpf_ringbuf_reserve_proto;\ninclude/linux/bpf.h-3906-extern const struct bpf_func_proto bpf_ringbuf_submit_proto;\n--\ninclude/linux/bpf.h=3908=extern const struct bpf_func_proto bpf_ringbuf_query_proto;\ninclude/linux/bpf.h:3909:extern const struct bpf_func_proto bpf_ringbuf_reserve_dynptr_proto;\ninclude/linux/bpf.h-3910-extern const struct bpf_func_proto bpf_ringbuf_submit_dynptr_proto;\n--\ninclude/uapi/linux/bpf.h=1527=union bpf_attr {\n--\ninclude/uapi/linux/bpf.h-4690- *\ninclude/uapi/linux/bpf.h:4691: * void *bpf_ringbuf_reserve(void *ringbuf, u64 size, u64 flags)\ninclude/uapi/linux/bpf.h-4692- * \tDescription\n--\ninclude/uapi/linux/bpf.h-5737- *\ninclude/uapi/linux/bpf.h:5738: * long bpf_ringbuf_reserve_dynptr(void *ringbuf, u32 size, u64 flags, struct bpf_dynptr *ptr)\ninclude/uapi/linux/bpf.h-5739- *\tDescription\n--\nkernel/bpf/helpers.c=2067=bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nkernel/bpf/helpers.c-2100-\tcase BPF_FUNC_ringbuf_reserve:\nkernel/bpf/helpers.c:2101:\t\treturn \u0026bpf_ringbuf_reserve_proto;\nkernel/bpf/helpers.c-2102-\tcase BPF_FUNC_ringbuf_submit:\n--\nkernel/bpf/helpers.c-2154-\tcase BPF_FUNC_ringbuf_reserve_dynptr:\nkernel/bpf/helpers.c:2155:\t\treturn \u0026bpf_ringbuf_reserve_dynptr_proto;\nkernel/bpf/helpers.c-2156-\tcase BPF_FUNC_ringbuf_submit_dynptr:\n--\nkernel/bpf/ringbuf.c=457=static u32 bpf_ringbuf_round_up_hdr_len(u32 hdr_len)\n--\nkernel/bpf/ringbuf.c-462-\nkernel/bpf/ringbuf.c:463:static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size)\nkernel/bpf/ringbuf.c-464-{\n--\nkernel/bpf/ringbuf.c-539-\nkernel/bpf/ringbuf.c:540:BPF_CALL_3(bpf_ringbuf_reserve, struct bpf_map *, map, u64, size, u64, flags)\nkernel/bpf/ringbuf.c-541-{\n--\nkernel/bpf/ringbuf.c-547-\trb_map = container_of(map, struct bpf_ringbuf_map, map);\nkernel/bpf/ringbuf.c:548:\treturn (unsigned long)__bpf_ringbuf_reserve(rb_map-\u003erb, size);\nkernel/bpf/ringbuf.c-549-}\nkernel/bpf/ringbuf.c-550-\nkernel/bpf/ringbuf.c:551:const struct bpf_func_proto bpf_ringbuf_reserve_proto = {\nkernel/bpf/ringbuf.c:552:\t.func\t\t= bpf_ringbuf_reserve,\nkernel/bpf/ringbuf.c-553-\t.ret_type\t= RET_PTR_TO_RINGBUF_MEM_OR_NULL,\n--\nkernel/bpf/ringbuf.c=613=BPF_CALL_4(bpf_ringbuf_output, struct bpf_map *, map, void *, data, u64, size,\n--\nkernel/bpf/ringbuf.c-622-\trb_map = container_of(map, struct bpf_ringbuf_map, map);\nkernel/bpf/ringbuf.c:623:\trec = __bpf_ringbuf_reserve(rb_map-\u003erb, size);\nkernel/bpf/ringbuf.c-624-\tif (!rec)\n--\nkernel/bpf/ringbuf.c=663=const struct bpf_func_proto bpf_ringbuf_query_proto = {\n--\nkernel/bpf/ringbuf.c-669-\nkernel/bpf/ringbuf.c:670:BPF_CALL_4(bpf_ringbuf_reserve_dynptr, struct bpf_map *, map, u32, size, u64, flags,\nkernel/bpf/ringbuf.c-671-\t   struct bpf_dynptr_kern *, ptr)\n--\nkernel/bpf/ringbuf.c-689-\nkernel/bpf/ringbuf.c:690:\tsample = __bpf_ringbuf_reserve(rb_map-\u003erb, size);\nkernel/bpf/ringbuf.c-691-\tif (!sample) {\n--\nkernel/bpf/ringbuf.c-700-\nkernel/bpf/ringbuf.c:701:const struct bpf_func_proto bpf_ringbuf_reserve_dynptr_proto = {\nkernel/bpf/ringbuf.c:702:\t.func\t\t= bpf_ringbuf_reserve_dynptr,\nkernel/bpf/ringbuf.c-703-\t.ret_type\t= RET_INTEGER,\n--\ntools/include/uapi/linux/bpf.h=1527=union bpf_attr {\n--\ntools/include/uapi/linux/bpf.h-4690- *\ntools/include/uapi/linux/bpf.h:4691: * void *bpf_ringbuf_reserve(void *ringbuf, u64 size, u64 flags)\ntools/include/uapi/linux/bpf.h-4692- * \tDescription\n--\ntools/include/uapi/linux/bpf.h-5737- *\ntools/include/uapi/linux/bpf.h:5738: * long bpf_ringbuf_reserve_dynptr(void *ringbuf, u32 size, u64 flags, struct bpf_dynptr *ptr)\ntools/include/uapi/linux/bpf.h-5739- *\tDescription\n--\ntools/lib/bpf/ringbuf.c=379=unsigned long ring__producer_pos(const struct ring *r)\ntools/lib/bpf/ringbuf.c-380-{\ntools/lib/bpf/ringbuf.c:381:\t/* Synchronizes with smp_store_release() in __bpf_ringbuf_reserve() in\ntools/lib/bpf/ringbuf.c-382-\t * the kernel.\n--\ntools/testing/selftests/bpf/benchs/bench_ringbufs.c=46=static const struct argp_option opts[] = {\ntools/testing/selftests/bpf/benchs/bench_ringbufs.c-47-\t{ \"rb-b2b\", ARG_RB_BACK2BACK, NULL, 0, \"Back-to-back mode\"},\ntools/testing/selftests/bpf/benchs/bench_ringbufs.c:48:\t{ \"rb-use-output\", ARG_RB_USE_OUTPUT, NULL, 0, \"Use bpf_ringbuf_output() instead of bpf_ringbuf_reserve()\"},\ntools/testing/selftests/bpf/benchs/bench_ringbufs.c-49-\t{ \"rb-batch-cnt\", ARG_RB_BATCH_CNT, \"CNT\", 0, \"Set BPF-side record batch count\"},\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=62=static int get_map_val_dynptr(struct bpf_dynptr *ptr)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-76-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:77:/* Every bpf_ringbuf_reserve_dynptr call must have a corresponding\ntools/testing/selftests/bpf/progs/dynptr_fail.c-78- * bpf_ringbuf_submit/discard_dynptr call\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=82=int ringbuf_missing_release1(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-85-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:86:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-87-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=95=int ringbuf_missing_release2(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-99-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:100:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, sizeof(*sample), 0, \u0026ptr1);\ntools/testing/selftests/bpf/progs/dynptr_fail.c:101:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, sizeof(*sample), 0, \u0026ptr2);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-102-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=117=static int missing_release_callback_fn(__u32 index, void *data)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-120-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:121:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-122-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=153=int use_after_invalid(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-157-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:158:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, sizeof(read_data), 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-159-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=173=int ringbuf_invalid_api(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-177-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:178:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, sizeof(*sample), 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-179-\tsample = bpf_dynptr_data(\u0026ptr, 0, sizeof(*sample));\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=196=int add_dynptr_to_map1(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-200-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:201:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-202-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=214=int add_dynptr_to_map2(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-218-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:219:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026x.ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-220-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=232=int data_slice_out_of_bounds_ringbuf(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-236-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:237:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 8, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-238-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=315=int data_slice_use_after_release1(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-319-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:320:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, sizeof(*sample), 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-321-\tsample = bpf_dynptr_data(\u0026ptr, 0, sizeof(*sample));\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=347=int data_slice_use_after_release2(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-351-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:352:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr1);\ntools/testing/selftests/bpf/progs/dynptr_fail.c:353:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, sizeof(*sample), 0, \u0026ptr2);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-354-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=379=int data_slice_missing_null_check1(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-383-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:384:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 8, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-385-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=400=int data_slice_missing_null_check2(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-404-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:405:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 16, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-406-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=475=int invalid_write2(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-480-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:481:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-482-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=499=int invalid_write3(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-505-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:506:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 8, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-507-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=531=int invalid_write4(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-534-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:535:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-536-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=550=int global(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-552-\t/* this should fail */\ntools/testing/selftests/bpf/progs/dynptr_fail.c:553:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 16, 0, \u0026global_dynptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-554-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=563=int invalid_read1(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-566-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:567:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-568-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=596=int invalid_read3(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-599-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:600:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 16, 0, \u0026ptr1);\ntools/testing/selftests/bpf/progs/dynptr_fail.c:601:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 16, 0, \u0026ptr2);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-602-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=623=int invalid_read4(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-626-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:627:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-628-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=639=int invalid_offset(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-643-\t/* this should fail */\ntools/testing/selftests/bpf/progs/dynptr_fail.c:644:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr + 1);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-645-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=654=int release_twice(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-657-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:658:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 16, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-659-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=681=int release_twice_callback(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-684-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:685:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 32, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-686-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=752=int dynptr_pruning_overwrite(struct __sk_buff *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-761-\t\t r4 += -16;\t\t\t\t\\\ntools/testing/selftests/bpf/progs/dynptr_fail.c:762:\t\t call %[bpf_ringbuf_reserve_dynptr];\t\\\ntools/testing/selftests/bpf/progs/dynptr_fail.c-763-\t\t if r0 == 0 goto pjmp1;\t\t\t\\\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-772-\t\t:\ntools/testing/selftests/bpf/progs/dynptr_fail.c:773:\t\t: __imm(bpf_ringbuf_reserve_dynptr),\ntools/testing/selftests/bpf/progs/dynptr_fail.c-774-\t\t  __imm(bpf_ringbuf_discard_dynptr),\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=783=int dynptr_pruning_stacksafe(struct __sk_buff *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-792-\t\t r4 += -16;\t\t\t\t\\\ntools/testing/selftests/bpf/progs/dynptr_fail.c:793:\t\t call %[bpf_ringbuf_reserve_dynptr];\t\\\ntools/testing/selftests/bpf/progs/dynptr_fail.c-794-\t\t if r0 == 0 goto stjmp1;\t\t\\\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-803-\t\t:\ntools/testing/selftests/bpf/progs/dynptr_fail.c:804:\t\t: __imm(bpf_ringbuf_reserve_dynptr),\ntools/testing/selftests/bpf/progs/dynptr_fail.c-805-\t\t  __imm(bpf_ringbuf_discard_dynptr),\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=814=int dynptr_pruning_type_confusion(struct __sk_buff *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-845-\t\t r0 = *(u64 *)(r0 + 0);\t\t\t\\\ntools/testing/selftests/bpf/progs/dynptr_fail.c:846:\t\t call %[bpf_ringbuf_reserve_dynptr];\t\\\ntools/testing/selftests/bpf/progs/dynptr_fail.c-847-\t\t if r0 == 0 goto tjmp2;\t\t\t\\\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-873-\t\t  __imm(bpf_map_lookup_elem),\ntools/testing/selftests/bpf/progs/dynptr_fail.c:874:\t\t  __imm(bpf_ringbuf_reserve_dynptr),\ntools/testing/selftests/bpf/progs/dynptr_fail.c-875-\t\t  __imm(bpf_dynptr_from_mem),\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=886=int dynptr_var_off_overwrite(struct __sk_buff *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-906-\t\t r4 += r8;\t\t\t\t\\\ntools/testing/selftests/bpf/progs/dynptr_fail.c:907:\t\t call %[bpf_ringbuf_reserve_dynptr];\t\\\ntools/testing/selftests/bpf/progs/dynptr_fail.c-908-\t\t r9 = 0xeB9F;\t\t\t\t\\\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-915-\t\t:\ntools/testing/selftests/bpf/progs/dynptr_fail.c:916:\t\t: __imm(bpf_ringbuf_reserve_dynptr),\ntools/testing/selftests/bpf/progs/dynptr_fail.c-917-\t\t  __imm(bpf_ringbuf_discard_dynptr),\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=926=int dynptr_partial_slot_invalidate(struct __sk_buff *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-951-\t\t r4 += -24;\t\t\t\t\\\ntools/testing/selftests/bpf/progs/dynptr_fail.c:952:\t\t call %[bpf_ringbuf_reserve_dynptr];\t\\\ntools/testing/selftests/bpf/progs/dynptr_fail.c-953-\t\t *(u64 *)(r10 - 16) = r9;\t\t\\\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-978-\t\t  __imm(bpf_map_lookup_elem),\ntools/testing/selftests/bpf/progs/dynptr_fail.c:979:\t\t  __imm(bpf_ringbuf_reserve_dynptr),\ntools/testing/selftests/bpf/progs/dynptr_fail.c-980-\t\t  __imm(bpf_ringbuf_discard_dynptr),\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=1102=int dynptr_overwrite_ref(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1105-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:1106:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1107-\t/* this should fail */\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=1116=int dynptr_read_into_slot(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1125-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:1126:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026data.ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1127-\t/* this should fail */\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=1562=int uninit_write_into_slot(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1568-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:1569:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 80, 0, \u0026data.ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1570-\t/* this should fail */\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=1754=int clone_invalid2(struct xdp_md *xdp)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1760-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:1761:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026clone);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1762-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=1774=int clone_invalidate1(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1779-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:1780:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1781-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=1795=int clone_invalidate2(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1800-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:1801:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1802-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=1816=int clone_invalidate3(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1822-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:1823:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1824-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=1842=int clone_invalidate4(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1847-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:1848:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1849-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=1868=int clone_invalidate5(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1873-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:1874:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1875-\tdata = bpf_dynptr_data(\u0026ptr, 0, sizeof(val));\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=1894=int clone_invalidate6(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1900-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:1901:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-1902-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=2042=int dynptr_overwrite_ref_with_clone(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-2045-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:2046:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-2047-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=2061=int dynptr_overwrite_ref_last_clone(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-2064-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:2065:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-2066-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=2081=int dynptr_overwrite_clone_with_original(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-2084-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:2085:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-2086-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=2100=int dynptr_overwrite_ref_invalidate_slice(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-2104-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:2105:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-2106-\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c=2128=int dynptr_overwrite_ref_clone_slice_valid(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_fail.c-2132-\ntools/testing/selftests/bpf/progs/dynptr_fail.c:2133:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_fail.c-2134-\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=38=int test_read_write(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-47-\ntools/testing/selftests/bpf/progs/dynptr_success.c:48:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, sizeof(write_data), 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_success.c-49-\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=139=int test_ringbuf(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-149-\t/* check that you can reserve a dynamic size reservation */\ntools/testing/selftests/bpf/progs/dynptr_success.c:150:\terr = bpf_ringbuf_reserve_dynptr(\u0026ringbuf, val, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_success.c-151-\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=270=int test_adjust(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-279-\ntools/testing/selftests/bpf/progs/dynptr_success.c:280:\terr = bpf_ringbuf_reserve_dynptr(\u0026ringbuf, bytes, 0, \u0026ptr);\ntools/testing/selftests/bpf/progs/dynptr_success.c-281-\tif (err) {\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=322=int test_adjust_err(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-331-\ntools/testing/selftests/bpf/progs/dynptr_success.c:332:\tif (bpf_ringbuf_reserve_dynptr(\u0026ringbuf, size, 0, \u0026ptr)) {\ntools/testing/selftests/bpf/progs/dynptr_success.c-333-\t\terr = 1;\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=380=int test_zero_size_dynptr(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-388-\ntools/testing/selftests/bpf/progs/dynptr_success.c:389:\tif (bpf_ringbuf_reserve_dynptr(\u0026ringbuf, size, 0, \u0026ptr)) {\ntools/testing/selftests/bpf/progs/dynptr_success.c-390-\t\terr = 1;\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=430=int test_dynptr_is_null(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-439-\t/* Pass in invalid flags, get back an invalid dynptr */\ntools/testing/selftests/bpf/progs/dynptr_success.c:440:\tif (bpf_ringbuf_reserve_dynptr(\u0026ringbuf, size, 123, \u0026ptr1) != -EINVAL) {\ntools/testing/selftests/bpf/progs/dynptr_success.c-441-\t\terr = 1;\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-451-\t/* Get a valid dynptr */\ntools/testing/selftests/bpf/progs/dynptr_success.c:452:\tif (bpf_ringbuf_reserve_dynptr(\u0026ringbuf, size, 0, \u0026ptr2)) {\ntools/testing/selftests/bpf/progs/dynptr_success.c-453-\t\terr = 3;\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=471=int test_dynptr_is_rdonly(struct __sk_buff *skb)\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-501-\t/* Get a read-writeable dynptr */\ntools/testing/selftests/bpf/progs/dynptr_success.c:502:\tif (bpf_ringbuf_reserve_dynptr(\u0026ringbuf, 64, 0, \u0026ptr3)) {\ntools/testing/selftests/bpf/progs/dynptr_success.c-503-\t\terr = 5;\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=639=int test_dynptr_copy(void *ctx)\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-645-\ntools/testing/selftests/bpf/progs/dynptr_success.c:646:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, sz, 0, \u0026src);\ntools/testing/selftests/bpf/progs/dynptr_success.c:647:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, sz, 0, \u0026dst);\ntools/testing/selftests/bpf/progs/dynptr_success.c-648-\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=666=int test_dynptr_copy_xdp(struct xdp_md *xdp)\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-676-\txdp_data_size = bpf_dynptr_size(\u0026ptr_xdp);\ntools/testing/selftests/bpf/progs/dynptr_success.c:677:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, len * chunks, 0, \u0026ptr_buf);\ntools/testing/selftests/bpf/progs/dynptr_success.c-678-\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=936=static __always_inline void test_dynptr_probe(void *ptr, bpf_read_dynptr_fn_t bpf_read_dynptr_fn)\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-944-\ntools/testing/selftests/bpf/progs/dynptr_success.c:945:\terr = bpf_ringbuf_reserve_dynptr(\u0026ringbuf, sizeof(buf), 0, \u0026ptr_buf);\ntools/testing/selftests/bpf/progs/dynptr_success.c-946-\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c=965=static __always_inline void test_dynptr_probe_str(void *ptr,\n--\ntools/testing/selftests/bpf/progs/dynptr_success.c-974-\ntools/testing/selftests/bpf/progs/dynptr_success.c:975:\tbpf_ringbuf_reserve_dynptr(\u0026ringbuf, sizeof(buf), 0, \u0026ptr_buf);\ntools/testing/selftests/bpf/progs/dynptr_success.c-976-\n--\ntools/testing/selftests/bpf/progs/ima.c=26=static void ima_test_common(struct file *file)\n--\ntools/testing/selftests/bpf/progs/ima.c-43-\ntools/testing/selftests/bpf/progs/ima.c:44:\t\tsample = bpf_ringbuf_reserve(\u0026ringbuf, sizeof(u64), 0);\ntools/testing/selftests/bpf/progs/ima.c-45-\t\tif (!sample)\n--\ntools/testing/selftests/bpf/progs/map_ptr_kern.c=664=static inline int check_ringbuf(void)\n--\ntools/testing/selftests/bpf/progs/map_ptr_kern.c-672-\ntools/testing/selftests/bpf/progs/map_ptr_kern.c:673:\tptr = bpf_ringbuf_reserve(\u0026m_ringbuf, 128, 0);\ntools/testing/selftests/bpf/progs/map_ptr_kern.c-674-\tVERIFY(ptr);\n--\ntools/testing/selftests/bpf/progs/ringbuf_bench.c=41=int bench_ringbuf(void *ctx)\n--\ntools/testing/selftests/bpf/progs/ringbuf_bench.c-47-\t\tfor (i = 0; i \u003c batch_cnt; i++) {\ntools/testing/selftests/bpf/progs/ringbuf_bench.c:48:\t\t\tsample = bpf_ringbuf_reserve(\u0026ringbuf,\ntools/testing/selftests/bpf/progs/ringbuf_bench.c-49-\t\t\t\t\t             sizeof(sample_val), 0);\n--\ntools/testing/selftests/bpf/progs/test_ringbuf.c=40=int test_ringbuf(void *ctx)\n--\ntools/testing/selftests/bpf/progs/test_ringbuf.c-47-\ntools/testing/selftests/bpf/progs/test_ringbuf.c:48:\tsample = bpf_ringbuf_reserve(\u0026ringbuf, sizeof(*sample), 0);\ntools/testing/selftests/bpf/progs/test_ringbuf.c-49-\tif (!sample) {\n--\ntools/testing/selftests/bpf/progs/test_ringbuf_map_key.c=35=int test_ringbuf_mem_map_key(void *ctx)\n--\ntools/testing/selftests/bpf/progs/test_ringbuf_map_key.c-43-\n\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 58172
							output: 491
							thoughts: 4921
						

Thoughts:
**Uninitialized Memory Analysis for bpf_ksock Patch**

Okay, let's break this down. My initial concern was centered around potential information leaks via uninitialized memory, especially with the use of `bpf_ksock_send` and its interaction with the ring buffer. I immediately went through the mental checklist: `bpf_ringbuf_reserve` can return uninitialized memory, which *could* be sent over the network via `bpf_ksock_send`. But wait, the ring buffer is *zeroed* on creation! Though memory reuse via the ring buffer means previously-used (and thus potentially uninitialized) data can exist in the reserved memory region. The kicker is, does the verifier catch this? No, it doesn't track *uninitialized* `PTR_TO_MEM` memory access. This means `bpf_ksock_send` *could* be used to leak stale ring buffer data, which KMSAN *would* catch, right?

Thinking further, does KMSAN hook `bpf_ringbuf_reserve` specifically? I don't know, but it generally catches uninitialized memory. But moving on, is there another similar case, perhaps, with `bpf_obj_new`? No, those objects are zero-allocated. A related thought, is `bpf_ksock_connect` risky? It utilizes a stack variable, `struct sockaddr_storage sa`. `memcpy` only partially initializes the variable, leaving some parts uninitialized. If any function then read those uninitialized bytes, it’s a problem.

Ah, but then I realized, this issue of partially-initialized stack memory is the *same* as in the standard `connect()` syscall, via `move_addr_to_kernel`. The `connect` path in the kernel also leaves the stack-allocated `address` structure partially initialized, and the rest is never read. Thus, `bpf_ksock_connect` doesn't introduce any *new* uninitialized memory risk!

So, the *only* potential new risk seems to be with `bpf_ksock_send`. I began to contrast it with `bpf_skb_store_bytes` and `bpf_msg_push_data`, is it the same? I mean both can leak uninitialized memory. Is there a good reason to run KMSAN specifically? Maybe there's some subtle bug in how it uses the `data` pointer or the size. That's worth further investigation.

I noticed that `bpf_ksock_send` calls `kernel_sendmsg`. Now, `kernel_sendmsg` is a standard kernel function. Now, are there any potential uninitialized memory bugs within the `bpf_ksock_send`? It initializes `struct msghdr msg` and `struct kvec iov`. The C compiler zeroes the rest of msg, therefore, `msg` is initialized. Similarly, `iov` also appears to be fully initialized.

Then, I went through the specific KMSAN criteria: kernel structures sent to userspace, conditional logic using uninitialized variables, complex data structure initialization, and boundary checks. I broke down what's being sent to userspace. In the C code introduced by this patch, there are no uninitialized kernel structures (i.e. everything is properly initialized via the C semantics that I am familiar with). The allocated `bpf_ksock` is zeroed via `kzalloc_obj`. The newly introduced `struct msghdr` and `struct kvec` are also initialized and, in the `connect` path, the risk of uninitialized memory is identical to the existing `connect()` syscall.

Now, let's look at `bpf_ksock_create`: It reads 4 bytes from `opts`, and then copies it. There is no padding within the newly defined `struct bpf_ksock_create_opts`. Nothing to worry about. For `bpf_ksock_connect`, it uses `memcpy` to copy `union bpf_ksock_addr`. The verifier ensures `addr` is initialized on the stack or from a map, and I reasoned that it's likely fine as BPF is designed to ensure stack variables are properly initialized.

Okay, more checks. Now, is there uninitialized memory in `bpf_ksock_send`? `struct msghdr` is fine since it's zeroed by the designated initializer. All the other variables are fully initialized. It looks good.

So, I re-evaluated if there are *any* uninitialized memory issues. No. The kernel-side code added by the patch is sound. All stack variables are initialized. Memory allocated with `kzalloc_obj` is zeroed. The data sent over the socket comes from BPF. While the BPF program *could* pass uninitialized map memory


44/3 2026/08/07 20:22 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch introduces a new BPF kfunc API for creating, connecting, and sending data over kernel sockets (`bpf_ksock`). \n\nFrom an uninitialized memory perspective:\n1. All newly introduced kernel structures and stack variables are properly initialized:\n   - `struct bpf_ksock` is allocated using `kzalloc_obj`, ensuring all fields are zeroed.\n   - `struct msghdr msg` and `struct kvec iov` in `bpf_ksock_send` are initialized using designated initializers (`= { ... }`), which guarantees that all unspecified fields are zero-initialized by the compiler.\n   - `struct sockaddr_storage sa` in `bpf_ksock_connect` is partially initialized via `memcpy` from the BPF-provided address (up to 28 bytes). The uninitialized portion is never read, as `connect_socket` only reads up to `addrlen` (16 or 28 bytes). This exactly mirrors the behavior of the standard `connect()` syscall (which leaves the tail of `sockaddr_storage` uninitialized via `move_addr_to_kernel`), so it does not introduce a new info-leak risk.\n   - `opts_copy` in `bpf_ksock_create` is fully initialized by reading the 4 byte fields explicitly.\n2. The data sent over the socket in `bpf_ksock_send` is provided by the BPF program. The BPF verifier ensures that memory passed to kfuncs is within bounds and initialized (for stack variables). While BPF map values might contain uninitialized padding, this is a known property of the BPF subsystem and not a bug in the `bpf_ksock` C implementation.\n3. The primary risks introduced by this patch are related to object lifetime (use-after-free of `bpf_ksock` or `socket`), reference counting, and out-of-bounds accesses. These are perfectly covered by KASAN and standard bug detectors.\n\nThere are no uninitialized memory info-leaks or uses of uninitialized memory in conditional logic introduced by this patch. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch introduces a new BPF kfunc API for creating, connecting, and sending data over kernel sockets (`bpf_ksock`). \n\nFrom an uninitialized memory perspective:\n1. All newly introduced kernel structures and stack variables are properly initialized:\n   - `struct bpf_ksock` is allocated using `kzalloc_obj`, ensuring all fields are zeroed.\n   - `struct msghdr msg` and `struct kvec iov` in `bpf_ksock_send` are initialized using designated initializers (`= { ... }`), which guarantees that all unspecified fields are zero-initialized by the compiler.\n   - `struct sockaddr_storage sa` in `bpf_ksock_connect` is partially initialized via `memcpy` from the BPF-provided address (up to 28 bytes). The uninitialized portion is never read, as `connect_socket` only reads up to `addrlen` (16 or 28 bytes). This exactly mirrors the behavior of the standard `connect()` syscall (which leaves the tail of `sockaddr_storage` uninitialized via `move_addr_to_kernel`), so it does not introduce a new info-leak risk.\n   - `opts_copy` in `bpf_ksock_create` is fully initialized by reading the 4 byte fields explicitly.\n2. The data sent over the socket in `bpf_ksock_send` is provided by the BPF program. The BPF verifier ensures that memory passed to kfuncs is within bounds and initialized (for stack variables). While BPF map values might contain uninitialized padding, this is a known property of the BPF subsystem and not a bug in the `bpf_ksock` C implementation.\n3. The primary risks introduced by this patch are related to object lifetime (use-after-free of `bpf_ksock` or `socket`), reference counting, and out-of-bounds accesses. These are perfectly covered by KASAN and standard bug detectors.\n\nThere are no uninitialized memory info-leaks or uses of uninitialized memory in conditional logic introduced by this patch. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

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