| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/01 09:31 | flow | patch-triage |
5mArgs:null Results: {
"EnableConfigs": [
"BATMAN_ADV",
"BATMAN_ADV_BLA",
"BATMAN_ADV_DAT"
],
"FocusSymbols": [
"batadv_tt_local_reserve_vlan",
"batadv_tt_local_unreserve_vlan",
"batadv_tt_local_reserve_entry",
"batadv_tt_local_set_pending",
"batadv_tt_local_mark_removed",
"batadv_tt_local_create",
"batadv_tt_local_add",
"batadv_tt_local_remove",
"batadv_meshif_create_vlan",
"batadv_meshif_vlan_release",
"batadv_bla_init"
],
"KMSANReasoning": "The patch series addresses synchronization, state management, and table size reservations in batman-adv (including claim_hash cleanup in bridge loop avoidance, bit assignment in DAT, and translation table reservation tracking for VLANs and local TT entries). \n\nNone of the changes introduce uninitialized memory allocations, uninitialized variable reads, or copy operations of uninitialized kernel structures to userspace/network buffers. All new structure fields in struct batadv_priv_tt are zero-initialized as part of netdev allocation, stack variables (such as table_size) are explicitly written before use, and newly allocated structures (such as batadv_meshif_vlan) continue to be allocated via kzalloc_obj.\n\nAny potential bugs introduced by these modifications would be related to locking/concurrency (handled by LOCKDEP) or reference counting/memory cleanup (handled by KASAN). Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies B.A.T.M.A.N. Advanced (batman-adv), an in-kernel mesh routing protocol accessible via virtual netdevs, netlink, and packet interfaces. The changes add a table size reservation mechanism for local translation table (TT) entries and VLANs, refactor TT removal and purging logic, introduce a new spinlock (reserve_lock) with scoped guards, and add lockdep assertions in batadv_tt_local_set_pending(). These modifications introduce new concurrency constraints, state tracking, and assertions in reachable network protocol code, making it a prime candidate for fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/10/01 09:31 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit bbc39027a78a5ab27162a649b95ca4ae19e604dd\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Oct 1 09:31:57 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c\nindex ad6ab4a50658f..a96f269da8d37 100644\n--- a/net/batman-adv/bridge_loop_avoidance.c\n+++ b/net/batman-adv/bridge_loop_avoidance.c\n@@ -1625,6 +1625,7 @@ int batadv_bla_init(struct batadv_priv *bat_priv)\n \tbat_priv-\u003ebla.backbone_hash = batadv_hash_new(32);\n \tif (!bat_priv-\u003ebla.backbone_hash) {\n \t\tbatadv_hash_destroy(bat_priv-\u003ebla.claim_hash);\n+\t\tbat_priv-\u003ebla.claim_hash = NULL;\n \t\treturn -ENOMEM;\n \t}\n \ndiff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c\nindex a4be5b2a87d7a..74050bffedbce 100644\n--- a/net/batman-adv/distributed-arp-table.c\n+++ b/net/batman-adv/distributed-arp-table.c\n@@ -863,10 +863,8 @@ static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,\n \t\t\t\t\t u8 flags,\n \t\t\t\t\t void *tvlv_value, u16 tvlv_value_len)\n {\n-\tif (flags \u0026 BATADV_TVLV_HANDLER_OGM_CIFNOTFND)\n-\t\tclear_bit(BATADV_ORIG_CAPA_HAS_DAT, \u0026orig-\u003ecapabilities);\n-\telse\n-\t\tset_bit(BATADV_ORIG_CAPA_HAS_DAT, \u0026orig-\u003ecapabilities);\n+\tassign_bit(BATADV_ORIG_CAPA_HAS_DAT, \u0026orig-\u003ecapabilities,\n+\t\t !(flags \u0026 BATADV_TVLV_HANDLER_OGM_CIFNOTFND));\n }\n \n /**\ndiff --git a/net/batman-adv/main.c b/net/batman-adv/main.c\nindex d89d44706269b..6c54a8d4be3ec 100644\n--- a/net/batman-adv/main.c\n+++ b/net/batman-adv/main.c\n@@ -198,6 +198,7 @@ int batadv_mesh_init(struct net_device *mesh_iface)\n \tspin_lock_init(\u0026bat_priv-\u003ett.roam_list_lock);\n \tspin_lock_init(\u0026bat_priv-\u003ett.last_changeset_lock);\n \tspin_lock_init(\u0026bat_priv-\u003ett.commit_lock);\n+\tspin_lock_init(\u0026bat_priv-\u003ett.reserve_lock);\n \tspin_lock_init(\u0026bat_priv-\u003egw.list_lock);\n #ifdef CONFIG_BATMAN_ADV_MCAST\n \tspin_lock_init(\u0026bat_priv-\u003emcast.mla_lock);\ndiff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c\nindex 63af21954cf90..1ce707aaf48a0 100644\n--- a/net/batman-adv/mesh-interface.c\n+++ b/net/batman-adv/mesh-interface.c\n@@ -561,6 +561,8 @@ void batadv_meshif_vlan_release(struct kref *ref)\n \thlist_del_rcu(\u0026vlan-\u003elist);\n \tspin_unlock_bh(\u0026vlan-\u003ebat_priv-\u003emeshif_vlan_list_lock);\n \n+\tbatadv_tt_local_unreserve_vlan(vlan-\u003ebat_priv);\n+\n \tkfree_rcu(vlan, rcu);\n }\n \n@@ -614,9 +616,15 @@ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)\n \t\treturn -EEXIST;\n \t}\n \n+\tif (!batadv_tt_local_reserve_vlan(bat_priv, vid)) {\n+\t\tspin_unlock_bh(\u0026bat_priv-\u003emeshif_vlan_list_lock);\n+\t\treturn -EMSGSIZE;\n+\t}\n+\n \tvlan = kzalloc_obj(*vlan, GFP_ATOMIC);\n \tif (!vlan) {\n \t\tspin_unlock_bh(\u0026bat_priv-\u003emeshif_vlan_list_lock);\n+\t\tbatadv_tt_local_unreserve_vlan(bat_priv);\n \t\treturn -ENOMEM;\n \t}\n \ndiff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c\nindex 12ea557cb8033..3349376a9087d 100644\n--- a/net/batman-adv/translation-table.c\n+++ b/net/batman-adv/translation-table.c\n@@ -234,6 +234,162 @@ batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,\n \treturn tt_global_entry;\n }\n \n+/**\n+ * batadv_tt_len() - compute length in bytes of given number of tt changes\n+ * @changes_num: number of tt changes\n+ *\n+ * Return: computed length in bytes.\n+ */\n+static int batadv_tt_len(int changes_num)\n+{\n+\treturn changes_num * sizeof(struct batadv_tvlv_tt_change);\n+}\n+\n+/**\n+ * batadv_tt_local_transmit_size() - calculate the size of a full table response\n+ * for a given number of VLANs and local TT entries\n+ * @num_vlan: number of announced VLANs\n+ * @num_entries: number of announced local TT entries\n+ *\n+ * Return: local translation table size in bytes.\n+ */\n+static int batadv_tt_local_transmit_size(u16 num_vlan, u16 num_entries)\n+{\n+\tint hdr_size;\n+\n+\t/* header size of tvlv encapsulated tt response payload */\n+\thdr_size = sizeof(struct batadv_unicast_tvlv_packet);\n+\thdr_size += sizeof(struct batadv_tvlv_hdr);\n+\thdr_size += sizeof(struct batadv_tvlv_tt_data);\n+\thdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);\n+\n+\treturn hdr_size + batadv_tt_len(num_entries);\n+}\n+\n+/**\n+ * batadv_tt_local_reserve() - reserve room in the transmittable local table\n+ * @bat_priv: the bat priv with all the mesh interface information\n+ * @num_vlan: number of VLANs to reserve\n+ * @num_entries: number of local TT entries to reserve\n+ * @table_size: stores the resulting worst case table size in bytes\n+ *\n+ * Add the requested number of VLANs and local TT entries to the reservation\n+ * counters and check whether the local translation table would then still fit\n+ * in a single full table response. The reservation is dropped again when it\n+ * would not.\n+ *\n+ * The reservation has to happen before the related object is allocated. This\n+ * way two parallel allocations cannot both observe enough room for themselves\n+ * and end up with a local table which can no longer be transmitted.\n+ *\n+ * A granted reservation must be returned via batadv_tt_local_unreserve() when\n+ * the related object is released or was never created.\n+ *\n+ * Return: true when the reservation was granted, false otherwise.\n+ */\n+static bool batadv_tt_local_reserve(struct batadv_priv *bat_priv, u16 num_vlan,\n+\t\t\t\t u16 num_entries, int *table_size)\n+{\n+\tint packet_size_max = READ_ONCE(bat_priv-\u003epacket_size_max);\n+\n+\tscoped_guard(spinlock_bh, \u0026bat_priv-\u003ett.reserve_lock) {\n+\t\tbat_priv-\u003ett.reserved_vlans += num_vlan;\n+\t\tbat_priv-\u003ett.reserved_entries += num_entries;\n+\n+\t\t*table_size = batadv_tt_local_transmit_size(bat_priv-\u003ett.reserved_vlans,\n+\t\t\t\t\t\t\t bat_priv-\u003ett.reserved_entries);\n+\t\tif (*table_size \u003c= packet_size_max)\n+\t\t\treturn true;\n+\n+\t\tbat_priv-\u003ett.reserved_vlans -= num_vlan;\n+\t\tbat_priv-\u003ett.reserved_entries -= num_entries;\n+\t}\n+\n+\treturn false;\n+}\n+\n+/**\n+ * batadv_tt_local_unreserve() - return room in the transmittable local table\n+ * @bat_priv: the bat priv with all the mesh interface information\n+ * @num_vlan: number of VLANs to return\n+ * @num_entries: number of local TT entries to return\n+ */\n+static void batadv_tt_local_unreserve(struct batadv_priv *bat_priv,\n+\t\t\t\t u16 num_vlan, u16 num_entries)\n+{\n+\tscoped_guard(spinlock_bh, \u0026bat_priv-\u003ett.reserve_lock) {\n+\t\tbat_priv-\u003ett.reserved_vlans -= num_vlan;\n+\t\tbat_priv-\u003ett.reserved_entries -= num_entries;\n+\t}\n+}\n+\n+/**\n+ * batadv_tt_local_reserve_entry() - reserve room for a new local TT entry\n+ * @bat_priv: the bat priv with all the mesh interface information\n+ * @addr: the mac address of the client to add\n+ *\n+ * Return: true when the reservation was granted, false otherwise.\n+ */\n+static bool batadv_tt_local_reserve_entry(struct batadv_priv *bat_priv,\n+\t\t\t\t\t const u8 *addr)\n+{\n+\tint table_size;\n+\n+\tif (batadv_tt_local_reserve(bat_priv, 0, 1, \u0026table_size))\n+\t\treturn true;\n+\n+\tnet_ratelimited_function(batadv_info, bat_priv-\u003emesh_iface,\n+\t\t\t\t \"Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\\n\",\n+\t\t\t\t table_size,\n+\t\t\t\t READ_ONCE(bat_priv-\u003epacket_size_max), addr);\n+\n+\treturn false;\n+}\n+\n+/**\n+ * batadv_tt_local_unreserve_entry() - return the room of a local TT entry\n+ * @bat_priv: the bat priv with all the mesh interface information\n+ */\n+static void batadv_tt_local_unreserve_entry(struct batadv_priv *bat_priv)\n+{\n+\tbatadv_tt_local_unreserve(bat_priv, 0, 1);\n+}\n+\n+/**\n+ * batadv_tt_local_reserve_vlan() - reserve room for a new VLAN\n+ * @bat_priv: the bat priv with all the mesh interface information\n+ * @vid: the VLAN identifier\n+ *\n+ * Each VLAN adds a per VLAN header to the full table response and therefore\n+ * has to be reserved before batadv_meshif_create_vlan() allocates it.\n+ *\n+ * Return: true when the reservation was granted, false otherwise.\n+ */\n+bool batadv_tt_local_reserve_vlan(struct batadv_priv *bat_priv,\n+\t\t\t\t unsigned short vid)\n+{\n+\tint table_size;\n+\n+\tif (batadv_tt_local_reserve(bat_priv, 1, 0, \u0026table_size))\n+\t\treturn true;\n+\n+\tnet_ratelimited_function(batadv_info, bat_priv-\u003emesh_iface,\n+\t\t\t\t \"Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new VLAN: %d\\n\",\n+\t\t\t\t table_size, READ_ONCE(bat_priv-\u003epacket_size_max),\n+\t\t\t\t batadv_print_vid(vid));\n+\n+\treturn false;\n+}\n+\n+/**\n+ * batadv_tt_local_unreserve_vlan() - return the room of a VLAN\n+ * @bat_priv: the bat priv with all the mesh interface information\n+ */\n+void batadv_tt_local_unreserve_vlan(struct batadv_priv *bat_priv)\n+{\n+\tbatadv_tt_local_unreserve(bat_priv, 1, 0);\n+}\n+\n /**\n * batadv_tt_local_entry_release() - release tt_local_entry from lists and queue\n * for free after rcu grace period\n@@ -246,6 +402,7 @@ static void batadv_tt_local_entry_release(struct kref *ref)\n \ttt_local_entry = container_of(ref, struct batadv_tt_local_entry,\n \t\t\t\t common.refcount);\n \n+\tbatadv_tt_local_unreserve_entry(tt_local_entry-\u003evlan-\u003ebat_priv);\n \tbatadv_meshif_vlan_put(tt_local_entry-\u003evlan);\n \n \tkfree_rcu(tt_local_entry, common.rcu);\n@@ -461,23 +618,21 @@ static u16 batadv_tt_flags_get(struct batadv_tt_common_entry *common)\n }\n \n /**\n- * batadv_tt_local_event() - store a local TT event (ADD/DEL)\n+ * __batadv_tt_local_event() - store a local TT event (ADD/DEL) with given flags\n * @bat_priv: the bat priv with all the mesh interface information\n- * @tt_local_entry: the TT entry involved in the event\n- * @event_flags: flags to store in the event structure\n+ * @common: the TT entry involved in the event\n+ * @flags: flags of the TT entry combined with the event flags\n */\n-static void batadv_tt_local_event(struct batadv_priv *bat_priv,\n-\t\t\t\t struct batadv_tt_local_entry *tt_local_entry,\n-\t\t\t\t u8 event_flags)\n+static void __batadv_tt_local_event(struct batadv_priv *bat_priv,\n+\t\t\t\t const struct batadv_tt_common_entry *common,\n+\t\t\t\t u8 flags)\n {\n-\tstruct batadv_tt_common_entry *common = \u0026tt_local_entry-\u003ecommon;\n \tstruct batadv_tt_change_node *tt_change_node;\n \tstruct batadv_tt_change_node *entry;\n \tstruct batadv_tt_change_node *safe;\n \tbool del_op_requested;\n \tbool del_op_entry;\n \tsize_t changes;\n-\tu8 flags;\n \n \ttt_change_node = kmem_cache_alloc(batadv_tt_change_cache, GFP_ATOMIC);\n \tif (!tt_change_node)\n@@ -488,8 +643,6 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,\n \tether_addr_copy(tt_change_node-\u003echange.addr, common-\u003eaddr);\n \ttt_change_node-\u003echange.vid = htons(common-\u003evid);\n \n-\tflags = batadv_tt_flags_get(common) | event_flags;\n-\n \ttt_change_node-\u003echange.flags = flags;\n \tdel_op_requested = flags \u0026 BATADV_TT_CLIENT_DEL;\n \n@@ -538,14 +691,20 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,\n }\n \n /**\n- * batadv_tt_len() - compute length in bytes of given number of tt changes\n- * @changes_num: number of tt changes\n- *\n- * Return: computed length in bytes.\n+ * batadv_tt_local_event() - store a local TT event (ADD/DEL)\n+ * @bat_priv: the bat priv with all the mesh interface information\n+ * @tt_local_entry: the TT entry involved in the event\n+ * @event_flags: flags to store in the event structure\n */\n-static int batadv_tt_len(int changes_num)\n+static void batadv_tt_local_event(struct batadv_priv *bat_priv,\n+\t\t\t\t struct batadv_tt_local_entry *tt_local_entry,\n+\t\t\t\t u8 event_flags)\n {\n-\treturn changes_num * sizeof(struct batadv_tvlv_tt_change);\n+\tstruct batadv_tt_common_entry *common = \u0026tt_local_entry-\u003ecommon;\n+\tu8 flags;\n+\n+\tflags = batadv_tt_flags_get(common) | event_flags;\n+\t__batadv_tt_local_event(bat_priv, common, flags);\n }\n \n /**\n@@ -571,7 +730,6 @@ static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)\n \tstruct batadv_meshif_vlan *vlan;\n \tu16 tt_local_entries = 0;\n \tu16 num_vlan = 0;\n-\tint hdr_size;\n \n \trcu_read_lock();\n \thlist_for_each_entry_rcu(vlan, \u0026bat_priv-\u003emeshif_vlan_list, list) {\n@@ -580,13 +738,7 @@ static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)\n \t}\n \trcu_read_unlock();\n \n-\t/* header size of tvlv encapsulated tt response payload */\n-\thdr_size = sizeof(struct batadv_unicast_tvlv_packet);\n-\thdr_size += sizeof(struct batadv_tvlv_hdr);\n-\thdr_size += sizeof(struct batadv_tvlv_tt_data);\n-\thdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);\n-\n-\treturn hdr_size + batadv_tt_len(tt_local_entries);\n+\treturn batadv_tt_local_transmit_size(num_vlan, tt_local_entries);\n }\n \n /**\n@@ -790,23 +942,15 @@ batadv_tt_local_create(struct net_device *mesh_iface, const u8 *addr,\n \tstruct batadv_priv *bat_priv = netdev_priv(mesh_iface);\n \tstruct batadv_tt_local_entry *tt_local;\n \tstruct batadv_meshif_vlan *vlan;\n-\tint packet_size_max;\n-\tint table_size;\n \n-\t/* Ignore the client if we cannot send it in a full table response. */\n-\ttable_size = batadv_tt_local_table_transmit_size(bat_priv);\n-\ttable_size += batadv_tt_len(1);\n-\tpacket_size_max = READ_ONCE(bat_priv-\u003epacket_size_max);\n-\tif (table_size \u003e packet_size_max) {\n-\t\tnet_ratelimited_function(batadv_info, mesh_iface,\n-\t\t\t\t\t \"Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\\n\",\n-\t\t\t\t\t table_size, packet_size_max, addr);\n+\tif (!batadv_tt_local_reserve_entry(bat_priv, addr))\n \t\treturn NULL;\n-\t}\n \n \ttt_local = kmem_cache_alloc(batadv_tl_cache, GFP_ATOMIC);\n-\tif (!tt_local)\n+\tif (!tt_local) {\n+\t\tbatadv_tt_local_unreserve_entry(bat_priv);\n \t\treturn NULL;\n+\t}\n \n \t/* increase the refcounter of the related vlan */\n \tvlan = batadv_meshif_vlan_get(bat_priv, vid);\n@@ -815,6 +959,7 @@ batadv_tt_local_create(struct net_device *mesh_iface, const u8 *addr,\n \t\t\t\t\t \"adding TT local entry %pM to non-existent VLAN %d\\n\",\n \t\t\t\t\t addr, batadv_print_vid(vid));\n \t\tkmem_cache_free(batadv_tl_cache, tt_local);\n+\t\tbatadv_tt_local_unreserve_entry(bat_priv);\n \t\treturn NULL;\n \t}\n \n@@ -1420,21 +1565,37 @@ int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)\n }\n \n /**\n- * batadv_tt_local_set_pending_event() - trigger events for TT pending removal\n+ * batadv_tt_local_set_pending() - mark local TT entry as pending removal\n * @bat_priv: the bat priv with all the mesh interface information\n- * @tt_local_entry: local TT entry which was marked as BATADV_TT_CLIENT_PENDING\n+ * @tt_local_entry: local TT entry to mark as BATADV_TT_CLIENT_PENDING\n * @flags: TT change flags to announce together with the pending removal\n * @message: debug message describing the reason for the change\n *\n- * Schedule the TT change announcement for the entry. The caller must already\n- * have added BATADV_TT_CLIENT_PENDING to the @tt_local_entry\n+ * Schedule the TT change announcement and set BATADV_TT_CLIENT_PENDING on the\n+ * entry. The entry is kept in the local table until the next TTVN increment\n+ * so that a consistency-check response can still be answered.\n+ *\n+ * Next to the flags_lock of the entry, the caller must hold the hash bucket\n+ * list_lock of @tt_local_entry. Otherwise\n+ * batadv_tt_local_purge_pending_clients() could remove the entry before its\n+ * change was queued.\n */\n static void\n-batadv_tt_local_set_pending_event(struct batadv_priv *bat_priv,\n-\t\t\t\t struct batadv_tt_local_entry *tt_local_entry,\n-\t\t\t\t u16 flags, const char *message)\n+batadv_tt_local_set_pending(struct batadv_priv *bat_priv,\n+\t\t\t struct batadv_tt_local_entry *tt_local_entry,\n+\t\t\t u16 flags, const char *message)\n+\t__must_hold(\u0026tt_local_entry-\u003ecommon.flags_lock)\n {\n-\tbatadv_tt_local_event(bat_priv, tt_local_entry, flags);\n+\tstruct batadv_tt_common_entry *common = \u0026tt_local_entry-\u003ecommon;\n+\tstruct batadv_hashtable *hash = bat_priv-\u003ett.local_hash;\n+\tu32 i;\n+\n+\ti = batadv_choose_tt(common, hash-\u003esize);\n+\tlockdep_assert_held(\u0026hash-\u003elist_locks[i]);\n+\tlockdep_assert_held(\u0026common-\u003eflags_lock);\n+\n+\t__batadv_tt_local_event(bat_priv, common, common-\u003eflags | flags);\n+\tcommon-\u003eflags |= BATADV_TT_CLIENT_PENDING;\n \n \tbatadv_dbg(BATADV_DBG_TT, bat_priv,\n \t\t \"Local tt entry (%pM, vid: %d) pending to be removed: %s\\n\",\n@@ -1443,45 +1604,77 @@ batadv_tt_local_set_pending_event(struct batadv_priv *bat_priv,\n }\n \n /**\n- * batadv_tt_local_mark_removed() - mark a local entry as removed\n+ * batadv_tt_local_mark_removed() - mark a local entry as removed and queue DEL\n+ * @bat_priv: the bat priv with all the mesh interface information\n * @tt_local_entry: local TT entry to mark\n+ * @message: message to append to the log on deletion\n * @roaming: true if the deletion is due to a roaming event\n * @curr_flags: pointer to store the flags of the entry before it was marked\n *\n+ * An already announced entry is marked as BATADV_TT_CLIENT_PENDING and the\n+ * (roamed) DEL change is queued. Both happen under the hash bucket list_lock\n+ * of the entry to prevent concurrent batadv_tt_local_purge_pending_clients()\n+ * from removing the entry and batadv_tt_local_transition_new() from clearing\n+ * BATADV_TT_CLIENT_NEW after it was checked.\n+ *\n * Return: true if the entry has to be kept in the local table until the next\n * ttvn increment, false if it can be purged immediately.\n */\n static bool\n-batadv_tt_local_mark_removed(struct batadv_tt_local_entry *tt_local_entry,\n-\t\t\t bool roaming, u16 *curr_flags)\n+batadv_tt_local_mark_removed(struct batadv_priv *bat_priv,\n+\t\t\t struct batadv_tt_local_entry *tt_local_entry,\n+\t\t\t const char *message, bool roaming, u16 *curr_flags)\n {\n+\tspinlock_t *list_lock; /* protects write access to the hash lists */\n \tstruct batadv_tt_common_entry *common = \u0026tt_local_entry-\u003ecommon;\n+\tstruct batadv_hashtable *hash = bat_priv-\u003ett.local_hash;\n \tbool pending = false;\n+\tu16 flags;\n+\tu32 i;\n+\n+\ti = batadv_choose_tt(common, hash-\u003esize);\n+\tlist_lock = \u0026hash-\u003elist_locks[i];\n+\n+\tspin_lock_bh(list_lock);\n \n \tscoped_guard(spinlock_bh, \u0026common-\u003eflags_lock) {\n \t\t*curr_flags = common-\u003eflags;\n \n-\t\t/* mark the local client as ROAMed */\n+\t\t/* if this global entry addition is due to a roaming, the node\n+\t\t * has to mark the local entry as \"roamed\" in order to\n+\t\t * correctly reroute packets later\n+\t\t */\n \t\tif (roaming)\n \t\t\tcommon-\u003eflags |= BATADV_TT_CLIENT_ROAM;\n \n-\t\tif (!(common-\u003eflags \u0026 BATADV_TT_CLIENT_NEW)) {\n-\t\t\tcommon-\u003eflags |= BATADV_TT_CLIENT_PENDING;\n-\t\t\tpending = true;\n-\t\t}\n+\t\tif (common-\u003eflags \u0026 BATADV_TT_CLIENT_NEW)\n+\t\t\tbreak;\n+\n+\t\tflags = BATADV_TT_CLIENT_DEL;\n+\t\tif (roaming)\n+\t\t\tflags |= BATADV_TT_CLIENT_ROAM;\n+\n+\t\tbatadv_tt_local_set_pending(bat_priv, tt_local_entry, flags,\n+\t\t\t\t\t message);\n+\t\tpending = true;\n \t}\n \n+\tspin_unlock_bh(list_lock);\n+\n \treturn pending;\n }\n \n /**\n- * batadv_tt_local_remove_now() - purge a local entry which was never announced\n+ * batadv_tt_local_remove_now() - purge a local entry which was not (yet) announced\n * @bat_priv: the bat priv with all the mesh interface information\n * @tt_local_entry: local TT entry to purge\n *\n- * A client which was added right after the last ttvn increment was never sent\n- * to the other nodes. It can therefore be dropped from the local table without\n- * waiting for the next ttvn increment.\n+ * A client which was added right after the last ttvn increment was not (yet)\n+ * sent to the other nodes. It can therefore be dropped from the local table\n+ * without waiting for the next ttvn increment.\n+ *\n+ * If it was still announced by a parallel context before it was removed from\n+ * the hash, then the local TT size adjustment will be handled automatically.\n */\n static void\n batadv_tt_local_remove_now(struct batadv_priv *bat_priv,\n@@ -1490,8 +1683,6 @@ batadv_tt_local_remove_now(struct batadv_priv *bat_priv,\n \tstruct batadv_tt_common_entry *common = \u0026tt_local_entry-\u003ecommon;\n \tstruct hlist_node *tt_removed_node;\n \n-\tbatadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);\n-\n \t/* remove exactly this object when still present in hash */\n \ttt_removed_node = batadv_hash_remove(bat_priv-\u003ett.local_hash,\n \t\t\t\t\t batadv_compare_tt_entry,\n@@ -1499,6 +1690,8 @@ batadv_tt_local_remove_now(struct batadv_priv *bat_priv,\n \tif (!tt_removed_node)\n \t\treturn;\n \n+\tbatadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);\n+\n \t/* batadv_tt_local_transition_new() may have committed the entry and\n \t * thus counted it in the local table size since the\n \t * BATADV_TT_CLIENT_NEW check in batadv_tt_local_mark_removed().\n@@ -1526,27 +1719,16 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,\n {\n \tstruct batadv_tt_local_entry *tt_local_entry;\n \tu16 curr_flags;\n-\tu16 flags;\n \n \ttt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);\n \tif (!tt_local_entry)\n \t\treturn BATADV_NO_FLAGS;\n \n-\tflags = BATADV_TT_CLIENT_DEL;\n-\t/* if this global entry addition is due to a roaming, the node has to\n-\t * mark the local entry as \"roamed\" in order to correctly reroute\n-\t * packets later\n+\t/* if this client has been added right now, it is possible to\n+\t * immediately purge it\n \t */\n-\tif (roaming)\n-\t\tflags |= BATADV_TT_CLIENT_ROAM;\n-\n-\tif (batadv_tt_local_mark_removed(tt_local_entry, roaming, \u0026curr_flags))\n-\t\tbatadv_tt_local_set_pending_event(bat_priv, tt_local_entry,\n-\t\t\t\t\t\t flags, message);\n-\telse\n-\t\t/* if this client has been added right now, it is possible to\n-\t\t * immediately purge it\n-\t\t */\n+\tif (!batadv_tt_local_mark_removed(bat_priv, tt_local_entry, message,\n+\t\t\t\t\t roaming, \u0026curr_flags))\n \t\tbatadv_tt_local_remove_now(bat_priv, tt_local_entry);\n \n \tbatadv_tt_local_entry_put(tt_local_entry);\n@@ -1571,37 +1753,25 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,\n \n \thlist_for_each_entry_safe(tt_common_entry, node_tmp, head,\n \t\t\t\t hash_entry) {\n-\t\tbool cont = false;\n-\n \t\ttt_local_entry = container_of(tt_common_entry,\n \t\t\t\t\t struct batadv_tt_local_entry,\n \t\t\t\t\t common);\n \n \t\tscoped_guard(spinlock_bh, \u0026tt_local_entry-\u003ecommon.flags_lock) {\n-\t\t\tif (tt_local_entry-\u003ecommon.flags \u0026 BATADV_TT_CLIENT_NOPURGE) {\n-\t\t\t\tcont = true;\n+\t\t\tif (tt_local_entry-\u003ecommon.flags \u0026 BATADV_TT_CLIENT_NOPURGE)\n \t\t\t\tbreak;\n-\t\t\t}\n \n \t\t\t/* entry already marked for deletion */\n-\t\t\tif (tt_local_entry-\u003ecommon.flags \u0026 BATADV_TT_CLIENT_PENDING) {\n-\t\t\t\tcont = true;\n+\t\t\tif (tt_local_entry-\u003ecommon.flags \u0026 BATADV_TT_CLIENT_PENDING)\n \t\t\t\tbreak;\n-\t\t\t}\n \n-\t\t\tif (!batadv_has_timed_out(tt_local_entry-\u003elast_seen, timeout)) {\n-\t\t\t\tcont = true;\n+\t\t\tif (!batadv_has_timed_out(tt_local_entry-\u003elast_seen, timeout))\n \t\t\t\tbreak;\n-\t\t\t}\n \n-\t\t\ttt_local_entry-\u003ecommon.flags |= BATADV_TT_CLIENT_PENDING;\n+\t\t\tbatadv_tt_local_set_pending(bat_priv, tt_local_entry,\n+\t\t\t\t\t\t BATADV_TT_CLIENT_DEL,\n+\t\t\t\t\t\t \"timed out\");\n \t\t}\n-\n-\t\tif (cont)\n-\t\t\tcontinue;\n-\n-\t\tbatadv_tt_local_set_pending_event(bat_priv, tt_local_entry,\n-\t\t\t\t\t\t BATADV_TT_CLIENT_DEL, \"timed out\");\n \t}\n }\n \n@@ -1870,9 +2040,10 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,\n * @tt_global_entry: the global TT entry of the announced client\n * @flags: TT flags announced for this non-mesh client\n *\n- * A client which is announced by another originator is no longer a local\n- * client. Remove it from the local table and take over the WIFI flag it was\n- * tracked with.\n+ * A non-multicast client which is announced by another originator is no longer\n+ * a local client. Remove it from the local table and (for the global entry)\n+ * take over the WIFI flag it was tracked with. Reset the roaming flag in case\n+ * the announcement didn't contain the roaming flag.\n */\n static void\n batadv_tt_global_purge_local(struct batadv_priv *bat_priv,\ndiff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h\nindex 618d9dbca5eac..ca01d20ee2d7f 100644\n--- a/net/batman-adv/translation-table.h\n+++ b/net/batman-adv/translation-table.h\n@@ -16,6 +16,9 @@\n #include \u003clinux/types.h\u003e\n \n int batadv_tt_init(struct batadv_priv *bat_priv);\n+bool batadv_tt_local_reserve_vlan(struct batadv_priv *bat_priv,\n+\t\t\t\t unsigned short vid);\n+void batadv_tt_local_unreserve_vlan(struct batadv_priv *bat_priv);\n bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,\n \t\t\t unsigned short vid, int ifindex, u32 mark);\n u16 batadv_tt_local_remove(struct batadv_priv *bat_priv,\ndiff --git a/net/batman-adv/types.h b/net/batman-adv/types.h\nindex 67872927cfb50..c19caa84e6920 100644\n--- a/net/batman-adv/types.h\n+++ b/net/batman-adv/types.h\n@@ -1055,6 +1055,25 @@ struct batadv_priv_tt {\n \t */\n \tspinlock_t commit_lock;\n \n+\t/**\n+\t * @reserved_entries: number of local TT entries which are currently\n+\t * allocated or about to be allocated. Together with @reserved_vlans it\n+\t * describes the worst case size of a full table response.\n+\t */\n+\tu16 reserved_entries;\n+\n+\t/**\n+\t * @reserved_vlans: number of mesh interface VLANs which are currently\n+\t * allocated or about to be allocated. Together with @reserved_entries\n+\t * it describes the worst case size of a full table response.\n+\t */\n+\tu16 reserved_vlans;\n+\n+\t/**\n+\t * @reserve_lock: lock protecting @reserved_entries \u0026 @reserved_vlans\n+\t */\n+\tspinlock_t reserve_lock;\n+\n \t/** @work: work queue callback item for translation table purging */\n \tstruct delayed_work work;\n };\n"
} |
| 2/1 | 2026/10/01 09:31 | agent | patch-evaluator |
1mModel:core Args: null Results: {
"EnableConfigs": [
"BATMAN_ADV",
"BATMAN_ADV_BLA",
"BATMAN_ADV_DAT"
],
"FocusSymbols": [
"batadv_tt_local_reserve_vlan",
"batadv_tt_local_unreserve_vlan",
"batadv_tt_local_reserve_entry",
"batadv_tt_local_set_pending",
"batadv_tt_local_mark_removed",
"batadv_tt_local_create",
"batadv_tt_local_add",
"batadv_tt_local_remove",
"batadv_meshif_create_vlan",
"batadv_meshif_vlan_release",
"batadv_bla_init"
],
"Reasoning": "The patch modifies B.A.T.M.A.N. Advanced (batman-adv), an in-kernel mesh routing protocol accessible via virtual netdevs, netlink, and packet interfaces. The changes add a table size reservation mechanism for local translation table (TT) entries and VLANs, refactor TT removal and purging logic, introduce a new spinlock (reserve_lock) with scoped guards, and add lockdep assertions in batadv_tt_local_set_pending(). These modifications introduce new concurrency constraints, state tracking, and assertions in reachable network protocol code, making it a prime candidate for fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit bbc39027a78a5ab27162a649b95ca4ae19e604dd
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Oct 1 09:31:57 2026 +0000
syz-cluster: applied patch under review
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index ad6ab4a50658f..a96f269da8d37 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -1625,6 +1625,7 @@ int batadv_bla_init(struct batadv_priv *bat_priv)
bat_priv->bla.backbone_hash = batadv_hash_new(32);
if (!bat_priv->bla.backbone_hash) {
batadv_hash_destroy(bat_priv->bla.claim_hash);
+ bat_priv->bla.claim_hash = NULL;
return -ENOMEM;
}
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index a4be5b2a87d7a..74050bffedbce 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -863,10 +863,8 @@ static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
u8 flags,
void *tvlv_value, u16 tvlv_value_len)
{
- if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
- clear_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities);
- else
- set_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities);
+ assign_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities,
+ !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND));
}
/**
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index d89d44706269b..6c54a8d4be3ec 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -198,6 +198,7 @@ int batadv_mesh_init(struct net_device *mesh_iface)
spin_lock_init(&bat_priv->tt.roam_list_lock);
spin_lock_init(&bat_priv->tt.last_changeset_lock);
spin_lock_init(&bat_priv->tt.commit_lock);
+ spin_lock_init(&bat_priv->tt.reserve_lock);
spin_lock_init(&bat_priv->gw.list_lock);
#ifdef CONFIG_BATMAN_ADV_MCAST
spin_lock_init(&bat_priv->mcast.mla_lock);
diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
index 63af21954cf90..1ce707aaf48a0 100644
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -561,6 +561,8 @@ void batadv_meshif_vlan_release(struct kref *ref)
hlist_del_rcu(&vlan->list);
spin_unlock_bh(&vlan->bat_priv->meshif_vlan_list_lock);
+ batadv_tt_local_unreserve_vlan(vlan->bat_priv);
+
kfree_rcu(vlan, rcu);
}
@@ -614,9 +616,15 @@ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
return -EEXIST;
}
+ if (!batadv_tt_local_reserve_vlan(bat_priv, vid)) {
+ spin_unlock_bh(&bat_priv->meshif_vlan_list_lock);
+ return -EMSGSIZE;
+ }
+
vlan = kzalloc_obj(*vlan, GFP_ATOMIC);
if (!vlan) {
spin_unlock_bh(&bat_priv->meshif_vlan_list_lock);
+ batadv_tt_local_unreserve_vlan(bat_priv);
return -ENOMEM;
}
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 12ea557cb8033..3349376a9087d 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -234,6 +234,162 @@ batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
return tt_global_entry;
}
+/**
+ * batadv_tt_len() - compute length in bytes of given number of tt changes
+ * @changes_num: number of tt changes
+ *
+ * Return: computed length in bytes.
+ */
+static int batadv_tt_len(int changes_num)
+{
+ return changes_num * sizeof(struct batadv_tvlv_tt_change);
+}
+
+/**
+ * batadv_tt_local_transmit_size() - calculate the size of a full table response
+ * for a given number of VLANs and local TT entries
+ * @num_vlan: number of announced VLANs
+ * @num_entries: number of announced local TT entries
+ *
+ * Return: local translation table size in bytes.
+ */
+static int batadv_tt_local_transmit_size(u16 num_vlan, u16 num_entries)
+{
+ int hdr_size;
+
+ /* header size of tvlv encapsulated tt response payload */
+ hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
+ hdr_size += sizeof(struct batadv_tvlv_hdr);
+ hdr_size += sizeof(struct batadv_tvlv_tt_data);
+ hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
+
+ return hdr_size + batadv_tt_len(num_entries);
+}
+
+/**
+ * batadv_tt_local_reserve() - reserve room in the transmittable local table
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @num_vlan: number of VLANs to reserve
+ * @num_entries: number of local TT entries to reserve
+ * @table_size: stores the resulting worst case table size in bytes
+ *
+ * Add the requested number of VLANs and local TT entries to the reservation
+ * counters and check whether the local translation table would then still fit
+ * in a single full table response. The reservation is dropped again when it
+ * would not.
+ *
+ * The reservation has to happen before the related object is allocated. This
+ * way two parallel allocations cannot both observe enough room for themselves
+ * and end up with a local table which can no longer be transmitted.
+ *
+ * A granted reservation must be returned via batadv_tt_local_unreserve() when
+ * the related object is released or was never created.
+ *
+ * Return: true when the reservation was granted, false otherwise.
+ */
+static bool batadv_tt_local_reserve(struct batadv_priv *bat_priv, u16 num_vlan,
+ u16 num_entries, int *table_size)
+{
+ int packet_size_max = READ_ONCE(bat_priv->packet_size_max);
+
+ scoped_guard(spinlock_bh, &bat_priv->tt.reserve_lock) {
+ bat_priv->tt.reserved_vlans += num_vlan;
+ bat_priv->tt.reserved_entries += num_entries;
+
+ *table_size = batadv_tt_local_transmit_size(bat_priv->tt.reserved_vlans,
+ bat_priv->tt.reserved_entries);
+ if (*table_size <= packet_size_max)
+ return true;
+
+ bat_priv->tt.reserved_vlans -= num_vlan;
+ bat_priv->tt.reserved_entries -= num_entries;
+ }
+
+ return false;
+}
+
+/**
+ * batadv_tt_local_unreserve() - return room in the transmittable local table
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @num_vlan: number of VLANs to return
+ * @num_entries: number of local TT entries to return
+ */
+static void batadv_tt_local_unreserve(struct batadv_priv *bat_priv,
+ u16 num_vlan, u16 num_entries)
+{
+ scoped_guard(spinlock_bh, &bat_priv->tt.reserve_lock) {
+ bat_priv->tt.reserved_vlans -= num_vlan;
+ bat_priv->tt.reserved_entries -= num_entries;
+ }
+}
+
+/**
+ * batadv_tt_local_reserve_entry() - reserve room for a new local TT entry
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @addr: the mac address of the client to add
+ *
+ * Return: true when the reservation was granted, false otherwise.
+ */
+static bool batadv_tt_local_reserve_entry(struct batadv_priv *bat_priv,
+ const u8 *addr)
+{
+ int table_size;
+
+ if (batadv_tt_local_reserve(bat_priv, 0, 1, &table_size))
+ return true;
+
+ net_ratelimited_function(batadv_info, bat_priv->mesh_iface,
+ "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
+ table_size,
+ READ_ONCE(bat_priv->packet_size_max), addr);
+
+ return false;
+}
+
+/**
+ * batadv_tt_local_unreserve_entry() - return the room of a local TT entry
+ * @bat_priv: the bat priv with all the mesh interface information
+ */
+static void batadv_tt_local_unreserve_entry(struct batadv_priv *bat_priv)
+{
+ batadv_tt_local_unreserve(bat_priv, 0, 1);
+}
+
+/**
+ * batadv_tt_local_reserve_vlan() - reserve room for a new VLAN
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @vid: the VLAN identifier
+ *
+ * Each VLAN adds a per VLAN header to the full table response and therefore
+ * has to be reserved before batadv_meshif_create_vlan() allocates it.
+ *
+ * Return: true when the reservation was granted, false otherwise.
+ */
+bool batadv_tt_local_reserve_vlan(struct batadv_priv *bat_priv,
+ unsigned short vid)
+{
+ int table_size;
+
+ if (batadv_tt_local_reserve(bat_priv, 1, 0, &table_size))
+ return true;
+
+ net_ratelimited_function(batadv_info, bat_priv->mesh_iface,
+ "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new VLAN: %d\n",
+ table_size, READ_ONCE(bat_priv->packet_size_max),
+ batadv_print_vid(vid));
+
+ return false;
+}
+
+/**
+ * batadv_tt_local_unreserve_vlan() - return the room of a VLAN
+ * @bat_priv: the bat priv with all the mesh interface information
+ */
+void batadv_tt_local_unreserve_vlan(struct batadv_priv *bat_priv)
+{
+ batadv_tt_local_unreserve(bat_priv, 1, 0);
+}
+
/**
* batadv_tt_local_entry_release() - release tt_local_entry from lists and queue
* for free after rcu grace period
@@ -246,6 +402,7 @@ static void batadv_tt_local_entry_release(struct kref *ref)
tt_local_entry = container_of(ref, struct batadv_tt_local_entry,
common.refcount);
+ batadv_tt_local_unreserve_entry(tt_local_entry->vlan->bat_priv);
batadv_meshif_vlan_put(tt_local_entry->vlan);
kfree_rcu(tt_local_entry, common.rcu);
@@ -461,23 +618,21 @@ static u16 batadv_tt_flags_get(struct batadv_tt_common_entry *common)
}
/**
- * batadv_tt_local_event() - store a local TT event (ADD/DEL)
+ * __batadv_tt_local_event() - store a local TT event (ADD/DEL) with given flags
* @bat_priv: the bat priv with all the mesh interface information
- * @tt_local_entry: the TT entry involved in the event
- * @event_flags: flags to store in the event structure
+ * @common: the TT entry involved in the event
+ * @flags: flags of the TT entry combined with the event flags
*/
-static void batadv_tt_local_event(struct batadv_priv *bat_priv,
- struct batadv_tt_local_entry *tt_local_entry,
- u8 event_flags)
+static void __batadv_tt_local_event(struct batadv_priv *bat_priv,
+ const struct batadv_tt_common_entry *common,
+ u8 flags)
{
- struct batadv_tt_common_entry *common = &tt_local_entry->common;
struct batadv_tt_change_node *tt_change_node;
struct batadv_tt_change_node *entry;
struct batadv_tt_change_node *safe;
bool del_op_requested;
bool del_op_entry;
size_t changes;
- u8 flags;
tt_change_node = kmem_cache_alloc(batadv_tt_change_cache, GFP_ATOMIC);
if (!tt_change_node)
@@ -488,8 +643,6 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
ether_addr_copy(tt_change_node->change.addr, common->addr);
tt_change_node->change.vid = htons(common->vid);
- flags = batadv_tt_flags_get(common) | event_flags;
-
tt_change_node->change.flags = flags;
del_op_requested = flags & BATADV_TT_CLIENT_DEL;
@@ -538,14 +691,20 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
}
/**
- * batadv_tt_len() - compute length in bytes of given number of tt changes
- * @changes_num: number of tt changes
- *
- * Return: computed length in bytes.
+ * batadv_tt_local_event() - store a local TT event (ADD/DEL)
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @tt_local_entry: the TT entry involved in the event
+ * @event_flags: flags to store in the event structure
*/
-static int batadv_tt_len(int changes_num)
+static void batadv_tt_local_event(struct batadv_priv *bat_priv,
+ struct batadv_tt_local_entry *tt_local_entry,
+ u8 event_flags)
{
- return changes_num * sizeof(struct batadv_tvlv_tt_change);
+ struct batadv_tt_common_entry *common = &tt_local_entry->common;
+ u8 flags;
+
+ flags = batadv_tt_flags_get(common) | event_flags;
+ __batadv_tt_local_event(bat_priv, common, flags);
}
/**
@@ -571,7 +730,6 @@ static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
struct batadv_meshif_vlan *vlan;
u16 tt_local_entries = 0;
u16 num_vlan = 0;
- int hdr_size;
rcu_read_lock();
hlist_for_each_entry_rcu(vlan, &bat_priv->meshif_vlan_list, list) {
@@ -580,13 +738,7 @@ static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
}
rcu_read_unlock();
- /* header size of tvlv encapsulated tt response payload */
- hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
- hdr_size += sizeof(struct batadv_tvlv_hdr);
- hdr_size += sizeof(struct batadv_tvlv_tt_data);
- hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
-
- return hdr_size + batadv_tt_len(tt_local_entries);
+ return batadv_tt_local_transmit_size(num_vlan, tt_local_entries);
}
/**
@@ -790,23 +942,15 @@ batadv_tt_local_create(struct net_device *mesh_iface, const u8 *addr,
struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
struct batadv_tt_local_entry *tt_local;
struct batadv_meshif_vlan *vlan;
- int packet_size_max;
- int table_size;
- /* Ignore the client if we cannot send it in a full table response. */
- table_size = batadv_tt_local_table_transmit_size(bat_priv);
- table_size += batadv_tt_len(1);
- packet_size_max = READ_ONCE(bat_priv->packet_size_max);
- if (table_size > packet_size_max) {
- net_ratelimited_function(batadv_info, mesh_iface,
- "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
- table_size, packet_size_max, addr);
+ if (!batadv_tt_local_reserve_entry(bat_priv, addr))
return NULL;
- }
tt_local = kmem_cache_alloc(batadv_tl_cache, GFP_ATOMIC);
- if (!tt_local)
+ if (!tt_local) {
+ batadv_tt_local_unreserve_entry(bat_priv);
return NULL;
+ }
/* increase the refcounter of the related vlan */
vlan = batadv_meshif_vlan_get(bat_priv, vid);
@@ -815,6 +959,7 @@ batadv_tt_local_create(struct net_device *mesh_iface, const u8 *addr,
"adding TT local entry %pM to non-existent VLAN %d\n",
addr, batadv_print_vid(vid));
kmem_cache_free(batadv_tl_cache, tt_local);
+ batadv_tt_local_unreserve_entry(bat_priv);
return NULL;
}
@@ -1420,21 +1565,37 @@ int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
}
/**
- * batadv_tt_local_set_pending_event() - trigger events for TT pending removal
+ * batadv_tt_local_set_pending() - mark local TT entry as pending removal
* @bat_priv: the bat priv with all the mesh interface information
- * @tt_local_entry: local TT entry which was marked as BATADV_TT_CLIENT_PENDING
+ * @tt_local_entry: local TT entry to mark as BATADV_TT_CLIENT_PENDING
* @flags: TT change flags to announce together with the pending removal
* @message: debug message describing the reason for the change
*
- * Schedule the TT change announcement for the entry. The caller must already
- * have added BATADV_TT_CLIENT_PENDING to the @tt_local_entry
+ * Schedule the TT change announcement and set BATADV_TT_CLIENT_PENDING on the
+ * entry. The entry is kept in the local table until the next TTVN increment
+ * so that a consistency-check response can still be answered.
+ *
+ * Next to the flags_lock of the entry, the caller must hold the hash bucket
+ * list_lock of @tt_local_entry. Otherwise
+ * batadv_tt_local_purge_pending_clients() could remove the entry before its
+ * change was queued.
*/
static void
-batadv_tt_local_set_pending_event(struct batadv_priv *bat_priv,
- struct batadv_tt_local_entry *tt_local_entry,
- u16 flags, const char *message)
+batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
+ struct batadv_tt_local_entry *tt_local_entry,
+ u16 flags, const char *message)
+ __must_hold(&tt_local_entry->common.flags_lock)
{
- batadv_tt_local_event(bat_priv, tt_local_entry, flags);
+ struct batadv_tt_common_entry *common = &tt_local_entry->common;
+ struct batadv_hashtable *hash = bat_priv->tt.local_hash;
+ u32 i;
+
+ i = batadv_choose_tt(common, hash->size);
+ lockdep_assert_held(&hash->list_locks[i]);
+ lockdep_assert_held(&common->flags_lock);
+
+ __batadv_tt_local_event(bat_priv, common, common->flags | flags);
+ common->flags |= BATADV_TT_CLIENT_PENDING;
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Local tt entry (%pM, vid: %d) pending to be removed: %s\n",
@@ -1443,45 +1604,77 @@ batadv_tt_local_set_pending_event(struct batadv_priv *bat_priv,
}
/**
- * batadv_tt_local_mark_removed() - mark a local entry as removed
+ * batadv_tt_local_mark_removed() - mark a local entry as removed and queue DEL
+ * @bat_priv: the bat priv with all the mesh interface information
* @tt_local_entry: local TT entry to mark
+ * @message: message to append to the log on deletion
* @roaming: true if the deletion is due to a roaming event
* @curr_flags: pointer to store the flags of the entry before it was marked
*
+ * An already announced entry is marked as BATADV_TT_CLIENT_PENDING and the
+ * (roamed) DEL change is queued. Both happen under the hash bucket list_lock
+ * of the entry to prevent concurrent batadv_tt_local_purge_pending_clients()
+ * from removing the entry and batadv_tt_local_transition_new() from clearing
+ * BATADV_TT_CLIENT_NEW after it was checked.
+ *
* Return: true if the entry has to be kept in the local table until the next
* ttvn increment, false if it can be purged immediately.
*/
static bool
-batadv_tt_local_mark_removed(struct batadv_tt_local_entry *tt_local_entry,
- bool roaming, u16 *curr_flags)
+batadv_tt_local_mark_removed(struct batadv_priv *bat_priv,
+ struct batadv_tt_local_entry *tt_local_entry,
+ const char *message, bool roaming, u16 *curr_flags)
{
+ spinlock_t *list_lock; /* protects write access to the hash lists */
struct batadv_tt_common_entry *common = &tt_local_entry->common;
+ struct batadv_hashtable *hash = bat_priv->tt.local_hash;
bool pending = false;
+ u16 flags;
+ u32 i;
+
+ i = batadv_choose_tt(common, hash->size);
+ list_lock = &hash->list_locks[i];
+
+ spin_lock_bh(list_lock);
scoped_guard(spinlock_bh, &common->flags_lock) {
*curr_flags = common->flags;
- /* mark the local client as ROAMed */
+ /* if this global entry addition is due to a roaming, the node
+ * has to mark the local entry as "roamed" in order to
+ * correctly reroute packets later
+ */
if (roaming)
common->flags |= BATADV_TT_CLIENT_ROAM;
- if (!(common->flags & BATADV_TT_CLIENT_NEW)) {
- common->flags |= BATADV_TT_CLIENT_PENDING;
- pending = true;
- }
+ if (common->flags & BATADV_TT_CLIENT_NEW)
+ break;
+
+ flags = BATADV_TT_CLIENT_DEL;
+ if (roaming)
+ flags |= BATADV_TT_CLIENT_ROAM;
+
+ batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags,
+ message);
+ pending = true;
}
+ spin_unlock_bh(list_lock);
+
return pending;
}
/**
- * batadv_tt_local_remove_now() - purge a local entry which was never announced
+ * batadv_tt_local_remove_now() - purge a local entry which was not (yet) announced
* @bat_priv: the bat priv with all the mesh interface information
* @tt_local_entry: local TT entry to purge
*
- * A client which was added right after the last ttvn increment was never sent
- * to the other nodes. It can therefore be dropped from the local table without
- * waiting for the next ttvn increment.
+ * A client which was added right after the last ttvn increment was not (yet)
+ * sent to the other nodes. It can therefore be dropped from the local table
+ * without waiting for the next ttvn increment.
+ *
+ * If it was still announced by a parallel context before it was removed from
+ * the hash, then the local TT size adjustment will be handled automatically.
*/
static void
batadv_tt_local_remove_now(struct batadv_priv *bat_priv,
@@ -1490,8 +1683,6 @@ batadv_tt_local_remove_now(struct batadv_priv *bat_priv,
struct batadv_tt_common_entry *common = &tt_local_entry->common;
struct hlist_node *tt_removed_node;
- batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);
-
/* remove exactly this object when still present in hash */
tt_removed_node = batadv_hash_remove(bat_priv->tt.local_hash,
batadv_compare_tt_entry,
@@ -1499,6 +1690,8 @@ batadv_tt_local_remove_now(struct batadv_priv *bat_priv,
if (!tt_removed_node)
return;
+ batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);
+
/* batadv_tt_local_transition_new() may have committed the entry and
* thus counted it in the local table size since the
* BATADV_TT_CLIENT_NEW check in batadv_tt_local_mark_removed().
@@ -1526,27 +1719,16 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
{
struct batadv_tt_local_entry *tt_local_entry;
u16 curr_flags;
- u16 flags;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
if (!tt_local_entry)
return BATADV_NO_FLAGS;
- flags = BATADV_TT_CLIENT_DEL;
- /* if this global entry addition is due to a roaming, the node has to
- * mark the local entry as "roamed" in order to correctly reroute
- * packets later
+ /* if this client has been added right now, it is possible to
+ * immediately purge it
*/
- if (roaming)
- flags |= BATADV_TT_CLIENT_ROAM;
-
- if (batadv_tt_local_mark_removed(tt_local_entry, roaming, &curr_flags))
- batadv_tt_local_set_pending_event(bat_priv, tt_local_entry,
- flags, message);
- else
- /* if this client has been added right now, it is possible to
- * immediately purge it
- */
+ if (!batadv_tt_local_mark_removed(bat_priv, tt_local_entry, message,
+ roaming, &curr_flags))
batadv_tt_local_remove_now(bat_priv, tt_local_entry);
batadv_tt_local_entry_put(tt_local_entry);
@@ -1571,37 +1753,25 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
hlist_for_each_entry_safe(tt_common_entry, node_tmp, head,
hash_entry) {
- bool cont = false;
-
tt_local_entry = container_of(tt_common_entry,
struct batadv_tt_local_entry,
common);
scoped_guard(spinlock_bh, &tt_local_entry->common.flags_lock) {
- if (tt_local_entry->common.flags & BATADV_TT_CLIENT_NOPURGE) {
- cont = true;
+ if (tt_local_entry->common.flags & BATADV_TT_CLIENT_NOPURGE)
break;
- }
/* entry already marked for deletion */
- if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) {
- cont = true;
+ if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)
break;
- }
- if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout)) {
- cont = true;
+ if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout))
break;
- }
- tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING;
+ batadv_tt_local_set_pending(bat_priv, tt_local_entry,
+ BATADV_TT_CLIENT_DEL,
+ "timed out");
}
-
- if (cont)
- continue;
-
- batadv_tt_local_set_pending_event(bat_priv, tt_local_entry,
- BATADV_TT_CLIENT_DEL, "timed out");
}
}
@@ -1870,9 +2040,10 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
* @tt_global_entry: the global TT entry of the announced client
* @flags: TT flags announced for this non-mesh client
*
- * A client which is announced by another originator is no longer a local
- * client. Remove it from the local table and take over the WIFI flag it was
- * tracked with.
+ * A non-multicast client which is announced by another originator is no longer
+ * a local client. Remove it from the local table and (for the global entry)
+ * take over the WIFI flag it was tracked with. Reset the roaming flag in case
+ * the announcement didn't contain the roaming flag.
*/
static void
batadv_tt_global_purge_local(struct batadv_priv *bat_priv,
diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h
index 618d9dbca5eac..ca01d20ee2d7f 100644
--- a/net/batman-adv/translation-table.h
+++ b/net/batman-adv/translation-table.h
@@ -16,6 +16,9 @@
#include <linux/types.h>
int batadv_tt_init(struct batadv_priv *bat_priv);
+bool batadv_tt_local_reserve_vlan(struct batadv_priv *bat_priv,
+ unsigned short vid);
+void batadv_tt_local_unreserve_vlan(struct batadv_priv *bat_priv);
bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
unsigned short vid, int ifindex, u32 mark);
u16 batadv_tt_local_remove(struct batadv_priv *bat_priv,
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 67872927cfb50..c19caa84e6920 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1055,6 +1055,25 @@ struct batadv_priv_tt {
*/
spinlock_t commit_lock;
+ /**
+ * @reserved_entries: number of local TT entries which are currently
+ * allocated or about to be allocated. Together with @reserved_vlans it
+ * describes the worst case size of a full table response.
+ */
+ u16 reserved_entries;
+
+ /**
+ * @reserved_vlans: number of mesh interface VLANs which are currently
+ * allocated or about to be allocated. Together with @reserved_entries
+ * it describes the worst case size of a full table response.
+ */
+ u16 reserved_vlans;
+
+ /**
+ * @reserve_lock: lock protecting @reserved_entries & @reserved_vlans
+ */
+ spinlock_t reserve_lock;
+
/** @work: work queue callback item for translation table purging */
struct delayed_work work;
};
|
| 3/2 | 2026/10/01 09:31 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 11790 output: 31 thoughts: 1229 Thoughts: **My Assessment of the batman-adv Patch Series** Okay, so I've been asked to assess this patch series for syzkaller fuzzing triage on the kernel HEAD. First, let's make sure we're on the same page with the triage guidelines. Reachability is the primary gate: can this code be executed in a virtualized environment like QEMU or GCE? We don't blindly trust refactoring claims. Any new or modified assertions in reachable code *must* be fuzzed. Conversely, if it's non-kernel code, structurally unreachable hardware, or dead code, we don't need to consider it for fuzzing. Core subsystems, refactorings in reachable code, runtime assertions, and reachable drivers are all fair game. batman-adv is a virtual network device, accessible via a number of interfaces – rtnl, generic netlink, tap, etc. – so that’s good. Now, let's extract some focus symbols and identify necessary `CONFIG_` options. Alright, risk and request analysis. This is a defensive security operation, standard fuzzing triage. Nothing sinister here. Benign request. Now, let's dive into the code changes. I see these files were modified: * `net/batman-adv/bridge_loop_avoidance.c`: A simple `NULL` assignment on an error path. Probably not interesting for fuzzing in itself. * `net/batman-adv/distributed-arp-table.c`: `assign_bit` change. Also, likely low priority. * `net/batman-adv/main.c`: A new spinlock initialization. Fine. * `net/batman-adv/mesh-interface.c`: Calls to new translation table functions. * `net/batman-adv/translation-table.c`: **Here's where it gets interesting.** There are a bunch of new functions related to a translation table, specifically around reserving and unreserving entries and VLANs. Important, but the *really* interesting bits are in `batadv_tt_local_set_pending`, `batadv_tt_local_mark_removed`, and `batadv_tt_local_remove_now`. We have `lockdep_assert_held` calls! Specifically, `lockdep_assert_held(&hash->list_locks[i]);` and `lockdep_assert_held(&common->flags_lock);`. This *strongly* suggests changes to locking and concurrency. It seems like they're redesigning the locking strategy for the translation table, including holding the hash bucket list lock and reordering events. These assertions are gold for fuzzing. A lockdep assertion failure is a serious bug waiting to happen. * `net/batman-adv/translation-table.h`: Header changes to match the .c changes. * `net/batman-adv/types.h`: Adding new fields `reserved_entries`, `reserved_vlans`, `reserve_lock` confirming the locking change. Notice the key points: 1. We have `lockdep_assert` calls, which is high signal 2. Significant changes to locking and concurrency, in the translation table. So, is batman-adv reachable? YES! The text spells out that it's a virtual network device and Syzkaller has the full setup. Given the new assertions and the concurrency changes, I'd say this patch series is *definitely* WorthFuzzing=true. I'm going to want to prioritize fuzzing the code paths touched by those `lockdep_assert` calls, especially `batadv_tt_local_set_pending` and related functions. We’ll need to figure out what `CONFIG_` options are required and get syzkaller targeting those functions. |
| 4/2 | 2026/10/01 09:32 | tool | grepper |
0mArgs:{
"Expression": "config BATMAN_ADV",
"PathPrefix": "net/batman-adv"
}Results: {
"Output": "net/batman-adv/Kconfig-9-\nnet/batman-adv/Kconfig:10:config BATMAN_ADV\nnet/batman-adv/Kconfig-11-\ttristate \"B.A.T.M.A.N. Advanced Meshing Protocol\"\n--\nnet/batman-adv/Kconfig-19-\nnet/batman-adv/Kconfig:20:config BATMAN_ADV_BATMAN_V\nnet/batman-adv/Kconfig-21-\tbool \"B.A.T.M.A.N. V protocol\"\n--\nnet/batman-adv/Kconfig-33-\nnet/batman-adv/Kconfig:34:config BATMAN_ADV_BLA\nnet/batman-adv/Kconfig-35-\tbool \"Bridge Loop Avoidance\"\n--\nnet/batman-adv/Kconfig-46-\nnet/batman-adv/Kconfig:47:config BATMAN_ADV_DAT\nnet/batman-adv/Kconfig-48-\tbool \"Distributed ARP Table\"\n--\nnet/batman-adv/Kconfig-56-\nnet/batman-adv/Kconfig:57:config BATMAN_ADV_MCAST\nnet/batman-adv/Kconfig-58-\tbool \"Multicast optimisation\"\n--\nnet/batman-adv/Kconfig-65-\nnet/batman-adv/Kconfig:66:config BATMAN_ADV_DEBUG\nnet/batman-adv/Kconfig-67-\tbool \"B.A.T.M.A.N. debugging\"\n--\nnet/batman-adv/Kconfig-74-\nnet/batman-adv/Kconfig:75:config BATMAN_ADV_TRACING\nnet/batman-adv/Kconfig-76-\tbool \"B.A.T.M.A.N. tracing support\"\n"
} |
| 5/2 | 2026/10/01 09:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 14335 output: 35 thoughts: 29 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Investigating BATMAN-ADV Configuration Dependencies** Alright, let's get down to brass tacks. I need to understand the dependencies for BATMAN-ADV, and specifically how BLA and DAT are woven into that configuration. My instinct says the central source of truth for these dependencies lies within `net/batman-adv/Kconfig`. That's where the conditional compilation magic happens, the places where features are enabled or disabled based on various underlying kernel configurations. I'll need to drill down into that Kconfig file. I'm assuming that it will clarify the interplay and dependencies between BATMAN_ADV and BLA / DAT, whether they are optional modules, or fundamental components. |
| 6/2 | 2026/10/01 09:32 | tool | read-file |
0mArgs:{
"File": "net/batman-adv/Kconfig",
"FirstLine": 1,
"LineCount": 60
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t# Copyright (C) B.A.T.M.A.N. contributors:\n 3:\t#\n 4:\t# Marek Lindner, Simon Wunderlich\n 5:\t\n 6:\t#\n 7:\t# B.A.T.M.A.N meshing protocol\n 8:\t#\n 9:\t\n 10:\tconfig BATMAN_ADV\n 11:\t\ttristate \"B.A.T.M.A.N. Advanced Meshing Protocol\"\n 12:\t\tselect CRC32\n 13:\t\thelp\n 14:\t\t B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is\n 15:\t\t a routing protocol for multi-hop ad-hoc mesh networks. The\n 16:\t\t networks may be wired or wireless. See\n 17:\t\t https://www.open-mesh.org/ for more information and user space\n 18:\t\t tools.\n 19:\t\n 20:\tconfig BATMAN_ADV_BATMAN_V\n 21:\t\tbool \"B.A.T.M.A.N. V protocol\"\n 22:\t\tdepends on BATMAN_ADV \u0026\u0026 !(CFG80211=m \u0026\u0026 BATMAN_ADV=y)\n 23:\t\tdefault y\n 24:\t\thelp\n 25:\t\t This option enables the B.A.T.M.A.N. V protocol, the successor\n 26:\t\t of the currently used B.A.T.M.A.N. IV protocol. The main\n 27:\t\t changes include splitting of the OGM protocol into a neighbor\n 28:\t\t discovery protocol (Echo Location Protocol, ELP) and a new OGM\n 29:\t\t Protocol OGMv2 for flooding protocol information through the\n 30:\t\t network, as well as a throughput based metric.\n 31:\t\t B.A.T.M.A.N. V is currently considered experimental and not\n 32:\t\t compatible to B.A.T.M.A.N. IV networks.\n 33:\t\n 34:\tconfig BATMAN_ADV_BLA\n 35:\t\tbool \"Bridge Loop Avoidance\"\n 36:\t\tdepends on BATMAN_ADV \u0026\u0026 INET\n 37:\t\tselect CRC16\n 38:\t\tselect NET_CRC32C\n 39:\t\tdefault y\n 40:\t\thelp\n 41:\t\t This option enables BLA (Bridge Loop Avoidance), a mechanism\n 42:\t\t to avoid Ethernet frames looping when mesh nodes are connected\n 43:\t\t to both the same LAN and the same mesh. If you will never use\n 44:\t\t more than one mesh node in the same LAN, you can safely remove\n 45:\t\t this feature and save some space.\n 46:\t\n 47:\tconfig BATMAN_ADV_DAT\n 48:\t\tbool \"Distributed ARP Table\"\n 49:\t\tdepends on BATMAN_ADV \u0026\u0026 INET\n 50:\t\tdefault y\n 51:\t\thelp\n 52:\t\t This option enables DAT (Distributed ARP Table), a DHT based\n 53:\t\t mechanism that increases ARP reliability on sparse wireless\n 54:\t\t mesh networks. If you think that your network does not need\n 55:\t\t this option you can safely remove it and save some space.\n 56:\t\n 57:\tconfig BATMAN_ADV_MCAST\n 58:\t\tbool \"Multicast optimisation\"\n 59:\t\tdepends on BATMAN_ADV \u0026\u0026 INET \u0026\u0026 !(BRIDGE=m \u0026\u0026 BATMAN_ADV=y)\n 60:\t\tdefault y\n"
} |
| 7/2 | 2026/10/01 09:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 15482 output: 34 thoughts: 16 Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own: **Analyzing the Makefile in net/batman-adv** Alright, time to get into the nitty-gritty of the `batman-adv` Makefile. I need to understand how the build process is structured for this critical networking component. I'm going to start with the main `Makefile` to see how it's orchestrating everything. I'm especially interested in the dependencies, compilation flags, and linking steps. I need to be able to quickly identify any potential bottlenecks or areas for optimization within the build process, or perhaps understand how new features are integrated. The goal is a clear picture of how this component is being built. |
| 8/2 | 2026/10/01 09:32 | tool | read-file |
0mArgs:{
"File": "net/batman-adv/Makefile",
"FirstLine": 1,
"LineCount": 40
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t# Copyright (C) B.A.T.M.A.N. contributors:\n 3:\t#\n 4:\t# Marek Lindner, Simon Wunderlich\n 5:\t\n 6:\tobj-$(CONFIG_BATMAN_ADV) += batman-adv.o\n 7:\tbatman-adv-y += bat_algo.o\n 8:\tbatman-adv-y += bat_iv_ogm.o\n 9:\tbatman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v.o\n 10:\tbatman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_elp.o\n 11:\tbatman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_ogm.o\n 12:\tbatman-adv-y += bitarray.o\n 13:\tbatman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o\n 14:\tbatman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o\n 15:\tbatman-adv-y += fragmentation.o\n 16:\tbatman-adv-y += gateway_client.o\n 17:\tbatman-adv-y += gateway_common.o\n 18:\tbatman-adv-y += hard-interface.o\n 19:\tbatman-adv-y += hash.o\n 20:\tbatman-adv-$(CONFIG_BATMAN_ADV_DEBUG) += log.o\n 21:\tbatman-adv-y += main.o\n 22:\tbatman-adv-y += mesh-interface.o\n 23:\tbatman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast.o\n 24:\tbatman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast_forw.o\n 25:\tbatman-adv-y += netlink.o\n 26:\tbatman-adv-y += originator.o\n 27:\tbatman-adv-y += routing.o\n 28:\tbatman-adv-y += send.o\n 29:\tbatman-adv-$(CONFIG_BATMAN_ADV_TRACING) += trace.o\n 30:\tbatman-adv-y += tp_meter.o\n 31:\tbatman-adv-y += translation-table.o\n 32:\tbatman-adv-y += tvlv.o\n 33:\tbatman-adv-y += version.o\n 34:\t\n 35:\tCFLAGS_trace.o := -I$(src)\n"
} |
| 9/2 | 2026/10/01 09:32 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 16382 output: 44 thoughts: 1248 Thoughts: **Fuzzing Analysis: B.A.T.M.A.N. Advanced Translation Table Reservation** Okay, let's break this down. This patch is touching a significant chunk of the B.A.T.M.A.N. Advanced codebase. I see it's modifying files related to bridge loop avoidance, the distributed ARP table, the main B.A.T.M.A.N. Advanced code, and mesh interface management, with the core changes concentrated in the translation table implementation. Seems like they're introducing some critical changes for local translation table entry management, specifically reservation and locking mechanisms for when entries or VLANs are added, removed, or purged. My initial read is that this is introducing a reservation accounting system, with `reserved_entries` and `reserved_vlans`, all protected by `reserve_lock` to prevent the translation table from exceeding its maximum packet size. That makes sense for preventing fragmentation, particularly in larger or dynamic networks. Looking at the added/modified functions, the focus seems to be on those reservation functions: `batadv_tt_local_reserve_vlan`, `batadv_tt_local_unreserve_vlan`, `batadv_tt_local_reserve_entry`, `batadv_tt_local_unreserve_entry`, and the core `batadv_tt_local_reserve` and `batadv_tt_local_unreserve`. There are also changes to VLAN management with `batadv_meshif_create_vlan` and `batadv_meshif_vlan_release`. We've got entry lifecycle management with `batadv_tt_local_create`, `batadv_tt_local_remove`, and `batadv_tt_local_purge_list`. Then, locking assertions, with `batadv_tt_local_set_pending` (with lockdep assertions, that's good for catching concurrency issues) and `batadv_tt_local_mark_removed` (which takes the hash bucket `list_lock`), and related clean-up with `batadv_tt_local_remove_now`. There are other changes to initialization, error handling, and a handler function. Now, from a fuzzing perspective, this looks promising. B.A.T.M.A.N. Advanced is eminently fuzzable because we can easily emulate it in a virtual environment. We can create and manage interfaces via generic netlink (`genl`) and rtnl, and drive updates to the translation tables with network traffic, ARP/DHCP, local client traffic, VLAN interfaces, or explicit netlink commands. This whole patch is screaming `WorthFuzzing = true`, especially with the new locking, runtime assertions, and reservation state tracking. Okay, let's identify some FocusSymbols. We need the functions that are changed the most, and which are not ubiquitous. Here are some that stand out: `batadv_meshif_create_vlan`, `batadv_meshif_vlan_release`, `batadv_tt_local_reserve_vlan`, `batadv_tt_local_unreserve_vlan`, `batadv_tt_local_create`, `batadv_tt_local_remove`, `batadv_tt_local_mark_removed`, `batadv_tt_local_set_pending`, `batadv_tt_local_remove_now`, `batadv_tt_local_purge_list`, `batadv_bla_init`, and `batadv_dat_tvlv_ogm_handler_v1`. These are the functions where the new logic is concentrated. A quick check to determine if any of these functions are static and not inlined. If they're static, they're harder to reach. Here we go: * `batadv_tt_local_reserve_vlan` is non-static (declared in the header). * `batadv_tt_local_unreserve_vlan` is non-static (declared in the header). * `batadv_meshif_create_vlan` is non-static. * `batadv_meshif_vlan_release` is non-static. * `batadv_tt_local_create` is non-static (let's verify the signature though). * `batadv_tt_local_remove` is non-static. These are all good entry points for fuzzing. We have non-static functions related to key operations: vlan creation/release and translation table entry management. This is great for a fuzzer! |
| 10/2 | 2026/10/01 09:33 | tool | grepper |
0mArgs:{
"Expression": "^[a-z0-9_ *]+batadv_tt_local_create",
"PathPrefix": "net/batman-adv"
}Results: {
"Output": "net/batman-adv/translation-table.c=884=static bool batadv_tt_local_add_existing(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/translation-table.c-924-/**\nnet/batman-adv/translation-table.c:925: * batadv_tt_local_create() - allocate and initialize a local TT entry\nnet/batman-adv/translation-table.c-926- * @mesh_iface: netdev struct of the mesh interface\n"
} |
| 11/2 | 2026/10/01 09:33 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 18800 output: 40 thoughts: 28 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing the batman-adv Translation Table Function** Alright, let's dive in. I need to get a clear picture of how the translation table is being handled within `batman-adv`. Let me zero in on the function definition itself. The code of interest must be somewhere around line 935 in `net/batman-adv/translation-table.c`. I'm expecting to see the core logic for address resolution and forwarding, likely involving lookups, cache management, and potentially some form of invalidation or aging mechanism. I'm keen to see how they're handling concurrency, too. Given the nature of a distributed network like batman-adv, this is critical, right? I'm anticipating some locks or atomic operations to ensure consistency. Also, I'm curious about the specific data structures used for the table. Is it a hash table, a radix tree, or something more specialized? The choice impacts lookup performance and memory footprint significantly. |
| 12/2 | 2026/10/01 09:33 | tool | read-file |
0mArgs:{
"File": "net/batman-adv/translation-table.c",
"FirstLine": 930,
"LineCount": 25
}Results: {
"Contents": " 930:\t *\n 931:\t * The returned entry is not yet part of bat_priv-\u003ett.local_hash. It is marked\n 932:\t * as BATADV_TT_CLIENT_NEW to avoid sending it in a full table response going\n 933:\t * out before the next ttvn increment (consistency check).\n 934:\t *\n 935:\t * Return: the new entry with an initialized reference counter on success, NULL\n 936:\t * otherwise.\n 937:\t */\n 938:\tstatic struct batadv_tt_local_entry *\n 939:\tbatadv_tt_local_create(struct net_device *mesh_iface, const u8 *addr,\n 940:\t\t\t unsigned short vid, bool iif_is_wifi)\n 941:\t{\n 942:\t\tstruct batadv_priv *bat_priv = netdev_priv(mesh_iface);\n 943:\t\tstruct batadv_tt_local_entry *tt_local;\n 944:\t\tstruct batadv_meshif_vlan *vlan;\n 945:\t\n 946:\t\tif (!batadv_tt_local_reserve_entry(bat_priv, addr))\n 947:\t\t\treturn NULL;\n 948:\t\n 949:\t\ttt_local = kmem_cache_alloc(batadv_tl_cache, GFP_ATOMIC);\n 950:\t\tif (!tt_local) {\n 951:\t\t\tbatadv_tt_local_unreserve_entry(bat_priv);\n 952:\t\t\treturn NULL;\n 953:\t\t}\n 954:\t\n"
} |
| 13/2 | 2026/10/01 09:33 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19520 output: 34 thoughts: 41 Thoughts: Here's a summary of my thought process: **Diving into `batadv_tt_local_create`** Okay, so I see `batadv_tt_local_create` is declared as `static` and is called by `batadv_tt_local_add`. That immediately tells me this function has a very specific scope within this module. The next logical step is to understand its usage. I need to understand *why* it's being called, and what it's responsible for. Since the code structure suggests a limited context, I need to verify that. I'm going to quickly check the direct callers of `batadv_tt_local_create` to ensure I have a complete picture of its role and dependencies. I expect this will be a fairly focused exploration. |
| 14/2 | 2026/10/01 09:33 | tool | grepper |
0mArgs:{
"Expression": "batadv_tt_local_create",
"PathPrefix": "net/batman-adv"
}Results: {
"Output": "net/batman-adv/translation-table.c=884=static bool batadv_tt_local_add_existing(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/translation-table.c-924-/**\nnet/batman-adv/translation-table.c:925: * batadv_tt_local_create() - allocate and initialize a local TT entry\nnet/batman-adv/translation-table.c-926- * @mesh_iface: netdev struct of the mesh interface\n--\nnet/batman-adv/translation-table.c=938=static struct batadv_tt_local_entry *\nnet/batman-adv/translation-table.c:939:batadv_tt_local_create(struct net_device *mesh_iface, const u8 *addr,\nnet/batman-adv/translation-table.c-940-\t\t unsigned short vid, bool iif_is_wifi)\n--\nnet/batman-adv/translation-table.c=1057=bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,\n--\nnet/batman-adv/translation-table.c-1079-\t} else {\nnet/batman-adv/translation-table.c:1080:\t\ttt_local = batadv_tt_local_create(mesh_iface, addr, vid, iif_is_wifi);\nnet/batman-adv/translation-table.c-1081-\t\tif (!tt_local)\n"
} |
| 15/2 | 2026/10/01 09:33 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20158 output: 36 thoughts: 51 Thoughts: Here's my take on the thought process, as requested: **Investigating Batadv's TT Local Management** Okay, so `batadv_tt_local_add` is an exposed function, either exported directly or declared in a header file, and crucially, it calls `batadv_tt_local_create`. This means it's likely a critical entry point for setting up something local to the TT (Tunneling Table) in the B.A.T.M.A.N. advanced mesh networking code. I need to understand its relationship to the TT. Since `batadv_tt_local_add` is used to *add*, I'm immediately interested in the opposite: who is calling `batadv_tt_local_remove`? The removal function is just as important. Knowing who removes these local TT entries is going to give me a complete view of its lifecycle and dependencies. It’s absolutely critical that I understand the add/remove balance, as an imbalance suggests a potential memory leak or incorrect cleanup. Let's see who's interacting with the TT removal function. |
| 16/2 | 2026/10/01 09:33 | tool | grepper |
0mArgs:{
"Expression": "batadv_tt_local_remove\\b",
"PathPrefix": "net/batman-adv"
}Results: {
"Output": "net/batman-adv/mesh-interface.c=141=static int batadv_interface_set_mac_addr(struct net_device *dev, void *p)\n--\nnet/batman-adv/mesh-interface.c-159-\thlist_for_each_entry_rcu(vlan, \u0026bat_priv-\u003emeshif_vlan_list, list) {\nnet/batman-adv/mesh-interface.c:160:\t\tbatadv_tt_local_remove(bat_priv, old_addr, vlan-\u003evid,\nnet/batman-adv/mesh-interface.c-161-\t\t\t\t \"mac address changed\", false);\n--\nnet/batman-adv/mesh-interface.c=659=void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/mesh-interface.c-664-\t */\nnet/batman-adv/mesh-interface.c:665:\tbatadv_tt_local_remove(bat_priv, bat_priv-\u003emesh_iface-\u003edev_addr,\nnet/batman-adv/mesh-interface.c-666-\t\t\t vlan-\u003evid, \"vlan interface destroyed\", false);\n--\nnet/batman-adv/multicast.c=684=static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/multicast.c-696-\nnet/batman-adv/multicast.c:697:\t\tbatadv_tt_local_remove(bat_priv, mcast_entry-\u003eaddr,\nnet/batman-adv/multicast.c-698-\t\t\t\t BATADV_NO_FLAGS,\n--\nnet/batman-adv/translation-table.c=1680=batadv_tt_local_remove_now(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/translation-table.c-1706-/**\nnet/batman-adv/translation-table.c:1707: * batadv_tt_local_remove() - logically remove an entry from the local table\nnet/batman-adv/translation-table.c-1708- * @bat_priv: the bat priv with all the mesh interface information\n--\nnet/batman-adv/translation-table.c-1715- */\nnet/batman-adv/translation-table.c:1716:u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,\nnet/batman-adv/translation-table.c-1717-\t\t\t unsigned short vid, const char *message,\n--\nnet/batman-adv/translation-table.c=2049=batadv_tt_global_purge_local(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/translation-table.c-2062-\t/* remove address from local hash if present */\nnet/batman-adv/translation-table.c:2063:\tlocal_flags = batadv_tt_local_remove(bat_priv, common-\u003eaddr, common-\u003evid,\nnet/batman-adv/translation-table.c-2064-\t\t\t\t\t \"global tt received\",\n--\nnet/batman-adv/translation-table.h=22=bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,\nnet/batman-adv/translation-table.h-23-\t\t\t unsigned short vid, int ifindex, u32 mark);\nnet/batman-adv/translation-table.h:24:u16 batadv_tt_local_remove(struct batadv_priv *bat_priv,\nnet/batman-adv/translation-table.h-25-\t\t\t const u8 *addr, unsigned short vid,\n"
} |
| 17/2 | 2026/10/01 09:33 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 21443 output: 34 thoughts: 29 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing `batadv_tt_local_purge` and Its Callers** Alright, my brain's kicking into gear. My initial thought process is centered around understanding the garbage collection strategy for local TT entries within the BATMAN Advanced (B.A.T.M.A.N.) mesh networking protocol. The phrase `batadv_tt_local_purge` immediately flags a function related to the removal of locally stored TT (Translation Table) entries. That’s the starting point. My next line of thinking focuses on how this function operates in the grand scheme of B.A.T.M.A.N. I need to quickly identify the points where this function, or specifically its related list counterparts, are invoked. Understanding the context of those calls is crucial. Are we talking about a timeout mechanism, a resource constraint issue, or something related to a specific event within the mesh? Is it related to the garbage collection of outdated entries? I need to drill down into the calling functions. The objective here is to have a robust understanding of the TT maintenance process: how long entries are valid and how the list of entries is handled. It could be that this is a critical spot for optimization, or potentially where subtle bugs regarding stale routing information could reside, or just a place to better understand the code and how it works. That's where I start. |
| 18/2 | 2026/10/01 09:33 | tool | grepper |
0mArgs:{
"Expression": "batadv_tt_local_purge",
"PathPrefix": "net/batman-adv"
}Results: {
"Output": "net/batman-adv/translation-table.c=1522=int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)\n--\nnet/batman-adv/translation-table.c-1579- * list_lock of @tt_local_entry. Otherwise\nnet/batman-adv/translation-table.c:1580: * batadv_tt_local_purge_pending_clients() could remove the entry before its\nnet/batman-adv/translation-table.c-1581- * change was queued.\n--\nnet/batman-adv/translation-table.c=1584=batadv_tt_local_set_pending(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/translation-table.c-1615- * (roamed) DEL change is queued. Both happen under the hash bucket list_lock\nnet/batman-adv/translation-table.c:1616: * of the entry to prevent concurrent batadv_tt_local_purge_pending_clients()\nnet/batman-adv/translation-table.c-1617- * from removing the entry and batadv_tt_local_transition_new() from clearing\n--\nnet/batman-adv/translation-table.c=1716=u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,\n--\nnet/batman-adv/translation-table.c-1739-/**\nnet/batman-adv/translation-table.c:1740: * batadv_tt_local_purge_list() - purge inactive tt local entries\nnet/batman-adv/translation-table.c-1741- * @bat_priv: the bat priv with all the mesh interface information\n--\nnet/batman-adv/translation-table.c-1745- */\nnet/batman-adv/translation-table.c:1746:static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,\nnet/batman-adv/translation-table.c-1747-\t\t\t\t struct hlist_head *head,\n--\nnet/batman-adv/translation-table.c-1778-/**\nnet/batman-adv/translation-table.c:1779: * batadv_tt_local_purge() - purge inactive tt local entries\nnet/batman-adv/translation-table.c-1780- * @bat_priv: the bat priv with all the mesh interface information\n--\nnet/batman-adv/translation-table.c-1783- */\nnet/batman-adv/translation-table.c:1784:static void batadv_tt_local_purge(struct batadv_priv *bat_priv,\nnet/batman-adv/translation-table.c-1785-\t\t\t\t int timeout)\n--\nnet/batman-adv/translation-table.c-1796-\t\tspin_lock_bh(list_lock);\nnet/batman-adv/translation-table.c:1797:\t\tbatadv_tt_local_purge_list(bat_priv, head, timeout);\nnet/batman-adv/translation-table.c-1798-\t\tspin_unlock_bh(list_lock);\n--\nnet/batman-adv/translation-table.c=4259=static void batadv_tt_purge(struct work_struct *work)\n--\nnet/batman-adv/translation-table.c-4268-\nnet/batman-adv/translation-table.c:4269:\tbatadv_tt_local_purge(bat_priv, BATADV_TT_LOCAL_TIMEOUT);\nnet/batman-adv/translation-table.c-4270-\tbatadv_tt_global_purge(bat_priv);\n--\nnet/batman-adv/translation-table.c=4305=static void batadv_tt_local_transition_new(struct batadv_priv *bat_priv)\n--\nnet/batman-adv/translation-table.c-4343-/**\nnet/batman-adv/translation-table.c:4344: * batadv_tt_local_purge_pending_clients() - finalise removal of pending local\nnet/batman-adv/translation-table.c-4345- * clients\n--\nnet/batman-adv/translation-table.c-4350- */\nnet/batman-adv/translation-table.c:4351:static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)\nnet/batman-adv/translation-table.c-4352-{\n--\nnet/batman-adv/translation-table.c=4413=static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv)\n--\nnet/batman-adv/translation-table.c-4424-\nnet/batman-adv/translation-table.c:4425:\tbatadv_tt_local_purge_pending_clients(bat_priv);\nnet/batman-adv/translation-table.c-4426-\tbatadv_tt_local_update_crc(bat_priv);\n--\nnet/batman-adv/translation-table.c=4673=void batadv_tt_local_resize_to_mtu(struct net_device *mesh_iface)\n--\nnet/batman-adv/translation-table.c-4689-\nnet/batman-adv/translation-table.c:4690:\t\tbatadv_tt_local_purge(bat_priv, timeout);\nnet/batman-adv/translation-table.c:4691:\t\tbatadv_tt_local_purge_pending_clients(bat_priv);\nnet/batman-adv/translation-table.c-4692-\n"
} |
| 19/2 | 2026/10/01 09:33 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23276 output: 36 thoughts: 31 Thoughts: Okay, let's dive into this code. **Investigating VLAN Creation and Destruction in batadv_meshif** Right, so I need to understand how VLANs are being handled within the `batadv_meshif` context. Specifically, I want to trace the life cycle of these VLANs – when they're created and when they're torn down. To do this properly, I need to know precisely *who* is calling the functions responsible for this process. Therefore, I will check the callers of `batadv_meshif_create_vlan` and `batadv_meshif_destroy_vlan` to get the necessary context. This will give me a clear picture of what's instantiating and cleaning up these virtual interfaces and the various states they go through. |
| 20/2 | 2026/10/01 09:33 | tool | grepper |
0mArgs:{
"Expression": "batadv_meshif_create_vlan",
"PathPrefix": "net/batman-adv"
}Results: {
"Output": "net/batman-adv/hard-interface.c=954=static int batadv_hard_if_event_meshif(unsigned long event,\n--\nnet/batman-adv/hard-interface.c-961-\t\tbat_priv = netdev_priv(net_dev);\nnet/batman-adv/hard-interface.c:962:\t\tbatadv_meshif_create_vlan(bat_priv, BATADV_NO_FLAGS);\nnet/batman-adv/hard-interface.c-963-\t\tbreak;\n--\nnet/batman-adv/mesh-interface.c=577=struct batadv_meshif_vlan *batadv_meshif_vlan_get(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/mesh-interface.c-599-/**\nnet/batman-adv/mesh-interface.c:600: * batadv_meshif_create_vlan() - allocate the needed resources for a new vlan\nnet/batman-adv/mesh-interface.c-601- * @bat_priv: the bat priv with all the mesh interface information\n--\nnet/batman-adv/mesh-interface.c-605- */\nnet/batman-adv/mesh-interface.c:606:int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)\nnet/batman-adv/mesh-interface.c-607-{\n--\nnet/batman-adv/mesh-interface.c=682=static int batadv_interface_add_vid(struct net_device *dev, __be16 proto,\n--\nnet/batman-adv/mesh-interface.c-711-\tif (!vlan)\nnet/batman-adv/mesh-interface.c:712:\t\treturn batadv_meshif_create_vlan(bat_priv, vid);\nnet/batman-adv/mesh-interface.c-713-\n--\nnet/batman-adv/mesh-interface.h=22=extern struct rtnl_link_ops batadv_link_ops;\nnet/batman-adv/mesh-interface.h:23:int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);\nnet/batman-adv/mesh-interface.h-24-void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/translation-table.c=353=static void batadv_tt_local_unreserve_entry(struct batadv_priv *bat_priv)\n--\nnet/batman-adv/translation-table.c-363- * Each VLAN adds a per VLAN header to the full table response and therefore\nnet/batman-adv/translation-table.c:364: * has to be reserved before batadv_meshif_create_vlan() allocates it.\nnet/batman-adv/translation-table.c-365- *\n"
} |
| 21/2 | 2026/10/01 09:33 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24260 output: 320 thoughts: 1380 Thoughts: **Analysis of Potential Fuzzing Targets in batman-adv** Okay, let's break this down. First, I need to pinpoint the relevant code modifications. I started by looking at `batadv_dat_tvlv_ogm_handler_v1` in `net/batman-adv/distributed-arp-table.c`. It looks like there's a change to how we handle the `DAT` feature flag within the OGM handler, specifically using `assign_bit`. Then, I checked `batadv_bla_init` in `net/batman-adv/bridge_loop_avoidance.c`, and I see that `bat_priv->bla.claim_hash` is now being cleared on the error path, which is important. Next, I need to figure out the required configurations. I know this is related to `BATMAN_ADV`, but I'll double check the dependencies. Looks like we need `BATMAN_ADV`, `BATMAN_ADV_BLA`, and `BATMAN_ADV_DAT`. I can see from the `Kconfig` that `BATMAN_ADV` selects `CRC32`, `BATMAN_ADV_BLA` depends on `BATMAN_ADV` and `INET` and selects `CRC16` and `NET_CRC32C`, and `BATMAN_ADV_DAT` depends on `BATMAN_ADV` and `INET`. `INET` is standard, so that's all good. Now, I'm thinking about which functions are most relevant for fuzzing. I need to select the right FocusSymbols. I need to include the non-ubiquitous, specific functions that are touched or directly execute the changed logic. Okay, I'll start with: `batadv_tt_local_reserve_vlan`, `batadv_tt_local_unreserve_vlan`, `batadv_meshif_create_vlan`, `batadv_meshif_vlan_release`, `batadv_tt_local_add`, `batadv_tt_local_remove`, `batadv_tt_local_purge`, `batadv_tt_local_entry_release`, `batadv_bla_init`, and `batadv_dat_tvlv_ogm_handler_v1`. Wait, but what about the functions that are called by the others? Are `batadv_tt_local_set_pending`, `batadv_tt_local_mark_removed`, `batadv_tt_local_reserve`, `batadv_tt_local_reserve_entry`, and `batadv_tt_local_create` static or can they be symbols? I should check this. Static functions can have symbols in kernel builds with `CONFIG_KALLSYMS` (and for fuzzing), unless they're inlined. I see that `batadv_tt_local_add` calls `batadv_tt_local_create` and `batadv_tt_local_remove` calls `batadv_tt_local_mark_removed`, etc. Including both static and the non-static callers (`batadv_tt_local_add`, `batadv_tt_local_remove`, `batadv_tt_local_purge`) makes sense here. Okay, so I should be sure to include these as FocusSymbols: `batadv_tt_local_reserve_vlan`, `batadv_tt_local_unreserve_vlan`, `batadv_tt_local_reserve_entry`, `batadv_tt_local_unreserve_entry`, `batadv_tt_local_reserve`, `batadv_tt_local_set_pending`, `batadv_tt_local_mark_removed`, `batadv_tt_local_create`, `batadv_tt_local_remove`, `batadv_tt_local_add`, `batadv_meshif_create_vlan`, `batadv_meshif_vlan_release`, and `batadv_bla_init`. Alright, time to evaluate if it's "WorthFuzzing". batman-adv is a virtual mesh network driver, accessible via netlink, sockets, and packets. This patch hits the criteria because it introduces things like new locking and concurrency synchronization with `bat_priv->tt.reserve_lock`, lockdep assertions like `lockdep_assert_held(&hash->list_locks[i])` and `lockdep_assert_held(&common->flags_lock)`, a table size reservation mechanism with `reserved_vlans` and `reserved_entries` to avoid translation table exceeding MTU/packet size, and restructured deletion/purge flows with functions like `batadv_tt_local_mark_removed` and `batadv_tt_local_set_ |
| 22/2 | 2026/10/01 09:33 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BATMAN_ADV",
"BATMAN_ADV_BLA",
"BATMAN_ADV_DAT"
],
"FocusSymbols": [
"batadv_tt_local_reserve_vlan",
"batadv_tt_local_unreserve_vlan",
"batadv_tt_local_reserve_entry",
"batadv_tt_local_set_pending",
"batadv_tt_local_mark_removed",
"batadv_tt_local_create",
"batadv_tt_local_add",
"batadv_tt_local_remove",
"batadv_meshif_create_vlan",
"batadv_meshif_vlan_release",
"batadv_bla_init"
],
"Reasoning": "The patch modifies B.A.T.M.A.N. Advanced (batman-adv), an in-kernel mesh routing protocol accessible via virtual netdevs, netlink, and packet interfaces. The changes add a table size reservation mechanism for local translation table (TT) entries and VLANs, refactor TT removal and purging logic, introduce a new spinlock (reserve_lock) with scoped guards, and add lockdep assertions in batadv_tt_local_set_pending(). These modifications introduce new concurrency constraints, state tracking, and assertions in reachable network protocol code, making it a prime candidate for fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BATMAN_ADV",
"BATMAN_ADV_BLA",
"BATMAN_ADV_DAT"
],
"FocusSymbols": [
"batadv_tt_local_reserve_vlan",
"batadv_tt_local_unreserve_vlan",
"batadv_tt_local_reserve_entry",
"batadv_tt_local_set_pending",
"batadv_tt_local_mark_removed",
"batadv_tt_local_create",
"batadv_tt_local_add",
"batadv_tt_local_remove",
"batadv_meshif_create_vlan",
"batadv_meshif_vlan_release",
"batadv_bla_init"
],
"Reasoning": "The patch modifies B.A.T.M.A.N. Advanced (batman-adv), an in-kernel mesh routing protocol accessible via virtual netdevs, netlink, and packet interfaces. The changes add a table size reservation mechanism for local translation table (TT) entries and VLANs, refactor TT removal and purging logic, introduce a new spinlock (reserve_lock) with scoped guards, and add lockdep assertions in batadv_tt_local_set_pending(). These modifications introduce new concurrency constraints, state tracking, and assertions in reachable network protocol code, making it a prime candidate for fuzzing.",
"WorthFuzzing": true
} |
| 23/1 | 2026/10/01 09:33 | action | If |
3mArgs:{
"WorthFuzzing": true
}Results: null |
| 24/2 | 2026/10/01 09:33 | agent | kmsan-evaluator |
3mModel:core Args: null Results: {
"KMSANReasoning": "The patch series addresses synchronization, state management, and table size reservations in batman-adv (including claim_hash cleanup in bridge loop avoidance, bit assignment in DAT, and translation table reservation tracking for VLANs and local TT entries). \n\nNone of the changes introduce uninitialized memory allocations, uninitialized variable reads, or copy operations of uninitialized kernel structures to userspace/network buffers. All new structure fields in struct batadv_priv_tt are zero-initialized as part of netdev allocation, stack variables (such as table_size) are explicitly written before use, and newly allocated structures (such as batadv_meshif_vlan) continue to be allocated via kzalloc_obj.\n\nAny potential bugs introduced by these modifications would be related to locking/concurrency (handled by LOCKDEP) or reference counting/memory cleanup (handled by KASAN). 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: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit bbc39027a78a5ab27162a649b95ca4ae19e604dd
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Oct 1 09:31:57 2026 +0000
syz-cluster: applied patch under review
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index ad6ab4a50658f..a96f269da8d37 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -1625,6 +1625,7 @@ int batadv_bla_init(struct batadv_priv *bat_priv)
bat_priv->bla.backbone_hash = batadv_hash_new(32);
if (!bat_priv->bla.backbone_hash) {
batadv_hash_destroy(bat_priv->bla.claim_hash);
+ bat_priv->bla.claim_hash = NULL;
return -ENOMEM;
}
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index a4be5b2a87d7a..74050bffedbce 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -863,10 +863,8 @@ static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
u8 flags,
void *tvlv_value, u16 tvlv_value_len)
{
- if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
- clear_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities);
- else
- set_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities);
+ assign_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities,
+ !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND));
}
/**
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index d89d44706269b..6c54a8d4be3ec 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -198,6 +198,7 @@ int batadv_mesh_init(struct net_device *mesh_iface)
spin_lock_init(&bat_priv->tt.roam_list_lock);
spin_lock_init(&bat_priv->tt.last_changeset_lock);
spin_lock_init(&bat_priv->tt.commit_lock);
+ spin_lock_init(&bat_priv->tt.reserve_lock);
spin_lock_init(&bat_priv->gw.list_lock);
#ifdef CONFIG_BATMAN_ADV_MCAST
spin_lock_init(&bat_priv->mcast.mla_lock);
diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
index 63af21954cf90..1ce707aaf48a0 100644
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -561,6 +561,8 @@ void batadv_meshif_vlan_release(struct kref *ref)
hlist_del_rcu(&vlan->list);
spin_unlock_bh(&vlan->bat_priv->meshif_vlan_list_lock);
+ batadv_tt_local_unreserve_vlan(vlan->bat_priv);
+
kfree_rcu(vlan, rcu);
}
@@ -614,9 +616,15 @@ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
return -EEXIST;
}
+ if (!batadv_tt_local_reserve_vlan(bat_priv, vid)) {
+ spin_unlock_bh(&bat_priv->meshif_vlan_list_lock);
+ return -EMSGSIZE;
+ }
+
vlan = kzalloc_obj(*vlan, GFP_ATOMIC);
if (!vlan) {
spin_unlock_bh(&bat_priv->meshif_vlan_list_lock);
+ batadv_tt_local_unreserve_vlan(bat_priv);
return -ENOMEM;
}
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 12ea557cb8033..3349376a9087d 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -234,6 +234,162 @@ batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
return tt_global_entry;
}
+/**
+ * batadv_tt_len() - compute length in bytes of given number of tt changes
+ * @changes_num: number of tt changes
+ *
+ * Return: computed length in bytes.
+ */
+static int batadv_tt_len(int changes_num)
+{
+ return changes_num * sizeof(struct batadv_tvlv_tt_change);
+}
+
+/**
+ * batadv_tt_local_transmit_size() - calculate the size of a full table response
+ * for a given number of VLANs and local TT entries
+ * @num_vlan: number of announced VLANs
+ * @num_entries: number of announced local TT entries
+ *
+ * Return: local translation table size in bytes.
+ */
+static int batadv_tt_local_transmit_size(u16 num_vlan, u16 num_entries)
+{
+ int hdr_size;
+
+ /* header size of tvlv encapsulated tt response payload */
+ hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
+ hdr_size += sizeof(struct batadv_tvlv_hdr);
+ hdr_size += sizeof(struct batadv_tvlv_tt_data);
+ hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
+
+ return hdr_size + batadv_tt_len(num_entries);
+}
+
+/**
+ * batadv_tt_local_reserve() - reserve room in the transmittable local table
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @num_vlan: number of VLANs to reserve
+ * @num_entries: number of local TT entries to reserve
+ * @table_size: stores the resulting worst case table size in bytes
+ *
+ * Add the requested number of VLANs and local TT entries to the reservation
+ * counters and check whether the local translation table would then still fit
+ * in a single full table response. The reservation is dropped again when it
+ * would not.
+ *
+ * The reservation has to happen before the related object is allocated. This
+ * way two parallel allocations cannot both observe enough room for themselves
+ * and end up with a local table which can no longer be transmitted.
+ *
+ * A granted reservation must be returned via batadv_tt_local_unreserve() when
+ * the related object is released or was never created.
+ *
+ * Return: true when the reservation was granted, false otherwise.
+ */
+static bool batadv_tt_local_reserve(struct batadv_priv *bat_priv, u16 num_vlan,
+ u16 num_entries, int *table_size)
+{
+ int packet_size_max = READ_ONCE(bat_priv->packet_size_max);
+
+ scoped_guard(spinlock_bh, &bat_priv->tt.reserve_lock) {
+ bat_priv->tt.reserved_vlans += num_vlan;
+ bat_priv->tt.reserved_entries += num_entries;
+
+ *table_size = batadv_tt_local_transmit_size(bat_priv->tt.reserved_vlans,
+ bat_priv->tt.reserved_entries);
+ if (*table_size <= packet_size_max)
+ return true;
+
+ bat_priv->tt.reserved_vlans -= num_vlan;
+ bat_priv->tt.reserved_entries -= num_entries;
+ }
+
+ return false;
+}
+
+/**
+ * batadv_tt_local_unreserve() - return room in the transmittable local table
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @num_vlan: number of VLANs to return
+ * @num_entries: number of local TT entries to return
+ */
+static void batadv_tt_local_unreserve(struct batadv_priv *bat_priv,
+ u16 num_vlan, u16 num_entries)
+{
+ scoped_guard(spinlock_bh, &bat_priv->tt.reserve_lock) {
+ bat_priv->tt.reserved_vlans -= num_vlan;
+ bat_priv->tt.reserved_entries -= num_entries;
+ }
+}
+
+/**
+ * batadv_tt_local_reserve_entry() - reserve room for a new local TT entry
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @addr: the mac address of the client to add
+ *
+ * Return: true when the reservation was granted, false otherwise.
+ */
+static bool batadv_tt_local_reserve_entry(struct batadv_priv *bat_priv,
+ const u8 *addr)
+{
+ int table_size;
+
+ if (batadv_tt_local_reserve(bat_priv, 0, 1, &table_size))
+ return true;
+
+ net_ratelimited_function(batadv_info, bat_priv->mesh_iface,
+ "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
+ table_size,
+ READ_ONCE(bat_priv->packet_size_max), addr);
+
+ return false;
+}
+
+/**
+ * batadv_tt_local_unreserve_entry() - return the room of a local TT entry
+ * @bat_priv: the bat priv with all the mesh interface information
+ */
+static void batadv_tt_local_unreserve_entry(struct batadv_priv *bat_priv)
+{
+ batadv_tt_local_unreserve(bat_priv, 0, 1);
+}
+
+/**
+ * batadv_tt_local_reserve_vlan() - reserve room for a new VLAN
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @vid: the VLAN identifier
+ *
+ * Each VLAN adds a per VLAN header to the full table response and therefore
+ * has to be reserved before batadv_meshif_create_vlan() allocates it.
+ *
+ * Return: true when the reservation was granted, false otherwise.
+ */
+bool batadv_tt_local_reserve_vlan(struct batadv_priv *bat_priv,
+ unsigned short vid)
+{
+ int table_size;
+
+ if (batadv_tt_local_reserve(bat_priv, 1, 0, &table_size))
+ return true;
+
+ net_ratelimited_function(batadv_info, bat_priv->mesh_iface,
+ "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new VLAN: %d\n",
+ table_size, READ_ONCE(bat_priv->packet_size_max),
+ batadv_print_vid(vid));
+
+ return false;
+}
+
+/**
+ * batadv_tt_local_unreserve_vlan() - return the room of a VLAN
+ * @bat_priv: the bat priv with all the mesh interface information
+ */
+void batadv_tt_local_unreserve_vlan(struct batadv_priv *bat_priv)
+{
+ batadv_tt_local_unreserve(bat_priv, 1, 0);
+}
+
/**
* batadv_tt_local_entry_release() - release tt_local_entry from lists and queue
* for free after rcu grace period
@@ -246,6 +402,7 @@ static void batadv_tt_local_entry_release(struct kref *ref)
tt_local_entry = container_of(ref, struct batadv_tt_local_entry,
common.refcount);
+ batadv_tt_local_unreserve_entry(tt_local_entry->vlan->bat_priv);
batadv_meshif_vlan_put(tt_local_entry->vlan);
kfree_rcu(tt_local_entry, common.rcu);
@@ -461,23 +618,21 @@ static u16 batadv_tt_flags_get(struct batadv_tt_common_entry *common)
}
/**
- * batadv_tt_local_event() - store a local TT event (ADD/DEL)
+ * __batadv_tt_local_event() - store a local TT event (ADD/DEL) with given flags
* @bat_priv: the bat priv with all the mesh interface information
- * @tt_local_entry: the TT entry involved in the event
- * @event_flags: flags to store in the event structure
+ * @common: the TT entry involved in the event
+ * @flags: flags of the TT entry combined with the event flags
*/
-static void batadv_tt_local_event(struct batadv_priv *bat_priv,
- struct batadv_tt_local_entry *tt_local_entry,
- u8 event_flags)
+static void __batadv_tt_local_event(struct batadv_priv *bat_priv,
+ const struct batadv_tt_common_entry *common,
+ u8 flags)
{
- struct batadv_tt_common_entry *common = &tt_local_entry->common;
struct batadv_tt_change_node *tt_change_node;
struct batadv_tt_change_node *entry;
struct batadv_tt_change_node *safe;
bool del_op_requested;
bool del_op_entry;
size_t changes;
- u8 flags;
tt_change_node = kmem_cache_alloc(batadv_tt_change_cache, GFP_ATOMIC);
if (!tt_change_node)
@@ -488,8 +643,6 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
ether_addr_copy(tt_change_node->change.addr, common->addr);
tt_change_node->change.vid = htons(common->vid);
- flags = batadv_tt_flags_get(common) | event_flags;
-
tt_change_node->change.flags = flags;
del_op_requested = flags & BATADV_TT_CLIENT_DEL;
@@ -538,14 +691,20 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
}
/**
- * batadv_tt_len() - compute length in bytes of given number of tt changes
- * @changes_num: number of tt changes
- *
- * Return: computed length in bytes.
+ * batadv_tt_local_event() - store a local TT event (ADD/DEL)
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @tt_local_entry: the TT entry involved in the event
+ * @event_flags: flags to store in the event structure
*/
-static int batadv_tt_len(int changes_num)
+static void batadv_tt_local_event(struct batadv_priv *bat_priv,
+ struct batadv_tt_local_entry *tt_local_entry,
+ u8 event_flags)
{
- return changes_num * sizeof(struct batadv_tvlv_tt_change);
+ struct batadv_tt_common_entry *common = &tt_local_entry->common;
+ u8 flags;
+
+ flags = batadv_tt_flags_get(common) | event_flags;
+ __batadv_tt_local_event(bat_priv, common, flags);
}
/**
@@ -571,7 +730,6 @@ static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
struct batadv_meshif_vlan *vlan;
u16 tt_local_entries = 0;
u16 num_vlan = 0;
- int hdr_size;
rcu_read_lock();
hlist_for_each_entry_rcu(vlan, &bat_priv->meshif_vlan_list, list) {
@@ -580,13 +738,7 @@ static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
}
rcu_read_unlock();
- /* header size of tvlv encapsulated tt response payload */
- hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
- hdr_size += sizeof(struct batadv_tvlv_hdr);
- hdr_size += sizeof(struct batadv_tvlv_tt_data);
- hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
-
- return hdr_size + batadv_tt_len(tt_local_entries);
+ return batadv_tt_local_transmit_size(num_vlan, tt_local_entries);
}
/**
@@ -790,23 +942,15 @@ batadv_tt_local_create(struct net_device *mesh_iface, const u8 *addr,
struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
struct batadv_tt_local_entry *tt_local;
struct batadv_meshif_vlan *vlan;
- int packet_size_max;
- int table_size;
- /* Ignore the client if we cannot send it in a full table response. */
- table_size = batadv_tt_local_table_transmit_size(bat_priv);
- table_size += batadv_tt_len(1);
- packet_size_max = READ_ONCE(bat_priv->packet_size_max);
- if (table_size > packet_size_max) {
- net_ratelimited_function(batadv_info, mesh_iface,
- "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
- table_size, packet_size_max, addr);
+ if (!batadv_tt_local_reserve_entry(bat_priv, addr))
return NULL;
- }
tt_local = kmem_cache_alloc(batadv_tl_cache, GFP_ATOMIC);
- if (!tt_local)
+ if (!tt_local) {
+ batadv_tt_local_unreserve_entry(bat_priv);
return NULL;
+ }
/* increase the refcounter of the related vlan */
vlan = batadv_meshif_vlan_get(bat_priv, vid);
@@ -815,6 +959,7 @@ batadv_tt_local_create(struct net_device *mesh_iface, const u8 *addr,
"adding TT local entry %pM to non-existent VLAN %d\n",
addr, batadv_print_vid(vid));
kmem_cache_free(batadv_tl_cache, tt_local);
+ batadv_tt_local_unreserve_entry(bat_priv);
return NULL;
}
@@ -1420,21 +1565,37 @@ int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
}
/**
- * batadv_tt_local_set_pending_event() - trigger events for TT pending removal
+ * batadv_tt_local_set_pending() - mark local TT entry as pending removal
* @bat_priv: the bat priv with all the mesh interface information
- * @tt_local_entry: local TT entry which was marked as BATADV_TT_CLIENT_PENDING
+ * @tt_local_entry: local TT entry to mark as BATADV_TT_CLIENT_PENDING
* @flags: TT change flags to announce together with the pending removal
* @message: debug message describing the reason for the change
*
- * Schedule the TT change announcement for the entry. The caller must already
- * have added BATADV_TT_CLIENT_PENDING to the @tt_local_entry
+ * Schedule the TT change announcement and set BATADV_TT_CLIENT_PENDING on the
+ * entry. The entry is kept in the local table until the next TTVN increment
+ * so that a consistency-check response can still be answered.
+ *
+ * Next to the flags_lock of the entry, the caller must hold the hash bucket
+ * list_lock of @tt_local_entry. Otherwise
+ * batadv_tt_local_purge_pending_clients() could remove the entry before its
+ * change was queued.
*/
static void
-batadv_tt_local_set_pending_event(struct batadv_priv *bat_priv,
- struct batadv_tt_local_entry *tt_local_entry,
- u16 flags, const char *message)
+batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
+ struct batadv_tt_local_entry *tt_local_entry,
+ u16 flags, const char *message)
+ __must_hold(&tt_local_entry->common.flags_lock)
{
- batadv_tt_local_event(bat_priv, tt_local_entry, flags);
+ struct batadv_tt_common_entry *common = &tt_local_entry->common;
+ struct batadv_hashtable *hash = bat_priv->tt.local_hash;
+ u32 i;
+
+ i = batadv_choose_tt(common, hash->size);
+ lockdep_assert_held(&hash->list_locks[i]);
+ lockdep_assert_held(&common->flags_lock);
+
+ __batadv_tt_local_event(bat_priv, common, common->flags | flags);
+ common->flags |= BATADV_TT_CLIENT_PENDING;
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Local tt entry (%pM, vid: %d) pending to be removed: %s\n",
@@ -1443,45 +1604,77 @@ batadv_tt_local_set_pending_event(struct batadv_priv *bat_priv,
}
/**
- * batadv_tt_local_mark_removed() - mark a local entry as removed
+ * batadv_tt_local_mark_removed() - mark a local entry as removed and queue DEL
+ * @bat_priv: the bat priv with all the mesh interface information
* @tt_local_entry: local TT entry to mark
+ * @message: message to append to the log on deletion
* @roaming: true if the deletion is due to a roaming event
* @curr_flags: pointer to store the flags of the entry before it was marked
*
+ * An already announced entry is marked as BATADV_TT_CLIENT_PENDING and the
+ * (roamed) DEL change is queued. Both happen under the hash bucket list_lock
+ * of the entry to prevent concurrent batadv_tt_local_purge_pending_clients()
+ * from removing the entry and batadv_tt_local_transition_new() from clearing
+ * BATADV_TT_CLIENT_NEW after it was checked.
+ *
* Return: true if the entry has to be kept in the local table until the next
* ttvn increment, false if it can be purged immediately.
*/
static bool
-batadv_tt_local_mark_removed(struct batadv_tt_local_entry *tt_local_entry,
- bool roaming, u16 *curr_flags)
+batadv_tt_local_mark_removed(struct batadv_priv *bat_priv,
+ struct batadv_tt_local_entry *tt_local_entry,
+ const char *message, bool roaming, u16 *curr_flags)
{
+ spinlock_t *list_lock; /* protects write access to the hash lists */
struct batadv_tt_common_entry *common = &tt_local_entry->common;
+ struct batadv_hashtable *hash = bat_priv->tt.local_hash;
bool pending = false;
+ u16 flags;
+ u32 i;
+
+ i = batadv_choose_tt(common, hash->size);
+ list_lock = &hash->list_locks[i];
+
+ spin_lock_bh(list_lock);
scoped_guard(spinlock_bh, &common->flags_lock) {
*curr_flags = common->flags;
- /* mark the local client as ROAMed */
+ /* if this global entry addition is due to a roaming, the node
+ * has to mark the local entry as "roamed" in order to
+ * correctly reroute packets later
+ */
if (roaming)
common->flags |= BATADV_TT_CLIENT_ROAM;
- if (!(common->flags & BATADV_TT_CLIENT_NEW)) {
- common->flags |= BATADV_TT_CLIENT_PENDING;
- pending = true;
- }
+ if (common->flags & BATADV_TT_CLIENT_NEW)
+ break;
+
+ flags = BATADV_TT_CLIENT_DEL;
+ if (roaming)
+ flags |= BATADV_TT_CLIENT_ROAM;
+
+ batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags,
+ message);
+ pending = true;
}
+ spin_unlock_bh(list_lock);
+
return pending;
}
/**
- * batadv_tt_local_remove_now() - purge a local entry which was never announced
+ * batadv_tt_local_remove_now() - purge a local entry which was not (yet) announced
* @bat_priv: the bat priv with all the mesh interface information
* @tt_local_entry: local TT entry to purge
*
- * A client which was added right after the last ttvn increment was never sent
- * to the other nodes. It can therefore be dropped from the local table without
- * waiting for the next ttvn increment.
+ * A client which was added right after the last ttvn increment was not (yet)
+ * sent to the other nodes. It can therefore be dropped from the local table
+ * without waiting for the next ttvn increment.
+ *
+ * If it was still announced by a parallel context before it was removed from
+ * the hash, then the local TT size adjustment will be handled automatically.
*/
static void
batadv_tt_local_remove_now(struct batadv_priv *bat_priv,
@@ -1490,8 +1683,6 @@ batadv_tt_local_remove_now(struct batadv_priv *bat_priv,
struct batadv_tt_common_entry *common = &tt_local_entry->common;
struct hlist_node *tt_removed_node;
- batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);
-
/* remove exactly this object when still present in hash */
tt_removed_node = batadv_hash_remove(bat_priv->tt.local_hash,
batadv_compare_tt_entry,
@@ -1499,6 +1690,8 @@ batadv_tt_local_remove_now(struct batadv_priv *bat_priv,
if (!tt_removed_node)
return;
+ batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);
+
/* batadv_tt_local_transition_new() may have committed the entry and
* thus counted it in the local table size since the
* BATADV_TT_CLIENT_NEW check in batadv_tt_local_mark_removed().
@@ -1526,27 +1719,16 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
{
struct batadv_tt_local_entry *tt_local_entry;
u16 curr_flags;
- u16 flags;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
if (!tt_local_entry)
return BATADV_NO_FLAGS;
- flags = BATADV_TT_CLIENT_DEL;
- /* if this global entry addition is due to a roaming, the node has to
- * mark the local entry as "roamed" in order to correctly reroute
- * packets later
+ /* if this client has been added right now, it is possible to
+ * immediately purge it
*/
- if (roaming)
- flags |= BATADV_TT_CLIENT_ROAM;
-
- if (batadv_tt_local_mark_removed(tt_local_entry, roaming, &curr_flags))
- batadv_tt_local_set_pending_event(bat_priv, tt_local_entry,
- flags, message);
- else
- /* if this client has been added right now, it is possible to
- * immediately purge it
- */
+ if (!batadv_tt_local_mark_removed(bat_priv, tt_local_entry, message,
+ roaming, &curr_flags))
batadv_tt_local_remove_now(bat_priv, tt_local_entry);
batadv_tt_local_entry_put(tt_local_entry);
@@ -1571,37 +1753,25 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
hlist_for_each_entry_safe(tt_common_entry, node_tmp, head,
hash_entry) {
- bool cont = false;
-
tt_local_entry = container_of(tt_common_entry,
struct batadv_tt_local_entry,
common);
scoped_guard(spinlock_bh, &tt_local_entry->common.flags_lock) {
- if (tt_local_entry->common.flags & BATADV_TT_CLIENT_NOPURGE) {
- cont = true;
+ if (tt_local_entry->common.flags & BATADV_TT_CLIENT_NOPURGE)
break;
- }
/* entry already marked for deletion */
- if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) {
- cont = true;
+ if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)
break;
- }
- if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout)) {
- cont = true;
+ if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout))
break;
- }
- tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING;
+ batadv_tt_local_set_pending(bat_priv, tt_local_entry,
+ BATADV_TT_CLIENT_DEL,
+ "timed out");
}
-
- if (cont)
- continue;
-
- batadv_tt_local_set_pending_event(bat_priv, tt_local_entry,
- BATADV_TT_CLIENT_DEL, "timed out");
}
}
@@ -1870,9 +2040,10 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
* @tt_global_entry: the global TT entry of the announced client
* @flags: TT flags announced for this non-mesh client
*
- * A client which is announced by another originator is no longer a local
- * client. Remove it from the local table and take over the WIFI flag it was
- * tracked with.
+ * A non-multicast client which is announced by another originator is no longer
+ * a local client. Remove it from the local table and (for the global entry)
+ * take over the WIFI flag it was tracked with. Reset the roaming flag in case
+ * the announcement didn't contain the roaming flag.
*/
static void
batadv_tt_global_purge_local(struct batadv_priv *bat_priv,
diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h
index 618d9dbca5eac..ca01d20ee2d7f 100644
--- a/net/batman-adv/translation-table.h
+++ b/net/batman-adv/translation-table.h
@@ -16,6 +16,9 @@
#include <linux/types.h>
int batadv_tt_init(struct batadv_priv *bat_priv);
+bool batadv_tt_local_reserve_vlan(struct batadv_priv *bat_priv,
+ unsigned short vid);
+void batadv_tt_local_unreserve_vlan(struct batadv_priv *bat_priv);
bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
unsigned short vid, int ifindex, u32 mark);
u16 batadv_tt_local_remove(struct batadv_priv *bat_priv,
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 67872927cfb50..c19caa84e6920 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1055,6 +1055,25 @@ struct batadv_priv_tt {
*/
spinlock_t commit_lock;
+ /**
+ * @reserved_entries: number of local TT entries which are currently
+ * allocated or about to be allocated. Together with @reserved_vlans it
+ * describes the worst case size of a full table response.
+ */
+ u16 reserved_entries;
+
+ /**
+ * @reserved_vlans: number of mesh interface VLANs which are currently
+ * allocated or about to be allocated. Together with @reserved_entries
+ * it describes the worst case size of a full table response.
+ */
+ u16 reserved_vlans;
+
+ /**
+ * @reserve_lock: lock protecting @reserved_entries & @reserved_vlans
+ */
+ spinlock_t reserve_lock;
+
/** @work: work queue callback item for translation table purging */
struct delayed_work work;
};
|
| 25/3 | 2026/10/01 09:33 | llm | kmsan-evaluator |
2mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 11002 output: 35 thoughts: 1610 Thoughts: **KMSAN Evaluation for batman-adv Patch Series** Okay, so I need to decide if this patch series in `net/batman-adv/` warrants firing up a dedicated KMSAN fuzzing session, beyond the standard KASAN checks. No real safety concerns here, just standard code analysis and QA. Let's dig in. First, I'm going to look at each patch in detail. 1. `net/batman-adv/bridge_loop_avoidance.c`: This one's straightforward; fixing a potential UAF/dangling pointer issue in the error path of `batadv_bla_init`. Good catch, but unlikely to trigger KMSAN specifically. It just improves error handling. 2. `net/batman-adv/distributed-arp-table.c`: Here, they’re simplifying some bit manipulation with the `assign_bit` helper. The change should be functionally equivalent, though a good clean up. No direct red flags for KMSAN. 3. `net/batman-adv/main.c`: They're initializing a new spinlock, `reserve_lock`. This is a new synchronization primitive. I need to make sure this is being used correctly, i.e., I need to find all uses of this lock. Initialization is fine, but concurrent access is what KMSAN can sniff out. 4. `net/batman-adv/mesh-interface.c`: This introduces VLAN reservation accounting using the local TT table, with `batadv_tt_local_reserve_vlan` and `batadv_tt_local_unreserve_vlan`. This looks like some sort of contention around resource management for VLANs and could result in concurrency issues. This is where things get interesting. I'm going to need to track the locking discipline around `bat_priv->meshif_vlan_list_lock` and the new `reserve_lock`. The failure handling with `-EMSGSIZE` is good. This might be a KMSAN target. 5. `net/batman-adv/translation-table.c`: A slew of new functions here related to the translation table (TT): calculating sizes, reserving/unreserving, and new events. The focus is on local resource accounting and synchronization using the `reserve_lock` and several helper methods. Looks like a refactor of concurrency control and table size calculations. I need to carefully look at `batadv_tt_local_reserve`, `batadv_tt_local_unreserve`, the new locking primitives (`reserve_lock`, `list_lock`, `flags_lock`), and how they interact. The changes in `batadv_tt_local_create` and other functions are especially crucial. They are allocating and freeing resources, and are now under new locking disciplines. This is a prime candidate for KMSAN analysis, especially since a lot of refactoring and synchronization is happening. I need to review this code for potential uninitialized memory reads, info leaks, or data races related to the TT. To be thorough, I'm going to check how `bat_priv` is initialized and used. I'll search for `batadv_mesh_init` (or wherever `bat_priv` comes from) to understand the broader context. This will help confirm that there isn't anything obviously missed (like uninitialized fields) in the new code. The new locks may be used on a `bat_priv` struct that may not be fully initialized. Now I need to use `grepper` or `read-file` and hunt down any instance of `bat_priv`, how it's allocated, initialized, and how it is used with the new locking in particular. This patch series looks like a strong candidate for a focused KMSAN fuzzing run to check the new locking and resource management, especially around the TT and VLANs. |
| 26/3 | 2026/10/01 09:36 | tool | grepper |
0mArgs:{
"Expression": "bat_priv-\u003ett\\.reserved_",
"PathPrefix": "net/batman-adv/"
}Results: {
"Output": "net/batman-adv/translation-table.c=290=static bool batadv_tt_local_reserve(struct batadv_priv *bat_priv, u16 num_vlan,\n--\nnet/batman-adv/translation-table.c-295-\tscoped_guard(spinlock_bh, \u0026bat_priv-\u003ett.reserve_lock) {\nnet/batman-adv/translation-table.c:296:\t\tbat_priv-\u003ett.reserved_vlans += num_vlan;\nnet/batman-adv/translation-table.c:297:\t\tbat_priv-\u003ett.reserved_entries += num_entries;\nnet/batman-adv/translation-table.c-298-\nnet/batman-adv/translation-table.c:299:\t\t*table_size = batadv_tt_local_transmit_size(bat_priv-\u003ett.reserved_vlans,\nnet/batman-adv/translation-table.c:300:\t\t\t\t\t\t\t bat_priv-\u003ett.reserved_entries);\nnet/batman-adv/translation-table.c-301-\t\tif (*table_size \u003c= packet_size_max)\n--\nnet/batman-adv/translation-table.c-303-\nnet/batman-adv/translation-table.c:304:\t\tbat_priv-\u003ett.reserved_vlans -= num_vlan;\nnet/batman-adv/translation-table.c:305:\t\tbat_priv-\u003ett.reserved_entries -= num_entries;\nnet/batman-adv/translation-table.c-306-\t}\n--\nnet/batman-adv/translation-table.c=317=static void batadv_tt_local_unreserve(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/translation-table.c-320-\tscoped_guard(spinlock_bh, \u0026bat_priv-\u003ett.reserve_lock) {\nnet/batman-adv/translation-table.c:321:\t\tbat_priv-\u003ett.reserved_vlans -= num_vlan;\nnet/batman-adv/translation-table.c:322:\t\tbat_priv-\u003ett.reserved_entries -= num_entries;\nnet/batman-adv/translation-table.c-323-\t}\n"
} |
| 27/3 | 2026/10/01 09:36 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 14045 output: 36 thoughts: 22 Thoughts: Here's a breakdown of my thought process: **Diving into batman-adv's `bat_priv` Allocation** Okay, so I need to understand how the `bat_priv` structure is being handled within the `net/batman-adv/` directory. This means I'm looking at memory allocation. My immediate focus is on *where* in the code, and under *what* circumstances, this private structure is being created. I need to trace the initialization, likely looking for calls to `kmalloc`, `kzalloc`, or some custom batman-adv memory management function if it exists. I’ll be paying close attention to any associated lifecycle - are there corresponding deallocation routines? Are there any subtle issues with how the memory is handled during interface setup, teardown, or any specific operational scenarios of the batman-adv mesh? I'll also be mindful of potential race conditions and memory leaks. The goal is a clear understanding of the allocation/deallocation patterns. |
| 28/3 | 2026/10/01 09:36 | tool | grepper |
0mError:no matches Args: {
"Expression": "alloc_netdev.*batadv_priv",
"PathPrefix": "net/batman-adv/"
}Results: {
"error": "no matches"
} |
| 29/3 | 2026/10/01 09:36 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 14323 output: 31 thoughts: 27 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person: **Batting a Few Ideas Around: Locating Key Batman-adv Structures** Right, so I'm diving into the `net/batman-adv/` directory. My immediate goal is to understand how Batman-adv's network device interaction is structured. I need to find the core data structures that represent a Batman-adv network device and how they relate to the generic networking stack. Specifically, I'm thinking about either `alloc_netdev` – that's often a good starting point for understanding how a driver creates and registers its network devices – or `netdev_priv`. The `netdev_priv` pointer, if present, is critical, because it's where the driver usually stores its private, Batman-adv-specific data associated with each network device. That's where I expect to find the bulk of Batman-adv's internal state. Let's see if I can find either of those and then take a look around! |
| 30/3 | 2026/10/01 09:36 | tool | grepper |
0mError:no matches Args: {
"Expression": "alloc_netdev",
"PathPrefix": "net/batman-adv/"
}Results: {
"error": "no matches"
} |
| 31/3 | 2026/10/01 09:36 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 14622 output: 36 thoughts: 26 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Diving into `batman-adv` Soft Interface Code** Alright, so I need to find something in the `batman-adv` code, and I'm thinking the best place to start is likely within the soft interface module, `net/batman-adv/soft-interface.c`. Specifically, I'm going to search for instances of `netdev_priv`. My intuition is that this macro or structure is a good starting point to locate relevant structures or state that's specific to each soft interface instance. It's often the key to accessing private data associated with a network device. From there, I expect to quickly uncover how `batman-adv` manages its soft interfaces internally, likely identifying associated data structures and functions that I'm interested in. The plan is to pinpoint the device-specific context. I'm hoping `netdev_priv` will lead me right to it. |
| 32/3 | 2026/10/01 09:36 | tool | grepper |
0mError:no matches Args: {
"Expression": "netdev_priv",
"PathPrefix": "net/batman-adv/soft-interface.c"
}Results: {
"error": "no matches"
} |
| 33/3 | 2026/10/01 09:36 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 14907 output: 24 thoughts: 17 Thoughts: Okay, let's see. My mind is jumping towards specific details here. I need to get a better overview. It seems like the next step is to examine the contents of a particular directory within the kernel source tree. Let's see...I need to list the files within `net/batman-adv/`. That's where I need to start. From there, I can begin to grasp what's there and begin to understand what's relevant. |
| 34/3 | 2026/10/01 09:36 | tool | codesearch-dir-index |
0mArgs:{
"Dir": "net/batman-adv"
}Results: {
"Files": [
"bat_algo.c",
"bat_algo.h",
"bat_iv_ogm.c",
"bat_iv_ogm.h",
"bat_v.c",
"bat_v.h",
"bat_v_elp.c",
"bat_v_elp.h",
"bat_v_ogm.c",
"bat_v_ogm.h",
"bitarray.c",
"bitarray.h",
"bridge_loop_avoidance.c",
"bridge_loop_avoidance.h",
"distributed-arp-table.c",
"distributed-arp-table.h",
"fragmentation.c",
"fragmentation.h",
"gateway_client.c",
"gateway_client.h",
"gateway_common.c",
"gateway_common.h",
"hard-interface.c",
"hard-interface.h",
"hash.c",
"hash.h",
"log.c",
"log.h",
"main.c",
"main.h",
"mesh-interface.c",
"mesh-interface.h",
"multicast.c",
"multicast.h",
"multicast_forw.c",
"netlink.c",
"netlink.h",
"originator.c",
"originator.h",
"routing.c",
"routing.h",
"send.c",
"send.h",
"tp_meter.c",
"tp_meter.h",
"trace.c",
"trace.h",
"translation-table.c",
"translation-table.h",
"tvlv.c",
"tvlv.h",
"types.h",
"version.c"
],
"Subdirs": null
} |
| 35/3 | 2026/10/01 09:36 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 15470 output: 38 thoughts: 25 Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own: **Analyzing `batadv_mesh_init`** Alright, time to get my hands dirty. My focus is immediately on `batadv_mesh_init`, and I'm heading straight to `net/batman-adv/main.c`. This is where the core initialization happens, so it's the logical place to start understanding the Batman-adv module's setup. I want to see how the mesh network is brought to life, the initial data structures, the crucial functions that are called, and the overall order of operations. What's the fundamental sequence that brings the mesh interface up and running? This is the starting point for really digging into its architecture and behavior. I'm looking for the foundational elements. |
| 36/3 | 2026/10/01 09:36 | tool | read-file |
0mArgs:{
"File": "net/batman-adv/main.c",
"FirstLine": 180,
"LineCount": 40
}Results: {
"Contents": " 180:\t\tbatadv_tt_cache_destroy();\n 181:\t}\n 182:\t\n 183:\t/**\n 184:\t * batadv_mesh_init() - Initialize mesh interface\n 185:\t * @mesh_iface: netdev struct of the mesh interface\n 186:\t *\n 187:\t * Return: 0 on success or negative error number in case of failure\n 188:\t */\n 189:\tint batadv_mesh_init(struct net_device *mesh_iface)\n 190:\t{\n 191:\t\tstruct batadv_priv *bat_priv = netdev_priv(mesh_iface);\n 192:\t\tint ret;\n 193:\t\n 194:\t\tspin_lock_init(\u0026bat_priv-\u003eforw_bat_list_lock);\n 195:\t\tspin_lock_init(\u0026bat_priv-\u003eforw_bcast_list_lock);\n 196:\t\tspin_lock_init(\u0026bat_priv-\u003ett.changes_list_lock);\n 197:\t\tspin_lock_init(\u0026bat_priv-\u003ett.req_list_lock);\n 198:\t\tspin_lock_init(\u0026bat_priv-\u003ett.roam_list_lock);\n 199:\t\tspin_lock_init(\u0026bat_priv-\u003ett.last_changeset_lock);\n 200:\t\tspin_lock_init(\u0026bat_priv-\u003ett.commit_lock);\n 201:\t\tspin_lock_init(\u0026bat_priv-\u003ett.reserve_lock);\n 202:\t\tspin_lock_init(\u0026bat_priv-\u003egw.list_lock);\n 203:\t#ifdef CONFIG_BATMAN_ADV_MCAST\n 204:\t\tspin_lock_init(\u0026bat_priv-\u003emcast.mla_lock);\n 205:\t\tspin_lock_init(\u0026bat_priv-\u003emcast.want_lists_lock);\n 206:\t#endif\n 207:\t\tspin_lock_init(\u0026bat_priv-\u003etvlv.container_list_lock);\n 208:\t\tspin_lock_init(\u0026bat_priv-\u003etvlv.handler_list_lock);\n 209:\t\tspin_lock_init(\u0026bat_priv-\u003emeshif_vlan_list_lock);\n 210:\t\tspin_lock_init(\u0026bat_priv-\u003etp_list_lock);\n 211:\t\n 212:\t\tINIT_HLIST_HEAD(\u0026bat_priv-\u003eforw_bat_list);\n 213:\t\tINIT_HLIST_HEAD(\u0026bat_priv-\u003eforw_bcast_list);\n 214:\t\tINIT_HLIST_HEAD(\u0026bat_priv-\u003egw.gateway_list);\n 215:\t#ifdef CONFIG_BATMAN_ADV_MCAST\n 216:\t\tINIT_HLIST_HEAD(\u0026bat_priv-\u003emcast.want_all_unsnoopables_list);\n 217:\t\tINIT_HLIST_HEAD(\u0026bat_priv-\u003emcast.want_all_ipv4_list);\n 218:\t\tINIT_HLIST_HEAD(\u0026bat_priv-\u003emcast.want_all_ipv6_list);\n 219:\t#endif\n"
} |
| 37/3 | 2026/10/01 09:36 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 16521 output: 40 thoughts: 90 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating Netdev Initialization and the BATMAN-Adv Translation Table** Alright, so the netdev, as I'm thinking about it, is getting allocated and it looks like `netdev_priv` is coming along for the ride. And based on what I recall, `alloc_netdev_mqs` is taking care of zeroing it out during the allocation (via `kzalloc`, naturally). That's standard practice, a good starting point to prevent any unexpected junk data from causing issues down the line. Now, I need to understand what's happening with BATMAN-Adv and the translation table, I'll go have a look at `__batadv_tt_local_event`. I'll probably be able to read up on this, it should be in `net/batman-adv/translation-table.c`. Let me just jump over to that file, and I'll focus on lines somewhere between 618 and 690 to get a handle on the key logic being executed here. I'm especially interested in how it interacts with the zeroed `netdev_priv` at that point. |
| 38/3 | 2026/10/01 09:37 | tool | read-file |
0mArgs:{
"File": "net/batman-adv/translation-table.c",
"FirstLine": 615,
"LineCount": 80
}Results: {
"Contents": " 615:\t\t\tflags = common-\u003eflags;\n 616:\t\n 617:\t\treturn flags;\n 618:\t}\n 619:\t\n 620:\t/**\n 621:\t * __batadv_tt_local_event() - store a local TT event (ADD/DEL) with given flags\n 622:\t * @bat_priv: the bat priv with all the mesh interface information\n 623:\t * @common: the TT entry involved in the event\n 624:\t * @flags: flags of the TT entry combined with the event flags\n 625:\t */\n 626:\tstatic void __batadv_tt_local_event(struct batadv_priv *bat_priv,\n 627:\t\t\t\t\t const struct batadv_tt_common_entry *common,\n 628:\t\t\t\t\t u8 flags)\n 629:\t{\n 630:\t\tstruct batadv_tt_change_node *tt_change_node;\n 631:\t\tstruct batadv_tt_change_node *entry;\n 632:\t\tstruct batadv_tt_change_node *safe;\n 633:\t\tbool del_op_requested;\n 634:\t\tbool del_op_entry;\n 635:\t\tsize_t changes;\n 636:\t\n 637:\t\ttt_change_node = kmem_cache_alloc(batadv_tt_change_cache, GFP_ATOMIC);\n 638:\t\tif (!tt_change_node)\n 639:\t\t\treturn;\n 640:\t\n 641:\t\tmemset(tt_change_node-\u003echange.reserved, 0,\n 642:\t\t sizeof(tt_change_node-\u003echange.reserved));\n 643:\t\tether_addr_copy(tt_change_node-\u003echange.addr, common-\u003eaddr);\n 644:\t\ttt_change_node-\u003echange.vid = htons(common-\u003evid);\n 645:\t\n 646:\t\ttt_change_node-\u003echange.flags = flags;\n 647:\t\tdel_op_requested = flags \u0026 BATADV_TT_CLIENT_DEL;\n 648:\t\n 649:\t\t/* check for ADD+DEL, DEL+ADD, ADD+ADD or DEL+DEL events */\n 650:\t\tspin_lock_bh(\u0026bat_priv-\u003ett.changes_list_lock);\n 651:\t\tchanges = READ_ONCE(bat_priv-\u003ett.local_changes);\n 652:\t\tlist_for_each_entry_safe(entry, safe, \u0026bat_priv-\u003ett.changes_list,\n 653:\t\t\t\t\t list) {\n 654:\t\t\tif (!batadv_compare_eth(entry-\u003echange.addr, common-\u003eaddr))\n 655:\t\t\t\tcontinue;\n 656:\t\n 657:\t\t\tif (entry-\u003echange.vid != tt_change_node-\u003echange.vid)\n 658:\t\t\t\tcontinue;\n 659:\t\n 660:\t\t\tdel_op_entry = entry-\u003echange.flags \u0026 BATADV_TT_CLIENT_DEL;\n 661:\t\t\tif (del_op_requested != del_op_entry) {\n 662:\t\t\t\t/* DEL+ADD in the same orig interval have no effect and\n 663:\t\t\t\t * can be removed to avoid silly behaviour on the\n 664:\t\t\t\t * receiver side. The other way around (ADD+DEL) can\n 665:\t\t\t\t * happen in case of roaming of a client still in the\n 666:\t\t\t\t * NEW state. Roaming of NEW clients is now possible due\n 667:\t\t\t\t * to automatically recognition of \"temporary\" clients\n 668:\t\t\t\t */\n 669:\t\t\t\tlist_del(\u0026entry-\u003elist);\n 670:\t\t\t\tkmem_cache_free(batadv_tt_change_cache, entry);\n 671:\t\t\t\tchanges--;\n 672:\t\t\t} else {\n 673:\t\t\t\t/* this is a second add or del in the same originator\n 674:\t\t\t\t * interval. It could mean that flags have been changed\n 675:\t\t\t\t * (e.g. double add): update them\n 676:\t\t\t\t */\n 677:\t\t\t\tentry-\u003echange.flags = flags;\n 678:\t\t\t}\n 679:\t\n 680:\t\t\tkmem_cache_free(batadv_tt_change_cache, tt_change_node);\n 681:\t\t\tgoto update_changes;\n 682:\t\t}\n 683:\t\n 684:\t\t/* track the change in the OGMinterval list */\n 685:\t\tlist_add_tail(\u0026tt_change_node-\u003elist, \u0026bat_priv-\u003ett.changes_list);\n 686:\t\tchanges++;\n 687:\t\n 688:\tupdate_changes:\n 689:\t\tWRITE_ONCE(bat_priv-\u003ett.local_changes, changes);\n 690:\t\tspin_unlock_bh(\u0026bat_priv-\u003ett.changes_list_lock);\n 691:\t}\n 692:\t\n 693:\t/**\n 694:\t * batadv_tt_local_event() - store a local TT event (ADD/DEL)\n"
} |
| 39/3 | 2026/10/01 09:37 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 18232 output: 41 thoughts: 1335 Thoughts: **Code Review: Translation Table Concerns** Okay, let's break this down. First, in `__batadv_tt_local_event`, I see explicit initialization of all `tt_change_node->change` fields after allocating memory. This is solid practice, and it's good that it's consistent with the existing structure. It seems like the refactoring to take `flags` as a parameter instead of recalculating them is fine, and doesn't introduce any new concerns. Now, looking at `batadv_tt_local_reserve`, I'm noticing an interesting point. The `*table_size` is always assigned within the `scoped_guard`, regardless of the return value. This is important to note. Meaning it's *always* initialized. Subsequently, in `batadv_tt_local_reserve_entry` and `batadv_tt_local_reserve_vlan`, the code correctly uses the initialized `table_size` variable in the `net_ratelimited_function` call, even when `batadv_tt_local_reserve` returns false. So, that's clean and safe from a use-before-initialization perspective. Finally, let's examine `batadv_tt_local_mark_removed`. I see that `*curr_flags = common->flags;` is written right at the beginning of the scoped guard. Crucially, before any `break` statement. The surrounding logic seems sound too. But wait... In the caller, `batadv_tt_local_remove`... I need to double-check something. Is the value passed in for `curr_flags` even used in `batadv_tt_local_remove`? I need to quickly scan lines 1715-1745 of `batadv_tt_local_remove`. Making sure it's actually used where I think it is, or not used at all. This requires immediate attention! |
| 40/3 | 2026/10/01 09:37 | tool | read-file |
0mArgs:{
"File": "net/batman-adv/translation-table.c",
"FirstLine": 1715,
"LineCount": 40
}Results: {
"Contents": "1715:\t */\n1716:\tu16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,\n1717:\t\t\t\t unsigned short vid, const char *message,\n1718:\t\t\t\t bool roaming)\n1719:\t{\n1720:\t\tstruct batadv_tt_local_entry *tt_local_entry;\n1721:\t\tu16 curr_flags;\n1722:\t\n1723:\t\ttt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);\n1724:\t\tif (!tt_local_entry)\n1725:\t\t\treturn BATADV_NO_FLAGS;\n1726:\t\n1727:\t\t/* if this client has been added right now, it is possible to\n1728:\t\t * immediately purge it\n1729:\t\t */\n1730:\t\tif (!batadv_tt_local_mark_removed(bat_priv, tt_local_entry, message,\n1731:\t\t\t\t\t\t roaming, \u0026curr_flags))\n1732:\t\t\tbatadv_tt_local_remove_now(bat_priv, tt_local_entry);\n1733:\t\n1734:\t\tbatadv_tt_local_entry_put(tt_local_entry);\n1735:\t\n1736:\t\treturn curr_flags;\n1737:\t}\n1738:\t\n1739:\t/**\n1740:\t * batadv_tt_local_purge_list() - purge inactive tt local entries\n1741:\t * @bat_priv: the bat priv with all the mesh interface information\n1742:\t * @head: pointer to the list containing the local tt entries\n1743:\t * @timeout: parameter deciding whether a given tt local entry is considered\n1744:\t * inactive or not\n1745:\t */\n1746:\tstatic void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,\n1747:\t\t\t\t\t struct hlist_head *head,\n1748:\t\t\t\t\t int timeout)\n1749:\t{\n1750:\t\tstruct batadv_tt_common_entry *tt_common_entry;\n1751:\t\tstruct batadv_tt_local_entry *tt_local_entry;\n1752:\t\tstruct hlist_node *node_tmp;\n1753:\t\n1754:\t\thlist_for_each_entry_safe(tt_common_entry, node_tmp, head,\n"
} |
| 41/3 | 2026/10/01 09:37 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20690 output: 216 thoughts: 1267 Thoughts: **Analysis of the Patch for KMSAN Needs** Okay, let's break this down. My primary concern here is determining whether this patch introduces any risk that *only* KMSAN would catch. From the analysis, `curr_flags` is handled correctly within the `batadv_tt_local_remove` function and is initialized, so no issues there. The VLAN allocation also uses `kzalloc_obj`, which is a good indicator, and initializes memory. So far, so good. Now, the core of the matter: user-space interaction. I need to make sure this patch isn't touching any kernel structures that are sent to user space or modifying existing ones in a way that introduces uninitialized memory reads or information leaks. I went through the modified files meticulously: * `bridge_loop_avoidance.c`: just setting `claim_hash = NULL` on error, no data transfer here. * `distributed-arp-table.c`: `assign_bit` usage, okay, it is a helper. * `main.c`: initialization of `reserve_lock` - good. * `mesh-interface.c`: VLAN reservation/unreservation, this is fine because of `kzalloc_obj` use. * `translation-table.c`: this is where it gets interesting, with all the `batadv_tt_*` modifications. The functions listed are touched and modified, but I checked that the patch doesn't involve any sending of data to userspace, or uninitialized variables. I also checked that the added variables and struct fields (`table_size`, `curr_flags`, `flags`, `i`, `reserved_entries`, `reserved_vlans`, `reserve_lock`) are all properly initialized before use. The `batadv_priv_tt` is `kzalloc`'ed during allocation, so all struct fields are zeroed. The `reserve_lock` is initialized in `batadv_mesh_init`. The potential bug categories I considered were locking issues and logic errors. Lockdep will catch any deadlock, and KASAN, UBSAN, and standard testing should catch double-frees, UAF, NULL dereferences and off-by-one errors. Critically, I didn't see anything that risked uninitialized memory reads or information disclosure to user space or the network. Now, let's revisit the guidelines. I need to distinguish when KMSAN is *necessary*. The rule states, and I agree completely, that KMSAN should only be used if the changes introduce or expose uninitialized memory risks that *only* KMSAN can detect. If the bugs are detectable by KASAN, LOCKDEP, or standard bug detectors, then KMSAN is not necessary. Based on my analysis, this patch does *not* introduce any such risks. All structures and variables are fully initialized or zeroed before being used or exposed. This primarily risks potential locking issues and logic errors, which are covered by the existing bug detection tools. Therefore, `NeedsKMSAN` should be `false`. Now, let's call `set-results` with this reasoning. |
| 42/3 | 2026/10/01 09:37 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series addresses synchronization, state management, and table size reservations in batman-adv (including claim_hash cleanup in bridge loop avoidance, bit assignment in DAT, and translation table reservation tracking for VLANs and local TT entries). \n\nNone of the changes introduce uninitialized memory allocations, uninitialized variable reads, or copy operations of uninitialized kernel structures to userspace/network buffers. All new structure fields in struct batadv_priv_tt are zero-initialized as part of netdev allocation, stack variables (such as table_size) are explicitly written before use, and newly allocated structures (such as batadv_meshif_vlan) continue to be allocated via kzalloc_obj.\n\nAny potential bugs introduced by these modifications would be related to locking/concurrency (handled by LOCKDEP) or reference counting/memory cleanup (handled by KASAN). Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series addresses synchronization, state management, and table size reservations in batman-adv (including claim_hash cleanup in bridge loop avoidance, bit assignment in DAT, and translation table reservation tracking for VLANs and local TT entries). \n\nNone of the changes introduce uninitialized memory allocations, uninitialized variable reads, or copy operations of uninitialized kernel structures to userspace/network buffers. All new structure fields in struct batadv_priv_tt are zero-initialized as part of netdev allocation, stack variables (such as table_size) are explicitly written before use, and newly allocated structures (such as batadv_meshif_vlan) continue to be allocated via kzalloc_obj.\n\nAny potential bugs introduced by these modifications would be related to locking/concurrency (handled by LOCKDEP) or reference counting/memory cleanup (handled by KASAN). Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|