AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/10/02 15:32 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch series exclusively modifies user-space selftests under tools/testing/selftests/net/ (adding so_reserve_mem.c and updating corresponding Makefile, config, and .gitignore files). It does not modify any kernel source code, headers, or modules affecting the compiled kernel image (vmlinux). Therefore, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

1/1 2026/10/02 15:32 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 188dec0b070900c1879dffad30e82a16290ca6bb\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Oct 2 15:32:26 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore\nindex dacd36ed8455e..21412510ac26b 100644\n--- a/tools/testing/selftests/net/.gitignore\n+++ b/tools/testing/selftests/net/.gitignore\n@@ -41,6 +41,7 @@ skf_net_off\n socket\n so_incoming_cpu\n so_netns_cookie\n+so_reserve_mem\n so_rcv_listener\n stress_reuseport_listen\n tap\ndiff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile\nindex cab3f2c90039f..54beea2e348c4 100644\n--- a/tools/testing/selftests/net/Makefile\n+++ b/tools/testing/selftests/net/Makefile\n@@ -197,6 +197,7 @@ TEST_GEN_PROGS := \\\n \tsk_connect_zero_addr \\\n \tsk_so_peek_off \\\n \tso_incoming_cpu \\\n+\tso_reserve_mem \\\n \ttap \\\n \ttcp_port_share \\\n \ttls \\\ndiff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config\nindex 737e7e6327b37..d355cf9805977 100644\n--- a/tools/testing/selftests/net/config\n+++ b/tools/testing/selftests/net/config\n@@ -7,6 +7,7 @@ CONFIG_BRIDGE_VLAN_FILTERING=y\n CONFIG_CAN=m\n CONFIG_CAN_DEV=m\n CONFIG_CAN_VXCAN=m\n+CONFIG_CGROUPS=y\n CONFIG_CRYPTO_ARIA=y\n CONFIG_CRYPTO_CHACHA20POLY1305=m\n CONFIG_CRYPTO_SHA1=y\n@@ -61,6 +62,7 @@ CONFIG_L2TP_V3=y\n CONFIG_MACSEC=m\n CONFIG_MACVLAN=y\n CONFIG_MACVTAP=y\n+CONFIG_MEMCG=y\n CONFIG_MPLS=y\n CONFIG_MPLS_IPTUNNEL=m\n CONFIG_MPLS_ROUTING=m\ndiff --git a/tools/testing/selftests/net/so_reserve_mem.c b/tools/testing/selftests/net/so_reserve_mem.c\nnew file mode 100644\nindex 0000000000000..aca5960ddff25\n--- /dev/null\n+++ b/tools/testing/selftests/net/so_reserve_mem.c\n@@ -0,0 +1,448 @@\n+// SPDX-License-Identifier: GPL-2.0\n+\n+#define _GNU_SOURCE\n+#include \u003cerrno.h\u003e\n+#include \u003cfcntl.h\u003e\n+#include \u003climits.h\u003e\n+#include \u003clinux/sock_diag.h\u003e\n+#include \u003cnet/if.h\u003e\n+#include \u003cnetinet/in.h\u003e\n+#include \u003csched.h\u003e\n+#include \u003cstdio.h\u003e\n+#include \u003cstdlib.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003csys/ioctl.h\u003e\n+#include \u003csys/mount.h\u003e\n+#include \u003csys/socket.h\u003e\n+#include \u003csys/stat.h\u003e\n+#include \u003cunistd.h\u003e\n+\n+#include \"kselftest_harness.h\"\n+\n+#ifndef IPPROTO_MPTCP\n+#define IPPROTO_MPTCP 262\n+#endif\n+\n+#define SO_RESERVE_MEM_MAX (1 \u003c\u003c 30)\n+\n+/* cgroup2 is mounted here, on a tmpfs, in a private mount namespace. */\n+#define CG_TMP \"/tmp\"\n+#define CG_MNT CG_TMP \"/cgroup2\"\n+\n+static int cg_write(const char *dir, const char *file, const char *buf,\n+\t\t    int flags)\n+{\n+\tssize_t len = strlen(buf);\n+\tchar path[PATH_MAX];\n+\tint fd, ret;\n+\n+\tsnprintf(path, sizeof(path), \"%s/%s\", dir, file);\n+\tfd = open(path, O_WRONLY | flags);\n+\tif (fd \u003c 0)\n+\t\treturn -1;\n+\tret = write(fd, buf, len) == len ? 0 : -1;\n+\tclose(fd);\n+\treturn ret;\n+}\n+\n+static int cg_read(const char *dir, const char *file, char *buf, size_t size)\n+{\n+\tchar path[PATH_MAX];\n+\tssize_t n;\n+\tint fd;\n+\n+\tsnprintf(path, sizeof(path), \"%s/%s\", dir, file);\n+\tfd = open(path, O_RDONLY);\n+\tif (fd \u003c 0)\n+\t\treturn -1;\n+\tn = read(fd, buf, size - 1);\n+\tclose(fd);\n+\tif (n \u003c 0)\n+\t\treturn -1;\n+\tbuf[n] = '\\0';\n+\treturn 0;\n+}\n+\n+static int cg_enter(const char *dir)\n+{\n+\tchar buf[32];\n+\n+\tsnprintf(buf, sizeof(buf), \"%d\\n\", getpid());\n+\treturn cg_write(dir, \"cgroup.procs\", buf, 0);\n+}\n+\n+/* Path of the current cgroup, below CG_MNT. */\n+static int cg_get_current(char *buf, size_t size)\n+{\n+\tchar line[PATH_MAX];\n+\tint ret = -1;\n+\tFILE *f;\n+\n+\tf = fopen(\"/proc/self/cgroup\", \"r\");\n+\tif (!f)\n+\t\treturn -1;\n+\twhile (fgets(line, sizeof(line), f)) {\n+\t\tif (strncmp(line, \"0::\", 3))\n+\t\t\tcontinue;\n+\t\tline[strcspn(line, \"\\n\")] = '\\0';\n+\t\tif (snprintf(buf, size, \"%s%s\", CG_MNT, line + 3) \u003c (int)size)\n+\t\t\tret = 0;\n+\t\tbreak;\n+\t}\n+\tfclose(f);\n+\treturn ret;\n+}\n+\n+static int get_reserve_mem(struct __test_metadata *_metadata, int fd)\n+{\n+\tint val = -1;\n+\tsocklen_t len = sizeof(val);\n+\n+\tEXPECT_EQ(getsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, \u0026len), 0);\n+\treturn val;\n+}\n+\n+static __u32 get_fwd_alloc(struct __test_metadata *_metadata, int fd)\n+{\n+\t__u32 meminfo[SK_MEMINFO_VARS] = {};\n+\tsocklen_t len = sizeof(meminfo);\n+\n+\tEXPECT_EQ(getsockopt(fd, SOL_SOCKET, SO_MEMINFO, meminfo, \u0026len), 0);\n+\treturn meminfo[SK_MEMINFO_FWD_ALLOC];\n+}\n+\n+/* Wait until a single SO_MEMINFO snapshot shows an empty write queue\n+ * and at least @min_fwd_alloc bytes of forward alloc: ACK processing\n+ * (possibly running on another CPU) first decrements sk_wmem_queued,\n+ * then uncharges sk_forward_alloc.\n+ */\n+static void wait_wmem_drained(struct __test_metadata *_metadata, int fd,\n+\t\t\t      __u32 min_fwd_alloc)\n+{\n+\t__u32 meminfo[SK_MEMINFO_VARS] = {};\n+\tsocklen_t len;\n+\tint i;\n+\n+\tfor (i = 0; i \u003c 5000; i++) {\n+\t\tlen = sizeof(meminfo);\n+\t\tASSERT_EQ(getsockopt(fd, SOL_SOCKET, SO_MEMINFO, meminfo, \u0026len), 0);\n+\t\tif (meminfo[SK_MEMINFO_WMEM_QUEUED] == 0 \u0026\u0026\n+\t\t    meminfo[SK_MEMINFO_FWD_ALLOC] \u003e= min_fwd_alloc)\n+\t\t\treturn;\n+\t\tusleep(1000);\n+\t}\n+\tEXPECT_EQ(meminfo[SK_MEMINFO_WMEM_QUEUED], 0U);\n+\tEXPECT_GE(meminfo[SK_MEMINFO_FWD_ALLOC], min_fwd_alloc);\n+}\n+\n+FIXTURE(so_reserve_mem)\n+{\n+\tchar cg_orig[PATH_MAX];\t/* cgroup the test started in */\n+\tchar cg_test[PATH_MAX];\t/* cgroup the test sockets are charged to */\n+\tlong page_size;\n+\tbool cg_created;\n+};\n+\n+/* The cgroup2 mount lives in a private mount namespace which goes away\n+ * with the test process, no need to unmount it.\n+ */\n+FIXTURE_TEARDOWN(so_reserve_mem)\n+{\n+\tif (!self-\u003ecg_created)\n+\t\treturn;\n+\n+\t/* Leave cg_test so that it can be removed. */\n+\tEXPECT_EQ(cg_enter(self-\u003ecg_orig), 0);\n+\tEXPECT_EQ(rmdir(self-\u003ecg_test), 0);\n+\tself-\u003ecg_created = false;\n+}\n+\n+FIXTURE_SETUP(so_reserve_mem)\n+{\n+\tstruct ifreq ifr = {\n+\t\t.ifr_name = \"lo\",\n+\t\t.ifr_flags = IFF_UP,\n+\t};\n+\tint fd, err, ret, val = 0;\n+\tchar buf[256];\n+\n+\tself-\u003epage_size = sysconf(_SC_PAGESIZE);\n+\tASSERT_GT(self-\u003epage_size, 0);\n+\n+\tif (unshare(CLONE_NEWNS | CLONE_NEWNET))\n+\t\tSKIP(return, \"Failed to unshare namespaces (need root)\");\n+\n+\t/* Make sure the mounts below do not propagate to the host. */\n+\tASSERT_EQ(mount(NULL, \"/\", NULL, MS_REC | MS_PRIVATE, NULL), 0);\n+\n+\tif (mount(\"none\", CG_TMP, \"tmpfs\", 0, NULL) ||\n+\t    mkdir(CG_MNT, 0755) ||\n+\t    mount(\"none\", CG_MNT, \"cgroup2\", 0, NULL))\n+\t\tSKIP(return, \"Failed to mount cgroup2\");\n+\n+\tASSERT_EQ(cg_get_current(self-\u003ecg_orig, sizeof(self-\u003ecg_orig)), 0);\n+\n+\t/* cg_test needs the memory controller to be enabled in the root of\n+\t * the hierarchy (which is not necessarily the global root cgroup\n+\t * when running in a cgroup namespace). Like selftests/cgroup, leave\n+\t * it enabled on exit: disabling it could break other users of the\n+\t * hierarchy.\n+\t */\n+\tASSERT_EQ(cg_read(CG_MNT, \"cgroup.subtree_control\", buf, sizeof(buf)), 0);\n+\tif (!strstr(buf, \"memory\") \u0026\u0026\n+\t    cg_write(CG_MNT, \"cgroup.subtree_control\", \"+memory\", 0))\n+\t\tSKIP(return, \"cgroup2 memory controller not available\");\n+\n+\t/* cg_test is a leaf cgroup, it can always host the test process. */\n+\tsnprintf(self-\u003ecg_test, sizeof(self-\u003ecg_test),\n+\t\t \"%s/ksft_so_reserve_mem_%d\", CG_MNT, getpid());\n+\tif (mkdir(self-\u003ecg_test, 0755))\n+\t\tSKIP(return, \"Failed to create test cgroup\");\n+\tself-\u003ecg_created = true;\n+\n+\tret = cg_enter(self-\u003ecg_test);\n+\tif (ret)\n+\t\tso_reserve_mem_teardown(_metadata, self, variant);\n+\tASSERT_EQ(ret, 0);\n+\n+\t/* Bring up loopback and verify memcg socket accounting is enabled */\n+\tfd = socket(AF_INET, SOCK_STREAM, 0);\n+\tret = fd \u003c 0 ? -1 : ioctl(fd, SIOCSIFFLAGS, \u0026ifr);\n+\tif (ret) {\n+\t\tif (fd \u003e= 0)\n+\t\t\tclose(fd);\n+\t\tso_reserve_mem_teardown(_metadata, self, variant);\n+\t}\n+\tASSERT_EQ(ret, 0);\n+\n+\tret = setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val));\n+\terr = errno;\n+\tclose(fd);\n+\tif (ret) {\n+\t\tso_reserve_mem_teardown(_metadata, self, variant);\n+\t\tif (err == EOPNOTSUPP)\n+\t\t\tSKIP(return, \"memcg socket accounting not enabled\");\n+\t}\n+\tASSERT_EQ(ret, 0);\n+}\n+\n+static void check_non_tcp_rejected(struct __test_metadata *_metadata,\n+\t\t\t\t   int domain, int type, int protocol,\n+\t\t\t\t   int val)\n+{\n+\tint fd = socket(domain, type, protocol);\n+\tint zero = 0;\n+\n+\tif (fd \u003c 0) {\n+\t\t/* Protocol not available, or no CAP_NET_RAW */\n+\t\tEXPECT_TRUE(errno == EAFNOSUPPORT ||\n+\t\t\t    errno == EPROTONOSUPPORT ||\n+\t\t\t    errno == ENOPROTOOPT ||\n+\t\t\t    errno == EPERM ||\n+\t\t\t    errno == EACCES);\n+\t\tTH_LOG(\"socket(%d, %d, %d): %s, skipped\",\n+\t\t       domain, type, protocol, strerror(errno));\n+\t\treturn;\n+\t}\n+\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026zero, sizeof(zero)), -1);\n+\tEXPECT_EQ(errno, EOPNOTSUPP);\n+\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), -1);\n+\tEXPECT_EQ(errno, EOPNOTSUPP);\n+\tclose(fd);\n+}\n+\n+TEST_F(so_reserve_mem, non_tcp_rejected)\n+{\n+\tint val = self-\u003epage_size * 4;\n+\n+\tcheck_non_tcp_rejected(_metadata, AF_INET, SOCK_DGRAM, 0, val);\n+\tcheck_non_tcp_rejected(_metadata, AF_UNIX, SOCK_STREAM, 0, val);\n+\tcheck_non_tcp_rejected(_metadata, AF_INET, SOCK_RAW, IPPROTO_ICMP, val);\n+\tcheck_non_tcp_rejected(_metadata, AF_INET, SOCK_STREAM, IPPROTO_MPTCP, val);\n+}\n+\n+TEST_F(so_reserve_mem, grow_shrink_and_rounding)\n+{\n+\tint ps = self-\u003epage_size;\n+\tint fd, val;\n+\n+\tfd = socket(AF_INET, SOCK_STREAM, 0);\n+\tASSERT_GE(fd, 0);\n+\n+\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 0);\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, fd), 0U);\n+\n+\t/* Negative or \u003e SO_RESERVE_MEM_MAX value -\u003e EINVAL */\n+\tval = -1;\n+\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), -1);\n+\tEXPECT_EQ(errno, EINVAL);\n+\n+\tval = SO_RESERVE_MEM_MAX + 1;\n+\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), -1);\n+\tEXPECT_EQ(errno, EINVAL);\n+\n+\tval = INT_MAX;\n+\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), -1);\n+\tEXPECT_EQ(errno, EINVAL);\n+\n+\t/* 1 byte rounds up to 1 page */\n+\tval = 1;\n+\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\n+\tEXPECT_EQ(get_reserve_mem(_metadata, fd), ps);\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, fd), (__u32)ps);\n+\n+\t/* Grow to 16 pages */\n+\tval = 16 * ps;\n+\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\n+\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 16 * ps);\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, fd), (__u32)(16 * ps));\n+\n+\t/* Shrink by 1 byte (rounds delta down to 0 -\u003e stays 16 pages) */\n+\tval = 16 * ps - 1;\n+\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\n+\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 16 * ps);\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, fd), (__u32)(16 * ps));\n+\n+\t/* Shrink to 4 pages */\n+\tval = 4 * ps;\n+\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\n+\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 4 * ps);\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, fd), (__u32)(4 * ps));\n+\n+\t/* Release all */\n+\tval = 0;\n+\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\n+\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 0);\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, fd), 0U);\n+\n+\tclose(fd);\n+}\n+\n+TEST_F(so_reserve_mem, cgroup_memory_max)\n+{\n+\tint ps = self-\u003epage_size;\n+\tchar buf[32];\n+\tint fd, val;\n+\n+\t/* The socket is charged to cg_test */\n+\tfd = socket(AF_INET, SOCK_STREAM, 0);\n+\tASSERT_GE(fd, 0);\n+\n+\t/* Move the test process back to its original cgroup before lowering\n+\t * cg_test's memory.max, so that the limit only governs the socket's\n+\t * memcg charges and cannot trigger OOM on the test process itself.\n+\t */\n+\tASSERT_EQ(cg_enter(self-\u003ecg_orig), 0);\n+\n+\t/* Limit cg_test memory to 8 pages and try to reserve 128 pages.\n+\t * O_NONBLOCK: do not try to reclaim cg_test usage above the new limit.\n+\t */\n+\tsnprintf(buf, sizeof(buf), \"%d\\n\", 8 * ps);\n+\tASSERT_EQ(cg_write(self-\u003ecg_test, \"memory.max\", buf, O_NONBLOCK), 0);\n+\n+\tval = 128 * ps;\n+\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), -1);\n+\tEXPECT_EQ(errno, ENOMEM);\n+\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 0);\n+\n+\t/* Restore unlimited memory.max */\n+\tASSERT_EQ(cg_write(self-\u003ecg_test, \"memory.max\", \"max\\n\", 0), 0);\n+\n+\tval = 4 * ps;\n+\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\n+\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 4 * ps);\n+\n+\tclose(fd);\n+}\n+\n+TEST_F(so_reserve_mem, accept_child_zero_reserve)\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};\n+\tsocklen_t alen = sizeof(addr);\n+\tint ps = self-\u003epage_size;\n+\tint lfd, cfd, sfd, val;\n+\n+\tlfd = socket(AF_INET, SOCK_STREAM, 0);\n+\tASSERT_GE(lfd, 0);\n+\n+\t/* Set SO_RESERVE_MEM on listener before listen() */\n+\tval = 4 * ps;\n+\tASSERT_EQ(setsockopt(lfd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\n+\tASSERT_EQ(bind(lfd, (struct sockaddr *)\u0026addr, sizeof(addr)), 0);\n+\tASSERT_EQ(listen(lfd, 2), 0);\n+\tASSERT_EQ(getsockname(lfd, (struct sockaddr *)\u0026addr, \u0026alen), 0);\n+\n+\t/* Grow SO_RESERVE_MEM on listener after listen() */\n+\tval = 8 * ps;\n+\tASSERT_EQ(setsockopt(lfd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\n+\tEXPECT_EQ(get_reserve_mem(_metadata, lfd), 8 * ps);\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, lfd), (__u32)(8 * ps));\n+\n+\tcfd = socket(AF_INET, SOCK_STREAM, 0);\n+\tASSERT_GE(cfd, 0);\n+\tASSERT_EQ(connect(cfd, (struct sockaddr *)\u0026addr, sizeof(addr)), 0);\n+\n+\tsfd = accept(lfd, NULL, NULL);\n+\tASSERT_GE(sfd, 0);\n+\n+\t/* Child after accept() must have 0 reserve while listener keeps 8 pages */\n+\tEXPECT_EQ(get_reserve_mem(_metadata, sfd), 0);\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, sfd), 0U);\n+\tEXPECT_EQ(get_reserve_mem(_metadata, lfd), 8 * ps);\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, lfd), (__u32)(8 * ps));\n+\n+\t/* Child can still independently set its own SO_RESERVE_MEM */\n+\tval = 6 * ps;\n+\tASSERT_EQ(setsockopt(sfd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\n+\tEXPECT_EQ(get_reserve_mem(_metadata, sfd), 6 * ps);\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, sfd), (__u32)(6 * ps));\n+\n+\tclose(sfd);\n+\tclose(cfd);\n+\tclose(lfd);\n+}\n+\n+TEST_F(so_reserve_mem, preserved_after_traffic)\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};\n+\tsocklen_t alen = sizeof(addr);\n+\tint ps = self-\u003epage_size;\n+\tint lfd, cfd, sfd, val;\n+\tchar buf[8192] = {};\n+\n+\tlfd = socket(AF_INET, SOCK_STREAM, 0);\n+\tASSERT_GE(lfd, 0);\n+\tASSERT_EQ(bind(lfd, (struct sockaddr *)\u0026addr, sizeof(addr)), 0);\n+\tASSERT_EQ(listen(lfd, 1), 0);\n+\tASSERT_EQ(getsockname(lfd, (struct sockaddr *)\u0026addr, \u0026alen), 0);\n+\n+\tcfd = socket(AF_INET, SOCK_STREAM, 0);\n+\tASSERT_GE(cfd, 0);\n+\tval = 16 * ps;\n+\tASSERT_EQ(setsockopt(cfd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\n+\tASSERT_EQ(connect(cfd, (struct sockaddr *)\u0026addr, sizeof(addr)), 0);\n+\n+\tsfd = accept(lfd, NULL, NULL);\n+\tASSERT_GE(sfd, 0);\n+\n+\t/* Send \u0026 drain traffic; cfd must retain its 16-page forward alloc,\n+\t * while sfd (0 reserve) reclaims its forward alloc back to 0.\n+\t */\n+\tASSERT_EQ(send(cfd, buf, sizeof(buf), 0), (ssize_t)sizeof(buf));\n+\tASSERT_EQ(recv(sfd, buf, sizeof(buf), MSG_WAITALL), (ssize_t)sizeof(buf));\n+\twait_wmem_drained(_metadata, cfd, 16 * ps);\n+\n+\tEXPECT_EQ(get_fwd_alloc(_metadata, sfd), 0U);\n+\n+\tclose(sfd);\n+\tclose(cfd);\n+\tclose(lfd);\n+}\n+\n+TEST_HARNESS_MAIN\n"
}

