7.0.11-rc1 review
Subsystems (6)
Greg Kroah-Hartman
maintainerPATCH 125/461 LoongArch: kprobes: Use larch_insn_text_copy() to patch instructions
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tiezhu Yang <[email protected]>
commit e3ef9a28f558d1cbf0b42d6dcd16c60da557562b upstream.
On SMP systems, kprobe handlers would occasionally fail to execute on certain CPU cores. The issue is hard to reproduce and typically occurs randomly under high system load.
The root cause is a software-side instruction hazard. According to the LoongArch Reference Manual, while the cache coherency is maintained by hardware, software must explicitly use the "IBAR" instruction to ensure the instruction fetch unit (IFU) observes the effects of recent stores.
The current arch_arm_kprobe() and arch_disarm_kprobe() only execute the "IBAR" barrier (via flush_insn_slot -> local_flush_icache_range) on the local CPU. This leaves a vulnerable window where remote CPU cores may continue executing stale instructions from their pipelines or prefetch buffers, as they have not executed an "IBAR" since the code modification.
Switch to larch_insn_text_copy() to fix this:
1. Synchronization: It uses stop_machine_cpuslocked() to synchronize all online CPUs, ensuring no CPU is executing the target code area during modification.
2. Visibility: By passing cpu_online_mask to stop_machine_cpuslocked(), the callback text_copy_cb() is executed on all online cores. Each CPU core invokes local_flush_icache_range() to execute "IBAR", clearing instruction hazards system-wide and ensuring the "break" instruction is visible to the fetch units of all cores.
3. Robustness: It properly manages memory write permissions (ROX/RW) for the kernel text segment during patching, ensuring compatibility with CONFIG_STRICT_KERNEL_RWX.
--- arch/loongarch/kernel/kprobes.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
arch/loongarch/kernel/kprobes.c +6 -4
@@ -60,16 +60,18 @@ NOKPROBE_SYMBOL(arch_prepare_kprobe);/* Install breakpoint in text */ void arch_arm_kprobe (struct kprobe *p) { - *p->addr =KPROBE_BP_INSN ; -flush_insn_slot (p); +u32 insn =KPROBE_BP_INSN ; + +larch_insn_text_copy (p->addr, &insn,LOONGARCH_INSN_SIZE ); }NOKPROBE_SYMBOL (arch_arm_kprobe);/* Remove breakpoint from text */ void arch_disarm_kprobe (struct kprobe *p) { - *p->addr = p->opcode; -flush_insn_slot (p); +u32 insn = p->opcode; + +larch_insn_text_copy (p->addr, &insn,LOONGARCH_INSN_SIZE ); }NOKPROBE_SYMBOL (arch_disarm_kprobe);
Greg Kroah-Hartman
maintainerPATCH 126/461 LoongArch: Remove unused code to avoid build warning
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huacai Chen <[email protected]>
commit 0ccc9d47cf020994097ff51827cebd04aa2b0bf4 upstream.
After commit feee6b2989165631b1 ("mm/memory_hotplug: shrink zones when offlining memory"), __remove_pages() doesn't need the "zone" parameter so the "page" variable is also unused. Remove the unused code to avoid such build warning:
arch/loongarch/mm/init.c: In function 'arch_remove_memory':arch/loongarch/mm/init.c:134:22: warning: variable 'page' set but not used [-Wunused-but-set-variable=]
134 | struct page *page = pfn_to_page(start_pfn);
--- arch/loongarch/mm/init.c | 4 ---- 1 file changed, 4 deletions(-)
arch/loongarch/mm/init.c +0 -4
@@ -93,11 +93,7 @@ void arch_remove_memory(u64 start, u64 s {unsigned long start_pfn = start >>PAGE_SHIFT ;unsigned long nr_pages = size >>PAGE_SHIFT ; -struct page *page =pfn_to_page (start_pfn); -/* With altmap the first mapped page is offset from @start */ -if (altmap) - page +=vmem_altmap_offset (altmap);__remove_pages (start_pfn, nr_pages, altmap); }#endif
Greg Kroah-Hartman
maintainerPATCH 127/461 cpufreq: intel_pstate: Use correct scaling factor on Raptor Lake-E
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rafael J. Wysocki <[email protected]>
commit 0e7c710478b3089cdfe8669347f77b163e836c4f upstream.
Raptor Lake-E has the same processor ID as Raptor Lake-S, so there is an entry in intel_hybrid_scaling_factor[] for it. It does not contain E-cores though and hybrid_get_cpu_type() returns 0 for its P-cores, so they get the default "core" scaling factor. However, the original Raptor Lake scaling factor for P-cores still needs to be used for mapping the HWP performance levels of the P-cores in Raptor Lake-E to frequency, as though they were part of a real hybrid system.
To address this, update hwp_get_cpu_scaling() to return hybrid_scaling_factor, which is the P-core scaling factor retrieved from intel_hybrid_scaling_factor[], for all CPUs that are not enumerated as E-cores.
--- drivers/cpufreq/intel_pstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/cpufreq/intel_pstate.c +1 -1
@@ -2279,7 +2279,7 @@ static int hwp_get_cpu_scaling(int cpu)* Return the hybrid scaling factor for P-cores and use the * default core scaling for E-cores. */ -if (hybrid_get_cpu_type (cpu) ==INTEL_CPU_TYPE_CORE ) +if (hybrid_get_cpu_type (cpu) !=INTEL_CPU_TYPE_ATOM )return hybrid_scaling_factor;return core_get_scaling ();
Greg Kroah-Hartman
maintainerPATCH 128/461 device property: set fwnode->secondary to NULL in fwnode_init()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bartosz Golaszewski <[email protected]>
commit 215c90ee656114f5e8c32408228d97082f8e0eef upstream.
If a firmware node is allocated on the stack (for instance: temporary software node whose life-time we control) or on the heap - but using a non-zeroing allocation function - and initialized using fwnode_init(), its secondary pointer will contain uninitalized memory which likely will be neither NULL nor IS_ERR() and so may end up being dereferenced (for
example: in dev_to_swnode()). Set fwnode->secondary to NULL on initialization.
--- include/linux/fwnode.h | 1 + 1 file changed, 1 insertion(+)
include/linux/fwnode.h +1 -0
@@ -208,6 +208,7 @@ struct fwnode_operations {static inline void fwnode_init (struct fwnode_handle *fwnode,const struct fwnode_operations *ops) { + fwnode->secondary =NULL ; fwnode->ops = ops;INIT_LIST_HEAD (&fwnode->consumers);INIT_LIST_HEAD (&fwnode->suppliers);
Greg Kroah-Hartman
maintainerPATCH 129/461 drm/i915/display: Copy color pipeline from plane in the primary joiner pipe
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chaitanya Kumar Borah <[email protected]>
commit 86ed2d96db1965e9008e919b1936145ae66540e3 upstream.
When copying plane color state in a joiner configuration, use the plane in the primary joiner pipe since it carries the pipeline number selected by the user-space.
This assumes that all pipes in the joiner are symmetric in their plane color capabilities.
--- drivers/gpu/drm/i915/display/intel_plane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/i915/display/intel_plane.c +1 -1
@@ -373,7 +373,7 @@ intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,bool changed = false;int i =0 ; - iter_colorop = plane_state->uapi.color_pipeline; + iter_colorop = from_plane_state->uapi.color_pipeline;while (iter_colorop) {for_each_new_colorop_in_state (state, colorop, new_colorop_state, i) {
Signature · 1 line
2.54.0
Greg Kroah-Hartman
maintainerPATCH 130/461 drm/msm: Fix shrinker deadlock
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel J Blueman <[email protected]>
commit 3392291fc509d8ad6e4ad90f15b0a193f721cbc9 upstream.
With PROVE_LOCKING on an Snapdragon X1 and VM reclaim pressure, we see:
======================================================
WARNING: possible circular locking dependency detected
7.0.0-debug+ #43 Tainted: G W
------------------------------------------------------
kswapd0/82 is trying to acquire lock:
ffff800080ec3870 (reservation_ww_class_acquire){+.+.}-{0:0}, at: msm_gem_shrinker_scan+0x17c/0x400 [msm]
but task is already holding lock:
ffffc31709b263b8 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat+0x88/0x988
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #2 (fs_reclaim){+.+.}-{0:0}:
__lock_acquire+0x4d0/0xad0
lock_acquire.part.0+0xc4/0x248
lock_acquire+0x8c/0x248
fs_reclaim_acquire+0xd0/0xf0
dma_resv_lockdep+0x224/0x348
do_one_initcall+0x84/0x5d0
do_initcalls+0x194/0x1d8
kernel_init_freeable+0x128/0x180
kernel_init+0x2c/0x160
ret_from_fork+0x10/0x20-> #1 (reservation_ww_class_mutex){+.+.}-{4:4}:
__lock_acquire+0x4d0/0xad0
lock_acquire.part.0+0xc4/0x248
lock_acquire+0x8c/0x248
dma_resv_lockdep+0x1a8/0x348
do_one_initcall+0x84/0x5d0
do_initcalls+0x194/0x1d8
kernel_init_freeable+0x128/0x180
kernel_init+0x2c/0x160
ret_from_fork+0x10/0x20-> #0 (reservation_ww_class_acquire){+.+.}-{0:0}:
check_prev_add+0x114/0x790
validate_chain+0x594/0x6f0
__lock_acquire+0x4d0/0xad0
lock_acquire.part.0+0xc4/0x248
lock_acquire+0x8c/0x248
drm_gem_lru_scan+0x1ac/0x440
msm_gem_shrinker_scan+0x17c/0x400 [msm]
do_shrink_slab+0x150/0x4a0
shrink_slab+0x144/0x460
shrink_one+0x9c/0x1b0
shrink_many+0x27c/0x5c0
shrink_node+0x344/0x550
balance_pgdat+0x2c0/0x988
kswapd+0x11c/0x318
kthread+0x10c/0x128
ret_from_fork+0x10/0x20 other info that might help us debug this:
Chain exists of:
reservation_ww_class_acquire --> reservation_ww_class_mutex --> fs_reclaim
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(fs_reclaim);
lock(reservation_ww_class_mutex);
lock(fs_reclaim);
lock(reservation_ww_class_acquire); *** DEADLOCK ***
1 lock held by kswapd0/82:
#0: ffffc31709b263b8 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat+0x88/0x988 stack backtrace:
CPU: 4 UID: 0 PID: 82 Comm: kswapd0 Tainted: G W 7.0.0-debug+ #43 PREEMPT(full)
Tainted: [W]=WARN
Hardware name: LENOVO 21BX0016US/21BX0016US, BIOS N3HET94W (1.66 ) 09/15/2025
Call trace:
show_stack+0x20/0x40 (C)
dump_stack_lvl+0x9c/0xd0
dump_stack+0x18/0x30
print_circular_bug+0x114/0x120
check_noncircular+0x178/0x198
check_prev_add+0x114/0x790
validate_chain+0x594/0x6f0
__lock_acquire+0x4d0/0xad0
lock_acquire.part.0+0xc4/0x248
lock_acquire+0x8c/0x248
drm_gem_lru_scan+0x1ac/0x440
msm_gem_shrinker_scan+0x17c/0x400 [msm]
do_shrink_slab+0x150/0x4a0
shrink_slab+0x144/0x460
shrink_one+0x9c/0x1b0
shrink_many+0x27c/0x5c0
shrink_node+0x344/0x550
balance_pgdat+0x2c0/0x988
kswapd+0x11c/0x318
kthread+0x10c/0x128
ret_from_fork+0x10/0x20kswapd0 holding fs_reclaim calls the MSM shrinker, which calls dma_resv_lock. This in turn acquires fs_reclaim.
Fix this deadlock by using dma_resv_trylock() instead, dropping the subsequently unused passed wait-wound lock 'ticket'.
Patchwork: https://patchwork.freedesktop.org/patch/723564/
Message-ID: <[email protected]>
--- drivers/gpu/drm/msm/msm_gem_shrinker.c | 40 +++++++++++++-------------------- 1 file changed, 16 insertions(+), 24 deletions(-)
drivers/gpu/drm/msm/msm_gem_shrinker.c +16 -24
@@ -43,8 +43,7 @@ msm_gem_shrinker_count(struct shrinker * }static bool -with_vm_locks (struct ww_acquire_ctx *ticket, -void (*fn)(struct drm_gem_object *obj), +with_vm_locks (void (*fn)(struct drm_gem_object *obj),struct drm_gem_object *obj) {/*
@@ -52,7 +51,7 @@ with_vm_locks(struct ww_acquire_ctx *tic* success paths */ struct drm_gpuvm_bo *vm_bo, *last_locked =NULL ; -int ret =0 ; +bool locked = true; drm_gem_for_each_gpuvm_bo (vm_bo, obj) {struct dma_resv *resv =drm_gpuvm_resv (vm_bo->vm);
@@ -60,23 +59,14 @@ with_vm_locks(struct ww_acquire_ctx *ticif (resv == obj->resv)continue ; - ret =dma_resv_lock (resv, ticket); - -/* -* Since we already skip the case when the VM and obj -* share a resv (ie. _NO_SHARE objs), we don't expect -* to hit a double-locking scenario... which the lock -* unwinding cannot really cope with. -*/ -WARN_ON (ret == -EALREADY ); -/* -* Don't bother with slow-lock / backoff / retry sequence, -* if we can't get the lock just give up and move on to -* the next object. +* dma_resv_lock can't be used due to acquiring 'ticket' before the +* fs_reclaim lock, which is held in shrinker context */ -if (ret) +if (!dma_resv_trylock (resv)) { + locked = false; goto out_unlock; + }/* * Hold a ref to prevent the vm_bo from being freed
@@ -108,11 +98,11 @@ out_unlock: } } -return ret ==0 ; +return locked; }static bool -purge (struct drm_gem_object *obj,struct ww_acquire_ctx *ticket) +purge (struct drm_gem_object *obj,struct ww_acquire_ctx *) {if (!is_purgeable (to_msm_bo (obj)))return false;
@@ -120,11 +110,11 @@ purge(struct drm_gem_object *obj, structif (msm_gem_active (obj))return false; -return with_vm_locks (ticket, msm_gem_purge, obj); +return with_vm_locks (msm_gem_purge, obj); }static bool -evict (struct drm_gem_object *obj,struct ww_acquire_ctx *ticket) +evict (struct drm_gem_object *obj,struct ww_acquire_ctx *) {if (is_unevictable (to_msm_bo (obj)))return false;
@@ -132,7 +122,7 @@ evict(struct drm_gem_object *obj, structif (msm_gem_active (obj))return false; -return with_vm_locks (ticket, msm_gem_evict, obj); +return with_vm_locks (msm_gem_evict, obj); } static bool
@@ -164,7 +154,6 @@ static unsigned long msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) { struct msm_drm_private *priv = shrinker->private_data; -struct ww_acquire_ctx ticket; struct { struct drm_gem_lru *lru; bool (*shrink)(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket);
@@ -185,11 +174,14 @@ msm_gem_shrinker_scan(struct shrinker *sfor (unsigned i =0 ; (nr >0 ) && (i <ARRAY_SIZE (stages)); i++) {if (!stages[i].cond)continue ; +/* +* 'ticket' not needed on trylock paths +*/ stages[i].freed =drm_gem_lru_scan (stages[i].lru, nr, &stages[i].remaining, stages[i].shrink, - &ticket); +NULL ); nr -= stages[i].freed; freed += stages[i].freed; remaining += stages[i].remaining;
Greg Kroah-Hartman
maintainerPATCH 131/461 drm/v3d: Fix use-after-free of CPU job query arrays on error path
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maíra Canal <[email protected]>
commit b0fe80c0b9250b35e2211bf3117e7aca814a21b0 upstream.
The CPU job ioctl's fail label calls kvfree() on cpu_job's timestamp and performance query arrays after v3d_job_cleanup(), which drops the job's last reference and frees cpu_job. Reading cpu_job at that point is a use-after-free. Also, on the early v3d_job_init() failure path, it is a NULL dereference, since v3d_job_deallocate() zeroes the local pointer.
In the success path, the arrays are released from the scheduler's .free_job callback, but on the error path, they are freed manually, as the job was never pushed to the scheduler. While the success path deals with this correctly, the fail path doesn't.
On top of that, the manual kvfree() calls only free the array storage; they don't drm_syncobj_put() the per-query syncobjs that v3d_timestamp_query_info_free() and v3d_performance_query_info_free() release on the success path. So the same fail path that triggers the use-after-free also leaks one syncobj reference per query.
Unify the CPU job teardown into the CPU job's kref destructor, mirroring v3d_render_job_free(). The scheduler's .free_job slot reverts to the generic v3d_sched_job_free() and the fail label drops the manual kvfree() calls, leaving a single teardown path that is reached from both the scheduler and the ioctl error path. That removes the use-after-free, the NULL dereference, and the syncobj leak by construction.
--- drivers/gpu/drm/v3d/v3d_sched.c | 16 +--------------- drivers/gpu/drm/v3d/v3d_submit.c | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 18 deletions(-)
drivers/gpu/drm/v3d/v3d_sched.c +1 -15
@@ -105,20 +105,6 @@ v3d_performance_query_info_free(struct v }static void -v3d_cpu_job_free (struct drm_sched_job *sched_job) -{ -struct v3d_cpu_job *job =to_cpu_job (sched_job); - -v3d_timestamp_query_info_free (&job->timestamp_query, - job->timestamp_query.count); - -v3d_performance_query_info_free (&job->performance_query, - job->performance_query.count); - -v3d_job_cleanup (&job->base); -} - -static void v3d_switch_perfmon (struct v3d_dev *v3d,struct v3d_job *job) {struct v3d_perfmon *perfmon = v3d->global_perfmon;
@@ -861,7 +847,7 @@ static const struct drm_sched_backend_opstatic const struct drm_sched_backend_ops v3d_cpu_sched_ops = { .run_job = v3d_cpu_job_run, - .free_job = v3d_cpu_job_free + .free_job = v3d_sched_job_free }; static int
drivers/gpu/drm/v3d/v3d_submit.c +16 -3
@@ -120,6 +120,21 @@ v3d_render_job_free(struct kref *ref)v3d_job_free (ref); } +static void +v3d_cpu_job_free (struct kref *ref) +{ + struct v3d_cpu_job *job = container_of(ref, struct v3d_cpu_job, + base.refcount); + +v3d_timestamp_query_info_free (&job->timestamp_query, + job->timestamp_query.count); + +v3d_performance_query_info_free (&job->performance_query, + job->performance_query.count); + +v3d_job_free (ref); +} +void v3d_job_cleanup (struct v3d_job *job) {if (!job)
@@ -1296,7 +1311,7 @@ v3d_submit_cpu_ioctl(struct drm_device *trace_v3d_submit_cpu_ioctl (&v3d->drm, cpu_job->job_type); ret =v3d_job_init (v3d, file_priv, &cpu_job->base, - v3d_job_free,0 , &se,V3D_CPU ); + v3d_cpu_job_free,0 , &se,V3D_CPU );if (ret) {v3d_job_deallocate ((void *)&cpu_job); goto fail;
@@ -1379,8 +1394,6 @@ fail:v3d_job_cleanup ((void *)csd_job);v3d_job_cleanup (clean_job);v3d_put_multisync_post_deps (&se); -kvfree (cpu_job->timestamp_query.queries); -kvfree (cpu_job->performance_query.queries);return ret; }
Greg Kroah-Hartman
maintainerPATCH 132/461 drm/v3d: Release indirect CSD GEM reference on CPU job free
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maíra Canal <[email protected]>
commit 6eb6e5acafa46854d4363e6c34981289995f3ace upstream.
v3d_get_cpu_indirect_csd_params() takes a reference to the indirect BO via drm_gem_object_lookup() and stashes it in cpu_job->indirect_csd.indirect, but nothing on the CPU job teardown path ever drops that reference.
Drop the extra reference in v3d_cpu_job_free(). The NULL check covers ioctl errors before the lookup ran and CPU job types other than V3D_CPU_JOB_TYPE_INDIRECT_CSD, which leave the field zero-initialised.
--- drivers/gpu/drm/v3d/v3d_submit.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/v3d/v3d_submit.c +3 -0
@@ -132,6 +132,9 @@ v3d_cpu_job_free(struct kref *ref)v3d_performance_query_info_free (&job->performance_query, job->performance_query.count); +if (job->indirect_csd.indirect) +drm_gem_object_put (job->indirect_csd.indirect); +v3d_job_free (ref); }
Greg Kroah-Hartman
maintainerPATCH 133/461 drm/virtio: use uninterruptible resv lock for plane updates
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
commit 9af1b6e175c82daf4b423da339a722d8e67a735a upstream.
virtio_gpu_cursor_plane_update() and virtio_gpu_resource_flush() lock the framebuffer BO's dma_resv via virtio_gpu_array_lock_resv() and ignore its return value. The function can fail with -EINTR from dma_resv_lock_interruptible() (signal during lock wait) or with -ENOMEM from dma_resv_reserve_fences() (fence slot allocation), leaving the resv lock not held. The queue path then walks the object array and calls dma_resv_add_fence(), which requires the lock held; with lockdep enabled this trips dma_resv_assert_held():
WARNING: drivers/dma-buf/dma-resv.c:296 at dma_resv_add_fence+0x71e/0x840 Call Trace: virtio_gpu_array_add_fence virtio_gpu_queue_ctrl_sgs virtio_gpu_queue_fenced_ctrl_buffer virtio_gpu_cursor_plane_update drm_atomic_helper_commit_planes drm_atomic_helper_commit_tail commit_tail drm_atomic_helper_commit drm_atomic_commit drm_atomic_helper_update_plane __setplane_atomic drm_mode_cursor_universal drm_mode_cursor_common drm_mode_cursor_ioctl drm_ioctl __x64_sys_ioctl
Beyond the WARN, mutating the dma_resv fence list without the lock races with concurrent readers/writers and can corrupt the list.
Both call sites run inside the .atomic_update plane callback, which DRM atomic helpers do not allow to fail (by the time it runs, the commit has been signed off to userspace and there is no clean rollback path). Moving the lock acquisition to .prepare_fb was rejected because the broader lock scope deadlocks against other BO locking paths in the same atomic commit.
Introduce virtio_gpu_lock_one_resv_uninterruptible() that uses dma_resv_lock() instead of dma_resv_lock_interruptible(). This eliminates the -EINTR failure mode -- the realistic syzbot trigger -- without extending the lock hold across the commit. The helper locks a single BO and rejects nents > 1 with -EINVAL; both fix sites lock exactly one BO.
Use it from virtio_gpu_cursor_plane_update() and virtio_gpu_resource_flush(); check the return value to handle the remaining -ENOMEM case from dma_resv_reserve_fences() by freeing the objs and skipping the plane update for that frame. The framebuffer BOs touched here are not shared with other contexts and lock contention is expected to be brief, so the loss of signal-interruptibility is acceptable.
Other callers of virtio_gpu_array_lock_resv() (the ioctl paths) continue to use the interruptible variant.
The bug was reported by syzbot, triggered via fault injection (fail_nth) on the DRM_IOCTL_MODE_CURSOR path, which forces the -ENOMEM branch in dma_resv_reserve_fences().
--- drivers/gpu/drm/virtio/virtgpu_drv.h | 1 + drivers/gpu/drm/virtio/virtgpu_gem.c | 17 +++++++++++++++++ drivers/gpu/drm/virtio/virtgpu_plane.c | 10 ++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-)
drivers/gpu/drm/virtio/virtgpu_drv.h +1 -0
@@ -317,6 +317,7 @@ virtio_gpu_array_from_handles(struct drmvoid virtio_gpu_array_add_obj (struct virtio_gpu_object_array *objs,struct drm_gem_object *obj);int virtio_gpu_array_lock_resv (struct virtio_gpu_object_array *objs); +int virtio_gpu_lock_one_resv_uninterruptible (struct virtio_gpu_object_array *objs);void virtio_gpu_array_unlock_resv (struct virtio_gpu_object_array *objs);void virtio_gpu_array_add_fence (struct virtio_gpu_object_array *objs,struct dma_fence *fence);
drivers/gpu/drm/virtio/virtgpu_gem.c +17 -0
@@ -238,6 +238,23 @@ int virtio_gpu_array_lock_resv(struct vireturn ret; } +int virtio_gpu_lock_one_resv_uninterruptible (struct virtio_gpu_object_array *objs) +{ +int ret; + +if (objs->nents !=1 ) +return -EINVAL ; + +dma_resv_lock (objs->objs[0 ]->resv,NULL ); + + ret =dma_resv_reserve_fences (objs->objs[0 ]->resv,1 ); +if (ret) { +virtio_gpu_array_unlock_resv (objs); +return ret; + } +return 0 ; +} +void virtio_gpu_array_unlock_resv (struct virtio_gpu_object_array *objs) {if (objs->nents ==1 ) {
drivers/gpu/drm/virtio/virtgpu_plane.c +8 -2
@@ -215,7 +215,10 @@ static void virtio_gpu_resource_flush(stif (!objs)return ;virtio_gpu_array_add_obj (objs, vgfb->base.obj[0 ]); -virtio_gpu_array_lock_resv (objs); +if (virtio_gpu_lock_one_resv_uninterruptible (objs)) { +virtio_gpu_array_put_free (objs); +return ; + }virtio_gpu_cmd_resource_flush (vgdev, bo->hw_res_handle, x, y, width, height, objs, vgplane_st->fence);
@@ -459,7 +462,10 @@ static void virtio_gpu_cursor_plane_updaif (!objs)return ;virtio_gpu_array_add_obj (objs, vgfb->base.obj[0 ]); -virtio_gpu_array_lock_resv (objs); +if (virtio_gpu_lock_one_resv_uninterruptible (objs)) { +virtio_gpu_array_put_free (objs); +return ; + } virtio_gpu_cmd_transfer_to_host_2d (vgdev, 0, plane->state->crtc_w,
Greg Kroah-Hartman
maintainerPATCH 134/461 drm/xe/multi_queue: Fix secondary queue error case
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Niranjana Vishwanathapura <[email protected]>
commit 00907da2126ed785451b2a2f0fef282246dad104 upstream.
If xe_lrc_create() fails, the secondary queue added to the multi-queue group list is not removed before freeing the queue. Fix error path handling for secondary queues by removing it from the multi-queue group list at the right place.
--- drivers/gpu/drm/xe/xe_guc_submit.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/gpu/drm/xe/xe_guc_submit.c +8 -8
@@ -1623,6 +1623,14 @@ static void guc_exec_queue_fini(struct xstruct xe_guc_exec_queue *ge = q->guc;struct xe_guc *guc =exec_queue_to_guc (q); +if (xe_exec_queue_is_multi_queue_secondary (q)) { +struct xe_exec_queue_group *group = q->multi_queue.group; + +mutex_lock (&group->list_lock); +list_del (&q->multi_queue.link); +mutex_unlock (&group->list_lock); + } +release_guc_id (guc, q);xe_sched_entity_fini (&ge->entity);xe_sched_fini (&ge->sched);
@@ -1644,14 +1652,6 @@ static void __guc_exec_queue_destroy_asyguard (xe_pm_runtime)(guc_to_xe (guc));trace_xe_exec_queue_destroy (q); -if (xe_exec_queue_is_multi_queue_secondary (q)) { -struct xe_exec_queue_group *group = q->multi_queue.group; - -mutex_lock (&group->list_lock); -list_del (&q->multi_queue.link); -mutex_unlock (&group->list_lock); - } -/* Confirm no work left behind accessing device structures */ cancel_delayed_work_sync (&ge->sched.base.work_tdr);
Greg Kroah-Hartman
maintainerPATCH 135/461 drm/amdgpu/vpe: Force collaborate sync after TRAP
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alan Liu <[email protected]>
commit b6074630a461b1322a814988779005cbc43612ea upstream.
VPE1 could possibly hang and fail to power off at the end of commands in collaboration mode. This workaround adds a COLLAB_SYNC after TRAP to force instances synchronized to avoid VPE1 fail to power off.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c +6 -1
@@ -562,6 +562,11 @@ static void vpe_ring_emit_fence(struct aamdgpu_ring_write (ring,0 ); } +/* WA: Force sync after TRAP to avoid VPE1 fail to power off */ +if (ring->adev->vpe.collaborate_mode) { +amdgpu_ring_write (ring,VPE_CMD_HEADER (VPE_CMD_OPCODE_COLLAB_SYNC ,0 )); +amdgpu_ring_write (ring,0xabcd ); + } }static void vpe_ring_emit_pipeline_sync (struct amdgpu_ring *ring)
@@ -968,7 +973,7 @@ static const struct amdgpu_ring_funcs vp .emit_frame_size =5 +/* vpe_ring_init_cond_exec */ 6 +/* vpe_ring_emit_pipeline_sync */ -10 +10 +10 +/* vpe_ring_emit_fence */ +12 +12 +12 +/* vpe_ring_emit_fence */ /* vpe_ring_emit_vm_flush */ SOC15_FLUSH_GPU_TLB_NUM_WREG *3 + SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 6,
Greg Kroah-Hartman
maintainerPATCH 136/461 drm/bridge: it66121: acquire reset GPIO in probe
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julien Chauveau <[email protected]>
commit e02b5262fd288cc235f14e12233ea54e78c04611 upstream.
The it66121_ctx structure has a gpio_reset field, and it66121_hw_reset() calls gpiod_set_value() on it. However, the GPIO descriptor is never acquired via devm_gpiod_get(), leaving gpio_reset as NULL throughout the driver lifetime.
gpiod_set_value() silently returns when passed a NULL descriptor, so the hardware reset sequence in it66121_hw_reset() is a no-op. This leaves the chip in an undefined state at probe time, which can prevent it from responding on the I2C bus.
The DT binding marks reset-gpios as a required property, so all compliant device trees provide this GPIO. Add the missing devm_gpiod_get() call after enabling power supplies and before the hardware reset, so the chip is properly reset with power applied.
--- drivers/gpu/drm/bridge/ite-it66121.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/gpu/drm/bridge/ite-it66121.c +5 -0
@@ -1559,6 +1559,11 @@ static int it66121_probe(struct i2c_cliereturn ret; } + ctx->gpio_reset =devm_gpiod_get (dev,"reset" ,GPIOD_OUT_LOW ); +if (IS_ERR (ctx->gpio_reset)) +return dev_err_probe (dev,PTR_ERR (ctx->gpio_reset), +"Failed to get reset GPIO\n" ); +it66121_hw_reset (ctx); ctx->regmap =devm_regmap_init_i2c (client, &it66121_regmap_config);
Greg Kroah-Hartman
maintainerPATCH 137/461 drm/bridge: megachips: remove bridge when irq request fails
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Osama Abdelkader <[email protected]>
commit d45d5c819f2cd0b6b5d76a194a537a5f4aeefecb upstream.
If devm_request_threaded_irq() fails after drm_bridge_add(), remove the bridge before returning.
Keep drm_bridge_add() rather than devm_drm_bridge_add(): registration is tied to the STDP4028 device while ge_b850v3_register() may complete from either I2C probe; devm would not unwind the bridge if the other client's probe fails.
--- drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 16 +++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +10 -6
@@ -251,7 +251,6 @@ static void ge_b850v3_lvds_remove(void) goto out;drm_bridge_remove (&ge_b850v3_lvds_ptr->bridge); - ge_b850v3_lvds_ptr =NULL ; out:mutex_unlock (&ge_b850v3_lvds_dev_mutex);
@@ -261,6 +260,7 @@ static int ge_b850v3_register(void) {struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c;struct device *dev = &stdp4028_i2c->dev; +int ret;/* drm bridge initialization */ ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT |
@@ -277,11 +277,15 @@ static int ge_b850v3_register(void)if (!stdp4028_i2c->irq)return 0 ; -return devm_request_threaded_irq (&stdp4028_i2c->dev, - stdp4028_i2c->irq,NULL , - ge_b850v3_lvds_irq_handler, -IRQF_TRIGGER_HIGH |IRQF_ONESHOT , -"ge-b850v3-lvds-dp" , ge_b850v3_lvds_ptr); + ret =devm_request_threaded_irq (&stdp4028_i2c->dev, + stdp4028_i2c->irq,NULL , + ge_b850v3_lvds_irq_handler, +IRQF_TRIGGER_HIGH |IRQF_ONESHOT , +"ge-b850v3-lvds-dp" , ge_b850v3_lvds_ptr); +if (ret) +drm_bridge_remove (&ge_b850v3_lvds_ptr->bridge); + +return ret; }static int stdp4028_ge_b850v3_fw_probe (struct i2c_client *stdp4028_i2c)
Greg Kroah-Hartman
maintainerPATCH 138/461 drm/amd/display: Fix integer overflow in bios_get_image()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harry Wentland <[email protected]>
commit cd86529ec61474a38c3837fb7823790a7c3f8cce upstream.
[Why&How]
The bounds check in bios_get_image() computes 'offset + size' using unsigned 32-bit arithmetic before comparing against bios_size. If a VBIOS image contains a near-UINT32_MAX offset the addition wraps to a small value, the comparison passes, and the function returns a wild pointer past the VBIOS mapping.
Additionally, the comparison uses '<' (strict), which incorrectly rejects the valid exact-fit case where offset + size == bios_size.
Fix both issues by restructuring the check to avoid the addition
entirely: first reject if offset alone exceeds bios_size, then check size against the remaining space (bios_size - offset). This eliminates the overflow and correctly permits exact-fit accesses.
--- drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c +6 -3
@@ -37,10 +37,13 @@ uint8_t *bios_get_image(struct dc_bios * uint32_t offset, uint32_t size) { -if (bp->bios && offset + size < bp->bios_size) -return bp->bios + offset; -else +if (!bp->bios)return NULL ; + +if (offset > bp->bios_size || size > bp->bios_size - offset) +return NULL ; + +return bp->bios + offset; }#include "reg_helper.h"
Greg Kroah-Hartman
maintainerPATCH 139/461 drm/amd/display: Validate GPIO pin LUT table size before iterating
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harry Wentland <[email protected]>
commit 86d2b20644b11d21fe52c596e6e922b4590a3e3f upstream.
[Why&How]
The GPIO pin table parsers in get_gpio_i2c_info() and bios_parser_get_gpio_pin_info() derive an element count from the VBIOS table_header.structuresize field, then iterate over gpio_pin[] entries. However, GET_IMAGE() only validates that the table header itself fits within the BIOS image. If the VBIOS reports a structuresize larger than the actual mapped data, the loop reads past the end of the BIOS image, causing an out-of-bounds read.
Fix this by calling bios_get_image() to validate that the full claimed structuresize is accessible within the BIOS image before entering the loop in both functions.
--- drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +9 -0
@@ -492,6 +492,10 @@ static enum bp_result get_gpio_i2c_info( - sizeof(struct atom_common_table_header)) /sizeof (struct atom_gpio_pin_assignment ); +if (!bios_get_image (&bp->base,DATA_TABLES (gpio_pin_lut), +le16_to_cpu (header->table_header.structuresize))) +return BP_RESULT_BADBIOSTABLE ; + pin = (struct atom_gpio_pin_assignment *) header->gpio_pin;for (table_index =0 ; table_index < count; table_index++) {
@@ -680,6 +684,11 @@ static enum bp_result bios_parser_get_gp count = (le16_to_cpu (header->table_header.structuresize) -sizeof (struct atom_common_table_header )) /sizeof (struct atom_gpio_pin_assignment ); + +if (!bios_get_image (&bp->base,DATA_TABLES (gpio_pin_lut), +le16_to_cpu (header->table_header.structuresize))) +return BP_RESULT_BADBIOSTABLE ; +for (i =0 ; i < count; ++i) {if (header->gpio_pin[i].gpio_id != gpio_id)continue ;
Greg Kroah-Hartman
maintainerPATCH 140/461 drm/amd/display: Validate payload length and link_index in dc_process_dmub_aux_transfer_async
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harry Wentland <[email protected]>
commit 6c92f6d9600efa3ef0d9e560a2b52776d9803c29 upstream.
[Why&How]dc_process_dmub_aux_transfer_async() copies payload->length bytes into a 16-byte stack buffer (dpaux.data[16]) guarded only by an ASSERT(), which is a no-op in release builds. If a caller ever passes length > 16 this results in a stack buffer overflow via memcpy.
Additionally, link_index is used to dereference dc->links[] without bounds checking against dc->link_count, risking an out-of-bounds access.
Replace the ASSERT with a hard runtime check that returns false when payload->length exceeds the destination buffer size, and add a bounds check for link_index before it is used.
--- drivers/gpu/drm/amd/display/dc/core/dc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/dc/core/dc.c +5 -1
@@ -5993,7 +5993,11 @@ bool dc_process_dmub_aux_transfer_async(uint8_t action;union dmub_rb_cmd cmd = {0 }; -ASSERT (payload->length <=16 ); +if (link_index >= dc->link_count || !dc->links[link_index]) +return false; + +if (payload->length >sizeof (cmd.dp_aux_access.aux_control.dpaux.data)) +return false; cmd.dp_aux_access.header.type =DMUB_CMD__DP_AUX_ACCESS ; cmd.dp_aux_access.header.payload_bytes =0 ;
Greg Kroah-Hartman
maintainerPATCH 141/461 batman-adv: v: stop OGMv2 on disabled interface
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit f8ce8b8331a1bc44ad4905886a482214d428b253 upstream.
When a batadv_hard_iface is disabled, its mesh_iface pointer is set to NULL. However, batadv_v_ogm_send_meshif() may still dispatch OGMs via batadv_v_ogm_queue_on_if() for interfaces that have since lost their mesh_iface association. This results in a NULL pointer dereference when batadv_v_ogm_queue_on_if() unconditionally calls netdev_priv() on the now NULL hard_iface->mesh_iface to retrieve the batadv_priv.
It is necessary to ensure that the batadv_v_ogm_queue_on_if() checks that it is using the same mesh_iface for which batadv_v_ogm_send_meshif() was called.
--- net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-)
net/batman-adv/bat_v_ogm.c +21 -12
@@ -113,14 +113,14 @@ static void batadv_v_ogm_start_timer(str/** * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface +* @bat_priv: the bat priv with all the mesh interface information * @skb: the OGM to send * @hard_iface: the interface to use to send the OGM */ -static void batadv_v_ogm_send_to_if (struct sk_buff *skb, +static void batadv_v_ogm_send_to_if (struct batadv_priv *bat_priv, +struct sk_buff *skb,struct batadv_hard_iface *hard_iface) { -struct batadv_priv *bat_priv =netdev_priv (hard_iface->mesh_iface); -if (hard_iface->if_status !=BATADV_IF_ACTIVE ) {kfree_skb (skb);return ;
@@ -187,6 +187,7 @@ static void batadv_v_ogm_aggr_list_free(/** * batadv_v_ogm_aggr_send() - flush & send aggregation queue +* @bat_priv: the bat priv with all the mesh interface information * @hard_iface: the interface with the aggregation queue to flush * * Aggregates all OGMv2 packets currently in the aggregation queue into a
@@ -196,7 +197,8 @@ static void batadv_v_ogm_aggr_list_free(* * Caller needs to hold the hard_iface->bat_v.aggr_list.lock. */ -static void batadv_v_ogm_aggr_send (struct batadv_hard_iface *hard_iface) +static void batadv_v_ogm_aggr_send (struct batadv_priv *bat_priv, +struct batadv_hard_iface *hard_iface) {unsigned int aggr_len = hard_iface->bat_v.aggr_len;struct sk_buff *skb_aggr;
@@ -226,27 +228,32 @@ static void batadv_v_ogm_aggr_send(strucconsume_skb (skb); } -batadv_v_ogm_send_to_if (skb_aggr, hard_iface); +batadv_v_ogm_send_to_if (bat_priv, skb_aggr, hard_iface); }/** * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface +* @bat_priv: the bat priv with all the mesh interface information * @skb: the OGM to queue * @hard_iface: the interface to queue the OGM on */ -static void batadv_v_ogm_queue_on_if (struct sk_buff *skb, +static void batadv_v_ogm_queue_on_if (struct batadv_priv *bat_priv, +struct sk_buff *skb,struct batadv_hard_iface *hard_iface) { -struct batadv_priv *bat_priv =netdev_priv (hard_iface->mesh_iface); +if (hard_iface->mesh_iface != bat_priv->mesh_iface) { +kfree_skb (skb); +return ; + }if (!atomic_read (&bat_priv->aggregated_ogms)) { -batadv_v_ogm_send_to_if (skb, hard_iface); +batadv_v_ogm_send_to_if (bat_priv, skb, hard_iface);return ; }spin_lock_bh (&hard_iface->bat_v.aggr_list.lock);if (!batadv_v_ogm_queue_left (skb, hard_iface)) -batadv_v_ogm_aggr_send (hard_iface); +batadv_v_ogm_aggr_send (bat_priv, hard_iface); hard_iface->bat_v.aggr_len +=batadv_v_ogm_len (skb);__skb_queue_tail (&hard_iface->bat_v.aggr_list, skb);
@@ -343,7 +350,7 @@ static void batadv_v_ogm_send_meshif(strbreak ; } -batadv_v_ogm_queue_on_if (skb_tmp, hard_iface); +batadv_v_ogm_queue_on_if (bat_priv, skb_tmp, hard_iface);batadv_hardif_put (hard_iface); }rcu_read_unlock ();
@@ -383,12 +390,14 @@ void batadv_v_ogm_aggr_work(struct work_ {struct batadv_hard_iface_bat_v *batv;struct batadv_hard_iface *hard_iface; +struct batadv_priv *bat_priv; batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work); hard_iface = container_of(batv, struct batadv_hard_iface, bat_v); + bat_priv =netdev_priv (hard_iface->mesh_iface);spin_lock_bh (&hard_iface->bat_v.aggr_list.lock); -batadv_v_ogm_aggr_send (hard_iface); +batadv_v_ogm_aggr_send (bat_priv, hard_iface);spin_unlock_bh (&hard_iface->bat_v.aggr_list.lock);batadv_v_ogm_start_queue_timer (hard_iface);
@@ -578,7 +587,7 @@ static void batadv_v_ogm_forward(struct if_outgoing->net_dev->name,ntohl (ogm_forward->throughput), ogm_forward->ttl, if_incoming->net_dev->name); -batadv_v_ogm_queue_on_if (skb, if_outgoing); +batadv_v_ogm_queue_on_if (bat_priv, skb, if_outgoing); out:batadv_orig_ifinfo_put (orig_ifinfo);
Greg Kroah-Hartman
maintainerPATCH 142/461 batman-adv: tvlv: abort OGM send on tvlv append failure
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 501368506563e151b322c8c3f228b796e615b90d upstream.
batadv_tvlv_container_ogm_append() could fail in two ways: a memory allocation failure when resizing the packet buffer, or the tvlv data exceeding U16_MAX bytes. In both cases the function previously returned the old (now stale) tvlv_value_len rather than signalling an error, causing the OGM/OGM2 send path to transmit a packet whose TVLV length field no longer matched the actual buffer contents. And because it also didn't fill in the new TVLV data, sending either uninitialized or corrupted data on the wire.
All errors in batadv_tvlv_container_ogm_append() must be forwarded to the caller. And the caller must abort the send of the OGM2. For B.A.T.M.A.N. IV, it is currently not allowed to abort the send. The non-TVLV part of the OGM must be queued up instead.
--- net/batman-adv/bat_iv_ogm.c | 16 +++++++++++++--- net/batman-adv/bat_v_ogm.c | 26 ++++++++++++++------------ net/batman-adv/tvlv.c | 17 ++++++++++++----- net/batman-adv/tvlv.h | 2 +- 4 files changed, 40 insertions(+), 21 deletions(-)
net/batman-adv/bat_iv_ogm.c +13 -3
@@ -790,6 +790,7 @@ static void batadv_iv_ogm_schedule_buff(u32 seqno;u16 tvlv_len =0 ;unsigned long send_time; +int ret;lockdep_assert_held (&hard_iface->bat_iv.ogm_buff_mutex);
@@ -813,9 +814,18 @@ static void batadv_iv_ogm_schedule_buff(* appended as it may alter the tt tvlv container */ batadv_tt_local_commit_changes (bat_priv); - tvlv_len =batadv_tvlv_container_ogm_append (bat_priv, ogm_buff, - ogm_buff_len, -BATADV_OGM_HLEN ); + ret =batadv_tvlv_container_ogm_append (bat_priv, ogm_buff, + ogm_buff_len, +BATADV_OGM_HLEN ); +if (ret <0 ) { +/* OGMs must be queued even when the buffer allocation for +* TVLVs failed. just fall back to the non-TVLV version +*/ + ret =0 ; + *ogm_buff_len =BATADV_OGM_HLEN ; + } + + tvlv_len = ret; } batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
net/batman-adv/bat_v_ogm.c +14 -12
@@ -269,10 +269,10 @@ static void batadv_v_ogm_send_meshif(strstruct batadv_hard_iface *hard_iface;struct batadv_ogm2_packet *ogm_packet;struct sk_buff *skb, *skb_tmp; -unsigned char *ogm_buff; +unsigned char **ogm_buff;struct list_head *iter; -int ogm_buff_len; -u16 tvlv_len =0 ; +int *ogm_buff_len; +u16 tvlv_len;int ret;lockdep_assert_held (&bat_priv->bat_v.ogm_buff_mutex);
@@ -280,25 +280,27 @@ static void batadv_v_ogm_send_meshif(strif (atomic_read (&bat_priv->mesh_state) ==BATADV_MESH_DEACTIVATING ) goto out; - ogm_buff = bat_priv->bat_v.ogm_buff; - ogm_buff_len = bat_priv->bat_v.ogm_buff_len; + ogm_buff = &bat_priv->bat_v.ogm_buff; + ogm_buff_len = &bat_priv->bat_v.ogm_buff_len; +/* tt changes have to be committed before the tvlv data is * appended as it may alter the tt tvlv container */ batadv_tt_local_commit_changes (bat_priv); - tvlv_len =batadv_tvlv_container_ogm_append (bat_priv, &ogm_buff, - &ogm_buff_len, -BATADV_OGM2_HLEN ); + ret =batadv_tvlv_container_ogm_append (bat_priv, ogm_buff, + ogm_buff_len, +BATADV_OGM2_HLEN ); +if (ret <0 ) + goto reschedule; - bat_priv->bat_v.ogm_buff = ogm_buff; - bat_priv->bat_v.ogm_buff_len = ogm_buff_len; + tvlv_len = ret; - skb =netdev_alloc_skb_ip_align (NULL ,ETH_HLEN + ogm_buff_len); + skb =netdev_alloc_skb_ip_align (NULL ,ETH_HLEN + *ogm_buff_len);if (!skb) goto reschedule;skb_reserve (skb,ETH_HLEN ); -skb_put_data (skb, ogm_buff, ogm_buff_len); +skb_put_data (skb, *ogm_buff, *ogm_buff_len); ogm_packet = (struct batadv_ogm2_packet *)skb->data; ogm_packet->seqno =htonl (atomic_read (&bat_priv->bat_v.ogm_seqno));
net/batman-adv/tvlv.c +12 -5
@@ -8,6 +8,7 @@#include <linux/byteorder/generic.h> #include <linux/container_of.h> +#include <linux/errno.h> #include <linux/etherdevice.h> #include <linux/gfp.h> #include <linux/if_ether.h>
@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_b* The ogm packet might be enlarged or shrunk depending on the current size * and the size of the to-be-appended tvlv containers. * -* Return: size of all appended tvlv containers in bytes. +* Return: size of all appended tvlv containers in bytes (max U16_MAX), negative +* if operation failed */ -u16 batadv_tvlv_container_ogm_append (struct batadv_priv *bat_priv, +int batadv_tvlv_container_ogm_append (struct batadv_priv *bat_priv,unsigned char **packet_buff,int *packet_buff_len,int packet_min_len) {
@@ -316,6 +318,7 @@ u16 batadv_tvlv_container_ogm_append(strstruct batadv_tvlv_hdr *tvlv_hdr;u16 tvlv_value_len;void *tvlv_value; +int tvlv_len_ret;bool ret;spin_lock_bh (&bat_priv->tvlv.container_list_lock);
@@ -323,9 +326,12 @@ u16 batadv_tvlv_container_ogm_append(str ret =batadv_tvlv_realloc_packet_buff (packet_buff, packet_buff_len, packet_min_len, tvlv_value_len); - -if (!ret) +if (!ret) { + tvlv_len_ret = -ENOMEM ; goto end; + } + + tvlv_len_ret = tvlv_value_len;if (!tvlv_value_len) goto end;
@@ -344,7 +350,8 @@ u16 batadv_tvlv_container_ogm_append(str end:spin_unlock_bh (&bat_priv->tvlv.container_list_lock); -return tvlv_value_len; + +return tvlv_len_ret; }/**
net/batman-adv/tvlv.h +1 -1
@@ -16,7 +16,7 @@void batadv_tvlv_container_register (struct batadv_priv *bat_priv,u8 type,u8 version,void *tvlv_value,u16 tvlv_value_len); -u16 batadv_tvlv_container_ogm_append (struct batadv_priv *bat_priv, +int batadv_tvlv_container_ogm_append (struct batadv_priv *bat_priv,unsigned char **packet_buff,int *packet_buff_len,int packet_min_len); void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
Greg Kroah-Hartman
maintainerPATCH 143/461 batman-adv: tvlv: reject oversized TVLV packets
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit f50487e3566358b2b982b7801945e858c78ad9ab upstream.
batadv_tvlv_container_ogm_append() builds a TVLV packet section from the tvlv.container_list. The total size of this section is computed by batadv_tvlv_container_list_size(), which sums the sizes of all registered containers.
The return type and accumulator in batadv_tvlv_container_list_size() were u16. If the accumulated size exceeds U16_MAX, the value wraps around, causing the subsequent allocation in batadv_tvlv_container_ogm_append() to be undersized. The memcpy-style copy that follows would then write beyond the end of the allocated buffer, corrupting kernel memory.
Fix this by widening the return type of batadv_tvlv_container_list_size() to size_t. In batadv_tvlv_container_ogm_append(), check the computed length against U16_MAX before proceeding, and bail out as if the allocation had failed when the limit is exceeded.
--- net/batman-adv/tvlv.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
net/batman-adv/tvlv.c +8 -3
@@ -13,6 +13,7 @@#include <linux/gfp.h> #include <linux/if_ether.h> #include <linux/kref.h> +#include <linux/limits.h> #include <linux/list.h> #include <linux/lockdep.h> #include <linux/netdevice.h>
@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_* * Return: size of all currently registered tvlv containers in bytes. */ -static u16 batadv_tvlv_container_list_size (struct batadv_priv *bat_priv) +static size_t batadv_tvlv_container_list_size (struct batadv_priv *bat_priv) {struct batadv_tvlv_container *tvlv; -u16 tvlv_len =0 ; +size_t tvlv_len =0 ;lockdep_assert_held (&bat_priv->tvlv.container_list_lock);
@@ -316,13 +317,17 @@ int batadv_tvlv_container_ogm_append(str {struct batadv_tvlv_container *tvlv;struct batadv_tvlv_hdr *tvlv_hdr; -u16 tvlv_value_len; +size_t tvlv_value_len;void *tvlv_value;int tvlv_len_ret;bool ret;spin_lock_bh (&bat_priv->tvlv.container_list_lock); tvlv_value_len =batadv_tvlv_container_list_size (bat_priv); +if (tvlv_value_len >U16_MAX ) { + tvlv_len_ret = -E2BIG ; + goto end; + } ret =batadv_tvlv_realloc_packet_buff (packet_buff, packet_buff_len, packet_min_len, tvlv_value_len);
Greg Kroah-Hartman
maintainerPATCH 144/461 batman-adv: iv: recover OGM scheduling after forward packet error
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit aa3153bd139a6c48667dcd02608d3b2c80bff02c upstream.
When batadv_iv_ogm_schedule_buff() fails to allocate and queue a forward packet for OGM transmission, the work item that drives periodic OGM scheduling is never re-armed. This silently halts transmission of the node's own OGMs on the affected interface — only OGMs from other peers continue to be aggregated and forwarded.
Fix this by tracking whether batadv_iv_ogm_queue_add() (and transitively batadv_iv_ogm_aggregate_new()) successfully scheduled a forward packet. When scheduling fails, batadv_iv_ogm_schedule_buff() falls back to queuing a dedicated recovery work item (reschedule_work) that fires after one originator interval and calls batadv_iv_ogm_schedule() again.
--- net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++++++++----------- net/batman-adv/types.h | 3 + 2 files changed, 60 insertions(+), 19 deletions(-)
net/batman-adv/bat_iv_ogm.c +57 -19
@@ -224,6 +224,8 @@ static void batadv_iv_ogm_iface_disable( hard_iface->bat_iv.ogm_buff =NULL ;mutex_unlock (&hard_iface->bat_iv.ogm_buff_mutex); + +cancel_delayed_work_sync (&hard_iface->bat_iv.reschedule_work); }static void batadv_iv_ogm_iface_update_mac (struct batadv_hard_iface *hard_iface)
@@ -536,8 +538,10 @@ out:* @if_incoming: interface where the packet was received * @if_outgoing: interface for which the retransmission should be considered * @own_packet: true if it is a self-generated ogm +* +* Return: whether forward packet was scheduled */ -static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, +static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, int packet_len, unsigned long send_time, bool direct_link, struct batadv_hard_iface *if_incoming,
@@ -561,13 +565,13 @@ static void batadv_iv_ogm_aggregate_new( skb =netdev_alloc_skb_ip_align (NULL , skb_size);if (!skb) -return ; +return false; forw_packet_aggr =batadv_forw_packet_alloc (if_incoming, if_outgoing, queue_left, bat_priv, skb);if (!forw_packet_aggr) {kfree_skb (skb); -return ; +return false; } forw_packet_aggr->skb->priority =TC_PRIO_CONTROL ;
@@ -590,6 +594,8 @@ static void batadv_iv_ogm_aggregate_new( batadv_iv_send_outstanding_bat_ogm_packet);batadv_forw_packet_ogmv1_queue (bat_priv, forw_packet_aggr, send_time); + +return true; }/* aggregate a new packet into the existing ogm packet */
@@ -617,8 +623,10 @@ static void batadv_iv_ogm_aggregate(stru* @if_outgoing: interface for which the retransmission should be considered * @own_packet: true if it is a self-generated ogm * @send_time: timestamp (jiffies) when the packet is to be sent +* +* Return: whether forward packet was scheduled */ -static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, +static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, unsigned char *packet_buff, int packet_len, struct batadv_hard_iface *if_incoming,
@@ -670,14 +678,16 @@ static void batadv_iv_ogm_queue_add(struif (!own_packet &&atomic_read (&bat_priv->aggregated_ogms)) send_time += max_aggregation_jiffies; -batadv_iv_ogm_aggregate_new (packet_buff, packet_len, - send_time, direct_link, - if_incoming, if_outgoing, - own_packet); +return batadv_iv_ogm_aggregate_new (packet_buff, packet_len, + send_time, direct_link, + if_incoming, if_outgoing, + own_packet); }else {batadv_iv_ogm_aggregate (forw_packet_aggr, packet_buff, packet_len, direct_link);spin_unlock_bh (&bat_priv->forw_bat_list_lock); + +return true; } }
@@ -790,6 +800,8 @@ static void batadv_iv_ogm_schedule_buff(u32 seqno;u16 tvlv_len =0 ;unsigned long send_time; +bool reschedule = false; +bool scheduled;int ret;lockdep_assert_held (&hard_iface->bat_iv.ogm_buff_mutex);
@@ -818,11 +830,8 @@ static void batadv_iv_ogm_schedule_buff( ogm_buff_len, BATADV_OGM_HLEN);if (ret <0 ) { -/* OGMs must be queued even when the buffer allocation for -* TVLVs failed. just fall back to the non-TVLV version -*/ - ret =0 ; - *ogm_buff_len =BATADV_OGM_HLEN ; + reschedule = true; + goto out; } tvlv_len = ret;
@@ -844,8 +853,11 @@ static void batadv_iv_ogm_schedule_buff(/* OGMs from secondary interfaces are only scheduled on their * respective interfaces. */ -batadv_iv_ogm_queue_add (bat_priv, *ogm_buff, *ogm_buff_len, - hard_iface, hard_iface,1 , send_time); + scheduled =batadv_iv_ogm_queue_add (bat_priv, *ogm_buff, *ogm_buff_len, + hard_iface, hard_iface,1 , send_time); +if (!scheduled) + reschedule = true; + goto out; }
@@ -857,15 +869,28 @@ static void batadv_iv_ogm_schedule_buff(if (!kref_get_unless_zero (&tmp_hard_iface->refcount))continue ; -batadv_iv_ogm_queue_add (bat_priv, *ogm_buff, - *ogm_buff_len, hard_iface, - tmp_hard_iface,1 , send_time); - + scheduled =batadv_iv_ogm_queue_add (bat_priv, *ogm_buff, + *ogm_buff_len, hard_iface, + tmp_hard_iface,1 , send_time);batadv_hardif_put (tmp_hard_iface); + +if (!scheduled && tmp_hard_iface == hard_iface) + reschedule = true; }rcu_read_unlock (); out: +if (reschedule) { +/* there was a failure scheduling the own forward packet. +* as result, the batadv_iv_send_outstanding_bat_ogm_packet() +* work item is no longer scheduled. it is therefore necessary +* to reschedule it manually +*/ +queue_delayed_work (batadv_event_workqueue, + &hard_iface->bat_iv.reschedule_work, +msecs_to_jiffies (atomic_read (&bat_priv->orig_interval))); + } +batadv_hardif_put (primary_if); }
@@ -880,6 +905,17 @@ static void batadv_iv_ogm_schedule(strucmutex_unlock (&hard_iface->bat_iv.ogm_buff_mutex); } +static void batadv_iv_ogm_reschedule (struct work_struct *work) +{ +struct delayed_work *delayed_work =to_delayed_work (work); +struct batadv_hard_iface *hard_iface; + + hard_iface =container_of (delayed_work, + struct batadv_hard_iface, + bat_iv.reschedule_work); +batadv_iv_ogm_schedule (hard_iface); +} +/** * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface * @orig_node: originator which reproadcasted the OGMs directly
@@ -2272,6 +2308,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadvstatic void batadv_iv_iface_enabled (struct batadv_hard_iface *hard_iface) { +INIT_DELAYED_WORK (&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule); +/* begin scheduling originator messages on that interface */ batadv_iv_ogm_schedule (hard_iface); }
net/batman-adv/types.h +3 -0
@@ -83,6 +83,9 @@ struct batadv_hard_iface_bat_iv {/** @ogm_seqno: OGM sequence number - used to identify each OGM */ atomic_t ogm_seqno; +/** @reschedule_work: recover OGM schedule after schedule error */ +struct delayed_work reschedule_work; +/** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */ struct mutex ogm_buff_mutex; };
Greg Kroah-Hartman
maintainerPATCH 145/461 batman-adv: mcast: fix use-after-free in orig_node RCU release
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 20c2d6a20ca936f5aaa6dd40f73f262ac45c87cc upstream.
batadv_mcast_purge_orig() removes entries from RCU-protected hlists but does not wait for an RCU grace period before returning. Concurrent RCU readers may still accesses references to those entries at the point of removal. RCU-protected readers trying to operate on entries like orig->mcast_want_all_ipv6_node will then access already freed memory.
Fix this by moving batadv_mcast_purge_orig() to batadv_orig_node_release(), just before the call_rcu() invocation. This ensures RCU readers that were active at purge time have drained before the orig_node memory is reclaimed.
--- net/batman-adv/originator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/batman-adv/originator.c +2 -2
@@ -835,8 +835,6 @@ static void batadv_orig_node_free_rcu(st orig_node = container_of(rcu, struct batadv_orig_node, rcu); -batadv_mcast_purge_orig (orig_node); -batadv_frag_purge_orig (orig_node,NULL );kfree (orig_node->tt_buff);
@@ -887,6 +885,8 @@ void batadv_orig_node_release(struct kre }spin_unlock_bh (&orig_node->vlan_list_lock); +batadv_mcast_purge_orig (orig_node); +call_rcu (&orig_node->rcu, batadv_orig_node_free_rcu); }
Greg Kroah-Hartman
maintainerPATCH 146/461 batman-adv: clear current gateway during teardown
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ruijie Li <[email protected]>
commit a340a51ed801eab7bb454150c226323b865263cc upstream.
batadv_gw_node_free() removes the gateway list entries during mesh teardown, but it does not clear the currently selected gateway. This leaves stale gateway state behind across cleanup and can break a later mesh recreation.
Clear bat_priv->gw.curr_gw before walking the gateway list so the selected gateway reference is dropped as part of teardown.
--- net/batman-adv/gateway_client.c | 4 ++++ 1 file changed, 4 insertions(+)
net/batman-adv/gateway_client.c +4 -0
@@ -478,10 +478,14 @@ void batadv_gw_node_delete(struct batadv*/ void batadv_gw_node_free (struct batadv_priv *bat_priv) { +struct batadv_gw_node *curr_gw;struct batadv_gw_node *gw_node;struct hlist_node *node_tmp;spin_lock_bh (&bat_priv->gw.list_lock); + curr_gw =rcu_replace_pointer (bat_priv->gw.curr_gw,NULL , true); +batadv_gw_node_put (curr_gw); +hlist_for_each_entry_safe (gw_node, node_tmp, &bat_priv->gw.gateway_list, list) {hlist_del_init_rcu (&gw_node->list);
Greg Kroah-Hartman
maintainerPATCH 147/461 batman-adv: dat: handle forward allocation error
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 2d8826a2d3657cea66fb0370f9e521575a673871 upstream.
batadv_dat_forward_data() calls pskb_copy_for_clone() to duplicate an skb for each DHT candidate, but does not check the return value before passing it to batadv_send_skb_prepare_unicast_4addr(). That function dereferences the skb unconditionally, so a failed allocation triggers a NULL pointer dereference.
Skip forwarding to the current DHT candidate on allocation failure.
--- net/batman-adv/distributed-arp-table.c | 3 +++ 1 file changed, 3 insertions(+)
net/batman-adv/distributed-arp-table.c +3 -0
@@ -696,6 +696,9 @@ static bool batadv_dat_forward_data(stru goto free_orig; tmp_skb =pskb_copy_for_clone (skb,GFP_ATOMIC ); +if (!tmp_skb) + goto free_neigh; +if (!batadv_send_skb_prepare_unicast_4addr (bat_priv, tmp_skb, cand[i].orig_node, packet_subtype)) {
Greg Kroah-Hartman
maintainerPATCH 148/461 batman-adv: fix fragment reassembly length accounting
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ruide Cao <[email protected]>
commit 9cd3f16c320bfdadd4509358122368deb56a5741 upstream.
batman-adv keeps a running payload length for queued fragments and uses it to validate a fragment chain before reassembly.
That accounting currently allows the accumulated fragment length to be truncated during updates. As a result, malformed fragment chains can bypass the intended validation and drive reassembly with inconsistent length state, leading to a local denial of service.
Fix the accounting by storing the accumulated length in a length-typed field and rejecting update overflows before the existing validation logic runs.
The fix was verified against the original reproducer and against valid fragment reassembly paths.
--- net/batman-adv/fragmentation.c | 23 +++++++++++++++++------ net/batman-adv/types.h | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-)
net/batman-adv/fragmentation.c +17 -6
@@ -17,6 +17,7 @@#include <linux/lockdep.h> #include <linux/minmax.h> #include <linux/netdevice.h> +#include <linux/overflow.h> #include <linux/skbuff.h> #include <linux/slab.h> #include <linux/spinlock.h>
@@ -80,9 +81,9 @@ void batadv_frag_purge_orig(struct batad* * Return: the maximum size of payload that can be fragmented. */ -static int batadv_frag_size_limit (void ) +static size_t batadv_frag_size_limit (void ) { -int limit =BATADV_FRAG_MAX_FRAG_SIZE ; +size_t limit =BATADV_FRAG_MAX_FRAG_SIZE ; limit -=sizeof (struct batadv_frag_packet ); limit *=BATADV_FRAG_MAX_FRAGMENTS ;
@@ -143,7 +144,9 @@ static bool batadv_frag_insert_packet(ststruct batadv_frag_packet *frag_packet;u8 bucket;u16 seqno, hdr_size =sizeof (struct batadv_frag_packet ); +bool overflow = false;bool ret = false; +size_t data_len;/* Linearize packet to avoid linearizing 16 packets in a row when doing * the later merge. Non-linear merge should be added to remove this
@@ -153,6 +156,7 @@ static bool batadv_frag_insert_packet(st goto err; frag_packet = (struct batadv_frag_packet *)skb->data; + data_len = skb->len - hdr_size; seqno =ntohs (frag_packet->seqno); bucket = seqno %BATADV_FRAG_BUFFER_COUNT ;
@@ -171,7 +175,7 @@ static bool batadv_frag_insert_packet(stspin_lock_bh (&chain->lock);if (batadv_frag_init_chain (chain, seqno)) {hlist_add_head (&frag_entry_new->list, &chain->fragment_list); - chain->size = skb->len - hdr_size; + chain->size = data_len; chain->timestamp = jiffies; chain->total_size =ntohs (frag_packet->total_size); ret = true;
@@ -188,7 +192,11 @@ static bool batadv_frag_insert_packet(stif (frag_entry_curr->no < frag_entry_new->no) {hlist_add_before (&frag_entry_new->list, &frag_entry_curr->list); - chain->size += skb->len - hdr_size; + +if (check_add_overflow (chain->size, data_len, + &chain->size)) + overflow = true; + chain->timestamp = jiffies; ret = true; goto out;
@@ -201,13 +209,16 @@ static bool batadv_frag_insert_packet(st/* Reached the end of the list, so insert after 'frag_entry_last'. */ if (likely (frag_entry_last)) {hlist_add_behind (&frag_entry_new->list, &frag_entry_last->list); - chain->size += skb->len - hdr_size; + +if (check_add_overflow (chain->size, data_len, &chain->size)) + overflow = true; + chain->timestamp = jiffies; ret = true; } out: -if (chain->size >batadv_frag_size_limit () || +if (overflow || chain->size >batadv_frag_size_limit () || chain->total_size !=ntohs (frag_packet->total_size) || chain->total_size >batadv_frag_size_limit ()) {/* Clear chain if total size of either the list or the packet
net/batman-adv/types.h +1 -1
@@ -304,7 +304,7 @@ struct batadv_frag_table_entry {u16 seqno;/** @size: accumulated size of packets in list */ -u16 size; +size_t size;/** @total_size: expected size of the assembled packet */ u16 total_size;
Greg Kroah-Hartman
maintainerPATCH 149/461 batman-adv: fix tp_meter counter underflow during shutdown
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luxiao Xu <[email protected]>
commit 94f3b133168d1c49895e7cc6afbcf1cc0b354602 upstream.
batadv_tp_sender_shutdown() unconditionally decrements the "sending" atomic counter. If multiple paths (e.g. timeout, user cancel, and normal finish) call this function, the counter can underflow to -1.
Since the sender logic treats any non-zero value as "still sending", a negative value causes the sender kthread to loop indefinitely. This leads to a use-after-free when the interface is removed while the zombie thread is still active.
Fix this by using atomic_xchg() to ensure the counter only transitions from 1 to 0 once.
--- net/batman-adv/tp_meter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/batman-adv/tp_meter.c +2 -2
@@ -451,7 +451,7 @@ static void batadv_tp_sender_end(structstatic void batadv_tp_sender_shutdown (struct batadv_tp_vars *tp_vars,enum batadv_tp_meter_reason reason) { -if (!atomic_dec_and_test (&tp_vars->sending)) +if (atomic_xchg (&tp_vars->sending,0 ) !=1 )return ; tp_vars->reason = reason;
@@ -885,7 +885,7 @@ static int batadv_tp_send(void *arg)"Meter: %s() cannot send packets (%d)\n" , __func__, err);/* ensure nobody else tries to stop the thread now */ -if (atomic_dec_and_test (&tp_vars->sending)) +if (atomic_xchg (&tp_vars->sending,0 ) ==1 ) tp_vars->reason = err;break ; }
Greg Kroah-Hartman
maintainerPATCH 150/461 batman-adv: frag: disallow unicast fragment in fragment
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit bc62216dc8e221e3781afa14430f45208bfa9af9 upstream.
batadv_frag_skb_buffer() is called by batadv_batman_skb_recv() when a BATADV_UNICAST_FRAG packet is received. Once all fragments are collected and the packet is reassembled, batadv_recv_frag_packet() calls batadv_batman_skb_recv() again to process the defragmented payload.
A malicious sender can craft a BATADV_UNICAST_FRAG packet whose reassembled payload is itself a BATADV_UNICAST_FRAG packet (matryoshka-style nesting). Each nesting level recurses through batadv_batman_skb_recv() without bound, growing the kernel stack until it is exhausted.
Since refragmentation or fragments in fragments are not actually allowed, discard all packets which are still BATADV_UNICAST_FRAG packets after the defragmentation process.
--- net/batman-adv/fragmentation.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+)
net/batman-adv/fragmentation.c +35 -0
@@ -305,6 +305,31 @@ free: }/** +* batadv_skb_is_frag() - check if newly merged skb is gain a unicast packet +* @skb: newly merged skb +* +* Return: if newly skb is of type BATADV_UNICAST_FRAG +*/ +static bool batadv_skb_is_frag (struct sk_buff *skb) +{ +struct batadv_ogm_packet *batadv_ogm_packet; + +/* packet should hold at least type and version */ +if (unlikely (!pskb_may_pull (skb,2 ))) +return false; + + batadv_ogm_packet = (struct batadv_ogm_packet *)skb->data; + +if (batadv_ogm_packet->version !=BATADV_COMPAT_VERSION ) +return false; + +if (batadv_ogm_packet->packet_type !=BATADV_UNICAST_FRAG ) +return false; + +return true; +} + +/** * batadv_frag_skb_buffer() - buffer fragment for later merge * @skb: skb to buffer * @orig_node_src: originator that the skb is received from
@@ -337,6 +362,16 @@ bool batadv_frag_skb_buffer(struct sk_buif (!skb_out) goto out_err; +/* fragment in fragment is not allowed. otherwise it is possible +* to exhaust the stack when receiving a matryoshka-style +* "fragments in a fragment packet" +*/ +if (batadv_skb_is_frag (skb_out)) { +kfree_skb (skb_out); + skb_out =NULL ; + goto out_err; + } + out: ret = true; out_err:
Greg Kroah-Hartman
maintainerPATCH 151/461 batman-adv: bla: fix report_work leak on backbone_gw purge
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 0459430add32ea41f3e2ef9351610e6d33627a6b upstream.
batadv_bla_purge_backbone_gw() removes stale backbone gateway entries, but fails to properly handle their associated report_work:
- If report_work is running, the purge must wait for it to finish before freeing the backbone_gw, otherwise the worker may access freed memory (e.g. bat_priv).
- If report_work is pending, the purge must cancel it and release the reference held for that pending work item.
The previous implementation called hlist_for_each_entry_safe() inside a spin_lock_bh() section, but cancel_work_sync() may sleep and therefore cannot be called from within a spinlock-protected region.
Restructure the loop to handle one entry per spinlock critical section:
acquire the lock, find the next entry to purge, remove it from the hash list, then release the lock before calling cancel_work_sync() and dropping the hash_entry reference. Repeat until no more entries require purging.
--- net/batman-adv/bridge_loop_avoidance.c | 60 ++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 22 deletions(-)
net/batman-adv/bridge_loop_avoidance.c +38 -22
@@ -1224,6 +1224,7 @@ static void batadv_bla_purge_backbone_gwstruct hlist_head *head;struct batadv_hashtable *hash;spinlock_t *list_lock;/* protects write access to the hash lists */ +bool purged;int i; hash = bat_priv->bla.backbone_hash;
@@ -1234,30 +1235,45 @@ static void batadv_bla_purge_backbone_gw head = &hash->table[i]; list_lock = &hash->list_locks[i]; -spin_lock_bh (list_lock); -hlist_for_each_entry_safe (backbone_gw, node_tmp, - head, hash_entry) { -if (now) - goto purge_now; -if (!batadv_has_timed_out (backbone_gw->lasttime, -BATADV_BLA_BACKBONE_TIMEOUT )) -continue ; - -batadv_dbg (BATADV_DBG_BLA , backbone_gw->bat_priv, -"%s(): backbone gw %pM timed out\n" , - __func__, backbone_gw->orig); +do { + purged = false; + +spin_lock_bh (list_lock); +hlist_for_each_entry_safe (backbone_gw, node_tmp, + head, hash_entry) { +if (now) + goto purge_now; +if (!batadv_has_timed_out (backbone_gw->lasttime, +BATADV_BLA_BACKBONE_TIMEOUT )) +continue ; + +batadv_dbg (BATADV_DBG_BLA , backbone_gw->bat_priv, +"%s(): backbone gw %pM timed out\n" , + __func__, backbone_gw->orig); purge_now: -/* don't wait for the pending request anymore */ -if (atomic_read (&backbone_gw->request_sent)) -atomic_dec (&bat_priv->bla.num_requests); - -batadv_bla_del_backbone_claims (backbone_gw); - -hlist_del_rcu (&backbone_gw->hash_entry); -batadv_backbone_gw_put (backbone_gw); - } -spin_unlock_bh (list_lock); + purged = true; + +/* don't wait for the pending request anymore */ +if (atomic_read (&backbone_gw->request_sent)) +atomic_dec (&bat_priv->bla.num_requests); + +batadv_bla_del_backbone_claims (backbone_gw); + +hlist_del_rcu (&backbone_gw->hash_entry); +break ; + } +spin_unlock_bh (list_lock); + +if (purged) { +/* reference for pending report_work */ +if (cancel_work_sync (&backbone_gw->report_work)) +batadv_backbone_gw_put (backbone_gw); + +/* reference for hash_entry */ +batadv_backbone_gw_put (backbone_gw); + } + }while (purged); } }
Greg Kroah-Hartman
maintainerPATCH 152/461 batman-adv: bla: avoid double decrement of bla.num_requests
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 83ab69bd12b80f6ea169c8bea6977701b53a043d upstream.
The bla.num_requests is increased when no request_sent was in progress. And it is decremented in various places (announcement was received, backbone is purged, periodic work). But the check if the request_sent is actually set to a specific state and the atomic_dec/_inc are not safe because they are not atomic (TOCTOU) and multiple such code portions can run concurrently.
At the same time, it is necessary to modify request_sent (state) and bla.num_requests atomically. Otherwise batadv_bla_send_request() might set request_sent to 1 and is interrupted. batadv_handle_announce() can then set request_sent back to 0 and decrement num_requests before batadv_bla_send_request() incremented it.
The two operations must therefore be locked. And since state (request_sent) and wait_periods are only accessed inside this lock, they can be converted to simpler datatypes. And to avoid that the bla.num_requests is touched by a parallel running context with a valid backbone_gw reference after batadv_bla_purge_backbone_gw() ran, a third state "stopped" is required to correctly signal that a backbone_gw is in the state of being cleaned up.
--- net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++++++----------- net/batman-adv/mesh-interface.c | 1 net/batman-adv/types.h | 39 ++++++++++++++++++++----- 3 files changed, 67 insertions(+), 24 deletions(-)
net/batman-adv/bridge_loop_avoidance.c +35 -16
@@ -514,8 +514,8 @@ batadv_bla_get_backbone_gw(struct batadv entry->crc =BATADV_BLA_CRC_INIT ; entry->bat_priv = bat_priv;spin_lock_init (&entry->crc_lock); -atomic_set (&entry->request_sent,0 ); -atomic_set (&entry->wait_periods,0 ); + entry->state =BATADV_BLA_BACKBONE_GW_SYNCED ; + entry->wait_periods =0 ;ether_addr_copy (entry->orig, orig);INIT_WORK (&entry->report_work, batadv_bla_loopdetect_report);kref_init (&entry->refcount);
@@ -544,9 +544,13 @@ batadv_bla_get_backbone_gw(struct batadvbatadv_bla_send_announce (bat_priv, entry);/* this will be decreased in the worker thread */ -atomic_inc (&entry->request_sent); -atomic_set (&entry->wait_periods,BATADV_BLA_WAIT_PERIODS ); -atomic_inc (&bat_priv->bla.num_requests); +spin_lock_bh (&bat_priv->bla.num_requests_lock); +if (entry->state ==BATADV_BLA_BACKBONE_GW_SYNCED ) { + entry->state =BATADV_BLA_BACKBONE_GW_UNSYNCED ; + entry->wait_periods =BATADV_BLA_WAIT_PERIODS ; +atomic_inc (&bat_priv->bla.num_requests); + } +spin_unlock_bh (&bat_priv->bla.num_requests_lock); }return entry;
@@ -649,10 +653,12 @@ static void batadv_bla_send_request(stru backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST);/* no local broadcasts should be sent or received, for now. */ -if (!atomic_read (&backbone_gw->request_sent)) { +spin_lock_bh (&backbone_gw->bat_priv->bla.num_requests_lock); +if (backbone_gw->state ==BATADV_BLA_BACKBONE_GW_SYNCED ) { + backbone_gw->state =BATADV_BLA_BACKBONE_GW_UNSYNCED ;atomic_inc (&backbone_gw->bat_priv->bla.num_requests); -atomic_set (&backbone_gw->request_sent,1 ); } +spin_unlock_bh (&backbone_gw->bat_priv->bla.num_requests_lock); }/**
@@ -873,10 +879,12 @@ static bool batadv_handle_announce(struc/* if we have sent a request and the crc was OK, * we can allow traffic again. */ -if (atomic_read (&backbone_gw->request_sent)) { +spin_lock_bh (&bat_priv->bla.num_requests_lock); +if (backbone_gw->state ==BATADV_BLA_BACKBONE_GW_UNSYNCED ) { + backbone_gw->state =BATADV_BLA_BACKBONE_GW_SYNCED ;atomic_dec (&backbone_gw->bat_priv->bla.num_requests); -atomic_set (&backbone_gw->request_sent,0 ); } +spin_unlock_bh (&bat_priv->bla.num_requests_lock); }batadv_backbone_gw_put (backbone_gw);
@@ -1255,9 +1263,13 @@ purge_now: purged = true;/* don't wait for the pending request anymore */ -if (atomic_read (&backbone_gw->request_sent)) +spin_lock_bh (&bat_priv->bla.num_requests_lock); +if (backbone_gw->state ==BATADV_BLA_BACKBONE_GW_UNSYNCED )atomic_dec (&bat_priv->bla.num_requests); + backbone_gw->state =BATADV_BLA_BACKBONE_GW_STOPPED ; +spin_unlock_bh (&bat_priv->bla.num_requests_lock); +batadv_bla_del_backbone_claims (backbone_gw);hlist_del_rcu (&backbone_gw->hash_entry);
@@ -1508,7 +1520,7 @@ static void batadv_bla_periodic_work(strbatadv_bla_send_loopdetect (bat_priv, backbone_gw); -/* request_sent is only set after creation to avoid +/* state is only set to unsynced after creation to avoid * problems when we are not yet known as backbone gw * in the backbone. *
@@ -1517,14 +1529,21 @@ static void batadv_bla_periodic_work(str* some grace time. */ -if (atomic_read (&backbone_gw->request_sent) ==0 ) -continue ; +spin_lock_bh (&bat_priv->bla.num_requests_lock); +if (backbone_gw->state !=BATADV_BLA_BACKBONE_GW_UNSYNCED ) + goto unlock_next; -if (!atomic_dec_and_test (&backbone_gw->wait_periods)) -continue ; +if (backbone_gw->wait_periods >0 ) + backbone_gw->wait_periods--; + +if (backbone_gw->wait_periods >0 ) + goto unlock_next; + backbone_gw->state =BATADV_BLA_BACKBONE_GW_SYNCED ;atomic_dec (&backbone_gw->bat_priv->bla.num_requests); -atomic_set (&backbone_gw->request_sent,0 ); + +unlock_next: +spin_unlock_bh (&bat_priv->bla.num_requests_lock); }rcu_read_unlock (); }
net/batman-adv/mesh-interface.c +1 -0
@@ -787,6 +787,7 @@ static int batadv_meshif_init_late(strucatomic_set (&bat_priv->tt.ogm_append_cnt,0 );#ifdef CONFIG_BATMAN_ADV_BLA atomic_set (&bat_priv->bla.num_requests,0 ); +spin_lock_init (&bat_priv->bla.num_requests_lock);#endif atomic_set (&bat_priv->tp_num,0 );
net/batman-adv/types.h +31 -8
@@ -1027,6 +1027,12 @@ struct batadv_priv_bla {atomic_t num_requests;/** +* @num_requests_lock: locks update num_requests + +* batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update +*/ +spinlock_t num_requests_lock; + +/** * @claim_hash: hash table containing mesh nodes this host has claimed */ struct batadv_hashtable *claim_hash;
@@ -1669,6 +1675,27 @@ struct batadv_priv {#ifdef CONFIG_BATMAN_ADV_BLA +enum batadv_bla_backbone_gw_state { +/** +* @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed +* and it must not longer work on requests +*/ +BATADV_BLA_BACKBONE_GW_STOPPED , + +/** +* @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out +* of sync and a request was send. No traffic is forwarded until the +* situation is resolved +*/ +BATADV_BLA_BACKBONE_GW_UNSYNCED , + +/** +* @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in +* sync. traffic can be forwarded +*/ +BATADV_BLA_BACKBONE_GW_SYNCED , +}; +/** * struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN */
@@ -1694,16 +1721,12 @@ struct batadv_bla_backbone_gw {/** * @wait_periods: grace time for bridge forward delays and bla group * forming at bootup phase - no bcast traffic is formwared until it has -* elapsed +* elapsed. Must only be access with num_requests_lock. */ -atomic_t wait_periods; +u8 wait_periods; -/** -* @request_sent: if this bool is set to true we are out of sync with -* this backbone gateway - no bcast traffic is formwared until the -* situation was resolved -*/ -atomic_t request_sent; +/** @state: sync state. Must only be access with num_requests_lock. */ +enum batadv_bla_backbone_gw_state state;/** @crc: crc16 checksum over all claims */ u16 crc;
Greg Kroah-Hartman
maintainerPATCH 153/461 batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit f80d3d98d2ff78d9e2fe5d68b1f45948c4f7bd24 upstream.
Without rtnl_lock held, a hardif might be retrieved as primary interface of a meshif, but then (while operating on this interface) getting decoupled from the mesh interface. In this case, the meshif still exists but the pointer from the primary hardif to the meshif is set to NULL.
The mesh_iface must be checked first to be non-NULL before continuing to send an ARP request using meshif.
--- net/batman-adv/bridge_loop_avoidance.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
net/batman-adv/bridge_loop_avoidance.c +4 -2
@@ -356,12 +356,14 @@ static void batadv_bla_send_claim(struct sizeof(local_claim_dest)); local_claim_dest.type = claimtype; - mesh_iface = primary_if->mesh_iface; + mesh_iface =READ_ONCE (primary_if->mesh_iface); +if (!mesh_iface) + goto out; skb = arp_create(ARPOP_REPLY, ETH_P_ARP, /* IP DST: 0.0.0.0 */ zeroip, - primary_if->mesh_iface, + mesh_iface, /* IP SRC: 0.0.0.0 */ zeroip,/* Ethernet DST: Broadcast */
Greg Kroah-Hartman
maintainerPATCH 154/461 batman-adv: tp_meter: avoid use of uninit sender vars
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 6c65cf23d4c6170fcf5714c32aa64689718cb142 upstream.
batadv_tp_recv_ack() and batadv_tp_stop() are only valid for tp_vars in the BATADV_TP_SENDER role. When called with a BATADV_TP_RECEIVER role, it proceeds to read sender-only members that were never initialized, leading to undefined behavior.
This can be triggered when a node that is currently acting as a receiver in an ongoing tp_meter session receives a malicious ACK packet.
Guard against this by checking tp_vars->role immediately after the lookup and bailing out if it is not BATADV_TP_SENDER, before any of those members are accessed.
--- net/batman-adv/tp_meter.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
net/batman-adv/tp_meter.c +9 -2
@@ -663,6 +663,9 @@ static void batadv_tp_recv_ack(struct baif (unlikely (!tp_vars))return ; +if (unlikely (tp_vars->role !=BATADV_TP_SENDER )) + goto out; +if (unlikely (atomic_read (&tp_vars->sending) ==0 )) goto out;
@@ -1100,12 +1103,16 @@ void batadv_tp_stop(struct batadv_priv *if (!tp_vars) {batadv_dbg (BATADV_DBG_TP_METER , bat_priv,"Meter: trying to interrupt an already over connection\n" ); - goto out; + goto out_put_orig_node; } +if (unlikely (tp_vars->role !=BATADV_TP_SENDER )) + goto out_put_tp_vars; +batadv_tp_sender_shutdown (tp_vars, return_value); +out_put_tp_vars:batadv_tp_vars_put (tp_vars); -out: +out_put_orig_node:batadv_orig_node_put (orig_node); }
Greg Kroah-Hartman
maintainerPATCH 155/461 batman-adv: tp_meter: directly shut down timer on cleanup
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit d5487249a81ea658717614009c8f46acc5b7101a upstream.
batadv_tp_sender_cleanup() was calling timer_delete_sync() followed by timer_delete() to guard against the timer handler re-arming itself between the two calls. This double-deletion hack relied on the sending status being set to 0 to suppress re-arming.
Replace both calls with a single timer_shutdown_sync(). This function both waits for any running timer callback to complete (like timer_delete_sync()) and permanently disarms the timer so it cannot be re-armed afterwards, making re-arming prevention unconditional and self-documenting.
The re-arming property is also required because otherwise:
1. context 0 (batadv_tp_recv_ack()) checks in batadv_tp_reset_sender_timer() if sending is still 1 -> it is
2. context 1 changes in batadv_tp_sender_shutdown() sending to 0 and in this process forces the kthread to stop timer in batadv_tp_sender_cleanup()
3. context 0 continues in batadv_tp_reset_sender_timer() and rearms the timer -> but the reference for it is already gone
--- net/batman-adv/tp_meter.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
net/batman-adv/tp_meter.c +1 -7
@@ -400,13 +400,7 @@ static void batadv_tp_sender_cleanup(strbatadv_tp_list_detach (tp_vars);/* kill the timer and remove its reference */ -timer_delete_sync (&tp_vars->timer); -/* the worker might have rearmed itself therefore we kill it again. Note -* that if the worker should run again before invoking the following -* timer_delete(), it would not re-arm itself once again because the status -* is OFF now -*/ -timer_delete (&tp_vars->timer); +timer_shutdown_sync (&tp_vars->timer);batadv_tp_vars_put (tp_vars); }
Greg Kroah-Hartman
maintainerPATCH 156/461 batman-adv: tp_meter: fix tp_vars reference leak in receiver shutdown
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 77098e4bea37af51d3962efa88a5af2ea5e1ac57 upstream.
The receiver shutdown timer handler, batadv_tp_receiver_shutdown(), is responsible for releasing the tp_vars reference it holds. However, the existing logic for coordinating this release with batadv_tp_stop_all() was flawed.
timer_shutdown_sync() guarantees the timer will not fire again after it returns, but it returns non-zero only when the timer was pending at the time of the call. If the timer had already expired (and batadv_tp_stop_all() would unsucessfully try to rearm itself), batadv_tp_stop_all() skips its batadv_tp_vars_put(), and batadv_tp_receiver_shutdown() fails to put its own reference as well.
Fix this by introducing a new atomic variable receiving that is set to 1 when the receiver is initialized and cleared atomically with atomic_xchg() by whichever side claims it first. Only the side that observes the transition from 1 to 0 is responsible for releasing the tp_vars timer reference, eliminating the uncertainty.
--- net/batman-adv/tp_meter.c | 13 +++++++++++-- net/batman-adv/types.h | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-)
net/batman-adv/tp_meter.c +11 -2
@@ -8,6 +8,7 @@#include "main.h" #include <linux/atomic.h> +#include <linux/bug.h> #include <linux/build_bug.h> #include <linux/byteorder/generic.h> #include <linux/cache.h>
@@ -1157,6 +1158,9 @@ static void batadv_tp_receiver_shutdown(spin_unlock_bh (&tp_vars->unacked_lock);/* drop reference of timer */ +if (WARN_ON (atomic_xchg (&tp_vars->receiving,0 ) !=1 )) +return ; +batadv_tp_vars_put (tp_vars); }
@@ -1375,6 +1379,7 @@ batadv_tp_init_recv(struct batadv_priv *ether_addr_copy (tp_vars->other_end, icmp->orig); tp_vars->role =BATADV_TP_RECEIVER ; +atomic_set (&tp_vars->receiving,1 );memcpy (tp_vars->session, icmp->session,sizeof (tp_vars->session)); tp_vars->last_recv =BATADV_TP_FIRST_SEQ ; tp_vars->bat_priv = bat_priv;
@@ -1547,8 +1552,12 @@ void batadv_tp_stop_all(struct batadv_prbreak ;case BATADV_TP_RECEIVER :batadv_tp_list_detach (tp_var); -if (timer_shutdown_sync (&tp_var->timer)) -batadv_tp_vars_put (tp_var); +timer_shutdown_sync (&tp_var->timer); + +if (atomic_xchg (&tp_var->receiving,0 ) !=1 ) +break ; + +batadv_tp_vars_put (tp_var);break ; }
net/batman-adv/types.h +3 -0
@@ -1332,6 +1332,9 @@ struct batadv_tp_vars {/** @sending: sending binary semaphore: 1 if sending, 0 is not */ atomic_t sending; +/** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */ +atomic_t receiving; +/** @reason: reason for a stopped session */ enum batadv_tp_meter_reason reason;
Greg Kroah-Hartman
maintainerPATCH 157/461 batman-adv: tp_meter: fix race condition in send error reporting
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 71dce47f0758537fff78fddb5fb0d4632d29b29f upstream.
batadv_tp_sender_shutdown() previously used two separate variables to track session state: sending (an atomic flag indicating whether the session was active) and reason (a plain enum storing the stop reason). This introduced a race window between the two writes: after sending was cleared to 0, batadv_tp_send() could observe the stopped state and call batadv_tp_sender_end() before reason was written, causing the wrong stop reason to be reported to the caller.
Fix this by consolidating both variables into a single atomic send_result, which holds 0 while the session is running and the stop reason once it ends.
--- net/batman-adv/tp_meter.c | 40 +++++++++++++++++++++++++--------------- net/batman-adv/types.h | 10 +++++----- 2 files changed, 30 insertions(+), 20 deletions(-)
net/batman-adv/tp_meter.c +25 -15
@@ -413,11 +413,14 @@ static void batadv_tp_sender_cleanup(strstatic void batadv_tp_sender_end (struct batadv_priv *bat_priv,struct batadv_tp_vars *tp_vars) { +enum batadv_tp_meter_reason reason;u32 session_cookie; + reason =atomic_read (&tp_vars->send_result); +batadv_dbg (BATADV_DBG_TP_METER , bat_priv,"Test towards %pM finished..shutting down (reason=%d)\n" , - tp_vars->other_end, tp_vars->reason); + tp_vars->other_end, reason); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n",
@@ -430,7 +433,7 @@ static void batadv_tp_sender_end(struct session_cookie =batadv_tp_session_cookie (tp_vars->session, tp_vars->icmp_uid); - batadv_tp_batctl_notify(tp_vars->reason, + batadv_tp_batctl_notify(reason, tp_vars->other_end, bat_priv, tp_vars->start_time,
@@ -446,10 +449,18 @@ static void batadv_tp_sender_end(structstatic void batadv_tp_sender_shutdown (struct batadv_tp_vars *tp_vars,enum batadv_tp_meter_reason reason) { -if (atomic_xchg (&tp_vars->sending,0 ) !=1 ) -return ; +atomic_cmpxchg (&tp_vars->send_result,0 , reason); +} - tp_vars->reason = reason; +/** +* batadv_tp_sender_stopped() - check if tp session was stopped with reason +* @tp_vars: the private data of the current TP meter session +* +* Return: whether stop reason was found +*/ +static bool batadv_tp_sender_stopped (struct batadv_tp_vars *tp_vars) +{ +return atomic_read (&tp_vars->send_result) !=0 ; }/**
@@ -479,7 +490,7 @@ static void batadv_tp_reset_sender_timer/* most of the time this function is invoked while normal packet * reception... */ -if (unlikely (atomic_read (&tp_vars->sending) ==0 )) +if (unlikely (batadv_tp_sender_stopped (tp_vars)))/* timer ref will be dropped in batadv_tp_sender_cleanup */ return ;
@@ -499,7 +510,7 @@ static void batadv_tp_sender_timeout(strstruct batadv_tp_vars *tp_vars =timer_container_of (tp_vars, t, timer);struct batadv_priv *bat_priv = tp_vars->bat_priv; -if (atomic_read (&tp_vars->sending) ==0 ) +if (batadv_tp_sender_stopped (tp_vars))return ;/* if the user waited long enough...shutdown the test */
@@ -661,7 +672,7 @@ static void batadv_tp_recv_ack(struct baif (unlikely (tp_vars->role !=BATADV_TP_SENDER )) goto out; -if (unlikely (atomic_read (&tp_vars->sending) ==0 )) +if (unlikely (batadv_tp_sender_stopped (tp_vars))) goto out;/* old ACK? silently drop it.. */
@@ -827,21 +838,21 @@ static int batadv_tp_send(void *arg)if (unlikely (tp_vars->role !=BATADV_TP_SENDER )) { err =BATADV_TP_REASON_DST_UNREACHABLE ; - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err); goto out; } orig_node =batadv_orig_hash_find (bat_priv, tp_vars->other_end);if (unlikely (!orig_node)) { err =BATADV_TP_REASON_DST_UNREACHABLE ; - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err); goto out; } primary_if =batadv_primary_if_get_selected (bat_priv);if (unlikely (!primary_if)) { err =BATADV_TP_REASON_DST_UNREACHABLE ; - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err); goto out; }
@@ -860,7 +871,7 @@ static int batadv_tp_send(void *arg)queue_delayed_work (batadv_event_workqueue, &tp_vars->finish_work,msecs_to_jiffies (tp_vars->test_length)); -while (atomic_read (&tp_vars->sending) !=0 ) { +while (!batadv_tp_sender_stopped (tp_vars)) {if (unlikely (!batadv_tp_avail (tp_vars, payload_len))) {batadv_tp_wait_available (tp_vars, payload_len);continue ;
@@ -883,8 +894,7 @@ static int batadv_tp_send(void *arg)"Meter: %s() cannot send packets (%d)\n" , __func__, err);/* ensure nobody else tries to stop the thread now */ -if (atomic_xchg (&tp_vars->sending,0 ) ==1 ) - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err);break ; }
@@ -1006,7 +1016,7 @@ void batadv_tp_start(struct batadv_privether_addr_copy (tp_vars->other_end, dst);kref_init (&tp_vars->refcount); tp_vars->role =BATADV_TP_SENDER ; -atomic_set (&tp_vars->sending,1 ); +atomic_set (&tp_vars->send_result,0 );memcpy (tp_vars->session, session_id,sizeof (session_id)); tp_vars->icmp_uid = icmp_uid;
net/batman-adv/types.h +5 -5
@@ -1329,15 +1329,15 @@ struct batadv_tp_vars {/** @role: receiver/sender modi */ enum batadv_tp_meter_role role; -/** @sending: sending binary semaphore: 1 if sending, 0 is not */ -atomic_t sending; +/** +* @send_result: 0 when sending is ongoing and otherwise +* enum batadv_tp_meter_reason +*/ +atomic_t send_result;/** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */ atomic_t receiving; -/** @reason: reason for a stopped session */ -enum batadv_tp_meter_reason reason; -/** @finish_work: work item for the finishing procedure */ struct delayed_work finish_work;
Greg Kroah-Hartman
maintainerPATCH 158/461 batman-adv: tp_meter: avoid role confusion in tp_list
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit ff24f2ecfd94c07a2b89bac497433e3b23271cac upstream.
Session lookups in tp_list matched only on destination address (and optionally session ID), leaving role validation to the caller. If two sessions with the same other_end coexisted (one as sender, one as receiver) a lookup could silently return the wrong one, causing the caller's role to bail out early, potentially skipping necessary cleanup.
Move the role check into the lookup functions themselves so the correct entry is always returned, or none at all. Since batadv_tp_start() legitimately needs to detect any active session to a destination regardless of role, introduce a dedicated helper for that case rather than bending the existing lookup semantics.
--- net/batman-adv/tp_meter.c | 59 ++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 23 deletions(-)
net/batman-adv/tp_meter.c +36 -23
@@ -255,6 +255,7 @@ static void batadv_tp_batctl_error_notif* batadv_tp_list_find() - find a tp_vars object in the global list * @bat_priv: the bat priv with all the mesh interface information * @dst: the other endpoint MAC address to look for +* @role: role of the session * * Look for a tp_vars object matching dst as end_point and return it after * having increment the refcounter. Return NULL is not found
@@ -262,7 +263,8 @@ static void batadv_tp_batctl_error_notif* Return: matching tp_vars or NULL when no tp_vars with @dst was found */ static struct batadv_tp_vars *batadv_tp_list_find (struct batadv_priv *bat_priv, -const u8 *dst) +const u8 *dst, +enum batadv_tp_meter_role role) {struct batadv_tp_vars *pos, *tp_vars =NULL ;
@@ -271,6 +273,9 @@ static struct batadv_tp_vars *batadv_tp_if (!batadv_compare_eth (pos->other_end, dst))continue ; +if (pos->role != role) +continue ; +/* most of the time this function is invoked during the normal * process..it makes sens to pay more when the session is * finished and to speed the process up during the measurement
@@ -287,11 +292,32 @@ static struct batadv_tp_vars *batadv_tp_ }/** +* batadv_tp_list_active() - check if session from/to destination is ongoing +* @bat_priv: the bat priv with all the mesh interface information +* @dst: the other endpoint MAC address to look for +* +* Return: if matching session with @dst was found +*/ +static bool batadv_tp_list_active (struct batadv_priv *bat_priv,const u8 *dst) + __must_hold(&bat_priv->tp_list_lock) +{ +struct batadv_tp_vars *tp_vars; + +hlist_for_each_entry_rcu (tp_vars, &bat_priv->tp_list, list) { +if (batadv_compare_eth (tp_vars->other_end, dst)) +return true; + } + +return false; +} + +/** * batadv_tp_list_find_session() - find tp_vars session object in the global * list * @bat_priv: the bat priv with all the mesh interface information * @dst: the other endpoint MAC address to look for * @session: session identifier +* @role: role of the session * * Look for a tp_vars object matching dst as end_point, session as tp meter * session and return it after having increment the refcounter. Return NULL
@@ -301,7 +327,7 @@ static struct batadv_tp_vars *batadv_tp_*/ static struct batadv_tp_vars *batadv_tp_list_find_session (struct batadv_priv *bat_priv,const u8 *dst, -const u8 *session) +const u8 *session,enum batadv_tp_meter_role role) {struct batadv_tp_vars *pos, *tp_vars =NULL ;
@@ -313,6 +339,9 @@ batadv_tp_list_find_session(struct batadif (memcmp (pos->session, session,sizeof (pos->session)) !=0 )continue ; +if (pos->role != role) +continue ; +/* most of the time this function is invoked during the normal * process..it makes sense to pay more when the session is * finished and to speed the process up during the measurement
@@ -665,13 +694,10 @@ static void batadv_tp_recv_ack(struct ba/* find the tp_vars */ tp_vars =batadv_tp_list_find_session (bat_priv, icmp->orig, - icmp->session); + icmp->session,BATADV_TP_SENDER );if (unlikely (!tp_vars))return ; -if (unlikely (tp_vars->role !=BATADV_TP_SENDER )) - goto out; -if (unlikely (batadv_tp_sender_stopped (tp_vars))) goto out;
@@ -980,10 +1006,8 @@ void batadv_tp_start(struct batadv_privreturn ; } - tp_vars =batadv_tp_list_find (bat_priv, dst); -if (tp_vars) { +if (batadv_tp_list_active (bat_priv, dst)) {spin_unlock_bh (&bat_priv->tp_list_lock); -batadv_tp_vars_put (tp_vars);batadv_dbg (BATADV_DBG_TP_METER , bat_priv,"Meter: test to or from the same node already ongoing, aborting\n" ); batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING,
@@ -1104,18 +1128,14 @@ void batadv_tp_stop(struct batadv_priv *if (!orig_node)return ; - tp_vars =batadv_tp_list_find (bat_priv, orig_node->orig); + tp_vars =batadv_tp_list_find (bat_priv, orig_node->orig,BATADV_TP_SENDER );if (!tp_vars) {batadv_dbg (BATADV_DBG_TP_METER , bat_priv,"Meter: trying to interrupt an already over connection\n" ); goto out_put_orig_node; } -if (unlikely (tp_vars->role !=BATADV_TP_SENDER )) - goto out_put_tp_vars; -batadv_tp_sender_shutdown (tp_vars, return_value); -out_put_tp_vars:batadv_tp_vars_put (tp_vars); out_put_orig_node:batadv_orig_node_put (orig_node);
@@ -1371,7 +1391,7 @@ batadv_tp_init_recv(struct batadv_priv * goto out_unlock; tp_vars =batadv_tp_list_find_session (bat_priv, icmp->orig, - icmp->session); + icmp->session,BATADV_TP_RECEIVER );if (tp_vars) goto out_unlock;
@@ -1442,7 +1462,7 @@ static void batadv_tp_recv_msg(struct ba } }else { tp_vars =batadv_tp_list_find_session (bat_priv, icmp->orig, - icmp->session); + icmp->session,BATADV_TP_RECEIVER );if (!tp_vars) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Unexpected packet from %pM!\n",
@@ -1451,13 +1471,6 @@ static void batadv_tp_recv_msg(struct ba } } -if (unlikely (tp_vars->role !=BATADV_TP_RECEIVER )) { -batadv_dbg (BATADV_DBG_TP_METER , bat_priv, -"Meter: dropping packet: not expected (role=%u)\n" , - tp_vars->role); - goto out; - } - tp_vars->last_recv_time = jiffies;/* if the packet is a duplicate, it may be the case that an ACK has been
Greg Kroah-Hartman
maintainerPATCH 159/461 batman-adv: tt: fix TOCTOU race for reported vlans
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 94d27005016be15ffc638b2ecbc4d58805ad7b48 upstream.
The local TT based TVLV is generated by first checking the number of VLANs which have at least one TT entry. A new buffer with the correct size for the VLANs is then allocated. Only then, the list of VLANs s used to fill the VLAN entries in the buffer. During this time, the meshif_vlan_list_lock is held. But the actual number of TT entries of each VLAN can still increase during this time - just not the number of VLANs in the list.
But the prefilter used in the buffer size calculation might still cause an increase of the number of VLANs which need to be stored. Simply because a VLAN might now suddenly have at least one entry when it had none in the pre-alloc check - and then needs to occupy space which was not allocated.
It is better to overestimate the buffer size at the beginning and then fill the buffer only with the VLANs which are not empty.
--- net/batman-adv/translation-table.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
net/batman-adv/translation-table.c +9 -4
@@ -887,11 +887,8 @@ batadv_tt_prepare_tvlv_local_data(structspin_lock_bh (&bat_priv->meshif_vlan_list_lock);hlist_for_each_entry (vlan, &bat_priv->meshif_vlan_list, list) { vlan_entries =atomic_read (&vlan->tt.num_entries); -if (vlan_entries <1 ) -continue ; - - num_vlan++; total_entries += vlan_entries; + num_vlan++; } change_offset =struct_size (*tt_data, vlan_data, num_vlan);
@@ -914,6 +911,7 @@ batadv_tt_prepare_tvlv_local_data(struct (*tt_data)->num_vlan =htons (num_vlan); tt_vlan = (*tt_data)->vlan_data; + num_vlan =0 ;hlist_for_each_entry (vlan, &bat_priv->meshif_vlan_list, list) { vlan_entries =atomic_read (&vlan->tt.num_entries);if (vlan_entries <1 )
@@ -924,8 +922,15 @@ batadv_tt_prepare_tvlv_local_data(struct tt_vlan->reserved =0 ; tt_vlan++; + num_vlan++; } +/* recalculate in case number of VLANs reduced */ + change_offset =struct_size (*tt_data, vlan_data, num_vlan); + tvlv_len = *tt_len + change_offset; + + (*tt_data)->num_vlan =htons (num_vlan); + tt_change_ptr = (u8 *)*tt_data + change_offset; *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
Greg Kroah-Hartman
maintainerPATCH 160/461 batman-adv: tt: reject oversized local TVLV buffers
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 1e9fab756f8395096d5bba7be0c373c4c8f5d165 upstream.
The commit 3a359bf5c61d ("batman-adv: reject oversized global TT response buffers") added a check to ensure that a global return buffer size can be stored in an u16. The same buffer handling also exists for the local data buffer but was not touched.
A similar check should be also be in place for the local TVLV buffer. It doesn't have the similar attack surface because it is only generated from locally discovered MAC addresses but the dynamic nature could still cause temporarily to large buffers.
--- net/batman-adv/translation-table.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
net/batman-adv/translation-table.c +5 -3
@@ -877,12 +877,12 @@ batadv_tt_prepare_tvlv_local_data(struct {struct batadv_tvlv_tt_vlan_data *tt_vlan;struct batadv_meshif_vlan *vlan; +size_t change_offset;u16 num_vlan =0 ;u16 vlan_entries =0 ;u16 total_entries =0 ;u16 tvlv_len;u8 *tt_change_ptr; -int change_offset;spin_lock_bh (&bat_priv->meshif_vlan_list_lock);hlist_for_each_entry (vlan, &bat_priv->meshif_vlan_list, list) {
@@ -897,8 +897,10 @@ batadv_tt_prepare_tvlv_local_data(structif (*tt_len <0 ) *tt_len =batadv_tt_len (total_entries); - tvlv_len = *tt_len; - tvlv_len += change_offset; +if (check_add_overflow (*tt_len, change_offset, &tvlv_len)) { + tvlv_len =0 ; + goto out; + } *tt_data =kmalloc (tvlv_len,GFP_ATOMIC );if (!*tt_data) {
Greg Kroah-Hartman
maintainerPATCH 161/461 batman-adv: tt: avoid empty VLAN responses
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit fa1bd704940b5bcbc32c0b28db9167405c8ee5e0 upstream.
The commit 16116dac2339 ("batman-adv: prevent TT request storms by not sending inconsistent TT TLVLs") added checks to the local (direct) TT response code. But the response can also be done indirectly by another node using the global TT state. To avoid such inconsistency states reported in the original fix, also avoid sending empty VLANs for replies from the global TT state.
--- net/batman-adv/translation-table.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)
net/batman-adv/translation-table.c +17 -3
@@ -797,24 +797,26 @@ batadv_tt_prepare_tvlv_global_data(struc s32 *tt_len) {u16 num_vlan =0 ; -u16 num_entries =0 ;u16 tvlv_len =0 ;unsigned int change_offset;struct batadv_tvlv_tt_vlan_data *tt_vlan;struct batadv_orig_node_vlan *vlan; +u16 total_entries =0 ;u8 *tt_change_ptr; +int vlan_entries;spin_lock_bh (&orig_node->vlan_list_lock);hlist_for_each_entry (vlan, &orig_node->vlan_list, list) { + vlan_entries =atomic_read (&vlan->tt.num_entries); + total_entries += vlan_entries; num_vlan++; - num_entries +=atomic_read (&vlan->tt.num_entries); } change_offset =struct_size (*tt_data, vlan_data, num_vlan);/* if tt_len is negative, allocate the space needed by the full table */ if (*tt_len <0 ) - *tt_len =batadv_tt_len (num_entries); + *tt_len =batadv_tt_len (total_entries);if (change_offset >U16_MAX || *tt_len >U16_MAX - change_offset) { *tt_len =0 ;
@@ -835,14 +837,26 @@ batadv_tt_prepare_tvlv_global_data(struc (*tt_data)->num_vlan =htons (num_vlan); tt_vlan = (*tt_data)->vlan_data; + num_vlan =0 ;hlist_for_each_entry (vlan, &orig_node->vlan_list, list) { + vlan_entries =atomic_read (&vlan->tt.num_entries); +if (vlan_entries <1 ) +continue ; + tt_vlan->vid =htons (vlan->vid); tt_vlan->crc =htonl (vlan->tt.crc); tt_vlan->reserved =0 ; tt_vlan++; + num_vlan++; } +/* recalculate in case number of VLANs reduced */ + change_offset =struct_size (*tt_data, vlan_data, num_vlan); + tvlv_len = *tt_len + change_offset; + + (*tt_data)->num_vlan =htons (num_vlan); + tt_change_ptr = (u8 *)*tt_data + change_offset; *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
Greg Kroah-Hartman
maintainerPATCH 162/461 batman-adv: tt: fix negative last_changeset_len
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit fc92cdfcb295cefa4344d71a527d61b638b7bfc4 upstream.
batadv_piv_tt::last_changeset_len len was declared as s16, but the field is never intended to hold a negative value. When a value greater than 32767 is assigned, it wraps to a negative signed integer.
In batadv_send_my_tt_response(), last_changeset_len is temporarily widened to s32. The incorrectly negative s16 value propagates into the s32, causing batadv_tt_prepare_tvlv_local_data() to allocate a full sized buffer but populates only a small portion of it with the collected changeset. All remaining bits are kept uninitialized.
Using an u16 avoids this type confusion and ensures that no (negative) sign extension is performed in batadv_send_my_tt_response().
--- net/batman-adv/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/batman-adv/types.h +1 -1
@@ -996,7 +996,7 @@ struct batadv_priv_tt {* @last_changeset_len: length of last tt changeset this host has * generated */ -s16 last_changeset_len; +u16 last_changeset_len;/** * @last_changeset_lock: lock protecting last_changeset &
Greg Kroah-Hartman
maintainerPATCH 163/461 batman-adv: tt: fix negative tt_buff_len
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit b64963a2ceeb7529310b6cf253a1e540784422f4 upstream.
batadv_orig_node::tt_buff_len was declared as s16, but the field is never intended to hold a negative value. When a value greater than 32767 is assigned, it wraps to a negative signed integer.
In batadv_send_other_tt_response(), tt_buff_len is temporarily widened to s32. The incorrectly negative s16 value propagates into the s32, causing batadv_tt_prepare_tvlv_global_data() to allocate a full sized buffer but populates only a small portion of it with the collected changeset. All remaining bits are kept uninitialized.
Using an u16 avoids this type confusion and ensures that no (negative) sign extension is performed in batadv_send_other_tt_response().
--- net/batman-adv/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/batman-adv/types.h +1 -1
@@ -455,7 +455,7 @@ struct batadv_orig_node {* @tt_buff_len: length of the last tt changeset this node received * from the orig node */ -s16 tt_buff_len; +u16 tt_buff_len;/** @tt_buff_lock: lock that protects tt_buff and tt_buff_len */ spinlock_t tt_buff_lock;
Greg Kroah-Hartman
maintainerPATCH 164/461 batman-adv: tt: prevent TVLV entry number overflow
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Eckelmann <[email protected]>
commit 99d9958fa10fb684b2a8e2c48a8d704122721420 upstream.
The helpers to prepare the buffers for the local and global TT based replies are trying to sum up all TT entries which can be found for each VLAN. In theory, this sum can be too big for an u16 and therefore overflow. A too small buffer would then be allocated for the TVLV.
The too small buffer will be handled gracefully by batadv_tt_tvlv_generate() and is not causing a buffer overflow - just a truncated reply. But this overflow shouldn't have happened in the first and the too small buffer should never have been allocated when an overflow was detected.
--- net/batman-adv/translation-table.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)
net/batman-adv/translation-table.c +17 -3
@@ -804,11 +804,18 @@ batadv_tt_prepare_tvlv_global_data(strucu16 total_entries =0 ;u8 *tt_change_ptr;int vlan_entries; +u16 sum_entries;spin_lock_bh (&orig_node->vlan_list_lock);hlist_for_each_entry (vlan, &orig_node->vlan_list, list) { vlan_entries =atomic_read (&vlan->tt.num_entries); - total_entries += vlan_entries; + +if (check_add_overflow (vlan_entries, total_entries, &sum_entries)) { + *tt_len =0 ; + goto out; + } + + total_entries = sum_entries; num_vlan++; }
@@ -893,15 +900,22 @@ batadv_tt_prepare_tvlv_local_data(structstruct batadv_meshif_vlan *vlan;size_t change_offset;u16 num_vlan =0 ; -u16 vlan_entries =0 ;u16 total_entries =0 ;u16 tvlv_len;u8 *tt_change_ptr; +int vlan_entries; +u16 sum_entries;spin_lock_bh (&bat_priv->meshif_vlan_list_lock);hlist_for_each_entry (vlan, &bat_priv->meshif_vlan_list, list) { vlan_entries =atomic_read (&vlan->tt.num_entries); - total_entries += vlan_entries; + +if (check_add_overflow (vlan_entries, total_entries, &sum_entries)) { + tvlv_len =0 ; + goto out; + } + + total_entries = sum_entries; num_vlan++; }
Greg Kroah-Hartman
maintainerPATCH 165/461 hwmon: (pmbus/adm1266) seed timestamp from the real-time clock
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdurrahman Hussain <[email protected]>
commit b86095e3d7dcf2bf80c747349a35912a87a85098 upstream.
adm1266_set_rtc() seeds the chip's SET_RTC register from ktime_get_seconds(), which returns CLOCK_MONOTONIC -- i.e. seconds since the host last booted, not seconds since the Unix epoch.
The chip stamps that value into every blackbox record it captures. Userspace reading those timestamps back expects wall-clock seconds:
that's what the SET_RTC frame layout documents (datasheet Rev. D, Table 84) and what every other consumer of "seconds since epoch" assumes. Seeding from CLOCK_MONOTONIC gives blackbox records a timestamp that is only meaningful within a single boot of the host and silently resets to small values on every reboot.
Switch to ktime_get_real_seconds() so the seed matches what the register is documented to hold.
--- drivers/hwmon/pmbus/adm1266.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/pmbus/adm1266.c +1 -1
@@ -432,7 +432,7 @@ static int adm1266_set_rtc(struct adm126char write_buf[6 ];int i; - kt =ktime_get_seconds (); + kt =ktime_get_real_seconds ();memset (write_buf,0 ,sizeof (write_buf));
Greg Kroah-Hartman
maintainerPATCH 166/461 hwmon: (pmbus/adm1266) reject implausible blackbox record_count
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdurrahman Hussain <[email protected]>
commit 4afca954622d672ea65ed961bed01cf91caa034e upstream.
adm1266_nvmem_read_blackbox() loops over a record_count that comes straight from byte 3 of the BLACKBOX_INFO response. The destination buffer is data->dev_mem, sized for the nvmem cell's declared 2048 bytes (ADM1266_BLACKBOX_MAX_RECORDS * ADM1266_BLACKBOX_SIZE = 32 * 64). A device that reports a record_count greater than 32 -- whether due to firmware bugs, bus corruption, or a non-responsive slave returning 0xff -- would walk read_buff past the end of the dev_mem allocation on the trailing iterations.
Cap record_count at ADM1266_BLACKBOX_MAX_RECORDS (introduced here) before entering the loop and return -EIO on any larger value, so a malformed BLACKBOX_INFO response cannot drive the loop out of bounds.
--- drivers/hwmon/pmbus/adm1266.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/hwmon/pmbus/adm1266.c +3 -0
@@ -46,6 +46,7 @@#define ADM1266_BLACKBOX_OFFSET 0#define ADM1266_BLACKBOX_SIZE 64 +#define ADM1266_BLACKBOX_MAX_RECORDS 32#define ADM1266_PMBUS_BLOCK_MAX 255
@@ -360,6 +361,8 @@ static int adm1266_nvmem_read_blackbox(sreturn -EIO ; record_count = buf[3 ]; +if (record_count >ADM1266_BLACKBOX_MAX_RECORDS ) +return -EIO ;for (index =0 ; index < record_count; index++) { ret =adm1266_pmbus_block_xfer (data,ADM1266_READ_BLACKBOX ,1 , &index, read_buff);
Greg Kroah-Hartman
maintainerPATCH 167/461 hwmon: (pmbus/adm1266) include PEC byte in pmbus_block_xfer read buffer
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdurrahman Hussain <[email protected]>
commit 487566cb1ccdf3756fdd7bf8d875e612ff3169bb upstream.
adm1266_pmbus_block_xfer() sets up the read transaction with
.buf = data->read_buf, .len = ADM1266_PMBUS_BLOCK_MAX + 2,
but read_buf in struct adm1266_data is declared as
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1];
For a max-length block response (length byte = 255 + up to 1 PEC byte), the i2c controller is told to write 257 bytes into a 256-byte buffer, putting one byte past the end of read_buf. The same response also makes the subsequent PEC compare
if (crc != msgs[1].buf[msgs[1].buf[0] + 1])
read a byte beyond the array.
Bump the read_buf declaration to ADM1266_PMBUS_BLOCK_MAX + 2 so the buffer can hold the length byte, up to 255 payload bytes, and the PEC byte the i2c_msg length already accounts for.
--- drivers/hwmon/pmbus/adm1266.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/pmbus/adm1266.c +1 -1
@@ -61,7 +61,7 @@ struct adm1266_data {u8 *dev_mem;struct mutex buf_mutex;u8 write_buf[ADM1266_PMBUS_BLOCK_MAX +1 ]____cacheline_aligned ; -u8 read_buf[ADM1266_PMBUS_BLOCK_MAX +1 ]____cacheline_aligned ; +u8 read_buf[ADM1266_PMBUS_BLOCK_MAX +2 ]____cacheline_aligned ; };static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
Greg Kroah-Hartman
maintainerPATCH 168/461 hwmon: (pmbus/adm1266) bounce blackbox records through a protocol-sized buffer
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdurrahman Hussain <[email protected]>
commit 43cae21424ff8e33894a0f86c6b80b840c049fd7 upstream.
adm1266_pmbus_block_xfer() copies the device-supplied block payload into the caller-provided buffer using the device-supplied length:
memcpy(data_r, &msgs[1].buf[1], msgs[1].buf[0]);
The helper does not know how large data_r is and trusts the device to return at most one record's worth of bytes. adm1266_nvmem_read_blackbox() violates that contract: it advances read_buff inside data->dev_mem in ADM1266_BLACKBOX_SIZE (64-byte) strides while the helper is willing to write up to ADM1266_PMBUS_BLOCK_MAX (255) bytes. A device that returns more than 64 bytes on the trailing record (read_buff offset 1984 in the 2048-byte dev_mem allocation) overflows dev_mem by up to 191 bytes before the post-call
if (ret != ADM1266_BLACKBOX_SIZE) return -EIO;
can reject the response.
Contain the fix in the caller without changing the helper signature:
read each record into a 255-byte local bounce buffer that matches the helper's maximum output, validate the returned length, and only then copy exactly ADM1266_BLACKBOX_SIZE bytes into the dev_mem slot.
--- drivers/hwmon/pmbus/adm1266.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hwmon/pmbus/adm1266.c +3 -1
@@ -348,6 +348,7 @@ static void adm1266_init_debugfs(structstatic int adm1266_nvmem_read_blackbox (struct adm1266_data *data,u8 *read_buff) { +u8 record[ADM1266_PMBUS_BLOCK_MAX ];int record_count;char index;u8 buf[I2C_SMBUS_BLOCK_MAX ];
@@ -365,13 +366,14 @@ static int adm1266_nvmem_read_blackbox(sreturn -EIO ;for (index =0 ; index < record_count; index++) { - ret =adm1266_pmbus_block_xfer (data,ADM1266_READ_BLACKBOX ,1 , &index, read_buff); + ret =adm1266_pmbus_block_xfer (data,ADM1266_READ_BLACKBOX ,1 , &index, record);if (ret <0 )return ret;if (ret !=ADM1266_BLACKBOX_SIZE )return -EIO ; +memcpy (read_buff, record,ADM1266_BLACKBOX_SIZE ); read_buff +=ADM1266_BLACKBOX_SIZE ; }
Greg Kroah-Hartman
maintainerPATCH 169/461 hwmon: (pmbus/adm1266) cap PDIO scan in get_multiple at ADM1266_PDIO_NR
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdurrahman Hussain <[email protected]>
commit d7834d92251baade796812876e95555e2066fa9f upstream.
adm1266_gpio_get_multiple() iterates the PDIO portion of the caller-supplied mask using
for_each_set_bit_from(gpio_nr, mask,
ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) {
...
}where ADM1266_PDIO_STATUS is the PMBus command code (0xE9, i.e. 233), not the number of PDIO pins. The intended upper bound is ADM1266_GPIO_NR + ADM1266_PDIO_NR = 25.
gpiolib hands in a mask sized for gc.ngpio (= 25 bits on this chip), so the iteration walks find_next_bit() up to 242, reading up to 217 extra bits (a handful of unsigned-long words: four on 64-bit, seven on 32-bit) of whatever lives past the end of the mask in the caller's stack. Any incidental set bit in that range then drives a set_bit(gpio_nr, bits) call that writes past the end of the caller-supplied bits array too -- both out-of-bounds.
Substitute ADM1266_PDIO_NR for the constant so the scan stops at the last real PDIO bit.
--- drivers/hwmon/pmbus/adm1266.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/pmbus/adm1266.c +1 -1
@@ -212,7 +212,7 @@ static int adm1266_gpio_get_multiple(str status = read_buf[0 ] + (read_buf[1 ] <<8 ); *bits =0 ; -for_each_set_bit_from (gpio_nr, mask,ADM1266_GPIO_NR +ADM1266_PDIO_STATUS ) { +for_each_set_bit_from (gpio_nr, mask,ADM1266_GPIO_NR +ADM1266_PDIO_NR ) {if (test_bit (gpio_nr -ADM1266_GPIO_NR , &status))set_bit (gpio_nr, bits); }
Greg Kroah-Hartman
maintainerPATCH 170/461 hwmon: (pmbus/adm1266) dont clobber GPIO bits before PDIO read in get_multiple
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdurrahman Hussain <[email protected]>
commit 3327a12aee9e10ffa903e28b8445dfd1af5307c0 upstream.
adm1266_gpio_get_multiple() zeroes *bits before the GPIO_STATUS loop and then a second time before the PDIO_STATUS loop:
*bits = 0;
for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) {
...
set_bit(gpio_nr, bits);
}
ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, ...);
...
*bits = 0;
for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_NR) {
...
set_bit(gpio_nr, bits);
}The second *bits = 0 throws away every GPIO bit the first loop just populated, so callers asking for any combination of GPIO and PDIO pins always see the GPIO portion of the returned bits as zero.
Drop the redundant second assignment so both halves of the result survive.
--- drivers/hwmon/pmbus/adm1266.c | 1 - 1 file changed, 1 deletion(-)
drivers/hwmon/pmbus/adm1266.c +0 -1
@@ -211,7 +211,6 @@ static int adm1266_gpio_get_multiple(str status = read_buf[0 ] + (read_buf[1 ] <<8 ); - *bits =0 ;for_each_set_bit_from (gpio_nr, mask,ADM1266_GPIO_NR +ADM1266_PDIO_NR ) {if (test_bit (gpio_nr -ADM1266_GPIO_NR , &status))set_bit (gpio_nr, bits);
Greg Kroah-Hartman
maintainerPATCH 171/461 hwmon: (pmbus/adm1266) register the gpio_chip after pmbus_do_probe()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdurrahman Hussain <[email protected]>
commit 491403b9b76cf66abd81301c5901aa4a4549f1e8 upstream.
adm1266_probe() calls adm1266_config_gpio() -- which goes on to devm_gpiochip_add_data() and exposes the gpio_chip callbacks to gpiolib -- before pmbus_do_probe() has initialised the per-client PMBus state (notably the pmbus_lock mutex the core hands out via pmbus_get_data()).
That ordering is already a latent hazard: any GPIO access that lands between adm1266_config_gpio() and the end of pmbus_do_probe() (for example a sysfs read from a user space agent that opens the gpiochip the instant gpiolib advertises it) races pmbus_do_probe()'s own device accesses with no serialisation.
Move adm1266_config_gpio() down past pmbus_do_probe() so the chip isn't reachable from userspace until the PMBus state it depends on is fully initialised.
--- drivers/hwmon/pmbus/adm1266.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/hwmon/pmbus/adm1266.c +4 -4
@@ -466,10 +466,6 @@ static int adm1266_probe(struct i2c_cliecrc8_populate_msb (pmbus_crc_table,0x7 );mutex_init (&data->buf_mutex); - ret =adm1266_config_gpio (data); -if (ret <0 ) -return ret; - ret =adm1266_set_rtc (data);if (ret <0 )return ret;
@@ -482,6 +478,10 @@ static int adm1266_probe(struct i2c_clieif (ret)return ret; + ret =adm1266_config_gpio (data); +if (ret <0 ) +return ret; +adm1266_init_debugfs (data);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 172/461 hwmon: (pmbus/adm1266) register the nvmem device after pmbus_do_probe()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdurrahman Hussain <[email protected]>
commit 6af713af91d5c34ec049eb3cc2c5b3f5eba953b8 upstream.
adm1266_probe() calls adm1266_config_nvmem() -- which goes on to devm_nvmem_register() and exposes adm1266_nvmem_read() to userspace -- before pmbus_do_probe() has initialised the per-client PMBus state.
Same latent hazard as the gpio_chip one fixed in the previous patch:
once the nvmem device is registered, gpiolib's nvmem char-dev / sysfs interface is reachable, and any concurrent read triggers adm1266_nvmem_read() -> adm1266_nvmem_read_blackbox(), which issues PMBus traffic that races pmbus_do_probe()'s own device accesses with no serialisation.
Move adm1266_config_nvmem() down past pmbus_do_probe() so the nvmem device isn't reachable from userspace until the PMBus state the nvmem accessors depend on is fully initialised.
--- drivers/hwmon/pmbus/adm1266.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/hwmon/pmbus/adm1266.c +4 -4
@@ -470,14 +470,14 @@ static int adm1266_probe(struct i2c_clieif (ret <0 )return ret; - ret =adm1266_config_nvmem (data); -if (ret <0 ) -return ret; - ret =pmbus_do_probe (client, &data->info);if (ret)return ret; + ret =adm1266_config_nvmem (data); +if (ret <0 ) +return ret; + ret =adm1266_config_gpio (data);if (ret <0 )return ret;
Greg Kroah-Hartman
maintainerPATCH 173/461 hwmon: (pmbus/adm1266) reject short block-read responses in the GPIO accessors
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdurrahman Hussain <[email protected]>
commit a7232f68c43ca62f545049b7f5fbfc75137b843b upstream.
adm1266_gpio_get() and adm1266_gpio_get_multiple() both compose the pin-status word as
pins_status = read_buf[0] + (read_buf[1] << 8);
right after i2c_smbus_read_block_data(), guarding only against an error return. A well-behaved device returns 2 bytes for GPIO_STATUS/PDIO_STATUS, but the helper happily reports a 0- or 1-byte response too. If the device returns 0 bytes, both read_buf slots are uninitialized stack memory; if it returns 1 byte, read_buf[1] is.
The composed value then flows through set_bit() into the caller's *bits in adm1266_gpio_get_multiple(), or into the return value of adm1266_gpio_get(), and ends up in userspace via gpiolib (sysfs and the char-dev ioctls). That leaks a few bits of kernel stack per request on any device whose firmware glitch, bus error, or hostile slave produces a short block-read response.
Add the missing length check to both call sites and surface a short response as -EIO.
--- drivers/hwmon/pmbus/adm1266.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/hwmon/pmbus/adm1266.c +6 -0
@@ -176,6 +176,8 @@ static int adm1266_gpio_get(struct gpio_ ret =i2c_smbus_read_block_data (data->client, pmbus_cmd, read_buf);if (ret <0 )return ret; +if (ret <2 ) +return -EIO ; pins_status = read_buf[0 ] + (read_buf[1 ] <<8 );if (offset <ADM1266_GPIO_NR )
@@ -196,6 +198,8 @@ static int adm1266_gpio_get_multiple(str ret =i2c_smbus_read_block_data (data->client,ADM1266_GPIO_STATUS , read_buf);if (ret <0 )return ret; +if (ret <2 ) +return -EIO ; status = read_buf[0 ] + (read_buf[1 ] <<8 );
@@ -208,6 +212,8 @@ static int adm1266_gpio_get_multiple(str ret =i2c_smbus_read_block_data (data->client,ADM1266_PDIO_STATUS , read_buf);if (ret <0 )return ret; +if (ret <2 ) +return -EIO ; status = read_buf[0 ] + (read_buf[1 ] <<8 );
Greg Kroah-Hartman
maintainerPATCH 174/461 pinctrl: mediatek: moore: implement gpio_chip::get_direction()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bartosz Golaszewski <[email protected]>
[ Upstream commit b560d414239232c6ed7205d3795d3f588034d69b ]
If the gpio_chip::get_direction() callback is not implemented by the GPIO controller driver, GPIOLIB emits a warning.
Implement get_direction() for the GPIO part of pinctrl-moore.
--- drivers/pinctrl/mediatek/pinctrl-moore.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
drivers/pinctrl/mediatek/pinctrl-moore.c +18 -0
@@ -520,6 +520,23 @@ static int mtk_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio,return pinctrl_gpio_direction_output (chip, gpio); } +static int mtk_gpio_get_direction (struct gpio_chip *chip,unsigned int offset) +{ +struct mtk_pinctrl *hw =gpiochip_get_data (chip); +const struct mtk_pin_desc *desc; +int ret, dir; + + desc = (const struct mtk_pin_desc *)&hw->soc->pins[offset]; +if (!desc->name) +return -ENOTSUPP ; + + ret =mtk_hw_get_value (hw, desc,PINCTRL_PIN_REG_DIR , &dir); +if (ret) +return ret; + +return dir ?GPIO_LINE_DIRECTION_OUT :GPIO_LINE_DIRECTION_IN ; +} +static int mtk_gpio_to_irq (struct gpio_chip *chip,unsigned int offset) {struct mtk_pinctrl *hw =gpiochip_get_data (chip);
@@ -566,6 +583,7 @@ static int mtk_build_gpiochip(struct mtk_pinctrl *hw) chip->parent = hw->dev; chip->request = gpiochip_generic_request; chip->free = gpiochip_generic_free; + chip->get_direction = mtk_gpio_get_direction; chip->direction_input = pinctrl_gpio_direction_input; chip->direction_output = mtk_gpio_direction_output; chip->get = mtk_gpio_get;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 175/461 pinctrl: qcom: ipq4019: mark gpio as a GPIO pin function
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Til Kaiser <[email protected]>
[ Upstream commit b51d33ea8a164bb5f0eec8ad817fa9730ac2b577 ]
The qcom pinctrl core supports marking functions that represent GPIO mode via PINCTRL_GPIO_PINFUNCTION(), so that strict pinmuxing does not reject GPIO requests for pins that are muxed to the GPIO function.
ipq4019 still describes its gpio function with QCA_PIN_FUNCTION(gpio), so it is not treated as a GPIO pin function. As a result, GPIO consumers can still conflict with pinctrl states that select the "gpio" function.
Add a QCA_GPIO_PIN_FUNCTION() helper and use it for the ipq4019 gpio function, matching how the msm-based qcom drivers handle this.
This allows ipq4019 to keep the GPIO-related pin configuration in DTS without tripping over strict pinmux ownership checks.
--- drivers/pinctrl/qcom/pinctrl-ipq4019.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-)
drivers/pinctrl/qcom/pinctrl-ipq4019.c +1 -1
@@ -480,7 +480,7 @@ static const struct pinfunction ipq4019_functions[] = {QCA_PIN_FUNCTION (blsp_uart0),QCA_PIN_FUNCTION (blsp_uart1),QCA_PIN_FUNCTION (chip_rst), -QCA_PIN_FUNCTION (gpio), +QCA_GPIO_PIN_FUNCTION (gpio),QCA_PIN_FUNCTION (i2s_rx),QCA_PIN_FUNCTION (i2s_spdif_in), QCA_PIN_FUNCTION(i2s_spdif_out),
drivers/pinctrl/qcom/pinctrl-msm.h +5 -0
@@ -39,6 +39,11 @@ struct pinctrl_pin_desc; fname##_groups, \ ARRAY_SIZE(fname##_groups)) +#define QCA_GPIO_PIN_FUNCTION(fname) \ + [qca_mux_##fname] = PINCTRL_GPIO_PINFUNCTION(#fname, \ + fname##_groups, \ + ARRAY_SIZE(fname##_groups)) +/** * struct msm_pingroup - Qualcomm pingroup definition * @grp: Generic data of the pin group (name and pins)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 176/461 arm64: dts: renesas: r8a78000: Fix SCIF brg_int clocks
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Geert Uytterhoeven <[email protected]>
[ Upstream commit 86637727c11a105499e9faa38f3422dfcf4d211d ]
According to the documentation, the internal clock input for the BRG is SGASYNCD4_PERW_BUSφ.
--- arch/arm64/boot/dts/renesas/r8a78000.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/boot/dts/renesas/r8a78000.dtsi +4 -4
@@ -699,7 +699,7 @@ scif0: serial@c0700000 { "renesas,rcar-gen5-scif", "renesas,scif"; reg = <0 0xc0700000 0 0x40>; interrupts = <GIC_ESPI 10 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd16>, <&scif_clk>; + clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd4>, <&scif_clk>; clock-names = "fck", "brg_int", "scif_clk"; status = "disabled"; };
@@ -709,7 +709,7 @@ scif1: serial@c0704000 { "renesas,rcar-gen5-scif", "renesas,scif"; reg = <0 0xc0704000 0 0x40>; interrupts = <GIC_ESPI 11 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd16>, <&scif_clk>; + clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd4>, <&scif_clk>; clock-names = "fck", "brg_int", "scif_clk"; status = "disabled"; };
@@ -719,7 +719,7 @@ scif3: serial@c0708000 { "renesas,rcar-gen5-scif", "renesas,scif"; reg = <0 0xc0708000 0 0x40>; interrupts = <GIC_ESPI 12 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd16>, <&scif_clk>; + clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd4>, <&scif_clk>; clock-names = "fck", "brg_int", "scif_clk"; status = "disabled"; };
@@ -729,7 +729,7 @@ scif4: serial@c070c000 { "renesas,rcar-gen5-scif", "renesas,scif"; reg = <0 0xc070c000 0 0x40>; interrupts = <GIC_ESPI 13 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd16>, <&scif_clk>; + clocks = <&dummy_clk_sgasyncd16>, <&dummy_clk_sgasyncd4>, <&scif_clk>; clock-names = "fck", "brg_int", "scif_clk"; status = "disabled"; };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 177/461 ARM: dts: renesas: genmai: Drop superfluous cells
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marek Vasut <[email protected]>
[ Upstream commit 714e1d6bba0e0abe5c87c8e189a35fa690540df4 ]
Drop superfluous address-cells and size-cells to fix DTC W=1 warning:
arch/arm/boot/dts/renesas/r7s72100-genmai.dts:28.17-55.4: Warning (avoid_unnecessary_addr_size): /flash@18000000: unnecessary #address-cells/#size-cells without "ranges", "dma-ranges" or child "reg" or "ranges" property
--- arch/arm/boot/dts/renesas/r7s72100-genmai.dts | 3 --- 1 file changed, 3 deletions(-)
arch/arm/boot/dts/renesas/r7s72100-genmai.dts +0 -3
@@ -34,9 +34,6 @@ flash@18000000 { clocks = <&mstp9_clks R7S72100_CLK_SPIBSC0>; power-domains = <&cpg_clocks>; - #address-cells = <1>; - #size-cells = <1>; - partitions { compatible = "fixed-partitions"; #address-cells = <1>;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 178/461 ARM: dts: renesas: rskrza1: Drop superfluous cells
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marek Vasut <[email protected]>
[ Upstream commit ab83176d3cf1cf1c1f6e604432905bda4515d17f ]
Drop superfluous address-cells and size-cells to fix DTC W=1 warning:
arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts:32.17-72.4: Warning (avoid_unnecessary_addr_size): /flash@18000000: unnecessary #address-cells/#size-cells without "ranges", "dma-ranges" or child "reg" or "ranges" property
--- arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts | 2 -- 1 file changed, 2 deletions(-)
arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts +0 -2
@@ -36,8 +36,6 @@ flash@18000000 { power-domains = <&cpg_clocks>; bank-width = <4>; device-width = <1>; - #address-cells = <1>; - #size-cells = <1>; partitions { compatible = "fixed-partitions";
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 179/461 pinctrl: renesas: rzg2l: Fix incorrect PUPD register offset for high pins during suspend/resume
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Biju Das <[email protected]>
[ Upstream commit 6dba9b7268cc50166bce47608670192fd874e363 ]
When saving/restoring pull-up/down register state during suspend/resume, the second PUPD register access was incorrectly using the same base offset as the first, effectively reading/writing the same register twice instead of the adjacent one.
Add the correct + 4 byte offset to the second RZG2L_PCTRL_REG_ACCESS32 call so that pupd[1][port] is properly saved and restored from the next 32-bit register in the PUPD register pair, covering pins 4–7 of ports with 4 or more pins.
--- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pinctrl/renesas/pinctrl-rzg2l.c +1 -1
@@ -3050,7 +3050,7 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspenRZG2L_PCTRL_REG_ACCESS32 (suspend, pctrl->base +PUPD (off), cache->pupd[0 ][port]);if (pincnt >=4 ) { -RZG2L_PCTRL_REG_ACCESS32 (suspend, pctrl->base +PUPD (off), +RZG2L_PCTRL_REG_ACCESS32 (suspend, pctrl->base +PUPD (off) +4 , cache->pupd[1 ][port]); } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 180/461 pinctrl: renesas: rzg2l: Fix SMT register cache handling
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lad Prabhakar <[email protected]>
[ Upstream commit c88ab9407986836820848128ce1f90f2fa49da95 ]
Store SMT register cache per bank instead of using a single array.
On RZ/V2H(P), RZ/V2N, and RZ/G3E, the SMT register is split across two 32-bit registers: bits 0/8/16/24 control pins 0-3, while pins 4-7 are controlled by the corresponding bits in the next register. The previous implementation cached only a single SMT register, leading to incomplete save/restore of SMT state.
Convert cache->smt to a per-bank array and allocate storage for both halves. Update suspend/resume handling to save and restore both SMT registers when present.
--- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-)
drivers/pinctrl/renesas/pinctrl-rzg2l.c +14 -7
@@ -335,7 +335,7 @@ struct rzg2l_pinctrl_reg_cache {u32 *iolh[2 ];u32 *ien[2 ];u32 *pupd[2 ]; -u32 *smt; +u32 *smt[2 ];u8 sd_ch[2 ];u8 eth_poc[2 ];u8 oen;
@@ -2738,10 +2738,6 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl)if (!cache->pfc)return -ENOMEM ; - cache->smt =devm_kcalloc (pctrl->dev, nports,sizeof (*cache->smt),GFP_KERNEL ); -if (!cache->smt) -return -ENOMEM ; -for (u8 i =0 ; i <2 ; i++) {u32 n_dedicated_pins = pctrl->data->n_dedicated_pins;
@@ -2760,6 +2756,11 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl)if (!cache->pupd[i])return -ENOMEM ; + cache->smt[i] =devm_kcalloc (pctrl->dev, nports,sizeof (*cache->smt[i]), +GFP_KERNEL ); +if (!cache->smt[i]) +return -ENOMEM ; +/* Allocate dedicated cache. */ dedicated_cache->iolh[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, sizeof(*dedicated_cache->iolh[i]),
@@ -3067,8 +3068,14 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen } } -if (has_smt) -RZG2L_PCTRL_REG_ACCESS32 (suspend, pctrl->base +SMT (off), cache->smt[port]); +if (has_smt) { +RZG2L_PCTRL_REG_ACCESS32 (suspend, pctrl->base +SMT (off), + cache->smt[0 ][port]); +if (pincnt >=4 ) { +RZG2L_PCTRL_REG_ACCESS32 (suspend, pctrl->base +SMT (off) +4 , + cache->smt[1 ][port]); + } + } } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 181/461 pinctrl: meson: amlogic-a4: fix deadlock issue
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xianwei Zhao <[email protected]>
[ Upstream commit e72ce029810390eb987a036fb2c8a5da9a23b685 ]
Accessing the pinconf-pins sysfs node may deadlock.
pinconf_pins_show() holds pctldev->mutex, and the platform driver calls pinctrl_find_gpio_range_from_pin(), which tries to acquire the same mutex again, leading to a deadlock.
Use pinctrl_find_gpio_range_from_pin_nolock() to fix this issue.
--- drivers/pinctrl/meson/pinctrl-amlogic-a4.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/pinctrl/meson/pinctrl-amlogic-a4.c +3 -3
@@ -292,7 +292,7 @@ static int aml_calc_reg_and_bit(struct pinctrl_gpio_range *range,static int aml_pinconf_get_pull (struct aml_pinctrl *info,unsigned int pin) {struct pinctrl_gpio_range *range = -pinctrl_find_gpio_range_from_pin (info->pctl, pin); +pinctrl_find_gpio_range_from_pin_nolock (info->pctl, pin);struct aml_gpio_bank *bank =gpio_chip_to_bank (range->gc);unsigned int reg, bit, val;int ret, conf;
@@ -326,7 +326,7 @@ static int aml_pinconf_get_drive_strength(struct aml_pinctrl *info, u16 *drive_strength_ua) {struct pinctrl_gpio_range *range = -pinctrl_find_gpio_range_from_pin (info->pctl, pin); +pinctrl_find_gpio_range_from_pin_nolock (info->pctl, pin);struct aml_gpio_bank *bank =gpio_chip_to_bank (range->gc);unsigned int reg, bit;unsigned int val;
@@ -365,7 +365,7 @@ static int aml_pinconf_get_gpio_bit(struct aml_pinctrl *info, unsigned int reg_type) {struct pinctrl_gpio_range *range = -pinctrl_find_gpio_range_from_pin (info->pctl, pin); +pinctrl_find_gpio_range_from_pin_nolock (info->pctl, pin);struct aml_gpio_bank *bank =gpio_chip_to_bank (range->gc);unsigned int reg, bit, val;int ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 182/461 pinctrl: qcom: Fix GPIO to PDC wake irq map for qcs615
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maulik Shah <[email protected]>
[ Upstream commit 9d69033ad967b6e09b1e5b30d1a32c6c4876465d ]
PDC interrupts 122-125 were meant for ibi_i3c wakeup but qcs615 do not support i3c. GPIOs 39,51,88 and 89 are also connected to different PDC pin to support non-ibi wakeup. Update the wakeirq map to reflect same.
--- drivers/pinctrl/qcom/pinctrl-qcs615.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/pinctrl/qcom/pinctrl-qcs615.c +3 -3
@@ -1043,11 +1043,11 @@ static const struct msm_pingroup qcs615_groups[] = {static const struct msm_gpio_wakeirq_map qcs615_pdc_map[] = { {1 ,45 }, {3 ,31 }, {7 ,55 }, {9 ,110 }, {11 ,34 }, {13 ,33 }, {14 ,35 }, {17 ,46 }, {19 ,48 }, {21 ,83 }, - {22 ,36 }, {26 ,38 }, {35 ,37 }, {39 ,125 }, {41 ,47 }, - {47 ,49 }, {48 ,51 }, {50 ,52 }, {51 ,123 }, {55 ,56 }, + {22 ,36 }, {26 ,38 }, {35 ,37 }, {39 ,118 }, {41 ,47 }, + {47 ,49 }, {48 ,51 }, {50 ,52 }, {51 ,116 }, {55 ,56 }, {56 ,57 }, {57 ,58 }, {60 ,60 }, {71 ,54 }, {80 ,73 }, {81 ,64 }, {82 ,50 }, {83 ,65 }, {84 ,92 }, {85 ,99 }, - {86 ,67 }, {87 ,84 }, {88 ,124 }, {89 ,122 }, {90 ,69 }, + {86 ,67 }, {87 ,84 }, {88 ,117 }, {89 ,115 }, {90 ,69 }, {92 ,88 }, {93 ,75 }, {94 ,91 }, {95 ,72 }, {96 ,82 }, {97 ,74 }, {98 ,95 }, {99 ,94 }, {100 ,100 }, {101 ,40 }, {102 ,93 }, {103 ,77 }, {104 ,78 }, {105 ,96 }, {107 ,97 },
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 183/461 kho: skip KHO for crash kernel
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Evangelos Petrongonas <[email protected]>
[ Upstream commit a6715d7ec472a476db17787697a4abda62962284 ]
kho_fill_kimage() unconditionally populates the kimage with KHO metadata for every kexec image type. When the image is a crash kernel, this can be problematic as the crash kernel can run in a small reserved region and the KHO scratch areas can sit outside it.
The crash kernel then faults during kho_memory_init() when it tries phys_to_virt() on the KHO FDT address:
Unable to handle kernel paging request at virtual address xxxxxxxx
...
fdt_offset_ptr+...
fdt_check_node_offset_+...
fdt_first_property_offset+...
fdt_get_property_namelen_+...
fdt_getprop+...
kho_memory_init+...
mm_core_init+...
start_kernel+...kho_locate_mem_hole() already skips KHO logic for KEXEC_TYPE_CRASH images, but kho_fill_kimage() was missing the same guard. As kho_fill_kimage() is the single point that populates image->kho.fdt and image->kho.scratch, fixing it here is sufficient for both arm64 and x86 as the FDT and boot_params path are bailing out when these fields are unset.
--- kernel/liveupdate/kexec_handover.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/liveupdate/kexec_handover.c +1 -1
@@ -1556,7 +1556,7 @@ int kho_fill_kimage(struct kimage *image)int err =0 ;struct kexec_buf scratch; -if (!kho_enable) +if (!kho_enable || image->type ==KEXEC_TYPE_CRASH )return 0 ; image->kho.fdt =virt_to_phys (kho_out.fdt);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 184/461 mm/memfd_luo: report error when restoring a folio fails mid-loop
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <[email protected]>
[ Upstream commit 0fb1daf0b78d0e23b63b6b65de56d4a3fd83bc14 ]
memfd_luo_retrieve_folios() initialises err to -EIO, but the per-iteration calls to mem_cgroup_charge(), shmem_add_to_page_cache() and shmem_inode_acct_blocks() reuse and overwrite err. Once any iteration completes successfully, err becomes zero.
If a later iteration's kho_restore_folio() returns NULL, the failure path jumps to put_folios without resetting err, so the function returns 0. The caller memfd_luo_retrieve() then takes the success path, sets args->file and reports the restore as successful, leaving userspace with a partially populated memfd and no indication that anything went wrong.
Set err to -EIO in the kho_restore_folio() failure branch so the error is propagated to the caller.
--- mm/memfd_luo.c | 1 + 1 file changed, 1 insertion(+)
mm/memfd_luo.c +1 -0
@@ -412,6 +412,7 @@ static int memfd_luo_retrieve_folios(struct file *file,if (!folio) {pr_err ("Unable to restore folio at physical address: %llx\n" , phys); + err = -EIO ; goto put_folios; } index = pfolio->index;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 185/461 HID: intel-thc-hid: Intel-quickspi: Fix some error codes
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dan Carpenter <[email protected]>
[ Upstream commit ae4ac077332ea3341a0f4c0973556c6b7ac5b7a1 ]
If we have a partial read that is supposed to be treated as failure but in this code we forgot to set the error code. Return -EINVAL.
--- drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +2 -2
@@ -94,7 +94,7 @@ static int quickspi_get_device_descriptor(struct quickspi_device *qsdev)dev_err_once (qsdev->dev,"Read DEVICE_DESCRIPTOR failed, ret = %d\n" , ret);dev_err_once (qsdev->dev,"DEVICE_DESCRIPTOR expected len = %u, actual read = %u\n" , input_len, read_len); -return ret; +return ret ?: -EINVAL ; } input_rep_type = ((struct input_report_body_header *)read_buf)->input_report_type;
@@ -318,7 +318,7 @@ int reset_tic(struct quickspi_device *qsdev)dev_err_once (qsdev->dev,"Read RESET_RESPONSE body failed, ret = %d\n" , ret);dev_err_once (qsdev->dev,"RESET_RESPONSE body expected len = %u, actual = %u\n" , read_len, actual_read_len); -return ret; +return ret ?: -EINVAL ; } input_rep_type =FIELD_GET (HIDSPI_IN_REP_BDY_HDR_REP_TYPE , reset_response);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 186/461 HID: uclogic: Fix regression of input name assignment
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit 487359284509a6745e14b8c0518768bc277809b0 ]
The previous fix for adding the devm_kasprintf() return check in the commit bd07f751208b ("HID: uclogic: Add NULL check in uclogic_input_configured()") changed the condition of hi->input->name assignment, and it resulted in missing the proper input device name when no custom suffix is defined.
Restore the conditional to the original content to address the regression.
--- drivers/hid/hid-uclogic-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hid/hid-uclogic-core.c +3 -1
@@ -184,7 +184,9 @@ static int uclogic_input_configured(struct hid_device *hdev, suffix ="System Control" ;break ; } - }else { + } + +if (suffix) { hi->input->name =devm_kasprintf (&hdev->dev,GFP_KERNEL ,"%s %s" , hdev->name, suffix);if (!hi->input->name)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 187/461 firmware: arm_ffa: Check for NULL FF-A ID table while driver registration
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit 0a5e695095c557d2380131b613dea4e8d90371be ]
The bus match callback assumes that every FF-A driver provides an id_table and dereferences it unconditionally. Enforce that contract at registration time so a buggy client driver cannot crash the bus during match.
--- drivers/firmware/arm_ffa/bus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/firmware/arm_ffa/bus.c +3 -1
@@ -26,6 +26,8 @@ static int ffa_device_match(struct device *dev, const struct device_driver *drv) id_table =to_ffa_driver (drv)->id_table; ffa_dev =to_ffa_dev (dev); +if (!id_table) +return 0 ;while (!uuid_is_null (&id_table->uuid)) {/*
@@ -123,7 +125,7 @@ int ffa_driver_register(struct ffa_driver *driver, struct module *owner, {int ret; -if (!driver->probe) +if (!driver->probe || !driver->id_table)return -EINVAL ; driver->driver.bus = &ffa_bus_type;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 188/461 firmware: arm_ffa: Skip free_pages on RX buffer alloc failure
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit 09527e2c534911619d7e098729711100290bc3e1 ]
If the RX buffer allocation fails in ffa_init(), the error path jumps to free_pages even though no buffer has been allocated yet. Route that case directly to free_drv_info so the cleanup path is only used after at least one RX/TX buffer allocation has succeeded.
--- drivers/firmware/arm_ffa/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/firmware/arm_ffa/driver.c +1 -1
@@ -2067,7 +2067,7 @@ static int __init ffa_init(void) drv_info->rx_buffer =alloc_pages_exact (rxtx_bufsz,GFP_KERNEL );if (!drv_info->rx_buffer) { ret = -ENOMEM ; - goto free_pages; + goto free_drv_info; } drv_info->tx_buffer =alloc_pages_exact (rxtx_bufsz,GFP_KERNEL );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 189/461 firmware: arm_ffa: Fix per-vcpu self notifications handling in workqueue
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit 9985d5357ed93af0d1933969c247e966957730e1 ]
Per-vcpu notification handling already runs from a per-cpu work item on the target cpu. Routing that path back through smp_call_function_single() re-enters the call-function IPI path and executes the notification handler with interrupts disabled. That makes the framework path unsafe, since it takes a mutex, allocates memory with GFP_KERNEL, and invokes client callbacks.
Handle per-vcpu self notifications directly from the existing per-cpu work item instead. This keeps the per-vcpu path in task context and avoids the extra IPI hop entirely.
--- drivers/firmware/arm_ffa/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/firmware/arm_ffa/driver.c +1 -1
@@ -1542,7 +1542,7 @@ static void notif_pcpu_irq_work_fn(struct work_struct *work) struct ffa_drv_info *info = container_of(work, struct ffa_drv_info, notif_pcpu_work); -ffa_self_notif_handle (smp_processor_id (), true, info); +notif_get_and_handle (info); }static const struct ffa_info_ops ffa_drv_info_ops = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 190/461 firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit 6d3daa9b8d313f42d52e75590310f26a29b61b44 ]
For FF-A v1.0 the driver registers a bus notifier to backfill UUID matching, but the notifier was never unregistered on cleanup paths. Track the registration state and unregister it during teardown and early partition-setup failure.
--- drivers/firmware/arm_ffa/driver.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
drivers/firmware/arm_ffa/driver.c +15 -0
@@ -100,6 +100,7 @@ struct ffa_drv_info {bool mem_ops_native;bool msg_direct_req2_supp;bool bitmap_created; +bool bus_notifier_registered;bool notif_enabled;unsigned int sched_recv_irq;unsigned int notif_pend_irq;
@@ -1629,6 +1630,15 @@ static struct notifier_block ffa_bus_nb = { .notifier_call = ffa_bus_notifier, }; +static void ffa_bus_notifier_unregister (void ) +{ +if (!drv_info->bus_notifier_registered) +return ; + +bus_unregister_notifier (&ffa_bus_type, &ffa_bus_nb); + drv_info->bus_notifier_registered = false; +} +static int ffa_xa_add_partition_info (struct ffa_device *dev) {struct ffa_dev_part_info *info;
@@ -1712,6 +1722,8 @@ static void ffa_partitions_cleanup(void)struct list_head *phead;unsigned long idx; +ffa_bus_notifier_unregister (); +/* Clean up/free all registered devices */ ffa_devices_unregister ();
@@ -1739,11 +1751,14 @@ static int ffa_setup_partitions(void) ret =bus_register_notifier (&ffa_bus_type, &ffa_bus_nb);if (ret)pr_err ("Failed to register FF-A bus notifiers\n" ); +else + drv_info->bus_notifier_registered = true; } count =ffa_partition_probe (&uuid_null, &pbuf);if (count <=0 ) {pr_info ("%s: No partitions found, error %d\n" , __func__, count); +ffa_bus_notifier_unregister ();return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 191/461 riscv: errata: Fix bitwise vs logical AND in MIPS errata patching
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Neuling <[email protected]>
[ Upstream commit 4d2b03699460b8fd5df34408a03a84a1a7ff8aa1 ]
The condition checking whether a specific errata needs patching uses logical AND (&&) instead of bitwise AND (&). Since logical AND only checks that both operands are non-zero, this causes all errata patches to be applied whenever any single errata is detected, rather than only applying the matching one.
The SiFive errata implementation correctly uses bitwise AND for the same check.
--- arch/riscv/errata/mips/errata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/riscv/errata/mips/errata.c +1 -1
@@ -57,7 +57,7 @@ void mips_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, } tmp = (1U << alt->patch_id); -if (cpu_req_errata && tmp) { +if (cpu_req_errata & tmp) {mutex_lock (&text_mutex);patch_text_nosync (ALT_OLD_PTR (alt),ALT_ALT_PTR (alt), alt->alt_len);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 192/461 riscv: Fix register corruption from uninitialized cregs on error
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Neuling <[email protected]>
[ Upstream commit 6ebcbb53fc9bc30843054ed99fd60b8e542628f4 ]
compat_riscv_gpr_set() calls cregs_to_regs() unconditionally, even when user_regset_copyin() fails. Since cregs is an uninitialized stack variable, a copyin failure causes uninitialized stack data to be written into the target task's pt_regs, corrupting its register state and potentially leaking kernel stack contents.
compat_restore_sigcontext() has the same issue: it calls cregs_to_regs() even when __copy_from_user() fails, leading to the same corruption of the signal-returning task's register state on error.
Only call cregs_to_regs() when the user copy succeeds.
--- arch/riscv/kernel/compat_signal.c | 2 ++ arch/riscv/kernel/ptrace.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-)
arch/riscv/kernel/compat_signal.c +2 -0
@@ -107,6 +107,8 @@ static long compat_restore_sigcontext(struct pt_regs *regs,/* sc_regs is structured the same as the start of pt_regs */ err =__copy_from_user (&cregs, &sc->sc_regs,sizeof (sc->sc_regs)); +if (unlikely (err)) +return err;cregs_to_regs (&cregs, regs);
arch/riscv/kernel/ptrace.c +2 -2
@@ -577,8 +577,8 @@ static int compat_riscv_gpr_set(struct task_struct *target,struct compat_user_regs_struct cregs; ret =user_regset_copyin (&pos, &count, &kbuf, &ubuf, &cregs,0 ,-1 ); - -cregs_to_regs (&cregs,task_pt_regs (target)); +if (!ret) +cregs_to_regs (&cregs,task_pt_regs (target));return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 193/461 riscv: mm: Fixup no5lvl failure when vaddr is invalid
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guo Ren (Alibaba DAMO Academy) <[email protected]>
[ Upstream commit db909bd7986c10da074917af3dae83a60fa65093 ]
Unlike no4lvl, no5lvl still continues to detect satp, which requires va=pa mapping. When pa=0x800000000000, no5lvl would fail in Sv48 mode due to an illegal VA value of 0x800000000000.
So, prevent detecting the satp flow for no5lvl, when vaddr is invalid. Add the is_vaddr_valid() function for checking.
--- arch/riscv/mm/init.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
arch/riscv/mm/init.c +25 -0
@@ -846,6 +846,27 @@ static void __init set_mmap_rnd_bits_max(void) mmap_rnd_bits_max =MMAP_VA_BITS -PAGE_SHIFT -3 ; } +static bool __init is_vaddr_valid (unsigned long va) +{ +unsigned long up =0 ; + +switch (satp_mode) { +case SATP_MODE_39 : + up =1UL <<38 ; +break ; +case SATP_MODE_48 : + up =1UL <<47 ; +break ; +case SATP_MODE_57 : + up =1UL <<56 ; +break ; +default : +return false; + } + +return (va < up) || (va >= (ULONG_MAX - up +1 )); +} +/* * There is a simple way to determine if 4-level is supported by the * underlying hardware: establish 1:1 mapping in 4-level page table mode
@@ -887,6 +908,9 @@ static __init void set_satp_mode(uintptr_t dtb_pa) set_satp_mode_pmd +PMD_SIZE , PMD_SIZE, PAGE_KERNEL_EXEC); retry: +if (!is_vaddr_valid (set_satp_mode_pmd)) + goto out; + create_pgd_mapping(early_pg_dir, set_satp_mode_pmd, pgtable_l5_enabled ?
@@ -909,6 +933,7 @@ static __init void set_satp_mode(uintptr_t dtb_pa)disable_pgtable_l4 (); } +out:memset (early_pg_dir,0 ,PAGE_SIZE );memset (early_p4d,0 ,PAGE_SIZE );memset (early_pud,0 ,PAGE_SIZE );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 194/461 kunit: config: Enable KUNIT_DEBUGFS by default
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Gow <[email protected]>
[ Upstream commit 17e4c68ff35090d8cb743e3c82c09f92fda1ebda ]
The KUNIT_DEBUGFS option is currently enabled based on the value of KUNIT_ALL_TESTS, but it really doesn't have anything to do with the set of enabled tests, so just enable it by default anyway. In particular, this shouldn't be only visible if KUNIT_ALL_TESTS is set, which is quite confusing.
--- lib/kunit/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
lib/kunit/Kconfig +2 -2
@@ -16,8 +16,8 @@ menuconfig KUNIT if KUNIT config KUNIT_DEBUGFS - bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS - default KUNIT_ALL_TESTS + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" + default y help Enable debugfs representation for kunit. Currently this consists of /sys/kernel/debug/kunit/<test_suite>/results files for each
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 195/461 kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Gow <[email protected]>
[ Upstream commit 8f80b5b227ef9ea422080487715c841856339aed ]
CONFIG_KUNIT_DEBUGFS is totally useless without debugfs, so it should depend on CONFIG_DEBUG_FS.
--- lib/kunit/Kconfig | 1 + 1 file changed, 1 insertion(+)
lib/kunit/Kconfig +1 -0
@@ -17,6 +17,7 @@ if KUNIT config KUNIT_DEBUGFS bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" + depends on DEBUG_FS default y help Enable debugfs representation for kunit. Currently this consists
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 196/461 pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for sm8150
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maulik Shah <[email protected]>
[ Upstream commit 52ac35b8a151446481496404af3a8e5e889b3c5a ]
PDC interrupts 122-125 were meant for ibi_i3c wakeup but sm8150 do not support i3c. GPIOs 39,51,88 and 144 are also connected to different PDC pin and already reflected in the wake irq map.
Remove the unsupported wakeup interrupts from the map.
--- drivers/pinctrl/qcom/pinctrl-sm8150.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/pinctrl/qcom/pinctrl-sm8150.c +4 -4
@@ -1496,18 +1496,18 @@ static const struct msm_gpio_wakeirq_map sm8150_pdc_map[] = { { 3, 31 }, { 5, 32 }, { 8, 33 }, { 9, 34 }, { 10, 100 }, { 12, 104 }, { 24, 37 }, { 26, 38 }, { 27, 41 }, { 28, 42 }, { 30, 39 }, { 36, 43 }, { 37, 44 }, { 38, 30 }, { 39, 118 }, - { 39, 125 }, { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, - { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, { 51, 123 }, + { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, + { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, { 53, 54 }, { 54, 55 }, { 55, 56 }, { 56, 57 }, { 58, 58 }, { 60, 60 }, { 61, 61 }, { 68, 62 }, { 70, 63 }, { 76, 71 }, { 77, 66 }, { 81, 64 }, { 83, 65 }, { 86, 67 }, { 87, 84 }, - { 88, 117 }, { 88, 124 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, + { 88, 117 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, { 95, 72 }, { 96, 73 }, { 97, 74 }, { 101, 40 }, { 103, 77 }, { 104, 78 }, { 108, 79 }, { 112, 80 }, { 113, 81 }, { 114, 82 }, { 117, 85 }, { 118, 101 }, { 119, 87 }, { 120, 88 }, { 121, 89 }, { 122, 90 }, { 123, 91 }, { 124, 92 }, { 125, 93 }, { 129, 94 }, { 132, 105 }, { 133, 83 }, { 134, 36 }, { 136, 97 }, { 142, 103 }, - { 144, 115 }, { 144, 122 }, { 147, 102 }, { 150, 107 }, + { 144, 115 }, { 147, 102 }, { 150, 107 }, { 152, 108 }, { 153, 109 } };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 197/461 firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit 3974ea1938406f9bfa7c1f48d4e43533f447bb08 ]
The register-based PARTITION_INFO_GET path trusted the firmware-provided indices when copying partition descriptors into the caller buffer. Reject inconsistent counts or index progressions so the copy loop cannot write past the allocated array.
(fixed cur_idx when exactly one descriptor in the first fragment)
--- drivers/firmware/arm_ffa/driver.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-)
drivers/firmware/arm_ffa/driver.c +23 -6
@@ -323,6 +323,12 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,#define PART_INFO_ID_MASK GENMASK(15, 0)#define PART_INFO_EXEC_CXT_MASK GENMASK(31, 16)#define PART_INFO_PROPS_MASK GENMASK(63, 32) +#define FFA_PART_INFO_GET_REGS_FIRST_REG 3 +#define FFA_PART_INFO_GET_REGS_REGS_PER_DESC 3 +#define FFA_PART_INFO_GET_REGS_MAX_DESC \ + (((sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) - \ + FFA_PART_INFO_GET_REGS_FIRST_REG) / \ + FFA_PART_INFO_GET_REGS_REGS_PER_DESC)#define PART_INFO_ID (x) ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x))))#define PART_INFO_EXEC_CXT (x) ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x))))#define PART_INFO_PROPERTIES (x) ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x))))
@@ -330,15 +336,13 @@ static int __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, struct ffa_partition_info *buffer, int num_parts) { - u16 buf_sz, start_idx, cur_idx, count = 0, prev_idx = 0, tag = 0; + u16 buf_sz, start_idx = 0, cur_idx, count = 0, tag = 0;struct ffa_partition_info *buf = buffer;ffa_value_t partition_info; do { __le64 *regs; - int idx; - - start_idx = prev_idx ? prev_idx + 1 : 0; + int idx, nr_desc, buf_idx; invoke_ffa_fn((ffa_value_t){ .a0 = FFA_PARTITION_INFO_GET_REGS,
@@ -354,15 +358,28 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, count =PARTITION_COUNT (partition_info.a2);if (!buffer || !num_parts)/* count only */ return count; +if (count > num_parts) +return -EINVAL ; cur_idx =CURRENT_INDEX (partition_info.a2); +if (cur_idx < start_idx || cur_idx >= count) +return -EINVAL ; + + nr_desc = cur_idx - start_idx +1 ; +if (nr_desc >FFA_PART_INFO_GET_REGS_MAX_DESC ) +return -EINVAL ; + + buf_idx = buf - buffer; +if (buf_idx + nr_desc > num_parts) +return -EINVAL ; + tag =UUID_INFO_TAG (partition_info.a2); buf_sz =PARTITION_INFO_SZ (partition_info.a2);if (buf_sz >sizeof (*buffer)) buf_sz =sizeof (*buffer); regs = (void *)&partition_info.a3; -for (idx =0 ; idx < cur_idx - start_idx +1 ; idx++, buf++) { +for (idx =0 ; idx < nr_desc; idx++, buf++) {union {uuid_t uuid;u64 regs[2 ];
@@ -380,7 +397,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,uuid_copy (&buf->uuid, &uuid_regs.uuid); regs +=3 ; } - prev_idx = cur_idx; + start_idx = cur_idx +1 ; }while (cur_idx < (count -1 ));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 198/461 firmware: arm_ffa: Keep framework RX release under lock
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit 2af18f8e36b277730527cacc2256b1332f56aa28 ]
The framework notification handler drops rx_lock before issuing FFA_RX_RELEASE, leaving a window where another RX-buffer user can start a new FF-A transaction before ownership has actually been returned to firmware.
Move the FFA_RX_RELEASE calls so they execute while rx_lock is still held on both the kmemdup() failure path and the normal success path. While doing that, switch the handler to scoped_guard() to keep the critical section explicit.
--- drivers/firmware/arm_ffa/driver.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-)
drivers/firmware/arm_ffa/driver.c +13 -16
@@ -1492,25 +1492,22 @@ static void handle_fwk_notif_callbacks(u32 bitmap)if (!(bitmap &FRAMEWORK_NOTIFY_RX_BUFFER_FULL ))return ; -mutex_lock (&drv_info->rx_lock); + scoped_guard(mutex, &drv_info->rx_lock) { + msg = drv_info->rx_buffer; + buf =kmemdup ((void *)msg + msg->offset, msg->size,GFP_KERNEL ); +if (!buf) { +ffa_rx_release (); +return ; + } - msg = drv_info->rx_buffer; - buf =kmemdup ((void *)msg + msg->offset, msg->size,GFP_KERNEL ); -if (!buf) { -mutex_unlock (&drv_info->rx_lock); -return ; + target =SENDER_ID (msg->send_recv_id); +if (msg->offset >=sizeof (*msg)) +uuid_copy (&uuid, &msg->uuid); +else +uuid_copy (&uuid, &uuid_null); +ffa_rx_release (); } - target =SENDER_ID (msg->send_recv_id); -if (msg->offset >=sizeof (*msg)) -uuid_copy (&uuid, &msg->uuid); -else -uuid_copy (&uuid, &uuid_null); - -mutex_unlock (&drv_info->rx_lock); - -ffa_rx_release (); -read_lock (&drv_info->notify_lock); cb_info =notifier_hnode_get_by_vmid_uuid (notify_id, target, &uuid);read_unlock (&drv_info->notify_lock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 199/461 firmware: arm_ffa: Validate framework notification message layout
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit 4a1cc9e96b311d2609a6f963a5e35bd4ae730d97 ]
Framework notifications carry an indirect message in the shared RX buffer. Validate the reported offset and size before using them, reject zero-length payloads, and ensure that any non-header payload starts at the UUID field rather than in the middle of the message header.
Use the validated offset and size values for both kmemdup() and the UUID parsing path so malformed firmware data cannot drive an out-of-bounds read or an oversized allocation.
--- drivers/firmware/arm_ffa/driver.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
drivers/firmware/arm_ffa/driver.c +16 -2
@@ -1487,21 +1487,35 @@ static void handle_fwk_notif_callbacks(u32 bitmap)int notify_id =0 , target;struct ffa_indirect_msg_hdr *msg;struct notifier_cb_info *cb_info =NULL ; +size_t min_offset = offsetof(struct ffa_indirect_msg_hdr , uuid);/* Only one framework notification defined and supported for now */ if (!(bitmap &FRAMEWORK_NOTIFY_RX_BUFFER_FULL ))return ; scoped_guard(mutex, &drv_info->rx_lock) { +u32 offset, size; + msg = drv_info->rx_buffer; - buf =kmemdup ((void *)msg + msg->offset, msg->size,GFP_KERNEL ); + offset = msg->offset; + size = msg->size; + +if (!size || (offset != min_offset && offset <sizeof (*msg)) || + offset > drv_info->rxtx_bufsz || + size > drv_info->rxtx_bufsz - offset) { +pr_err ("invalid framework notification message\n" ); +ffa_rx_release (); +return ; + } + + buf =kmemdup ((void *)msg + offset, size,GFP_KERNEL );if (!buf) {ffa_rx_release ();return ; } target =SENDER_ID (msg->send_recv_id); -if (msg->offset >=sizeof (*msg)) +if (offset >=sizeof (*msg))uuid_copy (&uuid, &msg->uuid);else uuid_copy (&uuid, &uuid_null);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 200/461 firmware: arm_ffa: Align RxTx buffer size before mapping
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit 0399e3f872ca3d78044bb715a73ea645806d2c7b ]
Commit 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during RXTX_MAP") advertises PAGE_ALIGN(rxtx_bufsz) to firmware when mapping the buffers but the driver continues to stores the minimum FF-A buffer size in drv_info->rxtx_bufsz which is used elsewhere in the driver.
Align the size before storing it so that the allocation, validation and FFA_RXTX_MAP all use the same buffer size.
--- drivers/firmware/arm_ffa/driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/firmware/arm_ffa/driver.c +2 -1
@@ -2106,6 +2106,7 @@ static int __init ffa_init(void) rxtx_bufsz =SZ_4K ; } + rxtx_bufsz =PAGE_ALIGN (rxtx_bufsz); drv_info->rxtx_bufsz = rxtx_bufsz; drv_info->rx_buffer =alloc_pages_exact (rxtx_bufsz,GFP_KERNEL );if (!drv_info->rx_buffer) {
@@ -2121,7 +2122,7 @@ static int __init ffa_init(void) ret =ffa_rxtx_map (virt_to_phys (drv_info->tx_buffer),virt_to_phys (drv_info->rx_buffer), -PAGE_ALIGN (rxtx_bufsz) /FFA_PAGE_SIZE ); + rxtx_bufsz /FFA_PAGE_SIZE );if (ret) {pr_err ("failed to register FFA RxTx buffers\n" ); goto free_pages;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 201/461 firmware: arm_ffa: Snapshot notifier callbacks under lock
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit 38290b180a4d5746baed796d49f88d56d2f336cd ]
Both notification handlers currently look up a notifier callback under notify_lock, drop the lock, and then dereference the returned notifier entry. A concurrent unregister can delete and free that entry in the gap, leaving the handler to dereference stale memory.
Copy the callback pointer and callback data while notify_lock is still held and invoke the callback only after the lock is dropped. This keeps the existing callback execution model while removing the use-after-free window in both the framework and non-framework notification paths.
--- drivers/firmware/arm_ffa/driver.c | 35 ++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-)
drivers/firmware/arm_ffa/driver.c +23 -12
@@ -1463,20 +1463,25 @@ static int ffa_notify_send(struct ffa_device *dev, int notify_id,static void handle_notif_callbacks (u64 bitmap,enum notify_type type) { +ffa_notifier_cb cb; +void *cb_data;int notify_id; -struct notifier_cb_info *cb_info =NULL ;for (notify_id =0 ; notify_id <=FFA_MAX_NOTIFICATIONS && bitmap; notify_id++, bitmap >>=1 ) {if (!(bitmap &1 ))continue ; -read_lock (&drv_info->notify_lock); - cb_info =notifier_hnode_get_by_type (notify_id, type); -read_unlock (&drv_info->notify_lock); + scoped_guard(read_lock, &drv_info->notify_lock) { +struct notifier_cb_info *cb_info; + + cb_info =notifier_hnode_get_by_type (notify_id, type); + cb = cb_info ? cb_info->cb :NULL ; + cb_data = cb_info ? cb_info->cb_data :NULL ; + } -if (cb_info && cb_info->cb) - cb_info->cb (notify_id, cb_info->cb_data); +if (cb) +cb (notify_id, cb_data); } }
@@ -1484,9 +1489,10 @@ static void handle_fwk_notif_callbacks(u32 bitmap) {void *buf;uuid_t uuid; +void *fwk_cb_data;int notify_id =0 , target; +ffa_fwk_notifier_cb fwk_cb;struct ffa_indirect_msg_hdr *msg; -struct notifier_cb_info *cb_info =NULL ;size_t min_offset = offsetof(struct ffa_indirect_msg_hdr , uuid);/* Only one framework notification defined and supported for now */
@@ -1522,12 +1528,17 @@ static void handle_fwk_notif_callbacks(u32 bitmap)ffa_rx_release (); } -read_lock (&drv_info->notify_lock); - cb_info =notifier_hnode_get_by_vmid_uuid (notify_id, target, &uuid); -read_unlock (&drv_info->notify_lock); + scoped_guard(read_lock, &drv_info->notify_lock) { +struct notifier_cb_info *cb_info; + + cb_info =notifier_hnode_get_by_vmid_uuid (notify_id, target, + &uuid); + fwk_cb = cb_info ? cb_info->fwk_cb :NULL ; + fwk_cb_data = cb_info ? cb_info->cb_data :NULL ; + } -if (cb_info && cb_info->fwk_cb) - cb_info->fwk_cb (notify_id, cb_info->cb_data, buf); +if (fwk_cb) +fwk_cb (notify_id, fwk_cb_data, buf);kfree (buf); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 202/461 firmware: arm_ffa: Fix sched-recv callback partition lookup
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit a6848a50404eefb6f0b131c21881a2d8d21b31a9 ]
ffa_sched_recv_cb_update() used list_for_each_entry_safe() to search for a matching partition and then tested the iterator against NULL. That is not a valid end-of-list check for circular lists and can fall through with an invalid pointer. Use a normal iterator and detect the not-found case correctly before touching the partition state.
--- drivers/firmware/arm_ffa/driver.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/firmware/arm_ffa/driver.c +3 -3
@@ -1207,7 +1207,7 @@ static int ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback, void *cb_data, bool is_registration) { - struct ffa_dev_part_info *partition = NULL, *tmp; + struct ffa_dev_part_info *partition = NULL;struct list_head *phead;bool cb_valid;
@@ -1220,11 +1220,11 @@ ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback,return -EINVAL ; } -list_for_each_entry_safe (partition, tmp, phead, node) +list_for_each_entry (partition, phead, node)if (partition->dev == dev)break ; -if (!partition) { +if (&partition->node == phead) {pr_err ("%s: No such partition ID 0x%x\n" , __func__, dev->vm_id);return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 203/461 ARM: integrator: Fix early initialization
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit 90d77b30a666049ad24df463f52e5d529c44e8cd ]
Starting with commit bdb249fce9ad4 ("ARM: integrator: read counter using syscon/regmap"), intcp_init_early calls syscon_regmap_lookup_by_compatible which in turn calls of_syscon_register. This function allocates memory. Since the memory management code has not been initialized at that time, the call always fails. It either returns -ENOMEM or crashes as follows.
Unable to handle kernel NULL pointer dereference at virtual address 0000000c when read [0000000c] *pgd=00000000 Internal error: Oops: 5 [#1] ARM Modules linked in: CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.15.0-rc5-00026-g5fcc9bf84ee5 #1 PREEMPT Hardware name: ARM Integrator/CP (Device Tree) PC is at __kmalloc_cache_noprof+0xec/0x39c LR is at __kmalloc_cache_noprof+0x34/0x39c ... Call trace: __kmalloc_cache_noprof from of_syscon_register+0x7c/0x310 of_syscon_register from device_node_get_regmap+0xa4/0xb0 device_node_get_regmap from intcp_init_early+0xc/0x40 intcp_init_early from start_kernel+0x60/0x688 start_kernel from 0x0
The crash is seen due to a dereferenced pointer which is not supposed to be NULL but is NULL if the memory management subsystem has not been initialized. The crash is not seen with all versions of gcc. Some versions such as gcc 9.x apparently do not dereference the pointer, presumably if tracing is disabled. The problem has been reproduced with gcc 10.x, 11.x, and 13.x. Either case, if the crash is not seen, the call to syscon_regmap_lookup_by_compatible returns -ENOMEM, and sched_clock_register is never called.
Fix the problem by moving the early initialization code into the standard machine initialization code.
--- arch/arm/mach-versatile/integrator_cp.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-)
arch/arm/mach-versatile/integrator_cp.c +4 -9
@@ -86,14 +86,6 @@ static u64 notrace intcp_read_sched_clock(void)return val; } -static void __init intcp_init_early (void ) -{ - cm_map =syscon_regmap_lookup_by_compatible ("arm,core-module-integrator" ); -if (IS_ERR (cm_map)) -return ; -sched_clock_register (intcp_read_sched_clock,32 ,24000000 ); -} -static void __init intcp_init_irq_of (void ) {cm_init ();
@@ -119,6 +111,10 @@ static void __init intcp_init_of(void) {struct device_node *cpcon; + cm_map =syscon_regmap_lookup_by_compatible ("arm,core-module-integrator" ); +if (!IS_ERR (cm_map)) +sched_clock_register (intcp_read_sched_clock,32 ,24000000 ); + cpcon =of_find_matching_node (NULL , intcp_syscon_match);if (!cpcon)return ;
@@ -138,7 +134,6 @@ static const char * intcp_dt_board_compat[] = {DT_MACHINE_START (INTEGRATOR_CP_DT ,"ARM Integrator/CP (Device Tree)" ) .reserve = integrator_reserve, .map_io = intcp_map_io, - .init_early = intcp_init_early, .init_irq = intcp_init_irq_of, .init_machine = intcp_init_of, .dt_compat = intcp_dt_board_compat,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 204/461 ALSA: hda: cs35l56: Put ACPI device after setting companion
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuhao Fu <[email protected]>
[ Upstream commit aa2fbece1b07954ef26488c800d126a36a8ab93e ]
acpi_dev_get_first_match_dev() returns a refcounted ACPI device and callers are expected to balance it with acpi_dev_put().
When no companion is already attached, cs35l56_hda_read_acpi() looks up an ACPI device and sets it with ACPI_COMPANION_SET(), but leaves the lookup reference held.
ACPI_COMPANION_SET() does not take ownership of that reference, so drop it with acpi_dev_put() after attaching the companion.
--- sound/hda/codecs/side-codecs/cs35l56_hda.c | 1 + 1 file changed, 1 insertion(+)
sound/hda/codecs/side-codecs/cs35l56_hda.c +1 -0
@@ -1041,6 +1041,7 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id)return -ENODEV ; }ACPI_COMPANION_SET (cs35l56->base.dev, adev); +acpi_dev_put (adev); }/* Initialize things that could be overwritten by a fixup */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 205/461 ALSA: hda: cs35l41: Put ACPI device on missing physical node
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuhao Fu <[email protected]>
[ Upstream commit fca7401fe37f7abc6e54147ea560f37279231137 ]
acpi_dev_get_first_match_dev() returns a refcounted ACPI device and callers must balance it with acpi_dev_put().
cs35l41_hda_read_acpi() stores the returned ACPI device in cs35l41->dacpi. That reference is normally released by the later probe cleanup or the remove path, but the NULL-check on physdev exits before either of those paths can run.
Drop the lookup reference before returning -ENODEV.
--- sound/hda/codecs/side-codecs/cs35l41_hda.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
sound/hda/codecs/side-codecs/cs35l41_hda.c +3 -1
@@ -1896,8 +1896,10 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i cs35l41->dacpi = adev; physdev =get_device (acpi_get_first_physical_node (adev)); -if (!physdev) +if (!physdev) { +acpi_dev_put (adev);return -ENODEV ; + } sub =acpi_get_subsystem_id (ACPI_HANDLE (physdev));if (IS_ERR (sub))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 206/461 btrfs: tracepoints: fix sleep while in atomic context in btrfs_sync_file()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit c73370c677646e86fc4b1780fb07027bdf847375 ]
The trace event btrfs_sync_file() is called in an atomic context (all trace events are) and its call to dput(), which is needed due to the call to dget_parent(), can sleep, triggering a kernel splat.
This can be reproduced by enabling the trace event and running btrfs/056 from fstests for example. The splat shown in dmesg is the following:
[53.919] BUG: sleeping function called from invalid context at fs/dcache.c:970 [53.947] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 32773, name: xfs_io [53.988] preempt_count: 2, expected: 0 [53.967] RCU nest depth: 0, expected: 0 [53.943] Preemption disabled at: [53.944] [<0000000000000000>] 0x0 [54.078] CPU: 0 UID: 0 PID: 32773 Comm: xfs_io Tainted: G W 7.1.0-rc1-btrfs-next-232+ #1 PREEMPT(full) [54.070] Tainted: [W]=WARN [54.071] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014 [54.072] Call Trace: [54.074] <TASK> [54.076] dump_stack_lvl+0x56/0x80 [54.079] __might_resched.cold+0xd6/0x10f [54.072] dput.part.0+0x24/0x110 [54.078] trace_event_raw_event_btrfs_sync_file+0x75/0x140 [btrfs] [54.089] btrfs_sync_file+0x1ed/0x530 [btrfs] [54.087] ? __handle_mm_fault+0x8ae/0xed0 [54.089] btrfs_do_write_iter+0x172/0x210 [btrfs] [54.091] vfs_write+0x21f/0x450 [54.094] __x64_sys_pwrite64+0x8d/0xc0 [54.096] ? do_user_addr_fault+0x20c/0x670 [54.099] do_syscall_64+0x60/0xf20 [54.092] ? clear_bhb_loop+0x60/0xb0 [54.094] entry_SYSCALL_64_after_hwframe+0x76/0x7e
So stop using dget_parent() and dput() and access the parent dentry directly as dentry->d_parent. This is also what ext4 is doing in its equivalent trace event ext4_sync_file_enter().
--- include/trace/events/btrfs.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
include/trace/events/btrfs.h +1 -3
@@ -771,10 +771,8 @@ TRACE_EVENT(btrfs_sync_file,TP_fast_assign ( struct dentry *dentry = file_dentry(file);struct inode *inode =file_inode (file); -struct dentry *parent =dget_parent (dentry); -struct inode *parent_inode =d_inode (parent); +struct inode *parent_inode =d_inode (dentry->d_parent); -dput (parent);TP_fast_assign_fsid (btrfs_sb (inode->i_sb)); __entry->ino =btrfs_ino (BTRFS_I (inode)); __entry->parent =btrfs_ino (BTRFS_I (parent_inode));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 207/461 netfilter: x_tables: allow initial table replace without emitting audit log message
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 8e72510db9fa2d41f2b06d5c01fe9020e076fee4 ]
At the moment we emit the audit log a bit too early, which makes it necessary to also emit an unregister log in case we have to unwind errors after possible hook register failure.
Followup patch will be slightly simpler if we can delay the register message until after the hooks have been wired up.
Stable-dep-of: b62eb8dcf2c4 ("netfilter: x_tables: allocate hook ops while under mutex")
--- net/netfilter/x_tables.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-)
net/netfilter/x_tables.c +20 -9
@@ -1405,11 +1405,9 @@ struct xt_counters *xt_counters_alloc(unsigned int counters) }EXPORT_SYMBOL (xt_counters_alloc); -struct xt_table_info * -xt_replace_table (struct xt_table *table, -unsigned int num_counters, -struct xt_table_info *newinfo, -int *error) +static struct xt_table_info * +do_replace_table (struct xt_table *table,unsigned int num_counters, +struct xt_table_info *newinfo,int *error) {struct xt_table_info *private;unsigned int cpu;
@@ -1464,10 +1462,23 @@ xt_replace_table(struct xt_table *table, } } -audit_log_nfcfg (table->name, table->af, private->number, - !private->number ?AUDIT_XT_OP_REGISTER : -AUDIT_XT_OP_REPLACE , -GFP_KERNEL ); +return private; +} + +struct xt_table_info * +xt_replace_table (struct xt_table *table,unsigned int num_counters, +struct xt_table_info *newinfo, +int *error) +{ +struct xt_table_info *private; + + private =do_replace_table (table, num_counters, newinfo, error); +if (private) +audit_log_nfcfg (table->name, table->af, private->number, + !private->number ?AUDIT_XT_OP_REGISTER : +AUDIT_XT_OP_REPLACE , +GFP_KERNEL ); +return private; }EXPORT_SYMBOL_GPL (xt_replace_table);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 208/461 netfilter: x_tables: allocate hook ops while under mutex
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit b62eb8dcf2c47d4d676a434efbd57c4f776f7829 ]
arp/ip(6)t_register_table() add the table to the per-netns list via xt_register_table() before allocating the per-netns hook ops copy via kmemdup_array(). This leaves a window where the table is visible in the list with ops=NULL.
If the pernet exit happens runs concurrently the pre_exit callback finds the table via xt_find_table() and passes the NULL ops pointer to nf_unregister_net_hooks(), causing a NULL dereference:
general protection fault in nf_unregister_net_hooks+0xbc/0x150
RIP: nf_unregister_net_hooks (net/netfilter/core.c:613)
Call Trace:
ipt_unregister_table_pre_exit
iptable_mangle_net_pre_exit
ops_pre_exit_list
cleanup_netFix by moving the ops allocation into the xtables core so the table is never in the list without valid ops. Also ensure the table is no longer processing packets before its torn down on error unwind. nf_register_net_hooks might have published at least one hook; call synchronize_rcu() if there was an error.
audit log register message gets deferred until all operations have passed, this avoids need to emit another ureg message in case of error unwinding.
Based on earlier patch by Tristan Madani.
--- include/linux/netfilter/x_tables.h | 1 + net/ipv4/netfilter/arp_tables.c | 35 +++------------------ net/ipv4/netfilter/ip_tables.c | 41 +++--------------------- net/ipv6/netfilter/ip6_tables.c | 38 +++-------------------- net/netfilter/x_tables.c | 50 +++++++++++++++++++++++++----- 5 files changed, 55 insertions(+), 110 deletions(-)
include/linux/netfilter/x_tables.h +1 -0
@@ -297,6 +297,7 @@ struct xt_counters *xt_counters_alloc(unsigned int counters);struct xt_table *xt_register_table (struct net *net,const struct xt_table *table, +const struct nf_hook_ops *template_ops,struct xt_table_info *bootstrap,struct xt_table_info *newinfo);void *xt_unregister_table (struct xt_table *table);
net/ipv4/netfilter/arp_tables.c +4 -31
@@ -1522,13 +1522,11 @@ int arpt_register_table(struct net *net,const struct arpt_replace *repl, const struct nf_hook_ops *template_ops) { - struct nf_hook_ops *ops; -unsigned int num_ops; -int ret, i; -struct xt_table_info *newinfo; struct xt_table_info bootstrap = {0}; -void *loc_cpu_entry; +struct xt_table_info *newinfo;struct xt_table *new_table; +void *loc_cpu_entry; +int ret; newinfo =xt_alloc_table_info (repl->size);if (!newinfo)
@@ -1543,7 +1541,7 @@ int arpt_register_table(struct net *net,return ret; } - new_table =xt_register_table (net, table, &bootstrap, newinfo); + new_table =xt_register_table (net, table, template_ops, &bootstrap, newinfo);if (IS_ERR (new_table)) {struct arpt_entry *iter;
@@ -1553,31 +1551,6 @@ int arpt_register_table(struct net *net,return PTR_ERR (new_table); } - num_ops =hweight32 (table->valid_hooks); -if (num_ops ==0 ) { - ret = -EINVAL ; - goto out_free; - } - - ops =kmemdup_array (template_ops, num_ops,sizeof (*ops),GFP_KERNEL ); -if (!ops) { - ret = -ENOMEM ; - goto out_free; - } - -for (i =0 ; i < num_ops; i++) - ops[i].priv = new_table; - - new_table->ops = ops; - - ret =nf_register_net_hooks (net, ops, num_ops); -if (ret !=0 ) - goto out_free; - -return ret; - -out_free: -__arpt_unregister_table (net, new_table);return ret; }
net/ipv4/netfilter/ip_tables.c +4 -37
61 lines not on this page · raw message
net/ipv6/netfilter/ip6_tables.c +4 -34
@@ -1733,13 +1733,11 @@ int ip6t_register_table(struct net *net, const struct xt_table *table,const struct ip6t_replace *repl, const struct nf_hook_ops *template_ops) { - struct nf_hook_ops *ops; -unsigned int num_ops; -int ret, i; -struct xt_table_info *newinfo; struct xt_table_info bootstrap = {0}; -void *loc_cpu_entry; +struct xt_table_info *newinfo;struct xt_table *new_table; +void *loc_cpu_entry; +int ret; newinfo =xt_alloc_table_info (repl->size);if (!newinfo)
@@ -1754,7 +1752,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table,return ret; } - new_table =xt_register_table (net, table, &bootstrap, newinfo); + new_table =xt_register_table (net, table, template_ops, &bootstrap, newinfo);if (IS_ERR (new_table)) {struct ip6t_entry *iter;
@@ -1764,34 +1762,6 @@ int ip6t_register_table(struct net *net, const struct xt_table *table,return PTR_ERR (new_table); } -if (!template_ops) -return 0 ; - - num_ops =hweight32 (table->valid_hooks); -if (num_ops ==0 ) { - ret = -EINVAL ; - goto out_free; - } - - ops =kmemdup_array (template_ops, num_ops,sizeof (*ops),GFP_KERNEL ); -if (!ops) { - ret = -ENOMEM ; - goto out_free; - } - -for (i =0 ; i < num_ops; i++) - ops[i].priv = new_table; - - new_table->ops = ops; - - ret =nf_register_net_hooks (net, ops, num_ops); -if (ret !=0 ) - goto out_free; - -return ret; - -out_free: -__ip6t_unregister_table (net, new_table);return ret; }
net/netfilter/x_tables.c +42 -8
90 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 209/461 netfilter: x_tables: unregister the templates first
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit d338693d778579b676a61346849bebd892427158 ]
When the module is going away we need to zap the template first. Else there is a small race window where userspace could instantiate a new table after the pernet exit function has removed the current table.
--- net/ipv4/netfilter/arptable_filter.c | 2 +- net/ipv4/netfilter/iptable_filter.c | 2 +- net/ipv4/netfilter/iptable_mangle.c | 2 +- net/ipv4/netfilter/iptable_raw.c | 2 +- net/ipv4/netfilter/iptable_security.c | 2 +- net/ipv6/netfilter/ip6table_filter.c | 2 +- net/ipv6/netfilter/ip6table_mangle.c | 2 +- net/ipv6/netfilter/ip6table_raw.c | 2 +- net/ipv6/netfilter/ip6table_security.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-)
net/ipv4/netfilter/arptable_filter.c +1 -1
@@ -82,8 +82,8 @@ static int __init arptable_filter_init(void) static void __exit arptable_filter_fini(void) { -unregister_pernet_subsys (&arptable_filter_net_ops);xt_unregister_template (&packet_filter); +unregister_pernet_subsys (&arptable_filter_net_ops);kfree (arpfilter_ops); }
net/ipv4/netfilter/iptable_filter.c +1 -1
@@ -101,8 +101,8 @@ static int __init iptable_filter_init(void) static void __exit iptable_filter_fini(void) { -unregister_pernet_subsys (&iptable_filter_net_ops);xt_unregister_template (&packet_filter); +unregister_pernet_subsys (&iptable_filter_net_ops);kfree (filter_ops); }
net/ipv4/netfilter/iptable_mangle.c +1 -1
@@ -135,8 +135,8 @@ static int __init iptable_mangle_init(void) static void __exit iptable_mangle_fini(void) { -unregister_pernet_subsys (&iptable_mangle_net_ops);xt_unregister_template (&packet_mangler); +unregister_pernet_subsys (&iptable_mangle_net_ops);kfree (mangle_ops); }
net/ipv4/netfilter/iptable_raw.c +1 -1
@@ -100,9 +100,9 @@ static int __init iptable_raw_init(void) static void __exit iptable_raw_fini(void) { +xt_unregister_template (&packet_raw);unregister_pernet_subsys (&iptable_raw_net_ops);kfree (rawtable_ops); -xt_unregister_template (&packet_raw); }module_init (iptable_raw_init);
net/ipv4/netfilter/iptable_security.c +1 -1
@@ -89,9 +89,9 @@ static int __init iptable_security_init(void) static void __exit iptable_security_fini(void) { +xt_unregister_template (&security_table);unregister_pernet_subsys (&iptable_security_net_ops);kfree (sectbl_ops); -xt_unregister_template (&security_table); }module_init (iptable_security_init);
net/ipv6/netfilter/ip6table_filter.c +1 -1
@@ -100,8 +100,8 @@ static int __init ip6table_filter_init(void) static void __exit ip6table_filter_fini(void) { -unregister_pernet_subsys (&ip6table_filter_net_ops);xt_unregister_template (&packet_filter); +unregister_pernet_subsys (&ip6table_filter_net_ops);kfree (filter_ops); }
net/ipv6/netfilter/ip6table_mangle.c +1 -1
@@ -128,8 +128,8 @@ static int __init ip6table_mangle_init(void) static void __exit ip6table_mangle_fini(void) { -unregister_pernet_subsys (&ip6table_mangle_net_ops);xt_unregister_template (&packet_mangler); +unregister_pernet_subsys (&ip6table_mangle_net_ops);kfree (mangle_ops); }
net/ipv6/netfilter/ip6table_raw.c +1 -1
@@ -98,8 +98,8 @@ static int __init ip6table_raw_init(void) static void __exit ip6table_raw_fini(void) { -unregister_pernet_subsys (&ip6table_raw_net_ops);xt_unregister_template (&packet_raw); +unregister_pernet_subsys (&ip6table_raw_net_ops);kfree (rawtable_ops); }
net/ipv6/netfilter/ip6table_security.c +1 -1
@@ -88,8 +88,8 @@ static int __init ip6table_security_init(void) static void __exit ip6table_security_fini(void) { -unregister_pernet_subsys (&ip6table_security_net_ops);xt_unregister_template (&security_table); +unregister_pernet_subsys (&ip6table_security_net_ops);kfree (sectbl_ops); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 210/461 netfilter: x_tables: add and use xt_unregister_table_pre_exit
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 527d6931473b75d90e38942aae6537d1a527f1fd ]
Remove the copypasted variants of _pre_exit and add one single function in the xtables core. ebtables is not compatible with x_tables and therefore unchanged.
This is a preparation patch to reduce noise in the followup bug fixes.
Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit")
--- include/linux/netfilter/x_tables.h | 1 + include/linux/netfilter_arp/arp_tables.h | 1 - include/linux/netfilter_ipv4/ip_tables.h | 1 - include/linux/netfilter_ipv6/ip6_tables.h | 1 - net/ipv4/netfilter/arp_tables.c | 9 ------- net/ipv4/netfilter/arptable_filter.c | 2 +- net/ipv4/netfilter/ip_tables.c | 9 ------- net/ipv4/netfilter/iptable_filter.c | 2 +- net/ipv4/netfilter/iptable_mangle.c | 2 +- net/ipv4/netfilter/iptable_nat.c | 1 + net/ipv4/netfilter/iptable_raw.c | 2 +- net/ipv4/netfilter/iptable_security.c | 2 +- net/ipv6/netfilter/ip6_tables.c | 9 ------- net/ipv6/netfilter/ip6table_filter.c | 2 +- net/ipv6/netfilter/ip6table_mangle.c | 2 +- net/ipv6/netfilter/ip6table_nat.c | 1 + net/ipv6/netfilter/ip6table_raw.c | 2 +- net/ipv6/netfilter/ip6table_security.c | 2 +- net/netfilter/x_tables.c | 29 +++++++++++++++++++++++ 19 files changed, 41 insertions(+), 39 deletions(-)
include/linux/netfilter/x_tables.h +1 -0
@@ -301,6 +301,7 @@ struct xt_table *xt_register_table(struct net *net,struct xt_table_info *bootstrap, struct xt_table_info *newinfo);void *xt_unregister_table (struct xt_table *table); +void xt_unregister_table_pre_exit (struct net *net,u8 af,const char *name); struct xt_table_info *xt_replace_table(struct xt_table *table, unsigned int num_counters,
include/linux/netfilter_arp/arp_tables.h +0 -1
@@ -53,7 +53,6 @@ int arpt_register_table(struct net *net, const struct xt_table *table,const struct arpt_replace *repl, const struct nf_hook_ops *ops);void arpt_unregister_table (struct net *net,const char *name); -void arpt_unregister_table_pre_exit (struct net *net,const char *name);extern unsigned int arpt_do_table (void *priv,struct sk_buff *skb,const struct nf_hook_state *state);
include/linux/netfilter_ipv4/ip_tables.h +0 -1
@@ -26,7 +26,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table,const struct ipt_replace *repl, const struct nf_hook_ops *ops); -void ipt_unregister_table_pre_exit (struct net *net,const char *name);void ipt_unregister_table_exit (struct net *net,const char *name);/* Standard entry. */
include/linux/netfilter_ipv6/ip6_tables.h +0 -1
@@ -27,7 +27,6 @@ extern void *ip6t_alloc_initial_table(const struct xt_table *);int ip6t_register_table (struct net *net,const struct xt_table *table,const struct ip6t_replace *repl,const struct nf_hook_ops *ops); -void ip6t_unregister_table_pre_exit (struct net *net,const char *name);void ip6t_unregister_table_exit (struct net *net,const char *name);extern unsigned int ip6t_do_table (void *priv,struct sk_buff *skb,const struct nf_hook_state *state);
net/ipv4/netfilter/arp_tables.c +0 -9
@@ -1554,15 +1554,6 @@ int arpt_register_table(struct net *net,return ret; } -void arpt_unregister_table_pre_exit (struct net *net,const char *name) -{ -struct xt_table *table =xt_find_table (net,NFPROTO_ARP , name); - -if (table) -nf_unregister_net_hooks (net, table->ops,hweight32 (table->valid_hooks)); -} -EXPORT_SYMBOL (arpt_unregister_table_pre_exit); -void arpt_unregister_table (struct net *net,const char *name) {struct xt_table *table =xt_find_table (net,NFPROTO_ARP , name);
net/ipv4/netfilter/arptable_filter.c +1 -1
@@ -43,7 +43,7 @@ static int arptable_filter_table_init(struct net *net) static void __net_exit arptable_filter_net_pre_exit(struct net *net) { -arpt_unregister_table_pre_exit (net,"filter" ); +xt_unregister_table_pre_exit (net,NFPROTO_ARP ,"filter" ); } static void __net_exit arptable_filter_net_exit(struct net *net)
net/ipv4/netfilter/ip_tables.c +0 -9
@@ -1756,14 +1756,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table,return ret; } -void ipt_unregister_table_pre_exit (struct net *net,const char *name) -{ -struct xt_table *table =xt_find_table (net,NFPROTO_IPV4 , name); - -if (table) -nf_unregister_net_hooks (net, table->ops,hweight32 (table->valid_hooks)); -} -void ipt_unregister_table_exit (struct net *net,const char *name) {struct xt_table *table =xt_find_table (net,NFPROTO_IPV4 , name);
@@ -1854,7 +1846,6 @@ static void __exit ip_tables_fini(void) }EXPORT_SYMBOL (ipt_register_table); -EXPORT_SYMBOL (ipt_unregister_table_pre_exit);EXPORT_SYMBOL (ipt_unregister_table_exit);EXPORT_SYMBOL (ipt_do_table);module_init (ip_tables_init);
net/ipv4/netfilter/iptable_filter.c +1 -1
@@ -61,7 +61,7 @@ static int __net_init iptable_filter_net_init(struct net *net) static void __net_exit iptable_filter_net_pre_exit(struct net *net) { -ipt_unregister_table_pre_exit (net,"filter" ); +xt_unregister_table_pre_exit (net,NFPROTO_IPV4 ,"filter" ); } static void __net_exit iptable_filter_net_exit(struct net *net)
net/ipv4/netfilter/iptable_mangle.c +1 -1
@@ -96,7 +96,7 @@ static int iptable_mangle_table_init(struct net *net) static void __net_exit iptable_mangle_net_pre_exit(struct net *net) { -ipt_unregister_table_pre_exit (net,"mangle" ); +xt_unregister_table_pre_exit (net,NFPROTO_IPV4 ,"mangle" ); } static void __net_exit iptable_mangle_net_exit(struct net *net)
net/ipv4/netfilter/iptable_nat.c +1 -0
@@ -129,6 +129,7 @@ static int iptable_nat_table_init(struct net *net) static void __net_exit iptable_nat_net_pre_exit(struct net *net) {ipt_nat_unregister_lookups (net); +xt_unregister_table_pre_exit (net,NFPROTO_IPV4 ,"nat" ); } static void __net_exit iptable_nat_net_exit(struct net *net)
net/ipv4/netfilter/iptable_raw.c +1 -1
@@ -53,7 +53,7 @@ static int iptable_raw_table_init(struct net *net) static void __net_exit iptable_raw_net_pre_exit(struct net *net) { -ipt_unregister_table_pre_exit (net,"raw" ); +xt_unregister_table_pre_exit (net,NFPROTO_IPV4 ,"raw" ); } static void __net_exit iptable_raw_net_exit(struct net *net)
net/ipv4/netfilter/iptable_security.c +1 -1
@@ -50,7 +50,7 @@ static int iptable_security_table_init(struct net *net) static void __net_exit iptable_security_net_pre_exit(struct net *net) { -ipt_unregister_table_pre_exit (net,"security" ); +xt_unregister_table_pre_exit (net,NFPROTO_IPV4 ,"security" ); } static void __net_exit iptable_security_net_exit(struct net *net)
net/ipv6/netfilter/ip6_tables.c +0 -9
@@ -1765,14 +1765,6 @@ int ip6t_register_table(struct net *net, const struct xt_table *table,return ret; } -void ip6t_unregister_table_pre_exit (struct net *net,const char *name) -{ -struct xt_table *table =xt_find_table (net,NFPROTO_IPV6 , name); - -if (table) -nf_unregister_net_hooks (net, table->ops,hweight32 (table->valid_hooks)); -} -void ip6t_unregister_table_exit (struct net *net,const char *name) {struct xt_table *table =xt_find_table (net,NFPROTO_IPV6 , name);
@@ -1864,7 +1856,6 @@ static void __exit ip6_tables_fini(void) }EXPORT_SYMBOL (ip6t_register_table); -EXPORT_SYMBOL (ip6t_unregister_table_pre_exit);EXPORT_SYMBOL (ip6t_unregister_table_exit);EXPORT_SYMBOL (ip6t_do_table);
net/ipv6/netfilter/ip6table_filter.c +1 -1
@@ -60,7 +60,7 @@ static int __net_init ip6table_filter_net_init(struct net *net) static void __net_exit ip6table_filter_net_pre_exit(struct net *net) { -ip6t_unregister_table_pre_exit (net,"filter" ); +xt_unregister_table_pre_exit (net,NFPROTO_IPV6 ,"filter" ); } static void __net_exit ip6table_filter_net_exit(struct net *net)
net/ipv6/netfilter/ip6table_mangle.c +1 -1
@@ -89,7 +89,7 @@ static int ip6table_mangle_table_init(struct net *net) static void __net_exit ip6table_mangle_net_pre_exit(struct net *net) { -ip6t_unregister_table_pre_exit (net,"mangle" ); +xt_unregister_table_pre_exit (net,NFPROTO_IPV6 ,"mangle" ); } static void __net_exit ip6table_mangle_net_exit(struct net *net)
net/ipv6/netfilter/ip6table_nat.c +1 -0
@@ -131,6 +131,7 @@ static int ip6table_nat_table_init(struct net *net) static void __net_exit ip6table_nat_net_pre_exit(struct net *net) {ip6t_nat_unregister_lookups (net); +xt_unregister_table_pre_exit (net,NFPROTO_IPV6 ,"nat" ); } static void __net_exit ip6table_nat_net_exit(struct net *net)
net/ipv6/netfilter/ip6table_raw.c +1 -1
@@ -52,7 +52,7 @@ static int ip6table_raw_table_init(struct net *net) static void __net_exit ip6table_raw_net_pre_exit(struct net *net) { -ip6t_unregister_table_pre_exit (net,"raw" ); +xt_unregister_table_pre_exit (net,NFPROTO_IPV6 ,"raw" ); } static void __net_exit ip6table_raw_net_exit(struct net *net)
net/ipv6/netfilter/ip6table_security.c +1 -1
@@ -49,7 +49,7 @@ static int ip6table_security_table_init(struct net *net) static void __net_exit ip6table_security_net_pre_exit(struct net *net) { -ip6t_unregister_table_pre_exit (net,"security" ); +xt_unregister_table_pre_exit (net,NFPROTO_IPV6 ,"security" ); } static void __net_exit ip6table_security_net_exit(struct net *net)
net/netfilter/x_tables.c +29 -0
@@ -1583,6 +1583,35 @@ void *xt_unregister_table(struct xt_table *table)return private; }EXPORT_SYMBOL_GPL (xt_unregister_table); + +/** +* xt_unregister_table_pre_exit - pre-shutdown unregister of a table +* @net: network namespace +* @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) +* @name: name of the table to unregister +* +* Unregisters the specified netfilter table from the given network namespace +* and also unregisters the hooks from netfilter core: no new packets will be +* processed. +*/ +void xt_unregister_table_pre_exit (struct net *net,u8 af,const char *name) +{ +struct xt_pernet *xt_net =net_generic (net, xt_pernet_id); +struct xt_table *t; + +mutex_lock (&xt[af].mutex); +list_for_each_entry (t, &xt_net->tables[af], list) { +if (strcmp (t->name, name) ==0 ) { +mutex_unlock (&xt[af].mutex); + +if (t->ops)/* nat table registers with nat core, t->ops is NULL. */ +nf_unregister_net_hooks (net, t->ops,hweight32 (t->valid_hooks)); +return ; + } + } +mutex_unlock (&xt[af].mutex); +} +EXPORT_SYMBOL (xt_unregister_table_pre_exit);#endif #ifdef CONFIG_PROC_FS
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 211/461 netfilter: x_tables: add and use xtables_unregister_table_exit
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit b4597d5fd7d2f8cebfffd40dffb5e003cc78964c ]
Previous change added xtables_unregister_table_pre_exit to detach the table from the packetpath and to unlink it from the active table list. In case of rmmod, userspace that is doing set/getsockopt for this table will not be able to re-instantiate the table:
1. The larval table has been removed already
2. existing instantiated table is no longer on the xt pernet table list.
This adds the second stage helper:
unlink the table from the dying list, free the hook ops (if any) and do the audit notification. It replaces xt_unregister_table().
--- include/linux/netfilter/x_tables.h | 2 +- net/ipv4/netfilter/arp_tables.c | 9 ++-- net/ipv4/netfilter/ip_tables.c | 9 ++-- net/ipv4/netfilter/iptable_nat.c | 5 +- net/ipv6/netfilter/ip6_tables.c | 9 ++-- net/ipv6/netfilter/ip6table_nat.c | 5 +- net/netfilter/x_tables.c | 81 +++++++++++++++++++++++------- 7 files changed, 83 insertions(+), 37 deletions(-)
include/linux/netfilter/x_tables.h +1 -1
@@ -300,8 +300,8 @@ struct xt_table *xt_register_table(struct net *net,const struct nf_hook_ops *template_ops, struct xt_table_info *bootstrap, struct xt_table_info *newinfo); -void *xt_unregister_table (struct xt_table *table);void xt_unregister_table_pre_exit (struct net *net,u8 af,const char *name); +struct xt_table *xt_unregister_table_exit (struct net *net,u8 af,const char *name); struct xt_table_info *xt_replace_table(struct xt_table *table, unsigned int num_counters,
net/ipv4/netfilter/arp_tables.c +4 -5
@@ -1501,13 +1501,11 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *lenstatic void __arpt_unregister_table (struct net *net,struct xt_table *table) { -struct xt_table_info *private; -void *loc_cpu_entry; +struct xt_table_info *private = table->private;struct module *table_owner = table->me; +void *loc_cpu_entry;struct arpt_entry *iter; - private =xt_unregister_table (table); -/* Decrease module usage counts and free resources */ loc_cpu_entry = private->entries;xt_entry_foreach (iter, loc_cpu_entry, private->size)
@@ -1515,6 +1513,7 @@ static void __arpt_unregister_table(struct net *net, struct xt_table *table)if (private->number > private->initial_entries)module_put (table_owner);xt_free_table_info (private); +kfree (table); } int arpt_register_table(struct net *net,
@@ -1556,7 +1555,7 @@ int arpt_register_table(struct net *net,void arpt_unregister_table (struct net *net,const char *name) { -struct xt_table *table =xt_find_table (net,NFPROTO_ARP , name); +struct xt_table *table =xt_unregister_table_exit (net,NFPROTO_ARP , name);if (table)__arpt_unregister_table (net, table);
net/ipv4/netfilter/ip_tables.c +4 -5
@@ -1704,12 +1704,10 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)static void __ipt_unregister_table (struct net *net,struct xt_table *table) { -struct xt_table_info *private; -void *loc_cpu_entry; +struct xt_table_info *private = table->private;struct module *table_owner = table->me;struct ipt_entry *iter; - - private =xt_unregister_table (table); +void *loc_cpu_entry;/* Decrease module usage counts and free resources */ loc_cpu_entry = private->entries;
@@ -1718,6 +1716,7 @@ static void __ipt_unregister_table(struct net *net, struct xt_table *table)if (private->number > private->initial_entries)module_put (table_owner);xt_free_table_info (private); +kfree (table); } int ipt_register_table(struct net *net, const struct xt_table *table,
@@ -1758,7 +1757,7 @@ int ipt_register_table(struct net *net, const struct xt_table *table,void ipt_unregister_table_exit (struct net *net,const char *name) { -struct xt_table *table =xt_find_table (net,NFPROTO_IPV4 , name); +struct xt_table *table =xt_unregister_table_exit (net,NFPROTO_IPV4 , name);if (table)__ipt_unregister_table (net, table);
net/ipv4/netfilter/iptable_nat.c +4 -1
@@ -119,8 +119,11 @@ static int iptable_nat_table_init(struct net *net) } ret =ipt_nat_register_lookups (net); -if (ret <0 ) +if (ret <0 ) { +xt_unregister_table_pre_exit (net,NFPROTO_IPV4 ,"nat" ); +synchronize_rcu ();ipt_unregister_table_exit (net,"nat" ); + }kfree (repl);return ret;
net/ipv6/netfilter/ip6_tables.c +4 -5
@@ -1713,12 +1713,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)static void __ip6t_unregister_table (struct net *net,struct xt_table *table) { -struct xt_table_info *private; -void *loc_cpu_entry; +struct xt_table_info *private = table->private;struct module *table_owner = table->me;struct ip6t_entry *iter; - - private =xt_unregister_table (table); +void *loc_cpu_entry;/* Decrease module usage counts and free resources */ loc_cpu_entry = private->entries;
@@ -1727,6 +1725,7 @@ static void __ip6t_unregister_table(struct net *net, struct xt_table *table)if (private->number > private->initial_entries)module_put (table_owner);xt_free_table_info (private); +kfree (table); } int ip6t_register_table(struct net *net, const struct xt_table *table,
@@ -1767,7 +1766,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table,void ip6t_unregister_table_exit (struct net *net,const char *name) { -struct xt_table *table =xt_find_table (net,NFPROTO_IPV6 , name); +struct xt_table *table =xt_unregister_table_exit (net,NFPROTO_IPV6 , name);if (table)__ip6t_unregister_table (net, table);
net/ipv6/netfilter/ip6table_nat.c +4 -1
@@ -121,8 +121,11 @@ static int ip6table_nat_table_init(struct net *net) } ret =ip6t_nat_register_lookups (net); -if (ret <0 ) +if (ret <0 ) { +xt_unregister_table_pre_exit (net,NFPROTO_IPV6 ,"nat" ); +synchronize_rcu ();ip6t_unregister_table_exit (net,"nat" ); + }kfree (repl);return ret;
net/netfilter/x_tables.c +62 -19
125 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 212/461 netfilter: ebtables: move to two-stage removal scheme
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit b7f0544d86d439cb946515d2ef6a0a75e8626710 ]
Like previous patches for x_tables, follow same pattern in ebtables. We can't reuse xt helpers: ebt_table struct layout is incompatible.
table->ops assignment is now done while still holding the ebt mutex to make sure we never expose partially-filled table struct.
--- net/bridge/netfilter/ebtable_broute.c | 2 +- net/bridge/netfilter/ebtable_filter.c | 2 +- net/bridge/netfilter/ebtable_nat.c | 2 +- net/bridge/netfilter/ebtables.c | 60 +++++++++++++++++---------- 4 files changed, 40 insertions(+), 26 deletions(-)
net/bridge/netfilter/ebtable_broute.c +1 -1
@@ -128,8 +128,8 @@ static int __init ebtable_broute_init(void) static void __exit ebtable_broute_fini(void) { -unregister_pernet_subsys (&broute_net_ops);ebt_unregister_template (&broute_table); +unregister_pernet_subsys (&broute_net_ops); }module_init (ebtable_broute_init);
net/bridge/netfilter/ebtable_filter.c +1 -1
@@ -109,8 +109,8 @@ static int __init ebtable_filter_init(void) static void __exit ebtable_filter_fini(void) { -unregister_pernet_subsys (&frame_filter_net_ops);ebt_unregister_template (&frame_filter); +unregister_pernet_subsys (&frame_filter_net_ops); }module_init (ebtable_filter_init);
net/bridge/netfilter/ebtable_nat.c +1 -1
@@ -109,8 +109,8 @@ static int __init ebtable_nat_init(void) static void __exit ebtable_nat_fini(void) { -unregister_pernet_subsys (&frame_nat_net_ops);ebt_unregister_template (&frame_nat); +unregister_pernet_subsys (&frame_nat_net_ops); }module_init (ebtable_nat_init);
net/bridge/netfilter/ebtables.c +37 -23
115 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 213/461 netfilter: ebtables: close dangling table module init race
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 92c603fa07bc0d6a17345de3ad7954730b8de44b ]
sashiko reported for a related patch:
In modules like iptable_raw.c, [..], if register_pernet_subsys() fails, the rollback might call kfree(rawtable_ops) before [..]
During this window, could a concurrent userspace process find the globally visible template, trigger table_init(), [..]
The table init functions must always register the template last.
Otherwise, set/getsockopt can instantiate a table in a namespace while the required pernet ops (contain the destructor) isn't available. This change is also required in x_tables, handled in followup change.
--- net/bridge/netfilter/ebtable_broute.c | 12 +++++------- net/bridge/netfilter/ebtable_filter.c | 12 +++++------- net/bridge/netfilter/ebtable_nat.c | 10 ++++------ 3 files changed, 14 insertions(+), 20 deletions(-)
net/bridge/netfilter/ebtable_broute.c +5 -7
@@ -112,18 +112,16 @@ static struct pernet_operations broute_net_ops = {static int __init ebtable_broute_init (void ) { -int ret =ebt_register_template (&broute_table, broute_table_init); +int ret =register_pernet_subsys (&broute_net_ops);if (ret)return ret; - ret =register_pernet_subsys (&broute_net_ops); -if (ret) { -ebt_unregister_template (&broute_table); -return ret; - } + ret =ebt_register_template (&broute_table, broute_table_init); +if (ret) +unregister_pernet_subsys (&broute_net_ops); -return 0 ; +return ret; } static void __exit ebtable_broute_fini(void)
net/bridge/netfilter/ebtable_filter.c +5 -7
@@ -93,18 +93,16 @@ static struct pernet_operations frame_filter_net_ops = {static int __init ebtable_filter_init (void ) { -int ret =ebt_register_template (&frame_filter, frame_filter_table_init); +int ret =register_pernet_subsys (&frame_filter_net_ops);if (ret)return ret; - ret =register_pernet_subsys (&frame_filter_net_ops); -if (ret) { -ebt_unregister_template (&frame_filter); -return ret; - } + ret =ebt_register_template (&frame_filter, frame_filter_table_init); +if (ret) +unregister_pernet_subsys (&frame_filter_net_ops); -return 0 ; +return ret; } static void __exit ebtable_filter_fini(void)
net/bridge/netfilter/ebtable_nat.c +4 -6
@@ -93,16 +93,14 @@ static struct pernet_operations frame_nat_net_ops = {static int __init ebtable_nat_init (void ) { -int ret =ebt_register_template (&frame_nat, frame_nat_table_init); +int ret =register_pernet_subsys (&frame_nat_net_ops);if (ret)return ret; - ret =register_pernet_subsys (&frame_nat_net_ops); -if (ret) { -ebt_unregister_template (&frame_nat); -return ret; - } + ret =ebt_register_template (&frame_nat, frame_nat_table_init); +if (ret) +unregister_pernet_subsys (&frame_nat_net_ops);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 214/461 netfilter: x_tables: close dangling table module init race
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 16bc4b6686b2c112c10e67d6b493adc3607256d3 ]
Similar to the previous ebtables patch:
template add exposes the table to userspace, we must do this last to rnsure the pernet ops are set up (contain the destructors).
--- net/ipv4/netfilter/arptable_filter.c | 23 ++++++++++++----------- net/ipv4/netfilter/iptable_filter.c | 23 ++++++++++++----------- net/ipv4/netfilter/iptable_mangle.c | 25 +++++++++++++------------ net/ipv4/netfilter/iptable_raw.c | 22 +++++++++++----------- net/ipv4/netfilter/iptable_security.c | 23 ++++++++++++----------- net/ipv6/netfilter/ip6table_filter.c | 22 +++++++++++----------- net/ipv6/netfilter/ip6table_mangle.c | 23 ++++++++++++----------- net/ipv6/netfilter/ip6table_raw.c | 20 ++++++++++---------- net/ipv6/netfilter/ip6table_security.c | 23 ++++++++++++----------- 9 files changed, 105 insertions(+), 99 deletions(-)
net/ipv4/netfilter/arptable_filter.c +12 -11
@@ -58,25 +58,26 @@ static struct pernet_operations arptable_filter_net_ops = {static int __init arptable_filter_init (void ) { -int ret =xt_register_template (&packet_filter, - arptable_filter_table_init); - -if (ret <0 ) -return ret; +int ret; arpfilter_ops =xt_hook_ops_alloc (&packet_filter, arpt_do_table); -if (IS_ERR (arpfilter_ops)) { -xt_unregister_template (&packet_filter); +if (IS_ERR (arpfilter_ops))return PTR_ERR (arpfilter_ops); - } ret =register_pernet_subsys (&arptable_filter_net_ops); +if (ret <0 ) + goto err_free; + + ret =xt_register_template (&packet_filter, + arptable_filter_table_init);if (ret <0 ) { -xt_unregister_template (&packet_filter); -kfree (arpfilter_ops); -return ret; +unregister_pernet_subsys (&arptable_filter_net_ops); + goto err_free; } +return 0 ; +err_free: +kfree (arpfilter_ops);return ret; }
net/ipv4/netfilter/iptable_filter.c +12 -11
@@ -77,26 +77,27 @@ static struct pernet_operations iptable_filter_net_ops = {static int __init iptable_filter_init (void ) { -int ret =xt_register_template (&packet_filter, - iptable_filter_table_init); - -if (ret <0 ) -return ret; +int ret; filter_ops =xt_hook_ops_alloc (&packet_filter, ipt_do_table); -if (IS_ERR (filter_ops)) { -xt_unregister_template (&packet_filter); +if (IS_ERR (filter_ops))return PTR_ERR (filter_ops); - } ret =register_pernet_subsys (&iptable_filter_net_ops); +if (ret <0 ) + goto err_free; + + ret =xt_register_template (&packet_filter, + iptable_filter_table_init);if (ret <0 ) { -xt_unregister_template (&packet_filter); -kfree (filter_ops); -return ret; +unregister_pernet_subsys (&iptable_filter_net_ops); + goto err_free; }return 0 ; +err_free: +kfree (filter_ops); +return ret; } static void __exit iptable_filter_fini(void)
net/ipv4/netfilter/iptable_mangle.c +13 -12
@@ -111,25 +111,26 @@ static struct pernet_operations iptable_mangle_net_ops = {static int __init iptable_mangle_init (void ) { -int ret =xt_register_template (&packet_mangler, - iptable_mangle_table_init); -if (ret <0 ) -return ret; +int ret; mangle_ops =xt_hook_ops_alloc (&packet_mangler, iptable_mangle_hook); -if (IS_ERR (mangle_ops)) { -xt_unregister_template (&packet_mangler); - ret =PTR_ERR (mangle_ops); -return ret; - } +if (IS_ERR (mangle_ops)) +return PTR_ERR (mangle_ops); ret =register_pernet_subsys (&iptable_mangle_net_ops); +if (ret <0 ) + goto err_free; + + ret =xt_register_template (&packet_mangler, + iptable_mangle_table_init);if (ret <0 ) { -xt_unregister_template (&packet_mangler); -kfree (mangle_ops); -return ret; +unregister_pernet_subsys (&iptable_mangle_net_ops); + goto err_free; } +return 0 ; +err_free: +kfree (mangle_ops);return ret; }
net/ipv4/netfilter/iptable_raw.c +11 -11
@@ -77,24 +77,24 @@ static int __init iptable_raw_init(void)pr_info ("Enabling raw table before defrag\n" ); } - ret =xt_register_template (table, - iptable_raw_table_init); -if (ret <0 ) -return ret; - rawtable_ops =xt_hook_ops_alloc (table, ipt_do_table); -if (IS_ERR (rawtable_ops)) { -xt_unregister_template (table); +if (IS_ERR (rawtable_ops))return PTR_ERR (rawtable_ops); - } ret =register_pernet_subsys (&iptable_raw_net_ops); +if (ret <0 ) + goto err_free; + + ret =xt_register_template (table, + iptable_raw_table_init);if (ret <0 ) { -xt_unregister_template (table); -kfree (rawtable_ops); -return ret; +unregister_pernet_subsys (&iptable_raw_net_ops); + goto err_free; } +return 0 ; +err_free: +kfree (rawtable_ops);return ret; }
net/ipv4/netfilter/iptable_security.c +12 -11
@@ -65,25 +65,26 @@ static struct pernet_operations iptable_security_net_ops = {static int __init iptable_security_init (void ) { -int ret =xt_register_template (&security_table, - iptable_security_table_init); - -if (ret <0 ) -return ret; +int ret; sectbl_ops =xt_hook_ops_alloc (&security_table, ipt_do_table); -if (IS_ERR (sectbl_ops)) { -xt_unregister_template (&security_table); +if (IS_ERR (sectbl_ops))return PTR_ERR (sectbl_ops); - } ret =register_pernet_subsys (&iptable_security_net_ops); +if (ret <0 ) + goto err_free; + + ret =xt_register_template (&security_table, + iptable_security_table_init);if (ret <0 ) { -xt_unregister_template (&security_table); -kfree (sectbl_ops); -return ret; +unregister_pernet_subsys (&iptable_security_net_ops); + goto err_free; } +return 0 ; +err_free: +kfree (sectbl_ops);return ret; }
net/ipv6/netfilter/ip6table_filter.c +11 -11
@@ -76,25 +76,25 @@ static struct pernet_operations ip6table_filter_net_ops = {static int __init ip6table_filter_init (void ) { -int ret =xt_register_template (&packet_filter, - ip6table_filter_table_init); - -if (ret <0 ) -return ret; +int ret; filter_ops =xt_hook_ops_alloc (&packet_filter, ip6t_do_table); -if (IS_ERR (filter_ops)) { -xt_unregister_template (&packet_filter); +if (IS_ERR (filter_ops))return PTR_ERR (filter_ops); - } ret =register_pernet_subsys (&ip6table_filter_net_ops); +if (ret <0 ) + goto err_free; + + ret =xt_register_template (&packet_filter, ip6table_filter_table_init);if (ret <0 ) { -xt_unregister_template (&packet_filter); -kfree (filter_ops); -return ret; +unregister_pernet_subsys (&ip6table_filter_net_ops); + goto err_free; } +return 0 ; +err_free: +kfree (filter_ops);return ret; }
net/ipv6/netfilter/ip6table_mangle.c +12 -11
@@ -104,25 +104,26 @@ static struct pernet_operations ip6table_mangle_net_ops = {static int __init ip6table_mangle_init (void ) { -int ret =xt_register_template (&packet_mangler, - ip6table_mangle_table_init); - -if (ret <0 ) -return ret; +int ret; mangle_ops =xt_hook_ops_alloc (&packet_mangler, ip6table_mangle_hook); -if (IS_ERR (mangle_ops)) { -xt_unregister_template (&packet_mangler); +if (IS_ERR (mangle_ops))return PTR_ERR (mangle_ops); - } ret =register_pernet_subsys (&ip6table_mangle_net_ops); +if (ret <0 ) + goto err_free; + + ret =xt_register_template (&packet_mangler, + ip6table_mangle_table_init);if (ret <0 ) { -xt_unregister_template (&packet_mangler); -kfree (mangle_ops); -return ret; +unregister_pernet_subsys (&ip6table_mangle_net_ops); + goto err_free; } +return 0 ; +err_free: +kfree (mangle_ops);return ret; }
net/ipv6/netfilter/ip6table_raw.c +10 -10
@@ -75,24 +75,24 @@ static int __init ip6table_raw_init(void)pr_info ("Enabling raw table before defrag\n" ); } - ret =xt_register_template (table, ip6table_raw_table_init); -if (ret <0 ) -return ret; -/* Register hooks */ rawtable_ops =xt_hook_ops_alloc (table, ip6t_do_table); -if (IS_ERR (rawtable_ops)) { -xt_unregister_template (table); +if (IS_ERR (rawtable_ops))return PTR_ERR (rawtable_ops); - } ret =register_pernet_subsys (&ip6table_raw_net_ops); +if (ret <0 ) + goto err_free; + + ret =xt_register_template (table, ip6table_raw_table_init);if (ret <0 ) { -kfree (rawtable_ops); -xt_unregister_template (table); -return ret; +unregister_pernet_subsys (&ip6table_raw_net_ops); + goto err_free; } +return 0 ; +err_free: +kfree (rawtable_ops);return ret; }
net/ipv6/netfilter/ip6table_security.c +12 -11
@@ -64,25 +64,26 @@ static struct pernet_operations ip6table_security_net_ops = {static int __init ip6table_security_init (void ) { -int ret =xt_register_template (&security_table, - ip6table_security_table_init); - -if (ret <0 ) -return ret; +int ret; sectbl_ops =xt_hook_ops_alloc (&security_table, ip6t_do_table); -if (IS_ERR (sectbl_ops)) { -xt_unregister_template (&security_table); +if (IS_ERR (sectbl_ops))return PTR_ERR (sectbl_ops); - } ret =register_pernet_subsys (&ip6table_security_net_ops); +if (ret <0 ) + goto err_free; + + ret =xt_register_template (&security_table, + ip6table_security_table_init);if (ret <0 ) { -kfree (sectbl_ops); -xt_unregister_template (&security_table); -return ret; +unregister_pernet_subsys (&ip6table_security_net_ops); + goto err_free; } +return 0 ; +err_free: +kfree (sectbl_ops);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 215/461 netfilter: bridge: eb_tables: close module init race
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 27414ff1b287ea9a2a11675149ec28e05539f3cc ]
sashiko reports for unrelated patch:
Does the core ebtables initialization in ebtables.c suffer from a similar race? Once nf_register_sockopt() completes, the sockopts are exposed globally.
sockopt has to be registered last, just like in ip/ip6/arptables.
--- net/bridge/netfilter/ebtables.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
net/bridge/netfilter/ebtables.c +6 -5
@@ -2583,19 +2583,20 @@ static int __init ebtables_init(void) {int ret; - ret =xt_register_target (&ebt_standard_target); + ret =register_pernet_subsys (&ebt_net_ops);if (ret <0 )return ret; - ret =nf_register_sockopt (&ebt_sockopts); + + ret =xt_register_target (&ebt_standard_target);if (ret <0 ) { -xt_unregister_target (&ebt_standard_target); +unregister_pernet_subsys (&ebt_net_ops);return ret; } - ret =register_pernet_subsys (&ebt_net_ops); + ret =nf_register_sockopt (&ebt_sockopts);if (ret <0 ) { -nf_unregister_sockopt (&ebt_sockopts);xt_unregister_target (&ebt_standard_target); +unregister_pernet_subsys (&ebt_net_ops);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 216/461 netfilter: nf_conntrack_expect: restore helper propagation via expectation
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit dcb0f9aefdd604d36710fda53c25bd7cf4a3e37a ]
A recent series to fix expectations broke helper propagation via expectation, this mechanism is used by the sip and h323 helper. This also propagates the conntrack helper to expected connections. I changed semantics of exp->helper which now tells us the actual helper that created the expectation.
Add an explicit assign_helper field to expectations for this purpose and update helpers to use it.
Restore this feature for userspace conntrack helper via ctnetlink nfqueue integration so it is again possible to attach a helper to an expectation, where it makes sense. This is not restored via ctnetlink expectation creation as there is no client for such feature. Use the expectation layer 4 protocol number for the helper lookup for consistency.
Make sure the expectation using this helper propagation mechanism also go away when the helper is unregistered.
--- include/net/netfilter/nf_conntrack_expect.h | 5 ++++- net/netfilter/nf_conntrack_broadcast.c | 1 + net/netfilter/nf_conntrack_core.c | 7 +++++-- net/netfilter/nf_conntrack_expect.c | 1 + net/netfilter/nf_conntrack_h323_main.c | 12 ++++++------ net/netfilter/nf_conntrack_helper.c | 5 +++++ net/netfilter/nf_conntrack_netlink.c | 18 ++++++++++++++++-- net/netfilter/nf_conntrack_sip.c | 2 +- 8 files changed, 39 insertions(+), 12 deletions(-)
include/net/netfilter/nf_conntrack_expect.h +4 -1
@@ -45,9 +45,12 @@ struct nf_conntrack_expect {void (*expectfn)(struct nf_conn *new,struct nf_conntrack_expect *this); -/* Helper to assign to new connection */ +/* Helper that created this expectation */ struct nf_conntrack_helper __rcu *helper; +/* Helper to assign to new connection */ +struct nf_conntrack_helper __rcu *assign_helper; +/* The conntrack of the master connection */ struct nf_conn *master;
net/netfilter/nf_conntrack_broadcast.c +1 -0
@@ -72,6 +72,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, exp->flags =NF_CT_EXPECT_PERMANENT ; exp->class =NF_CT_EXPECT_CLASS_DEFAULT ;rcu_assign_pointer (exp->helper, helper); +rcu_assign_pointer (exp->assign_helper,NULL );write_pnet (&exp->net, net);#ifdef CONFIG_NF_CONNTRACK_ZONES exp->zone = ct->zone;
net/netfilter/nf_conntrack_core.c +5 -2
@@ -1814,14 +1814,17 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,spin_lock_bh (&nf_conntrack_expect_lock); exp =nf_ct_find_expectation (net, zone, tuple, !tmpl ||nf_ct_is_confirmed (tmpl));if (exp) { +struct nf_conntrack_helper *assign_helper; +/* Welcome, Mr. Bond. We've been expecting you... */ __set_bit (IPS_EXPECTED_BIT , &ct->status);/* exp->master safe, refcnt bumped in nf_ct_find_expectation */ ct->master = exp->master; -if (exp->helper) { + assign_helper =rcu_dereference (exp->assign_helper); +if (assign_helper) { help =nf_ct_helper_ext_add (ct,GFP_ATOMIC );if (help) -rcu_assign_pointer (help->helper, exp->helper); +rcu_assign_pointer (help->helper, assign_helper); }#ifdef CONFIG_NF_CONNTRACK_MARK
net/netfilter/nf_conntrack_expect.c +1 -0
@@ -344,6 +344,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, helper =rcu_dereference (help->helper);rcu_assign_pointer (exp->helper, helper); +rcu_assign_pointer (exp->assign_helper,NULL );write_pnet (&exp->net, net);#ifdef CONFIG_NF_CONNTRACK_ZONES exp->zone = ct->zone;
net/netfilter/nf_conntrack_h323_main.c +6 -6
@@ -643,7 +643,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); -rcu_assign_pointer (exp->helper, &nf_conntrack_helper_h245); +rcu_assign_pointer (exp->assign_helper, &nf_conntrack_helper_h245); nathook =rcu_dereference (nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -767,7 +767,7 @@ static int expect_callforwarding(struct sk_buff *skb,nf_ct_expect_init (exp,NF_CT_EXPECT_CLASS_DEFAULT ,nf_ct_l3num (ct), &ct->tuplehash[!dir].tuple.src.u3, &addr,IPPROTO_TCP ,NULL , &port); -rcu_assign_pointer (exp->helper, nf_conntrack_helper_q931); +rcu_assign_pointer (exp->assign_helper, nf_conntrack_helper_q931); nathook =rcu_dereference (nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -1234,7 +1234,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3 : NULL, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); -rcu_assign_pointer (exp->helper, nf_conntrack_helper_q931); +rcu_assign_pointer (exp->assign_helper, nf_conntrack_helper_q931); exp->flags =NF_CT_EXPECT_PERMANENT ;/* Accept multiple calls */ nathook =rcu_dereference (nfct_h323_nat_hook);
@@ -1306,7 +1306,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct,nf_ct_expect_init (exp,NF_CT_EXPECT_CLASS_DEFAULT ,nf_ct_l3num (ct), &ct->tuplehash[!dir].tuple.src.u3, &addr,IPPROTO_UDP ,NULL , &port); -rcu_assign_pointer (exp->helper, nf_conntrack_helper_ras); +rcu_assign_pointer (exp->assign_helper, nf_conntrack_helper_ras);if (nf_ct_expect_related (exp,0 ) ==0 ) {pr_debug ("nf_ct_ras: expect RAS " );
@@ -1523,7 +1523,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags =NF_CT_EXPECT_PERMANENT ; -rcu_assign_pointer (exp->helper, nf_conntrack_helper_q931); +rcu_assign_pointer (exp->assign_helper, nf_conntrack_helper_q931);if (nf_ct_expect_related (exp,0 ) ==0 ) {pr_debug ("nf_ct_ras: expect Q.931 " );
@@ -1577,7 +1577,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags =NF_CT_EXPECT_PERMANENT ; -rcu_assign_pointer (exp->helper, nf_conntrack_helper_q931); +rcu_assign_pointer (exp->assign_helper, nf_conntrack_helper_q931);if (nf_ct_expect_related (exp,0 ) ==0 ) {pr_debug ("nf_ct_ras: expect Q.931 " );
net/netfilter/nf_conntrack_helper.c +5 -0
@@ -400,6 +400,11 @@ static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data) this =rcu_dereference_protected (exp->helper,lockdep_is_held (&nf_conntrack_expect_lock)); +if (this == me) +return true; + + this =rcu_dereference_protected (exp->assign_helper, +lockdep_is_held (&nf_conntrack_expect_lock));return this == me; }
net/netfilter/nf_conntrack_netlink.c +16 -2
@@ -2636,6 +2636,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {static struct nf_conntrack_expect *ctnetlink_alloc_expect (const struct nlattr *const cda[],struct nf_conn *ct, +const struct nf_conntrack_helper *assign_helper,struct nf_conntrack_tuple *tuple,struct nf_conntrack_tuple *mask);
@@ -2862,6 +2863,7 @@ static int ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, u32 portid, u32 report) { + struct nf_conntrack_helper *assign_helper = NULL;struct nlattr *cda[CTA_EXPECT_MAX +1 ];struct nf_conntrack_tuple tuple, mask;struct nf_conntrack_expect *exp;
@@ -2877,8 +2879,18 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,if (err <0 )return err; +if (cda[CTA_EXPECT_HELP_NAME ]) { +const char *helpname =nla_data (cda[CTA_EXPECT_HELP_NAME ]); + + assign_helper =__nf_conntrack_helper_find (helpname, +nf_ct_l3num (ct), + tuple.dst.protonum); +if (!assign_helper) +return -EOPNOTSUPP ; + } + exp =ctnetlink_alloc_expect ((const struct nlattr *const *)cda, ct, - &tuple, &mask); + assign_helper, &tuple, &mask);if (IS_ERR (exp))return PTR_ERR (exp);
@@ -3517,6 +3529,7 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr,static struct nf_conntrack_expect *ctnetlink_alloc_expect (const struct nlattr *const cda[],struct nf_conn *ct, +const struct nf_conntrack_helper *assign_helper,struct nf_conntrack_tuple *tuple,struct nf_conntrack_tuple *mask) {
@@ -3570,6 +3583,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, exp->zone = ct->zone;#endif rcu_assign_pointer (exp->helper, helper); +rcu_assign_pointer (exp->assign_helper, assign_helper); exp->tuple = *tuple; exp->mask.src.u3 = mask->src.u3; exp->mask.src.u.all = mask->src.u.all;
@@ -3625,7 +3639,7 @@ ctnetlink_create_expect(struct net *net, ct =nf_ct_tuplehash_to_ctrack (h);rcu_read_lock (); - exp =ctnetlink_alloc_expect (cda, ct, &tuple, &mask); + exp =ctnetlink_alloc_expect (cda, ct,NULL , &tuple, &mask);if (IS_ERR (exp)) { err =PTR_ERR (exp); goto err_rcu;
net/netfilter/nf_conntrack_sip.c +1 -1
@@ -1384,7 +1384,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,nf_ct_expect_init (exp,SIP_EXPECT_SIGNALLING ,nf_ct_l3num (ct), saddr, &daddr, proto,NULL , &port); exp->timeout.expires = sip_timeout *HZ ; -rcu_assign_pointer (exp->helper, helper); +rcu_assign_pointer (exp->assign_helper, helper); exp->flags =NF_CT_EXPECT_PERMANENT |NF_CT_EXPECT_INACTIVE ; hooks =rcu_dereference (nf_nat_sip_hooks);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 217/461 kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jianpeng Chang <[email protected]>
[ Upstream commit 307abfac04a254c09c5705d816b33354acee97a0 ]
When kprobe_add_area_blacklist() iterates through a section like .kprobes.text, the start address may not correspond to a named symbol. On ARM64 with CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS=y (introduced by commit baaf553d3bc3 ("arm64: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")), the compiler flag -fpatchable-function-entry=4,2 inserts 2 NOPs before each function entry point for ftrace call_ops. These pre-function NOPs sit at the section base address, before the first named function symbol. The compiler emits a $x mapping symbol at offset 0x00 to mark the start of code, but find_kallsyms_symbol() ignores mapping symbols.
Without CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS (e.g. defconfig), no pre-function NOPs are inserted, the first function starts at offset 0x00, and the bug does not trigger.
This only affects modules that have a .kprobes.text section (i.e. those using the __kprobes annotation). Modules using NOKPROBE_SYMBOL() instead (like kretprobe_example.ko) blacklist exact function addresses via the _kprobe_blacklist section and are not affected.
For kprobe_example.ko on ARM64 with -fpatchable-function-entry=4,2, the .kprobes.text section layout is:
offset 0x00: $x + 2 NOPs (mapping symbol + ftrace preamble) offset 0x08: handler_post (64 bytes) offset 0x50: handler_pre (68 bytes)
kprobe_add_area_blacklist() starts iterating from the section base address (offset 0x00), which only has the $x mapping symbol. kprobe_add_ksym_blacklist() then calls kallsyms_lookup_size_offset() for this address, which goes through:
kallsyms_lookup_size_offset()
-> module_address_lookup()
-> find_kallsyms_symbol()find_kallsyms_symbol() scans all module symbols to find the closest preceding symbol.
Since no named text symbol exists at offset 0x00, find_kallsyms_symbol() picks __UNIQUE_ID_vermagic (a .modinfo symbol whose address is in the temporary image) as the "best" match. The computed "size" = next_text_symbol - modinfo_symbol spans across these two unrelated memory regions, creating a blacklist entry with a bogus range of tens of terabytes.
Whether this causes a visible failure depends on address randomization, here is what happens on Raspberry Pi 4/5:
- On RPi5, the bogus size was ~35 TB. start + size stayed within 64-bit range, so the blacklist entry covered the entire kernel text. register_kprobe() in the module's own init function failed with -EINVAL.
- On RPi4, the bogus size was ~75 TB. start + size overflowed 64 bits and wrapped to a small address near zero. The range check (addr >= start && addr < end) then failed because end wrapped around, so the bogus entry was accidentally harmless and kprobes worked by luck.
The same bug exists on both machines, but randomization determines whether the integer overflow masks it or not.
Fix this by adding notrace to the __kprobes macro. Functions in .kprobes.text are kprobe infrastructure handlers that should never be traced by ftrace. With notrace, the compiler stops inserting them and the non-symbol gap at the section start disappears entirely.
--- include/asm-generic/kprobes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/asm-generic/kprobes.h +1 -1
@@ -14,7 +14,7 @@ static unsigned long __used \ _kbl_addr_##fname = (unsigned long)fname;# define NOKPROBE_SYMBOL (fname) __NOKPROBE_SYMBOL(fname)/* Use this to forbid a kprobes attach on very low level functions */ -# define __kprobes __section(".kprobes.text") +# define __kprobes notrace __section(".kprobes.text")# define nokprobe_inline __always_inline#else # define NOKPROBE_SYMBOL (fname)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 218/461 test_kprobes: clear kprobes between test runs
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Martin Kaiser <[email protected]>
[ Upstream commit ef5581bb30efb939cc2bf093475c6cc85258e5cd ]
Running the kprobes sanity tests twice makes all tests fail and eventually crashes the kernel.
[root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run
...
# Totals: pass:5 fail:0 skip:0 total:5
ok 1 kprobes_test
[root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run
...
# test_kprobe: EXPECTATION FAILED at lib/tests/test_kprobes.c:64 Expected 0 == register_kprobe(&kp), but
register_kprobe(&kp) == -22 (0xffffffffffffffea) ... Unable to handle kernel paging request ...
The testsuite defines several kprobes and kretprobes as static variables that are preserved across test runs.
After register_kprobe and unregister_kprobe, a kprobe contains some leftover data that must be cleared before the kprobe can be registered again. The tests are setting symbol_name to define the probe location. Address and flags must be cleared.
The existing code clears some of the probes between subsequent tests, but not between two test runs. The leftover data from a previous test run makes the registrations fail in the next run.
Move the cleanups for all kprobes into kprobes_test_init, this function is called before each single test (including the first test of a test run).
--- lib/tests/test_kprobes.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-)
lib/tests/test_kprobes.c +18 -11
@@ -12,6 +12,12 @@#define div_factor 3 +#define KP_CLEAR (_kp) \ +do { \ + (_kp).addr = NULL; \ + (_kp).flags = 0; \ +} while (0) +static u32 rand1, preh_val, posth_val;static u32 (*target)(u32 value);static u32 (*recursed_target)(u32 value);
@@ -125,10 +131,6 @@ static void test_kprobes(struct kunit *test) current_test = test; -/* addr and flags should be cleard for reusing kprobe. */ - kp.addr =NULL ; - kp.flags =0 ; -KUNIT_EXPECT_EQ (test,0 ,register_kprobes (kps,2 )); preh_val =0 ; posth_val =0 ;
@@ -226,9 +228,6 @@ static void test_kretprobes(struct kunit *test)struct kretprobe *rps[2 ] = {&rp, &rp2}; current_test = test; -/* addr and flags should be cleard for reusing kprobe. */ - rp.kp.addr =NULL ; - rp.kp.flags =0 ;KUNIT_EXPECT_EQ (test,0 ,register_kretprobes (rps,2 )); krph_val =0 ;
@@ -290,8 +289,6 @@ static void test_stacktrace_on_kretprobe(struct kunit *test)unsigned long myretaddr = (unsigned long )__builtin_return_address (0 ); current_test = test; - rp3.kp.addr =NULL ; - rp3.kp.flags =0 ;/* * Run the stacktrace_driver() to record correct return address in
@@ -352,8 +349,6 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test)struct kretprobe *rps[2 ] = {&rp3, &rp4}; current_test = test; - rp3.kp.addr =NULL ; - rp3.kp.flags =0 ;//KUNIT_ASSERT_NE(test, myretaddr, stacktrace_driver());
@@ -367,6 +362,18 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test)static int kprobes_test_init (struct kunit *test) { +KP_CLEAR (kp); +KP_CLEAR (kp2); +KP_CLEAR (kp_missed); +#ifdef CONFIG_KRETPROBES +KP_CLEAR (rp.kp); +KP_CLEAR (rp2.kp); +#ifdef CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE +KP_CLEAR (rp3.kp); +KP_CLEAR (rp4.kp); +#endif +#endif + target = kprobe_target; target2 = kprobe_target2; recursed_target = kprobe_recursed_target;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 219/461 tcp: Fix imbalanced icsk_accept_queue count.
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 7eca3292cac7c26dad4c236f51ba225c39a0523f ]
When TCP socket migration happens in reqsk_timer_handler(), @sk_listener will be updated with the new listener.
When we call __inet_csk_reqsk_queue_drop(), the listener must be the one stored in req->rsk_listener.
The cited commit accidentally replaced oreq->rsk_listener with sk_listener, leading to imbalanced icsk_accept_queue count.
Let's pass the correct listener to __inet_csk_reqsk_queue_drop().
--- net/ipv4/inet_connection_sock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/inet_connection_sock.c +1 -1
@@ -1137,7 +1137,7 @@ static void reqsk_timer_handler(struct timer_list *t) } drop: -__inet_csk_reqsk_queue_drop (sk_listener, oreq, true); +__inet_csk_reqsk_queue_drop (oreq->rsk_listener, oreq, true);reqsk_put (oreq); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 220/461 net: napi: Avoid gro timer misfiring at end of busypoll
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dragos Tatulea <[email protected]>
[ Upstream commit 58e2330bd45572a6e3d46ea94cf7a9641f43591a ]
When in irq deferral mode (defer-hard-irqs > 0), a short enough gro-flush timeout can trigger before NAPI_STATE_SCHED is cleared if the last poll in busy_poll_stop() takes too long. This can have the effect of leaving the queue stuck with interrupts disabled and no timer armed which results in a tx timeout if there is no subsequent busypoll cycle.
To prevent this, defer the gro-flush timer arm after the last poll.
--- net/core/dev.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-)
net/core/dev.c +12 -9
@@ -6855,9 +6855,9 @@ static void skb_defer_free_flush(void)#if defined(CONFIG_NET_RX_BUSY_POLL ) -static void __busy_poll_stop (struct napi_struct *napi,bool skip_schedule) +static void __busy_poll_stop (struct napi_struct *napi,unsigned long timeout) { -if (!skip_schedule) { +if (!timeout) {gro_normal_list (&napi->gro);__napi_schedule (napi);return ;
@@ -6867,6 +6867,8 @@ static void __busy_poll_stop(struct napi_struct *napi, bool skip_schedule)gro_flush_normal (&napi->gro,HZ >=1000 );clear_bit (NAPI_STATE_SCHED , &napi->state); +hrtimer_start (&napi->timer,ns_to_ktime (timeout), +HRTIMER_MODE_REL_PINNED ); }enum {
@@ -6878,8 +6880,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, unsigned flags, u16 budget) {struct bpf_net_context __bpf_net_ctx, *bpf_net_ctx; -bool skip_schedule = false; -unsigned long timeout; +unsigned long timeout =0 ;int rc;/* Busy polling means there is a high chance device driver hard irq
@@ -6899,10 +6900,12 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock,if (flags &NAPI_F_PREFER_BUSY_POLL ) { napi->defer_hard_irqs_count =napi_get_defer_hard_irqs (napi); - timeout =napi_get_gro_flush_timeout (napi); -if (napi->defer_hard_irqs_count && timeout) { -hrtimer_start (&napi->timer,ns_to_ktime (timeout),HRTIMER_MODE_REL_PINNED ); - skip_schedule = true; +if (napi->defer_hard_irqs_count) { +/* A short enough gro flush timeout and long enough +* poll can result in timer firing too early. +* Timer will be armed later if necessary. +*/ + timeout =napi_get_gro_flush_timeout (napi); } }
@@ -6917,7 +6920,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock,trace_napi_poll (napi, rc, budget);netpoll_poll_unlock (have_poll_lock);if (rc == budget) -__busy_poll_stop (napi, skip_schedule); +__busy_poll_stop (napi, timeout);bpf_net_ctx_clear (bpf_net_ctx);local_bh_enable (); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 221/461 net: shaper: Reject reparenting of existing nodes
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mohsin Bashir <[email protected]>
[ Upstream commit a77d5a069d959dc45f5f472d48cba37d8cba0f1c ]
When an existing node-scope shaper is moved to a different parent via the group operation, the framework fails to update the leaves count on both the old and new parent shapers. Only newly created nodes (handle.id == NET_SHAPER_ID_UNSPEC) trigger the parent leaves increment at line 1039.
This causes the parent's leaves counter to diverge from the actual number of children in the xarray. When the node is later deleted, pre_del_node() allocates an array sized by the stale leaves count, but the xarray iteration finds more children than expected, hitting the WARN_ON_ONCE guard and returning -EINVAL.
Rather than adding reparenting support with complex leaves count bookkeeping, reject group calls that attempt to change an existing node's parent. Updates to an existing node's rate or leaves under the same parent remain permitted. We expect that for any modification of the topology user should always create new groups and let the kernel garbage collect the leaf-less nodes.
--- net/shaper/shaper.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-)
net/shaper/shaper.c +23 -7
@@ -964,15 +964,22 @@ static int __net_shaper_group(struct net_shaper_binding *binding,int i, ret;if (node->handle.scope ==NET_SHAPER_SCOPE_NODE ) { +struct net_shaper *cur =NULL ; + new_node = node->handle.id ==NET_SHAPER_ID_UNSPEC ; -if (!new_node && !net_shaper_lookup (binding, &node->handle)) { -/* The related attribute is not available when -* reaching here from the delete() op. -*/ -NL_SET_ERR_MSG_FMT (extack,"Node shaper %d:%d does not exists" , - node->handle.scope, node->handle.id); -return -ENOENT ; +if (!new_node) { + cur =net_shaper_lookup (binding, &node->handle); +if (!cur) { +/* The related attribute is not available +* when reaching here from the delete() op. +*/ +NL_SET_ERR_MSG_FMT (extack, +"Node shaper %d:%d does not exist" , + node->handle.scope, + node->handle.id); +return -ENOENT ; + } }/* When unspecified, the node parent scope is inherited from
@@ -986,6 +993,15 @@ static int __net_shaper_group(struct net_shaper_binding *binding,return ret; } +if (cur &&net_shaper_handle_cmp (&cur->parent, + &node->parent)) { +NL_SET_ERR_MSG_FMT (extack, +"Cannot reparent node shaper %d:%d" , + node->handle.scope, + node->handle.id); +return -EOPNOTSUPP ; + } + }else {net_shaper_default_parent (&node->handle, &node->parent); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 222/461 idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Emil Tantilov <[email protected]>
[ Upstream commit da4f76b6a84ede14a71282ef841768299ead0221 ]
In idpf_ptp_init(), read_dev_clk_lock is initialized after ptp_schedule_worker() had already been called (and after idpf_ptp_settime64() could reach the lock). The PTP aux worker fires immediately upon scheduling and can call into idpf_ptp_read_src_clk_reg_direct(), which takes spin_lock(&ptp->read_dev_clk_lock) on an uninitialized lock, triggering the lockdep "non-static key" warning:
[12973.796587] idpf 0000:83:00.0: Device HW Reset initiated [12974.094507] INFO: trying to register non-static key. ... [12974.097208] Call Trace: [12974.097213] <TASK> [12974.097218] dump_stack_lvl+0x93/0xe0 [12974.097234] register_lock_class+0x4c4/0x4e0 [12974.097249] ? __lock_acquire+0x427/0x2290 [12974.097259] __lock_acquire+0x98/0x2290 [12974.097272] lock_acquire+0xc6/0x310 [12974.097281] ? idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] [12974.097311] ? lockdep_hardirqs_on_prepare+0xde/0x190 [12974.097318] ? finish_task_switch.isra.0+0xd2/0x350 [12974.097330] ? __pfx_ptp_aux_kworker+0x10/0x10 [ptp] [12974.097343] _raw_spin_lock+0x30/0x40 [12974.097353] ? idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] [12974.097373] idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] [12974.097391] ? kthread_worker_fn+0x88/0x3d0 [12974.097404] ? kthread_worker_fn+0x4e/0x3d0 [12974.097411] idpf_ptp_update_cached_phctime+0x26/0x120 [idpf] [12974.097428] ? _raw_spin_unlock_irq+0x28/0x50 [12974.097436] idpf_ptp_do_aux_work+0x15/0x20 [idpf] [12974.097454] ptp_aux_kworker+0x20/0x40 [ptp] [12974.097464] kthread_worker_fn+0xd5/0x3d0 [12974.097474] ? __pfx_kthread_worker_fn+0x10/0x10 [12974.097482] kthread+0xf4/0x130 [12974.097489] ? __pfx_kthread+0x10/0x10 [12974.097498] ret_from_fork+0x32c/0x410 [12974.097512] ? __pfx_kthread+0x10/0x10 [12974.097519] ret_from_fork_asm+0x1a/0x30 [12974.097540] </TASK>
Move the call to spin_lock_init() up a bit to make sure read_dev_clk_lock is not touched before it's been initialized.
--- drivers/net/ethernet/intel/idpf/idpf_ptp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_ptp.c +2 -2
@@ -952,6 +952,8 @@ int idpf_ptp_init(struct idpf_adapter *adapter) goto free_ptp; } +spin_lock_init (&adapter->ptp->read_dev_clk_lock); + err =idpf_ptp_create_clock (adapter);if (err) goto free_ptp;
@@ -977,8 +979,6 @@ int idpf_ptp_init(struct idpf_adapter *adapter) goto remove_clock; } -spin_lock_init (&adapter->ptp->read_dev_clk_lock); -pci_dbg (adapter->pdev,"PTP init successful\n" );return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 223/461 ice: fix setting RSS VSI hash for E830
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marcin Szycik <[email protected]>
[ Upstream commit b3cda96feb60d91fe88d52b974ff110dcfa91239 ]
ice_set_rss_hfunc() performs a VSI update, in which it sets hashing function, leaving other VSI options unchanged. However, ::q_opt_flags is mistakenly set to the value of another field, instead of its original value, probably due to a typo. What happens next is hardware-dependent:
On E810, only the first bit is meaningful (see ICE_AQ_VSI_Q_OPT_PE_FLTR_EN) and can potentially end up in a different state than before VSI update.
On E830, some of the remaining bits are not reserved. Setting them to some unrelated values can cause the firmware to reject the update because of invalid settings, or worse - succeed.
Reproducer:
sudo ethtool -X $PF1 equal 8
Output in dmesg:
Failed to configure RSS hash for VSI 6, error -5
--- drivers/net/ethernet/intel/ice/ice_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/intel/ice/ice_main.c +1 -1
@@ -8052,7 +8052,7 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc) ctx->info.q_opt_rss |=FIELD_PREP (ICE_AQ_VSI_Q_OPT_RSS_HASH_M , hfunc); ctx->info.q_opt_tc = vsi->info.q_opt_tc; - ctx->info.q_opt_flags = vsi->info.q_opt_rss; + ctx->info.q_opt_flags = vsi->info.q_opt_flags; err =ice_update_vsi (hw, vsi->idx, ctx,NULL );if (err) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 224/461 ice: fix locking in ice_dcb_rebuild()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bart Van Assche <[email protected]>
[ Upstream commit 0ded1f36ba4021cba50513e80be6b6e173710168 ]
Move the mutex_lock() call up to prevent that DCB settings change after the first ice_query_port_ets() call. The second ice_query_port_ets() call in ice_dcb_rebuild() is already protected by pf->tc_mutex.
This also fixes a bug in an error path, as before taking the first "goto dcb_error" in the function jumped over mutex_lock() to mutex_unlock().
This bug has been detected by the clang thread-safety analyzer.
--- drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/intel/ice/ice_dcb_lib.c +2 -2
@@ -537,14 +537,14 @@ void ice_dcb_rebuild(struct ice_pf *pf)struct ice_dcbx_cfg *err_cfg;int ret; +mutex_lock (&pf->tc_mutex); + ret =ice_query_port_ets (pf->hw.port_info, &buf,sizeof (buf),NULL );if (ret) {dev_err (dev,"Query Port ETS failed\n" ); goto dcb_error; } -mutex_lock (&pf->tc_mutex); -if (!pf->hw.port_info->qos_cfg.is_sw_lldp)ice_cfg_etsrec_defaults (pf->hw.port_info);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 225/461 ice: dpll: fix rclk pin state get for E810
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Vecera <[email protected]>
[ Upstream commit cce709d8df6ba6d2a0a0dbf34acc2cdd9e23bd46 ]
The refactoring of ice_dpll_rclk_state_on_pin_get() to use ice_dpll_pin_get_parent_idx() omitted the base_rclk_idx adjustment that was correctly added in the ice_dpll_rclk_state_on_pin_set() path. This breaks E810 devices where base_rclk_idx is non-zero, causing the wrong hardware index to be used for pin state lookup and incorrect recovered clock state to be reported via the DPLL subsystem. E825C is unaffected as its base_rclk_idx is 0.
While at it, add bounds check against ICE_DPLL_RCLK_NUM_MAX on hw_idx after the base_rclk_idx subtraction in both ice_dpll_rclk_state_on_pin_{get,set}() to prevent out-of-bounds access on the pin state array.
--- drivers/net/ethernet/intel/ice/ice_dpll.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/net/ethernet/intel/ice/ice_dpll.c +5 -0
@@ -2523,6 +2523,8 @@ ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv,if (hw_idx <0 ) goto unlock; hw_idx -= pf->dplls.base_rclk_idx; +if (hw_idx >=ICE_DPLL_RCLK_NUM_MAX ) + goto unlock;if ((enable && p->state[hw_idx] ==DPLL_PIN_STATE_CONNECTED ) || (!enable && p->state[hw_idx] ==DPLL_PIN_STATE_DISCONNECTED )) {
@@ -2586,6 +2588,9 @@ ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv, hw_idx =ice_dpll_pin_get_parent_idx (p, parent_pin);if (hw_idx <0 ) goto unlock; + hw_idx -= pf->dplls.base_rclk_idx; +if (hw_idx >=ICE_DPLL_RCLK_NUM_MAX ) + goto unlock; ret =ice_dpll_pin_state_update (pf, p,ICE_DPLL_PIN_TYPE_RCLK_INPUT , extack);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 226/461 ice: dpll: fix misplaced header macros
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Vecera <[email protected]>
[ Upstream commit 30f1658fc5387384c7a60b9d15c79cb959512c1a ]
The CGU register definitions (ICE_CGU_R10, ICE_CGU_R11 and related field masks) were placed after the #endif of the _ICE_DPLL_H_ include guard, leaving them unprotected. Move them inside the guard.
--- drivers/net/ethernet/intel/ice/ice_dpll.h | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-)
drivers/net/ethernet/intel/ice/ice_dpll.h +16 -16
@@ -8,6 +8,22 @@#define ICE_DPLL_RCLK_NUM_MAX 4 +#define ICE_CGU_R10 0x28 +#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5) +#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9) +#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14) +#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15) +#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16) +#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19) +#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24) +#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25) +#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27) + +#define ICE_CGU_R11 0x2C +#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1) + +#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3 +/** * enum ice_dpll_pin_sw - enumerate ice software pin indices: * @ICE_DPLL_PIN_SW_1_IDX: index of first SW pin
@@ -157,19 +173,3 @@ static inline void ice_dpll_deinit(struct ice_pf *pf) { }#endif #endif - -#define ICE_CGU_R10 0x28 -#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5) -#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9) -#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14) -#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15) -#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16) -#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19) -#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24) -#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25) -#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27) - -#define ICE_CGU_R11 0x2C -#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1) - -#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 227/461 net: lan966x: avoid unregistering netdev on register failure
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
[ Upstream commit c4f3d6eb1fcf6cd9ce4644f604d5aad1ce594dfc ]
lan966x_probe_port() stores the newly allocated net_device in the port before calling register_netdev(). If register_netdev() fails, the probe error path calls lan966x_cleanup_ports(), which sees port->dev and calls unregister_netdev() for a device that was never registered.
Destroy the phylink instance created for this port and clear port->dev before returning the registration error. The common cleanup path now skips ports without port->dev before reaching the registered netdev cleanup, so it only handles ports that reached the registered-netdev lifetime.
This also avoids treating an uninitialized FDMA netdev and the failed port as a NULL == NULL match in the common cleanup path.
--- drivers/net/ethernet/microchip/lan966x/lan966x_main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/net/ethernet/microchip/lan966x/lan966x_main.c +5 -3
@@ -749,11 +749,10 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x)for (p =0 ; p < lan966x->num_phys_ports; p++) { port = lan966x->ports[p]; -if (!port) +if (!port || !port->dev)continue ; -if (port->dev) -unregister_netdev (port->dev); +unregister_netdev (port->dev);lan966x_xdp_port_deinit (port);if (lan966x->fdma && lan966x->fdma_ndev == port->dev)
@@ -873,6 +872,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, err =register_netdev (dev);if (err) {dev_err (lan966x->dev,"register_netdev failed\n" ); +phylink_destroy (phylink); + port->phylink =NULL ; + port->dev =NULL ;return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 228/461 net: ti: icssm-prueth: fix eth_ports_node leak in probe
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shitalkumar Gandhi <[email protected]>
[ Upstream commit 6635fa84403c3a59455b66007c019a7cc632db30 ]
The error path on of_property_read_u32() failure inside icssm_prueth_probe() returns without putting eth_ports_node, which was acquired before the for_each_child_of_node() loop.
Drop it before returning.
--- drivers/net/ethernet/ti/icssm/icssm_prueth.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/ti/icssm/icssm_prueth.c +1 -0
@@ -1825,6 +1825,7 @@ static int icssm_prueth_probe(struct platform_device *pdev)dev_err (dev,"%pOF error reading port_id %d\n" , eth_node, ret);of_node_put (eth_node); +of_node_put (eth_ports_node);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 229/461 phy: marvell: mvebu-a3700-utmi: fix incorrect USB2_PHY_CTRL register access
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gabor Juhos <[email protected]>
[ Upstream commit 91ddf6f722084383fb05be731c0107814b055c0c ]
The mvebu_a3700_utmi_phy_power_off() function tries to modify the USB2_PHY_CTRL register by using the IO address of the PHY IP block along with the readl/writel IO accessors. However, the register exist in the USB miscellaneous register space, and as such it must be accessed via regmap like it is done in the mvebu_a3700_utmi_phy_power_on() function.
Change the code to use regmap_update_bits() for modífying the register to fix this.
--- drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/phy/marvell/phy-mvebu-a3700-utmi.c +2 -3
@@ -168,9 +168,8 @@ static int mvebu_a3700_utmi_phy_power_off(struct phy *phy)u32 reg;/* Disable PHY pull-up and enable USB2 suspend */ - reg =readl (utmi->regs +USB2_PHY_CTRL (usb32)); - reg &= ~(RB_USB2PHY_PU |RB_USB2PHY_SUSPM (usb32)); -writel (reg, utmi->regs +USB2_PHY_CTRL (usb32)); +regmap_update_bits (utmi->usb_misc,USB2_PHY_CTRL (usb32), +RB_USB2PHY_PU |RB_USB2PHY_SUSPM (usb32),0 );/* Power down OTG module */ if (usb32) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 230/461 phy: spacemit: Remove incorrect clk_disable() in spacemit_usb2phy_init()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Gu <[email protected]>
[ Upstream commit a4058c09dd6e28ec33316fd6eb45ddae4cab1f31 ]
When clk_enable() fails, the clock was never enabled. Calling clk_disable() in this error path is incorrect.
Remove the spurious clk_disable() call from the error handling in spacemit_usb2phy_init().
--- drivers/phy/spacemit/phy-k1-usb2.c | 1 - 1 file changed, 1 deletion(-)
drivers/phy/spacemit/phy-k1-usb2.c +0 -1
@@ -97,7 +97,6 @@ static int spacemit_usb2phy_init(struct phy *phy) ret =clk_enable (sphy->clk);if (ret) {dev_err (&phy->dev,"failed to enable clock\n" ); -clk_disable (sphy->clk);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 231/461 NFSD: Fix infinite loop in layout state revocation
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 4f8ef58c10bfe5f86a643c7c8331b37e69e3dae1 ]
find_one_sb_stid() skips stids whose sc_status is non-zero, but the SC_TYPE_LAYOUT case in nfsd4_revoke_states() never sets sc_status before calling nfsd4_close_layout(). The retry loop therefore finds the same layout stid on every iteration, hanging the revoker indefinitely.
--- fs/nfsd/nfs4state.c | 7 +++++++ 1 file changed, 7 insertions(+)
fs/nfsd/nfs4state.c +7 -0
@@ -1850,6 +1850,13 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)break ;case SC_TYPE_LAYOUT : ls =layoutstateid (stid); +spin_lock (&clp->cl_lock); +if (stid->sc_status ==0 ) { + stid->sc_status |= +SC_STATUS_ADMIN_REVOKED ; +atomic_inc (&clp->cl_admin_revoked); + } +spin_unlock (&clp->cl_lock);nfsd4_close_layout (ls);break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 232/461 ASoC: sdw_utils: Add quirk to ignore RT712 CODEC_MIC
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mac Chiang <[email protected]>
[ Upstream commit 9c37daee7c17fa17e8d41089ee1f658b06cb672a ]
Some devices do not use CODEC_MIC but use the host PCH_DMIC instead. Add a quirk to skip the CODEC_MIC DAI when it is not present in disco table, ensuring the correct capture device is used.
If CODEC_MIC is present, it continues to be used as default.
--- sound/soc/sdw_utils/soc_sdw_utils.c | 2 ++ 1 file changed, 2 insertions(+)
sound/soc/sdw_utils/soc_sdw_utils.c +2 -0
@@ -189,6 +189,8 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_type = SOC_SDW_DAI_TYPE_MIC, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, .rtd_init = asoc_sdw_rt_dmic_rtd_init, + .quirk = SOC_SDW_CODEC_MIC, + .quirk_exclude = true, }, }, .dai_num = 3,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 233/461 ASoC: sdw_utils: Add quirk to ignore RT721 CODEC_MIC
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mac Chiang <[email protected]>
[ Upstream commit fa749a77bdc50f0d695aaf81f1bd55967d77d10f ]
Add a quirk to skip the CODEC_MIC DAI when it is not present. This ensures PCH_DMIC is used as the fallback; otherwise, CODEC_MIC remains the default.
--- sound/soc/sdw_utils/soc_sdw_utils.c | 2 ++ 1 file changed, 2 insertions(+)
sound/soc/sdw_utils/soc_sdw_utils.c +2 -0
@@ -463,6 +463,8 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_type = SOC_SDW_DAI_TYPE_MIC, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, .rtd_init = asoc_sdw_rt_dmic_rtd_init, + .quirk = SOC_SDW_CODEC_MIC, + .quirk_exclude = true, }, }, .dai_num = 3,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 234/461 fprobe: Fix unregister_fprobe() to wait for RCU grace period
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
[ Upstream commit 657b594b2084b39a4bc6d8493aa2140cb00cea49 ]
Commit 4346ba1604093 ("fprobe: Rewrite fprobe on function-graph tracer") changed fprobe to register struct fprobe to an rcu-hlist, but it forgot to wait for RCU GP. Thus there can be use-after-free if the fprobe is released right after unregistering. This can be happened on fprobe event and sample module code.
To fix this issue, add synchronize_rcu() in unregister_fprobe().
Note that BPF is OK because fprobe is used as a part of bpf_kprobe_multi_link. This unregisters its fprobe in bpf_kprobe_multi_link_release() and it is deallocated via bpf_kprobe_multi_link_dealloc(), which is invoked from bpf_link_defer_dealloc_rcu_gp() RCU callback.
For BPF, this also introduced unregister_fprobe_async() which does NOT wait for RCU grace priod.
--- include/linux/fprobe.h | 5 +++++ kernel/trace/bpf_trace.c | 3 ++- kernel/trace/fprobe.c | 23 +++++++++++++++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-)
include/linux/fprobe.h +5 -0
@@ -94,6 +94,7 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilterint register_fprobe_ips (struct fprobe *fp,unsigned long *addrs,int num);int register_fprobe_syms (struct fprobe *fp,const char **syms,int num);int unregister_fprobe (struct fprobe *fp); +int unregister_fprobe_async (struct fprobe *fp);bool fprobe_is_registered (struct fprobe *fp);int fprobe_count_ips_from_filter (const char *filter,const char *notfilter);#else
@@ -113,6 +114,10 @@ static inline int unregister_fprobe(struct fprobe *fp) {return -EOPNOTSUPP ; } +static inline int unregister_fprobe_async (struct fprobe *fp) +{ +return -EOPNOTSUPP ; +}static inline bool fprobe_is_registered (struct fprobe *fp) {return false;
kernel/trace/bpf_trace.c +2 -1
@@ -2384,7 +2384,8 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link)struct bpf_kprobe_multi_link *kmulti_link; kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); -unregister_fprobe (&kmulti_link->fp); +/* Don't wait for RCU GP here. */ +unregister_fprobe_async (&kmulti_link->fp);kprobe_multi_put_modules (kmulti_link->mods, kmulti_link->mods_cnt); }
kernel/trace/fprobe.c +21 -2
@@ -1001,14 +1001,15 @@ static int unregister_fprobe_nolock(struct fprobe *fp) }/** -* unregister_fprobe() - Unregister fprobe. +* unregister_fprobe_async() - Unregister fprobe without RCU GP wait * @fp: A fprobe data structure to be unregistered. * * Unregister fprobe (and remove ftrace hooks from the function entries). +* This function will NOT wait until the fprobe is no longer used. * * Return 0 if @fp is unregistered successfully, -errno if not. */ -int unregister_fprobe (struct fprobe *fp) +int unregister_fprobe_async (struct fprobe *fp) {guard (mutex)(&fprobe_mutex);if (!fp || !fprobe_registered (fp))
@@ -1016,6 +1017,24 @@ int unregister_fprobe(struct fprobe *fp)return unregister_fprobe_nolock (fp); } + +/** +* unregister_fprobe() - Unregister fprobe with RCU GP wait +* @fp: A fprobe data structure to be unregistered. +* +* Unregister fprobe (and remove ftrace hooks from the function entries). +* This function will block until the fprobe is no longer used. +* +* Return 0 if @fp is unregistered successfully, -errno if not. +*/ +int unregister_fprobe (struct fprobe *fp) +{ +int ret =unregister_fprobe_async (fp); + +if (!ret) +synchronize_rcu (); +return ret; +}EXPORT_SYMBOL_GPL (unregister_fprobe);static int __init fprobe_initcall (void )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 235/461 fs/statmount: fix slab out-of-bounds write in statmount_mnt_idmap
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junyoung Jang <[email protected]>
[ Upstream commit a3bf0f28d4ba16e1f35f8c983bb04426b87e2a78 ]
statmount_mnt_idmap() writes one mapping with seq_printf() and then manually advances seq->count to include the NUL separator.
If seq_printf() overflows, seq_set_overflow() sets seq->count to seq->size. The manual seq->count++ changes this to seq->size + 1. seq_has_overflowed() then no longer detects the overflow. The corrupted count returns to statmount_string(), which later executes:
seq->buf[seq->count++] = '\0';
This causes a 1-byte NULL out-of-bounds write on the dynamically allocated seq buffer.
Fix this by checking for overflow immediately after seq_printf().
--- fs/mnt_idmapping.c | 2 ++ 1 file changed, 2 insertions(+)
fs/mnt_idmapping.c +2 -0
@@ -375,6 +375,8 @@ int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_continue ;seq_printf (seq,"%u %u %u" , extent->first, lower, extent->count); +if (seq_has_overflowed (seq)) +return -EAGAIN ; seq->count++;/* mappings are separated by \0 */ if (seq_has_overflowed (seq))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 236/461 fs: Fix return in jfs_mkdir and orangefs_mkdir
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongling Zeng <[email protected]>
[ Upstream commit a7cf1da7ac016490d6a1106f2aa6b602d34e9a12 ]
Return NULL instead of passing to ERR_PTR while err is zero Fixes these smatch warnings:
- fs/jfs/namei.c:311 jfs_mkdir() warn: passing zero to 'ERR_PTR'
- fs/orangefs/namei.c:369 orangefs_mkdir() warn: passing zero to 'ERR_PTR'
--- fs/jfs/namei.c | 2 +- fs/orangefs/namei.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
fs/jfs/namei.c +1 -1
@@ -309,7 +309,7 @@ static struct dentry *jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip, out1:jfs_info ("jfs_mkdir: rc:%d" , rc); -return ERR_PTR (rc); +return rc ?ERR_PTR (rc) :NULL ; }/*
fs/orangefs/namei.c +1 -1
@@ -362,7 +362,7 @@ static struct dentry *orangefs_mkdir(struct mnt_idmap *idmap, struct inode *dir,__orangefs_setattr (dir, &iattr); out:op_release (new_op); -return ERR_PTR (ret); +return ret ?ERR_PTR (ret) :NULL ; } static int orangefs_rename(struct mnt_idmap *idmap,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 237/461 irqchip/ath79-cpu: Remove unused function
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rosen Penev <[email protected]>
[ Upstream commit 0fa10fb77069fb67aa51384868ef3702b7791465 ]
ath79_cpu_irq_init() was part of the legacy pre-OF code that got removed a while back.
Remove it to get rid of a missing prototype warning, reported by the kernel test robot.
[ tglx: Fix the subject prefix. Sigh ... ]
--- drivers/irqchip/irq-ath79-cpu.c | 7 ------- 1 file changed, 7 deletions(-)
drivers/irqchip/irq-ath79-cpu.c +0 -7
@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( }IRQCHIP_DECLARE (ar79_cpu_intc,"qca,ar7100-cpu-intc" , ar79_cpu_intc_of_init); - -void __init ath79_cpu_irq_init (unsigned irq_wb_chan2,unsigned irq_wb_chan3) -{ - irq_wb_chan[2 ] = irq_wb_chan2; - irq_wb_chan[3 ] = irq_wb_chan3; -mips_cpu_irq_init (); -}
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 238/461 fs: fix forced iversion increment on lazytime timestamp updates
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pankaj Raghav <[email protected]>
[ Upstream commit 834e98acb748025c04fed3cac9c8954454f4b520 ]
When updating timestamps with lazytime enabled, if only I_DIRTY_TIME is set (pure lazytime update), inode_maybe_inc_iversion() should not be forced to increment i_version. The force parameter should only be true when actual data or metadata changes require an iversion bump.
The current code uses "!!dirty" which evaluates to true whenever dirty has any bits set, including the I_DIRTY_TIME bit alone. This forces an iversion increment on every lazytime timestamp update, which then sets I_DIRTY_SYNC, triggering expensive log flushes on subsequent fdatasync calls. Andres reported this issue when he noticed a perf regression[1].
Fix this by using "dirty != I_DIRTY_TIME" as the force parameter. This passes false for pure lazytime updates (allowing the I_VERSION_QUERIED optimization to work), while still forcing the increment when dirty contains other flags indicating real changes that require iversion updates.
[1] https://lore.kernel.org/linux-xfs/7ys6erh3nnyeerv2nybyfvp7dmaknuxrlxv74wx56ocdothkc6@ekfiadtkfn2r/
--- fs/inode.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
fs/inode.c +7 -1
@@ -2130,7 +2130,13 @@ static int inode_update_cmtime(struct inode *inode, unsigned int flags) inode_iversion_need_inc(inode))return -EAGAIN ; }else { -if (inode_maybe_inc_iversion (inode, !!dirty)) +/* +* Don't force iversion increment for pure lazytime +* updates (I_DIRTY_TIME only), let I_VERSION_QUERIED +* dictate whether the increment is needed. +*/ +if (inode_maybe_inc_iversion (inode, + dirty !=I_DIRTY_TIME )) dirty |=I_DIRTY_SYNC ; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 239/461 ublk: reject max_sectors smaller than PAGE_SECTORS in parameter validation
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ming Lei <[email protected]>
[ Upstream commit 1860c2f85922917d8a46f16a6f4bd2298ffa0fb5 ]
blk_validate_limits() requires max_hw_sectors >= PAGE_SECTORS and fires a WARN_ON_ONCE if this invariant is violated. ublk_validate_params() only checked the upper bound of max_sectors against max_io_buf_bytes, allowing userspace to pass small values (including zero) that trigger the warning when blk_mq_alloc_disk() is called from ublk_ctrl_start_dev().
Before 494ea040bcb5, ublk used blk_queue_max_hw_sectors() which silently clamped small values up to PAGE_SECTORS. The conversion to passing queue_limits directly to blk_mq_alloc_disk() lost that clamping and now hits blk_validate_limits()'s WARN_ON_ONCE instead.
Validate that max_sectors is at least PAGE_SECTORS in ublk_validate_params() so invalid values are rejected early with -EINVAL instead of reaching the block layer.
--- drivers/block/ublk_drv.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/block/ublk_drv.c +3 -0
@@ -868,6 +868,9 @@ static int ublk_validate_params(const struct ublk_device *ub)if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >>9 ))return -EINVAL ; +if (p->max_sectors <PAGE_SECTORS ) +return -EINVAL ; +if (ublk_dev_is_zoned (ub) && !p->chunk_sectors)return -EINVAL ; }else
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 240/461 nsfs: fix wrong error code returned for pidns ioctls
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhihao Cheng <[email protected]>
[ Upstream commit 725ecd80688bf3c57ca9205431f2c06174ff0756 ]
When executing NS_GET_PID_FROM_PIDNS (or similar pidns ioctls), if the target task cannot be found in the corresponding pid_ns, the error code should be ESRCH instead of ENOTTY.
This bug was introduced when the extensible ioctl handling was added. Without proper return, ret would be overwritten by the default case in the extensible ioctl switch statement.
--- fs/nsfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/nsfs.c +1 -1
@@ -266,7 +266,7 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,else tsk =find_task_by_pid_ns (arg, pid_ns);if (!tsk) -break ; +return ret;switch (ioctl) {case NS_GET_PID_FROM_PIDNS :
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 241/461 irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 91840be8f710370607f949a627e070896faeddb8 ]
On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via run_irq_workd(), so irq_work_sync() uses rcuwait() to wait for BUSY==0.
After irq_work_single() clears BUSY via atomic_cmpxchg(), it still dereferences @work for irq_work_is_hard() and rcuwait_wake_up().
An irq_work_sync() caller on another CPU that enters after BUSY is cleared can observe BUSY==0 immediately, return, and free the work before those accesses complete — causing a use-after-free.
Fix this by wrapping run_irq_workd() in guard(rcu)() so that the entire irq_work_single() execution is within an RCU read-side critical section. Then add synchronize_rcu() in irq_work_sync() after rcuwait_wait_event() to ensure the caller waits for the RCU grace period before returning, preventing premature frees.
--- kernel/irq_work.c | 7 +++++++ 1 file changed, 7 insertions(+)
kernel/irq_work.c +7 -0
@@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work) !arch_irq_work_has_interrupt()) {rcuwait_wait_event (&work->irqwait, !irq_work_is_busy (work),TASK_UNINTERRUPTIBLE ); +/* +* Ensure irq_work_single() does not access @work +* after removing IRQ_WORK_BUSY. It is always +* accessed within a RCU-read section. +*/ +synchronize_rcu ();return ; }
@@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync);static void run_irq_workd (unsigned int cpu) { +guard (rcu)();irq_work_run_list (this_cpu_ptr (&lazy_list)); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 242/461 nvme: fix bio leak on mapping failure
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Keith Busch <[email protected]>
[ Upstream commit 2279cd9c61a330e5de4d6eb0bc422820dd6fdf36 ]
The local bio is always NULL, so we'd leak the bio if the integrity mapping failed. Just get it directly from the request.
--- drivers/nvme/host/ioctl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/nvme/host/ioctl.c +2 -3
@@ -122,7 +122,6 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,bool supports_metadata = bdev &&blk_get_integrity (bdev->bd_disk);struct nvme_ctrl *ctrl =nvme_req (req)->ctrl;bool has_metadata = meta_buffer && meta_len; -struct bio *bio =NULL ;int ret;if (!nvme_ctrl_sgl_supported (ctrl))
@@ -154,8 +153,8 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,return ret; out_unmap: -if (bio) -blk_rq_unmap_user (bio); +if (req->bio) +blk_rq_unmap_user (req->bio);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 243/461 nvme-pci: fix use-after-free in nvme_free_host_mem()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chia-Lin Kao (AceLan) <[email protected]>
[ Upstream commit b35a13036755c5803168a7cb93bc66035c3e65b8 ]
nvme_free_host_mem() frees dev->hmb_sgt via dma_free_noncontiguous() but never clears the pointer afterward. This leads to a use-after-free if nvme_free_host_mem() is called twice in the same error path.
This can happen during nvme_probe() when nvme_setup_host_mem() succeeds in allocating the HMB (setting dev->hmb_sgt) but nvme_set_host_mem() fails with an I/O error:
nvme_setup_host_mem()
nvme_alloc_host_mem_single() -> sets dev->hmb_sgt
nvme_set_host_mem() -> fails with -EIO
nvme_free_host_mem() -> frees hmb_sgt, but does NOT NULL itreturn error
nvme_probe() error path:
nvme_free_host_mem() -> dev->hmb_sgt is stale, use-after-free
The second call dereferences the freed sgt, causing a NULL pointer dereference in iommu_dma_free_noncontiguous() when it accesses sgt->sgl->dma_address (the backing memory has been freed and zeroed).
This is reproducible on Thunderbolt-attached NVMe devices (e.g., OWC Envoy Express behind a Dell WD22TB4 dock) where the device intermittently returns I/O errors during HMB setup due to PCIe link instability.
BUG: kernel NULL pointer dereference, address: 0000000000000010 RIP: 0010:iommu_dma_free_noncontiguous+0x22/0x80 Call Trace: <TASK> dma_free_noncontiguous+0x3b/0x130 nvme_free_host_mem+0x30/0xf0 [nvme] nvme_probe.cold+0xcc/0x275 [nvme] local_pci_probe+0x43/0xa0 pci_device_probe+0xeea/0x290 really_probe+0xf9/0x3b0 __driver_probe_device+0x8b/0x170 driver_probe_device+0x24/0xd0 __driver_attach_async_helper+0x6b/0x110 async_run_entry_fn+0x37/0x170 process_one_work+0x1ac/0x3d0 worker_thread+0x1b8/0x360 kthread+0xf7/0x130 ret_from_fork+0x2d8/0x3a0 ret_from_fork_asm+0x1a/0x30 </TASK>
Fix this by setting dev->hmb_sgt to NULL after freeing it, so the second call takes the multi-descriptor path which safely handles the already-cleaned-up state.
--- drivers/nvme/host/pci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/nvme/host/pci.c +4 -2
@@ -2533,11 +2533,13 @@ static void nvme_free_host_mem_multi(struct nvme_dev *dev)static void nvme_free_host_mem (struct nvme_dev *dev) { -if (dev->hmb_sgt) +if (dev->hmb_sgt) {dma_free_noncontiguous (dev->dev, dev->host_mem_size, dev->hmb_sgt,DMA_BIDIRECTIONAL ); -else + dev->hmb_sgt =NULL ; + }else {nvme_free_host_mem_multi (dev); + }dma_free_coherent (dev->dev, dev->host_mem_descs_size, dev->host_mem_descs, dev->host_mem_descs_dma);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 244/461 zonefs: handle integer overflow in zonefs_fname_to_fno
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johannes Thumshirn <[email protected]>
[ Upstream commit 3a8389d42bdf4213730f4067f8bfa78bae6564ef ]
In zonefs the file name in one of the two directories corresponds to the zone number.
Here Alexey reported a possible integer overflow in zonefs_fname_to_fno(), where the parsing of the zone number from the file name can overflow the 'long' data type.
Add a check for integer overflows and if the fno 'long' did overflow return -ENOENT.
--- fs/zonefs/super.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
fs/zonefs/super.c +5 -1
@@ -610,10 +610,14 @@ static long zonefs_fname_to_fno(const struct qstr *fname)return c -'0' ;for (i =0 , rname = name + len -1 ; i < len; i++, rname--) { +long digit; + c = *rname;if (!isdigit (c))return -ENOENT ; - fno += (c -'0' ) * shift; + digit = (c -'0' ) * shift; +if (check_add_overflow (fno, digit, &fno)) +return -ENOENT ; shift *=10 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 245/461 tcp: Fix out-of-bounds access for twsk in tcp_ao_established_key().
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 03cb001ef87b3f8d859cf7f96329acf3d6235d29 ]
lockdep_sock_is_held() was added in tcp_ao_established_key() by the cited commit.
It can be called from tcp_v[46]_timewait_ack() with twsk.
Since it does not have sk->sk_lock, the lockdep annotation results in out-of-bound access.
$ pahole -C tcp_timewait_sock vmlinux | grep size /* size: 288, cachelines: 5, members: 8 */ $ pahole -C sock vmlinux | grep sk_lock socket_lock_t sk_lock; /* 440 192 */
Let's not use lockdep_sock_is_held() for TCP_TIME_WAIT.
--- net/ipv4/tcp_ao.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/ipv4/tcp_ao.c +2 -1
@@ -116,7 +116,8 @@ struct tcp_ao_key *tcp_ao_established_key(const struct sock *sk, {struct tcp_ao_key *key; -hlist_for_each_entry_rcu (key, &ao->head, node,lockdep_sock_is_held (sk)) { +hlist_for_each_entry_rcu (key, &ao->head, node, +sk_fullsock (sk) &&lockdep_sock_is_held (sk)) {if ((sndid >=0 && key->sndid != sndid) || (rcvid >=0 && key->rcvid != rcvid))continue ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 246/461 ASoC: SOF: amd: Fix error code handling in psp_send_cmd()
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello <[email protected]>
[ Upstream commit 2c7b1227e582e88db7917412dca4e752c1aff691 ]
The smn_read_register() helper returns negative error codes on failure or the register value on success. When used with read_poll_timeout(), the return value is stored in the 'data' variable.
Currently 'data' is declared as u32, which causes negative error codes to be cast to large positive values. This makes the condition 'data > 0' incorrectly treat errors as success.
Fix by changing 'data' from u32 to int, matching the pattern used in psp_mbox_ready() which correctly handles the same helper function.
--- sound/soc/sof/amd/acp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/sof/amd/acp.c +1 -1
@@ -223,7 +223,7 @@ static int psp_send_cmd(struct acp_dev_data *adata, int cmd) {struct snd_sof_dev *sdev = adata->dev;int ret; -u32 data; +int data;if (!cmd)return -EINVAL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 247/461 powerpc: 82xx: fix uninitialized pointers with free attribute
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ally Heev <[email protected]>
[ Upstream commit acd1e47db03d4b528fd5efb8565dd0de1c79f62a ]
Uninitialized pointers with __free attribute can cause undefined behavior as the memory allocated to the pointer is freed automatically when the pointer goes out of scope.
powerpc/km82xx doesn't have any bugs related to this as of now, but, it is better to initialize and assign pointers with __free attribute in one statement to ensure proper scope-based cleanup
--- arch/powerpc/platforms/82xx/km82xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/powerpc/platforms/82xx/km82xx.c +2 -2
@@ -27,8 +27,8 @@static void __init km82xx_pic_init (void ) { - struct device_node *np __free(device_node); - np =of_find_compatible_node (NULL ,NULL ,"fsl,pq2-pic" ); + struct device_node *np __free(device_node) = of_find_compatible_node(NULL, +NULL ,"fsl,pq2-pic" );if (!np) {pr_err ("PIC init: can not find cpm-pic node\n" );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 248/461 powerpc: fix dead default for GUEST_STATE_BUFFER_TEST
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Braha <[email protected]>
[ Upstream commit aef656a0e6c01796190bb5bd2bdba1c644ed7811 ]
The GUEST_STATE_BUFFER_TEST config option should default to KUNIT_ALL_TESTS so that if all tests are enabled then it is included, but currently the 'default KUNIT_ALL_TESTS' statement is shadowed by 'def_tristate n',
meaning that this second default statement is currently dead code.
It looks to me like the commit6ccbbc33f06a ("KVM: PPC: Add helper library for Guest State Buffers") intended to set the default to KUNIT_ALL_TESTS, but mistakenly missed the def_tristate.
This dead code was found by kconfirm, a static analysis tool for Kconfig.
--- arch/powerpc/Kconfig.debug | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
arch/powerpc/Kconfig.debug +1 -2
@@ -83,11 +83,10 @@ config MSI_BITMAP_SELFTEST depends on DEBUG_KERNEL config GUEST_STATE_BUFFER_TEST - def_tristate n + def_tristate KUNIT_ALL_TESTS prompt "Enable Guest State Buffer unit tests" depends on KUNIT depends on KVM_BOOK3S_HV_POSSIBLE - default KUNIT_ALL_TESTS help The Guest State Buffer is a data format specified in the PAPR. It is by hcalls to communicate the state of L2 guests between
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 249/461 powerpc/hv-gpci: fix preempt count leak in sysfs show paths
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aboorva Devarajan <[email protected]>
[ Upstream commit dbc30a57bd8e026995e9fa8e8c31cffd18542c01 ]
Four sysfs show() callbacks in hv-gpci take get_cpu_var(hv_gpci_reqb) (which calls preempt_disable()) but only call the matching put_cpu_var() on the error path under the 'out:' label. Every successful read leaks one preempt_disable():
processor_bus_topology_show()
processor_config_show()
affinity_domain_via_virtual_processor_show()
affinity_domain_via_domain_show()
(affinity_domain_via_partition_show() was already correct.)
On a CONFIG_PREEMPT=y kernel, repeated reads raise preempt_count and eventually return to userspace with preemption still disabled. The next user-mode page fault then hits faulthandler_disabled() == 1, gets forced to SIGSEGV, and the resulting coredump trips 'BUG: scheduling while atomic' in call_usermodehelper_exec -> wait_for_completion_state -> schedule:
BUG: scheduling while atomic: <task>/<pid>/0x00000004 ... __schedule_bug+0x6c/0x90 __schedule+0x58c/0x13a0 schedule+0x48/0x1a0 schedule_timeout+0x104/0x170 wait_for_completion_state+0x16c/0x330 call_usermodehelper_exec+0x254/0x2d0 vfs_coredump+0x1050/0x2590 get_signal+0xb9c/0xc80 do_notify_resume+0xf8/0x470
Add an out_success label that calls put_cpu_var() before returning the byte count, mirroring affinity_domain_via_partition_show().
--- arch/powerpc/perf/hv-gpci.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
arch/powerpc/perf/hv-gpci.c +16 -8
@@ -210,7 +210,7 @@ static ssize_t processor_bus_topology_show(struct device *dev, struct device_att 0, 0, buf, &n, arg);if (!ret) -return n; + goto out_success;if (ret !=H_PARAMETER ) goto out;
@@ -244,12 +244,14 @@ static ssize_t processor_bus_topology_show(struct device *dev, struct device_att starting_index, 0, buf, &n, arg);if (!ret) -return n; + goto out_success;if (ret !=H_PARAMETER ) goto out; } +out_success: +put_cpu_var (hv_gpci_reqb);return n; out:
@@ -278,7 +280,7 @@ static ssize_t processor_config_show(struct device *dev, struct device_attribute 0, 0, buf, &n, arg);if (!ret) -return n; + goto out_success;if (ret !=H_PARAMETER ) goto out;
@@ -312,12 +314,14 @@ static ssize_t processor_config_show(struct device *dev, struct device_attribute starting_index, 0, buf, &n, arg);if (!ret) -return n; + goto out_success;if (ret !=H_PARAMETER ) goto out; } +out_success: +put_cpu_var (hv_gpci_reqb);return n; out:
@@ -346,7 +350,7 @@ static ssize_t affinity_domain_via_virtual_processor_show(struct device *dev, 0, 0, buf, &n, arg);if (!ret) -return n; + goto out_success;if (ret !=H_PARAMETER ) goto out;
@@ -382,12 +386,14 @@ static ssize_t affinity_domain_via_virtual_processor_show(struct device *dev, starting_index, secondary_index, buf, &n, arg);if (!ret) -return n; + goto out_success;if (ret !=H_PARAMETER ) goto out; } +out_success: +put_cpu_var (hv_gpci_reqb);return n; out:
@@ -416,7 +422,7 @@ static ssize_t affinity_domain_via_domain_show(struct device *dev, struct device 0, 0, buf, &n, arg);if (!ret) -return n; + goto out_success;if (ret !=H_PARAMETER ) goto out;
@@ -448,12 +454,14 @@ static ssize_t affinity_domain_via_domain_show(struct device *dev, struct device starting_index, 0, buf, &n, arg);if (!ret) -return n; + goto out_success;if (ret !=H_PARAMETER ) goto out; } +out_success: +put_cpu_var (hv_gpci_reqb);return n; out:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 250/461 netfs: Fix cancellation of a DIO and single read subrequests
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 6f0f7ac1915abc0d202f0eb4b003a6548a5ba60d ]
When the preparation of a new subrequest for a read fails, if the subrequest has already been added to the stream->subrequests list, it can't simply be put and abandoned as the collector may see it. Also, if it hasn't been queued yet, it has two outstanding refs that both need to be put. Both DIO read and single-read dispatch fail at this; further, both differ in the order they do things to the way buffered read works.
Fix cancellation of both DIO-read and single-read subrequests that failed preparation by the following steps:
(1) Harmonise all three reads (buffered, dio, single) to queue the subreq before prepping it.
(2) Make all three call netfs_queue_read() to do the queuing.
(3) Set NETFS_RREQ_ALL_QUEUED independently of the queuing as we don't know the length of the subreq at this point.
(4) In all cases, set the error and NETFS_SREQ_FAILED flag on the subreq and then call netfs_read_subreq_terminated() to deal with it. This will pass responsibility off to the collector for dealing with it.
--- fs/netfs/buffered_read.c | 34 +++++++++++++------------------- fs/netfs/direct_read.c | 42 +++++++++++++--------------------------- fs/netfs/internal.h | 3 +++ fs/netfs/read_collect.c | 11 +++++++++++ fs/netfs/read_single.c | 23 ++++++++++------------ 5 files changed, 50 insertions(+), 63 deletions(-)
fs/netfs/buffered_read.c +13 -21
@@ -156,9 +156,8 @@ static void netfs_read_cache_to_pagecache(struct netfs_io_request *rreq, netfs_cache_read_terminated, subreq); } -static void netfs_queue_read (struct netfs_io_request *rreq, -struct netfs_io_subrequest *subreq, -bool last_subreq) +void netfs_queue_read (struct netfs_io_request *rreq, +struct netfs_io_subrequest *subreq) {struct netfs_io_stream *stream = &rreq->io_streams[0 ];
@@ -178,11 +177,6 @@ static void netfs_queue_read(struct netfs_io_request *rreq, } } -if (last_subreq) { -smp_wmb ();/* Write lists before ALL_QUEUED. */ -set_bit (NETFS_RREQ_ALL_QUEUED , &rreq->flags); - } -spin_unlock (&rreq->lock); }
@@ -233,6 +227,8 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, subreq->start = start; subreq->len = size; +netfs_queue_read (rreq, subreq); + source =netfs_cache_prepare_read (rreq, subreq, rreq->i_size); subreq->source = source;if (source ==NETFS_DOWNLOAD_FROM_SERVER ) {
@@ -253,6 +249,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, rreq->debug_id, subreq->debug_index, subreq->len, size, subreq->start, ictx->zero_point, rreq->i_size); +netfs_cancel_read (subreq, ret);break ; } subreq->len = len;
@@ -261,12 +258,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq,if (rreq->netfs_ops->prepare_read) { ret = rreq->netfs_ops->prepare_read (subreq);if (ret <0 ) { - subreq->error = ret; -/* Not queued - release both refs. */ -netfs_put_subrequest (subreq, - netfs_sreq_trace_put_cancel); -netfs_put_subrequest (subreq, - netfs_sreq_trace_put_cancel); +netfs_cancel_read (subreq, ret);break ; }trace_netfs_sreq (subreq, netfs_sreq_trace_prepare);
@@ -289,23 +281,23 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq,pr_err ("Unexpected read source %u\n" , source);WARN_ON_ONCE (1 ); +netfs_cancel_read (subreq, ret);break ; issue: slice =netfs_prepare_read_iterator (subreq, ractl);if (slice <0 ) { ret = slice; - subreq->error = ret; -trace_netfs_sreq (subreq, netfs_sreq_trace_cancel); -/* Not queued - release both refs. */ -netfs_put_subrequest (subreq, netfs_sreq_trace_put_cancel); -netfs_put_subrequest (subreq, netfs_sreq_trace_put_cancel); +netfs_cancel_read (subreq, ret);break ; } - size -= slice; start += slice; + size -= slice; +if (size <=0 ) { +smp_wmb ();/* Write lists before ALL_QUEUED. */ +set_bit (NETFS_RREQ_ALL_QUEUED , &rreq->flags); + } -netfs_queue_read (rreq, subreq, size <=0 );netfs_issue_read (rreq, subreq);cond_resched (); }while (size >0 );
fs/netfs/direct_read.c +13 -29
86 lines not on this page · raw message
fs/netfs/internal.h +3 -0
@@ -23,6 +23,8 @@/* * buffered_read.c */ +void netfs_queue_read (struct netfs_io_request *rreq, +struct netfs_io_subrequest *subreq);void netfs_cache_read_terminated (void *priv,ssize_t transferred_or_error);int netfs_prefetch_for_write (struct file *file,struct folio *folio,size_t offset,size_t len);
@@ -108,6 +110,7 @@ static inline void netfs_see_subrequest(struct netfs_io_subrequest *subreq,*/ bool netfs_read_collection (struct netfs_io_request *rreq);void netfs_read_collection_worker (struct work_struct *work); +void netfs_cancel_read (struct netfs_io_subrequest *subreq,int error);void netfs_cache_read_terminated (void *priv,ssize_t transferred_or_error);/*
fs/netfs/read_collect.c +11 -0
@@ -575,6 +575,17 @@ void netfs_read_subreq_terminated(struct netfs_io_subrequest *subreq) }EXPORT_SYMBOL (netfs_read_subreq_terminated); +/* +* Cancel a read subrequest due to preparation failure. +*/ +void netfs_cancel_read (struct netfs_io_subrequest *subreq,int error) +{ +trace_netfs_sreq (subreq, netfs_sreq_trace_cancel); + subreq->error = error; +__set_bit (NETFS_SREQ_FAILED , &subreq->flags); +netfs_read_subreq_terminated (subreq); +} +/* * Handle termination of a read from the cache. */
fs/netfs/read_single.c +10 -13
@@ -89,7 +89,6 @@ static void netfs_single_read_cache(struct netfs_io_request *rreq,*/ static int netfs_single_dispatch_read (struct netfs_io_request *rreq) { -struct netfs_io_stream *stream = &rreq->io_streams[0 ];struct netfs_io_subrequest *subreq;int ret =0 ;
@@ -102,14 +101,7 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) subreq->len = rreq->len; subreq->io_iter = rreq->buffer.iter; -__set_bit (NETFS_SREQ_IN_PROGRESS , &subreq->flags); - -spin_lock (&rreq->lock); -list_add_tail (&subreq->rreq_link, &stream->subrequests); -trace_netfs_sreq (subreq, netfs_sreq_trace_added); -/* Store list pointers before active flag */ -smp_store_release (&stream->active, true); -spin_unlock (&rreq->lock); +netfs_queue_read (rreq, subreq);netfs_single_cache_prepare_read (rreq, subreq);switch (subreq->source) {
@@ -121,10 +113,14 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) goto cancel; } +smp_wmb ();/* Write lists before ALL_QUEUED. */ +set_bit (NETFS_RREQ_ALL_QUEUED , &rreq->flags); rreq->netfs_ops->issue_read (subreq); rreq->submitted += subreq->len;break ;case NETFS_READ_FROM_CACHE : +smp_wmb ();/* Write lists before ALL_QUEUED. */ +set_bit (NETFS_RREQ_ALL_QUEUED , &rreq->flags);trace_netfs_sreq (subreq, netfs_sreq_trace_submit);netfs_single_read_cache (rreq, subreq); rreq->submitted += subreq->len;
@@ -134,14 +130,15 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq)pr_warn ("Unexpected single-read source %u\n" , subreq->source);WARN_ON_ONCE (true); ret = -EIO ; -break ; + goto cancel; } -smp_wmb ();/* Write lists before ALL_QUEUED. */ -set_bit (NETFS_RREQ_ALL_QUEUED , &rreq->flags);return ret; cancel: -netfs_put_subrequest (subreq, netfs_sreq_trace_put_cancel); +netfs_cancel_read (subreq, ret); +smp_wmb ();/* Write lists before ALL_QUEUED. */ +set_bit (NETFS_RREQ_ALL_QUEUED , &rreq->flags); +netfs_wake_collector (rreq);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 251/461 netfs: Fix missing locking around retry adding new subreqs
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit cce18c263e9623872327ba3c956012f73c1179cc ]
Fix netfs_retry_read_subrequests() and netfs_retry_write_stream() to take the appropriate lock when adding extra subrequests into stream->subrequests.
--- fs/netfs/read_retry.c | 6 +++++- fs/netfs/write_retry.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-)
fs/netfs/read_retry.c +5 -1
@@ -175,7 +175,9 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)list_for_each_entry_safe_from (subreq, tmp, &stream->subrequests, rreq_link) {trace_netfs_sreq (subreq, netfs_sreq_trace_superfluous); +spin_lock (&rreq->lock);list_del (&subreq->rreq_link); +spin_unlock (&rreq->lock);netfs_put_subrequest (subreq, netfs_sreq_trace_put_done);if (subreq == to)break ;
@@ -203,8 +205,10 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)refcount_read (&subreq->ref), netfs_sreq_trace_new); +spin_lock (&rreq->lock);list_add (&subreq->rreq_link, &to->rreq_link); - to =list_next_entry (to, rreq_link); +spin_unlock (&rreq->lock); + to = subreq;trace_netfs_sreq (subreq, netfs_sreq_trace_retry); stream->sreq_max_len =umin (len, rreq->rsize);
fs/netfs/write_retry.c +5 -1
@@ -130,7 +130,9 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,list_for_each_entry_safe_from (subreq, tmp, &stream->subrequests, rreq_link) {trace_netfs_sreq (subreq, netfs_sreq_trace_discard); +spin_lock (&wreq->lock);list_del (&subreq->rreq_link); +spin_unlock (&wreq->lock);netfs_put_subrequest (subreq, netfs_sreq_trace_put_done);if (subreq == to)break ;
@@ -153,8 +155,10 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq, netfs_sreq_trace_new);trace_netfs_sreq (subreq, netfs_sreq_trace_split); +spin_lock (&wreq->lock);list_add (&subreq->rreq_link, &to->rreq_link); - to =list_next_entry (to, rreq_link); +spin_unlock (&wreq->lock); + to = subreq;trace_netfs_sreq (subreq, netfs_sreq_trace_retry); stream->sreq_max_len = len;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 252/461 netfs: Fix missing barriers when accessing stream->subrequests locklessly
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit b5782e2d462c028096f922abca46318cec890670 ]
The list of subrequests attached to stream->subrequests is accessed without locks by netfs_collect_read_results() and netfs_collect_write_results(), and then they access subreq->flags without taking a barrier after getting the subreq pointer from the list. Relatedly, the functions that build the list don't use any sort of write barrier when constructing the list to make sure that the NETFS_SREQ_IN_PROGRESS flag is perceived to be set first if no lock is taken.
Fix this by:
(1) Add a new list_add_tail_release() function that uses a release barrier to set the pointer to the new member of the list.
(2) Add a new list_first_entry_or_null_acquire() function that uses an acquire barrier to read the pointer to the first member in a list (or return NULL).
(3) Use list_add_tail_release() when adding a subreq to ->subrequests.
(4) Use list_first_entry_or_null_acquire() when initially accessing the front of the list (when an item is removed, the pointer to the new front iterm is obtained under the same lock).
--- fs/netfs/buffered_read.c | 3 ++- fs/netfs/misc.c | 1 + fs/netfs/read_collect.c | 6 ++++-- fs/netfs/write_collect.c | 6 ++++-- fs/netfs/write_issue.c | 3 ++- include/linux/list.h | 37 +++++++++++++++++++++++++++++++++++++ 6 files changed, 50 insertions(+), 6 deletions(-)
fs/netfs/buffered_read.c +2 -1
@@ -168,7 +168,8 @@ void netfs_queue_read(struct netfs_io_request *rreq,* remove entries off of the front. */ spin_lock (&rreq->lock); -list_add_tail (&subreq->rreq_link, &stream->subrequests); +/* Write IN_PROGRESS before pointer to new subreq */ +list_add_tail_release (&subreq->rreq_link, &stream->subrequests);if (list_is_first (&subreq->rreq_link, &stream->subrequests)) {if (!stream->active) { stream->collected_to = subreq->start;
fs/netfs/misc.c +1 -0
@@ -356,6 +356,7 @@ void netfs_wait_for_in_progress_stream(struct netfs_io_request *rreq,DEFINE_WAIT (myself);list_for_each_entry (subreq, &stream->subrequests, rreq_link) { +smp_rmb ();/* Read ->next before IN_PROGRESS. */ if (!netfs_check_subreq_in_progress (subreq))continue ;
fs/netfs/read_collect.c +4 -2
@@ -205,8 +205,10 @@ static void netfs_collect_read_results(struct netfs_io_request *rreq)* in progress. The issuer thread may be adding stuff to the tail * whilst we're doing this. */ - front =list_first_entry_or_null (&stream->subrequests, - struct netfs_io_subrequest, rreq_link); + front =list_first_entry_or_null_acquire (&stream->subrequests, + struct netfs_io_subrequest, rreq_link); +/* Read first subreq pointer before IN_PROGRESS flag. */ +while (front) {size_t transferred;
fs/netfs/write_collect.c +4 -2
@@ -228,8 +228,10 @@ static void netfs_collect_write_results(struct netfs_io_request *wreq)if (!smp_load_acquire (&stream->active))continue ; - front =list_first_entry_or_null (&stream->subrequests, - struct netfs_io_subrequest, rreq_link); + front =list_first_entry_or_null_acquire (&stream->subrequests, + struct netfs_io_subrequest, rreq_link); +/* Read first subreq pointer before IN_PROGRESS flag. */ +while (front) {trace_netfs_collect_sreq (wreq, front);//_debug("sreq [%x] %llx %zx/%zx",
fs/netfs/write_issue.c +2 -1
@@ -204,7 +204,8 @@ void netfs_prepare_write(struct netfs_io_request *wreq,* remove entries off of the front. */ spin_lock (&wreq->lock); -list_add_tail (&subreq->rreq_link, &stream->subrequests); +/* Write IN_PROGRESS before pointer to new subreq */ +list_add_tail_release (&subreq->rreq_link, &stream->subrequests);if (list_is_first (&subreq->rreq_link, &stream->subrequests)) {if (!stream->active) { stream->collected_to = subreq->start;
include/linux/list.h +37 -0
@@ -191,6 +191,29 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head)__list_add (new, head->prev, head); } +/** +* list_add_tail_release - add a new entry with release barrier +* @new: new entry to be added +* @head: list head to add it before +* +* Insert a new entry before the specified head, using a release barrier to set +* the ->next pointer that points to it. This is useful for implementing +* queues, in particular one that the elements will be walked through forwards +* locklessly. +*/ +static inline void list_add_tail_release (struct list_head *new, +struct list_head *head) +{ +struct list_head *prev = head->prev; + +if (__list_add_valid (new, prev, head)) { + new->next = head; + new->prev = prev; + head->prev = new; +smp_store_release (&prev->next, new); + } +} +/* * Delete a list entry by making the prev/next entries * point to each other.
@@ -644,6 +667,20 @@ static inline void list_splice_tail_init(struct list_head *list, pos__ != head__ ?list_entry (pos__, type, member) :NULL ; \ }) +/** +* list_first_entry_or_null_acquire - get the first element from a list with barrier +* @ptr: the list head to take the element from. +* @type: the type of the struct this is embedded in. +* @member: the name of the list_head within the struct. +* +* Note that if the list is empty, it returns NULL. +*/ +#define list_first_entry_or_null_acquire (ptr, type, member) ({ \ + struct list_head *head__ = (ptr); \ + struct list_head *pos__ = smp_load_acquire(&head__->next); \ + pos__ != head__ ? list_entry(pos__, type, member) : NULL; \ +}) +/** * list_last_entry_or_null - get the last element from a list * @ptr: the list head to take the element from.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 253/461 netfs: Fix netfs_read_to_pagecache() to pause on subreq failure
In reply to Greg Kroah-Hartman7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 8a8c0cfdf4658fc5b295b7fc87be56e0d76741f4 ]
Fix netfs_read_to_pagecache() so that it pauses the generation of new subrequests if an already-issued subrequest fails.
--- fs/netfs/buffered_read.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/netfs/buffered_read.c +5 -0
@@ -300,6 +300,11 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, }netfs_issue_read (rreq, subreq); + +if (test_bit (NETFS_RREQ_PAUSE , &rreq->flags)) +netfs_wait_for_paused_read (rreq); +if (test_bit (NETFS_RREQ_FAILED , &rreq->flags)) +break ;cond_resched (); }while (size >0 );
Signature · 1 line
2.53.0