From: Sasha Levin Date: Wed, 3 Jun 2026 15:14:13 +0000 (-0400) Subject: Fixes for all trees X-Git-Tag: v6.12.93~63 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c9a2658b70840a34106a46a36f841ed0f2704e7f;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/queue-5.10/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch new file mode 100644 index 0000000000..68a769d387 --- /dev/null +++ b/queue-5.10/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch @@ -0,0 +1,232 @@ +From ff876916d00d800ff39013e984e4ac836fc253f5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:15:58 +0200 +Subject: batman-adv: bla: avoid double decrement of bla.num_requests + +From: Sven Eckelmann + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++-------- + net/batman-adv/soft-interface.c | 1 + + net/batman-adv/types.h | 39 ++++++++++++++++---- + 3 files changed, 67 insertions(+), 24 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index d01c636024ce15..8354e5473dc698 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -521,8 +521,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig, + 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); +@@ -551,9 +551,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig, + 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; +@@ -656,10 +660,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw) + 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); + } + + /** +@@ -880,10 +886,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, + /* 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); +@@ -1262,9 +1270,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int 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); +@@ -1515,7 +1527,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) + 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. + * +@@ -1524,14 +1536,21 @@ static void batadv_bla_periodic_work(struct work_struct *work) + * 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(); + } +diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c +index 0e6d0a5e684130..c8c62b0cd54c76 100644 +--- a/net/batman-adv/soft-interface.c ++++ b/net/batman-adv/soft-interface.c +@@ -822,6 +822,7 @@ static int batadv_softif_init_late(struct net_device *dev) + 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); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index aee90f613e13c5..86c149d5b52ae2 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1036,6 +1036,12 @@ struct batadv_priv_bla { + /** @num_requests: number of bla requests in flight */ + 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 + */ +@@ -1815,6 +1821,27 @@ struct batadv_socket_packet { + + #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 + */ +@@ -1840,16 +1867,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; +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch b/queue-5.10/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch new file mode 100644 index 0000000000..0fb9db5467 --- /dev/null +++ b/queue-5.10/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch @@ -0,0 +1,53 @@ +From fd95b14c518fe2e2e38b20e176c79bd03bca87f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:16:21 +0200 +Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface + +From: Sven Eckelmann + +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 +Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816 +[ switch to old "mesh_iface" name "soft_iface" ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index d8305961b59bd1..d01c636024ce15 100644 +--- 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 batadv_priv *bat_priv, u8 *mac, + sizeof(local_claim_dest)); + local_claim_dest.type = claimtype; + +- soft_iface = primary_if->soft_iface; ++ soft_iface = READ_ONCE(primary_if->soft_iface); ++ if (!soft_iface) ++ goto out; + + skb = arp_create(ARPOP_REPLY, ETH_P_ARP, + /* IP DST: 0.0.0.0 */ + zeroip, +- primary_if->soft_iface, ++ soft_iface, + /* IP SRC: 0.0.0.0 */ + zeroip, + /* Ethernet DST: Broadcast */ +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch b/queue-5.10/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch new file mode 100644 index 0000000000..2ddd3b8087 --- /dev/null +++ b/queue-5.10/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch @@ -0,0 +1,231 @@ +From dd3ad9e8a19ef319d3cc55b36ac980390f6bbb41 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:15:29 +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 + +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") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++---------- + net/batman-adv/types.h | 3 ++ + 2 files changed, 60 insertions(+), 19 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index 9cd2fa751c21ef..93c2c5f6facc0d 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -225,6 +225,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface) + 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) +@@ -530,8 +532,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, + * @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, +@@ -555,13 +559,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + + 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; +@@ -583,6 +587,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + 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 */ +@@ -612,8 +618,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr, + * @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, +@@ -665,14 +673,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, + 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; + } + } + +@@ -784,6 +794,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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); +@@ -812,11 +824,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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; +@@ -838,8 +847,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + /* 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; + } + +@@ -854,15 +866,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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))); ++ } ++ + if (primary_if) + batadv_hardif_put(primary_if); + } +@@ -878,6 +903,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) + 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 +@@ -2443,6 +2479,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1, + + 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); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index f7e5a8f7570a31..288fe330946fb4 100644 +--- 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; + }; +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch b/queue-5.10/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch new file mode 100644 index 0000000000..0380cf501a --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch @@ -0,0 +1,196 @@ +From 94794c40fade445b66016a69112e2625ec7a7a40 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:14:56 +0200 +Subject: batman-adv: tp_meter: avoid role confusion in tp_list + +From: Sven Eckelmann + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 59 ++++++++++++++++++++++++--------------- + 1 file changed, 36 insertions(+), 23 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index b93cb86129769c..ebefad5822eea0 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -253,6 +253,7 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason, + * batadv_tp_list_find() - find a tp_vars object in the global list + * @bat_priv: the bat priv with all the soft 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 +@@ -260,7 +261,8 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason, + * 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; + +@@ -269,6 +271,9 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, + 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 +@@ -284,12 +289,33 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, + return tp_vars; + } + ++/** ++ * 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 soft 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 +@@ -299,7 +325,7 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, + */ + 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; + +@@ -311,6 +337,9 @@ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst, + 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 +@@ -654,13 +683,10 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + + /* 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; + +@@ -972,10 +998,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, + 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, +@@ -1094,18 +1118,14 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, + 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); +@@ -1367,7 +1387,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_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; + +@@ -1435,7 +1455,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + } 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", +@@ -1444,13 +1464,6 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + } + +- 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 +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-fix-race-condition-in-send-error.patch b/queue-5.10/batman-adv-tp_meter-fix-race-condition-in-send-error.patch new file mode 100644 index 0000000000..55f672d933 --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-fix-race-condition-in-send-error.patch @@ -0,0 +1,186 @@ +From 4fc9090dfc3ef3ab7277309e85bc2eea8197ce7e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:14:34 +0200 +Subject: batman-adv: tp_meter: fix race condition in send error reporting + +From: Sven Eckelmann + +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") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 40 ++++++++++++++++++++++++--------------- + net/batman-adv/types.h | 10 +++++----- + 2 files changed, 30 insertions(+), 20 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 578da029a7b3d8..b93cb86129769c 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -402,11 +402,14 @@ static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv, + 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", +@@ -419,7 +422,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, + 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, +@@ -435,10 +438,18 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, + 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; + } + + /** +@@ -468,7 +479,7 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars) + /* 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; + +@@ -488,7 +499,7 @@ static void batadv_tp_sender_timeout(struct timer_list *t) + struct batadv_tp_vars *tp_vars = from_timer(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 */ +@@ -650,7 +661,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + 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.. */ +@@ -819,21 +830,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; + } + +@@ -852,7 +863,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; +@@ -875,8 +886,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; + } + +@@ -997,7 +1007,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, + 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; + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 288fe330946fb4..aee90f613e13c5 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1407,11 +1407,11 @@ 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; +- +- /** @reason: reason for a stopped session */ +- enum batadv_tp_meter_reason reason; ++ /** ++ * @send_result: 0 when sending is ongoing and otherwise ++ * enum batadv_tp_meter_reason ++ */ ++ atomic_t send_result; + + /** @finish_work: work item for the finishing procedure */ + struct delayed_work finish_work; +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tt-avoid-empty-vlan-responses.patch b/queue-5.10/batman-adv-tt-avoid-empty-vlan-responses.patch new file mode 100644 index 0000000000..1f80477599 --- /dev/null +++ b/queue-5.10/batman-adv-tt-avoid-empty-vlan-responses.patch @@ -0,0 +1,91 @@ +From f8cdd1e62a3eb30fe9cef4199a1df26dc2003afd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:12:08 +0200 +Subject: batman-adv: tt: avoid empty VLAN responses + +From: Sven Eckelmann + +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") +[ Context, drop flex array dependency ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 21 ++++++++++++++++++--- + 1 file changed, 18 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 18eeeb80378dca..483121a85cf3fc 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -848,17 +848,19 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + 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 = sizeof(**tt_data); +@@ -866,7 +868,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + + /* 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; +@@ -887,14 +889,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ 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 = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_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; + +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch b/queue-5.10/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch new file mode 100644 index 0000000000..2f9518b5bb --- /dev/null +++ b/queue-5.10/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch @@ -0,0 +1,78 @@ +From 68abc66755f098051bd9559c3d5b7b443c390d85 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 23:06:04 +0200 +Subject: batman-adv: tt: fix TOCTOU race for reported vlans + +From: Sven Eckelmann + +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") +[ Context, drop flex array dependency ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 73f1ab4f008c4f..18eeeb80378dca 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -939,11 +939,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_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 = sizeof(**tt_data); +@@ -967,6 +964,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ num_vlan = 0; + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); + if (vlan_entries < 1) +@@ -977,8 +975,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + tt_vlan->reserved = 0; + + tt_vlan++; ++ num_vlan++; + } + ++ /* recalculate in case number of VLANs reduced */ ++ change_offset = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_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; + +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch b/queue-5.10/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch new file mode 100644 index 0000000000..88805b4509 --- /dev/null +++ b/queue-5.10/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch @@ -0,0 +1,196 @@ +From f6c337bd4cd1b2ec09241b059ddae725aeab7b2c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:56:05 +0200 +Subject: batman-adv: tvlv: abort OGM send on tvlv append failure + +From: Sven Eckelmann + +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") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + 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(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index f35665a4045123..9cd2fa751c21ef 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -784,6 +784,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + u32 seqno; + u16 tvlv_len = 0; + unsigned long send_time; ++ int ret; + + lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); + +@@ -807,9 +808,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + * 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); +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 5f75bcf64ae56c..939aa4b303ad98 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -272,9 +272,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + struct batadv_hard_iface *hard_iface; + struct batadv_ogm2_packet *ogm_packet; + struct sk_buff *skb, *skb_tmp; +- unsigned char *ogm_buff; +- int ogm_buff_len; +- u16 tvlv_len = 0; ++ unsigned char **ogm_buff; ++ int *ogm_buff_len; ++ u16 tvlv_len; + int ret; + + lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex); +@@ -282,25 +282,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + 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)); +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 99fc48efde5431..75f7ea827ed9dd 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -7,6 +7,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff, + * 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(struct batadv_priv *bat_priv, + 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(struct batadv_priv *bat_priv, + + 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(struct batadv_priv *bat_priv, + + end: + spin_unlock_bh(&bat_priv->tvlv.container_list_lock); +- return tvlv_value_len; ++ ++ return tvlv_len_ret; + } + + /** +diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h +index d509d00c7a23e6..4823f4963df5bd 100644 +--- a/net/batman-adv/tvlv.h ++++ b/net/batman-adv/tvlv.h +@@ -15,7 +15,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, +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tvlv-reject-oversized-tvlv-packets.patch b/queue-5.10/batman-adv-tvlv-reject-oversized-tvlv-packets.patch new file mode 100644 index 0000000000..986b346892 --- /dev/null +++ b/queue-5.10/batman-adv-tvlv-reject-oversized-tvlv-packets.patch @@ -0,0 +1,85 @@ +From 1eb789040cee7f7b55911ba95c226511331006b3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:13:55 +0200 +Subject: batman-adv: tvlv: reject oversized TVLV packets + +From: Sven Eckelmann + +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 +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 75f7ea827ed9dd..8da8184a2ebdfc 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version) + * + * 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(struct batadv_priv *bat_priv, + { + 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); +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-v-stop-ogmv2-on-disabled-interface.patch b/queue-5.10/batman-adv-v-stop-ogmv2-on-disabled-interface.patch new file mode 100644 index 0000000000..c878255981 --- /dev/null +++ b/queue-5.10/batman-adv-v-stop-ogmv2-on-disabled-interface.patch @@ -0,0 +1,149 @@ +From a128c818ba38abae4cd7923931eb42c718b37b55 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:36:39 +0200 +Subject: batman-adv: v: stop OGMv2 on disabled interface + +From: Sven Eckelmann + +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 +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +[ switch to old "mesh_iface" name "soft_iface" ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------ + 1 file changed, 21 insertions(+), 12 deletions(-) + +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index d43fc72af9a994..5f75bcf64ae56c 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -116,14 +116,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv) + + /** + * 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->soft_iface); +- + if (hard_iface->if_status != BATADV_IF_ACTIVE) { + kfree_skb(skb); + return; +@@ -190,6 +190,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) + + /** + * 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 +@@ -199,7 +200,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) + * + * 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; +@@ -229,27 +231,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface) + 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->soft_iface); ++ if (hard_iface->soft_iface != bat_priv->soft_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); +@@ -348,7 +355,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + 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(); +@@ -388,12 +395,14 @@ void batadv_v_ogm_aggr_work(struct 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->soft_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); +@@ -583,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv, + 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: + if (orig_ifinfo) +-- +2.53.0 + diff --git a/queue-5.10/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-26331 b/queue-5.10/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-26331 new file mode 100644 index 0000000000..453fd5b676 --- /dev/null +++ b/queue-5.10/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-26331 @@ -0,0 +1,68 @@ +From 0836321027771a29176a7821e079363ff849be0d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 13:11:37 +0200 +Subject: RDMA/rxe: Fix double free in rxe_srq_from_init + +From: Jiasheng Jiang + +commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream. + +In rxe_srq_from_init(), the queue pointer 'q' is assigned to +'srq->rq.queue' before copying the SRQ number to user space. +If copy_to_user() fails, the function calls rxe_queue_cleanup() +to free the queue, but leaves the now-invalid pointer in +'srq->rq.queue'. + +The caller of rxe_srq_from_init() (rxe_create_srq) eventually +calls rxe_srq_cleanup() upon receiving the error, which triggers +a second rxe_queue_cleanup() on the same memory, leading to a +double free. + +The call trace looks like this: + kmem_cache_free+0x.../0x... + rxe_queue_cleanup+0x1a/0x30 [rdma_rxe] + rxe_srq_cleanup+0x42/0x60 [rdma_rxe] + rxe_elem_release+0x31/0x70 [rdma_rxe] + rxe_create_srq+0x12b/0x1a0 [rdma_rxe] + ib_create_srq_user+0x9a/0x150 [ib_core] + +Fix this by moving 'srq->rq.queue = q' after copy_to_user. + +Fixes: aae0484e15f0 ("IB/rxe: avoid srq memory leak") +Signed-off-by: Jiasheng Jiang +Link: https://patch.msgid.link/20260112015412.29458-1-jiashengjiangcool@gmail.com +Reviewed-by: Zhu Yanjun +Signed-off-by: Leon Romanovsky +[bwh: Backported to 5.10: There was no assignment to init->attr.max_wr + here; don't add it] +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/infiniband/sw/rxe/rxe_srq.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c +index 41b0d1e11bafdb..4e523d91e7dcb1 100644 +--- a/drivers/infiniband/sw/rxe/rxe_srq.c ++++ b/drivers/infiniband/sw/rxe/rxe_srq.c +@@ -98,8 +98,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, + return -ENOMEM; + } + +- srq->rq.queue = q; +- + err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, udata, q->buf, + q->buf_size, &q->ip); + if (err) { +@@ -116,6 +114,8 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, + } + } + ++ srq->rq.queue = q; ++ + return 0; + } + +-- +2.53.0 + diff --git a/queue-5.10/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch b/queue-5.10/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch new file mode 100644 index 0000000000..de2c12d0be --- /dev/null +++ b/queue-5.10/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch @@ -0,0 +1,35 @@ +From 2652c19f60f717d8cda243411cb78fb177179e77 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 13:06:43 +0200 +Subject: Revert "RDMA/rxe: Fix double free in rxe_srq_from_init" + +From: Ben Hutchings + +This reverts commit 22b8c23a3b92d023614bb00896fe364b2c1a31d3, which +was commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream. The +backported version did not move but duplicated the problematic +assignment, so it did not fix the bug. A proper backport will follow. + +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/infiniband/sw/rxe/rxe_srq.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c +index 9d9baca2694999..41b0d1e11bafdb 100644 +--- a/drivers/infiniband/sw/rxe/rxe_srq.c ++++ b/drivers/infiniband/sw/rxe/rxe_srq.c +@@ -116,9 +116,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, + } + } + +- srq->rq.queue = q; +- init->attr.max_wr = srq->rq.max_wr; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-5.10/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch b/queue-5.10/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch new file mode 100644 index 0000000000..8bd6cd3b13 --- /dev/null +++ b/queue-5.10/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch @@ -0,0 +1,108 @@ +From e25b37a2f6297fe641e3e33f9ab115732c71603a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 13:29:47 +0200 +Subject: selftests: forwarding: lib: Add helpers for checksum handling + +From: Petr Machata + +commit 952e0ee38c7215c45192d8c899acd1830873f28b upstream. + +In order to generate IGMPv3 and MLDv2 packets on the fly, we will need +helpers to calculate the packet checksum. + +The approach presented in this patch revolves around payload templates +for mausezahn. These are mausezahn-like payload strings (01:23:45:...) +with possibly one 2-byte sequence replaced with the word PAYLOAD. The +main function is payload_template_calc_checksum(), which calculates +RFC 1071 checksum of the message. There are further helpers to then +convert the checksum to the payload format, and to expand it. + +For IPv6, MLDv2 message checksum is computed using a pseudoheader that +differs from the header used in the payload itself. The fact that the +two messages are different means that the checksum needs to be +returned as a separate quantity, instead of being expanded in-place in +the payload itself. Furthermore, the pseudoheader includes a length of +the message. Much like the checksum, this needs to be expanded in +mausezahn format. And likewise for number of addresses for (S,G) +entries. Thus we have several places where a computed quantity needs +to be presented in the payload format. Add a helper u16_to_bytes(), +which will be used in all these cases. + +Signed-off-by: Petr Machata +Acked-by: Nikolay Aleksandrov +Signed-off-by: David S. Miller +Stable-dep-of: 02cb2e6bacbb ("selftests: forwarding: vxlan_bridge_1d: fix test failure with br_netfilter enabled") +[bwh: Backported to 5.10: adjust context] +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/net/forwarding/lib.sh | 56 +++++++++++++++++++ + 1 file changed, 56 insertions(+) + +diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh +index dfb41db7fbe481..2825c779ef30e0 100644 +--- a/tools/testing/selftests/net/forwarding/lib.sh ++++ b/tools/testing/selftests/net/forwarding/lib.sh +@@ -1298,3 +1298,59 @@ tcpdump_show() + { + tcpdump -e -n -r $capfile 2>&1 + } ++ ++u16_to_bytes() ++{ ++ local u16=$1; shift ++ ++ printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/' ++} ++ ++# Given a mausezahn-formatted payload (colon-separated bytes given as %02x), ++# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be, ++# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any) ++# stands for 00:00. ++payload_template_calc_checksum() ++{ ++ local payload=$1; shift ++ ++ ( ++ # Set input radix. ++ echo "16i" ++ # Push zero for the initial checksum. ++ echo 0 ++ ++ # Pad the payload with a terminating 00: in case we get an odd ++ # number of bytes. ++ echo "${payload%:}:00:" | ++ sed 's/CHECKSUM/00:00/g' | ++ tr '[:lower:]' '[:upper:]' | ++ # Add the word to the checksum. ++ sed 's/\(..\):\(..\):/\1\2+\n/g' | ++ # Strip the extra odd byte we pushed if left unconverted. ++ sed 's/\(..\):$//' ++ ++ echo "10000 ~ +" # Calculate and add carry. ++ echo "FFFF r - p" # Bit-flip and print. ++ ) | ++ dc | ++ tr '[:upper:]' '[:lower:]' ++} ++ ++payload_template_expand_checksum() ++{ ++ local payload=$1; shift ++ local checksum=$1; shift ++ ++ local ckbytes=$(u16_to_bytes $checksum) ++ ++ echo "$payload" | sed "s/CHECKSUM/$ckbytes/g" ++} ++ ++payload_template_nbytes() ++{ ++ local payload=$1; shift ++ ++ payload_template_expand_checksum "${payload%:}" 0 | ++ sed 's/:/\n/g' | wc -l ++} +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index c4aa23cf6e..5b8948c9f1 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -27,3 +27,16 @@ net-sched-revert-net-sched-restrict-conditions-for-a.patch bluetooth-l2cap-clear-chan-ident-on-ecred-reconfigur.patch bluetooth-l2cap-fix-possible-crash-on-l2cap_ecred_co.patch sctp-fix-race-between-sctp_wait_for_connect-and-peel.patch +batman-adv-v-stop-ogmv2-on-disabled-interface.patch +batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch +batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch +batman-adv-tvlv-reject-oversized-tvlv-packets.patch +batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch +batman-adv-tp_meter-fix-race-condition-in-send-error.patch +batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch +selftests-forwarding-lib-add-helpers-for-checksum-ha.patch +batman-adv-tt-fix-toctou-race-for-reported-vlans.patch +batman-adv-tt-avoid-empty-vlan-responses.patch +batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch +revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch +rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-26331 diff --git a/queue-5.15/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/queue-5.15/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch new file mode 100644 index 0000000000..a40272309c --- /dev/null +++ b/queue-5.15/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch @@ -0,0 +1,232 @@ +From daa64c8a65b7eece74a1a8c4c33018b4a7ffb742 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:08:08 +0200 +Subject: batman-adv: bla: avoid double decrement of bla.num_requests + +From: Sven Eckelmann + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++-------- + net/batman-adv/soft-interface.c | 1 + + net/batman-adv/types.h | 39 ++++++++++++++++---- + 3 files changed, 67 insertions(+), 24 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index d8ead0eb71da06..452e78fd70c039 100644 +--- 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_priv *bat_priv, u8 *orig, + 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_priv *bat_priv, u8 *orig, + 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(struct batadv_bla_backbone_gw *backbone_gw) + 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(struct batadv_priv *bat_priv, u8 *an_addr, + /* 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 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int 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(struct work_struct *work) + 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(struct work_struct *work) + * 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(); + } +diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c +index bff86ccadc2cc2..b46480ec7bd1d0 100644 +--- a/net/batman-adv/soft-interface.c ++++ b/net/batman-adv/soft-interface.c +@@ -785,6 +785,7 @@ static int batadv_softif_init_late(struct net_device *dev) + 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); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index ff40fc9e36f66c..6b64e6b7bf80ca 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1026,6 +1026,12 @@ struct batadv_priv_bla { + /** @num_requests: number of bla requests in flight */ + 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 + */ +@@ -1787,6 +1793,27 @@ struct batadv_socket_packet { + + #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 + */ +@@ -1812,16 +1839,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; +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch b/queue-5.15/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch new file mode 100644 index 0000000000..ebe20c6419 --- /dev/null +++ b/queue-5.15/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch @@ -0,0 +1,53 @@ +From a8197f02cd3ecacbf8778f9e5051db51d2227e6e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:08:49 +0200 +Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface + +From: Sven Eckelmann + +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 +Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816 +[ switch to old "mesh_iface" name "soft_iface" ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index f768a4e0bc0f49..d8ead0eb71da06 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -355,12 +355,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac, + sizeof(local_claim_dest)); + local_claim_dest.type = claimtype; + +- soft_iface = primary_if->soft_iface; ++ soft_iface = READ_ONCE(primary_if->soft_iface); ++ if (!soft_iface) ++ goto out; + + skb = arp_create(ARPOP_REPLY, ETH_P_ARP, + /* IP DST: 0.0.0.0 */ + zeroip, +- primary_if->soft_iface, ++ soft_iface, + /* IP SRC: 0.0.0.0 */ + zeroip, + /* Ethernet DST: Broadcast */ +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch b/queue-5.15/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch new file mode 100644 index 0000000000..cb386717dc --- /dev/null +++ b/queue-5.15/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch @@ -0,0 +1,230 @@ +From 66d82dbc14c35035b0c1b0e98619cf9ba3c01510 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:07:24 +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 + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++---------- + net/batman-adv/types.h | 3 ++ + 2 files changed, 60 insertions(+), 19 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index bc8685ffcb7100..669f77eed073a0 100644 +--- 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(struct batadv_hard_iface *hard_iface) + 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) +@@ -528,8 +530,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, + * @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, +@@ -553,13 +557,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + + 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; +@@ -581,6 +585,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + 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 */ +@@ -610,8 +616,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr, + * @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, +@@ -663,14 +671,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, + 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; + } + } + +@@ -782,6 +792,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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); +@@ -810,11 +822,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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; +@@ -836,8 +845,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + /* 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; + } + +@@ -852,15 +864,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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); + } + +@@ -875,6 +900,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) + 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 +@@ -2278,6 +2314,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1, + + 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); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 19fd1c9adf4931..ff40fc9e36f66c 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -82,6 +82,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; + }; +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch b/queue-5.15/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch new file mode 100644 index 0000000000..b74a7fa9cc --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch @@ -0,0 +1,60 @@ +From 7b75765536dbc4a9a0778f10897cc9f9c8e21a5e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 22:27:23 +0200 +Subject: batman-adv: tp_meter: directly shut down timer on cleanup + +From: Sven Eckelmann + +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") +[ adapt pre-hunk to old del_timer* names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 464fca0ca8ac28..0eaeffba4b4ffe 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -384,13 +384,7 @@ static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv, + atomic_dec(&tp_vars->bat_priv->tp_num); + + /* kill the timer and remove its reference */ +- del_timer_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 +- * del_timer(), it would not re-arm itself once again because the status +- * is OFF now +- */ +- del_timer(&tp_vars->timer); ++ timer_shutdown_sync(&tp_vars->timer); + batadv_tp_vars_put(tp_vars); + } + +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tt-avoid-empty-vlan-responses.patch b/queue-5.15/batman-adv-tt-avoid-empty-vlan-responses.patch new file mode 100644 index 0000000000..1c1a6c73d7 --- /dev/null +++ b/queue-5.15/batman-adv-tt-avoid-empty-vlan-responses.patch @@ -0,0 +1,91 @@ +From 1efdcd4a97c096e0812055453c0d9182de3090ab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:02:51 +0200 +Subject: batman-adv: tt: avoid empty VLAN responses + +From: Sven Eckelmann + +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") +[ Context, drop flex array dependency ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 21 ++++++++++++++++++--- + 1 file changed, 18 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 11c39a9ab90e46..e4d55b27f2551b 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -843,17 +843,19 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + 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 = sizeof(**tt_data); +@@ -861,7 +863,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + + /* 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; +@@ -882,14 +884,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ 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 = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_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; + +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch b/queue-5.15/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch new file mode 100644 index 0000000000..23d07deba2 --- /dev/null +++ b/queue-5.15/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch @@ -0,0 +1,78 @@ +From 3aae4099e4f746a570e865fa8bb96f34889a2f00 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 23:04:23 +0200 +Subject: batman-adv: tt: fix TOCTOU race for reported vlans + +From: Sven Eckelmann + +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") +[ Context, drop flex array dependency ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 76cf40d859908a..11c39a9ab90e46 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -934,11 +934,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_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 = sizeof(**tt_data); +@@ -962,6 +959,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ num_vlan = 0; + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); + if (vlan_entries < 1) +@@ -972,8 +970,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + tt_vlan->reserved = 0; + + tt_vlan++; ++ num_vlan++; + } + ++ /* recalculate in case number of VLANs reduced */ ++ change_offset = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_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; + +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch b/queue-5.15/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch new file mode 100644 index 0000000000..324eb95e64 --- /dev/null +++ b/queue-5.15/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch @@ -0,0 +1,196 @@ +From f500a77ad17a1fac80aea37f4ab5f19476ccf530 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:53:56 +0200 +Subject: batman-adv: tvlv: abort OGM send on tvlv append failure + +From: Sven Eckelmann + +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") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + 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(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index 58c18e22603d12..bc8685ffcb7100 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -782,6 +782,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + u32 seqno; + u16 tvlv_len = 0; + unsigned long send_time; ++ int ret; + + lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); + +@@ -805,9 +806,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + * 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); +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 74295915653336..63337e02cf2f11 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -272,9 +272,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + struct batadv_hard_iface *hard_iface; + struct batadv_ogm2_packet *ogm_packet; + struct sk_buff *skb, *skb_tmp; +- unsigned char *ogm_buff; +- int ogm_buff_len; +- u16 tvlv_len = 0; ++ unsigned char **ogm_buff; ++ int *ogm_buff_len; ++ u16 tvlv_len; + int ret; + + lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex); +@@ -282,25 +282,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + 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)); +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 992773376e51d4..4812137439708f 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -7,6 +7,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff, + * 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(struct batadv_priv *bat_priv, + 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(struct batadv_priv *bat_priv, + + 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(struct batadv_priv *bat_priv, + + end: + spin_unlock_bh(&bat_priv->tvlv.container_list_lock); +- return tvlv_value_len; ++ ++ return tvlv_len_ret; + } + + /** +diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h +index 54f2a35653d0f1..ffbc93f78688e5 100644 +--- a/net/batman-adv/tvlv.h ++++ b/net/batman-adv/tvlv.h +@@ -15,7 +15,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, +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tvlv-reject-oversized-tvlv-packets.patch b/queue-5.15/batman-adv-tvlv-reject-oversized-tvlv-packets.patch new file mode 100644 index 0000000000..559646edc2 --- /dev/null +++ b/queue-5.15/batman-adv-tvlv-reject-oversized-tvlv-packets.patch @@ -0,0 +1,85 @@ +From 85c934e513f72a0b78cd05213c98097a8436ee01 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 22:04:47 +0200 +Subject: batman-adv: tvlv: reject oversized TVLV packets + +From: Sven Eckelmann + +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 +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 4812137439708f..de0c139426839c 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version) + * + * 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(struct batadv_priv *bat_priv, + { + 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); +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-v-stop-ogmv2-on-disabled-interface.patch b/queue-5.15/batman-adv-v-stop-ogmv2-on-disabled-interface.patch new file mode 100644 index 0000000000..96773ed108 --- /dev/null +++ b/queue-5.15/batman-adv-v-stop-ogmv2-on-disabled-interface.patch @@ -0,0 +1,149 @@ +From 8d7c7294774177317c2d949c9be471421273f9e1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:34:43 +0200 +Subject: batman-adv: v: stop OGMv2 on disabled interface + +From: Sven Eckelmann + +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 +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +[ switch to old "mesh_iface" name "soft_iface" ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------ + 1 file changed, 21 insertions(+), 12 deletions(-) + +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index c357cf72396ebe..74295915653336 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -116,14 +116,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv) + + /** + * 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->soft_iface); +- + if (hard_iface->if_status != BATADV_IF_ACTIVE) { + kfree_skb(skb); + return; +@@ -190,6 +190,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) + + /** + * 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 +@@ -199,7 +200,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) + * + * 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; +@@ -229,27 +231,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface) + 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->soft_iface); ++ if (hard_iface->soft_iface != bat_priv->soft_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); +@@ -348,7 +355,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + 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(); +@@ -388,12 +395,14 @@ void batadv_v_ogm_aggr_work(struct 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->soft_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); +@@ -583,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv, + 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); +-- +2.53.0 + diff --git a/queue-5.15/drm-dp-add-edp-1.5-bit-definition.patch b/queue-5.15/drm-dp-add-edp-1.5-bit-definition.patch new file mode 100644 index 0000000000..ebb6d986a5 --- /dev/null +++ b/queue-5.15/drm-dp-add-edp-1.5-bit-definition.patch @@ -0,0 +1,41 @@ +From 19f0eb99335b3e44794620982a3312ab002e0f0c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 11:03:16 +0300 +Subject: drm/dp: Add eDP 1.5 bit definition +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Suraj Kandpal + +commit 5dfc37a6b77bf6beedbd30d70184b54e1a08ccac upstream. + +Add the eDP revision bit value for 1.5. + +Spec: eDPv1.5 Table 16-5 +Signed-off-by: Suraj Kandpal +Reviewed-by: Arun R Murthy +Tested-by: Ben Kao +Acked-by: Maarten Lankhorst +Link: https://patchwork.freedesktop.org/patch/msgid/20250206063253.2827017-2-suraj.kandpal@intel.com +Signed-off-by: Jouni Högander +Signed-off-by: Sasha Levin +--- + include/drm/drm_dp_helper.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h +index 9c7949ebc159eb..01422c2078f7db 100644 +--- a/include/drm/drm_dp_helper.h ++++ b/include/drm/drm_dp_helper.h +@@ -942,6 +942,7 @@ struct drm_panel; + # define DP_EDP_14 0x03 + # define DP_EDP_14a 0x04 /* eDP 1.4a */ + # define DP_EDP_14b 0x05 /* eDP 1.4b */ ++# define DP_EDP_15 0x06 /* eDP 1.5 */ + + #define DP_EDP_GENERAL_CAP_1 0x701 + # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP (1 << 0) +-- +2.53.0 + diff --git a/queue-5.15/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-5.15/drm-i915-psr-add-defininitions-for-intel_wa_register.patch new file mode 100644 index 0000000000..203c319f6a --- /dev/null +++ b/queue-5.15/drm-i915-psr-add-defininitions-for-intel_wa_register.patch @@ -0,0 +1,73 @@ +From f639114e72baf50818faad11165ad9a1e2c437c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 11:03:14 +0300 +Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD + register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream. + +EDP specification says: + +"If either VSC SDP is unable to be transmitted 100 ns before the SU region, +the Source device may optionally transmit the VSC SDP during the prior +video scan line’s HBlank period There is a Intel specific drm dp register +currently containing bits related how TCON can support PSR2 with SDP on +prior line." + +Unfortunately many panels are having problems in implementing this. So +there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to +figure out if this is properly implemented on a panel or if panel doesn't +require that 100 ns delay before the SU region. Here are the definitions in +this custom DPCD address: + +0 = Panel doesn't support SDP on prior line +1 = Panel supports SDP on prior line +2 = Panel doesn't have 100ns requirement +3 = Reserved + +Add definitions for this new register and it's values into new header +intel_dpcd.h. + +v2: add INTEL_DPCD_ prefix to definitions + +Bspec: 74741 +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com +(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h + +diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h +new file mode 100644 +index 00000000000000..4aea5326f2ed48 +--- /dev/null ++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: MIT */ ++/* ++ * Copyright © 2026 Intel Corporation ++ */ ++ ++#ifndef __INTEL_DPCD_H__ ++#define __INTEL_DPCD_H__ ++ ++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS 0x3f0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK REG_GENMASK(1, 0) ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1 0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE 1 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE 2 ++ ++#endif /* __INTEL_DPCD_H__ */ +-- +2.53.0 + diff --git a/queue-5.15/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-5.15/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch new file mode 100644 index 0000000000..75f3e47069 --- /dev/null +++ b/queue-5.15/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch @@ -0,0 +1,77 @@ +From 47d33a851e1598adf99cbb626e0f5fc46c990871 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 11:03:17 +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 + +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: # v5.15+ +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com +(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Jouni Högander +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_psr.c | 27 +++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index aa00e062a2a6aa..fca55ae3ffe5bd 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -824,6 +824,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp, + return true; + } + ++static bool apply_scanline_indication_wa(struct intel_dp *intel_dp, ++ struct intel_crtc_state *crtc_state) ++{ ++ u8 early_scanline_support = intel_dp->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_dp *intel_dp, + struct intel_crtc_state *crtc_state) + { +@@ -844,7 +868,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d + return false; + + crtc_state->req_psr2_sdp_prior_scanline = true; +- return true; ++ ++ return apply_scanline_indication_wa(intel_dp, crtc_state); + } + + static bool _compute_psr2_wake_times(struct intel_dp *intel_dp, +-- +2.53.0 + diff --git a/queue-5.15/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-5.15/drm-i915-psr-read-intel-dpcd-workaround-register.patch new file mode 100644 index 0000000000..fd43d7346d --- /dev/null +++ b/queue-5.15/drm-i915-psr-read-intel-dpcd-workaround-register.patch @@ -0,0 +1,67 @@ +From 5799d01879ad52437f710dfeb3d0029965551a9d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 11:03:15 +0300 +Subject: drm/i915/psr: Read Intel DPCD workaround register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream. + +Read Intel DPCD workaround register and store it into +intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains +only PSR workaround for PSR2 SDP on prior scanline implementation. + +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com +(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_display_types.h | 1 + + drivers/gpu/drm/i915/display/intel_psr.c | 7 +++++++ + 2 files changed, 8 insertions(+) + +diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h +index 90e055f0569940..ef4c280d2cb630 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_types.h ++++ b/drivers/gpu/drm/i915/display/intel_display_types.h +@@ -1554,6 +1554,7 @@ struct intel_dp { + u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE]; + u8 fec_capable; + u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]; ++ u8 intel_wa_dpcd; + /* source rates */ + int num_source_rates; + const int *source_rates; +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index 93d2fd4cd16b7c..aa00e062a2a6aa 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -30,6 +30,7 @@ + #include "intel_atomic.h" + #include "intel_de.h" + #include "intel_display_types.h" ++#include "intel_dpcd.h" + #include "intel_dp_aux.h" + #include "intel_hdmi.h" + #include "intel_psr.h" +@@ -363,6 +364,12 @@ void intel_psr_init_dpcd(struct intel_dp *intel_dp) + intel_dp_get_su_granularity(intel_dp); + } + } ++ ++ if (intel_dp->psr.sink_psr2_support) ++ drm_dp_dpcd_read(&intel_dp->aux, ++ INTEL_DPCD_INTEL_WA_REGISTER_CAPS, ++ &intel_dp->intel_wa_dpcd, ++ sizeof(intel_dp->intel_wa_dpcd)); + } + + static void hsw_psr_setup_aux(struct intel_dp *intel_dp) +-- +2.53.0 + diff --git a/queue-5.15/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-82 b/queue-5.15/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-82 new file mode 100644 index 0000000000..20ca0caf9d --- /dev/null +++ b/queue-5.15/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-82 @@ -0,0 +1,68 @@ +From 664eabc5fb730c778018eea31e50440c07787ed6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 13:12:21 +0200 +Subject: RDMA/rxe: Fix double free in rxe_srq_from_init + +From: Jiasheng Jiang + +commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream. + +In rxe_srq_from_init(), the queue pointer 'q' is assigned to +'srq->rq.queue' before copying the SRQ number to user space. +If copy_to_user() fails, the function calls rxe_queue_cleanup() +to free the queue, but leaves the now-invalid pointer in +'srq->rq.queue'. + +The caller of rxe_srq_from_init() (rxe_create_srq) eventually +calls rxe_srq_cleanup() upon receiving the error, which triggers +a second rxe_queue_cleanup() on the same memory, leading to a +double free. + +The call trace looks like this: + kmem_cache_free+0x.../0x... + rxe_queue_cleanup+0x1a/0x30 [rdma_rxe] + rxe_srq_cleanup+0x42/0x60 [rdma_rxe] + rxe_elem_release+0x31/0x70 [rdma_rxe] + rxe_create_srq+0x12b/0x1a0 [rdma_rxe] + ib_create_srq_user+0x9a/0x150 [ib_core] + +Fix this by moving 'srq->rq.queue = q' after copy_to_user. + +Fixes: aae0484e15f0 ("IB/rxe: avoid srq memory leak") +Signed-off-by: Jiasheng Jiang +Link: https://patch.msgid.link/20260112015412.29458-1-jiashengjiangcool@gmail.com +Reviewed-by: Zhu Yanjun +Signed-off-by: Leon Romanovsky +[bwh: Backported to 5.15: There was no assignment to init->attr.max_wr + here; don't add it] +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/infiniband/sw/rxe/rxe_srq.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c +index eb1c4c3b3a7865..595d4e7b91d0b8 100644 +--- a/drivers/infiniband/sw/rxe/rxe_srq.c ++++ b/drivers/infiniband/sw/rxe/rxe_srq.c +@@ -100,8 +100,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, + return -ENOMEM; + } + +- srq->rq.queue = q; +- + err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, udata, q->buf, + q->buf_size, &q->ip); + if (err) { +@@ -118,6 +116,8 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, + } + } + ++ srq->rq.queue = q; ++ + return 0; + } + +-- +2.53.0 + diff --git a/queue-5.15/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch b/queue-5.15/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch new file mode 100644 index 0000000000..5fad77a538 --- /dev/null +++ b/queue-5.15/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch @@ -0,0 +1,35 @@ +From 67f2ca027d973a97363336e1534dda681a13ccc6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 13:07:48 +0200 +Subject: Revert "RDMA/rxe: Fix double free in rxe_srq_from_init" + +From: Ben Hutchings + +This reverts commit af5956243018918130d52c9f671efdb40bab3366, which +was commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream. The +backported version did not move but duplicated the problematic +assignment, so it did not fix the bug. A proper backport will follow. + +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/infiniband/sw/rxe/rxe_srq.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c +index 05ae3d183b21d1..eb1c4c3b3a7865 100644 +--- a/drivers/infiniband/sw/rxe/rxe_srq.c ++++ b/drivers/infiniband/sw/rxe/rxe_srq.c +@@ -118,9 +118,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, + } + } + +- srq->rq.queue = q; +- init->attr.max_wr = srq->rq.max_wr; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-5.15/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch b/queue-5.15/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch new file mode 100644 index 0000000000..523e5ceaa6 --- /dev/null +++ b/queue-5.15/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch @@ -0,0 +1,108 @@ +From 7b3084888360b002704e9af39ce89f70c519fd7c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 13:33:11 +0200 +Subject: selftests: forwarding: lib: Add helpers for checksum handling + +From: Petr Machata + +commit 952e0ee38c7215c45192d8c899acd1830873f28b upstream. + +In order to generate IGMPv3 and MLDv2 packets on the fly, we will need +helpers to calculate the packet checksum. + +The approach presented in this patch revolves around payload templates +for mausezahn. These are mausezahn-like payload strings (01:23:45:...) +with possibly one 2-byte sequence replaced with the word PAYLOAD. The +main function is payload_template_calc_checksum(), which calculates +RFC 1071 checksum of the message. There are further helpers to then +convert the checksum to the payload format, and to expand it. + +For IPv6, MLDv2 message checksum is computed using a pseudoheader that +differs from the header used in the payload itself. The fact that the +two messages are different means that the checksum needs to be +returned as a separate quantity, instead of being expanded in-place in +the payload itself. Furthermore, the pseudoheader includes a length of +the message. Much like the checksum, this needs to be expanded in +mausezahn format. And likewise for number of addresses for (S,G) +entries. Thus we have several places where a computed quantity needs +to be presented in the payload format. Add a helper u16_to_bytes(), +which will be used in all these cases. + +Signed-off-by: Petr Machata +Acked-by: Nikolay Aleksandrov +Signed-off-by: David S. Miller +Stable-dep-of: 02cb2e6bacbb ("selftests: forwarding: vxlan_bridge_1d: fix test failure with br_netfilter enabled") +[bwh: Backported to 5.15: adjust context] +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/net/forwarding/lib.sh | 56 +++++++++++++++++++ + 1 file changed, 56 insertions(+) + +diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh +index 83e8f9466d6273..c570d8f65a0cec 100644 +--- a/tools/testing/selftests/net/forwarding/lib.sh ++++ b/tools/testing/selftests/net/forwarding/lib.sh +@@ -1491,3 +1491,59 @@ brmcast_check_sg_state() + check_err_fail $should_fail $? "Entry $src has blocked flag" + done + } ++ ++u16_to_bytes() ++{ ++ local u16=$1; shift ++ ++ printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/' ++} ++ ++# Given a mausezahn-formatted payload (colon-separated bytes given as %02x), ++# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be, ++# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any) ++# stands for 00:00. ++payload_template_calc_checksum() ++{ ++ local payload=$1; shift ++ ++ ( ++ # Set input radix. ++ echo "16i" ++ # Push zero for the initial checksum. ++ echo 0 ++ ++ # Pad the payload with a terminating 00: in case we get an odd ++ # number of bytes. ++ echo "${payload%:}:00:" | ++ sed 's/CHECKSUM/00:00/g' | ++ tr '[:lower:]' '[:upper:]' | ++ # Add the word to the checksum. ++ sed 's/\(..\):\(..\):/\1\2+\n/g' | ++ # Strip the extra odd byte we pushed if left unconverted. ++ sed 's/\(..\):$//' ++ ++ echo "10000 ~ +" # Calculate and add carry. ++ echo "FFFF r - p" # Bit-flip and print. ++ ) | ++ dc | ++ tr '[:upper:]' '[:lower:]' ++} ++ ++payload_template_expand_checksum() ++{ ++ local payload=$1; shift ++ local checksum=$1; shift ++ ++ local ckbytes=$(u16_to_bytes $checksum) ++ ++ echo "$payload" | sed "s/CHECKSUM/$ckbytes/g" ++} ++ ++payload_template_nbytes() ++{ ++ local payload=$1; shift ++ ++ payload_template_expand_checksum "${payload%:}" 0 | ++ sed 's/:/\n/g' | wc -l ++} +-- +2.53.0 + diff --git a/queue-5.15/series b/queue-5.15/series index 1b2dcdd146..aa34ceab69 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -32,3 +32,20 @@ bluetooth-l2cap-clear-chan-ident-on-ecred-reconfigur.patch bluetooth-l2cap-fix-possible-crash-on-l2cap_ecred_co.patch gpio-rockchip-convert-bank-clk-to-devm_clk_get_enabl.patch sctp-fix-race-between-sctp_wait_for_connect-and-peel.patch +batman-adv-v-stop-ogmv2-on-disabled-interface.patch +batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch +batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch +batman-adv-tvlv-reject-oversized-tvlv-packets.patch +batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch +selftests-forwarding-lib-add-helpers-for-checksum-ha.patch +batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch +batman-adv-tt-fix-toctou-race-for-reported-vlans.patch +batman-adv-tt-avoid-empty-vlan-responses.patch +batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch +wifi-brcmfmac-fix-use-after-free-when-rescheduling-b.patch +drm-i915-psr-add-defininitions-for-intel_wa_register.patch +drm-i915-psr-read-intel-dpcd-workaround-register.patch +drm-dp-add-edp-1.5-bit-definition.patch +drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch +revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch +rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-82 diff --git a/queue-5.15/wifi-brcmfmac-fix-use-after-free-when-rescheduling-b.patch b/queue-5.15/wifi-brcmfmac-fix-use-after-free-when-rescheduling-b.patch new file mode 100644 index 0000000000..7f397b6d02 --- /dev/null +++ b/queue-5.15/wifi-brcmfmac-fix-use-after-free-when-rescheduling-b.patch @@ -0,0 +1,107 @@ +From 4e6cc40ea75661a3abf4b56f56c1d7c9a57a5ff9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 13:54:31 +0800 +Subject: wifi: brcmfmac: fix use-after-free when rescheduling + brcmf_btcoex_info work +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Duoming Zhou + +[ Upstream commit 9cb83d4be0b9b697eae93d321e0da999f9cdfcfc ] + +The brcmf_btcoex_detach() only shuts down the btcoex timer, if the +flag timer_on is false. However, the brcmf_btcoex_timerfunc(), which +runs as timer handler, sets timer_on to false. This creates critical +race conditions: + +1.If brcmf_btcoex_detach() is called while brcmf_btcoex_timerfunc() +is executing, it may observe timer_on as false and skip the call to +timer_shutdown_sync(). + +2.The brcmf_btcoex_timerfunc() may then reschedule the brcmf_btcoex_info +worker after the cancel_work_sync() has been executed, resulting in +use-after-free bugs. + +The use-after-free bugs occur in two distinct scenarios, depending on +the timing of when the brcmf_btcoex_info struct is freed relative to +the execution of its worker thread. + +Scenario 1: Freed before the worker is scheduled + +The brcmf_btcoex_info is deallocated before the worker is scheduled. +A race condition can occur when schedule_work(&bt_local->work) is +called after the target memory has been freed. The sequence of events +is detailed below: + +CPU0 | CPU1 +brcmf_btcoex_detach | brcmf_btcoex_timerfunc + | bt_local->timer_on = false; + if (cfg->btcoex->timer_on) | + ... | + cancel_work_sync(); | + ... | + kfree(cfg->btcoex); // FREE | + | schedule_work(&bt_local->work); // USE + +Scenario 2: Freed after the worker is scheduled + +The brcmf_btcoex_info is freed after the worker has been scheduled +but before or during its execution. In this case, statements within +the brcmf_btcoex_handler() — such as the container_of macro and +subsequent dereferences of the brcmf_btcoex_info object will cause +a use-after-free access. The following timeline illustrates this +scenario: + +CPU0 | CPU1 +brcmf_btcoex_detach | brcmf_btcoex_timerfunc + | bt_local->timer_on = false; + if (cfg->btcoex->timer_on) | + ... | + cancel_work_sync(); | + ... | schedule_work(); // Reschedule + | + kfree(cfg->btcoex); // FREE | brcmf_btcoex_handler() // Worker + /* | btci = container_of(....); // USE + The kfree() above could | ... + also occur at any point | btci-> // USE + during the worker's execution| + */ | + +To resolve the race conditions, drop the conditional check and call +timer_shutdown_sync() directly. It can deactivate the timer reliably, +regardless of its current state. Once stopped, the timer_on state is +then set to false. + +Fixes: 61730d4dfffc ("brcmfmac: support critical protocol API for DHCP") +Acked-by: Arend van Spriel +Signed-off-by: Duoming Zhou +Link: https://patch.msgid.link/20250822050839.4413-1-duoming@zju.edu.cn +Signed-off-by: Johannes Berg +Signed-off-by: Robert Garcia +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c +index f9f18ff451ea7c..f46e4090021777 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c +@@ -392,10 +392,8 @@ void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg) + if (!cfg->btcoex) + return; + +- if (cfg->btcoex->timer_on) { +- cfg->btcoex->timer_on = false; +- del_timer_sync(&cfg->btcoex->timer); +- } ++ del_timer_sync(&cfg->btcoex->timer); ++ cfg->btcoex->timer_on = false; + + cancel_work_sync(&cfg->btcoex->work); + +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/queue-6.1/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch new file mode 100644 index 0000000000..0b0d3f265b --- /dev/null +++ b/queue-6.1/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch @@ -0,0 +1,232 @@ +From 68a8ce4e3c644d0a83009441df3fa637ed068bac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 21:57:07 +0200 +Subject: batman-adv: bla: avoid double decrement of bla.num_requests + +From: Sven Eckelmann + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++-------- + net/batman-adv/soft-interface.c | 1 + + net/batman-adv/types.h | 39 ++++++++++++++++---- + 3 files changed, 67 insertions(+), 24 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index 76d8c91c156a3b..cfb1eb25c6ac4d 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -516,8 +516,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, + 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); +@@ -546,9 +546,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, + 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; +@@ -651,10 +655,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw) + 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); + } + + /** +@@ -875,10 +881,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, + /* 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); +@@ -1257,9 +1265,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int 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); +@@ -1510,7 +1522,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) + 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. + * +@@ -1519,14 +1531,21 @@ static void batadv_bla_periodic_work(struct work_struct *work) + * 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(); + } +diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c +index d7b525a495e451..c0549abcae7a64 100644 +--- a/net/batman-adv/soft-interface.c ++++ b/net/batman-adv/soft-interface.c +@@ -785,6 +785,7 @@ static int batadv_softif_init_late(struct net_device *dev) + 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); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 41959a4eea7d3f..12fbcc89581adf 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1026,6 +1026,12 @@ struct batadv_priv_bla { + /** @num_requests: number of bla requests in flight */ + 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 + */ +@@ -1748,6 +1754,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 + */ +@@ -1773,16 +1800,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; +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch b/queue-6.1/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch new file mode 100644 index 0000000000..d0edac1307 --- /dev/null +++ b/queue-6.1/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch @@ -0,0 +1,53 @@ +From a1ca0972a60493b83816b12870b9ea3cfea52191 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 21:57:36 +0200 +Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface + +From: Sven Eckelmann + +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 +Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816 +[ switch to old "mesh_iface" name "soft_iface" ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index f614709e6cda74..76d8c91c156a3b 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -356,12 +356,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac, + sizeof(local_claim_dest)); + local_claim_dest.type = claimtype; + +- soft_iface = primary_if->soft_iface; ++ soft_iface = READ_ONCE(primary_if->soft_iface); ++ if (!soft_iface) ++ goto out; + + skb = arp_create(ARPOP_REPLY, ETH_P_ARP, + /* IP DST: 0.0.0.0 */ + zeroip, +- primary_if->soft_iface, ++ soft_iface, + /* IP SRC: 0.0.0.0 */ + zeroip, + /* Ethernet DST: Broadcast */ +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch b/queue-6.1/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch new file mode 100644 index 0000000000..7b29f61065 --- /dev/null +++ b/queue-6.1/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch @@ -0,0 +1,230 @@ +From f7dc6d9db4eaf933b61e16c80e22cde0030c4be7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 21:56:18 +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 + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++---------- + net/batman-adv/types.h | 3 ++ + 2 files changed, 60 insertions(+), 19 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index c93d30462054ce..c52e3b82889868 100644 +--- 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(struct batadv_hard_iface *hard_iface) + 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) +@@ -528,8 +530,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, + * @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, +@@ -553,13 +557,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + + 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; +@@ -581,6 +585,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + 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 */ +@@ -610,8 +616,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr, + * @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, +@@ -663,14 +671,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, + 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; + } + } + +@@ -782,6 +792,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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); +@@ -810,11 +822,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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; +@@ -836,8 +845,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + /* 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; + } + +@@ -852,15 +864,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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); + } + +@@ -875,6 +900,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) + 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 +@@ -2278,6 +2314,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1, + + 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); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index afd71f061c409a..41959a4eea7d3f 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -82,6 +82,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; + }; +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch b/queue-6.1/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch new file mode 100644 index 0000000000..6cc9cda47f --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch @@ -0,0 +1,60 @@ +From 19af649dbcd2e1b375b1041cb1d79e1133664527 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 22:25:20 +0200 +Subject: batman-adv: tp_meter: directly shut down timer on cleanup + +From: Sven Eckelmann + +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") +[ adapt pre-hunk to old del_timer* names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 61e6cb5bce8ec5..707f05aa14791f 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -384,13 +384,7 @@ static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv, + atomic_dec(&tp_vars->bat_priv->tp_num); + + /* kill the timer and remove its reference */ +- del_timer_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 +- * del_timer(), it would not re-arm itself once again because the status +- * is OFF now +- */ +- del_timer(&tp_vars->timer); ++ timer_shutdown_sync(&tp_vars->timer); + batadv_tp_vars_put(tp_vars); + } + +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tt-avoid-empty-vlan-responses.patch b/queue-6.1/batman-adv-tt-avoid-empty-vlan-responses.patch new file mode 100644 index 0000000000..83cf98280c --- /dev/null +++ b/queue-6.1/batman-adv-tt-avoid-empty-vlan-responses.patch @@ -0,0 +1,91 @@ +From 9d0b6f2b04a199e7db832b5a18664c2445697373 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 21:44:38 +0200 +Subject: batman-adv: tt: avoid empty VLAN responses + +From: Sven Eckelmann + +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") +[ Context, drop flex array dependency ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 21 ++++++++++++++++++--- + 1 file changed, 18 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index ac6c05971348d8..e0503c8f24c353 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -843,17 +843,19 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + 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 = sizeof(**tt_data); +@@ -861,7 +863,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + + /* 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; +@@ -882,14 +884,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ 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 = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_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; + +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch b/queue-6.1/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch new file mode 100644 index 0000000000..9eeb573bff --- /dev/null +++ b/queue-6.1/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch @@ -0,0 +1,78 @@ +From b79a409c07b9c294e6342ce42bdf10431a60cdc3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 23:02:13 +0200 +Subject: batman-adv: tt: fix TOCTOU race for reported vlans + +From: Sven Eckelmann + +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") +[ Context, drop flex array dependency ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 6c40d690ac6c3a..ac6c05971348d8 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -934,11 +934,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_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 = sizeof(**tt_data); +@@ -964,6 +961,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ num_vlan = 0; + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); + if (vlan_entries < 1) +@@ -974,8 +972,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + tt_vlan->reserved = 0; + + tt_vlan++; ++ num_vlan++; + } + ++ /* recalculate in case number of VLANs reduced */ ++ change_offset = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_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; + +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch b/queue-6.1/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch new file mode 100644 index 0000000000..e13eead5b9 --- /dev/null +++ b/queue-6.1/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch @@ -0,0 +1,62 @@ +From aaa2b73ec93daa8afe658d29061aa9a3da688234 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 21:43:08 +0200 +Subject: batman-adv: tt: reject oversized local TVLV buffers + +From: Sven Eckelmann + +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") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index f1e93a60482038..6c40d690ac6c3a 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -924,12 +924,12 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + { + struct batadv_tvlv_tt_vlan_data *tt_vlan; + struct batadv_softif_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->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { +@@ -948,8 +948,10 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + 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) { +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch b/queue-6.1/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch new file mode 100644 index 0000000000..f3960f5e33 --- /dev/null +++ b/queue-6.1/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch @@ -0,0 +1,196 @@ +From 904f40570779f3c8e51cfcf7c6ca46ab95084ea4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:51:13 +0200 +Subject: batman-adv: tvlv: abort OGM send on tvlv append failure + +From: Sven Eckelmann + +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") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + 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(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index 495196b741040e..c93d30462054ce 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -782,6 +782,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + u32 seqno; + u16 tvlv_len = 0; + unsigned long send_time; ++ int ret; + + lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); + +@@ -805,9 +806,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + * 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); +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 24beb06f7c332a..57d0c6862f449c 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -272,9 +272,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + struct batadv_hard_iface *hard_iface; + struct batadv_ogm2_packet *ogm_packet; + struct sk_buff *skb, *skb_tmp; +- unsigned char *ogm_buff; +- int ogm_buff_len; +- u16 tvlv_len = 0; ++ unsigned char **ogm_buff; ++ int *ogm_buff_len; ++ u16 tvlv_len; + int ret; + + lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex); +@@ -282,25 +282,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + 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)); +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 7ec2e234388454..83e20b0be747b6 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -8,6 +8,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff, + * 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(struct batadv_priv *bat_priv, + 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(struct batadv_priv *bat_priv, + + 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(struct batadv_priv *bat_priv, + + end: + spin_unlock_bh(&bat_priv->tvlv.container_list_lock); +- return tvlv_value_len; ++ ++ return tvlv_len_ret; + } + + /** +diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h +index 4cf8af00fc11a0..485b2a6070994b 100644 +--- a/net/batman-adv/tvlv.h ++++ b/net/batman-adv/tvlv.h +@@ -15,7 +15,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, +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tvlv-reject-oversized-tvlv-packets.patch b/queue-6.1/batman-adv-tvlv-reject-oversized-tvlv-packets.patch new file mode 100644 index 0000000000..b39a750c1d --- /dev/null +++ b/queue-6.1/batman-adv-tvlv-reject-oversized-tvlv-packets.patch @@ -0,0 +1,85 @@ +From 6d2cb253dc1b123413640e284d211f8c1fb70d2e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 21:47:23 +0200 +Subject: batman-adv: tvlv: reject oversized TVLV packets + +From: Sven Eckelmann + +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 +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 83e20b0be747b6..99e5e8518dcc9a 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version) + * + * 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(struct batadv_priv *bat_priv, + { + 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); +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-v-stop-ogmv2-on-disabled-interface.patch b/queue-6.1/batman-adv-v-stop-ogmv2-on-disabled-interface.patch new file mode 100644 index 0000000000..1e7b9cdc2c --- /dev/null +++ b/queue-6.1/batman-adv-v-stop-ogmv2-on-disabled-interface.patch @@ -0,0 +1,149 @@ +From 3d84d70d95aa09f24375c45f8fbfc7f0b503ae44 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:32:39 +0200 +Subject: batman-adv: v: stop OGMv2 on disabled interface + +From: Sven Eckelmann + +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 +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +[ switch to old "mesh_iface" name "soft_iface" ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------ + 1 file changed, 21 insertions(+), 12 deletions(-) + +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index deef817b28f0ae..24beb06f7c332a 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -116,14 +116,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv) + + /** + * 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->soft_iface); +- + if (hard_iface->if_status != BATADV_IF_ACTIVE) { + kfree_skb(skb); + return; +@@ -190,6 +190,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) + + /** + * 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 +@@ -199,7 +200,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) + * + * 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; +@@ -229,27 +231,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface) + 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->soft_iface); ++ if (hard_iface->soft_iface != bat_priv->soft_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); +@@ -348,7 +355,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + 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(); +@@ -388,12 +395,14 @@ void batadv_v_ogm_aggr_work(struct 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->soft_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); +@@ -583,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv, + 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); +-- +2.53.0 + diff --git a/queue-6.1/bpf-fix-a-few-selftest-failures-due-to-llvm18-change.patch b/queue-6.1/bpf-fix-a-few-selftest-failures-due-to-llvm18-change.patch new file mode 100644 index 0000000000..d8525fbdee --- /dev/null +++ b/queue-6.1/bpf-fix-a-few-selftest-failures-due-to-llvm18-change.patch @@ -0,0 +1,89 @@ +From 1f78df8b5f9c9ee8cbd8fb72f79a3630a0938941 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:41:17 +0200 +Subject: bpf: Fix a few selftest failures due to llvm18 change + +From: Yonghong Song + +[ Upstream commit b16904fd9f01b580db357ef2b1cc9e86d89576c2 ] + +With latest upstream llvm18, the following test cases failed: + + $ ./test_progs -j + #13/2 bpf_cookie/multi_kprobe_link_api:FAIL + #13/3 bpf_cookie/multi_kprobe_attach_api:FAIL + #13 bpf_cookie:FAIL + #77 fentry_fexit:FAIL + #78/1 fentry_test/fentry:FAIL + #78 fentry_test:FAIL + #82/1 fexit_test/fexit:FAIL + #82 fexit_test:FAIL + #112/1 kprobe_multi_test/skel_api:FAIL + #112/2 kprobe_multi_test/link_api_addrs:FAIL + [...] + #112 kprobe_multi_test:FAIL + #356/17 test_global_funcs/global_func17:FAIL + #356 test_global_funcs:FAIL + +Further analysis shows llvm upstream patch [1] is responsible for the above +failures. For example, for function bpf_fentry_test7() in net/bpf/test_run.c, +without [1], the asm code is: + + 0000000000000400 : + 400: f3 0f 1e fa endbr64 + 404: e8 00 00 00 00 callq 0x409 + 409: 48 89 f8 movq %rdi, %rax + 40c: c3 retq + 40d: 0f 1f 00 nopl (%rax) + +... and with [1], the asm code is: + + 0000000000005d20 : + 5d20: e8 00 00 00 00 callq 0x5d25 + 5d25: c3 retq + +... and is called instead of +and this caused test failures for #13/#77 etc. except #356. + +For test case #356/17, with [1] (progs/test_global_func17.c)), the main prog +looks like: + + 0000000000000000 : + 0: b4 00 00 00 2a 00 00 00 w0 = 0x2a + 1: 95 00 00 00 00 00 00 00 exit + +... which passed verification while the test itself expects a verification +failure. + +Let us add 'barrier_var' style asm code in both places to prevent function +specialization which caused selftests failure. + + [1] https://github.com/llvm/llvm-project/pull/72903 + +Signed-off-by: Yonghong Song +Signed-off-by: Daniel Borkmann +Link: https://lore.kernel.org/bpf/20231127050342.1945270-1-yonghong.song@linux.dev +[ Note: The change to test_run.c conflicted and was dropped. The related + tests are not failing anyway. ] +Signed-off-by: Paul Chaignon +Acked-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/progs/test_global_func17.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/testing/selftests/bpf/progs/test_global_func17.c b/tools/testing/selftests/bpf/progs/test_global_func17.c +index a32e11c7d933ee..5de44b09e8ec17 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func17.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func17.c +@@ -5,6 +5,7 @@ + + __noinline int foo(int *p) + { ++ barrier_var(p); + return p ? (*p = 42) : 0; + } + +-- +2.53.0 + diff --git a/queue-6.1/drm-dp-add-edp-1.5-bit-definition.patch b/queue-6.1/drm-dp-add-edp-1.5-bit-definition.patch new file mode 100644 index 0000000000..1a6f8fe000 --- /dev/null +++ b/queue-6.1/drm-dp-add-edp-1.5-bit-definition.patch @@ -0,0 +1,41 @@ +From 4fc18faf30e836829a43e90153d8dd7ee4d5a561 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 11:41:56 +0300 +Subject: drm/dp: Add eDP 1.5 bit definition +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Suraj Kandpal + +commit 5dfc37a6b77bf6beedbd30d70184b54e1a08ccac upstream. + +Add the eDP revision bit value for 1.5. + +Spec: eDPv1.5 Table 16-5 +Signed-off-by: Suraj Kandpal +Reviewed-by: Arun R Murthy +Tested-by: Ben Kao +Acked-by: Maarten Lankhorst +Link: https://patchwork.freedesktop.org/patch/msgid/20250206063253.2827017-2-suraj.kandpal@intel.com +Signed-off-by: Jouni Högander +Signed-off-by: Sasha Levin +--- + include/drm/display/drm_dp.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h +index b235d6833e27d9..7d3700490b68f0 100644 +--- a/include/drm/display/drm_dp.h ++++ b/include/drm/display/drm_dp.h +@@ -955,6 +955,7 @@ + # define DP_EDP_14 0x03 + # define DP_EDP_14a 0x04 /* eDP 1.4a */ + # define DP_EDP_14b 0x05 /* eDP 1.4b */ ++# define DP_EDP_15 0x06 /* eDP 1.5 */ + + #define DP_EDP_GENERAL_CAP_1 0x701 + # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP (1 << 0) +-- +2.53.0 + diff --git a/queue-6.1/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-6.1/drm-i915-psr-add-defininitions-for-intel_wa_register.patch new file mode 100644 index 0000000000..989e6a6663 --- /dev/null +++ b/queue-6.1/drm-i915-psr-add-defininitions-for-intel_wa_register.patch @@ -0,0 +1,73 @@ +From de285df3961bfcee727a1db556a0809c1eda0703 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 11:41:58 +0300 +Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD + register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream. + +EDP specification says: + +"If either VSC SDP is unable to be transmitted 100 ns before the SU region, +the Source device may optionally transmit the VSC SDP during the prior +video scan line’s HBlank period There is a Intel specific drm dp register +currently containing bits related how TCON can support PSR2 with SDP on +prior line." + +Unfortunately many panels are having problems in implementing this. So +there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to +figure out if this is properly implemented on a panel or if panel doesn't +require that 100 ns delay before the SU region. Here are the definitions in +this custom DPCD address: + +0 = Panel doesn't support SDP on prior line +1 = Panel supports SDP on prior line +2 = Panel doesn't have 100ns requirement +3 = Reserved + +Add definitions for this new register and it's values into new header +intel_dpcd.h. + +v2: add INTEL_DPCD_ prefix to definitions + +Bspec: 74741 +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com +(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h + +diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h +new file mode 100644 +index 00000000000000..4aea5326f2ed48 +--- /dev/null ++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: MIT */ ++/* ++ * Copyright © 2026 Intel Corporation ++ */ ++ ++#ifndef __INTEL_DPCD_H__ ++#define __INTEL_DPCD_H__ ++ ++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS 0x3f0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK REG_GENMASK(1, 0) ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1 0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE 1 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE 2 ++ ++#endif /* __INTEL_DPCD_H__ */ +-- +2.53.0 + diff --git a/queue-6.1/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-6.1/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch new file mode 100644 index 0000000000..e05b9c7e3d --- /dev/null +++ b/queue-6.1/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch @@ -0,0 +1,77 @@ +From 58a274e4fbb6d4e252a1ae8d2778587dcf3df8f5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 11:41: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 + +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: # v5.15+ +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com +(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Jouni Högander +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_psr.c | 27 +++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index 01fadf300ff679..3c5553b21fa894 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -818,6 +818,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp, + return true; + } + ++static bool apply_scanline_indication_wa(struct intel_dp *intel_dp, ++ struct intel_crtc_state *crtc_state) ++{ ++ u8 early_scanline_support = intel_dp->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_dp *intel_dp, + struct intel_crtc_state *crtc_state) + { +@@ -839,7 +863,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d + return false; + + crtc_state->req_psr2_sdp_prior_scanline = true; +- return true; ++ ++ return apply_scanline_indication_wa(intel_dp, crtc_state); + } + + static bool _compute_psr2_wake_times(struct intel_dp *intel_dp, +-- +2.53.0 + diff --git a/queue-6.1/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-6.1/drm-i915-psr-read-intel-dpcd-workaround-register.patch new file mode 100644 index 0000000000..f1ac52cd93 --- /dev/null +++ b/queue-6.1/drm-i915-psr-read-intel-dpcd-workaround-register.patch @@ -0,0 +1,67 @@ +From 67a94aeaa3005fbf324321fe756ad27611534263 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 11:41:57 +0300 +Subject: drm/i915/psr: Read Intel DPCD workaround register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream. + +Read Intel DPCD workaround register and store it into +intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains +only PSR workaround for PSR2 SDP on prior scanline implementation. + +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com +(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_display_types.h | 1 + + drivers/gpu/drm/i915/display/intel_psr.c | 7 +++++++ + 2 files changed, 8 insertions(+) + +diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h +index a8bf91a21cb246..a26c082bdc3289 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_types.h ++++ b/drivers/gpu/drm/i915/display/intel_display_types.h +@@ -1637,6 +1637,7 @@ struct intel_dp { + u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE]; + u8 fec_capable; + u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]; ++ u8 intel_wa_dpcd; + /* source rates */ + int num_source_rates; + const int *source_rates; +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index a465b192931085..01fadf300ff679 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -31,6 +31,7 @@ + #include "intel_crtc.h" + #include "intel_de.h" + #include "intel_display_types.h" ++#include "intel_dpcd.h" + #include "intel_dp_aux.h" + #include "intel_hdmi.h" + #include "intel_psr.h" +@@ -388,6 +389,12 @@ void intel_psr_init_dpcd(struct intel_dp *intel_dp) + intel_dp_get_su_granularity(intel_dp); + } + } ++ ++ if (intel_dp->psr.sink_psr2_support) ++ drm_dp_dpcd_read(&intel_dp->aux, ++ INTEL_DPCD_INTEL_WA_REGISTER_CAPS, ++ &intel_dp->intel_wa_dpcd, ++ sizeof(intel_dp->intel_wa_dpcd)); + } + + static void intel_psr_enable_sink(struct intel_dp *intel_dp) +-- +2.53.0 + diff --git a/queue-6.1/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch b/queue-6.1/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch new file mode 100644 index 0000000000..79b52ae39b --- /dev/null +++ b/queue-6.1/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch @@ -0,0 +1,67 @@ +From 0492d9ae159d6151c3e09cd2d80ebf2f33eaa129 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 13:02:30 +0800 +Subject: mm/page_alloc: clear page->private in free_pages_prepare() + +From: Mikhail Gavrilov + +[ Upstream commit ac1ea219590c09572ed5992dc233bbf7bb70fef9 ] + +Several subsystems (slub, shmem, ttm, etc.) use page->private but don't +clear it before freeing pages. When these pages are later allocated as +high-order pages and split via split_page(), tail pages retain stale +page->private values. + +This causes a use-after-free in the swap subsystem. The swap code uses +page->private to track swap count continuations, assuming freshly +allocated pages have page->private == 0. When stale values are present, +swap_count_continued() incorrectly assumes the continuation list is valid +and iterates over uninitialized page->lru containing LIST_POISON values, +causing a crash: + + KASAN: maybe wild-memory-access in range [0xdead000000000100-0xdead000000000107] + RIP: 0010:__do_sys_swapoff+0x1151/0x1860 + +Fix this by clearing page->private in free_pages_prepare(), ensuring all +freed pages have clean state regardless of previous use. + +Link: https://lkml.kernel.org/r/20260207173615.146159-1-mikhail.v.gavrilov@gmail.com +Fixes: 3b8000ae185c ("mm/vmalloc: huge vmalloc backing pages should be split rather than compound") +Signed-off-by: Mikhail Gavrilov +Suggested-by: Zi Yan +Acked-by: Zi Yan +Acked-by: David Hildenbrand (Arm) +Reviewed-by: Vlastimil Babka +Cc: Brendan Jackman +Cc: Chris Li +Cc: Hugh Dickins +Cc: Johannes Weiner +Cc: Kairui Song +Cc: Matthew Wilcox (Oracle) +Cc: Michal Hocko +Cc: Nicholas Piggin +Cc: Suren Baghdasaryan +Cc: +Signed-off-by: Andrew Morton +[backport: context only] +Signed-off-by: Li Wang +Signed-off-by: Sasha Levin +--- + mm/page_alloc.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/mm/page_alloc.c b/mm/page_alloc.c +index 4c17b9ceff8099..93c63b5fad2889 100644 +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -1483,6 +1483,7 @@ static __always_inline bool free_pages_prepare(struct page *page, + + page_cpupid_reset_last(page); + page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP; ++ page->private = 0; + reset_page_owner(page, order); + page_table_check_free(page, order); + +-- +2.53.0 + diff --git a/queue-6.1/net-packet-convert-po-has_vnet_hdr-to-an-atomic-flag.patch b/queue-6.1/net-packet-convert-po-has_vnet_hdr-to-an-atomic-flag.patch new file mode 100644 index 0000000000..c9cc3284aa --- /dev/null +++ b/queue-6.1/net-packet-convert-po-has_vnet_hdr-to-an-atomic-flag.patch @@ -0,0 +1,143 @@ +From 180d118cc965bf89a41abdc5925f9a749af9d486 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Mar 2023 01:10:12 +0000 +Subject: net/packet: convert po->has_vnet_hdr to an atomic flag + +From: Eric Dumazet + +[ Upstream commit 50d935eafee292fc432d5ac8c8715a6492961abc ] + +po->has_vnet_hdr can be read locklessly. + +Signed-off-by: Eric Dumazet +Signed-off-by: David S. Miller +Stable-dep-of: 2c054e17d9d4 ("net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd()") +Signed-off-by: Sasha Levin +--- + net/packet/af_packet.c | 19 ++++++++++--------- + net/packet/diag.c | 2 +- + net/packet/internal.h | 2 +- + 3 files changed, 12 insertions(+), 11 deletions(-) + +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 490bfec158035e..50d9618d85f3c7 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -2357,7 +2357,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, + netoff = TPACKET_ALIGN(po->tp_hdrlen + + (maclen < 16 ? 16 : maclen)) + + po->tp_reserve; +- if (po->has_vnet_hdr) { ++ if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) { + netoff += sizeof(struct virtio_net_hdr); + do_vnet = true; + } +@@ -2831,7 +2831,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) + size_max = po->tx_ring.frame_size + - (po->tp_hdrlen - sizeof(struct sockaddr_ll)); + +- if ((size_max > dev->mtu + reserve + VLAN_HLEN) && !po->has_vnet_hdr) ++ if ((size_max > dev->mtu + reserve + VLAN_HLEN) && ++ !packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) + size_max = dev->mtu + reserve + VLAN_HLEN; + + timeo = sock_sndtimeo(&po->sk, msg->msg_flags & MSG_DONTWAIT); +@@ -2866,7 +2867,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) + status = TP_STATUS_SEND_REQUEST; + hlen = LL_RESERVED_SPACE(dev); + tlen = dev->needed_tailroom; +- if (po->has_vnet_hdr) { ++ if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) { + vnet_hdr = data; + data += sizeof(*vnet_hdr); + tp_len -= sizeof(*vnet_hdr); +@@ -2894,7 +2895,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) + addr, hlen, copylen, &sockc); + if (likely(tp_len >= 0) && + tp_len > dev->mtu + reserve && +- !po->has_vnet_hdr && ++ !packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR) && + !packet_extra_vlan_len_allowed(dev, skb)) + tp_len = -EMSGSIZE; + +@@ -2913,7 +2914,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) + } + } + +- if (po->has_vnet_hdr) { ++ if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) { + if (virtio_net_hdr_to_skb(skb, vnet_hdr, vio_le())) { + tp_len = -EINVAL; + goto tpacket_error; +@@ -3041,7 +3042,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) + + if (sock->type == SOCK_RAW) + reserve = dev->hard_header_len; +- if (po->has_vnet_hdr) { ++ if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) { + err = packet_snd_vnet_parse(msg, &len, &vnet_hdr); + if (err) + goto out_unlock; +@@ -3506,7 +3507,7 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + + packet_rcv_try_clear_pressure(pkt_sk(sk)); + +- if (pkt_sk(sk)->has_vnet_hdr) { ++ if (packet_sock_flag(pkt_sk(sk), PACKET_SOCK_HAS_VNET_HDR)) { + err = packet_rcv_vnet(msg, skb, &len); + if (err) + goto out_free; +@@ -4002,7 +4003,7 @@ packet_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, + if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) { + ret = -EBUSY; + } else { +- po->has_vnet_hdr = !!val; ++ packet_sock_flag_set(po, PACKET_SOCK_HAS_VNET_HDR, val); + ret = 0; + } + release_sock(sk); +@@ -4136,7 +4137,7 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, + val = packet_sock_flag(po, PACKET_SOCK_ORIGDEV); + break; + case PACKET_VNET_HDR: +- val = po->has_vnet_hdr; ++ val = packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR); + break; + case PACKET_VERSION: + val = po->tp_version; +diff --git a/net/packet/diag.c b/net/packet/diag.c +index 677d442cd930fe..a3bd91dba43945 100644 +--- a/net/packet/diag.c ++++ b/net/packet/diag.c +@@ -27,7 +27,7 @@ static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb) + pinfo.pdi_flags |= PDI_AUXDATA; + if (packet_sock_flag(po, PACKET_SOCK_ORIGDEV)) + pinfo.pdi_flags |= PDI_ORIGDEV; +- if (po->has_vnet_hdr) ++ if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) + pinfo.pdi_flags |= PDI_VNETHDR; + if (packet_sock_flag(po, PACKET_SOCK_TP_LOSS)) + pinfo.pdi_flags |= PDI_LOSS; +diff --git a/net/packet/internal.h b/net/packet/internal.h +index 82a997824e5733..0956e4a934492d 100644 +--- a/net/packet/internal.h ++++ b/net/packet/internal.h +@@ -118,7 +118,6 @@ struct packet_sock { + struct mutex pg_vec_lock; + unsigned long flags; + unsigned int running; /* bind_lock must be held */ +- unsigned int has_vnet_hdr:1; /* writer must hold sock lock */ + int pressure; + int ifindex; /* bound device */ + __be16 num; +@@ -146,6 +145,7 @@ enum packet_sock_flags { + PACKET_SOCK_AUXDATA, + PACKET_SOCK_TX_HAS_OFF, + PACKET_SOCK_TP_LOSS, ++ PACKET_SOCK_HAS_VNET_HDR, + }; + + static inline void packet_sock_flag_set(struct packet_sock *po, +-- +2.53.0 + diff --git a/queue-6.1/net-packet-convert-po-running-to-an-atomic-flag.patch b/queue-6.1/net-packet-convert-po-running-to-an-atomic-flag.patch new file mode 100644 index 0000000000..fef9779501 --- /dev/null +++ b/queue-6.1/net-packet-convert-po-running-to-an-atomic-flag.patch @@ -0,0 +1,151 @@ +From 13efc9420cf09d14d84d4fa00f016fbc8fd92933 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Mar 2023 01:10:13 +0000 +Subject: net/packet: convert po->running to an atomic flag + +From: Eric Dumazet + +[ Upstream commit 61edf479818e63978cabd243b82ca80f8948a313 ] + +Instead of consuming 32 bits for po->running, use +one available bit in po->flags. + +Signed-off-by: Eric Dumazet +Signed-off-by: David S. Miller +Stable-dep-of: 2c054e17d9d4 ("net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd()") +Signed-off-by: Sasha Levin +--- + net/packet/af_packet.c | 20 ++++++++++---------- + net/packet/diag.c | 2 +- + net/packet/internal.h | 2 +- + 3 files changed, 12 insertions(+), 12 deletions(-) + +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 50d9618d85f3c7..36347814ec7ceb 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -340,14 +340,14 @@ static void __register_prot_hook(struct sock *sk) + { + struct packet_sock *po = pkt_sk(sk); + +- if (!po->running) { ++ if (!packet_sock_flag(po, PACKET_SOCK_RUNNING)) { + if (po->fanout) + __fanout_link(sk, po); + else + dev_add_pack(&po->prot_hook); + + sock_hold(sk); +- po->running = 1; ++ packet_sock_flag_set(po, PACKET_SOCK_RUNNING, 1); + } + } + +@@ -369,7 +369,7 @@ static void __unregister_prot_hook(struct sock *sk, bool sync) + + lockdep_assert_held_once(&po->bind_lock); + +- po->running = 0; ++ packet_sock_flag_set(po, PACKET_SOCK_RUNNING, 0); + + if (po->fanout) + __fanout_unlink(sk, po); +@@ -389,7 +389,7 @@ static void unregister_prot_hook(struct sock *sk, bool sync) + { + struct packet_sock *po = pkt_sk(sk); + +- if (po->running) ++ if (packet_sock_flag(po, PACKET_SOCK_RUNNING)) + __unregister_prot_hook(sk, sync); + } + +@@ -1834,7 +1834,7 @@ static int fanout_add(struct sock *sk, struct fanout_args *args) + err = -EINVAL; + + spin_lock(&po->bind_lock); +- if (po->running && ++ if (packet_sock_flag(po, PACKET_SOCK_RUNNING) && + match->type == type && + match->prot_hook.type == po->prot_hook.type && + match->prot_hook.dev == po->prot_hook.dev) { +@@ -3277,7 +3277,7 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex, + + if (need_rehook) { + dev_hold(dev); +- if (po->running) { ++ if (packet_sock_flag(po, PACKET_SOCK_RUNNING)) { + rcu_read_unlock(); + /* prevents packet_notifier() from calling + * register_prot_hook() +@@ -3290,7 +3290,7 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex, + dev->ifindex); + } + +- BUG_ON(po->running); ++ BUG_ON(packet_sock_flag(po, PACKET_SOCK_RUNNING)); + WRITE_ONCE(po->num, proto); + po->prot_hook.type = proto; + +@@ -4230,7 +4230,7 @@ static int packet_notifier(struct notifier_block *this, + case NETDEV_DOWN: + if (dev->ifindex == po->ifindex) { + spin_lock(&po->bind_lock); +- if (po->running) { ++ if (packet_sock_flag(po, PACKET_SOCK_RUNNING)) { + __unregister_prot_hook(sk, false); + sk->sk_err = ENETDOWN; + if (!sock_flag(sk, SOCK_DEAD)) +@@ -4541,7 +4541,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, + + /* Detach socket from network */ + spin_lock(&po->bind_lock); +- was_running = po->running; ++ was_running = packet_sock_flag(po, PACKET_SOCK_RUNNING); + num = po->num; + WRITE_ONCE(po->num, 0); + if (was_running) +@@ -4752,7 +4752,7 @@ static int packet_seq_show(struct seq_file *seq, void *v) + s->sk_type, + ntohs(READ_ONCE(po->num)), + READ_ONCE(po->ifindex), +- po->running, ++ packet_sock_flag(po, PACKET_SOCK_RUNNING), + atomic_read(&s->sk_rmem_alloc), + from_kuid_munged(seq_user_ns(seq), sock_i_uid(s)), + sock_i_ino(s)); +diff --git a/net/packet/diag.c b/net/packet/diag.c +index a3bd91dba43945..cd30cc619c6b45 100644 +--- a/net/packet/diag.c ++++ b/net/packet/diag.c +@@ -21,7 +21,7 @@ static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb) + pinfo.pdi_tstamp = po->tp_tstamp; + + pinfo.pdi_flags = 0; +- if (po->running) ++ if (packet_sock_flag(po, PACKET_SOCK_RUNNING)) + pinfo.pdi_flags |= PDI_RUNNING; + if (packet_sock_flag(po, PACKET_SOCK_AUXDATA)) + pinfo.pdi_flags |= PDI_AUXDATA; +diff --git a/net/packet/internal.h b/net/packet/internal.h +index 0956e4a934492d..9e50bf06131f29 100644 +--- a/net/packet/internal.h ++++ b/net/packet/internal.h +@@ -117,7 +117,6 @@ struct packet_sock { + spinlock_t bind_lock; + struct mutex pg_vec_lock; + unsigned long flags; +- unsigned int running; /* bind_lock must be held */ + int pressure; + int ifindex; /* bound device */ + __be16 num; +@@ -146,6 +145,7 @@ enum packet_sock_flags { + PACKET_SOCK_TX_HAS_OFF, + PACKET_SOCK_TP_LOSS, + PACKET_SOCK_HAS_VNET_HDR, ++ PACKET_SOCK_RUNNING, + }; + + static inline void packet_sock_flag_set(struct packet_sock *po, +-- +2.53.0 + diff --git a/queue-6.1/net-packet-convert-po-tp_loss-to-an-atomic-flag.patch b/queue-6.1/net-packet-convert-po-tp_loss-to-an-atomic-flag.patch new file mode 100644 index 0000000000..f8f2494daa --- /dev/null +++ b/queue-6.1/net-packet-convert-po-tp_loss-to-an-atomic-flag.patch @@ -0,0 +1,92 @@ +From 77ae60fdc1c3953fa714875b4114a83a77382503 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Mar 2023 01:10:11 +0000 +Subject: net/packet: convert po->tp_loss to an atomic flag + +From: Eric Dumazet + +[ Upstream commit 164bddace2e03f6005e650cb88f101a66ebdc05a ] + +tp_loss can be read locklessly. + +Convert it to an atomic flag to avoid races. + +Signed-off-by: Eric Dumazet +Signed-off-by: David S. Miller +Stable-dep-of: 2c054e17d9d4 ("net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd()") +Signed-off-by: Sasha Levin +--- + net/packet/af_packet.c | 6 +++--- + net/packet/diag.c | 2 +- + net/packet/internal.h | 4 ++-- + 3 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 1ceb8f765114b3..490bfec158035e 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -2900,7 +2900,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) + + if (unlikely(tp_len < 0)) { + tpacket_error: +- if (po->tp_loss) { ++ if (packet_sock_flag(po, PACKET_SOCK_TP_LOSS)) { + __packet_set_status(po, ph, + TP_STATUS_AVAILABLE); + packet_increment_head(&po->tx_ring); +@@ -3957,7 +3957,7 @@ packet_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, + if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) { + ret = -EBUSY; + } else { +- po->tp_loss = !!val; ++ packet_sock_flag_set(po, PACKET_SOCK_TP_LOSS, val); + ret = 0; + } + release_sock(sk); +@@ -4166,7 +4166,7 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, + val = po->tp_reserve; + break; + case PACKET_LOSS: +- val = po->tp_loss; ++ val = packet_sock_flag(po, PACKET_SOCK_TP_LOSS); + break; + case PACKET_TIMESTAMP: + val = po->tp_tstamp; +diff --git a/net/packet/diag.c b/net/packet/diag.c +index 057ee37bd0766c..677d442cd930fe 100644 +--- a/net/packet/diag.c ++++ b/net/packet/diag.c +@@ -29,7 +29,7 @@ static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb) + pinfo.pdi_flags |= PDI_ORIGDEV; + if (po->has_vnet_hdr) + pinfo.pdi_flags |= PDI_VNETHDR; +- if (po->tp_loss) ++ if (packet_sock_flag(po, PACKET_SOCK_TP_LOSS)) + pinfo.pdi_flags |= PDI_LOSS; + + return nla_put(nlskb, PACKET_DIAG_INFO, sizeof(pinfo), &pinfo); +diff --git a/net/packet/internal.h b/net/packet/internal.h +index 31bac09a687233..82a997824e5733 100644 +--- a/net/packet/internal.h ++++ b/net/packet/internal.h +@@ -118,8 +118,7 @@ struct packet_sock { + struct mutex pg_vec_lock; + unsigned long flags; + unsigned int running; /* bind_lock must be held */ +- unsigned int has_vnet_hdr:1, /* writer must hold sock lock */ +- tp_loss:1; ++ unsigned int has_vnet_hdr:1; /* writer must hold sock lock */ + int pressure; + int ifindex; /* bound device */ + __be16 num; +@@ -146,6 +145,7 @@ enum packet_sock_flags { + PACKET_SOCK_ORIGDEV, + PACKET_SOCK_AUXDATA, + PACKET_SOCK_TX_HAS_OFF, ++ PACKET_SOCK_TP_LOSS, + }; + + static inline void packet_sock_flag_set(struct packet_sock *po, +-- +2.53.0 + diff --git a/queue-6.1/net-packet-convert-po-tp_tx_has_off-to-an-atomic-fla.patch b/queue-6.1/net-packet-convert-po-tp_tx_has_off-to-an-atomic-fla.patch new file mode 100644 index 0000000000..1fbbb732ca --- /dev/null +++ b/queue-6.1/net-packet-convert-po-tp_tx_has_off-to-an-atomic-fla.patch @@ -0,0 +1,77 @@ +From 7f5cb358e765ae78093a336a4e03a1c85474e9db Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Mar 2023 01:10:10 +0000 +Subject: net/packet: convert po->tp_tx_has_off to an atomic flag + +From: Eric Dumazet + +[ Upstream commit 7438344660fa55b33b8234c1797c886eb73667a7 ] + +This is to use existing space in po->flags, and reclaim +the storage used by the non atomic bit fields. + +Signed-off-by: Eric Dumazet +Signed-off-by: David S. Miller +Stable-dep-of: 2c054e17d9d4 ("net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd()") +Signed-off-by: Sasha Levin +--- + net/packet/af_packet.c | 6 +++--- + net/packet/internal.h | 4 ++-- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 502d2f6de18a29..1ceb8f765114b3 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -2723,7 +2723,7 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame, + return -EMSGSIZE; + } + +- if (unlikely(po->tp_tx_has_off)) { ++ if (unlikely(packet_sock_flag(po, PACKET_SOCK_TX_HAS_OFF))) { + int off_min, off_max; + + off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll); +@@ -4064,7 +4064,7 @@ packet_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, + + lock_sock(sk); + if (!po->rx_ring.pg_vec && !po->tx_ring.pg_vec) +- po->tp_tx_has_off = !!val; ++ packet_sock_flag_set(po, PACKET_SOCK_TX_HAS_OFF, val); + + release_sock(sk); + return 0; +@@ -4191,7 +4191,7 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, + lv = sizeof(rstats); + break; + case PACKET_TX_HAS_OFF: +- val = po->tp_tx_has_off; ++ val = packet_sock_flag(po, PACKET_SOCK_TX_HAS_OFF); + break; + case PACKET_QDISC_BYPASS: + val = packet_use_direct_xmit(po); +diff --git a/net/packet/internal.h b/net/packet/internal.h +index b2edfe6fc8e770..31bac09a687233 100644 +--- a/net/packet/internal.h ++++ b/net/packet/internal.h +@@ -119,8 +119,7 @@ struct packet_sock { + unsigned long flags; + unsigned int running; /* bind_lock must be held */ + unsigned int has_vnet_hdr:1, /* writer must hold sock lock */ +- tp_loss:1, +- tp_tx_has_off:1; ++ tp_loss:1; + int pressure; + int ifindex; /* bound device */ + __be16 num; +@@ -146,6 +145,7 @@ static inline struct packet_sock *pkt_sk(struct sock *sk) + enum packet_sock_flags { + PACKET_SOCK_ORIGDEV, + PACKET_SOCK_AUXDATA, ++ PACKET_SOCK_TX_HAS_OFF, + }; + + static inline void packet_sock_flag_set(struct packet_sock *po, +-- +2.53.0 + diff --git a/queue-6.1/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpa.patch b/queue-6.1/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpa.patch new file mode 100644 index 0000000000..edf5cce9b6 --- /dev/null +++ b/queue-6.1/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpa.patch @@ -0,0 +1,96 @@ +From d224d8234c6d43a6c634e3671b057f508601244f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 18 Apr 2026 19:20:06 +0800 +Subject: net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd() + +From: Bingquan Chen + +[ Upstream commit 2c054e17d9d41f1020376806c7f750834ced4dc5 ] + +In tpacket_snd(), when PACKET_VNET_HDR is enabled, vnet_hdr points +directly into the mmap'd TX ring buffer shared with userspace. The +kernel validates the header via __packet_snd_vnet_parse() but then +re-reads all fields later in virtio_net_hdr_to_skb(). A concurrent +userspace thread can modify the vnet_hdr fields between validation +and use, bypassing all safety checks. + +The non-TPACKET path (packet_snd()) already correctly copies vnet_hdr +to a stack-local variable. All other vnet_hdr consumers in the kernel +(tun.c, tap.c, virtio_net.c) also use stack copies. The TPACKET TX +path is the only caller of virtio_net_hdr_to_skb() that reads directly +from user-controlled shared memory. + +Fix this by copying vnet_hdr from the mmap'd ring buffer to a +stack-local variable before validation and use, consistent with the +approach used in packet_snd() and all other callers. + +Fixes: 1d036d25e560 ("packet: tpacket_snd gso and checksum offload") +Signed-off-by: Bingquan Chen +Reviewed-by: Willem de Bruijn +Link: https://patch.msgid.link/20260418112006.78823-1-patzilla007@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/packet/af_packet.c | 25 +++++++++++++++---------- + 1 file changed, 15 insertions(+), 10 deletions(-) + +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 36347814ec7ceb..f3850784d66404 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -2767,7 +2767,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) + { + struct sk_buff *skb = NULL; + struct net_device *dev; +- struct virtio_net_hdr *vnet_hdr = NULL; ++ struct virtio_net_hdr vnet_hdr; ++ bool has_vnet_hdr = false; + struct sockcm_cookie sockc; + __be16 proto; + int err, reserve = 0; +@@ -2868,16 +2869,20 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) + hlen = LL_RESERVED_SPACE(dev); + tlen = dev->needed_tailroom; + if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) { +- vnet_hdr = data; +- data += sizeof(*vnet_hdr); +- tp_len -= sizeof(*vnet_hdr); +- if (tp_len < 0 || +- __packet_snd_vnet_parse(vnet_hdr, tp_len)) { ++ data += sizeof(vnet_hdr); ++ tp_len -= sizeof(vnet_hdr); ++ if (tp_len < 0) { ++ tp_len = -EINVAL; ++ goto tpacket_error; ++ } ++ memcpy(&vnet_hdr, data - sizeof(vnet_hdr), sizeof(vnet_hdr)); ++ if (__packet_snd_vnet_parse(&vnet_hdr, tp_len)) { + tp_len = -EINVAL; + goto tpacket_error; + } + copylen = __virtio16_to_cpu(vio_le(), +- vnet_hdr->hdr_len); ++ vnet_hdr.hdr_len); ++ has_vnet_hdr = true; + } + copylen = max_t(int, copylen, dev->hard_header_len); + skb = sock_alloc_send_skb(&po->sk, +@@ -2914,12 +2919,12 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) + } + } + +- if (packet_sock_flag(po, PACKET_SOCK_HAS_VNET_HDR)) { +- if (virtio_net_hdr_to_skb(skb, vnet_hdr, vio_le())) { ++ if (has_vnet_hdr) { ++ if (virtio_net_hdr_to_skb(skb, &vnet_hdr, vio_le())) { + tp_len = -EINVAL; + goto tpacket_error; + } +- virtio_net_hdr_set_proto(skb, vnet_hdr); ++ virtio_net_hdr_set_proto(skb, &vnet_hdr); + } + + skb->destructor = tpacket_destruct_skb; +-- +2.53.0 + diff --git a/queue-6.1/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-25226 b/queue-6.1/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-25226 new file mode 100644 index 0000000000..4878f90e86 --- /dev/null +++ b/queue-6.1/rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-25226 @@ -0,0 +1,68 @@ +From acb45424624dbd089eb23e89603533941407cff5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 13:13:39 +0200 +Subject: RDMA/rxe: Fix double free in rxe_srq_from_init + +From: Jiasheng Jiang + +commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream. + +In rxe_srq_from_init(), the queue pointer 'q' is assigned to +'srq->rq.queue' before copying the SRQ number to user space. +If copy_to_user() fails, the function calls rxe_queue_cleanup() +to free the queue, but leaves the now-invalid pointer in +'srq->rq.queue'. + +The caller of rxe_srq_from_init() (rxe_create_srq) eventually +calls rxe_srq_cleanup() upon receiving the error, which triggers +a second rxe_queue_cleanup() on the same memory, leading to a +double free. + +The call trace looks like this: + kmem_cache_free+0x.../0x... + rxe_queue_cleanup+0x1a/0x30 [rdma_rxe] + rxe_srq_cleanup+0x42/0x60 [rdma_rxe] + rxe_elem_release+0x31/0x70 [rdma_rxe] + rxe_create_srq+0x12b/0x1a0 [rdma_rxe] + ib_create_srq_user+0x9a/0x150 [ib_core] + +Fix this by moving 'srq->rq.queue = q' after copy_to_user. + +Fixes: aae0484e15f0 ("IB/rxe: avoid srq memory leak") +Signed-off-by: Jiasheng Jiang +Link: https://patch.msgid.link/20260112015412.29458-1-jiashengjiangcool@gmail.com +Reviewed-by: Zhu Yanjun +Signed-off-by: Leon Romanovsky +[bwh: Backported to 6.1: There was no assignment to init->attr.max_wr + here; don't add it] +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/infiniband/sw/rxe/rxe_srq.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c +index 02b39498c370d2..038a9cd55413e0 100644 +--- a/drivers/infiniband/sw/rxe/rxe_srq.c ++++ b/drivers/infiniband/sw/rxe/rxe_srq.c +@@ -69,8 +69,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, + return -ENOMEM; + } + +- srq->rq.queue = q; +- + err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, udata, q->buf, + q->buf_size, &q->ip); + if (err) { +@@ -87,6 +85,8 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, + } + } + ++ srq->rq.queue = q; ++ + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.1/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch b/queue-6.1/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch new file mode 100644 index 0000000000..2a73135d0b --- /dev/null +++ b/queue-6.1/revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch @@ -0,0 +1,35 @@ +From a3c06ee57522a4446b12cdab414e52d175fad96a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 13:13:17 +0200 +Subject: Revert "RDMA/rxe: Fix double free in rxe_srq_from_init" + +From: Ben Hutchings + +This reverts commit d286f0d4e3ad3caf5f0e673cdad7bf89bf37d947, which +was commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 upstream. The +backported version did not move but duplicated the problematic +assignment, so it did not fix the bug. A proper backport will follow. + +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + drivers/infiniband/sw/rxe/rxe_srq.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c +index 115ff5428f6cfb..02b39498c370d2 100644 +--- a/drivers/infiniband/sw/rxe/rxe_srq.c ++++ b/drivers/infiniband/sw/rxe/rxe_srq.c +@@ -87,9 +87,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, + } + } + +- srq->rq.queue = q; +- init->attr.max_wr = srq->rq.max_wr; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.1/revert-selftests-bpf-add-a-cgroup-prog-bpf_get_ns_cu.patch b/queue-6.1/revert-selftests-bpf-add-a-cgroup-prog-bpf_get_ns_cu.patch new file mode 100644 index 0000000000..a4b9923762 --- /dev/null +++ b/queue-6.1/revert-selftests-bpf-add-a-cgroup-prog-bpf_get_ns_cu.patch @@ -0,0 +1,146 @@ +From 7b922c862481a1e71cc830831d0d5cfe080a4e4f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:41:46 +0200 +Subject: Revert "selftests/bpf: Add a cgroup prog + bpf_get_ns_current_pid_tgid() test" + +From: Paul Chaignon + +This reverts commit 4d8fb7ed7a55 ("selftests/bpf: Add a cgroup prog +bpf_get_ns_current_pid_tgid() test"). + +That commit should have never been backported to 6.1 because it +introduces a test for a feature that isn't supported: +bpf_get_ns_current_pid_tgid() cannot be called from cgroup BPF programs +in 6.1. + +Signed-off-by: Paul Chaignon +Acked-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + .../bpf/prog_tests/ns_current_pid_tgid.c | 73 ------------------- + .../bpf/progs/test_ns_current_pid_tgid.c | 7 -- + 2 files changed, 80 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c b/tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c +index 2c57ceede095eb..a84c41862ff8c9 100644 +--- a/tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c ++++ b/tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c +@@ -12,7 +12,6 @@ + #include + #include + #include +-#include "network_helpers.h" + + #define STACK_SIZE (1024 * 1024) + static char child_stack[STACK_SIZE]; +@@ -75,50 +74,6 @@ static int test_current_pid_tgid_tp(void *args) + return ret; + } + +-static int test_current_pid_tgid_cgrp(void *args) +-{ +- struct test_ns_current_pid_tgid__bss *bss; +- struct test_ns_current_pid_tgid *skel; +- int server_fd = -1, ret = -1, err; +- int cgroup_fd = *(int *)args; +- pid_t tgid, pid; +- +- skel = test_ns_current_pid_tgid__open(); +- if (!ASSERT_OK_PTR(skel, "test_ns_current_pid_tgid__open")) +- return ret; +- +- bpf_program__set_autoload(skel->progs.cgroup_bind4, true); +- +- err = test_ns_current_pid_tgid__load(skel); +- if (!ASSERT_OK(err, "test_ns_current_pid_tgid__load")) +- goto cleanup; +- +- bss = skel->bss; +- if (get_pid_tgid(&pid, &tgid, bss)) +- goto cleanup; +- +- skel->links.cgroup_bind4 = bpf_program__attach_cgroup( +- skel->progs.cgroup_bind4, cgroup_fd); +- if (!ASSERT_OK_PTR(skel->links.cgroup_bind4, "bpf_program__attach_cgroup")) +- goto cleanup; +- +- server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0); +- if (!ASSERT_GE(server_fd, 0, "start_server")) +- goto cleanup; +- +- if (!ASSERT_EQ(bss->user_pid, pid, "pid")) +- goto cleanup; +- if (!ASSERT_EQ(bss->user_tgid, tgid, "tgid")) +- goto cleanup; +- ret = 0; +- +-cleanup: +- if (server_fd >= 0) +- close(server_fd); +- test_ns_current_pid_tgid__destroy(skel); +- return ret; +-} +- + static void test_ns_current_pid_tgid_new_ns(int (*fn)(void *), void *arg) + { + int wstatus; +@@ -140,25 +95,6 @@ static void test_ns_current_pid_tgid_new_ns(int (*fn)(void *), void *arg) + return; + } + +-static void test_in_netns(int (*fn)(void *), void *arg) +-{ +- struct nstoken *nstoken = NULL; +- +- SYS(cleanup, "ip netns add ns_current_pid_tgid"); +- SYS(cleanup, "ip -net ns_current_pid_tgid link set dev lo up"); +- +- nstoken = open_netns("ns_current_pid_tgid"); +- if (!ASSERT_OK_PTR(nstoken, "open_netns")) +- goto cleanup; +- +- test_ns_current_pid_tgid_new_ns(fn, arg); +- +-cleanup: +- if (nstoken) +- close_netns(nstoken); +- SYS_NOFAIL("ip netns del ns_current_pid_tgid"); +-} +- + /* TODO: use a different tracepoint */ + void serial_test_ns_current_pid_tgid(void) + { +@@ -166,13 +102,4 @@ void serial_test_ns_current_pid_tgid(void) + test_current_pid_tgid_tp(NULL); + if (test__start_subtest("new_ns_tp")) + test_ns_current_pid_tgid_new_ns(test_current_pid_tgid_tp, NULL); +- if (test__start_subtest("new_ns_cgrp")) { +- int cgroup_fd = -1; +- +- cgroup_fd = test__join_cgroup("/sock_addr"); +- if (ASSERT_GE(cgroup_fd, 0, "join_cgroup")) { +- test_in_netns(test_current_pid_tgid_cgrp, &cgroup_fd); +- close(cgroup_fd); +- } +- } + } +diff --git a/tools/testing/selftests/bpf/progs/test_ns_current_pid_tgid.c b/tools/testing/selftests/bpf/progs/test_ns_current_pid_tgid.c +index d0010e698f6688..aa3ec7ca16d9b6 100644 +--- a/tools/testing/selftests/bpf/progs/test_ns_current_pid_tgid.c ++++ b/tools/testing/selftests/bpf/progs/test_ns_current_pid_tgid.c +@@ -28,11 +28,4 @@ int tp_handler(const void *ctx) + return 0; + } + +-SEC("?cgroup/bind4") +-int cgroup_bind4(struct bpf_sock_addr *ctx) +-{ +- get_pid_tgid(); +- return 1; +-} +- + char _license[] SEC("license") = "GPL"; +-- +2.53.0 + diff --git a/queue-6.1/revert-selftests-bpf-add-tests-for-_opts-variants-of.patch b/queue-6.1/revert-selftests-bpf-add-tests-for-_opts-variants-of.patch new file mode 100644 index 0000000000..0bfbd63627 --- /dev/null +++ b/queue-6.1/revert-selftests-bpf-add-tests-for-_opts-variants-of.patch @@ -0,0 +1,173 @@ +From 5cebc9d33436dcf0e359610bba98f970eca386cf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:40:57 +0200 +Subject: Revert "selftests/bpf: Add tests for _opts variants of + bpf_*_get_fd_by_id()" + +From: Paul Chaignon + +This reverts commit 45108a7b4866 ("selftests/bpf: Add tests for _opts +variants of bpf_*_get_fd_by_id()"). As explained in the previous patch, +it introduces a new selftest for a feature that doesn't exist in 6.1. It +was backported as a stable-dep of a1914d146622 ("selftests/bpf: +Workaround strict bpf_lsm return value check"), also reverted in the +previous patch. + +Signed-off-by: Paul Chaignon +Acked-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/DENYLIST.s390x | 1 - + .../bpf/prog_tests/libbpf_get_fd_by_id_opts.c | 87 ------------------- + .../bpf/progs/test_libbpf_get_fd_by_id_opts.c | 36 -------- + 3 files changed, 124 deletions(-) + delete mode 100644 tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c + delete mode 100644 tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c + +diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x +index beef1232a47aeb..0fb03b8047d535 100644 +--- a/tools/testing/selftests/bpf/DENYLIST.s390x ++++ b/tools/testing/selftests/bpf/DENYLIST.s390x +@@ -76,4 +76,3 @@ lookup_key # JIT does not support calling kernel f + verify_pkcs7_sig # JIT does not support calling kernel function (kfunc) + kfunc_dynptr_param # JIT does not support calling kernel function (kfunc) + deny_namespace # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) +-libbpf_get_fd_by_id_opts # failed to attach: ERROR: strerror_r(-524)=22 (trampoline) +diff --git a/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c +deleted file mode 100644 +index 25e5dfa9c315ce..00000000000000 +--- a/tools/testing/selftests/bpf/prog_tests/libbpf_get_fd_by_id_opts.c ++++ /dev/null +@@ -1,87 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +- +-/* +- * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH +- * +- * Author: Roberto Sassu +- */ +- +-#include +- +-#include "test_libbpf_get_fd_by_id_opts.skel.h" +- +-void test_libbpf_get_fd_by_id_opts(void) +-{ +- struct test_libbpf_get_fd_by_id_opts *skel; +- struct bpf_map_info info_m = {}; +- __u32 len = sizeof(info_m), value; +- int ret, zero = 0, fd = -1; +- LIBBPF_OPTS(bpf_get_fd_by_id_opts, fd_opts_rdonly, +- .open_flags = BPF_F_RDONLY, +- ); +- +- skel = test_libbpf_get_fd_by_id_opts__open_and_load(); +- if (!ASSERT_OK_PTR(skel, +- "test_libbpf_get_fd_by_id_opts__open_and_load")) +- return; +- +- ret = test_libbpf_get_fd_by_id_opts__attach(skel); +- if (!ASSERT_OK(ret, "test_libbpf_get_fd_by_id_opts__attach")) +- goto close_prog; +- +- ret = bpf_obj_get_info_by_fd(bpf_map__fd(skel->maps.data_input), +- &info_m, &len); +- if (!ASSERT_OK(ret, "bpf_obj_get_info_by_fd")) +- goto close_prog; +- +- fd = bpf_map_get_fd_by_id(info_m.id); +- if (!ASSERT_LT(fd, 0, "bpf_map_get_fd_by_id")) +- goto close_prog; +- +- fd = bpf_map_get_fd_by_id_opts(info_m.id, NULL); +- if (!ASSERT_LT(fd, 0, "bpf_map_get_fd_by_id_opts")) +- goto close_prog; +- +- fd = bpf_map_get_fd_by_id_opts(info_m.id, &fd_opts_rdonly); +- if (!ASSERT_GE(fd, 0, "bpf_map_get_fd_by_id_opts")) +- goto close_prog; +- +- /* Map lookup should work with read-only fd. */ +- ret = bpf_map_lookup_elem(fd, &zero, &value); +- if (!ASSERT_OK(ret, "bpf_map_lookup_elem")) +- goto close_prog; +- +- if (!ASSERT_EQ(value, 0, "map value mismatch")) +- goto close_prog; +- +- /* Map update should not work with read-only fd. */ +- ret = bpf_map_update_elem(fd, &zero, &len, BPF_ANY); +- if (!ASSERT_LT(ret, 0, "bpf_map_update_elem")) +- goto close_prog; +- +- /* Map update should work with read-write fd. */ +- ret = bpf_map_update_elem(bpf_map__fd(skel->maps.data_input), &zero, +- &len, BPF_ANY); +- if (!ASSERT_OK(ret, "bpf_map_update_elem")) +- goto close_prog; +- +- /* Prog get fd with opts set should not work (no kernel support). */ +- ret = bpf_prog_get_fd_by_id_opts(0, &fd_opts_rdonly); +- if (!ASSERT_EQ(ret, -EINVAL, "bpf_prog_get_fd_by_id_opts")) +- goto close_prog; +- +- /* Link get fd with opts set should not work (no kernel support). */ +- ret = bpf_link_get_fd_by_id_opts(0, &fd_opts_rdonly); +- if (!ASSERT_EQ(ret, -EINVAL, "bpf_link_get_fd_by_id_opts")) +- goto close_prog; +- +- /* BTF get fd with opts set should not work (no kernel support). */ +- ret = bpf_btf_get_fd_by_id_opts(0, &fd_opts_rdonly); +- ASSERT_EQ(ret, -EINVAL, "bpf_btf_get_fd_by_id_opts"); +- +-close_prog: +- if (fd >= 0) +- close(fd); +- +- test_libbpf_get_fd_by_id_opts__destroy(skel); +-} +diff --git a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c +deleted file mode 100644 +index f5ac5f3e89196f..00000000000000 +--- a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c ++++ /dev/null +@@ -1,36 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +- +-/* +- * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH +- * +- * Author: Roberto Sassu +- */ +- +-#include "vmlinux.h" +-#include +-#include +-#include +- +-/* From include/linux/mm.h. */ +-#define FMODE_WRITE 0x2 +- +-struct { +- __uint(type, BPF_MAP_TYPE_ARRAY); +- __uint(max_entries, 1); +- __type(key, __u32); +- __type(value, __u32); +-} data_input SEC(".maps"); +- +-char _license[] SEC("license") = "GPL"; +- +-SEC("lsm/bpf_map") +-int BPF_PROG(check_access, struct bpf_map *map, fmode_t fmode) +-{ +- if (map != (struct bpf_map *)&data_input) +- return 0; +- +- if (fmode & FMODE_WRITE) +- return -EACCES; +- +- return 0; +-} +-- +2.53.0 + diff --git a/queue-6.1/revert-selftests-bpf-workaround-strict-bpf_lsm-retur.patch b/queue-6.1/revert-selftests-bpf-workaround-strict-bpf_lsm-retur.patch new file mode 100644 index 0000000000..19704f795a --- /dev/null +++ b/queue-6.1/revert-selftests-bpf-workaround-strict-bpf_lsm-retur.patch @@ -0,0 +1,40 @@ +From 7387948319daf8a9508c239821524a836e3cb6bb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:40:48 +0200 +Subject: Revert "selftests/bpf: Workaround strict bpf_lsm return value check." + +From: Paul Chaignon + +This reverts commit a1914d146622 ("selftests/bpf: Workaround strict +bpf_lsm return value check"). It seems it was picked up by mistake. + +It applies to a selftest that didn't exist in 6.1. The whole selftest +was then backported as a stable-dep in commit 45108a7b4866 +("selftests/bpf: Add tests for _opts variants of bpf_*_get_fd_by_id()") +(reverted as well in the next patch). + +The new selftest covers the bpf_*_get_fd_by_id structures. Those don't +exist in 6.1 so the selftest shouldn't either. + +Signed-off-by: Paul Chaignon +Acked-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + .../testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c +index 568816307f7125..f5ac5f3e89196f 100644 +--- a/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c ++++ b/tools/testing/selftests/bpf/progs/test_libbpf_get_fd_by_id_opts.c +@@ -31,7 +31,6 @@ int BPF_PROG(check_access, struct bpf_map *map, fmode_t fmode) + + if (fmode & FMODE_WRITE) + return -EACCES; +- barrier(); + + return 0; + } +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-add-generic-bpf-program-tester-loader.patch b/queue-6.1/selftests-bpf-add-generic-bpf-program-tester-loader.patch new file mode 100644 index 0000000000..a3d6e6b9f0 --- /dev/null +++ b/queue-6.1/selftests-bpf-add-generic-bpf-program-tester-loader.patch @@ -0,0 +1,398 @@ +From 87e3312c479bf46aa628049f486bb2068cd9b49a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:39:58 +0200 +Subject: selftests/bpf: add generic BPF program tester-loader + +From: Andrii Nakryiko + +[ Upstream commit 537c3f66eac137a02ec50a40219d2da6597e5dc9 ] + +It's become a common pattern to have a collection of small BPF programs +in one BPF object file, each representing one test case. On user-space +side of such tests we maintain a table of program names and expected +failure or success, along with optional expected verifier log message. + +This works, but each set of tests reimplement this mundane code over and +over again, which is a waste of time for anyone trying to add a new set +of tests. Furthermore, it's quite error prone as it's way too easy to miss +some entries in these manually maintained test tables (as evidences by +dynptr_fail tests, in which ringbuf_release_uninit_dynptr subtest was +accidentally missed; this is fixed in next patch). + +So this patch implements generic test_loader, which accepts skeleton +name and handles the rest of details: opens and loads BPF object file, +making sure each program is tested in isolation. Optionally each test +case can specify expected BPF verifier log message. In case of failure, +tester makes sure to report verifier log, but it also reports verifier +log in verbose mode unconditionally. + +Now, the interesting deviation from existing custom implementations is +the use of btf_decl_tag attribute to specify expected-to-fail vs +expected-to-succeed markers and, optionally, expected log message +directly next to BPF program source code, eliminating the need to +manually create and update table of tests. + +We define few macros wrapping btf_decl_tag with a convention that all +values of btf_decl_tag start with "comment:" prefix, and then utilizing +a very simple "just_some_text_tag" or "some_key_name=" pattern to +define things like expected success/failure, expected verifier message, +extra verifier log level (if necessary). This approach is demonstrated +by next patch in which two existing sets of failure tests are converted. + +Tester supports both expected-to-fail and expected-to-succeed programs, +though this patch set didn't convert any existing expected-to-succeed +programs yet, as existing tests couple BPF program loading with their +further execution through attach or test_prog_run. One way to allow +testing scenarios like this would be ability to specify custom callback, +executed for each successfully loaded BPF program. This is left for +follow up patches, after some more analysis of existing test cases. + +This test_loader is, hopefully, a start of a test_verifier-like runner, +but integrated into test_progs infrastructure. It will allow much better +"user experience" of defining low-level verification tests that can take +advantage of all the libbpf-provided nicety features on BPF side: global +variables, declarative maps, etc. All while having a choice of defining +it in C or as BPF assembly (through __attribute__((naked)) functions and +using embedded asm), depending on what makes most sense in each +particular case. This will be explored in follow up patches as well. + +Acked-by: John Fastabend +Signed-off-by: Andrii Nakryiko +Link: https://lore.kernel.org/r/20221207201648.2990661-1-andrii@kernel.org +Signed-off-by: Alexei Starovoitov +Stable-dep-of: 95ebb376176c ("selftests/bpf: Convert test_global_funcs test to test_loader framework") +[ Note: Minor conflict in Makefile. ] +Signed-off-by: Paul Chaignon +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/Makefile | 2 +- + tools/testing/selftests/bpf/progs/bpf_misc.h | 5 + + tools/testing/selftests/bpf/test_loader.c | 233 +++++++++++++++++++ + tools/testing/selftests/bpf/test_progs.h | 33 +++ + 4 files changed, 272 insertions(+), 1 deletion(-) + create mode 100644 tools/testing/selftests/bpf/test_loader.c + +diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile +index b09205d925114a..541f251036ae7b 100644 +--- a/tools/testing/selftests/bpf/Makefile ++++ b/tools/testing/selftests/bpf/Makefile +@@ -519,7 +519,7 @@ TRUNNER_BPF_PROGS_DIR := progs + TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c \ + network_helpers.c testing_helpers.c \ + btf_helpers.c flow_dissector_load.h \ +- cap_helpers.c ++ cap_helpers.c test_loader.c + TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \ + $(OUTPUT)/liburandom_read.so \ + $(OUTPUT)/xdp_synproxy \ +diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h +index 5bb11fe595a439..4a01ea9113bfd7 100644 +--- a/tools/testing/selftests/bpf/progs/bpf_misc.h ++++ b/tools/testing/selftests/bpf/progs/bpf_misc.h +@@ -2,6 +2,11 @@ + #ifndef __BPF_MISC_H__ + #define __BPF_MISC_H__ + ++#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" msg))) ++#define __failure __attribute__((btf_decl_tag("comment:test_expect_failure"))) ++#define __success __attribute__((btf_decl_tag("comment:test_expect_success"))) ++#define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl))) ++ + #if defined(__TARGET_ARCH_x86) + #define SYSCALL_WRAPPER 1 + #define SYS_PREFIX "__x64_" +diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c +new file mode 100644 +index 00000000000000..679efb3aa785e3 +--- /dev/null ++++ b/tools/testing/selftests/bpf/test_loader.c +@@ -0,0 +1,233 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ ++#include ++#include ++#include ++ ++#define str_has_pfx(str, pfx) \ ++ (strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0) ++ ++#define TEST_LOADER_LOG_BUF_SZ 1048576 ++ ++#define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure" ++#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success" ++#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg=" ++#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level=" ++ ++struct test_spec { ++ const char *name; ++ bool expect_failure; ++ const char *expect_msg; ++ int log_level; ++}; ++ ++static int tester_init(struct test_loader *tester) ++{ ++ if (!tester->log_buf) { ++ tester->log_buf_sz = TEST_LOADER_LOG_BUF_SZ; ++ tester->log_buf = malloc(tester->log_buf_sz); ++ if (!ASSERT_OK_PTR(tester->log_buf, "tester_log_buf")) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++void test_loader_fini(struct test_loader *tester) ++{ ++ if (!tester) ++ return; ++ ++ free(tester->log_buf); ++} ++ ++static int parse_test_spec(struct test_loader *tester, ++ struct bpf_object *obj, ++ struct bpf_program *prog, ++ struct test_spec *spec) ++{ ++ struct btf *btf; ++ int func_id, i; ++ ++ memset(spec, 0, sizeof(*spec)); ++ ++ spec->name = bpf_program__name(prog); ++ ++ btf = bpf_object__btf(obj); ++ if (!btf) { ++ ASSERT_FAIL("BPF object has no BTF"); ++ return -EINVAL; ++ } ++ ++ func_id = btf__find_by_name_kind(btf, spec->name, BTF_KIND_FUNC); ++ if (func_id < 0) { ++ ASSERT_FAIL("failed to find FUNC BTF type for '%s'", spec->name); ++ return -EINVAL; ++ } ++ ++ for (i = 1; i < btf__type_cnt(btf); i++) { ++ const struct btf_type *t; ++ const char *s; ++ ++ t = btf__type_by_id(btf, i); ++ if (!btf_is_decl_tag(t)) ++ continue; ++ ++ if (t->type != func_id || btf_decl_tag(t)->component_idx != -1) ++ continue; ++ ++ s = btf__str_by_offset(btf, t->name_off); ++ if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) { ++ spec->expect_failure = true; ++ } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) { ++ spec->expect_failure = false; ++ } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) { ++ spec->expect_msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; ++ } else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) { ++ errno = 0; ++ spec->log_level = strtol(s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1, NULL, 0); ++ if (errno) { ++ ASSERT_FAIL("failed to parse test log level from '%s'", s); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static void prepare_case(struct test_loader *tester, ++ struct test_spec *spec, ++ struct bpf_object *obj, ++ struct bpf_program *prog) ++{ ++ int min_log_level = 0; ++ ++ if (env.verbosity > VERBOSE_NONE) ++ min_log_level = 1; ++ if (env.verbosity > VERBOSE_VERY) ++ min_log_level = 2; ++ ++ bpf_program__set_log_buf(prog, tester->log_buf, tester->log_buf_sz); ++ ++ /* Make sure we set at least minimal log level, unless test requirest ++ * even higher level already. Make sure to preserve independent log ++ * level 4 (verifier stats), though. ++ */ ++ if ((spec->log_level & 3) < min_log_level) ++ bpf_program__set_log_level(prog, (spec->log_level & 4) | min_log_level); ++ else ++ bpf_program__set_log_level(prog, spec->log_level); ++ ++ tester->log_buf[0] = '\0'; ++} ++ ++static void emit_verifier_log(const char *log_buf, bool force) ++{ ++ if (!force && env.verbosity == VERBOSE_NONE) ++ return; ++ fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log_buf); ++} ++ ++static void validate_case(struct test_loader *tester, ++ struct test_spec *spec, ++ struct bpf_object *obj, ++ struct bpf_program *prog, ++ int load_err) ++{ ++ if (spec->expect_msg) { ++ char *match; ++ ++ match = strstr(tester->log_buf, spec->expect_msg); ++ if (!ASSERT_OK_PTR(match, "expect_msg")) { ++ /* if we are in verbose mode, we've already emitted log */ ++ if (env.verbosity == VERBOSE_NONE) ++ emit_verifier_log(tester->log_buf, true /*force*/); ++ fprintf(stderr, "EXPECTED MSG: '%s'\n", spec->expect_msg); ++ return; ++ } ++ } ++} ++ ++/* this function is forced noinline and has short generic name to look better ++ * in test_progs output (in case of a failure) ++ */ ++static noinline ++void run_subtest(struct test_loader *tester, ++ const char *skel_name, ++ skel_elf_bytes_fn elf_bytes_factory) ++{ ++ LIBBPF_OPTS(bpf_object_open_opts, open_opts, .object_name = skel_name); ++ struct bpf_object *obj = NULL, *tobj; ++ struct bpf_program *prog, *tprog; ++ const void *obj_bytes; ++ size_t obj_byte_cnt; ++ int err; ++ ++ if (tester_init(tester) < 0) ++ return; /* failed to initialize tester */ ++ ++ obj_bytes = elf_bytes_factory(&obj_byte_cnt); ++ obj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, &open_opts); ++ if (!ASSERT_OK_PTR(obj, "obj_open_mem")) ++ return; ++ ++ bpf_object__for_each_program(prog, obj) { ++ const char *prog_name = bpf_program__name(prog); ++ struct test_spec spec; ++ ++ if (!test__start_subtest(prog_name)) ++ continue; ++ ++ /* if we can't derive test specification, go to the next test */ ++ err = parse_test_spec(tester, obj, prog, &spec); ++ if (!ASSERT_OK(err, "parse_test_spec")) ++ continue; ++ ++ tobj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, &open_opts); ++ if (!ASSERT_OK_PTR(tobj, "obj_open_mem")) /* shouldn't happen */ ++ continue; ++ ++ bpf_object__for_each_program(tprog, tobj) ++ bpf_program__set_autoload(tprog, false); ++ ++ bpf_object__for_each_program(tprog, tobj) { ++ /* only load specified program */ ++ if (strcmp(bpf_program__name(tprog), prog_name) == 0) { ++ bpf_program__set_autoload(tprog, true); ++ break; ++ } ++ } ++ ++ prepare_case(tester, &spec, tobj, tprog); ++ ++ err = bpf_object__load(tobj); ++ if (spec.expect_failure) { ++ if (!ASSERT_ERR(err, "unexpected_load_success")) { ++ emit_verifier_log(tester->log_buf, false /*force*/); ++ goto tobj_cleanup; ++ } ++ } else { ++ if (!ASSERT_OK(err, "unexpected_load_failure")) { ++ emit_verifier_log(tester->log_buf, true /*force*/); ++ goto tobj_cleanup; ++ } ++ } ++ ++ emit_verifier_log(tester->log_buf, false /*force*/); ++ validate_case(tester, &spec, tobj, tprog, err); ++ ++tobj_cleanup: ++ bpf_object__close(tobj); ++ } ++ ++ bpf_object__close(obj); ++} ++ ++void test_loader__run_subtests(struct test_loader *tester, ++ const char *skel_name, ++ skel_elf_bytes_fn elf_bytes_factory) ++{ ++ /* see comment in run_subtest() for why we do this function nesting */ ++ run_subtest(tester, skel_name, elf_bytes_factory); ++} +diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h +index feb14f14006d98..ff1caffefa5256 100644 +--- a/tools/testing/selftests/bpf/test_progs.h ++++ b/tools/testing/selftests/bpf/test_progs.h +@@ -1,4 +1,7 @@ + /* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __TEST_PROGS_H ++#define __TEST_PROGS_H ++ + #include + #include + #include +@@ -210,6 +213,12 @@ int test__join_cgroup(const char *path); + #define CHECK_ATTR(condition, tag, format...) \ + _CHECK(condition, tag, tattr.duration, format) + ++#define ASSERT_FAIL(fmt, args...) ({ \ ++ static int duration = 0; \ ++ CHECK(false, "", fmt"\n", ##args); \ ++ false; \ ++}) ++ + #define ASSERT_TRUE(actual, name) ({ \ + static int duration = 0; \ + bool ___ok = (actual); \ +@@ -395,3 +404,27 @@ int write_sysctl(const char *sysctl, const char *value); + #endif + + #define BPF_TESTMOD_TEST_FILE "/sys/kernel/bpf_testmod" ++ ++struct test_loader { ++ char *log_buf; ++ size_t log_buf_sz; ++ ++ struct bpf_object *obj; ++}; ++ ++typedef const void *(*skel_elf_bytes_fn)(size_t *sz); ++ ++extern void test_loader__run_subtests(struct test_loader *tester, ++ const char *skel_name, ++ skel_elf_bytes_fn elf_bytes_factory); ++ ++extern void test_loader_fini(struct test_loader *tester); ++ ++#define RUN_TESTS(skel) ({ \ ++ struct test_loader tester = {}; \ ++ \ ++ test_loader__run_subtests(&tester, #skel, skel##__elf_bytes); \ ++ test_loader_fini(&tester); \ ++}) ++ ++#endif /* __TEST_PROGS_H */ +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-add-read_build_id-function.patch b/queue-6.1/selftests-bpf-add-read_build_id-function.patch new file mode 100644 index 0000000000..c36979c8cb --- /dev/null +++ b/queue-6.1/selftests-bpf-add-read_build_id-function.patch @@ -0,0 +1,148 @@ +From 8f6682bf60d248de4e5130bc840b9b3828b172ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:41:07 +0200 +Subject: selftests/bpf: Add read_build_id function + +From: Jiri Olsa + +[ Upstream commit 88dc8b3605b38a440fba45edcc53a6c7a98eee3b ] + +Adding read_build_id function that parses out build id from +specified binary. + +It will replace extract_build_id and also be used in following +changes. + +Signed-off-by: Jiri Olsa +Link: https://lore.kernel.org/r/20230331093157.1749137-3-jolsa@kernel.org +Signed-off-by: Alexei Starovoitov +Fixes: be4e85369e5a ("selftests/bpf: Replace extract_build_id with read_build_id") +Signed-off-by: Paul Chaignon +Acked-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/trace_helpers.c | 82 +++++++++++++++++++++ + tools/testing/selftests/bpf/trace_helpers.h | 5 ++ + 2 files changed, 87 insertions(+) + +diff --git a/tools/testing/selftests/bpf/trace_helpers.c b/tools/testing/selftests/bpf/trace_helpers.c +index 9c4be2cdb21a02..afc33ba36ccc6e 100644 +--- a/tools/testing/selftests/bpf/trace_helpers.c ++++ b/tools/testing/selftests/bpf/trace_helpers.c +@@ -11,6 +11,9 @@ + #include + #include + #include "trace_helpers.h" ++#include ++#include ++#include + + #define DEBUGFS "/sys/kernel/debug/tracing/" + +@@ -224,3 +227,82 @@ ssize_t get_rel_offset(uintptr_t addr) + fclose(f); + return -EINVAL; + } ++ ++static int ++parse_build_id_buf(const void *note_start, Elf32_Word note_size, char *build_id) ++{ ++ Elf32_Word note_offs = 0; ++ ++ while (note_offs + sizeof(Elf32_Nhdr) < note_size) { ++ Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs); ++ ++ if (nhdr->n_type == 3 && nhdr->n_namesz == sizeof("GNU") && ++ !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 && ++ nhdr->n_descsz <= BPF_BUILD_ID_SIZE) { ++ memcpy(build_id, note_start + note_offs + ++ ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz); ++ memset(build_id + nhdr->n_descsz, 0, BPF_BUILD_ID_SIZE - nhdr->n_descsz); ++ return (int) nhdr->n_descsz; ++ } ++ ++ note_offs = note_offs + sizeof(Elf32_Nhdr) + ++ ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4); ++ } ++ ++ return -ENOENT; ++} ++ ++/* Reads binary from *path* file and returns it in the *build_id* buffer ++ * with *size* which is expected to be at least BPF_BUILD_ID_SIZE bytes. ++ * Returns size of build id on success. On error the error value is ++ * returned. ++ */ ++int read_build_id(const char *path, char *build_id, size_t size) ++{ ++ int fd, err = -EINVAL; ++ Elf *elf = NULL; ++ GElf_Ehdr ehdr; ++ size_t max, i; ++ ++ if (size < BPF_BUILD_ID_SIZE) ++ return -EINVAL; ++ ++ fd = open(path, O_RDONLY | O_CLOEXEC); ++ if (fd < 0) ++ return -errno; ++ ++ (void)elf_version(EV_CURRENT); ++ ++ elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); ++ if (!elf) ++ goto out; ++ if (elf_kind(elf) != ELF_K_ELF) ++ goto out; ++ if (!gelf_getehdr(elf, &ehdr)) ++ goto out; ++ ++ for (i = 0; i < ehdr.e_phnum; i++) { ++ GElf_Phdr mem, *phdr; ++ char *data; ++ ++ phdr = gelf_getphdr(elf, i, &mem); ++ if (!phdr) ++ goto out; ++ if (phdr->p_type != PT_NOTE) ++ continue; ++ data = elf_rawfile(elf, &max); ++ if (!data) ++ goto out; ++ if (phdr->p_offset + phdr->p_memsz > max) ++ goto out; ++ err = parse_build_id_buf(data + phdr->p_offset, phdr->p_memsz, build_id); ++ if (err > 0) ++ break; ++ } ++ ++out: ++ if (elf) ++ elf_end(elf); ++ close(fd); ++ return err; ++} +diff --git a/tools/testing/selftests/bpf/trace_helpers.h b/tools/testing/selftests/bpf/trace_helpers.h +index 238a9c98cde27f..709871f3285256 100644 +--- a/tools/testing/selftests/bpf/trace_helpers.h ++++ b/tools/testing/selftests/bpf/trace_helpers.h +@@ -4,6 +4,9 @@ + + #include + ++#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) ++#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) ++ + struct ksym { + long addr; + char *name; +@@ -21,4 +24,6 @@ void read_trace_pipe(void); + ssize_t get_uprobe_offset(const void *addr); + ssize_t get_rel_offset(uintptr_t addr); + ++int read_build_id(const char *path, char *build_id, size_t size); ++ + #endif +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-convert-test_global_funcs-test-to-test.patch b/queue-6.1/selftests-bpf-convert-test_global_funcs-test-to-test.patch new file mode 100644 index 0000000000..ea49b1f06c --- /dev/null +++ b/queue-6.1/selftests-bpf-convert-test_global_funcs-test-to-test.patch @@ -0,0 +1,642 @@ +From e18371de4fcaddfe95085750932ac7de86e02fe4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:40:40 +0200 +Subject: selftests/bpf: Convert test_global_funcs test to test_loader + framework + +From: Andrii Nakryiko + +[ Upstream commit 95ebb376176c52382293e05e63f142114a5e40ef ] + +Convert 17 test_global_funcs subtests into test_loader framework for +easier maintenance and more declarative way to define expected +failures/successes. + +Signed-off-by: Andrii Nakryiko +Signed-off-by: Daniel Borkmann +Acked-by: Stanislav Fomichev +Link: https://lore.kernel.org/bpf/20230216045954.3002473-3-andrii@kernel.org +Fixes: bbac91d57ac2 ("bpf: Allow reads from uninit stack") +[ Notes: This backport fixes backport commit bbac91d57ac2 ("bpf: Allow + reads from uninit stack"), which broke the BPF selftest build. A minor + conflict needed resolution in test_global_func10.c on the error + message. ] +Signed-off-by: Paul Chaignon +Signed-off-by: Sasha Levin +--- + .../bpf/prog_tests/test_global_funcs.c | 131 +++++------------- + .../selftests/bpf/progs/test_global_func1.c | 6 +- + .../selftests/bpf/progs/test_global_func10.c | 1 + + .../selftests/bpf/progs/test_global_func11.c | 4 +- + .../selftests/bpf/progs/test_global_func12.c | 4 +- + .../selftests/bpf/progs/test_global_func13.c | 4 +- + .../selftests/bpf/progs/test_global_func14.c | 4 +- + .../selftests/bpf/progs/test_global_func15.c | 4 +- + .../selftests/bpf/progs/test_global_func16.c | 4 +- + .../selftests/bpf/progs/test_global_func17.c | 4 +- + .../selftests/bpf/progs/test_global_func2.c | 43 +++++- + .../selftests/bpf/progs/test_global_func3.c | 10 +- + .../selftests/bpf/progs/test_global_func4.c | 55 +++++++- + .../selftests/bpf/progs/test_global_func5.c | 4 +- + .../selftests/bpf/progs/test_global_func6.c | 4 +- + .../selftests/bpf/progs/test_global_func7.c | 4 +- + .../selftests/bpf/progs/test_global_func8.c | 4 +- + .../selftests/bpf/progs/test_global_func9.c | 4 +- + 18 files changed, 172 insertions(+), 122 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c b/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c +index 7295cc60f72487..2ff4d5c7abfceb 100644 +--- a/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c ++++ b/tools/testing/selftests/bpf/prog_tests/test_global_funcs.c +@@ -1,104 +1,41 @@ + // SPDX-License-Identifier: GPL-2.0 + /* Copyright (c) 2020 Facebook */ + #include +- +-const char *err_str; +-bool found; +- +-static int libbpf_debug_print(enum libbpf_print_level level, +- const char *format, va_list args) +-{ +- char *log_buf; +- +- if (level != LIBBPF_WARN || +- strcmp(format, "libbpf: \n%s\n")) { +- vprintf(format, args); +- return 0; +- } +- +- log_buf = va_arg(args, char *); +- if (!log_buf) +- goto out; +- if (err_str && strstr(log_buf, err_str) == 0) +- found = true; +-out: +- printf(format, log_buf); +- return 0; +-} +- +-extern int extra_prog_load_log_flags; +- +-static int check_load(const char *file) +-{ +- struct bpf_object *obj = NULL; +- struct bpf_program *prog; +- int err; +- +- found = false; +- +- obj = bpf_object__open_file(file, NULL); +- err = libbpf_get_error(obj); +- if (err) +- return err; +- +- prog = bpf_object__next_program(obj, NULL); +- if (!prog) { +- err = -ENOENT; +- goto err_out; +- } +- +- bpf_program__set_flags(prog, BPF_F_TEST_RND_HI32); +- bpf_program__set_log_level(prog, extra_prog_load_log_flags); +- +- err = bpf_object__load(obj); +- +-err_out: +- bpf_object__close(obj); +- return err; +-} +- +-struct test_def { +- const char *file; +- const char *err_str; +-}; ++#include "test_global_func1.skel.h" ++#include "test_global_func2.skel.h" ++#include "test_global_func3.skel.h" ++#include "test_global_func4.skel.h" ++#include "test_global_func5.skel.h" ++#include "test_global_func6.skel.h" ++#include "test_global_func7.skel.h" ++#include "test_global_func8.skel.h" ++#include "test_global_func9.skel.h" ++#include "test_global_func10.skel.h" ++#include "test_global_func11.skel.h" ++#include "test_global_func12.skel.h" ++#include "test_global_func13.skel.h" ++#include "test_global_func14.skel.h" ++#include "test_global_func15.skel.h" ++#include "test_global_func16.skel.h" ++#include "test_global_func17.skel.h" + + void test_test_global_funcs(void) + { +- struct test_def tests[] = { +- { "test_global_func1.bpf.o", "combined stack size of 4 calls is 544" }, +- { "test_global_func2.bpf.o" }, +- { "test_global_func3.bpf.o", "the call stack of 8 frames" }, +- { "test_global_func4.bpf.o" }, +- { "test_global_func5.bpf.o", "expected pointer to ctx, but got PTR" }, +- { "test_global_func6.bpf.o", "modified ctx ptr R2" }, +- { "test_global_func7.bpf.o", "foo() doesn't return scalar" }, +- { "test_global_func8.bpf.o" }, +- { "test_global_func9.bpf.o" }, +- { "test_global_func10.bpf.o", "invalid indirect read from stack" }, +- { "test_global_func11.bpf.o", "Caller passes invalid args into func#1" }, +- { "test_global_func12.bpf.o", "invalid mem access 'mem_or_null'" }, +- { "test_global_func13.bpf.o", "Caller passes invalid args into func#1" }, +- { "test_global_func14.bpf.o", "reference type('FWD S') size cannot be determined" }, +- { "test_global_func15.bpf.o", "At program exit the register R0 has value" }, +- { "test_global_func16.bpf.o", "invalid indirect read from stack" }, +- { "test_global_func17.bpf.o", "Caller passes invalid args into func#1" }, +- }; +- libbpf_print_fn_t old_print_fn = NULL; +- int err, i, duration = 0; +- +- old_print_fn = libbpf_set_print(libbpf_debug_print); +- +- for (i = 0; i < ARRAY_SIZE(tests); i++) { +- const struct test_def *test = &tests[i]; +- +- if (!test__start_subtest(test->file)) +- continue; +- +- err_str = test->err_str; +- err = check_load(test->file); +- CHECK_FAIL(!!err ^ !!err_str); +- if (err_str) +- CHECK(found, "", "expected string '%s'", err_str); +- } +- libbpf_set_print(old_print_fn); ++ RUN_TESTS(test_global_func1); ++ RUN_TESTS(test_global_func2); ++ RUN_TESTS(test_global_func3); ++ RUN_TESTS(test_global_func4); ++ RUN_TESTS(test_global_func5); ++ RUN_TESTS(test_global_func6); ++ RUN_TESTS(test_global_func7); ++ RUN_TESTS(test_global_func8); ++ RUN_TESTS(test_global_func9); ++ RUN_TESTS(test_global_func10); ++ RUN_TESTS(test_global_func11); ++ RUN_TESTS(test_global_func12); ++ RUN_TESTS(test_global_func13); ++ RUN_TESTS(test_global_func14); ++ RUN_TESTS(test_global_func15); ++ RUN_TESTS(test_global_func16); ++ RUN_TESTS(test_global_func17); + } +diff --git a/tools/testing/selftests/bpf/progs/test_global_func1.c b/tools/testing/selftests/bpf/progs/test_global_func1.c +index 7b42dad187b894..23970a20b3249d 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func1.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func1.c +@@ -3,10 +3,9 @@ + #include + #include + #include ++#include "bpf_misc.h" + +-#ifndef MAX_STACK + #define MAX_STACK (512 - 3 * 32 + 8) +-#endif + + static __attribute__ ((noinline)) + int f0(int var, struct __sk_buff *skb) +@@ -39,7 +38,8 @@ int f3(int val, struct __sk_buff *skb, int var) + } + + SEC("tc") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("combined stack size of 4 calls is 544") ++int global_func1(struct __sk_buff *skb) + { + return f0(1, skb) + f1(skb) + f2(2, skb) + f3(3, skb, 4); + } +diff --git a/tools/testing/selftests/bpf/progs/test_global_func10.c b/tools/testing/selftests/bpf/progs/test_global_func10.c +index d361eba167f6a4..8fba3f3649e227 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func10.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func10.c +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + struct Small { + long x; +diff --git a/tools/testing/selftests/bpf/progs/test_global_func11.c b/tools/testing/selftests/bpf/progs/test_global_func11.c +index ef5277d982d921..283e036dc401e9 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func11.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func11.c +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + struct S { + int x; +@@ -13,7 +14,8 @@ __noinline int foo(const struct S *s) + } + + SEC("cgroup_skb/ingress") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("Caller passes invalid args into func#1") ++int global_func11(struct __sk_buff *skb) + { + return foo((const void *)skb); + } +diff --git a/tools/testing/selftests/bpf/progs/test_global_func12.c b/tools/testing/selftests/bpf/progs/test_global_func12.c +index 62343527cc598d..7f159d83c6f673 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func12.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func12.c +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + struct S { + int x; +@@ -13,7 +14,8 @@ __noinline int foo(const struct S *s) + } + + SEC("cgroup_skb/ingress") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("invalid mem access 'mem_or_null'") ++int global_func12(struct __sk_buff *skb) + { + const struct S s = {.x = skb->len }; + +diff --git a/tools/testing/selftests/bpf/progs/test_global_func13.c b/tools/testing/selftests/bpf/progs/test_global_func13.c +index ff8897c1ac22b4..02ea80da75b573 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func13.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func13.c +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + struct S { + int x; +@@ -16,7 +17,8 @@ __noinline int foo(const struct S *s) + } + + SEC("cgroup_skb/ingress") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("Caller passes invalid args into func#1") ++int global_func13(struct __sk_buff *skb) + { + const struct S *s = (const struct S *)(0xbedabeda); + +diff --git a/tools/testing/selftests/bpf/progs/test_global_func14.c b/tools/testing/selftests/bpf/progs/test_global_func14.c +index 698c77199ebf7d..33b7d5efd7b262 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func14.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func14.c +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + struct S; + +@@ -14,7 +15,8 @@ __noinline int foo(const struct S *s) + } + + SEC("cgroup_skb/ingress") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("reference type('FWD S') size cannot be determined") ++int global_func14(struct __sk_buff *skb) + { + + return foo(NULL); +diff --git a/tools/testing/selftests/bpf/progs/test_global_func15.c b/tools/testing/selftests/bpf/progs/test_global_func15.c +index c19c435988d557..b512d6a6c75e54 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func15.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func15.c +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + __noinline int foo(unsigned int *v) + { +@@ -12,7 +13,8 @@ __noinline int foo(unsigned int *v) + } + + SEC("cgroup_skb/ingress") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("At program exit the register R0 has value") ++int global_func15(struct __sk_buff *skb) + { + unsigned int v = 1; + +diff --git a/tools/testing/selftests/bpf/progs/test_global_func16.c b/tools/testing/selftests/bpf/progs/test_global_func16.c +index 0312d1e8d8c060..e7206304632e15 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func16.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func16.c +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + __noinline int foo(int (*arr)[10]) + { +@@ -12,7 +13,8 @@ __noinline int foo(int (*arr)[10]) + } + + SEC("cgroup_skb/ingress") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("invalid indirect read from stack") ++int global_func16(struct __sk_buff *skb) + { + int array[10]; + +diff --git a/tools/testing/selftests/bpf/progs/test_global_func17.c b/tools/testing/selftests/bpf/progs/test_global_func17.c +index 2b8b9b8ba0183c..a32e11c7d933ee 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func17.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func17.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + #include + #include ++#include "bpf_misc.h" + + __noinline int foo(int *p) + { +@@ -10,7 +11,8 @@ __noinline int foo(int *p) + const volatile int i; + + SEC("tc") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("Caller passes invalid args into func#1") ++int global_func17(struct __sk_buff *skb) + { + return foo((int *)&i); + } +diff --git a/tools/testing/selftests/bpf/progs/test_global_func2.c b/tools/testing/selftests/bpf/progs/test_global_func2.c +index 2c18d82923a2d5..3dce97fb52a4b7 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func2.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func2.c +@@ -1,4 +1,45 @@ + // SPDX-License-Identifier: GPL-2.0-only + /* Copyright (c) 2020 Facebook */ ++#include ++#include ++#include ++#include "bpf_misc.h" ++ + #define MAX_STACK (512 - 3 * 32) +-#include "test_global_func1.c" ++ ++static __attribute__ ((noinline)) ++int f0(int var, struct __sk_buff *skb) ++{ ++ return skb->len; ++} ++ ++__attribute__ ((noinline)) ++int f1(struct __sk_buff *skb) ++{ ++ volatile char buf[MAX_STACK] = {}; ++ ++ return f0(0, skb) + skb->len; ++} ++ ++int f3(int, struct __sk_buff *skb, int); ++ ++__attribute__ ((noinline)) ++int f2(int val, struct __sk_buff *skb) ++{ ++ return f1(skb) + f3(val, skb, 1); ++} ++ ++__attribute__ ((noinline)) ++int f3(int val, struct __sk_buff *skb, int var) ++{ ++ volatile char buf[MAX_STACK] = {}; ++ ++ return skb->ifindex * val * var; ++} ++ ++SEC("tc") ++__success ++int global_func2(struct __sk_buff *skb) ++{ ++ return f0(1, skb) + f1(skb) + f2(2, skb) + f3(3, skb, 4); ++} +diff --git a/tools/testing/selftests/bpf/progs/test_global_func3.c b/tools/testing/selftests/bpf/progs/test_global_func3.c +index 01bf8275dfd640..142b682d3c2f08 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func3.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func3.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + __attribute__ ((noinline)) + int f1(struct __sk_buff *skb) +@@ -46,20 +47,15 @@ int f7(struct __sk_buff *skb) + return f6(skb); + } + +-#ifndef NO_FN8 + __attribute__ ((noinline)) + int f8(struct __sk_buff *skb) + { + return f7(skb); + } +-#endif + + SEC("tc") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("the call stack of 8 frames") ++int global_func3(struct __sk_buff *skb) + { +-#ifndef NO_FN8 + return f8(skb); +-#else +- return f7(skb); +-#endif + } +diff --git a/tools/testing/selftests/bpf/progs/test_global_func4.c b/tools/testing/selftests/bpf/progs/test_global_func4.c +index 610f75edf27643..1733d87ad3f3ea 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func4.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func4.c +@@ -1,4 +1,55 @@ + // SPDX-License-Identifier: GPL-2.0-only + /* Copyright (c) 2020 Facebook */ +-#define NO_FN8 +-#include "test_global_func3.c" ++#include ++#include ++#include ++#include "bpf_misc.h" ++ ++__attribute__ ((noinline)) ++int f1(struct __sk_buff *skb) ++{ ++ return skb->len; ++} ++ ++__attribute__ ((noinline)) ++int f2(int val, struct __sk_buff *skb) ++{ ++ return f1(skb) + val; ++} ++ ++__attribute__ ((noinline)) ++int f3(int val, struct __sk_buff *skb, int var) ++{ ++ return f2(var, skb) + val; ++} ++ ++__attribute__ ((noinline)) ++int f4(struct __sk_buff *skb) ++{ ++ return f3(1, skb, 2); ++} ++ ++__attribute__ ((noinline)) ++int f5(struct __sk_buff *skb) ++{ ++ return f4(skb); ++} ++ ++__attribute__ ((noinline)) ++int f6(struct __sk_buff *skb) ++{ ++ return f5(skb); ++} ++ ++__attribute__ ((noinline)) ++int f7(struct __sk_buff *skb) ++{ ++ return f6(skb); ++} ++ ++SEC("tc") ++__success ++int global_func4(struct __sk_buff *skb) ++{ ++ return f7(skb); ++} +diff --git a/tools/testing/selftests/bpf/progs/test_global_func5.c b/tools/testing/selftests/bpf/progs/test_global_func5.c +index 9248d03e0d06fb..cc55aedaf82d57 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func5.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func5.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + __attribute__ ((noinline)) + int f1(struct __sk_buff *skb) +@@ -25,7 +26,8 @@ int f3(int val, struct __sk_buff *skb) + } + + SEC("tc") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("expected pointer to ctx, but got PTR") ++int global_func5(struct __sk_buff *skb) + { + return f1(skb) + f2(2, skb) + f3(3, skb); + } +diff --git a/tools/testing/selftests/bpf/progs/test_global_func6.c b/tools/testing/selftests/bpf/progs/test_global_func6.c +index af8c78bdfb2575..46c38c8f2cf032 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func6.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func6.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + __attribute__ ((noinline)) + int f1(struct __sk_buff *skb) +@@ -25,7 +26,8 @@ int f3(int val, struct __sk_buff *skb) + } + + SEC("tc") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("modified ctx ptr R2") ++int global_func6(struct __sk_buff *skb) + { + return f1(skb) + f2(2, skb) + f3(3, skb); + } +diff --git a/tools/testing/selftests/bpf/progs/test_global_func7.c b/tools/testing/selftests/bpf/progs/test_global_func7.c +index 6cb8e2f5254cf1..f182febfde3c08 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func7.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func7.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + __attribute__ ((noinline)) + void foo(struct __sk_buff *skb) +@@ -11,7 +12,8 @@ void foo(struct __sk_buff *skb) + } + + SEC("tc") +-int test_cls(struct __sk_buff *skb) ++__failure __msg("foo() doesn't return scalar") ++int global_func7(struct __sk_buff *skb) + { + foo(skb); + return 0; +diff --git a/tools/testing/selftests/bpf/progs/test_global_func8.c b/tools/testing/selftests/bpf/progs/test_global_func8.c +index d55a6544b1abd2..9b9c57fa2dd34b 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func8.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func8.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + __noinline int foo(struct __sk_buff *skb) + { +@@ -10,7 +11,8 @@ __noinline int foo(struct __sk_buff *skb) + } + + SEC("cgroup_skb/ingress") +-int test_cls(struct __sk_buff *skb) ++__success ++int global_func8(struct __sk_buff *skb) + { + if (!foo(skb)) + return 0; +diff --git a/tools/testing/selftests/bpf/progs/test_global_func9.c b/tools/testing/selftests/bpf/progs/test_global_func9.c +index bd233ddede98ae..1f2cb0159b8d83 100644 +--- a/tools/testing/selftests/bpf/progs/test_global_func9.c ++++ b/tools/testing/selftests/bpf/progs/test_global_func9.c +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include "bpf_misc.h" + + struct S { + int x; +@@ -74,7 +75,8 @@ __noinline int quuz(int **p) + } + + SEC("cgroup_skb/ingress") +-int test_cls(struct __sk_buff *skb) ++__success ++int global_func9(struct __sk_buff *skb) + { + int result = 0; + +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-enhance-align-selftest-s-expected-log-.patch b/queue-6.1/selftests-bpf-enhance-align-selftest-s-expected-log-.patch new file mode 100644 index 0000000000..939e1ecb85 --- /dev/null +++ b/queue-6.1/selftests-bpf-enhance-align-selftest-s-expected-log-.patch @@ -0,0 +1,66 @@ +From 600bb6b05ff2caef1797b6532ff200c786083f65 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:41:34 +0200 +Subject: selftests/bpf: enhance align selftest's expected log matching + +From: Andrii Nakryiko + +[ Upstream commit 6f876e75d316a75957f3d43c3a8c2a6fe9bc18b2 ] + +Allow to search for expected register state in all the verifier log +output that's related to specified instruction number. + +See added comment for an example of possible situation that is happening +due to a simple enhancement done in the next patch, which fixes handling +of env->test_state_freq flag in state checkpointing logic. + +Signed-off-by: Andrii Nakryiko +Link: https://lore.kernel.org/r/20230302235015.2044271-4-andrii@kernel.org +Signed-off-by: Alexei Starovoitov +[ Note: Backport needed to fix the align selftest where some of the + expected log messages can't be found. This is happening because + commit 1a8a315f008a ("bpf: Ensure proper register state printing for + cond jumps") was also backported to 6.1. ] +Signed-off-by: Paul Chaignon +Acked-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/prog_tests/align.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/align.c b/tools/testing/selftests/bpf/prog_tests/align.c +index 8baebb41541dc7..b9277059256300 100644 +--- a/tools/testing/selftests/bpf/prog_tests/align.c ++++ b/tools/testing/selftests/bpf/prog_tests/align.c +@@ -660,16 +660,22 @@ static int do_test_single(struct bpf_align_test *test) + * func#0 @0 + * 0: R1=ctx(off=0,imm=0) R10=fp0 + * 0: (b7) r3 = 2 ; R3_w=2 ++ * ++ * Sometimes it's actually two lines below, e.g. when ++ * searching for "6: R3_w=scalar(umax=255,var_off=(0x0; 0xff))": ++ * from 4 to 6: R0_w=pkt(off=8,r=8,imm=0) R1=ctx(off=0,imm=0) R2_w=pkt(off=0,r=8,imm=0) R3_w=pkt_end(off=0,imm=0) R10=fp0 ++ * 6: R0_w=pkt(off=8,r=8,imm=0) R1=ctx(off=0,imm=0) R2_w=pkt(off=0,r=8,imm=0) R3_w=pkt_end(off=0,imm=0) R10=fp0 ++ * 6: (71) r3 = *(u8 *)(r2 +0) ; R2_w=pkt(off=0,r=8,imm=0) R3_w=scalar(umax=255,var_off=(0x0; 0xff)) + */ +- if (!strstr(line_ptr, m.match)) { ++ while (!strstr(line_ptr, m.match)) { + cur_line = -1; + line_ptr = strtok(NULL, "\n"); +- sscanf(line_ptr, "%u: ", &cur_line); ++ sscanf(line_ptr ?: "", "%u: ", &cur_line); ++ if (!line_ptr || cur_line != m.line) ++ break; + } +- if (cur_line != m.line || !line_ptr || +- !strstr(line_ptr, m.match)) { +- printf("Failed to find match %u: %s\n", +- m.line, m.match); ++ if (cur_line != m.line || !line_ptr || !strstr(line_ptr, m.match)) { ++ printf("Failed to find match %u: %s\n", m.line, m.match); + ret = 1; + printf("%s", bpf_vlog); + break; +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-fix-arg_ptr_to_long-half-uninitialized.patch b/queue-6.1/selftests-bpf-fix-arg_ptr_to_long-half-uninitialized.patch new file mode 100644 index 0000000000..d704ea0a04 --- /dev/null +++ b/queue-6.1/selftests-bpf-fix-arg_ptr_to_long-half-uninitialized.patch @@ -0,0 +1,57 @@ +From 0d9262b00e2d1f3dc00e7e9252603450556be798 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:41:55 +0200 +Subject: selftests/bpf: Fix ARG_PTR_TO_LONG {half-,}uninitialized test + +From: Daniel Borkmann + +[ Upstream commit b8e188f023e07a733b47d5865311ade51878fe40 ] + +The assumption of 'in privileged mode reads from uninitialized stack locations +are permitted' is not quite correct since the verifier was probing for read +access rather than write access. Both tests need to be annotated as __success +for privileged and unprivileged. + +Signed-off-by: Daniel Borkmann +Acked-by: Andrii Nakryiko +Link: https://lore.kernel.org/r/20240913191754.13290-6-daniel@iogearbox.net +Signed-off-by: Alexei Starovoitov +[ Note: The format of logs completely changed since 6.1 so this change + had to be reapplied to the old test file. This commit needs to be + backported because it fixes a test broken by commit 32556ce93bc4 + ("bpf: Fix helper writes to read-only maps") from the same patchset. ] +Signed-off-by: Paul Chaignon +Acked-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/verifier/int_ptr.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/tools/testing/selftests/bpf/verifier/int_ptr.c b/tools/testing/selftests/bpf/verifier/int_ptr.c +index 02d9e004260b33..8c74cff2090364 100644 +--- a/tools/testing/selftests/bpf/verifier/int_ptr.c ++++ b/tools/testing/selftests/bpf/verifier/int_ptr.c +@@ -25,9 +25,8 @@ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, +- .result = REJECT, ++ .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_CGROUP_SYSCTL, +- .errstr = "invalid indirect read from stack R4 off -16+0 size 8", + }, + { + "ARG_PTR_TO_LONG half-uninitialized", +@@ -57,9 +56,6 @@ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, +- .result_unpriv = REJECT, +- .errstr_unpriv = "invalid indirect read from stack R4 off -16+4 size 8", +- /* in privileged mode reads from uninitialized stack locations are permitted */ + .result = ACCEPT, + }, + { +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-s-iptables-iptables-legacy-in-the-bpf_.patch b/queue-6.1/selftests-bpf-s-iptables-iptables-legacy-in-the-bpf_.patch new file mode 100644 index 0000000000..fb7169cbca --- /dev/null +++ b/queue-6.1/selftests-bpf-s-iptables-iptables-legacy-in-the-bpf_.patch @@ -0,0 +1,84 @@ +From 16be54df6956e1b0372b80ae057e7b3675dc72a3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:42:04 +0200 +Subject: selftests/bpf: S/iptables/iptables-legacy/ in the bpf_nf and + xdp_synproxy test + +From: Martin KaFai Lau + +[ Upstream commit de9c8d848d90cf2e53aced50b350827442ca5a4f ] + +The recent vm image in CI has reported error in selftests that use +the iptables command. Manu Bretelle has pointed out the difference +in the recent vm image that the iptables is sym-linked to the iptables-nft. +With this knowledge, I can also reproduce the CI error by manually running +with the 'iptables-nft'. + +This patch is to replace the iptables command with iptables-legacy +to unblock the CI tests. + +Signed-off-by: Martin KaFai Lau +Signed-off-by: Andrii Nakryiko +Acked-by: David Vernet +Link: https://lore.kernel.org/bpf/20221012221235.3529719-1-martin.lau@linux.dev +Signed-off-by: Paul Chaignon +Acked-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/prog_tests/bpf_nf.c | 6 +++--- + tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c | 6 +++--- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c +index b2998896f9f7bc..b30ff6b3b81ae3 100644 +--- a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c ++++ b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c +@@ -49,14 +49,14 @@ static int connect_to_server(int srv_fd) + + static void test_bpf_nf_ct(int mode) + { +- const char *iptables = "iptables -t raw %s PREROUTING -j CONNMARK --set-mark 42/0"; ++ const char *iptables = "iptables-legacy -t raw %s PREROUTING -j CONNMARK --set-mark 42/0"; + int srv_fd = -1, client_fd = -1, srv_client_fd = -1; + struct sockaddr_in peer_addr = {}; + struct test_bpf_nf *skel; + int prog_fd, err; + socklen_t len; + u16 srv_port; +- char cmd[64]; ++ char cmd[128]; + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), +@@ -69,7 +69,7 @@ static void test_bpf_nf_ct(int mode) + + /* Enable connection tracking */ + snprintf(cmd, sizeof(cmd), iptables, "-A"); +- if (!ASSERT_OK(system(cmd), "iptables")) ++ if (!ASSERT_OK(system(cmd), cmd)) + goto end; + + srv_port = (mode == TEST_XDP) ? 5005 : 5006; +diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c +index 879f5da2f21e63..13daa3746064af 100644 +--- a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c ++++ b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c +@@ -94,12 +94,12 @@ static void test_synproxy(bool xdp) + SYS("sysctl -w net.ipv4.tcp_syncookies=2"); + SYS("sysctl -w net.ipv4.tcp_timestamps=1"); + SYS("sysctl -w net.netfilter.nf_conntrack_tcp_loose=0"); +- SYS("iptables -t raw -I PREROUTING \ ++ SYS("iptables-legacy -t raw -I PREROUTING \ + -i tmp1 -p tcp -m tcp --syn --dport 8080 -j CT --notrack"); +- SYS("iptables -t filter -A INPUT \ ++ SYS("iptables-legacy -t filter -A INPUT \ + -i tmp1 -p tcp -m tcp --dport 8080 -m state --state INVALID,UNTRACKED \ + -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460"); +- SYS("iptables -t filter -A INPUT \ ++ SYS("iptables-legacy -t filter -A INPUT \ + -i tmp1 -m state --state INVALID -j DROP"); + + ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --ports 8080 \ +-- +2.53.0 + diff --git a/queue-6.1/selftests-bpf-update-bpf_clone_redirect-expected-ret.patch b/queue-6.1/selftests-bpf-update-bpf_clone_redirect-expected-ret.patch new file mode 100644 index 0000000000..c6810ff717 --- /dev/null +++ b/queue-6.1/selftests-bpf-update-bpf_clone_redirect-expected-ret.patch @@ -0,0 +1,90 @@ +From 8d982a71260603aa8c9bc5df1238aa3add555921 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 21:41:25 +0200 +Subject: selftests/bpf: Update bpf_clone_redirect expected return code + +From: Stanislav Fomichev + +[ Upstream commit b772b70b69046c5b76e3f2eda680f692dee5e6d5 ] + +Commit 151e887d8ff9 ("veth: Fixing transmit return status for dropped +packets") started propagating proper NET_XMIT_DROP error to the caller +which means it's now possible to get positive error code when calling +bpf_clone_redirect() in this particular test. Update the test to reflect +that. + +Reported-by: Daniel Borkmann +Signed-off-by: Stanislav Fomichev +Signed-off-by: Daniel Borkmann +Link: https://lore.kernel.org/bpf/20230911194731.286342-2-sdf@google.com +[ Note: Commit 151e887d8ff9 was backported to 6.1 so this fix should be + as well. ] +Signed-off-by: Paul Chaignon +Acked-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/bpf/prog_tests/empty_skb.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c +index 0613f3bb8b5e4e..329e34e5226e3a 100644 +--- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c ++++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c +@@ -29,6 +29,7 @@ void serial_test_empty_skb(void) + int *ifindex; + int err; + int ret; ++ int lwt_egress_ret; /* expected retval at lwt/egress */ + bool success_on_tc; + } tests[] = { + /* Empty packets are always rejected. */ +@@ -62,6 +63,7 @@ void serial_test_empty_skb(void) + .data_size_in = sizeof(eth_hlen), + .ifindex = &veth_ifindex, + .ret = -ERANGE, ++ .lwt_egress_ret = -ERANGE, + .success_on_tc = true, + }, + { +@@ -75,6 +77,7 @@ void serial_test_empty_skb(void) + .data_size_in = sizeof(eth_hlen), + .ifindex = &ipip_ifindex, + .ret = -ERANGE, ++ .lwt_egress_ret = -ERANGE, + }, + + /* ETH_HLEN+1-sized packet should be redirected. */ +@@ -84,6 +87,7 @@ void serial_test_empty_skb(void) + .data_in = eth_hlen_pp, + .data_size_in = sizeof(eth_hlen_pp), + .ifindex = &veth_ifindex, ++ .lwt_egress_ret = 1, /* veth_xmit NET_XMIT_DROP */ + }, + { + .msg = "ipip ETH_HLEN+1 packet ingress", +@@ -113,8 +117,12 @@ void serial_test_empty_skb(void) + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + bpf_object__for_each_program(prog, bpf_obj->obj) { +- char buf[128]; ++ bool at_egress = strstr(bpf_program__name(prog), "egress") != NULL; + bool at_tc = !strncmp(bpf_program__section_name(prog), "tc", 2); ++ int expected_ret; ++ char buf[128]; ++ ++ expected_ret = at_egress && !at_tc ? tests[i].lwt_egress_ret : tests[i].ret; + + tattr.data_in = tests[i].data_in; + tattr.data_size_in = tests[i].data_size_in; +@@ -133,7 +141,7 @@ void serial_test_empty_skb(void) + if (at_tc && tests[i].success_on_tc) + ASSERT_GE(bpf_obj->bss->ret, 0, buf); + else +- ASSERT_EQ(bpf_obj->bss->ret, tests[i].ret, buf); ++ ASSERT_EQ(bpf_obj->bss->ret, expected_ret, buf); + } + } + +-- +2.53.0 + diff --git a/queue-6.1/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch b/queue-6.1/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch new file mode 100644 index 0000000000..c0bd9a8d31 --- /dev/null +++ b/queue-6.1/selftests-forwarding-lib-add-helpers-for-checksum-ha.patch @@ -0,0 +1,108 @@ +From 5c9a34d2e3f45d48ca596c75bd1b37d8dbb77bd4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 13:33:34 +0200 +Subject: selftests: forwarding: lib: Add helpers for checksum handling + +From: Petr Machata + +commit 952e0ee38c7215c45192d8c899acd1830873f28b upstream. + +In order to generate IGMPv3 and MLDv2 packets on the fly, we will need +helpers to calculate the packet checksum. + +The approach presented in this patch revolves around payload templates +for mausezahn. These are mausezahn-like payload strings (01:23:45:...) +with possibly one 2-byte sequence replaced with the word PAYLOAD. The +main function is payload_template_calc_checksum(), which calculates +RFC 1071 checksum of the message. There are further helpers to then +convert the checksum to the payload format, and to expand it. + +For IPv6, MLDv2 message checksum is computed using a pseudoheader that +differs from the header used in the payload itself. The fact that the +two messages are different means that the checksum needs to be +returned as a separate quantity, instead of being expanded in-place in +the payload itself. Furthermore, the pseudoheader includes a length of +the message. Much like the checksum, this needs to be expanded in +mausezahn format. And likewise for number of addresses for (S,G) +entries. Thus we have several places where a computed quantity needs +to be presented in the payload format. Add a helper u16_to_bytes(), +which will be used in all these cases. + +Signed-off-by: Petr Machata +Acked-by: Nikolay Aleksandrov +Signed-off-by: David S. Miller +Stable-dep-of: 02cb2e6bacbb ("selftests: forwarding: vxlan_bridge_1d: fix test failure with br_netfilter enabled") +[bwh: Backported to 6,1: adjust context] +Signed-off-by: Ben Hutchings +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/net/forwarding/lib.sh | 56 +++++++++++++++++++ + 1 file changed, 56 insertions(+) + +diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh +index 06027772cf79af..48d913341af267 100755 +--- a/tools/testing/selftests/net/forwarding/lib.sh ++++ b/tools/testing/selftests/net/forwarding/lib.sh +@@ -1701,3 +1701,59 @@ hw_stats_monitor_test() + + log_test "${type}_stats notifications" + } ++ ++u16_to_bytes() ++{ ++ local u16=$1; shift ++ ++ printf "%04x" $u16 | sed 's/^/000/;s/^.*\(..\)\(..\)$/\1:\2/' ++} ++ ++# Given a mausezahn-formatted payload (colon-separated bytes given as %02x), ++# possibly with a keyword CHECKSUM stashed where a 16-bit checksum should be, ++# calculate checksum as per RFC 1071, assuming the CHECKSUM field (if any) ++# stands for 00:00. ++payload_template_calc_checksum() ++{ ++ local payload=$1; shift ++ ++ ( ++ # Set input radix. ++ echo "16i" ++ # Push zero for the initial checksum. ++ echo 0 ++ ++ # Pad the payload with a terminating 00: in case we get an odd ++ # number of bytes. ++ echo "${payload%:}:00:" | ++ sed 's/CHECKSUM/00:00/g' | ++ tr '[:lower:]' '[:upper:]' | ++ # Add the word to the checksum. ++ sed 's/\(..\):\(..\):/\1\2+\n/g' | ++ # Strip the extra odd byte we pushed if left unconverted. ++ sed 's/\(..\):$//' ++ ++ echo "10000 ~ +" # Calculate and add carry. ++ echo "FFFF r - p" # Bit-flip and print. ++ ) | ++ dc | ++ tr '[:upper:]' '[:lower:]' ++} ++ ++payload_template_expand_checksum() ++{ ++ local payload=$1; shift ++ local checksum=$1; shift ++ ++ local ckbytes=$(u16_to_bytes $checksum) ++ ++ echo "$payload" | sed "s/CHECKSUM/$ckbytes/g" ++} ++ ++payload_template_nbytes() ++{ ++ local payload=$1; shift ++ ++ payload_template_expand_checksum "${payload%:}" 0 | ++ sed 's/:/\n/g' | wc -l ++} +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index 460625468f..d8b5c07ddb 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -39,3 +39,37 @@ sctp-fix-race-between-sctp_wait_for_connect-and-peel.patch ipv6-fix-possible-infinite-loop-in-rt6_fill_node.patch ipv6-fix-possible-infinite-loop-in-fib6_select_path.patch net-skbuff-fix-pskb_carve-leaking-zcopy-pages.patch +batman-adv-v-stop-ogmv2-on-disabled-interface.patch +batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch +batman-adv-tt-reject-oversized-local-tvlv-buffers.patch +batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch +batman-adv-tvlv-reject-oversized-tvlv-packets.patch +batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch +selftests-forwarding-lib-add-helpers-for-checksum-ha.patch +batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch +batman-adv-tt-fix-toctou-race-for-reported-vlans.patch +batman-adv-tt-avoid-empty-vlan-responses.patch +batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch +mm-page_alloc-clear-page-private-in-free_pages_prepa.patch +selftests-bpf-add-generic-bpf-program-tester-loader.patch +selftests-bpf-convert-test_global_funcs-test-to-test.patch +revert-selftests-bpf-workaround-strict-bpf_lsm-retur.patch +revert-selftests-bpf-add-tests-for-_opts-variants-of.patch +selftests-bpf-add-read_build_id-function.patch +bpf-fix-a-few-selftest-failures-due-to-llvm18-change.patch +selftests-bpf-update-bpf_clone_redirect-expected-ret.patch +selftests-bpf-enhance-align-selftest-s-expected-log-.patch +revert-selftests-bpf-add-a-cgroup-prog-bpf_get_ns_cu.patch +selftests-bpf-fix-arg_ptr_to_long-half-uninitialized.patch +selftests-bpf-s-iptables-iptables-legacy-in-the-bpf_.patch +net-packet-convert-po-tp_tx_has_off-to-an-atomic-fla.patch +net-packet-convert-po-tp_loss-to-an-atomic-flag.patch +net-packet-convert-po-has_vnet_hdr-to-an-atomic-flag.patch +net-packet-convert-po-running-to-an-atomic-flag.patch +net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpa.patch +drm-i915-psr-add-defininitions-for-intel_wa_register.patch +drm-dp-add-edp-1.5-bit-definition.patch +drm-i915-psr-read-intel-dpcd-workaround-register.patch +drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch +revert-rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch +rdma-rxe-fix-double-free-in-rxe_srq_from_init.patch-25226 diff --git a/queue-6.12/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/queue-6.12/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch new file mode 100644 index 0000000000..579d94a20c --- /dev/null +++ b/queue-6.12/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch @@ -0,0 +1,232 @@ +From 963533f502c669c85cee2e202682c19d4be6663b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:10:42 +0200 +Subject: batman-adv: bla: avoid double decrement of bla.num_requests + +From: Sven Eckelmann + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++-------- + net/batman-adv/soft-interface.c | 1 + + net/batman-adv/types.h | 39 ++++++++++++++++---- + 3 files changed, 67 insertions(+), 24 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index 17db4191ddb39e..15aeb07285e61f 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -516,8 +516,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, + 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); +@@ -546,9 +546,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, + 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; +@@ -651,10 +655,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw) + 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); + } + + /** +@@ -875,10 +881,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, + /* 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); +@@ -1257,9 +1265,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int 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); +@@ -1510,7 +1522,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) + 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. + * +@@ -1519,14 +1531,21 @@ static void batadv_bla_periodic_work(struct work_struct *work) + * 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(); + } +diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c +index 2758aba47a2f25..f46064333f3343 100644 +--- a/net/batman-adv/soft-interface.c ++++ b/net/batman-adv/soft-interface.c +@@ -787,6 +787,7 @@ static int batadv_softif_init_late(struct net_device *dev) + 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); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 0ccd55b251fa26..f703d266780d74 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1091,6 +1091,12 @@ struct batadv_priv_bla { + /** @num_requests: number of bla requests in flight */ + 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 + */ +@@ -1825,6 +1831,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 + */ +@@ -1850,16 +1877,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; +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch b/queue-6.12/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch new file mode 100644 index 0000000000..b41f387876 --- /dev/null +++ b/queue-6.12/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch @@ -0,0 +1,53 @@ +From 9dfb7ab4aa826246f27d18f2d57a9c192d0110a4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:11:25 +0200 +Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface + +From: Sven Eckelmann + +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 +Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816 +[ switch to old "mesh_iface" name "soft_iface" ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index e77f3ef3d7336a..17db4191ddb39e 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -356,12 +356,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac, + sizeof(local_claim_dest)); + local_claim_dest.type = claimtype; + +- soft_iface = primary_if->soft_iface; ++ soft_iface = READ_ONCE(primary_if->soft_iface); ++ if (!soft_iface) ++ goto out; + + skb = arp_create(ARPOP_REPLY, ETH_P_ARP, + /* IP DST: 0.0.0.0 */ + zeroip, +- primary_if->soft_iface, ++ soft_iface, + /* IP SRC: 0.0.0.0 */ + zeroip, + /* Ethernet DST: Broadcast */ +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch b/queue-6.12/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch new file mode 100644 index 0000000000..deff785c8f --- /dev/null +++ b/queue-6.12/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch @@ -0,0 +1,230 @@ +From 1b885ca819cefa038080fb411f5d2a3f012c2d5b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:10:00 +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 + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++---------- + net/batman-adv/types.h | 3 ++ + 2 files changed, 60 insertions(+), 19 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index 42b687c1a76807..b37c9fb178ae50 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -223,6 +223,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface) + 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) +@@ -527,8 +529,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, + * @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, +@@ -552,13 +556,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + + 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; +@@ -580,6 +584,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + 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 */ +@@ -609,8 +615,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr, + * @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, +@@ -662,14 +670,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, + 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; + } + } + +@@ -781,6 +791,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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); +@@ -809,11 +821,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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; +@@ -835,8 +844,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + /* 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; + } + +@@ -851,15 +863,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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); + } + +@@ -874,6 +899,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) + 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 +@@ -2277,6 +2313,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1, + + 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); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index fe774ec8b80b75..0ccd55b251fa26 100644 +--- 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; + }; +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch b/queue-6.12/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch new file mode 100644 index 0000000000..caff854851 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch @@ -0,0 +1,196 @@ +From 58fa179914a593328458ad3eb28c4b2e65f7c786 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:09:05 +0200 +Subject: batman-adv: tp_meter: avoid role confusion in tp_list + +From: Sven Eckelmann + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 59 ++++++++++++++++++++++++--------------- + 1 file changed, 36 insertions(+), 23 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 04a83d6be45bc0..bc3dc377f0bfd0 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -255,6 +255,7 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason, + * batadv_tp_list_find() - find a tp_vars object in the global list + * @bat_priv: the bat priv with all the soft 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_notify(enum batadv_tp_meter_reason reason, + * 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_list_find(struct batadv_priv *bat_priv, + 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 +@@ -286,12 +291,33 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, + return tp_vars; + } + ++/** ++ * 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 soft 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_list_find(struct batadv_priv *bat_priv, + */ + 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 batadv_priv *bat_priv, const u8 *dst, + 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 +@@ -671,13 +700,10 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + + /* 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; + +@@ -986,10 +1012,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, + 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, +@@ -1110,18 +1134,14 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, + 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); +@@ -1377,7 +1397,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_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; + +@@ -1448,7 +1468,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + } 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", +@@ -1457,13 +1477,6 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + } + +- 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 +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch b/queue-6.12/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch new file mode 100644 index 0000000000..b6a3c8fa0e --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch @@ -0,0 +1,60 @@ +From a5dbd1e85a7af01c6569e23d6e66a8954288c2ec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 22:21:35 +0200 +Subject: batman-adv: tp_meter: directly shut down timer on cleanup + +From: Sven Eckelmann + +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") +[ adapt pre-hunk to old del_timer* names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index bc3dc377f0bfd0..dfc3374549921f 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -430,13 +430,7 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars) + batadv_tp_list_detach(tp_vars); + + /* kill the timer and remove its reference */ +- del_timer_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 +- * del_timer(), it would not re-arm itself once again because the status +- * is OFF now +- */ +- del_timer(&tp_vars->timer); ++ timer_shutdown_sync(&tp_vars->timer); + batadv_tp_vars_put(tp_vars); + } + +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tt-avoid-empty-vlan-responses.patch b/queue-6.12/batman-adv-tt-avoid-empty-vlan-responses.patch new file mode 100644 index 0000000000..96014bbad2 --- /dev/null +++ b/queue-6.12/batman-adv-tt-avoid-empty-vlan-responses.patch @@ -0,0 +1,91 @@ +From 5642f51788b0bd26b6a7728d5740a72c5fe5ce56 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:04:50 +0200 +Subject: batman-adv: tt: avoid empty VLAN responses + +From: Sven Eckelmann + +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") +[ Context, drop flex array dependency ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 21 ++++++++++++++++++--- + 1 file changed, 18 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index d4cebe122e528a..4045ddefc29b47 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -843,17 +843,19 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + 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 = sizeof(**tt_data); +@@ -861,7 +863,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + + /* 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; +@@ -882,14 +884,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ 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 = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_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; + +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch b/queue-6.12/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch new file mode 100644 index 0000000000..b978744548 --- /dev/null +++ b/queue-6.12/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch @@ -0,0 +1,78 @@ +From 2f1e6d3e5d741e8138179ae67735e3d39e217381 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 22:57:44 +0200 +Subject: batman-adv: tt: fix TOCTOU race for reported vlans + +From: Sven Eckelmann + +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") +[ Context, drop flex array dependency ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 8ffebece03c529..d4cebe122e528a 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -934,11 +934,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_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 = sizeof(**tt_data); +@@ -964,6 +961,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ num_vlan = 0; + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); + if (vlan_entries < 1) +@@ -974,8 +972,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + tt_vlan->reserved = 0; + + tt_vlan++; ++ num_vlan++; + } + ++ /* recalculate in case number of VLANs reduced */ ++ change_offset = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_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; + +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch b/queue-6.12/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch new file mode 100644 index 0000000000..4b45e8273f --- /dev/null +++ b/queue-6.12/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch @@ -0,0 +1,62 @@ +From 7ec95e77d8cf28983bb8e5e7cad19943f138f2b3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:03:00 +0200 +Subject: batman-adv: tt: reject oversized local TVLV buffers + +From: Sven Eckelmann + +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") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index d830ccf016697b..8ffebece03c529 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -924,12 +924,12 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + { + struct batadv_tvlv_tt_vlan_data *tt_vlan; + struct batadv_softif_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->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { +@@ -948,8 +948,10 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + 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) { +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch b/queue-6.12/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch new file mode 100644 index 0000000000..69e56f8c7a --- /dev/null +++ b/queue-6.12/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch @@ -0,0 +1,196 @@ +From 9b197968072387191d0b3f4d3cdb5396109fbaba Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:46:02 +0200 +Subject: batman-adv: tvlv: abort OGM send on tvlv append failure + +From: Sven Eckelmann + +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") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + 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(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index 748188d3b878b2..42b687c1a76807 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -781,6 +781,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + u32 seqno; + u16 tvlv_len = 0; + unsigned long send_time; ++ int ret; + + lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); + +@@ -804,9 +805,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + * 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); +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 310248a5812c49..8cfc3944dcfd52 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -271,9 +271,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + struct batadv_hard_iface *hard_iface; + struct batadv_ogm2_packet *ogm_packet; + struct sk_buff *skb, *skb_tmp; +- unsigned char *ogm_buff; +- int ogm_buff_len; +- u16 tvlv_len = 0; ++ unsigned char **ogm_buff; ++ int *ogm_buff_len; ++ u16 tvlv_len; + int ret; + + lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex); +@@ -281,25 +281,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + 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)); +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 2a583215d439bd..76c6e0599694c7 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -8,6 +8,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff, + * 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(struct batadv_priv *bat_priv, + 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(struct batadv_priv *bat_priv, + + 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(struct batadv_priv *bat_priv, + + end: + spin_unlock_bh(&bat_priv->tvlv.container_list_lock); +- return tvlv_value_len; ++ ++ return tvlv_len_ret; + } + + /** +diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h +index e5697230d99173..f96f6b3f44a001 100644 +--- 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, +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tvlv-reject-oversized-tvlv-packets.patch b/queue-6.12/batman-adv-tvlv-reject-oversized-tvlv-packets.patch new file mode 100644 index 0000000000..2e0e365aee --- /dev/null +++ b/queue-6.12/batman-adv-tvlv-reject-oversized-tvlv-packets.patch @@ -0,0 +1,85 @@ +From 41c0d1833ed99ea68e8473043f65fbba76f136bc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:08:04 +0200 +Subject: batman-adv: tvlv: reject oversized TVLV packets + +From: Sven Eckelmann + +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 +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 76c6e0599694c7..8d6b017c433cc9 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version) + * + * 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(struct batadv_priv *bat_priv, + { + 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); +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-v-stop-ogmv2-on-disabled-interface.patch b/queue-6.12/batman-adv-v-stop-ogmv2-on-disabled-interface.patch new file mode 100644 index 0000000000..7e901f2176 --- /dev/null +++ b/queue-6.12/batman-adv-v-stop-ogmv2-on-disabled-interface.patch @@ -0,0 +1,149 @@ +From a4866cc765d098305ad5e0af312c15a329862277 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:27:33 +0200 +Subject: batman-adv: v: stop OGMv2 on disabled interface + +From: Sven Eckelmann + +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 +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +[ switch to old "mesh_iface" name "soft_iface" ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------ + 1 file changed, 21 insertions(+), 12 deletions(-) + +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 8f89ffe6020ced..310248a5812c49 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv) + + /** + * 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->soft_iface); +- + if (hard_iface->if_status != BATADV_IF_ACTIVE) { + kfree_skb(skb); + return; +@@ -189,6 +189,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) + + /** + * 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 +@@ -198,7 +199,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) + * + * 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; +@@ -228,27 +230,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface) + 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->soft_iface); ++ if (hard_iface->soft_iface != bat_priv->soft_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); +@@ -347,7 +354,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + 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(); +@@ -387,12 +394,14 @@ void batadv_v_ogm_aggr_work(struct 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->soft_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); +@@ -582,7 +591,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv, + 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); +-- +2.53.0 + diff --git a/queue-6.12/drm-dp-add-edp-1.5-bit-definition.patch b/queue-6.12/drm-dp-add-edp-1.5-bit-definition.patch new file mode 100644 index 0000000000..c55af3c9f2 --- /dev/null +++ b/queue-6.12/drm-dp-add-edp-1.5-bit-definition.patch @@ -0,0 +1,41 @@ +From 1be1a226934affa1bea20e0a351aae1e7c164050 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 13:42:05 +0300 +Subject: drm/dp: Add eDP 1.5 bit definition +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Suraj Kandpal + +commit 5dfc37a6b77bf6beedbd30d70184b54e1a08ccac upstream. + +Add the eDP revision bit value for 1.5. + +Spec: eDPv1.5 Table 16-5 +Signed-off-by: Suraj Kandpal +Reviewed-by: Arun R Murthy +Tested-by: Ben Kao +Acked-by: Maarten Lankhorst +Link: https://patchwork.freedesktop.org/patch/msgid/20250206063253.2827017-2-suraj.kandpal@intel.com +Signed-off-by: Jouni Högander +Signed-off-by: Sasha Levin +--- + include/drm/display/drm_dp.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h +index 3bd9f482f0c3e6..dd218400a613e3 100644 +--- a/include/drm/display/drm_dp.h ++++ b/include/drm/display/drm_dp.h +@@ -997,6 +997,7 @@ + # define DP_EDP_14 0x03 + # define DP_EDP_14a 0x04 /* eDP 1.4a */ + # define DP_EDP_14b 0x05 /* eDP 1.4b */ ++# define DP_EDP_15 0x06 /* eDP 1.5 */ + + #define DP_EDP_GENERAL_CAP_1 0x701 + # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP (1 << 0) +-- +2.53.0 + diff --git a/queue-6.12/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-6.12/drm-i915-psr-add-defininitions-for-intel_wa_register.patch new file mode 100644 index 0000000000..e65931e7f5 --- /dev/null +++ b/queue-6.12/drm-i915-psr-add-defininitions-for-intel_wa_register.patch @@ -0,0 +1,73 @@ +From 570a6a6f6b17c1ab5a96e802e3d8e6a069cc833a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 13:42:03 +0300 +Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD + register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream. + +EDP specification says: + +"If either VSC SDP is unable to be transmitted 100 ns before the SU region, +the Source device may optionally transmit the VSC SDP during the prior +video scan line’s HBlank period There is a Intel specific drm dp register +currently containing bits related how TCON can support PSR2 with SDP on +prior line." + +Unfortunately many panels are having problems in implementing this. So +there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to +figure out if this is properly implemented on a panel or if panel doesn't +require that 100 ns delay before the SU region. Here are the definitions in +this custom DPCD address: + +0 = Panel doesn't support SDP on prior line +1 = Panel supports SDP on prior line +2 = Panel doesn't have 100ns requirement +3 = Reserved + +Add definitions for this new register and it's values into new header +intel_dpcd.h. + +v2: add INTEL_DPCD_ prefix to definitions + +Bspec: 74741 +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com +(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h + +diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h +new file mode 100644 +index 00000000000000..4aea5326f2ed48 +--- /dev/null ++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: MIT */ ++/* ++ * Copyright © 2026 Intel Corporation ++ */ ++ ++#ifndef __INTEL_DPCD_H__ ++#define __INTEL_DPCD_H__ ++ ++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS 0x3f0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK REG_GENMASK(1, 0) ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1 0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE 1 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE 2 ++ ++#endif /* __INTEL_DPCD_H__ */ +-- +2.53.0 + diff --git a/queue-6.12/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-6.12/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch new file mode 100644 index 0000000000..5fb87472ff --- /dev/null +++ b/queue-6.12/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch @@ -0,0 +1,77 @@ +From 13a8e2b69c4b6c16032c020b9e4f5a8304622a25 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 13:42:06 +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 + +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: # v5.15+ +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com +(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Jouni Högander +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_psr.c | 27 +++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index 9c38b957701675..5173f5759ce888 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -1303,6 +1303,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp, + return true; + } + ++static bool apply_scanline_indication_wa(struct intel_dp *intel_dp, ++ struct intel_crtc_state *crtc_state) ++{ ++ u8 early_scanline_support = intel_dp->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_dp *intel_dp, + struct intel_crtc_state *crtc_state) + { +@@ -1324,7 +1348,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d + return false; + + crtc_state->req_psr2_sdp_prior_scanline = true; +- return true; ++ ++ return apply_scanline_indication_wa(intel_dp, crtc_state); + } + + static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp, +-- +2.53.0 + diff --git a/queue-6.12/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-6.12/drm-i915-psr-read-intel-dpcd-workaround-register.patch new file mode 100644 index 0000000000..3434a31b3f --- /dev/null +++ b/queue-6.12/drm-i915-psr-read-intel-dpcd-workaround-register.patch @@ -0,0 +1,67 @@ +From 7e7c761578ce407e19bb206dced0bac66da9a83e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 13:42:04 +0300 +Subject: drm/i915/psr: Read Intel DPCD workaround register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream. + +Read Intel DPCD workaround register and store it into +intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains +only PSR workaround for PSR2 SDP on prior scanline implementation. + +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com +(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_display_types.h | 1 + + drivers/gpu/drm/i915/display/intel_psr.c | 7 +++++++ + 2 files changed, 8 insertions(+) + +diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h +index 2039c17a9ee787..992945b37190e6 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_types.h ++++ b/drivers/gpu/drm/i915/display/intel_display_types.h +@@ -1773,6 +1773,7 @@ struct intel_dp { + u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE]; + u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE]; + u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]; ++ u8 intel_wa_dpcd; + /* source rates */ + int num_source_rates; + const int *source_rates; +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index 2a7f379c59fefd..9c38b957701675 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -35,6 +35,7 @@ + #include "intel_de.h" + #include "intel_display_types.h" + #include "intel_dp.h" ++#include "intel_dpcd.h" + #include "intel_dp_aux.h" + #include "intel_frontbuffer.h" + #include "intel_hdmi.h" +@@ -665,6 +666,12 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp) + drm_dbg_kms(display->drm, "PSR2 %ssupported\n", + intel_dp->psr.sink_psr2_support ? "" : "not "); + } ++ ++ if (intel_dp->psr.sink_psr2_support) ++ drm_dp_dpcd_read(&intel_dp->aux, ++ INTEL_DPCD_INTEL_WA_REGISTER_CAPS, ++ &intel_dp->intel_wa_dpcd, ++ sizeof(intel_dp->intel_wa_dpcd)); + } + + void intel_psr_init_dpcd(struct intel_dp *intel_dp) +-- +2.53.0 + diff --git a/queue-6.12/hid-core-add-printk_ratelimited-variants-to-hid_warn.patch b/queue-6.12/hid-core-add-printk_ratelimited-variants-to-hid_warn.patch new file mode 100644 index 0000000000..c29116084e --- /dev/null +++ b/queue-6.12/hid-core-add-printk_ratelimited-variants-to-hid_warn.patch @@ -0,0 +1,42 @@ +From f7fde9d942389143ae0aa178819bd77ece7adbf4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 1 Jun 2026 09:36:09 +0100 +Subject: HID: core: Add printk_ratelimited variants to hid_warn() etc + +From: Vicki Pfau + +[ Upstream commit 1d64624243af8329b4b219d8c39e28ea448f9929 ] + +hid_warn_ratelimited() is needed. Add the others as part of the block. + +Signed-off-by: Vicki Pfau +Signed-off-by: Jiri Kosina +Signed-off-by: Lee Jones +Signed-off-by: Sasha Levin +--- + include/linux/hid.h | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/include/linux/hid.h b/include/linux/hid.h +index 7d8d09318fa91d..bef017d6b44042 100644 +--- a/include/linux/hid.h ++++ b/include/linux/hid.h +@@ -1245,4 +1245,15 @@ void hid_quirks_exit(__u16 bus); + #define hid_dbg_once(hid, fmt, ...) \ + dev_dbg_once(&(hid)->dev, fmt, ##__VA_ARGS__) + ++#define hid_err_ratelimited(hid, fmt, ...) \ ++ dev_err_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) ++#define hid_notice_ratelimited(hid, fmt, ...) \ ++ dev_notice_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) ++#define hid_warn_ratelimited(hid, fmt, ...) \ ++ dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) ++#define hid_info_ratelimited(hid, fmt, ...) \ ++ dev_info_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) ++#define hid_dbg_ratelimited(hid, fmt, ...) \ ++ dev_dbg_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) ++ + #endif +-- +2.53.0 + diff --git a/queue-6.12/hid-core-fix-size_t-specifier-in-hid_report_raw_even.patch b/queue-6.12/hid-core-fix-size_t-specifier-in-hid_report_raw_even.patch new file mode 100644 index 0000000000..52305b55c4 --- /dev/null +++ b/queue-6.12/hid-core-fix-size_t-specifier-in-hid_report_raw_even.patch @@ -0,0 +1,67 @@ +From 61a38d8797ea96decb246fc6fbd728f7f1d09310 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 1 Jun 2026 09:36:12 +0100 +Subject: HID: core: Fix size_t specifier in hid_report_raw_event() + +From: Nathan Chancellor + +[ Upstream commit 4d3a2a466b8d68d852a1f3bbf11204b718428dc4 ] + +When building for 32-bit platforms, for which 'size_t' is +'unsigned int', there are warnings around using the incorrect format +specifier to print bsize in hid_report_raw_event(): + + drivers/hid/hid-core.c:2054:29: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat] + 2053 | hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n", + | ~~~ + | %zu + 2054 | report->id, csize, bsize); + | ^~~~~ + drivers/hid/hid-core.c:2076:29: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat] + 2075 | hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n", + | ~~~ + | %zu + 2076 | report->id, rsize, bsize); + | ^~~~~ + +Use the proper 'size_t' format specifier, '%zu', to clear up the +warnings. + +Cc: stable@vger.kernel.org +Fixes: 2c85c61d1332 ("HID: pass the buffer size to hid_report_raw_event") +Reported-by: Miguel Ojeda +Closes: https://lore.kernel.org/20260516020430.110135-1-ojeda@kernel.org/ +Signed-off-by: Nathan Chancellor +Signed-off-by: Linus Torvalds +(cherry picked from commit 3ab135238832446399614e7a4bb796d620717806) +Signed-off-by: Lee Jones +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-core.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index d9ea99cdb68e06..87d990ada8688a 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -2015,7 +2015,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * + return 0; + + if (unlikely(bsize < csize)) { +- hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n", ++ hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %zu)\n", + report->id, csize, bsize); + return -EINVAL; + } +@@ -2037,7 +2037,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * + rsize = max_buffer_size; + + if (bsize < rsize) { +- hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n", ++ hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %zu)\n", + report->id, rsize, bsize); + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.12/hid-core-introduce-hid_safe_input_report.patch b/queue-6.12/hid-core-introduce-hid_safe_input_report.patch new file mode 100644 index 0000000000..513947ad54 --- /dev/null +++ b/queue-6.12/hid-core-introduce-hid_safe_input_report.patch @@ -0,0 +1,139 @@ +From 993f336256ff20948bbc58104e32524d9d813fa4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 1 Jun 2026 09:36:11 +0100 +Subject: HID: core: introduce hid_safe_input_report() + +From: Benjamin Tissoires + +[ Upstream commit 206342541fc887ae919774a43942dc883161fece ] + +hid_input_report() is used in too many places to have a commit that +doesn't cross subsystem borders. Instead of changing the API, introduce +a new one when things matters in the transport layers: +- usbhid +- i2chid + +This effectively revert to the old behavior for those two transport +layers. + +Fixes: 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()") +Cc: stable@vger.kernel.org +Signed-off-by: Benjamin Tissoires +Signed-off-by: Jiri Kosina +(cherry picked from commit 301338b8edadc67a42b1c86add975091e66768d9) +Signed-off-by: Lee Jones +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-core.c | 25 +++++++++++++++++++++++++ + drivers/hid/i2c-hid/i2c-hid-core.c | 7 ++++--- + drivers/hid/usbhid/hid-core.c | 11 ++++++----- + include/linux/hid.h | 2 ++ + 4 files changed, 37 insertions(+), 8 deletions(-) + +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index ceff91722c3c83..d9ea99cdb68e06 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -2146,6 +2146,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, + * @interrupt: distinguish between interrupt and control transfers + * + * This is data entry for lower layers. ++ * Legacy, please use hid_safe_input_report() instead. + */ + int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt) +@@ -2156,6 +2157,30 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data + } + EXPORT_SYMBOL_GPL(hid_input_report); + ++/** ++ * hid_safe_input_report - report data from lower layer (usb, bt...) ++ * ++ * @hid: hid device ++ * @type: HID report type (HID_*_REPORT) ++ * @data: report contents ++ * @bufsize: allocated size of the data buffer ++ * @size: useful size of data parameter ++ * @interrupt: distinguish between interrupt and control transfers ++ * ++ * This is data entry for lower layers. ++ * Please use this function instead of the non safe version because we provide ++ * here the size of the buffer, allowing hid-core to make smarter decisions ++ * regarding the incoming buffer. ++ */ ++int hid_safe_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, ++ size_t bufsize, u32 size, int interrupt) ++{ ++ return __hid_input_report(hid, type, data, bufsize, size, interrupt, 0, ++ false, /* from_bpf */ ++ false /* lock_already_taken */); ++} ++EXPORT_SYMBOL_GPL(hid_safe_input_report); ++ + bool hid_match_one_id(const struct hid_device *hdev, + const struct hid_device_id *id) + { +diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c +index cf8ae0df0cda95..8ce0535fc42d60 100644 +--- a/drivers/hid/i2c-hid/i2c-hid-core.c ++++ b/drivers/hid/i2c-hid/i2c-hid-core.c +@@ -568,9 +568,10 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) + if (ihid->hid->group != HID_GROUP_RMI) + pm_wakeup_event(&ihid->client->dev, 0); + +- hid_input_report(ihid->hid, HID_INPUT_REPORT, +- ihid->inbuf + sizeof(__le16), +- ret_size - sizeof(__le16), 1); ++ hid_safe_input_report(ihid->hid, HID_INPUT_REPORT, ++ ihid->inbuf + sizeof(__le16), ++ ihid->bufsize - sizeof(__le16), ++ ret_size - sizeof(__le16), 1); + } + + return; +diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c +index f14b46ce00cb67..336ad7cf3d484e 100644 +--- a/drivers/hid/usbhid/hid-core.c ++++ b/drivers/hid/usbhid/hid-core.c +@@ -283,9 +283,9 @@ static void hid_irq_in(struct urb *urb) + break; + usbhid_mark_busy(usbhid); + if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) { +- hid_input_report(urb->context, HID_INPUT_REPORT, +- urb->transfer_buffer, +- urb->actual_length, 1); ++ hid_safe_input_report(urb->context, HID_INPUT_REPORT, ++ urb->transfer_buffer, urb->transfer_buffer_length, ++ urb->actual_length, 1); + /* + * autosuspend refused while keys are pressed + * because most keyboards don't wake up when +@@ -482,9 +482,10 @@ static void hid_ctrl(struct urb *urb) + switch (status) { + case 0: /* success */ + if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) +- hid_input_report(urb->context, ++ hid_safe_input_report(urb->context, + usbhid->ctrl[usbhid->ctrltail].report->type, +- urb->transfer_buffer, urb->actual_length, 0); ++ urb->transfer_buffer, urb->transfer_buffer_length, ++ urb->actual_length, 0); + break; + case -ESHUTDOWN: /* unplug */ + unplug = 1; +diff --git a/include/linux/hid.h b/include/linux/hid.h +index fdd401e4ebde3d..7d05b1edacd80c 100644 +--- a/include/linux/hid.h ++++ b/include/linux/hid.h +@@ -949,6 +949,8 @@ struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_ty + int hid_set_field(struct hid_field *, unsigned, __s32); + int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt); ++int hid_safe_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, ++ size_t bufsize, u32 size, int interrupt); + struct hid_field *hidinput_get_led_field(struct hid_device *hid); + unsigned int hidinput_count_leds(struct hid_device *hid); + __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); +-- +2.53.0 + diff --git a/queue-6.12/hid-pass-the-buffer-size-to-hid_report_raw_event.patch b/queue-6.12/hid-pass-the-buffer-size-to-hid_report_raw_event.patch new file mode 100644 index 0000000000..a597728cf5 --- /dev/null +++ b/queue-6.12/hid-pass-the-buffer-size-to-hid_report_raw_event.patch @@ -0,0 +1,362 @@ +From 159621bf6568570c7d59925c9e23a54ecc46fe3b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 1 Jun 2026 09:36:10 +0100 +Subject: HID: pass the buffer size to hid_report_raw_event + +From: Benjamin Tissoires + +[ Upstream commit 2c85c61d1332e1e16f020d76951baf167dcb6f7a ] + +commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing +bogus memset()") enforced the provided data to be at least the size of +the declared buffer in the report descriptor to prevent a buffer +overflow. However, we can try to be smarter by providing both the buffer +size and the data size, meaning that hid_report_raw_event() can make +better decision whether we should plaining reject the buffer (buffer +overflow attempt) or if we can safely memset it to 0 and pass it to the +rest of the stack. + +Fixes: 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()") +Cc: stable@vger.kernel.org +Signed-off-by: Benjamin Tissoires +Acked-by: Johan Hovold +Reviewed-by: Greg Kroah-Hartman +Signed-off-by: Jiri Kosina +Stable-dep-of: 206342541fc8 ("HID: core: introduce hid_safe_input_report()") +(cherry picked from commit 509c2605065004fc4cd86ee50a9350d402785307) +[Lee: Backported to linux-6.12.y and beyond] +Signed-off-by: Lee Jones +Signed-off-by: Sasha Levin +--- + drivers/hid/bpf/hid_bpf_dispatch.c | 6 +++-- + drivers/hid/hid-core.c | 37 +++++++++++++++++++++--------- + drivers/hid/hid-gfrm.c | 4 ++-- + drivers/hid/hid-logitech-hidpp.c | 2 +- + drivers/hid/hid-multitouch.c | 2 +- + drivers/hid/hid-primax.c | 2 +- + drivers/hid/hid-vivaldi-common.c | 2 +- + drivers/hid/wacom_sys.c | 6 ++--- + drivers/staging/greybus/hid.c | 2 +- + include/linux/hid.h | 4 ++-- + include/linux/hid_bpf.h | 14 +++++++---- + 11 files changed, 51 insertions(+), 30 deletions(-) + +diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c +index 284861c166d9c4..b711d83dfde1d4 100644 +--- a/drivers/hid/bpf/hid_bpf_dispatch.c ++++ b/drivers/hid/bpf/hid_bpf_dispatch.c +@@ -24,7 +24,8 @@ EXPORT_SYMBOL(hid_ops); + + u8 * + dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data, +- u32 *size, int interrupt, u64 source, bool from_bpf) ++ size_t *buf_size, u32 *size, int interrupt, u64 source, ++ bool from_bpf) + { + struct hid_bpf_ctx_kern ctx_kern = { + .ctx = { +@@ -74,6 +75,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type + *size = ret; + } + ++ *buf_size = ctx_kern.ctx.allocated_size; + return ctx_kern.data; + } + EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event); +@@ -514,7 +516,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *b + if (ret) + return ret; + +- return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (u64)(long)ctx, true, ++ return hid_ops->hid_input_report(ctx->hid, type, buf, size, size, 0, (u64)(long)ctx, true, + lock_already_taken); + } + +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index 294a25330ed030..ceff91722c3c83 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -1998,24 +1998,32 @@ int __hid_request(struct hid_device *hid, struct hid_report *report, + } + EXPORT_SYMBOL_GPL(__hid_request); + +-int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, +- int interrupt) ++int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, ++ size_t bufsize, u32 size, int interrupt) + { + struct hid_report_enum *report_enum = hid->report_enum + type; + struct hid_report *report; + struct hid_driver *hdrv; + int max_buffer_size = HID_MAX_BUFFER_SIZE; + u32 rsize, csize = size; ++ size_t bsize = bufsize; + u8 *cdata = data; + int ret = 0; + + report = hid_get_report(report_enum, data); + if (!report) +- goto out; ++ return 0; ++ ++ if (unlikely(bsize < csize)) { ++ hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n", ++ report->id, csize, bsize); ++ return -EINVAL; ++ } + + if (report_enum->numbered) { + cdata++; + csize--; ++ bsize--; + } + + rsize = hid_compute_report_size(report); +@@ -2028,9 +2036,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * + else if (rsize > max_buffer_size) + rsize = max_buffer_size; + ++ if (bsize < rsize) { ++ hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n", ++ report->id, rsize, bsize); ++ return -EINVAL; ++ } ++ + if (csize < rsize) { + dbg_hid("report %d is too short, (%d < %d)\n", report->id, +- csize, rsize); ++ csize, rsize); + memset(cdata + csize, 0, rsize - csize); + } + +@@ -2039,7 +2053,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * + if (hid->claimed & HID_CLAIMED_HIDRAW) { + ret = hidraw_report_event(hid, data, size); + if (ret) +- goto out; ++ return ret; + } + + if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) { +@@ -2051,15 +2065,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * + + if (hid->claimed & HID_CLAIMED_INPUT) + hidinput_report_event(hid, report); +-out: ++ + return ret; + } + EXPORT_SYMBOL_GPL(hid_report_raw_event); + + + static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, +- u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, +- bool lock_already_taken) ++ u8 *data, size_t bufsize, u32 size, int interrupt, u64 source, ++ bool from_bpf, bool lock_already_taken) + { + struct hid_report_enum *report_enum; + struct hid_driver *hdrv; +@@ -2084,7 +2098,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, + report_enum = hid->report_enum + type; + hdrv = hid->driver; + +- data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf); ++ data = dispatch_hid_bpf_device_event(hid, type, data, &bufsize, &size, interrupt, ++ source, from_bpf); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + goto unlock; +@@ -2113,7 +2128,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, + goto unlock; + } + +- ret = hid_report_raw_event(hid, type, data, size, interrupt); ++ ret = hid_report_raw_event(hid, type, data, bufsize, size, interrupt); + + unlock: + if (!lock_already_taken) +@@ -2135,7 +2150,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, + int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt) + { +- return __hid_input_report(hid, type, data, size, interrupt, 0, ++ return __hid_input_report(hid, type, data, size, size, interrupt, 0, + false, /* from_bpf */ + false /* lock_already_taken */); + } +diff --git a/drivers/hid/hid-gfrm.c b/drivers/hid/hid-gfrm.c +index 699186ff2349e9..d2a56bf92b416e 100644 +--- a/drivers/hid/hid-gfrm.c ++++ b/drivers/hid/hid-gfrm.c +@@ -66,7 +66,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report, + switch (data[1]) { + case GFRM100_SEARCH_KEY_DOWN: + ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn, +- sizeof(search_key_dn), 1); ++ sizeof(search_key_dn), sizeof(search_key_dn), 1); + break; + + case GFRM100_SEARCH_KEY_AUDIO_DATA: +@@ -74,7 +74,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report, + + case GFRM100_SEARCH_KEY_UP: + ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_up, +- sizeof(search_key_up), 1); ++ sizeof(search_key_up), sizeof(search_key_up), 1); + break; + + default: +diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c +index d60cd4379e866a..858ac2ab46bd97 100644 +--- a/drivers/hid/hid-logitech-hidpp.c ++++ b/drivers/hid/hid-logitech-hidpp.c +@@ -3691,7 +3691,7 @@ static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp, + memcpy(&consumer_report[1], &data[3], 4); + /* We are called from atomic context */ + hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT, +- consumer_report, 5, 1); ++ consumer_report, sizeof(consumer_report), 5, 1); + + return 1; + } +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index fcf9a806f109a5..760f9db44c9e32 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -500,7 +500,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) + } + + ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf, +- size, 0); ++ size, size, 0); + if (ret) + dev_warn(&hdev->dev, "failed to report feature\n"); + } +diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c +index e44d79dff8de63..8db054280afbcd 100644 +--- a/drivers/hid/hid-primax.c ++++ b/drivers/hid/hid-primax.c +@@ -44,7 +44,7 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report, + data[0] |= (1 << (data[idx] - 0xE0)); + data[idx] = 0; + } +- hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, 0); ++ hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, size, 0); + return 1; + + default: /* unknown report */ +diff --git a/drivers/hid/hid-vivaldi-common.c b/drivers/hid/hid-vivaldi-common.c +index bf734055d4b69d..b12bb5cc091aa3 100644 +--- a/drivers/hid/hid-vivaldi-common.c ++++ b/drivers/hid/hid-vivaldi-common.c +@@ -85,7 +85,7 @@ void vivaldi_feature_mapping(struct hid_device *hdev, + } + + ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data, +- report_len, 0); ++ report_len, report_len, 0); + if (ret) { + dev_warn(&hdev->dev, "failed to report feature %d\n", + field->report->id); +diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c +index 1b1112772777ca..ffcf65dcf71347 100644 +--- a/drivers/hid/wacom_sys.c ++++ b/drivers/hid/wacom_sys.c +@@ -74,7 +74,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev, + int err; + + size = kfifo_out(fifo, buf, sizeof(buf)); +- err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, false); ++ err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, size, false); + if (err) { + hid_warn(hdev, "%s: unable to flush event due to error %d\n", + __func__, err); +@@ -319,7 +319,7 @@ static void wacom_feature_mapping(struct hid_device *hdev, + data, n, WAC_CMD_RETRIES); + if (ret == n && features->type == HID_GENERIC) { + ret = hid_report_raw_event(hdev, +- HID_FEATURE_REPORT, data, n, 0); ++ HID_FEATURE_REPORT, data, n, n, 0); + } else if (ret == 2 && features->type != HID_GENERIC) { + features->touch_max = data[1]; + } else { +@@ -380,7 +380,7 @@ static void wacom_feature_mapping(struct hid_device *hdev, + data, n, WAC_CMD_RETRIES); + if (ret == n) { + ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, +- data, n, 0); ++ data, n, n, 0); + } else { + hid_warn(hdev, "%s: could not retrieve sensor offsets\n", + __func__); +diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c +index 63c77a3df59111..afa78c96ede898 100644 +--- a/drivers/staging/greybus/hid.c ++++ b/drivers/staging/greybus/hid.c +@@ -201,7 +201,7 @@ static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report) + * we just need to setup the input fields, so using + * hid_report_raw_event is safe. + */ +- hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, size, 1); ++ hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, ghid->bufsize, size, 1); + } + + static void gb_hid_init_reports(struct gb_hid *ghid) +diff --git a/include/linux/hid.h b/include/linux/hid.h +index bef017d6b44042..fdd401e4ebde3d 100644 +--- a/include/linux/hid.h ++++ b/include/linux/hid.h +@@ -1213,8 +1213,8 @@ static inline u32 hid_report_len(struct hid_report *report) + return DIV_ROUND_UP(report->size, 8) + (report->id > 0); + } + +-int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, +- int interrupt); ++int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, ++ size_t bufsize, u32 size, int interrupt); + + /* HID quirks API */ + unsigned long hid_lookup_quirk(const struct hid_device *hdev); +diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h +index 6a47223e646006..aa87513acbcd24 100644 +--- a/include/linux/hid_bpf.h ++++ b/include/linux/hid_bpf.h +@@ -72,8 +72,8 @@ struct hid_ops { + int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len, + u64 source, bool from_bpf); + int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type, +- u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, +- bool lock_already_taken); ++ u8 *data, size_t bufsize, u32 size, int interrupt, u64 source, ++ bool from_bpf, bool lock_already_taken); + struct module *owner; + const struct bus_type *bus_type; + }; +@@ -200,7 +200,8 @@ struct hid_bpf { + + #ifdef CONFIG_HID_BPF + u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, +- u32 *size, int interrupt, u64 source, bool from_bpf); ++ size_t *buf_size, u32 *size, int interrupt, u64 source, ++ bool from_bpf); + int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, + u32 size, enum hid_report_type rtype, +@@ -215,8 +216,11 @@ int hid_bpf_device_init(struct hid_device *hid); + u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size); + #else /* CONFIG_HID_BPF */ + static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, +- u8 *data, u32 *size, int interrupt, +- u64 source, bool from_bpf) { return data; } ++ u8 *data, size_t *buf_size, u32 *size, ++ int interrupt, u64 source, bool from_bpf) ++{ ++ return data; ++} + static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, + unsigned char reportnum, u8 *buf, + u32 size, enum hid_report_type rtype, +-- +2.53.0 + diff --git a/queue-6.12/inet-frags-add-inet_frag_queue_flush.patch b/queue-6.12/inet-frags-add-inet_frag_queue_flush.patch new file mode 100644 index 0000000000..d621b7c0e3 --- /dev/null +++ b/queue-6.12/inet-frags-add-inet_frag_queue_flush.patch @@ -0,0 +1,98 @@ +From 9d595a2730135481b82f345fe859a5e2f8483826 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 17:29:51 +0800 +Subject: inet: frags: add inet_frag_queue_flush() + +From: Jakub Kicinski + +[ Upstream commit 1231eec6994be29d6bb5c303dfa54731ed9fc0e6 ] + +Instead of exporting inet_frag_rbtree_purge() which requires that +caller takes care of memory accounting, add a new helper. We will +need to call it from a few places in the next patch. + +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20251207010942.1672972-3-kuba@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Rajani Kantha <681739313@139.com> +Signed-off-by: Sasha Levin +--- + include/net/inet_frag.h | 5 ++--- + net/ipv4/inet_fragment.c | 15 ++++++++++++--- + net/ipv4/ip_fragment.c | 6 +----- + 3 files changed, 15 insertions(+), 11 deletions(-) + +diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h +index 5af6eb14c5db15..94edc0e130d2c4 100644 +--- a/include/net/inet_frag.h ++++ b/include/net/inet_frag.h +@@ -141,9 +141,8 @@ void inet_frag_kill(struct inet_frag_queue *q); + void inet_frag_destroy(struct inet_frag_queue *q); + struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key); + +-/* Free all skbs in the queue; return the sum of their truesizes. */ +-unsigned int inet_frag_rbtree_purge(struct rb_root *root, +- enum skb_drop_reason reason); ++void inet_frag_queue_flush(struct inet_frag_queue *q, ++ enum skb_drop_reason reason); + + static inline void inet_frag_put(struct inet_frag_queue *q) + { +diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c +index d179a2c8422276..70640906337757 100644 +--- a/net/ipv4/inet_fragment.c ++++ b/net/ipv4/inet_fragment.c +@@ -264,8 +264,8 @@ static void inet_frag_destroy_rcu(struct rcu_head *head) + kmem_cache_free(f->frags_cachep, q); + } + +-unsigned int inet_frag_rbtree_purge(struct rb_root *root, +- enum skb_drop_reason reason) ++static unsigned int ++inet_frag_rbtree_purge(struct rb_root *root, enum skb_drop_reason reason) + { + struct rb_node *p = rb_first(root); + unsigned int sum = 0; +@@ -285,7 +285,16 @@ unsigned int inet_frag_rbtree_purge(struct rb_root *root, + } + return sum; + } +-EXPORT_SYMBOL(inet_frag_rbtree_purge); ++ ++void inet_frag_queue_flush(struct inet_frag_queue *q, ++ enum skb_drop_reason reason) ++{ ++ unsigned int sum; ++ ++ sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); ++ sub_frag_mem_limit(q->fqdir, sum); ++} ++EXPORT_SYMBOL(inet_frag_queue_flush); + + void inet_frag_destroy(struct inet_frag_queue *q) + { +diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c +index 183856b0b74094..eb5f6060b85d52 100644 +--- a/net/ipv4/ip_fragment.c ++++ b/net/ipv4/ip_fragment.c +@@ -253,16 +253,12 @@ static int ip_frag_too_far(struct ipq *qp) + + static int ip_frag_reinit(struct ipq *qp) + { +- unsigned int sum_truesize = 0; +- + if (!mod_timer(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) { + refcount_inc(&qp->q.refcnt); + return -ETIMEDOUT; + } + +- sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, +- SKB_DROP_REASON_FRAG_TOO_FAR); +- sub_frag_mem_limit(qp->q.fqdir, sum_truesize); ++ inet_frag_queue_flush(&qp->q, SKB_DROP_REASON_FRAG_TOO_FAR); + + qp->q.flags = 0; + qp->q.len = 0; +-- +2.53.0 + diff --git a/queue-6.12/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch b/queue-6.12/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch new file mode 100644 index 0000000000..8d1abecb45 --- /dev/null +++ b/queue-6.12/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch @@ -0,0 +1,187 @@ +From 4d76798300d3b111bdaa22eccdfe650a382b9496 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 17:29:52 +0800 +Subject: inet: frags: flush pending skbs in fqdir_pre_exit() + +From: Jakub Kicinski + +[ Upstream commit 006a5035b495dec008805df249f92c22c89c3d2e ] + +We have been seeing occasional deadlocks on pernet_ops_rwsem since +September in NIPA. The stuck task was usually modprobe (often loading +a driver like ipvlan), trying to take the lock as a Writer. +lockdep does not track readers for rwsems so the read wasn't obvious +from the reports. + +On closer inspection the Reader holding the lock was conntrack looping +forever in nf_conntrack_cleanup_net_list(). Based on past experience +with occasional NIPA crashes I looked thru the tests which run before +the crash and noticed that the crash follows ip_defrag.sh. An immediate +red flag. Scouring thru (de)fragmentation queues reveals skbs sitting +around, holding conntrack references. + +The problem is that since conntrack depends on nf_defrag_ipv6, +nf_defrag_ipv6 will load first. Since nf_defrag_ipv6 loads first its +netns exit hooks run _after_ conntrack's netns exit hook. + +Flush all fragment queue SKBs during fqdir_pre_exit() to release +conntrack references before conntrack cleanup runs. Also flush +the queues in timer expiry handlers when they discover fqdir->dead +is set, in case packet sneaks in while we're running the pre_exit +flush. + +The commit under Fixes is not exactly the culprit, but I think +previously the timer firing would eventually unblock the spinning +conntrack. + +Fixes: d5dd88794a13 ("inet: fix various use-after-free in defrags units") +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20251207010942.1672972-4-kuba@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Rajani Kantha <681739313@139.com> +Signed-off-by: Sasha Levin +--- + include/net/inet_frag.h | 13 +------------ + include/net/ipv6_frag.h | 9 ++++++--- + net/ipv4/inet_fragment.c | 36 ++++++++++++++++++++++++++++++++++++ + net/ipv4/ip_fragment.c | 12 +++++++----- + 4 files changed, 50 insertions(+), 20 deletions(-) + +diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h +index 94edc0e130d2c4..fcabb34fff35de 100644 +--- a/include/net/inet_frag.h ++++ b/include/net/inet_frag.h +@@ -123,18 +123,7 @@ void inet_frags_fini(struct inet_frags *); + + int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net); + +-static inline void fqdir_pre_exit(struct fqdir *fqdir) +-{ +- /* Prevent creation of new frags. +- * Pairs with READ_ONCE() in inet_frag_find(). +- */ +- WRITE_ONCE(fqdir->high_thresh, 0); +- +- /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire() +- * and ip6frag_expire_frag_queue(). +- */ +- WRITE_ONCE(fqdir->dead, true); +-} ++void fqdir_pre_exit(struct fqdir *fqdir); + void fqdir_exit(struct fqdir *fqdir); + + void inet_frag_kill(struct inet_frag_queue *q); +diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h +index 7321ffe3a108c1..df61b98b521531 100644 +--- a/include/net/ipv6_frag.h ++++ b/include/net/ipv6_frag.h +@@ -68,9 +68,6 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) + struct sk_buff *head; + + rcu_read_lock(); +- /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */ +- if (READ_ONCE(fq->q.fqdir->dead)) +- goto out_rcu_unlock; + spin_lock(&fq->q.lock); + + if (fq->q.flags & INET_FRAG_COMPLETE) +@@ -79,6 +76,12 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) + fq->q.flags |= INET_FRAG_DROP; + inet_frag_kill(&fq->q); + ++ /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */ ++ if (READ_ONCE(fq->q.fqdir->dead)) { ++ inet_frag_queue_flush(&fq->q, 0); ++ goto out; ++ } ++ + dev = dev_get_by_index_rcu(net, fq->iif); + if (!dev) + goto out; +diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c +index 70640906337757..f9cf20b21a0781 100644 +--- a/net/ipv4/inet_fragment.c ++++ b/net/ipv4/inet_fragment.c +@@ -219,6 +219,41 @@ static int __init inet_frag_wq_init(void) + + pure_initcall(inet_frag_wq_init); + ++void fqdir_pre_exit(struct fqdir *fqdir) ++{ ++ struct inet_frag_queue *fq; ++ struct rhashtable_iter hti; ++ ++ /* Prevent creation of new frags. ++ * Pairs with READ_ONCE() in inet_frag_find(). ++ */ ++ WRITE_ONCE(fqdir->high_thresh, 0); ++ ++ /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire() ++ * and ip6frag_expire_frag_queue(). ++ */ ++ WRITE_ONCE(fqdir->dead, true); ++ ++ rhashtable_walk_enter(&fqdir->rhashtable, &hti); ++ rhashtable_walk_start(&hti); ++ ++ while ((fq = rhashtable_walk_next(&hti))) { ++ if (IS_ERR(fq)) { ++ if (PTR_ERR(fq) != -EAGAIN) ++ break; ++ continue; ++ } ++ spin_lock_bh(&fq->lock); ++ if (!(fq->flags & INET_FRAG_COMPLETE)) ++ inet_frag_queue_flush(fq, 0); ++ spin_unlock_bh(&fq->lock); ++ } ++ ++ rhashtable_walk_stop(&hti); ++ rhashtable_walk_exit(&hti); ++} ++EXPORT_SYMBOL(fqdir_pre_exit); ++ + void fqdir_exit(struct fqdir *fqdir) + { + INIT_WORK(&fqdir->destroy_work, fqdir_work_fn); +@@ -291,6 +326,7 @@ void inet_frag_queue_flush(struct inet_frag_queue *q, + { + unsigned int sum; + ++ reason = reason ?: SKB_DROP_REASON_FRAG_REASM_TIMEOUT; + sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); + sub_frag_mem_limit(q->fqdir, sum); + } +diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c +index eb5f6060b85d52..124c0d64d4204b 100644 +--- a/net/ipv4/ip_fragment.c ++++ b/net/ipv4/ip_fragment.c +@@ -148,11 +148,6 @@ static void ip_expire(struct timer_list *t) + net = qp->q.fqdir->net; + + rcu_read_lock(); +- +- /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ +- if (READ_ONCE(qp->q.fqdir->dead)) +- goto out_rcu_unlock; +- + spin_lock(&qp->q.lock); + + if (qp->q.flags & INET_FRAG_COMPLETE) +@@ -160,6 +155,13 @@ static void ip_expire(struct timer_list *t) + + qp->q.flags |= INET_FRAG_DROP; + ipq_kill(qp); ++ ++ /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ ++ if (READ_ONCE(qp->q.fqdir->dead)) { ++ inet_frag_queue_flush(&qp->q, 0); ++ goto out; ++ } ++ + __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); + __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT); + +-- +2.53.0 + diff --git a/queue-6.12/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch b/queue-6.12/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch new file mode 100644 index 0000000000..19becad00b --- /dev/null +++ b/queue-6.12/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch @@ -0,0 +1,759 @@ +From 696344ed3f941bb466477beb5ecb66d31998a47e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 20 Dec 2025 10:33:26 +0000 +Subject: media: rc: fix race between unregister and urb/irq callbacks + +From: Sean Young + +[ Upstream commit dccc0c3ddf8f16071736f98a7d6dd46a2d43e037 ] + +Some rc device drivers have a race condition between rc_unregister_device() +and irq or urb callbacks. This is because rc_unregister_device() does two +things, it marks the device as unregistered so no new commands can be +issued and then it calls rc_free_device(). This means the driver has no +chance to cancel any pending urb callbacks or interrupts after the device +has been marked as unregistered. Those callbacks may access struct rc_dev +or its members (e.g. struct ir_raw_event_ctrl), which have been freed by +rc_free_device(). + +This change removes the implicit call to rc_free_device() from +rc_unregister_device(). This means that device drivers can call +rc_unregister_device() in their remove or disconnect function, then cancel +all the urbs and interrupts before explicitly calling rc_free_device(). + +Note this is an alternative fix for an issue found by Haotian Zhang, see +the Closes: tags. + +Reported-by: Haotian Zhang +Closes: https://lore.kernel.org/linux-media/20251114101432.2566-1-vulab@iscas.ac.cn/ +Closes: https://lore.kernel.org/linux-media/20251114101418.2548-1-vulab@iscas.ac.cn/ +Closes: https://lore.kernel.org/linux-media/20251114101346.2530-1-vulab@iscas.ac.cn/ +Closes: https://lore.kernel.org/linux-media/20251114090605.2413-1-vulab@iscas.ac.cn/ +Reviewed-by: Patrice Chotard +Signed-off-by: Sean Young +Signed-off-by: Hans Verkuil +Stable-dep-of: 646ebdd31058 ("media: rc: ttusbir: fix inverted error logic") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/bridge/sil-sii8620.c | 1 + + drivers/hid/hid-picolcd_cir.c | 1 + + drivers/media/cec/core/cec-core.c | 2 +- + drivers/media/common/siano/smsir.c | 1 + + drivers/media/i2c/ir-kbd-i2c.c | 2 ++ + drivers/media/pci/bt8xx/bttv-input.c | 3 ++- + drivers/media/pci/cx23885/cx23885-input.c | 1 + + drivers/media/pci/cx88/cx88-input.c | 3 ++- + drivers/media/pci/dm1105/dm1105.c | 1 + + drivers/media/pci/mantis/mantis_input.c | 1 + + drivers/media/pci/saa7134/saa7134-input.c | 1 + + drivers/media/pci/smipcie/smipcie-ir.c | 1 + + drivers/media/pci/ttpci/budget-ci.c | 1 + + drivers/media/rc/ati_remote.c | 6 +++--- + drivers/media/rc/ene_ir.c | 2 +- + drivers/media/rc/fintek-cir.c | 3 ++- + drivers/media/rc/igorplugusb.c | 1 + + drivers/media/rc/iguanair.c | 1 + + drivers/media/rc/img-ir/img-ir-hw.c | 3 ++- + drivers/media/rc/img-ir/img-ir-raw.c | 3 ++- + drivers/media/rc/imon.c | 3 ++- + drivers/media/rc/ir-hix5hd2.c | 2 +- + drivers/media/rc/ir_toy.c | 1 + + drivers/media/rc/ite-cir.c | 2 +- + drivers/media/rc/mceusb.c | 1 + + drivers/media/rc/rc-ir-raw.c | 5 ----- + drivers/media/rc/rc-loopback.c | 1 + + drivers/media/rc/rc-main.c | 6 +----- + drivers/media/rc/redrat3.c | 4 +++- + drivers/media/rc/st_rc.c | 2 +- + drivers/media/rc/streamzap.c | 7 ++++--- + drivers/media/rc/sunxi-cir.c | 1 + + drivers/media/rc/ttusbir.c | 2 +- + drivers/media/rc/winbond-cir.c | 2 +- + drivers/media/rc/xbox_remote.c | 5 +++-- + drivers/media/usb/au0828/au0828-input.c | 1 + + drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 1 + + drivers/media/usb/dvb-usb/dvb-usb-remote.c | 6 ++++-- + drivers/media/usb/em28xx/em28xx-input.c | 1 + + drivers/staging/media/av7110/av7110_ir.c | 1 + + include/media/rc-core.h | 2 -- + 41 files changed, 58 insertions(+), 36 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c +index 26b8d137bce096..2baeb1c5301ead 100644 +--- a/drivers/gpu/drm/bridge/sil-sii8620.c ++++ b/drivers/gpu/drm/bridge/sil-sii8620.c +@@ -2220,6 +2220,7 @@ static void sii8620_detach(struct drm_bridge *bridge) + return; + + rc_unregister_device(ctx->rc_dev); ++ rc_free_device(ctx->rc_dev); + } + + static int sii8620_is_packing_required(struct sii8620 *ctx, +diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c +index d6faa0e00f95ac..6d4c636e1c9f7e 100644 +--- a/drivers/hid/hid-picolcd_cir.c ++++ b/drivers/hid/hid-picolcd_cir.c +@@ -134,5 +134,6 @@ void picolcd_exit_cir(struct picolcd_data *data) + + data->rc_dev = NULL; + rc_unregister_device(rdev); ++ rc_free_device(rdev); + } + +diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c +index 865d86f34add0e..b3c0710eb5b18d 100644 +--- a/drivers/media/cec/core/cec-core.c ++++ b/drivers/media/cec/core/cec-core.c +@@ -337,8 +337,8 @@ int cec_register_adapter(struct cec_adapter *adap, + res = cec_devnode_register(&adap->devnode, adap->owner); + if (res) { + #ifdef CONFIG_MEDIA_CEC_RC +- /* Note: rc_unregister also calls rc_free */ + rc_unregister_device(adap->rc); ++ rc_free_device(adap->rc); + adap->rc = NULL; + #endif + return res; +diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c +index d85c78c104b990..5f4c0aa7a0d72a 100644 +--- a/drivers/media/common/siano/smsir.c ++++ b/drivers/media/common/siano/smsir.c +@@ -92,6 +92,7 @@ int sms_ir_init(struct smscore_device_t *coredev) + void sms_ir_exit(struct smscore_device_t *coredev) + { + rc_unregister_device(coredev->ir.dev); ++ rc_free_device(coredev->ir.dev); + + pr_debug("\n"); + } +diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c +index 5588cdd7ec20de..60474531700433 100644 +--- a/drivers/media/i2c/ir-kbd-i2c.c ++++ b/drivers/media/i2c/ir-kbd-i2c.c +@@ -355,6 +355,7 @@ static void ir_work(struct work_struct *work) + mutex_unlock(&ir->lock); + if (rc == -ENODEV) { + rc_unregister_device(ir->rc); ++ rc_free_device(ir->rc); + ir->rc = NULL; + return; + } +@@ -972,6 +973,7 @@ static void ir_remove(struct i2c_client *client) + i2c_unregister_device(ir->tx_c); + + rc_unregister_device(ir->rc); ++ rc_free_device(ir->rc); + } + + static const struct i2c_device_id ir_kbd_id[] = { +diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c +index 41226f1d0e5b64..d70e6282c48b2e 100644 +--- a/drivers/media/pci/bt8xx/bttv-input.c ++++ b/drivers/media/pci/bt8xx/bttv-input.c +@@ -572,8 +572,9 @@ void bttv_input_fini(struct bttv *btv) + if (btv->remote == NULL) + return; + +- bttv_ir_stop(btv); + rc_unregister_device(btv->remote->dev); ++ bttv_ir_stop(btv); ++ rc_free_device(btv->remote->dev); + kfree(btv->remote); + btv->remote = NULL; + } +diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c +index d2e84c6457e0ab..722329ef3fd2cc 100644 +--- a/drivers/media/pci/cx23885/cx23885-input.c ++++ b/drivers/media/pci/cx23885/cx23885-input.c +@@ -402,6 +402,7 @@ void cx23885_input_fini(struct cx23885_dev *dev) + if (dev->kernel_ir == NULL) + return; + rc_unregister_device(dev->kernel_ir->rc); ++ rc_free_device(dev->kernel_ir->rc); + kfree(dev->kernel_ir->phys); + kfree(dev->kernel_ir->name); + kfree(dev->kernel_ir); +diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c +index a04a1d33fadb1b..74a8769dd6c79c 100644 +--- a/drivers/media/pci/cx88/cx88-input.c ++++ b/drivers/media/pci/cx88/cx88-input.c +@@ -510,8 +510,9 @@ int cx88_ir_fini(struct cx88_core *core) + if (!ir) + return 0; + +- cx88_ir_stop(core); + rc_unregister_device(ir->dev); ++ cx88_ir_stop(core); ++ rc_free_device(ir->dev); + kfree(ir); + + /* done */ +diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c +index 9e9c7c071accce..e1185aa669f480 100644 +--- a/drivers/media/pci/dm1105/dm1105.c ++++ b/drivers/media/pci/dm1105/dm1105.c +@@ -763,6 +763,7 @@ static int dm1105_ir_init(struct dm1105_dev *dm1105) + static void dm1105_ir_exit(struct dm1105_dev *dm1105) + { + rc_unregister_device(dm1105->ir.dev); ++ rc_free_device(dm1105->ir.dev); + } + + static int dm1105_hw_init(struct dm1105_dev *dev) +diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c +index 34c0d979240fda..edb4cacf55d229 100644 +--- a/drivers/media/pci/mantis/mantis_input.c ++++ b/drivers/media/pci/mantis/mantis_input.c +@@ -72,5 +72,6 @@ EXPORT_SYMBOL_GPL(mantis_input_init); + void mantis_input_exit(struct mantis_pci *mantis) + { + rc_unregister_device(mantis->rc); ++ rc_free_device(mantis->rc); + } + EXPORT_SYMBOL_GPL(mantis_input_exit); +diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c +index 8610eb473b39e1..8a0f26d94d1de8 100644 +--- a/drivers/media/pci/saa7134/saa7134-input.c ++++ b/drivers/media/pci/saa7134/saa7134-input.c +@@ -834,6 +834,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) + return; + + rc_unregister_device(dev->remote->dev); ++ rc_free_device(dev->remote->dev); + kfree(dev->remote); + dev->remote = NULL; + } +diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c +index c0604d9c70119a..0bbe4fa2d5a84e 100644 +--- a/drivers/media/pci/smipcie/smipcie-ir.c ++++ b/drivers/media/pci/smipcie/smipcie-ir.c +@@ -181,5 +181,6 @@ void smi_ir_exit(struct smi_dev *dev) + + rc_unregister_device(rc_dev); + smi_ir_stop(ir); ++ rc_free_device(rc_dev); + ir->rc_dev = NULL; + } +diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c +index 33f08adf4feb10..16973ac8e6a920 100644 +--- a/drivers/media/pci/ttpci/budget-ci.c ++++ b/drivers/media/pci/ttpci/budget-ci.c +@@ -249,6 +249,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) + cancel_work_sync(&budget_ci->ir.msp430_irq_bh_work); + + rc_unregister_device(budget_ci->ir.dev); ++ rc_free_device(budget_ci->ir.dev); + } + + static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c +index d7721e60776edd..46d1844f5c9873 100644 +--- a/drivers/media/rc/ati_remote.c ++++ b/drivers/media/rc/ati_remote.c +@@ -921,7 +921,6 @@ static int ati_remote_probe(struct usb_interface *interface, + input_free_device(input_dev); + exit_unregister_device: + rc_unregister_device(rc_dev); +- rc_dev = NULL; + exit_kill_urbs: + usb_kill_urb(ati_remote->irq_urb); + usb_kill_urb(ati_remote->out_urb); +@@ -941,18 +940,19 @@ static void ati_remote_disconnect(struct usb_interface *interface) + struct ati_remote *ati_remote; + + ati_remote = usb_get_intfdata(interface); +- usb_set_intfdata(interface, NULL); + if (!ati_remote) { + dev_warn(&interface->dev, "%s - null device?\n", __func__); + return; + } + ++ rc_unregister_device(ati_remote->rdev); ++ usb_set_intfdata(interface, NULL); + usb_kill_urb(ati_remote->irq_urb); + usb_kill_urb(ati_remote->out_urb); + if (ati_remote->idev) + input_unregister_device(ati_remote->idev); +- rc_unregister_device(ati_remote->rdev); + ati_remote_free_buffers(ati_remote); ++ rc_free_device(ati_remote->rdev); + kfree(ati_remote); + } + +diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c +index 67722e2e47ff78..3fd51a41c3b2b6 100644 +--- a/drivers/media/rc/ene_ir.c ++++ b/drivers/media/rc/ene_ir.c +@@ -1090,7 +1090,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) + release_region(dev->hw_io, ENE_IO_SIZE); + exit_unregister_device: + rc_unregister_device(rdev); +- rdev = NULL; + exit_free_dev_rdev: + rc_free_device(rdev); + kfree(dev); +@@ -1110,6 +1109,7 @@ static void ene_remove(struct pnp_dev *pnp_dev) + ene_rx_restore_hw_buffer(dev); + spin_unlock_irqrestore(&dev->hw_lock, flags); + ++ rc_free_device(dev->rdev); + free_irq(dev->irq, dev); + release_region(dev->hw_io, ENE_IO_SIZE); + kfree(dev); +diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c +index 3fb0968efd57d3..9b789097cdd4c3 100644 +--- a/drivers/media/rc/fintek-cir.c ++++ b/drivers/media/rc/fintek-cir.c +@@ -568,6 +568,7 @@ static void fintek_remove(struct pnp_dev *pdev) + struct fintek_dev *fintek = pnp_get_drvdata(pdev); + unsigned long flags; + ++ rc_unregister_device(fintek->rdev); + spin_lock_irqsave(&fintek->fintek_lock, flags); + /* disable CIR */ + fintek_disable_cir(fintek); +@@ -580,7 +581,7 @@ static void fintek_remove(struct pnp_dev *pdev) + free_irq(fintek->cir_irq, fintek); + release_region(fintek->cir_addr, fintek->cir_port_len); + +- rc_unregister_device(fintek->rdev); ++ rc_free_device(fintek->rdev); + + kfree(fintek); + } +diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c +index f3616607d4f52b..8bf059b9a31b45 100644 +--- a/drivers/media/rc/igorplugusb.c ++++ b/drivers/media/rc/igorplugusb.c +@@ -247,6 +247,7 @@ static void igorplugusb_disconnect(struct usb_interface *intf) + usb_set_intfdata(intf, NULL); + usb_unpoison_urb(ir->urb); + usb_free_urb(ir->urb); ++ rc_free_device(ir->rc); + kfree(ir->buf_in); + kfree(ir->request); + } +diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c +index 8af94246e5916e..7bd6dd7254157a 100644 +--- a/drivers/media/rc/iguanair.c ++++ b/drivers/media/rc/iguanair.c +@@ -500,6 +500,7 @@ static void iguanair_disconnect(struct usb_interface *intf) + usb_set_intfdata(intf, NULL); + usb_kill_urb(ir->urb_in); + usb_kill_urb(ir->urb_out); ++ rc_free_device(ir->rc); + usb_free_urb(ir->urb_in); + usb_free_urb(ir->urb_out); + usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in); +diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c +index 5da7479c1793b1..07f41372976eaf 100644 +--- a/drivers/media/rc/img-ir/img-ir-hw.c ++++ b/drivers/media/rc/img-ir/img-ir-hw.c +@@ -1117,9 +1117,10 @@ void img_ir_remove_hw(struct img_ir_priv *priv) + struct rc_dev *rdev = hw->rdev; + if (!rdev) + return; ++ rc_unregister_device(rdev); + img_ir_set_decoder(priv, NULL, 0); + hw->rdev = NULL; +- rc_unregister_device(rdev); ++ rc_free_device(rdev); + #ifdef CONFIG_COMMON_CLK + if (!IS_ERR(priv->clk)) + clk_notifier_unregister(priv->clk, &hw->clk_nb); +diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c +index 8b0bdd9603b3c5..533d40dae54224 100644 +--- a/drivers/media/rc/img-ir/img-ir-raw.c ++++ b/drivers/media/rc/img-ir/img-ir-raw.c +@@ -136,6 +136,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv) + if (!rdev) + return; + ++ rc_unregister_device(rdev); + /* switch off and disable raw (edge) interrupts */ + spin_lock_irq(&priv->lock); + raw->rdev = NULL; +@@ -145,7 +146,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv) + img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE); + spin_unlock_irq(&priv->lock); + +- rc_unregister_device(rdev); ++ rc_free_device(rdev); + + del_timer_sync(&raw->timer); + } +diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c +index ddb1304cb77b82..cb9bd5a6ff54f7 100644 +--- a/drivers/media/rc/imon.c ++++ b/drivers/media/rc/imon.c +@@ -2546,9 +2546,10 @@ static void imon_disconnect(struct usb_interface *interface) + + if (ifnum == 0) { + ictx->dev_present_intf0 = false; ++ rc_unregister_device(ictx->rdev); + usb_kill_urb(ictx->rx_urb_intf0); + input_unregister_device(ictx->idev); +- rc_unregister_device(ictx->rdev); ++ rc_free_device(ictx->rdev); + if (ictx->display_supported) { + if (ictx->display_type == IMON_DISPLAY_TYPE_LCD) + usb_deregister_dev(interface, &imon_lcd_class); +diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c +index de5bb9a08ea4c0..1604679fa2c807 100644 +--- a/drivers/media/rc/ir-hix5hd2.c ++++ b/drivers/media/rc/ir-hix5hd2.c +@@ -331,7 +331,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) + + regerr: + rc_unregister_device(rdev); +- rdev = NULL; + clkerr: + clk_disable_unprepare(priv->clock); + err: +@@ -346,6 +345,7 @@ static void hix5hd2_ir_remove(struct platform_device *pdev) + + clk_disable_unprepare(priv->clock); + rc_unregister_device(priv->rdev); ++ rc_free_device(priv->rdev); + } + + #ifdef CONFIG_PM_SLEEP +diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c +index 533faa11751744..e79de56997a426 100644 +--- a/drivers/media/rc/ir_toy.c ++++ b/drivers/media/rc/ir_toy.c +@@ -536,6 +536,7 @@ static void irtoy_disconnect(struct usb_interface *intf) + usb_free_urb(ir->urb_out); + usb_kill_urb(ir->urb_in); + usb_free_urb(ir->urb_in); ++ rc_free_device(ir->rc); + kfree(ir->in); + kfree(ir->out); + kfree(ir); +diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c +index 2bacecb022623e..23afbafb557488 100644 +--- a/drivers/media/rc/ite-cir.c ++++ b/drivers/media/rc/ite-cir.c +@@ -1414,7 +1414,6 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id + release_region(itdev->cir_addr, itdev->params->io_region_size); + exit_unregister_device: + rc_unregister_device(rdev); +- rdev = NULL; + exit_free_dev_rdev: + rc_free_device(rdev); + kfree(itdev); +@@ -1439,6 +1438,7 @@ static void ite_remove(struct pnp_dev *pdev) + release_region(dev->cir_addr, dev->params->io_region_size); + + rc_unregister_device(dev->rdev); ++ rc_free_device(dev->rdev); + + kfree(dev); + } +diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c +index cd7af4d88b7f77..bf93b94d337fea 100644 +--- a/drivers/media/rc/mceusb.c ++++ b/drivers/media/rc/mceusb.c +@@ -1851,6 +1851,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf) + usb_free_urb(ir->urb_in); + usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in); + usb_put_dev(dev); ++ rc_free_device(ir->rc); + + kfree(ir); + } +diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c +index 16e33d7eaaa2d8..dd70f8ad526642 100644 +--- a/drivers/media/rc/rc-ir-raw.c ++++ b/drivers/media/rc/rc-ir-raw.c +@@ -647,9 +647,6 @@ int ir_raw_event_register(struct rc_dev *dev) + + void ir_raw_event_free(struct rc_dev *dev) + { +- if (!dev) +- return; +- + kfree(dev->raw); + dev->raw = NULL; + } +@@ -673,8 +670,6 @@ void ir_raw_event_unregister(struct rc_dev *dev) + + lirc_bpf_free(dev); + +- ir_raw_event_free(dev); +- + /* + * A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so + * ensure that the raw member is null on unlock; this is how +diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c +index 8288366f891fc9..a108b057b5fd56 100644 +--- a/drivers/media/rc/rc-loopback.c ++++ b/drivers/media/rc/rc-loopback.c +@@ -263,6 +263,7 @@ static int __init loop_init(void) + static void __exit loop_exit(void) + { + rc_unregister_device(loopdev.dev); ++ rc_free_device(loopdev.dev); + } + + module_init(loop_init); +diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c +index a4c539b17cf344..a4c0ec06ee6aa3 100644 +--- a/drivers/media/rc/rc-main.c ++++ b/drivers/media/rc/rc-main.c +@@ -1611,6 +1611,7 @@ static void rc_dev_release(struct device *device) + { + struct rc_dev *dev = to_rc_dev(device); + ++ ir_raw_event_free(dev); + kfree(dev); + } + +@@ -1773,7 +1774,6 @@ struct rc_dev *devm_rc_allocate_device(struct device *dev, + } + + rc->dev.parent = dev; +- rc->managed_alloc = true; + *dr = rc; + devres_add(dev, dr); + +@@ -2042,11 +2042,7 @@ void rc_unregister_device(struct rc_dev *dev) + device_del(&dev->dev); + + ida_free(&rc_ida, dev->minor); +- +- if (!dev->managed_alloc) +- rc_free_device(dev); + } +- + EXPORT_SYMBOL_GPL(rc_unregister_device); + + /* +diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c +index a49173f54a4d0e..b8289327f6a206 100644 +--- a/drivers/media/rc/redrat3.c ++++ b/drivers/media/rc/redrat3.c +@@ -1133,11 +1133,13 @@ static void redrat3_dev_disconnect(struct usb_interface *intf) + { + struct usb_device *udev = interface_to_usbdev(intf); + struct redrat3_dev *rr3 = usb_get_intfdata(intf); ++ struct rc_dev *rc = rr3->rc; + + usb_set_intfdata(intf, NULL); +- rc_unregister_device(rr3->rc); ++ rc_unregister_device(rc); + led_classdev_unregister(&rr3->led); + redrat3_delete(rr3, udev); ++ rc_free_device(rc); + } + + static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message) +diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c +index fd2f056f287b25..79aad3d7f69f0d 100644 +--- a/drivers/media/rc/st_rc.c ++++ b/drivers/media/rc/st_rc.c +@@ -203,6 +203,7 @@ static void st_rc_remove(struct platform_device *pdev) + device_init_wakeup(&pdev->dev, false); + clk_disable_unprepare(rc_dev->sys_clock); + rc_unregister_device(rc_dev->rdev); ++ rc_free_device(rc_dev->rdev); + } + + static int st_rc_open(struct rc_dev *rdev) +@@ -334,7 +335,6 @@ static int st_rc_probe(struct platform_device *pdev) + return ret; + rcerr: + rc_unregister_device(rdev); +- rdev = NULL; + clkerr: + clk_disable_unprepare(rc_dev->sys_clock); + err: +diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c +index 8e9b156e430022..8c85b9f30a3a96 100644 +--- a/drivers/media/rc/streamzap.c ++++ b/drivers/media/rc/streamzap.c +@@ -392,15 +392,16 @@ static void streamzap_disconnect(struct usb_interface *interface) + struct streamzap_ir *sz = usb_get_intfdata(interface); + struct usb_device *usbdev = interface_to_usbdev(interface); + +- usb_set_intfdata(interface, NULL); +- + if (!sz) + return; + +- usb_kill_urb(sz->urb_in); + rc_unregister_device(sz->rdev); ++ usb_set_intfdata(interface, NULL); ++ ++ usb_kill_urb(sz->urb_in); + usb_free_urb(sz->urb_in); + usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in); ++ rc_free_device(sz->rdev); + + kfree(sz); + } +diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c +index b49df8355e6b39..448d453cfda934 100644 +--- a/drivers/media/rc/sunxi-cir.c ++++ b/drivers/media/rc/sunxi-cir.c +@@ -371,6 +371,7 @@ static void sunxi_ir_remove(struct platform_device *pdev) + struct sunxi_ir *ir = platform_get_drvdata(pdev); + + rc_unregister_device(ir->rc); ++ rc_free_device(ir->rc); + sunxi_ir_hw_exit(&pdev->dev); + } + +diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c +index dde446a95eaa93..a670d4b008cb0d 100644 +--- a/drivers/media/rc/ttusbir.c ++++ b/drivers/media/rc/ttusbir.c +@@ -336,7 +336,6 @@ static int ttusbir_probe(struct usb_interface *intf, + return 0; + out3: + rc_unregister_device(rc); +- rc = NULL; + out2: + led_classdev_unregister(&tt->led); + out: +@@ -378,6 +377,7 @@ static void ttusbir_disconnect(struct usb_interface *intf) + usb_kill_urb(tt->bulk_urb); + usb_free_urb(tt->bulk_urb); + kfree(tt->bulk_buffer); ++ rc_free_device(tt->rc); + usb_set_intfdata(intf, NULL); + kfree(tt); + } +diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c +index 25884a79985c8a..14d8b58e283980 100644 +--- a/drivers/media/rc/winbond-cir.c ++++ b/drivers/media/rc/winbond-cir.c +@@ -1132,7 +1132,6 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) + release_region(data->wbase, WAKEUP_IOMEM_LEN); + exit_unregister_device: + rc_unregister_device(data->dev); +- data->dev = NULL; + exit_free_rc: + rc_free_device(data->dev); + exit_unregister_led: +@@ -1163,6 +1162,7 @@ wbcir_remove(struct pnp_dev *device) + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); + + rc_unregister_device(data->dev); ++ rc_free_device(data->dev); + + led_classdev_unregister(&data->led); + +diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c +index 0c9c855ced729c..80b7c247932a8f 100644 +--- a/drivers/media/rc/xbox_remote.c ++++ b/drivers/media/rc/xbox_remote.c +@@ -283,14 +283,15 @@ static void xbox_remote_disconnect(struct usb_interface *interface) + struct xbox_remote *xbox_remote; + + xbox_remote = usb_get_intfdata(interface); +- usb_set_intfdata(interface, NULL); + if (!xbox_remote) { + dev_warn(&interface->dev, "%s - null device?\n", __func__); + return; + } + +- usb_kill_urb(xbox_remote->irq_urb); + rc_unregister_device(xbox_remote->rdev); ++ usb_set_intfdata(interface, NULL); ++ usb_kill_urb(xbox_remote->irq_urb); ++ rc_free_device(xbox_remote->rdev); + usb_free_urb(xbox_remote->irq_urb); + kfree(xbox_remote->inbuf); + kfree(xbox_remote); +diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c +index 3d3368202cd018..283ad2c6288cd5 100644 +--- a/drivers/media/usb/au0828/au0828-input.c ++++ b/drivers/media/usb/au0828/au0828-input.c +@@ -357,6 +357,7 @@ void au0828_rc_unregister(struct au0828_dev *dev) + return; + + rc_unregister_device(ir->rc); ++ rc_free_device(ir->rc); + + /* done */ + kfree(ir); +diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +index f1c79f351ec8de..17e8961179d14b 100644 +--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c ++++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +@@ -187,6 +187,7 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) + if (d->rc_dev) { + cancel_delayed_work_sync(&d->rc_query_work); + rc_unregister_device(d->rc_dev); ++ rc_free_device(d->rc_dev); + d->rc_dev = NULL; + } + +diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c +index 65e2c9e2cdc99f..6dc11718dfb985 100644 +--- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c ++++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c +@@ -347,10 +347,12 @@ int dvb_usb_remote_exit(struct dvb_usb_device *d) + { + if (d->state & DVB_USB_STATE_REMOTE) { + cancel_delayed_work_sync(&d->rc_query_work); +- if (d->props.rc.mode == DVB_RC_LEGACY) ++ if (d->props.rc.mode == DVB_RC_LEGACY) { + input_unregister_device(d->input_dev); +- else ++ } else { + rc_unregister_device(d->rc_dev); ++ rc_free_device(d->rc_dev); ++ } + } + d->state &= ~DVB_USB_STATE_REMOTE; + return 0; +diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c +index 5f3b00869bdbc9..26f333b5be7325 100644 +--- a/drivers/media/usb/em28xx/em28xx-input.c ++++ b/drivers/media/usb/em28xx/em28xx-input.c +@@ -853,6 +853,7 @@ static int em28xx_ir_fini(struct em28xx *dev) + goto ref_put; + + rc_unregister_device(ir->rc); ++ rc_free_device(ir->rc); + + kfree(ir->i2c_client); + +diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c +index 68b3979ba5f20c..fdae467fd7ab81 100644 +--- a/drivers/staging/media/av7110/av7110_ir.c ++++ b/drivers/staging/media/av7110/av7110_ir.c +@@ -151,6 +151,7 @@ int av7110_ir_init(struct av7110 *av7110) + void av7110_ir_exit(struct av7110 *av7110) + { + rc_unregister_device(av7110->ir.rcdev); ++ rc_free_device(av7110->ir.rcdev); + } + + //MODULE_AUTHOR("Holger Waechtler , Oliver Endriss "); +diff --git a/include/media/rc-core.h b/include/media/rc-core.h +index d095908073ef9d..7a8511d0d4b4a4 100644 +--- a/include/media/rc-core.h ++++ b/include/media/rc-core.h +@@ -81,7 +81,6 @@ struct lirc_fh { + /** + * struct rc_dev - represents a remote control device + * @dev: driver model's view of this device +- * @managed_alloc: devm_rc_allocate_device was used to create rc_dev + * @sysfs_groups: sysfs attribute groups + * @device_name: name of the rc child device + * @input_phys: physical path to the input child device +@@ -156,7 +155,6 @@ struct lirc_fh { + */ + struct rc_dev { + struct device dev; +- bool managed_alloc; + const struct attribute_group *sysfs_groups[5]; + const char *device_name; + const char *input_phys; +-- +2.53.0 + diff --git a/queue-6.12/media-rc-ttusbir-fix-inverted-error-logic.patch b/queue-6.12/media-rc-ttusbir-fix-inverted-error-logic.patch new file mode 100644 index 0000000000..f239c47db0 --- /dev/null +++ b/queue-6.12/media-rc-ttusbir-fix-inverted-error-logic.patch @@ -0,0 +1,38 @@ +From 491f8165e6392cf906f3dc941f01c4afd1c84427 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Apr 2026 23:03:09 +0200 +Subject: media: rc: ttusbir: fix inverted error logic + +From: Oliver Neukum + +[ Upstream commit 646ebdd3105809d84ed04aa9e92e47e89cc44502 ] + +We have to report ENOMEM if no buffer is allocated. +Typo dropped a "!". Restore it. + +Fixes: 50acaad3d202 ("media: rc: ttusbir: respect DMA coherency rules") +Cc: stable@vger.kernel.org +Signed-off-by: Oliver Neukum +Signed-off-by: Sean Young +Signed-off-by: Hans Verkuil +Signed-off-by: Sasha Levin +--- + drivers/media/rc/ttusbir.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c +index a670d4b008cb0d..3452b5aefd2848 100644 +--- a/drivers/media/rc/ttusbir.c ++++ b/drivers/media/rc/ttusbir.c +@@ -191,7 +191,7 @@ static int ttusbir_probe(struct usb_interface *intf, + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + buffer = kzalloc(5, GFP_KERNEL); + rc = rc_allocate_device(RC_DRIVER_IR_RAW); +- if (!tt || !rc || buffer) { ++ if (!tt || !rc || !buffer) { + ret = -ENOMEM; + goto out; + } +-- +2.53.0 + diff --git a/queue-6.12/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch b/queue-6.12/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch new file mode 100644 index 0000000000..82e814d6d8 --- /dev/null +++ b/queue-6.12/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch @@ -0,0 +1,67 @@ +From adfba1244f2825484fdd8949ef2b4e8217109b61 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 13:02:30 +0800 +Subject: mm/page_alloc: clear page->private in free_pages_prepare() + +From: Mikhail Gavrilov + +[ Upstream commit ac1ea219590c09572ed5992dc233bbf7bb70fef9 ] + +Several subsystems (slub, shmem, ttm, etc.) use page->private but don't +clear it before freeing pages. When these pages are later allocated as +high-order pages and split via split_page(), tail pages retain stale +page->private values. + +This causes a use-after-free in the swap subsystem. The swap code uses +page->private to track swap count continuations, assuming freshly +allocated pages have page->private == 0. When stale values are present, +swap_count_continued() incorrectly assumes the continuation list is valid +and iterates over uninitialized page->lru containing LIST_POISON values, +causing a crash: + + KASAN: maybe wild-memory-access in range [0xdead000000000100-0xdead000000000107] + RIP: 0010:__do_sys_swapoff+0x1151/0x1860 + +Fix this by clearing page->private in free_pages_prepare(), ensuring all +freed pages have clean state regardless of previous use. + +Link: https://lkml.kernel.org/r/20260207173615.146159-1-mikhail.v.gavrilov@gmail.com +Fixes: 3b8000ae185c ("mm/vmalloc: huge vmalloc backing pages should be split rather than compound") +Signed-off-by: Mikhail Gavrilov +Suggested-by: Zi Yan +Acked-by: Zi Yan +Acked-by: David Hildenbrand (Arm) +Reviewed-by: Vlastimil Babka +Cc: Brendan Jackman +Cc: Chris Li +Cc: Hugh Dickins +Cc: Johannes Weiner +Cc: Kairui Song +Cc: Matthew Wilcox (Oracle) +Cc: Michal Hocko +Cc: Nicholas Piggin +Cc: Suren Baghdasaryan +Cc: +Signed-off-by: Andrew Morton +[backport: context only] +Signed-off-by: Li Wang +Signed-off-by: Sasha Levin +--- + mm/page_alloc.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/mm/page_alloc.c b/mm/page_alloc.c +index b1a8abe5005e99..259249a37faf01 100644 +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -1158,6 +1158,7 @@ __always_inline bool free_pages_prepare(struct page *page, + + page_cpupid_reset_last(page); + page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP; ++ page->private = 0; + reset_page_owner(page, order); + page_table_check_free(page, order); + pgalloc_tag_sub(page, 1 << order); +-- +2.53.0 + diff --git a/queue-6.12/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch b/queue-6.12/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch new file mode 100644 index 0000000000..4427f37ac7 --- /dev/null +++ b/queue-6.12/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch @@ -0,0 +1,64 @@ +From f17c5cf4f79bf980e7ed7511f2d03097bda6dfa7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 23:06:57 -0700 +Subject: perf: Fix dangling cgroup pointer in cpuctx + +From: Yeoreum Yun + +[ Upstream commit 3b7a34aebbdf2a4b7295205bf0c654294283ec82 ] + +Commit a3c3c6667("perf/core: Fix child_total_time_enabled accounting +bug at task exit") moves the event->state update to before +list_del_event(). This makes the event->state test in list_del_event() +always false; never calling perf_cgroup_event_disable(). + +As a result, cpuctx->cgrp won't be cleared properly; causing havoc. + +Fixes: a3c3c6667("perf/core: Fix child_total_time_enabled accounting bug at task exit") +Signed-off-by: Yeoreum Yun +Signed-off-by: Peter Zijlstra (Intel) +Tested-by: David Wang <00107082@163.com> +Link: https://lore.kernel.org/all/aD2TspKH%2F7yvfYoO@e129823.arm.com/ +Signed-off-by: Ian Klatzco +Signed-off-by: Sasha Levin +--- + kernel/events/core.c | 16 ++++------------ + 1 file changed, 4 insertions(+), 12 deletions(-) + +diff --git a/kernel/events/core.c b/kernel/events/core.c +index 6fce2bac6dae52..9099c0cc933be2 100644 +--- a/kernel/events/core.c ++++ b/kernel/events/core.c +@@ -2096,18 +2096,6 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) + if (event->group_leader == event) + del_event_from_groups(event, ctx); + +- /* +- * If event was in error state, then keep it +- * that way, otherwise bogus counts will be +- * returned on read(). The only way to get out +- * of error state is by explicit re-enabling +- * of the event +- */ +- if (event->state > PERF_EVENT_STATE_OFF) { +- perf_cgroup_event_disable(event, ctx); +- perf_event_set_state(event, PERF_EVENT_STATE_OFF); +- } +- + ctx->generation++; + event->pmu_ctx->nr_events--; + } +@@ -2457,6 +2445,10 @@ __perf_remove_from_context(struct perf_event *event, + state = PERF_EVENT_STATE_DEAD; + } + event_sched_out(event, ctx); ++ ++ if (event->state > PERF_EVENT_STATE_OFF) ++ perf_cgroup_event_disable(event, ctx); ++ + perf_event_set_state(event, min(event->state, state)); + if (flags & DETACH_GROUP) + perf_group_detach(event); +-- +2.53.0 + diff --git a/queue-6.12/s390-cio-restore-gfp_dma-for-chsc-allocation.patch b/queue-6.12/s390-cio-restore-gfp_dma-for-chsc-allocation.patch new file mode 100644 index 0000000000..a11d3dc959 --- /dev/null +++ b/queue-6.12/s390-cio-restore-gfp_dma-for-chsc-allocation.patch @@ -0,0 +1,152 @@ +From 462f5fe7184fef33160dba35c966ea3cc35a26dc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 17:48:55 +0200 +Subject: s390/cio: Restore GFP_DMA for CHSC allocation + +From: Peter Oberparleiter + +[ Upstream commit ea34567db0a6b3a7ce78ba421592344315c8f90e ] + +Re-add GFP_DMA when allocating memory for CHSC control blocks. +On some supported machines, CHSC cannot access memory outside +the DMA zone, causing CHSC command failures. + +Cc: stable@vger.kernel.org +Fixes: a3a64a4def8d ("s390/cio: remove unneeded DMA zone allocation") +Signed-off-by: Peter Oberparleiter +Reviewed-by: Heiko Carstens +Signed-off-by: Alexander Gordeev +[ adjusted context to account for missing commit bf4afc53b77ae ] +Signed-off-by: Peter Oberparleiter +Signed-off-by: Sasha Levin +--- + drivers/s390/cio/chsc.c | 4 ++-- + drivers/s390/cio/chsc_sch.c | 20 ++++++++++---------- + drivers/s390/cio/scm.c | 2 +- + 3 files changed, 13 insertions(+), 13 deletions(-) + +diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c +index dcc1e1c34ca2e1..8fe6658dcfe1a3 100644 +--- a/drivers/s390/cio/chsc.c ++++ b/drivers/s390/cio/chsc.c +@@ -1153,8 +1153,8 @@ int __init chsc_init(void) + { + int ret; + +- sei_page = (void *)get_zeroed_page(GFP_KERNEL); +- chsc_page = (void *)get_zeroed_page(GFP_KERNEL); ++ sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); ++ chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sei_page || !chsc_page) { + ret = -ENOMEM; + goto out_err; +diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c +index 1e58ee3cc87db1..9131ce3af1b8eb 100644 +--- a/drivers/s390/cio/chsc_sch.c ++++ b/drivers/s390/cio/chsc_sch.c +@@ -293,7 +293,7 @@ static int chsc_ioctl_start(void __user *user_area) + if (!css_general_characteristics.dynio) + /* It makes no sense to try. */ + return -EOPNOTSUPP; +- chsc_area = (void *)get_zeroed_page(GFP_KERNEL); ++ chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); + if (!chsc_area) + return -ENOMEM; + request = kzalloc(sizeof(*request), GFP_KERNEL); +@@ -341,7 +341,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area) + ret = -ENOMEM; + goto out_unlock; + } +- on_close_chsc_area = (void *)get_zeroed_page(GFP_KERNEL); ++ on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); + if (!on_close_chsc_area) { + ret = -ENOMEM; + goto out_free_request; +@@ -393,7 +393,7 @@ static int chsc_ioctl_start_sync(void __user *user_area) + struct chsc_sync_area *chsc_area; + int ret, ccode; + +- chsc_area = (void *)get_zeroed_page(GFP_KERNEL); ++ chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!chsc_area) + return -ENOMEM; + if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) { +@@ -439,7 +439,7 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd) + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *scpcd_area; + +- scpcd_area = (void *)get_zeroed_page(GFP_KERNEL); ++ scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scpcd_area) + return -ENOMEM; + cd = kzalloc(sizeof(*cd), GFP_KERNEL); +@@ -501,7 +501,7 @@ static int chsc_ioctl_info_cu(void __user *user_cd) + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *scucd_area; + +- scucd_area = (void *)get_zeroed_page(GFP_KERNEL); ++ scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scucd_area) + return -ENOMEM; + cd = kzalloc(sizeof(*cd), GFP_KERNEL); +@@ -564,7 +564,7 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud) + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *sscud_area; + +- sscud_area = (void *)get_zeroed_page(GFP_KERNEL); ++ sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sscud_area) + return -ENOMEM; + cud = kzalloc(sizeof(*cud), GFP_KERNEL); +@@ -626,7 +626,7 @@ static int chsc_ioctl_conf_info(void __user *user_ci) + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *sci_area; + +- sci_area = (void *)get_zeroed_page(GFP_KERNEL); ++ sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sci_area) + return -ENOMEM; + ci = kzalloc(sizeof(*ci), GFP_KERNEL); +@@ -697,7 +697,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl) + u32 res; + } __attribute__ ((packed)) *cssids_parm; + +- sccl_area = (void *)get_zeroed_page(GFP_KERNEL); ++ sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccl_area) + return -ENOMEM; + ccl = kzalloc(sizeof(*ccl), GFP_KERNEL); +@@ -757,7 +757,7 @@ static int chsc_ioctl_chpd(void __user *user_chpd) + int ret; + + chpd = kzalloc(sizeof(*chpd), GFP_KERNEL); +- scpd_area = (void *)get_zeroed_page(GFP_KERNEL); ++ scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scpd_area || !chpd) { + ret = -ENOMEM; + goto out_free; +@@ -797,7 +797,7 @@ static int chsc_ioctl_dcal(void __user *user_dcal) + u8 data[PAGE_SIZE - 36]; + } __attribute__ ((packed)) *sdcal_area; + +- sdcal_area = (void *)get_zeroed_page(GFP_KERNEL); ++ sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sdcal_area) + return -ENOMEM; + dcal = kzalloc(sizeof(*dcal), GFP_KERNEL); +diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c +index c7894d61306d74..375cbfa31b5373 100644 +--- a/drivers/s390/cio/scm.c ++++ b/drivers/s390/cio/scm.c +@@ -228,7 +228,7 @@ int scm_update_information(void) + size_t num; + int ret; + +- scm_info = (void *)__get_free_page(GFP_KERNEL); ++ scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); + if (!scm_info) + return -ENOMEM; + +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index b5c21368e9..c58c319a72 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -92,3 +92,29 @@ sctp-fix-race-between-sctp_wait_for_connect-and-peel.patch ipv6-fix-possible-infinite-loop-in-rt6_fill_node.patch ipv6-fix-possible-infinite-loop-in-fib6_select_path.patch net-skbuff-fix-pskb_carve-leaking-zcopy-pages.patch +perf-fix-dangling-cgroup-pointer-in-cpuctx.patch +batman-adv-v-stop-ogmv2-on-disabled-interface.patch +batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch +batman-adv-tt-reject-oversized-local-tvlv-buffers.patch +batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch +batman-adv-tvlv-reject-oversized-tvlv-packets.patch +batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch +batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch +s390-cio-restore-gfp_dma-for-chsc-allocation.patch +batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch +batman-adv-tt-fix-toctou-race-for-reported-vlans.patch +batman-adv-tt-avoid-empty-vlan-responses.patch +batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch +mm-page_alloc-clear-page-private-in-free_pages_prepa.patch +media-rc-fix-race-between-unregister-and-urb-irq-cal.patch +media-rc-ttusbir-fix-inverted-error-logic.patch +inet-frags-add-inet_frag_queue_flush.patch +inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch +hid-core-add-printk_ratelimited-variants-to-hid_warn.patch +hid-pass-the-buffer-size-to-hid_report_raw_event.patch +hid-core-introduce-hid_safe_input_report.patch +hid-core-fix-size_t-specifier-in-hid_report_raw_even.patch +drm-i915-psr-add-defininitions-for-intel_wa_register.patch +drm-i915-psr-read-intel-dpcd-workaround-register.patch +drm-dp-add-edp-1.5-bit-definition.patch +drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch diff --git a/queue-6.18/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-6.18/drm-i915-psr-add-defininitions-for-intel_wa_register.patch new file mode 100644 index 0000000000..f96557dc03 --- /dev/null +++ b/queue-6.18/drm-i915-psr-add-defininitions-for-intel_wa_register.patch @@ -0,0 +1,73 @@ +From 29c45fe3bb0ab56bbccb44b120543e7728827716 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 13:06:11 +0300 +Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD + register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream. + +EDP specification says: + +"If either VSC SDP is unable to be transmitted 100 ns before the SU region, +the Source device may optionally transmit the VSC SDP during the prior +video scan line’s HBlank period There is a Intel specific drm dp register +currently containing bits related how TCON can support PSR2 with SDP on +prior line." + +Unfortunately many panels are having problems in implementing this. So +there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to +figure out if this is properly implemented on a panel or if panel doesn't +require that 100 ns delay before the SU region. Here are the definitions in +this custom DPCD address: + +0 = Panel doesn't support SDP on prior line +1 = Panel supports SDP on prior line +2 = Panel doesn't have 100ns requirement +3 = Reserved + +Add definitions for this new register and it's values into new header +intel_dpcd.h. + +v2: add INTEL_DPCD_ prefix to definitions + +Bspec: 74741 +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com +(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h + +diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h +new file mode 100644 +index 00000000000000..4aea5326f2ed48 +--- /dev/null ++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: MIT */ ++/* ++ * Copyright © 2026 Intel Corporation ++ */ ++ ++#ifndef __INTEL_DPCD_H__ ++#define __INTEL_DPCD_H__ ++ ++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS 0x3f0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK REG_GENMASK(1, 0) ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1 0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE 1 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE 2 ++ ++#endif /* __INTEL_DPCD_H__ */ +-- +2.53.0 + diff --git a/queue-6.18/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-6.18/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch new file mode 100644 index 0000000000..c8068142e8 --- /dev/null +++ b/queue-6.18/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch @@ -0,0 +1,85 @@ +From 96a8f7c9a0997dda535ebf9a2d55d5f02716f609 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 13:06:13 +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 + +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: # v5.15+ +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com +(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Jouni Högander +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_psr.c | 28 +++++++++++++++++++++++- + 1 file changed, 27 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index 802a671b2afaaa..eed281b93cd84d 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -29,6 +29,7 @@ + #include + + #include "i915_reg.h" ++#include "i915_utils.h" + #include "intel_alpm.h" + #include "intel_atomic.h" + #include "intel_crtc.h" +@@ -1315,6 +1316,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp, + return true; + } + ++static bool apply_scanline_indication_wa(struct intel_dp *intel_dp, ++ struct intel_crtc_state *crtc_state) ++{ ++ u8 early_scanline_support = intel_dp->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_dp *intel_dp, + struct intel_crtc_state *crtc_state) + { +@@ -1336,7 +1361,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d + return false; + + crtc_state->req_psr2_sdp_prior_scanline = true; +- return true; ++ ++ return apply_scanline_indication_wa(intel_dp, crtc_state); + } + + static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp, +-- +2.53.0 + diff --git a/queue-6.18/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-6.18/drm-i915-psr-read-intel-dpcd-workaround-register.patch new file mode 100644 index 0000000000..2139667f9a --- /dev/null +++ b/queue-6.18/drm-i915-psr-read-intel-dpcd-workaround-register.patch @@ -0,0 +1,67 @@ +From c0493c4811c22fd21f1de77a27f328350822659b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 13:06:12 +0300 +Subject: drm/i915/psr: Read Intel DPCD workaround register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream. + +Read Intel DPCD workaround register and store it into +intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains +only PSR workaround for PSR2 SDP on prior scanline implementation. + +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com +(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_display_types.h | 1 + + drivers/gpu/drm/i915/display/intel_psr.c | 7 +++++++ + 2 files changed, 8 insertions(+) + +diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h +index 39dd7389f1a71a..197bdb6592a7f9 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_types.h ++++ b/drivers/gpu/drm/i915/display/intel_display_types.h +@@ -1710,6 +1710,7 @@ struct intel_dp { + u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE]; + u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE]; + u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]; ++ u8 intel_wa_dpcd; + /* source rates */ + int num_source_rates; + const int *source_rates; +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index 1da20065ea7763..802a671b2afaaa 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -41,6 +41,7 @@ + #include "intel_display_types.h" + #include "intel_dmc.h" + #include "intel_dp.h" ++#include "intel_dpcd.h" + #include "intel_dp_aux.h" + #include "intel_dsb.h" + #include "intel_frontbuffer.h" +@@ -679,6 +680,12 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp) + drm_dbg_kms(display->drm, "PSR2 %ssupported\n", + intel_dp->psr.sink_psr2_support ? "" : "not "); + } ++ ++ if (intel_dp->psr.sink_psr2_support) ++ drm_dp_dpcd_read(&intel_dp->aux, ++ INTEL_DPCD_INTEL_WA_REGISTER_CAPS, ++ &intel_dp->intel_wa_dpcd, ++ sizeof(intel_dp->intel_wa_dpcd)); + } + + void intel_psr_init_dpcd(struct intel_dp *intel_dp) +-- +2.53.0 + diff --git a/queue-6.18/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch b/queue-6.18/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch new file mode 100644 index 0000000000..23dd70e203 --- /dev/null +++ b/queue-6.18/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch @@ -0,0 +1,100 @@ +From 354c235ba8a454fec4ba58a0d86a2cb09746a428 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 26 May 2026 20:50:43 +0000 +Subject: Revert "x86/fpu: Refine and simplify the magic number check during + signal return" + +From: Andrei Vagin + +[ Upstream commit 44eeff9bc467bc7d1fec34fc3f6001f385fe462c ] + +This reverts + + dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return"). + +The aforementioned commit broke applications that construct signal frames in +userspace (such as CRIU and gVisor) if the frame's xstate size is smaller than +the kernel's fpstate->user_size. + +Furthermore, this introduces a critical issue for checkpoint/restore tools +like CRIU. If a process is checkpointed while inside a signal handler, its +stack contains a signal frame formatted according to the source host's xstate +capabilities. + +If that process is later restored on a destination host with larger xstate +capabilities (e.g., a newer CPU with more features enabled, resulting in +a larger fpstate->user_size), the kernel will look for FP_XSTATE_MAGIC2 at the +destination host's larger user_size offset instead of the offset encoded in +the frame's fx_sw->xstate_size. + +This causes the magic2 check to fail, forcing sigreturn to silently fall back +to "FX-only" mode. Upon return from the signal handler, the process's extended +state is reset to initial values instead of being restored, leading to silent +data corruption. + +The aforementioned commit cited + + d877550eaf2d ("x86/fpu: Stop relying on userspace for info to fault in xsave buffer") + +as justification to stop relying on userspace for the magic number check. + +However, these two changes are fundamentally different. The last one only +changed how much memory the kernel ensures is paged-in before running XRSTOR +to prevent an infinite loop. It did not change the signal frame format or how +the layout is validated. + +Reverting this change restores the use of fx_sw->xstate_size for +locating magic2 and restores the necessary sanity checks, ensuring that +the signal frame remains self-describing and portable. + + [ bp: Massage commit message. ] + +Fixes: dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return") +Signed-off-by: Andrei Vagin +Signed-off-by: Borislav Petkov (AMD) +Acked-by: Chang S. Bae +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/all/20260429000623.3356606-1-avagin@google.com +Signed-off-by: Sasha Levin +--- + arch/x86/kernel/fpu/signal.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c +index c3ec2512f2bbe4..20b638c507ca2d 100644 +--- a/arch/x86/kernel/fpu/signal.c ++++ b/arch/x86/kernel/fpu/signal.c +@@ -27,14 +27,19 @@ + static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf, + struct _fpx_sw_bytes *fx_sw) + { ++ int min_xstate_size = sizeof(struct fxregs_state) + ++ sizeof(struct xstate_header); + void __user *fpstate = fxbuf; + unsigned int magic2; + + if (__copy_from_user(fx_sw, &fxbuf->sw_reserved[0], sizeof(*fx_sw))) + return false; + +- /* Check for the first magic field */ +- if (fx_sw->magic1 != FP_XSTATE_MAGIC1) ++ /* Check for the first magic field and other error scenarios. */ ++ if (fx_sw->magic1 != FP_XSTATE_MAGIC1 || ++ fx_sw->xstate_size < min_xstate_size || ++ fx_sw->xstate_size > x86_task_fpu(current)->fpstate->user_size || ++ fx_sw->xstate_size > fx_sw->extended_size) + goto setfx; + + /* +@@ -43,7 +48,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf, + * fpstate layout with out copying the extended state information + * in the memory layout. + */ +- if (__get_user(magic2, (__u32 __user *)(fpstate + x86_task_fpu(current)->fpstate->user_size))) ++ if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size))) + return false; + + if (likely(magic2 == FP_XSTATE_MAGIC2)) +-- +2.53.0 + diff --git a/queue-6.18/s390-cio-restore-gfp_dma-for-chsc-allocation.patch b/queue-6.18/s390-cio-restore-gfp_dma-for-chsc-allocation.patch new file mode 100644 index 0000000000..b508c549ce --- /dev/null +++ b/queue-6.18/s390-cio-restore-gfp_dma-for-chsc-allocation.patch @@ -0,0 +1,152 @@ +From 69d085ec1e03fbcd9c213d08a92a03f1d8bf7fc0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Jun 2026 18:22:21 +0200 +Subject: s390/cio: Restore GFP_DMA for CHSC allocation + +From: Peter Oberparleiter + +[ Upstream commit ea34567db0a6b3a7ce78ba421592344315c8f90e ] + +Re-add GFP_DMA when allocating memory for CHSC control blocks. +On some supported machines, CHSC cannot access memory outside +the DMA zone, causing CHSC command failures. + +Cc: stable@vger.kernel.org +Fixes: a3a64a4def8d ("s390/cio: remove unneeded DMA zone allocation") +Signed-off-by: Peter Oberparleiter +Reviewed-by: Heiko Carstens +Signed-off-by: Alexander Gordeev +[ adjusted context to account for missing commit bf4afc53b77ae ] +Signed-off-by: Peter Oberparleiter +Signed-off-by: Sasha Levin +--- + drivers/s390/cio/chsc.c | 4 ++-- + drivers/s390/cio/chsc_sch.c | 20 ++++++++++---------- + drivers/s390/cio/scm.c | 2 +- + 3 files changed, 13 insertions(+), 13 deletions(-) + +diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c +index 239c92d4ec11e5..b5f6eb18ebadb2 100644 +--- a/drivers/s390/cio/chsc.c ++++ b/drivers/s390/cio/chsc.c +@@ -1143,8 +1143,8 @@ int __init chsc_init(void) + { + int ret; + +- sei_page = (void *)get_zeroed_page(GFP_KERNEL); +- chsc_page = (void *)get_zeroed_page(GFP_KERNEL); ++ sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); ++ chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sei_page || !chsc_page) { + ret = -ENOMEM; + goto out_err; +diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c +index 1e58ee3cc87db1..9131ce3af1b8eb 100644 +--- a/drivers/s390/cio/chsc_sch.c ++++ b/drivers/s390/cio/chsc_sch.c +@@ -293,7 +293,7 @@ static int chsc_ioctl_start(void __user *user_area) + if (!css_general_characteristics.dynio) + /* It makes no sense to try. */ + return -EOPNOTSUPP; +- chsc_area = (void *)get_zeroed_page(GFP_KERNEL); ++ chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); + if (!chsc_area) + return -ENOMEM; + request = kzalloc(sizeof(*request), GFP_KERNEL); +@@ -341,7 +341,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area) + ret = -ENOMEM; + goto out_unlock; + } +- on_close_chsc_area = (void *)get_zeroed_page(GFP_KERNEL); ++ on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); + if (!on_close_chsc_area) { + ret = -ENOMEM; + goto out_free_request; +@@ -393,7 +393,7 @@ static int chsc_ioctl_start_sync(void __user *user_area) + struct chsc_sync_area *chsc_area; + int ret, ccode; + +- chsc_area = (void *)get_zeroed_page(GFP_KERNEL); ++ chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!chsc_area) + return -ENOMEM; + if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) { +@@ -439,7 +439,7 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd) + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *scpcd_area; + +- scpcd_area = (void *)get_zeroed_page(GFP_KERNEL); ++ scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scpcd_area) + return -ENOMEM; + cd = kzalloc(sizeof(*cd), GFP_KERNEL); +@@ -501,7 +501,7 @@ static int chsc_ioctl_info_cu(void __user *user_cd) + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *scucd_area; + +- scucd_area = (void *)get_zeroed_page(GFP_KERNEL); ++ scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scucd_area) + return -ENOMEM; + cd = kzalloc(sizeof(*cd), GFP_KERNEL); +@@ -564,7 +564,7 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud) + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *sscud_area; + +- sscud_area = (void *)get_zeroed_page(GFP_KERNEL); ++ sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sscud_area) + return -ENOMEM; + cud = kzalloc(sizeof(*cud), GFP_KERNEL); +@@ -626,7 +626,7 @@ static int chsc_ioctl_conf_info(void __user *user_ci) + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *sci_area; + +- sci_area = (void *)get_zeroed_page(GFP_KERNEL); ++ sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sci_area) + return -ENOMEM; + ci = kzalloc(sizeof(*ci), GFP_KERNEL); +@@ -697,7 +697,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl) + u32 res; + } __attribute__ ((packed)) *cssids_parm; + +- sccl_area = (void *)get_zeroed_page(GFP_KERNEL); ++ sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccl_area) + return -ENOMEM; + ccl = kzalloc(sizeof(*ccl), GFP_KERNEL); +@@ -757,7 +757,7 @@ static int chsc_ioctl_chpd(void __user *user_chpd) + int ret; + + chpd = kzalloc(sizeof(*chpd), GFP_KERNEL); +- scpd_area = (void *)get_zeroed_page(GFP_KERNEL); ++ scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scpd_area || !chpd) { + ret = -ENOMEM; + goto out_free; +@@ -797,7 +797,7 @@ static int chsc_ioctl_dcal(void __user *user_dcal) + u8 data[PAGE_SIZE - 36]; + } __attribute__ ((packed)) *sdcal_area; + +- sdcal_area = (void *)get_zeroed_page(GFP_KERNEL); ++ sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sdcal_area) + return -ENOMEM; + dcal = kzalloc(sizeof(*dcal), GFP_KERNEL); +diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c +index 9b4da237a0ed52..f4faa38a4b17ef 100644 +--- a/drivers/s390/cio/scm.c ++++ b/drivers/s390/cio/scm.c +@@ -229,7 +229,7 @@ int scm_update_information(void) + size_t num; + int ret; + +- scm_info = (void *)__get_free_page(GFP_KERNEL); ++ scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); + if (!scm_info) + return -ENOMEM; + +-- +2.53.0 + diff --git a/queue-6.18/series b/queue-6.18/series index 982c70b6f8..24e6004d59 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -93,3 +93,9 @@ ipv6-fix-possible-infinite-loop-in-fib6_select_path.patch net-skbuff-fix-pskb_carve-leaking-zcopy-pages.patch media-rc-fix-race-between-unregister-and-urb-irq-cal.patch media-rc-ttusbir-fix-inverted-error-logic.patch +smb-client-validate-the-whole-dacl-before-rewriting-.patch +revert-x86-fpu-refine-and-simplify-the-magic-number-.patch +s390-cio-restore-gfp_dma-for-chsc-allocation.patch +drm-i915-psr-add-defininitions-for-intel_wa_register.patch +drm-i915-psr-read-intel-dpcd-workaround-register.patch +drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch diff --git a/queue-6.18/smb-client-validate-the-whole-dacl-before-rewriting-.patch b/queue-6.18/smb-client-validate-the-whole-dacl-before-rewriting-.patch new file mode 100644 index 0000000000..f3e6462e66 --- /dev/null +++ b/queue-6.18/smb-client-validate-the-whole-dacl-before-rewriting-.patch @@ -0,0 +1,229 @@ +From b7395002e94d09ad2a56106bead5544a4cfddba2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Apr 2026 20:11:31 -0400 +Subject: smb: client: validate the whole DACL before rewriting it in cifsacl + +From: Michael Bommarito + +[ Upstream commit 0a8cf165566ba55a39fd0f4de172119dd646d39a ] + +build_sec_desc() and id_mode_to_cifs_acl() derive a DACL pointer from a +server-supplied dacloffset and then use the incoming ACL to rebuild the +chmod/chown security descriptor. + +The original fix only checked that the struct smb_acl header fits before +reading dacl_ptr->size or dacl_ptr->num_aces. That avoids the immediate +header-field OOB read, but the rewrite helpers still walk ACEs based on +pdacl->num_aces with no structural validation of the incoming DACL body. + +A malicious server can return a truncated DACL that still contains a +header, claims one or more ACEs, and then drive +replace_sids_and_copy_aces() or set_chmod_dacl() past the validated +extent while they compare or copy attacker-controlled ACEs. + +Factor the DACL structural checks into validate_dacl(), extend them to +validate each ACE against the DACL bounds, and use the shared validator +before the chmod/chown rebuild paths. parse_dacl() reuses the same +validator so the read-side parser and write-side rewrite paths agree on +what constitutes a well-formed incoming DACL. + +Fixes: bc3e9dd9d104 ("cifs: Change SIDs in ACEs while transferring file ownership.") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-6 +Assisted-by: Codex:gpt-5-4 +Signed-off-by: Michael Bommarito +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/client/cifsacl.c | 116 +++++++++++++++++++++++++++++----------- + 1 file changed, 85 insertions(+), 31 deletions(-) + +diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c +index e2948835587094..d17912beb28ec5 100644 +--- a/fs/smb/client/cifsacl.c ++++ b/fs/smb/client/cifsacl.c +@@ -757,6 +757,77 @@ static void dump_ace(struct smb_ace *pace, char *end_of_acl) + } + #endif + ++static int validate_dacl(struct smb_acl *pdacl, char *end_of_acl) ++{ ++ int i, ace_hdr_size, ace_size, min_ace_size; ++ u16 dacl_size, num_aces; ++ char *acl_base, *end_of_dacl; ++ struct smb_ace *pace; ++ ++ if (!pdacl) ++ return 0; ++ ++ if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl)) { ++ cifs_dbg(VFS, "ACL too small to parse DACL\n"); ++ return -EINVAL; ++ } ++ ++ dacl_size = le16_to_cpu(pdacl->size); ++ if (dacl_size < sizeof(struct smb_acl) || ++ end_of_acl < (char *)pdacl + dacl_size) { ++ cifs_dbg(VFS, "ACL too small to parse DACL\n"); ++ return -EINVAL; ++ } ++ ++ num_aces = le16_to_cpu(pdacl->num_aces); ++ if (!num_aces) ++ return 0; ++ ++ ace_hdr_size = offsetof(struct smb_ace, sid) + ++ offsetof(struct smb_sid, sub_auth); ++ min_ace_size = ace_hdr_size + sizeof(__le32); ++ if (num_aces > (dacl_size - sizeof(struct smb_acl)) / min_ace_size) { ++ cifs_dbg(VFS, "ACL too small to parse DACL\n"); ++ return -EINVAL; ++ } ++ ++ end_of_dacl = (char *)pdacl + dacl_size; ++ acl_base = (char *)pdacl; ++ ace_size = sizeof(struct smb_acl); ++ ++ for (i = 0; i < num_aces; ++i) { ++ if (end_of_dacl - acl_base < ace_size) { ++ cifs_dbg(VFS, "ACL too small to parse ACE\n"); ++ return -EINVAL; ++ } ++ ++ pace = (struct smb_ace *)(acl_base + ace_size); ++ acl_base = (char *)pace; ++ ++ if (end_of_dacl - acl_base < ace_hdr_size || ++ pace->sid.num_subauth == 0 || ++ pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) { ++ cifs_dbg(VFS, "ACL too small to parse ACE\n"); ++ return -EINVAL; ++ } ++ ++ ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth; ++ if (end_of_dacl - acl_base < ace_size || ++ le16_to_cpu(pace->size) < ace_size) { ++ cifs_dbg(VFS, "ACL too small to parse ACE\n"); ++ return -EINVAL; ++ } ++ ++ ace_size = le16_to_cpu(pace->size); ++ if (end_of_dacl - acl_base < ace_size) { ++ cifs_dbg(VFS, "ACL too small to parse ACE\n"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ + static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, + struct smb_sid *pownersid, struct smb_sid *pgrpsid, + struct cifs_fattr *fattr, bool mode_from_special_sid) +@@ -764,7 +835,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, + int i; + u16 num_aces = 0; + int acl_size; +- char *acl_base; ++ char *acl_base, *end_of_dacl; + struct smb_ace **ppace; + + /* BB need to add parm so we can store the SID BB */ +@@ -776,12 +847,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, + return; + } + +- /* validate that we do not go past end of acl */ +- if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || +- end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { +- cifs_dbg(VFS, "ACL too small to parse DACL\n"); ++ if (validate_dacl(pdacl, end_of_acl)) + return; +- } + + cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), +@@ -792,6 +859,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, + user/group/other have no permissions */ + fattr->cf_mode &= ~(0777); + ++ end_of_dacl = (char *)pdacl + le16_to_cpu(pdacl->size); + acl_base = (char *)pdacl; + acl_size = sizeof(struct smb_acl); + +@@ -799,36 +867,16 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, + if (num_aces > 0) { + umode_t denied_mode = 0; + +- if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) / +- (offsetof(struct smb_ace, sid) + +- offsetof(struct smb_sid, sub_auth) + sizeof(__le16))) +- return; +- + ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), + GFP_KERNEL); + if (!ppace) + return; + + for (i = 0; i < num_aces; ++i) { +- if (end_of_acl - acl_base < acl_size) +- break; +- + ppace[i] = (struct smb_ace *) (acl_base + acl_size); +- acl_base = (char *)ppace[i]; +- acl_size = offsetof(struct smb_ace, sid) + +- offsetof(struct smb_sid, sub_auth); +- +- if (end_of_acl - acl_base < acl_size || +- ppace[i]->sid.num_subauth == 0 || +- ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || +- (end_of_acl - acl_base < +- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || +- (le16_to_cpu(ppace[i]->size) < +- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) +- break; + + #ifdef CONFIG_CIFS_DEBUG2 +- dump_ace(ppace[i], end_of_acl); ++ dump_ace(ppace[i], end_of_dacl); + #endif + if (mode_from_special_sid && + ppace[i]->sid.num_subauth >= 3 && +@@ -871,6 +919,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, + (void *)ppace[i], + sizeof(struct smb_ace)); */ + ++ acl_base = (char *)ppace[i]; + acl_size = le16_to_cpu(ppace[i]->size); + } + +@@ -1316,10 +1365,9 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd, + } + + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); +- if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) { +- cifs_dbg(VFS, "Server returned illegal ACL size\n"); +- return -EINVAL; +- } ++ rc = validate_dacl(dacl_ptr, end_of_acl); ++ if (rc) ++ return rc; + } + + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + +@@ -1698,6 +1746,12 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, + } + + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); ++ rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen); ++ if (rc) { ++ kfree(pntsd); ++ cifs_put_tlink(tlink); ++ return rc; ++ } + if (mode_from_sid) + nsecdesclen += + le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace); +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/queue-6.6/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch new file mode 100644 index 0000000000..c822500ef2 --- /dev/null +++ b/queue-6.6/batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch @@ -0,0 +1,232 @@ +From 08dda28b5d0acfaa0cc0d1c1b06485908972dd54 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:19:23 +0200 +Subject: batman-adv: bla: avoid double decrement of bla.num_requests + +From: Sven Eckelmann + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++-------- + net/batman-adv/soft-interface.c | 1 + + net/batman-adv/types.h | 39 ++++++++++++++++---- + 3 files changed, 67 insertions(+), 24 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index 76d8c91c156a3b..cfb1eb25c6ac4d 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -516,8 +516,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, + 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); +@@ -546,9 +546,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, + 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; +@@ -651,10 +655,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw) + 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); + } + + /** +@@ -875,10 +881,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, + /* 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); +@@ -1257,9 +1265,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int 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); +@@ -1510,7 +1522,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) + 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. + * +@@ -1519,14 +1531,21 @@ static void batadv_bla_periodic_work(struct work_struct *work) + * 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(); + } +diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c +index 1bf1232a4f7595..680f729b3b0228 100644 +--- a/net/batman-adv/soft-interface.c ++++ b/net/batman-adv/soft-interface.c +@@ -783,6 +783,7 @@ static int batadv_softif_init_late(struct net_device *dev) + 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); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index d333f9dfec55d0..2dfdc78d4ded99 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1027,6 +1027,12 @@ struct batadv_priv_bla { + /** @num_requests: number of bla requests in flight */ + 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 + */ +@@ -1755,6 +1761,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 + */ +@@ -1780,16 +1807,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; +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch b/queue-6.6/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch new file mode 100644 index 0000000000..df7a471a8c --- /dev/null +++ b/queue-6.6/batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch @@ -0,0 +1,53 @@ +From 4674fe10e8cd7155139266fd6797e45a0262fe45 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:20:23 +0200 +Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface + +From: Sven Eckelmann + +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 +Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816 +[ switch to old "mesh_iface" name "soft_iface" ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index f614709e6cda74..76d8c91c156a3b 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -356,12 +356,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac, + sizeof(local_claim_dest)); + local_claim_dest.type = claimtype; + +- soft_iface = primary_if->soft_iface; ++ soft_iface = READ_ONCE(primary_if->soft_iface); ++ if (!soft_iface) ++ goto out; + + skb = arp_create(ARPOP_REPLY, ETH_P_ARP, + /* IP DST: 0.0.0.0 */ + zeroip, +- primary_if->soft_iface, ++ soft_iface, + /* IP SRC: 0.0.0.0 */ + zeroip, + /* Ethernet DST: Broadcast */ +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch b/queue-6.6/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch new file mode 100644 index 0000000000..dd756abbcc --- /dev/null +++ b/queue-6.6/batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch @@ -0,0 +1,230 @@ +From 483fd0f4f26b416d40532d4415f07918adafaec1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:18:57 +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 + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++---------- + net/batman-adv/types.h | 3 ++ + 2 files changed, 60 insertions(+), 19 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index 42b687c1a76807..b37c9fb178ae50 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -223,6 +223,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface) + 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) +@@ -527,8 +529,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, + * @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, +@@ -552,13 +556,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + + 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; +@@ -580,6 +584,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + 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 */ +@@ -609,8 +615,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr, + * @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, +@@ -662,14 +670,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, + 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; + } + } + +@@ -781,6 +791,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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); +@@ -809,11 +821,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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; +@@ -835,8 +844,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + /* 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; + } + +@@ -851,15 +863,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + 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); + } + +@@ -874,6 +899,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) + 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 +@@ -2277,6 +2313,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1, + + 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); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 957a70854157dc..d333f9dfec55d0 100644 +--- 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; + }; +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch b/queue-6.6/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch new file mode 100644 index 0000000000..7c51995bb6 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch @@ -0,0 +1,196 @@ +From 55f0d0967fc724139b51a79017aef58c8a82b36c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:18:27 +0200 +Subject: batman-adv: tp_meter: avoid role confusion in tp_list + +From: Sven Eckelmann + +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 +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 59 ++++++++++++++++++++++++--------------- + 1 file changed, 36 insertions(+), 23 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 04a83d6be45bc0..bc3dc377f0bfd0 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -255,6 +255,7 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason, + * batadv_tp_list_find() - find a tp_vars object in the global list + * @bat_priv: the bat priv with all the soft 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_notify(enum batadv_tp_meter_reason reason, + * 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_list_find(struct batadv_priv *bat_priv, + 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 +@@ -286,12 +291,33 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, + return tp_vars; + } + ++/** ++ * 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 soft 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_list_find(struct batadv_priv *bat_priv, + */ + 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 batadv_priv *bat_priv, const u8 *dst, + 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 +@@ -671,13 +700,10 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + + /* 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; + +@@ -986,10 +1012,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, + 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, +@@ -1110,18 +1134,14 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, + 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); +@@ -1377,7 +1397,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_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; + +@@ -1448,7 +1468,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + } 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", +@@ -1457,13 +1477,6 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + } + +- 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 +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch b/queue-6.6/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch new file mode 100644 index 0000000000..6239950395 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch @@ -0,0 +1,60 @@ +From cced89522213d7463f127b96ffb4aaa475453363 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 22:23:16 +0200 +Subject: batman-adv: tp_meter: directly shut down timer on cleanup + +From: Sven Eckelmann + +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") +[ adapt pre-hunk to old del_timer* names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index bc3dc377f0bfd0..dfc3374549921f 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -430,13 +430,7 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars) + batadv_tp_list_detach(tp_vars); + + /* kill the timer and remove its reference */ +- del_timer_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 +- * del_timer(), it would not re-arm itself once again because the status +- * is OFF now +- */ +- del_timer(&tp_vars->timer); ++ timer_shutdown_sync(&tp_vars->timer); + batadv_tp_vars_put(tp_vars); + } + +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tt-avoid-empty-vlan-responses.patch b/queue-6.6/batman-adv-tt-avoid-empty-vlan-responses.patch new file mode 100644 index 0000000000..cab2fc5eb1 --- /dev/null +++ b/queue-6.6/batman-adv-tt-avoid-empty-vlan-responses.patch @@ -0,0 +1,91 @@ +From 2bbc0178e9bc0d94a1d1123ba06a4374950eb064 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:15:14 +0200 +Subject: batman-adv: tt: avoid empty VLAN responses + +From: Sven Eckelmann + +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") +[ Context, drop flex array dependency ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 21 ++++++++++++++++++--- + 1 file changed, 18 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index d4cebe122e528a..4045ddefc29b47 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -843,17 +843,19 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + 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 = sizeof(**tt_data); +@@ -861,7 +863,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + + /* 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; +@@ -882,14 +884,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ 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 = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_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; + +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch b/queue-6.6/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch new file mode 100644 index 0000000000..47deb3338c --- /dev/null +++ b/queue-6.6/batman-adv-tt-fix-toctou-race-for-reported-vlans.patch @@ -0,0 +1,78 @@ +From 8cbc0d53ee0c7e4f066543233ea7fee557a01b87 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 22:59:34 +0200 +Subject: batman-adv: tt: fix TOCTOU race for reported vlans + +From: Sven Eckelmann + +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") +[ Context, drop flex array dependency ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 8ffebece03c529..d4cebe122e528a 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -934,11 +934,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_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 = sizeof(**tt_data); +@@ -964,6 +961,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ num_vlan = 0; + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); + if (vlan_entries < 1) +@@ -974,8 +972,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + tt_vlan->reserved = 0; + + tt_vlan++; ++ num_vlan++; + } + ++ /* recalculate in case number of VLANs reduced */ ++ change_offset = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_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; + +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch b/queue-6.6/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch new file mode 100644 index 0000000000..ef3538eed6 --- /dev/null +++ b/queue-6.6/batman-adv-tt-reject-oversized-local-tvlv-buffers.patch @@ -0,0 +1,62 @@ +From 6216a8dca3fbf71cd1d81c6bf1022591cecae8ae Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:13:27 +0200 +Subject: batman-adv: tt: reject oversized local TVLV buffers + +From: Sven Eckelmann + +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") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index d830ccf016697b..8ffebece03c529 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -924,12 +924,12 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + { + struct batadv_tvlv_tt_vlan_data *tt_vlan; + struct batadv_softif_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->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { +@@ -948,8 +948,10 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + 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) { +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch b/queue-6.6/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch new file mode 100644 index 0000000000..696ee1e1e5 --- /dev/null +++ b/queue-6.6/batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch @@ -0,0 +1,196 @@ +From adfa8ec97ffd3a2d2101c893a08e02e6376bd680 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:48:30 +0200 +Subject: batman-adv: tvlv: abort OGM send on tvlv append failure + +From: Sven Eckelmann + +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") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + 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(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index 748188d3b878b2..42b687c1a76807 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -781,6 +781,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + u32 seqno; + u16 tvlv_len = 0; + unsigned long send_time; ++ int ret; + + lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); + +@@ -804,9 +805,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) + * 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); +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 310248a5812c49..8cfc3944dcfd52 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -271,9 +271,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + struct batadv_hard_iface *hard_iface; + struct batadv_ogm2_packet *ogm_packet; + struct sk_buff *skb, *skb_tmp; +- unsigned char *ogm_buff; +- int ogm_buff_len; +- u16 tvlv_len = 0; ++ unsigned char **ogm_buff; ++ int *ogm_buff_len; ++ u16 tvlv_len; + int ret; + + lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex); +@@ -281,25 +281,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + 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)); +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 2a583215d439bd..76c6e0599694c7 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -8,6 +8,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff, + * 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(struct batadv_priv *bat_priv, + 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(struct batadv_priv *bat_priv, + + 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(struct batadv_priv *bat_priv, + + end: + spin_unlock_bh(&bat_priv->tvlv.container_list_lock); +- return tvlv_value_len; ++ ++ return tvlv_len_ret; + } + + /** +diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h +index e5697230d99173..f96f6b3f44a001 100644 +--- 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, +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tvlv-reject-oversized-tvlv-packets.patch b/queue-6.6/batman-adv-tvlv-reject-oversized-tvlv-packets.patch new file mode 100644 index 0000000000..4895c2eb6c --- /dev/null +++ b/queue-6.6/batman-adv-tvlv-reject-oversized-tvlv-packets.patch @@ -0,0 +1,85 @@ +From 3fb3cc389028233ac522449e7fc8f0ecf0c986eb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 20:17:56 +0200 +Subject: batman-adv: tvlv: reject oversized TVLV packets + +From: Sven Eckelmann + +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 +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 76c6e0599694c7..8d6b017c433cc9 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version) + * + * 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(struct batadv_priv *bat_priv, + { + 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); +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-v-stop-ogmv2-on-disabled-interface.patch b/queue-6.6/batman-adv-v-stop-ogmv2-on-disabled-interface.patch new file mode 100644 index 0000000000..955e05119d --- /dev/null +++ b/queue-6.6/batman-adv-v-stop-ogmv2-on-disabled-interface.patch @@ -0,0 +1,149 @@ +From efaac38d454c9d628bf4e6794e3767548c67d249 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:29:56 +0200 +Subject: batman-adv: v: stop OGMv2 on disabled interface + +From: Sven Eckelmann + +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 +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +[ switch to old "mesh_iface" name "soft_iface" ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------ + 1 file changed, 21 insertions(+), 12 deletions(-) + +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 8f89ffe6020ced..310248a5812c49 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv) + + /** + * 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->soft_iface); +- + if (hard_iface->if_status != BATADV_IF_ACTIVE) { + kfree_skb(skb); + return; +@@ -189,6 +189,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) + + /** + * 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 +@@ -198,7 +199,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) + * + * 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; +@@ -228,27 +230,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface) + 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->soft_iface); ++ if (hard_iface->soft_iface != bat_priv->soft_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); +@@ -347,7 +354,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv) + 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(); +@@ -387,12 +394,14 @@ void batadv_v_ogm_aggr_work(struct 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->soft_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); +@@ -582,7 +591,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv, + 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); +-- +2.53.0 + diff --git a/queue-6.6/drm-dp-add-edp-1.5-bit-definition.patch b/queue-6.6/drm-dp-add-edp-1.5-bit-definition.patch new file mode 100644 index 0000000000..0a19b64278 --- /dev/null +++ b/queue-6.6/drm-dp-add-edp-1.5-bit-definition.patch @@ -0,0 +1,41 @@ +From 33966379ae9feb7cc7a5b0daa73547275a5dc158 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 12:06:35 +0300 +Subject: drm/dp: Add eDP 1.5 bit definition +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Suraj Kandpal + +commit 5dfc37a6b77bf6beedbd30d70184b54e1a08ccac upstream. + +Add the eDP revision bit value for 1.5. + +Spec: eDPv1.5 Table 16-5 +Signed-off-by: Suraj Kandpal +Reviewed-by: Arun R Murthy +Tested-by: Ben Kao +Acked-by: Maarten Lankhorst +Link: https://patchwork.freedesktop.org/patch/msgid/20250206063253.2827017-2-suraj.kandpal@intel.com +Signed-off-by: Jouni Högander +Signed-off-by: Sasha Levin +--- + include/drm/display/drm_dp.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h +index e69cece404b3cf..861527bf9ccb7f 100644 +--- a/include/drm/display/drm_dp.h ++++ b/include/drm/display/drm_dp.h +@@ -959,6 +959,7 @@ + # define DP_EDP_14 0x03 + # define DP_EDP_14a 0x04 /* eDP 1.4a */ + # define DP_EDP_14b 0x05 /* eDP 1.4b */ ++# define DP_EDP_15 0x06 /* eDP 1.5 */ + + #define DP_EDP_GENERAL_CAP_1 0x701 + # define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP (1 << 0) +-- +2.53.0 + diff --git a/queue-6.6/drm-fbcon-vga_switcheroo-avoid-race-condition-in-fbc.patch b/queue-6.6/drm-fbcon-vga_switcheroo-avoid-race-condition-in-fbc.patch new file mode 100644 index 0000000000..a79aba60f3 --- /dev/null +++ b/queue-6.6/drm-fbcon-vga_switcheroo-avoid-race-condition-in-fbc.patch @@ -0,0 +1,144 @@ +From 5eaa0004c1a4071fbf281711d29ed9478820f7e6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:18:17 +0800 +Subject: drm, fbcon, vga_switcheroo: Avoid race condition in fbcon setup + +From: Thomas Zimmermann + +[ Upstream commit eb76d0f5553575599561010f24c277cc5b31d003 ] + +Protect vga_switcheroo_client_fb_set() with console lock. Avoids OOB +access in fbcon_remap_all(). Without holding the console lock the call +races with switching outputs. + +VGA switcheroo calls fbcon_remap_all() when switching clients. The fbcon +function uses struct fb_info.node, which is set by register_framebuffer(). +As the fb-helper code currently sets up VGA switcheroo before registering +the framebuffer, the value of node is -1 and therefore not a legal value. +For example, fbcon uses the value within set_con2fb_map() [1] as an index +into an array. + +Moving vga_switcheroo_client_fb_set() after register_framebuffer() can +result in VGA switching that does not switch fbcon correctly. + +Therefore move vga_switcheroo_client_fb_set() under fbcon_fb_registered(), +which already holds the console lock. Fbdev calls fbcon_fb_registered() +from within register_framebuffer(). Serializes the helper with VGA +switcheroo's call to fbcon_remap_all(). + +Although vga_switcheroo_client_fb_set() takes an instance of struct fb_info +as parameter, it really only needs the contained fbcon state. Moving the +call to fbcon initialization is therefore cleaner than before. Only amdgpu, +i915, nouveau and radeon support vga_switcheroo. For all other drivers, +this change does nothing. + +Signed-off-by: Thomas Zimmermann +Link: https://elixir.bootlin.com/linux/v6.17/source/drivers/video/fbdev/core/fbcon.c#L2942 # [1] +Fixes: 6a9ee8af344e ("vga_switcheroo: initial implementation (v15)") +Acked-by: Javier Martinez Canillas +Acked-by: Alex Deucher +Cc: dri-devel@lists.freedesktop.org +Cc: nouveau@lists.freedesktop.org +Cc: amd-gfx@lists.freedesktop.org +Cc: linux-fbdev@vger.kernel.org +Cc: # v2.6.34+ +Link: https://patch.msgid.link/20251105161549.98836-1-tzimmermann@suse.de +[ Minor context conflict resolved. ] +Signed-off-by: Wenshan Lan +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/drm_fb_helper.c | 14 -------------- + drivers/video/fbdev/core/fbcon.c | 9 +++++++++ + 2 files changed, 9 insertions(+), 14 deletions(-) + +diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c +index eee7b56d441f71..9691c93f19a0dd 100644 +--- a/drivers/gpu/drm/drm_fb_helper.c ++++ b/drivers/gpu/drm/drm_fb_helper.c +@@ -30,9 +30,7 @@ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + + #include +-#include + #include +-#include + + #include + #include +@@ -575,11 +573,6 @@ EXPORT_SYMBOL(drm_fb_helper_release_info); + */ + void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper) + { +- struct fb_info *info = fb_helper->info; +- struct device *dev = info->device; +- +- if (dev_is_pci(dev)) +- vga_switcheroo_client_fb_set(to_pci_dev(dev), NULL); + unregister_framebuffer(fb_helper->info); + } + EXPORT_SYMBOL(drm_fb_helper_unregister_info); +@@ -1673,7 +1666,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper) + { + struct drm_client_dev *client = &fb_helper->client; + struct drm_fb_helper_surface_size sizes; +- struct fb_info *info; + int ret; + + ret = drm_fb_helper_find_sizes(fb_helper, &sizes); +@@ -1691,12 +1683,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper) + + strcpy(fb_helper->fb->comm, "[fbcon]"); + +- info = fb_helper->info; +- +- /* Set the fb info for vgaswitcheroo clients. Does nothing otherwise. */ +- if (dev_is_pci(info->device)) +- vga_switcheroo_client_fb_set(to_pci_dev(info->device), info); +- + return 0; + } + +diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c +index 703c4e851612c8..d1ac4e45eea68e 100644 +--- a/drivers/video/fbdev/core/fbcon.c ++++ b/drivers/video/fbdev/core/fbcon.c +@@ -64,6 +64,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -75,6 +76,7 @@ + #include + #include /* For counting font checksums */ + #include ++#include + #include + + #include "fbcon.h" +@@ -2914,6 +2916,9 @@ void fbcon_fb_unregistered(struct fb_info *info) + + console_lock(); + ++ if (info->device && dev_is_pci(info->device)) ++ vga_switcheroo_client_fb_set(to_pci_dev(info->device), NULL); ++ + fbcon_registered_fb[info->node] = NULL; + fbcon_num_registered_fb--; + +@@ -3047,6 +3052,10 @@ static int do_fb_registered(struct fb_info *info) + } + } + ++ /* Set the fb info for vga_switcheroo clients. Does nothing otherwise. */ ++ if (info->device && dev_is_pci(info->device)) ++ vga_switcheroo_client_fb_set(to_pci_dev(info->device), info); ++ + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.6/drm-fbdev-helper-set-and-clear-vga-switcheroo-client.patch b/queue-6.6/drm-fbdev-helper-set-and-clear-vga-switcheroo-client.patch new file mode 100644 index 0000000000..de3d982e75 --- /dev/null +++ b/queue-6.6/drm-fbdev-helper-set-and-clear-vga-switcheroo-client.patch @@ -0,0 +1,89 @@ +From d12b29d39cbc62bb933b2904b7c43cc20a0f6ea3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 21:18:16 +0800 +Subject: drm/fbdev-helper: Set and clear VGA switcheroo client from fb_info + +From: Thomas Zimmermann + +[ Upstream commit 02257549daf7ff839e2be6d4f3cac975e522fd7a ] + +Call vga_switcheroo_client_fb_set() with the PCI device from the +instance of struct fb_info. All fbdev clients now run these calls. +For non-PCI devices or drivers without vga-switcheroo, this does +nothing. For i915 and radeon, it allows these drivers to use a +common fbdev client. + +The device is the same as the one stored in struct drm_client and +struct drm_fb_helper, so there is no difference in behavior. Some +NULL-pointer checks are being removed, where those pointers cannot +be NULL. + +v4: +- clarify call semantics for drm_fb_helper_unregister_info() (Javier) + +Signed-off-by: Thomas Zimmermann +Reviewed-by: Javier Martinez Canillas +Link: https://patchwork.freedesktop.org/patch/msgid/20240924071734.98201-3-tzimmermann@suse.de +[ The variable 'dev' in the function drm_fb_helper_single_fb_probe() is +unused; remove it in v6.6. ] +Signed-off-by: Wenshan Lan +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/drm_fb_helper.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c +index b507c1c008a3e9..eee7b56d441f71 100644 +--- a/drivers/gpu/drm/drm_fb_helper.c ++++ b/drivers/gpu/drm/drm_fb_helper.c +@@ -567,7 +567,7 @@ EXPORT_SYMBOL(drm_fb_helper_release_info); + + /** + * drm_fb_helper_unregister_info - unregister fb_info framebuffer device +- * @fb_helper: driver-allocated fbdev helper, can be NULL ++ * @fb_helper: driver-allocated fbdev helper, must not be NULL + * + * A wrapper around unregister_framebuffer, to release the fb_info + * framebuffer device. This must be called before releasing all resources for +@@ -575,8 +575,12 @@ EXPORT_SYMBOL(drm_fb_helper_release_info); + */ + void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper) + { +- if (fb_helper && fb_helper->info) +- unregister_framebuffer(fb_helper->info); ++ struct fb_info *info = fb_helper->info; ++ struct device *dev = info->device; ++ ++ if (dev_is_pci(dev)) ++ vga_switcheroo_client_fb_set(to_pci_dev(dev), NULL); ++ unregister_framebuffer(fb_helper->info); + } + EXPORT_SYMBOL(drm_fb_helper_unregister_info); + +@@ -1668,8 +1672,8 @@ static int drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper, + static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper) + { + struct drm_client_dev *client = &fb_helper->client; +- struct drm_device *dev = fb_helper->dev; + struct drm_fb_helper_surface_size sizes; ++ struct fb_info *info; + int ret; + + ret = drm_fb_helper_find_sizes(fb_helper, &sizes); +@@ -1687,9 +1691,11 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper) + + strcpy(fb_helper->fb->comm, "[fbcon]"); + ++ info = fb_helper->info; ++ + /* Set the fb info for vgaswitcheroo clients. Does nothing otherwise. */ +- if (dev_is_pci(dev->dev)) +- vga_switcheroo_client_fb_set(to_pci_dev(dev->dev), fb_helper->info); ++ if (dev_is_pci(info->device)) ++ vga_switcheroo_client_fb_set(to_pci_dev(info->device), info); + + return 0; + } +-- +2.53.0 + diff --git a/queue-6.6/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-6.6/drm-i915-psr-add-defininitions-for-intel_wa_register.patch new file mode 100644 index 0000000000..969cc98eb6 --- /dev/null +++ b/queue-6.6/drm-i915-psr-add-defininitions-for-intel_wa_register.patch @@ -0,0 +1,73 @@ +From fe9c93a9f24bfc9ed637d9fad967d5517753f154 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 12:06:33 +0300 +Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD + register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream. + +EDP specification says: + +"If either VSC SDP is unable to be transmitted 100 ns before the SU region, +the Source device may optionally transmit the VSC SDP during the prior +video scan line’s HBlank period There is a Intel specific drm dp register +currently containing bits related how TCON can support PSR2 with SDP on +prior line." + +Unfortunately many panels are having problems in implementing this. So +there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to +figure out if this is properly implemented on a panel or if panel doesn't +require that 100 ns delay before the SU region. Here are the definitions in +this custom DPCD address: + +0 = Panel doesn't support SDP on prior line +1 = Panel supports SDP on prior line +2 = Panel doesn't have 100ns requirement +3 = Reserved + +Add definitions for this new register and it's values into new header +intel_dpcd.h. + +v2: add INTEL_DPCD_ prefix to definitions + +Bspec: 74741 +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com +(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h + +diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h +new file mode 100644 +index 00000000000000..4aea5326f2ed48 +--- /dev/null ++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: MIT */ ++/* ++ * Copyright © 2026 Intel Corporation ++ */ ++ ++#ifndef __INTEL_DPCD_H__ ++#define __INTEL_DPCD_H__ ++ ++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS 0x3f0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK REG_GENMASK(1, 0) ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1 0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE 1 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE 2 ++ ++#endif /* __INTEL_DPCD_H__ */ +-- +2.53.0 + diff --git a/queue-6.6/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-6.6/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch new file mode 100644 index 0000000000..e84fbcdb86 --- /dev/null +++ b/queue-6.6/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch @@ -0,0 +1,77 @@ +From d04df89f279f3c07dfec4c47955fe07ad65fea95 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 12:06:36 +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 + +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: # v5.15+ +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com +(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Jouni Högander +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_psr.c | 27 +++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index 49842f7877f435..416686a6566bab 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -1007,6 +1007,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp, + return true; + } + ++static bool apply_scanline_indication_wa(struct intel_dp *intel_dp, ++ struct intel_crtc_state *crtc_state) ++{ ++ u8 early_scanline_support = intel_dp->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_dp *intel_dp, + struct intel_crtc_state *crtc_state) + { +@@ -1028,7 +1052,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d + return false; + + crtc_state->req_psr2_sdp_prior_scanline = true; +- return true; ++ ++ return apply_scanline_indication_wa(intel_dp, crtc_state); + } + + static bool _compute_psr2_wake_times(struct intel_dp *intel_dp, +-- +2.53.0 + diff --git a/queue-6.6/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-6.6/drm-i915-psr-read-intel-dpcd-workaround-register.patch new file mode 100644 index 0000000000..8af80b9bb4 --- /dev/null +++ b/queue-6.6/drm-i915-psr-read-intel-dpcd-workaround-register.patch @@ -0,0 +1,67 @@ +From dd0d64dd0c92a33edbb0c7b03ebf6863cd04e8ae Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 12:06:34 +0300 +Subject: drm/i915/psr: Read Intel DPCD workaround register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream. + +Read Intel DPCD workaround register and store it into +intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains +only PSR workaround for PSR2 SDP on prior scanline implementation. + +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com +(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_display_types.h | 1 + + drivers/gpu/drm/i915/display/intel_psr.c | 7 +++++++ + 2 files changed, 8 insertions(+) + +diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h +index 1c23b186aff20c..3a228cfb1550f2 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_types.h ++++ b/drivers/gpu/drm/i915/display/intel_display_types.h +@@ -1719,6 +1719,7 @@ struct intel_dp { + u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE]; + u8 fec_capable; + u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]; ++ u8 intel_wa_dpcd; + /* source rates */ + int num_source_rates; + const int *source_rates; +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index b0818dc8480edb..49842f7877f435 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -31,6 +31,7 @@ + #include "intel_de.h" + #include "intel_display_types.h" + #include "intel_dp.h" ++#include "intel_dpcd.h" + #include "intel_dp_aux.h" + #include "intel_hdmi.h" + #include "intel_psr.h" +@@ -528,6 +529,12 @@ void intel_psr_init_dpcd(struct intel_dp *intel_dp) + intel_dp_get_su_granularity(intel_dp); + } + } ++ ++ if (intel_dp->psr.sink_psr2_support) ++ drm_dp_dpcd_read(&intel_dp->aux, ++ INTEL_DPCD_INTEL_WA_REGISTER_CAPS, ++ &intel_dp->intel_wa_dpcd, ++ sizeof(intel_dp->intel_wa_dpcd)); + } + + static void hsw_psr_setup_aux(struct intel_dp *intel_dp) +-- +2.53.0 + diff --git a/queue-6.6/inet-frags-add-inet_frag_queue_flush.patch b/queue-6.6/inet-frags-add-inet_frag_queue_flush.patch new file mode 100644 index 0000000000..d5771da292 --- /dev/null +++ b/queue-6.6/inet-frags-add-inet_frag_queue_flush.patch @@ -0,0 +1,98 @@ +From 4193c8738c7345b404b7e306e9011d8a6d70556e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 18:24:08 +0800 +Subject: inet: frags: add inet_frag_queue_flush() + +From: Jakub Kicinski + +[ Upstream commit 1231eec6994be29d6bb5c303dfa54731ed9fc0e6 ] + +Instead of exporting inet_frag_rbtree_purge() which requires that +caller takes care of memory accounting, add a new helper. We will +need to call it from a few places in the next patch. + +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20251207010942.1672972-3-kuba@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Rajani Kantha <681739313@139.com> +Signed-off-by: Sasha Levin +--- + include/net/inet_frag.h | 5 ++--- + net/ipv4/inet_fragment.c | 15 ++++++++++++--- + net/ipv4/ip_fragment.c | 6 +----- + 3 files changed, 15 insertions(+), 11 deletions(-) + +diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h +index 5af6eb14c5db15..94edc0e130d2c4 100644 +--- a/include/net/inet_frag.h ++++ b/include/net/inet_frag.h +@@ -141,9 +141,8 @@ void inet_frag_kill(struct inet_frag_queue *q); + void inet_frag_destroy(struct inet_frag_queue *q); + struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key); + +-/* Free all skbs in the queue; return the sum of their truesizes. */ +-unsigned int inet_frag_rbtree_purge(struct rb_root *root, +- enum skb_drop_reason reason); ++void inet_frag_queue_flush(struct inet_frag_queue *q, ++ enum skb_drop_reason reason); + + static inline void inet_frag_put(struct inet_frag_queue *q) + { +diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c +index 496308c0238485..1e390d24f11440 100644 +--- a/net/ipv4/inet_fragment.c ++++ b/net/ipv4/inet_fragment.c +@@ -264,8 +264,8 @@ static void inet_frag_destroy_rcu(struct rcu_head *head) + kmem_cache_free(f->frags_cachep, q); + } + +-unsigned int inet_frag_rbtree_purge(struct rb_root *root, +- enum skb_drop_reason reason) ++static unsigned int ++inet_frag_rbtree_purge(struct rb_root *root, enum skb_drop_reason reason) + { + struct rb_node *p = rb_first(root); + unsigned int sum = 0; +@@ -285,7 +285,16 @@ unsigned int inet_frag_rbtree_purge(struct rb_root *root, + } + return sum; + } +-EXPORT_SYMBOL(inet_frag_rbtree_purge); ++ ++void inet_frag_queue_flush(struct inet_frag_queue *q, ++ enum skb_drop_reason reason) ++{ ++ unsigned int sum; ++ ++ sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); ++ sub_frag_mem_limit(q->fqdir, sum); ++} ++EXPORT_SYMBOL(inet_frag_queue_flush); + + void inet_frag_destroy(struct inet_frag_queue *q) + { +diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c +index 484edc8513e4b7..7214d5bcc647e5 100644 +--- a/net/ipv4/ip_fragment.c ++++ b/net/ipv4/ip_fragment.c +@@ -253,16 +253,12 @@ static int ip_frag_too_far(struct ipq *qp) + + static int ip_frag_reinit(struct ipq *qp) + { +- unsigned int sum_truesize = 0; +- + if (!mod_timer(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) { + refcount_inc(&qp->q.refcnt); + return -ETIMEDOUT; + } + +- sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, +- SKB_DROP_REASON_FRAG_TOO_FAR); +- sub_frag_mem_limit(qp->q.fqdir, sum_truesize); ++ inet_frag_queue_flush(&qp->q, SKB_DROP_REASON_FRAG_TOO_FAR); + + qp->q.flags = 0; + qp->q.len = 0; +-- +2.53.0 + diff --git a/queue-6.6/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch b/queue-6.6/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch new file mode 100644 index 0000000000..15a0e4cde4 --- /dev/null +++ b/queue-6.6/inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch @@ -0,0 +1,187 @@ +From 6aa360db0b570312fae12f5544c7aa8e58599fd3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 18:24:09 +0800 +Subject: inet: frags: flush pending skbs in fqdir_pre_exit() + +From: Jakub Kicinski + +[ Upstream commit 006a5035b495dec008805df249f92c22c89c3d2e ] + +We have been seeing occasional deadlocks on pernet_ops_rwsem since +September in NIPA. The stuck task was usually modprobe (often loading +a driver like ipvlan), trying to take the lock as a Writer. +lockdep does not track readers for rwsems so the read wasn't obvious +from the reports. + +On closer inspection the Reader holding the lock was conntrack looping +forever in nf_conntrack_cleanup_net_list(). Based on past experience +with occasional NIPA crashes I looked thru the tests which run before +the crash and noticed that the crash follows ip_defrag.sh. An immediate +red flag. Scouring thru (de)fragmentation queues reveals skbs sitting +around, holding conntrack references. + +The problem is that since conntrack depends on nf_defrag_ipv6, +nf_defrag_ipv6 will load first. Since nf_defrag_ipv6 loads first its +netns exit hooks run _after_ conntrack's netns exit hook. + +Flush all fragment queue SKBs during fqdir_pre_exit() to release +conntrack references before conntrack cleanup runs. Also flush +the queues in timer expiry handlers when they discover fqdir->dead +is set, in case packet sneaks in while we're running the pre_exit +flush. + +The commit under Fixes is not exactly the culprit, but I think +previously the timer firing would eventually unblock the spinning +conntrack. + +Fixes: d5dd88794a13 ("inet: fix various use-after-free in defrags units") +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20251207010942.1672972-4-kuba@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Rajani Kantha <681739313@139.com> +Signed-off-by: Sasha Levin +--- + include/net/inet_frag.h | 13 +------------ + include/net/ipv6_frag.h | 9 ++++++--- + net/ipv4/inet_fragment.c | 36 ++++++++++++++++++++++++++++++++++++ + net/ipv4/ip_fragment.c | 12 +++++++----- + 4 files changed, 50 insertions(+), 20 deletions(-) + +diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h +index 94edc0e130d2c4..fcabb34fff35de 100644 +--- a/include/net/inet_frag.h ++++ b/include/net/inet_frag.h +@@ -123,18 +123,7 @@ void inet_frags_fini(struct inet_frags *); + + int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net); + +-static inline void fqdir_pre_exit(struct fqdir *fqdir) +-{ +- /* Prevent creation of new frags. +- * Pairs with READ_ONCE() in inet_frag_find(). +- */ +- WRITE_ONCE(fqdir->high_thresh, 0); +- +- /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire() +- * and ip6frag_expire_frag_queue(). +- */ +- WRITE_ONCE(fqdir->dead, true); +-} ++void fqdir_pre_exit(struct fqdir *fqdir); + void fqdir_exit(struct fqdir *fqdir); + + void inet_frag_kill(struct inet_frag_queue *q); +diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h +index 7321ffe3a108c1..df61b98b521531 100644 +--- a/include/net/ipv6_frag.h ++++ b/include/net/ipv6_frag.h +@@ -68,9 +68,6 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) + struct sk_buff *head; + + rcu_read_lock(); +- /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */ +- if (READ_ONCE(fq->q.fqdir->dead)) +- goto out_rcu_unlock; + spin_lock(&fq->q.lock); + + if (fq->q.flags & INET_FRAG_COMPLETE) +@@ -79,6 +76,12 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) + fq->q.flags |= INET_FRAG_DROP; + inet_frag_kill(&fq->q); + ++ /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */ ++ if (READ_ONCE(fq->q.fqdir->dead)) { ++ inet_frag_queue_flush(&fq->q, 0); ++ goto out; ++ } ++ + dev = dev_get_by_index_rcu(net, fq->iif); + if (!dev) + goto out; +diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c +index 1e390d24f11440..8cf88882d65e3e 100644 +--- a/net/ipv4/inet_fragment.c ++++ b/net/ipv4/inet_fragment.c +@@ -219,6 +219,41 @@ static int __init inet_frag_wq_init(void) + + pure_initcall(inet_frag_wq_init); + ++void fqdir_pre_exit(struct fqdir *fqdir) ++{ ++ struct inet_frag_queue *fq; ++ struct rhashtable_iter hti; ++ ++ /* Prevent creation of new frags. ++ * Pairs with READ_ONCE() in inet_frag_find(). ++ */ ++ WRITE_ONCE(fqdir->high_thresh, 0); ++ ++ /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire() ++ * and ip6frag_expire_frag_queue(). ++ */ ++ WRITE_ONCE(fqdir->dead, true); ++ ++ rhashtable_walk_enter(&fqdir->rhashtable, &hti); ++ rhashtable_walk_start(&hti); ++ ++ while ((fq = rhashtable_walk_next(&hti))) { ++ if (IS_ERR(fq)) { ++ if (PTR_ERR(fq) != -EAGAIN) ++ break; ++ continue; ++ } ++ spin_lock_bh(&fq->lock); ++ if (!(fq->flags & INET_FRAG_COMPLETE)) ++ inet_frag_queue_flush(fq, 0); ++ spin_unlock_bh(&fq->lock); ++ } ++ ++ rhashtable_walk_stop(&hti); ++ rhashtable_walk_exit(&hti); ++} ++EXPORT_SYMBOL(fqdir_pre_exit); ++ + void fqdir_exit(struct fqdir *fqdir) + { + INIT_WORK(&fqdir->destroy_work, fqdir_work_fn); +@@ -291,6 +326,7 @@ void inet_frag_queue_flush(struct inet_frag_queue *q, + { + unsigned int sum; + ++ reason = reason ?: SKB_DROP_REASON_FRAG_REASM_TIMEOUT; + sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); + sub_frag_mem_limit(q->fqdir, sum); + } +diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c +index 7214d5bcc647e5..f5b5aa036cc668 100644 +--- a/net/ipv4/ip_fragment.c ++++ b/net/ipv4/ip_fragment.c +@@ -148,11 +148,6 @@ static void ip_expire(struct timer_list *t) + net = qp->q.fqdir->net; + + rcu_read_lock(); +- +- /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ +- if (READ_ONCE(qp->q.fqdir->dead)) +- goto out_rcu_unlock; +- + spin_lock(&qp->q.lock); + + if (qp->q.flags & INET_FRAG_COMPLETE) +@@ -160,6 +155,13 @@ static void ip_expire(struct timer_list *t) + + qp->q.flags |= INET_FRAG_DROP; + ipq_kill(qp); ++ ++ /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ ++ if (READ_ONCE(qp->q.fqdir->dead)) { ++ inet_frag_queue_flush(&qp->q, 0); ++ goto out; ++ } ++ + __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); + __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT); + +-- +2.53.0 + diff --git a/queue-6.6/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch b/queue-6.6/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch new file mode 100644 index 0000000000..a31e2e76a2 --- /dev/null +++ b/queue-6.6/media-rc-fix-race-between-unregister-and-urb-irq-cal.patch @@ -0,0 +1,759 @@ +From e715b068fd0d731c4eb71b319158ce50a417e517 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 20 Dec 2025 10:33:26 +0000 +Subject: media: rc: fix race between unregister and urb/irq callbacks + +From: Sean Young + +[ Upstream commit dccc0c3ddf8f16071736f98a7d6dd46a2d43e037 ] + +Some rc device drivers have a race condition between rc_unregister_device() +and irq or urb callbacks. This is because rc_unregister_device() does two +things, it marks the device as unregistered so no new commands can be +issued and then it calls rc_free_device(). This means the driver has no +chance to cancel any pending urb callbacks or interrupts after the device +has been marked as unregistered. Those callbacks may access struct rc_dev +or its members (e.g. struct ir_raw_event_ctrl), which have been freed by +rc_free_device(). + +This change removes the implicit call to rc_free_device() from +rc_unregister_device(). This means that device drivers can call +rc_unregister_device() in their remove or disconnect function, then cancel +all the urbs and interrupts before explicitly calling rc_free_device(). + +Note this is an alternative fix for an issue found by Haotian Zhang, see +the Closes: tags. + +Reported-by: Haotian Zhang +Closes: https://lore.kernel.org/linux-media/20251114101432.2566-1-vulab@iscas.ac.cn/ +Closes: https://lore.kernel.org/linux-media/20251114101418.2548-1-vulab@iscas.ac.cn/ +Closes: https://lore.kernel.org/linux-media/20251114101346.2530-1-vulab@iscas.ac.cn/ +Closes: https://lore.kernel.org/linux-media/20251114090605.2413-1-vulab@iscas.ac.cn/ +Reviewed-by: Patrice Chotard +Signed-off-by: Sean Young +Signed-off-by: Hans Verkuil +Stable-dep-of: 646ebdd31058 ("media: rc: ttusbir: fix inverted error logic") +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/bridge/sil-sii8620.c | 1 + + drivers/hid/hid-picolcd_cir.c | 1 + + drivers/media/cec/core/cec-core.c | 2 +- + drivers/media/common/siano/smsir.c | 1 + + drivers/media/i2c/ir-kbd-i2c.c | 2 ++ + drivers/media/pci/bt8xx/bttv-input.c | 3 ++- + drivers/media/pci/cx23885/cx23885-input.c | 1 + + drivers/media/pci/cx88/cx88-input.c | 3 ++- + drivers/media/pci/dm1105/dm1105.c | 1 + + drivers/media/pci/mantis/mantis_input.c | 1 + + drivers/media/pci/saa7134/saa7134-input.c | 1 + + drivers/media/pci/smipcie/smipcie-ir.c | 1 + + drivers/media/pci/ttpci/budget-ci.c | 1 + + drivers/media/rc/ati_remote.c | 6 +++--- + drivers/media/rc/ene_ir.c | 2 +- + drivers/media/rc/fintek-cir.c | 3 ++- + drivers/media/rc/igorplugusb.c | 1 + + drivers/media/rc/iguanair.c | 1 + + drivers/media/rc/img-ir/img-ir-hw.c | 3 ++- + drivers/media/rc/img-ir/img-ir-raw.c | 3 ++- + drivers/media/rc/imon.c | 3 ++- + drivers/media/rc/ir-hix5hd2.c | 2 +- + drivers/media/rc/ir_toy.c | 1 + + drivers/media/rc/ite-cir.c | 2 +- + drivers/media/rc/mceusb.c | 1 + + drivers/media/rc/rc-ir-raw.c | 5 ----- + drivers/media/rc/rc-loopback.c | 1 + + drivers/media/rc/rc-main.c | 6 +----- + drivers/media/rc/redrat3.c | 4 +++- + drivers/media/rc/st_rc.c | 2 +- + drivers/media/rc/streamzap.c | 7 ++++--- + drivers/media/rc/sunxi-cir.c | 1 + + drivers/media/rc/ttusbir.c | 2 +- + drivers/media/rc/winbond-cir.c | 2 +- + drivers/media/rc/xbox_remote.c | 5 +++-- + drivers/media/usb/au0828/au0828-input.c | 1 + + drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 1 + + drivers/media/usb/dvb-usb/dvb-usb-remote.c | 6 ++++-- + drivers/media/usb/em28xx/em28xx-input.c | 1 + + drivers/staging/media/av7110/av7110_ir.c | 1 + + include/media/rc-core.h | 2 -- + 41 files changed, 58 insertions(+), 36 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c +index 599164e3877dbd..93c41c8dedff84 100644 +--- a/drivers/gpu/drm/bridge/sil-sii8620.c ++++ b/drivers/gpu/drm/bridge/sil-sii8620.c +@@ -2220,6 +2220,7 @@ static void sii8620_detach(struct drm_bridge *bridge) + return; + + rc_unregister_device(ctx->rc_dev); ++ rc_free_device(ctx->rc_dev); + } + + static int sii8620_is_packing_required(struct sii8620 *ctx, +diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c +index d6faa0e00f95ac..6d4c636e1c9f7e 100644 +--- a/drivers/hid/hid-picolcd_cir.c ++++ b/drivers/hid/hid-picolcd_cir.c +@@ -134,5 +134,6 @@ void picolcd_exit_cir(struct picolcd_data *data) + + data->rc_dev = NULL; + rc_unregister_device(rdev); ++ rc_free_device(rdev); + } + +diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c +index b4dcc352a3c6f1..74dff5f8d33658 100644 +--- a/drivers/media/cec/core/cec-core.c ++++ b/drivers/media/cec/core/cec-core.c +@@ -366,8 +366,8 @@ int cec_register_adapter(struct cec_adapter *adap, + res = cec_devnode_register(&adap->devnode, adap->owner); + if (res) { + #ifdef CONFIG_MEDIA_CEC_RC +- /* Note: rc_unregister also calls rc_free */ + rc_unregister_device(adap->rc); ++ rc_free_device(adap->rc); + adap->rc = NULL; + #endif + return res; +diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c +index d85c78c104b990..5f4c0aa7a0d72a 100644 +--- a/drivers/media/common/siano/smsir.c ++++ b/drivers/media/common/siano/smsir.c +@@ -92,6 +92,7 @@ int sms_ir_init(struct smscore_device_t *coredev) + void sms_ir_exit(struct smscore_device_t *coredev) + { + rc_unregister_device(coredev->ir.dev); ++ rc_free_device(coredev->ir.dev); + + pr_debug("\n"); + } +diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c +index a8026f0f980f98..943e761c149643 100644 +--- a/drivers/media/i2c/ir-kbd-i2c.c ++++ b/drivers/media/i2c/ir-kbd-i2c.c +@@ -355,6 +355,7 @@ static void ir_work(struct work_struct *work) + mutex_unlock(&ir->lock); + if (rc == -ENODEV) { + rc_unregister_device(ir->rc); ++ rc_free_device(ir->rc); + ir->rc = NULL; + return; + } +@@ -972,6 +973,7 @@ static void ir_remove(struct i2c_client *client) + i2c_unregister_device(ir->tx_c); + + rc_unregister_device(ir->rc); ++ rc_free_device(ir->rc); + } + + static const struct i2c_device_id ir_kbd_id[] = { +diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c +index 41226f1d0e5b64..d70e6282c48b2e 100644 +--- a/drivers/media/pci/bt8xx/bttv-input.c ++++ b/drivers/media/pci/bt8xx/bttv-input.c +@@ -572,8 +572,9 @@ void bttv_input_fini(struct bttv *btv) + if (btv->remote == NULL) + return; + +- bttv_ir_stop(btv); + rc_unregister_device(btv->remote->dev); ++ bttv_ir_stop(btv); ++ rc_free_device(btv->remote->dev); + kfree(btv->remote); + btv->remote = NULL; + } +diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c +index d2e84c6457e0ab..722329ef3fd2cc 100644 +--- a/drivers/media/pci/cx23885/cx23885-input.c ++++ b/drivers/media/pci/cx23885/cx23885-input.c +@@ -402,6 +402,7 @@ void cx23885_input_fini(struct cx23885_dev *dev) + if (dev->kernel_ir == NULL) + return; + rc_unregister_device(dev->kernel_ir->rc); ++ rc_free_device(dev->kernel_ir->rc); + kfree(dev->kernel_ir->phys); + kfree(dev->kernel_ir->name); + kfree(dev->kernel_ir); +diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c +index a04a1d33fadb1b..74a8769dd6c79c 100644 +--- a/drivers/media/pci/cx88/cx88-input.c ++++ b/drivers/media/pci/cx88/cx88-input.c +@@ -510,8 +510,9 @@ int cx88_ir_fini(struct cx88_core *core) + if (!ir) + return 0; + +- cx88_ir_stop(core); + rc_unregister_device(ir->dev); ++ cx88_ir_stop(core); ++ rc_free_device(ir->dev); + kfree(ir); + + /* done */ +diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c +index 9e9c7c071accce..e1185aa669f480 100644 +--- a/drivers/media/pci/dm1105/dm1105.c ++++ b/drivers/media/pci/dm1105/dm1105.c +@@ -763,6 +763,7 @@ static int dm1105_ir_init(struct dm1105_dev *dm1105) + static void dm1105_ir_exit(struct dm1105_dev *dm1105) + { + rc_unregister_device(dm1105->ir.dev); ++ rc_free_device(dm1105->ir.dev); + } + + static int dm1105_hw_init(struct dm1105_dev *dev) +diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c +index 34c0d979240fda..edb4cacf55d229 100644 +--- a/drivers/media/pci/mantis/mantis_input.c ++++ b/drivers/media/pci/mantis/mantis_input.c +@@ -72,5 +72,6 @@ EXPORT_SYMBOL_GPL(mantis_input_init); + void mantis_input_exit(struct mantis_pci *mantis) + { + rc_unregister_device(mantis->rc); ++ rc_free_device(mantis->rc); + } + EXPORT_SYMBOL_GPL(mantis_input_exit); +diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c +index 8610eb473b39e1..8a0f26d94d1de8 100644 +--- a/drivers/media/pci/saa7134/saa7134-input.c ++++ b/drivers/media/pci/saa7134/saa7134-input.c +@@ -834,6 +834,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) + return; + + rc_unregister_device(dev->remote->dev); ++ rc_free_device(dev->remote->dev); + kfree(dev->remote); + dev->remote = NULL; + } +diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c +index c0604d9c70119a..0bbe4fa2d5a84e 100644 +--- a/drivers/media/pci/smipcie/smipcie-ir.c ++++ b/drivers/media/pci/smipcie/smipcie-ir.c +@@ -181,5 +181,6 @@ void smi_ir_exit(struct smi_dev *dev) + + rc_unregister_device(rc_dev); + smi_ir_stop(ir); ++ rc_free_device(rc_dev); + ir->rc_dev = NULL; + } +diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c +index 66e1a004ee431c..2ad30f91bcd9ee 100644 +--- a/drivers/media/pci/ttpci/budget-ci.c ++++ b/drivers/media/pci/ttpci/budget-ci.c +@@ -247,6 +247,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) + tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); + + rc_unregister_device(budget_ci->ir.dev); ++ rc_free_device(budget_ci->ir.dev); + } + + static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c +index fff4dd48eacad2..1d68834ecd0b15 100644 +--- a/drivers/media/rc/ati_remote.c ++++ b/drivers/media/rc/ati_remote.c +@@ -921,7 +921,6 @@ static int ati_remote_probe(struct usb_interface *interface, + input_free_device(input_dev); + exit_unregister_device: + rc_unregister_device(rc_dev); +- rc_dev = NULL; + exit_kill_urbs: + usb_kill_urb(ati_remote->irq_urb); + usb_kill_urb(ati_remote->out_urb); +@@ -941,18 +940,19 @@ static void ati_remote_disconnect(struct usb_interface *interface) + struct ati_remote *ati_remote; + + ati_remote = usb_get_intfdata(interface); +- usb_set_intfdata(interface, NULL); + if (!ati_remote) { + dev_warn(&interface->dev, "%s - null device?\n", __func__); + return; + } + ++ rc_unregister_device(ati_remote->rdev); ++ usb_set_intfdata(interface, NULL); + usb_kill_urb(ati_remote->irq_urb); + usb_kill_urb(ati_remote->out_urb); + if (ati_remote->idev) + input_unregister_device(ati_remote->idev); +- rc_unregister_device(ati_remote->rdev); + ati_remote_free_buffers(ati_remote); ++ rc_free_device(ati_remote->rdev); + kfree(ati_remote); + } + +diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c +index 11ee21a7db8f00..9140e4f28c9955 100644 +--- a/drivers/media/rc/ene_ir.c ++++ b/drivers/media/rc/ene_ir.c +@@ -1093,7 +1093,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) + release_region(dev->hw_io, ENE_IO_SIZE); + exit_unregister_device: + rc_unregister_device(rdev); +- rdev = NULL; + exit_free_dev_rdev: + rc_free_device(rdev); + kfree(dev); +@@ -1113,6 +1112,7 @@ static void ene_remove(struct pnp_dev *pnp_dev) + ene_rx_restore_hw_buffer(dev); + spin_unlock_irqrestore(&dev->hw_lock, flags); + ++ rc_free_device(dev->rdev); + free_irq(dev->irq, dev); + release_region(dev->hw_io, ENE_IO_SIZE); + kfree(dev); +diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c +index 3fb0968efd57d3..9b789097cdd4c3 100644 +--- a/drivers/media/rc/fintek-cir.c ++++ b/drivers/media/rc/fintek-cir.c +@@ -568,6 +568,7 @@ static void fintek_remove(struct pnp_dev *pdev) + struct fintek_dev *fintek = pnp_get_drvdata(pdev); + unsigned long flags; + ++ rc_unregister_device(fintek->rdev); + spin_lock_irqsave(&fintek->fintek_lock, flags); + /* disable CIR */ + fintek_disable_cir(fintek); +@@ -580,7 +581,7 @@ static void fintek_remove(struct pnp_dev *pdev) + free_irq(fintek->cir_irq, fintek); + release_region(fintek->cir_addr, fintek->cir_port_len); + +- rc_unregister_device(fintek->rdev); ++ rc_free_device(fintek->rdev); + + kfree(fintek); + } +diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c +index f3616607d4f52b..8bf059b9a31b45 100644 +--- a/drivers/media/rc/igorplugusb.c ++++ b/drivers/media/rc/igorplugusb.c +@@ -247,6 +247,7 @@ static void igorplugusb_disconnect(struct usb_interface *intf) + usb_set_intfdata(intf, NULL); + usb_unpoison_urb(ir->urb); + usb_free_urb(ir->urb); ++ rc_free_device(ir->rc); + kfree(ir->buf_in); + kfree(ir->request); + } +diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c +index 8af94246e5916e..7bd6dd7254157a 100644 +--- a/drivers/media/rc/iguanair.c ++++ b/drivers/media/rc/iguanair.c +@@ -500,6 +500,7 @@ static void iguanair_disconnect(struct usb_interface *intf) + usb_set_intfdata(intf, NULL); + usb_kill_urb(ir->urb_in); + usb_kill_urb(ir->urb_out); ++ rc_free_device(ir->rc); + usb_free_urb(ir->urb_in); + usb_free_urb(ir->urb_out); + usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in); +diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c +index 5da7479c1793b1..07f41372976eaf 100644 +--- a/drivers/media/rc/img-ir/img-ir-hw.c ++++ b/drivers/media/rc/img-ir/img-ir-hw.c +@@ -1117,9 +1117,10 @@ void img_ir_remove_hw(struct img_ir_priv *priv) + struct rc_dev *rdev = hw->rdev; + if (!rdev) + return; ++ rc_unregister_device(rdev); + img_ir_set_decoder(priv, NULL, 0); + hw->rdev = NULL; +- rc_unregister_device(rdev); ++ rc_free_device(rdev); + #ifdef CONFIG_COMMON_CLK + if (!IS_ERR(priv->clk)) + clk_notifier_unregister(priv->clk, &hw->clk_nb); +diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c +index 8b0bdd9603b3c5..533d40dae54224 100644 +--- a/drivers/media/rc/img-ir/img-ir-raw.c ++++ b/drivers/media/rc/img-ir/img-ir-raw.c +@@ -136,6 +136,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv) + if (!rdev) + return; + ++ rc_unregister_device(rdev); + /* switch off and disable raw (edge) interrupts */ + spin_lock_irq(&priv->lock); + raw->rdev = NULL; +@@ -145,7 +146,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv) + img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE); + spin_unlock_irq(&priv->lock); + +- rc_unregister_device(rdev); ++ rc_free_device(rdev); + + del_timer_sync(&raw->timer); + } +diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c +index bb4aabb08c06ef..69e118863aa67c 100644 +--- a/drivers/media/rc/imon.c ++++ b/drivers/media/rc/imon.c +@@ -2546,9 +2546,10 @@ static void imon_disconnect(struct usb_interface *interface) + + if (ifnum == 0) { + ictx->dev_present_intf0 = false; ++ rc_unregister_device(ictx->rdev); + usb_kill_urb(ictx->rx_urb_intf0); + input_unregister_device(ictx->idev); +- rc_unregister_device(ictx->rdev); ++ rc_free_device(ictx->rdev); + if (ictx->display_supported) { + if (ictx->display_type == IMON_DISPLAY_TYPE_LCD) + usb_deregister_dev(interface, &imon_lcd_class); +diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c +index 0034f615b4660e..df347d2fb0ead3 100644 +--- a/drivers/media/rc/ir-hix5hd2.c ++++ b/drivers/media/rc/ir-hix5hd2.c +@@ -331,7 +331,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) + + regerr: + rc_unregister_device(rdev); +- rdev = NULL; + clkerr: + clk_disable_unprepare(priv->clock); + err: +@@ -346,6 +345,7 @@ static void hix5hd2_ir_remove(struct platform_device *pdev) + + clk_disable_unprepare(priv->clock); + rc_unregister_device(priv->rdev); ++ rc_free_device(priv->rdev); + } + + #ifdef CONFIG_PM_SLEEP +diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c +index 69e630d85262f6..490de29b7ffbef 100644 +--- a/drivers/media/rc/ir_toy.c ++++ b/drivers/media/rc/ir_toy.c +@@ -536,6 +536,7 @@ static void irtoy_disconnect(struct usb_interface *intf) + usb_free_urb(ir->urb_out); + usb_kill_urb(ir->urb_in); + usb_free_urb(ir->urb_in); ++ rc_free_device(ir->rc); + kfree(ir->in); + kfree(ir->out); + kfree(ir); +diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c +index fcfadd7ea31cf3..c584dc6bfd7b48 100644 +--- a/drivers/media/rc/ite-cir.c ++++ b/drivers/media/rc/ite-cir.c +@@ -1415,7 +1415,6 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id + release_region(itdev->cir_addr, itdev->params->io_region_size); + exit_unregister_device: + rc_unregister_device(rdev); +- rdev = NULL; + exit_free_dev_rdev: + rc_free_device(rdev); + kfree(itdev); +@@ -1440,6 +1439,7 @@ static void ite_remove(struct pnp_dev *pdev) + release_region(dev->cir_addr, dev->params->io_region_size); + + rc_unregister_device(dev->rdev); ++ rc_free_device(dev->rdev); + + kfree(dev); + } +diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c +index c76ba24c1f5595..4115a46929c473 100644 +--- a/drivers/media/rc/mceusb.c ++++ b/drivers/media/rc/mceusb.c +@@ -1858,6 +1858,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf) + usb_free_urb(ir->urb_in); + usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in); + usb_put_dev(dev); ++ rc_free_device(ir->rc); + + kfree(ir); + } +diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c +index 16e33d7eaaa2d8..dd70f8ad526642 100644 +--- a/drivers/media/rc/rc-ir-raw.c ++++ b/drivers/media/rc/rc-ir-raw.c +@@ -647,9 +647,6 @@ int ir_raw_event_register(struct rc_dev *dev) + + void ir_raw_event_free(struct rc_dev *dev) + { +- if (!dev) +- return; +- + kfree(dev->raw); + dev->raw = NULL; + } +@@ -673,8 +670,6 @@ void ir_raw_event_unregister(struct rc_dev *dev) + + lirc_bpf_free(dev); + +- ir_raw_event_free(dev); +- + /* + * A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so + * ensure that the raw member is null on unlock; this is how +diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c +index b356041c5c00e7..79073aaabd9175 100644 +--- a/drivers/media/rc/rc-loopback.c ++++ b/drivers/media/rc/rc-loopback.c +@@ -264,6 +264,7 @@ static int __init loop_init(void) + static void __exit loop_exit(void) + { + rc_unregister_device(loopdev.dev); ++ rc_free_device(loopdev.dev); + } + + module_init(loop_init); +diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c +index 6bdad6341844f5..10464f7942304e 100644 +--- a/drivers/media/rc/rc-main.c ++++ b/drivers/media/rc/rc-main.c +@@ -1611,6 +1611,7 @@ static void rc_dev_release(struct device *device) + { + struct rc_dev *dev = to_rc_dev(device); + ++ ir_raw_event_free(dev); + kfree(dev); + } + +@@ -1773,7 +1774,6 @@ struct rc_dev *devm_rc_allocate_device(struct device *dev, + } + + rc->dev.parent = dev; +- rc->managed_alloc = true; + *dr = rc; + devres_add(dev, dr); + +@@ -2042,11 +2042,7 @@ void rc_unregister_device(struct rc_dev *dev) + device_del(&dev->dev); + + ida_free(&rc_ida, dev->minor); +- +- if (!dev->managed_alloc) +- rc_free_device(dev); + } +- + EXPORT_SYMBOL_GPL(rc_unregister_device); + + /* +diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c +index 880981e1c507e1..d47f6e983ec67d 100644 +--- a/drivers/media/rc/redrat3.c ++++ b/drivers/media/rc/redrat3.c +@@ -1133,11 +1133,13 @@ static void redrat3_dev_disconnect(struct usb_interface *intf) + { + struct usb_device *udev = interface_to_usbdev(intf); + struct redrat3_dev *rr3 = usb_get_intfdata(intf); ++ struct rc_dev *rc = rr3->rc; + + usb_set_intfdata(intf, NULL); +- rc_unregister_device(rr3->rc); ++ rc_unregister_device(rc); + led_classdev_unregister(&rr3->led); + redrat3_delete(rr3, udev); ++ rc_free_device(rc); + } + + static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message) +diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c +index 8a9f31e15b0c01..bac1ac4e0f3a22 100644 +--- a/drivers/media/rc/st_rc.c ++++ b/drivers/media/rc/st_rc.c +@@ -202,6 +202,7 @@ static void st_rc_remove(struct platform_device *pdev) + device_init_wakeup(&pdev->dev, false); + clk_disable_unprepare(rc_dev->sys_clock); + rc_unregister_device(rc_dev->rdev); ++ rc_free_device(rc_dev->rdev); + } + + static int st_rc_open(struct rc_dev *rdev) +@@ -333,7 +334,6 @@ static int st_rc_probe(struct platform_device *pdev) + return ret; + rcerr: + rc_unregister_device(rdev); +- rdev = NULL; + clkerr: + clk_disable_unprepare(rc_dev->sys_clock); + err: +diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c +index 8e9b156e430022..8c85b9f30a3a96 100644 +--- a/drivers/media/rc/streamzap.c ++++ b/drivers/media/rc/streamzap.c +@@ -392,15 +392,16 @@ static void streamzap_disconnect(struct usb_interface *interface) + struct streamzap_ir *sz = usb_get_intfdata(interface); + struct usb_device *usbdev = interface_to_usbdev(interface); + +- usb_set_intfdata(interface, NULL); +- + if (!sz) + return; + +- usb_kill_urb(sz->urb_in); + rc_unregister_device(sz->rdev); ++ usb_set_intfdata(interface, NULL); ++ ++ usb_kill_urb(sz->urb_in); + usb_free_urb(sz->urb_in); + usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in); ++ rc_free_device(sz->rdev); + + kfree(sz); + } +diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c +index bf58c965ead8cf..e2477e724637fa 100644 +--- a/drivers/media/rc/sunxi-cir.c ++++ b/drivers/media/rc/sunxi-cir.c +@@ -370,6 +370,7 @@ static void sunxi_ir_remove(struct platform_device *pdev) + struct sunxi_ir *ir = platform_get_drvdata(pdev); + + rc_unregister_device(ir->rc); ++ rc_free_device(ir->rc); + sunxi_ir_hw_exit(&pdev->dev); + } + +diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c +index dde446a95eaa93..a670d4b008cb0d 100644 +--- a/drivers/media/rc/ttusbir.c ++++ b/drivers/media/rc/ttusbir.c +@@ -336,7 +336,6 @@ static int ttusbir_probe(struct usb_interface *intf, + return 0; + out3: + rc_unregister_device(rc); +- rc = NULL; + out2: + led_classdev_unregister(&tt->led); + out: +@@ -378,6 +377,7 @@ static void ttusbir_disconnect(struct usb_interface *intf) + usb_kill_urb(tt->bulk_urb); + usb_free_urb(tt->bulk_urb); + kfree(tt->bulk_buffer); ++ rc_free_device(tt->rc); + usb_set_intfdata(intf, NULL); + kfree(tt); + } +diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c +index 25884a79985c8a..14d8b58e283980 100644 +--- a/drivers/media/rc/winbond-cir.c ++++ b/drivers/media/rc/winbond-cir.c +@@ -1132,7 +1132,6 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) + release_region(data->wbase, WAKEUP_IOMEM_LEN); + exit_unregister_device: + rc_unregister_device(data->dev); +- data->dev = NULL; + exit_free_rc: + rc_free_device(data->dev); + exit_unregister_led: +@@ -1163,6 +1162,7 @@ wbcir_remove(struct pnp_dev *device) + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); + + rc_unregister_device(data->dev); ++ rc_free_device(data->dev); + + led_classdev_unregister(&data->led); + +diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c +index 0c9c855ced729c..80b7c247932a8f 100644 +--- a/drivers/media/rc/xbox_remote.c ++++ b/drivers/media/rc/xbox_remote.c +@@ -283,14 +283,15 @@ static void xbox_remote_disconnect(struct usb_interface *interface) + struct xbox_remote *xbox_remote; + + xbox_remote = usb_get_intfdata(interface); +- usb_set_intfdata(interface, NULL); + if (!xbox_remote) { + dev_warn(&interface->dev, "%s - null device?\n", __func__); + return; + } + +- usb_kill_urb(xbox_remote->irq_urb); + rc_unregister_device(xbox_remote->rdev); ++ usb_set_intfdata(interface, NULL); ++ usb_kill_urb(xbox_remote->irq_urb); ++ rc_free_device(xbox_remote->rdev); + usb_free_urb(xbox_remote->irq_urb); + kfree(xbox_remote->inbuf); + kfree(xbox_remote); +diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c +index 3d3368202cd018..283ad2c6288cd5 100644 +--- a/drivers/media/usb/au0828/au0828-input.c ++++ b/drivers/media/usb/au0828/au0828-input.c +@@ -357,6 +357,7 @@ void au0828_rc_unregister(struct au0828_dev *dev) + return; + + rc_unregister_device(ir->rc); ++ rc_free_device(ir->rc); + + /* done */ + kfree(ir); +diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +index f1c79f351ec8de..17e8961179d14b 100644 +--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c ++++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +@@ -187,6 +187,7 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) + if (d->rc_dev) { + cancel_delayed_work_sync(&d->rc_query_work); + rc_unregister_device(d->rc_dev); ++ rc_free_device(d->rc_dev); + d->rc_dev = NULL; + } + +diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c +index 65e2c9e2cdc99f..6dc11718dfb985 100644 +--- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c ++++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c +@@ -347,10 +347,12 @@ int dvb_usb_remote_exit(struct dvb_usb_device *d) + { + if (d->state & DVB_USB_STATE_REMOTE) { + cancel_delayed_work_sync(&d->rc_query_work); +- if (d->props.rc.mode == DVB_RC_LEGACY) ++ if (d->props.rc.mode == DVB_RC_LEGACY) { + input_unregister_device(d->input_dev); +- else ++ } else { + rc_unregister_device(d->rc_dev); ++ rc_free_device(d->rc_dev); ++ } + } + d->state &= ~DVB_USB_STATE_REMOTE; + return 0; +diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c +index 5f3b00869bdbc9..26f333b5be7325 100644 +--- a/drivers/media/usb/em28xx/em28xx-input.c ++++ b/drivers/media/usb/em28xx/em28xx-input.c +@@ -853,6 +853,7 @@ static int em28xx_ir_fini(struct em28xx *dev) + goto ref_put; + + rc_unregister_device(ir->rc); ++ rc_free_device(ir->rc); + + kfree(ir->i2c_client); + +diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c +index a851ba328e4a3e..c0a32325e2cc98 100644 +--- a/drivers/staging/media/av7110/av7110_ir.c ++++ b/drivers/staging/media/av7110/av7110_ir.c +@@ -152,6 +152,7 @@ int av7110_ir_init(struct av7110 *av7110) + void av7110_ir_exit(struct av7110 *av7110) + { + rc_unregister_device(av7110->ir.rcdev); ++ rc_free_device(av7110->ir.rcdev); + } + + //MODULE_AUTHOR("Holger Waechtler , Oliver Endriss "); +diff --git a/include/media/rc-core.h b/include/media/rc-core.h +index 803349599c272b..b487b0d5ffd1d0 100644 +--- a/include/media/rc-core.h ++++ b/include/media/rc-core.h +@@ -81,7 +81,6 @@ struct lirc_fh { + /** + * struct rc_dev - represents a remote control device + * @dev: driver model's view of this device +- * @managed_alloc: devm_rc_allocate_device was used to create rc_dev + * @sysfs_groups: sysfs attribute groups + * @device_name: name of the rc child device + * @input_phys: physical path to the input child device +@@ -157,7 +156,6 @@ struct lirc_fh { + */ + struct rc_dev { + struct device dev; +- bool managed_alloc; + const struct attribute_group *sysfs_groups[5]; + const char *device_name; + const char *input_phys; +-- +2.53.0 + diff --git a/queue-6.6/media-rc-ttusbir-fix-inverted-error-logic.patch b/queue-6.6/media-rc-ttusbir-fix-inverted-error-logic.patch new file mode 100644 index 0000000000..055c9e7946 --- /dev/null +++ b/queue-6.6/media-rc-ttusbir-fix-inverted-error-logic.patch @@ -0,0 +1,38 @@ +From c4dab51a93d185c810698f405fe6816d9077e4f3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Apr 2026 23:03:09 +0200 +Subject: media: rc: ttusbir: fix inverted error logic + +From: Oliver Neukum + +[ Upstream commit 646ebdd3105809d84ed04aa9e92e47e89cc44502 ] + +We have to report ENOMEM if no buffer is allocated. +Typo dropped a "!". Restore it. + +Fixes: 50acaad3d202 ("media: rc: ttusbir: respect DMA coherency rules") +Cc: stable@vger.kernel.org +Signed-off-by: Oliver Neukum +Signed-off-by: Sean Young +Signed-off-by: Hans Verkuil +Signed-off-by: Sasha Levin +--- + drivers/media/rc/ttusbir.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c +index a670d4b008cb0d..3452b5aefd2848 100644 +--- a/drivers/media/rc/ttusbir.c ++++ b/drivers/media/rc/ttusbir.c +@@ -191,7 +191,7 @@ static int ttusbir_probe(struct usb_interface *intf, + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + buffer = kzalloc(5, GFP_KERNEL); + rc = rc_allocate_device(RC_DRIVER_IR_RAW); +- if (!tt || !rc || buffer) { ++ if (!tt || !rc || !buffer) { + ret = -ENOMEM; + goto out; + } +-- +2.53.0 + diff --git a/queue-6.6/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch b/queue-6.6/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch new file mode 100644 index 0000000000..befa5014f2 --- /dev/null +++ b/queue-6.6/mm-page_alloc-clear-page-private-in-free_pages_prepa.patch @@ -0,0 +1,67 @@ +From 295402a6f7657051ac4775609e6dcb5b7803f8e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 13:02:30 +0800 +Subject: mm/page_alloc: clear page->private in free_pages_prepare() + +From: Mikhail Gavrilov + +[ Upstream commit ac1ea219590c09572ed5992dc233bbf7bb70fef9 ] + +Several subsystems (slub, shmem, ttm, etc.) use page->private but don't +clear it before freeing pages. When these pages are later allocated as +high-order pages and split via split_page(), tail pages retain stale +page->private values. + +This causes a use-after-free in the swap subsystem. The swap code uses +page->private to track swap count continuations, assuming freshly +allocated pages have page->private == 0. When stale values are present, +swap_count_continued() incorrectly assumes the continuation list is valid +and iterates over uninitialized page->lru containing LIST_POISON values, +causing a crash: + + KASAN: maybe wild-memory-access in range [0xdead000000000100-0xdead000000000107] + RIP: 0010:__do_sys_swapoff+0x1151/0x1860 + +Fix this by clearing page->private in free_pages_prepare(), ensuring all +freed pages have clean state regardless of previous use. + +Link: https://lkml.kernel.org/r/20260207173615.146159-1-mikhail.v.gavrilov@gmail.com +Fixes: 3b8000ae185c ("mm/vmalloc: huge vmalloc backing pages should be split rather than compound") +Signed-off-by: Mikhail Gavrilov +Suggested-by: Zi Yan +Acked-by: Zi Yan +Acked-by: David Hildenbrand (Arm) +Reviewed-by: Vlastimil Babka +Cc: Brendan Jackman +Cc: Chris Li +Cc: Hugh Dickins +Cc: Johannes Weiner +Cc: Kairui Song +Cc: Matthew Wilcox (Oracle) +Cc: Michal Hocko +Cc: Nicholas Piggin +Cc: Suren Baghdasaryan +Cc: +Signed-off-by: Andrew Morton +[backport: context only] +Signed-off-by: Li Wang +Signed-off-by: Sasha Levin +--- + mm/page_alloc.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/mm/page_alloc.c b/mm/page_alloc.c +index b617fb364b15d1..ffdc71103c2ad2 100644 +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -1178,6 +1178,7 @@ static __always_inline bool free_pages_prepare(struct page *page, + + page_cpupid_reset_last(page); + page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP; ++ page->private = 0; + reset_page_owner(page, order); + page_table_check_free(page, order); + +-- +2.53.0 + diff --git a/queue-6.6/net-af_key-zero-aligned-sockaddr-tail-in-pf_key-expo.patch b/queue-6.6/net-af_key-zero-aligned-sockaddr-tail-in-pf_key-expo.patch new file mode 100644 index 0000000000..4712da5187 --- /dev/null +++ b/queue-6.6/net-af_key-zero-aligned-sockaddr-tail-in-pf_key-expo.patch @@ -0,0 +1,146 @@ +From 3245660a79bf938f31ad7ec45fe4a97ff0500401 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 14:09:45 +0800 +Subject: net: af_key: zero aligned sockaddr tail in PF_KEY exports + +From: Zhengchuan Liang + +[ Upstream commit 426c355742f02cf743b347d9d7dbdc1bfbfa31ef ] + +PF_KEY export paths use `pfkey_sockaddr_size()` when reserving sockaddr +payload space, so IPv6 addresses occupy 32 bytes on the wire. However, +`pfkey_sockaddr_fill()` initializes only the first 28 bytes of +`struct sockaddr_in6`, leaving the final 4 aligned bytes uninitialized. + +Not every PF_KEY message is affected. The state and policy dump builders +already zero the whole message buffer before filling the sockaddr +payloads. Keep the fix to the export paths that still append aligned +sockaddr payloads with plain `skb_put()`: + + - `SADB_ACQUIRE` + - `SADB_X_NAT_T_NEW_MAPPING` + - `SADB_X_MIGRATE` + +Fix those paths by clearing only the aligned sockaddr tail after +`pfkey_sockaddr_fill()`. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Fixes: 08de61beab8a ("[PFKEYV2]: Extension for dynamic update of endpoint address(es)") +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Co-developed-by: Yuan Tan +Signed-off-by: Yuan Tan +Suggested-by: Xin Liu +Tested-by: Xiao Liu +Signed-off-by: Zhengchuan Liang +Signed-off-by: Steffen Klassert +Signed-off-by: Miles Wang <13621186580@139.com> +Signed-off-by: Sasha Levin +--- + net/key/af_key.c | 52 +++++++++++++++++++++++++++++++----------------- + 1 file changed, 34 insertions(+), 18 deletions(-) + +diff --git a/net/key/af_key.c b/net/key/af_key.c +index 4849407da86d6e..486c9a2e1f52f3 100644 +--- a/net/key/af_key.c ++++ b/net/key/af_key.c +@@ -757,6 +757,22 @@ static unsigned int pfkey_sockaddr_fill(const xfrm_address_t *xaddr, __be16 port + return 0; + } + ++static unsigned int pfkey_sockaddr_fill_zero_tail(const xfrm_address_t *xaddr, ++ __be16 port, ++ struct sockaddr *sa, ++ unsigned short family) ++{ ++ unsigned int prefixlen; ++ int sockaddr_len = pfkey_sockaddr_len(family); ++ int sockaddr_size = pfkey_sockaddr_size(family); ++ ++ prefixlen = pfkey_sockaddr_fill(xaddr, port, sa, family); ++ if (sockaddr_size > sockaddr_len) ++ memset((u8 *)sa + sockaddr_len, 0, sockaddr_size - sockaddr_len); ++ ++ return prefixlen; ++} ++ + static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x, + int add_keys, int hsc) + { +@@ -3205,9 +3221,9 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct + addr->sadb_address_proto = 0; + addr->sadb_address_reserved = 0; + addr->sadb_address_prefixlen = +- pfkey_sockaddr_fill(&x->props.saddr, 0, +- (struct sockaddr *) (addr + 1), +- x->props.family); ++ pfkey_sockaddr_fill_zero_tail(&x->props.saddr, 0, ++ (struct sockaddr *)(addr + 1), ++ x->props.family); + if (!addr->sadb_address_prefixlen) + BUG(); + +@@ -3220,9 +3236,9 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct + addr->sadb_address_proto = 0; + addr->sadb_address_reserved = 0; + addr->sadb_address_prefixlen = +- pfkey_sockaddr_fill(&x->id.daddr, 0, +- (struct sockaddr *) (addr + 1), +- x->props.family); ++ pfkey_sockaddr_fill_zero_tail(&x->id.daddr, 0, ++ (struct sockaddr *)(addr + 1), ++ x->props.family); + if (!addr->sadb_address_prefixlen) + BUG(); + +@@ -3420,9 +3436,9 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, + addr->sadb_address_proto = 0; + addr->sadb_address_reserved = 0; + addr->sadb_address_prefixlen = +- pfkey_sockaddr_fill(&x->props.saddr, 0, +- (struct sockaddr *) (addr + 1), +- x->props.family); ++ pfkey_sockaddr_fill_zero_tail(&x->props.saddr, 0, ++ (struct sockaddr *)(addr + 1), ++ x->props.family); + if (!addr->sadb_address_prefixlen) + BUG(); + +@@ -3442,9 +3458,9 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, + addr->sadb_address_proto = 0; + addr->sadb_address_reserved = 0; + addr->sadb_address_prefixlen = +- pfkey_sockaddr_fill(ipaddr, 0, +- (struct sockaddr *) (addr + 1), +- x->props.family); ++ pfkey_sockaddr_fill_zero_tail(ipaddr, 0, ++ (struct sockaddr *)(addr + 1), ++ x->props.family); + if (!addr->sadb_address_prefixlen) + BUG(); + +@@ -3473,15 +3489,15 @@ static int set_sadb_address(struct sk_buff *skb, int sasize, int type, + switch (type) { + case SADB_EXT_ADDRESS_SRC: + addr->sadb_address_prefixlen = sel->prefixlen_s; +- pfkey_sockaddr_fill(&sel->saddr, 0, +- (struct sockaddr *)(addr + 1), +- sel->family); ++ pfkey_sockaddr_fill_zero_tail(&sel->saddr, 0, ++ (struct sockaddr *)(addr + 1), ++ sel->family); + break; + case SADB_EXT_ADDRESS_DST: + addr->sadb_address_prefixlen = sel->prefixlen_d; +- pfkey_sockaddr_fill(&sel->daddr, 0, +- (struct sockaddr *)(addr + 1), +- sel->family); ++ pfkey_sockaddr_fill_zero_tail(&sel->daddr, 0, ++ (struct sockaddr *)(addr + 1), ++ sel->family); + break; + default: + return -EINVAL; +-- +2.53.0 + diff --git a/queue-6.6/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch b/queue-6.6/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch new file mode 100644 index 0000000000..477080c4b5 --- /dev/null +++ b/queue-6.6/perf-fix-dangling-cgroup-pointer-in-cpuctx.patch @@ -0,0 +1,64 @@ +From 8372446a216ced29b6cee5b08567fe4d3519f67f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 28 May 2026 23:06:58 -0700 +Subject: perf: Fix dangling cgroup pointer in cpuctx + +From: Yeoreum Yun + +[ Upstream commit 3b7a34aebbdf2a4b7295205bf0c654294283ec82 ] + +Commit a3c3c6667("perf/core: Fix child_total_time_enabled accounting +bug at task exit") moves the event->state update to before +list_del_event(). This makes the event->state test in list_del_event() +always false; never calling perf_cgroup_event_disable(). + +As a result, cpuctx->cgrp won't be cleared properly; causing havoc. + +Fixes: a3c3c6667("perf/core: Fix child_total_time_enabled accounting bug at task exit") +Signed-off-by: Yeoreum Yun +Signed-off-by: Peter Zijlstra (Intel) +Tested-by: David Wang <00107082@163.com> +Link: https://lore.kernel.org/all/aD2TspKH%2F7yvfYoO@e129823.arm.com/ +Signed-off-by: Ian Klatzco +Signed-off-by: Sasha Levin +--- + kernel/events/core.c | 16 ++++------------ + 1 file changed, 4 insertions(+), 12 deletions(-) + +diff --git a/kernel/events/core.c b/kernel/events/core.c +index eba5eb6fcb8762..a4187dea6402a7 100644 +--- a/kernel/events/core.c ++++ b/kernel/events/core.c +@@ -2056,18 +2056,6 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) + if (event->group_leader == event) + del_event_from_groups(event, ctx); + +- /* +- * If event was in error state, then keep it +- * that way, otherwise bogus counts will be +- * returned on read(). The only way to get out +- * of error state is by explicit re-enabling +- * of the event +- */ +- if (event->state > PERF_EVENT_STATE_OFF) { +- perf_cgroup_event_disable(event, ctx); +- perf_event_set_state(event, PERF_EVENT_STATE_OFF); +- } +- + ctx->generation++; + event->pmu_ctx->nr_events--; + } +@@ -2401,6 +2389,10 @@ __perf_remove_from_context(struct perf_event *event, + state = PERF_EVENT_STATE_DEAD; + } + event_sched_out(event, ctx); ++ ++ if (event->state > PERF_EVENT_STATE_OFF) ++ perf_cgroup_event_disable(event, ctx); ++ + perf_event_set_state(event, min(event->state, state)); + if (flags & DETACH_GROUP) + perf_group_detach(event); +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index f28fade55c..b89bedefbf 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -46,3 +46,27 @@ sctp-fix-race-between-sctp_wait_for_connect-and-peel.patch ipv6-fix-possible-infinite-loop-in-rt6_fill_node.patch ipv6-fix-possible-infinite-loop-in-fib6_select_path.patch net-skbuff-fix-pskb_carve-leaking-zcopy-pages.patch +perf-fix-dangling-cgroup-pointer-in-cpuctx.patch +batman-adv-v-stop-ogmv2-on-disabled-interface.patch +batman-adv-tvlv-abort-ogm-send-on-tvlv-append-failur.patch +batman-adv-tt-reject-oversized-local-tvlv-buffers.patch +batman-adv-bla-avoid-null-ptr-deref-for-claim-via-dr.patch +batman-adv-tvlv-reject-oversized-tvlv-packets.patch +batman-adv-iv-recover-ogm-scheduling-after-forward-p.patch +batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch +net-af_key-zero-aligned-sockaddr-tail-in-pf_key-expo.patch +batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch +batman-adv-tt-fix-toctou-race-for-reported-vlans.patch +batman-adv-tt-avoid-empty-vlan-responses.patch +batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch +mm-page_alloc-clear-page-private-in-free_pages_prepa.patch +media-rc-fix-race-between-unregister-and-urb-irq-cal.patch +media-rc-ttusbir-fix-inverted-error-logic.patch +drm-fbdev-helper-set-and-clear-vga-switcheroo-client.patch +drm-fbcon-vga_switcheroo-avoid-race-condition-in-fbc.patch +inet-frags-add-inet_frag_queue_flush.patch +inet-frags-flush-pending-skbs-in-fqdir_pre_exit.patch +drm-i915-psr-add-defininitions-for-intel_wa_register.patch +drm-i915-psr-read-intel-dpcd-workaround-register.patch +drm-dp-add-edp-1.5-bit-definition.patch +drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch diff --git a/queue-7.0/drm-i915-psr-add-defininitions-for-intel_wa_register.patch b/queue-7.0/drm-i915-psr-add-defininitions-for-intel_wa_register.patch new file mode 100644 index 0000000000..37537a52d9 --- /dev/null +++ b/queue-7.0/drm-i915-psr-add-defininitions-for-intel_wa_register.patch @@ -0,0 +1,73 @@ +From 8d7a860d1781bcc6bc2ff36ea8c3c19078ae5bf5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 12:38:35 +0300 +Subject: drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD + register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream. + +EDP specification says: + +"If either VSC SDP is unable to be transmitted 100 ns before the SU region, +the Source device may optionally transmit the VSC SDP during the prior +video scan line’s HBlank period There is a Intel specific drm dp register +currently containing bits related how TCON can support PSR2 with SDP on +prior line." + +Unfortunately many panels are having problems in implementing this. So +there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to +figure out if this is properly implemented on a panel or if panel doesn't +require that 100 ns delay before the SU region. Here are the definitions in +this custom DPCD address: + +0 = Panel doesn't support SDP on prior line +1 = Panel supports SDP on prior line +2 = Panel doesn't have 100ns requirement +3 = Reserved + +Add definitions for this new register and it's values into new header +intel_dpcd.h. + +v2: add INTEL_DPCD_ prefix to definitions + +Bspec: 74741 +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com +(cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h + +diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h +new file mode 100644 +index 00000000000000..4aea5326f2ed48 +--- /dev/null ++++ b/drivers/gpu/drm/i915/display/intel_dpcd.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: MIT */ ++/* ++ * Copyright © 2026 Intel Corporation ++ */ ++ ++#ifndef __INTEL_DPCD_H__ ++#define __INTEL_DPCD_H__ ++ ++#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS 0x3f0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK REG_GENMASK(1, 0) ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1 0 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE 1 ++# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE 2 ++ ++#endif /* __INTEL_DPCD_H__ */ +-- +2.53.0 + diff --git a/queue-7.0/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch b/queue-7.0/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch new file mode 100644 index 0000000000..6d09554b5d --- /dev/null +++ b/queue-7.0/drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch @@ -0,0 +1,92 @@ +From 6dddf4c53e8a54295d7b483c05bd72f8f196d7d3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 12:38:37 +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 + +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: # v5.15+ +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com +(cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_psr.c | 35 +++++++++++++++++++++--- + 1 file changed, 31 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index aa2ef49afa67a9..6709c434beb990 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -1357,9 +1357,35 @@ static bool psr2_granularity_check(struct intel_crtc_state *crtc_state, + 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; +@@ -1378,7 +1404,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d + 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, +@@ -1660,7 +1687,7 @@ static bool intel_sel_update_config_valid(struct intel_crtc_state *crtc_state, + conn_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; +-- +2.53.0 + diff --git a/queue-7.0/drm-i915-psr-read-intel-dpcd-workaround-register.patch b/queue-7.0/drm-i915-psr-read-intel-dpcd-workaround-register.patch new file mode 100644 index 0000000000..9478abcc64 --- /dev/null +++ b/queue-7.0/drm-i915-psr-read-intel-dpcd-workaround-register.patch @@ -0,0 +1,70 @@ +From baa645c232de6d5e84293966ab6c28cb861f8fa2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 May 2026 12:38:36 +0300 +Subject: drm/i915/psr: Read Intel DPCD workaround register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jouni Högander + +commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream. + +Read Intel DPCD workaround register and store it into +intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains +only PSR workaround for PSR2 SDP on prior scanline implementation. + +Signed-off-by: Jouni Högander +Reviewed-by: Suraj Kandpal +Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com +(cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c) +Signed-off-by: Tvrtko Ursulin +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_display_types.h | 1 + + drivers/gpu/drm/i915/display/intel_psr.c | 9 ++++++++- + 2 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h +index ced0e5a5989b85..3596ed0ff151bf 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_types.h ++++ b/drivers/gpu/drm/i915/display/intel_display_types.h +@@ -583,6 +583,7 @@ struct intel_connector { + + struct { + u8 dpcd[EDP_PSR_RECEIVER_CAP_SIZE]; ++ u8 intel_wa_dpcd; + + bool support; + bool su_support; +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +index 8a7075c4a2480f..aa2ef49afa67a9 100644 +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -43,6 +43,7 @@ + #include "intel_display_utils.h" + #include "intel_dmc.h" + #include "intel_dp.h" ++#include "intel_dpcd.h" + #include "intel_dp_aux.h" + #include "intel_dsb.h" + #include "intel_frontbuffer.h" +@@ -708,8 +709,14 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *co + connector->dp.psr_caps.su_support ? "" : "not "); + } + +- if (connector->dp.psr_caps.su_support) ++ if (connector->dp.psr_caps.su_support) { ++ ret = drm_dp_dpcd_read_byte(&intel_dp->aux, ++ INTEL_DPCD_INTEL_WA_REGISTER_CAPS, ++ &connector->dp.psr_caps.intel_wa_dpcd); ++ if (ret < 0) ++ return; + _psr_compute_su_granularity(intel_dp, connector); ++ } + } + + void intel_psr_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector) +-- +2.53.0 + diff --git a/queue-7.0/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch b/queue-7.0/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch new file mode 100644 index 0000000000..bb41a0bb32 --- /dev/null +++ b/queue-7.0/revert-x86-fpu-refine-and-simplify-the-magic-number-.patch @@ -0,0 +1,100 @@ +From 932b19138ce89cb0bb2f4e94f26f022114d0a2bd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 26 May 2026 20:50:43 +0000 +Subject: Revert "x86/fpu: Refine and simplify the magic number check during + signal return" + +From: Andrei Vagin + +[ Upstream commit 44eeff9bc467bc7d1fec34fc3f6001f385fe462c ] + +This reverts + + dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return"). + +The aforementioned commit broke applications that construct signal frames in +userspace (such as CRIU and gVisor) if the frame's xstate size is smaller than +the kernel's fpstate->user_size. + +Furthermore, this introduces a critical issue for checkpoint/restore tools +like CRIU. If a process is checkpointed while inside a signal handler, its +stack contains a signal frame formatted according to the source host's xstate +capabilities. + +If that process is later restored on a destination host with larger xstate +capabilities (e.g., a newer CPU with more features enabled, resulting in +a larger fpstate->user_size), the kernel will look for FP_XSTATE_MAGIC2 at the +destination host's larger user_size offset instead of the offset encoded in +the frame's fx_sw->xstate_size. + +This causes the magic2 check to fail, forcing sigreturn to silently fall back +to "FX-only" mode. Upon return from the signal handler, the process's extended +state is reset to initial values instead of being restored, leading to silent +data corruption. + +The aforementioned commit cited + + d877550eaf2d ("x86/fpu: Stop relying on userspace for info to fault in xsave buffer") + +as justification to stop relying on userspace for the magic number check. + +However, these two changes are fundamentally different. The last one only +changed how much memory the kernel ensures is paged-in before running XRSTOR +to prevent an infinite loop. It did not change the signal frame format or how +the layout is validated. + +Reverting this change restores the use of fx_sw->xstate_size for +locating magic2 and restores the necessary sanity checks, ensuring that +the signal frame remains self-describing and portable. + + [ bp: Massage commit message. ] + +Fixes: dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return") +Signed-off-by: Andrei Vagin +Signed-off-by: Borislav Petkov (AMD) +Acked-by: Chang S. Bae +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/all/20260429000623.3356606-1-avagin@google.com +Signed-off-by: Sasha Levin +--- + arch/x86/kernel/fpu/signal.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c +index c3ec2512f2bbe4..20b638c507ca2d 100644 +--- a/arch/x86/kernel/fpu/signal.c ++++ b/arch/x86/kernel/fpu/signal.c +@@ -27,14 +27,19 @@ + static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf, + struct _fpx_sw_bytes *fx_sw) + { ++ int min_xstate_size = sizeof(struct fxregs_state) + ++ sizeof(struct xstate_header); + void __user *fpstate = fxbuf; + unsigned int magic2; + + if (__copy_from_user(fx_sw, &fxbuf->sw_reserved[0], sizeof(*fx_sw))) + return false; + +- /* Check for the first magic field */ +- if (fx_sw->magic1 != FP_XSTATE_MAGIC1) ++ /* Check for the first magic field and other error scenarios. */ ++ if (fx_sw->magic1 != FP_XSTATE_MAGIC1 || ++ fx_sw->xstate_size < min_xstate_size || ++ fx_sw->xstate_size > x86_task_fpu(current)->fpstate->user_size || ++ fx_sw->xstate_size > fx_sw->extended_size) + goto setfx; + + /* +@@ -43,7 +48,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf, + * fpstate layout with out copying the extended state information + * in the memory layout. + */ +- if (__get_user(magic2, (__u32 __user *)(fpstate + x86_task_fpu(current)->fpstate->user_size))) ++ if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size))) + return false; + + if (likely(magic2 == FP_XSTATE_MAGIC2)) +-- +2.53.0 + diff --git a/queue-7.0/series b/queue-7.0/series index 00227d5d0b..2a7f2629da 100644 --- a/queue-7.0/series +++ b/queue-7.0/series @@ -110,3 +110,7 @@ ipv6-fix-possible-infinite-loop-in-rt6_fill_node.patch ipv6-fix-possible-infinite-loop-in-fib6_select_path.patch net-skbuff-fix-pskb_carve-leaking-zcopy-pages.patch revert-ipv6-preserve-insertion-order-for-same-scope-.patch +revert-x86-fpu-refine-and-simplify-the-magic-number-.patch +drm-i915-psr-add-defininitions-for-intel_wa_register.patch +drm-i915-psr-read-intel-dpcd-workaround-register.patch +drm-i915-psr-apply-intel-dpcd-workaround-when-sdp-on.patch