2/1 2026/10/02 15:32 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series exclusively modifies user-space selftests under tools/testing/selftests/net/ (adding so_reserve_mem.c and updating corresponding Makefile, config, and .gitignore files). It does not modify any kernel source code, headers, or modules affecting the compiled kernel image (vmlinux). Therefore, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


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

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 188dec0b070900c1879dffad30e82a16290ca6bb
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Oct 2 15:32:26 2026 +0000

    syz-cluster: applied patch under review

diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index dacd36ed8455e..21412510ac26b 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -41,6 +41,7 @@ skf_net_off
 socket
 so_incoming_cpu
 so_netns_cookie
+so_reserve_mem
 so_rcv_listener
 stress_reuseport_listen
 tap
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index cab3f2c90039f..54beea2e348c4 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -197,6 +197,7 @@ TEST_GEN_PROGS := \
 	sk_connect_zero_addr \
 	sk_so_peek_off \
 	so_incoming_cpu \
+	so_reserve_mem \
 	tap \
 	tcp_port_share \
 	tls \
diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config
index 737e7e6327b37..d355cf9805977 100644
--- a/tools/testing/selftests/net/config
+++ b/tools/testing/selftests/net/config
@@ -7,6 +7,7 @@ CONFIG_BRIDGE_VLAN_FILTERING=y
 CONFIG_CAN=m
 CONFIG_CAN_DEV=m
 CONFIG_CAN_VXCAN=m
