6.18.34-rc1 review
Subsystems (3)
Greg Kroah-Hartman
maintainerPATCH 128/377 drm/v3d: Fix use-after-free of CPU job query arrays on error path
In reply to Greg Kroah-Hartman6.18-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
@@ -104,20 +104,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;
@@ -860,7 +846,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
@@ -119,6 +119,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)
@@ -1321,7 +1336,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;
@@ -1404,8 +1419,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 129/377 drm/v3d: Release indirect CSD GEM reference on CPU job free
In reply to Greg Kroah-Hartman6.18-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
@@ -131,6 +131,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 130/377 drm/virtio: use uninterruptible resv lock for plane updates
In reply to Greg Kroah-Hartman6.18-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
@@ -214,7 +214,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);
@@ -458,7 +461,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 131/377 drm/amdgpu/vpe: Force collaborate sync after TRAP
In reply to Greg Kroah-Hartman6.18-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 132/377 drm/bridge: it66121: acquire reset GPIO in probe
In reply to Greg Kroah-Hartman6.18-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 133/377 drm/bridge: megachips: remove bridge when irq request fails
In reply to Greg Kroah-Hartman6.18-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 134/377 drm/amd/display: Fix integer overflow in bios_get_image()
In reply to Greg Kroah-Hartman6.18-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 135/377 drm/amd/display: Validate GPIO pin LUT table size before iterating
In reply to Greg Kroah-Hartman6.18-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 136/377 drm/amd/display: Validate payload length and link_index in dc_process_dmub_aux_transfer_async
In reply to Greg Kroah-Hartman6.18-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
@@ -5884,7 +5884,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 137/377 batman-adv: v: stop OGMv2 on disabled interface
In reply to Greg Kroah-Hartman6.18-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 138/377 batman-adv: tvlv: abort OGM send on tvlv append failure
In reply to Greg Kroah-Hartman6.18-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 139/377 batman-adv: tvlv: reject oversized TVLV packets
In reply to Greg Kroah-Hartman6.18-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 140/377 batman-adv: iv: recover OGM scheduling after forward packet error
In reply to Greg Kroah-Hartman6.18-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 141/377 batman-adv: mcast: fix use-after-free in orig_node RCU release
In reply to Greg Kroah-Hartman6.18-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 142/377 batman-adv: clear current gateway during teardown
In reply to Greg Kroah-Hartman6.18-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 143/377 batman-adv: dat: handle forward allocation error
In reply to Greg Kroah-Hartman6.18-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
@@ -697,6 +697,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 144/377 batman-adv: fix fragment reassembly length accounting
In reply to Greg Kroah-Hartman6.18-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 145/377 batman-adv: fix tp_meter counter underflow during shutdown
In reply to Greg Kroah-Hartman6.18-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 146/377 batman-adv: frag: disallow unicast fragment in fragment
In reply to Greg Kroah-Hartman6.18-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 147/377 batman-adv: bla: fix report_work leak on backbone_gw purge
In reply to Greg Kroah-Hartman6.18-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
@@ -1225,6 +1225,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;
@@ -1235,30 +1236,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 148/377 batman-adv: bla: avoid double decrement of bla.num_requests
In reply to Greg Kroah-Hartman6.18-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
@@ -515,8 +515,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);
@@ -545,9 +545,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;
@@ -650,10 +654,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); }/**
@@ -874,10 +880,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);
@@ -1256,9 +1264,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);
@@ -1509,7 +1521,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. *
@@ -1518,14 +1530,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 149/377 batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface
In reply to Greg Kroah-Hartman6.18-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
@@ -357,12 +357,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 150/377 batman-adv: tp_meter: avoid use of uninit sender vars
In reply to Greg Kroah-Hartman6.18-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 151/377 batman-adv: tp_meter: directly shut down timer on cleanup
In reply to Greg Kroah-Hartman6.18-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 152/377 batman-adv: tp_meter: fix tp_vars reference leak in receiver shutdown
In reply to Greg Kroah-Hartman6.18-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 153/377 batman-adv: tp_meter: fix race condition in send error reporting
In reply to Greg Kroah-Hartman6.18-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 154/377 batman-adv: tp_meter: avoid role confusion in tp_list
In reply to Greg Kroah-Hartman6.18-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 155/377 batman-adv: tt: fix TOCTOU race for reported vlans
In reply to Greg Kroah-Hartman6.18-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 156/377 batman-adv: tt: reject oversized local TVLV buffers
In reply to Greg Kroah-Hartman6.18-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 157/377 batman-adv: tt: avoid empty VLAN responses
In reply to Greg Kroah-Hartman6.18-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 158/377 batman-adv: tt: fix negative last_changeset_len
In reply to Greg Kroah-Hartman6.18-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 159/377 batman-adv: tt: fix negative tt_buff_len
In reply to Greg Kroah-Hartman6.18-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 160/377 batman-adv: tt: prevent TVLV entry number overflow
In reply to Greg Kroah-Hartman6.18-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 161/377 hwmon: (pmbus/adm1266) seed timestamp from the real-time clock
In reply to Greg Kroah-Hartman6.18-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 162/377 hwmon: (pmbus/adm1266) reject implausible blackbox record_count
In reply to Greg Kroah-Hartman6.18-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 163/377 hwmon: (pmbus/adm1266) include PEC byte in pmbus_block_xfer read buffer
In reply to Greg Kroah-Hartman6.18-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 164/377 hwmon: (pmbus/adm1266) bounce blackbox records through a protocol-sized buffer
In reply to Greg Kroah-Hartman6.18-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 165/377 hwmon: (pmbus/adm1266) cap PDIO scan in get_multiple at ADM1266_PDIO_NR
In reply to Greg Kroah-Hartman6.18-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 166/377 hwmon: (pmbus/adm1266) dont clobber GPIO bits before PDIO read in get_multiple
In reply to Greg Kroah-Hartman6.18-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 167/377 hwmon: (pmbus/adm1266) register the gpio_chip after pmbus_do_probe()
In reply to Greg Kroah-Hartman6.18-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 168/377 hwmon: (pmbus/adm1266) register the nvmem device after pmbus_do_probe()
In reply to Greg Kroah-Hartman6.18-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 169/377 hwmon: (pmbus/adm1266) reject short block-read responses in the GPIO accessors
In reply to Greg Kroah-Hartman6.18-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 170/377 pinctrl: qcom: ipq4019: mark gpio as a GPIO pin function
In reply to Greg Kroah-Hartman6.18-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 171/377 ARM: dts: renesas: genmai: Drop superfluous cells
In reply to Greg Kroah-Hartman6.18-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 172/377 ARM: dts: renesas: rskrza1: Drop superfluous cells
In reply to Greg Kroah-Hartman6.18-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 173/377 pinctrl: renesas: rzg2l: Fix incorrect PUPD register offset for high pins during suspend/resume
In reply to Greg Kroah-Hartman6.18-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
@@ -3035,7 +3035,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 174/377 pinctrl: renesas: rzg2l: Fix SMT register cache handling
In reply to Greg Kroah-Hartman6.18-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;
@@ -2723,10 +2723,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;
@@ -2745,6 +2741,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]),
@@ -3052,8 +3053,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 175/377 pinctrl: meson: amlogic-a4: fix deadlock issue
In reply to Greg Kroah-Hartman6.18-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 176/377 pinctrl: qcom: Fix GPIO to PDC wake irq map for qcs615
In reply to Greg Kroah-Hartman6.18-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 177/377 HID: intel-thc-hid: Intel-quickspi: Fix some error codes
In reply to Greg Kroah-Hartman6.18-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 178/377 HID: uclogic: Fix regression of input name assignment
In reply to Greg Kroah-Hartman6.18-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 179/377 firmware: arm_ffa: Check for NULL FF-A ID table while driver registration
In reply to Greg Kroah-Hartman6.18-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 180/377 firmware: arm_ffa: Skip free_pages on RX buffer alloc failure
In reply to Greg Kroah-Hartman6.18-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
@@ -2063,7 +2063,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 181/377 firmware: arm_ffa: Fix per-vcpu self notifications handling in workqueue
In reply to Greg Kroah-Hartman6.18-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
@@ -1538,7 +1538,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 182/377 firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0
In reply to Greg Kroah-Hartman6.18-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;
@@ -1625,6 +1626,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;
@@ -1708,6 +1718,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 ();
@@ -1735,11 +1747,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 183/377 riscv: errata: Fix bitwise vs logical AND in MIPS errata patching
In reply to Greg Kroah-Hartman6.18-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 184/377 riscv: mm: Fixup no5lvl failure when vaddr is invalid
In reply to Greg Kroah-Hartman6.18-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
@@ -852,6 +852,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
@@ -893,6 +914,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 ?
@@ -915,6 +939,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 185/377 kunit: config: Enable KUNIT_DEBUGFS by default
In reply to Greg Kroah-Hartman6.18-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 186/377 kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS
In reply to Greg Kroah-Hartman6.18-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 187/377 pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for sm8150
In reply to Greg Kroah-Hartman6.18-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 188/377 firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies
In reply to Greg Kroah-Hartman6.18-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
@@ -319,6 +319,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))))
@@ -326,15 +332,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,
@@ -350,15 +354,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 ];
@@ -376,7 +393,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 189/377 firmware: arm_ffa: Keep framework RX release under lock
In reply to Greg Kroah-Hartman6.18-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
@@ -1488,25 +1488,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 190/377 firmware: arm_ffa: Validate framework notification message layout
In reply to Greg Kroah-Hartman6.18-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
@@ -1483,21 +1483,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 191/377 firmware: arm_ffa: Align RxTx buffer size before mapping
In reply to Greg Kroah-Hartman6.18-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
@@ -2102,6 +2102,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) {
@@ -2117,7 +2118,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 192/377 firmware: arm_ffa: Snapshot notifier callbacks under lock
In reply to Greg Kroah-Hartman6.18-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
@@ -1459,20 +1459,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); } }
@@ -1480,9 +1485,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 */
@@ -1518,12 +1524,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 193/377 firmware: arm_ffa: Fix sched-recv callback partition lookup
In reply to Greg Kroah-Hartman6.18-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
@@ -1203,7 +1203,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;
@@ -1216,11 +1216,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 194/377 ARM: integrator: Fix early initialization
In reply to Greg Kroah-Hartman6.18-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 195/377 ALSA: hda: cs35l56: Put ACPI device after setting companion
In reply to Greg Kroah-Hartman6.18-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
@@ -949,6 +949,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 196/377 ALSA: hda: cs35l41: Put ACPI device on missing physical node
In reply to Greg Kroah-Hartman6.18-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
@@ -1901,8 +1901,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 197/377 btrfs: tracepoints: fix sleep while in atomic context in btrfs_sync_file()
In reply to Greg Kroah-Hartman6.18-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 198/377 netfilter: x_tables: unregister the templates first
In reply to Greg Kroah-Hartman6.18-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 199/377 netfilter: x_tables: add and use xt_unregister_table_pre_exit
In reply to Greg Kroah-Hartman6.18-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
@@ -300,6 +300,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
@@ -1581,15 +1581,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
@@ -1789,14 +1789,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);
@@ -1887,7 +1879,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
@@ -1795,14 +1795,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);
@@ -1894,7 +1886,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
@@ -1538,6 +1538,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 200/377 netfilter: x_tables: add and use xtables_unregister_table_exit
In reply to Greg Kroah-Hartman6.18-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
@@ -299,8 +299,8 @@ struct xt_table *xt_register_table(struct net *net,const struct xt_table *table, 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,
@@ -1583,7 +1582,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,
@@ -1791,7 +1790,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,
@@ -1797,7 +1796,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 201/377 netfilter: ebtables: move to two-stage removal scheme
In reply to Greg Kroah-Hartman6.18-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 202/377 netfilter: ebtables: close dangling table module init race
In reply to Greg Kroah-Hartman6.18-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 203/377 netfilter: x_tables: close dangling table module init race
In reply to Greg Kroah-Hartman6.18-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 204/377 netfilter: bridge: eb_tables: close module init race
In reply to Greg Kroah-Hartman6.18-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 205/377 kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist()
In reply to Greg Kroah-Hartman6.18-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 206/377 test_kprobes: clear kprobes between test runs
In reply to Greg Kroah-Hartman6.18-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 207/377 tcp: Fix imbalanced icsk_accept_queue count.
In reply to Greg Kroah-Hartman6.18-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
@@ -1149,7 +1149,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 208/377 net: napi: Avoid gro timer misfiring at end of busypoll
In reply to Greg Kroah-Hartman6.18-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
@@ -6777,9 +6777,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 ;
@@ -6789,6 +6789,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 {
@@ -6800,8 +6802,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
@@ -6821,10 +6822,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); } }
@@ -6839,7 +6842,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 209/377 net: shaper: Reject reparenting of existing nodes
In reply to Greg Kroah-Hartman6.18-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 210/377 idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init()
In reply to Greg Kroah-Hartman6.18-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
@@ -949,6 +949,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;
@@ -974,8 +976,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 211/377 ice: fix setting RSS VSI hash for E830
In reply to Greg Kroah-Hartman6.18-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
@@ -8075,7 +8075,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 212/377 ice: fix locking in ice_dcb_rebuild()
In reply to Greg Kroah-Hartman6.18-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 213/377 net: lan966x: avoid unregistering netdev on register failure
In reply to Greg Kroah-Hartman6.18-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 214/377 net: ti: icssm-prueth: fix eth_ports_node leak in probe
In reply to Greg Kroah-Hartman6.18-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
@@ -1347,6 +1347,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 215/377 phy: marvell: mvebu-a3700-utmi: fix incorrect USB2_PHY_CTRL register access
In reply to Greg Kroah-Hartman6.18-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 216/377 NFSD: Fix infinite loop in layout state revocation
In reply to Greg Kroah-Hartman6.18-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
@@ -1848,6 +1848,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 217/377 ASoC: sdw_utils: Add quirk to ignore RT712 CODEC_MIC
In reply to Greg Kroah-Hartman6.18-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
@@ -170,6 +170,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 218/377 ASoC: sdw_utils: Add quirk to ignore RT721 CODEC_MIC
In reply to Greg Kroah-Hartman6.18-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
@@ -431,6 +431,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 219/377 fprobe: Fix unregister_fprobe() to wait for RCU grace period
In reply to Greg Kroah-Hartman6.18-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
@@ -2371,7 +2371,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
@@ -1000,14 +1000,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))
@@ -1015,6 +1016,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 220/377 fs/statmount: fix slab out-of-bounds write in statmount_mnt_idmap
In reply to Greg Kroah-Hartman6.18-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 221/377 fs: Fix return in jfs_mkdir and orangefs_mkdir
In reply to Greg Kroah-Hartman6.18-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
@@ -308,7 +308,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 222/377 irqchip/ath79-cpu: Remove unused function
In reply to Greg Kroah-Hartman6.18-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 223/377 ublk: reject max_sectors smaller than PAGE_SECTORS in parameter validation
In reply to Greg Kroah-Hartman6.18-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
@@ -601,6 +601,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 224/377 nsfs: fix wrong error code returned for pidns ioctls
In reply to Greg Kroah-Hartman6.18-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
@@ -261,7 +261,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 225/377 irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT
In reply to Greg Kroah-Hartman6.18-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 226/377 nvme: fix bio leak on mapping failure
In reply to Greg Kroah-Hartman6.18-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 227/377 nvme-pci: fix use-after-free in nvme_free_host_mem()
In reply to Greg Kroah-Hartman6.18-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
@@ -2320,11 +2320,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 228/377 zonefs: handle integer overflow in zonefs_fname_to_fno
In reply to Greg Kroah-Hartman6.18-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 229/377 tcp: Fix out-of-bounds access for twsk in tcp_ao_established_key().
In reply to Greg Kroah-Hartman6.18-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 230/377 ASoC: SOF: amd: Fix error code handling in psp_send_cmd()
In reply to Greg Kroah-Hartman6.18-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 231/377 powerpc: 82xx: fix uninitialized pointers with free attribute
In reply to Greg Kroah-Hartman6.18-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 232/377 powerpc: fix dead default for GUEST_STATE_BUFFER_TEST
In reply to Greg Kroah-Hartman6.18-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 233/377 netfs: Fix cancellation of a DIO and single read subrequests
In reply to Greg Kroah-Hartman6.18-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 234/377 netfs: Fix netfs_read_to_pagecache() to pause on subreq failure
In reply to Greg Kroah-Hartman6.18-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
@@ -299,6 +299,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
Greg Kroah-Hartman
maintainerPATCH 235/377 netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Viacheslav Dubeyko <[email protected]>
[ Upstream commit dc7832d05deb4d632e8035e3299e31a3528fa0d0 ]
The multiple runs of generic/013 test-case is capable to reproduce a kernel BUG at mm/filemap.c:1504 with probability of 30%.
while true; do
sudo ./check generic/013
done
[ 9849.452376] page: refcount:3 mapcount:0 mapping:00000000e58ff252 index:0x10781 pfn:0x1c322 [ 9849.452412] memcg:ffff8881a1915800 [ 9849.452417] aops:ceph_aops ino:1000058db9e dentry name(?):"f9XXXXXX" [ 9849.452432] flags: 0x17ffffc0000000(node=0|zone=2|lastcpupid=0x1fffff) [ 9849.452441] raw: 0017ffffc0000000 0000000000000000 dead000000000122 ffff88816110d248 [ 9849.452445] raw: 0000000000010781 0000000000000000 00000003ffffffff ffff8881a1915800 [ 9849.452447] page dumped because: VM_BUG_ON_FOLIO(!folio_test_locked(folio)) [ 9849.452474] ------------[ cut here ]------------ [ 9849.452476] kernel BUG at mm/filemap.c:1504! [ 9849.478635] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI [ 9849.481772] CPU: 2 UID: 0 PID: 84223 Comm: fsstress Not tainted 7.0.0-rc1+ #18 PREEMPT(full) [ 9849.482881] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-9.fc43 06/1 0/2025 [ 9849.484539] RIP: 0010:folio_unlock+0x85/0xa0 [ 9849.485076] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 [ 9849.493818] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 [ 9849.495740] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 [ 9849.498678] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [ 9849.500559] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 [ 9849.501097] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 [ 9849.502108] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 [ 9849.502516] FS: 00007e36cbe94740(0000) GS:ffff88824a899000(0000) knlGS:0000000000000000 [ 9849.502996] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 9849.503810] CR2: 000000c0002b0000 CR3: 000000011bbf6004 CR4: 0000000000772ef0 [ 9849.504459] PKRU: 55555554 [ 9849.504626] Call Trace: [ 9849.505242] <TASK> [ 9849.505379] netfs_write_begin+0x7c8/0x10a0 [ 9849.505877] ? __kasan_check_read+0x11/0x20 [ 9849.506384] ? __pfx_netfs_write_begin+0x10/0x10 [ 9849.507178] ceph_write_begin+0x8c/0x1c0 [ 9849.507934] generic_perform_write+0x391/0x8f0 [ 9849.508503] ? __pfx_generic_perform_write+0x10/0x10 [ 9849.509062] ? file_update_time_flags+0x19a/0x4b0 [ 9849.509581] ? ceph_get_caps+0x63/0xf0 [ 9849.510259] ? ceph_get_caps+0x63/0xf0 [ 9849.510530] ceph_write_iter+0xe79/0x1ae0 [ 9849.511282] ? __pfx_ceph_write_iter+0x10/0x10 [ 9849.511839] ? lock_acquire+0x1ad/0x310 [ 9849.512334] ? ksys_write+0xf9/0x230 [ 9849.512582] ? lock_is_held_type+0xaa/0x140 [ 9849.513128] vfs_write+0x512/0x1110 [ 9849.513634] ? __fget_files+0x33/0x350 [ 9849.513893] ? __pfx_vfs_write+0x10/0x10 [ 9849.514143] ? mutex_lock_nested+0x1b/0x30 [ 9849.514394] ksys_write+0xf9/0x230 [ 9849.514621] ? __pfx_ksys_write+0x10/0x10 [ 9849.514887] ? do_syscall_64+0x25e/0x1520 [ 9849.515122] ? __kasan_check_read+0x11/0x20 [ 9849.515366] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.515655] __x64_sys_write+0x72/0xd0 [ 9849.515885] ? trace_hardirqs_on+0x24/0x1c0 [ 9849.516130] x64_sys_call+0x22f/0x2390 [ 9849.516341] do_syscall_64+0x12b/0x1520 [ 9849.516545] ? do_syscall_64+0x27c/0x1520 [ 9849.516783] ? do_syscall_64+0x27c/0x1520 [ 9849.517003] ? lock_release+0x318/0x480 [ 9849.517220] ? __x64_sys_io_getevents+0x143/0x2d0 [ 9849.517479] ? percpu_ref_put_many.constprop.0+0x8f/0x210 [ 9849.517779] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.518073] ? do_syscall_64+0x25e/0x1520 [ 9849.518291] ? __kasan_check_read+0x11/0x20 [ 9849.518519] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.518799] ? do_syscall_64+0x27c/0x1520 [ 9849.519024] ? local_clock_noinstr+0xf/0x120 [ 9849.519262] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.519544] ? do_syscall_64+0x25e/0x1520 [ 9849.519781] ? __kasan_check_read+0x11/0x20 [ 9849.520008] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.520273] ? do_syscall_64+0x27c/0x1520 [ 9849.520491] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.520767] ? irqentry_exit+0x10c/0x6c0 [ 9849.520984] ? trace_hardirqs_off+0x86/0x1b0 [ 9849.521224] ? exc_page_fault+0xab/0x130 [ 9849.521472] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.521766] RIP: 0033:0x7e36cbd14907 [ 9849.521989] Code: 10 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24 [ 9849.523057] RSP: 002b:00007ffff2d2a968 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 [ 9849.523484] RAX: ffffffffffffffda RBX: 000000000000e549 RCX: 00007e36cbd14907 [ 9849.523885] RDX: 000000000000e549 RSI: 00005bd797ec6370 RDI: 0000000000000004 [ 9849.524277] RBP: 0000000000000004 R08: 0000000000000047 R09: 00005bd797ec6370 [ 9849.524652] R10: 0000000000000078 R11: 0000000000000246 R12: 0000000000000049 [ 9849.525062] R13: 0000000010781a37 R14: 00005bd797ec6370 R15: 0000000000000000 [ 9849.525447] </TASK> [ 9849.525574] Modules linked in: intel_rapl_msr intel_rapl_common intel_uncore_frequency_common intel_pmc_core pmt_telemetry pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec kvm_intel joydev kvm irqbypass ghash_clmulni_intel aesni_intel input_leds rapl mac_hid psmouse vga16fb serio_raw vgastate floppy i2c_piix4 bochs qemu_fw_cfg i2c_smbus pata_acpi sch_fq_codel rbd msr parport_pc ppdev lp parport efi_pstore [ 9849.529150] ---[ end trace 0000000000000000 ]--- [ 9849.529502] RIP: 0010:folio_unlock+0x85/0xa0 [ 9849.530813] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 [ 9849.534986] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 [ 9849.536198] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 [ 9849.537718] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [ 9849.539321] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 [ 9849.540862] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 [ 9849.542438] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 [ 9849.543996] FS: 00007e36cbe94740(0000) GS:ffff88824b899000(0000) knlGS:0000000000000000 [ 9849.545854] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 9849.547092] CR2: 00007e36cb3ff000 CR3: 000000011bbf6006 CR4: 0000000000772ef0 [ 9849.548679] PKRU: 55555554
The race sequence:
1. Read completes -> netfs_read_collection() runs
2. netfs_wake_rreq_flag(rreq, NETFS_RREQ_IN_PROGRESS, ...)
3. netfs_wait_for_read() returns -EFAULT to netfs_write_begin()
4. The netfs_unlock_abandoned_read_pages() unlocks the folio
5. netfs_write_begin() calls folio_unlock(folio) -> VM_BUG_ON_FOLIO()
The key reason of the issue that netfs_unlock_abandoned_read_pages() doesn't check the flag NETFS_RREQ_NO_UNLOCK_FOLIO and executes folio_unlock() unconditionally. This patch implements in netfs_unlock_abandoned_read_pages() logic similar to netfs_unlock_read_folio().
--- fs/netfs/read_retry.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
fs/netfs/read_retry.c +9 -2
@@ -288,8 +288,15 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq)struct folio *folio =folioq_folio (p, slot);if (folio && !folioq_is_marked2 (p, slot)) { -trace_netfs_folio (folio, netfs_folio_trace_abandon); -folio_unlock (folio); +if (folio->index == rreq->no_unlock_folio && +test_bit (NETFS_RREQ_NO_UNLOCK_FOLIO , + &rreq->flags)) { +_debug ("no unlock" ); + }else { +trace_netfs_folio (folio, + netfs_folio_trace_abandon); +folio_unlock (folio); + } } } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 236/377 netfs: Fix overrun check in netfs_extract_user_iter()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 0ef37eef83fad3542ee06db2940433ae1a92b39d ]
Fix netfs_extract_user_iter() so that if iov_iter_extract_pages() overfills pages[], then those pages don't get included in the iterator constructed at the end of the function. If there was an overfill, memory corruption has already happened.
--- fs/netfs/iterator.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-)
fs/netfs/iterator.c +17 -9
@@ -72,21 +72,24 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len,break ; } -if (ret > count) { -pr_err ("get_pages rc=%zd more than %zu\n" , ret, count); +if (WARN (ret > count, +"%s: extract_pages overrun %zd > %zu bytes\n" , + __func__, ret, count)) { + ret = -EIO ;break ; } - count -= ret; - ret += offset; - cur_npages =DIV_ROUND_UP (ret,PAGE_SIZE ); - -if (npages + cur_npages > max_pages) { -pr_err ("Out of bvec array capacity (%u vs %u)\n" , - npages + cur_npages, max_pages); + cur_npages =DIV_ROUND_UP (offset + ret,PAGE_SIZE ); +if (WARN (cur_npages > max_pages - npages, +"%s: extract_pages overrun %u > %u pages\n" , + __func__, npages + cur_npages, max_pages)) { + ret = -EIO ;break ; } + count -= ret; + ret += offset; +for (i =0 ; i < cur_npages; i++) { len = ret >PAGE_SIZE ?PAGE_SIZE : ret;bvec_set_page (bv + npages + i, *pages++, len - offset, offset);
@@ -97,6 +100,11 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, npages += cur_npages; } +/* Note: Don't try to clean up after EIO. Either we got no pages, so +* nothing to clean up, or we got a buffer overrun, memory corruption +* and can't trust the stuff in the buffer (a WARN was emitted). +*/ +if (ret <0 && (ret == -ENOMEM || npages ==0 )) {for (i =0 ; i < npages; i++)unpin_user_page (bv[i].bv_page);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 237/377 netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes gone
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 156ac2ec2ee77c44c4eb7439d6d165247ba12247 ]
If a streaming write is made, this will leave the relevant modified folio in a not-uptodate, but dirty state with a netfs_folio struct hung off of folio->private indicating the dirty range. Subsequently truncating the file such that the dirty data in the folio is removed, but the first part of the folio theoretically remains will cause the netfs_folio struct to be discarded... but will leave the dirty flag set.
If the folio is then read via mmap(), netfs_read_folio() will see that the page is dirty and jump to netfs_read_gaps() to fill in the missing bits. netfs_read_gaps(), however, expects there to be a netfs_folio struct present and can oops because truncate removed it.
Fix this by calling folio_cancel_dirty() in netfs_invalidate_folio() in the event that all the dirty data in the folio is erased (as nfs does).
Also add some tracepoints to log modifications to a dirty page.
This can be reproduced with something like:
dd if=/dev/zero of=/xfstest.test/foo bs=1M count=1
umount /xfstest.test
mount /xfstest.test
xfs_io -c "w 0xbbbf 0xf96c" \
-c "truncate 0xbbbf" \
-c "mmap -r 0xb000 0x11000" \
-c "mr 0xb000 0x11000" \
/xfstest.test/foowith fscaching disabled (otherwise streaming writes are suppressed) and a change to netfs_perform_write() to disallow streaming writes if the fd is open O_RDWR:
if (//(file->f_mode & FMODE_READ) || <--- comment this out
netfs_is_cache_enabled(ctx)) {It should be reproducible even without this change, but if prevents the above trivial xfs_io command from reproducing it.
Note that the initial dd is important: the file must start out sufficiently large that the zero-point logic doesn't just clear the gaps because it knows there's nothing in the file to read yet. Unmounting and mounting is needed to clear the pagecache (there are other ways to do that that may also work).
This was initially reproduced with the generic/522 xfstest on some patches that remove the FMODE_READ restriction.
--- fs/netfs/misc.c | 6 +++++- include/trace/events/netfs.h | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-)
fs/netfs/misc.c +5 -1
@@ -256,6 +256,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)/* Move the start of the data. */ finfo->dirty_len = fend - iend; finfo->dirty_offset = offset; +trace_netfs_folio (folio, netfs_folio_trace_invalidate_front);return ; }
@@ -264,12 +265,14 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)*/ if (iend >= fend) { finfo->dirty_len = offset - fstart; +trace_netfs_folio (folio, netfs_folio_trace_invalidate_tail);return ; }/* A partial write was split. The caller has already zeroed * it, so just absorb the hole. */ +trace_netfs_folio (folio, netfs_folio_trace_invalidate_middle); }return ;
@@ -277,8 +280,9 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)netfs_put_group (netfs_folio_group (folio));folio_detach_private (folio);folio_clear_uptodate (folio); +folio_cancel_dirty (folio);kfree (finfo); -return ; +trace_netfs_folio (folio, netfs_folio_trace_invalidate_all); }EXPORT_SYMBOL (netfs_invalidate_folio);
include/trace/events/netfs.h +4 -0
@@ -194,6 +194,10 @@ EM(netfs_folio_trace_copy_to_cache, "mark-copy") \ EM(netfs_folio_trace_end_copy, "end-copy") \ EM(netfs_folio_trace_filled_gaps, "filled-gaps") \ + EM(netfs_folio_trace_invalidate_all, "inval-all") \ + EM(netfs_folio_trace_invalidate_front, "inval-front") \ + EM(netfs_folio_trace_invalidate_middle, "inval-mid") \ + EM(netfs_folio_trace_invalidate_tail, "inval-tail") \ EM(netfs_folio_trace_kill, "kill") \ EM(netfs_folio_trace_kill_cc, "kill-cc") \EM (netfs_folio_trace_kill_g,"kill-g" ) \
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 238/377 netfs: Defer the emission of trace_netfs_folio()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit daeb443b92817021c1234e8eded219e164b7c35d ]
Change netfs_perform_write() to keep the netfs_folio trace value in a variable and emit it later to make it easier to choose the value displayed. This is a prerequisite for a subsequent patch.
Stable-dep-of: 7b4dcf1b9455 ("netfs: Fix streaming write being overwritten")
--- fs/netfs/buffered_write.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-)
fs/netfs/buffered_write.c +10 -8
@@ -150,6 +150,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, } do { + enum netfs_folio_trace trace; struct netfs_folio *finfo; struct netfs_group *group; unsigned long long fpos;
@@ -223,7 +224,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,if (unlikely (copied ==0 )) goto copy_failed;netfs_set_group (folio, netfs_group); -trace_netfs_folio (folio, netfs_folio_is_uptodate); + trace = netfs_folio_is_uptodate; goto copied; }
@@ -239,7 +240,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,folio_zero_segment (folio, offset + copied, flen);__netfs_set_group (folio, netfs_group);folio_mark_uptodate (folio); -trace_netfs_folio (folio, netfs_modify_and_clear); + trace = netfs_modify_and_clear; goto copied; }
@@ -257,7 +258,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, }__netfs_set_group (folio, netfs_group);folio_mark_uptodate (folio); -trace_netfs_folio (folio, netfs_whole_folio_modify); + trace = netfs_whole_folio_modify; goto copied; }
@@ -284,7 +285,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,if (unlikely (copied ==0 )) goto copy_failed;netfs_set_group (folio, netfs_group); -trace_netfs_folio (folio, netfs_just_prefetch); + trace = netfs_just_prefetch; goto copied; }
@@ -298,7 +299,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,if (offset ==0 && copied == flen) {__netfs_set_group (folio, netfs_group);folio_mark_uptodate (folio); -trace_netfs_folio (folio, netfs_streaming_filled_page); + trace = netfs_streaming_filled_page; goto copied; }
@@ -313,7 +314,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, finfo->dirty_len = copied;folio_attach_private (folio, (void *)((unsigned long )finfo |NETFS_FOLIO_INFO )); -trace_netfs_folio (folio, netfs_streaming_write); + trace = netfs_streaming_write; goto copied; }
@@ -333,9 +334,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,folio_detach_private (folio);folio_mark_uptodate (folio);kfree (finfo); -trace_netfs_folio (folio, netfs_streaming_cont_filled_page); + trace = netfs_streaming_cont_filled_page; }else { -trace_netfs_folio (folio, netfs_streaming_write_cont); + trace = netfs_streaming_write_cont; } goto copied; }
@@ -351,6 +352,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,continue ; copied: +trace_netfs_folio (folio, trace);flush_dcache_folio (folio);/* Update the inode size if we moved the EOF marker */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 239/377 netfs: Fix streaming write being overwritten
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 7b4dcf1b9455a6e52ac7478b4057dbe10359576d ]
In order to avoid reading whilst writing, netfslib will allow "streaming writes" in which dirty data is stored directly into folios without reading them first. Such folios are marked dirty but may not be marked uptodate. If a folio is entirely written by a streaming write, uptodate will be set, otherwise it will have a netfs_folio struct attached to ->private recording the dirty region.
In the event that a partially written streaming write page is to be overwritten entirely by a single write(), netfs_perform_write() will try to copy over it, but doesn't discard the netfs_folio if it succeeds; further, it doesn't correctly handle a partial copy that overwrites some of the dirty data.
Fix this by the following:
(1) If the folio is successfully overwritten, free the netfs_folio struct before marking the page uptodate.
(2) If the copy to the folio partially fails, but short of the dirty data, just ignore the copy.
(3) If the copy partially fails and overwrites some of the dirty data, accept the copy, update the netfs_folio struct to record the new data. If the folio is now filled, free the netfs_folio and set uptodate, otherwise return a partial write.
Found with:
fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ /xfstest.test/junk --replay-ops=junk.fsxops
using the following as junk.fsxops:
truncate 0x0 0 0x927c0 write 0x63fb8 0x53c8 0 copy_range 0xb704 0x19b9 0x24429 0x79380 write 0x2402b 0x144a2 0x90660 * write 0x204d5 0x140a0 0x927c0 * copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * read 0x00000 0x20000 0x9157c read 0x20000 0x20000 0x9157c read 0x40000 0x20000 0x9157c read 0x60000 0x20000 0x9157c read 0x7e1a0 0xcfb9 0x9157c
on cifs with the default cache option.
It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in netfs_perform_write():
if (//(file->f_mode & FMODE_READ) ||
netfs_is_cache_enabled(ctx)) {and no fscache. This was initially found with the generic/522 xfstest.
--- fs/netfs/buffered_write.c | 47 ++++++++++++++++++++++++++---------- include/trace/events/netfs.h | 3 +++ 2 files changed, 37 insertions(+), 13 deletions(-)
fs/netfs/buffered_write.c +34 -13
@@ -247,18 +247,38 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,/* See if we can write a whole folio in one go. */ if (!maybe_trouble && offset ==0 && part >= flen) { copied =copy_folio_from_iter_atomic (folio, offset, part, iter); -if (unlikely (copied ==0 )) +if (likely (copied == part)) { +if (finfo) { + trace = netfs_whole_folio_modify_filled; + goto folio_now_filled; + } +__netfs_set_group (folio, netfs_group); +folio_mark_uptodate (folio); + trace = netfs_whole_folio_modify; + goto copied; + } +if (copied ==0 ) goto copy_failed; -if (unlikely (copied < part)) { +if (!finfo || copied <= finfo->dirty_offset) { maybe_trouble = true;iov_iter_revert (iter, copied); copied =0 ;folio_unlock (folio); goto retry; } -__netfs_set_group (folio, netfs_group); -folio_mark_uptodate (folio); - trace = netfs_whole_folio_modify; + +/* We overwrote some existing dirty data, so we have to +* accept the partial write. +*/ + finfo->dirty_len += finfo->dirty_offset; +if (finfo->dirty_len == flen) { + trace = netfs_whole_folio_modify_filled_efault; + goto folio_now_filled; + } +if (copied > finfo->dirty_len) + finfo->dirty_len = copied; + finfo->dirty_offset =0 ; + trace = netfs_whole_folio_modify_efault; goto copied; }
@@ -328,16 +348,10 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, goto copy_failed; finfo->dirty_len += copied;if (finfo->dirty_offset ==0 && finfo->dirty_len == flen) { -if (finfo->netfs_group) -folio_change_private (folio, finfo->netfs_group); -else -folio_detach_private (folio); -folio_mark_uptodate (folio); -kfree (finfo); trace = netfs_streaming_cont_filled_page; - }else { - trace = netfs_streaming_write_cont; + goto folio_now_filled; } + trace = netfs_streaming_write_cont; goto copied; }
@@ -351,6 +365,13 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, goto out;continue ; + folio_now_filled: +if (finfo->netfs_group) +folio_change_private (folio, finfo->netfs_group); +else +folio_detach_private (folio); +folio_mark_uptodate (folio); +kfree (finfo); copied:trace_netfs_folio (folio, trace);flush_dcache_folio (folio);
include/trace/events/netfs.h +3 -0
@@ -177,6 +177,9 @@ EM(netfs_folio_is_uptodate, "mod-uptodate") \ EM(netfs_just_prefetch, "mod-prefetch") \ EM(netfs_whole_folio_modify, "mod-whole-f") \ + EM(netfs_whole_folio_modify_efault, "mod-whole-f!") \ + EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ + EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ EM(netfs_modify_and_clear, "mod-n-clear") \ EM(netfs_streaming_write, "mod-streamw") \EM (netfs_streaming_write_cont,"mod-streamw+" ) \
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 240/377 netfs: Fix potential deadlock in write-through mode
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit b6a4ae1634b3ad2aaa05222e53d36da532852faf ]
Fix netfs_advance_writethrough() to always unlock the supplied folio and to mark it dirty if it isn't yet written to the end. Unfortunately, it can't be marked for writeback until the folio is done with as that may cause a deadlock against mmapped reads and writes.
Even though it has been marked dirty, premature writeback can't occur as the caller is holding both inode->i_rwsem (which will prevent concurrent truncation, fallocation, DIO and other writes) and ictx->wb_lock (which will cause flushing to wait and writeback to skip or wait).
Note that this may be easier to deal with once the queuing of folios is split from the generation of subrequests.
--- fs/netfs/write_issue.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-)
fs/netfs/write_issue.c +25 -14
@@ -413,12 +413,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq,if (streamw)netfs_issue_write (wreq, cache); -/* Flip the page to the writeback state and unlock. If we're called -* from write-through, then the page has already been put into the wb -* state. -*/ -if (wreq->origin ==NETFS_WRITEBACK ) -folio_start_writeback (folio); +folio_start_writeback (folio);folio_unlock (folio);if (fgroup ==NETFS_FOLIO_COPY_TO_CACHE ) {
@@ -646,29 +641,41 @@ int netfs_advance_writethrough(struct netfs_io_request *wreq, struct writeback_c struct folio *folio, size_t copied, bool to_page_end, struct folio **writethrough_cache) { + int ret; +_enter ("R=%x ic=%zu ws=%u cp=%zu tp=%u" , wreq->debug_id, wreq->buffer.iter.count, wreq->wsize, copied, to_page_end); -if (!*writethrough_cache) { -if (folio_test_dirty (folio)) -/* Sigh. mmap. */ -folio_clear_dirty_for_io (folio); +/* The folio is locked. */ +if (*writethrough_cache != folio) { +if (*writethrough_cache) { +/* Did the folio get moved? */ +folio_put (*writethrough_cache); + *writethrough_cache =NULL ; + }/* We can make multiple writes to the folio... */ -folio_start_writeback (folio);if (wreq->len ==0 )trace_netfs_folio (folio, netfs_folio_trace_wthru);else trace_netfs_folio (folio, netfs_folio_trace_wthru_plus); *writethrough_cache = folio; +folio_get (folio); } wreq->len += copied; -if (!to_page_end) + +if (!to_page_end) { +folio_mark_dirty (folio); +folio_unlock (folio);return 0 ; + } + ret =netfs_write_folio (wreq, wbc, folio); +folio_put (*writethrough_cache); *writethrough_cache =NULL ; -return netfs_write_folio (wreq, wbc, folio); + wreq->submitted = wreq->len; +return ret; }/*
@@ -682,8 +689,12 @@ ssize_t netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_c_enter ("R=%x" , wreq->debug_id); -if (writethrough_cache) +if (writethrough_cache) { +folio_lock (writethrough_cache);netfs_write_folio (wreq, wbc, writethrough_cache); +folio_put (writethrough_cache); + wreq->submitted = wreq->len; + }netfs_end_issue_write (wreq);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 241/377 netfs: Fix read-gaps to remove netfs_folio from filled folio
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit a41168aef634356a9b87ec44349e3c82835700a5 ]
Fix netfs_read_gaps() to remove the netfs_folio record from the folio record before marking the folio uptodate if it successfully fills the gaps around the dirty data in a streaming write folio (dirty, but not uptodate).
Found with:
fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \
/xfstest.test/junk --replay-ops=junk.fsxopsusing the following as junk.fsxops:
truncate 0x0 0x138b1 0x8b15d *
write 0x507ee 0x10df7 0x927c0
write 0x19993 0x10e04 0x927c0 *
mapwrite 0x66214 0x1a253 0x927c0
copy_range 0xb704 0x89b9 0x24429 0x79380
write 0x2402b 0x144a2 0x90660 *
mapwrite 0x204d5 0x140a0 0x927c0 *
copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 *
read 0 0x9157c 0x9157con cifs with the default cache option.
It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in netfs_perform_write():
if (//(file->f_mode & FMODE_READ) ||
netfs_is_cache_enabled(ctx)) {and no fscache. This was initially found with the generic/522 xfstest.
--- fs/netfs/buffered_read.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
fs/netfs/buffered_read.c +8 -3
@@ -394,6 +394,7 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) {struct netfs_io_request *rreq;struct address_space *mapping = folio->mapping; +struct netfs_group *group =netfs_folio_group (folio);struct netfs_folio *finfo =netfs_folio_info (folio);struct netfs_inode *ctx =netfs_inode (mapping->host);struct folio *sink =NULL ;
@@ -460,6 +461,12 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) ret =netfs_wait_for_read (rreq);if (ret >=0 ) { +if (group) +folio_change_private (folio, group); +else +folio_detach_private (folio); +kfree (finfo); +trace_netfs_folio (folio, netfs_folio_trace_filled_gaps);flush_dcache_folio (folio);folio_mark_uptodate (folio); }
@@ -495,10 +502,8 @@ int netfs_read_folio(struct file *file, struct folio *folio)struct netfs_inode *ctx =netfs_inode (mapping->host);int ret; -if (folio_test_dirty (folio)) { -trace_netfs_folio (folio, netfs_folio_trace_read_gaps); +if (folio_test_dirty (folio))return netfs_read_gaps (file, folio); - }_enter ("%lx" , folio->index);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 242/377 netfs: Fix write streaming disablement if fd open O_RDWR
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 70a7b9193bbbfceaab5974de66834c64ccc875dd ]
In netfs_perform_write(), "write streaming" (the caching of dirty data in dirty but !uptodate folios) is performed to avoid the need to read data that is just going to get immediately overwritten. However, this is/will be disabled in three circumstances: if the fd is open O_RDWR, if fscache is in use (as we need to round out the blocks for DIO) or if content encryption is enabled (again for rounding out purposes).
The idea behind disabling it if the fd is open O_RDWR is that we'd need to flush the write-streaming page before we could read the data, particularly through mmap. But netfs now fills in the gaps if ->read_folio() is called on the page, so that is unnecessary. Further, this doesn't actually work if a separate fd is open for reading.
Fix this by removing the check for O_RDWR, thereby allowing streaming writes even when we might read.
This caused a number of problems with the generic/522 xfstest, but those are now fixed.
--- fs/netfs/buffered_write.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
fs/netfs/buffered_write.c +7 -10
@@ -204,11 +204,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, }/* Decide how we should modify a folio. We might be attempting -* to do write-streaming, in which case we don't want to a -* local RMW cycle if we can avoid it. If we're doing local -* caching or content crypto, we award that priority over -* avoiding RMW. If the file is open readably, then we also -* assume that we may want to read what we wrote. +* to do write-streaming, as we don't want to a local RMW cycle +* if we can avoid it. If we're doing local caching or content +* crypto, we award that priority over avoiding RMW. If the +* file is open readably, then we let ->read_folio() fill in +* the gaps. */ finfo =netfs_folio_info (folio); group =netfs_folio_group (folio);
@@ -284,12 +284,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,/* We don't want to do a streaming write on a file that loses * caching service temporarily because the backing store got -* culled and we don't really want to get a streaming write on -* a file that's open for reading as ->read_folio() then has to -* be able to flush it. +* culled. */ -if ((file->f_mode &FMODE_READ ) || -netfs_is_cache_enabled (ctx)) { +if (netfs_is_cache_enabled (ctx)) {if (finfo) {netfs_stat (&netfs_n_wh_wstream_conflict); goto flush_content;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 243/377 netfs: Fix early put of sink folio in netfs_read_gaps()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 3e5dd91b87a8b1450217b56a336bee315f40da7d ]
Fix netfs_read_gaps() to release the sink page it uses after waiting for the request to complete. The way the sink page is used is that an ITER_BVEC-class iterator is created that has the gaps from the target folio at either end, but has the sink page tiled over the middle so that a single read op can fill in both gaps.
The bug was found by KASAN detecting a UAF on the generic/075 xfstest in the cifsd kernel thread that handles reception of data from the TCP socket:
BUG: KASAN: use-after-free in _copy_to_iter+0x48a/0xa20 Write of size 885 at addr ffff888107f92000 by task cifsd/1285 CPU: 2 UID: 0 PID: 1285 Comm: cifsd Not tainted 7.0.0 #6 PREEMPT(lazy) Call Trace: dump_stack_lvl+0x5d/0x80 print_report+0x17f/0x4f1 kasan_report+0x100/0x1e0 kasan_check_range+0x10f/0x1e0 __asan_memcpy+0x3c/0x60 _copy_to_iter+0x48a/0xa20 __skb_datagram_iter+0x2c9/0x430 skb_copy_datagram_iter+0x6e/0x160 tcp_recvmsg_locked+0xce0/0x1130 tcp_recvmsg+0xeb/0x300 inet_recvmsg+0xcf/0x3a0 sock_recvmsg+0xea/0x100 cifs_readv_from_socket+0x3a6/0x4d0 [cifs] cifs_read_iter_from_socket+0xdd/0x130 [cifs] cifs_readv_receive+0xaad/0xb10 [cifs] cifs_demultiplex_thread+0x1148/0x1740 [cifs] kthread+0x1cf/0x210
--- fs/netfs/buffered_read.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
fs/netfs/buffered_read.c +3 -3
@@ -456,9 +456,6 @@ static int netfs_read_gaps(struct file *file, struct folio *folio)netfs_read_to_pagecache (rreq,NULL ); -if (sink) -folio_put (sink); - ret =netfs_wait_for_read (rreq);if (ret >=0 ) {if (group)
@@ -470,6 +467,9 @@ static int netfs_read_gaps(struct file *file, struct folio *folio)flush_dcache_folio (folio);folio_mark_uptodate (folio); } + +if (sink) +folio_put (sink);folio_unlock (folio);netfs_put_request (rreq, netfs_rreq_trace_put_return);return ret <0 ? ret :0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 244/377 netfs: Fix leak of request in netfs_write_begin() error handling
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 5046a34f0643441f05b0253ea64e1a3af87efe14 ]
Fix netfs_write_begin() to not leak our ref on the request in the event that we get an error from netfs_wait_for_read().
--- fs/netfs/buffered_read.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/netfs/buffered_read.c +1 -1
@@ -686,9 +686,9 @@ int netfs_write_begin(struct netfs_inode *ctx,netfs_read_to_pagecache (rreq,NULL ); ret =netfs_wait_for_read (rreq); +netfs_put_request (rreq, netfs_rreq_trace_put_return);if (ret <0 ) goto error; -netfs_put_request (rreq, netfs_rreq_trace_put_return); have_folio: ret =folio_wait_private_2_killable (folio);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 245/377 netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit dbe556972100fabb8e5a1b3d2163831ff07b1e8e ]
netfs_unlock_abandoned_read_pages(rreq) accesses the index of the folios it is wanting to unlock and compares that to rreq->no_unlock_folio so that it doesn't unlock a folio being read for netfs_perform_write() or netfs_write_begin().
However, given that netfs_unlock_abandoned_read_pages() is called _after_ NETFS_RREQ_IN_PROGRESS is cleared, the one folio that it's not allowed to dereference is the one specified by ->no_unlock_folio as ownership immediately reverts to the caller.
Fix this by storing the folio pointer instead and using that rather than the index. Also fix netfs_unlock_read_folio() where the same applies.
--- fs/netfs/buffered_read.c | 4 ++-- fs/netfs/read_collect.c | 2 +- fs/netfs/read_retry.c | 2 +- include/linux/netfs.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-)
fs/netfs/buffered_read.c +2 -2
@@ -669,7 +669,7 @@ int netfs_write_begin(struct netfs_inode *ctx, ret =PTR_ERR (rreq); goto error; } - rreq->no_unlock_folio = folio->index; + rreq->no_unlock_folio = folio;__set_bit (NETFS_RREQ_NO_UNLOCK_FOLIO , &rreq->flags); ret =netfs_begin_cache_read (rreq, ctx);
@@ -735,7 +735,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio, goto error; } - rreq->no_unlock_folio = folio->index; + rreq->no_unlock_folio = folio;__set_bit (NETFS_RREQ_NO_UNLOCK_FOLIO , &rreq->flags); ret =netfs_begin_cache_read (rreq, ctx);if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS )
fs/netfs/read_collect.c +1 -1
@@ -83,7 +83,7 @@ static void netfs_unlock_read_folio(struct netfs_io_request *rreq, } just_unlock: -if (folio->index == rreq->no_unlock_folio && +if (folio == rreq->no_unlock_folio &&test_bit (NETFS_RREQ_NO_UNLOCK_FOLIO , &rreq->flags)) {_debug ("no unlock" ); }else {
fs/netfs/read_retry.c +1 -1
@@ -288,7 +288,7 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq)struct folio *folio =folioq_folio (p, slot);if (folio && !folioq_is_marked2 (p, slot)) { -if (folio->index == rreq->no_unlock_folio && +if (folio == rreq->no_unlock_folio &&test_bit (NETFS_RREQ_NO_UNLOCK_FOLIO , &rreq->flags)) {_debug ("no unlock" );
include/linux/netfs.h +1 -1
@@ -252,7 +252,7 @@ struct netfs_io_request {unsigned long long collected_to;/* Point we've collected to */ unsigned long long cleaned_to;/* Position we've cleaned folios to */ unsigned long long abandon_to;/* Position to abandon folios to */ -pgoff_t no_unlock_folio;/* Don't unlock this folio after read */ +const struct folio *no_unlock_folio;/* Don't unlock this folio after read */ unsigned int direct_bv_count;/* Number of elements in direct_bv[] */ unsigned int debug_id;unsigned int rsize;/* Maximum read size (0 for none) */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 246/377 netfs: Fix partial invalidation of streaming-write folio
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 6d91acc7fb85d33ea58fca9b964a32a453937f4b ]
In netfs_invalidate_folio(), if the region of a partial invalidation overlaps the front (but not all) of a dirty write cached in a streaming write page (dirty, but not uptodate, with the dirty region tracked by a netfs_folio struct), the function modifies the dirty region - but incorrectly as it moves the region forward by setting the start to the start, not the end, of the invalidation region.
Fix this by setting finfo->dirty_offset to the end of the invalidation region (iend).
--- fs/netfs/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/netfs/misc.c +1 -1
@@ -255,7 +255,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) goto erase_completely;/* Move the start of the data. */ finfo->dirty_len = fend - iend; - finfo->dirty_offset = offset; + finfo->dirty_offset = iend;trace_netfs_folio (folio, netfs_folio_trace_invalidate_front);return ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 247/377 netfs: Fix folio->private handling in netfs_perform_write()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit ccde2ac757c713535b224233a296de40efe5212d ]
Under some circumstances, netfs_perform_write() doesn't correctly manipulate folio->private between NULL, NETFS_FOLIO_COPY_TO_CACHE, pointing to a group and pointing to a netfs_folio struct, leading to potential multiple attachments of private data with associated folio ref leaks and also leaks of netfs_folio structs or netfs_group refs.
Fix this by consolidating the place at which a folio is marked uptodate in one place and having that look at what's attached to folio->private and decide how to clean it up and then set the new group. Also, the content shouldn't be flushed if group is NULL, even if a group is specified in the netfs_group parameter, as that would be the case for a new folio. A filesystem should always specify netfs_group or never specify netfs_group.
The Sashiko auto-review tool noted that it was theoretically possible that the fpos >= ctx->zero_point section might leak if it modified a streaming write folio. This is unlikely, but with a network filesystem, third party changes can happen. It also pointed out that __netfs_set_group() would leak if called multiple times on the same folio from the "whole folio modify section".
--- fs/netfs/buffered_write.c | 134 +++++++++++++++++++++-------------- include/trace/events/netfs.h | 1 + 2 files changed, 82 insertions(+), 53 deletions(-)
fs/netfs/buffered_write.c +81 -53
@@ -13,24 +13,6 @@#include <linux/pagevec.h> #include "internal.h" -static void __netfs_set_group (struct folio *folio,struct netfs_group *netfs_group) -{ -if (netfs_group) -folio_attach_private (folio,netfs_get_group (netfs_group)); -} - -static void netfs_set_group (struct folio *folio,struct netfs_group *netfs_group) -{ -void *priv =folio_get_private (folio); - -if (unlikely (priv != netfs_group)) { -if (netfs_group && (!priv || priv ==NETFS_FOLIO_COPY_TO_CACHE )) -folio_attach_private (folio,netfs_get_group (netfs_group)); -else if (!netfs_group && priv ==NETFS_FOLIO_COPY_TO_CACHE ) -folio_detach_private (folio); - } -} -/* * Grab a folio for writing and lock it. Attempt to allocate as large a folio * as possible to hold as much of the remaining length as possible in one go.
@@ -158,6 +140,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,size_t offset;/* Offset into pagecache folio */ size_t part;/* Bytes to write to folio */ size_t copied;/* Bytes copied from user */ +void *priv; offset = pos & (max_chunk -1 ); part =min (max_chunk - offset,iov_iter_count (iter));
@@ -203,6 +186,25 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, goto error_folio_unlock; } + finfo =netfs_folio_info (folio); + group =netfs_folio_group (folio); + +/* If the requested group differs from the group set on the +* page, then we need to flush out the folio if it has a group +* set (ie. is non-NULL). Note that COPY_TO_CACHE is a special +* case, being a netfs annotation rather than an actual group. +* +* The filesystem isn't permitted to mix writes with groups and +* writes without groups as the NULL group is used to indicate +* that no group is set. +*/ +if (unlikely (group != netfs_group) && + group !=NETFS_FOLIO_COPY_TO_CACHE && + group) { +WARN_ON_ONCE (!netfs_group); + goto flush_content; + } +/* Decide how we should modify a folio. We might be attempting * to do write-streaming, as we don't want to a local RMW cycle * if we can avoid it. If we're doing local caching or content
@@ -210,22 +212,14 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,* file is open readably, then we let ->read_folio() fill in * the gaps. */ - finfo =netfs_folio_info (folio); - group =netfs_folio_group (folio); - -if (unlikely (group != netfs_group) && - group !=NETFS_FOLIO_COPY_TO_CACHE ) - goto flush_content; -if (folio_test_uptodate (folio)) {if (mapping_writably_mapped (mapping))flush_dcache_folio (folio); copied =copy_folio_from_iter_atomic (folio, offset, part, iter);if (unlikely (copied ==0 )) goto copy_failed; -netfs_set_group (folio, netfs_group); trace = netfs_folio_is_uptodate; - goto copied; + goto copied_uptodate; }/* If the page is above the zero-point then we assume that the
@@ -238,24 +232,22 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,if (unlikely (copied ==0 )) goto copy_failed;folio_zero_segment (folio, offset + copied, flen); -__netfs_set_group (folio, netfs_group); -folio_mark_uptodate (folio); - trace = netfs_modify_and_clear; - goto copied; +if (finfo) + trace = netfs_modify_and_clear_rm_finfo; +else + trace = netfs_modify_and_clear; + goto mark_uptodate; }/* See if we can write a whole folio in one go. */ if (!maybe_trouble && offset ==0 && part >= flen) { copied =copy_folio_from_iter_atomic (folio, offset, part, iter);if (likely (copied == part)) { -if (finfo) { +if (finfo) trace = netfs_whole_folio_modify_filled; - goto folio_now_filled; - } -__netfs_set_group (folio, netfs_group); -folio_mark_uptodate (folio); - trace = netfs_whole_folio_modify; - goto copied; +else + trace = netfs_whole_folio_modify; + goto mark_uptodate; }if (copied ==0 ) goto copy_failed;
@@ -273,7 +265,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, finfo->dirty_len += finfo->dirty_offset;if (finfo->dirty_len == flen) { trace = netfs_whole_folio_modify_filled_efault; - goto folio_now_filled; + goto mark_uptodate; }if (copied > finfo->dirty_len) finfo->dirty_len = copied;
@@ -301,11 +293,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, copied =copy_folio_from_iter_atomic (folio, offset, part, iter);if (unlikely (copied ==0 )) goto copy_failed; -netfs_set_group (folio, netfs_group); trace = netfs_just_prefetch; - goto copied; + goto copied_uptodate; } +/* Do a streaming write on a folio that has nothing in it yet. */ if (!finfo) { ret = -EIO ;if (WARN_ON (folio_get_private (folio)))
@@ -314,10 +306,8 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,if (unlikely (copied ==0 )) goto copy_failed;if (offset ==0 && copied == flen) { -__netfs_set_group (folio, netfs_group); -folio_mark_uptodate (folio); trace = netfs_streaming_filled_page; - goto copied; + goto mark_uptodate; } finfo =kzalloc (sizeof (*finfo),GFP_KERNEL );
@@ -346,7 +336,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, finfo->dirty_len += copied;if (finfo->dirty_offset ==0 && finfo->dirty_len == flen) { trace = netfs_streaming_cont_filled_page; - goto folio_now_filled; + goto mark_uptodate; } trace = netfs_streaming_write_cont; goto copied;
@@ -362,13 +352,36 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, goto out;continue ; - folio_now_filled: -if (finfo->netfs_group) -folio_change_private (folio, finfo->netfs_group); -else -folio_detach_private (folio); +/* Mark a folio as being up to data when we've filled it +* completely. If the folio has a group attached, then it must +* be the same group, otherwise we should have flushed it out +* above. We have to get rid of the netfs_folio struct if +* there was one. +*/ + mark_uptodate:folio_mark_uptodate (folio); -kfree (finfo); + + copied_uptodate: + priv =folio_get_private (folio); +if (likely (priv == netfs_group)) { +/* Already set correctly; no change required. */ + }else if (priv ==NETFS_FOLIO_COPY_TO_CACHE ) { +if (!netfs_group) +folio_detach_private (folio); +else +folio_change_private (folio,netfs_get_group (netfs_group)); + }else if (!priv) { +folio_attach_private (folio,netfs_get_group (netfs_group)); + }else { +WARN_ON_ONCE (!finfo); +if (netfs_group) +/* finfo->netfs_group has a ref */ +folio_change_private (folio, netfs_group); +else +folio_detach_private (folio); +kfree (finfo); + } + copied:trace_netfs_folio (folio, trace);flush_dcache_folio (folio);
@@ -531,6 +544,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_grstruct inode *inode =file_inode (file);struct netfs_inode *ictx =netfs_inode (inode);vm_fault_t ret =VM_FAULT_NOPAGE ; +void *priv;int err;_enter ("%lx" , folio->index);
@@ -551,7 +565,9 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr } group =netfs_folio_group (folio); -if (group != netfs_group && group !=NETFS_FOLIO_COPY_TO_CACHE ) { +if (group && + group != netfs_group && + group !=NETFS_FOLIO_COPY_TO_CACHE ) {folio_unlock (folio); err = filemap_fdatawrite_range(mapping, folio_pos(folio),
@@ -573,7 +589,19 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_grtrace_netfs_folio (folio, netfs_folio_trace_mkwrite_plus); elsetrace_netfs_folio (folio ,netfs_folio_trace_mkwrite ); -netfs_set_group (folio, netfs_group); + + priv =folio_get_private (folio); +if (priv != netfs_group) { +if (!netfs_group && priv ==NETFS_FOLIO_COPY_TO_CACHE ) +folio_detach_private (folio); +else if (netfs_group && priv ==NETFS_FOLIO_COPY_TO_CACHE ) +folio_change_private (folio,netfs_get_group (netfs_group)); +else if (netfs_group && !priv) +folio_attach_private (folio,netfs_get_group (netfs_group)); +else +WARN_ON_ONCE (1 ); + } +file_update_time (file);set_bit (NETFS_ICTX_MODIFIED_ATTR , &ictx->flags);if (ictx->ops->post_modify)
include/trace/events/netfs.h +1 -0
@@ -181,6 +181,7 @@ EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ EM(netfs_modify_and_clear, "mod-n-clear") \ + EM(netfs_modify_and_clear_rm_finfo, "mod-n-clear+") \ EM(netfs_streaming_write, "mod-streamw") \ EM(netfs_streaming_write_cont, "mod-streamw+") \EM (netfs_flush_content,"flush" ) \
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 248/377 netfs: Fix netfs_read_folio() to wait on writeback
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit ded0c6f1606061148c202825f7e53d711f9f84cf ]
Fix netfs_read_folio() to wait for an ongoing writeback to complete so that it can trust the dirty flag and whatever is attached to folio->private (folio->private may get cleaned up by the collector before it clears the writeback flag).
--- fs/netfs/buffered_read.c | 2 ++ 1 file changed, 2 insertions(+)
fs/netfs/buffered_read.c +2 -0
@@ -502,6 +502,8 @@ int netfs_read_folio(struct file *file, struct folio *folio)struct netfs_inode *ctx =netfs_inode (mapping->host);int ret; +folio_wait_writeback (folio); +if (folio_test_dirty (folio))return netfs_read_gaps (file, folio);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 249/377 netfs, afs: Fix write skipping in dir/link writepages
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit 9871938f99cc6cb266a77265491660e2375271f5 ]
Fix netfs_write_single() and afs_single_writepages() to better handle a write that would be skipped due to lock contention and WB_SYNC_NONE by returning 1 from netfs_write_single() if it skipped and making afs_single_writepages() skip also. If a skip occurs, the inode must be re-marked as the VFS may have cleared the mark.
This is really only theoretical for directories in netfs_write_single() as the only path to that is through afs_single_writepages() that takes the ->validate_lock around it, thereby serialising it.
--- fs/afs/dir.c | 11 ++++++++++- fs/netfs/write_issue.c | 7 ++++++- 2 files changed, 16 insertions(+), 2 deletions(-)
fs/afs/dir.c +10 -1
@@ -2207,7 +2207,14 @@ int afs_single_writepages(struct address_space *mapping,/* Need to lock to prevent the folio queue and folios from being thrown * away. */ -down_read (&dvnode->validate_lock); +if (!down_read_trylock (&dvnode->validate_lock)) { +if (wbc->sync_mode ==WB_SYNC_NONE ) { +/* The VFS will have undirtied the inode. */ +netfs_single_mark_inode_dirty (&dvnode->netfs.inode); +return 0 ; + } +down_read (&dvnode->validate_lock); + } if (is_dir ? test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) :
@@ -2215,6 +2222,8 @@ int afs_single_writepages(struct address_space *mapping,iov_iter_folio_queue (&iter,ITER_SOURCE , dvnode->directory,0 ,0 ,i_size_read (&dvnode->netfs.inode)); ret =netfs_writeback_single (mapping, wbc, &iter); +if (ret ==1 ) + ret =0 ;/* Skipped write due to lock conflict. */ }up_read (&dvnode->validate_lock);
fs/netfs/write_issue.c +6 -1
@@ -829,6 +829,9 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,* * Write a monolithic, non-pagecache object back to the server and/or * the cache. +* +* Return: 0 if successful; 1 if skipped due to lock conflict and WB_SYNC_NONE; +* or a negative error code. */ int netfs_writeback_single(struct address_space *mapping, struct writeback_control *wbc,
@@ -845,8 +848,10 @@ int netfs_writeback_single(struct address_space *mapping,if (!mutex_trylock (&ictx->wb_lock)) {if (wbc->sync_mode ==WB_SYNC_NONE ) { +/* The VFS will have undirtied the inode. */ +netfs_single_mark_inode_dirty (&ictx->inode);netfs_stat (&netfs_n_wb_lock_skip); -return 0 ; +return 1 ; }netfs_stat (&netfs_n_wb_lock_wait);mutex_lock (&ictx->wb_lock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 250/377 net: ethernet: cortina: Make RX SKB per-port
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 06937db21ee311ed07eba47954447245041a982d ]
The SKB used to assemble packets from fragments in gmac_rx() is static local, but the Gemini has two ethernet ports, meaning there can be races between the ports on a bad day if a device is using both.
Make the RX SKB a per-port variable and carry it over between invocations in the port struct instead.
Zero the pointer once we call napi_gro_frags(), on error (after calling napi_free_frags()) or if the port is stopped.
Zero it in some place where not strictly necessary just to emphasize what is going on.
This was found by Sashiko during normal patch review.
--- drivers/net/ethernet/cortina/gemini.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/net/ethernet/cortina/gemini.c +6 -1
@@ -122,6 +122,8 @@ struct gemini_ethernet_port {struct napi_struct napi;struct hrtimer rx_coalesce_timer;unsigned int rx_coalesce_nsecs; +struct sk_buff *rx_skb; +unsigned int freeq_refill;struct gmac_txq txq[TX_QUEUE_NUM ];unsigned int txq_order;
@@ -1443,10 +1445,10 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)unsigned short m = (1 << port->rxq_order) -1 ;struct gemini_ethernet *geth = port->geth;void __iomem *ptr_reg = port->rxq_rwptr; +struct sk_buff *skb = port->rx_skb;unsigned int frame_len, frag_len;struct gmac_rxdesc *rx =NULL ;struct gmac_queue_page *gpage; -static struct sk_buff *skb;union gmac_rxdesc_0 word0;union gmac_rxdesc_1 word1;union gmac_rxdesc_3 word3;
@@ -1500,6 +1502,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)if (skb) {napi_free_frags (&port->napi); port->stats.rx_dropped++; + skb =NULL ; } skb =gmac_skb_if_good_frame (port, word0, frame_len);
@@ -1550,6 +1553,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) port->stats.rx_dropped++; } + port->rx_skb = skb;writew (r, ptr_reg);return budget; }
@@ -1877,6 +1881,7 @@ static int gmac_stop(struct net_device *netdev)gmac_disable_tx_rx (netdev);gmac_stop_dma (port);napi_disable (&port->napi); + port->rx_skb =NULL ;gmac_enable_irq (netdev,0 );gmac_cleanup_rxq (netdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 251/377 net: ethernet: cortina: Drop half-assembled SKB
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andreas Haarmann-Thiemann <[email protected]>
[ Upstream commit b266bacba796ff5c4dcd2ae2fc08aacf7ab39153 ]
In gmac_rx() (drivers/net/ethernet/cortina/gemini.c), when gmac_get_queue_page() returns NULL for the second page of a multi-page fragment, the driver logs an error and continues — but does not free the partially assembled skb that was being assembled via napi_build_skb() / napi_get_frags().
Free the in-progress partially assembled skb via napi_free_frags() and increase the number of dropped frames appropriately and assign the skb pointer NULL to make sure it is not lingering around, matching the pattern already used elsewhere in the driver.
--- drivers/net/ethernet/cortina/gemini.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/net/ethernet/cortina/gemini.c +5 -0
@@ -1494,6 +1494,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) gpage =gmac_get_queue_page (geth, port, mapping +PAGE_SIZE );if (!gpage) {dev_err (geth->dev,"could not find mapping\n" ); +if (skb) { +napi_free_frags (&port->napi); + port->stats.rx_dropped++; + skb =NULL ; + }continue ; } page = gpage->page;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 252/377 net: ethernet: cortina: Carry over frag counter
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit ebd8ec2b309e3a447851b456ccaf8fb39f3661e7 ]
The gmac_rx() NAPI poll function assembles packets in an SKB from a ring buffer.
If the ring buffer gets completely emptied during a poll cycle, we exit gmac_rx(), but the packet is not yet completely assembled in the SKB, yet the fragment counter frag_nr is reset to zero on the next invocation.
Solve this by making the RX fragment counter a part of the port struct, and carry it over between invocations.
Reset the fragment counter only right after calling napi_gro_frags(), on error (after calling napi_free_frags()) or if stopping the port.
Reset it in some place where not strictly necessary just to emphasize what is going on.
This was found by Sashiko during normal patch review.
--- drivers/net/ethernet/cortina/gemini.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/net/ethernet/cortina/gemini.c +8 -1
@@ -123,6 +123,7 @@ struct gemini_ethernet_port {struct hrtimer rx_coalesce_timer;unsigned int rx_coalesce_nsecs;struct sk_buff *rx_skb; +unsigned int rx_frag_nr;unsigned int freeq_refill;struct gmac_txq txq[TX_QUEUE_NUM ];
@@ -1445,6 +1446,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)unsigned short m = (1 << port->rxq_order) -1 ;struct gemini_ethernet *geth = port->geth;void __iomem *ptr_reg = port->rxq_rwptr; +unsigned int frag_nr = port->rx_frag_nr;struct sk_buff *skb = port->rx_skb;unsigned int frame_len, frag_len;struct gmac_rxdesc *rx =NULL ;
@@ -1458,7 +1460,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)unsigned short r, w;union dma_rwptr rw;dma_addr_t mapping; -int frag_nr =0 ;spin_lock_irqsave (&geth->irq_lock, flags); rw.bits32 =readl (ptr_reg);
@@ -1498,6 +1499,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)napi_free_frags (&port->napi); port->stats.rx_dropped++; skb =NULL ; + frag_nr =0 ; }continue ; }
@@ -1508,6 +1510,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)napi_free_frags (&port->napi); port->stats.rx_dropped++; skb =NULL ; + frag_nr =0 ; } skb =gmac_skb_if_good_frame (port, word0, frame_len);
@@ -1542,6 +1545,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)if (word3.bits32 &EOF_BIT ) {napi_gro_frags (&port->napi); skb =NULL ; + frag_nr =0 ; --budget; }continue ;
@@ -1550,6 +1554,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)if (skb) {napi_free_frags (&port->napi); skb =NULL ; + frag_nr =0 ; }if (mapping)
@@ -1559,6 +1564,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) } port->rx_skb = skb; + port->rx_frag_nr = frag_nr;writew (r, ptr_reg);return budget; }
@@ -1887,6 +1893,7 @@ static int gmac_stop(struct net_device *netdev)gmac_stop_dma (port);napi_disable (&port->napi); port->rx_skb =NULL ; + port->rx_frag_nr =0 ;gmac_enable_irq (netdev,0 );gmac_cleanup_rxq (netdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 253/377 net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ethan Nelson-Moore <[email protected]>
[ Upstream commit 36a8d04a8293afcb9304cf0cd3741f67698f2a1a ]
The legacy ARM board file for MACH_MX31ADS was removed in commit c93197b0041d ("ARM: imx: Remove i.MX31 board files"), but a reference to it remained in the cs89x0 driver. Drop this unused code.
--- drivers/net/ethernet/cirrus/cs89x0.c | 2 -- 1 file changed, 2 deletions(-)
drivers/net/ethernet/cirrus/cs89x0.c +0 -2
@@ -1271,7 +1271,6 @@ static const struct net_device_ops net_ops = {static void __init reset_chip (struct net_device *dev) { -#if !defined(CONFIG_MACH_MX31ADS )struct net_local *lp =netdev_priv (dev);unsigned long reset_start_time;
@@ -1298,7 +1297,6 @@ static void __init reset_chip(struct net_device *dev)while ((readreg (dev, PP_SelfST) &INIT_DONE ) ==0 &&time_before (jiffies, reset_start_time +2 )) ; -#endif /* !CONFIG_MACH_MX31ADS */ }/* This is the real probe routine.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 254/377 wifi: ath11k: fix error path leaks in some WMI WOW calls
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolas Escande <[email protected]>
[ Upstream commit 55dda532bbc261aef495e403c8900c5e2ab5fa34 ]
Fix two instances where we used to directly return the result of ath11k_wmi_cmd_send(...). Because we did not check the return value, we also did not free the skb in the error path.
--- drivers/net/wireless/ath/ath11k/wmi.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
drivers/net/wireless/ath/ath11k/wmi.c +16 -3
@@ -9191,6 +9191,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)struct wmi_wow_host_wakeup_ind *cmd;struct sk_buff *skb;size_t len; +int ret; len =sizeof (*cmd); skb =ath11k_wmi_alloc_skb (ar->wmi->wmi_ab, len);
@@ -9204,14 +9205,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)ath11k_dbg (ar->ab,ATH11K_DBG_WMI ,"tlv wow host wakeup ind\n" ); -return ath11k_wmi_cmd_send (ar->wmi, skb,WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID ); + ret =ath11k_wmi_cmd_send (ar->wmi, skb,WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID ); +if (ret) { +ath11k_warn (ar->ab,"failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n" ); +dev_kfree_skb (skb); + } + +return ret; }int ath11k_wmi_wow_enable (struct ath11k *ar) {struct wmi_wow_enable_cmd *cmd;struct sk_buff *skb; -int len; +int ret, len; len =sizeof (*cmd); skb =ath11k_wmi_alloc_skb (ar->wmi->wmi_ab, len);
@@ -9226,7 +9233,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar) cmd->pause_iface_config =WOW_IFACE_PAUSE_ENABLED ;ath11k_dbg (ar->ab,ATH11K_DBG_WMI ,"tlv wow enable\n" ); -return ath11k_wmi_cmd_send (ar->wmi, skb,WMI_WOW_ENABLE_CMDID ); + ret =ath11k_wmi_cmd_send (ar->wmi, skb,WMI_WOW_ENABLE_CMDID ); +if (ret) { +ath11k_warn (ar->ab,"failed to send WMI_WOW_ENABLE_CMDID\n" ); +dev_kfree_skb (skb); + } + +return ret; } int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 255/377 wifi: ath11k: fix error path leak in ath11k_tm_cmd_wmi_ftm()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolas Escande <[email protected]>
[ Upstream commit 7320d6eb861e9913193a7801834c661381756a79 ]
This is similar to what was fixed by previous patches. We have a call to ath11k_wmi_cmd_send() which does check the return value, but forgot to free the related skb on error.
--- drivers/net/wireless/ath/ath11k/testmode.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/wireless/ath/ath11k/testmode.c +1 -0
@@ -457,6 +457,7 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[]) ret =ath11k_wmi_cmd_send (wmi, skb, cmd_id);if (ret) {ath11k_warn (ar->ab,"failed to send wmi ftm command: %d\n" , ret); +dev_kfree_skb (skb); goto out; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 256/377 wifi: ath10k: skip WMI and beacon transmission when device is wedged
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kang Yang <[email protected]>
[ Upstream commit 54a5b38e4396530e5b2f12b54d3844e860ab6784 ]
In ath10k_wmi_cmd_send(), the current code detects ATH10K_STATE_WEDGED and sets ret to -ESHUTDOWN, but still proceeds to transmit pending beacons and calls ath10k_wmi_cmd_send_nowait().
This can lead to incorrect behavior, as WMI commands and beacons are still sent after the device has been marked as wedged, and the original -ESHUTDOWN return value may be overwritten by the result of the send path.
The wedged state indicates the hardware is already unreliable, and no further interaction with firmware is expected or meaningful in this state.
Fix this by skipping beacon transmission and the WMI send path entirely once ATH10K_STATE_WEDGED is detected, ensuring consistent return values and avoiding unnecessary firmware interaction.
Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00288-QCARMSWPZ-1
Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00189
--- drivers/net/wireless/ath/ath10k/wmi.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-)
drivers/net/wireless/ath/ath10k/wmi.c +7 -8
@@ -3,7 +3,6 @@* Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. -* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
@@ -1947,15 +1946,15 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) ret = -ESHUTDOWN ;ath10k_dbg (ar,ATH10K_DBG_WMI ,"drop wmi command %d, hardware is wedged\n" , cmd_id); - } -/* try to send pending beacons first. they take priority */ -ath10k_wmi_tx_beacons_nowait (ar); + }else { +/* try to send pending beacons first. they take priority */ +ath10k_wmi_tx_beacons_nowait (ar); - ret =ath10k_wmi_cmd_send_nowait (ar, skb, cmd_id); - -if (ret &&test_bit (ATH10K_FLAG_CRASH_FLUSH , &ar->dev_flags)) - ret = -ESHUTDOWN ; + ret =ath10k_wmi_cmd_send_nowait (ar, skb, cmd_id); +if (ret &&test_bit (ATH10K_FLAG_CRASH_FLUSH , &ar->dev_flags)) + ret = -ESHUTDOWN ; + } (ret != -EAGAIN ); }), 3 * HZ);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 257/377 net: shaper: flip the polarity of the valid flag
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 7cee43fcb0c3f71441d2faaa8c2202b6a88b6bef ]
The usual way of inserting entries which are not yet fully ready into XArray is to have a VALID flag. The shaper code has a NOT_VALID flag. Since XArray code does not let us create entries with marks already set - the creation of entries is currently not atomic.
Flip the polarity of the VALID flag. This closes the tiny race in net_shaper_pre_insert() of entries being created without the NOT_VALID flag.
--- net/shaper/shaper.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-)
net/shaper/shaper.c +17 -17
@@ -275,11 +275,13 @@ static void net_shaper_default_parent(const struct net_shaper_handle *handle, parent->id =0 ; } -/* -* MARK_0 is already in use due to XA_FLAGS_ALLOC, can't reuse such flag as -* it's cleared by xa_store(). +/* MARK_0 is already in use due to XA_FLAGS_ALLOC. The VALID mark is set on +* an entry only after the device-side configuration has completed +* successfully (see net_shaper_commit()). Lookups and dumps must filter on +* this mark to avoid exposing tentative entries inserted by +* net_shaper_pre_insert() while the driver call is still in flight. */ -#define NET_SHAPER_NOT_VALID XA_MARK_1 +#define NET_SHAPER_VALID XA_MARK_1static struct net_shaper * net_shaper_lookup(struct net_shaper_binding *binding,
@@ -289,8 +291,8 @@ net_shaper_lookup(struct net_shaper_binding *binding,struct net_shaper_hierarchy *hierarchy; hierarchy =net_shaper_hierarchy_rcu (binding); -if (!hierarchy ||xa_get_mark (&hierarchy->shapers, index, -NET_SHAPER_NOT_VALID )) +if (!hierarchy || !xa_get_mark (&hierarchy->shapers, index, +NET_SHAPER_VALID ))return NULL ;return xa_load (&hierarchy->shapers, index);
@@ -370,13 +372,10 @@ static int net_shaper_pre_insert(struct net_shaper_binding *binding, goto free_id; } -/* Mark 'tentative' shaper inside the hierarchy container. -* xa_set_mark is a no-op if the previous store fails. +/* Insert as 'tentative' (no VALID mark). The mark will be set by +* net_shaper_commit() once the driver-side configuration succeeds. */ -xa_lock (&hierarchy->shapers); - prev =__xa_store (&hierarchy->shapers, index, cur,GFP_KERNEL ); -__xa_set_mark (&hierarchy->shapers, index,NET_SHAPER_NOT_VALID ); -xa_unlock (&hierarchy->shapers); + prev =xa_store (&hierarchy->shapers, index, cur,GFP_KERNEL );if (xa_err (prev)) {NL_SET_ERR_MSG (extack,"Can't insert shaper into device store" );kfree_rcu (cur, rcu);
@@ -413,8 +412,7 @@ static void net_shaper_commit(struct net_shaper_binding *binding,/* Successful update: drop the tentative mark * and update the hierarchy container. */ -__xa_clear_mark (&hierarchy->shapers, index, -NET_SHAPER_NOT_VALID ); +__xa_set_mark (&hierarchy->shapers, index,NET_SHAPER_VALID ); *cur = shapers[i]; }xa_unlock (&hierarchy->shapers);
@@ -431,8 +429,9 @@ static void net_shaper_rollback(struct net_shaper_binding *binding)return ;xa_lock (&hierarchy->shapers); -xa_for_each_marked (&hierarchy->shapers, index, cur, -NET_SHAPER_NOT_VALID ) { +xa_for_each (&hierarchy->shapers, index, cur) { +if (xa_get_mark (&hierarchy->shapers, index,NET_SHAPER_VALID )) +continue ;__xa_erase (&hierarchy->shapers, index);kfree (cur); }
@@ -836,7 +835,8 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, goto out_unlock;for (; (shaper =xa_find (&hierarchy->shapers, &ctx->start_index, -U32_MAX ,XA_PRESENT )); ctx->start_index++) { +U32_MAX ,NET_SHAPER_VALID )); + ctx->start_index++) { ret =net_shaper_fill_one (skb, binding, shaper, info);if (ret)break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 258/377 net: shaper: fix trivial ordering issue in net_shaper_commit()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 235fb5376139c3419f2218349f1fa2f06f24f7ad ]
We should update the entry before we mark it as valid.
--- net/shaper/shaper.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
net/shaper/shaper.c +10 -1
@@ -295,6 +295,10 @@ net_shaper_lookup(struct net_shaper_binding *binding, NET_SHAPER_VALID))return NULL ; +/* Pairs with smp_wmb() in net_shaper_commit(): if the entry is +* valid, its contents must be visible too. +*/ +smp_rmb ();return xa_load (&hierarchy->shapers, index); }
@@ -412,8 +416,9 @@ static void net_shaper_commit(struct net_shaper_binding *binding,/* Successful update: drop the tentative mark * and update the hierarchy container. */ -__xa_set_mark (&hierarchy->shapers, index,NET_SHAPER_VALID ); *cur = shapers[i]; +smp_wmb (); +__xa_set_mark (&hierarchy->shapers, index,NET_SHAPER_VALID ); }xa_unlock (&hierarchy->shapers); }
@@ -837,6 +842,10 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb,for (; (shaper =xa_find (&hierarchy->shapers, &ctx->start_index,U32_MAX ,NET_SHAPER_VALID )); ctx->start_index++) { +/* Pairs with smp_wmb() in net_shaper_commit(): the entry +* is marked VALID, so its contents must be visible too. +*/ +smp_rmb (); ret =net_shaper_fill_one (skb, binding, shaper, info);if (ret)break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 259/377 net: shaper: reject duplicate leaves in GROUP request
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit a9a2fa1da619f276580b0d4c5d12efac89e8642b ]
net_shaper_nl_group_doit() does not deduplicate NET_SHAPER_A_LEAVES entries. When userspace supplies the same leaf handle twice, the same old-parent pointer lands twice in old_nodes[]. The cleanup loop double frees the parent. Of course the same parent may still be in old_nodes[] twice if we are moving multiple of its leaves.
Note that this patch also implicitly fixes the fact that the i >= leaves_count path forgets to set ret.
--- net/shaper/shaper.c | 60 +++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 15 deletions(-)
net/shaper/shaper.c +45 -15
@@ -941,6 +941,46 @@ static int net_shaper_handle_cmp(const struct net_shaper_handle *a,return memcmp (a, b,sizeof (*a)); } +static int net_shaper_parse_leaves (struct net_shaper_binding *binding, +struct genl_info *info, +const struct net_shaper *node, +struct net_shaper *leaves, +int leaves_count) +{ +struct nlattr *attr; +int i, j, ret, rem; + + i =0 ; +nla_for_each_attr_type (attr,NET_SHAPER_A_LEAVES , +genlmsg_data (info->genlhdr), +genlmsg_len (info->genlhdr), rem) { +if (WARN_ON_ONCE (i >= leaves_count)) +return -EINVAL ; + + ret =net_shaper_parse_leaf (binding, attr, info, + node, &leaves[i]); +if (ret) +return ret; + +/* Reject duplicates */ +for (j =0 ; j < i; j++) { +if (net_shaper_handle_cmp (&leaves[i].handle, + &leaves[j].handle)) +continue ; + +NL_SET_ERR_MSG_ATTR_FMT (info->extack, attr, +"Duplicate leaf shaper %d:%d" , + leaves[i].handle.scope, + leaves[i].handle.id); +return -EINVAL ; + } + + i++; + } + +return 0 ; +} + static int net_shaper_parent_from_leaves(int leaves_count, const struct net_shaper *leaves, struct net_shaper *node,
@@ -1198,10 +1238,9 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)struct net_shaper **old_nodes, *leaves, node = {};struct net_shaper_hierarchy *hierarchy;struct net_shaper_binding *binding; -int i, ret, rem, leaves_count; +int i, ret, leaves_count;int old_nodes_count =0 ;struct sk_buff *msg; -struct nlattr *attr;if (GENL_REQ_ATTR_CHECK (info,NET_SHAPER_A_LEAVES ))return -EINVAL ;
@@ -1229,19 +1268,10 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)if (ret) goto free_leaves; - i =0 ; -nla_for_each_attr_type (attr,NET_SHAPER_A_LEAVES , -genlmsg_data (info->genlhdr), -genlmsg_len (info->genlhdr), rem) { -if (WARN_ON_ONCE (i >= leaves_count)) - goto free_leaves; - - ret =net_shaper_parse_leaf (binding, attr, info, - &node, &leaves[i]); -if (ret) - goto free_leaves; - i++; - } + ret =net_shaper_parse_leaves (binding, info, &node, + leaves, leaves_count); +if (ret) + goto free_leaves;/* Prepare the msg reply in advance, to avoid device operation * rollback on allocation failure.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 260/377 net: shaper: set ret to -ENOMEM when genlmsg_new() fails in group_doit
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 8054f85b83f42a37d482fc77ea7c9ff06a9407d9 ]
genlmsg_new() alloc failure path in net_shaper_nl_group_doit() forgets to set ret before jumping to error handling.
--- net/shaper/shaper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/shaper/shaper.c +3 -1
@@ -1277,8 +1277,10 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)* rollback on allocation failure. */ msg =genlmsg_new (net_shaper_handle_size (),GFP_KERNEL ); -if (!msg) +if (!msg) { + ret = -ENOMEM ; goto free_leaves; + } hierarchy =net_shaper_hierarchy_setup (binding);if (!hierarchy) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 261/377 net: shaper: fix undersized reply skb allocation in GROUP command
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 0f9a857e34d0f8c018a3e4435c6f0e92e8d2f38c ]
net_shaper_group_send_reply() writes both the NET_SHAPER_A_IFINDEX attribute (via net_shaper_fill_binding()) and the nested NET_SHAPER_A_HANDLE attribute (via net_shaper_fill_handle()), but the reply skb at the call site in net_shaper_nl_group_doit() is allocated using net_shaper_handle_size(), which only accounts for the nested handle.
The allocation is therefore short by nla_total_size(sizeof(u32)) (8 bytes) for the IFINDEX attribute. In practice the slab allocator rounds up the small allocation so the bug is latent, but the size accounting is wrong and could bite if the reply grew further.
Introduce net_shaper_group_reply_size() that accounts for the full reply payload and use it both at the genlmsg_new() call site and in the defensive WARN_ONCE message.
--- net/shaper/shaper.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
net/shaper/shaper.c +8 -2
@@ -90,6 +90,12 @@ static int net_shaper_handle_size(void) nla_total_size(sizeof(u32))); } +static int net_shaper_group_reply_size (void ) +{ +return nla_total_size (sizeof (u32)) +/* NET_SHAPER_A_IFINDEX */ +net_shaper_handle_size ();/* NET_SHAPER_A_HANDLE */ +} +static int net_shaper_fill_binding (struct sk_buff *msg,const struct net_shaper_binding *binding,u32 type)
@@ -1228,7 +1234,7 @@ static int net_shaper_group_send_reply(struct net_shaper_binding *binding, free_msg:/* Should never happen as msg is pre-allocated with enough space. */ WARN_ONCE (true,"calculated message payload length (%d)" , -net_shaper_handle_size ()); +net_shaper_group_reply_size ());nlmsg_free (msg);return -EMSGSIZE ; }
@@ -1276,7 +1282,7 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)/* Prepare the msg reply in advance, to avoid device operation * rollback on allocation failure. */ - msg =genlmsg_new (net_shaper_handle_size (),GFP_KERNEL ); + msg =genlmsg_new (net_shaper_group_reply_size (),GFP_KERNEL );if (!msg) { ret = -ENOMEM ; goto free_leaves;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 262/377 net: shaper: reject handle IDs exceeding internal bit-width
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 8d5806c600fddb907ebe378f9c366d4b52ac3a39 ]
net_shaper_parse_handle() reads the user-supplied handle ID via nla_get_u32(), accepting the full u32 range. However, the xarray key is built by net_shaper_handle_to_index() using FIELD_PREP(NET_SHAPER_ID_MASK, handle->id), where NET_SHAPER_ID_MASK is GENMASK(25, 0) - only 26 bits wide. FIELD_PREP silently masks off the upper bits at runtime. A user-supplied NODE id like 0x04000123 becomes id 0x123.
Additionally, a user-supplied id equal to NET_SHAPER_ID_UNSPEC (0x03FFFFFF, which is NET_SHAPER_ID_MASK itself) would collide with the sentinel used internally by the group operation to signal "allocate a new NODE id".
Reject user-supplied IDs >= NET_SHAPER_ID_MASK (i.e., >= 0x03FFFFFF) in the policy.
--- Documentation/netlink/specs/net_shaper.yaml | 7 +++++++ net/shaper/shaper.c | 4 +++- net/shaper/shaper_nl_gen.c | 7 ++++++- net/shaper/shaper_nl_gen.h | 2 ++ 4 files changed, 18 insertions(+), 2 deletions(-)
Documentation/netlink/specs/net_shaper.yaml +7 -0
@@ -33,6 +33,11 @@ doc: | @cap-get operation. definitions: + - + type: const + name: max-handle-id + value: 0x3fffffe + scope: kernel - type: enum name: scope
@@ -140,6 +145,8 @@ attribute-sets: - name: id type: u32 + checks: + max: max-handle-id doc: | Numeric identifier of a shaper. The id semantic depends on the scope. For @queue scope it's the queue id and for @node
net/shaper/shaper.c +3 -1
@@ -21,6 +21,8 @@#define NET_SHAPER_ID_UNSPEC NET_SHAPER_ID_MASK +static_assert (NET_SHAPER_ID_UNSPEC ==NET_SHAPER_MAX_HANDLE_ID +1 ); +struct net_shaper_hierarchy {struct xarray shapers; };
@@ -360,7 +362,7 @@ static int net_shaper_pre_insert(struct net_shaper_binding *binding, handle->id == NET_SHAPER_ID_UNSPEC) {u32 min, max; - handle->id =NET_SHAPER_ID_MASK -1 ; + handle->id =NET_SHAPER_MAX_HANDLE_ID ; max =net_shaper_handle_to_index (handle); handle->id =0 ; min =net_shaper_handle_to_index (handle);
net/shaper/shaper_nl_gen.c +6 -1
@@ -10,10 +10,15 @@#include <uapi/linux/net_shaper.h> +/* Integer value ranges */ +static const struct netlink_range_validation net_shaper_a_handle_id_range = { + .max =NET_SHAPER_MAX_HANDLE_ID , +}; +/* Common nested types */ const struct nla_policy net_shaper_handle_nl_policy[NET_SHAPER_A_HANDLE_ID +1 ] = { [NET_SHAPER_A_HANDLE_SCOPE ] =NLA_POLICY_MAX (NLA_U32 ,3 ), - [NET_SHAPER_A_HANDLE_ID ] = { .type =NLA_U32 , }, + [NET_SHAPER_A_HANDLE_ID ] =NLA_POLICY_FULL_RANGE (NLA_U32 , &net_shaper_a_handle_id_range), };const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGHT +1 ] = {
net/shaper/shaper_nl_gen.h +2 -0
@@ -11,6 +11,8 @@#include <uapi/linux/net_shaper.h> +#define NET_SHAPER_MAX_HANDLE_ID 67108862 +/* Common nested types */ extern const struct nla_policy net_shaper_handle_nl_policy[NET_SHAPER_A_HANDLE_ID +1 ];extern const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGHT +1 ];
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 263/377 net: shaper: enforce singleton NETDEV scope with id 0
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit b62b29e6de6711f5918940aa6ff2bbab6d6af502 ]
The NETDEV scope represents a singleton root shaper in the per-device hierarchy. All code assumes NETDEV shapers have id 0:net_shaper_default_parent() hardcodes parent->id = 0 when returning the NETDEV parent for QUEUE/NODE children, and the UAPI documentation describes NETDEV scope as "the main shaper" (singular, not plural).
Make sure we reject non-0 IDs.
--- net/shaper/shaper.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/shaper/shaper.c +6 -0
@@ -482,6 +482,12 @@ static int net_shaper_parse_handle(const struct nlattr *attr,else if (handle->scope ==NET_SHAPER_SCOPE_NODE ) id =NET_SHAPER_ID_UNSPEC ; +if (id && handle->scope ==NET_SHAPER_SCOPE_NETDEV ) { +NL_SET_ERR_MSG_ATTR (info->extack, id_attr, +"Netdev scope is a singleton, must use ID 0" ); +return -EINVAL ; + } + handle->id = id;return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 264/377 net: shaper: reject QUEUE scope handle with missing id
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit ce372e869f9f492f3d5aa9a0ae75ed52c61d2d6f ]
net_shaper_parse_handle() does not enforce that the user provides the handle ID. For NODE the ID defaults to UNSPEC for all other cases it defaults to 0.
For NETDEV 0 is the only option. For QUEUE defaulting to 0 makes less intuitive sense. Specifically because the behavior should (IMHO) be the same for all cases where there may be more than one ID (QUEUE and NODE).
We should either document this as intentional or reject. I picked the latter with no strong conviction.
--- net/shaper/shaper.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
net/shaper/shaper.c +7 -2
@@ -477,10 +477,15 @@ static int net_shaper_parse_handle(const struct nlattr *attr,* shaper (any other value). */ id_attr = tb[NET_SHAPER_A_HANDLE_ID ]; -if (id_attr) +if (id_attr) { id =nla_get_u32 (id_attr); -else if (handle->scope ==NET_SHAPER_SCOPE_NODE ) + }else if (handle->scope ==NET_SHAPER_SCOPE_NODE ) { id =NET_SHAPER_ID_UNSPEC ; + }else if (handle->scope ==NET_SHAPER_SCOPE_QUEUE ) { +NL_SET_ERR_ATTR_MISS (info->extack, attr, +NET_SHAPER_A_HANDLE_ID ); +return -EINVAL ; + }if (id && handle->scope ==NET_SHAPER_SCOPE_NETDEV ) { NL_SET_ERR_MSG_ATTR(info->extack, id_attr,
Signature · 1 line
2.53.0