7.1.9-rc1 review
Greg Kroah-Hartman
maintainerPATCH 142/271 mm/vmalloc: acquire init_mm lock on huge vmap to avoid ptdump UAF
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes (ARM) <[email protected]>
commit 26444eb71465c9934d9d418ef69c43f61185329b upstream.
Patch series "mm: fix UAF caused by race between ptdump and vmap pgtable freeing", v6.
Kernel page table walkers fall into two broad categories - those ranges where no exclusion is required via walk_kernel_page_table_range_lockless() and those where exclusion is required via walk_kernel_page_table_range() or walk_page_range_debug().
The former category is used only by arm64 arch code operating on ranges it both wholly owns and does not concurrently write.
The latter category consists of kernel page table walkers operating on ranges that are wholly owned (but which need exclusion against concurrent writers).
The lock used for exclusion is the mmap lock, and for kernel ranges this is the mmap lock on init_mm.
ptdump is a special case being both the only user of walk_page_range_debug(), and the only case in which it walks ranges it does not own.
This presents a problem, as page tables may be freed under ptdump. And indeed there is a use-after-free bug in the kernel as a result, which this series addresses.
vmap promotes page tables to huge leaf entries where possible, freeing the lower page table when it does. It does this with no meaningful locks held against concurrent ptdump walks.
As a result, use-after-free can currently occur. This series addresses the issue by having the vmap huge promotion logic acquire the mmap read lock while both setting the huge page table entry and freeing the prior leaf page table.
The ptdump code already acquires the mmap write lock, so by doing so we ensure that the ptdump walker only ever observes either the huge page table entry or the existing page table entry, and nothing is freed underneath it.
A mitigation for this issue was already applied for arm64 in commit fa93b45fd397 ("arm64: Enable vmalloc-huge with ptdump"), which this series has to deal with carefully.
This mitigation resolves the issue by acquiring the mmap read lock on init_mm on vmap page table free if a ptdump is in progress.
However the fix in this series would cause a deadlock if we were to simply apply it for arm64 without also reverting the change.
This is because vmap may acquire the read lock before ptdump attempts to acquire the write lock, which then gets queued, and rwsem starvation rules mean that the (unacknowledged) nested mmap read lock in the arm64 code would also block, meaning the original read lock is never released and thus deadlock.
This series works around this by #ifndef CONFIG_ARM64'ing the mmap read lock in vmap logic, then partially reverting commit fa93b45fd397 ("arm64:
Enable vmalloc-huge with ptdump"), keeping the enablement of huge vmap support, and removing the ifdeffery with the partial revert patch.
There are related issues that are also addressed in this series:
* x86 page attribute logic, specifically Change Page Attributes (CPA), implements a feature whereby huge ranges can be collapsed into huge leaf entries. This can similarly cause a UAF when done in parallel with a ptdump walk, so similarly acquire the init_mm mmap lock to avoid this.
* The CPA logic allows concurrent page table manipulation and CPA collapse, meaning the former risks accessing a page table the latter frees. Fix this by acquiring mmap write lock on init_mm across the whole CPA collapse operation and read lock on the page table manipulation.
* x86 and arm64 permit walks of non-kernel mm's (both allowing efi mm walks, and in x86's case arbitrary mm's), so we ensure kernel mappings remain stable by locking the init_mm as well as the mm being walked.
The ordering of patches is established for both strict dependencies (the arm64 partial revert in particular has to be done after the vmap changes) and logical ones (the non-kernel mm fix only makes sense once the vmap/CPA fixes are in place).
This patch (of 3):
Currently there is a nasty race between ptdump and vmap when attempting to map a huge P4D, PUD or PMD entry:
* ptdump walks kernel page table ranges it doesn't own.
* When vmap maps ranges it tries to promotes existing ones to huge page tables in vmap_try_huge_[p4d,pud,pmd]() at P4D, PUD and PMD level, freeing the lower page table in [p4d,pud,pmd]_free_[pud,pmd,pte]_page() when it succeeds.
Both of these things can happen at the same time and as a result ptdump can access a freed page table, resulting in a use-after-free and memory corruption.
This is possible because while ptdump_walk_pgd() holds both the mem hotplug lock and the mmap write lock before invoking walk_page_range_debug(), vmap takes no relevant locks at all.
Fix this by holding the mmap read lock in vmap_try_huge_*() when freeing page tables.
The read lock is sufficient: ptdump is the only walker that must be excluded and it holds the mmap write lock. Other holders of the read lock may run concurrently, but each exclusively owns the range it operates on and cannot reach the page tables freed here.
We also hold the lock while assigning the huge page table entry, which means page table walkers observe only the huge or non-huge page table entry.
We use a trylock to prevent ptdump from blocking vmap making forward progress. This is fine because it's an optimisation in any case, and thus the vmap can safely proceed regardless.
All other kernel page table walkers that touch vmalloc ranges either exclusively own the memory walked or acquire the mmap lock, so this correctly excludes those walkers.
One wrinkle here is commit fa93b45fd397 ("arm64: Enable vmalloc-huge with ptdump"), which addresses the issue for arm64 only by explicitly acquiring the mmap read lock on kernel page table freeing should a concurrent ptdump be in progress.
This is problematic as vmap may acquire the mmap read lock prior to ptdump attempting to acquire an mmap write lock, leading to a deadlock when the mmap read lock is slept upon on page table freeing due to rwsem anti-starvation.
We work around this by predicating the mmap lock being taken on !CONFIG_ARM64 for the time being.
With this patch applied, a follow up will partially revert commit fa93b45fd397 ("arm64: Enable vmalloc-huge with ptdump") and at that stage remove the arm64 ifdeffery.
We also update walk_page_range_debug() to assert the mmap write lock unconditionally and update the comment here to reflect this change.
The issue has existed as long as ptdump was available and vmap freed page tables when promoting to a huge leaf entry, that is, since commit b6bdb7517c3d ("mm/vmalloc: add interfaces to free unmapped page table") for huge ioremap, and commit 121e6f3258fe ("mm/vmalloc: hugepage vmalloc mappings") for huge vmalloc.
Since the former is the earlier of the two we choose that for our Fixes tag.
We also define a guard class for mmap_read_trylock() so we can use cleanup.h to make the scope handling cleaner in the implementation.
This patch is based on work by David Carlier (linked), with gratitude!
--- include/linux/mmap_lock.h | 1 mm/pagewalk.c | 22 +++++++++++--------- mm/vmalloc.c | 49 +++++++++++++++++++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 19 deletions(-)
include/linux/mmap_lock.h +1 -0
@@ -621,6 +621,7 @@ static inline void mmap_read_unlock(stru DEFINE_GUARD(mmap_read_lock, struct mm_struct *, mmap_read_lock(_T), mmap_read_unlock(_T)) +DEFINE_GUARD_COND(mmap_read_lock, _try, mmap_read_trylock(_T))static inline void mmap_read_unlock_non_owner (struct mm_struct *mm) {
mm/pagewalk.c +12 -10
@@ -678,6 +678,8 @@ int walk_kernel_page_table_range_lockles* will also not lock the PTEs for the pte_entry() callback. * * This is for debugging purposes ONLY. +* +* The mmap write lock must be held. */ int walk_page_range_debug(struct mm_struct *mm, unsigned long start, unsigned long end, const struct mm_walk_ops *ops,
@@ -691,6 +693,16 @@ int walk_page_range_debug(struct mm_stru .no_vma = true }; +/* +* When walking userland page tables, an mmap write lock must be held to +* account for munmap() downgrading to an mmap read lock when tearing +* down page tables. +* +* When walking kernel page tables, an mmap write lock must also be held +* to account for page table freeing on vmap huge page mapping. +*/ +mmap_assert_write_locked (mm); +/* For convenience, we allow traversal of kernel mappings. */ if (mm == &init_mm) return walk_kernel_page_table_range(start, end, ops,
@@ -700,16 +712,6 @@ int walk_page_range_debug(struct mm_struif (!check_ops_safe (ops))return -EINVAL ; -/* -* The mmap lock protects the page walker from changes to the page -* tables during the walk. However a read lock is insufficient to -* protect those areas which don't have a VMA as munmap() detaches -* the VMAs before downgrading to a read lock and actually tearing -* down PTEs/page tables. In which case, the mmap write lock should -* be held. -*/ -mmap_assert_write_locked (mm); -return walk_pgd_range (start, end, &walk); }
mm/vmalloc.c +40 -9
76 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 143/271 ima: fix out-of-bounds read in xattr_verify()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lincoln Wallace <[email protected]>
commit 5ff232d31106f45ac87c3b64e1d35a0667777797 upstream.
The digest-length check in xattr_verify() mixes int and size_t:
if (xattr_len - sizeof(xattr_value->type) - hash_start >= iint->ima_hash->length)
sizeof() yields size_t, so the usual arithmetic conversions promote the whole left-hand side to unsigned 64-bit before the subtraction runs. For a truncated xattr this underflows instead of going negative:
a 1-byte IMA_XATTR_DIGEST_NG xattr (xattr_len == 1, hash_start == 1) turns "1 - 1 - 1" into SIZE_MAX, which is trivially >= ima_hash->length. The check then passes and the following memcmp() reads iint->ima_hash->length bytes starting past the end of the buffer vfs_getxattr_alloc() allocated for it.
Nothing upstream clamps xattr_len back into a safe range first:ima_get_hash_algo() only special-cases xattr_len < 2 to pick a default algorithm, and evm_verifyxattr() returns INTEGRITY_UNKNOWN rather than failing when no HMAC key is loaded, so a truncated security.ima value reaches the length check as-is.
Rewrite the comparison so every operand stays a signed int and no implicit conversion to size_t can occur.
--- security/integrity/ima/ima_appraise.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
security/integrity/ima/ima_appraise.c +7 -2
@@ -269,8 +269,13 @@ static int xattr_verify(enum ima_hooks f }else {set_bit (IMA_DIGSIG , &iint->atomic_flags); } -if (xattr_len -sizeof (xattr_value->type) - hash_start >= - iint->ima_hash->length) +/* +* Use addition, not subtraction: sizeof() forces unsigned +* math and a short xattr_len would wrap around, bypassing +* this bounds check. +*/ +if (xattr_len >= (int )sizeof (xattr_value->type) + hash_start + + (int )iint->ima_hash->length)/* * xattr length may be longer. md5 hash in previous * version occupied 20 bytes in xattr, instead of 16
Greg Kroah-Hartman
maintainerPATCH 144/271 ipvs: stop estimator after disabled calc phase
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit 558f67f1340f803a346ecd14a69c49653111c5f4 upstream.
IPVS estimator kthread 0 starts with zeroed chain and tick limits until its initial calculation phase completes. If network namespace teardown clears ipvs->enable during that phase, ip_vs_est_calc_phase() can return without installing positive limits.
The kthread can then continue into its main loop and drain est_temp_list with zero chain_max, tick_max and est_max_count values. Each enqueue consumes one available tick row, but est_count never reaches the zero est_max_count value. After all rows are consumed, the row lookup returns IPVS_EST_NTICKS and ip_vs_enqueue_estimator() writes past the ticks and tick_len arrays.
Exit kthread 0 after the calculation phase if the kthread is stopping or IPVS has been disabled. That keeps temporary estimators from being drained after the limits failed to initialize.
Estimator kthreads can now self-exit before teardown or reload stops kd->task. Keep an extra task reference after creation and release it with kthread_stop_put(), so kd->task remains valid until the stop paths consume that reference.
--- net/netfilter/ipvs/ip_vs_est.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
net/netfilter/ipvs/ip_vs_est.c +7 -3
@@ -191,8 +191,11 @@ static int ip_vs_estimation_kthread(void }/* kthread 0 will handle the calc phase */ -if (ipvs->est_calc_phase) +if (ipvs->est_calc_phase) {ip_vs_est_calc_phase (ipvs); +if (kthread_should_stop () || !READ_ONCE (ipvs->enable)) +return 0 ; + } }while (1 ) {
@@ -270,6 +273,7 @@ int ip_vs_est_kthread_start(struct netns kd->task =NULL ; goto out; } +get_task_struct (kd->task);set_user_nice (kd->task,sysctl_est_nice (ipvs));if (sysctl_est_preferred_cpulist (ipvs))
@@ -286,7 +290,7 @@ void ip_vs_est_kthread_stop(struct ip_vs {if (kd->task) {pr_info ("stopping estimator thread %d...\n" , kd->id); -kthread_stop (kd->task); +kthread_stop_put (kd->task); kd->task =NULL ; } }
@@ -526,7 +530,7 @@ static void ip_vs_est_kthread_destroy(stif (kd) {if (kd->task) {pr_info ("stop unused estimator thread %d...\n" , kd->id); -kthread_stop (kd->task); +kthread_stop_put (kd->task); }ip_vs_stats_free (kd->calc_stats);kfree (kd);
Greg Kroah-Hartman
maintainerPATCH 145/271 ipvs: add totalconns for dest
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
commit 04d2feaed8d0103c498727191ba04001d5100e67 upstream.
Replace the inactconns dest counter with totalconns, now inactconns can be obtained from totalconns - activeconns. This reduces the atomic inc/dec ops for TCP/SCTP from 6 to 4 if the connection is established and then closed.
--- include/net/ip_vs.h | 17 ++++++++++++----- net/netfilter/ipvs/ip_vs_conn.c | 24 +++++++----------------- net/netfilter/ipvs/ip_vs_ctl.c | 10 +++++----- net/netfilter/ipvs/ip_vs_lc.c | 4 ++-- net/netfilter/ipvs/ip_vs_proto_sctp.c | 2 -- net/netfilter/ipvs/ip_vs_proto_tcp.c | 2 -- net/netfilter/ipvs/ip_vs_sync.c | 7 ++----- 7 files changed, 28 insertions(+), 38 deletions(-)
include/net/ip_vs.h +12 -5
@@ -979,7 +979,7 @@ struct ip_vs_dest {/* connection counters and thresholds */ atomic_t activeconns;/* active connections */ -atomic_t inactconns;/* inactive connections */ +atomic_t totalconns;/* total connections */ atomic_t persistconns;/* persistent connections */ __u32 u_threshold;/* upper threshold */ __u32 l_threshold;/* lower threshold */
@@ -2198,14 +2198,21 @@ void ip_vs_unregister_hooks(struct netnsstatic inline int ip_vs_dest_conn_overhead (struct ip_vs_dest *dest) { -/* We think the overhead of processing active connections is 256 +/* We think the overhead of processing active connections is 257 * times higher than that of inactive connections in average. (This -* 256 times might not be accurate, we will change it later) We +* 257 times might not be accurate, we will change it later) We * use the following formula to estimate the overhead now: -* dest->activeconns*256 + dest->inactconns +* dest->activeconns*256 + dest->totalconns */ return (atomic_read (&dest->activeconns) <<8 ) + -atomic_read (&dest->inactconns); +atomic_read (&dest->totalconns); +} + +static inline int +ip_vs_dest_inactconns (const struct ip_vs_dest *dest) +{ +return max (atomic_read (&dest->totalconns) - +atomic_read (&dest->activeconns),0 ); }#ifdef CONFIG_IP_VS_PROTO_TCP
net/netfilter/ipvs/ip_vs_conn.c +7 -17
@@ -1081,12 +1081,6 @@ static inline void ip_vs_bind_xmit_v6(st#endif -static inline int ip_vs_dest_totalconns (struct ip_vs_dest *dest) -{ -return atomic_read (&dest->activeconns) - +atomic_read (&dest->inactconns); -} -/* * Bind a connection entry with a virtual service destination * Called just after a new connection entry is created.
@@ -1153,8 +1147,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, s*/ if (!(flags &IP_VS_CONN_F_INACTIVE ))atomic_inc (&dest->activeconns); -else -atomic_inc (&dest->inactconns); +atomic_inc (&dest->totalconns); }else {/* It is a persistent connection/template, so increase the persistent connection counter */
@@ -1162,7 +1155,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, s }if (dest->u_threshold !=0 && -ip_vs_dest_totalconns (dest) >= dest->u_threshold) +atomic_read (&dest->totalconns) >= dest->u_threshold) dest->flags |=IP_VS_DEST_F_OVERLOAD ; }
@@ -1244,13 +1237,10 @@ static inline void ip_vs_unbind_dest(str/* Update the connection counters */ if (!(cp->flags &IP_VS_CONN_F_TEMPLATE )) { -/* It is a normal connection, so decrease the inactconns -or activeconns counter */ -if (cp->flags &IP_VS_CONN_F_INACTIVE ) { -atomic_dec (&dest->inactconns); - }else { +/* It is a normal connection, so decrease the counters */ +if (!(cp->flags &IP_VS_CONN_F_INACTIVE ))atomic_dec (&dest->activeconns); - } +atomic_dec (&dest->totalconns); }else {/* It is a persistent connection/template, so decrease the persistent connection counter */
@@ -1258,10 +1248,10 @@ static inline void ip_vs_unbind_dest(str }if (dest->l_threshold !=0 ) { -if (ip_vs_dest_totalconns (dest) < dest->l_threshold) +if (atomic_read (&dest->totalconns) < dest->l_threshold) dest->flags &= ~IP_VS_DEST_F_OVERLOAD ; }else if (dest->u_threshold !=0 ) { -if (ip_vs_dest_totalconns (dest) *4 < dest->u_threshold *3 ) +if (atomic_read (&dest->totalconns) *4 < dest->u_threshold *3 ) dest->flags &= ~IP_VS_DEST_F_OVERLOAD ; }else {if (dest->flags &IP_VS_DEST_F_OVERLOAD )
net/netfilter/ipvs/ip_vs_ctl.c +5 -5
@@ -1445,7 +1445,7 @@ ip_vs_new_dest(struct ip_vs_service *svc dest->port = udest->port;atomic_set (&dest->activeconns,0 ); -atomic_set (&dest->inactconns,0 ); +atomic_set (&dest->totalconns,0 );atomic_set (&dest->persistconns,0 );refcount_set (&dest->refcnt,1 );
@@ -2986,7 +2986,7 @@ static int ip_vs_info_seq_show(struct seip_vs_fwd_name (atomic_read (&dest->conn_flags)),atomic_read (&dest->weight),atomic_read (&dest->activeconns), - atomic_read(&dest->inactconns)); + ip_vs_dest_inactconns(dest)); else #endif seq_printf(seq,
@@ -2997,7 +2997,7 @@ static int ip_vs_info_seq_show(struct seip_vs_fwd_name (atomic_read (&dest->conn_flags)),atomic_read (&dest->weight),atomic_read (&dest->activeconns), - atomic_read(&dest->inactconns)); + ip_vs_dest_inactconns(dest)); } }
@@ -3625,7 +3625,7 @@ __ip_vs_get_dest_entries(struct netns_ip entry.u_threshold = dest->u_threshold; entry.l_threshold = dest->l_threshold; entry.activeconns =atomic_read (&dest->activeconns); - entry.inactconns =atomic_read (&dest->inactconns); + entry.inactconns =ip_vs_dest_inactconns (dest); entry.persistconns =atomic_read (&dest->persistconns);ip_vs_copy_stats (&kstats, &dest->stats);ip_vs_export_stats_user (&entry.stats, &kstats);
@@ -4237,7 +4237,7 @@ static int ip_vs_genl_fill_dest(struct snla_put_u32 (skb,IPVS_DEST_ATTR_ACTIVE_CONNS ,atomic_read (&dest->activeconns)) ||nla_put_u32 (skb,IPVS_DEST_ATTR_INACT_CONNS , -atomic_read (&dest->inactconns)) || +ip_vs_dest_inactconns (dest)) ||nla_put_u32 (skb,IPVS_DEST_ATTR_PERSIST_CONNS ,atomic_read (&dest->persistconns)) || nla_put_u16(skb, IPVS_DEST_ATTR_ADDR_FAMILY, dest->af))
net/netfilter/ipvs/ip_vs_lc.c +2 -2
@@ -30,7 +30,7 @@ ip_vs_lc_schedule(struct ip_vs_service */* * Simply select the server with the least number of -* (activeconns<<5) + inactconns +* (activeconns*256) + totalconns * Except whose weight is equal to zero. * If the weight is equal to zero, it means that the server is * quiesced, the existing connections to the server still get
@@ -56,7 +56,7 @@ ip_vs_lc_schedule(struct ip_vs_service *IP_VS_DBG_ADDR (least->af, &least->addr),ntohs (least->port),atomic_read (&least->activeconns), - atomic_read(&least->inactconns)); + ip_vs_dest_inactconns(least));return least; }
net/netfilter/ipvs/ip_vs_proto_sctp.c +0 -2
@@ -446,12 +446,10 @@ set_sctp_state(struct ip_vs_proto_data *if (!(cp->flags &IP_VS_CONN_F_INACTIVE ) && (next_state !=IP_VS_SCTP_S_ESTABLISHED )) {atomic_dec (&dest->activeconns); -atomic_inc (&dest->inactconns); cp->flags |=IP_VS_CONN_F_INACTIVE ; }else if ((cp->flags &IP_VS_CONN_F_INACTIVE ) && (next_state ==IP_VS_SCTP_S_ESTABLISHED )) {atomic_inc (&dest->activeconns); -atomic_dec (&dest->inactconns); cp->flags &= ~IP_VS_CONN_F_INACTIVE ; } }
net/netfilter/ipvs/ip_vs_proto_tcp.c +0 -2
@@ -526,12 +526,10 @@ set_tcp_state(struct ip_vs_proto_data *pif (!(cp->flags &IP_VS_CONN_F_INACTIVE ) && !tcp_state_active (new_state)) {atomic_dec (&dest->activeconns); -atomic_inc (&dest->inactconns); cp->flags |=IP_VS_CONN_F_INACTIVE ; }else if ((cp->flags &IP_VS_CONN_F_INACTIVE ) &&tcp_state_active (new_state)) {atomic_inc (&dest->activeconns); -atomic_dec (&dest->inactconns); cp->flags &= ~IP_VS_CONN_F_INACTIVE ; } }
net/netfilter/ipvs/ip_vs_sync.c +2 -5
@@ -878,13 +878,10 @@ static void ip_vs_proc_conn(struct netnsspin_lock_bh (&cp->lock);if ((cp->flags ^ flags) &IP_VS_CONN_F_INACTIVE && !(flags &IP_VS_CONN_F_TEMPLATE ) && dest) { -if (flags &IP_VS_CONN_F_INACTIVE ) { +if (flags &IP_VS_CONN_F_INACTIVE )atomic_dec (&dest->activeconns); -atomic_inc (&dest->inactconns); - }else { +else atomic_inc (&dest->activeconns); -atomic_dec (&dest->inactconns); - } } flags &=IP_VS_CONN_F_BACKUP_UPD_MASK ; flags |= cp->flags & ~IP_VS_CONN_F_BACKUP_UPD_MASK ;
Greg Kroah-Hartman
maintainerPATCH 146/271 ipvs: properly update the overload flag on dest edit
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
commit 8f843441c4e7eae8ea83491e8c203c2b192edcf5 upstream.
The upper/lower connection thresholds for dest can be changed, so use ip_vs_dest_update_overload() to properly update the dest overload flag.
The thresholds were not limited, fit them in the 0 .. INT_MAX range as already done in ipvsadm.
As the thresholds are also read when connections are created and expired, use WRITE_ONCE/READ_ONCE to access them.
As the lower threshold is optional, use (u - (u >> 2)) to calculate the 75% default value based on the upper threshold by preserving the integer rounding, as suggested by Yizhou Zhao.
Trigger flag update when totalconns reaches one of the thresholds and use dst_lock to serialize the updating.
--- include/net/ip_vs.h | 3 + net/netfilter/ipvs/ip_vs_conn.c | 27 +++++----------- net/netfilter/ipvs/ip_vs_ctl.c | 67 +++++++++++++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 25 deletions(-)
include/net/ip_vs.h +3 -0
@@ -983,6 +983,7 @@ struct ip_vs_dest {atomic_t persistconns;/* persistent connections */ __u32 u_threshold;/* upper threshold */ __u32 l_threshold;/* lower threshold */ +__u32 l_threshold_val;/* used lower threshold */ /* for destination cache */ spinlock_t dst_lock;/* lock of dst_cache */
@@ -1885,6 +1886,8 @@ static inline void ip_vs_dest_put_and_frkfree (dest); } +void ip_vs_dest_update_overload (struct ip_vs_dest *dest,int mode); +/* IPVS sync daemon data and function prototypes * (from ip_vs_sync.c) */
net/netfilter/ipvs/ip_vs_conn.c +10 -17
@@ -1141,22 +1141,22 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, s/* Update the connection counters */ if (!(flags &IP_VS_CONN_F_TEMPLATE )) { +int tc; +/* It is a normal connection, so modify the counters * according to the flags, later the protocol can * update them on state change */ if (!(flags &IP_VS_CONN_F_INACTIVE ))atomic_inc (&dest->activeconns); -atomic_inc (&dest->totalconns); + tc =atomic_inc_return (&dest->totalconns); +if (tc ==READ_ONCE (dest->u_threshold)) +ip_vs_dest_update_overload (dest,1 ); }else {/* It is a persistent connection/template, so increase the persistent connection counter */ atomic_inc (&dest->persistconns); } - -if (dest->u_threshold !=0 && -atomic_read (&dest->totalconns) >= dest->u_threshold) - dest->flags |=IP_VS_DEST_F_OVERLOAD ; }
@@ -1237,27 +1237,20 @@ static inline void ip_vs_unbind_dest(str/* Update the connection counters */ if (!(cp->flags &IP_VS_CONN_F_TEMPLATE )) { +int tc; +/* It is a normal connection, so decrease the counters */ if (!(cp->flags &IP_VS_CONN_F_INACTIVE ))atomic_dec (&dest->activeconns); -atomic_dec (&dest->totalconns); + tc =atomic_fetch_dec (&dest->totalconns); +if (tc ==READ_ONCE (dest->l_threshold_val)) +ip_vs_dest_update_overload (dest,-1 ); }else {/* It is a persistent connection/template, so decrease the persistent connection counter */ atomic_dec (&dest->persistconns); } -if (dest->l_threshold !=0 ) { -if (atomic_read (&dest->totalconns) < dest->l_threshold) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD ; - }else if (dest->u_threshold !=0 ) { -if (atomic_read (&dest->totalconns) *4 < dest->u_threshold *3 ) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD ; - }else { -if (dest->flags &IP_VS_DEST_F_OVERLOAD ) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD ; - } -ip_vs_dest_put (dest); }
net/netfilter/ipvs/ip_vs_ctl.c +59 -8
103 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 147/271 ipvs: separate destination availability state
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
commit cdcc4e46180df8161f4d2f3c6fd6beaf6990133d upstream.
IPVS configuration paths update destination availability while connection accounting updates destination overload state. The two independent states share dest->flags, so their read-modify-write updates can race and lose one another.
Keep OVERLOAD in flags, where the preceding patch serializes its updates with dst_lock, and move AVAILABLE to cflags. This keeps configuration- controlled availability out of the scheduler hot cacheline until a scheduler needs to check it. It also prevents availability updates from clobbering overload state.
The destination status bits are not exposed through the IPVS sockopt or netlink interfaces, so keep their definitions in the internal IPVS header.
Readers can still observe stale destination state; this does not provide a cross-field snapshot.
--- include/net/ip_vs.h | 7 +++++++ include/uapi/linux/ip_vs.h | 6 ------ net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- net/netfilter/ipvs/ip_vs_core.c | 6 +++--- net/netfilter/ipvs/ip_vs_ctl.c | 4 ++-- net/netfilter/ipvs/ip_vs_dh.c | 4 ++-- net/netfilter/ipvs/ip_vs_lblc.c | 2 +- net/netfilter/ipvs/ip_vs_lblcr.c | 8 ++++---- net/netfilter/ipvs/ip_vs_xmit.c | 4 ++-- 9 files changed, 23 insertions(+), 22 deletions(-)
include/net/ip_vs.h +7 -0
@@ -36,6 +36,12 @@#define IP_VS_HDR_INVERSE 1#define IP_VS_HDR_ICMP 2 +/* Destination Server Flags */ +#define IP_VS_DEST_F_OVERLOAD 0x0002/* server is overloaded */ + +/* Destination Server Config Flags */ +#define IP_VS_DEST_CF_AVAILABLE 0x0001/* server is available */ +/* conn_tab limits (as per Kconfig) */ #define IP_VS_CONN_TAB_MIN_BITS 8#if BITS_PER_LONG >32
@@ -968,6 +974,7 @@ struct ip_vs_dest {volatile unsigned int flags;/* dest status flags */ atomic_t conn_flags;/* flags to copy to conn */ atomic_t weight;/* server weight */ +unsigned long cflags;/* config flags */ atomic_t last_weight;/* server latest weight */ __u16 tun_type;/* tunnel type */ __be16 tun_port;/* tunnel port */
include/uapi/linux/ip_vs.h +0 -6
@@ -29,12 +29,6 @@#define IP_VS_SVC_F_SCHED_SH_PORT IP_VS_SVC_F_SCHED2/* SH use port */ /* -* Destination Server Flags -*/ -#define IP_VS_DEST_F_AVAILABLE 0x0001/* server is available */ -#define IP_VS_DEST_F_OVERLOAD 0x0002/* server is overloaded */ - -/* * IPVS sync daemon states */ #define IP_VS_STATE_NONE 0x0000/* daemon is stopped */
net/netfilter/ipvs/ip_vs_conn.c +2 -2
@@ -1279,7 +1279,7 @@ int ip_vs_check_template(struct ip_vs_co* Checking the dest server status. */ if ((dest ==NULL ) || - !(dest->flags &IP_VS_DEST_F_AVAILABLE ) || + !(dest->cflags &IP_VS_DEST_CF_AVAILABLE ) ||expire_quiescent_template (ipvs, dest) || (cdest && (dest != cdest))) { IP_VS_DBG_BUF(9, "check_template: dest not available for "
@@ -2012,7 +2012,7 @@ repeat: cp =ip_vs_hn0_to_conn (hn); resched_score++; dest = cp->dest; -if (!dest || (dest->flags &IP_VS_DEST_F_AVAILABLE )) +if (!dest || (dest->cflags &IP_VS_DEST_CF_AVAILABLE ))continue ;if (atomic_read (&cp->n_control))
net/netfilter/ipvs/ip_vs_core.c +3 -3
@@ -302,7 +302,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, ststruct ip_vs_dest *dest = cp->dest;struct netns_ipvs *ipvs = cp->ipvs; -if (dest && (dest->flags &IP_VS_DEST_F_AVAILABLE )) { +if (dest && (dest->cflags &IP_VS_DEST_CF_AVAILABLE )) {struct ip_vs_cpu_stats *s;struct ip_vs_service *svc;
@@ -338,7 +338,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, sstruct ip_vs_dest *dest = cp->dest;struct netns_ipvs *ipvs = cp->ipvs; -if (dest && (dest->flags &IP_VS_DEST_F_AVAILABLE )) { +if (dest && (dest->cflags &IP_VS_DEST_CF_AVAILABLE )) {struct ip_vs_cpu_stats *s;struct ip_vs_service *svc;
@@ -2210,7 +2210,7 @@ ip_vs_in_hook(void *priv, struct sk_buff }/* Check the server status */ -if (cp && cp->dest && !(cp->dest->flags &IP_VS_DEST_F_AVAILABLE )) { +if (cp && cp->dest && !(cp->dest->cflags &IP_VS_DEST_CF_AVAILABLE )) {/* the destination server is not available */ if (sysctl_expire_nodest_conn (ipvs)) {bool old_ct =ip_vs_conn_uses_old_conntrack (cp, skb);
net/netfilter/ipvs/ip_vs_ctl.c +2 -2
@@ -1402,7 +1402,7 @@ __ip_vs_update_dest(struct ip_vs_service }/* set the dest status flags */ - dest->flags |=IP_VS_DEST_F_AVAILABLE ; + dest->cflags |=IP_VS_DEST_CF_AVAILABLE ;if (READ_ONCE (dest->u_threshold) != udest->u_threshold ||READ_ONCE (dest->l_threshold) != udest->l_threshold) {
@@ -1662,7 +1662,7 @@ static void __ip_vs_unlink_dest(struct i struct ip_vs_dest *dest, int svcupd) { - dest->flags &= ~IP_VS_DEST_F_AVAILABLE ; + dest->cflags &= ~IP_VS_DEST_CF_AVAILABLE ;spin_lock_bh (&dest->dst_lock);__ip_vs_dst_cache_reset (dest);
net/netfilter/ipvs/ip_vs_dh.c +2 -2
@@ -219,8 +219,8 @@ ip_vs_dh_schedule(struct ip_vs_service * s = (struct ip_vs_dh_state *) svc->sched_data; dest =ip_vs_dh_get (svc->af, s, &iph->daddr); -if (!dest - || !(dest->flags &IP_VS_DEST_F_AVAILABLE ) +if (!dest || + !(dest->cflags &IP_VS_DEST_CF_AVAILABLE ) ||atomic_read (&dest->weight) <=0 ||is_overloaded (dest)) {ip_vs_scheduler_err (svc,"no destination available" );
net/netfilter/ipvs/ip_vs_lblc.c +1 -1
@@ -502,7 +502,7 @@ ip_vs_lblc_schedule(struct ip_vs_service*/ dest = en->dest; -if ((dest->flags &IP_VS_DEST_F_AVAILABLE ) && +if ((dest->cflags &IP_VS_DEST_CF_AVAILABLE ) &&atomic_read (&dest->weight) >0 && !is_overloaded (dest, svc)) goto out; }
net/netfilter/ipvs/ip_vs_lblcr.c +4 -4
@@ -169,8 +169,8 @@ static inline struct ip_vs_dest *ip_vs_dif (least->flags &IP_VS_DEST_F_OVERLOAD )continue ; -if ((atomic_read (&least->weight) >0 ) - && (least->flags &IP_VS_DEST_F_AVAILABLE )) { +if ((atomic_read (&least->weight) >0 ) && + (least->cflags &IP_VS_DEST_CF_AVAILABLE )) { loh =ip_vs_dest_conn_overhead (least); goto nextstage; }
@@ -186,8 +186,8 @@ static inline struct ip_vs_dest *ip_vs_d doh =ip_vs_dest_conn_overhead (dest);if (((__s64 )loh *atomic_read (&dest->weight) > - (__s64 )doh *atomic_read (&least->weight)) - && (dest->flags &IP_VS_DEST_F_AVAILABLE )) { + (__s64 )doh *atomic_read (&least->weight)) && + (dest->cflags &IP_VS_DEST_CF_AVAILABLE )) { least = dest; loh = doh; }
net/netfilter/ipvs/ip_vs_xmit.c +2 -2
@@ -351,7 +351,7 @@ __ip_vs_get_out_rt(struct netns_ipvs *ip* stored in dest_trash. */ if (!rt_dev_is_down (dst_dev_rcu (&rt->dst)) && - dest->flags &IP_VS_DEST_F_AVAILABLE ) + dest->cflags &IP_VS_DEST_CF_AVAILABLE )__ip_vs_dst_set (dest, dest_dst, &rt->dst,0 );else noref =0 ;
@@ -530,7 +530,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs* stored in dest_trash. */ if (!rt_dev_is_down (dst_dev_rcu (&rt->dst)) && - dest->flags &IP_VS_DEST_F_AVAILABLE ) + dest->cflags &IP_VS_DEST_CF_AVAILABLE )__ip_vs_dst_set (dest, dest_dst, &rt->dst, cookie);else noref =0 ;
Greg Kroah-Hartman
maintainerPATCH 148/271 ipvs: clear IPv4 options after rebasing tunnel ICMP errors
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
commit e0ba936287dfe9783426aac27e5fd76fe35b38c9 upstream.
ip_vs_in_icmp() rebases an skb from the outer ICMP packet to the quoted original request before passing it to icmp_send(). However, IPCB(skb)->opt still describes the outer IPv4 header.
A timestamp option in the outer header can therefore leave an offset that points into the quoted transport header after the rebase. __ip_options_echo() treats a byte at that stale location as the option length and copies it into the fixed-size option storage on the __icmp_send() stack, causing a stack out-of-bounds write.
Clear the stale option metadata after resetting the network header. Keep the remaining control block fields, including the ingress interface used by the ICMP response path.
--- net/netfilter/ipvs/ip_vs_core.c | 1 + 1 file changed, 1 insertion(+)
net/netfilter/ipvs/ip_vs_core.c +1 -0
@@ -1950,6 +1950,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, sif (pskb_pull (skb, offset2) ==NULL ) goto ignore_tunnel;skb_reset_network_header (skb); +memset (&(IPCB (skb)->opt),0 ,sizeof (IPCB (skb)->opt));/* Ensure the IP header is present in headroom */ if (!pskb_may_pull (skb, hlen_orig)) goto ignore_tunnel;
Greg Kroah-Hartman
maintainerPATCH 149/271 packet: use consistent hard_header_len in non-ring send paths
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qihang Tang <[email protected]>
commit 03390aa32e669cc4ecd7d34108e2e1afc13d689d upstream.
packet_snd() reads dev->hard_header_len multiple times while allocating and constructing an skb. Device reconfiguration can change this value concurrently, for example through bonding device type changes.
For SOCK_RAW, packet_snd() can save a larger value in reserve and later allocate headroom using a smaller value. Moving skb->data back by reserve then places it before skb->head, and the following copy from userspace can attempt an out-of-bounds write.
packet_sendmsg_spkt() has the same issue because it calculates its reservation and header offset from separate reads before dropping the RCU read lock to allocate the skb.
Add LL_RESERVED_SPACE_EX() for callers that already saved a header length. Read hard_header_len once in packet_snd() and use it for allocation and construction. In packet_sendmsg_spkt(), preserve the allocation-time value through the device lookup retry.
The separate SOCK_DGRAM consistency problem between hard_header_len and header_ops->create is not addressed here.
--- include/linux/netdevice.h | 6 ++++-- net/packet/af_packet.c | 26 ++++++++++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-)
include/linux/netdevice.h +4 -2
@@ -300,9 +300,11 @@ struct hh_cache {* We could use other alignment values, but we must maintain the * relationship HH alignment <= LL alignment. */ -#define LL_RESERVED_SPACE (dev) \ - ((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom)) \ +#define LL_RESERVED_SPACE_EX (dev, hlen) \ + ((((hlen) + READ_ONCE((dev)->needed_headroom)) \ & ~(HH_DATA_MOD - 1)) + HH_DATA_MOD) +#define LL_RESERVED_SPACE (dev) \ + LL_RESERVED_SPACE_EX(dev, (dev)->hard_header_len)#define LL_RESERVED_SPACE_EXTRA (dev,extra) \ ((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom) + (extra)) \ & ~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
net/packet/af_packet.c +16 -10
@@ -1953,8 +1953,9 @@ static int packet_sendmsg_spkt(struct sostruct net_device *dev;struct sockcm_cookie sockc;__be16 proto =0 ; -int err; +int hard_header_len;int extra_len =0 ; +int err;/* * Get and verify the address.
@@ -1997,14 +1998,18 @@ retry: extra_len =4 ;/* We're doing our own CRC */ } +/* Keep the allocation-time header length across retry. */ +if (!skb) + hard_header_len =READ_ONCE (dev->hard_header_len); + err = -EMSGSIZE ; -if (len > dev->mtu + dev->hard_header_len +VLAN_HLEN + extra_len) +if (len > dev->mtu + hard_header_len +VLAN_HLEN + extra_len) goto out_unlock;if (!skb) { -size_t reserved =LL_RESERVED_SPACE (dev); +size_t reserved =LL_RESERVED_SPACE_EX (dev, hard_header_len);int tlen = dev->needed_tailroom; -unsigned int hhlen = dev->header_ops ? dev->hard_header_len :0 ; +unsigned int hhlen = dev->header_ops ? hard_header_len :0 ;rcu_read_unlock (); skb =sock_wmalloc (sk, len + reserved + tlen,0 ,GFP_KERNEL );
@@ -2034,7 +2039,7 @@ retry: err = -EINVAL ; goto out_unlock; } -if (len > (dev->mtu + dev->hard_header_len + extra_len) && +if (len > (dev->mtu + hard_header_len + extra_len) && !packet_extra_vlan_len_allowed (dev, skb)) { err = -EMSGSIZE ; goto out_unlock;
@@ -2956,7 +2961,7 @@ static int packet_snd(struct socket *socint offset =0 ;struct packet_sock *po =pkt_sk (sk);int vnet_hdr_sz =READ_ONCE (po->vnet_hdr_sz); -int hlen, tlen, linear; +int hard_header_len, hlen, tlen, linear;int extra_len =0 ;/*
@@ -2996,8 +3001,9 @@ static int packet_snd(struct socket *soc goto out_unlock; } + hard_header_len =READ_ONCE (dev->hard_header_len);if (sock->type ==SOCK_RAW ) - reserve = dev->hard_header_len; + reserve = hard_header_len;if (vnet_hdr_sz) { err =packet_snd_vnet_parse (msg, &len, &vnet_hdr, vnet_hdr_sz);if (err)
@@ -3018,10 +3024,10 @@ static int packet_snd(struct socket *soc goto out_unlock; err = -ENOBUFS ; - hlen =LL_RESERVED_SPACE (dev); + hlen =LL_RESERVED_SPACE_EX (dev, hard_header_len); tlen = dev->needed_tailroom; linear =__virtio16_to_cpu (vio_le (), vnet_hdr.hdr_len); - linear =max (linear,min_t (int, len, dev->hard_header_len)); + linear =max (linear,min_t (int, len, hard_header_len)); skb =packet_alloc_skb (sk, hlen + tlen, hlen, len, linear, msg->msg_flags &MSG_DONTWAIT , &err);if (skb ==NULL )
@@ -3037,7 +3043,7 @@ static int packet_snd(struct socket *soc }else if (reserve) {skb_reserve (skb, -reserve);if (len < reserve +sizeof (struct ipv6hdr ) && - dev->min_header_len != dev->hard_header_len) + dev->min_header_len != hard_header_len)skb_reset_network_header (skb); }
Greg Kroah-Hartman
maintainerPATCH 150/271 packet: use consistent hard_header_len in TX_RING send path
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qihang Tang <[email protected]>
commit 21b5953e7494c16a42e6cd8cf110e18d13ae4a6b upstream.
tpacket_snd() reads dev->hard_header_len independently for skb allocation and header construction in tpacket_fill_skb(). Concurrent netdevice reconfiguration can therefore make the reserved headroom smaller than the amount later pushed, or make copylen - hard_header_len negative.
Snapshot hard_header_len once before processing ring frames and use it for the frame limit, headroom allocation, copy length, and skb construction. Pass the snapshot to tpacket_fill_skb().
The separate SOCK_DGRAM consistency problem between hard_header_len and header_ops->create is not addressed here.
--- net/packet/af_packet.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
net/packet/af_packet.c +11 -8
@@ -2574,6 +2574,7 @@ static int packet_snd_vnet_parse(structstatic int tpacket_fill_skb (struct packet_sock *po,struct sk_buff *skb,void *frame,struct net_device *dev,void *data,int tp_len,__be16 proto,unsigned char *addr,int hlen,int copylen, +int hard_header_len,const struct sockcm_cookie *sockc) {union tpacket_uhdr ph;
@@ -2605,8 +2606,8 @@ static int tpacket_fill_skb(struct packe }else if (copylen) {int hdrlen =min_t (int, copylen, tp_len); -skb_push (skb, dev->hard_header_len); -skb_put (skb, copylen - dev->hard_header_len); +skb_push (skb, hard_header_len); +skb_put (skb, copylen - hard_header_len); err =skb_store_bits (skb,0 , data, hdrlen);if (unlikely (err))return err;
@@ -2737,7 +2738,7 @@ static int tpacket_snd(struct packet_socvoid *data;int len_sum =0 ;int status =TP_STATUS_AVAILABLE ; -int hlen, tlen, copylen =0 ; +int hard_header_len, hlen, tlen, copylen =0 ;long timeo;mutex_lock (&po->pg_vec_lock);
@@ -2784,8 +2785,9 @@ static int tpacket_snd(struct packet_soc goto out_put; } + hard_header_len =READ_ONCE (dev->hard_header_len);if (po->sk.sk_socket->type ==SOCK_RAW ) - reserve = dev->hard_header_len; + reserve = hard_header_len; size_max = po->tx_ring.frame_size - (po->tp_hdrlen -sizeof (struct sockaddr_ll ));
@@ -2822,7 +2824,7 @@ static int tpacket_snd(struct packet_soc goto tpacket_error; status =TP_STATUS_SEND_REQUEST ; - hlen =LL_RESERVED_SPACE (dev); + hlen =LL_RESERVED_SPACE_EX (dev, hard_header_len); tlen = dev->needed_tailroom;if (vnet_hdr_sz) { data += vnet_hdr_sz;
@@ -2840,10 +2842,10 @@ static int tpacket_snd(struct packet_soc vnet_hdr.hdr_len); has_vnet_hdr = true; } - copylen =max_t (int, copylen, dev->hard_header_len); + copylen =max_t (int, copylen, hard_header_len); skb =sock_alloc_send_skb (&po->sk, hlen + tlen +sizeof (struct sockaddr_ll ) + - (copylen - dev->hard_header_len), + (copylen - hard_header_len), !need_wait, &err);if (unlikely (skb ==NULL )) {
@@ -2853,7 +2855,8 @@ static int tpacket_snd(struct packet_soc goto out_status; } tp_len =tpacket_fill_skb (po, skb, ph, dev, data, tp_len, proto, - addr, hlen, copylen, &sockc); + addr, hlen, copylen, hard_header_len, + &sockc); if (likely(tp_len >= 0) && tp_len > dev->mtu + reserve && !vnet_hdr_sz &&
Greg Kroah-Hartman
maintainerPATCH 151/271 net/packet: reset the MAC header on the packet-socket transmit path
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit c2707480cfbf19c7619acc9c089d17f20869821f upstream.
packet_parse_headers() resets the MAC header only for a SOCK_RAW frame whose socket did not bind a protocol. A protocol-bound SOCK_RAW socket, any SOCK_DGRAM frame, and the legacy SOCK_PACKET path therefore leave skb->mac_header unset here.
For frames sent via __dev_queue_xmit() this is harmless: it resets the MAC header unconditionally. But the packet-socket PACKET_QDISC_BYPASS path uses dev_direct_xmit(), which does not, so the frame reaches ndo_start_xmit() with the MAC header unset. A driver that reads eth_hdr(skb) on transmit then dereferences skb->head + (u16)~0, an out-of-bounds access ~64 KiB past the head -- the same class fixed for one consumer in commit f5089008f90c ("macsec: do not read an unset MAC header in macsec_encrypt()").
packet_parse_headers() runs only on the transmit path, where skb->data points at the start of the L2 header for every packet-socket type regardless of its length: SOCK_RAW and SOCK_PACKET carry a user-supplied header and SOCK_DGRAM has one built by dev_hard_header(). Reset the MAC header unconditionally, mirroring __dev_queue_xmit(), so the frame is anchored on the bypass path too.
Found by 0sec (https://0sec.ai) using automated source analysis; verified against source and matched to the macsec KASAN report in f5089008f90c. Compile-tested.
--- net/packet/af_packet.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
net/packet/af_packet.c +4 -3
@@ -1924,11 +1924,12 @@ static void packet_parse_headers(struct {int depth; +/* On TX skb->data is the L2 header; anchor it for all socket types. */ +skb_reset_mac_header (skb); +if ((!skb->protocol || skb->protocol ==htons (ETH_P_ALL )) && - sock->type ==SOCK_RAW ) { -skb_reset_mac_header (skb); + sock->type ==SOCK_RAW ) skb->protocol =dev_parse_header_protocol (skb); - }/* Move network header to the right position for VLAN tagged packets */ if (likely(skb->dev->type == ARPHRD_ETHER) &&
Greg Kroah-Hartman
maintainerPATCH 152/271 packet: synchronize pressure clearing with ring reconfiguration
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
commit 1a35da325cac4d5bcad76a2aa943408a6f1d9000 upstream.
packet_set_ring() updates the RX ring state under sk_receive_queue.lock, but used to publish the tpacket receive mode through po->prot_hook.func after releasing that lock. packet_poll() and packet_recvmsg() can then run the pressure clearing path after the ring has been cleared while still seeing tpacket_rcv, causing __packet_rcv_has_room() to dereference stale or NULL ring storage.
Move the existing receive hook assignment into the same sk_receive_queue.lock section as the ring state update. Keep the assignment otherwise unchanged, including on TX ring reconfiguration, to avoid adding behavior changes that are not required for the fix.
Serialize packet_recvmsg() pressure clearing with the same queue lock only after PACKET_SOCK_PRESSURE has been observed. If the flag is clear and the socket has moved away from tpacket_rcv, packet_set_ring() has already detached the socket and waited for synchronize_net(), so no new packet input can set the flag again.
packet_poll() already holds sk_receive_queue.lock, so it uses the new unlocked helper directly.
--- net/packet/af_packet.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
net/packet/af_packet.c +16 -4
@@ -1315,13 +1315,25 @@ static int packet_rcv_has_room(struct pareturn ret; } -static void packet_rcv_try_clear_pressure (struct packet_sock *po) +static void __packet_rcv_try_clear_pressure (struct packet_sock *po) {if (packet_sock_flag (po,PACKET_SOCK_PRESSURE ) &&__packet_rcv_has_room (po,NULL ) ==ROOM_NORMAL )packet_sock_flag_set (po,PACKET_SOCK_PRESSURE , false); } +static void packet_rcv_try_clear_pressure (struct packet_sock *po) +{ +struct sock *sk = &po->sk; + +if (!packet_sock_flag (po,PACKET_SOCK_PRESSURE )) +return ; + +spin_lock_bh (&sk->sk_receive_queue.lock); +__packet_rcv_try_clear_pressure (po); +spin_unlock_bh (&sk->sk_receive_queue.lock); +} +static void packet_sock_destruct (struct sock *sk) {skb_queue_purge (&sk->sk_error_queue);
@@ -4314,7 +4326,7 @@ static __poll_t packet_poll(struct file TP_STATUS_KERNEL)) mask |= EPOLLIN | EPOLLRDNORM; } -packet_rcv_try_clear_pressure (po); +__packet_rcv_try_clear_pressure (po);spin_unlock_bh (&sk->sk_receive_queue.lock);spin_lock_bh (&sk->sk_write_queue.lock);if (po->tx_ring.pg_vec) {
@@ -4554,14 +4566,14 @@ static int packet_set_ring(struct sock * rb->frame_max = (req->tp_frame_nr -1 ); rb->head =0 ; rb->frame_size = req->tp_frame_size; + po->prot_hook.func = (po->rx_ring.pg_vec) ? + tpacket_rcv : packet_rcv;spin_unlock_bh (&rb_queue->lock);swap (rb->pg_vec_order, order);swap (rb->pg_vec_len, req->tp_block_nr); rb->pg_vec_pages = req->tp_block_size/PAGE_SIZE ; - po->prot_hook.func = (po->rx_ring.pg_vec) ? - tpacket_rcv : packet_rcv;skb_queue_purge (rb_queue);if (atomic_long_read (&po->mapped)) pr_err("packet_mmap: vma is busy: %ld\n",
Greg Kroah-Hartman
maintainerPATCH 153/271 net: fix skb length accounting after generic XDP frag adjustment
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sun Jian <[email protected]>
commit 33f2b2eb33d666ecac68031e0f31424fb70528db upstream.
Generic XDP exposes non-linear skb fragments through an xdp_buff. If an XDP program adjusts the fragment area, bpf_prog_run_generic_xdp() copies xdp_frags_size back to skb->data_len but leaves skb->len containing the old fragment contribution.
After a fragment shrink, this makes skb_headlen() larger than the actual linear area. In the reproduced UDP receive path, __skb_datagram_iter() copied 1024 bytes past the actual linear tail to userspace, starting at struct skb_shared_info. The copied bytes included the affected skb's nr_frags, xdp_frags_size and a kernel pointer from skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same amount and truncated at the end.
Subtract the old data_len before replacing it and add the new data_len afterwards, keeping skb->len and skb->data_len synchronized.
A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by 1024 bytes from its fragment area. Before the fix, all 10 runs produced corrupted payloads. After the fix, all 10 runs matched the expected payload exactly.
--- net/core/dev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
net/core/dev.c +7 -3
@@ -5515,12 +5515,16 @@ u32 bpf_prog_run_generic_xdp(struct sk_b }/* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers -* (e.g. bpf_xdp_adjust_tail), we need to update data_len here. +* (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution +* from skb->len before updating data_len, then add the new one back. */ -if (xdp_buff_has_frags (xdp)) + skb->len -= skb->data_len; +if (xdp_buff_has_frags (xdp)) { skb->data_len =skb_shinfo (skb)->xdp_frags_size; -else + skb->len += skb->data_len; + }else { skb->data_len =0 ; + }/* check if XDP changed eth hdr such SKB needs update */ eth = (struct ethhdr *)xdp->data;
Greg Kroah-Hartman
maintainerPATCH 154/271 net: openvswitch: reallocate update replies for mismatched IDs
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit 5d1c224dd914579524a183a514c12b95095d12ce upstream.
ovs_flow_cmd_new() preallocates the optional reply skb before it takes ovs_mutex and before it knows which existing flow will be updated.
That is normally fine because the skb is sized from the request flow identifier. That identifier also becomes the inserted flow's identifier. For updates, however, a request with a UFID may miss the UFID lookup and then fall back to the flow key lookup. That lookup can legitimately find an existing key-identified flow. UFIDs are optional and the flow key is the primary identifier.
For echoed replies, ovs_flow_cmd_fill_info() writes the matched flow's identifier, not the request identifier used for the preallocation. A short request UFID can therefore leave too little room for the key identifier. The fill can then fail with -EMSGSIZE and hit the BUG_ON(error < 0) in the update path.
Once the update target has been resolved, reallocate the reply skb if the matched flow needs a larger reply than the request identifier allowed. Do this before replacing the actions so the request can still fail cleanly if the rare extra allocation fails.
--- net/openvswitch/datapath.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-)
net/openvswitch/datapath.c +26 -3
@@ -1113,9 +1113,8 @@ static int ovs_flow_cmd_new(struct sk_bu error = -EEXIST ; goto err_unlock_ovs; } -/* The flow identifier has to be the same for flow updates. -* Look for any overlapping flow. -*/ + +/* Look for any overlapping flow. */ if (unlikely (!ovs_flow_cmp (flow, &match))) {if (ovs_identifier_is_key (&flow->id)) flow = ovs_flow_tbl_lookup_exact(&dp->table,
@@ -1127,6 +1126,30 @@ static int ovs_flow_cmd_new(struct sk_bu goto err_unlock_ovs; } } + +if (unlikely (reply)) { +size_t cur, req; + + cur =ovs_flow_cmd_msg_size (acts, &new_flow->id, + ufid_flags); + req =ovs_flow_cmd_msg_size (acts, &flow->id, + ufid_flags); +if (cur < req) { +struct sk_buff *resized; + + resized =ovs_flow_cmd_alloc_info (acts, + &flow->id, + info, false, + ufid_flags); +if (IS_ERR (resized)) { + error =PTR_ERR (resized); + goto err_unlock_ovs; + } +kfree_skb (reply); + reply = resized; + } + } +/* Update actions. */ old_acts =ovsl_dereference (flow->sf_acts);rcu_assign_pointer (flow->sf_acts, acts);
Greg Kroah-Hartman
maintainerPATCH 155/271 net/sched: reject overly deep qdisc hierarchies
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zijie Huang <[email protected]>
commit dedd34b0f2310e28c5f6d4875cfbf4b7ed821c01 upstream.
Deep qdisc hierarchies can lead to excessive recursion in qdisc tree walkers and exhaust the kernel stack. The existing loop check does not cover the create-and-graft path, so a hierarchy can still be extended by creating a new child qdisc below an already deep parent.
Store the hierarchy depth in struct Qdisc and update it when qdiscs are grafted. Reject new child qdiscs once the parent is already at the maximum allowed depth.
--- include/net/sch_generic.h | 1 + net/sched/sch_api.c | 9 +++++++++ 2 files changed, 10 insertions(+)
include/net/sch_generic.h +1 -0
@@ -99,6 +99,7 @@ struct Qdisc {struct hlist_node hash;u32 handle;u32 parent; +int depth;struct netdev_queue *dev_queue;
net/sched/sch_api.c +9 -0
@@ -1110,6 +1110,9 @@ static int qdisc_graft(struct net_deviceunsigned int i, num_q, ingress;struct netdev_queue *dev_queue; +if (new) + new->depth =0 ; + ingress =0 ; num_q = dev->num_tx_queues; if ((q && q->flags & TCQ_F_INGRESS) ||
@@ -1207,9 +1210,15 @@ skip:NL_SET_ERR_MSG (extack,"STAB not supported on a non root" );return -EINVAL ; } +if (new && parent->depth >=7 ) { +NL_SET_ERR_MSG (extack,"Qdisc hierarchy is too deep" ); +return -E2BIG ; + } err = cops->graft (parent, cl, new, &old, extack);if (err)return err; +if (new) + new->depth = parent->depth +1 ;notify_and_destroy (net, skb, n, classid, old, new, extack); }return 0 ;
Greg Kroah-Hartman
maintainerPATCH 156/271 net: octeontx2-pf: Fix UB in shift operation
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey V. Frolov <[email protected]>
commit 7e2d693af0d4c05bddccb3541a0aabd69f4cb244 upstream.
In function otx2_get_egress_burst_cfg, when the parameter burst is 255 and the max mantissa is 255 (0xFFULL), burst_exp is set to ilog2(255) - 1, which equals 6.
This results in an unsigned wrap-around when calculating (1ULL << (*burst_exp - 7)), since *burst_exp - 7 becomes -1, which makes the shift operand 0xFFFFFFFF. This value is greater than the width of the left operand.
According to standard 6.5.7 p.3:
"The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined."
Fix the off-by-one boundary condition.
Add a WARN_ON(*burst_exp < 7) before the else branch as an explicit safeguard. This ensures that if max_mantissa ever changes in a way that reintroduces this condition, it will be immediately caught at runtime rather than silently triggering UB.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
--- drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +4 -2
@@ -54,10 +54,12 @@ static void otx2_get_egress_burst_cfg(stif (burst) { *burst_exp =ilog2 (burst) ?ilog2 (burst) -1 :0 ; tmp = burst -rounddown_pow_of_two (burst); -if (burst < max_mantissa) +if (burst <= max_mantissa) { *burst_mantissa = tmp *2 ; -else + }else { +WARN_ON (*burst_exp <7 ); *burst_mantissa = tmp / (1ULL << (*burst_exp -7 )); + } }else { *burst_exp =MAX_BURST_EXPONENT ; *burst_mantissa = max_mantissa;
Greg Kroah-Hartman
maintainerPATCH 157/271 net: remove CAP_SYS_RAWIO zero-padding in dev_validate_header
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qihang Tang <[email protected]>
commit 3b9a324e646d3657a8d9806dfbfe4f3e4066e882 upstream.
dev_validate_header() reads dev->hard_header_len directly when zero-padding short link layer headers for CAP_SYS_RAWIO holders:
if (capable(CAP_SYS_RAWIO)) {
memset(ll_header + len, 0, dev->hard_header_len - len);
return true;
}Packet send paths call dev_validate_header() on skbs whose headroom was allocated from an earlier hard_header_len read. If the device is reconfigured so that dev->hard_header_len increases before validation, the memset writes past the reserved buffer, an out-of-bounds write.
This out-of-bounds write is masked in some SOCK_RAW paths today because the same concurrent increase can first make skb_push() exceed the reserved headroom and trigger skb_under_panic(). Remove the zero-padding branch before making those hard_header_len reads consistent, so the snapshot fixes do not turn a loud panic into a silent overwrite.
This path is only reached for variable length L2 protocols, where len < hard_header_len but len >= min_header_len. No remaining in-tree variable length L2 protocol implements header_ops->validate, and the CAP_SYS_RAWIO bypass that zero-pads and accepts short headers has no real value beyond allowing testing of intentionally malformed input.
Drop the CAP_SYS_RAWIO branch. The remaining reads of dev->hard_header_len in dev_validate_header() are comparisons only and have no memory safety impact.
--- include/linux/netdevice.h | 5 ----- 1 file changed, 5 deletions(-)
include/linux/netdevice.h +0 -5
@@ -3509,11 +3509,6 @@ static inline bool dev_validate_header(cif (len < dev->min_header_len)return false; -if (capable (CAP_SYS_RAWIO )) { -memset (ll_header + len,0 , dev->hard_header_len - len); -return true; - } -if (dev->header_ops && dev->header_ops->validate)return dev->header_ops->validate (ll_header, len);
Greg Kroah-Hartman
maintainerPATCH 158/271 inet: frags: publish queues before arming timer
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit 653d7ddf6cba867777a3d14c4f83ace008c5ad13 upstream.
inet_frag_create() arms the fragment queue timer before inserting the queue into the fqdir rhashtable. If the namespace fragment timeout is zero or negative, the timer can run before the queue is published.
The timer callback then marks the queue complete, tries to remove a node that is not in the hash table yet, and drops the anticipated hash reference. Creation can subsequently publish the completed queue without restoring that reference, leaving a stale hash node after the caller drops the remaining reference.
Publish the queue first and arm the timer while holding the queue lock. This makes timer expiry wait until the queue is visible in the hash table, so inet_frag_kill() can remove the node and balance the hash reference.
--- net/ipv4/inet_fragment.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
net/ipv4/inet_fragment.c +5 -5
@@ -395,8 +395,8 @@ static struct inet_frag_queue *inet_frag *prev =ERR_PTR (-ENOMEM );return NULL ; } -mod_timer (&q->timer, jiffies + fqdir->timeout); +spin_lock_bh (&q->lock); *prev =rhashtable_lookup_get_insert_key (&fqdir->rhashtable, &q->key, &q->node, f->rhash_params);if (*prev) {
@@ -404,13 +404,13 @@ static struct inet_frag_queue *inet_frag* we need to cancel what inet_frag_alloc() * anticipated. */ -int refs =1 ; - q->flags |=INET_FRAG_COMPLETE ; -inet_frag_kill (q, &refs); -inet_frag_putn (q, refs); +spin_unlock_bh (&q->lock); +inet_frag_putn (q,2 );return NULL ; } +mod_timer (&q->timer, jiffies + fqdir->timeout); +spin_unlock_bh (&q->lock);return q; }
Greg Kroah-Hartman
maintainerPATCH 159/271 mac802154: fix netdev use-after-free in beacon worker
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
commit 5f26a690e8efa54315e4922368daf54e0b8f5515 upstream.
mac802154_beacon_worker() reads local->beacon_req under RCU and derives the sub-interface from the request, but then drops the RCU read lock and continues to use both sdata and the embedded wpan_dev.
mac802154_stop_beacons_locked() cancels only pending beacon work, clears local->beacon_req and frees the request. A beacon worker that is already running can therefore continue after interface teardown and dereference the freed netdev private area.
The scan worker already pins the netdev before leaving RCU. Apply the same lifetime rule to the beacon worker: take a netdev reference while the request is still protected by RCU, and release it on all paths that continue after the reference is acquired.
--- net/mac802154/scan.c | 4 ++++ 1 file changed, 4 insertions(+)
net/mac802154/scan.c +4 -0
@@ -415,6 +415,7 @@ void mac802154_beacon_worker(struct work container_of(work, struct ieee802154_local, beacon_work.work);struct cfg802154_beacon_request *beacon_req;struct ieee802154_sub_if_data *sdata; +netdevice_tracker dev_tracker;struct wpan_dev *wpan_dev;u8 interval;int ret;
@@ -427,12 +428,14 @@ void mac802154_beacon_worker(struct work } sdata =IEEE802154_WPAN_DEV_TO_SUB_IF (beacon_req->wpan_dev); +netdev_hold (sdata->dev, &dev_tracker,GFP_ATOMIC );/* Wait an arbitrary amount of time in case we cannot use the device */ if (local->suspended || !ieee802154_sdata_running (sdata)) {rcu_read_unlock ();queue_delayed_work (local->mac_wq, &local->beacon_work,msecs_to_jiffies (1000 )); +netdev_put (sdata->dev, &dev_tracker);return ; }
@@ -450,6 +453,7 @@ void mac802154_beacon_worker(struct workif (interval <IEEE802154_ACTIVE_SCAN_DURATION )queue_delayed_work (local->mac_wq, &local->beacon_work, local->beacon_interval); +netdev_put (sdata->dev, &dev_tracker); } int mac802154_stop_beacons_locked(struct ieee802154_local *local,
Greg Kroah-Hartman
maintainerPATCH 160/271 igc: fix netdev not re-attached after resume if interface is down
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Philipp David <[email protected]>
commit b0ce5fd9fabe7c79463cf4602217d4dfeff5b1fd upstream.
__igc_resume() calls netif_device_attach() only inside the netif_running() branch, so an interface that was down during suspend is never re-attached on resume. It then stays in the not-present state that __igc_shutdown() set via netif_device_detach(): ethtool reports ENODEV and every attempt to bring the interface up fails the netif_device_present() check in __dev_open() with -ENODEV, silently, since __igc_resume() returns 0. Only reloading the driver recovers the device.
This is easy to hit in practice because NetworkManager brings managed interfaces down before sleep unless Wake-on-LAN is configured, making the adapter unusable after every suspend/resume cycle with WoL disabled.
Re-attach the netdev on every successful resume, as igb and e1000e do.
--- drivers/net/ethernet/intel/igc/igc_main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/net/ethernet/intel/igc/igc_main.c +5 -3
@@ -7585,11 +7585,13 @@ static int __igc_resume(struct device *d err =__igc_open (netdev, true);if (!rpm)rtnl_unlock (); -if (!err) -netif_device_attach (netdev); +if (err) +return err; } -return err; +netif_device_attach (netdev); + +return 0 ; }static int igc_resume (struct device *dev)
Greg Kroah-Hartman
maintainerPATCH 161/271 netfilter: ebt_nflog: pin the NFLOG backend
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
commit 30825970339c107bacaf7f61af90fcdb1f597ca1 upstream.
nf_log_unregister() runs after the per-net teardown so its final RCU grace period also drains readers that obtained the logger from a per-net binding. However, ebt_nflog passes an explicit ULOG log type to nf_log_packet() without holding a reference on the selected logger module, unlike the xt_NFLOG and nft_log frontends.
An ebtables nflog rule can therefore remain callable while nfnetlink_log is unloaded. The resulting interleaving is:
CPU 0 CPU 1
nfnetlink_log_fini()
unregister_pernet_subsys()
kfree(nfnl_log_pernet(net))
ebt_nflog_tg()
nf_log_packet()
nfulnl_log_packet()
instance_lookup_get_rcu()The global ULOG logger is still registered at this point, so CPU 1 dereferences the per-net state after CPU 0 has freed it. KASAN reported:
BUG: KASAN: slab-use-after-free in instance_lookup_get_rcu Read of size 8 at addr ff110001052e6210 by task poc/92 Call Trace: instance_lookup_get_rcu+0x1ce/0x1f0 [nfnetlink_log] nfulnl_log_packet+0x248/0x2fb0 [nfnetlink_log] nf_log_packet+0x204/0x300 ebt_nflog_tg+0x351/0x550 ebt_do_table+0xedf/0x22b0 Allocated by task 90: __kmalloc_noprof+0x186/0x470 ops_init+0x6d/0x420 register_pernet_operations+0x2f6/0x670 register_pernet_subsys+0x23/0x40 Freed by task 93: kfree+0x131/0x3c0 ops_undo_list+0x3e3/0x700 unregister_pernet_operations+0x232/0x490 unregister_pernet_subsys+0x1c/0x30 nfnetlink_log_fini+0x34/0x450 [nfnetlink_log]
Acquire the ULOG logger module reference when an ebt_nflog rule is validated and release it when the rule is destroyed. Request the NFLOG backend for legacy callers when needed, matching xt_NFLOG. This prevents module teardown until all ebt_nflog rules have stopped using the logger.
--- net/bridge/netfilter/ebt_nflog.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)
net/bridge/netfilter/ebt_nflog.c +16 -1
@@ -41,11 +41,25 @@ ebt_nflog_tg(struct sk_buff *skb, conststatic int ebt_nflog_tg_check (const struct xt_tgchk_param *par) {struct ebt_nflog_info *info = par->targinfo; +int ret;if (info->flags & ~EBT_NFLOG_MASK )return -EINVAL ; info->prefix[EBT_NFLOG_PREFIX_SIZE -1 ] ='\0' ; -return 0 ; + + ret =nf_logger_find_get (par->family,NF_LOG_TYPE_ULOG ); +if (ret !=0 && !par->nft_compat) { +request_module ("%s" ,"nfnetlink_log" ); + + ret =nf_logger_find_get (par->family,NF_LOG_TYPE_ULOG ); + } + +return ret; +} + +static void ebt_nflog_tg_destroy (const struct xt_tgdtor_param *par) +{ +nf_logger_put (par->family,NF_LOG_TYPE_ULOG ); }static struct xt_target ebt_nflog_tg_reg__read_mostly = {
@@ -54,6 +68,7 @@ static struct xt_target ebt_nflog_tg_reg .family = NFPROTO_BRIDGE, .target = ebt_nflog_tg, .checkentry = ebt_nflog_tg_check, + .destroy = ebt_nflog_tg_destroy, .targetsize = sizeof(struct ebt_nflog_info), .me = THIS_MODULE, };
Greg Kroah-Hartman
maintainerPATCH 162/271 net: bridge: mrp: fix uninitialised bytes on the wire
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
commit 63488dba65ef91373ef616575b32eb0eb21459f4 upstream.
br_mrp_alloc_test_skb() builds MRP test frames on an skb from dev_alloc_skb(), which does not clear the linear data area. On the MRA ring-role branch the sub-option TLV header is appended with
sub_tlv = skb_put(skb, sizeof(*sub_tlv)); sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR;
so sub_tlv->length is never written, and the two trailing alignment bytes are appended with a bare skb_put() that does not clear them either. The neighbouring oui and sub_opt regions are explicitly zeroed, so three uninitialised bytes are left in every MRA MRP_Test frame that goes out.
Put the sub-option TLV header and the alignment padding in a single skb_put_zero(), which clears both. The AUTO_MGR sub-TLV carries no payload, so the zeroed length field is already the value it should have.
--- net/bridge/br_mrp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
net/bridge/br_mrp.c +2 -4
@@ -224,11 +224,9 @@ static struct sk_buff *br_mrp_alloc_test sub_opt =skb_put (skb,sizeof (*sub_opt));memset (sub_opt,0x0 ,sizeof (*sub_opt)); - sub_tlv =skb_put (skb,sizeof (*sub_tlv)); - sub_tlv->type =BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR ; -/* 32 bit alligment shall be ensured therefore add 2 bytes */ -skb_put (skb,MRP_OPT_PADDING ); + sub_tlv =skb_put_zero (skb,sizeof (*sub_tlv) +MRP_OPT_PADDING ); + sub_tlv->type =BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR ; }br_mrp_skb_tlv (skb,BR_MRP_TLV_HEADER_END ,0x0 );
Greg Kroah-Hartman
maintainerPATCH 163/271 Revert "drm/amd/display: Fix backlight max_brightness to match exported range"
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
This reverts commit 1191285ecb4233e5bf907da7e9394d7999055566.
This patch is apparently missing a dependency from 7.2 as users have not reported any regressions with 7.2-rc, but there are regressions on 7.1.
The regression manifests as inconsistent lower brightness at the top end (e.g., around 98%).
--- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +3 -3
@@ -5401,11 +5401,11 @@ amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector) caps = &dm->backlight_caps[aconnector->bl_idx];if (get_brightness_range (caps, &min, &max)) {if (power_supply_is_system_supplied () >0 ) - props.brightness =DIV_ROUND_CLOSEST (max * caps->ac_level,100 ); + props.brightness =DIV_ROUND_CLOSEST ((max - min) * caps->ac_level,100 );else - props.brightness =DIV_ROUND_CLOSEST (max * caps->dc_level,100 ); + props.brightness =DIV_ROUND_CLOSEST ((max - min) * caps->dc_level,100 );/* min is zero, so max needs to be adjusted */ - props.max_brightness = max; + props.max_brightness = max - min;drm_dbg (drm,"Backlight caps: min: %d, max: %d, ac %d, dc %d\n" , min, max, caps->ac_level, caps->dc_level); }else
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 164/271 futex: Prevent robust futex exit race some more
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Keno Fischer <[email protected]>
commit 6d4514ca9cdf61fec4ec634cf50386f6f7e69748 upstream.
A robust futex unlock stores 0 over the whole futex value - wiping FUTEX_WAITERS - and wakes a single waiter. That wakeup is a one-shot
notification: the protocol relies on its recipient to either acquire the futex (and eventually unlock while aware of the remaining contention) or re-arm FUTEX_WAITERS before sleeping again. If the woken waiter is killed before it can do either, the kernel must jump in and wake the next task down the line.
This is a known complication of the futex protocol with a previous partial fix in commit ca16d5bee598 ("futex: Prevent robust futex exit race"). Unfortunately, that fix is insufficient.
If a third task re-acquired the futex through the uncontended fast path in the meantime, the notification is lost: robust exit processing sees that it is owned by another task and does nothing, while the new owner sees no FUTEX_WAITERS when it unlocks and wakes nobody. The remaining waiters sleep forever behind a free futex:
A owns the futex, B and C sleep in FUTEX_WAIT
uval == A | FUTEX_WAITERS
A robust unlock: store 0, FUTEX_WAKE(1) wakes B
uval == 0
D fast path acquire: cmpxchg(0 -> D)
uval == D, no FUTEX_WAITERS B killed before acting on the wakeup
B exit walk, pending op: owner D != B -> no action D unlock: no FUTEX_WAITERS -> no wake
C sleeps forever
This is clearly a shortcoming in the implementation, which fails to keep the FUTEX_WAITERS bit consistent.
Work around this by augmenting the robust list exit processing to also perform the extra wakeup if the futex word is owned by another thread but FUTEX_WAITERS is not set.
This does not fix the problem of a non-contended take over/release and free sequence, which has been discussed for years and has been addressed by commit 3ca9595d9fb6 ("futex: Add support for unlocking robust futexes") and subsequent changes, but failed to take the problem described above into account.
A more complete solution which is based on the in kernel unlock of contended robust futexes has been discussed in the context of this change and should show up in mainline sooner than later.
[ tglx: Amend change log slightly and fixup coding style ]
--- kernel/futex/core.c | 85 +++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 27 deletions(-)
kernel/futex/core.c +58 -27
@@ -1027,8 +1027,11 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr,return -1 ;/* -* Special case for regular (non PI) futexes. The unlock path in -* user space has two race scenarios: +* Special case for regular (non PI) futexes. Ordinarily, we do +* not perform any processing here unless the current thread was +* the owner of the futex (by the TID check below). +* +* However, the unlock path has three race scenarios: * * 1. The unlock path releases the user space futex value and * before it can execute the futex() syscall to wake up
@@ -1037,42 +1040,70 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr,* 2. A woken up waiter is killed before it can acquire the * futex in user space. * -* In the second case, the wake up notification could be generated -* by the unlock path in user space after setting the futex value -* to zero or by the kernel after setting the OWNER_DIED bit below. +* 3. A woken up waiter is killed in user space after another +* thread has acquired the futex, but before it can set +* FUTEX_WAITERS. +* +* Note that, if userspace uses the FUTEX_ROBUST_UNLOCK flag, we +* will not see case 1 here. +* +* In the second and third case, the wake up notification could +* be generated from any of: +* +* i. An ordinary futex wakeup after unlock (with or +* without FUTEX_ROBUST_UNLOCK) +* ii. A robust wakeup from another thread's death +* iii. A previous round through this special case +* +* As a result, the futex world will be in one of four states: +* +* A. The futex word is 0 (unlocked) +* B. The futex word is owned by another thread +* (FUTEX_WAITERS is not set) +* C. The futex word is owned by another thread +* (FUTEX_WAITERS set) +* D. The futex's owner died and OWNER_DIED is set +* (the owner part of the word is 0) * -* In both cases the TID validation below prevents a wakeup of -* potential waiters which can cause these waiters to block -* forever. +* The key issue is that the kernel usually (at least from +* sources ii. and iii. or when so requested by userspace from +* source i.) only ever wakes *one* waiter at a time. If this +* waiter dies before acquiring the futex (or setting the +* FUTEX_WAITERS bit), the kernel *must* still wake the next +* waiter down the line to uphold the futex invariants and +* avoid lost wakeups. Note we do not need to handle state C, +* as it does not matter to us whether *we* successfully set +* the bit or a third thread did so in the meantime. * -* In both cases the following conditions are met: +* Therefore, in these cases we must issue an additional +* futex_wake(). Note however that we *must not* set OWNER_DIED +* here. Our thread is *not* the owner of the futex. * -* 1) task->robust_list->list_op_pending != NULL -* @pending_op == true -* 2) The owner part of user space futex value == 0 +* Thus to summarize, the conditions for needing the additional +* futex_wake() are: +* +* 1) @pending_op == true (the thread has not finished the +* mutex operation) +* 2) The futex word is in one of the states A, B or D * 3) Regular futex: @pi == false * -* If these conditions are met, it is safe to attempt waking up a -* potential waiter without touching the user space futex value and -* trying to set the OWNER_DIED bit. If the futex value is zero, -* the rest of the user space mutex state is consistent, so a woken -* waiter will just take over the uncontended futex. Setting the -* OWNER_DIED bit would create inconsistent state and malfunction -* of the user space owner died handling. Otherwise, the OWNER_DIED -* bit is already set, and the woken waiter is expected to deal with -* this. +* Note in particular that in all of the states A-D the owner +* portion of the futex word differs from our thread's TID +* (unless the actual owner has the same TID in another PID +* namespace, but we cannot currently distinguish that +* scenario), so this can be a special-case wakeup in the bail +* path of the ordinary TID check. */ owner = uval &FUTEX_TID_MASK ; -if (pending_op && !pi && !owner) { -futex_wake (uaddr,FLAGS_SIZE_32 |FLAGS_SHARED ,1 , -FUTEX_BITSET_MATCH_ANY ); +if (owner !=task_pid_vnr (curr)) { +if (pending_op && !pi && (!owner || !(uval &FUTEX_WAITERS ))) { +futex_wake (uaddr,FLAGS_SIZE_32 |FLAGS_SHARED ,1 , +FUTEX_BITSET_MATCH_ANY ); + }return 0 ; } -if (owner !=task_pid_vnr (curr)) -return 0 ; -/* * Ok, this dying thread is truly holding a futex * of interest. Set the OWNER_DIED bit atomically
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 165/271 selftests/xsk: fix too-many-frags multi-buffer Tx test
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej Fijalkowski <[email protected]>
[ Upstream commit c5b1ca6a02886f00170ed91b757e244f23259e91 ]
The too-many-frags test describes a packet that is valid from the Tx ring ownership point of view, but invalid for transmission because it exceeds the supported number of fragments.
Keep the generated Tx descriptors valid so that __send_pkts() accounts them as outstanding descriptors that must be reclaimed through the CQ. Then mark the corresponding Rx packet invalid so the test still does not expect the oversized packet to appear on the receive side.
Add a valid synchronization packet after the oversized packet so the test can verify that the Tx path drains the bad packet and resumes at the next packet boundary.
--- .../selftests/bpf/prog_tests/test_xsk.c | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-)
tools/testing/selftests/bpf/prog_tests/test_xsk.c +15 -9
@@ -2229,7 +2229,7 @@ int testapp_too_many_frags(struct test_spec *test) max_frags +=1 ; } - pkts =calloc (2 * max_frags +2 ,sizeof (struct pkt )); + pkts =calloc (2 * max_frags +3 ,sizeof (struct pkt ));if (!pkts)return TEST_FAILURE ;
@@ -2247,24 +2247,30 @@ int testapp_too_many_frags(struct test_spec *test) } pkts[max_frags].options =0 ; -/* An invalid packet with the max amount of frags but signals packet -* continues on the last frag -*/ -for (i = max_frags +1 ; i <2 * max_frags +1 ; i++) { +/* An invalid packet with the max + 1 amount of frags */ +for (i = max_frags +1 ; i <2 * max_frags +2 ; i++) { pkts[i].len =MIN_PKT_SIZE ; pkts[i].options =XDP_PKT_CONTD ; - pkts[i].valid = false; + pkts[i].valid = true; } + pkts[2 * max_frags +1 ].options =0 ;/* Valid packet for synch */ - pkts[2 * max_frags +1 ].len =MIN_PKT_SIZE ; - pkts[2 * max_frags +1 ].valid = true; + pkts[2 * max_frags +2 ].len =MIN_PKT_SIZE ; + pkts[2 * max_frags +2 ].valid = true; -if (pkt_stream_generate_custom (test, pkts,2 * max_frags +2 )) { +if (pkt_stream_generate_custom (test, pkts,2 * max_frags +3 )) {free (pkts);return TEST_FAILURE ; } +/* The generated Tx stream must keep the too-big packet valid so that +* __send_pkts() accounts its descriptors in outstanding_tx. The Rx +* stream, however, must not expect this packet on the wire. +*/ + test->ifobj_rx->xsk->pkt_stream->pkts[2 ].valid = false; + test->ifobj_rx->xsk->pkt_stream->nb_valid_entries--; + ret =testapp_validate_traffic (test);free (pkts);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 166/271 selftests/xsk: account reclaimed invalid Tx descriptors
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej Fijalkowski <[email protected]>
[ Upstream commit f49d99eaee7c32badc7ddfaecbb01ce4d037d695 ]
Invalid Tx descriptors are now returned through the completion ring, regardless of whether they form a standalone packet or belong to an invalid multi-buffer packet.
The selftests previously counted only descriptors belonging to valid packets, with a special exception for some invalid multi-buffer packets in verbatim streams. This undercounts completion entries when a standalone invalid descriptor or another invalid packet is reclaimed by the kernel.
Keep valid_pkts as the number of packets expected on the Rx side, but count every descriptor submitted to the Tx ring in valid_frags, as every such descriptor is now expected to be returned through the completion ring.
Make fragment counting in verbatim mode follow the packet boundary instead of stopping at the first invalid fragment. Update custom stream generation so an invalid middle fragment terminates the generated Rx packet while Tx completion accounting still covers the complete invalid packet.
Also add explicit end fragments after invalid middle descriptors. This exercises the kernel drain logic and verifies that subsequent valid packets are not interpreted as continuations of the invalid packet.
[ rbm: in testapp_invalid_desc_mb() hunk, use umem_size instead of umem_sz ]
--- .../selftests/bpf/prog_tests/test_xsk.c | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-)
tools/testing/selftests/bpf/prog_tests/test_xsk.c +14 -12
@@ -414,14 +414,14 @@ static u32 pkt_nb_frags(u32 frame_size, struct pkt_stream *pkt_stream, struct pk }/* Search for the end of the packet in verbatim mode */ -if (!pkt_continues (pkt->options) || !pkt->valid) +if (!pkt_continues (pkt->options))return nb_frags; next_frag = pkt_stream->current_pkt_nb; pkt++;while (next_frag++ < pkt_stream->nb_pkts) { nb_frags++; -if (!pkt_continues (pkt->options) || !pkt->valid) +if (!pkt_continues (pkt->options))break ; pkt++; }
@@ -652,11 +652,11 @@ static struct pkt_stream *__pkt_stream_generate_custom(struct ifobject *ifobj, sif (!frame->valid || !pkt_continues (frame->options)) payload++; }else { -if (frame->valid) +if (frame->valid) { len += frame->len; -if (frame->valid &&pkt_continues (frame->options)) -continue ; - +if (pkt_continues (frame->options)) +continue ; + } pkt->pkt_nb = pkt_nb; pkt->len = len; pkt->valid = frame->valid;
@@ -1236,10 +1236,9 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, b } } -if (pkt && pkt->valid) { +if (pkt && pkt->valid) valid_pkts++; - valid_frags += nb_frags; - } + valid_frags += nb_frags; }pthread_mutex_lock (&pacing_mutex);
@@ -2075,13 +2074,16 @@ int testapp_invalid_desc_mb(struct test_spec *test) {0, 0, 0, false, 0},/* Invalid address in the second frame */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, - {umem_size, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, + {umem_size * 2, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, + {0, MIN_PKT_SIZE, 0, false, 0},/* Invalid len in the middle */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, {0, XSK_UMEM__INVALID_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, + {0, MIN_PKT_SIZE, 0, false, 0},/* Invalid options in the middle */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XSK_DESC__INVALID_OPTION}, + {0, MIN_PKT_SIZE, 0, false, 0},/* Transmit 2 frags, receive 3 */ {0, XSK_UMEM__MAX_FRAME_SIZE, 0, true, XDP_PKT_CONTD}, {0, XSK_UMEM__MAX_FRAME_SIZE, 0, true, 0},
@@ -2093,8 +2095,8 @@ int testapp_invalid_desc_mb(struct test_spec *test)if (umem->unaligned_mode) {/* Crossing a chunk boundary allowed */ - pkts[12 ].valid = true; - pkts[13 ].valid = true; + pkts[15 ].valid = true; + pkts[16 ].valid = true; } test->mtu =MAX_ETH_JUMBO_SIZE ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 167/271 Bluetooth: btrtl: fix RTL8761B/BU broken LE extended scan
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexej Sidorenko <[email protected]>
[ Upstream commit 5ead2063611ae56809b1b113ac44cef9547c81d7 ]
RTL8761B and RTL8761BU devices report HCI version 5.1 but do not support the LE Extended Scan commands. This causes repeated failures with Opcode 0x2042 (LE Set Extended Scan Parameters) returning -EBUSY when BlueZ attempts extended scanning while a connection is active.
Set HCI_QUIRK_BROKEN_EXT_SCAN for CHIP_ID_8761B to make BlueZ fall back to legacy LE scan commands which the firmware supports correctly.
Tested with RTL8761BU (USB ID 0bda:a728) where the issue manifested as continuous 'Opcode 0x2042 failed: -16' errors in dmesg whenever a BLE connection was active.
--- drivers/bluetooth/btrtl.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
drivers/bluetooth/btrtl.c +13 -0
@@ -1343,6 +1343,19 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)if (!btrtl_dev->ic_info)return ; +switch (btrtl_dev->project_id) { +case CHIP_ID_8761B : +/* RTL8761B/BU reports HCI version 5.1 but does not support +* the LE Extended Scan commands (Opcode 0x2042), causing +* repeated -EBUSY failures when BlueZ attempts extended +* scanning while a connection is active. +*/ +hci_set_quirk (hdev,HCI_QUIRK_BROKEN_EXT_SCAN ); +break ; +default : +break ; + } +switch (btrtl_dev->ic_info->lmp_subver) {case RTL_ROM_LMP_8703B :/* 8723CS reports two pages for local ext features,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 168/271 Bluetooth: btusb: Add TP-Link UB600 for Realtek 8761BUV
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nils Helmig <[email protected]>
[ Upstream commit bc597f0cc44f0b173c50ee986a047219cd559ee9 ]
Add the vendor/product ID (0x37ad, 0x0600) to usb_device_id table for Realtek 8761BUV.
The device info from /sys/kernel/debug/usb/devices as below.
T: Bus=03 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 4 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=37ad ProdID=0600 Rev= 2.00 S: Manufacturer= S: Product=TP-Link Bluetooth USB Adapter S: SerialNumber=ACA7F14FD2A5 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=500mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms
--- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/bluetooth/btusb.c +2 -0
@@ -832,6 +832,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x2b89, 0x6275), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 169/271 selftests/bpf: Ensure UDP sockets are bound
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michal Luczaj <[email protected]>
[ Upstream commit fe3ff273767ef22fe8a7cb3816f264927c190e50 ]
Update sockmap_basic tests to bind sockets before they are used. This accommodates the recent change in sockmap that rejects unbound UDP sockets.
--- tools/testing/selftests/bpf/prog_tests/sockmap_basic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +3 -3
@@ -807,7 +807,7 @@ static void test_sockmap_many_socket(void)return ; } - udp =xsocket (AF_INET ,SOCK_DGRAM |SOCK_NONBLOCK ,0 ); + udp =socket_loopback (AF_INET ,SOCK_DGRAM |SOCK_NONBLOCK );if (udp <0 ) {close (dgram);close (tcp);
@@ -876,7 +876,7 @@ static void test_sockmap_many_maps(void)return ; } - udp =xsocket (AF_INET ,SOCK_DGRAM |SOCK_NONBLOCK ,0 ); + udp =socket_loopback (AF_INET ,SOCK_DGRAM |SOCK_NONBLOCK );if (udp <0 ) {close (dgram);close (tcp);
@@ -947,7 +947,7 @@ static void test_sockmap_same_sock(void)return ; } - udp =xsocket (AF_INET ,SOCK_DGRAM |SOCK_NONBLOCK ,0 ); + udp =socket_loopback (AF_INET ,SOCK_DGRAM |SOCK_NONBLOCK );if (udp <0 ) {close (dgram);close (tcp);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 170/271 selftests/bpf: Adapt sockmap update error handling
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michal Luczaj <[email protected]>
[ Upstream commit 30581eda4a07ff15db623612cac578e81869e96f ]
Update sockmap_listen to accommodate the recent change in sockmap that rejects unbound UDP sockets.
TCP: Reject unbound and bound (unless established or listening).
UDP: Accept only bound sockets.
While at it, migrate to ASSERT_* and enforce reverse xmas tree.
--- .../selftests/bpf/prog_tests/sockmap_listen.c | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-)
tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +11 -10
@@ -53,8 +53,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, int family, int sotype, int mapfd) { u32 key = 0; -u64 value;int err, s; +u64 value; s =xsocket (family, sotype,0 );if (s ==-1 )
@@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, errno =0 ; value = s; err =bpf_map_update_elem (mapfd, &key, &value,BPF_NOEXIST ); -if (sotype ==SOCK_STREAM ) { -if (!err || errno !=EOPNOTSUPP ) -FAIL_ERRNO ("map_update: expected EOPNOTSUPP" ); - }else if (err) -FAIL_ERRNO ("map_update: expected success" ); +ASSERT_ERR (err,"map_update" ); +ASSERT_EQ (errno,EOPNOTSUPP ,"errno" );xclose (s); }
@@ -77,8 +74,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused,struct sockaddr_storage addr;socklen_t len =0 ;u32 key =0 ; -u64 value;int err, s; +u64 value;init_addr_loopback (family, &addr, &len);
@@ -93,8 +90,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, errno =0 ; value = s; err =bpf_map_update_elem (mapfd, &key, &value,BPF_NOEXIST ); -if (!err || errno !=EOPNOTSUPP ) -FAIL_ERRNO ("map_update: expected EOPNOTSUPP" ); +if (sotype ==SOCK_STREAM ) { +ASSERT_ERR (err,"map_update" ); +ASSERT_EQ (errno,EOPNOTSUPP ,"errno" ); + }else { +ASSERT_OK (err,"map_update" ); + } close:xclose (s); }
@@ -1289,7 +1290,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map,/* insert */ TEST (test_insert_invalid),TEST (test_insert_opened), -TEST (test_insert_bound,SOCK_STREAM ), +TEST (test_insert_bound),TEST (test_insert),/* delete */ TEST(test_delete_after_insert),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 171/271 ipv4: Fix fib_nlmsg_size() for RTA_VIA nexthops
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
commit 4ff9548d84945d2cbf9e4c207288063a200ea397 upstream.
fib_nlmsg_size() still estimates nexthop space as if every gateway is encoded as an IPv4 RTA_GATEWAY attribute. IPv4 routes can also carry an IPv6 gateway, which fib_nexthop_info() dumps as RTA_VIA.
As a result, route notifications can allocate an skb that is too small. fib_dump_info() then fails with -EMSGSIZE and rtmsg_fib() hits the WARN_ON() that marks such failures as a fib_nlmsg_size() bug. With panic_on_warn set, this becomes a kernel panic.
Mirror the actual nexthop dump layout in fib_nlmsg_size(): account for IPv6 nexthop gateways dumped as RTA_VIA, for the no-header rtnexthop layout used inside RTA_MULTIPATH, and for RTA_FLOW only when it is actually present.
--- net/ipv4/fib_semantics.c | 67 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 18 deletions(-)
net/ipv4/fib_semantics.c +49 -18
@@ -491,6 +491,34 @@ int ip_fib_check_default(__be32 gw, strureturn -1 ; } +static size_t fib_nexthop_nlmsg_size (const struct fib_nh_common *nhc, +bool skip_oif) +{ +size_t nhsize =0 ; + +switch (nhc->nhc_gw_family) { +case AF_INET : + nhsize +=nla_total_size (4 );/* RTA_GATEWAY */ +break ; +case AF_INET6 : + nhsize +=nla_total_size (sizeof (struct rtvia ) + +sizeof (struct in6_addr )); +break ; + } + +if (!skip_oif && nhc->nhc_dev) + nhsize +=nla_total_size (4 );/* RTA_OIF */ + +if (nhc->nhc_lwtstate) { +/* RTA_ENCAP */ + nhsize +=lwtunnel_get_encap_size (nhc->nhc_lwtstate); +/* RTA_ENCAP_TYPE */ + nhsize +=nla_total_size (2 ); + } + +return nhsize; +} +size_t fib_nlmsg_size (struct fib_info *fi) {size_t payload =NLMSG_ALIGN (sizeof (struct rtmsg ))
@@ -508,32 +536,35 @@ size_t fib_nlmsg_size(struct fib_info *f payload +=nla_total_size (4 );/* RTA_NH_ID */ if (nhs) { -size_t nh_encapsize =0 ; -/* Also handles the special case nhs == 1 */ - -/* each nexthop is packed in an attribute */ -size_t nhsize =nla_total_size (sizeof (struct rtnexthop )); +size_t mpsize =0 ;unsigned int i; -/* may contain flow and gateway attribute */ - nhsize +=2 *nla_total_size (4 ); - -/* grab encap info */ for (i =0 ; i <fib_info_num_path (fi); i++) {struct fib_nh_common *nhc =fib_info_nhc (fi, i); +size_t nhsize; + + nhsize =fib_nexthop_nlmsg_size (nhc, nhs !=1 ); -if (nhc->nhc_lwtstate) { -/* RTA_ENCAP_TYPE */ - nh_encapsize +=lwtunnel_get_encap_size ( - nhc->nhc_lwtstate); -/* RTA_ENCAP */ - nh_encapsize +=nla_total_size (2 ); +if (nhs !=1 ) + nhsize +=NLA_ALIGN (sizeof (struct rtnexthop )); + +#ifdef CONFIG_IP_ROUTE_CLASSID +if (nhc->nhc_family ==AF_INET ) { +struct fib_nh *nh; + + nh = container_of(nhc, struct fib_nh, nh_common); +if (nh->nh_tclassid) + nhsize +=nla_total_size (4 ); } +#endif +if (nhs ==1 ) + payload += nhsize; +else + mpsize += nhsize; } -/* all nexthops are packed in a nested attribute */ - payload +=nla_total_size ((nhs * nhsize) + nh_encapsize); - +if (nhs !=1 ) + payload +=nla_total_size (mpsize); }return payload;
Greg Kroah-Hartman
maintainerPATCH 172/271 ipv4: fix use-after-free in fib_nhc_update_mtu()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
commit bc5bde9ce3cc36502839dfe98e068f7303a50982 upstream.
fib_nhc_update_mtu() walks the nexthop exception table under RTNL, but RTNL does not serialize this walk with PMTU exception updates. The walk uses rcu_dereference_protected() with a constant true condition without holding fnhe_lock.
The following interleaving can therefore occur:
CPU 0 CPU 1
fib_nhc_update_mtu() update_or_create_fnhe()
load fnhe spin_lock_bh(&fnhe_lock)
fnhe_remove_oldest()
unlink fnhe kfree_rcu(fnhe, rcu)
<quiescent state>
access fnhe after grace period
KASAN reported:
BUG: KASAN: slab-use-after-free in fib_nhc_update_mtu+0x3df/0x410 Read of size 8 at addr ffff888107d49000 by task poc/90 Call Trace: fib_nhc_update_mtu+0x3df/0x410 fib_sync_mtu+0x7a/0xd0 fib_netdev_event+0x229/0x3f0 netif_set_mtu_ext+0x33a/0x570 dev_set_mtu+0x88/0x120
The same walk updates fnhe_pmtu and fnhe_mtu_locked. These fields form a pair and other writers serialize them with fnhe_lock. RCU alone prevents reclamation, but would still allow concurrent writers to leave a mixed pair.
Walk the table under RCU and acquire fnhe_lock only while updating each exception. RCU keeps the current entry alive while the short critical section serializes its paired PMTU fields. This avoids holding the global lock while scanning all 2048 buckets for every nexthop.
--- include/net/route.h | 2 ++ net/ipv4/fib_semantics.c | 34 +++++++++++----------------------- net/ipv4/route.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 23 deletions(-)
include/net/route.h +2 -0
@@ -276,6 +276,8 @@ int fib_dump_info_fnhe(struct sk_buff *s u32 table_id, struct fib_info *fi, int *fa_index, int fa_start, unsigned int flags); +void fnhe_update_pmtu (struct fib_nh_exception *fnhe,u32 new,u32 orig); +static inline void ip_rt_put (struct rtable *rt) {/* dst_release() accepts a NULL parameter.
net/ipv4/fib_semantics.c +11 -23
@@ -1899,42 +1899,30 @@ static int call_fib_nh_notifiers(structreturn NOTIFY_DONE ; } -/* Update the PMTU of exceptions when: -* - the new MTU of the first hop becomes smaller than the PMTU -* - the old MTU was the same as the PMTU, and it limited discovery of -* larger MTUs on the path. With that limit raised, we can now -* discover larger MTUs -* A special case is locked exceptions, for which the PMTU is smaller -* than the minimal accepted PMTU: -* - if the new MTU is greater than the PMTU, don't make any change -* - otherwise, unlock and set PMTU +/* Walk the exceptions of a nexthop after its first hop MTU changed. The +* chain is RCU protected here, while fnhe_update_pmtu() takes fnhe_lock +* for the update of each entry. */ void fib_nhc_update_mtu (struct fib_nh_common *nhc,u32 new,u32 orig) {struct fnhe_hash_bucket *bucket;int i; - bucket =rcu_dereference_protected (nhc->nhc_exceptions,1 ); +rcu_read_lock (); + bucket =rcu_dereference (nhc->nhc_exceptions);if (!bucket) -return ; + goto out;for (i =0 ; i <FNHE_HASH_SIZE ; i++) {struct fib_nh_exception *fnhe; -for (fnhe =rcu_dereference_protected (bucket[i].chain,1 ); +for (fnhe =rcu_dereference (bucket[i].chain); fnhe; - fnhe =rcu_dereference_protected (fnhe->fnhe_next,1 )) { -if (fnhe->fnhe_mtu_locked) { -if (new <= fnhe->fnhe_pmtu) { - fnhe->fnhe_pmtu = new; - fnhe->fnhe_mtu_locked = false; - } - }else if (new < fnhe->fnhe_pmtu || - orig == fnhe->fnhe_pmtu) { - fnhe->fnhe_pmtu = new; - } - } + fnhe =rcu_dereference (fnhe->fnhe_next)) +fnhe_update_pmtu (fnhe, new, orig); } +out: +rcu_read_unlock (); }void fib_sync_mtu (struct net_device *dev,u32 orig_mtu)
net/ipv4/route.c +29 -0
@@ -741,6 +741,35 @@ out_unlock:spin_unlock_bh (&fnhe_lock); } +/* Update the PMTU of an exception when: +* - the new MTU of the first hop becomes smaller than the PMTU +* - the old MTU was the same as the PMTU, and it limited discovery of +* larger MTUs on the path. With that limit raised, we can now +* discover larger MTUs +* A special case is locked exceptions, for which the PMTU is smaller +* than the minimal accepted PMTU: +* - if the new MTU is greater than the PMTU, don't make any change +* - otherwise, unlock and set PMTU +* +* fnhe_lock keeps fnhe_pmtu and fnhe_mtu_locked consistent against +* update_or_create_fnhe(), which sets both under the same lock. +*/ +void fnhe_update_pmtu (struct fib_nh_exception *fnhe,u32 new,u32 orig) +{ +spin_lock_bh (&fnhe_lock); + +if (fnhe->fnhe_mtu_locked) { +if (new <= fnhe->fnhe_pmtu) { + fnhe->fnhe_pmtu = new; + fnhe->fnhe_mtu_locked = false; + } + }else if (new < fnhe->fnhe_pmtu || orig == fnhe->fnhe_pmtu) { + fnhe->fnhe_pmtu = new; + } + +spin_unlock_bh (&fnhe_lock); +} +static void __ip_do_redirect (struct rtable *rt,struct sk_buff *skb,struct flowi4 *fl4,bool kill_route) {
Greg Kroah-Hartman
maintainerPATCH 173/271 mei: pull kvfree out of spinlock
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Usyskin <[email protected]>
commit b0495bb58af06a7de4628c72d500e3d5e180d808 upstream.
The read buffer allocation was changed from kmalloc() to kvmalloc().
This buffer is part of mei_cl_cb structure that can be queued in rd_complete queue protected by spinlock.
Releasing the structure leads to errors like below when freeing buffer that allocated non-contiguous:
BUG: sleeping function called from invalid context at mm/vmalloc.c:3448
Separate mei_cl_cb structure dequeue and release to perform only dequeue under spinlock and push release out of spinlock.
--- drivers/misc/mei/client.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
drivers/misc/mei/client.c +15 -10
@@ -425,18 +425,24 @@ static void mei_io_tx_list_free_cl(struc }/** -* mei_io_list_free_fp - free cb from a list that matches file pointer +* mei_io_rd_list_free_fp - free cb from a rd_completed list that matches file pointer * -* @head: io list +* @cl: host client * @fp: file pointer (matching cb file object), may be NULL */ -static void mei_io_list_free_fp (struct list_head *head,const struct file *fp) +static void mei_io_rd_list_free_fp (struct mei_cl *cl,const struct file *fp) {struct mei_cl_cb *cb, *next; +LIST_HEAD (cmpl_list); -list_for_each_entry_safe (cb, next, head, list) +spin_lock (&cl->rd_completed_lock); +list_for_each_entry_safe (cb, next, &cl->rd_completed, list)if (!fp || fp == cb->fp) -mei_io_cb_free (cb); +list_move (&cb->list, &cmpl_list); +spin_unlock (&cl->rd_completed_lock); + +list_for_each_entry_safe (cb, next, &cmpl_list, list) +mei_io_cb_free (cb); }/**
@@ -565,9 +571,7 @@ int mei_cl_flush_queues(struct mei_cl *cmei_io_list_flush_cl (&cl->dev->ctrl_rd_list, cl);mei_cl_free_pending (cl); } -spin_lock (&cl->rd_completed_lock); -mei_io_list_free_fp (&cl->rd_completed, fp); -spin_unlock (&cl->rd_completed_lock); +mei_io_rd_list_free_fp (cl, fp);return 0 ; }
@@ -1401,7 +1405,7 @@ void mei_cl_add_rd_completed(struct mei_ }/** -* mei_cl_del_rd_completed - free read completed callback with lock +* mei_cl_del_rd_completed - unlink read completed callback with lock and free it * * @cl: host client * @cb: callback block
@@ -1410,8 +1414,9 @@ void mei_cl_add_rd_completed(struct mei_void mei_cl_del_rd_completed (struct mei_cl *cl,struct mei_cl_cb *cb) {spin_lock (&cl->rd_completed_lock); -mei_io_cb_free (cb); +list_del_init (&cb->list);spin_unlock (&cl->rd_completed_lock); +mei_io_cb_free (cb); }/**
Greg Kroah-Hartman
maintainerPATCH 174/271 nvmem: apple-spmi-nvmem: wrap regmap calls to satisfy CFI
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aelin Reidel <[email protected]>
commit ff10b6db0ab75b132aed69ab144ac04f63ef9bdb upstream.
The Apple SPMI NVMEM driver previously cast regmap_bulk_read/write to void * when assigning them to nvmem_config's reg_read/reg_write function pointers.
This cast breaks the expected function signature of nvmem_reg_read_t and nvmem_reg_write_t. With CFI enabled, indirect calls through these pointers fail:
CFI failure at nvmem_reg_write+0x194/0x1e4 (target: regmap_bulk_write+0x0/0x2c8; expected type: 0x83a189c3)
...
Call trace: nvmem_reg_write+0x194/0x1e4 (P) __nvmem_cell_entry_write+0x298/0x2e8 nvmem_cell_write+0x24/0x34 macsmc_reboot_probe+0x1dc/0x454 [macsmc_reboot] ...
Introduce thin wrapper functions with the correct nvmem function pointer types to satisfy the CFI checks.
--- drivers/nvmem/apple-spmi-nvmem.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
drivers/nvmem/apple-spmi-nvmem.c +18 -2
@@ -18,6 +18,22 @@ static const struct regmap_config apple_ .max_register = 0xffff, }; +static int apple_spmi_nvmem_read (void *priv,unsigned int offset,void *val, +size_t bytes) +{ +struct regmap *map = priv; + +return regmap_bulk_read (map, offset, val, bytes); +} + +static int apple_spmi_nvmem_write (void *priv,unsigned int offset,void *val, +size_t bytes) +{ +struct regmap *map = priv; + +return regmap_bulk_write (map, offset, val, bytes); +} +static int apple_spmi_nvmem_probe (struct spmi_device *sdev) {struct regmap *regmap;
@@ -28,8 +44,8 @@ static int apple_spmi_nvmem_probe(struct .word_size = 1, .stride = 1, .size = 0xffff, - .reg_read = (void *)regmap_bulk_read, - .reg_write = (void *)regmap_bulk_write, + .reg_read = apple_spmi_nvmem_read, + .reg_write = apple_spmi_nvmem_write, }; regmap =devm_regmap_init_spmi_ext (sdev, &apple_spmi_regmap_config);
Greg Kroah-Hartman
maintainerPATCH 175/271 nvmem: layouts: Add fixed-layout driver
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mathieu Dubois-Briand <[email protected]>
commit b5be879519291f139fa7b365fd0dbc84710e4919 upstream.
Current implementation isn't working well when device tree nodes have a phandle on a fixed-layout nvmem node. As the fixed layout is handled in nvmem core, no driver is ever associated with the layout, and the device consumer driver probe is deferred indefinitely.
Remove the specific handling of fixed-layout and add a layout driver. This makes the fixed-layout similar to all other layouts, fixing the whole issue.
--- MAINTAINERS | 5 +++ drivers/nvmem/core.c | 24 +------------- drivers/nvmem/internals.h | 2 + drivers/nvmem/layouts.c | 11 ------ drivers/nvmem/layouts/Makefile | 1 drivers/nvmem/layouts/fixed-layout.c | 58 +++++++++++++++++++++++++++++++++++ include/linux/nvmem-provider.h | 6 +++ 7 files changed, 74 insertions(+), 33 deletions(-) create mode 100644 drivers/nvmem/layouts/fixed-layout.c
MAINTAINERS +5 -0
@@ -10031,6 +10031,11 @@ F: drivers/base/firmware_loader/ F: rust/kernel/firmware.rs F: include/linux/firmware.h +FIXED-LAYOUT NVMEM LAYOUT DRIVER +M: Mathieu Dubois-Briand <[email protected]> +S: Maintained +F: drivers/nvmem/layouts/fixed-layout.c + FLEXTIMER FTM-QUADDEC DRIVER M: Patrick Havelange <[email protected]> L: [email protected]
drivers/nvmem/core.c +2 -22
@@ -786,7 +786,7 @@ static int nvmem_validate_keepouts(strucreturn 0 ; } -static int nvmem_add_cells_from_dt (struct nvmem_device *nvmem,struct device_node *np) +int nvmem_add_cells_from_dt (struct nvmem_device *nvmem,struct device_node *np) {struct device *dev = &nvmem->dev;const __be32 *addr;
@@ -834,29 +834,13 @@ static int nvmem_add_cells_from_dt(strucreturn 0 ; } +EXPORT_SYMBOL_GPL (nvmem_add_cells_from_dt);static int nvmem_add_cells_from_legacy_of (struct nvmem_device *nvmem) {return nvmem_add_cells_from_dt (nvmem, nvmem->dev.of_node); } -static int nvmem_add_cells_from_fixed_layout (struct nvmem_device *nvmem) -{ -struct device_node *layout_np; -int err =0 ; - - layout_np =of_nvmem_layout_get_container (nvmem); -if (!layout_np) -return 0 ; - -if (of_device_is_compatible (layout_np,"fixed-layout" )) - err =nvmem_add_cells_from_dt (nvmem, layout_np); - -of_node_put (layout_np); - -return err; -} -int nvmem_layout_register (struct nvmem_layout *layout) {int ret;
@@ -1005,10 +989,6 @@ struct nvmem_device *nvmem_register(cons goto err_remove_cells; } - rval =nvmem_add_cells_from_fixed_layout (nvmem); -if (rval) - goto err_remove_cells; -dev_dbg (&nvmem->dev,"Registering nvmem device %s\n" , config->name); rval =device_add (&nvmem->dev);
drivers/nvmem/internals.h +2 -0
@@ -35,6 +35,8 @@ struct nvmem_device {bool sysfs_cells_populated; }; +int nvmem_add_cells_from_dt (struct nvmem_device *nvmem,struct device_node *np); +#if IS_ENABLED (CONFIG_OF )int nvmem_layout_bus_register (void );void nvmem_layout_bus_unregister (void );
drivers/nvmem/layouts.c +0 -11
@@ -125,11 +125,6 @@ static int nvmem_layout_create_device(streturn 0 ; } -static const struct of_device_id of_nvmem_layout_skip_table[] = { - { .compatible ="fixed-layout" , }, - {} -}; -static int nvmem_layout_bus_populate (struct nvmem_device *nvmem,struct device_node *layout_dn) {
@@ -142,12 +137,6 @@ static int nvmem_layout_bus_populate(strreturn 0 ; } -/* Fixed layouts are parsed manually somewhere else for now */ -if (of_match_node (of_nvmem_layout_skip_table, layout_dn)) { -pr_debug ("%s() - skipping %pOF node\n" , __func__, layout_dn); -return 0 ; - } -if (of_node_check_flag (layout_dn,OF_POPULATED_BUS )) {pr_debug ("%s() - skipping %pOF, already populated\n" , __func__, layout_dn);
drivers/nvmem/layouts/Makefile +1 -0
@@ -3,6 +3,7 @@ # Makefile for nvmem layouts. # +obj-$(CONFIG_NVMEM_LAYOUTS) += fixed-layout.o obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
drivers/nvmem/layouts/fixed-layout.c new file +58 -0
58 lines not on this page · raw message
include/linux/nvmem-provider.h +6 -0
@@ -214,6 +214,12 @@ static inline int nvmem_layout_register(static inline void nvmem_layout_unregister (struct nvmem_layout *layout) {} +static inline int nvmem_add_cells_from_dt (struct nvmem_device *nvmem, +struct device_node *np) +{ +return -EOPNOTSUPP ; +} +#endif /* CONFIG_NVMEM */ #if IS_ENABLED (CONFIG_NVMEM ) &&IS_ENABLED (CONFIG_OF )
Greg Kroah-Hartman
maintainerPATCH 176/271 rust_binder: do not query current thread for all ioctls
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alice Ryhl <[email protected]>
commit dd7aea9ee2091cfae3a5e376af87aa106d7735cd upstream.
The get_current_thread() method is currently called for every ioctl to ensure that a Thread struct exists for the thread calling into the driver. However, not all ioctls require a Thread object, so this means we are unnecessarily creating these objects in cases where we don't need to. If said thread does not invoke BINDER_THREAD_EXIT on exit, Binder's Thread struct stays around until the fd is closed. For long-lived processes the Thread object is effectively leaked.
Furthermore, when the BINDER_GET_NODE_DEBUG_INFO ioctl is invoked by libmemunreachable to ensure that objects reachable only through the Binder driver are not considered leaked, this is done from a fork of the process owning the fd, which means that it fails the group_leader check inside get_current_thread(). This results in EINVAL errors for this ioctl, causing libmemunreachable to report a false positive memory leak.
Thus, do not invoke get_current_thread() for ioctls that do not require it.
--- drivers/android/binder/process.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
drivers/android/binder/process.rs +8 -4
@@ -1586,6 +1586,10 @@ impl Process { cmd: u32, reader: &mut UserSliceReader, ) -> Result { +if cmd == uapi::BINDER_FREEZE { +return ioctl_freeze (reader); + } +let thread = this.get_current_thread ()?;match cmd { uapi::BINDER_SET_MAX_THREADS => this.set_max_threads (reader.read ()?),
@@ -1597,7 +1601,6 @@ impl Process { uapi::BINDER_ENABLE_ONEWAY_SPAM_DETECTION => { this.set_oneway_spam_detection_enabled (reader.read ()?) } - uapi::BINDER_FREEZE => ioctl_freeze(reader)?, _ => return Err(EINVAL), }Ok (())
@@ -1612,15 +1615,16 @@ impl Process { cmd: u32, data: UserSlice, ) -> Result { - let thread = this.get_current_thread()?; let blocking = (file.flags() & file::flags::O_NONBLOCK) == 0;match cmd { - uapi::BINDER_WRITE_READ => thread.write_read (data, blocking)?, + uapi::BINDER_WRITE_READ => this.get_current_thread ()?.write_read (data, blocking)?, uapi::BINDER_GET_NODE_DEBUG_INFO => this.get_node_debug_info (data)?, uapi::BINDER_GET_NODE_INFO_FOR_REF => this.get_node_info_from_ref (data)?, uapi::BINDER_VERSION => this.version (data)?, uapi::BINDER_GET_FROZEN_INFO =>get_frozen_status (data)?, - uapi::BINDER_GET_EXTENDED_ERROR => thread.get_extended_error (data)?, + uapi::BINDER_GET_EXTENDED_ERROR => { + this.get_current_thread ()?.get_extended_error (data)? + } _ =>return Err (EINVAL ), }Ok (())
Greg Kroah-Hartman
maintainerPATCH 177/271 serial: qcom-geni: fix TX DMA buffer flush
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Sebastian Götte <[email protected]>
commit e3c04834ae1ab5e9cfbe8ac54ec734aa4774249d upstream.
When transmit flushing a qcom-geni UART during an ongoing TX DMA, the UART gets stuck infinitely repeating corrupted TX DMA frames.
The DMA-mode uart_ops does not provide a flush_buffer callback, so an in-flight transfer can complete after serial core has reset the transmit kfifo, underflowing its length and resubmitting page-sized transfers indefinitely. Add one that stops the transfer and clears tx_remaining and tx_queued.
The stop path was also broken: it unmapped the buffer while the serial engine could still read it, and never reset the TX DMA state machine. Cancel the main sequencer command first, then reset the state machine and wait for it before unmapping. Drop the early return so a pending mapping is also cleaned up when the main command is inactive.
The bug can be triggered from userspace with a large write immediately followed by TCOFLUSH. A following tcdrain will hang forever. The bug was reproduced and this fix was validated on Arduino Uno Q (QRB2210) using /dev/ttyHS1.
--- drivers/tty/serial/qcom_geni_serial.c | 43 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-)
drivers/tty/serial/qcom_geni_serial.c +22 -21
@@ -158,6 +158,7 @@ static const struct uart_ops qcom_geni_ustatic struct uart_driver qcom_geni_console_driver;static struct uart_driver qcom_geni_uart_driver; +static void qcom_geni_serial_stop_tx_dma (struct uart_port *uport);static void __qcom_geni_serial_cancel_tx_cmd (struct uart_port *uport);static void qcom_geni_serial_cancel_tx_cmd (struct uart_port *uport);static int qcom_geni_serial_port_setup (struct uart_port *uport);
@@ -636,35 +637,34 @@ static unsigned int qcom_geni_serial_tx_return !readl (uport->membase +SE_GENI_TX_FIFO_STATUS ); } +static void qcom_geni_serial_flush_buffer_dma (struct uart_port *uport) +{ +struct qcom_geni_serial_port *port =to_dev_port (uport); + +qcom_geni_serial_stop_tx_dma (uport); + port->tx_remaining =0 ; + port->tx_queued =0 ; +} +static void qcom_geni_serial_stop_tx_dma (struct uart_port *uport) {struct qcom_geni_serial_port *port =to_dev_port (uport); -bool done; -if (!qcom_geni_serial_main_active (uport)) -return ; +if (qcom_geni_serial_main_active (uport)) +__qcom_geni_serial_cancel_tx_cmd (uport);if (port->tx_dma_addr) { +writel (1 , uport->membase +SE_DMA_TX_FSM_RST ); +if (!qcom_geni_serial_poll_bit (uport,SE_DMA_TX_IRQ_STAT , +TX_RESET_DONE , true)) +dev_err_ratelimited (uport->dev,"TX DMA reset failed" ); +writel (TX_RESET_DONE |TX_DMA_DONE , + uport->membase +SE_DMA_TX_IRQ_CLR ); +geni_se_tx_dma_unprep (&port->se, port->tx_dma_addr, port->tx_remaining); port->tx_dma_addr =0 ; - port->tx_remaining =0 ; } - -geni_se_cancel_m_cmd (&port->se); - - done =qcom_geni_serial_poll_bit (uport,SE_GENI_M_IRQ_STATUS , -M_CMD_CANCEL_EN , true); -if (!done) { -geni_se_abort_m_cmd (&port->se); - done =qcom_geni_serial_poll_bit (uport,SE_GENI_M_IRQ_STATUS , -M_CMD_ABORT_EN , true); -if (!done) -dev_err_ratelimited (uport->dev,"M_CMD_ABORT_EN not set" ); -writel (M_CMD_ABORT_EN , uport->membase +SE_GENI_M_IRQ_CLEAR ); - } - -writel (M_CMD_CANCEL_EN , uport->membase +SE_GENI_M_IRQ_CLEAR ); }static void qcom_geni_serial_start_tx_dma (struct uart_port *uport)
@@ -1180,7 +1180,7 @@ static void qcom_geni_serial_shutdown(stuart_port_unlock_irq (uport); } -static void qcom_geni_serial_flush_buffer (struct uart_port *uport) +static void qcom_geni_serial_flush_buffer_fifo (struct uart_port *uport) {qcom_geni_serial_cancel_tx_cmd (uport); }
@@ -1769,7 +1769,7 @@ static const struct uart_ops qcom_geni_c .request_port = qcom_geni_serial_request_port, .config_port = qcom_geni_serial_config_port, .shutdown = qcom_geni_serial_shutdown, - .flush_buffer = qcom_geni_serial_flush_buffer, + .flush_buffer = qcom_geni_serial_flush_buffer_fifo, .type = qcom_geni_serial_get_type, .set_mctrl = qcom_geni_serial_set_mctrl, .get_mctrl = qcom_geni_serial_get_mctrl,
@@ -1792,6 +1792,7 @@ static const struct uart_ops qcom_geni_u .request_port = qcom_geni_serial_request_port, .config_port = qcom_geni_serial_config_port, .shutdown = qcom_geni_serial_shutdown, + .flush_buffer = qcom_geni_serial_flush_buffer_dma, .type = qcom_geni_serial_get_type, .set_mctrl = qcom_geni_serial_set_mctrl, .get_mctrl = qcom_geni_serial_get_mctrl,
Greg Kroah-Hartman
maintainerPATCH 178/271 serial: sc16is7xx: enable THRI before filling TX FIFO
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luca Fresi <[email protected]>
commit 246ac114f485c2affb454240f3ea4fabfce22456 upstream.
sc16is7xx_handle_tx() currently requests the THRI enable only after it has filled the TX FIFO. The request is asynchronous because the IER update is performed later by reg_work.
The SC16IS7xx generates a THRI interrupt when the TX FIFO crosses its trigger level. If the FIFO drains past that level before reg_work enables THRI, the chip does not generate a new interrupt. Characters remain queued indefinitely even though the hardware FIFO is empty.
This was observed on an SC16IS752 while both UART channels were active. During the stall the software TX buffer remained non-empty while TXLVL reported 64 bytes free, LSR reported THR and transmitter empty, IER had THRI enabled, and IIR reported no interrupt pending.
Enable THRI synchronously before filling the FIFO so the threshold crossing cannot be missed.
--- drivers/tty/serial/sc16is7xx.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/tty/serial/sc16is7xx.c +3 -0
@@ -828,6 +828,9 @@ static void sc16is7xx_tx_proc(struct kthmsleep (port->rs485.delay_rts_before_send);guard (mutex)(&one->lock); +sc16is7xx_port_update (port,SC16IS7XX_IER_REG , +SC16IS7XX_IER_THRI_BIT , +SC16IS7XX_IER_THRI_BIT );sc16is7xx_handle_tx (port); }
Greg Kroah-Hartman
maintainerPATCH 179/271 serial: 8250_dma: Clear stale RX state on shutdown
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cunhao Lu <[email protected]>
commit e2fe6a0efecbef00e3ecc2db64dd5afa8c212b41 upstream.
serial8250_release_dma() terminates RX DMA and releases the channel, but leaves rx_running set. If the port is closed while an RX transfer is active, the stale state remains while rxchan is NULL until the channel is requested again on the next open.
The DesignWare BUSY workaround added by commit a7b9ce39fbe4 ("serial: 8250_dw: Ensure BUSY is deasserted") calls serial8250_rx_dma_flush() from the LCR write path during startup. This happens before serial8250_request_dma() obtains a new RX channel. On reopen, the stale rx_running state therefore makes the flush path pass a NULL channel to dmaengine_pause(), causing a kernel Oops.
Clear rx_running after terminating RX DMA, matching the TX cleanup. Also make the flush helper return if the DMA object or RX channel is not available so startup and teardown paths cannot pass a NULL channel to the DMAengine API.
--- drivers/tty/serial/8250/8250_dma.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
drivers/tty/serial/8250/8250_dma.c +7 -5
@@ -211,11 +211,12 @@ void serial8250_rx_dma_flush(struct uart {struct uart_8250_dma *dma = p->dma; -if (dma->rx_running) { -dmaengine_pause (dma->rxchan); -__dma_rx_complete (p); -dmaengine_terminate_async (dma->rxchan); - } +if (!dma || !dma->rxchan || !dma->rx_running) +return ; + +dmaengine_pause (dma->rxchan); +__dma_rx_complete (p); +dmaengine_terminate_async (dma->rxchan); }EXPORT_SYMBOL_GPL (serial8250_rx_dma_flush);
@@ -324,6 +325,7 @@ void serial8250_release_dma(struct uart_/* Release RX resources */ dmaengine_terminate_sync (dma->rxchan); + dma->rx_running =0 ;dma_free_coherent (dma->rxchan->device->dev, dma->rx_size, dma->rx_buf, dma->rx_addr);dma_release_channel (dma->rxchan);
Greg Kroah-Hartman
maintainerPATCH 180/271 serial: 8250_of: clear stuck empty-FIFO RX-timeout on LPC32xx
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ryan Wilbur <[email protected]>
commit 1423415471274abda87024967d7fe2206ceee0ea upstream.
The NXP LPC32xx UART (PORT_LPC3220) can latch an RX character-timeout interrupt while the RX FIFO is empty: IIR reports UART_IIR_RX_TIMEOUT (0x0c) but LSR.DR is clear. A character timeout is only cleared by reading RHR, but serial8250_rx_chars() reads RHR only when LSR.DR is set, so nothing ever clears the condition. The interrupt is level-triggered and re-fires immediately, so on a single-core ARM926 the resulting interrupt storm livelocks the CPU.
It is reproducible when userspace repeatedly opens the front-panel port (ttyS1): serial8250_do_set_termios() re-enables interrupts on unlock and the handler then spins forever with iir=0xcc lsr=0x60 ier=0x05, tripping the soft-lockup detector in serial8250_handle_irq_locked().
LPC32xx has no dedicated 8250 glue driver, it's driven by the generic 8250_of. Add a hardware specific handle_irq for PORT_LPC3220, wired up in of_platform_serial_setup() the same way fsl8250_handle_irq is installed. The handler follows dw8250_handle_irq(): on an RX timeout with an empty FIFO (LSR.DR and LSR.BI clear) it does one throwaway RHR read to clear the condition, then calls serial8250_handle_irq_locked(). No real received data is ever discarded, and it is a no-op on healthy UARTs which never report a timeout with DR clear.
This is the same class of bug already worked around in other 8250 drivers;
see commit 424d79183af0 ("serial: 8250_dw: Avoid "too much work" from bogus rx timeout interrupt") which reports the identical iir=0xcc/lsr=0x60. See alsoUART_RX_TIMEOUT_QUIRK in 8250_omap, and the note in 8250_bcm7271.
--- drivers/tty/serial/8250/8250_of.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
drivers/tty/serial/8250/8250_of.c +38 -0
@@ -81,6 +81,40 @@ static int of_platform_serial_clk_notifireturn NOTIFY_DONE ; } +static int lpc32xx_handle_irq (struct uart_port *port) +{ +struct uart_8250_port *up =up_to_u8250p (port); +unsigned int iir; +u16 status; + +guard (serial8250_rpm)(up); + + iir =serial_port_in (port,UART_IIR ); +if (iir &UART_IIR_NO_INT ) +return 0 ; + +guard (uart_port_lock_check_sysrq_irqsave)(port); + +/* +* The LPC32xx UART can assert an RX character-timeout interrupt while +* the RX FIFO is empty: IIR reports UART_IIR_RX_TIMEOUT but LSR.DR is +* clear. The timeout is only cleared by reading RHR, but the core RX +* path skips that read when the FIFO is empty, so the level-triggered +* IRQ re-fires forever and livelocks this single-core SoC. Do one +* throwaway RHR read to clear it; a healthy UART never reports a +* timeout with DR/BI clear, so no received data is ever discarded. +*/ +if ((iir &0x3f ) ==UART_IIR_RX_TIMEOUT ) { + status =serial_lsr_in (up); +if (!(status & (UART_LSR_DR |UART_LSR_BI ))) +serial_port_in (port,UART_RX ); + } + +serial8250_handle_irq_locked (port, iir); + +return 1 ; +} +/* * Fill a struct uart_port for a given device node */
@@ -173,6 +207,9 @@ static int of_platform_serial_setup(strucase PORT_NPCM : ret =npcm_setup (port);break ; +case PORT_LPC3220 : + port->handle_irq = lpc32xx_handle_irq; +break ;default :/* Nothing to do */ ret =0 ;
@@ -369,6 +406,7 @@ static struct platform_driver of_platformodule_platform_driver (of_platform_serial_driver); +MODULE_IMPORT_NS ("SERIAL_8250" );MODULE_AUTHOR ("Arnd Bergmann <[email protected]>" );MODULE_LICENSE ("GPL" );MODULE_DESCRIPTION ("Serial Port driver for Open Firmware platform devices" );
Greg Kroah-Hartman
maintainerPATCH 181/271 serial: amba-pl011: fix indefinite RS485 post-send delay
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit dcb2f7576ce460eb4f6b9048b7c266c8da5848a8 upstream.
The RS485 stop hrtimer is used both to drain the transmitter and to wait out delay_rts_after_send. The callback cannot tell the two apart, so it restarts the post-send delay on every expiry and the timer never stops.
Add a WAIT_AFTER_SEND_DELAY state so its expiry ends the stop sequence instead of restarting the delay.
--- drivers/tty/serial/amba-pl011.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/tty/serial/amba-pl011.c +6 -2
@@ -277,6 +277,7 @@ enum pl011_rs485_tx_state {WAIT_AFTER_RTS ,SEND ,WAIT_AFTER_SEND , + WAIT_AFTER_SEND_DELAY, };/*
@@ -1318,6 +1319,7 @@ static void pl011_rs485_tx_stop(struct ureturn ; }if (port->rs485.delay_rts_after_send >0 ) { + uap->rs485_tx_state =WAIT_AFTER_SEND_DELAY ;hrtimer_start (&uap->trigger_stop_tx,ms_to_ktime (port->rs485.delay_rts_after_send),HRTIMER_MODE_REL );
@@ -1383,7 +1385,8 @@ static void pl011_rs485_tx_start(struct uap->rs485_tx_state =SEND ;return ; } -if (uap->rs485_tx_state ==WAIT_AFTER_SEND ) { +if (uap->rs485_tx_state ==WAIT_AFTER_SEND || + uap->rs485_tx_state ==WAIT_AFTER_SEND_DELAY ) {hrtimer_try_to_cancel (&uap->trigger_stop_tx); uap->rs485_tx_state =SEND ;return ;
@@ -1450,7 +1453,8 @@ static enum hrtimer_restart pl011_triggeunsigned long flags;uart_port_lock_irqsave (&uap->port, &flags); -if (uap->rs485_tx_state ==WAIT_AFTER_SEND ) +if (uap->rs485_tx_state ==WAIT_AFTER_SEND || + uap->rs485_tx_state ==WAIT_AFTER_SEND_DELAY )pl011_rs485_tx_stop (uap);uart_port_unlock_irqrestore (&uap->port, flags);
Greg Kroah-Hartman
maintainerPATCH 182/271 serial: amba-pl011: cancel RS485 hrtimers after freeing IRQ
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 36672c8d7d14e9c43287528455d2c97b526ea6ad upstream.
The RS485 trigger hrtimers are embedded in the devm-managed port and can fire after it is freed. The IRQ handler can arm a timer, so free the IRQ first and then cancel both timers.
Complete the RS485 stop without arming a timer, and cancel the timers in remove() for the suspend-then-unbind path, where shutdown is not called.
This issue was found by an in-house static analysis tool.
--- drivers/tty/serial/amba-pl011.c | 52 ++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 17 deletions(-)
drivers/tty/serial/amba-pl011.c +35 -17
@@ -1302,11 +1302,30 @@ static inline bool pl011_dma_rx_running(#define pl011_dma_flush_buffer NULL#endif -static void pl011_rs485_tx_stop (struct uart_amba_port *uap) +static void pl011_rs485_tx_stop_now (struct uart_amba_port *uap) {struct uart_port *port = &uap->port;u32 cr; + cr =pl011_read (uap,REG_CR ); + +if (port->rs485.flags &SER_RS485_RTS_AFTER_SEND ) + cr &= ~UART011_CR_RTS ; +else + cr |=UART011_CR_RTS ; + +/* Disable the transmitter and reenable the transceiver */ + cr &= ~UART011_CR_TXE ; + cr |=UART011_CR_RXE ; +pl011_write (cr, uap,REG_CR ); + + uap->rs485_tx_state =OFF ; +} + +static void pl011_rs485_tx_stop (struct uart_amba_port *uap) +{ +struct uart_port *port = &uap->port; +if (uap->rs485_tx_state ==SEND ) uap->rs485_tx_state =WAIT_AFTER_SEND ;
@@ -1330,19 +1349,7 @@ static void pl011_rs485_tx_stop(struct uhrtimer_try_to_cancel (&uap->trigger_start_tx); } - cr =pl011_read (uap,REG_CR ); - -if (port->rs485.flags &SER_RS485_RTS_AFTER_SEND ) - cr &= ~UART011_CR_RTS ; -else - cr |=UART011_CR_RTS ; - -/* Disable the transmitter and reenable the transceiver */ - cr &= ~UART011_CR_TXE ; - cr |=UART011_CR_RXE ; -pl011_write (cr, uap,REG_CR ); - - uap->rs485_tx_state =OFF ; +pl011_rs485_tx_stop_now (uap); }static void pl011_stop_tx (struct uart_port *port)
@@ -2052,11 +2059,20 @@ static void pl011_shutdown(struct uart_ppl011_dma_shutdown (uap); -if ((port->rs485.flags &SER_RS485_ENABLED && uap->rs485_tx_state !=OFF )) -pl011_rs485_tx_stop (uap); -free_irq (uap->port.irq, uap); +/* +* free_irq() drains the UART interrupt handler, which can arm either +* timer. Cancel the timers afterwards to drain their callbacks too. +*/ +hrtimer_cancel (&uap->trigger_start_tx); +hrtimer_cancel (&uap->trigger_stop_tx); + +uart_port_lock_irq (port); +if (uap->rs485_tx_state !=OFF ) +pl011_rs485_tx_stop_now (uap); +uart_port_unlock_irq (port); +pl011_disable_uart (uap);/*
@@ -3035,6 +3051,8 @@ static void pl011_remove(struct amba_devstruct uart_amba_port *uap =amba_get_drvdata (dev);uart_remove_one_port (&amba_reg, &uap->port); +hrtimer_cancel (&uap->trigger_start_tx); +hrtimer_cancel (&uap->trigger_stop_tx);pl011_unregister_port (uap); }
Greg Kroah-Hartman
maintainerPATCH 183/271 serial: amba-pl011: synchronize DMA teardown
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 440915499231e9db1c361aa45bb702e8fd3b4a32 upstream.
dmaengine_terminate_all() does not wait for a running callback, so the TX callback can still touch the TX buffer after it is freed. The RX poll timer reads the RX buffers without the port lock.
Switch to dmaengine_terminate_sync() and delete the RX timer before freeing the buffers.
--- drivers/tty/serial/amba-pl011.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/tty/serial/amba-pl011.c +4 -4
@@ -1215,7 +1215,7 @@ static void pl011_dma_shutdown(struct uaif (uap->using_tx_dma) {/* In theory, this should already be done by pl011_dma_flush_buffer */ -dmaengine_terminate_all (uap->dmatx.chan); +dmaengine_terminate_sync (uap->dmatx.chan);if (uap->dmatx.queued) { dma_unmap_single(uap->dmatx.chan->device->dev, uap->dmatx.dma, uap->dmatx.len,
@@ -1228,12 +1228,12 @@ static void pl011_dma_shutdown(struct ua }if (uap->using_rx_dma) { -dmaengine_terminate_all (uap->dmarx.chan); +if (uap->dmarx.poll_rate) +timer_delete_sync (&uap->dmarx.timer); +dmaengine_terminate_sync (uap->dmarx.chan);/* Clean up the RX DMA */ pl011_dmabuf_free (uap->dmarx.chan, &uap->dmarx.dbuf_a,DMA_FROM_DEVICE );pl011_dmabuf_free (uap->dmarx.chan, &uap->dmarx.dbuf_b,DMA_FROM_DEVICE ); -if (uap->dmarx.poll_rate) -timer_delete_sync (&uap->dmarx.timer); uap->using_rx_dma = false; } }
Greg Kroah-Hartman
maintainerPATCH 184/271 staging: rtl8723bs: fix OOB read in rtw_get_wpa_ie()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 1c3e23e78862493e8cf1adad02b10ffcb8b9921c upstream.
rtw_get_wpa_ie() reads bytes at fixed offsets into a vendor-specific information element without checking that the element is long enough, causing an out-of-bounds read for a short trailing IE.
The function locates a vendor-specific IE (EID 221) with rtw_get_ie() and then compares a 4-byte OUI+type at pbuf + 2 and reads a 2-byte version word at pbuf + 6. Those accesses require the IE body to be at least 6 bytes, but rtw_get_ie() only guarantees that the element fits within the buffer; it does not enforce a minimum body length. A vendor-specific IE whose length byte is 0 to 5, placed at the end of the buffer, therefore makes these reads run past the end of the IE and past the end of the buffer itself.
The buffer holds information elements taken from received management frames and from the IE blob passed to rtw_cfg80211_set_wpa_ie(), which is kmemdup'd to its exact length, so the read can run off the end of the allocation.
The sibling helpers rtw_get_sec_ie(), rtw_get_wapi_ie() and rtw_get_wps_ie() in this file already reject too-short vendor-specific IEs before their OUI memcmp(); rtw_get_wpa_ie() was never brought in line with them, and needs a minimum of 6 rather than 4 bytes because of the version word. Add the missing length check.
--- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/staging/rtl8723bs/core/rtw_ieee80211.c +3 -0
@@ -371,6 +371,9 @@ unsigned char *rtw_get_wpa_ie(unsigned c pbuf =rtw_get_ie (pbuf,WLAN_EID_VENDOR_SPECIFIC , &len, limit_new);if (pbuf) { +if (len <6 ) + goto check_next_ie; +/* check if oui matches... */ if (memcmp ((pbuf +2 ), wpa_oui_type,sizeof (wpa_oui_type))) goto check_next_ie;
Greg Kroah-Hartman
maintainerPATCH 185/271 staging: rtl8723bs: fix OOB read in WMM_param_handler()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit ae21407350151bddfd4fea7aa39bd0643c0ca9d3 upstream.
WMM_param_handler() copies a fixed-size WMM parameter element out of a received information element without checking that the element is long enough, causing an out-of-bounds read for a short WMM IE.
The handler reads sizeof(struct WMM_para_element) (18) bytes at pIE->data + 6, so it requires pIE->length to be at least 24 (WLAN_WMM_LEN), but it never validates the length. Two of its three callers reach it after matching only the WMM OUI: OnAssocRsp() in rtw_mlme_ext.c matches a 6-byte OUI, and join_cmd_hdl() matches a 4-byte OUI, before calling the handler. A vendor-specific IE carrying the WMM OUI but a length between 6 and 23, placed in an association response or in the IE blob handed to join_cmd_hdl(), passes the OUI check and then makes the memcmp() and memcpy() at pIE->data + 6 read past the end of the element. OnAssocRsp() parses a frame received from the AP, so this is reachable from a remote peer.
The remaining caller in rtw_wlan_util.c already guards the handler with "pIE->length == WLAN_WMM_LEN". Move the equivalent check into the handler itself so every caller is covered; the sibling IE handlers in the same parsing loop (HT_caps_handler(), HT_info_handler(), ERP_IE_handler()) likewise bound their accesses by pIE->length.
--- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/staging/rtl8723bs/core/rtw_wlan_util.c +3 -0
@@ -693,6 +693,9 @@ int WMM_param_handler(struct adapter *pareturn false; } +if (pIE->length !=WLAN_WMM_LEN ) +return false; +if (!memcmp (&(pmlmeinfo->WMM_param), (pIE->data +6 ),sizeof (struct WMM_para_element )))return false;
Greg Kroah-Hartman
maintainerPATCH 186/271 staging: rtl8723bs: fix missing shared-key auth challenge length check
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Panagiotis Petrakopoulos <[email protected]>
commit 2c56ef658ac8c6bca36bc5574715e8f717207c6c upstream.
The WEP shared-key authentication handler uses the challenge-text element's attacker-controlled length without checking it against the fixed 128-byte chg_txt buffer.
In OnAuthClient() the length from rtw_get_ie() - up to 255 - is used to perform memcpy() into the 128-byte pmlmeinfo->chg_txt, so a malicious AP sending a malformed WLAN_EID_CHALLENGE element can overflow/underfill chg_txt by up to 127 bytes. It is reachable over the air, before association, during shared-key authentication. In the case of an overflow, the driver can write out of bounds. In the case of an underfill, the driver can echo stale buffer memory.
The challenge text is defined to be exactly 128 octets, which is already provided as the WLAN_AUTH_CHALLENGE_LEN define; require the element to be exactly that length before use.
--- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +1 -1
@@ -897,7 +897,7 @@ unsigned int OnAuthClient(struct adapter p =rtw_get_ie (pframe +WLAN_HDR_A3_LEN + _AUTH_IE_OFFSET_,WLAN_EID_CHALLENGE , (int *)&len, pkt_len -WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_); -if (!p) +if (!p || len !=WLAN_AUTH_CHALLENGE_LEN ) goto authclnt_fail;memcpy (pmlmeinfo->chg_txt, p +2 , len);
Greg Kroah-Hartman
maintainerPATCH 187/271 staging: rtl8723bs: validate monitor transmit frame lengths
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mariano Baragiola <[email protected]>
commit 6829665d050983907b560173e49dcc6c11cb2730 upstream.
rtw_cfg80211_monitor_if_xmit_entry() removes the radiotap header and then reads the 802.11 frame control field without checking that a base 802.11 header remains.
The data path also pulls the calculated 802.11, QoS and SNAP header span before confirming that the skb contains it. A truncated frame can therefore cause out-of-bounds reads or leave insufficient data for the Ethernet address writes.
Reject frames that do not contain the base 802.11 header and data frames that do not contain their complete calculated header span.
--- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +4 -0
@@ -2032,6 +2032,8 @@ static netdev_tx_t rtw_cfg80211_monitor_/* Skip the ratio tap header */ skb_pull (skb, rtap_len); +if (skb->len < dot11_hdr_len) + goto fail; dot11_hdr = (struct ieee80211_hdr *)skb->data; frame_control =le16_to_cpu (dot11_hdr->frame_control);
@@ -2044,6 +2046,8 @@ static netdev_tx_t rtw_cfg80211_monitor_ qos_len =2 ;if ((frame_control &0x0300 ) ==0x0300 ) dot11_hdr_len +=6 ; +if (skb->len < dot11_hdr_len + qos_len + snap_len) + goto fail;memcpy (dst_mac_addr, dot11_hdr->addr1,sizeof (dst_mac_addr));memcpy (src_mac_addr, dot11_hdr->addr2,sizeof (src_mac_addr));
Greg Kroah-Hartman
maintainerPATCH 188/271 misc: fastrpc: Fix initial memory allocation for Audio PD memory pool
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ekansh Gupta <[email protected]>
commit ab99eaafb0c4b412cfeb895a8cf091626e2bbd86 upstream.
The initial buffer allocated for the Audio PD memory pool is never added to the pool because pageslen is set to 0. As a result, the buffer is not registered with Audio PD and is never used, causing a memory leak. Audio PD immediately falls back to allocating memory from the remote heap since the pool starts out empty.
Fix this by setting pageslen to 1 so that the initially allocated buffer is correctly registered and becomes part of the Audio PD memory pool.
--- drivers/misc/fastrpc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/misc/fastrpc.c +4 -4
@@ -1370,7 +1370,9 @@ static int fastrpc_init_create_static_pr err =PTR_ERR (name); goto err; } - + inbuf.client_id = fl->client_id; + inbuf.namelen = init.namelen; + inbuf.pageslen =0 ;if (!fl->cctx->remote_heap) { err =fastrpc_remote_heap_alloc (fl, fl->sctx->dev, init.memlen, &fl->cctx->remote_heap);
@@ -1393,12 +1395,10 @@ static int fastrpc_init_create_static_pr goto err_map; } scm_done = true; + inbuf.pageslen =1 ; } } - inbuf.client_id = fl->client_id; - inbuf.namelen = init.namelen; - inbuf.pageslen =0 ; fl->pd =USER_PD ; args[0 ].ptr = (u64)(uintptr_t)&inbuf;
Greg Kroah-Hartman
maintainerPATCH 189/271 misc: fastrpc: fix channel ctx ref leak when session alloc fails
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anandu Krishnan E <[email protected]>
commit 310f7868399668c6d99d88acc9c4cf3462e69d5b upstream.
fastrpc_channel_ctx_get() is called in fastrpc_device_open() before fastrpc_session_alloc(). If session alloc fails, the error path returns -EBUSY without calling fastrpc_channel_ctx_put(), leaking the reference. Fix by adding the missing put.
--- drivers/misc/fastrpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/misc/fastrpc.c +1 -1
@@ -1673,7 +1673,7 @@ static int fastrpc_device_open(struct indev_err (&cctx->rpdev->dev,"No session available\n" );mutex_destroy (&fl->mutex);kfree (fl); - +fastrpc_channel_ctx_put (cctx);return -EBUSY ; }
Greg Kroah-Hartman
maintainerPATCH 190/271 misc: fastrpc: Remove buffer from list prior to unmap operation
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ekansh Gupta <[email protected]>
commit 6102ceb4eab845743ee57acd3863fbd06e93c927 upstream.
fastrpc_req_munmap_impl() is called to unmap any buffer. The buffer is getting removed from the list after it is unmapped from DSP. This can create potential race conditions if multiple threads invoke unmap concurrently, where one thread may remove the entry from the list while another thread's unmap operation is still ongoing.
Fix this by removing the buffer entry from the list before calling the unmap operation. If the unmap fails, the entry is re-added to the list so that userspace can retry the unmap, or alternatively, the buffer will be cleaned up during device release when the DSP process is torn down and all DSP-side mappings are freed along with remaining buffers in the list.
--- drivers/misc/fastrpc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/misc/fastrpc.c +10 -4
@@ -1889,9 +1889,6 @@ static int fastrpc_req_munmap_impl(struc &args[0]);if (!err) {dev_dbg (dev,"unmmap\tpt 0x%09lx OK\n" , buf->raddr); -spin_lock (&fl->lock); -list_del (&buf->node); -spin_unlock (&fl->lock);fastrpc_buf_free (buf); }else {dev_err (dev,"unmmap\tpt 0x%09lx ERROR\n" , buf->raddr);
@@ -1905,6 +1902,7 @@ static int fastrpc_req_munmap(struct fasstruct fastrpc_buf *buf =NULL , *iter, *b;struct fastrpc_req_munmap req;struct device *dev = fl->sctx->dev; +int err;if (copy_from_user (&req, argp,sizeof (req)))return -EFAULT ;
@@ -1912,6 +1910,7 @@ static int fastrpc_req_munmap(struct fasspin_lock (&fl->lock);list_for_each_entry_safe (iter, b, &fl->mmaps, node) {if ((iter->raddr == req.vaddrout) && (iter->size == req.size)) { +list_del (&iter->node); buf = iter;break ; }
@@ -1924,7 +1923,14 @@ static int fastrpc_req_munmap(struct fasreturn -EINVAL ; } -return fastrpc_req_munmap_impl (fl, buf); + err =fastrpc_req_munmap_impl (fl, buf); +if (err) { +spin_lock (&fl->lock); +list_add_tail (&buf->node, &fl->mmaps); +spin_unlock (&fl->lock); + } + +return err; }static int fastrpc_req_mmap (struct fastrpc_user *fl,char __user *argp)
Greg Kroah-Hartman
maintainerPATCH 191/271 misc: fastrpc: take fl->lock when moving mmaps on interrupted invoke
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junrui Luo <[email protected]>
commit b85a0e91d7d6cd06a53c881a46f749cfcef416a2 upstream.
When an invoke is interrupted by a signal, wait_for_completion_interruptible() returns -ERESTARTSYS and fastrpc_internal_invoke() moves every buffer from fl->mmaps onto cctx->invoke_interrupted_mmaps. This list_del()/list_add_tail() walk runs without holding fl->lock, the lock that serialises fl->mmaps in fastrpc_req_mmap() and fastrpc_req_munmap() everywhere else.
Take fl->lock around the move, matching every other fl->mmaps accessor.
--- drivers/misc/fastrpc.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/misc/fastrpc.c +2 -0
@@ -1305,10 +1305,12 @@ bail: }if (err == -ERESTARTSYS ) { +spin_lock (&fl->lock);list_for_each_entry_safe (buf, b, &fl->mmaps, node) {list_del (&buf->node);list_add_tail (&buf->node, &fl->cctx->invoke_interrupted_mmaps); } +spin_unlock (&fl->lock); }if (err)
Greg Kroah-Hartman
maintainerPATCH 192/271 misc: fastrpc: fix memory leak in fastrpc_channel_ctx_free
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eddie Lin <[email protected]>
commit 2fae94ee14f7fea11d3f95e10383a87c01d21518 upstream.
The 'ctx_idr' is initialized but never destroyed when the channel context is freed, leading to a memory leak. Add idr_destroy() to properly clean up the IDR resources.
--- drivers/misc/fastrpc.c | 1 + 1 file changed, 1 insertion(+)
drivers/misc/fastrpc.c +1 -0
@@ -492,6 +492,7 @@ static void fastrpc_channel_ctx_free(str cctx = container_of(ref, struct fastrpc_channel_ctx, refcount); +idr_destroy (&cctx->ctx_idr);kfree (cctx); }
Greg Kroah-Hartman
maintainerPATCH 193/271 ring-buffer: Fix crash passing ERR_PTR to kthread_stop()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Su <[email protected]>
commit 91542863abade2fd4f2b361991f5386ad9d19c8c upstream.
In test_ringbuffer()'s out_free cleanup loop, the check !rb_threads[cpu] only catches NULL entries and misses entries that hold an ERR_PTR.
rb_threads[] is static, so unassigned slots are NULL. But when kthread_run_on_cpu() fails for a cpu, it stores ERR_PTR(-ENOMEM) (or -EINTR) in rb_threads[cpu] before the creation loop jumps to out_free. That entry is non-NULL, so the old !ptr check does not break, and the cleanup proceeds to call kthread_stop() on the ERR_PTR. kthread_stop() then dereferences the bogus pointer, crashing the kernel during the late_initcall self-test.
crash logs:
BUG: kernel NULL pointer dereference, address: 000000000000001c Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 1 Comm: swapper/0 Not tainted 7.2.0-rc6-dirty #7 PREEMPT(lazy) RIP: 0010:kthread_stop+0x2e/0x220 RBX: fffffffffffffff4 CR2: 000000000000001c Call Trace: <TASK> test_ringbuffer+0x1ec/0x650 do_one_initcall+0x6c/0x2c0 kernel_init_freeable+0x21d/0x420 kernel_init+0x15/0x1c0 ret_from_fork+0x21b/0x320 </TASK> Kernel panic - not syncing: Fatal exception
--- kernel/trace/ring_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/ring_buffer.c +1 -1
@@ -8024,7 +8024,7 @@ static __init int test_ringbuffer(void) out_free:for_each_online_cpu (cpu) { -if (!rb_threads[cpu]) +if (IS_ERR_OR_NULL (rb_threads[cpu]))break ;kthread_stop (rb_threads[cpu]); }
Greg Kroah-Hartman
maintainerPATCH 194/271 mm/damon/lru_sort: error out for >10000 active_mem_bp
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit 06befa61c427e74319781e6f35a364cfc32dbae8 upstream.
damos_quota_score() can trigger division by zero if the target value is zero. DAMON_LRU_SORT lets users set the target value for the hot memory scheme via active_mem_bp parameter. It avoids setting it as the target value if the parameter value is zero. However, it also sets the cold memory scheme with a target value that is calculated as '10000 - active_mem_bp + 2'. Hence, if a user sets active_mem_bp 10002, the cold memory scheme's quota goal target value can be zero. As a result, division by zero can be triggered. Fix by returning an error when the user tries to start DAMON with >10000 active_mem_bp parameter value.
It makes no sense to set active_mem_bp with 10002. It also requires module parameters write permission to reproduce the issue. That said, the consequence is quite bad.
One reliable way to reproduce the issue is like below:
# cd /sys/module/damon_lru_sort/parameters
# echo 1000 > wmarks_high
# echo 995 > wmarks_mid
# echo 0 > wmarks_low
# echo 10002 > active_mem_bp
# echo Y > enabled
# dmesg -w
[...]
[ 597.421247] Oops: divide error: 0000 [#1] SMP NOPTI
[ 597.428848] RIP: 0010:damos_quota_score+0x6f/0x480This issue was discovered [1] by Sashiko.
--- mm/damon/lru_sort.c | 2 ++ 1 file changed, 2 insertions(+)
mm/damon/lru_sort.c +2 -0
@@ -234,6 +234,8 @@ static int damon_lru_sort_add_quota_goalif (!active_mem_bp)return 0 ; +if (10000 < active_mem_bp) +return -EINVAL ; goal =damos_new_quota_goal (DAMOS_QUOTA_ACTIVE_MEM_BP , active_mem_bp);if (!goal)return -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 195/271 mm/damon/ops-common: putback folios on invalid migrate nid
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: liyouhong <[email protected]>
commit 5deb65c34e682e7c5f5df417a70e223e8fcc5f5a upstream.
damon_pa_migrate() and damos_va_migrate() isolate folios into a local list and then call damon_migrate_pages(). When target_nid is invalid (including the scheme default NUMA_NO_NODE / -1), damon_migrate_pages() returns early without putting the folios back to the LRU.
Callers then discard the list head while those folios remain isolated with an extra reference taken by folio_isolate_lru(). The pages stay off the LRU for as long as the mapping exists (anon active+inactive counts drop while RSS does not), and the leftover references can pin the pages after the mapping is gone.
Put the folios back on the invalid-nid path so ignored migration requests still return them to the LRU.
--- mm/damon/ops-common.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
mm/damon/ops-common.c +8 -1
@@ -391,8 +391,15 @@ unsigned long damon_migrate_pages(structreturn nr_migrated;if (target_nid <0 || target_nid >=MAX_NUMNODES || - !node_state (target_nid,N_MEMORY )) + !node_state (target_nid,N_MEMORY )) { +while (!list_empty (folio_list)) { +struct folio *folio =lru_to_folio (folio_list); + +list_del (&folio->lru); +folio_putback_lru (folio); + }return nr_migrated; + } noreclaim_flag =memalloc_noreclaim_save ();
Greg Kroah-Hartman
maintainerPATCH 196/271 samples/damon/mtier: error out for zero quota goal target values
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit a16fd3ad9d89b05475864da97327870464611736 upstream.
Patch series "mm/damon: avoid division by zero from damos_quota_score()".
DAMON_SAMPLE_MTIER and DAMON_LRU_SORT allow the user to trigger division by zero in damos_quota_score(). Avoid it by adding parameters validation checks.
This patch (of 2):
damos_quota_score() can trigger division by zero if the target_value is zero. DAMON_SAMPLE_MTIER lets users set the target_value via node0_mem_{used,free}_bp parameters. It doesn't guard zero value case, though. As a result, users can trigger division by zero. Fix the issue by returning an error when the user tries to start DAMON with zero node0_mem_{used,free}_bp parameter values.
DAMON_SAMPLE_MTIER is just a sample module, but the consequence is quite bad. Also the zero node0_mem_free_bp parameter might look like a reasonable setup to some users. Hence, the issue might really happen in the real world.
One reliable way to reproduce the issue is like below:
# cd /sys/module/damon_sample_mtier/parameters
# echo 4096 > node0_start_addr
# echo 8192 > node0_end_addr
# echo 8192 > node1_start_addr
# echo 81920 > node1_end_addr
# echo 0 > node0_mem_free_bp
# echo Y > enabled
# dmesg -w
[...]
[18792.235916] Oops: divide error: 0000 [#1] SMP NOPTI
[...]
[18792.242787] RIP: 0010:damos_quota_score+0x6f/0x480
[...]This issue was discovered [1] by Sashiko.
--- samples/damon/mtier.c | 3 +++ 1 file changed, 3 insertions(+)
samples/damon/mtier.c +3 -0
@@ -154,6 +154,9 @@ static struct damon_ctx *damon_sample_mtif (!scheme) goto free_out;damon_set_schemes (ctx, &scheme,1 ); +/* zero target value causes division by zero in damos_quota_store() */ +if (!node0_mem_used_bp || !node0_mem_free_bp) + goto free_out; quota_goal = damos_new_quota_goal( promote ? DAMOS_QUOTA_NODE_MEM_USED_BP : DAMOS_QUOTA_NODE_MEM_FREE_BP,
Greg Kroah-Hartman
maintainerPATCH 197/271 mm/damon: adjust isolated pages stat for DAMOS_MIGRATE_{HOT,COLD}
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: SJ Park <[email protected]>
commit 1ec0e6b6f7321feb769f50d2f094a0aa6c2eda63 upstream.
Callers of migrate_pages() should adjust NR_MIGRATED_{ANON,FILE} for isolations and putback of the folios. That for migration succeeded folios is done by migrate_pages(), in migrate_folio_done(). That for MR_DEMOTION reason is an exception though.
DAMOS_MIGRATE_{HOT,COLD} call migrate_pages() but mistakenly not doing the stat adjustment. As a result, use of DAMOS_MIGRATE_{HOT,COLD} could corrupt the stat. It could confuse too_many_isolated(), make compaction and reclaim to behave in unexpected ways. The stat corruption can be reproduced and confirmed using DAMON user-space tool [1] on NUMA systems, like below.
$ numactl --hardware
available: 2 nodes (0-1)
[...]
$ sudo ./damo start --damos_action migrate_hot 1
$ sudo cat /proc/sys/vm/stat_refresh
$ sudo dmesg
[...]
[ 80.215554] vmstat_refresh: nr_isolated_anon -5578
[ 80.216842] vmstat_refresh: nr_isolated_file -34400This issue was discovered [2] by Sashiko.
--- mm/damon/ops-common.c | 4 ++++ mm/damon/paddr.c | 2 ++ mm/damon/vaddr.c | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-)
mm/damon/ops-common.c +4 -0
@@ -374,6 +374,8 @@ keep:while (!list_empty (folio_list)) { folio =lru_to_folio (folio_list);list_del (&folio->lru); +node_stat_sub_folio (folio,NR_ISOLATED_ANON + +folio_is_file_lru (folio));folio_putback_lru (folio); }
@@ -396,6 +398,8 @@ unsigned long damon_migrate_pages(structstruct folio *folio =lru_to_folio (folio_list);list_del (&folio->lru); +node_stat_sub_folio (folio,NR_ISOLATED_ANON + +folio_is_file_lru (folio));folio_putback_lru (folio); }return nr_migrated;
mm/damon/paddr.c +2 -0
@@ -277,6 +277,8 @@ static unsigned long damon_pa_migrate(stif (!folio_isolate_lru (folio)) goto put_folio; +node_stat_add_folio (folio,NR_ISOLATED_ANON + +folio_is_file_lru (folio));list_add (&folio->lru, &folio_list); put_folio: addr +=folio_size (folio);
mm/damon/vaddr.c +2 -1
@@ -627,7 +627,8 @@ static void damos_va_migrate_dests_add(s isolate:if (!folio_isolate_lru (folio))return ; - +node_stat_add_folio (folio,NR_ISOLATED_ANON + +folio_is_file_lru (folio));list_add (&folio->lru, &migration_lists[i]); }
Greg Kroah-Hartman
maintainerPATCH 198/271 ALSA: usb: Fix UAF at delayed release of MIDI2 EPs
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit f8a80cfb68613fb7e6452b66447dbc63f435d140 upstream.
The recent fix for UAF in ump_to_endpoint() caused another UAF because it tries to dereference the UMP endpoint object, but this might be executed at a delayed context where the endpoint has been already released.
Add private_free to clear the associated data for avoiding the further dereference for delayed releases.
--- sound/usb/midi2.c | 9 +++++++++ 1 file changed, 9 insertions(+)
sound/usb/midi2.c +9 -0
@@ -677,6 +677,14 @@ static int parse_midi_2_0_endpoints(strureturn 0 ; } +static void free_ump_private_data (struct snd_ump_endpoint *ump) +{ +struct snd_usb_midi2_ump *rmidi = ump->private_data; + +if (rmidi) + rmidi->ump =NULL ; +} +static void free_all_midi2_umps (struct snd_usb_midi2_interface *umidi) {struct snd_usb_midi2_ump *rmidi;
@@ -727,6 +735,7 @@ static int create_midi2_ump(struct snd_u ump->private_data = rmidi; ump->ops = &snd_usb_midi_v2_ump_ops; + ump->private_free = free_ump_private_data; rmidi->eps[STR_IN ] = ep_in; rmidi->eps[STR_OUT ] = ep_out;
Greg Kroah-Hartman
maintainerPATCH 199/271 ALSA: usx2y: bound the hwdep mmap fault offset
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
commit 2ca1eea3cd17930daffe9e429a7c89232036ec24 upstream.
snd_us428ctls_vm_fault() turns the faulting page offset into a kernel address with no bound of any kind:
offset = vmf->pgoff << PAGE_SHIFT; vaddr = (char *)(...)->us428ctls_sharedmem + offset; page = virt_to_page(vaddr); get_page(page); vmf->page = page; return 0;
snd_us428ctls_mmap() checks only the length of the mapping, never the offset, and us428ctls_sharedmem is a single page from alloc_pages_exact(). For a character device file_mmap_size_max() returns ULONG_MAX, so the mm layer imposes no ceiling either. Every page offset above zero resolves to a struct page outside the object, and the handler installs it into the caller's address space read-write; the vma is not marked read-only.
The caller picks the page frame with a single mmap() argument and gets read-write access to a page of kernel memory it does not own; an offset that lands in an unpopulated vmemmap region oopses instead.
A process that can open the hwdep node of an attached US-X2Y reaches this after loading the FPGA image through the same node; no capability check is involved.
On 7.2.0-rc5 (arm64), mmap() with a large offset:
Unable to handle kernel paging request at virtual address fffffdffc45d5ac8 pc : snd_us428ctls_vm_fault+0x68/0x140 [snd_usb_usx2y] Call trace: snd_us428ctls_vm_fault+0x68/0x140 [snd_usb_usx2y] __do_fault __handle_mm_fault handle_mm_fault el0_da
Reject any offset outside the shared region. The pcm hwdep handler in usx2yhwdeppcm.c computes its address the same way and needs the same bound.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- sound/usb/usx2y/usX2Yhwdep.c | 2 ++ sound/usb/usx2y/usx2yhwdeppcm.c | 2 ++ 2 files changed, 4 insertions(+)
sound/usb/usx2y/usX2Yhwdep.c +2 -0
@@ -25,6 +25,8 @@ static vm_fault_t snd_us428ctls_vm_faultvoid *vaddr; offset = vmf->pgoff <<PAGE_SHIFT ; +if (offset >=US428_SHAREDMEM_PAGES ) +return VM_FAULT_SIGBUS ; vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->us428ctls_sharedmem + offset; page =virt_to_page (vaddr);get_page (page);
sound/usb/usx2y/usx2yhwdeppcm.c +2 -0
@@ -672,6 +672,8 @@ static vm_fault_t snd_usx2y_hwdep_pcm_vmvoid *vaddr; offset = vmf->pgoff <<PAGE_SHIFT ; +if (offset >=USX2Y_HWDEP_PCM_PAGES ) +return VM_FAULT_SIGBUS ; vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset; vmf->page =virt_to_page (vaddr);get_page (vmf->page);
Greg Kroah-Hartman
maintainerPATCH 200/271 ALSA: FCP: fix OOB write in fcp_meter_ctl_get()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
commit 620f1e52a46f604635efd0fb78138afd6a513b5d upstream.
fcp_ioctl_set_meter_map() bounds the user-supplied Level Meter map size by the driver's own limit of 255
if (map.map_size < 1 || map.map_size > 255 || map.meter_slots < 1 || map.meter_slots > 255) return -EINVAL;
and passes it to fcp_add_new_ctl() as the control's channel count, where it is stored as elem->channels.
Every control read writes into struct snd_ctl_elem_value, whose integer array is declared long value[128], so the limit is 128, not 255. fcp_meter_ctl_get() stores one 64-bit word per channel into that array with no bound of its own:
for (i = 0; i < elem->channels; i++) {
int idx = private->meter_level_map[i];
int value = idx < 0 ? 0 : le32_to_cpu(resp[idx]);
ucontrol->value.integer.value[i] = value;
}snd_ctl_elem_read_user() serves that object from memdup_user(_control, sizeof(*control)), 1224 bytes on LP64 out of kmalloc-2048. offsetof(struct snd_ctl_elem_value, value) is 72, so element i is written at byte 72 + 8 * i and element 144 already lands past the allocation. At map_size 255 the last store ends at byte 2112, 888 bytes past the object and 64 bytes into the adjacent slab object. The stored words come from the device and meter_level_map[] selects which word lands in which slot, so extent and contents are both controlled.
The core does not catch this. snd_ctl_check_elem_info() is reached only from __snd_ctl_elem_info(), which snd_ctl_elem_read() calls under CONFIG_SND_CTL_DEBUG; without that option snd_ctl_skip_validation() is a compile-time true. __snd_ctl_add_replace() validates kcontrol->count and never inspects elem->channels.
Installing an oversized map needs CAP_SYS_RAWIO, but the control outlives the hwdep descriptor that created it, so the out-of-bounds stores are issued by any process able to read controls on /dev/snd/controlC0.
KASAN on 7.2.0-rc5 (arm64), triggered by an unprivileged control read:
BUG: KASAN: slab-out-of-bounds in fcp_meter_ctl_get Write of size 8 at addr ffff000017af04c8 by task fcp_trigger/185 __asan_store8 fcp_meter_ctl_get snd_ctl_elem_read snd_ctl_ioctl Allocated by task 185: memdup_user snd_ctl_ioctl The buggy address is located 0 bytes to the right of allocated 1224-byte region [ffff000017af0000, ffff000017af04c8)
Bound the map size by the ABI limit rather than by 255, and bound the store loop at the sink so it cannot run past the value array whatever elem->channels holds.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- sound/usb/fcp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
sound/usb/fcp.c +9 -1
@@ -129,6 +129,10 @@ struct fcp_data {#define FCP_SEGMENT_APP_GOLD 0 +#define FCP_MAX_METER_MAP_SIZE \ + (sizeof_field(struct snd_ctl_elem_value, value.integer.value) / \ + sizeof(long)) +/* Forward declarations */ static int fcp_init (struct usb_mixer_interface *mixer,void *step0_resp,void *step2_resp);
@@ -410,6 +414,9 @@ static int fcp_meter_ctl_get(struct snd_if (err <0 )return err; +if (WARN_ON_ONCE (elem->channels >FCP_MAX_METER_MAP_SIZE )) +return -EINVAL ; +/* copy & translate from resp[] using meter_level_map[] */ for (i =0 ; i < elem->channels; i++) {int idx = private->meter_level_map[i];
@@ -636,7 +643,8 @@ static int fcp_ioctl_set_meter_map(struc }/* Validate the map size */ -if (map.map_size <1 || map.map_size >255 || +if (map.map_size <1 || + map.map_size >FCP_MAX_METER_MAP_SIZE || map.meter_slots <1 || map.meter_slots >255 )return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 201/271 ALSA: hda/tas2781: fix ACPI reference handling
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
commit 8bec01c80e798eca1ae7863cf29bc6befd759db7 upstream.
tas2781_read_acpi() gets a reference to the matching ACPI device and then looks up its first physical device node. After taking a reference to the physical device, it immediately drops the ACPI device reference.
However, every later failure jumps to an error path that drops the ACPI device reference a second time. This unbalances the reference count and may prematurely release the ACPI device.
In addition, acpi_get_first_physical_node() may return NULL. Without a check, the driver passes the NULL physical device to the property helper calls and may dereference it.
Return -ENODEV when no physical device is associated with the ACPI node, and remove the duplicate acpi_dev_put() from the common error path.
--- sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
sound/hda/codecs/side-codecs/tas2781_hda_spi.c +2 -1
@@ -344,6 +344,8 @@ static int tas2781_read_acpi(struct tas2strscpy (p->dev_name, hid,sizeof (p->dev_name)); physdev =get_device (acpi_get_first_physical_node (adev));acpi_dev_put (adev); +if (!physdev) +return -ENODEV ; property ="ti,dev-index" ; ret =device_property_count_u32 (physdev, property);
@@ -386,7 +388,6 @@ static int tas2781_read_acpi(struct tas2 err:dev_err (p->dev,"read acpi error, ret: %d\n" , ret);put_device (physdev); -acpi_dev_put (adev);return ret; }
Greg Kroah-Hartman
maintainerPATCH 202/271 ALSA: us144mkii: re-anchor capture URBs on resubmission
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
commit 2615f0fb90df8cf5a96133ca4be74294ed288604 upstream.
capture_urb_complete() resubmits each capture URB without anchoring it:
usb_get_urb(urb); ret = usb_submit_urb(urb, GFP_ATOMIC);
Anchoring is a property of a submission, not of the URB. The giveback path calls usb_unanchor_urb() before urb->complete(), so an URB resubmitted from its own completion handler is off the anchor. The capture URBs are anchored once, at stream start, so from the first completion onward tascam->capture_anchor is empty.
tascam_free_urbs(), tascam_disconnect(), tascam_suspend() and the stop-work path all call usb_kill_anchored_urbs(&tascam->capture_anchor) to reap the capture URBs before anything is freed. With the anchor empty those calls return immediately and the URBs stay queued on the host controller.
tascam_free_urbs() then returns the capture transfer buffers with usb_free_coherent(), and snd_card_free() releases the snd_card allocation that embeds tascam (card->private_data). The controller completes the queued URBs afterwards, writing device-supplied data into the freed transfer buffer, and capture_urb_complete() dereferences the freed driver object.
KASAN on 7.2.0-rc5 (arm64):
BUG: KASAN: slab-use-after-free in dummy_timer Write of size 512 at addr ffff000015b62000 __asan_memcpy dummy_timer hrtimer_run_softirq Allocated by task 64: usb_alloc_coherent tascam_alloc_urbs tascam_probe Freed by task 170: usb_free_coherent tascam_free_urbs tascam_disconnect usb_unbind_interface BUG: KASAN: slab-use-after-free in capture_urb_complete Read of size 4 at addr ffff0000170ee878 Freed by task 170: release_card_device snd_card_free tascam_disconnect
Restore the usb_anchor_urb() between the reference count bump and the resubmission. That also makes the handler's usb_unanchor_urb() failure arm meaningful again and restores usb_kill_anchored_urbs() as a barrier on the disconnect, suspend and stop-work paths.
The anchoring was removed on the premise that the URB is already anchored from the initial submission, which does not hold once the first giveback has run.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- sound/usb/usx2y/us144mkii_capture.c | 1 + 1 file changed, 1 insertion(+)
sound/usb/usx2y/us144mkii_capture.c +1 -0
@@ -302,6 +302,7 @@ void capture_urb_complete(struct urb *ur }usb_get_urb (urb); +usb_anchor_urb (urb, &tascam->capture_anchor); ret =usb_submit_urb (urb,GFP_ATOMIC );if (ret <0 ) { dev_err_ratelimited(tascam->card->dev,
Greg Kroah-Hartman
maintainerPATCH 203/271 drm/v3d: Serialize the scheduler timeout handlers
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maíra Canal <[email protected]>
commit 4da94744707b27a3ae1197bdd7127da4505dc5b1 upstream.
V3D exposes several independent hardware queues (BIN, RENDER, TFU and CSD) but has only a single, global reset. A timeout on any one queue therefore has to stop, reset and restart the schedulers of every other queue as well. That makes concurrent timeout handlers unsafe.
reset_lock was never able to make them safe, as a driver-side lock can only cover the driver's &drm_sched_backend_ops.timedout_job callback. The scheduler handles the timed out job and its pending list around that callback, outside of the driver's control, so a global reset triggered by one queue can still interfere with another queue that is in the middle of handling a timeout of its own.
Consequently, if a reset happens in the CSD queue while a CL-intensive application is running, the global reset stops and restarts the CL queue's scheduler while that queue is handling a timeout of its own. As drm_sched_stop() and drm_sched_start() subtract and add the credits of every job sitting on the pending list of the scheduler they are called on, and as the CL queue's handler concurrently takes its job off that same list and puts it back, the stop and the start no longer see the same set of jobs. The CL queue is left with more credits in flight than its limit:
[ 327.302739] ------------[ cut here ]------------ [ 327.302744] WARNING: CPU: 2 PID: 43 at drivers/gpu/drm/scheduler/sched_main.c:102 drm_sched_run_job_work+0x238/0x4d0 [gpu_sched] [ 327.302884] CPU: 2 UID: 0 PID: 43 Comm: kworker/u16:1 Not tainted 6.18.39-v8-16k+ #3 PREEMPT [ 327.302889] Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT) [ 327.302893] Workqueue: v3d_bin drm_sched_run_job_work [gpu_sched] [ 327.302984] Call trace: [ 327.302987] drm_sched_run_job_work+0x238/0x4d0 [gpu_sched] (P) [ 327.302997] process_scheduled_works+0x180/0x3d0 [ 327.303010] worker_thread+0x268/0x3e8 [ 327.303016] kthread+0x140/0x250 [ 327.303022] ret_from_fork+0x10/0x20 [ 327.303031] ---[ end trace 0000000000000000 ]---
Quoted 1 line
From that point on, the credit count of the CL queue is broken, causing
a complete GPU hang and UI freeze.
The DRM scheduler already provides a mechanism to serialize the timeout handlers of different schedulers: an ordered workqueue passed as drm_sched_init()'s @timeout_wq parameter. By default, each scheduler queues its timeout work on the system workqueue, which runs the handlers concurrently. Give all of the queues a shared ordered workqueue instead, as recommended by the DRM scheduler documentation for hardware that has distinct queues but resets globally.
--- drivers/gpu/drm/v3d/v3d_drv.h | 6 ++++++ drivers/gpu/drm/v3d/v3d_sched.c | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-)
drivers/gpu/drm/v3d/v3d_drv.h +6 -0
@@ -181,6 +181,12 @@ struct v3d_dev {*/ struct mutex reset_lock; +/* Ordered workqueue shared by every queue's scheduler timeout work. +* V3D reset is global to all queues, so the timeout handlers must not +* run concurrently. +*/ +struct workqueue_struct *reset_wq; +/* Lock taken when creating and pushing the GPU scheduler * jobs, to keep the sched-fence seqnos in order. */
drivers/gpu/drm/v3d/v3d_sched.c +8 -1
@@ -838,6 +838,7 @@ v3d_queue_sched_init(struct v3d_dev *v3d .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = 1, .timeout = msecs_to_jiffies(500), + .timeout_wq = v3d->reset_wq, .dev = v3d->drm.dev, };
@@ -852,9 +853,13 @@ v3d_sched_init(struct v3d_dev *v3d) {int ret; + v3d->reset_wq =alloc_ordered_workqueue ("v3d_reset" ,0 ); +if (!v3d->reset_wq) +return -ENOMEM ; + ret =v3d_queue_sched_init (v3d, &v3d_bin_sched_ops,V3D_BIN ,"v3d_bin" );if (ret) -return ret; + goto fail; ret =v3d_queue_sched_init (v3d, &v3d_render_sched_ops,V3D_RENDER ,"v3d_render" );
@@ -897,4 +902,6 @@ v3d_sched_fini(struct v3d_dev *v3d)if (v3d->queue[q].sched.ready)drm_sched_fini (&v3d->queue[q].sched); } + +destroy_workqueue (v3d->reset_wq); }
Greg Kroah-Hartman
maintainerPATCH 204/271 perf/core: Fix group leader use-after-free after sibling detach
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aditya Chillara <[email protected]>
commit 42c5ca1f0a288a52878bd72a5595b08261057438 upstream.
perf_group_detach() handles leader and sibling detach differently. When the group leader is detached, all siblings are promoted to singleton events and their group_leader pointer is reset to themselves. When a sibling is detached, it is removed from the leader's sibling_list, but its group_leader pointer is left pointing at the old leader.
That is harmless when the sibling is being closed and freed immediately, as in the DETACH_DEAD path. It is not safe when the sibling is detached but kept alive, such as during CPU hotplug with DETACH_GROUP. In that case the sibling is removed from the context, while its file descriptor can still keep it alive.
A typical failing sequence is:
- A group contains leader L and sibling S.
- CPU hot-unplug detaches S with DETACH_GROUP, removing it from
L->sibling_list but leaving S->group_leader == L.
- L is later closed and freed.
- A PERF_IOC_FLAG_GROUP ioctl on S follows S->group_leader and dereferences the freed leader.
This was reproduced by running the perf event fuzzer, CPU hotplug, and a stress workload concurrently:
Unable to handle kernel paging request at virtual address 006b6b6b6b6b6cdb CPU: 2 PID: 12489 Comm: perf_fuzzer 6.18.7 PREEMPT pc : perf_ioctl+0x34c/0xc68 x20: ffffff89a3fa2c70 x8 : 6b6b6b6b6b6b6b6b Code: 943c4a0e 340047a0 f9404a94 f9411e88 (f940b908) Call trace: perf_ioctl+0x34c/0xc68 (P) __arm64_sys_ioctl+0xa0/0xf4 invoke_syscall+0x58/0xe4 el0_svc_common+0xa8/0xdc do_el0_svc+0x1c/0x28 el0_svc+0x40/0xc0 el0t_64_sync_handler+0x68/0xdc el0t_64_sync+0x1c4/0x1c8
The fault happened in perf_ioctl(), where perf_event_for_each() follows the stale group_leader pointer and perf_event_for_each_child() then dereferences the freed leader's context.
Fix the use-after-free by promoting the detached sibling to a singleton. Also fix __event_disable() cgroup accounting and event state change.
--- kernel/events/core.c | 67 ++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 30 deletions(-)
kernel/events/core.c +37 -30
@@ -2342,6 +2342,34 @@ static inline struct list_head *get_even &event->pmu_ctx->flexible_active; } +/* @sibling must already be unlinked from its old leader's sibling_list. */ +static void perf_promote_sibling_to_leader (struct perf_event *sibling, +struct perf_event_context *ctx, +int group_caps) +{ +/* +* Events that have PERF_EV_CAP_SIBLING require being part of +* a group and cannot exist on their own, schedule them out +* and move them into the ERROR state. Also see +* _perf_event_enable(), it will not be able to recover this +* ERROR state. +*/ +if (sibling->event_caps &PERF_EV_CAP_SIBLING ) +__event_disable (sibling, ctx,PERF_EVENT_STATE_ERROR ); + + sibling->group_leader = sibling; + sibling->group_caps = group_caps; + +if (sibling->attach_state &PERF_ATTACH_CONTEXT ) { +add_event_to_groups (sibling, ctx); + +if (sibling->state ==PERF_EVENT_STATE_ACTIVE ) +list_add_tail (&sibling->active_list,get_event_list (sibling)); + } + +perf_event__header_size (sibling); +} +static void perf_group_detach (struct perf_event *event) {struct perf_event *leader = event->group_leader;
@@ -2365,8 +2393,9 @@ static void perf_group_detach(struct per*/ if (leader != event) {list_del_init (&event->sibling_list); - event->group_leader->nr_siblings--; - event->group_leader->group_generation++; + leader->nr_siblings--; + leader->group_generation++; +perf_promote_sibling_to_leader (event, ctx, event->event_caps); goto out; }
@@ -2376,32 +2405,14 @@ static void perf_group_detach(struct per* to whatever list we are on. */ list_for_each_entry_safe (sibling, tmp, &event->sibling_list, sibling_list) { - -/* -* Events that have PERF_EV_CAP_SIBLING require being part of -* a group and cannot exist on their own, schedule them out -* and move them into the ERROR state. Also see -* _perf_event_enable(), it will not be able to recover this -* ERROR state. -*/ -if (sibling->event_caps &PERF_EV_CAP_SIBLING ) -__event_disable (sibling, ctx,PERF_EVENT_STATE_ERROR ); - - sibling->group_leader = sibling;list_del_init (&sibling->sibling_list);/* Inherit group flags from the previous leader */ - sibling->group_caps = event->group_caps; - -if (sibling->attach_state &PERF_ATTACH_CONTEXT ) { -add_event_to_groups (sibling, event->ctx); - -if (sibling->state ==PERF_EVENT_STATE_ACTIVE ) -list_add_tail (&sibling->active_list,get_event_list (sibling)); - } +perf_promote_sibling_to_leader (sibling, ctx, event->group_caps);WARN_ON_ONCE (sibling->ctx != event->ctx); } + event->nr_siblings =0 ; out:for_each_sibling_event (tmp, leader)
@@ -2591,12 +2602,7 @@ __perf_remove_from_context(struct perf_eif (flags &DETACH_DEAD ) state =PERF_EVENT_STATE_DEAD ; -event_sched_out (event, ctx); - -if (event->state >PERF_EVENT_STATE_OFF ) -perf_cgroup_event_disable (event, ctx); - -perf_event_set_state (event,min (event->state, state)); +__event_disable (event, ctx, state);if (flags &DETACH_GROUP )perf_group_detach (event);
@@ -2665,8 +2671,9 @@ static void __event_disable(struct perf_ enum perf_event_state state) {event_sched_out (event, ctx); -perf_cgroup_event_disable (event, ctx); -perf_event_set_state (event, state); +if (event->state >PERF_EVENT_STATE_OFF ) +perf_cgroup_event_disable (event, ctx); +perf_event_set_state (event,min (event->state, state)); }/*
Greg Kroah-Hartman
maintainerPATCH 205/271 tracing: Fix race between update_event_fields and, event_define_fields
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Wu <[email protected]>
commit c3730b8373bb5059d735509b9e6a00d7eb337d7c upstream.
The following sequence may leads race between event_define_fields() and update_event_fields():
CPU0 (loads module A) CPU1 (loads module B)
=============================== ===============================
load_module(A) load_module(B)
notifier_call_chain notifier_call_chain
trace_module_notify trace_module_notify
mutex_lock(&event_mutex) trace_event_update_all()
trace_module_add_events(A) down_write(&trace_event_sem)
__register_event(call_A)
__add_event_to_tracers(call_A)
event_define_fields(call_A)
for each f: list_for_each_entry(field,
list_add(&f->link, &class->fields, link)
&class->fields) field = class->fields->next;Where access to the class->fields is not protected by the event_mutex in trace_event_update_all().
This produces the following panic:
Unable to handle kernel access ... at virtual address 0000000000000018
pc : update_event_fields+0xf8/0x368
Call trace:
update_event_fields+0xf8/0x368
trace_event_update_all+0x7c/0x2b4
trace_module_notify+0x4c/0x1dc
notifier_call_chain+0x84/0x168
blocking_notifier_call_chain_robust+0x64/0xd4
load_module+0x10c8/0x123c
__arm64_sys_finit_module+0x230/0x31cFix by taking event_mutex in trace_event_update_all() before trace_event_sem.
--- kernel/trace/trace_events.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/trace/trace_events.c +2 -0
@@ -3566,6 +3566,7 @@ void trace_event_update_all(struct traceint last_i;int i; +mutex_lock (&event_mutex);down_write (&trace_event_sem);list_for_each_entry_safe (call, p, &ftrace_events, list) {/* events are usually grouped together with systems */
@@ -3604,6 +3605,7 @@ void trace_event_update_all(struct tracecond_resched (); }up_write (&trace_event_sem); +mutex_unlock (&event_mutex); } static bool event_in_systems(struct trace_event_call *call,
Greg Kroah-Hartman
maintainerPATCH 206/271 fbdev: bitblit: bound-check glyph index in bit_cursor()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rik van Riel <[email protected]>
commit e033cbf3975a8465f879ebd5989dc35b04423a4d upstream.
bit_cursor() fetches the glyph under the cursor with
c = scr_readw(vc_pos); src = vc_font.data + ((c & charmask) * w * height);
where charmask is 0x1ff when vc_hi_font_mask is set. The screen buffer value comes directly from scr_readw() and may be larger than the current font's glyph count.
Syzkaller triggers this via vcs_write(). The Call Trace shows vcs_write() in vc_screen.c writing an arbitrary 16-bit value with writev() to /dev/vcsa, which vcs_write_buf() in vc_screen.c stores via vcs_scr_writew() without checking charcount. The stored value is later read in bit_cursor() in bitblit.c.
When the font is changed from a font with 512 glyphs to a font with 256 glyphs, the screen buffer can retain characters with the high bit set from the previous mode, which could also produce the same out-of-bounds access.
BUG: KASAN: global-out-of-bounds in soft_cursor+0x378/0x6bc drivers/video/fbdev/core/softcursor.c:70 Read of size 16 at addr ffff800086c57970 Call Trace: soft_cursor+0x378/0x6bc drivers/video/fbdev/core/softcursor.c:70 bit_cursor+0xa90/0x1108 drivers/video/fbdev/core/bitblit.c:365 fbcon_cursor+0x344/0x498 drivers/video/fbdev/core/fbcon.c:1427 hide_cursor+0xdc/0x2d0 drivers/tty/vt/vt.c:883 update_region+0x100/0x18c drivers/tty/vt/vt.c:669 vcs_write+0x8ec/0xaf0 drivers/tty/vt/vc_screen.c:685
bit_putcs_aligned() and bit_putcs_unaligned() already clamp the glyph index to vc_font.charcount. Apply the same clamp in bit_cursor() after extracting the attribute and masking, before indexing fontdata.
The fix completes the bounds checking started in commit 18c4ef4e765a ("fbdev: bitblit: bound-check glyph index in bit_putcs*"), which missed the cursor path.
This change should be safe because the clamp reuses the existing contract from fbcon: charcount is maintained under console_lock in con_font_set() and fbcon_font_set(), and hi_font_mask is cleared when switching from 512 to 256 glyphs. When stale screen data with high bits remains after a font switch, or when vcs_write() stores an arbitrary value, clamping the index to 0 prevents the out-of-bounds read without changing cursor semantics — the same fallback bit_putcs uses.
--- drivers/video/fbdev/core/bitblit.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/video/fbdev/core/bitblit.c +7 -2
@@ -273,9 +273,14 @@ static void bit_cursor(struct vc_data *vif (!vc->vc_font.data)return ; - c =scr_readw ((u16 *) vc->vc_pos); + c =scr_readw ((u16 *) vc->vc_pos); attribute =get_attribute (info, c); - src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); + c &= charmask; + +/* Clamp to font size, same as bit_putcs_aligned() */ +if (c >= vc->vc_font.charcount) + c =0 ; + src = vc->vc_font.data + (c * (w * vc->vc_font.height));if (par->cursor_state.image.data != (const char *)src || par->cursor_reset) {
Greg Kroah-Hartman
maintainerPATCH 207/271 ring-buffer: Prevent subbuf order change when resizing is disabled
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
commit bf98d7b0d5a99991e47e66cee4eb1d3fa514be97 upstream.
Because ring_buffer_subbuf_order_set() frees buffer pages, we can't allow it when resizing is disabled. A non-consuming reader is at risk of use-after-free (rb_advance_iter()).
Return -EBUSY on resize_disabled, matching ring_buffer_resize() behaviour.
--- kernel/trace/ring_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/ring_buffer.c +1 -1
@@ -7168,7 +7168,7 @@ int ring_buffer_subbuf_order_set(struct cpu_buffer = buffer->buffers[cpu]; -if (cpu_buffer->mapped) { +if (atomic_read (&cpu_buffer->resize_disabled)) { err = -EBUSY ; goto error; }
Greg Kroah-Hartman
maintainerPATCH 208/271 tracing: Fix NULL pointer dereference in module event cache removal
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Su <[email protected]>
commit b69859204d4db3acd86c1c2dadcef0d77b451933 upstream.
A module-only event filter such as ":mod:foo" is cached with a NULL event_mod->match when foo has not been loaded. If a later write tries to remove a specific match from the same module, remove_cache_mod() passes the NULL cached match to strcmp(), causing a NULL pointer dereference.
The issue can be reproduced from userspace:
echo ':mod:trace_events_kunit_missing' > /sys/kernel/tracing/set_event echo '!foo_bar:mod:trace_events_kunit_missing' >> /sys/kernel/tracing/set_event
The second write must be a concatenation (">>") to not include O_TRUNC as that would cause ftrace_clear_events() to clear the cached modules lines.
The crash was reproduced on x86_64 QEMU while KUnit workers contended on the event tracing path:
BUG: kernel NULL pointer dereference, address: 0000000000000000 #PF: supervisor read access in kernel mode RIP: 0010:strcmp+0x10/0x30 Call Trace: __ftrace_set_clr_event_nolock+0x373/0x4a0 ftrace_set_clr_event+0xf0/0x180 ftrace_event_write+0xdf/0x110 vfs_write+0xf6/0x440 ksys_write+0x68/0xe0 do_syscall_64+0xf9/0x540 entry_SYSCALL_64_after_hwframe+0x77/0x7f
Check event_mod->match before comparing it, consistent with the existing NULL checks for the cached system and event fields. The mismatched removal continues to return -EINVAL; a broad cached module filter is removed with "!:mod:<module>".
--- kernel/trace/trace_events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_events.c +1 -1
@@ -945,7 +945,7 @@ static int remove_cache_mod(struct traceif (strcmp (event_mod->module, mod) !=0 )continue ; -if (match &&strcmp (event_mod->match, match) !=0 ) +if (match && (!event_mod->match ||strcmp (event_mod->match, match) !=0 ))continue ; if (system &&
Greg Kroah-Hartman
maintainerPATCH 209/271 mm/huge_memory: initialise workingset state before folio split
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matt Fleming <[email protected]>
commit aca1f2d5de17e138bc6c4859126b77e516b82541 upstream.
xas_try_split() adds __GFP_ACCOUNT for page-cache xa_nodes, but __folio_split() leaves the xa_state's xa_lru unset. That lets a live, memcg-charged xa_node exist without being linked into the mapping's shadow_nodes list_lru; when reclaim later walks the list_lru it trips VM_WARN_ON(!css_is_dying()).
Use mapping_set_update() to install both the workingset update callback and the shadow_nodes list_lru on the xa_state.
--- mm/huge_memory.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
mm/huge_memory.c +3 -1
@@ -4108,7 +4108,7 @@ static int __folio_split(struct folio *fgfp_t gfp; mapping = folio->mapping; - min_order =mapping_min_folio_order (folio->mapping); + min_order =mapping_min_folio_order (mapping);if (new_order < min_order) { ret = -EINVAL ; goto out;
@@ -4122,6 +4122,8 @@ static int __folio_split(struct folio *f goto out; } +mapping_set_update (&xas, mapping); +if (split_type ==SPLIT_TYPE_UNIFORM ) {xas_set_order (&xas, folio->index, new_order);xas_split_alloc (&xas, folio, old_order, gfp);
Greg Kroah-Hartman
maintainerPATCH 210/271 mm/huge_memory: fix huge_zero_pfn race
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes (ARM) <[email protected]>
commit 33192a26cddea7a7e4ca66e5c3eebd36fa8be2bb upstream.
Patch series "mm/huge_memory: fix huge_zero_pfn race", v2.
There is a subtle race in the reference-counted huge_zero_folio implementation.
The fast path atomic logic fails to account for the fact that the shrinker (which drops the final huge_zero_refcount pin) can overwrite huge_zero_pfn with the ~0UL sentinel value in shrink_huge_zero_folio_scan() after a racing get_huge_zero_folio() installed a valid value there.
This results in huge_zero_folio being correctly set but huge_zero_pfn being set incorrectly and thus is_huge_zero_pfn() and consequently is_huge_zero_pmd() will misidentify the huge zero folio as being an ordinary THP folio.
This can result in the huge zero folio being split and otherwise treated incorrectly.
The solution to this is very subtle as there is an atomic fast path, and thus ordering in weakly ordered architectures has to be treated very carefully.
The first commit fixes the issue by introducing a spinlock around huge_zero_[pfn, folio, refcount] write, with careful consideration paid to load/store ordering in the fast path. It is placed first and kept as small as possible so that it can be backported on its own.
The second commit is a pure cleanup which reworks the CONFIG_PERSISTENT_HUGE_ZERO_FOLIO logic to better separate the persistent logic from the dynamically allocated one.
This patch (of 2):
If !CONFIG_PERSISTENT_HUGE_ZERO_FOLIO, the huge_zero_folio is refcounted by huge_zero_refcount and returned by mm_get_huge_zero_folio().
When the caller is done with the huge zero page, its reference count is decremented. Only a shrinker can set the reference count to zero.
A race can unfortunately occur between a shrinker decrementing the reference count to zero and a concurrent page fault.
This is because shrink_huge_zero_folio_scan() might, if very unlucky, be preempted between setting huge_zero_refcount to zero and writing an invalid value.
During this time get_huge_zero_folio() could write to huge_zero_pfn before shrink_huge_zero_folio_scan() resumes.
In this event the huge zero folio will be persistently misidentified causing the THP code path to be entered inappropriately for the huge zero folio:
CPU 0 CPU 1
=======================================|=================================
shrink_huge_zero_folio_scan() |
atomic_cmpxchg() sets refcount to 0 |
xchg() sets huge_zero_folio to NULL | get_huge_zero_folio()
| | atomic_inc_not_zero() -> zero
preempted for a long time | Allocate new huge zero folio
| | Write valid huge_zero_folio
v | Write valid huge_zero_pfn Overwrite huge_zero_pfn with ~0UL <--- Invalid overwrite!
This results in is_huge_zero_pfn() and is_huge_zero_pmd() incorrectly returning false for a huge zero page which could result in issues like the huge zero folio being incorrectly split.
Note that the issue is with huge_zero_pfn not huge_zero_folio, as get_huge_zero_folio() uses cmpxchg() gated on huge_zero_folio being NULL with a retry loop and shrink_huge_zero_folio_scan() uses xchg() to set huge_zero_folio.
Fix the issue by introducing a spinlock, huge_zero_lock, to prevent concurrent write of huge_zero_folio, huge_zero_pfn and huge_zero_refcount.
There needs to be significant care taken here to ensure correctness:
The fast path in get_huge_zero_folio() uses atomic_inc_not_zero(), which is outside of the critical section, and means huge zero allocation is gated on zero huge_zero_refcount.
The fast path doesn't use huge_zero_lock, so the critical section is irrelevant to it.
So invariants are required - huge_zero_refcount MUST:
* Only be set in the huge_zero_lock critical section to ensure serialisation of huge_zero_pfn, huge_zero_folio and huge_zero_refcount writes.
* Be set non-zero only AFTER huge_zero_[pfn, folio] are set to valid values so installation of the huge zero folio on read page fault ensures concurrent is_huge_zero_*() calls correctly identify the huge zero folio.
* Be set zero only BEFORE huge_zero_[pfn, folio] are set to NULL and ~0UL respectively, and atomically.
Establish these by:
* Only setting huge_zero_refcount to zero or an absolute value in the huge_zero_lock critical section in get_huge_zero_folio() and shrink_huge_zero_folio_scan(), and always updating atomically there and elsewhere.
* Using atomic_set_release(&huge_zero_refcount) in get_huge_zero_folio() after huge_zero_[pfn, folio] are set. This is paired with atomic_inc_not_zero() to ensure atomic_inc_not_zero() only observes a non-zero value if huge_zero_[pfn, folio] are set.
* Using atomic_cmpxchg() in shrink_huge_zero_folio_scan() (as before) to ensure that it is set zero only when equal to 1 and set atomically.
* atomic_cmpxchg() being fully ordered ensures this is done prior to huge_zero_[folio, pfn] being set to NULL and ~0UL respectively.
Eliminate the retry loop in get_huge_zero_folio() as the atomic_cmpxchg() in shrink_huge_zero_folio_scan() is now performed under the lock, and replace with an equally locked atomic_inc() to set the reference count should the caller be raced on huge zero folio installation.
folio_put() naturally implies a full memory barrier so its ordering is maintained correctly.
The huge zero folio also cannot be released except when the shrinker does so as it is non-LRU and non-rmappable.
Note that only the huge zero shrinker (via shrink_huge_zero_folio_scan()) can actually set huge_zero_refcount to zero, which is the count of mm's which have at least one huge zero folio installed plus one shrinker pin.
Additionally convert a BUG_ON() to a VM_WARN_ON_ONCE().
--- mm/huge_memory.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-)
mm/huge_memory.c +29 -14
@@ -40,6 +40,7 @@#include <linux/pgalloc.h> #include <linux/pgalloc_tag.h> #include <linux/pagewalk.h> +#include <linux/cleanup.h> #include <asm/tlb.h> #include "internal.h"
@@ -75,6 +76,7 @@ static unsigned long deferred_split_scanstatic bool split_underused_thp = true;static atomic_t huge_zero_refcount; +static DEFINE_SPINLOCK (huge_zero_lock);struct folio *huge_zero_folio__read_mostly ;unsigned long huge_zero_pfn__read_mostly = ~0UL ;unsigned long huge_anon_orders_always__read_mostly ;
@@ -221,7 +223,8 @@ unsigned long __thp_vma_allowable_ordersstatic bool get_huge_zero_folio (void ) {struct folio *zero_folio; -retry: + +/* Paired with atomic_set_release(). */ if (likely (atomic_inc_not_zero (&huge_zero_refcount)))return true;
@@ -234,17 +237,22 @@ retry: }/* Ensure zero folio won't have large_rmappable flag set. */ folio_clear_large_rmappable (zero_folio); -preempt_disable (); -if (cmpxchg (&huge_zero_folio,NULL , zero_folio)) { -preempt_enable (); + +/* Paired with critical section in shrink_huge_zero_folio_scan(). */ +spin_lock (&huge_zero_lock); +if (huge_zero_folio) { +/* Somebody else already installed it. */ +atomic_inc (&huge_zero_refcount); +spin_unlock (&huge_zero_lock);folio_put (zero_folio); - goto retry; +return true; } +WRITE_ONCE (huge_zero_folio, zero_folio);WRITE_ONCE (huge_zero_pfn,folio_pfn (zero_folio)); +/* Paired with atomic_inc_not_zero(). +1 for shrinker pin. */ +atomic_set_release (&huge_zero_refcount,2 ); +spin_unlock (&huge_zero_lock); -/* We take additional reference here. It will be put back by shrinker */ -atomic_set (&huge_zero_refcount,2 ); -preempt_enable ();count_vm_event (THP_ZERO_PAGE_ALLOC );return true; }
@@ -294,15 +302,22 @@ static unsigned long shrink_huge_zero_fostatic unsigned long shrink_huge_zero_folio_scan (struct shrinker *shrink,struct shrink_control *sc) { -if (atomic_cmpxchg (&huge_zero_refcount,1 ,0 ) ==1 ) { -struct folio *zero_folio =xchg (&huge_zero_folio,NULL ); -BUG_ON (zero_folio ==NULL ); +struct folio *zero_folio; + +/* Paired with critical section in get_huge_zero_folio(). */ + scoped_guard(spinlock, &huge_zero_lock) { +/* Paired with atomic_inc_not_zero() in get_huge_zero_folio(). */ +if (atomic_cmpxchg (&huge_zero_refcount,1 ,0 ) !=1 ) +return 0 ; + + zero_folio = huge_zero_folio; +VM_WARN_ON_ONCE (!zero_folio); +WRITE_ONCE (huge_zero_folio,NULL );WRITE_ONCE (huge_zero_pfn, ~0UL ); -folio_put (zero_folio); -return HPAGE_PMD_NR ; } -return 0 ; +folio_put (zero_folio); +return HPAGE_PMD_NR ; }static struct shrinker *huge_zero_folio_shrinker;
Greg Kroah-Hartman
maintainerPATCH 211/271 net: phy: mediatek: fix TX blink masks using the RX bits
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ahmed Naseef <[email protected]>
commit f684c514f7965385dae21f2535f99938e73ec1af upstream.
MTK_GPHY_LED_TX_BLINK_SET and MTK_2P5GPHY_LED_TX_BLINK_SET are built from the RX blink bits instead of the TX ones, so both TX masks are identical to their RX counterparts. The TX bits they should be using, MTK_PHY_LED_BLINK_{10,100,1000,2500}TX, are otherwise only referenced by the per-speed branch of mtk_phy_led_hw_ctrl_set().
A TX trigger selected without a link trigger therefore programs the RX blink bits, and the LED blinks on received traffic. The masks are also used to decode the blink register in mtk_phy_led_hw_ctrl_get(), which as a result cannot tell the two triggers apart: an RX-only configuration reads back as RX and TX, and a TX-only configuration reads back as neither.
--- drivers/net/phy/mediatek/mtk.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/net/phy/mediatek/mtk.h +4 -4
@@ -53,15 +53,15 @@#define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ MTK_PHY_LED_BLINK_100RX | \ MTK_PHY_LED_BLINK_10RX) -#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ - MTK_PHY_LED_BLINK_100RX | \ - MTK_PHY_LED_BLINK_10RX) +#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000TX | \ + MTK_PHY_LED_BLINK_100TX | \ + MTK_PHY_LED_BLINK_10TX)#define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \ MTK_GPHY_LED_ON_SET)#define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ MTK_GPHY_LED_RX_BLINK_SET) -#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ +#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500TX | \ MTK_GPHY_LED_TX_BLINK_SET)#define MTK_PHY_LED_STATE_FORCE_ON 0
Greg Kroah-Hartman
maintainerPATCH 212/271 net: smc: fix splice entry lifetime imbalance in smc_rx_splice
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daming Li <[email protected]>
commit 5d9686af2976741bbd79b150d1c9e60b81e7f12e upstream.
smc_rx_splice() passes pages to splice_to_pipe() before taking the references that cover the lifetime of each splice entry. In the VM-backed RMB path, splice_to_pipe() may drop unqueued entries through smc_rx_spd_release(), while queued entries are released later via the pipe buffer callback.
The old post-splice accounting also derives the number of queued VM pages from an offset mutated while building the descriptor, and a multi-page splice pairs one sock_hold() with multiple sock_put() calls.
Take the page and socket references for every candidate entry before splice_to_pipe(), and drop the matching private state, page reference, and socket reference from smc_rx_spd_release() for entries that never get queued. This fixes a refcount imbalance that can underflow page refcounts and trigger a use-after-free.
--- net/smc/smc_rx.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-)
net/smc/smc_rx.c +10 -9
@@ -150,7 +150,12 @@ static const struct pipe_buf_operationsstatic void smc_rx_spd_release (struct splice_pipe_desc *spd,unsigned int i) { +struct smc_spd_priv *priv = (struct smc_spd_priv *)spd->partial[i].private; +struct sock *sk = &priv->smc->sk; + +kfree (priv);put_page (spd->pages[i]); +sock_put (sk); } static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
@@ -209,6 +214,10 @@ static int smc_rx_splice(struct pipe_ino offset =0 ; } } +for (i =0 ; i < nr_pages; i++) { +get_page (pages[i]); +sock_hold (&smc->sk); + } spd.nr_pages_max = nr_pages; spd.nr_pages = nr_pages; spd.pages = pages;
@@ -217,16 +226,8 @@ static int smc_rx_splice(struct pipe_ino spd.spd_release = smc_rx_spd_release; bytes =splice_to_pipe (pipe, &spd); -if (bytes >0 ) { -sock_hold (&smc->sk); -if (!lgr->is_smcd && smc->conn.rmb_desc->is_vm) { -for (i =0 ; i <PAGE_ALIGN (bytes + offset) /PAGE_SIZE ; i++) -get_page (pages[i]); - }else { -get_page (smc->conn.rmb_desc->pages); - } +if (bytes >0 )atomic_add (bytes, &smc->conn.splice_pending); - }kfree (priv);kfree (partial);kfree (pages);
Greg Kroah-Hartman
maintainerPATCH 213/271 ipv6: prevent in6_dev_get() from resurrecting inet6_dev
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
commit 0e243671bc7b8eaf00f83dd2f4367436dc0cff98 upstream.
in6_dev_get() reads dev->ip6_ptr under RCU and then unconditionally increments its refcount. Device teardown can clear the pointer and drop the last reference between these operations. The increment then resurrects an object whose RCU free has already been queued, so callers can use it after it is freed.
Use refcount_inc_not_zero() and return NULL when the object has already reached zero. RCU keeps the memory accessible through the attempted reference acquisition, and a successful increment pins the object for the caller.
An independent run on the exact unpatched 6f5156d7a31a (v7.2-rc3) kernel reproduced the invalid reference acquisition as UID 1000:
refcount_t: addition on 0; use-after-free.
ip6_mc_source+0xef4/0x17e0
It was followed by the corresponding reference underflow in ip6_mc_source(). The supplied trace from the same unpatched revision additionally shows the access after the RCU read-side section ends:
BUG: KASAN: slab-use-after-free in mutex_lock+0x76/0xe0 Write of size 8 at addr ffff888015b50240 by task poc/1219
Bug found and triaged by OpenAI Security Research and validated by Trail of Bits.
--- include/net/addrconf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
include/net/addrconf.h +2 -2
@@ -405,8 +405,8 @@ static inline struct inet6_dev *in6_dev_rcu_read_lock (); idev =rcu_dereference (dev->ip6_ptr); -if (idev) -refcount_inc (&idev->refcnt); +if (idev && !refcount_inc_not_zero (&idev->refcnt)) + idev =NULL ;rcu_read_unlock ();return idev; }
Greg Kroah-Hartman
maintainerPATCH 214/271 net/dibs: Correct freeing of dmb_clientid_arr
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandra Winter <[email protected]>
commit 9e6869be49064915edb6c8776b27c376cfdb0df5 upstream.
A dibs device interrupt handler can be active after dibs_dev_del() and may still access dmb_clientid_arr. (UAF)
In case of a failure in dibs_dev_add() being called by dibs_lo_dev_probe() dmb_clientid_arr is freed twice (double free).
Free dmb_clientid_arr in dibs_dev_release() after last reference is gone. Note that allocating in dibs_dev_add() instead of dibs_dev_alloc() is ok for now, because no dmbs can be registered before dibs_dev_add().
--- drivers/dibs/dibs_loopback.c | 1 - drivers/dibs/dibs_main.c | 14 ++++---------- 2 files changed, 4 insertions(+), 11 deletions(-)
drivers/dibs/dibs_loopback.c +0 -1
@@ -335,7 +335,6 @@ static int dibs_lo_dev_probe(void)return 0 ; err_reg: -kfree (dibs->dmb_clientid_arr);/* pairs with dibs_dev_alloc() */ put_device (&dibs->dev);kfree (ldev);
drivers/dibs/dibs_main.c +4 -10
@@ -128,6 +128,7 @@ static void dibs_dev_release(struct devi dibs = container_of(dev, struct dibs_dev, dev); +kfree (dibs->dmb_clientid_arr);kfree (dibs); }
@@ -194,12 +195,13 @@ int dibs_dev_add(struct dibs_dev *dibs) ret =device_add (&dibs->dev);if (ret) - goto free_client_arr; +return ret; ret =sysfs_create_group (&dibs->dev.kobj, &dibs_dev_attr_group);if (ret) {dev_err (&dibs->dev,"sysfs_create_group failed for dibs_dev\n" ); - goto err_device_del; +device_del (&dibs->dev); +return ret; }mutex_lock (&dibs_dev_list.mutex);mutex_lock (&clients_lock);
@@ -214,13 +216,6 @@ int dibs_dev_add(struct dibs_dev *dibs)mutex_unlock (&dibs_dev_list.mutex);return 0 ; - -err_device_del: -device_del (&dibs->dev); -free_client_arr: -kfree (dibs->dmb_clientid_arr); -return ret; - }EXPORT_SYMBOL_GPL (dibs_dev_add);
@@ -247,7 +242,6 @@ void dibs_dev_del(struct dibs_dev *dibs)mutex_unlock (&dibs_dev_list.mutex);device_del (&dibs->dev); -kfree (dibs->dmb_clientid_arr); }EXPORT_SYMBOL_GPL (dibs_dev_del);
Greg Kroah-Hartman
maintainerPATCH 215/271 net/x25: fix use-after-free of the socket by its timers
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
commit 2195424c3da2ef1829a63b807e3a900a90e57d85 upstream.
The x25 timers are armed with mod_timer() and cancelled with timer_delete(), so a pending timer holds no reference on the socket and a cancel does not wait for a callback already running on another CPU.
x25_heartbeat_expiry() also rearms unconditionally, so it can reinstall sk->sk_timer after __x25_destroy_socket() has passed its cancel point. The following __sock_put() frees the socket while the timer is still queued, and the next expiry uses freed memory. KASAN reports a slab-use-after-free on the kmalloc-2k object freed by close().
timer_delete_sync() cannot be used here: x25_heartbeat_expiry() and x25_timer_expiry() both reach the cancels from inside the timer they would wait on, through __x25_destroy_socket() and x25_disconnect().
Arm the timers with sk_reset_timer() and cancel them with sk_stop_timer() so that an armed timer owns a reference, and release it in both expiry handlers. Rearm the heartbeat only while sk_hashed(sk) is still true, since __x25_destroy_socket() unlinks the socket before dropping it. Arm the deferred destroy timer the same way and drop its reference in x25_destroy_timer().
Reproduced on net with KASAN, with the heartbeat period shortened so the window recurs. With this patch the reproducer no longer triggers a report and /proc/net/x25 drains.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- net/x25/af_x25.c | 4 ++-- net/x25/x25_timer.c | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-)
net/x25/af_x25.c +2 -2
@@ -362,6 +362,7 @@ static void x25_destroy_timer(struct timstruct sock *sk =timer_container_of (sk, t, sk_timer);x25_destroy_socket_from_timer (sk); +sock_put (sk); }/*
@@ -397,9 +398,8 @@ static void __x25_destroy_socket(structif (sk_has_allocations (sk)) {/* Defer: outstanding buffers */ - sk->sk_timer.expires = jiffies +10 *HZ ; sk->sk_timer.function = x25_destroy_timer; -add_timer (&sk->sk_timer); +sk_reset_timer (sk, &sk->sk_timer, jiffies +10 *HZ ); }else {/* drop last reference so sock_put will free */ __sock_put (sk);
net/x25/x25_timer.c +16 -9
@@ -36,45 +36,45 @@ void x25_init_timers(struct sock *sk)void x25_start_heartbeat (struct sock *sk) { -mod_timer (&sk->sk_timer, jiffies +5 *HZ ); +sk_reset_timer (sk, &sk->sk_timer, jiffies +5 *HZ ); }void x25_stop_heartbeat (struct sock *sk) { -timer_delete (&sk->sk_timer); +sk_stop_timer (sk, &sk->sk_timer); }void x25_start_t2timer (struct sock *sk) {struct x25_sock *x25 =x25_sk (sk); -mod_timer (&x25->timer, jiffies + x25->t2); +sk_reset_timer (sk, &x25->timer, jiffies + x25->t2); }void x25_start_t21timer (struct sock *sk) {struct x25_sock *x25 =x25_sk (sk); -mod_timer (&x25->timer, jiffies + x25->t21); +sk_reset_timer (sk, &x25->timer, jiffies + x25->t21); }void x25_start_t22timer (struct sock *sk) {struct x25_sock *x25 =x25_sk (sk); -mod_timer (&x25->timer, jiffies + x25->t22); +sk_reset_timer (sk, &x25->timer, jiffies + x25->t22); }void x25_start_t23timer (struct sock *sk) {struct x25_sock *x25 =x25_sk (sk); -mod_timer (&x25->timer, jiffies + x25->t23); +sk_reset_timer (sk, &x25->timer, jiffies + x25->t23); }void x25_stop_timer (struct sock *sk) { -timer_delete (&x25_sk (sk)->timer); +sk_stop_timer (sk, &x25_sk (sk)->timer); }unsigned long x25_display_timer (struct sock *sk)
@@ -108,7 +108,7 @@ static void x25_heartbeat_expiry(struct sock_flag(sk, SOCK_DEAD))) {bh_unlock_sock (sk);x25_destroy_socket_from_timer (sk); -return ; + goto out; }break ;
@@ -120,8 +120,14 @@ static void x25_heartbeat_expiry(structbreak ; } restart_heartbeat: -x25_start_heartbeat (sk); +/* Do not rearm once __x25_destroy_socket() has unlinked the socket: +* it is past its cancel point and owns the teardown from there on. +*/ +if (sk_hashed (sk)) +x25_start_heartbeat (sk);bh_unlock_sock (sk); +out: +sock_put (sk); }/*
@@ -166,4 +172,5 @@ static void x25_timer_expiry(struct time }else x25_do_timer_expiry (sk);bh_unlock_sock (sk); +sock_put (sk); }
Greg Kroah-Hartman
maintainerPATCH 216/271 net: devmem: prevent net-iov / page mixing
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pavel Begunkov <[email protected]>
commit 53a43508ee332d8bffe40590c3d189c92a551f9f upstream.
We should either have net_iov or page backed frags in a single skb, otherwise it blows up down the stack. Don't allow mixing in zerocopy_fill_skb_from_devmem().
--- net/core/datagram.c | 3 +++ 1 file changed, 3 insertions(+)
net/core/datagram.c +3 -0
@@ -712,6 +712,9 @@ zerocopy_fill_skb_from_devmem(struct sk_size_t virt_addr, size, off;struct net_iov *niov; +if (i &&skb_frags_readable (skb)) +return -EFAULT ; +/* Devmem filling works by taking an IOVEC from the user where the * iov_addrs are interpreted as an offset in bytes into the dma-buf to * send from. We do not support other iter types.
Greg Kroah-Hartman
maintainerPATCH 217/271 NTB: ntb_netdev: Preserve RX queue depth on allocation failure
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit d2121faf133ac3bf9531b53a7e21273649a08517 upstream.
ntb_netdev_rx_handler() hands the received skb to the network stack before allocating its replacement. If the allocation fails, nothing is reposted. Every failure therefore takes one buffer out of the RX queue while the interface remains up, and enough failures eventually stall reception.
A retry path could refill the queue later, but ntb_netdev has none. Allocate the replacement first instead. If that fails, drop the packet and repost the same skb. This keeps the queue full and lets packet delivery resume as soon as memory is available again.
--- drivers/net/ntb_netdev.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
drivers/net/ntb_netdev.c +8 -7
@@ -127,8 +127,8 @@ static void ntb_netdev_rx_handler(struct {struct ntb_netdev_queue *q = qp_data;struct ntb_netdev *dev = q->ntdev; +struct sk_buff *skb, *new_skb;struct net_device *ndev; -struct sk_buff *skb;int rc; ndev = dev->ndev;
@@ -144,6 +144,12 @@ static void ntb_netdev_rx_handler(struct goto enqueue_again; } + new_skb =netdev_alloc_skb (ndev, ndev->mtu +ETH_HLEN ); +if (!new_skb) { + ndev->stats.rx_dropped++; + goto enqueue_again; + } +skb_put (skb, len); skb->protocol =eth_type_trans (skb, ndev); skb->ip_summed =CHECKSUM_NONE ;
@@ -157,12 +163,7 @@ static void ntb_netdev_rx_handler(struct ndev->stats.rx_bytes += len; } - skb =netdev_alloc_skb (ndev, ndev->mtu +ETH_HLEN ); -if (!skb) { - ndev->stats.rx_errors++; - ndev->stats.rx_frame_errors++; -return ; - } + skb = new_skb; enqueue_again: rc =ntb_transport_rx_enqueue (qp, skb, skb->data, ndev->mtu +ETH_HLEN );
Greg Kroah-Hartman
maintainerPATCH 218/271 netfilter: bridge: release template ct on non-IP path
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit d45cc8020d7c0a9f01dee42ff5c40bc14c9af72f upstream.
A bridge nftables ct zone set rule can attach a conntrack template to an skb before nf_ct_bridge_pre() sees it. For non-IPv4 and non-IPv6 EtherTypes, nf_ct_bridge_pre() currently overwrites skb->_nfct with IP_CT_UNTRACKED without releasing the existing template reference.
That makes the per-cpu template, and any temporary templates allocated for concurrent use, unreachable and leaks memory until the host runs out of slab.
Reset the skb conntrack state before marking the frame untracked so the existing template reference is dropped on the non-IP path.
--- net/bridge/netfilter/nf_conntrack_bridge.c | 1 + 1 file changed, 1 insertion(+)
net/bridge/netfilter/nf_conntrack_bridge.c +1 -0
@@ -280,6 +280,7 @@ static unsigned int nf_ct_bridge_pre(voi ret =nf_ct_br_defrag6 (skb, &bridge_state);break ;default : +nf_reset_ct (skb);nf_ct_set (skb,NULL ,IP_CT_UNTRACKED );return NF_ACCEPT ; }
Greg Kroah-Hartman
maintainerPATCH 219/271 netfilter: nf_conntrack: defer invalid log until after unlock
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
commit 2d19b95c9723001f214f7a47d67b09f46238f200 upstream.
TCP and SCTP conntrack paths can emit invalid-packet logs while ct->lock is still held.
When invalid logging is routed to nfnetlink_log and conntrack export is enabled, the log path can re-enter conntrack netlink glue and dump the same conntrack again. Protocol attribute dumping may take ct->lock, so logging while holding that lock can deadlock.
Defer the TCP invalid logs by storing only the minimal log context while ct->lock is held and emitting the log after unlocking. Also make the TCP timeout-lowering invalid path return whether a log is needed, then emit that log after unlocking.
Do the same for the SCTP invalid state-transition log that can be reached while ct->lock is held.
Add a lockdep assertion to nf_ct_l4proto_log_invalid() so future callers that log invalid conntracks while holding ct->lock are caught outside TCP and SCTP as well.
--- net/netfilter/nf_conntrack_proto.c | 6 + net/netfilter/nf_conntrack_proto_sctp.c | 12 +- net/netfilter/nf_conntrack_proto_tcp.c | 132 +++++++++++++++++++++----------- 3 files changed, 102 insertions(+), 48 deletions(-)
net/netfilter/nf_conntrack_proto.c +6 -0
@@ -79,6 +79,12 @@ void nf_ct_l4proto_log_invalid(const strstruct net *net;va_list args; +/* nfnetlink_log may re-enter conntrack attribute dumping and try to +* take ct->lock again via helpers such as tcp_to_nlattr(), so invalid +* conntrack logs must only be emitted after dropping ct->lock. +*/ +lockdep_assert_not_held (&ct->lock); + net =nf_ct_net (ct);if (likely (net->ct.sysctl_log_invalid ==0 ))return ;
net/netfilter/nf_conntrack_proto_sctp.c +8 -4
@@ -336,10 +336,12 @@ int nf_conntrack_sctp_packet(struct nf_cstruct sctphdr _sctph;const struct sctp_chunkhdr *sch;struct sctp_chunkhdr _sch; +bool log_invalid = false;u_int32_t offset, count;unsigned int *timeouts;unsigned long map[256 /sizeof (unsigned long )] = {0 };bool ignore = false; +u8 invalid_type =0 ;if (sctp_error (skb, dataoff, state))return -NF_ACCEPT ;
@@ -451,10 +453,8 @@ int nf_conntrack_sctp_packet(struct nf_c/* Invalid */ if (new_state ==SCTP_CONNTRACK_MAX ) { -nf_ct_l4proto_log_invalid (skb, ct, state, -"Invalid, old_state %d, dir %d, type %d" , - old_state, dir, sch->type); - + log_invalid = true; + invalid_type = sch->type; goto out_unlock; }
@@ -529,6 +529,10 @@ int nf_conntrack_sctp_packet(struct nf_c out_unlock:spin_unlock_bh (&ct->lock); +if (log_invalid) +nf_ct_l4proto_log_invalid (skb, ct, state, +"Invalid, old_state %d, dir %d, type %d" , + old_state, dir, invalid_type); out:return -NF_ACCEPT ; }
net/netfilter/nf_conntrack_proto_tcp.c +88 -44
225 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 220/271 net: atlantic: free stranded TX buffers on ring deinit
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yangyu Chen <[email protected]>
commit 452636ea5410a96e02ebaaf80b21e3620b98e0dd upstream.
aq_vec_deinit() drains the TX rings with a single aq_ring_tx_clean() call, which frees at most AQ_CFG_TX_CLEAN_BUDGET (256) descriptors and stops at hw_head, which no longer moves once aq_vec_stop() has stopped the hardware and NAPI. Completed descriptors beyond the budget and everything still posted in [hw_head, sw_tail) keep their skb or xdp_frame when the interface goes down: aq_vec_ring_free() then frees the buffer ring and the references are lost for good.
Today this is a silent memory leak on every interface down under TX/XDP_TX load. With the conversion of the RX path to page_pool posted for net-next it becomes much more visible: XDP_TX frames carry fragment references on the RX ring's page_pool, so a single stranded frame keeps the pool's inflight count above zero forever. page_pool_destroy() then never completes, the pool is leaked together with its pages, and "page_pool_release_retry() stalled pool shutdown" is warned every 60 seconds from that point on, on every ifdown, XDP detach or ring resize under XDP_TX load.
Bring back aq_ring_tx_deinit() as it was before the removal and use it for teardown again, with one extension: TX rings can hold xdp_frames nowadays, so release those too. They are returned with xdp_return_frame() since this runs in process context.
--- drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 29 +++++++++++++++++++++++ drivers/net/ethernet/aquantia/atlantic/aq_ring.h | 1 drivers/net/ethernet/aquantia/atlantic/aq_vec.c | 2 - 3 files changed, 31 insertions(+), 1 deletion(-)
drivers/net/ethernet/aquantia/atlantic/aq_ring.c +29 -0
@@ -336,6 +336,35 @@ out:return !!budget; } +void aq_ring_tx_deinit (struct aq_ring_s *self) +{ +if (!self) +return ; + +for (; self->sw_head != self->sw_tail; + self->sw_head =aq_ring_next_dx (self, self->sw_head)) { +struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; +struct device *ndev =aq_nic_get_dev (self->aq_nic); + +if (buff->is_mapped) { +if (buff->is_sop) { +dma_unmap_single (ndev, buff->pa, buff->len, +DMA_TO_DEVICE ); + }else { +dma_unmap_page (ndev, buff->pa, buff->len, +DMA_TO_DEVICE ); + } + } + +if (buff->is_eop) { +if (buff->skb) +dev_kfree_skb_any (buff->skb); +else if (buff->xdpf) +xdp_return_frame (buff->xdpf); + } + } +} +static void aq_rx_checksum (struct aq_ring_s *self,struct aq_ring_buff_s *buff,struct sk_buff *skb)
drivers/net/ethernet/aquantia/atlantic/aq_ring.h +1 -0
@@ -199,6 +199,7 @@ void aq_ring_update_queue_state(struct avoid aq_ring_queue_wake (struct aq_ring_s *ring);void aq_ring_queue_stop (struct aq_ring_s *ring);bool aq_ring_tx_clean (struct aq_ring_s *self); +void aq_ring_tx_deinit (struct aq_ring_s *self);int aq_xdp_xmit (struct net_device *dev,int num_frames,struct xdp_frame **frames,u32 flags); int aq_ring_rx_clean(struct aq_ring_s *self,
drivers/net/ethernet/aquantia/atlantic/aq_vec.c +1 -1
@@ -275,7 +275,7 @@ void aq_vec_deinit(struct aq_vec_s *selffor (i =0U ; self->tx_rings > i; ++i) { ring = self->ring[i]; -aq_ring_tx_clean (&ring[AQ_VEC_TX_ID ]); +aq_ring_tx_deinit (&ring[AQ_VEC_TX_ID ]);aq_ring_rx_deinit (&ring[AQ_VEC_RX_ID ]); }
Greg Kroah-Hartman
maintainerPATCH 221/271 net: atlantic: free RX pages of consumed but not refilled buffers
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yangyu Chen <[email protected]>
commit e8e7471ef686b6c002218fee9671cc61992ae01a upstream.
aq_ring_rx_deinit() only walks [sw_head, sw_tail), the region posted to hardware. Since the page reuse strategy was added, a cleaned RX buffer keeps its page (and its DMA mapping) in the ring for reuse, and refill is batched: aq_ring_rx_fill() returns early until AQ_CFG_RX_REFILL_THRES slots are free. Slots that were consumed but not yet reposted therefore sit in the complementary [sw_tail, sw_head) gap with a live page, and the deinit walk never visits them: up to a refill batch worth of pages and DMA mappings leak on every interface down.
Walk the whole ring instead and release whatever is still there. Also bail out if the buffer ring is already gone: a partial aq_ptp_ring_alloc() failure frees the ring but leaves aq_nic set, so aq_ptp_ring_deinit() still gets here on the unwind path.
--- drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-)
drivers/net/ethernet/aquantia/atlantic/aq_ring.c +18 -4
@@ -926,15 +926,29 @@ err_exit:void aq_ring_rx_deinit (struct aq_ring_s *self) { -if (!self) +unsigned int i; + +if (!self || !self->buff_ring)return ; -for (; self->sw_head != self->sw_tail; - self->sw_head =aq_ring_next_dx (self, self->sw_head)) { -struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; +/* Release every page still owned by the ring. +* +* Walking [sw_head, sw_tail) is not enough: refill is batched +* (aq_ring_rx_fill() waits for AQ_CFG_RX_REFILL_THRES free slots), +* so slots that were cleaned but not yet reposted accumulate in the +* [sw_tail, sw_head) gap, and they keep their page for reuse. Walk +* the whole ring and release whatever is left. +*/ +for (i =0 ; i < self->size; i++) { +struct aq_ring_buff_s *buff = &self->buff_ring[i]; + +if (!buff->rxdata.page) +continue ;aq_free_rxpage (&buff->rxdata,aq_nic_get_dev (self->aq_nic)); } + + self->sw_head = self->sw_tail; }void aq_ring_free (struct aq_ring_s *self)
Greg Kroah-Hartman
maintainerPATCH 222/271 net/sched: act_ct: fix sk_buff leak when the header checks reject a packet
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunjung Ko <[email protected]>
commit 8a7ed561671aa6a911a2de99e59ef670a4d0b1df upstream.
tcf_ct_handle_fragments() runs its header sanity checks before handing anything to the defragmentation engine:
if (family == NFPROTO_IPV4) err = tcf_ct_ipv4_is_fragment(skb, &frag); else err = tcf_ct_ipv6_is_fragment(skb, &frag); if (err || !frag) return err;
tcf_ct_ipv4_is_fragment() returns -EINVAL or -ENOMEM; tcf_ct_ipv6_is_fragment() adds -EPROTO when ipv6_find_hdr() fails. None of them frees or queues the skb, so on that path the caller still owns it.
tcf_ct_act() however funnels every non-zero return into the ownership-transfer exit:
err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag); if (err) goto out_frag; ... out_frag: if (err != -EINPROGRESS) tcf_action_inc_drop_qstats(&c->common); return TC_ACT_CONSUMED;
TC_ACT_CONSUMED means the action took ownership of the skb, so no caller frees it - sch_handle_ingress(), sch_handle_egress() and tcf_qevent_handle() all deliberately skip the free for that verdict. The skb is therefore orphaned: one sk_buff plus its data buffer is leaked per malformed packet, unbounded. Note the drop counter is already incremented for these errors, so the statistics claim a drop that never happens.
Three different ownership states reach out_frag: today - the skb may be queued by the defrag engine (-EINPROGRESS), already freed by nf_ct_handle_fragments(), or still owned by us. Tell the caller which of those it is, and free the packet ourselves in the last case, which restores the TC_ACT_SHOT behaviour that predated the Fixes: commit.
Reproduced on v7.2-rc6 with a 54-byte frame carrying a 40-byte IPv6 header with nexthdr = 0 (hop-by-hop) and nothing after it, on a clsact ingress chain with "action ct". kmemleak reports one leaked 232-byte skbuff_head_cache object plus its 704-byte data buffer per packet; with this patch it reports none.
--- net/sched/act_ct.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-)
net/sched/act_ct.c +25 -4
@@ -840,8 +840,15 @@ static int tcf_ct_ipv6_is_fragment(strucreturn 0 ; } +/* On error, tells the caller whether it still owns @skb and must free it +* itself. @skb is ours only when the header checks below reject the packet +* before it is handed to the defragmentation engine; once nf_ct_handle_ +* fragments() has been called the skb is either queued (-EINPROGRESS) or has +* already been freed by it. +*/ static int tcf_ct_handle_fragments (struct net *net,struct sk_buff *skb, -u8 family,u16 zone,bool *defrag) +u8 family,u16 zone,bool *defrag, +bool *skb_is_ours) {enum ip_conntrack_info ctinfo;struct tc_skb_cb cb;
@@ -859,8 +866,12 @@ static int tcf_ct_handle_fragments(struc err =tcf_ct_ipv4_is_fragment (skb, &frag); else err =tcf_ct_ipv6_is_fragment (skb, &frag); -if (err || !frag) +if (err) { + *skb_is_ours = true;return err; + } +if (!frag) +return 0 ; cb = *tc_skb_cb (skb); err =nf_ct_handle_fragments (net, skb, zone, family, &proto, &cb.mru);
@@ -977,6 +988,7 @@ TC_INDIRECT_SCOPE int tcf_ct_act(structint nh_ofs, err, retval;struct tcf_ct_params *p;bool add_helper = false; +bool skb_is_ours = false;bool skip_add = false;bool defrag = false;struct nf_conn *ct;
@@ -1012,9 +1024,18 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct*/ nh_ofs =skb_network_offset (skb);skb_pull_rcsum (skb, nh_ofs); - err =tcf_ct_handle_fragments (net, skb, family, p->zone, &defrag); -if (err) + err =tcf_ct_handle_fragments (net, skb, family, p->zone, &defrag, + &skb_is_ours); +if (err) { +/* The skb is still ours only when the header checks rejected +* it; returning TC_ACT_CONSUMED for such a packet would leak +* it, since no caller frees an skb it was told it no longer +* owns. +*/ +if (skb_is_ours) + goto drop; goto out_frag; + } err =nf_ct_skb_network_trim (skb, family);if (err)
Greg Kroah-Hartman
maintainerPATCH 223/271 net/sched: act_gact, act_police: range check the fallback control action
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunjung Ko <[email protected]>
commit 883b56ae58fe657d8497806c7059646e9ba6dbd0 upstream.
tcf_action_check_ctrlact() range checks the primary control action:
if (!opcode) ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0;
TC_ACT_VALUE_MAX is TC_ACT_TRAP, so kernel-internal verdicts above it cannot be set that way. But act_gact and act_police each carry a second, independent control action supplied by user space that never reaches that helper - TCA_GACT_PROB.paction and TCA_POLICE_RESULT. Both only reject TC_ACT_GOTO_CHAIN, so any other value is stored verbatim and returned verbatim from the action.
In particular user space can store TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is deliberately not part of the UAPI value range. That verdict tells every caller the action took ownership of the skb, so nobody frees it: sch_handle_ingress(), sch_handle_egress() and tcf_qevent_handle() all deliberately skip the free for it. The result is one leaked sk_buff plus its data buffer per packet traversing the filter, unbounded, for all traffic on the chain including kernel-generated packets.
Both are trivially deterministic. act_gact clamps tcfg_pval to >= 1, so with pval = 1 gact_determ() returns the fallback for every packet. act_police has no mandatory rate, so rate = 0 leaves tcfp_mtu = ~0 and tcf_police_mtu_check() always passes.
TC_ACT_CONSUMED was added by commit 720f22fed81b ("net: sched: refactor reinsert action"), after both goto-chain guards were written:
commit 9469f375ab09 ("net/sched: act_gact: disallow 'goto chain' on fallback control action") and
commit c08f5ed5d625 ("net/sched: act_police: disallow 'goto chain' on fallback control action"). Neither guard was widened when the new verdict appeared.
Factor the existing range test out of tcf_action_check_ctrlact() as tcf_action_valid() and apply it to both fallbacks. The helper cannot call tcf_action_check_ctrlact() directly because that also allocates a goto_chain, which is exactly what these two sites must not do.
Reproduced on v7.2-rc6: kmemleak reports one leaked 232-byte skbuff_head_cache object plus its 704-byte data buffer per packet. With this patch both configurations are rejected with -EINVAL and kmemleak reports none.
--- include/net/act_api.h | 19 +++++++++++++++++++ net/sched/act_gact.c | 5 +++++ net/sched/act_police.c | 6 ++++++ 3 files changed, 30 insertions(+)
include/net/act_api.h +19 -0
@@ -270,6 +270,25 @@ int tcf_action_check_ctrlact(int action,struct tcf_chain *tcf_action_set_ctrlact (struct tc_action *a,int action,struct tcf_chain *newchain); +/* Range check for a control action supplied by user space. +* +* This is the same test tcf_action_check_ctrlact() applies to the primary +* control action, factored out for the *fallback* control actions +* (act_gact's TCA_GACT_PROB.paction and act_police's TCA_POLICE_RESULT), +* which must not reach tcf_action_check_ctrlact() because they have no +* goto_chain to allocate. Without it, user space can store kernel-internal +* verdicts such as TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is +* deliberately not part of the UAPI value range. +*/ +static inline bool tcf_action_valid (int action) +{ +int opcode =TC_ACT_EXT_OPCODE (action); + +if (!opcode) +return action <=TC_ACT_VALUE_MAX ; +return opcode <=TC_ACT_EXT_OPCODE_MAX || action ==TC_ACT_UNSPEC ; +} +#ifdef CONFIG_INET DECLARE_STATIC_KEY_FALSE (tcf_frag_xmit_count);#endif
net/sched/act_gact.c +5 -0
@@ -89,6 +89,11 @@ static int tcf_gact_init(struct net *net p_parm =nla_data (tb[TCA_GACT_PROB ]);if (p_parm->ptype >=MAX_RAND )return -EINVAL ; +if (!tcf_action_valid (p_parm->paction)) { +NL_SET_ERR_MSG (extack, +"invalid fallback control action" ); +return -EINVAL ; + }if (TC_ACT_EXT_CMP (p_parm->paction,TC_ACT_GOTO_CHAIN )) {NL_SET_ERR_MSG (extack,"goto chain not allowed on fallback" );
net/sched/act_police.c +6 -0
@@ -128,6 +128,12 @@ static int tcf_police_init(struct net *nif (tb[TCA_POLICE_RESULT ]) { tcfp_result =nla_get_u32 (tb[TCA_POLICE_RESULT ]); +if (!tcf_action_valid (tcfp_result)) { +NL_SET_ERR_MSG (extack, +"invalid fallback control action" ); + err = -EINVAL ; + goto failure; + }if (TC_ACT_EXT_CMP (tcfp_result,TC_ACT_GOTO_CHAIN )) {NL_SET_ERR_MSG (extack,"goto chain not allowed on fallback" );
Greg Kroah-Hartman
maintainerPATCH 224/271 ovl: dont warn when the mount is completed from another user namespace
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 63981fc786daaa626cb14d9be1406f674d79f98f upstream.
fsopen() records the caller's user namespace in fc->user_ns and hands back an ordinary file descriptor. Nothing ties the task that calls fsconfig(FSCONFIG_CMD_CREATE) to the task that created the context. The fd is inherited across fork() and exec() and it can be passed over a unix socket.
Completing a context from another user namespace is allowed on purpose. vfs_cmd_create() authorizes the create with mount_capable(), which for FS_USERNS_MOUNT checks ns_capable(fc->user_ns, CAP_SYS_ADMIN), and that succeeds for a task holding CAP_SYS_ADMIN in an ancestor of fc->user_ns. So an unprivileged task can reach the WARN_ON() in ovl_fill_super():
create a user and a mount namespace in a child, call fsopen("overlay") there, send the fscontext fd to the parent and let the parent issue FSCONFIG_CMD_CREATE. Both namespaces come from a plain unshare(1) and no capability is needed anywhere:
WARNING: fs/overlayfs/super.c:1551 at ovl_fill_super+0x7b9/0x1e20 [overlay] CPU: 3 UID: 1000 PID: 3243376 Comm: fswarn Call Trace: get_tree_nodev+0x71/0xa0 ovl_get_tree+0x15/0x20 [overlay] vfs_get_tree+0x2a/0x100 vfs_cmd_create+0x60/0xf0 __do_sys_fsconfig+0x4b2/0x500
The child needs the mount namespace because fsopen() itself gates on may_mount(), which asks for CAP_SYS_ADMIN in the user namespace owning the caller's mount namespace. fsconfig() doesn't repeat that check.
It is a WARN_ON() and not a WARN_ON_ONCE(), so the condition can be raised in a loop to taint the kernel and flood the log, and it panics a kernel booted with panic_on_warn.
Keep refusing the mount and stop warning about it. ovl_parse_param() already spells a user namespace check this way for Opt_override_creds.
--- fs/overlayfs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/overlayfs/super.c +2 -1
@@ -1544,7 +1544,8 @@ int ovl_fill_super(struct super_block *sint err; err = -EIO ; -if (WARN_ON (fc->user_ns !=current_user_ns ())) +/* The fscontext fd may have been passed to another user namespace. */ +if (fc->user_ns !=current_user_ns ()) goto out_err;ovl_set_d_op (sb);
Greg Kroah-Hartman
maintainerPATCH 225/271 binfmt_misc: dont warn when the mount is completed from another user namespace
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 79fdf39f1a31f88cb3833b6f8091fbf6acdca2c6 upstream.
fsopen() records the caller's user namespace in fc->user_ns and hands back an ordinary file descriptor. Nothing ties the task that calls fsconfig(FSCONFIG_CMD_CREATE) to the task that created the context. The fd is inherited across fork() and exec() and it can be passed over a unix socket.
Completing a context from another user namespace is allowed on purpose. vfs_cmd_create() authorizes the create with mount_capable(), which for FS_USERNS_MOUNT checks ns_capable(fc->user_ns, CAP_SYS_ADMIN), and that succeeds for a task holding CAP_SYS_ADMIN in an ancestor of fc->user_ns. So an unprivileged task can reach the WARN_ON() in bm_fill_super():
create a user and a mount namespace in a child, call fsopen("binfmt_misc") there, send the fscontext fd to the parent and let the parent issue FSCONFIG_CMD_CREATE. Both namespaces come from a plain unshare(1) and no capability is needed anywhere:
WARNING: fs/binfmt_misc.c:938 at bm_fill_super+0xa2/0xc0 [binfmt_misc] CPU: 15 UID: 1000 PID: 3243382 Comm: fswarn Call Trace: get_tree_keyed+0x7d/0xb0 bm_get_tree+0x34/0x90 [binfmt_misc] vfs_get_tree+0x2a/0x100 vfs_cmd_create+0x60/0xf0 __do_sys_fsconfig+0x4b2/0x500
The child needs the mount namespace because fsopen() itself gates on may_mount(), which asks for CAP_SYS_ADMIN in the user namespace owning the caller's mount namespace. fsconfig() doesn't repeat that check.
It is a WARN_ON() and not a WARN_ON_ONCE(), so the condition can be raised in a loop to taint the kernel and flood the log, and it panics a kernel booted with panic_on_warn.
Keep refusing the mount and stop warning about it. Nothing in bm_fill_super() depends on the two namespaces matching, it derives everything from sb->s_user_ns.
--- fs/binfmt_misc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/binfmt_misc.c +2 -1
@@ -937,7 +937,8 @@ static int bm_fill_super(struct super_bl /* last one */ {""} }; -if (WARN_ON (user_ns !=current_user_ns ())) +/* The fscontext fd may have been passed to another user namespace. */ +if (user_ns !=current_user_ns ())return -EINVAL ;/* Never exec off this instance and never let anything stack on it. */
Greg Kroah-Hartman
maintainerPATCH 226/271 Revert "drm/amdgpu: fix aperture mapping leak"
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Asad Kamal <[email protected]>
commit b96c529cd2551b78316a4afa3237b2ed96ba03c8 upstream.
devres teardown is LIFO. The aperture devres node was registered after the DRM device node, so devres_release_all() unmaps the aperture before the DRM device release callback fires amdgpu_device_fini_sw(). IP sw_fini callbacks (e.g. vcn_v4_0_sw_fini) write to fw_shared through a pointer derived from aper_base_kaddr, causing a kernel page fault on probe failure / rollback:
BUG: unable to handle page fault ... PMD 0
RIP: vcn_v4_0_sw_fini+0x7b/0x170 [amdgpu]
Call Trace:
amdgpu_device_fini_sw
amdgpu_driver_release_kms
devm_drm_dev_init_release
devres_release_allThis reverts commit d871e99879cb5fd1fa798b006b4888887e63a17a.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 + drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 35 +++++++++++++++-------------- 2 files changed, 21 insertions(+), 16 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +2 -0
@@ -4169,6 +4169,8 @@ static void amdgpu_device_unmap_mmio(striounmap (adev->rmmio); adev->rmmio =NULL ; +if (adev->mman.aper_base_kaddr) +iounmap (adev->mman.aper_base_kaddr); adev->mman.aper_base_kaddr =NULL ;/* Memory manager related */
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +19 -16
@@ -2121,23 +2121,17 @@ int amdgpu_ttm_init(struct amdgpu_device/* Change the size here instead of the init above so only lpfn is affected */ amdgpu_ttm_set_buffer_funcs_status (adev, false);#ifdef CONFIG_64BIT -if (adev->gmc.xgmi.connected_to_cpu) { -void *kaddr =devm_memremap (adev->dev, adev->gmc.aper_base, - adev->gmc.visible_vram_size, -MEMREMAP_WB ); -if (IS_ERR (kaddr)) -return PTR_ERR (kaddr); - adev->mman.aper_base_kaddr = (__force void __iomem *)kaddr; - }else if (adev->gmc.is_app_apu) { +#ifdef CONFIG_X86 +if (adev->gmc.xgmi.connected_to_cpu) + adev->mman.aper_base_kaddr =ioremap_cache (adev->gmc.aper_base, + adev->gmc.visible_vram_size); +else if (adev->gmc.is_app_apu)DRM_DEBUG_DRIVER ("No need to ioremap when real vram size is 0\n" ); - }else { - adev->mman.aper_base_kaddr =devm_ioremap_wc (adev->dev, - adev->gmc.aper_base, - adev->gmc.visible_vram_size); -if (!adev->mman.aper_base_kaddr) -return -ENOMEM ; - } + else +#endif + adev->mman.aper_base_kaddr =ioremap_wc (adev->gmc.aper_base, + adev->gmc.visible_vram_size);#endif amdgpu_ttm_init_vram_resv_regions (adev);
@@ -2266,6 +2260,8 @@ int amdgpu_ttm_init(struct amdgpu_device*/ void amdgpu_ttm_fini (struct amdgpu_device *adev) { +int idx; +if (!adev->mman.initialized)return ;
@@ -2288,7 +2284,14 @@ void amdgpu_ttm_fini(struct amdgpu_devicamdgpu_ttm_unmark_vram_reserved (adev,AMDGPU_RESV_FW_VRAM_USAGE );amdgpu_ttm_unmark_vram_reserved (adev,AMDGPU_RESV_DRV_VRAM_USAGE ); - adev->mman.aper_base_kaddr =NULL ; +if (drm_dev_enter (adev_to_drm (adev), &idx)) { + +if (adev->mman.aper_base_kaddr) +iounmap (adev->mman.aper_base_kaddr); + adev->mman.aper_base_kaddr =NULL ; + +drm_dev_exit (idx); + }if (!adev->gmc.is_app_apu)amdgpu_vram_mgr_fini (adev);
Greg Kroah-Hartman
maintainerPATCH 227/271 dibs: initialise dibs->lock in dibs_dev_alloc()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hidayath Khan <[email protected]>
commit c27e360545373b7aee9862a5beef3b9fb3df0c25 upstream.
dibs->lock is initialised by dibs_dev_add(), but a dibs device can already take interrupts before that call: ism_probe() runs ism_dev_init(), and hence request_irq(), before it calls dibs_dev_add(). No client can have registered a dmb at that point, so no dmb interrupt can occur, but a GID event interrupt can, and ism_handle_irq() takes dibs->lock unconditionally on entry, before it inspects anything else.
Initialise the lock in dibs_dev_alloc() instead, so that it is valid as soon as a driver can publish the device to its interrupt handler.
--- drivers/dibs/dibs_main.c | 2 +- include/linux/dibs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/dibs/dibs_main.c +1 -1
@@ -139,6 +139,7 @@ struct dibs_dev *dibs_dev_alloc(void) dibs =kzalloc_obj (*dibs);if (!dibs)return dibs; +spin_lock_init (&dibs->lock); dibs->dev.release = dibs_dev_release; dibs->dev.class = &dibs_class;device_initialize (&dibs->dev);
@@ -187,7 +188,6 @@ int dibs_dev_add(struct dibs_dev *dibs)int i, ret; max_dmbs = dibs->ops->max_dmbs (); -spin_lock_init (&dibs->lock); dibs->dmb_clientid_arr =kzalloc (max_dmbs,GFP_KERNEL );if (!dibs->dmb_clientid_arr)return -ENOMEM ;
include/linux/dibs.h +1 -1
@@ -439,7 +439,7 @@ static inline void *dibs_get_priv(struct/** * dibs_dev_alloc() - allocate and reference device structure * -* The following fields will be valid upon successful return: dev +* The following fields will be valid upon successful return: dev, lock * NOTE: Use put_device(dibs_get_dev(@dibs)) to give up your reference instead * of freeing @dibs @dev directly once you have successfully called this * function.
Greg Kroah-Hartman
maintainerPATCH 228/271 arm64: remove redundant concurrent ptdump UAF mitigation
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes (ARM) <[email protected]>
commit 9d3277b2c07ccc9508d648098b3bbb46c61b7f3c upstream.
This partially reverts commit fa93b45fd397 ("arm64: Enable vmalloc-huge with ptdump"), retaining vmalloc-huge support but eliminating the now redundant mitigation against a race between huge vmap page table freeing and ptdump, as this issue has now been fixed at core.
We also simultaneously remove the arm64 if-deffery when acquiring the mmap read lock upon vmap huge page table promotion as it is no longer required.
Note that this patch relies on the preceding vmalloc patch, and should not be backported alone.
--- arch/arm64/include/asm/ptdump.h | 2 - arch/arm64/mm/mmu.c | 43 +++------------------------------------- arch/arm64/mm/ptdump.c | 11 +--------- mm/vmalloc.c | 15 ++----------- 4 files changed, 9 insertions(+), 62 deletions(-)
arch/arm64/include/asm/ptdump.h +0 -2
@@ -7,8 +7,6 @@#include <linux/ptdump.h> -DECLARE_STATIC_KEY_FALSE (arm64_ptdump_lock_key); -#ifdef CONFIG_PTDUMP #include <linux/mm_types.h>
arch/arm64/mm/mmu.c +4 -39
81 lines not on this page · raw message
arch/arm64/mm/ptdump.c +2 -9
@@ -283,13 +283,6 @@ void note_page_flush(struct ptdump_statenote_page (pt_st,0 ,-1 ,pte_val (pte_zero)); } -static void arm64_ptdump_walk_pgd (struct ptdump_state *st,struct mm_struct *mm) -{ -static_branch_inc (&arm64_ptdump_lock_key); -ptdump_walk_pgd (st, mm,NULL ); -static_branch_dec (&arm64_ptdump_lock_key); -} -void ptdump_walk (struct seq_file *s,struct ptdump_info *info) {unsigned long end = ~0UL ;
@@ -318,7 +311,7 @@ void ptdump_walk(struct seq_file *s, str } }; -arm64_ptdump_walk_pgd (&st.ptdump, info->mm); +ptdump_walk_pgd (&st.ptdump, info->mm,NULL ); }static void __init ptdump_initialize (void )
@@ -360,7 +353,7 @@ bool ptdump_check_wx(void) } }; -arm64_ptdump_walk_pgd (&st.ptdump, &init_mm); +ptdump_walk_pgd (&st.ptdump, &init_mm,NULL );if (st.wx_pages || st.uxn_pages) { pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
mm/vmalloc.c +3 -12
@@ -169,10 +169,7 @@ static int vmap_try_huge_pmd(pmd_t *pmd,* Concurrent read lock holders are safe: each exclusively owns * the range it operates on and cannot reach this page table. */ -#ifndef CONFIG_ARM64 - scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) -#endif - { + scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) {if (!pmd_free_pte_page (pmd, addr))return 0 ;return pmd_set_huge (pmd, phys_addr, prot);
@@ -229,10 +226,7 @@ static int vmap_try_huge_pud(pud_t *pud,return pud_set_huge (pud, phys_addr, prot);/* See comment in vmap_try_huge_pmd(). */ -#ifndef CONFIG_ARM64 - scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) -#endif - { + scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) {if (!pud_free_pmd_page (pud, addr))return 0 ;return pud_set_huge (pud, phys_addr, prot);
@@ -289,10 +283,7 @@ static int vmap_try_huge_p4d(p4d_t *p4d,return p4d_set_huge (p4d, phys_addr, prot);/* See comment in vmap_try_huge_pmd(). */ -#ifndef CONFIG_ARM64 - scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) -#endif - { + scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) {if (!p4d_free_pud_page (p4d, addr))return 0 ;return p4d_set_huge (p4d, phys_addr, prot);
Greg Kroah-Hartman
maintainerPATCH 229/271 x86/CPU: Add a tlbi= cmdline switch
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rik van Riel <[email protected]>
commit abe7c8b09bd72a9c726016257c6281f129b4c02d upstream.
With the recently found INVLPGB / TLBSYNC issue, there has been some interest in disabling INVLPGB-based TLB flushing, in order to rule out that CPU issue as a cause of userspace crashes.
Add a kernel command line option to control the TLB flushing behavior.
If the need arises, we will add a "tlbi=broadcast" for the case when TLB invalidation broadcasts need to be explicitly selected, but this is not needed now yet.
[ bp: Rewrite commit message, move to cpu/common.c, add documentation. ]
--- Documentation/admin-guide/kernel-parameters.txt | 4 ++++ arch/x86/kernel/cpu/common.c | 10 ++++++++++ 2 files changed, 14 insertions(+)
Documentation/admin-guide/kernel-parameters.txt +4 -0
@@ -7573,6 +7573,10 @@ Kernel parameters See Documentation/admin-guide/mm/transhuge.rst for more details. + tlbi= [X86-64] + Format: {ipi} + ipi: switch to IPI-based TLB flushing + topology= [S390,EARLY] Format: {off | on} Specify if the kernel should make use of the cpu
arch/x86/kernel/cpu/common.c +10 -0
@@ -2668,3 +2668,13 @@ void __init arch_cpu_finalize_init(void)*/ mem_encrypt_init (); } + +/* Control TLB flushing methods */ +static int __init tlbi_setup (char *str) +{ +if (!strcmp (str,"ipi" )) +setup_clear_cpu_cap (X86_FEATURE_INVLPGB ); + +return 1 ; +} +__setup ("tlbi=" , tlbi_setup);
Greg Kroah-Hartman
maintainerPATCH 230/271 x86/mce: Set up the polling timer before CMCI discovery
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
commit a213dfaa2596c1c0dc4dae91c14fbfa499c03223 upstream.
I hit the following on one of my machines:
mce: CPU0 BANK15 CMCI inherited storm
------------[ cut here ]------------ ODEBUG: assert_init not available (active state 0) object: (____ptrval____) object type: timer_list hint: 0x0 WARNING: lib/debugobjects.c:632 at debug_object_assert_init+0x178/0x230, CPU#0: swapper/0/0 CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 7.2.0-rc5 #3 PREEMPTLAZY RIP: 0010:debug_object_assert_init+0x18f/0x230 Call Trace: <TASK> __mod_timer mce_timer_kick cmci_discover intel_init_cmci mce_intel_feature_init mcheck_cpu_init identify_cpu identify_boot_cpu arch_cpu_finalize_init start_kernel
A second splat follows right after, from timer_setup() finding that same timer already queued:
ODEBUG: init active (active state 0) object: (____ptrval____) object type: timer_list hint: stub_timer+0x0/0x10
This is happening because CMCI storm detection is trying to modify the timer before latter was properly set up.
Set up the timer first. __mcheck_cpu_setup_timer() only calls timer_setup(), and depends on neither the generic nor the vendor init.
[ bp: Massage commit message. ]
--- arch/x86/kernel/cpu/mce/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kernel/cpu/mce/core.c +1 -1
@@ -2269,10 +2269,10 @@ void mcheck_cpu_init(struct cpuinfo_x86 mca_cfg.initialized =1 ; +__mcheck_cpu_setup_timer ();__mcheck_cpu_init_generic ();__mcheck_cpu_init_vendor (c);__mcheck_cpu_init_prepare_banks (); -__mcheck_cpu_setup_timer ();cr4_set_bits (X86_CR4_MCE ); }
Greg Kroah-Hartman
maintainerPATCH 231/271 xdp: reject clones that overrun skb_shared_info tailroom
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit e48e8edbef2eb824201495daa5234560f632b23c upstream.
xdpf_clone() clones broadcast copies into a single page and sets frame_sz to PAGE_SIZE. __xdp_build_skb_from_frame() later treats that page like a normal XDP frame and expects the usual skb_shared_info tailroom at the end of the buffer.
The current check only rejects frames whose linear xdp_frame header, headroom, and packet data exceed PAGE_SIZE. A source frame backed by a larger allocation can still satisfy that check while extending into the clone's required shared-info area. When such a clone is converted back into an skb, build_skb_around() places skb_shared_info over live packet bytes and later writes can corrupt XDP return metadata.
Reject clones unless their linear area fits inside SKB_WITH_OVERHEAD(PAGE_SIZE), matching the tailroom requirement already enforced by the XDP-to-skb conversion path.
--- net/core/xdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/core/xdp.c +1 -1
@@ -871,7 +871,7 @@ struct xdp_frame *xdpf_clone(struct xdp_ headroom = xdpf->headroom +sizeof (*xdpf); totalsize = headroom + xdpf->len; -if (unlikely (totalsize >PAGE_SIZE )) +if (unlikely (totalsize >SKB_WITH_OVERHEAD (PAGE_SIZE )))return NULL ; page =dev_alloc_page ();if (!page)
Greg Kroah-Hartman
maintainerPATCH 232/271 vxlan: do not arm the ageing timer on a device that is down
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
commit b37971686ec59fb027fa4910ba16805e68fddb97 upstream.
vxlan_changelink() arms vxlan->age_timer whenever the requested ageing interval differs from the configured one:
if (conf.age_interval != vxlan->cfg.age_interval) mod_timer(&vxlan->age_timer, jiffies);
There is no netif_running() test, so the timer is armed even on a device that was never brought up. The only synchronous cancel in the driver is the timer_delete_sync() in vxlan_stop(), which is .ndo_stop. netif_close_many() drops devices without IFF_UP before __dev_close_many() runs, so that cancel is skipped for such a device.
vxlan_setup() sets dev->needs_free_netdev = true and age_timer is a member of struct vxlan_dev, so free_netdev() releases the allocation the timer lives in while it is still queued on a timer_base. expire_timers() unlinks the entry before it loads timer->function, so the timer core writes through the freed object's list pointers:
BUG: KASAN: slab-use-after-free in __run_timers+0x208/0x654 Write of size 8 at addr ffff00001adace68 by task true/192 __asan_store8+0x84/0xac __run_timers+0x208/0x654 run_timer_softirq+0x154/0x18c Allocated by task 189: alloc_netdev_mqs+0x64/0x720 rtnl_create_link+0x4ac/0x520 rtnl_newlink+0x758/0xd00 Freed by task 191: netdev_release+0x40/0x58 netdev_run_todo+0x4a4/0x8c0 rtnl_dellink+0x200/0x4e8
The rtnl operations involved are netns-scoped, so an unprivileged user can perform them in a new user and network namespace.
Arming the timer on a down device never had an effect: vxlan_cleanup() returns early on !netif_running(), and vxlan_open() arms the timer for any non-zero interval once the device is brought up. Add the missing test.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- drivers/net/vxlan/vxlan_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/vxlan/vxlan_core.c +1 -1
@@ -4501,7 +4501,7 @@ static int vxlan_changelink(struct net_dif (change_igmp &&vxlan_addr_multicast (&dst->remote_ip)) err =vxlan_multicast_leave (vxlan); -if (conf.age_interval != vxlan->cfg.age_interval) +if (netif_running (dev) && conf.age_interval != vxlan->cfg.age_interval)mod_timer (&vxlan->age_timer, jiffies);netdev_adjacent_change_commit (dst->remote_dev, lowerdev, dev);
Greg Kroah-Hartman
maintainerPATCH 233/271 vsock/virtio: read virtqueues under worker locks
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit ebac8f6b1ef0e9278afe204b8692a7479988dace upstream.
Commit bd50c5dc182b ("vsock/virtio: add support for device suspend/resume") made the *_run flags transition from false to true when restore installs replacement virtqueues. The RX, TX and event workers read their virtqueue before locking and checking the corresponding flag, so a worker delayed across freeze and restore can observe the replacement queue's running state while retaining a pointer to the deleted queue.
Read each virtqueue under its mutex after checking the run flag, keeping the pointer and state in the same queue generation.
--- net/vmw_vsock/virtio_transport.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
net/vmw_vsock/virtio_transport.c +6 -5
@@ -346,12 +346,13 @@ static void virtio_transport_tx_work(strstruct virtqueue *vq;bool added = false; - vq = vsock->vqs[VSOCK_VQ_TX ];mutex_lock (&vsock->tx_lock);if (!vsock->tx_run) goto out; + vq = vsock->vqs[VSOCK_VQ_TX ]; + do { struct sk_buff *skb; unsigned int len;
@@ -451,13 +452,13 @@ static void virtio_transport_event_work( container_of(work, struct virtio_vsock, event_work);struct virtqueue *vq; - vq = vsock->vqs[VSOCK_VQ_EVENT ]; -mutex_lock (&vsock->event_lock);if (!vsock->event_run) goto out; + vq = vsock->vqs[VSOCK_VQ_EVENT ]; + do { struct virtio_vsock_event *event; unsigned int len;
@@ -634,13 +635,13 @@ static void virtio_transport_rx_work(str container_of(work, struct virtio_vsock, rx_work);struct virtqueue *vq; - vq = vsock->vqs[VSOCK_VQ_RX ]; -mutex_lock (&vsock->rx_lock);if (!vsock->rx_run) goto out; + vq = vsock->vqs[VSOCK_VQ_RX ]; + do { virtqueue_disable_cb(vq); for (;;) {
Greg Kroah-Hartman
maintainerPATCH 234/271 vsock/virtio: avoid refilling the RX queue after teardown
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit a31e0ad444698d8aa7534a0f89fda543730f97a5 upstream.
Commit b917507e5ad9 ("vsock/virtio: stop workers during the .remove()") made the RX worker jump to its common exit when rx_run is clear. That exit still refills the RX queue when the buffer count is low, so work queued across virtio_vsock_vqs_del() can add buffers after the virtqueues have been deleted.
BUG: KASAN: slab-use-after-free in virtqueue_add_sgs Read of size 4 by task kworker/0:1 Workqueue: virtio_vsock virtio_transport_rx_work Call Trace: virtqueue_add_sgs (drivers/virtio/virtio_ring.c:2796) virtio_vsock_rx_fill (net/vmw_vsock/virtio_transport.c:332) virtio_transport_rx_work (net/vmw_vsock/virtio_transport.c:701) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3478) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) ... Freed by task 141: kfree (mm/slub.c:6566) vp_del_vq (drivers/virtio/virtio_pci_common.c:259) vp_del_vqs (drivers/virtio/virtio_pci_common.c:285) virtio_vsock_freeze (net/vmw_vsock/virtio_transport.c:912) virtio_device_freeze (drivers/virtio/virtio.c:658) virtio_pci_freeze (drivers/virtio/virtio_pci_common.c:601) pci_pm_freeze (drivers/pci/pci-driver.c:1098) device_suspend (drivers/base/power/main.c:1968) Kernel panic - not syncing: KASAN: panic_on_warn set ...
Jump to a no-refill exit when rx_run is clear, leaving the normal exit to replenish a running queue.
--- net/vmw_vsock/virtio_transport.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/vmw_vsock/virtio_transport.c +2 -1
@@ -638,7 +638,7 @@ static void virtio_transport_rx_work(strmutex_lock (&vsock->rx_lock);if (!vsock->rx_run) - goto out; + goto out_nofill; vq = vsock->vqs[VSOCK_VQ_RX ];
@@ -692,6 +692,7 @@ static void virtio_transport_rx_work(str out:if (vsock->rx_buf_nr < vsock->rx_buf_max_nr /2 )virtio_vsock_rx_fill (vsock); +out_nofill:mutex_unlock (&vsock->rx_lock); }
Greg Kroah-Hartman
maintainerPATCH 235/271 veth: fix skb length accounting after XDP frag adjustment
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sun Jian <[email protected]>
commit cb6379feaaff11c4e1e79c26c745ffa23182768a upstream.
veth exposes non-linear skb fragments through an xdp_buff. If an XDP program adjusts the fragment area, veth_xdp_rcv_skb() copies xdp_frags_size back to skb->data_len but leaves skb->len containing the old fragment contribution.
After a fragment shrink, this makes skb_headlen() larger than the actual linear area. In the reproduced UDP receive path, __skb_datagram_iter() copied 1024 bytes past the actual linear tail to userspace, starting at struct skb_shared_info. The copied bytes included the affected skb's nr_frags, xdp_frags_size, and a kernel pointer from skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same amount and truncated at the end.
Subtract the old data_len before replacing it and add the new data_len afterwards, keeping skb->len and skb->data_len synchronized.
Additionally, bpf_xdp_pull_data() can advance data_end while leaving frags present. The skb is then still non-linear, so the old __skb_put(skb, off) triggers SKB_LINEAR_ASSERT().
Use skb_set_tail_pointer() and update skb->len explicitly instead, following bpf_prog_run_generic_xdp(). Unlike __skb_put(), skb_set_tail_pointer() does not require a linear skb.
A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by 1024 bytes from its fragment area. Before the fix, all 10 runs produced corrupted payloads. After the fix, all 10 runs matched the expected payload exactly. A forced-tailroom reproducer also exercises bpf_xdp_pull_data() with frags still present; the old code triggers SKB_LINEAR_ASSERT(), while this fix passes 10/10 runs.
--- drivers/net/veth.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
drivers/net/veth.c +14 -8
@@ -865,18 +865,24 @@ static struct sk_buff *veth_xdp_rcv_skb(skb_reset_mac_header (skb); -/* check if bpf_xdp_adjust_tail was used */ - off = xdp->data_end - orig_data_end; -if (off !=0 ) -__skb_put (skb, off);/* positive on grow, negative on shrink */ -/* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers -* (e.g. bpf_xdp_adjust_tail), we need to update data_len here. +* (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution +* from skb->len before updating data_len, then add the new one back. */ -if (xdp_buff_has_frags (xdp)) + skb->len -= skb->data_len; +if (xdp_buff_has_frags (xdp)) { skb->data_len =skb_shinfo (skb)->xdp_frags_size; -else + skb->len += skb->data_len; + }else { skb->data_len =0 ; + } + +/* Synchronize the skb tail with XDP's updated linear area. */ + off = xdp->data_end - orig_data_end; +if (off !=0 ) { +skb_set_tail_pointer (skb, xdp->data_end - xdp->data); + skb->len += off;/* positive on grow, negative on shrink */ + } skb->protocol =eth_type_trans (skb, rq->dev);
Greg Kroah-Hartman
maintainerPATCH 236/271 vhost: reset the vring metadata cache on vring reconfiguration
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jun Yang <[email protected]>
commit de845981da67a6b049080c87e605130b0c30adc5 upstream.
vq->meta_iotlb[] caches the vhost_iotlb_map that backs each vring metadata region, and iotlb_access_ok() returns early on a cache hit, taking the hit as proof that the region has already been validated:
if (vhost_vq_meta_fetch(vq, addr, len, type)) return true;
The cache is reset on VHOST_IOTLB_UPDATE and VHOST_IOTLB_INVALIDATE, on device IOTLB (re)initialisation and on vq reset, but not when VHOST_SET_VRING_ADDR replaces vq->desc, vq->avail and vq->used, nor when VHOST_SET_VRING_NUM changes the region sizes.
With a device IOTLB attached both ioctls are accepted while the vq is live, and neither validates the addresses at ioctl time: vq_access_ok() and vq_log_used_access_ok() return true early because the addresses are GIOVAs, deferring validation to prefetch time. Once the cache has been populated that deferred validation no longer runs -- vq_meta_prefetch() hits the stale entry and returns true -- and vhost_vq_meta_fetch() keeps translating through the old mapping as
map->addr + addr - map->start
for an address the mapping no longer covers. vhost_copy_to_user() and vhost_copy_from_user() consume the result with __copy_to_user() and __copy_from_user(), which do not check it either, so a subsequent used ring update or descriptor fetch accesses memory outside the region the IOTLB actually maps.
Reset the metadata cache whenever the vring is reconfigured, so the new addresses are pushed back through iotlb_access_ok()'s slow path.
Message-ID: <[email protected]>
--- drivers/vhost/vhost.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/vhost/vhost.c +8 -0
@@ -2131,6 +2131,14 @@ static long vhost_vring_set_num_addr(strBUG (); } +/* +* The metadata cache holds the IOTLB mapping that backed the previous +* desc/avail/used addresses and vring size, both of which are being +* replaced here. iotlb_access_ok() takes a cache hit as proof that the +* region was validated, so the stale entries have to go. +*/ +__vhost_vq_meta_reset (vq); +mutex_unlock (&vq->mutex);return r;
Greg Kroah-Hartman
maintainerPATCH 237/271 tls: rx: restore msg_iter before TLS 1.3 optimistic retry
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jérémy Jean <[email protected]>
commit 1c8629651cb54f7b51db8fc0b1a9944e4a4b0f5e upstream.
tls_decrypt_sg() advances msg->msg_iter when it maps user pages for the optimistic TLS 1.3 zero-copy path. If the decrypted record turns out not to be unpadded application data, tls_decrypt_sw() retries into a kernel skb, but leaves the iterator advanced.
The subsequent copy from the skb then writes decrypted bytes again at a later point in the caller iovecs while recvmsg() reports only the post-retry length. A TLS peer can trigger this after the receiver enables TLS_RX_EXPECT_NO_PAD.
Revert the iterator by the number of bytes consumed by the optimistic mapping before retrying without zero-copy.
Add a selftest which sends a TLS 1.3 control record with TLS_RX_EXPECT_NO_PAD enabled and verifies that recvmsg() does not overwrite later iovecs beyond the returned length.
--- net/tls/tls_sw.c | 2 + tools/testing/selftests/net/tls.c | 57 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+)
net/tls/tls_sw.c +2 -0
@@ -1731,6 +1731,8 @@ tls_decrypt_sw(struct sock *sk, struct t/* If opportunistic TLS 1.3 ZC failed retry without ZC */ if (unlikely (darg->zc && prot->version ==TLS_1_3_VERSION && darg->tail !=TLS_RECORD_TYPE_DATA )) { +iov_iter_revert (&msg->msg_iter,strp_msg (darg->skb)->full_len - + prot->overhead_size); darg->zc = false;if (!darg->tail)TLS_INC_STATS (sock_net (sk),LINUX_MIB_TLSRXNOPADVIOL );
tools/testing/selftests/net/tls.c +57 -0
63 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 238/271 tls: dont leave a full plaintext sk_msg ring unpushed
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: chanyoung <[email protected]>
commit 7bca91d63341274e857f4aeaad54d229405e93dc upstream.
When the copy path in tls_sw_sendmsg_locked() adds the fragment that fills the plaintext sk_msg ring, it does not set full_record, so the record is left full and unpushed. A later splice() then adds to an already full
ring: sk_msg_page_add() has no fullness check of its own, so sg.end wraps onto sg.start and the ring appears empty. Fragments added after that overwrite live entries, and sg.size no longer matches what is reachable between sg.start and sg.end, so pushing the record runs the scatterwalk off the end of the scatterlist.
An unprivileged user can trigger this on a loopback TCP socket with the "tls" ULP attached:
BUG: kernel NULL pointer dereference, address: 0000000000000008 RIP: 0010:memcpy_from_scatterwalk+0x32/0xc0 Call Trace: skcipher_walk_next+0x1d1/0x2c0 gcm_encrypt_aesni_avx+0x1e9/0x220 bpf_exec_tx_verdict+0x3bb/0x860 tls_sw_sendmsg+0xa1a/0xca0 __sys_sendto+0x1da/0x1f0
Set full_record in the copy path when the ring becomes full, and push a record that is already full on entry to the sendmsg loop.
--- net/tls/tls_sw.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
net/tls/tls_sw.c +14 -0
@@ -1116,6 +1116,14 @@ static int tls_sw_sendmsg_locked(structif (!sk_stream_memory_free (sk)) goto wait_for_sndbuf; +/* open record may be full if we couldn't push it in the last sendmsg call */ +if (sk_msg_full (msg_pl)) { + full_record = true; +sk_msg_trim (sk, msg_en, + msg_pl->sg.size + prot->overhead_size); + goto copied; + } + alloc_encrypted: ret =tls_alloc_encrypted_msg (sk, required_size);if (ret) {
@@ -1216,6 +1224,12 @@ fallback_to_reg_send: msg_pl, try_to_copy);if (ret <0 ) goto trim_sgl; + +if (sk_msg_full (msg_pl)) { + full_record = true; +sk_msg_trim (sk, msg_en, + msg_pl->sg.size + prot->overhead_size); + } }/* Open records defined only if successfully copied, otherwise
Greg Kroah-Hartman
maintainerPATCH 239/271 tipc: read le->link under the node lock in tipc_node_link_down()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jun Yang <[email protected]>
commit cba9ccb47e9fa4cc77692fb896cc5ab57a667882 upstream.
tipc_node_link_down() caches the link pointer before taking n->lock:
struct tipc_link *l = le->link; /* unlocked */
if (!l)
return;
tipc_node_write_lock(n);
if (!tipc_link_is_establishing(l)) { /* deref l */
...
tipc_link_reset(l); /* write into l */
if (delete) {
kfree(l);
le->link = NULL;The delete=true caller frees that very object under n->lock, so the lock does not protect the cached pointer against it:
- CPU A, delete=false: tipc_rcv() on TIPC_LINK_DOWN_EVT, or the link supervision timer via tipc_node_timeout(), reads l unlocked and then dereferences it under n->lock;
- CPU B, delete=true: netlink TIPC_NL_BEARER_DISABLE -> bearer_disable() -> tipc_node_delete_links() -> tipc_node_link_down(n, bearer_id, true) -> kfree(l).
The link is freed with plain kfree(), not kfree_rcu(), and for UDP bearers disable_media() only schedules the asynchronous cleanup_bearer() work, so its synchronize_net() runs after the links are already gone. An in-flight CPU A that has read l therefore dereferences freed memory once B frees it:
a use-after-free read in tipc_link_is_establishing(), and a use-after-free write via tipc_link_reset() on the establishing branch.
The following trace was captured on 7.2.0-rc5-00284-gaf39eb111ce6:
BUG: KASAN: slab-use-after-free in tipc_link_is_establishing (net/tipc/link.c:285) Read of size 4 at addr ffff88802e2aa068 by task swapper/2/0 tipc_link_is_establishing (net/tipc/link.c:285) tipc_node_link_down (net/tipc/node.c:1076) tipc_node_timeout (net/tipc/node.c:843) Allocated by task 9549: tipc_link_create (net/tipc/link.c:490) tipc_node_check_dest (net/tipc/node.c:1279) tipc_disc_rcv (net/tipc/discover.c:252) tipc_udp_recv (net/tipc/udp_media.c:389) Freed by task 9549: tipc_node_link_down (net/tipc/node.c:1084) tipc_node_delete_links (net/tipc/node.c:1320) bearer_disable (net/tipc/bearer.c:414) __tipc_nl_bearer_disable (net/tipc/bearer.c:992)
Move the le->link read inside tipc_node_write_lock(), so it is serialised against the kfree() in the delete path. A racing teardown now either has not run yet, and we see a valid link, or has already run, and we see NULL.
--- net/tipc/node.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
net/tipc/node.c +10 -5
@@ -1061,18 +1061,23 @@ static void __tipc_node_link_down(structstatic void tipc_node_link_down (struct tipc_node *n,int bearer_id,bool delete) { -struct tipc_link_entry *le = &n->links[bearer_id];struct tipc_media_addr *maddr =NULL ; -struct tipc_link *l = le->link;int old_bearer_id = bearer_id; +struct tipc_link_entry *le;struct sk_buff_head xmitq; - -if (!l) -return ; +struct tipc_link *l;__skb_queue_head_init (&xmitq); +/* Synchronize the link lookup with bearer teardown. */ tipc_node_write_lock (n); + le = &n->links[bearer_id]; + l = le->link; +if (!l) { +tipc_node_write_unlock_fast (n); +return ; + } +if (!tipc_link_is_establishing (l)) {__tipc_node_link_down (n, &bearer_id, &xmitq, &maddr); }else {
Greg Kroah-Hartman
maintainerPATCH 240/271 smb: client: Fix use-after-free in cifs_try_adding_channels()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuangpeng Bai <[email protected]>
commit 4986410316b1ae0e63c6ce418e4eb196723626e7 upstream.
cifs_try_adding_channels() takes a temporary reference to an interface before dropping iface_lock. If cifs_ses_add_channel() fails, it drops that reference and then increments iface->weight_fulfilled.
A concurrent interface list refresh can remove the list reference while channel creation is in progress. In that case, the failure-path kref_put() releases the last reference and frees iface. Updating weight_fulfilled afterward then accesses freed memory.
Increment weight_fulfilled before dropping the temporary reference, keeping iface alive for the final access.
--- fs/smb/client/sess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/smb/client/sess.c +1 -1
@@ -233,9 +233,9 @@ int cifs_try_adding_channels(struct cifscifs_dbg (VFS ,"failed to open extra channel on iface:%pIS rc=%d\n" , &iface->sockaddr, rc); -kref_put (&iface->refcount, release_iface);/* failure to add chan should increase weight */ iface->weight_fulfilled++; +kref_put (&iface->refcount, release_iface);continue ; }
Greg Kroah-Hartman
maintainerPATCH 241/271 smb: client: fix SMB1 TRANS2 multi-response truncation in SendReceive()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Frank Sorenson <[email protected]>
commit 26fa4d17c023dbe5427a92d3a7bd9ae1d1e58bc5 upstream.
When a TRANS2 response is split across multiple secondary packets, coalesce_t2() assembles the payload into the large response buffer. Two bugs cause SendReceive() to copy only a small fraction of the assembled buffer into the caller's output buffer.
This manifests when listing a large directory on an SMB1 share (observed against Windows XP); the first getdents returns only partial results, and subsequent getdents returns EINVAL.
Bug 1: coalesce_t2() computes the coalesced size as:
*pdu_len += total_in_src;
cifs_demultiplex_thread() resets *pdu_len to each secondary's own pdu_length before calling coalesce_t2(), so this accumulates from the wrong baseline on every secondary after the first.
Bug 2: after reassembly, cifs_demultiplex_thread() sets mid->resp_buf_size to the final secondary's raw packet size. This value is later used as the memcpy length, so only a portion of the coalesced response is copied.
Fix both by replacing the stale *pdu_len arithmetic with smbCalcSize(), which reads the BCC field that coalesce_t2() maintains correctly throughout reassembly.
--- fs/smb/client/smb1transport.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-)
fs/smb/client/smb1transport.c +22 -6
@@ -260,9 +260,23 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, goto out;if (out_buf) { - *pbytes_returned = resp_iov.iov_len; -if (resp_iov.iov_len) -memcpy (out_buf, resp_iov.iov_base, resp_iov.iov_len); +/* Use smbCalcSize() for both single- and multi-part T2 responses, +* both here and in coalesce_t2(). +*/ +unsigned int copy_len; +if (WARN_ON_ONCE (!resp_iov.iov_base)) { + rc = -EIO ; + goto out; + } + copy_len =smbCalcSize (resp_iov.iov_base); +if (copy_len > CIFSMaxBufSize +MAX_CIFS_HDR_SIZE ) { +cifs_dbg (VFS ,"response size %u exceeds buffer\n" , + copy_len); + rc = -ENOBUFS ; + goto out; + } + *pbytes_returned = copy_len; +memcpy (out_buf, resp_iov.iov_base, copy_len); } out:
@@ -386,11 +400,13 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len) }put_bcc (byte_count, target_hdr); - byte_count = *pdu_len; - byte_count += total_in_src; +/* use smbCalcSize() rather than *pdu_len: the demux loop resets +* *pdu_len to each secondary's pdu_length, making it unreliable. +*/ + byte_count =smbCalcSize (target_hdr);/* don't allow buffer to overflow */ if (byte_count > CIFSMaxBufSize +MAX_CIFS_HDR_SIZE ) { -cifs_dbg (FYI ,"coalesced BCC exceeds buffer size (%u)\n" , +cifs_dbg (FYI ,"coalesced size exceeds buffer size (%u)\n" , byte_count);return -ENOBUFS ; }
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 242/271 KVM: SVM: Serialize accesses to the owner and mirror list with separate lock
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paolo Bonzini <[email protected]>
commit 1d78d33275ef2a16c6d080910b291d0a97a0e613 upstream.
Interaction between KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM and KVM_CAP_VM_COPY_ENC_CONTEXT_FROM can cause two separate issues:
- in sev_migrate_from(), when the destination KVM is a mirror, the mirror entry is moved from the source's list to the owner's mirror_vms list, without holding the owner's lock unlike other writers of the owner's mirror list (sev_vm_copy_enc_context_from(), sev_vm_destroy()). A concurrent COPY or destroy can race with sev_migrate_from() and corrupt the list.
- In sev_vm_destroy(), the *owner* is still active and could receive concurrently a KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM that causes sev->enc_context_owner to change. In this case the incorrect VM receives kvm_put_kvm().
The second issue needs particular care because the owner could disappear altogether (even though the race window is impossibly small) between reading it and locking it. There is thus no way to perform the checks under the owner lock without putting struct kvm under SLAB_TYPESAFE_BY_RCU (which would allow kvm_get_kvm_safe() under RCU critical section).
It is much simpler to just use a global lock, since the critical sections are so small and the new lock is always a leaf lock.
--- arch/x86/kvm/svm/sev.c | 34 +++++++++++++++++++++++++--------- arch/x86/kvm/svm/svm.h | 1 + 2 files changed, 26 insertions(+), 9 deletions(-)
arch/x86/kvm/svm/sev.c +25 -9
@@ -96,6 +96,8 @@ static u64 sev_supported_vmsa_features _static u8 sev_enc_bit;static DECLARE_RWSEM (sev_deactivate_lock);static DEFINE_MUTEX (sev_bitmap_lock); +/* Protects kvm_sev_info's enc_context_owner, mirror_vms and mirror_entry. */ +static DEFINE_MUTEX (sev_mirror_lock);unsigned int max_sev_asid;static unsigned int min_sev_asid;static unsigned int max_sev_es_asid;
@@ -2031,7 +2033,6 @@ static void sev_migrate_from(struct kvm dst->asid = src->asid; dst->handle = src->handle; dst->pages_locked = src->pages_locked; - dst->enc_context_owner = src->enc_context_owner; dst->es_active = src->es_active; dst->vmsa_features = src->vmsa_features;
@@ -2039,11 +2040,12 @@ static void sev_migrate_from(struct kvm src->active = false; src->handle =0 ; src->pages_locked =0 ; - src->enc_context_owner =NULL ; src->es_active = false;list_cut_before (&dst->regions_list, &src->regions_list, &src->regions_list); +mutex_lock (&sev_mirror_lock); +/* * If this VM has mirrors, "transfer" each mirror's refcount of the * source to the destination (this KVM). The caller holds a reference
@@ -2060,12 +2062,15 @@ static void sev_migrate_from(struct kvm* If this VM is a mirror, remove the old mirror from the owners list * and add the new mirror to the list. */ -if (is_mirroring_enc_context (dst_kvm)) { -struct kvm_sev_info *owner_sev_info =to_kvm_sev_info (dst->enc_context_owner); +if (is_mirroring_enc_context (src_kvm)) { +struct kvm_sev_info *owner_sev_info =to_kvm_sev_info (src->enc_context_owner); + dst->enc_context_owner = src->enc_context_owner; + src->enc_context_owner =NULL ;list_del (&src->mirror_entry);list_add_tail (&dst->mirror_entry, &owner_sev_info->mirror_vms); } +mutex_unlock (&sev_mirror_lock);kvm_for_each_vcpu (i, dst_vcpu, dst_kvm) { dst_svm =to_svm (dst_vcpu);
@@ -2885,11 +2890,14 @@ int sev_vm_copy_enc_context_from(struct* disappear until we're done with it */ source_sev =to_kvm_sev_info (source_kvm); -kvm_get_kvm (source_kvm); -list_add_tail (&mirror_sev->mirror_entry, &source_sev->mirror_vms);/* Set enc_context_owner and copy its encryption context over */ +mutex_lock (&sev_mirror_lock); +kvm_get_kvm (source_kvm); +list_add_tail (&mirror_sev->mirror_entry, &source_sev->mirror_vms); mirror_sev->enc_context_owner = source_kvm; +mutex_unlock (&sev_mirror_lock); + mirror_sev->active = true; mirror_sev->asid = source_sev->asid; mirror_sev->fd = source_sev->fd;
@@ -2977,11 +2985,19 @@ void sev_vm_destroy(struct kvm *kvm)* Note, mirror VMs don't support registering encrypted regions. */ if (is_mirroring_enc_context (kvm)) { -struct kvm *owner_kvm = sev->enc_context_owner; +struct kvm *owner_kvm; -mutex_lock (&owner_kvm->lock); +mutex_lock (&sev_mirror_lock); + owner_kvm = sev->enc_context_owner;list_del (&sev->mirror_entry); -mutex_unlock (&owner_kvm->lock); + sev->enc_context_owner =NULL ; + +/* +* The reference to owner_kvm cannot move after sev_mirror_lock is +* released. Release it before kvm_put_kvm() so that owner_kvm is +* never destroyed inside sev_mirror_lock. +*/ +mutex_unlock (&sev_mirror_lock);kvm_put_kvm (owner_kvm);return ; }
arch/x86/kvm/svm/svm.h +1 -0
@@ -106,6 +106,7 @@ struct kvm_sev_info {u64 ap_jump_table;/* SEV-ES AP Jump Table address */ u64 vmsa_features;u16 ghcb_version;/* Highest guest GHCB protocol version allowed */ +/* The three fields below are protected by sev_mirror_lock */ struct kvm *enc_context_owner;/* Owner of copied encryption context */ struct list_head mirror_vms;/* List of VMs mirroring */ struct list_head mirror_entry;/* Use as a list entry of mirrors */
Greg Kroah-Hartman
maintainerPATCH 243/271 KVM: x86/mmu: WARN and clear role.invalid when creating a child shadow page
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit 5ec42d57655c690234c14aece6dd3f209778c1d8 upstream.
Explicitly clear role.invalid when deriving a child shadow page's role from its parent to harden against bugs elsewhere in KVM, as violating KVM's invariant that invalid pages are NOT on the list of active MMU pages leads to use-after-free due to __kvm_mmu_prepare_zap_page() using list_add() instead of list_move() when processing an invalid shadow page, i.e. makes a bad situation far worse.
Yell loudly if the parent is invalid, as it means KVM has missed a validity check, i.e. KVM is attempting to map memory using an invalid/obsolete root, but continue on as the child is otherwise still a valid shadow page.
==================================================================
BUG: KASAN: slab-use-after-free in __kvm_mmu_get_shadow_page+0x1817/0x1860 [kvm] Write of size 8 at addr ff11000153dd1368 by task repro/853 CPU: 1 UID: 1000 PID: 853 Comm: repro Not tainted 7.2.0-rc2-3aec122bdcaf-next-vm #5 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 Call Trace: <TASK> dump_stack_lvl+0x4b/0x70 print_report+0x153/0x49c kasan_report+0xbc/0xf0 __kvm_mmu_get_shadow_page+0x1817/0x1860 [kvm] mmu_alloc_root+0x141/0x320 [kvm] kvm_mmu_load+0x612/0x20f0 [kvm] kvm_arch_vcpu_ioctl_run+0x3dd5/0x6150 [kvm] kvm_vcpu_ioctl+0x5e4/0x10d0 [kvm] __x64_sys_ioctl+0x131/0x1b0 do_syscall_64+0x67/0x5f0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 </TASK> Allocated by task 853: kasan_save_stack+0x20/0x40 kasan_save_track+0x14/0x30 __kasan_slab_alloc+0x5f/0x70 kmem_cache_alloc_noprof+0xfe/0x2e0 __kvm_mmu_topup_memory_cache+0x135/0x530 [kvm] paging64_page_fault+0x318/0x1e30 [kvm] kvm_mmu_do_page_fault+0x21d/0x630 [kvm] kvm_mmu_page_fault+0x18c/0x17b0 [kvm] kvm_arch_vcpu_ioctl_run+0x1f35/0x6150 [kvm] kvm_vcpu_ioctl+0x5e4/0x10d0 [kvm] __x64_sys_ioctl+0x131/0x1b0 do_syscall_64+0x67/0x5f0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Freed by task 853: kasan_save_stack+0x20/0x40 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x43/0x70 kmem_cache_free+0xe2/0x400 kvm_mmu_commit_zap_page.part.0+0x1e2/0x310 [kvm] kvm_mmu_free_roots+0x283/0x560 [kvm] kvm_arch_vcpu_ioctl_run+0x33c8/0x6150 [kvm] kvm_vcpu_ioctl+0x5e4/0x10d0 [kvm] __x64_sys_ioctl+0x131/0x1b0 do_syscall_64+0x67/0x5f0 entry_SYSCALL_64_after_hwframe+0x4b/0x53
--- arch/x86/kvm/mmu/mmu.c | 3 +++ 1 file changed, 3 insertions(+)
arch/x86/kvm/mmu/mmu.c +3 -0
@@ -2415,6 +2415,9 @@ static union kvm_mmu_page_role kvm_mmu_c role.direct = direct; role.passthrough =0 ; +WARN_ON_ONCE (role.invalid); + role.invalid =0 ; +/* * If the guest has 4-byte PTEs then that means it's using 32-bit, * 2-level, non-PAE paging. KVM shadows such guests with PAE paging
Greg Kroah-Hartman
maintainerPATCH 244/271 eventfs: Fix use-after-free in eventfs_remove_rec()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuangpeng Bai <[email protected]>
commit fd73b691702170d37d66f4b0278530cea8ed419a upstream.
eventfs_remove_rec() recursively removes the child at the current loop position. After the recursive call returns, list_for_each_entry() advances by reading list.next from the removed child.
If free_ei() drops the final reference, release_ei() reuses the list/rcu union to queue an SRCU callback. The child may be freed before that read. The eventfs_mutex serializes list updates, but it does not keep the removed child alive or prevent the SRCU callback from running.
Use list_for_each_entry_safe() to save the next sibling before recursively removing the current child.
--- fs/tracefs/event_inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/tracefs/event_inode.c +2 -2
@@ -824,7 +824,7 @@ struct eventfs_inode *eventfs_create_eve*/ static void eventfs_remove_rec (struct eventfs_inode *ei,int level) { -struct eventfs_inode *ei_child; +struct eventfs_inode *ei_child, *tmp;/* * Check recursion depth. It should never be greater than 3:
@@ -837,7 +837,7 @@ static void eventfs_remove_rec(struct evreturn ;/* search for nested folders or files */ -list_for_each_entry (ei_child, &ei->children, list) +list_for_each_entry_safe (ei_child, tmp, &ei->children, list)eventfs_remove_rec (ei_child, level +1 );list_del_rcu (&ei->list);
Greg Kroah-Hartman
maintainerPATCH 245/271 eventfs: Use children field for rcu head and add memory barriers
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
commit f0ece16ffca7384787b692431961ce202907acf5 upstream.
When an eventfs inode is freed, it sets ei->is_freed and then uses its ei->list to add it to the srcu link list as the list field is a union with the rcu list head. As the ei->list is used to iterate over an SRCU protected list without taking the eventfs_mutex, there's nothing stopping the iteration over that list to see the ei->rcu instead of the ei->list and it will read a corrupt target.
To fix this, change the union of the rcu list head with the children list. On freeing the eventfs inode, set the is_free and execute a smp_wmb() before adding the eventfs inode to the SRCU list.
On iteration of the ei->children list, at the start, execute a smp_rmb() and then read the is_freed of the ei to see if the children list is still valid. If is_freed is set, then the ei_child read is not valid and the loop should exit immediately.
--- fs/tracefs/event_inode.c | 24 ++++++++++++++++++++++++ fs/tracefs/internal.h | 4 ++-- 2 files changed, 26 insertions(+), 2 deletions(-)
fs/tracefs/event_inode.c +24 -0
@@ -124,7 +124,17 @@ static inline void put_ei(struct eventfsstatic inline void free_ei (struct eventfs_inode *ei) {if (ei) { +/* The ei should have no children if it is being freed. */ +WARN_ON_ONCE (!list_empty (&ei->children)); ei->is_freed =1 ; +/* +* The SRCU iteration has a smp_rmb() to make sure it +* sees a child (that may have already been freed) +* before it reads is_free. If is_free is set, it must +* not use the child it acquired from ei->children, as +* the list may be used for SRCU. +*/ +smp_wmb ();put_ei (ei); } }
@@ -629,6 +639,20 @@ static int eventfs_iterate(struct file *list_for_each_entry_srcu (ei_child, &ei->children, list,srcu_read_lock_held (&eventfs_srcu)) { +/* +* If the ei is being freed, then the ei->children may be +* being used as the rcu list, which means the next element +* may be garbage. The ei->is_free is set before switching +* the ei->children over to ei->rcu. The read memory barrier +* here makes sure the ei_child is read before is_free is +* updated. +* +* Matches the smp_wmb() in free_ei() +*/ +smp_rmb (); +if (ei->is_freed) +return -EINVAL ; +if (c >0 ) { c--;continue ;
fs/tracefs/internal.h +2 -2
@@ -46,11 +46,11 @@ struct eventfs_attr {* @ino: The saved inode number */ struct eventfs_inode { +struct list_head list;union { -struct list_head list; +struct list_head children;struct rcu_head rcu; }; -struct list_head children;const struct eventfs_entry *entries;const char *name;struct eventfs_attr *entry_attrs;
Greg Kroah-Hartman
maintainerPATCH 246/271 ring-buffer: Prevent resizing of persistent ring buffer
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
commit 7c727dfce6be04dd009b29091a4a17d952dbfe03 upstream.
Dynamically resizing a persistent ring buffer is not possible. Disable the feature.
--- kernel/trace/ring_buffer.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/trace/ring_buffer.c +2 -0
@@ -2418,6 +2418,8 @@ rb_allocate_cpu_buffer(struct trace_buffif (cpu_buffer->ring_meta->head_buffer)rb_meta_buffer_update (cpu_buffer, bpage); bpage->range =1 ; + +atomic_inc (&cpu_buffer->resize_disabled); }else if (buffer->remote) {struct ring_buffer_desc *desc =ring_buffer_desc (buffer->remote->desc, cpu);
Greg Kroah-Hartman
maintainerPATCH 247/271 Revert "thermal/drivers/hwmon: Cleanup coding style a bit"
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rafael J. Wysocki <[email protected]>
commit ff8da20b6f47c48d46e47f93f7a59e2d56ee9107 upstream.
Revert commit 030a48b0f6ce ("thermal/drivers/hwmon: Cleanup coding style a bit") that introduced a use-after-free into the error path of thermal_add_hwmon_sysfs() by removing a valid check from it.
--- drivers/thermal/thermal_hwmon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/thermal/thermal_hwmon.c +2 -1
@@ -157,7 +157,8 @@ int thermal_add_hwmon_sysfs(struct therm unregister_name:hwmon_device_unregister (hwmon->device); free_mem: -kfree (hwmon); +if (new_hwmon_device) +kfree (hwmon);return result; }
Greg Kroah-Hartman
maintainerPATCH 248/271 Revert "thermal: hwmon: Register a hwmon device for each thermal zone"
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rafael J. Wysocki <[email protected]>
commit f93d951ce0d02b5dca01c0c72add411fb17849bb upstream.
Revert commit d6323469bcfb ("thermal: hwmon: Register a hwmon device for each thermal zone") that changed the names of hwmon class devices associated with thermal zones and their sysfs layout which made user space unhappy.
--- drivers/thermal/thermal_hwmon.c | 151 +++++++++++++++++++++++++++------------- 1 file changed, 104 insertions(+), 47 deletions(-)
drivers/thermal/thermal_hwmon.c +104 -47
@@ -19,33 +19,30 @@#include "thermal_hwmon.h" #include "thermal_core.h" -/* -* Needs to be large enough to hold a thermal zone type string followed by an -* underline character and a 32-bit integer in decimal representation. -*/ -#define THERMAL_HWMON_NAME_LENGTH (THERMAL_NAME_LENGTH + 11) +/* hwmon sys I/F */ +/* thermal zone devices with the same type share one hwmon device */ +struct thermal_hwmon_device { +char type[THERMAL_NAME_LENGTH ]; +struct device *device; +int count; +struct list_head tz_list; +struct list_head node; +};struct thermal_hwmon_attr {struct device_attribute attr; +char name[16 ]; };/* one temperature input for each thermal zone */ struct thermal_hwmon_temp { +struct list_head hwmon_node;struct thermal_zone_device *tz;struct thermal_hwmon_attr temp_input;/* hwmon sys attr */ struct thermal_hwmon_attr temp_crit;/* hwmon sys attr */ bool temp_crit_present; }; -/* hwmon sys I/F */ -/* thermal zone devices with the same type share one hwmon device */ -struct thermal_hwmon_device { -char name[THERMAL_HWMON_NAME_LENGTH ]; -struct device *device; -struct list_head node; -struct thermal_hwmon_temp tz_temp; -}; -static LIST_HEAD (thermal_hwmon_list);static DEFINE_MUTEX (thermal_hwmon_list_lock);
@@ -91,6 +88,45 @@ temp_crit_show(struct device *dev, strucreturn sysfs_emit (buf,"%d\n" , temperature); } + +static struct thermal_hwmon_device * +thermal_hwmon_lookup_by_type (const struct thermal_zone_device *tz) +{ +struct thermal_hwmon_device *hwmon; +char type[THERMAL_NAME_LENGTH ]; + +mutex_lock (&thermal_hwmon_list_lock); +list_for_each_entry (hwmon, &thermal_hwmon_list, node) { +strscpy (type, tz->type); +strreplace (type,'-' ,'_' ); +if (!strcmp (hwmon->type, type)) { +mutex_unlock (&thermal_hwmon_list_lock); +return hwmon; + } + } +mutex_unlock (&thermal_hwmon_list_lock); + +return NULL ; +} + +/* Find the temperature input matching a given thermal zone */ +static struct thermal_hwmon_temp * +thermal_hwmon_lookup_temp (const struct thermal_hwmon_device *hwmon, +const struct thermal_zone_device *tz) +{ +struct thermal_hwmon_temp *temp; + +mutex_lock (&thermal_hwmon_list_lock); +list_for_each_entry (temp, &hwmon->tz_list, hwmon_node) +if (temp->tz == tz) { +mutex_unlock (&thermal_hwmon_list_lock); +return temp; + } +mutex_unlock (&thermal_hwmon_list_lock); + +return NULL ; +} +static bool thermal_zone_crit_temp_valid (struct thermal_zone_device *tz) {int temp;
@@ -101,39 +137,54 @@ int thermal_add_hwmon_sysfs(struct therm {struct thermal_hwmon_device *hwmon;struct thermal_hwmon_temp *temp; +int new_hwmon_device =1 ;int result; + hwmon =thermal_hwmon_lookup_by_type (tz); +if (hwmon) { + new_hwmon_device =0 ; + goto register_sys_interface; + } + hwmon =kzalloc_obj (*hwmon);if (!hwmon)return -ENOMEM ; -/* -* Append the thermal zone ID preceded by an underline character to the -* type to disambiguate the sensors command output. -*/ -scnprintf (hwmon->name,THERMAL_HWMON_NAME_LENGTH ,"%s_%d" , tz->type, tz->id); -strreplace (hwmon->name,'-' ,'_' ); +INIT_LIST_HEAD (&hwmon->tz_list); +strscpy (hwmon->type, tz->type,THERMAL_NAME_LENGTH ); +strreplace (hwmon->type,'-' ,'_' ); hwmon->device =hwmon_device_register_for_thermal (&tz->device, - hwmon->name, hwmon); + hwmon->type, hwmon);if (IS_ERR (hwmon->device)) { result =PTR_ERR (hwmon->device); goto free_mem; } - temp = &hwmon->tz_temp; + register_sys_interface: + temp =kzalloc_obj (*temp); +if (!temp) { + result = -ENOMEM ; + goto unregister_name; + } temp->tz = tz; + hwmon->count++; - temp->temp_input.attr.attr.name ="temp1_input" ; +snprintf (temp->temp_input.name,sizeof (temp->temp_input.name), +"temp%d_input" , hwmon->count); + temp->temp_input.attr.attr.name = temp->temp_input.name; temp->temp_input.attr.attr.mode =0444 ; temp->temp_input.attr.show = temp_input_show;sysfs_attr_init (&temp->temp_input.attr.attr); result =device_create_file (hwmon->device, &temp->temp_input.attr);if (result) - goto unregister_name; + goto free_temp_mem;if (thermal_zone_crit_temp_valid (tz)) { - temp->temp_crit.attr.attr.name ="temp1_crit" ; +snprintf (temp->temp_crit.name, +sizeof (temp->temp_crit.name), +"temp%d_crit" , hwmon->count); + temp->temp_crit.attr.attr.name = temp->temp_crit.name; temp->temp_crit.attr.attr.mode =0444 ; temp->temp_crit.attr.show = temp_crit_show;sysfs_attr_init (&temp->temp_crit.attr.attr);
@@ -145,17 +196,21 @@ int thermal_add_hwmon_sysfs(struct therm temp->temp_crit_present = true; } -/* The list is needed for hwmon lookup during removal. */ mutex_lock (&thermal_hwmon_list_lock); -list_add_tail (&hwmon->node, &thermal_hwmon_list); +if (new_hwmon_device) +list_add_tail (&hwmon->node, &thermal_hwmon_list); +list_add_tail (&temp->hwmon_node, &hwmon->tz_list);mutex_unlock (&thermal_hwmon_list_lock);return 0 ; unregister_input:device_remove_file (hwmon->device, &temp->temp_input.attr); + free_temp_mem: +kfree (temp); unregister_name: -hwmon_device_unregister (hwmon->device); +if (new_hwmon_device) +hwmon_device_unregister (hwmon->device); free_mem:if (new_hwmon_device)kfree (hwmon);
@@ -164,37 +219,39 @@ int thermal_add_hwmon_sysfs(struct therm }EXPORT_SYMBOL_GPL (thermal_add_hwmon_sysfs); -static struct thermal_hwmon_device * -thermal_hwmon_lookup (const struct thermal_zone_device *tz) -{ -struct thermal_hwmon_device *hwmon; - -list_for_each_entry (hwmon, &thermal_hwmon_list, node) { -if (hwmon->tz_temp.tz == tz) -return hwmon; - } -return NULL ; -} -void thermal_remove_hwmon_sysfs (struct thermal_zone_device *tz) {struct thermal_hwmon_device *hwmon;struct thermal_hwmon_temp *temp; - scoped_guard(mutex, &thermal_hwmon_list_lock) { - hwmon =thermal_hwmon_lookup (tz); -if (!hwmon) -return ; - -list_del (&hwmon->node); + hwmon =thermal_hwmon_lookup_by_type (tz); +if (unlikely (!hwmon)) { +/* Should never happen... */ +dev_dbg (&tz->device,"hwmon device lookup failed!\n" ); +return ; } - temp = &hwmon->tz_temp; + temp =thermal_hwmon_lookup_temp (hwmon, tz); +if (unlikely (!temp)) { +/* Should never happen... */ +dev_dbg (&tz->device,"temperature input lookup failed!\n" ); +return ; + }device_remove_file (hwmon->device, &temp->temp_input.attr);if (temp->temp_crit_present)device_remove_file (hwmon->device, &temp->temp_crit.attr); +mutex_lock (&thermal_hwmon_list_lock); +list_del (&temp->hwmon_node); +kfree (temp); +if (!list_empty (&hwmon->tz_list)) { +mutex_unlock (&thermal_hwmon_list_lock); +return ; + } +list_del (&hwmon->node); +mutex_unlock (&thermal_hwmon_list_lock); +hwmon_device_unregister (hwmon->device);kfree (hwmon); }
Greg Kroah-Hartman
maintainerPATCH 249/271 ptp: ocp: Fix board ID over-read
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ahmad Byagowi <[email protected]>
commit 6b69f2ef10cdb018c0b127a7cab88e590bbddba4 upstream.
The EEPROM board ID is a fixed 13-byte field and is not guaranteed to contain a NUL terminator. Passing it directly to devlink_info_version_fixed_put() treats it as a C string and may read beyond the field.
Format at most OCP_BOARD_ID_LEN bytes into the existing local buffer before reporting the ID. Use a precision limit because the snprintf() output size alone does not bound the source string scan.
--- drivers/ptp/ptp_ocp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/ptp/ptp_ocp.c +3 -1
@@ -2180,9 +2180,11 @@ ptp_ocp_devlink_info_get(struct devlinkif (err)return err; +snprintf (buf,sizeof (buf),"%.*s" ,OCP_BOARD_ID_LEN , + (const char *)bp->board_id); err =devlink_info_version_fixed_put (req,DEVLINK_INFO_VERSION_GENERIC_BOARD_ID , - bp->board_id); + buf);if (err)return err;
Greg Kroah-Hartman
maintainerPATCH 250/271 ring-buffer: Initialise reader page order in rb_allocate_cpu_buffer()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
commit 6d014e44b68ddd43f71288d2a4dbb1a259869149 upstream.
In rb_allocate_cpu_buffer(), bpage->order was omitted, leaving it as 0. This is an issue for a ring-buffer with subbufs bigger than PAGE_SIZE if when freed: free_buffer_page() relies on this value. Align the value with the actual allocation size (buffer::subbuf_order).
--- kernel/trace/ring_buffer.c | 1 + 1 file changed, 1 insertion(+)
kernel/trace/ring_buffer.c +1 -0
@@ -2400,6 +2400,7 @@ rb_allocate_cpu_buffer(struct trace_buff bpage =alloc_cpu_page (cpu);if (!bpage)return NULL ; + bpage->order = cpu_buffer->buffer->subbuf_order;rb_check_bpage (cpu_buffer, bpage);
Greg Kroah-Hartman
maintainerPATCH 251/271 ring-buffer: Use current_context for safe per-CPU buffer swap
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tengda Wu <[email protected]>
commit f27bdc43077e4fcb5557dfc315ee8d91e741f483 upstream.
The ring_buffer_swap_cpu() function currently checks the per-CPU committing counter to determine if a buffer is actively being written to before performing the swap. However, there exists a race window where this check can be bypassed:
ring_buffer_lock_reserve
cpu_buffer = buffer->buffers[cpu]; // cpu_buffer_a
rb_reserve_next_event
rb_start_commit // inc committing
if (unlikely(READ_ONCE(cpu_buffer->buffer) != buffer)) {...}
__rb_reserve_next
rb_move_tail
rb_end_commit(cpu_buffer); // dec committing => 0
/* interrupt hits here, successfully swaps! */
local_inc(&cpu_buffer->committing);
ring_buffer_unlock_commit
cpu_buffer = buffer->buffers[cpu]; // cpu_buffer_b
rb_commit
rb_end_commit
RB_WARN_ON(cpu_buffer, !local_read(&cpu_buffer->committing))
// triggers warningThe committing counter can temporarily drop to 0 during a single write operation (within rb_move_tail), creating a window where swap can succeed even though the write is still in progress. This leads to inconsistent buffer state and triggers the RB_WARN_ON in rb_commit().
Replace the committing counter check with current_context checks, which are set at the entry of ring_buffer_lock_reserve() and remain valid throughout the entire write operation, providing a reliable indicator of buffer busy state during swap.
--- kernel/trace/ring_buffer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
kernel/trace/ring_buffer.c +4 -4
@@ -6683,7 +6683,7 @@ int ring_buffer_swap_cpu(struct trace_bu {struct ring_buffer_per_cpu *cpu_buffer_a;struct ring_buffer_per_cpu *cpu_buffer_b; -int ret = -EINVAL ; +int ret = -EBUSY ;if (!cpumask_test_cpu (cpu, buffer_a->cpumask) || !cpumask_test_cpu (cpu, buffer_b->cpumask))
@@ -6724,10 +6724,10 @@ int ring_buffer_swap_cpu(struct trace_buatomic_inc (&cpu_buffer_a->record_disabled);atomic_inc (&cpu_buffer_b->record_disabled); - ret = -EBUSY ; -if (local_read (&cpu_buffer_a->committing)) +/* Do not swap if either buffer is in the process of writing */ +if (cpu_buffer_a->current_context) goto out_dec; -if (local_read (&cpu_buffer_b->committing)) +if (cpu_buffer_b->current_context) goto out_dec;/*
Greg Kroah-Hartman
maintainerPATCH 252/271 mm/page_table_check: skip special zero mappings
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit 8db4bab826ccc9ec10fa41736a48031cd338d392 upstream.
page_table_check_set() and page_table_check_clear() account mappings based on PageAnon(). Shared zero-page PTEs and huge zero PMDs are special mappings, but page_table_check can still account them as file-backed pages.
An unprivileged process can populate enough zero mappings to overflow file_map_count and hit the existing BUG_ON(). The PTE path can do this with the shared zero page, and the PMD path can do the same with huge zero mappings.
Skip special zero mappings in the user page-table accounting paths. Keep the PTE-side pte_special() check, and identify huge zero PMDs from the mapped folio instead of pmd_special(). That covers architectures where pmd_special() is a no-op without adding huge_zero_pfn checks to the generic counter helpers.
--- mm/page_table_check.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
mm/page_table_check.c +16 -4
@@ -151,18 +151,29 @@ void __page_table_check_pte_clear(structif (&init_mm == mm)return ; -if (pte_user_accessible_page (mm, addr, pte)) +if (pte_user_accessible_page (mm, addr, pte) && !pte_special (pte))page_table_check_clear (pte_pfn (pte),PAGE_SIZE >>PAGE_SHIFT ); }EXPORT_SYMBOL (__page_table_check_pte_clear); +static inline bool page_table_check_huge_zero_pmd (pmd_t pmd) +{ +unsigned long pfn =pmd_pfn (pmd); + +if (!pfn_valid (pfn)) +return false; + +return is_huge_zero_folio (page_folio (pfn_to_page (pfn))); +} +void __page_table_check_pmd_clear (struct mm_struct *mm,unsigned long addr,pmd_t pmd) {if (&init_mm == mm)return ; -if (pmd_user_accessible_page (mm, addr, pmd)) +if (pmd_user_accessible_page (mm, addr, pmd) && + !page_table_check_huge_zero_pmd (pmd))page_table_check_clear (pmd_pfn (pmd),PMD_SIZE >>PAGE_SHIFT ); }EXPORT_SYMBOL (__page_table_check_pmd_clear);
@@ -208,7 +219,7 @@ void __page_table_check_ptes_set(structfor (i =0 ; i < nr; i++)__page_table_check_pte_clear (mm, addr +PAGE_SIZE * i,ptep_get (ptep + i)); -if (pte_user_accessible_page (mm, addr, pte)) +if (pte_user_accessible_page (mm, addr, pte) && !pte_special (pte))page_table_check_set (pte_pfn (pte), nr,pte_write (pte)); }EXPORT_SYMBOL (__page_table_check_ptes_set);
@@ -238,7 +249,8 @@ void __page_table_check_pmds_set(structfor (i =0 ; i < nr; i++)__page_table_check_pmd_clear (mm, addr +PMD_SIZE * i, *(pmdp + i)); -if (pmd_user_accessible_page (mm, addr, pmd)) +if (pmd_user_accessible_page (mm, addr, pmd) && + !page_table_check_huge_zero_pmd (pmd))page_table_check_set (pmd_pfn (pmd), stride * nr,pmd_write (pmd)); }EXPORT_SYMBOL (__page_table_check_pmds_set);
Greg Kroah-Hartman
maintainerPATCH 253/271 mm/ptdump: always stabilise against page table freeing using init_mm
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes (ARM) <[email protected]>
commit 27c32e5538344b13c1505a08861e04620c125d47 upstream.
Previous commits have established the invariant that kernel page table freeing is performed while an mmap read lock on init_mm is held, which fixes races between ptdump and kernel page table freeing over init_mm.
However, x86 and arm64 can perform a ptdump over an mm other than init_mm via ptdump_walk_pgd() and since kernel memory ranges are shared across non-kernel mm's, this means that the race still exists for these cases.
Fix this by acquiring a nested mmap write lock for init_mm in ptdump_walk_pgd().
This is safe as we take this after mmap write locking the mm, and nothing acquires the init_mm lock first before locking an arbitrary mm, so no deadlock is possible.
Also update walk_page_range_debug() to assert that init_mm is write locked, add a comment explaining why and remove some redundant code, and eliminate the unnecessary and confusing invocation of walk_kernel_page_table_range().
We can safely remove the non-NULL check for walk.mm, as the mmap lock asserts would NULL pointer deref if it was (and of course no callers do this).
The first point at which ptdump can race kernel page table freeing is commit b6bdb7517c3d ("mm/vmalloc: add interfaces to free unmapped page table"), so we target this in the Fixes tag.
--- mm/pagewalk.c | 14 +++++++++----- mm/ptdump.c | 7 +++++++ 2 files changed, 16 insertions(+), 5 deletions(-)
mm/pagewalk.c +9 -5
@@ -702,12 +702,16 @@ int walk_page_range_debug(struct mm_stru* to account for page table freeing on vmap huge page mapping. */ mmap_assert_write_locked (mm); +/* +* x86, arm64 ptdump allow walks of efi mm's and x86 ptdump allows walks +* of arbitrary mm's. +* +* However, they both must also hold the init_mm lock to account for +* concurrent kernel page table freeing. +*/ +mmap_assert_write_locked (&init_mm); -/* For convenience, we allow traversal of kernel mappings. */ -if (mm == &init_mm) -return walk_kernel_page_table_range (start, end, ops, - pgd, private); -if (start >= end || !walk.mm) +if (start >= end)return -EINVAL ;if (!check_ops_safe (ops))return -EINVAL ;
mm/ptdump.c +7 -0
@@ -178,11 +178,18 @@ void ptdump_walk_pgd(struct ptdump_stateget_online_mems ();mmap_write_lock (mm); +/* To stabilise kernel page tables we must hold the init_mm lock too. */ +if (mm != &init_mm) +mmap_write_lock_nested (&init_mm,SINGLE_DEPTH_NESTING ); +while (range->start != range->end) {walk_page_range_debug (mm, range->start, range->end, &ptdump_ops, pgd, st); range++; } + +if (mm != &init_mm) +mmap_write_unlock (&init_mm);mmap_write_unlock (mm);put_online_mems ();
Greg Kroah-Hartman
maintainerPATCH 254/271 ipv6: fix Route Information option length validation
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuejie Shi <[email protected]>
commit d1ad8fb2ac6a1afb71dc22d9ae8efb4dda96c824 upstream.
rt6_route_rcv() validates the Route Information option (RFC 4191) length against the prefix length, but both checks are off by one.
rinfo->length is the ND option length in units of 8 octets and it *includes* the 8-byte option header, so an option carrying N bytes of prefix has length == 1 + N/8. RFC 4191 section 2.3 requires length 3 when Prefix Length is greater than 64, and 2 or 3 when it is greater than 0. The code accepts length >= 2 and length >= 1 respectively.
ipv6_addr_prefix() then copies prefix_len/8 bytes out of rinfo->prefix, so a Router Advertisement with (prefix_len=128, length=2) or (prefix_len=64, length=1) makes the kernel read up to 8 bytes past the end of the option. Those bytes end up in the prefix of the route that gets installed, so they are visible to userspace:
# RA with a Route Information option (prefix_len=128, length=2)
# followed by a source link-layer address option, 01 01 de ad be ef ca fe
$ ip -6 route show
2001:db8:dead:beef:101:dead:beef:cafe via fe80::1234 dev veth0 proto ra
^^^^^^^^^^^^^^^^^^ the next option, read out of boundsWhen the Route Information option is the last one in the packet, those eight bytes come from the skb tail room instead.
Reject the option lengths RFC 4191 does not allow.
--- net/ipv6/route.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
net/ipv6/route.c +4 -4
@@ -987,13 +987,13 @@ int rt6_route_rcv(struct net_device *dev }else if (rinfo->prefix_len >128 ) {return -EINVAL ; }else if (rinfo->prefix_len >64 ) { -if (rinfo->length <2 ) { +/* RFC 4191: Length MUST be 3 when Prefix Length > 64 */ +if (rinfo->length <3 )return -EINVAL ; - } }else if (rinfo->prefix_len >0 ) { -if (rinfo->length <1 ) { +/* RFC 4191: Length MUST be 2 or 3 when Prefix Length > 0 */ +if (rinfo->length <2 )return -EINVAL ; - } } pref = rinfo->route_pref;
Greg Kroah-Hartman
maintainerPATCH 255/271 ip6_tunnel: clear skb2->cb[] in ip6ip6_err()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit f803c086399da277b5d0ff36a107d0f162751800 upstream.
ip6ip6_err() clones an outer IPv6 ICMP error skb, pulls it to the quoted inner IPv6 packet, and then passes the clone to icmpv6_send(). The clone still carries the outer packet's inet6_skb_parm in skb->cb.
If the outer packet had a Home Address Option, IP6CB(skb2)->dsthao remains non-zero after skb_pull(). icmpv6_send() later calls mip6_addr_swap(), which uses that stale dsthao offset against the quoted inner packet. A malformed inner destination-options header can then make the HAO lookup and address swap run past the end of the quoted packet and corrupt skb_shared_info.
Clear skb2->cb[] before pulling the quoted inner IPv6 packet so the reply path does not reuse metadata left by the outer IPv6 stack.
--- net/ipv6/ip6_tunnel.c | 3 +++ 1 file changed, 3 insertions(+)
net/ipv6/ip6_tunnel.c +3 -0
@@ -684,6 +684,9 @@ ip6ip6_err(struct sk_buff *skb, struct iif (!skb2)return 0 ; +/* Remove debris left by outer IPv6 stack. */ +memset (IP6CB (skb2),0 ,sizeof (*IP6CB (skb2))); +skb_dst_drop (skb2);skb_pull (skb2, offset);skb_reset_network_header (skb2);
Greg Kroah-Hartman
maintainerPATCH 256/271 fs,fsverity: remove check for fsverity being enabled in setattr_prepare()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrey Albershteyn <[email protected]>
commit d2f96bcb89d36d488a10e3bcf819b98536968286 upstream.
The check that fs-verity is available in the kernel is not necessary here. Filesystems could have fsverity files even without fs-verity enabled. In that case, truncate on fsverity file will succeed, what this check is trying to prevent.
--- fs/attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/attr.c +1 -1
@@ -176,7 +176,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,* covered by the open-time check because sys_truncate() takes a * path, not an open file. */ -if (IS_ENABLED (CONFIG_FS_VERITY ) &&IS_VERITY (inode)) +if (IS_VERITY (inode))return -EPERM ; error =inode_newsize_ok (inode, attr->ia_size);
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 257/271 fscrypt: use the mount idmap for the owner check in fscrypt_ioctl_set_policy()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
commit cf6c993c0feca7984797e634deba3c80342e199a upstream.
fscrypt_ioctl_set_policy() calls inode_owner_or_capable() with &nop_mnt_idmap before allowing an encryption policy to be set, instead of the idmap of the mount the ioctl was issued on.
fscrypt is used by filesystems that support idmapped mounts (e.g. ext4, f2fs), so on such a mount this compares the caller's fsuid against the unmapped on-disk owner rather than the mapped owner: the actual owner can be wrongly denied with -EACCES and an unrelated caller wrongly allowed. Use file_mnt_idmap(filp) instead.
--- fs/crypto/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/crypto/policy.c +1 -1
@@ -534,7 +534,7 @@ int fscrypt_ioctl_set_policy(struct filereturn -EFAULT ; policy.version = version; -if (!inode_owner_or_capable (&nop_mnt_idmap, inode)) +if (!inode_owner_or_capable (file_mnt_idmap (filp), inode))return -EACCES ; ret =mnt_want_write_file (filp);
Greg Kroah-Hartman
maintainerPATCH 258/271 sched/psi: Shut down rtpoll_timer in psi_cgroup_free()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit 5457025fa8ca3c0d2732109513de839e3e797190 upstream.
psi_schedule_rtpoll_work() is called locklessly from the scheduler hotpath and can race psi_trigger_destroy() taking down the last rtpoll trigger under rtpoll_trigger_lock:
psi_schedule_rtpoll_work() psi_trigger_destroy()
rcu_read_lock();
task = rcu_dereference(rtpoll_task);
rcu_assign_pointer(rtpoll_task, NULL);
timer_delete(&rtpoll_timer);
mod_timer(&rtpoll_timer, ...);
rcu_read_unlock();
synchronize_rcu();
kthread_stop(task_to_destroy);The group can then be freed with the re-armed timer still pending, and poll_timer_fn() runs on freed memory.
461daba06bdc ("psi: eliminate kthread_worker from psi trigger scheduling mechanism") deleted the timer synchronously after the synchronize_rcu(), which prevented this but raced trigger creation instead: the deletion could cancel the timer that a new trigger set armed during the grace period and, as creation also reinitialized the timer at the time, corrupt it. 8f91efd870ea ("psi: Fix race between psi_trigger_create/destroy") moved the initialization into group_init() and the deletion into the locked section, trading the creation races for the window above.
Neither placement in the destruction path works. A pending timer firing while the group is alive is harmless though. poll_timer_fn() just wakes the rtpoll waitqueue and doesn't re-arm itself. Bind the timer to the group's lifetime instead and shut it down in psi_cgroup_free(). Nothing can arm it by then. timer_shutdown_sync() because the timer is never armed again.
--- kernel/sched/psi.c | 6 ++++++ 1 file changed, 6 insertions(+)
kernel/sched/psi.c +6 -0
@@ -1134,6 +1134,12 @@ void psi_cgroup_free(struct cgroup *cgroreturn ;cancel_delayed_work_sync (&cgroup->psi->avgs_work); +/* +* A psi_schedule_rtpoll_work() call racing the last trigger's +* destruction may have re-armed the timer after psi_trigger_destroy() +* deleted it. Spurious firing while the group is alive is harmless. +*/ +timer_shutdown_sync (&cgroup->psi->rtpoll_timer);free_percpu (cgroup->psi->pcpu);/* All triggers must be removed by now */ WARN_ONCE (cgroup->psi->rtpoll_states,"psi: trigger leak\n" );
Greg Kroah-Hartman
maintainerPATCH 259/271 sched/psi: Create the psimon kthread outside of cgroup_mutex
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit fadeedd7cfc5d73d33fa3d7ac54b9b27aabd09d2 upstream.
a5b98009f16d ("sched/psi: fix race between file release and pressure write") made pressure_write() hold cgroup_mutex across psi_trigger_create(), which forks the psimon kthread for the first rtpoll trigger. As kthread creation depends on the whole fork path, the commit inadvertently created a lot of unwanted locking dependencies from cgroup_mutex.
sched_ext got hit by one: its enable path blocks forks and then grabs cgroup_mutex, so a pressure write racing a scheduler enable deadlocks, with every other fork piling up behind.
Fix it by splitting trigger creation so that the worker is forked with cgroup_mutex dropped and the kernfs active reference left broken. The latter matters because rmdir and cgroup.pressure writes drain active references under cgroup_mutex. Publishing the trigger last keeps error reporting synchronous and preserves the of->priv lifetime rules.
The trigger registered in the first stage pins the group's rtpoll machinery across the unlocked window, leaving only creation races to resolve. The catch-up poll on installation covers scheduling attempts dropped while there was no worker.
v2: Retagged sched/psi (was cgroup).
--- include/linux/psi.h | 4 ++ kernel/cgroup/cgroup.c | 23 +++++++++++++++- kernel/sched/psi.c | 69 +++++++++++++++++++++++++++++++++++++------------ 3 files changed, 78 insertions(+), 18 deletions(-)
include/linux/psi.h +3 -1
@@ -25,7 +25,9 @@ void psi_memstall_leave(unsigned long *fint psi_show (struct seq_file *s,struct psi_group *group,enum psi_res res);struct psi_trigger *psi_trigger_create (struct psi_group *group,char *buf,enum psi_res res,struct file *file, -struct kernfs_open_file *of); +struct kernfs_open_file *of, +bool *need_rtpoll_worker); +int psi_trigger_create_rtpoll_worker (struct psi_group *group);void psi_trigger_destroy (struct psi_trigger *t); __poll_t psi_trigger_poll(void **trigger_ptr, struct file *file,
kernel/cgroup/cgroup.c +22 -1
@@ -3961,6 +3961,7 @@ static ssize_t pressure_write(struct kerstruct psi_trigger *new;struct cgroup *cgrp;struct psi_group *psi; +bool need_rtpoll_worker;ssize_t ret =0 ; cgrp =cgroup_kn_lock_live (of->kn, false);
@@ -3980,12 +3981,32 @@ static ssize_t pressure_write(struct ker } psi =cgroup_psi (cgrp); - new =psi_trigger_create (psi, buf, res, of->file, of); + new =psi_trigger_create (psi, buf, res, of->file, of, + &need_rtpoll_worker);if (IS_ERR (new)) { ret =PTR_ERR (new); goto out_unlock; } +/* +* The worker fork must run with neither cgroup_mutex nor the file's +* kernfs active reference held. The latter is broken since +* cgroup_kn_lock_live(). @of->priv may be released while unlocked, so +* recheck before publishing @new. +*/ +if (need_rtpoll_worker) { +cgroup_unlock (); + ret =psi_trigger_create_rtpoll_worker (psi); +cgroup_lock (); + +if (!ret && !of->priv) + ret = -ENODEV ; +if (ret) { +psi_trigger_destroy (new); + goto out_unlock; + } + } +smp_store_release (&ctx->psi.trigger, new); out_unlock:
kernel/sched/psi.c +53 -16
112 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 260/271 sched_ext: Take cgroup_lock() first in scx_cgroup_lock()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
commit 5f8b69642d18e1f3e11996707842ac530444e959 upstream.
scx_cgroup_lock() write-locks scx_cgroup_ops_rwsem and then takes cgroup_lock(), which can deadlock through kernfs:
scx enable/disable cgroup rmdir cpu.weight write ------------------ ------------ ----------------
cgroup_lock()
percpu_down_write(rwsem)
cgroup_lock()
kernfs_get_active() percpu_down_read(rwsem)
kernfs_drain()
The enable path waits for the rmdir to release cgroup_mutex. The rmdir, deactivating the cpu controller's files, waits in kernfs_drain() for the write's active reference. The write, in scx_group_set_weight(), waits for the rwsem behind the pending writer.
Take cgroup_lock() first. The set_* paths take no cgroup locks inside the read side, so a pending write-lock then only waits for read sections that always run to completion, and no dependency from the rwsem back to cgroup_mutex remains.
--- kernel/sched/ext.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
kernel/sched/ext.c +7 -2
@@ -4515,20 +4515,25 @@ static struct cgroup *root_cgroup(void) return &cgrp_dfl_root.cgrp; } +/* +* cgroup_lock() must nest outside the rwsem write side: a writer waiting +* for cgroup_mutex deadlocks with cgroup teardown, which holds it while +* draining a set_* file write blocked on the rwsem behind the writer. +*/ static void scx_cgroup_lock (void ) { +cgroup_lock ();#ifdef CONFIG_EXT_GROUP_SCHED percpu_down_write (&scx_cgroup_ops_rwsem);#endif -cgroup_lock (); }static void scx_cgroup_unlock (void ) { -cgroup_unlock ();#ifdef CONFIG_EXT_GROUP_SCHED percpu_up_write (&scx_cgroup_ops_rwsem);#endif +cgroup_unlock (); }#else /* CONFIG_EXT_GROUP_SCHED || CONFIG_EXT_SUB_SCHED */ static struct cgroup *root_cgroup (void ) {return NULL ; }
Greg Kroah-Hartman
maintainerPATCH 261/271 ima: Instantiate file_truncate and path_truncate hooks
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mimi Zohar <[email protected]>
commit b80bed5c871a80151351342c065579405ce77145 upstream.
Instantiate the file_truncate and path_truncate LSM hooks to reset the action cache flags (IMA_DONE_MASK) as soon as truncation is requested, so the file, based on policy, is re-collected, re-measured, re-audited, and re-appraised on next access.
--- security/integrity/ima/ima_main.c | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
security/integrity/ima/ima_main.c +39 -0
@@ -687,6 +687,43 @@ static int ima_file_check(struct file *f MAY_APPEND), FILE_CHECK, 0, false); } +/* +* ima_reset_action_flags - invalidate action flags after a content change +* @inode: inode of the file whose content is about to be truncated +* +* Clear IMA_DONE_MASK so the file is re-collected, re-measured, +* re-audited, and re-appraised on next access. +*/ +static void ima_reset_action_flags (struct inode *inode) +{ +struct ima_iint_cache *iint; + +if (!ima_policy_flag || !S_ISREG (inode->i_mode)) +return ; + + iint =ima_iint_find (inode); +if (!iint) +return ; + +mutex_lock (&iint->mutex); + iint->flags &= ~IMA_DONE_MASK ; + iint->measured_pcrs =0 ; +mutex_unlock (&iint->mutex); +return ; +} + +static int ima_path_truncate (const struct path *path) +{ +ima_reset_action_flags (path->dentry->d_inode); +return 0 ; +} + +static int ima_file_truncate (struct file *file) +{ +ima_reset_action_flags (file_inode (file)); +return 0 ; +} +static int __ima_inode_hash (struct inode *inode,struct file *file,char *buf,size_t buf_size) {
@@ -1300,11 +1337,13 @@ static struct security_hook_list ima_hooLSM_HOOK_INIT (file_release, ima_file_free),LSM_HOOK_INIT (mmap_file, ima_file_mmap),LSM_HOOK_INIT (file_mprotect, ima_file_mprotect), +LSM_HOOK_INIT (file_truncate, ima_file_truncate),LSM_HOOK_INIT (kernel_load_data, ima_load_data),LSM_HOOK_INIT (kernel_post_load_data, ima_post_load_data),LSM_HOOK_INIT (kernel_read_file, ima_read_file),LSM_HOOK_INIT (kernel_post_read_file, ima_post_read_file),LSM_HOOK_INIT (path_post_mknod, ima_post_path_mknod), +LSM_HOOK_INIT (path_truncate, ima_path_truncate), #ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS LSM_HOOK_INIT(key_post_create_or_update, ima_post_key_create_or_update), #endif
Greg Kroah-Hartman
maintainerPATCH 262/271 mm/filemap: __filemap_add_folio() restore index before retrying
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugh Dickins <[email protected]>
commit 86da3f7e1e609e1e8bfbab198af68467c5a015a5 upstream.
In __filemap_add_folio()'s split-a-conflict loop, xas_set_order() is applied repeatedly: each application modifies xas.xa_index, rounding it down according to the split_order attempted at that stage: and if all goes as intended, it eventually (or immediately) converges on an xas_try_split() to the required folio_order, with xas.xa_index now the same as index: then xas_store() puts the new folio into the xarray there.
But if a new node was needed, and GFP_NOWAIT allocation did not get one, the lock is dropped, xas_nomem() used to allocate, and sequence retried. If (that part of) the xarray is unchanged when the lock is reacquired, no problem. But what if the conflict was meanwhile resolved by another thread (perhaps even doing the same thing, inserting a folio at that same index)? Isn't there a danger of now putting our folio into the xarray at an intermediate rounded-down index? With !folio_contains() bug to follow, when CONFIG_DEBUG_VM=y is checking for that.
Fix this with an xas_set_order() to restore the original xas.xa_index at the bottom of the loop, so the retry does a full re-evaluation after reacquiring the lock, and cannot reach xas_store() with the wrong index.
Production was suffering from rare SIGILLs and SIGSEGVs, executable text found a page away from where it belonged, !folio_contains() bug hit when debug enabled: symptoms not seen since this patch went in.
--- mm/filemap.c | 6 ++++++ 1 file changed, 6 insertions(+)
mm/filemap.c +6 -0
@@ -932,6 +932,12 @@ unlock:if (!xas_nomem (&xas, gfp))break ; + +/* +* Lock has been dropped: start again with the original index +* and order (but now with the memory reserved by xas_nomem()). +*/ +xas_set_order (&xas, index, forder); }if (xas_error (&xas))
Greg Kroah-Hartman
maintainerPATCH 263/271 fsverity: Fix bpf_get_fsverity_digest() dynptr assumptions
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Biggers <[email protected]>
commit 3e8ec7c0387273329374f5c7bd61f5f38af71fe1 upstream.
The BPF verifier and the dynptr abstraction ensure that the memory space referenced by a dynptr remains valid. They do not, however, provide any guarantee that the contents of the memory are stable. kfuncs are expected to remain memory-safe even if concurrent modifications occur.
bpf_get_fsverity_digest() didn't follow that: it could crash if arg->digest_size was concurrently modified.
Fix that by using the known-good value hash_alg->digest_size instead.
Also widen 'dynptr_sz' and 'out_digest_sz' to u64 to match the return type of __bpf_dynptr_size(). It doesn't appear that it can actually be more than INT_MAX currently (since __bpf_dynptr_data_rw() excludes file-based pointers), but the correct type might as well be used.
--- fs/verity/measure.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
fs/verity/measure.c +6 -4
@@ -122,11 +122,11 @@ __bpf_kfunc int bpf_get_fsverity_digest( {struct bpf_dynptr_kern *digest_ptr = (struct bpf_dynptr_kern *)digest_p;const struct inode *inode =file_inode (file); -u32 dynptr_sz =__bpf_dynptr_size (digest_ptr); +u64 dynptr_sz =__bpf_dynptr_size (digest_ptr);struct fsverity_digest *arg;const struct fsverity_info *vi;const struct fsverity_hash_alg *hash_alg; -int out_digest_sz; +u64 out_digest_sz;if (dynptr_sz <sizeof (struct fsverity_digest ))return -EINVAL ;
@@ -150,11 +150,13 @@ __bpf_kfunc int bpf_get_fsverity_digest( out_digest_sz = dynptr_sz -sizeof (struct fsverity_digest );/* copy digest */ -memcpy (arg->digest, vi->file_digest,min_t (int, hash_alg->digest_size, out_digest_sz)); +memcpy (arg->digest, vi->file_digest, +min (hash_alg->digest_size, out_digest_sz));/* fill the extra buffer with zeros */ if (out_digest_sz > hash_alg->digest_size) -memset (arg->digest + arg->digest_size,0 , out_digest_sz - hash_alg->digest_size); +memset (arg->digest + hash_alg->digest_size,0 , + out_digest_sz - hash_alg->digest_size);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 264/271 fsverity: Fix silent truncation in bpf_get_fsverity_digest()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Biggers <[email protected]>
commit 7c68ed5c5ad4c185ea9654f5d8ee36560277b7dd upstream.
bpf_get_fsverity_digest() silently truncates the digest if the provided buffer is too small. This is a footgun, and it doesn't match the semantics of the equivalent UAPI (FS_IOC_MEASURE_VERITY).
Change it to return -EOVERFLOW instead, matching FS_IOC_MEASURE_VERITY.
--- fs/verity/measure.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
fs/verity/measure.c +5 -4
@@ -144,14 +144,15 @@ __bpf_kfunc int bpf_get_fsverity_digest( hash_alg = vi->tree_params.hash_alg; + out_digest_sz = dynptr_sz -sizeof (struct fsverity_digest ); +if (out_digest_sz < hash_alg->digest_size) +return -EOVERFLOW ; + arg->digest_algorithm = hash_alg - fsverity_hash_algs; arg->digest_size = hash_alg->digest_size; - out_digest_sz = dynptr_sz -sizeof (struct fsverity_digest ); -/* copy digest */ -memcpy (arg->digest, vi->file_digest, -min (hash_alg->digest_size, out_digest_sz)); +memcpy (arg->digest, vi->file_digest, hash_alg->digest_size);/* fill the extra buffer with zeros */ if (out_digest_sz > hash_alg->digest_size)
Greg Kroah-Hartman
maintainerPATCH 265/271 bpf, sockmap: Fix sk_redir use-after-free in send verdict
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
commit a76624733730e541e4955fdecf506af2f6b20558 upstream.
sk_psock_msg_verdict() takes a socket reference for psock->sk_redir. tcp_bpf_send_verdict() copies that pointer while holding the source socket lock, but does not take a reference for the local copy before dropping the lock around tcp_bpf_sendmsg_redir().
When apply_bytes keeps the cached verdict active, another sendmsg() on the same source socket can consume the remaining bytes and release the cached reference while the first thread still holds only the raw local pointer:
CPU 0 CPU 1
sk_redir = psock->sk_redir
apply_bytes remains nonzero
release_sock(sk)
lock_sock(sk) apply_bytes reaches zero psock->sk_redir = NULL release_sock(sk) tcp_bpf_sendmsg_redir(sk_redir) sock_put(sk_redir)
tcp_bpf_sendmsg_redir(sk_redir)
The final sock_put() can free sk_redir before CPU 0 dereferences it.
KASAN reported:
BUG: KASAN: slab-use-after-free in tcp_bpf_sendmsg_redir+0xf39/0x1020 Read of size 8 at addr ffff888108537090 by task poc/87 Call Trace: tcp_bpf_sendmsg_redir+0xf39/0x1020 tcp_bpf_sendmsg+0x977/0x1a50 __sys_sendto+0x32c/0x3a0 __x64_sys_sendto+0xdb/0x1b0 Allocated by task 85: sk_prot_alloc+0x56/0x210 sk_clone+0x6f/0x14b0 inet_csk_clone_lock+0x24/0x740 tcp_create_openreq_child+0x25/0x2710 tcp_v4_syn_recv_sock+0x10a/0xe00 Freed by task 0: __kasan_slab_free+0x43/0x70 slab_free_after_rcu_debug+0xa6/0x1e0 rcu_core+0x50a/0x1850 Last potentially related work creation: __sk_destruct+0x3da/0x540 sk_psock_destroy+0x81e/0xab0 process_one_work+0x63a/0x1070
Take a temporary socket reference while the source socket lock still protects psock->sk_redir, and drop it after tcp_bpf_sendmsg_redir() returns. This keeps each unlocked use independent of cached-verdict ownership.
--- net/ipv4/tcp_bpf.c | 2 ++ 1 file changed, 2 insertions(+)
net/ipv4/tcp_bpf.c +2 -0
@@ -454,6 +454,7 @@ more_data:case __SK_REDIRECT: redir_ingress = psock->redir_ingress; sk_redir = psock->sk_redir; +sock_hold (sk_redir);sk_msg_apply_bytes (psock, tosend);if (!psock->apply_bytes) {/* Clean up before releasing the sock lock. */
@@ -474,6 +475,7 @@ more_data:if (eval == __SK_REDIRECT)sock_put (sk_redir); +sock_put (sk_redir);lock_sock (sk);sk_mem_uncharge (sk, sent);
Greg Kroah-Hartman
maintainerPATCH 266/271 scsi: scsi_debug: Negate wrapped memcmp() result
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
commit c4f6916a99cf105c3ff340b6210fcbba3fa66b35 upstream.
comp_write_worker() returns true when the compared data matches. memcmp() returns zero for equal data and non-zero for different data, so its result must be negated before it is stored in a bool.
The first segment already uses !memcmp(), but the wrapped segment uses memcmp() directly, reversing the match result. Use !memcmp() there as well.
--- drivers/scsi/scsi_debug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/scsi/scsi_debug.c +2 -2
@@ -4318,8 +4318,8 @@ static bool comp_write_worker(struct sdeif (!res)return res;if (rest) - res =memcmp (fsp, arr + ((num - rest) * lb_size), - rest * lb_size); + res = !memcmp (fsp, arr + ((num - rest) * lb_size), + rest * lb_size);if (!res)return res;if (compare_only)
Greg Kroah-Hartman
maintainerPATCH 267/271 sctp: keep chunk->transport in step with the list it is queued on
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
commit 9f2cf069a9a72a2d6b97ca8b4c70e714aac99749 upstream.
__sctp_outq_flush_rtx() moves a gap-acked chunk onto another transport's transmitted list without updating chunk->transport:
if (chunk->tsn_gap_acked) {
list_move_tail(&chunk->transmitted_list,
&transport->transmitted);
continue;
}The chunk then sits on a live transport's list while chunk->transport still names a different one. If that transport is removed - sctp_assoc_rm_peer() from an ASCONF Delete-IP - sctp_transport_free() RCU-frees it and the chunk is left with a dangling pointer. sctp_assoc_rm_peer() scrubs peer->transmitted and asoc->outqueue.out_chunk_list, but the chunk is on neither.
The pointer is not followed while tsn_gap_acked is set. A SACK that reneges on the TSN clears the flag, and the next SACK reaches
tchunk->transport->flight_size -= sctp_data_size(tchunk);
inside the freed transport. KASAN reports a slab-use-after-free read in sctp_check_transmitted(), freed from sctp_assoc_rm_peer(). Both the removal and the SACKs come from the association peer.
Set chunk->transport at the move. The ordinary resend path needs nothing:
it reaches its list_move_tail() only after sctp_packet_append_chunk() returned SCTP_XMIT_OK, and __sctp_packet_append_chunk() has rebound the chunk by then.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- net/sctp/outqueue.c | 1 + 1 file changed, 1 insertion(+)
net/sctp/outqueue.c +1 -0
@@ -650,6 +650,7 @@ static int __sctp_outq_flush_rtx(structif (chunk->tsn_gap_acked) {list_move_tail (&chunk->transmitted_list, &transport->transmitted); + chunk->transport = transport;continue ; }
Greg Kroah-Hartman
maintainerPATCH 268/271 sctp: fix use-after-free of cached ASCONF chunk
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuxiang Yang <[email protected]>
commit 8c283e7b56adce00193837f3311b06662466fb21 upstream.
addip_last_asconf caches the outstanding outbound ASCONF chunk. The normal ASCONF-ACK completion path releases the chunk and clears the pointer.
However, sctp_asconf_queue_teardown() releases the cached chunk without clearing addip_last_asconf. During peer restart handling, sctp_sf_do_dupcook_a() queues SCTP_CMD_PURGE_ASCONF_QUEUE, which invokes sctp_asconf_queue_teardown() while the association remains alive and leaves the pointer dangling.
A delayed authenticated ASCONF-ACK can then reach sctp_sf_do_asconf_ack(), which accesses the stale chunk and passes it to sctp_process_asconf_ack(), causing a use-after-free and a second release.
Clearing the pointer exposes a race with T4 expiry. Peer restart handling queues the timer stop before the purge, but SCTP_CMD_TIMER_STOP uses timer_delete(), which does not wait for a callback already running on another CPU. Such a callback can reach sctp_sf_t4_timer_expire() after the purge and dereference NULL.
Clear addip_last_asconf after releasing the cached chunk, and make sctp_sf_t4_timer_expire() consume a stale T4 expiry if no outstanding ASCONF remains.
--- net/sctp/associola.c | 4 +++- net/sctp/sm_statefuns.c | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-)
net/sctp/associola.c +3 -1
@@ -1713,6 +1713,8 @@ void sctp_asconf_queue_teardown(struct ssctp_assoc_free_asconf_queue (asoc);/* Free any cached ASCONF chunk. */ -if (asoc->addip_last_asconf) +if (asoc->addip_last_asconf) {sctp_chunk_free (asoc->addip_last_asconf); + asoc->addip_last_asconf =NULL ; + } }
net/sctp/sm_statefuns.c +5 -1
@@ -6145,8 +6145,12 @@ enum sctp_disposition sctp_sf_t4_timer_e struct sctp_cmd_seq *commands) {struct sctp_chunk *chunk = asoc->addip_last_asconf; -struct sctp_transport *transport = chunk->transport; +struct sctp_transport *transport; +if (!chunk) +return SCTP_DISPOSITION_CONSUME ; + + transport = chunk->transport;SCTP_INC_STATS (net,SCTP_MIB_T4_RTO_EXPIREDS );/* ADDIP 4.1 B1) Increment the error counters and perform path failure
Greg Kroah-Hartman
maintainerPATCH 269/271 sctp: clear new_transport when removing a peer
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qing Ming <[email protected]>
commit beb33f8ee1ca83acddb2a5ae80f3d22ec550b4c3 upstream.
sctp_process_asconf_param() stores a newly added peer transport in asoc->new_transport. After all parameters in the ASCONF chunk have been processed, sctp_sf_do_asconf() uses this pointer to send a HEARTBEAT to the new transport.
An authenticated ASCONF from a remote SCTP peer can add a transport and remove it again with a wildcard DEL-IP parameter in the same chunk. The wildcard deletion preserves the transport on which the ASCONF arrived, but removes the newly added transport through sctp_assoc_del_nonprimary_peers(). The removal does not clear asoc->new_transport, leaving it pointing to the removed transport.
sctp_sf_do_asconf() then creates a HEARTBEAT whose chunk->transport points to the removed transport without holding a transport reference. During local address replacement, src_out_of_asoc_ok keeps this HEARTBEAT on control_chunk_list. After the transport is freed by RCU, a successful ASCONF_ACK for the replacement address releases the queued HEARTBEAT and sctp_outq_select_transport() reads the freed transport's state.
The issue was found during a static audit of SCTP objects. With an authenticated peer, the reproducer triggered the same KASAN report in 2 of 2 unpatched runs on a KASAN-enabled netdev/main kernel:
BUG: KASAN: slab-use-after-free in sctp_outq_select_transport Read of size 4 at addr ffff88800b9bd95c by task python3/197 Call Trace: sctp_outq_select_transport+0x549/0x8b0 [sctp] sctp_outq_flush+0x306/0x2c60 [sctp] sctp_transport_immediate_rtx+0xaf/0x260 [sctp] sctp_process_asconf_ack+0xa48/0xf70 [sctp] Allocated by task 197: sctp_transport_new+0x68/0x650 [sctp] sctp_assoc_add_peer+0x258/0x12a0 [sctp] sctp_process_asconf+0x5e9/0x1090 [sctp] Last potentially related work creation: __call_rcu_common.constprop.0+0x77/0xb70 sctp_assoc_del_nonprimary_peers+0x7c/0xd0 [sctp] sctp_process_asconf+0xd9c/0x1090 [sctp]
The first invalid access was a four-byte read of transport->state at net/sctp/outqueue.c:833. The same reproducer completed the full authenticated ASCONF and local-address replacement sequence with this change without a KASAN report or oops.
Clear new_transport when its peer is removed, before it can be used to create the HEARTBEAT.
--- net/sctp/associola.c | 3 +++ 1 file changed, 3 insertions(+)
net/sctp/associola.c +3 -0
@@ -543,6 +543,9 @@ void sctp_assoc_rm_peer(struct sctp_asso asoc->addip_last_asconf->transport == peer) asoc->addip_last_asconf->transport = NULL; +if (asoc->new_transport == peer) + asoc->new_transport =NULL ; +/* If we have something on the transmitted list, we have to * save it off. The best place is the active path. */
Greg Kroah-Hartman
maintainerPATCH 270/271 thunderbolt: Bound the DROM dual link port number before indexing sw->ports
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit d6764992f17b23d91ff93ce905ab53c2aa7191f0 upstream.
tb_drom_parse_entry_port() validates the device-supplied header->index against sw->config.max_port_number before indexing sw->ports[], but the sibling field entry->dual_link_port_nr -- a 6-bit value also read from the DROM -- indexes the same array with no such check. A malicious or malformed Thunderbolt device can set dual_link_port_nr beyond the allocated sw->ports[] (max_port_number + 1 entries), producing an out-of-bounds tb_port pointer that is stored and later dereferenced.
Reject a port entry whose dual_link_port_nr exceeds max_port_number, the same bound already applied to header->index.
--- drivers/thunderbolt/eeprom.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/thunderbolt/eeprom.c +8 -1
@@ -394,9 +394,16 @@ static int tb_drom_parse_entry_port(strureturn -EIO ; } port->link_nr = entry->link_nr; -if (entry->has_dual_link_port) +if (entry->has_dual_link_port) { +if (entry->dual_link_port_nr > sw->config.max_port_number) { +tb_sw_warn (sw, +"port entry has invalid dual link port number %u\n" , + entry->dual_link_port_nr); +return -EIO ; + } port->dual_link_port = &port->sw->ports[entry->dual_link_port_nr]; + } }return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 271/271 thunderbolt: Fix bandwidth group reservation indexing
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
commit d2ee4d47aacbd2ba456092eeec670dba35fde291 upstream.
Valid bandwidth group IDs range from 1 through MAX_GROUPS, while Group ID 0 is reserved. tb_consumed_dp_bandwidth() uses the Group ID directly to index its local group_reserved[] array.
The array currently has MAX_GROUPS entries, so its valid indices are 0 through MAX_GROUPS - 1. Group ID MAX_GROUPS therefore accesses one element past the end, and the final group's reserved bandwidth is not included when the array is summed.
Give group_reserved[] MAX_GROUPS + 1 entries so direct Group ID indexing covers the reserved ID 0 and valid IDs 1 through MAX_GROUPS.
--- drivers/thunderbolt/tb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/thunderbolt/tb.c +1 -1
@@ -609,7 +609,7 @@ static int tb_consumed_dp_bandwidth(struint *consumed_up, int *consumed_down) { - int group_reserved[MAX_GROUPS] = {}; + int group_reserved[MAX_GROUPS + 1] = {};struct tb_cm *tcm =tb_priv (tb);struct tb_tunnel *tunnel;bool downstream;
Hi
kernel build / boot test on x86_64 (Intel).
No regressions here.
Thanks
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Wed, 19 Aug 2026 13:23:44 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.9-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.y
and the diffstat can be found below.thanks,
greg k-h
Tested rc1 against the Fedora build system (aarch64, ppc64le, s390x, x86_64), and boot tested x86_64. No regressions noted.
Hi!
Quoted 4 lines
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
CIP testing did not find any problems here:
https://gitlab.com/cip-project/cip-testing/linux-stable-rc-ci/-/tree/linux-7.1.y
Best regards,
Pavel
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Wed, 19 Aug 2026 13:23:44 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.9-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.y
and the diffstat can be found below.thanks,
greg k-h
On ARCH_BRCMSTB using 32-bit and 64-bit ARM kernels, build tested on BMIPS_GENERIC:
Signature · 1 line
Florian
Am 17.08.2026 um 15:28 schrieb Greg Kroah-Hartman:
Quoted 4 lines
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Builds, boots and works on my 2-socket Ivy Bridge Xeon E5-2697 v2 server. No dmesg oddities or regressions found.
Beste Grüße,
Peter Schneider
Signature · 8 lines
Climb the mountain not to plant your flag, but to embrace the challenge, enjoy the air and behold the view. Climb it so you can see the world, not so the world can see you. -- David McCullough Jr. OpenPGP: 0xA3828BD796CCE11A8CADE8866E3A92C92C3FF244 Download: https://www.peters-netzplatz.de/download/pschneider1968_pub.asc https://keys.mailvelope.com/pks/lookup?op=get&[email protected] https://keys.mailvelope.com/pks/lookup?op=get&[email protected]
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Wed, 19 Aug 2026 13:23:44 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.9-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.y
and the diffstat can be found below.thanks,
greg k-h
Tested on my amd64 DIY home NAS. Working well, no regressions observed.
Signature · 1 line
-Barry K. Nathan <[email protected]>
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Wed, 19 Aug 2026 13:23:44 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.9-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.y
and the diffstat can be found below.thanks,
greg k-h
Built and booted successfully on RISC-V RV64 (HiFive Unmatched).
Hi Greg
Quoted 10 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Wed, 19 Aug 2026 13:23:44 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 9 lines
https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.9-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.y
and the diffstat can be found below.thanks,
greg k-h
Linux version 7.1.9-rc1 tested.
Build successfully completed.
Boot successfully completed.
No dmesg regressions.
Video output normal.
Sound output normal.
Lenovo ThinkPad X1 Carbon Gen10(Intel i7-1260P(x86_64) arch linux)
[ 0.000000] Linux version 7.1.9-rc1rv-gac99063060a9
(takeshi@ThinkPadX1Gen10J0764) (gcc (GCC) 16.2.1 20260810, GNU ld (GNU Binutils) 2.47) #1 SMP PREEMPT_DYNAMIC Tue Aug 18 18:11:50 JST 2026
Thanks
Quoted 4 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
# Librecast Test Results
020/020 [ OK ] liblcrq
010/010 [ OK ] libmld
120/120 [ OK ] liblibrecast
CPU/kernel: Linux auntie 7.1.9-rc1-gac99063060a9 #1 SMP PREEMPT_DYNAMIC Tue Aug 18 13:58:23 -00 2026 x86_64 AMD Ryzen 9 9950X 16-Core Processor AuthenticAMD GNU/Linux
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Wed, 19 Aug 2026 13:23:44 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 9 lines
https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.1.9-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.y
and the diffstat can be found below.thanks,
greg k-h
Compiled and booted on my test system. No dmesg regressions.
thanks,
-- Shuah
Quoted 4 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Build and boot tested with QEMU for x86_64, i386, arm64, and riscv across multiple configurations, and boots on AMD Ryzen 7 5800H. No regressions observed.
Quoted 8 lines · On Mon, 17 Aug 2026 15:28:45 +0200 Greg Kroah-Hartman…
This is the start of the stable review cycle for the 7.1.9 release. There are 271 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Wed, 19 Aug 2026 13:23:44 +0000. Anything received after that time might be too late.
Boot-tested under QEMU for Rust x86_64, arm64 and riscv64; built-tested for loongarch64 and arm32:
Thanks!
Cheers,
Miguel
hello,
Compiled and booted 7.1.9-rc1 (using qemu-system-x86_64)
No new typical dmesg regressions .
--
software engineer
rajagiri school of engineering and technology