+CONFIG_CGROUPS=y
 CONFIG_CRYPTO_ARIA=y
 CONFIG_CRYPTO_CHACHA20POLY1305=m
 CONFIG_CRYPTO_SHA1=y
@@ -61,6 +62,7 @@ CONFIG_L2TP_V3=y
 CONFIG_MACSEC=m
 CONFIG_MACVLAN=y
 CONFIG_MACVTAP=y
+CONFIG_MEMCG=y
 CONFIG_MPLS=y
 CONFIG_MPLS_IPTUNNEL=m
 CONFIG_MPLS_ROUTING=m
diff --git a/tools/testing/selftests/net/so_reserve_mem.c b/tools/testing/selftests/net/so_reserve_mem.c
new file mode 100644
index 0000000000000..aca5960ddff25
--- /dev/null
+++ b/tools/testing/selftests/net/so_reserve_mem.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/sock_diag.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "kselftest_harness.h"
+
+#ifndef IPPROTO_MPTCP
+#define IPPROTO_MPTCP 262
+#endif
+
+#define SO_RESERVE_MEM_MAX (1 << 30)
+
+/* cgroup2 is mounted here, on a tmpfs, in a private mount namespace. */
+#define CG_TMP "/tmp"
+#define CG_MNT CG_TMP "/cgroup2"
+
+static int cg_write(const char *dir, const char *file, const char *buf,
+		    int flags)
+{
+	ssize_t len = strlen(buf);
+	char path[PATH_MAX];
+	int fd, ret;
+
+	snprintf(path, sizeof(path), "%s/%s", dir, file);
+	fd = open(path, O_WRONLY | flags);
+	if (fd < 0)
+		return -1;
+	ret = write(fd, buf, len) == len ? 0 : -1;
+	close(fd);
+	return ret;
+}
+
+static int cg_read(const char *dir, const char *file, char *buf, size_t size)
+{
+	char path[PATH_MAX];
+	ssize_t n;
+	int fd;
+
+	snprintf(path, sizeof(path), "%s/%s", dir, file);
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return -1;
+	n = read(fd, buf, size - 1);
+	close(fd);
+	if (n < 0)
+		return -1;
+	buf[n] = '\0';
+	return 0;
+}
+
+static int cg_enter(const char *dir)
+{
+	char buf[32];
+
+	snprintf(buf, sizeof(buf), "%d\n", getpid());
+	return cg_write(dir, "cgroup.procs", buf, 0);
+}
+
+/* Path of the current cgroup, below CG_MNT. */
+static int cg_get_current(char *buf, size_t size)
+{
+	char line[PATH_MAX];
+	int ret = -1;
+	FILE *f;
+
+	f = fopen("/proc/self/cgroup", "r");
+	if (!f)
+		return -1;
+	while (fgets(line, sizeof(line), f)) {
+		if (strncmp(line, "0::", 3))
+			continue;
+		line[strcspn(line, "\n")] = '\0';
+		if (snprintf(buf, size, "%s%s", CG_MNT, line + 3) < (int)size)
+			ret = 0;
+		break;
+	}
+	fclose(f);
+	return ret;
+}
+
+static int get_reserve_mem(struct __test_metadata *_metadata, int fd)
+{
+	int val = -1;
+	socklen_t len = sizeof(val);
+
+	EXPECT_EQ(getsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, &len), 0);
+	return val;
+}
+
+static __u32 get_fwd_alloc(struct __test_metadata *_metadata, int fd)
+{
+	__u32 meminfo[SK_MEMINFO_VARS] = {};
+	socklen_t len = sizeof(meminfo);
+
+	EXPECT_EQ(getsockopt(fd, SOL_SOCKET, SO_MEMINFO, meminfo, &len), 0);
+	return meminfo[SK_MEMINFO_FWD_ALLOC];
+}
+
+/* Wait until a single SO_MEMINFO snapshot shows an empty write queue
+ * and at least @min_fwd_alloc bytes of forward alloc: ACK processing
+ * (possibly running on another CPU) first decrements sk_wmem_queued,
+ * then uncharges sk_forward_alloc.
+ */
+static void wait_wmem_drained(struct __test_metadata *_metadata, int fd,
+			      __u32 min_fwd_alloc)
+{
+	__u32 meminfo[SK_MEMINFO_VARS] = {};
+	socklen_t len;
+	int i;
+
+	for (i = 0; i < 5000; i++) {
+		len = sizeof(meminfo);
+		ASSERT_EQ(getsockopt(fd, SOL_SOCKET, SO_MEMINFO, meminfo, &len), 0);
+		if (meminfo[SK_MEMINFO_WMEM_QUEUED] == 0 &&
+		    meminfo[SK_MEMINFO_FWD_ALLOC] >= min_fwd_alloc)
+			return;
+		usleep(1000);
+	}
+	EXPECT_EQ(meminfo[SK_MEMINFO_WMEM_QUEUED], 0U);
+	EXPECT_GE(meminfo[SK_MEMINFO_FWD_ALLOC], min_fwd_alloc);
+}
+
+FIXTURE(so_reserve_mem)
+{
+	char cg_orig[PATH_MAX];	/* cgroup the test started in */
+	char cg_test[PATH_MAX];	/* cgroup the test sockets are charged to */
+	long page_size;
+	bool cg_created;
+};
+
+/* The cgroup2 mount lives in a private mount namespace which goes away
+ * with the test process, no need to unmount it.
+ */
+FIXTURE_TEARDOWN(so_reserve_mem)
+{
+	if (!self->cg_created)
+		return;
+
+	/* Leave cg_test so that it can be removed. */
+	EXPECT_EQ(cg_enter(self->cg_orig), 0);
+	EXPECT_EQ(rmdir(self->cg_test), 0);
+	self->cg_created = false;
+}
+
+FIXTURE_SETUP(so_reserve_mem)
+{
+	struct ifreq ifr = {
+		.ifr_name = "lo",
+		.ifr_flags = IFF_UP,
+	};
+	int fd, err, ret, val = 0;
+	char buf[256];
+
+	self->page_size = sysconf(_SC_PAGESIZE);
+	ASSERT_GT(self->page_size, 0);
+
+	if (unshare(CLONE_NEWNS | CLONE_NEWNET))
+		SKIP(return, "Failed to unshare namespaces (need root)");
+
+	/* Make sure the mounts below do not propagate to the host. */
+	ASSERT_EQ(mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL), 0);
+
+	if (mount("none", CG_TMP, "tmpfs", 0, NULL) ||
+	    mkdir(CG_MNT, 0755) ||
+	    mount("none", CG_MNT, "cgroup2", 0, NULL))
+		SKIP(return, "Failed to mount cgroup2");
+
+	ASSERT_EQ(cg_get_current(self->cg_orig, sizeof(self->cg_orig)), 0);
+
+	/* cg_test needs the memory controller to be enabled in the root of
+	 * the hierarchy (which is not necessarily the global root cgroup
+	 * when running in a cgroup namespace). Like selftests/cgroup, leave
+	 * it enabled on exit: disabling it could break other users of the
+	 * hierarchy.
+	 */
+	ASSERT_EQ(cg_read(CG_MNT, "cgroup.subtree_control", buf, sizeof(buf)), 0);
+	if (!strstr(buf, "memory") &&
+	    cg_write(CG_MNT, "cgroup.subtree_control", "+memory", 0))
+		SKIP(return, "cgroup2 memory controller not available");
+
+	/* cg_test is a leaf cgroup, it can always host the test process. */
+	snprintf(self->cg_test, sizeof(self->cg_test),
+		 "%s/ksft_so_reserve_mem_%d", CG_MNT, getpid());
+	if (mkdir(self->cg_test, 0755))
+		SKIP(return, "Failed to create test cgroup");
+	self->cg_created = true;
+
+	ret = cg_enter(self->cg_test);
+	if (ret)
+		so_reserve_mem_teardown(_metadata, self, variant);
+	ASSERT_EQ(ret, 0);
+
+	/* Bring up loopback and verify memcg socket accounting is enabled */
+	fd = socket(AF_INET, SOCK_STREAM, 0);
+	ret = fd < 0 ? -1 : ioctl(fd, SIOCSIFFLAGS, &ifr);
+	if (ret) {
+		if (fd >= 0)
+			close(fd);
+		so_reserve_mem_teardown(_metadata, self, variant);
+	}
+	ASSERT_EQ(ret, 0);
+
+	ret = setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val));
+	err = errno;
+	close(fd);
+	if (ret) {
+		so_reserve_mem_teardown(_metadata, self, variant);
+		if (err == EOPNOTSUPP)
+			SKIP(return, "memcg socket accounting not enabled");
+	}
+	ASSERT_EQ(ret, 0);
+}
+
+static void check_non_tcp_rejected(struct __test_metadata *_metadata,
+				   int domain, int type, int protocol,
+				   int val)
+{
+	int fd = socket(domain, type, protocol);
+	int zero = 0;
+
+	if (fd < 0) {
+		/* Protocol not available, or no CAP_NET_RAW */
+		EXPECT_TRUE(errno == EAFNOSUPPORT ||
+			    errno == EPROTONOSUPPORT ||
+			    errno == ENOPROTOOPT ||
+			    errno == EPERM ||
+			    errno == EACCES);
+		TH_LOG("socket(%d, %d, %d): %s, skipped",
+		       domain, type, protocol, strerror(errno));
+		return;
+	}
+	EXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &zero, sizeof(zero)), -1);
+	EXPECT_EQ(errno, EOPNOTSUPP);
+	EXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), -1);
+	EXPECT_EQ(errno, EOPNOTSUPP);
+	close(fd);
+}
+
+TEST_F(so_reserve_mem, non_tcp_rejected)
+{
+	int val = self->page_size * 4;
+
+	check_non_tcp_rejected(_metadata, AF_INET, SOCK_DGRAM, 0, val);
+	check_non_tcp_rejected(_metadata, AF_UNIX, SOCK_STREAM, 0, val);
+	check_non_tcp_rejected(_metadata, AF_INET, SOCK_RAW, IPPROTO_ICMP, val);
+	check_non_tcp_rejected(_metadata, AF_INET, SOCK_STREAM, IPPROTO_MPTCP, val);
+}
+
+TEST_F(so_reserve_mem, grow_shrink_and_rounding)
+{
+	int ps = self->page_size;
+	int fd, val;
+
+	fd = socket(AF_INET, SOCK_STREAM, 0);
+	ASSERT_GE(fd, 0);
+
+	EXPECT_EQ(get_reserve_mem(_metadata, fd), 0);
+	EXPECT_EQ(get_fwd_alloc(_metadata, fd), 0U);
+
+	/* Negative or > SO_RESERVE_MEM_MAX value -> EINVAL */
+	val = -1;
+	EXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), -1);
+	EXPECT_EQ(errno, EINVAL);
+
+	val = SO_RESERVE_MEM_MAX + 1;
+	EXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), -1);
+	EXPECT_EQ(errno, EINVAL);
+
+	val = INT_MAX;
+	EXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), -1);
+	EXPECT_EQ(errno, EINVAL);
+
+	/* 1 byte rounds up to 1 page */
+	val = 1;
+	ASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), 0);
+	EXPECT_EQ(get_reserve_mem(_metadata, fd), ps);
+	EXPECT_EQ(get_fwd_alloc(_metadata, fd), (__u32)ps);
+
+	/* Grow to 16 pages */
+	val = 16 * ps;
+	ASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), 0);
+	EXPECT_EQ(get_reserve_mem(_metadata, fd), 16 * ps);
+	EXPECT_EQ(get_fwd_alloc(_metadata, fd), (__u32)(16 * ps));
+
+	/* Shrink by 1 byte (rounds delta down to 0 -> stays 16 pages) */
+	val = 16 * ps - 1;
+	ASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), 0);
+	EXPECT_EQ(get_reserve_mem(_metadata, fd), 16 * ps);
+	EXPECT_EQ(get_fwd_alloc(_metadata, fd), (__u32)(16 * ps));
+
+	/* Shrink to 4 pages */
+	val = 4 * ps;
+	ASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), 0);
+	EXPECT_EQ(get_reserve_mem(_metadata, fd), 4 * ps);
+	EXPECT_EQ(get_fwd_alloc(_metadata, fd), (__u32)(4 * ps));
+
+	/* Release all */
+	val = 0;
+	ASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), 0);
+	EXPECT_EQ(get_reserve_mem(_metadata, fd), 0);
+	EXPECT_EQ(get_fwd_alloc(_metadata, fd), 0U);
+
+	close(fd);
+}
+
+TEST_F(so_reserve_mem, cgroup_memory_max)
+{
+	int ps = self->page_size;
+	char buf[32];
+	int fd, val;
+
+	/* The socket is charged to cg_test */
+	fd = socket(AF_INET, SOCK_STREAM, 0);
+	ASSERT_GE(fd, 0);
+
+	/* Move the test process back to its original cgroup before lowering
+	 * cg_test's memory.max, so that the limit only governs the socket's
+	 * memcg charges and cannot trigger OOM on the test process itself.
+	 */
+	ASSERT_EQ(cg_enter(self->cg_orig), 0);
+
+	/* Limit cg_test memory to 8 pages and try to reserve 128 pages.
+	 * O_NONBLOCK: do not try to reclaim cg_test usage above the new limit.
+	 */
+	snprintf(buf, sizeof(buf), "%d\n", 8 * ps);
+	ASSERT_EQ(cg_write(self->cg_test, "memory.max", buf, O_NONBLOCK), 0);
+
+	val = 128 * ps;
+	EXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), -1);
+	EXPECT_EQ(errno, ENOMEM);
+	EXPECT_EQ(get_reserve_mem(_metadata, fd), 0);
+
+	/* Restore unlimited memory.max */
+	ASSERT_EQ(cg_write(self->cg_test, "memory.max", "max\n", 0), 0);
+
+	val = 4 * ps;
+	ASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), 0);
+	EXPECT_EQ(get_reserve_mem(_metadata, fd), 4 * ps);
+
+	close(fd);
+}
+
+TEST_F(so_reserve_mem, accept_child_zero_reserve)
+{
+	struct sockaddr_in addr = {
+		.sin_family = AF_INET,
+		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+	};
+	socklen_t alen = sizeof(addr);
+	int ps = self->page_size;
+	int lfd, cfd, sfd, val;
+
+	lfd = socket(AF_INET, SOCK_STREAM, 0);
+	ASSERT_GE(lfd, 0);
+
+	/* Set SO_RESERVE_MEM on listener before listen() */
+	val = 4 * ps;
+	ASSERT_EQ(setsockopt(lfd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), 0);
+	ASSERT_EQ(bind(lfd, (struct sockaddr *)&addr, sizeof(addr)), 0);
+	ASSERT_EQ(listen(lfd, 2), 0);
+	ASSERT_EQ(getsockname(lfd, (struct sockaddr *)&addr, &alen), 0);
+
+	/* Grow SO_RESERVE_MEM on listener after listen() */
+	val = 8 * ps;
+	ASSERT_EQ(setsockopt(lfd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), 0);
+	EXPECT_EQ(get_reserve_mem(_metadata, lfd), 8 * ps);
+	EXPECT_EQ(get_fwd_alloc(_metadata, lfd), (__u32)(8 * ps));
+
+	cfd = socket(AF_INET, SOCK_STREAM, 0);
+	ASSERT_GE(cfd, 0);
+	ASSERT_EQ(connect(cfd, (struct sockaddr *)&addr, sizeof(addr)), 0);
+
+	sfd = accept(lfd, NULL, NULL);
+	ASSERT_GE(sfd, 0);
+
+	/* Child after accept() must have 0 reserve while listener keeps 8 pages */
+	EXPECT_EQ(get_reserve_mem(_metadata, sfd), 0);
+	EXPECT_EQ(get_fwd_alloc(_metadata, sfd), 0U);
+	EXPECT_EQ(get_reserve_mem(_metadata, lfd), 8 * ps);
+	EXPECT_EQ(get_fwd_alloc(_metadata, lfd), (__u32)(8 * ps));
+
+	/* Child can still independently set its own SO_RESERVE_MEM */
+	val = 6 * ps;
+	ASSERT_EQ(setsockopt(sfd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), 0);
+	EXPECT_EQ(get_reserve_mem(_metadata, sfd), 6 * ps);
+	EXPECT_EQ(get_fwd_alloc(_metadata, sfd), (__u32)(6 * ps));
+
+	close(sfd);
+	close(cfd);
+	close(lfd);
+}
+
+TEST_F(so_reserve_mem, preserved_after_traffic)
+{
+	struct sockaddr_in addr = {
+		.sin_family = AF_INET,
+		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+	};
+	socklen_t alen = sizeof(addr);
+	int ps = self->page_size;
+	int lfd, cfd, sfd, val;
+	char buf[8192] = {};
+
+	lfd = socket(AF_INET, SOCK_STREAM, 0);
+	ASSERT_GE(lfd, 0);
+	ASSERT_EQ(bind(lfd, (struct sockaddr *)&addr, sizeof(addr)), 0);
+	ASSERT_EQ(listen(lfd, 1), 0);
+	ASSERT_EQ(getsockname(lfd, (struct sockaddr *)&addr, &alen), 0);
+
+	cfd = socket(AF_INET, SOCK_STREAM, 0);
+	ASSERT_GE(cfd, 0);
+	val = 16 * ps;
+	ASSERT_EQ(setsockopt(cfd, SOL_SOCKET, SO_RESERVE_MEM, &val, sizeof(val)), 0);
+	ASSERT_EQ(connect(cfd, (struct sockaddr *)&addr, sizeof(addr)), 0);
+
+	sfd = accept(lfd, NULL, NULL);
+	ASSERT_GE(sfd, 0);
+
+	/* Send & drain traffic; cfd must retain its 16-page forward alloc,
+	 * while sfd (0 reserve) reclaims its forward alloc back to 0.
+	 */
+	ASSERT_EQ(send(cfd, buf, sizeof(buf), 0), (ssize_t)sizeof(buf));
+	ASSERT_EQ(recv(sfd, buf, sizeof(buf), MSG_WAITALL), (ssize_t)sizeof(buf));
+	wait_wmem_drained(_metadata, cfd, 16 * ps);
+
+	EXPECT_EQ(get_fwd_alloc(_metadata, sfd), 0U);
+
+	close(sfd);
+	close(cfd);
+	close(lfd);
+}
+
+TEST_HARNESS_MAIN

