--- /dev/null
+From 2ccd8ff980b50e842481bae71102fa3883fc4377 Mon Sep 17 00:00:00 2001
+From: Vladimir Murzin <vladimir.murzin@arm.com>
+Date: Fri, 15 May 2026 14:37:29 +0100
+Subject: arm64: probes: Handle probes on hinted conditional branch instructions
+
+From: Vladimir Murzin <vladimir.murzin@arm.com>
+
+commit 2ccd8ff980b50e842481bae71102fa3883fc4377 upstream.
+
+BC.cond instructions introduced by FEAT_HBC cannot be executed
+out-of-line, like other branch instructions. However, they can be
+simulated in the same way as B.cond instructions.
+
+Extend the B.cond decoder mask to match BC.cond instructions as well,
+and handle them using the existing B.cond simulation path.
+
+Fixes: 7f86d128e437 ("arm64: add HWCAP for FEAT_HBC (hinted conditional branches)")
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/arm64/include/asm/insn.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/include/asm/insn.h
++++ b/arch/arm64/include/asm/insn.h
+@@ -409,7 +409,7 @@ __AARCH64_INSN_FUNCS(cbz, 0x7F000000, 0x
+ __AARCH64_INSN_FUNCS(cbnz, 0x7F000000, 0x35000000)
+ __AARCH64_INSN_FUNCS(tbz, 0x7F000000, 0x36000000)
+ __AARCH64_INSN_FUNCS(tbnz, 0x7F000000, 0x37000000)
+-__AARCH64_INSN_FUNCS(bcond, 0xFF000010, 0x54000000)
++__AARCH64_INSN_FUNCS(bcond, 0xFF000000, 0x54000000)
+ __AARCH64_INSN_FUNCS(svc, 0xFFE0001F, 0xD4000001)
+ __AARCH64_INSN_FUNCS(hvc, 0xFFE0001F, 0xD4000002)
+ __AARCH64_INSN_FUNCS(smc, 0xFFE0001F, 0xD4000003)
--- /dev/null
+From 83ab69bd12b80f6ea169c8bea6977701b53a043d Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Tue, 12 May 2026 09:13:31 +0200
+Subject: batman-adv: bla: avoid double decrement of bla.num_requests
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 83ab69bd12b80f6ea169c8bea6977701b53a043d upstream.
+
+The bla.num_requests is increased when no request_sent was in progress. And
+it is decremented in various places (announcement was received, backbone is
+purged, periodic work). But the check if the request_sent is actually set
+to a specific state and the atomic_dec/_inc are not safe because they are
+not atomic (TOCTOU) and multiple such code portions can run concurrently.
+
+At the same time, it is necessary to modify request_sent (state) and
+bla.num_requests atomically. Otherwise batadv_bla_send_request() might set
+request_sent to 1 and is interrupted. batadv_handle_announce() can then
+set request_sent back to 0 and decrement num_requests before
+batadv_bla_send_request() incremented it.
+
+The two operations must therefore be locked. And since state (request_sent)
+and wait_periods are only accessed inside this lock, they can be converted
+to simpler datatypes. And to avoid that the bla.num_requests is touched by
+a parallel running context with a valid backbone_gw reference after
+batadv_bla_purge_backbone_gw() ran, a third state "stopped" is required to
+correctly signal that a backbone_gw is in the state of being cleaned up.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++++++-----------
+ net/batman-adv/mesh-interface.c | 1
+ net/batman-adv/types.h | 39 ++++++++++++++++++++-----
+ 3 files changed, 67 insertions(+), 24 deletions(-)
+
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -515,8 +515,8 @@ batadv_bla_get_backbone_gw(struct batadv
+ entry->crc = BATADV_BLA_CRC_INIT;
+ entry->bat_priv = bat_priv;
+ spin_lock_init(&entry->crc_lock);
+- atomic_set(&entry->request_sent, 0);
+- atomic_set(&entry->wait_periods, 0);
++ entry->state = BATADV_BLA_BACKBONE_GW_SYNCED;
++ entry->wait_periods = 0;
+ ether_addr_copy(entry->orig, orig);
+ INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report);
+ kref_init(&entry->refcount);
+@@ -545,9 +545,13 @@ batadv_bla_get_backbone_gw(struct batadv
+ batadv_bla_send_announce(bat_priv, entry);
+
+ /* this will be decreased in the worker thread */
+- atomic_inc(&entry->request_sent);
+- atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS);
+- atomic_inc(&bat_priv->bla.num_requests);
++ spin_lock_bh(&bat_priv->bla.num_requests_lock);
++ if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++ entry->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
++ entry->wait_periods = BATADV_BLA_WAIT_PERIODS;
++ atomic_inc(&bat_priv->bla.num_requests);
++ }
++ spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+ }
+
+ return entry;
+@@ -650,10 +654,12 @@ static void batadv_bla_send_request(stru
+ backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST);
+
+ /* no local broadcasts should be sent or received, for now. */
+- if (!atomic_read(&backbone_gw->request_sent)) {
++ spin_lock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
++ if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
++ backbone_gw->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
+ atomic_inc(&backbone_gw->bat_priv->bla.num_requests);
+- atomic_set(&backbone_gw->request_sent, 1);
+ }
++ spin_unlock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
+ }
+
+ /**
+@@ -874,10 +880,12 @@ static bool batadv_handle_announce(struc
+ /* if we have sent a request and the crc was OK,
+ * we can allow traffic again.
+ */
+- if (atomic_read(&backbone_gw->request_sent)) {
++ spin_lock_bh(&bat_priv->bla.num_requests_lock);
++ if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) {
++ backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+ atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+- atomic_set(&backbone_gw->request_sent, 0);
+ }
++ spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+ }
+
+ batadv_backbone_gw_put(backbone_gw);
+@@ -1256,9 +1264,13 @@ purge_now:
+ purged = true;
+
+ /* don't wait for the pending request anymore */
+- if (atomic_read(&backbone_gw->request_sent))
++ spin_lock_bh(&bat_priv->bla.num_requests_lock);
++ if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED)
+ atomic_dec(&bat_priv->bla.num_requests);
+
++ backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED;
++ spin_unlock_bh(&bat_priv->bla.num_requests_lock);
++
+ batadv_bla_del_backbone_claims(backbone_gw);
+
+ hlist_del_rcu(&backbone_gw->hash_entry);
+@@ -1509,7 +1521,7 @@ static void batadv_bla_periodic_work(str
+ batadv_bla_send_loopdetect(bat_priv,
+ backbone_gw);
+
+- /* request_sent is only set after creation to avoid
++ /* state is only set to unsynced after creation to avoid
+ * problems when we are not yet known as backbone gw
+ * in the backbone.
+ *
+@@ -1518,14 +1530,21 @@ static void batadv_bla_periodic_work(str
+ * some grace time.
+ */
+
+- if (atomic_read(&backbone_gw->request_sent) == 0)
+- continue;
++ spin_lock_bh(&bat_priv->bla.num_requests_lock);
++ if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED)
++ goto unlock_next;
+
+- if (!atomic_dec_and_test(&backbone_gw->wait_periods))
+- continue;
++ if (backbone_gw->wait_periods > 0)
++ backbone_gw->wait_periods--;
++
++ if (backbone_gw->wait_periods > 0)
++ goto unlock_next;
+
++ backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+ atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+- atomic_set(&backbone_gw->request_sent, 0);
++
++unlock_next:
++ spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+ }
+ rcu_read_unlock();
+ }
+--- a/net/batman-adv/mesh-interface.c
++++ b/net/batman-adv/mesh-interface.c
+@@ -787,6 +787,7 @@ static int batadv_meshif_init_late(struc
+ atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
+ #ifdef CONFIG_BATMAN_ADV_BLA
+ atomic_set(&bat_priv->bla.num_requests, 0);
++ spin_lock_init(&bat_priv->bla.num_requests_lock);
+ #endif
+ atomic_set(&bat_priv->tp_num, 0);
+
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1027,6 +1027,12 @@ struct batadv_priv_bla {
+ atomic_t num_requests;
+
+ /**
++ * @num_requests_lock: locks update num_requests +
++ * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update
++ */
++ spinlock_t num_requests_lock;
++
++ /**
+ * @claim_hash: hash table containing mesh nodes this host has claimed
+ */
+ struct batadv_hashtable *claim_hash;
+@@ -1669,6 +1675,27 @@ struct batadv_priv {
+
+ #ifdef CONFIG_BATMAN_ADV_BLA
+
++enum batadv_bla_backbone_gw_state {
++ /**
++ * @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed
++ * and it must not longer work on requests
++ */
++ BATADV_BLA_BACKBONE_GW_STOPPED,
++
++ /**
++ * @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out
++ * of sync and a request was send. No traffic is forwarded until the
++ * situation is resolved
++ */
++ BATADV_BLA_BACKBONE_GW_UNSYNCED,
++
++ /**
++ * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in
++ * sync. traffic can be forwarded
++ */
++ BATADV_BLA_BACKBONE_GW_SYNCED,
++};
++
+ /**
+ * struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN
+ */
+@@ -1694,16 +1721,12 @@ struct batadv_bla_backbone_gw {
+ /**
+ * @wait_periods: grace time for bridge forward delays and bla group
+ * forming at bootup phase - no bcast traffic is formwared until it has
+- * elapsed
++ * elapsed. Must only be access with num_requests_lock.
+ */
+- atomic_t wait_periods;
++ u8 wait_periods;
+
+- /**
+- * @request_sent: if this bool is set to true we are out of sync with
+- * this backbone gateway - no bcast traffic is formwared until the
+- * situation was resolved
+- */
+- atomic_t request_sent;
++ /** @state: sync state. Must only be access with num_requests_lock. */
++ enum batadv_bla_backbone_gw_state state;
+
+ /** @crc: crc16 checksum over all claims */
+ u16 crc;
--- /dev/null
+From f80d3d98d2ff78d9e2fe5d68b1f45948c4f7bd24 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Tue, 19 May 2026 09:23:49 +0200
+Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f80d3d98d2ff78d9e2fe5d68b1f45948c4f7bd24 upstream.
+
+Without rtnl_lock held, a hardif might be retrieved as primary interface of
+a meshif, but then (while operating on this interface) getting decoupled
+from the mesh interface. In this case, the meshif still exists but the
+pointer from the primary hardif to the meshif is set to NULL.
+
+The mesh_iface must be checked first to be non-NULL before continuing to
+send an ARP request using meshif.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Reported-by: Ido Schimmel <idosch@nvidia.com>
+Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -357,12 +357,14 @@ static void batadv_bla_send_claim(struct
+ sizeof(local_claim_dest));
+ local_claim_dest.type = claimtype;
+
+- mesh_iface = primary_if->mesh_iface;
++ mesh_iface = READ_ONCE(primary_if->mesh_iface);
++ if (!mesh_iface)
++ goto out;
+
+ skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
+ /* IP DST: 0.0.0.0 */
+ zeroip,
+- primary_if->mesh_iface,
++ mesh_iface,
+ /* IP SRC: 0.0.0.0 */
+ zeroip,
+ /* Ethernet DST: Broadcast */
--- /dev/null
+From 0459430add32ea41f3e2ef9351610e6d33627a6b Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sun, 10 May 2026 11:43:20 +0200
+Subject: batman-adv: bla: fix report_work leak on backbone_gw purge
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 0459430add32ea41f3e2ef9351610e6d33627a6b upstream.
+
+batadv_bla_purge_backbone_gw() removes stale backbone gateway entries,
+but fails to properly handle their associated report_work:
+
+- If report_work is running, the purge must wait for it to finish before
+ freeing the backbone_gw, otherwise the worker may access freed memory
+ (e.g. bat_priv).
+- If report_work is pending, the purge must cancel it and release the
+ reference held for that pending work item.
+
+The previous implementation called hlist_for_each_entry_safe() inside a
+spin_lock_bh() section, but cancel_work_sync() may sleep and therefore
+cannot be called from within a spinlock-protected region.
+
+Restructure the loop to handle one entry per spinlock critical section:
+acquire the lock, find the next entry to purge, remove it from the hash
+list, then release the lock before calling cancel_work_sync() and
+dropping the hash_entry reference. Repeat until no more entries require
+purging.
+
+Cc: stable@kernel.org
+Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
+Reviewed-by: Simon Wunderlich <sw@simonwunderlich.de>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/bridge_loop_avoidance.c | 60 ++++++++++++++++++++-------------
+ 1 file changed, 38 insertions(+), 22 deletions(-)
+
+--- a/net/batman-adv/bridge_loop_avoidance.c
++++ b/net/batman-adv/bridge_loop_avoidance.c
+@@ -1225,6 +1225,7 @@ static void batadv_bla_purge_backbone_gw
+ struct hlist_head *head;
+ struct batadv_hashtable *hash;
+ spinlock_t *list_lock; /* protects write access to the hash lists */
++ bool purged;
+ int i;
+
+ hash = bat_priv->bla.backbone_hash;
+@@ -1235,30 +1236,45 @@ static void batadv_bla_purge_backbone_gw
+ head = &hash->table[i];
+ list_lock = &hash->list_locks[i];
+
+- spin_lock_bh(list_lock);
+- hlist_for_each_entry_safe(backbone_gw, node_tmp,
+- head, hash_entry) {
+- if (now)
+- goto purge_now;
+- if (!batadv_has_timed_out(backbone_gw->lasttime,
+- BATADV_BLA_BACKBONE_TIMEOUT))
+- continue;
+-
+- batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv,
+- "%s(): backbone gw %pM timed out\n",
+- __func__, backbone_gw->orig);
++ do {
++ purged = false;
++
++ spin_lock_bh(list_lock);
++ hlist_for_each_entry_safe(backbone_gw, node_tmp,
++ head, hash_entry) {
++ if (now)
++ goto purge_now;
++ if (!batadv_has_timed_out(backbone_gw->lasttime,
++ BATADV_BLA_BACKBONE_TIMEOUT))
++ continue;
++
++ batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv,
++ "%s(): backbone gw %pM timed out\n",
++ __func__, backbone_gw->orig);
+
+ purge_now:
+- /* don't wait for the pending request anymore */
+- if (atomic_read(&backbone_gw->request_sent))
+- atomic_dec(&bat_priv->bla.num_requests);
+-
+- batadv_bla_del_backbone_claims(backbone_gw);
+-
+- hlist_del_rcu(&backbone_gw->hash_entry);
+- batadv_backbone_gw_put(backbone_gw);
+- }
+- spin_unlock_bh(list_lock);
++ purged = true;
++
++ /* don't wait for the pending request anymore */
++ if (atomic_read(&backbone_gw->request_sent))
++ atomic_dec(&bat_priv->bla.num_requests);
++
++ batadv_bla_del_backbone_claims(backbone_gw);
++
++ hlist_del_rcu(&backbone_gw->hash_entry);
++ break;
++ }
++ spin_unlock_bh(list_lock);
++
++ if (purged) {
++ /* reference for pending report_work */
++ if (cancel_work_sync(&backbone_gw->report_work))
++ batadv_backbone_gw_put(backbone_gw);
++
++ /* reference for hash_entry */
++ batadv_backbone_gw_put(backbone_gw);
++ }
++ } while (purged);
+ }
+ }
+
--- /dev/null
+From a340a51ed801eab7bb454150c226323b865263cc Mon Sep 17 00:00:00 2001
+From: Ruijie Li <ruijieli51@gmail.com>
+Date: Thu, 14 May 2026 16:13:25 +0800
+Subject: batman-adv: clear current gateway during teardown
+
+From: Ruijie Li <ruijieli51@gmail.com>
+
+commit a340a51ed801eab7bb454150c226323b865263cc upstream.
+
+batadv_gw_node_free() removes the gateway list entries during mesh teardown,
+but it does not clear the currently selected gateway. This leaves stale
+gateway state behind across cleanup and can break a later mesh recreation.
+
+Clear bat_priv->gw.curr_gw before walking the gateway list so the selected
+gateway reference is dropped as part of teardown.
+
+Fixes: 2265c1410864 ("batman-adv: gateway election code refactoring")
+Cc: stable@kernel.org
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Signed-off-by: Ruijie Li <ruijieli51@gmail.com>
+Signed-off-by: Zhanpeng Li <lzhanpeng2025@lzu.edu.cn>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/gateway_client.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/net/batman-adv/gateway_client.c
++++ b/net/batman-adv/gateway_client.c
+@@ -478,10 +478,14 @@ void batadv_gw_node_delete(struct batadv
+ */
+ void batadv_gw_node_free(struct batadv_priv *bat_priv)
+ {
++ struct batadv_gw_node *curr_gw;
+ struct batadv_gw_node *gw_node;
+ struct hlist_node *node_tmp;
+
+ spin_lock_bh(&bat_priv->gw.list_lock);
++ curr_gw = rcu_replace_pointer(bat_priv->gw.curr_gw, NULL, true);
++ batadv_gw_node_put(curr_gw);
++
+ hlist_for_each_entry_safe(gw_node, node_tmp,
+ &bat_priv->gw.gateway_list, list) {
+ hlist_del_init_rcu(&gw_node->list);
--- /dev/null
+From 2d8826a2d3657cea66fb0370f9e521575a673871 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Wed, 13 May 2026 09:01:34 +0200
+Subject: batman-adv: dat: handle forward allocation error
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 2d8826a2d3657cea66fb0370f9e521575a673871 upstream.
+
+batadv_dat_forward_data() calls pskb_copy_for_clone() to duplicate an skb
+for each DHT candidate, but does not check the return value before passing
+it to batadv_send_skb_prepare_unicast_4addr(). That function dereferences
+the skb unconditionally, so a failed allocation triggers a NULL pointer
+dereference.
+
+Skip forwarding to the current DHT candidate on allocation failure.
+
+Cc: stable@kernel.org
+Fixes: 785ea1144182 ("batman-adv: Distributed ARP Table - create DHT helper functions")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/distributed-arp-table.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/net/batman-adv/distributed-arp-table.c
++++ b/net/batman-adv/distributed-arp-table.c
+@@ -697,6 +697,9 @@ static bool batadv_dat_forward_data(stru
+ goto free_orig;
+
+ tmp_skb = pskb_copy_for_clone(skb, GFP_ATOMIC);
++ if (!tmp_skb)
++ goto free_neigh;
++
+ if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, tmp_skb,
+ cand[i].orig_node,
+ packet_subtype)) {
--- /dev/null
+From 9cd3f16c320bfdadd4509358122368deb56a5741 Mon Sep 17 00:00:00 2001
+From: Ruide Cao <caoruide123@gmail.com>
+Date: Wed, 13 May 2026 11:58:15 +0800
+Subject: batman-adv: fix fragment reassembly length accounting
+
+From: Ruide Cao <caoruide123@gmail.com>
+
+commit 9cd3f16c320bfdadd4509358122368deb56a5741 upstream.
+
+batman-adv keeps a running payload length for queued fragments and uses it
+to validate a fragment chain before reassembly.
+
+That accounting currently allows the accumulated fragment length to be
+truncated during updates. As a result, malformed fragment chains can
+bypass the intended validation and drive reassembly with inconsistent
+length state, leading to a local denial of service.
+
+Fix the accounting by storing the accumulated length in a length-typed
+field and rejecting update overflows before the existing validation logic
+runs.
+
+The fix was verified against the original reproducer and against valid
+fragment reassembly paths.
+
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Cc: stable@kernel.org
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Signed-off-by: Ruide Cao <caoruide123@gmail.com>
+Tested-by: Ren Wei <enjou1224z@gmail.com>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/fragmentation.c | 23 +++++++++++++++++------
+ net/batman-adv/types.h | 2 +-
+ 2 files changed, 18 insertions(+), 7 deletions(-)
+
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -17,6 +17,7 @@
+ #include <linux/lockdep.h>
+ #include <linux/minmax.h>
+ #include <linux/netdevice.h>
++#include <linux/overflow.h>
+ #include <linux/skbuff.h>
+ #include <linux/slab.h>
+ #include <linux/spinlock.h>
+@@ -80,9 +81,9 @@ void batadv_frag_purge_orig(struct batad
+ *
+ * Return: the maximum size of payload that can be fragmented.
+ */
+-static int batadv_frag_size_limit(void)
++static size_t batadv_frag_size_limit(void)
+ {
+- int limit = BATADV_FRAG_MAX_FRAG_SIZE;
++ size_t limit = BATADV_FRAG_MAX_FRAG_SIZE;
+
+ limit -= sizeof(struct batadv_frag_packet);
+ limit *= BATADV_FRAG_MAX_FRAGMENTS;
+@@ -143,7 +144,9 @@ static bool batadv_frag_insert_packet(st
+ struct batadv_frag_packet *frag_packet;
+ u8 bucket;
+ u16 seqno, hdr_size = sizeof(struct batadv_frag_packet);
++ bool overflow = false;
+ bool ret = false;
++ size_t data_len;
+
+ /* Linearize packet to avoid linearizing 16 packets in a row when doing
+ * the later merge. Non-linear merge should be added to remove this
+@@ -153,6 +156,7 @@ static bool batadv_frag_insert_packet(st
+ goto err;
+
+ frag_packet = (struct batadv_frag_packet *)skb->data;
++ data_len = skb->len - hdr_size;
+ seqno = ntohs(frag_packet->seqno);
+ bucket = seqno % BATADV_FRAG_BUFFER_COUNT;
+
+@@ -171,7 +175,7 @@ static bool batadv_frag_insert_packet(st
+ spin_lock_bh(&chain->lock);
+ if (batadv_frag_init_chain(chain, seqno)) {
+ hlist_add_head(&frag_entry_new->list, &chain->fragment_list);
+- chain->size = skb->len - hdr_size;
++ chain->size = data_len;
+ chain->timestamp = jiffies;
+ chain->total_size = ntohs(frag_packet->total_size);
+ ret = true;
+@@ -188,7 +192,11 @@ static bool batadv_frag_insert_packet(st
+ if (frag_entry_curr->no < frag_entry_new->no) {
+ hlist_add_before(&frag_entry_new->list,
+ &frag_entry_curr->list);
+- chain->size += skb->len - hdr_size;
++
++ if (check_add_overflow(chain->size, data_len,
++ &chain->size))
++ overflow = true;
++
+ chain->timestamp = jiffies;
+ ret = true;
+ goto out;
+@@ -201,13 +209,16 @@ static bool batadv_frag_insert_packet(st
+ /* Reached the end of the list, so insert after 'frag_entry_last'. */
+ if (likely(frag_entry_last)) {
+ hlist_add_behind(&frag_entry_new->list, &frag_entry_last->list);
+- chain->size += skb->len - hdr_size;
++
++ if (check_add_overflow(chain->size, data_len, &chain->size))
++ overflow = true;
++
+ chain->timestamp = jiffies;
+ ret = true;
+ }
+
+ out:
+- if (chain->size > batadv_frag_size_limit() ||
++ if (overflow || chain->size > batadv_frag_size_limit() ||
+ chain->total_size != ntohs(frag_packet->total_size) ||
+ chain->total_size > batadv_frag_size_limit()) {
+ /* Clear chain if total size of either the list or the packet
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -304,7 +304,7 @@ struct batadv_frag_table_entry {
+ u16 seqno;
+
+ /** @size: accumulated size of packets in list */
+- u16 size;
++ size_t size;
+
+ /** @total_size: expected size of the assembled packet */
+ u16 total_size;
--- /dev/null
+From 94f3b133168d1c49895e7cc6afbcf1cc0b354602 Mon Sep 17 00:00:00 2001
+From: Luxiao Xu <rakukuip@gmail.com>
+Date: Mon, 11 May 2026 18:52:09 +0200
+Subject: batman-adv: fix tp_meter counter underflow during shutdown
+
+From: Luxiao Xu <rakukuip@gmail.com>
+
+commit 94f3b133168d1c49895e7cc6afbcf1cc0b354602 upstream.
+
+batadv_tp_sender_shutdown() unconditionally decrements the "sending"
+atomic counter. If multiple paths (e.g. timeout, user cancel, and
+normal finish) call this function, the counter can underflow to -1.
+
+Since the sender logic treats any non-zero value as "still sending",
+a negative value causes the sender kthread to loop indefinitely.
+This leads to a use-after-free when the interface is removed while
+the zombie thread is still active.
+
+Fix this by using atomic_xchg() to ensure the counter only transitions
+from 1 to 0 once.
+
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Cc: stable@kernel.org
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Signed-off-by: Luxiao Xu <rakukuip@gmail.com>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+[sven: added missing change in batadv_tp_send]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/tp_meter.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -451,7 +451,7 @@ static void batadv_tp_sender_end(struct
+ static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars,
+ enum batadv_tp_meter_reason reason)
+ {
+- if (!atomic_dec_and_test(&tp_vars->sending))
++ if (atomic_xchg(&tp_vars->sending, 0) != 1)
+ return;
+
+ tp_vars->reason = reason;
+@@ -885,7 +885,7 @@ static int batadv_tp_send(void *arg)
+ "Meter: %s() cannot send packets (%d)\n",
+ __func__, err);
+ /* ensure nobody else tries to stop the thread now */
+- if (atomic_dec_and_test(&tp_vars->sending))
++ if (atomic_xchg(&tp_vars->sending, 0) == 1)
+ tp_vars->reason = err;
+ break;
+ }
--- /dev/null
+From bc62216dc8e221e3781afa14430f45208bfa9af9 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Wed, 13 May 2026 09:01:36 +0200
+Subject: batman-adv: frag: disallow unicast fragment in fragment
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit bc62216dc8e221e3781afa14430f45208bfa9af9 upstream.
+
+batadv_frag_skb_buffer() is called by batadv_batman_skb_recv() when a
+BATADV_UNICAST_FRAG packet is received. Once all fragments are collected
+and the packet is reassembled, batadv_recv_frag_packet() calls
+batadv_batman_skb_recv() again to process the defragmented payload.
+
+A malicious sender can craft a BATADV_UNICAST_FRAG packet whose reassembled
+payload is itself a BATADV_UNICAST_FRAG packet (matryoshka-style nesting).
+Each nesting level recurses through batadv_batman_skb_recv() without bound,
+growing the kernel stack until it is exhausted.
+
+Since refragmentation or fragments in fragments are not actually allowed,
+discard all packets which are still BATADV_UNICAST_FRAG packets after the
+defragmentation process.
+
+Cc: stable@kernel.org
+Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/fragmentation.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+--- a/net/batman-adv/fragmentation.c
++++ b/net/batman-adv/fragmentation.c
+@@ -305,6 +305,31 @@ free:
+ }
+
+ /**
++ * batadv_skb_is_frag() - check if newly merged skb is gain a unicast packet
++ * @skb: newly merged skb
++ *
++ * Return: if newly skb is of type BATADV_UNICAST_FRAG
++ */
++static bool batadv_skb_is_frag(struct sk_buff *skb)
++{
++ struct batadv_ogm_packet *batadv_ogm_packet;
++
++ /* packet should hold at least type and version */
++ if (unlikely(!pskb_may_pull(skb, 2)))
++ return false;
++
++ batadv_ogm_packet = (struct batadv_ogm_packet *)skb->data;
++
++ if (batadv_ogm_packet->version != BATADV_COMPAT_VERSION)
++ return false;
++
++ if (batadv_ogm_packet->packet_type != BATADV_UNICAST_FRAG)
++ return false;
++
++ return true;
++}
++
++/**
+ * batadv_frag_skb_buffer() - buffer fragment for later merge
+ * @skb: skb to buffer
+ * @orig_node_src: originator that the skb is received from
+@@ -337,6 +362,16 @@ bool batadv_frag_skb_buffer(struct sk_bu
+ if (!skb_out)
+ goto out_err;
+
++ /* fragment in fragment is not allowed. otherwise it is possible
++ * to exhaust the stack when receiving a matryoshka-style
++ * "fragments in a fragment packet"
++ */
++ if (batadv_skb_is_frag(skb_out)) {
++ kfree_skb(skb_out);
++ skb_out = NULL;
++ goto out_err;
++ }
++
+ out:
+ ret = true;
+ out_err:
--- /dev/null
+From aa3153bd139a6c48667dcd02608d3b2c80bff02c Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Fri, 15 May 2026 22:00:40 +0200
+Subject: batman-adv: iv: recover OGM scheduling after forward packet error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit aa3153bd139a6c48667dcd02608d3b2c80bff02c upstream.
+
+When batadv_iv_ogm_schedule_buff() fails to allocate and queue a forward
+packet for OGM transmission, the work item that drives periodic OGM
+scheduling is never re-armed. This silently halts transmission of the
+node's own OGMs on the affected interface — only OGMs from other peers
+continue to be aggregated and forwarded.
+
+Fix this by tracking whether batadv_iv_ogm_queue_add() (and transitively
+batadv_iv_ogm_aggregate_new()) successfully scheduled a forward packet.
+When scheduling fails, batadv_iv_ogm_schedule_buff() falls back to queuing
+a dedicated recovery work item (reschedule_work) that fires after one
+originator interval and calls batadv_iv_ogm_schedule() again.
+
+Cc: stable@kernel.org
+Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++++++++-----------
+ net/batman-adv/types.h | 3 +
+ 2 files changed, 60 insertions(+), 19 deletions(-)
+
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -224,6 +224,8 @@ static void batadv_iv_ogm_iface_disable(
+ hard_iface->bat_iv.ogm_buff = NULL;
+
+ mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
++
++ cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work);
+ }
+
+ static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
+@@ -536,8 +538,10 @@ out:
+ * @if_incoming: interface where the packet was received
+ * @if_outgoing: interface for which the retransmission should be considered
+ * @own_packet: true if it is a self-generated ogm
++ *
++ * Return: whether forward packet was scheduled
+ */
+-static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
++static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+ int packet_len, unsigned long send_time,
+ bool direct_link,
+ struct batadv_hard_iface *if_incoming,
+@@ -561,13 +565,13 @@ static void batadv_iv_ogm_aggregate_new(
+
+ skb = netdev_alloc_skb_ip_align(NULL, skb_size);
+ if (!skb)
+- return;
++ return false;
+
+ forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
+ queue_left, bat_priv, skb);
+ if (!forw_packet_aggr) {
+ kfree_skb(skb);
+- return;
++ return false;
+ }
+
+ forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
+@@ -590,6 +594,8 @@ static void batadv_iv_ogm_aggregate_new(
+ batadv_iv_send_outstanding_bat_ogm_packet);
+
+ batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
++
++ return true;
+ }
+
+ /* aggregate a new packet into the existing ogm packet */
+@@ -617,8 +623,10 @@ static void batadv_iv_ogm_aggregate(stru
+ * @if_outgoing: interface for which the retransmission should be considered
+ * @own_packet: true if it is a self-generated ogm
+ * @send_time: timestamp (jiffies) when the packet is to be sent
++ *
++ * Return: whether forward packet was scheduled
+ */
+-static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
++static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+ unsigned char *packet_buff,
+ int packet_len,
+ struct batadv_hard_iface *if_incoming,
+@@ -670,14 +678,16 @@ static void batadv_iv_ogm_queue_add(stru
+ if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
+ send_time += max_aggregation_jiffies;
+
+- batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
+- send_time, direct_link,
+- if_incoming, if_outgoing,
+- own_packet);
++ return batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
++ send_time, direct_link,
++ if_incoming, if_outgoing,
++ own_packet);
+ } else {
+ batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
+ packet_len, direct_link);
+ spin_unlock_bh(&bat_priv->forw_bat_list_lock);
++
++ return true;
+ }
+ }
+
+@@ -790,6 +800,8 @@ static void batadv_iv_ogm_schedule_buff(
+ u32 seqno;
+ u16 tvlv_len = 0;
+ unsigned long send_time;
++ bool reschedule = false;
++ bool scheduled;
+ int ret;
+
+ lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+@@ -818,11 +830,8 @@ static void batadv_iv_ogm_schedule_buff(
+ ogm_buff_len,
+ BATADV_OGM_HLEN);
+ if (ret < 0) {
+- /* OGMs must be queued even when the buffer allocation for
+- * TVLVs failed. just fall back to the non-TVLV version
+- */
+- ret = 0;
+- *ogm_buff_len = BATADV_OGM_HLEN;
++ reschedule = true;
++ goto out;
+ }
+
+ tvlv_len = ret;
+@@ -844,8 +853,11 @@ static void batadv_iv_ogm_schedule_buff(
+ /* OGMs from secondary interfaces are only scheduled on their
+ * respective interfaces.
+ */
+- batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
+- hard_iface, hard_iface, 1, send_time);
++ scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
++ hard_iface, hard_iface, 1, send_time);
++ if (!scheduled)
++ reschedule = true;
++
+ goto out;
+ }
+
+@@ -857,15 +869,28 @@ static void batadv_iv_ogm_schedule_buff(
+ if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
+ continue;
+
+- batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
+- *ogm_buff_len, hard_iface,
+- tmp_hard_iface, 1, send_time);
+-
++ scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
++ *ogm_buff_len, hard_iface,
++ tmp_hard_iface, 1, send_time);
+ batadv_hardif_put(tmp_hard_iface);
++
++ if (!scheduled && tmp_hard_iface == hard_iface)
++ reschedule = true;
+ }
+ rcu_read_unlock();
+
+ out:
++ if (reschedule) {
++ /* there was a failure scheduling the own forward packet.
++ * as result, the batadv_iv_send_outstanding_bat_ogm_packet()
++ * work item is no longer scheduled. it is therefore necessary
++ * to reschedule it manually
++ */
++ queue_delayed_work(batadv_event_workqueue,
++ &hard_iface->bat_iv.reschedule_work,
++ msecs_to_jiffies(atomic_read(&bat_priv->orig_interval)));
++ }
++
+ batadv_hardif_put(primary_if);
+ }
+
+@@ -880,6 +905,17 @@ static void batadv_iv_ogm_schedule(struc
+ mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
+ }
+
++static void batadv_iv_ogm_reschedule(struct work_struct *work)
++{
++ struct delayed_work *delayed_work = to_delayed_work(work);
++ struct batadv_hard_iface *hard_iface;
++
++ hard_iface = container_of(delayed_work,
++ struct batadv_hard_iface,
++ bat_iv.reschedule_work);
++ batadv_iv_ogm_schedule(hard_iface);
++}
++
+ /**
+ * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
+ * @orig_node: originator which reproadcasted the OGMs directly
+@@ -2272,6 +2308,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv
+
+ static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface)
+ {
++ INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule);
++
+ /* begin scheduling originator messages on that interface */
+ batadv_iv_ogm_schedule(hard_iface);
+ }
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -83,6 +83,9 @@ struct batadv_hard_iface_bat_iv {
+ /** @ogm_seqno: OGM sequence number - used to identify each OGM */
+ atomic_t ogm_seqno;
+
++ /** @reschedule_work: recover OGM schedule after schedule error */
++ struct delayed_work reschedule_work;
++
+ /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
+ struct mutex ogm_buff_mutex;
+ };
--- /dev/null
+From 20c2d6a20ca936f5aaa6dd40f73f262ac45c87cc Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Thu, 14 May 2026 19:22:02 +0200
+Subject: batman-adv: mcast: fix use-after-free in orig_node RCU release
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 20c2d6a20ca936f5aaa6dd40f73f262ac45c87cc upstream.
+
+batadv_mcast_purge_orig() removes entries from RCU-protected hlists but
+does not wait for an RCU grace period before returning. Concurrent RCU
+readers may still accesses references to those entries at the point of
+removal. RCU-protected readers trying to operate on entries like
+orig->mcast_want_all_ipv6_node will then access already freed memory.
+
+Fix this by moving batadv_mcast_purge_orig() to batadv_orig_node_release(),
+just before the call_rcu() invocation. This ensures RCU readers that were
+active at purge time have drained before the orig_node memory is reclaimed.
+
+Cc: stable@kernel.org
+Fixes: ab49886e3da7 ("batman-adv: Add IPv4 link-local/IPv6-ll-all-nodes multicast support")
+Acked-by: Linus Lüssing <linus.luessing@c0d3.blue>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/originator.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/net/batman-adv/originator.c
++++ b/net/batman-adv/originator.c
+@@ -835,8 +835,6 @@ static void batadv_orig_node_free_rcu(st
+
+ orig_node = container_of(rcu, struct batadv_orig_node, rcu);
+
+- batadv_mcast_purge_orig(orig_node);
+-
+ batadv_frag_purge_orig(orig_node, NULL);
+
+ kfree(orig_node->tt_buff);
+@@ -887,6 +885,8 @@ void batadv_orig_node_release(struct kre
+ }
+ spin_unlock_bh(&orig_node->vlan_list_lock);
+
++ batadv_mcast_purge_orig(orig_node);
++
+ call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu);
+ }
+
--- /dev/null
+From ff24f2ecfd94c07a2b89bac497433e3b23271cac Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 16 May 2026 12:33:41 +0200
+Subject: batman-adv: tp_meter: avoid role confusion in tp_list
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit ff24f2ecfd94c07a2b89bac497433e3b23271cac upstream.
+
+Session lookups in tp_list matched only on destination address (and
+optionally session ID), leaving role validation to the caller. If two
+sessions with the same other_end coexisted (one as sender, one as receiver)
+a lookup could silently return the wrong one, causing the caller's role to
+bail out early, potentially skipping necessary cleanup.
+
+Move the role check into the lookup functions themselves so the correct
+entry is always returned, or none at all. Since batadv_tp_start()
+legitimately needs to detect any active session to a destination regardless
+of role, introduce a dedicated helper for that case rather than bending the
+existing lookup semantics.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/tp_meter.c | 59 ++++++++++++++++++++++++++++------------------
+ 1 file changed, 36 insertions(+), 23 deletions(-)
+
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -255,6 +255,7 @@ static void batadv_tp_batctl_error_notif
+ * batadv_tp_list_find() - find a tp_vars object in the global list
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @dst: the other endpoint MAC address to look for
++ * @role: role of the session
+ *
+ * Look for a tp_vars object matching dst as end_point and return it after
+ * having increment the refcounter. Return NULL is not found
+@@ -262,7 +263,8 @@ static void batadv_tp_batctl_error_notif
+ * Return: matching tp_vars or NULL when no tp_vars with @dst was found
+ */
+ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
+- const u8 *dst)
++ const u8 *dst,
++ enum batadv_tp_meter_role role)
+ {
+ struct batadv_tp_vars *pos, *tp_vars = NULL;
+
+@@ -271,6 +273,9 @@ static struct batadv_tp_vars *batadv_tp_
+ if (!batadv_compare_eth(pos->other_end, dst))
+ continue;
+
++ if (pos->role != role)
++ continue;
++
+ /* most of the time this function is invoked during the normal
+ * process..it makes sens to pay more when the session is
+ * finished and to speed the process up during the measurement
+@@ -287,11 +292,32 @@ static struct batadv_tp_vars *batadv_tp_
+ }
+
+ /**
++ * batadv_tp_list_active() - check if session from/to destination is ongoing
++ * @bat_priv: the bat priv with all the mesh interface information
++ * @dst: the other endpoint MAC address to look for
++ *
++ * Return: if matching session with @dst was found
++ */
++static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst)
++ __must_hold(&bat_priv->tp_list_lock)
++{
++ struct batadv_tp_vars *tp_vars;
++
++ hlist_for_each_entry_rcu(tp_vars, &bat_priv->tp_list, list) {
++ if (batadv_compare_eth(tp_vars->other_end, dst))
++ return true;
++ }
++
++ return false;
++}
++
++/**
+ * batadv_tp_list_find_session() - find tp_vars session object in the global
+ * list
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @dst: the other endpoint MAC address to look for
+ * @session: session identifier
++ * @role: role of the session
+ *
+ * Look for a tp_vars object matching dst as end_point, session as tp meter
+ * session and return it after having increment the refcounter. Return NULL
+@@ -301,7 +327,7 @@ static struct batadv_tp_vars *batadv_tp_
+ */
+ static struct batadv_tp_vars *
+ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
+- const u8 *session)
++ const u8 *session, enum batadv_tp_meter_role role)
+ {
+ struct batadv_tp_vars *pos, *tp_vars = NULL;
+
+@@ -313,6 +339,9 @@ batadv_tp_list_find_session(struct batad
+ if (memcmp(pos->session, session, sizeof(pos->session)) != 0)
+ continue;
+
++ if (pos->role != role)
++ continue;
++
+ /* most of the time this function is invoked during the normal
+ * process..it makes sense to pay more when the session is
+ * finished and to speed the process up during the measurement
+@@ -665,13 +694,10 @@ static void batadv_tp_recv_ack(struct ba
+
+ /* find the tp_vars */
+ tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+- icmp->session);
++ icmp->session, BATADV_TP_SENDER);
+ if (unlikely(!tp_vars))
+ return;
+
+- if (unlikely(tp_vars->role != BATADV_TP_SENDER))
+- goto out;
+-
+ if (unlikely(batadv_tp_sender_stopped(tp_vars)))
+ goto out;
+
+@@ -980,10 +1006,8 @@ void batadv_tp_start(struct batadv_priv
+ return;
+ }
+
+- tp_vars = batadv_tp_list_find(bat_priv, dst);
+- if (tp_vars) {
++ if (batadv_tp_list_active(bat_priv, dst)) {
+ spin_unlock_bh(&bat_priv->tp_list_lock);
+- batadv_tp_vars_put(tp_vars);
+ batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+ "Meter: test to or from the same node already ongoing, aborting\n");
+ batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING,
+@@ -1104,18 +1128,14 @@ void batadv_tp_stop(struct batadv_priv *
+ if (!orig_node)
+ return;
+
+- tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig);
++ tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig, BATADV_TP_SENDER);
+ if (!tp_vars) {
+ batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+ "Meter: trying to interrupt an already over connection\n");
+ goto out_put_orig_node;
+ }
+
+- if (unlikely(tp_vars->role != BATADV_TP_SENDER))
+- goto out_put_tp_vars;
+-
+ batadv_tp_sender_shutdown(tp_vars, return_value);
+-out_put_tp_vars:
+ batadv_tp_vars_put(tp_vars);
+ out_put_orig_node:
+ batadv_orig_node_put(orig_node);
+@@ -1371,7 +1391,7 @@ batadv_tp_init_recv(struct batadv_priv *
+ goto out_unlock;
+
+ tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+- icmp->session);
++ icmp->session, BATADV_TP_RECEIVER);
+ if (tp_vars)
+ goto out_unlock;
+
+@@ -1442,7 +1462,7 @@ static void batadv_tp_recv_msg(struct ba
+ }
+ } else {
+ tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
+- icmp->session);
++ icmp->session, BATADV_TP_RECEIVER);
+ if (!tp_vars) {
+ batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+ "Unexpected packet from %pM!\n",
+@@ -1451,13 +1471,6 @@ static void batadv_tp_recv_msg(struct ba
+ }
+ }
+
+- if (unlikely(tp_vars->role != BATADV_TP_RECEIVER)) {
+- batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+- "Meter: dropping packet: not expected (role=%u)\n",
+- tp_vars->role);
+- goto out;
+- }
+-
+ tp_vars->last_recv_time = jiffies;
+
+ /* if the packet is a duplicate, it may be the case that an ACK has been
--- /dev/null
+From 6c65cf23d4c6170fcf5714c32aa64689718cb142 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Wed, 13 May 2026 09:01:35 +0200
+Subject: batman-adv: tp_meter: avoid use of uninit sender vars
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 6c65cf23d4c6170fcf5714c32aa64689718cb142 upstream.
+
+batadv_tp_recv_ack() and batadv_tp_stop() are only valid for tp_vars in the
+BATADV_TP_SENDER role. When called with a BATADV_TP_RECEIVER role, it
+proceeds to read sender-only members that were never initialized, leading
+to undefined behavior.
+
+This can be triggered when a node that is currently acting as a receiver in
+an ongoing tp_meter session receives a malicious ACK packet.
+
+Guard against this by checking tp_vars->role immediately after the
+lookup and bailing out if it is not BATADV_TP_SENDER, before any of
+those members are accessed.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/tp_meter.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -663,6 +663,9 @@ static void batadv_tp_recv_ack(struct ba
+ if (unlikely(!tp_vars))
+ return;
+
++ if (unlikely(tp_vars->role != BATADV_TP_SENDER))
++ goto out;
++
+ if (unlikely(atomic_read(&tp_vars->sending) == 0))
+ goto out;
+
+@@ -1100,12 +1103,16 @@ void batadv_tp_stop(struct batadv_priv *
+ if (!tp_vars) {
+ batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+ "Meter: trying to interrupt an already over connection\n");
+- goto out;
++ goto out_put_orig_node;
+ }
+
++ if (unlikely(tp_vars->role != BATADV_TP_SENDER))
++ goto out_put_tp_vars;
++
+ batadv_tp_sender_shutdown(tp_vars, return_value);
++out_put_tp_vars:
+ batadv_tp_vars_put(tp_vars);
+-out:
++out_put_orig_node:
+ batadv_orig_node_put(orig_node);
+ }
+
--- /dev/null
+From d5487249a81ea658717614009c8f46acc5b7101a Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Wed, 13 May 2026 10:43:54 +0200
+Subject: batman-adv: tp_meter: directly shut down timer on cleanup
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit d5487249a81ea658717614009c8f46acc5b7101a upstream.
+
+batadv_tp_sender_cleanup() was calling timer_delete_sync() followed by
+timer_delete() to guard against the timer handler re-arming itself between
+the two calls. This double-deletion hack relied on the sending status being
+set to 0 to suppress re-arming.
+
+Replace both calls with a single timer_shutdown_sync(). This function both
+waits for any running timer callback to complete (like timer_delete_sync())
+and permanently disarms the timer so it cannot be re-armed afterwards,
+making re-arming prevention unconditional and self-documenting.
+
+The re-arming property is also required because otherwise:
+
+1. context 0 (batadv_tp_recv_ack()) checks in
+ batadv_tp_reset_sender_timer() if sending is still 1 -> it is
+2. context 1 changes in batadv_tp_sender_shutdown() sending to 0 and in
+ this process forces the kthread to stop timer in
+ batadv_tp_sender_cleanup()
+3. context 0 continues in batadv_tp_reset_sender_timer() and rearms the
+ timer -> but the reference for it is already gone
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/tp_meter.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -400,13 +400,7 @@ static void batadv_tp_sender_cleanup(str
+ batadv_tp_list_detach(tp_vars);
+
+ /* kill the timer and remove its reference */
+- timer_delete_sync(&tp_vars->timer);
+- /* the worker might have rearmed itself therefore we kill it again. Note
+- * that if the worker should run again before invoking the following
+- * timer_delete(), it would not re-arm itself once again because the status
+- * is OFF now
+- */
+- timer_delete(&tp_vars->timer);
++ timer_shutdown_sync(&tp_vars->timer);
+ batadv_tp_vars_put(tp_vars);
+ }
+
--- /dev/null
+From 71dce47f0758537fff78fddb5fb0d4632d29b29f Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Wed, 13 May 2026 23:38:54 +0200
+Subject: batman-adv: tp_meter: fix race condition in send error reporting
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 71dce47f0758537fff78fddb5fb0d4632d29b29f upstream.
+
+batadv_tp_sender_shutdown() previously used two separate variables to track
+session state: sending (an atomic flag indicating whether the session was
+active) and reason (a plain enum storing the stop reason). This introduced
+a race window between the two writes: after sending was cleared to 0,
+batadv_tp_send() could observe the stopped state and call
+batadv_tp_sender_end() before reason was written, causing the wrong stop
+reason to be reported to the caller.
+
+Fix this by consolidating both variables into a single atomic send_result,
+which holds 0 while the session is running and the stop reason once it
+ends.
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/tp_meter.c | 40 +++++++++++++++++++++++++---------------
+ net/batman-adv/types.h | 10 +++++-----
+ 2 files changed, 30 insertions(+), 20 deletions(-)
+
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -413,11 +413,14 @@ static void batadv_tp_sender_cleanup(str
+ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
+ struct batadv_tp_vars *tp_vars)
+ {
++ enum batadv_tp_meter_reason reason;
+ u32 session_cookie;
+
++ reason = atomic_read(&tp_vars->send_result);
++
+ batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+ "Test towards %pM finished..shutting down (reason=%d)\n",
+- tp_vars->other_end, tp_vars->reason);
++ tp_vars->other_end, reason);
+
+ batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+ "Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n",
+@@ -430,7 +433,7 @@ static void batadv_tp_sender_end(struct
+ session_cookie = batadv_tp_session_cookie(tp_vars->session,
+ tp_vars->icmp_uid);
+
+- batadv_tp_batctl_notify(tp_vars->reason,
++ batadv_tp_batctl_notify(reason,
+ tp_vars->other_end,
+ bat_priv,
+ tp_vars->start_time,
+@@ -446,10 +449,18 @@ static void batadv_tp_sender_end(struct
+ static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars,
+ enum batadv_tp_meter_reason reason)
+ {
+- if (atomic_xchg(&tp_vars->sending, 0) != 1)
+- return;
++ atomic_cmpxchg(&tp_vars->send_result, 0, reason);
++}
+
+- tp_vars->reason = reason;
++/**
++ * batadv_tp_sender_stopped() - check if tp session was stopped with reason
++ * @tp_vars: the private data of the current TP meter session
++ *
++ * Return: whether stop reason was found
++ */
++static bool batadv_tp_sender_stopped(struct batadv_tp_vars *tp_vars)
++{
++ return atomic_read(&tp_vars->send_result) != 0;
+ }
+
+ /**
+@@ -479,7 +490,7 @@ static void batadv_tp_reset_sender_timer
+ /* most of the time this function is invoked while normal packet
+ * reception...
+ */
+- if (unlikely(atomic_read(&tp_vars->sending) == 0))
++ if (unlikely(batadv_tp_sender_stopped(tp_vars)))
+ /* timer ref will be dropped in batadv_tp_sender_cleanup */
+ return;
+
+@@ -499,7 +510,7 @@ static void batadv_tp_sender_timeout(str
+ struct batadv_tp_vars *tp_vars = timer_container_of(tp_vars, t, timer);
+ struct batadv_priv *bat_priv = tp_vars->bat_priv;
+
+- if (atomic_read(&tp_vars->sending) == 0)
++ if (batadv_tp_sender_stopped(tp_vars))
+ return;
+
+ /* if the user waited long enough...shutdown the test */
+@@ -661,7 +672,7 @@ static void batadv_tp_recv_ack(struct ba
+ if (unlikely(tp_vars->role != BATADV_TP_SENDER))
+ goto out;
+
+- if (unlikely(atomic_read(&tp_vars->sending) == 0))
++ if (unlikely(batadv_tp_sender_stopped(tp_vars)))
+ goto out;
+
+ /* old ACK? silently drop it.. */
+@@ -827,21 +838,21 @@ static int batadv_tp_send(void *arg)
+
+ if (unlikely(tp_vars->role != BATADV_TP_SENDER)) {
+ err = BATADV_TP_REASON_DST_UNREACHABLE;
+- tp_vars->reason = err;
++ batadv_tp_sender_shutdown(tp_vars, err);
+ goto out;
+ }
+
+ orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end);
+ if (unlikely(!orig_node)) {
+ err = BATADV_TP_REASON_DST_UNREACHABLE;
+- tp_vars->reason = err;
++ batadv_tp_sender_shutdown(tp_vars, err);
+ goto out;
+ }
+
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (unlikely(!primary_if)) {
+ err = BATADV_TP_REASON_DST_UNREACHABLE;
+- tp_vars->reason = err;
++ batadv_tp_sender_shutdown(tp_vars, err);
+ goto out;
+ }
+
+@@ -860,7 +871,7 @@ static int batadv_tp_send(void *arg)
+ queue_delayed_work(batadv_event_workqueue, &tp_vars->finish_work,
+ msecs_to_jiffies(tp_vars->test_length));
+
+- while (atomic_read(&tp_vars->sending) != 0) {
++ while (!batadv_tp_sender_stopped(tp_vars)) {
+ if (unlikely(!batadv_tp_avail(tp_vars, payload_len))) {
+ batadv_tp_wait_available(tp_vars, payload_len);
+ continue;
+@@ -883,8 +894,7 @@ static int batadv_tp_send(void *arg)
+ "Meter: %s() cannot send packets (%d)\n",
+ __func__, err);
+ /* ensure nobody else tries to stop the thread now */
+- if (atomic_xchg(&tp_vars->sending, 0) == 1)
+- tp_vars->reason = err;
++ batadv_tp_sender_shutdown(tp_vars, err);
+ break;
+ }
+
+@@ -1006,7 +1016,7 @@ void batadv_tp_start(struct batadv_priv
+ ether_addr_copy(tp_vars->other_end, dst);
+ kref_init(&tp_vars->refcount);
+ tp_vars->role = BATADV_TP_SENDER;
+- atomic_set(&tp_vars->sending, 1);
++ atomic_set(&tp_vars->send_result, 0);
+ memcpy(tp_vars->session, session_id, sizeof(session_id));
+ tp_vars->icmp_uid = icmp_uid;
+
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1329,15 +1329,15 @@ struct batadv_tp_vars {
+ /** @role: receiver/sender modi */
+ enum batadv_tp_meter_role role;
+
+- /** @sending: sending binary semaphore: 1 if sending, 0 is not */
+- atomic_t sending;
++ /**
++ * @send_result: 0 when sending is ongoing and otherwise
++ * enum batadv_tp_meter_reason
++ */
++ atomic_t send_result;
+
+ /** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */
+ atomic_t receiving;
+
+- /** @reason: reason for a stopped session */
+- enum batadv_tp_meter_reason reason;
+-
+ /** @finish_work: work item for the finishing procedure */
+ struct delayed_work finish_work;
+
--- /dev/null
+From 77098e4bea37af51d3962efa88a5af2ea5e1ac57 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sun, 10 May 2026 11:31:03 +0200
+Subject: batman-adv: tp_meter: fix tp_vars reference leak in receiver shutdown
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 77098e4bea37af51d3962efa88a5af2ea5e1ac57 upstream.
+
+The receiver shutdown timer handler, batadv_tp_receiver_shutdown(), is
+responsible for releasing the tp_vars reference it holds. However, the
+existing logic for coordinating this release with batadv_tp_stop_all() was
+flawed.
+
+timer_shutdown_sync() guarantees the timer will not fire again after it
+returns, but it returns non-zero only when the timer was pending at the
+time of the call. If the timer had already expired (and
+batadv_tp_stop_all() would unsucessfully try to rearm itself),
+batadv_tp_stop_all() skips its batadv_tp_vars_put(), and
+batadv_tp_receiver_shutdown() fails to put its own reference as well.
+
+Fix this by introducing a new atomic variable receiving that is set to 1
+when the receiver is initialized and cleared atomically with atomic_xchg()
+by whichever side claims it first. Only the side that observes the
+transition from 1 to 0 is responsible for releasing the tp_vars timer
+reference, eliminating the uncertainty.
+
+Cc: stable@kernel.org
+Fixes: 3d3cf6a7314a ("batman-adv: stop tp_meter sessions during mesh teardown")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/tp_meter.c | 13 +++++++++++--
+ net/batman-adv/types.h | 3 +++
+ 2 files changed, 14 insertions(+), 2 deletions(-)
+
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -8,6 +8,7 @@
+ #include "main.h"
+
+ #include <linux/atomic.h>
++#include <linux/bug.h>
+ #include <linux/build_bug.h>
+ #include <linux/byteorder/generic.h>
+ #include <linux/cache.h>
+@@ -1157,6 +1158,9 @@ static void batadv_tp_receiver_shutdown(
+ spin_unlock_bh(&tp_vars->unacked_lock);
+
+ /* drop reference of timer */
++ if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1))
++ return;
++
+ batadv_tp_vars_put(tp_vars);
+ }
+
+@@ -1375,6 +1379,7 @@ batadv_tp_init_recv(struct batadv_priv *
+
+ ether_addr_copy(tp_vars->other_end, icmp->orig);
+ tp_vars->role = BATADV_TP_RECEIVER;
++ atomic_set(&tp_vars->receiving, 1);
+ memcpy(tp_vars->session, icmp->session, sizeof(tp_vars->session));
+ tp_vars->last_recv = BATADV_TP_FIRST_SEQ;
+ tp_vars->bat_priv = bat_priv;
+@@ -1547,8 +1552,12 @@ void batadv_tp_stop_all(struct batadv_pr
+ break;
+ case BATADV_TP_RECEIVER:
+ batadv_tp_list_detach(tp_var);
+- if (timer_shutdown_sync(&tp_var->timer))
+- batadv_tp_vars_put(tp_var);
++ timer_shutdown_sync(&tp_var->timer);
++
++ if (atomic_xchg(&tp_var->receiving, 0) != 1)
++ break;
++
++ batadv_tp_vars_put(tp_var);
+ break;
+ }
+
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -1332,6 +1332,9 @@ struct batadv_tp_vars {
+ /** @sending: sending binary semaphore: 1 if sending, 0 is not */
+ atomic_t sending;
+
++ /** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */
++ atomic_t receiving;
++
+ /** @reason: reason for a stopped session */
+ enum batadv_tp_meter_reason reason;
+
--- /dev/null
+From fa1bd704940b5bcbc32c0b28db9167405c8ee5e0 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 2 May 2026 20:47:34 +0200
+Subject: batman-adv: tt: avoid empty VLAN responses
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit fa1bd704940b5bcbc32c0b28db9167405c8ee5e0 upstream.
+
+The commit 16116dac2339 ("batman-adv: prevent TT request storms by not
+sending inconsistent TT TLVLs") added checks to the local (direct) TT
+response code. But the response can also be done indirectly by another node
+using the global TT state. To avoid such inconsistency states reported in
+the original fix, also avoid sending empty VLANs for replies from the
+global TT state.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/translation-table.c | 20 +++++++++++++++++---
+ 1 file changed, 17 insertions(+), 3 deletions(-)
+
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -797,24 +797,26 @@ batadv_tt_prepare_tvlv_global_data(struc
+ s32 *tt_len)
+ {
+ u16 num_vlan = 0;
+- u16 num_entries = 0;
+ u16 tvlv_len = 0;
+ unsigned int change_offset;
+ struct batadv_tvlv_tt_vlan_data *tt_vlan;
+ struct batadv_orig_node_vlan *vlan;
++ u16 total_entries = 0;
+ u8 *tt_change_ptr;
++ int vlan_entries;
+
+ spin_lock_bh(&orig_node->vlan_list_lock);
+ hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++ vlan_entries = atomic_read(&vlan->tt.num_entries);
++ total_entries += vlan_entries;
+ num_vlan++;
+- num_entries += atomic_read(&vlan->tt.num_entries);
+ }
+
+ change_offset = struct_size(*tt_data, vlan_data, num_vlan);
+
+ /* if tt_len is negative, allocate the space needed by the full table */
+ if (*tt_len < 0)
+- *tt_len = batadv_tt_len(num_entries);
++ *tt_len = batadv_tt_len(total_entries);
+
+ if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) {
+ *tt_len = 0;
+@@ -835,14 +837,26 @@ batadv_tt_prepare_tvlv_global_data(struc
+ (*tt_data)->num_vlan = htons(num_vlan);
+
+ tt_vlan = (*tt_data)->vlan_data;
++ num_vlan = 0;
+ hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
++ vlan_entries = atomic_read(&vlan->tt.num_entries);
++ if (vlan_entries < 1)
++ continue;
++
+ tt_vlan->vid = htons(vlan->vid);
+ tt_vlan->crc = htonl(vlan->tt.crc);
+ tt_vlan->reserved = 0;
+
+ tt_vlan++;
++ num_vlan++;
+ }
+
++ /* recalculate in case number of VLANs reduced */
++ change_offset = struct_size(*tt_data, vlan_data, num_vlan);
++ tvlv_len = *tt_len + change_offset;
++
++ (*tt_data)->num_vlan = htons(num_vlan);
++
+ tt_change_ptr = (u8 *)*tt_data + change_offset;
+ *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+
--- /dev/null
+From fc92cdfcb295cefa4344d71a527d61b638b7bfc4 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 2 May 2026 19:53:21 +0200
+Subject: batman-adv: tt: fix negative last_changeset_len
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit fc92cdfcb295cefa4344d71a527d61b638b7bfc4 upstream.
+
+batadv_piv_tt::last_changeset_len len was declared as s16, but the field is
+never intended to hold a negative value. When a value greater than 32767 is
+assigned, it wraps to a negative signed integer.
+
+In batadv_send_my_tt_response(), last_changeset_len is temporarily widened
+to s32. The incorrectly negative s16 value propagates into the s32, causing
+batadv_tt_prepare_tvlv_local_data() to allocate a full sized buffer but
+populates only a small portion of it with the collected changeset. All
+remaining bits are kept uninitialized.
+
+Using an u16 avoids this type confusion and ensures that no (negative) sign
+extension is performed in batadv_send_my_tt_response().
+
+Cc: stable@kernel.org
+Fixes: a73105b8d4c7 ("batman-adv: improved client announcement mechanism")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/types.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -996,7 +996,7 @@ struct batadv_priv_tt {
+ * @last_changeset_len: length of last tt changeset this host has
+ * generated
+ */
+- s16 last_changeset_len;
++ u16 last_changeset_len;
+
+ /**
+ * @last_changeset_lock: lock protecting last_changeset &
--- /dev/null
+From b64963a2ceeb7529310b6cf253a1e540784422f4 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 2 May 2026 19:53:21 +0200
+Subject: batman-adv: tt: fix negative tt_buff_len
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit b64963a2ceeb7529310b6cf253a1e540784422f4 upstream.
+
+batadv_orig_node::tt_buff_len was declared as s16, but the field is never
+intended to hold a negative value. When a value greater than 32767 is
+assigned, it wraps to a negative signed integer.
+
+In batadv_send_other_tt_response(), tt_buff_len is temporarily widened to
+s32. The incorrectly negative s16 value propagates into the s32, causing
+batadv_tt_prepare_tvlv_global_data() to allocate a full sized buffer but
+populates only a small portion of it with the collected changeset. All
+remaining bits are kept uninitialized.
+
+Using an u16 avoids this type confusion and ensures that no (negative) sign
+extension is performed in batadv_send_other_tt_response().
+
+Cc: stable@kernel.org
+Fixes: a73105b8d4c7 ("batman-adv: improved client announcement mechanism")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/types.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -455,7 +455,7 @@ struct batadv_orig_node {
+ * @tt_buff_len: length of the last tt changeset this node received
+ * from the orig node
+ */
+- s16 tt_buff_len;
++ u16 tt_buff_len;
+
+ /** @tt_buff_lock: lock that protects tt_buff and tt_buff_len */
+ spinlock_t tt_buff_lock;
--- /dev/null
+From 94d27005016be15ffc638b2ecbc4d58805ad7b48 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 2 May 2026 19:47:11 +0200
+Subject: batman-adv: tt: fix TOCTOU race for reported vlans
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 94d27005016be15ffc638b2ecbc4d58805ad7b48 upstream.
+
+The local TT based TVLV is generated by first checking the number of VLANs
+which have at least one TT entry. A new buffer with the correct size for
+the VLANs is then allocated. Only then, the list of VLANs s used to fill
+the VLAN entries in the buffer. During this time, the meshif_vlan_list_lock
+is held. But the actual number of TT entries of each VLAN can still
+increase during this time - just not the number of VLANs in the list.
+
+But the prefilter used in the buffer size calculation might still cause an
+increase of the number of VLANs which need to be stored. Simply because a
+VLAN might now suddenly have at least one entry when it had none in the
+pre-alloc check - and then needs to occupy space which was not allocated.
+
+It is better to overestimate the buffer size at the beginning and then fill
+the buffer only with the VLANs which are not empty.
+
+Cc: stable@kernel.org
+Fixes: 16116dac2339 ("batman-adv: prevent TT request storms by not sending inconsistent TT TLVLs")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/translation-table.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -887,11 +887,8 @@ batadv_tt_prepare_tvlv_local_data(struct
+ spin_lock_bh(&bat_priv->meshif_vlan_list_lock);
+ hlist_for_each_entry(vlan, &bat_priv->meshif_vlan_list, list) {
+ vlan_entries = atomic_read(&vlan->tt.num_entries);
+- if (vlan_entries < 1)
+- continue;
+-
+- num_vlan++;
+ total_entries += vlan_entries;
++ num_vlan++;
+ }
+
+ change_offset = struct_size(*tt_data, vlan_data, num_vlan);
+@@ -914,6 +911,7 @@ batadv_tt_prepare_tvlv_local_data(struct
+ (*tt_data)->num_vlan = htons(num_vlan);
+
+ tt_vlan = (*tt_data)->vlan_data;
++ num_vlan = 0;
+ hlist_for_each_entry(vlan, &bat_priv->meshif_vlan_list, list) {
+ vlan_entries = atomic_read(&vlan->tt.num_entries);
+ if (vlan_entries < 1)
+@@ -924,8 +922,15 @@ batadv_tt_prepare_tvlv_local_data(struct
+ tt_vlan->reserved = 0;
+
+ tt_vlan++;
++ num_vlan++;
+ }
+
++ /* recalculate in case number of VLANs reduced */
++ change_offset = struct_size(*tt_data, vlan_data, num_vlan);
++ tvlv_len = *tt_len + change_offset;
++
++ (*tt_data)->num_vlan = htons(num_vlan);
++
+ tt_change_ptr = (u8 *)*tt_data + change_offset;
+ *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+
--- /dev/null
+From 99d9958fa10fb684b2a8e2c48a8d704122721420 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 2 May 2026 21:25:19 +0200
+Subject: batman-adv: tt: prevent TVLV entry number overflow
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 99d9958fa10fb684b2a8e2c48a8d704122721420 upstream.
+
+The helpers to prepare the buffers for the local and global TT based
+replies are trying to sum up all TT entries which can be found for each
+VLAN. In theory, this sum can be too big for an u16 and therefore overflow.
+A too small buffer would then be allocated for the TVLV.
+
+The too small buffer will be handled gracefully by
+batadv_tt_tvlv_generate() and is not causing a buffer overflow - just a
+truncated reply. But this overflow shouldn't have happened in the first and
+the too small buffer should never have been allocated when an overflow was
+detected.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/translation-table.c | 20 +++++++++++++++++---
+ 1 file changed, 17 insertions(+), 3 deletions(-)
+
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -804,11 +804,18 @@ batadv_tt_prepare_tvlv_global_data(struc
+ u16 total_entries = 0;
+ u8 *tt_change_ptr;
+ int vlan_entries;
++ u16 sum_entries;
+
+ spin_lock_bh(&orig_node->vlan_list_lock);
+ hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
+ vlan_entries = atomic_read(&vlan->tt.num_entries);
+- total_entries += vlan_entries;
++
++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) {
++ *tt_len = 0;
++ goto out;
++ }
++
++ total_entries = sum_entries;
+ num_vlan++;
+ }
+
+@@ -893,15 +900,22 @@ batadv_tt_prepare_tvlv_local_data(struct
+ struct batadv_meshif_vlan *vlan;
+ size_t change_offset;
+ u16 num_vlan = 0;
+- u16 vlan_entries = 0;
+ u16 total_entries = 0;
+ u16 tvlv_len;
+ u8 *tt_change_ptr;
++ int vlan_entries;
++ u16 sum_entries;
+
+ spin_lock_bh(&bat_priv->meshif_vlan_list_lock);
+ hlist_for_each_entry(vlan, &bat_priv->meshif_vlan_list, list) {
+ vlan_entries = atomic_read(&vlan->tt.num_entries);
+- total_entries += vlan_entries;
++
++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) {
++ tvlv_len = 0;
++ goto out;
++ }
++
++ total_entries = sum_entries;
+ num_vlan++;
+ }
+
--- /dev/null
+From 1e9fab756f8395096d5bba7be0c373c4c8f5d165 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 2 May 2026 19:08:37 +0200
+Subject: batman-adv: tt: reject oversized local TVLV buffers
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 1e9fab756f8395096d5bba7be0c373c4c8f5d165 upstream.
+
+The commit 3a359bf5c61d ("batman-adv: reject oversized global TT response
+buffers") added a check to ensure that a global return buffer size can be
+stored in an u16. The same buffer handling also exists for the local data
+buffer but was not touched.
+
+A similar check should be also be in place for the local TVLV buffer. It
+doesn't have the similar attack surface because it is only generated from
+locally discovered MAC addresses but the dynamic nature could still cause
+temporarily to large buffers.
+
+Cc: stable@kernel.org
+Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/translation-table.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -877,12 +877,12 @@ batadv_tt_prepare_tvlv_local_data(struct
+ {
+ struct batadv_tvlv_tt_vlan_data *tt_vlan;
+ struct batadv_meshif_vlan *vlan;
++ size_t change_offset;
+ u16 num_vlan = 0;
+ u16 vlan_entries = 0;
+ u16 total_entries = 0;
+ u16 tvlv_len;
+ u8 *tt_change_ptr;
+- int change_offset;
+
+ spin_lock_bh(&bat_priv->meshif_vlan_list_lock);
+ hlist_for_each_entry(vlan, &bat_priv->meshif_vlan_list, list) {
+@@ -897,8 +897,10 @@ batadv_tt_prepare_tvlv_local_data(struct
+ if (*tt_len < 0)
+ *tt_len = batadv_tt_len(total_entries);
+
+- tvlv_len = *tt_len;
+- tvlv_len += change_offset;
++ if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) {
++ tvlv_len = 0;
++ goto out;
++ }
+
+ *tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
+ if (!*tt_data) {
--- /dev/null
+From 501368506563e151b322c8c3f228b796e615b90d Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Thu, 14 May 2026 16:33:12 +0200
+Subject: batman-adv: tvlv: abort OGM send on tvlv append failure
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit 501368506563e151b322c8c3f228b796e615b90d upstream.
+
+batadv_tvlv_container_ogm_append() could fail in two ways: a memory
+allocation failure when resizing the packet buffer, or the tvlv data
+exceeding U16_MAX bytes. In both cases the function previously returned the
+old (now stale) tvlv_value_len rather than signalling an error, causing the
+OGM/OGM2 send path to transmit a packet whose TVLV length field no longer
+matched the actual buffer contents. And because it also didn't fill in the
+new TVLV data, sending either uninitialized or corrupted data on the wire.
+
+All errors in batadv_tvlv_container_ogm_append() must be forwarded to the
+caller. And the caller must abort the send of the OGM2. For B.A.T.M.A.N.
+IV, it is currently not allowed to abort the send. The non-TVLV part of the
+OGM must be queued up instead.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/bat_iv_ogm.c | 16 +++++++++++++---
+ net/batman-adv/bat_v_ogm.c | 26 ++++++++++++++------------
+ net/batman-adv/tvlv.c | 17 ++++++++++++-----
+ net/batman-adv/tvlv.h | 2 +-
+ 4 files changed, 40 insertions(+), 21 deletions(-)
+
+--- a/net/batman-adv/bat_iv_ogm.c
++++ b/net/batman-adv/bat_iv_ogm.c
+@@ -790,6 +790,7 @@ static void batadv_iv_ogm_schedule_buff(
+ u32 seqno;
+ u16 tvlv_len = 0;
+ unsigned long send_time;
++ int ret;
+
+ lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
+
+@@ -813,9 +814,18 @@ static void batadv_iv_ogm_schedule_buff(
+ * appended as it may alter the tt tvlv container
+ */
+ batadv_tt_local_commit_changes(bat_priv);
+- tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
+- ogm_buff_len,
+- BATADV_OGM_HLEN);
++ ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++ ogm_buff_len,
++ BATADV_OGM_HLEN);
++ if (ret < 0) {
++ /* OGMs must be queued even when the buffer allocation for
++ * TVLVs failed. just fall back to the non-TVLV version
++ */
++ ret = 0;
++ *ogm_buff_len = BATADV_OGM_HLEN;
++ }
++
++ tvlv_len = ret;
+ }
+
+ batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -269,10 +269,10 @@ static void batadv_v_ogm_send_meshif(str
+ struct batadv_hard_iface *hard_iface;
+ struct batadv_ogm2_packet *ogm_packet;
+ struct sk_buff *skb, *skb_tmp;
+- unsigned char *ogm_buff;
++ unsigned char **ogm_buff;
+ struct list_head *iter;
+- int ogm_buff_len;
+- u16 tvlv_len = 0;
++ int *ogm_buff_len;
++ u16 tvlv_len;
+ int ret;
+
+ lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex);
+@@ -280,25 +280,27 @@ static void batadv_v_ogm_send_meshif(str
+ if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
+ goto out;
+
+- ogm_buff = bat_priv->bat_v.ogm_buff;
+- ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
++ ogm_buff = &bat_priv->bat_v.ogm_buff;
++ ogm_buff_len = &bat_priv->bat_v.ogm_buff_len;
++
+ /* tt changes have to be committed before the tvlv data is
+ * appended as it may alter the tt tvlv container
+ */
+ batadv_tt_local_commit_changes(bat_priv);
+- tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff,
+- &ogm_buff_len,
+- BATADV_OGM2_HLEN);
++ ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
++ ogm_buff_len,
++ BATADV_OGM2_HLEN);
++ if (ret < 0)
++ goto reschedule;
+
+- bat_priv->bat_v.ogm_buff = ogm_buff;
+- bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
++ tvlv_len = ret;
+
+- skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
++ skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len);
+ if (!skb)
+ goto reschedule;
+
+ skb_reserve(skb, ETH_HLEN);
+- skb_put_data(skb, ogm_buff, ogm_buff_len);
++ skb_put_data(skb, *ogm_buff, *ogm_buff_len);
+
+ ogm_packet = (struct batadv_ogm2_packet *)skb->data;
+ ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -8,6 +8,7 @@
+
+ #include <linux/byteorder/generic.h>
+ #include <linux/container_of.h>
++#include <linux/errno.h>
+ #include <linux/etherdevice.h>
+ #include <linux/gfp.h>
+ #include <linux/if_ether.h>
+@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_b
+ * The ogm packet might be enlarged or shrunk depending on the current size
+ * and the size of the to-be-appended tvlv containers.
+ *
+- * Return: size of all appended tvlv containers in bytes.
++ * Return: size of all appended tvlv containers in bytes (max U16_MAX), negative
++ * if operation failed
+ */
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ unsigned char **packet_buff,
+ int *packet_buff_len, int packet_min_len)
+ {
+@@ -316,6 +318,7 @@ u16 batadv_tvlv_container_ogm_append(str
+ struct batadv_tvlv_hdr *tvlv_hdr;
+ u16 tvlv_value_len;
+ void *tvlv_value;
++ int tvlv_len_ret;
+ bool ret;
+
+ spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+@@ -323,9 +326,12 @@ u16 batadv_tvlv_container_ogm_append(str
+
+ ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+ packet_min_len, tvlv_value_len);
+-
+- if (!ret)
++ if (!ret) {
++ tvlv_len_ret = -ENOMEM;
+ goto end;
++ }
++
++ tvlv_len_ret = tvlv_value_len;
+
+ if (!tvlv_value_len)
+ goto end;
+@@ -344,7 +350,8 @@ u16 batadv_tvlv_container_ogm_append(str
+
+ end:
+ spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+- return tvlv_value_len;
++
++ return tvlv_len_ret;
+ }
+
+ /**
+--- a/net/batman-adv/tvlv.h
++++ b/net/batman-adv/tvlv.h
+@@ -16,7 +16,7 @@
+ void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
+ u8 type, u8 version,
+ void *tvlv_value, u16 tvlv_value_len);
+-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ unsigned char **packet_buff,
+ int *packet_buff_len, int packet_min_len);
+ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
--- /dev/null
+From f50487e3566358b2b982b7801945e858c78ad9ab Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 9 May 2026 21:55:29 +0200
+Subject: batman-adv: tvlv: reject oversized TVLV packets
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f50487e3566358b2b982b7801945e858c78ad9ab upstream.
+
+batadv_tvlv_container_ogm_append() builds a TVLV packet section from
+the tvlv.container_list. The total size of this section is computed by
+batadv_tvlv_container_list_size(), which sums the sizes of all registered
+containers.
+
+The return type and accumulator in batadv_tvlv_container_list_size() were
+u16. If the accumulated size exceeds U16_MAX, the value wraps around,
+causing the subsequent allocation in batadv_tvlv_container_ogm_append()
+to be undersized. The memcpy-style copy that follows would then write
+beyond the end of the allocated buffer, corrupting kernel memory.
+
+Fix this by widening the return type of batadv_tvlv_container_list_size()
+to size_t. In batadv_tvlv_container_ogm_append(), check the computed length
+against U16_MAX before proceeding, and bail out as if the allocation had
+failed when the limit is exceeded.
+
+Cc: stable@kernel.org
+Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/tvlv.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+--- a/net/batman-adv/tvlv.c
++++ b/net/batman-adv/tvlv.c
+@@ -13,6 +13,7 @@
+ #include <linux/gfp.h>
+ #include <linux/if_ether.h>
+ #include <linux/kref.h>
++#include <linux/limits.h>
+ #include <linux/list.h>
+ #include <linux/lockdep.h>
+ #include <linux/netdevice.h>
+@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_
+ *
+ * Return: size of all currently registered tvlv containers in bytes.
+ */
+-static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
++static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
+ {
+ struct batadv_tvlv_container *tvlv;
+- u16 tvlv_len = 0;
++ size_t tvlv_len = 0;
+
+ lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
+
+@@ -316,13 +317,17 @@ int batadv_tvlv_container_ogm_append(str
+ {
+ struct batadv_tvlv_container *tvlv;
+ struct batadv_tvlv_hdr *tvlv_hdr;
+- u16 tvlv_value_len;
++ size_t tvlv_value_len;
+ void *tvlv_value;
+ int tvlv_len_ret;
+ bool ret;
+
+ spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+ tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
++ if (tvlv_value_len > U16_MAX) {
++ tvlv_len_ret = -E2BIG;
++ goto end;
++ }
+
+ ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+ packet_min_len, tvlv_value_len);
--- /dev/null
+From f8ce8b8331a1bc44ad4905886a482214d428b253 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 9 May 2026 22:44:12 +0200
+Subject: batman-adv: v: stop OGMv2 on disabled interface
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit f8ce8b8331a1bc44ad4905886a482214d428b253 upstream.
+
+When a batadv_hard_iface is disabled, its mesh_iface pointer is set to
+NULL. However, batadv_v_ogm_send_meshif() may still dispatch OGMs via
+batadv_v_ogm_queue_on_if() for interfaces that have since lost their
+mesh_iface association. This results in a NULL pointer dereference when
+batadv_v_ogm_queue_on_if() unconditionally calls netdev_priv() on the
+now NULL hard_iface->mesh_iface to retrieve the batadv_priv.
+
+It is necessary to ensure that the batadv_v_ogm_queue_on_if() checks that
+it is using the same mesh_iface for which batadv_v_ogm_send_meshif() was
+called.
+
+Cc: stable@kernel.org
+Fixes: 0da0035942d4 ("batman-adv: OGMv2 - add basic infrastructure")
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Reviewed-by: Yuan Tan <yuantan098@gmail.com>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------
+ 1 file changed, 21 insertions(+), 12 deletions(-)
+
+--- a/net/batman-adv/bat_v_ogm.c
++++ b/net/batman-adv/bat_v_ogm.c
+@@ -113,14 +113,14 @@ static void batadv_v_ogm_start_timer(str
+
+ /**
+ * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+ * @skb: the OGM to send
+ * @hard_iface: the interface to use to send the OGM
+ */
+-static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
++static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
++ struct sk_buff *skb,
+ struct batadv_hard_iface *hard_iface)
+ {
+- struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
+-
+ if (hard_iface->if_status != BATADV_IF_ACTIVE) {
+ kfree_skb(skb);
+ return;
+@@ -187,6 +187,7 @@ static void batadv_v_ogm_aggr_list_free(
+
+ /**
+ * batadv_v_ogm_aggr_send() - flush & send aggregation queue
++ * @bat_priv: the bat priv with all the mesh interface information
+ * @hard_iface: the interface with the aggregation queue to flush
+ *
+ * Aggregates all OGMv2 packets currently in the aggregation queue into a
+@@ -196,7 +197,8 @@ static void batadv_v_ogm_aggr_list_free(
+ *
+ * Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
+ */
+-static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
++static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
++ struct batadv_hard_iface *hard_iface)
+ {
+ unsigned int aggr_len = hard_iface->bat_v.aggr_len;
+ struct sk_buff *skb_aggr;
+@@ -226,27 +228,32 @@ static void batadv_v_ogm_aggr_send(struc
+ consume_skb(skb);
+ }
+
+- batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
++ batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
+ }
+
+ /**
+ * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
++ * @bat_priv: the bat priv with all the mesh interface information
+ * @skb: the OGM to queue
+ * @hard_iface: the interface to queue the OGM on
+ */
+-static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
++static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
++ struct sk_buff *skb,
+ struct batadv_hard_iface *hard_iface)
+ {
+- struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
++ if (hard_iface->mesh_iface != bat_priv->mesh_iface) {
++ kfree_skb(skb);
++ return;
++ }
+
+ if (!atomic_read(&bat_priv->aggregated_ogms)) {
+- batadv_v_ogm_send_to_if(skb, hard_iface);
++ batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
+ return;
+ }
+
+ spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+ if (!batadv_v_ogm_queue_left(skb, hard_iface))
+- batadv_v_ogm_aggr_send(hard_iface);
++ batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+
+ hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
+ __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
+@@ -343,7 +350,7 @@ static void batadv_v_ogm_send_meshif(str
+ break;
+ }
+
+- batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
++ batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
+ batadv_hardif_put(hard_iface);
+ }
+ rcu_read_unlock();
+@@ -383,12 +390,14 @@ void batadv_v_ogm_aggr_work(struct work_
+ {
+ struct batadv_hard_iface_bat_v *batv;
+ struct batadv_hard_iface *hard_iface;
++ struct batadv_priv *bat_priv;
+
+ batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
+ hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
++ bat_priv = netdev_priv(hard_iface->mesh_iface);
+
+ spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
+- batadv_v_ogm_aggr_send(hard_iface);
++ batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+ spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
+
+ batadv_v_ogm_start_queue_timer(hard_iface);
+@@ -578,7 +587,7 @@ static void batadv_v_ogm_forward(struct
+ if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
+ ogm_forward->ttl, if_incoming->net_dev->name);
+
+- batadv_v_ogm_queue_on_if(skb, if_outgoing);
++ batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
+
+ out:
+ batadv_orig_ifinfo_put(orig_ifinfo);
--- /dev/null
+From c68337442f03953237a94577beb468ab2662a851 Mon Sep 17 00:00:00 2001
+From: Zhihao Cheng <chengzhihao1@huawei.com>
+Date: Tue, 19 May 2026 17:18:05 +0800
+Subject: cifs: Fix busy dentry used after unmounting
+
+From: Zhihao Cheng <chengzhihao1@huawei.com>
+
+commit c68337442f03953237a94577beb468ab2662a851 upstream.
+
+Since commit 340cea84f691c ("cifs: open files should not hold ref on
+superblock"), cifs file only holds the dentry ref_cnt, the cifs file
+close work(cfile->deferred) could be executed after unmounting, which
+will trigger a warning in generic_shutdown_super:
+ BUG: Dentry 00000000a14a6845{i=c,n=file} still in use (1) [unmount of
+ cifs cifs]
+
+The detailed processs is:
+ process A process B kworker
+ fd = open(PATH)
+ vfs_open
+ file->__f_path = *path // dentry->d_lockref.count = 1
+ cifs_open
+ cifs_new_fileinfo
+ cfile->dentry = dget(dentry) // dentry->d_lockref.count = 2
+ close(fd)
+ __fput
+ cifs_close
+ queue_delayed_work(deferredclose_wq, cfile->deferred)
+ dput(dentry) // dentry->d_lockref.count = 1
+ smb2_deferred_work_close
+ _cifsFileInfo_put
+ list_del(&cifs_file->flist)
+ umount
+ cleanup_mnt
+ deactivate_super
+ cifs_kill_sb
+ cifs_close_all_deferred_files_sb
+ cifs_close_all_deferred_files
+ // cannot find cfile, skip _cifsFileInfo_put
+ kill_anon_super
+ generic_shutdown_super
+ shrink_dcache_for_umount
+ umount_check
+ WARN ! // dentry->d_lockref.count = 1
+ cifsFileInfo_put_final
+ dput(cifs_file->dentry)
+ // dentry->d_lockref.count = 0
+
+Fix it by flushing 'deferredclose_wq' before calling kill_anon_super.
+
+Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=221548.
+
+Fixes: 340cea84f691c ("cifs: open files should not hold ref on superblock")
+Cc: stable@vger.kernel.org
+Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/cifsfs.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/smb/client/cifsfs.c
++++ b/fs/smb/client/cifsfs.c
+@@ -338,6 +338,8 @@ static void cifs_kill_sb(struct super_bl
+
+ /* Wait for all pending oplock breaks to complete */
+ flush_workqueue(cifsoplockd_wq);
++ /* Wait for all opened files to release */
++ flush_workqueue(deferredclose_wq);
+
+ /* finally release root dentry */
+ dput(cifs_sb->root);
--- /dev/null
+From 215c90ee656114f5e8c32408228d97082f8e0eef Mon Sep 17 00:00:00 2001
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Date: Wed, 6 May 2026 13:57:00 +0200
+Subject: device property: set fwnode->secondary to NULL in fwnode_init()
+
+From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+
+commit 215c90ee656114f5e8c32408228d97082f8e0eef upstream.
+
+If a firmware node is allocated on the stack (for instance: temporary
+software node whose life-time we control) or on the heap - but using a
+non-zeroing allocation function - and initialized using fwnode_init(),
+its secondary pointer will contain uninitalized memory which likely will
+be neither NULL nor IS_ERR() and so may end up being dereferenced (for
+example: in dev_to_swnode()). Set fwnode->secondary to NULL on
+initialization.
+
+Cc: stable <stable@kernel.org>
+Fixes: 01bb86b380a3 ("driver core: Add fwnode_init()")
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Reviewed-by: Rafael J. Wysocki (Intel) <rafael@kernel.org>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Link: https://patch.msgid.link/20260506115701.23035-1-bartosz.golaszewski@oss.qualcomm.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/fwnode.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/linux/fwnode.h
++++ b/include/linux/fwnode.h
+@@ -208,6 +208,7 @@ struct fwnode_operations {
+ static inline void fwnode_init(struct fwnode_handle *fwnode,
+ const struct fwnode_operations *ops)
+ {
++ fwnode->secondary = NULL;
+ fwnode->ops = ops;
+ INIT_LIST_HEAD(&fwnode->consumers);
+ INIT_LIST_HEAD(&fwnode->suppliers);
--- /dev/null
+From cd86529ec61474a38c3837fb7823790a7c3f8cce Mon Sep 17 00:00:00 2001
+From: Harry Wentland <harry.wentland@amd.com>
+Date: Mon, 4 May 2026 11:14:45 -0400
+Subject: drm/amd/display: Fix integer overflow in bios_get_image()
+
+From: Harry Wentland <harry.wentland@amd.com>
+
+commit cd86529ec61474a38c3837fb7823790a7c3f8cce upstream.
+
+[Why&How]
+The bounds check in bios_get_image() computes 'offset + size' using
+unsigned 32-bit arithmetic before comparing against bios_size. If a
+VBIOS image contains a near-UINT32_MAX offset the addition wraps to a
+small value, the comparison passes, and the function returns a wild
+pointer past the VBIOS mapping.
+
+Additionally, the comparison uses '<' (strict), which incorrectly
+rejects the valid exact-fit case where offset + size == bios_size.
+
+Fix both issues by restructuring the check to avoid the addition
+entirely: first reject if offset alone exceeds bios_size, then check
+size against the remaining space (bios_size - offset). This eliminates
+the overflow and correctly permits exact-fit accesses.
+
+Assisted-by: GitHub Copilot:claude-opus-4.6
+Reviewed-by: Alex Hung <alex.hung@amd.com>
+Signed-off-by: Harry Wentland <harry.wentland@amd.com>
+Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
+Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit d40fb392af659c4a02b560319f226842f6ec1a95)
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c
++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c
+@@ -37,10 +37,13 @@ uint8_t *bios_get_image(struct dc_bios *
+ uint32_t offset,
+ uint32_t size)
+ {
+- if (bp->bios && offset + size < bp->bios_size)
+- return bp->bios + offset;
+- else
++ if (!bp->bios)
+ return NULL;
++
++ if (offset > bp->bios_size || size > bp->bios_size - offset)
++ return NULL;
++
++ return bp->bios + offset;
+ }
+
+ #include "reg_helper.h"
--- /dev/null
+From 86d2b20644b11d21fe52c596e6e922b4590a3e3f Mon Sep 17 00:00:00 2001
+From: Harry Wentland <harry.wentland@amd.com>
+Date: Mon, 4 May 2026 16:14:11 -0400
+Subject: drm/amd/display: Validate GPIO pin LUT table size before iterating
+
+From: Harry Wentland <harry.wentland@amd.com>
+
+commit 86d2b20644b11d21fe52c596e6e922b4590a3e3f upstream.
+
+[Why&How]
+The GPIO pin table parsers in get_gpio_i2c_info() and
+bios_parser_get_gpio_pin_info() derive an element count from the VBIOS
+table_header.structuresize field, then iterate over gpio_pin[] entries.
+However, GET_IMAGE() only validates that the table header itself fits
+within the BIOS image. If the VBIOS reports a structuresize larger than
+the actual mapped data, the loop reads past the end of the BIOS image,
+causing an out-of-bounds read.
+
+Fix this by calling bios_get_image() to validate that the full claimed
+structuresize is accessible within the BIOS image before entering the
+loop in both functions.
+
+Assisted-by: GitHub Copilot:claude-opus-4-6
+Reviewed-by: Alex Hung <alex.hung@amd.com>
+Signed-off-by: Harry Wentland <harry.wentland@amd.com>
+Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
+Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit ba5e95b43b773ae1bf1f66ee6b31eb774e65afe3)
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c
++++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c
+@@ -492,6 +492,10 @@ static enum bp_result get_gpio_i2c_info(
+ - sizeof(struct atom_common_table_header))
+ / sizeof(struct atom_gpio_pin_assignment);
+
++ if (!bios_get_image(&bp->base, DATA_TABLES(gpio_pin_lut),
++ le16_to_cpu(header->table_header.structuresize)))
++ return BP_RESULT_BADBIOSTABLE;
++
+ pin = (struct atom_gpio_pin_assignment *) header->gpio_pin;
+
+ for (table_index = 0; table_index < count; table_index++) {
+@@ -680,6 +684,11 @@ static enum bp_result bios_parser_get_gp
+ count = (le16_to_cpu(header->table_header.structuresize)
+ - sizeof(struct atom_common_table_header))
+ / sizeof(struct atom_gpio_pin_assignment);
++
++ if (!bios_get_image(&bp->base, DATA_TABLES(gpio_pin_lut),
++ le16_to_cpu(header->table_header.structuresize)))
++ return BP_RESULT_BADBIOSTABLE;
++
+ for (i = 0; i < count; ++i) {
+ if (header->gpio_pin[i].gpio_id != gpio_id)
+ continue;
--- /dev/null
+From 6c92f6d9600efa3ef0d9e560a2b52776d9803c29 Mon Sep 17 00:00:00 2001
+From: Harry Wentland <harry.wentland@amd.com>
+Date: Thu, 7 May 2026 16:26:31 -0400
+Subject: drm/amd/display: Validate payload length and link_index in dc_process_dmub_aux_transfer_async
+
+From: Harry Wentland <harry.wentland@amd.com>
+
+commit 6c92f6d9600efa3ef0d9e560a2b52776d9803c29 upstream.
+
+[Why&How]
+dc_process_dmub_aux_transfer_async() copies payload->length bytes into a
+16-byte stack buffer (dpaux.data[16]) guarded only by an ASSERT(), which
+is a no-op in release builds. If a caller ever passes length > 16 this
+results in a stack buffer overflow via memcpy.
+
+Additionally, link_index is used to dereference dc->links[] without
+bounds checking against dc->link_count, risking an out-of-bounds access.
+
+Replace the ASSERT with a hard runtime check that returns false when
+payload->length exceeds the destination buffer size, and add a bounds
+check for link_index before it is used.
+
+Assisted-by: GitHub Copilot:Claude claude-4-opus
+Reviewed-by: Alex Hung <alex.hung@amd.com>
+Signed-off-by: Harry Wentland <harry.wentland@amd.com>
+Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
+Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit ba4caa9fecdf7a38f98c878ad05a8a64148b6881)
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/display/dc/core/dc.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
++++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
+@@ -5884,7 +5884,11 @@ bool dc_process_dmub_aux_transfer_async(
+ uint8_t action;
+ union dmub_rb_cmd cmd = {0};
+
+- ASSERT(payload->length <= 16);
++ if (link_index >= dc->link_count || !dc->links[link_index])
++ return false;
++
++ if (payload->length > sizeof(cmd.dp_aux_access.aux_control.dpaux.data))
++ return false;
+
+ cmd.dp_aux_access.header.type = DMUB_CMD__DP_AUX_ACCESS;
+ cmd.dp_aux_access.header.payload_bytes = 0;
--- /dev/null
+From b6074630a461b1322a814988779005cbc43612ea Mon Sep 17 00:00:00 2001
+From: Alan Liu <haoping.liu@amd.com>
+Date: Fri, 1 May 2026 12:35:48 +0800
+Subject: drm/amdgpu/vpe: Force collaborate sync after TRAP
+
+From: Alan Liu <haoping.liu@amd.com>
+
+commit b6074630a461b1322a814988779005cbc43612ea upstream.
+
+VPE1 could possibly hang and fail to power off at the end of commands in
+collaboration mode. This workaround adds a COLLAB_SYNC after TRAP to
+force instances synchronized to avoid VPE1 fail to power off.
+
+Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
+Signed-off-by: Alan liu <haoping.liu@amd.com>
+Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5171
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit a8b749c5c5afb7e5daa2bfb95d958fb3c6b8f055)
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c
+@@ -562,6 +562,11 @@ static void vpe_ring_emit_fence(struct a
+ amdgpu_ring_write(ring, 0);
+ }
+
++ /* WA: Force sync after TRAP to avoid VPE1 fail to power off */
++ if (ring->adev->vpe.collaborate_mode) {
++ amdgpu_ring_write(ring, VPE_CMD_HEADER(VPE_CMD_OPCODE_COLLAB_SYNC, 0));
++ amdgpu_ring_write(ring, 0xabcd);
++ }
+ }
+
+ static void vpe_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
+@@ -968,7 +973,7 @@ static const struct amdgpu_ring_funcs vp
+ .emit_frame_size =
+ 5 + /* vpe_ring_init_cond_exec */
+ 6 + /* vpe_ring_emit_pipeline_sync */
+- 10 + 10 + 10 + /* vpe_ring_emit_fence */
++ 12 + 12 + 12 + /* vpe_ring_emit_fence */
+ /* vpe_ring_emit_vm_flush */
+ SOC15_FLUSH_GPU_TLB_NUM_WREG * 3 +
+ SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 6,
--- /dev/null
+From 73d01051e8040c0b1de7fd26b3b8d0c2ffa6895c Mon Sep 17 00:00:00 2001
+From: Osama Abdelkader <osama.abdelkader@gmail.com>
+Date: Thu, 30 Apr 2026 21:49:42 +0200
+Subject: drm/bridge: chipone-icn6211: use devm_drm_bridge_add in i2c probe
+
+From: Osama Abdelkader <osama.abdelkader@gmail.com>
+
+commit 73d01051e8040c0b1de7fd26b3b8d0c2ffa6895c upstream.
+
+Use devm_drm_bridge_add() so the bridge is released if probe
+fails after registration, and drop drm_bridge_remove() in chipone_i2c_probe.
+
+Signed-off-by: Osama Abdelkader <osama.abdelkader@gmail.com>
+Fixes: 8dde6f7452a1 ("drm: bridge: icn6211: Add I2C configuration support")
+Cc: stable@vger.kernel.org
+Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
+Link: https://patch.msgid.link/20260430194944.78119-1-osama.abdelkader@gmail.com
+Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/bridge/chipone-icn6211.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/bridge/chipone-icn6211.c
++++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
+@@ -758,7 +758,9 @@ static int chipone_i2c_probe(struct i2c_
+ dev_set_drvdata(dev, icn);
+ i2c_set_clientdata(client, icn);
+
+- drm_bridge_add(&icn->bridge);
++ ret = devm_drm_bridge_add(dev, &icn->bridge);
++ if (ret)
++ return ret;
+
+ return chipone_dsi_host_attach(icn);
+ }
--- /dev/null
+From e02b5262fd288cc235f14e12233ea54e78c04611 Mon Sep 17 00:00:00 2001
+From: Julien Chauveau <chauveau.julien@gmail.com>
+Date: Tue, 24 Mar 2026 20:30:11 +0100
+Subject: drm/bridge: it66121: acquire reset GPIO in probe
+
+From: Julien Chauveau <chauveau.julien@gmail.com>
+
+commit e02b5262fd288cc235f14e12233ea54e78c04611 upstream.
+
+The it66121_ctx structure has a gpio_reset field, and it66121_hw_reset()
+calls gpiod_set_value() on it. However, the GPIO descriptor is never
+acquired via devm_gpiod_get(), leaving gpio_reset as NULL throughout
+the driver lifetime.
+
+gpiod_set_value() silently returns when passed a NULL descriptor, so
+the hardware reset sequence in it66121_hw_reset() is a no-op. This
+leaves the chip in an undefined state at probe time, which can prevent
+it from responding on the I2C bus.
+
+The DT binding marks reset-gpios as a required property, so all
+compliant device trees provide this GPIO. Add the missing
+devm_gpiod_get() call after enabling power supplies and before the
+hardware reset, so the chip is properly reset with power applied.
+
+Fixes: 988156dc2fc9 ("drm: bridge: add it66121 driver")
+Cc: stable@vger.kernel.org
+Signed-off-by: Julien Chauveau <chauveau.julien@gmail.com>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Tested-by: Javier Martinez Canillas <javierm@redhat.com>
+Link: https://patch.msgid.link/20260324193011.16583-1-chauveau.julien@gmail.com
+Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/bridge/ite-it66121.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/gpu/drm/bridge/ite-it66121.c
++++ b/drivers/gpu/drm/bridge/ite-it66121.c
+@@ -1559,6 +1559,11 @@ static int it66121_probe(struct i2c_clie
+ return ret;
+ }
+
++ ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
++ if (IS_ERR(ctx->gpio_reset))
++ return dev_err_probe(dev, PTR_ERR(ctx->gpio_reset),
++ "Failed to get reset GPIO\n");
++
+ it66121_hw_reset(ctx);
+
+ ctx->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config);
--- /dev/null
+From d45d5c819f2cd0b6b5d76a194a537a5f4aeefecb Mon Sep 17 00:00:00 2001
+From: Osama Abdelkader <osama.abdelkader@gmail.com>
+Date: Thu, 30 Apr 2026 21:56:59 +0200
+Subject: drm/bridge: megachips: remove bridge when irq request fails
+
+From: Osama Abdelkader <osama.abdelkader@gmail.com>
+
+commit d45d5c819f2cd0b6b5d76a194a537a5f4aeefecb upstream.
+
+If devm_request_threaded_irq() fails after drm_bridge_add(), remove the
+bridge before returning.
+
+Keep drm_bridge_add() rather than devm_drm_bridge_add(): registration is
+tied to the STDP4028 device while ge_b850v3_register() may complete from
+either I2C probe; devm would not unwind the bridge if the other client's
+probe fails.
+
+Signed-off-by: Osama Abdelkader <osama.abdelkader@gmail.com>
+Fixes: fcfa0ddc18ed ("drm/bridge: Drivers for megachips-stdpxxxx-ge-b850v3-fw (LVDS-DP++)")
+Cc: stable@vger.kernel.org
+Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
+Tested-by: Ian Ray <ian.ray@gehealthcare.com>
+Link: https://patch.msgid.link/20260430195700.80317-1-osama.abdelkader@gmail.com
+Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 16 +++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
++++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
+@@ -251,7 +251,6 @@ static void ge_b850v3_lvds_remove(void)
+ goto out;
+
+ drm_bridge_remove(&ge_b850v3_lvds_ptr->bridge);
+-
+ ge_b850v3_lvds_ptr = NULL;
+ out:
+ mutex_unlock(&ge_b850v3_lvds_dev_mutex);
+@@ -261,6 +260,7 @@ static int ge_b850v3_register(void)
+ {
+ struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c;
+ struct device *dev = &stdp4028_i2c->dev;
++ int ret;
+
+ /* drm bridge initialization */
+ ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT |
+@@ -277,11 +277,15 @@ static int ge_b850v3_register(void)
+ if (!stdp4028_i2c->irq)
+ return 0;
+
+- return devm_request_threaded_irq(&stdp4028_i2c->dev,
+- stdp4028_i2c->irq, NULL,
+- ge_b850v3_lvds_irq_handler,
+- IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+- "ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr);
++ ret = devm_request_threaded_irq(&stdp4028_i2c->dev,
++ stdp4028_i2c->irq, NULL,
++ ge_b850v3_lvds_irq_handler,
++ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
++ "ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr);
++ if (ret)
++ drm_bridge_remove(&ge_b850v3_lvds_ptr->bridge);
++
++ return ret;
+ }
+
+ static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c)
--- /dev/null
+From 4703049f768fc1c1caac754134118bee1a3af189 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jouni=20H=C3=B6gander?= <jouni.hogander@intel.com>
+Date: Fri, 15 May 2026 12:57:55 +0300
+Subject: drm/i915/psr: Apply Intel DPCD workaround when SDP on prior line used
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+commit 4703049f768fc1c1caac754134118bee1a3af189 upstream.
+
+There is Intel specific workaround DPCD address containing workaround for
+case where SDP is on prior line. Apply this workaround according to values
+in the offset.
+
+Fixes: 61e887329e33 ("drm/i915/xelpd: Handle PSR2 SDP indication in the prior scanline")
+Cc: <stable@vger.kernel.org> # v5.15+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
+Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com
+(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/i915/display/intel_psr.c | 35 +++++++++++++++++++++++++++----
+ 1 file changed, 31 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -1308,9 +1308,35 @@ static bool psr2_granularity_check(struc
+ return true;
+ }
+
+-static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_dp,
+- struct intel_crtc_state *crtc_state)
++static bool apply_scanline_indication_wa(struct intel_crtc_state *crtc_state,
++ struct intel_connector *connector)
+ {
++ struct intel_dp *intel_dp = intel_attached_dp(connector);
++ u8 early_scanline_support = connector->dp.psr_caps.intel_wa_dpcd &
++ INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK;
++
++ if (intel_dp->edp_dpcd[0] >= DP_EDP_15)
++ return true;
++
++ switch (early_scanline_support) {
++ case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1:
++ crtc_state->req_psr2_sdp_prior_scanline = false;
++ return false;
++ case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE:
++ return true;
++ case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE:
++ crtc_state->req_psr2_sdp_prior_scanline = false;
++ return true;
++ default:
++ MISSING_CASE(early_scanline_support);
++ return false;
++ }
++}
++
++static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_crtc_state *crtc_state,
++ struct intel_connector *connector)
++{
++ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ struct intel_display *display = to_intel_display(intel_dp);
+ const struct drm_display_mode *adjusted_mode = &crtc_state->uapi.adjusted_mode;
+ u32 hblank_total, hblank_ns, req_ns;
+@@ -1329,7 +1355,8 @@ static bool _compute_psr2_sdp_prior_scan
+ return false;
+
+ crtc_state->req_psr2_sdp_prior_scanline = true;
+- return true;
++
++ return apply_scanline_indication_wa(crtc_state, connector);
+ }
+
+ static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp,
+@@ -1534,7 +1561,7 @@ static bool intel_sel_update_config_vali
+ if (!crtc_state->has_panel_replay && !intel_psr2_config_valid(intel_dp, crtc_state))
+ goto unsupported;
+
+- if (!_compute_psr2_sdp_prior_scanline_indication(intel_dp, crtc_state)) {
++ if (!_compute_psr2_sdp_prior_scanline_indication(crtc_state, connector)) {
+ drm_dbg_kms(display->drm,
+ "Selective update not enabled, SDP indication do not fit in hblank\n");
+ goto unsupported;
--- /dev/null
+From 3392291fc509d8ad6e4ad90f15b0a193f721cbc9 Mon Sep 17 00:00:00 2001
+From: Daniel J Blueman <daniel@quora.org>
+Date: Fri, 8 May 2026 14:57:21 +0800
+Subject: drm/msm: Fix shrinker deadlock
+
+From: Daniel J Blueman <daniel@quora.org>
+
+commit 3392291fc509d8ad6e4ad90f15b0a193f721cbc9 upstream.
+
+With PROVE_LOCKING on an Snapdragon X1 and VM reclaim pressure, we see:
+
+ ======================================================
+ WARNING: possible circular locking dependency detected
+ 7.0.0-debug+ #43 Tainted: G W
+ ------------------------------------------------------
+ kswapd0/82 is trying to acquire lock:
+ ffff800080ec3870 (reservation_ww_class_acquire){+.+.}-{0:0}, at: msm_gem_shrinker_scan+0x17c/0x400 [msm]
+
+ but task is already holding lock:
+ ffffc31709b263b8 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat+0x88/0x988
+
+ which lock already depends on the new lock.
+
+ the existing dependency chain (in reverse order) is:
+
+ -> #2 (fs_reclaim){+.+.}-{0:0}:
+ __lock_acquire+0x4d0/0xad0
+ lock_acquire.part.0+0xc4/0x248
+ lock_acquire+0x8c/0x248
+ fs_reclaim_acquire+0xd0/0xf0
+ dma_resv_lockdep+0x224/0x348
+ do_one_initcall+0x84/0x5d0
+ do_initcalls+0x194/0x1d8
+ kernel_init_freeable+0x128/0x180
+ kernel_init+0x2c/0x160
+ ret_from_fork+0x10/0x20
+
+ -> #1 (reservation_ww_class_mutex){+.+.}-{4:4}:
+ __lock_acquire+0x4d0/0xad0
+ lock_acquire.part.0+0xc4/0x248
+ lock_acquire+0x8c/0x248
+ dma_resv_lockdep+0x1a8/0x348
+ do_one_initcall+0x84/0x5d0
+ do_initcalls+0x194/0x1d8
+ kernel_init_freeable+0x128/0x180
+ kernel_init+0x2c/0x160
+ ret_from_fork+0x10/0x20
+
+ -> #0 (reservation_ww_class_acquire){+.+.}-{0:0}:
+ check_prev_add+0x114/0x790
+ validate_chain+0x594/0x6f0
+ __lock_acquire+0x4d0/0xad0
+ lock_acquire.part.0+0xc4/0x248
+ lock_acquire+0x8c/0x248
+ drm_gem_lru_scan+0x1ac/0x440
+ msm_gem_shrinker_scan+0x17c/0x400 [msm]
+ do_shrink_slab+0x150/0x4a0
+ shrink_slab+0x144/0x460
+ shrink_one+0x9c/0x1b0
+ shrink_many+0x27c/0x5c0
+ shrink_node+0x344/0x550
+ balance_pgdat+0x2c0/0x988
+ kswapd+0x11c/0x318
+ kthread+0x10c/0x128
+ ret_from_fork+0x10/0x20
+
+ other info that might help us debug this:
+ Chain exists of:
+ reservation_ww_class_acquire --> reservation_ww_class_mutex --> fs_reclaim
+ Possible unsafe locking scenario:
+ CPU0 CPU1
+ ---- ----
+ lock(fs_reclaim);
+ lock(reservation_ww_class_mutex);
+ lock(fs_reclaim);
+ lock(reservation_ww_class_acquire);
+
+ *** DEADLOCK ***
+ 1 lock held by kswapd0/82:
+ #0: ffffc31709b263b8 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat+0x88/0x988
+
+ stack backtrace:
+ CPU: 4 UID: 0 PID: 82 Comm: kswapd0 Tainted: G W 7.0.0-debug+ #43 PREEMPT(full)
+ Tainted: [W]=WARN
+ Hardware name: LENOVO 21BX0016US/21BX0016US, BIOS N3HET94W (1.66 ) 09/15/2025
+ Call trace:
+ show_stack+0x20/0x40 (C)
+ dump_stack_lvl+0x9c/0xd0
+ dump_stack+0x18/0x30
+ print_circular_bug+0x114/0x120
+ check_noncircular+0x178/0x198
+ check_prev_add+0x114/0x790
+ validate_chain+0x594/0x6f0
+ __lock_acquire+0x4d0/0xad0
+ lock_acquire.part.0+0xc4/0x248
+ lock_acquire+0x8c/0x248
+ drm_gem_lru_scan+0x1ac/0x440
+ msm_gem_shrinker_scan+0x17c/0x400 [msm]
+ do_shrink_slab+0x150/0x4a0
+ shrink_slab+0x144/0x460
+ shrink_one+0x9c/0x1b0
+ shrink_many+0x27c/0x5c0
+ shrink_node+0x344/0x550
+ balance_pgdat+0x2c0/0x988
+ kswapd+0x11c/0x318
+ kthread+0x10c/0x128
+ ret_from_fork+0x10/0x20
+
+kswapd0 holding fs_reclaim calls the MSM shrinker, which calls
+dma_resv_lock. This in turn acquires fs_reclaim.
+
+Fix this deadlock by using dma_resv_trylock() instead, dropping the
+subsequently unused passed wait-wound lock 'ticket'.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Daniel J Blueman <daniel@quora.org>
+Fixes: fe4952b5f27c ("drm/msm: Convert vm locking")
+Patchwork: https://patchwork.freedesktop.org/patch/723564/
+Message-ID: <20260508065722.18785-1-daniel@quora.org>
+[rob: fixup compile errors, replace lockdep splat with something legible]
+Signed-off-by: Rob Clark <robin.clark@oss.qualcomm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/msm/msm_gem_shrinker.c | 40 +++++++++++---------------
+ 1 file changed, 16 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c
+index 31fa51a44f86..6e39e4e578bb 100644
+--- a/drivers/gpu/drm/msm/msm_gem_shrinker.c
++++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c
+@@ -43,8 +43,7 @@ msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
+ }
+
+ static bool
+-with_vm_locks(struct ww_acquire_ctx *ticket,
+- void (*fn)(struct drm_gem_object *obj),
++with_vm_locks(void (*fn)(struct drm_gem_object *obj),
+ struct drm_gem_object *obj)
+ {
+ /*
+@@ -52,7 +51,7 @@ with_vm_locks(struct ww_acquire_ctx *ticket,
+ * success paths
+ */
+ struct drm_gpuvm_bo *vm_bo, *last_locked = NULL;
+- int ret = 0;
++ bool locked = true;
+
+ drm_gem_for_each_gpuvm_bo (vm_bo, obj) {
+ struct dma_resv *resv = drm_gpuvm_resv(vm_bo->vm);
+@@ -60,23 +59,14 @@ with_vm_locks(struct ww_acquire_ctx *ticket,
+ if (resv == obj->resv)
+ continue;
+
+- ret = dma_resv_lock(resv, ticket);
+-
+ /*
+- * Since we already skip the case when the VM and obj
+- * share a resv (ie. _NO_SHARE objs), we don't expect
+- * to hit a double-locking scenario... which the lock
+- * unwinding cannot really cope with.
++ * dma_resv_lock can't be used due to acquiring 'ticket' before the
++ * fs_reclaim lock, which is held in shrinker context
+ */
+- WARN_ON(ret == -EALREADY);
+-
+- /*
+- * Don't bother with slow-lock / backoff / retry sequence,
+- * if we can't get the lock just give up and move on to
+- * the next object.
+- */
+- if (ret)
++ if (!dma_resv_trylock(resv)) {
++ locked = false;
+ goto out_unlock;
++ }
+
+ /*
+ * Hold a ref to prevent the vm_bo from being freed
+@@ -108,11 +98,11 @@ with_vm_locks(struct ww_acquire_ctx *ticket,
+ }
+ }
+
+- return ret == 0;
++ return locked;
+ }
+
+ static bool
+-purge(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket)
++purge(struct drm_gem_object *obj, struct ww_acquire_ctx *)
+ {
+ if (!is_purgeable(to_msm_bo(obj)))
+ return false;
+@@ -120,11 +110,11 @@ purge(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket)
+ if (msm_gem_active(obj))
+ return false;
+
+- return with_vm_locks(ticket, msm_gem_purge, obj);
++ return with_vm_locks(msm_gem_purge, obj);
+ }
+
+ static bool
+-evict(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket)
++evict(struct drm_gem_object *obj, struct ww_acquire_ctx *)
+ {
+ if (is_unevictable(to_msm_bo(obj)))
+ return false;
+@@ -132,7 +122,7 @@ evict(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket)
+ if (msm_gem_active(obj))
+ return false;
+
+- return with_vm_locks(ticket, msm_gem_evict, obj);
++ return with_vm_locks(msm_gem_evict, obj);
+ }
+
+ static bool
+@@ -164,7 +154,6 @@ static unsigned long
+ msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
+ {
+ struct msm_drm_private *priv = shrinker->private_data;
+- struct ww_acquire_ctx ticket;
+ struct {
+ struct drm_gem_lru *lru;
+ bool (*shrink)(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket);
+@@ -185,11 +174,14 @@ msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
+ for (unsigned i = 0; (nr > 0) && (i < ARRAY_SIZE(stages)); i++) {
+ if (!stages[i].cond)
+ continue;
++ /*
++ * 'ticket' not needed on trylock paths
++ */
+ stages[i].freed =
+ drm_gem_lru_scan(stages[i].lru, nr,
+ &stages[i].remaining,
+ stages[i].shrink,
+- &ticket);
++ NULL);
+ nr -= stages[i].freed;
+ freed += stages[i].freed;
+ remaining += stages[i].remaining;
+--
+2.54.0
+
--- /dev/null
+From b0fe80c0b9250b35e2211bf3117e7aca814a21b0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+Date: Fri, 15 May 2026 12:07:14 -0300
+Subject: drm/v3d: Fix use-after-free of CPU job query arrays on error path
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Maíra Canal <mcanal@igalia.com>
+
+commit b0fe80c0b9250b35e2211bf3117e7aca814a21b0 upstream.
+
+The CPU job ioctl's fail label calls kvfree() on cpu_job's timestamp and
+performance query arrays after v3d_job_cleanup(), which drops the job's
+last reference and frees cpu_job. Reading cpu_job at that point is a
+use-after-free. Also, on the early v3d_job_init() failure path, it is a
+NULL dereference, since v3d_job_deallocate() zeroes the local pointer.
+
+In the success path, the arrays are released from the scheduler's
+.free_job callback, but on the error path, they are freed manually, as
+the job was never pushed to the scheduler. While the success path deals
+with this correctly, the fail path doesn't.
+
+On top of that, the manual kvfree() calls only free the array storage;
+they don't drm_syncobj_put() the per-query syncobjs that
+v3d_timestamp_query_info_free() and v3d_performance_query_info_free()
+release on the success path. So the same fail path that triggers the
+use-after-free also leaks one syncobj reference per query.
+
+Unify the CPU job teardown into the CPU job's kref destructor, mirroring
+v3d_render_job_free(). The scheduler's .free_job slot reverts to the
+generic v3d_sched_job_free() and the fail label drops the manual
+kvfree() calls, leaving a single teardown path that is reached from both
+the scheduler and the ioctl error path. That removes the use-after-free,
+the NULL dereference, and the syncobj leak by construction.
+
+Cc: stable@vger.kernel.org
+Fixes: 9ba0ff3e083f ("drm/v3d: Create a CPU job extension for the timestamp query job")
+Assisted-by: Claude:claude-opus-4.7
+Reviewed-by: Iago Toral Quiroga <itoral@igalia.com>
+Link: https://patch.msgid.link/20260515-v3d-cpu-job-leaks-v1-1-7f147cbbf935@igalia.com
+Signed-off-by: Maíra Canal <mcanal@igalia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/v3d/v3d_sched.c | 16 +---------------
+ drivers/gpu/drm/v3d/v3d_submit.c | 19 ++++++++++++++++---
+ 2 files changed, 17 insertions(+), 18 deletions(-)
+
+--- a/drivers/gpu/drm/v3d/v3d_sched.c
++++ b/drivers/gpu/drm/v3d/v3d_sched.c
+@@ -104,20 +104,6 @@ v3d_performance_query_info_free(struct v
+ }
+
+ static void
+-v3d_cpu_job_free(struct drm_sched_job *sched_job)
+-{
+- struct v3d_cpu_job *job = to_cpu_job(sched_job);
+-
+- v3d_timestamp_query_info_free(&job->timestamp_query,
+- job->timestamp_query.count);
+-
+- v3d_performance_query_info_free(&job->performance_query,
+- job->performance_query.count);
+-
+- v3d_job_cleanup(&job->base);
+-}
+-
+-static void
+ v3d_switch_perfmon(struct v3d_dev *v3d, struct v3d_job *job)
+ {
+ struct v3d_perfmon *perfmon = v3d->global_perfmon;
+@@ -860,7 +846,7 @@ static const struct drm_sched_backend_op
+
+ static const struct drm_sched_backend_ops v3d_cpu_sched_ops = {
+ .run_job = v3d_cpu_job_run,
+- .free_job = v3d_cpu_job_free
++ .free_job = v3d_sched_job_free
+ };
+
+ static int
+--- a/drivers/gpu/drm/v3d/v3d_submit.c
++++ b/drivers/gpu/drm/v3d/v3d_submit.c
+@@ -119,6 +119,21 @@ v3d_render_job_free(struct kref *ref)
+ v3d_job_free(ref);
+ }
+
++static void
++v3d_cpu_job_free(struct kref *ref)
++{
++ struct v3d_cpu_job *job = container_of(ref, struct v3d_cpu_job,
++ base.refcount);
++
++ v3d_timestamp_query_info_free(&job->timestamp_query,
++ job->timestamp_query.count);
++
++ v3d_performance_query_info_free(&job->performance_query,
++ job->performance_query.count);
++
++ v3d_job_free(ref);
++}
++
+ void v3d_job_cleanup(struct v3d_job *job)
+ {
+ if (!job)
+@@ -1321,7 +1336,7 @@ v3d_submit_cpu_ioctl(struct drm_device *
+ trace_v3d_submit_cpu_ioctl(&v3d->drm, cpu_job->job_type);
+
+ ret = v3d_job_init(v3d, file_priv, &cpu_job->base,
+- v3d_job_free, 0, &se, V3D_CPU);
++ v3d_cpu_job_free, 0, &se, V3D_CPU);
+ if (ret) {
+ v3d_job_deallocate((void *)&cpu_job);
+ goto fail;
+@@ -1404,8 +1419,6 @@ fail:
+ v3d_job_cleanup((void *)csd_job);
+ v3d_job_cleanup(clean_job);
+ v3d_put_multisync_post_deps(&se);
+- kvfree(cpu_job->timestamp_query.queries);
+- kvfree(cpu_job->performance_query.queries);
+
+ return ret;
+ }
--- /dev/null
+From 6eb6e5acafa46854d4363e6c34981289995f3ace Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+Date: Fri, 15 May 2026 12:07:15 -0300
+Subject: drm/v3d: Release indirect CSD GEM reference on CPU job free
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Maíra Canal <mcanal@igalia.com>
+
+commit 6eb6e5acafa46854d4363e6c34981289995f3ace upstream.
+
+v3d_get_cpu_indirect_csd_params() takes a reference to the indirect BO via
+drm_gem_object_lookup() and stashes it in cpu_job->indirect_csd.indirect,
+but nothing on the CPU job teardown path ever drops that reference.
+
+Drop the extra reference in v3d_cpu_job_free(). The NULL check covers ioctl
+errors before the lookup ran and CPU job types other than
+V3D_CPU_JOB_TYPE_INDIRECT_CSD, which leave the field zero-initialised.
+
+Cc: stable@vger.kernel.org
+Fixes: 18b8413b25b7 ("drm/v3d: Create a CPU job extension for a indirect CSD job")
+Assisted-by: Claude:claude-opus-4.7
+Reviewed-by: Iago Toral Quiroga <itoral@igalia.com>
+Link: https://patch.msgid.link/20260515-v3d-cpu-job-leaks-v1-2-7f147cbbf935@igalia.com
+Signed-off-by: Maíra Canal <mcanal@igalia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/v3d/v3d_submit.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/gpu/drm/v3d/v3d_submit.c
++++ b/drivers/gpu/drm/v3d/v3d_submit.c
+@@ -131,6 +131,9 @@ v3d_cpu_job_free(struct kref *ref)
+ v3d_performance_query_info_free(&job->performance_query,
+ job->performance_query.count);
+
++ if (job->indirect_csd.indirect)
++ drm_gem_object_put(job->indirect_csd.indirect);
++
+ v3d_job_free(ref);
+ }
+
--- /dev/null
+From 9af1b6e175c82daf4b423da339a722d8e67a735a Mon Sep 17 00:00:00 2001
+From: Deepanshu Kartikey <kartikey406@gmail.com>
+Date: Tue, 19 May 2026 13:52:47 +0530
+Subject: drm/virtio: use uninterruptible resv lock for plane updates
+
+From: Deepanshu Kartikey <kartikey406@gmail.com>
+
+commit 9af1b6e175c82daf4b423da339a722d8e67a735a upstream.
+
+virtio_gpu_cursor_plane_update() and virtio_gpu_resource_flush() lock
+the framebuffer BO's dma_resv via virtio_gpu_array_lock_resv() and
+ignore its return value. The function can fail with -EINTR from
+dma_resv_lock_interruptible() (signal during lock wait) or with
+-ENOMEM from dma_resv_reserve_fences() (fence slot allocation),
+leaving the resv lock not held. The queue path then walks the object
+array and calls dma_resv_add_fence(), which requires the lock held;
+with lockdep enabled this trips dma_resv_assert_held():
+
+ WARNING: drivers/dma-buf/dma-resv.c:296 at dma_resv_add_fence+0x71e/0x840
+ Call Trace:
+ virtio_gpu_array_add_fence
+ virtio_gpu_queue_ctrl_sgs
+ virtio_gpu_queue_fenced_ctrl_buffer
+ virtio_gpu_cursor_plane_update
+ drm_atomic_helper_commit_planes
+ drm_atomic_helper_commit_tail
+ commit_tail
+ drm_atomic_helper_commit
+ drm_atomic_commit
+ drm_atomic_helper_update_plane
+ __setplane_atomic
+ drm_mode_cursor_universal
+ drm_mode_cursor_common
+ drm_mode_cursor_ioctl
+ drm_ioctl
+ __x64_sys_ioctl
+
+Beyond the WARN, mutating the dma_resv fence list without the lock
+races with concurrent readers/writers and can corrupt the list.
+
+Both call sites run inside the .atomic_update plane callback, which
+DRM atomic helpers do not allow to fail (by the time it runs, the
+commit has been signed off to userspace and there is no clean
+rollback path). Moving the lock acquisition to .prepare_fb was
+rejected because the broader lock scope deadlocks against other BO
+locking paths in the same atomic commit.
+
+Introduce virtio_gpu_lock_one_resv_uninterruptible() that uses
+dma_resv_lock() instead of dma_resv_lock_interruptible(). This
+eliminates the -EINTR failure mode -- the realistic syzbot trigger
+-- without extending the lock hold across the commit. The helper
+locks a single BO and rejects nents > 1 with -EINVAL; both fix
+sites lock exactly one BO.
+
+Use it from virtio_gpu_cursor_plane_update() and
+virtio_gpu_resource_flush(); check the return value to handle the
+remaining -ENOMEM case from dma_resv_reserve_fences() by freeing
+the objs and skipping the plane update for that frame. The
+framebuffer BOs touched here are not shared with other contexts
+and lock contention is expected to be brief, so the loss of
+signal-interruptibility is acceptable.
+
+Other callers of virtio_gpu_array_lock_resv() (the ioctl paths)
+continue to use the interruptible variant.
+
+The bug was reported by syzbot, triggered via fault injection
+(fail_nth) on the DRM_IOCTL_MODE_CURSOR path, which forces the
+-ENOMEM branch in dma_resv_reserve_fences().
+
+Reported-by: syzbot+72bd3dd3a5d5f39a0271@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=72bd3dd3a5d5f39a0271
+Fixes: 5cfd31c5b3a3 ("drm/virtio: fix virtio_gpu_cursor_plane_update().")
+Cc: stable@vger.kernel.org
+Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
+Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
+Link: https://patch.msgid.link/20260519082247.34470-1-kartikey406@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/virtio/virtgpu_drv.h | 1 +
+ drivers/gpu/drm/virtio/virtgpu_gem.c | 17 +++++++++++++++++
+ drivers/gpu/drm/virtio/virtgpu_plane.c | 10 ++++++++--
+ 3 files changed, 26 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
++++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
+@@ -317,6 +317,7 @@ virtio_gpu_array_from_handles(struct drm
+ void virtio_gpu_array_add_obj(struct virtio_gpu_object_array *objs,
+ struct drm_gem_object *obj);
+ int virtio_gpu_array_lock_resv(struct virtio_gpu_object_array *objs);
++int virtio_gpu_lock_one_resv_uninterruptible(struct virtio_gpu_object_array *objs);
+ void virtio_gpu_array_unlock_resv(struct virtio_gpu_object_array *objs);
+ void virtio_gpu_array_add_fence(struct virtio_gpu_object_array *objs,
+ struct dma_fence *fence);
+--- a/drivers/gpu/drm/virtio/virtgpu_gem.c
++++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
+@@ -238,6 +238,23 @@ int virtio_gpu_array_lock_resv(struct vi
+ return ret;
+ }
+
++int virtio_gpu_lock_one_resv_uninterruptible(struct virtio_gpu_object_array *objs)
++{
++ int ret;
++
++ if (objs->nents != 1)
++ return -EINVAL;
++
++ dma_resv_lock(objs->objs[0]->resv, NULL);
++
++ ret = dma_resv_reserve_fences(objs->objs[0]->resv, 1);
++ if (ret) {
++ virtio_gpu_array_unlock_resv(objs);
++ return ret;
++ }
++ return 0;
++}
++
+ void virtio_gpu_array_unlock_resv(struct virtio_gpu_object_array *objs)
+ {
+ if (objs->nents == 1) {
+--- a/drivers/gpu/drm/virtio/virtgpu_plane.c
++++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
+@@ -214,7 +214,10 @@ static void virtio_gpu_resource_flush(st
+ if (!objs)
+ return;
+ virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]);
+- virtio_gpu_array_lock_resv(objs);
++ if (virtio_gpu_lock_one_resv_uninterruptible(objs)) {
++ virtio_gpu_array_put_free(objs);
++ return;
++ }
+ virtio_gpu_cmd_resource_flush(vgdev, bo->hw_res_handle, x, y,
+ width, height, objs,
+ vgplane_st->fence);
+@@ -458,7 +461,10 @@ static void virtio_gpu_cursor_plane_upda
+ if (!objs)
+ return;
+ virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]);
+- virtio_gpu_array_lock_resv(objs);
++ if (virtio_gpu_lock_one_resv_uninterruptible(objs)) {
++ virtio_gpu_array_put_free(objs);
++ return;
++ }
+ virtio_gpu_cmd_transfer_to_host_2d
+ (vgdev, 0,
+ plane->state->crtc_w,
--- /dev/null
+From e7537735028c3ad4b0bfc02ff8fa2a1a28aa04fe Mon Sep 17 00:00:00 2001
+From: Heechan Kang <gganji11@naver.com>
+Date: Sun, 17 May 2026 15:22:32 +0900
+Subject: fwctl: pds: Validate RPC input size before parsing
+
+From: Heechan Kang <gganji11@naver.com>
+
+commit e7537735028c3ad4b0bfc02ff8fa2a1a28aa04fe upstream.
+
+The fwctl core allocates the device-specific RPC input buffer with
+fwctl_rpc.in_len and passes that buffer to the driver callback.
+
+pdsfc_fw_rpc() casts the buffer to struct fwctl_rpc_pds and then calls
+pdsfc_validate_rpc(), which reads fields from that structure before
+checking that the input buffer is large enough to contain it. A short
+in_len can make pds_fwctl read beyond the allocation.
+
+Reject pds RPC buffers that are smaller than struct fwctl_rpc_pds before
+parsing any pds-specific fields.
+
+Fixes: 92c66ee829b9 ("pds_fwctl: add rpc and query support")
+Link: https://patch.msgid.link/r/20260517062232.1858747-1-gganji11@naver.com
+Cc: stable@vger.kernel.org # v6.15+
+Signed-off-by: Heechan Kang <gganji11@naver.com>
+Reviewed-by: Dave Jiang <dave.jiang@intel.com>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/fwctl/pds/main.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/fwctl/pds/main.c b/drivers/fwctl/pds/main.c
+index 08872ee8422f..68fe254dd10a 100644
+--- a/drivers/fwctl/pds/main.c
++++ b/drivers/fwctl/pds/main.c
+@@ -362,6 +362,9 @@ static void *pdsfc_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
+ void *out = NULL;
+ int err;
+
++ if (in_len < sizeof(*rpc))
++ return ERR_PTR(-EINVAL);
++
+ err = pdsfc_validate_rpc(pdsfc, rpc, scope);
+ if (err)
+ return ERR_PTR(err);
+--
+2.54.0
+
--- /dev/null
+From 43cae21424ff8e33894a0f86c6b80b840c049fd7 Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Fri, 15 May 2026 15:11:51 -0700
+Subject: hwmon: (pmbus/adm1266) bounce blackbox records through a protocol-sized buffer
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit 43cae21424ff8e33894a0f86c6b80b840c049fd7 upstream.
+
+adm1266_pmbus_block_xfer() copies the device-supplied block payload
+into the caller-provided buffer using the device-supplied length:
+
+ memcpy(data_r, &msgs[1].buf[1], msgs[1].buf[0]);
+
+The helper does not know how large data_r is and trusts the device to
+return at most one record's worth of bytes. adm1266_nvmem_read_blackbox()
+violates that contract: it advances read_buff inside data->dev_mem in
+ADM1266_BLACKBOX_SIZE (64-byte) strides while the helper is willing to
+write up to ADM1266_PMBUS_BLOCK_MAX (255) bytes. A device that returns
+more than 64 bytes on the trailing record (read_buff offset 1984 in
+the 2048-byte dev_mem allocation) overflows dev_mem by up to 191 bytes
+before the post-call
+
+ if (ret != ADM1266_BLACKBOX_SIZE)
+ return -EIO;
+
+can reject the response.
+
+Contain the fix in the caller without changing the helper signature:
+read each record into a 255-byte local bounce buffer that matches the
+helper's maximum output, validate the returned length, and only then
+copy exactly ADM1266_BLACKBOX_SIZE bytes into the dev_mem slot.
+
+Fixes: 407dc802a9c0 ("hwmon: (pmbus/adm1266) Add Block process call")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Link: https://lore.kernel.org/r/20260515-adm1266-fixes-v1-5-1c1ea1349cfe@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -348,6 +348,7 @@ static void adm1266_init_debugfs(struct
+
+ static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff)
+ {
++ u8 record[ADM1266_PMBUS_BLOCK_MAX];
+ int record_count;
+ char index;
+ u8 buf[I2C_SMBUS_BLOCK_MAX];
+@@ -365,13 +366,14 @@ static int adm1266_nvmem_read_blackbox(s
+ return -EIO;
+
+ for (index = 0; index < record_count; index++) {
+- ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, &index, read_buff);
++ ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, &index, record);
+ if (ret < 0)
+ return ret;
+
+ if (ret != ADM1266_BLACKBOX_SIZE)
+ return -EIO;
+
++ memcpy(read_buff, record, ADM1266_BLACKBOX_SIZE);
+ read_buff += ADM1266_BLACKBOX_SIZE;
+ }
+
--- /dev/null
+From d7834d92251baade796812876e95555e2066fa9f Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Mon, 18 May 2026 17:52:25 -0700
+Subject: hwmon: (pmbus/adm1266) cap PDIO scan in get_multiple at ADM1266_PDIO_NR
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit d7834d92251baade796812876e95555e2066fa9f upstream.
+
+adm1266_gpio_get_multiple() iterates the PDIO portion of the
+caller-supplied mask using
+
+ for_each_set_bit_from(gpio_nr, mask,
+ ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) {
+ ...
+ }
+
+where ADM1266_PDIO_STATUS is the PMBus command code (0xE9, i.e. 233),
+not the number of PDIO pins. The intended upper bound is
+ADM1266_GPIO_NR + ADM1266_PDIO_NR = 25.
+
+gpiolib hands in a mask sized for gc.ngpio (= 25 bits on this chip),
+so the iteration walks find_next_bit() up to 242, reading up to 217
+extra bits (a handful of unsigned-long words: four on 64-bit, seven
+on 32-bit) of whatever lives past the end of the mask in the
+caller's stack. Any incidental set bit in that range then drives a
+set_bit(gpio_nr, bits) call that writes past the end of the
+caller-supplied bits array too -- both out-of-bounds.
+
+Substitute ADM1266_PDIO_NR for the constant so the scan stops at the
+last real PDIO bit.
+
+Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Reviewed-by: Linus Walleij <linusw@kernel.org>
+Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-1-e425e4f88139@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -212,7 +212,7 @@ static int adm1266_gpio_get_multiple(str
+ status = read_buf[0] + (read_buf[1] << 8);
+
+ *bits = 0;
+- for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) {
++ for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_NR) {
+ if (test_bit(gpio_nr - ADM1266_GPIO_NR, &status))
+ set_bit(gpio_nr, bits);
+ }
--- /dev/null
+From 3327a12aee9e10ffa903e28b8445dfd1af5307c0 Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Mon, 18 May 2026 17:52:26 -0700
+Subject: hwmon: (pmbus/adm1266) don't clobber GPIO bits before PDIO read in get_multiple
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit 3327a12aee9e10ffa903e28b8445dfd1af5307c0 upstream.
+
+adm1266_gpio_get_multiple() zeroes *bits before the GPIO_STATUS loop
+and then a second time before the PDIO_STATUS loop:
+
+ *bits = 0;
+ for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) {
+ ...
+ set_bit(gpio_nr, bits);
+ }
+
+ ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, ...);
+ ...
+ *bits = 0;
+ for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_NR) {
+ ...
+ set_bit(gpio_nr, bits);
+ }
+
+The second *bits = 0 throws away every GPIO bit the first loop just
+populated, so callers asking for any combination of GPIO and PDIO
+pins always see the GPIO portion of the returned bits as zero.
+
+Drop the redundant second assignment so both halves of the result
+survive.
+
+Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Reviewed-by: Linus Walleij <linusw@kernel.org>
+Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-2-e425e4f88139@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -211,7 +211,6 @@ static int adm1266_gpio_get_multiple(str
+
+ status = read_buf[0] + (read_buf[1] << 8);
+
+- *bits = 0;
+ for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_NR) {
+ if (test_bit(gpio_nr - ADM1266_GPIO_NR, &status))
+ set_bit(gpio_nr, bits);
--- /dev/null
+From 487566cb1ccdf3756fdd7bf8d875e612ff3169bb Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Fri, 15 May 2026 15:11:50 -0700
+Subject: hwmon: (pmbus/adm1266) include PEC byte in pmbus_block_xfer read buffer
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit 487566cb1ccdf3756fdd7bf8d875e612ff3169bb upstream.
+
+adm1266_pmbus_block_xfer() sets up the read transaction with
+
+ .buf = data->read_buf,
+ .len = ADM1266_PMBUS_BLOCK_MAX + 2,
+
+but read_buf in struct adm1266_data is declared as
+
+ u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
+
+For a max-length block response (length byte = 255 + up to 1 PEC
+byte), the i2c controller is told to write 257 bytes into a 256-byte
+buffer, putting one byte past the end of read_buf. The same response
+also makes the subsequent PEC compare
+
+ if (crc != msgs[1].buf[msgs[1].buf[0] + 1])
+
+read a byte beyond the array.
+
+Bump the read_buf declaration to ADM1266_PMBUS_BLOCK_MAX + 2 so the
+buffer can hold the length byte, up to 255 payload bytes, and the PEC
+byte the i2c_msg length already accounts for.
+
+Fixes: 407dc802a9c0 ("hwmon: (pmbus/adm1266) Add Block process call")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Link: https://lore.kernel.org/r/20260515-adm1266-fixes-v1-4-1c1ea1349cfe@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -61,7 +61,7 @@ struct adm1266_data {
+ u8 *dev_mem;
+ struct mutex buf_mutex;
+ u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] ____cacheline_aligned;
+- u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] ____cacheline_aligned;
++ u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 2] ____cacheline_aligned;
+ };
+
+ static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
--- /dev/null
+From 491403b9b76cf66abd81301c5901aa4a4549f1e8 Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Mon, 18 May 2026 17:52:28 -0700
+Subject: hwmon: (pmbus/adm1266) register the gpio_chip after pmbus_do_probe()
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit 491403b9b76cf66abd81301c5901aa4a4549f1e8 upstream.
+
+adm1266_probe() calls adm1266_config_gpio() -- which goes on to
+devm_gpiochip_add_data() and exposes the gpio_chip callbacks to
+gpiolib -- before pmbus_do_probe() has initialised the per-client
+PMBus state (notably the pmbus_lock mutex the core hands out via
+pmbus_get_data()).
+
+That ordering is already a latent hazard: any GPIO access that lands
+between adm1266_config_gpio() and the end of pmbus_do_probe() (for
+example a sysfs read from a user space agent that opens the gpiochip
+the instant gpiolib advertises it) races pmbus_do_probe()'s own
+device accesses with no serialisation.
+
+Move adm1266_config_gpio() down past pmbus_do_probe() so the chip
+isn't reachable from userspace until the PMBus state it depends on
+is fully initialised.
+
+Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-4-e425e4f88139@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -468,10 +468,6 @@ static int adm1266_probe(struct i2c_clie
+ crc8_populate_msb(pmbus_crc_table, 0x7);
+ mutex_init(&data->buf_mutex);
+
+- ret = adm1266_config_gpio(data);
+- if (ret < 0)
+- return ret;
+-
+ ret = adm1266_set_rtc(data);
+ if (ret < 0)
+ return ret;
+@@ -484,6 +480,10 @@ static int adm1266_probe(struct i2c_clie
+ if (ret)
+ return ret;
+
++ ret = adm1266_config_gpio(data);
++ if (ret < 0)
++ return ret;
++
+ adm1266_init_debugfs(data);
+
+ return 0;
--- /dev/null
+From 6af713af91d5c34ec049eb3cc2c5b3f5eba953b8 Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Mon, 18 May 2026 17:52:29 -0700
+Subject: hwmon: (pmbus/adm1266) register the nvmem device after pmbus_do_probe()
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit 6af713af91d5c34ec049eb3cc2c5b3f5eba953b8 upstream.
+
+adm1266_probe() calls adm1266_config_nvmem() -- which goes on to
+devm_nvmem_register() and exposes adm1266_nvmem_read() to userspace --
+before pmbus_do_probe() has initialised the per-client PMBus state.
+
+Same latent hazard as the gpio_chip one fixed in the previous patch:
+once the nvmem device is registered, gpiolib's nvmem char-dev / sysfs
+interface is reachable, and any concurrent read triggers
+adm1266_nvmem_read() -> adm1266_nvmem_read_blackbox(), which issues
+PMBus traffic that races pmbus_do_probe()'s own device accesses with
+no serialisation.
+
+Move adm1266_config_nvmem() down past pmbus_do_probe() so the nvmem
+device isn't reachable from userspace until the PMBus state the
+nvmem accessors depend on is fully initialised.
+
+Fixes: 15609d189302 ("hwmon: (pmbus/adm1266) read blackbox")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-5-e425e4f88139@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -472,14 +472,14 @@ static int adm1266_probe(struct i2c_clie
+ if (ret < 0)
+ return ret;
+
+- ret = adm1266_config_nvmem(data);
+- if (ret < 0)
+- return ret;
+-
+ ret = pmbus_do_probe(client, &data->info);
+ if (ret)
+ return ret;
+
++ ret = adm1266_config_nvmem(data);
++ if (ret < 0)
++ return ret;
++
+ ret = adm1266_config_gpio(data);
+ if (ret < 0)
+ return ret;
--- /dev/null
+From 4afca954622d672ea65ed961bed01cf91caa034e Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Fri, 15 May 2026 15:11:49 -0700
+Subject: hwmon: (pmbus/adm1266) reject implausible blackbox record_count
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit 4afca954622d672ea65ed961bed01cf91caa034e upstream.
+
+adm1266_nvmem_read_blackbox() loops over a record_count that comes
+straight from byte 3 of the BLACKBOX_INFO response. The destination
+buffer is data->dev_mem, sized for the nvmem cell's declared 2048
+bytes (ADM1266_BLACKBOX_MAX_RECORDS * ADM1266_BLACKBOX_SIZE = 32 * 64).
+A device that reports a record_count greater than 32 -- whether due
+to firmware bugs, bus corruption, or a non-responsive slave returning
+0xff -- would walk read_buff past the end of the dev_mem allocation
+on the trailing iterations.
+
+Cap record_count at ADM1266_BLACKBOX_MAX_RECORDS (introduced here)
+before entering the loop and return -EIO on any larger value, so a
+malformed BLACKBOX_INFO response cannot drive the loop out of bounds.
+
+Fixes: 15609d189302 ("hwmon: (pmbus/adm1266) read blackbox")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Link: https://lore.kernel.org/r/20260515-adm1266-fixes-v1-3-1c1ea1349cfe@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -46,6 +46,7 @@
+
+ #define ADM1266_BLACKBOX_OFFSET 0
+ #define ADM1266_BLACKBOX_SIZE 64
++#define ADM1266_BLACKBOX_MAX_RECORDS 32
+
+ #define ADM1266_PMBUS_BLOCK_MAX 255
+
+@@ -360,6 +361,8 @@ static int adm1266_nvmem_read_blackbox(s
+ return -EIO;
+
+ record_count = buf[3];
++ if (record_count > ADM1266_BLACKBOX_MAX_RECORDS)
++ return -EIO;
+
+ for (index = 0; index < record_count; index++) {
+ ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, &index, read_buff);
--- /dev/null
+From a7232f68c43ca62f545049b7f5fbfc75137b843b Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Mon, 18 May 2026 17:52:27 -0700
+Subject: hwmon: (pmbus/adm1266) reject short block-read responses in the GPIO accessors
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit a7232f68c43ca62f545049b7f5fbfc75137b843b upstream.
+
+adm1266_gpio_get() and adm1266_gpio_get_multiple() both compose the
+pin-status word as
+
+ pins_status = read_buf[0] + (read_buf[1] << 8);
+
+right after i2c_smbus_read_block_data(), guarding only against an
+error return. A well-behaved device returns 2 bytes for
+GPIO_STATUS/PDIO_STATUS, but the helper happily reports a 0- or
+1-byte response too. If the device returns 0 bytes, both read_buf
+slots are uninitialized stack memory; if it returns 1 byte, read_buf[1]
+is.
+
+The composed value then flows through set_bit() into the caller's
+*bits in adm1266_gpio_get_multiple(), or into the return value of
+adm1266_gpio_get(), and ends up in userspace via gpiolib (sysfs and
+the char-dev ioctls). That leaks a few bits of kernel stack per
+request on any device whose firmware glitch, bus error, or hostile
+slave produces a short block-read response.
+
+Add the missing length check to both call sites and surface a short
+response as -EIO.
+
+Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-3-e425e4f88139@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -176,6 +176,8 @@ static int adm1266_gpio_get(struct gpio_
+ ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf);
+ if (ret < 0)
+ return ret;
++ if (ret < 2)
++ return -EIO;
+
+ pins_status = read_buf[0] + (read_buf[1] << 8);
+ if (offset < ADM1266_GPIO_NR)
+@@ -196,6 +198,8 @@ static int adm1266_gpio_get_multiple(str
+ ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, read_buf);
+ if (ret < 0)
+ return ret;
++ if (ret < 2)
++ return -EIO;
+
+ status = read_buf[0] + (read_buf[1] << 8);
+
+@@ -208,6 +212,8 @@ static int adm1266_gpio_get_multiple(str
+ ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, read_buf);
+ if (ret < 0)
+ return ret;
++ if (ret < 2)
++ return -EIO;
+
+ status = read_buf[0] + (read_buf[1] << 8);
+
--- /dev/null
+From b86095e3d7dcf2bf80c747349a35912a87a85098 Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Fri, 15 May 2026 15:11:47 -0700
+Subject: hwmon: (pmbus/adm1266) seed timestamp from the real-time clock
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit b86095e3d7dcf2bf80c747349a35912a87a85098 upstream.
+
+adm1266_set_rtc() seeds the chip's SET_RTC register from
+ktime_get_seconds(), which returns CLOCK_MONOTONIC -- i.e. seconds
+since the host last booted, not seconds since the Unix epoch.
+
+The chip stamps that value into every blackbox record it captures.
+Userspace reading those timestamps back expects wall-clock seconds:
+that's what the SET_RTC frame layout documents (datasheet Rev. D,
+Table 84) and what every other consumer of "seconds since epoch"
+assumes. Seeding from CLOCK_MONOTONIC gives blackbox records a
+timestamp that is only meaningful within a single boot of the host
+and silently resets to small values on every reboot.
+
+Switch to ktime_get_real_seconds() so the seed matches what the
+register is documented to hold.
+
+Fixes: 15609d189302 ("hwmon: (pmbus/adm1266) read blackbox")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Link: https://lore.kernel.org/r/20260515-adm1266-fixes-v1-1-1c1ea1349cfe@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -434,7 +434,7 @@ static int adm1266_set_rtc(struct adm126
+ char write_buf[6];
+ int i;
+
+- kt = ktime_get_seconds();
++ kt = ktime_get_real_seconds();
+
+ memset(write_buf, 0, sizeof(write_buf));
+
--- /dev/null
+From bab8c6fb5af8df7e753d196c1262cb78e92ca872 Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Mon, 18 May 2026 17:52:30 -0700
+Subject: hwmon: (pmbus/adm1266) serialize GPIO PMBus accesses with pmbus_lock
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit bab8c6fb5af8df7e753d196c1262cb78e92ca872 upstream.
+
+adm1266_gpio_get(), adm1266_gpio_get_multiple(), and
+adm1266_gpio_dbg_show() all issue PMBus reads against the device but
+none of them take pmbus_lock. The pmbus_core framework holds
+pmbus_lock around its own multi-transaction sequences (notably the
+"set PAGE, then read paged register" pattern used by hwmon
+attributes), so an unlocked GPIO accessor can land between a PAGE
+write and the subsequent paged read in another thread and corrupt
+either side's view of the device state machine.
+
+Take pmbus_lock at the top of each of the three accessors via the
+scope-based guard(). The lock is uncontended in the common case and
+adds only a single mutex round-trip per call.
+
+Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
+Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-6-e425e4f88139@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -173,6 +173,8 @@ static int adm1266_gpio_get(struct gpio_
+ else
+ pmbus_cmd = ADM1266_PDIO_STATUS;
+
++ guard(pmbus_lock)(data->client);
++
+ ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf);
+ if (ret < 0)
+ return ret;
+@@ -195,6 +197,8 @@ static int adm1266_gpio_get_multiple(str
+ unsigned int gpio_nr;
+ int ret;
+
++ guard(pmbus_lock)(data->client);
++
+ ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, read_buf);
+ if (ret < 0)
+ return ret;
+@@ -236,6 +240,8 @@ static void adm1266_gpio_dbg_show(struct
+ int ret;
+ int i;
+
++ guard(pmbus_lock)(data->client);
++
+ for (i = 0; i < ADM1266_GPIO_NR; i++) {
+ write_cmd = adm1266_gpio_mapping[i][1];
+ ret = adm1266_pmbus_block_xfer(data, ADM1266_GPIO_CONFIG, 1, &write_cmd, read_buf);
--- /dev/null
+From 9f1dd8f9491eb840cbea7ffdf4cad031e25f8ae0 Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Mon, 18 May 2026 17:52:31 -0700
+Subject: hwmon: (pmbus/adm1266) serialize NVMEM blackbox read with pmbus_lock
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit 9f1dd8f9491eb840cbea7ffdf4cad031e25f8ae0 upstream.
+
+adm1266_nvmem_read() is the reg_read callback the NVMEM core invokes
+when userspace reads /sys/bus/nvmem/devices/.../nvmem on this chip.
+On the first byte of every read it does a memset of data->dev_mem,
+walks the device blackbox through adm1266_nvmem_read_blackbox()
+(which issues a chain of PMBus block transactions), and then memcpys
+the refreshed buffer out to userspace. None of that runs under
+pmbus_lock today.
+
+Two consequences:
+
+ - The PMBus traffic the refresh issues is not serialised against
+ pmbus_core's own multi-step PAGE+register sequences. A paged
+ hwmon attribute read from another thread can land between a
+ PAGE write and the paged read in either direction and corrupt
+ one side's view of the device state machine.
+
+ - The NVMEM core does not serialise concurrent reg_read calls, so
+ two userspace readers racing at offset 0 can interleave the
+ memset of data->dev_mem with another reader's
+ adm1266_nvmem_read_blackbox() refill or memcpy out, returning
+ torn data to userspace.
+
+Take pmbus_lock at the top of adm1266_nvmem_read() via the
+scope-based guard(). Patch 5 of this series moves
+adm1266_config_nvmem() past pmbus_do_probe() so the lock is
+guaranteed to be live before the callback is reachable from
+userspace.
+
+Fixes: 15609d189302 ("hwmon: (pmbus/adm1266) read blackbox")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-7-e425e4f88139@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -383,6 +383,8 @@ static int adm1266_nvmem_read(void *priv
+ if (offset + bytes > data->nvmem_config.size)
+ return -EINVAL;
+
++ guard(pmbus_lock)(data->client);
++
+ if (offset == 0) {
+ memset(data->dev_mem, 0, data->nvmem_config.size);
+
--- /dev/null
+From 4e4af55aaca7f6d7673d5f9889ad0529db86a048 Mon Sep 17 00:00:00 2001
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Date: Mon, 18 May 2026 17:52:32 -0700
+Subject: hwmon: (pmbus/adm1266) serialize sequencer_state debugfs read with pmbus_lock
+
+From: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+
+commit 4e4af55aaca7f6d7673d5f9889ad0529db86a048 upstream.
+
+adm1266_state_read() backs the sequencer_state debugfs entry and
+issues an i2c_smbus_read_word_data(client, ADM1266_READ_STATE)
+against the device without taking pmbus_lock. pmbus_core holds
+pmbus_lock around its own multi-transaction sequences (notably the
+"set PAGE, then read paged register" pattern used by hwmon
+attributes), so an unlocked debugfs reader can land between a PAGE
+write and the subsequent paged read in another thread. READ_STATE
+itself is not paged, so it cannot corrupt PAGE in flight, but the
+same defensive serialisation that applies to the GPIO accessors
+applies here: any direct device access from outside pmbus_core
+should be ordered with respect to pmbus_core's own.
+
+Take pmbus_lock at the top of adm1266_state_read() via the
+scope-based guard().
+
+Fixes: ed1ff457e187 ("hwmon: (pmbus/adm1266) add debugfs for states")
+Cc: stable@vger.kernel.org
+Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
+Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-8-e425e4f88139@nexthop.ai
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hwmon/pmbus/adm1266.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/hwmon/pmbus/adm1266.c
++++ b/drivers/hwmon/pmbus/adm1266.c
+@@ -334,6 +334,7 @@ static int adm1266_state_read(struct seq
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
++ guard(pmbus_lock)(client);
+ ret = i2c_smbus_read_word_data(client, ADM1266_READ_STATE);
+ if (ret < 0)
+ return ret;
--- /dev/null
+From f19c354dbd457759dfcf1195ab4bdba2bb568323 Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Tue, 19 May 2026 09:50:42 -0400
+Subject: KVM: arm64: vgic: Free private_irqs when init fails after allocation
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit f19c354dbd457759dfcf1195ab4bdba2bb568323 upstream.
+
+Companion to commit 250f25367b58 ("KVM: arm64: Tear down vGIC on
+failed vCPU creation"), which added the missing kvm_vgic_vcpu_destroy()
+call to the kvm_share_hyp() failure path in kvm_arch_vcpu_create(). The
+kvm_vgic_vcpu_init() failure path immediately above it has the same
+shape and still needs the same cleanup.
+
+Call kvm_vgic_vcpu_destroy() when kvm_vgic_vcpu_init() fails so private
+IRQs allocated before a redistributor iodev registration failure are
+released before the failed vCPU is freed.
+
+Fixes: 03b3d00a70b5 ("KVM: arm64: vgic: Allocate private interrupts on demand")
+Cc: stable@vger.kernel.org
+Cc: Will Deacon <will@kernel.org>
+Reviewed-by: Yuan Yao <yaoyuan@linux.alibaba.com>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Link: https://lore.kernel.org/r/20260519135042.2219239-1-michael.bommarito@gmail.com
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/arm64/kvm/arm.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/arch/arm64/kvm/arm.c
++++ b/arch/arm64/kvm/arm.c
+@@ -490,8 +490,10 @@ int kvm_arch_vcpu_create(struct kvm_vcpu
+ kvm_destroy_mpidr_data(vcpu->kvm);
+
+ err = kvm_vgic_vcpu_init(vcpu);
+- if (err)
++ if (err) {
++ kvm_vgic_vcpu_destroy(vcpu);
+ return err;
++ }
+
+ err = kvm_share_hyp(vcpu, vcpu + 1);
+ if (err)
--- /dev/null
+From 9ce754ed8e7ab4e3999767ce1505f85c449ccb07 Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Tue, 19 May 2026 09:25:19 -0400
+Subject: KVM: arm64: vgic-its: Reject restored DTE with out-of-range num_eventid_bits
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit 9ce754ed8e7ab4e3999767ce1505f85c449ccb07 upstream.
+
+Userspace can restore an ITS Device Table Entry whose Size field encodes
+more EventID bits than the virtual ITS supports. The live MAPD path
+rejects that state, but vgic_its_restore_dte() accepts it and stores the
+out-of-range value in dev->num_eventid_bits.
+
+Reject restored DTEs with num_eventid_bits > VITS_TYPER_IDBITS before
+allocating the device. This mirrors the MAPD check and prevents the
+restored state from reaching vgic_its_restore_itt(), where the unchecked
+value can be converted into an oversized scan_its_table() range.
+
+Fixes: 57a9a117154c ("KVM: arm64: vgic-its: Device table save/restore")
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Link: https://lore.kernel.org/r/20260519132519.2142458-1-michael.bommarito@gmail.com
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/arm64/kvm/vgic/vgic-its.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/arm64/kvm/vgic/vgic-its.c
++++ b/arch/arm64/kvm/vgic/vgic-its.c
+@@ -2307,6 +2307,10 @@ static int vgic_its_restore_dte(struct v
+ /* dte entry is valid */
+ offset = (entry & KVM_ITS_DTE_NEXT_MASK) >> KVM_ITS_DTE_NEXT_SHIFT;
+
++ /* Mimic the MAPD behaviour and reject invalid EID bits. */
++ if (num_eventid_bits > VITS_TYPER_IDBITS)
++ return -EINVAL;
++
+ if (!vgic_its_check_id(its, baser, id, NULL))
+ return -EINVAL;
+
--- /dev/null
+From 9a12fa5213cfc391e0eed63902d3be98f0913765 Mon Sep 17 00:00:00 2001
+From: Tina Zhang <zhang_wei@open-hieco.net>
+Date: Fri, 22 May 2026 12:00:14 +0800
+Subject: KVM: SVM: Disable AVIC IPI virtualization on Hygon Family 18h (erratum #1235)
+
+From: Tina Zhang <zhang_wei@open-hieco.net>
+
+commit 9a12fa5213cfc391e0eed63902d3be98f0913765 upstream.
+
+Hygon Family 18h CPUs are derived from AMD Family 17h (Zen1) silicon and
+share the same erratum #1235: hardware may read a stale IsRunning=1 bit
+during ICR write emulation and silently fail to generate an
+AVIC_IPI_FAILURE_TARGET_NOT_RUNNING VM-Exit on the sending vCPU.
+
+The absence of the VM-Exit causes KVM to miss the required wakeup of
+blocking target vCPUs, leading to hung vCPUs and unbounded delays in
+guest execution.
+
+Extend the existing AMD Family 17h erratum #1235 workaround to also cover
+Hygon Family 18h. With IPI virtualization disabled, KVM never sets
+IsRunning=1 in the Physical ID table, so every non-self IPI generates a
+VM-Exit and is correctly emulated.
+
+Fixes: 8de4a1c8164e ("KVM: SVM: Disable (x2)AVIC IPI virtualization if CPU has erratum #1235")
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Tina Zhang <zhang_wei@open-hieco.net>
+Message-ID: <20260522040014.3380201-1-zhang_wei@open-hieco.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/x86/kvm/svm/avic.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/arch/x86/kvm/svm/avic.c
++++ b/arch/x86/kvm/svm/avic.c
+@@ -1252,12 +1252,14 @@ bool __init avic_hardware_setup(void)
+ svm_x86_ops.allow_apicv_in_x2apic_without_x2apic_virtualization = true;
+
+ /*
+- * Disable IPI virtualization for AMD Family 17h CPUs (Zen1 and Zen2)
+- * due to erratum 1235, which results in missed VM-Exits on the sender
+- * and thus missed wake events for blocking vCPUs due to the CPU
+- * failing to see a software update to clear IsRunning.
++ * Disable IPI virtualization for AMD Family 17h (Zen1 and Zen2) and
++ * Hygon Family 18h (derived from AMD Zen1) CPUs due to erratum 1235,
++ * which results in missed VM-Exits on the sender and thus missed wake
++ * events for blocking vCPUs due to the CPU failing to see a software
++ * update to clear IsRunning.
+ */
+- enable_ipiv = enable_ipiv && boot_cpu_data.x86 != 0x17;
++ if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18)
++ enable_ipiv = false;
+
+ amd_iommu_register_ga_log_notifier(&avic_ga_log_notifier);
+
--- /dev/null
+From e3ef9a28f558d1cbf0b42d6dcd16c60da557562b Mon Sep 17 00:00:00 2001
+From: Tiezhu Yang <yangtiezhu@loongson.cn>
+Date: Fri, 22 May 2026 15:05:07 +0800
+Subject: LoongArch: kprobes: Use larch_insn_text_copy() to patch instructions
+
+From: Tiezhu Yang <yangtiezhu@loongson.cn>
+
+commit e3ef9a28f558d1cbf0b42d6dcd16c60da557562b upstream.
+
+On SMP systems, kprobe handlers would occasionally fail to execute on
+certain CPU cores. The issue is hard to reproduce and typically occurs
+randomly under high system load.
+
+The root cause is a software-side instruction hazard. According to the
+LoongArch Reference Manual, while the cache coherency is maintained by
+hardware, software must explicitly use the "IBAR" instruction to ensure
+the instruction fetch unit (IFU) observes the effects of recent stores.
+
+The current arch_arm_kprobe() and arch_disarm_kprobe() only execute the
+"IBAR" barrier (via flush_insn_slot -> local_flush_icache_range) on the
+local CPU. This leaves a vulnerable window where remote CPU cores may
+continue executing stale instructions from their pipelines or prefetch
+buffers, as they have not executed an "IBAR" since the code modification.
+
+Switch to larch_insn_text_copy() to fix this:
+1. Synchronization: It uses stop_machine_cpuslocked() to synchronize all
+ online CPUs, ensuring no CPU is executing the target code area during
+ modification.
+2. Visibility: By passing cpu_online_mask to stop_machine_cpuslocked(),
+ the callback text_copy_cb() is executed on all online cores. Each CPU
+ core invokes local_flush_icache_range() to execute "IBAR", clearing
+ instruction hazards system-wide and ensuring the "break" instruction
+ is visible to the fetch units of all cores.
+3. Robustness: It properly manages memory write permissions (ROX/RW) for
+ the kernel text segment during patching, ensuring compatibility with
+ CONFIG_STRICT_KERNEL_RWX.
+
+Cc: <stable@vger.kernel.org> # 6.18+
+Fixes: 6d4cc40fb5f5 ("LoongArch: Add kprobes support")
+Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
+Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/loongarch/kernel/kprobes.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+--- a/arch/loongarch/kernel/kprobes.c
++++ b/arch/loongarch/kernel/kprobes.c
+@@ -60,16 +60,18 @@ NOKPROBE_SYMBOL(arch_prepare_kprobe);
+ /* Install breakpoint in text */
+ void arch_arm_kprobe(struct kprobe *p)
+ {
+- *p->addr = KPROBE_BP_INSN;
+- flush_insn_slot(p);
++ u32 insn = KPROBE_BP_INSN;
++
++ larch_insn_text_copy(p->addr, &insn, LOONGARCH_INSN_SIZE);
+ }
+ NOKPROBE_SYMBOL(arch_arm_kprobe);
+
+ /* Remove breakpoint from text */
+ void arch_disarm_kprobe(struct kprobe *p)
+ {
+- *p->addr = p->opcode;
+- flush_insn_slot(p);
++ u32 insn = p->opcode;
++
++ larch_insn_text_copy(p->addr, &insn, LOONGARCH_INSN_SIZE);
+ }
+ NOKPROBE_SYMBOL(arch_disarm_kprobe);
+
--- /dev/null
+From 0ccc9d47cf020994097ff51827cebd04aa2b0bf4 Mon Sep 17 00:00:00 2001
+From: Huacai Chen <chenhuacai@loongson.cn>
+Date: Thu, 21 May 2026 20:58:40 +0800
+Subject: LoongArch: Remove unused code to avoid build warning
+
+From: Huacai Chen <chenhuacai@loongson.cn>
+
+commit 0ccc9d47cf020994097ff51827cebd04aa2b0bf4 upstream.
+
+After commit feee6b2989165631b1 ("mm/memory_hotplug: shrink zones when
+offlining memory"), __remove_pages() doesn't need the "zone" parameter
+so the "page" variable is also unused. Remove the unused code to avoid
+such build warning:
+
+arch/loongarch/mm/init.c: In function 'arch_remove_memory':
+arch/loongarch/mm/init.c:134:22: warning: variable 'page' set but not used [-Wunused-but-set-variable=]
+ 134 | struct page *page = pfn_to_page(start_pfn);
+
+Cc: <stable@vger.kernel.org>
+Reviewed-by: Guo Ren <guoren@kernel.org>
+Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/loongarch/mm/init.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+--- a/arch/loongarch/mm/init.c
++++ b/arch/loongarch/mm/init.c
+@@ -97,11 +97,7 @@ void arch_remove_memory(u64 start, u64 s
+ {
+ unsigned long start_pfn = start >> PAGE_SHIFT;
+ unsigned long nr_pages = size >> PAGE_SHIFT;
+- struct page *page = pfn_to_page(start_pfn);
+
+- /* With altmap the first mapped page is offset from @start */
+- if (altmap)
+- page += vmem_altmap_offset(altmap);
+ __remove_pages(start_pfn, nr_pages, altmap);
+ }
+ #endif
--- /dev/null
+From 5a759b120e31aa3ed914d98b51eb1755235250f2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C5=81ukasz=20Lebiedzi=C5=84ski?= <kernel@lvkasz.us>
+Date: Mon, 6 Apr 2026 15:56:27 +0200
+Subject: phy: exynos5-usbdrd: fix USB 2.0 HS PHY tuning values for Exynos7870
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Łukasz Lebiedziński <kernel@lvkasz.us>
+
+commit 5a759b120e31aa3ed914d98b51eb1755235250f2 upstream.
+
+The existing PHYPARAM0 tuning values for Exynos7870 are incorrect,
+causing the USB 2.0 PHY to fail high-speed negotiation and fall back
+to full-speed (12Mbps) operation.
+
+Fix TXVREFTUNE (transmitter voltage reference) from 14 to 3,
+TXRESTUNE (transmitter impedance) from 3 to 2, and SQRXTUNE
+(squelch threshold) from 6 to 5. Also explicitly set
+TXPREEMPPULSETUNE to 0, which was previously missing from the
+tuning table despite being included in the register mask.
+
+All values are derived from the vendor kernel for the Samsung
+Galaxy A6 (SM-A600FN), as no public hardware documentation is
+available for the Exynos7870 USB DRD PHY. With these corrections,
+the PHY successfully negotiates high-speed (480Mbps) operation.
+
+Fixes: 588d5d20ca8d ("phy: exynos5-usbdrd: add exynos7870 USBDRD support")
+Cc: stable@vger.kernel.org
+Tested-by: Kaustabh Chakraborty <kauschluss@disroot.org>
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
+Signed-off-by: Łukasz Lebiedziński <kernel@lvkasz.us>
+Link: https://patch.msgid.link/20260406135627.234835-1-kernel@lvkasz.us
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/phy/samsung/phy-exynos5-usbdrd.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/drivers/phy/samsung/phy-exynos5-usbdrd.c
++++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c
+@@ -1905,13 +1905,14 @@ const struct exynos5_usbdrd_phy_tuning e
+ PHYPARAM0_TXPREEMPAMPTUNE | PHYPARAM0_TXHSXVTUNE |
+ PHYPARAM0_TXFSLSTUNE | PHYPARAM0_SQRXTUNE |
+ PHYPARAM0_OTGTUNE | PHYPARAM0_COMPDISTUNE),
+- (FIELD_PREP_CONST(PHYPARAM0_TXVREFTUNE, 14) |
++ (FIELD_PREP_CONST(PHYPARAM0_TXVREFTUNE, 3) |
+ FIELD_PREP_CONST(PHYPARAM0_TXRISETUNE, 1) |
+- FIELD_PREP_CONST(PHYPARAM0_TXRESTUNE, 3) |
++ FIELD_PREP_CONST(PHYPARAM0_TXRESTUNE, 2) |
++ FIELD_PREP_CONST(PHYPARAM0_TXPREEMPPULSETUNE, 0) |
+ FIELD_PREP_CONST(PHYPARAM0_TXPREEMPAMPTUNE, 0) |
+ FIELD_PREP_CONST(PHYPARAM0_TXHSXVTUNE, 0) |
+ FIELD_PREP_CONST(PHYPARAM0_TXFSLSTUNE, 3) |
+- FIELD_PREP_CONST(PHYPARAM0_SQRXTUNE, 6) |
++ FIELD_PREP_CONST(PHYPARAM0_SQRXTUNE, 5) |
+ FIELD_PREP_CONST(PHYPARAM0_OTGTUNE, 2) |
+ FIELD_PREP_CONST(PHYPARAM0_COMPDISTUNE, 3))),
+ PHY_TUNING_ENTRY_LAST
--- /dev/null
+From 80305760d7a55b884fb9023c490b75568d1ea0b1 Mon Sep 17 00:00:00 2001
+From: Nitin Rawat <nitin.rawat@oss.qualcomm.com>
+Date: Wed, 15 Apr 2026 16:18:51 +0530
+Subject: phy: qcom-qmp-ufs: Fix kaanapali PHY PLL lock failure after SM8650 G4 fix
+
+From: Nitin Rawat <nitin.rawat@oss.qualcomm.com>
+
+commit 80305760d7a55b884fb9023c490b75568d1ea0b1 upstream.
+
+Commit 81af9e40e2e4 ("phy: qcom: qmp-ufs: Fix SM8650 PCS table for Gear 4")
+moved QPHY_V6_PCS_UFS_PLL_CNTL register configuration from the shared
+sm8650_ufsphy_g5_pcs table to the SM8650-specific sm8650_ufsphy_pcs base
+table to fix Gear 4 operation on SM8650.
+
+However, this change inadvertently broke kaanapali and SM8750 SoCs
+which also rely on the shared sm8650_ufsphy_g5_pcs table for Gear 5
+configuration but use their own sm8750_ufsphy_pcs base table. After the
+change, kaanapali PHYs are left without the required PLL_CNTL = 0x33
+setting, causing the PHY PLL to remain at its hardware reset default
+value, preventing PLL lock and resulting in DME_LINKSTARTUP timeouts.
+
+Fix this by adding the missing QPHY_V6_PCS_UFS_PLL_CNTL = 0x33 entry
+to the sm8750_ufsphy_pcs table, mirroring what the original commit
+already did for sm8650_ufsphy_pcs.
+
+Cc: stable@vger.kernel.org # v6.19.12
+Fixes: 81af9e40e2e4 ("phy: qcom: qmp-ufs: Fix SM8650 PCS table for Gear 4")
+Signed-off-by: Nitin Rawat <nitin.rawat@oss.qualcomm.com>
+Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
+Link: https://patch.msgid.link/20260415104851.2763238-1-nitin.rawat@oss.qualcomm.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
++++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
+@@ -1050,6 +1050,7 @@ static const struct qmp_phy_init_tbl sm8
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1, 0x43),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PCS_CTRL1, 0x40),
++ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x33),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2, 0x68),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S4, 0x0e),
--- /dev/null
+From da110228b54f2e2143d97ea7151e0dc22e539d67 Mon Sep 17 00:00:00 2001
+From: Wayne Chang <waynec@nvidia.com>
+Date: Mon, 4 May 2026 11:33:05 +0800
+Subject: phy: tegra: xusb: Fix per-pad high-speed termination calibration
+
+From: Wayne Chang <waynec@nvidia.com>
+
+commit da110228b54f2e2143d97ea7151e0dc22e539d67 upstream.
+
+The existing code reads a single hs_term_range_adj value from bit field
+[10:7] of FUSE_SKU_CALIB_0 and applies it to all USB2 pads uniformly.
+However, on SoCs that support per-pad termination, each pad has its own
+hs_term_range_adj field: pad 0 in FUSE_SKU_CALIB_0[10:7], and pads 1-3
+in FUSE_USB_CALIB_EXT_0 at bit offsets [8:5], [12:9], and [16:13]
+respectively.
+
+Fix the calibration by reading per-pad values from the appropriate fuse
+registers. For SoCs that do not support per-pad termination, replicate
+pad 0's value to all pads to maintain existing behavior.
+
+Add a has_per_pad_term flag to the SoC data to indicate whether per-pad
+termination values are available in FUSE_USB_CALIB_EXT_0.
+
+Fixes: 1ef535c6ba8e ("phy: tegra: xusb: Add Tegra194 support")
+Cc: stable@vger.kernel.org
+Signed-off-by: Wayne Chang <waynec@nvidia.com>
+Signed-off-by: Wei-Cheng Chen <weichengc@nvidia.com>
+Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
+Tested-by: Jon Hunter <jonathanh@nvidia.com>
+Link: https://patch.msgid.link/20260504033305.2283145-1-weichengc@nvidia.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/phy/tegra/xusb-tegra186.c | 33 ++++++++++++++++++++++++++-------
+ drivers/phy/tegra/xusb.h | 1 +
+ 2 files changed, 27 insertions(+), 7 deletions(-)
+
+--- a/drivers/phy/tegra/xusb-tegra186.c
++++ b/drivers/phy/tegra/xusb-tegra186.c
+@@ -20,8 +20,8 @@
+ /* FUSE USB_CALIB registers */
+ #define HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? (11 + (x - 1) * 6) : 0)
+ #define HS_CURR_LEVEL_PAD_MASK 0x3f
+-#define HS_TERM_RANGE_ADJ_SHIFT 7
+-#define HS_TERM_RANGE_ADJ_MASK 0xf
++#define HS_TERM_RANGE_ADJ_PADX_SHIFT(x) ((x) ? (5 + (x - 1) * 4) : 7)
++#define HS_TERM_RANGE_ADJ_PAD_MASK 0xf
+ #define HS_SQUELCH_SHIFT 29
+ #define HS_SQUELCH_MASK 0x7
+
+@@ -253,7 +253,7 @@
+ struct tegra_xusb_fuse_calibration {
+ u32 *hs_curr_level;
+ u32 hs_squelch;
+- u32 hs_term_range_adj;
++ u32 *hs_term_range_adj;
+ u32 rpd_ctrl;
+ };
+
+@@ -930,7 +930,7 @@ static int tegra186_utmi_phy_power_on(st
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+ value &= ~TERM_RANGE_ADJ(~0);
+- value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj);
++ value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj[index]);
+ value &= ~RPD_CTRL(~0);
+ value |= RPD_CTRL(priv->calib.rpd_ctrl);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+@@ -1464,17 +1464,23 @@ static const char * const tegra186_usb3_
+ static int
+ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
+ {
++ const struct tegra_xusb_padctl_soc *soc = padctl->base.soc;
+ struct device *dev = padctl->base.dev;
+ unsigned int i, count;
+ u32 value, *level;
++ u32 *hs_term_range_adj;
+ int err;
+
+- count = padctl->base.soc->ports.usb2.count;
++ count = soc->ports.usb2.count;
+
+ level = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
+ if (!level)
+ return -ENOMEM;
+
++ hs_term_range_adj = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
++ if (!hs_term_range_adj)
++ return -ENOMEM;
++
+ err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
+ if (err)
+ return dev_err_probe(dev, err,
+@@ -1490,8 +1496,8 @@ tegra186_xusb_read_fuse_calibration(stru
+
+ padctl->calib.hs_squelch = (value >> HS_SQUELCH_SHIFT) &
+ HS_SQUELCH_MASK;
+- padctl->calib.hs_term_range_adj = (value >> HS_TERM_RANGE_ADJ_SHIFT) &
+- HS_TERM_RANGE_ADJ_MASK;
++ hs_term_range_adj[0] = (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(0)) &
++ HS_TERM_RANGE_ADJ_PAD_MASK;
+
+ err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value);
+ if (err) {
+@@ -1503,6 +1509,17 @@ tegra186_xusb_read_fuse_calibration(stru
+
+ padctl->calib.rpd_ctrl = (value >> RPD_CTRL_SHIFT) & RPD_CTRL_MASK;
+
++ for (i = 1; i < count; i++) {
++ if (soc->has_per_pad_term)
++ hs_term_range_adj[i] =
++ (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(i)) &
++ HS_TERM_RANGE_ADJ_PAD_MASK;
++ else
++ hs_term_range_adj[i] = hs_term_range_adj[0];
++ }
++
++ padctl->calib.hs_term_range_adj = hs_term_range_adj;
++
+ return 0;
+ }
+
+@@ -1708,6 +1725,7 @@ const struct tegra_xusb_padctl_soc tegra
+ .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
+ .supports_gen2 = true,
+ .poll_trk_completed = true,
++ .has_per_pad_term = true,
+ };
+ EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc);
+
+@@ -1732,6 +1750,7 @@ const struct tegra_xusb_padctl_soc tegra
+ .trk_hw_mode = false,
+ .trk_update_on_idle = true,
+ .supports_lp_cfg_en = true,
++ .has_per_pad_term = true,
+ };
+ EXPORT_SYMBOL_GPL(tegra234_xusb_padctl_soc);
+ #endif
+--- a/drivers/phy/tegra/xusb.h
++++ b/drivers/phy/tegra/xusb.h
+@@ -436,6 +436,7 @@ struct tegra_xusb_padctl_soc {
+ bool trk_hw_mode;
+ bool trk_update_on_idle;
+ bool supports_lp_cfg_en;
++ bool has_per_pad_term;
+ };
+
+ struct tegra_xusb_padctl {
--- /dev/null
+From 0ce1bc9e46ecabe84772bb561e373c0d9876d6f2 Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Wed, 13 May 2026 13:53:24 -0400
+Subject: RDMA/siw: Reject MPA FPDU length underflow before signed receive math
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit 0ce1bc9e46ecabe84772bb561e373c0d9876d6f2 upstream.
+
+A malicious connected siw peer can send an iWARP FPDU whose MPA length
+field (c_hdr->mpa_len, 16 bit big-endian, peer-controlled) is smaller
+than the fixed DDP/RDMAP header for the announced opcode. Soft-iWARP
+parses the full header in siw_get_hdr() based on iwarp_pktinfo[opcode]
+.hdr_len, but never compares mpa_len against that header length.
+
+siw_tcp_rx_data() then derives
+
+ srx->fpdu_part_rem = be16_to_cpu(mpa_len) - fpdu_part_rcvd
+ + MPA_HDR_SIZE;
+
+where fpdu_part_rcvd equals iwarp_pktinfo[opcode].hdr_len at this
+point. For a tagged WRITE (hdr_len 16, MPA_HDR_SIZE 2) the smallest
+on-wire mpa_len of 0 yields fpdu_part_rem = -14, and any mpa_len below
+hdr_len - MPA_HDR_SIZE underflows to a negative int.
+
+The signed value then flows into siw_proc_write()/siw_proc_rresp() as
+
+ bytes = min(srx->fpdu_part_rem, srx->skb_new);
+
+is handed to siw_check_mem() as an int len (whose interval check
+addr + len > mem->va + mem->len is satisfied for a valid base when
+len is negative), and reaches siw_rx_data() -> siw_rx_kva() /
+siw_rx_umem() -> skb_copy_bits() as a signed copy length. The header
+copy branch in skb_copy_bits() promotes that to size_t, producing a
+multi-gigabyte read.
+
+KASAN under a KUnit harness that drives the real kernel TCP receive
+path -- a loopback AF_INET socketpair, the malformed FPDU written via
+kernel_sendmsg, sk_data_ready firing in softirq, tcp_read_sock
+dispatching to siw_tcp_rx_data -- reports:
+
+ BUG: KASAN: use-after-free in skb_copy_bits+0x284/0x480
+ Read of size 4294967295 at addr ffff888...
+ Call Trace:
+ skb_copy_bits
+ siw_rx_kva
+ siw_rx_data
+ siw_check_mem
+ siw_proc_write
+ siw_tcp_rx_data
+ __tcp_read_sock
+ siw_qp_llp_data_ready
+ tcp_data_ready
+ tcp_data_queue
+
+Add the missing invariant at the earliest point where the peer header
+is fully assembled. iwarp_pktinfo[*].hdr_len - MPA_HDR_SIZE is exactly
+the value the siw transmitter uses as the minimum mpa_len for each
+opcode (drivers/infiniband/sw/siw/siw_qp.c:33), so this matches the
+protocol contract. Out-of-range FPDUs terminate the connection with
+TERM_ERROR_LAYER_LLP / LLP_ETYPE_MPA / LLP_ECODE_FPDU_START -- which
+is RFC 5044 Section 8 error code 3 ("Marker and ULPDU Length fields
+do not agree on the start of an FPDU"), the correct framing-error
+class for this inconsistency.
+
+Fixes: 8b6a361b8c48 ("rdma/siw: receive path")
+Link: https://patch.msgid.link/r/20260513175325.2042630-2-michael.bommarito@gmail.com
+Cc: stable@vger.kernel.org
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Acked-by: Bernard Metzler <bernard.metzler@linux.dev>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/infiniband/sw/siw/siw_qp_rx.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/infiniband/sw/siw/siw_qp_rx.c
++++ b/drivers/infiniband/sw/siw/siw_qp_rx.c
+@@ -1082,6 +1082,21 @@ static int siw_get_hdr(struct siw_rx_str
+ }
+
+ /*
++ * Peer-controlled mpa_len must not underflow srx->fpdu_part_rem
++ * in siw_tcp_rx_data(); a negative value flows as a signed copy
++ * length into siw_check_mem() and skb_copy_bits().
++ */
++ if (unlikely(be16_to_cpu(c_hdr->mpa_len) + MPA_HDR_SIZE <
++ iwarp_pktinfo[opcode].hdr_len)) {
++ pr_warn_ratelimited("siw: short mpa_len %u for opcode %u (hdr_len %u)\n",
++ be16_to_cpu(c_hdr->mpa_len), opcode,
++ iwarp_pktinfo[opcode].hdr_len);
++ siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_LLP,
++ LLP_ETYPE_MPA, LLP_ECODE_FPDU_START, 0);
++ return -EINVAL;
++ }
++
++ /*
+ * DDP/RDMAP header receive completed. Check if the current
+ * DDP segment starts a new RDMAP message or continues a previously
+ * started RDMAP message.
--- /dev/null
+From 0e9d0e7a7c78db7aa1c13796c65cfe0aefa54a5b Mon Sep 17 00:00:00 2001
+From: Osama Abdelkader <osama.abdelkader@gmail.com>
+Date: Thu, 14 May 2026 19:36:41 +0200
+Subject: riscv: kvm: return SBI_ERR_FAILURE for pmu_event_info() when OOM
+
+From: Osama Abdelkader <osama.abdelkader@gmail.com>
+
+commit 0e9d0e7a7c78db7aa1c13796c65cfe0aefa54a5b upstream.
+
+kvm_riscv_vcpu_pmu_event_info() returned -ENOMEM from the
+SBI extension handler, which caused kvm_riscv_vcpu_sbi_ecall()
+to abort KVM_RUN and surface the error to userspace instead of
+completing the ECALL with a negative SBI error in a0.
+Use SBI_ERR_FAILURE and the normal retdata path, matching other PMU
+handlers and kvm_sbi_ext_pmu_handler comment.
+
+Fixes: e309fd113b9f ("RISC-V: KVM: Implement get event info function")
+Cc: stable@vger.kernel.org
+Signed-off-by: Osama Abdelkader <osama.abdelkader@gmail.com>
+Reviewed-by: Anup Patel <anup@brainfault.org>
+Link: https://lore.kernel.org/r/20260514173642.41448-2-osama.abdelkader@gmail.com
+Signed-off-by: Anup Patel <anup@brainfault.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/riscv/kvm/vcpu_pmu.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/arch/riscv/kvm/vcpu_pmu.c
++++ b/arch/riscv/kvm/vcpu_pmu.c
+@@ -478,8 +478,10 @@ int kvm_riscv_vcpu_pmu_event_info(struct
+ }
+
+ einfo = kzalloc(shmem_size, GFP_KERNEL);
+- if (!einfo)
+- return -ENOMEM;
++ if (!einfo) {
++ ret = SBI_ERR_FAILURE;
++ goto out;
++ }
+
+ ret = kvm_vcpu_read_guest(vcpu, shmem, einfo, shmem_size);
+ if (ret) {
--- /dev/null
+From 0835ee26938e15eccd70f7d33da386b6490f9449 Mon Sep 17 00:00:00 2001
+From: Osama Abdelkader <osama.abdelkader@gmail.com>
+Date: Thu, 14 May 2026 19:36:40 +0200
+Subject: riscv: kvm: return SBI_ERR_FAILURE for pmu_snapshot_set_shmem() when OOM
+
+From: Osama Abdelkader <osama.abdelkader@gmail.com>
+
+commit 0835ee26938e15eccd70f7d33da386b6490f9449 upstream.
+
+kvm_riscv_vcpu_pmu_snapshot_set_shmem() returned -ENOMEM from the
+SBI extension handler, which caused kvm_riscv_vcpu_sbi_ecall() to
+abort KVM_RUN and surface the error to userspace instead of
+ompleting the ECALL with a negative SBI error in a0.
+Use SBI_ERR_FAILURE and the normal retdata path, matching other PMU
+handlers and kvm_sbi_ext_pmu_handler comment.
+
+Fixes: c2f41ddbcdd7 ("RISC-V: KVM: Implement SBI PMU Snapshot feature")
+Cc: stable@vger.kernel.org
+Signed-off-by: Osama Abdelkader <osama.abdelkader@gmail.com>
+Reviewed-by: Anup Patel <anup@brainfault.org>
+Link: https://lore.kernel.org/r/20260514173642.41448-1-osama.abdelkader@gmail.com
+Signed-off-by: Anup Patel <anup@brainfault.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/riscv/kvm/vcpu_pmu.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/arch/riscv/kvm/vcpu_pmu.c
++++ b/arch/riscv/kvm/vcpu_pmu.c
+@@ -431,8 +431,10 @@ int kvm_riscv_vcpu_pmu_snapshot_set_shme
+ }
+
+ kvpmu->sdata = kzalloc(snapshot_area_size, GFP_ATOMIC);
+- if (!kvpmu->sdata)
+- return -ENOMEM;
++ if (!kvpmu->sdata) {
++ sbiret = SBI_ERR_FAILURE;
++ goto out;
++ }
+
+ /* No need to check writable slot explicitly as kvm_vcpu_write_guest does it internally */
+ if (kvm_vcpu_write_guest(vcpu, saddr, kvpmu->sdata, snapshot_area_size)) {
--- /dev/null
+From b52a8d52c3125ec9a93106ed816582368de34426 Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Sun, 19 Apr 2026 17:04:20 -0400
+Subject: scsi: isci: Fix use-after-free in device removal path
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit b52a8d52c3125ec9a93106ed816582368de34426 upstream.
+
+The ISCI completion tasklet is initialized in isci_host_alloc()
+(drivers/scsi/isci/init.c:496) and scheduled from both MSI-X and legacy
+interrupt handlers (drivers/scsi/isci/host.c:223,613).
+
+isci_host_deinit() stops the controller and waits for stop completion,
+but it never kills completion_tasklet before teardown continues. A
+top-of-function tasklet_kill() is not sufficient here: interrupts are
+only disabled when isci_host_stop_complete() runs, so until
+wait_for_stop() returns the IRQ handlers can still requeue the
+tasklet. The tasklet callback also re-enables interrupts after draining
+completions, so killing the tasklet before the source is quiesced leaves
+the same race open.
+
+Once wait_for_stop() returns, no further IRQ-driven scheduling can
+occur. Kill completion_tasklet there so teardown cannot race a queued
+tasklet running on a dead ihost. On remove or unload, the stale callback
+can otherwise dereference ihost and touch ihost->smu_registers after the
+host lifetime ends.
+
+A UML + KASAN analogue reproduced the failure class both with no
+tasklet_kill() and with tasklet_kill() placed before source quiesce, and
+stayed clean once the kill happened after quiescing the scheduling
+source.
+
+This mirrors commit f6ab594672d4 ("scsi: aic94xx: fix use-after-free in
+device removal path"), but ISCI needs the kill after wait_for_stop().
+
+Fixes: 6f231dda6808 ("isci: Intel(R) C600 Series Chipset Storage Control Unit Driver")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7
+Assisted-by: Codex:gpt-5-4
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Link: https://patch.msgid.link/20260419210420.2134639-1-michael.bommarito@gmail.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/scsi/isci/host.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/scsi/isci/host.c
++++ b/drivers/scsi/isci/host.c
+@@ -1252,6 +1252,9 @@ void isci_host_deinit(struct isci_host *
+
+ wait_for_stop(ihost);
+
++ /* No further IRQ-driven scheduling can happen past wait_for_stop(). */
++ tasklet_kill(&ihost->completion_tasklet);
++
+ /* phy stop is after controller stop to allow port and device to
+ * go idle before shutting down the phys, but the expectation is
+ * that i/o has been shut off well before we reach this
wifi-cfg80211-advance-loop-vars-in-cfg80211_merge_profile.patch
af_unix-fix-uaf-read-of-tail-len-in-unix_stream_data_wait.patch
wifi-mac80211-consume-only-present-negotiated-ttlm-maps.patch
+cifs-fix-busy-dentry-used-after-unmounting.patch
+tracing-do-not-call-map-ops-elt_free-if-elt_alloc-fails.patch
+arm64-probes-handle-probes-on-hinted-conditional-branch-instructions.patch
+kvm-arm64-vgic-its-reject-restored-dte-with-out-of-range-num_eventid_bits.patch
+kvm-arm64-vgic-free-private_irqs-when-init-fails-after-allocation.patch
+kvm-svm-disable-avic-ipi-virtualization-on-hygon-family-18h-erratum-1235.patch
+riscv-kvm-return-sbi_err_failure-for-pmu_snapshot_set_shmem-when-oom.patch
+riscv-kvm-return-sbi_err_failure-for-pmu_event_info-when-oom.patch
+virt-sev-guest-explicitly-leak-pages-in-unknown-state.patch
+hwmon-pmbus-adm1266-serialize-nvmem-blackbox-read-with-pmbus_lock.patch
+drm-bridge-chipone-icn6211-use-devm_drm_bridge_add-in-i2c-probe.patch
+spi-qup-fix-error-pointer-deref-after-dma-setup-failure.patch
+phy-exynos5-usbdrd-fix-usb-2.0-hs-phy-tuning-values-for-exynos7870.patch
+phy-tegra-xusb-fix-per-pad-high-speed-termination-calibration.patch
+phy-qcom-qmp-ufs-fix-kaanapali-phy-pll-lock-failure-after-sm8650-g4-fix.patch
+scsi-isci-fix-use-after-free-in-device-removal-path.patch
+spi-ep93xx-fix-error-pointer-deref-after-dma-setup-failure.patch
+spi-sprd-fix-error-pointer-deref-after-dma-setup-failure.patch
+spi-ti-qspi-fix-use-after-free-after-dma-setup-failure.patch
+rdma-siw-reject-mpa-fpdu-length-underflow-before-signed-receive-math.patch
+fwctl-pds-validate-rpc-input-size-before-parsing.patch
+loongarch-kprobes-use-larch_insn_text_copy-to-patch-instructions.patch
+loongarch-remove-unused-code-to-avoid-build-warning.patch
+device-property-set-fwnode-secondary-to-null-in-fwnode_init.patch
+drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on-prior-line-used.patch
+drm-msm-fix-shrinker-deadlock.patch
+drm-v3d-fix-use-after-free-of-cpu-job-query-arrays-on-error-path.patch
+drm-v3d-release-indirect-csd-gem-reference-on-cpu-job-free.patch
+drm-virtio-use-uninterruptible-resv-lock-for-plane-updates.patch
+drm-amdgpu-vpe-force-collaborate-sync-after-trap.patch
+drm-bridge-it66121-acquire-reset-gpio-in-probe.patch
+drm-bridge-megachips-remove-bridge-when-irq-request-fails.patch
+drm-amd-display-fix-integer-overflow-in-bios_get_image.patch
+drm-amd-display-validate-gpio-pin-lut-table-size-before-iterating.patch
+drm-amd-display-validate-payload-length-and-link_index-in-dc_process_dmub_aux_transfer_async.patch
+batman-adv-v-stop-ogmv2-on-disabled-interface.patch
+batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failure.patch
+batman-adv-tvlv-reject-oversized-tvlv-packets.patch
+batman-adv-iv-recover-ogm-scheduling-after-forward-packet-error.patch
+batman-adv-mcast-fix-use-after-free-in-orig_node-rcu-release.patch
+batman-adv-clear-current-gateway-during-teardown.patch
+batman-adv-dat-handle-forward-allocation-error.patch
+batman-adv-fix-fragment-reassembly-length-accounting.patch
+batman-adv-fix-tp_meter-counter-underflow-during-shutdown.patch
+batman-adv-frag-disallow-unicast-fragment-in-fragment.patch
+batman-adv-bla-fix-report_work-leak-on-backbone_gw-purge.patch
+batman-adv-bla-avoid-double-decrement-of-bla.num_requests.patch
+batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dropped-interface.patch
+batman-adv-tp_meter-avoid-use-of-uninit-sender-vars.patch
+batman-adv-tp_meter-directly-shut-down-timer-on-cleanup.patch
+batman-adv-tp_meter-fix-tp_vars-reference-leak-in-receiver-shutdown.patch
+batman-adv-tp_meter-fix-race-condition-in-send-error-reporting.patch
+batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch
+batman-adv-tt-fix-toctou-race-for-reported-vlans.patch
+batman-adv-tt-reject-oversized-local-tvlv-buffers.patch
+batman-adv-tt-avoid-empty-vlan-responses.patch
+batman-adv-tt-fix-negative-last_changeset_len.patch
+batman-adv-tt-fix-negative-tt_buff_len.patch
+batman-adv-tt-prevent-tvlv-entry-number-overflow.patch
+hwmon-pmbus-adm1266-seed-timestamp-from-the-real-time-clock.patch
+hwmon-pmbus-adm1266-reject-implausible-blackbox-record_count.patch
+hwmon-pmbus-adm1266-include-pec-byte-in-pmbus_block_xfer-read-buffer.patch
+hwmon-pmbus-adm1266-bounce-blackbox-records-through-a-protocol-sized-buffer.patch
+hwmon-pmbus-adm1266-cap-pdio-scan-in-get_multiple-at-adm1266_pdio_nr.patch
+hwmon-pmbus-adm1266-don-t-clobber-gpio-bits-before-pdio-read-in-get_multiple.patch
+hwmon-pmbus-adm1266-register-the-gpio_chip-after-pmbus_do_probe.patch
+hwmon-pmbus-adm1266-register-the-nvmem-device-after-pmbus_do_probe.patch
+hwmon-pmbus-adm1266-reject-short-block-read-responses-in-the-gpio-accessors.patch
+hwmon-pmbus-adm1266-serialize-gpio-pmbus-accesses-with-pmbus_lock.patch
+hwmon-pmbus-adm1266-serialize-sequencer_state-debugfs-read-with-pmbus_lock.patch
--- /dev/null
+From 5e121a81667a83e9a01d62b429e340f5a4a84abc Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Tue, 12 May 2026 09:48:49 +0200
+Subject: spi: ep93xx: fix error pointer deref after DMA setup failure
+
+From: Johan Hovold <johan@kernel.org>
+
+commit 5e121a81667a83e9a01d62b429e340f5a4a84abc upstream.
+
+The driver falls back to PIO mode if DMA setup fails during probe.
+
+Make sure to the clear the DMA channel pointers on setup failure to
+avoid dereferencing an error pointer on later probe errors or driver
+unbind.
+
+This issue was flagged by Sashiko when reviewing a devres allocation
+conversion patch.
+
+Fixes: e79e7c2df627 ("spi: ep93xx: add DT support for Cirrus EP93xx")
+Link: https://sashiko.dev/#/patchset/20260429091333.165363-1-johan%40kernel.org?part=10
+Cc: stable@vger.kernel.org # 6.12
+Cc: Nikita Shubin <nikita.shubin@maquefel.me>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Acked-by: Nikita Shubin <nikita.shubin@maquefel.me>
+Link: https://patch.msgid.link/20260512074849.915143-1-johan@kernel.org
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/spi/spi-ep93xx.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/spi/spi-ep93xx.c
++++ b/drivers/spi/spi-ep93xx.c
+@@ -582,12 +582,14 @@ static int ep93xx_spi_setup_dma(struct d
+ espi->dma_rx = dma_request_chan(dev, "rx");
+ if (IS_ERR(espi->dma_rx)) {
+ ret = dev_err_probe(dev, PTR_ERR(espi->dma_rx), "rx DMA setup failed");
++ espi->dma_rx = NULL;
+ goto fail_free_page;
+ }
+
+ espi->dma_tx = dma_request_chan(dev, "tx");
+ if (IS_ERR(espi->dma_tx)) {
+ ret = dev_err_probe(dev, PTR_ERR(espi->dma_tx), "tx DMA setup failed");
++ espi->dma_tx = NULL;
+ goto fail_release_rx;
+ }
+
--- /dev/null
+From a7e8f3efd50a165ba0189f6dc57f7e51a7d149db Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Tue, 12 May 2026 09:43:34 +0200
+Subject: spi: qup: fix error pointer deref after DMA setup failure
+
+From: Johan Hovold <johan@kernel.org>
+
+commit a7e8f3efd50a165ba0189f6dc57f7e51a7d149db upstream.
+
+The driver falls back to PIO mode if DMA setup fails during probe.
+
+Make sure to the clear the DMA channel pointers on setup failure to
+avoid dereferencing an error pointer (or attempting to release a channel
+a second time) on later probe errors or driver unbind.
+
+This issue was flagged by Sashiko when reviewing a devres allocation
+conversion patch.
+
+Fixes: 612762e82ae6 ("spi: qup: Add DMA capabilities")
+Link: https://sashiko.dev/#/patchset/20260505072909.618363-1-johan%40kernel.org?part=4
+Cc: stable@vger.kernel.org # 4.1
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Link: https://patch.msgid.link/20260512074334.914735-1-johan@kernel.org
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/spi/spi-qup.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -996,8 +996,11 @@ static int spi_qup_init_dma(struct spi_c
+
+ err:
+ dma_release_channel(host->dma_tx);
++ host->dma_tx = NULL;
+ err_tx:
+ dma_release_channel(host->dma_rx);
++ host->dma_rx = NULL;
++
+ return ret;
+ }
+
--- /dev/null
+From 3d67fffb74267772d461c02c67f1eff893ad547d Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Tue, 12 May 2026 09:47:33 +0200
+Subject: spi: sprd: fix error pointer deref after DMA setup failure
+
+From: Johan Hovold <johan@kernel.org>
+
+commit 3d67fffb74267772d461c02c67f1eff893ad547d upstream.
+
+The driver falls back to PIO mode if DMA setup fails during probe.
+
+Make sure to check the dma.enabled flag before trying to release the DMA
+channels also on late probe errors to avoid dereferencing an error
+pointer (or attempting to release a channel a second time).
+
+This issue was flagged by Sashiko when reviewing a devres allocation
+conversion patch.
+
+Fixes: 386119bc7be9 ("spi: sprd: spi: sprd: Add DMA mode support")
+Link: https://sashiko.dev/#/patchset/20260505072909.618363-1-johan%40kernel.org?part=10
+Cc: stable@vger.kernel.org # 5.1
+Cc: Lanqing Liu <lanqing.liu@unisoc.com>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Link: https://patch.msgid.link/20260512074733.915029-1-johan@kernel.org
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/spi/spi-sprd.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/spi/spi-sprd.c
++++ b/drivers/spi/spi-sprd.c
+@@ -992,7 +992,8 @@ err_rpm_put:
+ disable_clk:
+ clk_disable_unprepare(ss->clk);
+ release_dma:
+- sprd_spi_dma_release(ss);
++ if (ss->dma.enable)
++ sprd_spi_dma_release(ss);
+ free_controller:
+ spi_controller_put(sctlr);
+
--- /dev/null
+From ea6ec3343e05f7937a53eb6d7617b3abdb4abc19 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Tue, 12 May 2026 09:48:09 +0200
+Subject: spi: ti-qspi: fix use-after-free after DMA setup failure
+
+From: Johan Hovold <johan@kernel.org>
+
+commit ea6ec3343e05f7937a53eb6d7617b3abdb4abc19 upstream.
+
+The driver falls back to PIO mode if DMA setup fails during probe.
+
+Make sure to clear the DMA channel pointer also if buffer allocation
+fails to avoid passing a pointer to the released channel to the DMA
+engine (or trying to free the channel a second time on late probe errors
+or driver unbind).
+
+This issue was flagged by Sashiko when reviewing a devres allocation
+conversion patch.
+
+Fixes: c687c46e9e45 ("spi: spi-ti-qspi: Use bounce buffer if read buffer is not DMA'ble")
+Link: https://sashiko.dev/#/patchset/20260505072909.618363-1-johan%40kernel.org?part=17
+Cc: stable@vger.kernel.org # 4.12
+Cc: Vignesh R <vigneshr@ti.com>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Link: https://patch.msgid.link/20260512074809.915084-1-johan@kernel.org
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/spi/spi-ti-qspi.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/spi/spi-ti-qspi.c
++++ b/drivers/spi/spi-ti-qspi.c
+@@ -868,6 +868,7 @@ static int ti_qspi_probe(struct platform
+ dev_err(qspi->dev,
+ "dma_alloc_coherent failed, using PIO mode\n");
+ dma_release_channel(qspi->rx_chan);
++ qspi->rx_chan = NULL;
+ goto no_dma;
+ }
+ host->dma_rx = qspi->rx_chan;
--- /dev/null
+From 8f0f5c4fb9df0e19a341e0c6ed8dc4fda9124f03 Mon Sep 17 00:00:00 2001
+From: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
+Date: Thu, 21 May 2026 13:49:14 +0900
+Subject: tracing: Do not call map->ops->elt_free() if elt_alloc() fails
+
+From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+
+commit 8f0f5c4fb9df0e19a341e0c6ed8dc4fda9124f03 upstream.
+
+In paths where tracing_map_elt_alloc() failed to allocate objects,
+the map->ops->elt_alloc() call was never successful. In this case,
+map->ops->elt_free() should not be called.
+
+Link: https://sashiko.dev/#/patchset/20260520223101.34710-1-rosenp%40gmail.com
+
+Cc: stable@vger.kernel.org
+Cc: Tom Zanussi <tom.zanussi@linux.intel.com>
+Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+Cc: Rosen Penev <rosenp@gmail.com>
+Reported-by: Sashiko <sashiko-bot@kernel.org>
+Fixes: 2734b629525a ("tracing: Add per-element variable support to tracing_map")
+Link: https://patch.msgid.link/177933895460.108746.5396070821443932634.stgit@devnote2
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/trace/tracing_map.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+--- a/kernel/trace/tracing_map.c
++++ b/kernel/trace/tracing_map.c
+@@ -386,13 +386,11 @@ static void tracing_map_elt_init_fields(
+ }
+ }
+
+-static void tracing_map_elt_free(struct tracing_map_elt *elt)
++static void __tracing_map_elt_free(struct tracing_map_elt *elt)
+ {
+ if (!elt)
+ return;
+
+- if (elt->map->ops && elt->map->ops->elt_free)
+- elt->map->ops->elt_free(elt);
+ kfree(elt->fields);
+ kfree(elt->vars);
+ kfree(elt->var_set);
+@@ -400,6 +398,17 @@ static void tracing_map_elt_free(struct
+ kfree(elt);
+ }
+
++static void tracing_map_elt_free(struct tracing_map_elt *elt)
++{
++ if (!elt)
++ return;
++
++ /* Only objects initialized with alloc_elt() should be passed to free_elt().*/
++ if (elt->map->ops && elt->map->ops->elt_free)
++ elt->map->ops->elt_free(elt);
++ __tracing_map_elt_free(elt);
++}
++
+ static struct tracing_map_elt *tracing_map_elt_alloc(struct tracing_map *map)
+ {
+ struct tracing_map_elt *elt;
+@@ -444,7 +453,7 @@ static struct tracing_map_elt *tracing_m
+ }
+ return elt;
+ free:
+- tracing_map_elt_free(elt);
++ __tracing_map_elt_free(elt);
+
+ return ERR_PTR(err);
+ }
--- /dev/null
+From fd948c3f96b18ff9ba7d3e8eae13d196593e1aaf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Carlos=20L=C3=B3pez?= <clopez@suse.de>
+Date: Tue, 12 May 2026 12:00:42 +0200
+Subject: virt: sev-guest: Explicitly leak pages in unknown state
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Carlos López <clopez@suse.de>
+
+commit fd948c3f96b18ff9ba7d3e8eae13d196593e1aaf upstream.
+
+When set_memory_{encrypted,decrypted}() fail, the user cannot know at which
+point the function failed, meaning that the pages are left in an unknown state
+from the point of view of the caller.
+
+Since the pages may be left in an unencrypted state, they are not suitable for
+general use, and cannot be returned safely to the buddy allocator. Avoid the
+issue by never freeing the pages, and then do the proper accounting by calling
+snp_leak_pages().
+
+Fixes: 3e385c0d6ce8 ("virt: sev-guest: Move SNP Guest Request data pages handling under snp_cmd_mutex")
+Signed-off-by: Carlos López <clopez@suse.de>
+Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
+Cc: stable@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/virt/coco/sev-guest/sev-guest.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/virt/coco/sev-guest/sev-guest.c
++++ b/drivers/virt/coco/sev-guest/sev-guest.c
+@@ -176,6 +176,7 @@ static int get_ext_report(struct snp_gue
+ struct snp_guest_req req = {};
+ int ret, npages = 0, resp_len;
+ sockptr_t certs_address;
++ u64 pfn;
+
+ if (sockptr_is_null(io->req_data) || sockptr_is_null(io->resp_data))
+ return -EINVAL;
+@@ -215,10 +216,11 @@ static int get_ext_report(struct snp_gue
+ if (!req.certs_data)
+ return -ENOMEM;
+
++ pfn = PHYS_PFN(virt_to_phys(req.certs_data));
+ ret = set_memory_decrypted((unsigned long)req.certs_data, npages);
+ if (ret) {
+ pr_err("failed to mark page shared, ret=%d\n", ret);
+- free_pages_exact(req.certs_data, npages << PAGE_SHIFT);
++ snp_leak_pages(pfn, npages);
+ return -EFAULT;
+ }
+
+@@ -272,10 +274,12 @@ e_free:
+ kfree(report_resp);
+ e_free_data:
+ if (npages) {
+- if (set_memory_encrypted((unsigned long)req.certs_data, npages))
++ if (set_memory_encrypted((unsigned long)req.certs_data, npages)) {
+ WARN_ONCE(ret, "failed to restore encryption mask (leak it)\n");
+- else
++ snp_leak_pages(pfn, npages);
++ } else {
+ free_pages_exact(req.certs_data, npages << PAGE_SHIFT);
++ }
+ }
+ return ret;
+ }