5.10.259-rc1 review
Subsystems (4)
Greg Kroah-Hartman
maintainerPATCH 135/342 netfilter: conntrack_irc: fix possible out-of-bounds read
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 66eba0ffce3b7e11449946b4cbbef8ea36112f56 ]
When parsing fails after we've matched the command string we should bail out instead of trying to match a different command.
This helper should be deprecated, given prevalence of TLS I doubt it has any relevance in 2026.
--- net/netfilter/nf_conntrack_irc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/netfilter/nf_conntrack_irc.c +2 -2
@@ -199,7 +199,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,if (parse_dcc (data, data_limit, &dcc_ip, &dcc_port, &addr_beg_p, &addr_end_p)) {pr_debug ("unable to parse dcc command\n" ); -continue ; + goto out; } pr_debug("DCC bound ip/port: %pI4:%u\n",
@@ -213,7 +213,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,net_warn_ratelimited ("Forged DCC command from %pI4: %pI4:%u\n" , &tuple->src.u3.ip, &dcc_ip, dcc_port); -continue ; + goto out; } exp =nf_ct_expect_alloc (ct);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 136/342 netfilter: bridge: make ebt_snat ARP rewrite writable
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yiming Qian <[email protected]>
[ Upstream commit 67ba971ae02514d85818fe0c32549ab4bfa3bf49 ]
The ebtables SNAT target keeps the Ethernet source address rewrite behind skb_ensure_writable(skb, 0). This is intentional: at the bridge ebtables hooks the Ethernet header is addressed through skb_mac_header()/eth_hdr(), while skb->data points at the Ethernet payload. Asking skb_ensure_writable() for ETH_HLEN bytes would check the payload, not the Ethernet header, and would reintroduce the small packet regression fixed by commit 63137bc5882a.
However, the optional ARP sender hardware address rewrite is different. It writes through skb_store_bits() at an offset relative to skb->data:
skb_store_bits(skb, sizeof(struct arphdr), info->mac, ETH_ALEN)
skb_header_pointer() only safely reads the ARP header; it does not make the later sender hardware address range writable. If that range is still held in a nonlinear skb fragment backed by a splice-imported file page, skb_store_bits() maps the frag page and copies the new MAC address directly into it.
Ensure the ARP SHA range is writable before reading the ARP header and before calling skb_store_bits().
--- net/bridge/netfilter/ebt_snat.c | 3 +++ 1 file changed, 3 insertions(+)
net/bridge/netfilter/ebt_snat.c +3 -0
@@ -31,6 +31,9 @@ ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par)const struct arphdr *ap;struct arphdr _ah; +if (skb_ensure_writable (skb,sizeof (_ah) +ETH_ALEN )) +return EBT_DROP ; + ap =skb_header_pointer (skb,0 ,sizeof (_ah), &_ah);if (ap ==NULL )return EBT_DROP ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 137/342 dm cache policy smq: check allocation under invalidate lock
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
[ Upstream commit d3f0a606b9f278ece8a0df626ded9c4044071235 ]
commit 2d1f7b65f5de ("dm cache policy smq: fix missing locks in invalidating cache blocks") added mq->lock around the destructive part of smq_invalidate_mapping(), but left the e->allocated check outside the critical section.
That leaves a check-then-act race. Two concurrent invalidators can both observe e->allocated as true before either of them takes mq->lock. The first invalidator that acquires the lock removes the entry from the queues and hash table and then calls free_entry(), which clears e->allocated and puts the entry back on the free list. The second invalidator can then acquire mq->lock and continue with the stale result of the unlocked check.
This can corrupt the SMQ queues or hash table by deleting an entry that is no longer on those structures. It can also hit the allocation check in free_entry() when the same entry is freed again.
Move the allocation check under mq->lock so the predicate and the destructive operations are serialized by the same lock.
--- drivers/md/dm-cache-policy-smq.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
drivers/md/dm-cache-policy-smq.c +8 -4
@@ -1585,18 +1585,22 @@ static int smq_invalidate_mapping(struct dm_cache_policy *p, dm_cblock_t cblock)struct smq_policy *mq =to_smq_policy (p);struct entry *e =get_entry (&mq->cache_alloc,from_cblock (cblock));unsigned long flags; - -if (!e->allocated) -return -ENODATA ; +int r =0 ;spin_lock_irqsave (&mq->lock, flags); +if (!e->allocated) { + r = -ENODATA ; + goto out; + }// FIXME: what if this block has pending background work? del_queue (mq, e);h_remove (&mq->table, e);free_entry (&mq->cache_alloc, e); + +out:spin_unlock_irqrestore (&mq->lock, flags); -return 0 ; +return r; }static uint32_t smq_get_hint (struct dm_cache_policy *p,dm_cblock_t cblock)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 138/342 net/sched: act_api: use RCU with deferred freeing for action lifecycle
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 5057e1aca011e51ef51498c940ef96f3d3e8a305 ]
When NEWTFILTER and DELFILTER are run concurrently it is possible to create a race with an associated action.
Let's illustrate with CPU0 running NEWTFILTER and CPU1 running DELFILTER:
0: mutex_lock() <-- holds the idr lock
0: rcu_read_lock()
0: p = idr_find(idr, index) <-- action p is valid (RCU protects IDR) 0: mutex_unlock() <-- releases the idr lock
1: refcount_dec_and_mutex_lock() <-- refcnt 1->0, mutex held
1: idr_remove(idr, index) <-- Action removed from IDR
1: mutex_unlock() <-- mutex released allowing us to delete the action 1: tcf_action_cleanup(p); kfree(p) <-- Kfrees p immediately, no deferral 0: refcount_inc_not_zero(&p->tcfa_refcnt) <-- ouch, UAF p points to freed memory
This patch fixes the race condition between NEWTFILTER and DELFILTER by adding struct rcu_head to tc_action used in the deferral and introducing a call_rcu() in the delete path to defer the final kfree().
Note: this is a revert of commit d7fb60b9cafb ("net_sched: get rid of tcfa_rcu") but also modernization/simplification to directly use kfree_rcu().
Let's illustrate the new restored code path:
0: rcu_read_lock()
1: refcount_dec_and_mutex_lock() <-- refcnt 1->0, mutex held
1: idr_remove(idr, index)
1: mutex_unlock()
1: call_rcu(&p->tcfa_rcu, tcf_action_rcu_free) <-- defer kfree after grace period 0: p = idr_find(idr, index)
0: refcount_inc_not_zero(&p->tcfa_refcnt) <-- fails, refcnt already 0 1: rcu_read_unlock() <-- release so freeing can run after grace period
After CPU1 calls idr_remove(), the object is no longer reachable through the IDR. CPU0's subsequent idr_find() will return NULL, and even if it still held a stale pointer, the immediate kfree() is now deferred until after the RCU grace period, so no UAF can occur.
--- include/net/act_api.h | 1 + net/sched/act_api.c | 7 +------ 2 files changed, 2 insertions(+), 6 deletions(-)
include/net/act_api.h +1 -0
@@ -41,6 +41,7 @@ struct tc_action {struct tc_cookie __rcu *act_cookie;struct tcf_chain __rcu *goto_chain;u32 tcfa_flags; +struct rcu_head tcfa_rcu;u8 hw_stats;u8 used_hw_stats;bool used_hw_stats_valid;
net/sched/act_api.c +1 -6
@@ -93,11 +93,6 @@ struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action, }EXPORT_SYMBOL (tcf_action_set_ctrlact); -/* XXX: For standalone actions, we don't need a RCU grace period either, because -* actions are always connected to filters and filters are already destroyed in -* RCU callbacks, so after a RCU grace period actions are already disconnected -* from filters. Readers later can not find us. -*/ static void free_tcf (struct tc_action *p) {struct tcf_chain *chain =rcu_dereference_protected (p->goto_chain,1 );
@@ -110,7 +105,7 @@ static void free_tcf(struct tc_action *p)if (chain)tcf_chain_put_by_act (chain); -kfree (p); +kfree_rcu (p, tcfa_rcu); }static void tcf_action_cleanup (struct tc_action *p)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 139/342 6lowpan: fix off-by-one in multicast context address compression
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
[ Upstream commit 2a58899d11009bffc7b4b32a571858f381121837 ]
The second memcpy in lowpan_iphc_mcast_ctx_addr_compress() uses &data[1] as destination and &ipaddr->s6_addr[11] as source, but both should be offset by one: &data[2] and &ipaddr->s6_addr[12] respectively.
This off-by-one has two consequences:
1. data[1] is overwritten with s6_addr[11], corrupting the RIID field in the compressed multicast address
2. data[5] is never written, so uninitialized kernel stack memory is transmitted over the network via lowpan_push_hc_data(), leaking kernel stack contents
The correct inline data layout must match what the decompression function lowpan_uncompress_multicast_ctx_daddr() expects:
data[0..1] = s6_addr[1..2] (flags/scope + RIID) data[2..5] = s6_addr[12..15] (group ID)
Also zero-initialize the data array as a defensive measure against similar bugs in the future.
--- net/6lowpan/iphc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/6lowpan/iphc.c +2 -2
@@ -1086,12 +1086,12 @@ static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr,const struct lowpan_iphc_ctx *ctx, const struct in6_addr *ipaddr) { - u8 data[6]; + u8 data[6] = {};/* flags/scope, reserved (RIID) */ memcpy (data, &ipaddr->s6_addr[1 ],2 );/* group ID */ -memcpy (&data[1 ], &ipaddr->s6_addr[11 ],4 ); +memcpy (&data[2 ], &ipaddr->s6_addr[12 ],4 );lowpan_push_hc_data (hc_ptr, data,6 );return LOWPAN_IPHC_DAM_00 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 140/342 drm/imx: Fix three kernel-doc warnings in dcss-scaler.c
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yicong Hui <[email protected]>
[ Upstream commit ae0383e5a9a4b12d68c76c4769857def4665deff ]
Fix the following W=1 kerneldoc warnings by adding the missing parameter descriptions for @phase0_identity and @nn_interpolation in dcss_scaler_filter_design() and @phase0_identity in dcss_scaler_gaussian_filter()
Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:173 function parameter 'phase0_identity' not described in 'dcss_scaler_gaussian_filter'
Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:270 function parameter 'phase0_identity' not described in 'dcss_scaler_filter_design'
Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:270 function parameter 'nn_interpolation' not described in 'dcss_scaler_filter_design'
--- drivers/gpu/drm/imx/dcss/dcss-scaler.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/imx/dcss/dcss-scaler.c +3 -0
@@ -164,6 +164,7 @@ static int exp_approx_q(int x)* dcss_scaler_gaussian_filter() - Generate gaussian prototype filter. * @fc_q: fixed-point cutoff frequency normalized to range [0, 1] * @use_5_taps: indicates whether to use 5 taps or 7 taps +* @phase0_identity: whether to override phase 0 coefficients with identity filter * @coef: output filter coefficients */ static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps,
@@ -249,7 +250,9 @@ static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps,* @src_length: length of input * @dst_length: length of output * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps +* @phase0_identity: whether to override phase 0 coefficients with identity filter * @coef: output coefficients +* @nn_interpolation: whether to use nearest neighbor instead of gaussian filter */ static void dcss_scaler_filter_design(int src_length, int dst_length, bool use_5_taps, bool phase0_identity,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 141/342 pcnet32: stop holding device spin lock during napi_complete_done
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oscar Maes <[email protected]>
[ Upstream commit 73bf3cca7de6a73f53b6a52dc3b1c82ae5667a4d ]
napi_complete_done may call gro_flush_normal (though not currently, as GRO is unsupported at the moment), which may result in packet TX. This will eventually result in calling pcnet32_start_xmit - resulting in a deadlock while trying to re-acquire the already locked spin lock.
It is safe to split the spinlock block into two, because the hardware registers are still protected from concurrent access, and the two blocks perform unrelated operations that don't need to happen atomically.
--- drivers/net/ethernet/amd/pcnet32.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/ethernet/amd/pcnet32.c +3 -1
@@ -1399,8 +1399,10 @@ static int pcnet32_poll(struct napi_struct *napi, int budget)pcnet32_restart (dev,CSR0_START );netif_wake_queue (dev); } +spin_unlock_irqrestore (&lp->lock, flags);if (work_done < budget &&napi_complete_done (napi, work_done)) { +spin_lock_irqsave (&lp->lock, flags);/* clear interrupt masks */ val = lp->a->read_csr (ioaddr,CSR3 ); val &=0x00ff ;
@@ -1408,9 +1410,9 @@ static int pcnet32_poll(struct napi_struct *napi, int budget)/* Set interrupt enable. */ lp->a->write_csr (ioaddr,CSR0 ,CSR0_INTEN ); +spin_unlock_irqrestore (&lp->lock, flags); } -spin_unlock_irqrestore (&lp->lock, flags);return work_done; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 142/342 net: garp: fix unsigned integer underflow in garp_pdu_parse_attr
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
[ Upstream commit 16e408e607a94b646fb14a2a98422c6877ae4b3c ]
The receive-side GARP attribute parser computes dlen with reversed operands:
dlen = sizeof(*ga) - ga->len;
ga->len is the on-wire attribute length and includes the GARP attribute header. For normal attributes with data, ga->len is larger than sizeof(*ga), so the subtraction underflows in unsigned arithmetic.
The resulting value is later passed to garp_attr_lookup(), whose length argument is u8. After truncation, the parsed data length usually no longer matches the length stored for locally registered attributes, so received Join/Leave events are ignored. This breaks the GARP receive path for common attributes, such as GVRP VLAN registration attributes.
Compute the data length as the attribute length minus the header length.
--- net/802/garp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/802/garp.c +1 -1
@@ -452,7 +452,7 @@ static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb,if (!pskb_may_pull (skb, ga->len))return -1 ;skb_pull (skb, ga->len); - dlen =sizeof (*ga) - ga->len; + dlen = ga->len -sizeof (*ga);if (attrtype > app->app->maxattr)return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 143/342 net: lan743x: permit VLAN-tagged packets up to configured MTU
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Thompson <[email protected]>
[ Upstream commit 8173d22b211f615015f7b35f48ab11a6dd78dc99 ]
VLAN-tagged interfaces on lan743x devices were previously unreachable via SSH and failed to respond to large ping packets (e.g. "ping -s 1469" given MTU=1500). In these scenarios, "ethtool -S" reports non-zero "RX Oversize Frame Errors". According to Microchip AN2948, the MAC_RX FSE (VLAN field size enforcement) bit determines whether frames with VLAN tags exceeding the base MTU plus tag length are discarded.
The driver must set the MAC_RX.FSE bit before setting MAC_RX.RXEN to allow VLAN-tagged frames up to the interface MTU, preventing them from being treated as oversized. As a result, both the base and VLAN-tagged interfaces can use the same MTU without receive errors.
--- drivers/net/ethernet/microchip/lan743x_main.c | 32 +++++++++++++++++++ drivers/net/ethernet/microchip/lan743x_main.h | 1 + 2 files changed, 33 insertions(+)
drivers/net/ethernet/microchip/lan743x_main.c +32 -0
@@ -799,6 +799,36 @@ static void lan743x_mac_set_address(struct lan743x_adapter *adapter, "MAC address set to %pM\n", addr); } +static void lan743x_mac_rx_enable_fse (struct lan743x_adapter *adapter) +{ +u32 mac_rx; +bool rxen; + + mac_rx =lan743x_csr_read (adapter,MAC_RX ); +if (mac_rx &MAC_RX_FSE_ ) +return ; + + rxen = mac_rx &MAC_RX_RXEN_ ; +if (rxen) { + mac_rx &= ~MAC_RX_RXEN_ ; +lan743x_csr_write (adapter,MAC_RX , mac_rx); +lan743x_csr_wait_for_bit (adapter,MAC_RX ,MAC_RX_RXD_ , +1 ,1000 ,20000 ,100 ); + } + +/* Per AN2948, hardware prevents modification of the FSE bit while the +* MAC receiver is enabled (RXEN bit set). Use separate register write +* to assert the FSE bit before enabling the RXEN bit in MAC_RX +*/ + mac_rx |=MAC_RX_FSE_ ; +lan743x_csr_write (adapter,MAC_RX , mac_rx); + +if (rxen) { + mac_rx |=MAC_RX_RXEN_ ; +lan743x_csr_write (adapter,MAC_RX , mac_rx); + } +} +static int lan743x_mac_init (struct lan743x_adapter *adapter) {bool mac_address_valid = true;
@@ -838,6 +868,8 @@ static int lan743x_mac_init(struct lan743x_adapter *adapter)lan743x_mac_set_address (adapter, adapter->mac_address);ether_addr_copy (netdev->dev_addr, adapter->mac_address); +lan743x_mac_rx_enable_fse (adapter); +return 0 ; }
drivers/net/ethernet/microchip/lan743x_main.h +1 -0
@@ -118,6 +118,7 @@#define MAC_RX (0x104)#define MAC_RX_MAX_SIZE_SHIFT_ (16)#define MAC_RX_MAX_SIZE_MASK_ (0x3FFF0000) +#define MAC_RX_FSE_ BIT(2)#define MAC_RX_RXD_ BIT(1)#define MAC_RX_RXEN_ BIT(0)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 144/342 Bluetooth: RFCOMM: hold listener socket in rfcomm_connect_ind()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Cen <[email protected]>
[ Upstream commit 43c441edacf953b39517a44f5e5e10a93618b226 ]
rfcomm_get_sock_by_channel() scans rfcomm_sk_list under the list lock, but returns the selected listener after dropping that lock without taking a reference. rfcomm_connect_ind() then locks the listener, queues a child socket on it, and may notify it after unlocking it.
The buggy scenario involves two paths, with each column showing the order within that path:
rfcomm_connect_ind(): listener close:
1. Find parent in 1. close() enters
rfcomm_get_sock_by_channel() rfcomm_sock_release().
2. Drop rfcomm_sk_list.lock 2. rfcomm_sock_shutdown()
without pinning parent. closes the listener.
3. Call lock_sock(parent) and 3. rfcomm_sock_kill()
bt_accept_enqueue(parent, unlinks and puts parent.
sk, true).
4. Read parent flags and may 4. parent can be freed.
call sk_state_change().
If close wins the race, parent can be freed before rfcomm_connect_ind() reaches lock_sock(), bt_accept_enqueue(), or the deferred-setup callback.
Take a reference on the listener before leaving rfcomm_sk_list.lock. After lock_sock() succeeds, recheck that it is still in BT_LISTEN before queueing a child, cache the deferred-setup bit while the parent is locked, and drop the reference after the last parent use.
KASAN reported a slab-use-after-free in lock_sock_nested() from rfcomm_connect_ind(), with the freeing stack going through rfcomm_sock_kill() and rfcomm_sock_release().
--- net/bluetooth/rfcomm/sock.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-)
net/bluetooth/rfcomm/sock.c +22 -4
@@ -122,7 +122,7 @@ static struct sock *__rfcomm_get_listen_sock_by_addr(u8 channel, bdaddr_t *src) }/* Find socket with channel and source bdaddr. -* Returns closest match. +* Returns closest match with an extra reference held. */ static struct sock *rfcomm_get_sock_by_channel (int state,u8 channel,bdaddr_t *src) {
@@ -136,15 +136,25 @@ static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *if (rfcomm_pi (sk)->channel == channel) {/* Exact match. */ -if (!bacmp (&rfcomm_pi (sk)->src, src)) +if (!bacmp (&rfcomm_pi (sk)->src, src)) { +sock_hold (sk);break ; + }/* Closest match */ -if (!bacmp (&rfcomm_pi (sk)->src,BDADDR_ANY )) +if (!bacmp (&rfcomm_pi (sk)->src,BDADDR_ANY )) { +if (sk1) +sock_put (sk1); + sk1 = sk; +sock_hold (sk1); + } } } +if (sk && sk1) +sock_put (sk1); +read_unlock (&rfcomm_sk_list.lock);return sk ? sk : sk1;
@@ -933,6 +943,7 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc * {struct sock *sk, *parent;bdaddr_t src, dst; +bool defer_setup = false;int result =0 ;BT_DBG ("session %p channel %d" , s, channel);
@@ -946,6 +957,11 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *bh_lock_sock (parent); +if (parent->sk_state !=BT_LISTEN ) + goto done; + + defer_setup =test_bit (BT_SK_DEFER_SETUP , &bt_sk (parent)->flags); +/* Check for backlog size */ if (sk_acceptq_is_full (parent)) {BT_DBG ("backlog full %d" , parent->sk_ack_backlog);
@@ -973,9 +989,11 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc * done:bh_unlock_sock (parent); -if (test_bit (BT_SK_DEFER_SETUP , &bt_sk (parent)->flags)) +if (defer_setup) parent->sk_state_change (parent); +sock_put (parent); +return result; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 145/342 Bluetooth: MGMT: validate advertising TLV before type checks
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Cen <[email protected]>
[ Upstream commit de23fb62259aa01d294f77238ae3b835eb674413 ]
tlv_data_is_valid() reads each advertising data field length from data[i], then inspects data[i + 1] for managed EIR types before checking that the current field still fits inside the supplied buffer.
A malformed field whose length byte is the last byte of the buffer can therefore make the parser read one byte past the advertising data.
KASAN reported the following when a malformed MGMT_OP_ADD_ADVERTISING request reached that path:
BUG: KASAN: vmalloc-out-of-bounds in tlv_data_is_valid()
Read of size 1
Call trace:
tlv_data_is_valid()
add_advertising()
hci_mgmt_cmd()
hci_sock_sendmsg()Move the existing element-length check before any type-octet inspection so each non-empty element is proven to contain its type byte before the parser looks at data[i + 1].
--- net/bluetooth/mgmt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
net/bluetooth/mgmt.c +6 -6
@@ -7364,6 +7364,12 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,if (!cur_len)continue ; +/* If the current field length would exceed the total data +* length, then it's invalid. +*/ +if (i + cur_len >= len) +return false; +if (data[i +1 ] ==EIR_FLAGS && (!is_adv_data ||flags_managed (adv_flags)))return false;
@@ -7380,12 +7386,6 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,if (data[i +1 ] ==EIR_APPEARANCE &&appearance_managed (adv_flags))return false; - -/* If the current field length would exceed the total data -* length, then it's invalid. -*/ -if (i + cur_len >= len) -return false; }return true;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 146/342 ipv4: restrict IPOPT_SSRR and IPOPT_LSRR options
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit d3915a1f5a4bc0ac911032903c3c6ab8df9fcc7c ]
This patch restricts setting Loose Source and Record Route (LSRR) and Strict Source and Record Route (SSRR) IP options to users with CAP_NET_RAW capability.
This prevents unprivileged applications from forcing packets to route through attacker-controlled nodes to leak TCP ISN and possibly other protocol information.
While LSRR and SSRR are commonly filtered in many network environments, they may still be supported and forwarded along some network paths.
RFC 7126 (Recommendations on Filtering of IPv4 Packets Containing IPv4 Options) recommend to drop these options in 4.3 and 4.4.
--- net/ipv4/ip_options.c | 4 ++++ 1 file changed, 4 insertions(+)
net/ipv4/ip_options.c +4 -0
@@ -543,6 +543,10 @@ int ip_options_get(struct net *net, struct ip_options_rcu **optp,kfree (opt);return -EINVAL ; } +if (opt->opt.srr && !ns_capable (net->user_ns,CAP_NET_RAW )) { +kfree (opt); +return -EPERM ; + }kfree (*optp); *optp = opt;return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 147/342 ieee802154: 6lowpan: only accept IPv6 packets in lowpan_xmit()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 3a5f3f7aff18bcc36a57839cf50cf0cc8de707f3 ]
The aoe driver (or similar) generates a non-IPv6 packet (e.g., ETH_P_AOE) and queues it for transmission via dev_queue_xmit() on a 6LoWPAN interface (configured by the user or test case).
Since the packet is not IPv6, the 6LoWPAN header_ops->create function (lowpan_header_create or header_create) returns early without initializing the lowpan_addr_info structure in the skb headroom.
In the transmit function (lowpan_xmit), the driver calls lowpan_header (or setup_header) which unconditionally copies and uses the lowpan_addr_info from the headroom, which contains uninitialized data.
Fix this by dropping non IPv6 packets.
A similar fix is needed in net/bluetooth/6lowpan.c bt_xmit().
--- net/ieee802154/6lowpan/tx.c | 5 +++++ 1 file changed, 5 insertions(+)
net/ieee802154/6lowpan/tx.c +5 -0
@@ -255,6 +255,11 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)pr_debug ("package xmit\n" ); +if (skb->protocol !=htons (ETH_P_IPV6 )) { +kfree_skb (skb); +return NET_XMIT_DROP ; + } +WARN_ON_ONCE (skb->len >IPV6_MIN_MTU );/* We must take a copy of the skb before we modify/replace the ipv6
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 148/342 net/802/mrp: fix vector attribute parsing in mrp_pdu_parse_vecattr
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
[ Upstream commit 7561c7fbc694308da73300f036719e63e42bf0b4 ]
In mrp_pdu_parse_vecattr(), vector attribute events are encoded three per byte and valen tracks the number of events left to process.
The parser decrements valen after processing the first and second events from each event byte, but not after processing the third one. When valen is exactly a multiple of three, the loop continues after the last valid event and consumes the next byte as a new event byte, applying a spurious event to the MRP applicant state.
Additionally, when valen is zero the parser unconditionally consumes attrlen bytes as FirstValue and advances the offset, even though per IEEE 802.1ak a VectorAttribute with only a LeaveAllEvent has valen of zero and no FirstValue or Vector fields. This corrupts the offset for subsequent PDU parsing.
Also, when valen exceeds three the loop crosses byte boundaries but the attribute value is not incremented between the last event of one byte and the first event of the next. This causes the first event of the next byte to use the same attribute value as the third event rather than the next consecutive value.
Decrement valen after processing the third event, skip FirstValue consumption when valen is zero, and increment the attribute value at the end of each loop iteration.
--- net/802/mrp.c | 9 +++++++++ 1 file changed, 9 insertions(+)
net/802/mrp.c +9 -0
@@ -702,6 +702,12 @@ static int mrp_pdu_parse_vecattr(struct mrp_applicant *app, valen =be16_to_cpu (get_unaligned (&mrp_cb (skb)->vah->lenflags) &MRP_VECATTR_HDR_LEN_MASK ); +/* If valen is 0, only a LeaveAllEvent is present; FirstValue and +* Vector fields are absent per IEEE 802.1ak. +*/ +if (valen ==0 ) +return 0 ; +/* The VectorAttribute structure in a PDU carries event information * about one or more attributes having consecutive values. Only the * value for the first attribute is contained in the structure. So
@@ -752,6 +758,9 @@ static int mrp_pdu_parse_vecattr(struct mrp_applicant *app, vaevents %= __MRP_VECATTR_EVENT_MAX; vaevent = vaevents;mrp_pdu_parse_vecattr_event (app, skb, vaevent); + valen--; +mrp_attrvalue_inc (mrp_cb (skb)->attrvalue, +mrp_cb (skb)->mh->attrlen); }return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 149/342 sctp: purge outqueue on stale COOKIE-ECHO handling
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xin Long <[email protected]>
[ Upstream commit e374b22e9b07b72a25909621464ff74096151bfb ]
sctp_stream_update() is only invoked when the association is moved into COOKIE_WAIT during association setup/reconfiguration. In this path, the outbound stream scheduler state (stream->out_curr) is expected to be clean, since no user data should have been transmitted yet unless the state machine has already partially progressed.
However, a corner case exists in sctp_sf_do_5_2_6_stale(): when a Stale Cookie ERROR is received, the association is rolled back from COOKIE_ECHOED to COOKIE_WAIT. In this scenario, user data may already have been queued and even bundled with the COOKIE-ECHO chunk.
During the rollback, sctp_stream_update() frees the old stream table and installs a new one, but it does not invalidate stream->out_curr. As a result, out_curr may still point to a freed sctp_stream_out entry from the previous stream state.
Later, SCTP scheduler dequeue paths (FCFS, RR, PRIO, etc.) rely on stream->out_curr->ext, which can lead to use-after-free once the old stream state has been released via sctp_stream_free().
This results in crashes such as (reported by Yuqi):
BUG: KASAN: slab-use-after-free in sctp_sched_fcfs_dequeue+0x13a/0x140
Read of size 8 at addr ff1100004d4d3208 by task mini_poc/9312
CPU: 1 UID: 1001 PID: 9312 Comm: mini_poc Not tainted
7.1.0-rc1-00305-gbd3a4795d574 #5 PREEMPT(full)
sctp_sched_fcfs_dequeue+0x13a/0x140
sctp_outq_flush+0x1603/0x33e0
sctp_do_sm+0x31c9/0x5d30
sctp_assoc_bh_rcv+0x392/0x6f0
sctp_inq_push+0x1db/0x270
sctp_rcv+0x138d/0x3c10Fix this by fully purging the association outqueue when handling the Stale Cookie case. This ensures all pending transmit and retransmit state is dropped, and any scheduler cached pointers are invalidated, making it safe to rebuild stream state during COOKIE_WAIT restart.
Updating only stream->out_curr would be insufficient, since queued and retransmittable data would still reference the old stream state and trigger later use-after-free in dequeue paths.
--- net/sctp/sm_statefuns.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
net/sctp/sm_statefuns.c +1 -5
@@ -2523,11 +2523,7 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale(*/ sctp_add_cmd_sf (commands,SCTP_CMD_DEL_NON_PRIMARY ,SCTP_NULL ()); -/* If we've sent any data bundled with COOKIE-ECHO we will need to -* resend -*/ -sctp_add_cmd_sf (commands,SCTP_CMD_T1_RETRAN , -SCTP_TRANSPORT (asoc->peer.primary_path)); +sctp_add_cmd_sf (commands,SCTP_CMD_PURGE_OUTQUEUE ,SCTP_NULL ());/* Cast away the const modifier, as we want to just * rerun it through as a sideffect.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 150/342 signal: clear JOBCTL_PENDING_MASK for caller in zap_other_threads()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksandr Nogikh <[email protected]>
[ Upstream commit 90918794a4e2c3b440f8fcf3847765a8b1d81b25 ]
When a multi-threaded process receives a stop signal (e.g., SIGSTOP), do_signal_stop() sets JOBCTL_STOP_PENDING and JOBCTL_STOP_CONSUME on all threads and sets signal->group_stop_count to the number of threads. If one of the threads concurrently calls execve(), de_thread() invokes zap_other_threads() to kill all other threads. zap_other_threads() aborts the pending group stop by resetting signal->group_stop_count to 0 and clears the JOBCTL_PENDING_MASK for all other threads. However, it fails to clear the job control flags for the calling thread.
When execve() completes, the calling thread returns to user mode and checks for pending signals. Seeing the stale JOBCTL_STOP_PENDING flag, it calls do_signal_stop(), which invokes task_participate_group_stop(). Since JOBCTL_STOP_CONSUME is still set, it attempts to decrement the already-zero signal->group_stop_count, triggering a warning:
sig->group_stop_count == 0
WARNING: CPU: 1 PID: 6475 at kernel/signal.c:373 task_participate_group_stop+0x215/0x2d0 Call Trace: <TASK> do_signal_stop+0x3be/0x5c0 kernel/signal.c:2619 get_signal+0xa8c/0x1330 kernel/signal.c:2884 arch_do_signal_or_restart+0xbc/0x840 arch/x86/kernel/signal.c:337 exit_to_user_mode_loop+0x8c/0x4d0 kernel/entry/common.c:98 do_syscall_64+0x33e/0xf80 arch/x86/entry/syscall_64.c:100 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK>
Fix this race condition by clearing the JOBCTL_PENDING_MASK for the calling thread in zap_other_threads(), ensuring it does not retain any stale job control state after the thread group is destroyed. This aligns with other functions that tear down a thread group and abort group stops, such as zap_process() and complete_signal(), which correctly clear these flags for all threads including the current one.
--- kernel/signal.c | 1 + 1 file changed, 1 insertion(+)
kernel/signal.c +1 -0
@@ -1351,6 +1351,7 @@ int zap_other_threads(struct task_struct *p)int count =0 ; p->signal->group_stop_count =0 ; +task_clear_jobctl_pending (p,JOBCTL_PENDING_MASK ); while_each_thread(p, t) {task_clear_jobctl_pending (t,JOBCTL_PENDING_MASK );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 151/342 time: Fix off-by-one in settimeofday() usec validation
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Naveen Kumar Chaudhary <[email protected]>
[ Upstream commit ce4abda5e12622f33450159e76c8f56d28d7f03d ]
The validation check uses '>' instead of '>=' when comparing tv_usec against USEC_PER_SEC, allowing the value 1000000 through. After conversion to nanoseconds (*= 1000), this produces tv_nsec == NSEC_PER_SEC, violating the timespec invariant that tv_nsec must be less than NSEC_PER_SEC.
Use '>=' to reject tv_usec values that are not in the valid range of 0 to 999999.
--- kernel/time/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/time/time.c +1 -1
@@ -207,7 +207,7 @@ SYSCALL_DEFINE2(settimeofday, struct __kernel_old_timeval __user *, tv, get_user(new_ts.tv_nsec, &tv->tv_usec))return -EFAULT ; -if (new_ts.tv_nsec >USEC_PER_SEC || new_ts.tv_nsec <0 ) +if (new_ts.tv_nsec >=USEC_PER_SEC || new_ts.tv_nsec <0 )return -EINVAL ; new_ts.tv_nsec *=NSEC_PER_USEC ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 152/342 ALSA: PCM: Fix wait queue list corruption in snd_pcm_drain() on linked streams
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ji'an Zhou <[email protected]>
[ Upstream commit 88fe2e3658726cb21ff2dcf9770bf672f9b9d31b ]
snd_pcm_drain() uses init_waitqueue_entry which does not clear entry.prev/next, and add_wait_queue with a conditional remove_wait_queue that is skipped when to_check is no longer in the group after concurrent UNLINK. The orphaned wait entry remains on the unlinked substream sleep queue. On the next drain iteration, add_wait_queue adds the entry to a new queue while still linked on the old one, corrupting both lists. A subsequent wake_up dereferences NULL at the func pointer (mapped from the spinlock at offset 0 of the misinterpreted wait_queue_head_t), causing a kernel panic.
Replace init_waitqueue_entry/add_wait_queue/conditional remove_wait_queue with init_wait_entry/prepare_to_wait/ finish_wait. init_wait_entry clears prev/next via INIT_LIST_HEAD on each iteration and sets autoremove_wake_function which auto-removes the entry on wake-up. finish_wait safely handles both the already-removed and still-queued cases.
--- sound/core/pcm_native.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
sound/core/pcm_native.c +3 -4
@@ -2161,9 +2161,8 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, drain_no_period_wakeup = to_check->no_period_wakeup; drain_rate = to_check->rate; drain_bufsz = to_check->buffer_size; -init_waitqueue_entry (&wait, current); -set_current_state (TASK_INTERRUPTIBLE ); -add_wait_queue (&to_check->sleep, &wait); +init_wait_entry (&wait,0 ); +prepare_to_wait (&to_check->sleep, &wait,TASK_INTERRUPTIBLE );snd_pcm_stream_unlock_irq (substream);if (drain_no_period_wakeup) tout =MAX_SCHEDULE_TIMEOUT ;
@@ -2181,7 +2180,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, group =snd_pcm_stream_group_ref (substream);snd_pcm_group_for_each_entry (s, substream) {if (s->runtime == to_check) { -remove_wait_queue (&to_check->sleep, &wait); +finish_wait (&to_check->sleep, &wait);break ; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 153/342 ext4: validate p_idx bounds in ext4_ext_correct_indexes
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejas Bharambe <[email protected]>
commit 2acb5c12ebd860f30e4faf67e6cc8c44ddfe5fe8 upstream.
ext4_ext_correct_indexes() walks up the extent tree correcting index entries when the first extent in a leaf is modified. Before accessing path[k].p_idx->ei_block, there is no validation that p_idx falls within the valid range of index entries for that level.
If the on-disk extent header contains a corrupted or crafted eh_entries value, p_idx can point past the end of the allocated buffer, causing a slab-out-of-bounds read.
Fix this by validating path[k].p_idx against EXT_LAST_INDEX() at both access sites: before the while loop and inside it. Return -EFSCORRUPTED if the index pointer is out of range, consistent with how other bounds violations are handled in the ext4 extent tree code.
[ Alexey: Adapt goto clean to break because the clean error path is not
present in linux-5.10.y and linux-5.15.y. ]
--- fs/ext4/extents.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
fs/ext4/extents.c +15 -0
@@ -1731,6 +1731,13 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode, err =ext4_ext_get_access (handle, inode, path + k);if (err)return err; +if (unlikely (path[k].p_idx >EXT_LAST_INDEX (path[k].p_hdr))) { +EXT4_ERROR_INODE (inode, +"path[%d].p_idx %p > EXT_LAST_INDEX %p" , + k, path[k].p_idx, +EXT_LAST_INDEX (path[k].p_hdr)); +return -EFSCORRUPTED ; + } path[k].p_idx->ei_block = border; err =ext4_ext_dirty (handle, inode, path + k);if (err)
@@ -1743,6 +1750,14 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode, err =ext4_ext_get_access (handle, inode, path + k);if (err)break ; +if (unlikely (path[k].p_idx >EXT_LAST_INDEX (path[k].p_hdr))) { +EXT4_ERROR_INODE (inode, +"path[%d].p_idx %p > EXT_LAST_INDEX %p" , + k, path[k].p_idx, +EXT_LAST_INDEX (path[k].p_hdr)); + err = -EFSCORRUPTED ; +break ; + } path[k].p_idx->ei_block = border; err =ext4_ext_dirty (handle, inode, path + k);if (err)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 154/342 bonding: limit BOND_MODE_8023AD to Ethernet devices
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit c84fcb79e5dbde0b8d5aeeaf04282d2149aebcf6 upstream.
BOND_MODE_8023AD makes sense for ARPHRD_ETHER only.
syzbot reported:
BUG: KASAN: global-out-of-bounds in __hw_addr_create net/core/dev_addr_lists.c:63 [inline] BUG: KASAN: global-out-of-bounds in __hw_addr_add_ex+0x25d/0x760 net/core/dev_addr_lists.c:118 Read of size 16 at addr ffffffff8bf94040 by task syz.1.3580/19497 CPU: 1 UID: 0 PID: 19497 Comm: syz.1.3580 Tainted: G L syzkaller #0 PREEMPT(full) Tainted: [L]=SOFTLOCKUP Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025 Call Trace: <TASK> dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xca/0x240 mm/kasan/report.c:482 kasan_report+0x118/0x150 mm/kasan/report.c:595 check_region_inline mm/kasan/generic.c:-1 [inline] kasan_check_range+0x2b0/0x2c0 mm/kasan/generic.c:200 __asan_memcpy+0x29/0x70 mm/kasan/shadow.c:105 __hw_addr_create net/core/dev_addr_lists.c:63 [inline] __hw_addr_add_ex+0x25d/0x760 net/core/dev_addr_lists.c:118 __dev_mc_add net/core/dev_addr_lists.c:868 [inline] dev_mc_add+0xa1/0x120 net/core/dev_addr_lists.c:886 bond_enslave+0x2b8b/0x3ac0 drivers/net/bonding/bond_main.c:2180 do_set_master+0x533/0x6d0 net/core/rtnetlink.c:2963 do_setlink+0xcf0/0x41c0 net/core/rtnetlink.c:3165 rtnl_changelink net/core/rtnetlink.c:3776 [inline] __rtnl_newlink net/core/rtnetlink.c:3935 [inline] rtnl_newlink+0x161c/0x1c90 net/core/rtnetlink.c:4072 rtnetlink_rcv_msg+0x7cf/0xb70 net/core/rtnetlink.c:6958 netlink_rcv_skb+0x208/0x470 net/netlink/af_netlink.c:2550 netlink_unicast_kernel net/netlink/af_netlink.c:1318 [inline] netlink_unicast+0x82f/0x9e0 net/netlink/af_netlink.c:1344 netlink_sendmsg+0x805/0xb30 net/netlink/af_netlink.c:1894 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0x21c/0x270 net/socket.c:742 ____sys_sendmsg+0x505/0x820 net/socket.c:2592 ___sys_sendmsg+0x21f/0x2a0 net/socket.c:2646 __sys_sendmsg+0x164/0x220 net/socket.c:2678 do_syscall_32_irqs_on arch/x86/entry/syscall_32.c:83 [inline] __do_fast_syscall_32+0x1dc/0x560 arch/x86/entry/syscall_32.c:307 do_fast_syscall_32+0x34/0x80 arch/x86/entry/syscall_32.c:332 entry_SYSENTER_compat_after_hwframe+0x84/0x8e </TASK> The buggy address belongs to the variable: lacpdu_mcast_addr+0x0/0x40
[ Alexey: Replace SLAVE_NL_ERR() with NL_SET_ERR_MSG() and slave_err()
because SLAVE_NL_ERR() is not present in linux-5.10.y. ]
--- drivers/net/bonding/bond_main.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/net/bonding/bond_main.c +7 -0
@@ -1763,6 +1763,13 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,*/ if (!bond_has_slaves (bond)) {if (bond_dev->type != slave_dev->type) { +if (slave_dev->type !=ARPHRD_ETHER && +BOND_MODE (bond) ==BOND_MODE_8023AD ) { +NL_SET_ERR_MSG (extack,"8023AD mode requires Ethernet devices" ); +slave_err (bond_dev, slave_dev, +"Error: 8023AD mode requires Ethernet devices\n" ); +return -EINVAL ; + }slave_dbg (bond_dev, slave_dev,"change device type from %d to %d\n" , bond_dev->type, slave_dev->type);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 155/342 usbnet: Fix using smp_processor_id() in preemptible code warnings
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zqiang <[email protected]>
commit 327cd4b68b4398b6c24f10eb2b2533ffbfc10185 upstream.
Syzbot reported the following warning:
BUG: using smp_processor_id() in preemptible [00000000] code: dhcpcd/2879 caller is usbnet_skb_return+0x74/0x490 drivers/net/usb/usbnet.c:331 CPU: 1 UID: 0 PID: 2879 Comm: dhcpcd Not tainted 6.15.0-rc4-syzkaller-00098-g615dca38c2ea #0 PREEMPT(voluntary) Call Trace: <TASK> __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x16c/0x1f0 lib/dump_stack.c:120 check_preemption_disabled+0xd0/0xe0 lib/smp_processor_id.c:49 usbnet_skb_return+0x74/0x490 drivers/net/usb/usbnet.c:331 usbnet_resume_rx+0x4b/0x170 drivers/net/usb/usbnet.c:708 usbnet_change_mtu+0x1be/0x220 drivers/net/usb/usbnet.c:417 __dev_set_mtu net/core/dev.c:9443 [inline] netif_set_mtu_ext+0x369/0x5c0 net/core/dev.c:9496 netif_set_mtu+0xb0/0x160 net/core/dev.c:9520 dev_set_mtu+0xae/0x170 net/core/dev_api.c:247 dev_ifsioc+0xa31/0x18d0 net/core/dev_ioctl.c:572 dev_ioctl+0x223/0x10e0 net/core/dev_ioctl.c:821 sock_do_ioctl+0x19d/0x280 net/socket.c:1204 sock_ioctl+0x42f/0x6a0 net/socket.c:1311 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:906 [inline] __se_sys_ioctl fs/ioctl.c:892 [inline] __x64_sys_ioctl+0x190/0x200 fs/ioctl.c:892 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xcd/0x260 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f
For historical and portability reasons, the netif_rx() is usually run in the softirq or interrupt context, this commit therefore add local_bh_disable/enable() protection in the usbnet_resume_rx().
[ Alexey: Keep tasklet_schedule() because commit 2c04d279e857 ("net: usb:
Convert tasklet API to new bottom half workqueue mechanism") is not present in linux-5.10.y. ]
--- drivers/net/usb/usbnet.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/usb/usbnet.c +2 -0
@@ -684,6 +684,7 @@ void usbnet_resume_rx(struct usbnet *dev)struct sk_buff *skb;int num =0 ; +local_bh_disable ();clear_bit (EVENT_RX_PAUSED , &dev->flags);while ((skb =skb_dequeue (&dev->rxq_pause)) !=NULL ) {
@@ -692,6 +693,7 @@ void usbnet_resume_rx(struct usbnet *dev) }tasklet_schedule (&dev->bh); +local_bh_enable ();netif_dbg (dev, rx_status, dev->net,"paused rx queue disabled, %d skbs requeued\n" , num);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 156/342 nfsd: dont ignore the return code of svc_proc_register()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
commit 930b64ca0c511521f0abdd1d57ce52b2a6e3476b upstream.
Currently, nfsd_proc_stat_init() ignores the return value of svc_proc_register(). If the procfile creation fails, then the kernel will WARN when it tries to remove the entry later.
Fix nfsd_proc_stat_init() to return the same type of pointer as svc_proc_register(), and fix up nfsd_net_init() to check that and fail the nfsd_net construction if it occurs.
svc_proc_register() can fail if the dentry can't be allocated, or if an identical dentry already exists. The second case is pretty unlikely in the nfsd_net construction codepath, so if this happens, return -ENOMEM.
--- fs/nfsd/nfsctl.c | 9 ++++++++- fs/nfsd/stats.c | 4 ++-- fs/nfsd/stats.h | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-)
fs/nfsd/nfsctl.c +8 -1
@@ -1471,17 +1471,24 @@ static __net_init int nfsd_init_net(struct net *net) retval =nfsd_stat_counters_init (nn);if (retval) goto out_repcache_error; +memset (&nn->nfsd_svcstats,0 ,sizeof (nn->nfsd_svcstats)); nn->nfsd_svcstats.program = &nfsd_program; +if (!nfsd_proc_stat_init (net)) { + retval = -ENOMEM ; + goto out_proc_error; + } + nn->nfsd_versions =NULL ; nn->nfsd4_minorversions =NULL ;nfsd4_init_leases_net (nn);get_random_bytes (&nn->siphash_key,sizeof (nn->siphash_key));seqlock_init (&nn->writeverf_lock); -nfsd_proc_stat_init (net);return 0 ; +out_proc_error: +nfsd_stat_counters_destroy (nn); out_repcache_error:nfsd_idmap_shutdown (net); out_idmap_error:
fs/nfsd/stats.c +2 -2
@@ -113,11 +113,11 @@ void nfsd_stat_counters_destroy(struct nfsd_net *nn)nfsd_percpu_counters_destroy (nn->counter,NFSD_STATS_COUNTERS_NUM ); } -void nfsd_proc_stat_init (struct net *net) +struct proc_dir_entry *nfsd_proc_stat_init (struct net *net) {struct nfsd_net *nn =net_generic (net, nfsd_net_id); -svc_proc_register (net, &nn->nfsd_svcstats, &nfsd_proc_ops); +return svc_proc_register (net, &nn->nfsd_svcstats, &nfsd_proc_ops); }void nfsd_proc_stat_shutdown (struct net *net)
fs/nfsd/stats.h +1 -1
@@ -15,7 +15,7 @@ void nfsd_percpu_counters_reset(struct percpu_counter *counters, int num);void nfsd_percpu_counters_destroy (struct percpu_counter *counters,int num);int nfsd_stat_counters_init (struct nfsd_net *nn);void nfsd_stat_counters_destroy (struct nfsd_net *nn); -void nfsd_proc_stat_init (struct net *net); +struct proc_dir_entry *nfsd_proc_stat_init (struct net *net);void nfsd_proc_stat_shutdown (struct net *net);static inline void nfsd_stats_rc_hits_inc (struct nfsd_net *nn)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 157/342 wifi: mac80211: check tdls flag in ieee80211_tdls_oper
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
commit 7d73872d949c488a1d7c308031d6a9d89b5e0a8b upstream.
When NL80211_TDLS_ENABLE_LINK is called, the code only checks if the station exists but not whether it is actually a TDLS station. This allows the operation to proceed for non-TDLS stations, causing unintended side effects like modifying channel context and HT protection before failing.
Add a check for sta->sta.tdls early in the ENABLE_LINK case, before any side effects occur, to ensure the operation is only allowed for actual TDLS peers.
--- net/mac80211/tdls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/mac80211/tdls.c +1 -1
@@ -1380,7 +1380,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,mutex_lock (&local->sta_mtx); sta =sta_info_get (sdata, peer); -if (!sta) { +if (!sta || !sta->sta.tdls) {mutex_unlock (&local->sta_mtx); ret = -ENOLINK ;break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 158/342 spi: meson-spicc: Fix double-put in remove path
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Gu <[email protected]>
commit 63542bb402b7013171c9f621c28b609eda4dbf1f upstream.
meson_spicc_probe() registers the controller with devm_spi_register_controller(), so teardown already drops the controller reference via devm cleanup.
Calling spi_controller_put() again in meson_spicc_remove() causes a double-put.
[ Alexey: Remove the equivalent legacy spi_master_put() call used in
linux-5.10.y. ]
--- drivers/spi/spi-meson-spicc.c | 2 -- 1 file changed, 2 deletions(-)
drivers/spi/spi-meson-spicc.c +0 -2
@@ -863,8 +863,6 @@ static int meson_spicc_remove(struct platform_device *pdev)clk_disable_unprepare (spicc->core);clk_disable_unprepare (spicc->pclk); -spi_master_put (spicc->master); -return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 159/342 io_uring: prevent opcode speculation
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pavel Begunkov <[email protected]>
commit 1e988c3fe1264708f4f92109203ac5b1d65de50b upstream.
sqe->opcode is used for different tables, make sure we santitise it against speculations.
[ Alexey: Sanitize req->opcode directly because io_init_req() in
linux-5.10.y has no local opcode variable and subsequent lookups use it. ]
--- io_uring/io_uring.c | 2 ++ 1 file changed, 2 insertions(+)
io_uring/io_uring.c +2 -0
@@ -7193,6 +7193,8 @@ static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req,return -EINVAL ;if (unlikely (req->opcode >=IORING_OP_LAST ))return -EINVAL ; + req->opcode =array_index_nospec (req->opcode,IORING_OP_LAST ); +if (!io_check_restriction (ctx, req, sqe_flags))return -EACCES ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 160/342 tap: free page on error paths in tap_get_user_xdp()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit 3bcf7aec6a9d16438f2cec29f5d7c8d5b8edf9b2 ]
tap_get_user_xdp() rejects a frame shorter than ETH_HLEN with -EINVAL, and returns -ENOMEM when build_skb() fails. Both paths jump to the err label without freeing the page that vhost_net_build_xdp() allocated for the frame. tap_sendmsg() discards the per-buffer return value and always returns 0, so vhost_tx_batch() takes the success path and never frees the page; each rejected frame in a batch leaks one page-frag chunk.
Free the page on both error paths, before the skb is built. This is the tap counterpart of the same leak in tun_xdp_one().
--- drivers/net/tap.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/tap.c +2 -0
@@ -1145,6 +1145,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)int err, depth;if (unlikely (xdp->data_end - xdp->data <ETH_HLEN )) { +put_page (virt_to_head_page (xdp->data)); err = -EINVAL ; goto err; }
@@ -1154,6 +1155,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) skb =build_skb (xdp->data_hard_start, buflen);if (!skb) { +put_page (virt_to_head_page (xdp->data)); err = -ENOMEM ; goto err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 161/342 tun: free page on build_skb failure in tun_xdp_one()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit aa8963fdce667a42fb7f0bdd2909fadcab02f9a8 ]
When build_skb() fails in tun_xdp_one(), the function sets ret to -ENOMEM and jumps to the out label, which returns without freeing the page that vhost_net_build_xdp() allocated for the frame. As with the short-frame rejection path, tun_sendmsg() discards the per-buffer error and still returns total_len, so vhost_tx_batch() takes the success path and never frees the page. Each build_skb() failure in a batch leaks one page-frag chunk.
Free the page before taking the error path, matching the put_page() the other error exits of tun_xdp_one() already perform.
[Harshit: Backport to 5.15.y/5.10.y, use err instead of ret, no change needed]
--- drivers/net/tun.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/tun.c +1 -0
@@ -2518,6 +2518,7 @@ static int tun_xdp_one(struct tun_struct *tun, build: skb =build_skb (xdp->data_hard_start, buflen);if (!skb) { +put_page (virt_to_head_page (xdp->data)); err = -ENOMEM ; goto out; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 162/342 KVM: arm64: Remove VPIPT I-cache handling
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marc Zyngier <[email protected]>
commit ced242ba9d7cb3571f6e0f165f643cb832d52148 upstream.
We have some special handling for VPIPT I-cache in critical parts of the cache and TLB maintenance. Remove it.
--- arch/arm64/include/asm/kvm_mmu.h | 4 ++-- arch/arm64/kvm/hyp/nvhe/tlb.c | 35 -------------------------------- arch/arm64/kvm/hyp/vhe/tlb.c | 13 ------------ 3 files changed, 2 insertions(+), 50 deletions(-)
arch/arm64/include/asm/kvm_mmu.h +2 -2
@@ -162,8 +162,8 @@ static inline void __invalidate_icache_guest_page(kvm_pfn_t pfn,if (icache_is_aliasing ()) {/* any kind of VIPT cache */ __flush_icache_all (); - }else if (is_kernel_in_hyp_mode () || !icache_is_vpipt ()) { -/* PIPT or VPIPT at EL2 (see comment in __kvm_tlb_flush_vmid_ipa) */ + }else { +/* PIPT */ void *va =page_address (pfn_to_page (pfn)); invalidate_icache_range((unsigned long)va,
arch/arm64/kvm/hyp/nvhe/tlb.c +0 -35
@@ -82,28 +82,6 @@ void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu,dsb (ish);isb (); -/* -* If the host is running at EL1 and we have a VPIPT I-cache, -* then we must perform I-cache maintenance at EL2 in order for -* it to have an effect on the guest. Since the guest cannot hit -* I-cache lines allocated with a different VMID, we don't need -* to worry about junk out of guest reset (we nuke the I-cache on -* VMID rollover), but we do need to be careful when remapping -* executable pages for the same guest. This can happen when KSM -* takes a CoW fault on an executable page, copies the page into -* a page that was previously mapped in the guest and then needs -* to invalidate the guest view of the I-cache for that page -* from EL1. To solve this, we invalidate the entire I-cache when -* unmapping a page from a guest if we have a VPIPT I-cache but -* the host is running at EL1. As above, we could do better if -* we had the VA. -* -* The moral of this story is: if you have a VPIPT I-cache, then -* you should be running with VHE enabled. -*/ -if (icache_is_vpipt ()) -__flush_icache_all (); -__tlb_switch_to_host (&cxt); }
@@ -142,18 +120,5 @@ void __kvm_flush_vm_context(void) {dsb (ishst);__tlbi (alle1is); - -/* -* VIPT and PIPT caches are not affected by VMID, so no maintenance -* is necessary across a VMID rollover. -* -* VPIPT caches constrain lookup and maintenance to the active VMID, -* so we need to invalidate lines with a stale VMID to avoid an ABA -* race after multiple rollovers. -* -*/ -if (icache_is_vpipt ()) - asmvolatile ("ic ialluis" ); -dsb (ish); }
arch/arm64/kvm/hyp/vhe/tlb.c +0 -13
@@ -146,18 +146,5 @@ void __kvm_flush_vm_context(void) {dsb (ishst);__tlbi (alle1is); - -/* -* VIPT and PIPT caches are not affected by VMID, so no maintenance -* is necessary across a VMID rollover. -* -* VPIPT caches constrain lookup and maintenance to the active VMID, -* so we need to invalidate lines with a stale VMID to avoid an ABA -* race after multiple rollovers. -* -*/ -if (icache_is_vpipt ()) - asmvolatile ("ic ialluis" ); -dsb (ish); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 163/342 arm64: tlb: Allow XZR argument to TLBI ops
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mark Rutland <[email protected]>
commit bfd9c931d19aa59fb8371d557774fa169b15db9a upstream.
The TLBI instruction accepts XZR as a register argument, and for TLBI operations with a register argument, there is no functional difference between using XZR or another GPR which contains zeroes. Operations without a register argument are encoded as if XZR were used.
Allow the __TLBI_1() macro to use XZR when a register argument is all zeroes.
Today this only results in a trivial code saving in __do_compat_cache_op()'s workaround for Neoverse-N1 erratum #1542419. In subsequent patches this pattern will be used more generally.
There should be no functional change as a result of this patch.
--- arch/arm64/include/asm/tlbflush.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/arm64/include/asm/tlbflush.h +3 -3
@@ -37,12 +37,12 @@ : : )#define __TLBI_1 (op, arg) asm (ARM64_ASM_PREAMBLE \ - "tlbi " #op ", %0\n" \ + "tlbi " #op ", %x0\n" \ ALTERNATIVE("nop\n nop", \ - "dsb ish\n tlbi " #op ", %0", \ + "dsb ish\n tlbi " #op ", %x0", \ ARM64_WORKAROUND_REPEAT_TLBI, \ CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) \ - : : "r" (arg)) + : : "rZ" (arg))#define __TLBI_N (op, arg, n, ...) __TLBI_##n(op, arg)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 164/342 arm64: tlb: Optimize ARM64_WORKAROUND_REPEAT_TLBI
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mark Rutland <[email protected]>
commit a8f78680ee6bf795086384e8aea159a52814f827 upstream.
The ARM64_WORKAROUND_REPEAT_TLBI workaround is used to mitigate several errata where broadcast TLBI;DSB sequences don't provide all the architecturally required synchronization. The workaround performs more work than necessary, and can have significant overhead. This patch optimizes the workaround, as explained below.
The workaround was originally added for Qualcomm Falkor erratum 1009 in commit:
d9ff80f83ecb ("arm64: Work around Falkor erratum 1009")
As noted in the message for that commit, the workaround is applied even in cases where it is not strictly necessary.
The workaround was later reused without changes for:
* Arm Cortex-A76 erratum #1286807
SDEN v33: https://developer.arm.com/documentation/SDEN-885749/33-0/
* Arm Cortex-A55 erratum #2441007
SDEN v16: https://developer.arm.com/documentation/SDEN-859338/1600/
* Arm Cortex-A510 erratum #2441009
SDEN v19: https://developer.arm.com/documentation/SDEN-1873351/1900/
The important details to note are as follows:
1. All relevant errata only affect the ordering and/or completion of memory accesses which have been translated by an invalidated TLB entry. The actual invalidation of TLB entries is unaffected.
2. The existing workaround is applied to both broadcast and local TLB invalidation, whereas for all relevant errata it is only necessary to apply a workaround for broadcast invalidation.
3. The existing workaround replaces every TLBI with a TLBI;DSB;TLBI sequence, whereas for all relevant errata it is only necessary to execute a single additional TLBI;DSB sequence after any number of TLBIs are completed by a DSB.
For example, for a sequence of batched TLBIs:
TLBI <op1>[, <arg1>]
TLBI <op2>[, <arg2>]
TLBI <op3>[, <arg3>]
DSB ISH... the existing workaround will expand this to:
TLBI <op1>[, <arg1>]
DSB ISH // additional
TLBI <op1>[, <arg1>] // additional
TLBI <op2>[, <arg2>]
DSB ISH // additional
TLBI <op2>[, <arg2>] // additional
TLBI <op3>[, <arg3>]
DSB ISH // additional
TLBI <op3>[, <arg3>] // additional
DSB ISH... whereas it is sufficient to have:
TLBI <op1>[, <arg1>]
TLBI <op2>[, <arg2>]
TLBI <op3>[, <arg3>]
DSB ISH
TLBI <opX>[, <argX>] // additional
DSB ISH // additionalUsing a single additional TBLI and DSB at the end of the sequence can have significantly lower overhead as each DSB which completes a TLBI must synchronize with other PEs in the system, with potential performance effects both locally and system-wide.
4. The existing workaround repeats each specific TLBI operation, whereas for all relevant errata it is sufficient for the additional TLBI to use *any* operation which will be broadcast, regardless of which translation regime or stage of translation the operation applies to.
For example, for a single TLBI:
TLBI ALLE2IS
DSB ISH
... the existing workaround will expand this to:
TLBI ALLE2IS
DSB ISH
TLBI ALLE2IS // additional
DSB ISH // additional
... whereas it is sufficient to have:
TLBI ALLE2IS
DSB ISH
TLBI VALE1IS, XZR // additional
DSB ISH // additional
As the additional TLBI doesn't have to match a specific earlier TLBI, the additional TLBI can be implemented in separate code, with no memory of the earlier TLBIs. The additional TLBI can also use a cheaper TLBI operation.
5. The existing workaround is applied to both Stage-1 and Stage-2 TLB invalidation, whereas for all relevant errata it is only necessary to apply a workaround for Stage-1 invalidation.
Architecturally, TLBI operations which invalidate only Stage-2 information (e.g. IPAS2E1IS) are not required to invalidate TLB entries which combine information from Stage-1 and Stage-2 translation table entries, and consequently may not complete memory accesses translated by those combined entries. In these cases, completion of memory accesses is only guaranteed after subsequent invalidation of Stage-1 information (e.g. VMALLE1IS).
Taking the above points into account, this patch reworks the workaround logic to reduce overhead:
* New __tlbi_sync_s1ish() and __tlbi_sync_s1ish_hyp() functions are added and used in place of any dsb(ish) which is used to complete broadcast Stage-1 TLB maintenance. When the ARM64_WORKAROUND_REPEAT_TLBI workaround is enabled, these helpers will execute an additional TLBI;DSB sequence.
For consistency, it might make sense to add __tlbi_sync_*() helpers for local and stage 2 maintenance. For now I've left those with open-coded dsb() to keep the diff small.
* The duplication of TLBIs in __TLBI_0() and __TLBI_1() is removed. This is no longer needed as the necessary synchronization will happen in __tlbi_sync_s1ish() or __tlbi_sync_s1ish_hyp().
* The additional TLBI operation is chosen to have minimal impact:
- __tlbi_sync_s1ish() uses "TLBI VALE1IS, XZR". This is only used at EL1 or at EL2 with {E2H,TGE}=={1,1}, where it will target an unused entry for the reserved ASID in the kernel's own translation regime, and have no adverse affect.
- __tlbi_sync_s1ish_hyp() uses "TLBI VALE2IS, XZR". This is only used in hyp code, where it will target an unused entry in the hyp code's TTBR0 mapping, and should have no adverse effect.
* As __TLBI_0() and __TLBI_1() no longer replace each TLBI with a TLBI;DSB;TLBI sequence, batching TLBIs is worthwhile, and there's no need for arch_tlbbatch_should_defer() to consider ARM64_WORKAROUND_REPEAT_TLBI.
When building defconfig with GCC 15.1.0, compared to v6.19-rc1, this patch saves ~1KiB of text, makes the vmlinux ~42KiB smaller, and makes the resulting Image 64KiB smaller:
| [mark@lakrids:~/src/linux]% size vmlinux-* | text data bss dec hex filename | 21179831 19660919 708216 41548966 279fca6 vmlinux-after | 21181075 19660903 708216 41550194 27a0172 vmlinux-before | [mark@lakrids:~/src/linux]% ls -l vmlinux-* | -rwxr-xr-x 1 mark mark 157771472 Feb 4 12:05 vmlinux-after | -rwxr-xr-x 1 mark mark 157815432 Feb 4 12:05 vmlinux-before | [mark@lakrids:~/src/linux]% ls -l Image-* | -rw-r--r-- 1 mark mark 41007616 Feb 4 12:05 Image-after | -rw-r--r-- 1 mark mark 41073152 Feb 4 12:05 Image-before
--- arch/arm64/include/asm/tlbflush.h | 51 ++++++++++++++++++++++--------- arch/arm64/kernel/sys_compat.c | 2 +- arch/arm64/kvm/hyp/nvhe/tlb.c | 6 ++-- arch/arm64/kvm/hyp/vhe/tlb.c | 6 ++-- 4 files changed, 44 insertions(+), 21 deletions(-)
arch/arm64/include/asm/tlbflush.h +37 -14
@@ -30,18 +30,10 @@*/ #define __TLBI_0 (op, arg) asm (ARM64_ASM_PREAMBLE \ "tlbi " #op "\n" \ - ALTERNATIVE("nop\n nop", \ - "dsb ish\n tlbi " #op, \ - ARM64_WORKAROUND_REPEAT_TLBI, \ - CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) \ : : )#define __TLBI_1 (op, arg) asm (ARM64_ASM_PREAMBLE \ "tlbi " #op ", %x0\n" \ - ALTERNATIVE("nop\n nop", \ - "dsb ish\n tlbi " #op ", %x0", \ - ARM64_WORKAROUND_REPEAT_TLBI, \ - CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) \ : : "rZ" (arg))#define __TLBI_N (op, arg, n, ...) __TLBI_##n(op, arg)
@@ -158,6 +150,37 @@ static inline unsigned long get_trans_granule(void)#define __TLBI_RANGE_NUM (pages, scale) \ ((((pages) >> (5 * (scale) + 1)) & TLBI_RANGE_MASK) - 1) +#define __repeat_tlbi_sync (op, arg) \ +do { \ + asm volatile( \ + ALTERNATIVE("nop\n nop", \ + "tlbi " #op ", %x0\n dsb ish", \ + ARM64_WORKAROUND_REPEAT_TLBI, \ + CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) \ + : \ + : "rZ" (arg)); \ +} while (0) + +/* +* Complete broadcast TLB maintenance issued by the host which invalidates +* stage 1 information in the host's own translation regime. +*/ +static inline void __tlbi_sync_s1ish (void ) +{ +dsb (ish); +__repeat_tlbi_sync (vale1is,0 ); +} + +/* +* Complete broadcast TLB maintenance issued by hyp code which invalidates +* stage 1 translation information in any translation regime. +*/ +static inline void __tlbi_sync_s1ish_hyp (void ) +{ +dsb (ish); +__repeat_tlbi_sync (vale2is,0 ); +} +/* * TLB Invalidation * ================
@@ -239,7 +262,7 @@ static inline void flush_tlb_all(void) {dsb (ishst);__tlbi (vmalle1is); -dsb (ish); +__tlbi_sync_s1ish ();isb (); }
@@ -251,7 +274,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm) asid =__TLBI_VADDR (0 ,ASID (mm));__tlbi (aside1is, asid);__tlbi_user (aside1is, asid); -dsb (ish); +__tlbi_sync_s1ish (); } static inline void flush_tlb_page_nosync(struct vm_area_struct *vma,
@@ -269,7 +292,7 @@ static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) {flush_tlb_page_nosync (vma, uaddr); -dsb (ish); +__tlbi_sync_s1ish (); }/*
@@ -357,7 +380,7 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, } scale++; } -dsb (ish); +__tlbi_sync_s1ish (); } static inline void flush_tlb_range(struct vm_area_struct *vma,
@@ -386,7 +409,7 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long enddsb (ishst);for (addr = start; addr < end; addr +=1 << (PAGE_SHIFT -12 ))__tlbi (vaale1is, addr); -dsb (ish); +__tlbi_sync_s1ish ();isb (); }
@@ -400,7 +423,7 @@ static inline void __flush_tlb_kernel_pgtable(unsigned long kaddr)dsb (ishst);__tlbi (vaae1is, addr); -dsb (ish); +__tlbi_sync_s1ish (); isb(); }#endif
arch/arm64/kernel/sys_compat.c +1 -1
@@ -38,7 +38,7 @@ __do_compat_cache_op(unsigned long start, unsigned long end)* We pick the reserved-ASID to minimise the impact. */ __tlbi (aside1is,__TLBI_VADDR (0 ,0 )); -dsb (ish); +__tlbi_sync_s1ish (); } ret =__flush_cache_user_range (start, start + chunk);
arch/arm64/kvm/hyp/nvhe/tlb.c +3 -3
@@ -79,7 +79,7 @@ void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu,*/ dsb (ish);__tlbi (vmalle1is); -dsb (ish); +__tlbi_sync_s1ish_hyp ();isb ();__tlb_switch_to_host (&cxt);
@@ -95,7 +95,7 @@ void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu)__tlb_switch_to_guest (mmu, &cxt);__tlbi (vmalls12e1is); -dsb (ish); +__tlbi_sync_s1ish_hyp ();isb ();__tlb_switch_to_host (&cxt);
@@ -120,5 +120,5 @@ void __kvm_flush_vm_context(void) {dsb (ishst);__tlbi (alle1is); -dsb (ish); +__tlbi_sync_s1ish_hyp (); }
arch/arm64/kvm/hyp/vhe/tlb.c +3 -3
@@ -105,7 +105,7 @@ void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu,*/ dsb (ish);__tlbi (vmalle1is); -dsb (ish); +__tlbi_sync_s1ish_hyp ();isb ();__tlb_switch_to_host (&cxt);
@@ -121,7 +121,7 @@ void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu)__tlb_switch_to_guest (mmu, &cxt);__tlbi (vmalls12e1is); -dsb (ish); +__tlbi_sync_s1ish_hyp ();isb ();__tlb_switch_to_host (&cxt);
@@ -146,5 +146,5 @@ void __kvm_flush_vm_context(void) {dsb (ishst);__tlbi (alle1is); -dsb (ish); +__tlbi_sync_s1ish_hyp (); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 165/342 xfrm: policy: fix use-after-free on inexact bin in xfrm_policy_bysel_ctx()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sanghyun Park <[email protected]>
[ Upstream commit 7f2d76c9c03257c0782afef9d95321fa04096f60 ]
Fix the race by pruning the bin while still holding xfrm_policy_lock, before dropping it. Use __xfrm_policy_inexact_prune_bin() directly since the lock is already held. The wrapper xfrm_policy_inexact_prune_bin() becomes unused and is removed.
Race:
CPU0 (XFRM_MSG_DELPOLICY) CPU1 (XFRM_MSG_NEWSPDINFO)
========================== ==========================
xfrm_policy_bysel_ctx():
spin_lock_bh(xfrm_policy_lock)
bin = xfrm_policy_inexact_lookup()
__xfrm_policy_unlink(pol)
spin_unlock_bh(xfrm_policy_lock)
xfrm_policy_kill(ret)
// wide window, lock not held
xfrm_hash_rebuild():
spin_lock_bh(xfrm_policy_lock)
__xfrm_policy_inexact_flush():
kfree_rcu(bin) // bin freed
spin_unlock_bh(xfrm_policy_lock)
xfrm_policy_inexact_prune_bin(bin)
// UAF: bin is freed--- net/xfrm/xfrm_policy.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-)
net/xfrm/xfrm_policy.c +2 -11
@@ -1124,15 +1124,6 @@ static void __xfrm_policy_inexact_prune_bin(struct xfrm_pol_inexact_bin *b, bool } } -static void xfrm_policy_inexact_prune_bin (struct xfrm_pol_inexact_bin *b) -{ -struct net *net =read_pnet (&b->k.net); - -spin_lock_bh (&net->xfrm.xfrm_policy_lock); -__xfrm_policy_inexact_prune_bin (b, false); -spin_unlock_bh (&net->xfrm.xfrm_policy_lock); -} -static void __xfrm_policy_inexact_flush (struct net *net) {struct xfrm_pol_inexact_bin *bin, *t;
@@ -1720,12 +1711,12 @@ xfrm_policy_bysel_ctx(struct net *net, const struct xfrm_mark *mark, u32 if_id, } ret = pol; } +if (bin && delete) +__xfrm_policy_inexact_prune_bin (bin, false);spin_unlock_bh (&net->xfrm.xfrm_policy_lock);if (ret && delete)xfrm_policy_kill (ret); -if (bin && delete) -xfrm_policy_inexact_prune_bin (bin);return ret; }EXPORT_SYMBOL (xfrm_policy_bysel_ctx);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 166/342 netlabel: validate unlabeled address and mask attribute lengths
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chenguang Zhao <[email protected]>
[ Upstream commit 9772589b57e44aedc240211c5c3f7a684a034d3a ]
netlbl_unlabel_addrinfo_get() used the address attribute length to determine whether the attribute data could be read as an IPv4 or IPv6 address, but did not independently validate the corresponding mask attribute length. A crafted Generic Netlink request could therefore provide a valid IPv4/IPv6 address attribute with a shorter mask attribute, which would later be read as a full struct in_addr or struct in6_addr.
NLA_BINARY policy lengths are maximum lengths by default, so use NLA_POLICY_EXACT_LEN() for the unlabeled IPv4/IPv6 address and mask attributes. This rejects short attributes during policy validation and also exposes the exact length requirements through policy introspection.
--- net/netlabel/netlabel_unlabeled.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-)
net/netlabel/netlabel_unlabeled.c +10 -20
@@ -114,14 +114,14 @@ static struct genl_family netlbl_unlabel_gnl_family;/* NetLabel Netlink attribute policy */ static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX +1 ] = { [NLBL_UNLABEL_A_ACPTFLG ] = { .type =NLA_U8 }, - [NLBL_UNLABEL_A_IPV6ADDR ] = { .type =NLA_BINARY , - .len =sizeof (struct in6_addr ) }, - [NLBL_UNLABEL_A_IPV6MASK ] = { .type =NLA_BINARY , - .len =sizeof (struct in6_addr ) }, - [NLBL_UNLABEL_A_IPV4ADDR ] = { .type =NLA_BINARY , - .len =sizeof (struct in_addr ) }, - [NLBL_UNLABEL_A_IPV4MASK ] = { .type =NLA_BINARY , - .len =sizeof (struct in_addr ) }, + [NLBL_UNLABEL_A_IPV6ADDR ] = +NLA_POLICY_EXACT_LEN (sizeof (struct in6_addr )), + [NLBL_UNLABEL_A_IPV6MASK ] = +NLA_POLICY_EXACT_LEN (sizeof (struct in6_addr )), + [NLBL_UNLABEL_A_IPV4ADDR ] = +NLA_POLICY_EXACT_LEN (sizeof (struct in_addr )), + [NLBL_UNLABEL_A_IPV4MASK ] = +NLA_POLICY_EXACT_LEN (sizeof (struct in_addr )), [NLBL_UNLABEL_A_IFACE ] = { .type =NLA_NUL_STRING , .len =IFNAMSIZ -1 }, [NLBL_UNLABEL_A_SECCTX ] = { .type =NLA_BINARY }
@@ -766,24 +766,14 @@ static int netlbl_unlabel_addrinfo_get(struct genl_info *info, void **mask, u32 *len) { - u32 addr_len; -if (info->attrs[NLBL_UNLABEL_A_IPV4ADDR ] && info->attrs[NLBL_UNLABEL_A_IPV4MASK ]) { - addr_len =nla_len (info->attrs[NLBL_UNLABEL_A_IPV4ADDR ]); -if (addr_len !=sizeof (struct in_addr ) && - addr_len !=nla_len (info->attrs[NLBL_UNLABEL_A_IPV4MASK ])) -return -EINVAL ; - *len = addr_len; + *len =sizeof (struct in_addr ); *addr =nla_data (info->attrs[NLBL_UNLABEL_A_IPV4ADDR ]); *mask =nla_data (info->attrs[NLBL_UNLABEL_A_IPV4MASK ]);return 0 ; }else if (info->attrs[NLBL_UNLABEL_A_IPV6ADDR ]) { - addr_len =nla_len (info->attrs[NLBL_UNLABEL_A_IPV6ADDR ]); -if (addr_len !=sizeof (struct in6_addr ) && - addr_len !=nla_len (info->attrs[NLBL_UNLABEL_A_IPV6MASK ])) -return -EINVAL ; - *len = addr_len; + *len =sizeof (struct in6_addr ); *addr =nla_data (info->attrs[NLBL_UNLABEL_A_IPV6ADDR ]); *mask =nla_data (info->attrs[NLBL_UNLABEL_A_IPV6MASK ]);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 167/342 net: qrtr: fix refcount saturation and potential UAF in qrtr_port_remove
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mingyu Wang <[email protected]>
[ Upstream commit a2171131ecda1ed61a594a1eb715e75fdad0fef5 ]
In qrtr_port_remove(), the socket reference count is decremented via __sock_put() before the port is removed from the qrtr_ports XArray and before the RCU grace period elapses.
This breaks the fundamental RCU update paradigm. It exposes a race window where a concurrent RCU reader (such as qrtr_reset_ports() or qrtr_port_lookup()) can obtain a pointer to the socket from the XArray, and attempt to call sock_hold() on a socket whose reference count has already dropped to zero.
This exact race condition was hit during syzkaller fuzzing, leading to the following refcount saturation warning and a potential Use-After-Free:
refcount_t: saturated; leaking memory.
WARNING: CPU: 3 PID: 1273 at lib/refcount.c:22 refcount_warn_saturate+0xae/0x1d0 Modules linked in: qrtr(+) bochs drm_shmem_helper ... Call Trace: <TASK> qrtr_reset_ports net/qrtr/af_qrtr.c:768 [inline] [qrtr] __qrtr_bind.isra.0+0x48b/0x570 net/qrtr/af_qrtr.c:805 [qrtr] qrtr_bind+0x17d/0x210 net/qrtr/af_qrtr.c:901 [qrtr] kernel_bind+0xe4/0x120 net/socket.c:3592 qrtr_ns_init+0x1a6/0x380 net/qrtr/ns.c:715 [qrtr] qrtr_proto_init+0x3b/0xff0 net/qrtr/af_qrtr.c:169 [qrtr] do_one_initcall+0xf5/0x5e0 init/main.c:1283 ... </TASK>
Fix this by deferring the reference count decrement until after the xa_erase() and the synchronize_rcu() complete.
(Note: The v1 of this patch incorrectly replaced __sock_put() with sock_put(). As Simon Horman pointed out, the callers of qrtr_port_remove() still hold a reference to the socket, so freeing the socket memory here would lead to a subsequent UAF in the caller. Thus, the __sock_put() is kept, but only repositioned to close the RCU race.)
--- net/qrtr/af_qrtr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/qrtr/af_qrtr.c +2 -2
@@ -701,13 +701,13 @@ static void qrtr_port_remove(struct qrtr_sock *ipc)if (port ==QRTR_PORT_CTRL ) port =0 ; -__sock_put (&ipc->sk); -xa_erase (&qrtr_ports, port);/* Ensure that if qrtr_port_lookup() did enter the RCU read section we * wait for it to up increment the refcount */ synchronize_rcu (); + +__sock_put (&ipc->sk); }/* Assign port number to socket.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 168/342 ipv6: sit: reload inner IPv6 header after GSO offloads
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
[ Upstream commit f0e42f0c4337b1f220de1ddd63f47197c7dee4de ]
ipip6_tunnel_xmit() caches the inner IPv6 header pointer at function entry and continues using it after iptunnel_handle_offloads().
For GSO skbs, iptunnel_handle_offloads() calls skb_header_unclone(). When the skb header is cloned, skb_header_unclone() can call pskb_expand_head(), which may move the skb head. The pskb_expand_head() contract requires pointers into the skb header to be reloaded after the call.
If the later skb_realloc_headroom() branch is not taken, SIT uses the stale iph6 pointer to read the inner hop limit and DS field. That can read from a freed skb head after the old head's remaining clone is released.
Reload iph6 after the offload helper succeeds and before subsequent reads from the inner IPv6 header. Keep the existing reload after skb_realloc_headroom(), since that branch can also replace the skb.
--- net/ipv6/sit.c | 1 + 1 file changed, 1 insertion(+)
net/ipv6/sit.c +1 -0
@@ -964,6 +964,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,ip_rt_put (rt); goto tx_error; } + iph6 =ipv6_hdr (skb);if (df) { mtu =dst_mtu (&rt->dst) - t_hlen;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 169/342 net: openvswitch: fix possible kfree_skb of ERR_PTR
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adrian Moreno <[email protected]>
[ Upstream commit ee30dd2909d8b98619f4341c70ec8dc8e155ab02 ]
After the patch in the "Fixes" tag, the allocation of the "reply" skb can happen either before or after locking the ovs_mutex.
However, error cleanups still follow the classical reversed order, assuming "reply" is allocated before locking: it is freed after unlocking.
If "reply" allocation happens after locking the mutex and it fails, "reply" is left with an ERR_PTR, and execution jumps to the correspondent cleanup stage which will try to free an invalid pointer.
Fix this by setting the pointer to NULL after having saved its error value.
--- net/openvswitch/datapath.c | 1 + 1 file changed, 1 insertion(+)
net/openvswitch/datapath.c +1 -0
@@ -1248,6 +1248,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)if (IS_ERR (reply)) { error =PTR_ERR (reply); + reply =NULL ; goto err_unlock_ovs; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 170/342 sctp: fix uninit-value in __sctp_rcv_asconf_lookup()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit f8373d7090b745728de66308deeecc67e8d319ce ]
__sctp_rcv_asconf_lookup() in net/sctp/input.c only checks that the ASCONF chunk can hold the ADDIP header and a parameter header, then calls af->from_addr_param(), which reads the full address (16 bytes for IPv6) trusting the parameter's declared length.
An unauthenticated peer can send a truncated trailing ASCONF chunk that declares an IPv6 address parameter but stops after the 4-byte parameter header; reached from the no-association lookup path, from_addr_param() then reads uninitialized bytes past the parameter.
Impact: an unauthenticated SCTP peer makes the receive path read up to 16 bytes of uninitialized memory past a truncated ASCONF address parameter.
The sibling __sctp_rcv_init_lookup() bounds parameters with sctp_walk_params(); this path open-codes the fetch and omits the bound. Verify the whole address parameter lies within the chunk before from_addr_param() reads it, the same class of fix as commit 51e5ad549c43 ("net: sctp: fix KMSAN uninit-value in sctp_inq_pop").
--- net/sctp/input.c | 8 ++++++++ 1 file changed, 8 insertions(+)
net/sctp/input.c +8 -0
@@ -1172,6 +1172,14 @@ static struct sctp_association *__sctp_rcv_asconf_lookup(/* Skip over the ADDIP header and find the Address parameter */ param = (union sctp_addr_param *)(asconf +1 ); +/* The whole address parameter must lie within the chunk before +* af->from_addr_param() reads the variable-length address; otherwise a +* truncated trailing ASCONF chunk lets it read uninitialized bytes past +* the parameter. +*/ +if (sizeof (*asconf) +ntohs (param->p.length) >ntohs (ch->length)) +return NULL ; + af =sctp_get_af_specific (param_type2af (param->p.type));if (unlikely (!af))return NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 171/342 net: guard timestamp cmsgs to real error queue skbs
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
[ Upstream commit 1ee90b77b727df903033db873c75caac5c27ec98 ]
skb_is_err_queue() treats PACKET_OUTGOING as the sole marker for an skb from sk_error_queue. That assumption is not true for AF_PACKET sockets:
outgoing packet taps are also delivered to packet sockets with skb->pkt_type == PACKET_OUTGOING, but their skb->cb is owned by AF_PACKET instead of struct sock_exterr_skb.
If such an skb is received with timestamping enabled, the generic timestamp cmsg path can read AF_PACKET control-buffer state as sock_exterr_skb::opt_stats. With SO_RXQ_OVFL enabled, the packet drop counter overlaps opt_stats. An odd drop count makes the path emit SCM_TIMESTAMPING_OPT_STATS with skb->len and skb->data. For non-linear skbs this copies past the linear head and can trigger hardened usercopy or disclose adjacent heap contents.
Keep skb_is_err_queue() local to net/socket.c, but make it verify that the PACKET_OUTGOING marker is paired with the sock_rmem_free destructor installed by sock_queue_err_skb(). AF_PACKET receive skbs use normal receive ownership and no longer pass as error-queue skbs, while legitimate sk_error_queue entries keep the PACKET_OUTGOING marker and sock_rmem_free ownership.
--- include/net/sock.h | 1 + net/core/skbuff.c | 6 +++--- net/socket.c | 11 ++++++----- 3 files changed, 10 insertions(+), 8 deletions(-)
include/net/sock.h +1 -0
@@ -1779,6 +1779,7 @@ struct sk_buff *sock_omalloc(struct sock *sk, unsigned long size, gfp_t priority);void skb_orphan_partial (struct sk_buff *skb);void sock_rfree (struct sk_buff *skb); +void sock_rmem_free (struct sk_buff *skb);void sock_efree (struct sk_buff *skb);#ifdef CONFIG_INET void sock_edemux (struct sk_buff *skb);
net/core/skbuff.c +3 -3
@@ -4633,7 +4633,7 @@ int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer) }EXPORT_SYMBOL_GPL (skb_cow_data); -static void sock_rmem_free (struct sk_buff *skb) +void sock_rmem_free (struct sk_buff *skb) {struct sock *sk = skb->sk;
@@ -4642,8 +4642,8 @@ static void sock_rmem_free(struct sk_buff *skb)static void skb_set_err_queue (struct sk_buff *skb) { -/* pkt_type of skbs received on local sockets is never PACKET_OUTGOING. -* So, it is safe to (mis)use it to mark skbs on the error queue. +/* The error-queue test in skb_is_err_queue() matches this marker +* with the sock_rmem_free destructor installed by sock_queue_err_skb(). */ skb->pkt_type =PACKET_OUTGOING ;BUILD_BUG_ON (PACKET_OUTGOING ==0 );
net/socket.c +6 -5
@@ -740,12 +740,13 @@ EXPORT_SYMBOL(kernel_sendmsg_locked);static bool skb_is_err_queue (const struct sk_buff *skb) { -/* pkt_type of skbs enqueued on the error queue are set to -* PACKET_OUTGOING in skb_set_err_queue(). This is only safe to do -* in recvmsg, since skbs received on a local socket will never -* have a pkt_type of PACKET_OUTGOING. +/* Error-queue skbs are marked as PACKET_OUTGOING in +* skb_set_err_queue() and use the destructor installed by +* sock_queue_err_skb(). PACKET_OUTGOING alone is not unique: +* AF_PACKET outgoing taps use the same pkt_type. */ -return skb->pkt_type ==PACKET_OUTGOING ; +return skb->pkt_type ==PACKET_OUTGOING && + skb->destructor == sock_rmem_free; }/* On transmit, software and hardware timestamps are returned independently.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 172/342 net/rds: fix NULL deref in rds_ib_send_cqe_handler() on masked atomic completion
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit 34080db3e70ddf94c38512ad2331e3c3afca6cc1 ]
rds_ib_xmit_atomic() always programs a masked atomic opcode (IB_WR_MASKED_ATOMIC_CMP_AND_SWP or IB_WR_MASKED_ATOMIC_FETCH_AND_ADD) for every RDS atomic cmsg. But the completion-side switch in rds_ib_send_unmap_op() only handles the non-masked opcodes, so a masked atomic completion falls through to default and returns rm == NULL while send->s_op is left set. rds_ib_send_cqe_handler() then dereferences the NULL rm via rm->m_final_op, oopsing in softirq context. An unprivileged AF_RDS sendmsg() of an atomic cmsg over an active RDS/IB connection triggers it; on hardware that natively accepts masked atomics (mlx4, mlx5) no extra setup is needed.
RDS/IB: rds_ib_send_unmap_op: unexpected opcode 0xd in WR!
Oops: general protection fault [#1] SMP KASAN KASAN: null-ptr-deref in range [0x0000000000000190-0x0000000000000197] RIP: rds_ib_send_cqe_handler+0x25c/0xb10 (net/rds/ib_send.c:282) Call Trace: <IRQ> rds_ib_send_cqe_handler (net/rds/ib_send.c:282) poll_scq (net/rds/ib_cm.c:274) rds_ib_tasklet_fn_send (net/rds/ib_cm.c:294) tasklet_action_common (kernel/softirq.c:943) handle_softirqs (kernel/softirq.c:573) run_ksoftirqd (kernel/softirq.c:479) </IRQ> Kernel panic - not syncing: Fatal exception in interrupt
Handle the masked atomic opcodes in the same case as the non-masked
ones: they map to the same struct rds_message.atomic union member, so the existing container_of()/rds_ib_send_unmap_atomic() body is correct for them.
--- net/rds/ib_send.c | 2 ++ 1 file changed, 2 insertions(+)
net/rds/ib_send.c +2 -0
@@ -170,6 +170,8 @@ static struct rds_message *rds_ib_send_unmap_op(struct rds_ib_connection *ic,break ;case IB_WR_ATOMIC_FETCH_AND_ADD :case IB_WR_ATOMIC_CMP_AND_SWP : +case IB_WR_MASKED_ATOMIC_FETCH_AND_ADD : +case IB_WR_MASKED_ATOMIC_CMP_AND_SWP :if (send->s_op) { rm = container_of(send->s_op, struct rds_message, atomic);rds_ib_send_unmap_atomic (ic, send->s_op, wc_status);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 173/342 ip6_vti: fix incorrect tunnel matching in vti6_tnl_lookup()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit a5c0359f5cbc51a2e2b114d6041e0f3c73f903e9 ]
In vti6_tnl_lookup(), when an exact match for a tunnel fails, the code falls back to searching for wildcard tunnels:
- Tunnels matching the packet's local address, with any remote address wildcard remote).
- Tunnels matching the packet's remote address, with any local address (wildcard local).
However, vti6 stores all these different types of tunnels in the same hash table (ip6n->tnls_r_l) prone to hash collisions.
The bug is that the fallback search loops in vti6_tnl_lookup() were missing checks to ensure that the candidate tunnel actually has a wildcard address.
--- net/ipv6/ip6_vti.c | 2 ++ 1 file changed, 2 insertions(+)
net/ipv6/ip6_vti.c +2 -0
@@ -105,6 +105,7 @@ vti6_tnl_lookup(struct net *net, const struct in6_addr *remote, hash =HASH (&any, local);for_each_vti6_tunnel_rcu (ip6n->tnls_r_l[hash]) {if (ipv6_addr_equal (local, &t->parms.laddr) && +ipv6_addr_any (&t->parms.raddr) && (t->dev->flags &IFF_UP ))return t; }
@@ -112,6 +113,7 @@ vti6_tnl_lookup(struct net *net, const struct in6_addr *remote, hash =HASH (remote, &any);for_each_vti6_tunnel_rcu (ip6n->tnls_r_l[hash]) {if (ipv6_addr_equal (remote, &t->parms.raddr) && +ipv6_addr_any (&t->parms.laddr) && (t->dev->flags &IFF_UP ))return t; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 174/342 rds: mark snapshot pages dirty in rds_info_getsockopt()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
[ Upstream commit 512db8267b73a220a64180d95ab5eebe7c4964a8 ]
rds_info_getsockopt() pins the destination user pages with FOLL_WRITE and the RDS_INFO_* producers memcpy the snapshot into them through kmap_atomic(). Because that copy goes through the kernel direct map, the dirty bit on the user PTE is never set, so unpin_user_pages() releases the pages without marking them dirty. A file-backed destination page can then be reclaimed without writeback, silently discarding the copied data.
Use unpin_user_pages_dirty_lock() with make_dirty=true so the modified pages are marked dirty before they are unpinned.
--- net/rds/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/rds/info.c +1 -1
@@ -235,7 +235,7 @@ int rds_info_getsockopt(struct socket *sock, int optname, char __user *optval, out:if (pages) -unpin_user_pages (pages, nr_pages); +unpin_user_pages_dirty_lock (pages, nr_pages, true);kfree (pages);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 175/342 netfilter: x_tables: avoid leaking percpu counter pointers
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
[ Upstream commit f7f2fbb0e893a0238dc464f8d8c0f5609bec584f ]
The native and compat get-entries paths copy the fixed rule entry header from the kernelized rule blob to userspace before overwriting the entry's counter fields with a sanitized counter snapshot.
On SMP kernels, entry->counters.pcnt contains the percpu allocation address used by x_tables rule counters. A caller can provide a userspace buffer that faults during the initial fixed-header copy after pcnt has been copied but before the later sanitized counter copy runs. The syscall then returns -EFAULT while leaving the raw percpu pointer in userspace.
Copy only the fixed entry prefix before counters from the kernelized rule blob, then copy the sanitized counter snapshot into the counter field. Apply this ordering to the IPv4, IPv6, and ARP native and compat get-entries implementations so a fault cannot expose the internal percpu counter pointer.
--- net/ipv4/netfilter/arp_tables.c | 15 ++++++--------- net/ipv4/netfilter/ip_tables.c | 15 ++++++--------- net/ipv6/netfilter/ip6_tables.c | 15 ++++++--------- 3 files changed, 18 insertions(+), 27 deletions(-)
net/ipv4/netfilter/arp_tables.c +6 -9
@@ -701,14 +701,12 @@ static int copy_entries_to_user(unsigned int total_size,const struct xt_entry_target *t; e = loc_cpu_entry + off; -if (copy_to_user (userptr + off, e,sizeof (*e))) { - ret = -EFAULT ; - goto free_counters; - } -if (copy_to_user (userptr + off +if (copy_to_user (userptr + off, e, + offsetof(struct arpt_entry , counters)) || +copy_to_user (userptr + off + offsetof(struct arpt_entry , counters), &counters[num], -sizeof (counters[num])) !=0 ) { +sizeof (counters[num]))) { ret = -EFAULT ; goto free_counters; }
@@ -1326,9 +1324,8 @@ static int compat_copy_entry_to_user(struct arpt_entry *e, void __user **dstptr, origsize = *size; ce = *dstptr; -if (copy_to_user (ce, e,sizeof (struct arpt_entry )) !=0 || -copy_to_user (&ce->counters, &counters[i], -sizeof (counters[i])) !=0 ) +if (copy_to_user (ce, e, offsetof(struct compat_arpt_entry , counters)) || +copy_to_user (&ce->counters, &counters[i],sizeof (counters[i])))return -EFAULT ; *dstptr +=sizeof (struct compat_arpt_entry );
net/ipv4/netfilter/ip_tables.c +6 -9
@@ -833,14 +833,12 @@ copy_entries_to_user(unsigned int total_size,const struct xt_entry_target *t; e = loc_cpu_entry + off; -if (copy_to_user (userptr + off, e,sizeof (*e))) { - ret = -EFAULT ; - goto free_counters; - } -if (copy_to_user (userptr + off +if (copy_to_user (userptr + off, e, + offsetof(struct ipt_entry , counters)) || +copy_to_user (userptr + off + offsetof(struct ipt_entry , counters), &counters[num], -sizeof (counters[num])) !=0 ) { +sizeof (counters[num]))) { ret = -EFAULT ; goto free_counters; }
@@ -1229,9 +1227,8 @@ compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr, origsize = *size; ce = *dstptr; -if (copy_to_user (ce, e,sizeof (struct ipt_entry )) !=0 || -copy_to_user (&ce->counters, &counters[i], -sizeof (counters[i])) !=0 ) +if (copy_to_user (ce, e, offsetof(struct compat_ipt_entry , counters)) || +copy_to_user (&ce->counters, &counters[i],sizeof (counters[i])))return -EFAULT ; *dstptr +=sizeof (struct compat_ipt_entry );
net/ipv6/netfilter/ip6_tables.c +6 -9
@@ -850,14 +850,12 @@ copy_entries_to_user(unsigned int total_size,const struct xt_entry_target *t; e = loc_cpu_entry + off; -if (copy_to_user (userptr + off, e,sizeof (*e))) { - ret = -EFAULT ; - goto free_counters; - } -if (copy_to_user (userptr + off +if (copy_to_user (userptr + off, e, + offsetof(struct ip6t_entry , counters)) || +copy_to_user (userptr + off + offsetof(struct ip6t_entry , counters), &counters[num], -sizeof (counters[num])) !=0 ) { +sizeof (counters[num]))) { ret = -EFAULT ; goto free_counters; }
@@ -1246,9 +1244,8 @@ compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr, origsize = *size; ce = *dstptr; -if (copy_to_user (ce, e,sizeof (struct ip6t_entry )) !=0 || -copy_to_user (&ce->counters, &counters[i], -sizeof (counters[i])) !=0 ) +if (copy_to_user (ce, e, offsetof(struct compat_ip6t_entry , counters)) || +copy_to_user (&ce->counters, &counters[i],sizeof (counters[i])))return -EFAULT ; *dstptr +=sizeof (struct compat_ip6t_entry );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 176/342 netfilter: nft_exthdr: fix register tracking for F_PRESENT flag
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 772cecf198da732faebb5dcfc46d66a505be8495 ]
nft_exthdr_init() passes user-controlled priv->len to nft_parse_register_store(), which marks that many bytes in the register bitmap as initialized. However, when NFT_EXTHDR_F_PRESENT is set, the eval paths write only 1 byte (nft_reg_store8) or 4 bytes (*dest = 0 on TCP/DCCP error path). When len > 4, registers beyond the first are never written, retaining uninitialized stack data from nft_regs.
Bail out if userspace requests too much data when F_PRESENT is set.
--- net/netfilter/nft_exthdr.c | 3 +++ 1 file changed, 3 insertions(+)
net/netfilter/nft_exthdr.c +3 -0
@@ -454,6 +454,9 @@ static int nft_exthdr_init(const struct nft_ctx *ctx,return err; } +if ((flags &NFT_EXTHDR_F_PRESENT ) && len !=1 ) +return -EINVAL ; + priv->type =nla_get_u8 (tb[NFTA_EXTHDR_TYPE ]); priv->offset = offset; priv->len = len;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 177/342 net: mvpp2: sync RX data at the hardware packet offset
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Til Kaiser <[email protected]>
[ Upstream commit 180235600934bef6add3be637c296d6cf3272e67 ]
mvpp2 programs the RX queue packet offset, so hardware writes received data at dma_addr + MVPP2_SKB_HEADROOM. The current CPU sync starts at dma_addr and only covers rx_bytes + MVPP2_MH_SIZE bytes, which syncs the unused headroom and misses the same number of bytes at the packet tail.
On non-coherent DMA systems this can leave the CPU reading stale cache contents for the end of the received frame.
Use dma_sync_single_range_for_cpu() with MVPP2_SKB_HEADROOM as the range offset so the sync covers the Marvell header and packet data actually written by hardware.
--- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +4 -3
@@ -3588,9 +3588,10 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, dma_dir =DMA_FROM_DEVICE ; } -dma_sync_single_for_cpu (dev->dev.parent, dma_addr, - rx_bytes +MVPP2_MH_SIZE , - dma_dir); +dma_sync_single_range_for_cpu (dev->dev.parent, dma_addr, +MVPP2_SKB_HEADROOM , + rx_bytes +MVPP2_MH_SIZE , + dma_dir);/* Buffer header not supported */ if (rx_status &MVPP2_RXD_BUF_HDR )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 178/342 netfilter: nft_tunnel: fix use-after-free on object destroy
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tristan Madani <[email protected]>
commit c32b26aaa2f9216520a38b3f4bfeec846eb3eb8a upstream.
nft_tunnel_obj_destroy() calls metadata_dst_free() which directly kfree()s the metadata_dst, ignoring the dst_entry refcount. Packets that took a reference via dst_hold() in nft_tunnel_obj_eval() and are still queued (e.g. in a netem qdisc) are left with a dangling pointer. When these packets are eventually dequeued, dst_release() operates on freed memory.
Replace metadata_dst_free() with dst_release() so the metadata_dst is freed only after all references are dropped. The dst subsystem already handles metadata_dst cleanup in dst_destroy() when DST_METADATA is set.
--- net/netfilter/nft_tunnel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/nft_tunnel.c +1 -1
@@ -669,7 +669,7 @@ static void nft_tunnel_obj_destroy(const {struct nft_tunnel_obj *priv =nft_obj_data (obj); -metadata_dst_free (priv->md); +dst_release (&priv->md->dst); }static struct nft_object_type nft_tunnel_obj_type;
Greg Kroah-Hartman
maintainerPATCH 179/342 Bluetooth: L2CAP: reject BR/EDR signaling packets over MTUsig
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit dd214733544427587a95f66dbf3adff072568990 upstream.
net/bluetooth/l2cap_core.c:l2cap_sig_channel() accepts BR/EDR signaling packets up to the channel MTU and dispatches each command without enforcing the signaling MTU (MTUsig). A Bluetooth BR/EDR peer within radio range can send a fixed-channel CID 0x0001 packet that is larger than MTUsig and contains many L2CAP_ECHO_REQ commands before pairing. In a real-radio stock-kernel run, one 681-byte signaling packet containing 168 zero-length ECHO_REQ commands made the target transmit 168 ECHO_RSP frames over about 220 ms.
Impact: a Bluetooth BR/EDR peer within radio range, before pairing, can force 168 ECHO_RSP frames from one 681-byte fixed-channel signaling packet containing packed ECHO_REQ commands.
Define Linux's BR/EDR signaling MTU as the spec minimum of 48 bytes and reject any larger signaling packet with one L2CAP_COMMAND_REJECT_RSP carrying L2CAP_REJ_MTU_EXCEEDED before any command is dispatched.
The Bluetooth Core spec wording for MTUExceeded says the reject identifier shall match the first request command in the packet, and that packets containing only responses shall be silently discarded. Linux intentionally deviates from that prescription: silently discarding desynchronizes the peer because the remote stack never learns its responses were dropped, and locating the first request command requires walking command headers past MTUsig, i.e. processing bytes from a packet we have already decided is too large to process. We therefore always emit one reject and use the identifier from the first command header, a single fixed-offset byte read.
The unrestricted BR/EDR signaling parser and ECHO_REQ response path both trace to the initial git import; no later introducing commit is available for a Fixes tag.
--- include/net/bluetooth/l2cap.h | 1 net/bluetooth/l2cap_core.c | 46 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+)
include/net/bluetooth/l2cap.h +1 -0
@@ -33,6 +33,7 @@/* L2CAP defaults */ #define L2CAP_DEFAULT_MTU 672#define L2CAP_DEFAULT_MIN_MTU 48 +#define L2CAP_SIG_MTU 48/* BR/EDR signaling MTU */ #define L2CAP_DEFAULT_FLUSH_TO 0xFFFF#define L2CAP_EFS_DEFAULT_FLUSH_TO 0xFFFFFFFF#define L2CAP_DEFAULT_TX_WINDOW 63
net/bluetooth/l2cap_core.c +46 -0
58 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 180/342 drm/i915/gem: Fix phys BO pread/pwrite with offset
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joonas Lahtinen <[email protected]>
commit d21ad938398bca695a511307de38a65889e3b354 upstream.
sg_page() returns struct page pointer not (void *) so the scaling of pread/pwrite is wrong for phys BO and wrong parts of BO would be accessed if non-zero offset is used.
Last impacted platform with overlay or cursor planes using phys mapping was Gen3/945G/Lakeport.
--- drivers/gpu/drm/i915/gem/i915_gem_phys.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
drivers/gpu/drm/i915/gem/i915_gem_phys.c +15 -4
@@ -17,6 +17,17 @@#include "i915_gem_region.h" #include "i915_scatterlist.h" +/* Abuse scatterlist to store pointer instead of struct page. */ +static inline void __set_phys_vaddr (struct scatterlist *sg,void *vaddr) +{ +sg_assign_page (sg, (struct page *)vaddr); +} + +static inline void *__get_phys_vaddr (struct scatterlist *sg) +{ +return (void *)sg_page (sg); +} +static int i915_gem_object_get_pages_phys (struct drm_i915_gem_object *obj) {struct address_space *mapping = obj->base.filp->f_mapping;
@@ -52,7 +63,7 @@ static int i915_gem_object_get_pages_phy sg->offset =0 ; sg->length = obj->base.size; -sg_assign_page (sg, (struct page *)vaddr); +__set_phys_vaddr (sg, vaddr);sg_dma_address (sg) = dma;sg_dma_len (sg) = obj->base.size;
@@ -94,7 +105,7 @@ i915_gem_object_put_pages_phys(struct dr struct sg_table *pages) {dma_addr_t dma =sg_dma_address (pages->sgl); -void *vaddr =sg_page (pages->sgl); +void *vaddr =__get_phys_vaddr (pages->sgl);__i915_gem_object_release_shmem (obj, pages, false);
@@ -138,7 +149,7 @@ static int phys_pwrite(struct drm_i915_gem_object *obj, const struct drm_i915_gem_pwrite *args) { - void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset; + void *vaddr = __get_phys_vaddr(obj->mm.pages->sgl) + args->offset;char __user *user_data =u64_to_user_ptr (args->data_ptr);int err;
@@ -169,7 +180,7 @@ static int phys_pread(struct drm_i915_gem_object *obj, const struct drm_i915_gem_pread *args) { - void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset; + void *vaddr = __get_phys_vaddr(obj->mm.pages->sgl) + args->offset;char __user *user_data =u64_to_user_ptr (args->data_ptr);int err;
Greg Kroah-Hartman
maintainerPATCH 181/342 xfrm: espintcp: do not reuse an in-progress partial send
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wyatt Feng <[email protected]>
commit c381039ade2e161ab08c0eda73c4f8b9a7115928 upstream.
espintcp keeps a single in-flight transmit in ctx->partial. Before building a new sk_msg, espintcp_sendmsg() first tries to flush that state through espintcp_push_msgs().
For blocking callers, espintcp_push_msgs() may return success even when the previous partial send is still pending. espintcp_sendmsg() would then reinitialize emsg->skmsg and reuse ctx->partial while the old transfer still owns that state.
Do not rebuild the send message when ctx->partial is still in progress. If espintcp_push_msgs() returns with emsg->len still set, fail the new send instead of overwriting the live partial state.
This is a memory-safety fix: reusing the live partial-send state can leave a stale offset attached to a new sk_msg and lead to an out-of- bounds read in the send path.
tcp_sendmsg_locked() already handles waiting for send buffer memory, so the fix here is just to preserve espintcp's one-message-at-a-time transmit state.
--- net/xfrm/espintcp.c | 4 ++++ 1 file changed, 4 insertions(+)
net/xfrm/espintcp.c +4 -0
@@ -340,6 +340,10 @@ static int espintcp_sendmsg(struct sock err = -ENOBUFS ; goto unlock; } +if (emsg->len) { + err = -ENOBUFS ; + goto unlock; + }sk_msg_init (&emsg->skmsg);while (1 ) {
Greg Kroah-Hartman
maintainerPATCH 182/342 USB: serial: io_ti: fix heap overflow in get_manuf_info()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adrian Korwel <[email protected]>
commit 183c1076eca43bbb3e7bdf597456f91d81c73e74 upstream.
get_manuf_info() reads le16_to_cpu(rom_desc->Size) bytes from the device I2C EEPROM into a buffer allocated with kmalloc_obj(), which is sizeof(struct edge_ti_manuf_descriptor) = 10 bytes.
The Size field comes from the device and is only validated (in check_i2c_image()) to make sure the descriptor fits within TI_MAX_I2C_SIZE (16384 bytes), not against the destination buffer size. A malicious USB device can therefore set Size to any value up to 16377, causing a heap overflow of up to 16367 bytes when plugged into a host running this driver.
valid_csum() is called after read_rom() and also iterates buffer[0..Size-1], compounding the out-of-bounds access.
Fix by rejecting descriptors with unexpected length before calling read_rom().
--- drivers/usb/serial/io_ti.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/usb/serial/io_ti.c +6 -0
@@ -776,6 +776,12 @@ static int get_manuf_info(struct edgepor }/* Read the descriptor data */ +if (le16_to_cpu (rom_desc->Size) !=sizeof (struct edge_ti_manuf_descriptor )) { +dev_err (dev,"unexpected Edge descriptor length: %u\n" , +le16_to_cpu (rom_desc->Size)); + status = -EINVAL ; + goto exit; + } status =read_rom (serial, start_address+sizeof (struct ti_i2c_desc ),le16_to_cpu (rom_desc->Size), buffer);if (status)
Greg Kroah-Hartman
maintainerPATCH 183/342 USB: serial: io_ti: fix heap overflow in build_i2c_fw_hdr()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adrian Korwel <[email protected]>
commit 0fd2b00b2d3d05e3eaa13342b3dfb0fa85c226ae upstream.
build_i2c_fw_hdr() allocates a fixed-size buffer of (16*1024 - 512) + sizeof(struct ti_i2c_firmware_rec) bytes, then copies le16_to_cpu(img_header->Length) bytes into it without validating that Length fits within the available space after the firmware record header.
img_header->Length is a __le16 from the firmware file and can be up to 65535. check_fw_sanity() validates the total firmware size but not img_header->Length specifically.
Fix by rejecting images where img_header->Length exceeds the available destination space.
--- drivers/usb/serial/io_ti.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/usb/serial/io_ti.c +5 -0
@@ -847,6 +847,11 @@ static int build_i2c_fw_hdr(u8 *header,/* Pointer to fw_down memory image */ img_header = (struct ti_i2c_image_header *)&fw->data[4 ]; +if (le16_to_cpu (img_header->Length) > + buffer_size -sizeof (struct ti_i2c_firmware_rec )) { +kfree (buffer); +return -EINVAL ; + }memcpy (buffer +sizeof (struct ti_i2c_firmware_rec ), &fw->data[4 +sizeof (struct ti_i2c_image_header )],le16_to_cpu (img_header->Length));
Greg Kroah-Hartman
maintainerPATCH 184/342 USB: serial: option: add usb-id for Dell Wireless DW5826e-m
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jack Wu <[email protected]>
commit 1938fb9fe38c4f04a3f30bea44f8071c80a63be4 upstream.
Add support for Dell DW5826e-m with USB-id 0x413c:0x81ea
T: Bus=03 Lev=01 Prnt=01 Port=04 Cnt=01 Dev#= 8 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=413c ProdID=81ea Rev= 5.04 S: Manufacturer=DELL S: Product=DW5826e-m Qualcomm Snapdragon X12 Global LTE-A S: SerialNumber=358988870177734 C:* #Ifs= 7 Cfg#= 1 Atr=a0 MxPwr=500mA A: FirstIf#=12 IfCount= 2 Cls=02(comm.) Sub=0e Prot=00 I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=usbfs E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=87(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I:* If#=12 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=88(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#=13 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I:* If#=13 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=0f(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
--- drivers/usb/serial/option.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/usb/serial/option.c +3 -0
@@ -202,6 +202,7 @@ static void option_instat_callback(struc#define DELL_PRODUCT_5821E_ESIM 0x81e0#define DELL_PRODUCT_5829E_ESIM 0x81e4#define DELL_PRODUCT_5829E 0x81e6 +#define DELL_PRODUCT_5826E_ESIM 0x81ea#define DELL_PRODUCT_FM101R_ESIM 0x8213#define DELL_PRODUCT_FM101R 0x8215
@@ -1123,6 +1124,8 @@ static const struct usb_device_id option .driver_info = RSVD(0) | RSVD(6) }, { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5829E_ESIM), .driver_info = RSVD(0) | RSVD(6) }, + { USB_DEVICE_INTERFACE_CLASS(DELL_VENDOR_ID, DELL_PRODUCT_5826E_ESIM, 0xff), + .driver_info = RSVD(1) | RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(DELL_VENDOR_ID, DELL_PRODUCT_FM101R, 0xff) }, { USB_DEVICE_INTERFACE_CLASS(DELL_VENDOR_ID, DELL_PRODUCT_FM101R_ESIM, 0xff) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, /* ADU-E100, ADU-310 */
Greg Kroah-Hartman
maintainerPATCH 185/342 USB: serial: kl5kusb105: fix bulk-out buffer overflow
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit 96d47e40bf9db4a9efd5c8fb53287a508d165f14 upstream.
klsi_105_prepare_write_buffer() is called by the generic write path with the bulk-out buffer and its size (bulk_out_size, 64 bytes). It stores a two-byte length header at the start of the buffer and copies the payload from the write fifo starting at buf + KLSI_HDR_LEN, but passes the full buffer size as the number of bytes to copy:
count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN,
size, &port->lock);
When the fifo holds at least size bytes, size bytes are copied starting two bytes into the size-byte buffer, writing KLSI_HDR_LEN bytes past its end. Copy at most size - KLSI_HDR_LEN bytes instead, leaving room for the header as safe_serial already does.
Writing bulk_out_size or more bytes to the tty triggers a slab out-of-bounds write, observed with KASAN by emulating the device with dummy_hcd and raw-gadget:
BUG: KASAN: slab-out-of-bounds in kfifo_copy_out+0x83/0xc0 Write of size 64 at addr ffff888112c62202 by task python3 kfifo_copy_out klsi_105_prepare_write_buffer [kl5kusb105] usb_serial_generic_write_start [usbserial] Allocated by task 139: usb_serial_probe [usbserial] The buggy address is located 2 bytes inside of allocated 64-byte region
The out-of-bounds write no longer occurs with this change applied.
--- drivers/usb/serial/kl5kusb105.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/usb/serial/kl5kusb105.c +2 -2
@@ -359,8 +359,8 @@ static int klsi_105_prepare_write_bufferunsigned char *buf = dest;int count; - count =kfifo_out_locked (&port->write_fifo, buf +KLSI_HDR_LEN , size, - &port->lock); + count =kfifo_out_locked (&port->write_fifo, buf +KLSI_HDR_LEN , + size -KLSI_HDR_LEN , &port->lock);put_unaligned_le16 (count, buf);return count +KLSI_HDR_LEN ;
Greg Kroah-Hartman
maintainerPATCH 186/342 ALSA: timer: Fix UAF at snd_timer_user_params()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit 053a401b592be424fea9d57c789f66cd5d8cec11 upstream.
At releasing a timer object, e.g. when a userspace timer (CONFIG_SND_UTIMER) gets closed and snd_timer_free() is called, it tries to detach the timer instances and release the resources. However, it's still possible that other in-flight tasks are holding the timer instance where the to-be-deleted timer object is associated, and this may lead to racy accesses.
Fortunately, most of ioctls dealing with the timer instance list already have the protection with register_mutex, and this also avoids such races. But, SNDRV_TIMER_IOCTL_PARAMS isn't protected, hence the concurrent ioctl may lead to use-after-free.
This patch just adds the guard with register_mutex to protect snd_timer_user_params() for covering the code path as a quick workaround. It's no hot-path but rather a rarely issued ioctl, so the performance penalty doesn't matter.
--- sound/core/timer.c | 1 + 1 file changed, 1 insertion(+)
sound/core/timer.c +1 -0
@@ -1842,6 +1842,7 @@ static int snd_timer_user_params(structstruct snd_timer *t;int err; +guard (mutex)(®ister_mutex); tu = file->private_data;if (!tu->timeri)return -EBADFD ;
Greg Kroah-Hartman
maintainerPATCH 187/342 drm/amd/display: Reject gpio_bitshift >= 32 in bios_parser_get_gpio_pin_info()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harry Wentland <[email protected]>
commit 49c3da65961fe9857c831d47fa1989084e87514a upstream.
[Why & How]gpio_bitshift is a uint8_t read directly from the VBIOS GPIO pin table. If the value is >= 32, the expression "1 << gpio_bitshift" triggers undefined behaviour in C (shift count exceeds type width). On x86 the shift is silently masked to 5 bits, producing an incorrect GPIO mask that may cause wrong MMIO register bits to be toggled.
Validate gpio_bitshift before use and return BP_RESULT_BADBIOSTABLE for out-of-range values.
--- drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +4 -2
@@ -546,8 +546,10 @@ static enum bp_result bios_parser_get_gp info->offset_en = info->offset +1 ; info->offset_mask = info->offset -1 ; - info->mask = (uint32_t ) (1 << - header->gpio_pin[i].gpio_bitshift); +if (header->gpio_pin[i].gpio_bitshift >=32 ) +return BP_RESULT_BADBIOSTABLE ; + + info->mask =1u << header->gpio_pin[i].gpio_bitshift; info->mask_y = info->mask +2 ; info->mask_en = info->mask +1 ; info->mask_mask = info->mask -1 ;
Greg Kroah-Hartman
maintainerPATCH 188/342 RDMA/srp: bound SRP_RSP sense copy by the received length
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 13e91fd076306f5d0cdfa14f53d69e37274723c4 upstream.
srp_process_rsp() copies sense data from rsp->data + resp_data_len, where resp_data_len is the full 32-bit value supplied by the SRP target and is never checked against the number of bytes actually received (wc->byte_len). The copy length is bounded to SCSI_SENSE_BUFFERSIZE, so at most 96 bytes are copied, but the source offset is not bounded.
A malicious or compromised SRP target on the InfiniBand/RoCE fabric that the initiator has logged into can return an SRP_RSP with SRP_RSP_FLAG_SNSVALID set and a large resp_data_len. The receive buffer is allocated at the target-chosen max_ti_iu_len, so the source of the sense copy lands past the bytes actually received; with resp_data_len near 0xFFFFFFFF it is gigabytes past the buffer and the read faults.
Copy the sense data only if it has not been truncated, that is, only if the response header, the response data, and the sense region fit within the bytes actually received; otherwise drop the sense and log. The in-tree iSER and NVMe-RDMA receive paths already bound their parse by wc->byte_len; this brings ib_srp into line with them.
--- drivers/infiniband/ulp/srp/ib_srp.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-)
drivers/infiniband/ulp/srp/ib_srp.c +24 -6
@@ -1929,7 +1929,8 @@ static int srp_post_recv(struct srp_rdmareturn ib_post_recv (ch->qp, &wr,NULL ); } -static void srp_process_rsp (struct srp_rdma_ch *ch,struct srp_rsp *rsp) +static void srp_process_rsp (struct srp_rdma_ch *ch,struct srp_rsp *rsp, +u32 byte_len) {struct srp_target_port *target = ch->target;struct srp_request *req;
@@ -1970,10 +1971,27 @@ static void srp_process_rsp(struct srp_r scmnd->result = rsp->status;if (rsp->flags &SRP_RSP_FLAG_SNSVALID ) { -memcpy (scmnd->sense_buffer, rsp->data + -be32_to_cpu (rsp->resp_data_len), -min_t (int,be32_to_cpu (rsp->sense_data_len), -SCSI_SENSE_BUFFERSIZE )); +u32 resp_len =be32_to_cpu (rsp->resp_data_len); +u32 sense_len =be32_to_cpu (rsp->sense_data_len); + +/* +* The sense data starts resp_data_len bytes past the +* response data area; both lengths come from the +* target-controlled response. Copy the sense data +* only if it has not been truncated, that is, only if +* the full sense region fits within the bytes actually +* received. Otherwise the copy source would run past +* the receive buffer (sized to the target-chosen +* max_ti_iu_len), reading out of bounds. +*/ +if (sizeof (*rsp) + (u64 )resp_len + sense_len <= byte_len) +memcpy (scmnd->sense_buffer, + rsp->data + resp_len, +min (sense_len,SCSI_SENSE_BUFFERSIZE )); +else +shost_printk (KERN_ERR , target->scsi_host, +"dropping truncated sense data (resp_data_len %u sense_data_len %u, %u bytes received)\n" , + resp_len, sense_len, byte_len); }if (unlikely (rsp->flags &SRP_RSP_FLAG_DIUNDER ))
@@ -2083,7 +2101,7 @@ static void srp_recv_done(struct ib_cq *switch (opcode) {case SRP_RSP : -srp_process_rsp (ch, iu->buf); +srp_process_rsp (ch, iu->buf, wc->byte_len);break ;case SRP_CRED_REQ :
Greg Kroah-Hartman
maintainerPATCH 189/342 ARM: socfpga: Fix OF node refcount leak in SMP setup
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuho Choi <[email protected]>
commit 63838c323924fe4a78b2323bd45aa1030f72ca60 upstream.
socfpga_smp_prepare_cpus() looks up the Cortex-A9 SCU node with of_find_compatible_node(), which returns a node reference that must be released with of_node_put().
The function maps the SCU registers and then returns without dropping that reference, leaking the node on both the success path and the of_iomap() failure path.
Drop the reference once the mapping attempt is complete. The returned MMIO mapping does not depend on keeping the device node reference held.
--- arch/arm/mach-socfpga/platsmp.c | 1 + 1 file changed, 1 insertion(+)
arch/arm/mach-socfpga/platsmp.c +1 -0
@@ -78,6 +78,7 @@ static void __init socfpga_smp_prepare_c } socfpga_scu_base_addr =of_iomap (np,0 ); +of_node_put (np);if (!socfpga_scu_base_addr)return ;scu_enable (socfpga_scu_base_addr);
Greg Kroah-Hartman
maintainerPATCH 190/342 vsock/vmci: fix sk_ack_backlog leak on failed handshake
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raf Dickson <[email protected]>
commit c05fa14db43ebef3bd862ca9d073981c0358b3f0 upstream.
When vmci_transport_recv_connecting_server() returns an error, vmci_transport_recv_listen() calls vsock_remove_pending() but never calls sk_acceptq_removed(). This leaves sk_ack_backlog incremented permanently.
Repeated handshake failures (malformed packets, queue pair alloc failure, event subscribe failure) cause sk_ack_backlog to climb toward sk_max_ack_backlog. Once it reaches the limit the listener permanently refuses all new connections with -ECONNREFUSED, a silent denial of service requiring a process restart to recover.
The two existing sk_acceptq_removed() calls in af_vsock.c do not cover this path: line 764 checks vsock_is_pending() which returns false after vsock_remove_pending(), and line 1889 is only reached on successful accept().
Fix by balancing sk_acceptq_added() with sk_acceptq_removed() on the error path.
--- net/vmw_vsock/vmci_transport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/vmw_vsock/vmci_transport.c +3 -1
@@ -974,8 +974,10 @@ static int vmci_transport_recv_listen(st err = -EINVAL ; } -if (err <0 ) +if (err <0 ) {vsock_remove_pending (sk, pending); +sk_acceptq_removed (sk); + }release_sock (pending);vmci_transport_release_pending (pending);
Greg Kroah-Hartman
maintainerPATCH 191/342 IB/isert: Reject login PDUs shorter than ISER_HEADERS_LEN
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 29e7b925ae6df64894e82ab6419994dc25580a8a upstream.
In drivers/infiniband/ulp/isert/ib_isert.c, isert_login_recv_done() computes the login request payload length as wc->byte_len minus ISER_HEADERS_LEN with no lower bound, and login_req_len is a signed int. A remote iSER initiator can post a login Send work request carrying fewer than ISER_HEADERS_LEN (76) bytes, so the subtraction underflows and login_req_len becomes negative.
isert_rx_login_req() then reads that negative length back into a signed int, takes size = min(rx_buflen, MAX_KEY_VALUE_PAIRS), and because the min() is signed it keeps the negative value; the value is then passed as the memcpy() length and sign-extended to a multi-gigabyte size_t. The copy into the 8192-byte login->req_buf runs far out of bounds and faults, crashing the target node. The login phase precedes iSCSI authentication, so no credentials are required to reach this path.
Reject any login PDU shorter than ISER_HEADERS_LEN before the subtraction, mirroring the existing early return on a failed work completion, so login_req_len can never go negative. The upper bound was already safe: a posted login buffer cannot deliver more than ISER_RX_PAYLOAD_SIZE, so the difference stays at or below MAX_KEY_VALUE_PAIRS and the existing min() clamps it; only the missing lower bound needs to be added.
--- drivers/infiniband/ulp/isert/ib_isert.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/infiniband/ulp/isert/ib_isert.c +6 -0
@@ -1388,6 +1388,12 @@ isert_login_recv_done(struct ib_cq *cq,ib_dma_sync_single_for_cpu (ib_dev, isert_conn->login_desc->dma_addr,ISER_RX_SIZE ,DMA_FROM_DEVICE ); +if (unlikely (wc->byte_len <ISER_HEADERS_LEN )) { +isert_dbg ("login request length %u is too short\n" , + wc->byte_len); +return ; + } + isert_conn->login_req_len = wc->byte_len -ISER_HEADERS_LEN ;if (isert_conn->conn) {
Greg Kroah-Hartman
maintainerPATCH 192/342 pidfd: refuse access to tasks that have started exiting harder
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 62c4d31d78294bd61cf3403626b789e854357177 upstream.
The recent ptrace fix closed a hole where someone could rely on task->mm becoming NULL during do_exit() to bypass dumpability checks. This api here leans on on the very same check and so inherits the fix.
But there is no good reason to let it succeed at all once the target has entered do_exit(). PF_EXITING is set by exit_signals() at the very top of do_exit(), before exit_mm() and exit_files() run. Once we observe it, the task is committed to dying and exit_files() will release the fdtable shortly.
--- kernel/pid.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
kernel/pid.c +5 -3
@@ -637,10 +637,12 @@ static struct file *__pidfd_fget(structif (ret)return ERR_PTR (ret); -if (ptrace_may_access (task,PTRACE_MODE_ATTACH_REALCREDS )) - file =fget_task (task, fd); -else +if (!ptrace_may_access (task,PTRACE_MODE_ATTACH_REALCREDS )) file =ERR_PTR (-EPERM ); +else if (task->flags &PF_EXITING ) + file =ERR_PTR (-ESRCH ); +else + file =fget_task (task, fd);up_read (&task->signal->exec_update_lock);
Greg Kroah-Hartman
maintainerPATCH 193/342 fuse: reject fuse_notify() pagecache ops on directories
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jann Horn <[email protected]>
commit 9c954499d43aefac01c5dfb57a82b13d2dcf4b94 upstream.
The operations FUSE_NOTIFY_STORE and FUSE_NOTIFY_RETRIEVE allow the FUSE daemon to actively write/read pagecache contents.
For directories with FOPEN_CACHE_DIR, the pagecache is used as kernel-internal cache storage, and userspace is not supposed to have direct access to this cache - in particular, fuse_parse_cache() will hit WARN_ON() if the cache contains bogus data.
Reject FUSE_NOTIFY_STORE and FUSE_NOTIFY_RETRIEVE on anything other than regular files with -EINVAL.
--- fs/fuse/dev.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
fs/fuse/dev.c +8 -1
@@ -1603,6 +1603,10 @@ static int fuse_notify_store(struct fuse inode =fuse_ilookup (fc, nodeid,NULL );if (!inode) goto out_up_killsb; +if (!S_ISREG (inode->i_mode)) { + err = -EINVAL ; + goto out_iput; + } mapping = inode->i_mapping; index = outarg.offset >>PAGE_SHIFT ;
@@ -1774,7 +1778,10 @@ static int fuse_notify_retrieve(struct f inode =fuse_ilookup (fc, nodeid, &fm);if (inode) { - err =fuse_retrieve (fm, inode, &outarg); +if (!S_ISREG (inode->i_mode)) + err = -EINVAL ; +else + err =fuse_retrieve (fm, inode, &outarg);iput (inode); }up_read (&fc->killsb);
Greg Kroah-Hartman
maintainerPATCH 194/342 i2c: qcom-cci: Fix NULL pointer dereference in cci_remove()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vladimir Zapolskiy <[email protected]>
commit 729ac5a4b966aac42e08a94dea966f4429008548 upstream.
On all modern platforms Qualcomm CCI controller provides two I2C masters, and on particular boards only one I2C master may be initialized, and in such cases the device unbinding or driver removal causes a NULL pointer dereference, because cci_halt() is called for all two I2C masters, but a completion is initialized only for the single enabled master:
% rmmod i2c-qcom-cci
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
<snip>
Call trace:
__wait_for_common+0x194/0x1a8 (P)
wait_for_completion_timeout+0x20/0x2c
cci_remove+0xc4/0x138 [i2c_qcom_cci]
platform_remove+0x20/0x30
device_remove+0x4c/0x80
device_release_driver_internal+0x1c8/0x224
driver_detach+0x50/0x98
bus_remove_driver+0x6c/0xbc
driver_unregister+0x30/0x60
platform_driver_unregister+0x14/0x20
qcom_cci_driver_exit+0x18/0x1008 [i2c_qcom_cci]
....--- drivers/i2c/busses/i2c-qcom-cci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-qcom-cci.c +1 -1
@@ -683,8 +683,8 @@ static int cci_remove(struct platform_deif (cci->master[i].cci) {i2c_del_adapter (&cci->master[i].adap);of_node_put (cci->master[i].adap.dev.of_node); +cci_halt (cci, i); } -cci_halt (cci, i); }disable_irq (cci->irq);
Greg Kroah-Hartman
maintainerPATCH 195/342 i2c: tegra: Fix NOIRQ suspend/resume
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Akhil R <[email protected]>
commit 656646b3847ac6a21b074a813223feef2aadd6e2 upstream.
The Tegra I2C driver relies on runtime PM to wake up the controller before each transfer. However, runtime PM is disabled between the system suspend and NOIRQ suspend. If an I2C device initiates a transfer during this window, the I2C controller fails to wake up and the transfer fails. To handle this, the controller must be kept available for this period to allow transfers.
Rework the I2C controller's system PM callbacks such that the controller is resumed from runtime suspend during system suspend and it stays RPM_ACTIVE throughout the suspend-resume cycle until it is runtime suspended back in the system resume. The clocks are disabled in NOIRQ suspend and enabled back in NOIRQ resume by calling the controller's runtime PM functions directly.
--- drivers/i2c/busses/i2c-tegra.c | 53 +++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 23 deletions(-)
drivers/i2c/busses/i2c-tegra.c +30 -23
@@ -1873,28 +1873,37 @@ static int __maybe_unused tegra_i2c_runtstatic int __maybe_unused tegra_i2c_suspend (struct device *dev) { +/* +* Bring the controller up and hold a usage count so it stays +* available until the noirq phase. +*/ +return pm_runtime_resume_and_get (dev); +} + +static int __maybe_unused tegra_i2c_suspend_noirq (struct device *dev) +{struct tegra_i2c_dev *i2c_dev =dev_get_drvdata (dev); -int err;i2c_mark_adapter_suspended (&i2c_dev->adapter); -if (!pm_runtime_status_suspended (dev)) { - err =tegra_i2c_runtime_suspend (dev); -if (err) -return err; - } - -return 0 ; +/* +* Runtime PM is already disabled at this point, so invoke the +* runtime_suspend callback directly to put the controller down. +*/ +return tegra_i2c_runtime_suspend (dev); } -static int __maybe_unused tegra_i2c_resume (struct device *dev) +static int __maybe_unused tegra_i2c_resume_noirq (struct device *dev) {struct tegra_i2c_dev *i2c_dev =dev_get_drvdata (dev);int err;/* -* We need to ensure that clocks are enabled so that registers can be -* restored in tegra_i2c_init(). +* Runtime PM is still disabled at this point, so invoke the +* runtime_resume callback directly to bring the controller back up +* before re-initializing the hardware. The adapter is then marked +* resumed so that consumers can issue transfers from their own +* resume_noirq() handlers and onwards. */ err =tegra_i2c_runtime_resume (dev);if (err)
@@ -1904,24 +1913,22 @@ static int __maybe_unused tegra_i2c_resuif (err)return err; -/* -* In case we are runtime suspended, disable clocks again so that we -* don't unbalance the clock reference counts during the next runtime -* resume transition. -*/ -if (pm_runtime_status_suspended (dev)) { - err =tegra_i2c_runtime_suspend (dev); -if (err) -return err; - } -i2c_mark_adapter_resumed (&i2c_dev->adapter);return 0 ; } +static int __maybe_unused tegra_i2c_resume (struct device *dev) +{ +pm_runtime_put (dev); + +return 0 ; +} +static const struct dev_pm_ops tegra_i2c_pm = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume) + SET_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend_noirq, + tegra_i2c_resume_noirq)SET_RUNTIME_PM_OPS (tegra_i2c_runtime_suspend, tegra_i2c_runtime_resume,NULL ) };
Greg Kroah-Hartman
maintainerPATCH 196/342 Input: atkbd - add DMI quirk for Lenovo Yoga Air 14 (83QK)
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zeyu WANG <[email protected]>
commit ad0979fe053e9f2db82da82188256ef6eb41095a upstream.
The Lenovo Yoga Air 14 (83QK) laptop keyboard becomes unresponsive after the standard atkbd init sequence. Controlled testing on the actual hardware shows the F5 (ATKBD_CMD_RESET_DIS / deactivate) command specifically corrupts the EC state, causing zero IRQ1 interrupts after init.
Skipping only the deactivate command (while keeping F4 ENABLE) resolves the issue completely: both keystroke input and CapsLock LED toggle work correctly. The reverse test - skipping only F4 while keeping F5 - makes the problem worse (zero keystroke interrupts), confirming F5 is the sole culprit.
Add a DMI quirk entry for LENOVO/83QK using the existing atkbd_deactivate_fixup callback, consistent with the existing entries for LG Electronics and HONOR FMB-P that address the same EC F5 deactivate issue.
--- drivers/input/keyboard/atkbd.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/input/keyboard/atkbd.c +8 -0
@@ -1939,6 +1939,14 @@ static const struct dmi_system_id atkbd_ }, .callback = atkbd_deactivate_fixup, }, + { + /* Lenovo Yoga Air 14 (83QK) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83QK"), + }, + .callback = atkbd_deactivate_fixup, + }, { } };
Greg Kroah-Hartman
maintainerPATCH 197/342 Input: atkbd - skip deactivate for HONOR BCC-Ns internal keyboard
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cryolitia PukNgae <[email protected]>
commit fb402386af4cdce108ff991a796386de55439735 upstream.
After commit 9cf6e24c9fbf17e52de9fff07f12be7565ea6d61 ("Input: atkbd - do not skip atkbd_deactivate() when skipping ATKBD_CMD_GETID"), HONOR BCC-N, aka HONOR MagicBook 14 2026's internal keyboard stops working. Adding the atkbd_deactivate_fixup quirk fixes it.
DMI: HONOR BCC-N/BCC-N-PCB, BIOS 1.04 04/07/2026
--- drivers/input/keyboard/atkbd.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/input/keyboard/atkbd.c +7 -0
@@ -1947,6 +1947,13 @@ static const struct dmi_system_id atkbd_ }, .callback = atkbd_deactivate_fixup, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "BCC-N"), + }, + .callback = atkbd_deactivate_fixup, + }, { } };
Greg Kroah-Hartman
maintainerPATCH 198/342 ipc/shm: serialize orphan cleanup with shm_nattch updates
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yilin Zhu <[email protected]>
commit 2e5c6f4fd4001562781e99bbfc7f1f0127187542 upstream.
shm_destroy_orphaned() walks the shm idr under shm_ids(ns).rwsem, but that does not serialize all fields tested by shm_may_destroy(). In particular, shm_nattch is updated while holding shm_perm.lock, and attach paths can do that without holding the rwsem.
Do not decide that an orphaned segment is unused before taking the object lock. Move the shm_may_destroy() check under shm_perm.lock, matching the other destroy paths, and unlock the segment when it no longer qualifies for removal.
--- ipc/shm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
ipc/shm.c +6 -4
@@ -402,15 +402,17 @@ static int shm_try_destroy_orphaned(int* We want to destroy segments without users and with already * exit'ed originating process. * -* As shp->* are changed under rwsem, it's safe to skip shp locking. +* shm_nattch can be changed under shm_perm.lock without holding the +* rwsem, so take the object lock before checking shm_may_destroy(). */ if (!list_empty (&shp->shm_clist))return 0 ; -if (shm_may_destroy (shp)) { -shm_lock_by_ptr (shp); +shm_lock_by_ptr (shp); +if (shm_may_destroy (shp))shm_destroy (ns, shp); - } +else +shm_unlock (shp);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 199/342 misc: fastrpc: fix use-after-free of fastrpc_user in workqueue context
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anandu Krishnan E <[email protected]>
commit e85eb5feca8e254905ffa6c57a3c99c89a674a0f upstream.
There is a race between fastrpc_device_release() and the workqueue that processes DSP responses. When the user closes the file descriptor, fastrpc_device_release() frees the fastrpc_user structure. Concurrently, an in-flight DSP invocation can complete and fastrpc_rpmsg_callback() schedules context cleanup via schedule_work(&ctx->put_work). If the workqueue runs fastrpc_context_free() in parallel with or after fastrpc_device_release() has freed the user structure, it dereferences the freed fastrpc_user. Depending on the state of the context at the time of the race, any one of the following accesses can be hit:
1. fastrpc_buf_free() calls fastrpc_ipa_to_dma_addr(buf->fl->cctx, ...) to strip the SID bits from the stored IOVA before passing the physical address to dma_free_coherent().
2. fastrpc_free_map() reads map->fl->cctx->vmperms[0].vmid to reconstruct the source permission bitmask needed for the qcom_scm_assign_mem() call that returns memory from the DSP VM back to HLOS.
3. fastrpc_free_map() acquires map->fl->lock to safely remove the
map node from the fl->maps list.
The resulting use-after-free manifests as:
pc : fastrpc_buf_free+0x38/0x80 [fastrpc] lr : fastrpc_context_free+0xa8/0x1b0 [fastrpc] fastrpc_context_free+0xa8/0x1b0 [fastrpc] fastrpc_context_put_wq+0x78/0xa0 [fastrpc] process_one_work+0x180/0x450 worker_thread+0x26c/0x388
Add kref-based reference counting to fastrpc_user. Have each invoke context take a reference on the user at allocation time and release it when the context is freed. Release the initial reference in fastrpc_device_release() at file close. Move the teardown of the user structure — freeing pending contexts, maps, mmaps, and the channel context reference — into the kref release callback fastrpc_user_free(), so that it runs only when the last reference is dropped, regardless of whether that happens at device close or after the final in-flight context completes.
--- drivers/misc/fastrpc.c | 75 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 23 deletions(-)
drivers/misc/fastrpc.c +52 -23
@@ -232,6 +232,8 @@ struct fastrpc_user {spinlock_t lock;/* lock for allocations */ struct mutex mutex; +/* Reference count */ +struct kref refcount; };static void fastrpc_free_map (struct kref *ref)
@@ -352,15 +354,57 @@ static void fastrpc_channel_ctx_put(strukref_put (&cctx->refcount, fastrpc_channel_ctx_free); } +static void fastrpc_context_put (struct fastrpc_invoke_ctx *ctx); + +static void fastrpc_user_free (struct kref *ref) +{ + struct fastrpc_user *fl = container_of(ref, struct fastrpc_user, refcount); +struct fastrpc_invoke_ctx *ctx, *n; +struct fastrpc_map *map, *m; +struct fastrpc_buf *buf, *b; + +if (fl->init_mem) +fastrpc_buf_free (fl->init_mem); + +list_for_each_entry_safe (ctx, n, &fl->pending, node) { +list_del (&ctx->node); +fastrpc_context_put (ctx); + } + +list_for_each_entry_safe (map, m, &fl->maps, node) +fastrpc_map_put (map); + +list_for_each_entry_safe (buf, b, &fl->mmaps, node) { +list_del (&buf->node); +fastrpc_buf_free (buf); + } + +fastrpc_channel_ctx_put (fl->cctx); +mutex_destroy (&fl->mutex); +kfree (fl); +} + +static void fastrpc_user_get (struct fastrpc_user *fl) +{ +kref_get (&fl->refcount); +} + +static void fastrpc_user_put (struct fastrpc_user *fl) +{ +kref_put (&fl->refcount, fastrpc_user_free); +} +static void fastrpc_context_free (struct kref *ref) {struct fastrpc_invoke_ctx *ctx;struct fastrpc_channel_ctx *cctx; +struct fastrpc_user *fl;unsigned long flags;int i; ctx = container_of(ref, struct fastrpc_invoke_ctx, refcount); cctx = ctx->cctx; + fl = ctx->fl;for (i =0 ; i < ctx->nscalars; i++)fastrpc_map_put (ctx->maps[i]);
@@ -376,6 +420,8 @@ static void fastrpc_context_free(structkfree (ctx->olaps);kfree (ctx); +/* Release the reference taken in fastrpc_context_alloc() */ +fastrpc_user_put (fl);fastrpc_channel_ctx_put (cctx); }
@@ -485,6 +531,8 @@ static struct fastrpc_invoke_ctx *fastrp/* Released in fastrpc_context_put() */ fastrpc_channel_ctx_get (cctx); +/* Take a reference to user, released in fastrpc_context_free() */ +fastrpc_user_get (user); ctx->sc = sc; ctx->retval =-1 ;
@@ -515,6 +563,7 @@ err_idr:spin_lock (&user->lock);list_del (&ctx->node);spin_unlock (&user->lock); +fastrpc_user_put (user);fastrpc_channel_ctx_put (cctx);kfree (ctx->maps);kfree (ctx->olaps);
@@ -1179,9 +1228,6 @@ static int fastrpc_device_release(struct {struct fastrpc_user *fl = (struct fastrpc_user *)file->private_data;struct fastrpc_channel_ctx *cctx = fl->cctx; -struct fastrpc_invoke_ctx *ctx, *n; -struct fastrpc_map *map, *m; -struct fastrpc_buf *buf, *b;unsigned long flags;fastrpc_release_current_dsp_process (fl);
@@ -1190,28 +1236,10 @@ static int fastrpc_device_release(structlist_del (&fl->user);spin_unlock_irqrestore (&cctx->lock, flags); -if (fl->init_mem) -fastrpc_buf_free (fl->init_mem); - -list_for_each_entry_safe (ctx, n, &fl->pending, node) { -list_del (&ctx->node); -fastrpc_context_put (ctx); - } - -list_for_each_entry_safe (map, m, &fl->maps, node) -fastrpc_map_put (map); - -list_for_each_entry_safe (buf, b, &fl->mmaps, node) { -list_del (&buf->node); -fastrpc_buf_free (buf); - } -fastrpc_session_free (cctx, fl->sctx); -fastrpc_channel_ctx_put (cctx); - -mutex_destroy (&fl->mutex); -kfree (fl); file->private_data =NULL ; +/* Release the reference taken in fastrpc_device_open */ +fastrpc_user_put (fl);return 0 ; }
@@ -1251,6 +1279,7 @@ static int fastrpc_device_open(struct inspin_lock_irqsave (&cctx->lock, flags);list_add_tail (&fl->user, &cctx->users);spin_unlock_irqrestore (&cctx->lock, flags); +kref_init (&fl->refcount);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 200/342 net: bonding: fix NULL pointer dereference in bond_do_ioctl()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: ZhaoJinming <[email protected]>
commit a764b0e8317a863006e05732e1aefe821b9d8c2d upstream.
In bond_do_ioctl(), slave_dev is obtained via __dev_get_by_name() which can return NULL if the requested interface name does not exist. However, the subsequent slave_dbg() call is placed before the NULL check:
slave_dev = __dev_get_by_name(net, ifr->ifr_slave);
slave_dbg(bond_dev, slave_dev, "slave_dev=%p:\n", slave_dev); //here
if (!slave_dev)
return -ENODEV;The slave_dbg() macro expands to netdev_dbg(bond_dev, "(slave %s): " fmt, (slave_dev)->name, ...) which unconditionally dereferences slave_dev->name before the NULL check is performed. This results in a NULL pointer dereference kernel oops when a user calls bonding ioctl (e.g. SIOCBONDENSLAVE, SIOCBONDRELEASE, etc.) with a non-existent slave interface name.
This is reachable from userspace via the bonding ioctl interface with CAP_NET_ADMIN capability, making it a potential local denial-of-service vector.
Fix by moving the slave_dbg() call after the NULL check.
--- drivers/net/bonding/bond_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/bonding/bond_main.c +2 -2
@@ -4018,11 +4018,11 @@ static int bond_do_ioctl(struct net_devi slave_dev =__dev_get_by_name (net, ifr->ifr_slave); -slave_dbg (bond_dev, slave_dev,"slave_dev=%p:\n" , slave_dev); -if (!slave_dev)return -ENODEV ; +slave_dbg (bond_dev, slave_dev,"slave_dev=%p:\n" , slave_dev); +switch (cmd) {case BOND_ENSLAVE_OLD :case SIOCBONDENSLAVE :
Greg Kroah-Hartman
maintainerPATCH 201/342 net: mv643xx: fix OF node refcount
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bartosz Golaszewski <[email protected]>
commit 4aacf509e537a711fa71bca9f234e5eb6968850e upstream.
Platform devices created with platform_device_alloc() call platform_device_release() when the last reference to the device's kobject is dropped. This function calls of_node_put() unconditionally. This works fine for devices created with platform_device_register_full() but users of the split approach (platform_device_alloc() + platform_device_add()) must bump the reference of the of_node they assign manually. Add the missing call to of_node_get().
--- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/marvell/mv643xx_eth.c +1 -1
@@ -2754,7 +2754,7 @@ static int mv643xx_eth_shared_of_add_porif (!ppdev)return -ENOMEM ; ppdev->dev.coherent_dma_mask =DMA_BIT_MASK (32 ); - ppdev->dev.of_node = pnp; + ppdev->dev.of_node =of_node_get (pnp); ret =platform_device_add_resources (ppdev, &res,1 );if (ret)
Greg Kroah-Hartman
maintainerPATCH 202/342 net: rds: clear i_sends on setup unwind
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuqi Xu <[email protected]>
commit 20cf0fb715c41111469577e85e35d15f099473e0 upstream.
The RDS IB connection teardown path is written so it can run during partial startup and on repeated shutdown attempts. It uses NULL pointers to distinguish resources that are still owned from resources that have already been released.
When rds_ib_setup_qp() fails after allocating i_sends but before allocating i_recvs, the sends_out path frees i_sends without clearing the pointer. A later shutdown pass can still treat that stale pointer as a live send ring allocation.
Clear i_sends after vfree() in the error unwind path so the existing shutdown logic continues to use the correct ownership state.
--- net/rds/ib_cm.c | 1 + 1 file changed, 1 insertion(+)
net/rds/ib_cm.c +1 -0
@@ -690,6 +690,7 @@ static int rds_ib_setup_qp(struct rds_co sends_out:vfree (ic->i_sends); + ic->i_sends =NULL ; ack_dma_out: rds_dma_hdr_free(rds_ibdev->dev, ic->i_ack, ic->i_ack_dma,
Greg Kroah-Hartman
maintainerPATCH 203/342 mmc: core: Fix host controller programming for fixed driver type
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kamal Dasu <[email protected]>
commit 5a52c5701a67d5176eb1afbf1bdaf7d6dfeec597 upstream.
When using the fixed-emmc-driver-type device tree property, the MMC core correctly selects the driver strength for the card but fails to program the host controller accordingly. This causes a mismatch where the card uses the specified driver type while the host controller defaults to Type B (since ios->drv_type remains zero).
Split the driver type programming logic to handle both fixed and dynamic driver type selection paths. For fixed driver types, program the host controller with the selected drive_strength value. For dynamic selection, use the existing drv_type as before.
This ensures both the eMMC device and host controller use matching driver strengths, preventing potential signal integrity issues.
--- drivers/mmc/core/mmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/mmc/core/mmc.c +3 -1
@@ -1336,7 +1336,9 @@ static void mmc_select_driver_type(struc card->drive_strength = drive_strength; -if (drv_type) +if (fixed_drv_type >=0 && drive_strength) +mmc_set_driver_type (card->host, drive_strength); +else if (drv_type)mmc_set_driver_type (card->host, drv_type); }
Greg Kroah-Hartman
maintainerPATCH 204/342 mmc: sdhci: add signal voltage switch in sdhci_resume_host
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jisheng Zhang <[email protected]>
commit f595e8e77a51eee35e331f69321766593a845ef2 upstream.
I met one suspend/resume issue with sdr104 capable sdio wifi card (with "keep-power-in-suspend" set in DT property):
After resuming from suspend to ram, the sdio wifi card stops working. Further debug shows that although ios shows the sdio card is at sdr104 mode, the voltage is still at 3V3. This is due to missing the calling of ->start_signal_voltage_switch() in sdhci_resume_host().
Fix this issue by adding ->start_signal_voltage_switch() in sdhci_resume_host(). This also matches what we do for sdhci_runtime_resume_host().
Then the question is: why this issue hasn't reported and fixed for so long time. IMHO, several reasons: Some host controllers just kick off the runtime resume for system resume, so they benefit from the well supported runtime pm code; Some platforms just use the old sdio wifi card which doesn't need signal voltage switch at all, the default voltage is 3v3 after resuming.
--- drivers/mmc/host/sdhci.c | 1 + 1 file changed, 1 insertion(+)
drivers/mmc/host/sdhci.c +1 -0
@@ -3756,6 +3756,7 @@ int sdhci_resume_host(struct sdhci_host host->pwr =0 ; host->clock =0 ; host->reinit_uhs = true; + mmc->ops->start_signal_voltage_switch (mmc, &mmc->ios); mmc->ops->set_ios (mmc, &mmc->ios); }else {sdhci_init (host, (host->mmc->pm_flags &MMC_PM_KEEP_POWER ));
Greg Kroah-Hartman
maintainerPATCH 205/342 sctp: diag: reject stale associations in dump_one path
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhao Zhang <[email protected]>
commit 5eba3e48d78edd7551b992cb7ba687019b3a78da upstream.
The SCTP exact sock_diag lookup can hold a transport reference, block on lock_sock(sk), and then resume after sctp_association_free() has marked the association dead and freed its bind address list.
When that happens, inet_assoc_attr_size() and inet_diag_msg_sctpasoc_fill() can still dereference association state that is no longer valid for reporting. In particular, inet_diag_msg_sctpasoc_fill() may read an empty bind-address list as a real sctp_sockaddr_entry and trigger an out-of-bounds read from unrelated association memory.
Reject the association after taking the socket lock if it has been reaped or detached from the endpoint, and report the lookup as stale. This keeps the exact dump-one path from formatting torn association state.
--- net/sctp/diag.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
net/sctp/diag.c +9 -8
@@ -266,15 +266,15 @@ static int sctp_sock_dump_one(struct sctlock_sock (sk); - rep =nlmsg_new (inet_assoc_attr_size (sk, assoc),GFP_KERNEL ); -if (!rep) { -release_sock (sk); -return -ENOMEM ; +if (ep != assoc->ep || assoc->base.dead) { + err = -ESTALE ; + goto out_unlock; } -if (ep != assoc->ep) { - err = -EAGAIN ; - goto out; + rep =nlmsg_new (inet_assoc_attr_size (sk, assoc),GFP_KERNEL ); +if (!rep) { + err = -ENOMEM ; + goto out_unlock; } err = inet_sctp_diag_fill(sk, assoc, rep, req, sk_user_ns(NETLINK_CB(skb).sk),
@@ -289,8 +289,9 @@ static int sctp_sock_dump_one(struct sctreturn nlmsg_unicast (sock_net (skb->sk)->diag_nlsk, rep,NETLINK_CB (skb).portid); out: -release_sock (sk);kfree_skb (rep); +out_unlock: +release_sock (sk);return err; }
Greg Kroah-Hartman
maintainerPATCH 206/342 sctp: stream: fully roll back denied add-stream state
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wyatt Feng <[email protected]>
commit a5f8a90ac9f77c678a9781c0a464b635e0d63e49 upstream.
When ADD_OUT_STREAMS is denied, SCTP only shrinks the queued chunks and then lowers outcnt. That leaves removed stream metadata behind, so a later re-add can reuse a stale ext and hit a null-pointer dereference in the scheduler get path.
Fix the rollback by tearing down the removed stream state the same way other stream resizes do. Unschedule the current scheduler state, drop the removed stream ext state with sctp_stream_outq_migrate(), and then reschedule the remaining streams.
This keeps scheduler-private RR/FC/PRIO lists consistent while fully rolling back denied outgoing stream additions.
--- net/sctp/stream.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
net/sctp/stream.c +5 -1
@@ -1038,6 +1038,7 @@ struct sctp_chunk *sctp_process_strreset stsn, rtsn, GFP_ATOMIC); }else if (req->type ==SCTP_PARAM_RESET_ADD_OUT_STREAMS ) {struct sctp_strreset_addstrm *addstrm; +const struct sctp_sched_ops *sched;__u16 number; addstrm = (struct sctp_strreset_addstrm *)req;
@@ -1048,7 +1049,10 @@ struct sctp_chunk *sctp_process_strresetfor (i = number; i < stream->outcnt; i++)SCTP_SO (stream, i)->state =SCTP_STREAM_OPEN ; }else { -sctp_stream_shrink_out (stream, number); + sched =sctp_sched_ops_from_stream (stream); + sched->unsched_all (stream); +sctp_stream_outq_migrate (stream,NULL , number); + sched->sched_all (stream); stream->outcnt = number; }
Greg Kroah-Hartman
maintainerPATCH 207/342 thunderbolt: Reject zero-length property entries in validator
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit cff8eb65d1eafe7793e54b4d0cf6bf831644630b upstream.
tb_property_entry_valid() accepts entries with length == 0 for DIRECTORY, DATA, and TEXT types. A zero-length TEXT entry passes validation but causes an underflow in the null-termination logic:
property->value.text[property->length * 4 - 1] = '\0';
When property->length is 0 this writes to offset -1 relative to the allocation.
Reject zero-length entries early in the validator since they have no valid representation in the XDomain property protocol.
--- drivers/thunderbolt/property.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/thunderbolt/property.c +2 -0
@@ -59,6 +59,8 @@ static bool tb_property_entry_valid(conscase TB_PROPERTY_TYPE_DIRECTORY :case TB_PROPERTY_TYPE_DATA :case TB_PROPERTY_TYPE_TEXT : +if (!entry->length) +return false;if (entry->length > block_len)return false; if (check_add_overflow(entry->value, entry->length, &end) ||
Greg Kroah-Hartman
maintainerPATCH 208/342 thunderbolt: Bound root directory content to block size
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 65423079c7420e3dbf9a7aa345c243a3f5752e5d upstream.
__tb_property_parse_dir() does not check that content_offset + content_len fits within block_len for the root directory case. When rootdir->length equals or exceeds block_len - 2, the entry loop reads past the allocated property block.
Add a bounds check after computing content_offset and content_len to reject directories whose content extends past the block.
--- drivers/thunderbolt/property.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/thunderbolt/property.c +4 -0
@@ -181,6 +181,10 @@ static struct tb_property_dir *__tb_propif (is_root) { content_offset = dir_offset +2 ; content_len = dir_len; +if (content_offset + content_len > block_len) { +tb_property_free_dir (dir); +return NULL ; + } }else {if (dir_len <4 ) {tb_property_free_dir (dir);
Greg Kroah-Hartman
maintainerPATCH 209/342 thunderbolt: Clamp XDomain response data copy to allocation size
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 322e93448d908434ae5545660fcbe8f5a7a8e141 upstream.
tb_xdp_properties_request() derives the per-packet copy length from the response header without checking that it fits in the previously allocated data buffer. A malicious peer can set its length field larger than the declared data_length, causing memcpy to write past the kcalloc allocation.
Clamp the per-packet copy length so that the cumulative offset never exceeds data_len.
--- drivers/thunderbolt/xdomain.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/thunderbolt/xdomain.c +2 -0
@@ -355,6 +355,8 @@ static int tb_xdp_properties_request(str } } +if (req.offset + len > data_len) + len = data_len - req.offset;memcpy (data + req.offset, res->data, len *4 ); req.offset += len; }while (!data_len || req.offset < data_len);
Greg Kroah-Hartman
maintainerPATCH 210/342 thunderbolt: Limit XDomain response copy to actual frame size
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 4db2bd2ed4785dbadaeeab9f4e346b21ac5fb8eb upstream.
tb_xdomain_copy() copies req->response_size bytes from the received packet buffer regardless of the actual frame size. When a short response arrives, this reads past the valid frame data in the DMA pool buffer into stale contents from previous transactions.
Use the minimum of frame size and expected response size for the copy length.
--- drivers/thunderbolt/xdomain.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/thunderbolt/xdomain.c +3 -1
@@ -81,7 +81,9 @@ static bool tb_xdomain_match(const strucstatic bool tb_xdomain_copy (struct tb_cfg_request *req,const struct ctl_pkg *pkg) { -memcpy (req->response, pkg->buffer, req->response_size); +size_t len =min_t (size_t, pkg->frame.size, req->response_size); + +memcpy (req->response, pkg->buffer, len); req->result.err =0 ;return true; }
Greg Kroah-Hartman
maintainerPATCH 211/342 drm/amd/display: Clamp HDMI HDCP2 rx_id_list read to buffer size
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harry Wentland <[email protected]>
commit f0f3981c43b32cadfe373d636d9e9ca522bb3702 upstream.
[Why & How]
During HDCP 2.x repeater authentication over HDMI, the driver reads the sink's RxStatus register and extracts a 10-bit message size field (max value 1023). This value is used as the read length for the ReceiverID list without being clamped to the size of the destination buffer rx_id_list[177]. A malicious HDMI repeater could advertise a message size larger than the buffer, causing an out-of-bounds write during the I2C read.
Clamp the read length in mod_hdcp_read_rx_id_list() to the size of the rx_id_list buffer, matching the approach already used in the DP branch.
--- drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c +2 -1
@@ -533,7 +533,8 @@ enum mod_hdcp_status mod_hdcp_read_rx_id }else { status =read (hdcp,MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST , hdcp->auth.msg.hdcp2.rx_id_list, - hdcp->auth.msg.hdcp2.rx_id_list_size); +MIN (hdcp->auth.msg.hdcp2.rx_id_list_size, +sizeof (hdcp->auth.msg.hdcp2.rx_id_list))); }return status; }
Greg Kroah-Hartman
maintainerPATCH 212/342 drm/amd/display: Fix NULL deref and buffer over-read in SDP debugfs
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harry Wentland <[email protected]>
commit adf67034b1f61f7119295208085bfd43f85f56af upstream.
[Why & How]dp_sdp_message_debugfs_write() dereferences connector->base.state->crtc without checking for NULL. A connector can be connected but not bound to any CRTC (e.g. after hot-plug before the next atomic commit), causing a kernel crash when writing to the sdp_message debugfs node.
The function also ignores the user-provided size argument and always passes 36 bytes to copy_from_user(), reading past the user buffer when size < 36.
Fix both issues by:
- Returning -ENODEV when connector->base.state or state->crtc is NULL
- Clamping write_size to min(size, sizeof(data))
--- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +5 -0
@@ -895,8 +895,13 @@ static ssize_t dp_sdp_message_debugfs_wrif (size ==0 )return 0 ; +if (!connector->base.state || !connector->base.state->crtc) +return -ENODEV ; + acrtc_state =to_dm_crtc_state (connector->base.state->crtc->state); + write_size =min_t (size_t, size,sizeof (data)); + r =copy_from_user (data, buf, write_size); write_size -= r;
Greg Kroah-Hartman
maintainerPATCH 213/342 fs/fcntl: fix SOFTIRQ-unsafe lock order in fasync signaling
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mingyu Wang <[email protected]>
commit 00633c4683828acd5256fa8d5163f440d74bbe71 upstream.
A SOFTIRQ-safe to SOFTIRQ-unsafe lock order deadlock can occur in send_sigio() and send_sigurg() when a process group receives a signal.
When FASYNC is configured for a process group (PIDTYPE_PGID), both functions use read_lock(&tasklist_lock) to traverse the task list. However, they are frequently called from softirq context:
- send_sigio() via input_inject_event -> kill_fasync
- send_sigurg() via tcp_check_urg -> sk_send_sigurg (NET_RX_SOFTIRQ)
The deadlock is caused by the rwlock writer fairness mechanism:
1. CPU 0 (process context) holds read_lock(&tasklist_lock) in do_wait().
2. CPU 1 (process context) attempts write_lock(&tasklist_lock) in fork() or exit() and spins, which blocks all new readers.
3. CPU 0 is interrupted by a softirq (e.g., TCP URG packet reception).
4. The softirq calls send_sigurg() and attempts to acquire read_lock(&tasklist_lock), deadlocking because CPU 1 is waiting.
Since PID hashing and do_each_pid_task() traversals are already RCU-protected, the read_lock on tasklist_lock is no longer strictly required for safe traversal. Fix this by replacing tasklist_lock with rcu_read_lock(), aligning the process group signaling path with the single-PID path. This also mitigates a potential remote denial of service vector via TCP URG packets.
Lockdep splat:
=====================================================
WARNING: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected [...]
Chain exists of: &dev->event_lock --> &f_owner->lock --> tasklist_lock
Possible interrupt unsafe locking scenario:
CPU0 CPU1
---- ----
lock(tasklist_lock);
local_irq_disable();
lock(&dev->event_lock);
lock(&f_owner->lock);
<Interrupt>
lock(&dev->event_lock);*** DEADLOCK ***
--- fs/fcntl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
fs/fcntl.c +4 -4
@@ -800,11 +800,11 @@ void send_sigio(struct fown_struct *fownsend_sigio_to_task (p, fown, fd, band, type);rcu_read_unlock (); }else { -read_lock (&tasklist_lock); +rcu_read_lock (); do_each_pid_task(pid, type, p) {send_sigio_to_task (p, fown, fd, band, type); }while_each_pid_task (pid, type, p); -read_unlock (&tasklist_lock); +rcu_read_unlock (); } out_unlock_fown:read_unlock_irqrestore (&fown->lock, flags);
@@ -841,11 +841,11 @@ int send_sigurg(struct fown_struct *fownsend_sigurg_to_task (p, fown, type);rcu_read_unlock (); }else { -read_lock (&tasklist_lock); +rcu_read_lock (); do_each_pid_task(pid, type, p) {send_sigurg_to_task (p, fown, type); }while_each_pid_task (pid, type, p); -read_unlock (&tasklist_lock); +rcu_read_unlock (); } out_unlock_fown:read_unlock_irqrestore (&fown->lock, flags);
Greg Kroah-Hartman
maintainerPATCH 214/342 io_uring/poll: fix signed comparison in io_poll_get_ownership()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Longxuan Yu <[email protected]>
Commit 326941b22806cbf2df1fbfe902b7908b368cce42 usptream.
io_poll_get_ownership() uses a signed comparison to check whether poll_refs has reached the threshold for the slowpath:
if (unlikely(atomic_read(&req->poll_refs) >= IO_POLL_REF_BIAS))
atomic_read() returns int (signed). When IO_POLL_CANCEL_FLAG (BIT(31)) is set in poll_refs, the value becomes negative in signed arithmetic, so the >= 128 comparison always evaluates to false and the slowpath is never taken.
Fix this by casting the atomic_read() result to unsigned int before the comparison, so that the cancel flag is treated as a large positive value and correctly triggers the slowpath.
--- io_uring/io_uring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
io_uring/io_uring.c +1 -1
@@ -5378,7 +5378,7 @@ static bool io_poll_get_ownership_slowpa*/ static inline bool io_poll_get_ownership (struct io_kiocb *req) { -if (unlikely (atomic_read (&req->poll_refs) >=IO_POLL_REF_BIAS )) +if (unlikely ((unsigned int )atomic_read (&req->poll_refs) >=IO_POLL_REF_BIAS ))return io_poll_get_ownership_slowpath (req);return !(atomic_fetch_inc (&req->poll_refs) &IO_POLL_REF_MASK ); }
Greg Kroah-Hartman
maintainerPATCH 215/342 ipvs: skip ipv6 extension headers for csum checks
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
commit 05cfe9863ef049d98141dc2969eefde72fb07625 upstream.
Protocol checksum validation fails for IPv6 if there are extension headers before the protocol header. iph->len already contains its offset, so use it to fix the problem.
--- net/netfilter/ipvs/ip_vs_proto_sctp.c | 18 ++++++------------ net/netfilter/ipvs/ip_vs_proto_tcp.c | 21 +++++++-------------- net/netfilter/ipvs/ip_vs_proto_udp.c | 20 +++++++------------- 3 files changed, 20 insertions(+), 39 deletions(-)
net/netfilter/ipvs/ip_vs_proto_sctp.c +6 -12
@@ -10,7 +10,8 @@#include <net/ip_vs.h> static int -sctp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp); +sctp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp, +unsigned int sctphoff); static int sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -108,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, sint ret;/* Some checks before mangling */ -if (!sctp_csum_check (cp->af, skb, pp)) +if (!sctp_csum_check (cp->af, skb, pp, sctphoff))return 0 ;/* Call application helper if needed */
@@ -156,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, sint ret;/* Some checks before mangling */ -if (!sctp_csum_check (cp->af, skb, pp)) +if (!sctp_csum_check (cp->af, skb, pp, sctphoff))return 0 ;/* Call application helper if needed */
@@ -185,19 +186,12 @@ sctp_dnat_handler(struct sk_buff *skb, s }static int -sctp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp) +sctp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp, +unsigned int sctphoff) { -unsigned int sctphoff;struct sctphdr *sh;__le32 cmp, val; -#ifdef CONFIG_IP_VS_IPV6 -if (af ==AF_INET6 ) - sctphoff =sizeof (struct ipv6hdr ); - else -#endif - sctphoff =ip_hdrlen (skb); - sh = (struct sctphdr *)(skb->data + sctphoff); cmp = sh->checksum; val =sctp_compute_cksum (skb, sctphoff);
net/netfilter/ipvs/ip_vs_proto_tcp.c +7 -14
@@ -29,7 +29,8 @@#include <net/ip_vs.h> static int -tcp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp); +tcp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp, +unsigned int tcphoff); static int tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -166,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, stint ret;/* Some checks before mangling */ -if (!tcp_csum_check (cp->af, skb, pp)) +if (!tcp_csum_check (cp->af, skb, pp, tcphoff))return 0 ;/* Call application helper if needed */
@@ -244,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, stint ret;/* Some checks before mangling */ -if (!tcp_csum_check (cp->af, skb, pp)) +if (!tcp_csum_check (cp->af, skb, pp, tcphoff))return 0 ;/*
@@ -301,17 +302,9 @@ tcp_dnat_handler(struct sk_buff *skb, ststatic int -tcp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp) +tcp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp, +unsigned int tcphoff) { -unsigned int tcphoff; - -#ifdef CONFIG_IP_VS_IPV6 -if (af ==AF_INET6 ) - tcphoff =sizeof (struct ipv6hdr ); - else -#endif - tcphoff =ip_hdrlen (skb); -switch (skb->ip_summed) {case CHECKSUM_NONE : skb->csum =skb_checksum (skb, tcphoff, skb->len - tcphoff,0 );
@@ -322,7 +315,7 @@ tcp_csum_check(int af, struct sk_buff *sif (csum_ipv6_magic (&ipv6_hdr (skb)->saddr, &ipv6_hdr (skb)->daddr, skb->len - tcphoff, -ipv6_hdr (skb)->nexthdr, +IPPROTO_TCP , skb->csum)) {IP_VS_DBG_RL_PKT (0 , af, pp, skb,0 ,"Failed checksum for" );
net/netfilter/ipvs/ip_vs_proto_udp.c +7 -13
@@ -25,7 +25,8 @@#include <net/ip6_checksum.h> static int -udp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp); +udp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp, +unsigned int udphoff); static int udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -155,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, stint ret;/* Some checks before mangling */ -if (!udp_csum_check (cp->af, skb, pp)) +if (!udp_csum_check (cp->af, skb, pp, udphoff))return 0 ;/*
@@ -238,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, stint ret;/* Some checks before mangling */ -if (!udp_csum_check (cp->af, skb, pp)) +if (!udp_csum_check (cp->af, skb, pp, udphoff))return 0 ;/*
@@ -297,17 +298,10 @@ udp_dnat_handler(struct sk_buff *skb, ststatic int -udp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp) +udp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp, +unsigned int udphoff) {struct udphdr _udph, *uh; -unsigned int udphoff; - -#ifdef CONFIG_IP_VS_IPV6 -if (af ==AF_INET6 ) - udphoff =sizeof (struct ipv6hdr ); - else -#endif - udphoff =ip_hdrlen (skb); uh =skb_header_pointer (skb, udphoff,sizeof (_udph), &_udph);if (uh ==NULL )
@@ -325,7 +319,7 @@ udp_csum_check(int af, struct sk_buff *sif (csum_ipv6_magic (&ipv6_hdr (skb)->saddr, &ipv6_hdr (skb)->daddr, skb->len - udphoff, -ipv6_hdr (skb)->nexthdr, +IPPROTO_UDP , skb->csum)) {IP_VS_DBG_RL_PKT (0 , af, pp, skb,0 ,"Failed checksum for" );
Greg Kroah-Hartman
maintainerPATCH 216/342 batman-adv: stop tp_meter sessions during mesh teardown
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiexun Wang <[email protected]>
commit 3d3cf6a7314aca4df0a6dde28ce784a2a30d0166 upstream.
TP meter sessions remain linked on bat_priv->tp_list after the netlink request has already finished. When the mesh interface is removed, batadv_mesh_free() currently tears down the mesh without first draining these sessions.
A running sender thread or a late incoming tp_meter packet can then keep processing against a mesh instance which is already shutting down. Synchronize tp_meter with the mesh lifetime by stopping all active sessions from batadv_mesh_free() and waiting for sender threads to exit before teardown continues.
--- net/batman-adv/main.c | 1 net/batman-adv/tp_meter.c | 94 +++++++++++++++++++++++++++++++++++++--------- net/batman-adv/tp_meter.h | 1 net/batman-adv/types.h | 4 + 4 files changed, 82 insertions(+), 18 deletions(-)
net/batman-adv/main.c +1 -0
@@ -269,6 +269,7 @@ void batadv_mesh_free(struct net_deviceatomic_set (&bat_priv->mesh_state,BATADV_MESH_DEACTIVATING );batadv_purge_outstanding_packets (bat_priv,NULL ); +batadv_tp_stop_all (bat_priv);batadv_gw_node_free (bat_priv);
net/batman-adv/tp_meter.c +76 -18
145 lines not on this page · raw message
net/batman-adv/tp_meter.h +1 -0
@@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv u32 test_length, u32 *cookie);void batadv_tp_stop (struct batadv_priv *bat_priv,const u8 *dst,u8 return_value); +void batadv_tp_stop_all (struct batadv_priv *bat_priv);void batadv_tp_meter_recv (struct batadv_priv *bat_priv,struct sk_buff *skb);#endif /* _NET_BATMAN_ADV_TP_METER_H_ */
net/batman-adv/types.h +4 -0
@@ -14,6 +14,7 @@#include <linux/average.h> #include <linux/bitops.h> #include <linux/compiler.h> +#include <linux/completion.h> #include <linux/if.h> #include <linux/if_ether.h> #include <linux/kref.h>
@@ -1422,6 +1423,9 @@ struct batadv_tp_vars {/** @finish_work: work item for the finishing procedure */ struct delayed_work finish_work; +/** @finished: completion signaled when a sender thread exits */ +struct completion finished; +/** @test_length: test length in milliseconds */ u32 test_length;
Greg Kroah-Hartman
maintainerPATCH 217/342 batman-adv: tp_meter: fix tp_num leak on kmalloc failure
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit ce425dd05d0fe7594930a0fb103634f35ac47bb6 upstream.
When batadv_tp_start() or batadv_tp_init_recv() fail to allocate a new tp_vars object, the previously incremented bat_priv->tp_num counter is never decremented. This causes tp_num to drift upward on each allocation failure. Since only BATADV_TP_MAX_NUM sessions can be started and the count is never reduced for these failed allocations, it causes to an exhaustion of throughput meter sessions. In worst case, no new throughput meter session can be started until the mesh interface is removed.
The error handling must decrement tp_num releasing the lock and aborting the creation of an throughput meter session
--- net/batman-adv/tp_meter.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/batman-adv/tp_meter.c +4 -1
@@ -1036,6 +1036,7 @@ void batadv_tp_start(struct batadv_priv tp_vars =kmalloc (sizeof (*tp_vars),GFP_ATOMIC );if (!tp_vars) { +atomic_dec (&bat_priv->tp_num);spin_unlock_bh (&bat_priv->tp_list_lock); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: %s cannot allocate list elements\n",
@@ -1410,8 +1411,10 @@ batadv_tp_init_recv(struct batadv_priv * } tp_vars =kmalloc (sizeof (*tp_vars),GFP_ATOMIC ); -if (!tp_vars) +if (!tp_vars) { +atomic_dec (&bat_priv->tp_num); goto out_unlock; + }ether_addr_copy (tp_vars->other_end, icmp->orig); tp_vars->role =BATADV_TP_RECEIVER ;
Greg Kroah-Hartman
maintainerPATCH 218/342 f2fs: fix UAF caused by decrementing sbi->nr_pages[] in f2fs_write_end_io()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yongpeng Yang <[email protected]>
[ Upstream commit 2d9c4a4ed4eef1f82c5b16b037aee8bad819fd53 ]
The xfstests case "generic/107" and syzbot have both reported a NULL pointer dereference.
The concurrent scenario that triggers the panic is as follows:
F2FS_WB_CP_DATA write callback umount
- f2fs_write_checkpoint
- f2fs_wait_on_all_pages(sbi, F2FS_WB_CP_DATA)
- blk_mq_end_request
- bio_endio
- f2fs_write_end_io
: dec_page_count(sbi, F2FS_WB_CP_DATA)
: wake_up(&sbi->cp_wait)
- kill_f2fs_super
- kill_block_super
- f2fs_put_super
: iput(sbi->node_inode)
: sbi->node_inode = NULL
: f2fs_in_warm_node_list
- is_node_folio // sbi->node_inode is NULL and panic
The root cause is that f2fs_put_super() calls iput(sbi->node_inode) and sets sbi->node_inode to NULL after sbi->nr_pages[F2FS_WB_CP_DATA] is decremented to zero. As a result, f2fs_in_warm_node_list() may dereference a NULL node_inode when checking whether a folio belongs to the node inode, leading to a panic.
This patch fixes the issue by calling f2fs_in_warm_node_list() before decrementing sbi->nr_pages[F2FS_WB_CP_DATA], thus preventing the use-after-free condition.
--- fs/f2fs/data.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/f2fs/data.c +2 -2
@@ -377,6 +377,8 @@ static void f2fs_write_end_io(struct biof2fs_bug_on (sbi, page->mapping ==NODE_MAPPING (sbi) && page->index !=nid_of_node (page)); +if (f2fs_in_warm_node_list (sbi, page)) +f2fs_del_fsync_node_entry (sbi, page);dec_page_count (sbi, type);
@@ -388,8 +390,6 @@ static void f2fs_write_end_io(struct bio wq_has_sleeper(&sbi->cp_wait))wake_up (&sbi->cp_wait); -if (f2fs_in_warm_node_list (sbi, page)) -f2fs_del_fsync_node_entry (sbi, page);clear_cold_data (page);end_page_writeback (page); }
Greg Kroah-Hartman
maintainerPATCH 219/342 smb: client: require a full NFS mode SID before reading mode bits
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit 2757ad3e4b6f9e0fed4c7739594e702abc5cab21 ]
parse_dacl() treats an ACE SID matching sid_unix_NFS_mode as an NFS mode SID and reads sid.sub_auth[2] to recover the mode bits.
That assumes the ACE carries three subauthorities, but compare_sids() only compares min(a, b) subauthorities. A malicious server can return an ACE with num_subauth = 2 and sub_auth[] = {88, 3}, which still matches sid_unix_NFS_mode and then drives the sub_auth[2] read four bytes past the end of the ACE.
Require num_subauth >= 3 before treating the ACE as an NFS mode SID. This keeps the fix local to the special-SID mode path without changing compare_sids() semantics for the rest of cifsacl.
--- fs/cifs/cifsacl.c | 1 + 1 file changed, 1 insertion(+)
fs/cifs/cifsacl.c +1 -0
@@ -757,6 +757,7 @@ static void parse_dacl(struct cifs_acl *dump_ace (ppace[i], end_of_acl);#endif if (mode_from_special_sid && + ppace[i]->sid.num_subauth >=3 && (compare_sids (&(ppace[i]->sid), &sid_unix_NFS_mode) ==0 )) {/*
Greg Kroah-Hartman
maintainerPATCH 220/342 smb: client: fix OOB read in smb2_ioctl_query_info QUERY_INFO path
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit a58c5af19ff0d6f44f6e9fe31e33a2c92223f77e ]
smb2_ioctl_query_info() has two response-copy branches: PASSTHRU_FSCTL and the default QUERY_INFO path. The QUERY_INFO branch clamps qi.input_buffer_length to the server-reported OutputBufferLength and then copies qi.input_buffer_length bytes from qi_rsp->Buffer to userspace, but it never verifies that the flexible-array payload actually fits within rsp_iov[1].iov_len.
A malicious server can return OutputBufferLength larger than the actual QUERY_INFO response, causing copy_to_user() to walk past the response buffer and expose adjacent kernel heap to userspace.
Guard the QUERY_INFO copy with a bounds check on the actual Buffer payload. Use struct_size(qi_rsp, Buffer, qi.input_buffer_length) rather than an open-coded addition so the guard cannot overflow on 32-bit builds.
--- fs/cifs/smb2ops.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/cifs/smb2ops.c +6 -0
@@ -1720,6 +1720,12 @@ smb2_ioctl_query_info(const unsigned int qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1 ].iov_base;if (le32_to_cpu (qi_rsp->OutputBufferLength) < qi.input_buffer_length) qi.input_buffer_length =le32_to_cpu (qi_rsp->OutputBufferLength); +if (qi.input_buffer_length >0 && +struct_size (qi_rsp, Buffer, qi.input_buffer_length) > + rsp_iov[1 ].iov_len) { + rc = -EFAULT ; + goto out; + }if (copy_to_user (&pqi->input_buffer_length, &qi.input_buffer_length,sizeof (qi.input_buffer_length))) {
Greg Kroah-Hartman
maintainerPATCH 221/342 net/packet: fix TOCTOU race on mmapd vnet_hdr in tpacket_snd()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bingquan Chen <[email protected]>
[ 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.
--- net/packet/af_packet.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
net/packet/af_packet.c +15 -10
@@ -2726,7 +2726,8 @@ static int tpacket_snd(struct packet_soc {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 ;
@@ -2826,16 +2827,20 @@ static int tpacket_snd(struct packet_soc hlen =LL_RESERVED_SPACE (dev); tlen = dev->needed_tailroom;if (po->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,
@@ -2872,12 +2877,12 @@ tpacket_error: } } -if (po->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;
Greg Kroah-Hartman
maintainerPATCH 222/342 drm/nouveau: fix u32 overflow in pushbuf reloc bounds check
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Greg Kroah-Hartman <[email protected]>
[ Upstream commit 2fc87d37be1b730a149b035f9375fdb8cc5333a5 ]
nouveau_gem_pushbuf_reloc_apply() validates each relocation with
if (r->reloc_bo_offset + 4 > nvbo->bo.base.size)
but reloc_bo_offset is __u32 (uapi/drm/nouveau_drm.h) and the integer literal 4 promotes to unsigned int, so the addition is performed in 32 bits and wraps before the comparison against the size_t bo size.
Cast to u64 so the addition happens in 64-bit arithmetic.
--- drivers/gpu/drm/nouveau/nouveau_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nouveau_gem.c +1 -1
@@ -618,7 +618,7 @@ nouveau_gem_pushbuf_reloc_apply(struct n } nvbo = (void *)(unsigned long )bo[r->reloc_bo_index].user_priv; -if (unlikely (r->reloc_bo_offset +4 > +if (unlikely ((u64 )r->reloc_bo_offset +4 > nvbo->bo.mem.num_pages <<PAGE_SHIFT )) {NV_PRINTK (err, cli,"reloc outside of bo\n" ); ret = -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 223/342 arm64/mm: Enable batched TLB flush in unmap_hotplug_range()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anshuman Khandual <[email protected]>
[ Upstream commit 48478b9f791376b4b89018d7afdfd06865498f65 ]
During a memory hot remove operation, both linear and vmemmap mappings for the memory range being removed, get unmapped via unmap_hotplug_range() but mapped pages get freed only for vmemmap mapping. This is just a sequential operation where each table entry gets cleared, followed by a leaf specific TLB flush, and then followed by memory free operation when applicable.
This approach was simple and uniform both for vmemmap and linear mappings. But linear mapping might contain CONT marked block memory where it becomes necessary to first clear out all entire in the range before a TLB flush. This is as per the architecture requirement. Hence batch all TLB flushes during the table tear down walk and finally do it in unmap_hotplug_range().
Prior to this fix, it was hypothetically possible for a speculative access to a higher address in the contiguous block to fill the TLB with shattered entries for the entire contiguous range after a lower address had already been cleared and invalidated. Due to the table entries being shattered, the subsequent TLB invalidation for the higher address would not then clear the TLB entries for the lower address, meaning stale TLB entries could persist.
Besides it also helps in improving the performance via TLBI range operation along with reduced synchronization instructions. The time spent executing unmap_hotplug_range() improved 97% measured over a 2GB memory hot removal in KVM guest.
This scheme is not applicable during vmemmap mapping tear down where memory needs to be freed and hence a TLB flush is required after clearing out page table entry.
--- arch/arm64/mm/mmu.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-)
arch/arm64/mm/mmu.c +20 -16
@@ -862,10 +862,14 @@ static void unmap_hotplug_pte_range(pmd_WARN_ON (!pte_present (pte));pte_clear (&init_mm, addr, ptep); -flush_tlb_kernel_range (addr, addr +PAGE_SIZE ); -if (free_mapped) +if (free_mapped) { +/* CONT blocks are not supported in the vmemmap */ +WARN_ON (pte_cont (pte)); +flush_tlb_kernel_range (addr, addr +PAGE_SIZE );free_hotplug_page_range (pte_page (pte),PAGE_SIZE , altmap); + } +/* unmap_hotplug_range() flushes TLB for !free_mapped */ }while (addr +=PAGE_SIZE , addr < end); }
@@ -886,15 +890,14 @@ static void unmap_hotplug_pmd_range(pud_WARN_ON (!pmd_present (pmd));if (pmd_sect (pmd)) {pmd_clear (pmdp); - -/* -* One TLBI should be sufficient here as the PMD_SIZE -* range is mapped with a single block entry. -*/ -flush_tlb_kernel_range (addr, addr +PAGE_SIZE ); -if (free_mapped) +if (free_mapped) { +/* CONT blocks are not supported in the vmemmap */ +WARN_ON (pmd_val (pmd) &PMD_SECT_CONT ); +flush_tlb_kernel_range (addr, addr +PMD_SIZE );free_hotplug_page_range (pmd_page (pmd),PMD_SIZE , altmap); + } +/* unmap_hotplug_range() flushes TLB for !free_mapped */ continue ; }WARN_ON (!pmd_table (pmd));
@@ -919,15 +922,12 @@ static void unmap_hotplug_pud_range(p4d_WARN_ON (!pud_present (pud));if (pud_sect (pud)) {pud_clear (pudp); - -/* -* One TLBI should be sufficient here as the PUD_SIZE -* range is mapped with a single block entry. -*/ -flush_tlb_kernel_range (addr, addr +PAGE_SIZE ); -if (free_mapped) +if (free_mapped) { +flush_tlb_kernel_range (addr, addr +PUD_SIZE );free_hotplug_page_range (pud_page (pud),PUD_SIZE , altmap); + } +/* unmap_hotplug_range() flushes TLB for !free_mapped */ continue ; }WARN_ON (!pud_table (pud));
@@ -957,6 +957,7 @@ static void unmap_hotplug_p4d_range(pgd_static void unmap_hotplug_range (unsigned long addr,unsigned long end,bool free_mapped,struct vmem_altmap *altmap) { +unsigned long start = addr;unsigned long next;pgd_t *pgdp, pgd;
@@ -978,6 +979,9 @@ static void unmap_hotplug_range(unsignedWARN_ON (!pgd_present (pgd));unmap_hotplug_p4d_range (pgdp, addr, next, free_mapped, altmap); }while (addr = next, addr < end); + +if (!free_mapped) +flush_tlb_kernel_range (start, end); } static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr,
Greg Kroah-Hartman
maintainerPATCH 224/342 thermal: core: Fix thermal zone governor cleanup issues
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Rafael J. Wysocki" <[email protected]>
[ Upstream commit 41ff66baf81c6541f4f985dd7eac4494d03d9440 ]
If thermal_zone_device_register_with_trips() fails after adding a thermal governor to the thermal zone being registered, the governor is not removed from it as appropriate which may lead to a memory leak.
In turn, thermal_zone_device_unregister() calls thermal_set_governor() without acquiring the thermal zone lock beforehand which may race with a governor update via sysfs and may lead to a use-after-free in that case.
Address these issues by adding two thermal_set_governor() calls, one to thermal_release() to remove the governor from the given thermal zone, and one to the thermal zone registration error path to cover failures preceding the thermal zone device registration.
--- drivers/thermal/thermal_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/thermal/thermal_core.c +4 -3
@@ -986,6 +986,7 @@ static void thermal_release(struct devic sizeof("thermal_zone") - 1)) { tz =to_thermal_zone (dev);thermal_zone_destroy_device_groups (tz); +thermal_set_governor (tz,NULL );kfree (tz); } else if (!strncmp(dev_name(dev), "cooling_device", sizeof("cooling_device") - 1)) {
@@ -1447,8 +1448,10 @@ thermal_zone_device_register(const char/* sys I/F */ /* Add nodes that are always present via .groups */ result =thermal_zone_create_device_groups (tz, mask); -if (result) +if (result) { +thermal_set_governor (tz,NULL ); goto remove_id; + }/* A new thermal zone needs to be updated anyway. */ atomic_set (&tz->need_update,1 );
@@ -1571,8 +1574,6 @@ void thermal_zone_device_unregister(strucancel_delayed_work_sync (&tz->poll_queue); -thermal_set_governor (tz,NULL ); -thermal_remove_hwmon_sysfs (tz);ida_simple_remove (&thermal_tz_ida, tz->id);ida_destroy (&tz->ida);
Greg Kroah-Hartman
maintainerPATCH 225/342 wifi: mwifiex: fix use-after-free in mwifiex_adapter_cleanup()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Hodges <[email protected]>
[ Upstream commit ae5e95d4157481693be2317e3ffcd84e36010cbb ]
The mwifiex_adapter_cleanup() function uses timer_delete() (non-synchronous) for the wakeup_timer before the adapter structure is freed. This is incorrect because timer_delete() does not wait for any running timer callback to complete.
If the wakeup_timer callback (wakeup_timer_fn) is executing when mwifiex_adapter_cleanup() is called, the callback will continue to access adapter fields (adapter->hw_status, adapter->if_ops.card_reset, etc.) which may be freed by mwifiex_free_adapter() called later in the mwifiex_remove_card() path.
Use timer_delete_sync() instead to ensure any running timer callback has completed before returning.
--- drivers/net/wireless/marvell/mwifiex/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/marvell/mwifiex/init.c +1 -1
@@ -399,7 +399,7 @@ static void mwifiex_invalidate_lists(strstatic void mwifiex_adapter_cleanup (struct mwifiex_adapter *adapter) { -del_timer (&adapter->wakeup_timer); +del_timer_sync (&adapter->wakeup_timer);del_timer_sync (&adapter->devdump_timer);mwifiex_cancel_all_pending_cmd (adapter);wake_up_interruptible (&adapter->cmd_wait_q.wait);
Greg Kroah-Hartman
maintainerPATCH 226/342 ALSA: aoa: Use guard() for mutex locks
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit 1cb6ecbb372002ef9e531c5377e5f60122411e40 ]
Replace the manual mutex lock/unlock pairs with guard() for code simplification.
Only code refactoring, and no behavior change.
Stable-dep-of: 5ed060d54915 ("ALSA: aoa: i2sbus: clear stale prepared state")
--- sound/aoa/codecs/onyx.c | 104 +++++++++++------------------------- sound/aoa/codecs/tas.c | 113 +++++++++++++--------------------------- sound/aoa/core/gpio-feature.c | 20 ++----- sound/aoa/core/gpio-pmf.c | 26 +++------ sound/aoa/soundbus/i2sbus/pcm.c | 76 ++++++++------------------ 5 files changed, 112 insertions(+), 227 deletions(-)
sound/aoa/codecs/onyx.c +32 -72
@@ -121,10 +121,9 @@ static int onyx_snd_vol_get(struct snd_kstruct onyx *onyx =snd_kcontrol_chip (kcontrol);s8 l, r; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx,ONYX_REG_DAC_ATTEN_LEFT , &l);onyx_read_register (onyx,ONYX_REG_DAC_ATTEN_RIGHT , &r); -mutex_unlock (&onyx->mutex); ucontrol->value.integer.value[0 ] = l +VOLUME_RANGE_SHIFT ; ucontrol->value.integer.value[1 ] = r +VOLUME_RANGE_SHIFT ;
@@ -145,15 +144,13 @@ static int onyx_snd_vol_put(struct snd_k ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT)return -EINVAL ; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx,ONYX_REG_DAC_ATTEN_LEFT , &l);onyx_read_register (onyx,ONYX_REG_DAC_ATTEN_RIGHT , &r);if (l +VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0 ] && - r +VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1 ]) { -mutex_unlock (&onyx->mutex); + r +VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1 ])return 0 ; - }onyx_write_register (onyx,ONYX_REG_DAC_ATTEN_LEFT , ucontrol->value.integer.value[0]
@@ -161,7 +158,6 @@ static int onyx_snd_vol_put(struct snd_konyx_write_register (onyx,ONYX_REG_DAC_ATTEN_RIGHT , ucontrol->value.integer.value[1 ] -VOLUME_RANGE_SHIFT ); -mutex_unlock (&onyx->mutex);return 1 ; }
@@ -197,9 +193,8 @@ static int onyx_snd_inputgain_get(structstruct onyx *onyx =snd_kcontrol_chip (kcontrol);u8 ig; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx,ONYX_REG_ADC_CONTROL , &ig); -mutex_unlock (&onyx->mutex); ucontrol->value.integer.value[0 ] = (ig &ONYX_ADC_PGA_GAIN_MASK ) +INPUTGAIN_RANGE_SHIFT ;
@@ -216,14 +211,13 @@ static int onyx_snd_inputgain_put(structif (ucontrol->value.integer.value[0 ] <3 +INPUTGAIN_RANGE_SHIFT || ucontrol->value.integer.value[0 ] >28 +INPUTGAIN_RANGE_SHIFT )return -EINVAL ; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx,ONYX_REG_ADC_CONTROL , &v); n = v; n &= ~ONYX_ADC_PGA_GAIN_MASK ; n |= (ucontrol->value.integer.value[0 ] -INPUTGAIN_RANGE_SHIFT ) &ONYX_ADC_PGA_GAIN_MASK ;onyx_write_register (onyx,ONYX_REG_ADC_CONTROL , n); -mutex_unlock (&onyx->mutex);return n != v; }
@@ -251,9 +245,8 @@ static int onyx_snd_capture_source_get(sstruct onyx *onyx =snd_kcontrol_chip (kcontrol);s8 v; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx,ONYX_REG_ADC_CONTROL , &v); -mutex_unlock (&onyx->mutex); ucontrol->value.enumerated.item[0 ] = !!(v&ONYX_ADC_INPUT_MIC );
@@ -264,13 +257,12 @@ static void onyx_set_capture_source(stru {s8 v; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx,ONYX_REG_ADC_CONTROL , &v); v &= ~ONYX_ADC_INPUT_MIC ;if (mic) v |=ONYX_ADC_INPUT_MIC ;onyx_write_register (onyx,ONYX_REG_ADC_CONTROL , v); -mutex_unlock (&onyx->mutex); } static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
@@ -311,9 +303,8 @@ static int onyx_snd_mute_get(struct snd_struct onyx *onyx =snd_kcontrol_chip (kcontrol);u8 c; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx,ONYX_REG_DAC_CONTROL , &c); -mutex_unlock (&onyx->mutex); ucontrol->value.integer.value[0 ] = !(c &ONYX_MUTE_LEFT ); ucontrol->value.integer.value[1 ] = !(c &ONYX_MUTE_RIGHT );
@@ -328,9 +319,9 @@ static int onyx_snd_mute_put(struct snd_u8 v =0 , c =0 ;int err = -EBUSY ; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);if (onyx->analog_locked) - goto out_unlock; +return -EBUSY ;onyx_read_register (onyx,ONYX_REG_DAC_CONTROL , &v); c = v;
@@ -341,9 +332,6 @@ static int onyx_snd_mute_put(struct snd_ c |=ONYX_MUTE_RIGHT ; err =onyx_write_register (onyx,ONYX_REG_DAC_CONTROL , c); - out_unlock: -mutex_unlock (&onyx->mutex); -return !err ? (v != c) : err; }
@@ -372,9 +360,8 @@ static int onyx_snd_single_bit_get(strucu8 address = (pv >>8 ) &0xff ;u8 mask = pv &0xff ; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx, address, &c); -mutex_unlock (&onyx->mutex); ucontrol->value.integer.value[0 ] = !!(c & mask) ^ polarity;
@@ -393,11 +380,10 @@ static int onyx_snd_single_bit_put(strucu8 address = (pv >>8 ) &0xff ;u8 mask = pv &0xff ; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);if (spdiflock && onyx->spdif_locked) {/* even if alsamixer doesn't care.. */ - err = -EBUSY ; - goto out_unlock; +return -EBUSY ; }onyx_read_register (onyx, address, &v); c = v;
@@ -406,9 +392,6 @@ static int onyx_snd_single_bit_put(struc c |= mask; err =onyx_write_register (onyx, address, c); - out_unlock: -mutex_unlock (&onyx->mutex); -return !err ? (v != c) : err; }
@@ -489,7 +472,7 @@ static int onyx_spdif_get(struct snd_kcostruct onyx *onyx =snd_kcontrol_chip (kcontrol);u8 v; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx,ONYX_REG_DIG_INFO1 , &v); ucontrol->value.iec958.status[0 ] = v &0x3e ;
@@ -501,7 +484,6 @@ static int onyx_spdif_get(struct snd_kcoonyx_read_register (onyx,ONYX_REG_DIG_INFO4 , &v); ucontrol->value.iec958.status[4 ] = v &0x0f ; -mutex_unlock (&onyx->mutex);return 0 ; }
@@ -512,7 +494,7 @@ static int onyx_spdif_put(struct snd_kcostruct onyx *onyx =snd_kcontrol_chip (kcontrol);u8 v; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx,ONYX_REG_DIG_INFO1 , &v); v = (v & ~0x3e ) | (ucontrol->value.iec958.status[0 ] &0x3e );onyx_write_register (onyx,ONYX_REG_DIG_INFO1 , v);
@@ -527,7 +509,6 @@ static int onyx_spdif_put(struct snd_kcoonyx_read_register (onyx,ONYX_REG_DIG_INFO4 , &v); v = (v & ~0x0f ) | (ucontrol->value.iec958.status[4 ] &0x0f );onyx_write_register (onyx,ONYX_REG_DIG_INFO4 , v); -mutex_unlock (&onyx->mutex);return 1 ; }
@@ -672,14 +653,13 @@ static int onyx_usable(struct codec_infostruct onyx *onyx = cii->codec_data;int spdif_enabled, analog_enabled; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);onyx_read_register (onyx,ONYX_REG_DIG_INFO4 , &v); spdif_enabled = !!(v &ONYX_SPDIF_ENABLE );onyx_read_register (onyx,ONYX_REG_DAC_CONTROL , &v); analog_enabled = (v & (ONYX_MUTE_RIGHT |ONYX_MUTE_LEFT )) != (ONYX_MUTE_RIGHT |ONYX_MUTE_LEFT ); -mutex_unlock (&onyx->mutex);switch (ti->tag) {case 0 :return 1 ;
@@ -695,9 +675,8 @@ static int onyx_prepare(struct codec_inf {u8 v;struct onyx *onyx = cii->codec_data; -int err = -EBUSY ; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE if (substream->runtime->format ==SNDRV_PCM_FMTBIT_COMPRESSED_16BE ) {
@@ -706,10 +685,9 @@ static int onyx_prepare(struct codec_infif (onyx_write_register (onyx,ONYX_REG_DAC_CONTROL , v |ONYX_MUTE_RIGHT |ONYX_MUTE_LEFT )) - goto out_unlock; +return -EBUSY ; onyx->analog_locked =1 ; - err =0 ; - goto out_unlock; + return 0; }#endif switch (substream->runtime->rate) {
@@ -719,8 +697,7 @@ static int onyx_prepare(struct codec_inf/* these rates are ok for all outputs */ /* FIXME: program spdif channel control bits here so that * userspace doesn't have to if it only plays pcm! */ - err =0 ; - goto out_unlock; +return 0 ;default :/* got some rate that the digital output can't do, * so disable and lock it */
@@ -728,16 +705,12 @@ static int onyx_prepare(struct codec_infif (onyx_write_register (onyx,ONYX_REG_DIG_INFO4 , v & ~ONYX_SPDIF_ENABLE )) - goto out_unlock; +return -EBUSY ; onyx->spdif_locked =1 ; - err =0 ; - goto out_unlock; +return 0 ; } - out_unlock: -mutex_unlock (&onyx->mutex); - -return err; +return -EBUSY ; } static int onyx_open(struct codec_info_item *cii,
@@ -745,9 +718,8 @@ static int onyx_open(struct codec_info_i {struct onyx *onyx = cii->codec_data; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex); onyx->open_count++; -mutex_unlock (&onyx->mutex);return 0 ; }
@@ -757,11 +729,10 @@ static int onyx_close(struct codec_info_ {struct onyx *onyx = cii->codec_data; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex); onyx->open_count--;if (!onyx->open_count) onyx->spdif_locked = onyx->analog_locked =0 ; -mutex_unlock (&onyx->mutex);return 0 ; }
@@ -771,7 +742,7 @@ static int onyx_switch_clock(struct code {struct onyx *onyx = cii->codec_data; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);/* this *MUST* be more elaborate later... */ switch (what) {case CLOCK_SWITCH_PREPARE_SLAVE :
@@ -783,7 +754,6 @@ static int onyx_switch_clock(struct codedefault :/* silence warning */ break ; } -mutex_unlock (&onyx->mutex);return 0 ; }
@@ -794,27 +764,21 @@ static int onyx_suspend(struct codec_inf {struct onyx *onyx = cii->codec_data;u8 v; -int err = -ENXIO ; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);if (onyx_read_register (onyx,ONYX_REG_CONTROL , &v)) - goto out_unlock; +return -ENXIO ;onyx_write_register (onyx,ONYX_REG_CONTROL , v |ONYX_ADPSV |ONYX_DAPSV );/* Apple does a sleep here but the datasheet says to do it on resume */ - err =0 ; - out_unlock: -mutex_unlock (&onyx->mutex); - -return err; +return 0 ; }static int onyx_resume (struct codec_info_item *cii) {struct onyx *onyx = cii->codec_data;u8 v; -int err = -ENXIO ; -mutex_lock (&onyx->mutex); +guard (mutex)(&onyx->mutex);/* reset codec */ onyx->codec.gpio->methods->set_hw_reset (onyx->codec.gpio,0 );
@@ -826,17 +790,13 @@ static int onyx_resume(struct codec_info/* take codec out of suspend (if it still is after reset) */ if (onyx_read_register (onyx,ONYX_REG_CONTROL , &v)) - goto out_unlock; +return -ENXIO ;onyx_write_register (onyx,ONYX_REG_CONTROL , v & ~(ONYX_ADPSV |ONYX_DAPSV ));/* FIXME: should divide by sample rate, but 8k is the lowest we go */ msleep (2205000 /8000 );/* reset all values */ onyx_register_init (onyx); - err =0 ; - out_unlock: -mutex_unlock (&onyx->mutex); - - return err; + return 0; }#endif /* CONFIG_PM */
sound/aoa/codecs/tas.c +39 -74
303 lines not on this page · raw message
sound/aoa/core/gpio-feature.c +7 -13
@@ -212,10 +212,9 @@ static void ftr_handle_notify(struct worstruct gpio_notification *notif = container_of(work, struct gpio_notification, work.work); -mutex_lock (¬if->mutex); +guard (mutex)(¬if->mutex);if (notif->notify) notif->notify (notif->data); -mutex_unlock (¬if->mutex); }static void gpio_enable_dual_edge (int gpio)
@@ -341,19 +340,17 @@ static int ftr_set_notify(struct gpio_ruif (!irq)return -ENODEV ; -mutex_lock (¬if->mutex); +guard (mutex)(¬if->mutex); old = notif->notify; -if (!old && !notify) { - err =0 ; - goto out_unlock; - } +if (!old && !notify) +return 0 ;if (old && notify) {if (old == notify && notif->data == data) err =0 ; - goto out_unlock; +return err; }if (old && !notify)
@@ -362,16 +359,13 @@ static int ftr_set_notify(struct gpio_ruif (!old && notify) { err =request_irq (irq, ftr_handle_notify_irq,0 , name, notif);if (err) - goto out_unlock; +return err; } notif->notify = notify; notif->data = data; - err =0 ; - out_unlock: -mutex_unlock (¬if->mutex); -return err; +return 0 ; } static int ftr_get_detect(struct gpio_runtime *rt,
sound/aoa/core/gpio-pmf.c +9 -17
@@ -74,10 +74,9 @@ static void pmf_handle_notify(struct worstruct gpio_notification *notif = container_of(work, struct gpio_notification, work.work); -mutex_lock (¬if->mutex); +guard (mutex)(¬if->mutex);if (notif->notify) notif->notify (notif->data); -mutex_unlock (¬if->mutex); }static void pmf_gpio_init (struct gpio_runtime *rt)
@@ -154,19 +153,17 @@ static int pmf_set_notify(struct gpio_rureturn -EINVAL ; } -mutex_lock (¬if->mutex); +guard (mutex)(¬if->mutex); old = notif->notify; -if (!old && !notify) { - err =0 ; - goto out_unlock; - } +if (!old && !notify) +return 0 ;if (old && notify) {if (old == notify && notif->data == data) err =0 ; - goto out_unlock; +return err; }if (old && !notify) {
@@ -178,10 +175,8 @@ static int pmf_set_notify(struct gpio_ruif (!old && notify) { irq_client =kzalloc (sizeof (struct pmf_irq_client ),GFP_KERNEL ); -if (!irq_client) { - err = -ENOMEM ; - goto out_unlock; - } +if (!irq_client) +return -ENOMEM ; irq_client->data = notif; irq_client->handler = pmf_handle_notify_irq; irq_client->owner =THIS_MODULE ;
@@ -192,17 +187,14 @@ static int pmf_set_notify(struct gpio_ruprintk (KERN_ERR "snd-aoa: gpio layer failed to" " register %s irq (%d)\n" , name, err);kfree (irq_client); - goto out_unlock; +return err; } notif->gpio_private = irq_client; } notif->notify = notify; notif->data = data; - err =0 ; - out_unlock: -mutex_unlock (¬if->mutex); -return err; +return 0 ; } static int pmf_get_detect(struct gpio_runtime *rt,
sound/aoa/soundbus/i2sbus/pcm.c +25 -51
184 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 227/342 ALSA: aoa: i2sbus: clear stale prepared state
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cássio Gabriel <[email protected]>
[ Upstream commit 5ed060d5491597490fb53ec69da3edc4b1e8c165 ]
The i2sbus PCM code uses pi->active to constrain the sibling stream to an already prepared duplex format and rate in i2sbus_pcm_open().
That state is set from i2sbus_pcm_prepare(), but the current code only clears it on close. As a result, the sibling stream can inherit stale constraints after the prepared state has been torn down.
Clear pi->active when hw_params() or hw_free() tears down the prepared state, and set it again only after prepare succeeds.
Replace the stale FIXME in the duplex constraint comment with a description of the current driver behavior: i2sbus still programs a single shared transport configuration for both directions, so mixed formats are not supported in duplex mode.
--- sound/aoa/soundbus/i2sbus/pcm.c | 55 ++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 11 deletions(-)
sound/aoa/soundbus/i2sbus/pcm.c +44 -11
@@ -165,17 +165,16 @@ static int i2sbus_pcm_open(struct i2sbus* currently in use (if any). */ hw->rate_min =5512 ; hw->rate_max =192000 ; -/* if the other stream is active, then we can only -* support what it is currently using. -* FIXME: I lied. This comment is wrong. We can support -* anything that works with the same serial format, ie. -* when recording 24 bit sound we can well play 16 bit -* sound at the same time iff using the same transfer mode. +/* If the other stream is already prepared, keep this stream +* on the same duplex format and rate. +* +* i2sbus_pcm_prepare() still programs one shared transport +* configuration for both directions, so mixed duplex formats +* are not supported here. */ if (other->active) { -/* FIXME: is this guaranteed by the alsa api? */ hw->formats &=pcm_format_to_bits (i2sdev->format); -/* see above, restrict rates to the one we already have */ +/* Restrict rates to the one already in use. */ hw->rate_min = i2sdev->rate; hw->rate_max = i2sdev->rate; }
@@ -283,6 +282,23 @@ void i2sbus_wait_for_stop_both(struct i2 }#endif +static void i2sbus_pcm_clear_active (struct i2sbus_dev *i2sdev,int in) +{ +struct pcm_info *pi; + +guard (mutex)(&i2sdev->lock); + +get_pcm_info (i2sdev, in, &pi,NULL ); + pi->active =0 ; +} + +static inline int i2sbus_hw_params (struct snd_pcm_substream *substream, +struct snd_pcm_hw_params *params,int in) +{ +i2sbus_pcm_clear_active (snd_pcm_substream_chip (substream), in); +return 0 ; +} +static inline int i2sbus_hw_free (struct snd_pcm_substream *substream,int in) {struct i2sbus_dev *i2sdev =snd_pcm_substream_chip (substream);
@@ -291,14 +307,27 @@ static inline int i2sbus_hw_free(structget_pcm_info (i2sdev, in, &pi,NULL );if (pi->dbdma_ring.stopping)i2sbus_wait_for_stop (i2sdev, pi); +i2sbus_pcm_clear_active (i2sdev, in);return 0 ; } +static int i2sbus_playback_hw_params (struct snd_pcm_substream *substream, +struct snd_pcm_hw_params *params) +{ +return i2sbus_hw_params (substream, params,0 ); +} +static int i2sbus_playback_hw_free (struct snd_pcm_substream *substream) {return i2sbus_hw_free (substream,0 ); } +static int i2sbus_record_hw_params (struct snd_pcm_substream *substream, +struct snd_pcm_hw_params *params) +{ +return i2sbus_hw_params (substream, params,1 ); +} +static int i2sbus_record_hw_free (struct snd_pcm_substream *substream) {return i2sbus_hw_free (substream,1 );
@@ -335,7 +364,6 @@ static int i2sbus_pcm_prepare(struct i2sreturn -EINVAL ; runtime = pi->substream->runtime; - pi->active =1 ;if (other->active && ((i2sdev->format != runtime->format) || (i2sdev->rate != runtime->rate)))
@@ -450,9 +478,11 @@ static int i2sbus_pcm_prepare(struct i2s/* early exit if already programmed correctly */ /* not locking these is fine since we touch them only in this function */ -if (in_le32 (&i2sdev->intfregs->serial_format) == sfr - &&in_le32 (&i2sdev->intfregs->data_word_sizes) == dws) +if (in_le32 (&i2sdev->intfregs->serial_format) == sfr && +in_le32 (&i2sdev->intfregs->data_word_sizes) == dws) { + pi->active =1 ;return 0 ; + }/* let's notify the codecs about clocks going away. * For now we only do mastering on the i2s cell... */
@@ -490,6 +520,7 @@ static int i2sbus_pcm_prepare(struct i2sif (cii->codec->switch_clock) cii->codec->switch_clock (cii,CLOCK_SWITCH_SLAVE ); + pi->active =1 ;return 0 ; }
@@ -746,6 +777,7 @@ static snd_pcm_uframes_t i2sbus_playbackstatic const struct snd_pcm_ops i2sbus_playback_ops = { .open = i2sbus_playback_open, .close = i2sbus_playback_close, + .hw_params = i2sbus_playback_hw_params, .hw_free = i2sbus_playback_hw_free, .prepare = i2sbus_playback_prepare, .trigger = i2sbus_playback_trigger,
@@ -814,6 +846,7 @@ static snd_pcm_uframes_t i2sbus_record_pstatic const struct snd_pcm_ops i2sbus_record_ops = { .open = i2sbus_record_open, .close = i2sbus_record_close, + .hw_params = i2sbus_record_hw_params, .hw_free = i2sbus_record_hw_free, .prepare = i2sbus_record_prepare, .trigger = i2sbus_record_trigger,
Greg Kroah-Hartman
maintainerPATCH 228/342 media: rc: ttusbir: respect DMA coherency rules
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Neukum <[email protected]>
[ Upstream commit 50acaad3d202c064779db8dc3d010007347f59c7 ]
Buffers must not share a cache line with other data structures. Allocate separately.
--- drivers/media/rc/ttusbir.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/media/rc/ttusbir.c +9 -4
@@ -32,7 +32,7 @@ struct ttusbir {struct led_classdev led;struct urb *bulk_urb; -uint8_t bulk_buffer[5 ]; +u8 *bulk_buffer;int bulk_out_endp, iso_in_endp;bool led_on, is_led_on;atomic_t led_complete;
@@ -188,13 +188,16 @@ static int ttusbir_probe(struct usb_intestruct rc_dev *rc;int i, j, ret;int altsetting =-1 ; +u8 *buffer; tt =kzalloc (sizeof (*tt),GFP_KERNEL ); + buffer =kzalloc (5 ,GFP_KERNEL ); rc =rc_allocate_device (RC_DRIVER_IR_RAW ); -if (!tt || !rc) { +if (!tt || !rc || buffer) { ret = -ENOMEM ; goto out; } + tt->bulk_buffer = buffer;/* find the correct alt setting */ for (i =0 ; i < intf->num_altsetting && altsetting ==-1 ; i++) {
@@ -283,8 +286,8 @@ static int ttusbir_probe(struct usb_inte tt->bulk_buffer[3 ] =0x01 ;usb_fill_bulk_urb (tt->bulk_urb, tt->udev,usb_sndbulkpipe (tt->udev, - tt->bulk_out_endp), tt->bulk_buffer,sizeof (tt->bulk_buffer), - ttusbir_bulk_complete, tt); + tt->bulk_out_endp), tt->bulk_buffer,5 , + ttusbir_bulk_complete, tt); tt->led.name ="ttusbir:green:power" ; tt->led.default_trigger ="rc-feedback" ;
@@ -353,6 +356,7 @@ out:kfree (tt); }rc_free_device (rc); +kfree (buffer);return ret; }
@@ -375,6 +379,7 @@ static void ttusbir_disconnect(struct us }usb_kill_urb (tt->bulk_urb);usb_free_urb (tt->bulk_urb); +kfree (tt->bulk_buffer);usb_set_intfdata (intf,NULL );kfree (tt); }
Greg Kroah-Hartman
maintainerPATCH 229/342 ALSA: aoa: Skip devices with no codecs in i2sbus_resume()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit fd7df93013c5118812e63a52635dc6c3a805a1de ]
In i2sbus_resume(), skip devices with an empty codec list, which avoids using an uninitialized 'sysclock_factor' in the 32-bit format path in i2sbus_pcm_prepare().
In i2sbus_pcm_prepare(), replace two list_for_each_entry() loops with a single list_first_entry() now that the codec list is guaranteed to be non-empty by all callers.
--- sound/aoa/soundbus/i2sbus/core.c | 3 +++ sound/aoa/soundbus/i2sbus/pcm.c | 16 +++++----------- 2 files changed, 8 insertions(+), 11 deletions(-)
sound/aoa/soundbus/i2sbus/core.c +3 -0
@@ -411,6 +411,9 @@ static int i2sbus_resume(struct macio_deint err, ret =0 ;list_for_each_entry (i2sdev, &control->list, item) { +if (list_empty (&i2sdev->sound.codec_list)) +continue ; +/* reset i2s bus format etc. */ i2sbus_pcm_prepare_both (i2sdev);
sound/aoa/soundbus/i2sbus/pcm.c +5 -11
@@ -411,6 +411,9 @@ static int i2sbus_pcm_prepare(struct i2s/* set stop command */ command->command =cpu_to_le16 (DBDMA_STOP ); + cii =list_first_entry (&i2sdev->sound.codec_list, + struct codec_info_item, list); +/* ok, let's set the serial format and stuff */ switch (runtime->format) {/* 16 bit formats */
@@ -418,13 +421,7 @@ static int i2sbus_pcm_prepare(struct i2scase SNDRV_PCM_FORMAT_U16_BE :/* FIXME: if we add different bus factors we need to * do more here!! */ - bi.bus_factor =0 ; -list_for_each_entry (cii, &i2sdev->sound.codec_list, list) { - bi.bus_factor = cii->codec->bus_factor; -break ; - } -if (!bi.bus_factor) -return -ENODEV ; + bi.bus_factor = cii->codec->bus_factor; input_16bit =1 ;break ;case SNDRV_PCM_FORMAT_S32_BE :
@@ -438,10 +435,7 @@ static int i2sbus_pcm_prepare(struct i2sreturn -EINVAL ; }/* we assume all sysclocks are the same! */ -list_for_each_entry (cii, &i2sdev->sound.codec_list, list) { - bi.sysclock_factor = cii->codec->sysclock_factor; -break ; - } + bi.sysclock_factor = cii->codec->sysclock_factor; if (clock_and_divisors(bi.sysclock_factor, bi.bus_factor,
Greg Kroah-Hartman
maintainerPATCH 230/342 erofs: fix the out-of-bounds nameoff handling for trailing dirents
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gao Xiang <[email protected]>
[ Upstream commit d18a3b5d337fa412a38e776e6b4b857a58836575 ]
Currently we already have boundary-checks for nameoffs, but the trailing dirents are special since the namelens are calculated with strnlen() with unchecked nameoffs.
If a crafted EROFS has a trailing dirent with nameoff >= maxsize, maxsize - nameoff can underflow, causing strnlen() to read past the directory block.
nameoff0 should also be verified to be a multiple of sizeof(struct erofs_dirent) as well [1].
[1] https://sashiko.dev/#/patchset/20260416063511.3173774-1-hsiangkao%40linux.alibaba.com
--- fs/erofs/dir.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-)
fs/erofs/dir.c +16 -14
@@ -38,20 +38,18 @@ static int erofs_fill_dentries(struct in nameoff =le16_to_cpu (de->nameoff); de_name = (char *)dentry_blk + nameoff; -/* the last dirent in the block? */ -if (de +1 >= end) - de_namelen =strnlen (de_name, maxsize - nameoff); -else +/* non-trailing dirent in the directory block? */ +if (de +1 < end) de_namelen =le16_to_cpu (de[1 ].nameoff) - nameoff; +else if (maxsize <= nameoff) + goto err_bogus; +else + de_namelen =strnlen (de_name, maxsize - nameoff); -/* a corrupted entry is found */ -if (nameoff + de_namelen > maxsize || - de_namelen >EROFS_NAME_LEN ) { -erofs_err (dir->i_sb,"bogus dirent @ nid %llu" , -EROFS_I (dir)->nid); -DBG_BUGON (1 ); -return -EFSCORRUPTED ; - } +/* a corrupted entry is found (including negative namelen) */ +if (!in_range32 (de_namelen,1 ,EROFS_NAME_LEN ) || + nameoff + de_namelen > maxsize) + goto err_bogus;debug_one_dentry (d_type, de_name, de_namelen); if (!dir_emit(ctx, de_name, de_namelen,
@@ -63,6 +61,10 @@ static int erofs_fill_dentries(struct in } *ofs = maxsize;return 0 ; +err_bogus: +erofs_err (dir->i_sb,"bogus dirent @ nid %llu" ,EROFS_I (dir)->nid); +DBG_BUGON (1 ); +return -EFSCORRUPTED ; }static int erofs_readdir (struct file *f,struct dir_context *ctx)
@@ -96,8 +98,8 @@ static int erofs_readdir(struct file *f, nameoff =le16_to_cpu (de->nameoff); -if (nameoff <sizeof (struct erofs_dirent ) || - nameoff >=PAGE_SIZE ) { +if (!nameoff || nameoff >=PAGE_SIZE || + (nameoff %sizeof (struct erofs_dirent ))) {erofs_err (dir->i_sb,"invalid de[0].nameoff %u @ nid %llu" , nameoff,EROFS_I (dir)->nid);
Greg Kroah-Hartman
maintainerPATCH 231/342 media: rc: igorplugusb: heed coherency rules
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Neukum <[email protected]>
[ Upstream commit eac69475b01fe1e861dfe3960b57fa95671c132e ]
In a control request, the USB request structure can be subject to DMA on some HCs. Hence it must obey the rules for DMA coherency. Allocate it separately.
--- drivers/media/rc/igorplugusb.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)
drivers/media/rc/igorplugusb.c +12 -5
@@ -14,6 +14,7 @@#include <linux/device.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/usb.h> #include <linux/usb/input.h> #include <media/rc-core.h>
@@ -34,7 +35,7 @@ struct igorplugusb {struct device *dev;struct urb *urb; -struct usb_ctrlrequest request; +struct usb_ctrlrequest *request;struct timer_list timer;
@@ -123,7 +124,7 @@ static void igorplugusb_cmd(struct igorp {int ret; - ir->request.bRequest = cmd; + ir->request->bRequest = cmd; ir->urb->transfer_flags =0 ; ret =usb_submit_urb (ir->urb,GFP_ATOMIC );if (ret)
@@ -165,13 +166,17 @@ static int igorplugusb_probe(struct usb_if (!ir)return -ENOMEM ; + ir->request =kzalloc (sizeof (*ir->request),GFP_KERNEL ); +if (!ir->request) + goto fail; + ir->dev = &intf->dev;timer_setup (&ir->timer, igorplugusb_timer,0 ); - ir->request.bRequest =GET_INFRACODE ; - ir->request.bRequestType =USB_TYPE_VENDOR |USB_DIR_IN ; - ir->request.wLength =cpu_to_le16 (sizeof (ir->buf_in)); + ir->request->bRequest =GET_INFRACODE ; + ir->request->bRequestType =USB_TYPE_VENDOR |USB_DIR_IN ; + ir->request->wLength =cpu_to_le16 (sizeof (ir->buf_in)); ir->urb =usb_alloc_urb (0 ,GFP_KERNEL );if (!ir->urb)
@@ -223,6 +228,7 @@ fail:rc_free_device (ir->rc);usb_free_urb (ir->urb);del_timer (&ir->timer); +kfree (ir->request);return ret; }
@@ -236,6 +242,7 @@ static void igorplugusb_disconnect(strucusb_set_intfdata (intf,NULL );usb_kill_urb (ir->urb);usb_free_urb (ir->urb); +kfree (ir->request); }static const struct usb_device_id igorplugusb_table[] = {
Greg Kroah-Hartman
maintainerPATCH 232/342 sched: Use u64 for bandwidth ratio calculations
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joseph Salisbury <[email protected]>
[ Upstream commit c6e80201e057dfb7253385e60bf541121bf5dc33 ]
to_ratio() computes BW_SHIFT-scaled bandwidth ratios from u64 period and runtime values, but it returns unsigned long. tg_rt_schedulable() also stores the current group limit and the accumulated child sum in unsigned long.
On 32-bit builds, large bandwidth ratios can be truncated and the RT group sum can wrap when enough siblings are present. That can let an overcommitted RT hierarchy pass the schedulability check, and it also narrows the helper result for other callers.
Return u64 from to_ratio() and use u64 for the RT group totals so bandwidth ratios are preserved and compared at full width on both 32-bit and 64-bit builds.
--- kernel/sched/core.c | 2 +- kernel/sched/rt.c | 2 +- kernel/sched/sched.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
kernel/sched/core.c +1 -1
@@ -3328,7 +3328,7 @@ void sched_post_fork(struct task_structuclamp_post_fork (p); } -unsigned long to_ratio (u64 period,u64 runtime) +u64 to_ratio (u64 period,u64 runtime) {if (runtime ==RUNTIME_INF )return BW_UNIT ;
kernel/sched/rt.c +1 -1
@@ -2527,7 +2527,7 @@ static int tg_rt_schedulable(struct task {struct rt_schedulable_data *d = data;struct task_group *child; -unsigned long total, sum =0 ; +u64 total, sum =0 ;u64 period, runtime; period =ktime_to_ns (tg->rt_bandwidth.rt_period);
kernel/sched/sched.h +1 -1
@@ -1956,7 +1956,7 @@ extern void init_dl_inactive_task_timer(#define RATIO_SHIFT 8#define MAX_BW_BITS (64 - BW_SHIFT)#define MAX_BW ((1ULL << MAX_BW_BITS) - 1) -unsigned long to_ratio (u64 period,u64 runtime); +u64 to_ratio (u64 period,u64 runtime);extern void init_entity_runnable_average (struct sched_entity *se);extern void post_init_entity_util_avg (struct task_struct *p);
Greg Kroah-Hartman
maintainerPATCH 233/342 ALSA: core: Fix potential data race at fasync handling
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit 8146cd333d235ed32d48bb803fdf743472d7c783 ]
In snd_fasync_work_fn(), which is the offload work for traversing and processing the pending fasync list, the call of kill_fasync() is done outside the snd_fasync_lock for avoiding deadlocks. The problem is that its the references of fasync->on, fasync->signal and fasync->poll are done there also outside the lock. Since these may be modified by snd_kill_fasync() call concurrently from other process, inconsistent values might be passed to kill_fasync(). Although there shouldn't be critical UAF, it's still better to be addressed.
This patch moves the kill_fasync() argument evaluations inside the snd_fasync_lock for avoiding the data races above. The handling in fasync->on flag is optimized in the loop to skip directly.
Also, for more clarity, snd_fasync_free() takes the lock and unlink the pending entry more directly instead of clearing fasync->on flag.
--- sound/core/misc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
sound/core/misc.c +11 -3
@@ -171,14 +171,18 @@ static LIST_HEAD(snd_fasync_list);static void snd_fasync_work_fn (struct work_struct *work) {struct snd_fasync *fasync; +int signal, poll;spin_lock_irq (&snd_fasync_lock);while (!list_empty (&snd_fasync_list)) { fasync = list_first_entry(&snd_fasync_list, struct snd_fasync, list);list_del_init (&fasync->list); +if (!fasync->on) +continue ; + signal = fasync->signal; + poll = fasync->poll;spin_unlock_irq (&snd_fasync_lock); -if (fasync->on) -kill_fasync (&fasync->fasync, fasync->signal, fasync->poll); +kill_fasync (&fasync->fasync, signal, poll);spin_lock_irq (&snd_fasync_lock); }spin_unlock_irq (&snd_fasync_lock);
@@ -234,7 +238,11 @@ void snd_fasync_free(struct snd_fasync * {if (!fasync)return ; - fasync->on =0 ; + +spin_lock_irq (&snd_fasync_lock); +list_del_init (&fasync->list); +spin_unlock_irq (&snd_fasync_lock); +flush_work (&snd_fasync_work);kfree (fasync); }
Greg Kroah-Hartman
maintainerPATCH 234/342 net: qrtr: ns: Limit the maximum number of lookups
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manivannan Sadhasivam <[email protected]>
[ Upstream commit 5640227d9a21c6a8be249a10677b832e7f40dc55 ]
Current code does no bound checking on the number of lookups a client can perform. Though the code restricts the lookups to local clients, there is still a possibility of a malicious local client sending a flood of NEW_LOOKUP messages over the same socket.
Fix this issue by limiting the maximum number of lookups to 64 globally. Since the nameserver allows only atmost one local observer, this global lookup count will ensure that the lookups stay within the limit.
Note that, limit of 64 is chosen based on the current platform requirements. If requirement changes in the future, this limit can be increased.
--- net/qrtr/ns.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
net/qrtr/ns.c +14 -0
@@ -21,6 +21,7 @@ static struct {struct socket *sock;struct sockaddr_qrtr bcast_sq;struct list_head lookups; +u32 lookup_count;struct workqueue_struct *workqueue;struct work_struct work;void (*saved_data_ready)(struct sock *sk);
@@ -69,6 +70,11 @@ struct qrtr_node {struct radix_tree_root servers; }; +/* Max lookup limit is chosen based on the current platform requirements. If the +* requirement changes in the future, this value can be increased. +*/ +#define QRTR_NS_MAX_LOOKUPS 64 +static struct qrtr_node *node_get (unsigned int node_id) {struct qrtr_node *node;
@@ -457,6 +463,7 @@ static int ctrl_cmd_del_client(struct solist_del (&lookup->li);kfree (lookup); + qrtr_ns.lookup_count--; }/* Remove the server belonging to this port but don't broadcast
@@ -598,6 +605,11 @@ static int ctrl_cmd_new_lookup(struct soif (from->sq_node != qrtr_ns.local_node)return -EINVAL ; +if (qrtr_ns.lookup_count >=QRTR_NS_MAX_LOOKUPS ) { +pr_err_ratelimited ("QRTR client node exceeds max lookup limit!\n" ); +return -ENOSPC ; + } + lookup =kzalloc (sizeof (*lookup),GFP_KERNEL );if (!lookup)return -ENOMEM ;
@@ -606,6 +618,7 @@ static int ctrl_cmd_new_lookup(struct so lookup->service = service; lookup->instance = instance;list_add_tail (&lookup->li, &qrtr_ns.lookups); + qrtr_ns.lookup_count++;memset (&filter,0 ,sizeof (filter)); filter.service = service;
@@ -672,6 +685,7 @@ static void ctrl_cmd_del_lookup(struct slist_del (&lookup->li);kfree (lookup); + qrtr_ns.lookup_count--; } }
Greg Kroah-Hartman
maintainerPATCH 235/342 net: qrtr: ns: Change servers radix tree to xarray
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vignesh Viswanathan <[email protected]>
[ Upstream commit 608a147a88728f84bbd2efdde3d4984339f1d872 ]
There is a use after free scenario while iterating through the servers radix tree despite the ns being a single threaded process. This can happen when the radix tree APIs are not synchronized with the rcu_read_lock() APIs.
Convert the radix tree for servers to xarray to take advantage of the built in rcu lock usage provided by xarray.
Stable-dep-of: 68efba36446a ("net: qrtr: ns: Free the node during ctrl_cmd_bye()")
--- net/qrtr/ns.c | 133 ++++++++++------------------------------------------------ 1 file changed, 24 insertions(+), 109 deletions(-)
net/qrtr/ns.c +24 -109
@@ -67,7 +67,7 @@ struct qrtr_server {struct qrtr_node {unsigned int id; -struct radix_tree_root servers; +struct xarray servers; };/* Max lookup limit is chosen based on the current platform requirements. If the
@@ -89,6 +89,7 @@ static struct qrtr_node *node_get(unsignreturn NULL ; node->id = node_id; +xa_init (&node->servers);if (radix_tree_insert (&nodes, node_id, node)) {kfree (node);
@@ -199,40 +200,23 @@ static void lookup_notify(struct sockaddstatic int announce_servers (struct sockaddr_qrtr *sq) { -struct radix_tree_iter iter;struct qrtr_server *srv;struct qrtr_node *node; -void __rcu **slot; +unsigned long index;int ret; node =node_get (qrtr_ns.local_node);if (!node)return 0 ; -rcu_read_lock ();/* Announce the list of servers registered in this node */ -radix_tree_for_each_slot (slot, &node->servers, &iter,0 ) { - srv =radix_tree_deref_slot (slot); -if (!srv) -continue ; -if (radix_tree_deref_retry (srv)) { - slot =radix_tree_iter_retry (&iter); -continue ; - } - slot =radix_tree_iter_resume (slot, &iter); -rcu_read_unlock (); - +xa_for_each (&node->servers, index, srv) { ret =service_announce_new (sq, srv);if (ret <0 ) {pr_err ("failed to announce new service\n" );return ret; } - -rcu_read_lock (); } - -rcu_read_unlock (); -return 0 ; }
@@ -262,14 +246,17 @@ static struct qrtr_server *server_add(un goto err;/* Delete the old server on the same port */ - old =radix_tree_lookup (&node->servers, port); + old =xa_store (&node->servers, port, srv,GFP_KERNEL );if (old) { -radix_tree_delete (&node->servers, port); -kfree (old); +if (xa_is_err (old)) { +pr_err ("failed to add server [0x%x:0x%x] ret:%d\n" , + srv->service, srv->instance,xa_err (old)); + goto err; + }else { +kfree (old); + } } -radix_tree_insert (&node->servers, port, srv); -trace_qrtr_ns_server_add (srv->service, srv->instance, srv->node, srv->port);
@@ -286,11 +273,11 @@ static int server_del(struct qrtr_node *struct qrtr_server *srv;struct list_head *li; - srv =radix_tree_lookup (&node->servers, port); + srv =xa_load (&node->servers, port);if (!srv)return -ENOENT ; -radix_tree_delete (&node->servers, port); +xa_erase (&node->servers, port);/* Broadcast the removal of local servers */ if (srv->node == qrtr_ns.local_node && bcast)
@@ -350,13 +337,12 @@ static int ctrl_cmd_hello(struct sockaddstatic int ctrl_cmd_bye (struct sockaddr_qrtr *from) {struct qrtr_node *local_node; -struct radix_tree_iter iter;struct qrtr_ctrl_pkt pkt;struct qrtr_server *srv;struct sockaddr_qrtr sq;struct msghdr msg = { };struct qrtr_node *node; -void __rcu **slot; +unsigned long index;struct kvec iv;int ret;
@@ -367,22 +353,9 @@ static int ctrl_cmd_bye(struct sockaddr_if (!node)return 0 ; -rcu_read_lock ();/* Advertise removal of this client to all servers of remote node */ -radix_tree_for_each_slot (slot, &node->servers, &iter,0 ) { - srv =radix_tree_deref_slot (slot); -if (!srv) -continue ; -if (radix_tree_deref_retry (srv)) { - slot =radix_tree_iter_retry (&iter); -continue ; - } - slot =radix_tree_iter_resume (slot, &iter); -rcu_read_unlock (); +xa_for_each (&node->servers, index, srv)server_del (node, srv->port, true); -rcu_read_lock (); - } -rcu_read_unlock ();/* Advertise the removal of this client to all local servers */ local_node =node_get (qrtr_ns.local_node);
@@ -393,18 +366,7 @@ static int ctrl_cmd_bye(struct sockaddr_ pkt.cmd =cpu_to_le32 (QRTR_TYPE_BYE ); pkt.client.node =cpu_to_le32 (from->sq_node); -rcu_read_lock (); -radix_tree_for_each_slot (slot, &local_node->servers, &iter,0 ) { - srv =radix_tree_deref_slot (slot); -if (!srv) -continue ; -if (radix_tree_deref_retry (srv)) { - slot =radix_tree_iter_retry (&iter); -continue ; - } - slot =radix_tree_iter_resume (slot, &iter); -rcu_read_unlock (); - +xa_for_each (&local_node->servers, index, srv) { sq.sq_family =AF_QIPCRTR ; sq.sq_node = srv->node; sq.sq_port = srv->port;
@@ -417,11 +379,7 @@ static int ctrl_cmd_bye(struct sockaddr_pr_err ("failed to send bye cmd\n" );return ret; } -rcu_read_lock (); } - -rcu_read_unlock (); -return 0 ; }
@@ -429,7 +387,6 @@ static int ctrl_cmd_del_client(struct so unsigned int node_id, unsigned int port) {struct qrtr_node *local_node; -struct radix_tree_iter iter;struct qrtr_lookup *lookup;struct qrtr_ctrl_pkt pkt;struct msghdr msg = { };
@@ -438,7 +395,7 @@ static int ctrl_cmd_del_client(struct sostruct qrtr_node *node;struct list_head *tmp;struct list_head *li; -void __rcu **slot; +unsigned long index;struct kvec iv;int ret;
@@ -484,18 +441,7 @@ static int ctrl_cmd_del_client(struct so pkt.client.node =cpu_to_le32 (node_id); pkt.client.port =cpu_to_le32 (port); -rcu_read_lock (); -radix_tree_for_each_slot (slot, &local_node->servers, &iter,0 ) { - srv =radix_tree_deref_slot (slot); -if (!srv) -continue ; -if (radix_tree_deref_retry (srv)) { - slot =radix_tree_iter_retry (&iter); -continue ; - } - slot =radix_tree_iter_resume (slot, &iter); -rcu_read_unlock (); - +xa_for_each (&local_node->servers, index, srv) { sq.sq_family =AF_QIPCRTR ; sq.sq_node = srv->node; sq.sq_port = srv->port;
@@ -508,11 +454,7 @@ static int ctrl_cmd_del_client(struct sopr_err ("failed to send del client cmd\n" );return ret; } -rcu_read_lock (); } - -rcu_read_unlock (); -return 0 ; }
@@ -593,13 +535,12 @@ static int ctrl_cmd_del_server(struct sostatic int ctrl_cmd_new_lookup (struct sockaddr_qrtr *from,unsigned int service,unsigned int instance) { -struct radix_tree_iter node_iter;struct qrtr_server_filter filter; -struct radix_tree_iter srv_iter;struct qrtr_lookup *lookup; +struct qrtr_server *srv;struct qrtr_node *node; -void __rcu **node_slot; -void __rcu **srv_slot; +unsigned long node_idx; +unsigned long srv_idx;/* Accept only local observers */ if (from->sq_node != qrtr_ns.local_node)
@@ -624,40 +565,14 @@ static int ctrl_cmd_new_lookup(struct so filter.service = service; filter.instance = instance; -rcu_read_lock (); -radix_tree_for_each_slot (node_slot, &nodes, &node_iter,0 ) { - node =radix_tree_deref_slot (node_slot); -if (!node) -continue ; -if (radix_tree_deref_retry (node)) { - node_slot =radix_tree_iter_retry (&node_iter); -continue ; - } - node_slot =radix_tree_iter_resume (node_slot, &node_iter); - -radix_tree_for_each_slot (srv_slot, &node->servers, - &srv_iter,0 ) { -struct qrtr_server *srv; - - srv =radix_tree_deref_slot (srv_slot); -if (!srv) -continue ; -if (radix_tree_deref_retry (srv)) { - srv_slot =radix_tree_iter_retry (&srv_iter); -continue ; - } - +xa_for_each (&nodes, node_idx, node) { +xa_for_each (&node->servers, srv_idx, srv) {if (!server_match (srv, &filter))continue ; - srv_slot =radix_tree_iter_resume (srv_slot, &srv_iter); - -rcu_read_unlock ();lookup_notify (from, srv, true); -rcu_read_lock (); } } -rcu_read_unlock ();/* Empty notification, to indicate end of listing */ lookup_notify (from,NULL , true);
Greg Kroah-Hartman
maintainerPATCH 236/342 net: qrtr: ns: Free the node during ctrl_cmd_bye()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manivannan Sadhasivam <[email protected]>
[ Upstream commit 68efba36446a7774ea5b971257ade049272a07ac ]
A node sends the BYE packet when it is about to go down. So the nameserver should advertise the removal of the node to all remote and local observers and free the node finally. But currently, the nameserver doesn't free the node memory even after processing the BYE packet. This causes the node memory to leak.
Hence, remove the node from Xarray list and free the node memory during both success and failure case of ctrl_cmd_bye().
--- net/qrtr/ns.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-)
net/qrtr/ns.c +15 -5
@@ -344,7 +344,7 @@ static int ctrl_cmd_bye(struct sockaddr_struct qrtr_node *node;unsigned long index;struct kvec iv; -int ret; +int ret =0 ; iv.iov_base = &pkt; iv.iov_len =sizeof (pkt);
@@ -359,8 +359,10 @@ static int ctrl_cmd_bye(struct sockaddr_/* Advertise the removal of this client to all local servers */ local_node =node_get (qrtr_ns.local_node); -if (!local_node) -return 0 ; +if (!local_node) { + ret =0 ; + goto delete_node; + }memset (&pkt,0 ,sizeof (pkt)); pkt.cmd =cpu_to_le32 (QRTR_TYPE_BYE );
@@ -377,10 +379,18 @@ static int ctrl_cmd_bye(struct sockaddr_ ret =kernel_sendmsg (qrtr_ns.sock, &msg, &iv,1 ,sizeof (pkt));if (ret <0 ) {pr_err ("failed to send bye cmd\n" ); -return ret; + goto delete_node; } } -return 0 ; + +/* Ignore -ENODEV */ + ret =0 ; + +delete_node: +xa_erase (&nodes, from->sq_node); +kfree (node); + +return ret; } static int ctrl_cmd_del_client(struct sockaddr_qrtr *from,
Greg Kroah-Hartman
maintainerPATCH 237/342 net: qrtr: ns: Limit the total number of nodes
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manivannan Sadhasivam <[email protected]>
[ Upstream commit 27d5e84e810b0849d08b9aec68e48570461ce313 ]
Currently, the nameserver doesn't limit the number of nodes it handles. This can be an attack vector if a malicious client starts registering random nodes, leading to memory exhaustion.
Hence, limit the maximum number of nodes to 64. Note that, limit of 64 is chosen based on the current platform requirements. If requirement changes in the future, this limit can be increased.
--- net/qrtr/ns.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
net/qrtr/ns.c +17 -0
@@ -75,6 +75,16 @@ struct qrtr_node {*/ #define QRTR_NS_MAX_LOOKUPS 64 +/* Max nodes, server, lookup limits are chosen based on the current platform +* requirements. If the requirement changes in the future, these values can be +* increased. +*/ +#define QRTR_NS_MAX_NODES 64 +#define QRTR_NS_MAX_SERVERS 256 +#define QRTR_NS_MAX_LOOKUPS 64 + +static u8 node_count; +static struct qrtr_node *node_get (unsigned int node_id) {struct qrtr_node *node;
@@ -83,6 +93,11 @@ static struct qrtr_node *node_get(unsignif (node)return node; +if (node_count >=QRTR_NS_MAX_NODES ) { +pr_err_ratelimited ("QRTR clients exceed max node limit!\n" ); +return NULL ; + } +/* If node didn't exist, allocate and insert it to the tree */ node =kzalloc (sizeof (*node),GFP_KERNEL );if (!node)
@@ -96,6 +111,8 @@ static struct qrtr_node *node_get(unsignreturn NULL ; } + node_count++; +return node; }
Greg Kroah-Hartman
maintainerPATCH 238/342 net: bridge: use a stable FDB dst snapshot in RCU readers
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhengchuan Liang <[email protected]>
[ Upstream commit df4601653201de21b487c3e7fffd464790cab808 ]
Local FDB entries can be rewritten in place by fdb_delete_local(), which updates f->dst to another port or to NULL while keeping the entry alive. Several bridge RCU readers inspect f->dst, including br_fdb_fillbuf() through the brforward_read() sysfs path.
These readers currently load f->dst multiple times and can therefore observe inconsistent values across the check and later dereference. In br_fdb_fillbuf(), this means a concurrent local-FDB update can change f->dst after the NULL check and before the port_no dereference, leading to a NULL-ptr-deref.
Fix this by taking a single READ_ONCE() snapshot of f->dst in each affected RCU reader and using that snapshot for the rest of the access sequence. Also publish the in-place f->dst updates in fdb_delete_local() with WRITE_ONCE() so the readers and writer use matching access patterns.
--- net/bridge/br_arp_nd_proxy.c | 8 +++++--- net/bridge/br_fdb.c | 28 ++++++++++++++++++---------- 2 files changed, 23 insertions(+), 13 deletions(-)
net/bridge/br_arp_nd_proxy.c +5 -3
@@ -199,11 +199,12 @@ void br_do_proxy_suppress_arp(struct sk_ f =br_fdb_find_rcu (br, n->ha, vid);if (f) { +const struct net_bridge_port *dst =READ_ONCE (f->dst);bool replied = false;if ((p && (p->flags &BR_PROXYARP )) || - (f->dst && (f->dst->flags & (BR_PROXYARP_WIFI | -BR_NEIGH_SUPPRESS )))) { + (dst && (dst->flags & (BR_PROXYARP_WIFI | +BR_NEIGH_SUPPRESS )))) {if (!vid)br_arp_send (br, p, skb->dev, sip, tip, sha, n->ha, sha,0 ,0 );
@@ -463,9 +464,10 @@ void br_do_suppress_nd(struct sk_buff *s f =br_fdb_find_rcu (br, n->ha, vid);if (f) { +const struct net_bridge_port *dst =READ_ONCE (f->dst);bool replied = false; -if (f->dst && (f->dst->flags &BR_NEIGH_SUPPRESS )) { +if (dst && (dst->flags &BR_NEIGH_SUPPRESS )) {if (vid !=0 ) br_nd_send(br, p, skb, n, skb->vlan_proto,
net/bridge/br_fdb.c +18 -10
@@ -121,6 +121,7 @@ struct net_device *br_fdb_find_port(cons const unsigned char *addr, __u16 vid) { +const struct net_bridge_port *dst;struct net_bridge_fdb_entry *f;struct net_device *dev =NULL ;struct net_bridge *br;
@@ -133,8 +134,11 @@ struct net_device *br_fdb_find_port(cons br =netdev_priv (br_dev);rcu_read_lock (); f =br_fdb_find_rcu (br, addr, vid); -if (f && f->dst) - dev = f->dst->dev; +if (f) { + dst =READ_ONCE (f->dst); +if (dst) + dev = dst->dev; + }rcu_read_unlock ();return dev;
@@ -224,7 +228,7 @@ static void fdb_delete_local(struct net_ vg =nbp_vlan_group (op);if (op != p &ðer_addr_equal (op->dev->dev_addr, addr) && (!vid ||br_vlan_find (vg, vid))) { - f->dst = op; +WRITE_ONCE (f->dst, op);clear_bit (BR_FDB_ADDED_BY_USER , &f->flags);return ; }
@@ -235,7 +239,7 @@ static void fdb_delete_local(struct net_/* Maybe bridge device has same hw addr? */ if (p &ðer_addr_equal (br->dev->dev_addr, addr) && (!vid || (v &&br_vlan_should_use (v)))) { - f->dst =NULL ; +WRITE_ONCE (f->dst,NULL );clear_bit (BR_FDB_ADDED_BY_USER , &f->flags);return ; }
@@ -457,6 +461,7 @@ int br_fdb_test_addr(struct net_device *int br_fdb_fillbuf (struct net_bridge *br,void *buf,unsigned long maxnum,unsigned long skip) { +const struct net_bridge_port *dst;struct net_bridge_fdb_entry *f;struct __fdb_entry *fe = buf;int num =0 ;
@@ -472,7 +477,8 @@ int br_fdb_fillbuf(struct net_bridge *brcontinue ;/* ignore pseudo entry for local MAC address */ -if (!f->dst) + dst =READ_ONCE (f->dst); +if (!dst)continue ;if (skip) {
@@ -484,8 +490,8 @@ int br_fdb_fillbuf(struct net_bridge *brmemcpy (fe->mac_addr, f->key.addr.addr,ETH_ALEN );/* due to ABI compat need to split into hi/lo */ - fe->port_no = f->dst->port_no; - fe->port_hi = f->dst->port_no >>8 ; + fe->port_no = dst->port_no; + fe->port_hi = dst->port_no >>8 ; fe->is_local =test_bit (BR_FDB_LOCAL , &f->flags);if (!test_bit (BR_FDB_STATIC , &f->flags))
@@ -775,9 +781,11 @@ int br_fdb_dump(struct sk_buff *skb,rcu_read_lock ();hlist_for_each_entry_rcu (f, &br->fdb_list, fdb_node) { +const struct net_bridge_port *dst =READ_ONCE (f->dst); +if (*idx < cb->args[2 ]) goto skip; -if (filter_dev && (!f->dst || f->dst->dev != filter_dev)) { +if (filter_dev && (!dst || dst->dev != filter_dev)) {if (filter_dev != dev) goto skip;/* !f->dst is a special case for bridge
@@ -785,10 +793,10 @@ int br_fdb_dump(struct sk_buff *skb,* Therefore need a little more filtering * we only want to dump the !f->dst case */ -if (f->dst) +if (dst) goto skip; } -if (!filter_dev && f->dst) +if (!filter_dev && dst) goto skip; err = fdb_fill_info(skb, br, f,
Re: [PATCH 5.10 238/342] net: bridge: use a stable FDB dst snapshot in RCU readers
Quoted 19 lines · Greg Kroah-Hartman
5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhengchuan Liang <[email protected]>
[ Upstream commit
df4601653201de21b487c3e7fffd464790cab808]Local FDB entries can be rewritten in place by
fdb_delete_local(), which updatesf->dstto another port or toNULLwhile keeping the entry alive. Several bridge RCU readers inspectf->dst, includingbr_fdb_fillbuf()through thebrforward_read()sysfs path.These readers currently load
f->dstmultiple times and can therefore observe inconsistent values across the check and later dereference. Inbr_fdb_fillbuf(), this means a concurrent local-FDB update can changef->dstafter the NULL check and before theport_nodereference, leading to a NULL-ptr-deref.
Quoted 4 lines
Fix this by taking a single
READ_ONCE()snapshot off->dstin each affected RCU reader and using that snapshot for the rest of the access sequence. Also publish the in-placef->dstupdates infdb_delete_local()withWRITE_ONCE()so the readers and writer use matching access patterns.
[...]
For 5.10 only, commit 3e19ae7c6fd6 "net: bridge: use READ_ONCE() and WRITE_ONCE() compiler barriers for fdb->dst" is also needed (but doesn't apply cleanly).
Ben.
Signature · 2 lines
Ben Hutchings I haven't lost my mind; it's backed up on tape somewhere.
Greg Kroah-Hartman
maintainerPATCH 239/342 mtd: spi-nor: sst: Fix write enable before AAI sequence
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sanjaikumar V S <[email protected]>
[ Upstream commit a0f64241d3566a49c0a9b33ba7ae458ae22003a9 ]
When writing to SST flash starting at an odd address, a single byte is first programmed using the byte program (BP) command. After this operation completes, the flash hardware automatically clears the Write Enable Latch (WEL) bit.
If an AAI (Auto Address Increment) word program sequence follows, it requires WEL to be set. Without re-enabling writes, the AAI sequence fails.
Add spi_nor_write_enable() after the odd-address byte program when more data needs to be written. Use a local boolean for clarity.
--- drivers/mtd/spi-nor/sst.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
drivers/mtd/spi-nor/sst.c +13 -0
@@ -63,6 +63,8 @@ static int sst_write(struct mtd_info *mt/* Start write from odd address. */ if (to %2 ) { +bool needs_write_enable = (len >1 ); + nor->program_opcode =SPINOR_OP_BP ;/* write one byte. */
@@ -76,6 +78,17 @@ static int sst_write(struct mtd_info *mt to++; actual++; + +/* +* Byte program clears the write enable latch. If more +* data needs to be written using the AAI sequence, +* re-enable writes. +*/ +if (needs_write_enable) { + ret =spi_nor_write_enable (nor); +if (ret) + goto out; + } }/* Write out most of the data here. */
Greg Kroah-Hartman
maintainerPATCH 240/342 udf: fix partition descriptor append bookkeeping
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Seohyeon Maeng <[email protected]>
[ Upstream commit 08841b06fa64d8edbd1a21ca6e613420c90cc4b8 ]
Mounting a crafted UDF image with repeated partition descriptors can trigger a heap out-of-bounds write in part_descs_loc[].
handle_partition_descriptor() deduplicates entries by partition number, but appended slots never record partnum. As a result duplicate Partition Descriptors are appended repeatedly and num_part_descs keeps growing.
Once the table is full, the growth path still sizes the allocation from partnum even though inserts are indexed by num_part_descs. If partnum is already aligned to PART_DESC_ALLOC_STEP, ALIGN(partnum, step) can keep the old capacity and the next append writes past the end of the table.
Store partnum in the appended slot and size growth from the next append count so deduplication and capacity tracking follow the same model.
--- fs/udf/super.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/udf/super.c +3 -1
@@ -1657,8 +1657,9 @@ static struct udf_vds_record *handle_parreturn &(data->part_descs_loc[i].rec);if (data->num_part_descs >= data->size_part_descs) {struct part_desc_seq_scan_data *new_loc; -unsigned int new_size =ALIGN (partnum,PART_DESC_ALLOC_STEP ); +unsigned int new_size; + new_size = data->num_part_descs +PART_DESC_ALLOC_STEP ; new_loc =kcalloc (new_size,sizeof (*new_loc),GFP_KERNEL );if (!new_loc)return ERR_PTR (-ENOMEM );
@@ -1668,6 +1669,7 @@ static struct udf_vds_record *handle_par data->part_descs_loc = new_loc; data->size_part_descs = new_size; } + data->part_descs_loc[data->num_part_descs].partnum = partnum;return &(data->part_descs_loc[data->num_part_descs++].rec); }
Greg Kroah-Hartman
maintainerPATCH 241/342 hfsplus: fix uninit-value by validating catalog record size
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
[ Upstream commit b6b592275aeff184aa82fcf6abccd833fb71b393 ]
Syzbot reported a KMSAN uninit-value issue in hfsplus_strcasecmp(). The root cause is that hfs_brec_read() doesn't validate that the on-disk record size matches the expected size for the record type being read.
When mounting a corrupted filesystem, hfs_brec_read() may read less data than expected. For example, when reading a catalog thread record, the debug output showed:
HFSPLUS_BREC_READ: rec_len=520, fd->entrylength=26
HFSPLUS_BREC_READ: WARNING - entrylength (26) < rec_len (520) - PARTIAL READ!
hfs_brec_read() only validates that entrylength is not greater than the buffer size, but doesn't check if it's less than expected. It successfully reads 26 bytes into a 520-byte structure and returns success, leaving 494 bytes uninitialized.
This uninitialized data in tmp.thread.nodeName then gets copied by hfsplus_cat_build_key_uni() and used by hfsplus_strcasecmp(), triggering the KMSAN warning when the uninitialized bytes are used as array indices in case_fold().
Fix by introducing hfsplus_brec_read_cat() wrapper that:
1. Calls hfs_brec_read() to read the data
2. Validates the record size based on the type field:
- Fixed size for folder and file records
- Variable size for thread records (depends on string length)
3. Returns -EIO if size doesn't match expected
For thread records, check against HFSPLUS_MIN_THREAD_SZ before reading nodeName.length to avoid reading uninitialized data at call sites that don't zero-initialize the entry structure.
Also initialize the tmp variable in hfsplus_find_cat() as defensive programming to ensure no uninitialized data even if validation is bypassed.
Stable-dep-of: 90c500e4fd83 ("hfsplus: fix held lock freed on hfsplus_fill_super()")
--- fs/hfsplus/bfind.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/hfsplus/catalog.c | 4 +-- fs/hfsplus/dir.c | 2 - fs/hfsplus/hfsplus_fs.h | 9 ++++++++ fs/hfsplus/super.c | 2 - 5 files changed, 64 insertions(+), 4 deletions(-)
fs/hfsplus/bfind.c +51 -0
@@ -287,3 +287,54 @@ out: fd->bnode = bnode;return res; } + +/** +* hfsplus_brec_read_cat - read and validate a catalog record +* @fd: find data structure +* @entry: pointer to catalog entry to read into +* +* Reads a catalog record and validates its size matches the expected +* size based on the record type. +* +* Returns 0 on success, or negative error code on failure. +*/ +int hfsplus_brec_read_cat (struct hfs_find_data *fd,hfsplus_cat_entry *entry) +{ +int res; +u32 expected_size; + + res =hfs_brec_read (fd, entry,sizeof (hfsplus_cat_entry)); +if (res) +return res; + +/* Validate catalog record size based on type */ +switch (be16_to_cpu (entry->type)) { +case HFSPLUS_FOLDER : + expected_size =sizeof (struct hfsplus_cat_folder ); +break ; +case HFSPLUS_FILE : + expected_size =sizeof (struct hfsplus_cat_file ); +break ; +case HFSPLUS_FOLDER_THREAD : +case HFSPLUS_FILE_THREAD : +/* Ensure we have at least the fixed fields before reading nodeName.length */ +if (fd->entrylength <HFSPLUS_MIN_THREAD_SZ ) { +pr_err ("thread record too short (got %u)\n" , fd->entrylength); +return -EIO ; + } + expected_size =hfsplus_cat_thread_size (&entry->thread); +break ; +default : +pr_err ("unknown catalog record type %d\n" , +be16_to_cpu (entry->type)); +return -EIO ; + } + +if (fd->entrylength != expected_size) { +pr_err ("catalog record size mismatch (type %d, got %u, expected %u)\n" , +be16_to_cpu (entry->type), fd->entrylength, expected_size); +return -EIO ; + } + +return 0 ; +}
fs/hfsplus/catalog.c +2 -2
@@ -194,12 +194,12 @@ static int hfsplus_fill_cat_thread(strucint hfsplus_find_cat (struct super_block *sb,u32 cnid,struct hfs_find_data *fd) { -hfsplus_cat_entry tmp; +hfsplus_cat_entry tmp = {0 };int err;u16 type;hfsplus_cat_build_key_with_cnid (sb, fd->search_key, cnid); - err =hfs_brec_read (fd, &tmp,sizeof (hfsplus_cat_entry)); + err =hfsplus_brec_read_cat (fd, &tmp);if (err)return err;
fs/hfsplus/dir.c +1 -1
@@ -49,7 +49,7 @@ static struct dentry *hfsplus_lookup(strif (unlikely (err <0 )) goto fail; again: - err =hfs_brec_read (&fd, &entry,sizeof (entry)); + err =hfsplus_brec_read_cat (&fd, &entry);if (err) {if (err == -ENOENT ) {hfs_find_exit (&fd);
fs/hfsplus/hfsplus_fs.h +9 -0
@@ -536,6 +536,15 @@ int hfsplus_submit_bio(struct super_bloc void **data, int op, int op_flags);int hfsplus_read_wrapper (struct super_block *sb); +static inline u32 hfsplus_cat_thread_size (const struct hfsplus_cat_thread *thread) +{ +return offsetof(struct hfsplus_cat_thread , nodeName) + + offsetof(struct hfsplus_unistr , unicode) + +be16_to_cpu (thread->nodeName.length) *sizeof (hfsplus_unichr); +} + +int hfsplus_brec_read_cat (struct hfs_find_data *fd,hfsplus_cat_entry *entry); +/* * time helpers: convert between 1904-base and 1970-base timestamps *
fs/hfsplus/super.c +1 -1
@@ -541,7 +541,7 @@ static int hfsplus_fill_super(struct sup err =hfsplus_cat_build_key (sb, fd.search_key,HFSPLUS_ROOT_CNID , &str);if (unlikely (err <0 )) goto out_put_root; -if (!hfs_brec_read (&fd, &entry,sizeof (entry))) { +if (!hfsplus_brec_read_cat (&fd, &entry)) {hfs_find_exit (&fd);if (entry.type !=cpu_to_be16 (HFSPLUS_FOLDER )) { err = -EIO ;
Greg Kroah-Hartman
maintainerPATCH 242/342 hfsplus: fix held lock freed on hfsplus_fill_super()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zilin Guan <[email protected]>
[ Upstream commit 90c500e4fd83fa33c09bc7ee23b6d9cc487ac733 ]
hfsplus_fill_super() calls hfs_find_init() to initialize a search structure, which acquires tree->tree_lock. If the subsequent call to hfsplus_cat_build_key() fails, the function jumps to the out_put_root error label without releasing the lock. The later cleanup path then frees the tree data structure with the lock still held, triggering a held lock freed warning.
Fix this by adding the missing hfs_find_exit(&fd) call before jumping to the out_put_root error label. This ensures that tree->tree_lock is properly released on the error path.
The bug was originally detected on v6.13-rc1 using an experimental static analysis tool we are developing, and we have verified that the issue persists in the latest mainline kernel. The tool is specifically designed to detect memory management issues. It is currently under active development and not yet publicly available.
We confirmed the bug by runtime testing under QEMU with x86_64 defconfig, lockdep enabled, and CONFIG_HFSPLUS_FS=y. To trigger the error path, we used GDB to dynamically shrink the max_unistr_len parameter to 1 before hfsplus_asc2uni() is called. This forces hfsplus_asc2uni() to naturally return -ENAMETOOLONG, which propagates to hfsplus_cat_build_key() and exercises the faulty error path. The following warning was observed during mount:
=========================
WARNING: held lock freed!
7.0.0-rc3-00016-gb4f0dd314b39 #4 Not tainted
-------------------------
mount/174 is freeing memory ffff888103f92000-ffff888103f92fff, with a lock still held there!
ffff888103f920b0 (&tree->tree_lock){+.+.}-{4:4}, at: hfsplus_find_init+0x154/0x1e0
2 locks held by mount/174:
#0: ffff888103f960e0 (&type->s_umount_key#42/1){+.+.}-{4:4}, at: alloc_super.constprop.0+0x167/0xa40
#1: ffff888103f920b0 (&tree->tree_lock){+.+.}-{4:4}, at: hfsplus_find_init+0x154/0x1e0
stack backtrace:
CPU: 2 UID: 0 PID: 174 Comm: mount Not tainted 7.0.0-rc3-00016-gb4f0dd314b39 #4 PREEMPT(lazy)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0x82/0xd0
debug_check_no_locks_freed+0x13a/0x180
kfree+0x16b/0x510
? hfsplus_fill_super+0xcb4/0x18a0
hfsplus_fill_super+0xcb4/0x18a0
? __pfx_hfsplus_fill_super+0x10/0x10
? srso_return_thunk+0x5/0x5f
? bdev_open+0x65f/0xc30
? srso_return_thunk+0x5/0x5f
? pointer+0x4ce/0xbf0
? trace_contention_end+0x11c/0x150
? __pfx_pointer+0x10/0x10
? srso_return_thunk+0x5/0x5f
? bdev_open+0x79b/0xc30
? srso_return_thunk+0x5/0x5f
? srso_return_thunk+0x5/0x5f
? vsnprintf+0x6da/0x1270
? srso_return_thunk+0x5/0x5f
? __mutex_unlock_slowpath+0x157/0x740
? __pfx_vsnprintf+0x10/0x10
? srso_return_thunk+0x5/0x5f
? srso_return_thunk+0x5/0x5f
? mark_held_locks+0x49/0x80
? srso_return_thunk+0x5/0x5f
? srso_return_thunk+0x5/0x5f
? irqentry_exit+0x17b/0x5e0
? trace_irq_disable.constprop.0+0x116/0x150
? __pfx_hfsplus_fill_super+0x10/0x10
? __pfx_hfsplus_fill_super+0x10/0x10
get_tree_bdev_flags+0x302/0x580
? __pfx_get_tree_bdev_flags+0x10/0x10
? vfs_parse_fs_qstr+0x129/0x1a0
? __pfx_vfs_parse_fs_qstr+0x3/0x10
vfs_get_tree+0x89/0x320
fc_mount+0x10/0x1d0
path_mount+0x5c5/0x21c0
? __pfx_path_mount+0x10/0x10
? trace_irq_enable.constprop.0+0x116/0x150
? trace_irq_enable.constprop.0+0x116/0x150
? srso_return_thunk+0x5/0x5f
? srso_return_thunk+0x5/0x5f
? kmem_cache_free+0x307/0x540
? user_path_at+0x51/0x60
? __x64_sys_mount+0x212/0x280
? srso_return_thunk+0x5/0x5f
__x64_sys_mount+0x212/0x280
? __pfx___x64_sys_mount+0x10/0x10
? srso_return_thunk+0x5/0x5f
? trace_irq_enable.constprop.0+0x116/0x150
? srso_return_thunk+0x5/0x5f
do_syscall_64+0x111/0x680
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ffacad55eae
Code: 48 8b 0d 85 1f 0f 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 49 89 ca b8 a5 00 00 8
RSP: 002b:00007fff1ab55718 EFLAGS: 00000246 ORIG_RAX: 00000000000000a5
RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ffacad55eae
RDX: 000055740c64e5b0 RSI: 000055740c64e630 RDI: 000055740c651ab0
RBP: 000055740c64e380 R08: 0000000000000000 R09: 0000000000000001
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 000055740c64e5b0 R14: 000055740c651ab0 R15: 000055740c64e380
</TASK>After applying this patch, the warning no longer appears.
--- fs/hfsplus/super.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/hfsplus/super.c +3 -1
@@ -539,8 +539,10 @@ static int hfsplus_fill_super(struct supif (err) goto out_put_root; err =hfsplus_cat_build_key (sb, fd.search_key,HFSPLUS_ROOT_CNID , &str); -if (unlikely (err <0 )) +if (unlikely (err <0 )) { +hfs_find_exit (&fd); goto out_put_root; + }if (!hfsplus_brec_read_cat (&fd, &entry)) {hfs_find_exit (&fd);if (entry.type !=cpu_to_be16 (HFSPLUS_FOLDER )) {
Greg Kroah-Hartman
maintainerPATCH 243/342 Bluetooth: hci_event: fix potential UAF in SSP passkey handlers
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuvam Pandey <[email protected]>
[ Upstream commit 85fa3512048793076eef658f66489112dcc91993 ]
hci_conn lookup and field access must be covered by hdev lock in hci_user_passkey_notify_evt() and hci_keypress_notify_evt(), otherwise the connection can be freed concurrently.
Extend the hci_dev_lock critical section to cover all conn usage in both handlers.
Keep the existing keypress notification behavior unchanged by routing the early exits through a common unlock path.
--- net/bluetooth/hci_event.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
net/bluetooth/hci_event.c +14 -4
@@ -4842,9 +4842,11 @@ static void hci_user_passkey_notify_evt(BT_DBG ("%s" , hdev->name); +hci_dev_lock (hdev); + conn =hci_conn_hash_lookup_ba (hdev,ACL_LINK , &ev->bdaddr);if (!conn) -return ; + goto unlock; conn->passkey_notify =__le32_to_cpu (ev->passkey); conn->passkey_entered =0 ;
@@ -4853,6 +4855,9 @@ static void hci_user_passkey_notify_evt(mgmt_user_passkey_notify (hdev, &conn->dst, conn->type, conn->dst_type, conn->passkey_notify, conn->passkey_entered); + +unlock: +hci_dev_unlock (hdev); }static void hci_keypress_notify_evt (struct hci_dev *hdev,struct sk_buff *skb)
@@ -4862,14 +4867,16 @@ static void hci_keypress_notify_evt(struBT_DBG ("%s" , hdev->name); +hci_dev_lock (hdev); + conn =hci_conn_hash_lookup_ba (hdev,ACL_LINK , &ev->bdaddr);if (!conn) -return ; + goto unlock;switch (ev->type) {case HCI_KEYPRESS_STARTED : conn->passkey_entered =0 ; -return ; + goto unlock;case HCI_KEYPRESS_ENTERED : conn->passkey_entered++;
@@ -4884,13 +4891,16 @@ static void hci_keypress_notify_evt(strubreak ;case HCI_KEYPRESS_COMPLETED : -return ; + goto unlock; }if (hci_dev_test_flag (hdev,HCI_MGMT ))mgmt_user_passkey_notify (hdev, &conn->dst, conn->type, conn->dst_type, conn->passkey_notify, conn->passkey_entered); + +unlock: +hci_dev_unlock (hdev); } static void hci_simple_pair_complete_evt(struct hci_dev *hdev,
Greg Kroah-Hartman
maintainerPATCH 244/342 can: ucan: fix typos in comments
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julia Lawall <[email protected]>
[ Upstream commit c34983c94166689358372d4af8d5def57752860c ]
Various spelling mistakes in comments.
Detected with the help of Coccinelle.
Stable-dep-of: fed4626501c8 ("can: ucan: fix devres lifetime")
--- drivers/net/can/usb/ucan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/can/usb/ucan.c +2 -2
@@ -1393,7 +1393,7 @@ static int ucan_probe(struct usb_interfa* Stage 3 for the final driver initialisation. */ -/* Prepare Memory for control transferes */ +/* Prepare Memory for control transfers */ ctl_msg_buffer =devm_kzalloc (&udev->dev,sizeof (union ucan_ctl_payload ),GFP_KERNEL );
@@ -1527,7 +1527,7 @@ static int ucan_probe(struct usb_interfa ret =ucan_device_request_in (up,UCAN_DEVICE_GET_FW_STRING ,0 ,sizeof (union ucan_ctl_payload ));if (ret >0 ) { -/* copy string while ensuring zero terminiation */ +/* copy string while ensuring zero termination */ strncpy (firmware_str, up->ctl_msg_buffer->raw,sizeof (union ucan_ctl_payload )); firmware_str[sizeof (union ucan_ctl_payload )] ='\0' ;
Greg Kroah-Hartman
maintainerPATCH 245/342 can: ucan: fix devres lifetime
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit fed4626501c871890da287bec62a96e52da1af89 ]
USB drivers bind to USB interfaces and any device managed resources should have their lifetime tied to the interface rather than parent USB device. This avoids issues like memory leaks when drivers are unbound without their devices being physically disconnected (e.g. on probe deferral or configuration changes).
Fix the control message buffer lifetime so that it is released on driver unbind.
--- drivers/net/can/usb/ucan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/can/usb/ucan.c +1 -1
@@ -1394,7 +1394,7 @@ static int ucan_probe(struct usb_interfa*/ /* Prepare Memory for control transfers */ - ctl_msg_buffer =devm_kzalloc (&udev->dev, + ctl_msg_buffer =devm_kzalloc (&intf->dev,sizeof (union ucan_ctl_payload ),GFP_KERNEL );if (!ctl_msg_buffer) {
Greg Kroah-Hartman
maintainerPATCH 246/342 ktest: Fixing indentation to match expected pattern
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: "John 'Warthog9' Hawley (VMware)" <[email protected]>
[ Upstream commit 12d4cddda2043466a5af8fc0c49e49f24f1d4c59 ]
This is a followup to "ktest: Adding editor hints to improve consistency" to actually adjust the existing indentation to match the, now, expected pattern (first column 4 spaces, 2nd tab, 3rd tab + 4 spaces, etc). This should, at least help, keep things consistent going forward now.
Stable-dep-of: 768059ede35f ("ktest: Fix the month in the name of the failure directory")
--- tools/testing/ktest/ktest.pl | 188 +++++++++++++++++++++---------------------- 1 file changed, 93 insertions(+), 95 deletions(-)
tools/testing/ktest/ktest.pl +93 -95
@@ -763,7 +763,7 @@ sub process_variables { # remove the space added in the beginning $retval =~ s/ //; - return "$retval" + return "$retval"; } sub set_value {
@@ -1099,7 +1099,7 @@ sub __read_config { } } } - + if ( ! -r $file ) { die "$name: $.: Can't read file $file\n$_"; }
@@ -1190,13 +1190,13 @@ sub __read_config { } sub get_test_case { - print "What test case would you like to run?\n"; - print " (build, install or boot)\n"; - print " Other tests are available but require editing ktest.conf\n"; - print " (see tools/testing/ktest/sample.conf)\n"; - my $ans = <STDIN>; - chomp $ans; - $default{"TEST_TYPE"} = $ans; + print "What test case would you like to run?\n"; + print " (build, install or boot)\n"; + print " Other tests are available but require editing ktest.conf\n"; + print " (see tools/testing/ktest/sample.conf)\n"; + my $ans = <STDIN>; + chomp $ans; + $default{"TEST_TYPE"} = $ans; } sub read_config {
@@ -1545,13 +1545,13 @@ sub dodie { close O; close L; } - send_email("KTEST: critical failure for test $i [$name]", - "Your test started at $script_start_time has failed with:\n@_\n", $log_file); + send_email("KTEST: critical failure for test $i [$name]", + "Your test started at $script_start_time has failed with:\n@_\n", $log_file); } if ($monitor_cnt) { - # restore terminal settings - system("stty $stty_orig"); + # restore terminal settings + system("stty $stty_orig"); } if (defined($post_test)) {
@@ -1736,81 +1736,81 @@ sub wait_for_monitor { } sub save_logs { - my ($result, $basedir) = @_; - my @t = localtime; - my $date = sprintf "%04d%02d%02d%02d%02d%02d", - 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; + my ($result, $basedir) = @_; + my @t = localtime; + my $date = sprintf "%04d%02d%02d%02d%02d%02d", + 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; - my $type = $build_type; - if ($type =~ /useconfig/) { - $type = "useconfig"; - } + my $type = $build_type; + if ($type =~ /useconfig/) { + $type = "useconfig"; + } - my $dir = "$machine-$test_type-$type-$result-$date"; + my $dir = "$machine-$test_type-$type-$result-$date"; - $dir = "$basedir/$dir"; + $dir = "$basedir/$dir"; - if (!-d $dir) { - mkpath($dir) or - dodie "can't create $dir"; - } + if (!-d $dir) { + mkpath($dir) or + dodie "can't create $dir"; + } - my %files = ( - "config" => $output_config, - "buildlog" => $buildlog, - "dmesg" => $dmesg, - "testlog" => $testlog, - ); + my %files = ( + "config" => $output_config, + "buildlog" => $buildlog, + "dmesg" => $dmesg, + "testlog" => $testlog, + ); - while (my ($name, $source) = each(%files)) { - if (-f "$source") { - cp "$source", "$dir/$name" or - dodie "failed to copy $source"; - } + while (my ($name, $source) = each(%files)) { + if (-f "$source") { + cp "$source", "$dir/$name" or + dodie "failed to copy $source"; } + } - doprint "*** Saved info to $dir ***\n"; + doprint "*** Saved info to $dir ***\n"; } sub fail { - if ($die_on_failure) { - dodie @_; - } + if ($die_on_failure) { + dodie @_; + } - doprint "FAILED\n"; + doprint "FAILED\n"; - my $i = $iteration; + my $i = $iteration; - # no need to reboot for just building. - if (!do_not_reboot) { - doprint "REBOOTING\n"; - reboot_to_good $sleep_time; - } + # no need to reboot for just building. + if (!do_not_reboot) { + doprint "REBOOTING\n"; + reboot_to_good $sleep_time; + } - my $name = ""; + my $name = ""; - if (defined($test_name)) { - $name = " ($test_name)"; - } + if (defined($test_name)) { + $name = " ($test_name)"; + } - print_times; + print_times; - doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - doprint "KTEST RESULT: TEST $i$name Failed: ", @_, "\n"; - doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - - if (defined($store_failures)) { - save_logs "fail", $store_failures; - } + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + doprint "KTEST RESULT: TEST $i$name Failed: ", @_, "\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - if (defined($post_test)) { - run_command $post_test; - } + if (defined($store_failures)) { + save_logs "fail", $store_failures; + } - return 1; + if (defined($post_test)) { + run_command $post_test; + } + + return 1; } sub run_command {
@@ -2011,9 +2011,9 @@ sub get_grub_index { $skip = '^\s*menuentry\s'; $submenu = '^\s*submenu\s'; } elsif ($reboot_type eq "grub2bls") { - $command = $grub_bls_get; - $target = '^title=.*' . $grub_menu_qt; - $skip = '^title='; + $command = $grub_bls_get; + $target = '^title=.*' . $grub_menu_qt; + $skip = '^title='; } else { return; }
@@ -2446,7 +2446,7 @@ sub check_buildlog { while (<IN>) { if (/$check_build_re/) { my $warning = process_warning_line $_; - + $warnings_list{$warning} = 1; } }
@@ -2708,7 +2708,7 @@ sub success { doprint "*******************************************\n"; if (defined($store_successes)) { - save_logs "success", $store_successes; + save_logs "success", $store_successes; } if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) {
@@ -3293,13 +3293,13 @@ sub run_config_bisect { $ret = run_config_bisect_test $config_bisect_type; if ($ret) { - doprint "NEW GOOD CONFIG ($pass)\n"; + doprint "NEW GOOD CONFIG ($pass)\n"; system("cp $output_config $tmpdir/good_config.tmp.$pass"); $pass++; # Return 3 for good config return 3; } else { - doprint "NEW BAD CONFIG ($pass)\n"; + doprint "NEW BAD CONFIG ($pass)\n"; system("cp $output_config $tmpdir/bad_config.tmp.$pass"); $pass++; # Return 4 for bad config
@@ -3418,7 +3418,7 @@ sub config_bisect { } while ($ret == 3 || $ret == 4); if ($ret == 2) { - config_bisect_end "$good_config.tmp", "$bad_config.tmp"; + config_bisect_end "$good_config.tmp", "$bad_config.tmp"; } return $ret if ($ret < 0);
@@ -3598,7 +3598,6 @@ sub read_kconfig { my $cont = 0; my $line; - if (! -f $kconfig) { doprint "file $kconfig does not exist, skipping\n"; return;
@@ -3707,7 +3706,7 @@ sub read_depends { if (! -f $kconfig && $arch =~ /\d$/) { my $orig = $arch; - # some subarchs have numbers, truncate them + # some subarchs have numbers, truncate them $arch =~ s/\d*$//; $kconfig = "$builddir/arch/$arch/Kconfig"; if (! -f $kconfig) {
@@ -3903,7 +3902,7 @@ sub make_min_config { foreach my $config (@config_keys) { my $kconfig = chomp_config $config; if (!defined $depcount{$kconfig}) { - $depcount{$kconfig} = 0; + $depcount{$kconfig} = 0; } }
@@ -4005,13 +4004,13 @@ sub make_min_config { my $failed = 0; build "oldconfig" or $failed = 1; if (!$failed) { - start_monitor_and_install or $failed = 1; + start_monitor_and_install or $failed = 1; - if ($type eq "test" && !$failed) { - do_run_test or $failed = 1; - } + if ($type eq "test" && !$failed) { + do_run_test or $failed = 1; + } - end_monitor; + end_monitor; } $in_bisect = 0;
@@ -4330,8 +4329,8 @@ sub cancel_test { } if ($email_when_canceled) { my $name = get_test_name; - send_email("KTEST: Your [$name] test was cancelled", - "Your test started at $script_start_time was cancelled: sig int"); + send_email("KTEST: Your [$name] test was cancelled", + "Your test started at $script_start_time was cancelled: sig int"); } run_post_ktest; die "\nCaught Sig Int, test interrupted: $!\n"
@@ -4380,15 +4379,15 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; # The first test may override the PRE_KTEST option if ($i == 1) { - if (defined($pre_ktest)) { - doprint "\n"; - run_command $pre_ktest; - } - if ($email_when_started) { + if (defined($pre_ktest)) { + doprint "\n"; + run_command $pre_ktest; + } + if ($email_when_started) { my $name = get_test_name; - send_email("KTEST: Your [$name] test was started", - "Your test was started on $script_start_time"); - } + send_email("KTEST: Your [$name] test was started", + "Your test was started on $script_start_time"); + } } # Any test can override the POST_KTEST option
@@ -4556,12 +4555,11 @@ if ($opt{"POWEROFF_ON_SUCCESS"}) { run_command $switch_to_good; } - doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n"; if ($email_when_finished) { send_email("KTEST: Your test has finished!", - "$successes of $opt{NUM_TESTS} tests started at $script_start_time were successful!"); + "$successes of $opt{NUM_TESTS} tests started at $script_start_time were successful!"); } if (defined($opt{"LOG_FILE"})) {
Greg Kroah-Hartman
maintainerPATCH 247/342 ktest: Fix the month in the name of the failure directory
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
[ Upstream commit 768059ede35f197575a38b10797b52402d9d4d2f ]
The Perl localtime() function returns the month starting at 0 not 1. This caused the date produced to create the directory for saving files of a failed run to have the month off by one.
machine-test-useconfig-fail-20260314073628
The above happened in April, not March. The correct name should have been:
machine-test-useconfig-fail-20260414073628
This was somewhat confusing.
--- tools/testing/ktest/ktest.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/ktest/ktest.pl +1 -1
@@ -1739,7 +1739,7 @@ sub save_logs { my ($result, $basedir) = @_; my @t = localtime; my $date = sprintf "%04d%02d%02d%02d%02d%02d", - 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; + 1900+$t[5],$t[4]+1,$t[3],$t[2],$t[1],$t[0]; my $type = $build_type; if ($type =~ /useconfig/) {
Greg Kroah-Hartman
maintainerPATCH 248/342 ceph: only d_add() negative dentries when they are unhashed
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Kellermann <[email protected]>
[ Upstream commit 803447f93d75ab6e40c85e6d12b5630d281d70d6 ]
Ceph can call d_add(dentry, NULL) on a negative dentry that is already present in the primary dcache hash.
In the current VFS that is not safe. d_add() goes through __d_add() to __d_rehash(), which unconditionally reinserts dentry->d_hash into the hlist_bl bucket. If the dentry is already hashed, reinserting the same node can corrupt the bucket, including creating a self-loop. Once that happens, __d_lookup() can spin forever in the hlist_bl walk, typically looping only on the d_name.hash mismatch check and eventually triggering RCU stall reports like this one:
rcu: INFO: rcu_sched self-detected stall on CPU rcu: 87-....: (2100 ticks this GP) idle=3a4c/1/0x4000000000000000 softirq=25003319/25003319 fqs=829 rcu: (t=2101 jiffies g=79058445 q=698988 ncpus=192) CPU: 87 UID: 2952868916 PID: 3933303 Comm: php-cgi8.3 Not tainted 6.18.17-i1-amd #950 NONE Hardware name: Dell Inc. PowerEdge R7615/0G9DHV, BIOS 1.6.6 09/22/2023 RIP: 0010:__d_lookup+0x46/0xb0 Code: c1 e8 07 48 8d 04 c2 48 8b 00 49 89 fc 49 89 f5 48 89 c3 48 83 e3 fe 48 83 f8 01 77 0f eb 2d 0f 1f 44 00 00 48 8b 1b 48 85 db <74> 20 39 6b 18 75 f3 48 8d 7b 78 e8 ba 85 d0 00 4c 39 63 10 74 1f RSP: 0018:ff745a70c8253898 EFLAGS: 00000282 RAX: ff26e470054cb208 RBX: ff26e470054cb208 RCX: 000000006e958966 RDX: ff26e48267340000 RSI: ff745a70c82539b0 RDI: ff26e458f74655c0 RBP: 000000006e958966 R08: 0000000000000180 R09: 9cd08d909b919a89 R10: ff26e458f74655c0 R11: 0000000000000000 R12: ff26e458f74655c0 R13: ff745a70c82539b0 R14: d0d0d0d0d0d0d0d0 R15: 2f2f2f2f2f2f2f2f FS: 00007f5770896980(0000) GS:ff26e482c5d88000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f5764de50c0 CR3: 000000a72abb5001 CR4: 0000000000771ef0 PKRU: 55555554 Call Trace: <TASK> lookup_fast+0x9f/0x100 walk_component+0x1f/0x150 link_path_walk+0x20e/0x3d0 path_lookupat+0x68/0x180 filename_lookup+0xdc/0x1e0 vfs_statx+0x6c/0x140 vfs_fstatat+0x67/0xa0 __do_sys_newfstatat+0x24/0x60 do_syscall_64+0x6a/0x230 entry_SYSCALL_64_after_hwframe+0x76/0x7e
This is reachable with reused cached negative dentries. A Ceph lookup or atomic_open can be handed a negative dentry that is already hashed, and fs/ceph/dir.c then hits one of two paths that incorrectly assume "negative" also means "unhashed":
- ceph_finish_lookup():
MDS reply is -ENOENT with no trace
-> d_add(dentry, NULL)
- ceph_lookup():
local ENOENT fast path for a complete directory with shared caps -> d_add(dentry, NULL)
Both paths can therefore re-add an already-hashed negative dentry.
Ceph already uses the correct pattern elsewhere: ceph_fill_trace() only calls d_add(dn, NULL) for a negative null-dentry reply when d_unhashed(dn) is true.
Fix both fs/ceph/dir.c sites the same way: only call d_add() for a negative dentry when it is actually unhashed. If the negative dentry is already hashed, leave it in place and reuse it as-is.
This preserves the existing behavior for unhashed dentries while avoiding d_hash list corruption for reused hashed negatives.
--- fs/ceph/dir.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
fs/ceph/dir.c +4 -2
@@ -719,7 +719,8 @@ struct dentry *ceph_finish_lookup(structd_drop (dentry); err = -ENOENT ; }else { -d_add (dentry,NULL ); +if (d_unhashed (dentry)) +d_add (dentry,NULL ); } } }
@@ -775,7 +776,8 @@ static struct dentry *ceph_lookup(struct__ceph_touch_fmode (ci, mdsc,CEPH_FILE_MODE_RD );spin_unlock (&ci->i_ceph_lock);dout (" dir %p complete, -ENOENT\n" , dir); -d_add (dentry,NULL ); +if (d_unhashed (dentry)) +d_add (dentry,NULL ); di->lease_shared_gen =atomic_read (&ci->i_shared_gen);return NULL ; }
Greg Kroah-Hartman
maintainerPATCH 249/342 ALSA: aloop: Fix peer runtime UAF during format-change stop
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cássio Gabriel <[email protected]>
[ Upstream commit e5c33cdc6f402eab8abd36ecf436b22c9d3a8aff ]
loopback_check_format() may stop the capture side when playback starts with parameters that no longer match a running capture stream. Commit 826af7fa62e3 ("ALSA: aloop: Fix racy access at PCM trigger") moved the peer lookup under cable->lock, but the actual snd_pcm_stop() still runs after dropping that lock.
A concurrent close can clear the capture entry from cable->streams[] and detach or free its runtime while the playback trigger path still holds a stale peer substream pointer.
Keep a per-cable count of in-flight peer stops before dropping cable->lock, and make free_cable() wait for those stops before detaching the runtime. This preserves the existing behavior while making the peer runtime lifetime explicit.
--- sound/drivers/aloop.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-)
sound/drivers/aloop.c +27 -13
@@ -99,6 +99,9 @@ struct loopback_ops {struct loopback_cable {spinlock_t lock;struct loopback_pcm *streams[2 ]; +/* in-flight peer stops running outside cable->lock */ +atomic_t stop_count; +wait_queue_head_t stop_wait;struct snd_pcm_hardware hw;/* flags */ unsigned int valid;
@@ -342,8 +345,12 @@ static int loopback_check_format(structif (stream ==SNDRV_PCM_STREAM_CAPTURE ) {return -EIO ; }else { +/* close must not free the peer runtime below */ +atomic_inc (&cable->stop_count);snd_pcm_stop (cable->streams[SNDRV_PCM_STREAM_CAPTURE ]-> substream,SNDRV_PCM_STATE_DRAINING ); +if (atomic_dec_and_test (&cable->stop_count)) +wake_up (&cable->stop_wait); __notify: runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK ]-> substream->runtime;
@@ -995,24 +1002,29 @@ static void free_cable(struct snd_pcm_sustruct loopback *loopback = substream->private_data;int dev =get_cable_index (substream);struct loopback_cable *cable; +struct loopback_pcm *dpcm; +bool other_alive; cable = loopback->cables[substream->number][dev];if (!cable)return ; -if (cable->streams[!substream->stream]) { -/* other stream is still alive */ -spin_lock_irq (&cable->lock); - cable->streams[substream->stream] =NULL ; -spin_unlock_irq (&cable->lock); - }else { -struct loopback_pcm *dpcm = substream->runtime->private_data; -if (cable->ops && cable->ops->close_cable && dpcm) - cable->ops->close_cable (dpcm); -/* free the cable */ - loopback->cables[substream->number][dev] =NULL ; -kfree (cable); - } +spin_lock_irq (&cable->lock); + cable->streams[substream->stream] =NULL ; + other_alive = cable->streams[!substream->stream] !=NULL ; +spin_unlock_irq (&cable->lock); + +/* Pair with the stop_count increment in loopback_check_format(). */ +wait_event (cable->stop_wait, !atomic_read (&cable->stop_count)); +if (other_alive) +return ; + + dpcm = substream->runtime->private_data; +if (cable->ops && cable->ops->close_cable && dpcm) + cable->ops->close_cable (dpcm); +/* free the cable */ + loopback->cables[substream->number][dev] =NULL ; +kfree (cable); }static int loopback_jiffies_timer_open (struct loopback_pcm *dpcm)
@@ -1207,6 +1219,8 @@ static int loopback_open(struct snd_pcm_ goto unlock; }spin_lock_init (&cable->lock); +atomic_set (&cable->stop_count,0 ); +init_waitqueue_head (&cable->stop_wait); cable->hw = loopback_pcm_hardware;if (loopback->timer_source) cable->ops = &loopback_snd_timer_ops;
Greg Kroah-Hartman
maintainerPATCH 250/342 printk: add print_hex_dump_devel()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit d134feeb5df33fbf77f482f52a366a44642dba09 ]
Add print_hex_dump_devel() as the hex dump equivalent of pr_devel(), which emits output only when DEBUG is enabled, but keeps call sites compiled otherwise.
Stable-dep-of: 177730a273b1 ("crypto: caam - guard HMAC key hex dumps in hash_digest_key")
--- include/linux/printk.h | 13 +++++++++++++ 1 file changed, 13 insertions(+)
include/linux/printk.h +13 -0
@@ -608,6 +608,19 @@ static inline void print_hex_dump_debug( }#endif +#if defined(DEBUG ) +#define print_hex_dump_devel (prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) +#else +static inline void print_hex_dump_devel (const char *prefix_str,int prefix_type, +int rowsize,int groupsize, +const void *buf,size_t len,bool ascii) +{ +} +#endif +/** * print_hex_dump_bytes - shorthand form of print_hex_dump_debug() with default * params
Greg Kroah-Hartman
maintainerPATCH 251/342 crypto: caam - guard HMAC key hex dumps in hash_digest_key
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit 177730a273b18e195263ed953853273e901b5064 ]
Use print_hex_dump_devel() for dumping sensitive HMAC key bytes in hash_digest_key() to avoid leaking secrets at runtime when CONFIG_DYNAMIC_DEBUG is enabled.
--- drivers/crypto/caam/caamalg_qi2.c | 4 ++-- drivers/crypto/caam/caamhash.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
drivers/crypto/caam/caamalg_qi2.c +2 -2
@@ -3261,7 +3261,7 @@ static int hash_digest_key(struct caam_hdpaa2_fl_set_addr (out_fle, key_dma);dpaa2_fl_set_len (out_fle, digestsize); - print_hex_dump_debug("key_in@" __stringify(__LINE__)": ", + print_hex_dump_devel("key_in@" __stringify(__LINE__)": ",DUMP_PREFIX_ADDRESS ,16 ,4 , key, *keylen,1 ); print_hex_dump_debug("shdesc@" __stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc),
@@ -3281,7 +3281,7 @@ static int hash_digest_key(struct caam_h/* in progress */ wait_for_completion (&result.completion); ret = result.err; - print_hex_dump_debug("digested key@" __stringify(__LINE__)": ", + print_hex_dump_devel("digested key@" __stringify(__LINE__)": ",DUMP_PREFIX_ADDRESS ,16 ,4 , key, digestsize,1 ); }
drivers/crypto/caam/caamhash.c +2 -2
@@ -390,7 +390,7 @@ static int hash_digest_key(struct caam_happend_seq_store (desc, digestsize,LDST_CLASS_2_CCB |LDST_SRCDST_BYTE_CONTEXT ); - print_hex_dump_debug("key_in@"__stringify(__LINE__)": ", + print_hex_dump_devel("key_in@"__stringify(__LINE__)": ",DUMP_PREFIX_ADDRESS ,16 ,4 , key, *keylen,1 ); print_hex_dump_debug("jobdesc@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc),
@@ -405,7 +405,7 @@ static int hash_digest_key(struct caam_hwait_for_completion (&result.completion); ret = result.err; - print_hex_dump_debug("digested key@"__stringify(__LINE__)": ", + print_hex_dump_devel("digested key@"__stringify(__LINE__)": ",DUMP_PREFIX_ADDRESS ,16 ,4 , key, digestsize,1 ); }
Greg Kroah-Hartman
maintainerPATCH 252/342 ACPI: scan: Use acpi_dev_put() in object add error paths
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
[ Upstream commit 9c0acc169ac71535477caedea8315f7041c5f07c ]
After acpi_init_device_object(), the lifetime of struct acpi_device is managed by the driver core through reference counting.
Both acpi_add_power_resource() and acpi_add_single_object() call acpi_init_device_object() and then invoke acpi_device_add(). If that fails, their error paths call the release callback directly instead of dropping the device reference through acpi_dev_put().
This bypasses the normal device lifetime rules and frees the object without releasing the reference acquired by device_initialize(), which may lead to a refcount leak.
The issue was identified by a static analysis tool I developed and confirmed by manual review.
Fix both error paths by using acpi_dev_put() and let the release callback handle the final cleanup.
--- drivers/acpi/power.c | 2 +- drivers/acpi/scan.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/acpi/power.c +1 -1
@@ -977,7 +977,7 @@ int acpi_add_power_resource(acpi_handlereturn 0 ; err: -acpi_release_power_resource (&device->dev); +acpi_dev_put (device);return result; }
drivers/acpi/scan.c +1 -1
@@ -1679,7 +1679,7 @@ static int acpi_add_single_object(struct result =acpi_device_add (device, acpi_device_release);if (result) { -acpi_device_release (&device->dev); +acpi_dev_put (device);return result; }
Greg Kroah-Hartman
maintainerPATCH 253/342 tracepoint: balance regfunc() on func_add() failure in tracepoint_add_func()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <[email protected]>
[ Upstream commit fad217e16fded7f3c09f8637b0f6a224d58b5f2e ]
When a tracepoint goes through the 0 -> 1 transition, tracepoint_add_func() invokes the subsystem's ext->regfunc() before attempting to install the new probe via func_add(). If func_add() then fails (for example, when allocate_probes() cannot allocate a new probe array under memory pressure and returns -ENOMEM), the function returns the error without calling the matching ext->unregfunc(), leaving the side effects of regfunc() behind with no installed probe to justify them.
For syscall tracepoints this is particularly unpleasant: syscall_regfunc() bumps sys_tracepoint_refcount and sets SYSCALL_TRACEPOINT on every task. After a leaked failure, the refcount is stuck at a non-zero value with no consumer, and every task continues paying the syscall trace entry/exit overhead until reboot. Other subsystems providing regfunc()/unregfunc() pairs exhibit similarly scoped persistent state.
Mirror the existing 1 -> 0 cleanup and call ext->unregfunc() in the func_add() error path, gated on the same condition used there so the unwind is symmetric with the registration.
--- kernel/tracepoint.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/tracepoint.c +2 -0
@@ -358,6 +358,8 @@ static int tracepoint_add_func(struct tr lockdep_is_held(&tracepoints_mutex)); old =func_add (&tp_funcs, func, prio);if (IS_ERR (old)) { +if (tp->unregfunc && !static_key_enabled (&tp->key)) + tp->unregfunc ();WARN_ON_ONCE (warn &&PTR_ERR (old) != -ENOMEM );return PTR_ERR (old); }
Greg Kroah-Hartman
maintainerPATCH 254/342 wifi: brcmfmac: Fix potential use-after-free issue when stopping watchdog task
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marek Szyprowski <[email protected]>
[ Upstream commit c623b63580880cc742255eaed3d79804c1b91143 ]
Watchdog task might end between send_sig() and kthread_stop() calls, what results in the use-after-free issue. Fix this by increasing watchdog task reference count before calling send_sig() and dropping it by switching to kthread_stop_put().
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +4 -0
@@ -2464,8 +2464,10 @@ static void brcmf_sdio_bus_stop(struct dbrcmf_dbg (TRACE ,"Enter\n" );if (bus->watchdog_tsk) { +get_task_struct (bus->watchdog_tsk);send_sig (SIGTERM , bus->watchdog_tsk,1 );kthread_stop (bus->watchdog_tsk); +put_task_struct (bus->watchdog_tsk); bus->watchdog_tsk =NULL ; }
@@ -4536,8 +4538,10 @@ void brcmf_sdio_remove(struct brcmf_sdioif (bus) {/* Stop watchdog task */ if (bus->watchdog_tsk) { +get_task_struct (bus->watchdog_tsk);send_sig (SIGTERM , bus->watchdog_tsk,1 );kthread_stop (bus->watchdog_tsk); +put_task_struct (bus->watchdog_tsk); bus->watchdog_tsk =NULL ; }
Greg Kroah-Hartman
maintainerPATCH 255/342 usb: dwc3: Move GUID programming after PHY initialization
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvarasu Ganesan <[email protected]>
[ Upstream commit aad35f9c926ec220b0742af1ada45666ae667956 ]
The Linux Version Code is currently written to the GUID register before PHY initialization. Certain PHY implementations (such as Synopsys eUSB PHY performing link_sw_reset) clear the GUID register to its default value during initialization, causing the kernel version information to be lost.
Move the GUID register programming to occur after PHY initialization completes to ensure the Linux version information persists.
--- drivers/usb/dwc3/core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/usb/dwc3/core.c +6 -6
@@ -978,12 +978,6 @@ static int dwc3_core_init(struct dwc3 *d hw_mode =DWC3_GHWPARAMS0_MODE (dwc->hwparams.hwparams0); -/* -* Write Linux Version Code to our GUID register so it's easy to figure -* out which kernel version a bug was found. -*/ -dwc3_writel (dwc->regs,DWC3_GUID ,LINUX_VERSION_CODE ); - ret =dwc3_phy_setup (dwc);if (ret) goto err0;
@@ -1023,6 +1017,12 @@ static int dwc3_core_init(struct dwc3 *dif (ret) goto err1; +/* +* Write Linux Version Code to our GUID register so it's easy to figure +* out which kernel version a bug was found. +*/ +dwc3_writel (dwc->regs,DWC3_GUID ,LINUX_VERSION_CODE ); +dwc3_core_setup_global_control (dwc);dwc3_core_num_eps (dwc);
Greg Kroah-Hartman
maintainerPATCH 256/342 spi: syncuacer: fix controller deregistration
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 75d849c3452e9611de031db45b3149ba9a99035f ]
Make sure to deregister the controller before disabling underlying resources like clocks during driver unbind.
--- drivers/spi/spi-synquacer.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/spi/spi-synquacer.c +7 -1
@@ -719,7 +719,7 @@ static int synquacer_spi_probe(struct plpm_runtime_set_active (sspi->dev);pm_runtime_enable (sspi->dev); - ret =devm_spi_register_master (sspi->dev, master); + ret =spi_register_master (master);if (ret) goto disable_pm;
@@ -740,10 +740,16 @@ static int synquacer_spi_remove(struct pstruct spi_master *master =platform_get_drvdata (pdev);struct synquacer_spi *sspi =spi_master_get_devdata (master); +spi_master_get (master); + +spi_unregister_master (master); +pm_runtime_disable (sspi->dev);clk_disable_unprepare (sspi->clk); +spi_master_put (master); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 257/342 spi: sun4i: fix controller deregistration
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 42108a2f03e0fdeabe9d02d085bdb058baa1189f ]
Make sure to deregister the controller before disabling underlying resources like clocks during driver unbind.
--- drivers/spi/spi-sun4i.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/spi/spi-sun4i.c +9 -1
@@ -503,7 +503,7 @@ static int sun4i_spi_probe(struct platfopm_runtime_enable (&pdev->dev);pm_runtime_idle (&pdev->dev); - ret =devm_spi_register_master (&pdev->dev, master); + ret =spi_register_master (master);if (ret) {dev_err (&pdev->dev,"cannot register SPI master\n" ); goto err_pm_disable;
@@ -521,8 +521,16 @@ err_free_master:static int sun4i_spi_remove (struct platform_device *pdev) { +struct spi_master *master =platform_get_drvdata (pdev); + +spi_master_get (master); + +spi_unregister_master (master); +pm_runtime_force_suspend (&pdev->dev); +spi_master_put (master); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 258/342 spi: ti-qspi: fix controller deregistration
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 0c18a1bacbb1d8b8aa34d3d004a2cb8226c8b1ea ]
Make sure to deregister the controller before disabling underlying resources like clocks during driver unbind.
Note that the controller is suspended before disabling and releasing resources since commit 3ac066e2227c ("spi: spi-ti-qspi: Suspend the queue before removing the device") which avoids issues like unclocked accesses but prevents SPI device drivers from doing I/O during deregistration.
--- drivers/spi/spi-ti-qspi.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
drivers/spi/spi-ti-qspi.c +6 -5
@@ -895,7 +895,7 @@ no_dma: qspi->mmap_enabled = false; qspi->current_cs =-1 ; - ret =devm_spi_register_master (&pdev->dev, master); + ret =spi_register_master (master);if (!ret)return 0 ;
@@ -910,17 +910,18 @@ free_master:static int ti_qspi_remove (struct platform_device *pdev) {struct ti_qspi *qspi =platform_get_drvdata (pdev); -int rc; - rc =spi_master_suspend (qspi->master); -if (rc) -return rc; +spi_master_get (qspi->master); + +spi_unregister_master (qspi->master);pm_runtime_put_sync (&pdev->dev);pm_runtime_disable (&pdev->dev);ti_qspi_dma_cleanup (qspi); +spi_master_put (qspi->master); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 259/342 spi: zynq-qspi: fix controller deregistration
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit c9c012706c9fa8ca6d129a9161caf92ab625a3fd ]
Make sure to deregister the controller before disabling it during driver unbind.
Note that clocks were also disabled before the recent commit 1f8fd9490e31 ("spi: zynq-qspi: Simplify clock handling with devm_clk_get_enabled()").
--- drivers/spi/spi-zynq-qspi.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
drivers/spi/spi-zynq-qspi.c +11 -4
@@ -652,7 +652,7 @@ static int zynq_qspi_probe(struct platfo xqspi =spi_controller_get_devdata (ctlr); xqspi->dev = dev; -platform_set_drvdata (pdev, xqspi); +platform_set_drvdata (pdev, ctlr); xqspi->regs =devm_platform_ioremap_resource (pdev,0 );if (IS_ERR (xqspi->regs)) { ret =PTR_ERR (xqspi->regs);
@@ -722,9 +722,9 @@ static int zynq_qspi_probe(struct platfo/* QSPI controller initializations */ zynq_qspi_init_hw (xqspi, ctlr->num_chipselect); - ret =devm_spi_register_controller (&pdev->dev, ctlr); + ret =spi_register_controller (ctlr);if (ret) { -dev_err (&pdev->dev,"spi_register_master failed\n" ); +dev_err (&pdev->dev,"failed to register controller\n" ); goto clk_dis_all; }
@@ -752,13 +752,20 @@ remove_master:*/ static int zynq_qspi_remove (struct platform_device *pdev) { -struct zynq_qspi *xqspi =platform_get_drvdata (pdev); +struct spi_controller *ctlr =platform_get_drvdata (pdev); +struct zynq_qspi *xqspi =spi_controller_get_devdata (ctlr); + +spi_controller_get (ctlr); + +spi_unregister_controller (ctlr);zynq_qspi_write (xqspi,ZYNQ_QSPI_ENABLE_OFFSET ,0 );clk_disable_unprepare (xqspi->refclk);clk_disable_unprepare (xqspi->pclk); +spi_controller_put (ctlr); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 260/342 spi: sun6i: fix controller deregistration
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit d874a1c33aee0d88fb4ba2f8aeadaa9f1965209a ]
Make sure to deregister the controller before disabling underlying resources like clocks during driver unbind.
--- drivers/spi/spi-sun6i.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/spi/spi-sun6i.c +9 -1
@@ -512,7 +512,7 @@ static int sun6i_spi_probe(struct platfopm_runtime_enable (&pdev->dev);pm_runtime_idle (&pdev->dev); - ret =devm_spi_register_master (&pdev->dev, master); + ret =spi_register_master (master);if (ret) {dev_err (&pdev->dev,"cannot register SPI master\n" ); goto err_pm_disable;
@@ -530,8 +530,16 @@ err_free_master:static int sun6i_spi_remove (struct platform_device *pdev) { +struct spi_master *master =platform_get_drvdata (pdev); + +spi_master_get (master); + +spi_unregister_master (master); +pm_runtime_force_suspend (&pdev->dev); +spi_master_put (master); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 261/342 spi: tegra20-sflash: fix controller deregistration
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit ad7310e983327f939dd6c4e801eab13238992572 ]
Make sure to deregister the controller before disabling underlying resources like clocks during driver unbind.
--- drivers/spi/spi-tegra20-sflash.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/spi/spi-tegra20-sflash.c +7 -1
@@ -508,7 +508,7 @@ static int tegra_sflash_probe(struct plapm_runtime_put (&pdev->dev); master->dev.of_node = pdev->dev.of_node; - ret =devm_spi_register_master (&pdev->dev, master); + ret =spi_register_master (master);if (ret <0 ) {dev_err (&pdev->dev,"can not register to master err %d\n" , ret); goto exit_pm_disable;
@@ -531,12 +531,18 @@ static int tegra_sflash_remove(struct plstruct spi_master *master =platform_get_drvdata (pdev);struct tegra_sflash_data *tsd =spi_master_get_devdata (master); +spi_master_get (master); + +spi_unregister_master (master); +free_irq (tsd->irq, tsd);pm_runtime_disable (&pdev->dev);if (!pm_runtime_status_suspended (&pdev->dev))tegra_sflash_runtime_suspend (&pdev->dev); +spi_master_put (master); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 262/342 spi: tegra114: fix controller deregistration
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 9c9c27ff2058142d8f800de3186d6864184958de ]
Make sure to deregister the controller before disabling underlying resources like clocks during driver unbind.
--- drivers/spi/spi-tegra114.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/spi/spi-tegra114.c +7 -1
@@ -1422,7 +1422,7 @@ static int tegra_spi_probe(struct platfo } master->dev.of_node = pdev->dev.of_node; - ret =devm_spi_register_master (&pdev->dev, master); + ret =spi_register_master (master);if (ret <0 ) {dev_err (&pdev->dev,"can not register to master err %d\n" , ret); goto exit_free_irq;
@@ -1448,6 +1448,10 @@ static int tegra_spi_remove(struct platfstruct spi_master *master =platform_get_drvdata (pdev);struct tegra_spi_data *tspi =spi_master_get_devdata (master); +spi_master_get (master); + +spi_unregister_master (master); +free_irq (tspi->irq, tspi);if (tspi->tx_dma_chan)
@@ -1460,6 +1464,8 @@ static int tegra_spi_remove(struct platfif (!pm_runtime_status_suspended (&pdev->dev))tegra_spi_runtime_suspend (&pdev->dev); +spi_master_put (master); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 263/342 spi: uniphier: fix controller deregistration
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 0245435f777264ac45945ed2f325dd095a41d1af ]
Make sure to deregister the controller before releasing underlying resources like DMA during driver unbind.
Note that clocks were also disabled before the recent commit fdca270f8f87 ("spi: uniphier: Simplify clock handling with devm_clk_get_enabled()").
--- drivers/spi/spi-uniphier.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/spi/spi-uniphier.c +7 -1
@@ -751,7 +751,7 @@ static int uniphier_spi_probe(struct pla master->max_dma_len =min (dma_tx_burst, dma_rx_burst); - ret =devm_spi_register_master (&pdev->dev, master); + ret =spi_register_master (master);if (ret) goto out_release_dma;
@@ -780,6 +780,10 @@ static int uniphier_spi_remove(struct plstruct spi_master *master =platform_get_drvdata (pdev);struct uniphier_spi_priv *priv =spi_master_get_devdata (master); +spi_master_get (master); + +spi_unregister_master (master); +if (master->dma_tx)dma_release_channel (master->dma_tx);if (master->dma_rx)
@@ -787,6 +791,8 @@ static int uniphier_spi_remove(struct plclk_disable_unprepare (priv->clk); +spi_master_put (master); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 264/342 mm/hugetlb_cma: round up per_node before logging it
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sang-Heon Jeon <[email protected]>
[ Upstream commit 8f5ce56b76303c55b78a87af996e2e0f8535f979 ]
When the user requests a total hugetlb CMA size without per-node specification, hugetlb_cma_reserve() computes per_node from hugetlb_cma_size and the number of nodes that have memory
per_node = DIV_ROUND_UP(hugetlb_cma_size,
nodes_weight(hugetlb_bootmem_nodes));
The reservation loop later computes
size = round_up(min(per_node, hugetlb_cma_size - reserved),
PAGE_SIZE << order);
So the actually reserved per_node size is multiple of (PAGE_SIZE << order), but the logged per_node is not rounded up, so it may be smaller than the actual reserved size.
For example, as the existing comment describes, if a 3 GB area is requested on a machine with 4 NUMA nodes that have memory, 1 GB is allocated on the first three nodes, but the printed log is
hugetlb_cma: reserve 3072 MiB, up to 768 MiB per node
Round per_node up to (PAGE_SIZE << order) before logging so that the printed log always matches the actual reserved size. No functional change to the actual reservation size, as the following case analysis shows
1. remaining (hugetlb_cma_size - reserved) >= rounded per_node
- AS-IS: min() picks unrounded per_node;
round_up() returns rounded per_node
- TO-BE: min() picks rounded per_node;
round_up() returns rounded per_node (no-op)
2. remaining < unrounded per_node
- AS-IS: min() picks remaining;
round_up() returns round_up(remaining)
- TO-BE: min() picks remaining;
round_up() returns round_up(remaining)
3. unrounded per_node <= remaining < rounded per_node
- AS-IS: min() picks unrounded per_node;
round_up() returns rounded per_node
- TO-BE: min() picks remaining;
round_up() returns round_up(remaining) equals rounded per_node
--- mm/hugetlb.c | 1 + 1 file changed, 1 insertion(+)
mm/hugetlb.c +1 -0
@@ -5842,6 +5842,7 @@ void __init hugetlb_cma_reserve(int orde* let's allocate 1 GB on first three nodes and ignore the last one. */ per_node =DIV_ROUND_UP (hugetlb_cma_size, nr_online_nodes); + per_node =round_up (per_node,PAGE_SIZE << order);pr_info ("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n" , hugetlb_cma_size /SZ_1M , per_node /SZ_1M );
Greg Kroah-Hartman
maintainerPATCH 265/342 fbcon: Avoid OOB font access if console rotation fails
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit e4ef723d8975a2694cc90733a6b888a5e2841842 ]
Clear the font buffer if the reallocation during console rotation fails in fbcon_rotate_font(). The putcs implementations for the rotated buffer will return early in this case. See [1] for an example.
Currently, fbcon_rotate_font() keeps the old buffer, which is too small for the rotated font. Printing to the rotated console with a high-enough character code will overflow the font buffer.
v2:
- fix typos in commit message
--- drivers/video/fbdev/core/fbcon_rotate.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/video/fbdev/core/fbcon_rotate.c +4 -1
@@ -46,6 +46,10 @@ static int fbcon_rotate_font(struct fb_i info->fbops->fb_sync (info);if (ops->fd_size < d_cellsize * len) { +kfree (ops->fontbuffer); + ops->fontbuffer =NULL ; + ops->fd_size =0 ; + dst =kmalloc_array (len, d_cellsize,GFP_KERNEL );if (dst ==NULL ) {
@@ -54,7 +58,6 @@ static int fbcon_rotate_font(struct fb_i } ops->fd_size = d_cellsize * len; -kfree (ops->fontbuffer); ops->fontbuffer = dst; }
Greg Kroah-Hartman
maintainerPATCH 266/342 spi: topcliff-pch: fix controller deregistration
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 5d6f477d6fc0767c57c5e1e6f55a1662820eef87 ]
Make sure to deregister the controller before disabling and releasing underlying resources like interrupts and DMA during driver unbind.
--- drivers/spi/spi-topcliff-pch.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/spi/spi-topcliff-pch.c +6 -1
@@ -1449,11 +1449,16 @@ static int pch_spi_pd_remove(struct platfree_irq (board_dat->pdev->irq, data); } +spi_controller_get (data->master); + +spi_unregister_controller (data->master); +if (use_dma)pch_free_dma_buf (board_dat, data);pci_iounmap (board_dat->pdev, data->io_remap_addr); -spi_unregister_master (data->master); + +spi_controller_put (data->master);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 267/342 btrfs: fix btrfs_ioctl_space_info() slot_count TOCTOU which can lead to info-leak
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yochai Eisenrich <[email protected]>
[ Upstream commit 973e57c726c1f8e77259d1c8e519519f1e9aea77 ]
btrfs_ioctl_space_info() has a TOCTOU race between two passes over the block group RAID type lists. The first pass counts entries to determine the allocation size, then the second pass fills the buffer. The groups_sem rwlock is released between passes, allowing concurrent block group removal to reduce the entry count.
When the second pass fills fewer entries than the first pass counted, copy_to_user() copies the full alloc_size bytes including trailing uninitialized kmalloc bytes to userspace.
Fix by copying only total_spaces entries (the actually-filled count from the second pass) instead of alloc_size bytes, and switch to kzalloc so any future copy size mismatch cannot leak heap data.
--- fs/btrfs/ioctl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
fs/btrfs/ioctl.c +3 -2
@@ -3587,7 +3587,7 @@ static long btrfs_ioctl_space_info(strucreturn -ENOMEM ; space_args.total_spaces =0 ; - dest =kmalloc (alloc_size,GFP_KERNEL ); + dest =kzalloc (alloc_size,GFP_KERNEL );if (!dest)return -ENOMEM ; dest_orig = dest;
@@ -3643,7 +3643,8 @@ static long btrfs_ioctl_space_info(struc user_dest = (struct btrfs_ioctl_space_info __user *) (arg +sizeof (struct btrfs_ioctl_space_args )); -if (copy_to_user (user_dest, dest_orig, alloc_size)) +if (copy_to_user (user_dest, dest_orig, + space_args.total_spaces *sizeof (*dest_orig))) ret = -EFAULT ;kfree (dest_orig);
Greg Kroah-Hartman
maintainerPATCH 268/342 tracing/probes: Limit size of event probe to 3K
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
[ Upstream commit b2aa3b4d64e460ac606f386c24e7d8a873ce6f1a ]
There currently isn't a max limit an event probe can be. One could make an event greater than PAGE_SIZE, which makes the event useless because if it's bigger than the max event that can be recorded into the ring buffer, then it will never be recorded.
A event probe should never need to be greater than 3K, so make that the max size. As long as the max is less than the max that can be recorded onto the ring buffer, it should be fine.
--- kernel/trace/trace_probe.c | 5 +++++ kernel/trace/trace_probe.h | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-)
kernel/trace/trace_probe.c +5 -0
@@ -610,6 +610,11 @@ static int traceprobe_parse_probe_arg_bo parg->offset = *size; *size += parg->type->size * (parg->count ?:1 ); +if (*size >MAX_PROBE_EVENT_SIZE ) { +trace_probe_log_err (offset,EVENT_TOO_BIG ); +return -E2BIG ; + } +if (parg->count) { len =strlen (parg->type->fmttype) +6 ; parg->fmt =kmalloc (len,GFP_KERNEL );
kernel/trace/trace_probe.h +3 -1
@@ -33,6 +33,7 @@#define MAX_ARRAY_LEN 64#define MAX_ARG_NAME_LEN 32#define MAX_STRING_SIZE PATH_MAX +#define MAX_PROBE_EVENT_SIZE 3072/* Reserved field names */ #define FIELD_STRING_IP "__probe_ip"
@@ -439,7 +440,8 @@ extern int traceprobe_define_arg_fields(C (FAIL_REG_PROBE ,"Failed to register probe event" ),\C (DIFF_PROBE_TYPE ,"Probe type is different from existing probe" ),\C (DIFF_ARG_TYPE ,"Argument type or name is different from existing probe" ),\ -C (SAME_PROBE ,"There is already the exact same probe event" ), +C (SAME_PROBE ,"There is already the exact same probe event" ),\ +C (EVENT_TOO_BIG ,"Event too big (too many fields?)" ), #undef C #define C(a, b) TP_ERR_##a
Greg Kroah-Hartman
maintainerPATCH 269/342 pmdomain: core: Fix detach procedure for virtual devices in genpd
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ulf Hansson <[email protected]>
[ Upstream commit 26735dfdd8930d9ef1fa92e590a9bf77726efdf6 ]
If a device is attached to a PM domain through genpd_dev_pm_attach_by_id(), genpd calls pm_runtime_enable() for the corresponding virtual device that it registers. While this avoids boilerplate code in drivers, there is no corresponding call to pm_runtime_disable() in genpd_dev_pm_detach().
This means these virtual devices are typically detached from its genpd, while runtime PM remains enabled for them, which is not how things are designed to work. In worst cases it may lead to critical errors, like a NULL pointer dereference bug in genpd_runtime_suspend(), which was recently reported. For another case, we may end up keeping an unnecessary vote for a performance state for the device.
To fix these problems, let's add this missing call to pm_runtime_disable() in genpd_dev_pm_detach().
--- drivers/base/power/domain.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/base/power/domain.c +9 -1
@@ -2525,6 +2525,7 @@ static struct bus_type genpd_bus_type =static void genpd_dev_pm_detach (struct device *dev,bool power_off) {struct generic_pm_domain *pd; +bool is_virt_dev;unsigned int i;int ret =0 ;
@@ -2534,6 +2535,13 @@ static void genpd_dev_pm_detach(struct ddev_dbg (dev,"removing from PM domain %s\n" , pd->name); +/* Check if the device was created by genpd at attach. */ + is_virt_dev = dev->bus == &genpd_bus_type; + +/* Disable runtime PM if we enabled it at attach. */ +if (is_virt_dev) +pm_runtime_disable (dev); +for (i =1 ; i <GENPD_RETRY_MAX_MS ; i <<=1 ) { ret =genpd_remove_device (pd, dev);if (ret != -EAGAIN )
@@ -2553,7 +2561,7 @@ static void genpd_dev_pm_detach(struct dgenpd_queue_power_off_work (pd);/* Unregister the device if it was created by genpd. */ -if (dev->bus == &genpd_bus_type) +if (is_virt_dev)device_unregister (dev); }
Greg Kroah-Hartman
maintainerPATCH 270/342 dm btree: improve btree residency
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joe Thornber <[email protected]>
[ Upstream commit 4eafdb1515a708d97e4659bd488ddac19f274c4f ]
This commit improves the residency of btrees built in the metadata for dm-thin and dm-cache.
When inserting a new entry into a full btree node the current code splits the node into two. This can result in very many half full nodes, particularly if the insertions are occurring in an ascending order (as happens in dm-thin with large writes).
With this commit, when we insert into a full node we first try and move some entries to a neighbouring node that has space, failing that it tries to split two neighbouring nodes into three.
Results are given below. 'Residency' is how full nodes are on average as a percentage. Average instruction counts for the operations are given to show the extra processing has little overhead.
+--------------------------+--------------------------+
| Before | After |
+------------+-----------+-----------+--------------+-----------+--------------+
| Test | Phase | Residency | Instructions | Residency | Instructions |
+------------+-----------+-----------+--------------+-----------+--------------+
| Ascending | insert | 50 | 1876 | 96 | 1930 |
| | overwrite | 50 | 1789 | 96 | 1746 |
| | lookup | 50 | 778 | 96 | 778 |
| Descending | insert | 50 | 3024 | 96 | 3181 |
| | overwrite | 50 | 1789 | 96 | 1746 |
| | lookup | 50 | 778 | 96 | 778 |
| Random | insert | 68 | 3800 | 84 | 3736 |
| | overwrite | 68 | 4254 | 84 | 3911 |
| | lookup | 68 | 779 | 84 | 779 |
| Runs | insert | 63 | 2546 | 82 | 2815 |
| | overwrite | 63 | 2013 | 82 | 1986 |
| | lookup | 63 | 778 | 82 | 779 |
+------------+-----------+-----------+--------------+-----------+--------------+ Ascending - keys are inserted in ascending order. Descending - keys are inserted in descending order. Random - keys are inserted in random order.
Runs - keys are split into ascending runs of ~20 length. Then
the runs are shuffled.
Stable-dep-of: 09a65adc7d8b ("dm-thin: fix metadata refcount underflow")
--- drivers/md/persistent-data/dm-btree.c | 451 ++++++++++++++++++-- drivers/md/persistent-data/dm-transaction-manager.c | 9 drivers/md/persistent-data/dm-transaction-manager.h | 10 3 files changed, 439 insertions(+), 31 deletions(-)
drivers/md/persistent-data/dm-btree.c +422 -29
@@ -502,6 +502,122 @@ out:EXPORT_SYMBOL_GPL (dm_btree_lookup_next); +/*----------------------------------------------------------------*/ + +/* +* Copies entries from one region of a btree node to another. The regions +* must not overlap. +*/ +static void copy_entries (struct btree_node *dest,unsigned dest_offset, +struct btree_node *src,unsigned src_offset, +unsigned count) +{ +size_t value_size =le32_to_cpu (dest->header.value_size); +memcpy (dest->keys + dest_offset, src->keys + src_offset, count *sizeof (uint64_t )); +memcpy (value_ptr (dest, dest_offset),value_ptr (src, src_offset), count * value_size); +} + +/* +* Moves entries from one region fo a btree node to another. The regions +* may overlap. +*/ +static void move_entries (struct btree_node *dest,unsigned dest_offset, +struct btree_node *src,unsigned src_offset, +unsigned count) +{ +size_t value_size =le32_to_cpu (dest->header.value_size); +memmove (dest->keys + dest_offset, src->keys + src_offset, count *sizeof (uint64_t )); +memmove (value_ptr (dest, dest_offset),value_ptr (src, src_offset), count * value_size); +} + +/* +* Erases the first 'count' entries of a btree node, shifting following +* entries down into their place. +*/ +static void shift_down (struct btree_node *n,unsigned count) +{ +move_entries (n,0 , n, count,le32_to_cpu (n->header.nr_entries) - count); +} + +/* +* Moves entries in a btree node up 'count' places, making space for +* new entries at the start of the node. +*/ +static void shift_up (struct btree_node *n,unsigned count) +{ +move_entries (n, count, n,0 ,le32_to_cpu (n->header.nr_entries)); +} + +/* +* Redistributes entries between two btree nodes to make them +* have similar numbers of entries. +*/ +static void redistribute2 (struct btree_node *left,struct btree_node *right) +{ +unsigned nr_left =le32_to_cpu (left->header.nr_entries); +unsigned nr_right =le32_to_cpu (right->header.nr_entries); +unsigned total = nr_left + nr_right; +unsigned target_left = total /2 ; +unsigned target_right = total - target_left; + +if (nr_left < target_left) { +unsigned delta = target_left - nr_left; +copy_entries (left, nr_left, right,0 , delta); +shift_down (right, delta); + }else if (nr_left > target_left) { +unsigned delta = nr_left - target_left; +if (nr_right) +shift_up (right, delta); +copy_entries (right,0 , left, target_left, delta); + } + + left->header.nr_entries =cpu_to_le32 (target_left); + right->header.nr_entries =cpu_to_le32 (target_right); +} + +/* +* Redistribute entries between three nodes. Assumes the central +* node is empty. +*/ +static void redistribute3 (struct btree_node *left,struct btree_node *center, +struct btree_node *right) +{ +unsigned nr_left =le32_to_cpu (left->header.nr_entries); +unsigned nr_center =le32_to_cpu (center->header.nr_entries); +unsigned nr_right =le32_to_cpu (right->header.nr_entries); +unsigned total, target_left, target_center, target_right; + +BUG_ON (nr_center); + + total = nr_left + nr_right; + target_left = total /3 ; + target_center = (total - target_left) /2 ; + target_right = (total - target_left - target_center); + +if (nr_left < target_left) { +unsigned left_short = target_left - nr_left; +copy_entries (left, nr_left, right,0 , left_short); +copy_entries (center,0 , right, left_short, target_center); +shift_down (right, nr_right - target_right); + + }else if (nr_left < (target_left + target_center)) { +unsigned left_to_center = nr_left - target_left; +copy_entries (center,0 , left, target_left, left_to_center); +copy_entries (center, left_to_center, right,0 , target_center - left_to_center); +shift_down (right, nr_right - target_right); + + }else { +unsigned right_short = target_right - nr_right; +shift_up (right, right_short); +copy_entries (right,0 , left, nr_left - right_short, right_short); +copy_entries (center,0 , left, target_left, nr_left - target_left); + } + + left->header.nr_entries =cpu_to_le32 (target_left); + center->header.nr_entries =cpu_to_le32 (target_center); + right->header.nr_entries =cpu_to_le32 (target_right); +} +/* * Splits a node by creating a sibling node and shifting half the nodes * contents across. Assumes there is a parent node, and it has room for
@@ -532,12 +648,10 @@ EXPORT_SYMBOL_GPL(dm_btree_lookup_next);* * Where A* is a shadow of A. */ -static int btree_split_sibling (struct shadow_spine *s,unsigned parent_index, -uint64_t key) +static int split_one_into_two (struct shadow_spine *s,unsigned parent_index, +struct dm_btree_value_type *vt,uint64_t key) {int r; -size_t size; -unsigned nr_left, nr_right;struct dm_block *left, *right, *parent;struct btree_node *ln, *rn, *pn;__le64 location;
@@ -551,36 +665,18 @@ static int btree_split_sibling(struct sh ln =dm_block_data (left); rn =dm_block_data (right); - nr_left =le32_to_cpu (ln->header.nr_entries) /2 ; - nr_right =le32_to_cpu (ln->header.nr_entries) - nr_left; - - ln->header.nr_entries =cpu_to_le32 (nr_left); - rn->header.flags = ln->header.flags; - rn->header.nr_entries =cpu_to_le32 (nr_right); + rn->header.nr_entries =cpu_to_le32 (0 ); rn->header.max_entries = ln->header.max_entries; rn->header.value_size = ln->header.value_size; -memcpy (rn->keys, ln->keys + nr_left, nr_right *sizeof (rn->keys[0 ])); - - size =le32_to_cpu (ln->header.flags) &INTERNAL_NODE ? -sizeof (uint64_t ) : s->info->value_type.size; -memcpy (value_ptr (rn,0 ),value_ptr (ln, nr_left), - size * nr_right); +redistribute2 (ln, rn); -/* -* Patch up the parent -*/ +/* patch up the parent */ parent =shadow_parent (s); - pn =dm_block_data (parent); - location =cpu_to_le64 (dm_block_location (left)); -__dm_bless_for_disk (&location); -memcpy_disk (value_ptr (pn, parent_index), - &location,sizeof (__le64)); location =cpu_to_le64 (dm_block_location (right));__dm_bless_for_disk (&location); - r =insert_at (sizeof (__le64), pn, parent_index +1 ,le64_to_cpu (rn->keys[0 ]), &location);if (r) {
@@ -588,6 +684,7 @@ static int btree_split_sibling(struct shreturn r; } +/* patch up the spine */ if (key <le64_to_cpu (rn->keys[0 ])) {unlock_block (s->info, right); s->nodes[1 ] = left;
@@ -600,6 +697,121 @@ static int btree_split_sibling(struct sh }/* +* We often need to modify a sibling node. This function shadows a particular +* child of the given parent node. Making sure to update the parent to point +* to the new shadow. +*/ +static int shadow_child (struct dm_btree_info *info,struct dm_btree_value_type *vt, +struct btree_node *parent,unsigned index, +struct dm_block **result) +{ +int r, inc; +dm_block_t root; +struct btree_node *node; + + root =value64 (parent, index); + + r =dm_tm_shadow_block (info->tm, root, &btree_node_validator, + result, &inc); +if (r) +return r; + + node =dm_block_data (*result); + +if (inc) +inc_children (info->tm, node, vt); + + *((__le64 *)value_ptr (parent, index)) = +cpu_to_le64 (dm_block_location (*result)); + +return 0 ; +} + +/* +* Splits two nodes into three. This is more work, but results in fuller +* nodes, so saves metadata space. +*/ +static int split_two_into_three (struct shadow_spine *s,unsigned parent_index, +struct dm_btree_value_type *vt,uint64_t key) +{ +int r; +unsigned middle_index; +struct dm_block *left, *middle, *right, *parent; +struct btree_node *ln, *rn, *mn, *pn; +__le64 location; + + parent =shadow_parent (s); + pn =dm_block_data (parent); + +if (parent_index ==0 ) { + middle_index =1 ; + left =shadow_current (s); + r =shadow_child (s->info, vt, pn, parent_index +1 , &right); +if (r) +return r; + }else { + middle_index = parent_index; + right =shadow_current (s); + r =shadow_child (s->info, vt, pn, parent_index -1 , &left); +if (r) +return r; + } + + r =new_block (s->info, &middle); +if (r <0 ) +return r; + + ln =dm_block_data (left); + mn =dm_block_data (middle); + rn =dm_block_data (right); + + mn->header.nr_entries =cpu_to_le32 (0 ); + mn->header.flags = ln->header.flags; + mn->header.max_entries = ln->header.max_entries; + mn->header.value_size = ln->header.value_size; + +redistribute3 (ln, mn, rn); + +/* patch up the parent */ + pn->keys[middle_index] = rn->keys[0 ]; + location =cpu_to_le64 (dm_block_location (middle)); +__dm_bless_for_disk (&location); + r =insert_at (sizeof (__le64), pn, middle_index, +le64_to_cpu (mn->keys[0 ]), &location); +if (r) { +if (shadow_current (s) != left) +unlock_block (s->info, left); + +unlock_block (s->info, middle); + +if (shadow_current (s) != right) +unlock_block (s->info, right); + +return r; + } + + +/* patch up the spine */ +if (key <le64_to_cpu (mn->keys[0 ])) { +unlock_block (s->info, middle); +unlock_block (s->info, right); + s->nodes[1 ] = left; + }else if (key <le64_to_cpu (rn->keys[0 ])) { +unlock_block (s->info, left); +unlock_block (s->info, right); + s->nodes[1 ] = middle; + }else { +unlock_block (s->info, left); +unlock_block (s->info, middle); + s->nodes[1 ] = right; + } + +return 0 ; +} + +/*----------------------------------------------------------------*/ + +/* * Splits a node by creating two new children beneath the given node. * * Before:
@@ -692,6 +904,186 @@ static int btree_split_beneath(struct shreturn 0 ; } +/*----------------------------------------------------------------*/ + +/* +* Redistributes a node's entries with its left sibling. +*/ +static int rebalance_left (struct shadow_spine *s,struct dm_btree_value_type *vt, +unsigned parent_index,uint64_t key) +{ +int r; +struct dm_block *sib; +struct btree_node *left, *right, *parent =dm_block_data (shadow_parent (s)); + + r =shadow_child (s->info, vt, parent, parent_index -1 , &sib); +if (r) +return r; + + left =dm_block_data (sib); + right =dm_block_data (shadow_current (s)); +redistribute2 (left, right); + *key_ptr (parent, parent_index) = right->keys[0 ]; + +if (key <le64_to_cpu (right->keys[0 ])) { +unlock_block (s->info, s->nodes[1 ]); + s->nodes[1 ] = sib; + }else { +unlock_block (s->info, sib); + } + +return 0 ; +} + +/* +* Redistributes a nodes entries with its right sibling. +*/ +static int rebalance_right (struct shadow_spine *s,struct dm_btree_value_type *vt, +unsigned parent_index,uint64_t key) +{ +int r; +struct dm_block *sib; +struct btree_node *left, *right, *parent =dm_block_data (shadow_parent (s)); + + r =shadow_child (s->info, vt, parent, parent_index +1 , &sib); +if (r) +return r; + + left =dm_block_data (shadow_current (s)); + right =dm_block_data (sib); +redistribute2 (left, right); + *key_ptr (parent, parent_index +1 ) = right->keys[0 ]; + +if (key <le64_to_cpu (right->keys[0 ])) { +unlock_block (s->info, sib); + }else { +unlock_block (s->info, s->nodes[1 ]); + s->nodes[1 ] = sib; + } + +return 0 ; +} + +/* +* Returns the number of spare entries in a node. +*/ +static int get_node_free_space (struct dm_btree_info *info,dm_block_t b,unsigned *space) +{ +int r; +unsigned nr_entries; +struct dm_block *block; +struct btree_node *node; + + r =bn_read_lock (info, b, &block); +if (r) +return r; + + node =dm_block_data (block); + nr_entries =le32_to_cpu (node->header.nr_entries); + *space =le32_to_cpu (node->header.max_entries) - nr_entries; + +unlock_block (info, block); +return 0 ; +} + +/* +* Make space in a node, either by moving some entries to a sibling, +* or creating a new sibling node. SPACE_THRESHOLD defines the minimum +* number of free entries that must be in the sibling to make the move +* worth while. If the siblings are shared (eg, part of a snapshot), +* then they are not touched, since this break sharing and so consume +* more space than we save. +*/ +#define SPACE_THRESHOLD 8 +static int rebalance_or_split (struct shadow_spine *s,struct dm_btree_value_type *vt, +unsigned parent_index,uint64_t key) +{ +int r; +struct btree_node *parent =dm_block_data (shadow_parent (s)); +unsigned nr_parent =le32_to_cpu (parent->header.nr_entries); +unsigned free_space; +int left_shared =0 , right_shared =0 ; + +/* Should we move entries to the left sibling? */ +if (parent_index >0 ) { +dm_block_t left_b =value64 (parent, parent_index -1 ); + r =dm_tm_block_is_shared (s->info->tm, left_b, &left_shared); +if (r) +return r; + +if (!left_shared) { + r =get_node_free_space (s->info, left_b, &free_space); +if (r) +return r; + +if (free_space >=SPACE_THRESHOLD ) +return rebalance_left (s, vt, parent_index, key); + } + } + +/* Should we move entries to the right sibling? */ +if (parent_index < (nr_parent -1 )) { +dm_block_t right_b =value64 (parent, parent_index +1 ); + r =dm_tm_block_is_shared (s->info->tm, right_b, &right_shared); +if (r) +return r; + +if (!right_shared) { + r =get_node_free_space (s->info, right_b, &free_space); +if (r) +return r; + +if (free_space >=SPACE_THRESHOLD ) +return rebalance_right (s, vt, parent_index, key); + } + } + +/* +* We need to split the node, normally we split two nodes +* into three. But when inserting a sequence that is either +* monotonically increasing or decreasing it's better to split +* a single node into two. +*/ +if (left_shared || right_shared || (nr_parent <=2 ) || + (parent_index ==0 ) || (parent_index +1 == nr_parent)) { +return split_one_into_two (s, parent_index, vt, key); + }else { +return split_two_into_three (s, parent_index, vt, key); + } +} + +/* +* Does the node contain a particular key? +*/ +static bool contains_key (struct btree_node *node,uint64_t key) +{ +int i =lower_bound (node, key); + +if (i >=0 &&le64_to_cpu (node->keys[i]) == key) +return true; + +return false; +} + +/* +* In general we preemptively make sure there's a free entry in every +* node on the spine when doing an insert. But we can avoid that with +* leaf nodes if we know it's an overwrite. +*/ +static bool has_space_for_insert (struct btree_node *node,uint64_t key) +{ +if (node->header.nr_entries == node->header.max_entries) { +if (le32_to_cpu (node->header.flags) &LEAF_NODE ) { +/* we don't need space if it's an overwrite */ +return contains_key (node, key); + } + +return false; + } + +return true; +} +static int btree_insert_raw (struct shadow_spine *s,dm_block_t root,struct dm_btree_value_type *vt,uint64_t key,unsigned *index)
@@ -721,17 +1113,18 @@ static int btree_insert_raw(struct shado node =dm_block_data (shadow_current (s)); -if (node->header.nr_entries == node->header.max_entries) { +if (!has_space_for_insert (node, key)) {if (top) r =btree_split_beneath (s, key);else - r =btree_split_sibling (s, i, key); + r =rebalance_or_split (s, vt, i, key);if (r <0 )return r; - } - node =dm_block_data (shadow_current (s)); +/* making space can cause the current node to change */ + node =dm_block_data (shadow_current (s)); + } i =lower_bound (node, key);
drivers/md/persistent-data/dm-transaction-manager.c +9 -0
@@ -379,6 +379,15 @@ int dm_tm_ref(struct dm_transaction_manareturn dm_sm_get_count (tm->sm, b, result); } +int dm_tm_block_is_shared (struct dm_transaction_manager *tm,dm_block_t b, +int *result) +{ +if (tm->is_clone) +return -EWOULDBLOCK ; + +return dm_sm_count_is_more_than_one (tm->sm, b, result); +} +struct dm_block_manager *dm_tm_get_bm (struct dm_transaction_manager *tm) {return tm->bm;
drivers/md/persistent-data/dm-transaction-manager.h +8 -2
@@ -103,8 +103,14 @@ void dm_tm_inc(struct dm_transaction_manvoid dm_tm_dec (struct dm_transaction_manager *tm,dm_block_t b); -int dm_tm_ref (struct dm_transaction_manager *tm,dm_block_t b, -uint32_t *result); +int dm_tm_ref (struct dm_transaction_manager *tm,dm_block_t b,uint32_t *result); + +/* +* Finds out if a given block is shared (ie. has a reference count higher +* than one). +*/ +int dm_tm_block_is_shared (struct dm_transaction_manager *tm,dm_block_t b, +int *result);struct dm_block_manager *dm_tm_get_bm (struct dm_transaction_manager *tm);