3/2 2026/10/02 15:32 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 9056
							output: 21
							thoughts: 791
						

Thoughts:
**Analyzing the Diff for WorthFuzzing Determination**

Okay, let's break this down. First, I see the diff shows modifications to files within `tools/testing/selftests/net/`. Specifically, I'm looking at `.gitignore`, `Makefile`, `config`, and `so_reserve_mem.c`. The first thing I need to confirm is whether this is the *entire* set of changes in the HEAD commit. I'm going to leverage the tools available, like `grepper` or `read-file`, to verify this is *the* commit in the repository. The prompt helpfully informs that the diff represents the changes in the `HEAD` commit, which is `188dec0b070900c1879dffad30e82a16290ca6bb`.

Now, before jumping to conclusions, I need to thoroughly assess if this change impacts anything *outside* of `tools/testing/selftests/net/`. The key is determining if these changes are confined to the user-space self-tests, which would mean `WorthFuzzing=false` per the prompt's criteria. I'll focus on the second section in the prompt regarding returning `WorthFuzzing=false`. The relevant criteria are modifications to user-space directories, self-tests, samples or scripts, that *do not* affect the compiled kernel image or modules.

I'm also considering the possibility that `so_reserve_mem.c` might be related to other commits. The introduction of `SO_RESERVE_MEM` could be part of a series. I'll need to use `grepper` to search for `SO_RESERVE_MEM` across the entire kernel source code to determine if it exists, and if so, how it has been introduced or referenced. This should help to know the impact of this particular change.



