| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/01 02:29 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"IP_VS"
],
"FocusSymbols": [
"ip_vs_conn_change_fwd_mask",
"ip_vs_bind_dest",
"ip_vs_conn_fill_cport"
],
"Reasoning": "The patch modifies the core connection tracking logic in IPVS (IP Virtual Server), specifically changing how connection hash tables are locked and how forwarding methods are updated for hashed connections. This is reachable functional code that handles network load balancing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/01 02:29 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 27e201e3e8fb35abe6db3bf40403058ff0ce7e69\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Jul 1 02:29:03 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c\nindex e76a73d183d531..53a7de3a9f2bf9 100644\n--- a/net/netfilter/ipvs/ip_vs_conn.c\n+++ b/net/netfilter/ipvs/ip_vs_conn.c\n@@ -70,25 +70,47 @@ static struct kmem_cache *ip_vs_conn_cachep __read_mostly;\n * bucket or hash table\n * - hash table resize works like rehash but always rehashes into new table\n * - bit lock on bucket serializes all operations that modify the chain\n+ * - on resize, bucket from the old table is locked before bucket from the\n+ * new table\n * - cp-\u003elock protects conn fields like cp-\u003eflags, cp-\u003edest\n */\n \n-/* Lock conn_tab bucket for conn hash/unhash, not for rehash */\n+/**\n+ * conn_tab_lock - Lock conn_tab buckets for conn hash/unhash, not for rehash\n+ * @t:\t\thash table for hn0\n+ * @t2:\thash table for hn1\n+ * @cp:\tconnection\n+ * @hash_key:\thash key for hn0\n+ * @hash_key2:\thash key for hn1\n+ * @use2:\tusing hn1 (double hashing) based on the forwarding method\n+ * @new_hash:\tmode for hn0, hash node (true) or seek node (false)\n+ * @new_hash2:\tmode for hn1, hash node (true) or seek node (false)\n+ * @head_ret:\treturned head for hn0\n+ * @head2_ret:\treturned head for hn1\n+ *\n+ * We support 3 modes:\n+ * - seek mode for both nodes, used for unhashing\n+ * - hash mode for both nodes, used for hashing\n+ * - seek hn0 and hash hn1, used when forwarding method is changed\n+ */\n static __always_inline void\n-conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,\n-\t u32 hash_key2, bool use2, bool new_hash,\n-\t struct hlist_bl_head **head_ret, struct hlist_bl_head **head2_ret)\n+conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_rht *t2, struct ip_vs_conn *cp,\n+\t u32 hash_key, u32 hash_key2, bool use2, bool new_hash,\n+\t bool new_hash2, struct hlist_bl_head **head_ret,\n+\t struct hlist_bl_head **head2_ret)\n {\n \tstruct hlist_bl_head *head, *head2;\n \tu32 hash_key_new, hash_key_new2;\n-\tstruct ip_vs_rht *t2 = t;\n \tu32 idx, idx2;\n \n \tidx = hash_key \u0026 t-\u003emask;\n-\tif (use2)\n-\t\tidx2 = hash_key2 \u0026 t-\u003emask;\n-\telse\n-\t\tidx2 = idx;\n+\tidx2 = hash_key2 \u0026 t-\u003emask;\n+\t/* Advance idx2 when new_hash is not set but hash_key2\n+\t * is for new table\n+\t */\n+\tif (new_hash2 \u0026\u0026 use2 \u0026\u0026 t != t2)\n+\t\tidx2 |= IP_VS_RHT_TABLE_ID_MASK;\n+\n \tif (!new_hash) {\n \t\t/* We need to lock the bucket in the right table */\n \n@@ -103,24 +125,19 @@ conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,\n \t\t\tidx = hash_key \u0026 t-\u003emask;\n \t\t\tidx |= IP_VS_RHT_TABLE_ID_MASK;\n \t\t}\n-\t\tif (use2) {\n-\t\t\tif (!ip_vs_rht_same_table(t2, hash_key2)) {\n-\t\t\t\t/* It is already moved to new table */\n-\t\t\t\tt2 = rcu_dereference(t2-\u003enew_tbl);\n-\t\t\t\tidx2 = hash_key2 \u0026 t2-\u003emask;\n-\t\t\t\tidx2 |= IP_VS_RHT_TABLE_ID_MASK;\n-\t\t\t}\n-\t\t} else {\n-\t\t\tidx2 = idx;\n-\t\t}\n+\t}\n+\tif (use2 \u0026\u0026 !new_hash2 \u0026\u0026 !ip_vs_rht_same_table(t2, hash_key2)) {\n+\t\t/* It is already moved to new table */\n+\t\tt2 = rcu_dereference(t2-\u003enew_tbl);\n+\t\tidx2 = hash_key2 \u0026 t2-\u003emask;\n+\t\tidx2 |= IP_VS_RHT_TABLE_ID_MASK;\n \t}\n \n+\tif (!use2)\n+\t\tidx2 = idx;\n \thead = t-\u003ebuckets + (hash_key \u0026 t-\u003emask);\n \thead2 = use2 ? t2-\u003ebuckets + (hash_key2 \u0026 t2-\u003emask) : head;\n \n-\tlocal_bh_disable();\n-\t/* Do not touch seqcount, this is a safe operation */\n-\n \tif (idx \u003c= idx2) {\n \t\thlist_bl_lock(head);\n \t\tif (head != head2)\n@@ -130,16 +147,22 @@ conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,\n \t\thlist_bl_lock(head);\n \t}\n \tif (!new_hash) {\n+\t\tbool changed;\n+\n \t\t/* Ensure hash_key is read under lock */\n \t\thash_key_new = READ_ONCE(cp-\u003ehn0.hash_key);\n-\t\thash_key_new2 = READ_ONCE(cp-\u003ehn1.hash_key);\n+\t\tchanged = hash_key != hash_key_new;\n+\t\tif (use2 \u0026\u0026 !new_hash2) {\n+\t\t\thash_key_new2 = READ_ONCE(cp-\u003ehn1.hash_key);\n+\t\t\tchanged |= hash_key2 != hash_key_new2;\n+\t\t} else {\n+\t\t\thash_key_new2 = hash_key2;\n+\t\t}\n \t\t/* Hash changed ? */\n-\t\tif (hash_key != hash_key_new ||\n-\t\t (hash_key2 != hash_key_new2 \u0026\u0026 use2)) {\n+\t\tif (changed) {\n \t\t\tif (head != head2)\n \t\t\t\thlist_bl_unlock(head2);\n \t\t\thlist_bl_unlock(head);\n-\t\t\tlocal_bh_enable();\n \t\t\thash_key = hash_key_new;\n \t\t\thash_key2 = hash_key_new2;\n \t\t\tgoto retry;\n@@ -155,7 +178,6 @@ static inline void conn_tab_unlock(struct hlist_bl_head *head,\n \tif (head != head2)\n \t\thlist_bl_unlock(head2);\n \thlist_bl_unlock(head);\n-\tlocal_bh_enable();\n }\n \n static void ip_vs_conn_expire(struct timer_list *t);\n@@ -268,8 +290,9 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)\n \t\tuse2 = false;\n \t}\n \n-\tconn_tab_lock(t, cp, hash_key, hash_key2, use2, true /* new_hash */,\n-\t\t \u0026head, \u0026head2);\n+\tlocal_bh_disable();\n+\tconn_tab_lock(t, t, cp, hash_key, hash_key2, use2, true /* new_hash */,\n+\t\t true /* new_hash2 */, \u0026head, \u0026head2);\n \n \tcp-\u003eflags |= IP_VS_CONN_F_HASHED;\n \tWRITE_ONCE(cp-\u003ehn0.hash_key, hash_key);\n@@ -280,6 +303,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)\n \t\thlist_bl_add_head_rcu(\u0026cp-\u003ehn1.node, head2);\n \n \tconn_tab_unlock(head, head2);\n+\tlocal_bh_enable();\n \tret = 1;\n \n \t/* Schedule resizing if load increases */\n@@ -306,18 +330,20 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)\n \t\treturn refcount_dec_if_one(\u0026cp-\u003erefcnt);\n \n \trcu_read_lock();\n+\tlocal_bh_disable();\n \n \tt = rcu_dereference(ipvs-\u003econn_tab);\n \thash_key = READ_ONCE(cp-\u003ehn0.hash_key);\n \thash_key2 = READ_ONCE(cp-\u003ehn1.hash_key);\n \tuse2 = ip_vs_conn_use_hash2(cp);\n \n-\tconn_tab_lock(t, cp, hash_key, hash_key2, use2, false /* new_hash */,\n-\t\t \u0026head, \u0026head2);\n+\tconn_tab_lock(t, t, cp, hash_key, hash_key2, use2, false /* new_hash */,\n+\t\t false /* new_hash2 */, \u0026head, \u0026head2);\n \n \tif (cp-\u003eflags \u0026 IP_VS_CONN_F_HASHED) {\n \t\t/* Decrease refcnt and unlink conn only if we are last user */\n-\t\tif (refcount_dec_if_one(\u0026cp-\u003erefcnt)) {\n+\t\tif (use2 == ip_vs_conn_use_hash2(cp) \u0026\u0026\n+\t\t refcount_dec_if_one(\u0026cp-\u003erefcnt)) {\n \t\t\thlist_bl_del_rcu(\u0026cp-\u003ehn0.node);\n \t\t\tif (use2)\n \t\t\t\thlist_bl_del_rcu(\u0026cp-\u003ehn1.node);\n@@ -328,6 +354,7 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)\n \n \tconn_tab_unlock(head, head2);\n \n+\tlocal_bh_enable();\n \trcu_read_unlock();\n \n \treturn ret;\n@@ -686,6 +713,16 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)\n \t/* Protect the cp-\u003eflags modification */\n \tspin_lock_bh(\u0026cp-\u003elock);\n \n+\t/* Recheck the forwarding method under lock */\n+\tif (use2 != ip_vs_conn_use_hash2(cp)) {\n+\t\tspin_unlock_bh(\u0026cp-\u003elock);\n+\t\tuse2 = !use2;\n+\t\tif (dir) {\n+\t\t\tdir--;\n+\t\t\tgoto next_dir;\n+\t\t}\n+\t}\n+\n \t/* Lock seqcount only for the old bucket, even if we are on new table\n \t * because it affects the del operation, not the adding.\n \t */\n@@ -752,6 +789,60 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)\n \t\tgoto next_dir;\n }\n \n+/* Change forwarding method for hashed conn */\n+static void ip_vs_conn_change_fwd_mask(struct ip_vs_conn *cp, u32 new_flags)\n+{\n+\tstruct netns_ipvs *ipvs = cp-\u003eipvs;\n+\tstruct hlist_bl_head *head, *head2;\n+\tu32 hash2, hash_key, hash_key2;\n+\tstruct ip_vs_rht *t, *t2;\n+\n+\t/* See ip_vs_conn_use_hash2() for reference */\n+\tif ((cp-\u003eflags \u0026 IP_VS_CONN_F_TEMPLATE) ||\n+\t /* No change in double hashing ? */\n+\t !((cp-\u003eflags ^ new_flags) \u0026 IP_VS_CONN_F_FWD_MASK)) {\n+\t\tcp-\u003eflags = new_flags;\n+\t\treturn;\n+\t}\n+\tt = rcu_dereference(ipvs-\u003econn_tab);\n+\tif (ip_vs_conn_use_hash2(cp)) {\n+\t\t/* Stop double hashing */\n+\t\thash_key = READ_ONCE(cp-\u003ehn0.hash_key);\n+\t\thash_key2 = READ_ONCE(cp-\u003ehn1.hash_key);\n+\n+\t\tconn_tab_lock(t, t, cp, hash_key, hash_key2, true /* use2 */,\n+\t\t\t false /* new_hash */, false /* new_hash2 */,\n+\t\t\t \u0026head, \u0026head2);\n+\n+\t\t/* Keep both hash keys in same table */\n+\t\thash_key = READ_ONCE(cp-\u003ehn0.hash_key);\n+\t\tWRITE_ONCE(cp-\u003ehn1.hash_key, hash_key);\n+\t\thlist_bl_del_rcu(\u0026cp-\u003ehn1.node);\n+\t\tcp-\u003eflags = new_flags;\n+\n+\t\tconn_tab_unlock(head, head2);\n+\t} else {\n+\t\t/* Start double hashing */\n+\n+\t\thash_key = READ_ONCE(cp-\u003ehn0.hash_key);\n+\n+\t\tt2 = rcu_dereference(t-\u003enew_tbl);\n+\t\thash2 = ip_vs_conn_hashkey_conn(t2, cp, true);\n+\t\thash_key2 = ip_vs_rht_build_hash_key(t2, hash2);\n+\n+\t\t/* Change the forwarding method under locked hn0 */\n+\t\tconn_tab_lock(t, t2, cp, hash_key, hash_key2, true /* use2 */,\n+\t\t\t false /* new_hash */, true /* new_hash2 */,\n+\t\t\t \u0026head, \u0026head2);\n+\n+\t\tWRITE_ONCE(cp-\u003ehn1.hash_key, hash_key2);\n+\t\tcp-\u003eflags = new_flags;\n+\t\thlist_bl_add_head_rcu(\u0026cp-\u003ehn1.node, head2);\n+\n+\t\tconn_tab_unlock(head, head2);\n+\t}\n+}\n+\n /* Get default load factor to map conn_count/u_thresh to t-\u003esize */\n static int ip_vs_conn_default_load_factor(struct netns_ipvs *ipvs)\n {\n@@ -1014,6 +1105,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)\n \tflags = cp-\u003eflags;\n \t/* Bind with the destination and its corresponding transmitter */\n \tif (flags \u0026 IP_VS_CONN_F_SYNC) {\n+\t\t/* Synced conns are hashed, so they can not get this flag */\n+\t\tconn_flags \u0026= ~IP_VS_CONN_F_ONE_PACKET;\n+\n \t\t/* if the connection is not template and is created\n \t\t * by sync, preserve the activity flag.\n \t\t */\n@@ -1021,9 +1115,18 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)\n \t\t\tconn_flags \u0026= ~IP_VS_CONN_F_INACTIVE;\n \t\t/* connections inherit forwarding method from dest */\n \t\tflags \u0026= ~(IP_VS_CONN_F_FWD_MASK | IP_VS_CONN_F_NOOUTPUT);\n+\t\tflags |= conn_flags;\n+\t\t/* Changing forwarding method for hashed conn can\n+\t\t * happen only under locks\n+\t\t */\n+\t\tif (cp-\u003eflags \u0026 IP_VS_CONN_F_HASHED)\n+\t\t\tip_vs_conn_change_fwd_mask(cp, flags);\n+\t\telse\n+\t\t\tcp-\u003eflags = flags;\n+\t} else {\n+\t\tflags |= conn_flags;\n+\t\tcp-\u003eflags = flags;\n \t}\n-\tflags |= conn_flags;\n-\tcp-\u003eflags = flags;\n \tcp-\u003edest = dest;\n \n \tIP_VS_DBG_BUF(7, \"Bind-dest %s c:%s:%d v:%s:%d \"\n"
} |
| 2/1 | 2026/07/01 02:29 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"IP_VS"
],
"FocusSymbols": [
"ip_vs_conn_change_fwd_mask",
"ip_vs_bind_dest",
"ip_vs_conn_fill_cport"
],
"Reasoning": "The patch modifies the core connection tracking logic in IPVS (IP Virtual Server), specifically changing how connection hash tables are locked and how forwarding methods are updated for hashed connections. This is reachable functional code that handles network load balancing.",
"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). 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. 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. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. 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 27e201e3e8fb35abe6db3bf40403058ff0ce7e69
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Jul 1 02:29:03 2026 +0000
syz-cluster: applied patch under review
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
index e76a73d183d531..53a7de3a9f2bf9 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -70,25 +70,47 @@ static struct kmem_cache *ip_vs_conn_cachep __read_mostly;
* bucket or hash table
* - hash table resize works like rehash but always rehashes into new table
* - bit lock on bucket serializes all operations that modify the chain
+ * - on resize, bucket from the old table is locked before bucket from the
+ * new table
* - cp->lock protects conn fields like cp->flags, cp->dest
*/
-/* Lock conn_tab bucket for conn hash/unhash, not for rehash */
+/**
+ * conn_tab_lock - Lock conn_tab buckets for conn hash/unhash, not for rehash
+ * @t: hash table for hn0
+ * @t2: hash table for hn1
+ * @cp: connection
+ * @hash_key: hash key for hn0
+ * @hash_key2: hash key for hn1
+ * @use2: using hn1 (double hashing) based on the forwarding method
+ * @new_hash: mode for hn0, hash node (true) or seek node (false)
+ * @new_hash2: mode for hn1, hash node (true) or seek node (false)
+ * @head_ret: returned head for hn0
+ * @head2_ret: returned head for hn1
+ *
+ * We support 3 modes:
+ * - seek mode for both nodes, used for unhashing
+ * - hash mode for both nodes, used for hashing
+ * - seek hn0 and hash hn1, used when forwarding method is changed
+ */
static __always_inline void
-conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,
- u32 hash_key2, bool use2, bool new_hash,
- struct hlist_bl_head **head_ret, struct hlist_bl_head **head2_ret)
+conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_rht *t2, struct ip_vs_conn *cp,
+ u32 hash_key, u32 hash_key2, bool use2, bool new_hash,
+ bool new_hash2, struct hlist_bl_head **head_ret,
+ struct hlist_bl_head **head2_ret)
{
struct hlist_bl_head *head, *head2;
u32 hash_key_new, hash_key_new2;
- struct ip_vs_rht *t2 = t;
u32 idx, idx2;
idx = hash_key & t->mask;
- if (use2)
- idx2 = hash_key2 & t->mask;
- else
- idx2 = idx;
+ idx2 = hash_key2 & t->mask;
+ /* Advance idx2 when new_hash is not set but hash_key2
+ * is for new table
+ */
+ if (new_hash2 && use2 && t != t2)
+ idx2 |= IP_VS_RHT_TABLE_ID_MASK;
+
if (!new_hash) {
/* We need to lock the bucket in the right table */
@@ -103,24 +125,19 @@ conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,
idx = hash_key & t->mask;
idx |= IP_VS_RHT_TABLE_ID_MASK;
}
- if (use2) {
- if (!ip_vs_rht_same_table(t2, hash_key2)) {
- /* It is already moved to new table */
- t2 = rcu_dereference(t2->new_tbl);
- idx2 = hash_key2 & t2->mask;
- idx2 |= IP_VS_RHT_TABLE_ID_MASK;
- }
- } else {
- idx2 = idx;
- }
+ }
+ if (use2 && !new_hash2 && !ip_vs_rht_same_table(t2, hash_key2)) {
+ /* It is already moved to new table */
+ t2 = rcu_dereference(t2->new_tbl);
+ idx2 = hash_key2 & t2->mask;
+ idx2 |= IP_VS_RHT_TABLE_ID_MASK;
}
+ if (!use2)
+ idx2 = idx;
head = t->buckets + (hash_key & t->mask);
head2 = use2 ? t2->buckets + (hash_key2 & t2->mask) : head;
- local_bh_disable();
- /* Do not touch seqcount, this is a safe operation */
-
if (idx <= idx2) {
hlist_bl_lock(head);
if (head != head2)
@@ -130,16 +147,22 @@ conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,
hlist_bl_lock(head);
}
if (!new_hash) {
+ bool changed;
+
/* Ensure hash_key is read under lock */
hash_key_new = READ_ONCE(cp->hn0.hash_key);
- hash_key_new2 = READ_ONCE(cp->hn1.hash_key);
+ changed = hash_key != hash_key_new;
+ if (use2 && !new_hash2) {
+ hash_key_new2 = READ_ONCE(cp->hn1.hash_key);
+ changed |= hash_key2 != hash_key_new2;
+ } else {
+ hash_key_new2 = hash_key2;
+ }
/* Hash changed ? */
- if (hash_key != hash_key_new ||
- (hash_key2 != hash_key_new2 && use2)) {
+ if (changed) {
if (head != head2)
hlist_bl_unlock(head2);
hlist_bl_unlock(head);
- local_bh_enable();
hash_key = hash_key_new;
hash_key2 = hash_key_new2;
goto retry;
@@ -155,7 +178,6 @@ static inline void conn_tab_unlock(struct hlist_bl_head *head,
if (head != head2)
hlist_bl_unlock(head2);
hlist_bl_unlock(head);
- local_bh_enable();
}
static void ip_vs_conn_expire(struct timer_list *t);
@@ -268,8 +290,9 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
use2 = false;
}
- conn_tab_lock(t, cp, hash_key, hash_key2, use2, true /* new_hash */,
- &head, &head2);
+ local_bh_disable();
+ conn_tab_lock(t, t, cp, hash_key, hash_key2, use2, true /* new_hash */,
+ true /* new_hash2 */, &head, &head2);
cp->flags |= IP_VS_CONN_F_HASHED;
WRITE_ONCE(cp->hn0.hash_key, hash_key);
@@ -280,6 +303,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
hlist_bl_add_head_rcu(&cp->hn1.node, head2);
conn_tab_unlock(head, head2);
+ local_bh_enable();
ret = 1;
/* Schedule resizing if load increases */
@@ -306,18 +330,20 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
return refcount_dec_if_one(&cp->refcnt);
rcu_read_lock();
+ local_bh_disable();
t = rcu_dereference(ipvs->conn_tab);
hash_key = READ_ONCE(cp->hn0.hash_key);
hash_key2 = READ_ONCE(cp->hn1.hash_key);
use2 = ip_vs_conn_use_hash2(cp);
- conn_tab_lock(t, cp, hash_key, hash_key2, use2, false /* new_hash */,
- &head, &head2);
+ conn_tab_lock(t, t, cp, hash_key, hash_key2, use2, false /* new_hash */,
+ false /* new_hash2 */, &head, &head2);
if (cp->flags & IP_VS_CONN_F_HASHED) {
/* Decrease refcnt and unlink conn only if we are last user */
- if (refcount_dec_if_one(&cp->refcnt)) {
+ if (use2 == ip_vs_conn_use_hash2(cp) &&
+ refcount_dec_if_one(&cp->refcnt)) {
hlist_bl_del_rcu(&cp->hn0.node);
if (use2)
hlist_bl_del_rcu(&cp->hn1.node);
@@ -328,6 +354,7 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
conn_tab_unlock(head, head2);
+ local_bh_enable();
rcu_read_unlock();
return ret;
@@ -686,6 +713,16 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)
/* Protect the cp->flags modification */
spin_lock_bh(&cp->lock);
+ /* Recheck the forwarding method under lock */
+ if (use2 != ip_vs_conn_use_hash2(cp)) {
+ spin_unlock_bh(&cp->lock);
+ use2 = !use2;
+ if (dir) {
+ dir--;
+ goto next_dir;
+ }
+ }
+
/* Lock seqcount only for the old bucket, even if we are on new table
* because it affects the del operation, not the adding.
*/
@@ -752,6 +789,60 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)
goto next_dir;
}
+/* Change forwarding method for hashed conn */
+static void ip_vs_conn_change_fwd_mask(struct ip_vs_conn *cp, u32 new_flags)
+{
+ struct netns_ipvs *ipvs = cp->ipvs;
+ struct hlist_bl_head *head, *head2;
+ u32 hash2, hash_key, hash_key2;
+ struct ip_vs_rht *t, *t2;
+
+ /* See ip_vs_conn_use_hash2() for reference */
+ if ((cp->flags & IP_VS_CONN_F_TEMPLATE) ||
+ /* No change in double hashing ? */
+ !((cp->flags ^ new_flags) & IP_VS_CONN_F_FWD_MASK)) {
+ cp->flags = new_flags;
+ return;
+ }
+ t = rcu_dereference(ipvs->conn_tab);
+ if (ip_vs_conn_use_hash2(cp)) {
+ /* Stop double hashing */
+ hash_key = READ_ONCE(cp->hn0.hash_key);
+ hash_key2 = READ_ONCE(cp->hn1.hash_key);
+
+ conn_tab_lock(t, t, cp, hash_key, hash_key2, true /* use2 */,
+ false /* new_hash */, false /* new_hash2 */,
+ &head, &head2);
+
+ /* Keep both hash keys in same table */
+ hash_key = READ_ONCE(cp->hn0.hash_key);
+ WRITE_ONCE(cp->hn1.hash_key, hash_key);
+ hlist_bl_del_rcu(&cp->hn1.node);
+ cp->flags = new_flags;
+
+ conn_tab_unlock(head, head2);
+ } else {
+ /* Start double hashing */
+
+ hash_key = READ_ONCE(cp->hn0.hash_key);
+
+ t2 = rcu_dereference(t->new_tbl);
+ hash2 = ip_vs_conn_hashkey_conn(t2, cp, true);
+ hash_key2 = ip_vs_rht_build_hash_key(t2, hash2);
+
+ /* Change the forwarding method under locked hn0 */
+ conn_tab_lock(t, t2, cp, hash_key, hash_key2, true /* use2 */,
+ false /* new_hash */, true /* new_hash2 */,
+ &head, &head2);
+
+ WRITE_ONCE(cp->hn1.hash_key, hash_key2);
+ cp->flags = new_flags;
+ hlist_bl_add_head_rcu(&cp->hn1.node, head2);
+
+ conn_tab_unlock(head, head2);
+ }
+}
+
/* Get default load factor to map conn_count/u_thresh to t->size */
static int ip_vs_conn_default_load_factor(struct netns_ipvs *ipvs)
{
@@ -1014,6 +1105,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
flags = cp->flags;
/* Bind with the destination and its corresponding transmitter */
if (flags & IP_VS_CONN_F_SYNC) {
+ /* Synced conns are hashed, so they can not get this flag */
+ conn_flags &= ~IP_VS_CONN_F_ONE_PACKET;
+
/* if the connection is not template and is created
* by sync, preserve the activity flag.
*/
@@ -1021,9 +1115,18 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
conn_flags &= ~IP_VS_CONN_F_INACTIVE;
/* connections inherit forwarding method from dest */
flags &= ~(IP_VS_CONN_F_FWD_MASK | IP_VS_CONN_F_NOOUTPUT);
+ flags |= conn_flags;
+ /* Changing forwarding method for hashed conn can
+ * happen only under locks
+ */
+ if (cp->flags & IP_VS_CONN_F_HASHED)
+ ip_vs_conn_change_fwd_mask(cp, flags);
+ else
+ cp->flags = flags;
+ } else {
+ flags |= conn_flags;
+ cp->flags = flags;
}
- flags |= conn_flags;
- cp->flags = flags;
cp->dest = dest;
IP_VS_DBG_BUF(7, "Bind-dest %s c:%s:%d v:%s:%d "
|
| 3/2 | 2026/07/01 02:29 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 5728 output: 34 thoughts: 242 Thoughts: **Analyzing the IPVS Connection Tracking Patch** Okay, so I've got this patch here that’s tweaking `net/netfilter/ipvs/ip_vs_conn.c`. It's a connection-tracking modification, specifically around the `ip_vs_conn` hash tables and the locking mechanism, and introduces a new function, `ip_vs_conn_change_fwd_mask`. This `ip_vs_conn_change_fwd_mask` is then being utilized in `ip_vs_bind_dest`, when a connection is hashed, to adjust the forwarding method. So, fundamentally, we're looking at a functional change in the IPVS subsystem. Given that IPVS is a core component, a key part of the Linux kernel's networking stack, and frequently leveraged for load balancing, it's definitely something we need to assess for fuzzing potential. It's definitely reachable, typically via netlink or `setsockopt`, and the changes are, as I mentioned, in `net/netfilter/ipvs/ip_vs_conn.c`. Now, the next step is to nail down the relevant `CONFIG` options that will allow me to target this code effectively. I'll need to figure out how to configure the build environment to ensure IPVS is enabled. I'll use `grepper` and look for `IP_VS` within `net/netfilter/ipvs/Kconfig` to identify the correct flags. |
| 4/2 | 2026/07/01 02:29 | tool | grepper |
0mArgs:{
"Expression": "config IP_VS",
"PathPrefix": "net/netfilter/ipvs/Kconfig"
}Results: {
"Output": "net/netfilter/ipvs/Kconfig-4-#\nnet/netfilter/ipvs/Kconfig:5:menuconfig IP_VS\nnet/netfilter/ipvs/Kconfig-6-\ttristate \"IP virtual server support\"\n--\nnet/netfilter/ipvs/Kconfig=286=comment 'IPVS SH scheduler'\nnet/netfilter/ipvs/Kconfig-287-\nnet/netfilter/ipvs/Kconfig:288:config IP_VS_SH_TAB_BITS\nnet/netfilter/ipvs/Kconfig-289-\tint \"IPVS source hashing table size (the Nth power of 2)\"\n--\nnet/netfilter/ipvs/Kconfig=301=comment 'IPVS MH scheduler'\nnet/netfilter/ipvs/Kconfig-302-\nnet/netfilter/ipvs/Kconfig:303:config IP_VS_MH_TAB_INDEX\nnet/netfilter/ipvs/Kconfig-304-\tint \"IPVS maglev hashing table index of size (the prime numbers)\"\n"
} |
| 5/2 | 2026/07/01 02:29 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 6588 output: 47 thoughts: 277 Thoughts: **Analyzing the IPVS Configuration Changes** Okay, so I'm diving into this IPVS configuration and code. My main point of reference is the `IP_VS` config – that's the core. I need to understand what's been changed or added related to this. Alright, looking at the modifications, it seems the focus is on connection table management: `conn_tab_lock`, `ip_vs_conn_hash`, `ip_vs_conn_unlink`, `ip_vs_conn_fill_cport`, and `ip_vs_bind_dest`. These are all fairly low-level, essential functions. Then, there's a new function – `ip_vs_conn_change_fwd_mask`. Okay, that's interesting; I'll flag that as a significant change. `ip_vs_bind_dest` and `ip_vs_conn_fill_cport` are also worth closer inspection. Now, beyond the core `IP_VS` config, are there any other related configurations that are also important? It looks like `IP_VS_PROTO_TCP` and `IP_VS_PROTO_UDP` are good candidates to add as well, just to be thorough and cover those protocols, though `IP_VS` is definitely the main one. Wait, hold on. I need to remember that in the kernel, `IP_VS` is a menuconfig, so it's `CONFIG_IP_VS` that I should be looking for. I'll specifically look at `ip_vs_conn_change_fwd_mask` in `net/netfilter/ipvs/ip_vs_conn.c` because it's a new function. |
| 6/2 | 2026/07/01 02:29 | tool | grepper |
0mArgs:{
"Expression": "ip_vs_conn_change_fwd_mask",
"PathPrefix": "net/netfilter/ipvs/ip_vs_conn.c"
}Results: {
"Output": "net/netfilter/ipvs/ip_vs_conn.c=647=void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)\n--\nnet/netfilter/ipvs/ip_vs_conn.c-792-/* Change forwarding method for hashed conn */\nnet/netfilter/ipvs/ip_vs_conn.c:793:static void ip_vs_conn_change_fwd_mask(struct ip_vs_conn *cp, u32 new_flags)\nnet/netfilter/ipvs/ip_vs_conn.c-794-{\n--\nnet/netfilter/ipvs/ip_vs_conn.c=1090=ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)\n--\nnet/netfilter/ipvs/ip_vs_conn.c-1122-\t\tif (cp-\u003eflags \u0026 IP_VS_CONN_F_HASHED)\nnet/netfilter/ipvs/ip_vs_conn.c:1123:\t\t\tip_vs_conn_change_fwd_mask(cp, flags);\nnet/netfilter/ipvs/ip_vs_conn.c-1124-\t\telse\n"
} |
| 7/2 | 2026/07/01 02:29 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7601 output: 34 thoughts: 99 Thoughts: **Patch Analysis and Fuzzing Strategy** Okay, so I've got this patch here. From a quick glance, it looks like it's touching the IPVS connection tracking logic – specifically, how it deals with double hashing and changes to the forwarding method. That immediately flags a potential area for vulnerabilities. Connection tracking is a complex beast, and changes to its core components are always worth a closer look. My first instinct is, *definitely* fuzz this. The potential for integer overflows, off-by-one errors, or even more subtle logic flaws within connection management is high. I want to make sure the assumptions built into the previous versions are maintained after this change. Now, let's pinpoint the key areas to target. The symbols that jump out at me as potential hotspots are `ip_vs_conn_change_fwd_mask`, `ip_vs_bind_dest`, and `ip_vs_conn_fill_cport`. These functions likely handle critical operations related to forwarding rules, destination binding, and port information – the kind of places where things can go sideways if input validation isn't rock solid. I'll need to develop fuzzing strategies focused on these function entry points and how they interact with the overall connection tracking data structures. Let me also check the configuration needed to build this function. I see IP_VS is needed. Okay. Now, I want to double-check that there aren't any other dependent configs that might also be relevant to fully enable the impacted code paths. We can't let a configuration issue block the testing strategy. I'll need to review the related Kconfig files. |
| 8/2 | 2026/07/01 02:29 | tool | grepper |
0mArgs:{
"Expression": "config IP_VS",
"PathPrefix": "net/netfilter/ipvs/Kconfig"
}Results: {
"Output": "net/netfilter/ipvs/Kconfig-4-#\nnet/netfilter/ipvs/Kconfig:5:menuconfig IP_VS\nnet/netfilter/ipvs/Kconfig-6-\ttristate \"IP virtual server support\"\n--\nnet/netfilter/ipvs/Kconfig=286=comment 'IPVS SH scheduler'\nnet/netfilter/ipvs/Kconfig-287-\nnet/netfilter/ipvs/Kconfig:288:config IP_VS_SH_TAB_BITS\nnet/netfilter/ipvs/Kconfig-289-\tint \"IPVS source hashing table size (the Nth power of 2)\"\n--\nnet/netfilter/ipvs/Kconfig=301=comment 'IPVS MH scheduler'\nnet/netfilter/ipvs/Kconfig-302-\nnet/netfilter/ipvs/Kconfig:303:config IP_VS_MH_TAB_INDEX\nnet/netfilter/ipvs/Kconfig-304-\tint \"IPVS maglev hashing table index of size (the prime numbers)\"\n"
} |
| 9/2 | 2026/07/01 02:29 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8346 output: 118 thoughts: 6 Thoughts: Okay, I'm ready. Here's how I'll summarize the text, assuming I'm an expert in the field: **Setting the Stage for Analysis** Alright, I see the task is to essentially summarize a thought process. I need to be careful here – not just to provide a simple regurgitation of what's *said*, but to capture the underlying reasoning and implication. It looks like the goal here is to establish the context for future results. This initial step, setting the parameters, is absolutely critical. If I don’t define the initial conditions I won’t get the correct results at the end. I am going to make certain I document these results. |
| 10/2 | 2026/07/01 02:29 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"IP_VS"
],
"FocusSymbols": [
"ip_vs_conn_change_fwd_mask",
"ip_vs_bind_dest",
"ip_vs_conn_fill_cport"
],
"Reasoning": "The patch modifies the core connection tracking logic in IPVS (IP Virtual Server), specifically changing how connection hash tables are locked and how forwarding methods are updated for hashed connections. This is reachable functional code that handles network load balancing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"IP_VS"
],
"FocusSymbols": [
"ip_vs_conn_change_fwd_mask",
"ip_vs_bind_dest",
"ip_vs_conn_fill_cport"
],
"Reasoning": "The patch modifies the core connection tracking logic in IPVS (IP Virtual Server), specifically changing how connection hash tables are locked and how forwarding methods are updated for hashed connections. This is reachable functional code that handles network load balancing.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|