6.1.183-rc1 review
Greg Kroah-Hartman
maintainerPATCH 284/609 drm/amdgpu: fix division by zero with invalid uvd dimensions
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Boyuan Zhang <[email protected]>
commit 0c01c811be47e6b146552dd59bfedbea8f09b8f4 upstream.
When width or height is less than 16, width_in_mb or height_in_mb becomes 0, leading to fs_in_mb being 0. This causes a division by zero when calculating num_dpb_buffer in H264 and H264 Perf decode paths.
Add validation to reject frames with width < 16 or height < 16 before performing any calculations that depend on these values.
V2: Format change - move up all vaiable definitions.
V3: Use warn_once to avoid spam.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +8 -0
@@ -630,6 +630,14 @@ static int amdgpu_uvd_cs_msg_decode(struunsigned image_size, tmp, min_dpb_size, num_dpb_buffer;unsigned min_ctx_size = ~0 ; +/* Reject invalid dimensions to prevent division by zero */ +if (width <16 || height <16 ) { +dev_WARN_ONCE (adev->dev,1 , +"Invalid UVD decoding dimensions (%dx%d)!\n" , + width, height); +return -EINVAL ; + } + image_size = width * height; image_size += image_size /2 ; image_size =ALIGN (image_size,1024 );
Greg Kroah-Hartman
maintainerPATCH 285/609 drm/amdgpu: invoke pm_genpd_remove() before freeing genpd
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ce Sun <[email protected]>
commit 28c9b3c5dc35cc790d11e26ca3fc6e068be63998 upstream.
Call pm_genpd_remove() to unregister from global list prior to releasing acp_genpd memory, and clear the pointer after free.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +2 -0
@@ -559,7 +559,9 @@ static int acp_hw_fini(void *handle)mfd_remove_devices (adev->acp.parent);kfree (adev->acp.acp_res); +pm_genpd_remove (&adev->acp.acp_genpd->gpd);kfree (adev->acp.acp_genpd); + adev->acp.acp_genpd =NULL ;kfree (adev->acp.acp_cell);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 286/609 RISC-V: KVM: Serialize virtual interrupt pending state updates
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xie Bo <[email protected]>
commit d024a0a7879e6f37c0152aacf6d8e37b214a1738 upstream.
KVM RISC-V tracks guest local interrupt state with two bitmaps:
- irqs_pending: interrupts that should be visible to the guest
- irqs_pending_mask: interrupts whose pending state changed
The current code updates those bitmaps with independent atomic bitops and assumes a multiple-producer, single-consumer protocol. That model does not actually hold.
kvm_riscv_vcpu_sync_interrupts() is not a pure consumer. When the guest changes guest-visible HVIP state, sync_interrupts() writes both irqs_pending and irqs_pending_mask to reflect the new guest state back into KVM state. As a result, irqs_pending and irqs_pending_mask form a single logical state transition, but they are not updated atomically as a pair.
This allows a race where a newly injected interrupt is lost. For example:
CPU0 CPU1
---- ----
kvm_riscv_vcpu_set_interrupt(VS_SOFT)
set_bit(VS_SOFT, irqs_pending)
kvm_riscv_vcpu_sync_interrupts()
sees guest-cleared HVIP.VSSIP sets irqs_pending_mask clear_bit(IRQ_VS_SOFT, irqs_pending)
set_bit(VS_SOFT, irqs_pending_mask)
kvm_vcpu_kick()
After that interleaving, a later flush can update HVIP without VSSIP even though a new virtual interrupt was injected. In practice, the guest can remain blocked in WFI with work pending.
The same pending/mask protocol is shared by VS soft interrupts, PMU overflow delivery, and AIA high interrupt synchronization, so the race is not limited to one interrupt source.
Fix this by serializing all updates to irqs_pending and irqs_pending_mask with a per-vCPU raw spinlock. This keeps the pending bit and the dirty mask as one state transition across:
- set/unset interrupt
- guest HVIP sync
- interrupt flush to guest CSR state
- vCPU reset
- AIA CSR writes that clear dirty state
Use non-atomic bitmap operations while holding the lock. Hold the lock across the AIA sync, flush, and pending checks as well, so both bitmap words share the same serialization domain.
This intentionally replaces the existing lockless protocol instead of trying to repair it with additional barriers. The problem is not memory ordering on a single field; it is that two separate bitmaps encode one shared state machine while both producers and sync paths can modify them. A per-vCPU raw spinlock keeps the fix small, local, and suitable for backporting.
[ bo: Adapt to the scalar interrupt state in 6.1.y and its CSR one-reg
helpers in vcpu.c. Drop AIA and PMU overflow handling, which are not present in this tree. ]
--- arch/riscv/include/asm/kvm_host.h | 10 ++-- arch/riscv/kvm/vcpu.c | 78 ++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 28 deletions(-)
arch/riscv/include/asm/kvm_host.h +5 -5
@@ -192,12 +192,12 @@ struct kvm_vcpu_arch {/* * VCPU interrupts * -* We have a lockless approach for tracking pending VCPU interrupts -* implemented using atomic bitops. The irqs_pending bitmap represent -* pending interrupts whereas irqs_pending_mask represent bits changed -* in irqs_pending. Our approach is modeled around multiple producer -* and single consumer problem where the consumer is the VCPU itself. +* The irqs_pending field represents pending interrupts whereas +* irqs_pending_mask represents bits changed in irqs_pending. Updates +* to these fields are serialized so vcpu interrupt sync/flush cannot +* drop a newly injected interrupt while syncing guest-visible HVIP. */ +raw_spinlock_t irqs_pending_lock;unsigned long irqs_pending;unsigned long irqs_pending_mask;
arch/riscv/kvm/vcpu.c +55 -23
165 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 287/609 i40e: remove read access to debugfs files
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jacob Keller <[email protected]>
[ Upstream commit 9fcdb1c3c4ba134434694c001dbff343f1ffa319 ]
The 'command' and 'netdev_ops' debugfs files are a legacy debugging interface supported by the i40e driver since its early days by commit 02e9c290814c ("i40e: debugfs interface").
Both of these debugfs files provide a read handler which is mostly useless, and which is implemented with questionable logic. They both use a static 256 byte buffer which is initialized to the empty string. In the case of the 'command' file this buffer is literally never used and simply wastes space. In the case of the 'netdev_ops' file, the last command written is saved here.
On read, the files contents are presented as the name of the device followed by a colon and then the contents of their respective static buffer. For 'command' this will always be "<device>: ". For 'netdev_ops', this will be "<device>: <last command written>". But note the buffer is shared between all devices operated by this module. At best, it is mostly meaningless information, and at worse it could be accessed simultaneously as there doesn't appear to be any locking mechanism.
We have also recently received multiple reports for both read functions about their use of snprintf and potential overflow that could result in reading arbitrary kernel memory. For the 'command' file, this is definitely impossible, since the static buffer is always zero and never written to. For the 'netdev_ops' file, it does appear to be possible, if the user carefully crafts the command input, it will be copied into the buffer, which could be large enough to cause snprintf to truncate, which then causes the copy_to_user to read beyond the length of the buffer allocated by kzalloc.
A minimal fix would be to replace snprintf() with scnprintf() which would cap the return to the number of bytes written, preventing an overflow. A more involved fix would be to drop the mostly useless static buffers, saving 512 bytes and modifying the read functions to stop needing those as input.
Instead, lets just completely drop the read access to these files. These are debug interfaces exposed as part of debugfs, and I don't believe that dropping read access will break any script, as the provided output is pretty useless. You can find the netdev name through other more standard interfaces, and the 'netdev_ops' interface can easily result in garbage if you issue simultaneous writes to multiple devices at once.
In order to properly remove the i40e_dbg_netdev_ops_buf, we need to refactor its write function to avoid using the static buffer. Instead, use the same logic as the i40e_dbg_command_write, with an allocated buffer. Update the code to use this instead of the static buffer, and ensure we free the buffer on exit. This fixes simultaneous writes to 'netdev_ops' on multiple devices, and allows us to remove the now unused static buffer along with removing the read access.
vs i40e_pf_get_main_vsi(pf) in the removed read functions; resolution is simply to delete the 6.1 version of those functions.]
--- .../net/ethernet/intel/i40e/i40e_debugfs.c | 121 +++--------------- 1 file changed, 19 insertions(+), 102 deletions(-)
drivers/net/ethernet/intel/i40e/i40e_debugfs.c +19 -102
@@ -57,47 +57,6 @@ static struct i40e_veb *i40e_dbg_find_veb(struct i40e_pf *pf, int seid)* setup, adding or removing filters, or other things. Many of * these will be useful for some forms of unit testing. **************************************************************/ -static char i40e_dbg_command_buf[256 ] ="" ; - -/** -* i40e_dbg_command_read - read for command datum -* @filp: the opened file -* @buffer: where to write the data for the user to read -* @count: the size of the user's buffer -* @ppos: file position offset -**/ -static ssize_t i40e_dbg_command_read (struct file *filp,char __user *buffer, -size_t count,loff_t *ppos) -{ -struct i40e_pf *pf = filp->private_data; -int bytes_not_copied; -int buf_size =256 ; -char *buf; -int len; - -/* don't allow partial reads */ -if (*ppos !=0 ) -return 0 ; -if (count < buf_size) -return -ENOSPC ; - - buf =kzalloc (buf_size,GFP_KERNEL ); -if (!buf) -return -ENOSPC ; - - len =snprintf (buf, buf_size,"%s: %s\n" , - pf->vsi[pf->lan_vsi]->netdev->name, - i40e_dbg_command_buf); - - bytes_not_copied =copy_to_user (buffer, buf, len); -kfree (buf); - -if (bytes_not_copied) -return -EFAULT ; - - *ppos = len; -return len; -}static char *i40e_filter_state_string[] = {"INVALID" ,
@@ -1636,7 +1595,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp,static const struct file_operations i40e_dbg_command_fops = { .owner =THIS_MODULE , .open = simple_open, - .read = i40e_dbg_command_read, .write = i40e_dbg_command_write, };
@@ -1645,47 +1603,6 @@ static const struct file_operations i40e_dbg_command_fops = {* The netdev_ops entry in debugfs is for giving the driver commands * to be executed from the netdev operations. **************************************************************/ -static char i40e_dbg_netdev_ops_buf[256 ] ="" ; - -/** -* i40e_dbg_netdev_ops_read - read for netdev_ops datum -* @filp: the opened file -* @buffer: where to write the data for the user to read -* @count: the size of the user's buffer -* @ppos: file position offset -**/ -static ssize_t i40e_dbg_netdev_ops_read (struct file *filp,char __user *buffer, -size_t count,loff_t *ppos) -{ -struct i40e_pf *pf = filp->private_data; -int bytes_not_copied; -int buf_size =256 ; -char *buf; -int len; - -/* don't allow partal reads */ -if (*ppos !=0 ) -return 0 ; -if (count < buf_size) -return -ENOSPC ; - - buf =kzalloc (buf_size,GFP_KERNEL ); -if (!buf) -return -ENOSPC ; - - len =snprintf (buf, buf_size,"%s: %s\n" , - pf->vsi[pf->lan_vsi]->netdev->name, - i40e_dbg_netdev_ops_buf); - - bytes_not_copied =copy_to_user (buffer, buf, len); -kfree (buf); - -if (bytes_not_copied) -return -EFAULT ; - - *ppos = len; -return len; -}/** * i40e_dbg_netdev_ops_write - write into netdev_ops datum
@@ -1699,35 +1616,36 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, size_t count, loff_t *ppos) { struct i40e_pf *pf = filp->private_data; +char *cmd_buf, *buf_tmp;int bytes_not_copied;struct i40e_vsi *vsi; -char *buf_tmp;int vsi_seid;int i, cnt;/* don't allow partial writes */ if (*ppos !=0 )return 0 ; -if (count >=sizeof (i40e_dbg_netdev_ops_buf)) -return -ENOSPC ; -memset (i40e_dbg_netdev_ops_buf,0 ,sizeof (i40e_dbg_netdev_ops_buf)); - bytes_not_copied =copy_from_user (i40e_dbg_netdev_ops_buf, - buffer, count); -if (bytes_not_copied) + cmd_buf =kzalloc (count +1 ,GFP_KERNEL ); +if (!cmd_buf) +return count; + bytes_not_copied =copy_from_user (cmd_buf, buffer, count); +if (bytes_not_copied) { +kfree (cmd_buf);return -EFAULT ; - i40e_dbg_netdev_ops_buf[count] ='\0' ; + } + cmd_buf[count] ='\0' ; - buf_tmp =strchr (i40e_dbg_netdev_ops_buf,'\n' ); + buf_tmp =strchr (cmd_buf,'\n' );if (buf_tmp) { *buf_tmp ='\0' ; - count = buf_tmp - i40e_dbg_netdev_ops_buf +1 ; + count = buf_tmp - cmd_buf +1 ; } -if (strncmp (i40e_dbg_netdev_ops_buf,"change_mtu" ,10 ) ==0 ) { +if (strncmp (cmd_buf,"change_mtu" ,10 ) ==0 ) {int mtu; - cnt =sscanf (&i40e_dbg_netdev_ops_buf[11 ],"%i %i" , + cnt =sscanf (&cmd_buf[11 ],"%i %i" , &vsi_seid, &mtu);if (cnt !=2 ) {dev_info (&pf->pdev->dev,"change_mtu <vsi_seid> <mtu>\n" );
@@ -1749,8 +1667,8 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,dev_info (&pf->pdev->dev,"Could not acquire RTNL - please try again\n" ); } - }else if (strncmp (i40e_dbg_netdev_ops_buf,"set_rx_mode" ,11 ) ==0 ) { - cnt =sscanf (&i40e_dbg_netdev_ops_buf[11 ],"%i" , &vsi_seid); + }else if (strncmp (cmd_buf,"set_rx_mode" ,11 ) ==0 ) { + cnt =sscanf (&cmd_buf[11 ],"%i" , &vsi_seid);if (cnt !=1 ) {dev_info (&pf->pdev->dev,"set_rx_mode <vsi_seid>\n" ); goto netdev_ops_write_done;
@@ -1770,8 +1688,8 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,dev_info (&pf->pdev->dev,"Could not acquire RTNL - please try again\n" ); } - }else if (strncmp (i40e_dbg_netdev_ops_buf,"napi" ,4 ) ==0 ) { - cnt =sscanf (&i40e_dbg_netdev_ops_buf[4 ],"%i" , &vsi_seid); + }else if (strncmp (cmd_buf,"napi" ,4 ) ==0 ) { + cnt =sscanf (&cmd_buf[4 ],"%i" , &vsi_seid);if (cnt !=1 ) {dev_info (&pf->pdev->dev,"napi <vsi_seid>\n" ); goto netdev_ops_write_done;
@@ -1789,21 +1707,20 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,dev_info (&pf->pdev->dev,"napi called\n" ); } }else { -dev_info (&pf->pdev->dev,"unknown command '%s'\n" , - i40e_dbg_netdev_ops_buf); +dev_info (&pf->pdev->dev,"unknown command '%s'\n" , cmd_buf);dev_info (&pf->pdev->dev,"available commands\n" );dev_info (&pf->pdev->dev," change_mtu <vsi_seid> <mtu>\n" );dev_info (&pf->pdev->dev," set_rx_mode <vsi_seid>\n" );dev_info (&pf->pdev->dev," napi <vsi_seid>\n" ); } netdev_ops_write_done: +kfree (cmd_buf);return count; }static const struct file_operations i40e_dbg_netdev_ops_fops = { .owner =THIS_MODULE , .open = simple_open, - .read = i40e_dbg_netdev_ops_read, .write = i40e_dbg_netdev_ops_write, };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 288/609 ipv6: ndisc: fix NULL deref in accept_untracked_na()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit d186e942365acece7c56d39da05dd63bf95b280a upstream.
accept_untracked_na() re-fetches the inet6_dev with __in6_dev_get(dev) and dereferences idev->cnf.accept_untracked_na without a NULL check, even though its only caller ndisc_recv_na() already fetched and NULL-checked idev for the same device.
Both reads of dev->ip6_ptr run in the same RCU read-side critical section, but a concurrent addrconf_ifdown() can clear dev->ip6_ptr between them: lowering the MTU below IPV6_MIN_MTU calls addrconf_ifdown() without the synchronize_net() that orders the unregister path, so the re-fetch returns NULL and oopses:
BUG: KASAN: null-ptr-deref in ndisc_recv_na (net/ipv6/ndisc.c:974) Read of size 4 at addr 0000000000000364 Call Trace: <IRQ> ndisc_recv_na (net/ipv6/ndisc.c:974) icmpv6_rcv (net/ipv6/icmp.c:1193) ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:479) ip6_input_finish (net/ipv6/ip6_input.c:534) ip6_input (net/ipv6/ip6_input.c:545) ip6_mc_input (net/ipv6/ip6_input.c:635) ipv6_rcv (net/ipv6/ip6_input.c:351) </IRQ>
It is reachable by an unprivileged user via a network namespace.
Pass the caller's already validated idev instead of re-fetching it; the idev stays alive for the whole RCU critical section, so it is safe even after dev->ip6_ptr has been cleared.
--- net/ipv6/ndisc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
net/ipv6/ndisc.c +3 -5
@@ -972,10 +972,8 @@ static void ndisc_recv_ns(struct sk_buff *skb)in6_dev_put (idev); } -static int accept_untracked_na (struct net_device *dev,struct in6_addr *saddr) +static int accept_untracked_na (struct inet6_dev *idev,struct in6_addr *saddr) { -struct inet6_dev *idev =__in6_dev_get (dev); -switch (idev->cnf.accept_untracked_na) {case 0 :/* Don't accept untracked na (absent in neighbor cache) */ return 0 ;
@@ -985,7 +983,7 @@ static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr)* same subnet as an address configured on the interface that * received the na */ -return !!ipv6_chk_prefix (saddr, dev); +return !!ipv6_chk_prefix (saddr, idev->dev);default :return 0 ; }
@@ -1086,7 +1084,7 @@ static void ndisc_recv_na(struct sk_buff *skb)*/ new_state = msg->icmph.icmp6_solicited ?NUD_REACHABLE :NUD_STALE ;if (!neigh && lladdr && idev && idev->cnf.forwarding) { -if (accept_untracked_na (dev, saddr)) { +if (accept_untracked_na (idev, saddr)) { neigh =neigh_create (&nd_tbl, &msg->target, dev); new_state =NUD_STALE ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 289/609 tipc: fix use-after-free of the discoverer in tipc_disc_rcv()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit 1579342d71133da7f00daa02c75cebec7372097b upstream.
bearer_disable() frees b->disc with tipc_disc_delete()'s plain kfree(), but tipc_disc_rcv() still dereferences b->disc in RX softirq under rcu_read_lock() (tipc_udp_recv -> tipc_rcv -> tipc_disc_rcv).
L2 bearers are safe thanks to the synchronize_net() in tipc_disable_l2_media(), but the UDP bearer defers that call to the cleanup_bearer() workqueue, so the discoverer is freed with no grace period:
BUG: KASAN: slab-use-after-free in tipc_disc_rcv (net/tipc/discover.c:149) Read of size 8 at addr ffff88802348b728 by task poc_tipc/184 <IRQ> tipc_disc_rcv (net/tipc/discover.c:149) tipc_rcv (net/tipc/node.c:2126) tipc_udp_recv (net/tipc/udp_media.c:391) udp_rcv (net/ipv4/udp.c:2643) ip_local_deliver_finish (net/ipv4/ip_input.c:241) </IRQ> Freed by task 181: kfree (mm/slub.c:6565) bearer_disable (net/tipc/bearer.c:418) tipc_nl_bearer_disable (net/tipc/bearer.c:1001)
The bearer is freed with kfree_rcu(); free the discoverer the same way. Add an rcu_head to struct tipc_discoverer and free it and its skb from an RCU callback.
Because the RCU callback (tipc_disc_free_rcu) lives in module text, a call_rcu() that is still pending when the tipc module is unloaded would invoke a freed function. Add an rcu_barrier() to tipc_exit() after the bearer subsystem has been torn down, so all pending discoverer callbacks have run before the module text goes away.
Reachable from an unprivileged user namespace: the TIPCv2 genl family is netnsok and its bearer commands have no GENL_ADMIN_PERM. Needs CONFIG_TIPC and CONFIG_TIPC_MEDIA_UDP.
--- net/tipc/core.c | 5 +++++ net/tipc/discover.c | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-)
net/tipc/core.c +5 -0
@@ -218,6 +218,11 @@ static void __exit tipc_exit(void)unregister_pernet_device (&tipc_net_ops);tipc_unregister_sysctl (); +/* TODO: Wait for all timers that called call_rcu() to finish before +* calling rcu_barrier(). +*/ +rcu_barrier (); +pr_info ("Deactivated\n" ); }
net/tipc/discover.c +12 -2
@@ -58,6 +58,7 @@* @skb: request message to be (repeatedly) sent * @timer: timer governing period between requests * @timer_intv: current interval between requests (in ms) +* @rcu: RCU head for deferred freeing */ struct tipc_discoverer {u32 bearer_id;
@@ -69,6 +70,7 @@ struct tipc_discoverer {struct sk_buff *skb;struct timer_list timer;unsigned long timer_intv; +struct rcu_head rcu; };/**
@@ -382,6 +384,15 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,return 0 ; } +static void tipc_disc_free_rcu (struct rcu_head *rp) +{ + struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer, + rcu); + +kfree_skb (d->skb); +kfree (d); +} +/** * tipc_disc_delete - destroy object sending periodic link setup requests * @d: ptr to link dest structure
@@ -389,8 +400,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,void tipc_disc_delete (struct tipc_discoverer *d) {del_timer_sync (&d->timer); -kfree_skb (d->skb); -kfree (d); +call_rcu (&d->rcu, tipc_disc_free_rcu); }/**
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 290/609 openvswitch: fix GSO userspace truncation underflow
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
[ Upstream commit 4032f8ed10fcb84d41c508dfb04be96589f78dfe ]
OVS_ACTION_ATTR_TRUNC currently stores a delta from the original skb length in OVS_CB(skb)->cutlen. When a later userspace action segments a GSO skb, queue_gso_packets() reuses that delta for each smaller segment. A segment can then reach queue_userspace_packet() with cutlen greater than skb->len, underflowing the length passed to skb_zerocopy().
Store the maximum preserved length instead and bound each consumer against the current skb length. Use U32_MAX as the no-truncation sentinel so the value remains valid if skb geometry changes before a consumer handles it.
--- net/openvswitch/actions.c | 15 +++++---------- net/openvswitch/datapath.c | 25 ++++++++++++++----------- net/openvswitch/datapath.h | 2 +- net/openvswitch/vport.c | 2 +- 4 files changed, 21 insertions(+), 23 deletions(-)
net/openvswitch/actions.c +5 -10
@@ -856,12 +856,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,u16 mru =OVS_CB (skb)->mru;u32 cutlen =OVS_CB (skb)->cutlen; -if (unlikely (cutlen >0 )) { -if (skb->len - cutlen >ovs_mac_header_len (key)) -pskb_trim (skb, skb->len - cutlen); -else -pskb_trim (skb,ovs_mac_header_len (key)); - } +if (unlikely (cutlen < skb->len)) +pskb_trim (skb,max (cutlen,ovs_mac_header_len (key)));if (likely (!mru || (skb->len <= mru + vport->dev->hard_header_len))) {
@@ -1242,22 +1238,21 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, clone =skb_clone (skb,GFP_ATOMIC );if (clone)do_output (dp, clone, port, key); -OVS_CB (skb)->cutlen =0 ; +OVS_CB (skb)->cutlen =U32_MAX ;break ; }case OVS_ACTION_ATTR_TRUNC : {struct ovs_action_trunc *trunc =nla_data (a); -if (skb->len > trunc->max_len) -OVS_CB (skb)->cutlen = skb->len - trunc->max_len; +OVS_CB (skb)->cutlen = trunc->max_len;break ; }case OVS_ACTION_ATTR_USERSPACE :output_userspace (dp, skb, key, a, attr, len,OVS_CB (skb)->cutlen); -OVS_CB (skb)->cutlen =0 ; +OVS_CB (skb)->cutlen =U32_MAX ;break ;case OVS_ACTION_ATTR_HASH :
net/openvswitch/datapath.c +14 -11
@@ -251,7 +251,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) upcall.portid =ovs_vport_find_upcall_portid (p, skb); upcall.mru =OVS_CB (skb)->mru; - error =ovs_dp_upcall (dp, skb, key, &upcall,0 ); + error =ovs_dp_upcall (dp, skb, key, &upcall,U32_MAX );switch (error) {case 0 :case -EAGAIN :
@@ -414,7 +414,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,struct sk_buff *nskb =NULL ;struct sk_buff *user_skb =NULL ;/* to be queued to userspace */ struct nlattr *nla; -size_t len; +size_t msg_size; +size_t skb_len;unsigned int hlen;int err, dp_ifindex;u64 hash;
@@ -435,7 +436,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, skb = nskb; } -if (nla_attr_size (skb->len) >USHRT_MAX ) { + skb_len =min (skb->len, cutlen); +if (nla_attr_size (skb_len) >USHRT_MAX ) { err = -EFBIG ; goto out; }
@@ -450,13 +452,13 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,* padding logic. Only perform zerocopy if padding is not required. */ if (dp->user_features &OVS_DP_F_UNALIGNED ) - hlen =skb_zerocopy_headlen (skb); + hlen =min (skb_zerocopy_headlen (skb), cutlen);else - hlen = skb->len; + hlen = skb_len; - len =upcall_msg_size (upcall_info, hlen - cutlen, -OVS_CB (skb)->acts_origlen); - user_skb =genlmsg_new (len,GFP_ATOMIC ); + msg_size =upcall_msg_size (upcall_info, hlen, +OVS_CB (skb)->acts_origlen); + user_skb =genlmsg_new (msg_size,GFP_ATOMIC );if (!user_skb) { err = -ENOMEM ; goto out;
@@ -517,7 +519,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, }/* Add OVS_PACKET_ATTR_LEN when packet is truncated */ -if (cutlen >0 && +if (skb_len < skb->len &&nla_put_u32 (user_skb,OVS_PACKET_ATTR_LEN , skb->len)) { err = -ENOBUFS ; goto out;
@@ -542,9 +544,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, err = -ENOBUFS ; goto out; } - nla->nla_len =nla_attr_size (skb->len - cutlen); + nla->nla_len =nla_attr_size (skb_len); - err =skb_zerocopy (user_skb, skb, skb->len - cutlen, hlen); + err =skb_zerocopy (user_skb, skb, skb_len, hlen);if (err) goto out;
@@ -601,6 +603,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) packet->ignore_df =1 ; }OVS_CB (packet)->mru = mru; +OVS_CB (packet)->cutlen =U32_MAX ;if (a[OVS_PACKET_ATTR_HASH ]) { hash =nla_get_u64 (a[OVS_PACKET_ATTR_HASH ]);
net/openvswitch/datapath.h +1 -1
@@ -114,7 +114,7 @@ struct datapath {* @mru: The maximum received fragement size; 0 if the packet is not * fragmented. * @acts_origlen: The netlink size of the flow actions applied to this skb. -* @cutlen: The number of bytes from the packet end to be removed. +* @cutlen: The number of bytes in the packet to preserve on output. */ struct ovs_skb_cb {struct vport *input_vport;
net/openvswitch/vport.c +1 -1
@@ -438,7 +438,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,OVS_CB (skb)->input_vport = vport;OVS_CB (skb)->mru =0 ; -OVS_CB (skb)->cutlen =0 ; +OVS_CB (skb)->cutlen =U32_MAX ;if (unlikely (dev_net (skb->dev) !=ovs_dp_get_net (vport->dp))) {u32 mark;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 291/609 fscrypt: Avoid dynamic allocation in fscrypt_get_devices()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Biggers <[email protected]>
commit 6fe4e4b8259e1330945b5f3c9476e08473b8e0e8 upstream.
When a blk_crypto_key starts being used or is evicted, fs/crypto/ calls fscrypt_get_devices() to get the filesystem's list of block devices, then iterates over them and calls blk_crypto_config_supported(), blk_crypto_start_using_key(), or blk_crypto_evict_key() on each one.
Currently, the block device pointers are placed in a dynamically allocated array. This dynamic allocation is problematic because:
- It can fail, especially at the fscrypt_destroy_inline_crypt_key() call site when it's invoked for inode eviction under direct reclaim.
- fscrypt_destroy_inline_crypt_key() doesn't handle the failure. It just zeroizes and frees the blk_crypto_key without calling blk_crypto_evict_key(). That causes a use-after-free.
For now, let's fix this in the straightforward and easily-backportable way by switching to an on-stack array. Currently the fscrypt multi-device functionality is used only by f2fs, which has a hardcoded limit of 8 block devices. An on-stack array works fine for that.
(Of course, this solution won't scale up to large number of block devices. For that we'd need a different solution, like moving the block device iteration into the filesystem. Or in the case of btrfs, which will only support blk-crypto-fallback, we should make it just call blk-crypto-fallback directly, so the block devices won't be needed.)
--- fs/crypto/inline_crypt.c | 57 ++++++++++++++-------------------------- fs/f2fs/super.c | 25 ++++++++++-------- include/linux/fscrypt.h | 18 +++++++------ 3 files changed, 44 insertions(+), 56 deletions(-)
fs/crypto/inline_crypt.c +20 -37
@@ -21,22 +21,14 @@#include "fscrypt_private.h" -static struct block_device **fscrypt_get_devices (struct super_block *sb, -unsigned int *num_devs) +static unsigned int +fscrypt_get_devices (struct super_block *sb, +struct block_device *devs[FSCRYPT_MAX_DEVICES ]) { -struct block_device **devs; - -if (sb->s_cop->get_devices) { - devs = sb->s_cop->get_devices (sb, num_devs); -if (devs) -return devs; - } - devs =kmalloc (sizeof (*devs),GFP_KERNEL ); -if (!devs) -return ERR_PTR (-ENOMEM ); +if (sb->s_cop->get_devices) +return sb->s_cop->get_devices (sb, devs); devs[0 ] = sb->s_bdev; - *num_devs =1 ; -return devs; +return 1 ; }static unsigned int fscrypt_get_dun_bytes (const struct fscrypt_info *ci)
@@ -95,7 +87,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci)const struct inode *inode = ci->ci_inode;struct super_block *sb = inode->i_sb;struct blk_crypto_config crypto_cfg; -struct block_device **devs; +struct block_device *devs[FSCRYPT_MAX_DEVICES ];unsigned int num_devs;unsigned int i;
@@ -132,20 +124,15 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci) crypto_cfg.data_unit_size = sb->s_blocksize; crypto_cfg.dun_bytes =fscrypt_get_dun_bytes (ci); - devs =fscrypt_get_devices (sb, &num_devs); -if (IS_ERR (devs)) -return PTR_ERR (devs); - + num_devs =fscrypt_get_devices (sb, devs);for (i =0 ; i < num_devs; i++) {if (!blk_crypto_config_supported (devs[i], &crypto_cfg)) - goto out_free_devs; +return 0 ; }fscrypt_log_blk_crypto_impl (ci->ci_mode, devs, num_devs, &crypto_cfg); ci->ci_inlinecrypt = true; -out_free_devs: -kfree (devs);return 0 ; }
@@ -158,7 +145,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,struct super_block *sb = inode->i_sb;enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode;struct blk_crypto_key *blk_key; -struct block_device **devs; +struct block_device *devs[FSCRYPT_MAX_DEVICES ];unsigned int num_devs;unsigned int i;int err;
@@ -175,17 +162,12 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, }/* Start using blk-crypto on all the filesystem's block devices. */ - devs =fscrypt_get_devices (sb, &num_devs); -if (IS_ERR (devs)) { - err =PTR_ERR (devs); - goto fail; - } + num_devs =fscrypt_get_devices (sb, devs);for (i =0 ; i < num_devs; i++) { err =blk_crypto_start_using_key (devs[i], blk_key);if (err)break ; } -kfree (devs);if (err) {fscrypt_err (inode,"error %d starting to use blk-crypto" , err); goto fail;
@@ -209,20 +191,21 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb, struct fscrypt_prepared_key *prep_key) {struct blk_crypto_key *blk_key = prep_key->blk_key; -struct block_device **devs; +struct block_device *devs[FSCRYPT_MAX_DEVICES ];unsigned int num_devs;unsigned int i;if (!blk_key)return ; -/* Evict the key from all the filesystem's block devices. */ - devs =fscrypt_get_devices (sb, &num_devs); -if (!IS_ERR (devs)) { -for (i =0 ; i < num_devs; i++) -blk_crypto_evict_key (devs[i], blk_key); -kfree (devs); - } +/* +* Evict the key from all the filesystem's block devices. +* This *must* be done before the key is freed. +*/ + num_devs =fscrypt_get_devices (sb, devs); +for (i =0 ; i < num_devs; i++) +blk_crypto_evict_key (devs[i], blk_key); +kfree_sensitive (blk_key); }
fs/f2fs/super.c +14 -11
@@ -3126,24 +3126,27 @@ static void f2fs_get_ino_and_lblk_bits(struct super_block *sb, *lblk_bits_ret =8 *sizeof (block_t); } -static struct block_device **f2fs_get_devices (struct super_block *sb, -unsigned int *num_devs) +static unsigned int +f2fs_get_devices (struct super_block *sb, +struct block_device *devs[FSCRYPT_MAX_DEVICES ]) {struct f2fs_sb_info *sbi =F2FS_SB (sb); -struct block_device **devs; +int ndevs;int i; -if (!f2fs_is_multi_device (sbi)) -return NULL ; +static_assert (MAX_DEVICES <=FSCRYPT_MAX_DEVICES ); - devs =kmalloc_array (sbi->s_ndevs,sizeof (*devs),GFP_KERNEL ); -if (!devs) -return ERR_PTR (-ENOMEM ); +if (!f2fs_is_multi_device (sbi)) { + devs[0 ] = sb->s_bdev; +return 1 ; + } + ndevs = sbi->s_ndevs; +if (WARN_ON_ONCE (ndevs >FSCRYPT_MAX_DEVICES )) + ndevs =FSCRYPT_MAX_DEVICES ; -for (i =0 ; i < sbi->s_ndevs; i++) +for (i =0 ; i < ndevs; i++) devs[i] =FDEV (i).bdev; - *num_devs = sbi->s_ndevs; -return devs; +return ndevs; }static const struct fscrypt_operations f2fs_cryptops = {
include/linux/fscrypt.h +10 -8
@@ -57,6 +57,9 @@ struct fscrypt_name {/* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 +/* Maximum supported number of block devices per filesystem */ +#define FSCRYPT_MAX_DEVICES 8 +#ifdef CONFIG_FS_ENCRYPTION /*
@@ -161,21 +164,20 @@ struct fscrypt_operations { int *ino_bits_ret, int *lblk_bits_ret);/* -* Return an array of pointers to the block devices to which the -* filesystem may write encrypted file contents, NULL if the filesystem -* only has a single such block device, or an ERR_PTR() on error. +* Retrieve the list of block devices to which the filesystem may write +* encrypted file contents. * -* On successful non-NULL return, *num_devs is set to the number of -* devices in the returned array. The caller must free the returned -* array using kfree(). +* This writes the block_device pointers to @devs and returns the count +* (between 1 and FSCRYPT_MAX_DEVICES inclusively). * * If the filesystem can use multiple block devices (other than block * devices that aren't used for encrypted file contents, such as * external journal devices), and wants to support inline encryption, * then it must implement this function. Otherwise it's not needed. */ -struct block_device **(*get_devices)(struct super_block *sb, -unsigned int *num_devs); +unsigned int (*get_devices)( +struct super_block *sb, +struct block_device *devs[FSCRYPT_MAX_DEVICES ]); };static inline struct fscrypt_info *fscrypt_get_info (const struct inode *inode)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 292/609 exfat: validate cluster allocation bits of the allocation bitmap
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 79c1587b6cda74deb0c86fc7ba194b92958c793c ]
syzbot created an exfat image with cluster bits not set for the allocation bitmap. exfat-fs reads and uses the allocation bitmap without checking this. The problem is that if the start cluster of the allocation bitmap is 6, cluster 6 can be allocated when creating a directory with mkdir. exfat zeros out this cluster in exfat_mkdir, which can delete existing entries. This can reallocate the allocated entries. In addition, the allocation bitmap is also zeroed out, so cluster 6 can be reallocated. This patch adds exfat_test_bitmap_range to validate that clusters used for the allocation bitmap are correctly marked as in-use.
[Adapted to 6.1: replaced __le_long/lel_to_cpu word-level bitmap access
with per-bit test_bit_le() calls, as __le_long and lel_to_cpu do not exist in 6.1. Uses same test_bit_le API as rest of exfat bitmap code.]
--- fs/exfat/balloc.c | 54 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 12 deletions(-)
fs/exfat/balloc.c +42 -12
@@ -45,12 +45,37 @@ static const unsigned char used_bit[] = {/* * Allocation Bitmap Management Functions */ +static bool exfat_test_bitmap_range (struct super_block *sb,unsigned int clu, +unsigned int count) +{ +struct exfat_sb_info *sbi =EXFAT_SB (sb); +unsigned int start = clu; +unsigned int end = clu + count; +unsigned int ent_idx, i, b; + +if (!is_valid_cluster (sbi, start) || !is_valid_cluster (sbi, end -1 )) +return false; + +while (start < end) { + ent_idx =CLUSTER_TO_BITMAP_ENT (start); + i =BITMAP_OFFSET_SECTOR_INDEX (sb, ent_idx); + b =BITMAP_OFFSET_BIT_IN_SECTOR (sb, ent_idx); + +if (!test_bit_le (b, sbi->vol_amap[i]->b_data)) +return false; + + start++; + } + +return true; +} +static int exfat_allocate_bitmap (struct super_block *sb,struct exfat_dentry *ep) {struct exfat_sb_info *sbi =EXFAT_SB (sb);long long map_size; -unsigned int i, need_map_size; +unsigned int i, j, need_map_size;sector_t sector; sbi->map_clu =le32_to_cpu (ep->dentry.bitmap.start_clu);
@@ -77,20 +102,25 @@ static int exfat_allocate_bitmap(struct super_block *sb, sector =exfat_cluster_to_sector (sbi, sbi->map_clu);for (i =0 ; i < sbi->map_sectors; i++) { sbi->vol_amap[i] =sb_bread (sb, sector + i); -if (!sbi->vol_amap[i]) { -/* release all buffers and free vol_amap */ -int j =0 ; - -while (j < i) -brelse (sbi->vol_amap[j++]); - -kvfree (sbi->vol_amap); - sbi->vol_amap =NULL ; -return -EIO ; - } +if (!sbi->vol_amap[i]) + goto err_out; } +if (exfat_test_bitmap_range (sb, sbi->map_clu, +EXFAT_B_TO_CLU_ROUND_UP (map_size, sbi)) == false) + goto err_out; +return 0 ; + +err_out: + j =0 ; +/* release all buffers and free vol_amap */ +while (j < i) +brelse (sbi->vol_amap[j++]); + +kvfree (sbi->vol_amap); + sbi->vol_amap =NULL ; +return -EIO ; }int exfat_load_bitmap (struct super_block *sb)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 293/609 bpf: drop bpf_lsm_getselfattr from hook list
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Guan <[email protected]>
Backport ("bpf, lsm: Add disabled BPF LSM hook list") for v6.1.y bring the warning "WARN: resolve_btfids: unresolved symbol bpf_lsm_getselfattr".
The lsm_getselfattr from commit a04a1198088a
("LSM: syscalls for current process attributes"), no need to backport the huge patch, simply drop the entry to fix the noise.
This is a fix for stable v6.1.178 backport commit, so no upstream commit.
--- kernel/bpf/bpf_lsm.c | 1 - 1 file changed, 1 deletion(-)
kernel/bpf/bpf_lsm.c +0 -1
@@ -42,7 +42,6 @@ BTF_ID(func, bpf_lsm_inode_need_killpriv) BTF_ID(func, bpf_lsm_inode_getsecurity) BTF_ID(func, bpf_lsm_inode_listsecurity) BTF_ID(func, bpf_lsm_inode_copy_up_xattr) -BTF_ID(func, bpf_lsm_getselfattr) BTF_ID(func, bpf_lsm_getprocattr) BTF_ID(func, bpf_lsm_setprocattr)#ifdef CONFIG_KEYS
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 294/609 KVM: SVM: Bump asid_generation on CPU online to avoid ASID collision after hotplug
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikunj A Dadhania <[email protected]>
commit 25f744ffa0c8e799e06250ce2e618367b166b0d4 upstream.
If a vCPU stays scheduled out (or blocked) while the last pCPU it ran on goes through a hotplug cycle (online->offline->online), and the vCPU then resumes execution on the same pCPU, then it is possible for it to run with an ASID that has now been assigned to a different vCPU, resulting in stale TLB translations being used.
svm_enable_virtualization_cpu() resets asid_generation to 1 and sets next_asid to max_asid + 1 on every CPU online event, including hotplug cycles. Because next_asid starts beyond the pool boundary, the first call to new_asid() after an online event always wraps the pool, incrementing asid_generation to 2 and assigning ASIDs starting from min_asid.
Consider two vCPUs from different VMs, vCPU-A pinned to CPU-X holding asid_generation=2 and ASID=N from before the hotplug event:
1. CPU-X goes offline and back online: asid_generation resets to 1, next_asid = max_asid + 1.
2. One or more vCPUs migrate to CPU-X and call new_asid(), wrapping the pool and consuming ASIDs starting from min_asid. Eventually vCPU-B from a different VM is assigned asid_generation=2, ASID=N — the same ASID that vCPU-A held before the hotplug.
3. vCPU-A enters pre_svm_run() on CPU-X: current_vmcb->cpu is unchanged so the migration branch is skipped. Its saved asid_generation=2 matches sd->asid_generation=2, so the generation check silently passes and vCPU-A continues running with ASID=N — the same ASID just freshly assigned to vCPU-B.
Both vCPUs from different VMs now run on CPU-X with the same ASID, causing them to share NPT TLB entries and producing stale translations.
The collision manifests as a KVM internal error (Suberror: 1, emulation failure). The NPT page fault reports a faulting GPA far outside the VM's physical memory range — a sign of stale TLB translations being used. KVM falls back to instruction emulation, which fails on FPU/XSave instructions (XRSTOR, STMXCSR) that the emulator does not implement.
Fix this by incrementing asid_generation instead of resetting it to 1 in svm_enable_virtualization_cpu(). On module load, asid_generation starts at 0 (memset) and the increment produces 1, identical to the old behaviour. On subsequent hotplug cycles the generation advances beyond any value a vCPU previously observed on this CPU, so the generation check in pre_svm_run() reliably forces new_asid() on every vCPU after every hotplug cycle.
Message-ID: <[email protected]>
--- arch/x86/kvm/svm/svm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
arch/x86/kvm/svm/svm.c +6 -1
@@ -589,7 +589,12 @@ static int svm_hardware_enable(void)return -EINVAL ; } sd =per_cpu_ptr (&svm_data, me); - sd->asid_generation =1 ; +/* +* Bump the current asid_generation value to ensure any vCPU that +* previously ran on this CPU sees a stale generation and is forced +* to acquire a new ASID, preventing a latent ASID collision. +*/ + sd->asid_generation++; sd->max_asid =cpuid_ebx (SVM_CPUID_FUNC ) -1 ; sd->next_asid = sd->max_asid +1 ; sd->min_asid = max_sev_asid +1 ;
Greg Kroah-Hartman
maintainerPATCH 295/609 io_uring/rw: fix missing ERESTARTSYS conversion in read paths
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yitang Yang <[email protected]>
Commit ab05caca123c6d0b41850b7c05b246e4dca4a770 upstream.
Both read and write may receive internal restart error codes from the filesystem layer and should be converted to -EINTR. However, when multishot read support was added, the error code normalization was lost for both io_read() and io_read_mshot().
Extract the conversion into io_fixup_restart_res() and apply it in all three locations: io_rw_done(), io_read(), and io_read_mshot().
--- io_uring/rw.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-)
io_uring/rw.c +19 -9
@@ -129,27 +129,37 @@ void io_readv_writev_cleanup(struct io_kiocb *req)kfree (io->free_iovec); } -static inline void io_rw_done (struct kiocb *kiocb,ssize_t ret) +static inline ssize_t io_fixup_restart_res (ssize_t ret) {switch (ret) { -case -EIOCBQUEUED : -break ;case -ERESTARTSYS :case -ERESTARTNOINTR :case -ERESTARTNOHAND :case -ERESTART_RESTARTBLOCK :/* * We can't just restart the syscall, since previously -* submitted sqes may already be in progress. Just fail this -* IO with EINTR. +* submitted sqes may already be in progress. Just fail +* this IO with EINTR. */ - ret = -EINTR ; - fallthrough; +return -EINTR ;default : - kiocb->ki_complete (kiocb, ret); +return ret; } } +static inline void io_rw_done (struct kiocb *kiocb,ssize_t ret) +{ +/* IO was queued async, completion will happen later */ +if (ret == -EIOCBQUEUED ) +return ; + +/* transform internal restart error codes */ +if (unlikely (ret <0 )) + ret =io_fixup_restart_res (ret); + + kiocb->ki_complete (kiocb, ret); +} +static inline loff_t *io_kiocb_update_pos (struct io_kiocb *req) { struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
@@ -854,7 +864,7 @@ int io_read(struct io_kiocb *req, unsigned int issue_flags)if (ret >=0 )return kiocb_done (req, ret, issue_flags); -return ret; +return io_fixup_restart_res (ret); }static bool io_kiocb_start_write (struct io_kiocb *req,struct kiocb *kiocb)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 296/609 mm/damon/core: validate ranges in damon_set_regions()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit 1292c0ecb1caefb8ca064a3639d5673991e8810c upstream.
DAMON core logic assumes zero length regions don't exist. However, a few DAMON API callers including DAMON_SYSFS, DAMON_RECLAIM and DAMON_LRU_SORT allow users to set empty monitoring target regions. This could result in WARN_ONCE() on CONFIG_DAMON_DEBUG_SANITY enabled kernel, and divide-by-zero from damon_merge_two_regions().
For example, the WANR_ONCE() can be triggered like below.
# grep DAMON_DEBUG_SANITY /boot/config-$(uname -r)
# CONFIG_DAMON_DEBUG_SANITY=y
# damo start
# cd /sys/kernel/mm/damon/admin/kdamonds/0
# echo 0 > contexts/0/targets/0/regions/0/start
# echo 0 > contexts/0/targets/0/regions/0/end
# echo commit > state
# dmesg
[....]
[ 73.705780] ------------[ cut here ]------------
[ 73.707552] start 0 >= end 0
[ 73.708452] WARNING: mm/damon/core.c:359 at damon_new_region+0x6e/0x80, CPU#1: kdamond.0/758
[...]All DAMON API callers eventually use damon_set_regions() to setup the regions. Add the validation logic in the function.
--- mm/damon/core.c | 6 ++++++ 1 file changed, 6 insertions(+)
mm/damon/core.c +6 -0
@@ -212,6 +212,12 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges,unsigned int i;int err; +for (i =0 ; i < nr_ranges; i++) { +if (ALIGN_DOWN (ranges[i].start,DAMON_MIN_REGION ) >= +ALIGN (ranges[i].end,DAMON_MIN_REGION )) +return -EINVAL ; + } +/* Remove regions which are not in the new ranges */ damon_for_each_region_safe (r, next, t) {for (i =0 ; i < nr_ranges; i++) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 297/609 mm/damon/core: disallow overlapping input ranges for damon_set_regions()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit 954157679ec34661c2e87e7eb796104a797c32db upstream.
damon_set_regions() assumes the input ranges are sorted by the address and don't overlap each other. Hence the assumption was initially to be explicitly validated. But commit 97d482f4592f ("mm/damon/sysfs: reuse damon_set_regions() for regions setting") has mistakenly removed the validation.
This can make DAMON behave in unexpected ways. At the best, the monitoring results snapshot will just look weird since there will be overlapping regions. DAMOS will also work weirdly, applying the same action multiple times for overlapping regions, and make DAMOS quota weird. More seriously, depending on the setup and regions updates sequence, negative size regions can be made. It will trigger WARN_ONCE() if the kernel is built with CONFIG_DAMON_DEBUG_SANITY=y. Depending on the monitoring results, the negative size region can further trigger division by zero in damon_merge_two_regions().
Note that some of the consequences including the WARN_ONCE() and the divide by zero depend on commits that were introduced after the root cause commit 97d482f4592f ("mm/damon/sysfs: reuse damon_set_regions() for regions setting").
Fix the problems by checking the assumption and returning an error if the input ranges don't meet the assumption.
The issue was discovered [1] by Sashiko.
--- mm/damon/core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
mm/damon/core.c +9 -2
@@ -210,12 +210,19 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, {struct damon_region *r, *next;unsigned int i; +unsigned long last_end;int err;for (i =0 ; i < nr_ranges; i++) { -if (ALIGN_DOWN (ranges[i].start,DAMON_MIN_REGION ) >= -ALIGN (ranges[i].end,DAMON_MIN_REGION )) +unsigned long start, end; + + start =ALIGN_DOWN (ranges[i].start,DAMON_MIN_REGION ); + end =ALIGN (ranges[i].end,DAMON_MIN_REGION ); +if (start >= end) +return -EINVAL ; +if (i >0 && last_end > start)return -EINVAL ; + last_end = end; }/* Remove regions which are not in the new ranges */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 298/609 netfilter: nf_conntrack_expect: restore helper propagation via expectation
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit dcb0f9aefdd604d36710fda53c25bd7cf4a3e37a ]
A recent series to fix expectations broke helper propagation via expectation, this mechanism is used by the sip and h323 helper. This also propagates the conntrack helper to expected connections. I changed semantics of exp->helper which now tells us the actual helper that created the expectation.
Add an explicit assign_helper field to expectations for this purpose and update helpers to use it.
Restore this feature for userspace conntrack helper via ctnetlink nfqueue integration so it is again possible to attach a helper to an expectation, where it makes sense. This is not restored via ctnetlink expectation creation as there is no client for such feature. Use the expectation layer 4 protocol number for the helper lookup for consistency.
Make sure the expectation using this helper propagation mechanism also go away when the helper is unregistered.
--- include/net/netfilter/nf_conntrack_expect.h | 5 ++++- net/netfilter/nf_conntrack_broadcast.c | 1 + net/netfilter/nf_conntrack_core.c | 7 +++++-- net/netfilter/nf_conntrack_expect.c | 1 + net/netfilter/nf_conntrack_h323_main.c | 12 ++++++------ net/netfilter/nf_conntrack_helper.c | 5 +++++ net/netfilter/nf_conntrack_netlink.c | 18 ++++++++++++++++-- net/netfilter/nf_conntrack_sip.c | 2 +- 8 files changed, 39 insertions(+), 12 deletions(-)
include/net/netfilter/nf_conntrack_expect.h +4 -1
@@ -45,9 +45,12 @@ struct nf_conntrack_expect {void (*expectfn)(struct nf_conn *new,struct nf_conntrack_expect *this); -/* Helper to assign to new connection */ +/* Helper that created this expectation */ struct nf_conntrack_helper __rcu *helper; +/* Helper to assign to new connection */ +struct nf_conntrack_helper __rcu *assign_helper; +/* The conntrack of the master connection */ struct nf_conn *master;
net/netfilter/nf_conntrack_broadcast.c +1 -0
@@ -72,6 +72,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, exp->flags =NF_CT_EXPECT_PERMANENT ; exp->class =NF_CT_EXPECT_CLASS_DEFAULT ;rcu_assign_pointer (exp->helper, helper); +rcu_assign_pointer (exp->assign_helper,NULL );write_pnet (&exp->net, net);#ifdef CONFIG_NF_CONNTRACK_ZONES exp->zone = ct->zone;
net/netfilter/nf_conntrack_core.c +5 -2
@@ -1800,16 +1800,19 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,spin_lock_bh (&nf_conntrack_expect_lock); exp =nf_ct_find_expectation (net, zone, tuple, !tmpl ||nf_ct_is_confirmed (tmpl));if (exp) { +struct nf_conntrack_helper *assign_helper; +pr_debug ("expectation arrives ct=%p exp=%p\n" , ct, exp);/* Welcome, Mr. Bond. We've been expecting you... */ __set_bit (IPS_EXPECTED_BIT , &ct->status);/* exp->master safe, refcnt bumped in nf_ct_find_expectation */ ct->master = exp->master; -if (exp->helper) { + assign_helper =rcu_dereference (exp->assign_helper); +if (assign_helper) { help =nf_ct_helper_ext_add (ct,GFP_ATOMIC );if (help) -rcu_assign_pointer (help->helper, exp->helper); +rcu_assign_pointer (help->helper, assign_helper); }#ifdef CONFIG_NF_CONNTRACK_MARK
net/netfilter/nf_conntrack_expect.c +1 -0
@@ -344,6 +344,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, helper =rcu_dereference (help->helper);rcu_assign_pointer (exp->helper, helper); +rcu_assign_pointer (exp->assign_helper,NULL );write_pnet (&exp->net, net);#ifdef CONFIG_NF_CONNTRACK_ZONES exp->zone = ct->zone;
net/netfilter/nf_conntrack_h323_main.c +6 -6
@@ -642,7 +642,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); -rcu_assign_pointer (exp->helper, &nf_conntrack_helper_h245); +rcu_assign_pointer (exp->assign_helper, &nf_conntrack_helper_h245); nathook =rcu_dereference (nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -766,7 +766,7 @@ static int expect_callforwarding(struct sk_buff *skb,nf_ct_expect_init (exp,NF_CT_EXPECT_CLASS_DEFAULT ,nf_ct_l3num (ct), &ct->tuplehash[!dir].tuple.src.u3, &addr,IPPROTO_TCP ,NULL , &port); -rcu_assign_pointer (exp->helper, nf_conntrack_helper_q931); +rcu_assign_pointer (exp->assign_helper, nf_conntrack_helper_q931); nathook =rcu_dereference (nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -1233,7 +1233,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3 : NULL, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); -rcu_assign_pointer (exp->helper, nf_conntrack_helper_q931); +rcu_assign_pointer (exp->assign_helper, nf_conntrack_helper_q931); exp->flags =NF_CT_EXPECT_PERMANENT ;/* Accept multiple calls */ nathook =rcu_dereference (nfct_h323_nat_hook);
@@ -1305,7 +1305,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct,nf_ct_expect_init (exp,NF_CT_EXPECT_CLASS_DEFAULT ,nf_ct_l3num (ct), &ct->tuplehash[!dir].tuple.src.u3, &addr,IPPROTO_UDP ,NULL , &port); -rcu_assign_pointer (exp->helper, nf_conntrack_helper_ras); +rcu_assign_pointer (exp->assign_helper, nf_conntrack_helper_ras);if (nf_ct_expect_related (exp,0 ) ==0 ) {pr_debug ("nf_ct_ras: expect RAS " );
@@ -1522,7 +1522,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags =NF_CT_EXPECT_PERMANENT ; -rcu_assign_pointer (exp->helper, nf_conntrack_helper_q931); +rcu_assign_pointer (exp->assign_helper, nf_conntrack_helper_q931);if (nf_ct_expect_related (exp,0 ) ==0 ) {pr_debug ("nf_ct_ras: expect Q.931 " );
@@ -1576,7 +1576,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags =NF_CT_EXPECT_PERMANENT ; -rcu_assign_pointer (exp->helper, nf_conntrack_helper_q931); +rcu_assign_pointer (exp->assign_helper, nf_conntrack_helper_q931);if (nf_ct_expect_related (exp,0 ) ==0 ) {pr_debug ("nf_ct_ras: expect Q.931 " );
net/netfilter/nf_conntrack_helper.c +5 -0
@@ -422,6 +422,11 @@ static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data) this =rcu_dereference_protected (exp->helper,lockdep_is_held (&nf_conntrack_expect_lock)); +if (this == me) +return true; + + this =rcu_dereference_protected (exp->assign_helper, +lockdep_is_held (&nf_conntrack_expect_lock));return this == me; }
net/netfilter/nf_conntrack_netlink.c +16 -2
@@ -2628,6 +2628,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {static struct nf_conntrack_expect *ctnetlink_alloc_expect (const struct nlattr *const cda[],struct nf_conn *ct, +const struct nf_conntrack_helper *assign_helper,struct nf_conntrack_tuple *tuple,struct nf_conntrack_tuple *mask);
@@ -2854,6 +2855,7 @@ static int ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, u32 portid, u32 report) { + struct nf_conntrack_helper *assign_helper = NULL;struct nlattr *cda[CTA_EXPECT_MAX +1 ];struct nf_conntrack_tuple tuple, mask;struct nf_conntrack_expect *exp;
@@ -2869,8 +2871,18 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,if (err <0 )return err; +if (cda[CTA_EXPECT_HELP_NAME ]) { +const char *helpname =nla_data (cda[CTA_EXPECT_HELP_NAME ]); + + assign_helper =__nf_conntrack_helper_find (helpname, +nf_ct_l3num (ct), + tuple.dst.protonum); +if (!assign_helper) +return -EOPNOTSUPP ; + } + exp =ctnetlink_alloc_expect ((const struct nlattr *const *)cda, ct, - &tuple, &mask); + assign_helper, &tuple, &mask);if (IS_ERR (exp))return PTR_ERR (exp);
@@ -3509,6 +3521,7 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr,static struct nf_conntrack_expect *ctnetlink_alloc_expect (const struct nlattr *const cda[],struct nf_conn *ct, +const struct nf_conntrack_helper *assign_helper,struct nf_conntrack_tuple *tuple,struct nf_conntrack_tuple *mask) {
@@ -3562,6 +3575,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, exp->zone = ct->zone;#endif rcu_assign_pointer (exp->helper, helper); +rcu_assign_pointer (exp->assign_helper, assign_helper); exp->tuple = *tuple; exp->mask.src.u3 = mask->src.u3; exp->mask.src.u.all = mask->src.u.all;
@@ -3617,7 +3631,7 @@ ctnetlink_create_expect(struct net *net, ct =nf_ct_tuplehash_to_ctrack (h);rcu_read_lock (); - exp =ctnetlink_alloc_expect (cda, ct, &tuple, &mask); + exp =ctnetlink_alloc_expect (cda, ct,NULL , &tuple, &mask);if (IS_ERR (exp)) { err =PTR_ERR (exp); goto err_rcu;
net/netfilter/nf_conntrack_sip.c +1 -1
@@ -1384,7 +1384,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,nf_ct_expect_init (exp,SIP_EXPECT_SIGNALLING ,nf_ct_l3num (ct), saddr, &daddr, proto,NULL , &port); exp->timeout.expires = sip_timeout *HZ ; -rcu_assign_pointer (exp->helper, helper); +rcu_assign_pointer (exp->assign_helper, helper); exp->flags =NF_CT_EXPECT_PERMANENT |NF_CT_EXPECT_INACTIVE ; hooks =rcu_dereference (nf_nat_sip_hooks);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 299/609 netfilter: br_netfilter: Reallocate headroom if necessary in neigh_hh_bridge()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit b2870fc21601db9133bc70c48c603b487614fa3b ]
neigh_hh_bridge() assumes the skb always has sufficient headroom to copy the aligned L2 header. This assumption can trigger the crash reported below using the following netfilter setup:
$modprobe br_netfilter
$sysctl -w net.bridge.bridge-nf-call-iptables=1
$root@OpenWrt:~# nft list ruleset
table ip nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
ip daddr 192.168.83.123 dnat to 192.168.83.120
}
}- iperf3 client (192.168.83.119) --> bridge (192.168.83.118) --> iperf3 server (192.168.83.120)
the iperf3 client is sending packet for 192.168.83.123 to the bridge device.
[ 1579.036575] Unable to handle kernel write to read-only memory at virtual address ffffff8004d76ffe [ 1579.045482] Mem abort info: [ 1579.048273] ESR = 0x000000009600004f [ 1579.052024] EC = 0x25: DABT (current EL), IL = 32 bits [ 1579.057363] SET = 0, FnV = 0 [ 1579.060417] EA = 0, S1PTW = 0 [ 1579.063550] FSC = 0x0f: level 3 permission fault [ 1579.068345] Data abort info: [ 1579.071224] ISV = 0, ISS = 0x0000004f, ISS2 = 0x00000000 [ 1579.076720] CM = 0, WnR = 1, TnD = 0, TagAccess = 0 [ 1579.081770] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [ 1579.087092] swapper pgtable: 4k pages, 39-bit VAs, pgdp=0000000080dc4000 [ 1579.093794] [ffffff8004d76ffe] pgd=180000009ffff003, p4d=180000009ffff003, pud=180000009ffff003, pmd=180000009ffe3003, pte=0060000084d76787 [ 1579.106343] Internal error: Oops: 000000009600004f [#1] SMP [ 1579.193824] CPU: 0 UID: 0 PID: 235 Comm: napi/qdma_eth-3 Tainted: G O 6.12.57 #0 [ 1579.202614] Tainted: [O]=OOT_MODULE [ 1579.206102] Hardware name: Airoha AN7581 Evaluation Board (DT) [ 1579.211929] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 1579.218889] pc : br_nf_pre_routing_finish_bridge+0x1ac/0xcc8 [br_netfilter] [ 1579.225859] lr : br_nf_pre_routing_finish_bridge+0x18c/0xcc8 [br_netfilter] [ 1579.232822] sp : ffffffc0817cba20 [ 1579.236128] x29: ffffffc0817cba20 x28: 0000000000000000 x27: ffffff8002b89000 [ 1579.243273] x26: ffffff8004d7700e x25: 0000000000000008 x24: 0000000000000000 [ 1579.250416] x23: ffffffc08179d4c0 x22: 0000000000000000 x21: ffffffc08179d4c0 [ 1579.257561] x20: ffffff8004d9b800 x19: ffffff8015010000 x18: 0000000000000014 [ 1579.264704] x17: ffffffbf9e930000 x16: ffffffc0817c8000 x15: 0000000000000070 [ 1579.271848] x14: 0000000000000080 x13: 0000000000000001 x12: 0000000000000000 [ 1579.278993] x11: ffffffc0798caae0 x10: ffffff8014db6fd8 x9 : 0000000000000000 [ 1579.286136] x8 : 0000000000000003 x7 : ffffffc08171f628 x6 : 000000001a3b83d3 [ 1579.293281] x5 : 0000000000000000 x4 : 1beb76f22fee0000 x3 : ffffff8004d7700e [ 1579.300425] x2 : 0000000000000000 x1 : ffffff8004d9b8bc x0 : ffffff80026ed000 [ 1579.307570] Call trace: [ 1579.310018] br_nf_pre_routing_finish_bridge+0x1ac/0xcc8 [br_netfilter] [ 1579.316632] br_nf_hook_thresh+0xd4/0x14bc [br_netfilter] [ 1579.322032] br_nf_hook_thresh+0x250/0x14bc [br_netfilter] [ 1579.327517] br_nf_hook_thresh+0x76c/0x14bc [br_netfilter] [ 1579.333003] br_handle_frame+0x180/0x480 [ 1579.336935] __netif_receive_skb_core.constprop.0+0x540/0xf40 [ 1579.342682] __netif_receive_skb_one_core+0x28/0x50 [ 1579.347561] process_backlog+0x98/0x1e0 [ 1579.351398] __napi_poll+0x34/0x1c4 [ 1579.354887] net_rx_action+0x178/0x330 [ 1579.358638] handle_softirqs+0x108/0x2d4 [ 1579.362560] __do_softirq+0x10/0x18 [ 1579.366051] ____do_softirq+0xc/0x20 [ 1579.369627] call_on_irq_stack+0x30/0x4c [ 1579.373550] do_softirq_own_stack+0x18/0x20 [ 1579.377734] do_softirq+0x4c/0x60 [ 1579.381050] __local_bh_enable_ip+0x88/0x98 [ 1579.385234] napi_threaded_poll_loop+0x188/0x21c [ 1579.389853] napi_threaded_poll+0x70/0x80 [ 1579.393863] kthread+0xd8/0xdc [ 1579.396918] ret_from_fork+0x10/0x20 [ 1579.400499] Code: 88dffc22 3707ffc2 f9406663 f9406684 (f81f0064) [ 1579.406589] ---[ end trace 0000000000000000 ]--- [ 1579.411209] Kernel panic - not syncing: Oops: Fatal exception in interrupt [ 1579.418083] SMP: stopping secondary CPUs [ 1579.422012] Kernel Offset: disabled
Fix the issue reallocating the skb headroom if necessary in neigh_hh_bridge routine.
--- include/net/neighbour.h | 8 ++++++-- net/bridge/br_netfilter_hooks.c | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-)
include/net/neighbour.h +6 -2
@@ -478,11 +478,15 @@ static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)#if IS_ENABLED (CONFIG_BRIDGE_NETFILTER )static inline int neigh_hh_bridge (struct hh_cache *hh,struct sk_buff *skb) { -unsigned int seq, hh_alen; +unsigned int seq, hh_alen =HH_DATA_ALIGN (ETH_HLEN ); +int err; + + err =skb_cow_head (skb, hh_alen); +if (err) +return err;do { seq =read_seqbegin (&hh->hh_lock); - hh_alen =HH_DATA_ALIGN (ETH_HLEN );memcpy (skb->data - hh_alen, hh->hh_data,ETH_ALEN + hh_alen -ETH_HLEN ); }while (read_seqretry (&hh->hh_lock, seq));return 0 ;
net/bridge/br_netfilter_hooks.c +5 -1
@@ -292,7 +292,11 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_ goto free_skb; } -neigh_hh_bridge (&neigh->hh, skb); +if (neigh_hh_bridge (&neigh->hh, skb)) { +neigh_release (neigh); + goto free_skb; + } + skb->dev = br_indev; ret =br_handle_frame_finish (net, sk, skb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 300/609 net: mpls: initialize rtm_tos in mpls_getroute()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yehyeong Lee <[email protected]>
[ Upstream commit 295dd295e2137e10e9a5b1891d97e0f08de76f03 ]
mpls_getroute() builds the RTM_NEWROUTE reply to an RTM_GETROUTE request by filling a struct rtmsg allocated from an skb whose data area is not zeroed (alloc_skb(NLMSG_GOODSIZE, ...)). It sets every field of the header except rtm_tos:
r = nlmsg_data(nlh); r->rtm_family = AF_MPLS; r->rtm_dst_len = 20; r->rtm_src_len = 0; r->rtm_table = RT_TABLE_MAIN; r->rtm_type = RTN_UNICAST; r->rtm_scope = RT_SCOPE_UNIVERSE; r->rtm_protocol = rt->rt_protocol; r->rtm_flags = 0;
struct rtmsg has no padding, so the one uninitialised byte rtm_tos (offset 3) is copied straight to user space on recvmsg(), leaking a byte of uninitialised heap memory. This is in contrast to mpls_dump_route(), which fills the very same header and does set rtm_tos = 0.
Initialize rtm_tos to 0, matching mpls_dump_route().
Reproduced with KMSAN by adding an MPLS route and issuing a non-RTM_F_FIB_MATCH RTM_GETROUTE for its label:
BUG: KMSAN: kernel-infoleak in _copy_to_iter+0x36c/0x33f0 _copy_to_iter+0x36c/0x33f0 __skb_datagram_iter+0x196/0x12c0 skb_copy_datagram_iter+0x5b/0x210 netlink_recvmsg+0x37b/0xef0 ... Uninit was created at: __alloc_skb+0x8ca/0x10e0 mpls_getroute+0x1280/0x3a40 rtnetlink_rcv_msg+0x1138/0x15a0 ... Byte 19 of 64 is uninitialized
(byte 19 = nlmsghdr(16) + rtmsg offset 3 = rtm_tos)
--- net/mpls/af_mpls.c | 1 + 1 file changed, 1 insertion(+)
net/mpls/af_mpls.c +1 -0
@@ -2483,6 +2483,7 @@ static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, r->rtm_family =AF_MPLS ; r->rtm_dst_len =20 ; r->rtm_src_len =0 ; + r->rtm_tos =0 ; r->rtm_table =RT_TABLE_MAIN ; r->rtm_type =RTN_UNICAST ; r->rtm_scope =RT_SCOPE_UNIVERSE ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 301/609 media: uvcvideo: Implement dual stream quirk to fix loss of usb packets
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Isaac Scott <[email protected]>
commit c2eda35e675b6ea4a0a21a4b1167b121571a9036 upstream.
Some cameras, such as the Sonix Technology Co. 292A, exhibit issues when running two parallel streams, causing USB packets to be dropped when an H.264 stream posts a keyframe while an MJPEG stream is running simultaneously. This occasionally causes the driver to erroneously output two consecutive JPEG images as a single frame.
To fix this, we inspect the buffer, and trigger a new frame when we find an SOI.
--- drivers/media/usb/uvc/uvc_video.c | 28 +++++++++++++++++++++++++++- drivers/media/usb/uvc/uvcvideo.h | 4 ++++ 2 files changed, 31 insertions(+), 1 deletion(-)
drivers/media/usb/uvc/uvc_video.c +27 -1
@@ -24,6 +24,8 @@#include "uvcvideo.h" +#define JPEG_MARKER_SOI 0xd8 +/* ------------------------------------------------------------------------ * UVC Controls */
@@ -1089,6 +1091,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,struct uvc_buffer *meta_buf, const u8 *data, int len) { + u8 header_len;u8 fid;/*
@@ -1102,6 +1105,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,return -EINVAL ; } + header_len = data[0 ]; fid = data[1 ] &UVC_STREAM_FID ;/*
@@ -1194,9 +1198,31 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,return -EAGAIN ; } +/* +* Some cameras, when running two parallel streams (one MJPEG alongside +* another non-MJPEG stream), are known to lose the EOF packet for a frame. +* We can detect the end of a frame by checking for a new SOI marker, as +* the SOI always lies on the packet boundary between two frames for +* these devices. +*/ +if (stream->dev->quirks &UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc ==V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc ==V4L2_PIX_FMT_JPEG )) { +const u8 *packet = data + header_len; + +if (len >= header_len +2 && + packet[0 ] ==0xff && packet[1 ] ==JPEG_MARKER_SOI && + buf->bytesused !=0 ) { + buf->state =UVC_BUF_STATE_READY ; + buf->error =1 ; + stream->last_fid ^=UVC_STREAM_FID ; +return -EAGAIN ; + } + } + stream->last_fid = fid; -return data[0 ]; +return header_len; } static inline enum dma_data_direction uvc_stream_dir(
drivers/media/usb/uvc/uvcvideo.h +4 -0
@@ -77,6 +77,10 @@#define UVC_QUIRK_FORCE_Y8 0x00000800#define UVC_QUIRK_FORCE_BPP 0x00001000#define UVC_QUIRK_WAKE_AUTOSUSPEND 0x00002000 +#define UVC_QUIRK_NO_RESET_RESUME 0x00004000 +#define UVC_QUIRK_DISABLE_AUTOSUSPEND 0x00008000 +#define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000 +#define UVC_QUIRK_MJPEG_NO_EOF 0x00020000/* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 302/609 media: uvcvideo: Fix sequence number when no EOF
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ricardo Ribalda <[email protected]>
commit f078966ca1fb1b3865d8e6bbe2705cfd277fc637 upstream.
If the driver could not detect the EOF, the sequence number is increased twice:
1) When we enter uvc_video_decode_start() with the old buffer and FID has flipped => We return -EAGAIN and last_fid is not flipped
2) When we enter uvc_video_decode_start() with the new buffer.
Fix this issue by moving the new frame detection logic earlier in uvc_video_decode_start().
This also has some nice side affects:
- The error status from the new packet will no longer get propagated to the previous frame-buffer.
- uvc_video_clock_decode() will no longer update the previous frame buf->stf with info from the new packet.
- uvc_video_clock_decode() and uvc_video_stats_decode() will no longer get called twice for the same packet.
--- drivers/media/usb/uvc/uvc_video.c | 92 ++++++++++++++++--------------- 1 file changed, 47 insertions(+), 45 deletions(-)
drivers/media/usb/uvc/uvc_video.c +47 -45
@@ -1108,6 +1108,53 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, header_len = data[0 ]; fid = data[1 ] &UVC_STREAM_FID ; +/* +* Mark the buffer as done if we're at the beginning of a new frame. +* End of frame detection is better implemented by checking the EOF +* bit (FID bit toggling is delayed by one frame compared to the EOF +* bit), but some devices don't set the bit at end of frame (and the +* last payload can be lost anyway). We thus must check if the FID has +* been toggled. +* +* stream->last_fid is initialized to -1, and buf->bytesused to 0, +* so the first isochronous frame will never trigger an end of frame +* detection. +* +* Empty buffers (bytesused == 0) don't trigger end of frame detection +* as it doesn't make sense to return an empty buffer. This also +* avoids detecting end of frame conditions at FID toggling if the +* previous payload had the EOF bit set. +*/ +if (fid != stream->last_fid && buf && buf->bytesused !=0 ) { +uvc_dbg (stream->dev,FRAME , +"Frame complete (FID bit toggled)\n" ); + buf->state =UVC_BUF_STATE_READY ; + +return -EAGAIN ; + } + +/* +* Some cameras, when running two parallel streams (one MJPEG alongside +* another non-MJPEG stream), are known to lose the EOF packet for a frame. +* We can detect the end of a frame by checking for a new SOI marker, as +* the SOI always lies on the packet boundary between two frames for +* these devices. +*/ +if (stream->dev->quirks &UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc ==V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc ==V4L2_PIX_FMT_JPEG ) && + buf && buf->bytesused !=0 ) { +const u8 *packet = data + header_len; + +if (len >= header_len +2 && + packet[0 ] ==0xff && packet[1 ] ==JPEG_MARKER_SOI ) { + buf->state =UVC_BUF_STATE_READY ; + buf->error =1 ; + stream->last_fid ^=UVC_STREAM_FID ; +return -EAGAIN ; + } + } +/* * Increase the sequence number regardless of any buffer states, so * that discontinuous sequence numbers always indicate lost frames.
@@ -1175,51 +1222,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, meta_buf->state =UVC_BUF_STATE_ACTIVE ; } -/* -* Mark the buffer as done if we're at the beginning of a new frame. -* End of frame detection is better implemented by checking the EOF -* bit (FID bit toggling is delayed by one frame compared to the EOF -* bit), but some devices don't set the bit at end of frame (and the -* last payload can be lost anyway). We thus must check if the FID has -* been toggled. -* -* stream->last_fid is initialized to -1, so the first isochronous -* frame will never trigger an end of frame detection. -* -* Empty buffers (bytesused == 0) don't trigger end of frame detection -* as it doesn't make sense to return an empty buffer. This also -* avoids detecting end of frame conditions at FID toggling if the -* previous payload had the EOF bit set. -*/ -if (fid != stream->last_fid && buf->bytesused !=0 ) { -uvc_dbg (stream->dev,FRAME , -"Frame complete (FID bit toggled)\n" ); - buf->state =UVC_BUF_STATE_READY ; -return -EAGAIN ; - } - -/* -* Some cameras, when running two parallel streams (one MJPEG alongside -* another non-MJPEG stream), are known to lose the EOF packet for a frame. -* We can detect the end of a frame by checking for a new SOI marker, as -* the SOI always lies on the packet boundary between two frames for -* these devices. -*/ -if (stream->dev->quirks &UVC_QUIRK_MJPEG_NO_EOF && - (stream->cur_format->fcc ==V4L2_PIX_FMT_MJPEG || - stream->cur_format->fcc ==V4L2_PIX_FMT_JPEG )) { -const u8 *packet = data + header_len; - -if (len >= header_len +2 && - packet[0 ] ==0xff && packet[1 ] ==JPEG_MARKER_SOI && - buf->bytesused !=0 ) { - buf->state =UVC_BUF_STATE_READY ; - buf->error =1 ; - stream->last_fid ^=UVC_STREAM_FID ; -return -EAGAIN ; - } - } - stream->last_fid = fid;return header_len;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 303/609 gve: fix Rx queue stall on alloc failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eddie Phillips <[email protected]>
commit b65352a1bac64442ad95e64f385b40ccb9f1b0db upstream.
When the system is under extreme memory pressure, page allocations can fail during the Rx buffer refill loop. If the number of buffers posted to hardware falls below a critical low threshold and the refill loop exits due to allocation failures, the queue can stall:
1. The device drops incoming packets because there are no descriptors.
2. Since no packets are processed, no Rx completions are generated.
3. Because no completions occur, NAPI is never scheduled, preventing the refill loop from running again even after memory is freed.
This results in a permanent queue stall.
Resolve this by introducing a starvation recovery timer for each Rx queue. If the number of buffers posted to hardware falls below a critical low threshold, start a timer to periodically reschedule NAPI. Once NAPI runs and successfully refills the queue above the threshold, the timer is not rescheduled.
The threshold is set to 32 because a single maximum-sized Receive Segment Coalescing (RSC) packet can consume up to 19 descriptors in the Rx path. Lower thresholds (such as 8 or 16) would be insufficient to process a complete maximum-sized RSC packet, risking packet drops or unexpected hardware behavior under memory pressure. Setting the threshold to 32 guarantees a safe margin to handle at least one full RSC packet.
--- drivers/net/ethernet/google/gve/gve.h | 3 ++ drivers/net/ethernet/google/gve/gve_main.c | 3 ++ drivers/net/ethernet/google/gve/gve_rx_dqo.c | 33 ++++++++++++++++++++ 3 files changed, 39 insertions(+)
drivers/net/ethernet/google/gve/gve.h +3 -0
@@ -10,6 +10,7 @@#include <linux/dma-mapping.h> #include <linux/netdevice.h> #include <linux/pci.h> +#include <linux/timer.h> #include <linux/u64_stats_sync.h> #include "gve_desc.h"
@@ -35,6 +36,7 @@/* Interval to schedule a stats report update, 20000ms. */ #define GVE_STATS_REPORT_TIMER_PERIOD 20000 +#define GVE_RX_NAPI_RESCHED_MS 20/* msecs */ /* Numbers of NIC tx/rx stats in stats report. */ #define NIC_TX_STATS_REPORT_NUM 0
@@ -226,6 +228,7 @@ struct gve_rx_ring {struct u64_stats_sync statss;/* sync stats for 32bit archs */ struct gve_rx_ctx ctx;/* Info for packet currently being processed in this ring. */ +struct timer_list starvation_timer;/* for queue starvation recovery */ };/* A TX desc ring entry */
drivers/net/ethernet/google/gve/gve_main.c +3 -0
@@ -529,6 +529,9 @@ static void gve_remove_napi(struct gve_priv *priv, int ntfy_idx) {struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; +if (block->rx && !gve_is_gqi (priv)) +timer_shutdown_sync (&block->rx->starvation_timer); +netif_napi_del (&block->napi);disable_irq (block->irq); }
drivers/net/ethernet/google/gve/gve_rx_dqo.c +33 -0
@@ -16,6 +16,16 @@#include <net/ipv6.h> #include <net/tcp.h> +static void gve_rx_starvation_timer (struct timer_list *t) +{ +struct gve_rx_ring *rx =from_timer (rx, t, starvation_timer); +struct gve_priv *priv = rx->gve; +struct gve_notify_block *block; + + block = &priv->ntfy_blocks[rx->ntfy_id]; +napi_schedule (&block->napi); +} +static int gve_buf_ref_cnt (struct gve_rx_buf_state_dqo *bs) {return page_count (bs->page_info.page) - bs->page_info.pagecnt_bias;
@@ -185,6 +195,7 @@ static void gve_rx_free_ring_dqo(struct gve_priv *priv, int idx) completion_queue_slots = rx->dqo.complq.mask +1 ; buffer_queue_slots = rx->dqo.bufq.mask +1 ; +timer_shutdown_sync (&rx->starvation_timer);gve_rx_remove_from_block (priv, idx);if (rx->q_resources) {
@@ -237,6 +248,7 @@ static int gve_rx_alloc_ring_dqo(struct gve_priv *priv, int idx)memset (rx,0 ,sizeof (*rx)); rx->gve = priv; rx->q_num = idx; +timer_setup (&rx->starvation_timer, gve_rx_starvation_timer,0 ); rx->dqo.bufq.mask = buffer_queue_slots -1 ; rx->dqo.complq.num_free_slots = completion_queue_slots; rx->dqo.complq.mask = completion_queue_slots -1 ;
@@ -337,6 +349,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx)u32 num_avail_slots;u32 num_full_slots;u32 num_posted =0 ; +u32 num_bufs_avail_to_hw; num_full_slots = (bufq->tail - bufq->head) & bufq->mask; num_avail_slots = bufq->mask - num_full_slots;
@@ -374,6 +387,26 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) } rx->fill_cnt += num_posted; + +/* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors +* visible to the hardware, the hardware is in danger of starving +* and cannot trigger interrupts. +* +* We use a threshold of 32 because a single maximum-sized RSC +* packet can consume up to 19 descriptors in the Rx path. Lower +* thresholds (e.g., 8 or 16) would be unsafe as they could cause +* the device to drop/stall on a maximum-sized RSC packet. +* +* Start the timer to periodically reschedule NAPI and recover. +*/ + num_bufs_avail_to_hw = + ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO -1 )) - + bufq->head) & bufq->mask; + +if (num_bufs_avail_to_hw <GVE_RX_BUF_THRESH_DQO ) { +mod_timer (&rx->starvation_timer, + jiffies +msecs_to_jiffies (GVE_RX_NAPI_RESCHED_MS )); + } } static void gve_try_recycle_buf(struct gve_priv *priv, struct gve_rx_ring *rx,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 304/609 HID: logitech-dj: Standardise hid_report_enum variable nomenclature
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lee Jones <[email protected]>
[ Upstream commit a940aee176437046598dfc786b719bd96db3c74c ]
Since we will need to differentiate between the two report_enum types soon, let's unify the naming conventions now to save confusion and/or unnecessary/unrelated changes in upcoming commits.
{input,output}_report_enum is used in other places to let's conform.
--- drivers/hid/hid-logitech-dj.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/hid/hid-logitech-dj.c +6 -6
@@ -1749,7 +1749,7 @@ static int logi_dj_raw_event(struct hid_device *hdev,static int logi_dj_probe (struct hid_device *hdev,const struct hid_device_id *id) { -struct hid_report_enum *rep_enum; +struct hid_report_enum *input_report_enum;struct hid_report *rep;struct dj_receiver_dev *djrcv_dev;struct usb_interface *intf;
@@ -1793,10 +1793,10 @@ static int logi_dj_probe(struct hid_device *hdev, } } - rep_enum = &hdev->report_enum[HID_INPUT_REPORT ]; + input_report_enum = &hdev->report_enum[HID_INPUT_REPORT ];/* no input reports, bail out */ -if (list_empty (&rep_enum->report_list)) +if (list_empty (&input_report_enum->report_list))return -ENODEV ;/*
@@ -1804,7 +1804,7 @@ static int logi_dj_probe(struct hid_device *hdev,* Note: we should theoretically check for HID++ and DJ * collections, but this will do. */ -list_for_each_entry (rep, &rep_enum->report_list, list) { +list_for_each_entry (rep, &input_report_enum->report_list, list) {if (rep->application ==0xff000001 ) has_hidpp = true; }
@@ -1817,7 +1817,7 @@ static int logi_dj_probe(struct hid_device *hdev,return -ENODEV ;/* get the current application attached to the node */ - rep = list_first_entry(&rep_enum->report_list, struct hid_report, list); + rep = list_first_entry(&input_report_enum->report_list, struct hid_report, list); djrcv_dev =dj_get_receiver_dev (hdev, id->driver_data, rep->application, has_hidpp);if (!djrcv_dev) {
@@ -1825,7 +1825,7 @@ static int logi_dj_probe(struct hid_device *hdev,return -ENOMEM ; } -if (!rep_enum->numbered) +if (!input_report_enum->numbered) djrcv_dev->unnumbered_application = rep->application;/* Starts the usb device and connects to upper interfaces hiddev and
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 305/609 HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lee Jones <[email protected]>
[ Upstream commit b6a57912854e7ea36f3b270032661140cc4209cd ]
logi_dj_recv_send_report() assumes that all incoming REPORT_ID_DJ_SHORT reports are 14 Bytes (DJREPORT_SHORT_LENGTH - 1) long. It uses that assumption to load the associated field's 'value' array with 14 Bytes of data. However, if a malicious user only sends say 1 Byte of data, 'report_count' will be 1 and only 1 Byte of memory will be allocated to the 'value' Byte array. When we come to populate 'value[1-13]' we will experience an OOB write.
--- drivers/hid/hid-logitech-dj.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/hid/hid-logitech-dj.c +10 -0
@@ -1750,6 +1750,7 @@ static int logi_dj_probe(struct hid_device *hdev, const struct hid_device_id *id) {struct hid_report_enum *input_report_enum; +struct hid_report_enum *output_report_enum;struct hid_report *rep;struct dj_receiver_dev *djrcv_dev;struct usb_interface *intf;
@@ -1793,6 +1794,15 @@ static int logi_dj_probe(struct hid_device *hdev, } } + output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT ]; + rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT ]; + +if (rep->maxfield <1 || rep->field[0 ]->report_count !=DJREPORT_SHORT_LENGTH -1 ) { +hid_err (hdev,"Expected size of DJ short report is %d, but got %d" , +DJREPORT_SHORT_LENGTH -1 , rep->field[0 ]->report_count); +return -EINVAL ; + } + input_report_enum = &hdev->report_enum[HID_INPUT_REPORT ];/* no input reports, bail out */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 306/609 HID: logitech-dj: fix wrong detection of bad DJ_SHORT output report
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Tissoires <[email protected]>
[ Upstream commit 8b9a097eb2fc37b486afd81388c693bf3ab44466 ]
commit b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") assumed that all HID devices attached to the logitech-dj driver was having an output report of DJ_SHORT.
However, on the receiver itself, we have 2 other HID device we attach
here: the mouse emulation and the keyboard emulation. For those devices the value of rep is NULL and we are triggered a segfault here.
This is doubly required because logitech-dj also handles non DJ devices that might not have the DJ collection.
--- drivers/hid/hid-logitech-dj.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/hid/hid-logitech-dj.c +2 -1
@@ -1797,7 +1797,8 @@ static int logi_dj_probe(struct hid_device *hdev, output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT ]; rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT ]; -if (rep->maxfield <1 || rep->field[0 ]->report_count !=DJREPORT_SHORT_LENGTH -1 ) { +if (rep && (rep->maxfield <1 || + rep->field[0 ]->report_count !=DJREPORT_SHORT_LENGTH -1 )) {hid_err (hdev,"Expected size of DJ short report is %d, but got %d" ,DJREPORT_SHORT_LENGTH -1 , rep->field[0 ]->report_count);return -EINVAL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 307/609 net: qrtr: ns: Limit the maximum server registration per node
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manivannan Sadhasivam <[email protected]>
[ Upstream commit d5ee2ff98322337951c56398e79d51815acbf955 ]
Current code does no bound checking on the number of servers added per node. A malicious client can flood NEW_SERVER messages and exhaust memory.
Fix this issue by limiting the maximum number of server registrations to 256 per node. If the NEW_SERVER message is received for an old port, then don't restrict it as it will get replaced. While at it, also rate limit the error messages in the failure path of qrtr_ns_worker().
Note that the limit of 256 is chosen based on the current platform requirements. If requirement changes in the future, this limit can be increased.
--- net/qrtr/ns.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-)
net/qrtr/ns.c +16 -10
@@ -68,13 +68,9 @@ struct qrtr_server {struct qrtr_node {unsigned int id;struct xarray servers; +u32 server_count; }; -/* Max lookup limit is chosen based on the current platform requirements. If the -* requirement changes in the future, this value can be increased. -*/ -#define QRTR_NS_MAX_LOOKUPS 64 -/* Max nodes, server, lookup limits are chosen based on the current platform * requirements. If the requirement changes in the future, these values can be * increased.
@@ -249,6 +245,17 @@ static struct qrtr_server *server_add(unsigned int service,if (!service || !port)return NULL ; + node =node_get (node_id); +if (!node) +return NULL ; + +/* Make sure the new servers per port are capped at the maximum value */ + old =xa_load (&node->servers, port); +if (!old && node->server_count >=QRTR_NS_MAX_SERVERS ) { +pr_err_ratelimited ("QRTR client node %u exceeds max server limit!\n" , node_id); +return NULL ; + } + srv =kzalloc (sizeof (*srv),GFP_KERNEL );if (!srv)return NULL ;
@@ -258,10 +265,6 @@ static struct qrtr_server *server_add(unsigned int service, srv->node = node_id; srv->port = port; - node =node_get (node_id); -if (!node) - goto err; -/* Delete the old server on the same port */ old =xa_store (&node->servers, port, srv,GFP_KERNEL );if (old) {
@@ -272,6 +275,8 @@ static struct qrtr_server *server_add(unsigned int service, }else {kfree (old); } + }else { + node->server_count++; } trace_qrtr_ns_server_add(srv->service, srv->instance,
@@ -312,6 +317,7 @@ static int server_del(struct qrtr_node *node, unsigned int port, bool bcast) }kfree (srv); + node->server_count--;return 0 ; }
@@ -708,7 +714,7 @@ static void qrtr_ns_worker(struct work_struct *work) }if (ret <0 ) -pr_err ("failed while handling packet from %d:%d" , +pr_err_ratelimited ("failed while handling packet from %d:%d" , sq.sq_node, sq.sq_port); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 308/609 net: qrtr: ns: Raise node count limit to 512
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Youssef Samir <[email protected]>
[ Upstream commit ff194cffd586cbd4cc49eccb002c65f2a902a277 ]
The current node limit of 64 breaks the functionality for a number of AI200 deployments that have up to 384 nodes. Raise the limit to 512.
Also, the backport of commit 27d5e84e810b ("net: qrtr: ns: Limit the total number of nodes") to 5.10, 5.15 and 6.1 dropped the node_count-- hunk in ctrl_cmd_bye(). Add it back.
--- net/qrtr/ns.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
net/qrtr/ns.c +3 -2
@@ -75,11 +75,11 @@ struct qrtr_node {* requirements. If the requirement changes in the future, these values can be * increased. */ -#define QRTR_NS_MAX_NODES 64 +#define QRTR_NS_MAX_NODES 512#define QRTR_NS_MAX_SERVERS 256#define QRTR_NS_MAX_LOOKUPS 64 -static u8 node_count; +static u16 node_count;static struct qrtr_node *node_get (unsigned int node_id) {
@@ -412,6 +412,7 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from) delete_node:xa_erase (&nodes, from->sq_node);kfree (node); + node_count--;return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 309/609 pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Konrad Dybcio <[email protected]>
[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ]
Pins 143 and 151 were not included in the PDC wakeup map. They are normally used for PCIe2A and PCIe3a PERST# respectively, so they're unlikely to be excercised in practice, but still add them for the sake of completeness.
--- drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
drivers/pinctrl/qcom/pinctrl-sc8280xp.c +11 -10
@@ -1892,16 +1892,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = { { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 }, { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 }, { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 }, - { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 }, - { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 }, - { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 }, - { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 }, - { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 }, - { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 }, - { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 }, - { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 }, - { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 }, - { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 }, + { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 }, + { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 }, + { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 }, + { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 }, + { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 }, + { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 }, + { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 }, + { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 }, + { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 }, + { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 }, + { 225, 196 }, { 227, 197 }, }; static struct msm_pinctrl_soc_data sc8280xp_pinctrl = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 310/609 dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongling Zeng <[email protected]>
[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ]
When terminating DMA transfers, active descriptors are not properly reclaimed. Only cyclic descriptors were handled, leaving non-cyclic descriptors and their LLI chains to be permanently leaked.
Fix by using vchan_terminate_vdesc() which handles both cyclic and non-cyclic descriptors by adding them to desc_terminated queue for proper cleanup.
Add pchan->desc != pchan->done check to prevent double-adding completed descriptors, which would corrupt the list.
--- drivers/dma/sun6i-dma.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
drivers/dma/sun6i-dma.c +4 -7
@@ -933,16 +933,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)spin_lock_irqsave (&vchan->vc.lock, flags); -if (vchan->cyclic) { - vchan->cyclic = false; -if (pchan && pchan->desc) { -struct virt_dma_desc *vd = &pchan->desc->vd; -struct virt_dma_chan *vc = &vchan->vc; +if (pchan && pchan->desc && pchan->desc != pchan->done) { +struct virt_dma_desc *vd = &pchan->desc->vd; -list_add_tail (&vd->node, &vc->desc_completed); - } +vchan_terminate_vdesc (vd); } + vchan->cyclic = false;vchan_get_all_descriptors (&vchan->vc, &head);if (pchan) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 311/609 ata: sata_mv: accept 1 or 2 resources in platform probe
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rosen Penev <[email protected]>
[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ]
Board files in arch/arm/plat-orion, arch/arm/mach-dove, arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the "sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ). Those devices are rejected with -EINVAL, so SATA no longer probes on legacy Marvell Orion/Kirkwood-style boards.
Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2 resources (legacy, IRQ supplied as a second resource) so both probing paths work.
--- drivers/ata/sata_mv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/ata/sata_mv.c +1 -1
@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev)/* * Simple resource validation .. */ -if (unlikely (pdev->num_resources !=1 )) { +if (unlikely (pdev->num_resources !=1 && pdev->num_resources !=2 )) {dev_err (&pdev->dev,"invalid number of resources\n" );return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 312/609 ata: libahci_platform: support non-consecutive port numbers
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josua Mayer <[email protected]>
[ Upstream commit 8c87215dd3a2c814dcffc0bafe8c80c8f98f2574 ]
So far ahci_platform relied on number of child nodes in firmware to allocate arrays and expected port numbers to start from 0 without holes. This number of ports is then set in private structure for use when configuring phys and regulators.
Some platforms may not use every port of an ahci controller. E.g. SolidRUN CN9130 Clearfog uses only port 1 but not port 0, leading to the following errors during boot:
[ 1.719476] ahci f2540000.sata: invalid port number 1 [ 1.724562] ahci f2540000.sata: No port enabled
Update all accessesors of ahci_host_priv phys and target_pwrs arrays to support holes. Access is gated by hpriv->mask_port_map which has a bit set for each enabled port.
Update ahci_platform_get_resources to ignore holes in the port numbers and enable ports defined in firmware by their reg property only.
When firmware does not define children it is assumed that there is exactly one port, using index 0.
Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()")
--- drivers/ata/ahci_brcm.c | 3 +++ drivers/ata/ahci_ceva.c | 6 +++++ drivers/ata/libahci_platform.c | 40 +++++++++++++++++++++++++++++----- 3 files changed, 43 insertions(+), 6 deletions(-)
drivers/ata/ahci_brcm.c +3 -0
@@ -288,6 +288,9 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,/* Re-initialize and calibrate the PHY */ for (i =0 ; i < hpriv->nports; i++) { +if (!(hpriv->mask_port_map & (1 << i))) +continue ; + rc =phy_init (hpriv->phys[i]);if (rc) goto disable_phys;
drivers/ata/ahci_ceva.c +6 -0
@@ -206,6 +206,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) goto disable_clks;for (i =0 ; i < hpriv->nports; i++) { +if (!(hpriv->mask_port_map & (1 << i))) +continue ; + rc =phy_init (hpriv->phys[i]);if (rc) goto disable_rsts;
@@ -215,6 +218,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)ahci_platform_deassert_rsts (hpriv);for (i =0 ; i < hpriv->nports; i++) { +if (!(hpriv->mask_port_map & (1 << i))) +continue ; + rc =phy_power_on (hpriv->phys[i]);if (rc) {phy_exit (hpriv->phys[i]);
drivers/ata/libahci_platform.c +34 -6
@@ -48,6 +48,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)int rc, i;for (i =0 ; i < hpriv->nports; i++) { +if (!(hpriv->mask_port_map & (1 << i))) +continue ; + rc =phy_init (hpriv->phys[i]);if (rc) goto disable_phys;
@@ -69,6 +72,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) disable_phys:while (--i >=0 ) { +if (!(hpriv->mask_port_map & (1 << i))) +continue ; +phy_power_off (hpriv->phys[i]);phy_exit (hpriv->phys[i]); }
@@ -87,6 +93,9 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)int i;for (i =0 ; i < hpriv->nports; i++) { +if (!(hpriv->mask_port_map & (1 << i))) +continue ; +phy_power_off (hpriv->phys[i]);phy_exit (hpriv->phys[i]); }
@@ -434,6 +443,20 @@ static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv,return 0 ; } +static u32 ahci_platform_find_max_port_id (struct device *dev) +{ +u32 max_port =0 ; + +for_each_child_of_node_scoped (dev->of_node, child) { +u32 port; + +if (!of_property_read_u32 (child,"reg" , &port)) + max_port =max (max_port, port); + } + +return max_port; +} +/** * ahci_platform_get_resources - Get platform resources * @pdev: platform device to get resources for
@@ -461,6 +484,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,struct ahci_host_priv *hpriv;struct device_node *child;u32 mask_port_map =0 ; +u32 max_port;if (!devres_open_group (dev,NULL ,GFP_KERNEL ))return ERR_PTR (-ENOMEM );
@@ -552,15 +576,17 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, goto err_out; } +/* find maximum port id for allocating structures */ + max_port =ahci_platform_find_max_port_id (dev);/* -* If no sub-node was found, we still need to set nports to -* one in order to be able to use the +* Set nports according to maximum port id. Clamp at +* AHCI_MAX_PORTS, warning message for invalid port id +* is generated later. +* When DT has no sub-nodes max_port is 0, nports is 1, +* in order to be able to use the * ahci_platform_[en|dis]able_[phys|regulators] functions. */ -if (child_nodes) - hpriv->nports = child_nodes; -else - hpriv->nports =1 ; + hpriv->nports =min (AHCI_MAX_PORTS , max_port +1 ); hpriv->phys =devm_kcalloc (dev, hpriv->nports,sizeof (*hpriv->phys),GFP_KERNEL );if (!hpriv->phys) {
@@ -633,6 +659,8 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev,* If no sub-node was found, keep this for device tree * compatibility */ + hpriv->mask_port_map |=BIT (0 ); + rc =ahci_platform_get_phy (hpriv,0 , dev, dev->of_node);if (rc) goto err_out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 313/609 ahci: Introduce ahci_ignore_port() helper
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Damien Le Moal <[email protected]>
[ Upstream commit c9b5be909e6595547ed5d45aef39fd65948aa342 ]
libahci and AHCI drivers may ignore some ports if the port is invalid (its ID does not correspond to a valid physical port) or if the user explicitly requested the port to be ignored with the mask_port_map ahci module parameter. Such port that shall be ignored can be identified by checking that the bit corresponding to the port ID is not set in the mask_port_map field of struct ahci_host_priv. E.g. code such as:
"if (!(hpriv->mask_port_map & (1 << portid)))".
Replace all direct use of the mask_port_map field to detect such port with the new helper inline function ahci_ignore_port() to make the code more readable/easier to understand.
The comment describing the mask_port_map field of struct ahci_host_priv is also updated to be more accurate.
Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()")
--- drivers/ata/ahci.h | 13 ++++++++++++- drivers/ata/ahci_brcm.c | 2 +- drivers/ata/ahci_ceva.c | 4 ++-- drivers/ata/libahci_platform.c | 6 +++--- 4 files changed, 18 insertions(+), 7 deletions(-)
drivers/ata/ahci.h +12 -1
@@ -330,7 +330,7 @@ struct ahci_port_priv {struct ahci_host_priv {/* Input fields */ unsigned int flags;/* AHCI_HFLAG_* */ -u32 mask_port_map;/* mask out particular bits */ +u32 mask_port_map;/* Mask of valid ports */ void __iomem * mmio;/* bus-independent mem map */ u32 cap;/* cap to use */
@@ -381,6 +381,17 @@ struct ahci_host_priv { int port); }; +/* +* Return true if a port should be ignored because it is excluded from +* the host port map. +*/ +static inline bool ahci_ignore_port (struct ahci_host_priv *hpriv, +unsigned int portid) +{ +return portid >= hpriv->nports || + !(hpriv->mask_port_map & (1 << portid)); +} +extern int ahci_ignore_sss;extern const struct attribute_group *ahci_shost_groups[];
drivers/ata/ahci_brcm.c +1 -1
@@ -288,7 +288,7 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev,/* Re-initialize and calibrate the PHY */ for (i =0 ; i < hpriv->nports; i++) { -if (!(hpriv->mask_port_map & (1 << i))) +if (ahci_ignore_port (hpriv, i))continue ; rc =phy_init (hpriv->phys[i]);
drivers/ata/ahci_ceva.c +2 -2
@@ -206,7 +206,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) goto disable_clks;for (i =0 ; i < hpriv->nports; i++) { -if (!(hpriv->mask_port_map & (1 << i))) +if (ahci_ignore_port (hpriv, i))continue ; rc =phy_init (hpriv->phys[i]);
@@ -218,7 +218,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)ahci_platform_deassert_rsts (hpriv);for (i =0 ; i < hpriv->nports; i++) { -if (!(hpriv->mask_port_map & (1 << i))) +if (ahci_ignore_port (hpriv, i))continue ; rc =phy_power_on (hpriv->phys[i]);
drivers/ata/libahci_platform.c +3 -3
@@ -48,7 +48,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv)int rc, i;for (i =0 ; i < hpriv->nports; i++) { -if (!(hpriv->mask_port_map & (1 << i))) +if (ahci_ignore_port (hpriv, i))continue ; rc =phy_init (hpriv->phys[i]);
@@ -72,7 +72,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) disable_phys:while (--i >=0 ) { -if (!(hpriv->mask_port_map & (1 << i))) +if (ahci_ignore_port (hpriv, i))continue ;phy_power_off (hpriv->phys[i]);
@@ -93,7 +93,7 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv)int i;for (i =0 ; i < hpriv->nports; i++) { -if (!(hpriv->mask_port_map & (1 << i))) +if (ahci_ignore_port (hpriv, i))continue ;phy_power_off (hpriv->phys[i]);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 314/609 ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Radhey Shyam Pandey <[email protected]>
[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ]
On phy_init() failure the error path fallsthrough to disable_rsts, which deasserts the controller reset and then enters disable_phys calling phy_power_off() on PHYs that were never powered on. That corrupts the PHY power_count and triggers an extra runtime PM put.
Use a separate exit_phys path that unwinds with phy_exit() only and falls through to disable_clks while the controller remains in reset. Reserve phy_power_off() for the phy_power_on() failure path only, and skip masked-out ports in both unwind loops.
On phy_power_on() failure re-assert the controller reset before disabling clocks and regulators, matching the teardown order used by ahci_platform_enable_resources() and ahci_platform_disable_resources().
--- drivers/ata/ahci_ceva.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
drivers/ata/ahci_ceva.c +14 -4
@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) rc =phy_init (hpriv->phys[i]);if (rc) - goto disable_rsts; + goto exit_phys; }/* De-assert the controller reset */
@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)return 0 ; -disable_rsts: -ahci_platform_deassert_rsts (hpriv); - disable_phys:while (--i >=0 ) { +if (ahci_ignore_port (hpriv, i)) +continue ; +phy_power_off (hpriv->phys[i]);phy_exit (hpriv->phys[i]); } +ahci_platform_assert_rsts (hpriv); + goto disable_clks; + +exit_phys: +while (--i >=0 ) { +if (ahci_ignore_port (hpriv, i)) +continue ; + +phy_exit (hpriv->phys[i]); + } disable_clks:ahci_platform_disable_clks (hpriv);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 315/609 ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Uday Khare <[email protected]>
[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ]
In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is broken due to a missing IS_ERR() guard.
The code intends to return -EPROBE_DEFER only when the clock lookup fails with that specific error. However, without IS_ERR() the check:
if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
is called unconditionally, including when devm_clk_get() succeeds and returns a valid pointer. Calling PTR_ERR() on a valid pointer reinterprets its address as a signed long; the result is arbitrary and is almost never equal to -EPROBE_DEFER, so the check silently does nothing in the success case. When devm_clk_get() fails with any error other than -EPROBE_DEFER the check is also skipped, leaving max98095->mclk holding an error pointer with no indication to the caller.
This means a deferred probe will never actually be triggered for this device, and any non-EPROBE_DEFER clock error is silently swallowed with the error pointer left in the mclk field.
Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, matching the pattern already used in the sibling max98088 and wm8960 drivers.
--- sound/soc/codecs/max98095.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
sound/soc/codecs/max98095.c +3 -2
@@ -1984,8 +1984,9 @@ static int max98095_probe(struct snd_soc_component *component)int ret =0 ; max98095->mclk =devm_clk_get (component->dev,"mclk" ); -if (PTR_ERR (max98095->mclk) == -EPROBE_DEFER ) -return -EPROBE_DEFER ; +if (IS_ERR (max98095->mclk)) +if (PTR_ERR (max98095->mclk) == -EPROBE_DEFER ) +return -EPROBE_DEFER ;/* reset the codec, the DSP core, and disable all interrupts */ max98095_reset (component);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 316/609 ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Uday Khare <[email protected]>
[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ]
In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is broken due to a missing IS_ERR() guard.
The code intends to return -EPROBE_DEFER only when the clock lookup fails with that specific error. However, without IS_ERR() the check:
if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
is called unconditionally, including when devm_clk_get() succeeds and returns a valid pointer. Calling PTR_ERR() on a valid pointer reinterprets its address as a signed long; the result is arbitrary and is almost never equal to -EPROBE_DEFER, so the check silently does nothing in the success case. When devm_clk_get() fails with any error other than -EPROBE_DEFER the check is also skipped, leaving max98090->mclk holding an error pointer with no indication to the caller.
This means a deferred probe will never actually be triggered for this device, and any non-EPROBE_DEFER clock error is silently swallowed with the error pointer left in the mclk field.
Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, matching the pattern already used in the sibling max98088 and wm8960 drivers.
--- sound/soc/codecs/max98090.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
sound/soc/codecs/max98090.c +3 -2
@@ -2391,8 +2391,9 @@ static int max98090_probe(struct snd_soc_component *component)dev_dbg (component->dev,"max98090_probe\n" ); max98090->mclk =devm_clk_get (component->dev,"mclk" ); -if (PTR_ERR (max98090->mclk) == -EPROBE_DEFER ) -return -EPROBE_DEFER ; +if (IS_ERR (max98090->mclk)) +if (PTR_ERR (max98090->mclk) == -EPROBE_DEFER ) +return -EPROBE_DEFER ; max98090->component = component;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 317/609 phy: zynqmp: Allow variation in refclk rate
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Anderson <[email protected]>
[ Upstream commit 76009ee76e05e30e29aade02e788aebe9ce9ffd2 ]
Due to limited available frequency ratios, the reference clock rate may not be exactly the same as the required rate. Allow a small (100 ppm) deviation.
Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()")
--- drivers/phy/xilinx/phy-zynqmp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/phy/xilinx/phy-zynqmp.c +4 -1
@@ -958,7 +958,10 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) rate =clk_get_rate (clk);for (i =0 ; i <ARRAY_SIZE (ssc_lookup); i++) { -if (rate == ssc_lookup[i].refclk_rate) { +/* Allow an error of 100 ppm */ +unsigned long error = ssc_lookup[i].refclk_rate /10000 ; + +if (abs (rate - ssc_lookup[i].refclk_rate) < error) { gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i];break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 318/609 phy-zynqmp: Postpone getting clock rate until actually needed
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mike Looijmans <[email protected]>
[ Upstream commit 065d5885f6180c534b7b176847b3e008f4e11850 ]
At probe time the driver would display the following error and abort:
xilinx-psgtr fd400000.phy: Invalid rate 0 for reference clock 0
At probe time, the associated GTR driver (e.g. SATA or PCIe) hasn't initialized the clock yet, so clk_get_rate() likely returns 0 if the clock is programmable. So this driver only works if the clock is fixed.
The PHY driver doesn't need to know the clock frequency at probe yet, so wait until the associated driver initializes the lane before requesting the clock rate setting.
In addition to allowing the driver to be used with programmable clocks, this also reduces the driver's runtime memory footprint by removing an array of pointers from struct xpsgtr_phy.
Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()")
--- drivers/phy/xilinx/phy-zynqmp.c | 70 +++++++++++++++++---------------- 1 file changed, 37 insertions(+), 33 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +37 -33
@@ -228,7 +228,6 @@ struct xpsgtr_phy {* @siou: siou base address * @gtr_mutex: mutex for locking * @phys: PHY lanes -* @refclk_sscs: spread spectrum settings for the reference clocks * @clk: reference clocks * @tx_term_fix: fix for GT issue * @saved_icm_cfg0: stored value of ICM CFG0 register
@@ -241,7 +240,6 @@ struct xpsgtr_dev {void __iomem *siou;struct mutex gtr_mutex;/* mutex for locking */ struct xpsgtr_phy phys[NUM_LANES ]; -const struct xpsgtr_ssc *refclk_sscs[NUM_LANES ];struct clk *clk[NUM_LANES ];bool tx_term_fix;unsigned int saved_icm_cfg0;
@@ -384,13 +382,40 @@ static int xpsgtr_wait_pll_lock(struct phy *phy)return ret; } +/* Get the spread spectrum (SSC) settings for the reference clock rate */ +static const struct xpsgtr_ssc *xpsgtr_find_sscs (struct xpsgtr_phy *gtr_phy) +{ +unsigned long rate; +struct clk *clk; +unsigned int i; + + clk = gtr_phy->dev->clk[gtr_phy->refclk]; + rate =clk_get_rate (clk); + +for (i =0 ; i <ARRAY_SIZE (ssc_lookup); i++) { +/* Allow an error of 100 ppm */ +unsigned long error = ssc_lookup[i].refclk_rate /10000 ; + +if (abs (rate - ssc_lookup[i].refclk_rate) < error) +return &ssc_lookup[i]; + } + +dev_err (gtr_phy->dev->dev,"Invalid rate %lu for reference clock %u\n" , + rate, gtr_phy->refclk); + +return NULL ; +} +/* Configure PLL and spread-sprectrum clock. */ -static void xpsgtr_configure_pll (struct xpsgtr_phy *gtr_phy) +static int xpsgtr_configure_pll (struct xpsgtr_phy *gtr_phy) {const struct xpsgtr_ssc *ssc;u32 step_size; - ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk]; + ssc =xpsgtr_find_sscs (gtr_phy); +if (!ssc) +return -EINVAL ; + step_size = ssc->step_size; xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane),
@@ -432,6 +457,8 @@ static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)xpsgtr_clr_set_phy (gtr_phy,L0_PLL_SS_STEP_SIZE_3_MSB ,STEP_SIZE_3_MASK , (step_size &STEP_SIZE_3_MASK ) |FORCE_STEP_SIZE |FORCE_STEPS ); + +return 0 ; }/* Configure the lane protocol. */
@@ -644,7 +671,10 @@ static int xpsgtr_phy_init(struct phy *phy)* Configure the PLL, the lane protocol, and perform protocol-specific * initialization. */ -xpsgtr_configure_pll (gtr_phy); + ret =xpsgtr_configure_pll (gtr_phy); +if (ret) + goto out; +xpsgtr_lane_set_protocol (gtr_phy);switch (gtr_phy->protocol) {
@@ -855,8 +885,7 @@ static struct phy *xpsgtr_xlate(struct device *dev, } refclk = args->args[3 ]; -if (refclk >=ARRAY_SIZE (gtr_dev->refclk_sscs) || - !gtr_dev->refclk_sscs[refclk]) { +if (refclk >=ARRAY_SIZE (gtr_dev->clk)) {dev_err (dev,"Invalid reference clock number %u\n" , refclk);return ERR_PTR (-EINVAL ); }
@@ -932,9 +961,7 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) {unsigned int refclk; -for (refclk =0 ; refclk <ARRAY_SIZE (gtr_dev->refclk_sscs); ++refclk) { -unsigned long rate; -unsigned int i; +for (refclk =0 ; refclk <ARRAY_SIZE (gtr_dev->clk); ++refclk) {struct clk *clk;char name[8 ];
@@ -950,29 +977,6 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)continue ; gtr_dev->clk[refclk] = clk; - -/* -* Get the spread spectrum (SSC) settings for the reference -* clock rate. -*/ - rate =clk_get_rate (clk); - -for (i =0 ; i <ARRAY_SIZE (ssc_lookup); i++) { -/* Allow an error of 100 ppm */ -unsigned long error = ssc_lookup[i].refclk_rate /10000 ; - -if (abs (rate - ssc_lookup[i].refclk_rate) < error) { - gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; -break ; - } - } - -if (i ==ARRAY_SIZE (ssc_lookup)) { -dev_err (gtr_dev->dev, -"Invalid rate %lu for reference clock %u\n" , - rate, refclk); -return -EINVAL ; - } }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 319/609 phy: zynqmp: fix clock error handling in xpsgtr_phy_init()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Radhey Shyam Pandey <[email protected]>
[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ]
Propagate clk_prepare_enable() failures to the caller instead of returning success, and disable the reference clock on initialization error paths to avoid leaking clock references when phy_exit() is not called.
--- drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +9 -4
@@ -644,12 +644,13 @@ static int xpsgtr_phy_init(struct phy *phy) {struct xpsgtr_phy *gtr_phy =phy_get_drvdata (phy);struct xpsgtr_dev *gtr_dev = gtr_phy->dev; -int ret =0 ; +int ret;mutex_lock (>r_dev->gtr_mutex);/* Configure and enable the clock when peripheral phy_init call */ -if (clk_prepare_enable (gtr_dev->clk[gtr_phy->refclk])) + ret =clk_prepare_enable (gtr_dev->clk[gtr_phy->refclk]); +if (ret) goto out;/* Skip initialization if not required. */
@@ -659,7 +660,7 @@ static int xpsgtr_phy_init(struct phy *phy)if (gtr_dev->tx_term_fix) { ret =xpsgtr_phy_tx_term_fix (gtr_phy);if (ret <0 ) - goto out; + goto out_disable_clk; gtr_dev->tx_term_fix = false; }
@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy)*/ ret =xpsgtr_configure_pll (gtr_phy);if (ret) - goto out; + goto out_disable_clk;xpsgtr_lane_set_protocol (gtr_phy);
@@ -691,6 +692,10 @@ static int xpsgtr_phy_init(struct phy *phy)break ; } + goto out; + +out_disable_clk: +clk_disable_unprepare (gtr_dev->clk[gtr_phy->refclk]); out:mutex_unlock (>r_dev->gtr_mutex);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 320/609 phy: zynqmp: fix runtime PM leak on probe allocation failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Radhey Shyam Pandey <[email protected]>
[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ]
Allocate saved_regs before pm_runtime_resume_and_get() so a devm_kmalloc() failure does not leave an unreleased runtime PM usage counter.
--- drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +6 -6
@@ -1046,6 +1046,12 @@ static int xpsgtr_probe(struct platform_device *pdev)return PTR_ERR (provider); } + gtr_dev->saved_regs =devm_kmalloc (gtr_dev->dev, +sizeof (save_reg_address), +GFP_KERNEL ); +if (!gtr_dev->saved_regs) +return -ENOMEM ; +pm_runtime_set_active (gtr_dev->dev);pm_runtime_enable (gtr_dev->dev);
@@ -1055,12 +1061,6 @@ static int xpsgtr_probe(struct platform_device *pdev)return ret; } - gtr_dev->saved_regs =devm_kmalloc (gtr_dev->dev, -sizeof (save_reg_address), -GFP_KERNEL ); -if (!gtr_dev->saved_regs) -return -ENOMEM ; -return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 321/609 netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in sip_help_tcp()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ]
sip_help_tcp() stores the size change of each NAT-rewritten SIP message in s16 diff and accumulates it in s16 tdiff, but a single message can grow by more than S16_MAX while the packet stays under the 65535 enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long Contact list expands the message by tens of kilobytes. diff then wraps, and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, so the next iteration's ct_sip_get_header() reads past the linearized skb tail.
Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the 65535 byte packet limit, and the seqadj core is already s32 (nf_ct_seqadj_set() takes s32), so no previously accepted input is rejected.
BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) nf_confirm (net/netfilter/nf_conntrack_proto.c:183) nf_hook_slow (net/netfilter/core.c:619) ip6_output (net/ipv6/ip6_output.c:246) ip6_forward (net/ipv6/ip6_output.c:690) ipv6_rcv (net/ipv6/ip6_input.c:351) __netif_receive_skb_one_core (net/core/dev.c:6212) process_backlog (net/core/dev.c:6676) __napi_poll (net/core/dev.c:7735) net_rx_action (net/core/dev.c:7955) handle_softirqs (kernel/softirq.c:622) run_ksoftirqd (kernel/softirq.c:1076) ...
--- include/linux/netfilter/nf_conntrack_sip.h | 2 +- net/netfilter/nf_conntrack_sip.c | 2 +- net/netfilter/nf_nat_sip.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
include/linux/netfilter/nf_conntrack_sip.h +1 -1
@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { unsigned int *datalen);void (*seq_adjust)(struct sk_buff *skb, -unsigned int protoff,s16 off); +unsigned int protoff,s32 off); unsigned int (*expect)(struct sk_buff *skb, unsigned int protoff,
net/netfilter/nf_conntrack_sip.c +1 -1
@@ -1627,7 +1627,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,unsigned int matchoff, matchlen;unsigned int msglen, origlen;const char *dptr, *end; -s16 diff, tdiff =0 ; +s32 diff, tdiff =0 ;int ret =NF_ACCEPT ;unsigned long clen;bool term;
net/netfilter/nf_nat_sip.c +1 -1
@@ -315,7 +315,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, }static void nf_nat_sip_seq_adjust (struct sk_buff *skb,unsigned int protoff, -s16 off) +s32 off) {enum ip_conntrack_info ctinfo;struct nf_conn *ct =nf_ct_get (skb, &ctinfo);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 322/609 drm/mediatek: Check CRTC state before freeing
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ruoyu Wang <[email protected]>
[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ]
mtk_crtc_reset() destroys the current CRTC state only when crtc->state is non-NULL, but it always converts crtc->state to struct mtk_crtc_state and passes the result to kfree().
When reset is called without an existing state, container_of(NULL, ...) does not produce NULL. Keep the mtk state free in the same crtc->state guard as the helper state destruction.
This issue was found by a static analysis checker and confirmed by manual source review.
--- drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpu/drm/mediatek/mtk_drm_crtc.c +3 -3
@@ -183,10 +183,10 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc) {struct mtk_crtc_state *state; -if (crtc->state) +if (crtc->state) {__drm_atomic_helper_crtc_destroy_state (crtc->state); - -kfree (to_mtk_crtc_state (crtc->state)); +kfree (to_mtk_crtc_state (crtc->state)); + } crtc->state =NULL ; state =kzalloc (sizeof (*state),GFP_KERNEL );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 323/609 keys: fix out-of-bounds read in keyring_get_key_chunk()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ]
For description-level chunks keyring_get_key_chunk() advances the read pointer by level * sizeof(long) past the inline prefix but only bounds-checks the prefix, so a long enough key description is read past its kmemdup(desc, desc_len + 1) allocation. Compute the full byte offset and bounds-check the description against it before reading.
The walk only reaches a description-level chunk when two keys collide through the hash, x, type and domain_tag chunks, so this is reached from an unprivileged add_key(2) with a crafted pair of same-type keys whose index hashes collide; KASAN reports a slab-out-of-bounds read.
--- security/keys/keyring.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
security/keys/keyring.c +5 -4
@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)unsigned long chunk =0 ;const u8 *d;int desc_len = index_key->desc_len, n =sizeof (chunk); +unsigned int offset; level /=ASSOC_ARRAY_KEY_CHUNK_SIZE ;switch (level) {
@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)return (unsigned long )index_key->domain_tag;default : level -=4 ; -if (desc_len <=sizeof (index_key->desc)) + offset =sizeof (index_key->desc) + level *sizeof (long ); +if (desc_len <= offset)return 0 ; - d = index_key->description +sizeof (index_key->desc); - d += level *sizeof (long ); - desc_len -=sizeof (index_key->desc); + d = index_key->description + offset; + desc_len -= offset;if (desc_len > n) desc_len = n; do {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 324/609 keys: make keyring key-chunk byte order agree with keyring_diff_objects()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ]
keyring_get_key_chunk() loads description bytes into the index chunk low address first, while keyring_diff_objects() numbers the first differing bit from the low end and folds the absolute byte index into the level without removing the inline-prefix offset the level already carries. The two disagree on byte order and bit position, so the array can be told two keys first differ at a bit that does not differ in the chunk the walker uses, letting crafted descriptions collide into one node.
Load the chunk in the order keyring_diff_objects() assumes and drop the inline-prefix length when folding the byte index into the level. This only changes the in-memory ordering used to place keys within a keyring; add, search and read of non-colliding keys are unaffected.
--- security/keys/keyring.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
security/keys/keyring.c +3 -2
@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) desc_len -= offset;if (desc_len > n) desc_len = n; + d += desc_len;do { chunk <<=8 ; - chunk |= *d++; + chunk |= *--d; }while (--desc_len >0 );return chunk; }
@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data)return -1 ; differ_plus_i: - level += i; + level += i - (int )sizeof (a->desc); differ: i = level *8 +__ffs (seg_a ^ seg_b);return i;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 325/609 assoc_array: trim the final shortcut word using the current chunk end
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ]
assoc_array_walk() masks off the bits past shortcut->skip_to_level in the word that contains skip_to_level, gated onround_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level.
That guard is wrong in two opposite ways:
- When sc_level is word-aligned (every word after the first) round_up() is a no-op, so the guard is sc_level > skip_to_level and never fires for the word that holds skip_to_level. A shortcut that spans more than one word and ends in the middle of its last word leaves that word untrimmed, and its stale high bits leak into the dissimilarity word and can steer the walk down the wrong descendant.
- When sc_level is unaligned (the first word) and skip_to_level sits on the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears the whole dissimilarity word and makes a differing shortcut compare equal.
Use the end of the chunk that contains sc_level instead:
skip_to_level < round_down(sc_level, CHUNK) + CHUNK
For an aligned sc_level whose word holds skip_to_level this now fires (the first bug); for an unaligned sc_level with skip_to_level on the following boundary it does not, so shift is never 0 when the branch runs and the trim never clears the whole word.
--- lib/assoc_array.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
lib/assoc_array.c +2 -1
@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array, sc_segments = shortcut->index_key[sc_level >>ASSOC_ARRAY_KEY_CHUNK_SHIFT ]; dissimilarity = segments ^ sc_segments; -if (round_up (sc_level,ASSOC_ARRAY_KEY_CHUNK_SIZE ) > shortcut->skip_to_level) { +if (shortcut->skip_to_level <round_down (sc_level, +ASSOC_ARRAY_KEY_CHUNK_SIZE ) +ASSOC_ARRAY_KEY_CHUNK_SIZE ) {/* Trim segments that are beyond the shortcut */ int shift = shortcut->skip_to_level &ASSOC_ARRAY_KEY_CHUNK_MASK ; dissimilarity &= ~(ULONG_MAX << shift);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 326/609 netfilter: xt_hashlimit: validate hashtable supports XT_HASHLIMIT_RATE_MATCH
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ]
The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the dsthash_ent structure which represents an entry in the hashtable. There is a union area which uses a different layout to express the rate match mode.
Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode flag is requested by two or more different rules that refer to the same hashtable. Otherwise, uninitialized access to the burst field in the union is possible.
Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by revision less than 3 too.
--- net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
net/netfilter/xt_hashlimit.c +15 -1
@@ -118,6 +118,7 @@ struct xt_hashlimit_htable {refcount_t use;u_int8_t family;bool rnd_initialized; +bool ratematch;struct hashlimit_cfg3 cfg;/* config */
@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,vfree (hinfo);return -ENOMEM ; } + hinfo->ratematch = !!(cfg->mode &XT_HASHLIMIT_RATE_MATCH );spin_lock_init (&hinfo->lock);switch (revision) {
@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, }/* Check for overflow. */ -if (revision >=3 && cfg->mode &XT_HASHLIMIT_RATE_MATCH ) { +if (cfg->mode &XT_HASHLIMIT_RATE_MATCH ) { +if (revision <3 ) +return -EINVAL ; +if (cfg->avg ==0 || cfg->avg >U32_MAX ) {pr_info_ratelimited ("invalid rate\n" );return -ERANGE ;
@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,mutex_unlock (&hashlimit_mutex);return ret; } + }else { +if ((cfg->mode &XT_HASHLIMIT_RATE_MATCH && + !(*hinfo)->ratematch) || + (!(cfg->mode &XT_HASHLIMIT_RATE_MATCH ) && + (*hinfo)->ratematch)) { +mutex_unlock (&hashlimit_mutex); +htable_put (*hinfo); +return -EINVAL ; + } }mutex_unlock (&hashlimit_mutex);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 327/609 ipv6: introduce dst_rt6_info() helper
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 e8dfd42c17faf183415323db1ef0c977be0d6489 ]
Instead of (struct rt6_info *)dst casts, we can use :
#define dst_rt6_info(_ptr) \
container_of_const(_ptr, struct rt6_info, dst)
Some places needed missing const qualifiers :
ip6_confirm_neigh(), ipv6_anycast_destination(),ipv6_unicast_destination(), has_gateway()
v2: added missing parts (David Ahern)
Stable-dep-of: e876b75b9020 ("ipvs: fix the checksum validations")
--- drivers/infiniband/core/addr.c | 6 ++-- .../ethernet/mellanox/mlxsw/spectrum_span.c | 2 +- drivers/net/vrf.c | 2 +- drivers/net/vxlan/vxlan_core.c | 2 +- drivers/s390/net/qeth_core.h | 4 +-- include/net/ip6_fib.h | 6 ++-- include/net/ip6_route.h | 11 ++++---- net/bluetooth/6lowpan.c | 2 +- net/core/dst_cache.c | 2 +- net/core/filter.c | 2 +- net/ipv4/ip_tunnel.c | 2 +- net/ipv6/icmp.c | 8 +++--- net/ipv6/ila/ila_lwt.c | 4 +-- net/ipv6/ip6_output.c | 18 ++++++------ net/ipv6/ip6mr.c | 2 +- net/ipv6/ndisc.c | 2 +- net/ipv6/ping.c | 2 +- net/ipv6/raw.c | 4 +-- net/ipv6/route.c | 28 +++++++++---------- net/ipv6/tcp_ipv6.c | 4 +-- net/ipv6/udp.c | 11 +++----- net/ipv6/xfrm6_policy.c | 2 +- net/l2tp/l2tp_ip6.c | 2 +- net/mpls/mpls_iptunnel.c | 2 +- net/netfilter/ipvs/ip_vs_xmit.c | 14 +++++----- net/netfilter/nf_flow_table_core.c | 8 ++---- net/netfilter/nf_flow_table_ip.c | 4 +-- net/netfilter/nft_rt.c | 2 +- net/sctp/ipv6.c | 2 +- net/xfrm/xfrm_policy.c | 3 +- 30 files changed, 77 insertions(+), 86 deletions(-)
drivers/infiniband/core/addr.c +3 -3
@@ -338,15 +338,15 @@ static int dst_fetch_ha(const struct dst_entry *dst,static bool has_gateway (const struct dst_entry *dst,sa_family_t family) { -struct rtable *rt; -struct rt6_info *rt6; +const struct rtable *rt; +const struct rt6_info *rt6;if (family ==AF_INET ) { rt = container_of(dst, struct rtable, dst);return rt->rt_uses_gateway; } - rt6 = container_of(dst, struct rt6_info, dst); + rt6 =dst_rt6_info (dst);return rt6->rt6i_flags &RTF_GATEWAY ; }
drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c +1 -1
@@ -538,7 +538,7 @@ mlxsw_sp_span_gretap6_route(const struct net_device *to_dev,if (!dst || dst->error) goto out; - rt6 = container_of(dst, struct rt6_info, dst); + rt6 =dst_rt6_info (dst); dev = dst->dev; *saddrp = fl6.saddr;
drivers/net/vrf.c +1 -1
@@ -655,7 +655,7 @@ static int vrf_finish_output6(struct net *net, struct sock *sk, skb->dev = dev;rcu_read_lock (); - nexthop =rt6_nexthop ((struct rt6_info *)dst, &ipv6_hdr (skb)->daddr); + nexthop =rt6_nexthop (dst_rt6_info (dst), &ipv6_hdr (skb)->daddr); neigh =__ipv6_neigh_lookup_noref (dst->dev, nexthop);if (unlikely (!neigh)) neigh =__neigh_create (&nd_tbl, nexthop, dst->dev, false);
drivers/net/vxlan/vxlan_core.c +1 -1
@@ -2715,7 +2715,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, }if (!info) { -u32 rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags; +u32 rt6i_flags =dst_rt6_info (ndst)->rt6i_flags; err = encap_bypass_if_local(skb, dev, vxlan, dst, dst_port, ifindex, vni,
drivers/s390/net/qeth_core.h +2 -2
@@ -956,7 +956,7 @@ static inline struct dst_entry *qeth_dst_check_rcu(struct sk_buff *skb,struct dst_entry *dst =skb_dst (skb);struct rt6_info *rt; - rt = (struct rt6_info *) dst; + rt =dst_rt6_info (dst);if (dst) {if (proto ==htons (ETH_P_IPV6 )) dst =dst_check (dst,rt6_get_cookie (rt));
@@ -978,7 +978,7 @@ static inline __be32 qeth_next_hop_v4_rcu(struct sk_buff *skb,static inline struct in6_addr *qeth_next_hop_v6_rcu (struct sk_buff *skb,struct dst_entry *dst) { -struct rt6_info *rt = (struct rt6_info *) dst; +struct rt6_info *rt =dst_rt6_info (dst);if (rt && !ipv6_addr_any (&rt->rt6i_gateway))return &rt->rt6i_gateway;
include/net/ip6_fib.h +4 -2
@@ -240,9 +240,11 @@ struct fib6_result {for (rt = (w)->leaf; rt; \ rt = rcu_dereference_protected(rt->fib6_next, 1)) -static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst) +#define dst_rt6_info (_ptr) container_of_const(_ptr, struct rt6_info, dst) + +static inline struct inet6_dev *ip6_dst_idev (const struct dst_entry *dst) { -return ((struct rt6_info *)dst)->rt6i_idev; +return dst_rt6_info (dst)->rt6i_idev; }static inline bool fib6_requires_src (const struct fib6_info *rt)
include/net/ip6_route.h +5 -6
@@ -222,12 +222,11 @@ void rt6_uncached_list_del(struct rt6_info *rt);static inline const struct rt6_info *skb_rt6_info (const struct sk_buff *skb) {const struct dst_entry *dst =skb_dst (skb); -const struct rt6_info *rt6 =NULL ;if (dst) - rt6 = container_of(dst, struct rt6_info, dst); +return dst_rt6_info (dst); -return rt6; +return NULL ; }/*
@@ -239,7 +238,7 @@ static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, {struct ipv6_pinfo *np =inet6_sk (sk); - np->dst_cookie =rt6_get_cookie ((struct rt6_info *)dst); + np->dst_cookie =rt6_get_cookie (dst_rt6_info (dst));sk_setup_caps (sk, dst); np->daddr_cache = daddr;#ifdef CONFIG_IPV6_SUBTREES
@@ -252,7 +251,7 @@ void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst,static inline bool ipv6_unicast_destination (const struct sk_buff *skb) { -struct rt6_info *rt = (struct rt6_info *)skb_dst (skb); +const struct rt6_info *rt =dst_rt6_info (skb_dst (skb));return rt->rt6i_flags &RTF_LOCAL ; }
@@ -260,7 +259,7 @@ static inline bool ipv6_unicast_destination(const struct sk_buff *skb)static inline bool ipv6_anycast_destination (const struct dst_entry *dst,const struct in6_addr *daddr) { -struct rt6_info *rt = (struct rt6_info *)dst; +const struct rt6_info *rt =dst_rt6_info (dst); return rt->rt6i_flags & RTF_ANYCAST || (rt->rt6i_dst.plen < 127 &&
net/bluetooth/6lowpan.c +1 -1
@@ -140,7 +140,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,struct in6_addr *daddr, struct sk_buff *skb) { - struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); + struct rt6_info *rt = dst_rt6_info(skb_dst(skb));int count =atomic_read (&dev->peer_count);const struct in6_addr *nexthop;struct lowpan_peer *peer;
net/core/dst_cache.c +1 -1
@@ -112,7 +112,7 @@ void dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst, idst =this_cpu_ptr (dst_cache->cache);dst_cache_per_cpu_dst_set (this_cpu_ptr (dst_cache->cache), dst, -rt6_get_cookie ((struct rt6_info *)dst)); +rt6_get_cookie (dst_rt6_info (dst))); idst->in6_saddr = *saddr; }EXPORT_SYMBOL_GPL (dst_cache_set_ip6);
net/core/filter.c +1 -1
@@ -2226,7 +2226,7 @@ static int bpf_out_neigh_v6(struct net *net, struct sk_buff *skb,rcu_read_lock ();if (!nh) { dst =skb_dst (skb); - nexthop = rt6_nexthop(container_of(dst, struct rt6_info, dst), + nexthop =rt6_nexthop (dst_rt6_info (dst), &ipv6_hdr (skb)->daddr); }else { nexthop = &nh->ipv6_nh;
net/ipv4/ip_tunnel.c +1 -1
@@ -544,7 +544,7 @@ static int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb,struct rt6_info *rt6;__be32 daddr; - rt6 =skb_valid_dst (skb) ? (struct rt6_info *)skb_dst (skb) : + rt6 =skb_valid_dst (skb) ?dst_rt6_info (skb_dst (skb)) :NULL ; daddr = md ? dst : tunnel->parms.iph.daddr;
net/ipv6/icmp.c +4 -4
@@ -214,7 +214,7 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type, }else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK )) { res = true; }else { -struct rt6_info *rt = (struct rt6_info *)dst; +struct rt6_info *rt =dst_rt6_info (dst);int tmo = net->ipv6.sysctl.icmpv6_time;struct inet_peer *peer;
@@ -245,7 +245,7 @@ static bool icmpv6_rt_has_prefsrc(struct sock *sk, u8 type, dst =ip6_route_output (net, sk, fl6);if (!dst->error) { -struct rt6_info *rt = (struct rt6_info *)dst; +struct rt6_info *rt =dst_rt6_info (dst);struct in6_addr prefsrc;rt6_get_prefsrc (rt, &prefsrc);
@@ -624,7 +624,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,if (ip6_append_data (sk, icmpv6_getfrag, &msg, len +sizeof (struct icmp6hdr ),sizeof (struct icmp6hdr ), - &ipc6, &fl6, (struct rt6_info *)dst, + &ipc6, &fl6,dst_rt6_info (dst),MSG_DONTWAIT )) {ICMP6_INC_STATS (net, idev,ICMP6_MIB_OUTERRORS );ip6_flush_pending_frames (sk);
@@ -817,7 +817,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)if (ip6_append_data (sk, icmpv6_getfrag, &msg, skb->len +sizeof (struct icmp6hdr ),sizeof (struct icmp6hdr ), &ipc6, &fl6, - (struct rt6_info *)dst,MSG_DONTWAIT )) { +dst_rt6_info (dst),MSG_DONTWAIT )) {__ICMP6_INC_STATS (net, idev,ICMP6_MIB_OUTERRORS );ip6_flush_pending_frames (sk); }else {
net/ipv6/ila/ila_lwt.c +2 -2
@@ -38,7 +38,7 @@ static inline struct ila_params *ila_params_lwtunnel(static int ila_output (struct net *net,struct sock *sk,struct sk_buff *skb) {struct dst_entry *orig_dst =skb_dst (skb); -struct rt6_info *rt = (struct rt6_info *)orig_dst; +struct rt6_info *rt =dst_rt6_info (orig_dst);struct ila_lwt *ilwt =ila_lwt_lwtunnel (orig_dst->lwtstate);struct dst_entry *dst;int err = -EINVAL ;
@@ -72,7 +72,7 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)memset (&fl6,0 ,sizeof (fl6)); fl6.flowi6_oif = orig_dst->dev->ifindex; fl6.flowi6_iif =LOOPBACK_IFINDEX ; - fl6.daddr = *rt6_nexthop ((struct rt6_info *)orig_dst, + fl6.daddr = *rt6_nexthop (dst_rt6_info (orig_dst), &ip6h->daddr); dst =ip6_route_output (net,NULL , &fl6);
net/ipv6/ip6_output.c +9 -9
@@ -121,7 +121,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff * }rcu_read_lock (); - nexthop =rt6_nexthop ((struct rt6_info *)dst, daddr); + nexthop =rt6_nexthop (dst_rt6_info (dst), daddr); neigh =__ipv6_neigh_lookup_noref (dev, nexthop);if (unlikely (IS_ERR_OR_NULL (neigh))) {
@@ -611,7 +611,7 @@ int ip6_forward(struct sk_buff *skb)* send a redirect. */ - rt = (struct rt6_info *) dst; + rt =dst_rt6_info (dst);if (rt->rt6i_flags &RTF_GATEWAY ) target = &rt->rt6i_gateway;else
@@ -866,7 +866,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, int (*output)(struct net *, struct sock *, struct sk_buff *)) {struct sk_buff *frag; -struct rt6_info *rt = (struct rt6_info *)skb_dst (skb); +struct rt6_info *rt =dst_rt6_info (skb_dst (skb));struct ipv6_pinfo *np = skb->sk && !dev_recursion_level () ?inet6_sk (skb->sk) :NULL ;bool mono_delivery_time = skb->mono_delivery_time;
@@ -1074,7 +1074,7 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk,return NULL ; } - rt = (struct rt6_info *)dst; + rt =dst_rt6_info (dst);/* Yes, checking route validity in not connected * case is not very simple. Take into account, * that we do not support routing by source, TOS,
@@ -1129,7 +1129,7 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk,struct rt6_info *rt; *dst =ip6_route_output (net, sk, fl6); - rt = (*dst)->error ?NULL : (struct rt6_info *)*dst; + rt = (*dst)->error ?NULL :dst_rt6_info (*dst);rcu_read_lock (); from = rt ?rcu_dereference (rt->from) :NULL ;
@@ -1171,7 +1171,7 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk,* dst entry and replace it instead with the * dst entry of the nexthop router */ - rt = (struct rt6_info *) *dst; + rt =dst_rt6_info (*dst);rcu_read_lock (); n =__ipv6_neigh_lookup_noref (rt->dst.dev,rt6_nexthop (rt, &fl6->daddr));
@@ -1437,7 +1437,7 @@ static int __ip6_append_data(struct sock *sk,int offset =0 ;bool zc = false;u32 tskey =0 ; -struct rt6_info *rt = (struct rt6_info *)cork->dst; +struct rt6_info *rt =dst_rt6_info (cork->dst);bool paged, hold_tskey, extra_uref = false;struct ipv6_txoptions *opt = v6_cork->opt;int csummode =CHECKSUM_NONE ;
@@ -1869,7 +1869,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk,struct net *net =sock_net (sk);struct ipv6hdr *hdr;struct ipv6_txoptions *opt = v6_cork->opt; -struct rt6_info *rt = (struct rt6_info *)cork->base.dst; +struct rt6_info *rt =dst_rt6_info (cork->base.dst);struct flowi6 *fl6 = &cork->fl.u.ip6;unsigned char proto = fl6->flowi6_proto;
@@ -1941,7 +1941,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk,int ip6_send_skb (struct sk_buff *skb) {struct net *net =sock_net (skb->sk); -struct rt6_info *rt = (struct rt6_info *)skb_dst (skb); +struct rt6_info *rt =dst_rt6_info (skb_dst (skb));int err;rcu_read_lock ();
net/ipv6/ip6mr.c +1 -1
@@ -2301,7 +2301,7 @@ int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,int err;struct mr_table *mrt;struct mfc6_cache *cache; -struct rt6_info *rt = (struct rt6_info *)skb_dst (skb); +struct rt6_info *rt =dst_rt6_info (skb_dst (skb));rcu_read_lock (); mrt =__ip6mr_get_table (net,RT6_TABLE_DFLT );
net/ipv6/ndisc.c +1 -1
@@ -1717,7 +1717,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)if (IS_ERR (dst))return ; - rt = (struct rt6_info *) dst; + rt =dst_rt6_info (dst);if (rt->rt6i_flags &RTF_GATEWAY ) { ND_PRINTK(2, warn,
net/ipv6/ping.c +1 -1
@@ -154,7 +154,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) dst =ip6_sk_dst_lookup_flow (sk, &fl6, daddr, false);if (IS_ERR (dst))return PTR_ERR (dst); - rt = (struct rt6_info *) dst; + rt =dst_rt6_info (dst);if (!fl6.flowi6_oif &&ipv6_addr_is_multicast (&fl6.daddr)) fl6.flowi6_oif = np->mcast_oif;
net/ipv6/raw.c +2 -2
@@ -591,7 +591,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,struct ipv6hdr *iph;struct sk_buff *skb;int err; -struct rt6_info *rt = (struct rt6_info *)*dstp; +struct rt6_info *rt =dst_rt6_info (*dstp);int hlen =LL_RESERVED_SPACE (rt->dst.dev);int tlen = rt->dst.dev->needed_tailroom;
@@ -915,7 +915,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipc6.opt = opt;lock_sock (sk); err =ip6_append_data (sk, raw6_getfrag, &rfv, - len,0 , &ipc6, &fl6, (struct rt6_info *)dst, + len,0 , &ipc6, &fl6,dst_rt6_info (dst), msg->msg_flags);if (err)
net/ipv6/route.c +14 -14
@@ -227,7 +227,7 @@ static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,struct sk_buff *skb, const void *daddr) { - const struct rt6_info *rt = container_of(dst, struct rt6_info, dst); + const struct rt6_info *rt = dst_rt6_info(dst);return ip6_neigh_lookup (rt6_nexthop (rt, &in6addr_any), dst->dev, skb, daddr);
@@ -235,8 +235,8 @@ static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,static void ip6_confirm_neigh (const struct dst_entry *dst,const void *daddr) { +const struct rt6_info *rt =dst_rt6_info (dst);struct net_device *dev = dst->dev; -struct rt6_info *rt = (struct rt6_info *)dst; daddr =choose_neigh_daddr (rt6_nexthop (rt, &in6addr_any),NULL , daddr);if (!daddr)
@@ -356,7 +356,7 @@ EXPORT_SYMBOL(ip6_dst_alloc);static void ip6_dst_destroy (struct dst_entry *dst) { -struct rt6_info *rt = (struct rt6_info *)dst; +struct rt6_info *rt =dst_rt6_info (dst);struct fib6_info *from;struct inet6_dev *idev;
@@ -376,7 +376,7 @@ static void ip6_dst_destroy(struct dst_entry *dst)static void ip6_dst_ifdown (struct dst_entry *dst,struct net_device *dev,int how) { -struct rt6_info *rt = (struct rt6_info *)dst; +struct rt6_info *rt =dst_rt6_info (dst);struct inet6_dev *idev = rt->rt6i_idev;struct fib6_info *from;
@@ -1324,7 +1324,7 @@ struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, dst =fib6_rule_lookup (net, &fl6, skb, flags, ip6_pol_route_lookup);if (dst->error ==0 ) -return (struct rt6_info *) dst; +return dst_rt6_info (dst);dst_release (dst);
@@ -2681,7 +2681,7 @@ struct dst_entry *ip6_route_output_flags(struct net *net,rcu_read_lock (); dst =ip6_route_output_flags_noref (net, sk, fl6, flags); - rt6 = (struct rt6_info *)dst; + rt6 =dst_rt6_info (dst);/* For dst cached in uncached_list, refcnt is already taken. */ if (list_empty (&rt6->rt6i_uncached) && !dst_hold_safe (dst)) { dst = &net->ipv6.ip6_null_entry->dst;
@@ -2695,7 +2695,7 @@ EXPORT_SYMBOL_GPL(ip6_route_output_flags);struct dst_entry *ip6_blackhole_route (struct net *net,struct dst_entry *dst_orig) { -struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; +struct rt6_info *rt, *ort =dst_rt6_info (dst_orig);struct net_device *loopback_dev = net->loopback_dev;struct dst_entry *new =NULL ;
@@ -2778,7 +2778,7 @@ INDIRECT_CALLABLE_SCOPE struct dst_entry *ip6_dst_check(struct dst_entry *dst,struct fib6_info *from;struct rt6_info *rt; - rt = container_of(dst, struct rt6_info, dst); + rt =dst_rt6_info (dst);if (rt->sernum)return rt6_is_valid (rt) ? dst :NULL ;
@@ -2807,7 +2807,7 @@ EXPORT_INDIRECT_CALLABLE(ip6_dst_check);static void ip6_negative_advice (struct sock *sk,struct dst_entry *dst) { -struct rt6_info *rt = (struct rt6_info *) dst; +struct rt6_info *rt =dst_rt6_info (dst);if (rt->rt6i_flags &RTF_CACHE ) {rcu_read_lock ();
@@ -2830,7 +2830,7 @@ static void ip6_link_failure(struct sk_buff *skb)icmpv6_send (skb,ICMPV6_DEST_UNREACH ,ICMPV6_ADDR_UNREACH ,0 ); - rt = (struct rt6_info *)skb_dst (skb); + rt =dst_rt6_info (skb_dst (skb));if (rt) {rcu_read_lock ();if (rt->rt6i_flags &RTF_CACHE ) {
@@ -2886,7 +2886,7 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, bool confirm_neigh) {const struct in6_addr *daddr, *saddr; -struct rt6_info *rt6 = (struct rt6_info *)dst; +struct rt6_info *rt6 =dst_rt6_info (dst);/* Note: do *NOT* check dst_metric_locked(dst, RTAX_MTU) * IPv6 pmtu discovery isn't optional, so 'mtu lock' cannot disable it.
@@ -4214,7 +4214,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu } } - rt = (struct rt6_info *) dst; + rt =dst_rt6_info (dst);if (rt->rt6i_flags &RTF_REJECT ) {net_dbg_ratelimited ("rt6_redirect: source isn't a valid nexthop for redirect target\n" );return ;
@@ -5665,7 +5665,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb, int iif, int type, u32 portid, u32 seq, unsigned int flags) { - struct rt6_info *rt6 = (struct rt6_info *)dst; + struct rt6_info *rt6 = dst_rt6_info(dst);struct rt6key *rt6_dst, *rt6_src;u32 *pmetrics, table, rt6_flags;unsigned char nh_flags =0 ;
@@ -6182,7 +6182,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, } - rt = container_of(dst, struct rt6_info, dst); + rt =dst_rt6_info (dst);if (rt->dst.error) { err = rt->dst.error;ip6_rt_put (rt);
net/ipv6/tcp_ipv6.c +1 -3
@@ -106,11 +106,9 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)struct dst_entry *dst =skb_dst (skb);if (dst &&dst_hold_safe (dst)) { -const struct rt6_info *rt = (const struct rt6_info *)dst; -rcu_assign_pointer (sk->sk_rx_dst, dst); sk->sk_rx_dst_ifindex = skb->skb_iif; - sk->sk_rx_dst_cookie =rt6_get_cookie (rt); + sk->sk_rx_dst_cookie =rt6_get_cookie (dst_rt6_info (dst)); } }
net/ipv6/udp.c +4 -7
@@ -925,11 +925,8 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,static void udp6_sk_rx_dst_set (struct sock *sk,struct dst_entry *dst) { -if (udp_sk_rx_dst_set (sk, dst)) { -const struct rt6_info *rt = (const struct rt6_info *)dst; - - sk->sk_rx_dst_cookie =rt6_get_cookie (rt); - } +if (udp_sk_rx_dst_set (sk, dst)) + sk->sk_rx_dst_cookie =rt6_get_cookie (dst_rt6_info (dst)); }/* wrapper for udp_queue_rcv_skb tacking care of csum conversion and
@@ -1593,7 +1590,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) skb =ip6_make_skb (sk, getfrag, msg, ulen,sizeof (struct udphdr ), &ipc6, - (struct rt6_info *)dst, +dst_rt6_info (dst), msg->msg_flags, &cork); err =PTR_ERR (skb);if (!IS_ERR_OR_NULL (skb))
@@ -1620,7 +1617,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipc6.dontfrag = np->dontfrag; up->len += ulen; err =ip6_append_data (sk, getfrag, msg, ulen,sizeof (struct udphdr ), - &ipc6, fl6, (struct rt6_info *)dst, + &ipc6, fl6,dst_rt6_info (dst), corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);if (err)udp_v6_flush_pending_frames (sk);
net/ipv6/xfrm6_policy.c +1 -1
@@ -80,7 +80,7 @@ static int xfrm6_get_saddr(xfrm_address_t *saddr,static int xfrm6_fill_dst (struct xfrm_dst *xdst,struct net_device *dev,const struct flowi *fl) { -struct rt6_info *rt = (struct rt6_info *)xdst->route; +struct rt6_info *rt =dst_rt6_info (xdst->route); xdst->u.dst.dev = dev;netdev_hold (dev, &xdst->u.dst.dev_tracker,GFP_ATOMIC );
net/l2tp/l2tp_ip6.c +1 -1
@@ -633,7 +633,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ulen = len + (skb_queue_empty (&sk->sk_write_queue) ? transhdrlen :0 ); err =ip6_append_data (sk, ip_generic_getfrag, msg, ulen, transhdrlen, &ipc6, - &fl6, (struct rt6_info *)dst, + &fl6,dst_rt6_info (dst), msg->msg_flags);if (err)ip6_flush_pending_frames (sk);
net/mpls/mpls_iptunnel.c +1 -1
@@ -92,7 +92,7 @@ static int mpls_xmit(struct sk_buff *skb) ttl = net->mpls.default_ttl; else ttl =ipv6_hdr (skb)->hop_limit; - rt6 = (struct rt6_info *)dst; + rt6 =dst_rt6_info (dst); }else { goto drop; }
net/netfilter/ipvs/ip_vs_xmit.c +7 -7
@@ -184,7 +184,7 @@ static inline bool crosses_local_route_boundary(int skb_af, struct sk_buff *skb, (!skb->dev || skb->dev->flags &IFF_LOOPBACK ) && (addr_type &IPV6_ADDR_LOOPBACK ); old_rt_is_local =__ip_vs_is_local_route6 ( - (struct rt6_info *)skb_dst (skb)); +dst_rt6_info (skb_dst (skb))); } else#endif {
@@ -484,7 +484,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,if (dest) { dest_dst =__ip_vs_dst_check (dest);if (likely (dest_dst)) - rt = (struct rt6_info *) dest_dst->dst_cache; + rt =dst_rt6_info (dest_dst->dst_cache);else {u32 cookie;
@@ -504,7 +504,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,ip_vs_dest_dst_free (dest_dst); goto err_unreach; } - rt = (struct rt6_info *) dst; + rt =dst_rt6_info (dst); cookie =rt6_get_cookie (rt);__ip_vs_dst_set (dest, dest_dst, &rt->dst, cookie);spin_unlock_bh (&dest->dst_lock);
@@ -520,7 +520,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, rt_mode);if (!dst) goto err_unreach; - rt = (struct rt6_info *) dst; + rt =dst_rt6_info (dst); } local =__ip_vs_is_local_route6 (rt);
@@ -879,7 +879,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, IP_VS_RT_MODE_RDR);if (local <0 ) goto tx_error; - rt = (struct rt6_info *)skb_dst (skb); + rt =dst_rt6_info (skb_dst (skb));/* * Avoid duplicate tuple in reply direction for NAT traffic * to local address when connection is sync-ed
@@ -1315,7 +1315,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,if (local)return ip_vs_send_or_cont (NFPROTO_IPV6 , skb, cp,1 ); - rt = (struct rt6_info *)skb_dst (skb); + rt =dst_rt6_info (skb_dst (skb)); tdev = rt->dst.dev;/*
@@ -1636,7 +1636,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, &cp->daddr.in6, NULL, ipvsh, 0, rt_mode);if (local <0 ) goto tx_error; - rt = (struct rt6_info *)skb_dst (skb); + rt =dst_rt6_info (skb_dst (skb));/* * Avoid duplicate tuple in reply direction for NAT traffic * to local address when connection is sync-ed
net/netfilter/nf_flow_table_core.c +2 -6
@@ -77,12 +77,8 @@ EXPORT_SYMBOL_GPL(flow_offload_alloc);static u32 flow_offload_dst_cookie (struct flow_offload_tuple *flow_tuple) { -const struct rt6_info *rt; - -if (flow_tuple->l3proto ==NFPROTO_IPV6 ) { - rt = (const struct rt6_info *)flow_tuple->dst_cache; -return rt6_get_cookie (rt); - } +if (flow_tuple->l3proto ==NFPROTO_IPV6 ) +return rt6_get_cookie (dst_rt6_info (flow_tuple->dst_cache));return 0 ; }
net/netfilter/nf_flow_table_ip.c +2 -2
@@ -665,7 +665,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,nf_ct_acct_update (flow->ct, tuplehash->tuple.dir, skb->len);if (unlikely (tuplehash->tuple.xmit_type ==FLOW_OFFLOAD_XMIT_XFRM )) { - rt = (struct rt6_info *)tuplehash->tuple.dst_cache; + rt =dst_rt6_info (tuplehash->tuple.dst_cache);memset (skb->cb,0 ,sizeof (struct inet6_skb_parm ));IP6CB (skb)->iif = skb->dev->ifindex;IP6CB (skb)->flags =IP6SKB_FORWARDED ;
@@ -674,7 +674,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,switch (tuplehash->tuple.xmit_type) {case FLOW_OFFLOAD_XMIT_NEIGH : - rt = (struct rt6_info *)tuplehash->tuple.dst_cache; + rt =dst_rt6_info (tuplehash->tuple.dst_cache); outdev = rt->dst.dev; skb->dev = outdev; nexthop =rt6_nexthop (rt, &flow->tuplehash[!dir].tuple.src_v6);
net/netfilter/nft_rt.c +1 -1
@@ -80,7 +80,7 @@ void nft_rt_get_eval(const struct nft_expr *expr,if (nft_pf (pkt) !=NFPROTO_IPV6 ) goto err; -memcpy (dest,rt6_nexthop ((struct rt6_info *)dst, +memcpy (dest,rt6_nexthop (dst_rt6_info (dst), &ipv6_hdr (skb)->daddr),sizeof (struct in6_addr ));break ;
net/sctp/ipv6.c +1 -1
@@ -416,7 +416,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,if (!IS_ERR_OR_NULL (dst)) {struct rt6_info *rt; - rt = (struct rt6_info *)dst; + rt =dst_rt6_info (dst); t->dst_cookie =rt6_get_cookie (rt); pr_debug("rt6_dst:%pI6/%d rt6_src:%pI6\n", &rt->rt6i_dst.addr, rt->rt6i_dst.plen,
net/xfrm/xfrm_policy.c +1 -2
@@ -2526,8 +2526,7 @@ static void xfrm_init_path(struct xfrm_dst *path, struct dst_entry *dst, int nfheader_len) {if (dst->ops->family ==AF_INET6 ) { -struct rt6_info *rt = (struct rt6_info *)dst; - path->path_cookie =rt6_get_cookie (rt); + path->path_cookie =rt6_get_cookie (dst_rt6_info (dst)); path->u.rt6.rt6i_nfheader_len = nfheader_len; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 328/609 ipvs: fix the checksum validations
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ]
ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6 packets from clients. In fact, as for TCP/UDP we should validate the checksum for ICMP packets only when we mangle the packets on MASQ or on reply for tunnel.
Also, Sashiko points out that handle_response_icmp() being common for IPv4 and IPv6 is missing the pseudo-header calculation while validating ICMPv6 messages from real servers which is a problem if checksum is not validated by the hardware.
Fix the problems by creating ip_vs_checksum_common_check() helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6. Rely on the nf_checksum() for validating the ICMP messages but use it also for TCP and UDP.
Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP.
IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum validation on LOCAL_OUT (local clients or local real servers) and on FORWARD (traffic from servers on LAN). Do it only on LOCAL_IN, in case nf_checksum() is not called on PRE_ROUTING.
Also, ip_vs_checksum_complete() can be marked static.
--- include/net/ip_vs.h | 31 +++++++++++++++-- net/netfilter/ipvs/ip_vs_core.c | 20 +++++++++-- net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++---- net/netfilter/ipvs/ip_vs_proto_tcp.c | 44 +++++------------------ net/netfilter/ipvs/ip_vs_proto_udp.c | 50 ++++++--------------------- 5 files changed, 74 insertions(+), 86 deletions(-)
include/net/ip_vs.h +29 -2
@@ -24,7 +24,9 @@#include <linux/netfilter.h> /* for union nf_inet_addr */ #include <linux/ip.h> #include <linux/ipv6.h> /* for struct ipv6hdr */ +#include <net/route.h> #include <net/ipv6.h> +#include <net/ip6_fib.h> #if IS_ENABLED (CONFIG_NF_CONNTRACK )#include <net/netfilter/nf_conntrack.h> #endif
@@ -1581,8 +1583,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int dir);#endif -__sum16 ip_vs_checksum_complete (struct sk_buff *skb,int offset); -static inline __wsum ip_vs_check_diff4 (__be32 old,__be32 new,__wsum oldsum) {__be32 diff[2 ] = { ~old, new };
@@ -1608,6 +1608,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)return csum_partial (diff,sizeof (diff), oldsum); } +static inline bool ip_vs_checksum_needed (struct sk_buff *skb,int af) +{ +/* Checksum unnecessary or already validated? */ +if (skb_csum_unnecessary (skb)) +return false; +/* LOCAL_OUT ? */ +if (!skb->dev || skb->dev->flags &IFF_LOOPBACK ) +return false; +/* !LOCAL_IN (FORWARD) ? */ +if (af ==AF_INET6 ) { +if (!(dst_rt6_info (skb_dst (skb))->rt6i_flags &RTF_LOCAL )) +return false; + }else { +if (!(skb_rtable (skb)->rt_flags &RTCF_LOCAL )) +return false; + } +return true; +} + +static inline bool ip_vs_checksum_common_check (struct sk_buff *skb, +int offset,int proto,int af) +{ +if (!ip_vs_checksum_needed (skb, af)) +return true; +return !nf_checksum (skb,NF_INET_LOCAL_IN , offset, proto, af); +} +/* Forget current conntrack (unconfirmed) and attach notrack entry */ static inline void ip_vs_notrack (struct sk_buff *skb) {
net/netfilter/ipvs/ip_vs_core.c +17 -3
@@ -689,7 +689,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }#endif -__sum16 ip_vs_checksum_complete (struct sk_buff *skb,int offset) +static __sum16 ip_vs_checksum_complete (struct sk_buff *skb,int offset) {return csum_fold (skb_checksum (skb, offset, skb->len - offset,0 )); }
@@ -860,13 +860,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb, unsigned int offset, unsigned int ihl, unsigned int hooknum) { + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;unsigned int verdict =NF_DROP ;if (IP_VS_FWD_METHOD (cp) !=IP_VS_CONN_F_MASQ ) goto after_nat;/* Ensure the checksum is correct */ -if (!skb_csum_unnecessary (skb) &&ip_vs_checksum_complete (skb, ihl)) { +if (!ip_vs_checksum_common_check (skb, ihl, iproto, af)) {/* Failed checksum! */ IP_VS_DBG_BUF (1 ,"Forward ICMP: failed checksum from %s!\n" ,IP_VS_DBG_ADDR (af, snet));
@@ -1725,7 +1726,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, verdict =NF_DROP ;/* Ensure the checksum is correct */ -if (!skb_csum_unnecessary (skb) &&ip_vs_checksum_complete (skb, ihl)) { +if ((IP_VS_FWD_METHOD (cp) ==IP_VS_CONN_F_MASQ || tunnel) && + !ip_vs_checksum_common_check (skb, ihl,IPPROTO_ICMP ,AF_INET )) {/* Failed checksum! */ IP_VS_DBG (1 ,"Incoming ICMP: failed checksum from %pI4!\n" , &iph->saddr);
@@ -1891,6 +1893,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, goto out; } + verdict =NF_DROP ; + +/* Ensure the checksum is correct */ +if (IP_VS_FWD_METHOD (cp) ==IP_VS_CONN_F_MASQ && + !ip_vs_checksum_common_check (skb, iph->len,IPPROTO_ICMPV6 , +AF_INET6 )) { +/* Failed checksum! */ +IP_VS_DBG (1 ,"Incoming ICMPv6: failed checksum from %pI6c!\n" , + &iph->saddr); + goto out; + } +/* do the statistics and put it back */ ip_vs_in_stats (cp, skb);
net/netfilter/ipvs/ip_vs_proto_sctp.c +9 -6
@@ -11,7 +11,7 @@static int sctp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp, -unsigned int sctphoff); +struct ip_vs_iphdr *iph); static int sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,int ret;/* Some checks before mangling */ -if (!sctp_csum_check (cp->af, skb, pp, sctphoff)) +if (!sctp_csum_check (cp->af, skb, pp, iph))return 0 ;/* Call application helper if needed */
@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,int ret;/* Some checks before mangling */ -if (!sctp_csum_check (cp->af, skb, pp, sctphoff)) +if (!sctp_csum_check (cp->af, skb, pp, iph))return 0 ;/* Call application helper if needed */
@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,static int sctp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp, -unsigned int sctphoff) +struct ip_vs_iphdr *iph) { +unsigned int sctphoff = iph->len;struct sctphdr *sh;__le32 cmp, val; +if (!ip_vs_checksum_needed (skb, af)) +return 1 ; sh = (struct sctphdr *)(skb->data + sctphoff); cmp = sh->checksum; val =sctp_compute_cksum (skb, sctphoff);if (val != cmp) {/* CRC failure, dump it. */ -IP_VS_DBG_RL_PKT (0 , af, pp, skb,0 , -"Failed checksum for" ); +IP_VS_DBG_RL_PKT (0 , af, pp, skb, iph->off, +"Failed checksum for" );return 0 ; }return 1 ;
net/netfilter/ipvs/ip_vs_proto_tcp.c +8 -36
70 lines not on this page · raw message
net/netfilter/ipvs/ip_vs_proto_udp.c +11 -39
80 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 329/609 ipvs: fix places with wrong packet offsets
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ]
The offsets we use to packet headers and payloads should be based on skb->data. We even already respect non-zero network offset in ip_vs_fill_iph_skb() but some places do it wrongly and support only zero offset which is expected for the IP layer where IPVS has hooks.
Change all places that instead of skb->data use offsets based on the network header (skb_network_header, ip_hdr, etc) because this doubles the network offset as noted by Sashiko.
For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header parsing done by the caller.
--- include/net/ip_vs.h | 15 +-- net/netfilter/ipvs/ip_vs_app.c | 4 +- net/netfilter/ipvs/ip_vs_core.c | 133 +++++++++++++------------- net/netfilter/ipvs/ip_vs_proto_sctp.c | 4 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 4 +- net/netfilter/ipvs/ip_vs_proto_udp.c | 4 +- net/netfilter/ipvs/ip_vs_xmit.c | 26 ++--- 7 files changed, 97 insertions(+), 93 deletions(-)
include/net/ip_vs.h +9 -6
@@ -1496,8 +1496,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,int ip_vs_dr_xmit (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,struct ip_vs_iphdr *iph);int ip_vs_icmp_xmit (struct sk_buff *skb,struct ip_vs_conn *cp, -struct ip_vs_protocol *pp,int offset, -unsigned int hooknum,struct ip_vs_iphdr *iph); +struct ip_vs_protocol *pp,unsigned int toff, +unsigned int wlen,unsigned int hooknum, +struct ip_vs_iphdr *ciph);void ip_vs_dest_dst_rcu_free (struct rcu_head *head);#ifdef CONFIG_IP_VS_IPV6
@@ -1510,8 +1511,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,int ip_vs_dr_xmit_v6 (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,struct ip_vs_iphdr *iph);int ip_vs_icmp_xmit_v6 (struct sk_buff *skb,struct ip_vs_conn *cp, -struct ip_vs_protocol *pp,int offset, -unsigned int hooknum,struct ip_vs_iphdr *iph); +struct ip_vs_protocol *pp,unsigned int toff, +unsigned int wlen,unsigned int hooknum, +struct ip_vs_iphdr *ciph);#endif #ifdef CONFIG_SYSCTL
@@ -1576,11 +1578,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) }void ip_vs_nat_icmp (struct sk_buff *skb,struct ip_vs_protocol *pp, -struct ip_vs_conn *cp,int dir); +struct ip_vs_conn *cp,int dir,unsigned int toff);#ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6 (struct sk_buff *skb,struct ip_vs_protocol *pp, -struct ip_vs_conn *cp,int dir); +struct ip_vs_conn *cp,int dir,unsigned int toff, +struct ip_vs_iphdr *ciph);#endif static inline __wsum ip_vs_check_diff4 (__be32 old,__be32 new,__wsum oldsum)
net/netfilter/ipvs/ip_vs_app.c +2 -2
@@ -368,7 +368,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,if (skb_ensure_writable (skb, ipvsh->len +sizeof (*th)))return 0 ; - th = (struct tcphdr *)(skb_network_header (skb) + ipvsh->len); + th = (struct tcphdr *)(skb->data + ipvsh->len);/* * Remember seq number in case this pkt gets resized
@@ -444,7 +444,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,if (skb_ensure_writable (skb, ipvsh->len +sizeof (*th)))return 0 ; - th = (struct tcphdr *)(skb_network_header (skb) + ipvsh->len); + th = (struct tcphdr *)(skb->data + ipvsh->len);/* * Remember seq number in case this pkt gets resized
net/netfilter/ipvs/ip_vs_core.c +66 -67
332 lines not on this page · raw message
net/netfilter/ipvs/ip_vs_proto_sctp.c +2 -2
@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - sctph = (void *)skb_network_header (skb) + sctphoff; + sctph = (void *)skb->data + sctphoff;/* Only update csum if we really have to */ if (sctph->source != cp->vport || payload_csum ||
@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - sctph = (void *)skb_network_header (skb) + sctphoff; + sctph = (void *)skb->data + sctphoff;/* Only update csum if we really have to */ if (sctph->dest != cp->dport || payload_csum ||
net/netfilter/ipvs/ip_vs_proto_tcp.c +2 -2
@@ -180,7 +180,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - tcph = (void *)skb_network_header (skb) + tcphoff; + tcph = (void *)skb->data + tcphoff; tcph->source = cp->vport;/* Adjust TCP checksums */
@@ -261,7 +261,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - tcph = (void *)skb_network_header (skb) + tcphoff; + tcph = (void *)skb->data + tcphoff; tcph->dest = cp->dport;/*
net/netfilter/ipvs/ip_vs_proto_udp.c +2 -2
@@ -171,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - udph = (void *)skb_network_header (skb) + udphoff; + udph = (void *)skb->data + udphoff; udph->source = cp->vport;/*
@@ -255,7 +255,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - udph = (void *)skb_network_header (skb) + udphoff; + udph = (void *)skb->data + udphoff; udph->dest = cp->dport;/*
net/netfilter/ipvs/ip_vs_xmit.c +14 -12
@@ -1511,8 +1511,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,*/ int ip_vs_icmp_xmit (struct sk_buff *skb,struct ip_vs_conn *cp, -struct ip_vs_protocol *pp,int offset,unsigned int hooknum, -struct ip_vs_iphdr *iph) +struct ip_vs_protocol *pp,unsigned int toff, +unsigned int wlen,unsigned int hooknum, +struct ip_vs_iphdr *ciph) {struct rtable *rt;/* Route to the other host */ int rc;
@@ -1526,7 +1527,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,translate address/port back */ if (IP_VS_FWD_METHOD (cp) !=IP_VS_CONN_F_MASQ ) {if (cp->packet_xmit) - rc = cp->packet_xmit (skb, cp, pp, iph); + rc = cp->packet_xmit (skb, cp, pp, ciph);else rc =NF_ACCEPT ;/* do not touch skb anymore */
@@ -1544,7 +1545,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,IP_VS_RT_MODE_LOCAL |IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; local =__ip_vs_get_out_rt (cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, -NULL , iph); +NULL , ciph);if (local <0 ) goto tx_error; rt =skb_rtable (skb);
@@ -1576,13 +1577,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, }/* copy-on-write the packet before mangling it */ -if (skb_ensure_writable (skb, offset)) +if (skb_ensure_writable (skb, wlen)) goto tx_error;if (skb_cow (skb, rt->dst.dev->hard_header_len)) goto tx_error; -ip_vs_nat_icmp (skb, pp, cp,0 ); +ip_vs_nat_icmp (skb, pp, cp,0 , toff);/* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df =1 ;
@@ -1601,8 +1602,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,#ifdef CONFIG_IP_VS_IPV6 int ip_vs_icmp_xmit_v6 (struct sk_buff *skb,struct ip_vs_conn *cp, -struct ip_vs_protocol *pp,int offset,unsigned int hooknum, -struct ip_vs_iphdr *ipvsh) +struct ip_vs_protocol *pp,unsigned int toff, +unsigned int wlen,unsigned int hooknum, +struct ip_vs_iphdr *ciph) {struct rt6_info *rt;/* Route to the other host */ int rc;
@@ -1616,7 +1618,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,translate address/port back */ if (IP_VS_FWD_METHOD (cp) !=IP_VS_CONN_F_MASQ ) {if (cp->packet_xmit) - rc = cp->packet_xmit (skb, cp, pp, ipvsh); + rc = cp->packet_xmit (skb, cp, pp, ciph);else rc =NF_ACCEPT ;/* do not touch skb anymore */
@@ -1633,7 +1635,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,IP_VS_RT_MODE_LOCAL |IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; local =__ip_vs_get_out_rt_v6 (cp->ipvs, cp->af, skb, cp->dest, - &cp->daddr.in6,NULL , ipvsh,0 , rt_mode); + &cp->daddr.in6,NULL , ciph,0 , rt_mode);if (local <0 ) goto tx_error; rt =dst_rt6_info (skb_dst (skb));
@@ -1665,13 +1667,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, }/* copy-on-write the packet before mangling it */ -if (skb_ensure_writable (skb, offset)) +if (skb_ensure_writable (skb, wlen)) goto tx_error;if (skb_cow (skb, rt->dst.dev->hard_header_len)) goto tx_error; -ip_vs_nat_icmp_v6 (skb, pp, cp,0 ); +ip_vs_nat_icmp_v6 (skb, pp, cp,0 , toff, ciph);/* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df =1 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 330/609 ipvs: do not mangle ICMP replies for non-first fragments
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ]
Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the payload for embedded non-first IPv4 fragments. The problem is in the very old inverted pp->dont_defrag check which should not continue when embedded is a non-first TCP/UDP/SCTP fragment.
Check for embedded non-first fragment is also missing from ip_vs_out_icmp_v6(), it is needed before any connection lookups that expect ports after the network headers.
Drop the blocking code from ip_vs_in_icmp_v6() which prevents ICMPv6 from local clients to use non-MASQ forwarding.
--- include/net/ip_vs.h | 11 +++--- net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++--------------------- net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++---- 3 files changed, 48 insertions(+), 52 deletions(-)
include/net/ip_vs.h +5 -6
@@ -1497,8 +1497,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);int ip_vs_icmp_xmit (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,unsigned int toff, -unsigned int wlen,unsigned int hooknum, -struct ip_vs_iphdr *ciph); +unsigned int hooknum,struct ip_vs_iphdr *ciph);void ip_vs_dest_dst_rcu_free (struct rcu_head *head);#ifdef CONFIG_IP_VS_IPV6
@@ -1512,8 +1511,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);int ip_vs_icmp_xmit_v6 (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,unsigned int toff, -unsigned int wlen,unsigned int hooknum, -struct ip_vs_iphdr *ciph); +unsigned int hooknum,struct ip_vs_iphdr *ciph);#endif #ifdef CONFIG_SYSCTL
@@ -1578,12 +1576,13 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) }void ip_vs_nat_icmp (struct sk_buff *skb,struct ip_vs_protocol *pp, -struct ip_vs_conn *cp,int dir,unsigned int toff); +struct ip_vs_conn *cp,int dir,unsigned int toff, +bool has_ports);#ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6 (struct sk_buff *skb,struct ip_vs_protocol *pp,struct ip_vs_conn *cp,int dir,unsigned int toff, -struct ip_vs_iphdr *ciph); +bool has_ports,struct ip_vs_iphdr *ciph);#endif static inline __wsum ip_vs_check_diff4 (__be32 old,__be32 new,__wsum oldsum)
net/netfilter/ipvs/ip_vs_core.c +21 -40
161 lines not on this page · raw message
net/netfilter/ipvs/ip_vs_xmit.c +22 -6
@@ -1512,13 +1512,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,int ip_vs_icmp_xmit (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,unsigned int toff, -unsigned int wlen,unsigned int hooknum, -struct ip_vs_iphdr *ciph) +unsigned int hooknum,struct ip_vs_iphdr *ciph) {struct rtable *rt;/* Route to the other host */ int rc;int local;int rt_mode, was_input; +bool has_ports = false; +unsigned int wlen;EnterFunction (10 );
@@ -1576,6 +1577,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, goto tx_error; } + wlen = ciph->len; +if (ciph->protocol ==IPPROTO_TCP || ciph->protocol ==IPPROTO_UDP || + ciph->protocol ==IPPROTO_SCTP ) { + wlen +=2 *sizeof (__u16);/* Also mangle ports */ + has_ports = true; + } +/* copy-on-write the packet before mangling it */ if (skb_ensure_writable (skb, wlen)) goto tx_error;
@@ -1583,7 +1591,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,if (skb_cow (skb, rt->dst.dev->hard_header_len)) goto tx_error; -ip_vs_nat_icmp (skb, pp, cp,0 , toff); +ip_vs_nat_icmp (skb, pp, cp,0 , toff, has_ports);/* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df =1 ;
@@ -1603,10 +1611,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,int ip_vs_icmp_xmit_v6 (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,unsigned int toff, -unsigned int wlen,unsigned int hooknum, -struct ip_vs_iphdr *ciph) +unsigned int hooknum,struct ip_vs_iphdr *ciph) { +bool has_ports = false;struct rt6_info *rt;/* Route to the other host */ +unsigned int wlen;int rc;int local;int rt_mode;
@@ -1666,6 +1675,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, goto tx_error; } + wlen = ciph->len; +if (ciph->protocol ==IPPROTO_TCP || ciph->protocol ==IPPROTO_UDP || + ciph->protocol ==IPPROTO_SCTP ) { + wlen +=2 *sizeof (__u16);/* Also mangle ports */ + has_ports = true; + } +/* copy-on-write the packet before mangling it */ if (skb_ensure_writable (skb, wlen)) goto tx_error;
@@ -1673,7 +1689,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,if (skb_cow (skb, rt->dst.dev->hard_header_len)) goto tx_error; -ip_vs_nat_icmp_v6 (skb, pp, cp,0 , toff, ciph); +ip_vs_nat_icmp_v6 (skb, pp, cp,0 , toff, has_ports, ciph);/* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df =1 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 331/609 netfilter: nft_payload: fix mask build for partial field offload
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei (Microsoft) <[email protected]>
[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ]
nft_payload_offload_mask() builds the offload match mask for a payload expression that covers only part of a header field. For a partial IPv6 address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which is undefined on the 32-bit int operand. It also trims only one word, so the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 the trim is skipped entirely), leaving the mask covering more bytes than the rule matches.
UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 shift exponent 120 is too large for 32-bit type 'int' ...
The match is byte-granular and struct nft_data is zero-initialised, so the correct mask is simply the first priv_len bytes set to 0xff. Set those bytes directly and drop the word/shift trimming; this removes the undefined shift and no longer over-masks the trailing bytes.
--- net/netfilter/nft_payload.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-)
net/netfilter/nft_payload.c +1 -11
@@ -241,9 +241,7 @@ static bool nft_payload_reduce(struct nft_regs_track *track,static bool nft_payload_offload_mask (struct nft_offload_reg *reg,u32 priv_len,u32 field_len) { -unsigned int remainder, delta, k;struct nft_data mask = {}; -__be32 remainder_mask;if (priv_len == field_len) {memset (®->mask,0xff , priv_len);
@@ -252,15 +250,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,return false; } -memset (&mask,0xff , field_len); - remainder = priv_len %sizeof (u32); -if (remainder) { - k = priv_len /sizeof (u32); - delta = field_len - priv_len; - remainder_mask =htonl (~((1 << (delta *BITS_PER_BYTE )) -1 )); - mask.data[k] = (__force u32 )remainder_mask; - } - +memset (&mask,0xff , priv_len);memcpy (®->mask, &mask, field_len);return true;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 332/609 rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilia Gavrilov <[email protected]>
[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ]
When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst is never initialized because inet6_init() exits before addrconf_init() is called to initialize it. An attempt to bind an RDS socket to an ipv6 address results in a crash in __ipv6_chk_addr_and_flags()
KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0 Call Trace: <TASK> ipv6_chk_addr+0x3b/0x50 rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp] rds_trans_get_preferred+0x15d/0x2d0 [rds] ? trace_hardirqs_on+0x2d/0x110 rds_bind+0x1433/0x1d60 [rds] ? rds_remove_bound+0xd50/0xd50 [rds] ? aa_af_perm+0x250/0x250 ? __might_fault+0xde/0x190 ? __sys_bind+0x1dc/0x210 __sys_bind+0x1dc/0x210 ? __ia32_sys_socketpair+0x100/0x100 ? restore_fpregs_from_fpstate+0x53/0x100 __x64_sys_bind+0x73/0xb0 ? syscall_enter_from_user_mode+0x1c/0x50 do_syscall_64+0x34/0x80 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 RIP: 0033:0x7f47f8269ea9 </TASK>
The following code reproduces the issue:
struct sockaddr_in6 addr;
s = socket(PF_RDS, SOCK_SEQPACKET, 0);
memset(&addr, 0, sizeof(addr));inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr);
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(PORT);
bind(s, &addr, sizeof(addr));
Found by InfoTeCS on behalf of Linux Verification Center (linuxtesting.org) with Syzkaller.
Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()")
--- net/rds/ib.c | 4 ++++ net/rds/ib_cm.c | 4 ++++ net/rds/tcp.c | 8 +++++--- 3 files changed, 13 insertions(+), 3 deletions(-)
net/rds/ib.c +4 -0
@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, sa = (struct sockaddr *)&sin; }else {#if IS_ENABLED (CONFIG_IPV6 ) +if (!ipv6_mod_enabled ()) { + ret = -EADDRNOTAVAIL ; + goto out; + }memset (&sin6,0 ,sizeof (sin6)); sin6.sin6_family =AF_INET6 ; sin6.sin6_addr = *addr;
net/rds/ib_cm.c +4 -0
@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, dp = event->param.conn.private_data;if (isv6) {#if IS_ENABLED (CONFIG_IPV6 ) +if (!ipv6_mod_enabled ()) { + err = -EOPNOTSUPP ; + goto out; + } dp_cmn = &dp->ricp_v6.dp_cmn; saddr6 = &dp->ricp_v6.dp_saddr; daddr6 = &dp->ricp_v6.dp_daddr;
net/rds/tcp.c +5 -3
@@ -341,9 +341,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,rcu_read_unlock (); }#if IS_ENABLED (CONFIG_IPV6 ) - ret =ipv6_chk_addr (net, addr, dev,0 ); -if (ret) -return 0 ; +if (ipv6_mod_enabled ()) { + ret =ipv6_chk_addr (net, addr, dev,0 ); +if (ret) +return 0 ; + }#endif return -EADDRNOTAVAIL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 333/609 rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ]
rds_tcp_laddr_check() looks up a scoped IPv6 interface with dev_get_by_index_rcu(), drops the RCU read-side lock, and only then passes the bare struct net_device * into ipv6_chk_addr().
dev_get_by_index_rcu() only keeps the device alive within the same RCU read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can free the net_device; ipv6_chk_addr() then dereferences the stale pointer in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading freed memory.
Keep the RCU read-side lock held across the ipv6_chk_addr() call instead of dropping it right after the lookup, so the device cannot be freed while it is in use.
BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) Read of size 8 at addr ffff8880106ec000 by task exploit/153 Call Trace: ... kasan_report (mm/kasan/report.c:595) __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) rds_tcp_laddr_check (net/rds/tcp.c:370) rds_bind (net/rds/bind.c:248) __sys_bind (net/socket.c:1920) __x64_sys_bind (net/socket.c:1956) do_syscall_64 (arch/x86/entry/syscall_64.c:63) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
--- net/rds/tcp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
net/rds/tcp.c +5 -3
@@ -330,23 +330,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,/* If the scope_id is specified, check only those addresses * hosted on the specified interface. */ +rcu_read_lock ();if (scope_id !=0 ) { -rcu_read_lock (); dev =dev_get_by_index_rcu (net, scope_id);/* scope_id is not valid... */ if (!dev) {rcu_read_unlock ();return -EADDRNOTAVAIL ; } -rcu_read_unlock (); }#if IS_ENABLED (CONFIG_IPV6 )if (ipv6_mod_enabled ()) { ret =ipv6_chk_addr (net, addr, dev,0 ); -if (ret) +if (ret) { +rcu_read_unlock ();return 0 ; + } }#endif +rcu_read_unlock ();return -EADDRNOTAVAIL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 334/609 pinctrl-amd: Dont clear S4 wake bits at probe
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello <[email protected]>
[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ]
commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") introduced a regression where Wake-on-LAN no longer works after suspend or shutdown on some AMD platforms.
Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME does not use GPIO IRQ infrastructure and relies on firmware configuration.
The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") was to clear spurious wake bits left by firmware to prevent unwanted wakeups. However, S4 wake bits are used for hardware-level wake sources like WoL that bypass the kernel's IRQ wake API.
Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits:
- Firmware-configured S4 wake sources (WoL) continue working
- Kernel maintains control of S3/S0i3 wake policy via set_wake()
- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl:
amd: Take suspend type into consideration which pins are non-wake")
The trade-off is that firmware-programmed spurious S4 wake bits remain set, but this is less problematic than breaking WoL.
--- drivers/pinctrl/pinctrl-amd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/pinctrl/pinctrl-amd.c +1 -2
@@ -869,8 +869,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev)u32 pin_reg, mask;int i; - mask =BIT (WAKE_CNTRL_OFF_S0I3 ) |BIT (WAKE_CNTRL_OFF_S3 ) | -BIT (WAKE_CNTRL_OFF_S4 ); + mask =BIT (WAKE_CNTRL_OFF_S0I3 ) |BIT (WAKE_CNTRL_OFF_S3 );for (i =0 ; i < desc->npins; i++) {int pin = desc->pins[i].number;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 335/609 scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ]
iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the target-supplied data segment. The segment carries a 2-byte sense length followed by the sense bytes, so it must hold 2 + senselen bytes, but the bounds check only requires datalen >= senselen:
senselen = get_unaligned_be16(data); if (datalen < senselen) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2, min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
A target that returns a SCSI Response whose datalen equals senselen (with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + 2 read up to two bytes past the received data. Those bytes are stale conn->data contents and end up in the command's sense buffer, which is returned to userspace.
Account for the 2-byte sense length prefix in the check.
--- drivers/scsi/libiscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/libiscsi.c +1 -1
@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, } senselen =get_unaligned_be16 (data); -if (datalen < senselen) +if (datalen < senselen +2 ) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 336/609 scsi: libiscsi_tcp: Bound SCSI Response data segment to the connection buffer
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ]
iscsi_tcp_hdr_dissect() receives the data segment of several PDU types into the fixed-size conn->data buffer, which is allocated for ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU whose DataSegmentLength exceeds that buffer.
The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its data segment (sense/response data) into conn->data via iscsi_tcp_data_recv_prep(), but it does so without the same check. The only upstream bound on in.datalen is conn->max_recv_dlength, the initiator's advertised MaxRecvDataSegmentLength, which is commonly negotiated well above 8192 (open-iscsi defaults to 262144). A target that returns a SCSI Response with a DataSegmentLength between 8193 and max_recv_dlength therefore overflows the 8192-byte conn->data buffer.
Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly like those responses: bound the data segment, receive it into conn->data when present, and otherwise complete the PDU with no data. Fold the opcode into that case group rather than duplicating the check.
--- drivers/scsi/libiscsi_tcp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
drivers/scsi/libiscsi_tcp.c +1 -7
@@ -763,13 +763,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) rc =__iscsi_complete_pdu (conn, hdr,NULL ,0 );spin_unlock (&conn->session->back_lock);break ; -case ISCSI_OP_SCSI_CMD_RSP : -if (tcp_conn->in.datalen) { -iscsi_tcp_data_recv_prep (tcp_conn); -return 0 ; - } - rc =iscsi_complete_pdu (conn, hdr,NULL ,0 ); -break ;case ISCSI_OP_R2T :if (ahslen) { rc =ISCSI_ERR_AHSLEN ;
@@ -777,6 +770,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) } rc =iscsi_tcp_r2t_rsp (conn, hdr);break ; +case ISCSI_OP_SCSI_CMD_RSP :case ISCSI_OP_LOGIN_RSP :case ISCSI_OP_TEXT_RSP :case ISCSI_OP_REJECT :
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 337/609 scsi: libsas: Abort all in-flight requests when device is gone
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jason Yan <[email protected]>
[ Upstream commit 0e4b1791d9b192ac263a03707d876132eb0f8dab ]
When a disk is removed with in-flight I/O, the application needs to wait for 30 seconds (depending on the timeout configuration) to hear back from the kernel. Xingui tried to fix this issue by aborting the ATA link for SATA devices[1], however this approach left the SAS devices unresolved.
Try to fix this issue by aborting all in-flight requests when the device is gone. This is implemented by iterating over the tagset.
[1] https://lore.kernel.org/lkml/[email protected]/T/
Stable-dep-of: 3dbbbf656b85 ("scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race")
--- drivers/scsi/libsas/sas_discover.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
drivers/scsi/libsas/sas_discover.c +29 -0
@@ -360,6 +360,33 @@ static void sas_destruct_ports(struct asd_sas_port *port) } } +static bool sas_abort_cmd (struct request *req,void *data) +{ +struct scsi_cmnd *cmd =blk_mq_rq_to_pdu (req); +struct domain_device *dev = data; + +if (dev ==cmd_to_domain_dev (cmd)) +blk_abort_request (req); +return true; +} + +static void sas_abort_device_scsi_cmds (struct domain_device *dev) +{ +struct sas_ha_struct *sas_ha = dev->port->ha; +struct Scsi_Host *shost = sas_ha->core.shost; + +if (dev_is_expander (dev->dev_type)) +return ; + +/* +* For removed device with active IOs, the user space applications have +* to spend very long time waiting for the timeout. This is not +* necessary because a removed device will not return the IOs. +* Abort the inflight IOs here so that EH can be quickly kicked in. +*/ +blk_mq_tagset_busy_iter (&shost->tag_set, sas_abort_cmd, dev); +} +void sas_unregister_dev (struct asd_sas_port *port,struct domain_device *dev) { if (!test_bit(SAS_DEV_DESTROY, &dev->state) &&
@@ -372,6 +399,8 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) }if (!test_and_set_bit (SAS_DEV_DESTROY , &dev->state)) { +if (test_bit (SAS_DEV_GONE , &dev->state)) +sas_abort_device_scsi_cmds (dev);sas_rphy_unlink (dev->rphy);list_move_tail (&dev->disco_list_node, &port->destroy_list); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 338/609 scsi: libsas: Delete struct scsi_core
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Garry <[email protected]>
[ Upstream commit 1136a0225d0582c4464fa37e3a91ed4b19b8745e ]
Since commit 79855d178557 ("libsas: remove task_collector mode"), struct scsi_core only contains a reference to the shost. struct scsi_core is only used in sas_ha_struct.core, so delete scsi_core and replace with a reference to the shost there.
Stable-dep-of: 3dbbbf656b85 ("scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race")
--- drivers/scsi/aic94xx/aic94xx_hwi.c | 2 +- drivers/scsi/aic94xx/aic94xx_init.c | 6 +++--- drivers/scsi/hisi_sas/hisi_sas_main.c | 6 +++--- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 6 +++--- drivers/scsi/isci/host.h | 2 +- drivers/scsi/isci/init.c | 4 ++-- drivers/scsi/libsas/sas_ata.c | 8 ++++---- drivers/scsi/libsas/sas_discover.c | 8 ++++---- drivers/scsi/libsas/sas_expander.c | 2 +- drivers/scsi/libsas/sas_host_smp.c | 4 ++-- drivers/scsi/libsas/sas_init.c | 16 ++++++++-------- drivers/scsi/libsas/sas_phy.c | 8 ++++---- drivers/scsi/libsas/sas_port.c | 6 +++--- drivers/scsi/libsas/sas_scsi_host.c | 14 +++++++------- drivers/scsi/mvsas/mv_init.c | 4 ++-- drivers/scsi/pm8001/pm8001_init.c | 2 +- include/scsi/libsas.h | 7 +------ 17 files changed, 50 insertions(+), 55 deletions(-)
drivers/scsi/aic94xx/aic94xx_hwi.c +1 -1
@@ -28,7 +28,7 @@ static int asd_get_user_sas_addr(struct asd_ha_struct *asd_ha)if (asd_ha->hw_prof.sas_addr[0 ])return 0 ; -return sas_request_addr (asd_ha->sas_ha.core.shost, +return sas_request_addr (asd_ha->sas_ha.shost, asd_ha->hw_prof.sas_addr); }
drivers/scsi/aic94xx/aic94xx_init.c +3 -3
@@ -688,8 +688,8 @@ static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha) err =sas_unregister_ha (&asd_ha->sas_ha); -sas_remove_host (asd_ha->sas_ha.core.shost); -scsi_host_put (asd_ha->sas_ha.core.shost); +sas_remove_host (asd_ha->sas_ha.shost); +scsi_host_put (asd_ha->sas_ha.shost);kfree (asd_ha->sas_ha.sas_phy);kfree (asd_ha->sas_ha.sas_port);
@@ -739,7 +739,7 @@ static int asd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)asd_printk ("found %s, device %s\n" , asd_ha->name,pci_name (dev));SHOST_TO_SAS_HA (shost) = &asd_ha->sas_ha; - asd_ha->sas_ha.core.shost = shost; + asd_ha->sas_ha.shost = shost; shost->transportt = aic94xx_transport_template; shost->max_id = ~0 ; shost->max_lun = ~0 ;
drivers/scsi/hisi_sas/hisi_sas_main.c +3 -3
@@ -2458,7 +2458,7 @@ int hisi_sas_probe(struct platform_device *pdev, sha->lldd_module =THIS_MODULE ; sha->sas_addr = &hisi_hba->sas_addr[0 ]; sha->num_phys = hisi_hba->n_phy; - sha->core.shost = hisi_hba->shost; + sha->shost = hisi_hba->shost;for (i =0 ; i < hisi_hba->n_phy; i++) { sha->sas_phy[i] = &hisi_hba->phy[i].sas_phy;
@@ -2500,12 +2500,12 @@ int hisi_sas_remove(struct platform_device *pdev) {struct sas_ha_struct *sha =platform_get_drvdata (pdev);struct hisi_hba *hisi_hba = sha->lldd_ha; -struct Scsi_Host *shost = sha->core.shost; +struct Scsi_Host *shost = sha->shost;del_timer_sync (&hisi_hba->timer);sas_unregister_ha (sha); -sas_remove_host (sha->core.shost); +sas_remove_host (shost);hisi_sas_free (hisi_hba);scsi_host_put (shost);
drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +3 -3
@@ -4820,7 +4820,7 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id) sha->sas_phy = arr_phy; sha->sas_port = arr_port; - sha->core.shost = shost; + sha->shost = shost; sha->lldd_ha = hisi_hba; shost->transportt = hisi_sas_stt;
@@ -4921,14 +4921,14 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev)struct device *dev = &pdev->dev;struct sas_ha_struct *sha =dev_get_drvdata (dev);struct hisi_hba *hisi_hba = sha->lldd_ha; -struct Scsi_Host *shost = sha->core.shost; +struct Scsi_Host *shost = sha->shost;pm_runtime_get_noresume (dev);del_timer_sync (&hisi_hba->timer);sas_unregister_ha (sha);flush_workqueue (hisi_hba->wq); -sas_remove_host (sha->core.shost); +sas_remove_host (shost);hisi_sas_v3_destroy_irqs (pdev, hisi_hba);hisi_sas_free (hisi_hba);
drivers/scsi/isci/host.h +1 -1
@@ -306,7 +306,7 @@ static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev)static inline struct Scsi_Host *to_shost (struct isci_host *ihost) { -return ihost->sas_ha.core.shost; +return ihost->sas_ha.shost; }#define for_each_isci_host (id, ihost, pdev) \
drivers/scsi/isci/init.c +2 -2
@@ -571,7 +571,7 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) goto err_shost;SHOST_TO_SAS_HA (shost) = &ihost->sas_ha; - ihost->sas_ha.core.shost = shost; + ihost->sas_ha.shost = shost; shost->transportt = isci_transport_template; shost->max_id = ~0 ;
@@ -726,7 +726,7 @@ static int isci_resume(struct device *dev)sas_prep_resume_ha (&ihost->sas_ha);isci_host_init (ihost); -isci_host_start (ihost->sas_ha.core.shost); +isci_host_start (ihost->sas_ha.shost);wait_for_start (ihost);sas_resume_ha (&ihost->sas_ha);
drivers/scsi/libsas/sas_ata.c +4 -4
@@ -162,7 +162,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)struct ata_port *ap = qc->ap;struct domain_device *dev = ap->private_data;struct sas_ha_struct *sas_ha = dev->port->ha; -struct Scsi_Host *host = sas_ha->core.shost; +struct Scsi_Host *host = sas_ha->shost;struct sas_internal *i =to_sas_internal (host->transportt);/* TODO: we should try to remove that unlock */
@@ -236,7 +236,7 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc)static struct sas_internal *dev_to_sas_internal (struct domain_device *dev) { -return to_sas_internal (dev->port->ha->core.shost->transportt); +return to_sas_internal (dev->port->ha->shost->transportt); }static int sas_get_ata_command_set (struct domain_device *dev);
@@ -576,7 +576,7 @@ static struct ata_port_info sata_port_info = {int sas_ata_init (struct domain_device *found_dev) {struct sas_ha_struct *ha = found_dev->port->ha; -struct Scsi_Host *shost = ha->core.shost; +struct Scsi_Host *shost = ha->shost;struct ata_host *ata_host;struct ata_port *ap;int rc;
@@ -766,7 +766,7 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie)struct sas_ha_struct *ha = dev->port->ha;sas_ata_printk (KERN_DEBUG , dev,"dev error handler\n" ); -ata_scsi_port_error_handler (ha->core.shost, ap); +ata_scsi_port_error_handler (ha->shost, ap);sas_put_device (dev); }
drivers/scsi/libsas/sas_discover.c +4 -4
@@ -170,7 +170,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) {int res =0 ;struct sas_ha_struct *sas_ha = dev->port->ha; -struct Scsi_Host *shost = sas_ha->core.shost; +struct Scsi_Host *shost = sas_ha->shost;struct sas_internal *i =to_sas_internal (shost->transportt);if (!i->dft->lldd_dev_found)
@@ -192,7 +192,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev)void sas_notify_lldd_dev_gone (struct domain_device *dev) {struct sas_ha_struct *sas_ha = dev->port->ha; -struct Scsi_Host *shost = sas_ha->core.shost; +struct Scsi_Host *shost = sas_ha->shost;struct sas_internal *i =to_sas_internal (shost->transportt);if (!i->dft->lldd_dev_gone)
@@ -234,7 +234,7 @@ static void sas_suspend_devices(struct work_struct *work)struct domain_device *dev;struct sas_discovery_event *ev =to_sas_discovery_event (work);struct asd_sas_port *port = ev->port; -struct Scsi_Host *shost = port->ha->core.shost; +struct Scsi_Host *shost = port->ha->shost;struct sas_internal *si =to_sas_internal (shost->transportt);clear_bit (DISCE_SUSPEND , &port->disc.pending);
@@ -373,7 +373,7 @@ static bool sas_abort_cmd(struct request *req, void *data)static void sas_abort_device_scsi_cmds (struct domain_device *dev) {struct sas_ha_struct *sas_ha = dev->port->ha; -struct Scsi_Host *shost = sas_ha->core.shost; +struct Scsi_Host *shost = sas_ha->shost;if (dev_is_expander (dev->dev_type))return ;
drivers/scsi/libsas/sas_expander.c +1 -1
@@ -37,7 +37,7 @@ static int smp_execute_task_sg(struct domain_device *dev,int res, retry;struct sas_task *task =NULL ;struct sas_internal *i = -to_sas_internal (dev->port->ha->core.shost->transportt); +to_sas_internal (dev->port->ha->shost->transportt);struct sas_ha_struct *ha = dev->port->ha;pm_runtime_get_sync (ha->dev);
drivers/scsi/libsas/sas_host_smp.c +2 -2
@@ -114,7 +114,7 @@ static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data, u8 reg_type, u8 reg_index, u8 reg_count, u8 *req_data) { - struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); + struct sas_internal *i = to_sas_internal(sas_ha->shost->transportt);int written;if (i->dft->lldd_write_gpio ==NULL ) {
@@ -182,7 +182,7 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id, enum sas_linkrate max, u8 *resp_data) { struct sas_internal *i = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt);struct sas_phy_linkrates rates;struct asd_sas_phy *asd_phy;
drivers/scsi/libsas/sas_init.c +8 -8
@@ -186,7 +186,7 @@ static int sas_get_linkerrors(struct sas_phy *phy)struct sas_ha_struct *sas_ha =SHOST_TO_SAS_HA (shost);struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];struct sas_internal *i = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt);return i->dft->lldd_control_phy (asd_phy,PHY_FUNC_GET_EVENTS ,NULL ); }
@@ -235,7 +235,7 @@ static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset)struct sas_ha_struct *sas_ha =SHOST_TO_SAS_HA (shost);struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];struct sas_internal *i = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt);if (!hard_reset &&sas_try_ata_reset (asd_phy) ==0 )return 0 ;
@@ -269,7 +269,7 @@ int sas_phy_enable(struct sas_phy *phy, int enable)struct sas_ha_struct *sas_ha =SHOST_TO_SAS_HA (shost);struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];struct sas_internal *i = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt);if (enable) ret =transport_sas_phy_reset (phy,0 );
@@ -306,7 +306,7 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset)struct sas_ha_struct *sas_ha =SHOST_TO_SAS_HA (shost);struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];struct sas_internal *i = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt); ret = i->dft->lldd_control_phy (asd_phy, reset_type,NULL ); }else {
@@ -342,7 +342,7 @@ int sas_set_phy_speed(struct sas_phy *phy,struct sas_ha_struct *sas_ha =SHOST_TO_SAS_HA (shost);struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];struct sas_internal *i = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt); ret = i->dft->lldd_control_phy (asd_phy,PHY_FUNC_SET_LINK_RATE , rates);
@@ -441,7 +441,7 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)/* all phys are back up or timed out, turn on i/o so we can * flush out disks that did not return */ -scsi_unblock_requests (ha->core.shost); +scsi_unblock_requests (ha->shost);if (drain)sas_drain_work (ha);clear_bit (SAS_HA_RESUMING , &ha->state);
@@ -471,7 +471,7 @@ void sas_suspend_ha(struct sas_ha_struct *ha)int i;sas_disable_events (ha); -scsi_block_requests (ha->core.shost); +scsi_block_requests (ha->shost);for (i =0 ; i < ha->num_phys; i++) {struct asd_sas_port *port = ha->sas_port[i];
@@ -644,7 +644,7 @@ struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy,struct asd_sas_event *event;struct sas_ha_struct *sas_ha = phy->ha;struct sas_internal *i = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt); event =kmem_cache_zalloc (sas_event_cache, gfp_flags);if (!event)
drivers/scsi/libsas/sas_phy.c +4 -4
@@ -38,7 +38,7 @@ static void sas_phye_oob_error(struct work_struct *work)struct sas_ha_struct *sas_ha = phy->ha;struct asd_sas_port *port = phy->port;struct sas_internal *i = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt);sas_deform_port (phy,1 );
@@ -66,7 +66,7 @@ static void sas_phye_spinup_hold(struct work_struct *work)struct asd_sas_phy *phy = ev->phy;struct sas_ha_struct *sas_ha = phy->ha;struct sas_internal *i = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt); phy->error =0 ; i->dft->lldd_control_phy (phy,PHY_FUNC_RELEASE_SPINUP_HOLD ,NULL );
@@ -95,7 +95,7 @@ static void sas_phye_shutdown(struct work_struct *work)struct asd_sas_phy *phy = ev->phy;struct sas_ha_struct *sas_ha = phy->ha;struct sas_internal *i = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt);if (phy->enabled) {int ret;
@@ -131,7 +131,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)spin_lock_init (&phy->sas_prim_lock); phy->frame_rcvd_size =0 ; - phy->phy =sas_phy_alloc (&sas_ha->core.shost->shost_gendev, i); + phy->phy =sas_phy_alloc (&sas_ha->shost->shost_gendev, i);if (!phy->phy)return -ENOMEM ;
drivers/scsi/libsas/sas_port.c +3 -3
@@ -28,7 +28,7 @@ static void sas_resume_port(struct asd_sas_phy *phy)struct domain_device *dev, *n;struct asd_sas_port *port = phy->port;struct sas_ha_struct *sas_ha = phy->ha; -struct sas_internal *si =to_sas_internal (sas_ha->core.shost->transportt); +struct sas_internal *si =to_sas_internal (sas_ha->shost->transportt);if (si->dft->lldd_port_formed) si->dft->lldd_port_formed (phy);
@@ -109,7 +109,7 @@ static void sas_form_port(struct asd_sas_phy *phy)struct asd_sas_port *port = phy->port;struct domain_device *port_dev =NULL ;struct sas_internal *si = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt);unsigned long flags;if (port) {
@@ -212,7 +212,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)struct sas_ha_struct *sas_ha = phy->ha;struct asd_sas_port *port = phy->port;struct sas_internal *si = -to_sas_internal (sas_ha->core.shost->transportt); +to_sas_internal (sas_ha->shost->transportt);struct domain_device *dev;unsigned long flags;
drivers/scsi/libsas/sas_scsi_host.c +7 -7
@@ -279,7 +279,7 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task)unsigned long flags;int i, res;struct sas_internal *si = -to_sas_internal (task->dev->port->ha->core.shost->transportt); +to_sas_internal (task->dev->port->ha->shost->transportt);for (i =0 ; i <5 ; i++) {pr_notice ("%s: aborting task 0x%p\n" , __func__, task);
@@ -327,7 +327,7 @@ static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd)int res =TMF_RESP_FUNC_FAILED ;struct scsi_lun lun;struct sas_internal *i = -to_sas_internal (dev->port->ha->core.shost->transportt); +to_sas_internal (dev->port->ha->shost->transportt);int_to_scsilun (cmd->device->lun, &lun);
@@ -355,7 +355,7 @@ static int sas_recover_I_T(struct domain_device *dev) {int res =TMF_RESP_FUNC_FAILED ;struct sas_internal *i = -to_sas_internal (dev->port->ha->core.shost->transportt); +to_sas_internal (dev->port->ha->shost->transportt);pr_notice ("I_T nexus reset for dev %016llx\n" ,SAS_ADDR (dev->sas_addr));
@@ -410,7 +410,7 @@ static void sas_wait_eh(struct domain_device *dev)spin_unlock_irq (&ha->lock);/* make sure SCSI EH is complete */ -if (scsi_host_in_recovery (ha->core.shost)) { +if (scsi_host_in_recovery (ha->shost)) {msleep (10 ); goto retry; }
@@ -440,7 +440,7 @@ static int sas_queue_reset(struct domain_device *dev, int reset_type,set_bit (SAS_DEV_EH_PENDING , &dev->state);set_bit (reset_type, &dev->state);int_to_scsilun (lun, &dev->ssp_dev.reset_lun); -scsi_schedule_eh (ha->core.shost); +scsi_schedule_eh (ha->shost); }spin_unlock_irq (&ha->lock);
@@ -926,7 +926,7 @@ static int sas_execute_internal_abort(struct domain_device *device, unsigned int qid, void *data) { struct sas_ha_struct *ha = device->port->ha; -struct sas_internal *i =to_sas_internal (ha->core.shost->transportt); +struct sas_internal *i =to_sas_internal (ha->shost->transportt);struct sas_task *task =NULL ;int res, retry;
@@ -1016,7 +1016,7 @@ int sas_execute_tmf(struct domain_device *device, void *parameter, {struct sas_task *task;struct sas_internal *i = -to_sas_internal (device->port->ha->core.shost->transportt); +to_sas_internal (device->port->ha->shost->transportt);int res, retry;for (retry =0 ; retry <TASK_RETRY ; retry++) {
drivers/scsi/mvsas/mv_init.c +2 -2
@@ -416,7 +416,7 @@ static int mvs_prep_sas_ha_init(struct Scsi_Host *shost, sha->sas_phy = arr_phy; sha->sas_port = arr_port; - sha->core.shost = shost; + sha->shost = shost; sha->lldd_ha =kzalloc (sizeof (struct mvs_prv_info ),GFP_KERNEL );if (!sha->lldd_ha)
@@ -473,7 +473,7 @@ static void mvs_post_sas_ha_init(struct Scsi_Host *shost, shost->sg_tablesize =min_t (u16,SG_ALL ,MVS_MAX_SG ); shost->can_queue = can_queue; mvi->shost->cmd_per_lun =MVS_QUEUE_SIZE ; - sha->core.shost = mvi->shost; + sha->shost = mvi->shost; }static void mvs_init_sas_add (struct mvs_info *mvi)
drivers/scsi/pm8001/pm8001_init.c +1 -1
@@ -653,7 +653,7 @@ static void pm8001_post_sas_ha_init(struct Scsi_Host *shost, sha->lldd_module =THIS_MODULE ; sha->sas_addr = &pm8001_ha->sas_addr[0 ]; sha->num_phys = chip_info->n_phy; - sha->core.shost = shost; + sha->shost = shost; }/**
include/scsi/libsas.h +1 -6
@@ -346,11 +346,6 @@ struct asd_sas_phy {void *lldd_phy;/* not touched by the sas_class_code */ }; -struct scsi_core { -struct Scsi_Host *shost; - -}; -enum sas_ha_state {SAS_HA_REGISTERED ,SAS_HA_DRAINING ,
@@ -371,7 +366,7 @@ struct sas_ha_struct {struct mutex disco_mutex; -struct scsi_core core; +struct Scsi_Host *shost;/* public: */ char *sas_ha_name;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 339/609 scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xingui Yang <[email protected]>
[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ]
Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock:
the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue, calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI devices and waits for the host to become runtime-active. But the host cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the drain is blocked on that very handler.
However skipping the drain reintroduces a race: hisi_sas returns from resume before all PHY UP work and libsas discovery work finish. The controller may then autosuspend while disks are still waking up. The disks issue IO to a suspended controller, the IO fails, and the disks get disabled.
Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT notification to after sas_drain_work(). By then the host resume is about to complete, so device removal through device_link no longer blocks on the resume and the cycle is broken.
With the deadlock gone, restore sas_resume_ha() (the draining variant) in hisi_sas and remove sas_resume_ha_no_sync().
The reorder is safe for the other libsas consumers (isci, pm8001, aic94xx, mvsas). During suspend, sas_suspend_devices() calls sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a timed-out phy's disk is immediately rejected by the LLDD before reaching
hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET), and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both complete directly via scsi_done() without entering SCSI EH. This is identical in both the old and new ordering since lldd_dev_gone runs during suspend, before resume. The reorder only affects when the PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work() vs. asynchronous after resume returns), not whether I/O can reach the device. aic94xx and mvsas do not register any PM ops and never reach this code path.
--- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------ drivers/scsi/libsas/sas_init.c | 37 +++++++++++++------------- include/scsi/libsas.h | 1 - 3 files changed, 19 insertions(+), 29 deletions(-)
drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +1 -9
@@ -5047,15 +5047,7 @@ static int _resume_v3_hw(struct device *device)return rc; }phys_init_v3_hw (hisi_hba); - -/* -* If a directly-attached disk is removed during suspend, a deadlock -* may occur, as the PHYE_RESUME_TIMEOUT processing will require the -* hisi_hba->device to be active, which can only happen when resume -* completes. So don't wait for the HA event workqueue to drain upon -* resume. -*/ -sas_resume_ha_no_sync (sha); +sas_resume_ha (sha);clear_bit (HISI_SAS_RESETTING_BIT , &hisi_hba->flags);dev_warn (dev,"end of resuming controller\n" );
drivers/scsi/libsas/sas_init.c +18 -19
@@ -412,7 +412,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) } } -static void _sas_resume_ha (struct sas_ha_struct *ha,bool drain) +void sas_resume_ha (struct sas_ha_struct *ha) {const unsigned long tmo =msecs_to_jiffies (25000 );int i;
@@ -428,6 +428,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)dev_info (ha->dev,"waiting up to 25 seconds for %d phy%s to resume\n" , i, i >1 ?"s" :"" );wait_event_timeout (ha->eh_wait_q,phys_suspended (ha) ==0 , tmo); + +/* +* All phys are back up or timed out. Turn on I/O and drain +* pending work. +*/ +scsi_unblock_requests (ha->shost); +sas_drain_work (ha); + +/* +* Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler +* calls sas_deform_port() -> sas_destruct_devices(), which removes +* SCSI devices and, for LLDDs using device_link() PM sync, waits +* for the host to be runtime-active. Sending it before the drain +* would deadlock: the drain waits for the handler, the handler +* waits for host resume, and host resume waits for the drain to +* finish. +*/ for (i =0 ; i < ha->num_phys; i++) {struct asd_sas_phy *phy = ha->sas_phy[i];
@@ -438,12 +455,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) } } -/* all phys are back up or timed out, turn on i/o so we can -* flush out disks that did not return -*/ -scsi_unblock_requests (ha->shost); -if (drain) -sas_drain_work (ha);clear_bit (SAS_HA_RESUMING , &ha->state);sas_queue_deferred_work (ha);
@@ -452,20 +463,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)*/ sas_resume_insert_broadcast_ha (ha); } - -void sas_resume_ha (struct sas_ha_struct *ha) -{ -_sas_resume_ha (ha, true); -}EXPORT_SYMBOL (sas_resume_ha); -/* A no-sync variant, which does not call sas_drain_ha(). */ -void sas_resume_ha_no_sync (struct sas_ha_struct *ha) -{ -_sas_resume_ha (ha, false); -} -EXPORT_SYMBOL (sas_resume_ha_no_sync); -void sas_suspend_ha (struct sas_ha_struct *ha) {int i;
include/scsi/libsas.h +0 -1
@@ -703,7 +703,6 @@ extern int sas_register_ha(struct sas_ha_struct *);extern int sas_unregister_ha (struct sas_ha_struct *);extern void sas_prep_resume_ha (struct sas_ha_struct *sas_ha);extern void sas_resume_ha (struct sas_ha_struct *sas_ha); -extern void sas_resume_ha_no_sync (struct sas_ha_struct *sas_ha);extern void sas_suspend_ha (struct sas_ha_struct *sas_ha);int sas_set_phy_speed (struct sas_phy *phy,struct sas_phy_linkrates *rates);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 340/609 smb: client: fix buffer leaks in SMB1 read and write
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawei Feng <[email protected]>
[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ]
CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request buffer before checking whether tcon->ses->server is NULL. If that defensive check ever fails, the helper returns -ECONNABORTED without releasing the request buffer.
Fix these leaks by releasing the allocated request buffer before returning from these error paths. Use cifs_small_buf_release() for the buffers allocated by small_smb_init() and cifs_buf_release() for the buffer allocated by smb_init().
The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1.
An x86_64 allyesconfig build showed no new warnings.
Runtime validation used a temporary fault-injection hook to force tcon->ses->server to NULL after request-buffer initialization. On the unfixed kernel, the harness observed two leaked small request buffers and one leaked large request buffer, with directed kmemleak dumps confirming the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer deltas remained.
--- fs/smb/client/cifssmb.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
fs/smb/client/cifssmb.c +9 -3
@@ -1409,8 +1409,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh =cpu_to_le16 ((__u16)(pid >>16 ));/* tcon and ses pointer are checked in smb_init */ -if (tcon->ses->server ==NULL ) +if (!tcon->ses->server) { +cifs_small_buf_release (pSMB);return -ECONNABORTED ; + } pSMB->AndXCommand =0xFF ;/* none */ pSMB->Fid = netfid;
@@ -1522,8 +1524,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh =cpu_to_le16 ((__u16)(pid >>16 ));/* tcon and ses pointer are checked in smb_init */ -if (tcon->ses->server ==NULL ) +if (!tcon->ses->server) { +cifs_buf_release (pSMB);return -ECONNABORTED ; + } pSMB->AndXCommand =0xFF ;/* none */ pSMB->Fid = netfid;
@@ -1776,8 +1780,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh =cpu_to_le16 ((__u16)(pid >>16 ));/* tcon and ses pointer are checked in smb_init */ -if (tcon->ses->server ==NULL ) +if (!tcon->ses->server) { +cifs_small_buf_release (pSMB);return -ECONNABORTED ; + } pSMB->AndXCommand =0xFF ;/* none */ pSMB->Fid = netfid;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 341/609 hwmon: (nct6755) Add support for NCT6799D
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit aee395bb190564a3fa22aa65c60812c25410e94a ]
NCT6799D is mostly compatible to NCT6798D, with minor variations.
Note that NCT6798D and NCT6799D have a new means to select temperature sources, and to report temperatures from those sources. This is not currently implemented, meaning that most likely not all temperatures are reported.
Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
--- drivers/hwmon/nct6775-core.c | 55 ++++++++++++++++++++++++++++++-- drivers/hwmon/nct6775-i2c.c | 2 ++ drivers/hwmon/nct6775-platform.c | 41 ++++++++++++++++++++++-- drivers/hwmon/nct6775.h | 2 +- 4 files changed, 94 insertions(+), 6 deletions(-)
drivers/hwmon/nct6775-core.c +53 -2
@@ -33,6 +33,7 @@* (0xd451) * nct6798d 14 7 7 2+6 0xd428 0xc1 0x5ca3 * (0xd429) +* nct6799d 14 7 7 2+6 0xd802 0xc1 0x5ca3 * * #temp lists the number of monitored temperature sources (first value) plus * the number of directly connectable temperature sensors (second value).
@@ -73,6 +74,7 @@ static const char * const nct6775_device_names[] = {"nct6796" ,"nct6797" ,"nct6798" , + "nct6799", };/* Common and NCT6775 specific data */
@@ -381,7 +383,7 @@ static const u16 NCT6779_REG_TEMP_OVER[ARRAY_SIZE(NCT6779_REG_TEMP)] = { 0x39, 0x155 };static const u16 NCT6779_REG_TEMP_OFFSET [] = { -0x454 ,0x455 ,0x456 ,0x44a ,0x44b ,0x44c }; +0x454 ,0x455 ,0x456 ,0x44a ,0x44b ,0x44c ,0x44d ,0x449 };static const char *const nct6779_temp_label[] = {"" ,
@@ -654,6 +656,44 @@ static const char *const nct6798_temp_label[] = {#define NCT6798_TEMP_MASK 0xbfff0ffe#define NCT6798_VIRT_TEMP_MASK 0x80000c00 +static const char *const nct6799_temp_label[] = { +"" , +"SYSTIN" , +"CPUTIN" , +"AUXTIN0" , +"AUXTIN1" , +"AUXTIN2" , +"AUXTIN3" , +"AUXTIN4" , +"SMBUSMASTER 0" , +"SMBUSMASTER 1" , +"Virtual_TEMP" , +"Virtual_TEMP" , +"" , +"AUXTIN5" , +"" , +"" , +"PECI Agent 0" , +"PECI Agent 1" , +"PCH_CHIP_CPU_MAX_TEMP" , +"PCH_CHIP_TEMP" , +"PCH_CPU_TEMP" , +"PCH_MCH_TEMP" , +"Agent0 Dimm0" , +"Agent0 Dimm1" , +"Agent1 Dimm0" , +"Agent1 Dimm1" , +"BYTE_TEMP0" , +"BYTE_TEMP1" , +"PECI Agent 0 Calibration" ,/* undocumented */ +"PECI Agent 1 Calibration" ,/* undocumented */ +"" , +"Virtual_TEMP" +}; + +#define NCT6799_TEMP_MASK 0xbfff2ffe +#define NCT6799_VIRT_TEMP_MASK 0x80000c00 +/* NCT6102D/NCT6106D specific data */ #define NCT6106_REG_VBAT 0x318
@@ -1109,6 +1149,7 @@ bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg)case nct6796:case nct6797:case nct6798: +case nct6799:return reg ==0x150 || reg ==0x153 || reg ==0x155 || (reg &0xfff0 ) ==0x4c0 || reg == 0x402 ||
@@ -1462,6 +1503,7 @@ static int nct6775_update_pwm_limits(struct device *dev)case nct6796:case nct6797:case nct6798: +case nct6799: err =nct6775_read_value (data, data->REG_CRITICAL_PWM_ENABLE[i], ®);if (err)return err;
@@ -3119,6 +3161,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,case nct6796:case nct6797:case nct6798: +case nct6799: err =nct6775_write_value (data, data->REG_CRITICAL_PWM[nr], val);if (err)break ;
@@ -3817,10 +3860,12 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,case nct6796:case nct6797:case nct6798: +case nct6799: data->in_num =15 ; data->pwm_num = (data->kind == nct6796 || data->kind == nct6797 || - data->kind == nct6798) ?7 :6 ; + data->kind == nct6798 || + data->kind == nct6799) ?7 :6 ; data->auto_pwm_num =4 ; data->has_fan_div = false; data->temp_fixed_num =6 ;
@@ -3869,6 +3914,11 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, data->temp_mask =NCT6798_TEMP_MASK ; data->virt_temp_mask =NCT6798_VIRT_TEMP_MASK ;break ; +case nct6799: + data->temp_label = nct6799_temp_label; + data->temp_mask =NCT6799_TEMP_MASK ; + data->virt_temp_mask =NCT6799_VIRT_TEMP_MASK ; +break ; } data->REG_CONFIG =NCT6775_REG_CONFIG ;
@@ -3928,6 +3978,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,case nct6796:case nct6797:case nct6798: +case nct6799: data->REG_TSI_TEMP =NCT6796_REG_TSI_TEMP ; num_reg_tsi_temp =ARRAY_SIZE (NCT6796_REG_TSI_TEMP );break ;
drivers/hwmon/nct6775-i2c.c +2 -0
@@ -87,6 +87,7 @@ static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = { { .compatible = "nuvoton,nct6796", .data = (void *)nct6796, }, { .compatible = "nuvoton,nct6797", .data = (void *)nct6797, }, { .compatible = "nuvoton,nct6798", .data = (void *)nct6798, }, + { .compatible = "nuvoton,nct6799", .data = (void *)nct6799, }, { }, }; MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match);
@@ -104,6 +105,7 @@ static const struct i2c_device_id nct6775_i2c_id[] = { { "nct6796", nct6796 }, { "nct6797", nct6797 }, { "nct6798", nct6798 }, + { "nct6799", nct6799 }, { } };MODULE_DEVICE_TABLE (i2c, nct6775_i2c_id);
drivers/hwmon/nct6775-platform.c +38 -3
107 lines not on this page · raw message
drivers/hwmon/nct6775.h +1 -1
@@ -5,7 +5,7 @@#include <linux/types.h> enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792, - nct6793, nct6795, nct6796, nct6797, nct6798 }; + nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 };enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };#define NUM_TEMP 10/* Max number of temp attribute sets w/ limits*/
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 342/609 hwmon: (nct6775) Fix IN scaling factors for 6798/6799
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ahmad Khalifa <[email protected]>
[ Upstream commit 13558a2e6341d1ba6dff9f8e2febf97877067885 ]
Scaling for VTT/VIN5/VIN6 registers were based on prior chips
* Split scaling factors for 6798/6799 and assign at probe()
* Pass them through driver data to sysfs functions
Tested on nct6799 with old/new input/min/max
Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
--- drivers/hwmon/nct6775-core.c | 28 ++++++++++++++++++++++------ drivers/hwmon/nct6775.h | 1 + 2 files changed, 23 insertions(+), 6 deletions(-)
drivers/hwmon/nct6775-core.c +22 -6
@@ -955,14 +955,25 @@ static const u16 scale_in[15] = { 800, 800 }; -static inline long in_from_reg (u8 reg,u8 nr) +/* +* NCT6798 scaling: +* CPUVC, IN1, AVSB, 3VCC, IN0, IN8, IN4, 3VSB, VBAT, VTT, IN5, IN6, IN2, +* IN3, IN7 +* Additional scales to be added later: IN9 (800), VHIF (1600) +*/ +static const u16 scale_in_6798[15 ] = { +800 ,800 ,1600 ,1600 ,800 ,800 ,800 ,1600 ,1600 ,1600 ,1600 ,1600 ,800 , +800 ,800 +}; + +static inline long in_from_reg (u8 reg,u8 nr,const u16 *scales) { -return DIV_ROUND_CLOSEST (reg * scale_in[nr],100 ); +return DIV_ROUND_CLOSEST (reg * scales[nr],100 ); } -static inline u8 in_to_reg (u32 val,u8 nr) +static inline u8 in_to_reg (u32 val,u8 nr,const u16 *scales) { -return clamp_val (DIV_ROUND_CLOSEST (val *100 , scale_in[nr]),0 ,255 ); +return clamp_val (DIV_ROUND_CLOSEST (val *100 , scales[nr]),0 ,255 ); }/* TSI temperatures are in 8.3 format */
@@ -1677,7 +1688,8 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)if (IS_ERR (data))return PTR_ERR (data); -return sprintf (buf,"%ld\n" ,in_from_reg (data->in[nr][index], nr)); +return sprintf (buf,"%ld\n" , +in_from_reg (data->in[nr][index], nr, data->scale_in)); } static ssize_t
@@ -1695,7 +1707,7 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf,if (err <0 )return err;mutex_lock (&data->update_lock); - data->in[nr][index] =in_to_reg (val, nr); + data->in[nr][index] =in_to_reg (val, nr, data->scale_in); err =nct6775_write_value (data, data->REG_IN_MINMAX[index -1 ][nr], data->in[nr][index]);mutex_unlock (&data->update_lock);return err ? : count;
@@ -3472,6 +3484,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,mutex_init (&data->update_lock); data->name = nct6775_device_names[data->kind]; data->bank =0xff ;/* Force initial bank selection */ + data->scale_in = scale_in;switch (data->kind) {case nct6106:
@@ -3987,6 +4000,9 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,break ; } +if (data->kind == nct6798 || data->kind == nct6799) + data->scale_in = scale_in_6798; + reg_temp =NCT6779_REG_TEMP ; num_reg_temp =ARRAY_SIZE (NCT6779_REG_TEMP );if (data->kind == nct6791) {
drivers/hwmon/nct6775.h +1 -0
@@ -98,6 +98,7 @@ struct nct6775_data {u8 bank;/* current register bank */ u8 in_num;/* number of in inputs we have */ u8 in[15 ][3 ];/* [0]=in, [1]=in_max, [2]=in_min */ +const u16 *scale_in;/* internal scaling factors */ unsigned int rpm[NUM_FAN ];u16 fan_min[NUM_FAN ];u8 fan_pulses[NUM_FAN ];
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 343/609 hwmon: (nct6775) Increase and reorder ALARM/BEEP bits
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ahmad Khalifa <[email protected]>
[ Upstream commit 3b7f4bde06daaff391a374fc27c8163b2847de34 ]
* Increase available bits, IN: 16 to 24, FAN: 8 to 12,
TEMP: 6 to 12
* Reorder alarm/beep definitions to match in order to allow additional inputs in the future
* Remove comments about 'unused' bits as probe() is a better reference
Testing note:
* Tested on nct6799 with IN/FAN/TEMP, and changing min/max/high/hyst, that triggers the corresponding alarms correctly. Good confirmation on the original mapping of the registers and masks.
As to be expected, only 4 fans and 2 temps (fixed) have limits currently on nct6799 on my board.
* Trouble with testing intrusion alarms and beeps, no way to confirm those. As I understand now, intrusion/caseopen is probably not connected on my board.
And I haven't seen a buzzer on a board in ages.
Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
--- drivers/hwmon/nct6775-core.c | 169 +++++++++++++++-------------------- drivers/hwmon/nct6775.h | 23 ++++- 2 files changed, 93 insertions(+), 99 deletions(-)
drivers/hwmon/nct6775-core.c +74 -95
@@ -97,31 +97,23 @@ static const u16 NCT6775_REG_IN[] = {static const u16 NCT6775_REG_ALARM [NUM_REG_ALARM ] = {0x459 ,0x45A ,0x45B }; -/* 0..15 voltages, 16..23 fans, 24..29 temperatures, 30..31 intrusion */ - -static const s8 NCT6775_ALARM_BITS [] = { -0 ,1 ,2 ,3 ,8 ,21 ,20 ,16 ,/* in0.. in7 */ -17 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in8..in14 */ --1 ,/* unused */ -6 ,7 ,11 ,-1 ,-1 ,/* fan1..fan5 */ --1 ,-1 ,-1 ,/* unused */ -4 ,5 ,13 ,-1 ,-1 ,-1 ,/* temp1..temp6 */ -12 ,-1 };/* intrusion0, intrusion1 */ +static const s8 NCT6775_ALARM_BITS [NUM_ALARM_BITS ] = { +0 ,1 ,2 ,3 ,8 ,21 ,20 ,16 ,17 ,-1 ,-1 ,-1 ,/* in0-in11 */ +-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +6 ,7 ,11 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +4 ,5 ,13 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +12 ,-1 ,/* intr0-intr1 */ +};static const u16 NCT6775_REG_BEEP [NUM_REG_BEEP ] = {0x56 ,0x57 ,0x453 ,0x4e }; -/* -* 0..14 voltages, 15 global beep enable, 16..23 fans, 24..29 temperatures, -* 30..31 intrusion -*/ -static const s8 NCT6775_BEEP_BITS [] = { -0 ,1 ,2 ,3 ,8 ,9 ,10 ,16 ,/* in0.. in7 */ -17 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in8..in14 */ -21 ,/* global beep enable */ -6 ,7 ,11 ,28 ,-1 ,/* fan1..fan5 */ --1 ,-1 ,-1 ,/* unused */ -4 ,5 ,13 ,-1 ,-1 ,-1 ,/* temp1..temp6 */ -12 ,-1 };/* intrusion0, intrusion1 */ +static const s8 NCT6775_BEEP_BITS [NUM_BEEP_BITS ] = { +0 ,1 ,2 ,3 ,8 ,9 ,10 ,16 ,17 ,-1 ,-1 ,-1 ,/* in0-in11 */ +-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +6 ,7 ,11 ,28 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +4 ,5 ,13 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +12 ,-1 ,21 /* intr0-intr1, beep_en */ +};/* DC or PWM output fan configuration */ static const u8 NCT6775_REG_PWM_MODE [] = {0x04 ,0x04 ,0x12 };
@@ -255,25 +247,23 @@ static const u16 NCT6775_REG_TSI_TEMP[] = { 0x669 };#define NCT6776_REG_FAN_STEP_UP_TIME NCT6775_REG_FAN_STEP_DOWN_TIME#define NCT6776_REG_FAN_STEP_DOWN_TIME NCT6775_REG_FAN_STEP_UP_TIME -static const s8 NCT6776_ALARM_BITS [] = { -0 ,1 ,2 ,3 ,8 ,21 ,20 ,16 ,/* in0.. in7 */ -17 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in8..in14 */ --1 ,/* unused */ -6 ,7 ,11 ,10 ,23 ,/* fan1..fan5 */ --1 ,-1 ,-1 ,/* unused */ -4 ,5 ,13 ,-1 ,-1 ,-1 ,/* temp1..temp6 */ -12 ,9 };/* intrusion0, intrusion1 */ +static const s8 NCT6776_ALARM_BITS [NUM_ALARM_BITS ] = { +0 ,1 ,2 ,3 ,8 ,21 ,20 ,16 ,17 ,-1 ,-1 ,-1 ,/* in0-in11 */ +-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +6 ,7 ,11 ,10 ,23 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +4 ,5 ,13 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +12 ,9 ,/* intr0-intr1 */ +};static const u16 NCT6776_REG_BEEP [NUM_REG_BEEP ] = {0xb2 ,0xb3 ,0xb4 ,0xb5 }; -static const s8 NCT6776_BEEP_BITS [] = { -0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,/* in0.. in7 */ -8 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in8..in14 */ -24 ,/* global beep enable */ -25 ,26 ,27 ,28 ,29 ,/* fan1..fan5 */ --1 ,-1 ,-1 ,/* unused */ -16 ,17 ,18 ,19 ,20 ,21 ,/* temp1..temp6 */ -30 ,31 };/* intrusion0, intrusion1 */ +static const s8 NCT6776_BEEP_BITS [NUM_BEEP_BITS ] = { +0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,-1 ,-1 ,-1 ,/* in0-in11 */ +-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +25 ,26 ,27 ,28 ,29 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +16 ,17 ,18 ,19 ,20 ,21 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +30 ,31 ,24 /* intr0-intr1, beep_en */ +};static const u16 NCT6776_REG_TOLERANCE_H [] = {0x10c ,0x20c ,0x30c ,0x80c ,0x90c ,0xa0c ,0xb0c };
@@ -344,23 +334,21 @@ static const u16 NCT6779_REG_IN[] = {static const u16 NCT6779_REG_ALARM [NUM_REG_ALARM ] = {0x459 ,0x45A ,0x45B ,0x568 }; -static const s8 NCT6779_ALARM_BITS [] = { -0 ,1 ,2 ,3 ,8 ,21 ,20 ,16 ,/* in0.. in7 */ -17 ,24 ,25 ,26 ,27 ,28 ,29 ,/* in8..in14 */ --1 ,/* unused */ -6 ,7 ,11 ,10 ,23 ,/* fan1..fan5 */ --1 ,-1 ,-1 ,/* unused */ -4 ,5 ,13 ,-1 ,-1 ,-1 ,/* temp1..temp6 */ -12 ,9 };/* intrusion0, intrusion1 */ - -static const s8 NCT6779_BEEP_BITS [] = { -0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,/* in0.. in7 */ -8 ,9 ,10 ,11 ,12 ,13 ,14 ,/* in8..in14 */ -24 ,/* global beep enable */ -25 ,26 ,27 ,28 ,29 ,/* fan1..fan5 */ --1 ,-1 ,-1 ,/* unused */ -16 ,17 ,-1 ,-1 ,-1 ,-1 ,/* temp1..temp6 */ -30 ,31 };/* intrusion0, intrusion1 */ +static const s8 NCT6779_ALARM_BITS [NUM_ALARM_BITS ] = { +0 ,1 ,2 ,3 ,8 ,21 ,20 ,16 ,17 ,24 ,25 ,26 ,/* in0-in11 */ +27 ,28 ,29 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +6 ,7 ,11 ,10 ,23 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +4 ,5 ,13 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +12 ,9 ,/* intr0-intr1 */ +}; + +static const s8 NCT6779_BEEP_BITS [NUM_BEEP_BITS ] = { +0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,/* in0-in11 */ +12 ,13 ,14 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +25 ,26 ,27 ,28 ,29 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +16 ,17 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +30 ,31 ,24 /* intr0-intr1, beep_en */ +};static const u16 NCT6779_REG_FAN [] = {0x4c0 ,0x4c2 ,0x4c4 ,0x4c6 ,0x4c8 ,0x4ca ,0x4ce };
@@ -448,14 +436,13 @@ static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[NUM_FAN] = { 0, 0x23e };static const u16 NCT6791_REG_ALARM [NUM_REG_ALARM ] = {0x459 ,0x45A ,0x45B ,0x568 ,0x45D }; -static const s8 NCT6791_ALARM_BITS [] = { -0 ,1 ,2 ,3 ,8 ,21 ,20 ,16 ,/* in0.. in7 */ -17 ,24 ,25 ,26 ,27 ,28 ,29 ,/* in8..in14 */ --1 ,/* unused */ -6 ,7 ,11 ,10 ,23 ,33 ,/* fan1..fan6 */ --1 ,-1 ,/* unused */ -4 ,5 ,13 ,-1 ,-1 ,-1 ,/* temp1..temp6 */ -12 ,9 };/* intrusion0, intrusion1 */ +static const s8 NCT6791_ALARM_BITS [NUM_ALARM_BITS ] = { +0 ,1 ,2 ,3 ,8 ,21 ,20 ,16 ,17 ,24 ,25 ,26 ,/* in0-in11 */ +27 ,28 ,29 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +6 ,7 ,11 ,10 ,23 ,33 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +4 ,5 ,13 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +12 ,9 ,/* intr0-intr1 */ +};/* NCT6792/NCT6793 specific data */
@@ -763,27 +750,23 @@ static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };static const u16 NCT6106_REG_ALARM [NUM_REG_ALARM ] = {0x77 ,0x78 ,0x79 ,0x7a ,0x7b ,0x7c ,0x7d }; -static const s8 NCT6106_ALARM_BITS [] = { -0 ,1 ,2 ,3 ,4 ,5 ,7 ,8 ,/* in0.. in7 */ -9 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in8..in14 */ --1 ,/* unused */ -32 ,33 ,34 ,-1 ,-1 ,/* fan1..fan5 */ --1 ,-1 ,-1 ,/* unused */ -16 ,17 ,18 ,19 ,20 ,21 ,/* temp1..temp6 */ -48 ,-1 /* intrusion0, intrusion1 */ +static const s8 NCT6106_ALARM_BITS [NUM_ALARM_BITS ] = { +0 ,1 ,2 ,3 ,4 ,5 ,7 ,8 ,9 ,-1 ,-1 ,-1 ,/* in0-in11 */ +-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +32 ,33 ,34 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +16 ,17 ,18 ,19 ,20 ,21 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +48 ,-1 ,/* intr0-intr1 */ };static const u16 NCT6106_REG_BEEP [NUM_REG_BEEP ] = {0x3c0 ,0x3c1 ,0x3c2 ,0x3c3 ,0x3c4 }; -static const s8 NCT6106_BEEP_BITS [] = { -0 ,1 ,2 ,3 ,4 ,5 ,7 ,8 ,/* in0.. in7 */ -9 ,10 ,11 ,12 ,-1 ,-1 ,-1 ,/* in8..in14 */ -32 ,/* global beep enable */ -24 ,25 ,26 ,27 ,28 ,/* fan1..fan5 */ --1 ,-1 ,-1 ,/* unused */ -16 ,17 ,18 ,19 ,20 ,21 ,/* temp1..temp6 */ -34 ,-1 /* intrusion0, intrusion1 */ +static const s8 NCT6106_BEEP_BITS [NUM_BEEP_BITS ] = { +0 ,1 ,2 ,3 ,4 ,5 ,7 ,8 ,9 ,10 ,11 ,12 ,/* in0-in11 */ +-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +24 ,25 ,26 ,27 ,28 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +16 ,17 ,18 ,19 ,20 ,21 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +34 ,-1 ,32 /* intr0-intr1, beep_en */ };static const u16 NCT6106_REG_TEMP_ALTERNATE [32 ] = {
@@ -843,24 +826,20 @@ static const u16 NCT6116_REG_AUTO_TEMP[] = {static const u16 NCT6116_REG_AUTO_PWM [] = {0x164 ,0x174 ,0x184 ,0x1d4 ,0x1e4 }; -static const s8 NCT6116_ALARM_BITS [] = { -0 ,1 ,2 ,3 ,4 ,5 ,7 ,8 ,/* in0.. in7 */ -9 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in8..in9 */ --1 ,/* unused */ -32 ,33 ,34 ,35 ,36 ,/* fan1..fan5 */ --1 ,-1 ,-1 ,/* unused */ -16 ,17 ,18 ,-1 ,-1 ,-1 ,/* temp1..temp6 */ -48 ,-1 /* intrusion0, intrusion1 */ +static const s8 NCT6116_ALARM_BITS [NUM_ALARM_BITS ] = { +0 ,1 ,2 ,3 ,4 ,5 ,7 ,8 ,9 ,-1 ,-1 ,-1 ,/* in0-in11 */ +-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +32 ,33 ,34 ,35 ,36 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +16 ,17 ,18 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +48 ,-1 ,/* intr0-intr1 */ }; -static const s8 NCT6116_BEEP_BITS [] = { -0 ,1 ,2 ,3 ,4 ,5 ,7 ,8 ,/* in0.. in7 */ -9 ,10 ,11 ,12 ,-1 ,-1 ,-1 ,/* in8..in14 */ -32 ,/* global beep enable */ -24 ,25 ,26 ,27 ,28 ,/* fan1..fan5 */ --1 ,-1 ,-1 ,/* unused */ -16 ,17 ,18 ,-1 ,-1 ,-1 ,/* temp1..temp6 */ -34 ,-1 /* intrusion0, intrusion1 */ +static const s8 NCT6116_BEEP_BITS [NUM_BEEP_BITS ] = { +0 ,1 ,2 ,3 ,4 ,5 ,7 ,8 ,9 ,10 ,11 ,12 ,/* in0-in11 */ +-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +24 ,25 ,26 ,27 ,28 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +16 ,17 ,18 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +34 ,-1 ,32 /* intr0-intr1, beep_en */ };static const u16 NCT6116_REG_TSI_TEMP [] = {0x59 ,0x5b };
drivers/hwmon/nct6775.h +19 -4
@@ -239,10 +239,25 @@ nct6775_add_attr_group(struct nct6775_data *data, const struct attribute_group *#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28 -#define FAN_ALARM_BASE 16 -#define TEMP_ALARM_BASE 24 -#define INTRUSION_ALARM_BASE 30 -#define BEEP_ENABLE_BASE 15 +/* +* ALARM_BITS and BEEP_BITS store bit-index for the mask of the registers +* loaded into data->alarm and data->beep. +* +* Every input register (IN/TEMP/FAN) must have a corresponding +* ALARM/BEEP bit at the same index BITS[BASE + index] +* Set value to -1 to disable the visibility of that '*_alarm' attribute and +* to pad the bits until the next BASE +* +* Beep has an additional GLOBAL_BEEP_ENABLE bit +*/ +#define VIN_ALARM_BASE 0 +#define FAN_ALARM_BASE 24 +#define TEMP_ALARM_BASE 36 +#define INTRUSION_ALARM_BASE 48 +#define BEEP_ENABLE_BASE 50 + +#define NUM_ALARM_BITS (INTRUSION_ALARM_BASE + 4) +#define NUM_BEEP_BITS (BEEP_ENABLE_BASE + 1)/* * Not currently used:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 344/609 hwmon: (nct6775) Add support for 18 IN readings for nct6799
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ahmad Khalifa <[email protected]>
[ Upstream commit 4f65c15cf70eb22c074889af60b9d2bcffbb375a ]
* Add additional VIN/IN_MIN/IN_MAX register values
* Separate ALARM/BEEP bits for nct6799
* Update scaling factors for nct6799
Registers/alarms match for NCT6796D-S and NCT6799D-R Tested on NCT6799D-R for new IN/MIN/MAX and ALARMS
Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
--- drivers/hwmon/nct6775-core.c | 51 +++++++++++++++++++++++++++++------- drivers/hwmon/nct6775.h | 5 ++-- 2 files changed, 45 insertions(+), 11 deletions(-)
drivers/hwmon/nct6775-core.c +42 -9
@@ -79,14 +79,17 @@ static const char * const nct6775_device_names[] = {/* Common and NCT6775 specific data */ -/* Voltage min/max registers for nr=7..14 are in bank 5 */ +/* +* Voltage min/max registers for nr=7..14 are in bank 5 +* min/max: 15-17 for NCT6799 only +*/ static const u16 NCT6775_REG_IN_MAX [] = {0x2b ,0x2d ,0x2f ,0x31 ,0x33 ,0x35 ,0x37 ,0x554 ,0x556 ,0x558 ,0x55a , -0x55c ,0x55e ,0x560 ,0x562 }; +0x55c ,0x55e ,0x560 ,0x562 ,0x564 ,0x570 ,0x572 };static const u16 NCT6775_REG_IN_MIN [] = {0x2c ,0x2e ,0x30 ,0x32 ,0x34 ,0x36 ,0x38 ,0x555 ,0x557 ,0x559 ,0x55b , -0x55d ,0x55f ,0x561 ,0x563 }; +0x55d ,0x55f ,0x561 ,0x563 ,0x565 ,0x571 ,0x573 };static const u16 NCT6775_REG_IN [] = {0x20 ,0x21 ,0x22 ,0x23 ,0x24 ,0x25 ,0x26 ,0x550 ,0x551 ,0x552 };
@@ -255,7 +258,8 @@ static const s8 NCT6776_ALARM_BITS[NUM_ALARM_BITS] = { 12, 9, /* intr0-intr1 */ }; -static const u16 NCT6776_REG_BEEP [NUM_REG_BEEP ] = {0xb2 ,0xb3 ,0xb4 ,0xb5 }; +/* 0xbf: nct6799 only */ +static const u16 NCT6776_REG_BEEP [NUM_REG_BEEP ] = {0xb2 ,0xb3 ,0xb4 ,0xb5 ,0xbf };static const s8 NCT6776_BEEP_BITS [NUM_BEEP_BITS ] = {0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,-1 ,-1 ,-1 ,/* in0-in11 */
@@ -327,9 +331,16 @@ static const u16 NCT6776_REG_TSI_TEMP[] = {/* NCT6779 specific data */ +/* +* 15-17 for NCT6799 only, register labels are: +* CPUVC, VIN1, AVSB, 3VCC, VIN0, VIN8, VIN4, 3VSB +* VBAT, VTT, VIN5, VIN6, VIN2, VIN3, VIN7, VIN9 +* VHIF, VIN10 +*/ static const u16 NCT6779_REG_IN [] = {0x480 ,0x481 ,0x482 ,0x483 ,0x484 ,0x485 ,0x486 ,0x487 , -0x488 ,0x489 ,0x48a ,0x48b ,0x48c ,0x48d ,0x48e }; +0x488 ,0x489 ,0x48a ,0x48b ,0x48c ,0x48d ,0x48e ,0x48f , +0x470 ,0x471 };static const u16 NCT6779_REG_ALARM [NUM_REG_ALARM ] = {0x459 ,0x45A ,0x45B ,0x568 };
@@ -643,6 +654,22 @@ static const char *const nct6798_temp_label[] = {#define NCT6798_TEMP_MASK 0xbfff0ffe#define NCT6798_VIRT_TEMP_MASK 0x80000c00 +static const s8 NCT6799_ALARM_BITS [NUM_ALARM_BITS ] = { +0 ,1 ,2 ,3 ,8 ,-1 ,20 ,16 ,17 ,24 ,25 ,26 ,/* in0-in11 */ +27 ,28 ,29 ,30 ,31 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +6 ,7 ,11 ,10 ,23 ,33 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +4 ,5 ,13 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +12 ,9 ,/* intr0-intr1 */ +}; + +static const s8 NCT6799_BEEP_BITS [NUM_BEEP_BITS ] = { +0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,/* in0-in11 */ +12 ,13 ,14 ,15 ,34 ,35 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ +25 ,26 ,27 ,28 ,29 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ +16 ,17 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +30 ,31 ,24 /* intr0-intr1, beep_en */ +}; +static const char *const nct6799_temp_label[] = {"" ,"SYSTIN" ,
@@ -937,12 +964,12 @@ static const u16 scale_in[15] = {/* * NCT6798 scaling: * CPUVC, IN1, AVSB, 3VCC, IN0, IN8, IN4, 3VSB, VBAT, VTT, IN5, IN6, IN2, -* IN3, IN7 -* Additional scales to be added later: IN9 (800), VHIF (1600) +* IN3, IN7, IN9, VHIF, IN10 +* 15-17 for NCT6799 only */ -static const u16 scale_in_6798[15 ] = { +static const u16 scale_in_6798[NUM_IN ] = {800 ,800 ,1600 ,1600 ,800 ,800 ,800 ,1600 ,1600 ,1600 ,1600 ,1600 ,800 , -800 ,800 +800 ,800 ,800 ,1600 ,800 };static inline long in_from_reg (u8 reg,u8 nr,const u16 *scales)
@@ -3970,7 +3997,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,case nct6796:case nct6797:case nct6798: + data->REG_TSI_TEMP =NCT6796_REG_TSI_TEMP ; + num_reg_tsi_temp =ARRAY_SIZE (NCT6796_REG_TSI_TEMP ); +break ;case nct6799: + data->in_num =18 ; + data->ALARM_BITS =NCT6799_ALARM_BITS ; + data->BEEP_BITS =NCT6799_BEEP_BITS ; data->REG_TSI_TEMP =NCT6796_REG_TSI_TEMP ; num_reg_tsi_temp =ARRAY_SIZE (NCT6796_REG_TSI_TEMP );break ;
drivers/hwmon/nct6775.h +3 -2
@@ -16,6 +16,7 @@ enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };#define NUM_REG_BEEP 5/* Max number of beep registers */ #define NUM_FAN 7 +#define NUM_IN 18struct nct6775_data {int addr;/* IO base of hw monitor block */
@@ -97,7 +98,7 @@ struct nct6775_data {/* Register values */ u8 bank;/* current register bank */ u8 in_num;/* number of in inputs we have */ -u8 in[15 ][3 ];/* [0]=in, [1]=in_max, [2]=in_min */ +u8 in[NUM_IN ][3 ];/* [0]=in, [1]=in_max, [2]=in_min */ const u16 *scale_in;/* internal scaling factors */ unsigned int rpm[NUM_FAN ];u16 fan_min[NUM_FAN ];
@@ -166,7 +167,7 @@ struct nct6775_data {u16 have_temp;u16 have_temp_fixed;u16 have_tsi_temp; -u16 have_in; +u32 have_in;/* Remember extra register values over suspend/resume */ u8 vbat;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 345/609 hwmon: (nct6775) Additional TEMP registers for nct6799
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ahmad Khalifa <[email protected]>
[ Upstream commit b7f1f7b2523a6a4382f12fe953380b847b80e09d ]
Additional TEMP registers for nct6798d, nct6799d-r and nct6796d-s This allows the max/max_hyst/crit attributes to be shown/stored
* Increase NUM_TEMP from 10 to 12
* Separate TEMP/MON_TEMP/OVER/HYST/CRIT registers
* Rename "PECI Calibration" to include "TSI" too
* Update ALARM/BEEP bits for temps for 6799
* For 6799, keep temp_fixed_num at 6, but increase num_temp_alarms/num_temp_beeps to 7/8
Tested with NCT6799D-R showing additional sysfs attributes:
* temp3-temp8: max/max_hyst/beep/alarm
* temp3-temp6: crit/offset
Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
--- drivers/hwmon/nct6775-core.c | 150 ++++++++++++++++++++++++++++------- drivers/hwmon/nct6775.h | 2 +- 2 files changed, 121 insertions(+), 31 deletions(-)
drivers/hwmon/nct6775-core.c +120 -30
@@ -616,6 +616,28 @@ static const char *const nct6796_temp_label[] = {static const u16 NCT6796_REG_TSI_TEMP [] = {0x409 ,0x40b }; +static const u16 NCT6798_REG_TEMP [] = { +0x27 ,0x150 ,0x670 ,0x672 ,0x674 ,0x676 ,0x678 ,0x67a }; + +static const u16 NCT6798_REG_TEMP_SOURCE [] = { +0x621 ,0x622 ,0xc26 ,0xc27 ,0xc28 ,0xc29 ,0xc2a ,0xc2b }; + +static const u16 NCT6798_REG_TEMP_MON [] = { +0x73 ,0x75 ,0x77 ,0x79 ,0x7b ,0x7d ,0x4a0 }; +static const u16 NCT6798_REG_TEMP_OVER [] = { +0x39 ,0x155 ,0xc1a ,0xc1b ,0xc1c ,0xc1d ,0xc1e ,0xc1f }; +static const u16 NCT6798_REG_TEMP_HYST [] = { +0x3a ,0x153 ,0xc20 ,0xc21 ,0xc22 ,0xc23 ,0xc24 ,0xc25 }; + +static const u16 NCT6798_REG_TEMP_CRIT [32 ] = { +0x135 ,0x235 ,0x335 ,0x835 ,0x935 ,0xa35 ,0xb35 ,0 }; + +static const u16 NCT6798_REG_TEMP_ALTERNATE [32 ] = { +0x490 ,0x491 ,0x492 ,0x493 ,0x494 ,0x495 ,0x496 ,0 , +0 ,0 ,0 ,0 ,0x4a2 ,0 ,0 ,0 , +0 ,0x400 ,0x401 ,0x402 ,0x404 ,0x405 ,0x406 ,0x407 , +0x408 ,0x419 ,0x41a ,0x4f4 ,0x4f5 }; +static const char *const nct6798_temp_label[] = {"" ,"SYSTIN" ,
@@ -654,11 +676,14 @@ static const char *const nct6798_temp_label[] = {#define NCT6798_TEMP_MASK 0xbfff0ffe#define NCT6798_VIRT_TEMP_MASK 0x80000c00 +static const u16 NCT6799_REG_ALARM [NUM_REG_ALARM ] = { +0x459 ,0x45A ,0x45B ,0x568 ,0x45D ,0xc01 }; +static const s8 NCT6799_ALARM_BITS [NUM_ALARM_BITS ] = {0 ,1 ,2 ,3 ,8 ,-1 ,20 ,16 ,17 ,24 ,25 ,26 ,/* in0-in11 */ 27 ,28 ,29 ,30 ,31 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ 6 ,7 ,11 ,10 ,23 ,33 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ -4 ,5 ,13 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +4 ,5 ,40 ,41 ,42 ,43 ,44 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ 12 ,9 ,/* intr0-intr1 */ };
@@ -666,10 +691,11 @@ static const s8 NCT6799_BEEP_BITS[NUM_BEEP_BITS] = {0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,/* in0-in11 */ 12 ,13 ,14 ,15 ,34 ,35 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* in12-in23 */ 25 ,26 ,27 ,28 ,29 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* fan1-fan12 */ -16 ,17 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ +16 ,17 ,18 ,19 ,20 ,21 ,22 ,23 ,-1 ,-1 ,-1 ,-1 ,/* temp1-temp12 */ 30 ,31 ,24 /* intr0-intr1, beep_en */ }; +/* PECI Calibration only for NCT6799D, not NCT6796D-S */ static const char *const nct6799_temp_label[] = {"" , "SYSTIN",
@@ -699,8 +725,8 @@ static const char *const nct6799_temp_label[] = {"Agent1 Dimm1" ,"BYTE_TEMP0" ,"BYTE_TEMP1" , -"PECI Agent 0 Calibration" ,/* undocumented */ -"PECI Agent 1 Calibration" ,/* undocumented */ +"PECI/TSI Agent 0 Calibration" , +"PECI/TSI Agent 1 Calibration" ,"" , "Virtual_TEMP" };
@@ -3878,13 +3904,9 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,case nct6795:case nct6796:case nct6797: -case nct6798: -case nct6799: data->in_num =15 ; data->pwm_num = (data->kind == nct6796 || - data->kind == nct6797 || - data->kind == nct6798 || - data->kind == nct6799) ?7 :6 ; + data->kind == nct6797) ?7 :6 ; data->auto_pwm_num =4 ; data->has_fan_div = false; data->temp_fixed_num =6 ;
@@ -3928,16 +3950,6 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, data->temp_mask =NCT6796_TEMP_MASK ; data->virt_temp_mask =NCT6796_VIRT_TEMP_MASK ;break ; -case nct6798: - data->temp_label = nct6798_temp_label; - data->temp_mask =NCT6798_TEMP_MASK ; - data->virt_temp_mask =NCT6798_VIRT_TEMP_MASK ; -break ; -case nct6799: - data->temp_label = nct6799_temp_label; - data->temp_mask =NCT6799_TEMP_MASK ; - data->virt_temp_mask =NCT6799_VIRT_TEMP_MASK ; -break ; } data->REG_CONFIG =NCT6775_REG_CONFIG ;
@@ -3996,14 +4008,6 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,case nct6795:case nct6796:case nct6797: -case nct6798: - data->REG_TSI_TEMP =NCT6796_REG_TSI_TEMP ; - num_reg_tsi_temp =ARRAY_SIZE (NCT6796_REG_TSI_TEMP ); -break ; -case nct6799: - data->in_num =18 ; - data->ALARM_BITS =NCT6799_ALARM_BITS ; - data->BEEP_BITS =NCT6799_BEEP_BITS ; data->REG_TSI_TEMP =NCT6796_REG_TSI_TEMP ; num_reg_tsi_temp =ARRAY_SIZE (NCT6796_REG_TSI_TEMP );break ;
@@ -4012,9 +4016,6 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,break ; } -if (data->kind == nct6798 || data->kind == nct6799) - data->scale_in = scale_in_6798; - reg_temp =NCT6779_REG_TEMP ; num_reg_temp =ARRAY_SIZE (NCT6779_REG_TEMP );if (data->kind == nct6791) {
@@ -4030,6 +4031,95 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp_alternate =NCT6779_REG_TEMP_ALTERNATE ; reg_temp_crit =NCT6779_REG_TEMP_CRIT ; +break ; +case nct6798: +case nct6799: + data->in_num = data->kind == nct6799 ?18 :15 ; + data->scale_in = scale_in_6798; + data->pwm_num =7 ; + data->auto_pwm_num =4 ; + data->has_fan_div = false; + data->temp_fixed_num =6 ; + data->num_temp_alarms =7 ; + data->num_temp_beeps =8 ; + + data->ALARM_BITS =NCT6799_ALARM_BITS ; + data->BEEP_BITS =NCT6799_BEEP_BITS ; + + data->fan_from_reg = fan_from_reg_rpm; + data->fan_from_reg_min = fan_from_reg13; + data->target_temp_mask =0xff ; + data->tolerance_mask =0x07 ; + data->speed_tolerance_limit =63 ; + +switch (data->kind) { +default : +case nct6798: + data->temp_label = nct6798_temp_label; + data->temp_mask =NCT6798_TEMP_MASK ; + data->virt_temp_mask =NCT6798_VIRT_TEMP_MASK ; +break ; +case nct6799: + data->temp_label = nct6799_temp_label; + data->temp_mask =NCT6799_TEMP_MASK ; + data->virt_temp_mask =NCT6799_VIRT_TEMP_MASK ; +break ; + } + + data->REG_CONFIG =NCT6775_REG_CONFIG ; + data->REG_VBAT =NCT6775_REG_VBAT ; + data->REG_DIODE =NCT6775_REG_DIODE ; + data->DIODE_MASK =NCT6775_DIODE_MASK ; + data->REG_VIN =NCT6779_REG_IN ; + data->REG_IN_MINMAX[0 ] =NCT6775_REG_IN_MIN ; + data->REG_IN_MINMAX[1 ] =NCT6775_REG_IN_MAX ; + data->REG_TARGET =NCT6775_REG_TARGET ; + data->REG_FAN =NCT6779_REG_FAN ; + data->REG_FAN_MODE =NCT6775_REG_FAN_MODE ; + data->REG_FAN_MIN =NCT6776_REG_FAN_MIN ; + data->REG_FAN_PULSES =NCT6779_REG_FAN_PULSES ; + data->FAN_PULSE_SHIFT =NCT6775_FAN_PULSE_SHIFT ; + data->REG_FAN_TIME[0 ] =NCT6775_REG_FAN_STOP_TIME ; + data->REG_FAN_TIME[1 ] =NCT6776_REG_FAN_STEP_UP_TIME ; + data->REG_FAN_TIME[2 ] =NCT6776_REG_FAN_STEP_DOWN_TIME ; + data->REG_TOLERANCE_H =NCT6776_REG_TOLERANCE_H ; + data->REG_PWM[0 ] =NCT6775_REG_PWM ; + data->REG_PWM[1 ] =NCT6775_REG_FAN_START_OUTPUT ; + data->REG_PWM[2 ] =NCT6775_REG_FAN_STOP_OUTPUT ; + data->REG_PWM[5 ] =NCT6791_REG_WEIGHT_DUTY_STEP ; + data->REG_PWM[6 ] =NCT6791_REG_WEIGHT_DUTY_BASE ; + data->REG_PWM_READ =NCT6775_REG_PWM_READ ; + data->REG_PWM_MODE =NCT6776_REG_PWM_MODE ; + data->PWM_MODE_MASK =NCT6776_PWM_MODE_MASK ; + data->REG_AUTO_TEMP =NCT6775_REG_AUTO_TEMP ; + data->REG_AUTO_PWM =NCT6775_REG_AUTO_PWM ; + data->REG_CRITICAL_TEMP =NCT6775_REG_CRITICAL_TEMP ; + data->REG_CRITICAL_TEMP_TOLERANCE =NCT6775_REG_CRITICAL_TEMP_TOLERANCE ; + data->REG_CRITICAL_PWM_ENABLE =NCT6779_REG_CRITICAL_PWM_ENABLE ; + data->CRITICAL_PWM_ENABLE_MASK =NCT6779_CRITICAL_PWM_ENABLE_MASK ; + data->REG_CRITICAL_PWM =NCT6779_REG_CRITICAL_PWM ; + data->REG_TEMP_OFFSET =NCT6779_REG_TEMP_OFFSET ; + data->REG_TEMP_SOURCE =NCT6798_REG_TEMP_SOURCE ; + data->REG_TEMP_SEL =NCT6775_REG_TEMP_SEL ; + data->REG_WEIGHT_TEMP_SEL =NCT6791_REG_WEIGHT_TEMP_SEL ; + data->REG_WEIGHT_TEMP[0 ] =NCT6791_REG_WEIGHT_TEMP_STEP ; + data->REG_WEIGHT_TEMP[1 ] =NCT6791_REG_WEIGHT_TEMP_STEP_TOL ; + data->REG_WEIGHT_TEMP[2 ] =NCT6791_REG_WEIGHT_TEMP_BASE ; + data->REG_ALARM =NCT6799_REG_ALARM ; + data->REG_BEEP =NCT6792_REG_BEEP ; + data->REG_TSI_TEMP =NCT6796_REG_TSI_TEMP ; + num_reg_tsi_temp =ARRAY_SIZE (NCT6796_REG_TSI_TEMP ); + + reg_temp =NCT6798_REG_TEMP ; + num_reg_temp =ARRAY_SIZE (NCT6798_REG_TEMP ); + reg_temp_mon =NCT6798_REG_TEMP_MON ; + num_reg_temp_mon =ARRAY_SIZE (NCT6798_REG_TEMP_MON ); + reg_temp_over =NCT6798_REG_TEMP_OVER ; + reg_temp_hyst =NCT6798_REG_TEMP_HYST ; + reg_temp_config =NCT6779_REG_TEMP_CONFIG ; + reg_temp_alternate =NCT6798_REG_TEMP_ALTERNATE ; + reg_temp_crit =NCT6798_REG_TEMP_CRIT ; +break ;default :return -ENODEV ;
drivers/hwmon/nct6775.h +1 -1
@@ -8,7 +8,7 @@ enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 };enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; -#define NUM_TEMP 10/* Max number of temp attribute sets w/ limits*/ +#define NUM_TEMP 12/* Max number of temp attribute sets w/ limits*/ #define NUM_TEMP_FIXED 6/* Max number of fixed temp attribute sets */ #define NUM_TSI_TEMP 8/* Max number of TSI temp register pairs */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 346/609 hwmon: (nct6775) Fix access to temperature configuration registers
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit d56e460e19ea8382f813eb489730248ec8d7eb73 ]
The number of temperature configuration registers does not always match the total number of temperature registers. This can result in access errors reported if KASAN is enabled.
BUG: KASAN: global-out-of-bounds in nct6775_probe+0x5654/0x6fe9 nct6775_core
Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116")
--- drivers/hwmon/nct6775-core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
drivers/hwmon/nct6775-core.c +12 -2
@@ -3506,6 +3506,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,const u16 *reg_temp_mon, *reg_temp_alternate, *reg_temp_crit;const u16 *reg_temp_crit_l =NULL , *reg_temp_crit_h =NULL ;int num_reg_temp, num_reg_temp_mon, num_reg_tsi_temp; +int num_reg_temp_config;struct device *hwmon_dev;struct sensor_template_group tsi_temp_tg;
@@ -3588,6 +3589,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp_over =NCT6106_REG_TEMP_OVER ; reg_temp_hyst =NCT6106_REG_TEMP_HYST ; reg_temp_config =NCT6106_REG_TEMP_CONFIG ; + num_reg_temp_config =ARRAY_SIZE (NCT6106_REG_TEMP_CONFIG ); reg_temp_alternate =NCT6106_REG_TEMP_ALTERNATE ; reg_temp_crit =NCT6106_REG_TEMP_CRIT ; reg_temp_crit_l =NCT6106_REG_TEMP_CRIT_L ;
@@ -3663,6 +3665,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp_over =NCT6106_REG_TEMP_OVER ; reg_temp_hyst =NCT6106_REG_TEMP_HYST ; reg_temp_config =NCT6106_REG_TEMP_CONFIG ; + num_reg_temp_config =ARRAY_SIZE (NCT6106_REG_TEMP_CONFIG ); reg_temp_alternate =NCT6106_REG_TEMP_ALTERNATE ; reg_temp_crit =NCT6106_REG_TEMP_CRIT ; reg_temp_crit_l =NCT6106_REG_TEMP_CRIT_L ;
@@ -3740,6 +3743,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp_over =NCT6775_REG_TEMP_OVER ; reg_temp_hyst =NCT6775_REG_TEMP_HYST ; reg_temp_config =NCT6775_REG_TEMP_CONFIG ; + num_reg_temp_config =ARRAY_SIZE (NCT6775_REG_TEMP_CONFIG ); reg_temp_alternate =NCT6775_REG_TEMP_ALTERNATE ; reg_temp_crit =NCT6775_REG_TEMP_CRIT ;
@@ -3815,6 +3819,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp_over =NCT6775_REG_TEMP_OVER ; reg_temp_hyst =NCT6775_REG_TEMP_HYST ; reg_temp_config =NCT6776_REG_TEMP_CONFIG ; + num_reg_temp_config =ARRAY_SIZE (NCT6776_REG_TEMP_CONFIG ); reg_temp_alternate =NCT6776_REG_TEMP_ALTERNATE ; reg_temp_crit =NCT6776_REG_TEMP_CRIT ;
@@ -3894,6 +3899,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp_over =NCT6779_REG_TEMP_OVER ; reg_temp_hyst =NCT6779_REG_TEMP_HYST ; reg_temp_config =NCT6779_REG_TEMP_CONFIG ; + num_reg_temp_config =ARRAY_SIZE (NCT6779_REG_TEMP_CONFIG ); reg_temp_alternate =NCT6779_REG_TEMP_ALTERNATE ; reg_temp_crit =NCT6779_REG_TEMP_CRIT ;
@@ -4028,6 +4034,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp_over =NCT6779_REG_TEMP_OVER ; reg_temp_hyst =NCT6779_REG_TEMP_HYST ; reg_temp_config =NCT6779_REG_TEMP_CONFIG ; + num_reg_temp_config =ARRAY_SIZE (NCT6779_REG_TEMP_CONFIG ); reg_temp_alternate =NCT6779_REG_TEMP_ALTERNATE ; reg_temp_crit =NCT6779_REG_TEMP_CRIT ;
@@ -4117,6 +4124,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp_over =NCT6798_REG_TEMP_OVER ; reg_temp_hyst =NCT6798_REG_TEMP_HYST ; reg_temp_config =NCT6779_REG_TEMP_CONFIG ; + num_reg_temp_config =ARRAY_SIZE (NCT6779_REG_TEMP_CONFIG ); reg_temp_alternate =NCT6798_REG_TEMP_ALTERNATE ; reg_temp_crit =NCT6798_REG_TEMP_CRIT ;
@@ -4198,7 +4206,8 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, = reg_temp_crit[src - 1];if (reg_temp_crit_l && reg_temp_crit_l[i]) data->reg_temp[4 ][src -1 ] = reg_temp_crit_l[i]; - data->reg_temp_config[src -1 ] = reg_temp_config[i]; +if (i < num_reg_temp_config) + data->reg_temp_config[src -1 ] = reg_temp_config[i]; data->temp_src[src -1 ] = src;continue ; }
@@ -4211,7 +4220,8 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, data->reg_temp[0 ][s] = reg_temp[i]; data->reg_temp[1 ][s] = reg_temp_over[i]; data->reg_temp[2 ][s] = reg_temp_hyst[i]; - data->reg_temp_config[s] = reg_temp_config[i]; +if (i < num_reg_temp_config) + data->reg_temp_config[s] = reg_temp_config[i];if (reg_temp_crit_h && reg_temp_crit_h[i]) data->reg_temp[3 ][s] = reg_temp_crit_h[i];else if (reg_temp_crit[src -1 ])
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 347/609 hwmon: (nct6775-core) Fix number of temperature registers for NCT6116
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ]
Unlike NCT6106, NCT6116 only has three temperature registers, and with it only three temperature source and temperature source configuration registers. The register addresses match those of NCT6106 and can be re-used.
The code used a separate array to list the temperature source registers for NCT6116, but used the size of the NCT6106 register array to set the number of registers. The NCT6106 register array provides six addresses, while the temperature source register array for NCT6116 only provides three addresses. This causes a KASAN report.
BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775] Read of size 2 at addr ffffffffc19561a6 by task modprobe/954 ... Call Trace: dump_stack+0x7d/0xa7 print_address_description.constprop.0+0x1c/0x220 ? __kasan_kmalloc.constprop.0+0xc9/0xd0 ? __kmalloc_node_track_caller+0x194/0x5b0 ? nct6775_probe+0x936/0x46f0 [nct6775] ? nct6775_probe+0x936/0x46f0 [nct6775]
...
Fix the problem by hard-coding the number of temperature and temperature configuration registers to three for NCT6116. Drop the unnecessary NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE.
--- drivers/hwmon/nct6775-core.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/hwmon/nct6775-core.c +3 -5
@@ -845,8 +845,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 };static const u16 NCT6116_REG_PWM [] = {0x119 ,0x129 ,0x139 ,0x199 ,0x1a9 };static const u16 NCT6116_REG_FAN_MODE [] = {0x113 ,0x123 ,0x133 ,0x193 ,0x1a3 };static const u16 NCT6116_REG_TEMP_SEL [] = {0x110 ,0x120 ,0x130 ,0x190 ,0x1a0 }; -static const u16 NCT6116_REG_TEMP_SOURCE [] = { -0xb0 ,0xb1 ,0xb2 };static const u16 NCT6116_REG_CRITICAL_TEMP [] = {0x11a ,0x12a ,0x13a ,0x19a ,0x1aa };
@@ -3645,7 +3643,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, = NCT6106_CRITICAL_PWM_ENABLE_MASK; data->REG_CRITICAL_PWM =NCT6116_REG_CRITICAL_PWM ; data->REG_TEMP_OFFSET =NCT6106_REG_TEMP_OFFSET ; - data->REG_TEMP_SOURCE =NCT6116_REG_TEMP_SOURCE ; + data->REG_TEMP_SOURCE =NCT6106_REG_TEMP_SOURCE ; data->REG_TEMP_SEL =NCT6116_REG_TEMP_SEL ; data->REG_WEIGHT_TEMP_SEL =NCT6106_REG_WEIGHT_TEMP_SEL ; data->REG_WEIGHT_TEMP[0 ] =NCT6106_REG_WEIGHT_TEMP_STEP ;
@@ -3659,13 +3657,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp =NCT6106_REG_TEMP ; reg_temp_mon =NCT6106_REG_TEMP_MON ; - num_reg_temp =ARRAY_SIZE (NCT6106_REG_TEMP ); + num_reg_temp =3 ; num_reg_temp_mon =ARRAY_SIZE (NCT6106_REG_TEMP_MON ); num_reg_tsi_temp =ARRAY_SIZE (NCT6116_REG_TSI_TEMP ); reg_temp_over =NCT6106_REG_TEMP_OVER ; reg_temp_hyst =NCT6106_REG_TEMP_HYST ; reg_temp_config =NCT6106_REG_TEMP_CONFIG ; - num_reg_temp_config =ARRAY_SIZE (NCT6106_REG_TEMP_CONFIG ); + num_reg_temp_config =3 ; reg_temp_alternate =NCT6106_REG_TEMP_ALTERNATE ; reg_temp_crit =NCT6106_REG_TEMP_CRIT ; reg_temp_crit_l =NCT6106_REG_TEMP_CRIT_L ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 348/609 hwmon: (lm90) Only report alarms if driver is ready
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ]
Userspace can read sysfs attributes before driver registration is complete, immediately after devm_hwmon_device_register_with_info() has been called. At that time, data->hwmon_dev is not yet initialized. This can trigger a NULL pointer access since lm90_update_device() and with it lm90_update_alarms_locked() will be called. This call schedules report_work and lm90_report_alarms(), which passes the still-NULL data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer dereference.
Fix the problem by only scheduling the report and alert workers data->hwmon_dev is set.
--- drivers/hwmon/lm90.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hwmon/lm90.c +2 -2
@@ -1148,7 +1148,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) check_enable = (client->irq || !(data->config_orig &0x80 )) && (data->config &0x80 ); -if (force || check_enable) +if (data->hwmon_dev && (force || check_enable))schedule_work (&data->report_work);/*
@@ -1156,7 +1156,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)* alarms are all clear, and alerts are currently disabled. * Otherwise (re)schedule worker if needed. */ -if (check_enable) { +if (check_enable && data->hwmon_dev) {if (!(data->current_alarms & data->alert_alarms)) {dev_dbg (&client->dev,"Re-enabling ALERT#\n" );lm90_update_confreg (data, data->config & ~0x80 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 349/609 hwmon: (nzxt-smart2) DMA-align output buffer
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ]
Sashiko reports:
When send_output_report() calls hid_hw_output_report(), the underlying USB HID core calls usb_interrupt_msg() which maps this buffer directly for DMA.
When the DMA mapping flushes or invalidates the cacheline, it will corrupt the adjacent variables (mutex, update_interval) that were modified concurrently by the CPU. This causes memory corruption due to cacheline sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings for this violation.
Any operation that triggers send_output_report() (like setting a fan speed or updating the interval) causes the USB DMA mapping. On systems with non-coherent caches, this structural bug causes immediate and deterministic memory corruption.
Align the output buffer to ARCH_DMA_MINALIGN to fix the problem.
--- drivers/hwmon/nzxt-smart2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/nzxt-smart2.c +1 -1
@@ -203,7 +203,7 @@ struct drvdata {*/ struct mutex mutex;long update_interval; -u8 output_buffer[OUTPUT_REPORT_SIZE ]; +u8 output_buffer[OUTPUT_REPORT_SIZE ]__aligned(ARCH_DMA_MINALIGN) ; };static long scale_pwm_value (long val,long orig_max,long new_max)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 350/609 net: do not send ICMP/NDISC Redirects when peer allocation fails
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 dbc3791e3b2472e1ccc08947e0f83b443470ff4f ]
When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry under memory pressure or tree size caps, redirect handlers previously fell back to sending un-rate-limited ICMP/NDISC Redirect messages.
In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL. In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into inet_peer_xrlim_allow(), which returned true when peer == NULL.
Because ICMP/NDISC Redirects are not part of the default global rate limit mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates an un-rate-limited ICMP packet storm.
Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and ndisc_send_redirect() when peer is NULL.
--- net/ipv4/route.c | 2 -- net/ipv6/ip6_output.c | 2 +- net/ipv6/ndisc.c | 2 ++ 3 files changed, 3 insertions(+), 3 deletions(-)
net/ipv4/route.c +0 -2
@@ -902,8 +902,6 @@ void ip_rt_send_redirect(struct sk_buff *skb) peer =inet_getpeer_v4 (net->ipv4.peers,ip_hdr (skb)->saddr, vif);if (!peer) {rcu_read_unlock (); -icmp_send (skb,ICMP_REDIRECT ,ICMP_REDIR_HOST , -rt_nexthop (rt,ip_hdr (skb)->daddr));return ; }
net/ipv6/ip6_output.c +1 -1
@@ -623,7 +623,7 @@ int ip6_forward(struct sk_buff *skb)/* Limit redirects both by destination (here) and by source (inside ndisc_send_redirect) */ -if (inet_peer_xrlim_allow (peer,1 *HZ )) +if (peer &&inet_peer_xrlim_allow (peer,1 *HZ ))ndisc_send_redirect (skb, target);rcu_read_unlock (); }else {
net/ipv6/ndisc.c +2 -0
@@ -1727,6 +1727,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)rcu_read_lock (); peer =inet_getpeer_v6 (net->ipv6.peers, &ipv6_hdr (skb)->saddr); +if (!peer) + goto release; ret =inet_peer_xrlim_allow (peer,1 *HZ );rcu_read_unlock ();
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 351/609 hwmon: (nct6775-core) Prevent access to unsupported weight registers
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ]
Sashiko reports:
During initialization of the nct6116 chip, the driver sets data->pwm_num to 5. However, it assigns several NCT6106 register arrays (such as NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. These arrays only contain 3 elements.
In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If data->has_pwm has bits 3 or 4 set (which is structurally possible for nct6116), the loop attempts to read elements at index 3 and 4 from these 3-element arrays. This results in a global out-of-bounds read, which can be caught by KASAN.
Furthermore, the driver uses these garbage out-of-bounds values as hardware register addresses for subsequent read and write operations. This leads to invalid hardware register access, potentially causing hardware misconfiguration or system crashes.
The underlying problem is that the chip does support up to five fan control channels, but only the first three support weight control. Fix the problem by extending the affected weight register arrays with zeroed fields. The driver uses zeroed register addresses to determine if a register is supported or not, and skips accesses for unsupported registers.
--- drivers/hwmon/nct6775-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/hwmon/nct6775-core.c +6 -6
@@ -790,12 +790,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };static const u16 NCT6106_REG_TARGET [] = {0x111 ,0x121 ,0x131 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_SEL [] = {0x168 ,0x178 ,0x188 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_STEP [] = {0x169 ,0x179 ,0x189 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL [] = {0x16a ,0x17a ,0x18a }; -static const u16 NCT6106_REG_WEIGHT_DUTY_STEP [] = {0x16b ,0x17b ,0x18b }; -static const u16 NCT6106_REG_WEIGHT_TEMP_BASE [] = {0x16c ,0x17c ,0x18c }; -static const u16 NCT6106_REG_WEIGHT_DUTY_BASE [] = {0x16d ,0x17d ,0x18d }; +static const u16 NCT6106_REG_WEIGHT_TEMP_SEL [] = {0x168 ,0x178 ,0x188 ,0 ,0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP [] = {0x169 ,0x179 ,0x189 ,0 ,0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL [] = {0x16a ,0x17a ,0x18a ,0 ,0 }; +static const u16 NCT6106_REG_WEIGHT_DUTY_STEP [] = {0x16b ,0x17b ,0x18b ,0 ,0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_BASE [] = {0x16c ,0x17c ,0x18c ,0 ,0 }; +static const u16 NCT6106_REG_WEIGHT_DUTY_BASE [] = {0x16d ,0x17d ,0x18d ,0 ,0 };static const u16 NCT6106_REG_AUTO_TEMP [] = {0x160 ,0x170 ,0x180 };static const u16 NCT6106_REG_AUTO_PWM [] = {0x164 ,0x174 ,0x184 };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 352/609 net: bridge: mrp: fix Option TLV length in MRP_Test frames
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Corvaglia <[email protected]>
[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ]
oui is a pointer, so sizeof(oui) is the pointer size. The MRA Option TLV thus advertises a wrong length (15 vs 10 on x86_64), causing misparsing of the frame on peers. Fix is to replace with sizeof(*oui).
--- net/bridge/br_mrp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/bridge/br_mrp.c +1 -1
@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,struct br_mrp_oui_hdr *oui =NULL ;u8 length; - length =sizeof (*sub_opt) +sizeof (*sub_tlv) +sizeof (oui) + + length =sizeof (*sub_opt) +sizeof (*sub_tlv) +sizeof (*oui) +MRP_OPT_PADDING ;br_mrp_skb_tlv (skb,BR_MRP_TLV_HEADER_OPTION , length);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 353/609 forcedeth: fix UAF of txrx_stats in nv_remove
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chenguang Zhao <[email protected]>
[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ]
nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, and nv_close()/drain may still access txrx_stats, leading to a use-after-free.
Free the stats only after unregister_netdev().
--- drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/nvidia/forcedeth.c +2 -2
@@ -6199,10 +6199,10 @@ static void nv_remove(struct pci_dev *pci_dev)struct net_device *dev =pci_get_drvdata (pci_dev);struct fe_priv *np =netdev_priv (dev); -free_percpu (np->txrx_stats); -unregister_netdev (dev); +free_percpu (np->txrx_stats); +nv_restore_mac_addr (pci_dev);/* restore any phy related changes */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 354/609 hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ]
During adt7470_read_temperatures(), the driver temporarily switches the PWM channels to manual mode, performs the temperature collection, and then restores the original configuration registers.
However, if an I2C transaction fails at any point after entering manual mode, the function aborts and returns immediately. This leaves the configuration registers un-restored, permanently trapping the fans in manual mode.
Introduce a recovery path to ensure that the original PWM configuration registers are always restored, even when intermediate I2C operations fail.
--- drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-)
drivers/hwmon/adt7470.c +29 -11
@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in/* Probe for temperature sensors. Assumes lock is held */ static int adt7470_read_temperatures (struct adt7470_data *data) { -unsigned long res; +struct device *dev =regmap_get_device (data->regmap); +u8 pwm[ADT7470_FAN_COUNT ];unsigned int pwm_cfg[2 ]; -int err; +unsigned long res; +int err, err2;int i; -u8 pwm[ADT7470_FAN_COUNT ];/* save pwm[1-4] config register */ err =regmap_read (data->regmap,ADT7470_REG_PWM_CFG (0 ), &pwm_cfg[0 ]);
@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) err =regmap_update_bits (data->regmap,ADT7470_REG_PWM_CFG (2 ),ADT7470_PWM_AUTO_MASK ,0 );if (err <0 ) -return err; + goto out_restore;/* write pwm control to whatever it was */ err =regmap_bulk_write (data->regmap,ADT7470_REG_PWM (0 ), &pwm[0 ],ADT7470_PWM_COUNT );if (err <0 ) -return err; + goto out_restore;/* start reading temperature sensors */ err =regmap_update_bits (data->regmap,ADT7470_REG_CFG ,ADT7470_T05_STB_MASK ,ADT7470_T05_STB_MASK );if (err <0 ) -return err; + goto out_restore;/* Delay is 200ms * number of temp sensors. */ res = msleep_interruptible((data->num_temp_sensors >= 0 ?
@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) err =regmap_update_bits (data->regmap,ADT7470_REG_CFG ,ADT7470_T05_STB_MASK ,0 );if (err <0 ) -return err; + goto out_restore; +out_restore:/* restore pwm[1-4] config registers */ - err =regmap_write (data->regmap,ADT7470_REG_PWM_CFG (0 ), pwm_cfg[0 ]); -if (err <0 ) -return err; - err =regmap_write (data->regmap,ADT7470_REG_PWM_CFG (2 ), pwm_cfg[1 ]); + err2 =regmap_write (data->regmap,ADT7470_REG_PWM_CFG (0 ), pwm_cfg[0 ]); +if (err2 <0 ) { +dev_warn_ratelimited (dev, +"failed to restore PWM{1,2} config (%d)\n" , + err2); + +if (!err) + err = err2; + } + + err2 =regmap_write (data->regmap,ADT7470_REG_PWM_CFG (2 ), pwm_cfg[1 ]); +if (err2 <0 ) { +dev_warn_ratelimited (dev, +"failed to restore PWM{3,4} config (%d)\n" , + err2); + +if (!err) + err = err2; + } +if (err <0 )return err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 355/609 hwmon: (adt7470) Fix cache updated before hardware write on I2C error
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ]
adt7470_temp_write() and adt7470_pwm_write() update the driver's cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing the corresponding regmap_write(), and never check whether the write succeeded before committing that update. If the I2C transaction fails, the function correctly propagates the error to the caller, but the cache silently keeps the new value, which was never actually applied to the hardware. Subsequent reads then report a value that does not match the device state.
Reorder both write paths to update the cache only after a successful regmap_write(), so the cache always reflects what was actually written to the hardware.
--- drivers/hwmon/adt7470.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
drivers/hwmon/adt7470.c +9 -5
@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long vaswitch (attr) {case hwmon_temp_min:mutex_lock (&data->lock); - data->temp_min[channel] = val; err =regmap_write (data->regmap,ADT7470_TEMP_MIN_REG (channel), val); +if (!err) + data->temp_min[channel] = val;mutex_unlock (&data->lock);break ;case hwmon_temp_max:mutex_lock (&data->lock); - data->temp_max[channel] = val; err =regmap_write (data->regmap,ADT7470_TEMP_MAX_REG (channel), val); +if (!err) + data->temp_max[channel] = val;mutex_unlock (&data->lock);break ;default :
@@ -839,9 +841,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long valcase hwmon_pwm_input: val =clamp_val (val,0 ,255 );mutex_lock (&data->lock); - data->pwm[channel] = val; err =regmap_write (data->regmap,ADT7470_REG_PWM (channel), - data->pwm[channel]); + val); +if (!err) + data->pwm[channel] = val;mutex_unlock (&data->lock);break ;case hwmon_pwm_enable:
@@ -855,10 +858,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val val--;mutex_lock (&data->lock); - data->pwm_automatic[channel] = val; err =regmap_update_bits (data->regmap,ADT7470_REG_PWM_CFG (channel), pwm_auto_reg_mask, val ? pwm_auto_reg_mask :0 ); +if (!err) + data->pwm_automatic[channel] = val;mutex_unlock (&data->lock);break ;case hwmon_pwm_freq:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 356/609 hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ]
When userspace configures 'auto_update_interval' to 0 via sysfs, the background kthread executes schedule_timeout_interruptible(0), which returns immediately.
If 'num_temp_sensors' is concurrently or previously set to 0, the msleep_interruptible() delay inside adt7470_read_temperatures() also becomes 0. This combination forces the background thread into a tight, unbounded busy-loop, hogging the CPU and flooding the I2C bus with a continuous stream of transactions.
Fix this vulnerability by raising the lower limit of the clamp_val in auto_update_interval_store() from 0 to 500 milliseconds. This guarantees a reasonable minimum sleep window between sensor updates, protecting the system from intentional or accidental I2C bus denial of service.
--- drivers/hwmon/adt7470.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/adt7470.c +1 -1
@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev,if (kstrtol (buf,10 , &temp))return -EINVAL ; - temp =clamp_val (temp,0 ,60000 ); + temp =clamp_val (temp,500 ,60000 );mutex_lock (&data->lock); data->auto_update_interval = temp;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 357/609 hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ]
During the conversion the alarm callback started interpreting the channel index as an alarm bitmask, resulting in incorrect alarm reporting. Compute the proper alarm bit instead.
--- drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
drivers/hwmon/adt7470.c +17 -2
@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };#define ALARM2 (x) ((x) << 8) +/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and +* 0x72, or BIT(0)..BIT(6) of data->alarm. +* TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and +* 0x73, or BIT(8)..BIT(10) of data->alarm. +*/ +#define TEMP_ALARM_BIT (ch) ({ \ + typeof(ch) _ch = (ch); \ + (1 << (_ch < 7 ? _ch : _ch + 1)); \ +}) + +/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in +* reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. +*/ +#define FAN_ALARM_BIT (ch) (1 << (12 + (ch))) +#define ADT7470_VENDOR 0x41#define ADT7470_DEVICE 0x70/* datasheet only mentions a revision 2 */
@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va *val =1000 * data->temp_max[channel];break ;case hwmon_temp_alarm: - *val = !!(data->alarm & channel); + *val = !!(data->alarm &TEMP_ALARM_BIT (channel));break ;default :return -EOPNOTSUPP ;
@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val *val =0 ;break ;case hwmon_fan_alarm: - *val = !!(data->alarm & (1 << (12 + channel))); + *val = !!(data->alarm &FAN_ALARM_BIT (channel));break ;default :return -EOPNOTSUPP ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 358/609 hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ]
The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are currently defined with swapped bit values.
According to Table 22 of the ADT7470 datasheet, the Fan Control Mode Configuration for register 0x69 follows the exact same bit position layout as register 0x68:
- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80
- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40
- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80
- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40
Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40.
This typo did not cause any functional bugs because these specific macros are never referenced in the driver code. Instead, the driver correctly applies the configuration by relying on the modulo parity of the channel index (e.g., channel % 2) to selectively apply either ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). Since the bit layout is identical between the two configuration registers, the hardware is currently configured correctly.
Fix the macro definitions to reflect the datasheet accurately and prevent future bugs or confusion during code review and refactoring. As this is a purely cosmetic fix with no functional impact, a backport to stable kernels is not necessary.
--- drivers/hwmon/adt7470.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hwmon/adt7470.c +2 -2
@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };#define ADT7470_PWM1_AUTO_MASK 0x80#define ADT7470_PWM_AUTO_MASK 0xC0#define ADT7470_REG_PWM34_CFG 0x69 -#define ADT7470_PWM3_AUTO_MASK 0x40 -#define ADT7470_PWM4_AUTO_MASK 0x80 +#define ADT7470_PWM4_AUTO_MASK 0x40 +#define ADT7470_PWM3_AUTO_MASK 0x80#define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 359/609 hwmon: (adt7470) Use cached PWM frequency value
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ]
adt7470_pwm_read() currently ignores failures returned by pwm1_freq_get(). If the register read fails, the negative error code is returned through *val while the function itself reports success, potentially exposing a negative PWM frequency through sysfs.
Fix this by using the cached PWM frequency maintained by the driver, eliminating the register access from the read path.
Apart from the corrected error propagation and using the cached value, no functional change is intended.
--- drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
drivers/hwmon/adt7470.c +18 -2
@@ -182,6 +182,7 @@ struct adt7470_data {u8 pwm_min[ADT7470_PWM_COUNT ];s8 pwm_tmin[ADT7470_PWM_COUNT ];u8 pwm_auto_temp[ADT7470_PWM_COUNT ]; +u32 pwm_freq;struct task_struct *auto_update;unsigned int auto_update_interval;
@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, }/* These are the valid PWM frequencies to the nearest Hz */ -static const int adt7470_freq_map[] = { +static const u32 adt7470_freq_map[] = {11 ,15 ,22 ,29 ,35 ,44 ,59 ,88 ,1400 ,22500 };
@@ -804,7 +805,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val *val =1 + data->pwm_automatic[channel];break ;case hwmon_pwm_freq: - *val =pwm1_freq_get (dev); + *val = data->pwm_freq;break ;default :return -EOPNOTSUPP ;
@@ -817,12 +818,14 @@ static int pwm1_freq_set(struct device *dev, long freq) {struct adt7470_data *data =dev_get_drvdata (dev);unsigned int low_freq =ADT7470_CFG_LF ; +u32 closest_freq;int index;int err;/* Round the user value given to the closest available frequency */ index =find_closest (freq, adt7470_freq_map,ARRAY_SIZE (adt7470_freq_map)); + closest_freq = adt7470_freq_map[index];if (index >=8 ) { index -=8 ;
@@ -840,6 +843,10 @@ static int pwm1_freq_set(struct device *dev, long freq) err =regmap_update_bits (data->regmap,ADT7470_REG_CFG_2 ,ADT7470_FREQ_MASK , index <<ADT7470_FREQ_SHIFT ); +if (err <0 ) + goto out; + + data->pwm_freq = closest_freq; out:mutex_unlock (&data->lock);
@@ -1293,6 +1300,7 @@ static int adt7470_probe(struct i2c_client *client)struct device *dev = &client->dev;struct adt7470_data *data;struct device *hwmon_dev; +int freq_val;int err; data =devm_kzalloc (dev,sizeof (struct adt7470_data ),GFP_KERNEL );
@@ -1317,6 +1325,14 @@ static int adt7470_probe(struct i2c_client *client)if (err <0 )return err; + freq_val =pwm1_freq_get (dev); +if (freq_val <=0 ) { + err = freq_val <0 ? freq_val : -EINVAL ; +return err; + } + + data->pwm_freq = (u32 )freq_val; +/* Register sysfs hooks */ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &adt7470_chip_info,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 360/609 hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ]
If the fan data becomes 0 between the FAN_DATA_VALID() check and the FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash due to a race with a concurrent update of the cached fan value.
Fix a TOCTOU issue by reading fan data once.
--- drivers/hwmon/adt7470.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-)
drivers/hwmon/adt7470.c +10 -13
@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev,static int adt7470_fan_read (struct device *dev,u32 attr,int channel,long *val) {struct adt7470_data *data =adt7470_update_device (dev); +u16 fan_data;if (IS_ERR (data))return PTR_ERR (data);switch (attr) {case hwmon_fan_input: -if (FAN_DATA_VALID (data->fan[channel])) - *val =FAN_PERIOD_TO_RPM (data->fan[channel]); -else - *val =0 ; + fan_data =READ_ONCE (data->fan[channel]);break ;case hwmon_fan_min: -if (FAN_DATA_VALID (data->fan_min[channel])) - *val =FAN_PERIOD_TO_RPM (data->fan_min[channel]); -else - *val =0 ; + fan_data =READ_ONCE (data->fan_min[channel]);break ;case hwmon_fan_max: -if (FAN_DATA_VALID (data->fan_max[channel])) - *val =FAN_PERIOD_TO_RPM (data->fan_max[channel]); -else - *val =0 ; + fan_data =READ_ONCE (data->fan_max[channel]);break ;case hwmon_fan_alarm: *val = !!(data->alarm &FAN_ALARM_BIT (channel)); -break ; +return 0 ;default :return -EOPNOTSUPP ; } +if (FAN_DATA_VALID (fan_data)) + *val =FAN_PERIOD_TO_RPM (fan_data); +else + *val =0 ; +return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 361/609 hwmon: (adt7470) Fix PWM auto temp state array and bounds check
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ]
In pwm_auto_temp_store(), the parsed user input was missing bounds checks, allowing values > 0xF to overflow into the adjacent channel's bits. Furthermore, the value was being incorrectly written to the pwm_automatic state array instead of pwm_auto_temp.
Fix this by rejecting values > 0xF with -EINVAL, and assigning the value to the correct array only after a successful I2C write.
--- drivers/hwmon/adt7470.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/hwmon/adt7470.c +6 -1
@@ -1057,8 +1057,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,if (temp <0 )return temp; +if (temp >0xF ) +return -EINVAL ; +mutex_lock (&data->lock); - data->pwm_automatic[attr->index] = temp;if (!(attr->index %2 )) { mask =0xF0 ;
@@ -1069,6 +1071,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, } err =regmap_update_bits (data->regmap, pwm_auto_reg, mask, val); +if (!err) + data->pwm_auto_temp[attr->index] = temp; +mutex_unlock (&data->lock);return err <0 ? err : count;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 362/609 powerpc/boot: Fix simpleboot CPU node lookup check
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ]
fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly.
--- arch/powerpc/boot/simpleboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/boot/simpleboot.c +1 -1
@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,/* finally, setup the timebase */ node =fdt_node_offset_by_prop_value (_dtb_start,-1 ,"device_type" ,"cpu" ,sizeof ("cpu" )); -if (!node) +if (node <0 )fatal ("Cannot find cpu node\n" ); timebase =fdt_getprop (_dtb_start, node,"timebase-frequency" , &size);if (timebase && (size ==4 ))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 363/609 powerpc/boot: Fix treeboot-currituck CPU node lookup check
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ]
fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly.
--- arch/powerpc/boot/treeboot-currituck.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/boot/treeboot-currituck.c +1 -1
@@ -102,7 +102,7 @@ void platform_init(void) node =fdt_node_offset_by_prop_value (_dtb_start,-1 ,"device_type" ,"cpu" ,sizeof ("cpu" )); -if (!node) +if (node <0 )fatal ("Cannot find cpu node\n" ); timebase =fdt_getprop (_dtb_start, node,"timebase-frequency" , &size);if (timebase && (size ==4 ))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 364/609 powerpc/boot: Fix treeboot-akebono CPU node lookup check
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ]
fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly.
--- arch/powerpc/boot/treeboot-akebono.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/boot/treeboot-akebono.c +1 -1
@@ -146,7 +146,7 @@ void platform_init(char *userdata) node =fdt_node_offset_by_prop_value (_dtb_start,-1 ,"device_type" ,"cpu" ,sizeof ("cpu" )); -if (!node) +if (node <0 )fatal ("Cannot find cpu node\n" ); timebase =fdt_getprop (_dtb_start, node,"timebase-frequency" , &size);if (timebase && (size ==4 ))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 365/609 wifi: mac80211: validate individual TWT params before driver setup
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 0502d5077e419427d80f4d46ba95d0067f5fb916 ]
ieee80211_process_rx_twt_action() only partially validates a received S1G TWT setup frame before queueing it.
An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup() with twt->length too short for the full struct ieee80211_twt_params.
The individual path passes twt to drv_add_twt_setup(). Both the tracepoint and the driver callback consume the complete parameters block, not merely req_type. Do not pass a short individual agreement to the driver. Broadcast agreements remain unchanged because they are rejected locally after accessing only req_type.
[edit commit message to not overclaim lack of validation nor
understate driver impact]
--- net/mac80211/s1g.c | 4 ++++ 1 file changed, 4 insertions(+)
net/mac80211/s1g.c +4 -0
@@ -100,6 +100,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;struct ieee80211_twt_params *twt_agrt = (void *)twt->params; +if (!(twt->control &IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST ) && + twt->length <sizeof (twt->control) +sizeof (*twt_agrt)) +return ; + twt_agrt->req_type &=cpu_to_le16 (~IEEE80211_TWT_REQTYPE_REQUEST );/* broadcast TWT not supported yet */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 366/609 hwmon: (pmbus) Fix return value from pmbus_update_byte_data()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ]
pmbus_update_byte_data() is supposed to return a negative error code or 0. However, if no change is made to the register, it actually returns the register value. This can result in problems if the calling code explicitly expects to see an error code or 0.
Fix it to return 0 on success or the error code as expected.
--- drivers/hwmon/pmbus/pmbus_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/pmbus/pmbus_core.c +1 -1
@@ -442,7 +442,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,if (tmp != rv) rv =_pmbus_write_byte_data (client, page, reg, tmp); -return rv; +return rv <0 ? rv :0 ; }EXPORT_SYMBOL_NS_GPL (pmbus_update_byte_data,PMBUS );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 367/609 Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiale Yao <[email protected]>
[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ]
l2cap_le_connect_rsp() obtains a channel via __l2cap_get_chan_by_ident() but neither holds a reference nor uses l2cap_chan_hold_unless_zero() before locking and operating on it. A concurrent l2cap_chan_del() triggered by a remote disconnect can free the channel between the lookup and l2cap_chan_lock(), causing a use-after-free.
The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() to safely hold a reference, but l2cap_le_connect_rsp() was left unprotected.
Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup and l2cap_chan_put() on the exit path, consistent with other L2CAP response handlers.
--- net/bluetooth/l2cap_core.c | 5 +++++ 1 file changed, 5 insertions(+)
net/bluetooth/l2cap_core.c +5 -0
@@ -4831,6 +4831,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, goto unlock; } + chan =l2cap_chan_hold_unless_zero (chan); +if (!chan) +return -EBADSLT ; + err =0 ;l2cap_chan_lock (chan);
@@ -4876,6 +4880,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, }l2cap_chan_unlock (chan); +l2cap_chan_put (chan); unlock:mutex_unlock (&conn->chan_lock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 368/609 net: phylink: put link_gpio if phylink_create fails
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Marangi <[email protected]>
[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ]
In phylink_create() if phylink_register_sfp() returns an error, link_gpio obtained by phylink_parse_fixedlink() is never released. While this is a very unlikely scenario, it's worth to fix/handle this.
This was present from the very first implementation of phylink but got relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") where additional function were added after phylink_parse_fixedlink() making the release of link_gpio needed if such additional function errored out.
While at it, restructure the exit condition of phylink_create() with the goto pattern to reduce code duplication on handling error conditions.
--- drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-)
drivers/net/phy/phylink.c +15 -14
@@ -1483,8 +1483,8 @@ struct phylink *phylink_create(struct phylink_config *config, }else if (config->type ==PHYLINK_DEV ) { pl->dev = config->dev; }else { -kfree (pl); -return ERR_PTR (-EINVAL ); + ret = -EINVAL ; + goto free_pl; } pl->using_mac_select_pcs = using_mac_select_pcs;
@@ -1508,28 +1508,29 @@ struct phylink *phylink_create(struct phylink_config *config,phylink_validate (pl, pl->supported, &pl->link_config); ret =phylink_parse_mode (pl, fwnode); -if (ret <0 ) { -kfree (pl); -return ERR_PTR (ret); - } +if (ret <0 ) + goto free_pl;if (pl->cfg_link_an_mode ==MLO_AN_FIXED ) { ret =phylink_parse_fixedlink (pl, fwnode); -if (ret <0 ) { -kfree (pl); -return ERR_PTR (ret); - } +if (ret <0 ) + goto release_link_gpio; } pl->cur_link_an_mode = pl->cfg_link_an_mode; ret =phylink_register_sfp (pl, fwnode); -if (ret <0 ) { -kfree (pl); -return ERR_PTR (ret); - } +if (ret <0 ) + goto release_link_gpio;return pl; + +release_link_gpio: +if (pl->link_gpio) +gpiod_put (pl->link_gpio); +free_pl: +kfree (pl); +return ERR_PTR (ret); }EXPORT_SYMBOL_GPL (phylink_create);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 369/609 scsi: zfcp: Fix memory leak during adapter release by destroying gid_pn_req
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Block <[email protected]>
[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ]
When releasing an adapter we don't free the mempool 'gid_pn_req' that is allocated during the enqueue. This leaks memory:
unreferenced object 0xd8d29297de700 (size 256):
comm "(udev-worker)", pid 2105, jiffies 4294945794
hex dump (first 32 bytes):
00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N.........
ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g...
backtrace (crc 4a5b5da2):
[<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0
[<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0
[<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150
[<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp]
[<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp]
[<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp]
[<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80
[<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390
[<000dc45f643d8238>] online_store+0x298/0x5b0
[<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480
[<000dc45f62c81150>] new_sync_write+0x370/0x4b0
[<000dc45f62c87abe>] vfs_write+0x43e/0x5b0
[<000dc45f62c87ff4>] ksys_write+0x114/0x1f0
[<000dc45f621c4a16>] do_syscall+0x2f6/0x430
[<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0
[<000dc45f64dc2224>] system_call+0x74/0xa0Fix this by destroying the mempool during the adapter's release.
--- drivers/s390/scsi/zfcp_aux.c | 1 + 1 file changed, 1 insertion(+)
drivers/s390/scsi/zfcp_aux.c +1 -0
@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)static void zfcp_free_low_mem_buffers (struct zfcp_adapter *adapter) {mempool_destroy (adapter->pool.erp_req); +mempool_destroy (adapter->pool.gid_pn_req);mempool_destroy (adapter->pool.scsi_req);mempool_destroy (adapter->pool.scsi_abort);mempool_destroy (adapter->pool.qtcb_pool);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 370/609 scsi: target: Clear cmd_cnt when initial counter enrollment 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 a8ddfd2425bbbafadae8700d63ed8a61a4109878 ]
When target_get_sess_cmd() fails during session shutdown because percpu_ref_tryget_live() returns false, the command keeps the se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier without owning a reference. Final release through target_release_cmd_kref() then issues an unmatched percpu_ref_put().
Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") moved the cmd_cnt assignment ahead of the reference acquisition. Clear se_cmd->cmd_cnt whenever the initial target_get_sess_cmd() fails in target_init_cmd() and target_submit_tmr(), so release performs exactly one matching put per acquired reference.
--- drivers/target/target_core_transport.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
drivers/target/target_core_transport.c +10 -2
@@ -1670,6 +1670,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, u32 data_length, int task_attr, int data_dir, int flags) { struct se_portal_group *se_tpg; +int ret; se_tpg = se_sess->se_tpg;BUG_ON (!se_tpg);
@@ -1699,7 +1700,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,* necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second * kref_put() to happen during fabric packet acknowledgement. */ -return target_get_sess_cmd (se_cmd, flags &TARGET_SCF_ACK_KREF ); + ret =target_get_sess_cmd (se_cmd, flags &TARGET_SCF_ACK_KREF ); +if (ret) + se_cmd->cmd_cnt =NULL ; + +return ret; }EXPORT_SYMBOL_GPL (target_init_cmd);
@@ -1994,8 +1999,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,* allocation failure. */ ret =core_tmr_alloc_req (se_cmd, fabric_tmr_ptr, tm_type, gfp); -if (ret <0 ) +if (ret <0 ) { + se_cmd->cmd_cnt =NULL ;return -ENOMEM ; + }if (tm_type ==TMR_ABORT_TASK ) se_cmd->se_tmr_req->ref_task_tag = tag;
@@ -2003,6 +2010,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,/* See target_submit_cmd for commentary */ ret =target_get_sess_cmd (se_cmd, flags &TARGET_SCF_ACK_KREF );if (ret) { + se_cmd->cmd_cnt =NULL ;core_tmr_release_req (se_cmd->se_tmr_req);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 371/609 net: sxgbe: free TX rings on RX allocation failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chenguang Zhao <[email protected]>
[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ]
When RX descriptor ring allocation fails, init_dma_desc_rings() only frees the partially allocated RX rings and returns. The TX rings that were allocated earlier in the same function are leaked.
Rearrange error labels to clean up TX rings upon RX failures.
--- drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +4 -5
@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd)return 0 ; -txalloc_err: -while (queue_num--) -free_tx_ring (priv->device, priv->txq[queue_num], tx_rsize); -return ret; - rxalloc_err:while (queue_num--)free_rx_ring (priv->device, priv->rxq[queue_num], rx_rsize); + queue_num =SXGBE_TX_QUEUES ; +txalloc_err: +while (queue_num--) +free_tx_ring (priv->device, priv->txq[queue_num], tx_rsize);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 372/609 net: sxgbe: check descriptor ring allocation failures
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chenguang Zhao <[email protected]>
[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ]
sxgbe_open() ignores the return value of init_dma_desc_rings() and continues to program DMA with invalid ring addresses when allocation fails. Check the return value and disconnect the PHY on failure.
--- drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +4 -1
@@ -1080,7 +1080,9 @@ static int sxgbe_open(struct net_device *dev) priv->dma_buf_sz =SXGBE_ALIGN (DMA_BUFFER_SIZE ); priv->tx_tc =TC_DEFAULT ; priv->rx_tc =TC_DEFAULT ; -init_dma_desc_rings (dev); + ret =init_dma_desc_rings (dev); +if (ret) + goto init_phy_error;/* DMA initialization and SW reset */ ret =sxgbe_init_dma_engine (priv);
@@ -1189,6 +1191,7 @@ static int sxgbe_open(struct net_device *dev) init_error:free_dma_desc_resources (priv); +init_phy_error:if (dev->phydev)phy_disconnect (dev->phydev); phy_error:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 373/609 can: isotp: check register_netdevice_notifier() error in module init
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Minhong He <[email protected]>
[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ]
Register the netdevice notifier before can_proto_register() and check the return value. If protocol registration fails, unregister the notifier before returning the error.
Align isotp_module_init() with the reordering already done for raw.c (commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization in bcm_module_init()")).
--- net/can/isotp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
net/can/isotp.c +9 -4
@@ -1825,13 +1825,18 @@ static __init int isotp_module_init(void)pr_info ("can: isotp protocol\n" ); + err =register_netdevice_notifier (&canisotp_notifier); +if (err) +return err; + err =can_proto_register (&isotp_can_proto); -if (err <0 ) +if (err <0 ) {pr_err ("can: registration of isotp protocol failed %pe\n" ,ERR_PTR (err)); -else -register_netdevice_notifier (&canisotp_notifier); +unregister_netdevice_notifier (&canisotp_notifier); +return err; + } -return err; +return 0 ; } static __exit void isotp_module_exit(void)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 374/609 tracing/mmiotrace: Reset dropped_count in mmio_reset_data()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ]
mmio_reset_data() is called during tracer initialization, reset, and start. While it resets overrun_detected and prev_overruns, it neglects to reset dropped_count. Consequently, dropped event counts from prior tracing sessions persist in dropped_count and corrupt overrun reports in subsequent runs.
Fix this by explicitly calling atomic_set(&dropped_count, 0) in mmio_reset_data().
--- kernel/trace/trace_mmiotrace.c | 1 + 1 file changed, 1 insertion(+)
kernel/trace/trace_mmiotrace.c +1 -0
@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr) { overrun_detected = false; prev_overruns =0 ; +atomic_set (&dropped_count,0 );tracing_reset_online_cpus (&tr->array_buffer); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 375/609 octeontx2-pf: Set correct sequence for carrier off and tx queue stop
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Suman Ghosh <[email protected]>
[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ]
During link down event, we were doing netif_tx_stop_all_queues() first and then netif_carrier_off(). This can cause a potential race since carrier is still on during down event. This patch reverse the calling order to fix the issue.
--- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +1 -1
@@ -842,8 +842,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf)netif_carrier_on (netdev);netif_tx_start_all_queues (netdev); }else { -netif_tx_stop_all_queues (netdev);netif_carrier_off (netdev); +netif_tx_stop_all_queues (netdev); } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 376/609 qede: sync udp_tunnel ports outside qede_lock in the recovery path
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Denis V. Lunev <[email protected]>
[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ]
A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports configured wedges the rtnetlink control plane of the whole machine:
NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2! [qede_recovery_handler:2665(ens6f0)]Starting a recovery process
The recovery path deadlocks on the driver's own mutex:
qede_sp_task
rtnl_lock()
mutex_lock(&edev->qede_lock) <- taken
qede_recovery_handler
qede_load
udp_tunnel_nic_reset_ntf
__udp_tunnel_nic_device_sync
info->sync_table == qede_udp_tunnel_sync
mutex_lock(&edev->qede_lock) <- same task: deadlockThe mutex is not recursive, so the kworker blocks on itself with rtnl_lock held, and neither lock is ever released. Every task that calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6 addrconf, sshd) blocks forever while the node still answers ping. In a vmcore from an affected production node rtnl_mutex.owner decodes to the very kworker blocked at the innermost mutex_lock() above.
Re-sync the tunnel ports from qede_sp_task() after the internal lock is dropped, still under rtnl_lock as the udp_tunnel API requires. This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf() under rtnl without the internal lock.
qede_recovery_handler() now returns whether it has successfully reloaded an open device, and the caller re-syncs the ports only in that case. This keeps the old gating exactly: a device that was down or a failed recovery returns false, as those paths never reached the udp_tunnel_nic_reset_ntf() call before either.
This was the only user of the qede_lock()/qede_unlock() helpers, so remove them.
--- drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-)
drivers/net/ethernet/qlogic/qede/qede_main.c +22 -22
@@ -108,7 +108,7 @@ static void qede_remove(struct pci_dev *pdev);static void qede_shutdown (struct pci_dev *pdev);static void qede_link_update (void *dev,struct qed_link_output *link);static void qede_schedule_recovery_handler (void *dev); -static void qede_recovery_handler (struct qede_dev *edev); +static bool qede_recovery_handler (struct qede_dev *edev);static void qede_schedule_hw_err_handler (void *dev,enum qed_hw_err_type err_type);static void qede_get_eth_tlv_data (void *edev,void *data);
@@ -1063,21 +1063,6 @@ void __qede_unlock(struct qede_dev *edev)mutex_unlock (&edev->qede_lock); } -/* This version of the lock should be used when acquiring the RTNL lock is also -* needed in addition to the internal qede lock. -*/ -static void qede_lock (struct qede_dev *edev) -{ -rtnl_lock (); -__qede_lock (edev); -} - -static void qede_unlock (struct qede_dev *edev) -{ -__qede_unlock (edev); -rtnl_unlock (); -} -static void qede_periodic_task (struct work_struct *work) { struct qede_dev *edev = container_of(work, struct qede_dev,
@@ -1114,6 +1099,8 @@ static void qede_sp_task(struct work_struct *work)*/ if (test_and_clear_bit (QEDE_SP_RECOVERY , &edev->sp_flags)) { +bool reloaded; +cancel_delayed_work_sync (&edev->periodic_task);#ifdef CONFIG_QED_SRIOV /* SRIOV must be disabled outside the lock to avoid a deadlock.
@@ -1122,9 +1109,17 @@ static void qede_sp_task(struct work_struct *work)if (pci_num_vf (edev->pdev))qede_sriov_configure (edev->pdev,0 );#endif -qede_lock (edev); -qede_recovery_handler (edev); -qede_unlock (edev); +rtnl_lock (); +__qede_lock (edev); + reloaded =qede_recovery_handler (edev); +__qede_unlock (edev); + +/* The udp_tunnel core synchronously calls back into +* qede_udp_tunnel_sync(), which takes the qede lock. +*/ +if (reloaded) +udp_tunnel_nic_reset_ntf (edev->ndev); +rtnl_unlock (); }__qede_lock (edev);
@@ -2665,9 +2660,13 @@ static void qede_recovery_failed(struct qede_dev *edev) edev->ops->common->set_power_state (edev->cdev, PCI_D3hot); } -static void qede_recovery_handler (struct qede_dev *edev) +/* Returns true if an open device was successfully reloaded and its +* udp_tunnel ports need to be re-synced by the caller. +*/ +static bool qede_recovery_handler (struct qede_dev *edev) {u32 curr_state = edev->state; +bool reloaded = false;int rc;DP_NOTICE (edev,"Starting a recovery process\n" );
@@ -2697,17 +2696,18 @@ static void qede_recovery_handler(struct qede_dev *edev) goto err;qede_config_rx_mode (edev->ndev); -udp_tunnel_nic_reset_ntf (edev->ndev); + reloaded = true; } edev->state = curr_state;DP_NOTICE (edev,"Recovery handling is done\n" ); -return ; +return reloaded; err:qede_recovery_failed (edev); +return false; }static void qede_atomic_hw_err_handler (struct qede_dev *edev)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 377/609 ksmbd: return success for deferred final close
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 c5db4de8988f1a621556ca5c4537f77b766ca07d ]
ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table reference. If another in-flight request still holds a reference, the final close is deferred until that request drops its reference.
The function currently returns -EINVAL in that deferred-final-close case because fp is cleared when the reference count does not reach zero. That turns a valid close into STATUS_FILE_CLOSED.
smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then closes the same directory handle before receiving the find response. The query holds a reference while it builds the response, so close must mark the handle closed and return success even though final teardown is delayed. Track whether the handle was successfully transitioned to FP_CLOSED and return success when only the final close is deferred.
Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()")
--- fs/smb/server/vfs_cache.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/smb/server/vfs_cache.c +3 -1
@@ -394,6 +394,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) {struct ksmbd_file *fp;struct ksmbd_file_table *ft; +bool closed = false;if (!has_file_id (id))return 0 ;
@@ -408,6 +409,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) fp =NULL ; else { fp->f_state =FP_CLOSED ; + closed = true;if (!atomic_dec_and_test (&fp->refcount)) fp =NULL ; }
@@ -415,7 +417,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)write_unlock (&ft->lock);if (!fp) -return -EINVAL ; +return closed ?0 : -EINVAL ;__put_fd_final (work, fp);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 378/609 ksmbd: fix use-after-free in __close_file_table_ids()
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 e7188199eff46a636f3436356f0aae039be6dd66 ]
A ksmbd_file can remain alive after logical close while another session holds a temporary reference obtained through ksmbd_lookup_fd_inode(). ksmbd_close_fd() currently marks the file closed and drops the idr-owned reference, but leaves the pointer published in the closing session's idr until the final reference is dropped.
If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final() supplies the foreign session's file table to __ksmbd_close_fd(). The object is then freed without being removed from its owner's idr, and the owner session later dereferences the stale pointer during file-table teardown.
Remove the volatile id from the owner's idr while ksmbd_close_fd() still holds that table's lock, and clear volatile_id before dropping the idr-owned reference. A later foreign final put then only performs physical destruction and cannot remove the object from the wrong table.
--- fs/smb/server/vfs_cache.c | 2 ++ 1 file changed, 2 insertions(+)
fs/smb/server/vfs_cache.c +2 -0
@@ -409,6 +409,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) fp =NULL ; else { fp->f_state =FP_CLOSED ; +idr_remove (ft->idr, id); + fp->volatile_id =KSMBD_NO_FID ; closed = true;if (!atomic_dec_and_test (&fp->refcount)) fp =NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 379/609 ipv6: fib6: fix NULL deref in fib6_walk_continue() on multi-batch dump
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengfei Zhang <[email protected]>
commit 9facb861dc6b9b9ea9793ef5032a9a826f7a4229 upstream.
inet6_dump_fib() saves its progress in cb->args[1] as a positional index within the current hash chain. Between batches, a concurrent fib6_new_table() can insert a new table at the chain head, shifting all existing entries. The saved index then lands on a different table, causing fib6_dump_table() to set w->root to the wrong table while w->node still points into the previous one. fib6_walk_continue() dereferences w->node->parent (NULL) and panics:
BUG: kernel NULL pointer dereference, address: 0000000000000008 RIP: 0010:fib6_walk_continue+0x6e/0x170 Call Trace: <TASK> fib6_dump_table.isra.0+0xc5/0x240 inet6_dump_fib+0xf6/0x420 rtnl_dumpit+0x30/0xa0 netlink_dump+0x15b/0x460 netlink_recvmsg+0x1d6/0x2a0 ____sys_recvmsg+0x17a/0x190
Fix by storing tb->tb6_id in cb->args[1] instead of a positional index. On resume, skip entries until the id matches; a concurrent head-insert can never match the saved id, so the walker always resumes on the correct table.
[Adapted to 5.10/6.1/6.6: inet6_dump_fib() there predates 22e36ea9f5d7
and 5fc68320c1fb, so the return variable is "res" not "err" and the RCU-protected hash walk exits via "out_unlock" instead of "unlock". Context-only change; the fix itself is identical.]
--- net/ipv6/ip6_fib.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
net/ipv6/ip6_fib.c +8 -9
@@ -625,11 +625,11 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)const struct nlmsghdr *nlh = cb->nlh;struct net *net =sock_net (skb->sk);unsigned int h, s_h; -unsigned int e =0 , s_e;struct fib6_walker *w;struct fib6_table *tb;struct hlist_head *head;int res =0 ; +u32 s_id;if (cb->strict_check) {int err;
@@ -687,25 +687,24 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) } s_h = cb->args[0 ]; - s_e = cb->args[1 ]; + s_id = cb->args[1 ];rcu_read_lock (); -for (h = s_h; h <FIB6_TABLE_HASHSZ ; h++, s_e =0 ) { - e =0 ; +for (h = s_h; h <FIB6_TABLE_HASHSZ ; h++, s_id =0 ) { head = &net->ipv6.fib_table_hash[h];hlist_for_each_entry_rcu (tb, head, tb6_hlist) { -if (e < s_e) - goto next; +if (s_id && tb->tb6_id != s_id) +continue ; + + s_id =0 ; + cb->args[1 ] = tb->tb6_id; res =fib6_dump_table (tb, skb, cb);if (res !=0 ) goto out_unlock; -next: - e++; } } out_unlock:rcu_read_unlock (); - cb->args[1 ] = e; cb->args[0 ] = h; out: res = res <0 ? res : skb->len;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 380/609 af_unix: Give up GC if MSG_PEEK intervened.
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 e5b31d988a41549037b8d8721a3c3cae893d8670 ]
Igor Ushakov reported that GC purged the receive queue of an alive socket due to a race with MSG_PEEK with a nice repro.
This is the exact same issue previously fixed by commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK").
After GC was replaced with the current algorithm, the cited commit removed the locking dance in unix_peek_fds() and reintroduced the same issue.
The problem is that MSG_PEEK bumps a file refcount without interacting with GC.
Consider an SCC containing sk-A and sk-B, where sk-A is close()d but can be recv()ed via sk-B.
The bad thing happens if sk-A is recv()ed with MSG_PEEK from sk-B and sk-B is close()d while GC is checking unix_vertex_dead() for sk-A and sk-B.
GC thread User thread
--------- -----------
unix_vertex_dead(sk-A)
-> true <------.
\
`------ recv(sk-B, MSG_PEEK)
invalidate !! -> sk-A's file refcount : 1 -> 2 close(sk-B)
-> sk-B's file refcount : 2 -> 1 unix_vertex_dead(sk-B)
-> true
Initially, sk-A's file refcount is 1 by the inflight fd in sk-B recvq. GC thinks sk-A is dead because the file refcount is the same as the number of its inflight fds.
However, sk-A's file refcount is bumped silently by MSG_PEEK, which invalidates the previous evaluation.
At this moment, sk-B's file refcount is 2; one by the open fd, and one by the inflight fd in sk-A. The subsequent close() releases one refcount by the former.
Finally, GC incorrectly concludes that both sk-A and sk-B are dead.
One option is to restore the locking dance in unix_peek_fds(), but we can resolve this more elegantly thanks to the new algorithm.
The point is that the issue does not occur without the subsequent close() and we actually do not need to synchronise MSG_PEEK with the dead SCC detection.
When the issue occurs, close() and GC touch the same file refcount. If GC sees the refcount being decremented by close(), it can just give up garbage-collecting the SCC.
Therefore, we only need to signal the race during MSG_PEEK with a proper memory barrier to make it visible to the GC.
Let's use seqcount_t to notify GC when MSG_PEEK occurs and let it defer the SCC to the next run.
This way no locking is needed on the MSG_PEEK side, and we can avoid imposing a penalty on every MSG_PEEK unnecessarily.
Note that we can retry within unix_scc_dead() if MSG_PEEK is detected, but we do not do so to avoid hung task splat from abusive MSG_PEEK calls.
--- include/net/af_unix.h | 1 + net/unix/af_unix.c | 2 ++ net/unix/garbage.c | 79 ++++++++++++++++++++++++++++--------------- 3 files changed, 54 insertions(+), 28 deletions(-)
include/net/af_unix.h +1 -0
@@ -23,6 +23,7 @@ void unix_del_edges(struct scm_fp_list *fpl);void unix_update_edges (struct unix_sock *receiver);int unix_prepare_fpl (struct scm_fp_list *fpl);void unix_destroy_fpl (struct scm_fp_list *fpl); +void unix_peek_fpl (struct scm_fp_list *fpl);void unix_gc (void );void wait_for_unix_gc (struct scm_fp_list *fpl);
net/unix/af_unix.c +2 -0
@@ -1778,6 +1778,8 @@ static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)static void unix_peek_fds (struct scm_cookie *scm,struct sk_buff *skb) { scm->fp =scm_fp_dup (UNIXCB (skb).fp); + +unix_peek_fpl (scm->fp); }static void unix_destruct_scm (struct sk_buff *skb)
net/unix/garbage.c +51 -28
126 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 381/609 rhashtable: clear stale iter->p on table restart
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang (Microsoft) <[email protected]>
[ Upstream commit 8173f7e2ce67e6ca1d4763f3da14e5b01ce77456 ]
rhashtable_walk_start_check() has two restart paths when resuming a walk. When iter->walker.tbl is valid, it re-validates iter->p against the table and sets iter->p = NULL if the object is gone. When iter->walker.tbl is NULL (table was freed during resize), it resets slot and skip but forgets to clear iter->p.
rhashtable_walk_next() then dereferences the stale iter->p, reading freed memory. This is a use-after-free.
Any caller that does multi-fragment rhashtable walks across walk_stop/walk_start boundaries is affected. Concrete cases include netlink_diag (__netlink_diag_dump in net/netlink/diag.c) and TIPC (tipc_nl_sk_walk in net/tipc/socket.c).
Crash stack (netlink_diag):
BUG: KASAN: slab-use-after-free in rhashtable_walk_next+0x365/0x3c0 Read of size 8 at addr ffff88801a9d2438 (freed kmalloc-2k, offset 1080) Call Trace: rhashtable_walk_next+0x365/0x3c0 (lib/rhashtable.c:1016) __netlink_diag_dump+0x160/0x760 (net/netlink/diag.c:122) netlink_diag_dump+0xc2/0x240 netlink_dump+0x5bc/0x1270 netlink_recvmsg+0x7a3/0x980 sock_recvmsg+0x1bc/0x200 __sys_recvfrom+0x1d4/0x2c0
--- lib/rhashtable.c | 1 + 1 file changed, 1 insertion(+)
lib/rhashtable.c +1 -0
@@ -730,6 +730,7 @@ int rhashtable_walk_start_check(struct rhashtable_iter *iter) iter->walker.tbl =rht_dereference_rcu (ht->tbl, ht); iter->slot =0 ; iter->skip =0 ; + iter->p =NULL ;return -EAGAIN ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 382/609 pinctrl: microchip-sgpio: add missing select REGMAP_MMIO
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Boortz <[email protected]>
commit 25cb6e9a13123d1039cdc75b446ac52e1ebdc26d upstream.
The driver calls ocelot_regmap_from_resource() via <linux/mfd/ocelot.h>, which internally uses devm_regmap_init_mmio() and requires REGMAP_MMIO. The Kconfig entry does not select REGMAP_MMIO, causing a build failure when no other driver in the config happens to pull in REGMAP_MMIO:
include/linux/mfd/ocelot.h:34:24: error: implicit declaration of function 'devm_regmap_init_mmio'
Found by randconfig testing on arm64; tinyconfig reproducer below.
--- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+)
drivers/pinctrl/Kconfig +1 -0
@@ -315,6 +315,7 @@ config PINCTRL_MICROCHIP_SGPIO select GENERIC_PINCTRL_GROUPS select GENERIC_PINMUX_FUNCTIONS select OF_GPIO + select REGMAP_MMIO help Support for the serial GPIO interface used on Microsemi and Microchip SoCs. By using a serial interface, the SIO
Greg Kroah-Hartman
maintainerPATCH 383/609 pinctrl: devicetree: dont free uninitialized dev_name on error path
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
commit 015b5bcbcb622b32317642be91a7f79aa5413649 upstream.
dt_remember_or_free_map() duplicates dev_name for each map entry. If kstrdup_const() fails, dt_free_map() frees dev_name in all num_maps entries, including entries that have not been initialized.
Some pinctrl drivers, including pinctrl-imx, allocate the map with kmalloc() and leave dev_name for the core to initialize. The untouched entries therefore contain uninitialized data which is passed to kfree_const().
Reproduced on qemu's mcimx6ul-evk (pinctrl-imx) with failslab injection while binding the pinctrl-consuming device, under KASAN:
BUG: KASAN: double-free in dt_free_map+0x34/0xa4 Free of addr c425a900 by task init/1 kfree from dt_free_map+0x34/0xa4 dt_free_map from dt_remember_or_free_map+0x184/0x198 dt_remember_or_free_map from pinctrl_dt_to_map+0x33c/0x4c8 pinctrl_dt_to_map from create_pinctrl+0x9c/0x5c0
Initialize all dev_name fields to NULL before duplicating the device name, making the full-map cleanup safe after a partial failure.
--- drivers/pinctrl/devicetree.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/pinctrl/devicetree.c +4 -0
@@ -69,6 +69,10 @@ static int dt_remember_or_free_map(strucint i;struct pinctrl_dt_map *dt_map; +/* Initialize dev_name before any allocation can fail */ +for (i =0 ; i < num_maps; i++) + map[i].dev_name =NULL ; +/* Initialize common mapping table entry fields */ for (i =0 ; i < num_maps; i++) {const char *devname;
Greg Kroah-Hartman
maintainerPATCH 384/609 pinctrl: bm1880: add missing select GENERIC_PINCONF
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Boortz <[email protected]>
commit dad6e107b3cd9d20514e7799b7ad8674f81e3f30 upstream.
drivers/pinctrl/pinctrl-bm1880.c initialises its pinconf_ops with .is_generic = true, but that field is only present when CONFIG_GENERIC_PINCONF is enabled (guarded by #ifdef in pinconf.h). The Kconfig entry for PINCTRL_BM1880 never selects GENERIC_PINCONF, so any config that enables CONFIG_PINCTRL_BM1880=y without CONFIG_GENERIC_PINCONF=y fails to compile:
drivers/pinctrl/pinctrl-bm1880.c:1288:10: error: 'const struct pinconf_ops' has no member named 'is_generic'
Found by randconfig testing on arm64; tinyconfig reproducer below. Add the missing select to fix the build.
--- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+)
drivers/pinctrl/Kconfig +1 -0
@@ -132,6 +132,7 @@ config PINCTRL_BM1880 depends on OF && (ARCH_BITMAIN || COMPILE_TEST) default ARCH_BITMAIN select PINMUX + select GENERIC_PINCONF help Pinctrl driver for Bitmain BM1880 SoC.
Greg Kroah-Hartman
maintainerPATCH 385/609 mm/percpu-km: fix bitmap overflow and accounting in pcpu_create_chunk()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zi Yan <[email protected]>
commit 89b1b79c308818a715e75f28744b70d8940a07c9 upstream.
In pcpu_create_chunk(), nr_pages is the total contiguous backing allocation, i.e., nr_units * pcpu_unit_pages, but pcpu_chunk_populated() uses it to set chunk->populated, whose size is pcpu_unit_pages, bitmap. Since bit N in chunk->populated means page offset N inside every unit is backed. When nr_units > 1, the function writes beyond chunk->populated. Fix it by using chunk->nr_pages.
It also fixes the global pcpu_nr_empty_pop_pages accounting, since pcpu_balance_free() only iterates up to chunk->nr_pages.
Commit a63d4ac4ab609 ("percpu: make percpu-km set chunk->populated bitmap properly") introduced the bitmap overflow issue. Later, commit b539b87fed37f ("percpu: implmeent pcpu_nr_empty_pop_pages and chunk->nr_populated") added pcpu_nr_empty_pop_pages and caused the accounting issue.
--- mm/percpu-km.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/percpu-km.c +1 -1
@@ -75,7 +75,7 @@ static struct pcpu_chunk *pcpu_create_ch chunk->base_addr =page_address (pages);spin_lock_irqsave (&pcpu_lock, flags); -pcpu_chunk_populated (chunk,0 , nr_pages); +pcpu_chunk_populated (chunk,0 , chunk->nr_pages);spin_unlock_irqrestore (&pcpu_lock, flags);pcpu_stats_chunk_alloc ();
Greg Kroah-Hartman
maintainerPATCH 386/609 mm/hugetlb: fix list corruption in allocate_file_region_entries()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiangfeng Cai <[email protected]>
commit dd9623f58ec702a07b2d67179d6fcea79c52231a upstream.
allocate_file_region_entries() tops up resv->region_cache with freshly allocated file_region descriptors. The allocation uses GFP_KERNEL, so resv->lock is dropped around it: the new entries are gathered on a stack-local list head, allocated_regions, and spliced into resv->region_cache once the lock is re-acquired.
The splice used list_splice(), which moves the entries but does not re-initialize the source head, so allocated_regions is left pointing at an entry that now lives on resv->region_cache. The top-up runs in a while loop that re-checks the cache deficit after re-acquiring the lock. For a shared mapping the resv_map is shared by every mapper of the hugetlbfs inode, so a concurrent region_chg()/region_add()/region_del() on the same resv_map can consume cache entries during the unlocked window and force a second iteration. That iteration calls list_add() on the stale head and corrupts the list; with CONFIG_DEBUG_LIST the __list_add_valid() check trips:
list_add corruption. next->prev should be prev (ffffc900011ff7f8), but was ffff88814c281460. (next=ffff88814c545640). kernel BUG at lib/list_debug.c:31!
allocate_file_region_entries+0x191/0x420 region_chg+0x267/0x300 hugetlb_reserve_pages+0x387/0xc80 hugetlbfs_file_mmap+0x2ce/0x3f0 mmap_region+0x1348/0x1a80 do_mmap+0x85e/0xb90 vm_mmap_pgoff+0x18c/0x330 ksys_mmap_pgoff+0x2a1/0x3e0 do_syscall_64+0xd7/0x420
Without CONFIG_DEBUG_LIST the bad list_add() silently links a kernel-stack address into resv->region_cache, leading to later use-after-free.
This was observed as a real host panic on a dense KVM host where a QEMU guest-RAM hugetlbfs file was mapped MAP_SHARED by both QEMU and a separate SPDK/DPDK vhost-user target, generating concurrent region_* traffic on one shared resv_map.
Use list_splice_init() so the source head is re-initialized empty after each splice, making the retry loop safe.
--- mm/hugetlb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/hugetlb.c +1 -1
@@ -679,7 +679,7 @@ static int allocate_file_region_entries(spin_lock (&resv->lock); -list_splice (&allocated_regions, &resv->region_cache); +list_splice_init (&allocated_regions, &resv->region_cache); resv->region_cache_count += to_allocate; }
Greg Kroah-Hartman
maintainerPATCH 387/609 KVM: SVM: Update x2APIC MSR intercepts if AVIC is inhibited while L2 is active
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit 7d3aae206663c4e006b25a1c7a20a4029e67da76 upstream.
Always update x2APIC MSR intercepts for L1 when AVIC is deactivated, even if L2 is active and KVM is using a separate MSR bitmap to run L2. If AVIC is fully enabled prior to running L2, and is then inhibited while L2 is active (for a VM-scoped inhibit), then KVM will run L1 with AVIC disabled, but with x2APIC MSR intercepts disabled, i.e. will allow L1 to read most of the host's APIC state, send arbitrary interrupts, change task priority, and ultimately trivially DoS the host.
E.g. sending a self-IPI in L1 on HYPERV_REENLIGHTENMENT_VECTOR, 0xee, with CONFIG_HYPERV=n in the host kernel as a "safe" PoC, yields:
Spurious interrupt (vector 0xee) on CPU#425. Acked
And hacking KVM to abuse kvm_set_posted_intr_wakeup_handler() to register a handler and WARN on POSTED_INTR_WAKEUP_VECTOR yields:
------------[ cut here ]------------ WARNING: arch/x86/kvm/svm/svm.c:5594 at pi_wakeup_handler+0x9/0x10 [kvm_amd], CPU#156: nested_x2apic_t/316940 CPU: 156 UID: 0 PID: 316940 Comm: nested_x2apic_t Tainted: G S U Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER Hardware name: Google Astoria-Turin/astoria, BIOS 0.20260209.0-0 02/09/2026 RIP: 0010:pi_wakeup_handler+0x9/0x10 [kvm_amd] Call Trace: <IRQ> sysvec_kvm_posted_intr_wakeup_ipi+0x64/0x80 </IRQ> <TASK> asm_sysvec_kvm_posted_intr_wakeup_ipi+0x1a/0x20 RIP: 0010:vcpu_run+0x1430/0x1e40 [kvm] kvm_arch_vcpu_ioctl_run+0x2c1/0x600 [kvm] kvm_vcpu_ioctl+0x580/0x6b0 [kvm] __se_sys_ioctl+0x6d/0xb0 do_syscall_64+0x10a/0x480 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x46ff4b </TASK> ---[ end trace 0000000000000000 ]---
--- arch/x86/kvm/svm/avic.c | 8 -------- 1 file changed, 8 deletions(-)
arch/x86/kvm/svm/avic.c +0 -8
@@ -120,14 +120,6 @@ static void avic_deactivate_vmcb(structif (!sev_es_guest (svm->vcpu.kvm))svm_set_intercept (svm,INTERCEPT_CR8_WRITE ); -/* -* If running nested and the guest uses its own MSR bitmap, there -* is no need to update L0's msr bitmap -*/ -if (is_guest_mode (&svm->vcpu) && -vmcb12_is_intercept (&svm->nested.ctl,INTERCEPT_MSR_PROT )) -return ; -/* Enabling MSR intercept for x2APIC registers */ svm_set_x2apic_msr_interception (svm, true); }
Greg Kroah-Hartman
maintainerPATCH 388/609 KVM: s390: pci: Reject adapter interrupt forwarding if already enabled
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Farhan Ali <[email protected]>
commit 8fa01be5a6149404adb82c0979a78f6347edd3ef upstream.
The MPCIFC instruction doesn't allow registering adapter interrupts without first unregistering. So reject any request to enable interrupt forwarding if its already enabled for the zPCI device. This also fixes overwriting and thus leaking resources when the ioctl is called multiple times for the same device.
--- arch/s390/kvm/pci.c | 4 ++++ 1 file changed, 4 insertions(+)
arch/s390/kvm/pci.c +4 -0
@@ -239,6 +239,10 @@ static int kvm_s390_pci_aif_enable(strucif (zdev->gisa ==0 )return -EINVAL ; +/* AIF already enabled for the device */ +if (zdev->kzdev->fib.fmt0.aibv !=0 ) +return -EINVAL ; + kvm = zdev->kzdev->kvm; msi_vecs = min_t(unsigned int, fib->fmt0.noi, zdev->max_msi);
Greg Kroah-Hartman
maintainerPATCH 389/609 KVM: s390: pci: Fix NULL dereference on AIBV allocation failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Farhan Ali <[email protected]>
commit 8bf09b9b7d3232806df95f409581f8a9fd99a3fa upstream.
The airq_iv_create() can return NULL on failure, but the return value was never checked. If it fails, zdev->aibv will be NULL and fail when dereferenced in kvm_zpci_set_airq(). Add a NULL check and free the previously allocated AISB bit and zdev->aisb on failure.
--- arch/s390/kvm/pci.c | 8 ++++++++ 1 file changed, 8 insertions(+)
arch/s390/kvm/pci.c +8 -0
@@ -295,6 +295,11 @@ static int kvm_s390_pci_aif_enable(strucAIRQ_IV_GUESTVEC , phys_to_virt(fib->fmt0.aibv)); +if (!zdev->aibv) { + rc = -ENOMEM ; + goto free_aisb; + } +spin_lock_irq (&aift->gait_lock); gaite = aift->gait + zdev->aisb;
@@ -331,6 +336,9 @@ static int kvm_s390_pci_aif_enable(struc rc =kvm_zpci_set_airq (zdev);return rc; +free_aisb: +airq_iv_free_bit (aift->sbv, zdev->aisb); + zdev->aisb =0 ; unlock:mutex_unlock (&aift->aift_lock); unpin2:
Greg Kroah-Hartman
maintainerPATCH 390/609 KVM: s390: pci: Validate AIBV and AISB before pinning guest pages
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Farhan Ali <[email protected]>
commit 868d32ac72cba21c5c6d8a66a814b7c25a3a5c01 upstream.
The AIBV holds one bit per MSI-X vector for a given function. The size of the bit vector is derived from the NOI and the AIBVO. If the size of the AIBV exceeds a single page boundary, then reject the request as we cannot safely pin the guest AIBV.
Similarly reject the request if the AISB address is not 8-byte aligned as the architecture requires doubleword alignment for the summary bit address. Since the AISBO can address up to 64 bits, the size of the AISB can only be 8 bytes for the function. This also ensures the AISB doesn't exceed a single page boundary.
--- arch/s390/kvm/pci.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
arch/s390/kvm/pci.c +15 -1
@@ -225,7 +225,7 @@ static int kvm_s390_pci_aif_enable(struc bool assist) {struct page *pages[1 ], *aibv_page, *aisb_page =NULL ; -unsigned int msi_vecs, idx; +unsigned int msi_vecs, idx, size;struct zpci_gaite *gaite;unsigned long hva, bit;struct kvm *kvm;
@@ -252,6 +252,14 @@ static int kvm_s390_pci_aif_enable(strucreturn gisc;/* Replace AIBV address */ + size =BITS_TO_LONGS (msi_vecs + fib->fmt0.aibvo) *sizeof (unsigned long ); + npages =DIV_ROUND_UP ((fib->fmt0.aibv & ~PAGE_MASK ) + size,PAGE_SIZE ); +/* AIBV cannot span more than 1 page */ +if (npages >1 ) { + rc = -EINVAL ; + goto out; + } + idx =srcu_read_lock (&kvm->srcu); hva =gfn_to_hva (kvm,gpa_to_gfn ((gpa_t )fib->fmt0.aibv)); npages =pin_user_pages_fast (hva,1 ,FOLL_WRITE |FOLL_LONGTERM , pages);
@@ -267,6 +275,12 @@ static int kvm_s390_pci_aif_enable(struc/* Pin the guest AISB if one was specified */ if (fib->fmt0.sum ==1 ) { +/* AISB must be dword aligned */ +if (fib->fmt0.aisb &0x7 ) { + rc = -EINVAL ; + goto unpin1; + } + idx =srcu_read_lock (&kvm->srcu); hva =gfn_to_hva (kvm,gpa_to_gfn ((gpa_t )fib->fmt0.aisb)); npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM,
Greg Kroah-Hartman
maintainerPATCH 391/609 sctp: validate Adaptation Indication parameter length
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Charles Vosburgh <[email protected]>
commit 74b21f52c5c5a71a05c0ff70e513f4f04ff28b17 upstream.
The Adaptation Layer Indication parameter contains a fixed 32-bit Adaptation Code Point after its parameter header. However, sctp_verify_param() accepts a header-only parameter because the generic parameter walker only requires the header to be present.
sctp_process_param() then reads adaptation_ind beyond the declared parameter. When the malformed parameter is last in an INIT, the read starts at the receive skb tail, and the value is copied into the state cookie returned in the INIT ACK. This may disclose four receive-buffer tail bytes.
Require the declared parameter length to match the fixed structure size and abort the association through the existing invalid parameter length path otherwise.
--- net/sctp/sm_make_chunk.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/sctp/sm_make_chunk.c +6 -0
@@ -2187,7 +2187,13 @@ static enum sctp_ierror sctp_verify_paracase SCTP_PARAM_HEARTBEAT_INFO :case SCTP_PARAM_UNRECOGNIZED_PARAMETERS :case SCTP_PARAM_ECN_CAPABLE : +break ;case SCTP_PARAM_ADAPTATION_LAYER_IND : +if (ntohs (param.p->length) !=sizeof (*param.aind)) { +sctp_process_inv_paramlength (asoc, param.p, + chunk, err_chunk); + retval =SCTP_IERROR_ABORT ; + }break ;case SCTP_PARAM_SUPPORTED_EXT :
Greg Kroah-Hartman
maintainerPATCH 392/609 audit: fix potential integer overflow in audit_log_n_string()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
commit f865c143629d4094866a811dba5f329250bad486 upstream.
audit_log_n_string() computes new_len as "slen + 3" (enclosing quotes plus the NUL terminator) and stores it into an int, while slen is a size_t. For a sufficiently large slen the addition can overflow and/or the result be truncated when assigned to the int new_len, so the "new_len > avail" check can be bypassed and the subsequent memcpy(ptr, string, slen) can write past the skb tail.
This is the same class of bug that was fixed for the hex sibling in commit 65dfde57d1e2 ("audit: fix potential integer overflow in audit_log_n_hex()"); both helpers are reached through audit_log_n_untrustedstring() with the same length source.
Make new_len a size_t and use check_add_overflow() to catch the overflow, mirroring the audit_log_n_hex() fix. No functional change for the in-tree callers, which all pass bounded lengths.
--- kernel/audit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
kernel/audit.c +9 -2
@@ -2075,7 +2075,8 @@ void audit_log_n_hex(struct audit_buffervoid audit_log_n_string (struct audit_buffer *ab,const char *string,size_t slen) { -int avail, new_len; +int avail; +size_t new_len;unsigned char *ptr;struct sk_buff *skb;
@@ -2085,7 +2086,13 @@ void audit_log_n_string(struct audit_bufBUG_ON (!ab->skb); skb = ab->skb; avail =skb_tailroom (skb); - new_len = slen +3 ;/* enclosing quotes + null terminator */ + +/* enclosing quotes + null terminator */ +if (check_add_overflow (slen,3 , &new_len)) { +audit_log_format (ab,"?" ); +return ; + } +if (new_len > avail) { avail =audit_expand (ab, new_len);if (!avail)
Greg Kroah-Hartman
maintainerPATCH 393/609 audit: fix potential use-after-free in audit_del_rule()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luxiao Xu <[email protected]>
commit 246df90b5f1a8a6e6abbd2f058b029558720adec upstream.
audit_del_rule() destroys e->rule.exe via audit_remove_mark_rule() before unlinking the rule from RCU-visible filter lists and waiting for a grace period. Concurrent readers in audit_filter() and audit_filter_rules() still dereference e->rule.exe, while the fsnotify mark can be freed on an independent lifetime path. This creates a use-after-free window during rule deletion.
Fix this by unlinking the rule from the RCU-visible lists and invoking synchronize_rcu() before calling audit_remove_mark_rule() (and other rule removal helpers). This ensures that all existing RCU readers have exited the critical section before any underlying resources are destroyed.
--- kernel/auditfilter.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
kernel/auditfilter.c +4 -2
@@ -1045,6 +1045,10 @@ int audit_del_rule(struct audit_entry *e goto out; } +list_del_rcu (&e->list); +list_del (&e->rule.list); +synchronize_rcu (); +if (e->rule.watch)audit_remove_watch_rule (&e->rule);
@@ -1062,8 +1066,6 @@ int audit_del_rule(struct audit_entry *e audit_signals--;#endif -list_del_rcu (&e->list); -list_del (&e->rule.list);call_rcu (&e->rcu, audit_free_rule_rcu); out:
Greg Kroah-Hartman
maintainerPATCH 394/609 Bluetooth: HIDP: reject frames without a transaction header
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sangho Lee <[email protected]>
commit 47778d2c2087b5d192398f6fddf692d16a5431cf upstream.
hidp_recv_ctrl_frame() and hidp_recv_intr_frame() read skb->data[0] before checking that the L2CAP SDU contains a transaction header. A connected HIDP peer can send an empty basic-mode SDU and make both paths use an uninitialized byte from skb tailroom.
KMSAN reports the use in hidp_session_run(), with the uninitialized value originating in __alloc_skb() through vhci_write(). The control path produces two reports and the interrupt path produces one.
The byte can also be controlled by a malformed lower-layer packet. If an HCI ACL packet contains an L2CAP PDU with a declared zero-length payload followed by an extra 0x15 byte, l2cap_recv_acldata() reduces skb->len to the declared PDU length before dispatch. The current HIDP path nevertheless consumes the extra byte as HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG and terminates the HIDP session. With this change, the same packet is discarded and a subsequent feature report request succeeds.
Pull the transaction header with skb_pull_data() and discard frames that do not contain it.
--- net/bluetooth/hidp/core.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
net/bluetooth/hidp/core.c +15 -10
@@ -563,16 +563,18 @@ static int hidp_process_data(struct hidpstatic void hidp_recv_ctrl_frame (struct hidp_session *session,struct sk_buff *skb) { -unsigned char hdr, type, param; +unsigned char type, param; +u8 *hdr;int free_skb =1 ;BT_DBG ("session %p skb %p len %u" , session, skb, skb->len); - hdr = skb->data[0 ]; -skb_pull (skb,1 ); + hdr =skb_pull_data (skb,1 ); +if (!hdr) + goto free; - type = hdr &HIDP_HEADER_TRANS_MASK ; - param = hdr &HIDP_HEADER_PARAM_MASK ; + type = *hdr &HIDP_HEADER_TRANS_MASK ; + param = *hdr &HIDP_HEADER_PARAM_MASK ;switch (type) {case HIDP_TRANS_HANDSHAKE :
@@ -593,6 +595,7 @@ static void hidp_recv_ctrl_frame(structbreak ; } +free:if (free_skb)kfree_skb (skb); }
@@ -600,14 +603,15 @@ static void hidp_recv_ctrl_frame(structstatic void hidp_recv_intr_frame (struct hidp_session *session,struct sk_buff *skb) { -unsigned char hdr; +u8 *hdr;BT_DBG ("session %p skb %p len %u" , session, skb, skb->len); - hdr = skb->data[0 ]; -skb_pull (skb,1 ); + hdr =skb_pull_data (skb,1 ); +if (!hdr) + goto free; -if (hdr == (HIDP_TRANS_DATA |HIDP_DATA_RTYPE_INPUT )) { +if (*hdr == (HIDP_TRANS_DATA |HIDP_DATA_RTYPE_INPUT )) {hidp_set_timer (session);if (session->input)
@@ -619,9 +623,10 @@ static void hidp_recv_intr_frame(structBT_DBG ("report len %d" , skb->len); } }else { -BT_DBG ("Unsupported protocol header 0x%02x" , hdr); +BT_DBG ("Unsupported protocol header 0x%02x" , *hdr); } +free:kfree_skb (skb); }