4/2 2026/10/02 15:32 tool grepper
0m Args:
{
  "Expression": "SO_RESERVE_MEM"
}

Results:
{
  "Output": "arch/alpha/include/uapi/asm/socket.h-133-\narch/alpha/include/uapi/asm/socket.h:134:#define SO_RESERVE_MEM\t\t73\narch/alpha/include/uapi/asm/socket.h-135-\n--\narch/mips/include/uapi/asm/socket.h-144-\narch/mips/include/uapi/asm/socket.h:145:#define SO_RESERVE_MEM\t\t73\narch/mips/include/uapi/asm/socket.h-146-\n--\narch/parisc/include/uapi/asm/socket.h-125-\narch/parisc/include/uapi/asm/socket.h:126:#define SO_RESERVE_MEM\t\t0x4047\narch/parisc/include/uapi/asm/socket.h-127-\n--\narch/sparc/include/uapi/asm/socket.h-126-\narch/sparc/include/uapi/asm/socket.h:127:#define SO_RESERVE_MEM           0x0052\narch/sparc/include/uapi/asm/socket.h-128-\n--\ninclude/uapi/asm-generic/socket.h-128-\ninclude/uapi/asm-generic/socket.h:129:#define SO_RESERVE_MEM\t\t73\ninclude/uapi/asm-generic/socket.h-130-\n--\nnet/core/sock.c=1195=int sk_setsockopt(struct sock *sk, int level, int optname,\n--\nnet/core/sock.c-1659-\nnet/core/sock.c:1660:\tcase SO_RESERVE_MEM:\nnet/core/sock.c-1661-\t{\n--\nnet/core/sock.c=1732=int sk_getsockopt(struct sock *sk, int level, int optname,\n--\nnet/core/sock.c-2150-\nnet/core/sock.c:2151:\tcase SO_RESERVE_MEM:\nnet/core/sock.c-2152-\t\tv.val = READ_ONCE(sk-\u003esk_reserved_mem);\n--\ntools/include/uapi/asm-generic/socket.h-128-\ntools/include/uapi/asm-generic/socket.h:129:#define SO_RESERVE_MEM\t\t73\ntools/include/uapi/asm-generic/socket.h-130-\n--\ntools/testing/selftests/net/so_reserve_mem.c-25-\ntools/testing/selftests/net/so_reserve_mem.c:26:#define SO_RESERVE_MEM_MAX (1 \u003c\u003c 30)\ntools/testing/selftests/net/so_reserve_mem.c-27-\n--\ntools/testing/selftests/net/so_reserve_mem.c=96=static int get_reserve_mem(struct __test_metadata *_metadata, int fd)\n--\ntools/testing/selftests/net/so_reserve_mem.c-100-\ntools/testing/selftests/net/so_reserve_mem.c:101:\tEXPECT_EQ(getsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, \u0026len), 0);\ntools/testing/selftests/net/so_reserve_mem.c-102-\treturn val;\n--\ntools/testing/selftests/net/so_reserve_mem.c=160=FIXTURE_SETUP(so_reserve_mem)\n--\ntools/testing/selftests/net/so_reserve_mem.c-217-\ntools/testing/selftests/net/so_reserve_mem.c:218:\tret = setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val));\ntools/testing/selftests/net/so_reserve_mem.c-219-\terr = errno;\n--\ntools/testing/selftests/net/so_reserve_mem.c=229=static void check_non_tcp_rejected(struct __test_metadata *_metadata,\n--\ntools/testing/selftests/net/so_reserve_mem.c-246-\t}\ntools/testing/selftests/net/so_reserve_mem.c:247:\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026zero, sizeof(zero)), -1);\ntools/testing/selftests/net/so_reserve_mem.c-248-\tEXPECT_EQ(errno, EOPNOTSUPP);\ntools/testing/selftests/net/so_reserve_mem.c:249:\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), -1);\ntools/testing/selftests/net/so_reserve_mem.c-250-\tEXPECT_EQ(errno, EOPNOTSUPP);\n--\ntools/testing/selftests/net/so_reserve_mem.c=264=TEST_F(so_reserve_mem, grow_shrink_and_rounding)\n--\ntools/testing/selftests/net/so_reserve_mem.c-274-\ntools/testing/selftests/net/so_reserve_mem.c:275:\t/* Negative or \u003e SO_RESERVE_MEM_MAX value -\u003e EINVAL */\ntools/testing/selftests/net/so_reserve_mem.c-276-\tval = -1;\ntools/testing/selftests/net/so_reserve_mem.c:277:\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), -1);\ntools/testing/selftests/net/so_reserve_mem.c-278-\tEXPECT_EQ(errno, EINVAL);\ntools/testing/selftests/net/so_reserve_mem.c-279-\ntools/testing/selftests/net/so_reserve_mem.c:280:\tval = SO_RESERVE_MEM_MAX + 1;\ntools/testing/selftests/net/so_reserve_mem.c:281:\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), -1);\ntools/testing/selftests/net/so_reserve_mem.c-282-\tEXPECT_EQ(errno, EINVAL);\n--\ntools/testing/selftests/net/so_reserve_mem.c-284-\tval = INT_MAX;\ntools/testing/selftests/net/so_reserve_mem.c:285:\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), -1);\ntools/testing/selftests/net/so_reserve_mem.c-286-\tEXPECT_EQ(errno, EINVAL);\n--\ntools/testing/selftests/net/so_reserve_mem.c-289-\tval = 1;\ntools/testing/selftests/net/so_reserve_mem.c:290:\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\ntools/testing/selftests/net/so_reserve_mem.c-291-\tEXPECT_EQ(get_reserve_mem(_metadata, fd), ps);\n--\ntools/testing/selftests/net/so_reserve_mem.c-295-\tval = 16 * ps;\ntools/testing/selftests/net/so_reserve_mem.c:296:\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\ntools/testing/selftests/net/so_reserve_mem.c-297-\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 16 * ps);\n--\ntools/testing/selftests/net/so_reserve_mem.c-301-\tval = 16 * ps - 1;\ntools/testing/selftests/net/so_reserve_mem.c:302:\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\ntools/testing/selftests/net/so_reserve_mem.c-303-\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 16 * ps);\n--\ntools/testing/selftests/net/so_reserve_mem.c-307-\tval = 4 * ps;\ntools/testing/selftests/net/so_reserve_mem.c:308:\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\ntools/testing/selftests/net/so_reserve_mem.c-309-\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 4 * ps);\n--\ntools/testing/selftests/net/so_reserve_mem.c-313-\tval = 0;\ntools/testing/selftests/net/so_reserve_mem.c:314:\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\ntools/testing/selftests/net/so_reserve_mem.c-315-\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 0);\n--\ntools/testing/selftests/net/so_reserve_mem.c=321=TEST_F(so_reserve_mem, cgroup_memory_max)\n--\ntools/testing/selftests/net/so_reserve_mem.c-343-\tval = 128 * ps;\ntools/testing/selftests/net/so_reserve_mem.c:344:\tEXPECT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), -1);\ntools/testing/selftests/net/so_reserve_mem.c-345-\tEXPECT_EQ(errno, ENOMEM);\n--\ntools/testing/selftests/net/so_reserve_mem.c-351-\tval = 4 * ps;\ntools/testing/selftests/net/so_reserve_mem.c:352:\tASSERT_EQ(setsockopt(fd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\ntools/testing/selftests/net/so_reserve_mem.c-353-\tEXPECT_EQ(get_reserve_mem(_metadata, fd), 4 * ps);\n--\ntools/testing/selftests/net/so_reserve_mem.c=358=TEST_F(so_reserve_mem, accept_child_zero_reserve)\n--\ntools/testing/selftests/net/so_reserve_mem.c-370-\ntools/testing/selftests/net/so_reserve_mem.c:371:\t/* Set SO_RESERVE_MEM on listener before listen() */\ntools/testing/selftests/net/so_reserve_mem.c-372-\tval = 4 * ps;\ntools/testing/selftests/net/so_reserve_mem.c:373:\tASSERT_EQ(setsockopt(lfd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\ntools/testing/selftests/net/so_reserve_mem.c-374-\tASSERT_EQ(bind(lfd, (struct sockaddr *)\u0026addr, sizeof(addr)), 0);\n--\ntools/testing/selftests/net/so_reserve_mem.c-377-\ntools/testing/selftests/net/so_reserve_mem.c:378:\t/* Grow SO_RESERVE_MEM on listener after listen() */\ntools/testing/selftests/net/so_reserve_mem.c-379-\tval = 8 * ps;\ntools/testing/selftests/net/so_reserve_mem.c:380:\tASSERT_EQ(setsockopt(lfd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\ntools/testing/selftests/net/so_reserve_mem.c-381-\tEXPECT_EQ(get_reserve_mem(_metadata, lfd), 8 * ps);\n--\ntools/testing/selftests/net/so_reserve_mem.c-396-\ntools/testing/selftests/net/so_reserve_mem.c:397:\t/* Child can still independently set its own SO_RESERVE_MEM */\ntools/testing/selftests/net/so_reserve_mem.c-398-\tval = 6 * ps;\ntools/testing/selftests/net/so_reserve_mem.c:399:\tASSERT_EQ(setsockopt(sfd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\ntools/testing/selftests/net/so_reserve_mem.c-400-\tEXPECT_EQ(get_reserve_mem(_metadata, sfd), 6 * ps);\n--\ntools/testing/selftests/net/so_reserve_mem.c=408=TEST_F(so_reserve_mem, preserved_after_traffic)\n--\ntools/testing/selftests/net/so_reserve_mem.c-427-\tval = 16 * ps;\ntools/testing/selftests/net/so_reserve_mem.c:428:\tASSERT_EQ(setsockopt(cfd, SOL_SOCKET, SO_RESERVE_MEM, \u0026val, sizeof(val)), 0);\ntools/testing/selftests/net/so_reserve_mem.c-429-\tASSERT_EQ(connect(cfd, (struct sockaddr *)\u0026addr, sizeof(addr)), 0);\n"
}

