6.12.103-rc1 review
Subsystems (2)
Greg Kroah-Hartman
maintainerPATCH 127/337 ksmbd: fix use-after-free in __close_file_table_ids()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ]
A ksmbd_file can remain alive after logical close while another session holds a temporary reference obtained through ksmbd_lookup_fd_inode(). ksmbd_close_fd() currently marks the file closed and drops the idr-owned reference, but leaves the pointer published in the closing session's idr until the final reference is dropped.
If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final() supplies the foreign session's file table to __ksmbd_close_fd(). The object is then freed without being removed from its owner's idr, and the owner session later dereferences the stale pointer during file-table teardown.
Remove the volatile id from the owner's idr while ksmbd_close_fd() still holds that table's lock, and clear volatile_id before dropping the idr-owned reference. A later foreign final put then only performs physical destruction and cannot remove the object from the wrong table.
--- fs/smb/server/vfs_cache.c | 2 ++ 1 file changed, 2 insertions(+)
fs/smb/server/vfs_cache.c +2 -0
@@ -502,6 +502,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) fp =NULL ; else { fp->f_state =FP_CLOSED ; +idr_remove (ft->idr, id); + fp->volatile_id =KSMBD_NO_FID ; closed = true;if (!atomic_dec_and_test (&fp->refcount)) fp =NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 128/337 rhashtable: clear stale iter->p on table restart
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang (Microsoft) <[email protected]>
[ Upstream commit 8173f7e2ce67e6ca1d4763f3da14e5b01ce77456 ]
rhashtable_walk_start_check() has two restart paths when resuming a walk. When iter->walker.tbl is valid, it re-validates iter->p against the table and sets iter->p = NULL if the object is gone. When iter->walker.tbl is NULL (table was freed during resize), it resets slot and skip but forgets to clear iter->p.
rhashtable_walk_next() then dereferences the stale iter->p, reading freed memory. This is a use-after-free.
Any caller that does multi-fragment rhashtable walks across walk_stop/walk_start boundaries is affected. Concrete cases include netlink_diag (__netlink_diag_dump in net/netlink/diag.c) and TIPC (tipc_nl_sk_walk in net/tipc/socket.c).
Crash stack (netlink_diag):
BUG: KASAN: slab-use-after-free in rhashtable_walk_next+0x365/0x3c0 Read of size 8 at addr ffff88801a9d2438 (freed kmalloc-2k, offset 1080) Call Trace: rhashtable_walk_next+0x365/0x3c0 (lib/rhashtable.c:1016) __netlink_diag_dump+0x160/0x760 (net/netlink/diag.c:122) netlink_diag_dump+0xc2/0x240 netlink_dump+0x5bc/0x1270 netlink_recvmsg+0x7a3/0x980 sock_recvmsg+0x1bc/0x200 __sys_recvfrom+0x1d4/0x2c0
--- lib/rhashtable.c | 1 + 1 file changed, 1 insertion(+)
lib/rhashtable.c +1 -0
@@ -742,6 +742,7 @@ int rhashtable_walk_start_check(struct rhashtable_iter *iter) iter->walker.tbl =rht_dereference_rcu (ht->tbl, ht); iter->slot =0 ; iter->skip =0 ; + iter->p =NULL ;return -EAGAIN ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 129/337 pinctrl: microchip-sgpio: add missing select REGMAP_MMIO
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Boortz <[email protected]>
commit 25cb6e9a13123d1039cdc75b446ac52e1ebdc26d upstream.
The driver calls ocelot_regmap_from_resource() via <linux/mfd/ocelot.h>, which internally uses devm_regmap_init_mmio() and requires REGMAP_MMIO. The Kconfig entry does not select REGMAP_MMIO, causing a build failure when no other driver in the config happens to pull in REGMAP_MMIO:
include/linux/mfd/ocelot.h:34:24: error: implicit declaration of function 'devm_regmap_init_mmio'
Found by randconfig testing on arm64; tinyconfig reproducer below.
--- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+)
drivers/pinctrl/Kconfig +1 -0
@@ -213,6 +213,7 @@ config PINCTRL_EQUILIBRIUM select GENERIC_PINCONF select GENERIC_PINCTRL_GROUPS select GENERIC_PINMUX_FUNCTIONS + select REGMAP_MMIO help Equilibrium driver is a pinctrl and GPIO driver for Intel Lightning Mountain network processor SoC that supports both the GPIO and pin
Re: [PATCH 6.12 129/337] pinctrl: microchip-sgpio: add missing select REGMAP_MMIO
Hi Greg,
Quoted 30 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Boortz <[email protected]>
commit
25cb6e9a13123d1039cdc75b446ac52e1ebdc26dupstream.The driver calls
ocelot_regmap_from_resource()via <linux/mfd/ocelot.h>, which internally usesdevm_regmap_init_mmio()and requiresREGMAP_MMIO. TheKconfigentry does not selectREGMAP_MMIO, causing a build failure when no other driver in the config happens to pull inREGMAP_MMIO:
include/linux/mfd/ocelot.h:34:24: error: implicit declaration of function 'devm_regmap_init_mmio'Found by randconfig testing on arm64; tinyconfig reproducer below.
Fixes 2afbbab45c26 ("pinctrl: microchip-sgpio: update to support regmap")Signed-off-by Benjamin Boortz <[email protected]>Reviewed-by Andy Shevchenko <[email protected]>Signed-off-by Linus Walleij <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+)
drivers/pinctrl/Kconfig config PINCTRL_EQUILIBRIUM · line 216
Quoted 4 lines
drivers/pinctrl/Kconfig +1
-0@@ -213,6 +213,7 @@ config PINCTRL_EQUILIBRIUM select GENERIC_PINCONF select GENERIC_PINCTRL_GROUPS select GENERIC_PINMUX_FUNCTIONS + select REGMAP_MMIO
I have run an AI assisted backport review and it spotted an issue; I checked the Kconfig delta and the selector was added to the wrong symbol.
Upstream has:
config PINCTRL_MICROCHIP_SGPIO
... select REGMAP_MMIO
The posted 6.12.y change instead leaves PINCTRL_MICROCHIP_SGPIO without that line and adds it under PINCTRL_EQUILIBRIUM.
I suggest moving select REGMAP_MMIO to the PINCTRL_MICROCHIP_SGPIO.
Thanks,
Harshit
Quoted 6 lines
drivers/pinctrl/Kconfig +0
-0help Equilibrium driver is a pinctrl and GPIO driver for Intel Lightning Mountain network processor SoC that supports both the GPIO and pin
Sasha Levin
maintainerRe: [PATCH 6.12 129/337] pinctrl: microchip-sgpio: add missing select REGMAP_MMIO
Quoted 4 lines · Harshit Mogalapalli
The posted 6.12.y change instead leaves
PINCTRL_MICROCHIP_SGPIOwithout that line and adds it underPINCTRL_EQUILIBRIUM.I suggest moving select
REGMAP_MMIOto thePINCTRL_MICROCHIP_SGPIO.
Confirmed, and thanks for catching it. The hunk fuzzed onto the wrong Kconfig symbol.
It is not only 6.12: 6.18.y, 6.6.y and 6.1.y have the same misapply. 7.1.y is correct.
I've regenerated the four queue files with the hunk anchored at the right place. Thanks!
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 130/337 pinctrl: devicetree: dont free uninitialized dev_name on error path
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
commit 015b5bcbcb622b32317642be91a7f79aa5413649 upstream.
dt_remember_or_free_map() duplicates dev_name for each map entry. If kstrdup_const() fails, dt_free_map() frees dev_name in all num_maps entries, including entries that have not been initialized.
Some pinctrl drivers, including pinctrl-imx, allocate the map with kmalloc() and leave dev_name for the core to initialize. The untouched entries therefore contain uninitialized data which is passed to kfree_const().
Reproduced on qemu's mcimx6ul-evk (pinctrl-imx) with failslab injection while binding the pinctrl-consuming device, under KASAN:
BUG: KASAN: double-free in dt_free_map+0x34/0xa4 Free of addr c425a900 by task init/1 kfree from dt_free_map+0x34/0xa4 dt_free_map from dt_remember_or_free_map+0x184/0x198 dt_remember_or_free_map from pinctrl_dt_to_map+0x33c/0x4c8 pinctrl_dt_to_map from create_pinctrl+0x9c/0x5c0
Initialize all dev_name fields to NULL before duplicating the device name, making the full-map cleanup safe after a partial failure.
--- drivers/pinctrl/devicetree.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/pinctrl/devicetree.c +4 -0
@@ -69,6 +69,10 @@ static int dt_remember_or_free_map(strucint i;struct pinctrl_dt_map *dt_map; +/* Initialize dev_name before any allocation can fail */ +for (i =0 ; i < num_maps; i++) + map[i].dev_name =NULL ; +/* Initialize common mapping table entry fields */ for (i =0 ; i < num_maps; i++) {const char *devname;
Greg Kroah-Hartman
maintainerPATCH 131/337 erofs: cap LZMA stream pool size
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit c9b47e6b23114e939b17f818471c7a46e59006e7 upstream.
fs/erofs/decompressor_lzma.c sizes the module-global MicroLZMA stream pool from num_possible_cpus() when the lzma_streams module parameter is unset, then z_erofs_load_lzma_config() preallocates one image-supplied dictionary per stream, accepting dictionaries up to 8 MiB. On high-CPU systems, a small EROFS image can pin hundreds of MiB of vmalloc-backed decoder state until the erofs module is unloaded.
Impact: An EROFS image mounted by the system can pin up to 8 MiB of vmalloc memory per LZMA stream, either as intended or unexpectedly.
Bound the default stream count by a new CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS option, default 16, so the worst-case default preallocation is 128 MiB if the number of CPUs is no less than 16 while preserving the existing per-image dictionary limit. An explicit lzma_streams module parameter is still honoured as-is, so administrators who deliberately size the pool are not affected.
--- fs/erofs/Kconfig | 14 ++++++++++++++ fs/erofs/decompressor_lzma.c | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-)
fs/erofs/Kconfig +14 -0
@@ -114,6 +114,20 @@ config EROFS_FS_ZIP_LZMA If unsure, say N. +config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS + int "EROFS LZMA default maximum decompression streams" + depends on EROFS_FS_ZIP_LZMA + range 1 NR_CPUS + default 16 + help + By default EROFS allocates one LZMA decompression stream per CPU. + Each stream can hold a dictionary of up to 8 MiB taken from the + mounted image, so on systems with many CPUs this can reserve a lot + of memory. This caps the default; the lzma_streams module parameter + still overrides it. + + If unsure, keep the default of 16. + config EROFS_FS_ZIP_DEFLATE bool "EROFS DEFLATE compressed data support" depends on EROFS_FS_ZIP
fs/erofs/decompressor_lzma.c +2 -1
@@ -51,7 +51,8 @@ static int __init z_erofs_lzma_init(void/* by default, use # of possible CPUs instead */ if (!z_erofs_lzma_nstrms) - z_erofs_lzma_nstrms =num_possible_cpus (); + z_erofs_lzma_nstrms = min_t(unsigned int, num_possible_cpus(), +CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS );for (i =0 ; i < z_erofs_lzma_nstrms; ++i) {struct z_erofs_lzma *strm =kzalloc (sizeof (*strm),GFP_KERNEL );
Greg Kroah-Hartman
maintainerPATCH 132/337 pinctrl: bm1880: add missing select GENERIC_PINCONF
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Boortz <[email protected]>
commit dad6e107b3cd9d20514e7799b7ad8674f81e3f30 upstream.
drivers/pinctrl/pinctrl-bm1880.c initialises its pinconf_ops with .is_generic = true, but that field is only present when CONFIG_GENERIC_PINCONF is enabled (guarded by #ifdef in pinconf.h). The Kconfig entry for PINCTRL_BM1880 never selects GENERIC_PINCONF, so any config that enables CONFIG_PINCTRL_BM1880=y without CONFIG_GENERIC_PINCONF=y fails to compile:
drivers/pinctrl/pinctrl-bm1880.c:1288:10: error: 'const struct pinconf_ops' has no member named 'is_generic'
Found by randconfig testing on arm64; tinyconfig reproducer below. Add the missing select to fix the build.
--- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+)
drivers/pinctrl/Kconfig +1 -0
@@ -150,6 +150,7 @@ config PINCTRL_BM1880 depends on OF && (ARCH_BITMAIN || COMPILE_TEST) default ARCH_BITMAIN select PINMUX + select GENERIC_PINCONF help Pinctrl driver for Bitmain BM1880 SoC.
Greg Kroah-Hartman
maintainerPATCH 133/337 fortify: Disable -Wstringop-overread in tests
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nathan Chancellor <[email protected]>
commit c1f3e770eec26d6f96dd6d2ea30555ba7c09a244 upstream.
clang recently added support for -Wstringop-overread [1], which is on by default like -Wfortify-source. This breaks the usage of -Werror in the fortify tests, resulting in the following false positive warnings in the kernel build:
warning: unsafe memcmp() usage lacked '__read_overflow2' warning in lib/test_fortify/read_overflow2-memcmp.c
warning: unsafe memcmp() usage lacked '__read_overflow' warning in lib/test_fortify/read_overflow-memcmp.c
warning: unsafe memchr() usage lacked '__read_overflow' warning in lib/test_fortify/read_overflow-memchr.c
Examining the fortify test logs shows a warning like the following in each of the failed logs:
In file included from lib/test_fortify/read_overflow2-memcmp.c:5:
lib/test_fortify/test_fortify.h:34:2: error: 'memcmp' reading 17 bytes from a region of size 16 [-Werror,-Wstringop-overread]
34 | TEST;
| ^
lib/test_fortify/read_overflow2-memcmp.c:3:2: note: expanded from macro 'TEST'
3 | memcmp(large, small, sizeof(small) + 1)
| ^1 error generated.
Disable -Wstringop-overread for the fortify tests, as it defeats the purpose of testing the Linux specific implementation of fortify, like -Wfortify-source.
--- lib/test_fortify/Makefile | 1 + 1 file changed, 1 insertion(+)
lib/test_fortify/Makefile +1 -0
@@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 ccflags-y := $(call cc-disable-warning,fortify-source) +ccflags-y += $(call cc-disable-warning,stringop-overread) quiet_cmd_test_fortify = TEST $@ cmd_test_fortify = $(CONFIG_SHELL) $(src)/test_fortify.sh \
Greg Kroah-Hartman
maintainerPATCH 134/337 mm: migrate_device: fix pte_pfn/pte_dirty called on non-present PTE
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kefeng Wang <[email protected]>
commit 63867c82d0c0c2d182016a32b1cc0103116b0ea5 upstream.
pte_pfn() and pte_dirty() have undefined behaviour when called on a non-present PTE. In migrate_vma_collect_pmd(), these functions may be invoked on non-present entries (e.g., device-private entries), leading to potential crashes from pte_pfn() or incorrect dirty folio accounting from pte_dirty(). Fix both by guarding with pte_present() checks.
--- mm/migrate_device.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
mm/migrate_device.c +3 -2
@@ -196,7 +196,8 @@ again:bool anon_exclusive;pte_t swp_pte; -flush_cache_page (vma, addr,pte_pfn (pte)); +if (pte_present (pte)) +flush_cache_page (vma, addr,pte_pfn (pte)); anon_exclusive =folio_test_anon (folio) &&PageAnonExclusive (page);if (anon_exclusive) {
@@ -216,7 +217,7 @@ again: migrate->cpages++;/* Set the dirty flag on the folio now the pte is gone. */ -if (pte_dirty (pte)) +if (pte_present (pte) &&pte_dirty (pte))folio_mark_dirty (folio);/* Setup special migration page table entry */
Greg Kroah-Hartman
maintainerPATCH 135/337 fs/proc/task_mmu: fix PAGEMAP_SCAN written state for PMD holes
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kiryl Shutsemau (Meta) <[email protected]>
commit 40de8160ca7f67d14619ee0351ce5d68fc4a237a upstream.
PAGEMAP_SCAN reports an unpopulated PTE in a uffd-wp VMA as written, but a range with no page table at all -- a PMD hole -- is skipped:pagemap_scan_pte_hole() tests p->cur_vma_category, which never carries PAGE_IS_WRITTEN, so the hole is neither reported nor (under PM_SCAN_WP_MATCHING) armed.
In a uffd-wp VMA, WP_UNPOPULATED installs uffd-wp markers when protecting a range, allocating page tables as needed, so an unpopulated slot is treated as written -- see the pte_none() handling in pagemap_page_category(). A missing marker therefore means the range was zapped, e.g. via MADV_DONTNEED. This applies to anon and shmem VMAs.
An anonymous THP is write-protected in place as a huge PMD, so a full-PMD MADV_DONTNEED clears it to pmd_none -- a hole with no page table -- and pagemap_scan_pte_hole() misses it. For a MAP_PRIVATE|MAP_ANON mapping MADV_DONTNEED has fill-with-zeros semantics, so a write-tracking checkpoint/migration tool (e.g. CRIU) treats the range as unchanged and keeps its previous contents; after restore or live migration the process reads stale data instead of zeroes -- data corruption.
Report a hole in a non-hugetlb uffd-wp VMA as written, matching the pte_none handling in pagemap_page_category(); the existing PM_SCAN_WP_MATCHING path then arms it via uffd_wp_range().
hugetlb is excluded: pagemap_hugetlb_category() reports an empty hugetlb entry (huge_pte_none) as not-written, unlike pagemap_page_category(), which reports pte_none as written. pagemap_scan_pte_hole() fires for a hugetlb slot only when it has no page table; keeping that not-written matches how an allocated-but-empty hugetlb entry reads, so the hole and the empty-entry cases agree within the VMA.
--- fs/proc/task_mmu.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
fs/proc/task_mmu.c +18 -2
@@ -2690,12 +2690,28 @@ static int pagemap_scan_pte_hole(unsigne {struct pagemap_scan_private *p = walk->private;struct vm_area_struct *vma = walk->vma; +unsigned long categories;int ret, err; -if (!vma || !pagemap_scan_is_interesting_page (p->cur_vma_category, p)) +if (!vma)return 0 ; - ret =pagemap_scan_output (p->cur_vma_category, p, addr, &end); +/* +* In a uffd-wp VMA an unpopulated range is treated as written: +* uffd-wp registration populates page tables and installs markers +* with WP_UNPOPULATED, so a missing marker means the range was +* zapped. See the pte_none() handling in pagemap_page_category(). +* +* hugetlb differs, see pagemap_hugetlb_category(). +*/ + categories = p->cur_vma_category; +if (userfaultfd_wp (vma) && !is_vm_hugetlb_page (vma)) + categories |=PAGE_IS_WRITTEN ; + +if (!pagemap_scan_is_interesting_page (categories, p)) +return 0 ; + + ret =pagemap_scan_output (categories, p, addr, &end);if (addr == end)return ret;
Greg Kroah-Hartman
maintainerPATCH 136/337 mm/percpu-km: fix bitmap overflow and accounting in pcpu_create_chunk()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zi Yan <[email protected]>
commit 89b1b79c308818a715e75f28744b70d8940a07c9 upstream.
In pcpu_create_chunk(), nr_pages is the total contiguous backing allocation, i.e., nr_units * pcpu_unit_pages, but pcpu_chunk_populated() uses it to set chunk->populated, whose size is pcpu_unit_pages, bitmap. Since bit N in chunk->populated means page offset N inside every unit is backed. When nr_units > 1, the function writes beyond chunk->populated. Fix it by using chunk->nr_pages.
It also fixes the global pcpu_nr_empty_pop_pages accounting, since pcpu_balance_free() only iterates up to chunk->nr_pages.
Commit a63d4ac4ab609 ("percpu: make percpu-km set chunk->populated bitmap properly") introduced the bitmap overflow issue. Later, commit b539b87fed37f ("percpu: implmeent pcpu_nr_empty_pop_pages and chunk->nr_populated") added pcpu_nr_empty_pop_pages and caused the accounting issue.
--- mm/percpu-km.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/percpu-km.c +1 -1
@@ -75,7 +75,7 @@ static struct pcpu_chunk *pcpu_create_ch chunk->base_addr =page_address (pages);spin_lock_irqsave (&pcpu_lock, flags); -pcpu_chunk_populated (chunk,0 , nr_pages); +pcpu_chunk_populated (chunk,0 , chunk->nr_pages);spin_unlock_irqrestore (&pcpu_lock, flags);pcpu_stats_chunk_alloc ();
Greg Kroah-Hartman
maintainerPATCH 137/337 mm/hugetlb: fix list corruption in allocate_file_region_entries()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiangfeng Cai <[email protected]>
commit dd9623f58ec702a07b2d67179d6fcea79c52231a upstream.
allocate_file_region_entries() tops up resv->region_cache with freshly allocated file_region descriptors. The allocation uses GFP_KERNEL, so resv->lock is dropped around it: the new entries are gathered on a stack-local list head, allocated_regions, and spliced into resv->region_cache once the lock is re-acquired.
The splice used list_splice(), which moves the entries but does not re-initialize the source head, so allocated_regions is left pointing at an entry that now lives on resv->region_cache. The top-up runs in a while loop that re-checks the cache deficit after re-acquiring the lock. For a shared mapping the resv_map is shared by every mapper of the hugetlbfs inode, so a concurrent region_chg()/region_add()/region_del() on the same resv_map can consume cache entries during the unlocked window and force a second iteration. That iteration calls list_add() on the stale head and corrupts the list; with CONFIG_DEBUG_LIST the __list_add_valid() check trips:
list_add corruption. next->prev should be prev (ffffc900011ff7f8), but was ffff88814c281460. (next=ffff88814c545640). kernel BUG at lib/list_debug.c:31!
allocate_file_region_entries+0x191/0x420 region_chg+0x267/0x300 hugetlb_reserve_pages+0x387/0xc80 hugetlbfs_file_mmap+0x2ce/0x3f0 mmap_region+0x1348/0x1a80 do_mmap+0x85e/0xb90 vm_mmap_pgoff+0x18c/0x330 ksys_mmap_pgoff+0x2a1/0x3e0 do_syscall_64+0xd7/0x420
Without CONFIG_DEBUG_LIST the bad list_add() silently links a kernel-stack address into resv->region_cache, leading to later use-after-free.
This was observed as a real host panic on a dense KVM host where a QEMU guest-RAM hugetlbfs file was mapped MAP_SHARED by both QEMU and a separate SPDK/DPDK vhost-user target, generating concurrent region_* traffic on one shared resv_map.
Use list_splice_init() so the source head is re-initialized empty after each splice, making the retry loop safe.
--- mm/hugetlb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/hugetlb.c +1 -1
@@ -676,7 +676,7 @@ static int allocate_file_region_entries(spin_lock (&resv->lock); -list_splice (&allocated_regions, &resv->region_cache); +list_splice_init (&allocated_regions, &resv->region_cache); resv->region_cache_count += to_allocate; }
Greg Kroah-Hartman
maintainerPATCH 138/337 mm/vmstat: fold stranded per-cpu node stats when a node comes online
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gregory Price <[email protected]>
commit ea3034b2b00fa50c8d2518d0804c9d427bbafa86 upstream.
A per-node vmstat counter is pgdat->vm_stat[] plus per-cpu deltas. A balanced counter can sit split as global=+N / per-cpu=-N.
The folds reconciling the split only walk online nodes, so when try_offline_node() marks a node offline the per-cpu deltas are stranded.
A subsequent online resets the per-cpu area but not pgdat->vm_stat[], orphaning the +N permanently. All NR_VM_NODE_STAT_ITEMS are affected.
The existing code zeroes the per-cpu counters and causes a permanent skew. Fold the stranded deltas instead, before the node rejoins the online set. The node is not online yet and the hotplug lock is held, so the remote access to per-cpu values is safe.
Discovered when node compaction hung for a nearly empty node, as the math to determine throttling broke. Reproduced by repeated memory hotplug/unplug cycles on a node under pressure: NR_ISOLATED_ANON ratchets up and never returns to zero.
--- mm/mm_init.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
mm/mm_init.c +11 -4
@@ -1524,7 +1524,7 @@ void __ref free_area_init_core_hotplug(s {int nid = pgdat->node_id;enum zone_type z; -int cpu; +int cpu, i;pgdat_init_internals (pgdat);
@@ -1542,10 +1542,17 @@ void __ref free_area_init_core_hotplug(s pgdat->node_start_pfn =0 ; pgdat->node_present_pages =0 ; -for_each_online_cpu (cpu) { -struct per_cpu_nodestat *p; +/* +* Hot-unplug can leave per-cpu vmstat deltas unfolded (folders skip +* offline nodes) - reconcile this at online. Foreign access to counters +* is safe: the node is not online yet and we hold the hotplug lock. +*/ +for_each_possible_cpu (cpu) { +struct per_cpu_nodestat *p =per_cpu_ptr (pgdat->per_cpu_nodestats, cpu); - p =per_cpu_ptr (pgdat->per_cpu_nodestats, cpu); +for (i =0 ; i <NR_VM_NODE_STAT_ITEMS ; i++) +if (p->vm_node_stat_diff[i]) +node_page_state_add (p->vm_node_stat_diff[i], pgdat, i);memset (p,0 ,sizeof (*p)); }
Greg Kroah-Hartman
maintainerPATCH 139/337 tracing/probes: Reject $arg0 in meta argument expansion
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raushan Patel <[email protected]>
commit 00a8ce2a2a9fa17674e1feec4d9105c1a5d6a419 upstream.
traceprobe_expand_meta_args() parses $argN with simple_strtoul() and calls sprint_nth_btf_arg(n - 1, ...). For $arg0, n is 0 so the index is -1. Because ctx->nr_params is signed, the "idx >= nr_params" guard in sprint_nth_btf_arg() does not catch the negative index, and ctx->params[-1].name_off is read out of bounds.
The normal per-argument path (parse_probe_vars()) already rejects $arg0 via its argument-number check, but meta-argument expansion runs before per-argument parsing and substitutes the value first, bypassing that check.
Reject $arg0 explicitly during expansion.
--- kernel/trace/trace_probe.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
kernel/trace/trace_probe.c +5 -1
@@ -1766,7 +1766,11 @@ const char **traceprobe_expand_meta_args ret = -ENOENT ; goto error; } -/* Note: $argN starts from $arg1 */ +/* Note: $argN starts from $arg1, so $arg0 is invalid. */ +if (n ==0 ) { +trace_probe_log_err (0 ,BAD_ARG_NUM ); +return ERR_PTR (-EINVAL ); + } ret =sprint_nth_btf_arg (n -1 , type, buf + used, bufsize - used, ctx);if (ret <0 )
Greg Kroah-Hartman
maintainerPATCH 140/337 KVM: SVM: Update x2APIC MSR intercepts if AVIC is inhibited while L2 is active
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 7d3aae206663c4e006b25a1c7a20a4029e67da76 upstream.
Always update x2APIC MSR intercepts for L1 when AVIC is deactivated, even if L2 is active and KVM is using a separate MSR bitmap to run L2. If AVIC is fully enabled prior to running L2, and is then inhibited while L2 is active (for a VM-scoped inhibit), then KVM will run L1 with AVIC disabled, but with x2APIC MSR intercepts disabled, i.e. will allow L1 to read most of the host's APIC state, send arbitrary interrupts, change task priority, and ultimately trivially DoS the host.
E.g. sending a self-IPI in L1 on HYPERV_REENLIGHTENMENT_VECTOR, 0xee, with CONFIG_HYPERV=n in the host kernel as a "safe" PoC, yields:
Spurious interrupt (vector 0xee) on CPU#425. Acked
And hacking KVM to abuse kvm_set_posted_intr_wakeup_handler() to register a handler and WARN on POSTED_INTR_WAKEUP_VECTOR yields:
------------[ cut here ]------------ WARNING: arch/x86/kvm/svm/svm.c:5594 at pi_wakeup_handler+0x9/0x10 [kvm_amd], CPU#156: nested_x2apic_t/316940 CPU: 156 UID: 0 PID: 316940 Comm: nested_x2apic_t Tainted: G S U Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER Hardware name: Google Astoria-Turin/astoria, BIOS 0.20260209.0-0 02/09/2026 RIP: 0010:pi_wakeup_handler+0x9/0x10 [kvm_amd] Call Trace: <IRQ> sysvec_kvm_posted_intr_wakeup_ipi+0x64/0x80 </IRQ> <TASK> asm_sysvec_kvm_posted_intr_wakeup_ipi+0x1a/0x20 RIP: 0010:vcpu_run+0x1430/0x1e40 [kvm] kvm_arch_vcpu_ioctl_run+0x2c1/0x600 [kvm] kvm_vcpu_ioctl+0x580/0x6b0 [kvm] __se_sys_ioctl+0x6d/0xb0 do_syscall_64+0x10a/0x480 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x46ff4b </TASK> ---[ end trace 0000000000000000 ]---
--- arch/x86/kvm/svm/avic.c | 8 -------- 1 file changed, 8 deletions(-)
arch/x86/kvm/svm/avic.c +0 -8
@@ -167,14 +167,6 @@ static void avic_deactivate_vmcb(structif (!sev_es_guest (svm->vcpu.kvm))svm_set_intercept (svm,INTERCEPT_CR8_WRITE ); -/* -* If running nested and the guest uses its own MSR bitmap, there -* is no need to update L0's msr bitmap -*/ -if (is_guest_mode (&svm->vcpu) && -vmcb12_is_intercept (&svm->nested.ctl,INTERCEPT_MSR_PROT )) -return ; -/* Enabling MSR intercept for x2APIC registers */ svm_set_x2apic_msr_interception (svm, true); }
Greg Kroah-Hartman
maintainerPATCH 141/337 KVM: s390: pci: Reject adapter interrupt forwarding if already enabled
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Farhan Ali <[email protected]>
commit 8fa01be5a6149404adb82c0979a78f6347edd3ef upstream.
The MPCIFC instruction doesn't allow registering adapter interrupts without first unregistering. So reject any request to enable interrupt forwarding if its already enabled for the zPCI device. This also fixes overwriting and thus leaking resources when the ioctl is called multiple times for the same device.
--- arch/s390/kvm/pci.c | 4 ++++ 1 file changed, 4 insertions(+)
arch/s390/kvm/pci.c +4 -0
@@ -239,6 +239,10 @@ static int kvm_s390_pci_aif_enable(strucif (zdev->gisa ==0 )return -EINVAL ; +/* AIF already enabled for the device */ +if (zdev->kzdev->fib.fmt0.aibv !=0 ) +return -EINVAL ; + kvm = zdev->kzdev->kvm; msi_vecs = min_t(unsigned int, fib->fmt0.noi, zdev->max_msi);
Greg Kroah-Hartman
maintainerPATCH 142/337 KVM: s390: pci: Fix NULL dereference on AIBV allocation failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Farhan Ali <[email protected]>
commit 8bf09b9b7d3232806df95f409581f8a9fd99a3fa upstream.
The airq_iv_create() can return NULL on failure, but the return value was never checked. If it fails, zdev->aibv will be NULL and fail when dereferenced in kvm_zpci_set_airq(). Add a NULL check and free the previously allocated AISB bit and zdev->aisb on failure.
--- arch/s390/kvm/pci.c | 8 ++++++++ 1 file changed, 8 insertions(+)
arch/s390/kvm/pci.c +8 -0
@@ -295,6 +295,11 @@ static int kvm_s390_pci_aif_enable(strucAIRQ_IV_GUESTVEC , phys_to_virt(fib->fmt0.aibv)); +if (!zdev->aibv) { + rc = -ENOMEM ; + goto free_aisb; + } +spin_lock_irq (&aift->gait_lock); gaite = aift->gait + zdev->aisb;
@@ -331,6 +336,9 @@ static int kvm_s390_pci_aif_enable(struc rc =kvm_zpci_set_airq (zdev);return rc; +free_aisb: +airq_iv_free_bit (aift->sbv, zdev->aisb); + zdev->aisb =0 ; unlock:mutex_unlock (&aift->aift_lock); unpin2:
Greg Kroah-Hartman
maintainerPATCH 143/337 KVM: s390: pci: Validate AIBV and AISB before pinning guest pages
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Farhan Ali <[email protected]>
commit 868d32ac72cba21c5c6d8a66a814b7c25a3a5c01 upstream.
The AIBV holds one bit per MSI-X vector for a given function. The size of the bit vector is derived from the NOI and the AIBVO. If the size of the AIBV exceeds a single page boundary, then reject the request as we cannot safely pin the guest AIBV.
Similarly reject the request if the AISB address is not 8-byte aligned as the architecture requires doubleword alignment for the summary bit address. Since the AISBO can address up to 64 bits, the size of the AISB can only be 8 bytes for the function. This also ensures the AISB doesn't exceed a single page boundary.
--- arch/s390/kvm/pci.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
arch/s390/kvm/pci.c +15 -1
@@ -225,7 +225,7 @@ static int kvm_s390_pci_aif_enable(struc bool assist) {struct page *pages[1 ], *aibv_page, *aisb_page =NULL ; -unsigned int msi_vecs, idx; +unsigned int msi_vecs, idx, size;struct zpci_gaite *gaite;unsigned long hva, bit;struct kvm *kvm;
@@ -252,6 +252,14 @@ static int kvm_s390_pci_aif_enable(strucreturn gisc;/* Replace AIBV address */ + size =BITS_TO_LONGS (msi_vecs + fib->fmt0.aibvo) *sizeof (unsigned long ); + npages =DIV_ROUND_UP ((fib->fmt0.aibv & ~PAGE_MASK ) + size,PAGE_SIZE ); +/* AIBV cannot span more than 1 page */ +if (npages >1 ) { + rc = -EINVAL ; + goto out; + } + idx =srcu_read_lock (&kvm->srcu); hva =gfn_to_hva (kvm,gpa_to_gfn ((gpa_t )fib->fmt0.aibv)); npages =pin_user_pages_fast (hva,1 ,FOLL_WRITE |FOLL_LONGTERM , pages);
@@ -267,6 +275,12 @@ static int kvm_s390_pci_aif_enable(struc/* Pin the guest AISB if one was specified */ if (fib->fmt0.sum ==1 ) { +/* AISB must be dword aligned */ +if (fib->fmt0.aisb &0x7 ) { + rc = -EINVAL ; + goto unpin1; + } + idx =srcu_read_lock (&kvm->srcu); hva =gfn_to_hva (kvm,gpa_to_gfn ((gpa_t )fib->fmt0.aisb)); npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM,
Greg Kroah-Hartman
maintainerPATCH 144/337 sctp: validate Adaptation Indication parameter length
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Charles Vosburgh <[email protected]>
commit 74b21f52c5c5a71a05c0ff70e513f4f04ff28b17 upstream.
The Adaptation Layer Indication parameter contains a fixed 32-bit Adaptation Code Point after its parameter header. However, sctp_verify_param() accepts a header-only parameter because the generic parameter walker only requires the header to be present.
sctp_process_param() then reads adaptation_ind beyond the declared parameter. When the malformed parameter is last in an INIT, the read starts at the receive skb tail, and the value is copied into the state cookie returned in the INIT ACK. This may disclose four receive-buffer tail bytes.
Require the declared parameter length to match the fixed structure size and abort the association through the existing invalid parameter length path otherwise.
--- net/sctp/sm_make_chunk.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/sctp/sm_make_chunk.c +6 -0
@@ -2187,7 +2187,13 @@ static enum sctp_ierror sctp_verify_paracase SCTP_PARAM_HEARTBEAT_INFO :case SCTP_PARAM_UNRECOGNIZED_PARAMETERS :case SCTP_PARAM_ECN_CAPABLE : +break ;case SCTP_PARAM_ADAPTATION_LAYER_IND : +if (ntohs (param.p->length) !=sizeof (*param.aind)) { +sctp_process_inv_paramlength (asoc, param.p, + chunk, err_chunk); + retval =SCTP_IERROR_ABORT ; + }break ;case SCTP_PARAM_SUPPORTED_EXT :
Greg Kroah-Hartman
maintainerPATCH 145/337 audit: fix potential integer overflow in audit_log_n_string()
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 f865c143629d4094866a811dba5f329250bad486 upstream.
audit_log_n_string() computes new_len as "slen + 3" (enclosing quotes plus the NUL terminator) and stores it into an int, while slen is a size_t. For a sufficiently large slen the addition can overflow and/or the result be truncated when assigned to the int new_len, so the "new_len > avail" check can be bypassed and the subsequent memcpy(ptr, string, slen) can write past the skb tail.
This is the same class of bug that was fixed for the hex sibling in commit 65dfde57d1e2 ("audit: fix potential integer overflow in audit_log_n_hex()"); both helpers are reached through audit_log_n_untrustedstring() with the same length source.
Make new_len a size_t and use check_add_overflow() to catch the overflow, mirroring the audit_log_n_hex() fix. No functional change for the in-tree callers, which all pass bounded lengths.
--- kernel/audit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
kernel/audit.c +9 -2
@@ -2070,7 +2070,8 @@ void audit_log_n_hex(struct audit_buffervoid audit_log_n_string (struct audit_buffer *ab,const char *string,size_t slen) { -int avail, new_len; +int avail; +size_t new_len;unsigned char *ptr;struct sk_buff *skb;
@@ -2080,7 +2081,13 @@ void audit_log_n_string(struct audit_bufBUG_ON (!ab->skb); skb = ab->skb; avail =skb_tailroom (skb); - new_len = slen +3 ;/* enclosing quotes + null terminator */ + +/* enclosing quotes + null terminator */ +if (check_add_overflow (slen,3 , &new_len)) { +audit_log_format (ab,"?" ); +return ; + } +if (new_len > avail) { avail =audit_expand (ab, new_len);if (!avail)
Greg Kroah-Hartman
maintainerPATCH 146/337 audit: fix potential use-after-free in audit_del_rule()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luxiao Xu <[email protected]>
commit 246df90b5f1a8a6e6abbd2f058b029558720adec upstream.
audit_del_rule() destroys e->rule.exe via audit_remove_mark_rule() before unlinking the rule from RCU-visible filter lists and waiting for a grace period. Concurrent readers in audit_filter() and audit_filter_rules() still dereference e->rule.exe, while the fsnotify mark can be freed on an independent lifetime path. This creates a use-after-free window during rule deletion.
Fix this by unlinking the rule from the RCU-visible lists and invoking synchronize_rcu() before calling audit_remove_mark_rule() (and other rule removal helpers). This ensures that all existing RCU readers have exited the critical section before any underlying resources are destroyed.
--- kernel/auditfilter.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
kernel/auditfilter.c +4 -2
@@ -1047,6 +1047,10 @@ int audit_del_rule(struct audit_entry *e goto out; } +list_del_rcu (&e->list); +list_del (&e->rule.list); +synchronize_rcu (); +if (e->rule.watch)audit_remove_watch_rule (&e->rule);
@@ -1064,8 +1068,6 @@ int audit_del_rule(struct audit_entry *e audit_signals--;#endif -list_del_rcu (&e->list); -list_del (&e->rule.list);call_rcu (&e->rcu, audit_free_rule_rcu); out:
Greg Kroah-Hartman
maintainerPATCH 147/337 Bluetooth: btusb: Fix short read errors in btusb_qca_send_vendor_req()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Greg Kroah-Hartman <[email protected]>
commit cac43d360c928bc0cbbd18809632388265649761 upstream.
If btusb_qca_send_vendor_req() gets a "short" read from a device, it will accidentally treat that as a "real" read and populate the returned value with some unknown and probably totally invalid data.
Fix this logic error up by calling usb_control_msg_recv() which guarantees a "full" read happens, and then simplify the error checking for when btusb_qca_send_vendor_req() is called.
--- drivers/bluetooth/btusb.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-)
drivers/bluetooth/btusb.c +9 -21
@@ -3304,28 +3304,16 @@ static u16 qca_extract_board_id(const ststatic int btusb_qca_send_vendor_req (struct usb_device *udev,u8 request,void *data,u16 size) { -int pipe, err; -u8 *buf; - - buf =kmalloc (size,GFP_KERNEL ); -if (!buf) -return -ENOMEM ; +int err;/* Found some of USB hosts have IOT issues with ours so that we should * not wait until HCI layer is ready. */ - pipe =usb_rcvctrlpipe (udev,0 ); - err =usb_control_msg (udev, pipe, request,USB_TYPE_VENDOR |USB_DIR_IN , -0 ,0 , buf, size,USB_CTRL_GET_TIMEOUT ); -if (err <0 ) { + err =usb_control_msg_recv (udev,0 , request,USB_TYPE_VENDOR |USB_DIR_IN , +0 ,0 , data, size,USB_CTRL_GET_TIMEOUT , +GFP_KERNEL ); +if (err)dev_err (&udev->dev,"Failed to access otp area (%d)" , err); - goto done; - } - -memcpy (data, buf, size); - -done: -kfree (buf);return err; }
@@ -3519,7 +3507,7 @@ static bool btusb_qca_need_patch(structstruct qca_version ver;if (btusb_qca_send_vendor_req (udev,QCA_GET_TARGET_VERSION , &ver, -sizeof (ver)) <0 ) +sizeof (ver)))return false;/* only low ROM versions need patches */ return !(le32_to_cpu (ver.rom_version) & ~0xffffU );
@@ -3537,7 +3525,7 @@ static int btusb_setup_qca(struct hci_de err =btusb_qca_send_vendor_req (udev,QCA_GET_TARGET_VERSION , &ver,sizeof (ver)); -if (err <0 ) +if (err)return err; ver_rom =le32_to_cpu (ver.rom_version);
@@ -3560,7 +3548,7 @@ static int btusb_setup_qca(struct hci_de err =btusb_qca_send_vendor_req (udev,QCA_CHECK_STATUS , &status,sizeof (status)); -if (err <0 ) +if (err)return err;if (!(status &QCA_PATCH_UPDATED )) {
@@ -3571,7 +3559,7 @@ static int btusb_setup_qca(struct hci_de err =btusb_qca_send_vendor_req (udev,QCA_GET_TARGET_VERSION , &ver,sizeof (ver)); -if (err <0 ) +if (err)return err; btdata->qca_dump.fw_version =le32_to_cpu (ver.patch_version);
Greg Kroah-Hartman
maintainerPATCH 148/337 Bluetooth: btmtk: Fix short read errors in btmtk_usb_uhw_reg_read()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Greg Kroah-Hartman <[email protected]>
commit b186c18c4843dd58adc29443369bddc71cb626a3 upstream.
If btmtk_usb_uhw_reg_read() gets a "short" read from a device, it will accidentally treat that as a "real" read and populate the returned value with some unknown and probably totally invalid data.
Fix this logic error up by calling usb_control_msg_recv() which guarantees a "full" read happens, and then simplify the error checking for when btmtk_usb_uhw_reg_read() is called.
Note, one caller of btmtk_usb_uhw_reg_read() does not check the return value, but as we pre-initialize the return value as 0, an incorrect read will not do anything wrong.
--- drivers/bluetooth/btmtk.c | 50 ++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 28 deletions(-)
drivers/bluetooth/btmtk.c +22 -28
@@ -763,30 +763,24 @@ static int btmtk_usb_uhw_reg_write(strucstatic int btmtk_usb_uhw_reg_read (struct hci_dev *hdev,u32 reg,u32 *val) {struct btmtk_data *data =hci_get_priv (hdev); -int pipe, err; -void *buf; - - buf =kzalloc (4 ,GFP_KERNEL ); -if (!buf) -return -ENOMEM ; +u8 buf[sizeof (u32)]; +int err; - pipe =usb_rcvctrlpipe (data->udev,0 ); - err =usb_control_msg (data->udev, pipe,0x01 , -0xDE , - reg >>16 , reg &0xffff , - buf,4 ,USB_CTRL_GET_TIMEOUT ); -if (err <0 ) { + *val =0 ; + err =usb_control_msg_recv (data->udev,0 ,0x01 , +0xDE , + reg >>16 , reg &0xffff , + buf,sizeof (buf),USB_CTRL_GET_TIMEOUT , +GFP_KERNEL ); +if (err) {bt_dev_err (hdev,"Failed to read uhw reg(%d)" , err); - goto err_free_buf; +return err; } *val =get_unaligned_le32 (buf);bt_dev_dbg (hdev,"reg=%x, value=0x%08x" , reg, *val); -err_free_buf: -kfree (buf); - -return err; +return 0 ; }static int btmtk_usb_reg_read (struct hci_dev *hdev,u32 reg,u32 *val)
@@ -836,7 +830,7 @@ int btmtk_usb_subsys_reset(struct hci_deif (dev_id ==0x7922 ) { err =btmtk_usb_uhw_reg_read (hdev,MTK_BT_SUBSYS_RST , &val); -if (err <0 ) +if (err)return err; val |=0x00002020 ; err =btmtk_usb_uhw_reg_write (hdev,MTK_BT_SUBSYS_RST , val);
@@ -846,7 +840,7 @@ int btmtk_usb_subsys_reset(struct hci_deif (err <0 )return err; err =btmtk_usb_uhw_reg_read (hdev,MTK_BT_SUBSYS_RST , &val); -if (err <0 ) +if (err)return err; val |=BIT (0 ); err =btmtk_usb_uhw_reg_write (hdev,MTK_BT_SUBSYS_RST , val);
@@ -855,14 +849,14 @@ int btmtk_usb_subsys_reset(struct hci_demsleep (100 ); }else if (dev_id ==0x7925 ) { err =btmtk_usb_uhw_reg_read (hdev,MTK_BT_RESET_REG_CONNV3 , &val); -if (err <0 ) +if (err)return err; val |= (1 <<5 ); err =btmtk_usb_uhw_reg_write (hdev,MTK_BT_RESET_REG_CONNV3 , val);if (err <0 )return err; err =btmtk_usb_uhw_reg_read (hdev,MTK_BT_RESET_REG_CONNV3 , &val); -if (err <0 ) +if (err)return err; val &=0xFFFF00FF ; val |= (1 <<13 );
@@ -873,7 +867,7 @@ int btmtk_usb_subsys_reset(struct hci_deif (err <0 )return err; err =btmtk_usb_uhw_reg_read (hdev,MTK_BT_RESET_REG_CONNV3 , &val); -if (err <0 ) +if (err)return err; val |= (1 <<0 ); err =btmtk_usb_uhw_reg_write (hdev,MTK_BT_RESET_REG_CONNV3 , val);
@@ -883,13 +877,13 @@ int btmtk_usb_subsys_reset(struct hci_deif (err <0 )return err; err =btmtk_usb_uhw_reg_read (hdev,MTK_UDMA_INT_STA_BT , &val); -if (err <0 ) +if (err)return err; err =btmtk_usb_uhw_reg_write (hdev,MTK_UDMA_INT_STA_BT1 ,0x000000FF );if (err <0 )return err; err =btmtk_usb_uhw_reg_read (hdev,MTK_UDMA_INT_STA_BT1 , &val); -if (err <0 ) +if (err)return err;msleep (100 ); }else {
@@ -899,7 +893,7 @@ int btmtk_usb_subsys_reset(struct hci_deif (err <0 )return err; err =btmtk_usb_uhw_reg_read (hdev,MTK_BT_WDT_STATUS , &val); -if (err <0 ) +if (err)return err;/* Reset the bluetooth chip via USB interface. */ err =btmtk_usb_uhw_reg_write (hdev,MTK_BT_SUBSYS_RST ,1 );
@@ -909,13 +903,13 @@ int btmtk_usb_subsys_reset(struct hci_deif (err <0 )return err; err =btmtk_usb_uhw_reg_read (hdev,MTK_UDMA_INT_STA_BT , &val); -if (err <0 ) +if (err)return err; err =btmtk_usb_uhw_reg_write (hdev,MTK_UDMA_INT_STA_BT1 ,0x000000FF );if (err <0 )return err; err =btmtk_usb_uhw_reg_read (hdev,MTK_UDMA_INT_STA_BT1 , &val); -if (err <0 ) +if (err)return err;/* MT7921 need to delay 20ms between toggle reset bit */ msleep (20 );
@@ -923,7 +917,7 @@ int btmtk_usb_subsys_reset(struct hci_deif (err <0 )return err; err =btmtk_usb_uhw_reg_read (hdev,MTK_BT_SUBSYS_RST , &val); -if (err <0 ) +if (err)return err; }
Greg Kroah-Hartman
maintainerPATCH 149/337 Bluetooth: mgmt: fix pending command UAF in EIR updates
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 8f2f62855a41d1730fb9e8122912bd2c8d6bed5d upstream.
MGMT_OP_SET_LOCAL_NAME is handled asynchronously on powered controllers and can run set_name_sync(). When the controller is BR/EDR capable, set_name_sync() updates the local name and then rebuilds EIR data through eir_create(). The EIR builder walks hdev->uuids, but the UUID list can be changed and entries can be freed by MGMT_OP_ADD_UUID and MGMT_OP_REMOVE_UUID.
pending_eir_or_class() is meant to serialize management commands that can change EIR or the class of device, but it did not include MGMT_OP_SET_LOCAL_NAME. In addition, it walked hdev->mgmt_pending without hdev->mgmt_pending_lock even though pending commands are added and removed under that mutex. A racing command completion can therefore remove and free a pending command while pending_eir_or_class() is still inspecting it, leading to a use-after-free in the pending-command list or allowing a local name update to rebuild EIR while UUID entries are being removed.
Take hdev->mgmt_pending_lock while scanning hdev->mgmt_pending and treat MGMT_OP_SET_LOCAL_NAME as an EIR/class-affecting pending command on the powered asynchronous path. Check for a conflicting pending command before copying the new short name so a rejected SET_LOCAL_NAME request does not modify hdev->short_name.
--- net/bluetooth/mgmt.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
net/bluetooth/mgmt.c +18 -2
@@ -2617,18 +2617,28 @@ unlock:static bool pending_eir_or_class (struct hci_dev *hdev) {struct mgmt_pending_cmd *cmd; +bool pending = false; + +mutex_lock (&hdev->mgmt_pending_lock);list_for_each_entry (cmd, &hdev->mgmt_pending, list) {switch (cmd->opcode) {case MGMT_OP_ADD_UUID :case MGMT_OP_REMOVE_UUID :case MGMT_OP_SET_DEV_CLASS : +case MGMT_OP_SET_LOCAL_NAME :case MGMT_OP_SET_POWERED : -return true; + pending = true; +break ; } + +if (pending) +break ; } -return false; +mutex_unlock (&hdev->mgmt_pending_lock); + +return pending; }static const u8 bluetooth_base_uuid[] = {
@@ -3964,6 +3974,12 @@ static int set_local_name(struct sock *s goto failed; } +if (hdev_is_powered (hdev) &&pending_eir_or_class (hdev)) { + err =mgmt_cmd_status (sk, hdev->id,MGMT_OP_SET_LOCAL_NAME , +MGMT_STATUS_BUSY ); + goto failed; + } +memcpy (hdev->short_name, cp->short_name,sizeof (hdev->short_name));if (!hdev_is_powered (hdev)) {
Greg Kroah-Hartman
maintainerPATCH 150/337 Bluetooth: mgmt: fix UAF in pair command cancellation
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 d0a7b48ad0921bd88effaee10bf970ab1d5d0ddd upstream.
The pairing completion and authentication failure callbacks look up the pending MGMT_OP_PAIR_DEVICE command by walking hdev->mgmt_pending. The lookup returned a command that was still linked on the shared pending list, without keeping mgmt_pending_lock held for the later dereference and removal.
A concurrent MGMT_OP_CANCEL_PAIR_DEVICE request can remove and free the same pending command before the callback uses it. The reverse race is also possible when cancel_pair_device() gets a command from pending_find() and a callback removes it before the cancel path dereferences it. This can lead to a use-after-free and a second list_del().
Make the pairing lookup helpers transfer ownership of the pending command by removing it from hdev->mgmt_pending while holding mgmt_pending_lock. The callbacks and cancel path then complete the command and free it directly, so racing paths cannot find or free the same command again. Take a temporary hci_conn reference in cancel_pair_device() because the command completion drops the reference stored in the pending command.
--- net/bluetooth/mgmt.c | 64 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 18 deletions(-)
net/bluetooth/mgmt.c +46 -18
@@ -3445,11 +3445,13 @@ static int set_io_capability(struct sock NULL, 0); } -static struct mgmt_pending_cmd *find_pairing (struct hci_conn *conn) +static struct mgmt_pending_cmd *remove_pairing (struct hci_conn *conn) {struct hci_dev *hdev = conn->hdev;struct mgmt_pending_cmd *cmd; +mutex_lock (&hdev->mgmt_pending_lock); +list_for_each_entry (cmd, &hdev->mgmt_pending, list) {if (cmd->opcode !=MGMT_OP_PAIR_DEVICE )continue ;
@@ -3457,9 +3459,39 @@ static struct mgmt_pending_cmd *find_paiif (cmd->user_data != conn)continue ; +list_del (&cmd->list); +mutex_unlock (&hdev->mgmt_pending_lock);return cmd; } +mutex_unlock (&hdev->mgmt_pending_lock); + +return NULL ; +} + +static struct mgmt_pending_cmd *remove_pairing_by_addr (struct hci_dev *hdev, +bdaddr_t *bdaddr) +{ +struct mgmt_pending_cmd *cmd; +struct hci_conn *conn; + +mutex_lock (&hdev->mgmt_pending_lock); + +list_for_each_entry (cmd, &hdev->mgmt_pending, list) { +if (cmd->opcode !=MGMT_OP_PAIR_DEVICE ) +continue ; + + conn = cmd->user_data; +if (bacmp (bdaddr, &conn->dst) !=0 ) +continue ; + +list_del (&cmd->list); +mutex_unlock (&hdev->mgmt_pending_lock); +return cmd; + } + +mutex_unlock (&hdev->mgmt_pending_lock); +return NULL ; }
@@ -3497,10 +3529,10 @@ void mgmt_smp_complete(struct hci_conn *u8 status = complete ?MGMT_STATUS_SUCCESS :MGMT_STATUS_FAILED ;struct mgmt_pending_cmd *cmd; - cmd =find_pairing (conn); + cmd =remove_pairing (conn);if (cmd) { cmd->cmd_complete (cmd, status); -mgmt_pending_remove (cmd); +mgmt_pending_free (cmd); } }
@@ -3510,14 +3542,14 @@ static void pairing_complete_cb(struct hBT_DBG ("status %u" , status); - cmd =find_pairing (conn); + cmd =remove_pairing (conn);if (!cmd) {BT_DBG ("Unable to find a pending command" );return ; } cmd->cmd_complete (cmd,mgmt_status (status)); -mgmt_pending_remove (cmd); +mgmt_pending_free (cmd); }static void le_pairing_complete_cb (struct hci_conn *conn,u8 status)
@@ -3529,14 +3561,14 @@ static void le_pairing_complete_cb(strucif (!status)return ; - cmd =find_pairing (conn); + cmd =remove_pairing (conn);if (!cmd) {BT_DBG ("Unable to find a pending command" );return ; } cmd->cmd_complete (cmd,mgmt_status (status)); -mgmt_pending_remove (cmd); +mgmt_pending_free (cmd); } static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -3693,23 +3725,17 @@ static int cancel_pair_device(struct soc goto unlock; } - cmd =pending_find (MGMT_OP_PAIR_DEVICE , hdev); + cmd =remove_pairing_by_addr (hdev, &addr->bdaddr);if (!cmd) { err =mgmt_cmd_status (sk, hdev->id,MGMT_OP_CANCEL_PAIR_DEVICE ,MGMT_STATUS_INVALID_PARAMS ); goto unlock; } - conn = cmd->user_data; - -if (bacmp (&addr->bdaddr, &conn->dst) !=0 ) { - err =mgmt_cmd_status (sk, hdev->id,MGMT_OP_CANCEL_PAIR_DEVICE , -MGMT_STATUS_INVALID_PARAMS ); - goto unlock; - } + conn =hci_conn_get (cmd->user_data); cmd->cmd_complete (cmd,MGMT_STATUS_CANCELLED ); -mgmt_pending_remove (cmd); +mgmt_pending_free (cmd); err =mgmt_cmd_complete (sk, hdev->id,MGMT_OP_CANCEL_PAIR_DEVICE ,0 , addr,sizeof (*addr));
@@ -3727,6 +3753,8 @@ static int cancel_pair_device(struct socif (conn->conn_reason ==CONN_REASON_PAIR_DEVICE )hci_abort_conn (conn,HCI_ERROR_REMOTE_USER_TERM ); +hci_conn_put (conn); + unlock:hci_dev_unlock (hdev);return err;
@@ -10221,14 +10249,14 @@ void mgmt_auth_failed(struct hci_conn *c ev.addr.type =link_to_bdaddr (conn->type, conn->dst_type); ev.status = status; - cmd =find_pairing (conn); + cmd =remove_pairing (conn);mgmt_event (MGMT_EV_AUTH_FAILED , conn->hdev, &ev,sizeof (ev), cmd ? cmd->sk :NULL );if (cmd) { cmd->cmd_complete (cmd, status); -mgmt_pending_remove (cmd); +mgmt_pending_free (cmd); } }
Greg Kroah-Hartman
maintainerPATCH 151/337 Bluetooth: hci_sync: Fix advertising data UAFs
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 cdc36db204ffd97b947d64374cf23a210dc74777 upstream.
hci_find_adv_instance() returns an adv_info pointer that is valid only while hdev->lock is held. The advertising command-sync paths perform instance lookups without that lock and, in some cases, retain the pointer while waiting for a controller response.
An advertising termination event can therefore interleave as follows:
hci_cmd_sync_work hci_rx_work
hci_find_adv_instance()
__hci_cmd_sync_status()
wait for controller reply hci_dev_lock()
hci_remove_adv_instance()
kfree(adv)
adv->scan_rsp_changed = false
KASAN reported:
BUG: KASAN: slab-use-after-free in hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 Write of size 1 at addr ffff88810a45d21d by task kworker/u17:0/88 Workqueue: hci0 hci_cmd_sync_work Call Trace: hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 hci_schedule_adv_instance_sync+0x390/0x4c0 hci_cmd_sync_work+0x173/0x300 Allocated by task 87: hci_add_adv_instance+0x538/0xac0 add_advertising+0x885/0x1160 Freed by task 89: kfree+0x131/0x3c0 hci_remove_adv_instance+0x1d8/0x3b0 hci_le_ext_adv_term_evt+0x17b/0x730
Protect the instance lookup and payload construction in the extended advertising, scan response, and periodic advertising data paths. Snapshot the advertising parameters under hdev->lock, but release the lock before waiting for the controller.
Clear advertising-data dirty bits before issuing their commands and restore them after a failure using a fresh lookup. Likewise, update the reported transmit power through a fresh lookup after the parameter command completes. No adv_info pointer then survives an HCI command wait.
--- net/bluetooth/hci_sync.c | 131 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 97 insertions(+), 34 deletions(-)
net/bluetooth/hci_sync.c +97 -34
@@ -1228,10 +1228,11 @@ static int hci_set_adv_set_random_addr_s }static int -hci_set_ext_adv_params_sync (struct hci_dev *hdev,struct adv_info *adv, +hci_set_ext_adv_params_sync (struct hci_dev *hdev,u8 instance,const struct hci_cp_le_set_ext_adv_params *cp,struct hci_rp_le_set_ext_adv_params *rp) { +struct adv_info *adv;struct sk_buff *skb; skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp),
@@ -1259,11 +1260,15 @@ hci_set_ext_adv_params_sync(struct hci_dif (!rp->status) { hdev->adv_addr_type = cp->own_addr_type; -if (!cp->handle) { +if (!instance) {/* Store in hdev for instance 0 */ hdev->adv_tx_power = rp->tx_power; - }else if (adv) { - adv->tx_power = rp->tx_power; + }else { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (adv) + adv->tx_power = rp->tx_power; +hci_dev_unlock (hdev); } }
@@ -1279,9 +1284,13 @@ static int hci_set_ext_adv_data_sync(strint err;if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); -if (!adv || !adv->adv_data_changed) +if (!adv || !adv->adv_data_changed) { +hci_dev_unlock (hdev);return 0 ; + } } len = eir_create_adv_data(hdev, instance, pdu->data,
@@ -1292,16 +1301,27 @@ static int hci_set_ext_adv_data_sync(str pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; pdu->frag_pref =LE_SET_ADV_DATA_NO_FRAG ; +if (adv) { + adv->adv_data_changed = false; +hci_dev_unlock (hdev); + } + err =__hci_cmd_sync_status (hdev,HCI_OP_LE_SET_EXT_ADV_DATA ,struct_size (pdu, data, len), pdu,HCI_CMD_TIMEOUT ); -if (err) +if (err) { +if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (adv) + adv->adv_data_changed = true; +hci_dev_unlock (hdev); + } +return err; + } -/* Update data if the command succeed */ -if (adv) { - adv->adv_data_changed = false; - }else { +if (!instance) {memcpy (hdev->adv_data, pdu->data, len); hdev->adv_data_len = len; }
@@ -1355,22 +1375,22 @@ int hci_setup_ext_adv_instance_sync(strustruct adv_info *adv;bool secondary_adv; -if (instance >0 ) { - adv =hci_find_adv_instance (hdev, instance); -if (!adv) -return -EINVAL ; - }else { - adv =NULL ; - } -/* Updating parameters of an active instance will return a -* Command Disallowed error, so we must first disable the -* instance if it is active. +* Command Disallowed error, so disable it before taking a snapshot. */ -if (adv) { +if (instance >0 ) { err =hci_disable_ext_adv_instance_sync (hdev, instance);if (err)return err; + +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (!adv) { +hci_dev_unlock (hdev); +return -EINVAL ; + } + }else { + adv =NULL ; } flags =hci_adv_instance_flags (hdev, instance);
@@ -1381,8 +1401,11 @@ int hci_setup_ext_adv_instance_sync(stru connectable = (flags &MGMT_ADV_FLAG_CONNECTABLE ) ||mgmt_get_connectable (hdev); -if (!is_advertising_allowed (hdev, connectable)) +if (!is_advertising_allowed (hdev, connectable)) { +if (instance) +hci_dev_unlock (hdev);return -EPERM ; + }/* Set require_privacy to true only when non-connectable * advertising is used and it is not periodic.
@@ -1393,8 +1416,11 @@ int hci_setup_ext_adv_instance_sync(stru err =hci_get_random_address (hdev, require_privacy,adv_use_rpa (hdev, flags), adv, &own_addr_type, &random_addr); -if (err <0 ) +if (err <0 ) { +if (instance) +hci_dev_unlock (hdev);return err; + }memset (&cp,0 ,sizeof (cp));
@@ -1445,6 +1471,9 @@ int hci_setup_ext_adv_instance_sync(stru cp.channel_map = hdev->le_adv_channel_map; cp.handle = adv ? adv->handle : instance; +if (instance) +hci_dev_unlock (hdev); +if (flags &MGMT_ADV_FLAG_SEC_2M ) { cp.primary_phy =HCI_ADV_PHY_1M ; cp.secondary_phy =HCI_ADV_PHY_2M ;
@@ -1457,12 +1486,12 @@ int hci_setup_ext_adv_instance_sync(stru cp.secondary_phy =HCI_ADV_PHY_1M ; } - err =hci_set_ext_adv_params_sync (hdev, adv, &cp, &rp); + err =hci_set_ext_adv_params_sync (hdev, instance, &cp, &rp);if (err)return err;/* Update adv data as tx power is known now */ - err =hci_set_ext_adv_data_sync (hdev, cp.handle); + err =hci_set_ext_adv_data_sync (hdev, instance);if (err)return err;
@@ -1470,9 +1499,14 @@ int hci_setup_ext_adv_instance_sync(stru own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) && bacmp(&random_addr, BDADDR_ANY)) {/* Check if random address need to be updated */ -if (adv) { -if (!bacmp (&random_addr, &adv->random_addr)) +if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (!adv || !bacmp (&random_addr, &adv->random_addr)) { +hci_dev_unlock (hdev);return 0 ; + } +hci_dev_unlock (hdev); }else {if (!bacmp (&random_addr, &hdev->random_addr))return 0 ;
@@ -1494,9 +1528,13 @@ static int hci_set_ext_scan_rsp_data_synint err;if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); -if (!adv || !adv->scan_rsp_changed) +if (!adv || !adv->scan_rsp_changed) { +hci_dev_unlock (hdev);return 0 ; + } } len =eir_create_scan_rsp (hdev, instance, pdu->data);
@@ -1506,15 +1544,27 @@ static int hci_set_ext_scan_rsp_data_syn pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; pdu->frag_pref =LE_SET_ADV_DATA_NO_FRAG ; +if (adv) { + adv->scan_rsp_changed = false; +hci_dev_unlock (hdev); + } + err =__hci_cmd_sync_status (hdev,HCI_OP_LE_SET_EXT_SCAN_RSP_DATA ,struct_size (pdu, data, len), pdu,HCI_CMD_TIMEOUT ); -if (err) +if (err) { +if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); +if (adv) + adv->scan_rsp_changed = true; +hci_dev_unlock (hdev); + } +return err; + } -if (adv) { - adv->scan_rsp_changed = false; - }else { +if (!instance) {memcpy (hdev->scan_rsp_data, pdu->data, len); hdev->scan_rsp_data_len = len; }
@@ -1529,8 +1579,14 @@ static int __hci_set_scan_rsp_data_sync(memset (&cp,0 ,sizeof (cp)); +if (instance) +hci_dev_lock (hdev); + len =eir_create_scan_rsp (hdev, instance, cp.data); +if (instance) +hci_dev_unlock (hdev); +if (hdev->scan_rsp_data_len == len && !memcmp (cp.data, hdev->scan_rsp_data, len))return 0 ;
@@ -1665,9 +1721,13 @@ static int hci_set_per_adv_data_sync(strstruct adv_info *adv =NULL ;if (instance) { +hci_dev_lock (hdev); + adv =hci_find_adv_instance (hdev, instance); -if (!adv || !adv->periodic) +if (!adv || !adv->periodic) { +hci_dev_unlock (hdev);return 0 ; + } } len =eir_create_per_adv_data (hdev, instance, pdu->data);
@@ -1676,6 +1736,9 @@ static int hci_set_per_adv_data_sync(str pdu->handle = adv ? adv->handle : instance; pdu->operation =LE_SET_ADV_DATA_OP_COMPLETE ; +if (adv) +hci_dev_unlock (hdev); +return __hci_cmd_sync_status (hdev,HCI_OP_LE_SET_PER_ADV_DATA ,struct_size (pdu, data, len), pdu,HCI_CMD_TIMEOUT );
@@ -6422,7 +6485,7 @@ static int hci_le_ext_directed_advertisiif (err)return err; - err =hci_set_ext_adv_params_sync (hdev,NULL , &cp, &rp); + err =hci_set_ext_adv_params_sync (hdev,0 , &cp, &rp);if (err)return err;
Greg Kroah-Hartman
maintainerPATCH 152/337 Bluetooth: HIDP: reject frames without a transaction header
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sangho Lee <[email protected]>
commit 47778d2c2087b5d192398f6fddf692d16a5431cf upstream.
hidp_recv_ctrl_frame() and hidp_recv_intr_frame() read skb->data[0] before checking that the L2CAP SDU contains a transaction header. A connected HIDP peer can send an empty basic-mode SDU and make both paths use an uninitialized byte from skb tailroom.
KMSAN reports the use in hidp_session_run(), with the uninitialized value originating in __alloc_skb() through vhci_write(). The control path produces two reports and the interrupt path produces one.
The byte can also be controlled by a malformed lower-layer packet. If an HCI ACL packet contains an L2CAP PDU with a declared zero-length payload followed by an extra 0x15 byte, l2cap_recv_acldata() reduces skb->len to the declared PDU length before dispatch. The current HIDP path nevertheless consumes the extra byte as HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG and terminates the HIDP session. With this change, the same packet is discarded and a subsequent feature report request succeeds.
Pull the transaction header with skb_pull_data() and discard frames that do not contain it.
--- net/bluetooth/hidp/core.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
net/bluetooth/hidp/core.c +15 -10
@@ -563,16 +563,18 @@ static int hidp_process_data(struct hidpstatic void hidp_recv_ctrl_frame (struct hidp_session *session,struct sk_buff *skb) { -unsigned char hdr, type, param; +unsigned char type, param; +u8 *hdr;int free_skb =1 ;BT_DBG ("session %p skb %p len %u" , session, skb, skb->len); - hdr = skb->data[0 ]; -skb_pull (skb,1 ); + hdr =skb_pull_data (skb,1 ); +if (!hdr) + goto free; - type = hdr &HIDP_HEADER_TRANS_MASK ; - param = hdr &HIDP_HEADER_PARAM_MASK ; + type = *hdr &HIDP_HEADER_TRANS_MASK ; + param = *hdr &HIDP_HEADER_PARAM_MASK ;switch (type) {case HIDP_TRANS_HANDSHAKE :
@@ -593,6 +595,7 @@ static void hidp_recv_ctrl_frame(structbreak ; } +free:if (free_skb)kfree_skb (skb); }
@@ -600,14 +603,15 @@ static void hidp_recv_ctrl_frame(structstatic void hidp_recv_intr_frame (struct hidp_session *session,struct sk_buff *skb) { -unsigned char hdr; +u8 *hdr;BT_DBG ("session %p skb %p len %u" , session, skb, skb->len); - hdr = skb->data[0 ]; -skb_pull (skb,1 ); + hdr =skb_pull_data (skb,1 ); +if (!hdr) + goto free; -if (hdr == (HIDP_TRANS_DATA |HIDP_DATA_RTYPE_INPUT )) { +if (*hdr == (HIDP_TRANS_DATA |HIDP_DATA_RTYPE_INPUT )) {hidp_set_timer (session);if (session->input)
@@ -619,9 +623,10 @@ static void hidp_recv_intr_frame(structBT_DBG ("report len %d" , skb->len); } }else { -BT_DBG ("Unsupported protocol header 0x%02x" , hdr); +BT_DBG ("Unsupported protocol header 0x%02x" , *hdr); } +free:kfree_skb (skb); }
Greg Kroah-Hartman
maintainerPATCH 153/337 Bluetooth: HIDP: validate numbered report payloads
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sangho Lee <[email protected]>
commit 34f53d27b81a16a02828c8fdfa4e02badc326f17 upstream.
When hidp_get_raw_report() waits for a numbered report, hidp_process_data() compares the expected report number with skb->data[0]. A connected HIDP peer can reply with only a DATA transaction header, leaving the skb empty after the header is removed.
KMSAN reports an uninitialized-value use in hidp_session_run(), with the value originating in __alloc_skb() through vhci_write(). The transaction header checks remove the empty-frame reports, but this report remains until the payload check is added.
The comparison can also consume a peer-controlled byte beyond the declared L2CAP PDU. A DATA | FEATURE response followed by an extra 0x01 byte made the current code accept that byte as report ID 1 and complete HIDIOCGFEATURE with a zero-byte result. With this change the malformed response is rejected with -EIO, while a subsequent valid response still succeeds.
Require a payload byte before comparing a numbered report ID. Unnumbered reports continue to accept an empty payload.
--- net/bluetooth/hidp/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
net/bluetooth/hidp/core.c +3 -2
@@ -546,9 +546,10 @@ static int hidp_process_data(struct hidp }if (test_bit (HIDP_WAITING_FOR_RETURN , &session->flags) && - param == session->waiting_report_type) { + param == session->waiting_report_type) {if (session->waiting_report_number <0 || - session->waiting_report_number == skb->data[0 ]) { + (skb->len && + session->waiting_report_number == skb->data[0 ])) {/* hidp_get_raw_report() is waiting on this report. */ session->report_return = skb; done_with_skb =0 ;
Greg Kroah-Hartman
maintainerPATCH 154/337 bpf: lwt: Fix dst reference leak on reroute failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuanqiang Luo <[email protected]>
commit 88c17de85ddb459c3fe1e3c65d61fa366b1cf0a8 upstream.
bpf_lwt_xmit_reroute() obtains a referenced dst from the route lookup. When skb_cow_head() fails before that dst is installed on the skb, the error path only frees the skb. The skb still owns its previous dst, so the newly looked up dst reference is leaked.
Release the new dst reference before freeing the skb on this error path.
--- net/core/lwt_bpf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/core/lwt_bpf.c +3 -1
@@ -248,8 +248,10 @@ static int bpf_lwt_xmit_reroute(struct s* if there is enough header space in skb. */ err =skb_cow_head (skb,LL_RESERVED_SPACE (dst->dev)); -if (unlikely (err)) +if (unlikely (err)) { +dst_release (dst); goto err; + }skb_dst_drop (skb);skb_dst_set (skb, dst);
Greg Kroah-Hartman
maintainerPATCH 155/337 ALSA: 6fire: Fix UAF at error handling during probe
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 a54bf16965f896415c3337bc4fbb40fb11941d99 upstream.
Although 6fire driver had a few fixes for dealing with the early error handling during the probe phase, it forgot a pending URB before freeing the resources, which may lead to a UAF.
This patch addresses it by doing the almost same cleanup procedure like the normal disconnect phase at the error path.
--- sound/usb/6fire/chip.c | 4 ++++ 1 file changed, 4 insertions(+)
sound/usb/6fire/chip.c +4 -0
@@ -158,6 +158,10 @@ static int usb6fire_chip_probe(struct usreturn 0 ; destroy_chip: + chip->shutdown = true; +if (card) +snd_card_disconnect (card); +usb6fire_chip_abort (chip);snd_card_free (card);return ret; }
Greg Kroah-Hartman
maintainerPATCH 156/337 ALSA: lx6464es: fix period byte count for 16-bit streams
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 6437033bffe8bd2af174d139af552d90d40c7ac6 upstream.
The lx6464es driver advertises both 16-bit and packed 24-bit PCM formats, but lx_trigger_start() and lx_interrupt_request_new_buffer() calculate the DMA period size as runtime->period_size * runtime->channels * 3. That is only correct for the packed 24-bit formats.
For 16-bit streams the driver submits buffers that are 50% larger than the actual ALSA period and advances the DMA address by the same wrong amount. For example, with 2 channels, 256 frames and 4 periods, the third buffer already extends beyond the ALSA buffer and the fourth buffer starts outside it.
Use snd_pcm_lib_period_bytes() so the byte count matches the runtime format, channel count and period size.
--- sound/pci/lx6464es/lx6464es.c | 5 +---- sound/pci/lx6464es/lx_core.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-)
sound/pci/lx6464es/lx6464es.c +1 -4
@@ -410,11 +410,8 @@ static void lx_trigger_start(struct lx64int err; -const u32 channels = substream->runtime->channels; -const u32 bytes_per_frame = channels *3 ; -const u32 period_size = substream->runtime->period_size;const u32 periods = substream->runtime->periods; -const u32 period_bytes = period_size * bytes_per_frame; +const u32 period_bytes =snd_pcm_lib_period_bytes (substream);dma_addr_t buf = substream->dma_buffer.addr;int i;
sound/pci/lx6464es/lx_core.c +1 -4
@@ -1015,10 +1015,7 @@ static int lx_interrupt_request_new_buffconst unsigned int is_capture = lx_stream->is_capture;int err; -const u32 channels = substream->runtime->channels; -const u32 bytes_per_frame = channels *3 ; -const u32 period_size = substream->runtime->period_size; -const u32 period_bytes = period_size * bytes_per_frame; +const u32 period_bytes =snd_pcm_lib_period_bytes (substream);const u32 pos = lx_stream->frame_pos;const u32 next_pos = ((pos+1 ) == substream->runtime->periods) ?0 : pos +1 ;
Greg Kroah-Hartman
maintainerPATCH 157/337 ALSA: pcm: wake linked drain waiters on unlink
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
commit f495b6c4c8594122918552c9be2b51eb71647cd9 upstream.
snd_pcm_drain() on a linked stream parks an on-stack wait entry on the drained peer's runtime->sleep, and after schedule_timeout() removes it only if that peer is still found in the caller's group. If group membership changes during the wait and the sleep ends by signal or timeout (so autoremove_wake_function() does not run), finish_wait() is skipped and snd_pcm_drain() returns with the entry still queued on that stream's sleep list; a later wake_up() then walks a freed stack frame. This is reachable by unlinking either the drained or the draining stream.
Unlike the close path (snd_pcm_drop() -> snd_pcm_post_stop()), snd_pcm_unlink() never wakes the sleep queues. Wake every group member under the group lock before the membership change, so a linked drainer is released and drops its entry while the streams are still grouped.
The window was opened when snd_pcm_link_rwsem stopped being held across the wait and the removal became conditional on group membership (see Fixes). The later switch to finish_wait() kept that conditional removal, so the signal/timeout case remained.
--- sound/core/pcm_native.c | 7 +++++++ 1 file changed, 7 insertions(+)
sound/core/pcm_native.c +7 -0
@@ -2334,6 +2334,7 @@ static void relink_to_local(struct snd_pstatic int snd_pcm_unlink (struct snd_pcm_substream *substream) { +struct snd_pcm_substream *s;struct snd_pcm_group *group;bool nonatomic = substream->pcm->nonatomic;bool do_free = false;
@@ -2346,6 +2347,12 @@ static int snd_pcm_unlink(struct snd_pcm group = substream->group;snd_pcm_group_lock_irq (group, nonatomic); +/* release drain waiters before changing membership, else snd_pcm_drain() +* leaves its on-stack wait entry queued on a member's sleep list +*/ +snd_pcm_group_for_each_entry (s, substream) +wake_up (&s->runtime->sleep); +relink_to_local (substream);refcount_dec (&group->refs);
Greg Kroah-Hartman
maintainerPATCH 158/337 ALSA: seq: Fix division by zero in initialize_timer()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
commit 21e19688433452dfbbbe6b2bb670dea6eb92f0f6 upstream.
A userspace-driven ALSA timer (SND_UTIMER) lets an unprivileged user set the backing snd_timer's hardware resolution to an arbitrary 64-bit value via SNDRV_TIMER_IOCTL_CREATE. snd_utimer_create() only rejects zero.
When such a timer is bound to a sequencer queue, initialize_timer() computes the tick period as
tmr->ticks = 1000000000 / (r * freq);
where r is that user-controlled resolution and freq is the sequencer update rate in Hz, clamped to MIN_FREQUENCY..MAX_FREQUENCY (10..6250). A resolution of 2^63 makes the 64-bit product r * freq wrap to zero for any even freq, including DEFAULT_FREQUENCY (1000), so the division faults with a divide-by-zero.
The division runs under tmr->lock with interrupts disabled, so the oops leaves the spinlock held and hangs the CPU. It is reachable by an unprivileged user with access to /dev/snd/timer and /dev/snd/seq.
Oops: divide error: 0000 [#1] SMP KASAN PTI CPU: 7 UID: 1000 PID: 456 Comm: alsa_seq_utimer Not tainted 7.2.0-rc4+ RIP: 0010:initialize_timer.constprop.0+0x20a/0x2d0 snd_seq_timer_start+0x15e/0x2b0 snd_seq_control_queue+0x56f/0xba0 snd_seq_write+0x3e0/0x730
Reject an overflowing product with check_mul_overflow() and fall back to a single tick, which also avoids feeding a wrapped-but-nonzero divisor (e.g. 2^63 * 1000 mod 2^64 == 0, or other resolutions wrapping to a small value) into the period computation.
--- sound/core/seq/seq_timer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
sound/core/seq/seq_timer.c +4 -5
@@ -362,11 +362,10 @@ static int initialize_timer(struct snd_s tmr->ticks =1 ;if (!(t->hw.flags &SNDRV_TIMER_HW_SLAVE )) {unsigned long r =snd_timer_resolution (tmr->timeri); -if (r) { - tmr->ticks = (unsigned int )(1000000000uL / (r * freq)); -if (! tmr->ticks) - tmr->ticks =1 ; - } +unsigned long den; + +if (r && !check_mul_overflow (r, freq, &den)) + tmr->ticks =max (1U , (unsigned int )(1000000000uL / den)); } tmr->initialized =1 ;return 0 ;
Greg Kroah-Hartman
maintainerPATCH 159/337 ALSA: timer: Clear SNDRV_TIMER_IFLG_DEAD once the close completes
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
commit c2744d5f3aea474513fd2298daecb94a952ce441 upstream.
snd_timer_close_locked() marks an instance with SNDRV_TIMER_IFLG_DEAD and returns early when the flag is already set, but the flag is never cleared again. A completed close ends in remove_slave_links(), which leaves timeri->timer NULL, so a second close is already harmless through the timer == NULL path; the early return can only be reached by an instance that was opened again in between. For such an instance the close unlinks nothing, so snd_timer_instance_free() frees an object that is still on timer->open_list_head, still on snd_timer_master_list if it was opened with a slave key, still owns any adopted slaves, and still holds its timer and module references.
snd_seq_timer_open() reopens an instance exactly like that: it retries its fallback open on the same object after a failure that has already run snd_timer_close_locked() internally. An unprivileged user with access to /dev/snd/timer and /dev/snd/seq can force that failure, since snd_timer_check_master() returns -EBUSY when a pending slave matches the new master's (slave_class, slave_id) key and the target timer has reached max_instances, and SNDRV_TIMER_IOCTL_SELECT with dev_class = SNDRV_TIMER_CLASS_SLAVE keeps the caller-supplied dev_sclass, so a sequencer queue's key can be forged. The freed instance is afterwards dereferenced by any further snd_timer_open() on that timer, by snd_timer_check_slave(), and by /proc/asound/timers, which faults on the stale ti->owner pointer.
The flag only has to be visible while the close is in progress, which is all its other users need. Clear it in remove_slave_links(), under the same timer->lock that sets it, once the instance is off every list.
--- sound/core/timer.c | 2 ++ 1 file changed, 2 insertions(+)
sound/core/timer.c +2 -0
@@ -409,6 +409,8 @@ static void remove_slave_links(struct snlist_del_init (&slave->ack_list);list_del_init (&slave->active_list); } +/* the close is done; a reopen must not see the mark */ + timeri->flags &= ~SNDRV_TIMER_IFLG_DEAD ; }/*
Greg Kroah-Hartman
maintainerPATCH 160/337 ALSA: ump: fix double free of out_cvts on rawmidi error
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 70c977815af0d997feb2d0c5d284d55689bf7051 upstream.
snd_ump_attach_legacy_rawmidi() allocates the legacy conversion array ump->out_cvts and, on the snd_rawmidi_new() error path, frees it with kfree() but leaves ump->out_cvts pointing at the freed memory. When the endpoint is later torn down, snd_ump_endpoint_free() frees ump->out_cvts a second time, resulting in a double free.
The host snd-usb-audio driver attaches the legacy rawmidi for any USB MIDI 2.0 (UMP) device, so a device that makes snd_rawmidi_new() fail reaches this path on enumeration.
Clear ump->out_cvts after freeing it on the error path so it is not freed again during teardown.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- sound/core/ump.c | 1 + 1 file changed, 1 insertion(+)
sound/core/ump.c +1 -0
@@ -1297,6 +1297,7 @@ int snd_ump_attach_legacy_rawmidi(struct &rmidi);if (err <0 ) {kfree (ump->out_cvts); + ump->out_cvts =NULL ;return err; }
Greg Kroah-Hartman
maintainerPATCH 161/337 ASoC: tas2562: fix DVC coefficient write order
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haidar Lee <[email protected]>
commit 8e957e4907c58e9ca944f98799524f2bbb9cf68a upstream.
The TAS2562 applies the 32-bit digital volume coefficient to the playback path when the last byte, DVC_CFG4 (book 0 page 2 reg 0x0F), is written. tas2562_volume_control_put() wrote DVC_CFG4 first and DVC_CFG1 (the MSB) last, so every volume change latched a value made of the previous coefficient's upper three bytes combined with the new LSB; the remaining bytes only took effect on the next volume change.
In practice the control was unusable: the first setting after power-on always played at roughly 0 dB no matter what value was requested (the chip's default upper bytes were still latched), and most subsequent changes muted the output entirely or produced a distorted, over-unity gain.
Verified on a TAS2562 (ADLINK OSM-520 / MT8189 board) by tracing the I2C writes with ftrace and by writing the same coefficients manually in both byte orders: written MSB-first the register block behaves exactly as the driver expects, LSB-first reproduces the broken behaviour.
Write the bytes MSB first with DVC_CFG4 last so the complete new coefficient is latched atomically.
--- sound/soc/codecs/tas2562.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-)
sound/soc/codecs/tas2562.c +15 -8
@@ -475,20 +475,27 @@ static int tas2562_volume_control_put(stu32 reg_val; reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0 ]/2 ]; - ret =snd_soc_component_write (component,TAS2562_DVC_CFG4 , - (reg_val &0xff )); -if (ret) -return ret; - ret =snd_soc_component_write (component,TAS2562_DVC_CFG3 , - ((reg_val >>8 ) &0xff )); +/* +* The device applies the 32-bit coefficient to the playback path on +* the write to DVC_CFG4 (the LSB, book 0 page 2 reg 0x0F), so the +* bytes must be written MSB first and DVC_CFG4 last. Writing CFG4 +* first latches a mix of the previous coefficient's upper bytes and +* the new LSB instead of the requested value. +*/ + ret =snd_soc_component_write (component,TAS2562_DVC_CFG1 , + ((reg_val >>24 ) &0xff ));if (ret)return ret; ret =snd_soc_component_write (component,TAS2562_DVC_CFG2 , ((reg_val >>16 ) &0xff ));if (ret)return ret; - ret =snd_soc_component_write (component,TAS2562_DVC_CFG1 , - ((reg_val >>24 ) &0xff )); + ret =snd_soc_component_write (component,TAS2562_DVC_CFG3 , + ((reg_val >>8 ) &0xff )); +if (ret) +return ret; + ret =snd_soc_component_write (component,TAS2562_DVC_CFG4 , + (reg_val &0xff ));if (ret)return ret;
Greg Kroah-Hartman
maintainerPATCH 162/337 ASoC: tas2562: fix broken entries in the volume lookup table
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haidar Lee <[email protected]>
commit bdb0fd6de403fcea7b85dc9d38f0a571583ebe80 upstream.
The float_vol_db_lookup table is supposed to hold round(10^(dB/20) * 2^30) for every 2 dB step from -110 dB to 0 dB, which is 56 entries, but it only has 55: the -90 dB entry duplicates the -92 dB value (0x0000695b) and the -20 dB entry (0x06666666) is missing altogether. As a result every step between -90 dB and -22 dB is off by 2 dB, and the control's maximum raw value of 110 indexes one element past the end of the array.
Replace the duplicated -90 dB entry with the correct value 0x000084a3 and add the missing -20 dB entry, bringing the table to the full 56 entries so index 55 (raw value 110, 0 dB) is in range again.
--- sound/soc/codecs/tas2562.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
sound/soc/codecs/tas2562.c +4 -3
@@ -32,15 +32,16 @@static const unsigned int float_vol_db_lookup[] = {0x00000d43 ,0x000010b2 ,0x00001505 ,0x00001a67 ,0x00002151 ,0x000029f1 ,0x000034cd ,0x00004279 ,0x000053af ,0x0000695b , -0x0000695b ,0x0000a6fa ,0x0000d236 ,0x000108a4 ,0x00014d2a , +0x000084a3 ,0x0000a6fa ,0x0000d236 ,0x000108a4 ,0x00014d2a ,0x0001a36e ,0x00021008 ,0x000298c0 ,0x000344df ,0x00041d8f ,0x00052e5a ,0x000685c8 ,0x00083621 ,0x000a566d ,0x000d03a7 ,0x0010624d ,0x0014a050 ,0x0019f786 ,0x0020b0bc ,0x0029279d ,0x0033cf8d ,0x004139d3 ,0x00521d50 ,0x00676044 ,0x0082248a ,0x00a3d70a ,0x00ce4328 ,0x0103ab3d ,0x0146e75d ,0x019b8c27 ,0x02061b89 ,0x028c423f ,0x03352529 ,0x0409c2b0 ,0x05156d68 , -0x080e9f96 ,0x0a24b062 ,0x0cc509ab ,0x10137987 ,0x143d1362 , -0x197a967f ,0x2013739e ,0x28619ae9 ,0x32d64617 ,0x40000000 +0x06666666 ,0x080e9f96 ,0x0a24b062 ,0x0cc509ab ,0x10137987 , +0x143d1362 ,0x197a967f ,0x2013739e ,0x28619ae9 ,0x32d64617 , +0x40000000 };struct tas2562_data {
Greg Kroah-Hartman
maintainerPATCH 163/337 ata: libata-eh: Increase STANDBY IMMEDIATE timeout
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matt Vollrath <[email protected]>
commit 1e024d2b41ee32bc06818f7f09a3562c58842cf9 upstream.
Correct a previous change (see Fixes) which reduced the standby timeout from 30 to 5 seconds. Increase it to 15 seconds.
I was troubleshooting an error spotted during system suspend:
[ 1217.152867] ata1.00: Entering standby power mode
[ 1222.322948] ata1.00: qc timeout after 5000 msecs (cmd 0xe0)
[ 1222.324010] ata1.00: STANDBY IMMEDIATE failed (err_mask=0x4)This drive is a Samsung 870 EVO SSD in good SMART standing, and I wasn't aware of any reason it should be taking so long to standby. The issue is intermittent, but I observed it sometimes taking 7 seconds to manually standby. I assume this was interruption of background maintenance after a power outage.
As a desktop user, I would prefer to wait the extra 2 seconds at suspend to let the drive finish its business rather than drop the rails from under it.
The change from 30 to 5 seconds was implicit when switching suspend from START STOP UNIT to an internal command with no timeout table entry. No reason was stated for the change.
--- drivers/ata/libata-eh.c | 8 ++++++++ include/linux/libata.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-)
drivers/ata/libata-eh.c +8 -0
@@ -106,6 +106,12 @@ static const unsigned int ata_eh_flush_t UINT_MAX, }; +static const unsigned int ata_eh_standby_timeouts[] = { +15000 ,/* Some drives may be slow to standby */ +/* but don't hold up a suspend too long waiting for them */ +UINT_MAX , +}; +static const unsigned int ata_eh_other_timeouts[] = {5000 ,/* same rationale as identify timeout */ 10000 ,/* ditto */
@@ -147,6 +153,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIME .timeouts = ata_eh_other_timeouts, }, { .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT), .timeouts = ata_eh_flush_timeouts }, + { .commands = CMDS(ATA_CMD_STANDBYNOW1), + .timeouts = ata_eh_standby_timeouts }, { .commands = CMDS(ATA_CMD_VERIFY), .timeouts = ata_eh_reset_timeouts }, };
include/linux/libata.h +1 -1
@@ -402,7 +402,7 @@ enum {/* This should match the actual table size of * ata_eh_cmd_timeout_table in libata-eh.c. */ - ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 8, + ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 9,/* * Quirk flags: may be set by libata or controller drivers on drives.
Greg Kroah-Hartman
maintainerPATCH 164/337 ata: libata-sata: fix ata_scsi_lpm_supported() iteration
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Niklas Cassel <[email protected]>
commit 3fd70e96914d761c17c376aadd0b0d1a3c9badba upstream.
The inner loop of ata_scsi_lpm_supported() uses the wrong variable when iterating.
It should obviously use the link that we are currently iterating over, rather than always using the host link.
ata_scsi_lpm_supported() is used to control if a user should be allowed to change lpm policy (from the default) via sysfs.
Thus, this bug could potentially disallow users to change the LPM policy for certain SATA devices via sysfs.
--- drivers/ata/libata-sata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/ata/libata-sata.c +1 -1
@@ -913,7 +913,7 @@ static bool ata_scsi_lpm_supported(struct ata_port *ap)return false;ata_for_each_link (link, ap,EDGE ) { -ata_for_each_dev (dev, &ap->link,ENABLED ) { +ata_for_each_dev (dev, link,ENABLED ) {if (dev->quirks &ATA_QUIRK_NOLPM )return false; }
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 165/337 ALSA: usb-audio: fix use-after-free in ump_to_endpoint()
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 4a05b2d1b4642df74f30b6f54843e825c4a2bfd3 upstream.
create_midi2_ump() registers a card-owned snd_ump_endpoint and stores a back-pointer to its per-interface snd_usb_midi2_ump object in ump->private_data, but it never installs an ump->private_free hook and never clears that pointer.
If a later step of snd_usb_midi_v2_create() fails, its error path calls free_all_midi2_umps(), which kfree()s the snd_usb_midi2_ump object while the already-registered endpoint keeps pointing at it. The created /dev/snd/umpC*D* node stays exposed, so the first operation of any UMP open, ump_to_endpoint(), dereferences the dangling ump->private_data and reads rmidi->eps[dir] out of freed memory.
A malicious USB MIDI 2.0 device that makes creation fail after the endpoint is registered can thus trigger a slab use-after-free read on a subsequent open of the UMP node.
Clear the endpoint's back-pointer before freeing the object, and let ump_to_endpoint() tolerate a NULL private_data so the open/close/trigger callbacks fail cleanly (their callers already handle a NULL endpoint) instead of dereferencing a stale pointer.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- sound/usb/midi2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
sound/usb/midi2.c +3 -1
@@ -337,7 +337,7 @@ ump_to_endpoint(struct snd_ump_endpoint {struct snd_usb_midi2_ump *rmidi = ump->private_data; -return rmidi->eps[dir]; +return rmidi ? rmidi->eps[dir] :NULL ; }/* ump open callback */
@@ -693,6 +693,8 @@ static void free_all_midi2_umps(struct s rmidi =list_first_entry (&umidi->rawmidi_list, struct snd_usb_midi2_ump, list);list_del (&rmidi->list); +if (rmidi->ump) + rmidi->ump->private_data =NULL ;kfree (rmidi); } }
Greg Kroah-Hartman
maintainerPATCH 166/337 ALSA: usb-audio: fix stack info leak in RME Digiface status
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 441aaad150c57edaf57ee482a79a3bf4c5b7e353 upstream.
snd_rme_digiface_read_status() reads a four-word status block from the device into an uninitialised on-stack __le32 buf[4] and, whenever the vendor control-IN transfer does not return a negative error, copies all four words into the caller's status[].
snd_usb_ctl_msg() copies the full requested size back into the caller's buffer regardless of how many bytes the data stage actually delivered:
buf = kmemdup(data, size, GFP_KERNEL); err = usb_control_msg(dev, pipe, request, requesttype, value, index, buf, size, timeout); memcpy(data, buf, size);
usb_control_msg() returns the transferred length on a short control-IN, which is a non-negative value, and writes only that many bytes. The remainder of the copy back is the kmemdup()ed image of the caller's buffer, so a device answering with a short data stage leaves the trailing words of buf[] holding leftover kernel stack. The only guard in the caller is err < 0, so those words are stored into status[].
They then reach user space: snd_rme_digiface_get_status_val() selects a 16-bit halfword of status[] per the control's reg/mask, and the eight Digiface status controls together expose the whole 16-byte frame to an unprivileged reader of /dev/snd/controlC*.
Zero-initialise the buffer so a short read yields zeros instead of stack residue. This mirrors snd_rme_get_status1(), which already clears its output word before the same kind of vendor read.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- sound/usb/mixer_quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/usb/mixer_quirks.c +1 -1
@@ -3413,7 +3413,7 @@ static int snd_rme_digiface_read_status(struct usb_mixer_elem_list *list =snd_kcontrol_chip (kcontrol);struct snd_usb_audio *chip = list->mixer->chip;struct usb_device *dev = chip->dev; -__le32 buf[4 ]; +__le32 buf[4 ] = {};int err; err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
Greg Kroah-Hartman
maintainerPATCH 167/337 ALSA: usb-audio: fix OOB write in snd_usbmidi_akai_output()
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 0970274613fb463d376211450cab066d34ebfe6a upstream.
snd_usbmidi_akai_output() computes its fill-loop bound
buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1;
as a signed int, so a small device-advertised bulk-OUT max_transfer makes buf_end negative. The loop guard then compares the u32 urb->transfer_buffer_length against that negative int: the usual arithmetic conversion turns buf_end into a large unsigned value, so the guard stays true and each iteration keeps appending SysEx framing and payload bytes past the end of the URB transfer buffer, which is only max_transfer bytes long.
A USB device that advertises a tiny bulk-OUT endpoint can therefore trigger an attacker-length- and content-controlled heap out-of-bounds write when a process writes to the created /dev/snd/midiC*D* node.
Return early when there is no room for even one SysEx, so the loop is never entered with a bound that would wrap. The loop is the last statement of the function, so bailing out is equivalent to it not running.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- sound/usb/midi.c | 2 ++ 1 file changed, 2 insertions(+)
sound/usb/midi.c +2 -0
@@ -800,6 +800,8 @@ static void snd_usbmidi_akai_output(stru msg = urb->transfer_buffer + urb->transfer_buffer_length; buf_end = ep->max_transfer -MAX_AKAI_SYSEX_LEN -1 ; +if (buf_end <=0 ) +return ;/* only try adding more data when there's space for at least 1 SysEx */ while (urb->transfer_buffer_length < buf_end) {
Greg Kroah-Hartman
maintainerPATCH 168/337 ALSA: usb-audio: Fix DMA buffer out-of-bounds write when fill_max is set
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sonali Pradhan <[email protected]>
commit d0199ae1666ff9ae2d1d568d64c3430d4c47f0e5 upstream.
When a USB audio endpoint requests full packet transfers via the fill_max descriptor flag, data_ep_set_params() promotes ep->curpacksize to ep->maxpacksize. However, maxsize is left at the original sample-rate derived value.
Since u->buffer_size is allocated as maxsize * packets, the resulting DMA buffer is far too small for the requested transfer length. When the USB host controller streams up to curpacksize bytes per packet, it writes past the end of the buffer via DMA, corrupting kernel heap memory.
Update maxsize to curpacksize when fill_max is set so that the allocated DMA buffer size matches the actual transfer request size.
[ changed to reassign maxsize only when ep->fill_max is set -- tiwai ]
--- sound/usb/endpoint.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
sound/usb/endpoint.c +4 -2
@@ -1188,10 +1188,12 @@ static int data_ep_set_params(struct snd << (16 - ep->datainterval); } -if (ep->fill_max) +if (ep->fill_max) { ep->curpacksize = ep->maxpacksize; -else + maxsize = ep->curpacksize; + }else { ep->curpacksize = maxsize; + }if (snd_usb_get_speed (chip->dev) !=USB_SPEED_FULL ) { packs_per_ms =8 >> ep->datainterval;
Greg Kroah-Hartman
maintainerPATCH 169/337 ALSA: usb-audio: Clamp frame size in implicit-feedback mode
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sonali Pradhan <[email protected]>
commit 8d7a30c50c2e58a6839634ed0acde14466d1dc61 upstream.
snd_usb_handle_sync_urb() scales received sync packet sizes by the sender's stride and stores the result directly in out_packet->packet_size[i]. If a connected USB device sends an oversized sync packet, this frame count can exceed ep->maxframesize.
The un-clamped frame count then propagates to the playback endpoint queue, potentially driving packet transfers beyond the endpoint's hardware frame limits.
Cap the calculated frame count against ep->maxframesize in snd_usb_handle_sync_urb() to prevent oversized packets from entering the playback queue.
--- sound/usb/endpoint.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
sound/usb/endpoint.c +5 -3
@@ -1837,11 +1837,13 @@ static void snd_usb_handle_sync_urb(stru out_packet->packets = in_ctx->packets;for (i =0 ; i < in_ctx->packets; i++) { -if (urb->iso_frame_desc[i].status ==0 ) - out_packet->packet_size[i] = +if (urb->iso_frame_desc[i].status ==0 ) { +unsigned int frames = urb->iso_frame_desc[i].actual_length / sender->stride; -else + out_packet->packet_size[i] =min (frames, ep->maxframesize); + }else { out_packet->packet_size[i] =0 ; + } }spin_unlock_irqrestore (&ep->lock, flags);
Greg Kroah-Hartman
maintainerPATCH 170/337 dmaengine: qcom: bam_dma: Fix command element mask field for BAM v1.6.0+
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Md Sadre Alam <[email protected]>
commit 867621ba203027338b525af6729719c544135336 upstream.
BAM version 1.6.0 and later changed the behavior of the mask field in command elements for read operations.
In older BAM versions, or prior implementation assumptions, the mask field was effectively ignored for read commands. However, starting from BAM v1.6.0, the mask field for read commands is repurposed to carry the upper 4 bits of the destination address, enabling support for 36-bit addressing. For write commands, the mask field continues to function as a traditional write mask.
The current driver sets mask = 0xffffffff for all command elements. While this works for write operations, it breaks read operations on BAM v1.6.0+ hardware. In such cases, the hardware interprets the upper address bits as 0xf, resulting in an invalid destination address (0xf_xxxxxxxx instead of 0x0_xxxxxxxx).
This leads to failures such as NAND enumeration issues observed on platforms like IPQ5424.
Fix this by assigning the mask field based on command type:
- For read commands: set mask = 0 (upper address bits = 0)
- For write commands: retain mask = 0xffffffff
Also update the bam_cmd_element structure documentation to reflect the dual purpose of the mask field across BAM versions.
This ensures correct behavior on BAM v1.6.0+ while maintaining backward compatibility with older hardware.
--- include/linux/dma/qcom_bam_dma.h | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-)
include/linux/dma/qcom_bam_dma.h +16 -5
@@ -13,9 +13,12 @@* supported by BAM DMA Engine. * * @cmd_and_addr - upper 8 bits command and lower 24 bits register address. -* @data - for write command: content to be written into peripheral register. -* for read command: dest addr to write peripheral register value. -* @mask - register mask. +* @data - For write command: content to be written into peripheral register. +* For read command: lower 32 bits of destination address. +* @mask - For write command: register write mask. +* For read command on BAM v1.6.0+: upper 4 bits of destination address. +* For read command on BAM < v1.6.0: ignored by hardware. +* Setting to 0 ensures 32-bit addressing compatibility. * @reserved - for future usage. * */
@@ -42,6 +45,10 @@ enum bam_command_type {* @addr: target address * @cmd: BAM command * @data: actual data for write and dest addr for read in le32 +* +* For BAM v1.6.0+, the mask field behavior depends on command type: +* - Write commands: mask = write mask (typically 0xffffffff) +* - Read commands: mask = upper 4 bits of destination address (0 for 32-bit) */ static inline void bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr,
@@ -50,7 +57,11 @@ bam_prep_ce_le32(struct bam_cmd_element bam_ce->cmd_and_addr =cpu_to_le32 ((addr &0xffffff ) | ((cmd &0xff ) <<24 )); bam_ce->data = data; - bam_ce->mask =cpu_to_le32 (0xffffffff ); +if (cmd ==BAM_READ_COMMAND ) + bam_ce->mask =cpu_to_le32 (0x0 );/* 32-bit addressing */ +else + bam_ce->mask =cpu_to_le32 (0xffffffff );/* Write mask */ + bam_ce->reserved =0 ; }/*
@@ -60,7 +71,7 @@ bam_prep_ce_le32(struct bam_cmd_element* @bam_ce: BAM command element * @addr: target address * @cmd: BAM command -* @data: actual data for write and dest addr for read +* @data: actual data for write and destination address for read */ static inline void bam_prep_ce(struct bam_cmd_element *bam_ce, u32 addr,
Greg Kroah-Hartman
maintainerPATCH 171/337 e1000: fix memory leak in e1000_probe()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawei Feng <[email protected]>
commit 816419dfea5c88126f35eb7a1b429a1bf546665e upstream.
In the e1000_probe() path, e1000_sw_init() allocates adapter->tx_ring and adapter->rx_ring. If the subsequent CE4100-specific MDIO BAR mapping fails, the error handling jumps past the ring cleanup code, leaking both allocations.
Fix this leak by moving the err_mdio_ioremap label above the ring deallocation logic. This guarantees the proper release of these resources and prevents the memory leak.
The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc6.
An x86_64 allyesconfig build showed no new warnings. As we do not have a CE4100 reference platform to test with, no runtime testing was able to be performed.
--- drivers/net/ethernet/intel/e1000/e1000_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/intel/e1000/e1000_main.c +1 -1
@@ -1225,11 +1225,11 @@ err_eeprom:if (hw->flash_address)iounmap (hw->flash_address); +err_mdio_ioremap:kfree (adapter->tx_ring);kfree (adapter->rx_ring); err_dma: err_sw_init: -err_mdio_ioremap:iounmap (hw->ce4100_gbe_mdio_base_virt);iounmap (hw->hw_addr); err_ioremap:
Greg Kroah-Hartman
maintainerPATCH 172/337 igbvf: Fix leak in TX DMA error cleanup
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matt Vollrath <[email protected]>
commit 0565052b7e2f436b7f1541f4849da96dc0aa7a0e upstream.
If an error is encountered while mapping TX buffers, the driver should unmap any buffers already mapped for that skb.
Because count is incremented before each frag mapping, it will always match the correct number of unmappings needed when dma_error is reached. Decrementing count before the while loop in dma_error causes an off-by-one error. If any mapping was successful before an unsuccessful mapping, exactly one DMA mapping (the head) would leak.
This bug was introduced by a 2010 fix for an endless loop in dma_error. All other affected drivers have already been fixed.
--- drivers/net/ethernet/intel/igbvf/netdev.c | 2 -- 1 file changed, 2 deletions(-)
drivers/net/ethernet/intel/igbvf/netdev.c +0 -2
@@ -2201,8 +2201,6 @@ dma_error: buffer_info->time_stamp =0 ; buffer_info->length =0 ; buffer_info->mapped_as_page = false; -if (count) - count--;/* clear timestamp and dma mappings for remaining portion of packet */ while (count--) {
Greg Kroah-Hartman
maintainerPATCH 173/337 ipvs: do not propagate one-packet flag to synced conns
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 a63d2dbaeb50a85d4c976b15a36e6b0c7113db5b upstream.
Synced connections can be created before their destination exists. When the destination is later added, ip_vs_bind_dest() copies connection flags from the destination into cp->flags.
IP_VS_CONN_F_ONE_PACKET connections are not synced. If a synced connection inherits IP_VS_CONN_F_ONE_PACKET while it is already hashed, expiry can treat it as a one-packet connection and skip unlinking the existing conn_tab node, leaving stale hash nodes pointing at a freed struct ip_vs_conn.
Drop IP_VS_CONN_F_ONE_PACKET from destination flags when binding synced connections.
--- net/netfilter/ipvs/ip_vs_conn.c | 3 +++ 1 file changed, 3 insertions(+)
net/netfilter/ipvs/ip_vs_conn.c +3 -0
@@ -599,6 +599,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, s flags = cp->flags;/* Bind with the destination and its corresponding transmitter */ if (flags &IP_VS_CONN_F_SYNC ) { +/* Synced conns are hashed, so they can not get this flag */ + conn_flags &= ~IP_VS_CONN_F_ONE_PACKET ; +/* if the connection is not template and is created * by sync, preserve the activity flag. */
Greg Kroah-Hartman
maintainerPATCH 174/337 net/smc: fix socket use-after-free during link group termination
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuanqiang Luo <[email protected]>
commit f621d6ebeebb6374342571e4ddf45fdbc420f6cd upstream.
__smc_lgr_terminate() drops conns_lock after finding a connection in lgr->conns_all, but before taking a reference on its socket. The connection is embedded in the socket, and its registration reference protects it only while the connection remains in the tree.
A concurrent close can unregister the connection and drop that reference, freeing the socket before the termination worker reaches sock_hold().
The race is reachable when close overlaps link group termination. Local stress testing reproduced the use-after-free and KASAN reported:
BUG: KASAN: slab-use-after-free in __smc_lgr_terminate.part.0 [smc] Write of size 4 by task kworker/3:3 Workqueue: events smc_lgr_terminate_work [smc] __smc_lgr_terminate.part.0 [smc]
The socket was allocated by smc_create(), freed through slab_free_after_rcu_debug(), and was followed by:
refcount_t: addition on 0; use-after-free.
__smc_lgr_terminate.part.0 [smc]
Take the socket reference while conns_lock still protects the tree entry. The unregister path then cannot drop the last reference until termination has finished using the socket.
--- net/smc/smc_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/smc/smc_core.c +1 -1
@@ -1553,10 +1553,10 @@ static void __smc_lgr_terminate(struct sread_lock_bh (&lgr->conns_lock); node =rb_first (&lgr->conns_all);while (node) { -read_unlock_bh (&lgr->conns_lock); conn = rb_entry(node, struct smc_connection, alert_node); smc = container_of(conn, struct smc_sock, conn);sock_hold (&smc->sk);/* sock_put below */ +read_unlock_bh (&lgr->conns_lock);lock_sock (&smc->sk);smc_conn_kill (conn, soft);release_sock (&smc->sk);
Greg Kroah-Hartman
maintainerPATCH 175/337 netfilter: ipset: do not update comments from kernel-side hash adds
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Lee <[email protected]>
commit f30415929be8aeb002d557c8d3f7ab2d2188003a upstream.
mtype_resize() copies comment pointers with memcpy(), not the comment objects themselves. During the window after an entry has been copied but before the table swap and backlog replay, the old table is still published for packet-side updates while the replacement-table entry already holds the same ip_set_comment_rcu pointer.
If xt_SET --add-set ... --exist hits that old entry in this window, mtype_add() calls ip_set_init_comment() even though packet-side adds carry no comment payload. That call frees the shared comment through the old entry, so the replacement-table entry now holds a stale pointer. When the queued add is replayed on the new table, mtype_add() calls ip_set_init_comment() again and strlen() dereferences the stale pointer.
Fix this in mtype_add() by skipping ip_set_init_comment() when ext->target marks a packet-side add. Userspace adds still update comments, while packet-side adds can no longer free comment storage shared with a resize copy.
--- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/ipset/ip_set_hash_gen.h +1 -1
@@ -984,7 +984,7 @@ overwrite_extensions:#endif if (SET_WITH_COUNTER (set))ip_set_init_counter (ext_counter (data, set), ext); -if (SET_WITH_COMMENT (set)) +if (SET_WITH_COMMENT (set) && !ext->target)ip_set_init_comment (set,ext_comment (data, set), ext);if (SET_WITH_SKBINFO (set))ip_set_init_skbinfo (ext_skbinfo (data, set), ext);
Greg Kroah-Hartman
maintainerPATCH 176/337 tipc: avoid use-after-free in poll trace queue dumps
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 b4f1719dfea023220e0e6bd892b087d76b2a6a49 upstream.
TIPC socket tracepoints dump queue state through tipc_sk_dump(). Most queue-dump callsites already serialize that walk under the socket lock or sk->sk_lock.slock, but tipc_poll() calls trace_tipc_sk_poll(..., TIPC_DUMP_ALL, ...) without holding either lock.
That lets the poll trace path reach tipc_list_dump() and backlog head/tail dumping while another context dequeues and frees an skb, leaving the trace helper dereferencing a stale queue entry.
Stop the unlocked poll trace site from requesting queue dumps. Other queue dump trace callsites keep their existing output under the locking they already provide, while poll still emits the event itself without walking live queue members from an unlocked context.
--- net/tipc/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/tipc/socket.c +1 -1
@@ -795,7 +795,7 @@ static __poll_t tipc_poll(struct file *f__poll_t revents =0 ;sock_poll_wait (file, sock, wait); -trace_tipc_sk_poll (sk,NULL ,TIPC_DUMP_ALL ," " ); +trace_tipc_sk_poll (sk,NULL ,TIPC_DUMP_NONE ," " );if (sk->sk_shutdown &RCV_SHUTDOWN ) revents |=EPOLLRDHUP |EPOLLIN |EPOLLRDNORM ;
Greg Kroah-Hartman
maintainerPATCH 177/337 wifi: mwifiex: use the subframe length when parsing A-MSDU TDLS frames
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhao Li <[email protected]>
commit 99a948382af8a225e2d5e54a7052158cd6281cc6 upstream.
mwifiex_11n_dispatch_amsdu_pkt() splits an A-MSDU with ieee80211_amsdu_to_8023s() and walks the resulting subframes. For each subframe it passes the subframe data pointer to mwifiex_process_tdls_action_frame(), but pairs it with skb->len, the length of the A-MSDU parent, instead of rx_skb->len:
rx_skb = __skb_dequeue(&list);
rx_hdr = (struct rx_packet_hdr *)rx_skb->data;
if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
mwifiex_process_tdls_action_frame(priv, (u8 *)rx_hdr,
skb->len);
}The parent is not a valid description of that buffer, and may not be valid memory at all. ieee80211_amsdu_to_8023s() ends with
if (!reuse_skb) dev_kfree_skb(skb);
and it only sets reuse_skb when the parent is linear, is not a head_frag, and is being consumed as the *last* subframe. So when the parent does not qualify for reuse it has already been freed, and the read of skb->len is a use-after-free. When it is reused, skb->len is the length of the last subframe, applied to every earlier subframe, which over-states the buffer whenever an earlier subframe is shorter.
The callee cannot absorb a wrong length, because it derives its own ceiling from the value it is given. Each frame type computes
ies_len = len - sizeof(struct ethhdr) - TDLS_*_FIX_LEN;
and the element walk is then bounded entirely against that ceiling,
for (end = pos + ies_len; pos + 1 < end; pos += 2 + pos[1]) {
u8 ie_len = pos[1];
if (pos + 2 + ie_len > end)
break;so a too-large len moves end past the end of the subframe and the walk reads and copies beyond it. The A-MSDU layout is chosen by the sender, which makes the difference between the last subframe and a shorter earlier one remotely selectable. Reaching this requires TDLS support in firmware and the TDLS ethertype on the subframe.
The other caller, mwifiex_process_rx_packet(), is correct: it passes a pointer and a length that describe the same region of the RX buffer.
Pass rx_skb->len, the length of the subframe actually being parsed.
--- drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +1 -1
@@ -44,7 +44,7 @@ static int mwifiex_11n_dispatch_amsdu_pk ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {mwifiex_process_tdls_action_frame (priv, (u8 *)rx_hdr, - skb->len); + rx_skb->len); }if (priv->bss_role ==MWIFIEX_BSS_ROLE_UAP )
Greg Kroah-Hartman
maintainerPATCH 178/337 binfmt_misc: reject a flag character as the field delimiter
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 8e85d50ba1117fd446bf9a250bd8a97d48384bdc upstream.
The registration string starts with a user chosen delimiter that separates the individual fields. So that the field parsers terminate even on a truncated string create_entry() pads the buffer with that same delimiter:
memset(buf + count, del, 8);
Most fields are scanned for the delimiter with strchr()/scanarg() and happily stop on the padding. The flags field is different: instead of scanning for the delimiter check_special_flags() consumes the flag characters 'P', 'O', 'C' and 'F' and stops at the first byte that is none of them, relying on the trailing delimiter to end the scan.
If the delimiter is itself a flag character the padding no longer acts as a terminator. The scan swallows all eight padding bytes and keeps reading past the end of the allocation until it hits a byte that is not a flag character. For example registering
PaPEPPxPPiP
with 'P' as the delimiter (name "a", type extension, magic "x", interpreter "i", empty flags) leaves the flag scan running off the end of the buffer. The registration is rejected in the end because the parser does not stop exactly at buf + count, but only after the out of bounds read has already happened. With an unlucky allocation layout the scan can walk into an unmapped page; under KASAN it is reported as a slab out of bounds read. binfmt_misc mounts are available to unprivileged users in a user namespace so the read is reachable without privileges.
Reject a delimiter that is one of the flag characters up front. Such a registration was always rejected anyway, only after the out of bounds read, so no valid registration string changes meaning.
--- fs/binfmt_misc.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/binfmt_misc.c +4 -0
@@ -375,6 +375,10 @@ static Node *create_entry(const char __upr_debug ("register: delim: %#x {%c}\n" , del, del); +/* A flag-char delimiter runs the flag scan off the buffer. */ +if (del =='P' || del =='O' || del =='C' || del =='F' ) + goto einval; +/* Pad the buffer with the delim to simplify parsing below. */ memset (buf + count, del,8 );
Greg Kroah-Hartman
maintainerPATCH 179/337 binfmt_misc: dont let an F entry pin its own instance
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 79055d82772b9584f259b747fe40ff56a076678d upstream.
An entry registered with 'F' opens its interpreter at registration time and holds that file until the entry is freed. Any entry nobody removes by hand only gets closed once the binfmt_misc superblock is shut down. If the interpreter lives on a mount that keeps that superblock alive the two pin each other:
binfmt_misc sb -> inode -> entry -> interp_file -> vfsmount -> binfmt_misc sb
TL;DR the file is never closed. Once the mount namespace is gone there is nothing left to unregister through either.
There are two ways to trigger this bug:
- Point the interpreter at the instance itself. Its files are regular files owned by the mounter and both bm_get_inode() and simple_fill_super() leave i_op at empty_iops. So notify_change() falls back to simple_setattr() and chmod +x works. We never set SB_I_NOEXEC and so open_exec() accepts it.
- Use the instance as an overlayfs lower layer. The overlay superblock holds a clone_private_mount() of every layer until it is destroyed and that clone is in no namespace. So umount_tree() never reaches it.
That's a DoS. And it isn't only the superblock that leaks. It pins the user namespace it was mounted in, so every iteration permanently eats one of the caller's user namespace charges.
So let's just do the sane thing. SB_I_NOEXEC makes open_exec() fail on the instance's own files and s_stack_depth makes overlayfs reject the layer before it ever takes a clone. That also covers the ecryptfs and fuse passthrough variants. What 'F' promises is unchanged.
The stable tag is narrower than the Fixes tags on purpose. Before sandboxed mounts this needed global root against the single instance everyone shares, and the change doesn't apply to those trees anyway.
Note that SB_I_NODEV is implicitly raised for userns mounts but raise it explicitly here as well.
--- fs/binfmt_misc.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/binfmt_misc.c +4 -0
@@ -986,6 +986,10 @@ static int bm_fill_super(struct super_blif (WARN_ON (user_ns !=current_user_ns ()))return -EINVAL ; +/* Never exec off this instance and never let anything stack on it. */ + sb->s_iflags |=SB_I_NOEXEC |SB_I_NODEV ; + sb->s_stack_depth =FILESYSTEM_MAX_STACK_DEPTH ; +/* * Lazily allocate a new binfmt_misc instance for this namespace, i.e. * do it here during the first mount of binfmt_misc. We don't need to
Greg Kroah-Hartman
maintainerPATCH 180/337 mm/page_reporting: use system_freezable_wq to fix UAF during suspend
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Link Lin <[email protected]>
commit 0b45f6927a14914ff685fe0e6f9d11232a1e03df upstream.
During PM freeze (e.g. S3 suspend or S4 hibernation), device drivers like virtio_balloon reset their underlying virtio devices and delete their virtqueues via vdev->config->del_vqs().
However, page reporting work (page_reporting_process) was scheduled on the global system_wq. Because system_wq lacks the WQ_FREEZABLE flag, the PM freezer skips it, leaving page_reporting_process active during suspend.
If pages are freed into the buddy allocator while suspending (for example, when core MM invokes the balloon shrinker during S4 hibernation image saving), page reporting triggers virtballoon_free_page_report() on deleted virtqueues, resulting in a Use-After-Free / General Protection Fault:
[ 196.795226] general protection fault, probably for non-canonical address 0xaa1436fe70dae6df: 0000 [#1] SMP NOPTI
[ 196.825967] Workqueue: events page_reporting_process
[ 196.831038] RIP: 0010:virtqueue_add_split+0x233/0x4c0 [virtio_ring]
[ 196.927073] virtballoon_free_page_report+0x3a/0xe0 [virtio_balloon]
[ 196.946943] page_reporting_process+0x370/0x4f0Fix this by switching page reporting work to system_freezable_wq. This ensures that the PM freezer pauses page_reporting_process before device drivers destroy their reporting virtqueues. Because the reporting worker is frozen, memory reclamation/freeing (e.g. via shrinker execution) can safely return pages to MM during freeze without triggering unfrozen reporting work on deleted virtqueues.
This aligns with the driver's existing design. The comment in virtballoon_freeze() states:
/*
* The workqueue is already frozen by the PM core before this
* function is called.
*/
Testing:
I have verified these fixes using Google’s virtualization infrastructure by running continuous suspend/resume iterations (40+ cycles) while churning memory using stress-ng (stress-ng --vm 4 --vm-bytes 60% --timeout 1) to constantly create free pages for the buddy allocator. We also set the page_reporting_order parameter to 0 to make the page reporting worker highly sensitive, forcing it to pick up any 4K free pages. This confirmed that the UAF crashes are no longer reproducible.
--- mm/page_reporting.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
mm/page_reporting.c +4 -2
@@ -80,7 +80,8 @@ __page_reporting_request(struct page_rep* now we are limiting this to running no more than once every * couple of seconds. */ -schedule_delayed_work (&prdev->work,PAGE_REPORTING_DELAY ); +queue_delayed_work (system_freezable_wq, &prdev->work, +PAGE_REPORTING_DELAY ); }/* notify prdev of free page reporting request */
@@ -343,7 +344,8 @@ err_out:*/ state =atomic_cmpxchg (&prdev->state, state,PAGE_REPORTING_IDLE );if (state ==PAGE_REPORTING_REQUESTED ) -schedule_delayed_work (&prdev->work,PAGE_REPORTING_DELAY ); +queue_delayed_work (system_freezable_wq, &prdev->work, +PAGE_REPORTING_DELAY ); }static DEFINE_MUTEX (page_reporting_mutex);
Greg Kroah-Hartman
maintainerPATCH 181/337 mm: memcg: initialize *locked in memcg1_oom_prepare() stub
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
commit 1833ce36b35426504c64600c94f322437ea44bb2 upstream.
mem_cgroup_oom() passes an uninitialized "locked" to memcg1_oom_prepare() and reads it back in memcg1_oom_finish():
bool locked, ret; ... if (!memcg1_oom_prepare(memcg, &locked)) return false; ret = mem_cgroup_out_of_memory(memcg, mask, order); memcg1_oom_finish(memcg, locked);
This relies on memcg1_oom_prepare() setting *locked whenever it returns true. The CONFIG_MEMCG_V1=y version does, but the stub used when CONFIG_MEMCG_V1=n returns true without touching *locked, so memcg1_oom_finish() consumes an uninitialized value. On a memcg OOM this is reported by UBSAN:
UBSAN: invalid-load in mm/memcontrol.c:1932:27 load of value 0 is not a valid value for type 'bool' (aka '_Bool')
Initialize *locked to false in the stub; with cgroup v1 compiled out there is no OOM lock to take.
--- mm/memcontrol-v1.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
mm/memcontrol-v1.h +5 -1
@@ -133,7 +133,11 @@ static inline void memcg1_soft_limit_resstatic inline bool memcg1_wait_acct_move (struct mem_cgroup *memcg) {return false; }static inline void memcg1_css_offline (struct mem_cgroup *memcg) {} -static inline bool memcg1_oom_prepare (struct mem_cgroup *memcg,bool *locked) {return true; } +static inline bool memcg1_oom_prepare (struct mem_cgroup *memcg,bool *locked) +{ + *locked = false; +return true; +}static inline void memcg1_oom_finish (struct mem_cgroup *memcg,bool locked) {}static inline void memcg1_oom_recover (struct mem_cgroup *memcg) {}
Greg Kroah-Hartman
maintainerPATCH 182/337 net: bridge: stop fast-leave after deleting a port group
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 a39789f211b8a4125f0c70e05b30cf715f4f187d upstream.
br_multicast_leave_group() iterates mp->ports with pp = &p->next in its fast-leave path. After br_multicast_del_pg() removes p, continuing the loop advances pp through the deleted entry.
If multicast-to-unicast was enabled, the bridge can hold multiple port groups for the same port and group with different source MAC addresses. Once multicast-to-unicast is disabled, br_port_group_equal() matches those entries by port only. A fast leave can then delete one entry and continue from its stale next pointer, leaving mp->ports pointing at a deleted port group.
Fast leave only needs to remove one matching port group. Break after br_multicast_del_pg() so the loop stops before dereferencing the removed entry.
--- net/bridge/br_multicast.c | 1 + 1 file changed, 1 insertion(+)
net/bridge/br_multicast.c +1 -0
@@ -3685,6 +3685,7 @@ br_multicast_leave_group(struct net_brid p->flags |=MDB_PG_FLAGS_FAST_LEAVE ;br_multicast_del_pg (mp, p, pp); +break ; } goto out; }
Greg Kroah-Hartman
maintainerPATCH 183/337 net: ipv6: clear suppressed fib6 rule result
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 6aea62e433fe1b586202a5fee8b5807ce635e1d7 upstream.
fib6_rule_suppress() drops a suppressed route with ip6_rt_put_flags(), but leaves res->rt6 pointing at the released rt6_info.
If no later rule supplies a replacement, fib6_rule_lookup() still sees res.rt6 and returns that stale dst to its caller. A suppressing rule can therefore leak a released route back to rt6_lookup(), and the next put hits rcuref_put_slowpath() from dst_release().
Clear res->rt6 when suppressing the route so suppressed lookups fall through to the null dst instead of reusing the released one.
--- net/ipv6/fib6_rules.c | 1 + 1 file changed, 1 insertion(+)
net/ipv6/fib6_rules.c +1 -0
@@ -302,6 +302,7 @@ INDIRECT_CALLABLE_SCOPE bool fib6_rule_s suppress_route:ip6_rt_put_flags (rt, flags); + res->rt6 =NULL ;return true; }
Greg Kroah-Hartman
maintainerPATCH 184/337 powerpc/ps3: Fix map failure path in dma_ioc0_map_pages()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
commit 0bb024f11d120abff3e8db9144a585b9d7fb8459 upstream.
If lv1_put_iopte() fails in dma_ioc0_map_pages(), the error path decrements iopage but keeps using the failed mapping's offset. As a result, it repeatedly tries to invalidate the failed IOPTE slot and leaves the already installed IOPTEs valid.
Recompute offset and invalidate the installed IOPTEs instead.
--- arch/powerpc/platforms/ps3/mm.c | 1 + 1 file changed, 1 insertion(+)
arch/powerpc/platforms/ps3/mm.c +1 -0
@@ -615,6 +615,7 @@ static int dma_ioc0_map_pages(struct ps3 fail_map:for (iopage--;0 <= iopage; iopage--) { + offset = (1 << r->page_size) * iopage; lv1_put_iopte(0, c->bus_addr + offset, c->lpar_addr + offset,
Greg Kroah-Hartman
maintainerPATCH 185/337 um: vector: fix use-after-free in vector_mmsg_rx()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit af421e9aed3920c7ac88c24daa48606c7112feca upstream.
When vector_mmsg_rx() discards a packet whose overlay header fails verify_header(), it frees the skb and continues the loop:
if (header_check < 0) {
dev_kfree_skb_irq(skb);
vp->estats.rx_encaps_errors++;
continue;
}The normal and short-packet paths fall through to the bottom of the loop body, which clears the consumed slot and advances the cursors:
(*skbuff_vector) = NULL; mmsg_vector++; skbuff_vector++;
The verify_header() < 0 path skips that via continue, so the freed skb is left in skbuff_vector[] and the cursors do not advance. The next iteration reads the same slot, gets the freed skb, and frees it again, producing a refcount underflow / use-after-free in the RX path.
Discard the slot the same way the other paths do before continuing.
Only transports whose verify_header() can return negative are affected:
GRE and L2TPv3 do so on a cookie/session-id mismatch (raw/tap do not), so any peer on such a transport can trigger it without authentication.
--- arch/um/drivers/vector_kern.c | 3 +++ 1 file changed, 3 insertions(+)
arch/um/drivers/vector_kern.c +3 -0
@@ -999,6 +999,9 @@ static int vector_mmsg_rx(struct vector_*/ dev_kfree_skb_irq (skb); vp->estats.rx_encaps_errors++; + (*skbuff_vector) =NULL ; + mmsg_vector++; + skbuff_vector++;continue ; }if (header_check >0 ) {
Greg Kroah-Hartman
maintainerPATCH 186/337 veth: convert frag_list skbs before running XDP
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matt Fleming <[email protected]>
commit d0d6415963040c401e7a7e4e482a698ba52448cb upstream.
A frag_list skb can reach veth with data_len set but nr_frags zero. veth_convert_skb_to_xdp_buff() only converts skbs that are shared, locked, have frags[], or do not have enough headroom. It later uses skb_is_nonlinear() to decide whether to set XDP_FLAGS_HAS_FRAGS and xdp_frags_size.
That exposes frag_list data to XDP as if it were stored in frags[], but frags[] is empty. AF_XDP copy mode can then trust the bogus XDP fragment metadata, walk an empty fragment entry, and crash in memcpy() from __xsk_rcv().
Route non-linear skbs through skb_pp_cow_data() before exposing them to XDP, and only advertise XDP frags when the resulting skb has frags[]. skb_copy_bits() already handles frag_list input, and skb_pp_cow_data() builds frags[] output with skb_add_rx_frag(), which is the representation XDP multi-buffer expects.
--- drivers/net/veth.c | 4 ++-- net/core/skbuff.c | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-)
drivers/net/veth.c +2 -2
@@ -756,7 +756,7 @@ static int veth_convert_skb_to_xdp_buff(u32 frame_sz;if (skb_shared (skb) ||skb_head_is_locked (skb) || -skb_shinfo (skb)->nr_frags || +skb_is_nonlinear (skb) ||skb_headroom (skb) <XDP_PACKET_HEADROOM ) {if (skb_pp_cow_data (rq->page_pool, pskb,XDP_PACKET_HEADROOM )) goto drop;
@@ -771,7 +771,7 @@ static int veth_convert_skb_to_xdp_buff(xdp_prepare_buff (xdp, skb->head,skb_headroom (skb),skb_headlen (skb), true); -if (skb_is_nonlinear (skb)) { +if (skb_shinfo (skb)->nr_frags) {skb_shinfo (skb)->xdp_frags_size = skb->data_len;xdp_buff_set_frags_flag (xdp); }else {
net/core/skbuff.c +12 -6
@@ -925,6 +925,18 @@ static void skb_clone_fraglist(struct skskb_get (list); } +/** +* skb_pp_cow_data() - copy skb data into page-pool backed storage +* @pool: page pool to allocate from +* @pskb: pointer to skb pointer, replaced with the copied skb on success +* @headroom: headroom to reserve in the copied skb +* +* skb_copy_bits() handles both frags[] and frag_list input. If the copied +* skb remains non-linear, it uses frags[], which is the representation used +* by XDP multi-buffer. +* +* Return: 0 on success or a negative errno on failure. +*/ int skb_pp_cow_data (struct page_pool *pool,struct sk_buff **pskb,unsigned int headroom) {
@@ -934,12 +946,6 @@ int skb_pp_cow_data(struct page_pool *point err, i, head_off;void *data; -/* XDP does not support fraglist so we need to linearize -* the skb. -*/ -if (skb_has_frag_list (skb)) -return -EOPNOTSUPP ; - max_head_size =SKB_WITH_OVERHEAD (PAGE_SIZE - headroom);if (skb->len > max_head_size +MAX_SKB_FRAGS *PAGE_SIZE )return -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 187/337 vxlan: re-fetch eth header after route_shortcircuit()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit 1395a676ec15a0a02a2a6d86602324f2d5fd41d5 upstream.
Before route_shortcircuit(), the eth header pointer is cached from eth_hdr(skb).
Inside route_shortcircuit(), pskb_may_pull() can be called, which may reallocate skb->head.
In this case, returning to vxlan_xmit() leaves the cached eth pointer pointing to freed memory, leading to a use-after-free when dereferencing eth->h_dest.
Fix this by updating eth = eth_hdr(skb) after calling route_shortcircuit().
--- drivers/net/vxlan/vxlan_core.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/vxlan/vxlan_core.c +1 -0
@@ -2790,6 +2790,7 @@ static netdev_tx_t vxlan_xmit(struct sk_ (ntohs(eth->h_proto) == ETH_P_IP || ntohs(eth->h_proto) == ETH_P_IPV6)) { did_rsc =route_shortcircuit (dev, skb); + eth =eth_hdr (skb);if (did_rsc) f =vxlan_find_mac_tx (vxlan, eth->h_dest, vni); }
Greg Kroah-Hartman
maintainerPATCH 188/337 vxlan: unclone skb head before modifying eth header in route_shortcircuit()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit 760d36e737f2b3867762f42af36c663f55babcc4 upstream.
When route_shortcircuit() performs L3 short-circuit routing, it modifies the Ethernet header of the skb in-place:
memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, dev->addr_len);
memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len);If the incoming skb is cloned (for example by packet sockets, tcpdump, or dev_queue_xmit), modifying the Ethernet header without uncloning can corrupt the packet header for other readers holding a reference to the cloned skb.
Ensure the skb header is writable and unshared by calling skb_cow_head(skb, 0) prior to updating the Ethernet header. If skb_cow_head() fails, abort short-circuiting and return false to allow standard packet processing fallback.
--- drivers/net/vxlan/vxlan_core.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/vxlan/vxlan_core.c +4 -0
@@ -2190,6 +2190,10 @@ static bool route_shortcircuit(struct ne diff = !ether_addr_equal (eth_hdr (skb)->h_dest, n->ha);if (diff) { +if (skb_cow_head (skb,0 )) { +neigh_release (n); +return false; + }memcpy (eth_hdr (skb)->h_source,eth_hdr (skb)->h_dest, dev->addr_len);memcpy (eth_hdr (skb)->h_dest, n->ha, dev->addr_len);
Greg Kroah-Hartman
maintainerPATCH 189/337 vxlan: use neigh_ha_snapshot() in route_shortcircuit()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit 8eca411347e1d38964f9ed2c8d3b6ab0e7e4473d upstream.
The neighbour hardware address n->ha can be updated asynchronously by the neighbour subsystem, protected by n->ha_lock seqlock. Reading n->ha without holding the seqlock loop can lead to torn reads or reading a partially updated MAC address.
Use neigh_ha_snapshot() in route_shortcircuit() to safely copy n->ha under read_seqbegin()/read_seqretry() lock protection before using it.
Note that arp_reduce() and neigh_reduce() seem to have the same issue left for future patches.
--- drivers/net/vxlan/vxlan_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/net/vxlan/vxlan_core.c +4 -2
@@ -2186,9 +2186,11 @@ static bool route_shortcircuit(struct ne }if (n) { +u8 haddr[ETH_ALEN ];bool diff; - diff = !ether_addr_equal (eth_hdr (skb)->h_dest, n->ha); +neigh_ha_snapshot (haddr, n, dev); + diff = !ether_addr_equal_unaligned (eth_hdr (skb)->h_dest, haddr);if (diff) {if (skb_cow_head (skb,0 )) {neigh_release (n);
@@ -2196,7 +2198,7 @@ static bool route_shortcircuit(struct ne }memcpy (eth_hdr (skb)->h_source,eth_hdr (skb)->h_dest, dev->addr_len); -memcpy (eth_hdr (skb)->h_dest, n->ha, dev->addr_len); +memcpy (eth_hdr (skb)->h_dest, haddr, dev->addr_len); }neigh_release (n);return diff;
Greg Kroah-Hartman
maintainerPATCH 190/337 vxlan: use pskb_network_may_pull() in route_shortcircuit()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit 26bb2dd0a8839617e2c79ffbbe1923f8e4bab9fb upstream.
route_shortcircuit() currently calls pskb_may_pull(skb, sizeof(struct iphdr)) (or ipv6hdr), which checks if bytes are available starting from skb->data.
However, in vxlan_xmit(), skb->data points to the MAC header, so skb_network_offset(skb) is ETH_HLEN (14 bytes). Using pskb_may_pull(skb, 20) only checks 20 bytes from skb->data (which is 14 bytes MAC header + 6 bytes of IP header), leaving the rest of the IP header potentially un-pulled in non-linear frags. Subsequent dereferences of ip_hdr(skb)->daddr can read beyond the pulled linear buffer length.
Fix this by using pskb_network_may_pull(), which adds skb_network_offset(skb) to the length check to ensure the full network header is present in the linear buffer.
--- drivers/net/vxlan/vxlan_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/vxlan/vxlan_core.c +2 -2
@@ -2138,7 +2138,7 @@ static bool route_shortcircuit(struct ne {struct iphdr *pip; -if (!pskb_may_pull (skb,sizeof (struct iphdr ))) +if (!pskb_network_may_pull (skb,sizeof (struct iphdr )))return false; pip =ip_hdr (skb); n =neigh_lookup (&arp_tbl, &pip->daddr, dev);
@@ -2164,7 +2164,7 @@ static bool route_shortcircuit(struct ne*/ if (!ipv6_stub->nd_tbl)return false; -if (!pskb_may_pull (skb,sizeof (struct ipv6hdr ))) +if (!pskb_network_may_pull (skb,sizeof (struct ipv6hdr )))return false; pip6 =ipv6_hdr (skb); n =neigh_lookup (ipv6_stub->nd_tbl, &pip6->daddr, dev);
Greg Kroah-Hartman
maintainerPATCH 191/337 ublk: reset kernel-owned dev_info fields in ublk_ctrl_add_dev()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ming Lei <[email protected]>
commit e65848e4ce352bac9e3465099354c8b8f845391f upstream.
ublk_ctrl_add_dev() memcpy()s the userspace ublksrv_ctrl_dev_info into ub->dev_info and then fixes up the fields the driver owns, but misses ->state and ->ublksrv_pid.
A device added with ->state = UBLK_S_DEV_LIVE passes the "->state != UBLK_S_DEV_DEAD" test that ublk_stop_dev_unlocked() uses as its proxy for "a disk is attached", while ->ub_disk is still NULL, so DEL_DEV right after ADD_DEV oopses in del_gendisk(). UBLK_S_DEV_QUIESCED plus UBLK_F_USER_RECOVERY dies one step earlier, in ublk_force_abort_dev(). A poisoned ->state also gets START_USER_RECOVERY and the char device read/write path onto a device that was never started, and wedges START_DEV at -EEXIST. A poisoned ->ublksrv_pid just makes GET_DEV_INFO report an unrelated task as the ublk server.
Reset both after the memcpy(), as ublk_detach_disk() does. Userspace only ever reads these back, so correcting them silently breaks nothing.
ADD_DEV has copied ->state in unsanitized since ublk was merged, but back then it was harmless: the gendisk was allocated during ADD_DEV, and both teardown and the START_DEV -EEXIST check keyed off disk_live() rather than ->state. The oops became reachable once the disk allocation moved to START_DEV and those checks switched to ->state.
--- drivers/block/ublk_drv.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/block/ublk_drv.c +9 -0
@@ -2559,6 +2559,15 @@ static int ublk_ctrl_add_dev(struct io_u ub->dev_info.dev_id = ub->ub_number;/* +* ->state and ->ublksrv_pid are owned by the driver and only read back +* by userspace, but they come from the copied-in dev_info, so reset +* them. Otherwise a device added with ->state != DEAD looks live while +* ->ub_disk is still NULL. +*/ + ub->dev_info.state =UBLK_S_DEV_DEAD ; + ub->dev_info.ublksrv_pid =-1 ; + +/* * 64bit flags will be copied back to userspace as feature * negotiation result, so have to clear flags which driver * doesn't support yet, then userspace can get correct flags
Greg Kroah-Hartman
maintainerPATCH 192/337 tracing: Check return value of __register_event() in trace_module_add_events()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit ac8719969e6c3c54e939834df812bc41f25453cf upstream.
trace_module_add_events() ignores the return value of __register_event() and unconditionally calls __add_event_to_tracers() for each event.
If __register_event() fails (for example, if event_init() fails), the trace_event_call is not added to ftrace_events list, but __add_event_to_tracers() still creates a trace_event_file pointing to it. If module loading subsequently fails and module memory is freed, tracing state retains a stale trace_event_call pointer in trace_event_file, leading to a use-after-free when tracefs or tracing subsystem operations are later executed.
Fix this by checking the return value of __register_event() and only calling __add_event_to_tracers() if event registration succeeded.
--- kernel/trace/trace_events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/trace/trace_events.c +2 -2
@@ -3405,8 +3405,8 @@ static void trace_module_add_events(stru end = mod->trace_events + mod->num_trace_events;for_each_event (call, start, end) { -__register_event (*call, mod); -__add_event_to_tracers (*call); +if (!__register_event (*call, mod)) +__add_event_to_tracers (*call); } }
Greg Kroah-Hartman
maintainerPATCH 193/337 tracing/filters: Fix false positive match in regex_match_full()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit c22c7b735f9810ad276014f788f9aa5c879ec238 upstream.
regex_match_full() calls strncmp(str, r->pattern, len) where len is the target field buffer size. When len is smaller than r->len (the filter pattern length), strncmp() checks only len bytes of r->pattern against str. If those len bytes match, strncmp() returns 0, resulting in a false-positive match where a shorter string in a fixed-size field matches a longer filter pattern.
For example, a 4-byte static string field containing "abcd" matched the filter pattern "abcdefgh" because strncmp("abcd", "abcdefgh", 4) returned 0. In this case, @len does NOT include '\0' because it is fixed-size array.
Fix this by returning 0 (no match) early when len < r->len.
--- kernel/trace/trace_events_filter.c | 3 +++ 1 file changed, 3 insertions(+)
kernel/trace/trace_events_filter.c +3 -0
@@ -1027,6 +1027,9 @@ static int regex_match_full(char *str, sif (!len)return strcmp (str, r->pattern) ==0 ; +if (len < r->len) +return 0 ; +return strncmp (str, r->pattern, len) ==0 ; }
Greg Kroah-Hartman
maintainerPATCH 194/337 spi: qcom-qspi: Correct max DMA length to avoid 64K boundary failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vijaya Krishna Nivarthi <[email protected]>
commit 90ef2f2961c2dc55957dafe2f53b3efdb4675efc upstream.
The maximum size for a DMA data descriptor is 64KB-1 because the size field in HW is 16 bits wide. For this reason, transfers fail at 64KB and beyond.
Lower max_dma_len to 60KB so larger transfers are split into multiple DMA blocks and do not hit the failing 64KB boundary. 60KB is chosen as a safe round number below the 64KB-1 hardware limit while satisfying alignment requirements.
Tested on x1e80100 (Hamoa) with SPI-NOR flash (/dev/mtd0):
Without patch:
dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=32768 count=2 # works dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=65536 count=1 # fails
With patch:
dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=65536 count=1 # works
--- drivers/spi/spi-qcom-qspi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/spi/spi-qcom-qspi.c +2 -1
@@ -759,7 +759,8 @@ static int qcom_qspi_probe(struct platforeturn dev_err_probe (dev, ret,"could not set DMA mask\n" ); host->max_speed_hz =300000000 ; - host->max_dma_len =65536 ;/* as per HPG */ +/* as per HPG, it is 64KB, limit to 60KB to avoid boundary condition failures */ + host->max_dma_len =0xf000 ; host->dma_alignment =QSPI_ALIGN_REQ ; host->num_chipselect =QSPI_NUM_CS ; host->bus_num =-1 ;
Greg Kroah-Hartman
maintainerPATCH 195/337 selftests/mm: fix potential wild pointer access of getline due to missing init
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Gellermann <[email protected]>
commit 9f1d75a4ce04095afdb63d8e540092ff8151dacf upstream.
This is another occurrence of using getline where the code assumes that getline allocates memory to store the line, but the pointer passed to it is uninitialized and potentially a non-null pointer. This violates the Open Group Spec[1] and caused a segfault in a similar situation in selftest/clone3/clone3_set_tid. Fix it by initializing the line pointer to NULL.
The issue has been found by simply grepping through the selftest code after running into the issue in clone3_set_tid. Whether it segfaults in its current state is unknown to me. But it's good to be addressed due to defensive reasons.
--- tools/testing/selftests/mm/mlock-random-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/mm/mlock-random-test.c +1 -1
@@ -84,7 +84,7 @@ int get_proc_locked_vm_size(void)int get_proc_page_size (unsigned long addr) {FILE *smaps; -char *line; +char *line =NULL ;unsigned long mmupage_size =0 ;size_t size;
Greg Kroah-Hartman
maintainerPATCH 196/337 selftests/clone3: fix wild pointer access of getline due to missing init
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Gellermann <[email protected]>
commit 8f6f9fd93cd7a5dd607ad5cd910476dd68fff3ed upstream.
Patch series "selftests: Add missing initalization of pointer passed to getline", v2.
This patch (of 2):
Clone3_set_tid uses getline(&line, ...) in a loop to read the child's process status. The code expects that getline allocates the buffer for the line on the first loop iteration. According to the Open Group Spec[1], char *line has to be null pointer for this:
Quoted 3 lines
ssize_tgetline(char **restrict lineptr, ...);
If *lineptr is a null pointer or if the object pointed to by *lineptr is of insufficient size, an object shall be allocated as if by
malloc()
Quoted 1 line
or the object shall be reallocated as if by realloc()[...].
However, char *line is only declared, leading to an undefined value that is potentially non-null. In an example run with Musl v1.2.6, the realloc call[2] of getdelim, which implements getline, triggers a segfault:
./run_kselftest.sh --test clone3:clone3_set_tid
[ 1366.165898] kselftest: Running tests in clone3 ... [ 1367.799244] clone3_set_tid[811]: unhandled signal 11 code 0x1 at 0x0000000000000000 in libc.so[68184,3fbf69f000+4c000] [ 1367.802808] CPU: 0 UID: 0 PID: 811 Comm: clone3_set_tid Not tainted .. [ 1367.804188] epc: 0x0000003fbf6b0184 [ 1367.804188] ra : 0x0000003fbf6d4664 [ 1367.804188] sp : 0x0000003fce5f2e40 [ 1367.805314] gp : 0x0000002aaab0dfb8 [ 1367.805314] tp : 0x0000003fbf6f14a8 [ 1367.805314] t0 : 0x0000003fbf63d000
...
Looking at the realloc implementation, Musl mallocs for a null pointer memory. But for a non-null pointer, it assumes it's passed a valid pointer to the heap and tries to access its meta-data. This leads to the segfault we see:
void *realloc(void *p, size_t n)
{
if (!p) return malloc(n);
if (size_overflows(n)) return 0;
struct meta *g = get_meta(p);
...
}Fix this by properly initializing the line pointer to NULL.
--- tools/testing/selftests/clone3/clone3_set_tid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/clone3/clone3_set_tid.c +1 -1
@@ -141,7 +141,7 @@ int main(int argc, char *argv[]) {FILE *f;char buf; -char *line; +char *line =NULL ;int status;int ret =-1 ;size_t len =0 ;
Greg Kroah-Hartman
maintainerPATCH 197/337 scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Hashimov <[email protected]>
commit 93dde0bf2f39a0f9f57fd610aa3201ce5b753433 upstream.
resp_report_zones() sizes the reply buffer from the CDB allocation length. The v3 fix rounds alloc_len up with ALIGN() before deriving the descriptor count:
rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >> ilog2(RZONES_DESC_HD); arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1);
For alloc_len in 0xFFFFFFC1..0xFFFFFFFF, ALIGN() rounds up to 0x100000000, so arr_len is 4 GB. On 32-bit, kzalloc()'s size_t is 32-bit and truncates 0x100000000 to 0; kzalloc(0) returns ZERO_SIZE_PTR, which passes the !arr check, and desc = arr + 64 is then dereferenced in the loop -> out-of-bounds write / panic.
Clamp rep_max_zones to devip->nr_zones. The loop already stops at sdebug_capacity (after nr_zones zones), so a report can never hold more than nr_zones descriptors; the clamp does not change the report, it only bounds arr_len to (nr_zones + 1) * RZONES_DESC_HD, a real device property that can never reach 0x100000000.
--- drivers/scsi/scsi_debug.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/scsi/scsi_debug.c +6 -2
@@ -5191,6 +5191,7 @@ static int resp_report_zones(struct scsiu32 alloc_len, rep_opts, rep_len;bool partial;u64 lba, zs_lba; +u64 arr_len;u8 *arr =NULL , *desc;u8 *cmd = scp->cmnd;struct sdeb_zone_state *zsp =NULL ;
@@ -5212,9 +5213,12 @@ static int resp_report_zones(struct scsireturn check_condition_result; } - rep_max_zones = (alloc_len -64 ) >>ilog2 (RZONES_DESC_HD ); + rep_max_zones = (ALIGN ((u64 )alloc_len,RZONES_DESC_HD ) -RZONES_DESC_HD ) >> +ilog2 (RZONES_DESC_HD ); + rep_max_zones = min_t(unsigned int, rep_max_zones, devip->nr_zones); + arr_len = (u64 )RZONES_DESC_HD * (rep_max_zones +1 ); - arr =kzalloc (alloc_len,GFP_ATOMIC | __GFP_NOWARN); + arr =kzalloc (arr_len,GFP_ATOMIC | __GFP_NOWARN);if (!arr) {mk_sense_buffer (scp,ILLEGAL_REQUEST ,INSUFF_RES_ASC ,INSUFF_RES_ASCQ );
Greg Kroah-Hartman
maintainerPATCH 198/337 sctp: reject stale cookies with mismatched verification tags
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 9d8da8e0a9bce4a340af60dd0446bc7eb8d07587 upstream.
sctp_unpack_cookie() skips cookie expiration checks whenever an association already exists. This is broader than the exception in RFC 9260 Section 5.2.4.
For an existing association, Section 5.2.4 permits an expired State Cookie only when both Verification Tags in the cookie match the current association. Otherwise, the packet SHOULD be discarded and a Stale Cookie ERROR MUST be sent.
The broad check lets an expired Action A restart cookie reach sctp_sf_do_dupcook_a(). In a runtime test with the default 60 second cookie lifetime, replaying such a cookie after 65 seconds returned a COOKIE-ACK and restarted the association.
Check cookie expiration unless both Verification Tags match. This preserves the Action D exception for a lost COOKIE ACK while rejecting expired cookies in all other cases.
--- net/sctp/sm_make_chunk.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
net/sctp/sm_make_chunk.c +7 -4
@@ -1821,9 +1821,9 @@ no_hmac: goto fail; } -/* Check to see if the cookie is stale. If there is already -* an association, there is no need to check cookie's expiration -* for init collision case of lost COOKIE ACK. +/* Check to see if the cookie is stale. RFC 9260 Section 5.2.4 +* exempts an expired cookie only when both Verification Tags match +* the current association. * If skb has been timestamped, then use the stamp, otherwise * use current time. This introduces a small possibility that * a cookie may be considered expired, but this would only slow
@@ -1834,7 +1834,10 @@ no_hmac:else kt =ktime_get_real (); -if (!asoc &&ktime_before (bear_cookie->expiration, kt)) { +if ((!asoc || + asoc->c.my_vtag != bear_cookie->my_vtag || + asoc->c.peer_vtag != bear_cookie->peer_vtag) && +ktime_before (bear_cookie->expiration, kt)) {suseconds_t usecs =ktime_to_us (ktime_sub (kt, bear_cookie->expiration));__be32 n =htonl (usecs);
Greg Kroah-Hartman
maintainerPATCH 199/337 sctp: prevent peer transport count overflow
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Asim Viladi Oglu Manizada <[email protected]>
commit bd0e9289e2642f6a5c54faad304ce0f41e926d22 upstream.
sctp_assoc_add_peer() increments the association's 16-bit transport_count for every new unique peer. Adding the 65,536th transport wraps the count to zero.
SCTP sock_diag uses transport_count to reserve the INET_DIAG_PEERS payload, then copies one sockaddr_storage for every entry in transport_addr_list. After the wrap, a diagnostic dump reserves an empty payload and writes 8 MiB of peer addresses past the skb tail.
Reject a new unique peer when transport_count has reached U16_MAX. Perform the check after the existing-peer lookup so a duplicate address continues to return its existing transport at the limit.
--- net/sctp/associola.c | 3 +++ 1 file changed, 3 insertions(+)
net/sctp/associola.c +3 -0
@@ -614,6 +614,9 @@ struct sctp_transport *sctp_assoc_add_pereturn peer; } +if (asoc->peer.transport_count ==U16_MAX ) +return NULL ; + peer =sctp_transport_new (asoc->base.net, addr, gfp);if (!peer)return NULL ;
Greg Kroah-Hartman
maintainerPATCH 200/337 hwmon: (npcm750-pwm-fan): stop fan timer on device detach
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongyan Xu <[email protected]>
commit f27f6976ea269219c1259a7c2f8c6dfe782540a3 upstream.
When a fan tach channel is present, npcm7xx_pwm_fan_probe() starts fan_timer. The timer callback polls tach state and rearms the timer, but the driver has no remove callback or devm cleanup action to stop it. On device detach, the devm-managed driver data and I/O mappings can be released while the timer is still pending or running.
Register a devm cleanup action before starting the timer and shut the timer down synchronously from that action.
This issue was found by a static analysis tool.
--- drivers/hwmon/npcm750-pwm-fan.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/hwmon/npcm750-pwm-fan.c +11 -0
@@ -364,6 +364,11 @@ static void npcm7xx_fan_polling(struct tadd_timer (&data->fan_timer); } +static void npcm7xx_fan_cleanup (void *timer) +{ +timer_shutdown_sync (timer); +} +static inline void npcm7xx_fan_compute (struct npcm7xx_pwm_fan_data *data,u8 fan,u8 cmp,u8 fan_id,u8 flag_int,u8 flag_mode,u8 flag_clear)
@@ -1027,6 +1032,12 @@ static int npcm7xx_pwm_fan_probe(structmsecs_to_jiffies (NPCM7XX_FAN_POLL_TIMER_200MS );timer_setup (&data->fan_timer, npcm7xx_fan_polling,0 ); + ret =devm_add_action_or_reset (dev, + npcm7xx_fan_cleanup, + &data->fan_timer); +if (ret) +return ret; +add_timer (&data->fan_timer);break ; }
Greg Kroah-Hartman
maintainerPATCH 201/337 hwmon: (pmbus/core) notify on the hwmon device, not the i2c client
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Jardin <[email protected]>
commit a64a7e8a0b012ba81b0eadbd7afc84ab0dbfd70c upstream.
pmbus_notify() calls sysfs_notify() and kobject_uevent() on the i2c client's kobject, but the alarm attributes live on the hwmon class device registered by pmbus_do_probe(). Notifying the parent i2c device is a no-op for both poll(POLLPRI) waiters and udev listeners: the named attribute does not exist on that kobject.
Notify the hwmon device instead, so poll() wakes up and "change" uevents fire on the inX_alarm/tempX_alarm attributes when SMBALERT# reports a fault.
--- drivers/hwmon/pmbus/pmbus_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/hwmon/pmbus/pmbus_core.c +3 -2
@@ -2930,8 +2930,9 @@ static void pmbus_notify(struct pmbus_daif (reg == sreg && page == spage && (smask & flags)) {dev_dbg (data->dev,"sysfs notify: %s" , da->attr.name); -sysfs_notify (&data->dev->kobj,NULL , da->attr.name); -kobject_uevent (&data->dev->kobj,KOBJ_CHANGE ); +sysfs_notify (&data->hwmon_dev->kobj,NULL , + da->attr.name); +kobject_uevent (&data->hwmon_dev->kobj,KOBJ_CHANGE ); flags &= ~smask; }
Greg Kroah-Hartman
maintainerPATCH 202/337 i2c: amd-mp2: Unregister callback on adapter add failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 82048795242f04275a3f49ffc66ad851b6120954 upstream.
amd_mp2_register_cb() stores the platform I2C context in the MP2 PCI driver's callback table before the adapter is registered. If i2c_add_adapter() fails, probe returns and devres frees the context, but the PCI driver can still dereference the stale pointer from its IRQ and system-sleep callbacks.
Unregister the callback before returning the adapter registration error.
--- drivers/i2c/busses/i2c-amd-mp2-plat.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-amd-mp2-plat.c +3 -1
@@ -316,8 +316,10 @@ static int i2c_amd_probe(struct platformamd_mp2_pm_runtime_put (mp2_dev); -if (ret <0 ) +if (ret <0 ) {dev_err (&pdev->dev,"i2c add adapter failed = %d\n" , ret); +amd_mp2_unregister_cb (&i2c_dev->common); + }return ret; }
Greg Kroah-Hartman
maintainerPATCH 203/337 gpio: pca953x: fix cache_only and IRQ state on restore_context() failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: bui duc phuc <[email protected]>
commit d233087c19f6607ef926ac3f47d776e2406ffd1f upstream.
When pca953x_restore_context() fails, cache_only is left disabled and the IRQ left enabled, even though register synchronization may not have completed successfully. Restore cache_only and disable the IRQ again on failure, matching the state set by pca953x_save_context().
--- drivers/gpio/gpio-pca953x.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
drivers/gpio/gpio-pca953x.c +13 -2
@@ -1256,9 +1256,20 @@ static int pca953x_restore_context(strucregcache_mark_dirty (chip->regmap); ret =pca953x_regcache_sync (chip);if (ret) -return ret; + goto err; -return regcache_sync (chip->regmap); + ret =regcache_sync (chip->regmap); +if (ret) + goto err; + +return 0 ; + +err: +if (chip->client->irq >0 ) +disable_irq (chip->client->irq); +regcache_cache_only (chip->regmap, true); + +return ret; }static void pca953x_save_context (struct pca953x_chip *chip)
Greg Kroah-Hartman
maintainerPATCH 204/337 cpufreq: powernow-k8: Fix possible memory leak in powernowk8_cpu_init()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit d5f8e5f6040d052d44fcbf4f31dd35145c0c8d7d upstream.
The memory allocated for data->powernow_table inside powernow_k8_cpu_init_acpi() or find_psb_table() is not freed in one of the error paths in powernowk8_cpu_init(). Fix that by adding a kfree().
--- drivers/cpufreq/powernow-k8.c | 1 + 1 file changed, 1 insertion(+)
drivers/cpufreq/powernow-k8.c +1 -0
@@ -1083,6 +1083,7 @@ static int powernowk8_cpu_init(struct cp err_out_exit_acpi:powernow_k8_cpu_exit_acpi (data); +kfree (data->powernow_table); err_out:kfree (data);
Greg Kroah-Hartman
maintainerPATCH 205/337 cpufreq: schedutil: Publish util hooks only after all sg_cpu are initialized
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhongqiu Han <[email protected]>
commit f0a3f042293a8c5a2152346b3637ea60866c503a upstream.
Commit 16a03c71bba0 ("cpufreq: schedutil: Merge initialization code of sg_cpu in single loop") merged the per-CPU initialization and the utilization-hook registration into a single loop in sugov_start().
For a shared cpufreq policy this re-introduces the race originally fixed by commit ab2f7cf141aa ("cpufreq: schedutil: Fix sugov_start() versus sugov_update_shared() race").
The scheduler's util path reaches the hook under RCU-sched and never takes policy->rwsem, so the rwsem held across sugov_start() cannot serialize the two. Once the first CPU's hook is published, sugov_update_shared() may run and, via sugov_next_freq_shared(), read/write each sibling sugov_cpu (iowait_boost, util, bw_min, ...) concurrently with the memset() still initializing them, with no lock common to both sides: the update side holds sg_policy->update_lock while the init side holds only policy->rwsem, which the scheduler's util path never takes.
The walk only accesses scalar members, never a pointer like ->sg_policy, so it does not crash today; it merely uses stale (or zero on first start) values that skew the frequency selection and tracepoints. It is still a genuine data race, and a latent crash once any pointer member is dereferenced there.
Restore the two-phase approach: initialize all per-CPU structures first, and only then publish the per-CPU utilization update hooks.
--- kernel/sched/cpufreq_schedutil.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
kernel/sched/cpufreq_schedutil.c +11 -0
@@ -880,8 +880,19 @@ static int sugov_start(struct cpufreq_pomemset (sg_cpu,0 ,sizeof (*sg_cpu)); sg_cpu->cpu = cpu; sg_cpu->sg_policy = sg_policy; + } + +/* +* Publish the hooks only after all per-CPU data is initialized, so a +* shared policy's sugov_update_shared() never reads an uninitialized +* sibling sugov_cpu. +*/ +for_each_cpu (cpu, policy->cpus) { +struct sugov_cpu *sg_cpu = &per_cpu (sugov_cpu, cpu); +cpufreq_add_update_util_hook (cpu, &sg_cpu->update_util, uu); } +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 206/337 power: supply: bq25890: fix the -10 C NTC lookup entry
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 160a783aa65b74782bc17cb874af1a6d3f5fba3c upstream.
The TSPCT lookup table is monotonically decreasing except for ADC code 121, where the sequence reads -9.0 C, -1.0 C, -12.0 C. This makes the reported battery temperature jump upward by eight degrees for one code and then downward by eleven degrees for the next code.
The entry is a missing zero: use -10.0 C so the sequence remains monotonic between -9.0 C and -12.0 C.
--- drivers/power/supply/bq25890_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/power/supply/bq25890_charger.c +1 -1
@@ -320,7 +320,7 @@ static const u32 bq25890_tspct_tbl[] = {145 ,140 ,130 ,120 ,115 ,110 ,100 ,90 ,80 ,70 ,60 ,50 ,40 ,30 ,20 ,10 ,0 ,-10 ,-20 ,-30 ,-40 ,-60 ,-70 ,-80 , - -90, -10, -120, -140, -150, -170, -190, -210, + -90, -100, -120, -140, -150, -170, -190, -210, };#define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl)
Greg Kroah-Hartman
maintainerPATCH 207/337 power: supply: max17040: handle missing status supplier
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jianing Li <[email protected]>
commit 725668c6b6aa3971fe850659102c250d0d676e18 upstream.
MAX17040 does not report charger state itself, so the driver forwards POWER_SUPPLY_PROP_STATUS to a supplier power supply. If no supplier is registered, power_supply_get_property_from_supplier() returns -ENODEV and leaves the output value untouched.
max17040_get_property() currently ignores that error and returns success, so userspace can read an uninitialized status value from the battery power supply. This happens on systems that use the fuel gauge without a charger supplier relationship in firmware.
Return POWER_SUPPLY_STATUS_UNKNOWN when no supplier provides STATUS, and propagate other supplier lookup errors.
--- drivers/power/supply/max17040_battery.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/power/supply/max17040_battery.c +5 -1
@@ -405,7 +405,11 @@ static int max17040_get_property(struct val->intval = chip->low_soc_alert;break ;case POWER_SUPPLY_PROP_STATUS : -power_supply_get_property_from_supplier (psy, psp, val); + ret =power_supply_get_property_from_supplier (psy, psp, val); +if (ret == -ENODEV ) + val->intval =POWER_SUPPLY_STATUS_UNKNOWN ; +else if (ret) +return ret;break ;case POWER_SUPPLY_PROP_TEMP :if (!chip->channel_temp)
Greg Kroah-Hartman
maintainerPATCH 208/337 s390/pci: Fix s390_pci_mmio_write syscall error return without MIO
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Niklas Schnelle <[email protected]>
commit b7ab86bdc65eadcfc43a0e3faf682a3f750cfb96 upstream.
On a machine without PCI memory-I/O (MIO) support or when running with pci=nomio the s390 specific PCI MMIO write syscall checks if the MMIO cookie is above ZPCI_IOMAP_ADDR_BASE as a sanity check before even trying to perform the MMIO. If this check fails the return value was left unchanged and thus 0 from prior operations falsely indicating success. This could potentially confuse user-space into falsely believing the MMIO, on a mapping not valid for MMIO was successful.
Fix this by setting the return value to -EFAULT prior to the check following the same pattern as elsewhere in the same function.
--- arch/s390/pci/pci_mmio.c | 1 + 1 file changed, 1 insertion(+)
arch/s390/pci/pci_mmio.c +1 -0
@@ -178,6 +178,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_write, uns goto out_unlock_mmap; } + ret = -EFAULT ; io_addr = (void __iomem *)((args.pfn <<PAGE_SHIFT ) | (mmio_addr & ~PAGE_MASK ));
Greg Kroah-Hartman
maintainerPATCH 209/337 s390/qeth: Check CAP_NET_ADMIN for private ioctls
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aswin Karuvally <[email protected]>
commit d211028bac1bd0fff0026bfa2a8328e5b78cd0e6 upstream.
Gate the SIOCDEVPRIVATE ioctl commands SIOC_QETH_ADP_SET_SNMP_CONTROL, SIOC_QETH_GET_CARD_TYPE and SIOC_QETH_QUERY_OAT with CAP_NET_ADMIN capable check to ensure unprivileged users cannot invoke them.
--- drivers/s390/net/qeth_core_main.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/s390/net/qeth_core_main.c +3 -0
@@ -6526,6 +6526,9 @@ int qeth_siocdevprivate(struct net_devicstruct qeth_card *card = dev->ml_priv;int rc =0 ; +if (!capable (CAP_NET_ADMIN )) +return -EPERM ; +switch (cmd) {case SIOC_QETH_ADP_SET_SNMP_CONTROL : rc =qeth_snmp_command (card, data);
Greg Kroah-Hartman
maintainerPATCH 210/337 s390/dasd: Fix potential NULL pointer dereference
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Höppner <[email protected]>
commit 9973026f572db6b67570cadc30942f3014e41079 upstream.
dasd_release_space() checks the implementation of the is_ese() discipline function before calling it to determine if a given device is an ESE DASD.
The current usage of the logical AND operator will lead to a NULL pointer dereference as the function is called even if the function pointer is NULL.
Fix this by using the logical OR operator.
--- drivers/s390/block/dasd_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/s390/block/dasd_ioctl.c +1 -1
@@ -324,7 +324,7 @@ out_err:static int dasd_release_space (struct dasd_device *device,struct format_data_t *rdata) { -if (!device->discipline->is_ese && !device->discipline->is_ese (device)) +if (!device->discipline->is_ese || !device->discipline->is_ese (device))return -ENOTSUPP ;if (!device->discipline->release_space)return -ENOTSUPP ;
Greg Kroah-Hartman
maintainerPATCH 211/337 s390/dasd: Fix undersized format-check buffer
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Haberland <[email protected]>
commit 7f40b346462f563a0d6e841a77b5163d2a882a04 upstream.
fmt_buffer_size in dasd_eckd_check_device_format() is declared as int, even though one of the multiplicands, sizeof(struct eckd_count), is a size_t. The expression
trkcount * rpt_max * sizeof(struct eckd_count)
is therefore correctly evaluated at 64-bit width, but the result is silently truncated when it is stored back into the 32-bit fmt_buffer_size variable. For a sufficiently large track range (start_unit/stop_unit are caller-controlled) this truncation yields a buffer size far smaller than the number of tracks actually requested. kzalloc() then succeeds with an undersized allocation, while the subsequent channel program build still operates on the untruncated track count and writes past the end of that buffer.
Compute the buffer size with check_mul_overflow() and keep it in a size_t, so that a value that no longer fits results in -EINVAL instead of a silently truncated allocation size.
--- drivers/s390/block/dasd_eckd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/s390/block/dasd_eckd.c +7 -4
@@ -21,6 +21,7 @@#include <linux/seq_file.h> #include <linux/uaccess.h> #include <linux/io.h> +#include <linux/overflow.h> #include <asm/css_chars.h> #include <asm/debug.h>
@@ -3475,11 +3476,11 @@ static int dasd_eckd_check_device_format {struct dasd_eckd_private *private = base->private;struct eckd_count *fmt_buffer; -struct irb irb; +size_t fmt_buffer_size; +unsigned int trkcount;int rpt_max, rpt_exp; -int fmt_buffer_size; +struct irb irb;int trk_per_cyl; -int trkcount;int tpm =0 ;int rc;
@@ -3490,7 +3491,9 @@ static int dasd_eckd_check_device_format rpt_exp =recs_per_track (&private->rdc_data,0 , cdata->expect.blksize); trkcount = cdata->expect.stop_unit - cdata->expect.start_unit +1 ; - fmt_buffer_size = trkcount * rpt_max *sizeof (struct eckd_count ); +if (check_mul_overflow (trkcount, rpt_max, &fmt_buffer_size) || +check_mul_overflow (fmt_buffer_size,sizeof (struct eckd_count ), &fmt_buffer_size)) +return -EINVAL ; fmt_buffer =kzalloc (fmt_buffer_size,GFP_KERNEL |GFP_DMA );if (!fmt_buffer)
Greg Kroah-Hartman
maintainerPATCH 212/337 s390/zcrypt: Fix wrong domain value verification with EP11 CPRBs
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harald Freudenberger <[email protected]>
commit 983279d7f86ade73db86f886e09172dd567031b5 upstream.
There is a wrong upper limit check for the domain value when an EP11 CPRB is processed for sending to a crypto card. This check is only active on custom device nodes but may lead to access heap memory behind perms->adm when an administrative CPRB is sent. Add correct limit (AP_DOMAINS = 256) checking to fix this.
--- drivers/s390/crypto/zcrypt_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/s390/crypto/zcrypt_api.c +1 -1
@@ -1071,7 +1071,7 @@ static long _zcrypt_send_ep11_cprb(boolprint_hex_dump_debug ("ep11req: " ,DUMP_PREFIX_ADDRESS ,16 ,1 , ap_msg.msg, ap_msg.len, false); -if (perms != &ap_perms && domain <AUTOSEL_DOM ) { +if (perms != &ap_perms && domain <AP_DOMAINS ) {if (ap_msg.flags &AP_MSG_FLAG_ADMIN ) {if (!test_bit_inv (domain, perms->adm)) { rc = -ENODEV ;
Greg Kroah-Hartman
maintainerPATCH 213/337 s390/zcrypt: Validate length for CCA AES cipher key requests
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Holger Dengler <[email protected]>
commit 06afe425d5283b9764303de47f554da5a808ce8a upstream.
cca_cipher2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block.
--- drivers/s390/crypto/zcrypt_ccamisc.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/s390/crypto/zcrypt_ccamisc.c +3 -0
@@ -1218,6 +1218,9 @@ int cca_cipher2protkey(u16 cardnr, u16 d }__packed * prepparm;int keytoklen = ((struct cipherkeytoken *)ckey)->len; +if (keytoklen >PARMBSIZE -sizeof (struct aureqparm )) +return -EINVAL ; +/* get already prepared memory for 2 cprbs with param block each */ rc =alloc_and_prep_cprbmem (PARMBSIZE , &mem, &preqcblk, &prepcblk);if (rc)
Greg Kroah-Hartman
maintainerPATCH 214/337 s390/zcrypt: Validate length for CCA ECC private key requests
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Holger Dengler <[email protected]>
commit a9ae0f6dd45c3ccc1d69363f7aea8af179122730 upstream.
cca_ecc2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block.
--- drivers/s390/crypto/zcrypt_ccamisc.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/s390/crypto/zcrypt_ccamisc.c +3 -0
@@ -1384,6 +1384,9 @@ int cca_ecc2protkey(u16 cardnr, u16 doma }__packed * prepparm;int keylen = ((struct eccprivkeytoken *)key)->len; +if (keylen >PARMBSIZE -sizeof (struct aureqparm )) +return -EINVAL ; +/* get already prepared memory for 2 cprbs with param block each */ rc =alloc_and_prep_cprbmem (PARMBSIZE , &mem, &preqcblk, &prepcblk);if (rc)
Greg Kroah-Hartman
maintainerPATCH 215/337 phy: zynqmp: fix L0_TM_DISABLE_SCRAMBLE_ENCODER mask
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nava kishore Manne <[email protected]>
commit 6cb22477929489a412df8d153e550e77a012e701 upstream.
The L0_TX_DIG_61 register bit 2 is a reserved read-only field. The previous mask value 0x0f incorrectly included bit 2, causing unintended writes to a reserved bit on every scrambler bypass operation.
Correct the mask to (BIT(3) | GENMASK(1, 0)) to cover only the valid scramble bypass control bits.
--- drivers/phy/xilinx/phy-zynqmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/phy/xilinx/phy-zynqmp.c +1 -1
@@ -53,7 +53,7 @@#define L0_TM_DIG_6 0x106c#define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f#define L0_TX_DIG_61 0x00f4 -#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f +#define L0_TM_DISABLE_SCRAMBLE_ENCODER (BIT(3) | GENMASK(1, 0))/* PLL Test Mode register parameters */ #define L0_TM_PLL_DIG_37 0x2094
Greg Kroah-Hartman
maintainerPATCH 216/337 phy: zynqmp: use read-modify-write for SERDES scrambler bypass
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nava kishore Manne <[email protected]>
commit 21e0749f931702765b9d52d05740092bc87fcd8d upstream.
xpsgtr_bypass_scrambler_8b10b() used xpsgtr_write_phy() which performs a full register write, silently clearing any bits beyond the intended bypass control fields.
Switch to xpsgtr_clr_set_phy() with clr=mask, set=mask to set only the bypass bits while preserving the remaining bits in each register.
--- drivers/phy/xilinx/phy-zynqmp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +6 -2
@@ -505,8 +505,12 @@ static void xpsgtr_lane_set_protocol(str/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ static void xpsgtr_bypass_scrambler_8b10b (struct xpsgtr_phy *gtr_phy) { -xpsgtr_write_phy (gtr_phy,L0_TM_DIG_6 ,L0_TM_DIS_DESCRAMBLE_DECODER ); -xpsgtr_write_phy (gtr_phy,L0_TX_DIG_61 ,L0_TM_DISABLE_SCRAMBLE_ENCODER ); +xpsgtr_clr_set_phy (gtr_phy,L0_TM_DIG_6 , +L0_TM_DIS_DESCRAMBLE_DECODER , +L0_TM_DIS_DESCRAMBLE_DECODER ); +xpsgtr_clr_set_phy (gtr_phy,L0_TX_DIG_61 , +L0_TM_DISABLE_SCRAMBLE_ENCODER , +L0_TM_DISABLE_SCRAMBLE_ENCODER ); }/* DP-specific initialization. */
Greg Kroah-Hartman
maintainerPATCH 217/337 phy: zynqmp: keep SERDES scrambler and 8b/10b enabled for USB
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nava kishore Manne <[email protected]>
commit 7eb61caf45607e1e1270f51f8f93f0ded53146da upstream.
USB Gen1 requires scrambling and 8b/10b encoding to be performed in the physical layer. Do not bypass PHY-side scrambler or encoder/decoder for USB operation, as mandated by the USB 3.x specification.
Scrambler and 8b/10b bypass remain restricted to SATA and SGMII modes, where encoding is handled in the controller.
--- drivers/phy/xilinx/phy-zynqmp.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +29 -10
@@ -502,15 +502,30 @@ static void xpsgtr_lane_set_protocol(str } } -/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ -static void xpsgtr_bypass_scrambler_8b10b (struct xpsgtr_phy *gtr_phy) +/** +* xpsgtr_bypass_scrambler_8b10b - Configure scrambler/encoder behavior +* @gtr_phy: pointer to lane context +* @bypass: true to enable scrambler/encoder bypass (SATA/SGMII), +* false to disable scrambler/encoder bypass (USB3) +* +* Uses RMW to preserve reserved and unrelated register fields. +*/ +static void xpsgtr_bypass_scrambler_8b10b (struct xpsgtr_phy *gtr_phy, +bool bypass) { -xpsgtr_clr_set_phy (gtr_phy,L0_TM_DIG_6 , -L0_TM_DIS_DESCRAMBLE_DECODER , -L0_TM_DIS_DESCRAMBLE_DECODER ); -xpsgtr_clr_set_phy (gtr_phy,L0_TX_DIG_61 , -L0_TM_DISABLE_SCRAMBLE_ENCODER , -L0_TM_DISABLE_SCRAMBLE_ENCODER ); +if (bypass) { +xpsgtr_clr_set_phy (gtr_phy,L0_TM_DIG_6 , +L0_TM_DIS_DESCRAMBLE_DECODER , +L0_TM_DIS_DESCRAMBLE_DECODER ); +xpsgtr_clr_set_phy (gtr_phy,L0_TX_DIG_61 , +L0_TM_DISABLE_SCRAMBLE_ENCODER , +L0_TM_DISABLE_SCRAMBLE_ENCODER ); + }else { +xpsgtr_clr_set_phy (gtr_phy,L0_TM_DIG_6 , +L0_TM_DIS_DESCRAMBLE_DECODER ,0 ); +xpsgtr_clr_set_phy (gtr_phy,L0_TX_DIG_61 , +L0_TM_DISABLE_SCRAMBLE_ENCODER ,0 ); + } }/* DP-specific initialization. */
@@ -531,7 +546,7 @@ static void xpsgtr_phy_init_sata(struct {struct xpsgtr_dev *gtr_dev = gtr_phy->dev; -xpsgtr_bypass_scrambler_8b10b (gtr_phy); +xpsgtr_bypass_scrambler_8b10b (gtr_phy, true);writel (gtr_phy->lane, gtr_dev->siou +SATA_CONTROL_OFFSET ); }
@@ -547,7 +562,7 @@ static void xpsgtr_phy_init_sgmii(structxpsgtr_clr_set (gtr_dev,TX_PROT_BUS_WIDTH , mask, val);xpsgtr_clr_set (gtr_dev,RX_PROT_BUS_WIDTH , mask, val); -xpsgtr_bypass_scrambler_8b10b (gtr_phy); +xpsgtr_bypass_scrambler_8b10b (gtr_phy, true); }/* Configure TX de-emphasis and margining for DP. */
@@ -708,6 +723,10 @@ static int xpsgtr_phy_init(struct phy *pcase ICM_PROTOCOL_SGMII :xpsgtr_phy_init_sgmii (gtr_phy);break ; + +case ICM_PROTOCOL_USB : +xpsgtr_bypass_scrambler_8b10b (gtr_phy, false); +break ; } goto out;
Greg Kroah-Hartman
maintainerPATCH 218/337 net: openvswitch: fix potential UAF on meter attach failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Maximets <[email protected]>
commit a58a2b0ce354df531ebc71fc870058c2feb59f6b upstream.
While attaching a newly created meter attach_meter() function makes the new meter visible to other CPUs but can still fail afterwards. On failure, it detaches the meter back and returns an error.
However, this is an unexpected behavior for the ovs_meter_cmd_set() that uses a plain kfree(meter) on attach failure without waiting for RCU readers to stop using it, assuming it was never visible.
This is never a problem for ovs-vswitchd as it always creates meters before creating any flows that use them. But the UAF can be triggered with a custom application using uAPI:
BUG: KASAN: slab-use-after-free in ovs_meter_execute (net/openvswitch/meter.c:653) Read of size 8 at addr ffff88810d152650 by task meter/2508 Call Trace: ovs_meter_execute (net/openvswitch/meter.c:653) do_execute_actions (net/openvswitch/actions.c:1407) ovs_execute_actions (net/openvswitch/actions.c:1584) ovs_packet_cmd_execute (net/openvswitch/datapath.c:703) ... netlink_sendmsg (af_netlink.c:1900) Allocated by task 2519: __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415) ovs_meter_cmd_set (net/openvswitch/meter.c:422) ... netlink_sendmsg (af_netlink.c:1900) Freed by task 2519: kfree (mm/slub.c:2705 mm/slub.c:6405 mm/slub.c:6720) ovs_meter_cmd_set (net/openvswitch/meter.c:479) ... netlink_sendmsg (af_netlink.c:1900)
Fix that by making sure attach_meter() doesn't make the meter visible until all the checks are done and the function can't fail anymore.
This also makes sure the "hash" value is calculated after the potential re-sizing of the table.
Reported by Trend Micro's Zero Day Initiative as ZDI-CAN-31642.
--- net/openvswitch/meter.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-)
net/openvswitch/meter.c +19 -14
@@ -133,18 +133,10 @@ static void dp_meter_instance_remove(strstatic int attach_meter (struct dp_meter_table *tbl,struct dp_meter *meter) { -struct dp_meter_instance *ti =rcu_dereference_ovsl (tbl->ti); -u32 hash =meter_hash (ti, meter->id); +struct dp_meter_instance *ti; +u32 hash;int err; -/* In generally, slots selected should be empty, because -* OvS uses id-pool to fetch a available id. -*/ -if (unlikely (rcu_dereference_ovsl (ti->dp_meters[hash]))) -return -EBUSY ; - -dp_meter_instance_insert (ti, meter); -/* That function is thread-safe. */ tbl->count++;if (tbl->count >= tbl->max_meters_allowed) {
@@ -152,16 +144,29 @@ static int attach_meter(struct dp_meter_ goto attach_err; } -if (tbl->count >= ti->n_meters && -dp_meter_instance_realloc (tbl, ti->n_meters *2 )) { - err = -ENOMEM ; + ti =rcu_dereference_ovsl (tbl->ti); +if (tbl->count >= ti->n_meters) { + err =dp_meter_instance_realloc (tbl, ti->n_meters *2 ); +if (err) + goto attach_err; + + ti =rcu_dereference_ovsl (tbl->ti); + } + + hash =meter_hash (ti, meter->id); + +/* In general, selected slots should be empty, because +* OvS uses id-pool to fetch available ids. +*/ +if (unlikely (rcu_dereference_ovsl (ti->dp_meters[hash]))) { + err = -EBUSY ; goto attach_err; } +dp_meter_instance_insert (ti, meter);return 0 ; attach_err: -dp_meter_instance_remove (ti, meter); tbl->count--;return err; }
Greg Kroah-Hartman
maintainerPATCH 219/337 net: openvswitch: fix skb leak on flow key update failure during recirculation
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Maximets <[email protected]>
commit e1cf066244dad576221b7123a0e5005967f25a20 upstream.
do_execute_actions() returns right away when execute_recirc() fails on the last action as it assumes this function always takes ownership of the skb when 'last' is true. But when the flow key update fails, the function doesn't free the skb and it ends up leaked.
This is a very unlikely scenario as it requires the packet to become unparseable by applying a set of actions on a previously parseable skb, but should be fixed nevertheless.
Reported by Sashiko.
--- net/openvswitch/actions.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
net/openvswitch/actions.c +9 -1
@@ -1146,6 +1146,10 @@ static int execute_masked_set_action(strreturn err; } +/* When 'last' is true, recirc() should always consume the 'skb'. +* Otherwise, recirc() should keep 'skb' intact regardless what +* actions are executed on recirculation. +*/ static int execute_recirc (struct datapath *dp,struct sk_buff *skb,struct sw_flow_key *key,const struct nlattr *a,bool last)
@@ -1156,8 +1160,12 @@ static int execute_recirc(struct datapatint err; err =ovs_flow_key_update (skb, key); -if (err) +if (err) { +if (last) +ovs_kfree_skb_reason (skb, +OVS_DROP_ACTION_ERROR );return err; + } }BUG_ON (!is_flow_key_valid (key));
Greg Kroah-Hartman
maintainerPATCH 220/337 net: openvswitch: fix skb leak on flow key update failure during ct
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Maximets <[email protected]>
commit bc62e843bc48f933da765ce47079fd992e535794 upstream.
ovs_ct_execute() always steals or frees the skb on failure while ovs_flow_key_update() does not. So, if it fails and we return right away, the skb ends up leaked.
Fix that by breaking instead and letting the common error handling code at the bottom of the loop to free the skb properly.
This is a very unlikely scenario as it requires the packet to become unparseable by applying a set of actions on a previously parseable skb, but should be fixed nevertheless.
Reported by Sashiko.
--- net/openvswitch/actions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/openvswitch/actions.c +1 -1
@@ -1418,7 +1418,7 @@ static int do_execute_actions(struct datif (!is_flow_key_valid (key)) { err =ovs_flow_key_update (skb, key);if (err) -return err; +break ; } err = ovs_ct_execute(ovs_dp_get_net(dp), skb, key,
Greg Kroah-Hartman
maintainerPATCH 221/337 ice: wait for reset completion in ice_resume()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaron Ma <[email protected]>
commit c2816d613f388814d27bc9fd6dbd931a88056e19 upstream.
ice_resume() schedules an asynchronous PF reset and returns immediately. The reset runs later in ice_service_task(). If userspace tries to bring up the net device before the reset finishes, ice_open() fails with -EBUSY:
ice_resume()
ice_schedule_reset() # sets ICE_PFR_REQ, returns
...
ice_open()
ice_is_reset_in_progress() # ICE_PFR_REQ still set, -EBUSY
...
ice_service_task()
ice_do_reset()
ice_rebuild() # clears ICE_PFR_REQ, too late
Reproduced on E800 series NICs during suspend/resume with irdma enabled, where the aux device probe widens the race window.
ice 0000:81:00.0: can't open net device while reset is in progress
Add a best-effort wait (10s timeout, matching ice_devlink_info_get()) for the reset to complete before returning from ice_resume(). In practice the reset completes in ~300ms.
--- drivers/net/ethernet/intel/ice/ice_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/net/ethernet/intel/ice/ice_main.c +10 -0
@@ -5708,6 +5708,16 @@ static int ice_resume(struct device *dev/* Restart the service task */ mod_timer (&pf->serv_tmr,round_jiffies (jiffies + pf->serv_tmr_period)); +/* Best-effort wait for the scheduled reset to finish so that the +* device is operational before returning. Without this, userspace +* (e.g. NetworkManager) may try to open the net device while the +* asynchronous reset is still in progress, hitting -EBUSY. +*/ + ret =ice_wait_for_reset (pf,secs_to_jiffies (10 )); +if (ret) +dev_err (dev,"Wait for reset timed out (10s) during resume: %d\n" , + ret); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 222/337 ice: fix memory leak in ice_lbtest_prepare_rings()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawei Feng <[email protected]>
commit 3a9de5590da4ffd9e9c541c4c4d492aa2b54cf6e upstream.
ice_lbtest_prepare_rings() frees Rx rings only when ice_vsi_start_all_rx_rings() fails. If ice_vsi_setup_rx_rings() fails after allocating some descriptors, or if ice_vsi_cfg_lan() fails after the Rx rings were prepared, the function reaches the Tx cleanup path without releasing the initialized Rx resources.
Fix this by adding separate unwind paths for Rx setup failure and LAN configuration failure. The Rx setup failure path releases the partially prepared Rx rings before freeing Tx rings, while later failures first undo the LAN Tx configuration and then release the Rx rings in reverse setup order.
The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc7.
An x86_64 allyesconfig build showed no new warnings. As we do not have an Intel E800 Series adapter available to run the ethtool offline loopback selftest, no runtime testing was able to be performed.
--- drivers/net/ethernet/intel/ice/ice_ethtool.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/net/ethernet/intel/ice/ice_ethtool.c +5 -5
@@ -1067,18 +1067,18 @@ static int ice_lbtest_prepare_rings(stru status =ice_vsi_cfg_lan (vsi);if (status) - goto err_setup_rx_ring; + goto err_cfg_lan; status =ice_vsi_start_all_rx_rings (vsi);if (status) - goto err_start_rx_ring; + goto err_cfg_lan;return 0 ; -err_start_rx_ring: -ice_vsi_free_rx_rings (vsi); -err_setup_rx_ring: +err_cfg_lan:ice_vsi_stop_lan_tx_rings (vsi,ICE_NO_RESET ,0 ); +err_setup_rx_ring: +ice_vsi_free_rx_rings (vsi); err_setup_tx_ring:ice_vsi_free_tx_rings (vsi);
Greg Kroah-Hartman
maintainerPATCH 223/337 i2c: jz4780: Cache host clock rate at probe to prevent CCF prepare_lock deadlock
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: H. Nikolaus Schaller <[email protected]>
commit d99607c888f26e8a4e9fe9772860cef4aff86bb4 upstream.
Fix a severe AB/BA deadlock between the Common Clock Framework (CCF) and the I2C adapter lock, which triggers when an I2C-controlled clock generator client (like the Si5351) is registered or modified under the CCF.
During an i2c client clock (generator) frequency change, the CCF acquires its global 'prepare_lock' mutex and the driver calls i2c_transfer() to update the client's chip registers, stalling for the adapter's I2C bus lock.
Concurrently, an independent, parallel transfer on the same bus (e.g., a GPIO expander handling LEDs) can hold the I2C adapter lock. Inside this parallel transfer path, jz4780_i2c_set_speed() calls clk_get_rate() on the host controller's input clock to calculate bus timings. This call attempts to acquire the blocked CCF 'prepare_lock', creating a circular dependency that freezes the system.
The jz4780 host controller clock itself is static and never changes at runtime.
However, calling clk_get_rate() inside the active transfer path introduces an unnecessary dependency on the CCF internal locks.
Eliminate this synchronous clk_get_rate() call from the active transfer path by caching the static host peripheral clock rate once - inside the private jz4780_i2c structure during jz4780_i2c_probe(). Update jz4780_i2c_set_speed() to use this cached value, safely decoupling active I2C transactions from the CCF internal locks without any risk of stale timings.
Assisted-by web based Google AI (pinpointing the bug and writing the message).
--- drivers/i2c/busses/i2c-jz4780.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-jz4780.c +4 -1
@@ -141,6 +141,7 @@ struct jz4780_i2c {void __iomem *iomem;int irq;struct clk *clk; +unsigned long clk_rate_khz;struct i2c_adapter adap;const struct ingenic_i2c_config *cdata;
@@ -246,7 +247,7 @@ static int jz4780_i2c_set_target(structstatic int jz4780_i2c_set_speed (struct jz4780_i2c *i2c) { -int dev_clk_khz =clk_get_rate (i2c->clk) /1000 ; +int dev_clk_khz = i2c->clk_rate_khz;int cnt_high =0 ;/* HIGH period count of the SCL clock */ int cnt_low =0 ;/* LOW period count of the SCL clock */ int cnt_period =0 ;/* period count of the SCL clock */
@@ -796,6 +797,8 @@ static int jz4780_i2c_probe(struct platfif (IS_ERR (i2c->clk))return PTR_ERR (i2c->clk); + i2c->clk_rate_khz =clk_get_rate (i2c->clk) /1000 ; + ret =of_property_read_u32 (pdev->dev.of_node,"clock-frequency" , &clk_freq);if (ret) {
Greg Kroah-Hartman
maintainerPATCH 224/337 i2c: iproc: reset bus after timeout if START_BUSY is stuck
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jonas Gorski <[email protected]>
commit 98f2e9e6d6f91a6abb43f166b244b428ba85fa2b upstream.
If a transaction times out, the START_BUSY signal can stay up, and subsequent transactaction attempts will fail as the bus is still considered busy.
I can easily trigger this by attempting to read from an address with no device, e.g. when running i2cdetect. After the first read times out, all subsequent read attempts return busy.
To get to a working state again, the controller needs to be reset to clear the START_BUSY signal. So check for START_BUSY still asserted on a timeout, and do reset in case it is,
This is also done by the original non-upstream iproc-smbus driver implementation [1].
Works around situations like:
bcm-iproc-2c 1803b000.i2c: transaction timed out
bcm-iproc-2c 1803b000.i2c: bus is busy
bcm-iproc-2c 1803b000.i2c: bus is busy
bcm-iproc-2c 1803b000.i2c: bus is busy
bcm-iproc-2c 1803b000.i2c: bus is busy
bcm-iproc-2c 1803b000.i2c: bus is busy
...where the bus never recovers after a timeout.
--- drivers/i2c/busses/i2c-bcm-iproc.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/i2c/busses/i2c-bcm-iproc.c +11 -0
@@ -811,6 +811,17 @@ static int bcm_iproc_i2c_xfer_wait(struc }if (!time_left && !iproc_i2c->xfer_is_done) { +/* +* The controller may fail to clear START_BUSY after a timeout, +* reset the controller to recover in that case. +*/ +if (!!(iproc_i2c_rd_reg (iproc_i2c,M_CMD_OFFSET ) & +BIT (M_CMD_START_BUSY_SHIFT ))) { +bcm_iproc_i2c_enable_disable (iproc_i2c, false); +bcm_iproc_i2c_init (iproc_i2c); +bcm_iproc_i2c_enable_disable (iproc_i2c, true); + } +/* flush both TX/RX FIFOs */ val =BIT (M_FIFO_RX_FLUSH_SHIFT ) |BIT (M_FIFO_TX_FLUSH_SHIFT );iproc_i2c_wr_reg (iproc_i2c,M_FIFO_CTRL_OFFSET , val);
Greg Kroah-Hartman
maintainerPATCH 225/337 i2c: imx: Fix slave registration race and error handling
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Liem <[email protected]>
commit d64ec362c369bbc33833f7936d5f3a706b0d5c45 upstream.
In i2c_imx_reg_slave(), the slave pointer was assigned before pm_runtime_resume_and_get(). If pm_runtime_resume_and_get() failed, the error path returned without clearing i2c_imx->slave, leaving it non-NULL and causing all subsequent registration attempts to fail with -EBUSY.
Additionally, because this driver uses a shared IRQ, the interrupt handler i2c_imx_isr() can execute concurrently and, after acquiring slave_lock, dereference i2c_imx->slave. The previous fix attempt added a lockless i2c_imx->slave = NULL on the error path, but that could race with the ISR under the lock and still cause a NULL pointer dereference.
Fix both issues by deferring the assignment of i2c_imx->slave and i2c_imx->last_slave_event to after a successful resume, and by performing the assignment inside the slave_lock critical section. This guarantees that the slave pointer is never left stale on the error path and is always valid when observed by the interrupt handler.
--- drivers/i2c/busses/i2c-imx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/i2c/busses/i2c-imx.c +5 -3
@@ -866,9 +866,6 @@ static int i2c_imx_reg_slave(struct i2c_if (i2c_imx->slave)return -EBUSY ; - i2c_imx->slave = client; - i2c_imx->last_slave_event =I2C_SLAVE_STOP ; -/* Resume */ ret =pm_runtime_resume_and_get (i2c_imx->adapter.dev.parent);if (ret <0 ) {
@@ -876,6 +873,11 @@ static int i2c_imx_reg_slave(struct i2c_return ret; } + scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) { + i2c_imx->slave = client; + i2c_imx->last_slave_event =I2C_SLAVE_STOP ; + } +i2c_imx_slave_init (i2c_imx);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 226/337 i2c: imx: Cancel hrtimer before clearing slave pointer
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Liem <[email protected]>
commit 6ac7702b6cc2b94aaed9ef2d95bfbefcdc90061f upstream.
In i2c_imx_unreg_slave(), the slave pointer is set to NULL after disabling interrupts. However, a pending interrupt might already have started the hrtimer (i2c_imx_slave_timeout) before the pointer was cleared. If the hrtimer fires after i2c_imx->slave is set to NULL, the timer callback i2c_imx_slave_finish_op() will call i2c_imx_slave_event() with a NULL slave pointer, which results in a use-after-free / NULL pointer dereference.
Fix by canceling the hrtimer and waiting for it to complete after disabling interrupts, before clearing the slave pointer.
--- drivers/i2c/busses/i2c-imx.c | 1 + 1 file changed, 1 insertion(+)
drivers/i2c/busses/i2c-imx.c +1 -0
@@ -896,6 +896,7 @@ static int i2c_imx_unreg_slave(struct i2i2c_imx_reset_regs (i2c_imx); +hrtimer_cancel (&i2c_imx->slave_timer); i2c_imx->slave =NULL ;/* Suspend */
Greg Kroah-Hartman
maintainerPATCH 227/337 can: c_can: c_can_chip_config(): keep controller in init mode until bittiming is configured
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lucas Martins Alves <[email protected]>
commit 26504844613fb44c7cab1c5f6fcff77861709baa upstream.
c_can_chip_config() was programming C_CAN_CTRL_REG without CONTROL_INIT, which may allow the controller to become active before c_can_set_bittiming() finishes.
That creates a short timing window where the peripheral can interact with the bus using a different/default bitrate, potentially generating bus errors and corrupting traffic.
Set CONTROL_INIT together with the control-mode writes in c_can_chip_config() (normal, loopback and listen-only paths), so the controller stays halted until bit timing is fully programmed.
This prevents transient bus disturbance during startup when the configured bitrate differs from the active bus bitrate.
--- drivers/net/can/c_can/c_can_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/net/can/c_can/c_can_main.c +4 -4
@@ -597,20 +597,20 @@ static int c_can_chip_config(struct net_return err;/* enable automatic retransmission */ - priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_ENABLE_AR ); + priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_ENABLE_AR |CONTROL_INIT );if ((priv->can.ctrlmode &CAN_CTRLMODE_LISTENONLY ) && (priv->can.ctrlmode &CAN_CTRLMODE_LOOPBACK )) {/* loopback + silent mode : useful for hot self-test */ - priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST ); + priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST |CONTROL_INIT ); priv->write_reg (priv,C_CAN_TEST_REG ,TEST_LBACK |TEST_SILENT ); }else if (priv->can.ctrlmode &CAN_CTRLMODE_LOOPBACK ) {/* loopback mode : useful for self-test function */ - priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST ); + priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST |CONTROL_INIT ); priv->write_reg (priv,C_CAN_TEST_REG ,TEST_LBACK ); }else if (priv->can.ctrlmode &CAN_CTRLMODE_LISTENONLY ) {/* silent mode : bus-monitoring mode */ - priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST ); + priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST |CONTROL_INIT ); priv->write_reg (priv,C_CAN_TEST_REG ,TEST_SILENT ); }
Greg Kroah-Hartman
maintainerPATCH 228/337 can: ems_usb: validate CPC message lengths
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit 02925f51377f2a42a6724f00549167499c9302e5 upstream.
ems_usb_read_bulk_callback() walks CPC messages packed in one USB receive buffer.
Check that each declared message fits in the URB payload. Also require the type-specific payload to cover the fields used by the CAN, state, error and overrun handlers.
--- drivers/net/can/usb/ems_usb.c | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
drivers/net/can/usb/ems_usb.c +43 -0
@@ -409,6 +409,40 @@ static void ems_usb_rx_err(struct ems_usnetif_rx (skb); } +static bool ems_usb_rx_msg_len_valid (struct ems_cpc_msg *msg) +{ +size_t len = msg->length; +size_t can_len; + +switch (msg->type) { +case CPC_MSG_TYPE_CAN_STATE : +return len >=sizeof (msg->msg.can_state); + +case CPC_MSG_TYPE_CAN_FRAME : +case CPC_MSG_TYPE_EXT_CAN_FRAME : +case CPC_MSG_TYPE_RTR_FRAME : +case CPC_MSG_TYPE_EXT_RTR_FRAME : +if (len <CPC_CAN_MSG_MIN_SIZE ) +return false; + +if (msg->type ==CPC_MSG_TYPE_RTR_FRAME || + msg->type ==CPC_MSG_TYPE_EXT_RTR_FRAME ) +return true; + + can_len =can_cc_dlc2len (msg->msg.can_msg.length &0xf ); +return len >=CPC_CAN_MSG_MIN_SIZE + can_len; + +case CPC_MSG_TYPE_CAN_FRAME_ERROR : +return len >=sizeof (msg->msg.error); + +case CPC_MSG_TYPE_OVERRUN : +return len >=sizeof (msg->msg.overrun); + +default : +return true; + } +} +/* * callback for bulk IN urb */
@@ -451,6 +485,15 @@ static void ems_usb_read_bulk_callback(s } msg = (struct ems_cpc_msg *)&ibuf[start]; +if (msg->length > + urb->actual_length - start -CPC_MSG_HEADER_LEN ) { +netdev_err (netdev,"format error\n" ); +break ; + } +if (!ems_usb_rx_msg_len_valid (msg)) { +netdev_err (netdev,"format error\n" ); +break ; + }switch (msg->type) {case CPC_MSG_TYPE_CAN_STATE :
Greg Kroah-Hartman
maintainerPATCH 229/337 can: etas_es58x: es58x_read_bulk_callback(): fix RX buffer leak on URB resubmit failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 7a0cf2b2497c757c3cb1286eddf2986abb0d387b upstream.
es58x_read_bulk_callback() resubmits the RX URB after processing a received packet. If the resubmit succeeds, the URB remains anchored and will be handled by the normal RX path or by teardown.
However, if usb_submit_urb() fails, the callback unanchors the URB and then returns directly. This skips the existing free_urb path, so the coherent transfer buffer allocated with usb_alloc_coherent() is not released.
Reuse the existing free_urb path after a resubmit failure so that the RX coherent buffer is freed before leaving the callback.
--- drivers/net/can/usb/etas_es58x/es58x_core.c | 1 - 1 file changed, 1 deletion(-)
drivers/net/can/usb/etas_es58x/es58x_core.c +0 -1
@@ -1476,7 +1476,6 @@ static void es58x_read_bulk_callback(strdev_err_ratelimited (dev,"Failed resubmitting read bulk urb: %pe\n" ,ERR_PTR (ret)); -return ; free_urb: usb_free_coherent(urb->dev, urb->transfer_buffer_length,
Greg Kroah-Hartman
maintainerPATCH 230/337 can: gs_usb: gs_usb_receive_bulk_callback(): resubmit URB on skb allocation failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marc Kleine-Budde <[email protected]>
commit 68c5724ecd159992f76edb7b57dc508a44c8b7da upstream.
If the allocation of the SKB in gs_usb_receive_bulk_callback() fails, the driver returns from the callback without resubmitting the URB in order to receive further USB in URBs.
This results in a silent performance degradation which, if it occurs repeatedly, results in starvation of USB in traffic.
Instead of returning immediately, try to resend the URB. If this also fails, this is logged as an info message.
--- drivers/net/can/usb/gs_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/can/usb/gs_usb.c +2 -2
@@ -671,7 +671,7 @@ static void gs_usb_receive_bulk_callbackif (hf->flags &GS_CAN_FLAG_FD ) { skb =alloc_canfd_skb (netdev, &cfd);if (!skb) -return ; + goto resubmit_urb; cfd->can_id =le32_to_cpu (hf->can_id); cfd->len = data_length;
@@ -684,7 +684,7 @@ static void gs_usb_receive_bulk_callback }else { skb =alloc_can_skb (netdev, &cf);if (!skb) -return ; + goto resubmit_urb; cf->can_id =le32_to_cpu (hf->can_id);can_frame_set_cc_len (cf, hf->can_dlc, dev->can.ctrlmode);
Greg Kroah-Hartman
maintainerPATCH 231/337 can: j1939: transport: j1939_session_fresh_new(): initialize receive buffer
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oleksij Rempel <[email protected]>
commit eb96c58907922546e415e545fe9a14ea63b02719 upstream.
Zero the allocated buffer in j1939_session_fresh_new() to ensure it contains no residual data.
While there is a potential performance impact if users allocate maximum sized ETP buffers, most real-world use cases are not noticeably affected since the maximum known buffer size is typically around 65K.
Message-ID: <CAPAUci5dykCLjoijqkUtFqJFesgncrD7+S6y_V=gjbFkY2Tifg@mail.gmail.com>
--- net/can/j1939/transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/can/j1939/transport.c +1 -1
@@ -1562,7 +1562,7 @@ j1939_session *j1939_session_fresh_new(s }/* alloc data area */ -skb_put (skb, size); +skb_put_zero (skb, size);/* skb is recounted in j1939_session_new() */ return session; }
Greg Kroah-Hartman
maintainerPATCH 232/337 can: j1939: use netdevice_tracker for j1939_{priv,session,ecu} tracking
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tetsuo Handa <[email protected]>
commit d2fb981384b3a45f690616d550b29046e8ad16a4 upstream.
syzbot is still reporting
unregister_netdevice: waiting for vcan0 to become free. Usage count = 2
problem. A debug printk() patch in linux-next-20260508 identified that there is dev_hold()/dev_put() imbalance in j1939_priv management.
Call trace for vcan0[26] +4 at
__dev_hold include/linux/netdevice.h:4470 [inline]
netdev_hold include/linux/netdevice.h:4513 [inline]
dev_hold include/linux/netdevice.h:4536 [inline]
j1939_priv_create net/can/j1939/main.c:140 [inline]
j1939_netdev_start+0x36b/0xc10 net/can/j1939/main.c:268
j1939_sk_bind+0x853/0xb30 net/can/j1939/socket.c:506
__sys_bind_socket net/socket.c:1948 [inline]
__sys_bind+0x2e9/0x410 net/socket.c:1979Call trace for vcan0[28] -3 at
__dev_put include/linux/netdevice.h:4456 [inline]
netdev_put include/linux/netdevice.h:4523 [inline]
dev_put include/linux/netdevice.h:4548 [inline]
__j1939_priv_release net/can/j1939/main.c:166 [inline]
kref_put include/linux/kref.h:65 [inline]
j1939_priv_put+0x128/0x270 net/can/j1939/main.c:172
j1939_sk_sock_destruct+0x52/0x90 net/can/j1939/socket.c:388
__sk_destruct+0x8d/0x9d0 net/core/sock.c:2352
rcu_do_batch kernel/rcu/tree.c:2617 [inline]
rcu_core kernel/rcu/tree.c:2869 [inline]
rcu_cpu_kthread+0x99e/0x1470 kernel/rcu/tree.c:2957
smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245This refcount leak in j1939_priv might be caused by a refcount leak in j1939_{session,ecu} because j1939_{session,ecu} holds a ref on j1939_priv. For further investigation using upstream kernels, enable netdevice_tracker in j1939_{priv,session,ecu} management.
--- net/can/j1939/bus.c | 2 ++ net/can/j1939/j1939-priv.h | 3 +++ net/can/j1939/main.c | 8 ++++---- net/can/j1939/transport.c | 2 ++ 4 files changed, 11 insertions(+), 4 deletions(-)
net/can/j1939/bus.c +2 -0
@@ -20,6 +20,7 @@ static void __j1939_ecu_release(struct kstruct j1939_priv *priv = ecu->priv;list_del (&ecu->list); +netdev_put (priv->ndev, &ecu->priv_dev_tracker);kfree (ecu);j1939_priv_put (priv); }
@@ -155,6 +156,7 @@ struct j1939_ecu *j1939_ecu_create_lockeif (!ecu)return ERR_PTR (-ENOMEM );kref_init (&ecu->kref); +netdev_hold (priv->ndev, &ecu->priv_dev_tracker,gfp_any ()); ecu->addr =J1939_IDLE_ADDR ; ecu->name = name;
net/can/j1939/j1939-priv.h +3 -0
@@ -38,6 +38,7 @@ struct j1939_ecu {struct hrtimer ac_timer;struct kref kref;struct j1939_priv *priv; +netdevice_tracker priv_dev_tracker;/* count users, to help transport protocol decide for interaction */ int nusers;
@@ -60,6 +61,7 @@ struct j1939_priv {rwlock_t lock;struct net_device *ndev; +netdevice_tracker dev_tracker;/* list of 256 ecu ptrs, that cache the claimed addresses. * also protected by the above lock
@@ -229,6 +231,7 @@ enum j1939_session_state {struct j1939_session {struct j1939_priv *priv; +netdevice_tracker priv_dev_tracker;struct list_head active_session_list_entry;struct list_head sk_session_queue_entry;struct kref kref;
net/can/j1939/main.c +4 -4
@@ -137,7 +137,7 @@ static struct j1939_priv *j1939_priv_cre priv->ndev = ndev;kref_init (&priv->kref);kref_init (&priv->rx_kref); -dev_hold (ndev); +netdev_hold (ndev, &priv->dev_tracker,GFP_KERNEL );netdev_dbg (priv->ndev,"%s : 0x%p\n" , __func__, priv);
@@ -163,7 +163,7 @@ static void __j1939_priv_release(structWARN_ON_ONCE (!list_empty (&priv->ecus));WARN_ON_ONCE (!list_empty (&priv->j1939_socks)); -dev_put (ndev); +netdev_put (ndev, &priv->dev_tracker);kfree (priv); }
@@ -281,7 +281,7 @@ struct j1939_priv *j1939_netdev_start(st*/ kref_get (&priv_new->rx_kref);mutex_unlock (&j1939_netdev_lock); -dev_put (ndev); +netdev_put (ndev, &priv->dev_tracker);kfree (priv);return priv_new; }
@@ -298,7 +298,7 @@ struct j1939_priv *j1939_netdev_start(stj1939_priv_set (ndev,NULL );mutex_unlock (&j1939_netdev_lock); -dev_put (ndev); +netdev_put (ndev, &priv->dev_tracker);kfree (priv);return ERR_PTR (ret);
net/can/j1939/transport.c +2 -0
@@ -282,6 +282,7 @@ static void j1939_session_destroy(structkfree_skb (skb); }__j1939_session_drop (session); +netdev_put (session->priv->ndev, &session->priv_dev_tracker);j1939_priv_put (session->priv);kfree (session); }
@@ -1510,6 +1511,7 @@ static struct j1939_session *j1939_sessiINIT_LIST_HEAD (&session->active_session_list_entry);INIT_LIST_HEAD (&session->sk_session_queue_entry);kref_init (&session->kref); +netdev_hold (priv->ndev, &session->priv_dev_tracker,gfp_any ());j1939_priv_get (priv); session->priv = priv;
Greg Kroah-Hartman
maintainerPATCH 233/337 can: kvaser_usb: kvaser_usb_hydra_get_busparams(): fix memory leak in kvaser_usb_hydra_get_busparams()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 941eaf9a6d3b33dea49f2c0a1da7546a03b6ff71 upstream.
The memory allocated for cmd is not freed after the call to kvaser_usb_send_cmd() in both the normal and error paths. Fix that by adding a kfree() immediately after the call.
--- drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +1 -0
@@ -1625,6 +1625,7 @@ static int kvaser_usb_hydra_get_busparamreinit_completion (&priv->get_busparams_comp); err =kvaser_usb_send_cmd (dev, cmd, cmd_len); +kfree (cmd);if (err)return err;
Greg Kroah-Hartman
maintainerPATCH 234/337 can: kvaser_usb_leaf: kvaser_usb_leaf_wait_cmd(): validate received command extents
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit 0293dd153f9dbc1ddf5dacdccc76b363bce4a8ee upstream.
The wait and bulk receive paths walk variable-length commands from a USB buffer. A nonzero command shorter than CMD_HEADER_LEN can still be dispatched, and the wait path copies a matching command into a fixed caller-owned struct kvaser_cmd using the device-provided length.
Reject nonzero commands that do not contain the fixed header or that extend beyond the current USB buffer item. In the wait path, also reject a matching command that exceeds the destination before copying it.
--- drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +11 -2
@@ -668,13 +668,22 @@ static int kvaser_usb_leaf_wait_cmd(conscontinue ; } -if (pos + tmp->len > actual_len) { +if (tmp->len <CMD_HEADER_LEN || + tmp->len > actual_len - pos) {dev_err_ratelimited (&dev->intf->dev,"Format error\n" );break ; }if (tmp->id == id) { +if (tmp->len >sizeof (*cmd)) { +dev_err_ratelimited (&dev->intf->dev, +"Received command %u too large (%u)\n" , + tmp->id, tmp->len); + err = -EIO ; + goto end; + } +memcpy (cmd, tmp, tmp->len); goto end; }
@@ -1677,7 +1686,7 @@ static void kvaser_usb_leaf_read_bulk_cacontinue ; } -if (pos + cmd->len > len) { +if (cmd->len <CMD_HEADER_LEN || cmd->len > len - pos) {dev_err_ratelimited (&dev->intf->dev,"Format error\n" );break ; }
Greg Kroah-Hartman
maintainerPATCH 235/337 can: softing: fw_parse(): validate firmware record spans
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit 856d6cb04e5407523566b075841dcd6423757d1c upstream.
fw_parse() reads a fixed record header, a firmware-provided payload, and a trailing checksum without knowing the end of the firmware blob. A truncated record can therefore make those reads exceed the blob.
The same record also supplies addresses and lengths for writes into DPRAM. The generic loader uses wrap-prone mixed signed arithmetic for its bounds check, while the application loader does not bound the staging copy at all.
Pass the firmware end to the parser and validate the full source record. Use a signed wide offset for generic DPRAM records and validate the application staging span against the mapped DPRAM before copying.
--- drivers/net/can/softing/softing_fw.c | 46 ++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 14 deletions(-)
drivers/net/can/softing/softing_fw.c +32 -14
@@ -91,12 +91,12 @@ int softing_bootloader_command(struct soreturn ret; } -static int fw_parse (const uint8_t **pmem,uint16_t *ptype,uint32_t *paddr, -uint16_t *plen,const uint8_t **pdat) +static int fw_parse (const u8 **pmem,const u8 *limit,u16 *ptype, +u32 *paddr,u16 *plen,const u8 **pdat) {uint16_t checksum[2 ]; -const uint8_t *mem; -const uint8_t *end; +const u8 *mem; +const u8 *record_end;/* * firmware records are a binary, unaligned stream composed of:
@@ -114,14 +114,21 @@ static int fw_parse(const uint8_t **pmem* endianness & alignment. */ mem = *pmem; +/* A record needs an 8-byte prefix and a 2-byte checksum. */ +if (mem > limit || limit - mem <10 ) +return -EINVAL ; + *ptype =le16_to_cpup ((void *)&mem[0 ]); *paddr =le32_to_cpup ((void *)&mem[2 ]); *plen =le16_to_cpup ((void *)&mem[6 ]); +if (*plen > limit - mem -10 ) +return -EINVAL ; + *pdat = &mem[8 ];/* verify checksum */ - end = &mem[8 + *plen]; - checksum[0 ] =le16_to_cpup ((void *)end); -for (checksum[1 ] =0 ; mem < end; ++mem) + record_end = &mem[8 + *plen]; + checksum[0 ] =le16_to_cpup ((void *)record_end); +for (checksum[1 ] =0 ; mem < record_end; ++mem) checksum[1 ] += *mem;if (checksum[0 ] != checksum[1 ])return -EINVAL ;
@@ -139,6 +146,7 @@ int softing_load_fw(const char *file, stuint16_t type, len;uint32_t addr;uint8_t *buf =NULL , *new_buf; +s64 dpram_offset;int buflen =0 ;int8_t type_end =0 ;
@@ -153,7 +161,7 @@ int softing_load_fw(const char *file, st mem = fw->data; end = &mem[fw->size];/* look for header record */ - ret =fw_parse (&mem, &type, &addr, &len, &dat); + ret =fw_parse (&mem, end, &type, &addr, &len, &dat);if (ret <0 ) goto failed;if (type !=0xffff )
@@ -164,7 +172,7 @@ int softing_load_fw(const char *file, st }/* ok, we had a header */ while (mem < end) { - ret =fw_parse (&mem, &type, &addr, &len, &dat); + ret =fw_parse (&mem, end, &type, &addr, &len, &dat);if (ret <0 ) goto failed;if (type ==3 ) {
@@ -179,9 +187,13 @@ int softing_load_fw(const char *file, st goto failed; } -if ((addr + len + offset) > size) + dpram_offset = (s64 )addr + offset; +if (dpram_offset <0 || dpram_offset > size || + len > size - dpram_offset) { + ret = -EINVAL ; goto failed; -memcpy_toio (&dpram[addr + offset], dat, len); + } +memcpy_toio (&dpram[dpram_offset], dat, len);/* be sure to flush caches from IO space */ mb ();if (len > buflen) {
@@ -195,7 +207,7 @@ int softing_load_fw(const char *file, st buf = new_buf; }/* verify record data */ -memcpy_fromio (buf, &dpram[addr + offset], len); +memcpy_fromio (buf, &dpram[dpram_offset], len);if (memcmp (buf, dat, len)) {/* is not ok */ dev_alert (&card->pdev->dev,"DPRAM readback failed\n" );
@@ -237,7 +249,7 @@ int softing_load_app_fw(const char *file mem = fw->data; end = &mem[fw->size];/* look for header record */ - ret =fw_parse (&mem, &type, &addr, &len, &dat); + ret =fw_parse (&mem, end, &type, &addr, &len, &dat);if (ret) goto failed; ret = -EINVAL ;
@@ -253,7 +265,7 @@ int softing_load_app_fw(const char *file }/* ok, we had a header */ while (mem < end) { - ret =fw_parse (&mem, &type, &addr, &len, &dat); + ret =fw_parse (&mem, end, &type, &addr, &len, &dat);if (ret) goto failed;
@@ -279,6 +291,12 @@ int softing_load_app_fw(const char *file/* work in 16bit (target) */ sum &=0xffff ; +if (card->pdat->app.offs > card->dpram_size || + len > card->dpram_size - card->pdat->app.offs) { + ret = -EINVAL ; + goto failed; + } +memcpy_toio (&card->dpram[card->pdat->app.offs], dat, len);iowrite32 (card->pdat->app.offs + card->pdat->app.addr, &card->dpram[DPRAM_COMMAND +2 ]);
Greg Kroah-Hartman
maintainerPATCH 236/337 can: peak_usb: add bounds check for USB channel index
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: James Gao <[email protected]>
commit 39132f166ca8ce00ae60d8a9068e06a60943cc4b upstream.
The channel control index ctrl_idx is derived from rx->len which comes directly from a device USB payload. The mask 0x0f allows values 0-15, but the array size of usb_if->dev[] is only 2. Values 2-15 cause heap out-of-bounds read, eventually causing kernel panic in the IRQ context.
Add bounds checking for ctrl_idx before the array access in both pcan_usb_pro_handle_canmsg() and pcan_usb_pro_handle_error().
--- drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
drivers/net/can/usb/peak_usb/pcan_usb_pro.c +16 -4
@@ -534,12 +534,18 @@ static int pcan_usb_pro_handle_canmsg(st struct pcan_usb_pro_rxmsg *rx) {const unsigned int ctrl_idx = (rx->len >>4 ) &0x0f ; -struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; -struct net_device *netdev = dev->netdev; +struct peak_usb_device *dev; +struct net_device *netdev;struct can_frame *can_frame;struct sk_buff *skb;struct skb_shared_hwtstamps *hwts; +if (ctrl_idx >=ARRAY_SIZE (usb_if->dev)) +return -EINVAL ; + + dev = usb_if->dev[ctrl_idx]; + netdev = dev->netdev; + skb =alloc_can_skb (netdev, &can_frame);if (!skb)return -ENOMEM ;
@@ -573,14 +579,20 @@ static int pcan_usb_pro_handle_error(str {const u16 raw_status =le16_to_cpu (er->status);const unsigned int ctrl_idx = (er->channel >>4 ) &0x0f ; -struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; -struct net_device *netdev = dev->netdev; +struct peak_usb_device *dev; +struct net_device *netdev;struct can_frame *can_frame;enum can_state new_state =CAN_STATE_ERROR_ACTIVE ;u8 err_mask =0 ;struct sk_buff *skb;struct skb_shared_hwtstamps *hwts; +if (ctrl_idx >=ARRAY_SIZE (usb_if->dev)) +return -EINVAL ; + + dev = usb_if->dev[ctrl_idx]; + netdev = dev->netdev; +/* nothing should be sent while in BUS_OFF state */ if (dev->can.state ==CAN_STATE_BUS_OFF )return 0 ;
Greg Kroah-Hartman
maintainerPATCH 237/337 can: peak_usb: peak_usb_start(): fix double free of transfer buffer on URB submit error
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
commit 9b3d5a6d952c38bbcf07f903cbeadefdb56b9bc9 upstream.
In peak_usb_start(), each RX URB transfer buffer is allocated with kmalloc() and the URB is flagged URB_FREE_BUFFER so that the final usb_free_urb() also frees the transfer buffer.
If usb_submit_urb() fails, the error path frees the buffer explicitly with kfree(buf) and then calls usb_free_urb(urb). Because URB_FREE_BUFFER is set, usb_free_urb() -> urb_destroy() frees the same buffer a second time, a double free of the transfer buffer.
BUG: KASAN: double-free in usb_free_urb.part.0+0x91/0xb0 Free of addr ffff8881069ccb80 by task trigger.sh/285 Call Trace: kfree+0x113/0x3c0 usb_free_urb.part.0+0x91/0xb0
Drop the redundant kfree(buf); usb_free_urb() already releases the transfer buffer. This mirrors commit 03819abbeb11 ("net: usb: lan78xx: Fix double free issue with interrupt buffer allocation").
--- drivers/net/can/usb/peak_usb/pcan_usb_core.c | 1 - 1 file changed, 1 deletion(-)
drivers/net/can/usb/peak_usb/pcan_usb_core.c +0 -1
@@ -470,7 +470,6 @@ static int peak_usb_start(struct peak_usnetif_device_detach (dev->netdev);usb_unanchor_urb (urb); -kfree (buf);usb_free_urb (urb);break ; }
Greg Kroah-Hartman
maintainerPATCH 238/337 can: peak_usb: validate uCAN receive record lengths
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit 93fcab2c6968446316bbb49548848df604d6346f upstream.
pcan_usb_fd_decode_buf() walks uCAN records packed in one USB receive buffer.
Require each record to contain the fixed header for its type, and verify CAN payload bytes before copying them into the skb.
--- drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 40 ++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-)
drivers/net/can/usb/peak_usb/pcan_usb_fd.c +39 -1
@@ -565,6 +565,13 @@ static int pcan_usb_fd_decode_canmsg(str dev->can.ctrlmode); } +if (!(rx_msg_flags &PUCAN_MSG_RTR ) && +le16_to_cpu (rx_msg->size) - offsetof(struct pucan_rx_msg , d) < + cfd->len) { +kfree_skb (skb); +return -EBADMSG ; + } + cfd->can_id =le32_to_cpu (rm->can_id);if (rx_msg_flags &PUCAN_MSG_EXT_ID )
@@ -713,6 +720,24 @@ static void pcan_usb_fd_decode_ts(structpeak_usb_set_ts_now (&usb_if->time_ref,le32_to_cpu (ts->ts_low)); } +static size_t pcan_usb_fd_rx_msg_min_size (u16 rx_msg_type) +{ +switch (rx_msg_type) { +case PUCAN_MSG_CAN_RX : +return offsetof(struct pucan_rx_msg , d); +case PCAN_UFD_MSG_CALIBRATION : +return sizeof (struct pcan_ufd_ts_msg ); +case PUCAN_MSG_ERROR : +return sizeof (struct pucan_error_msg ); +case PUCAN_MSG_STATUS : +return sizeof (struct pucan_status_msg ); +case PCAN_UFD_MSG_OVERRUN : +return sizeof (struct pcan_ufd_ovr_msg ); +default : +return sizeof (struct pucan_msg ); + } +} +/* callback for bulk IN urb */ static int pcan_usb_fd_decode_buf (struct peak_usb_device *dev,struct urb *urb) {
@@ -727,6 +752,12 @@ static int pcan_usb_fd_decode_buf(struct msg_end = urb->transfer_buffer + urb->actual_length;for (; msg_ptr < msg_end;) {u16 rx_msg_type, rx_msg_size; +size_t rx_msg_min_size; + +if (msg_end - msg_ptr <sizeof (*rx_msg)) { + err = -EBADMSG ; +break ; + } rx_msg = (struct pucan_msg *)msg_ptr;if (!rx_msg->size) {
@@ -738,12 +769,19 @@ static int pcan_usb_fd_decode_buf(struct rx_msg_type =le16_to_cpu (rx_msg->type);/* check if the record goes out of current packet */ -if (msg_ptr + rx_msg_size > msg_end) { +if (rx_msg_size > msg_end - msg_ptr) {netdev_err (netdev,"got frag rec: should inc usb rx buf sze\n" ); err = -EBADMSG ;break ; } + + rx_msg_min_size =pcan_usb_fd_rx_msg_min_size (rx_msg_type); +if (rx_msg_size < rx_msg_min_size) { +netdev_err (netdev,"got short rec\n" ); + err = -EBADMSG ; +break ; + }switch (rx_msg_type) {case PUCAN_MSG_CAN_RX :
Greg Kroah-Hartman
maintainerPATCH 239/337 can: ctucanfd: add missing MODULE_DEVICE_TABLE()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit d937bdb244a751fe5967052ea2d64a7b2c476cc0 upstream.
The driver has a match table for the pci bus wired into its driver structure, but the table is not exported with MODULE_DEVICE_TABLE().
Add the missing MODULE_DEVICE_TABLE() entry so module alias information is generated for automatic module loading.
This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the driver registration structure, and the missing module alias publication.
--- drivers/net/can/ctucanfd/ctucanfd_pci.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/can/ctucanfd/ctucanfd_pci.c +1 -0
@@ -274,6 +274,7 @@ static const struct pci_device_id ctucan CTUCAN_WITH_CTUCAN_ID)}, {}, }; +MODULE_DEVICE_TABLE (pci, ctucan_pci_tbl);static struct pci_driver ctucan_pci_driver = { .name =KBUILD_MODNAME ,
Greg Kroah-Hartman
maintainerPATCH 240/337 can: ctucanfd: use self-test mode for PRESUME_ACK
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avi Weiss <[email protected]>
commit c31a435933f18be0f874302161333e9f16e200a0 upstream.
Use self-test mode for CAN_CTRLMODE_PRESUME_ACK so transmitted frames can complete without receiving an ACK.
ACK forbidden mode prevents the controller from acknowledging received frames and does not implement the presume-ack behavior.
--- drivers/net/can/ctucanfd/ctucanfd_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/can/ctucanfd/ctucanfd_base.c +2 -2
@@ -340,8 +340,8 @@ static void ctucan_set_mode(struct ctuca (mode_reg & ~REG_MODE_FDE ); mode_reg = (mode->flags &CAN_CTRLMODE_PRESUME_ACK ) ? - (mode_reg |REG_MODE_ACF ) : - (mode_reg & ~REG_MODE_ACF ); + (mode_reg |REG_MODE_STM ) : + (mode_reg & ~REG_MODE_STM ); mode_reg = (mode->flags &CAN_CTRLMODE_FD_NON_ISO ) ? (mode_reg | REG_MODE_NISOFD) :
Greg Kroah-Hartman
maintainerPATCH 241/337 can: ctucanfd: unmap BAR0 using base address
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avi Weiss <[email protected]>
commit a6873910f983096746d1a2e0af94f36b8003e839 upstream.
BAR0 is mapped into bar0_base, while cra_addr points to an offset within that mapping and is used for other purposes.
Pass bar0_base to pci_iounmap(), instead of cra_addr, on the probe error path so the address returned by pci_iomap() is used for unmapping.
--- drivers/net/can/ctucanfd/ctucanfd_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/can/ctucanfd/ctucanfd_pci.c +1 -1
@@ -202,7 +202,7 @@ err_free_board:pci_set_drvdata (pdev,NULL );kfree (bdata); err_pci_iounmap_bar0: -pci_iounmap (pdev, cra_addr); +pci_iounmap (pdev, bar0_base); err_pci_iounmap_bar1:pci_iounmap (pdev, addr); err_release_regions:
Greg Kroah-Hartman
maintainerPATCH 242/337 can: ctucanfd: handle bus error interrupts
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avi Weiss <[email protected]>
commit e74bae899529f49c0f375307983d12e8ecad7d4b upstream.
Include REG_INT_STAT_BEI in the top-level error interrupt condition.
BEI is enabled when CAN_CTRLMODE_BERR_REPORTING is requested and ctucan_err_interrupt() already handles it. Without checking and clearing BEI in the top-level handler, bus error interrupts are not handled or acknowledged.
--- drivers/net/can/ctucanfd/ctucanfd_base.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/net/can/ctucanfd/ctucanfd_base.c +6 -2
@@ -1135,8 +1135,12 @@ static irqreturn_t ctucan_interrupt(int/* Error interrupts */ if (FIELD_GET (REG_INT_STAT_EWLI , isr) ||FIELD_GET (REG_INT_STAT_FCSI , isr) || -FIELD_GET (REG_INT_STAT_ALI , isr)) { - icr = isr & (REG_INT_STAT_EWLI |REG_INT_STAT_FCSI |REG_INT_STAT_ALI ); +FIELD_GET (REG_INT_STAT_ALI , isr) || +FIELD_GET (REG_INT_STAT_BEI , isr)) { + icr = isr & (REG_INT_STAT_EWLI | +REG_INT_STAT_FCSI | +REG_INT_STAT_ALI | +REG_INT_STAT_BEI );ctucan_netdev_dbg (ndev,"some ERR interrupt: clearing 0x%08x\n" , icr);ctucan_write32 (priv,CTUCANFD_INT_STAT , icr);
Greg Kroah-Hartman
maintainerPATCH 243/337 can: ctucanfd: mark error-active controller status valid
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avi Weiss <[email protected]>
commit 4e735cbe3affe88001428fdd9cae8e685ce92f21 upstream.
In the CAN_STATE_ERROR_ACTIVE case, cf->data[1] is set to CAN_ERR_CRTL_ACTIVE, but cf->can_id is not set with CAN_ERR_CRTL in that path.
Set CAN_ERR_CRTL so consumers know the controller-status information in cf->data[1] is valid.
--- drivers/net/can/ctucanfd/ctucanfd_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/can/ctucanfd/ctucanfd_base.c +1 -1
@@ -868,7 +868,7 @@ static void ctucan_err_interrupt(structbreak ;case CAN_STATE_ERROR_ACTIVE :if (skb) { - cf->can_id |=CAN_ERR_CNT ; + cf->can_id |=CAN_ERR_CRTL |CAN_ERR_CNT ; cf->data[1 ] =CAN_ERR_CRTL_ACTIVE ; cf->data[6 ] = bec.txerr; cf->data[7 ] = bec.rxerr;
Greg Kroah-Hartman
maintainerPATCH 244/337 drm/dp: Read the PCON max FRL bandwidth only for HDMI DFPs
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Kaplan <[email protected]>
commit e40e20ac089e32f1d910636155dc82e61e61dcf3 upstream.
The PCON max FRL bandwidth field lives in byte 2 of the DFP Detailed Capability Info (DPCD 0x82 for the first DFP).
The DP standard defines the meaning of descriptor bytes 1-3 strictly per DFP type, and for a DisplayPort type DFP all of them are reserved, with "read all 0s" semantics (DP v2.0, section 2.12.3, Table 2-183).
The FRL bandwidth field is an HDMI DFP extension added by the VESA DP-to-HDMI PCON specification. drm_dp_get_pcon_max_frl_bw() however parses the byte without checking the DFP type, the branch presence or DETAILED_CAP_INFO_AVAILABLE. Without the latter the port descriptors are one byte wide and port_cap[2] is not even the right register.
All neighbouring helpers parsing the same descriptor are scoped by the DFP type already, see for instance drm_dp_downstream_max_bpc() reading the same byte and returning 0 for a DP type DFP. amdgpu's DC parses the field only for HDMI(/DP++) detailed types as well.
This is not theoretical.
A Synaptics VMM7100 based USB-C to HDMI adapter with a macOS targeted firmware advertises a DisplayPort type DFP with the type byte replicated across the whole descriptor (08 08 08 08).
i915 decodes that as "PCON limited to 18 Gbps FRL" and prunes every mode above ~750 MHz dotclock, including all the 4k@100/120 modes the sink EDID offers, while macOS drives 4k@120 through the same adapter just fine via DP DSC (and amdgpu's type-scoped parser would ignore the bogus field as well).
Only parse the field for an HDMI DFP behind a DPCD 1.1+ branch device that reports detailed cap info, matching the type-scoped field layout of the spec and the rest of the helpers.
--- drivers/gpu/drm/display/drm_dp_helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/gpu/drm/display/drm_dp_helper.c +12 -0
@@ -3145,6 +3145,18 @@ int drm_dp_get_pcon_max_frl_bw(const u8int bw;u8 buf; +if (!drm_dp_is_branch (dpcd)) +return 0 ; + +if (dpcd[DP_DPCD_REV ] <0x11 ) +return 0 ; + +if ((dpcd[DP_DOWNSTREAMPORT_PRESENT ] &DP_DETAILED_CAP_INFO_AVAILABLE ) ==0 ) +return 0 ; + +if ((port_cap[0 ] &DP_DS_PORT_TYPE_MASK ) !=DP_DS_PORT_TYPE_HDMI ) +return 0 ; + buf = port_cap[2 ]; bw = buf &DP_PCON_MAX_FRL_BW ;
Greg Kroah-Hartman
maintainerPATCH 245/337 drm/vc4: Supply the overflow slot size in BPOS, not the whole bin BO size
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jose Maria Casanova Crespo <[email protected]>
commit 6395789e4739aa5177bbec0fa0f07ccc38d249b0 upstream.
vc4_overflow_mem_work() points BPOA at a 512KB slot inside the 16MB binner BO, but writes the size of the whole BO to BPOS. On every binner out-of-memory event the PTB is therefore authorized to write tile lists across all the other slots (which may hold the tile state, tile alloc and overflow memory of in-flight jobs) and, for any slot but the first, past the end of the binner BO into unrelated CMA memory.
Since CMA pages are recycled into page cache and user allocations, this is arbitrary memory corruption by GPU DMA. In practice it shows up as GPU hangs with corrupted control list pointers, userspace heap corruption, a GPU that stays permanently wedged after the first hang, and occasional full system crashes, whenever a job overflows the initial binner slot.
The bug dates back to the conversion from a dedicated overflow BO (where writing the full BO size was correct) to the slotted binner BO.
--- drivers/gpu/drm/vc4/vc4_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/vc4/vc4_irq.c +1 -1
@@ -104,7 +104,7 @@ vc4_overflow_mem_work(struct work_struct vc4->bin_alloc_overflow =BIT (bin_bo_slot);V3D_WRITE (V3D_BPOA , bo->base.dma_addr + bin_bo_slot * vc4->bin_alloc_size); -V3D_WRITE (V3D_BPOS , bo->base.base.size); +V3D_WRITE (V3D_BPOS , vc4->bin_alloc_size);V3D_WRITE (V3D_INTCTL ,V3D_INT_OUTOMEM );V3D_WRITE (V3D_INTENA ,V3D_INT_OUTOMEM );spin_unlock_irqrestore (&vc4->job_lock, irqflags);
Greg Kroah-Hartman
maintainerPATCH 246/337 drm/vc4: Zero the tile state data array before each BIN job
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maíra Canal <[email protected]>
commit 48a570c964d8e37d353381e4195106277e17f5cb upstream.
The binner BO is a single 16MB buffer split into 512KB slots that are handed out to jobs at submission time and recycled as jobs complete, without ever being cleared. Each slot holds the job's Tile State Data Array (TSDA) at its start, followed by the tile allocation pool.
While the tile allocation pool is only walked by the render thread through branches the binner generated during the current job, the TSDA is the PTB's own per-tile bookkeeping and is consumed by the hardware itself. Although the kernel sets the "Auto-initialise Tile State Data Array" flag in the tile binning mode configuration, the PTB demonstrably still acts on stale tile state left by the slot's previous user: the binner ends up creating invalid command streams with invalid primitive streams and branches, which can cause GPU hangs as observed in [1][2].
Zero the TSDA when the job's binning slot is configured. This clears 48 bytes per tile (~24KB for a 1080p frame) in the submission path, and guarantees the PTB never sees another job's tile state.
The tile count is only checked for being non-zero today, so the 8-bit fields it comes from can describe a tile state array almost six times larger than the slot it has to live in. Bound it before the slot is handed out, since such size decides how much of the slot is left for the tile alloc pool.
--- drivers/gpu/drm/vc4/vc4_validate.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-)
drivers/gpu/drm/vc4/vc4_validate.c +23 -6
@@ -387,6 +387,23 @@ validate_tile_binning_config(VALIDATE_ARreturn -EINVAL ; } +/* The tile state data array is 48 bytes per tile, and we put it at +* the start of a BO containing both it and the tile alloc. +*/ + tile_state_size =48 * tile_count; + +/* Since the tile alloc array will follow us, align. */ + tile_state_size =roundup (tile_state_size,4096 ); + +/* Reject configurations whose tile state would leave no room for +* the tile alloc pool that follows it in the slot. +*/ +if (tile_state_size >= vc4->bin_alloc_size) { +DRM_DEBUG ("Tile binning config of %dx%d too large\n" , + exec->bin_tiles_x, exec->bin_tiles_y); +return -EINVAL ; + } + bin_slot =vc4_v3d_get_bin_slot (vc4);if (bin_slot <0 ) {if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS ) {
@@ -402,13 +419,13 @@ validate_tile_binning_config(VALIDATE_AR exec->bin_slots |=BIT (bin_slot); bin_addr = vc4->bin_bo->base.dma_addr + bin_slot * vc4->bin_alloc_size; -/* The tile state data array is 48 bytes per tile, and we put it at -* the start of a BO containing both it and the tile alloc. -*/ - tile_state_size =48 * tile_count; + exec->tile_alloc_offset = bin_addr + tile_state_size; -/* Since the tile alloc array will follow us, align. */ - exec->tile_alloc_offset = bin_addr +roundup (tile_state_size,4096 ); +/* The TSDA area must be zeroed out before use, otherwise the PTB might +* consume a stale tile state. +*/ +memset (vc4->bin_bo->base.vaddr + bin_slot * vc4->bin_alloc_size,0 , + tile_state_size); *(uint8_t *)(validated + 14) = ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
Greg Kroah-Hartman
maintainerPATCH 247/337 drm/panthor: reject firmware sections with oversized data
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Osama Abdelkader <[email protected]>
commit a3caaa06809248b996254be5b47e10804a3494e2 upstream.
In panthor_fw_load_section_entry(), the data size to copy is calculated without validating it against the allocated section_size:
section->data.size = hdr.data.end - hdr.data.start;
If a crafted firmware sets data.size larger than the allocated memory, this could cause a heap buffer overflow in panthor_fw_init_section_mem()
memcpy(section->mem->kmap, section->data.buf, section->data.size);
Additionally, if the section->data.size exceeds the BO size, could this memset underflow the size calculation, leading to a massive out-of-bounds zeroing of kernel memory?
memset(section->mem->kmap + section->data.size, 0,
panthor_kernel_bo_size(section->mem) - section->data.size);Reject section entries whose initial data is larger than the section size.
--- drivers/gpu/drm/panthor/panthor_fw.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/gpu/drm/panthor/panthor_fw.c +9 -2
@@ -491,6 +491,7 @@ static int panthor_fw_load_section_entrystruct panthor_fw_binary_section_entry_hdr hdr;struct panthor_fw_section *section;u32 section_size; +u32 data_size;u32 name_len;int ret;
@@ -541,6 +542,13 @@ static int panthor_fw_load_section_entryreturn -EINVAL ; } + section_size = hdr.va.end - hdr.va.start; + data_size = hdr.data.end - hdr.data.start; +if (data_size > section_size) { +drm_err (&ptdev->base,"Firmware corrupted, section data exceeds section size\n" ); +return -EINVAL ; + } + name_len = iter->size - iter->offset; section =drmm_kzalloc (&ptdev->base,sizeof (*section),GFP_KERNEL );
@@ -549,7 +557,7 @@ static int panthor_fw_load_section_entrylist_add_tail (§ion->node, &ptdev->fw->sections); section->flags = hdr.flags; - section->data.size = hdr.data.end - hdr.data.start; + section->data.size = data_size;if (section->data.size >0 ) {void *data =drmm_kmalloc (&ptdev->base, section->data.size,GFP_KERNEL );
@@ -572,7 +580,6 @@ static int panthor_fw_load_section_entry section->name = name; } - section_size = hdr.va.end - hdr.va.start;if (section_size) {u32 cache_mode = hdr.flags &CSF_FW_BINARY_IFACE_ENTRY_RD_CACHE_MODE_MASK ;struct panthor_gem_object *bo;
Greg Kroah-Hartman
maintainerPATCH 248/337 drm/panthor: validate firmware interface structure sizes
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Osama Abdelkader <[email protected]>
commit b921b8613790a3f9e78ab64017fa7149ef0b750c upstream.
iface_fw_to_cpu_addr() only checks that the firmware-provided MCU virtual address points inside the shared section. The returned pointer is later used as a full firmware interface structure, so accepting an address near the end of the shared section can still lead to out-of-bounds accesses.
Pass the expected object size to iface_fw_to_cpu_addr() and reject ranges that do not fit entirely in the shared section.
--- drivers/gpu/drm/panthor/panthor_fw.c | 36 +++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-)
drivers/gpu/drm/panthor/panthor_fw.c +24 -12
@@ -767,18 +767,24 @@ out:* iface_fw_to_cpu_addr() - Turn an MCU address into a CPU address * @ptdev: Device. * @mcu_va: MCU address. +* @size: Size of the object pointed to by @mcu_va. * -* Return: NULL if the address is not part of the shared section, non-NULL otherwise. +* Return: NULL if the object is not part of the shared section, non-NULL otherwise. */ -static void *iface_fw_to_cpu_addr (struct panthor_device *ptdev,u32 mcu_va) +static void *iface_fw_to_cpu_addr (struct panthor_device *ptdev,u32 mcu_va,size_t size) {u64 shared_mem_start =panthor_kernel_bo_gpuva (ptdev->fw->shared_section->mem); -u64 shared_mem_end = shared_mem_start + -panthor_kernel_bo_size (ptdev->fw->shared_section->mem); -if (mcu_va < shared_mem_start || mcu_va >= shared_mem_end) +size_t shared_mem_size =panthor_kernel_bo_size (ptdev->fw->shared_section->mem); +u64 offset; + +if (mcu_va < shared_mem_start) +return NULL ; + + offset = mcu_va - shared_mem_start; +if (offset > shared_mem_size || size > shared_mem_size - offset)return NULL ; -return ptdev->fw->shared_section->mem->kmap + (mcu_va - shared_mem_start); +return ptdev->fw->shared_section->mem->kmap + offset; } static int panthor_init_cs_iface(struct panthor_device *ptdev,
@@ -800,8 +806,10 @@ static int panthor_init_cs_iface(structspin_lock_init (&cs_iface->lock); cs_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset; - cs_iface->input =iface_fw_to_cpu_addr (ptdev, cs_iface->control->input_va); - cs_iface->output =iface_fw_to_cpu_addr (ptdev, cs_iface->control->output_va); + cs_iface->input =iface_fw_to_cpu_addr (ptdev, cs_iface->control->input_va, +sizeof (*cs_iface->input)); + cs_iface->output =iface_fw_to_cpu_addr (ptdev, cs_iface->control->output_va, +sizeof (*cs_iface->output));if (!cs_iface->input || !cs_iface->output) {drm_err (&ptdev->base,"Invalid stream control interface input/output VA" );
@@ -851,8 +859,10 @@ static int panthor_init_csg_iface(structspin_lock_init (&csg_iface->lock); csg_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset; - csg_iface->input =iface_fw_to_cpu_addr (ptdev, csg_iface->control->input_va); - csg_iface->output =iface_fw_to_cpu_addr (ptdev, csg_iface->control->output_va); + csg_iface->input =iface_fw_to_cpu_addr (ptdev, csg_iface->control->input_va, +sizeof (*csg_iface->input)); + csg_iface->output =iface_fw_to_cpu_addr (ptdev, csg_iface->control->output_va, +sizeof (*csg_iface->output));if (csg_iface->control->stream_num <MIN_CS_PER_CSG || csg_iface->control->stream_num >MAX_CS_PER_CSG )
@@ -909,8 +919,10 @@ static int panthor_fw_init_ifaces(structreturn -EINVAL ; } - glb_iface->input =iface_fw_to_cpu_addr (ptdev, glb_iface->control->input_va); - glb_iface->output =iface_fw_to_cpu_addr (ptdev, glb_iface->control->output_va); + glb_iface->input =iface_fw_to_cpu_addr (ptdev, glb_iface->control->input_va, +sizeof (*glb_iface->input)); + glb_iface->output =iface_fw_to_cpu_addr (ptdev, glb_iface->control->output_va, +sizeof (*glb_iface->output));if (!glb_iface->input || !glb_iface->output) {drm_err (&ptdev->base,"Invalid global control interface input/output VA" );return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 249/337 drm/mediatek: ovl_adaptor: balance component registrations
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 533e3469a57996905cdb95f178e7efe38c21aeb2 upstream.
The OVL adaptor registers both an aggregate driver for its child devices and a component for the main DRM aggregate. Probe currently ignores an error from registering the child aggregate and leaves that aggregate registered if registering the DRM component fails. The remove callback also leaves the DRM component registered.
These imbalances can leave component framework entries referring to a device whose probe failed or whose driver has been detached. The aggregate unbind callback also fails to undo component_bind_all(), leaving its child components marked as bound when the aggregate is removed.
Check the aggregate registration result, unwind it when the component registration fails, and unregister the component before the aggregate on remove. Keep runtime PM enabled until both framework registrations have been removed, and unbind all child components from the aggregate unbind callback.
--- drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c +6 -1
@@ -585,6 +585,7 @@ static void mtk_disp_ovl_adaptor_master_struct mtk_disp_ovl_adaptor *priv =dev_get_drvdata (dev); priv->children_bound = false; +component_unbind_all (dev, priv->mmsys_dev); }static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = {
@@ -611,12 +612,15 @@ static int mtk_disp_ovl_adaptor_probe(st priv->mmsys_dev = pdev->dev.platform_data; -component_master_add_with_match (dev, &mtk_disp_ovl_adaptor_master_ops, match); + ret =component_master_add_with_match (dev, &mtk_disp_ovl_adaptor_master_ops, match); +if (ret) +return dev_err_probe (dev, ret,"Failed to add component master\n" );pm_runtime_enable (dev); ret =component_add (dev, &mtk_disp_ovl_adaptor_comp_ops);if (ret !=0 ) { +component_master_del (dev, &mtk_disp_ovl_adaptor_master_ops);pm_runtime_disable (dev);return dev_err_probe (dev, ret,"Failed to add component\n" ); }
@@ -626,6 +630,7 @@ static int mtk_disp_ovl_adaptor_probe(ststatic void mtk_disp_ovl_adaptor_remove (struct platform_device *pdev) { +component_del (&pdev->dev, &mtk_disp_ovl_adaptor_comp_ops);component_master_del (&pdev->dev, &mtk_disp_ovl_adaptor_master_ops);pm_runtime_disable (&pdev->dev); }
Greg Kroah-Hartman
maintainerPATCH 250/337 drm/amdgpu: restore UMD profile pstate after runtime resume
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Candice Li <[email protected]>
commit f931c54b241ce2f36bfc34955aec43a188276b8d upstream.
Runtime suspend runs GFX hw_fini and clears perfmon clock gating while the UMD profile DPM level remains set in software. Re-apply stable pstate after a successful runtime resume when a profile mode is active.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +15 -0
@@ -2824,6 +2824,19 @@ static int amdgpu_pmops_runtime_suspend(return 0 ; } +static void amdgpu_restore_umd_profile_pstate_after_runpm (struct amdgpu_device *adev) +{ +enum amd_dpm_forced_level level; +uint32_t profile_mode_mask =AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD | +AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK | +AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK | +AMD_DPM_FORCED_LEVEL_PROFILE_PEAK ; + + level =amdgpu_dpm_get_performance_level (adev); +if (level & profile_mode_mask) +amdgpu_asic_update_umd_stable_pstate (adev, true); +} +static int amdgpu_pmops_runtime_resume (struct device *dev) {struct pci_dev *pdev =to_pci_dev (dev);
@@ -2868,6 +2881,8 @@ static int amdgpu_pmops_runtime_resume(sif (adev->pm.rpm_mode ==AMDGPU_RUNPM_PX ) drm_dev->switch_power_state =DRM_SWITCH_POWER_ON ; + +amdgpu_restore_umd_profile_pstate_after_runpm (adev); adev->in_runpm = false;return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 251/337 drm/amdgpu: cap GTT size to physical RAM on APUs
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harkirat Gill <[email protected]>
commit 5e70f6804b4d6256058c360b10e044ee04ea4a4e upstream.
On APUs, the GTT pool is backed by system RAM, but its size is not bound to the non-carveout memory that actually backs it. A user can end up with GTT + VRAM exceeding total physical memory through the following sequence:
- Have a large non-carveout memory space (~128GB) and accordingly set a large GTT (~100GB) via the ttm module parameter.
- Lower the non-carveout memory space in BIOS by increasing the UMA Frame Buffer Size (VRAM) to 64GB.
- The previously set GTT value (~100GB) persists, even though the new non-carveout space (64GB) can no longer back it.
This leads to a case where kernel reports GTT (100GB) + VRAM (64GB) despite the sum being greater than total physical memory (128GB).
Cap the GTT size to totalram_pages() on APUs. totalram_pages() already excludes the VRAM carveout, so the resulting GTT can never exceed the system RAM that actually backs it.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +12 -0
@@ -1996,6 +1996,18 @@ int amdgpu_ttm_init(struct amdgpu_deviceelse gtt_size = (uint64_t )amdgpu_gtt_size <<20 ; +/* Cap GTT so that it does not exceed total physical RAM. */ +if (adev->flags &AMD_IS_APU ) { +u64 phys_ram = (u64 )totalram_pages () <<PAGE_SHIFT ; + +if (gtt_size > phys_ram) { + gtt_size = phys_ram; +dev_info (adev->dev, +"Capping GTT to %uM to not exceed available system memory\n" , + (unsigned int )(gtt_size / (1024 *1024 ))); + } + } +/* Initialize GTT memory pool */ r =amdgpu_gtt_mgr_init (adev, gtt_size);if (r) {
Greg Kroah-Hartman
maintainerPATCH 252/337 drm/amd/display: Increase HDMI AV mute wait from 2 to 3 frames
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ray Wu <[email protected]>
commit c216b39fbbc4b007fd6984cffd85039d49a55154 upstream.
Some HDMI sinks need additional GCP packets to properly process the mute state before the timing generator is disabled, especially after link re-establishment with HDMI 2.0 scrambling enabled. Waiting for only 2 frames is insufficient for certain monitor firmware, resulting in garbled display output on resume from suspend.
Increase the AV mute wait in dcn30_set_avmute() from 2 to 3 frames to ensure the sink receives enough GCP packets.
--- drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-)
drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +11 -5
@@ -821,13 +821,19 @@ void dcn30_set_avmute(struct pipe_ctx *p pipe_ctx->stream_res.stream_enc, enable); -/* Wait for two frame to make sure AV mute is sent out */ +/* Wait for three frames to make sure AV mute is sent out. +* Some HDMI sinks need additional GCP packets to properly +* process the mute state, especially after link re-establishment +* with HDMI 2.0 scrambling enabled. +*/ if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled (pipe_ctx->stream_res.tg)) { +int i; + pipe_ctx->stream_res.tg->funcs->wait_for_state (pipe_ctx->stream_res.tg,CRTC_STATE_VACTIVE ); - pipe_ctx->stream_res.tg->funcs->wait_for_state (pipe_ctx->stream_res.tg,CRTC_STATE_VBLANK ); - pipe_ctx->stream_res.tg->funcs->wait_for_state (pipe_ctx->stream_res.tg,CRTC_STATE_VACTIVE ); - pipe_ctx->stream_res.tg->funcs->wait_for_state (pipe_ctx->stream_res.tg,CRTC_STATE_VBLANK ); - pipe_ctx->stream_res.tg->funcs->wait_for_state (pipe_ctx->stream_res.tg,CRTC_STATE_VACTIVE ); +for (i =0 ; i <3 ; i++) { + pipe_ctx->stream_res.tg->funcs->wait_for_state (pipe_ctx->stream_res.tg,CRTC_STATE_VBLANK ); + pipe_ctx->stream_res.tg->funcs->wait_for_state (pipe_ctx->stream_res.tg,CRTC_STATE_VACTIVE ); + } } } }
Greg Kroah-Hartman
maintainerPATCH 253/337 drm/amd/display: use proper context for logging
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiri Slaby (SUSE) <[email protected]>
commit 114b42507b6a23d9d24e24e4ef165233332c64d4 upstream.
The same as the rest of the code, get_ss_info_from_atombios() uses calc_pll_cs->ctx->logger for logging. But calc_pll_cs->ctx is initialized only later in calc_pll_max_vco_construct(). Therefore, any output using DC_LOG_SYNC() leads to a NULL pointer deference in get_ss_info_from_atombios().
According to Sashiko, the very same problem exists in dce112_get_pix_clk_dividers() and dcn3_get_pix_clk_dividers() too.
To avoid accessing the NULL context, use clk_src->base.ctx->logger everywhere. That context in base is initialized earlier in dce110_clk_src_construct() and dce112_clk_src_construct(). Before get_ss_info_from_atombios() or Sashiko's get_pix_clk_dividers functions above are actually called. This is done by redefining DC_LOGGER to CTX->logger.
Before:dce110_clk_src_construct() did:
-> sets clk_src->base.ctx = ctx;
-> ss_info_from_atombios_create()
-> get_ss_info_from_atombios() <- uses calc_pll_cs->ctx # BOOM
-> calc_pll_max_vco_construct() <- sets calc_pll_cs->ctx
After:dce110_clk_src_construct() does:
-> sets clk_src->base.ctx = ctx;
-> ss_info_from_atombios_create()
-> get_ss_info_from_atombios() <- uses clk_src->base.ctx
--- drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c | 20 ++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-)
drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +9 -11
@@ -45,9 +45,7 @@ clk_src->base.ctx#define DC_LOGGER \ - calc_pll_cs->ctx->logger -#define DC_LOGGER_INIT () \ - struct calc_pll_clock_source *calc_pll_cs = &clk_src->calc_pll + CTX->logger#undef FN#define FN (reg_name, field_name) \
@@ -291,6 +289,7 @@ static bool calc_pll_dividers_in_range( }static uint32_t calculate_pixel_clock_pll_dividers ( +struct dce110_clk_src *clk_src,struct calc_pll_clock_source *calc_pll_cs,struct pll_settings *pll_settings) {
@@ -479,7 +478,7 @@ static uint32_t dce110_get_pix_clk_divid {uint32_t field =0 ;uint32_t pll_calc_error =MAX_PLL_CALC_ERROR ; -DC_LOGGER_INIT (); +/* Check if reference clock is external (not pcie/xtalin) * HW Dce80 spec: * 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB
@@ -522,12 +521,14 @@ static uint32_t dce110_get_pix_clk_divid/*Calculate Dividers by HDMI object, no SS case or SS case */ pll_calc_error =calculate_pixel_clock_pll_dividers ( + clk_src, &clk_src->calc_pll_hdmi, pll_settings); else/*Calculate Dividers by default object, no SS case or SS case */ pll_calc_error =calculate_pixel_clock_pll_dividers ( + clk_src, &clk_src->calc_pll, pll_settings);
@@ -572,7 +573,6 @@ static uint32_t dce110_get_pix_clk_divid {struct dce110_clk_src *clk_src =TO_DCE110_CLK_SRC (cs);uint32_t pll_calc_error =MAX_PLL_CALC_ERROR ; -DC_LOGGER_INIT ();if (pix_clk_params ==NULL || pll_settings ==NULL || pix_clk_params->requested_pix_clk_100hz ==0 ) {
@@ -604,7 +604,6 @@ static uint32_t dce112_get_pix_clk_divid struct pll_settings *pll_settings) {struct dce110_clk_src *clk_src =TO_DCE110_CLK_SRC (cs); -DC_LOGGER_INIT ();if (pix_clk_params ==NULL || pll_settings ==NULL || pix_clk_params->requested_pix_clk_100hz ==0 ) {
@@ -1366,8 +1365,6 @@ static uint32_t dcn3_get_pix_clk_dividerunsigned long long actual_pix_clk_100Hz = pix_clk_params ? pix_clk_params->requested_pix_clk_100hz :0 ;struct dce110_clk_src *clk_src =TO_DCE110_CLK_SRC (cs); -DC_LOGGER_INIT (); -if (pix_clk_params ==NULL || pll_settings ==NULL || pix_clk_params->requested_pix_clk_100hz ==0 ) { DC_LOG_ERROR(
@@ -1437,7 +1434,6 @@ static const struct clock_source_funcs d .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz }; - static void get_ss_info_from_atombios( struct dce110_clk_src *clk_src, enum as_signal_type as_signal,
@@ -1450,7 +1446,7 @@ static void get_ss_info_from_atombios(struct spread_spectrum_info *ss_info_cur;struct spread_spectrum_data *ss_data_cur;uint32_t i; -DC_LOGGER_INIT (); +if (ss_entries_num ==NULL ) {DC_LOG_SYNC ("Invalid entry !!!\n" );
@@ -1585,6 +1581,7 @@ static void ss_info_from_atombios_create }static bool calc_pll_max_vco_construct ( +struct dce110_clk_src *clk_src,struct calc_pll_clock_source *calc_pll_cs,struct calc_pll_clock_source_init_data *init_data) {
@@ -1736,6 +1733,7 @@ bool dce110_clk_src_construct(ss_info_from_atombios_create (clk_src);if (!calc_pll_max_vco_construct ( + clk_src, &clk_src->calc_pll, &calc_pll_cs_init_data)) {ASSERT_CRITICAL (false);
@@ -1750,7 +1748,7 @@ bool dce110_clk_src_construct(if (!calc_pll_max_vco_construct ( - &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { + clk_src, &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) {ASSERT_CRITICAL (false); goto unexpected_failure; }
Greg Kroah-Hartman
maintainerPATCH 254/337 drm/amdkfd: Fix missing authorization check in KFD_IOC_DBG_TRAP_DISABLE
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gang Ba <[email protected]>
commit 99b2fe4f19e3be0a8d0a0b5ea98d855970889653 upstream.
Prevent unauthorized termination of active GPU debug sessions. Previously, users with /dev/kfd access could terminate another process's debug session without proper ownership or ptrace authorization.
--- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +7 -3
@@ -2978,10 +2978,14 @@ static int kfd_ioctl_set_debug_trap(stru goto out; } -/* Check if target is still PTRACED. */ +/* +* Verify debugger has permission to debug target process. +* For cross-process debugging, require active ptrace relationship. +* This applies to ALL operations to prevent unauthorized interference. +*/ rcu_read_lock (); -if (target != p && args->op !=KFD_IOC_DBG_TRAP_DISABLE - &&ptrace_parent (target->lead_thread) != current) { +if (target != p &&ptrace_parent (target->lead_thread) != current + && target->debugger_process != p) {pr_err ("PID %i is not PTRACED and cannot be debugged\n" , args->pid); r = -EPERM ; }
Greg Kroah-Hartman
maintainerPATCH 255/337 drm/amdkfd: fix QID bit leak in pqm_create_queue()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vladimir Marioukhine <[email protected]>
commit 38b73293f38658a4685ffcea666462024f858ad9 upstream.
When MES is enabled and amdgpu_amdkfd_alloc_kernel_mem() fails during the first queue creation for a process, pqm_create_queue() returns early via 'return retval' without going through the err_create_queue cleanup label.
This means clear_bit(*qid, pqm->queue_slot_bitmap) is never called, leaving the reserved QID bit permanently set in queue_slot_bitmap. Over time this leaks QID slots, potentially exhausting all available queue slots.
Fix this by replacing 'return retval' with 'goto err_allocate_pqn' so that clear_bit() is always called on the error path without touching the uninitialized pqn pointer.
AILIKFD-813
--- drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +1 -1
@@ -376,7 +376,7 @@ int pqm_create_queue(struct process_queu false);if (retval) {dev_err (dev->adev->dev,"failed to allocate process context bo\n" ); -return retval; + goto err_allocate_pqn; }memset (pdd->proc_ctx_cpu_ptr,0 ,AMDGPU_MES_PROC_CTX_SIZE ); }
Greg Kroah-Hartman
maintainerPATCH 256/337 drm/amdkfd: fix uint32_t overflow in EOP ring buffer size alignment
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: William Palacek <[email protected]>
commit 83463a96ea3c7d8ae636a4d6a0ba63c9ce410724 upstream.
eop_ring_buffer_size in struct queue_properties is a u32. In kfd_queue_acquire_buffers() the expected EOP buffer size is computed as ALIGN(eop_ring_buffer_size, PAGE_SIZE); ALIGN uses typeof(x), so the addition is done in 32-bit. A user-supplied size of 0xFFFFF001 wraps to 0, causing kfd_queue_buffer_get() to skip its exact-size check (gated on size != 0) and accept any BO mapped at the address. On GFX8/GFX9 the MQD cp_hqd_eop_control is then programmed for an 8KB EOP ring backed by a 4KB BO, so CP EOP writes can land past the buffer and fault the GPU.
Cast the operand to u64 so the alignment is computed in 64-bit; the size check in kfd_queue_buffer_get() then rejects the oversized request.
--- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdkfd/kfd_queue.c +1 -1
@@ -285,7 +285,7 @@ int kfd_queue_acquire_buffers(struct kfd } err =kfd_queue_buffer_get (vm, (void *)properties->eop_ring_buffer_address, &properties->eop_buf_bo, -ALIGN (properties->eop_ring_buffer_size,PAGE_SIZE )); +ALIGN ((u64 )properties->eop_ring_buffer_size,PAGE_SIZE ));if (err) goto out_err_unreserve; }
Greg Kroah-Hartman
maintainerPATCH 257/337 drm/amdkfd: Handle invalid event type in CRIU event restore
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Francis <[email protected]>
commit a9cdc85839e4fe2c760aa4ca6cc341c31ad1918a upstream.
In kfd_criu_restore_event, there was no handling for the event priv data having an invalid event type. The priv data here is untrusted and can be invalid.
In that case, fail with EINVAL.
--- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/amd/amdkfd/kfd_events.c +3 -0
@@ -521,6 +521,9 @@ int kfd_criu_restore_event(struct file * ret =create_other_event (p, ev, &ev_priv->event_id);break ; +default : + ret = -EINVAL ; +break ; }mutex_unlock (&p->event_mutex);
Greg Kroah-Hartman
maintainerPATCH 258/337 drm/amdkfd: hold event_mutex while checkpointing CRIU events
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: William Palacek <[email protected]>
commit ff8bc5a68a9a70bdc38d61a72c7a49c56063f9d2 upstream.
kfd_criu_checkpoint_events() counts the entries in p->event_idr via kfd_get_num_events(), allocates an array sized to that count, and then walks the same IDR to fill it. Neither the count nor the walk holds p->event_mutex.
The CRIU checkpoint caller holds only p->mutex. Event create and destroy (kfd_event_create()/kfd_event_destroy()) take p->event_mutex and do not take p->mutex, so a second thread in the same process can insert or remove events between the count and the walk. If an event is inserted, the walk iterates more entries than were counted and writes past the end of the ev_privs allocation; if an event is removed, the walk dereferences an entry that is being freed.
Hold p->event_mutex across the count and the walk so both observe a consistent view of p->event_idr. The lock is released before copy_to_user(), which only touches the local buffer. The caller already holds p->mutex and the create/destroy paths never take p->mutex, so the p->mutex -> p->event_mutex order is not inverted and no deadlock is introduced.
--- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdkfd/kfd_events.c +18 -4
@@ -545,15 +545,27 @@ int kfd_criu_checkpoint_events(struct kfint ret =0 ;struct kfd_event *ev;uint32_t ev_id; +uint32_t num_events; -uint32_t num_events =kfd_get_num_events (p); - -if (!num_events) +/* Serialize the count and the walk below against concurrent event +* create/destroy. Those paths take only p->event_mutex, not the +* p->mutex held by the CRIU checkpoint caller, so without this the +* event_idr can grow between kfd_get_num_events() and the loop and the +* walk writes past the ev_privs allocation. +*/ +mutex_lock (&p->event_mutex); + + num_events =kfd_get_num_events (p); +if (!num_events) { +mutex_unlock (&p->event_mutex);return 0 ; + } ev_privs =kvzalloc (num_events *sizeof (*ev_privs),GFP_KERNEL ); -if (!ev_privs) +if (!ev_privs) { +mutex_unlock (&p->event_mutex);return -ENOMEM ; + }idr_for_each_entry (&p->event_idr, ev, ev_id) {
@@ -594,6 +606,8 @@ int kfd_criu_checkpoint_events(struct kf i++; } +mutex_unlock (&p->event_mutex); + ret =copy_to_user (user_priv_data + *priv_data_offset, ev_privs, num_events *sizeof (*ev_privs));if (ret) {
Greg Kroah-Hartman
maintainerPATCH 259/337 drm/vmwgfx: fix guest_memory_dirty bitfield clobbered as size
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 83195b778f2d109a3a4f3ffaba4dce7e4cdb58aa upstream.
Two sites in vmwgfx_resource.c assign boolean literals to res->guest_memory_size, which is an unsigned long allocation-size field; the intended target is the adjacent res->guest_memory_dirty bitfield. After the assignments the field holds 0 or 1 instead of the resource's MOB allocation size:
- vmw_resource_release() writes 0 (false), and
- vmw_resource_unbind_list() writes 1 (true).
Subsequent revalidation paths read guest_memory_size when computing the dirty page range (vmw_bo_dirty_transfer_to_res()) and the buffer allocation size (vmw_resource_buf_alloc()), producing zero-length walks or wrap-around ranges that read or write past the MOB bitmap. The dirty-tracking intent of the original code (mark the resource as dirtied since the last sync) is also lost, since guest_memory_dirty is never updated.
Rename both assignments to guest_memory_dirty.
--- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +2 -2
@@ -136,7 +136,7 @@ static void vmw_resource_release(struct val_buf.num_shared =0 ; res->func->unbind (res, false, &val_buf); } - res->guest_memory_size = false; + res->guest_memory_dirty = false;vmw_resource_mob_detach (res);if (res->dirty) res->func->dirty_free (res);
@@ -773,7 +773,7 @@ void vmw_resource_unbind_list(struct vmwif (!WARN_ON_ONCE (!res->func->unbind)) (void ) res->func->unbind (res, res->res_dirty, &val_buf); - res->guest_memory_size = true; + res->guest_memory_dirty = true; res->res_dirty = false;vmw_resource_mob_detach (res); }
Greg Kroah-Hartman
maintainerPATCH 260/337 drm/vmwgfx: reject DX_BIND_QUERY without a DX context
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 55ec09c9ce10b1272802c7ab6c1be2ea0dbc68db upstream.
vmw_cmd_dx_bind_query() unconditionally dereferences sw_context->dx_ctx_node->ctx. Userspace can trigger a NULL pointer dereference from any render-node fd by submitting an execbuf with dx_context_handle == SVGA3D_INVALID_ID and a SVGA_3D_CMD_DX_BIND_QUERY opcode in the command stream: dx_ctx_node is left NULL and the kernel oopses on the assignment. The same NULL is then re-read in vmw_resources_reserve() via vmw_context_get_dx_query_mob().
All sibling DX handlers fail-close on a missing dx_ctx_node using VMW_GET_CTX_NODE(). Use the same pattern here, returning -EINVAL up front before any relocation state is published.
--- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +5 -1
@@ -1290,9 +1290,13 @@ static int vmw_cmd_dx_bind_query(struct SVGA3dCmdHeader *header) {VMW_DECLARE_CMD_VAR (*cmd, SVGA3dCmdDXBindQuery); +struct vmw_ctx_validation_info *ctx_node =VMW_GET_CTX_NODE (sw_context);struct vmw_bo *vmw_bo;int ret; +if (!ctx_node) +return -EINVAL ; + cmd =container_of (header,typeof (*cmd), header);/*
@@ -1306,7 +1310,7 @@ static int vmw_cmd_dx_bind_query(structreturn ret; sw_context->dx_query_mob = vmw_bo; - sw_context->dx_query_ctx = sw_context->dx_ctx_node->ctx; + sw_context->dx_query_ctx = ctx_node->ctx;return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 261/337 drm/vmwgfx: drop dma_buf reference on foreign-fd prime import
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit f739416dc555fa205a785e5135d73fa39b26f35d upstream.
ttm_prime_fd_to_handle() returns -ENOSYS when the imported fd's dma_buf->ops do not match the ttm_object_device's ops, but does so without releasing the reference acquired by dma_buf_get(). Any unprivileged renderD client passing a non-vmwgfx prime fd through the DRM_VMW_GB_SURFACE_REF{,_EXT} path leaks one dma_buf reference per call and indefinitely pins the foreign exporter's GEM resources.
Funnel the error path through the existing dma_buf_put() so the reference is always dropped.
--- drivers/gpu/drm/vmwgfx/ttm_object.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/vmwgfx/ttm_object.c +5 -2
@@ -547,14 +547,17 @@ int ttm_prime_fd_to_handle(struct ttm_obif (IS_ERR (dma_buf))return PTR_ERR (dma_buf); -if (dma_buf->ops != &tdev->ops) -return -ENOSYS ; +if (dma_buf->ops != &tdev->ops) { + ret = -ENOSYS ; + goto out; + } prime = (struct ttm_prime_object *) dma_buf->priv; base = &prime->base; *handle = base->handle; ret =ttm_ref_object_add (tfile, base,NULL , false); +out:dma_buf_put (dma_buf);return ret;
Greg Kroah-Hartman
maintainerPATCH 262/337 drm/vmwgfx: validate DRAW_PRIMITIVES header size before division
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 85891d174707d8bddcec7a888fb4e1d17def34f3 upstream.
vmw_cmd_draw() computes
maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl);
where header->size is u32 and is taken straight from the user-supplied command stream. When header->size is less than sizeof(cmd->body) the unsigned subtraction wraps to nearly 4 GiB, producing a huge maxnum. Any user-controlled cmd->body.numVertexDecls then passes the bound and the loop dereferences decl[i] far past the end of the kernel command bounce buffer, producing an out-of-bounds read of kernel memory.
Reject undersized headers up front.
--- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +7 -1
@@ -1589,11 +1589,17 @@ static int vmw_cmd_draw(struct vmw_privauint32_t maxnum;int ret; + cmd =container_of (header,typeof (*cmd), header); + +if (unlikely (header->size <sizeof (cmd->body))) { +VMW_DEBUG_USER ("Illegal DRAW_PRIMITIVES header size.\n" ); +return -EINVAL ; + } + ret =vmw_cmd_cid_check (dev_priv, sw_context, header);if (unlikely (ret !=0 ))return ret; - cmd =container_of (header,typeof (*cmd), header); maxnum = (header->size -sizeof (cmd->body)) /sizeof (*decl);if (unlikely (cmd->body.numVertexDecls > maxnum)) {
Greg Kroah-Hartman
maintainerPATCH 263/337 drm/vmwgfx: bound DMA command body size against suffix pointer
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit f4f1db96bfd68b81053693ba53405b6f510ac16c upstream.
vmw_cmd_dma() locates the DMA suffix at
(unsigned long) &cmd->body + header->size - sizeof(*suffix)
without checking that header->size is large enough to contain both cmd->body and the suffix. An undersized header makes the suffix pointer underflow back into the previous command in the bounce buffer. The verifier later writes suffix->maximumOffset, clobbering verified fields of an already-relocated earlier command -- a TOCTOU on the device-visible command stream that lets one command rewrite another's GMR id, surface id, or other authenticated fields.
Reject the command if the body is too small for the suffix to fit.
--- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +6 -0
@@ -1528,6 +1528,12 @@ static int vmw_cmd_dma(struct vmw_privatbool dirty; cmd =container_of (header,typeof (*cmd), header); + +if (unlikely (header->size <sizeof (cmd->body) +sizeof (*suffix))) { +VMW_DEBUG_USER ("Illegal SVGA_3D_CMD_SURFACE_DMA size.\n" ); +return -EINVAL ; + } + suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long ) &cmd->body + header->size -sizeof (*suffix));
Greg Kroah-Hartman
maintainerPATCH 264/337 drm/vmwgfx: avoid destroy_workqueue(NULL) on vkms init failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 05eaa887e7b4f40fba425f8a1d7a5a8a043092a6 upstream.
Two paths through vmw_vkms_init() can leave vmw->crc_workq NULL while still leaving the rest of the driver in a state that calls vmw_vkms_cleanup() at module unload:
1. vmw_host_get_guestinfo(GUESTINFO_VBLANK, ...) failing or returning an oversized buffer -- the common case on hosts without a VBLANK guestinfo entry -- early-returned before the workqueue allocation.
2. alloc_ordered_workqueue() returning NULL on memory pressure.
vmw_vkms_cleanup() then calls destroy_workqueue(NULL), which dereferences wq->name and panics.
Fix the first case by removing the early return: vmw->vkms_enabled is already false on the rpci-failure path so no work will ever be queued, and allocating the workqueue unconditionally keeps the control flow simple. Fix the second case by guarding the cleanup with a NULL check, since alloc_ordered_workqueue() can still fail under low memory.
--- drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c +9 -8
@@ -214,14 +214,14 @@ vmw_vkms_init(struct vmw_private *vmw) vmw->vkms_enabled = false; ret =vmw_host_get_guestinfo (GUESTINFO_VBLANK , buffer, &buf_len); -if (ret || buf_len > max_buf_len) -return ; - buffer[buf_len] ='\0' ; +if (!ret && buf_len <= max_buf_len) { + buffer[buf_len] ='\0' ; - ret =kstrtobool (buffer, &vmw->vkms_enabled); -if (!ret && vmw->vkms_enabled) { - ret =drm_vblank_init (&vmw->drm,VMWGFX_NUM_DISPLAY_UNITS ); - vmw->vkms_enabled = (ret ==0 ); + ret =kstrtobool (buffer, &vmw->vkms_enabled); +if (!ret && vmw->vkms_enabled) { + ret =drm_vblank_init (&vmw->drm,VMWGFX_NUM_DISPLAY_UNITS ); + vmw->vkms_enabled = (ret ==0 ); + } } vmw->crc_workq =alloc_ordered_workqueue ("vmwgfx_crc_generator" ,0 );
@@ -236,7 +236,8 @@ vmw_vkms_init(struct vmw_private *vmw)void vmw_vkms_cleanup (struct vmw_private *vmw) { -destroy_workqueue (vmw->crc_workq); +if (vmw->crc_workq) +destroy_workqueue (vmw->crc_workq); }bool
Greg Kroah-Hartman
maintainerPATCH 265/337 drm/vmwgfx: use check_add_overflow for shader size+offset bound
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 54d56d5b42d2e4c72ba6e365e9774da90698aa22 upstream.
vmw_shader_define() validates the user-supplied shader window against its backing buffer with
(u64)buffer->tbo.base.size < (u64)size + (u64)offset
drm_vmw_shader_create_arg::offset is __u64 in the uapi; when it is near U64_MAX the unsigned addition wraps and the resulting tiny value passes the check. The unbounded offset is then stored in res->guest_memory_offset and forwarded to host SVGA shader-create commands.
Use check_add_overflow() to detect the wrap and compare the resulting endpoint against the buffer size.
--- drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +9 -4
@@ -25,6 +25,8 @@* **************************************************************************/ +#include <linux/overflow.h> +#include <drm/ttm/ttm_placement.h> #include "vmwgfx_binding.h"
@@ -685,7 +687,7 @@ int vmw_shader_destroy_ioctl(struct drm_ static int vmw_user_shader_alloc(struct vmw_private *dev_priv, struct vmw_bo *buffer, size_t shader_size, - size_t offset, + u64 offset, SVGA3dShaderType shader_type, uint8_t num_input_sig, uint8_t num_output_sig,
@@ -739,7 +741,7 @@ out:static struct vmw_resource *vmw_shader_alloc (struct vmw_private *dev_priv,struct vmw_bo *buffer,size_t shader_size, -size_t offset, +u64 offset,SVGA3dShaderType shader_type) {struct vmw_shader *shader;
@@ -768,7 +770,7 @@ out_err:static int vmw_shader_define (struct drm_device *dev,struct drm_file *file_priv,enum drm_vmw_shader_type shader_type_drm, -u32 buffer_handle,size_t size,size_t offset, +u32 buffer_handle,size_t size,u64 offset,uint8_t num_input_sig,uint8_t num_output_sig,uint32_t *shader_handle) {
@@ -779,13 +781,16 @@ static int vmw_shader_define(struct drm_int ret;if (buffer_handle !=SVGA3D_INVALID_ID ) { +u64 end; + ret =vmw_user_bo_lookup (file_priv, buffer_handle, &buffer);if (unlikely (ret !=0 )) {VMW_DEBUG_USER ("Couldn't find buffer for shader creation.\n" );return ret; } -if ((u64 )buffer->tbo.base.size < (u64 )size + (u64 )offset) { +if (check_add_overflow ((u64 )size, (u64 )offset, &end) || + end > buffer->tbo.base.size) {VMW_DEBUG_USER ("Illegal buffer- or shader size.\n" ); ret = -EINVAL ; goto out_bad_arg;
Greg Kroah-Hartman
maintainerPATCH 266/337 drm/vmwgfx: validate external BO copy bounds for both stride paths
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 706c93c5813caabbb0d0a576c017d15aeec2c113 upstream.
vmw_external_bo_copy() trusts caller-supplied offsets, strides, and heights and operates on imported dma-buf vmaps:
- The equal-stride memcpy() bound was clamped after subtracting the offsets from dst_size and src_size; an offset larger than the BO size wraps the unsigned subtraction to a huge value and the resulting memcpy() runs off the end of the vmap. dst_stride * height is also a u32 multiplication that can overflow.
- The non-equal-stride row-by-row path had no bound at all. The loop touches bytes through offset + (height - 1) * stride + width_in_bytes, with only a WARN_ON(dst_stride < width_in_bytes), and could likewise step past the end of either mapping.
The offsets and strides are derived from STDU/SOU plane state, so a configured CRTC submitting a crafted atomic commit on an imported framebuffer can reach this path.
Validate the exact row-copy endpoint against each BO's size up front using check_mul_overflow() and check_add_overflow(). Use the bulk memcpy() path only when width_in_bytes covers the whole stride; otherwise copy one row at a time so partial-row updates near the bottom of a framebuffer remain valid. Also reject zero strides and stride < width_in_bytes, both of which the row-by-row path cannot represent safely.
--- drivers/gpu/drm/vmwgfx/vmwgfx_blit.c | 39 +++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_blit.c +31 -8
@@ -30,6 +30,7 @@#include "vmwgfx_bo.h" #include <linux/highmem.h> +#include <linux/overflow.h> /* * Template that implements find_first_diff() for a generic
@@ -463,19 +464,42 @@ static int vmw_external_bo_copy(struct v container_of(dst->tbo.bdev, struct vmw_private, bdev);size_t dst_size = dst->tbo.resource->size;size_t src_size = src->tbo.resource->size; +size_t dst_end, src_end;struct iosys_map dst_map = {0 };struct iosys_map src_map = {0 }; +bool dst_mapped = false; +bool src_mapped = false;int ret, i;int x_in_bytes;u8 *vsrc;u8 *vdst; +if (!height || !width_in_bytes) +return 0 ; + +if (!dst_stride || !src_stride) +return -EINVAL ; +if (dst_stride < width_in_bytes || src_stride < width_in_bytes) +return -EINVAL ; +if (check_mul_overflow ((size_t )dst_stride, (size_t )height -1 , &dst_end) || +check_add_overflow (dst_end, (size_t )width_in_bytes, &dst_end) || +check_add_overflow ((size_t )dst_offset, dst_end, &dst_end) || + dst_end > dst_size || +check_mul_overflow ((size_t )src_stride, (size_t )height -1 , &src_end) || +check_add_overflow (src_end, (size_t )width_in_bytes, &src_end) || +check_add_overflow ((size_t )src_offset, src_end, &src_end) || + src_end > src_size) { +drm_dbg_driver (&vmw->drm,"Out-of-bounds external BO copy\n" ); +return -EINVAL ; + } + vsrc =map_external (src, &src_map);if (!vsrc) {drm_dbg_driver (&vmw->drm,"Wasn't able to map src\n" ); ret = -ENOMEM ; goto out; } + src_mapped = true; vdst =map_external (dst, &dst_map);if (!vdst) {
@@ -483,16 +507,13 @@ static int vmw_external_bo_copy(struct v ret = -ENOMEM ; goto out; } + dst_mapped = true; vsrc += src_offset; vdst += dst_offset; -if (src_stride == dst_stride) { - dst_size -= dst_offset; - src_size -= src_offset; -memcpy (vdst, vsrc, -min (dst_stride * height,min (dst_size, src_size))); +if (src_stride == dst_stride && width_in_bytes == dst_stride) { +memcpy (vdst, vsrc, dst_stride * (size_t )height); }else { -WARN_ON (dst_stride < width_in_bytes);for (i =0 ; i < height; ++i) {memcpy (vdst, vsrc, width_in_bytes); vsrc += src_stride;
@@ -508,8 +529,10 @@ static int vmw_external_bo_copy(struct v ret =0 ; out: -unmap_external (src, &src_map); -unmap_external (dst, &dst_map); +if (src_mapped) +unmap_external (src, &src_map); +if (dst_mapped) +unmap_external (dst, &dst_map);return ret; }
Greg Kroah-Hartman
maintainerPATCH 267/337 spi: spi-cadence: enable SPI_CONTROLLER_MUST_TX
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jun Guo <[email protected]>
commit f6b625639e39bc384a7bddbf134a698d40258b3b upstream.
During an SPI read operation, even if the xspi->txbuf passed to the cdns_spi_writerinterface is empty, it is still necessary to call cdns_spi_write(xspi, CDNS_SPI_TXD, txw); otherwise, the read operation will fail to obtain data correctly due to a lack of clocks.
--- drivers/spi/spi-cadence.c | 1 + 1 file changed, 1 insertion(+)
drivers/spi/spi-cadence.c +1 -0
@@ -735,6 +735,7 @@ static int cdns_spi_probe(struct platfor ctlr->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware; ctlr->mode_bits =SPI_CPOL |SPI_CPHA ; ctlr->bits_per_word_mask =SPI_BPW_MASK (8 ); + ctlr->flags =SPI_CONTROLLER_MUST_TX ;if (of_device_is_compatible (pdev->dev.of_node,"cix,sky1-spi-r1p6" )) ctlr->bits_per_word_mask |=SPI_BPW_MASK (16 ) |SPI_BPW_MASK (32 );
Greg Kroah-Hartman
maintainerPATCH 268/337 HID: logitech-dj: Fix maxfield check in DJ short report validation
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit 590cc4d782487632a52f37c2171bee1eeea29627 upstream.
Commit b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") added validation for the DJ short output report, but the error path dereferences rep->field[0] even when rep->maxfield is zero.
Commit 8b9a097eb2fc ("HID: logitech-dj: fix wrong detection of bad DJ_SHORT output report") made the check conditional on rep being present, but a crafted descriptor can still create report ID 0x20 with only padding output items. hid-core registers the report, ignores the padding field, and leaves rep->maxfield as zero.
In that case the validation enters the rep->maxfield < 1 branch and then dereferences rep->field[0]->report_count while printing the error message, causing a NULL pointer dereference during probe. This is reproducible with uhid by emulating a Logitech receiver with a padding-only DJ short output report:
BUG: KASAN: null-ptr-deref in logi_dj_probe+0xb1/0x754 [hid_logitech_dj] Read of size 4 at addr 0000000000000028 by task kworker/4:1/129 ... Call Trace: logi_dj_probe+0xb1/0x754 [hid_logitech_dj] hid_device_probe+0x329/0x3f0 [hid] really_probe+0x162/0x570 __device_attach+0x137/0x2c0 bus_probe_device+0x38/0xc0 device_add+0xa56/0xce0 hid_add_device+0x19c/0x280 [hid] uhid_device_add_worker+0x2c/0xb0 [uhid]
Reject the zero-field report before printing the field report_count.
--- drivers/hid/hid-logitech-dj.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/hid/hid-logitech-dj.c +7 -2
@@ -1797,8 +1797,13 @@ static int logi_dj_probe(struct hid_devi output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT ]; rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT ]; -if (rep && (rep->maxfield <1 || - rep->field[0 ]->report_count !=DJREPORT_SHORT_LENGTH -1 )) { +if (rep && rep->maxfield <1 ) { +hid_err (hdev,"Expected size of DJ short report is %d, but got 0" , +DJREPORT_SHORT_LENGTH -1 ); +return -EINVAL ; + } + +if (rep && rep->field[0 ]->report_count !=DJREPORT_SHORT_LENGTH -1 ) {hid_err (hdev,"Expected size of DJ short report is %d, but got %d" ,DJREPORT_SHORT_LENGTH -1 , rep->field[0 ]->report_count);return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 269/337 ata: libahci_platform: Do not set mask_port_map when not needed
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Damien Le Moal <[email protected]>
commit 2c202e6c4f4dd19d2e8c1dfac9df05170aa3934f upstream.
Commit 8c87215dd3a2 ("ata: libahci_platform: support non-consecutive port numbers") modified ahci_platform_get_resources() to allow identifying the ports of a controller that are defined as child nodes of the controller node in order to support non-consecutive port numbers (as defined by the platform device tree).
However, this commit also erroneously sets bit 0 of hpriv->mask_port_map when the platform devices tree does not define port child nodes, to match the fact that the temporary default number of ports used in that case is 1 (which is also consistent with the fact that only index 0 of hpriv->phys[] is initialized with the call to ahci_platform_get_phy(). But doing so causes ahci_platform_init_host() to initialize and probe only the first port, even if this function determines that the controller has in fact multiple ports using the capability register of the controller (through a call to ahci_nr_ports()). This can be seen with the ahci_mvebu driver (Armada 385 SoC) with the second port declared as "dummy":
ahci-mvebu f10a8000.sata: masking port_map 0x3 -> 0x1
ahci-mvebu f10a8000.sata: AHCI vers 0001.0000, 32 command slots, 6 Gbps, platform mode ahci-mvebu f10a8000.sata: 1/2 ports implemented (port mask 0x1) ahci-mvebu f10a8000.sata: flags: 64bit ncq sntf led only pmp fbs pio slum part sxs scsi host0: ahci-mvebu
scsi host1: ahci-mvebu
ata1: SATA max UDMA/133 mmio [mem 0xf10a8000-0xf10a9fff] port 0x100 irq 40 lpm-pol 0
ata2: DUMMY
Fix this issue by removing setting bit 0 of hpriv->mask_port_map when the platform device tree does not define port child nodes.
--- drivers/ata/libahci_platform.c | 2 -- 1 file changed, 2 deletions(-)
drivers/ata/libahci_platform.c +0 -2
@@ -651,8 +651,6 @@ struct ahci_host_priv *ahci_platform_get* If no sub-node was found, keep this for device tree * compatibility */ - hpriv->mask_port_map |=BIT (0 ); - rc =ahci_platform_get_phy (hpriv,0 , dev, dev->of_node);if (rc) goto err_out;
Greg Kroah-Hartman
maintainerPATCH 270/337 ata: ahci: Make ahci_ignore_port() handle empty mask_port_map
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Niklas Cassel <[email protected]>
commit 130ff5c8b78e6fd05270a04985c50bce6a3de6c1 upstream.
Commit 8c87215dd3a2 ("ata: libahci_platform: support non-consecutive port numbers") added a skip to ahci_platform_enable_phys() for ports that are not in mask_port_map.
The code in ahci_platform_get_resources(), will currently set mask_port_map for each child "port" node it finds in the device tree.
However, device trees that do not have any child "port" nodes will not have mask_port_map set, and for non-device tree platforms mask_port_map will only exist as a quirk for specific PCI device + vendor IDs, or as a kernel module parameter, but will not be set by default.
Therefore, the common thing is that mask_port_map is only set if you do not want to use all ports (as defined by Offset 0Ch: PI – Ports Implemented register), but instead only want to use the ports in mask_port_map. If mask_port_map is not set, all ports are available.
Thus, ahci_ignore_port() must be able to handle an empty mask_port_map.
--- drivers/ata/ahci.h | 8 ++++++-- drivers/ata/libahci.c | 1 + 2 files changed, 7 insertions(+), 2 deletions(-)
drivers/ata/ahci.h +6 -2
@@ -387,8 +387,12 @@ struct ahci_host_priv {static inline bool ahci_ignore_port (struct ahci_host_priv *hpriv,unsigned int portid) { -return portid >= hpriv->nports || - !(hpriv->mask_port_map & (1 << portid)); +if (portid >= hpriv->nports) +return true; +/* mask_port_map not set means that all ports are available */ +if (!hpriv->mask_port_map) +return false; +return !(hpriv->mask_port_map & (1 << portid)); }extern int ahci_ignore_sss;
drivers/ata/libahci.c +1 -0
@@ -542,6 +542,7 @@ void ahci_save_initial_config(struct dev hpriv->saved_port_map = port_map; } +/* mask_port_map not set means that all ports are available */ if (hpriv->mask_port_map) { dev_warn(dev, "masking port_map 0x%lx -> 0x%lx\n", port_map,
Greg Kroah-Hartman
maintainerPATCH 271/337 of: reserved_mem: avoid post-init UAF when alloc_reserved_mem_array() fails
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wandun Chen <[email protected]>
commit e1686ca81dbf3edbde589b7daf312b45cbf76e03 upstream.
The global pointer 'reserved_mem' continues to reference the reserved_mem_array which lives in __initdata if alloc_reserved_mem_array() fails. of_reserved_mem_lookup() is exported for post-init use, that would dereference freed memory and trigger a use-after-free.
So reset reserved_mem_count to 0 when alloc_reserved_mem_array() fails.
--- drivers/of/of_reserved_mem.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-)
drivers/of/of_reserved_mem.c +19 -9
@@ -70,29 +70,32 @@ static int __init early_init_dt_alloc_re* the initial static array is copied over to this new array and * the new array is used from this point on. */ -static void __init alloc_reserved_mem_array (void ) +static int __init alloc_reserved_mem_array (void ) {struct reserved_mem *new_array;size_t alloc_size, copy_size, memset_size; +int ret; + +if (!total_reserved_mem_cnt) +return 0 ; alloc_size =array_size (total_reserved_mem_cnt,sizeof (*new_array));if (alloc_size ==SIZE_MAX ) { -pr_err ("Failed to allocate memory for reserved_mem array with err: %d" , -EOVERFLOW ); -return ; + ret = -EOVERFLOW ; + goto fail; } new_array =memblock_alloc (alloc_size,SMP_CACHE_BYTES );if (!new_array) { -pr_err ("Failed to allocate memory for reserved_mem array with err: %d" , -ENOMEM ); -return ; + ret = -ENOMEM ; + goto fail; } copy_size =array_size (reserved_mem_count,sizeof (*new_array));if (copy_size ==SIZE_MAX ) {memblock_free (new_array, alloc_size); - total_reserved_mem_cnt =MAX_RESERVED_REGIONS ; -pr_err ("Failed to allocate memory for reserved_mem array with err: %d" , -EOVERFLOW ); -return ; + ret = -EOVERFLOW ; + goto fail; } memset_size = alloc_size - copy_size;
@@ -101,6 +104,12 @@ static void __init alloc_reserved_mem_armemset (new_array + reserved_mem_count,0 , memset_size); reserved_mem = new_array; +return 0 ; + +fail: +pr_err ("Failed to allocate memory for reserved_mem array with err: %d" , ret); + reserved_mem_count =0 ; +return ret; }static void __init fdt_init_reserved_mem_node (struct reserved_mem *rmem);
@@ -246,7 +255,8 @@ void __init fdt_scan_reserved_mem_reg_no }/* Attempt dynamic allocation of a new reserved_mem array */ -alloc_reserved_mem_array (); +if (alloc_reserved_mem_array ()) +return ;if (__reserved_mem_check_root (node)) {pr_err ("Reserved memory: unsupported node format, ignoring\n" );
Greg Kroah-Hartman
maintainerPATCH 272/337 Bluetooth: ISO: fix CONNECTED -> CLOSED transition on shutdown/release
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
commit 0786469ee242952008628ed0e2d386098e2065ab upstream.
Commit d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon") merged a version of the UAF fix that breaks releasing connected
ISO sockets. Since hci_conn::iso_data is set to NULL, iso_chan_del() won't be called when the hci_conn disconnects, and the ISO socket does not emit POLLHUP correctly.
Fix by retaining full hci_conn <-> iso_conn association while in BT_DISCONNECT state, so that local disconnect via shutdown() follows similar ISO socket code path as remote disconnect. Use a separate flag to track whether hci_conn_drop() is needed, instead of setting iso_conn::hcon = NULL
In iso_sock_ready(), disallow disconnecting socket going BT_CONNECTED, in case hcon connects while its drop is pending.
--- net/bluetooth/iso.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-)
net/bluetooth/iso.c +20 -6
@@ -23,8 +23,14 @@ static struct bt_sock_list iso_sk_list = };/* ---- ISO connections ---- */ +enum { +ISO_CONN_DROPPED , + __ISO_CONN_NUM_FLAGS +}; +struct iso_conn {struct hci_conn *hcon; + DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS);/* @lock: spinlock protecting changes to iso_conn fields */ spinlock_t lock;
@@ -105,7 +111,8 @@ static void iso_conn_free(struct kref *rif (conn->hcon) { conn->hcon->iso_data =NULL ; -hci_conn_drop (conn->hcon); +if (!test_and_set_bit (ISO_CONN_DROPPED , conn->flags)) +hci_conn_drop (conn->hcon); }kfree_skb (conn->rx_skb);
@@ -299,6 +306,7 @@ static int __iso_chan_add(struct iso_coniso_pi (sk)->conn = conn; conn->sk = sk; +clear_bit (ISO_CONN_DROPPED , conn->flags);if (parent)bt_accept_enqueue (parent, sk, true);
@@ -813,11 +821,8 @@ static void iso_sock_disconn(struct sock } sk->sk_state =BT_DISCONN ; -iso_conn_lock (iso_pi (sk)->conn); -hci_conn_drop (iso_pi (sk)->conn->hcon); -iso_pi (sk)->conn->hcon->iso_data =NULL ; -iso_pi (sk)->conn->hcon =NULL ; -iso_conn_unlock (iso_pi (sk)->conn); +if (!test_and_set_bit (ISO_CONN_DROPPED ,iso_pi (sk)->conn->flags)) +hci_conn_drop (iso_pi (sk)->conn->hcon); }static void __iso_sock_close (struct sock *sk)
@@ -1894,9 +1899,18 @@ static void iso_sock_ready(struct sock *return ;lock_sock (sk); + +switch (sk->sk_state) { +case BT_DISCONN : +case BT_CLOSED : +release_sock (sk); +return ; + } +iso_sock_clear_timer (sk); sk->sk_state =BT_CONNECTED ; sk->sk_state_change (sk); +release_sock (sk); }
Greg Kroah-Hartman
maintainerPATCH 273/337 drm/xe/rtp: Refactor OAG MMIO trigger register whitelisting
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
[ Upstream commit ed455775c5a68b75e5f6ad6c8e0e3e9c98fd3f64 ]
Minor refactor of OAG MMIO trigger register whitelisting for code reuse with OAM MMIO trigger register whitelisting.
--- drivers/gpu/drm/xe/xe_reg_whitelist.c | 29 ++++++++++++--------------- 1 file changed, 13 insertions(+), 16 deletions(-)
drivers/gpu/drm/xe/xe_reg_whitelist.c +13 -16
@@ -64,27 +64,24 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(WHITELIST(CSBE_DEBUG_STATUS(RENDER_RING_BASE), 0)) }, - {XE_RTP_NAME ("oa_reg_render" ), + +#define WHITELIST_OA_MMIO_TRG (trg, status, head) \ + WHITELIST(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ + WHITELIST(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ + WHITELIST(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4) + +#define WHITELIST_OAG_MMIO_TRG \ + WHITELIST_OA_MMIO_TRG(OAG_MMIOTRIGGER, OAG_OASTATUS, OAG_OAHEADPTR) + + {XE_RTP_NAME ("oag_mmio_trg_rcs" ),XE_RTP_RULES (GRAPHICS_VERSION_RANGE (1200 ,XE_RTP_END_VERSION_UNDEFINED ),ENGINE_CLASS (RENDER )), -XE_RTP_ACTIONS (WHITELIST (OAG_MMIOTRIGGER , -RING_FORCE_TO_NONPRIV_ACCESS_RW ), -WHITELIST (OAG_OASTATUS , -RING_FORCE_TO_NONPRIV_ACCESS_RD ), -WHITELIST (OAG_OAHEADPTR , -RING_FORCE_TO_NONPRIV_ACCESS_RD | -RING_FORCE_TO_NONPRIV_RANGE_4 )) +XE_RTP_ACTIONS (WHITELIST_OAG_MMIO_TRG ) }, - { XE_RTP_NAME("oa_reg_compute"), + { XE_RTP_NAME("oag_mmio_trg_ccs"),XE_RTP_RULES (GRAPHICS_VERSION_RANGE (1200 ,XE_RTP_END_VERSION_UNDEFINED ),ENGINE_CLASS (COMPUTE )), -XE_RTP_ACTIONS (WHITELIST (OAG_MMIOTRIGGER , -RING_FORCE_TO_NONPRIV_ACCESS_RW ), -WHITELIST (OAG_OASTATUS , -RING_FORCE_TO_NONPRIV_ACCESS_RD ), -WHITELIST (OAG_OAHEADPTR , -RING_FORCE_TO_NONPRIV_ACCESS_RD | -RING_FORCE_TO_NONPRIV_RANGE_4 )) +XE_RTP_ACTIONS (WHITELIST_OAG_MMIO_TRG ) }, {} };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 274/337 drm/xe: Introduce xe_gt_dbg_printer()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michal Wajdeczko <[email protected]>
[ Upstream commit 26582fc61a923e81832df8d5a169bbdab095d15e ]
We didn't have GT-oriented debug level printer as it was hard to correctly show actual callsite annotation. But this is now doable from commit c2ef66e9ad88 ("drm/print: Improve drm_dbg_printer").
--- drivers/gpu/drm/xe/xe_gt_printk.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
drivers/gpu/drm/xe/xe_gt_printk.h +31 -0
@@ -60,6 +60,21 @@ static inline void __xe_gt_printfn_info(struct drm_printer *p, struct va_formatxe_gt_info (gt,"%pV" , vaf); } +static inline void __xe_gt_printfn_dbg (struct drm_printer *p,struct va_format *vaf) +{ +struct xe_gt *gt = p->arg; +struct drm_printer dbg; + +/* +* The original xe_gt_dbg() callsite annotations are useless here, +* redirect to the tweaked drm_dbg_printer() instead. +*/ + dbg =drm_dbg_printer (>_to_xe (gt)->drm,DRM_UT_DRIVER ,NULL ); + dbg.origin = p->origin; + +drm_printf (&dbg,"GT%u: %pV" , gt->info.id, vaf); +} +/** * xe_gt_err_printer - Construct a &drm_printer that outputs to xe_gt_err() * @gt: the &xe_gt pointer to use in xe_gt_err()
@@ -90,4 +105,20 @@ static inline struct drm_printer xe_gt_info_printer(struct xe_gt *gt) return p; } +/** +* xe_gt_dbg_printer - Construct a &drm_printer that outputs like xe_gt_dbg() +* @gt: the &xe_gt pointer to use in xe_gt_dbg() +* +* Return: The &drm_printer object. +*/ +static inline struct drm_printer xe_gt_dbg_printer (struct xe_gt *gt) +{ +struct drm_printer p = { + .printfn = __xe_gt_printfn_dbg, + .arg = gt, + .origin = (const void *)_THIS_IP_, + }; +return p; +} +#endif
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 275/337 drm/xe: Apply whitelist to engine save-restore
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lucas De Marchi <[email protected]>
[ Upstream commit 3fcf68d7393fd622b96cd45124ccb1eddf77684c ]
Changes in xe_guc_ads.c in the above commit are not needed for linux-6.12.y.
Instead of handling the whitelist directly in the GuC ADS initialization, make it follow the same logic as other engine registers that are save-restored. Main benefit is that then the SW tracking then shows it in debugfs and there's no risk of an engine workaround to write to the same nopriv register that is being passed directly to GuC.
This means that xe_reg_whitelist_process_engine() only has to process the RTP and convert them to entries for the hwe. With that all the registers should be covered by xe_reg_sr_apply_mmio() to write to the HW and there's no special handling in GuC ADS to also add these registers to the list of registers that is passed to GuC.
Example for DG2:
# cat /sys/kernel/debug/dri/0000\:03\:00.0/gt0/register-save-restore ... Engine rcs0 ... REG[0x24d0] clr=0xffffffff set=0x1000dafc masked=no mcr=no REG[0x24d4] clr=0xffffffff set=0x1000db01 masked=no mcr=no REG[0x24d8] clr=0xffffffff set=0x0000db1c masked=no mcr=no ... Whitelist rcs0 REG[0xdafc-0xdaff]: allow read access REG[0xdb00-0xdb1f]: allow read access REG[0xdb1c-0xdb1f]: allow rw access
v2:
- Use ~0u for clr bits so it's just a write (Matt Roper)
- Simplify helpers now that unused slots are not written
--- drivers/gpu/drm/xe/xe_gt.c | 4 +- drivers/gpu/drm/xe/xe_hw_engine.c | 1 - drivers/gpu/drm/xe/xe_reg_sr.c | 53 --------------------------- drivers/gpu/drm/xe/xe_reg_whitelist.c | 37 +++++++++++++++++++ 4 files changed, 38 insertions(+), 57 deletions(-)
drivers/gpu/drm/xe/xe_gt.c +1 -3
@@ -722,10 +722,8 @@ static int do_gt_restart(struct xe_gt *gt)xe_mocs_init (gt); -for_each_hw_engine (hwe, gt, id) { +for_each_hw_engine (hwe, gt, id)xe_reg_sr_apply_mmio (&hwe->reg_sr, gt); -xe_reg_sr_apply_whitelist (hwe); - }/* Get CCS mode in sync between sw/hw */ xe_gt_apply_ccs_mode (gt);
drivers/gpu/drm/xe/xe_hw_engine.c +0 -1
@@ -546,7 +546,6 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe,xe_gt_assert (gt, gt->info.engine_mask &BIT (id));xe_reg_sr_apply_mmio (&hwe->reg_sr, gt); -xe_reg_sr_apply_whitelist (hwe); hwe->hwsp = xe_managed_bo_create_pin_map(xe, tile, SZ_4K, XE_BO_FLAG_VRAM_IF_DGFX(tile) |
drivers/gpu/drm/xe/xe_reg_sr.c +0 -53
65 lines not on this page · raw message
drivers/gpu/drm/xe/xe_reg_whitelist.c +37 -0
@@ -10,7 +10,9 @@#include "regs/xe_oa_regs.h" #include "regs/xe_regs.h" #include "xe_gt_types.h" +#include "xe_gt_printk.h" #include "xe_platform_types.h" +#include "xe_reg_sr.h" #include "xe_rtp.h" #include "xe_step.h"
@@ -86,6 +88,40 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { {} }; +static void whitelist_apply_to_hwe (struct xe_hw_engine *hwe) +{ +struct xe_reg_sr *sr = &hwe->reg_whitelist; +struct xe_reg_sr_entry *entry; +struct drm_printer p; +unsigned long reg; +unsigned int slot; + +xe_gt_dbg (hwe->gt,"Add %s whitelist to engine\n" , sr->name); + p =xe_gt_dbg_printer (hwe->gt); + + slot =0 ; +xa_for_each (&sr->xa, reg, entry) { +struct xe_reg_sr_entry hwe_entry = { + .reg =RING_FORCE_TO_NONPRIV (hwe->mmio_base, slot), + .set_bits = entry->reg.addr | entry->set_bits, + .clr_bits = ~0u , + .read_mask = entry->read_mask, + }; + +if (slot ==RING_MAX_NONPRIV_SLOTS ) { +xe_gt_err (hwe->gt, +"hwe %s: maximum register whitelist slots (%d) reached, refusing to add more\n" , + hwe->name,RING_MAX_NONPRIV_SLOTS ); +break ; + } + +xe_reg_whitelist_print_entry (&p,0 , reg, entry); +xe_reg_sr_add (&hwe->reg_sr, &hwe_entry, hwe->gt); + + slot++; + } +} +/** * xe_reg_whitelist_process_engine - process table of registers to whitelist * @hwe: engine instance to process whitelist for
@@ -99,6 +135,7 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe)struct xe_rtp_process_ctx ctx =XE_RTP_PROCESS_CTX_INITIALIZER (hwe);xe_rtp_process_to_sr (&ctx, register_whitelist, &hwe->reg_whitelist); +whitelist_apply_to_hwe (hwe); }/**
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 276/337 drm/xe/rtp: Add RING_FORCE_TO_NONPRIV_DENY to OA whitelists
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
[ Upstream commit e70086a3a06d276b4a5d9a2c51c9330c6cf72780 ]
Unconditionally whitelisting OA registers is a security violation. Set RING_FORCE_TO_NONPRIV_DENY bit in OA nonpriv slots, so that OA registers don't get whitelisted by default after probe, gt reset, resume and engine reset.
--- drivers/gpu/drm/xe/xe_reg_whitelist.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/gpu/drm/xe/xe_reg_whitelist.c +5 -3
@@ -67,10 +67,12 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { XE_RTP_ACTIONS(WHITELIST(CSBE_DEBUG_STATUS(RENDER_RING_BASE), 0)) }, +#define WHITELIST_DENY (r, f) WHITELIST(r, (f) | RING_FORCE_TO_NONPRIV_DENY) +#define WHITELIST_OA_MMIO_TRG (trg, status, head) \ - WHITELIST(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ - WHITELIST(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ - WHITELIST(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4) + WHITELIST_DENY(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ + WHITELIST_DENY(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ + WHITELIST_DENY(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4)#define WHITELIST_OAG_MMIO_TRG \ WHITELIST_OA_MMIO_TRG(OAG_MMIOTRIGGER, OAG_OASTATUS, OAG_OAHEADPTR)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 277/337 drm/xe/rtp: Maintain OA whitelists separately
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
[ Upstream commit 31e2437561621b4867c08efc890bf629d017df03 ]
Contains minor modification of the above commit because of changed API.
OA registers are dynamically whitelisted (and again dewhitelisted) on OA stream open/close. Maintaining OA whitelists separately from non-OA register whitlists simplifies this management of OA register whitelisting/dewhitelisting.
--- drivers/gpu/drm/xe/xe_gt_debugfs.c | 4 +++- drivers/gpu/drm/xe/xe_hw_engine.c | 2 ++ drivers/gpu/drm/xe/xe_hw_engine_types.h | 8 ++++++++ drivers/gpu/drm/xe/xe_reg_whitelist.c | 6 ++++++ 4 files changed, 19 insertions(+), 1 deletion(-)
drivers/gpu/drm/xe/xe_gt_debugfs.c +3 -1
@@ -191,8 +191,10 @@ static int register_save_restore(struct xe_gt *gt, struct drm_printer *p)drm_printf (p,"\n" );drm_printf (p,"Whitelist\n" ); -for_each_hw_engine (hwe, gt, id) +for_each_hw_engine (hwe, gt, id) {xe_reg_whitelist_dump (&hwe->reg_whitelist, p); +xe_reg_whitelist_dump (&hwe->oa_whitelist, p); + }xe_pm_runtime_put (gt_to_xe (gt));
drivers/gpu/drm/xe/xe_hw_engine.c +2 -0
@@ -532,6 +532,8 @@ static void hw_engine_init_early(struct xe_gt *gt, struct xe_hw_engine *hwe,hw_engine_setup_default_state (hwe);xe_reg_sr_init (&hwe->reg_whitelist, hwe->name,gt_to_xe (gt)); +xe_reg_sr_init (&hwe->oa_whitelist, hwe->name,gt_to_xe (gt)); +xe_reg_sr_init (&hwe->oa_sr, hwe->name,gt_to_xe (gt));xe_reg_whitelist_process_engine (hwe); }
drivers/gpu/drm/xe/xe_hw_engine_types.h +8 -0
@@ -128,6 +128,14 @@ struct xe_hw_engine {* @reg_whitelist: table with registers to be whitelisted */ struct xe_reg_sr reg_whitelist; +/** +* @oa_whitelist: oa registers to be whitelisted +*/ +struct xe_reg_sr oa_whitelist; +/** +* @oa_sr: oa nonpriv whitelist registers, changed on oa stream open/close +*/ +struct xe_reg_sr oa_sr;/** * @reg_lrc: LRC workaround registers */
drivers/gpu/drm/xe/xe_reg_whitelist.c +6 -0
@@ -66,6 +66,10 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { ENGINE_CLASS(RENDER)),XE_RTP_ACTIONS (WHITELIST (CSBE_DEBUG_STATUS (RENDER_RING_BASE ),0 )) }, + {} +}; + +static const struct xe_rtp_entry_sr oa_whitelist[] = { #define WHITELIST_DENY(r, f) WHITELIST(r, (f) | RING_FORCE_TO_NONPRIV_DENY)
@@ -138,6 +142,8 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe)xe_rtp_process_to_sr (&ctx, register_whitelist, &hwe->reg_whitelist);whitelist_apply_to_hwe (hwe); + +xe_rtp_process_to_sr (&ctx, oa_whitelist, &hwe->oa_whitelist); }/**
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 278/337 drm/xe/rtp: Keep track of non-OA nonpriv slots
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
[ Upstream commit 60d49ea28bb190a640bd8dc3f4c946e0811a948c ]
In order to dynamically whitelist/dewhitelist OA registers on OA stream open/close, we need to keep track of nonpriv slots occupied by non-OA register whitelists.
--- drivers/gpu/drm/xe/xe_reg_whitelist.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/xe/xe_reg_whitelist.c +5 -2
@@ -94,7 +94,7 @@ static const struct xe_rtp_entry_sr oa_whitelist[] = { {} }; -static void whitelist_apply_to_hwe (struct xe_hw_engine *hwe) +static int whitelist_apply_to_hwe (struct xe_hw_engine *hwe) {struct xe_reg_sr *sr = &hwe->reg_whitelist;struct xe_reg_sr_entry *entry;
@@ -126,6 +126,8 @@ static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) slot++; } + +return slot; }/**
@@ -139,9 +141,10 @@ static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe)void xe_reg_whitelist_process_engine (struct xe_hw_engine *hwe) {struct xe_rtp_process_ctx ctx =XE_RTP_PROCESS_CTX_INITIALIZER (hwe); +int first_oa_slot;xe_rtp_process_to_sr (&ctx, register_whitelist, &hwe->reg_whitelist); -whitelist_apply_to_hwe (hwe); + first_oa_slot =whitelist_apply_to_hwe (hwe);xe_rtp_process_to_sr (&ctx, oa_whitelist, &hwe->oa_whitelist); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 279/337 drm/xe/rtp: Generalize whitelist_apply_to_hwe
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
[ Upstream commit 4fe2844b0f0c7cdc45ca4c4c62ca56b7f26c514c ]
Generalize whitelist_apply_to_hwe to construct both non-OA and OA whitelist nonpriv registers.
--- drivers/gpu/drm/xe/xe_reg_whitelist.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
drivers/gpu/drm/xe/xe_reg_whitelist.c +6 -5
@@ -94,9 +94,10 @@ static const struct xe_rtp_entry_sr oa_whitelist[] = { {} }; -static int whitelist_apply_to_hwe (struct xe_hw_engine *hwe) +static int whitelist_apply_to_hwe (struct xe_hw_engine *hwe,struct xe_reg_sr *in, +struct xe_reg_sr *out,int first_slot) { -struct xe_reg_sr *sr = &hwe->reg_whitelist; +struct xe_reg_sr *sr = in;struct xe_reg_sr_entry *entry;struct drm_printer p;unsigned long reg;
@@ -105,7 +106,7 @@ static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe)xe_gt_dbg (hwe->gt,"Add %s whitelist to engine\n" , sr->name); p =xe_gt_dbg_printer (hwe->gt); - slot =0 ; + slot = first_slot;xa_for_each (&sr->xa, reg, entry) {struct xe_reg_sr_entry hwe_entry = { .reg =RING_FORCE_TO_NONPRIV (hwe->mmio_base, slot),
@@ -122,7 +123,7 @@ static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe) }xe_reg_whitelist_print_entry (&p,0 , reg, entry); -xe_reg_sr_add (&hwe->reg_sr, &hwe_entry, hwe->gt); +xe_reg_sr_add (out, &hwe_entry, hwe->gt); slot++; }
@@ -144,7 +145,7 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe)int first_oa_slot;xe_rtp_process_to_sr (&ctx, register_whitelist, &hwe->reg_whitelist); - first_oa_slot =whitelist_apply_to_hwe (hwe); + first_oa_slot =whitelist_apply_to_hwe (hwe, &hwe->reg_whitelist, &hwe->reg_sr,0 );xe_rtp_process_to_sr (&ctx, oa_whitelist, &hwe->oa_whitelist); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 280/337 drm/xe/rtp: Save OA nonpriv registers to register save/restore lists
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
[ Upstream commit a19a83721a28ccaddace846da70da5c53d7dd052 ]
Now we can save OA whitelisting nonpriv registers to register save/restore lists. OA nonpriv registers are saved to both hwe->oa_sr as well as hwe->reg_sr.
During probe, resume and gt-reset flows KMD will apply hwe->reg_sr, ensuring OA registers are de-whitelisted after these events. For engine-reset, hwe->reg_sr is registered with GuC and GuC will apply these registers, ensuring OA registers are de-whitelisted after engine resets.
hwe->oa_sr is used for whitelisting or de-whitelisting OA registers during OA operation, by toggling the 'deny' bit on oa stream open/close.
--- drivers/gpu/drm/xe/xe_reg_whitelist.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/gpu/drm/xe/xe_reg_whitelist.c +12 -0
@@ -148,6 +148,18 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) first_oa_slot =whitelist_apply_to_hwe (hwe, &hwe->reg_whitelist, &hwe->reg_sr,0 );xe_rtp_process_to_sr (&ctx, oa_whitelist, &hwe->oa_whitelist); + +/* +* Save oa nonpriv registers to hwe->oa_sr, from which oa registers are whitelisted +* or de-whitelisted, by toggling the 'deny' bit on oa stream open/close +*/ +whitelist_apply_to_hwe (hwe, &hwe->oa_whitelist, &hwe->oa_sr, first_oa_slot); + +/* +* Also save oa nonpriv registers to hwe->reg_sr, to ensure oa registers are not +* whitelisted by default after probe, gt reset, resume and engine reset +*/ +whitelist_apply_to_hwe (hwe, &hwe->oa_whitelist, &hwe->reg_sr, first_oa_slot); }/**
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 281/337 drm/xe/rtp: Toggle deny bit to (de-)whitelist OA regs
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
[ Upstream commit b422babd77fac2c96b92db484050e460899bddaf ]
Whitelist or de-whitelist OA registers by setting or resetting the 'deny' bit in OA nonpriv registers and writing new register values to HW.
--- drivers/gpu/drm/xe/xe_reg_whitelist.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
drivers/gpu/drm/xe/xe_reg_whitelist.c +15 -0
@@ -162,6 +162,21 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe)whitelist_apply_to_hwe (hwe, &hwe->oa_whitelist, &hwe->reg_sr, first_oa_slot); } +__maybe_unused static void __whitelist_oa_regs (struct xe_hw_engine *hwe,bool whitelist) +{ +struct xe_reg_sr_entry *entry; +unsigned long reg; + +xa_for_each (&hwe->oa_sr.xa, reg, entry) { +if (whitelist) + entry->set_bits &= ~RING_FORCE_TO_NONPRIV_DENY ; +else + entry->set_bits |=RING_FORCE_TO_NONPRIV_DENY ; + } + +xe_reg_sr_apply_mmio (&hwe->oa_sr, hwe->gt); +} +/** * xe_reg_whitelist_print_entry - print one whitelist entry * @p: DRM printer
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 282/337 drm/xe/rtp: (De-)whitelist OA registers for all hwes for a gt
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
[ Upstream commit ebba7ce65252a4ab0e3794ff14854df2afca5c08 ]
Whitelist or de-whitelist OA registers for all hwe's on the gt on which the OA stream is opened. This simplifies the case where an oa unit has 0 attached hwe's (but which monitors OA events on the associated GT).
--- drivers/gpu/drm/xe/xe_reg_whitelist.c | 34 +++++++++++++++++++++++++-- drivers/gpu/drm/xe/xe_reg_whitelist.h | 4 ++++ 2 files changed, 36 insertions(+), 2 deletions(-)
drivers/gpu/drm/xe/xe_reg_whitelist.c +32 -2
@@ -9,7 +9,7 @@#include "regs/xe_gt_regs.h" #include "regs/xe_oa_regs.h" #include "regs/xe_regs.h" -#include "xe_gt_types.h" +#include "xe_gt.h" #include "xe_gt_printk.h" #include "xe_platform_types.h" #include "xe_reg_sr.h"
@@ -162,7 +162,7 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe)whitelist_apply_to_hwe (hwe, &hwe->oa_whitelist, &hwe->reg_sr, first_oa_slot); } -__maybe_unused static void __whitelist_oa_regs (struct xe_hw_engine *hwe,bool whitelist) +static void __whitelist_oa_regs (struct xe_hw_engine *hwe,bool whitelist) {struct xe_reg_sr_entry *entry;unsigned long reg;
@@ -177,6 +177,36 @@ __maybe_unused static void __whitelist_oa_regs(struct xe_hw_engine *hwe, bool whxe_reg_sr_apply_mmio (&hwe->oa_sr, hwe->gt); } +/** +* xe_reg_whitelist_oa_regs - whitelist oa registers for gt +* @gt: gt to whitelist oa registers for +* +* Whitelist OA registers by resetting RING_FORCE_TO_NONPRIV_DENY +*/ +void xe_reg_whitelist_oa_regs (struct xe_gt *gt) +{ +struct xe_hw_engine *hwe; +enum xe_hw_engine_id id; + +for_each_hw_engine (hwe, gt, id) +__whitelist_oa_regs (hwe, true); +} + +/** +* xe_reg_dewhitelist_oa_regs - dewhitelist oa registers for gt +* @gt: gt to dewhitelist oa registers for +* +* Dewhitelist OA registers by setting RING_FORCE_TO_NONPRIV_DENY +*/ +void xe_reg_dewhitelist_oa_regs (struct xe_gt *gt) +{ +struct xe_hw_engine *hwe; +enum xe_hw_engine_id id; + +for_each_hw_engine (hwe, gt, id) +__whitelist_oa_regs (hwe, false); +} +/** * xe_reg_whitelist_print_entry - print one whitelist entry * @p: DRM printer
drivers/gpu/drm/xe/xe_reg_whitelist.h +4 -0
@@ -9,12 +9,16 @@#include <linux/types.h> struct drm_printer ; +struct xe_gt ;struct xe_hw_engine ;struct xe_reg_sr ;struct xe_reg_sr_entry ;void xe_reg_whitelist_process_engine (struct xe_hw_engine *hwe); +void xe_reg_whitelist_oa_regs (struct xe_gt *gt); +void xe_reg_dewhitelist_oa_regs (struct xe_gt *gt); +void xe_reg_whitelist_print_entry (struct drm_printer *p,unsigned int indent,u32 reg,struct xe_reg_sr_entry *entry);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 283/337 drm/xe/oa: (De-)whitelist OA registers on OA stream open/release
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
[ Upstream commit 63ddb3ad08ff4e89c108499dfec5e9be5ddc25c9 ]
Whitelist OA registers on stream open and de-whitelist on stream close/release. Whitelisting is only done when 'stream->sample' is true. 'stream->sample' is only true when (a) xe_observation_paranoid is set to false by system admin, or (b) the process is perfmon_capable(). This therefore enforces the OA register whitelisting security requirements.
--- drivers/gpu/drm/xe/xe_oa.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/gpu/drm/xe/xe_oa.c +7 -0
@@ -34,6 +34,7 @@#include "xe_oa.h" #include "xe_observation.h" #include "xe_pm.h" +#include "xe_reg_whitelist.h" #include "xe_sched_job.h" #include "xe_sriov.h" #include "xe_sync.h"
@@ -836,6 +837,9 @@ static void xe_oa_stream_destroy(struct xe_oa_stream *stream)mutex_destroy (&stream->stream_lock); +if (stream->sample) +xe_reg_dewhitelist_oa_regs (stream->gt); +xe_oa_disable_metric_set (stream);xe_exec_queue_put (stream->k_exec_q);
@@ -1868,6 +1872,9 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa, goto err_disable; } +if (stream->sample) +xe_reg_whitelist_oa_regs (stream->gt); +/* Hold a reference on the drm device till stream_fd is released */ drm_dev_get (&stream->oa->xe->drm);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 284/337 drm/xe/rtp: Ensure locking/ref counting for OA whitelists
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Dixit <[email protected]>
[ Upstream commit ef78e2a22f72c892fd6663f0760abd208d49a3e2 ]
Since multiple OA streams might be open in parallel on a gt, ensure that proper locking is in place. Also ensure that OA registers are whitelisted when the first OA stream is open and de-whitelisted after the last OA stream is closed.
--- drivers/gpu/drm/xe/xe_oa_types.h | 3 +++ drivers/gpu/drm/xe/xe_reg_whitelist.c | 10 ++++++++++ 2 files changed, 13 insertions(+)
drivers/gpu/drm/xe/xe_oa_types.h +3 -0
@@ -120,6 +120,9 @@ struct xe_oa_gt {/** @oa_unit: array of oa_units */ struct xe_oa_unit *oa_unit; + +/** @whitelist_count: number of open streams for which oa registers are whitelisted */ +u32 whitelist_count; };/**
drivers/gpu/drm/xe/xe_reg_whitelist.c +10 -0
@@ -9,6 +9,7 @@#include "regs/xe_gt_regs.h" #include "regs/xe_oa_regs.h" #include "regs/xe_regs.h" +#include "xe_assert.h" #include "xe_gt.h" #include "xe_gt_printk.h" #include "xe_platform_types.h"
@@ -188,6 +189,10 @@ void xe_reg_whitelist_oa_regs(struct xe_gt *gt)struct xe_hw_engine *hwe;enum xe_hw_engine_id id; +lockdep_assert_held (>->oa.gt_lock); +if (gt->oa.whitelist_count++) +return ; +for_each_hw_engine (hwe, gt, id)__whitelist_oa_regs (hwe, true); }
@@ -203,6 +208,11 @@ void xe_reg_dewhitelist_oa_regs(struct xe_gt *gt)struct xe_hw_engine *hwe;enum xe_hw_engine_id id; +lockdep_assert_held (>->oa.gt_lock); +xe_assert (gt_to_xe (gt), gt->oa.whitelist_count); +if (--gt->oa.whitelist_count) +return ; +for_each_hw_engine (hwe, gt, id)__whitelist_oa_regs (hwe, false); }
Signature · 1 line
2.53.0