6.12.104-rc1 review
Greg Kroah-Hartman
maintainerPATCH 133/181 misc: fastrpc: fix memory leak in fastrpc_channel_ctx_free
In reply to Greg Kroah-Hartman6.12-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
@@ -469,6 +469,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 134/181 ring-buffer: Fix crash passing ERR_PTR to kthread_stop()
In reply to Greg Kroah-Hartman6.12-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
@@ -7604,7 +7604,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 135/181 ALSA: usb: Fix UAF at delayed release of MIDI2 EPs
In reply to Greg Kroah-Hartman6.12-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
@@ -685,6 +685,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;
@@ -735,6 +743,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 136/181 ALSA: usx2y: bound the hwdep mmap fault offset
In reply to Greg Kroah-Hartman6.12-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
@@ -681,6 +681,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 137/181 tracing: Fix race between update_event_fields and, event_define_fields
In reply to Greg Kroah-Hartman6.12-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
@@ -3072,6 +3072,7 @@ void trace_event_eval_update(struct tracint 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 */
@@ -3105,6 +3106,7 @@ void trace_event_eval_update(struct traccond_resched (); }up_write (&trace_event_sem); +mutex_unlock (&event_mutex); } static bool event_in_systems(struct trace_event_call *call,
Greg Kroah-Hartman
maintainerPATCH 138/181 fbdev: bitblit: bound-check glyph index in bit_cursor()
In reply to Greg Kroah-Hartman6.12-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
@@ -274,9 +274,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 != src || par->cursor_reset) {
Greg Kroah-Hartman
maintainerPATCH 139/181 ring-buffer: Prevent subbuf order change when resizing is disabled
In reply to Greg Kroah-Hartman6.12-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
@@ -6756,7 +6756,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 140/181 mm/huge_memory: fix huge_zero_pfn race
In reply to Greg Kroah-Hartman6.12-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
@@ -41,6 +41,7 @@#include <linux/compat.h> #include <linux/pgalloc_tag.h> #include <linux/pagewalk.h> +#include <linux/cleanup.h> #include <asm/tlb.h> #include <asm/pgalloc.h>
@@ -77,6 +78,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 ;
@@ -197,7 +199,8 @@ unsigned long __thp_vma_allowable_ordersstatic bool get_huge_zero_page (void ) {struct folio *zero_folio; -retry: + +/* Paired with atomic_set_release(). */ if (likely (atomic_inc_not_zero (&huge_zero_refcount)))return true;
@@ -209,17 +212,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; }
@@ -263,15 +271,22 @@ static unsigned long shrink_huge_zero_pastatic unsigned long shrink_huge_zero_page_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_page_shrinker;
Greg Kroah-Hartman
maintainerPATCH 141/181 net: smc: fix splice entry lifetime imbalance in smc_rx_splice
In reply to Greg Kroah-Hartman6.12-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 142/181 ipv6: prevent in6_dev_get() from resurrecting inet6_dev
In reply to Greg Kroah-Hartman6.12-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
@@ -382,8 +382,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 143/181 netfilter: bridge: release template ct on non-IP path
In reply to Greg Kroah-Hartman6.12-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
@@ -281,6 +281,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 144/181 netfilter: nf_conntrack: defer invalid log until after unlock
In reply to Greg Kroah-Hartman6.12-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
@@ -341,10 +341,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 ;
@@ -456,10 +458,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; }
@@ -534,6 +534,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 145/181 net: atlantic: free stranded TX buffers on ring deinit
In reply to Greg Kroah-Hartman6.12-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 146/181 net: atlantic: free RX pages of consumed but not refilled buffers
In reply to Greg Kroah-Hartman6.12-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 147/181 net/sched: act_ct: fix sk_buff leak when the header checks reject a packet
In reply to Greg Kroah-Hartman6.12-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
@@ -838,8 +838,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;
@@ -857,8 +864,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);
@@ -975,6 +986,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;
@@ -1010,9 +1022,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 148/181 net/sched: act_gact, act_police: range check the fallback control action
In reply to Greg Kroah-Hartman6.12-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 149/181 ovl: dont warn when the mount is completed from another user namespace
In reply to Greg Kroah-Hartman6.12-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
@@ -1299,7 +1299,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; sb->s_d_op = &ovl_dentry_operations;
Greg Kroah-Hartman
maintainerPATCH 150/181 binfmt_misc: dont warn when the mount is completed from another user namespace
In reply to Greg Kroah-Hartman6.12-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
@@ -983,7 +983,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 151/181 Revert "drm/amdgpu: fix aperture mapping leak"
In reply to Greg Kroah-Hartman6.12-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
@@ -4635,6 +4635,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
@@ -1909,23 +1909,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 /*
@@ -2072,6 +2066,8 @@ int amdgpu_ttm_init(struct amdgpu_device*/ void amdgpu_ttm_fini (struct amdgpu_device *adev) { +int idx; +if (!adev->mman.initialized)return ;
@@ -2094,7 +2090,14 @@ void amdgpu_ttm_fini(struct amdgpu_devicamdgpu_ttm_fw_reserve_vram_fini (adev);amdgpu_ttm_drv_reserve_vram_fini (adev); - 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); + }amdgpu_vram_mgr_fini (adev);amdgpu_gtt_mgr_fini (adev);
Greg Kroah-Hartman
maintainerPATCH 152/181 xdp: reject clones that overrun skb_shared_info tailroom
In reply to Greg Kroah-Hartman6.12-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
@@ -731,7 +731,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 153/181 vxlan: do not arm the ageing timer on a device that is down
In reply to Greg Kroah-Hartman6.12-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
@@ -4462,7 +4462,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 154/181 vsock/virtio: read virtqueues under worker locks
In reply to Greg Kroah-Hartman6.12-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
@@ -343,12 +343,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;
@@ -448,13 +449,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;
@@ -614,13 +615,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 155/181 vsock/virtio: avoid refilling the RX queue after teardown
In reply to Greg Kroah-Hartman6.12-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
@@ -618,7 +618,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 ];
@@ -668,6 +668,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 156/181 veth: fix skb length accounting after XDP frag adjustment
In reply to Greg Kroah-Hartman6.12-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 157/181 vhost: reset the vring metadata cache on vring reconfiguration
In reply to Greg Kroah-Hartman6.12-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
@@ -2125,6 +2125,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 158/181 tls: dont leave a full plaintext sk_msg ring unpushed
In reply to Greg Kroah-Hartman6.12-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 159/181 tipc: read le->link under the node lock in tipc_node_link_down()
In reply to Greg Kroah-Hartman6.12-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 160/181 smb: client: Fix use-after-free in cifs_try_adding_channels()
In reply to Greg Kroah-Hartman6.12-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
@@ -259,9 +259,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 161/181 KVM: x86/mmu: WARN and clear role.invalid when creating a child shadow page
In reply to Greg Kroah-Hartman6.12-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
@@ -2291,6 +2291,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 162/181 eventfs: Fix use-after-free in eventfs_remove_rec()
In reply to Greg Kroah-Hartman6.12-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
@@ -849,7 +849,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:
@@ -862,7 +862,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 163/181 eventfs: Use children field for rcu head and add memory barriers
In reply to Greg Kroah-Hartman6.12-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); } }
@@ -647,6 +657,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;
Re: [PATCH 6.12 163/181] eventfs: Use children field for rcu head and add memory barriers
HI Greg,
Quoted 65 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
commit
f0ece16ffca7384787b692431961ce202907acf5upstream.When an eventfs inode is freed, it sets
ei->is_freedand then uses itsei->listto add it to the srcu link list as the list field is a union with the rcu list head. As theei->listis used to iterate over an SRCU protected list without taking theeventfs_mutex, there's nothing stopping the iteration over that list to see theei->rcuinstead of theei->listand 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_freeand execute asmp_wmb()before adding the eventfs inode to the SRCU list.On iteration of the
ei->childrenlist, at the start, execute asmp_rmb()and then read theis_freedof the ei to see if the children list is still valid. Ifis_freedis set, then theei_childread is not valid and the loop should exit immediately.Fixes 704f960dbee2f ("eventfs: Read ei->entries before ei->children in eventfs_iterate()")Reported-by Sashiko <[email protected]>Reviewed-by Masami Hiramatsu (Google) <[email protected]>Signed-off-by Steven Rostedt <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- fs/tracefs/event_inode.c | 24 ++++++++++++++++++++++++ fs/tracefs/internal.h | 4 ++-- 2 files changed, 26 insertions(+), 2 deletions(-)fs/tracefs/event_inode.c +14
-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); } }@@ -647,6 +657,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
fs/tracefs/event_inode.c static int eventfs_iterate(struct file * · line 673
Quoted 10 lines
fs/tracefs/event_inode.c +10
-0+* 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 ; +
I have run an AI-assisted backport review and it spotted an issue. I checked the affected control flow.
Upstream has automatic SRCU cleanup:
guard(srcu)(&eventfs_srcu); ... if (ei->is_freed) return -EINVAL;
6.12.y still uses manual cleanup:
idx = srcu_read_lock(&eventfs_srcu); ... if (ei->is_freed) return -EINVAL; ... out: srcu_read_unlock(&eventfs_srcu, idx);
The new direct return bypasses srcu_read_unlock(). This leaves the SRCU reader counts unbalanced and can prevent eventfs callbacks from completing, allowing removed inode objects to accumulate.
I would suggest adapting the new check to set ret = -EINVAL and goto out.
thanks,
Harshit
Quoted 22 lines
fs/tracefs/event_inode.c +0
-0if (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;
Sasha Levin
maintainerRe: [PATCH 6.12 163/181] eventfs: Use children field for rcu head and add memory barriers
Quoted 3 lines · Harshit Mogalapalli
The new direct return bypasses
srcu_read_unlock(). This leaves the SRCU reader counts unbalanced and can prevent eventfs callbacks from completing, allowing removed inode objects to accumulate.
Confirmed, thanks. Upstream is fine because eventfs_iterate() there uses guard(srcu)(&eventfs_srcu) after 4d9b262031ff ("eventfs: Simplify code using guard()s"), which is only in 7.1. On 6.18, 6.12 and 6.6 the function still does the manual srcu_read_lock() with a single out: label, so the new "return -EINVAL" is the one post-lock path that skips srcu_read_unlock().
Quoted 2 lines
I would suggest adapting the new check to set ret =
-EINVALand goto out.
I'd rather not adapt the control flow by hand here, so I've dropped this from the affected trees: 6.12, plus 6.18 and 6.6, which have the same problem. 7.1 is correct as-is and keeps the patch.
If a properly adapted backport shows up for the older trees I'm happy to take it.
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 164/181 Revert "thermal/drivers/hwmon: Cleanup coding style a bit"
In reply to Greg Kroah-Hartman6.12-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
@@ -215,7 +215,8 @@ int thermal_add_hwmon_sysfs(struct thermif (new_hwmon_device)hwmon_device_unregister (hwmon->device); free_mem: -kfree (hwmon); +if (new_hwmon_device) +kfree (hwmon);return result; }
Greg Kroah-Hartman
maintainerPATCH 165/181 ptp: ocp: Fix board ID over-read
In reply to Greg Kroah-Hartman6.12-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
@@ -1914,9 +1914,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 166/181 ring-buffer: Use current_context for safe per-CPU buffer swap
In reply to Greg Kroah-Hartman6.12-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
@@ -6253,7 +6253,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))
@@ -6298,10 +6298,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;/*
Re: [PATCH 6.12 166/181] ring-buffer: Use current_context for safe per-CPU buffer swap
Hi Greg/Sasha
Quoted 64 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tengda Wu <[email protected]>
commit
f27bdc43077e4fcb5557dfc315ee8d91e741f483upstream.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 theRB_WARN_ONinrb_commit().Replace the committing counter check with
current_contextchecks, which are set at the entry ofring_buffer_lock_reserve()and remain valid throughout the entire write operation, providing a reliable indicator of buffer busy state during swap.Fixes 4239c38fe0b3 ("ring-buffer: Process commits whenever moving to a new page.")Signed-off-by Tengda Wu <[email protected]>Signed-off-by Steven Rostedt <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- kernel/trace/ring_buffer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)kernel/trace/ring_buffer.c +1
-1@@ -6253,7 +6253,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))@@ -6298,10 +6298,10 @@ int ring_buffer_swap_cpu(struct trace_buatomic_inc (&cpu_buffer_a->record_disabled);
kernel/trace/ring_buffer.c int ring_buffer_swap_cpu(struct trace_bu · line 6305
Quoted 10 lines
kernel/trace/ring_buffer.c +3
-3atomic_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;
I have run an AI-assisted backport review and it spotted an issue. The current_context checks themselves are correct, but the return-value change does not fit the older 6.12.y control flow.
Upstream has direct validation returns:
int ret = -EBUSY; ... if (cpu_buffer_a->nr_pages != cpu_buffer_b->nr_pages) return -EINVAL;
6.12.y instead has:
int ret = -EBUSY; ... if (cpu_buffer_a->nr_pages != cpu_buffer_b->nr_pages) goto out;
This changes invalid CPU-mask, page-count, and sub-buffer-order results
from -EINVAL to -EBUSY. I think update_max_tr_single() treats -EBUSY as an expected commit/resize collision.
Maybe we could be retaining ret = -EINVAL at declaration and assigning ret = -EBUSY immediately before the new current_context checks.
thanks,
Harshit
Quoted 5 lines
kernel/trace/ring_buffer.c +0
-0/*
Sasha Levin
maintainerRe: [PATCH 6.12 166/181] ring-buffer: Use current_context for safe per-CPU buffer swap
Quoted 3 lines · Harshit Mogalapalli
This changes invalid CPU-mask, page-count, and sub-buffer-order results from
-EINVALto-EBUSY. I thinkupdate_max_tr_single()treats-EBUSYas an expected commit/resize collision.
Correct, thanks. Upstream only has the direct returns after f115d2b70bff ("ring-buffer: Remove jump to out label in ring_buffer_swap_cpu()"), which first appeared in v6.16. There's a second bit of drift on top of what you spotted: the patch also removes the "ret = -EBUSY;" assignment, so on those trees the out_dec: paths (current_context busy, buffer resizing) end up returning the earlier -EAGAIN instead of -EBUSY.
Quoted 2 lines
Maybe we could be retaining ret =
-EINVALat declaration and assigning ret =-EBUSYimmediately before the newcurrent_contextchecks.
Rather than hand-adapting it, I've dropped this from the affected trees: 6.12, 6.6, 6.1, 5.15 and 5.10. 7.1 and 6.18 already carry f115d2b70bff, so the backport is identical to upstream there and stays queued.
The race fix itself is fine on the older trees, so I'll look at requeuing it with f115d2b70bff as a prerequisite.
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 167/181 ipv6: fix Route Information option length validation
In reply to Greg Kroah-Hartman6.12-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
@@ -988,13 +988,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 168/181 ip6_tunnel: clear skb2->cb[] in ip6ip6_err()
In reply to Greg Kroah-Hartman6.12-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
@@ -677,6 +677,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 169/181 fscrypt: use the mount idmap for the owner check in fscrypt_ioctl_set_policy()
In reply to Greg Kroah-Hartman6.12-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
@@ -532,7 +532,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 170/181 sched/psi: Shut down rtpoll_timer in psi_cgroup_free()
In reply to Greg Kroah-Hartman6.12-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
@@ -1130,6 +1130,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 171/181 ima: Instantiate file_truncate and path_truncate hooks
In reply to Greg Kroah-Hartman6.12-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
@@ -587,6 +587,43 @@ static int ima_file_check(struct file *f MAY_APPEND), FILE_CHECK); } +/* +* 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) {
@@ -1195,11 +1232,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 172/181 fsverity: Fix bpf_get_fsverity_digest() dynptr assumptions
In reply to Greg Kroah-Hartman6.12-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
@@ -121,11 +121,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 ;
@@ -149,11 +149,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 173/181 fsverity: Fix silent truncation in bpf_get_fsverity_digest()
In reply to Greg Kroah-Hartman6.12-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
@@ -143,14 +143,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 174/181 bpf, sockmap: Fix sk_redir use-after-free in send verdict
In reply to Greg Kroah-Hartman6.12-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
@@ -455,6 +455,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. */
@@ -475,6 +476,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 175/181 scsi: scsi_debug: Negate wrapped memcmp() result
In reply to Greg Kroah-Hartman6.12-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
@@ -3772,8 +3772,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 176/181 sctp: keep chunk->transport in step with the list it is queued on
In reply to Greg Kroah-Hartman6.12-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 177/181 sctp: fix use-after-free of cached ASCONF chunk
In reply to Greg Kroah-Hartman6.12-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
@@ -1731,6 +1731,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
@@ -6110,8 +6110,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 178/181 sctp: clear new_transport when removing a peer
In reply to Greg Kroah-Hartman6.12-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 179/181 thunderbolt: Bound the DROM dual link port number before indexing sw->ports
In reply to Greg Kroah-Hartman6.12-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
@@ -392,9 +392,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 180/181 thunderbolt: Fix bandwidth group reservation indexing
In reply to Greg Kroah-Hartman6.12-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
@@ -597,7 +597,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;
Greg Kroah-Hartman
maintainerPATCH 181/181 bpf: tcp: fix double sock release on batch realloc
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei (Microsoft) <[email protected]>
commit 980a813452754f8001704744e92f7aa697c53dd3 upstream.
bpf_iter_tcp_batch() releases the current batch via bpf_iter_tcp_put_batch(), which drops the socket refs and rewrites each slot with the socket cookie, then grows the batch. cur_sk/end_sk are kept for bpf_iter_tcp_resume(), but on realloc failure the function returns ERR_PTR() before resume runs, leaving cur_sk < end_sk over slots that now hold cookies rather than sock pointers. bpf_iter_tcp_seq_stop() then calls bpf_iter_tcp_put_batch() again and dereferences a cookie as a struct sock.
Empty the batch on the failure path so stop() does not release it again. The sockets were already freed by the first bpf_iter_tcp_put_batch(), so nothing leaks, and a later read() rescans the bucket from the start instead of skipping it. The sibling GFP_NOWAIT failure path still holds real socket references and is left for stop() to release.
BUG: KASAN: null-ptr-deref in __sock_gen_cookie Read of size 8 at addr 0000000000000059 by task exploit ... __sock_gen_cookie (net/core/sock_diag.c:28) bpf_iter_tcp_put_batch (net/ipv4/tcp_ipv4.c:2918) bpf_iter_tcp_seq_stop (net/ipv4/tcp_ipv4.c:3270) bpf_seq_read (kernel/bpf/bpf_iter.c:205) vfs_read (fs/read_write.c:572) ksys_read (fs/read_write.c:716) do_syscall_64 entry_SYSCALL_64_after_hwframe Kernel panic - not syncing: Fatal exception
--- net/ipv4/tcp_ipv4.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/ipv4/tcp_ipv4.c +4 -1
@@ -3268,8 +3268,11 @@ again:bpf_iter_tcp_put_batch (iter); err =bpf_iter_tcp_realloc_batch (iter, expected *3 /2 ,GFP_USER ); -if (err) +if (err) { + iter->cur_sk =0 ; + iter->end_sk =0 ;return ERR_PTR (err); + } sk =bpf_iter_tcp_resume (seq);if (!sk)
Hi!
Quoted 4 lines
This is the start of the stable review cycle for the 6.12.104 release. There are 181 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-6.12.y
Best regards,
Pavel
Am 17.08.2026 um 15:31 schrieb Greg Kroah-Hartman:
Quoted 4 lines
This is the start of the stable review cycle for the 6.12.104 release. There are 181 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 6.12.104 release. There are 181 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/v6.x/stable-review/patch-6.12.104-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.12.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
Greg Kroah-Hartman wrote on Mon, Aug 17, 2026 at 03:31:34PM +0200:
Quoted 5 lines
This is the start of the stable review cycle for the 6.12.104 release. There are 181 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.
Quoted 8 lines
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:
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.12.104-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.12.y
and the diffstat can be found below.
Tested 98c5a5b9f23e ("Linux 6.12.104-rc1") on:
- arm i.MX6ULL (Armadillo 640)
- arm64 i.MX8ULP (Armadillo IoT A9E)
No obvious regression in dmesg or basic tests:
Signature · 1 line
Dominique Martinet
Quoted 4 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.104 release. There are 181 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.
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.104 release. There are 181 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/v6.x/stable-review/patch-6.12.104-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.12.y
and the diffstat can be found below.thanks,
greg k-h
Built and booted successfully on RISC-V RV64 (HiFive Unmatched).
# Librecast Test Results
020/020 [ OK ] liblcrq
010/010 [ OK ] libmld
120/120 [ OK ] liblibrecast
CPU/kernel: Linux auntie 6.12.104-rc1-g98c5a5b9f23e #1 SMP PREEMPT_DYNAMIC Tue Aug 18 13:52:11 -00 2026 x86_64 AMD Ryzen 9 9950X 16-Core Processor AuthenticAMD GNU/Linux
Quoted 4 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.104 release. There are 181 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.
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.104 release. There are 181 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/v6.x/stable-review/patch-6.12.104-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.12.y
and the diffstat can be found below.thanks,
greg k-h
Compiled and booted on my test system. No dmesg regressions.
thanks,
-- Shuah
Hi Greg,
Quoted 8 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.104 release. There are 181 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.
No problems seen on x86_64 and aarch64 with our testing.
3 AI reports sent:
- https://lore.kernel.org/all/[email protected]/
- https://lore.kernel.org/all/[email protected]/
- https://lore.kernel.org/all/[email protected]/
Thanks,
Harshit
Quoted 10 lines
The whole patch series can be found in one patch at:
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/
patch-6.12.104-rc1.gz
or in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.12.y
and the diffstat can be found below.thanks,
greg k-h