5/2 2026/10/02 15:32 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 13832
							output: 106
							thoughts: 1761
						

Thoughts:
**Fuzzing Verdict: WorthFuzzing = False**

Okay, first things first, I see `SO_RESERVE_MEM` mentioned. Wait a minute, isn't that in `net/core/sock.c`? That's what initially came to mind, but the instructions are pretty clear: *the changes have ALREADY been applied and are the HEAD commit*.  We're looking at what's *there*, not what might have been. So, what's in the commit? The provided diff points to HEAD, which includes `tools/testing/selftests/net/.gitignore`, `Makefile`, `config`, and `so_reserve_mem.c`.

Aha! This is a self-test, contained within `tools/testing/selftests/net/`. The prompt clearly states that the changes *are* committed as the HEAD, which makes the patch under review *the* HEAD commit. No need to dig through commit history looking for the kernel patch. It's the base.

The critical section states to return `WorthFuzzing=false` if all changes are in non-kernel and non-executable areas. This includes self-tests, which are, as described, *user-space directories* that do not affect the compiled kernel image or modules. This new commit only changes the self-test directory.

Specifically, the files modified are: `.gitignore`, `Makefile`, `config`, and the new test `so_reserve_mem.c`.  All are within `tools/testing/selftests/net/`. Absolutely no kernel code (`vmlinux` or modules) is touched.

So, according to the provided criteria, and my understanding of syzkaller triage, this clearly falls under the negative criteria.

Now, because `WorthFuzzing` is `false`, `EnableConfigs` and `FocusSymbols` will be empty arrays: `[]`.  The reasoning is clear: the changes are *exclusively* in the selftest directory, adding a test and updating related files. It does not affect the kernel image or modules.

I'm confident in this assessment. Let's set the results and move on.



6/2 2026/10/02 15:32 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch series exclusively modifies user-space selftests under tools/testing/selftests/net/ (adding so_reserve_mem.c and updating corresponding Makefile, config, and .gitignore files). It does not modify any kernel source code, headers, or modules affecting the compiled kernel image (vmlinux). Therefore, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series exclusively modifies user-space selftests under tools/testing/selftests/net/ (adding so_reserve_mem.c and updating corresponding Makefile, config, and .gitignore files). It does not modify any kernel source code, headers, or modules affecting the compiled kernel image (vmlinux). Therefore, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

7/1 2026/10/02 15:32 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

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)