6.1.191-rc1 review
Subsystems (12)
Greg Kroah-Hartman
maintainerPATCH 1515/1887 octeontx2-vf: fix workqueue and netdev race in probe/remove
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anshumali Gaur <[email protected]>
[ Upstream commit 3ba97ff4f873481d370bee7f7dfb87f8296af9be ]
Initialize the VF workqueue before register_netdev() so ndo_set_rx_mode does not queue work on a NULL workqueue. Unregister the netdev before destroying the workqueue, and add proper probe error cleanup.
--- .../ethernet/marvell/octeontx2/nic/otx2_vf.c | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c +13 -7
@@ -685,16 +685,16 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id)snprintf (netdev->name,sizeof (netdev->name),"lbk%d" , n); } + err =otx2_wq_init (vf); +if (err) + goto err_ptp_destroy; + err =register_netdev (netdev);if (err) {dev_err (dev,"Failed to register netdevice\n" ); - goto err_ptp_destroy; + goto err_wq_destroy; } - err =otx2_wq_init (vf); -if (err) - goto err_unreg_netdev; -otx2vf_set_ethtool_ops (netdev); err =otx2vf_mcam_flow_init (vf);
@@ -722,6 +722,10 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id)otx2_shutdown_tc (vf); err_unreg_netdev:unregister_netdev (netdev); +err_wq_destroy: +cancel_work_sync (&vf->reset_task); +cancel_work_sync (&vf->rx_mode_work); +destroy_workqueue (vf->otx2_wq); err_ptp_destroy:otx2_ptp_destroy (vf); err_detach_rsrc:
@@ -769,11 +773,13 @@ static void otx2vf_remove(struct pci_dev *pdev) }#endif -cancel_work_sync (&vf->reset_task);otx2_unregister_dl (vf);unregister_netdev (netdev); -if (vf->otx2_wq) +if (vf->otx2_wq) { +cancel_work_sync (&vf->reset_task); +cancel_work_sync (&vf->rx_mode_work);destroy_workqueue (vf->otx2_wq); + }otx2_ptp_destroy (vf);otx2_mcam_flow_del (vf);otx2_shutdown_tc (vf);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1516/1887 Bluetooth: do not leak an hci_conn when a second LE connect is rejected
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Radek Podgorny <[email protected]>
[ Upstream commit 7e1e4047200fd7519f9bdfe8a001437715e618b4 ]
create_le_conn_complete() decides whether the failed connection is still pending by comparing it against hci_lookup_le_connect(), which returns the first LE connection in BT_CONNECT. That is the same connection only while at most one is pending.
Two can be pending. Connections created on the passive scan path sit in BT_CONNECT with HCI_CONN_SCANNING set and are invisible to hci_lookup_le_connect() until hci_le_create_conn_sync() clears the flag when their command is issued, so the -EBUSY guard in hci_connect_le() does not prevent a second connection from being queued while the first is still on the scan path. Whenever two connections are in BT_CONNECT at once, the lookup may return one connection while create_le_conn_complete() is reporting the failure of the other; the early exit then drops the error and hci_conn_failed() never runs on the connection that failed.
The controller also rejects a second HCI_OP_LE_CREATE_CONN issued while another connection creation is still outstanding, per Core Spec Vol 4, Part E. The spec calls for Command Disallowed there; the bcm43438 observed here answers with an LMP/LL error code instead, which bt_to_errno() maps to the -EPROTO (-71) in the log below.
The leaked connection stays in BT_CONNECT forever, and because hci_connect_le() refuses to dial while hci_lookup_le_connect() finds anything, every subsequent attempt to reach any peer fails with -EBUSY and no command reaches the controller at all.
Seen on a bcm43438 with two BLE peers polled on the same interval (state 5 is BT_CONNECT; both handles are UNSET ones, allocated from the ida above HCI_CONN_HANDLE_MAX):
Bluetooth: hci1: Opcode 0x2013 failed: -71
# hcitool con
< LE 14:9C:EF:03:68:81 handle 3840 state 5 lm CENTRAL
< LE C4:D3:6A:8C:B5:38 handle 3841 state 5 lm CENTRALA btmon capture across the next ten minutes of connect attempts contains no HCI_OP_LE_CREATE_CONN at all; outgoing LE connections do not recover until the adapter is reset. With this change the same scenario fails the rejected connection cleanly and further connects to both peers go through.
Ask about the connection itself instead of about the device.
--- net/bluetooth/hci_conn.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
net/bluetooth/hci_conn.c +7 -2
@@ -1344,8 +1344,13 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err) goto done; } -/* Check if connection is still pending */ -if (conn !=hci_lookup_le_connect (hdev)) +/* Check if this connection is still pending. +* +* hci_lookup_le_connect() returns only the first LE connection +* in BT_CONNECT, which is not necessarily this one when two are +* pending at once, so ask the connection itself. +*/ +if (!hci_conn_valid (hdev, conn) || conn->state !=BT_CONNECT ) goto done;hci_conn_failed (conn,bt_status (err));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1517/1887 Drivers: hv: vmbus: Skip VMBus module cleanup for non-nested root partition
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Kelley <[email protected]>
[ Upstream commit cee0d90bceae1dee3bcc70f8d6b2ceb5b87deb42 ]
The VMBus module initialization function, hv_acpi_init(), currently does nothing when running in the root partition and root is not nested in another VM. But the initialization function reports success, so the VMBus module is indeed loaded. VMBus functionality is not actually needed, but the VMBus module must be loaded so that hv_vmbus_exists() can answer correctly. Furthermore, the mshv_root dependency on the VMBus module is needed as described in the commit message for 840b740a35bf ("mshv: Add conditional VMBus dependency").
Loading the VMBus module without actually initializing it causes failures if the module should later be unloaded. The module unload code tries to clean up things that were never initialized, resulting in memory faults and a panic.
Fix this by having VMBus module exit function perform the same check for non-nested root partition, and do nothing in such a case, just like hv_acpi_init().
In the long run, the code that manages the Hyper-V provided SynIC should be refactored to better coordinate the requirements of root partition scenarios and normal VM scenarios, and to hopefully remove the hv_vmbus_exists() dependnecy between mshv_root and VMBus modules. Preventing the current unload failure scenario is an expediency until such a refactoring is done.
--- drivers/hv/vmbus_drv.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
drivers/hv/vmbus_drv.c +15 -0
@@ -2594,6 +2594,13 @@ static int __init hv_acpi_init(void)return -ENODEV ;if (hv_root_partition) +/* +* A non-nested root partition does not need VMBus client +* functionality. However, the mshv_root module may have +* a dependency on the VMBus module as described in +* commit 840b740a35bf. Return success so the module +* loads even though no VMBus initialization is done. +*/ return 0 ;/*
@@ -2643,6 +2650,14 @@ static void __exit vmbus_exit(void) {int cpu; +if (hv_root_partition) +/* +* If a non-nested root partition loaded the VMBus module, +* hv_acpi_init() did not do any VMBus initialization. +* There's nothing to clean up, so just return. +*/ +return ; +unregister_syscore_ops (&hv_synic_syscore_ops);hv_remove_kexec_handler ();
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1518/1887 net/sched: account classifier filter allocations to memcg
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 1beb81947eb486716af80db7a584f9e9fef7e003 ]
Allocations in the tc classifier *_change() paths (filter objects, per-CPU counters, and per-filter aux data) use plain GFP_KERNEL without __GFP_ACCOUNT, allowing unprivileged users to pin kernel memory outside memcg charging. The shared tcf_exts_init_ex() action array allocation in cls_api.c was also uncharged; this patch closes it along with the per-classifier filter-object/percpu/aux allocations that remain unaccounted.
Add GFP_KERNEL_ACCOUNT to:
- the shared tcf_exts_init_ex() action array (cls_api.c), common to every filter of every classifier (32 pointers, 256 bytes);
- the filter-object, per-CPU-counter, and per-filter aux allocations in cls_basic, cls_bpf, cls_cgroup, cls_flow, cls_flower, cls_fw, cls_matchall, cls_route and cls_u32;
- the u32_init_knode() replace-path knode allocation (cls_u32.c), which allocates the same struct tc_u_knode + sel.keys on every replace of an existing knode and was missed by the create-path-only conversion.
Also fix the cls_basic error path: basic_change() inserts fnew into the IDR before allocating the per-CPU counter. If alloc_percpu() fails the errout path kfree'd fnew without idr_remove, leaving a dangling pointer in the IDR. With GFP_KERNEL_ACCOUNT the percpu alloc becomes failable on demand (memcg at memory.max), making the dead path attacker-reachable and burning the handle permanently. Add the idr_remove on the percpu failure path, matching the basic_set_parms failure-path pattern.
Note: [email protected] provided a poc for basic_cls, but it was easy to extend to the other classifiers.
Conditions to recreate the bug:
- CONFIG_NET_SCHED, CONFIG_NET_CLS_* (the classifier being used), CONFIG_NET_CLS_ACT, CONFIG_MEMCG, CONFIG_USER_NS, CONFIG_NET_NS.
- Unprivileged user in a fresh user+network namespace (unshare -Urn), or root with CAP_NET_ADMIN.
- Create a large number of tc filters (e.g. tc filter add dev lo ingress ... <classifier> ...) while watching a memcg-limited cgroup:
system slab grows far faster than memory.current, pinning kernel memory outside memcg charging.
--- include/net/pkt_cls.h | 2 +- net/sched/cls_basic.c | 6 ++++-- net/sched/cls_bpf.c | 6 +++--- net/sched/cls_cgroup.c | 2 +- net/sched/cls_flow.c | 2 +- net/sched/cls_flower.c | 4 ++-- net/sched/cls_fw.c | 4 ++-- net/sched/cls_matchall.c | 4 ++-- net/sched/cls_route.c | 4 ++-- net/sched/cls_u32.c | 12 +++++++----- 10 files changed, 25 insertions(+), 21 deletions(-)
include/net/pkt_cls.h +1 -1
@@ -280,7 +280,7 @@ static inline int tcf_exts_init(struct tcf_exts *exts, struct net *net,*/ exts->net = net; exts->actions =kcalloc (TCA_ACT_MAX_PRIO ,sizeof (struct tc_action *), -GFP_KERNEL ); +GFP_KERNEL_ACCOUNT );if (!exts->actions)return -ENOMEM ;#endif
net/sched/cls_basic.c +4 -2
@@ -191,7 +191,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,return -EINVAL ; } - fnew =kzalloc (sizeof (*fnew),GFP_KERNEL ); + fnew =kzalloc (sizeof (*fnew),GFP_KERNEL_ACCOUNT );if (!fnew)return -ENOBUFS ;
@@ -210,9 +210,11 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,if (err) goto errout; fnew->handle = handle; - fnew->pf = alloc_percpu(struct tc_basic_pcnt); + fnew->pf = alloc_percpu_gfp(struct tc_basic_pcnt, GFP_KERNEL_ACCOUNT);if (!fnew->pf) { err = -ENOMEM ; +if (!fold) +idr_remove (&head->handle_idr, fnew->handle); goto errout; }
net/sched/cls_bpf.c +3 -3
@@ -350,7 +350,7 @@ static int cls_bpf_prog_from_ops(struct nlattr **tb, struct cls_bpf_prog *prog)if (bpf_size !=nla_len (tb[TCA_BPF_OPS ]))return -EINVAL ; - bpf_ops =kmemdup (nla_data (tb[TCA_BPF_OPS ]), bpf_size,GFP_KERNEL ); + bpf_ops =kmemdup (nla_data (tb[TCA_BPF_OPS ]), bpf_size,GFP_KERNEL_ACCOUNT );if (bpf_ops ==NULL )return -ENOMEM ;
@@ -387,7 +387,7 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog,return PTR_ERR (fp);if (tb[TCA_BPF_NAME ]) { - name =nla_memdup (tb[TCA_BPF_NAME ],GFP_KERNEL ); + name =nla_memdup (tb[TCA_BPF_NAME ],GFP_KERNEL_ACCOUNT );if (!name) {bpf_prog_put (fp);return -ENOMEM ;
@@ -427,7 +427,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,if (ret <0 )return ret; - prog =kzalloc (sizeof (*prog),GFP_KERNEL ); + prog =kzalloc (sizeof (*prog),GFP_KERNEL_ACCOUNT );if (!prog)return -ENOBUFS ;
net/sched/cls_cgroup.c +1 -1
@@ -93,7 +93,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,if (head && handle != head->handle)return -ENOENT ; - new =kzalloc (sizeof (*head),GFP_KERNEL ); + new =kzalloc (sizeof (*head),GFP_KERNEL_ACCOUNT );if (!new)return -ENOBUFS ;
net/sched/cls_flow.c +1 -1
@@ -436,7 +436,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,return -EOPNOTSUPP ; } - fnew =kzalloc (sizeof (*fnew),GFP_KERNEL ); + fnew =kzalloc (sizeof (*fnew),GFP_KERNEL_ACCOUNT );if (!fnew)return -ENOBUFS ;
net/sched/cls_flower.c +2 -2
@@ -2003,7 +2003,7 @@ static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head,struct fl_flow_mask *newmask;int err; - newmask =kzalloc (sizeof (*newmask),GFP_KERNEL ); + newmask =kzalloc (sizeof (*newmask),GFP_KERNEL_ACCOUNT );if (!newmask)return ERR_PTR (-ENOMEM );
@@ -2196,7 +2196,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, goto errout_tb; } - fnew =kzalloc (sizeof (*fnew),GFP_KERNEL ); + fnew =kzalloc (sizeof (*fnew),GFP_KERNEL_ACCOUNT );if (!fnew) { err = -ENOBUFS ; goto errout_tb;
net/sched/cls_fw.c +2 -2
@@ -274,7 +274,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,if (f->id != handle && handle)return -EINVAL ; - fnew =kzalloc (sizeof (struct fw_filter ),GFP_KERNEL ); + fnew =kzalloc (sizeof (struct fw_filter ),GFP_KERNEL_ACCOUNT );if (!fnew)return -ENOBUFS ;
@@ -328,7 +328,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,rcu_assign_pointer (tp->root, head); } - f =kzalloc (sizeof (struct fw_filter ),GFP_KERNEL ); + f =kzalloc (sizeof (struct fw_filter ),GFP_KERNEL_ACCOUNT );if (f ==NULL )return -ENOBUFS ;
net/sched/cls_matchall.c +2 -2
@@ -187,7 +187,7 @@ static int mall_change(struct net *net, struct sk_buff *in_skb,return -EINVAL ; } - new =kzalloc (sizeof (*new),GFP_KERNEL ); + new =kzalloc (sizeof (*new),GFP_KERNEL_ACCOUNT );if (!new)return -ENOBUFS ;
@@ -199,7 +199,7 @@ static int mall_change(struct net *net, struct sk_buff *in_skb, handle =1 ; new->handle = handle; new->flags = userflags; - new->pf = alloc_percpu(struct tc_matchall_pcnt); + new->pf = alloc_percpu_gfp(struct tc_matchall_pcnt, GFP_KERNEL_ACCOUNT);if (!new->pf) { err = -ENOMEM ; goto err_alloc_percpu;
net/sched/cls_route.c +2 -2
@@ -442,7 +442,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, h1 =to_hash (nhandle); b =rtnl_dereference (head->table[h1]);if (!b) { - b =kzalloc (sizeof (struct route4_bucket ),GFP_KERNEL ); + b =kzalloc (sizeof (struct route4_bucket ),GFP_KERNEL_ACCOUNT );if (b ==NULL )return -ENOBUFS ;
@@ -513,7 +513,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,return -EINVAL ; err = -ENOBUFS ; - f =kzalloc (sizeof (struct route4_filter ),GFP_KERNEL ); + f =kzalloc (sizeof (struct route4_filter ),GFP_KERNEL_ACCOUNT );if (!f) goto errout;
net/sched/cls_u32.c +7 -5
@@ -839,7 +839,8 @@ static struct tc_u_knode *u32_init_knode(struct net *net, struct tcf_proto *tp,struct tc_u32_sel *s = &n->sel;struct tc_u_knode *new; - new =kzalloc (struct_size (new, sel.keys, s->nkeys),GFP_KERNEL ); + new =kzalloc (struct_size (new, sel.keys, s->nkeys), +GFP_KERNEL_ACCOUNT );if (!new)return NULL ;
@@ -1137,15 +1138,16 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, goto erridr; } - n =kzalloc (struct_size (n, sel.keys, s->nkeys),GFP_KERNEL ); + n =kzalloc (struct_size (n, sel.keys, s->nkeys),GFP_KERNEL_ACCOUNT );if (n ==NULL ) { err = -ENOBUFS ; goto erridr; }#ifdef CONFIG_CLS_U32_PERF - n->pf =__alloc_percpu (struct_size (n->pf, kcnts, s->nkeys), - __alignof__(struct tc_u32_pcnt )); + n->pf =__alloc_percpu_gfp (struct_size (n->pf, kcnts, s->nkeys), + __alignof__(struct tc_u32_pcnt ), +GFP_KERNEL_ACCOUNT );if (!n->pf) { err = -ENOBUFS ; goto errfree;
@@ -1167,7 +1169,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, goto errout;#ifdef CONFIG_CLS_U32_MARK - n->pcpu_success =alloc_percpu (u32); + n->pcpu_success =alloc_percpu_gfp (u32,GFP_KERNEL_ACCOUNT );if (!n->pcpu_success) { err = -ENOMEM ; goto errout;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1519/1887 NFSv4.1: zero referring call lists before decoding
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jérémy Jean <[email protected]>
[ Upstream commit 8fa4804fe62ca4155a2d8fc2789d630376cbf2fc ]
decode_cb_sequence_args() allocates csa_rclists with kmalloc_objs(), so each referring_call_list starts uninitialized. decode_rc_list() assigns rcl_refcalls only when rcl_nrefcalls is nonzero. A valid list with zero referring calls therefore leaves the pointer uninitialized, and nfs4_callback_sequence() later passes stale slab contents to kfree().
Allocate csa_rclists with kzalloc_objs() so every rcl_refcalls member is NULL from the beginning, including valid empty referring call lists.
--- fs/nfs/callback_xdr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
fs/nfs/callback_xdr.c +3 -3
@@ -419,9 +419,9 @@ static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp, args->csa_nrclists =ntohl (*p++); args->csa_rclists =NULL ;if (args->csa_nrclists) { - args->csa_rclists =kmalloc_array (args->csa_nrclists, -sizeof (*args->csa_rclists), -GFP_KERNEL ); + args->csa_rclists =kcalloc (args->csa_nrclists, +sizeof (*args->csa_rclists), +GFP_KERNEL );if (unlikely (args->csa_rclists ==NULL ))return htonl (NFS4ERR_RESOURCE );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1520/1887 vxlan: fix reading neigh ha
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikolay Aleksandrov <[email protected]>
[ Upstream commit b824059a673b2283e78c7aae2c7d257aad7f0e1d ]
Currently arp/neigh_reduce read neigh ha directly which can lead to partial reads while the neigh is being updated. Use neigh_ha_snapshot to take a stable snapshot of the address similar to route_shortcircuit which already does the right thing.
--- drivers/net/vxlan/vxlan_core.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
drivers/net/vxlan/vxlan_core.c +13 -7
@@ -1944,6 +1944,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)if (n) {struct vxlan_rdst *rdst =NULL ; +u8 ha[ETH_ALEN ]__aligned(2) ;struct vxlan_fdb *f;struct sk_buff *reply;
@@ -1952,7 +1953,9 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) goto out; } - f =vxlan_find_mac (vxlan, n->ha, vni); +neigh_ha_snapshot (ha, n, n->dev); + + f =vxlan_find_mac (vxlan, ha, vni);if (f) rdst =first_remote_rcu (f);if (rdst &&vxlan_addr_any (&rdst->remote_ip)) {
@@ -1962,7 +1965,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) } reply =arp_create (ARPOP_REPLY ,ETH_P_ARP , sip, dev, tip, sha, - n->ha, sha); + ha, sha);neigh_release (n);
@@ -1995,7 +1998,8 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)#if IS_ENABLED (CONFIG_IPV6 )static struct sk_buff *vxlan_na_create (struct sk_buff *request, -struct neighbour *n,bool isrouter) +struct neighbour *n,u8 *ha, +bool isrouter) {struct net_device *dev = request->dev;struct sk_buff *reply;
@@ -2043,7 +2047,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,/* Ethernet header */ ether_addr_copy (eth_hdr (reply)->h_dest, daddr); -ether_addr_copy (eth_hdr (reply)->h_source, n->ha); +ether_addr_copy (eth_hdr (reply)->h_source, ha);eth_hdr (reply)->h_proto =htons (ETH_P_IPV6 ); reply->protocol =htons (ETH_P_IPV6 );
@@ -2072,7 +2076,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request, na->icmph.icmp6_override =1 ; na->icmph.icmp6_solicited =1 ; na->target = ns->target; -ether_addr_copy (&na->opt[2 ], n->ha); +ether_addr_copy (&na->opt[2 ], ha); na->opt[0 ] =ND_OPT_TARGET_LL_ADDR ; na->opt[1 ] = na_olen >>3 ;
@@ -2115,6 +2119,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)if (n) {struct vxlan_rdst *rdst =NULL ; +u8 ha[ETH_ALEN ]__aligned(2) ;struct vxlan_fdb *f;struct sk_buff *reply;
@@ -2123,7 +2128,8 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) goto out; } - f =vxlan_find_mac (vxlan, n->ha, vni); +neigh_ha_snapshot (ha, n, n->dev); + f =vxlan_find_mac (vxlan, ha, vni);if (f) rdst =first_remote_rcu (f);if (rdst &&vxlan_addr_any (&rdst->remote_ip)) {
@@ -2132,7 +2138,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) goto out; } - reply =vxlan_na_create (skb, n, + reply =vxlan_na_create (skb, n, ha, !!(f ? f->flags &NTF_ROUTER :0 ));neigh_release (n);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1521/1887 net: bridge: arp/nd proxy: fix reading neigh ha
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikolay Aleksandrov <[email protected]>
[ Upstream commit 57549ab9079122991dfa0f8248ca00e101e8e69b ]
Currently neigh ha address is read directly, but that can result in torn/partial reads if the neigh is being updated. Use neigh_ha_snapshot to take a stable snapshot of the address.
--- net/bridge/br_arp_nd_proxy.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-)
net/bridge/br_arp_nd_proxy.c +14 -10
@@ -191,13 +191,15 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, n =neigh_lookup (&arp_tbl, &tip, vlandev);if (n) {struct net_bridge_fdb_entry *f; +u8 ha[ETH_ALEN ]__aligned(2) ;if (!(READ_ONCE (n->nud_state) &NUD_VALID )) {neigh_release (n);return ; } - f =br_fdb_find_rcu (br, n->ha, vid); +neigh_ha_snapshot (ha, n, n->dev); + f =br_fdb_find_rcu (br, ha, vid);if (f) {const struct net_bridge_port *dst =READ_ONCE (f->dst);bool replied = false;
@@ -207,10 +209,10 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, BR_NEIGH_SUPPRESS)))) {if (!vid)br_arp_send (br, p, skb->dev, sip, tip, - sha, n->ha, sha,0 ,0 ); + sha, ha, sha,0 ,0 );else br_arp_send (br, p, skb->dev, sip, tip, - sha, n->ha, sha, + sha, ha, sha, skb->vlan_proto,skb_vlan_tag_get (skb)); replied = true;
@@ -248,7 +250,7 @@ struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *msg) }static void br_nd_send (struct net_bridge *br,struct net_bridge_port *p, -struct sk_buff *request,struct neighbour *n, +struct sk_buff *request,struct neighbour *n,u8 *ha,__be16 vlan_proto,u16 vlan_tci) {struct net_device *dev = request->dev;
@@ -306,7 +308,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,ipv6_eth_mc_map (&in6addr_linklocal_allnodes,eth_hdr (reply)->h_dest); else ether_addr_copy(eth_hdr(reply)->h_dest, daddr); -ether_addr_copy (eth_hdr (reply)->h_source, n->ha); +ether_addr_copy (eth_hdr (reply)->h_source, ha);eth_hdr (reply)->h_proto =htons (ETH_P_IPV6 ); reply->protocol =htons (ETH_P_IPV6 );
@@ -336,7 +338,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, na->icmph.icmp6_override =1 ; na->icmph.icmp6_solicited = dad ?0 :1 ; na->target = ns->target; -ether_addr_copy (&na->opt[2 ], n->ha); +ether_addr_copy (&na->opt[2 ], ha); na->opt[0 ] =ND_OPT_TARGET_LL_ADDR ; na->opt[1 ] = na_olen >>3 ;
@@ -365,7 +367,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,__vlan_hwaccel_put_tag (reply, vlan_proto, vlan_tci);netdev_dbg (dev,"nd send dev %s dst %pI6 dst_hw %pM src %pI6 src_hw %pM\n" , - dev->name, &pip6->daddr, daddr, &pip6->saddr, n->ha); + dev->name, &pip6->daddr, daddr, &pip6->saddr, ha);if (p) {dev_queue_xmit (reply);
@@ -462,24 +464,26 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, n =neigh_lookup (ipv6_stub->nd_tbl, &msg->target, vlandev);if (n) {struct net_bridge_fdb_entry *f; +u8 ha[ETH_ALEN ]__aligned(2) ;if (!(READ_ONCE (n->nud_state) &NUD_VALID )) {neigh_release (n);return ; } - f =br_fdb_find_rcu (br, n->ha, vid); +neigh_ha_snapshot (ha, n, n->dev); + f =br_fdb_find_rcu (br, ha, vid);if (f) {const struct net_bridge_port *dst =READ_ONCE (f->dst);bool replied = false;if (dst && (dst->flags &BR_NEIGH_SUPPRESS )) {if (vid !=0 ) -br_nd_send (br, p, skb, n, +br_nd_send (br, p, skb, n, ha, skb->vlan_proto,skb_vlan_tag_get (skb));else -br_nd_send (br, p, skb, n,0 ,0 ); +br_nd_send (br, p, skb, n, ha,0 ,0 ); replied = true; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1522/1887 net_sched: sch_fifo: implement lockless __fifo_dump()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 01daf66b791e1ddd1a4aae2a65ea65fa1d4a96ef ]
Instead of relying on RTNL, __fifo_dump() can use READ_ONCE() annotations, paired with WRITE_ONCE() ones in __fifo_init().
Also add missing READ_ONCE(sh->limit) in bfifo_enqueue(), pfifo_enqueue() and pfifo_tail_enqueue().
Stable-dep-of: 4c660ee8c809 ("net: sched: fix 32-bit backlog wrap in gred, bfifo and plug enqueue")
--- net/sched/sch_fifo.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
net/sched/sch_fifo.c +7 -6
@@ -19,7 +19,8 @@static int bfifo_enqueue (struct sk_buff *skb,struct Qdisc *sch,struct sk_buff **to_free) { -if (likely (sch->qstats.backlog +qdisc_pkt_len (skb) <= sch->limit)) +if (likely (sch->qstats.backlog +qdisc_pkt_len (skb) <= +READ_ONCE (sch->limit)))return qdisc_enqueue_tail (skb, sch);return qdisc_drop (skb, sch, to_free);
@@ -28,7 +29,7 @@ static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch,static int pfifo_enqueue (struct sk_buff *skb,struct Qdisc *sch,struct sk_buff **to_free) { -if (likely (sch->q.qlen < sch->limit)) +if (likely (sch->q.qlen <READ_ONCE (sch->limit)))return qdisc_enqueue_tail (skb, sch);return qdisc_drop (skb, sch, to_free);
@@ -42,7 +43,7 @@ static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch,if (unlikely (READ_ONCE (sch->limit) ==0 ))return qdisc_drop (skb, sch, to_free); -if (likely (sch->q.qlen < sch->limit)) +if (likely (sch->q.qlen <READ_ONCE (sch->limit)))return qdisc_enqueue_tail (skb, sch); prev_backlog = sch->qstats.backlog;
@@ -108,14 +109,14 @@ static int __fifo_init(struct Qdisc *sch, struct nlattr *opt,if (is_bfifo) limit *=psched_mtu (qdisc_dev (sch)); - sch->limit = limit; +WRITE_ONCE (sch->limit, limit); }else {struct tc_fifo_qopt *ctl =nla_data (opt);if (nla_len (opt) <sizeof (*ctl))return -EINVAL ; - sch->limit = ctl->limit; +WRITE_ONCE (sch->limit, ctl->limit); }if (is_bfifo)
@@ -157,7 +158,7 @@ static void fifo_destroy(struct Qdisc *sch)static int __fifo_dump (struct Qdisc *sch,struct sk_buff *skb) { -struct tc_fifo_qopt opt = { .limit = sch->limit }; +struct tc_fifo_qopt opt = { .limit =READ_ONCE (sch->limit) };if (nla_put (skb,TCA_OPTIONS ,sizeof (opt), &opt)) goto nla_put_failure;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1523/1887 net: sched: fix 32-bit backlog wrap in gred, bfifo and plug enqueue
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 4c660ee8c809637909f4f7eb1017f7b9401c75c4 ]
gred_enqueue(), bfifo_enqueue() and plug_enqueue() admit a packet when the current backlog plus the packet length fits within the queue limit:
sch->qstats.backlog + qdisc_pkt_len(skb) <= sch->limit (gred default VQ) gred_backlog+qdisc_pkt_len(skb) <= q->limit (gred configured VQ) sch->qstats.backlog + qdisc_pkt_len(skb) <= sch->limit (bfifo)
sch->qstats.backlog + skb->len <= q->limit (plug)
sch->qstats.backlog and q->backlog are u32, and qdisc_pkt_len()/skb->len are unsigned int, so all sums are computed in 32 bits and wrap at 2^32. Once the true backlog exceeds 4 GiB the wrapped sum becomes small and admission keeps succeeding, so the queue grows without bound and the kernel can be driven to OOM.
Promote the sums to u64 so admission stops once the true backlog exceeds the limit. The limit is u32, so the bounded queue stays below 2^32 and the stored u32 backlog never wraps.
The bug can only be reproduced as root (albeit with ridiculous setup):
attach a gred (or bfifo/plug) qdisc with a limit near 4 GiB, leaving the default VQ unconfigured (for gred), and drive >4 GiB of queued traffic (e.g. via a size table / stab to inflate qdisc_pkt_len, or sustained high-rate traffic). The u32 backlog+len sum wraps at 2^32, admission keeps succeeding, and the queue grows unboundedly to OOM.
--- net/sched/sch_fifo.c | 2 +- net/sched/sch_gred.c | 4 ++-- net/sched/sch_plug.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-)
net/sched/sch_fifo.c +1 -1
@@ -19,7 +19,7 @@static int bfifo_enqueue (struct sk_buff *skb,struct Qdisc *sch,struct sk_buff **to_free) { -if (likely (sch->qstats.backlog +qdisc_pkt_len (skb) <= +if (likely ((u64 )sch->qstats.backlog +qdisc_pkt_len (skb) <=READ_ONCE (sch->limit)))return qdisc_enqueue_tail (skb, sch);
net/sched/sch_gred.c +2 -2
@@ -179,7 +179,7 @@ static int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch,* if no default DP has been configured. This * allows for DP flows to be left untouched. */ -if (likely (sch->qstats.backlog +qdisc_pkt_len (skb) <= +if (likely ((u64 )sch->qstats.backlog +qdisc_pkt_len (skb) <= sch->limit))return qdisc_enqueue_tail (skb, sch);else
@@ -244,7 +244,7 @@ static int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch,break ; } -if (gred_backlog (t, q, sch) +qdisc_pkt_len (skb) <= q->limit) { +if ((u64 )gred_backlog (t, q, sch) +qdisc_pkt_len (skb) <= q->limit) { q->backlog +=qdisc_pkt_len (skb);return qdisc_enqueue_tail (skb, sch); }
net/sched/sch_plug.c +1 -1
@@ -89,7 +89,7 @@ static int plug_enqueue(struct sk_buff *skb, struct Qdisc *sch, {struct plug_sched_data *q =qdisc_priv (sch); -if (likely (sch->qstats.backlog + skb->len <= q->limit)) { +if (likely ((u64 )sch->qstats.backlog + skb->len <= q->limit)) {if (!q->unplug_indefinite) q->pkts_current_epoch++;return qdisc_enqueue_tail (skb, sch);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1524/1887 ip6mr: do not clone dst in ip6mr_cache_report()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 235b42b5860189eb8c27c36435ad932cae65a734 ]
IPv6 input attaches a non-refcounted (NOREF) dst to skbs under RCU. When an ingress multicast packet misses MFC lookup, ip6mr_cache_unresolved() places the skb onto the unresolved queue, escaping the receive-side RCU grace period.
If the underlying route is deleted and freed, and the MFC queue is later resolved with a wrong parent interface, ip6_mr_forward() invokes ip6mr_cache_report(..., MRT6MSG_WRONGMIF), which executes dst_clone(skb_dst(pkt)) on the freed dst entry, triggering a slab use-after-free.
Report packets queued to mroute6_sk (a raw socket) and netlink notifications do not require an attached dst entry.
Fix this by:
1. Removing dst_clone() in ip6mr_cache_report() and ensuring report skbs do not hold a dst.
2. Dropping skb_dst before queuing unresolved skbs in ip6mr_cache_unresolved(), matching the fact that multicast forwarding resolves outgoing routes anew via ip6_route_output().
--- net/ipv6/ip6mr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/ipv6/ip6mr.c +2 -1
@@ -1128,7 +1128,6 @@ static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt, msg->im6_src =ipv6_hdr (pkt)->saddr; msg->im6_dst =ipv6_hdr (pkt)->daddr; -skb_dst_set (skb,dst_clone (skb_dst (pkt))); skb->ip_summed =CHECKSUM_UNNECESSARY ; }
@@ -1138,6 +1137,7 @@ static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt,return -EINVAL ; } +skb_dst_drop (skb);mrt6msg_netlink_event (mrt, skb);/* Deliver to user space multicast routing algorithms */
@@ -1217,6 +1217,7 @@ static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi, skb->dev = dev; skb->skb_iif = dev->ifindex; } +skb_dst_drop (skb);skb_queue_tail (&c->_c.mfc_un.unres.unresolved, skb); err =0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1525/1887 net: qlcnic: validate unified ROM sections before loading
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
[ Upstream commit 5e8076e4e4124dae75a3e080ddc20404700d7585 ]
The unified ROM parser reads directory, product, and data-descriptor fields from the firmware file. Existing validation forms table and data ends with unchecked additions and multiplications. Malformed values can wrap before they are compared with the firmware size. The parser also dereferences typed pointers at firmware-controlled offsets.
Valid descriptor extents alone are insufficient for the consumers. The loader reads a fixed-size bootloader regardless of its declared size, the version parser assumes a 17-byte tail, and a partial final firmware word is read as a full u64. A truncated image can therefore make the driver read beyond the firmware allocation during validation or loading.
Replace the pointer-returning parser with bounded range helpers. Validate table entry sizes, descriptor indices, section ranges, the fixed bootloader load length, and the version tail before exposing any section. Read all file fields with unaligned little-endian accessors and assemble a partial final word from only the bytes that remain. Apply the same range checks to the legacy image before reading its fixed fields.
--- .../net/ethernet/qlogic/qlcnic/qlcnic_init.c | 445 ++++++++++-------- 1 file changed, 248 insertions(+), 197 deletions(-)
drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c +248 -197
@@ -6,6 +6,7 @@#include "qlcnic.h" #include "qlcnic_hw.h" +#include <asm/unaligned.h> struct crb_addr_pair {u32 addr;
@@ -740,159 +741,212 @@ qlcnic_has_mn(struct qlcnic_adapter *adapter)return 0 ; } -static -struct uni_table_desc *qlcnic_get_table_desc (const u8 *unirom,int section) -{ -u32 i, entries; -struct uni_table_desc *directory = (struct uni_table_desc *) &unirom[0 ]; - entries =le32_to_cpu (directory->num_entries); - -for (i =0 ; i < entries; i++) { +#define FILEHEADER_SIZE (14 * 4) +#define QLCNIC_UNI_DIR_TYPE_OFF (8 * sizeof(__le32)) +#define QLCNIC_UNI_DIR_ENTRY_MIN_SIZE (9 * sizeof(__le32)) +#define QLCNIC_UNI_PRODUCT_ENTRY_MIN_SIZE \ + ((QLCNIC_UNI_FIRMWARE_IDX_OFF + 1) * sizeof(__le32)) +#define QLCNIC_UNI_VERSION_TAIL_SIZE 17 +#define QLCNIC_UNI_BOOTLD_SIZE \ + (QLCNIC_IMAGE_START - QLCNIC_BOOTLD_START) + +struct qlcnic_uni_data { +u32 offset; +u32 size; +}; -u32 offs =le32_to_cpu (directory->findex) + - i *le32_to_cpu (directory->entry_size); -u32 tab_type =le32_to_cpu (*((__le32 *)&unirom[offs] +8 )); +static bool qlcnic_rom_range_valid (size_t size,size_t offset,size_t len) +{ +return offset <= size && len <= size - offset; +} -if (tab_type == section) -return (struct uni_table_desc *) &unirom[offs]; - } +static bool qlcnic_rom_table_valid (size_t size,u32 offset,u32 entries, +u32 entry_size,u32 min_entry_size) +{ +if (entry_size < min_entry_size || offset > size) +return false; -return NULL ; +return entries <= (size - offset) / entry_size; } -#define FILEHEADER_SIZE (14 * 4) - -static int -qlcnic_validate_header (struct qlcnic_adapter *adapter) +static int qlcnic_get_directory (struct qlcnic_adapter *adapter, +size_t *offset,u32 *entries,u32 *entry_size) { -const u8 *unirom = adapter->fw->data; -struct uni_table_desc *directory = (struct uni_table_desc *) &unirom[0 ]; -u32 entries, entry_size, tab_size, fw_file_size; - - fw_file_size = adapter->fw->size; +const struct firmware *fw = adapter->fw; +const u8 *directory = fw->data; -if (fw_file_size <FILEHEADER_SIZE ) +if (fw->size <FILEHEADER_SIZE )return -EINVAL ; - entries =le32_to_cpu (directory->num_entries); - entry_size =le32_to_cpu (directory->entry_size); - tab_size =le32_to_cpu (directory->findex) + (entries * entry_size); + *offset =get_unaligned_le32 (directory + + offsetof(struct uni_table_desc , findex)); + *entries =get_unaligned_le32 (directory + + offsetof(struct uni_table_desc , num_entries)); + *entry_size =get_unaligned_le32 (directory + + offsetof(struct uni_table_desc , entry_size)); -if (fw_file_size < tab_size) +if (!qlcnic_rom_table_valid (fw->size, *offset, *entries, *entry_size, +QLCNIC_UNI_DIR_ENTRY_MIN_SIZE ))return -EINVAL ;return 0 ; } -static int -qlcnic_validate_bootld (struct qlcnic_adapter *adapter) +static int qlcnic_get_table_desc (struct qlcnic_adapter *adapter,int section, +size_t *desc_offset) { -struct uni_table_desc *tab_desc; -struct uni_data_desc *descr; -u32 offs, tab_size, data_size, idx;const u8 *unirom = adapter->fw->data; -__le32 temp; - - temp = *((__le32 *)&unirom[adapter->file_prd_off] + -QLCNIC_UNI_BOOTLD_IDX_OFF ); - idx =le32_to_cpu (temp); - tab_desc =qlcnic_get_table_desc (unirom,QLCNIC_UNI_DIR_SECT_BOOTLD ); - -if (!tab_desc) -return -EINVAL ; - - tab_size =le32_to_cpu (tab_desc->findex) + -le32_to_cpu (tab_desc->entry_size) * (idx +1 ); - -if (adapter->fw->size < tab_size) -return -EINVAL ; +size_t directory_offset; +u32 entries, entry_size; +size_t i; +int ret; - offs =le32_to_cpu (tab_desc->findex) + -le32_to_cpu (tab_desc->entry_size) * idx; - descr = (struct uni_data_desc *)&unirom[offs]; + ret =qlcnic_get_directory (adapter, &directory_offset, &entries, + &entry_size); +if (ret) +return ret; - data_size =le32_to_cpu (descr->findex) +le32_to_cpu (descr->size); +for (i =0 ; i < entries; i++) { +size_t offset = directory_offset + i * entry_size; +u32 table_type; -if (adapter->fw->size < data_size) -return -EINVAL ; + table_type =get_unaligned_le32 (unirom + offset + +QLCNIC_UNI_DIR_TYPE_OFF ); +if (table_type == section) { + *desc_offset = offset; +return 0 ; + } + } -return 0 ; +return -ENOENT ; }static int -qlcnic_validate_fw (struct qlcnic_adapter *adapter) +qlcnic_validate_header (struct qlcnic_adapter *adapter) { -struct uni_table_desc *tab_desc; -struct uni_data_desc *descr; -const u8 *unirom = adapter->fw->data; -u32 offs, tab_size, data_size, idx; -__le32 temp; +u32 entries, entry_size; +size_t offset; + +return qlcnic_get_directory (adapter, &offset, &entries, &entry_size); +} - temp = *((__le32 *)&unirom[adapter->file_prd_off] + -QLCNIC_UNI_FIRMWARE_IDX_OFF ); - idx =le32_to_cpu (temp); - tab_desc =qlcnic_get_table_desc (unirom,QLCNIC_UNI_DIR_SECT_FW ); +static int qlcnic_get_data_desc (struct qlcnic_adapter *adapter,u32 section, +u32 index_offset,struct qlcnic_uni_data *data) +{ +size_t table_desc_offset, table_offset, desc_offset; +const struct firmware *fw = adapter->fw; +const u8 *unirom = fw->data; +size_t product_index_offset; +u32 entries, entry_size, idx; +int ret; -if (!tab_desc) + product_index_offset = adapter->file_prd_off + + (size_t )index_offset *sizeof (__le32); +if (!qlcnic_rom_range_valid (fw->size, product_index_offset, +sizeof (__le32)))return -EINVAL ; - tab_size =le32_to_cpu (tab_desc->findex) + -le32_to_cpu (tab_desc->entry_size) * (idx +1 ); + idx =get_unaligned_le32 (unirom + product_index_offset); + ret =qlcnic_get_table_desc (adapter, section, &table_desc_offset); +if (ret) +return ret; -if (adapter->fw->size < tab_size) + table_offset =get_unaligned_le32 (unirom + table_desc_offset + + offsetof(struct uni_table_desc , findex)); + entries =get_unaligned_le32 (unirom + table_desc_offset + + offsetof(struct uni_table_desc , num_entries)); + entry_size =get_unaligned_le32 (unirom + table_desc_offset + + offsetof(struct uni_table_desc , entry_size)); +if (!qlcnic_rom_table_valid (fw->size, table_offset, entries, + entry_size,sizeof (struct uni_data_desc )) || + idx >= entries)return -EINVAL ; - offs =le32_to_cpu (tab_desc->findex) + -le32_to_cpu (tab_desc->entry_size) * idx; - descr = (struct uni_data_desc *)&unirom[offs]; - data_size =le32_to_cpu (descr->findex) +le32_to_cpu (descr->size); + desc_offset = table_offset + (size_t )idx * entry_size; + data->offset =get_unaligned_le32 (unirom + desc_offset + + offsetof(struct uni_data_desc , findex)); + data->size =get_unaligned_le32 (unirom + desc_offset + + offsetof(struct uni_data_desc , size)); -if (adapter->fw->size < data_size) +if (!qlcnic_rom_range_valid (fw->size, data->offset, data->size))return -EINVAL ;return 0 ; } +static int qlcnic_validate_bootld (struct qlcnic_adapter *adapter) +{ +struct qlcnic_uni_data data; +int ret; + + ret =qlcnic_get_data_desc (adapter,QLCNIC_UNI_DIR_SECT_BOOTLD , +QLCNIC_UNI_BOOTLD_IDX_OFF , &data); +if (ret) +return ret; + +return data.size <QLCNIC_UNI_BOOTLD_SIZE ? -EINVAL :0 ; +} + +static int qlcnic_validate_fw (struct qlcnic_adapter *adapter) +{ +struct qlcnic_uni_data data; +int ret; + + ret =qlcnic_get_data_desc (adapter,QLCNIC_UNI_DIR_SECT_FW , +QLCNIC_UNI_FIRMWARE_IDX_OFF , &data); +if (ret) +return ret; + +return data.size <QLCNIC_UNI_VERSION_TAIL_SIZE ? -EINVAL :0 ; +} +static int qlcnic_validate_product_offs (struct qlcnic_adapter *adapter) { -struct uni_table_desc *ptab_descr; +size_t table_desc_offset, table_offset;const u8 *unirom = adapter->fw->data;int mn_present =qlcnic_has_mn (adapter); -u32 entries, entry_size, tab_size, i; -__le32 temp; - - ptab_descr =qlcnic_get_table_desc (unirom, -QLCNIC_UNI_DIR_SECT_PRODUCT_TBL ); -if (!ptab_descr) -return -EINVAL ; +u32 entries, entry_size; +size_t i; +int ret; - entries =le32_to_cpu (ptab_descr->num_entries); - entry_size =le32_to_cpu (ptab_descr->entry_size); - tab_size =le32_to_cpu (ptab_descr->findex) + (entries * entry_size); + ret =qlcnic_get_table_desc (adapter,QLCNIC_UNI_DIR_SECT_PRODUCT_TBL , + &table_desc_offset); +if (ret) +return ret; -if (adapter->fw->size < tab_size) + table_offset =get_unaligned_le32 (unirom + table_desc_offset + + offsetof(struct uni_table_desc , findex)); + entries =get_unaligned_le32 (unirom + table_desc_offset + + offsetof(struct uni_table_desc , num_entries)); + entry_size =get_unaligned_le32 (unirom + table_desc_offset + + offsetof(struct uni_table_desc , entry_size)); +if (!qlcnic_rom_table_valid (adapter->fw->size, table_offset, entries, + entry_size, +QLCNIC_UNI_PRODUCT_ENTRY_MIN_SIZE ))return -EINVAL ; nomn:for (i =0 ; i < entries; i++) { - -u32 flags, file_chiprev, offs; +size_t offset = table_offset + i * entry_size;u8 chiprev = adapter->ahw->revision_id; +u32 flags, file_chiprev;u32 flagbit; - offs =le32_to_cpu (ptab_descr->findex) + - i *le32_to_cpu (ptab_descr->entry_size); - temp = *((__le32 *)&unirom[offs] +QLCNIC_UNI_FLAGS_OFF ); - flags =le32_to_cpu (temp); - temp = *((__le32 *)&unirom[offs] +QLCNIC_UNI_CHIP_REV_OFF ); - file_chiprev =le32_to_cpu (temp); + flags =get_unaligned_le32 (unirom + offset + +QLCNIC_UNI_FLAGS_OFF *sizeof (__le32)); + file_chiprev =get_unaligned_le32 (unirom + offset + +QLCNIC_UNI_CHIP_REV_OFF * +sizeof (__le32)); flagbit = mn_present ?1 :2 ;if ((chiprev == file_chiprev) && ((1ULL << flagbit) & flags)) { - adapter->file_prd_off = offs; +if (offset >U32_MAX ) +return -EINVAL ; + + adapter->file_prd_off = offset;return 0 ; } }
@@ -933,93 +987,81 @@ qlcnic_validate_unified_romimage(struct qlcnic_adapter *adapter)return 0 ; } -static -struct uni_data_desc *qlcnic_get_data_desc (struct qlcnic_adapter *adapter, -u32 section,u32 idx_offset) -{ -const u8 *unirom = adapter->fw->data; -struct uni_table_desc *tab_desc; -u32 offs, idx; -__le32 temp; - - temp = *((__le32 *)&unirom[adapter->file_prd_off] + idx_offset); - idx =le32_to_cpu (temp); - - tab_desc =qlcnic_get_table_desc (unirom, section); - -if (tab_desc ==NULL ) -return NULL ; - - offs =le32_to_cpu (tab_desc->findex) + -le32_to_cpu (tab_desc->entry_size) * idx; - -return (struct uni_data_desc *)&unirom[offs]; -} - -static u8 * -qlcnic_get_bootld_offs (struct qlcnic_adapter *adapter) +static int qlcnic_get_bootld_data (struct qlcnic_adapter *adapter, +const u8 **bootld) {u32 offs =QLCNIC_BOOTLD_START ; -struct uni_data_desc *data_desc; - - data_desc =qlcnic_get_data_desc (adapter,QLCNIC_UNI_DIR_SECT_BOOTLD , -QLCNIC_UNI_BOOTLD_IDX_OFF ); +struct qlcnic_uni_data data; +int ret; -if (adapter->ahw->fw_type ==QLCNIC_UNIFIED_ROMIMAGE ) - offs =le32_to_cpu (data_desc->findex); +if (adapter->ahw->fw_type ==QLCNIC_UNIFIED_ROMIMAGE ) { + ret =qlcnic_get_data_desc (adapter,QLCNIC_UNI_DIR_SECT_BOOTLD , +QLCNIC_UNI_BOOTLD_IDX_OFF , &data); +if (ret || data.size <QLCNIC_UNI_BOOTLD_SIZE ) +return -EINVAL ; + offs = data.offset; + }else if (!qlcnic_rom_range_valid (adapter->fw->size, offs, +QLCNIC_UNI_BOOTLD_SIZE )) { +return -EINVAL ; + } -return (u8 *)&adapter->fw->data[offs]; + *bootld = adapter->fw->data + offs; +return 0 ; } -static u8 * -qlcnic_get_fw_offs (struct qlcnic_adapter *adapter) +static int qlcnic_get_fw_data (struct qlcnic_adapter *adapter, +const u8 **image,u32 *image_size) {u32 offs =QLCNIC_IMAGE_START ; -struct uni_data_desc *data_desc; - - data_desc =qlcnic_get_data_desc (adapter,QLCNIC_UNI_DIR_SECT_FW , -QLCNIC_UNI_FIRMWARE_IDX_OFF ); -if (adapter->ahw->fw_type ==QLCNIC_UNIFIED_ROMIMAGE ) - offs =le32_to_cpu (data_desc->findex); - -return (u8 *)&adapter->fw->data[offs]; -} +struct qlcnic_uni_data data; +int ret; -static u32 qlcnic_get_fw_size (struct qlcnic_adapter *adapter) -{ -struct uni_data_desc *data_desc; -const u8 *unirom = adapter->fw->data; +if (adapter->ahw->fw_type ==QLCNIC_UNIFIED_ROMIMAGE ) { + ret =qlcnic_get_data_desc (adapter,QLCNIC_UNI_DIR_SECT_FW , +QLCNIC_UNI_FIRMWARE_IDX_OFF , &data); +if (ret) +return ret; + offs = data.offset; + *image_size = data.size; + }else { +if (!qlcnic_rom_range_valid (adapter->fw->size, +QLCNIC_FW_SIZE_OFFSET , +sizeof (__le32))) +return -EINVAL ; + *image_size =get_unaligned_le32 (adapter->fw->data + +QLCNIC_FW_SIZE_OFFSET ); + } - data_desc =qlcnic_get_data_desc (adapter,QLCNIC_UNI_DIR_SECT_FW , -QLCNIC_UNI_FIRMWARE_IDX_OFF ); +if (!qlcnic_rom_range_valid (adapter->fw->size, offs, *image_size)) +return -EINVAL ; -if (adapter->ahw->fw_type ==QLCNIC_UNIFIED_ROMIMAGE ) -return le32_to_cpu (data_desc->size); -else -return le32_to_cpu (*(__le32 *)&unirom[QLCNIC_FW_SIZE_OFFSET ]); + *image = adapter->fw->data + offs; +return 0 ; }static u32 qlcnic_get_fw_version (struct qlcnic_adapter *adapter) { -struct uni_data_desc *fw_data_desc; +char ver_str[QLCNIC_UNI_VERSION_TAIL_SIZE +1 ];const struct firmware *fw = adapter->fw; +struct qlcnic_uni_data data;u32 major, minor, sub; -__le32 version_offset; -const u8 *ver_str;int i, ret; -if (adapter->ahw->fw_type !=QLCNIC_UNIFIED_ROMIMAGE ) { - version_offset = *(__le32 *)&fw->data[QLCNIC_FW_VERSION_OFFSET ]; -return le32_to_cpu (version_offset); - } +if (adapter->ahw->fw_type !=QLCNIC_UNIFIED_ROMIMAGE ) +return get_unaligned_le32 (fw->data +QLCNIC_FW_VERSION_OFFSET ); + + ret =qlcnic_get_data_desc (adapter,QLCNIC_UNI_DIR_SECT_FW , +QLCNIC_UNI_FIRMWARE_IDX_OFF , &data); +if (ret || data.size <QLCNIC_UNI_VERSION_TAIL_SIZE ) +return 0 ; - fw_data_desc =qlcnic_get_data_desc (adapter,QLCNIC_UNI_DIR_SECT_FW , -QLCNIC_UNI_FIRMWARE_IDX_OFF ); - ver_str = fw->data +le32_to_cpu (fw_data_desc->findex) + -le32_to_cpu (fw_data_desc->size) -17 ; +memcpy (ver_str, fw->data + data.offset + data.size - +QLCNIC_UNI_VERSION_TAIL_SIZE , +QLCNIC_UNI_VERSION_TAIL_SIZE ); + ver_str[QLCNIC_UNI_VERSION_TAIL_SIZE ] ='\0' ;for (i =0 ; i <12 ; i++) { -if (!strncmp (&ver_str[i],"REV=" ,4 )) { +if (!strncmp (ver_str + i,"REV=" ,4 )) { ret =sscanf (&ver_str[i+4 ],"%u.%u.%u " , &major, &minor, &sub);if (ret !=3 )
@@ -1034,18 +1076,15 @@ static u32 qlcnic_get_fw_version(struct qlcnic_adapter *adapter)static u32 qlcnic_get_bios_version (struct qlcnic_adapter *adapter) { -const struct firmware *fw = adapter->fw;u32 bios_ver, prd_off = adapter->file_prd_off; -u8 *version_offset; -__le32 temp; +const struct firmware *fw = adapter->fw; -if (adapter->ahw->fw_type !=QLCNIC_UNIFIED_ROMIMAGE ) { - version_offset = (u8 *)&fw->data[QLCNIC_BIOS_VERSION_OFFSET ]; -return le32_to_cpu (*(__le32 *)version_offset); - } +if (adapter->ahw->fw_type !=QLCNIC_UNIFIED_ROMIMAGE ) +return get_unaligned_le32 (fw->data +QLCNIC_BIOS_VERSION_OFFSET ); - temp = *((__le32 *)(&fw->data[prd_off]) +QLCNIC_UNI_BIOS_VERSION_OFF ); - bios_ver =le32_to_cpu (temp); + bios_ver =get_unaligned_le32 (fw->data + prd_off + +QLCNIC_UNI_BIOS_VERSION_OFF * +sizeof (__le32));return (bios_ver <<16 ) + ((bios_ver >>8 ) &0xff00 ) + (bios_ver >>24 ); }
@@ -1106,24 +1145,26 @@ static const char *fw_name[] = {int qlcnic_load_firmware (struct qlcnic_adapter *adapter) { -__le64 *ptr64; -u32 i, flashaddr, size;const struct firmware *fw = adapter->fw;struct pci_dev *pdev = adapter->pdev; +const u8 *bootld, *image; +u32 i, flashaddr, image_size; +int ret;dev_info (&pdev->dev,"loading firmware from %s\n" , fw_name[adapter->ahw->fw_type]);if (fw) { +u32 words, remainder;u64 data; - size = (QLCNIC_IMAGE_START -QLCNIC_BOOTLD_START ) /8 ; - - ptr64 = (__le64 *)qlcnic_get_bootld_offs (adapter); + ret =qlcnic_get_bootld_data (adapter, &bootld); +if (ret) +return ret; flashaddr =QLCNIC_BOOTLD_START ; -for (i =0 ; i < size; i++) { - data =le64_to_cpu (ptr64[i]); +for (i =0 ; i <QLCNIC_UNI_BOOTLD_SIZE /sizeof (u64); i++) { + data =get_unaligned_le64 (bootld + i *sizeof (u64));if (qlcnic_pci_mem_write_2M (adapter, flashaddr, data))return -EIO ;
@@ -1131,13 +1172,15 @@ qlcnic_load_firmware(struct qlcnic_adapter *adapter) flashaddr +=8 ; } - size =qlcnic_get_fw_size (adapter) /8 ; - - ptr64 = (__le64 *)qlcnic_get_fw_offs (adapter); + ret =qlcnic_get_fw_data (adapter, &image, &image_size); +if (ret) +return ret; + words = image_size /sizeof (u64); + remainder = image_size %sizeof (u64); flashaddr =QLCNIC_IMAGE_START ; -for (i =0 ; i < size; i++) { - data =le64_to_cpu (ptr64[i]); +for (i =0 ; i < words; i++) { + data =get_unaligned_le64 (image + i *sizeof (u64));if (qlcnic_pci_mem_write_2M (adapter, flashaddr, data))
@@ -1146,9 +1189,11 @@ qlcnic_load_firmware(struct qlcnic_adapter *adapter) flashaddr +=8 ; } - size =qlcnic_get_fw_size (adapter) %8 ; -if (size) { - data =le64_to_cpu (ptr64[i]); +if (remainder) { +__le64 tail =0 ; + +memcpy (&tail, image + words *sizeof (u64), remainder); + data =le64_to_cpu (tail);if (qlcnic_pci_mem_write_2M (adapter, flashaddr, data))
@@ -1156,10 +1201,9 @@ qlcnic_load_firmware(struct qlcnic_adapter *adapter) } }else { -u64 data; -u32 hi, lo; -int ret;struct qlcnic_flt_entry bootld_entry; +u32 hi, lo, size; +u64 data; ret =qlcnic_get_flt_entry (adapter,QLCNIC_BOOTLD_REGION , &bootld_entry);
@@ -1200,26 +1244,33 @@ qlcnic_load_firmware(struct qlcnic_adapter *adapter)static int qlcnic_validate_firmware (struct qlcnic_adapter *adapter) { -u32 val; -u32 ver, bios, min_size; -struct pci_dev *pdev = adapter->pdev;const struct firmware *fw = adapter->fw; +struct pci_dev *pdev = adapter->pdev;u8 fw_type = adapter->ahw->fw_type; +u32 ver, bios, min_size; +const u8 *data; +u32 data_size; +u32 val; + +if (fw_type ==QLCNIC_UNIFIED_ROMIMAGE ) + min_size =QLCNIC_UNI_FW_MIN_SIZE ; +else + min_size =QLCNIC_FW_MIN_SIZE ; + +if (fw->size < min_size) +return -EINVAL ;if (fw_type ==QLCNIC_UNIFIED_ROMIMAGE ) {if (qlcnic_validate_unified_romimage (adapter))return -EINVAL ; - - min_size =QLCNIC_UNI_FW_MIN_SIZE ; }else { - val =le32_to_cpu (*(__le32 *)&fw->data[QLCNIC_FW_MAGIC_OFFSET ]); + val =get_unaligned_le32 (fw->data +QLCNIC_FW_MAGIC_OFFSET );if (val !=QLCNIC_BDINFO_MAGIC )return -EINVAL ; - - min_size =QLCNIC_FW_MIN_SIZE ; } -if (fw->size < min_size) +if (qlcnic_get_bootld_data (adapter, &data) || +qlcnic_get_fw_data (adapter, &data, &data_size))return -EINVAL ; val =qlcnic_get_fw_version (adapter);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1526/1887 ALSA: mtpav: shut down output timer before card teardown
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
[ Upstream commit c73cb5b7c159246dd572277c668851a56e516019 ]
snd_mtpav_output_timer() rearms chip->timer while holding chip->spinlock and accesses the card-private mtpav state.
snd_mtpav_free() currently takes the same lock and calls timer_delete() when the timer is active. This only removes a pending timer; it does not wait for a callback that is already running and does not prevent the callback from rearming the timer.
A callback running on another CPU can therefore continue after snd_mtpav_free() releases the lock and access the card-private state while the card is being torn down. It can also rearm the timer after timer_delete() has returned.
Call timer_shutdown_sync() without holding chip->spinlock. This waits for any running callback to finish and prevents further rearming before the card-private mtpav state is released.
--- sound/drivers/mtpav.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
sound/drivers/mtpav.c +1 -5
@@ -663,12 +663,8 @@ static int snd_mtpav_get_RAWMIDI(struct mtpav *mcard)static void snd_mtpav_free (struct snd_card *card) {struct mtpav *crd = card->private_data; -unsigned long flags; -spin_lock_irqsave (&crd->spinlock, flags); -if (crd->istimer >0 ) -snd_mtpav_remove_output_timer (crd); -spin_unlock_irqrestore (&crd->spinlock, flags); +timer_shutdown_sync (&crd->timer); }/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1527/1887 smb: client: fix request buffer leak in smb2_new_read_req()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christopher Lusk <[email protected]>
[ Upstream commit deb6468f4164640e4dc875f008aa449cf55987a5 ]
smb2_new_read_req() allocates the request buffer with smb2_plain_req_init() but only publishes it to the caller with *buf = req at the very end of the function. Two error returns sit in between:
rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server, (void **) &req, total_len); if (rc) return rc; if (server == NULL) return -ECONNABORTED; [...] rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->subreq.io_iter, true, need_invalidate); if (!rdata->mr) return -EAGAIN;
On either of them the buffer is neither released nor handed back, so it is leaked. The caller cannot clean up after it: smb2_async_readv() does 'goto out' on a non-zero return, which skips the cifs_small_buf_release(buf) at async_readv_out, and buf has not been assigned at that point in any case.
The write path has never had this problem. smb2_async_writev() registers the memory region inline and jumps to its release label instead of returning:
wdata->mr = smbd_register_mr(...);
if (!wdata->mr) {
rc = -EAGAIN;
goto async_writev_out;
}Commit b7972092199f ("cifs: smbd: Retry on memory registration failure") changed both sides from -ENOBUFS to -EAGAIN in a single patch, which puts the two shapes next to each other.
Only the -EAGAIN return is reachable in practice, because smb2_plain_req_init() calls smb2_reconnect() first and that already fails with -EIO when server is NULL, before anything is allocated. Both returns are given the same treatment here rather than leaving one of them correct only by accident.
Because -EAGAIN is a replayable error, the failure also reaches the retry block at the end of smb2_async_readv(), which marks the subrequest NETFS_SREQ_NEED_RETRY, so a failing registration can be retried rather than ending the I/O, and every attempt that reaches it leaks another buffer. smb2_should_replay() short-circuits on tcon->retry, so on a hard mount the attempt count is not bounded by the retrans setting.
Only the asynchronous read path is affected. The synchronous SMB2_read() caller passes rdata == NULL and the memory registration block is guarded on rdata.
The memory registration failure path was pointed out by the Sashiko AI reviewer while it was reviewing an unrelated patch to smb2_async_readv().
--- fs/smb/client/smb2pdu.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
fs/smb/client/smb2pdu.c +12 -4
@@ -4287,8 +4287,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,if (rc)return rc; -if (server ==NULL ) -return -ECONNABORTED ; +if (!server) { + rc = -ECONNABORTED ; + goto free_req; + } shdr = &req->hdr; shdr->Id.SyncId.ProcessId =cpu_to_le32 (io_parms->pid);
@@ -4319,8 +4321,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len, server->smbd_conn, rdata->pages, rdata->nr_pages, rdata->page_offset, rdata->tailsz, true, need_invalidate); -if (!rdata->mr) -return -EAGAIN ; +if (!rdata->mr) { + rc = -EAGAIN ; + goto free_req; + } req->Channel =SMB2_CHANNEL_RDMA_V1_INVALIDATE ;if (need_invalidate)
@@ -4363,6 +4367,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len, *buf = req;return rc; + +free_req: +cifs_small_buf_release (req); +return rc; } static void
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1528/1887 net: add missing ref_tracker_dir_exit() to alloc_netdev_mqs()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tetsuo Handa <[email protected]>
[ Upstream commit 0b1c2af8a22c35cb099c735c2f63ea3ba757557d ]
sashiko is reporting that trying to read /sys/kernel/debug/ref_tracker/* causes use-afer-free crash when either alloc_percpu() or dev_addr_init() in alloc_netdev_mqs() failed, for commit 4d92b95ff2f9 ("net: add net device refcount tracker infrastructure") added ref_tracker_dir_exit() to only free_netdev() path.
--- net/core/dev.c | 1 + 1 file changed, 1 insertion(+)
net/core/dev.c +1 -0
@@ -10873,6 +10873,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,free_percpu (dev->pcpu_refcnt); free_dev:#endif +ref_tracker_dir_exit (&dev->refcnt_tracker);netdev_freemem (dev);return NULL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1529/1887 vsock: dont check the listeners sk_err in vsock_accept()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nguyen Dinh Phi <[email protected]>
[ Upstream commit b8c899cf5e7be29840a172c183dedd8d3e7a0287 ]
Syzbot reported an issue which can be reproduced with these steps:
r0 = socket(AF_VSOCK, SOCK_STREAM, 0)
bind(r0, {VMADDR_CID_ANY, PORT})
connect(r0, {VMADDR_CID_LOCAL, PORT}) -> -1, EPROTO (self-connect)
listen(r0, backlog) -> 0
r1 = socket(AF_VSOCK, SOCK_STREAM, 0)
connect(r1, {VMADDR_CID_LOCAL, PORT}) -> 0
accept(r0) -> -1, EPROTO (stale sk_err)Basically, it creates a socket (r0) and triggers a self-connect after binding it. This self-connect fails with EPROTO because it loops back to r0 while the socket is still in the TCP_SYN_SENT state, causing it to be incorrectly dispatched to the connecting-client path. The unexpected packet type encountered there sets sk_err to EPROTO.
After that, it invokes a listen() call on the same socket. This listen() call succeeds because the kernel's listening path never inspects or clears sk_err. Then, a new socket (r1) is created as a normal client and connects to r0. However, vsock_accept() rejects this incoming connection because the listener's sk_err still holds the EPROTO error from the earlier failed self-connect.
This rejection causes the child socket created for r1's connection to never be freed on virtio or hyperv transports; only the VMCI transport implements pending_work to revisit and clean up a rejected socket.
For a non-blocking connect(), vsock_connect() may return -EINPROGRESS immediately, and vsock_connect_timeout() can later set sk->sk_err asynchronously.
Since no vsock transport ever sets sk_err on a socket while it is in TCP_LISTEN state, checking it in vsock_accept() serves no purpose and only carries forward errors left behind by earlier, unrelated connection attempts on the same socket. Remove the checks so accept() no longer rejects valid incoming connections because of a stale error, which also avoids the resource leak described above.
--- net/vmw_vsock/af_vsock.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
net/vmw_vsock/af_vsock.c +1 -5
@@ -1586,8 +1586,7 @@ static int vsock_accept(struct socket *sock, struct socket *newsock, int flags, timeout =sock_rcvtimeo (listener, flags &O_NONBLOCK );prepare_to_wait (sk_sleep (listener), &wait,TASK_INTERRUPTIBLE ); -while ((connected =vsock_dequeue_accept (listener)) ==NULL && - listener->sk_err ==0 ) { +while ((connected =vsock_dequeue_accept (listener)) ==NULL ) {release_sock (listener); timeout =schedule_timeout (timeout);finish_wait (sk_sleep (listener), &wait);
@@ -1605,9 +1604,6 @@ static int vsock_accept(struct socket *sock, struct socket *newsock, int flags, }finish_wait (sk_sleep (listener), &wait); -if (listener->sk_err) - err = -listener->sk_err; -if (connected) {sk_acceptq_removed (listener);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1530/1887 fuse: check for NULL root inode in fuse_fill_super_submount
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baokun Li <[email protected]>
[ Upstream commit 928f659a3e3650978a5b4829cc982324f72b474b ]
fuse_iget() can return NULL when its inode allocation fails, but fuse_fill_super_submount() passed the result straight to get_fuse_inode() and decremented fi->nlookup without checking it:
root = fuse_iget(sb, parent_fi->nodeid, ...);
fi = get_fuse_inode(root);
fi->nlookup--;Inside fuse_iget() the inode allocation can fail and return NULL. The submount root takes the iget5_locked() path, whose alloc_inode() can fail under memory pressure (the auto-submount branch can fail the same way in new_inode() or fuse_alloc_submount_lookup()):
inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set,
&nodeid);
if (!inode)
return NULL;A NULL root makes get_fuse_inode() a container_of() on NULL and the nlookup decrement a write to a bogus address, oopsing the mount. With CONFIG_KASAN the following null pointer dereference is reported when the root inode allocation of an auto-submount fails (e.g. under memory pressure):
==================================================================
BUG: KASAN: null-ptr-deref in fuse_get_tree_submount+0x656/0x8b0 Read of size 8 at addr 00000000000002b0 by task ls/942 CPU: 0 PID: 942 Comm: ls Tainted: G W 6.6 #15 Call Trace: <TASK> fuse_get_tree_submount+0x656/0x8b0 vfs_get_tree+0x48/0x140 fc_mount+0x13/0x50 fuse_dentry_automount+0x7a/0xb0 __traverse_mounts+0xca/0x330 step_into+0x339/0xac0 path_lookupat+0xc5/0x2f0 filename_lookup+0x163/0x2a0 vfs_statx+0xd5/0x200 do_statx+0x83/0xd0 __x64_sys_statx+0xa0/0xc0 do_syscall_64+0x37/0x90 entry_SYSCALL_64_after_hwframe+0x78/0xe2 </TASK> ==================================================================
Return -ENOMEM instead; the caller tears down the partially built superblock on error, matching the other error returns in this function.
--- fs/fuse/inode.c | 2 ++ 1 file changed, 2 insertions(+)
fs/fuse/inode.c +2 -0
@@ -1537,6 +1537,8 @@ static int fuse_fill_super_submount(struct super_block *sb,fuse_fill_attr_from_inode (&root_attr, parent_fi); root =fuse_iget (sb, parent_fi->nodeid,0 , &root_attr,0 ,0 ); +if (!root) +return -ENOMEM ;/* * This inode is just a duplicate, so it is not looked up and * its nlookup should not be incremented. fuse_iget() does
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1531/1887 bpf, xdp: move offload check into dev_xdp_install()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit ad27ed7d2309419a129078d781504f486b1b469a ]
bpf_xdp_link_update() calls dev_xdp_install() directly and skips dev_xdp_attach(), so the checks in dev_xdp_attach() do not run. A user can make an XDP link with a normal program and then swap in an offloaded or device-bound program with BPF_LINK_UPDATE, which puts it on the software path.
dev_xdp_install() is the one place all three paths go through:
"ip link set xdp" and BPF_LINK_CREATE reach it via dev_xdp_attach(), and BPF_LINK_UPDATE calls it directly. So move the program checks (offloaded, bound to another device, device-bound in generic mode, native vs generic, DEVMAP and CPUMAP) there, and keep only the netlink-flag check (XDP_FLAGS_UPDATE_IF_NOEXIST) in dev_xdp_attach().
--- net/core/dev.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-)
net/core/dev.c +23 -20
@@ -9249,6 +9249,29 @@ static int dev_xdp_install(struct net_device *dev, enum bpf_xdp_mode mode,struct netdev_bpf xdp;int err; +if (prog) { +enum bpf_xdp_mode other_mode = mode ==XDP_MODE_SKB + ?XDP_MODE_DRV :XDP_MODE_SKB ; +bool offload = mode ==XDP_MODE_HW ; + +if (!offload &&dev_xdp_prog (dev, other_mode)) { +NL_SET_ERR_MSG (extack,"Native and generic XDP can't be active at the same time" ); +return -EEXIST ; + } +if (!offload &&bpf_prog_is_dev_bound (prog->aux)) { +NL_SET_ERR_MSG (extack,"Using device-bound program without HW_MODE flag is not supported" ); +return -EINVAL ; + } +if (prog->expected_attach_type ==BPF_XDP_DEVMAP ) { +NL_SET_ERR_MSG (extack,"BPF_XDP_DEVMAP programs can not be attached to a device" ); +return -EINVAL ; + } +if (prog->expected_attach_type ==BPF_XDP_CPUMAP ) { +NL_SET_ERR_MSG (extack,"BPF_XDP_CPUMAP programs can not be attached to a device" ); +return -EINVAL ; + } + } +memset (&xdp,0 ,sizeof (xdp)); xdp.command = mode ==XDP_MODE_HW ?XDP_SETUP_PROG_HW :XDP_SETUP_PROG ; xdp.extack = extack;
@@ -9377,30 +9400,10 @@ static int dev_xdp_attach(struct net_device *dev, struct netlink_ext_ack *extack new_prog = link->link.prog;if (new_prog) { -bool offload = mode ==XDP_MODE_HW ; -enum bpf_xdp_mode other_mode = mode ==XDP_MODE_SKB - ?XDP_MODE_DRV :XDP_MODE_SKB ; -if ((flags &XDP_FLAGS_UPDATE_IF_NOEXIST ) && cur_prog) {NL_SET_ERR_MSG (extack,"XDP program already attached" );return -EBUSY ; } -if (!offload &&dev_xdp_prog (dev, other_mode)) { -NL_SET_ERR_MSG (extack,"Native and generic XDP can't be active at the same time" ); -return -EEXIST ; - } -if (!offload &&bpf_prog_is_dev_bound (new_prog->aux)) { -NL_SET_ERR_MSG (extack,"Using device-bound program without HW_MODE flag is not supported" ); -return -EINVAL ; - } -if (new_prog->expected_attach_type ==BPF_XDP_DEVMAP ) { -NL_SET_ERR_MSG (extack,"BPF_XDP_DEVMAP programs can not be attached to a device" ); -return -EINVAL ; - } -if (new_prog->expected_attach_type ==BPF_XDP_CPUMAP ) { -NL_SET_ERR_MSG (extack,"BPF_XDP_CPUMAP programs can not be attached to a device" ); -return -EINVAL ; - } }/* don't call drivers if the effective program didn't change */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1532/1887 NFSv4: Fix incorrect argument passed to nfs4_delete_lease() in nfs4_add_lease()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhansong Gao <[email protected]>
[ Upstream commit 468e458ffde907ba19acd2102ca1fbb8f6fbede2 ]
When nfs4_add_lease() races with a delegation return, it calls nfs4_delete_lease() to clean up. Previously, it passed priv, which can legitimately be NULL. Passing a NULL priv eventually leads to a NULL pointer dereference in generic_setlease().
--- fs/nfs/nfs4proc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/nfs/nfs4proc.c +3 -1
@@ -7640,6 +7640,7 @@ static int nfs4_add_lease(struct file *file, long arg, struct file_lock **lease, {struct inode *inode =file_inode (file);fmode_t type = arg ==F_RDLCK ?FMODE_READ :FMODE_WRITE ; +fl_owner_t owner = (*lease)->fl_owner;int ret;/* No delegation, no lease */
@@ -7649,7 +7650,8 @@ static int nfs4_add_lease(struct file *file, long arg, struct file_lock **lease,if (ret ||nfs4_have_delegation (inode, type))return ret;/* We raced with a delegation return */ -nfs4_delete_lease (file, priv); +dprintk ("%s: raced with a delegation return\n" , __func__); +nfs4_delete_lease (file, &owner);return -EAGAIN ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1533/1887 NFSv4/flexfiles: fix NULL dereference for NFSv4.0 data servers
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeuk Kim <[email protected]>
[ Upstream commit 2b03ebbf8d5e8f6af4ecd6c65375232dd1ec32cc ]
flexfiles accepts NFSv4.0 data servers, but two NFSv4 code paths assume the data server client has a session. Unlike NFSv4.1+, an NFSv4.0 client has no session (clp->cl_session is NULL; it uses clp->cl_slot_tbl), so I/O to a v4.0 flexfiles DS oopses:
- nfs4_init_ds_session() dereferences clp->cl_session->session_state while seeding the DS lease. It also only seeds cl_lease_time when NFS4_SESSION_INITING is set; without a session that never happens, so cl_lease_time stays 0 and nfs4_renew_state() busy-loops, requeuing every 5 seconds. Seed the lease whenever there is no session and return before touching session state.
- ff_layout_async_handle_error_v4() dereferences
clp->cl_session->fc_slot_table on every DS I/O error. Fall back to the
v4.0 transport slot table (clp->cl_slot_tbl) when there is no session.--- fs/nfs/flexfilelayout/flexfilelayout.c | 3 ++- fs/nfs/nfs4session.c | 16 +++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-)
fs/nfs/flexfilelayout/flexfilelayout.c +2 -1
@@ -1127,7 +1127,8 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task,struct pnfs_layout_hdr *lo = lseg->pls_layout;struct inode *inode = lo->plh_inode;struct nfs4_deviceid_node *devid =FF_LAYOUT_DEVID_NODE (lseg, idx); -struct nfs4_slot_table *tbl = &clp->cl_session->fc_slot_table; +struct nfs4_slot_table *tbl =nfs4_has_session (clp) ? + &clp->cl_session->fc_slot_table : clp->cl_slot_tbl;switch (op_status) {case NFS4_OK :
fs/nfs/nfs4session.c +11 -5
@@ -634,16 +634,22 @@ int nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time)int ret;spin_lock (&clp->cl_lock); -if (test_and_clear_bit (NFS4_SESSION_INITING , &session->session_state)) { -/* -* Do not set NFS_CS_CHECK_LEASE_TIME instead set the -* DS lease to be equal to the MDS lease. -*/ +/* +* Do not set NFS_CS_CHECK_LEASE_TIME instead set the +* DS lease to be equal to the MDS lease. +* +* A v4.0 DS has no session, so seed the lease every time. +*/ +if (!session || +test_and_clear_bit (NFS4_SESSION_INITING , &session->session_state)) { clp->cl_lease_time = lease_time; clp->cl_last_renewal = jiffies; }spin_unlock (&clp->cl_lock); +if (!session) +return 0 ; + ret =nfs41_check_session_ready (clp);if (ret)return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1534/1887 pnfs/blocklayout: Fix device leaks on parse failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: ZhangGuoDong <[email protected]>
[ Upstream commit c056f817e4200fb18079d5052c273a22f191ff0a ]
bl_parse_concat() and bl_parse_stripe() allocate a child device array and then parse each child in turn. If parsing a child fails, the failed child is not counted in nr_children and the parent may be left with a children array that bl_free_device() will not release when nr_children is zero.
Release the failed child and the already parsed children before returning the error. Also make bl_free_device() release the child array whenever the children pointer is set, so that partially initialised concat or stripe devices are cleaned up correctly.
bl_parse_scsi() can also fail after assigning d->bdev_file and dropping the file reference. Clear the pointer after fput() so that an outer cleanup path does not put it again.
--- fs/nfs/blocklayout/dev.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
fs/nfs/blocklayout/dev.c +15 -4
@@ -16,12 +16,14 @@static void bl_free_device (struct pnfs_block_dev *dev) { -if (dev->nr_children) { +if (dev->children) {int i;for (i =0 ; i < dev->nr_children; i++)bl_free_device (&dev->children[i]);kfree (dev->children); + dev->children =NULL ; + dev->nr_children =0 ; }else {if (dev->pr_registered) { const struct pr_ops *ops =
@@ -34,8 +36,10 @@ bl_free_device(struct pnfs_block_dev *dev)pr_err ("failed to unregister PR key.\n" ); } -if (dev->bdev) +if (dev->bdev) {blkdev_put (dev->bdev,FMODE_READ |FMODE_WRITE ); + dev->bdev =NULL ; + } } }
@@ -375,6 +379,7 @@ bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d, out_blkdev_put:blkdev_put (d->bdev,FMODE_READ |FMODE_WRITE ); + d->bdev =NULL ;return error; }
@@ -410,8 +415,11 @@ bl_parse_concat(struct nfs_server *server, struct pnfs_block_dev *d,for (i =0 ; i < v->concat.volumes_count; i++) { ret =bl_parse_deviceid (server, &d->children[i], volumes, v->concat.volumes[i], gfp_mask); -if (ret) +if (ret) { +bl_free_device (&d->children[i]); +bl_free_device (d);return ret; + } d->nr_children++; d->children[i].start += len;
@@ -439,8 +447,11 @@ bl_parse_stripe(struct nfs_server *server, struct pnfs_block_dev *d,for (i =0 ; i < v->stripe.volumes_count; i++) { ret =bl_parse_deviceid (server, &d->children[i], volumes, v->stripe.volumes[i], gfp_mask); -if (ret) +if (ret) { +bl_free_device (&d->children[i]); +bl_free_device (d);return ret; + } d->nr_children++; len += d->children[i].len;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1535/1887 smb/server: fix session leak in ksmbd_session_register()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ze Tan <[email protected]>
[ Upstream commit 99b25b046e47e4904373cfeb445c5483f1633d88 ]
See the procedure below:
smb2_sess_setup
ksmbd_smb2_session_create
__session_create
atomic_set(&sess->refcnt, 2)
hash_add(sessions_table, &sess->hlist, sess->id)
ksmbd_session_register
xa_store(&conn->sessions, sess->id, sess) // fail
ksmbd_user_session_put
atomic_dec(&sess->refcnt) // refcnt is 1, session is not freedRemove the session from sessions_table and drop its table reference if xa_store() fails.
--- fs/smb/server/mgmt/user_session.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
fs/smb/server/mgmt/user_session.c +12 -1
@@ -212,10 +212,21 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)int ksmbd_session_register (struct ksmbd_conn *conn,struct ksmbd_session *sess) { +int ret; + sess->dialect = conn->dialect;memcpy (sess->ClientGUID, conn->ClientGUID,SMB2_CLIENT_GUID_SIZE );ksmbd_expire_session (conn); -return xa_err (xa_store (&conn->sessions, sess->id, sess,GFP_KERNEL )); + ret =xa_err (xa_store (&conn->sessions, sess->id, sess, +GFP_KERNEL )); +if (ret) { +down_write (&sessions_table_lock); +hash_del (&sess->hlist); +up_write (&sessions_table_lock); +ksmbd_user_session_put (sess); + } + +return ret; }static int ksmbd_chann_del (struct ksmbd_conn *conn,struct ksmbd_session *sess)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1536/1887 ksmbd: bound smb_check_perm_dacl() ACE walks by DACL size
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hang Nan <[email protected]>
[ Upstream commit 79decd88dd3f0d42e7fb0689b1a7853bfa302459 ]
smb_check_perm_dacl() validates that the DACL fits inside the NT security descriptor, but then bounds its two ACE walks by the remaining NTSD length (acl_size) rather than the DACL's declared size (pdacl_size).
When pdacl->size is smaller than the trailing NTSD buffer, bytes after the declared DACL boundary - still inside the stored security descriptor - are parsed as ACEs during access checks. A crafted DACL can place an access-granting ACE beyond pdacl->size, and the current code accepts it during SMB2_CREATE access validation, while parse_dacl() and smb_inherit_dacl() stop at pdacl_size.
Bound both ACE walks by pdacl_size to match the DACL boundary semantics used elsewhere in the server.
Validation:
- semantic KUnit harness shows the post-boundary ACE is selected before the fix and rejected (EACCES) after it
- linux master (7.2-rc6), x86_64
--- fs/smb/server/smbacl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/smb/server/smbacl.c +2 -2
@@ -1483,7 +1483,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,DELETE ; ace = (struct smb_ace *)((char *)pdacl +sizeof (struct smb_acl )); - aces_size = acl_size -sizeof (struct smb_acl ); + aces_size = pdacl_size -sizeof (struct smb_acl );for (i =0 ; i <le32_to_cpu (pdacl->num_aces); i++) {if (aces_size < offsetof(struct smb_ace , sid) +CIFS_SID_BASE_SIZE )
@@ -1507,7 +1507,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,id_to_sid (uid, sid_type, &sid); ace = (struct smb_ace *)((char *)pdacl +sizeof (struct smb_acl )); - aces_size = acl_size -sizeof (struct smb_acl ); + aces_size = pdacl_size -sizeof (struct smb_acl );for (i =0 ; i <le32_to_cpu (pdacl->num_aces); i++) {if (aces_size < offsetof(struct smb_ace , sid) +CIFS_SID_BASE_SIZE )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1537/1887 ksmbd: detach blocked lock requests before freeing
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 215e8816b1ac25176d911abb8704390413ccee4b ]
A file_lock retained by ksmbd for byte-range lock bookkeeping can still be part of the VFS blocked-request graph. In particular, the VFS can chain a new waiter below an already blocked request through flc_blocked_requests. The ksmbd_file reference count does not cover that graph.
Both __ksmbd_close_fd() and the cross-request unlock path free these retained file_lock objects directly. If a dependent waiter is still attached, locks_release_private() hits BUG_ON(!list_empty(&flc->flc_blocked_requests)). The same lifetime mismatch can leave a freed ksmbd_lock reachable through its request-local llist.
Detach the file_lock from the blocked-request graph before freeing it in the close, cross-request unlock, and rollback paths. locks_delete_block() also wakes requests chained below the object. Remove llist when a completed lock is published so a globally visible ksmbd_lock no longer points into the submitting worker's stack.
--- fs/smb/server/smb2pdu.c | 14 ++++++++++---- fs/smb/server/vfs_cache.c | 8 +++++--- 2 files changed, 15 insertions(+), 7 deletions(-)
fs/smb/server/smb2pdu.c +10 -4
@@ -7081,6 +7081,12 @@ static void smb2_remove_blocked_lock(void **argv)wake_up (&flock->fl_wait); } +static void smb2_free_lock (struct file_lock *flock) +{ +ksmbd_vfs_posix_lock_unblock (flock); +locks_free_lock (flock); +} +static inline bool lock_defer_pending (struct file_lock *fl) {/* check pending lock waiters */
@@ -7234,14 +7240,14 @@ int smb2_lock(struct ksmbd_work *work) cmp_lock->end == smb_lock->end && !lock_defer_pending(cmp_lock->fl)) { nolock =0 ; -list_del (&cmp_lock->flist); -list_del (&cmp_lock->clist); +list_del_init (&cmp_lock->flist); +list_del_init (&cmp_lock->clist); cmp_lock->conn =NULL ;spin_unlock (&conn->llist_lock);up_read (&conn_list_lock);ksmbd_conn_put (conn); -locks_free_lock (cmp_lock->fl); +smb2_free_lock (cmp_lock->fl);kfree (cmp_lock); goto out_check_cl; }
@@ -7439,7 +7445,7 @@ int smb2_lock(struct ksmbd_work *work)spin_unlock (&conn->llist_lock);ksmbd_conn_put (conn); -locks_free_lock (smb_lock->fl); +smb2_free_lock (smb_lock->fl);if (rlock)locks_free_lock (rlock);kfree (smb_lock);
fs/smb/server/vfs_cache.c +5 -3
@@ -353,8 +353,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)if (!IS_ERR_OR_NULL (filp))fput (filp); -/* because the reference count of fp is 0, it is guaranteed that -* there are not accesses to fp->lock_list. +/* +* The zero fp reference count serializes access to fp->lock_list, but +* the VFS may still have blocked requests chained below these locks. */ list_for_each_entry_safe (smb_lock, tmp_lock, &fp->lock_list, flist) {struct ksmbd_conn *conn = smb_lock->conn;
@@ -367,7 +368,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)ksmbd_conn_put (conn); } -list_del (&smb_lock->flist); +list_del_init (&smb_lock->flist); +ksmbd_vfs_posix_lock_unblock (smb_lock->fl);locks_free_lock (smb_lock->fl);kfree (smb_lock); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1538/1887 ksmbd: safely discard unregistered deferred locks
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 054bcca4cd9f00719b01f7108b51a2168fb94f15 ]
When vfs_lock_file() defers a lock, smb2_lock() puts its ksmbd_lock on rollback_list before allocating and registering the asynchronous work. If either operation fails, rollback assumes that smb_lock->conn is initialized and dereferences NULL. The deferred file_lock also remains linked into the VFS blocked-lock state while it is freed.
Keep the lock off rollback_list until async setup succeeds. On setup failures, explicitly unblock and wake the deferred lock before freeing it and its ksmbd wrapper.
--- fs/smb/server/smb2pdu.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
fs/smb/server/smb2pdu.c +12 -1
@@ -7087,6 +7087,13 @@ static void smb2_free_lock(struct file_lock *flock)locks_free_lock (flock); } +static void smb2_free_blocked_lock (struct file_lock *flock) +{ +ksmbd_vfs_posix_lock_unblock (flock); +wake_up (&flock->fl_wait); +locks_free_lock (flock); +} +static inline bool lock_defer_pending (struct file_lock *fl) {/* check pending lock waiters */
@@ -7329,11 +7336,12 @@ int smb2_lock(struct ksmbd_work *work)ksmbd_debug (SMB ,"would have to wait for getting lock\n" ); -list_add (&smb_lock->llist, &rollback_list); argv =kmalloc (sizeof (void *),GFP_KERNEL );if (!argv) { err = -ENOMEM ; +smb2_free_blocked_lock (flock); +kfree (smb_lock); goto out; } argv[0 ] = flock;
@@ -7344,8 +7352,11 @@ int smb2_lock(struct ksmbd_work *work)if (rc) {kfree (argv); err = -ENOMEM ; +smb2_free_blocked_lock (flock); +kfree (smb_lock); goto out; } +list_add (&smb_lock->llist, &rollback_list);spin_lock (&fp->f_lock);list_add (&work->fp_entry, &fp->blocked_works);spin_unlock (&fp->f_lock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1539/1887 erofs: fix interlaced ztailpacking pclusters
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gao Xiang <[email protected]>
[ Upstream commit 862427ebb81d1f6abbf74d799790e1694b37b187 ]
On-disk sizes of interlaced pclusters should be block-aligned, and ztailpacking interlaced pclusters should be invalid at all.
Currently, mkfs.erofs won't generate any interlaced pcluster with ztailpacking enabled, so this doesn't affect any existing valid filesystems.
However, crafted images can contain invalid interlaced ztailpacking pclusters, resulting in an out-of-bounds read from a kmap'd page and copying irrelevant kernel memory into userspace-visible page cache.
--- fs/erofs/zmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/erofs/zmap.c +2 -1
@@ -577,7 +577,8 @@ static int z_erofs_do_map_blocks(struct inode *inode, err = -EFSCORRUPTED ; goto unmap_out; } - afmt = vi->z_advise &Z_EROFS_ADVISE_INTERLACED_PCLUSTER ? + afmt = (vi->z_advise &Z_EROFS_ADVISE_INTERLACED_PCLUSTER ) && + !(map->m_flags &EROFS_MAP_META ) ?Z_EROFS_COMPRESSION_INTERLACED :Z_EROFS_COMPRESSION_SHIFTED ; }else {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1540/1887 ksmbd: defer publishing granted locks to prevent UAF/double-free race
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilan Dudnik <[email protected]>
[ Upstream commit 29f74f0f2e6df3b393b7b66e810136d0c64e3c59 ]
In smb2_lock(), mid-batch granted locks are published to connection-wide (conn->lock_list) and file-wide (fp->lock_list) lists immediately upon vfs_lock_file() success, while also remaining tracked on the stack-local rollback_list.
If a subsequent element in the same SMB2_LOCK request array fails validation or execution, the thread jumps to out: and walks rollback_list to undo previously granted locks. However, because the granted lock was already published to conn->lock_list, a concurrent UNLOCK request on the same connection can find the lock object and kfree() it before the rollback loop executes.
When the granting thread subsequently walks rollback_list, it dereferences and frees the already-freed ksmbd_lock structure, resulting in a Use-After-Free and Double-Free (on both ksmbd_lock and struct file_lock).
Fix this by deferring the publication of granted locks to conn->lock_list and fp->lock_list until after the entire array of lock elements has been processed without error. Mid-batch grants remain tracked exclusively on the request-local rollback_list until the whole batch succeeds, eliminating the race window.
--- fs/smb/server/smb2pdu.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-)
fs/smb/server/smb2pdu.c +15 -17
@@ -7389,16 +7389,10 @@ int smb2_lock(struct ksmbd_work *work) rsp->hdr.Status =STATUS_RANGE_NOT_LOCKED ;kfree (smb_lock); - goto out2; +/* rollback_list may still hold earlier grants */ + goto out; }else if (!rc) {list_add (&smb_lock->llist, &rollback_list); - smb_lock->conn =ksmbd_conn_get (work->conn); -spin_lock (&work->conn->llist_lock); -list_add_tail (&smb_lock->clist, - &work->conn->lock_list); -list_add_tail (&smb_lock->flist, - &fp->lock_list); -spin_unlock (&work->conn->llist_lock);ksmbd_debug (SMB ,"successful in taking lock\n" ); }else {locks_free_lock (flock);
@@ -7420,6 +7414,19 @@ int smb2_lock(struct ksmbd_work *work)if (err) goto out; +/* publish only once the whole batch has committed */ +if (!list_empty (&rollback_list)) { +spin_lock (&work->conn->llist_lock); +list_for_each_entry (smb_lock, &rollback_list, llist) { + smb_lock->conn =ksmbd_conn_get (work->conn); +list_add_tail (&smb_lock->clist, + &work->conn->lock_list); +list_add_tail (&smb_lock->flist, + &fp->lock_list); + } +spin_unlock (&work->conn->llist_lock); + } +ksmbd_fd_put (work, fp);return 0 ;
@@ -7447,15 +7454,6 @@ int smb2_lock(struct ksmbd_work *work) }list_del (&smb_lock->llist); - conn = smb_lock->conn; -spin_lock (&conn->llist_lock); -if (!list_empty (&smb_lock->flist)) -list_del (&smb_lock->flist); -list_del (&smb_lock->clist); - smb_lock->conn =NULL ; -spin_unlock (&conn->llist_lock); -ksmbd_conn_put (conn); -smb2_free_lock (smb_lock->fl);if (rlock)locks_free_lock (rlock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1541/1887 smb/server: fix null-ptr-deref in ksmbd_ipc_tree_connect_request()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: ZhangGuoDong <[email protected]>
[ Upstream commit d2ccf905f47d2344270749f4dfa905afcd3edeb0 ]
See the procedure below:
ksmbd_tree_conn_connect
ksmbd_share_config_get
share->name = kstrdup() // fail
if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) // false
// do not check `share->name`
ksmbd_ipc_tree_connect_request
strlen(share->name) // null-ptr-deref--- fs/smb/server/mgmt/share_config.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
fs/smb/server/mgmt/share_config.c +6 -1
@@ -161,6 +161,11 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,atomic_set (&share->refcount,1 );INIT_LIST_HEAD (&share->veto_list); share->name =kstrdup (name,GFP_KERNEL ); +if (!share->name) { +kill_share (share); + share =NULL ; + goto out; + }if (!test_share_config_flag (share,KSMBD_SHARE_FLAG_PIPE )) {size_t path_len;
@@ -217,7 +222,7 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work, share->path =NULL ; } } -if (ret || !share->name) { +if (ret) {kill_share (share); share =NULL ; goto out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1542/1887 ksmbd: serialize oplock close with pending break ownership
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit b0148dc5625dbfd50596ac63c7487c12e8a8ab03 ]
close may abort an in-flight oplock break while another breaker already holds an opinfo reference. Releasing pending_break wakes that waiter, but without serializing the close transition with bit acquisition it can become a new break owner through the test_and_set_bit() fast path. It can then overwrite OPLOCK_CLOSING with OPLOCK_ACK_WAIT and continue a break for a dying opinfo.
Make OPLOCK_CLOSING terminal once the opinfo is removed from the inode list. Serialize that transition, pending_break acquisition, and OPLOCK_ACK_WAIT setup with an opinfo state lock. A breaker which loses the race releases its ownership and returns -ENOENT. Explicitly wake pending_break waiters during close so they can observe the terminal state.
Also prevent ACK and timeout paths from replacing OPLOCK_CLOSING with OPLOCK_STATE_NONE.
--- fs/smb/server/oplock.c | 81 ++++++++++++++++++++++++++++++++++------- fs/smb/server/oplock.h | 1 + fs/smb/server/smb2pdu.c | 12 ++++-- 3 files changed, 77 insertions(+), 17 deletions(-)
fs/smb/server/oplock.c +67 -14
@@ -41,6 +41,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, opinfo->conn =ksmbd_conn_get (work->conn); opinfo->level =SMB2_OPLOCK_LEVEL_NONE ; opinfo->op_state =OPLOCK_STATE_NONE ; +spin_lock_init (&opinfo->state_lock); opinfo->pending_break =0 ; opinfo->fid = id; opinfo->Tid = Tid;
@@ -405,14 +406,23 @@ void close_id_del_oplock(struct ksmbd_file *fp)opinfo_del (opinfo);rcu_assign_pointer (fp->f_opinfo,NULL ); -if (opinfo->op_state ==OPLOCK_ACK_WAIT ) { - opinfo->op_state =OPLOCK_CLOSING ; -wake_up_interruptible_all (&opinfo->oplock_q); -if (opinfo->is_lease) { -atomic_set (&opinfo->breaking_cnt,0 ); -wake_up_interruptible_all (&opinfo->oplock_brk); - } - } +spin_lock (&opinfo->state_lock); +if (opinfo->op_state ==OPLOCK_ACK_WAIT && opinfo->is_lease) +atomic_set (&opinfo->breaking_cnt,0 ); +/* +* An opinfo that has been removed from the inode list is terminal. Keep +* this transition and releasing pending_break under state_lock. a breaker +* takes the same lock before it acquires pending_break or sets ACK_WAIT. +*/ + opinfo->op_state =OPLOCK_CLOSING ; +clear_bit_unlock (0 , &opinfo->pending_break); +spin_unlock (&opinfo->state_lock); +wake_up_interruptible_all (&opinfo->oplock_q); +if (opinfo->is_lease) +wake_up_interruptible_all (&opinfo->oplock_brk); +/* memory barrier is needed for wake_up_bit() */ +smp_mb__after_atomic (); +wake_up_bit (&opinfo->pending_break,0 );opinfo_count_dec (fp);atomic_dec (&opinfo->refcount);
@@ -591,10 +601,16 @@ static void wait_for_break_ack(struct oplock_info *opinfo)/* is this a timeout ? */ if (!rc) { +spin_lock (&opinfo->state_lock); +if (opinfo->op_state ==OPLOCK_CLOSING ) { +spin_unlock (&opinfo->state_lock); +return ; + }if (opinfo->is_lease) opinfo->o_lease->state =SMB2_LEASE_NONE_LE ; opinfo->level =SMB2_OPLOCK_LEVEL_NONE ; opinfo->op_state =OPLOCK_STATE_NONE ; +spin_unlock (&opinfo->state_lock); } }
@@ -606,17 +622,46 @@ static void wake_up_oplock_break(struct oplock_info *opinfo)wake_up_bit (&opinfo->pending_break,0 ); } +static bool oplock_break_set_ack_wait (struct oplock_info *opinfo) +{ +bool ret = false; + +spin_lock (&opinfo->state_lock); +if (opinfo->op_state !=OPLOCK_CLOSING ) { + opinfo->op_state =OPLOCK_ACK_WAIT ; + ret = true; + } +spin_unlock (&opinfo->state_lock); + +return ret; +} +static int oplock_break_pending (struct oplock_info *opinfo,int req_op_level) { -while (test_and_set_bit (0 , &opinfo->pending_break)) { +for (;;) { +bool closing; + +spin_lock (&opinfo->state_lock); + closing = opinfo->op_state ==OPLOCK_CLOSING ; +if (!closing && !test_and_set_bit (0 , &opinfo->pending_break)) { +spin_unlock (&opinfo->state_lock); +break ; + } +spin_unlock (&opinfo->state_lock); +if (closing) +return -ENOENT ; +wait_on_bit (&opinfo->pending_break,0 ,TASK_UNINTERRUPTIBLE );/* Not immediately break to none. */ opinfo->open_trunc =0 ; -if (opinfo->op_state ==OPLOCK_CLOSING ) +spin_lock (&opinfo->state_lock); + closing = opinfo->op_state ==OPLOCK_CLOSING ; +spin_unlock (&opinfo->state_lock); +if (closing)return -ENOENT ; -else if (opinfo->level <= req_op_level) { +if (opinfo->level <= req_op_level) {if (opinfo->is_lease == false)return 1 ;
@@ -938,7 +983,11 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level,release_async_work (in_work); } - brk_opinfo->op_state =OPLOCK_ACK_WAIT ; +if (!oplock_break_set_ack_wait (brk_opinfo)) { +atomic_dec_if_positive (&brk_opinfo->breaking_cnt); +wake_up_oplock_break (brk_opinfo); +return -ENOENT ; + } }else atomic_dec (&brk_opinfo->breaking_cnt); }else {
@@ -947,8 +996,12 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level,return err <0 ? err :0 ;if (brk_opinfo->level ==SMB2_OPLOCK_LEVEL_BATCH || - brk_opinfo->level ==SMB2_OPLOCK_LEVEL_EXCLUSIVE ) - brk_opinfo->op_state =OPLOCK_ACK_WAIT ; + brk_opinfo->level ==SMB2_OPLOCK_LEVEL_EXCLUSIVE ) { +if (!oplock_break_set_ack_wait (brk_opinfo)) { +wake_up_oplock_break (brk_opinfo); +return -ENOENT ; + } + } }if (brk_opinfo->is_lease)
fs/smb/server/oplock.h +1 -0
@@ -66,6 +66,7 @@ struct oplock_info {struct ksmbd_file *o_fp;int level;int op_state; +spinlock_t state_lock;unsigned long pending_break;u64 fid;atomic_t breaking_cnt;
fs/smb/server/smb2pdu.c +9 -3
@@ -8392,7 +8392,10 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work)smb2_set_err_rsp (work); out: - opinfo->op_state =OPLOCK_STATE_NONE ; +spin_lock (&opinfo->state_lock); +if (opinfo->op_state !=OPLOCK_CLOSING ) + opinfo->op_state =OPLOCK_STATE_NONE ; +spin_unlock (&opinfo->state_lock);wake_up_interruptible_all (&opinfo->oplock_q);opinfo_put (opinfo);ksmbd_fd_put (work, fp);
@@ -8538,9 +8541,12 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)smb2_set_err_rsp (work); } - opinfo->op_state =OPLOCK_STATE_NONE ; +spin_lock (&opinfo->state_lock); +if (opinfo->op_state !=OPLOCK_CLOSING ) + opinfo->op_state =OPLOCK_STATE_NONE ; +spin_unlock (&opinfo->state_lock);wake_up_interruptible_all (&opinfo->oplock_q); -atomic_dec (&opinfo->breaking_cnt); +atomic_dec_if_positive (&opinfo->breaking_cnt);wake_up_interruptible_all (&opinfo->oplock_brk);opinfo_put (opinfo); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1543/1887 ksmbd: free preauth sessions on connection teardown
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 06c7b1d731bc105a8644f1b70165ba8b9416cbab ]
SMB3.1.1 multichannel binding preserves the preauthentication hash in a preauth_session between the NTLM negotiate and authenticate requests. The binding NTLM negotiate allocates this object and returns STATUS_MORE_PROCESSING_REQUIRED. If the client disconnects before it sends the authenticate request, neither the authenticate nor error cleanup paths free the object.
Release any remaining preauthentication sessions when tearing down the connection. Initialize the list when allocating the connection so that this cleanup is safe regardless of the negotiated dialect.
--- fs/smb/server/connection.c | 3 +++ fs/smb/server/mgmt/user_session.c | 11 +++++++++++ fs/smb/server/mgmt/user_session.h | 1 + fs/smb/server/smb2ops.c | 1 - 4 files changed, 15 insertions(+), 1 deletion(-)
fs/smb/server/connection.c +3 -0
@@ -11,6 +11,7 @@#include "server.h" #include "smb_common.h" #include "mgmt/ksmbd_ida.h" +#include "mgmt/user_session.h" #include "connection.h" #include "transport_tcp.h" #include "transport_rdma.h"
@@ -85,6 +86,7 @@ void ksmbd_conn_free(struct ksmbd_conn *conn)kvfree (conn->request_buf);kfree (conn->preauth_info);kfree (conn->mechToken); +ksmbd_preauth_session_destroy (conn);ksmbd_conn_put (conn); }
@@ -124,6 +126,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)INIT_LIST_HEAD (&conn->conns_list);INIT_LIST_HEAD (&conn->requests);INIT_LIST_HEAD (&conn->async_requests); +INIT_LIST_HEAD (&conn->preauth_sess_table);spin_lock_init (&conn->request_lock);spin_lock_init (&conn->credits_lock);ida_init (&conn->async_ida);
fs/smb/server/mgmt/user_session.c +11 -0
@@ -382,6 +382,17 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,return sess; } +void ksmbd_preauth_session_destroy (struct ksmbd_conn *conn) +{ +struct preauth_session *sess, *tmp; + +list_for_each_entry_safe (sess, tmp, &conn->preauth_sess_table, + preauth_entry) { +list_del (&sess->preauth_entry); +kfree (sess); + } +} +static bool ksmbd_preauth_session_id_match (struct preauth_session *sess,unsigned long long id) {
fs/smb/server/mgmt/user_session.h +1 -0
@@ -98,6 +98,7 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, unsigned long long id);struct preauth_session *ksmbd_preauth_session_alloc (struct ksmbd_conn *conn,u64 sess_id); +void ksmbd_preauth_session_destroy (struct ksmbd_conn *conn);struct preauth_session *ksmbd_preauth_session_lookup (struct ksmbd_conn *conn,unsigned long long id);
fs/smb/server/smb2ops.c +0 -1
@@ -283,7 +283,6 @@ int init_smb3_11_server(struct ksmbd_conn *conn)if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL ) conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_MULTI_CHANNEL ; -INIT_LIST_HEAD (&conn->preauth_sess_table);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1544/1887 ksmbd: fix slab-out-of-bounds read in ksmbd_alloc_user()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yunseong Kim <[email protected]>
[ Upstream commit 7405d0ba294306721843bc551611775e6edef516 ]
ksmbd_alloc_user() copies resp->hash_sz bytes out of the mountd IPC login response with
user->passkey_sz = resp->hash_sz; user->passkey = kmalloc(resp->hash_sz, KSMBD_DEFAULT_GFP); if (user->passkey) memcpy(user->passkey, resp->hash, resp->hash_sz);
resp->hash_sz is a __u16 supplied by the response, but resp->hash[] is only KSMBD_REQ_MAX_HASH_SZ bytes. A malformed or malicious login response can set hash_sz well beyond that (up to 65535), so the memcpy() reads past the end of the response object. ipc_validate_msg() does not bound hash_sz, so reject any response whose hash_sz exceeds the on-stack hash[] buffer before allocating and copying.
[ 2030.238706] BUG: KASAN: slab-out-of-bounds in ksmbd_alloc_user+0x278/0x680 [ 2030.240549] Read of size 65535 at addr ffff888121bb6680 by task kworker/4:1/18611 [ 2030.242296] [ 2030.242710] CPU: 4 UID: 0 PID: 18611 Comm: kworker/4:1 Not tainted 7.1.0-next-20260623-virtme #96 PREEMPT(lazy) [ 2030.242732] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014 [ 2030.242743] Workqueue: ksmbd-io handle_ksmbd_work [ 2030.242763] Call Trace: [ 2030.242769] <TASK> [ 2030.242776] dump_stack_lvl+0xa2/0xd0 [ 2030.242794] print_address_description+0x77/0x200 [ 2030.242815] ? ksmbd_alloc_user+0x278/0x680 [ 2030.242831] print_report+0x58/0x70 [ 2030.242848] kasan_report+0x117/0x150 [ 2030.242869] ? ksmbd_alloc_user+0x278/0x680 [ 2030.242888] kasan_check_range+0x3c7/0x3f0 [ 2030.242908] ? ksmbd_alloc_user+0x278/0x680 [ 2030.242925] __asan_memcpy+0x29/0x70 [ 2030.242942] ksmbd_alloc_user+0x278/0x680 [ 2030.242960] ksmbd_login_user+0xc3/0x120 [ 2030.242978] ntlm_authenticate+0x5e6/0x1b00 [ 2030.243017] ? __pfx_ntlm_authenticate+0x10/0x10 [ 2030.243035] ? ksmbd_session_lookup+0x188/0x1d0 [ 2030.243054] ? __pfx_ksmbd_session_lookup+0x10/0x10 [ 2030.243090] ? __sanitizer_cov_trace_switch+0x7b/0x140 [ 2030.243108] smb2_sess_setup+0x1e4a/0x27b0 [ 2030.243126] ? copy_from_kernel_nofault+0x199/0x300 [ 2030.243156] ? __pfx_smb2_sess_setup+0x10/0x10 [ 2030.243173] ? get_smb2_cmd_val+0xe3/0x1c0 [ 2030.243208] handle_ksmbd_work+0x954/0x1280 [ 2030.243230] ? __pfx_handle_ksmbd_work+0x10/0x10 [ 2030.243249] ? process_scheduled_works+0xa07/0x1490 [ 2030.243270] ? process_scheduled_works+0xa07/0x1490 [ 2030.243291] process_scheduled_works+0xa70/0x1490 [ 2030.243320] ? __pfx_process_scheduled_works+0x10/0x10 [ 2030.243340] ? do_raw_spin_lock+0x130/0x300 [ 2030.243358] ? lock_is_held_type+0x7b/0x110 [ 2030.243388] worker_thread+0x932/0xe20 [ 2030.243415] kthread+0x38a/0x470 [ 2030.243431] ? __pfx_worker_thread+0x10/0x10 [ 2030.243451] ? __pfx_kthread+0x10/0x10 [ 2030.243467] ret_from_fork+0x484/0x910 [ 2030.243485] ? __pfx_ret_from_fork+0x10/0x10 [ 2030.243501] ? __switch_to+0xc77/0x12c0 [ 2030.243523] ? __pfx_kthread+0x10/0x10 [ 2030.243540] ret_from_fork_asm+0x1a/0x30 [ 2030.243564] </TASK> [ 2030.243570] [ 2030.290164] Allocated by task 19279: [ 2030.290911] kasan_save_track+0x3e/0x80 [ 2030.292179] __kasan_kmalloc+0x72/0x90 [ 2030.293217] __kvmalloc_node_noprof+0x3ff/0x6b0 [ 2030.294467] handle_generic_event+0x59b/0x750 [ 2030.295345] genl_family_rcv_msg_doit+0x238/0x340 [ 2030.296553] genl_rcv_msg+0x606/0x7b0 [ 2030.297129] netlink_rcv_skb+0x22b/0x4a0 [ 2030.298500] genl_rcv+0x2d/0x40 [ 2030.299273] netlink_unicast+0x7ba/0x930 [ 2030.300019] netlink_sendmsg+0x8c3/0xb00 [ 2030.301073] __sock_sendmsg+0xec/0x140 [ 2030.301579] __sys_sendto+0x357/0x470 [ 2030.302255] __x64_sys_sendto+0xe3/0x100 [ 2030.303425] do_syscall_64+0x135/0x460 [ 2030.304763] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 2030.305594] [ 2030.305819] The buggy address belongs to the object at ffff888121bb6640 [ 2030.305819] which belongs to the cache kmalloc-192 of size 192 [ 2030.309595] The buggy address is located 64 bytes inside of [ 2030.309595] allocated 166-byte region [ffff888121bb6640, ffff888121bb66e6) [ 2030.312484] [ 2030.312719] The buggy address belongs to the physical page: [ 2030.314315] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x121bb6 [ 2030.316481] head: order:1 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 [ 2030.318248] flags: 0x100000000000040(head|node=0|zone=2) [ 2030.319662] page_type: f5(slab) [ 2030.320242] raw: 0100000000000040 ffff8881000424c0 ffffea00047c1510 ffff888100040468 [ 2030.321911] raw: 0000000000000000 0000000000150015 00000000f5000000 0000000000000000 [ 2030.324413] head: 0100000000000040 ffff8881000424c0 ffffea00047c1510 ffff888100040468 [ 2030.326150] head: 0000000000000000 0000000000150015 00000000f5000000 0000000000000000 [ 2030.327960] head: 0100000000000001 ffffffffffffff81 00000000ffffffff 00000000ffffffff [ 2030.329615] head: ffff888121bb7ab0 0000000000000000 00000000ffffffff 0000000000000000 [ 2030.331861] page dumped because: kasan: bad access detected [ 2030.332946] [ 2030.333502] Memory state around the buggy address: [ 2030.334698] ffff888121bb6580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 2030.336475] ffff888121bb6600: fc fc fc fc fc fc fc fc 00 00 00 00 00 00 00 00 [ 2030.338143] >ffff888121bb6680: 00 00 00 00 00 00 00 00 00 00 00 00 06 fc fc fc [ 2030.339116] ^ [ 2030.341315] ffff888121bb6700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 2030.342801] ffff888121bb6780: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb [ 2030.344643] ==================================================================
Found with ksmbdzzer [2], a KSMBD fuzzer that drives libFuzzer with a kcov-dataflow [1] coverage vector: it folds each instrumented comparison/argument's runtime operand value together with its PC (the default arm mixes them as pc⊕val) so that a new operand value at a known site counts as new coverage.
[1] https://lwn.net/Articles/1077606/
[2] https://github.com/yskzalloc/kcov-dataflow
--- fs/smb/server/mgmt/user_config.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
fs/smb/server/mgmt/user_config.c +11 -0
@@ -31,6 +31,17 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) {struct ksmbd_user *user =NULL ; +/* +* resp->hash_sz is a __u16 taken from the mountd IPC login response but +* resp->hash[] is only KSMBD_REQ_MAX_HASH_SZ bytes. A malformed or +* malicious response can set hash_sz far beyond that (up to 65535), +* making the memcpy() below read past the response object +* (slab-out-of-bounds in ksmbd_alloc_user()). Reject any oversized +* hash rather than trust the length. +*/ +if (resp->hash_sz >sizeof (resp->hash)) +return NULL ; + user =kmalloc (sizeof (struct ksmbd_user ),GFP_KERNEL );if (!user)return NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1545/1887 apparmor: fix deadlock in complain-mode change_hat
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Johansen <[email protected]>
[ Upstream commit 4ec11f14d1d6fdda787d991b142537be7841d395 ]
The use of change_hat when in complain mode can cause a deadlock when the hat doesn't exist and a new learning profile is created for the missing profile. This is because change_hat() has taken the lock to search the hat list and creating the new learning profile needs to take the lock to add it to the list.
Quoted 1 line
From the bug report:
Originally found in 7.0.0 in LTS ubuntu 26.04 with pam_apparmor + su in complain mode set to change hats. Then verified in newest available vanilla kernel I've compiled to see if still present:
7.2-rc7 vanilla -> affected
checked also some other kernels:
6.18.44 vanilla -> affected
6.12.95 with debian patches -> unaffected
On systems without bug (for example 6.12.95 debian) it just prints:
aa_change_hat rc=0
On systems with bug, the executable always hangs, prints nothing and becomes unkillable. (And once stuck this way, it will cause any further hat changes to also cause the changing process to get stuck)
Then in syslog you can find hint about cause:
kernel: INFO: task hat:3409 blocked for more than 483 seconds. kernel: Not tainted 7.2.0-rc7 #1 kernel: "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. kernel: task:hat state:D stack:0 pid:3409 tgid:3409 ppid:2605 task_flags:0x400000 flags:0x00080800 kernel: Call Trace: kernel: <TASK> kernel: __schedule+0x48f/0xfe0 kernel: schedule+0x27/0xa0 kernel: schedule_preempt_disabled+0x15/0x30 kernel: __mutex_lock.constprop.0+0x569/0xa10 kernel: aa_new_learning_profile+0x15f/0x210 kernel: build_change_hat+0x19f/0x3b0 kernel: change_hat.isra.0+0x5dd/0xd60 kernel: aa_change_hat+0x2f3/0x710 kernel: aa_setprocattr_changehat+0x121/0x1f0 kernel: do_setattr+0x28c/0x340 kernel: apparmor_setselfattr+0x20/0x50 kernel: security_setselfattr+0xf6/0x110 kernel: __x64_sys_lsm_set_self_attr+0x53/0x90 kernel: do_syscall_64+0xdd/0x5e0 kernel: ? __mod_memcg_lruvec_state+0xfd/0x260 kernel: ? lruvec_stat_mod_folio+0x8d/0xd0 kernel: ? __folio_mod_stat+0x2d/0x90 kernel: ? map_anon_folio_pte_nopf+0xd1/0x1f0 kernel: ? do_anonymous_page+0x184/0xa10 kernel: ? __handle_mm_fault+0x805/0x870 kernel: ? count_memcg_events+0xef/0x230 kernel: ? handle_mm_fault+0x1f0/0x2f0 kernel: ? do_user_addr_fault+0x2bb/0x7b0 kernel: ? do_syscall_64+0x94/0x5e0 kernel: ? exc_page_fault+0x75/0x160 kernel: entry_SYSCALL_64_after_hwframe+0x76/0x7e kernel: RIP: 0033:0x7f815e134c8d kernel: RSP: 002b:00007fff6df94ea8 EFLAGS: 00000246 ORIG_RAX: 00000000000001cc kernel: RAX: ffffffffffffffda RBX: 0000556d8c81d040 RCX: 00007f815e134c8d kernel: RDX: 0000000000000046 RSI: 0000556d8c81d040 RDI: 0000000000000064 kernel: RBP: 00007fff6df94ef0 R08: 00007f815e212ac8 R09: 000000000000000c kernel: R10: 0000000000000000 R11: 0000000000000246 R12: 0000556d8c81d010 kernel: R13: 0000000000000026 R14: 0000000000000046 R15: 0000000000000064 kernel: </TASK> kernel: INFO: task hat:3409 is blocked on a mutex likely owned by task hat:3409.
To fix the issue, lift the locking out of the core of aa_new_learning_profile(), introduce a wrapper function that takes the lock where needed, and have build_change_hat() call the core function that no longer takes the lock.
In addition fix 4 other issues introduced by commit32e92764d6f8d ("apparmor: grab ns lock and refresh when looking up changehat child profiles")
- aa_get_profile_rcu() was replaced-by: aa_get_profile without the accompanying rcu_dereference_protected()
- an extra aa_get_label(label) was introduced at the start of
change_hat() without an accompanying aa_put_label() causing a
reference count leak.
- a reference count leak was introduced in the label_is_stale(label)
case, where the newest profile would be leaked instead of the
label passed to the function.
- a potential UAF when the lookup walks up the tree with new_ns != ns
the new label reference is put, and then used for the next lookup.
The mutex_lock, will block replacement, and removal in the locked
ns. However there are two cases where putting the reference can
result in the label being freed even with the lock held.
1. the label does not have a list reference (possible for temporary or special profiles) in which case the put can trigger the cleanup.
2. the new label reference is in a different namespace, which does not have a lock held on it. This extends case 1 to also include replacement, and removal that could be occurring in the namespace new is in.
--- security/apparmor/domain.c | 25 +++++++++++++++++-------- security/apparmor/include/policy.h | 2 ++ security/apparmor/policy.c | 21 ++++++++++++++++----- 3 files changed, 35 insertions(+), 13 deletions(-)
security/apparmor/domain.c +17 -8
@@ -1006,8 +1006,8 @@ static struct aa_label *build_change_hat(struct aa_profile *profile,if (!hat) { error = -ENOENT ;if (COMPLAIN_MODE (profile)) { - hat =aa_new_null_profile (profile, true, name, -GFP_KERNEL ); + hat =__aa_new_null_profile (profile, true, name, +GFP_KERNEL );if (!hat) { info ="failed null profile create" ; error = -ENOMEM ;
@@ -1043,6 +1043,7 @@ static struct aa_label *change_hat(struct aa_label *label, const char *hats[],bool sibling = false;const char *name, *info =NULL ;int i, error; +bool needput = false;AA_BUG (!label);AA_BUG (!hats);
@@ -1055,7 +1056,6 @@ static struct aa_label *change_hat(struct aa_label *label, const char *hats[],* the profiles and label, we can rely on the namespaces being live * and avoid incrementing their refcounts while grabbing the lock. */ - label =aa_get_label (label); ns =labels_ns (label); retry:
@@ -1063,15 +1063,19 @@ static struct aa_label *change_hat(struct aa_label *label, const char *hats[],if (label_is_stale (label)) { new =aa_get_newest_label (label); new_ns =labels_ns (new); + +if (needput) +/* aa_put_label() is safe to call when under lock */ +aa_put_label (label); + label = new; + needput = true; +/* check if replaced with label in parent ns, and lock there */ if (new_ns != ns) { -aa_put_label (new);mutex_unlock (&ns->lock); ns = new_ns; - label = new; +/* retry will bottom out at the root of the tree */ goto retry; } -aa_put_label (label); - label = new; }if (PROFILE_IS_HAT (labels_profile (label)))
@@ -1082,7 +1086,8 @@ static struct aa_label *change_hat(struct aa_label *label, const char *hats[], name = hats[i];label_for_each_in_ns (it,labels_ns (label), label, profile) {if (sibling &&PROFILE_IS_HAT (profile)) { - root =aa_get_profile (profile->parent); + root =aa_get_profile (rcu_dereference_protected (profile->parent, +mutex_is_locked (&ns->lock))); }else if (!sibling && !PROFILE_IS_HAT (profile)) { root =aa_get_profile (profile); }else {/* conflicting change type */
@@ -1142,6 +1147,8 @@ static struct aa_label *change_hat(struct aa_label *label, const char *hats[], } }mutex_unlock (&ns->lock); +if (needput) +aa_put_label (label);return ERR_PTR (error); build:
@@ -1154,6 +1161,8 @@ static struct aa_label *change_hat(struct aa_label *label, const char *hats[], goto fail; }/* else if (IS_ERR) build_change_hat has logged error so return new */ mutex_unlock (&ns->lock); +if (needput) +aa_put_label (label);return new; }
security/apparmor/include/policy.h +2 -0
@@ -181,6 +181,8 @@ void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);void aa_free_proxy_kref (struct kref *kref);struct aa_profile *aa_alloc_profile (const char *name,struct aa_proxy *proxy,gfp_t gfp); +struct aa_profile *__aa_new_null_profile (struct aa_profile *parent,bool hat, +const char *base,gfp_t gfp);struct aa_profile *aa_new_null_profile (struct aa_profile *parent,bool hat,const char *base,gfp_t gfp);void aa_free_profile (struct aa_profile *profile);
security/apparmor/policy.c +16 -5
@@ -507,7 +507,7 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, }/** -* aa_new_null_profile - create or find a null-X learning profile +* __aa_new_null_profile - create or find a null-X learning profile * @parent: profile that caused this profile to be created (NOT NULL) * @hat: true if the null- learning profile is a hat * @base: name to base the null profile off of
@@ -524,14 +524,15 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,* * Returns: new refcounted profile else NULL on failure */ -struct aa_profile *aa_new_null_profile (struct aa_profile *parent,bool hat, -const char *base,gfp_t gfp) +struct aa_profile *__aa_new_null_profile (struct aa_profile *parent,bool hat, +const char *base,gfp_t gfp) {struct aa_profile *p, *profile;const char *bname;char *name =NULL ;AA_BUG (!parent); +AA_BUG (!mutex_is_locked (&parent->ns->lock));if (base) { name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base),
@@ -572,7 +573,6 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, profile->file.dfa =aa_get_dfa (nulldfa); profile->policy.dfa =aa_get_dfa (nulldfa); -mutex_lock_nested (&profile->ns->lock, profile->ns->level); p =__find_child (&parent->base.profiles, bname);if (p) {aa_free_profile (profile);
@@ -580,7 +580,6 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, }else {__add_profile (&parent->base.profiles, profile); } -mutex_unlock (&profile->ns->lock);/* refcount released by caller */ out:
@@ -594,6 +593,18 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,return NULL ; } +struct aa_profile *aa_new_null_profile (struct aa_profile *parent,bool hat, +const char *base,gfp_t gfp) +{ +struct aa_profile *profile; + +mutex_lock_nested (&parent->ns->lock, parent->ns->level); + profile =__aa_new_null_profile (parent, hat, base, gfp); +mutex_unlock (&parent->ns->lock); + +return profile; +} +/** * replacement_allowed - test to see if replacement is allowed * @profile: profile to test if it can be replaced (MAYBE NULL)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1546/1887 blk-cgroup: delay freeing policy data after rcu grace period
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yu Kuai <[email protected]>
[ Upstream commit 0af3fedb8c8ed3c07b4f76927bd7fc88f6f82efb ]
Currently blkcg_print_blkgs() must hold RCU to iterate blkgs from a blkcg, and prfill() must hold queue_lock to prevent policy data from being freed by policy deactivation. As a consequence, queue_lock has to be nested under RCU from blkcg_print_blkgs().
Delay freeing policy data until after an RCU grace period so prfill() can be protected by RCU alone.
Adapted for this tree: bfqg_release() also exits the group stats, the throttle policy keeps its existing offline helpers, and the bfq_group reference counter becomes atomic because the final bfqg_put() now runs from an RCU callback without the locks that protect the other users.
Stable-dep-of: 97cb95d21488 ("blk-iocost: clear delay state when freeing policy data")
--- block/bfq-cgroup.c | 15 ++++++++++----- block/bfq-iosched.h | 2 +- block/blk-cgroup.h | 2 ++ block/blk-iocost.c | 14 ++++++++++++-- block/blk-iolatency.c | 10 +++++++++- block/blk-throttle.c | 13 +++++++++++-- 6 files changed, 45 insertions(+), 11 deletions(-)
block/bfq-cgroup.c +10 -5
@@ -316,14 +316,12 @@ struct bfq_group *bfqq_group(struct bfq_queue *bfqq)static void bfqg_get (struct bfq_group *bfqg) { - bfqg->ref++; +atomic_inc (&bfqg->ref); }static void bfqg_put (struct bfq_group *bfqg) { - bfqg->ref--; - -if (bfqg->ref ==0 ) +if (atomic_dec_and_test (&bfqg->ref))kfree (bfqg); }
@@ -556,14 +554,21 @@ static void bfq_pd_init(struct blkg_policy_data *pd) bfqg->rq_pos_tree =RB_ROOT ; } -static void bfq_pd_free (struct blkg_policy_data *pd) +static void bfqg_release (struct rcu_head *rcu) { +struct blkg_policy_data *pd = + container_of(rcu, struct blkg_policy_data, rcu_head);struct bfq_group *bfqg =pd_to_bfqg (pd);bfqg_stats_exit (&bfqg->stats);bfqg_put (bfqg); } +static void bfq_pd_free (struct blkg_policy_data *pd) +{ +call_rcu (&pd->rcu_head, bfqg_release); +} +static void bfq_pd_reset_stats (struct blkg_policy_data *pd) {struct bfq_group *bfqg =pd_to_bfqg (pd);
block/bfq-iosched.h +1 -1
@@ -953,7 +953,7 @@ struct bfq_group {char blkg_path[128 ];/* reference counter (see comments in bfq_bic_update_cgroup) */ -int ref; +atomic_t ref;/* Is bfq_group still online? */ bool online;
block/blk-cgroup.h +2 -0
@@ -126,6 +126,8 @@ struct blkg_policy_data {struct blkcg_gq *blkg;int plid;bool online; + +struct rcu_head rcu_head; };/*
block/blk-iocost.c +12 -2
@@ -3003,6 +3003,16 @@ static void ioc_pd_init(struct blkg_policy_data *pd)spin_unlock_irqrestore (&ioc->lock, flags); } +static void iocg_release (struct rcu_head *rcu) +{ +struct blkg_policy_data *pd = + container_of(rcu, struct blkg_policy_data, rcu_head); +struct ioc_gq *iocg =pd_to_iocg (pd); + +free_percpu (iocg->pcpu_stat); +kfree (iocg); +} +static void ioc_pd_free (struct blkg_policy_data *pd) {struct ioc_gq *iocg =pd_to_iocg (pd);
@@ -3027,8 +3037,8 @@ static void ioc_pd_free(struct blkg_policy_data *pd)hrtimer_cancel (&iocg->waitq_timer); } -free_percpu (iocg->pcpu_stat); -kfree (iocg); + +call_rcu (&pd->rcu_head, iocg_release); }static void ioc_pd_stat (struct blkg_policy_data *pd,struct seq_file *s)
block/blk-iolatency.c +9 -1
@@ -1025,13 +1025,21 @@ static void iolatency_pd_offline(struct blkg_policy_data *pd)iolatency_clear_scaling (blkg); } -static void iolatency_pd_free (struct blkg_policy_data *pd) +static void iolat_release (struct rcu_head *rcu) { +struct blkg_policy_data *pd = + container_of(rcu, struct blkg_policy_data, rcu_head);struct iolatency_grp *iolat =pd_to_lat (pd); +free_percpu (iolat->stats);kfree (iolat); } +static void iolatency_pd_free (struct blkg_policy_data *pd) +{ +call_rcu (&pd->rcu_head, iolat_release); +} +static struct cftype iolatency_files[] = { { .name ="latency" ,
block/blk-throttle.c +11 -2
@@ -486,16 +486,25 @@ static void throtl_pd_offline(struct blkg_policy_data *pd)throtl_upgrade_state (tg->td); } -static void throtl_pd_free (struct blkg_policy_data *pd) +static void tg_release (struct rcu_head *rcu) { +struct blkg_policy_data *pd = + container_of(rcu, struct blkg_policy_data, rcu_head);struct throtl_grp *tg =pd_to_tg (pd); -del_timer_sync (&tg->service_queue.pending_timer);blkg_rwstat_exit (&tg->stat_bytes);blkg_rwstat_exit (&tg->stat_ios);kfree (tg); } +static void throtl_pd_free (struct blkg_policy_data *pd) +{ +struct throtl_grp *tg =pd_to_tg (pd); + +del_timer_sync (&tg->service_queue.pending_timer); +call_rcu (&pd->rcu_head, tg_release); +} +static struct throtl_grp *throtl_rb_first (struct throtl_service_queue *parent_sq) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1547/1887 blk-iocost: clear delay state when freeing policy data
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Usama Arif <[email protected]>
[ Upstream commit 97cb95d2148835ae86ff916b145aef332d40439d ]
iocg_kick_delay() turns sufficiently large debt into an explicit block-cgroup delay with blkcg_set_delay(), setting blkg->use_delay to -1 and incrementing blkcg->congestion_count. Clearing it again depends on iocg_kick_delay() running from the period timer, the waitq timer or the issue path.
ioc_pd_free() removes the iocg from active_iocgs and cancels its waitq timer, and no further bios can arrive, so once it has run nothing is left which can reduce the debt and clear the delay. The blkcg stays marked congested for the rest of its life.
blk_cgroup_congested() then returns true for every task in that cgroup and its descendants: page_cache_sync_ra() cuts readahead to a single page, page_cache_async_ra() skips it altogether, and __folio_throttle_swaprate() takes swap_avail_lock and schedules a throttle on anonymous folio allocation.
Clear it explicitly, after the list removal and the synchronous hrtimer_cancel() so that neither timer processing nor an I/O path can re-arm it. The free callback can also see policy data which was never attached to a blkg, hence the pd->blkg check.
--- block/blk-iocost.c | 7 +++++++ 1 file changed, 7 insertions(+)
block/blk-iocost.c +7 -0
@@ -3016,6 +3016,7 @@ static void iocg_release(struct rcu_head *rcu)static void ioc_pd_free (struct blkg_policy_data *pd) {struct ioc_gq *iocg =pd_to_iocg (pd); +struct blkcg_gq *blkg =pd_to_blkg (pd);struct ioc *ioc = iocg->ioc;unsigned long flags;
@@ -3038,6 +3039,12 @@ static void ioc_pd_free(struct blkg_policy_data *pd)hrtimer_cancel (&iocg->waitq_timer); } +/* off ->active_iocgs and timer gone, so nothing can re-arm the delay */ + iocg->delay =0 ; + iocg->indelay_since =0 ; +if (blkg) +blkcg_clear_delay (blkg); +call_rcu (&pd->rcu_head, iocg_release); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1548/1887 blk-iolatency: clear delay state when freeing policy data
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Usama Arif <[email protected]>
[ Upstream commit 8935bf22c0a0db517a7f72f7097300e05dd852f5 ]
io.latency can throttle a group which has no latency target of its own. When a sibling misses its target, check_scale_change() scales down its peers, and a peer that reaches queue depth one gets blkcg_use_delay() called on it on every further scale-down, even with min_lat_nsec == 0.
iolatency_pd_offline() resets the target through iolatency_set_min_lat_nsec(), which clears the delay only on a nonzero to zero transition, so it never clears such a peer. Freeing the policy data then leaves blkg->use_delay set and blkcg->congestion_count elevated with nothing left that can drop it.
blk_cgroup_congested() then returns true for every task in that cgroup and its descendants for as long as the cgroup lives: page_cache_sync_ra() cuts readahead to a single page, page_cache_async_ra() skips it altogether, and __folio_throttle_swaprate() takes swap_avail_lock and schedules a throttle on anonymous folio allocation.
Clear the delay in iolatency_pd_free(). By then bio-held blkg references have drained, or the queue is frozen for policy deactivation, so check_scale_change() cannot re-arm it. The free callback can also see policy data which was never attached to a blkg, hence the pd->blkg check.
--- block/blk-iolatency.c | 9 +++++++++ 1 file changed, 9 insertions(+)
block/blk-iolatency.c +9 -0
@@ -1037,6 +1037,15 @@ static void iolat_release(struct rcu_head *rcu)static void iolatency_pd_free (struct blkg_policy_data *pd) { +struct blkcg_gq *blkg =pd_to_blkg (pd); + +/* +* Groups throttled as collateral have min_lat_nsec == 0, so +* iolatency_pd_offline() leaves their delay set. Drop it here, where +* no in-flight bio can re-arm it via check_scale_change(). +*/ +if (blkg) +blkcg_clear_delay (blkg);call_rcu (&pd->rcu_head, iolat_release); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1549/1887 null_blk: free zones array on device power-off
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zizhi Wo <[email protected]>
[ Upstream commit 2a6357a9b935a34f5508618fee8a7fffbf7722a8 ]
null_init_zoned_dev() allocates dev->zones when a zoned device is powered on, but null_del_dev() never frees it on power-off; dev->zones is only freed later in null_free_dev(), when the configfs directory is removed. If the device is powered off and then on again, null_init_zoned_dev() allocates a new array and overwrites the dev->zones pointer, leaking the previous allocation each power cycle.
Free dev->zones in null_del_dev() via null_free_zoned_dev() to solve it. And calling null_free_zoned_dev() in null_free_dev() is no longer necessary because every caller already invokes null_del_dev() first: via nullb_group_drop_item() before nullb_device_release(), in the null_add_dev() error path of null_create_dev(), and in null_destroy_dev(). Remove the redundant call.
And take &lock around zone_cond_store() in the two store wrappers to serialize dev->zones check-and-deref against its alloc/free, which already run under &lock. The reason there was no problem before is that only nullb_device_release() or null_exit() frees the dev->zones, which guarantees that subsequent users won't access the configfs interface.
--- drivers/block/null_blk/main.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
drivers/block/null_blk/main.c +13 -3
@@ -552,8 +552,13 @@ static ssize_t nullb_device_zone_readonly_store(struct config_item *item, const char *page, size_t count) {struct nullb_device *dev =to_nullb_device (item); +ssize_t ret; + +mutex_lock (&lock); + ret =zone_cond_store (dev, page, count,BLK_ZONE_COND_READONLY ); +mutex_unlock (&lock); -return zone_cond_store (dev, page, count,BLK_ZONE_COND_READONLY ); +return ret; }CONFIGFS_ATTR_WO (nullb_device_, zone_readonly);
@@ -561,8 +566,13 @@ static ssize_t nullb_device_zone_offline_store(struct config_item *item, const char *page, size_t count) {struct nullb_device *dev =to_nullb_device (item); +ssize_t ret; -return zone_cond_store (dev, page, count,BLK_ZONE_COND_OFFLINE ); +mutex_lock (&lock); + ret =zone_cond_store (dev, page, count,BLK_ZONE_COND_OFFLINE ); +mutex_unlock (&lock); + +return ret; }CONFIGFS_ATTR_WO (nullb_device_, zone_offline);
@@ -746,7 +756,6 @@ static void null_free_dev(struct nullb_device *dev)if (!dev)return ; -null_free_zoned_dev (dev);badblocks_exit (&dev->badblocks);kfree (dev); }
@@ -1825,6 +1834,7 @@ static void null_del_dev(struct nullb *nullb) }put_disk (nullb->disk); +null_free_zoned_dev (dev);if (dev->queue_mode ==NULL_Q_MQ && nullb->tag_set == &nullb->__tag_set)blk_mq_free_tag_set (nullb->tag_set);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1550/1887 fs/ntfs3: fix integer overflow in MFT cluster validation
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
[ Upstream commit c510c63873103a5da6a498fe537bdb5d6f8d03a2 ]
In ntfs_init_from_boot(), the boot sector's MFT cluster numbers are validated against the volume size with:
if (mlcn * sct_per_clst >= sectors || mlcn2 * sct_per_clst >= sectors) goto out;
mlcn and mlcn2 are u64 fields read directly from the boot sector. sct_per_clst is bounded above by 4096 (true_sectors_per_clst() plus the is_power_of_2() check below it), but the multiplication is done in u64 and wraps when mlcn (or mlcn2) is large enough -- e.g. mlcn near 2^62 with sct_per_clst == 4 wraps to 0, which compares below any non-zero 'sectors', so the check is bypassed and the malformed record is accepted.
The accepted mlcn is then used unchanged in
sbi->mft.lbo = mlcn << cluster_bits;
In practice the resulting reads fail at the block layer (sb_bread() returns NULL via grow_buffers()'s check_mul_overflow() guard), so today this manifests as mount failing in odd places rather than as something more dangerous, but the validation step is still wrong and there is no reason for callers to rely on the block layer to catch a value that should never have been accepted in the first place.
Use check_mul_overflow() to compute the two sector positions and fail the mount if either multiplication wraps; this preserves the existing semantics (mlcn * sct_per_clst >= sectors) instead of switching to division (mlcn >= sectors / sct_per_clst), which would tighten the check at edge cases where 'sectors' is not a multiple of sct_per_clst. The check_*_overflow() style is the one ntfs3 already uses for similar on-disk arithmetic in fs/ntfs3/run.c.
--- fs/ntfs3/super.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-)
fs/ntfs3/super.c +11 -5
@@ -33,6 +33,7 @@#include <linux/minmax.h> #include <linux/module.h> #include <linux/nls.h> +#include <linux/overflow.h> #include <linux/seq_file.h> #include <linux/statfs.h>
@@ -685,7 +686,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,struct ntfs_sb_info *sbi = sb->s_fs_info;int err;u32 mb, gb, boot_sector_size, sct_per_clst, record_size; -u64 sectors, clusters, mlcn, mlcn2; +u64 sectors, clusters, mlcn, mlcn2, mft_pos, mft2_pos;struct NTFS_BOOT *boot;struct buffer_head *bh;struct MFT_REC *rec;
@@ -730,10 +731,15 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, mlcn2 =le64_to_cpu (boot->mft2_clst); sectors =le64_to_cpu (boot->sectors_per_volume); -if (mlcn * sct_per_clst >= sectors) - goto out; - -if (mlcn2 * sct_per_clst >= sectors) +/* +* Convert mlcn/mlcn2 to sector positions before comparing with +* 'sectors'. All three are u64 values that come from the boot +* sector, so use check_mul_overflow() to keep a wraparound from +* silently bypassing the comparison. +*/ +if (check_mul_overflow (mlcn, (u64 )sct_per_clst, &mft_pos) || +check_mul_overflow (mlcn2, (u64 )sct_per_clst, &mft2_pos) || + mft_pos >= sectors || mft2_pos >= sectors) goto out;/* Check MFT record size. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1551/1887 ocfs2: validate DIO orphan slot during inode read
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: ZhengYuan Huang <[email protected]>
[ Upstream commit bb88131c9831075b8dc08cdd375743e5d44c7ca2 ]
[BUG]
A corrupted append-DIO dinode (high byte at offset 0xa1 corrupted from 0 to 1) can carry an i_dio_orphaned_slot outside the mounted filesystem slot range and trigger a use-after-free error:
BUG: KASAN: slab-use-after-free in ocfs2_get_system_file_inode+0x780/0x820 fs/ocfs2/sysfile.c:102 Read of size 8 at addr ffff88800b767c00 by task kworker/u8:3/85 Call Trace: ... ocfs2_get_system_file_inode+0x780/0x820 fs/ocfs2/sysfile.c:102 ocfs2_wipe_inode+0x292/0xf70 fs/ocfs2/inode.c:840 ocfs2_delete_inode fs/ocfs2/inode.c:1155 [inline] ocfs2_evict_inode+0x6c9/0x1170 fs/ocfs2/inode.c:1295 evict+0x38e/0x8f0 fs/inode.c:810 iput_final fs/inode.c:1914 [inline] iput fs/inode.c:1966 [inline] iput+0x55b/0x8b0 fs/inode.c:1926 ocfs2_recover_orphans+0x610/0xe40 fs/ocfs2/journal.c:2374 ocfs2_complete_recovery+0x5af/0xd00 fs/ocfs2/journal.c:1373 ...
[CAUSE]ocfs2_del_inode_from_orphan() uses i_dio_orphaned_slot to index the slot-local system inode cache. The dinode validator does not check this active slot, so an out-of-range value produces an invalid cache entry pointer that is dereferenced as an inode pointer.
[FIX]
Reject an active i_dio_orphaned_slot outside the slot range during dinode validation, before DIO orphan recovery can consume it.
--- fs/ocfs2/inode.c | 8 ++++++++ 1 file changed, 8 insertions(+)
fs/ocfs2/inode.c +8 -0
@@ -1585,6 +1585,14 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } +if ((le32_to_cpu (di->i_flags) &OCFS2_DIO_ORPHANED_FL ) && +le16_to_cpu (di->i_dio_orphaned_slot) >=OCFS2_SB (sb)->max_slots) { + rc =ocfs2_error (sb,"Invalid dinode %llu: DIO orphaned slot %u\n" , + (unsigned long long )bh->b_blocknr, +le16_to_cpu (di->i_dio_orphaned_slot)); + goto bail; + } +if (S_ISLNK (le16_to_cpu (di->i_mode)) && !le32_to_cpu (di->i_clusters)) {int max_inline =ocfs2_fast_symlink_chars (sb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1552/1887 ocfs2: validate orphan slot during inode read
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: ZhengYuan Huang <[email protected]>
[ Upstream commit b2d31acbd3b182755b019183fb46949ba5e39b9f ]
Patch series "ocfs2: validate active orphan slots during inode read".
OCFS2 trusts active ordinary and append-DIO orphan slots read from dinodes. A corrupted slot can therefore index osb_orphan_wipes or the slot-local system-inode cache outside their allocations before the corruption is reported.
Patch 1 validates the ordinary orphan slot used by inode wipe processing. Patch 2 validates the append-DIO orphan slot used by DIO completion and orphan recovery. Both checks reject corrupt metadata at the existing inode validation boundary.
This patch (of 2):
[BUG]
A corrupted dinode with OCFS2_ORPHANED_FL can carry an i_orphaned_slot outside the mounted filesystem slot range. ocfs2_wipe_inode() uses it to index osb_orphan_wipes before looking up the orphan directory, causing an out-of-bounds memory access.
BUG: KASAN: slab-use-after-free in ocfs2_get_system_file_inode+0x780/0x820 fs/ocfs2/sysfile.c:102 Read of size 8 at addr ffff88800b767c00 by task kworker/u8:3/85 Call Trace: ... ocfs2_get_system_file_inode+0x780/0x820 fs/ocfs2/sysfile.c:102 ocfs2_wipe_inode+0x292/0xf70 fs/ocfs2/inode.c:840 ocfs2_delete_inode fs/ocfs2/inode.c:1155 [inline] ocfs2_evict_inode+0x6c9/0x1170 fs/ocfs2/inode.c:1295 evict+0x38e/0x8f0 fs/inode.c:810 iput_final fs/inode.c:1914 [inline] iput fs/inode.c:1966 [inline] iput+0x55b/0x8b0 fs/inode.c:1926 ocfs2_recover_orphans+0x610/0xe40 fs/ocfs2/journal.c:2374 ocfs2_complete_recovery+0x5af/0xd00 fs/ocfs2/journal.c:1373 ...
[CAUSE]ocfs2_validate_inode_block() validates i_suballoc_slot but leaves the active ordinary orphan slot unchecked. Downstream consumers assume that the value is smaller than osb->max_slots.
[FIX]
Reject an active i_orphaned_slot outside the slot range during dinode validation, before the inode reaches orphan wipe processing.
--- fs/ocfs2/inode.c | 8 ++++++++ 1 file changed, 8 insertions(+)
fs/ocfs2/inode.c +8 -0
@@ -1585,6 +1585,14 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } +if ((le32_to_cpu (di->i_flags) &OCFS2_ORPHANED_FL ) && +le16_to_cpu (di->i_orphaned_slot) >=OCFS2_SB (sb)->max_slots) { + rc =ocfs2_error (sb,"Invalid dinode %llu: orphaned slot %u\n" , + (unsigned long long )bh->b_blocknr, +le16_to_cpu (di->i_orphaned_slot)); + goto bail; + } +if ((le32_to_cpu (di->i_flags) &OCFS2_DIO_ORPHANED_FL ) &&le16_to_cpu (di->i_dio_orphaned_slot) >=OCFS2_SB (sb)->max_slots) { rc = ocfs2_error(sb, "Invalid dinode %llu: DIO orphaned slot %u\n",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1553/1887 nvmet: fix max_qid race between configfs and controller allocation
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maurizio Lombardi <[email protected]>
[ Upstream commit f1a8846e06388113dfdbb89dee005083fa9afdf9 ]
The function nvmet_subsys_attr_qid_max_store() can race against nvmet_alloc_ctrl() when a subsystem's max_qid limit is modified.
Suppose max_qid is currently 64. If nvmet_alloc_ctrl() executes:ctrl->sqs = kzalloc_objs(struct nvmet_sq *, subsys->max_qid + 1); and at this exact point, a userspace process changes max_qid to 128, nvmet_subsys_attr_qid_max_store() will set the new max_qid value. It attempts to delete active controllers to force a reconnect, but the new controller won't be deleted because it hasn't been added to the subsys->ctrls list yet.
nvmet_alloc_ctrl() then proceeds and adds the new controller to the subsys->ctrls list. Later, when nvmet_install_queue() is called, it will see max_qid set to 128, but the memory allocated for sqs is only sized for 64 entries. This results in a KASAN out-of-bounds warning and potential memory corruptions.
Fix this by protecting the queue allocations and list insertion in nvmet_alloc_ctrl() with down_read(&nvmet_config_sem). Because nvmet_subsys_attr_qid_max_store() acquires down_write(&nvmet_config_sem) to modify the attribute, this safely prevents the configfs writer from modifying max_qid during controller creation.
Copy the max_qid from the subsystem to the controller's structure during the allocation; ctrl->max_qid never changes as long as the controller remains in LIVE state, so this will prevent similar race conditions.
--- drivers/nvme/target/admin-cmd.c | 8 +++--- drivers/nvme/target/core.c | 41 ++++++++++++++++++------------- drivers/nvme/target/fabrics-cmd.c | 2 +- drivers/nvme/target/nvmet.h | 6 +++++ 4 files changed, 35 insertions(+), 22 deletions(-)
drivers/nvme/target/admin-cmd.c +4 -4
@@ -831,7 +831,7 @@ u16 nvmet_set_feat_async_event(struct nvmet_req *req, u32 mask)void nvmet_execute_set_features (struct nvmet_req *req) { -struct nvmet_subsys *subsys =nvmet_req_subsys (req); +struct nvmet_ctrl *ctrl =nvmet_req_ctrl (req);u32 cdw10 =le32_to_cpu (req->cmd->common.cdw10);u32 cdw11 =le32_to_cpu (req->cmd->common.cdw11);u16 status =0 ;
@@ -850,7 +850,7 @@ void nvmet_execute_set_features(struct nvmet_req *req)break ; }nvmet_set_result (req, - (subsys->max_qid -1 ) | ((subsys->max_qid -1 ) <<16 )); + (ctrl->max_qid -1 ) | ((ctrl->max_qid -1 ) <<16 ));break ;case NVME_FEAT_KATO : status =nvmet_set_feat_kato (req);
@@ -905,7 +905,7 @@ void nvmet_get_feat_async_event(struct nvmet_req *req)void nvmet_execute_get_features (struct nvmet_req *req) { -struct nvmet_subsys *subsys =nvmet_req_subsys (req); +struct nvmet_ctrl *ctrl =nvmet_req_ctrl (req);u32 cdw10 =le32_to_cpu (req->cmd->common.cdw10);u16 status =0 ;
@@ -942,7 +942,7 @@ void nvmet_execute_get_features(struct nvmet_req *req)break ;case NVME_FEAT_NUM_QUEUES :nvmet_set_result (req, - (subsys->max_qid-1 ) | ((subsys->max_qid-1 ) <<16 )); + (ctrl->max_qid-1 ) | ((ctrl->max_qid-1 ) <<16 ));break ;case NVME_FEAT_KATO :nvmet_get_feat_kato (req);
drivers/nvme/target/core.c +24 -17
62 lines not on this page · raw message
drivers/nvme/target/fabrics-cmd.c +1 -1
@@ -351,7 +351,7 @@ static void nvmet_execute_io_connect(struct nvmet_req *req) goto out; } -if (unlikely (qid > ctrl->subsys->max_qid)) { +if (unlikely (qid > ctrl->max_qid)) {pr_warn ("invalid queue id (%d)\n" , qid); status =NVME_SC_CONNECT_INVALID_PARAM |NVME_STATUS_DNR ; req->cqe->result.u32 =IPO_IATTR_CONNECT_SQE (qid);
drivers/nvme/target/nvmet.h +6 -0
@@ -190,6 +190,7 @@ struct nvmet_ctrl {uuid_t hostid;u16 cntlid; +u16 max_qid;u32 kato;struct nvmet_port *port;
@@ -617,6 +618,11 @@ static inline struct nvmet_subsys *nvmet_req_subsys(struct nvmet_req *req)return req->sq->ctrl->subsys; } +static inline struct nvmet_ctrl *nvmet_req_ctrl (struct nvmet_req *req) +{ +return req->sq->ctrl; +} +static inline bool nvmet_is_disc_subsys (struct nvmet_subsys *subsys) {return subsys->type !=NVME_NQN_NVME ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1554/1887 clocksource/drivers/samsung_pwm: Switch to raw_spinlock_t type
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marek Szyprowski <[email protected]>
[ Upstream commit 3b212f9d70805d91febae01d618868f4860e267c ]
Samsung PWM timer might be used as a clock source on some legacy systems. When PREEMPT_RT is enabled on ARM, regular spinlock is converted to a sleeping lock (mutex-based), which must not be used in atomic context such as hard interrupt handlers. Switch the samsung_pwm_lock to the raw_spinlock, which remains a true non-sleeping spinlock even under PREEMPT_RT.
--- drivers/clocksource/samsung_pwm_timer.c | 22 +++++++++++----------- drivers/pwm/pwm-samsung.c | 22 +++++++++++----------- include/clocksource/samsung_pwm.h | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-)
drivers/clocksource/samsung_pwm_timer.c +11 -11
@@ -56,7 +56,7 @@#define TCON_AUTORELOAD (chan) \ ((chan < 5) ? _TCON_AUTORELOAD(chan) : _TCON_AUTORELOAD4(chan)) -DEFINE_SPINLOCK (samsung_pwm_lock); +DEFINE_RAW_SPINLOCK (samsung_pwm_lock);EXPORT_SYMBOL (samsung_pwm_lock);struct samsung_pwm_clocksource {
@@ -87,14 +87,14 @@ static void samsung_timer_set_prescale(unsigned int channel, u16 prescale)if (channel >=2 ) shift =TCFG0_PRESCALER1_SHIFT ; -spin_lock_irqsave (&samsung_pwm_lock, flags); +raw_spin_lock_irqsave (&samsung_pwm_lock, flags); reg =readl (pwm.base +REG_TCFG0 ); reg &= ~(TCFG0_PRESCALER_MASK << shift); reg |= (prescale -1 ) << shift;writel (reg, pwm.base +REG_TCFG0 ); -spin_unlock_irqrestore (&samsung_pwm_lock, flags); +raw_spin_unlock_irqrestore (&samsung_pwm_lock, flags); }static void samsung_timer_set_divisor (unsigned int channel,u8 divisor)
@@ -106,14 +106,14 @@ static void samsung_timer_set_divisor(unsigned int channel, u8 divisor) bits = (fls (divisor) -1 ) - pwm.variant.div_base; -spin_lock_irqsave (&samsung_pwm_lock, flags); +raw_spin_lock_irqsave (&samsung_pwm_lock, flags); reg =readl (pwm.base +REG_TCFG1 ); reg &= ~(TCFG1_MUX_MASK << shift); reg |= bits << shift;writel (reg, pwm.base +REG_TCFG1 ); -spin_unlock_irqrestore (&samsung_pwm_lock, flags); +raw_spin_unlock_irqrestore (&samsung_pwm_lock, flags); }static void samsung_time_stop (unsigned int channel)
@@ -124,13 +124,13 @@ static void samsung_time_stop(unsigned int channel)if (channel >0 ) ++channel; -spin_lock_irqsave (&samsung_pwm_lock, flags); +raw_spin_lock_irqsave (&samsung_pwm_lock, flags); tcon =readl_relaxed (pwm.base +REG_TCON ); tcon &= ~TCON_START (channel);writel_relaxed (tcon, pwm.base +REG_TCON ); -spin_unlock_irqrestore (&samsung_pwm_lock, flags); +raw_spin_unlock_irqrestore (&samsung_pwm_lock, flags); }static void samsung_time_setup (unsigned int channel,unsigned long tcnt)
@@ -142,7 +142,7 @@ static void samsung_time_setup(unsigned int channel, unsigned long tcnt)if (tcon_chan >0 ) ++tcon_chan; -spin_lock_irqsave (&samsung_pwm_lock, flags); +raw_spin_lock_irqsave (&samsung_pwm_lock, flags); tcon =readl_relaxed (pwm.base +REG_TCON );
@@ -153,7 +153,7 @@ static void samsung_time_setup(unsigned int channel, unsigned long tcnt)writel_relaxed (tcnt, pwm.base +REG_TCMPB (channel));writel_relaxed (tcon, pwm.base +REG_TCON ); -spin_unlock_irqrestore (&samsung_pwm_lock, flags); +raw_spin_unlock_irqrestore (&samsung_pwm_lock, flags); }static void samsung_time_start (unsigned int channel,bool periodic)
@@ -164,7 +164,7 @@ static void samsung_time_start(unsigned int channel, bool periodic)if (channel >0 ) ++channel; -spin_lock_irqsave (&samsung_pwm_lock, flags); +raw_spin_lock_irqsave (&samsung_pwm_lock, flags); tcon =readl_relaxed (pwm.base +REG_TCON );
@@ -178,7 +178,7 @@ static void samsung_time_start(unsigned int channel, bool periodic)writel_relaxed (tcon, pwm.base +REG_TCON ); -spin_unlock_irqrestore (&samsung_pwm_lock, flags); +raw_spin_unlock_irqrestore (&samsung_pwm_lock, flags); } static int samsung_set_next_event(unsigned long cycles,
drivers/pwm/pwm-samsung.c +11 -11
@@ -102,7 +102,7 @@ struct samsung_pwm_chip {* IP. Should this change, both drivers will need to be modified to * properly synchronize accesses to particular instances. */ -static DEFINE_SPINLOCK (samsung_pwm_lock); +static DEFINE_RAW_SPINLOCK (samsung_pwm_lock);#endif static inline
@@ -141,14 +141,14 @@ static void pwm_samsung_set_divisor(struct samsung_pwm_chip *pwm, bits = (fls (divisor) -1 ) - pwm->variant.div_base; -spin_lock_irqsave (&samsung_pwm_lock, flags); +raw_spin_lock_irqsave (&samsung_pwm_lock, flags); reg =readl (pwm->base +REG_TCFG1 ); reg &= ~(TCFG1_MUX_MASK << shift); reg |= bits << shift;writel (reg, pwm->base +REG_TCFG1 ); -spin_unlock_irqrestore (&samsung_pwm_lock, flags); +raw_spin_unlock_irqrestore (&samsung_pwm_lock, flags); }static int pwm_samsung_is_tdiv (struct samsung_pwm_chip *chip,unsigned int chan)
@@ -258,7 +258,7 @@ static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm)unsigned long flags;u32 tcon; -spin_lock_irqsave (&samsung_pwm_lock, flags); +raw_spin_lock_irqsave (&samsung_pwm_lock, flags); tcon =readl (our_chip->base +REG_TCON );
@@ -272,7 +272,7 @@ static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) our_chip->disabled_mask &= ~BIT (pwm->hwpwm); -spin_unlock_irqrestore (&samsung_pwm_lock, flags); +raw_spin_unlock_irqrestore (&samsung_pwm_lock, flags);return 0 ; }
@@ -284,7 +284,7 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm)unsigned long flags;u32 tcon; -spin_lock_irqsave (&samsung_pwm_lock, flags); +raw_spin_lock_irqsave (&samsung_pwm_lock, flags); tcon =readl (our_chip->base +REG_TCON ); tcon &= ~TCON_AUTORELOAD (tcon_chan);
@@ -299,7 +299,7 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) our_chip->disabled_mask |=BIT (pwm->hwpwm); -spin_unlock_irqrestore (&samsung_pwm_lock, flags); +raw_spin_unlock_irqrestore (&samsung_pwm_lock, flags); } static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip,
@@ -307,11 +307,11 @@ static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, {unsigned long flags; -spin_lock_irqsave (&samsung_pwm_lock, flags); +raw_spin_lock_irqsave (&samsung_pwm_lock, flags);__pwm_samsung_manual_update (chip, pwm); -spin_unlock_irqrestore (&samsung_pwm_lock, flags); +raw_spin_unlock_irqrestore (&samsung_pwm_lock, flags); } static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -400,7 +400,7 @@ static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip,unsigned long flags;u32 tcon; -spin_lock_irqsave (&samsung_pwm_lock, flags); +raw_spin_lock_irqsave (&samsung_pwm_lock, flags); tcon =readl (chip->base +REG_TCON );
@@ -414,7 +414,7 @@ static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip,writel (tcon, chip->base +REG_TCON ); -spin_unlock_irqrestore (&samsung_pwm_lock, flags); +raw_spin_unlock_irqrestore (&samsung_pwm_lock, flags); } static int pwm_samsung_set_polarity(struct pwm_chip *chip,
include/clocksource/samsung_pwm.h +1 -1
@@ -15,7 +15,7 @@* spinlock is not shared between both drivers. */ #ifdef CONFIG_CLKSRC_SAMSUNG_PWM -extern spinlock_t samsung_pwm_lock; +extern raw_spinlock_t samsung_pwm_lock;#endif struct samsung_pwm_variant {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1555/1887 firewire: core: fix memory leak in error path of build_tree()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Sakamoto <[email protected]>
[ Upstream commit 05bfb1327dc5fb61528bab31cd8f0c1e4bddec23 ]
In the error path of build_tree(), node instances can remain in the local linked list when the function returns.
Whenever an invalid value is detected in the self ID sequence, each allocated node instance is either an entry in the linked list or an entry in the ports array of its parent node. Therefore, the allocate node instances can be safely released by traversing the linked list from its head.
Release the remaining node instances with for_each_fw_node() before returning to the caller.
--- drivers/firewire/core-topology.c | 40 +++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 14 deletions(-)
drivers/firewire/core-topology.c +26 -14
@@ -161,6 +161,17 @@ static inline struct fw_node *fw_node(struct list_head *l) return list_entry(l, struct fw_node, link); } +typedef void (*fw_node_callback_t )(struct fw_card *card,struct fw_node *node, +struct fw_node *parent); + +static void for_each_fw_node (struct fw_card *card,struct fw_node *root, +fw_node_callback_t callback); + +static void free_fw_node (struct fw_card *card,struct fw_node *node,struct fw_node *parent) +{ +fw_node_put (node); +} +/* * This function builds the tree representation of the topology given * by the self IDs from the latest bus reset. During the construction
@@ -193,19 +204,19 @@ static struct fw_node *build_tree(struct fw_card *card,if (next_sid ==NULL ) {fw_err (card,"inconsistent extended self IDs\n" ); -return NULL ; + goto error; } q = *sid;if (phy_id !=SELF_ID_PHY_ID (q)) {fw_err (card,"PHY ID mismatch in self ID: %d != %d\n" , phy_id,SELF_ID_PHY_ID (q)); -return NULL ; + goto error; }if (child_port_count > stack_depth) {fw_err (card,"topology stack underflow\n" ); -return NULL ; + goto error; }/*
@@ -223,7 +234,7 @@ static struct fw_node *build_tree(struct fw_card *card, node =fw_node_create (q, port_count, card->color);if (node ==NULL ) {fw_err (card,"out of memory while building topology\n" ); -return NULL ; + goto error; }if (phy_id == (card->node_id &0x3f ))
@@ -264,6 +275,11 @@ static struct fw_node *build_tree(struct fw_card *card, } } +/* Pop the child nodes off the stack and push the new node. */ +__list_del (h->prev, &stack); +list_add_tail (&node->link, &stack); + stack_depth +=1 - child_port_count; +/* * Check that the node reports exactly one parent * port, except for the root, which of course should
@@ -273,14 +289,9 @@ static struct fw_node *build_tree(struct fw_card *card, (next_sid < end && parent_count != 1)) {fw_err (card,"parent port inconsistency for node %d: " "parent_count=%d\n" , phy_id, parent_count); -return NULL ; + goto error; } -/* Pop the child nodes off the stack and push the new node. */ -__list_del (h->prev, &stack); -list_add_tail (&node->link, &stack); - stack_depth +=1 - child_port_count; -if (node->phy_speed ==SCODE_BETA && parent_count + child_port_count >1 ) beta_repeaters_present = true;
@@ -304,12 +315,13 @@ static struct fw_node *build_tree(struct fw_card *card, card->beta_repeaters_present = beta_repeaters_present;return local_node; +error: + ++card->color; +list_for_each_entry_safe (node, child, &stack, link) +for_each_fw_node (card, node, free_fw_node); +return NULL ; } -typedef void (*fw_node_callback_t )(struct fw_card * card, -struct fw_node * node, -struct fw_node * parent); -static void for_each_fw_node (struct fw_card *card,struct fw_node *root,fw_node_callback_t callback) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1556/1887 nvme-apple: Destroy the admin queue on removal
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Peter <[email protected]>
[ Upstream commit 87d5b9864c8118d26f54de4b66d2bddf2c659272 ]
The admin queue is allocated with blk_mq_alloc_queue() but never destroyed. nvme_free_ctrl() only drops the last reference and blk_mq_exit_queue() and blk_sync_queue() never run: the hctx is never moved to q->unused_hctx_list and the timeout timer and work stay armed on a queue that is about to be freed which will eventually oops inside blk_mq_timeout_work().
This can only be triggered when the controller fails to come up and is then immediately torn down again which is why no one ever ran into this before.
Let's just copy what the pcie driver does: unquiesce and destroy the admin queue before nvme_uninit_ctrl().
With this the following WARN followed by a panic no longer happens:
WARNING: block/blk-mq.c:4390 at blk_mq_release+0x194/0x238, CPU#4: kworker/u34:4/119
CPU: 4 UID: 0 PID: 119 Comm: kworker/u34:4 Not tainted 7.2.0-rc1-dirty #248 PREEMPT
Hardware name: Apple Mac mini (M1, 2020) (DT)
Workqueue: nvme-wq apple_nvme_remove_dead_ctrl_work
pstate: 61400005 (nZCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
pc : blk_mq_release+0x194/0x238
lr : blk_mq_release+0x58/0x238
sp : ffffc000833a3b50
x29: ffffc000833a3b50 x28: ffff80001d0450f8 x27: ffff800020c95200
x26: 0000000000000088 x25: 0000000000000000 x24: ffff800020f36805
x23: 0000000000000000 x22: ffffc00081a86878 x21: ffff800020be9c60
x20: 0000000000000000 x19: ffff800022501698 x18: 000000000000000a
x17: 7365757165722066 x16: 666f7265776f7020 x15: 0000000000000000
x14: 0000000000000028 x13: 0000000000004def x12: 0000000000000003
x11: 0000000000000000 x10: 0000000000000000 x9 : ffffc000805b4fc8
x8 : ffffc00081915820 x7 : ffffc00081c4f3c8 x6 : 0000000000000001
x5 : 0000000000000004 x4 : ffff800022498d80 x3 : ffffc000833a3b14
x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff800022501698
Call trace:
blk_mq_release+0x194/0x238 (P)
blk_put_queue+0x8c/0xf0
nvme_free_ctrl+0x4c/0x260
device_release+0x44/0x128
kobject_put+0xa0/0x120
put_device+0x1c/0x40
nvme_uninit_ctrl+0x48/0x60
apple_nvme_remove+0x54/0xb0
platform_remove+0x28/0x40
device_remove+0x54/0x98
device_release_driver_internal+
device_release_driver+0x20/0x38
apple_nvme_remove_dead_ctrl_wor
process_one_work+0x1f4/0x770
worker_thread+0x1b8/0x360
kthread+0x140/0x160
ret_from_fork+0x10/0x20
irq event stamp: 448
hardirqs last enabled at (447):in_unlock_irqrestore+0x74/0x80
hardirqs last disabled at (448): [<ffffc000811cf5c0>] el1_brk64+0x20/0x60
softirqs last enabled at (0): [ess+0xb28/0x2698
softirqs last disabled at (0): [<0000000000000000>] 0x0
---[ end trace 0000000000000000
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
Mem abort info:
ESR = 0x0000000096000005
EC = 0x25: DABT (current EL),
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x05: level 1 translation fault
Data abort info:
ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000
CM = 0, WnR = 0, TnD = 0, TagA
GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
[0000000000000000] user address
Internal error: Oops: 0000000096000005 [#1] SMP
CPU: 7 UID: 0 PID: 54 Comm: kwor 7.2.0-rc1-dirty #248PREEMPT
Tainted: [W]=WARN
Hardware name: Apple Mac mini (M1, 2020) (DT)
Workqueue: kblockd blk_mq_timeou
pstate: 01400005 (nzcv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
pc : percpu_ref_tryget_many.cons
lr : percpu_ref_tryget_many.constprop.0+0xc0/0x168
sp : ffffc000829cbce0
x29: ffffc000829cbce0 x28: ffff800020be9f48 x27: ffff800013e503c0
x26: 0000000000000108 x25: 000009c05
x23: 0000000000000000 x22: ffffc000819f5000 x21: ffff800020be9f48
x20: ffff8001deda4808 x19: ffff8000a
x17: 00000000580e1fac x16: ffffc00082bbbb7c x15: 0000000000000000
x14: 0000000000000028 x13: 000000001
x11: 0000000000000000 x10: 0000000000000000 x9 : ffffc000829cbc20
x8 : ffffc00081915820 x7 : ffffc0001
x5 : ffff80001ca77d08 x4 : 0000000000000000 x3 : ffff80001ca77cb8
x2 : 0000000000000000 x1 : 000000007
Call trace:
percpu_ref_tryget_many.constpro
blk_mq_timeout_work+0x48/0x298
process_one_work+0x1f4/0x770
worker_thread+0x1b8/0x360
kthread+0x140/0x160
ret_from_fork+0x10/0x20
Code: 91282000 97ed44b2 17ffffd2
---[ end trace 0000000000000000 ]------ drivers/nvme/host/apple.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/nvme/host/apple.c +9 -0
@@ -1566,6 +1566,15 @@ static int apple_nvme_remove(struct platform_device *pdev)nvme_stop_ctrl (&anv->ctrl);nvme_remove_namespaces (&anv->ctrl);apple_nvme_disable (anv, true); +if (anv->ctrl.admin_q && !blk_queue_dying (anv->ctrl.admin_q)) { +/* +* If the controller was reset during removal, it's possible +* user requests may be waiting on a stopped queue. Start the +* queue to flush these to completion. +*/ +nvme_start_admin_queue (&anv->ctrl); +blk_mq_destroy_queue (anv->ctrl.admin_q); + }nvme_uninit_ctrl (&anv->ctrl);if (apple_rtkit_is_running (anv->rtk))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1557/1887 amt: Dont support cross-netns setup.
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit e99ecc3046ea5f5c6b5e1f8b4ef854c6c6998e06 ]
When a lower device is unregistered, amt_device_event() tries to unregister its upper AMT device, but it has two problems.
1. amt_lookup_upper_dev() looks up an upper device in the lower device's netns only
2. amt_device_event() unregisters a single upper device only
If AMT device is created on a lower device in another netns, removing the lower device triggers the splat below and gets stuck until all upper devices are removed. [0]
The cross-netns setup seems unintentional considering 1. and the following points:
* amt_link_setup() sets dev->netns_immutable to true
* skb_scrub_packet() is not called in the fast path
* iproute2 binary fails to find cross-netns lower device via link-netns:
# ip -n ns1 link add amt0 link-netns ns2 type amt dev veth1 Cannot find device "veth1"
Instead of supporting it properly and preparing for per-netns netdev unreg, let's forbid cross-netns setup.
Note that the problem 2. needs a separate fix.
[0]:
WARNING: net/core/dev.c:12518 at unregister_netdevice_many_notify+0x1cce/0x2250, CPU#48: ip/2031
Modules linked in:
CPU: 48 UID: 0 PID: 2031 Comm: ip Not tainted 7.2.0-rc5+ #27 PREEMPT(full)
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
RIP: 0010:unregister_netdevice_many_notify (net/core/dev.c:12518)
Code: 89 ef e8 d5 52 ae fe e9 d0 f4 ff ff 48 8d 3d f9 3b 9c 02 48 c7 c6 c0 0b 63 84 ba ab 1f 00 00 67 48 0f b9 3a e9 65 ff ff ff 90 <0f> 0b 90 eb 81 48 8d 3d f6 3b 9c 02 48 c7 c6 c0 0b 63 84 ba e2 1f
RSP: 0018:ffffc90004abf160 EFLAGS: 00010212
RAX: ffff888104d38260 RBX: ffff88800b0911b8 RCX: dffffc0000000000
RDX: 0000000000000000 RSI: 0000000000000008 RDI: ffffffff85b9f880
RBP: ffffc90004abf2d0 R08: ffffffff85b9f887 R09: 1ffffffff0b73f10
R10: dffffc0000000000 R11: fffffbfff0b73f11 R12: ffff88800b091d08
R13: ffff88800b091178 R14: dffffc0000000000 R15: ffff88800b091000
FS: 00007f555b86c600(0000) GS:ffff8881942a0000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000562107d489c0 CR3: 0000000109a40002 CR4: 0000000000372ef0
Call Trace:
<TASK>
rtnl_dellink (net/core/rtnetlink.c:3632 net/core/rtnetlink.c:3674)
rtnetlink_rcv_msg (net/core/rtnetlink.c:7112)
netlink_rcv_skb (net/netlink/af_netlink.c:2556)
netlink_unicast (net/netlink/af_netlink.c:1319)
netlink_sendmsg (net/netlink/af_netlink.c:1900)
____sys_sendmsg (net/socket.c:775)
__sys_sendmsg (net/socket.c:2738)
do_syscall_64 (arch/x86/entry/syscall_64.c:63)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
...
unregister_netdevice: waiting for veth0 to become free. Usage count = 7
ref_tracker: netdev@ffff88800d7496d8 has 3/3 users at
__netdev_adjacent_dev_insert (./include/linux/netdevice.h:4525 ./include/linux/netdevice.h:4554 net/core/dev.c:8791)
__netdev_upper_dev_link (net/core/dev.c:8879 net/core/dev.c:8963)
netdev_upper_dev_link (net/core/dev.c:9009)
amt_newlink (drivers/net/amt.c:3321)--- drivers/net/amt.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/amt.c +3 -0
@@ -3195,6 +3195,9 @@ static int amt_newlink(struct net *net, struct net_device *dev,struct amt_dev *amt =netdev_priv (dev);int err = -EINVAL ; +if (!net_eq (net,dev_net (dev))) +return err; + amt->net = net; amt->mode =nla_get_u32 (data[IFLA_AMT_MODE ]);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1558/1887 NFS: Return a delegation the client fails to record
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 220af23d863995091f0edeb1e6aa0945b3db8b37 ]
When an NFS server grants a delegation in an OPEN reply, nfs_inode_set_delegation() records it on the client. However, three of its error flows return without sending DELEGRETURN.
A delegation can be relinquished only by DELEGRETURN (RFC 8881 Section 20.2.4), so dropping one silently leaves the server believing the client still holds it. If the server happens to recall that delegation, the client answers CB_RECALL with NFS4ERR_BADHANDLE because it has no record of the stateid. The server revokes the delegation and moves it onto its cl_revoked list, because the client never sends the FREE_STATEID that would drain it. Every subsequent SEQUENCE reply then carries SEQ4_STATUS_RECALLABLE_STATE_REVOKED, and the client's state manager loops issuing TEST_STATEID across its delegations without ever clearing the condition.
The window is easy to reach now that a server offers a write delegation on any write OPEN: a delegation recalled for one opener races a re-open that the server answers with a fresh write delegation.
Instead of dropping it, hand the delegation back during these error flows.
--- fs/nfs/delegation.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
fs/nfs/delegation.c +14 -4
@@ -443,11 +443,14 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,struct nfs_inode *nfsi =NFS_I (inode);struct nfs_delegation *delegation, *old_delegation;struct nfs_delegation *freeme =NULL ; +bool orphaned = false;int status =0 ; delegation =kmalloc (sizeof (*delegation),GFP_KERNEL_ACCOUNT ); -if (delegation ==NULL ) +if (delegation ==NULL ) { +nfs4_proc_delegreturn (inode, cred, stateid,0 );return -ENOMEM ; + }nfs4_stateid_copy (&delegation->stateid, stateid);refcount_set (&delegation->refcount,1 ); delegation->type = type;
@@ -489,12 +492,16 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, goto out; }if (test_and_set_bit (NFS_DELEGATION_RETURNING , - &old_delegation->flags)) + &old_delegation->flags)) { + orphaned = true; goto out; + } } freeme =nfs_detach_delegation_locked (nfsi, old_delegation, clp); -if (freeme ==NULL ) +if (freeme ==NULL ) { + orphaned = true; goto out; + } add_new:/* * If we didn't revalidate the change attribute before setting
@@ -521,8 +528,11 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,trace_nfs4_set_delegation (inode, type); out:spin_unlock (&clp->cl_lock); -if (delegation !=NULL ) +if (delegation !=NULL ) { +if (orphaned) +nfs_do_return_delegation (inode, delegation,0 );__nfs_free_delegation (delegation); + }if (freeme !=NULL ) {nfs_do_return_delegation (inode, freeme,0 );nfs_free_delegation (freeme);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1559/1887 fs: dlm: use listen sock as dlm running indicator
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Aring <[email protected]>
[ Upstream commit 1037c2a94ab51997d8b1ef9e7f6ed697e6e17d84 ]
This patch will switch from dlm_allow_conn to check if dlm lowcomms is running or not to if we actually have a listen socket set or not. The list socket will be set and unset in lowcomms start and shutdown functionality. To synchronize with data_ready() callback we will set the socket callback to NULL while socket lock is held.
Stable-dep-of: 33930840b5f0 ("sunrpc: xprtsock: annotate shared socket callbacks with READ_ONCE/WRITE_ONCE")
--- fs/dlm/config.c | 4 ++-- fs/dlm/lowcomms.c | 17 ++++++----------- fs/dlm/lowcomms.h | 4 ++-- 3 files changed, 10 insertions(+), 15 deletions(-)
fs/dlm/config.c +2 -2
@@ -183,7 +183,7 @@ static int dlm_check_protocol_and_dlm_running(unsigned int x)return -EINVAL ; } -if (dlm_allow_conn) +if (dlm_lowcomms_is_running ())return -EBUSY ;return 0 ;
@@ -194,7 +194,7 @@ static int dlm_check_zero_and_dlm_running(unsigned int x)if (!x)return -EINVAL ; -if (dlm_allow_conn) +if (dlm_lowcomms_is_running ())return -EBUSY ;return 0 ;
fs/dlm/lowcomms.c +6 -11
@@ -176,7 +176,6 @@ static DEFINE_SPINLOCK(dlm_node_addrs_spin);static struct listen_connection listen_con;static struct sockaddr_storage dlm_local_addr[DLM_MAX_ADDR_COUNT ];static int dlm_local_count; -int dlm_allow_conn;/* Work queues */ static struct workqueue_struct *recv_workqueue;
@@ -191,6 +190,11 @@ static const struct dlm_proto_ops *dlm_proto_ops;static void process_recv_sockets (struct work_struct *work);static void process_send_sockets (struct work_struct *work); +bool dlm_lowcomms_is_running (void ) +{ +return !!listen_con.sock; +} +static void writequeue_entry_ctor (void *data) {struct writequeue_entry *entry = data;
@@ -514,9 +518,6 @@ static void lowcomms_data_ready(struct sock *sk)static void lowcomms_listen_data_ready (struct sock *sk) { -if (!dlm_allow_conn) -return ; -queue_work (recv_workqueue, &listen_con.rwork); }
@@ -1676,10 +1677,7 @@ void dlm_lowcomms_shutdown(void) {int idx; -/* Set all the flags to prevent any -* socket activity. -*/ - dlm_allow_conn =0 ; +restore_callbacks (listen_con.sock);if (recv_workqueue)flush_workqueue (recv_workqueue);
@@ -1982,8 +1980,6 @@ int dlm_lowcomms_start(void)if (error) goto fail; - dlm_allow_conn =1 ; -/* Start listening */ switch (dlm_config.ci_protocol) {case DLM_PROTO_TCP :
@@ -2008,7 +2004,6 @@ int dlm_lowcomms_start(void) fail_listen: dlm_proto_ops =NULL ; fail_proto_ops: - dlm_allow_conn =0 ;work_stop (); fail:return error;
fs/dlm/lowcomms.h +2 -2
@@ -29,8 +29,8 @@ static inline int nodeid_hash(int nodeid)return nodeid & (CONN_HASH_SIZE -1 ); } -/* switch to check if dlm is running */ -extern int dlm_allow_conn; +/* check if dlm is running */ +bool dlm_lowcomms_is_running (void );int dlm_lowcomms_start (void );void dlm_lowcomms_shutdown (void );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1560/1887 fs: dlm: remove socket shutdown handling
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Aring <[email protected]>
[ Upstream commit 4f567acb0b8622fe265aac4b0037a03ef544ba24 ]
Since commit 489d8e559c65 ("fs: dlm: add reliable connection if reconnect") we have functionality like TCP offers for half-closed sockets on dlm application protocol layer. This feature is required because the cluster manager events about leaving resource memberships can be locally already occurred but other cluster nodes having a pending leaving membership over the cluster manager protocol happening. In this time the local dlm node already shutdown it's connection and don't transmit anymore any new dlm messages, but however it still needs to be able to accept dlm messages because the pending leave membership request of the cluster manager protocol which the dlm kernel implementation has no control about it.
We have this functionality on the application for two reasons, the main reason is that SCTP does not support such functionality on socket layer. But we can do it inside application layer.
Another small issue is that this feature is broken in the TCP world because some NAT devices does not implement such functionality correctly. This is the same reason why the reliable connection session layer in DLM exists. We give up on middle devices in the networking which sends e.g. TCP resets out. In DLM we cannot have any message dropping and we ensure it over a session layer that it can't happen.
Back to the half-closed grace shutdown handling. It's not necessary anymore to do it on socket layer (which is only support for TCP sockets) because we do it on application layer. This patch removes this handling, if there are still issues then we have a problem on the application layer for such handling.
Stable-dep-of: 33930840b5f0 ("sunrpc: xprtsock: annotate shared socket callbacks with READ_ONCE/WRITE_ONCE")
--- fs/dlm/lowcomms.c | 127 ++++++++-------------------------------------- fs/dlm/lowcomms.h | 1 + fs/dlm/midcomms.c | 6 ++- 3 files changed, 27 insertions(+), 107 deletions(-)
fs/dlm/lowcomms.c +22 -105
@@ -65,7 +65,6 @@/* Number of messages to send before rescheduling */ #define MAX_SEND_MSG_COUNT 25 -#define DLM_SHUTDOWN_WAIT_TIMEOUT msecs_to_jiffies(10000)struct connection {struct socket *sock;/* NULL if not connected */
@@ -79,14 +78,11 @@ struct connection {#define CF_CLOSE 6#define CF_APP_LIMITED 7#define CF_CLOSING 8 -#define CF_SHUTDOWN 9 -#define CF_CONNECTED 10 -#define CF_RECONNECT 11 -#define CF_DELAY_CONNECT 12 -#define CF_EOF 13 +#define CF_CONNECTED 9 +#define CF_RECONNECT 10 +#define CF_DELAY_CONNECT 11struct list_head writequeue;/* List of outgoing writequeue_entries */ spinlock_t writequeue_lock; -atomic_t writequeue_cnt;int retries;#define MAX_CONNECT_RETRIES 3struct hlist_node list;
@@ -94,7 +90,6 @@ struct connection {struct connection *sendcon;struct work_struct rwork;/* Receive workqueue */ struct work_struct swork;/* Send workqueue */ -wait_queue_head_t shutdown_wait;/* wait for graceful shutdown */ unsigned char *rx_buf;int rx_buflen;int rx_leftover;
@@ -157,10 +152,6 @@ struct dlm_proto_ops {int (*listen_validate)(void );void (*listen_sockopts)(struct socket *sock);int (*listen_bind)(struct socket *sock); -/* What to do to shutdown */ -void (*shutdown_action)(struct connection *con); -/* What to do to eof check */ -bool (*eof_condition)(struct connection *con); }; static struct listen_sock_callbacks {
@@ -244,11 +235,6 @@ static struct connection *__find_con(int nodeid, int r)return NULL ; } -static bool tcp_eof_condition (struct connection *con) -{ -return atomic_read (&con->writequeue_cnt); -} -static int dlm_con_init (struct connection *con,int nodeid) { con->rx_buflen = dlm_config.ci_buffer_size;
@@ -260,10 +246,8 @@ static int dlm_con_init(struct connection *con, int nodeid)mutex_init (&con->sock_mutex);INIT_LIST_HEAD (&con->writequeue);spin_lock_init (&con->writequeue_lock); -atomic_set (&con->writequeue_cnt,0 );INIT_WORK (&con->swork, process_send_sockets);INIT_WORK (&con->rwork, process_recv_sockets); -init_waitqueue_head (&con->shutdown_wait);return 0 ; }
@@ -774,7 +758,6 @@ static void free_entry(struct writequeue_entry *e) }list_del (&e->list); -atomic_dec (&e->con->writequeue_cnt);kref_put (&e->ref, dlm_page_release); }
@@ -837,56 +820,10 @@ static void close_connection(struct connection *con, bool and_other,clear_bit (CF_CONNECTED , &con->flags);clear_bit (CF_DELAY_CONNECT , &con->flags);clear_bit (CF_RECONNECT , &con->flags); -clear_bit (CF_EOF , &con->flags);mutex_unlock (&con->sock_mutex);clear_bit (CF_CLOSING , &con->flags); } -static void shutdown_connection (struct connection *con) -{ -int ret; - -flush_work (&con->swork); - -mutex_lock (&con->sock_mutex); -/* nothing to shutdown */ -if (!con->sock) { -mutex_unlock (&con->sock_mutex); -return ; - } - -set_bit (CF_SHUTDOWN , &con->flags); - ret =kernel_sock_shutdown (con->sock,SHUT_WR ); -mutex_unlock (&con->sock_mutex); -if (ret) { -log_print ("Connection %p failed to shutdown: %d will force close" , - con, ret); - goto force_close; - }else { - ret =wait_event_timeout (con->shutdown_wait, - !test_bit (CF_SHUTDOWN , &con->flags), -DLM_SHUTDOWN_WAIT_TIMEOUT ); -if (ret ==0 ) { -log_print ("Connection %p shutdown timed out, will force close" , - con); - goto force_close; - } - } - -return ; - -force_close: -clear_bit (CF_SHUTDOWN , &con->flags); -close_connection (con, false, true, true); -} - -static void dlm_tcp_shutdown (struct connection *con) -{ -if (con->othercon) -shutdown_connection (con->othercon); -shutdown_connection (con); -} -static int con_realloc_receive_buf (struct connection *con,int newlen) {unsigned char *newbuf;
@@ -978,19 +915,8 @@ static int receive_from_sock(struct connection *con)log_print ("connection %p got EOF from %d" , con, con->nodeid); -if (dlm_proto_ops->eof_condition && - dlm_proto_ops->eof_condition (con)) { -set_bit (CF_EOF , &con->flags); -mutex_unlock (&con->sock_mutex); - }else { -mutex_unlock (&con->sock_mutex); -close_connection (con, false, true, false); - -/* handling for tcp shutdown */ -clear_bit (CF_SHUTDOWN , &con->flags); -wake_up (&con->shutdown_wait); - } - +mutex_unlock (&con->sock_mutex); +close_connection (con, false, true, false);/* signal to breaking receive worker */ ret =-1 ; }else {
@@ -1253,7 +1179,6 @@ static struct writequeue_entry *new_wq_entry(struct connection *con, int len,kref_get (&e->ref); *ppc =page_address (e->page); e->end += len; -atomic_inc (&con->writequeue_cnt);if (cb)cb (data);
@@ -1454,20 +1379,6 @@ static void send_to_sock(struct connection *con) }spin_unlock (&con->writequeue_lock); -/* close if we got EOF */ -if (test_and_clear_bit (CF_EOF , &con->flags)) { -mutex_unlock (&con->sock_mutex); -close_connection (con, false, false, true); - -/* handling for tcp shutdown */ -clear_bit (CF_SHUTDOWN , &con->flags); -wake_up (&con->shutdown_wait); - }else { -mutex_unlock (&con->sock_mutex); - } - -return ; - out:mutex_unlock (&con->sock_mutex);return ;
@@ -1667,16 +1578,8 @@ static int work_start(void)return 0 ; } -static void shutdown_conn (struct connection *con) -{ -if (dlm_proto_ops->shutdown_action) - dlm_proto_ops->shutdown_action (con); -} -void dlm_lowcomms_shutdown (void ) { -int idx; -restore_callbacks (listen_con.sock);if (recv_workqueue)
@@ -1685,9 +1588,25 @@ void dlm_lowcomms_shutdown(void)flush_workqueue (send_workqueue);dlm_close_sock (&listen_con.sock); +} + +void dlm_lowcomms_shutdown_node (int nodeid,bool force) +{ +struct connection *con; +int idx; idx =srcu_read_lock (&connections_srcu); -foreach_conn (shutdown_conn); + con =nodeid2con (nodeid,0 ); +if (WARN_ON_ONCE (!con)) { +srcu_read_unlock (&connections_srcu, idx); +return ; + } + +WARN_ON_ONCE (!force && !list_empty (&con->writequeue)); +clean_one_writequeue (con); +if (con->othercon) +clean_one_writequeue (con->othercon); +close_connection (con, true, true, true);srcu_read_unlock (&connections_srcu, idx); }
@@ -1899,8 +1818,6 @@ static const struct dlm_proto_ops dlm_tcp_ops = { .listen_validate = dlm_tcp_listen_validate, .listen_sockopts = dlm_tcp_listen_sockopts, .listen_bind = dlm_tcp_listen_bind, - .shutdown_action = dlm_tcp_shutdown, - .eof_condition = tcp_eof_condition, };static int dlm_sctp_bind (struct socket *sock)
fs/dlm/lowcomms.h +1 -0
@@ -34,6 +34,7 @@ bool dlm_lowcomms_is_running(void);int dlm_lowcomms_start (void );void dlm_lowcomms_shutdown (void ); +void dlm_lowcomms_shutdown_node (int nodeid,bool force);void dlm_lowcomms_stop (void );void dlm_lowcomms_init (void );void dlm_lowcomms_exit (void );
fs/dlm/midcomms.c +4 -2
@@ -1416,11 +1416,13 @@ static void midcomms_shutdown(struct midcomms_node *node)pr_debug ("active shutdown timed out for node %d with state %s\n" , node->nodeid,dlm_state_str (node->state));midcomms_node_reset (node); +dlm_lowcomms_shutdown_node (node->nodeid, true);return ; }pr_debug ("active shutdown done for node %d with state %s\n" , node->nodeid,dlm_state_str (node->state)); +dlm_lowcomms_shutdown_node (node->nodeid, false); }void dlm_midcomms_shutdown (void )
@@ -1428,6 +1430,8 @@ void dlm_midcomms_shutdown(void)struct midcomms_node *node;int i, idx; +dlm_lowcomms_shutdown (); +mutex_lock (&close_lock); idx =srcu_read_lock (&nodes_srcu);for (i =0 ; i <CONN_HASH_SIZE ; i++) {
@@ -1445,8 +1449,6 @@ void dlm_midcomms_shutdown(void) }srcu_read_unlock (&nodes_srcu, idx);mutex_unlock (&close_lock); - -dlm_lowcomms_shutdown (); }int dlm_midcomms_close (int nodeid)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1561/1887 fs: dlm: cleanup listen sock handling
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Aring <[email protected]>
[ Upstream commit c3d88dfd15835f42c79467eee2c40db3095e5271 ]
This patch removes save_listen_callbacks() and add_listen_sock() as they are only used once in lowcomms functionality. For shutdown lowcomms it's not necessary to whole flush the workqueues to synchronize with restoring the old sk_data_ready() callback. Only the listen con receive work need to be cancelled. For each individual node shutdown we should be sure that last ack was been transmitted which is done by flushing per connection swork worker.
Stable-dep-of: 33930840b5f0 ("sunrpc: xprtsock: annotate shared socket callbacks with READ_ONCE/WRITE_ONCE")
--- fs/dlm/lowcomms.c | 51 ++++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 34 deletions(-)
fs/dlm/lowcomms.c +17 -34
@@ -650,17 +650,6 @@ static void lowcomms_error_report(struct sock *sk)orig_report (sk); } -/* Note: sk_callback_lock must be locked before calling this function. */ -static void save_listen_callbacks (struct socket *sock) -{ -struct sock *sk = sock->sk; - - listen_sock.sk_data_ready = sk->sk_data_ready; - listen_sock.sk_state_change = sk->sk_state_change; - listen_sock.sk_write_space = sk->sk_write_space; - listen_sock.sk_error_report = sk->sk_error_report; -} -static void restore_callbacks (struct socket *sock) {struct sock *sk = sock->sk;
@@ -674,21 +663,6 @@ static void restore_callbacks(struct socket *sock)release_sock (sk); } -static void add_listen_sock (struct socket *sock,struct listen_connection *con) -{ -struct sock *sk = sock->sk; - -lock_sock (sk); -save_listen_callbacks (sock); - con->sock = sock; - - sk->sk_user_data = con; - sk->sk_allocation =GFP_NOFS ; -/* Install a data_ready callback */ - sk->sk_data_ready = lowcomms_listen_data_ready; -release_sock (sk); -} -/* Make a socket active */ static void add_sock (struct socket *sock,struct connection *con) {
@@ -1580,13 +1554,12 @@ static int work_start(void)void dlm_lowcomms_shutdown (void ) { -restore_callbacks (listen_con.sock); - -if (recv_workqueue) -flush_workqueue (recv_workqueue); -if (send_workqueue) -flush_workqueue (send_workqueue); +/* stop lowcomms_listen_data_ready calls */ +lock_sock (listen_con.sock->sk); + listen_con.sock->sk->sk_data_ready = listen_sock.sk_data_ready; +release_sock (listen_con.sock->sk); +cancel_work_sync (&listen_con.rwork);dlm_close_sock (&listen_con.sock); }
@@ -1602,6 +1575,7 @@ void dlm_lowcomms_shutdown_node(int nodeid, bool force)return ; } +flush_work (&con->swork);WARN_ON_ONCE (!force && !list_empty (&con->writequeue));clean_one_writequeue (con);if (con->othercon)
@@ -1722,8 +1696,17 @@ static int dlm_listen_for_all(void)if (result <0 ) goto out; -save_listen_callbacks (sock); -add_listen_sock (sock, &listen_con); +lock_sock (sock->sk); + listen_sock.sk_data_ready = sock->sk->sk_data_ready; + listen_sock.sk_write_space = sock->sk->sk_write_space; + listen_sock.sk_error_report = sock->sk->sk_error_report; + listen_sock.sk_state_change = sock->sk->sk_state_change; + + listen_con.sock = sock; + + sock->sk->sk_allocation =GFP_NOFS ; + sock->sk->sk_data_ready = lowcomms_listen_data_ready; +release_sock (sock->sk);INIT_WORK (&listen_con.rwork, process_listen_recv_socket); result = sock->ops->listen (sock,5 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1562/1887 net: Introduce sk_use_task_frag in struct sock.
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guillaume Nault <[email protected]>
[ Upstream commit fb87bd47516d9a26b6d549231aa743b20fd4a569 ]
Sockets that can be used while recursing into memory reclaim, like those used by network block devices and file systems, mustn't use current->task_frag: if the current process is already using it, then the inner memory reclaim call would corrupt the task_frag structure.
To avoid this, sk_page_frag() uses ->sk_allocation to detect sockets that mustn't use current->task_frag, assuming that those used during memory reclaim had their allocation constraints reflected in ->sk_allocation.
This unfortunately doesn't cover all cases: in an attempt to remove all usage of GFP_NOFS and GFP_NOIO, sunrpc stopped setting these flags in ->sk_allocation, and used memalloc_nofs critical sections instead. This breaks the sk_page_frag() heuristic since the allocation constraints are now stored in current->flags, which sk_page_frag() can't read without risking triggering a cache miss and slowing down TCP's fast path.
This patch creates a new field in struct sock, named sk_use_task_frag, which sockets with memory reclaim constraints can set to false if they can't safely use current->task_frag. In such cases, sk_page_frag() now always returns the socket's page_frag (->sk_frag). The first user is sunrpc, which needs to avoid using current->task_frag but can keep ->sk_allocation set to GFP_KERNEL otherwise.
Eventually, it might be possible to simplify sk_page_frag() by only testing ->sk_use_task_frag and avoid relying on the ->sk_allocation heuristic entirely (assuming other sockets will set ->sk_use_task_frag according to their constraints in the future).
The new ->sk_use_task_frag field is placed in a hole in struct sock and belongs to a cache line shared with ->sk_shutdown. Therefore it should be hot and shouldn't have negative performance impacts on TCP's fast path (sk_shutdown is tested just before the while() loop in tcp_sendmsg_locked()).
Stable-dep-of: 33930840b5f0 ("sunrpc: xprtsock: annotate shared socket callbacks with READ_ONCE/WRITE_ONCE")
--- include/net/sock.h | 11 +++++++++-- net/core/sock.c | 1 + 2 files changed, 10 insertions(+), 2 deletions(-)
include/net/sock.h +9 -2
@@ -319,6 +319,9 @@ struct sk_filter;* @sk_stamp: time stamp of last packet received * @sk_stamp_seq: lock for accessing sk_stamp on 32 bit architectures only * @sk_tsflags: SO_TIMESTAMPING flags +* @sk_use_task_frag: allow sk_page_frag() to use current->task_frag. +* Sockets that can be used under memory reclaim should +* set this to false. * @sk_bind_phc: SO_TIMESTAMPING bind PHC index of PTP virtual clock * for timestamping * @sk_tskey: counter to disambiguate concurrent tstamp requests
@@ -520,6 +523,7 @@ struct sock { u8 sk_txtime_deadline_mode : 1, sk_txtime_report_errors : 1, sk_txtime_unused : 6; +bool sk_use_task_frag;struct socket *sk_socket;void *sk_user_data;
@@ -2685,14 +2689,17 @@ static inline void sk_stream_moderate_sndbuf(struct sock *sk)* socket operations and end up recursing into sk_page_frag() * while it's already in use: explicitly avoid task page_frag * usage if the caller is potentially doing any of them. -* This assumes that page fault handlers use the GFP_NOFS flags. +* This assumes that page fault handlers use the GFP_NOFS flags or +* explicitly disable sk_use_task_frag. * * Return: a per task page_frag if context allows that, * otherwise a per socket one. */ static inline struct page_frag *sk_page_frag (struct sock *sk) { -if ((sk->sk_allocation & (__GFP_DIRECT_RECLAIM | __GFP_MEMALLOC | __GFP_FS)) == +if (sk->sk_use_task_frag && + (sk->sk_allocation & (__GFP_DIRECT_RECLAIM | __GFP_MEMALLOC | + __GFP_FS)) == (__GFP_DIRECT_RECLAIM | __GFP_FS))return ¤t->task_frag;
net/core/sock.c +1 -0
@@ -3440,6 +3440,7 @@ void sock_init_data_uid(struct socket *sock, struct sock *sk, kuid_t uid) sk->sk_rcvbuf =READ_ONCE (sysctl_rmem_default); sk->sk_sndbuf =READ_ONCE (sysctl_wmem_default); sk->sk_state =TCP_CLOSE ; + sk->sk_use_task_frag = true;sk_set_socket (sk, sock);sock_set_flag (sk,SOCK_ZAPPED );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1563/1887 Treewide: Stop corrupting sockets task_frag
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Coddington <[email protected]>
[ Upstream commit 98123866fcf3fe95a0c1b198ef122dfdbd351916 ]
Since moving to memalloc_nofs_save/restore, SUNRPC has stopped setting the GFP_NOIO flag on sk_allocation which the networking system uses to decide when it is safe to use current->task_frag. The results of this are unexpected corruption in task_frag when SUNRPC is involved in memory reclaim.
The corruption can be seen in crashes, but the root cause is often difficult to ascertain as a crashing machine's stack trace will have no evidence of being near NFS or SUNRPC code. I believe this problem to be much more pervasive than reports to the community may indicate.
Fix this by having kernel users of sockets that may corrupt task_frag due to reclaim set sk_use_task_frag = false. Preemptively correcting this situation for users that still set sk_allocation allows them to convert to memalloc_nofs_save/restore without the same unexpected corruptions that are sure to follow, unlikely to show up in testing, and difficult to bisect.
Stable-dep-of: 33930840b5f0 ("sunrpc: xprtsock: annotate shared socket callbacks with READ_ONCE/WRITE_ONCE")
--- drivers/block/drbd/drbd_receiver.c | 3 +++ drivers/block/nbd.c | 1 + drivers/nvme/host/tcp.c | 1 + drivers/scsi/iscsi_tcp.c | 1 + drivers/usb/usbip/usbip_common.c | 1 + fs/dlm/lowcomms.c | 2 ++ fs/ocfs2/cluster/tcp.c | 1 + fs/smb/client/connect.c | 1 + net/9p/trans_fd.c | 1 + net/ceph/messenger.c | 1 + net/sunrpc/xprtsock.c | 3 +++ net/xfrm/espintcp.c | 1 + 12 files changed, 17 insertions(+)
drivers/block/drbd/drbd_receiver.c +3 -0
@@ -1030,6 +1030,9 @@ static int conn_connect(struct drbd_connection *connection) sock.socket->sk->sk_allocation =GFP_NOIO ; msock.socket->sk->sk_allocation =GFP_NOIO ; + sock.socket->sk->sk_use_task_frag = false; + msock.socket->sk->sk_use_task_frag = false; + sock.socket->sk->sk_priority =TC_PRIO_INTERACTIVE_BULK ; msock.socket->sk->sk_priority =TC_PRIO_INTERACTIVE ;
drivers/block/nbd.c +1 -0
@@ -520,6 +520,7 @@ static int __sock_xmit(struct nbd_device *nbd, struct socket *sock, int send, noreclaim_flag =memalloc_noreclaim_save (); do { sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC; + sock->sk->sk_use_task_frag = false; msg.msg_flags = msg_flags | MSG_NOSIGNAL; if (send)
drivers/nvme/host/tcp.c +1 -0
@@ -1655,6 +1655,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid) queue->sock->sk->sk_rcvtimeo =10 *HZ ; queue->sock->sk->sk_allocation =GFP_ATOMIC ; + queue->sock->sk->sk_use_task_frag = false;nvme_tcp_set_queue_io_cpu (queue); queue->request =NULL ; queue->data_remaining =0 ;
drivers/scsi/iscsi_tcp.c +1 -0
@@ -742,6 +742,7 @@ iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session, sk->sk_reuse =SK_CAN_REUSE ; sk->sk_sndtimeo =15 *HZ ;/* FIXME: make it configurable */ sk->sk_allocation =GFP_ATOMIC ; + sk->sk_use_task_frag = false;sk_set_memalloc (sk);sock_no_linger (sk);
drivers/usb/usbip/usbip_common.c +1 -0
@@ -315,6 +315,7 @@ int usbip_recv(struct socket *sock, void *buf, int size) do { sock->sk->sk_allocation = GFP_NOIO; + sock->sk->sk_use_task_frag = false; result = sock_recvmsg(sock, &msg, MSG_WAITALL); if (result <= 0)
fs/dlm/lowcomms.c +2 -0
@@ -677,6 +677,7 @@ static void add_sock(struct socket *sock, struct connection *con) sk->sk_write_space = lowcomms_write_space; sk->sk_state_change = lowcomms_state_change; sk->sk_allocation =GFP_NOFS ; + sk->sk_use_task_frag = false; sk->sk_error_report = lowcomms_error_report;release_sock (sk); }
@@ -1705,6 +1706,7 @@ static int dlm_listen_for_all(void) listen_con.sock = sock; sock->sk->sk_allocation =GFP_NOFS ; + sock->sk->sk_use_task_frag = false; sock->sk->sk_data_ready = lowcomms_listen_data_ready;release_sock (sock->sk);
fs/ocfs2/cluster/tcp.c +1 -0
@@ -1604,6 +1604,7 @@ static void o2net_start_connect(struct work_struct *work) sc->sc_sock = sock;/* freed by sc_kref_release */ sock->sk->sk_allocation =GFP_ATOMIC ; + sock->sk->sk_use_task_frag = false; myaddr.sin_family =AF_INET ; myaddr.sin_addr.s_addr = mynode->nd_ipv4_address;
fs/smb/client/connect.c +1 -0
@@ -3115,6 +3115,7 @@ generic_ip_connect(struct TCP_Server_Info *server)cifs_dbg (FYI ,"Socket created\n" ); server->ssocket = socket; socket->sk->sk_allocation =GFP_NOFS ; + socket->sk->sk_use_task_frag = false;if (sfamily ==AF_INET6 )cifs_reclassify_socket6 (socket);else
net/9p/trans_fd.c +1 -0
@@ -875,6 +875,7 @@ static int p9_socket_open(struct p9_client *client, struct socket *csocket) } csocket->sk->sk_allocation =GFP_NOIO ; + csocket->sk->sk_use_task_frag = false; file =sock_alloc_file (csocket,0 ,NULL );if (IS_ERR (file)) { pr_err("%s (%d): failed to map fd\n",
net/ceph/messenger.c +1 -0
@@ -446,6 +446,7 @@ int ceph_tcp_connect(struct ceph_connection *con)if (ret)return ret; sock->sk->sk_allocation =GFP_NOFS ; + sock->sk->sk_use_task_frag = false;#ifdef CONFIG_LOCKDEP lockdep_set_class (&sock->sk->sk_lock, &socket_class);
net/sunrpc/xprtsock.c +3 -0
@@ -1889,6 +1889,7 @@ static int xs_local_finish_connecting(struct rpc_xprt *xprt, sk->sk_write_space = xs_udp_write_space; sk->sk_state_change = xs_local_state_change; sk->sk_error_report = xs_error_report; + sk->sk_use_task_frag = false;xprt_clear_connected (xprt);
@@ -2089,6 +2090,7 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) sk->sk_user_data = xprt; sk->sk_data_ready = xs_data_ready; sk->sk_write_space = xs_udp_write_space; + sk->sk_use_task_frag = false;xprt_set_connected (xprt);
@@ -2257,6 +2259,7 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) sk->sk_state_change = xs_tcp_state_change; sk->sk_write_space = xs_tcp_write_space; sk->sk_error_report = xs_error_report; + sk->sk_use_task_frag = false;/* socket options */ sock_reset_flag (sk,SOCK_LINGER );
net/xfrm/espintcp.c +1 -0
@@ -491,6 +491,7 @@ static int espintcp_init_sk(struct sock *sk)/* avoid using task_frag */ sk->sk_allocation =GFP_ATOMIC ; + sk->sk_use_task_frag = false;return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1564/1887 sunrpc: xprtsock: annotate shared socket callbacks with READ_ONCE/WRITE_ONCE
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
[ Upstream commit 33930840b5f0a79f826e7c69dc6cd78f72a67481 ]
xprtsock replaces and restores sk->sk_data_ready and sk->sk_write_space on live sockets with plain stores, and xs_udp_do_set_buffer_size() invokes sk->sk_write_space via a plain load. These callback pointers are shared with generic socket and protocol paths that may read or invoke them concurrently, so xprtsock needs the same READ_ONCE()/WRITE_ONCE() callback visibility contract that the validated 4022 family applied elsewhere.
When SUNRPC takes over an AF_LOCAL, UDP, or TCP socket and later restores the lower-socket callbacks during teardown, another CPU may still hold an earlier callback snapshot. The plain replace/restore pattern leaves the same visibility hole as the validated 4022 family, so a stale snapshot can still invoke xs_data_ready() or xs_udp_write_space() after the live callback fields have already been restored to the lower-socket handlers.
Use WRITE_ONCE() for the shared sk_data_ready and sk_write_space stores in xs_local_finish_connecting(), xs_udp_finish_connecting(), xs_tcp_finish_connecting(), and xs_restore_old_callbacks(). Use READ_ONCE() for the direct sk_write_space invocation in xs_udp_do_set_buffer_size(). This matches the required callback visibility contract while leaving adjacent sk_state_change and sk_error_report handling unchanged.
--- net/sunrpc/xprtsock.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
net/sunrpc/xprtsock.c +11 -9
@@ -1115,9 +1115,9 @@ static void xs_save_old_callbacks(struct sock_xprt *transport, struct sock *sk)static void xs_restore_old_callbacks (struct sock_xprt *transport,struct sock *sk) { - sk->sk_data_ready = transport->old_data_ready; +WRITE_ONCE (sk->sk_data_ready, transport->old_data_ready); sk->sk_state_change = transport->old_state_change; - sk->sk_write_space = transport->old_write_space; +WRITE_ONCE (sk->sk_write_space, transport->old_write_space); sk->sk_error_report = transport->old_error_report; }
@@ -1563,6 +1563,7 @@ static void xs_udp_do_set_buffer_size(struct rpc_xprt *xprt) { struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);struct sock *sk = transport->inet; +void (*write_space)(struct sock *sock);if (transport->rcvsize) { sk->sk_userlocks |=SOCK_RCVBUF_LOCK ;
@@ -1571,7 +1572,8 @@ static void xs_udp_do_set_buffer_size(struct rpc_xprt *xprt)if (transport->sndsize) { sk->sk_userlocks |=SOCK_SNDBUF_LOCK ; sk->sk_sndbuf = transport->sndsize * xprt->max_reqs *2 ; - sk->sk_write_space (sk); + write_space =READ_ONCE (sk->sk_write_space); +write_space (sk); } }
@@ -1885,8 +1887,8 @@ static int xs_local_finish_connecting(struct rpc_xprt *xprt,xs_save_old_callbacks (transport, sk); sk->sk_user_data = xprt; - sk->sk_data_ready = xs_data_ready; - sk->sk_write_space = xs_udp_write_space; +WRITE_ONCE (sk->sk_data_ready, xs_data_ready); +WRITE_ONCE (sk->sk_write_space, xs_udp_write_space); sk->sk_state_change = xs_local_state_change; sk->sk_error_report = xs_error_report; sk->sk_use_task_frag = false;
@@ -2088,8 +2090,8 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)xs_save_old_callbacks (transport, sk); sk->sk_user_data = xprt; - sk->sk_data_ready = xs_data_ready; - sk->sk_write_space = xs_udp_write_space; +WRITE_ONCE (sk->sk_data_ready, xs_data_ready); +WRITE_ONCE (sk->sk_write_space, xs_udp_write_space); sk->sk_use_task_frag = false;xprt_set_connected (xprt);
@@ -2255,9 +2257,9 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)xs_save_old_callbacks (transport, sk); sk->sk_user_data = xprt; - sk->sk_data_ready = xs_data_ready; +WRITE_ONCE (sk->sk_data_ready, xs_data_ready); sk->sk_state_change = xs_tcp_state_change; - sk->sk_write_space = xs_tcp_write_space; +WRITE_ONCE (sk->sk_write_space, xs_tcp_write_space); sk->sk_error_report = xs_error_report; sk->sk_use_task_frag = false;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1565/1887 bpf: Fix mmap_lock leak in irq_work path
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sanghyun Park <[email protected]>
[ Upstream commit fa9dcacdcdf487f0ffef64bf67622f1caed509f1 ]
stack_map_get_build_id_offset() introduced a per-CPU irq_work to defer mmap_read_unlock() from NMI context, and bpf_find_vma() later reused the same mmap_unlock_work. Both callers only check whether the work is busy before taking mmap_lock, so a nested caller can reuse the slot before the first caller queues it. Two read locks may then be acquired while only one deferred unlock runs, leaking a read lock and blocking exit_mmap().
Reserve the per-CPU slot before mmap_read_trylock(). Use the same wrapper in stackmap and bpf_find_vma() so both callers release the reservation on trylock failure. Keep rejecting the slot while the irq_work remains busy. Release it after the irq_work callback unlocks the mm.
--- kernel/bpf/mmap_unlock_work.h | 51 ++++++++++++++++++++--------------- kernel/bpf/stackmap.c | 35 +++++++++++++----------- kernel/bpf/task_iter.c | 14 +++++++--- 3 files changed, 59 insertions(+), 41 deletions(-)
kernel/bpf/mmap_unlock_work.h +29 -22
@@ -4,12 +4,15 @@#ifndef __MMAP_UNLOCK_WORK_H__#define __MMAP_UNLOCK_WORK_H__ +#include <linux/atomic.h> +#include <linux/err.h> #include <linux/irq_work.h> /* irq_work to run mmap_read_unlock() in irq_work */ struct mmap_unlock_irq_work {struct irq_work irq_work;struct mm_struct *mm; +atomic_t active; }; DECLARE_PER_CPU(struct mmap_unlock_irq_work, mmap_unlock_work);
@@ -18,32 +21,36 @@ DECLARE_PER_CPU(struct mmap_unlock_irq_work, mmap_unlock_work);* We cannot do mmap_read_unlock() when the irq is disabled, because of * risk to deadlock with rq_lock. To look up vma when the irqs are * disabled, we need to run mmap_read_unlock() in irq_work. We use a -* percpu variable to do the irq_work. If the irq_work is already used -* by another lookup, we fall over. +* percpu variable to do the irq_work. The active flag reserves the slot +* before mmap_read_trylock() and until the irq_work callback consumes mm. */ -static inline bool bpf_mmap_unlock_get_irq_work (struct mmap_unlock_irq_work **work_ptr) +static inline struct mmap_unlock_irq_work *bpf_mmap_unlock_guard_get (void ) { -struct mmap_unlock_irq_work *work =NULL ; -bool irq_work_busy = false; +struct mmap_unlock_irq_work *work; -if (irqs_disabled ()) { -if (!IS_ENABLED (CONFIG_PREEMPT_RT )) { - work =this_cpu_ptr (&mmap_unlock_work); -if (irq_work_is_busy (&work->irq_work)) { -/* cannot queue more up_read, fallback */ - irq_work_busy = true; - } - }else { -/* -* PREEMPT_RT does not allow to trylock mmap sem in -* interrupt disabled context. Force the fallback code. -*/ - irq_work_busy = true; - } - } +if (!irqs_disabled ()) +return NULL ; + +/* +* PREEMPT_RT does not allow to trylock mmap sem in interrupt +* disabled context. Force the fallback code. +*/ +if (IS_ENABLED (CONFIG_PREEMPT_RT )) +return ERR_PTR (-EBUSY ); + + work =this_cpu_ptr (&mmap_unlock_work); +if (irq_work_is_busy (&work->irq_work) || +atomic_cmpxchg_acquire (&work->active,0 ,1 )) +return ERR_PTR (-EBUSY ); - *work_ptr = work; -return irq_work_busy; +return work; +} + +static inline void +bpf_mmap_unlock_guard_put (struct mmap_unlock_irq_work *work) +{ +if (work) +atomic_set_release (&work->active,0 ); }static inline void bpf_mmap_unlock_mm (struct mmap_unlock_irq_work *work,struct mm_struct *mm)
kernel/bpf/stackmap.c +20 -15
@@ -131,24 +131,20 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs, u64 *ips, u32 trace_nr, bool user) { int i; -struct mmap_unlock_irq_work *work =NULL ; -bool irq_work_busy =bpf_mmap_unlock_get_irq_work (&work); +struct mmap_unlock_irq_work *work;struct vm_area_struct *vma, *prev_vma =NULL ;const char *prev_build_id; -/* If the irq_work is in use, fall back to report ips. Same -* fallback is used for kernel stack (!user) on a stackmap with -* build_id. -*/ -if (!user || !current || !current->mm || irq_work_busy || - !mmap_read_trylock (current->mm)) { -/* cannot access current->mm, fall back to ips */ -for (i =0 ; i < trace_nr; i++) { - id_offs[i].status =BPF_STACK_BUILD_ID_IP ; - id_offs[i].ip = ips[i]; -memset (id_offs[i].build_id,0 ,BUILD_ID_SIZE_MAX ); - } -return ; +if (!user || !current || !current->mm) + goto fallback; + + work =bpf_mmap_unlock_guard_get (); +if (IS_ERR (work)) + goto fallback; + +if (!mmap_read_trylock (current->mm)) { +bpf_mmap_unlock_guard_put (work); + goto fallback; }for (i =0 ; i < trace_nr; i++) {
@@ -174,6 +170,15 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs, prev_build_id = id_offs[i].build_id; }bpf_mmap_unlock_mm (work, current->mm); +return ; + +fallback: +/* cannot access current->mm, fall back to ips */ +for (i =0 ; i < trace_nr; i++) { + id_offs[i].status =BPF_STACK_BUILD_ID_IP ; + id_offs[i].ip = ips[i]; +memset (id_offs[i].build_id,0 ,BUILD_ID_SIZE_MAX ); + } } static struct perf_callchain_entry *
kernel/bpf/task_iter.c +10 -4
@@ -780,9 +780,8 @@ static struct bpf_iter_reg task_vma_reg_info = { BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, bpf_callback_t, callback_fn, void *, callback_ctx, u64, flags) { -struct mmap_unlock_irq_work *work =NULL ; +struct mmap_unlock_irq_work *work;struct vm_area_struct *vma; -bool irq_work_busy = false;bool __maybe_unused mmput_needed = false;struct mm_struct *mm;int ret = -ENOENT ;
@@ -819,9 +818,14 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start,if (!mm)return -ENOENT ; - irq_work_busy =bpf_mmap_unlock_get_irq_work (&work); + work =bpf_mmap_unlock_guard_get (); +if (IS_ERR (work)) { + ret =PTR_ERR (work); + goto out; + } -if (irq_work_busy || !mmap_read_trylock (mm)) { +if (!mmap_read_trylock (mm)) { +bpf_mmap_unlock_guard_put (work); ret = -EBUSY ; goto out; }
@@ -864,6 +868,8 @@ static void do_mmap_read_unlock(struct irq_work *entry) work = container_of(entry, struct mmap_unlock_irq_work, irq_work);mmap_read_unlock_non_owner (work->mm); + work->mm =NULL ; +bpf_mmap_unlock_guard_put (work); }static int __init task_iter_init (void )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1566/1887 bpf, cgroup: Fix storage null-ptr-deref after replacing prog
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pu Lehui <[email protected]>
[ Upstream commit 3f562c537e9ecf4bc5e206cfffc2cc047f1b7e94 ]
Syzkaller reported a storage null-ptr-deref issue after replacing prog. This occurs in the following scenario:
1. prog A, an empty prog, is attached to a cgrp.
2. prog B uses BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE and calls the bpf_get_local_storage helper.
3. link_update is called to replace prog A with prog B.
The reason is that __cgroup_bpf_replace fails to alloc and assign the required cgrp storage for the incoming replacement prog. Consequently, the new prog inherits an uninit storage, leading to null-ptr-deref panic when kick the new prog.
Fix this by rejecting a link update if new_prog's cgroup storage is incompatible with link->prog.
--- kernel/bpf/cgroup.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
kernel/bpf/cgroup.c +17 -0
@@ -797,6 +797,20 @@ static void replace_effective_prog(struct cgroup *cgrp, } } +static bool cgroup_bpf_storages_compatible (struct bpf_prog *old_prog, +struct bpf_prog *new_prog) +{ +enum bpf_cgroup_storage_type stype; + +for_each_cgroup_storage_type (stype) { +if (old_prog->aux->cgroup_storage[stype] != + new_prog->aux->cgroup_storage[stype]) +return false; + } + +return true; +} +/** * __cgroup_bpf_replace() - Replace link's program and propagate the change * to descendants
@@ -834,6 +848,9 @@ static int __cgroup_bpf_replace(struct cgroup *cgrp,if (!found)return -ENOENT ; +if (!cgroup_bpf_storages_compatible (link->link.prog, new_prog)) +return -EINVAL ; + old_prog =xchg (&link->link.prog, new_prog);replace_effective_prog (cgrp, atype, link);bpf_prog_put (old_prog);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1567/1887 btrfs: qgroup: fix a wrong length calculation in qgroup_free_reserved_data()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qu Wenruo <[email protected]>
[ Upstream commit 9102b179512e11644fb0489ae62010a09afa199c ]
In that function, we round down the start position and round up the ending position.
But during the calculation of @len, we use "round_up(start + len, sectorsize)", which is the rounded up end position, not the rounded up length.
Which results a much larger length, and later we are still using "start + len", which is completely incorrect.
Fix it by declaring a local @aligned_start and @aligned_len and use them instead.
--- fs/btrfs/qgroup.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
fs/btrfs/qgroup.c +11 -7
@@ -3878,12 +3878,13 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode,struct ulist_node *unode;struct ulist_iterator uiter;struct extent_changeset changeset; +const u32 sectorsize = root->fs_info->sectorsize; +const u64 aligned_start =round_down (start, sectorsize); +const u64 aligned_len =round_up (start + len, sectorsize) - aligned_start;u64 freed =0 ;int ret;extent_changeset_init (&changeset); - len =round_up (start + len, root->fs_info->sectorsize); - start =round_down (start, root->fs_info->sectorsize);ULIST_ITER_INIT (&uiter);while ((unode =ulist_next (&reserved->range_changed, &uiter))) {
@@ -3895,12 +3896,15 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode,extent_changeset_release (&changeset); -/* Only free range in range [start, start + len) */ -if (range_start >= start + len || - range_start + range_len <= start) +/* +* Only free the range within +* [aligned_start, aligned_start + aligned_len). +*/ +if (range_start >= aligned_start + aligned_len || + range_start + range_len <= aligned_start)continue ; - free_start =max (range_start, start); - free_len =min (start + len, range_start + range_len) - + free_start =max (range_start, aligned_start); + free_len =min (aligned_start + aligned_len, range_start + range_len) - free_start;/* * TODO: To also modify reserved->ranges_reserved to reflect
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1568/1887 btrfs: check if root is readonly when setting posix acl
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sun YangKai <[email protected]>
[ Upstream commit 01c2f41fc441f75b3f9326443372faceb1cb6638 ]
For a filesystem which has btrfs read-only property set to true, all write operations including acl and xattr should be denied. However, acl can still be set even if btrfs ro property is true.
This happens because no function on the set_acl code path checks the root is readonly or not. It was checked in btrfs_setxattr_trans() but got removed in commit 353c2ea735e4 ("btrfs: remove redundant readonly root check in btrfs_setxattr_trans")
That commit didn't check if all the callers properly check the root's read-only flag. A previous fix is commit b51111271b03 ("btrfs: check if root is readonly while setting security xattr").
Always check if the root is read-only before performing the set acl operation.
--- fs/btrfs/acl.c | 3 +++ 1 file changed, 3 insertions(+)
fs/btrfs/acl.c +3 -0
@@ -116,6 +116,9 @@ int btrfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,int ret;umode_t old_mode = inode->i_mode; +if (btrfs_root_readonly (BTRFS_I (inode)->root)) +return -EROFS ; +if (type ==ACL_TYPE_ACCESS && acl) { ret =posix_acl_update_mode (mnt_userns, inode, &inode->i_mode, &acl);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1569/1887 btrfs: always wait for ordered extents to avoid OE races
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qu Wenruo <[email protected]>
[ Upstream commit ec78575dde998c21be7e0cb2503b5620f34b6255 ]
[BUG]
Syzbot reported a bug that there can be conflicting OEs for the same range:
BTRFS critical (device loop4): panic in insert_ordered_extent:264: overlapping ordered extents, existing oe file_offset 16384 num_bytes 430080 flags 0x1089, new oe file_offset 16384 num_bytes 430080 flags 0x80 (errno=-17 Object alrea[ 179.162726][ T6897] BTRFS critical (device loop4): panic in insert_ordered_extent:264: overlapping ordered extents, existing oe file_offset 16384 num_bytes 430080 flags 0x1089, new oe file_offset 16384 num_bytes 430080 flags 0x80 (errno=-17 Object already exists)
------------[ cut here ]------------ kernel BUG at fs/btrfs/ordered-data.c:264! Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026 RIP: 0010:btrfs_alloc_ordered_extent+0x943/0xad0 Call Trace: <TASK> cow_file_range+0x744/0x12a0 fallback_to_cow+0x5ea/0xa00 run_delalloc_nocow+0x110c/0x17a0 btrfs_run_delalloc_range+0xbe4/0x1c20 writepage_delalloc+0x104d/0x1ba0 btrfs_writepages+0x1667/0x28b0 do_writepages+0x338/0x560 filemap_fdatawrite_range+0x1f2/0x300 btrfs_fdatawrite_range+0x54/0xf0 btrfs_direct_write+0x6a0/0xc30 btrfs_do_write_iter+0x329/0x790 do_iter_readv_writev+0x624/0x8d0 vfs_writev+0x34c/0x990 __se_sys_pwritev2+0x17a/0x2a0 do_syscall_64+0x174/0x580 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> ---[ end trace 0000000000000000 ]---
[CAUSE]
Since commit ff66fe666233 ("btrfs: fix incorrect buffered IO fallback for append direct writes"), if the direct IO finished short, we will revert the isize back to the original one, so that append writes can be respected during the buffered fallback.
Normally we rely on lock_and_cleanup_extent_if_need() function during buffered writeback to wait for any existing ordered extents.
But that ordered extent waiting only happens if the start_pos is inside the isize.
Since we have reverted the isize during failed direct IO, we will not wait for any ordered extents.
This means we can have a race where the direct IO OE is still in the tree, finished but not yet removed, then we're inserting the OE for the buffered write, causing the above crash.
[FIX]
Make the OE wait to be unconditional, to handle the reverted isize situation.
And since lock_and_cleanup_extent_if_need() now either lock the extents or return -EAGAIN, also remove the branches that handles no-extent-locked cases, and rename it to remove the "_if_need" suffix.
The following micro benchmark shows the runtime difference for btrfs_buffered_write(), doing xfs_io -f -c "pwrite 0 1m" workload, all values are the average runtime in nano seconds.
function runtime | before | after
-----------------------------------+-------------+---------------
lock_and_cleanup_extent_if_need() | 58.2 | 183.0 btrfs_buffered_write() | 2115.6 | 2973.3
The overall runtime of btrfs_buffered_write() is still pretty tiny (still less than 3 micro seconds), I'd say the extra cost is still acceptable.
An alternative to fix this problem is to wait ordered extents during iomap_end() where the isize revert is done.
But that solution will break nowait requirement, as if a nowait direct IO finished short, we have to wait for the OEs unconditionally or the next append buffered IO can still hit the same problem.
So here we have to move the wait cost to buffered write, but at least the code is slightly more streamline.
--- fs/btrfs/file.c | 103 ++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 61 deletions(-)
fs/btrfs/file.c +42 -61
@@ -949,69 +949,63 @@ static noinline int prepare_pages(struct inode *inode, struct page **pages,/* * This function locks the extent and properly waits for data=ordered extents -* to finish before allowing the pages to be modified if need. +* to finish before allowing the pages to be modified. * * The return value: -* 1 - the extent is locked -* 0 - the extent is not locked, and everything is OK +* 0 - the extent is locked * -EAGAIN - need re-prepare the pages * the other < 0 number - Something wrong happens */ static noinline int -lock_and_cleanup_extent_if_need (struct btrfs_inode *inode,struct page **pages, -size_t num_pages,loff_t pos, -size_t write_bytes, -u64 *lockstart,u64 *lockend,bool nowait, -struct extent_state **cached_state) +lock_and_cleanup_extent (struct btrfs_inode *inode,struct page **pages, +size_t num_pages,loff_t pos, +size_t write_bytes, +u64 *lockstart,u64 *lockend,bool nowait, +struct extent_state **cached_state) {struct btrfs_fs_info *fs_info = inode->root->fs_info; +struct btrfs_ordered_extent *ordered;u64 start_pos;u64 last_pos;int i; -int ret =0 ; start_pos =round_down (pos, fs_info->sectorsize); last_pos =round_up (pos + write_bytes, fs_info->sectorsize) -1 ; -if (start_pos < inode->vfs_inode.i_size) { -struct btrfs_ordered_extent *ordered; - -if (nowait) { -if (!try_lock_extent (&inode->io_tree, start_pos, last_pos)) { -for (i =0 ; i < num_pages; i++) { -unlock_page (pages[i]); -put_page (pages[i]); - pages[i] =NULL ; - } - -return -EAGAIN ; - } - }else { -lock_extent (&inode->io_tree, start_pos, last_pos, cached_state); - } - - ordered =btrfs_lookup_ordered_range (inode, start_pos, - last_pos - start_pos +1 ); -if (ordered && - ordered->file_offset + ordered->num_bytes > start_pos && - ordered->file_offset <= last_pos) { -unlock_extent (&inode->io_tree, start_pos, last_pos, - cached_state); +if (nowait) { +if (!try_lock_extent (&inode->io_tree, start_pos, last_pos)) {for (i =0 ; i < num_pages; i++) {unlock_page (pages[i]);put_page (pages[i]); + pages[i] =NULL ; } -btrfs_start_ordered_extent (ordered,1 ); -btrfs_put_ordered_extent (ordered); +return -EAGAIN ; } -if (ordered) -btrfs_put_ordered_extent (ordered); + }else { +lock_extent (&inode->io_tree, start_pos, last_pos, cached_state); + } - *lockstart = start_pos; - *lockend = last_pos; - ret =1 ; + ordered =btrfs_lookup_ordered_range (inode, start_pos, + last_pos - start_pos +1 ); +if (ordered && + ordered->file_offset + ordered->num_bytes > start_pos && + ordered->file_offset <= last_pos) { +unlock_extent (&inode->io_tree, start_pos, last_pos, + cached_state); +for (i =0 ; i < num_pages; i++) { +unlock_page (pages[i]); +put_page (pages[i]); + } +btrfs_start_ordered_extent (ordered,1 ); +btrfs_put_ordered_extent (ordered); +return -EAGAIN ; } +if (ordered) +btrfs_put_ordered_extent (ordered); + + *lockstart = start_pos; + *lockend = last_pos;/* * We should be called after prepare_pages() which should have locked
@@ -1020,7 +1014,7 @@ lock_and_cleanup_extent_if_need(struct btrfs_inode *inode, struct page **pages,for (i =0 ; i < num_pages; i++)WARN_ON (!PageLocked (pages[i])); -return ret; +return 0 ; }/*
@@ -1214,7 +1208,6 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,size_t copied;size_t dirty_sectors;size_t num_sectors; -int extents_locked;/* * Fault pages before locking them in prepare_pages
@@ -1298,17 +1291,15 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,break ; } - extents_locked =lock_and_cleanup_extent_if_need ( -BTRFS_I (inode), pages, - num_pages, pos, write_bytes, &lockstart, - &lockend, nowait, &cached_state); -if (extents_locked <0 ) { -if (!nowait && extents_locked == -EAGAIN ) + ret =lock_and_cleanup_extent (BTRFS_I (inode), pages, + num_pages, pos, write_bytes, &lockstart, + &lockend, nowait, &cached_state); +if (ret <0 ) { +if (!nowait && ret == -EAGAIN ) goto again;btrfs_delalloc_release_extents (BTRFS_I (inode), reserve_bytes); - ret = extents_locked;break ; }
@@ -1361,18 +1352,8 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb, dirty_pages, pos, copied, &cached_state, only_release_metadata); -/* -* If we have not locked the extent range, because the range's -* start offset is >= i_size, we might still have a non-NULL -* cached extent state, acquired while marking the extent range -* as delalloc through btrfs_dirty_pages(). Therefore free any -* possible cached extent state to avoid a memory leak. -*/ -if (extents_locked) -unlock_extent (&BTRFS_I (inode)->io_tree, lockstart, - lockend, &cached_state); -else -free_extent_state (cached_state); +unlock_extent (&BTRFS_I (inode)->io_tree, lockstart, lockend, + &cached_state);btrfs_delalloc_release_extents (BTRFS_I (inode), reserve_bytes);if (ret) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1570/1887 btrfs: defrag: fix deadlock between defrag and delalloc space reservation
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit ba02eab28041f9a4bbe9fc90c7249644fef6de0f ]
While running fsstress with autodefrag and flushoncommit, hit a deadlock due to the fact that defrag reserves delalloc space while it's holding dirty and locked folios, besides the extent range lock. The stack traces are the following:
[958.624] task:kworker/u50:3 state:D stack:0 pid:20365 tgid:20365 ppid:2 task_flags:0x4208060 flags:0x00080000 [958.626] Workqueue: events_unbound btrfs_async_reclaim_metadata_space [btrfs] [958.627] Call Trace: [958.628] <TASK> [958.628] __schedule+0x4be/0x10f0 [958.629] ? preempt_count_add+0x69/0xa0 [958.630] schedule+0x26/0xd0 [958.631] wait_current_trans+0x102/0x160 [btrfs] [958.632] ? __pfx_autoremove_wake_function+0x10/0x10 [958.633] start_transaction+0x374/0x900 [btrfs] [958.634] btrfs_commit_current_transaction+0x1d/0x70 [btrfs] [958.635] flush_space+0xca/0x5e0 [btrfs] [958.636] ? _raw_spin_unlock+0x15/0x30 [958.637] ? btrfs_reduce_alloc_profile+0x8c/0x190 [btrfs] [958.639] ? _raw_spin_unlock+0x15/0x30 [958.640] ? calc_available_free_space.isra.0+0x6f/0x110 [btrfs] [958.641] do_async_reclaim_metadata_space+0x84/0x190 [btrfs] [958.642] btrfs_async_reclaim_metadata_space+0x64/0x80 [btrfs] [958.644] process_one_work+0x19d/0x3a0 [958.644] worker_thread+0x1c4/0x330 [958.645] ? __pfx_worker_thread+0x10/0x10 [958.646] kthread+0xfc/0x130 [958.647] ? __pfx_kthread+0x10/0x10 [958.648] ret_from_fork+0x1f7/0x2c0 [958.648] ? __pfx_kthread+0x10/0x10 [958.649] ret_from_fork_asm+0x1a/0x30 [958.650] </TASK> [958.651] task:kworker/u49:7 state:D stack:0 pid:52990 tgid:52990 ppid:2 task_flags:0x4208060 flags:0x00080000 [958.653] Workqueue: writeback wb_workfn (flush-btrfs-334) [958.655] Call Trace: [958.655] <TASK> [958.656] __schedule+0x4be/0x10f0 [958.657] ? __blk_flush_plug+0xe9/0x140 [958.658] schedule+0x26/0xd0 [958.658] io_schedule+0x42/0x70 [958.659] folio_wait_bit_common+0x12b/0x330 [958.660] ? folio_wait_bit_common+0x100/0x330 [958.662] ? __pfx_wake_page_function+0x10/0x10 [958.663] extent_write_cache_pages+0x599/0x830 [btrfs] [958.664] ? acpi_fwnode_get_reference_args+0x1fa/0x270 [958.665] btrfs_writepages+0x77/0x130 [btrfs] [958.666] ? __pfx_end_bbio_data_write+0x10/0x10 [btrfs] [958.667] do_writepages+0xc6/0x160 [958.668] __writeback_single_inode+0x42/0x310 [958.669] writeback_sb_inodes+0x231/0x570 [958.670] wb_writeback+0x8a/0x340 [958.671] wb_workfn+0xbf/0x450 [958.672] ? finish_task_switch.isra.0+0xc1/0x350 [958.673] process_one_work+0x19d/0x3a0 [958.673] worker_thread+0x1c4/0x330 [958.674] ? __pfx_worker_thread+0x10/0x10 [958.675] kthread+0xfc/0x130 [958.676] ? __pfx_kthread+0x10/0x10 [958.676] ret_from_fork+0x1f7/0x2c0 [958.677] ? __pfx_kthread+0x10/0x10 [958.678] ret_from_fork_asm+0x1a/0x30 [958.679] </TASK> [958.679] task:btrfs-cleaner state:D stack:0 pid:296750 tgid:296750 ppid:2 task_flags:0x208040 flags:0x00080000 [958.681] Call Trace: [958.682] <TASK> [958.682] __schedule+0x4be/0x10f0 [958.683] schedule+0x26/0xd0 [958.684] handle_reserve_ticket+0x1b9/0x2c0 [btrfs] [958.685] ? __pfx_autoremove_wake_function+0x10/0x10 [958.686] reserve_bytes+0x283/0x4c0 [btrfs] [958.687] btrfs_reserve_metadata_bytes+0x18/0xb0 [btrfs] [958.688] btrfs_delalloc_reserve_metadata+0x121/0x320 [btrfs] [958.690] btrfs_delalloc_reserve_space+0x46/0xb0 [btrfs] [958.691] btrfs_defrag_file+0x903/0x1110 [btrfs] [958.692] btrfs_run_defrag_inodes+0x334/0x430 [btrfs] [958.694] cleaner_kthread+0x97/0x1c0 [btrfs] [958.694] ? __pfx_cleaner_kthread+0x10/0x10 [btrfs] [958.696] kthread+0xfc/0x130 [958.696] ? __pfx_kthread+0x10/0x10 [958.697] ret_from_fork+0x1f7/0x2c0 [958.698] ? __pfx_kthread+0x10/0x10 [958.699] ret_from_fork_asm+0x1a/0x30 [958.700] </TASK> [958.716] task:fsstress state:D stack:0 pid:296769 tgid:296769 ppid:296768 task_flags:0x400140 flags:0x00080000 [958.718] Call Trace: [958.719] <TASK> [958.719] __schedule+0x4be/0x10f0 [958.720] ? preempt_count_add+0x69/0xa0 [958.721] schedule+0x26/0xd0 [958.722] wb_wait_for_completion+0x79/0xc0 [958.723] ? __pfx_autoremove_wake_function+0x10/0x10 [958.724] __writeback_inodes_sb_nr+0xc5/0xf0 [958.725] try_to_writeback_inodes_sb+0x55/0x70 [958.726] btrfs_commit_transaction+0x19d/0xeb0 [btrfs] [958.727] ? start_transaction+0x343/0x900 [btrfs] [958.728] btrfs_mksubvol+0x28b/0x4e0 [btrfs] [958.729] btrfs_mksnapshot+0x74/0xa0 [btrfs] [958.730] __btrfs_ioctl_snap_create+0x194/0x210 [btrfs] [958.732] btrfs_ioctl_snap_create_v2+0xef/0x150 [btrfs] [958.733] btrfs_ioctl+0x7ec/0x2a70 [btrfs] [958.734] ? __virt_addr_valid+0xe4/0x180 [958.735] ? __check_object_size+0x1cd/0x1f0 [958.736] ? kmem_cache_free+0x146/0x380 [958.737] ? _raw_spin_unlock+0x15/0x30 [958.738] ? do_sys_openat2+0x83/0xd0 [958.739] __x64_sys_ioctl+0x92/0xe0 [958.740] do_syscall_64+0x60/0x590 [958.741] ? clear_bhb_loop+0x60/0xb0 [958.742] entry_SYSCALL_64_after_hwframe+0x76/0x7e [958.743] RIP: 0033:0x7f4431e108db [958.744] RSP: 002b:00007ffcd147db20 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 [958.746] RAX: ffffffffffffffda RBX: 0000000000000004 RCX: 00007f4431e108db [958.747] RDX: 00007ffcd147eb90 RSI: 0000000050009417 RDI: 0000000000000005 [958.749] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 [958.751] R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffcd147fbf0 [958.752] R13: 00007ffcd147eb90 R14: 0000000000000005 R15: 0000000000000003 [958.754] </TASK>
What happens is the following:
1) The cleaner kthread is running autodefrag, and in defrag_one_range() it acquired all the folios for the range and locked them.
Then it locked the extent range in the inode's iotree.
It got two subranges from defrag_collect_targets(), the first one with folio A and the second one with folio B.
After it defragged the first subrange, folio A remains locked and dirty - it's only unlocked when defrag_one_range() returns.
When it attempts to defrag the second subrange (containing folio B), btrfs_delalloc_reserve_space() creates a space reservation ticket, due to lack of free metadata space and blocks waiting for the async metadata reclaim task to free space and wake it up;
2) The async reclaim metadata task attempts to commit the current transaction, but it blocks because there is another task that started the commit first;
3) A task creating a snapshot is committing the transaction and because the fs was mounted with flushoncommit, it calls try_to_writeback_inodes_sb(), which spawns a task to flush delalloc and waits for it to complete;
4) The task flushing delalloc (kworker/u49:7), finds that folio A for the inode being defragged is dirty, so it tries to lock it...
But it blocks because folio A is locked by the defrag task (the cleaner kthread) which is blocked waiting for the reservation ticket to be served, but the async reclaim metadata task is blocked waiting for the transaction commit, which in turn is blocked waiting for the delalloc flush task, which is trying to lock folio A, resulting in a deadlock.
The same type of problem can happen if the async reclaim task starts to flush delalloc, as that requires both locking the folio and the extent range in the inode's io tree, and in this case we don't need the fs to be mounted with flushoncommit. This type of problem has ocurred several times in the past with reflinks for example, where we had a dirty folio while holding the extent range locked and then starting a transaction blocked waiting for the async reclaim task due to lack of free metadata space.
So fix this by reserving delalloc space before locking folios and locking the extent range in the inode's iotree. We can not simply unlock the folios for each subrange given by defrag_collect_targets() after we defrag it because the same folio may be present too in the next subrange (due to large folios).
--- fs/btrfs/ioctl.c | 50 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 17 deletions(-)
fs/btrfs/ioctl.c +33 -17
@@ -1630,26 +1630,21 @@ static_assert(IS_ALIGNED(CLUSTER_SIZE, PAGE_SIZE));* * - Extent bits are locked */ -static int defrag_one_locked_target (struct btrfs_inode *inode, -struct defrag_target_range *target, -struct page **pages,int nr_pages, -struct extent_state **cached_state) +static void defrag_one_locked_target (struct btrfs_inode *inode, +struct defrag_target_range *target, +struct page **pages,int nr_pages, +struct extent_state **cached_state) {struct btrfs_fs_info *fs_info = inode->root->fs_info; -struct extent_changeset *data_reserved =NULL ;const u64 start = target->start;const u64 len = target->len;unsigned long last_index = (start + len -1 ) >>PAGE_SHIFT ;unsigned long start_index = start >>PAGE_SHIFT ;unsigned long first_index =page_index (pages[0 ]); -int ret =0 ;int i;ASSERT (last_index - first_index +1 <= nr_pages); - ret =btrfs_delalloc_reserve_space (inode, &data_reserved, start, len); -if (ret <0 ) -return ret;clear_extent_bit (&inode->io_tree, start, start + len -1 ,EXTENT_DELALLOC |EXTENT_DO_ACCOUNTING |EXTENT_DEFRAG , cached_state);
@@ -1660,10 +1655,6 @@ static int defrag_one_locked_target(struct btrfs_inode *inode,ClearPageChecked (pages[i]);btrfs_page_clamp_set_dirty (fs_info, pages[i], start, len); } -btrfs_delalloc_release_extents (inode, len); -extent_changeset_free (data_reserved); - -return ret; } static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
@@ -1679,6 +1670,8 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,u64 last_index = (start + len -1 ) >>PAGE_SHIFT ;u64 start_index = start >>PAGE_SHIFT ;unsigned int nr_pages = last_index - start_index +1 ; +struct extent_changeset *data_reserved =NULL ; +u64 last_defrag_end = start;int ret =0 ;int i;
@@ -1689,6 +1682,22 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,if (!pages)return -ENOMEM ; +/* +* Reserve delalloc space before locking the range and before locking +* and dirtying any pages - otherwise we could deadlock, for example +* after defrag of one range we dirty pages and keep them locked when +* we move to the next range, so reserving delalloc space right before +* each range could trigger flushing of delalloc and deadlock on the +* extent lock or trigger a transaction commit with flushoncommit, which +* can either deadlock on the lock of a page made dirty in the previous +* range or the extent lock. +*/ + ret =btrfs_delalloc_reserve_space (inode, &data_reserved, start, len); +if (ret <0 ) { +kfree (pages); +return ret; + } +/* Prepare all pages */ for (i =0 ; i < nr_pages; i++) { pages[i] =defrag_prepare_one_page (inode, start_index + i);
@@ -1719,10 +1728,11 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, goto unlock_extent;list_for_each_entry (entry, &target_list, list) { - ret =defrag_one_locked_target (inode, entry, pages, nr_pages, - &cached_state); -if (ret <0 ) -break ; +defrag_one_locked_target (inode, entry, pages, nr_pages, &cached_state); +if (entry->start > last_defrag_end) +btrfs_delalloc_release_space (inode, data_reserved, last_defrag_end, + entry->start - last_defrag_end, true); + last_defrag_end = entry->start + entry->len; }list_for_each_entry_safe (entry, tmp, &target_list, list) {
@@ -1741,6 +1751,12 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, } }kfree (pages); +btrfs_delalloc_release_extents (inode, len); +if (last_defrag_end < start + len) +btrfs_delalloc_release_space (inode, data_reserved, last_defrag_end, + start + len - last_defrag_end, true); +extent_changeset_free (data_reserved); +return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1571/1887 scsi: sd: Fix special_vec mempool leak when scsi_alloc_sgtables() fails
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Xiuwei <[email protected]>
[ Upstream commit bb31844d88b77138b67aa20c3600203baff40140 ]
sd_set_special_bvec() allocates a special payload page for UNMAP and WRITE SAME commands. If scsi_alloc_sgtables() fails afterward in sd_setup_unmap_cmnd() or sd_setup_write_same{10,16}_cmnd(), the SCSI midlayer does not call uninit_command() because RQF_DONTPREP is not set yet, leaking the page.
Call sd_uninit_command() on error, and clear RQF_SPECIAL_PAYLOAD after freeing the page.
--- drivers/scsi/sd.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)
drivers/scsi/sd.c +20 -5
@@ -920,6 +920,7 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd)u32 nr_blocks =sectors_to_logical (sdp,blk_rq_sectors (rq));unsigned int data_len =24 ;char *buf; +blk_status_t ret; rq->special_vec.bv_page =mempool_alloc (sd_page_pool,GFP_ATOMIC );if (!rq->special_vec.bv_page)
@@ -943,7 +944,10 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd) cmd->transfersize = data_len; rq->timeout =SD_TIMEOUT ; -return scsi_alloc_sgtables (cmd); + ret =scsi_alloc_sgtables (cmd); +if (ret !=BLK_STS_OK ) +sd_uninit_command (cmd); +return ret; } static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd,
@@ -955,6 +959,7 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd,u64 lba =sectors_to_logical (sdp,blk_rq_pos (rq));u32 nr_blocks =sectors_to_logical (sdp,blk_rq_sectors (rq));u32 data_len = sdp->sector_size; +blk_status_t ret; rq->special_vec.bv_page =mempool_alloc (sd_page_pool,GFP_ATOMIC );if (!rq->special_vec.bv_page)
@@ -975,7 +980,10 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd, cmd->transfersize = data_len; rq->timeout = unmap ?SD_TIMEOUT :SD_WRITE_SAME_TIMEOUT ; -return scsi_alloc_sgtables (cmd); + ret =scsi_alloc_sgtables (cmd); +if (ret !=BLK_STS_OK ) +sd_uninit_command (cmd); +return ret; } static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd,
@@ -987,6 +995,7 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd,u64 lba =sectors_to_logical (sdp,blk_rq_pos (rq));u32 nr_blocks =sectors_to_logical (sdp,blk_rq_sectors (rq));u32 data_len = sdp->sector_size; +blk_status_t ret; rq->special_vec.bv_page =mempool_alloc (sd_page_pool,GFP_ATOMIC );if (!rq->special_vec.bv_page)
@@ -1007,7 +1016,10 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd, cmd->transfersize = data_len; rq->timeout = unmap ?SD_TIMEOUT :SD_WRITE_SAME_TIMEOUT ; -return scsi_alloc_sgtables (cmd); + ret =scsi_alloc_sgtables (cmd); +if (ret !=BLK_STS_OK ) +sd_uninit_command (cmd); +return ret; }static blk_status_t sd_setup_write_zeroes_cmnd (struct scsi_cmnd *cmd)
@@ -1352,8 +1364,11 @@ static void sd_uninit_command(struct scsi_cmnd *SCpnt) {struct request *rq =scsi_cmd_to_rq (SCpnt); -if (rq->rq_flags &RQF_SPECIAL_PAYLOAD ) -mempool_free (rq->special_vec.bv_page, sd_page_pool); +if (!(rq->rq_flags &RQF_SPECIAL_PAYLOAD )) +return ; + +mempool_free (rq->special_vec.bv_page, sd_page_pool); + rq->rq_flags &= ~RQF_SPECIAL_PAYLOAD ; } static bool sd_need_revalidate(struct block_device *bdev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1572/1887 coresight: etm4x: missing cscfg_csdev_disable_active_config() in perf enable
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yeoreum Yun <[email protected]>
[ Upstream commit 0a47f0be6557b4a851addd430383a4dc7ee08752 ]
In the perf enable path, there are missing cases where cscfg_csdev_disable_active_config() is not called:
- Branch broadcast is selected but not supported by the hardware
- etm4_enable_hw() fails
This can lead to a leak of config_desc->active_cnt. Fix this by properly calling cscfg_csdev_disable_active_config() in these error paths.
--- .../coresight/coresight-etm4x-core.c | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-)
drivers/hwtracing/coresight/coresight-etm4x-core.c +22 -16
@@ -669,8 +669,6 @@ static int etm4_parse_event_config(struct coresight_device *csdev,struct etmv4_drvdata *drvdata =dev_get_drvdata (csdev->dev.parent);struct etmv4_config *config = &drvdata->config;struct perf_event_attr *attr = &event->attr; -unsigned long cfg_hash; -int preset;/* Clear configuration from previous run */ memset (config,0 ,sizeof (struct etmv4_config ));
@@ -740,20 +738,6 @@ static int etm4_parse_event_config(struct coresight_device *csdev,/* bit[12], Return stack enable bit */ config->cfg |=TRCCONFIGR_RS ; -/* -* Set any selected configuration and preset. -* -* This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset) -* in the perf attributes defined in coresight-etm-perf.c. -* configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config. -* A zero configid means no configuration active, preset = 0 means no preset selected. -*/ -if (attr->config2 &GENMASK_ULL (63 ,32 )) { - cfg_hash = (u32)(attr->config2 >>32 ); - preset = attr->config &0xF ; - ret =cscfg_csdev_enable_active_config (csdev, cfg_hash, preset); - } -/* branch broadcast - enable if selected and supported */ if (attr->config &BIT (ETM_OPT_BRANCH_BROADCAST )) {if (!drvdata->trcbb) {
@@ -777,6 +761,9 @@ static int etm4_enable_perf(struct coresight_device *csdev, {int ret =0 ;struct etmv4_drvdata *drvdata =dev_get_drvdata (csdev->dev.parent); +struct perf_event_attr *attr = &event->attr; +unsigned long cfg_hash =0 ; +int preset;if (WARN_ON_ONCE (drvdata->cpu !=smp_processor_id ())) { ret = -EINVAL ;
@@ -787,8 +774,27 @@ static int etm4_enable_perf(struct coresight_device *csdev, ret =etm4_parse_event_config (csdev, event);if (ret) goto out; + +/* +* Set any selected configuration and preset. +* +* This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset) +* in the perf attributes defined in coresight-etm-perf.c. +* configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config. +* A zero configid means no configuration active, preset = 0 means no preset selected. +*/ +if (attr->config2 &GENMASK_ULL (63 ,32 )) { + cfg_hash = (u32)(attr->config2 >>32 ); + preset = attr->config &0xF ; + ret =cscfg_csdev_enable_active_config (csdev, cfg_hash, preset); +if (ret) + goto out; + } +/* And enable it */ ret =etm4_enable_hw (drvdata); +if (ret && cfg_hash) +cscfg_csdev_disable_active_config (csdev); out:return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1573/1887 drm/amd/display: Resize MST HDCP per-connector arrays to 32
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harry Wentland <[email protected]>
[ Upstream commit 261e0fe4e2c99f687114b64b10b98db964b475f4 ]
AMDGPU_DM_MAX_DISPLAY_INDEX is 31. It suggest a maximum number of 32 connectors. But the way it's used is like MAX_DISPLAY_COUNT. Hence we're off by one with DRM core, which supports a max of 32 connectors.
Rename AMDGPU_DM_MAX_DISPLAY_INDEX to AMDGPU_DM_MAX_DISPLAY_COUNT to match its actual use, and increase the size to 32 to match the originally intended size.
--- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 2 +- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 10 +++++----- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +1 -1
@@ -43,7 +43,7 @@* in amdgpu_dm_kms.h file */ -#define AMDGPU_DM_MAX_DISPLAY_INDEX 31 +#define AMDGPU_DM_MAX_DISPLAY_COUNT 32#define AMDGPU_DM_MAX_CRTC 6
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +5 -5
@@ -256,7 +256,7 @@ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_indecancel_delayed_work (&hdcp_w->property_validate_dwork); -for (conn_index =0 ; conn_index <AMDGPU_DM_MAX_DISPLAY_INDEX ; conn_index++) { +for (conn_index =0 ; conn_index <AMDGPU_DM_MAX_DISPLAY_COUNT ; conn_index++) { hdcp_w->encryption_status[conn_index] =MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF ;if (hdcp_w->aconnector[conn_index]) {
@@ -303,7 +303,7 @@ static void event_property_update(struct work_struct *work)struct drm_connector *connector;struct drm_connector_state *conn_state; -for (conn_index =0 ; conn_index <AMDGPU_DM_MAX_DISPLAY_INDEX ; conn_index++) { +for (conn_index =0 ; conn_index <AMDGPU_DM_MAX_DISPLAY_COUNT ; conn_index++) { aconnector = hdcp_work->aconnector[conn_index];if (!aconnector)
@@ -375,7 +375,7 @@ static void event_property_validate(struct work_struct *work)guard (mutex)(&hdcp_work->mutex); -for (conn_index =0 ; conn_index <AMDGPU_DM_MAX_DISPLAY_INDEX ; +for (conn_index =0 ; conn_index <AMDGPU_DM_MAX_DISPLAY_COUNT ; conn_index++) { aconnector = hdcp_work->aconnector[conn_index];
@@ -753,10 +753,10 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev,memset (hdcp_work[i].aconnector,0 ,sizeof (struct amdgpu_dm_connector *) * -AMDGPU_DM_MAX_DISPLAY_INDEX ); +AMDGPU_DM_MAX_DISPLAY_COUNT );memset (hdcp_work[i].encryption_status,0 ,sizeof (enum mod_hdcp_encryption_status ) * -AMDGPU_DM_MAX_DISPLAY_INDEX ); +AMDGPU_DM_MAX_DISPLAY_COUNT ); } cp_psp->funcs.update_stream_config = update_config;
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h +4 -4
@@ -43,7 +43,7 @@ struct hdcp_workqueue {struct delayed_work callback_dwork;struct delayed_work watchdog_timer_dwork;struct delayed_work property_validate_dwork; -struct amdgpu_dm_connector *aconnector[AMDGPU_DM_MAX_DISPLAY_INDEX ]; +struct amdgpu_dm_connector *aconnector[AMDGPU_DM_MAX_DISPLAY_COUNT ];struct mutex mutex;struct mod_hdcp hdcp;
@@ -51,7 +51,7 @@ struct hdcp_workqueue {struct mod_hdcp_display display;struct mod_hdcp_link link; -enum mod_hdcp_encryption_status encryption_status[AMDGPU_DM_MAX_DISPLAY_INDEX ]; +enum mod_hdcp_encryption_status encryption_status[AMDGPU_DM_MAX_DISPLAY_COUNT ];/* when display is unplugged from mst hub, connctor will be * destroyed within dm_dp_mst_connector_destroy. connector * hdcp perperties, like type, undesired, desired, enabled,
@@ -61,9 +61,9 @@ struct hdcp_workqueue {* will be retrieved from hdcp_work within dm_dp_mst_get_modes */ /* un-desired, desired, enabled */ -unsigned int content_protection[AMDGPU_DM_MAX_DISPLAY_INDEX ]; +unsigned int content_protection[AMDGPU_DM_MAX_DISPLAY_COUNT ];/* hdcp1.x, hdcp2.x */ -unsigned int hdcp_content_type[AMDGPU_DM_MAX_DISPLAY_INDEX ]; +unsigned int hdcp_content_type[AMDGPU_DM_MAX_DISPLAY_COUNT ];uint8_t max_link;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1574/1887 bpf: Clear buf on error in __bpf_get_task_stack
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Olsa <[email protected]>
[ Upstream commit f5d242825ca417bb6afe35fde6e8880f97ca43fb ]
Both bpf_get_task_stack and bpf_get_task_stack_sleepable helpers that use __bpf_get_task_stack have buf defined as ARG_PTR_TO_UNINIT_MEM argument and we should initialize the buf on every return path.
Adding missing buf memset for __bpf_get_task_stack fail paths. This provides deterministic buffer contents, which is useful when the buffer is used directly as a map key.
--- kernel/bpf/stackmap.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
kernel/bpf/stackmap.c +5 -2
@@ -504,14 +504,17 @@ BPF_CALL_4(bpf_get_task_stack, struct task_struct *, task, void *, buf,struct pt_regs *regs;long res = -EINVAL ; -if (!try_get_task_stack (task)) +if (!try_get_task_stack (task)) { +memset (buf,0 , size);return -EFAULT ; + } regs =task_pt_regs (task);if (regs) res =__bpf_get_stack (regs, task,NULL , buf, size, flags); +else +memset (buf,0 , size);put_task_stack (task); -return res; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1575/1887 ocfs2/heartbeat: fix slot mapping rollback leaks on error paths
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yufan Chen <[email protected]>
[ Upstream commit 5686459423d03d192134166f0ec45f98bb2d5d57 ]
o2hb_map_slot_data() allocates hr_tmp_block, hr_slots, hr_slot_data, and pages in stages. If a later allocation fails, the current code returns without unwinding the earlier allocations.
o2hb_region_dev_store() also leaves slot mapping resources behind when setup aborts, and it keeps hr_aborted_start/hr_node_deleted set across retries. That leaves stale state behind after a failed start.
Factor the slot cleanup into o2hb_unmap_slot_data(), use it from both o2hb_map_slot_data() and o2hb_region_release(), and call it from the dev_store() rollback after stopping a started heartbeat thread. While freeing pages, clear each hr_slot_data entry as it is released, and reset the start state before each new setup attempt.
This closes the slot mapping leak on allocation/setup failure paths and keeps failed setup attempts retryable.
Stable-dep-of: 011291b70ba4 ("ocfs2: o2hb: quiesce negotiate handlers and timeout work")
--- fs/ocfs2/cluster/heartbeat.c | 69 +++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 20 deletions(-)
fs/ocfs2/cluster/heartbeat.c +49 -20
@@ -1511,33 +1511,45 @@ static struct o2hb_region *to_o2hb_region(struct config_item *item) return item ? container_of(item, struct o2hb_region, hr_item) : NULL; } -/* drop_item only drops its ref after killing the thread, nothing should -* be using the region anymore. this has to clean up any state that -* attributes might have built up. */ -static void o2hb_region_release (struct config_item *item) +static void o2hb_unmap_slot_data (struct o2hb_region *reg) {int i;struct page *page; -struct o2hb_region *reg =to_o2hb_region (item); - -mlog (ML_HEARTBEAT ,"hb region release (%pg)\n" , reg->hr_bdev); - -kfree (reg->hr_tmp_block);if (reg->hr_slot_data) {for (i =0 ; i < reg->hr_num_pages; i++) { page = reg->hr_slot_data[i]; -if (page) +if (page) {__free_page (page); + reg->hr_slot_data[i] =NULL ; + } }kfree (reg->hr_slot_data); + reg->hr_slot_data =NULL ; } +kfree (reg->hr_slots); + reg->hr_slots =NULL ; + +kfree (reg->hr_tmp_block); + reg->hr_tmp_block =NULL ; +} + +/* drop_item only drops its ref after killing the thread, nothing should +* be using the region anymore. this has to clean up any state that +* attributes might have built up. +*/ +static void o2hb_region_release (struct config_item *item) +{ +struct o2hb_region *reg =to_o2hb_region (item); + +mlog (ML_HEARTBEAT ,"hb region release (%pg)\n" , reg->hr_bdev); + +o2hb_unmap_slot_data (reg); +if (reg->hr_bdev)blkdev_put (reg->hr_bdev,FMODE_READ |FMODE_WRITE ); -kfree (reg->hr_slots); -debugfs_remove_recursive (reg->hr_debug_dir);kfree (reg->hr_db_livenodes);kfree (reg->hr_db_regnum);
@@ -1688,6 +1700,7 @@ static void o2hb_init_region_params(struct o2hb_region *reg)static int o2hb_map_slot_data (struct o2hb_region *reg) {int i, j; +int ret = -ENOMEM ;unsigned int last_slot;unsigned int spp = reg->hr_slots_per_page;struct page *page;
@@ -1695,15 +1708,15 @@ static int o2hb_map_slot_data(struct o2hb_region *reg)struct o2hb_disk_slot *slot; reg->hr_tmp_block =kmalloc (reg->hr_block_bytes,GFP_KERNEL ); -if (reg->hr_tmp_block ==NULL ) -return -ENOMEM ; +if (!reg->hr_tmp_block) + goto out; reg->hr_slots =kcalloc (reg->hr_blocks,sizeof (struct o2hb_disk_slot ),GFP_KERNEL ); -if (reg->hr_slots ==NULL ) -return -ENOMEM ; +if (!reg->hr_slots) + goto out; -for (i =0 ; i < reg->hr_blocks; i++) { +for (i =0 ; i < reg->hr_blocks; i++) { slot = ®->hr_slots[i]; slot->ds_node_num = i;INIT_LIST_HEAD (&slot->ds_live_item);
@@ -1718,12 +1731,12 @@ static int o2hb_map_slot_data(struct o2hb_region *reg) reg->hr_slot_data =kcalloc (reg->hr_num_pages,sizeof (struct page *),GFP_KERNEL );if (!reg->hr_slot_data) -return -ENOMEM ; + goto out; -for (i =0 ; i < reg->hr_num_pages; i++) { +for (i =0 ; i < reg->hr_num_pages; i++) { page =alloc_page (GFP_KERNEL );if (!page) -return -ENOMEM ; + goto out; reg->hr_slot_data[i] = page;
@@ -1743,6 +1756,10 @@ static int o2hb_map_slot_data(struct o2hb_region *reg) }return 0 ; + +out: +o2hb_unmap_slot_data (reg); +return ret; }/* Read in all the slots available and populate the tracking
@@ -1833,6 +1850,8 @@ static ssize_t o2hb_region_dev_store(struct config_item *item, goto out3; } + reg->hr_aborted_start =0 ; + reg->hr_node_deleted =0 ;o2hb_init_region_params (reg);/* Generation of zero is invalid */
@@ -1924,6 +1943,16 @@ static ssize_t o2hb_region_dev_store(struct config_item *item, out3:if (ret <0 ) { +spin_lock (&o2hb_live_lock); + hb_task = reg->hr_task; + reg->hr_task =NULL ; +spin_unlock (&o2hb_live_lock); + +if (hb_task) +kthread_stop (hb_task); + +o2hb_unmap_slot_data (reg); +blkdev_put (reg->hr_bdev,FMODE_READ |FMODE_WRITE ); reg->hr_bdev =NULL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1576/1887 ocfs2/cluster: keep heartbeat local node stable
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <[email protected]>
[ Upstream commit 688bc88e2046dd6ce81ce18079b5254cb8dadc0e ]
o2nm_node_local_store() handles local=0 by stopping o2net and setting cl_local_node to O2NM_INVALID_NODE_NUM, but it leaves cl_has_local set. That stale state makes o2nm_this_node() return 255, blocks a later local=1 attempt with -EBUSY, and can feed 255 to heartbeat users that call o2nm_this_node() dynamically.
Clearing cl_has_local is required when the local node is reset. But heartbeat threads can still be running at that point. They pin the local node config item at startup, yet o2hb_do_disk_heartbeat() and thread teardown re-read o2nm_this_node() for the local slot and for o2nm_undepend_this_node(). Once local=0 has cleared the live local-node state, those dynamic reads return O2NM_MAX_NODES, which is also the invalid node number 255.
Store the local node number in the heartbeat region when the region starts. Use that stable node for heartbeat slot writes/checks, negotiation messages, and the final configfs undepend. Stop the heartbeat loop when the current local node no longer matches the stored node, and clear cl_has_local together with cl_local_node in the local=0 path so nodemanager state matches node removal.
Validation reproduced this kernel report:
KASAN slab-out-of-bounds in o2hb_do_disk_heartbeat+0x372/0xb30
RIP: 0010:memset+0xf/0x20 Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xd0/0x630 o2hb_do_disk_heartbeat+0x372/0xb30 (fs/ocfs2/cluster/heartbeat.c:1079) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x188/0x2f0 kasan_report+0xe4/0x120 o2hb_do_disk_heartbeat+0x5/0xb30 (fs/ocfs2/cluster/heartbeat.c:1079) o2hb_thread+0x14e/0x770 kthread_affine_node+0x139/0x180 lockdep_hardirqs_on_prepare+0xda/0x190 trace_hardirqs_on+0x18/0x130 kthread+0x19d/0x1e0 ret_from_fork+0x37a/0x4d0 __switch_to+0x2d5/0x6f0 ret_from_fork_asm+0x1a/0x30
Stable-dep-of: 011291b70ba4 ("ocfs2: o2hb: quiesce negotiate handlers and timeout work")
--- fs/ocfs2/cluster/heartbeat.c | 43 +++++++++++++++++++++++----------- fs/ocfs2/cluster/nodemanager.c | 19 +++++++++++---- fs/ocfs2/cluster/nodemanager.h | 2 ++ 3 files changed, 46 insertions(+), 18 deletions(-)
fs/ocfs2/cluster/heartbeat.c +29 -14
@@ -210,6 +210,7 @@ struct o2hb_region {/* protected by the hr_callback_sem */ struct task_struct *hr_task; +u8 hr_node_num;unsigned int hr_blocks;unsigned long long hr_start_block;
@@ -352,12 +353,12 @@ static void o2hb_disarm_timeout(struct o2hb_region *reg)cancel_delayed_work_sync (®->hr_nego_timeout_work); } -static int o2hb_send_nego_msg (int key,int type,u8 target) +static int o2hb_send_nego_msg (int key,int type,u8 target,u8 node_num) {struct o2hb_nego_msg msg;int status, ret; - msg.node_num =o2nm_this_node (); + msg.node_num = node_num; again: ret =o2net_send_message (type, key, &msg,sizeof (msg), target, &status);
@@ -375,8 +376,10 @@ static void o2hb_nego_timeout(struct work_struct *work)unsigned long live_node_bitmap[BITS_TO_LONGS (O2NM_MAX_NODES )];int master_node, i, ret;struct o2hb_region *reg; +u8 node_num; reg = container_of(work, struct o2hb_region, hr_nego_timeout_work.work); + node_num = reg->hr_node_num;/* don't negotiate timeout if last hb failed since it is very * possible io failed. Should let write timeout fence self. */
@@ -387,10 +390,10 @@ static void o2hb_nego_timeout(struct work_struct *work)/* lowest node as master node to make negotiate decision. */ master_node =find_first_bit (live_node_bitmap,O2NM_MAX_NODES ); -if (master_node ==o2nm_this_node ()) { +if (master_node == node_num) {if (!test_bit (master_node, reg->hr_nego_node_bitmap)) {printk (KERN_NOTICE "o2hb: node %d hb write hung for %ds on region %s (%pg).\n" , -o2nm_this_node (),O2HB_NEGO_TIMEOUT_MS /1000 , + node_num,O2HB_NEGO_TIMEOUT_MS /1000 ,config_item_name (®->hr_item), reg->hr_bdev);set_bit (master_node, reg->hr_nego_node_bitmap); }
@@ -418,7 +421,7 @@ static void o2hb_nego_timeout(struct work_struct *work)mlog (ML_HEARTBEAT ,"send NEGO_APPROVE msg to node %d\n" , i); ret =o2hb_send_nego_msg (reg->hr_key, -O2HB_NEGO_APPROVE_MSG , i); +O2HB_NEGO_APPROVE_MSG , i, node_num);if (ret)mlog (ML_ERROR ,"send NEGO_APPROVE msg to node %d fail %d\n" , i, ret);
@@ -426,10 +429,10 @@ static void o2hb_nego_timeout(struct work_struct *work) }else {/* negotiate timeout with master node. */ printk (KERN_NOTICE "o2hb: node %d hb write hung for %ds on region %s (%pg), negotiate timeout with node %d.\n" , -o2nm_this_node (),O2HB_NEGO_TIMEOUT_MS /1000 ,config_item_name (®->hr_item), + node_num,O2HB_NEGO_TIMEOUT_MS /1000 ,config_item_name (®->hr_item), reg->hr_bdev, master_node); ret =o2hb_send_nego_msg (reg->hr_key,O2HB_NEGO_TIMEOUT_MSG , - master_node); + master_node, node_num);if (ret)mlog (ML_ERROR ,"send NEGO_TIMEOUT msg to node %d fail %d\n" , master_node, ret);
@@ -601,7 +604,9 @@ static int o2hb_issue_node_write(struct o2hb_region *reg,o2hb_bio_wait_init (write_wc); - slot =o2nm_this_node (); + slot = reg->hr_node_num; +if (slot >=O2NM_MAX_NODES ) +return -EINVAL ; bio =o2hb_setup_one_bio (reg, write_wc, &slot, slot+1 ,REQ_OP_WRITE |REQ_SYNC );
@@ -670,8 +675,12 @@ static int o2hb_check_own_slot(struct o2hb_region *reg)struct o2hb_disk_slot *slot;struct o2hb_disk_heartbeat_block *hb_block;char *errstr; +u8 node_num = reg->hr_node_num; + +if (node_num >=O2NM_MAX_NODES ) +return 0 ; - slot = ®->hr_slots[o2nm_this_node ()]; + slot = ®->hr_slots[node_num];/* Don't check on our 1st timestamp */ if (!slot->ds_last_time)return 0 ;
@@ -712,7 +721,10 @@ static inline void o2hb_prepare_block(struct o2hb_region *reg,struct o2hb_disk_slot *slot;struct o2hb_disk_heartbeat_block *hb_block; - node_num =o2nm_this_node (); + node_num = reg->hr_node_num; +if (node_num >=O2NM_MAX_NODES ) +return ; + slot = ®->hr_slots[node_num]; hb_block = (struct o2hb_disk_heartbeat_block *)slot->ds_raw_block;
@@ -1206,7 +1218,7 @@ static int o2hb_thread(void *data)set_user_nice (current,MIN_NICE );/* Pin node */ - ret =o2nm_depend_this_node (); + ret =o2nm_depend_node (reg->hr_node_num);if (ret) {mlog (ML_ERROR ,"Node has been deleted, ret = %d\n" , ret); reg->hr_node_deleted =1 ;
@@ -1215,7 +1227,8 @@ static int o2hb_thread(void *data) }while (!kthread_should_stop () && - !reg->hr_unclean_stop && !reg->hr_aborted_start) { + !reg->hr_unclean_stop && !reg->hr_aborted_start && +o2nm_this_node () == reg->hr_node_num) {/* We track the time spent inside * o2hb_do_disk_heartbeat so that we avoid more than * hr_timeout_ms between disk writes. On busy systems
@@ -1264,7 +1277,7 @@ static int o2hb_thread(void *data) }/* Unpin node */ -o2nm_undepend_this_node (); +o2nm_undepend_node (reg->hr_node_num);mlog (ML_HEARTBEAT |ML_KTHREAD ,"o2hb thread exiting\n" );
@@ -1812,7 +1825,8 @@ static ssize_t o2hb_region_dev_store(struct config_item *item,/* We can't heartbeat without having had our node number * configured yet. */ -if (o2nm_this_node () ==O2NM_MAX_NODES ) + reg->hr_node_num =o2nm_this_node (); +if (reg->hr_node_num ==O2NM_MAX_NODES ) goto out; fd =simple_strtol (p, &p,0 );
@@ -2060,6 +2074,7 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g ret = -ENAMETOOLONG ; goto free; } + reg->hr_node_num =O2NM_MAX_NODES ;spin_lock (&o2hb_live_lock); reg->hr_region_num =0 ;
fs/ocfs2/cluster/nodemanager.c +15 -4
@@ -368,6 +368,7 @@ static ssize_t o2nm_node_local_store(struct config_item *item, const char *page,if (!tmp && cluster->cl_has_local && cluster->cl_local_node == node->nd_num) {o2net_stop_listening (node); + cluster->cl_has_local =0 ; cluster->cl_local_node =O2NM_INVALID_NODE_NUM ; }
@@ -791,12 +792,12 @@ void o2nm_undepend_item(struct config_item *item)configfs_undepend_item (item); } -int o2nm_depend_this_node (void ) +int o2nm_depend_node (u8 node_num) {int ret =0 ;struct o2nm_node *local_node; - local_node =o2nm_get_node_by_num (o2nm_this_node ()); + local_node =o2nm_get_node_by_num (node_num);if (!local_node) { ret = -EINVAL ; goto out;
@@ -809,17 +810,27 @@ int o2nm_depend_this_node(void)return ret; } -void o2nm_undepend_this_node (void ) +void o2nm_undepend_node (u8 node_num) {struct o2nm_node *local_node; - local_node =o2nm_get_node_by_num (o2nm_this_node ()); + local_node =o2nm_get_node_by_num (node_num);BUG_ON (!local_node);o2nm_undepend_item (&local_node->nd_item);o2nm_node_put (local_node); } +int o2nm_depend_this_node (void ) +{ +return o2nm_depend_node (o2nm_this_node ()); +} + +void o2nm_undepend_this_node (void ) +{ +o2nm_undepend_node (o2nm_this_node ()); +} + static void __exit exit_o2nm(void) {
fs/ocfs2/cluster/nodemanager.h +2 -0
@@ -66,6 +66,8 @@ void o2nm_node_put(struct o2nm_node *node);int o2nm_depend_item (struct config_item *item);int o2nm_depend_item_unlocked (struct config_item *item);void o2nm_undepend_item (struct config_item *item); +int o2nm_depend_node (u8 node_num); +void o2nm_undepend_node (u8 node_num);int o2nm_depend_this_node (void );void o2nm_undepend_this_node (void );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1577/1887 ocfs2: o2hb: quiesce negotiate handlers and timeout work
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <[email protected]>
[ Upstream commit 011291b70ba4832e136b7b581825b2bc0f525bf6 ]
Heartbeat regions publish struct o2hb_region as the private data for the NEGO_TIMEOUT and NEGO_APPROVE o2net handlers as soon as make_item() creates the configfs region. The approve handler can call o2hb_arm_timeout(), so a peer can touch the region timeout work before dev_store() has finished building the heartbeat runtime, or after teardown has started to shut that runtime back down.
The final configfs put also has to keep reg alive until the last in-flight o2net callback drops its handler reference. o2net_unregister_handler_list() blocks future handler lookups, but it does not wait for sc_rx_work that already passed o2net_handler_get(). That drain needs to cover local listener teardown as well, where the o2net ordered workqueue may already be inside destroy_workqueue().
Fix the lifetime rule in both directions. Initialize the region delayed works before publishing reg through the o2net handler table, keep new or stopping regions non-armable with hr_stopping, and quiesce both delayed works on failed-start and teardown paths even when no heartbeat thread is left to call o2hb_disarm_timeout(). Then unregister handlers before tearing down handler-visible region state and make the drain wait for the active or destroying o2net ordered workqueue before release frees reg.
The buggy scenario involves two paths, with each column showing the order within that path:
region lifecycle: late negotiate callback:
1. make_item() registers the 1. o2net_process_message() gets a
region handlers before heartbeat handler for reg.
dev_store() has built a 2. The callback runs after the lookup
runnable heartbeat context. lock is dropped and dereferences reg.
2. A failed start or rmdir 3. An approve or timeout path tries to
stops the heartbeat thread, queue reg's delayed work, or release quiesces existing work, and races the callback body after handler drops the final configfs ref. unregister.
3. region_release() must drain 4. The callback or delayed work can
handler-visible o2net rx work outlive reg unless lifecycle code
before freeing reg. keeps the region non-armable and
drains the active-or-destroying o2net workqueue.
Validation reproduced this kernel report:
KASAN slab-use-after-free in __run_timers+0x22c/0x5b0
Write of size 8 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 __run_timers+0x22c/0x5b0 kasan_report+0xe0/0x110 _raw_spin_unlock_irqrestore+0x27/0x60 try_to_wake_up+0x191/0xf70 timer_expire_remote+0xae/0xf0 run_timer_softirq+0x19b/0x1a0 handle_softirqs+0x156/0x660 __irq_exit_rcu+0xc4/0x160 irq_exit_rcu+0xe/0x20 sysvec_apic_timer_interrupt+0x6c/0x80 asm_sysvec_apic_timer_interrupt+0x1a/0x20 Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 o2hb_heartbeat_group_make_item+0x3c/0x600
--- fs/ocfs2/cluster/heartbeat.c | 56 ++++++++++++++++--- fs/ocfs2/cluster/tcp.c | 103 ++++++++++++++++++++++++++++++----- fs/ocfs2/cluster/tcp.h | 1 + 3 files changed, 138 insertions(+), 22 deletions(-)
fs/ocfs2/cluster/heartbeat.c +48 -8
@@ -14,6 +14,7 @@#include <linux/file.h> #include <linux/kthread.h> #include <linux/configfs.h> +#include <linux/mutex.h> #include <linux/random.h> #include <linux/crc32.h> #include <linux/time.h>
@@ -265,6 +266,9 @@ struct o2hb_region {/* Message key for negotiate timeout message. */ unsigned int hr_key;struct list_head hr_handler_list; +/* Serializes timeout arming against failed-start and teardown. */ +struct mutex hr_arming_mutex; +bool hr_stopping;/* last hb status, 0 for success, other value for error. */ int hr_last_hb_status;
@@ -324,9 +328,14 @@ static void o2hb_write_timeout(struct work_struct *work)static void o2hb_arm_timeout (struct o2hb_region *reg) { +mutex_lock (®->hr_arming_mutex); + +if (reg->hr_stopping) + goto out_unlock; +/* Arm writeout only after thread reaches steady state */ if (atomic_read (®->hr_steady_iterations) !=0 ) -return ; + goto out_unlock;mlog (ML_HEARTBEAT ,"Queue write timeout for %u ms\n" ,O2HB_MAX_WRITE_TIMEOUT_MS );
@@ -345,6 +354,18 @@ static void o2hb_arm_timeout(struct o2hb_region *reg)schedule_delayed_work (®->hr_nego_timeout_work,msecs_to_jiffies (O2HB_NEGO_TIMEOUT_MS ));memset (reg->hr_nego_node_bitmap,0 ,sizeof (reg->hr_nego_node_bitmap)); + +out_unlock: +mutex_unlock (®->hr_arming_mutex); +} + +static void o2hb_queue_nego_timeout (struct o2hb_region *reg, +unsigned long delay) +{ +mutex_lock (®->hr_arming_mutex); +if (!reg->hr_stopping) +schedule_delayed_work (®->hr_nego_timeout_work, delay); +mutex_unlock (®->hr_arming_mutex); }static void o2hb_disarm_timeout (struct o2hb_region *reg)
@@ -353,6 +374,19 @@ static void o2hb_disarm_timeout(struct o2hb_region *reg)cancel_delayed_work_sync (®->hr_nego_timeout_work); } +static void o2hb_set_region_stopping (struct o2hb_region *reg,bool stopping) +{ +mutex_lock (®->hr_arming_mutex); + reg->hr_stopping = stopping; +mutex_unlock (®->hr_arming_mutex); +} + +static void o2hb_quiesce_timeout (struct o2hb_region *reg) +{ +o2hb_set_region_stopping (reg, true); +o2hb_disarm_timeout (reg); +} +static int o2hb_send_nego_msg (int key,int type,u8 target,u8 node_num) {struct o2hb_nego_msg msg;
@@ -402,8 +436,7 @@ static void o2hb_nego_timeout(struct work_struct *work)/* check negotiate bitmap every second to do timeout * approve decision. */ -schedule_delayed_work (®->hr_nego_timeout_work, -msecs_to_jiffies (1000 )); +o2hb_queue_nego_timeout (reg,msecs_to_jiffies (1000 ));return ; }
@@ -1558,6 +1591,8 @@ static void o2hb_region_release(struct config_item *item)mlog (ML_HEARTBEAT ,"hb region release (%pg)\n" , reg->hr_bdev); +o2hb_quiesce_timeout (reg); +o2net_unregister_and_flush_handler_list (®->hr_handler_list);o2hb_unmap_slot_data (reg);if (reg->hr_bdev)
@@ -1573,7 +1608,6 @@ static void o2hb_region_release(struct config_item *item)list_del (®->hr_all_item);spin_unlock (&o2hb_live_lock); -o2net_unregister_handler_list (®->hr_handler_list);kfree (reg); }
@@ -1886,9 +1920,6 @@ static ssize_t o2hb_region_dev_store(struct config_item *item, goto out3; } -INIT_DELAYED_WORK (®->hr_write_timeout_work, o2hb_write_timeout); -INIT_DELAYED_WORK (®->hr_nego_timeout_work, o2hb_nego_timeout); -/* * A node is considered live after it has beat LIVE_THRESHOLD * times. We're not steady until we've given them a chance
@@ -1908,6 +1939,7 @@ static ssize_t o2hb_region_dev_store(struct config_item *item,atomic_set (®->hr_steady_iterations, live_threshold);/* unsteady_iterations is triple the steady_iterations */ atomic_set (®->hr_unsteady_iterations, (live_threshold *3 )); +o2hb_set_region_stopping (reg, false); hb_task =kthread_run (o2hb_thread, reg,"o2hb-%s" , reg->hr_item.ci_name);
@@ -1957,6 +1989,8 @@ static ssize_t o2hb_region_dev_store(struct config_item *item, out3:if (ret <0 ) { +o2hb_quiesce_timeout (reg); +spin_lock (&o2hb_live_lock); hb_task = reg->hr_task; reg->hr_task =NULL ;
@@ -2099,6 +2133,10 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g*/ reg->hr_key =crc32_le (reg->hr_region_num +O2NM_MAX_REGIONS , name,strlen (name)); +mutex_init (®->hr_arming_mutex); + reg->hr_stopping = true; +INIT_DELAYED_WORK (®->hr_write_timeout_work, o2hb_write_timeout); +INIT_DELAYED_WORK (®->hr_nego_timeout_work, o2hb_nego_timeout);INIT_LIST_HEAD (®->hr_handler_list); ret = o2net_register_handler(O2HB_NEGO_TIMEOUT_MSG, reg->hr_key, sizeof(struct o2hb_nego_msg),
@@ -2119,7 +2157,7 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *greturn ®->hr_item; unregister_handler: -o2net_unregister_handler_list (®->hr_handler_list); +o2net_unregister_and_flush_handler_list (®->hr_handler_list); remove_item:spin_lock (&o2hb_live_lock);list_del (®->hr_all_item);
@@ -2138,6 +2176,8 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group,struct o2hb_region *reg =to_o2hb_region (item);int quorum_region =0 ; +o2hb_quiesce_timeout (reg); +/* stop the thread when the user removes the region dir */ spin_lock (&o2hb_live_lock); hb_task = reg->hr_task;
fs/ocfs2/cluster/tcp.c +89 -14
160 lines not on this page · raw message
fs/ocfs2/cluster/tcp.h +1 -0
@@ -89,6 +89,7 @@ int o2net_register_handler(u32 msg_type, u32 key, u32 max_len,o2net_post_msg_handler_func *post_func, struct list_head *unreg_list);void o2net_unregister_handler_list (struct list_head *list); +void o2net_unregister_and_flush_handler_list (struct list_head *list);void o2net_fill_node_map (unsigned long *map,unsigned bytes);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1578/1887 RDMA/erdma: Hold QP references for AE and CM processing
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cheng Xu <[email protected]>
[ Upstream commit a52eeff32024f190b3bdc99088c7becccd4fa60b ]
AE QP fatal events and iWARP CM paths load QPs from dev->qp_xa and then use or reference them outside the xarray lock. erdma_destroy_qp() can drop the destroy-path reference and free QP resources while such a lookup is in flight.
Add erdma_qp_get_by_qpn() to acquire a kref under the xarray lock with kref_get_unless_zero(). Remove the QP from the xarray before dropping the destroy-path reference so no new lookup can acquire it while destruction waits for existing users.
--- drivers/infiniband/hw/erdma/erdma_cm.c | 6 ++---- drivers/infiniband/hw/erdma/erdma_eq.c | 3 ++- drivers/infiniband/hw/erdma/erdma_verbs.c | 6 +++++- drivers/infiniband/hw/erdma/erdma_verbs.h | 15 +++++++++++++++ 4 files changed, 24 insertions(+), 6 deletions(-)
drivers/infiniband/hw/erdma/erdma_cm.c +2 -4
@@ -1014,10 +1014,9 @@ int erdma_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params)if (laddr->sa_family !=AF_INET || raddr->sa_family !=AF_INET )return -EAFNOSUPPORT ; - qp =find_qp_by_qpn (dev, params->qpn); + qp =erdma_qp_get_by_qpn (dev, params->qpn);if (!qp)return -ENOENT ; -erdma_qp_get (qp); ret =sock_create (AF_INET ,SOCK_STREAM ,IPPROTO_TCP , &s);if (ret <0 )
@@ -1146,10 +1145,9 @@ int erdma_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params)return -ECONNRESET ; } - qp =find_qp_by_qpn (dev, params->qpn); + qp =erdma_qp_get_by_qpn (dev, params->qpn);if (!qp)return -ENOENT ; -erdma_qp_get (qp);down_write (&qp->state_lock);if (qp->attrs.state >ERDMA_QP_STATE_RTR ) {
drivers/infiniband/hw/erdma/erdma_eq.c +2 -1
@@ -65,7 +65,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev)erdma_cq_put (cq); }else { qpn =le32_to_cpu (aeqe->event_data0); - qp =find_qp_by_qpn (dev, qpn); + qp =erdma_qp_get_by_qpn (dev, qpn);if (!qp)continue ;
@@ -75,6 +75,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev)if (qp->ibqp.event_handler) qp->ibqp.event_handler (&event, qp->ibqp.qp_context); +erdma_qp_put (qp); } }
drivers/infiniband/hw/erdma/erdma_verbs.c +5 -1
@@ -1026,6 +1026,7 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)struct erdma_ucontext *ctx =rdma_udata_to_drv_context ( udata, struct erdma_ucontext, ibucontext);struct erdma_qp_attrs qp_attrs; +unsigned long flags;int err;struct erdma_cmdq_destroy_qp_req req;
@@ -1042,6 +1043,10 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)if (err)return err; +xa_lock_irqsave (&dev->qp_xa, flags); +__xa_erase (&dev->qp_xa,QP_ID (qp)); +xa_unlock_irqrestore (&dev->qp_xa, flags); +erdma_qp_put (qp);wait_for_completion (&qp->safe_free);
@@ -1064,7 +1069,6 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)if (qp->cep)erdma_cep_put (qp->cep); -xa_erase (&dev->qp_xa,QP_ID (qp));return 0 ; }
drivers/infiniband/hw/erdma/erdma_verbs.h +15 -0
@@ -250,6 +250,21 @@ static inline struct erdma_qp *find_qp_by_qpn(struct erdma_dev *dev, int id)return (struct erdma_qp *)xa_load (&dev->qp_xa, id); } +static inline struct erdma_qp *erdma_qp_get_by_qpn (struct erdma_dev *dev, +int id) +{ +struct erdma_qp *qp; +unsigned long flags; + +xa_lock_irqsave (&dev->qp_xa, flags); + qp =xa_load (&dev->qp_xa, id); +if (qp && !kref_get_unless_zero (&qp->ref)) + qp =NULL ; +xa_unlock_irqrestore (&dev->qp_xa, flags); + +return qp; +} +static inline struct erdma_cq *erdma_cq_get_by_cqn (struct erdma_dev *dev,int id) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1579/1887 thermal: hwmon: Remove hwmon class device along with its parent
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rafael J. Wysocki <[email protected]>
[ Upstream commit 2b57e24d34b2dfc43c81942bb359d6315bf302fb ]
The current code creates one hwmon device per thermal zone type and that device is registered under the first thermal zone of the given type.
That turns out to be problematic when the thermal zone holding the hwmon device is removed.
For example, say that there are two ACPI thermal zones on a system
/sys/devices/virtual/thermal/thermal_zone0/
/sys/devices/virtual/thermal/thermal_zone1/
The current code registers a hwmon class device for thermal_zone0 only:
/sys/devices/virtual/thermal/thermal_zone0/hwmon0/
because the type is "acpitz" for both of them, but it adds a sysfs attribute that belongs to thermal_zone1 under it:
/sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp2_input
There is also
/sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp1_input
which belongs to thermal_zone0.
When thermal_zone0 is removed, say because the ACPI thermal driver is unbound from the underlying platform device, thermal_remove_hwmon_sysfs() skips the removal of hwmon0 because of the temp2_input attribute belonging to thermal_zone1 which effectively prevents thermal_zone0 removal from making progress.
Address this by making thermal_remove_hwmon_sysfs() remove the entire hwmon class device interface for the given thermal zone type when the thermal zone device holding it is removed.
To prevent races with thermal_add_hwmon_sysfs() that may interfere with this, carry out the entire addition and removal of hwmon sysfs interfaces for thermal zones under thermal_hwmon_list_lock.
Also adjust the layout of the labels in thermal_add_hwmon_sysfs() to the current kernel coding style to align with the new "unlock" label.
--- drivers/thermal/thermal_hwmon.c | 87 +++++++++++++-------------------- 1 file changed, 34 insertions(+), 53 deletions(-)
drivers/thermal/thermal_hwmon.c +34 -53
@@ -92,34 +92,12 @@ thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)struct thermal_hwmon_device *hwmon;char type[THERMAL_NAME_LENGTH ]; -mutex_lock (&thermal_hwmon_list_lock);list_for_each_entry (hwmon, &thermal_hwmon_list, node) {strcpy (type, tz->type);strreplace (type,'-' ,'_' ); -if (!strcmp (hwmon->type, type)) { -mutex_unlock (&thermal_hwmon_list_lock); +if (!strcmp (hwmon->type, type))return hwmon; - } } -mutex_unlock (&thermal_hwmon_list_lock); - -return NULL ; -} - -/* Find the temperature input matching a given thermal zone */ -static struct thermal_hwmon_temp * -thermal_hwmon_lookup_temp (const struct thermal_hwmon_device *hwmon, -const struct thermal_zone_device *tz) -{ -struct thermal_hwmon_temp *temp; - -mutex_lock (&thermal_hwmon_list_lock); -list_for_each_entry (temp, &hwmon->tz_list, hwmon_node) -if (temp->tz == tz) { -mutex_unlock (&thermal_hwmon_list_lock); -return temp; - } -mutex_unlock (&thermal_hwmon_list_lock);return NULL ; }
@@ -135,7 +113,9 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)struct thermal_hwmon_device *hwmon;struct thermal_hwmon_temp *temp;int new_hwmon_device =1 ; -int result; +int result =0 ; + +mutex_lock (&thermal_hwmon_list_lock); hwmon =thermal_hwmon_lookup_by_type (tz);if (hwmon) {
@@ -144,8 +124,10 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) } hwmon =kzalloc (sizeof (*hwmon),GFP_KERNEL ); -if (!hwmon) -return -ENOMEM ; +if (!hwmon) { + result = -ENOMEM ; + goto unlock; + }INIT_LIST_HEAD (&hwmon->tz_list);strscpy (hwmon->type, tz->type,THERMAL_NAME_LENGTH );
@@ -193,24 +175,24 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) temp->temp_crit_present = true; } -mutex_lock (&thermal_hwmon_list_lock);if (new_hwmon_device)list_add_tail (&hwmon->node, &thermal_hwmon_list);list_add_tail (&temp->hwmon_node, &hwmon->tz_list); -mutex_unlock (&thermal_hwmon_list_lock); -return 0 ; + goto unlock; - unregister_input: +unregister_input:device_remove_file (hwmon->device, &temp->temp_input.attr); - free_temp_mem: +free_temp_mem:kfree (temp); - unregister_name: +unregister_name:if (new_hwmon_device)hwmon_device_unregister (hwmon->device); - free_mem: +free_mem:if (new_hwmon_device)kfree (hwmon); +unlock: +mutex_unlock (&thermal_hwmon_list_lock);return result; }
@@ -218,8 +200,11 @@ EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);void thermal_remove_hwmon_sysfs (struct thermal_zone_device *tz) { +struct thermal_hwmon_temp *temp, *entry;struct thermal_hwmon_device *hwmon; -struct thermal_hwmon_temp *temp; +bool unregister; + +guard (mutex)(&thermal_hwmon_list_lock); hwmon =thermal_hwmon_lookup_by_type (tz);if (unlikely (!hwmon)) {
@@ -228,29 +213,25 @@ void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)return ; } - temp =thermal_hwmon_lookup_temp (hwmon, tz); -if (unlikely (!temp)) { -/* Should never happen... */ -dev_dbg (&tz->device,"temperature input lookup failed!\n" ); -return ; - } + unregister = hwmon->device->parent == &tz->device; -device_remove_file (hwmon->device, &temp->temp_input.attr); -if (temp->temp_crit_present) -device_remove_file (hwmon->device, &temp->temp_crit.attr); +list_for_each_entry_safe_reverse (temp, entry, &hwmon->tz_list, hwmon_node) { +if (!unregister && temp->tz != tz) +continue ; -mutex_lock (&thermal_hwmon_list_lock); -list_del (&temp->hwmon_node); -kfree (temp); -if (!list_empty (&hwmon->tz_list)) { -mutex_unlock (&thermal_hwmon_list_lock); -return ; +device_remove_file (hwmon->device, &temp->temp_input.attr); +if (temp->temp_crit_present) +device_remove_file (hwmon->device, &temp->temp_crit.attr); + +list_del (&temp->hwmon_node); +kfree (temp); } -list_del (&hwmon->node); -mutex_unlock (&thermal_hwmon_list_lock); -hwmon_device_unregister (hwmon->device); -kfree (hwmon); +if (unregister) { +list_del (&hwmon->node); +hwmon_device_unregister (hwmon->device); +kfree (hwmon); + } }EXPORT_SYMBOL_GPL (thermal_remove_hwmon_sysfs);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1580/1887 bpf, cgroup: Fix invalid storage access after __cgroup_bpf_attach failed
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pu Lehui <[email protected]>
[ Upstream commit 6655c409707ec8ce9ce0850ffe4fe02331fd4d9c ]
A potential invalid storage access issue can occur after replacing a cgroup bpf prog.
This occurs in the following scenario:
1. prog1 with storage is attached to a cgroup in multi-attach mode.
2. prog1 is replaced with prog2 using BPF_F_REPLACE in multi-attach mode, but fails midway (e.g. in bpf_trampoline_link_cgroup_shim or update_effective_progs).
3. A new prog3 is attached to the cgroup in multi-attach mode.
The reason is that __cgroup_bpf_attach overwrites pl->storage with the new storage prior to attachment completion. When attachment fails midway, the cleanup path calls bpf_cgroup_storages_free(new_storage) to free the newly allocated storage, but fails to restore pl->storage back to old_storage.
Consequently, the still-active prog1 holds invalid or dangling storage pointers, leading to an invalid memory access when prog1 executes and calls bpf_get_local_storage. Additionally, original pl->flags and cgrp->bpf.flags[atype] are left unrestored.
Fix this by saving old_pl_flags, old_storage, and old_flags prior to the update, and properly restoring all of them in the cleanup path on error.
--- kernel/bpf/cgroup.c | 6 ++++++ 1 file changed, 6 insertions(+)
kernel/bpf/cgroup.c +6 -0
@@ -630,8 +630,10 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,struct bpf_prog *old_prog =NULL ;struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE ] = {};struct bpf_cgroup_storage *new_storage[MAX_BPF_CGROUP_STORAGE_TYPE ] = {}; +struct bpf_cgroup_storage *old_storage[MAX_BPF_CGROUP_STORAGE_TYPE ] = {};struct bpf_prog *new_prog = prog ? : link->link.prog;enum cgroup_bpf_attach_type atype; +u32 old_flags;struct bpf_prog_list *pl;struct hlist_head *progs;int err;
@@ -677,6 +679,7 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,if (pl) { old_prog = pl->prog; +bpf_cgroup_storages_assign (old_storage, pl->storage); }else {struct hlist_node *last =NULL ;
@@ -699,6 +702,7 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp, pl->prog = prog; pl->link = link;bpf_cgroup_storages_assign (pl->storage, storage); + old_flags = cgrp->bpf.flags[atype]; cgrp->bpf.flags[atype] = saved_flags;if (type ==BPF_LSM_CGROUP ) {
@@ -729,12 +733,14 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,if (old_prog) { pl->prog = old_prog; pl->link =NULL ; +bpf_cgroup_storages_assign (pl->storage, old_storage); }bpf_cgroup_storages_free (new_storage);if (!old_prog) {hlist_del (&pl->node);kfree (pl); } + cgrp->bpf.flags[atype] = old_flags;return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1581/1887 HID: steam: Reject short reads
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vicki Pfau <[email protected]>
[ Upstream commit 33ff7b49c38b39b1f3d27db508ac0720fb25c08a ]
Steam Controller FEATURE reports encode the size of the message in the message itself. Previously we were trusting that the size reported matched the size we actually read, leading to a potential issue with short reads. Instead, we should actually verify the length of the read.
--- drivers/hid/hid-steam.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-)
drivers/hid/hid-steam.c +31 -3
@@ -135,6 +135,13 @@ static int steam_recv_report(struct steam_device *steam,u8 *buf;int ret; +/* +* All reports start with a two byte header. +* We must read at least two bytes to get a sensible output. +*/ +if (size <2 ) +return -EINVAL ; + r = steam->hdev->report_enum[HID_FEATURE_REPORT ].report_id_hash[0 ];if (!r) {hid_err (steam->hdev,"No HID_FEATURE_REPORT submitted - nothing to read\n" );
@@ -157,10 +164,31 @@ static int steam_recv_report(struct steam_device *steam, ret =hid_hw_raw_request (steam->hdev,0x00 , buf,hid_report_len (r) +1 ,HID_FEATURE_REPORT ,HID_REQ_GET_REPORT ); -if (ret >0 ) -memcpy (data, buf +1 ,min (size, ret -1 )); +if (ret >0 ) { +/* Remove the report ID from the return buffer */ + ret--; + size =min (size, ret); +memcpy (data, buf +1 , size); + }kfree (buf); -return ret; + +if (ret <0 ) +hid_err (steam->hdev,"%s: error %d\n" , __func__, ret); +else +hid_dbg (steam->hdev,"Received report %*ph\n" , size, data); +if (ret <0 ) +return ret; + +if (ret <2 ) { +hid_err (steam->hdev,"%s: reply too short\n" , __func__); +return -EPROTO ; + } +if (ret < data[1 ] +2 ) { +hid_err (steam->hdev,"%s: expected %u bytes, read %i\n" , + __func__, data[1 ] +2 , ret); +return -EPROTO ; + } +return size; } static int steam_send_report(struct steam_device *steam,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1582/1887 HID: logitech-hidpp: Fix FF device cleanup on init failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
[ Upstream commit dd5be4d9ce2dfc4d4a4527ef7d31d21a78e3cdac ]
hidpp_ff_init() creates the input force-feedback device with input_ff_create(), then allocates the HID++ FF private data, effect ID array, and workqueue.
If any of those allocations fail after input_ff_create() succeeds, the function returns an error without destroying the FF device. Add an unwind path that frees the private allocations made by hidpp_ff_init() and calls input_ff_destroy() for failures after input_ff_create() succeeds.
--- drivers/hid/hid-logitech-hidpp.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-)
drivers/hid/hid-logitech-hidpp.c +16 -7
@@ -2634,18 +2634,19 @@ static int hidpp_ff_init(struct hidpp_device *hidpp,* ownership to FF core */ data =kmemdup (data,sizeof (*data),GFP_KERNEL ); -if (!data) -return -ENOMEM ; +if (!data) { + error = -ENOMEM ; + goto err_destroy_ff; + } data->effect_ids =kcalloc (num_slots,sizeof (int ),GFP_KERNEL );if (!data->effect_ids) { -kfree (data); -return -ENOMEM ; + error = -ENOMEM ; + goto err_free_data; } data->wq =create_singlethread_workqueue ("hidpp-ff-sendqueue" );if (!data->wq) { -kfree (data->effect_ids); -kfree (data); -return -ENOMEM ; + error = -ENOMEM ; + goto err_free_effect_ids; } data->hidpp = hidpp;
@@ -2675,6 +2676,14 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, version);return 0 ; + +err_free_effect_ids: +kfree (data->effect_ids); +err_free_data: +kfree (data); +err_destroy_ff: +input_ff_destroy (dev); +return error; }/* ************************************************************************** */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1583/1887 tty: make tty_class a static const structure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Greg Kroah-Hartman <[email protected]>
[ Upstream commit 862d8312eed994a8a9af7aa8e9e15456183b10a7 ]
Now that the driver core allows for struct class to be in read-only memory, move the tty_class structure to be declared at build time placing it into read-only memory, instead of having to be dynamically allocated at boot time.
Stable-dep-of: c3b5623fd976 ("tty: skip cdev_del() when no cdev is registered")
--- drivers/tty/pty.c | 2 +- drivers/tty/tty_io.c | 24 +++++++++++------------- drivers/tty/vt/vt.c | 2 +- include/linux/tty.h | 2 +- 4 files changed, 14 insertions(+), 16 deletions(-)
drivers/tty/pty.c +1 -1
@@ -931,7 +931,7 @@ static void __init unix98_pty_init(void)if (cdev_add (&ptmx_cdev,MKDEV (TTYAUX_MAJOR ,2 ),1 ) ||register_chrdev_region (MKDEV (TTYAUX_MAJOR ,2 ),1 ,"/dev/ptmx" ) <0 )panic ("Couldn't register /dev/ptmx driver" ); - device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); + device_create(&tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); }#else
drivers/tty/tty_io.c +11 -13
@@ -3086,7 +3086,7 @@ static struct device *tty_get_device(struct tty_struct *tty) {dev_t devt =tty_devnum (tty); -return class_find_device_by_devt (tty_class, devt); +return class_find_device_by_devt (&tty_class, devt); }
@@ -3160,8 +3160,6 @@ int tty_put_char(struct tty_struct *tty, unsigned char ch) }EXPORT_SYMBOL_GPL (tty_put_char); -struct class *tty_class; -static int tty_cdev_add (struct tty_driver *driver,dev_t dev,unsigned int index,unsigned int count) {
@@ -3258,7 +3256,7 @@ struct device *tty_register_device_attr(struct tty_driver *driver,return ERR_PTR (-ENOMEM ); dev->devt = devt; - dev->class = tty_class; + dev->class = &tty_class; dev->parent = device; dev->release = tty_device_create_release;dev_set_name (dev,"%s" , name);
@@ -3315,8 +3313,7 @@ EXPORT_SYMBOL_GPL(tty_register_device_attr);*/ void tty_unregister_device (struct tty_driver *driver,unsigned index) { -device_destroy (tty_class, -MKDEV (driver->major, driver->minor_start) + index); +device_destroy (&tty_class,MKDEV (driver->major, driver->minor_start) + index);if (!(driver->flags &TTY_DRIVER_DYNAMIC_ALLOC )) {cdev_del (driver->cdevs[index]); driver->cdevs[index] =NULL ;
@@ -3531,13 +3528,14 @@ static char *tty_devnode(struct device *dev, umode_t *mode)return NULL ; } +struct class tty_class = { + .name ="tty" , + .devnode = tty_devnode, +}; +static int __init tty_class_init (void ) { - tty_class =class_create (THIS_MODULE ,"tty" ); -if (IS_ERR (tty_class)) -return PTR_ERR (tty_class); - tty_class->devnode = tty_devnode; -return 0 ; +return class_register (&tty_class); }postcore_initcall (tty_class_init);
@@ -3610,13 +3608,13 @@ int __init tty_init(void)if (cdev_add (&tty_cdev,MKDEV (TTYAUX_MAJOR ,0 ),1 ) ||register_chrdev_region (MKDEV (TTYAUX_MAJOR ,0 ),1 ,"/dev/tty" ) <0 )panic ("Couldn't register /dev/tty driver\n" ); -device_create (tty_class,NULL ,MKDEV (TTYAUX_MAJOR ,0 ),NULL ,"tty" ); +device_create (&tty_class,NULL ,MKDEV (TTYAUX_MAJOR ,0 ),NULL ,"tty" );cdev_init (&console_cdev, &console_fops);if (cdev_add (&console_cdev,MKDEV (TTYAUX_MAJOR ,1 ),1 ) ||register_chrdev_region (MKDEV (TTYAUX_MAJOR ,1 ),1 ,"/dev/console" ) <0 )panic ("Couldn't register /dev/console driver\n" ); - consdev =device_create_with_groups (tty_class,NULL , + consdev =device_create_with_groups (&tty_class,NULL ,MKDEV (TTYAUX_MAJOR ,1 ),NULL , cons_dev_groups,"console" );if (IS_ERR (consdev))
drivers/tty/vt/vt.c +1 -1
@@ -3603,7 +3603,7 @@ int __init vty_init(const struct file_operations *console_fops)if (cdev_add (&vc0_cdev,MKDEV (TTY_MAJOR ,0 ),1 ) ||register_chrdev_region (MKDEV (TTY_MAJOR ,0 ),1 ,"/dev/vc/0" ) <0 )panic ("Couldn't register /dev/tty0 driver\n" ); - tty0dev =device_create_with_groups (tty_class,NULL , + tty0dev =device_create_with_groups (&tty_class,NULL ,MKDEV (TTY_MAJOR ,0 ),NULL , vt_dev_groups,"tty0" );if (IS_ERR (tty0dev))
include/linux/tty.h +1 -1
@@ -389,7 +389,7 @@ extern struct ktermios tty_std_termios;int vcs_init (void ); -extern struct class *tty_class; +extern struct class tty_class;/** * tty_kref_get - get a tty reference
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1584/1887 tty: skip cdev_del() when no cdev is registered
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit c3b5623fd97648f2747444aa8932ae604662df93 ]
TTY device registration can fail before a cdev is allocated. Serial core keeps the port so setserial can still use it, and later removal passes the NULL cdev slot to cdev_del(), causing a NULL-pointer dereference.
Only delete the cdev when the slot is not NULL.
--- drivers/tty/tty_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/tty/tty_io.c +1 -1
@@ -3314,7 +3314,7 @@ EXPORT_SYMBOL_GPL(tty_register_device_attr);void tty_unregister_device (struct tty_driver *driver,unsigned index) {device_destroy (&tty_class,MKDEV (driver->major, driver->minor_start) + index); -if (!(driver->flags &TTY_DRIVER_DYNAMIC_ALLOC )) { +if (!(driver->flags &TTY_DRIVER_DYNAMIC_ALLOC ) && driver->cdevs[index]) {cdev_del (driver->cdevs[index]); driver->cdevs[index] =NULL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1585/1887 serial: core: clear freed pointers on uart_register_driver() failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit 61a2fb25551be0375bc16ef2a70c987dfca26183 ]
uart_register_driver() leaves drv->state pointing to freed memory when tty_alloc_driver() fails. If tty_register_driver() fails, drv->tty_driver also retains a pointer after its reference is dropped.
Drivers that use drv->state as an "already registered" flag can then skip registration on the next probe and pass the freed state to uart_add_one_port().
This issue was found with failslab on QEMU's raspi1ap board by failing registration and binding the PL011 port again.
Clear both pointers on their failure paths, as uart_unregister_driver() already does.
--- drivers/tty/serial/serial_core.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/tty/serial/serial_core.c +2 -0
@@ -2812,8 +2812,10 @@ int uart_register_driver(struct uart_driver *drv)for (i =0 ; i < drv->nr; i++)tty_port_destroy (&drv->state[i].port);tty_driver_kref_put (normal); + drv->tty_driver =NULL ; out_kfree:kfree (drv->state); + drv->state =NULL ; out:return retval; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1586/1887 serial: core: do fallible allocations before the console can be registered
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit 1a0e4fbce5d9c1bc179a35a2fd9ed142664299e3 ]
serial_core_add_one_port() allocates uport->tty_groups after uart_configure_port(), which may register the console. If the allocation fails, the driver unwinds the port while its console remains registered. The earlier uport->name allocation has a related failure path that leaves state->uart_port linked to a port being freed.
Failslab reproduced a NULL dereference in PL011 console output and a KASAN use-after-free in i.MX console output after failed binds.
Allocate the name and tty_groups before linking the port and configuring it. Reserve space for the optional driver attribute group because config_port() may populate uport->attr_group during configuration.
--- drivers/tty/serial/serial_core.c | 37 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-)
drivers/tty/serial/serial_core.c +19 -18
@@ -3094,7 +3094,6 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)struct tty_port *port;int ret =0 ;struct device *tty_dev; -int num_groups;if (uport->line >= drv->nr)return -EINVAL ;
@@ -3109,6 +3108,25 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) goto out; } + uport->name =kasprintf (GFP_KERNEL ,"%s%d" , drv->dev_name, + drv->tty_driver->name_base + uport->line); +if (!uport->name) { + ret = -ENOMEM ; + goto out; + } + +/* +* uart_configure_port() may set uport->attr_group and register the +* console. Allocate room for both groups and a NULL terminator first. +*/ + uport->tty_groups =kcalloc (3 ,sizeof (*uport->tty_groups),GFP_KERNEL ); +if (!uport->tty_groups) { +kfree (uport->name); + ret = -ENOMEM ; + goto out; + } + uport->tty_groups[0 ] = &tty_dev_attr_group; +/* Link the port to the driver state table and vice versa */ atomic_set (&state->refcount,1 );init_waitqueue_head (&state->remove_wait);
@@ -3118,12 +3136,6 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) state->pm_state =UART_PM_STATE_UNDEFINED ; uport->cons = drv->cons; uport->minor = drv->tty_driver->minor_start + uport->line; - uport->name =kasprintf (GFP_KERNEL ,"%s%d" , drv->dev_name, - drv->tty_driver->name_base + uport->line); -if (!uport->name) { - ret = -ENOMEM ; - goto out; - }/* * If this port is in use as a console then the spinlock is already
@@ -3140,17 +3152,6 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) port->console =uart_console (uport); - num_groups =2 ; -if (uport->attr_group) - num_groups++; - - uport->tty_groups =kcalloc (num_groups,sizeof (*uport->tty_groups), -GFP_KERNEL ); -if (!uport->tty_groups) { - ret = -ENOMEM ; - goto out; - } - uport->tty_groups[0 ] = &tty_dev_attr_group;if (uport->attr_group) uport->tty_groups[1 ] = uport->attr_group;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1587/1887 wifi: cfg80211: stop PMSR before P2P and NAN teardown
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhao Li <[email protected]>
[ Upstream commit 6c5fc504d0d6934132637aa3db4b9b58148eaa78 ]
PMSR request teardown must abort active measurements while the wireless_dev is still present in the driver. cfg80211_leave_locked() and cfg80211_stop_pd() already do this before invoking the driver's stop callback, but cfg80211_stop_p2p_device() and cfg80211_stop_nan() do not.
Those helpers are also called directly by nl80211, rfkill shutdown, and wireless_dev unregister paths. If one of these paths stops a P2P device or NAN interface with a pending request, it removes the mac80211 subinterface from the driver first. Subsequent request cleanup cannot reach the lower driver's abort callback, but cfg80211 frees the request regardless. Driver state can then retain a stale request and use it when it later reports a result.
Call cfg80211_pmsr_wdev_down() before stopping the P2P device or NAN interface. This keeps lower-driver request state and cfg80211 request ownership in sync for all of the helpers' callers.
--- net/wireless/core.c | 7 +++++++ 1 file changed, 7 insertions(+)
net/wireless/core.c +7 -0
@@ -232,6 +232,9 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,if (!wdev_running (wdev))return ; +wdev_lock (wdev); +cfg80211_pmsr_wdev_down (wdev); +wdev_unlock (wdev);rdev_stop_p2p_device (rdev, wdev); wdev->is_running = false;
@@ -257,6 +260,10 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,if (!wdev_running (wdev))return ; +wdev_lock (wdev); +cfg80211_pmsr_wdev_down (wdev); +wdev_unlock (wdev); +rdev_stop_nan (rdev, wdev); wdev->is_running = false;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1588/1887 wifi: brcmfmac: fix P2P action frame handling without device vif
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jason Huang <[email protected]>
[ Upstream commit 1b1edb9ebed49099bdc924cef49a9aea8b552199 ]
Some P2P action frame paths assume the P2P device vif is always available. That is not true when userspace sends non-P2P public action frames through the primary interface, or when action-frame abort runs after the P2P device vif has not been created.
Fall back to the primary vif when aborting an action frame without a P2P device vif, and guard P2P device saved IE access before using it for peer channel search.
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +4 -0
@@ -1281,6 +1281,9 @@ static s32 brcmf_p2p_abort_action_frame(struct brcmf_cfg80211_info *cfg)brcmf_dbg (TRACE ,"Enter\n" ); vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE ].vif; +if (!vif) + vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY ].vif; + err =brcmf_fil_bsscfg_data_set (vif->ifp,"actframe_abort" , &int_val,sizeof (s32));if (err)
@@ -1817,6 +1820,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_if *ifp,/* validate channel and p2p ies */ if (config_af_params.search_channel &&IS_P2P_SOCIAL_CHANNEL (le32_to_cpu (af_params->channel)) && + p2p->bss_idx[P2PAPI_BSSCFG_DEVICE ].vif && p2p->bss_idx[P2PAPI_BSSCFG_DEVICE ].vif->saved_ie.probe_req_ie_len) { afx_hdl = &p2p->afx_hdl; afx_hdl->peer_listen_chan =le32_to_cpu (af_params->channel);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1589/1887 wifi: nl80211: clean up color-change beacon data on errors
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhao Li <[email protected]>
[ Upstream commit 927ee844c47ac2aef22c8f7a35f098ff576b398b ]
nl80211_color_change() calls nl80211_parse_beacon() for the beacon_next template, which can allocate params.beacon_next.mbssid_ies and .rnr_ies. A parsing failure returned directly instead of using the out: cleanup, leaking any allocations completed before the error.
Allocate the nested attribute table before parsing beacon_next. Its allocation failure can then return before beacon data exists, while a later parsing failure uses out: to release the parsed data.
--- net/wireless/nl80211.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
net/wireless/nl80211.c +5 -5
@@ -15968,15 +15968,15 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) params.count =nla_get_u8 (info->attrs[NL80211_ATTR_COLOR_CHANGE_COUNT ]); params.color =nla_get_u8 (info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR ]); - err =nl80211_parse_beacon (rdev, info->attrs, ¶ms.beacon_next, - info->extack); -if (err) -return err; - tb =kcalloc (NL80211_ATTR_MAX +1 ,sizeof (*tb),GFP_KERNEL );if (!tb)return -ENOMEM ; + err =nl80211_parse_beacon (rdev, info->attrs, ¶ms.beacon_next, + info->extack); +if (err) + goto out; + err =nla_parse_nested (tb,NL80211_ATTR_MAX , info->attrs[NL80211_ATTR_COLOR_CHANGE_ELEMS ], nl80211_policy, info->extack);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1590/1887 arm64: ptrace: Keep orig_x0 in-sync with x0 on syscall entry
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Will Deacon <[email protected]>
[ Upstream commit 88b839ce497ccb1ff92f7ae742c78dd2937ba572 ]
Commit e057b9477232 ("arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates") attempted to resolve a long-standing issue with syscall entry tracing, where a tracer is able to manipulate the first syscall argument without being subjected to seccomp or audit checking.
Unfortunately, that fix was incomplete [1], as it failed to update 'orig_x0' between a tracer updating x0 during a seccomp ptrace exit (SECCOMP_RET_TRACE) and the seccomp filter being re-evaluated.
Rather than add hooks to the core seccomp code, instead move the synchronisation code into the ptrace GPR and syscall setting code so that 'orig_x0' is kept up to date with x0 whenever we're stopped on the syscall entry path.
--- arch/arm64/kernel/ptrace.c | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+)
arch/arm64/kernel/ptrace.c +50 -0
@@ -564,6 +564,42 @@ static int gpr_get(struct task_struct *target,return membuf_write (&to, uregs,sizeof (*uregs)); } +static void update_syscall_orig_x0_after_ptrace (struct task_struct *target) +{ +struct pt_regs *regs =task_pt_regs (target); +struct kernel_siginfo *info = target->last_siginfo; + +/* +* Skip the update for NO_SYSCALL (set either by the user or the +* tracer), as regs[0] holds the return value (see the comment in +* el0_svc_common()) and can be unwound using syscall_rollback(). +*/ +if (regs->syscallno ==NO_SYSCALL ) +return ; + +/* We should only be called when target is in a ptrace stop */ +if (WARN_ON_ONCE (!info)) +return ; + +/* +* For compat tasks, orig_r0 is provided directly through GPR index +* 17. +*/ +if (is_compat_thread (task_thread_info (target))) +return ; + +/* +* Don't update orig_x0 for a syscall-exit-stop, as x0 now contains the +* return value of the system call. +*/ +if ((info->si_code & ~0x80 ) ==SIGTRAP && + target->ptrace_message ==PTRACE_EVENTMSG_SYSCALL_EXIT ) { +return ; + } + + regs->orig_x0 = regs->regs[0 ]; +} +static int gpr_set (struct task_struct *target,const struct user_regset *regset,unsigned int pos,unsigned int count,const void *kbuf,const void __user *ubuf)
@@ -579,6 +615,14 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset,return -EINVAL ;task_pt_regs (target)->user_regs = newregs; + +/* +* Keep orig_x0 authoritative so that seccomp (via +* syscall_get_arguments()), audit and the restart path all see the same +* first argument the syscall is dispatched with, even if it has been +* updated by a tracer. +*/ +update_syscall_orig_x0_after_ptrace (target);return 0 ; }
@@ -722,6 +766,12 @@ static int system_call_set(struct task_struct *target,return ret;task_pt_regs (target)->syscallno = syscallno; + +/* +* Re-sync orig_x0 in case the syscall number has been changed +* from NO_SYSCALL. +*/ +update_syscall_orig_x0_after_ptrace (target);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1591/1887 bpf, x86: Fix trampoline stack size for 128-bit arguments
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yonghong Song <[email protected]>
[ Upstream commit 814cba835ef648e0c5eb79505c96c0493b29eea6 ]
btf_distill_func_proto() accepts a function argument up to 16 bytes, so a 128-bit scalar such as __int128 reaches the x86 trampoline with arg_size == 16. But the current implementation assumes an __int128 argument only needs one register, so the register save area is under-allocated and save_args() overwrites adjacent stack slots.
Compute the register count from arg_size for all arguments to fix it.
--- arch/x86/net/bpf_jit_comp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
arch/x86/net/bpf_jit_comp.c +1 -3
@@ -2131,10 +2131,8 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *iu8 *prog;bool save_ret; -/* extra registers for struct arguments */ for (i =0 ; i < m->nr_args; i++) -if (m->arg_flags[i] &BTF_FMODEL_STRUCT_ARG ) - nr_regs += (m->arg_size[i] +7 ) /8 -1 ; + nr_regs += (m->arg_size[i] +7 ) /8 -1 ;/* x86-64 supports up to 6 arguments. 7+ can be added in the future */ if (nr_regs >6 )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1592/1887 wifi: mt76: cancel reset and rc work on device unregister
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Fietkau <[email protected]>
[ Upstream commit e995d3dccc9d980af13a60ad37409ed1561f9eeb ]
Both drivers cancelled dump_work on unregister but left reset_work and rc_work to be flushed only by destroy_workqueue() in mt76_free_device(), which runs after the hw is unregistered and the hardware stopped. A reset_work that fires in that window calls ieee80211_restart_hw() and re-arms mac_work on an unregistered hw, and rc_work touches station state being torn down. Cancel both up front, alongside dump_work.
--- drivers/net/wireless/mediatek/mt76/mt7915/init.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7915/init.c +2 -0
@@ -1152,6 +1152,8 @@ int mt7915_register_device(struct mt7915_dev *dev)void mt7915_unregister_device (struct mt7915_dev *dev) {cancel_work_sync (&dev->dump_work); +cancel_work_sync (&dev->reset_work); +cancel_work_sync (&dev->rc_work);mt7915_unregister_ext_phy (dev);mt7915_coredump_unregister (dev);mt7915_unregister_thermal (&dev->phy);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1593/1887 wifi: mt76: mt7915: allocate vif wcid in the same range as stations
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Fietkau <[email protected]>
[ Upstream commit f3049b88b2b32326df97461813ae73e8bbc296fc ]
Reduces the amount of unnecessary WTBL bank switching, while still reserving WTBL entries for vifs.
Stable-dep-of: 2fb6480c52f6 ("wifi: mt76: mt7915: unwind state on add_interface failure")
--- drivers/net/wireless/mediatek/mt76/mt7915/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/main.c +4 -1
@@ -236,7 +236,9 @@ static int mt7915_add_interface(struct ieee80211_hw *hw, dev->mt76.vif_mask |=BIT_ULL (mvif->mt76.idx); phy->omac_mask |=BIT_ULL (mvif->mt76.omac_idx); - idx =MT7915_WTBL_RESERVED - mvif->mt76.idx; + idx =mt76_wcid_alloc (dev->mt76.wcid_mask,mt7915_wtbl_size (dev)); +if (idx <0 ) +return -ENOSPC ;INIT_LIST_HEAD (&mvif->sta.rc_list);INIT_LIST_HEAD (&mvif->sta.poll_list);
@@ -283,6 +285,7 @@ static void mt7915_remove_interface(struct ieee80211_hw *hw,mt7915_mcu_add_bss_info (phy, vif, false);mt7915_mcu_add_sta (dev, vif,NULL , false); +mt76_wcid_mask_clear (dev->mt76.wcid_mask, mvif->sta.wcid.idx);mutex_lock (&dev->mt76.mutex);mt76_testmode_reset (phy->mt76, true);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1594/1887 wifi: mt76: mt7915: Fix an error handling path in mt7915_add_interface()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christophe JAILLET <[email protected]>
[ Upstream commit 126a516fe30639708e759678bcb10178938cc718 ]
If mt76_wcid_alloc() fails, the "mt76.mutex" mutex needs to be released as done in the other error handling paths of mt7915_add_interface().
Stable-dep-of: 2fb6480c52f6 ("wifi: mt76: mt7915: unwind state on add_interface failure")
--- drivers/net/wireless/mediatek/mt76/mt7915/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7915/main.c +4 -2
@@ -237,8 +237,10 @@ static int mt7915_add_interface(struct ieee80211_hw *hw, phy->omac_mask |=BIT_ULL (mvif->mt76.omac_idx); idx =mt76_wcid_alloc (dev->mt76.wcid_mask,mt7915_wtbl_size (dev)); -if (idx <0 ) -return -ENOSPC ; +if (idx <0 ) { + ret = -ENOSPC ; + goto out; + }INIT_LIST_HEAD (&mvif->sta.rc_list);INIT_LIST_HEAD (&mvif->sta.poll_list);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1595/1887 wifi: mt76: mt7915: unwind state on add_interface failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Fietkau <[email protected]>
[ Upstream commit 2fb6480c52f611338e1b0abe5e6219be1fc9ab75 ]
When mt76_wcid_alloc() fails, mt7915_add_interface() returned without clearing the vif_mask/omac_mask bits it had already set, without removing the firmware dev info added earlier, and without clearing a monitor_vif pointer to the vif mac80211 is about to free. mac80211 does not call remove_interface() for a failed add, so the indices and firmware dev entry leaked permanently and testmode could dereference the stale monitor_vif. Add a proper error unwind.
--- drivers/net/wireless/mediatek/mt76/mt7915/main.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7915/main.c +11 -1
@@ -239,7 +239,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw, idx =mt76_wcid_alloc (dev->mt76.wcid_mask,mt7915_wtbl_size (dev));if (idx <0 ) { ret = -ENOSPC ; - goto out; + goto err; }INIT_LIST_HEAD (&mvif->sta.rc_list);
@@ -270,7 +270,17 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,mt7915_mcu_add_sta (dev, vif,NULL , true);rcu_assign_pointer (dev->mt76.wcid[idx], &mvif->sta.wcid); +mutex_unlock (&dev->mt76.mutex); + +return 0 ; + +err: + dev->mt76.vif_mask &= ~BIT_ULL (mvif->mt76.idx); + phy->omac_mask &= ~BIT_ULL (mvif->mt76.omac_idx); +mt7915_mcu_add_dev_info (phy, vif, false); out: +if (phy->monitor_vif == vif) + phy->monitor_vif =NULL ;mutex_unlock (&dev->mt76.mutex);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1596/1887 wifi: mt76: fix non-AQL packet accounting for MLO stations
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael-CY Lee <[email protected]>
[ Upstream commit 8ae659743ba936b22ecb4620815887728e2820d6 ]
__mt76_tx_queue_skb() overrides the wcid passed by the driver with sta->drv_priv, so the wcid might incorrectly be changed after TX, causing wcid->non_aql_packets to be counted on the wrong wcid. For example, on the AP side, if a station's setup link is the 5G link and the station uses 2G to transmit a frame, the value of non_aql_packets is increased on the 5G wcid but decreased on the 2G wcid. Once the inflated counter exceeds MT_MAX_NON_AQL_PKT, the TX scheduler permanently refuses to service the station.
Drop the reassignment and account on the wcid used for transmission. This also records the actual wcid in the queue entry.
--- drivers/net/wireless/mediatek/mt76/tx.c | 1 - 1 file changed, 1 deletion(-)
drivers/net/wireless/mediatek/mt76/tx.c +0 -1
@@ -298,7 +298,6 @@ __mt76_tx_queue_skb(struct mt76_phy *phy, int qid, struct sk_buff *skb,if (idx <0 || !sta)return idx; - wcid = (struct mt76_wcid *)sta->drv_priv; q->entry[idx].wcid = wcid->idx;if (!non_aql)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1597/1887 misc: sgi-gru: remove interrupt-context page-table walks
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Usama Anjum <[email protected]>
[ Upstream commit 928a8e9f523df845fc496bcb9811013b67aabec5 ]
The GRU TLB miss handler walks a process's page tables without holding page-table locks or a reference to the mapped page. It also uses a kernel page-table accessor on user page tables and supports only PMD-level large mappings on x86-64.
Remove the direct walker. Send interrupt faults directly to user polling mode so the existing call-OS fallback retries them in process context.
Remove the mmap-lock failure statistic that can no longer be incremented.
--- drivers/misc/sgi-gru/grufault.c | 101 ++++--------------------------- drivers/misc/sgi-gru/gruprocfs.c | 1 - drivers/misc/sgi-gru/grutables.h | 1 - 3 files changed, 12 insertions(+), 91 deletions(-)
drivers/misc/sgi-gru/grufault.c +12 -89
@@ -166,13 +166,8 @@ static void get_clear_fault_map(struct gru_state *gru, }/* -* Atomic (interrupt context) & non-atomic (user context) functions to -* convert a vaddr into a physical address. The size of the page -* is returned in pageshift. -* returns: -* 0 - successful -* < 0 - error code -* 1 - (atomic only) try again in non-atomic context +* Convert a user virtual address to a physical address in process context. +* The size of the page is returned in pageshift. */ static int non_atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr, int write,
@@ -192,87 +187,25 @@ static int non_atomic_pte_lookup(struct vm_area_struct *vma,return 0 ; } -/* -* atomic_pte_lookup -* -* Convert a user virtual address to a physical address -* Only supports Intel large pages (2MB only) on x86_64. -* ZZZ - hugepage support is incomplete -* -* NOTE: mmap_lock is already held on entry to this function. This -* guarantees existence of the page tables. -*/ -static int atomic_pte_lookup (struct vm_area_struct *vma,unsigned long vaddr, -int write,unsigned long *paddr,int *pageshift) -{ -pgd_t *pgdp; -p4d_t *p4dp; -pud_t *pudp; -pmd_t *pmdp; -pte_t pte; - - pgdp =pgd_offset (vma->vm_mm, vaddr); -if (unlikely (pgd_none (*pgdp))) - goto err; - - p4dp =p4d_offset (pgdp, vaddr); -if (unlikely (p4d_none (*p4dp))) - goto err; - - pudp =pud_offset (p4dp, vaddr); -if (unlikely (pud_none (*pudp))) - goto err; - - pmdp =pmd_offset (pudp, vaddr); -if (unlikely (pmd_none (*pmdp))) - goto err; -#ifdef CONFIG_X86_64 -if (unlikely (pmd_large (*pmdp))) - pte = *(pte_t *) pmdp; - else -#endif - pte = *pte_offset_kernel (pmdp, vaddr); - -if (unlikely (!pte_present (pte) || - (write && (!pte_write (pte) || !pte_dirty (pte))))) -return 1 ; - - *paddr =pte_pfn (pte) <<PAGE_SHIFT ; -#ifdef CONFIG_HUGETLB_PAGE - *pageshift =is_vm_hugetlb_page (vma) ?HPAGE_SHIFT :PAGE_SHIFT ; -#else - *pageshift =PAGE_SHIFT ; -#endif -return 0 ; - -err: -return 1 ; -} -static int gru_vtop (struct gru_thread_state *gts,unsigned long vaddr,int write,int atomic,unsigned long *gpa,int *pageshift) {struct mm_struct *mm = gts->ts_mm;struct vm_area_struct *vma;unsigned long paddr; -int ret, ps; +int ps; vma =find_vma (mm, vaddr);if (!vma) goto inval; -/* -* Atomic lookup is faster & usually works even if called in non-atomic -* context. -*/ -rmb ();/* Must/check ms_range_active before loading PTEs */ - ret =atomic_pte_lookup (vma, vaddr, write, &paddr, &ps); -if (ret) { -if (atomic) - goto upm; -if (non_atomic_pte_lookup (vma, vaddr, write, &paddr, &ps)) - goto inval; - } +if (atomic) + goto upm; + +/* Order the caller's ms_range_active check before loading PTEs. */ +rmb (); +if (non_atomic_pte_lookup (vma, vaddr, write, &paddr, &ps)) + goto inval;if (is_gru_paddr (paddr)) goto inval; paddr = paddr & ~((1UL << ps) -1 );
@@ -569,19 +502,9 @@ static irqreturn_t gru_intr(int chiplet, int blade)continue ; } -/* -* This is running in interrupt context. Trylock the mmap_lock. -* If it fails, retry the fault in user context. -*/ +/* Address translation may sleep, so retry the fault in user context. */ gts->ustats.fmm_tlbmiss++; -if (!gts->ts_force_cch_reload && -mmap_read_trylock (gts->ts_mm)) { -gru_try_dropin (gru, gts, tfh,NULL ); -mmap_read_unlock (gts->ts_mm); - }else { -tfh_user_polling_mode (tfh); -STAT (intr_mm_lock_failed); - } +tfh_user_polling_mode (tfh); }return IRQ_HANDLED ; }
drivers/misc/sgi-gru/gruprocfs.c +0 -1
@@ -54,7 +54,6 @@ static int statistics_show(struct seq_file *s, void *p)printstat (s, intr_cbr);printstat (s, intr_tfh);printstat (s, intr_spurious); -printstat (s, intr_mm_lock_failed);printstat (s, call_os);printstat (s, call_os_wait_queue);printstat (s, user_flush_tlb);
drivers/misc/sgi-gru/grutables.h +0 -1
@@ -182,7 +182,6 @@ struct gru_stats_s {atomic_long_t intr_cbr;atomic_long_t intr_tfh;atomic_long_t intr_spurious; -atomic_long_t intr_mm_lock_failed;atomic_long_t call_os;atomic_long_t call_os_wait_queue;atomic_long_t user_flush_tlb;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1598/1887 md/raid1: create serial pool adding rdev to array with serialize_policy=1
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Martin Wilck <[email protected]>
[ Upstream commit 140234b2380ffb8ffb0cfc46fee0e822f43adef7 ]
The following bug has been observed with kernel 7.1.3 after adding a new rdev to an existing RAID1 array with serialize_policy enabled:
Oops: 0002 [#1] CPU: 0 UID: 0 PID: 19639 Comm: ext4lazyinit Not tainted 7.1.3-1-default RIP: _raw_spin_lock_irqsave+0x27/0x50 CR2: 0000000000004960 Call Trace: wait_for_serialization+0xb9/0x260 [raid1] raid1_make_request+0x762/0xaff [raid1] md_handle_request+0x1c9/0x2e0 [md_mod]
The raid1.c code calls wait_for_serialization() if the MD_SERIALIZE_POLICY is set, and wait_for_serialization assumes that rdev->serial is initialized. Normally this will be the case for arrays that have the serialize_policy sysfs attribute set to 1.
But when a new rdev is added to an existing array in bind_rdev_to_array(), the condition at mddev_create_serial_pool() causes creation of rdev->serial to be skipped. Fix it.
--- drivers/md/md.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/md/md.c +2 -1
@@ -213,7 +213,8 @@ void mddev_create_serial_pool(struct mddev *mddev, struct md_rdev *rdev, {int ret =0 ; -if (rdev && !rdev_need_serial (rdev) && +if (!mddev->serialize_policy && + rdev && !rdev_need_serial (rdev) && !test_bit (CollisionCheck, &rdev->flags))return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1599/1887 x86/ftrace: Remove SYSTEM_BOOTING exceptions
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Zijlstra <[email protected]>
[ Upstream commit eb7d389d5b2b3c453332abc41c3eea73290cc006 ]
Now that text_poke is available before ftrace, remove the SYSTEM_BOOTING exceptions.
Specifically, this cures a W+X case during boot.
Stable-dep-of: 863f3ddd0b8a ("bpf: Fix potential UAF when reading bpf link info")
--- arch/x86/kernel/alternative.c | 10 ---------- arch/x86/kernel/ftrace.c | 3 +-- 2 files changed, 1 insertion(+), 12 deletions(-)
arch/x86/kernel/alternative.c +0 -10
@@ -1976,11 +1976,6 @@ void __ref text_poke_queue(void *addr, const void *opcode, size_t len, const voi {struct text_poke_loc *tp; -if (unlikely (system_state ==SYSTEM_BOOTING )) { -text_poke_early (addr, opcode, len); -return ; - } -text_poke_flush (addr); tp = &tp_vec[tp_vec_nr++];
@@ -2002,11 +1997,6 @@ void __ref text_poke_bp(void *addr, const void *opcode, size_t len, const void * {struct text_poke_loc tp; -if (unlikely (system_state ==SYSTEM_BOOTING )) { -text_poke_early (addr, opcode, len); -return ; - } -text_poke_loc_init (&tp, addr, opcode, len, emulate);text_poke_bp_batch (&tp,1 ); }
arch/x86/kernel/ftrace.c +1 -2
@@ -415,8 +415,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)/* ALLOC_TRAMP flags lets us know we created it */ ops->flags |=FTRACE_OPS_FL_ALLOC_TRAMP ; -if (likely (system_state !=SYSTEM_BOOTING )) -set_memory_ro ((unsigned long )trampoline, npages); +set_memory_ro ((unsigned long )trampoline, npages);set_memory_x ((unsigned long )trampoline, npages);return (unsigned long )trampoline; fail:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1600/1887 bpf: Retire the struct_ops map kvalue->refcnt.
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kui-Feng Lee <[email protected]>
[ Upstream commit b671c2067a04c0668df174ff5dfdb573d1f9b074 ]
We have replaced kvalue-refcnt with synchronize_rcu() to wait for an RCU grace period.
Maintenance of kvalue->refcnt was a complicated task, as we had to simultaneously keep track of two reference counts: one for the reference count of bpf_map. When the kvalue->refcnt reaches zero, we also have to reduce the reference count on bpf_map - yet these steps are not performed in an atomic manner and require us to be vigilant when managing them. By eliminating kvalue->refcnt, we can make our maintenance more straightforward as the refcount of bpf_map is now solely managed!
To prevent the trampoline image of a struct_ops from being released while it is still in use, we wait for an RCU grace period. The setsockopt(TCP_CONGESTION, "...") command allows you to change your socket's congestion control algorithm and can result in releasing the old struct_ops implementation. It is fine. However, this function is exposed through bpf_setsockopt(), it may be accessed by BPF programs as well. To ensure that the trampoline image belonging to struct_op can be safely called while its method is in use, the trampoline safeguarde the BPF program with rcu_read_lock(). Doing so prevents any destruction of the associated images before returning from a trampoline and requires us to wait for an RCU grace period.
Stable-dep-of: 863f3ddd0b8a ("bpf: Fix potential UAF when reading bpf link info")
--- include/linux/bpf.h | 1 + kernel/bpf/bpf_struct_ops.c | 77 +++++++++++++++++++++---------------- kernel/bpf/syscall.c | 6 ++- 3 files changed, 49 insertions(+), 35 deletions(-)
include/linux/bpf.h +1 -0
@@ -1813,6 +1813,7 @@ struct bpf_map *bpf_map_get_with_uref(u32 ufd);struct bpf_map *__bpf_map_get (struct fd f);void bpf_map_inc (struct bpf_map *map);void bpf_map_inc_with_uref (struct bpf_map *map); +struct bpf_map *__bpf_map_inc_not_zero (struct bpf_map *map,bool uref);struct bpf_map *__must_check bpf_map_inc_not_zero (struct bpf_map *map);void bpf_map_put_with_uref (struct bpf_map *map);void bpf_map_put (struct bpf_map *map);
kernel/bpf/bpf_struct_ops.c +44 -33
139 lines not on this page · raw message
kernel/bpf/syscall.c +4 -2
@@ -1298,8 +1298,10 @@ struct bpf_map *bpf_map_get_with_uref(u32 ufd)return map; } -/* map_idr_lock should have been held */ -static struct bpf_map *__bpf_map_inc_not_zero (struct bpf_map *map,bool uref) +/* map_idr_lock should have been held or the map should have been +* protected by rcu read lock. +*/ +struct bpf_map *__bpf_map_inc_not_zero (struct bpf_map *map,bool uref) {int refold;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1601/1887 bpf: Fix potential UAF when reading bpf link info
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pu Lehui <[email protected]>
[ Upstream commit 863f3ddd0b8ac65abfb50d3be0869268ac0e277b ]
In bpf_link_show_fdinfo and bpf_link_get_info_by_fd, link->prog is accessed without holding any locks. If the prog is concurrently replaced via bpf_link_update, the old prog can be freed, leading to a potential UAF issue.
Fix this by accessing link->prog under RCU protection to safely fetch the pointer and guarantee its lifetime while reading its fields.
--- kernel/bpf/syscall.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-)
kernel/bpf/syscall.c +24 -8
@@ -2851,9 +2851,10 @@ static const char *bpf_link_type_strs[] = {static void bpf_link_show_fdinfo (struct seq_file *m,struct file *filp) {const struct bpf_link *link = filp->private_data; -const struct bpf_prog *prog = link->prog; +const struct bpf_prog *prog;enum bpf_link_type type = link->type;char prog_tag[sizeof (prog->tag) *2 +1 ] = { }; +u32 prog_id =0 ;if (type <ARRAY_SIZE (bpf_link_type_strs) && bpf_link_type_strs[type]) {seq_printf (m,"link_type:\t%s\n" , bpf_link_type_strs[type]);
@@ -2863,12 +2864,21 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp) }seq_printf (m,"link_id:\t%u\n" , link->id); -bin2hex (prog_tag, prog->tag,sizeof (prog->tag)); -seq_printf (m, -"prog_tag:\t%s\n" -"prog_id:\t%u\n" , - prog_tag, - prog->aux->id); +rcu_read_lock (); + prog =READ_ONCE (link->prog); +if (prog) { +bin2hex (prog_tag, prog->tag,sizeof (prog->tag)); + prog_id = prog->aux->id; + } +rcu_read_unlock (); + +if (prog) { +seq_printf (m, +"prog_tag:\t%s\n" +"prog_id:\t%u\n" , + prog_tag, + prog_id); + }if (link->ops->show_fdinfo) link->ops->show_fdinfo (link, m); }
@@ -4355,6 +4365,7 @@ static int bpf_link_get_info_by_fd(struct file *file, {struct bpf_link_info __user *uinfo =u64_to_user_ptr (attr->info.info);struct bpf_link_info info; +const struct bpf_prog *prog;u32 info_len = attr->info.info_len;int err;
@@ -4369,7 +4380,12 @@ static int bpf_link_get_info_by_fd(struct file *file, info.type = link->type; info.id = link->id; - info.prog_id = link->prog->aux->id; + +rcu_read_lock (); + prog =READ_ONCE (link->prog); +if (prog) + info.prog_id = prog->aux->id; +rcu_read_unlock ();if (link->ops->fill_link_info) { err = link->ops->fill_link_info (link, &info);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1602/1887 wifi: ath11k: fix stride mismatch in mac_phy_caps_parse()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Johnson <[email protected]>
[ Upstream commit 7a246c72132eb943b5844ba79dad597b47429dba ]
Currently, in ath11k_wmi_tlv_mac_phy_caps_parse(), kcalloc() sizes the mac_phy_caps buffer as tot_phy_id * len, where len is clamped to min(firmware_len, sizeof(struct wmi_mac_phy_capabilities)). The subsequent memcpy() destination advances by sizeof(full struct) per slot via C pointer arithmetic, not by the clamped len. When firmware sends short TLVs, the second and later slots are written past the end of the allocation.
The reader in ath11k_pull_mac_phy_cap_svc_ready_ext() also indexes the buffer with full-struct pointer arithmetic, so the allocation must match that stride.
Fix by using kzalloc_objs(), which derives the element size from the pointer type, making allocation size and pointer stride provably consistent regardless of what len the firmware provides.
Compile tested only.
--- drivers/net/wireless/ath/ath11k/wmi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/net/wireless/ath/ath11k/wmi.c +5 -3
@@ -4513,14 +4513,16 @@ static int ath11k_wmi_tlv_mac_phy_caps_parse(struct ath11k_base *soc,if (svc_rdy_ext->n_mac_phy_caps >= svc_rdy_ext->tot_phy_id)return -ENOBUFS ; - len =min_t (u16, len,sizeof (struct wmi_mac_phy_capabilities ));if (!svc_rdy_ext->n_mac_phy_caps) { - svc_rdy_ext->mac_phy_caps =kcalloc (svc_rdy_ext->tot_phy_id, - len,GFP_ATOMIC ); + svc_rdy_ext->mac_phy_caps = +kcalloc (svc_rdy_ext->tot_phy_id, +sizeof (*svc_rdy_ext->mac_phy_caps), +GFP_ATOMIC );if (!svc_rdy_ext->mac_phy_caps)return -ENOMEM ; } + len =min_t (u16, len,sizeof (struct wmi_mac_phy_capabilities ));memcpy (svc_rdy_ext->mac_phy_caps + svc_rdy_ext->n_mac_phy_caps, ptr, len); svc_rdy_ext->n_mac_phy_caps++;return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1603/1887 wifi: ath11k: fix overreads in ath11k_wmi_process_csa_switch_count_event()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Johnson <[email protected]>
[ Upstream commit 208d7fdb85976a737a715b81d54efaff6703880c ]
There is no policy entry for WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT, so the parse infrastructure does not enforce a minimum length for the event struct. Additionally, the num_vdevs field is taken directly from firmware and used as a loop bound over the vdev_ids array without checking that it fits within the TLV payload. Either condition can cause an out-of-bounds read.
Add a TLV policy entry for WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT so the parse infrastructure enforces a minimum length for the fixed-size event struct. Add a helper ath11k_wmi_tlv_data_len() to recover the payload length of a parsed TLV from the header preceding its data pointer. Use it in ath11k_wmi_process_csa_switch_count_event() to bound num_vdevs before the loop.
Compile tested only.
--- drivers/net/wireless/ath/ath11k/wmi.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
drivers/net/wireless/ath/ath11k/wmi.c +19 -2
@@ -145,6 +145,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = { .min_len = sizeof(struct wmi_per_chain_rssi_stats) }, [WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT] = { .min_len = sizeof(struct wmi_twt_add_dialog_event) }, + [WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT] = { + .min_len = sizeof(struct wmi_pdev_csa_switch_ev) }, };#define PRIMAP (_hw_mode_) \
@@ -249,6 +251,13 @@ ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr,return tb; } +static u32 ath11k_wmi_tlv_data_len (const void *data) +{ +const struct wmi_tlv *tlv = (const struct wmi_tlv *)data -1 ; + +return FIELD_GET (WMI_TLV_LEN , tlv->header); +} +static int ath11k_wmi_cmd_send_nowait (struct ath11k_pdev_wmi *wmi,struct sk_buff *skb,u32 cmd_id) {
@@ -7656,15 +7665,23 @@ ath11k_wmi_process_csa_switch_count_event(struct ath11k_base *ab,const struct wmi_pdev_csa_switch_ev *ev, const u32 *vdev_ids) { - int i; + u32 vdev_ids_len = ath11k_wmi_tlv_data_len(vdev_ids); +u32 num_vdevs = ev->num_vdevs;struct ath11k_vif *arvif; +int i;/* Finish CSA once the switch count becomes NULL */ if (ev->current_switch_count)return ; +if (num_vdevs > vdev_ids_len /sizeof (*vdev_ids)) { +ath11k_warn (ab,"csa switch count num_vdevs %u exceeds tlv array length %u\n" , + num_vdevs, vdev_ids_len); +return ; + } +rcu_read_lock (); -for (i =0 ; i < ev->num_vdevs; i++) { +for (i =0 ; i < num_vdevs; i++) { arvif =ath11k_mac_get_arvif_by_vdev_id (ab, vdev_ids[i]);if (!arvif) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1604/1887 spi: davinci: switch to managed controller allocation
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
[ Upstream commit ea408a05dc8f18b4a184b88d6e19d2fd1acc1527 ]
The controller is allocated with the non-managed spi_alloc_host() while the interrupt is registered with devm_request_threaded_irq(). During removal, spi_bitbang_stop() only unregisters the controller; the subsequent spi_controller_put() then frees the controller together with its embedded davinci_spi devdata, which is the IRQ handler's dev_id. The devm_request_threaded_irq() release action (free_irq()), which drains the handler, does not run until after .remove() returns. A late or latched interrupt can therefore reach davinci_spi_irq() and dereference already-freed memory.
Switch to devm_spi_alloc_host() so that the devres LIFO order releases the controller only after free_irq() has drained the handler, and drop the now-redundant spi_controller_put() from .remove(). The probe error path is simplified to direct returns.
The clock is acquired with devm_clk_get_enabled(), which is registered after the IRQ and thus released before it by the devres LIFO order. Drain the interrupt explicitly with devm_free_irq() before disabling the controller so that a late interrupt cannot access the registers of a clock-gated controller.
This issue was found by an in-house static analysis tool.
--- drivers/spi/spi-davinci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/spi/spi-davinci.c +3 -2
@@ -863,7 +863,7 @@ static int davinci_spi_probe(struct platform_device *pdev)int ret =0 ;u32 spipc0; - master =spi_alloc_master (&pdev->dev,sizeof (struct davinci_spi )); + master =devm_spi_alloc_master (&pdev->dev,sizeof (struct davinci_spi ));if (master ==NULL ) { ret = -ENOMEM ; goto err;
@@ -1004,7 +1004,6 @@ static int davinci_spi_probe(struct platform_device *pdev) free_clk:clk_disable_unprepare (dspi->clk); free_master: -spi_master_put (master); err:return ret; }
@@ -1028,6 +1027,8 @@ static int davinci_spi_remove(struct platform_device *pdev)spi_bitbang_stop (&dspi->bitbang); +devm_free_irq (&pdev->dev, dspi->irq, dspi); +clk_disable_unprepare (dspi->clk);if (dspi->dma_rx) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1605/1887 RDMA/cxgb4: Fix dereg_skb leak and double free in write_tpt_entry()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Leon Romanovsky <[email protected]>
[ Upstream commit 373f3716a2de7adc739269ebb4d87e5bf4dc180c ]
When the device is in the fatal error state, write_tpt_entry() returns -EIO before handing the caller's preallocated skb to the transmit path; its allocation-failure returns do the same. c4iw_dereg_mr() ignores the error and frees mhp, leaking mhp->dereg_skb. c4iw_get_dma_mr() instead frees the skb a second time after dereg_mem() already consumed it, a double free.
Make write_tpt_entry() the sole owner of a non-NULL skb, freeing it on every return preceding handoff to c4iw_ofld_send(): fatal error, tpt and stag allocation failure. c4iw_ofld_send() consumes the skb on success and error alike, so drop the redundant kfree_skb() in c4iw_get_dma_mr() after dereg_mem().
--- drivers/infiniband/hw/cxgb4/mem.c | 46 ++++++++++++------------------- 1 file changed, 17 insertions(+), 29 deletions(-)
drivers/infiniband/hw/cxgb4/mem.c +17 -29
@@ -199,7 +199,8 @@ static int _c4iw_write_mem_dma(struct c4iw_rdev *rdev, u32 addr, u32 len, daddr =dma_map_single (&rdev->lldi.pdev->dev, data, len,DMA_TO_DEVICE );if (dma_mapping_error (&rdev->lldi.pdev->dev, daddr)) -return -1 ; +return _c4iw_write_mem_inline (rdev, addr, len, data, skb, + wr_waitp); save = daddr;while (remain > inline_threshold) {
@@ -235,30 +236,12 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len, void *data, struct sk_buff *skb, struct c4iw_wr_wait *wr_waitp) { - int ret; - -if (!rdev->lldi.ulptx_memwrite_dsgl || !use_dsgl) { - ret =_c4iw_write_mem_inline (rdev, addr, len, data, skb, - wr_waitp); - goto out; - } - -if (len <= inline_threshold) { - ret =_c4iw_write_mem_inline (rdev, addr, len, data, skb, + if (!rdev->lldi.ulptx_memwrite_dsgl || !use_dsgl || + len <= inline_threshold) + return _c4iw_write_mem_inline(rdev, addr, len, data, skb, wr_waitp); - goto out; - } - - ret =_c4iw_write_mem_dma (rdev, addr, len, data, skb, wr_waitp); -if (ret) { -pr_warn_ratelimited ("%s: dma map failure (non fatal)\n" , -pci_name (rdev->lldi.pdev)); - ret =_c4iw_write_mem_inline (rdev, addr, len, data, skb, - wr_waitp); - } -out: -return ret; + return _c4iw_write_mem_dma(rdev, addr, len, data, skb, wr_waitp); }/*
@@ -280,12 +263,16 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry,bool stag_idx_allocated = false;static atomic_t key; -if (c4iw_fatal_error (rdev)) +if (c4iw_fatal_error (rdev)) { +kfree_skb (skb);return -EIO ; + } tpt =kmalloc (sizeof (*tpt),GFP_KERNEL ); -if (!tpt) +if (!tpt) { +kfree_skb (skb);return -ENOMEM ; + } stag_state = stag_state >0 ; stag_idx = (*stag) >>8 ;
@@ -297,6 +284,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, rdev->stats.stag.fail++;mutex_unlock (&rdev->stats.lock);kfree (tpt); +kfree_skb (skb);return -ENOMEM ; }mutex_lock (&rdev->stats.lock);
@@ -471,8 +459,10 @@ struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc)FW_RI_STAG_NSMR , mhp->attr.perms, mhp->attr.mw_bind_enable,0 ,0 , ~0ULL ,0 ,0 ,0 , NULL, mhp->wr_waitp); -if (ret) - goto err_free_skb; +if (ret) { +kfree_skb (mhp->dereg_skb); + goto err_free_wr_wait; + } ret =finish_mem_reg (mhp, stag);if (ret)
@@ -481,8 +471,6 @@ struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc) err_dereg_mem:dereg_mem (&rhp->rdev, mhp->attr.stag, mhp->attr.pbl_size, mhp->attr.pbl_addr, mhp->dereg_skb, mhp->wr_waitp); -err_free_skb: -kfree_skb (mhp->dereg_skb); err_free_wr_wait:c4iw_put_wr_wait (mhp->wr_waitp); err_free_mhp:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1606/1887 nilfs2: fix BUG in nilfs_copy_dirty_pages() on dirty state mismatch
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ryusuke Konishi <[email protected]>
[ Upstream commit 66f4ad3ce158902e5f98afea93189972ed8750c2 ]
Syzbot reported a kernel BUG triggered within nilfs_copy_dirty_pages(), which copies dirty DAT file folios/pages to its shadow page cache. The BUG occurs when a retrieved dirty folio/page unexpectedly loses its 'dirty' status.
This issue arises because, since the commit referenced below, the 'dirty' flag of a folio/page can be cleared asynchronously after the filesystem detects metadata corruption and transitions to read-only mode.
Resolve the issue by returning an -EROFS error if the filesystem has transitioned to read-only mode. Also change the behavior to issue a kernel warning only once instead of triggering a kernel BUG when this unexpected 'dirty' state is detected while the filesystem is not in read-only mode.
--- fs/nilfs2/page.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
fs/nilfs2/page.c +15 -2
@@ -241,6 +241,7 @@ static void nilfs_copy_page(struct page *dst, struct page *src, int copy_dirty)int nilfs_copy_dirty_pages (struct address_space *dmap,struct address_space *smap) { +struct inode *smap_inode = smap->host;struct pagevec pvec;unsigned int i;pgoff_t index =0 ;
@@ -255,8 +256,19 @@ int nilfs_copy_dirty_pages(struct address_space *dmap,struct page *page = pvec.pages[i], *dpage;lock_page (page); -if (unlikely (!PageDirty (page))) -NILFS_PAGE_BUG (page,"inconsistent dirty state" ); +if (unlikely (!PageDirty (page))) { +if (WARN_ONCE (!sb_rdonly (smap_inode->i_sb), +"inconsistent dirty state\n" )) + goto unlock_src_page; + +/* +* If the filesystem has been forced to read-only +* due to metadata corruption. +*/ +unlock_page (page); + err = -EROFS ; +break ; + } dpage =grab_cache_page (dmap, page->index);if (unlikely (!dpage)) {
@@ -274,6 +286,7 @@ int nilfs_copy_dirty_pages(struct address_space *dmap,unlock_page (dpage);put_page (dpage); +unlock_src_page:unlock_page (page); }pagevec_release (&pvec);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1607/1887 PCI: Initialize temporary device in new_id_store()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Samiullah Khawaja <[email protected]>
[ Upstream commit f45a49a2380a47332817b7248c61a0ebbc6f0d00 ]
When setting new_id of a PCI device driver using sysfs a lockdep splat occurs. This is because new_id_store() builds a temporary pci_dev for pci_match_device(), which calls device_match_driver_override(). That depends on the driver_override.lock added by cb3d1049f4ea ("driver core:
generalize driver_override in struct device").
The new driver_override.lock was not initialized in the temporary pci_dev, resulting in this lockdep splat.
Initialize the temporary pci_dev to fix this.
Repro:
Build with CONFIG_LOCKDEP=y, boot with QEMU, and add a new ID:
# echo "8086 10f5" > /sys/bus/pci/drivers/e1000e/new_id
INFO: trying to register non-static key.
The code is fine but needs lockdep annotation, or maybe you didn't initialize this object before use? turning off the locking correctness validator.
CPU: 2 UID: 0 PID: 177 Comm: liveupdate-iomm Not tainted 7.0.0+ #9 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014 Call Trace: <TASK> dump_stack_lvl+0x5d/0x80 register_lock_class+0x77e/0x790 lock_acquire+0xbf/0x2e0 pci_match_device+0x24/0x180 new_id_store+0x189/0x1d0 kernfs_fop_write_iter+0x14f/0x210 vfs_write+0x263/0x5e0 ksys_write+0x79/0xf0 do_syscall_64+0x117/0xf80
Stable-dep-of: 3ffc4c9690c3 ("PCI: Fix UAF when probe runs concurrent to dyn ID removal")
--- drivers/pci/pci-driver.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/pci/pci-driver.c +8 -1
@@ -177,6 +177,11 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,return NULL ; } +static void _pci_free_device (struct device *dev) +{ +kfree (to_pci_dev (dev)); +} +/** * new_id_store - sysfs frontend to pci_add_dynid() * @driver: target device driver
@@ -212,11 +217,13 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, pdev->subsystem_vendor = subvendor; pdev->subsystem_device = subdevice; pdev->class = class; + pdev->dev.release = _pci_free_device; +device_initialize (&pdev->dev);if (pci_match_device (pdrv, pdev)) retval = -EEXIST ; -kfree (pdev); +put_device (&pdev->dev);if (retval)return retval;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1608/1887 PCI: Make pci_match_one_device() match on ID instead of device
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gary Guo <[email protected]>
[ Upstream commit c967b365d7b51c2297d9be5df39ac3e20638faf4 ]
PCI dynamic ID needs to match IDs against a new ID to see if it already exists. Existing APIs can only match IDs against devices, so the dynamic ID insertion code creates a temporary device only for matching purposes. Rename pci_match_one_device() to pci_match_one_id() so it can be used for this purpose instead; add a pci_id_from_device() helper to make it easy to convert users.
Similarly, convert pci_match_id() to do_pci_match_id(). But keep the existing API because there are many users.
Stable-dep-of: 3ffc4c9690c3 ("PCI: Fix UAF when probe runs concurrent to dyn ID removal")
--- drivers/pci/pci-driver.c | 38 +++++++++++++++++++++++++---------- drivers/pci/pci.h | 43 +++++++++++++++++++++++++++++----------- drivers/pci/search.c | 4 +++- 3 files changed, 62 insertions(+), 23 deletions(-)
drivers/pci/pci-driver.c +28 -10
@@ -90,6 +90,27 @@ static void pci_free_dynids(struct pci_driver *drv)spin_unlock (&drv->dynids.lock); } +/** +* do_pci_match_id - See if a PCI ID matches a given pci_id table +* @ids: array of PCI device ID structures to search in +* @dev_id: the PCI device ID structure to match against. +* +* Returns the matching pci_device_id structure or %NULL if there is no match. +*/ +static const struct pci_device_id * +do_pci_match_id (const struct pci_device_id *ids, +const struct pci_device_id *dev_id) +{ +if (ids) { +while (ids->vendor || ids->subvendor || ids->class_mask) { +if (pci_match_one_id (ids, dev_id)) +return ids; + ids++; + } + } +return NULL ; +} +/** * pci_match_id - See if a PCI device matches a given pci_id table * @ids: array of PCI device ID structures to search in
@@ -105,14 +126,9 @@ static void pci_free_dynids(struct pci_driver *drv)const struct pci_device_id *pci_match_id (const struct pci_device_id *ids,struct pci_dev *dev) { -if (ids) { -while (ids->vendor || ids->subvendor || ids->class_mask) { -if (pci_match_one_device (ids, dev)) -return ids; - ids++; - } - } -return NULL ; +struct pci_device_id dev_id =pci_id_from_device (dev); + +return do_pci_match_id (ids, &dev_id); }EXPORT_SYMBOL (pci_match_id);
@@ -138,15 +154,17 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv, {struct pci_dynid *dynid;const struct pci_device_id *found_id =NULL , *ids; +struct pci_device_id dev_id;/* When driver_override is set, only bind to the matching driver */ if (dev->driver_override &&strcmp (dev->driver_override, drv->name))return NULL ; + dev_id =pci_id_from_device (dev);/* Look at the dynamic ids first, before the static ones */ spin_lock (&drv->dynids.lock);list_for_each_entry (dynid, &drv->dynids.list, node) { -if (pci_match_one_device (&dynid->id, dev)) { +if (pci_match_one_id (&dynid->id, &dev_id)) { found_id = &dynid->id;break ; }
@@ -156,7 +174,7 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,if (found_id)return found_id; -for (ids = drv->id_table; (found_id =pci_match_id (ids, dev)); +for (ids = drv->id_table; (found_id =do_pci_match_id (ids, &dev_id)); ids = found_id +1 ) {/* * The match table is split based on driver_override.
drivers/pci/pci.h +31 -12
52 lines not on this page · raw message
drivers/pci/search.c +3 -1
@@ -243,8 +243,10 @@ static int match_pci_dev_by_id(struct device *dev, const void *data) {struct pci_dev *pdev =to_pci_dev (dev);const struct pci_device_id *id = data; +struct pci_device_id dev_id; -if (pci_match_one_device (id, pdev)) + dev_id =pci_id_from_device (pdev); +if (pci_match_one_id (id, &dev_id))return 1 ;return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1609/1887 PCI: Fix dyn_id add TOCTOU
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gary Guo <[email protected]>
[ Upstream commit 04fde70f782b6ce984f9f80c2023786caea28287 ]
Currently there is a TOCTOU issue in new_id_store() as the dyn ID insertion in pci_add_dynid() and the pci_match_device() are in separate critical sections.
Fix this by moving the existing ID check to inside pci_add_dynid() and only check against the static ID table outside the critical section.
Adapted to this tree: use kzalloc() instead of kzalloc_obj(), and keep the dev->driver_override check to decide whether override-only table entries are matched.
Stable-dep-of: 3ffc4c9690c3 ("PCI: Fix UAF when probe runs concurrent to dyn ID removal")
--- drivers/pci/pci-driver.c | 142 ++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 69 deletions(-)
drivers/pci/pci-driver.c +73 -69
@@ -29,6 +29,47 @@ struct pci_dynid {struct pci_device_id id; }; +/** +* do_pci_add_dynid - Add a new PCI device ID to this driver and re-probe +* @drv: target PCI driver +* @id: ID to be added +* @check_dup: whether to check if matching ID is already present +* +* Add a new dynamic PCI device ID to this driver and causes the driver to +* probe for all devices again. @drv must have been registered prior to calling +* this function. +* +* Context: Does GFP_KERNEL allocation. +* +* Return: 0 on success, -errno on failure. +*/ +static int do_pci_add_dynid (struct pci_driver *drv, +const struct pci_device_id *id, +bool check_dup) +{ +struct pci_dynid *dynid, *existing_dynid; + + dynid =kzalloc (sizeof (*dynid),GFP_KERNEL ); +if (!dynid) +return -ENOMEM ; + + dynid->id = *id; + + scoped_guard(spinlock, &drv->dynids.lock) { +if (check_dup) { +list_for_each_entry (existing_dynid, &drv->dynids.list, node) { +if (pci_match_one_id (&existing_dynid->id, id)) { +kfree (dynid); +return -EEXIST ; + } + } + } +list_add_tail (&dynid->node, &drv->dynids.list); + } + +return driver_attach (&drv->driver); +} +/** * pci_add_dynid - add a new PCI device ID to this driver and re-probe devices * @drv: target pci driver
@@ -56,25 +97,17 @@ int pci_add_dynid(struct pci_driver *drv, unsigned int class, unsigned int class_mask, unsigned long driver_data) { - struct pci_dynid *dynid; - - dynid =kzalloc (sizeof (*dynid),GFP_KERNEL ); -if (!dynid) -return -ENOMEM ; + struct pci_device_id id = { + .vendor = vendor, + .device = device, + .subvendor = subvendor, + .subdevice = subdevice, + .class = class, + .class_mask = class_mask, + .driver_data = driver_data, + }; - dynid->id.vendor = vendor; - dynid->id.device = device; - dynid->id.subvendor = subvendor; - dynid->id.subdevice = subdevice; - dynid->id.class = class; - dynid->id.class_mask = class_mask; - dynid->id.driver_data = driver_data; - -spin_lock (&drv->dynids.lock); -list_add_tail (&dynid->node, &drv->dynids.list); -spin_unlock (&drv->dynids.lock); - -return driver_attach (&drv->driver); +return do_pci_add_dynid (drv, &id, false); }EXPORT_SYMBOL_GPL (pci_add_dynid);
@@ -94,16 +127,20 @@ static void pci_free_dynids(struct pci_driver *drv)* do_pci_match_id - See if a PCI ID matches a given pci_id table * @ids: array of PCI device ID structures to search in * @dev_id: the PCI device ID structure to match against. +* @include_override_only: also match against device ID entries marked as +* override only. * * Returns the matching pci_device_id structure or %NULL if there is no match. */ static const struct pci_device_id *do_pci_match_id (const struct pci_device_id *ids, -const struct pci_device_id *dev_id) +const struct pci_device_id *dev_id, +bool include_override_only) {if (ids) {while (ids->vendor || ids->subvendor || ids->class_mask) { -if (pci_match_one_id (ids, dev_id)) +if ((!ids->override_only || include_override_only) && +pci_match_one_id (ids, dev_id))return ids; ids++; }
@@ -128,7 +165,7 @@ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, {struct pci_device_id dev_id =pci_id_from_device (dev); -return do_pci_match_id (ids, &dev_id); +return do_pci_match_id (ids, &dev_id, true); }EXPORT_SYMBOL (pci_match_id);
@@ -153,7 +190,7 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv, struct pci_dev *dev) {struct pci_dynid *dynid; -const struct pci_device_id *found_id =NULL , *ids; +const struct pci_device_id *found_id =NULL ;struct pci_device_id dev_id;/* When driver_override is set, only bind to the matching driver */
@@ -174,20 +211,9 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,if (found_id)return found_id; -for (ids = drv->id_table; (found_id =do_pci_match_id (ids, &dev_id)); - ids = found_id +1 ) { -/* -* The match table is split based on driver_override. -* In case override_only was set, enforce driver_override -* matching. -*/ -if (found_id->override_only) { -if (dev->driver_override) -return found_id; - }else { -return found_id; - } - } + found_id =do_pci_match_id (drv->id_table, &dev_id, !!dev->driver_override); +if (found_id) +return found_id;/* driver_override will always match, send a dummy id */ if (dev->driver_override)
@@ -195,11 +221,6 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,return NULL ; } -static void _pci_free_device (struct device *dev) -{ -kfree (to_pci_dev (dev)); -} -/** * new_id_store - sysfs frontend to pci_add_dynid() * @driver: target device driver
@@ -213,38 +234,22 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, {struct pci_driver *pdrv =to_pci_driver (driver);const struct pci_device_id *ids = pdrv->id_table; -u32 vendor, device, subvendor =PCI_ANY_ID , - subdevice =PCI_ANY_ID , class =0 , class_mask =0 ; -unsigned long driver_data =0 ; -int fields =0 ; +struct pci_device_id id = { + .subvendor =PCI_ANY_ID , + .subdevice =PCI_ANY_ID + }; +int fields;int retval =0 ; fields =sscanf (buf,"%x %x %x %x %x %x %lx" , - &vendor, &device, &subvendor, &subdevice, - &class, &class_mask, &driver_data); + &id.vendor, &id.device, &id.subvendor, &id.subdevice, + &id.class, &id.class_mask, &id.driver_data);if (fields <2 )return -EINVAL ;if (fields !=7 ) { -struct pci_dev *pdev =kzalloc (sizeof (*pdev),GFP_KERNEL ); -if (!pdev) -return -ENOMEM ; - - pdev->vendor = vendor; - pdev->device = device; - pdev->subsystem_vendor = subvendor; - pdev->subsystem_device = subdevice; - pdev->class = class; - pdev->dev.release = _pci_free_device; - -device_initialize (&pdev->dev); -if (pci_match_device (pdrv, pdev)) - retval = -EEXIST ; - -put_device (&pdev->dev); - -if (retval) -return retval; +if (do_pci_match_id (pdrv->id_table, &id, false)) +return -EEXIST ; }/* Only accept driver_data values that match an existing id_table
@@ -252,7 +257,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,if (ids) { retval = -EINVAL ;while (ids->vendor || ids->subvendor || ids->class_mask) { -if (driver_data == ids->driver_data) { +if (id.driver_data == ids->driver_data) { retval =0 ;break ; }
@@ -262,8 +267,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,return retval; } - retval =pci_add_dynid (pdrv, vendor, device, subvendor, subdevice, - class, class_mask, driver_data); + retval =do_pci_add_dynid (pdrv, &id, fields !=7 );if (retval)return retval;return count;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1610/1887 PCI: Fix UAF when probe runs concurrent to dyn ID removal
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gary Guo <[email protected]>
[ Upstream commit 3ffc4c9690c33ee28cdb3d0182b12f9c623e3acc ]
Dynamic IDs are only guaranteed to be valid when dynids.lock is held, as remove_id_store() can free the node. Thus, make a copy in pci_match_device(). Also, clarify that the id parameter is only valid during probe.
--- drivers/pci/pci-driver.c | 28 +++++++++++++++------------- include/linux/pci.h | 1 + 2 files changed, 16 insertions(+), 13 deletions(-)
drivers/pci/pci-driver.c +15 -13
@@ -180,6 +180,7 @@ static const struct pci_device_id pci_device_id_any = {* pci_match_device - See if a device matches a driver's list of IDs * @drv: the PCI driver to match against * @dev: the PCI device structure to match against +* @id_copy: place to store copy of pci_device_id for dynamic ID * * Used by a driver to check whether a PCI device is in its list of * supported devices or in the dynids list, which may have been augmented
@@ -187,9 +188,9 @@ static const struct pci_device_id pci_device_id_any = {* structure or %NULL if there is no match. */ static const struct pci_device_id *pci_match_device (struct pci_driver *drv, -struct pci_dev *dev) +struct pci_dev *dev, +struct pci_device_id *id_copy) { -struct pci_dynid *dynid;const struct pci_device_id *found_id =NULL ;struct pci_device_id dev_id;
@@ -199,17 +200,16 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv, dev_id =pci_id_from_device (dev);/* Look at the dynamic ids first, before the static ones */ -spin_lock (&drv->dynids.lock); -list_for_each_entry (dynid, &drv->dynids.list, node) { -if (pci_match_one_id (&dynid->id, &dev_id)) { - found_id = &dynid->id; -break ; + scoped_guard(spinlock, &drv->dynids.lock) { +struct pci_dynid *dynid; + +list_for_each_entry (dynid, &drv->dynids.list, node) { +if (pci_match_one_id (&dynid->id, &dev_id)) { + *id_copy = dynid->id; +return id_copy; + } } } -spin_unlock (&drv->dynids.lock); - -if (found_id) -return found_id; found_id =do_pci_match_id (drv->id_table, &dev_id, !!dev->driver_override);if (found_id)
@@ -436,12 +436,13 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,static int __pci_device_probe (struct pci_driver *drv,struct pci_dev *pci_dev) {const struct pci_device_id *id; +struct pci_device_id id_copy;int error =0 ;if (drv->probe) { error = -ENODEV ; - id =pci_match_device (drv, pci_dev); + id =pci_match_device (drv, pci_dev, &id_copy);if (id) error =pci_call_probe (drv, pci_dev, id); }
@@ -1546,12 +1547,13 @@ static int pci_bus_match(struct device *dev, struct device_driver *drv)struct pci_dev *pci_dev =to_pci_dev (dev);struct pci_driver *pci_drv;const struct pci_device_id *found_id; +struct pci_device_id id_copy;if (!pci_dev->match_driver)return 0 ; pci_drv =to_pci_driver (drv); - found_id =pci_match_device (pci_drv, pci_dev); + found_id =pci_match_device (pci_drv, pci_dev, &id_copy);if (found_id)return 1 ;
include/linux/pci.h +1 -0
@@ -882,6 +882,7 @@ struct module;* function returns zero when the driver chooses to * take "ownership" of the device or an error code * (negative number) otherwise. +* The pci_device_id parameter is only valid during probe. * The probe function always gets called from process * context, so it can sleep. * @remove: The remove() function gets called whenever a device
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1611/1887 spi: oc-tiny: switch to managed controller allocation
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
[ Upstream commit d710f43ce30975d197f73c543bfe47b958d8ba17 ]
The controller is allocated with the non-managed spi_alloc_host() while the interrupt is registered with devm_request_irq(). During removal, spi_bitbang_stop() only unregisters the controller; the subsequent spi_controller_put() then frees the controller together with its embedded driver-private devdata, which is the IRQ handler's dev_id. The devm_request_irq() release action (free_irq()), which drains the handler, does not run until after .remove() returns. A late or latched interrupt can therefore reach tiny_spi_irq() and dereference already-freed memory (e.g. hw->base).
Switch to devm_spi_alloc_host() so that the devres LIFO order releases the controller only after free_irq() has drained the handler, and drop the now-redundant spi_controller_put() from .remove(). The probe error path is simplified to direct returns.
This issue was found by an in-house static analysis tool.
--- drivers/spi/spi-oc-tiny.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-)
drivers/spi/spi-oc-tiny.c +8 -16
@@ -213,11 +213,11 @@ static int tiny_spi_probe(struct platform_device *pdev)struct tiny_spi_platform_data *platp =dev_get_platdata (&pdev->dev);struct tiny_spi *hw;struct spi_master *master; -int err = -ENODEV ; +int err; - master =spi_alloc_master (&pdev->dev,sizeof (struct tiny_spi )); + master =devm_spi_alloc_master (&pdev->dev,sizeof (struct tiny_spi ));if (!master) -return err; +return -ENOMEM ;/* setup the master state. */ master->bus_num = pdev->id;
@@ -235,10 +235,8 @@ static int tiny_spi_probe(struct platform_device *pdev)/* find and map our resources */ hw->base =devm_platform_ioremap_resource (pdev,0 ); -if (IS_ERR (hw->base)) { - err =PTR_ERR (hw->base); - goto exit; - } +if (IS_ERR (hw->base)) +return PTR_ERR (hw->base);/* irq is optional */ hw->irq =platform_get_irq (pdev,0 );if (hw->irq >=0 ) {
@@ -246,7 +244,7 @@ static int tiny_spi_probe(struct platform_device *pdev) err =devm_request_irq (&pdev->dev, hw->irq, tiny_spi_irq,0 , pdev->name, hw);if (err) - goto exit; +return err; }/* find platform data */ if (platp) {
@@ -255,29 +253,23 @@ static int tiny_spi_probe(struct platform_device *pdev) }else { err =tiny_spi_of_probe (pdev);if (err) - goto exit; +return err; }/* register our spi controller */ err =spi_bitbang_start (&hw->bitbang);if (err) - goto exit; +return err;dev_info (&pdev->dev,"base %p, irq %d\n" , hw->base, hw->irq);return 0 ; - -exit: -spi_master_put (master); -return err; }static int tiny_spi_remove (struct platform_device *pdev) {struct tiny_spi *hw =platform_get_drvdata (pdev); -struct spi_master *master = hw->bitbang.master;spi_bitbang_stop (&hw->bitbang); -spi_master_put (master);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1612/1887 scsi: ufs: core: Validate string descriptors
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li Qiang <[email protected]>
[ Upstream commit d96e83d028d7d8762e424e49c671d49ac2ecf14f ]
The string descriptor length includes a two-byte header while the UTF-16 payload starts after it. utf16s_to_utf8s() expects a count of UTF-16 code units, not bytes. Passing the payload byte count can make it read beyond the descriptor buffer.
Validate that the payload has an even byte count, pass a code-unit count to the converter, and allocate sufficient UTF-8 output space.
The raw string buffer starts after the descriptor header but its size is bLength. Copying bLength bytes from that pointer can read beyond the response buffer.
Allocate a zeroed bLength-sized buffer and copy only the UTF-16 payload. This preserves the raw buffer size consumed by the RPMB device-ID ABI while avoiding the overread.
--- drivers/ufs/core/ufshcd.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-)
drivers/ufs/core/ufshcd.c +19 -5
@@ -3530,7 +3530,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, {struct uc_string_id *uc_str;u8 *str; -int ret; +int ret, uc_len;if (!buf)return -EINVAL ;
@@ -3555,11 +3555,19 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, goto out; } + uc_len = uc_str->len -QUERY_DESC_HDR_SIZE ; +if (uc_len %sizeof (*uc_str->uc)) { +dev_err (hba->dev,"String Desc has an odd UTF-16 payload length\n" ); + str =NULL ; + ret = -EINVAL ; + goto out; + } +if (ascii) {ssize_t ascii_len;int i; -/* remove header and divide by 2 to move from UTF16 to UTF8 */ - ascii_len = (uc_str->len -QUERY_DESC_HDR_SIZE ) /2 +1 ; +/* Allow up to three UTF-8 bytes per UTF-16 code unit plus a NUL. */ + ascii_len = uc_len /sizeof (*uc_str->uc) *3 +1 ; str =kzalloc (ascii_len,GFP_KERNEL );if (!str) { ret = -ENOMEM ;
@@ -3571,7 +3579,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index,* we need to convert to utf-8 so it can be displayed */ ret =utf16s_to_utf8s (uc_str->uc, - uc_str->len -QUERY_DESC_HDR_SIZE , + uc_len /sizeof (*uc_str->uc),UTF16_BIG_ENDIAN , str, ascii_len -1 );/* replace non-printable or non-ASCII characters with spaces */
@@ -3581,11 +3589,17 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, str[ret++] ='\0' ; }else { - str =kmemdup (uc_str->uc, uc_str->len,GFP_KERNEL ); +/* +* Keep the bLength-sized raw output for the RPMB device ID ABI. +* The two bytes beyond the UTF-16 payload are explicitly zeroed +* instead of being read past the descriptor buffer. +*/ + str =kzalloc (uc_str->len,GFP_KERNEL );if (!str) { ret = -ENOMEM ; goto out; } +memcpy (str, uc_str->uc, uc_len); ret = uc_str->len; } out:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1613/1887 pinctrl: mediatek: use devm_gpiochip_add_data() for GPIO chip
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Justin Yeh <[email protected]>
[ Upstream commit 9c650317ba553d297f3fc5f0ae40ec53d86f9dab ]
The gpio_chip is allocated with device-managed memory but registered with the non-managed gpiochip_add_data(). This was harmless while the drivers were built-in, but once they can be built as modules and unbound/rmmod'd, devm frees the gpio_chip's memory while it is still registered, causing a use-after-free.
Register it with devm_gpiochip_add_data() so it shares the same device-managed lifecycle, which also lets the manual gpiochip_remove() error paths go away.
--- drivers/pinctrl/mediatek/pinctrl-moore.c | 6 ++---- drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 14 ++++---------- drivers/pinctrl/mediatek/pinctrl-paris.c | 2 +- 3 files changed, 7 insertions(+), 15 deletions(-)
drivers/pinctrl/mediatek/pinctrl-moore.c +2 -4
@@ -538,7 +538,7 @@ static int mtk_build_gpiochip(struct mtk_pinctrl *hw) chip->ngpio = hw->soc->npins; chip->of_gpio_n_cells =2 ; - ret =gpiochip_add_data (chip, hw); + ret =devm_gpiochip_add_data (hw->dev, chip, hw);if (ret <0 )return ret;
@@ -552,10 +552,8 @@ static int mtk_build_gpiochip(struct mtk_pinctrl *hw)if (!of_find_property (hw->dev->of_node,"gpio-ranges" ,NULL )) { ret =gpiochip_add_pin_range (chip,dev_name (hw->dev),0 ,0 , chip->ngpio); -if (ret <0 ) { -gpiochip_remove (chip); +if (ret <0 )return ret; - } }return 0 ;
drivers/pinctrl/mediatek/pinctrl-mtk-common.c +4 -10
@@ -1121,30 +1121,24 @@ int mtk_pctrl_init(struct platform_device *pdev, pctl->chip->parent = &pdev->dev; pctl->chip->base =-1 ; - ret =gpiochip_add_data (pctl->chip, pctl); + ret =devm_gpiochip_add_data (&pdev->dev, pctl->chip, pctl);if (ret)return -EINVAL ;/* Register the GPIO to pin mappings. */ ret =gpiochip_add_pin_range (pctl->chip,dev_name (&pdev->dev),0 ,0 , pctl->devdata->npins); -if (ret) { - ret = -EINVAL ; - goto chip_error; - } +if (ret) +return -EINVAL ;/* Only initialize EINT if we have EINT pins */ if (data->eint_hw.ap_num >0 ) { ret =mtk_eint_init (pctl, pdev);if (ret) - goto chip_error; +return ret; }return 0 ; - -chip_error: -gpiochip_remove (pctl->chip); -return ret; }int mtk_pctrl_common_probe (struct platform_device *pdev)
drivers/pinctrl/mediatek/pinctrl-paris.c +1 -1
@@ -970,7 +970,7 @@ static int mtk_build_gpiochip(struct mtk_pinctrl *hw) chip->ngpio = hw->soc->npins; chip->of_gpio_n_cells =2 ; - ret =gpiochip_add_data (chip, hw); + ret =devm_gpiochip_add_data (hw->dev, chip, hw);if (ret <0 )return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1614/1887 bpf: Fix WARNING in bpf_tracing_link_release
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Leon Hwang <[email protected]>
[ Upstream commit 61aaa8782bec59ecffd22e030f54ef9351bcabf9 ]
The trampoline could be corrupted by the blindly 'tr->flags = BPF_TRAMP_F_TAIL_CALL_CTX' in verifier.
1. A fexit attached to a tail_call_reachable prog. 'tr->flags' became 'BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_TAIL_CALL_CTX'. And, the trampoline would poke the target prog's nop insn using jmp insn instead of call insn.
2. Another fexit loaded with the same tail_call_reachable prog target. 'tr->flags' became 'BPF_TRAMP_F_TAIL_CALL_CTX'.
3. Close the first fexit link. Due to no BPF_TRAMP_F_CALL_ORIG in 'tr->flags', the trampoline will fail to restore the prog's nop insn using call insn.
[ 3.410719] WARNING: kernel/bpf/syscall.c:3551 at bpf_tracing_link_release+0x53/0x60, CPU#1: test_progs/98 ... [ 3.428793] bpf_link_free+0x58/0x130 [ 3.429293] bpf_link_release+0x23/0x30
Fix the warning by updating 'tr->flags' with '|=' and lock.
--- include/linux/bpf.h | 3 +++ kernel/bpf/trampoline.c | 7 +++++++ kernel/bpf/verifier.c | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-)
include/linux/bpf.h +3 -0
@@ -1025,6 +1025,8 @@ struct bpf_trampoline *bpf_trampoline_get(u64 key,void bpf_trampoline_put (struct bpf_trampoline *tr);int arch_prepare_bpf_dispatcher (void *image,void *buf,s64 *funcs,int num_funcs); +void bpf_trampoline_set_flags (struct bpf_trampoline *tr,u32 flags); +/* * When the architecture supports STATIC_CALL replace the bpf_dispatcher_fn * indirection with a direct call to the bpf program. If the architecture does
@@ -1132,6 +1134,7 @@ static inline bool bpf_prog_has_trampoline(const struct bpf_prog *prog) {return false; } +static inline void bpf_trampoline_set_flags (struct bpf_trampoline *tr,u32 flags) {}#endif struct bpf_func_info_aux {
kernel/bpf/trampoline.c +7 -0
@@ -418,6 +418,13 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key)return ERR_PTR (err); } +void bpf_trampoline_set_flags (struct bpf_trampoline *tr,u32 flags) +{ +mutex_lock (&tr->mutex); + tr->flags |= flags; +mutex_unlock (&tr->mutex); +} +static int bpf_trampoline_update (struct bpf_trampoline *tr,bool lock_direct_mutex) {struct bpf_tramp_image *im;
kernel/bpf/verifier.c +1 -1
@@ -15493,7 +15493,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)return -ENOMEM ;if (tgt_prog && tgt_prog->aux->tail_call_reachable) - tr->flags =BPF_TRAMP_F_TAIL_CALL_CTX ; +bpf_trampoline_set_flags (tr,BPF_TRAMP_F_TAIL_CALL_CTX ); prog->aux->dst_trampoline = tr;return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1615/1887 RDMA/erdma: complete object teardown when the destroy command fails
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Leon Romanovsky <[email protected]>
[ Upstream commit 652befcba956ef357f480525ccbe25c59bc81d4d ]
erdma_destroy_qp(), erdma_destroy_cq(), erdma_dereg_mr(), and erdma_destroy_ah() returned early when erdma_post_cmd_wait() failed, leaking the queue buffers, MTTs, doorbells and the STAG, QPN, CQN and AHN identifiers. A command timeout clears ERDMA_CMDQ_STATE_OK_BIT and permanently disables the command queue, so no retry can succeed; the RDMA core keeps the object after a failed destructor and forced uverbs cleanup then nulls the pointers, making the resources unreachable.
Warn on failure but release every software-owned resource and return success, since during terminal destruction the hardware command result is only diagnostic.
--- drivers/infiniband/hw/erdma/erdma_verbs.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
drivers/infiniband/hw/erdma/erdma_verbs.c +14 -3
@@ -971,8 +971,15 @@ int erdma_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata)FIELD_PREP (ERDMA_CMD_MR_KEY_MASK , ibmr->lkey &0xFF ); ret =erdma_post_cmd_wait (&dev->cmdq, &req,sizeof (req),NULL ,NULL ); +/* +* A timeout disables the command queue, so retry cannot succeed. Treat +* terminal command failures as diagnostic; propagating them can make +* forced uverbs cleanup discard the last software resource pointers. +*/ if (ret) -return ret; +ibdev_warn_ratelimited (&dev->ibdev, +"failed to deregister MR 0x%x: %d\n" , + ibmr->lkey, ret);erdma_free_idx (&dev->res_cb[ERDMA_RES_TYPE_STAG_IDX ], ibmr->lkey >>8 );
@@ -998,7 +1005,9 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) err =erdma_post_cmd_wait (&dev->cmdq, &req,sizeof (req),NULL ,NULL );if (err) -return err; +ibdev_warn_ratelimited (&dev->ibdev, +"failed to destroy CQ %u: %d\n" , + cq->cqn, err);xa_lock_irqsave (&dev->cq_xa, flags);__xa_erase (&dev->cq_xa, cq->cqn);
@@ -1041,7 +1050,9 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) err =erdma_post_cmd_wait (&dev->cmdq, &req,sizeof (req),NULL ,NULL );if (err) -return err; +ibdev_warn_ratelimited (&dev->ibdev, +"failed to destroy QP %u: %d\n" , +QP_ID (qp), err);xa_lock_irqsave (&dev->qp_xa, flags);__xa_erase (&dev->qp_xa,QP_ID (qp));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1616/1887 ext4: fix ABBA deadlock in ext4_xattr_inode_cache_find()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aditya Prakash Srivastava <[email protected]>
[ Upstream commit 03438084a7b8621fb5c762dd3d04cff5f2630fb2 ]
Syzbot/stress-ng reported an ABBA deadlock in ext4 when exercising concurrent xattr workloads (using the ea_inode mount/format option).
The deadlock occurs between the running transaction and the eviction thread:
- Task 1 (stress-ng): Holds a reference to a shared mbcache_entry (ce) and calls ext4_xattr_inode_cache_find() -> ext4_iget() to retrieve the corresponding EA inode. Since the EA inode is currently being evicted, ext4_iget() blocks in __wait_on_freeing_inode() waiting for eviction to complete.
- Task 2 (eviction thread): Currently evicting the same EA inode in ext4_evict_ea_inode(). It calls mb_cache_entry_wait_unused(oe) which blocks waiting for Task 1 to release the reference to the mbcache_entry.
To break this deadlock, implement a new ext4_iget() configuration flag named EXT4_IGET_NOWAIT. When set, perform a non-blocking lookup of the inode via VFS's find_inode_nowait() API.
If the inode is currently being evicted (marked with I_FREEING or I_WILL_FREE) or created (I_CREATING), or if it is not present in the VFS inode cache (cache miss), simply skip it (returning -ENOENT) rather than waiting for eviction/creation to complete, breaking the ABBA cycle.
Since we return -ENOENT immediately on a cache miss, we never attempt to allocate a new inode or call iget_locked(), completely eliminating any TOCTOU race window.
If the returned inode is I_NEW, wait for its initialization to clear via wait_on_new_inode(). If initialization fails and the inode is unhashed during wait_on_new_inode() waking up (e.g., due to an I/O read error in another thread), safely drop the reference and return -ENOENT. This unhashed check is executed unconditionally on all cache-hit pathways to properly handle concurrent initialization failures.
Finally, standard validation checks (including is_bad_inode, EXT4_EA_INODE_FL, file_acl, and xattr flags) are executed as normal inside check_igot_inode() to fully guarantee VFS-layer safety.
In ext4_xattr_inode_cache_find(), invoke ext4_iget() with the new EXT4_IGET_NOWAIT flag to perform the non-blocking cache search.
--- fs/ext4/ext4.h | 3 ++- fs/ext4/inode.c | 36 +++++++++++++++++++++++++++++++++--- fs/ext4/xattr.c | 2 +- fs/inode.c | 1 + 4 files changed, 37 insertions(+), 5 deletions(-)
fs/ext4/ext4.h +2 -1
@@ -3008,7 +3008,8 @@ typedef enum {EXT4_IGET_SPECIAL =0x0001 ,/* OK to iget a system inode */ EXT4_IGET_HANDLE =0x0002 ,/* Inode # is from a handle */ EXT4_IGET_BAD =0x0004 ,/* Allow to iget a bad inode */ - EXT4_IGET_EA_INODE = 0x0008 /* Inode should contain an EA value */ +EXT4_IGET_EA_INODE =0x0008 ,/* Inode should contain an EA value */ + EXT4_IGET_NOWAIT = 0x0010 /* Non-blocking lookup (skip if freeing) */ } ext4_iget_flags; extern struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
fs/ext4/inode.c +33 -3
@@ -4837,6 +4837,21 @@ static int check_igot_inode(struct inode *inode, ext4_iget_flags flags,return -EFSCORRUPTED ; } +static int ext4_iget_match (struct inode *inode,unsigned long ino, +void *data) +{ +if (inode->i_ino != ino) +return 0 ; +spin_lock (&inode->i_lock); +if (inode->i_state & (I_FREEING |I_WILL_FREE |I_CREATING )) { +spin_unlock (&inode->i_lock); +return -1 ; + } +__iget (inode); +spin_unlock (&inode->i_lock); +return 1 ; +} +struct inode *__ext4_iget (struct super_block *sb,unsigned long ino,ext4_iget_flags flags,const char *function,unsigned int line)
@@ -4870,9 +4885,24 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,return ERR_PTR (-EFSCORRUPTED ); } - inode =iget_locked (sb, ino); -if (!inode) -return ERR_PTR (-ENOMEM ); +if (flags &EXT4_IGET_NOWAIT ) { + inode =find_inode_nowait (sb, ino, ext4_iget_match,NULL ); +if (!inode) +return ERR_PTR (-ENOENT ); + +if (inode->i_state &I_NEW ) +wait_on_inode (inode); + +if (unlikely (inode_unhashed (inode))) { +iput (inode); +return ERR_PTR (-ENOENT ); + } + }else { + inode =iget_locked (sb, ino); +if (!inode) +return ERR_PTR (-ENOMEM ); + } +if (!(inode->i_state &I_NEW )) { ret =check_igot_inode (inode, flags, function, line);if (ret) {
fs/ext4/xattr.c +1 -1
@@ -1497,7 +1497,7 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value,while (ce) { ea_inode =ext4_iget (inode->i_sb, ce->e_value, -EXT4_IGET_EA_INODE ); +EXT4_IGET_EA_INODE |EXT4_IGET_NOWAIT );if (IS_ERR (ea_inode)) goto next_entry;ext4_xattr_inode_set_class (ea_inode);
fs/inode.c +1 -0
@@ -443,6 +443,7 @@ void __iget(struct inode *inode) {atomic_inc (&inode->i_count); } +EXPORT_SYMBOL (__iget);/* * get additional reference to inode; caller must already hold one.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1617/1887 bpf: Zero queue and stack outputs on lock failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 7ac6e1ae41a09f1dd4baeeff1d028ae49ee01232 ]
Queue and stack pop/peek helpers accept an uninitialized output buffer because the verifier expects the helper to initialize it. The empty-map error path clears the buffer, but a failed lock acquisition returns -EBUSY without writing it.
Clear the output before returning -EBUSY so BPF programs cannot observe uninitialized stack contents after a failed helper call.
--- kernel/bpf/queue_stack_maps.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
kernel/bpf/queue_stack_maps.c +6 -2
@@ -103,8 +103,10 @@ static int __queue_map_get(struct bpf_map *map, void *value, bool delete)void *ptr;if (in_nmi ()) { -if (!raw_spin_trylock_irqsave (&qs->lock, flags)) +if (!raw_spin_trylock_irqsave (&qs->lock, flags)) { +memset (value,0 , qs->map.value_size);return -EBUSY ; + } }else {raw_spin_lock_irqsave (&qs->lock, flags); }
@@ -138,8 +140,10 @@ static int __stack_map_get(struct bpf_map *map, void *value, bool delete)u32 index;if (in_nmi ()) { -if (!raw_spin_trylock_irqsave (&qs->lock, flags)) +if (!raw_spin_trylock_irqsave (&qs->lock, flags)) { +memset (value,0 , qs->map.value_size);return -EBUSY ; + } }else {raw_spin_lock_irqsave (&qs->lock, flags); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1618/1887 dmaengine: dw-edma: Terminate all descriptors without callbacks
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
[ Upstream commit 99109a51efd28c9a661fbfb9469b023c517b31d1 ]
The DMA Engine client documentation says in the "Terminate APIs" section of Documentation/driver-api/dmaengine/client.rst:
"No callback functions will be called for any incomplete transfers."
dw-edma instead calls vchan_cookie_complete() when a deferred STOP reaches the interrupt handler. This schedules a callback for the active descriptor and leaves other issued or submitted descriptors queued. A late callback after dmaengine_terminate_sync() can dereference client state that has already been freed, while leftover descriptors may later restart into reused buffers or leak.
Move all issued and submitted descriptors to the terminated list whenever termination completes. For a pending STOP, do this from both the DONE and ABORT paths. Complete their cookies in order without scheduling callbacks.
A STOP can remain pending until the running transfer raises an interrupt. Make device_synchronize() wait for such a pending STOP to complete before releasing terminated descriptors. Reuse it from free_chan_resources(), then release the remaining virt-dma resources. Sleep instead of busy-polling while waiting, and warn if the existing timeout expires.
--- drivers/dma/dw-edma/dw-edma-core.c | 90 +++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 14 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +76 -14
@@ -7,6 +7,7 @@*/ #include <linux/module.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/dmaengine.h>
@@ -199,6 +200,35 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)return 1 ; } +static void dw_edma_terminate_vdesc (struct virt_dma_desc *vd) +{ +list_del (&vd->node); +dma_cookie_complete (&vd->tx); +vchan_terminate_vdesc (vd); +} + +static void dw_edma_terminate_vdesc_list (struct list_head *head) +{ +struct virt_dma_desc *vd, *_vd; + +list_for_each_entry_safe (vd, _vd, head, node) +dw_edma_terminate_vdesc (vd); +} + +/* Must be called with vc.lock held. */ +static void dw_edma_terminate_all_descs (struct dw_edma_chan *chan) +{ +/* +* This order must not be reversed. Cookies are assigned when +* descriptors are submitted, so desc_issued contains older cookies +* than desc_submitted. Completing desc_submitted first could move +* chan->vc.chan.completed_cookie backwards when desc_issued is +* terminated afterwards. +*/ +dw_edma_terminate_vdesc_list (&chan->vc.desc_issued); +dw_edma_terminate_vdesc_list (&chan->vc.desc_submitted); +} +static int dw_edma_device_config (struct dma_chan *dchan,struct dma_slave_config *config) {
@@ -256,20 +286,22 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan)struct dw_edma_chan *chan =dchan2dw_edma_chan (dchan);int err =0 ; +guard (spinlock_irqsave)(&chan->vc.lock); +if (!chan->configured) { -/* Do nothing */ +dw_edma_terminate_all_descs (chan); }else if (chan->status ==EDMA_ST_PAUSE ) { +dw_edma_terminate_all_descs (chan); chan->status =EDMA_ST_IDLE ; - chan->configured = false; }else if (chan->status ==EDMA_ST_IDLE ) { - chan->configured = false; +dw_edma_terminate_all_descs (chan); }else if (dw_edma_v0_core_ch_status (chan) ==DMA_COMPLETE ) {/* * The channel is in a false BUSY state, probably didn't * receive or lost an interrupt */ +dw_edma_terminate_all_descs (chan); chan->status =EDMA_ST_IDLE ; - chan->configured = false; }else if (chan->request >EDMA_REQ_PAUSE ) { err = -EPERM ; }else {
@@ -626,8 +658,7 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)break ;case EDMA_REQ_STOP : -list_del (&vd->node); -vchan_cookie_complete (vd); +dw_edma_terminate_all_descs (chan); chan->request =EDMA_REQ_NONE ; chan->status =EDMA_ST_IDLE ;break ;
@@ -648,7 +679,9 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)spin_lock_irqsave (&chan->vc.lock, flags); vd =vchan_next_desc (&chan->vc); -if (vd) { +if (vd && chan->request ==EDMA_REQ_STOP ) { +dw_edma_terminate_all_descs (chan); + }else if (vd) {dw_hdma_set_callback_result (vd,DMA_TRANS_ABORTED );list_del (&vd->node);vchan_cookie_complete (vd);
@@ -727,21 +760,49 @@ static int dw_edma_alloc_chan_resources(struct dma_chan *dchan)return 0 ; } -static void dw_edma_free_chan_resources (struct dma_chan *dchan) +static void dw_edma_wait_termination (struct dma_chan *dchan) { +struct dw_edma_chan *chan =dchan2dw_edma_chan (dchan);unsigned long timeout = jiffies +msecs_to_jiffies (5000 ); -int ret; +bool stopping; +/* +* A STOP may be deferred to a later interrupt while the channel is still +* running. Wait until that handler completes the termination. +*/ while (time_before (jiffies, timeout)) { - ret =dw_edma_device_terminate_all (dchan); -if (!ret) -break ; +scoped_guard (spinlock_irqsave, &chan->vc.lock) + stopping = chan->request == EDMA_REQ_STOP; -if (time_after_eq (jiffies, timeout)) +if (!stopping)return ; -cpu_relax (); +fsleep (1000 ); } + +dev_warn (chan->dw->chip->dev, +"timeout waiting for channel termination\n" ); +} + +static void dw_edma_device_synchronize (struct dma_chan *dchan) +{ +struct dw_edma_chan *chan =dchan2dw_edma_chan (dchan); + +dw_edma_wait_termination (dchan); +vchan_synchronize (&chan->vc); +} + +static void dw_edma_free_chan_resources (struct dma_chan *dchan) +{ +struct dw_edma_chan *chan =dchan2dw_edma_chan (dchan); + +dw_edma_device_terminate_all (dchan); +dw_edma_device_synchronize (dchan); + +scoped_guard (spinlock_irqsave, &chan->vc.lock) + chan->configured = false; + +vchan_free_chan_resources (&chan->vc); } static int dw_edma_channel_setup(struct dw_edma *dw, bool write,
@@ -853,6 +914,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, bool write, dma->device_pause = dw_edma_device_pause; dma->device_resume = dw_edma_device_resume; dma->device_terminate_all = dw_edma_device_terminate_all; + dma->device_synchronize = dw_edma_device_synchronize; dma->device_issue_pending = dw_edma_device_issue_pending; dma->device_tx_status = dw_edma_device_tx_status; dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg;
Signature · 1 line
2.53.0