Skip to contentSkip to conversation

6.18.42-rc1 review

PATCH In review Greg Kroah-Hartman 13 participants 689 messages Jul 30 – Aug 2 Page 4 of 6

Subsystems (2)
lore
Share & tools
lore
author maintainer
#381 lore raw

PATCH 381/675 drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Leo Li <[email protected]>

commit 8382cd234981ae36299bb66a10bac2cd8ff1b99d upstream.

[Why]

On DCN, vblank events were delivered from VSTARTUP/VUPDATE (dm_crtc_high_irq/dm_vupdate_high_irq) and pageflip completion from GRPH_PFLIP (dm_pflip_high_irq). These signals can be masked by hardware by a few things:

* DPG - DCN can Dynamically Power Gate parts of the display pipe when a self-refresh capable eDP is connected. DPG is engaged when there's enough static frames (detected through drm_vblank_off). Once gated, even though the OTG (output timing generator) is still enabled, VSTARTUP and GRPH_FLIP are masked.

* GSL - Driver can use the Global Sync Lock to block HW from latching onto double-buffered registers during programming, to prevent HW from latching onto a partially programmed state. This will mask VSTARTUP, GRPH_FLIP, and VUPDATE. See dcn20_pipe_control_lock().

* MALL - A DCN accessible cache introduced in DCN32+ DGPUs that can store fb data to allow for longer DRAM sleep. When scanning out from MALL, VSTARTUP is masked.

When masked, events are never delivered, which can show up as flip_done timeouts in the wild.

However, there is an interrupt source on DCN that is never masked:
VUPDATE_NO_LOCK. It's simply an unmasked variant of VUPDATE, which fires while the OTG is active, at the exact point hardware latches double-buffered registers. It is therefore the natural single signal for delivering both vblank and flip-completion events on DCN, and the correct point to timestamp both VRR and non-VRR vblanks.

DCE's interrupt sources are different, it does not have an unmaskable VUPDATE_NO_LOCK. The only unmaskable DCE interrupt is VLINE0, but it can only be programmed as a vline offset from vsync_start, making it unsuitable for VRR. Thus, we keep DCE untouched and use the existing mix of interrupt sources.

[How]

For DCN1 and newer only:

* Factor the body of dm_crtc_high_irq() into dm_crtc_high_irq_handler() and drive it from dm_vupdate_high_irq() (VUPDATE_NO_LOCK). DCE keeps using dm_crtc_high_irq() (VSTARTUP) and dm_pflip_high_irq() (GRPH_PFLIP) unchanged.

* Stop registering VSTARTUP (crtc_irq) and GRPH_PFLIP (pageflip_irq) on DCN, and stop enabling them in amdgpu_dm_crtc_set_vblank() / manage_dm_interrupts(). Enable VUPDATE whenever vblank is enabled on DCN (previously only in VRR mode). The secure-display vline0 interrupt is left untouched.

* VUPDATE_NO_LOCK does not early-fire on an immediate (tearing / async) flip, since HW latches the new address right away. Deliver the flip completion event immediately after programming such flips in amdgpu_dm_commit_planes(), and clear pflip_status so the next vupdate handler does not double-send.

v2: Do not gate VUPDATE_NO_LOCK on DCN in dm_handle_vrr_transition()
Also toggle VUPDATE_NO_LOCK on DCN in dm_gpureset_toggle_interrupts() Re-cook vblank event count and timestamp for immediate flips

Fixes 9b47278cec98 ("drm/amd/display: temp w/a for dGPU to enter idle optimizations")
Assisted-by Copilot:claude-opus-4.8
(cherry picked from commit c87e6635d2db02c88ae8d09529362da672d34770)
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c      |  349 +++++++++--------
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c |   70 ++-
 2 files changed, 229 insertions(+), 190 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +187 -162
@@ -575,89 +575,25 @@ static void schedule_dc_vmin_vmax(struct
 	queue_work(system_wq, &offload_work->work);
 }
 
-static void dm_vupdate_high_irq(void *interrupt_params)
-{
-	struct common_irq_params *irq_params = interrupt_params;
-	struct amdgpu_device *adev = irq_params->adev;
-	struct amdgpu_crtc *acrtc;
-	struct drm_device *drm_dev;
-	struct drm_vblank_crtc *vblank;
-	ktime_t frame_duration_ns, previous_timestamp;
-	unsigned long flags;
-	int vrr_active;
-
-	acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
-
-	if (acrtc) {
-		vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
-		drm_dev = acrtc->base.dev;
-		vblank = drm_crtc_vblank_crtc(&acrtc->base);
-		previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
-		frame_duration_ns = vblank->time - previous_timestamp;
-
-		if (frame_duration_ns > 0) {
-			trace_amdgpu_refresh_rate_track(acrtc->base.index,
-						frame_duration_ns,
-						ktime_divns(NSEC_PER_SEC, frame_duration_ns));
-			atomic64_set(&irq_params->previous_timestamp, vblank->time);
-		}
-
-		drm_dbg_vbl(drm_dev,
-			    "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
-			    vrr_active);
-
-		/* Core vblank handling is done here after end of front-porch in
-		 * vrr mode, as vblank timestamping will give valid results
-		 * while now done after front-porch. This will also deliver
-		 * page-flip completion events that have been queued to us
-		 * if a pageflip happened inside front-porch.
-		 */
-		if (vrr_active && acrtc->dm_irq_params.stream) {
-			bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
-			bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
-			bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
-				== VRR_STATE_ACTIVE_VARIABLE;
-
-			amdgpu_dm_crtc_handle_vblank(acrtc);
-
-			/* BTR processing for pre-DCE12 ASICs */
-			if (adev->family < AMDGPU_FAMILY_AI) {
-				spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
-				mod_freesync_handle_v_update(
-				    adev->dm.freesync_module,
-				    acrtc->dm_irq_params.stream,
-				    &acrtc->dm_irq_params.vrr_params);
-
-				if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
-					schedule_dc_vmin_vmax(adev,
-						acrtc->dm_irq_params.stream,
-						&acrtc->dm_irq_params.vrr_params.adjust);
-				}
-				spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-			}
-		}
-	}
-}
-
 /**
- * dm_crtc_high_irq() - Handles CRTC interrupt
- * @interrupt_params: used for determining the CRTC instance
+ * dm_crtc_high_irq_handler() - Common OTG vblank/flip event handling
+ * @adev: amdgpu device
+ * @acrtc: the CRTC to service
  *
- * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK
- * event handler.
+ * Performs writeback completion, vblank event handling, CRC processing, VRR BTR
+ * updates and pageflip completion delivery.
+ *
+ * On DCN this is driven by VUPDATE_NO_LOCK (the register latch point) from
+ * dm_vupdate_high_irq(); on DCE it is driven by VLINE0 at the start of vblank
+ * from dm_crtc_high_irq().
  */
-static void dm_crtc_high_irq(void *interrupt_params)
+static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
+				     struct amdgpu_crtc *acrtc)
 {
-	struct common_irq_params *irq_params = interrupt_params;
-	struct amdgpu_device *adev = irq_params->adev;
 	struct drm_writeback_job *job;
-	struct amdgpu_crtc *acrtc;
 	unsigned long flags;
 	int vrr_active;
-
-	acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
-	if (!acrtc)
-		return;
+	bool is_dcn = amdgpu_ip_version(adev, DCE_HWIP, 0) != 0;
 
 	if (acrtc->wb_conn) {
 		spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
@@ -694,12 +630,17 @@ static void dm_crtc_high_irq(void *inter
 		    vrr_active, acrtc->dm_irq_params.active_planes);
 
 	/**
-	 * Core vblank handling at start of front-porch is only possible
-	 * in non-vrr mode, as only there vblank timestamping will give
-	 * valid results while done in front-porch. Otherwise defer it
-	 * to dm_vupdate_high_irq after end of front-porch.
+	 * Core vblank handling.
+	 *
+	 * On DCN this handler runs at VUPDATE_NO_LOCK, the register latch
+	 * point, which is the correct place to timestamp both VRR and non-VRR
+	 * vblanks.
+	 *
+	 * On DCE this handler runs at the start of front-porch, where only
+	 * non-VRR timestamping is valid; VRR vblank is deferred to
+	 * dm_vupdate_high_irq() after end of front-porch.
 	 */
-	if (!vrr_active)
+	if (is_dcn || !vrr_active)
 		amdgpu_dm_crtc_handle_vblank(acrtc);
 
 	/**
@@ -732,18 +673,16 @@ static void dm_crtc_high_irq(void *inter
 	}
 
 	/*
-	 * If there aren't any active_planes then DCH HUBP may be clock-gated.
-	 * In that case, pageflip completion interrupts won't fire and pageflip
-	 * completion events won't get delivered. Prevent this by sending
-	 * pending pageflip events from here if a flip is still pending.
+	 * Deliver pageflip completion events (DCN only).
 	 *
-	 * If any planes are enabled, use dm_pflip_high_irq() instead, to
-	 * avoid race conditions between flip programming and completion,
-	 * which could cause too early flip completion events.
-	 */
-	if (adev->family >= AMDGPU_FAMILY_RV &&
-	    acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
-	    acrtc->dm_irq_params.active_planes == 0) {
+	 * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch
+	 * point. Deliver any pending pageflip completion event from here.
+	 *
+	 * NOTE: This can deliver an event for a flip that was armed but not yet
+	 * programmed into HW; that race is closed in a follow-up change by
+	 * checking the programmed flip status.
+	 */
+	if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED) {
 		if (acrtc->event) {
 			drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
 			acrtc->event = NULL;
@@ -755,6 +694,104 @@ static void dm_crtc_high_irq(void *inter
 	spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
 }
 
+static void dm_vupdate_high_irq(void *interrupt_params)
+{
+	struct common_irq_params *irq_params = interrupt_params;
+	struct amdgpu_device *adev = irq_params->adev;
+	struct amdgpu_crtc *acrtc;
+	struct drm_device *drm_dev;
+	struct drm_vblank_crtc *vblank;
+	ktime_t frame_duration_ns, previous_timestamp;
+	unsigned long flags;
+	int vrr_active;
+
+	acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
+	if (!acrtc)
+		return;
+
+	vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
+	drm_dev = acrtc->base.dev;
+	vblank = drm_crtc_vblank_crtc(&acrtc->base);
+	previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
+	frame_duration_ns = vblank->time - previous_timestamp;
+
+	if (frame_duration_ns > 0) {
+		trace_amdgpu_refresh_rate_track(acrtc->base.index,
+					frame_duration_ns,
+					ktime_divns(NSEC_PER_SEC, frame_duration_ns));
+		atomic64_set(&irq_params->previous_timestamp, vblank->time);
+	}
+
+	drm_dbg_vbl(drm_dev,
+		    "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
+		    vrr_active);
+
+	/*
+	 * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
+	 * vblank and pageflip completion events; VSTARTUP and GRPH_PFLIP are
+	 * not used. Run the full handler here.
+	 */
+	if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
+		dm_crtc_high_irq_handler(adev, acrtc);
+		return;
+	}
+
+	/* DCE only below. */
+
+	/* Core vblank handling is done here after end of front-porch in
+	 * vrr mode, as vblank timestamping will give valid results
+	 * while now done after front-porch. This will also deliver
+	 * page-flip completion events that have been queued to us
+	 * if a pageflip happened inside front-porch.
+	 */
+	if (vrr_active && acrtc->dm_irq_params.stream) {
+		bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
+		bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
+		bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
+			== VRR_STATE_ACTIVE_VARIABLE;
+
+		amdgpu_dm_crtc_handle_vblank(acrtc);
+
+		/* BTR processing for pre-DCE12 ASICs */
+		if (adev->family < AMDGPU_FAMILY_AI) {
+			spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
+			mod_freesync_handle_v_update(
+				adev->dm.freesync_module,
+				acrtc->dm_irq_params.stream,
+				&acrtc->dm_irq_params.vrr_params);
+
+			if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
+				schedule_dc_vmin_vmax(adev,
+					acrtc->dm_irq_params.stream,
+					&acrtc->dm_irq_params.vrr_params.adjust);
+			}
+			spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
+		}
+	}
+}
+
+/**
+ * dm_crtc_high_irq() - Handles CRTC interrupt
+ * @interrupt_params: used for determining the CRTC instance
+ *
+ * Handles the CRTC/VSYNC interrupt by notifying DRM's VBLANK event handler.
+ *
+ * Used on DCE (VLINE0, set to vblank start). On DCN the equivalent handling is
+ * driven by VUPDATE_NO_LOCK in dm_vupdate_high_irq().
+ */
+static void dm_crtc_high_irq(void *interrupt_params)
+{
+	struct common_irq_params *irq_params = interrupt_params;
+	struct amdgpu_device *adev = irq_params->adev;
+	struct amdgpu_crtc *acrtc;
+
+	acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
+	if (!acrtc)
+		return;
+
+	dm_crtc_high_irq_handler(adev, acrtc);
+}
+
 #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
 /**
  * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
@@ -3059,6 +3096,13 @@ static void dm_gpureset_toggle_interrupt
 			 */
 			if (!dc_interrupt_set(adev->dm.dc, irq_source, enable))
 				drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis");
+
+		} else if (acrtc && state->stream_status[i].plane_count != 0) {
+			/* DCN only needs to toggle VUPDATE_NO_LOCK */
+			rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, enable);
+			if (rc)
+				drm_warn(adev_to_drm(adev), "Failed to %sable vupdate interrupt\n",
+					 enable ? "en" : "dis");
 		}
 	}
 
@@ -4558,38 +4602,6 @@ static int dcn10_register_irq_handlers(s
 	 *    for acknowledging and handling.
 	 */
 
-	/* Use VSTARTUP interrupt */
-	for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP;
-			i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1;
-			i++) {
-		r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq);
-
-		if (r) {
-			drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
-			return r;
-		}
-
-		int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
-		int_params.irq_source =
-			dc_interrupt_to_irq_source(dc, i, 0);
-
-		if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
-			int_params.irq_source  < DC_IRQ_SOURCE_VBLANK1 ||
-			int_params.irq_source  > DC_IRQ_SOURCE_VBLANK6) {
-			drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
-			return -EINVAL;
-		}
-
-		c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
-
-		c_irq_params->adev = adev;
-		c_irq_params->irq_src = int_params.irq_source;
-
-		if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
-			dm_crtc_high_irq, c_irq_params))
-			return -ENOMEM;
-	}
-
 	/* Use otg vertical line interrupt */
 #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
 	for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) {
@@ -4661,37 +4673,6 @@ static int dcn10_register_irq_handlers(s
 			return -ENOMEM;
 	}
 
-	/* Use GRPH_PFLIP interrupt */
-	for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT;
-			i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1;
-			i++) {
-		r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq);
-		if (r) {
-			drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
-			return r;
-		}
-
-		int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
-		int_params.irq_source =
-			dc_interrupt_to_irq_source(dc, i, 0);
-
-		if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
-			int_params.irq_source  < DC_IRQ_SOURCE_PFLIP_FIRST ||
-			int_params.irq_source  > DC_IRQ_SOURCE_PFLIP_LAST) {
-			drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
-			return -EINVAL;
-		}
-
-		c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
-
-		c_irq_params->adev = adev;
-		c_irq_params->irq_src = int_params.irq_source;
-
-		if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
-			dm_pflip_high_irq, c_irq_params))
-			return -ENOMEM;
-	}
-
 	/* HPD */
 	r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT,
 			&adev->hpd_irq);
@@ -9118,14 +9099,22 @@ static void manage_dm_interrupts(struct
 
 		drm_crtc_vblank_on_config(&acrtc->base,
 					  &config);
-		/* Allow RX6xxx, RX7700, RX7800 GPUs to call amdgpu_irq_get.*/
+		/*
+		 * Since pflip_high_irq is no longer registered for DCN, grab an
+		 * extra reference to vupdate irq instead to workaround this
+		 * issue:
+		 * https://gitlab.freedesktop.org/drm/amd/-/work_items/3936
+		 *
+		 * The callbacks to drm_vblank_on/off should really take care of
+		 * this though.
+		 */
 		switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
 		case IP_VERSION(3, 0, 0):
 		case IP_VERSION(3, 0, 2):
 		case IP_VERSION(3, 0, 3):
 		case IP_VERSION(3, 2, 0):
-			if (amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type))
-				drm_err(dev, "DM_IRQ: Cannot get pageflip irq!\n");
+			if (amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type))
+				drm_err(dev, "DM_IRQ: Cannot get vupdate irq!\n");
 #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
 			if (amdgpu_irq_get(adev, &adev->vline0_irq, irq_type))
 				drm_err(dev, "DM_IRQ: Cannot get vline0 irq!\n");
@@ -9143,8 +9132,8 @@ static void manage_dm_interrupts(struct
 			if (amdgpu_irq_put(adev, &adev->vline0_irq, irq_type))
 				drm_err(dev, "DM_IRQ: Cannot put vline0 irq!\n");
 #endif
-			if (amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type))
-				drm_err(dev, "DM_IRQ: Cannot put pageflip irq!\n");
+			if (amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type))
+				drm_err(dev, "DM_IRQ: Cannot put vupdate irq!\n");
 		}
 
 		drm_crtc_vblank_off(&acrtc->base);
@@ -9157,6 +9146,10 @@ static void dm_update_pflip_irq_state(st
 	int irq_type =
 		amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);
 
+	/* GRPH_PFLIP is not used on DCN; nothing to reapply. */
+	if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0)
+		return;
+
 	/**
 	 * This reads the current state for the IRQ and force reapplies
 	 * the setting to hardware.
@@ -9488,9 +9481,13 @@ static void update_stream_irq_parameters
 static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
 					    struct dm_crtc_state *new_state)
 {
+	struct amdgpu_device *adev = drm_to_adev(new_state->base.crtc->dev);
 	bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
 	bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);
 
+	/* Only DCE gates vupdate on VRR, keep it enabled for DCN */
+	bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0;
+
 	if (!old_vrr_active && new_vrr_active) {
 		/* Transition VRR inactive -> active:
 		 * While VRR is active, we must not disable vblank irq, as a
@@ -9500,7 +9497,8 @@ static void amdgpu_dm_handle_vrr_transit
 		 * We also need vupdate irq for the actual core vblank handling
 		 * at end of vblank.
 		 */
-		WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
+		if (vrr_gates_vupdate)
+			WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
 		WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
 		drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n",
 				 __func__, new_state->base.crtc->base.id);
@@ -9508,7 +9506,8 @@ static void amdgpu_dm_handle_vrr_transit
 		/* Transition VRR active -> inactive:
 		 * Allow vblank irq disable again for fixed refresh rate.
 		 */
-		WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
+		if (vrr_gates_vupdate)
+			WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
 		drm_crtc_vblank_put(new_state->base.crtc);
 		drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n",
 				 __func__, new_state->base.crtc->base.id);
@@ -9683,6 +9682,7 @@ static void amdgpu_dm_commit_planes(stru
 	bool vrr_active = amdgpu_dm_crtc_vrr_active(acrtc_state);
 	bool cursor_update = false;
 	bool pflip_present = false;
+	bool immediate_flip = false;
 	bool dirty_rects_changed = false;
 	bool updated_planes_and_streams = false;
 	struct {
@@ -9848,6 +9848,8 @@ static void amdgpu_dm_commit_planes(stru
 			acrtc_state->update_type == UPDATE_TYPE_FAST &&
 			get_mem_type(old_plane_state->fb) == get_mem_type(fb);
 
+		immediate_flip |= bundle->flip_addrs[planes_count].flip_immediate;
+
 		timestamp_ns = ktime_get_ns();
 		bundle->flip_addrs[planes_count].flip_timestamp_in_us = div_u64(timestamp_ns, 1000);
 		bundle->surface_updates[planes_count].flip_addr = &bundle->flip_addrs[planes_count];
@@ -10049,6 +10051,29 @@ static void amdgpu_dm_commit_planes(stru
 	    acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE)
 		amdgpu_dm_commit_cursors(state);
 
+	/*
+	 * On DCN, flip completion is normally delivered from VUPDATE_NO_LOCK.
+	 * However, an immediate (tearing / async) flip is latched by HW right
+	 * away and does not wait for the next vupdate, so deliver its
+	 * completion event here after programming.
+	 *
+	 * On DCE, GRPH_PFLIP already fires immediately for immediate flips, so
+	 * this is DCN-only.
+	 */
+	if (immediate_flip && amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) {
+		spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
+		if (acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED &&
+		    acrtc_attach->event) {
+			drm_crtc_accurate_vblank_count(&acrtc_attach->base);
+			drm_crtc_send_vblank_event(&acrtc_attach->base,
+						   acrtc_attach->event);
+			acrtc_attach->event = NULL;
+			drm_crtc_vblank_put(&acrtc_attach->base);
+			acrtc_attach->pflip_status = AMDGPU_FLIP_NONE;
+		}
+		spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
+	}
+
 cleanup:
 	kfree(bundle);
 }
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +42 -28

85 lines not on this page · raw message

author maintainer
#382 lore raw

PATCH 382/675 drm/amd/display: dce100: skip non-DP stream encoders for DP MST

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Andriy Korud <[email protected]>

commit d340cba0df4cf327c7e89c7c1a4e79d4771d7dd5 upstream.

On DCE8-class ASICs (e.g. Bonaire), the resource pool contains digital DIG stream encoders plus one analog DAC encoder. When assigning a stream encoder for a second DisplayPort MST stream, if the preferred digital encoder is already acquired, dce100_find_first_free_match_stream_enc_for_link() falls back to the first free pool entry. That entry may be the analog encoder, whose funcs table lacks DP hooks such as dp_set_stream_attribute. The subsequent atomic commit then dereferences NULL function pointers in link_set_dpms_on() and crashes.

Skip encoders without dp_set_stream_attribute when the stream uses a DP signal (including MST). Use dc_is_dp_signal(stream->signal) for the MST fallback path instead of checking only the link connector signal.

Tested on:
- GPU: AMD Radeon R7 260X (Bonaire / DCE8)
- Board: Supermicro C9X299-PG300
- Setup: DP MST daisy chain, hotplug second monitor or have it connected on boot
- Kernel: 7.1.3 (issue observed since 6.19)
- Result: kernel oops without patch; dual monitors stable with patch

(cherry picked from commit 28ec64943e3ee4d9b8d30cea61e380f1429953a8)
---
 drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c +6 -1
@@ -957,6 +957,11 @@ struct stream_encoder *dce100_find_first
 	for (i = 0; i < pool->stream_enc_count; i++) {
 		if (!res_ctx->is_stream_enc_acquired[i] &&
 				pool->stream_enc[i]) {
+			/* DP/MST needs a digital encoder; skip analog/no-DP encoders */
+			if (dc_is_dp_signal(stream->signal) &&
+			    (!pool->stream_enc[i]->funcs ||
+			     !pool->stream_enc[i]->funcs->dp_set_stream_attribute))
+				continue;
 			/* Store first available for MST second display
 			 * in daisy chain use case
 			 */
@@ -980,7 +985,7 @@ struct stream_encoder *dce100_find_first
 	 * required for non DP connectors.
 	 */
 
-	if (j >= 0 && link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT)
+	if (j >= 0 && dc_is_dp_signal(stream->signal))
 		return pool->stream_enc[j];
 
 	return NULL;
author maintainer
#383 lore raw

PATCH 383/675 drm/amd/display: Force PWM backlight on Lenovo Legion 5 15ARH05

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alessandro Rinaldi <[email protected]>

commit bad177fa75e607e396cd57daaaed881450d7a471 upstream.

The Lenovo Legion 5 15ARH05 (Renoir) ships a BOE 0x08DF eDP panel that advertises AUX/DPCD backlight control, so amdgpu's automatic detection (amdgpu_backlight == -1) selects AUX. On this panel the AUX backlight path has no effect: brightness writes are accepted but the panel level never changes, the display is stuck at a fixed brightness and max_brightness is reported as a bogus 511000. As a result neither the desktop brightness slider nor the brightness hotkeys do anything.

Forcing PWM backlight (amdgpu.backlight=0) restores working control:
max_brightness becomes 65535 and the level tracks writes. This has long been applied by users as a manual kernel-parameter workaround.

Extend the generic panel backlight quirk with a force_pwm flag, add an entry for the Legion 5 15ARH05 / BOE 0x08DF panel, and have amdgpu disable AUX backlight (use PWM) when the quirk matches and the user lets the driver auto-select the backlight type.

(cherry picked from commit 81b39f43e7e53589491e2eef6bad5389626b4b9c)
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |    7 +++++--
 drivers/gpu/drm/drm_panel_backlight_quirks.c      |    9 +++++++++
 include/drm/drm_utils.h                           |    1 +
 3 files changed, 15 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +5 -2
@@ -3724,6 +3724,8 @@ static void update_connector_ext_caps(st
 	caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
 	caps->aux_support = false;
 
+	panel_backlight_quirk = drm_get_panel_backlight_quirk(aconnector->drm_edid);
+
 	if (caps->ext_caps->bits.oled == 1
 	    /*
 	     * ||
@@ -3736,6 +3738,9 @@ static void update_connector_ext_caps(st
 		caps->aux_support = false;
 	else if (amdgpu_backlight == 1)
 		caps->aux_support = true;
+	else if (!IS_ERR_OR_NULL(panel_backlight_quirk) &&
+		 panel_backlight_quirk->force_pwm)
+		caps->aux_support = false;
 	if (caps->aux_support)
 		aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
 
@@ -3751,8 +3756,6 @@ static void update_connector_ext_caps(st
 	else
 		caps->aux_min_input_signal = 1;
 
-	panel_backlight_quirk =
-		drm_get_panel_backlight_quirk(aconnector->drm_edid);
 	if (!IS_ERR_OR_NULL(panel_backlight_quirk)) {
 		if (panel_backlight_quirk->min_brightness) {
 			caps->min_input_signal =
drivers/gpu/drm/drm_panel_backlight_quirks.c +9 -0
@@ -21,6 +21,15 @@ struct drm_get_panel_backlight_quirk {
 };
 
 static const struct drm_get_panel_backlight_quirk drm_panel_min_backlight_quirks[] = {
+	/* Lenovo Legion 5 15ARH05, AUX backlight non-functional, force PWM */
+	{
+		.dmi_match.field = DMI_SYS_VENDOR,
+		.dmi_match.value = "LENOVO",
+		.dmi_match_other.field = DMI_PRODUCT_VERSION,
+		.dmi_match_other.value = "Lenovo Legion 5 15ARH05",
+		.ident.panel_id = drm_edid_encode_panel_id('B', 'O', 'E', 0x08df),
+		.quirk = { .force_pwm = true, },
+	},
 	/* 13 inch matte panel */
 	{
 		.dmi_match.field = DMI_BOARD_VENDOR,
include/drm/drm_utils.h +1 -0
@@ -19,6 +19,7 @@ int drm_get_panel_orientation_quirk(int
 struct drm_panel_backlight_quirk {
 	u16 min_brightness;
 	u32 brightness_mask;
+	bool force_pwm;
 };
 
 const struct drm_panel_backlight_quirk *
author maintainer
#384 lore raw

PATCH 384/675 drm/amd/display: Fix backlight max_brightness to match exported range

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mario Limonciello <[email protected]>

commit f1b5d8f9cc54ae8a2567ac126867ae488e1bf625 upstream.

[Why]
FWTS autobrightness fails on eDP panels because actual_brightness can read higher than the advertised max_brightness (e.g. 63576 vs 62451).

The conversion helpers expose the firmware PWM range to userspace as [0..max]. But max_brightness is advertised as (max - min), which is smaller. So reading the level can return a value above max_brightness.

This regressed in commit 4b61b8a39051 ("drm/amd/display: Add debugging message for brightness caps"), which changed max_brightness to (max - min) and undid commit 8dbd72cb7900 ("drm/amd/display: Export full brightness range to userspace").

[How]
Advertise max_brightness as max, and scale the initial AC/DC brightness against max too. Update the KUnit expectations to match.

Fixes 4b61b8a39051 ("drm/amd/display: Add debugging message for brightness caps")
(cherry picked from commit bd9e2b5b0473c75abc0f4134dfe79ecbfb16610d)
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +3 -3
@@ -5204,11 +5204,11 @@ amdgpu_dm_register_backlight_device(stru
 	caps = &dm->backlight_caps[aconnector->bl_idx];
 	if (get_brightness_range(caps, &min, &max)) {
 		if (power_supply_is_system_supplied() > 0)
-			props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level, 100);
+			props.brightness = DIV_ROUND_CLOSEST(max * caps->ac_level, 100);
 		else
-			props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level, 100);
+			props.brightness = DIV_ROUND_CLOSEST(max * caps->dc_level, 100);
 		/* min is zero, so max needs to be adjusted */
-		props.max_brightness = max - min;
+		props.max_brightness = max;
 		drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max,
 			caps->ac_level, caps->dc_level);
 	} else
author maintainer
#385 lore raw

PATCH 385/675 drm/amdgpu: Disable PCIe dynamic speed switching on Ryzen Pinnacle Ridge

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mario Limonciello <[email protected]>

commit 0148ac33547b9af1c5a7f3bb6e5baffcb6e9fac2 upstream.

AMD Ryzen Pinnacle Ridge (Zen+, family 0x17 model 0x08) CPUs have PCI controllers that don't support PCIe dynamic speed switching, causing system freezes during GPU initialization when enabled.

Disable dynamic speed switching when this CPU is detected.

Assisted-by Claude:sonnet
Fixes 466a7d115326 ("drm/amd: Use the first non-dGPU PCI device for BW limits")
(cherry picked from commit 9ceb4e034a327a04155f32f1cd1a5031dfa5fe02)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c |    9 +++++++++
 1 file changed, 9 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +9 -0
@@ -1878,6 +1878,15 @@ static bool amdgpu_device_pcie_dynamic_s
 
 	if (c->x86_vendor == X86_VENDOR_INTEL)
 		return false;
+
+	/*
+	 * AMD Ryzen Pinnacle Ridge (Zen+, family 0x17 model 0x08) CPUs don't
+	 * support PCIe dynamic speed switching.
+	 * https://gitlab.freedesktop.org/drm/amd/-/work_items/5436
+	 */
+	if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 0x17 &&
+	    c->x86_model == 0x08)
+		return false;
 #endif
 	return true;
 }
author maintainer
#386 lore raw

PATCH 386/675 drm/amdgpu: fix bo->pin leaking in amdgpu_bo_create_reserved

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhu Lingshan <[email protected]>

commit a2f895f3c852063258d62e9f74b081de07ca95df upstream.

amdgpu_bo_create_reserved() only allocates a new BO when *bo_ptr (struct amdgpu_bo **bo_ptr as input parameter) is NULL, it simply skips creation when *bo_ptr is non-NULL. But it unconditionally reserves, pins, gart allocates and maps the BO afterwards.

When the same non-NULL BO pointer is passed in again,
for example firmware buffers that live in adev and are re-loaded on every resume / cp_resume / start
under AMDGPU_FW_LOAD_DIRECT, amdgpu_bo_pin() just increases pin_count unconditionally, however the matching teardown only unpins once, so pin_count never drops to zero, so TTM is not able to move, swap or evict a BO, causing BO leaks.

This commit fixes this issue by only pinning the bo once at creation, and repeated calls no longer take additional pin references.

(cherry picked from commit 3ddc0ae76202c447b6aec61e907b852bc94671cf)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_object.c |   13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +8 -5
@@ -284,10 +284,12 @@ int amdgpu_bo_create_reserved(struct amd
 		goto error_free;
 	}
 
-	r = amdgpu_bo_pin(*bo_ptr, domain);
-	if (r) {
-		dev_err(adev->dev, "(%d) kernel bo pin failed\n", r);
-		goto error_unreserve;
+	if (free) {
+		r = amdgpu_bo_pin(*bo_ptr, domain);
+		if (r) {
+			dev_err(adev->dev, "(%d) kernel bo pin failed\n", r);
+			goto error_unreserve;
+		}
 	}
 
 	r = amdgpu_ttm_alloc_gart(&(*bo_ptr)->tbo);
@@ -310,7 +312,8 @@ int amdgpu_bo_create_reserved(struct amd
 	return 0;
 
 error_unpin:
-	amdgpu_bo_unpin(*bo_ptr);
+	if (free)
+		amdgpu_bo_unpin(*bo_ptr);
 error_unreserve:
 	amdgpu_bo_unreserve(*bo_ptr);
 
author maintainer
#387 lore raw

PATCH 387/675 drm/amd/display: Fix flip-done timeouts on mode1 reset

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Leo Li <[email protected]>

commit 82730dba0cf9d9524af0ceeb7eb6b5c3ab1bdb87 upstream.

The vblank on/off callbacks mixed use of amdgpu_irq_get/put() and amdgpu_dm_crtc_set_vupdate_irq() to enable and disable IRQs.

With get/put, base driver will callback into DC to disable IRQs when refcount == 0. With set_vupdate_irq(), DC is called directly to disable IRQs, bypassing base driver's refcount tracking.

During gpu reset, base driver can restore IRQs via amdgpu_irq_gpu_reset_resume_helper() > amdgpu_irq_update(). So if get/put() is not used (i.e. refcount == 0), then vupdate_irq will be disabled.

This is problematic if DRM requests vblank on before amdgpu_irq_update() is called: drm_vblank_on() > set_vupdate_irq() enables vupdate_irq, but the refcount is still 0. gpu_reset_resume_helper() > irq_update() then immediately disables it, thus leading to flip done timeouts.

This is made worse on DCN since VUPDATE_NO_LOCK is the only IRQ enabled. Prior to 8382cd234981, a combination of GRPH_FLIP and VSTARTUP IRQs were used, and they used get/put(). This explains why 8382cd234981 exposed this issue.

Fix by using get/put() instead of set_vupdate_irq(). DCE is unchanged, since it relies on unbalanced enable/disable calls based on VRR status, and hence requires direct set_vupdate_irq(). Plus, it also uses GRPH_FLIP and VLINE IRQs, which are properly tracked by get/put().

Fixes 8382cd234981 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock")
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +11 -2
@@ -333,10 +333,19 @@ static inline int amdgpu_dm_crtc_set_vbl
 	 * is enabled. On DCE, vupdate is only needed in VRR mode.
 	 */
 	if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
-		rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable);
+		if (enable) {
+			rc = amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type);
+			drm_dbg_vbl(crtc->dev, "Get vupdate_irq ret=%d\n", rc);
+		} else {
+			rc = amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type);
+			drm_dbg_vbl(crtc->dev, "Put vupdate_irq ret=%d\n", rc);
+		}
 	} else if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
 		if (enable) {
-			/* vblank irq on -> Only need vupdate irq in vrr mode */
+			/* vblank irq on -> Only need vupdate irq in vrr mode
+			 * Not ref-counted since we need explicit enable/disable
+			 * for DCE VRR handling
+			 */
 			if (amdgpu_dm_crtc_vrr_active(acrtc_state))
 				rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true);
 		} else {
author maintainer
#388 lore raw

PATCH 388/675 drm/vc4: Shut down BO cache timer before teardown

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Linmao Li <[email protected]>

commit 6273dd3ffb54ec581855b82ae77331b66028249c upstream.

The BO cache timer callback schedules time_work, and time_work can rearm the timer through vc4_bo_cache_free_old().

vc4_bo_cache_destroy() deletes the timer and then cancels the work, which does not break that cycle: the work being cancelled can rearm the timer, and the timer then queues work again after teardown.

Use timer_shutdown_sync() instead, so the timer cannot be rearmed and the cycle ends with cancel_work_sync().

---
 drivers/gpu/drm/vc4/vc4_bo.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/vc4/vc4_bo.c +1 -1
@@ -1046,7 +1046,7 @@ static void vc4_bo_cache_destroy(struct
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	int i;
 
-	timer_delete(&vc4->bo_cache.time_timer);
+	timer_shutdown_sync(&vc4->bo_cache.time_timer);
 	cancel_work_sync(&vc4->bo_cache.time_work);
 
 	vc4_bo_cache_purge(dev);
author maintainer
#389 lore raw

PATCH 389/675 drm/amd/display: Fix missing DCE check in dm_gpureset_toggle_interrupts()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Leo Li <[email protected]>

commit fbbaca9e208733652828ea98d00f09f260a7770e upstream.

This line was lost when cping from amd-staging-drm-next to drm-fixes. So add it back.

Fixes 8382cd234981 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock")
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +2 -1
@@ -3068,7 +3068,8 @@ static void dm_gpureset_toggle_interrupt
 		acrtc = get_crtc_by_otg_inst(
 				adev, state->stream_status[i].primary_otg_inst);
 
-		if (acrtc && state->stream_status[i].plane_count != 0) {
+		if (acrtc && state->stream_status[i].plane_count != 0 &&
+		    amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) {
 			irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst;
 			rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
 			if (rc)
author maintainer
#390 lore raw

PATCH 390/675 drm/vmwgfx: Validate vmw_surface_metadata::array_size

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ian Forbes <[email protected]>

commit a4f55260f7f7d4dc4d0ee55063dfb0c457b77991 upstream.

This field comes from userspace and should be validated against specific limits depending on which Shader Model (SM) is available.

---
 drivers/gpu/drm/vmwgfx/vmwgfx_surface.c |   22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +17 -5
@@ -77,7 +77,7 @@ static int vmw_gb_surface_unbind(struct
 static int vmw_gb_surface_destroy(struct vmw_resource *res);
 static int
 vmw_gb_surface_define_internal(struct drm_device *dev,
-			       struct drm_vmw_gb_surface_create_ext_req *req,
+			       const  struct drm_vmw_gb_surface_create_ext_req *req,
 			       struct drm_vmw_gb_surface_create_rep *rep,
 			       struct drm_file *file_priv);
 static int
@@ -1503,7 +1503,7 @@ int vmw_gb_surface_reference_ext_ioctl(s
  */
 static int
 vmw_gb_surface_define_internal(struct drm_device *dev,
-			       struct drm_vmw_gb_surface_create_ext_req *req,
+			       const  struct drm_vmw_gb_surface_create_ext_req *req,
 			       struct drm_vmw_gb_surface_create_rep *rep,
 			       struct drm_file *file_priv)
 {
@@ -1521,9 +1521,21 @@ vmw_gb_surface_define_internal(struct dr
 				req->base.svga3d_flags);
 
 	/* array_size must be null for non-GL3 host. */
-	if (req->base.array_size > 0 && !has_sm4_context(dev_priv)) {
-		VMW_DEBUG_USER("SM4 surface not supported.\n");
-		return -EINVAL;
+	if (req->base.array_size > 0) {
+		if (has_sm5_context(dev_priv)) {
+			if (req->base.array_size > SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE) {
+				VMW_DEBUG_USER("Invalid Surface Array Size.\n");
+				return -EINVAL;
+			}
+		} else if (has_sm4_context(dev_priv)) {
+			if (req->base.array_size > SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE) {
+				VMW_DEBUG_USER("Invalid Surface Array Size.\n");
+				return -EINVAL;
+			}
+		} else {
+			VMW_DEBUG_USER("SM4+ surface not supported.\n");
+			return -EINVAL;
+		}
 	}
 
 	if (!has_sm4_1_context(dev_priv)) {
author maintainer
#391 lore raw

PATCH 391/675 drm/vc4: Prevent shader BO mappings from becoming writable

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Linmao Li <[email protected]>

commit 0c9e6367639548307d3f578f6943ce72c9d39087 upstream.

vc4_gem_object_mmap() rejects a writable mapping of a validated shader BO, but leaves VM_MAYWRITE set. Userspace can map the BO read-only and then turn it writable with mprotect().

Validated shader BOs must stay read-only: the validator checks the instructions once and the GPU trusts them afterwards. A writable mapping lets userspace rewrite the code after validation, bypassing the validator.

Clear VM_MAYWRITE on the read-only path so the mapping cannot be upgraded, as i915 already does for its read-only objects.

---
 drivers/gpu/drm/vc4/vc4_bo.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/gpu/drm/vc4/vc4_bo.c +7 -3
@@ -733,9 +733,13 @@ static int vc4_gem_object_mmap(struct dr
 {
 	struct vc4_bo *bo = to_vc4_bo(obj);
 
-	if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) {
-		DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n");
-		return -EINVAL;
+	if (bo->validated_shader) {
+		if (vma->vm_flags & VM_WRITE) {
+			DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n");
+			return -EINVAL;
+		}
+
+		vm_flags_clear(vma, VM_MAYWRITE);
 	}
 
 	mutex_lock(&bo->madv_lock);
author maintainer
#392 lore raw

PATCH 392/675 drm/gpusvm: Fix MM reference leak in drm_gpusvm_range_evict

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Matthew Brost <[email protected]>

commit 847b371debf3c8c72384ab7b9a0c4123a74cc925 upstream.

If kvmalloc_array() fails in drm_gpusvm_range_evict(), the MM reference acquired earlier is not released, resulting in a reference leak.

Fix this by dropping the MM reference on the kvmalloc_array() failure path.

Fixes 99624bdff867 ("drm/gpusvm: Add support for GPU Shared Virtual Memory")
---
 drivers/gpu/drm/drm_gpusvm.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/drm_gpusvm.c +3 -1
@@ -1566,8 +1566,10 @@ int drm_gpusvm_range_evict(struct drm_gp
 		return -EFAULT;
 
 	pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL);
-	if (!pfns)
+	if (!pfns) {
+		mmput(mm);
 		return -ENOMEM;
+	}
 
 	hmm_range.hmm_pfns = pfns;
 	while (!time_after(jiffies, timeout)) {
author maintainer
#393 lore raw

PATCH 393/675 drm/v3d: Reach the GMP through the hub registers on V3D 7.x

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Maíra Canal <[email protected]>

commit f7df2da0d1c375c1c4c70e1e0b569592de0d6b73 upstream.

v3d_idle_axi() drains the GPU's memory interface for a safe powerdown by using the V3D_GMP_CFG register. It reached both registers with the macros V3D_CORE_READ and V3D_CORE_WRITE.

On V3D 7.x the GMP is no longer a per-core block; it lives in the hub register region. Reaching it through the per-core register block addresses the wrong region.

Select the hub accessors (V3D_{READ,WRITE}) for the GMP on V3D 7.x and keep the per-core path for earlier generations.

---
 drivers/gpu/drm/v3d/v3d_gem.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/gpu/drm/v3d/v3d_gem.c +12 -0
@@ -45,6 +45,18 @@ v3d_init_hw_state(struct v3d_dev *v3d)
 static void
 v3d_idle_axi(struct v3d_dev *v3d, int core)
 {
+	if (v3d->ver >= V3D_GEN_71) {
+		V3D_WRITE(V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ);
+
+		if (wait_for((V3D_READ(V3D_GMP_STATUS(v3d->ver)) &
+			      (V3D_GMP_STATUS_RD_COUNT_MASK |
+			       V3D_GMP_STATUS_WR_COUNT_MASK |
+			       V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) {
+			drm_err(&v3d->drm, "Failed to wait for safe GMP shutdown\n");
+		}
+		return;
+	}
+
 	V3D_CORE_WRITE(core, V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ);
 
 	if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS(v3d->ver)) &
author maintainer
#394 lore raw

PATCH 394/675 media: airspy: Return queued buffers on start_streaming() failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Valery Borovsky <[email protected]>

commit 04344d0b4929caa94c0df72f767752aa0935ef5d upstream.

The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.

airspy_start_streaming() returned -ENODEV early when the USB device had been disconnected (s->udev == NULL) without returning any buffers that buf_queue() had already accepted. Take v4l2_lock first and jump to the existing err_clear_bit label, which already drains s->queued_bufs via vb2_buffer_done(..., VB2_BUF_STATE_QUEUED) before unlocking.

This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").

Fixes 634fe5033951 ("[media] airspy: AirSpy SDR driver")
---
 drivers/media/usb/airspy/airspy.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/media/usb/airspy/airspy.c +5 -3
@@ -522,11 +522,13 @@ static int airspy_start_streaming(struct
 
 	dev_dbg(s->dev, "\n");
 
-	if (!s->udev)
-		return -ENODEV;
-
 	mutex_lock(&s->v4l2_lock);
 
+	if (!s->udev) {
+		ret = -ENODEV;
+		goto err_clear_bit;
+	}
+
 	s->sequence = 0;
 
 	set_bit(POWER_ON, &s->flags);
author maintainer
#395 lore raw

PATCH 395/675 media: amlogic-c3: Add validations for ae and awb config

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ricardo Ribalda <[email protected]>

commit 9724164f71974a2a44a5e026614fbcc05bab6d91 upstream.

Avoid invalid memory access if the zones_num is bigger than zone_weight.

This patch fixes the following smatch errors:
drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:111 c3_isp_params_awb_wt() error: buffer overflow 'cfg->zone_weight' 768 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:111 c3_isp_params_awb_wt() error: buffer overflow 'cfg->zone_weight' 768 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:227 c3_isp_params_ae_wt() error: buffer overflow 'cfg->zone_weight' 255 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:227 c3_isp_params_ae_wt() error: buffer overflow 'cfg->zone_weight' 255 <= u32max

---
 drivers/media/platform/amlogic/c3/isp/c3-isp-params.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/media/platform/amlogic/c3/isp/c3-isp-params.c +4 -0
@@ -107,6 +107,8 @@ static void c3_isp_params_awb_wt(struct
 	c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0);
 
 	zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
+	if (zones_num > C3_ISP_AWB_MAX_ZONES)
+		zones_num = C3_ISP_AWB_MAX_ZONES;
 
 	/* Need to write 8 weights at once */
 	for (i = 0; i < zones_num / 8; i++) {
@@ -223,6 +225,8 @@ static void c3_isp_params_ae_wt(struct c
 	c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0);
 
 	zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
+	if (zones_num > C3_ISP_AE_MAX_ZONES)
+		zones_num = C3_ISP_AE_MAX_ZONES;
 
 	/* Need to write 8 weights at once */
 	for (i = 0; i < zones_num / 8; i++) {
author maintainer
#396 lore raw

PATCH 396/675 media: aspeed: fix missing of_reserved_mem_device_release() on probe failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: David Carlier <[email protected]>

commit 253c8ef7d57da0c74db251f385324faaa5ae2257 upstream.

aspeed_video_init() calls of_reserved_mem_device_init() to associate reserved memory regions with the device. When aspeed_video_setup_video() subsequently fails in aspeed_video_probe(), the error path frees the JPEG buffer and unprepares the clocks but does not release the reserved memory association, leaking the rmem_assigned_device entry on the global list.

The normal remove path already calls of_reserved_mem_device_release() correctly; only the probe error path was missing it.

Add the missing of_reserved_mem_device_release() call to the aspeed_video_setup_video() failure cleanup.

Fixes d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver")
---
 drivers/media/platform/aspeed/aspeed-video.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/platform/aspeed/aspeed-video.c +1 -0
@@ -2327,6 +2327,7 @@ static int aspeed_video_probe(struct pla
 	rc = aspeed_video_setup_video(video);
 	if (rc) {
 		aspeed_video_free_buf(video, &video->jpeg);
+		of_reserved_mem_device_release(&pdev->dev);
 		clk_unprepare(video->vclk);
 		clk_unprepare(video->eclk);
 		return rc;
author maintainer
#397 lore raw

PATCH 397/675 media: cec: seco: unregister adapter on IR probe failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Myeonghun Pak <[email protected]>

commit c3a78691be8245e52ced489f268e413f18061ac2 upstream.

If secocec_ir_probe() fails after cec_register_adapter() succeeds, probe returns an error and the driver remove callback is not called. The current unwind path unregisters the notifier and then falls through to cec_delete_adapter(), which violates the CEC adapter lifetime rules after a successful registration.

Add a registered-adapter unwind path that unregisters the notifier and the adapter instead.

Fixes daef95769b3a ("media: seco-cec: add Consumer-IR support")
---
 drivers/media/cec/platform/seco/seco-cec.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/media/cec/platform/seco/seco-cec.c +5 -1
@@ -649,7 +649,7 @@ static int secocec_probe(struct platform
 
 	ret = secocec_ir_probe(secocec);
 	if (ret)
-		goto err_notifier;
+		goto err_unregister_adapter;
 
 	platform_set_drvdata(pdev, secocec);
 
@@ -657,6 +657,10 @@ static int secocec_probe(struct platform
 
 	return ret;
 
+err_unregister_adapter:
+	cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
+	cec_unregister_adapter(secocec->cec_adap);
+	goto err;
 err_notifier:
 	cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
 err_delete_adapter:
author maintainer
#398 lore raw

PATCH 398/675 media: cedrus: clean up media device on probe failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Myeonghun Pak <[email protected]>

commit 2c869b6969f3061cbbdab587f4c0a88bd7fc3cc9 upstream.

cedrus_probe() initializes the media device before registering the video device, the media controller, and the media device. If any of those later steps fails, probe returns without calling media_device_cleanup(), so the media device internals initialized by media_device_init() are left behind.

Add a media-device cleanup label to the probe unwind path and route video registration failures through it as well.

Fixes 50e761516f2b8c ("media: platform: Add Cedrus VPU decoder driver")
---
 drivers/staging/media/sunxi/cedrus/cedrus.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/staging/media/sunxi/cedrus/cedrus.c +3 -2
@@ -507,7 +507,7 @@ static int cedrus_probe(struct platform_
 	ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
 	if (ret) {
 		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
-		goto err_m2m;
+		goto err_media;
 	}
 
 	v4l2_info(&dev->v4l2_dev,
@@ -533,7 +533,8 @@ err_m2m_mc:
 	v4l2_m2m_unregister_media_controller(dev->m2m_dev);
 err_video:
 	video_unregister_device(&dev->vfd);
-err_m2m:
+err_media:
+	media_device_cleanup(&dev->mdev);
 	v4l2_m2m_release(dev->m2m_dev);
 err_v4l2:
 	v4l2_device_unregister(&dev->v4l2_dev);
author maintainer
#399 lore raw

PATCH 399/675 media: cedrus: Fix missing cleanup in error path

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Samuel Holland <[email protected]>

commit d99732334aaf33b9f93926b70b6a11c2cef3de39 upstream.

According to the documentation struct v4l2_fh has to be cleaned up with v4l2_fh_exit() before being freed. [1]
Currently there is no actual bug here, when v4l2_fh_exit() isn't called. v4l2_fh_exit() in this case only destroys internal mutex. But it may change in the future, when v4l2_fh_init/v4l2_fh_exit will be enhanced.

1. https://docs.kernel.org/driver-api/media/v4l2-fh.html

---
 drivers/staging/media/sunxi/cedrus/cedrus.c |    1 +
 1 file changed, 1 insertion(+)
drivers/staging/media/sunxi/cedrus/cedrus.c +1 -0
@@ -391,6 +391,7 @@ static int cedrus_open(struct file *file
 err_m2m_release:
 	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
 err_free:
+	v4l2_fh_exit(&ctx->fh);
 	kfree(ctx);
 	mutex_unlock(&dev->dev_mutex);
 
author maintainer
#400 lore raw

PATCH 400/675 media: cedrus: skip invalid H.264 reference list entries

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Pengpeng Hou <[email protected]>

commit 10358ea986c3c85516d1c8206486464f79d36e76 upstream.

Cedrus consumes H.264 ref_pic_list0/ref_pic_list1 entries from the stateless slice control and later uses their indices to look up decode->dpb[] in _cedrus_write_ref_list().

Rejecting such controls in cedrus_try_ctrl() would break existing userspace, since stateless H.264 reference lists may legitimately carry out-of-range indices for missing references. Instead, guard the actual DPB lookup in Cedrus and skip entries whose indices do not fit the fixed V4L2_H264_NUM_DPB_ENTRIES array.

This keeps the fix local to the driver use site and avoids out-of-bounds reads from malformed or unsupported reference list entries.

---
 drivers/staging/media/sunxi/cedrus/cedrus_h264.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/staging/media/sunxi/cedrus/cedrus_h264.c +3 -0
@@ -210,6 +210,9 @@ static void _cedrus_write_ref_list(struc
 		u8 dpb_idx;
 
 		dpb_idx = ref_list[i].index;
+		if (dpb_idx >= V4L2_H264_NUM_DPB_ENTRIES)
+			continue;
+
 		dpb = &decode->dpb[dpb_idx];
 
 		if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
author maintainer
#401 lore raw

PATCH 401/675 media: chips-media: wave5: Move src_buf Removal to finish_encode

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Brandon Brnich <[email protected]>

commit b20157147089a9c16a38c7810e2fe6f2df8e3277 upstream.

During encoder processing, there is a case where the IRQ response could return the buffer back to userspace via v4l2_m2m_buf_done call. In this time, userspace could queue up this same buffer before start_encode removes the index from the ready queue. This would then lead to a case where the buffer in the ready queue could be a self loop due to the WRITE_ONCE(prev->next, new) call in __list_add.

When __list_del is finally called, the loop is already made so nothing points back to ready queue list head and pointers are poisoned.

A buffer should not be marked as DONE before the buffer is removed from m2m ready queue. Move removal entirely to finish_encode.

Fixes 9707a6254a8a6 ("media: chips-media: wave5: Add the v4l2 layer")
---
 drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c |   29 ++-------------
 1 file changed, 4 insertions(+), 25 deletions(-)
drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +4 -25
@@ -226,13 +226,6 @@ static int start_encode(struct vpu_insta
 	} else {
 		dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n",
 			__func__);
-		/*
-		 * Remove the source buffer from the ready-queue now and finish
-		 * it in the videobuf2 framework once the index is returned by the
-		 * firmware in finish_encode
-		 */
-		if (src_buf)
-			v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index);
 	}
 
 	return 0;
@@ -259,27 +252,13 @@ static void wave5_vpu_enc_finish_encode(
 		__func__,  enc_output_info.pic_type, enc_output_info.recon_frame_index,
 		enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts);
 
-	/*
-	 * The source buffer will not be found in the ready-queue as it has been
-	 * dropped after sending of the encode firmware command, locate it in
-	 * the videobuf2 queue directly
-	 */
 	if (enc_output_info.enc_src_idx >= 0) {
-		struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx),
-						       enc_output_info.enc_src_idx);
-		if (vb->state != VB2_BUF_STATE_ACTIVE)
-			dev_warn(inst->dev->dev,
-				 "%s: encoded buffer (%d) was not in ready queue %i.",
-				 __func__, enc_output_info.enc_src_idx, vb->state);
-		else
-			src_buf = to_vb2_v4l2_buffer(vb);
-
-		if (src_buf) {
+		src_buf = v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, enc_output_info.enc_src_idx);
+		if (!src_buf) {
+			dev_warn(inst->dev->dev, "%s: no source buffer found\n", __func__);
+		} else {
 			inst->timestamp = src_buf->vb2_buf.timestamp;
 			v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
-		} else {
-			dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n",
-				 __func__, enc_output_info.enc_src_idx);
 		}
 	}
 
author maintainer
#402 lore raw

PATCH 402/675 media: cx231xx: fix devres lifetime

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Johan Hovold <[email protected]>

commit 7d6358ab02866e5b7ed8d3a00805297617bbb0ec upstream.

USB drivers bind to USB interfaces and any device managed resources should have their lifetime tied to the interface rather than parent USB device. This avoids issues like memory leaks when drivers are unbound without their devices being physically disconnected (e.g. on probe deferral or configuration changes).

Fix the driver state lifetime so that it is released on driver unbind.

Fixes 184a82784d50 ("[media] cx231xx: use devm_ functions to allocate memory")
---
 drivers/media/usb/cx231xx/cx231xx-cards.c |   16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)
drivers/media/usb/cx231xx/cx231xx-cards.c +11 -5
@@ -1575,7 +1575,8 @@ static int cx231xx_init_v4l2(struct cx23
 		 dev->video_mode.end_point_addr,
 		 dev->video_mode.num_alt);
 
-	dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL);
+	dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+							      dev->video_mode.num_alt, GFP_KERNEL);
 	if (dev->video_mode.alt_max_pkt_size == NULL)
 		return -ENOMEM;
 
@@ -1616,7 +1617,8 @@ static int cx231xx_init_v4l2(struct cx23
 		 dev->vbi_mode.num_alt);
 
 	/* compute alternate max packet sizes for vbi */
-	dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL);
+	dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+							    dev->vbi_mode.num_alt, GFP_KERNEL);
 	if (dev->vbi_mode.alt_max_pkt_size == NULL)
 		return -ENOMEM;
 
@@ -1658,7 +1660,9 @@ static int cx231xx_init_v4l2(struct cx23
 		 "sliced CC EndPoint Addr 0x%x, Alternate settings: %i\n",
 		 dev->sliced_cc_mode.end_point_addr,
 		 dev->sliced_cc_mode.num_alt);
-	dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL);
+	dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+								  dev->sliced_cc_mode.num_alt,
+								  GFP_KERNEL);
 	if (dev->sliced_cc_mode.alt_max_pkt_size == NULL)
 		return -ENOMEM;
 
@@ -1722,7 +1726,7 @@ static int cx231xx_usb_probe(struct usb_
 	udev = usb_get_dev(interface_to_usbdev(interface));
 
 	/* allocate memory for our device state and initialize it */
-	dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL);
+	dev = devm_kzalloc(&interface->dev, sizeof(*dev), GFP_KERNEL);
 	if (dev == NULL) {
 		retval = -ENOMEM;
 		goto err_if;
@@ -1852,7 +1856,9 @@ static int cx231xx_usb_probe(struct usb_
 			 dev->ts1_mode.end_point_addr,
 			 dev->ts1_mode.num_alt);
 
-		dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->ts1_mode.num_alt, GFP_KERNEL);
+		dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+								    dev->ts1_mode.num_alt,
+								    GFP_KERNEL);
 		if (dev->ts1_mode.alt_max_pkt_size == NULL) {
 			retval = -ENOMEM;
 			goto err_video_alt;
author maintainer
#403 lore raw

PATCH 403/675 media: cx23885: add ioremap return check and cleanup

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Wang Jun <[email protected]>

commit a0701e387b46e2481c05b47f1235b954bfc2af3e upstream.

Add a check for the return value of pci_ioremap_bar() in cx23885_dev_setup().
If ioremap for BAR0 fails, release the already allocated PCI memory region,
decrement the device count, and return -ENODEV.

This prevents a potential null pointer dereference and ensures proper cleanup
on memory mapping failure.

Fixes d19770e5178a ("V4L/DVB (6150): Add CX23885/CX23887 PCIe bridge driver")
---
 drivers/media/pci/cx23885/cx23885-core.c |   14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)
drivers/media/pci/cx23885/cx23885-core.c +12 -2
@@ -990,8 +990,12 @@ static int cx23885_dev_setup(struct cx23
 	}
 
 	/* PCIe stuff */
-	dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
-			     pci_resource_len(dev->pci, 0));
+	dev->lmmio = pci_ioremap_bar(dev->pci, 0);
+	if (!dev->lmmio) {
+		dev_err(&dev->pci->dev, "CORE %s: can't ioremap MMIO memory\n",
+			dev->name);
+		goto err_release_region;
+	}
 
 	dev->bmmio = (u8 __iomem *)dev->lmmio;
 
@@ -1096,6 +1100,12 @@ static int cx23885_dev_setup(struct cx23
 	}
 
 	return 0;
+
+err_release_region:
+	release_mem_region(pci_resource_start(dev->pci, 0),
+			   pci_resource_len(dev->pci, 0));
+	cx23885_devcount--;
+	return -ENODEV;
 }
 
 static void cx23885_dev_unregister(struct cx23885_dev *dev)
author maintainer
#404 lore raw

PATCH 404/675 media: i2c: alvium: fix critical pointer access in alvium_ctrl_init

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Martin Hecht <[email protected]>

commit 4f6f28ff24709710c08557c127b3e4c3fb1b4159 upstream.

The current implementation of alvium_ctrl_init creates several controls in function alvium_ctrl_init and uses the returned pointer without check. That can cause write access over NULL-pointer for several controls. The reworked code checks the pointers before adding flags.

Fixes 0a7af872915e ("media: i2c: Add support for alvium camera")
---
 drivers/media/i2c/alvium-csi2.c |   21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)
drivers/media/i2c/alvium-csi2.c +14 -7
@@ -2100,20 +2100,21 @@ static int alvium_ctrl_init(struct alviu
 					      V4L2_CID_PIXEL_RATE, 0,
 					      ALVIUM_DEFAULT_PIXEL_RATE_MHZ, 1,
 					      ALVIUM_DEFAULT_PIXEL_RATE_MHZ);
-	ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
 	/* Link freq is fixed */
 	ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops,
 						  V4L2_CID_LINK_FREQ,
 						  0, 0, &alvium->link_freq);
-	ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	if (ctrls->link_freq)
+		ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
 	/* Auto/manual white balance */
 	if (alvium->avail_ft.auto_whiteb) {
 		ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
 						   V4L2_CID_AUTO_WHITE_BALANCE,
 						   0, 1, 1, 1);
-		v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
+		if (ctrls->auto_wb)
+			v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
 	}
 
 	ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops,
@@ -2122,6 +2123,7 @@ static int alvium_ctrl_init(struct alviu
 						alvium->max_bbalance,
 						alvium->inc_bbalance,
 						alvium->dft_bbalance);
+
 	ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops,
 					       V4L2_CID_RED_BALANCE,
 					       alvium->min_rbalance,
@@ -2136,7 +2138,9 @@ static int alvium_ctrl_init(struct alviu
 					       V4L2_CID_EXPOSURE_AUTO,
 					       V4L2_EXPOSURE_MANUAL, 0,
 					       V4L2_EXPOSURE_AUTO);
-		v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
+		if (ctrls->auto_exp)
+			v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp,
+					       V4L2_EXPOSURE_MANUAL, true);
 	}
 
 	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops,
@@ -2145,14 +2149,16 @@ static int alvium_ctrl_init(struct alviu
 					    alvium->max_exp,
 					    alvium->inc_exp,
 					    alvium->dft_exp);
-	ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
+	if (ctrls->exposure)
+		ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
 
 	/* Auto/manual gain */
 	if (alvium->avail_ft.auto_gain) {
 		ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops,
 						     V4L2_CID_AUTOGAIN,
 						     0, 1, 1, 1);
-		v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
+		if (ctrls->auto_gain)
+			v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
 	}
 
 	if (alvium->avail_ft.gain) {
@@ -2162,7 +2168,8 @@ static int alvium_ctrl_init(struct alviu
 						alvium->max_gain,
 						alvium->inc_gain,
 						alvium->dft_gain);
-		ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
+		if (ctrls->gain)
+			ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
 	}
 
 	if (alvium->avail_ft.sat)
author maintainer
#405 lore raw

PATCH 405/675 media: imx219: Fix maximum frame length in lines

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Sakari Ailus <[email protected]>

commit 2c4f1ba7354312ad2d6e34e70a518a51a9344715 upstream.

The driver used the maximum frame length in lines value of 0xffff, but the maximum appears to be 0xfffe instead. Fix it.

Fixes 1283b3b8f82b ("media: i2c: Add driver for Sony IMX219 sensor")
---
 drivers/media/i2c/imx219.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/i2c/imx219.c +1 -1
@@ -71,7 +71,7 @@
 
 /* V_TIMING internal */
 #define IMX219_REG_FRM_LENGTH_A		CCI_REG16(0x0160)
-#define IMX219_FLL_MAX			0xffff
+#define IMX219_FLL_MAX			0xfffe
 #define IMX219_VBLANK_MIN		32
 #define IMX219_REG_LINE_LENGTH_A	CCI_REG16(0x0162)
 #define IMX219_LLP_MIN			0x0d78
author maintainer
#406 lore raw

PATCH 406/675 media: intel/ipu6: Improve DWC PHY HSFREQRANGE band selection for overlapping ranges

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Marco Nenciarini <[email protected]>

commit 477620dccf3e9481ed6ae67cb5c747f25751c531 upstream.

The get_hsfreq_by_mbps() function searches the freqranges[] table backward (from highest to lowest index). Because adjacent frequency bands overlap, a data rate that falls in the overlap region always lands on the higher-indexed band.

For data rates up to 1500 Mbps (index 42) every band uses osc_freq_target 335. Starting at index 43 (1461-1640 Mbps) the osc_freq_target drops to 208. A sensor running at 1498 Mbps sits in the overlap between index 42 (1414-1588, osc 335) and index 43 (1461-1640, osc 208). The backward search picks index 43, programming the lower osc_freq_target of 208 instead of the optimal 335.

This causes DDL lock instability and CSI-2 CRC errors on affected configurations, such as the OmniVision OV08X40 sensor on Intel Arrow Lake platforms (Dell Pro Max 16).

Rewrite get_hsfreq_by_mbps() to select the optimal band:

1. Among bands whose min/max range covers the data rate, prefer the one with the higher osc_freq_target.
2. If osc_freq_target is equal, prefer the band whose default_mbps is closest to the requested rate.

Since the frequency ranges are monotonically increasing, the loop exits early once min exceeds the requested rate.

For 1498 Mbps this now correctly selects index 42 (osc_freq_target 335, range 1414-1588) instead of index 43 (osc_freq_target 208, range 1461-1640).

Fixes 1e7eeb301696 ("media: intel/ipu6: add the CSI2 DPHY implementation")
---
 drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c |   24 +++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)
drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c +18 -6
@@ -288,15 +288,27 @@ static const struct dwc_dphy_freq_range
 
 static u16 get_hsfreq_by_mbps(u32 mbps)
 {
-	unsigned int i = DPHY_FREQ_RANGE_NUM;
+	u16 best = DPHY_FREQ_RANGE_INVALID_INDEX;
+	unsigned int i;
 
-	while (i--) {
-		if (freqranges[i].default_mbps == mbps ||
-		    (mbps >= freqranges[i].min && mbps <= freqranges[i].max))
-			return i;
+	for (i = 0; i < DPHY_FREQ_RANGE_NUM; i++) {
+		if (mbps > freqranges[i].max)
+			continue;
+
+		if (mbps < freqranges[i].min)
+			break;
+
+		if (best == DPHY_FREQ_RANGE_INVALID_INDEX ||
+		    freqranges[i].osc_freq_target >
+		    freqranges[best].osc_freq_target ||
+		    (freqranges[i].osc_freq_target ==
+		     freqranges[best].osc_freq_target &&
+		     abs((int)mbps - (int)freqranges[i].default_mbps) <
+		     abs((int)mbps - (int)freqranges[best].default_mbps)))
+			best = i;
 	}
 
-	return DPHY_FREQ_RANGE_INVALID_INDEX;
+	return best;
 }
 
 static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,
author maintainer
#407 lore raw

PATCH 407/675 media: iris: Fix use IRQF_NO_AUTOEN when requesting the IRQ

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Dmitry Baryshkov <[email protected]>

commit 45ac2230f929b9f0fae899c4a5c6bf763250fc13 upstream.

Requesting the IRQ and then immediately disabling it is fragile as it leaves a window when the IRQ is still enabled although the underlying device might be not completely setup for IRQ handling. Pass IRQF_NO_AUTOEN instead of calling disable_irq_nosync().

Fixes fb583a214337 ("media: iris: introduce host firmware interface with necessary hooks")
[bod: Appended Fix to patch title for -stable clarity]
[bod: Added cc stable for backporting]
---
 drivers/media/platform/qcom/iris/iris_probe.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/media/platform/qcom/iris/iris_probe.c +3 -3
@@ -256,12 +256,12 @@ static int iris_probe(struct platform_de
 	core->iris_platform_data = of_device_get_match_data(core->dev);
 
 	ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr,
-					iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core);
+					iris_hfi_isr_handler,
+					IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN,
+					"iris", core);
 	if (ret)
 		return ret;
 
-	disable_irq_nosync(core->irq);
-
 	iris_init_ops(core);
 	core->iris_platform_data->init_hfi_command_ops(core);
 	core->iris_platform_data->init_hfi_response_ops(core);
author maintainer
#408 lore raw

PATCH 408/675 media: marvell-cam: fix missing pci_disable_device() on remove

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guangshuo Li <[email protected]>

commit 033ff0420e4c9c240ae5523fff39770298efa964 upstream.

During manual code audit, we found that cafe_pci_probe() enables the PCI device with pci_enable_device(), and its probe error path properly calls pci_disable_device() on failure.

However, cafe_pci_remove() tears down the controller and frees the driver data without disabling the PCI device, leaving the remove path inconsistent with probe cleanup.

Add the missing pci_disable_device() call to cafe_pci_remove().

Fixes abfa3df36c01 ("[media] marvell-cam: Separate out the Marvell camera core")
---
 drivers/media/platform/marvell/cafe-driver.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/platform/marvell/cafe-driver.c +1 -0
@@ -609,6 +609,7 @@ static void cafe_pci_remove(struct pci_d
 		return;
 	}
 	cafe_shutdown(cam);
+	pci_disable_device(pdev);
 	kfree(cam);
 }
 
author maintainer
#409 lore raw

PATCH 409/675 media: meson: vdec: Fix memory leak in error path of vdec_open

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Anand Moon <[email protected]>

commit 940f161f734b25f175a95d2684c2021f6323693a upstream.

The vdec_open() function previously jumped directly to err_m2m_release when vdec_init_ctrls() failed, skipping release of the m2m context. This caused a resource leak.

Fix it by introducing a proper err_m2m_ctx_release label that calls v4l2_m2m_ctx_release(sess->m2m_ctx) before releasing the m2m device.

This was identified via kmemleak:
unreferenced object 0xffff0000205d6878 (size 8):
comm "v4l_id", pid 5289, jiffies 4294938580
hex dump (first 8 bytes):

    40 d2 49 18 00 00 ff ff                          @.I.....
  backtrace (crc d3204599):
    kmemleak_alloc+0xc8/0xf0
    __kvmalloc_node_noprof+0x60c/0x850
    v4l2_ctrl_handler_init_class+0x1b4/0x2e8 [videodev]
    vdec_open+0x1f4/0x788 [meson_vdec]
    v4l2_open+0x144/0x460 [videodev]
    chrdev_open+0x1ac/0x500
    do_dentry_open+0x3f0/0xfe8
    vfs_open+0x68/0x320
    do_open+0x2d8/0x9a8
    path_openat+0x1d0/0x4f0
    do_filp_open+0x190/0x380
    do_sys_openat2+0xf8/0x1b0
    __arm64_sys_openat+0x13c/0x1e8
    invoke_syscall+0xdc/0x268
    el0_svc_common.constprop.0+0x178/0x258
    do_el0_svc+0x4c/0x70
Fixes 3e7f51bd9607 ("media: meson: add v4l2 m2m video decoder driver")
---
 drivers/staging/media/meson/vdec/vdec.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/staging/media/meson/vdec/vdec.c +3 -1
@@ -889,7 +889,7 @@ static int vdec_open(struct file *file)
 
 	ret = vdec_init_ctrls(sess);
 	if (ret)
-		goto err_m2m_release;
+		goto err_m2m_ctx_release;
 
 	sess->pixfmt_cap = formats[0].pixfmts_cap[0];
 	sess->fmt_out = &formats[0];
@@ -913,6 +913,8 @@ static int vdec_open(struct file *file)
 
 	return 0;
 
+err_m2m_ctx_release:
+	v4l2_m2m_ctx_release(sess->m2m_ctx);
 err_m2m_release:
 	v4l2_m2m_release(sess->m2m_dev);
 err_free_sess:
author maintainer
#410 lore raw

PATCH 410/675 media: msi2500: Return queued buffers on start_streaming() failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Valery Borovsky <[email protected]>

commit 7201c17786a498497bca57752883b90914d405ac upstream.

The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.

msi2500_start_streaming() had five error paths that all hit this trap and were further tangled by ret-overwriting between calls:

- -ENODEV when the USB device was already disconnected
- -ERESTARTSYS when mutex_lock_interruptible() was interrupted
- msi2500_set_usb_adc() failure: ret was silently overwritten by the next call (msi2500_isoc_init), so the error was lost entirely
- msi2500_isoc_init() failure: cleanup_queued_bufs was called, but the function then fell through to msi2500_ctrl_msg() and again masked the original error by overwriting ret
- msi2500_ctrl_msg(CMD_START_STREAMING) failure: no cleanup at all, leaving isoc URBs submitted with no way for the driver to consume them

Consolidate the error paths into a small goto chain. Every failure now stops the function, drains the queued-buffer list, and returns the real error code. The ctrl_msg failure path also rolls back the preceding msi2500_isoc_init() via msi2500_isoc_cleanup() before unlocking and draining.

The cleanup helper takes a vb2_buffer_state argument so that the start_streaming error paths can pass VB2_BUF_STATE_QUEUED (as expected by userspace on start_streaming failure) while stop_streaming keeps its existing VB2_BUF_STATE_ERROR semantics.

This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").

Fixes 977e444f59ad ("[media] Mirics MSi3101 SDR Dongle driver")
---
 drivers/media/usb/msi2500/msi2500.c |   32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)
drivers/media/usb/msi2500/msi2500.c +24 -8
@@ -541,7 +541,8 @@ static int msi2500_isoc_init(struct msi2
 }
 
 /* Must be called with vb_queue_lock hold */
-static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev)
+static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev,
+					enum vb2_buffer_state state)
 {
 	unsigned long flags;
 
@@ -554,7 +555,7 @@ static void msi2500_cleanup_queued_bufs(
 		buf = list_entry(dev->queued_bufs.next,
 				 struct msi2500_frame_buf, list);
 		list_del(&buf->list);
-		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+		vb2_buffer_done(&buf->vb.vb2_buf, state);
 	}
 	spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
 }
@@ -830,25 +831,40 @@ static int msi2500_start_streaming(struc
 
 	dev_dbg(dev->dev, "\n");
 
-	if (!dev->udev)
-		return -ENODEV;
+	if (!dev->udev) {
+		ret = -ENODEV;
+		goto err_cleanup;
+	}
 
-	if (mutex_lock_interruptible(&dev->v4l2_lock))
-		return -ERESTARTSYS;
+	if (mutex_lock_interruptible(&dev->v4l2_lock)) {
+		ret = -ERESTARTSYS;
+		goto err_cleanup;
+	}
 
 	/* wake-up tuner */
 	v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1);
 
 	ret = msi2500_set_usb_adc(dev);
+	if (ret)
+		goto err_unlock_cleanup;
 
 	ret = msi2500_isoc_init(dev);
 	if (ret)
-		msi2500_cleanup_queued_bufs(dev);
+		goto err_unlock_cleanup;
 
 	ret = msi2500_ctrl_msg(dev, CMD_START_STREAMING, 0);
+	if (ret)
+		goto err_isoc_cleanup;
 
 	mutex_unlock(&dev->v4l2_lock);
+	return 0;
 
+err_isoc_cleanup:
+	msi2500_isoc_cleanup(dev);
+err_unlock_cleanup:
+	mutex_unlock(&dev->v4l2_lock);
+err_cleanup:
+	msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
 	return ret;
 }
 
@@ -863,7 +879,7 @@ static void msi2500_stop_streaming(struc
 	if (dev->udev)
 		msi2500_isoc_cleanup(dev);
 
-	msi2500_cleanup_queued_bufs(dev);
+	msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR);
 
 	/* according to tests, at least 700us delay is required  */
 	msleep(20);
author maintainer
#411 lore raw

PATCH 411/675 media: nuvoton: npcm-video: fix error handling in npcm_video_init()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: David Carlier <[email protected]>

commit 60ca00792bce46ec170c7ed101f376186d4cf8a9 upstream.

npcm_video_init() has two error handling issues after of_reserved_mem_device_init() is called:

When dma_set_mask_and_coherent() fails, the function releases the reserved memory but does not return, allowing execution to fall through into npcm_video_ece_init() with a failed DMA configuration.

When npcm_video_ece_init() fails, the function returns an error without calling of_reserved_mem_device_release(), leaking the reserved memory association.

Fix both by adding the missing return after the DMA mask failure and adding the missing of_reserved_mem_device_release() call on the ECE init error path.

Fixes 46c15a4ff1f4 ("media: nuvoton: Add driver for NPCM video capture and encoding engine")
---
 drivers/media/platform/nuvoton/npcm-video.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/media/platform/nuvoton/npcm-video.c +2 -0
@@ -1720,10 +1720,12 @@ static int npcm_video_init(struct npcm_v
 	if (rc) {
 		dev_err(dev, "Failed to set DMA mask\n");
 		of_reserved_mem_device_release(dev);
+		return rc;
 	}
 
 	rc = npcm_video_ece_init(video);
 	if (rc) {
+		of_reserved_mem_device_release(dev);
 		dev_err(dev, "Failed to initialize ECE\n");
 		return rc;
 	}
author maintainer
#412 lore raw

PATCH 412/675 media: nuvoton: npcm-video: fix memory leaks in probe and remove

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: David Carlier <[email protected]>

commit 50cc0e547da50b887e63dfa1ad203cd5b735d01e upstream.

npcm_video_probe() allocates the npcm_video structure with kzalloc_obj() but never frees it on any probe error path or in npcm_video_remove(), leaking the allocation on every failed probe and every normal unbind.

Additionally, when npcm_video_setup_video() fails, the reserved memory association established by of_reserved_mem_device_init() in npcm_video_init() is not released, leaking the rmem_assigned_device entry on the global list.

Fix both by adding kfree(video) to all probe error paths and to npcm_video_remove(), and adding the missing of_reserved_mem_device_release() call when npcm_video_setup_video() fails.

Fixes 46c15a4ff1f4 ("media: nuvoton: Add driver for NPCM video capture and encoding engine")
---
 drivers/media/platform/nuvoton/npcm-video.c |   32 ++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 9 deletions(-)
drivers/media/platform/nuvoton/npcm-video.c +23 -9
@@ -1750,42 +1750,55 @@ static int npcm_video_probe(struct platf
 	regs = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(regs)) {
 		dev_err(&pdev->dev, "Failed to parse VCD reg in DTS\n");
-		return PTR_ERR(regs);
+		rc = PTR_ERR(regs);
+		goto err_free;
 	}
 
 	video->vcd_regmap = devm_regmap_init_mmio(&pdev->dev, regs,
 						  &npcm_video_regmap_cfg);
 	if (IS_ERR(video->vcd_regmap)) {
 		dev_err(&pdev->dev, "Failed to initialize VCD regmap\n");
-		return PTR_ERR(video->vcd_regmap);
+		rc = PTR_ERR(video->vcd_regmap);
+		goto err_free;
 	}
 
 	video->reset = devm_reset_control_get(&pdev->dev, NULL);
 	if (IS_ERR(video->reset)) {
 		dev_err(&pdev->dev, "Failed to get VCD reset control in DTS\n");
-		return PTR_ERR(video->reset);
+		rc = PTR_ERR(video->reset);
+		goto err_free;
 	}
 
 	video->gcr_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
 							    "nuvoton,sysgcr");
-	if (IS_ERR(video->gcr_regmap))
-		return PTR_ERR(video->gcr_regmap);
+	if (IS_ERR(video->gcr_regmap)) {
+		rc = PTR_ERR(video->gcr_regmap);
+		goto err_free;
+	}
 
 	video->gfx_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
 							    "nuvoton,sysgfxi");
-	if (IS_ERR(video->gfx_regmap))
-		return PTR_ERR(video->gfx_regmap);
+	if (IS_ERR(video->gfx_regmap)) {
+		rc = PTR_ERR(video->gfx_regmap);
+		goto err_free;
+	}
 
 	rc = npcm_video_init(video);
 	if (rc)
-		return rc;
+		goto err_free;
 
 	rc = npcm_video_setup_video(video);
 	if (rc)
-		return rc;
+		goto err_release_mem;
 
 	dev_info(video->dev, "NPCM video driver probed\n");
 	return 0;
+
+err_release_mem:
+	of_reserved_mem_device_release(&pdev->dev);
+err_free:
+	kfree(video);
+	return rc;
 }
 
 static void npcm_video_remove(struct platform_device *pdev)
@@ -1800,6 +1813,7 @@ static void npcm_video_remove(struct pla
 	v4l2_device_unregister(v4l2_dev);
 	if (video->ece.enable)
 		npcm_video_ece_stop(video);
+	kfree(video);
 	of_reserved_mem_device_release(dev);
 }
 
author maintainer
#413 lore raw

PATCH 413/675 media: nxp: imx8-isi: Add missing v4l2_subdev_cleanup() in crossbar and pipe

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xiaolei Wang <[email protected]>

commit 567418eedd25b3d86d489807682030b4b98b73d9 upstream.

Both mxc_isi_crossbar_init() and mxc_isi_pipe_init() call v4l2_subdev_init_finalize() which allocates the subdev active state, but neither mxc_isi_crossbar_cleanup() nor mxc_isi_pipe_cleanup() calls v4l2_subdev_cleanup() to free it.

This causes a memory leak on every rmmod, reported by kmemleak:

unreferenced object 0xffff0000d06fc800 (size 192):

    comm "(udev-worker)", pid 254, jiffies 4294913455
    backtrace (crc 36eeae58):
      kmemleak_alloc+0x34/0x40
      __kvmalloc_node_noprof+0x5f8/0x7d8
      __v4l2_subdev_state_alloc+0x1fc/0x30c
      __v4l2_subdev_init_finalize+0x178/0x368

Add the missing v4l2_subdev_cleanup() calls before media_entity_cleanup() in both crossbar and pipe cleanup paths.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c |    1 +
 drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c     |    1 +
 2 files changed, 2 insertions(+)
drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +1 -0
@@ -492,6 +492,7 @@ err_free:
 
 void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar)
 {
+	v4l2_subdev_cleanup(&xbar->sd);
 	media_entity_cleanup(&xbar->sd.entity);
 	kfree(xbar->pads);
 	kfree(xbar->inputs);
drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +1 -0
@@ -819,6 +819,7 @@ void mxc_isi_pipe_cleanup(struct mxc_isi
 {
 	struct v4l2_subdev *sd = &pipe->sd;
 
+	v4l2_subdev_cleanup(sd);
 	media_entity_cleanup(&sd->entity);
 	mutex_destroy(&pipe->lock);
 }
author maintainer
#414 lore raw

PATCH 414/675 media: nxp: imx8-isi: Clean up already-initialized pipes on probe failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xiaolei Wang <[email protected]>

commit d970b27cc48ec42f8a72bc3a4a4ad2e5c7a36395 upstream.

When mxc_isi_pipe_init() fails partway through the channel loop or when mxc_isi_v4l2_init() fails, the already initialized pipes are not cleaned up.

Fix this by calling mxc_isi_pipe_cleanup() for each already-initialized pipe in the err_xbar error path.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +2 -0
@@ -517,6 +517,8 @@ static int mxc_isi_probe(struct platform
 	return 0;
 
 err_xbar:
+	while (i--)
+		mxc_isi_pipe_cleanup(&isi->pipes[i]);
 	mxc_isi_crossbar_cleanup(&isi->crossbar);
 
 	return ret;
author maintainer
#415 lore raw

PATCH 415/675 media: nxp: imx8-isi: Fix missing v4l2_subdev_cleanup() in pipe init error path

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xiaolei Wang <[email protected]>

commit 8262de0663318124824aaafd97ddb5d7bb53bd77 upstream.

After v4l2_subdev_init_finalize() succeeds in mxc_isi_pipe_init(), if platform_get_irq() or devm_request_irq() fails, the error path jumps to a label that only calls media_entity_cleanup() and mutex_destroy(), missing the v4l2_subdev_cleanup() call needed to free the subdev active state allocated by v4l2_subdev_init_finalize().

Add an error_subdev label that calls v4l2_subdev_cleanup() before falling through to the existing error cleanup.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +4 -2
@@ -796,18 +796,20 @@ int mxc_isi_pipe_init(struct mxc_isi_dev
 	irq = platform_get_irq(to_platform_device(isi->dev), id);
 	if (irq < 0) {
 		ret = irq;
-		goto error;
+		goto error_subdev;
 	}
 
 	ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler,
 			       0, dev_name(isi->dev), pipe);
 	if (ret < 0) {
 		dev_err(isi->dev, "failed to request IRQ (%d)\n", ret);
-		goto error;
+		goto error_subdev;
 	}
 
 	return 0;
 
+error_subdev:
+	v4l2_subdev_cleanup(sd);
 error:
 	media_entity_cleanup(&sd->entity);
 	mutex_destroy(&pipe->lock);
author maintainer
#416 lore raw

PATCH 416/675 media: nxp: imx8-isi: Fix potential out-of-bounds issues

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guoniu Zhou <[email protected]>

commit 57a7ec5c9f38ce6c4d6209c4b75c8e57e1fea6cf upstream.

The maximum downscaling factor supported by ISI can be up to 16. Add minimum value constraint before applying the setting to hardware. Otherwise, the process will not respond even when Ctrl+C is executed.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h |   16 ++++++++++++++++
 drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c  |   11 ++++++++---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c |   13 ++++++++-----
 3 files changed, 32 insertions(+), 8 deletions(-)
drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +16 -0
@@ -11,6 +11,7 @@
 #define __MXC_ISI_CORE_H__
 
 #include <linux/list.h>
+#include <linux/math.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
@@ -412,4 +413,19 @@ static inline void mxc_isi_debug_cleanup
 }
 #endif
 
+/*
+ * ISI scaling engine works in two parts: it performs pre-decimation of
+ * the image followed by bilinear filtering to achieve the desired
+ * downscaling factor.
+ *
+ * The decimation filter provides a maximum downscaling factor of 8, and
+ * the subsequent bilinear filter provides a maximum downscaling factor
+ * of 2. Combined, the maximum scaling factor can be up to 16.
+ */
+static inline unsigned int
+mxc_isi_clamp_downscale_16(unsigned int val, unsigned int max_val)
+{
+	return clamp(val, max(1U, DIV_ROUND_UP(max_val, 16)), max_val);
+}
+
 #endif /* __MXC_ISI_CORE_H__ */
drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +8 -3
@@ -509,9 +509,14 @@ __mxc_isi_m2m_try_fmt_vid(struct mxc_isi
 			  const enum mxc_isi_video_type type)
 {
 	if (type == MXC_ISI_VIDEO_M2M_CAP) {
-		/* Downscaling only  */
-		pix->width = min(pix->width, ctx->queues.out.format.width);
-		pix->height = min(pix->height, ctx->queues.out.format.height);
+		const struct v4l2_pix_format_mplane *format =
+			&ctx->queues.out.format;
+
+		/* Downscaling only, by up to 16. */
+		pix->width = mxc_isi_clamp_downscale_16(pix->width,
+							format->width);
+		pix->height = mxc_isi_clamp_downscale_16(pix->height,
+							 format->height);
 	}
 
 	return mxc_isi_format_try(ctx->m2m->pipe, pix, type);
drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +8 -5
@@ -641,16 +641,19 @@ static int mxc_isi_pipe_set_selection(st
 			/* Composing is supported on the sink only. */
 			return -EINVAL;
 
-		/* The sink crop is bound by the sink format downscaling only). */
+		/*
+		 * The ISI supports downscaling only, with a factor up to 16.
+		 * Clamp the compose rectangle size accordingly.
+		 */
 		format = mxc_isi_pipe_get_pad_format(pipe, state,
 						     MXC_ISI_PIPE_PAD_SINK);
 
 		sel->r.left = 0;
 		sel->r.top = 0;
-		sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH,
-				     format->width);
-		sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT,
-				      format->height);
+		sel->r.width = mxc_isi_clamp_downscale_16(sel->r.width,
+							  format->width);
+		sel->r.height = mxc_isi_clamp_downscale_16(sel->r.height,
+							   format->height);
 
 		rect = mxc_isi_pipe_get_pad_compose(pipe, state,
 						    MXC_ISI_PIPE_PAD_SINK);
author maintainer
#417 lore raw

PATCH 417/675 media: nxp: imx8-isi: Fix scale factor calculation for hardware rounding

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guoniu Zhou <[email protected]>

commit 5eb54da3f874b44149556542d949909898865e29 upstream.

The ISI hardware rounds the actual output size up to an integer, as described in i.MX93 Reference Manual section 57.7.8 (Channel 0 Scale Factor). The scale factor must be calculated to ensure the theoretical output value rounds up to exactly the desired size.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c +8 -1
@@ -112,7 +112,14 @@ static u32 mxc_isi_channel_scaling_ratio
 	else
 		*dec = 8;
 
-	return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD);
+	/*
+	 * The ISI rounds output dimensions up to the next integer (i.MX93 RM
+	 * section 57.7.8). Calculate the scale factor such that the theoretical
+	 * output (input / scale_factor) rounds up to exactly the desired
+	 * output.
+	 */
+	return min_t(u32, DIV_ROUND_UP(from * 0x1000, to * *dec),
+		     ISI_DOWNSCALE_THRESHOLD);
 }
 
 static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe,
author maintainer
#418 lore raw

PATCH 418/675 media: pci: dm1105: Free allocated workqueue

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Krzysztof Kozlowski <[email protected]>

commit 1a65db225b25bb8c8febf16974c060e0cc242eb9 upstream.

Destroy allocated workqueue in remove() callback to free its resources, thus fixing memory leak.

Fixes 519a4bdcf822 ("V4L/DVB (11984): Add support for yet another SDMC DM1105 based DVB-S card.")
---
 drivers/media/pci/dm1105/dm1105.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/pci/dm1105/dm1105.c +1 -0
@@ -1194,6 +1194,7 @@ static void dm1105_remove(struct pci_dev
 
 	dm1105_hw_exit(dev);
 	free_irq(pdev->irq, dev);
+	destroy_workqueue(dev->wq);
 	pci_iounmap(pdev, dev->io_mem);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
author maintainer
#419 lore raw

PATCH 419/675 media: pwc: Drain fill_buf on start_streaming() failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Valery Borovsky <[email protected]>

commit 906e410dcffbbd99fb4081abab817a830033aa28 upstream.

pwc_isoc_init() submits its isochronous URBs with usb_submit_urb(.., GFP_KERNEL) in a loop. After the first URB is submitted, its completion handler pwc_isoc_handler() can run on another CPU before the loop finishes:

start_streaming()

    pwc_isoc_init()
      usb_submit_urb(urbs[0], GFP_KERNEL)
                                  pwc_isoc_handler(urbs[0])
                                    pdev->fill_buf =
                                      pwc_get_next_fill_buf(pdev)
      usb_submit_urb(urbs[i>0], ..)  -> fails
      pwc_isoc_cleanup(pdev)           /* kills URBs */
      return ret;
    pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED)

pwc_get_next_fill_buf() detaches a buffer from pdev->queued_bufs and stores it in pdev->fill_buf. The error path in start_streaming() only drains pdev->queued_bufs, so the buffer parked in pdev->fill_buf is leaked. vb2_start_streaming() then triggers WARN_ON(owned_by_drv_count).

stop_streaming() already handles this since commit 80b0963e1698 ("[media] pwc: fix WARN_ON"), which added the fill_buf drain in the teardown path but not in the start_streaming() error path. Mirror that handling on failure so start_streaming() returns with no buffer owned by the driver.

Issue identified by automated review of the INV-003 series at
https://sashiko.dev/

Fixes 885fe18f5542 ("[media] pwc: Replace private buffer management code with videobuf2")
---
 drivers/media/usb/pwc/pwc-if.c |    5 +++++
 1 file changed, 5 insertions(+)
drivers/media/usb/pwc/pwc-if.c +5 -0
@@ -726,6 +726,11 @@ static int start_streaming(struct vb2_qu
 		pwc_camera_power(pdev, 0);
 		/* And cleanup any queued bufs!! */
 		pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
+		if (pdev->fill_buf) {
+			vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf,
+					VB2_BUF_STATE_QUEUED);
+			pdev->fill_buf = NULL;
+		}
 	}
 	mutex_unlock(&pdev->v4l2_lock);
 
author maintainer
#420 lore raw

PATCH 420/675 media: pwc: Return queued buffers on start_streaming() failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Valery Borovsky <[email protected]>

commit 975b2ee20e569d47821e4f6c9761b4664d48a6a4 upstream.

The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.

pwc's start_streaming() had two early returns that hit this trap:
-ENODEV when the USB device was already disconnected, and -ERESTARTSYS when mutex_lock_interruptible() was interrupted by a signal. Call the existing pwc_cleanup_queued_bufs() helper with VB2_BUF_STATE_QUEUED before returning (matching the state already used by the pwc_isoc_init() error path in the same function).

This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").

Fixes ceede9fa8939 ("[media] pwc: Fix locking")
---
 drivers/media/usb/pwc/pwc-if.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/media/usb/pwc/pwc-if.c +6 -2
@@ -710,11 +710,15 @@ static int start_streaming(struct vb2_qu
 	struct pwc_device *pdev = vb2_get_drv_priv(vq);
 	int r;
 
-	if (!pdev->udev)
+	if (!pdev->udev) {
+		pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
 		return -ENODEV;
+	}
 
-	if (mutex_lock_interruptible(&pdev->v4l2_lock))
+	if (mutex_lock_interruptible(&pdev->v4l2_lock)) {
+		pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
 		return -ERESTARTSYS;
+	}
 	/* Turn on camera and set LEDS on */
 	pwc_camera_power(pdev, 1);
 	pwc_set_leds(pdev, leds[0], leds[1]);
author maintainer
#421 lore raw

PATCH 421/675 media: qcom: camss: Fix RDI streaming for CSID 680

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryan O'Donoghue <[email protected]>

commit 93ea81d16570442dbca04d2e2563ae8c3e65fa1b upstream.

Fix streaming to RDI1 and RDI2. csid->phy.en_vc contains a bitmask of enabled CSID ports not virtual channels.

We cycle through the number of available CSID ports and test this value against the vc_en bitmask.

We then use the passed value both as an index to the port configuration macros and as a virtual channel index.

This is a very broken pattern. Reviewing the initial introduction of VC support it states that you can only map one CSID to one VFE. This is true however each CSID has multiple sources which can sink inside of the VFE - for example there is a "pixel" path for bayer stats which sources @ CSID(x):3 and sinks on VFE(x):pix.

That is CSID port # 3 should drive VFE port #3. With our current setup only a sensor which drives virtual channel number #3 could possibly enable that setup.

This is deeply wrong the virtual channel has no relevance to hooking CSID to VFE, a fact that is proven after this patch is applied allowing RDI0,RDI1 and RDI2 to function with VC0 whereas before only RDI1 worked.

Another way the current model breaks is the DT field. A sensor driving different data-types on the same VC would not be able to separate the VC:DT pair to separate RDI outputs, thus breaking another feature of VCs in the MIPI data-stream.

Default the VC back to zero. A follow on series will implement subdev streams to actually enable VCs without breaking CSID source to VFE sink.

---
 drivers/media/platform/qcom/camss/camss-csid-680.c |   30 ++++++++++-----------
 1 file changed, 15 insertions(+), 15 deletions(-)
drivers/media/platform/qcom/camss/camss-csid-680.c +15 -15
@@ -219,9 +219,9 @@ static void __csid_configure_top(struct
 	    CSID_TOP_IO_PATH_CFG0(csid->id));
 }
 
-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
 {
-	struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+	struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
 	const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
 								   csid->res->formats->nformats,
 								   input_format->code);
@@ -233,28 +233,28 @@ static void __csid_configure_rdi_stream(
 		lane_cnt = 4;
 
 	val = 0;
-	writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
+	writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port));
 
 	/*
 	 * DT_ID is a two bit bitfield that is concatenated with
 	 * the four least significant bits of the five bit VC
 	 * bitfield to generate an internal CID value.
 	 *
-	 * CSID_RDI_CFG0(vc)
+	 * CSID_RDI_CFG0(port)
 	 * DT_ID : 28:27
 	 * VC    : 26:22
 	 * DT    : 21:16
 	 *
 	 * CID   : VC 3:0 << 2 | DT_ID 1:0
 	 */
-	dt_id = vc & 0x03;
+	dt_id = port & 0x03;
 
 	/* note: for non-RDI path, this should be format->decode_format */
 	val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT;
 	val |= format->data_type << RDI_CFG0_DATA_TYPE;
 	val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
 	val |= dt_id << RDI_CFG0_DT_ID;
-	writel(val, csid->base + CSID_RDI_CFG0(vc));
+	writel(val, csid->base + CSID_RDI_CFG0(port));
 
 	val = RDI_CFG1_TIMESTAMP_STB_FRAME;
 	val |= RDI_CFG1_BYTE_CNTR_EN;
@@ -265,23 +265,23 @@ static void __csid_configure_rdi_stream(
 	val |= RDI_CFG1_CROP_V_EN;
 	val |= RDI_CFG1_PACKING_MIPI;
 
-	writel(val, csid->base + CSID_RDI_CFG1(vc));
+	writel(val, csid->base + CSID_RDI_CFG1(port));
 
 	val = 0;
-	writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+	writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port));
 
 	val = 1;
-	writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+	writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port));
 
 	val = 0;
-	writel(val, csid->base + CSID_RDI_CTRL(vc));
+	writel(val, csid->base + CSID_RDI_CTRL(port));
 
-	val = readl(csid->base + CSID_RDI_CFG0(vc));
+	val = readl(csid->base + CSID_RDI_CFG0(port));
 	if (enable)
 		val |= RDI_CFG0_ENABLE;
 	else
 		val &= ~RDI_CFG0_ENABLE;
-	writel(val, csid->base + CSID_RDI_CFG0(vc));
+	writel(val, csid->base + CSID_RDI_CFG0(port));
 }
 
 static void csid_configure_stream(struct csid_device *csid, u8 enable)
@@ -290,11 +290,11 @@ static void csid_configure_stream(struct
 
 	__csid_configure_top(csid);
 
-       /* Loop through all enabled VCs and configure stream for each */
+	/* Loop through all enabled ports and configure a stream for each */
 	for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
 		if (csid->phy.en_vc & BIT(i)) {
-			__csid_configure_rdi_stream(csid, enable, i);
-			__csid_configure_rx(csid, &csid->phy, i);
+			__csid_configure_rdi_stream(csid, enable, i, 0);
+			__csid_configure_rx(csid, &csid->phy, 0);
 			__csid_ctrl_rdi(csid, enable, i);
 		}
 	}
author maintainer
#422 lore raw

PATCH 422/675 media: qcom: camss: Fix RDI streaming for CSID GEN2

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryan O'Donoghue <[email protected]>

commit 618765634cefbdddafa84f07f82e9dd05b86cb9c upstream.

Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works.

Fix that for CSID gen2 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later.

Fixes 729fc005c8e2 ("media: qcom: camss: Split testgen, RDI and RX for CSID 170")
---
 drivers/media/platform/qcom/camss/camss-csid-gen2.c |   47 ++++++++++----------
 1 file changed, 24 insertions(+), 23 deletions(-)
drivers/media/platform/qcom/camss/camss-csid-gen2.c +24 -23
@@ -203,10 +203,10 @@ static void __csid_ctrl_rdi(struct csid_
 	writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi));
 }
 
-static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 port, u8 vc)
 {
 	struct csid_testgen_config *tg = &csid->testgen;
-	struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+	struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
 	const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
 								   csid->res->formats->nformats,
 								   input_format->code);
@@ -253,10 +253,10 @@ static void __csid_configure_testgen(str
 	writel_relaxed(val, csid->base + CSID_TPG_CTRL);
 }
 
-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
 {
 	/* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
-	struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+	struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
 	const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
 								   csid->res->formats->nformats,
 								   input_format->code);
@@ -267,14 +267,14 @@ static void __csid_configure_rdi_stream(
 	 * the four least significant bits of the five bit VC
 	 * bitfield to generate an internal CID value.
 	 *
-	 * CSID_RDI_CFG0(vc)
+	 * CSID_RDI_CFG0(port)
 	 * DT_ID : 28:27
 	 * VC    : 26:22
 	 * DT    : 21:16
 	 *
 	 * CID   : VC 3:0 << 2 | DT_ID 1:0
 	 */
-	u8 dt_id = vc & 0x03;
+	u8 dt_id = port & 0x03;
 
 	val = 1 << RDI_CFG0_BYTE_CNTR_EN;
 	val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN;
@@ -284,56 +284,57 @@ static void __csid_configure_rdi_stream(
 	val |= format->data_type << RDI_CFG0_DATA_TYPE;
 	val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
 	val |= dt_id << RDI_CFG0_DT_ID;
-	writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_CFG0(port));
 
 	/* CSID_TIMESTAMP_STB_POST_IRQ */
 	val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL;
-	writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_CFG1(port));
 
 	val = 1;
-	writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port));
 
 	val = 0;
-	writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(port));
 
 	val = 1;
-	writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port));
 
 	val = 0;
-	writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port));
 
 	val = 1;
-	writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(port));
 
 	val = 0;
-	writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(port));
 
 	val = 1;
-	writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(port));
 
 	val = 0;
-	writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(port));
 
 	val = 0;
-	writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_CTRL(port));
 
-	val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc));
+	val = readl_relaxed(csid->base + CSID_RDI_CFG0(port));
 	val |=  enable << RDI_CFG0_ENABLE;
-	writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
+	writel_relaxed(val, csid->base + CSID_RDI_CFG0(port));
 }
 
 static void csid_configure_stream(struct csid_device *csid, u8 enable)
 {
 	struct csid_testgen_config *tg = &csid->testgen;
 	u8 i;
-	/* Loop through all enabled VCs and configure stream for each */
+
+	/* Loop through all enabled ports and configure a stream for each */
 	for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
 		if (csid->phy.en_vc & BIT(i)) {
 			if (tg->enabled)
-				__csid_configure_testgen(csid, enable, i);
+				__csid_configure_testgen(csid, enable, i, 0);
 
-			__csid_configure_rdi_stream(csid, enable, i);
-			__csid_configure_rx(csid, &csid->phy, i);
+			__csid_configure_rdi_stream(csid, enable, i, 0);
+			__csid_configure_rx(csid, &csid->phy, 0);
 			__csid_ctrl_rdi(csid, enable, i);
 		}
 }
author maintainer
#423 lore raw

PATCH 423/675 media: qcom: camss: Fix RDI streaming for CSID GEN3

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryan O'Donoghue <[email protected]>

commit ad136e52634d5a393a9dc93383f8ea1e898faf37 upstream.

Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works.

Fix that for CSID gen3 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later.

---
 drivers/media/platform/qcom/camss/camss-csid-gen3.c |   28 ++++++++++----------
 1 file changed, 14 insertions(+), 14 deletions(-)
drivers/media/platform/qcom/camss/camss-csid-gen3.c +14 -14
@@ -145,12 +145,12 @@ static void __csid_configure_wrapper(str
 	writel(val, csid->camss->csid_wrapper_base + CSID_IO_PATH_CFG0(csid->id));
 }
 
-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
 {
 	u32 val;
 	u8 lane_cnt = csid->phy.lane_cnt;
 	/* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
-	struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+	struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
 	const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
 								   csid->res->formats->nformats,
 								   input_format->code);
@@ -163,14 +163,14 @@ static void __csid_configure_rdi_stream(
 	 * the four least significant bits of the five bit VC
 	 * bitfield to generate an internal CID value.
 	 *
-	 * CSID_RDI_CFG0(vc)
+	 * CSID_RDI_CFG0(port)
 	 * DT_ID : 28:27
 	 * VC    : 26:22
 	 * DT    : 21:16
 	 *
 	 * CID   : VC 3:0 << 2 | DT_ID 1:0
 	 */
-	u8 dt_id = vc & 0x03;
+	u8 dt_id = port & 0x03;
 
 	val = RDI_CFG0_TIMESTAMP_EN;
 	val |= RDI_CFG0_TIMESTAMP_STB_SEL;
@@ -180,7 +180,7 @@ static void __csid_configure_rdi_stream(
 	val |= format->data_type << RDI_CFG0_DT;
 	val |= dt_id << RDI_CFG0_DT_ID;
 
-	writel(val, csid->base + CSID_RDI_CFG0(vc));
+	writel(val, csid->base + CSID_RDI_CFG0(port));
 
 	val = RDI_CFG1_PACKING_FORMAT_MIPI;
 	val |= RDI_CFG1_PIX_STORE;
@@ -189,22 +189,22 @@ static void __csid_configure_rdi_stream(
 	val |= RDI_CFG1_CROP_H_EN;
 	val |= RDI_CFG1_CROP_V_EN;
 
-	writel(val, csid->base + CSID_RDI_CFG1(vc));
+	writel(val, csid->base + CSID_RDI_CFG1(port));
 
 	val = 0;
-	writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+	writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port));
 
 	val = 1;
-	writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+	writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port));
 
 	val = 0;
-	writel(val, csid->base + CSID_RDI_CTRL(vc));
+	writel(val, csid->base + CSID_RDI_CTRL(port));
 
-	val = readl(csid->base + CSID_RDI_CFG0(vc));
+	val = readl(csid->base + CSID_RDI_CFG0(port));
 
 	if (enable)
 		val |= RDI_CFG0_EN;
-	writel(val, csid->base + CSID_RDI_CFG0(vc));
+	writel(val, csid->base + CSID_RDI_CFG0(port));
 }
 
 static void csid_configure_stream(struct csid_device *csid, u8 enable)
@@ -213,11 +213,11 @@ static void csid_configure_stream(struct
 
 	__csid_configure_wrapper(csid);
 
-	/* Loop through all enabled VCs and configure stream for each */
+	/* Loop through all enabled ports and configure a stream for each */
 	for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
 		if (csid->phy.en_vc & BIT(i)) {
-			__csid_configure_rdi_stream(csid, enable, i);
-			__csid_configure_rx(csid, &csid->phy, i);
+			__csid_configure_rdi_stream(csid, enable, i, 0);
+			__csid_configure_rx(csid, &csid->phy, 0);
 			__csid_ctrl_rdi(csid, enable, i);
 		}
 }
author maintainer
#424 lore raw

PATCH 424/675 media: radio-si476x: Unregister v4l2_device on probe failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Myeonghun Pak <[email protected]>

commit 436a693af04ffb889aaf87cb69ec1f2b21d3569c upstream.

si476x_radio_probe() registers radio->v4l2dev before allocating the V4L2 controls and before registering the video device. If any of those later steps fails, probe returns through the exit label after freeing only the control handler.

A failed probe does not call si476x_radio_remove(), so the v4l2_device_unregister() there is not reached. This leaves the parent device reference taken by v4l2_device_register() behind on the error path.

Unregister the V4L2 device in the probe error path after freeing the controls.

Fixes b879a9c2a755 ("[media] v4l2: Add a V4L2 driver for SI476X MFD")
---
 drivers/media/radio/radio-si476x.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/radio/radio-si476x.c +1 -0
@@ -1493,6 +1493,7 @@ static int si476x_radio_probe(struct pla
 	return 0;
 exit:
 	v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
+	v4l2_device_unregister(&radio->v4l2dev);
 	return rval;
 }
 
author maintainer
#425 lore raw

PATCH 425/675 media: rtl2832: fix use-after-free in rtl2832_remove()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Deepanshu Kartikey <[email protected]>

commit 680daf40a82d483949f87f0d8f98639dc47e610c upstream.

cancel_delayed_work_sync() is called before i2c_mux_del_adapters() in rtl2832_remove(). While the cancel waits for any running instance of i2c_gate_work to finish, it does not prevent the timer from being rescheduled by a concurrent thread.

During probe, the r820t_attach() call attempts I2C transfers through the mux adapter. These transfers go through i2c_mux_master_xfer(), which calls rtl2832_deselect() after the transfer completes, rescheduling i2c_gate_work via schedule_delayed_work(). If this transfer is still in flight when rtl2832_remove() runs, rtl2832_deselect() can reschedule i2c_gate_work after it has been cancelled, causing a use-after-free when kfree(dev) is called.

Fix this by calling i2c_mux_del_adapters() before cancel_delayed_work_sync(). Once the mux adapter is unregistered, no new I2C transfers can go through it, so rtl2832_deselect() can no longer reschedule i2c_gate_work. The subsequent cancel_delayed_work_sync() is then guaranteed to be final.

Fixes cddcc40b1b15 ("[media] rtl2832: convert to use an explicit i2c mux core")
Reported-by [email protected]
---
 drivers/media/dvb-frontends/rtl2832.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/dvb-frontends/rtl2832.c +2 -2
@@ -1115,10 +1115,10 @@ static void rtl2832_remove(struct i2c_cl
 
 	dev_dbg(&client->dev, "\n");
 
-	cancel_delayed_work_sync(&dev->i2c_gate_work);
-
 	i2c_mux_del_adapters(dev->muxc);
 
+	cancel_delayed_work_sync(&dev->i2c_gate_work);
+
 	regmap_exit(dev->regmap);
 
 	kfree(dev);
author maintainer
#426 lore raw

PATCH 426/675 media: rtl2832_sdr: Return queued buffers on start_streaming() failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Valery Borovsky <[email protected]>

commit 33ca0aab6f4bd90921fc1395478f38f72c4d19af upstream.

The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.

rtl2832_sdr_start_streaming() had multiple error paths that hit this
trap: two direct early returns (-ENODEV, -ERESTARTSYS), plus six goto err paths covering subdev s_power, tuner setup, ADC setup, stream-buffer allocation, urb allocation, and urb submission failures. None of them returned the queued buffers.

The original function had no distinct success exit and fell straight through into the err label, which previously only did mutex_unlock and "return ret". Adding queued-buffer cleanup at err must therefore be paired with an explicit success return; otherwise every successful start would also drain the buffer queue and kill streaming. Add that success return, then add rtl2832_sdr_cleanup_queued_bufs() at the err label and before each early return.

The cleanup helper takes a vb2_buffer_state argument so that the start_streaming error paths can pass VB2_BUF_STATE_QUEUED (as expected by userspace on start_streaming failure) while stop_streaming keeps its existing VB2_BUF_STATE_ERROR semantics.

This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").

The err label still does not roll back power_ctrl(), frontend_ctrl(), the POWER_ON flag, or stream/URB allocations that may have happened before the failing step. Those are pre-existing leaks of a different class and are not addressed here.

Fixes 771138920eaf ("[media] rtl2832_sdr: Realtek RTL2832 SDR driver module")
---
 drivers/media/dvb-frontends/rtl2832_sdr.c |   19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)
drivers/media/dvb-frontends/rtl2832_sdr.c +14 -5
@@ -399,7 +399,8 @@ static int rtl2832_sdr_alloc_urbs(struct
 }
 
 /* Must be called with vb_queue_lock hold */
-static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev)
+static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev,
+					    enum vb2_buffer_state state)
 {
 	struct platform_device *pdev = dev->pdev;
 	unsigned long flags;
@@ -413,7 +414,7 @@ static void rtl2832_sdr_cleanup_queued_b
 		buf = list_entry(dev->queued_bufs.next,
 				struct rtl2832_sdr_frame_buf, list);
 		list_del(&buf->list);
-		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+		vb2_buffer_done(&buf->vb.vb2_buf, state);
 	}
 	spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
 }
@@ -855,11 +856,15 @@ static int rtl2832_sdr_start_streaming(s
 
 	dev_dbg(&pdev->dev, "\n");
 
-	if (!dev->udev)
+	if (!dev->udev) {
+		rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
 		return -ENODEV;
+	}
 
-	if (mutex_lock_interruptible(&dev->v4l2_lock))
+	if (mutex_lock_interruptible(&dev->v4l2_lock)) {
+		rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
 		return -ERESTARTSYS;
+	}
 
 	if (d->props->power_ctrl)
 		d->props->power_ctrl(d, 1);
@@ -900,7 +905,11 @@ static int rtl2832_sdr_start_streaming(s
 	if (ret)
 		goto err;
 
+	mutex_unlock(&dev->v4l2_lock);
+	return 0;
+
 err:
+	rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
 	mutex_unlock(&dev->v4l2_lock);
 
 	return ret;
@@ -920,7 +929,7 @@ static void rtl2832_sdr_stop_streaming(s
 	rtl2832_sdr_kill_urbs(dev);
 	rtl2832_sdr_free_urbs(dev);
 	rtl2832_sdr_free_stream_bufs(dev);
-	rtl2832_sdr_cleanup_queued_bufs(dev);
+	rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR);
 	rtl2832_sdr_unset_adc(dev);
 
 	/* sleep tuner */
author maintainer
#427 lore raw

PATCH 427/675 media: rzg2l-cru: Skip ICnMC configuration when ICnSVC is used

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tommaso Merciai <[email protected]>

commit cd4ce68875f406b0cf3f5a94c0fd22989689222f upstream.

When the CRU is configured to use ICnSVC for virtual channel mapping, as on the RZ/{G3E, V2H/P} SoC, the ICnMC register must not be programmed.

Return early after setting up ICnSVC to avoid overriding the ICnMC register, which is not applicable in this mode.

This prevents unintended register programming when ICnSVC is enabled.

Fixes 3c5ca0a48bb0 ("media: rzg2l-cru: Drop function pointer to configure CSI")
[Rework to not break image format programming]
---
 .../platform/renesas/rzg2l-cru/rzg2l-cru-regs.h |  1 +
 .../platform/renesas/rzg2l-cru/rzg2l-video.c    | 17 +++++++++++------
 2 files changed, 12 insertions(+), 6 deletions(-)
drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h +1 -0
index a5a57369ef0e..10e62f2646d0 100644
@@ -60,6 +60,7 @@
 #define ICnMC_CSCTHR			BIT(5)
 #define ICnMC_INF(x)			((x) << 16)
 #define ICnMC_VCSEL(x)			((x) << 22)
+#define ICnMC_VCSEL_MASK		GENMASK(23, 22)
 #define ICnMC_INF_MASK			GENMASK(21, 16)
 
 #define ICnMS_IA			BIT(2)
drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +11 -6
index 162e2ace6931..6aea7c244df1 100644
@@ -262,19 +262,24 @@ static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru,
 				 u8 csi_vc)
 {
 	const struct rzg2l_cru_info *info = cru->info;
-	u32 icnmc = ICnMC_INF(ip_fmt->datatype);
+	u32 icnmc = rzg2l_cru_read(cru, info->image_conv) & ~(ICnMC_INF_MASK |
+							      ICnMC_VCSEL_MASK);
+	icnmc |= ICnMC_INF(ip_fmt->datatype);
 
+	/*
+	 * VC filtering goes through SVC register on G3E/V2H.
+	 *
+	 * FIXME: virtual channel filtering is likely broken and only VC=0
+	 * works.
+	 */
 	if (cru->info->regs[ICnSVC]) {
 		rzg2l_cru_write(cru, ICnSVCNUM, csi_vc);
 		rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) |
 				ICnSVC_SVC2(2) | ICnSVC_SVC3(3));
+	} else {
+		icnmc |= ICnMC_VCSEL(csi_vc);
 	}
 
-	icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK;
-
-	/* Set virtual channel CSI2 */
-	icnmc |= ICnMC_VCSEL(csi_vc);
-
 	rzg2l_cru_write(cru, info->image_conv, icnmc);
 }
 
Signature · 1 line
2.55.0
author maintainer
#428 lore raw

PATCH 428/675 media: saa7134: Fix a possible memory leak in saa7134_video_init1

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ma Ke <[email protected]>

commit f86ed548386e3050e5f8f25b450d09dc009d9a88 upstream.

In saa7134_video_init1(), the return value of the first saa7134_pgtable_alloc() is not checked. If it fails, the function continues as if successful, leaving the driver with an invalid page table. Additionally, if vb2_queue_init() for the VBI queue fails after the video queue page table has been allocated, the allocated memory is not freed before returning. The second saa7134_pgtable_alloc() also lacks a return value check. Errors occur during device probing before the device is fully registered, the normal cleanup path in saa7134_finidev() is not executed, leading to memory leaks and potential use of uninitialized DMA resources.

Check the return value of both saa7134_pgtable_alloc() calls and propagate errors. On failure of any later step, free allocated page tables to avoid memory leaks. Ensure control handlers are also released on error to prevent further resource leakage.

Found by code review.

Fixes a00e68888d5d ("[media] saa7134: move saa7134_pgtable to saa7134_dmaqueue")
---
 drivers/media/pci/saa7134/saa7134-video.c |   25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)
drivers/media/pci/saa7134/saa7134-video.c +20 -5
@@ -1714,8 +1714,10 @@ int saa7134_video_init1(struct saa7134_d
 	q->dev = &dev->pci->dev;
 	ret = vb2_queue_init(q);
 	if (ret)
-		return ret;
-	saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt);
+		goto err_free_ctrl;
+	ret = saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt);
+	if (ret)
+		goto err_free_ctrl;
 
 	q = &dev->vbi_vbq;
 	q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
@@ -1732,11 +1734,24 @@ int saa7134_video_init1(struct saa7134_d
 	q->lock = &dev->lock;
 	q->dev = &dev->pci->dev;
 	ret = vb2_queue_init(q);
-	if (ret)
-		return ret;
-	saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt);
+	if (ret) {
+		saa7134_pgtable_free(dev->pci, &dev->video_q.pt);
+		goto err_free_ctrl;
+	}
+
+	ret = saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt);
+	if (ret) {
+		saa7134_pgtable_free(dev->pci, &dev->video_q.pt);
+		goto err_free_ctrl;
+	}
 
 	return 0;
+
+err_free_ctrl:
+	v4l2_ctrl_handler_free(&dev->ctrl_handler);
+	if (card_has_radio(dev))
+		v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
+	return ret;
 }
 
 void saa7134_video_fini(struct saa7134_dev *dev)
author maintainer
#429 lore raw

PATCH 429/675 media: stm32-dcmipp: Return queued buffers on start_streaming() failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Valery Borovsky <[email protected]>

commit ffc8eec06378a340d708c889184ab3e14b57d540 upstream.

The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.

dcmipp_bytecap_start_streaming() returned -EINVAL when the source subdevice could not be resolved from the media graph, before pm_runtime_resume_and_get() and media_pipeline_start() had been called. The remaining error paths already converge on the err_buffer_done label, which calls dcmipp_bytecap_all_buffers_done(..., VB2_BUF_STATE_QUEUED). Jump to that label directly: the intermediate err_pm_put / err_media_pipeline_stop labels are skipped, which is correct because nothing they would undo has happened yet.

This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").

Fixes 28e0f3772296 ("media: stm32-dcmipp: STM32 DCMIPP camera interface driver")
---
 drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c +4 -2
@@ -398,8 +398,10 @@ static int dcmipp_bytecap_start_streamin
 	 */
 	if (!vcap->s_subdev) {
 		pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]);
-		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
-			return -EINVAL;
+		if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) {
+			ret = -EINVAL;
+			goto err_buffer_done;
+		}
 		vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity);
 		vcap->s_subdev_pad_nb = pad->index;
 	}
author maintainer
#430 lore raw

PATCH 430/675 media: stm32: dcmi: unregister notifier on probe failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Myeonghun Pak <[email protected]>

commit 084973ebd67b28f0945c5d45408f86c58b540110 upstream.

dcmi_graph_init() registers the async notifier before dcmi_probe() toggles the reset line. If reset_control_assert() or reset_control_deassert() fails afterwards, probe returns through err_cleanup and the driver core will not call dcmi_remove().

Unregister the notifier before cleaning it up on that error path, matching the successful remove path and the V4L2 async notifier lifetime rules.

Fixes d079f94c9046 ("media: platform: Switch to v4l2_async_notifier_add_subdev")
[hverkuil: added Fixes tag]
---
 drivers/media/platform/st/stm32/stm32-dcmi.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/platform/st/stm32/stm32-dcmi.c +1 -0
@@ -2063,6 +2063,7 @@ static int dcmi_probe(struct platform_de
 	return 0;
 
 err_cleanup:
+	v4l2_async_nf_unregister(&dcmi->notifier);
 	v4l2_async_nf_cleanup(&dcmi->notifier);
 err_media_entity_cleanup:
 	media_entity_cleanup(&dcmi->vdev->entity);
author maintainer
#431 lore raw

PATCH 431/675 media: sun4i-csi: Return queued buffers on start_streaming() failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Valery Borovsky <[email protected]>

commit bbba3e260a62810a717b4442a3bb96d0ec0f6309 upstream.

The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.

sun4i_csi_start_streaming() returned -EINVAL when no matching CSI format could be found, before any setup (scratch buffer allocation, pipeline start) had been performed. The remaining error paths already converge on the err_clear_dma_queue label, which calls return_all_buffers(..., VB2_BUF_STATE_QUEUED) under csi->qlock. Jump to that label directly: the intermediate err_disable_device / err_disable_pipeline / err_free_scratch_buffer labels are skipped, which is correct because nothing they would undo has happened yet.

This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").

Fixes 577bbf23b758 ("media: sunxi: Add A10 CSI driver")
---
 drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +4 -2
@@ -234,8 +234,10 @@ static int sun4i_csi_start_streaming(str
 	int ret;
 
 	csi_fmt = sun4i_csi_find_format(&csi->fmt.pixelformat, NULL);
-	if (!csi_fmt)
-		return -EINVAL;
+	if (!csi_fmt) {
+		ret = -EINVAL;
+		goto err_clear_dma_queue;
+	}
 
 	dev_dbg(csi->dev, "Starting capture\n");
 
author maintainer
#432 lore raw

PATCH 432/675 media: synopsys: hdmirx: Fix HPD lane hold time

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Dmitry Osipenko <[email protected]>

commit d1162a5adbb5e95953d460b5bde3a04cd4473fe9 upstream.

Increase time of holding HPD lane low by 50ms. This fixes EDID change not detected by source/display side.

---
 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +2 -2
@@ -504,9 +504,9 @@ static void hdmirx_hpd_ctrl(struct snps_
 	hdmirx_writel(hdmirx_dev, CORE_CONFIG,
 		      hdmirx_dev->hpd_trigger_level_high ? en : !en);
 
-	/* 100ms delay as per HDMI spec */
+	/* 100ms delay as per HDMI spec + extra 50ms to cover internal delay */
 	if (!en)
-		msleep(100);
+		msleep(100 + 50);
 }
 
 static void hdmirx_write_edid_data(struct snps_hdmirx_dev *hdmirx_dev,
author maintainer
#433 lore raw

PATCH 433/675 media: tegra-video: vi: fix invalid u32 return value in format lookup

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hungyu Lin <[email protected]>

commit d5b50055338e131a1a99f923ebb0361974a00f36 upstream.

tegra_get_format_fourcc_by_idx() returns a u32 but uses -EINVAL to signal an out-of-bounds index. This results in a large unsigned value being returned, which may be interpreted as a valid fourcc.

Returning 0 is not a valid fourcc either. This condition should never happen, so use WARN_ON_ONCE() to catch unexpected out-of-bounds access and return a valid fallback format instead.

Fixes 3d8a97eabef0 ("media: tegra-video: Add Tegra210 Video input driver")
---
 drivers/staging/media/tegra-video/vi.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/staging/media/tegra-video/vi.c +2 -2
@@ -80,8 +80,8 @@ static int tegra_get_format_idx_by_code(
 static u32 tegra_get_format_fourcc_by_idx(struct tegra_vi *vi,
 					  unsigned int index)
 {
-	if (index >= vi->soc->nformats)
-		return -EINVAL;
+	if (WARN_ON_ONCE(index >= vi->soc->nformats))
+		return vi->soc->video_formats[0].fourcc;
 
 	return vi->soc->video_formats[index].fourcc;
 }
author maintainer
#434 lore raw

PATCH 434/675 media: ti: vpe: unwind v4l2 device registration on probe error

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Myeonghun Pak <[email protected]>

commit e0f1c9a90ef665f2587c274a8fed59f2dfc575a6 upstream.

If the vpe_top resource is missing, vpe_probe() returns -ENODEV after v4l2_device_register() has succeeded. Probe failures do not call the driver's remove callback, so the v4l2 device remains registered on that error path.

Route that failure through the existing v4l2_device_unregister() unwind label, matching the other errors after v4l2_device_register().

Fixes 4d59c7d45585 ("media: ti-vpe: vpe: Add missing null pointer checks")
---
 drivers/media/platform/ti/vpe/vpe.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/media/platform/ti/vpe/vpe.c +2 -1
@@ -2546,7 +2546,8 @@ static int vpe_probe(struct platform_dev
 						"vpe_top");
 	if (!dev->res) {
 		dev_err(&pdev->dev, "missing 'vpe_top' resources data\n");
-		return -ENODEV;
+		ret = -ENODEV;
+		goto v4l2_dev_unreg;
 	}
 
 	/*
author maintainer
#435 lore raw

PATCH 435/675 media: v4l2-ctrls-request: add NULL check in v4l2_ctrl_request_complete()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Sergey Shtylyov <[email protected]>

commit caced3578bf9f104a4aaad8f46c4c719e705d9a6 upstream.

If CONFIG_MEDIA_CONTROLLER is undefined, media_request_object_find() will always return NULL, so its 2nd call in v4l2_ctrl_request_complete() would fail as well as the 1st one and thus cause hdl to have a wrong value (at the top of memory) and list_for_each_entry() to iterate over the garbage data located there. Add NULL check for the 2nd call and place the error cleanup at the end of v4l2_ctrl_request_complete()...

Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool.

Fixes c3bf5129f339 ("media: v4l2-ctrls: always copy the controls on completion")
---
 drivers/media/v4l2-core/v4l2-ctrls-request.c |   14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)
drivers/media/v4l2-core/v4l2-ctrls-request.c +9 -5
@@ -348,13 +348,12 @@ void v4l2_ctrl_request_complete(struct m
 		ret = v4l2_ctrl_handler_init(hdl, (main_hdl->nr_of_buckets - 1) * 8);
 		if (!ret)
 			ret = v4l2_ctrl_request_bind(req, hdl, main_hdl);
-		if (ret) {
-			v4l2_ctrl_handler_free(hdl);
-			kfree(hdl);
-			return;
-		}
+		if (ret)
+			goto error;
 		hdl->request_is_queued = true;
 		obj = media_request_object_find(req, &req_ops, main_hdl);
+		if (!obj)
+			goto error;
 	}
 	hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj);
 
@@ -389,6 +388,11 @@ void v4l2_ctrl_request_complete(struct m
 	mutex_unlock(main_hdl->lock);
 	media_request_object_complete(obj);
 	media_request_object_put(obj);
+	return;
+
+error:
+	v4l2_ctrl_handler_free(hdl);
+	kfree(hdl);
 }
 EXPORT_SYMBOL(v4l2_ctrl_request_complete);
 
author maintainer
#436 lore raw

PATCH 436/675 media: v4l2-ctrls: validate HEVC active reference counts

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Pengpeng Hou <[email protected]>

commit afbe4bc252d90a6f8fad869b06d5430f615f22f9 upstream.

HEVC slice parameters are shared stateless V4L2 controls, but the common validation path does not verify the active L0/L1 reference counts before driver-specific code consumes them.

The original report came from Cedrus, but the active count bounds are not Cedrus-specific. Validate them in the common HEVC slice control path so stateless HEVC drivers get the same basic guarantees as soon as the control is queued.

Do not reject ref_idx_l0/ref_idx_l1 entries here. Existing userspace may use out-of-range sentinel values such as 0xff for missing references, and some hardware can use that information for concealment. Keep this common check limited to the active reference counts.

Fixes d395a78db9eab ("media: hevc: Add decode params control")
---
 drivers/media/v4l2-core/v4l2-ctrls-core.c |   13 +++++++++++++
 1 file changed, 13 insertions(+)
drivers/media/v4l2-core/v4l2-ctrls-core.c +13 -0
@@ -882,6 +882,7 @@ static int std_validate_compound(const s
 	struct v4l2_ctrl_h264_decode_params *p_h264_dec_params;
 	struct v4l2_ctrl_hevc_sps *p_hevc_sps;
 	struct v4l2_ctrl_hevc_pps *p_hevc_pps;
+	struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params;
 	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
 	struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
 	struct v4l2_area *area;
@@ -1171,6 +1172,18 @@ static int std_validate_compound(const s
 		break;
 
 	case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
+		p_hevc_slice_params = p;
+
+		if (p_hevc_slice_params->num_ref_idx_l0_active_minus1 >=
+		    V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
+			return -EINVAL;
+
+		if (p_hevc_slice_params->slice_type != V4L2_HEVC_SLICE_TYPE_B)
+			break;
+
+		if (p_hevc_slice_params->num_ref_idx_l1_active_minus1 >=
+		    V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
+			return -EINVAL;
 		break;
 
 	case V4L2_CTRL_TYPE_HDR10_CLL_INFO:
author maintainer
#437 lore raw

PATCH 437/675 media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mirela Rabulea <[email protected]>

commit 06cb687a5132fcffe624c0070576ab852ac6b568 upstream.

The v4l2 helper v4l2_async_register_subdev_sensor() calls v4l2_async_register_subdev(), which is a macro that expands to __v4l2_async_register_subdev(sd,THIS_MODULE). Since the macro is expanded inside v4l2-fwnode.c, THIS_MODULE resolves to the v4l2-fwnode module rather than the sensor driver module that originally set sd->owner. When v4l2-fwnode is built-in, THIS_MODULE evaluates to NULL, which then overwrites the sensor driver's owner with NULL.

This causes the problem that the sensor module's reference count is never incremented during async registration, so the module can be removed while the subdevice is still in use by a notifier (e.g., a CSI-2 receiver bridge driver).

Fix this by renaming v4l2_async_register_subdev_sensor() to __v4l2_async_register_subdev_sensor() with an added explicit module argument and introducing a wrapper macro:

    #define v4l2_async_register_subdev_sensor(sd) \
        __v4l2_async_register_subdev_sensor(sd, THIS_MODULE)

This ensures the sensor driver module is properly referenced even when the sensor driver does not init the owner field before calling v4l2_async_register_subdev_sensor() and prevents premature module removal.

---
 drivers/media/v4l2-core/v4l2-fwnode.c |    6 +++---
 include/media/v4l2-async.h            |    4 +++-
 2 files changed, 6 insertions(+), 4 deletions(-)
drivers/media/v4l2-core/v4l2-fwnode.c +3 -3
@@ -1246,7 +1246,7 @@ v4l2_async_nf_parse_fwnode_sensor(struct
 	return 0;
 }
 
-int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
+int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module)
 {
 	struct v4l2_async_notifier *notifier;
 	int ret;
@@ -1272,7 +1272,7 @@ int v4l2_async_register_subdev_sensor(st
 	if (ret < 0)
 		goto out_cleanup;
 
-	ret = v4l2_async_register_subdev(sd);
+	ret = __v4l2_async_register_subdev(sd, module);
 	if (ret < 0)
 		goto out_unregister;
 
@@ -1290,7 +1290,7 @@ out_cleanup:
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor);
+EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_sensor);
 
 MODULE_DESCRIPTION("V4L2 fwnode binding parsing library");
 MODULE_LICENSE("GPL");
include/media/v4l2-async.h +3 -1
@@ -333,8 +333,10 @@ int __v4l2_async_register_subdev(struct
  * An error is returned if the module is no longer loaded on any attempts
  * to register it.
  */
+#define v4l2_async_register_subdev_sensor(sd) \
+	__v4l2_async_register_subdev_sensor(sd, THIS_MODULE)
 int __must_check
-v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd);
+__v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module);
 
 /**
  * v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous
author maintainer
#438 lore raw

PATCH 438/675 media: v4l2-subdev: Fail {enable,disable}_streams and s_streaming nicely

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Sakari Ailus <[email protected]>

commit 0bcbfd1c1142d85faef8df5cb679d37f71394c5f upstream.

If a sub-device does not set enable_streams() and disable_streams() pad ops while it sets the s_stream() video op to v4l2_subdev_s_stream_helper(), enabling or disabling streaming either way on the sub-device will result calling v4l2_subdev_s_stream_helper() and v4l2_subdev_{enable,disable}_streams() recursively, exhausting the stack. Return -ENOIOCTLCMD in this case to handle the situation gracefully.

Fixes b62949ddaa52 ("media: subdev: Support single-stream case in v4l2_subdev_enable/disable_streams()")
---
 drivers/media/v4l2-core/v4l2-subdev.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/media/v4l2-core/v4l2-subdev.c +4 -0
@@ -2506,6 +2506,10 @@ int v4l2_subdev_s_stream_helper(struct v
 	u64 source_mask = 0;
 	int pad_index = -1;
 
+	if (WARN_ON(!v4l2_subdev_has_op(sd, pad, enable_streams) ||
+		    !v4l2_subdev_has_op(sd, pad, disable_streams)))
+		return -ENOIOCTLCMD;
+
 	/*
 	 * Find the source pad. This helper is meant for subdevs that have a
 	 * single source pad, so failures shouldn't happen, but catch them
author maintainer
#439 lore raw

PATCH 439/675 media: vb2: use ssize_t for vb2_read/vb2_write

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zile Xiong <[email protected]>

commit a562d6dc86bdfdd299e1b4734977a8d63e803583 upstream.

vb2_read() and vb2_write() return size_t, but propagate negative errno values from __vb2_perform_fileio().

This relies on implicit signed/unsigned conversions in callers (e.g. vb2_fop_read()) to recover error codes:

    __vb2_perform_fileio() -> -EINVAL
    vb2_read()             -> (size_t)-EINVAL
    vb2_fop_read()         -> -EINVAL

This relies on implicit conversions that are not obvious.

These helpers are exported (EXPORT_SYMBOL_GPL) and part of the vb2 API, so changing their return type may affect existing users.

However, they conceptually follow read/write semantics, where ssize_t is typically used to return either a byte count or a negative error code.

Switch vb2_read() and vb2_write() to ssize_t, and update __vb2_perform_fileio() accordingly.

Fixes b25748fe6126 ("[media] v4l: videobuf2: add read() and write() emulator")
---
 drivers/media/common/videobuf2/videobuf2-core.c |   12 ++++++------
 include/media/videobuf2-core.h                  |    8 ++++----
 2 files changed, 10 insertions(+), 10 deletions(-)
drivers/media/common/videobuf2/videobuf2-core.c +6 -6
@@ -3006,8 +3006,8 @@ static int __vb2_cleanup_fileio(struct v
  * @nonblock:	mode selector (1 means blocking calls, 0 means nonblocking)
  * @read:	access mode selector (1 means read, 0 means write)
  */
-static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
-		loff_t *ppos, int nonblock, int read)
+static ssize_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
+				    loff_t *ppos, int nonblock, int read)
 {
 	struct vb2_fileio_data *fileio;
 	struct vb2_fileio_buf *buf;
@@ -3170,15 +3170,15 @@ static size_t __vb2_perform_fileio(struc
 	return ret;
 }
 
-size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
-		loff_t *ppos, int nonblocking)
+ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+		 loff_t *ppos, int nonblocking)
 {
 	return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
 }
 EXPORT_SYMBOL_GPL(vb2_read);
 
-size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
-		loff_t *ppos, int nonblocking)
+ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
+		  loff_t *ppos, int nonblocking)
 {
 	return __vb2_perform_fileio(q, (char __user *) data, count,
 							ppos, nonblocking, 0);
include/media/videobuf2-core.h +4 -4
@@ -1106,8 +1106,8 @@ __poll_t vb2_core_poll(struct vb2_queue
  * @ppos:	file handle position tracking pointer
  * @nonblock:	mode selector (1 means blocking calls, 0 means nonblocking)
  */
-size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
-		loff_t *ppos, int nonblock);
+ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+		 loff_t *ppos, int nonblock);
 /**
  * vb2_write() - implements write() syscall logic.
  * @q:		pointer to &struct vb2_queue with videobuf2 queue.
@@ -1116,8 +1116,8 @@ size_t vb2_read(struct vb2_queue *q, cha
  * @ppos:	file handle position tracking pointer
  * @nonblock:	mode selector (1 means blocking calls, 0 means nonblocking)
  */
-size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
-		loff_t *ppos, int nonblock);
+ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
+		  loff_t *ppos, int nonblock);
 
 /**
  * typedef vb2_thread_fnc - callback function for use with vb2_thread.
author maintainer
#440 lore raw

PATCH 440/675 media: verisilicon: Export only needed pixels formats

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Benjamin Gaignard <[email protected]>

commit e0f5d6ae76423ec5b6f97c7c3e1f02187b988afd upstream.

Some pixel formats can only be produced if the decoder outputs reference pictures directly. In some cases, such as AV1 film-grain, the use of the post-processor is strictly required. In this case, only enumerate the post-processor supported formats. The exception is when V4L2_FMTDESC_FLAG_ENUM_ALL is set, in this case, we enumerate everything regardless of the state.

Fixes bcd4f091cf1e ("media: verisilicon: Use V4L2_FMTDESC_FLAG_ENUM_ALL flag")
---
 drivers/media/platform/verisilicon/hantro_v4l2.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/media/platform/verisilicon/hantro_v4l2.c +5 -1
@@ -222,6 +222,7 @@ static int vidioc_enum_fmt(struct file *
 	unsigned int num_fmts, i, j = 0;
 	bool skip_mode_none, enum_all_formats;
 	u32 index = f->index & ~V4L2_FMTDESC_FLAG_ENUM_ALL;
+	bool need_postproc = ctx->need_postproc;
 
 	/*
 	 * If the V4L2_FMTDESC_FLAG_ENUM_ALL flag is set, we want to enumerate all
@@ -230,6 +231,9 @@ static int vidioc_enum_fmt(struct file *
 	enum_all_formats = !!(f->index & V4L2_FMTDESC_FLAG_ENUM_ALL);
 	f->index = index;
 
+	if (enum_all_formats)
+		need_postproc = HANTRO_AUTO_POSTPROC;
+
 	/*
 	 * When dealing with an encoder:
 	 *  - on the capture side we want to filter out all MODE_NONE formats.
@@ -242,7 +246,7 @@ static int vidioc_enum_fmt(struct file *
 	 */
 	skip_mode_none = capture == ctx->is_encoder;
 
-	formats = hantro_get_formats(ctx, &num_fmts, HANTRO_AUTO_POSTPROC);
+	formats = hantro_get_formats(ctx, &num_fmts, need_postproc);
 	for (i = 0; i < num_fmts; i++) {
 		bool mode_none = formats[i].codec_mode == HANTRO_MODE_NONE;
 		fmt = &formats[i];
author maintainer
#441 lore raw

PATCH 441/675 media: vidtv: fix reference leak on failed device registration

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guangshuo Li <[email protected]>

commit 9aa21e1549db8882ff77b691e7714153df21dff0 upstream.

When platform_device_register() fails in vidtv_bridge_init(), the embedded struct device in vidtv_bridge_dev has already been initialized by device_initialize(), but the failure path returns the error without dropping the device reference for the current platform device:

vidtv_bridge_init()

    -> platform_device_register(&vidtv_bridge_dev)
       -> device_initialize(&vidtv_bridge_dev.dev)
       -> setup_pdev_dma_masks(&vidtv_bridge_dev)
       -> platform_device_add(&vidtv_bridge_dev)

This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error.

The issue was identified by a static analysis tool I developed and confirmed by manual review.

Fixes f90cf6079bf67 ("media: vidtv: add a bridge driver")
---
 drivers/media/test-drivers/vidtv/vidtv_bridge.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/media/test-drivers/vidtv/vidtv_bridge.c +3 -1
@@ -594,8 +594,10 @@ static int __init vidtv_bridge_init(void
 	int ret;
 
 	ret = platform_device_register(&vidtv_bridge_dev);
-	if (ret)
+	if (ret) {
+		platform_device_put(&vidtv_bridge_dev);
 		return ret;
+	}
 
 	ret = platform_driver_register(&vidtv_bridge_driver);
 	if (ret)
author maintainer
#442 lore raw

PATCH 442/675 media: vimc: fix reference leak on failed device registration

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guangshuo Li <[email protected]>

commit 33e2b833c66b890a0d71c4fa82d4c97143f7f75f upstream.

When platform_device_register() fails in vimc_init(), the embedded struct device in vimc_pdev has already been initialized by device_initialize(), but the failure path returns the error without dropping the device reference for the current platform device:

vimc_init()

    -> platform_device_register(&vimc_pdev)
       -> device_initialize(&vimc_pdev.dev)
       -> setup_pdev_dma_masks(&vimc_pdev)
       -> platform_device_add(&vimc_pdev)

This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error.

The issue was identified by a static analysis tool I developed and confirmed by manual review.

Fixes 4babf057c143f ("media: vimc: allocate vimc_device dynamically")
---
 drivers/media/test-drivers/vimc/vimc-core.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/test-drivers/vimc/vimc-core.c +1 -0
@@ -422,6 +422,7 @@ static int __init vimc_init(void)
 	if (ret) {
 		dev_err(&vimc_pdev.dev,
 			"platform device registration failed (err=%d)\n", ret);
+		platform_device_put(&vimc_pdev);
 		return ret;
 	}
 
author maintainer
#443 lore raw

PATCH 443/675 media: vivid: add vivid_update_reduced_fps()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hans Verkuil <[email protected]>

commit 1d793a29efb4260f90913f5287939bf95573b073 upstream.

Don't call vivid_update_format_cap() when switching to/from reduced fps for HDMI inputs: that will also reset the format, which is overkill for this.

Make a new vivid_update_reduced_fps() function that just updates the dev->timeperframe_vid_cap.

Fixes c79aa6aeadb0 ("[media] vivid-capture: add control for reduced frame rate")
---
 drivers/media/test-drivers/vivid/vivid-ctrls.c   |    3 +-
 drivers/media/test-drivers/vivid/vivid-vid-cap.c |   32 +++++++++++++----------
 drivers/media/test-drivers/vivid/vivid-vid-cap.h |    1 
 3 files changed, 22 insertions(+), 14 deletions(-)
drivers/media/test-drivers/vivid/vivid-ctrls.c +2 -1
@@ -609,7 +609,8 @@ static int vivid_vid_cap_s_ctrl(struct v
 		break;
 	case VIVID_CID_REDUCED_FPS:
 		dev->reduced_fps = ctrl->val;
-		vivid_update_format_cap(dev, true);
+		if (dev->input_type[dev->input] == HDMI)
+			vivid_update_reduced_fps(dev);
 		break;
 	case VIVID_CID_HAS_CROP_CAP:
 		dev->has_crop_cap = ctrl->val;
drivers/media/test-drivers/vivid/vivid-vid-cap.c +19 -13
@@ -362,6 +362,24 @@ static enum tpg_pixel_aspect vivid_get_p
 	return TPG_PIXEL_ASPECT_SQUARE;
 }
 
+void vivid_update_reduced_fps(struct vivid_dev *dev)
+{
+	struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt;
+	unsigned int size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
+	u64 pixelclock;
+
+	if (dev->reduced_fps && can_reduce_fps(bt)) {
+		pixelclock = div_u64(bt->pixelclock * 1000, 1001);
+		bt->flags |= V4L2_DV_FL_REDUCED_FPS;
+	} else {
+		pixelclock = bt->pixelclock;
+		bt->flags &= ~V4L2_DV_FL_REDUCED_FPS;
+	}
+	dev->timeperframe_vid_cap = (struct v4l2_fract) {
+		size / 100, (u32)pixelclock / 100
+	};
+}
+
 /*
  * Called whenever the format has to be reset which can occur when
  * changing inputs, standard, timings, etc.
@@ -370,8 +388,6 @@ void vivid_update_format_cap(struct vivi
 {
 	struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt;
 	u32 dims[V4L2_CTRL_MAX_DIMS] = {};
-	unsigned size;
-	u64 pixelclock;
 
 	switch (dev->input_type[dev->input]) {
 	case WEBCAM:
@@ -400,17 +416,7 @@ void vivid_update_format_cap(struct vivi
 	case HDMI:
 		dev->src_rect.width = bt->width;
 		dev->src_rect.height = bt->height;
-		size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
-		if (dev->reduced_fps && can_reduce_fps(bt)) {
-			pixelclock = div_u64(bt->pixelclock * 1000, 1001);
-			bt->flags |= V4L2_DV_FL_REDUCED_FPS;
-		} else {
-			pixelclock = bt->pixelclock;
-			bt->flags &= ~V4L2_DV_FL_REDUCED_FPS;
-		}
-		dev->timeperframe_vid_cap = (struct v4l2_fract) {
-			size / 100, (u32)pixelclock / 100
-		};
+		vivid_update_reduced_fps(dev);
 		if (bt->interlaced)
 			dev->field_cap = V4L2_FIELD_ALTERNATE;
 		else
drivers/media/test-drivers/vivid/vivid-vid-cap.h +1 -0
@@ -9,6 +9,7 @@
 #define _VIVID_VID_CAP_H_
 
 void vivid_update_quality(struct vivid_dev *dev);
+void vivid_update_reduced_fps(struct vivid_dev *dev);
 void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls);
 void vivid_update_outputs(struct vivid_dev *dev);
 void vivid_update_connected_outputs(struct vivid_dev *dev);
author maintainer
#444 lore raw

PATCH 444/675 media: vivid: check for vb2_is_busy() when toggling caps

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hans Verkuil <[email protected]>

commit c2d1a2130c93f6d758af58590b86b2254c7a1dec upstream.

The vivid_update_format_cap/out() functions must only be called if the capture/output queue are not busy. But for the controls that select the CROP/COMPOSE/SCALE capability that is not checked.

Only when streaming starts will they be set to 'grabbed' and it is impossible to change the control, but between REQBUFS and STREAMON you are still allowed to set these controls. Since vivid_update_format_cap/out will change the format, this can cause unexpected results.

Besides adding these checks, also add a WARN_ON in vivid_update_format_cap/out() if the queue is busy.

I'm 90% certain that this is the cause of this syzbot bug:

https://syzkaller.appspot.com/bug?extid=dac8f5eaa46837e97b89

But since we never have reproducers, it is hard to be certain. In any case, these checks are needed regardless.

---
 drivers/media/test-drivers/vivid/vivid-ctrls.c   |   12 ++++++++++++
 drivers/media/test-drivers/vivid/vivid-vid-cap.c |    6 ++++++
 drivers/media/test-drivers/vivid/vivid-vid-out.c |    6 ++++++
 3 files changed, 24 insertions(+)
drivers/media/test-drivers/vivid/vivid-ctrls.c +12 -0
@@ -613,14 +613,20 @@ static int vivid_vid_cap_s_ctrl(struct v
 			vivid_update_reduced_fps(dev);
 		break;
 	case VIVID_CID_HAS_CROP_CAP:
+		if (vb2_is_busy(&dev->vb_vid_cap_q))
+			return -EBUSY;
 		dev->has_crop_cap = ctrl->val;
 		vivid_update_format_cap(dev, true);
 		break;
 	case VIVID_CID_HAS_COMPOSE_CAP:
+		if (vb2_is_busy(&dev->vb_vid_cap_q))
+			return -EBUSY;
 		dev->has_compose_cap = ctrl->val;
 		vivid_update_format_cap(dev, true);
 		break;
 	case VIVID_CID_HAS_SCALER_CAP:
+		if (vb2_is_busy(&dev->vb_vid_cap_q))
+			return -EBUSY;
 		dev->has_scaler_cap = ctrl->val;
 		vivid_update_format_cap(dev, true);
 		break;
@@ -1117,14 +1123,20 @@ static int vivid_vid_out_s_ctrl(struct v
 
 	switch (ctrl->id) {
 	case VIVID_CID_HAS_CROP_OUT:
+		if (vb2_is_busy(&dev->vb_vid_out_q))
+			return -EBUSY;
 		dev->has_crop_out = ctrl->val;
 		vivid_update_format_out(dev);
 		break;
 	case VIVID_CID_HAS_COMPOSE_OUT:
+		if (vb2_is_busy(&dev->vb_vid_out_q))
+			return -EBUSY;
 		dev->has_compose_out = ctrl->val;
 		vivid_update_format_out(dev);
 		break;
 	case VIVID_CID_HAS_SCALER_OUT:
+		if (vb2_is_busy(&dev->vb_vid_out_q))
+			return -EBUSY;
 		dev->has_scaler_out = ctrl->val;
 		vivid_update_format_out(dev);
 		break;
drivers/media/test-drivers/vivid/vivid-vid-cap.c +6 -0
@@ -389,6 +389,12 @@ void vivid_update_format_cap(struct vivi
 	struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt;
 	u32 dims[V4L2_CTRL_MAX_DIMS] = {};
 
+	/*
+	 * This resets the format, so must never be called while vb2_is_busy().
+	 */
+	if (WARN_ON(vb2_is_busy(&dev->vb_vid_cap_q)))
+		return;
+
 	switch (dev->input_type[dev->input]) {
 	case WEBCAM:
 	default:
drivers/media/test-drivers/vivid/vivid-vid-out.c +6 -0
@@ -214,6 +214,12 @@ void vivid_update_format_out(struct vivi
 	unsigned size, p;
 	u64 pixelclock;
 
+	/*
+	 * This resets the format, so must never be called while vb2_is_busy().
+	 */
+	if (WARN_ON(vb2_is_busy(&dev->vb_vid_out_q)))
+		return;
+
 	switch (dev->output_type[dev->output]) {
 	case SVID:
 	default:
author maintainer
#445 lore raw

PATCH 445/675 media: vivid: fix cleanup bugs in vivid_init()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guangshuo Li <[email protected]>

commit a07c179a92e949172ca52f6d4a13202ea88cd4b7 upstream.

When platform_device_register() fails in vivid_init(), the embedded struct device in vivid_pdev has already been initialized by device_initialize(), but the failure path jumps to free_output_strings without dropping the device reference for the current platform device:

vivid_init()

    -> platform_device_register(&vivid_pdev)
       -> device_initialize(&vivid_pdev.dev)
       -> setup_pdev_dma_masks(&vivid_pdev)
       -> platform_device_add(&vivid_pdev)

This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before jumping to the common cleanup path.

Also, the unreg_driver label incorrectly calls platform_driver_register() instead of platform_driver_unregister(), which breaks cleanup when workqueue creation fails after successful driver registration. Fix that as well.

The reference leak was identified by a static analysis tool I developed and confirmed by manual review. The incorrect cleanup call was found during code inspection.

Fixes f46d740fb0258 ("[media] vivid: turn this into a platform_device")
Fixes d7c969f37515d ("media: vivid: Add 'Is Connected To' menu controls")
---
 drivers/media/test-drivers/vivid/vivid-core.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/media/test-drivers/vivid/vivid-core.c +4 -2
@@ -2289,8 +2289,10 @@ static int __init vivid_init(void)
 		}
 	}
 	ret = platform_device_register(&vivid_pdev);
-	if (ret)
+	if (ret) {
+		platform_device_put(&vivid_pdev);
 		goto free_output_strings;
+	}
 	ret = platform_driver_register(&vivid_pdrv);
 	if (ret)
 		goto unreg_device;
@@ -2311,7 +2313,7 @@ static int __init vivid_init(void)
 destroy_hdmi_wq:
 	destroy_workqueue(update_hdmi_ctrls_workqueue);
 unreg_driver:
-	platform_driver_register(&vivid_pdrv);
+	platform_driver_unregister(&vivid_pdrv);
 unreg_device:
 	platform_device_unregister(&vivid_pdev);
 free_output_strings:
author maintainer
#446 lore raw

PATCH 446/675 media: vpif_capture: fix OF node reference imbalance

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Johan Hovold <[email protected]>

commit 2282f979560af6bbc8ee2c1ee8663197312cee5b upstream.

The driver reuses the OF node of the parent device but fails to take another reference to balance the one dropped by the platform bus code when unbinding the parent and releasing the child devices.

Fix this by using the intended helper for reusing OF nodes.

Fixes 4a5f8ae50b66 ("[media] davinci: vpif_capture: get subdevs from DT when available")
Cc Kevin Hilman <[email protected]>
---
 drivers/media/platform/ti/davinci/vpif_capture.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/platform/ti/davinci/vpif_capture.c +1 -1
@@ -1499,7 +1499,7 @@ vpif_capture_get_pdata(struct platform_d
 	 * video ports & endpoints data.
 	 */
 	if (pdev->dev.parent && pdev->dev.parent->of_node)
-		pdev->dev.of_node = pdev->dev.parent->of_node;
+		device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
 	if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
 		return pdev->dev.platform_data;
 
author maintainer
#447 lore raw

PATCH 447/675 ALSA: hda/realtek: Fix speakers on Lunnen Ground 14

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nikita Maksimov <[email protected]>

commit 5c3f8dac531b454bf67b6ee3c2aac89f0aaaef74 upstream.

The firmware on the Lunnen Ground 14 marks pin 0x1b as unused even though the internal speakers are connected to it. As a result, the speakers are not detected.

Add a pin configuration quirk for PCI subsystem ID 2782:a212 to configure pin 0x1b as an internal speaker.

The pin configuration was tested on a Lunnen Ground 14 (DMI product LL4FA) with an ALC269VC codec. The internal speakers and microphone work as expected.

---
 sound/hda/codecs/realtek/alc269.c |    9 +++++++++
 1 file changed, 9 insertions(+)
sound/hda/codecs/realtek/alc269.c +9 -0
@@ -3742,6 +3742,7 @@ enum {
 	ALC269_FIXUP_DMIC_THINKPAD_ACPI,
 	ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13,
 	ALC269VC_FIXUP_INFINIX_Y4_MAX,
+	ALC269VC_FIXUP_LUNNEN_GROUND_14,
 	ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO,
 	ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
 	ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
@@ -4183,6 +4184,13 @@ static const struct hda_fixup alc269_fix
 		.chained = true,
 		.chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
 	},
+	[ALC269VC_FIXUP_LUNNEN_GROUND_14] = {
+		.type = HDA_FIXUP_PINS,
+		.v.pins = (const struct hda_pintbl[]) {
+			{ 0x1b, 0x90170150 }, /* internal speaker */
+			{ }
+		},
+	},
 	[ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = {
 		.type = HDA_FIXUP_PINS,
 		.v.pins = (const struct hda_pintbl[]) {
@@ -7573,6 +7581,7 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX),
 	SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME),
 	SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK),
+	SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14),
 	SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER),
 	SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC),
 	SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED),
author maintainer
#448 lore raw

PATCH 448/675 ALSA: seq: close a re-opened queue timer in the destructor

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Norbert Szetei <[email protected]>

commit 2c4dc0ed50b05cd847a4b34b8cebf0775f19aeb9 upstream.

queue_delete() closes the queue timer, then frees it. snd_seq_timer_close() clears q->timer->timeri. snd_use_lock_sync() then drains borrowers, and snd_seq_timer_delete() frees q->timer.

A borrower can re-open the timer inside that window. A SET_QUEUE_CLIENT that took a queueptr() use_lock reference before the queue was unlinked runs snd_seq_timer_open() after the close. Open refuses re-open only while timeri is set, and the close just cleared it, so it re-opens timeri.

snd_seq_timer_delete() does not close that instance. Its snd_seq_timer_stop() is a no-op, because running was cleared first. So it frees q->timer with the instance still live. The queue is freed next.

The instance stays on the global timer with callback_data pointing at the freed queue. A non-owner START on the unlocked queue arms it. The next tick derefs the freed queue in snd_seq_timer_interrupt().

Reachable by an unprivileged user with access to /dev/snd/seq. No CAP and no queue ownership required.

Close any lingering instance in the destructor. There, ->timeri can no longer change: the queue is unlinked and all use_lock borrowers have drained, so no snd_seq_queue_use() can re-open it. Close it before clearing q->timer. snd_timer_close() waits for any in-flight snd_seq_timer_interrupt() to finish, and that callback still reads q->timer (via snd_seq_check_queue()), so q->timer must stay valid until it drains.

Fixes 1da177e4c3f4 ("Linux-2.6.12-rc2")
Assisted-by Claude:claude-opus-4-8
---
 sound/core/seq/seq_timer.c |   13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)
sound/core/seq/seq_timer.c +12 -1
@@ -61,12 +61,23 @@ struct snd_seq_timer *snd_seq_timer_new(
 void snd_seq_timer_delete(struct snd_seq_timer **tmr)
 {
 	struct snd_seq_timer *t = *tmr;
-	*tmr = NULL;
+	struct snd_timer_instance *ti;
 
 	if (t == NULL) {
 		pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n");
 		return;
 	}
+
+	scoped_guard(spinlock_irq, &t->lock) {
+		ti = t->timeri;
+		t->timeri = NULL;
+	}
+	if (ti) {
+		snd_timer_close(ti);
+		snd_timer_instance_free(ti);
+	}
+
+	*tmr = NULL;
 	t->running = 0;
 
 	/* reset time */
author maintainer
#449 lore raw

PATCH 449/675 ALSA: hda: codecs: hdmi: disable keep-alive before audio format change

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kai Vehmanen <[email protected]>

commit a3d6d3cedfe87bbd5a677d52b22ac20d28e59cf8 upstream.

When a keep-alive (KAE) silent stream is active on an Intel HDMI/DP codec, opening a real PCM stream reprograms the converter format and the audio infoframe in snd_hda_hdmi_generic_pcm_prepare(). Part of that reprogramming - the converter channel count and the channel mapping in snd_hda_hdmi_setup_audio_infoframe() - is not safe to do while a keep-alive stream is active. This is most visible when switching to a multichannel PCM configuration, where the active channel count actually changes. In that case the newly opened PCM stream plays no sound.

Add an optional hdmi_ops .prepare hook, called at the start of the PCM prepare sequence (before the format and infoframe are touched), and implement it for HSW+ to release keep-alive. Keep-alive is then re-enabled as before once the new stream has been set up, in the setup_stream op.

---
 sound/hda/codecs/hdmi/hdmi.c       |    3 +++
 sound/hda/codecs/hdmi/hdmi_local.h |    9 +++++++++
 sound/hda/codecs/hdmi/intelhdmi.c  |   36 ++++++++++++++++++++++++++++++------
 3 files changed, 42 insertions(+), 6 deletions(-)
sound/hda/codecs/hdmi/hdmi.c +3 -0
@@ -1688,6 +1688,9 @@ int snd_hda_hdmi_generic_pcm_prepare(str
 		per_pin->channels = substream->runtime->channels;
 		per_pin->setup = true;
 
+		if (spec->ops.prepare)
+			spec->ops.prepare(codec, per_pin);
+
 		if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) {
 			stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core,
 								substream);
sound/hda/codecs/hdmi/hdmi_local.h +9 -0
@@ -74,6 +74,15 @@ struct hdmi_ops {
 			    hda_nid_t pin_nid, int dev_id, u32 stream_tag,
 			    int format);
 
+	/*
+	 * Optional hook invoked at the beginning of the PCM prepare
+	 * sequence, before the audio infoframe and stream format are
+	 * (re)programmed. Used to disable keep-alive / silent stream so
+	 * that the format change is not done while keep-alive is active.
+	 */
+	void (*prepare)(struct hda_codec *codec,
+			struct hdmi_spec_per_pin *per_pin);
+
 	void (*pin_cvt_fixup)(struct hda_codec *codec,
 			      struct hdmi_spec_per_pin *per_pin,
 			      hda_nid_t cvt_nid);
sound/hda/codecs/hdmi/intelhdmi.c +30 -6
@@ -418,6 +418,28 @@ static void intel_not_share_assigned_cvt
 		intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx);
 }
 
+/*
+ * prepare ops override for HSW+
+ *
+ * Disable keep-alive before the converter format and audio infoframe are
+ * reprogrammed by the PCM prepare sequence. Changing the audio format (e.g.
+ * the channel count when switching to multichannel PCM) while a keep-alive
+ * stream is active is not safe, so release keep-alive here, early in the
+ * sequence. It is re-enabled once the new stream has been set up, in
+ * i915_hsw_setup_stream().
+ */
+static void i915_hsw_prepare(struct hda_codec *codec,
+			     struct hdmi_spec_per_pin *per_pin)
+{
+	struct hdmi_spec *spec = codec->spec;
+
+	if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin->silent_stream) {
+		silent_stream_set_kae(codec, per_pin, false);
+		/* wait for pending transfers in codec to clear */
+		usleep_range(100, 200);
+	}
+}
+
 /* setup_stream ops override for HSW+ */
 static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
 				 hda_nid_t pin_nid, int dev_id, u32 stream_tag,
@@ -435,15 +457,16 @@ static int i915_hsw_setup_stream(struct
 
 	haswell_verify_D0(codec, cvt_nid, pin_nid);
 
-	if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
-		silent_stream_set_kae(codec, per_pin, false);
-		/* wait for pending transfers in codec to clear */
-		usleep_range(100, 200);
-	}
-
 	res = snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
 					stream_tag, format);
 
+	/*
+	 * Keep-alive was disabled in i915_hsw_prepare(), re-enable it now.
+	 * The pin lookup above resolves to the same per_pin that prepare
+	 * used (pin_nid comes from that per_pin), so this stays balanced; a
+	 * NULL per_pin only occurs on a lookup failure that also implies no
+	 * active keep-alive stream to restore.
+	 */
 	if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
 		usleep_range(100, 200);
 		silent_stream_set_kae(codec, per_pin, true);
@@ -607,6 +630,7 @@ static int intel_hsw_common_init(struct
 	codec->depop_delay = 0;
 	codec->auto_runtime_pm = 1;
 
+	spec->ops.prepare = i915_hsw_prepare;
 	spec->ops.setup_stream = i915_hsw_setup_stream;
 	spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
 	spec->ops.silent_stream = i915_set_silent_stream;
author maintainer
#450 lore raw

PATCH 450/675 ALSA: timer: drain a slaves callback before its master detaches it

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Norbert Szetei <[email protected]>

commit bdefe1346a8e6b8dc8593406dc2617e985fcbcab upstream.

snd_timer_close_locked() drains the closing instance's own in-flight callback (IFLG_CALLBACK) before freeing it, but not its slaves'. When a master instance is closed, remove_slave_links() clears each slave's ->timer; the slave's own close then reads timer == NULL and takes the branch that skips the drain entirely (snd_timer_stop_slave() also no-ops on a NULL timer). So a slave whose callback is still running when the master is closed is freed underneath the live callback, leading to use-after-free.

Drain the slaves too before remove_slave_links() severs them. snd_timer_stop() has already taken this instance off the active list, so no new slave callback can be queued. Take the slaves off the ack list so a pending one can't fire either, then wait for any that is already in flight.

Fixes 37745918e0e7 ("ALSA: timer: Introduce virtual userspace-driven timers")
Assisted-by Claude:claude-opus-4-8
---
 sound/core/timer.c |   14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)
sound/core/timer.c +12 -2
@@ -437,10 +437,20 @@ static void snd_timer_close_locked(struc
 	snd_timer_stop(timeri);
 
 	if (timer) {
+		struct snd_timer_instance *slave;
+		bool busy;
+
 		timer->num_instances--;
-		/* wait, until the active callback is finished */
+		/* unqueue then drain the slaves' callbacks before remove_slave_links() severs them */
 		spin_lock_irq(&timer->lock);
-		while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
+		list_for_each_entry(slave, &timeri->slave_list_head, open_list)
+			list_del_init(&slave->ack_list);
+		for (;;) {
+			busy = timeri->flags & SNDRV_TIMER_IFLG_CALLBACK;
+			list_for_each_entry(slave, &timeri->slave_list_head, open_list)
+				busy |= slave->flags & SNDRV_TIMER_IFLG_CALLBACK;
+			if (!busy)
+				break;
 			spin_unlock_irq(&timer->lock);
 			udelay(10);
 			spin_lock_irq(&timer->lock);
author maintainer
#451 lore raw

PATCH 451/675 ALSA: timer: dont re-enter an instance callback that is still running

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Norbert Szetei <[email protected]>

commit 70d28bfcd6224eed75986b3b987b997e59643fa4 upstream.

The userspace-driven timer (utimer) TRIGGER ioctl calls snd_timer_interrupt() directly with no serialization, so two threads triggering the same utimer can run snd_timer_interrupt() on one snd_timer concurrently.

snd_timer_process_callbacks() drops timer->lock around each instance callback and marks the in-flight callback with the single SNDRV_TIMER_IFLG_CALLBACK bit; snd_timer_close_locked() waits on that bit to drain an in-flight callback before freeing the instance. The bit cannot represent two concurrent callbacks: when a second interrupt re-queues an instance whose callback is still running, both run at once, the first to finish clears the bit, and the close-path drain then frees the instance (and its callback_data) while the other callback is still live - a use-after-free reachable by any user able to open /dev/snd/timer, both via a user timer instance and via a sequencer queue timer bound to the utimer.

snd_timer_interrupt() sets IFLG_CALLBACK before dropping timer->lock, so a concurrent interrupt already observes it under the lock. Skip re-queuing an instance (and its slaves) to the ack/sack list while its callback is in flight; the accumulated pticks are delivered on the next tick, so no event is lost.

Fixes 37745918e0e7 ("ALSA: timer: Introduce virtual userspace-driven timers")
Assisted-by Claude:claude-opus-4-8
---
 sound/core/timer.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
sound/core/timer.c +5 -2
@@ -883,12 +883,15 @@ void snd_timer_interrupt(struct snd_time
 			ack_list_head = &timer->ack_list_head;
 		else
 			ack_list_head = &timer->sack_list_head;
-		if (list_empty(&ti->ack_list))
+		/* don't requeue an instance whose callback is still running */
+		if (list_empty(&ti->ack_list) &&
+		    !(ti->flags & SNDRV_TIMER_IFLG_CALLBACK))
 			list_add_tail(&ti->ack_list, ack_list_head);
 		list_for_each_entry(ts, &ti->slave_active_head, active_list) {
 			ts->pticks = ti->pticks;
 			ts->resolution = resolution;
-			if (list_empty(&ts->ack_list))
+			if (list_empty(&ts->ack_list) &&
+			    !(ts->flags & SNDRV_TIMER_IFLG_CALLBACK))
 				list_add_tail(&ts->ack_list, ack_list_head);
 		}
 	}
author maintainer
#452 lore raw

PATCH 452/675 wifi: ath6kl: fix OOB access from firmware ADDBA window size

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tristan Madani <[email protected]>

commit 44126b6994eeb28f2103b638e698f40a1244f327 upstream.

aggr_recv_addba_req_evt() logs a debug message when the firmware-supplied win_sz is outside [AGGR_WIN_SZ_MIN, AGGR_WIN_SZ_MAX] but does not return. The out-of-range win_sz is then used in TID_WINDOW_SZ() to compute a kzalloc size and stored in rxtid->hold_q_sz, leading to zero-size or overflowed allocations and subsequent out-of-bounds access.

Clean up any previously active aggregation session for the TID first, then return early when win_sz is out of the valid range, instead of proceeding with a broken allocation size.

---
 drivers/net/wireless/ath/ath6kl/txrx.c |   10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/net/wireless/ath/ath6kl/txrx.c +6 -4
@@ -1723,13 +1723,15 @@ void aggr_recv_addba_req_evt(struct ath6
 
 	rxtid = &aggr_conn->rx_tid[tid];
 
-	if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX)
-		ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n",
-			   __func__, win_sz, tid);
-
 	if (rxtid->aggr)
 		aggr_delete_tid_state(aggr_conn, tid);
 
+	if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) {
+		ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n",
+			   __func__, win_sz, tid);
+		return;
+	}
+
 	rxtid->seq_next = seq_no;
 	hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q);
 	rxtid->hold_q = kzalloc(hold_q_size, GFP_KERNEL);
author maintainer
#453 lore raw

PATCH 453/675 wifi: ath6kl: fix use-after-free in aggr_reset_state()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Daniel Hodges <[email protected]>

commit ba7debb4dd6427386862220e8335a53a4bfc235d upstream.

The aggr_reset_state() function uses timer_delete() (non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers like aggr_module_destroy().

If the timer callback (aggr_timeout) is executing when aggr_reset_state() is called, the callback will continue to access aggr_conn fields like rx_tid[] and stat[] which may be freed immediately after by kfree(aggr_info->aggr_conn) in aggr_module_destroy().

Additionally, the timer callback can re-arm itself via mod_timer() while aggr_reset_state() is running, creating a more complex race condition.

Use timer_delete_sync() instead to ensure any running timer callback has completed before returning.

---
 drivers/net/wireless/ath/ath6kl/txrx.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/ath/ath6kl/txrx.c +1 -1
@@ -1830,7 +1830,7 @@ void aggr_reset_state(struct aggr_info_c
 		return;
 
 	if (aggr_conn->timer_scheduled) {
-		timer_delete(&aggr_conn->timer);
+		timer_delete_sync(&aggr_conn->timer);
 		aggr_conn->timer_scheduled = false;
 	}
 
author maintainer
#454 lore raw

PATCH 454/675 wifi: mwifiex: fix NULL dereference when the AP has HT-cap but no HT-oper

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Doruk Tan Ozturk <[email protected]>

commit c3d68e294cbb6a4090bb219d3dcaca85a011809b upstream.

mwifiex_tdls_add_ht_oper() gates its follow-the-AP-bandwidth path on bss_desc->bcn_ht_cap being present, but then dereferences a different pointer, bss_desc->bcn_ht_oper:

	if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) &&
	    bss_desc->bcn_ht_cap &&
	    ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param))

bcn_ht_cap and bcn_ht_oper are populated independently while parsing the associated AP's beacon in mwifiex_update_bss_desc_with_ie(): an AP that advertises an HT Capabilities element but no HT Operation element leaves bcn_ht_cap non-NULL and bcn_ht_oper NULL. Setting up a TDLS link to a peer while associated to such an AP then dereferences the NULL bcn_ht_oper and crashes the kernel. Every other bcn_ht_oper user in the driver NULL-checks it first.

Guard on the pointer that is actually dereferenced.

Found by 0sec automated security-research tooling (https://0sec.ai).

Fixes 396939f94084 ("mwifiex: add HT operation IE in TDLS setup confirm")
Assisted-by 0sec:multi-model
---
 drivers/net/wireless/marvell/mwifiex/tdls.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/marvell/mwifiex/tdls.c +1 -1
@@ -215,7 +215,7 @@ mwifiex_tdls_add_ht_oper(struct mwifiex_
 
 	/* follow AP's channel bandwidth */
 	if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) &&
-	    bss_desc->bcn_ht_cap &&
+	    bss_desc->bcn_ht_oper &&
 	    ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param))
 		ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param;
 
author maintainer
#455 lore raw

PATCH 455/675 wifi: wilc1000: validate assoc response length before subtracting header

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Huihui Huang <[email protected]>

commit 4c4c97b60a5e978121d9ee8cb0ab3916e5d6a8de upstream.

wilc_parse_assoc_resp_info() computes the trailing IE length as

	ies_len = buffer_len - sizeof(*res);

without first checking that buffer_len is at least sizeof(struct wilc_assoc_resp) (6 bytes). buffer_len is the length reported for a received association response (host_int_parse_assoc_resp_info() passes hif_drv->assoc_resp / assoc_resp_info_len straight in) and must be validated before the driver accesses the fixed header.

For a frame shorter than the 6-byte fixed header, the subtraction wraps. For a four-byte response the result is truncated to a u16 ies_len of 65534, so kmemdup() then attempts to copy 65534 bytes starting at buffer + sizeof(*res), beyond the valid association-response data (CWE-125). A response shorter than four bytes can also cause an out-of-bounds read of res->status_code at offsets 2 and 3.

Reject frames too short to hold the fixed header before touching the header or computing ies_len. Also set the connection status to a failure on this path: the caller falls through to a "conn_info->status == WLAN_STATUS_SUCCESS" check after the parser returns, so leaving the status untouched could let a malformed short response be treated as a successful association.

Fixes c5c77ba18ea6 ("staging: wilc1000: Add SDIO/SPI 802.11 driver")
Assisted-by Claude:claude-opus-4-8
---
 drivers/net/wireless/microchip/wilc1000/hif.c |    5 +++++
 1 file changed, 5 insertions(+)
drivers/net/wireless/microchip/wilc1000/hif.c +5 -0
@@ -600,6 +600,11 @@ static s32 wilc_parse_assoc_resp_info(u8
 	u16 ies_len;
 	struct wilc_assoc_resp *res = (struct wilc_assoc_resp *)buffer;
 
+	if (buffer_len < sizeof(*res)) {
+		ret_conn_info->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		return -EINVAL;
+	}
+
 	ret_conn_info->status = le16_to_cpu(res->status_code);
 	if (ret_conn_info->status == WLAN_STATUS_SUCCESS) {
 		ies = &buffer[sizeof(*res)];
author maintainer
#456 lore raw

PATCH 456/675 wifi: mt76: mt7615: drop TXRX_NOTIFY on non-mmio buses

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Devin Wittmayer <[email protected]>

commit 39afc46c0243d10b7795e6e6cf4ae91f41732120 upstream.

PKT_TYPE_TXRX_NOTIFY is an mmio-only event, but mt7615_rx_check() and mt7615_queue_rx_skb() dispatch it to mt7615_mac_tx_free() on every bus. mt7615_mac_tx_free() cleans the DMA tx queues with mt76_queue_tx_cleanup(), which calls queue_ops->tx_cleanup(). Only the mmio queue ops implement that callback; on the mt7663 USB and SDIO buses it is NULL, so a TXRX_NOTIFY there calls a NULL pointer in the RX worker. Same defect as the mt7921 and mt7925 patches in this series.

Drop the event on non-mmio buses via mt76_is_mmio(), as in commit 5683e1488aa9 ("wifi: mt76: connac: do not check WED status for non-mmio devices").

---
 drivers/net/wireless/mediatek/mt76/mt7615/mac.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7615/mac.c +6 -0
@@ -1601,6 +1601,8 @@ bool mt7615_rx_check(struct mt76_dev *md
 
 	switch (type) {
 	case PKT_TYPE_TXRX_NOTIFY:
+		if (!mt76_is_mmio(mdev))
+			return false;
 		mt7615_mac_tx_free(dev, data, len);
 		return false;
 	case PKT_TYPE_TXS:
@@ -1634,6 +1636,10 @@ void mt7615_queue_rx_skb(struct mt76_dev
 		dev_kfree_skb(skb);
 		break;
 	case PKT_TYPE_TXRX_NOTIFY:
+		if (!mt76_is_mmio(mdev)) {
+			dev_kfree_skb(skb);
+			break;
+		}
 		mt7615_mac_tx_free(dev, skb->data, skb->len);
 		dev_kfree_skb(skb);
 		break;
author maintainer
#457 lore raw

PATCH 457/675 wifi: mt76: mt7921: drop TXRX_NOTIFY on non-mmio buses

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Devin Wittmayer <[email protected]>

commit da4082e91acabc1498611ed8ccc53f0610baefc6 upstream.

PKT_TYPE_TXRX_NOTIFY is an mmio-only event, but mt7921_rx_check() and mt7921_queue_rx_skb() dispatch it to mt7921_mac_tx_free() on every bus. mt7921_mac_tx_free() cleans the DMA tx queues with mt76_queue_tx_cleanup(), which calls queue_ops->tx_cleanup(). Only the mmio queue ops implement that callback; on USB and SDIO it is NULL, so a TXRX_NOTIFY there calls a NULL pointer in the RX worker:

  BUG: kernel NULL pointer dereference, address: 0000000000000000
  RIP: 0010:0x0
  Call Trace:
   mt7921_mac_tx_free+0x64/0x310 [mt7921_common]
   mt7921_rx_check+0x5f/0xf0 [mt7921_common]
   mt76u_rx_worker+0x1b9/0x620 [mt76_usb]

Drop the event on non-mmio buses via mt76_is_mmio(), as in commit 5683e1488aa9 ("wifi: mt76: connac: do not check WED status for non-mmio devices").

---
 drivers/net/wireless/mediatek/mt76/mt7921/mac.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7921/mac.c +7 -3
@@ -569,8 +569,9 @@ bool mt7921_rx_check(struct mt76_dev *md
 
 	switch (type) {
 	case PKT_TYPE_TXRX_NOTIFY:
-		/* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
-		mt7921_mac_tx_free(dev, data, len); /* mmio */
+		if (!mt76_is_mmio(mdev))
+			return false;
+		mt7921_mac_tx_free(dev, data, len);
 		return false;
 	case PKT_TYPE_TXS:
 		for (rxd += 2; rxd + 8 <= end; rxd += 8)
@@ -599,7 +600,10 @@ void mt7921_queue_rx_skb(struct mt76_dev
 
 	switch (type) {
 	case PKT_TYPE_TXRX_NOTIFY:
-		/* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
+		if (!mt76_is_mmio(mdev)) {
+			napi_consume_skb(skb, 1);
+			break;
+		}
 		mt7921_mac_tx_free(dev, skb->data, skb->len);
 		napi_consume_skb(skb, 1);
 		break;
author maintainer
#458 lore raw

PATCH 458/675 wifi: mt76: mt7925: drop TXRX_NOTIFY on non-mmio buses

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Devin Wittmayer <[email protected]>

commit feeff151c83e7f0ffcdedcad5343852d23d1f6e1 upstream.

PKT_TYPE_TXRX_NOTIFY is an mmio-only event, but mt7925_rx_check() and mt7925_queue_rx_skb() dispatch it to mt7925_mac_tx_free() on every bus. mt7925_mac_tx_free() cleans the DMA tx queues with mt76_queue_tx_cleanup(), which calls queue_ops->tx_cleanup(). Only the mmio queue ops implement that callback; on USB it is NULL, so a TXRX_NOTIFY there calls a NULL pointer in the RX worker:

  BUG: kernel NULL pointer dereference, address: 0000000000000000
  RIP: 0010:0x0
  Call Trace:
   mt7925_mac_tx_free+0x58/0x350 [mt7925_common]
   mt7925_rx_check+0xe2/0x130 [mt7925_common]
   mt76u_rx_worker+0x1b9/0x620 [mt76_usb]

Drop the event on non-mmio buses via mt76_is_mmio(), as in commit 5683e1488aa9 ("wifi: mt76: connac: do not check WED status for non-mmio devices").

Fixes c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips")
---
 drivers/net/wireless/mediatek/mt76/mt7925/mac.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7925/mac.c +7 -3
@@ -1193,8 +1193,9 @@ bool mt7925_rx_check(struct mt76_dev *md
 
 	switch (type) {
 	case PKT_TYPE_TXRX_NOTIFY:
-		/* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
-		mt7925_mac_tx_free(dev, data, len); /* mmio */
+		if (!mt76_is_mmio(mdev))
+			return false;
+		mt7925_mac_tx_free(dev, data, len);
 		return false;
 	case PKT_TYPE_TXS:
 		for (rxd += 4; rxd + 12 <= end; rxd += 12)
@@ -1230,7 +1231,10 @@ void mt7925_queue_rx_skb(struct mt76_dev
 
 	switch (type) {
 	case PKT_TYPE_TXRX_NOTIFY:
-		/* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
+		if (!mt76_is_mmio(mdev)) {
+			napi_consume_skb(skb, 1);
+			break;
+		}
 		mt7925_mac_tx_free(dev, skb->data, skb->len);
 		napi_consume_skb(skb, 1);
 		break;
author maintainer
#459 lore raw

PATCH 459/675 wifi: brcmfmac: make release_scratchbuffers idempotent

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Fan Wu <[email protected]>

commit 538c51e9d124cf656f2dd0c0394a8545efc7102d upstream.

brcmf_pcie_release_scratchbuffers() frees the shared.scratch and shared.ringupd DMA buffers with dma_free_coherent() but does not clear the pointers afterwards, unlike the sibling release_ringbuffers() which NULLs commonrings/flowrings/idxbuf on release.

Both the bus_reset .reset callback (brcmf_pcie_reset) and brcmf_pcie_remove() call release_scratchbuffers. When reset teardown has run before removal, remove's own teardown would call dma_free_coherent() a second time on the already-freed DMA allocation.

NULL the pointers after free, matching release_ringbuffers(), so a later release observes that the allocation has already been released. This patch makes repeated sequential release safe; the reset-work lifetime is handled separately by the following patch.

This issue was found by an in-house static analysis tool.

Fixes 4684997d9eea ("brcmfmac: reset PCIe bus on a firmware crash")
Assisted-by Codex:gpt-5.6
---
 drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +6 -2
@@ -1383,16 +1383,20 @@ fail:
 static void
 brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo)
 {
-	if (devinfo->shared.scratch)
+	if (devinfo->shared.scratch) {
 		dma_free_coherent(&devinfo->pdev->dev,
 				  BRCMF_DMA_D2H_SCRATCH_BUF_LEN,
 				  devinfo->shared.scratch,
 				  devinfo->shared.scratch_dmahandle);
-	if (devinfo->shared.ringupd)
+		devinfo->shared.scratch = NULL;
+	}
+	if (devinfo->shared.ringupd) {
 		dma_free_coherent(&devinfo->pdev->dev,
 				  BRCMF_DMA_D2H_RINGUPD_BUF_LEN,
 				  devinfo->shared.ringupd,
 				  devinfo->shared.ringupd_dmahandle);
+		devinfo->shared.ringupd = NULL;
+	}
 }
 
 static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo)
author maintainer
#460 lore raw

PATCH 460/675 wifi: brcmfmac: set F2 blocksize to 256 for BCM43752

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: LiangCheng Wang <[email protected]>

commit 29ab31f3f27157648f2f7e6d5e1fd9792fdf0614 upstream.

The BCM43752 is not reliable with the default 512-byte SDIO function 2 block size: on an i.MX8MP board with an AMPAK AP6275S module at SDR104 / 200 MHz, an iperf TX stress test kills WLAN within seconds:

mmc_submit_one: CMD53 sg block write failed -84
brcmf_sdio_dpc: failed backplane access over SDIO, halting operation

Commit d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") set up the 43752 like the 4373 for the F2 watermark but missed the F2 block size, which the 4373 limits to 256 bytes. The vendor driver (bcmdhd) also programs a 256-byte F2 block size for this chip and runs the same hardware without errors.

Group the 43752 with the 4373, matching the F2 watermark handling. With this change a 10-minute bidirectional iperf3 soak completes with zero SDIO errors at ~270 Mbit/s in each direction.

Backporting note: kernels before v6.18 name this id SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752, so on those trees the case label added by this patch must be adjusted to that name. Cherry-picking the rename commit 74e2ef72bd4b ("wifi: brcmfmac: fix 43752 SDIO FWVID incorrectly labelled as Cypress (CYW)") first is not a clean
alternative: on trees before v6.17 its context collides with the 43751 additions, and trees before v6.2 lack the FWVID framework it touches.

Fixes d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization")
Cc [email protected] # see patch description, needs adjustments for <= 6.17
---
 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +1 -0
@@ -911,6 +911,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_
 		return ret;
 	}
 	switch (sdiodev->func2->device) {
+	case SDIO_DEVICE_ID_BROADCOM_43752:
 	case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
 		f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE;
 		break;
author maintainer
#461 lore raw

PATCH 461/675 wifi: ath11k: fix refcount leak in ath11k_ahb_fw_resources_init()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Wentao Liang <[email protected]>

commit 0e120ee0822b7cc650bd7b29682a34e137cec10d upstream.

of_get_child_by_name() returns a node pointer with refcount incremented, but the error path when ath11k_ahb_setup_msa_resources() fails does not release it. Add the missing of_node_put() to avoid leaking the reference.

---
 drivers/net/wireless/ath/ath11k/ahb.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/wireless/ath/ath11k/ahb.c +1 -0
@@ -998,6 +998,7 @@ static int ath11k_ahb_fw_resources_init(
 	ret = ath11k_ahb_setup_msa_resources(ab);
 	if (ret) {
 		ath11k_err(ab, "failed to setup msa resources\n");
+		of_node_put(node);
 		return ret;
 	}
 
author maintainer
#462 lore raw

PATCH 462/675 staging: rtl8723bs: fix OOB reads in rtw_get_wps_ie()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Moksh Panicker <[email protected]>

commit 0e95ff792ae0aa6fbad9455943e9e1e4062670e9 upstream.

rtw_get_wps_ie() iterates over IE data from network frames without validating that the IE header and payload fit within the remaining buffer before reading them. Specifically:

- in_ie[cnt + 1] is read without checking cnt + 1 < in_len
- memcmp(&in_ie[cnt + 2], ...) accesses cnt + 2 without bounds check
- in_ie[cnt + 1] is used as length without verifying payload fits

Add bounds checks at the top of the loop body to break early if fewer than 2 bytes remain for the IE header, or if the declared payload extends past the end of the buffer. Also require at least 4 bytes of payload before comparing the WPS OUI.

Fixes 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver")
Cc stable <[email protected]>
---
 drivers/staging/rtl8723bs/core/rtw_ieee80211.c |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/staging/rtl8723bs/core/rtw_ieee80211.c +8 -1
@@ -679,7 +679,14 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_le
 	while (cnt < in_len) {
 		eid = in_ie[cnt];
 
-		if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) {
+		if (cnt + 2 > in_len)
+			break;
+
+		if (in_ie[cnt + 1] + 2 > in_len - cnt)
+			break;
+
+		if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (in_ie[cnt + 1] >= 4) &&
+		    (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) {
 			wpsie_ptr = &in_ie[cnt];
 
 			if (wps_ie)
author maintainer
#463 lore raw

PATCH 463/675 staging: rtl8723bs: fix inverted HT40 secondary channel offset

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: MinJea Kim <[email protected]>

commit 30d49cba27f8905bc288cef5846963f0004f644c upstream.

rtw_get_chan_type() maps the driver's channel offset to nl80211 channel types the wrong way around.

In this driver HAL_PRIME_CHNL_OFFSET_LOWER means the primary channel is the lower 20 MHz half of the 40 MHz pair, i.e. the secondary channel is above the primary one: rtw_get_center_ch() computes the center channel as "channel + 2" for OFFSET_LOWER, and bwmode_update_check() sets OFFSET_LOWER when the AP's HT operation IE announces SCA (secondary channel above). In nl80211 terms that is NL80211_CHAN_HT40PLUS, not HT40MINUS.

Because of the inversion, cfg80211_rtw_get_channel() reports an HT40+ association as HT40-. For an HT40+ AP on a low channel (e.g. channel 3) the resulting chandef spans below the 2.4 GHz band edge and is invalid, so the regulatory core tears the connection down 60 seconds (REG_ENFORCE_GRACE_MS) after the AP's country IE triggers a regdomain
change: reg_check_chans_work() considers the reported chandef unusable and calls cfg80211_leave(). The supplicant then reconnects, the country IE changes the regdomain again, and the cycle repeats, causing a disconnect/reconnect loop every ~65 seconds for as long as the link is up.

Observed on a TECLAST X80 Power tablet (RTL8723BS) associated to an HT40+ AP on channel 3 with a KR country IE; a kprobe trace showed cfg80211_disconnect() being invoked from reg_check_chans_work(). With the mapping fixed, "iw dev wlan0 info" reports the correct "width: 40 MHz, center1: 2432 MHz" and the periodic disconnects stop.

Fixes 5402cc178c5d ("staging: rtl8723bs: add get_channel cfg80211 implementation")
Assisted-by Claude-Code:claude-fable-5 bpftrace
---
 drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +1 -1
@@ -1958,7 +1958,7 @@ static u8 rtw_get_chan_type(struct adapt
 		else
 			return NL80211_CHAN_NO_HT;
 	case CHANNEL_WIDTH_40:
-		if (mlme_ext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+		if (mlme_ext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
 			return NL80211_CHAN_HT40PLUS;
 		else
 			return NL80211_CHAN_HT40MINUS;
author maintainer
#464 lore raw

PATCH 464/675 Bluetooth: hci_sync: Protect UUID list traversal

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chengfeng Ye <[email protected]>

commit e9027ffbf5a0f3c12ca8900822e884eae9f0821b upstream.

The hci_sync conversion moved class-of-device and EIR generation from an HCI request built under hdev->lock to asynchronous command sync work. The worker holds hdev->req_lock, but that lock does not serialize access to hdev->uuids against add_uuid() and remove_uuid(), which update the list under hdev->lock.

The following interleaving can therefore occur:

CPU0 (command sync work) CPU1 (management socket)
fetch uuid from the list

                                list_del(&uuid->list)
                                kfree(uuid)

read uuid->size

KASAN reports the resulting use-after-free:

  BUG: KASAN: slab-use-after-free in eir_create+0xb8f/0xee0
  Read of size 1 at addr ffff88810dbd8620 by task kworker/u17:0/87
  Workqueue: hci0 hci_cmd_sync_work
  Call Trace:
   eir_create+0xb8f/0xee0
   hci_update_eir_sync+0x1c0/0x330
   hci_cmd_sync_work+0x13c/0x290
   process_one_work+0x63a/0x1070
   worker_thread+0x45b/0xd10

  Allocated by task 86:
   __kasan_kmalloc+0x8f/0xa0
   add_uuid+0x18a/0x4b0
   hci_sock_sendmsg+0x1033/0x1ea0

  Freed by task 92:
   __kasan_slab_free+0x43/0x70
   kfree+0x131/0x3c0
   remove_uuid+0x25e/0x560
   hci_sock_sendmsg+0x1033/0x1ea0

Hold hdev->lock while generating and committing the class-of-device and EIR snapshots. Release it before sending an HCI command, so controller waits do not happen under the device lock. This protects all UUID list walks in these paths and restores the serialization lost in the command sync conversion.

Fixes 161510ccf91c ("Bluetooth: hci_sync: Make use of hci_cmd_sync_queue set 1")
---
 net/bluetooth/hci_sync.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
net/bluetooth/hci_sync.c +11 -2
@@ -929,12 +929,16 @@ int hci_update_eir_sync(struct hci_dev *
 
 	memset(&cp, 0, sizeof(cp));
 
+	hci_dev_lock(hdev);
 	eir_create(hdev, cp.data);
 
-	if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
+	if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) {
+		hci_dev_unlock(hdev);
 		return 0;
+	}
 
 	memcpy(hdev->eir, cp.data, sizeof(cp.data));
+	hci_dev_unlock(hdev);
 
 	return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp,
 				     HCI_CMD_TIMEOUT);
@@ -966,6 +970,7 @@ int hci_update_class_sync(struct hci_dev
 	if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
 		return 0;
 
+	hci_dev_lock(hdev);
 	cod[0] = hdev->minor_class;
 	cod[1] = hdev->major_class;
 	cod[2] = get_service_classes(hdev);
@@ -973,8 +978,12 @@ int hci_update_class_sync(struct hci_dev
 	if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
 		cod[1] |= 0x20;
 
-	if (memcmp(cod, hdev->dev_class, 3) == 0)
+	if (memcmp(cod, hdev->dev_class, 3) == 0) {
+		hci_dev_unlock(hdev);
 		return 0;
+	}
+
+	hci_dev_unlock(hdev);
 
 	return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CLASS_OF_DEV,
 				     sizeof(cod), cod, HCI_CMD_TIMEOUT);
author maintainer
#465 lore raw

PATCH 465/675 Bluetooth: RFCOMM: Fix session UAF in set_termios

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chengfeng Ye <[email protected]>

commit c783399efc22d035443f1dfbf2a09bf9562aaa5e upstream.

rfcomm_tty_set_termios() tests dlc->session without rfcomm_mutex and later passes the pointer to rfcomm_send_rpn(). The latter dereferences both session->initiator and session->sock. Meanwhile, krfcommd can unlink the DLC and free the session while holding rfcomm_mutex.

The race can proceed as follows:

TTY ioctl task krfcommd
-------------- --------
load dlc->session
enter rfcomm_send_rpn()

                                 lock rfcomm_mutex
                                 clear dlc->session
                                 free session
                                 unlock rfcomm_mutex

read session->initiator

KASAN reported:

  BUG: KASAN: slab-use-after-free in rfcomm_send_rpn+0x297/0x2a0
  Read of size 4 at addr ffff88810012a850 by task poc/92

  Call Trace:
   rfcomm_send_rpn+0x297/0x2a0
   rfcomm_tty_set_termios+0x50d/0x850
   tty_set_termios+0x596/0x950
   set_termios+0x46a/0x6e0
   tty_mode_ioctl+0x152/0xbd0
   tty_ioctl+0x915/0x1240
   __x64_sys_ioctl+0x134/0x1c0

  Allocated by task 92:
   rfcomm_session_add+0x9e/0x2e0
   rfcomm_dlc_open+0x8b1/0xe00
   rfcomm_dev_activate+0x85/0x1a0
   rfcomm_tty_open+0x90/0x280

  Freed by task 68:
   kfree+0x131/0x3c0
   rfcomm_session_del+0x119/0x180
   rfcomm_run+0x737/0x4710

Add rfcomm_dlc_send_rpn(), which holds rfcomm_mutex while it verifies that the DLC is still attached and sends the RPN frame. Have the TTY path use the helper and drop its unlocked session check. This keeps the session valid through both the frame construction and socket send.

Fixes 3a5e903c09ae ("[Bluetooth]: Implement RFCOMM remote port negotiation")
---
 include/net/bluetooth/rfcomm.h |    3 +++
 net/bluetooth/rfcomm/core.c    |   17 +++++++++++++++++
 net/bluetooth/rfcomm/tty.c     |    7 +++----
 3 files changed, 23 insertions(+), 4 deletions(-)
include/net/bluetooth/rfcomm.h +3 -0
@@ -229,6 +229,9 @@ int rfcomm_send_rpn(struct rfcomm_sessio
 			u8 bit_rate, u8 data_bits, u8 stop_bits,
 			u8 parity, u8 flow_ctrl_settings,
 			u8 xon_char, u8 xoff_char, u16 param_mask);
+int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits,
+			u8 stop_bits, u8 parity, u8 flow_ctrl_settings,
+			u8 xon_char, u8 xoff_char, u16 param_mask);
 
 /* ---- RFCOMM DLCs (channels) ---- */
 struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio);
net/bluetooth/rfcomm/core.c +17 -0
@@ -1031,6 +1031,23 @@ int rfcomm_send_rpn(struct rfcomm_sessio
 	return rfcomm_send_frame(s, buf, ptr - buf);
 }
 
+int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits,
+			u8 stop_bits, u8 parity, u8 flow_ctrl_settings,
+			u8 xon_char, u8 xoff_char, u16 param_mask)
+{
+	int err = -ENOTCONN;
+
+	rfcomm_lock();
+	if (d->session)
+		err = rfcomm_send_rpn(d->session, 1, d->dlci, bit_rate,
+				      data_bits, stop_bits, parity,
+				      flow_ctrl_settings, xon_char, xoff_char,
+				      param_mask);
+	rfcomm_unlock();
+
+	return err;
+}
+
 static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
 {
 	struct rfcomm_hdr *hdr;
net/bluetooth/rfcomm/tty.c +3 -4
@@ -861,7 +861,7 @@ static void rfcomm_tty_set_termios(struc
 
 	BT_DBG("tty %p termios %p", tty, old);
 
-	if (!dev || !dev->dlc || !dev->dlc->session)
+	if (!dev || !dev->dlc)
 		return;
 
 	/* Handle turning off CRTSCTS */
@@ -982,9 +982,8 @@ static void rfcomm_tty_set_termios(struc
 	}
 
 	if (changes)
-		rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud,
-				data_bits, stop_bits, parity,
-				RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes);
+		rfcomm_dlc_send_rpn(dev->dlc, baud, data_bits, stop_bits, parity,
+				    RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes);
 }
 
 static void rfcomm_tty_throttle(struct tty_struct *tty)
author maintainer
#466 lore raw

PATCH 466/675 exec: fix unsigned loop counter wrap in transfer_args_to_stack()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Christian Brauner <[email protected]>

commit 16cc4f5c1c4b9e45eca7f7deefa5410a292db599 upstream.

The stop value is derived from bprm->p >> PAGE_SHIFT. The index variable is an unsigned long. If bprm->p drops below PAGE_SIZE and stop becomes zero the loop condition index >= stop is always true.

After the index == 0 iteration the decrement wraps to ULONG_MAX and bprm->page[ULONG_MAX] reads sizeof(void *) bytes in front of the array. The pointer has wrapped to -1. That garbage pointer is then passed to kmap_local_page() and PAGE_SIZE bytes are copied from wherever that lands into the stack of the process being created. And the loop doesn't terminate either...

Getting there only requires bprm->p < PAGE_SIZE. On !MMU bprm_set_stack_limit() and bprm_hit_stack_limit() are empty. So the only constraint on how far bprm->p is pushed down is valid_arg_len(), i.e. that each individual string still fits in what is left.

bprm->p starts at PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *) so a single argument or environment string of a little over 31 pages leaves it in the first page:

Oops - load access fault [#1]

  CPU: 0 UID: 0 PID: 1 Comm: victim Not tainted 7.2.0-rc4 #1
  epc : __memcpy+0xd4/0xf8
   ra : transfer_args_to_stack+0xaa/0xae
   s4 : ffffffffffffffff   s2 : 0000000000000000
   a1 : ffffffdc98000000   a2 : 0000000000001000
  status: 0000000a00001880 badaddr: ffffffdc98000000 cause: 0000000000000005
  [<801a5324>] __memcpy+0xd4/0xf8
  [<800d5f6a>] load_flat_binary+0x43a/0x65e
  [<800a2de4>] bprm_execve+0x1d4/0x316
  [<800a351a>] do_execveat_common+0x12e/0x138
  [<800a3d44>] __riscv_sys_execve+0x38/0x4e
  Kernel panic - not syncing: Fatal exception in interrupt

This is an arcane bug but we should still fix it.

Count down from MAX_ARG_PAGES so the loop ends when index reaches stop, stop == 0 included. The iterations performed are unchanged for every other value of stop.

Only CONFIG_MMU=n builds are affected, transfer_args_to_stack() is used by binfmt_flat and binfmt_elf_fdpic on nommu only.

The loop predates git history. commit 7e7ec6a93434 ("elf_fdpic_transfer_args_to_stack(): make it generic") only moved it from binfmt_elf_fdpic.c into fs/exec.c and narrowed the copy to the used part of the first page. The condition and the decrement are unchanged from 2.6.12-rc2.

---
 fs/exec.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/exec.c +1 -1
@@ -736,7 +736,7 @@ int transfer_args_to_stack(struct linux_
 	stop = bprm->p >> PAGE_SHIFT;
 	sp = *sp_location;
 
-	for (index = MAX_ARG_PAGES - 1; index >= stop; index--) {
+	for (index = MAX_ARG_PAGES; index-- > stop; ) {
 		unsigned int offset = index == stop ? bprm->p & ~PAGE_MASK : 0;
 		char *src = kmap_local_page(bprm->page[index]) + offset;
 		sp -= PAGE_SIZE - offset;
author maintainer
#467 lore raw

PATCH 467/675 binfmt_misc: set have_execfd only once the interpreter is opened

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Christian Brauner <[email protected]>

commit bbf5f639918dc011aaf60aab8480218758ee68c5 upstream.

load_misc_binary() raises bprm->have_execfd as soon as it sees the 'O' (or 'C') flag. This happens well before it opens the interpreter. If that open fails the flag stays set on the bprm. binfmt_misc is at the head of the format list so an interpreter open failure that returns -ENOEXEC lets the search fall through to a later format. This means it runs the matched binary directly having never staged an interpreter. So bprm->executable is NULL while have_execfd falsely claims a descriptor is present.

Consequently, begin_new_exec() dereferences the missing executable:

  would_dump(bprm, bprm->executable);

and NULL derefs. Had it not, the hand-off later in the same function would have failed anyway. FD_ADD(0, bprm->executable) rejects a NULL file with -ENOMEM. Both sites are past the point of no return so the exec cannot be unwound either way.

This can be reached by unprivileged users as binfmt_misc can be mounted in user namespaces. So a user can register an 'O' entry whose interpreter lives on a FUSE mount, have the FUSE server fail the open with -ENOEXEC and execute a native ELF file that matches the entry.

have_execfd only means anything alongside the executable it describes which is not set until the interpreter has been opened and staged. So lets raise it there, next to execfd_creds, which is already set at that point. An open failure now leaves it clear, so the fallback format derives credentials from the binary and emits no AT_EXECFD, as it would for any native exec. The argv rewrite load_misc_binary() performs before the open is still not undone. This means the binary sees the interpreter path in argv[0] and its own path in argv[1] but that predates this change and only became observable once the exec stopped faulting.

---
 fs/binfmt_misc.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
fs/binfmt_misc.c +2 -3
@@ -227,9 +227,6 @@ static int load_misc_binary(struct linux
 			goto ret;
 	}
 
-	if (fmt->flags & MISC_FMT_OPEN_BINARY)
-		bprm->have_execfd = 1;
-
 	/* make argv[1] be the path to the binary */
 	retval = copy_string_kernel(bprm->interp, bprm);
 	if (retval < 0)
@@ -259,6 +256,8 @@ static int load_misc_binary(struct linux
 		goto ret;
 
 	bprm->interpreter = interp_file;
+	if (fmt->flags & MISC_FMT_OPEN_BINARY)
+		bprm->have_execfd = 1;
 	if (fmt->flags & MISC_FMT_CREDENTIALS)
 		bprm->execfd_creds = 1;
 
author maintainer
#468 lore raw

PATCH 468/675 platform/loongarch: laptop: Explicitly reset bl_powered state when suspend

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zixing Liu <[email protected]>

commit 91a70492c03040d51b36f595530d6491d5d6c541 upstream.

On EAECIS NL60R with EC firmware version 1.11, resuming from S3 has a very high chance (>90%) of causing the EC to lose the previous backlight power state. When this happens, the laptop resumes normally from S3, but the backlight remains off (when shining on the screen with a flash light, we can see the screen contents are updating normally).

Since there is no generic way to query the EC's backlight state on Loongson laptop platforms, assume the worst-case scenario and restart the backlight power inside the kernel each time the system resumes.

Fixes 53c762b47f72 ("platform/loongarch: laptop: Add backlight power control support")
---
 drivers/platform/loongarch/loongson-laptop.c |    1 +
 1 file changed, 1 insertion(+)
drivers/platform/loongarch/loongson-laptop.c +1 -0
@@ -189,6 +189,7 @@ static int __init setup_acpi_notify(stru
 
 static int loongson_hotkey_suspend(struct device *dev)
 {
+	bl_powered = false;
 	return 0;
 }
 
author maintainer
#469 lore raw

PATCH 469/675 rust_binder: only print failure if error has source

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alice Ryhl <[email protected]>

commit bb66b1a3452534adb8b72abf2f761375970fe472 upstream.

The commit that fixes BINDER_GET_EXTENDED_ERROR changed the condition for printing transaction failures so errors are printed even if the cause is a dead or frozen process. Undo this change so that the error is only printed if the failure has an errno associated with it.

---
 drivers/android/binder/thread.rs |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
drivers/android/binder/thread.rs +7 -7
@@ -1268,14 +1268,14 @@ impl Thread {
                         inner.extended_error =
                             ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno());
                     }
-                }
 
-                pr_warn!(
-                    "{}:{} transaction to {} failed: {err:?}",
-                    info.from_pid,
-                    info.from_tid,
-                    info.to_pid
-                );
+                    pr_warn!(
+                        "{}:{} transaction to {} failed: {err:?}",
+                        info.from_pid,
+                        info.from_tid,
+                        info.to_pid
+                    );
+                }
             }
         }
 
author maintainer
#470 lore raw

PATCH 470/675 rust: time: fix as_micros_ceil() to round correctly for negative Delta

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: FUJITA Tomonori <[email protected]>

commit 880c43b185ca52239e75bc546cc4f4d9154d0fed upstream.

The ceiling-division idiom (n + d - 1) / d only produces the correct result when n is non-negative.

For example, if n = -1000 (exactly -1us), the old code computed (-1000
+ 999) / 1000 == 0 instead of -1.

For negative n, truncating division already rounds towards positive infinity, so no bias is needed in that case.

---
 rust/kernel/time.rs |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
rust/kernel/time.rs +9 -2
@@ -421,15 +421,22 @@ impl Delta {
     /// to the value in the [`Delta`].
     #[inline]
     pub fn as_micros_ceil(self) -> i64 {
+        let n = self.as_nanos();
+        let n = if n >= 0 {
+            n.saturating_add(NSEC_PER_USEC - 1)
+        } else {
+            n
+        };
+
         #[cfg(CONFIG_64BIT)]
         {
-            self.as_nanos().saturating_add(NSEC_PER_USEC - 1) / NSEC_PER_USEC
+            n / NSEC_PER_USEC
         }
 
         #[cfg(not(CONFIG_64BIT))]
         // SAFETY: It is always safe to call `ktime_to_us()` with any value.
         unsafe {
-            bindings::ktime_to_us(self.as_nanos().saturating_add(NSEC_PER_USEC - 1))
+            bindings::ktime_to_us(n)
         }
     }
 
author maintainer
#471 lore raw

PATCH 471/675 rust: allow `clippy::unwrap_or_default` globally

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alexandre Courbot <[email protected]>

commit 4688cf884b3abcd12498e03b625d1916bf49a1e4 upstream.

Starting with rustc 1.88, the clippy::unwrap_or_default lint triggers on rust/kernel/soc.rs if CONFIG_CC_OPTIMIZE_FOR_SIZE=y:

    warning: use of `unwrap_or` to construct default value
      --> ../rust/kernel/soc.rs:66:10
      |
   66 |         .unwrap_or(core::ptr::null())
      |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`

This is a clippy bug [1]: the lint decides whether an expression is equivalent to Default::default() by inspecting the optimized MIR of <*const T as Default>::default exported by core, so its outcome depends on the optimization level core was built with. Moreover, its suggestion ignores our MSRV of 1.85 (Default for *const T is only stable since Rust 1.88), so we could not apply it anyway.

Disable the lint globally rather than working around this single occurrence; it can be re-enabled conditionally using rustc-min-version once clippy is fixed.

---
 Makefile |    5 +++++
 1 file changed, 5 insertions(+)
Makefile +5 -0
@@ -470,6 +470,10 @@ KBUILD_USERLDFLAGS := $(USERLDFLAGS)
 
 # These flags apply to all Rust code in the tree, including the kernel and
 # host programs.
+#
+# `-Aclippy::unwrap_or_default`: the lint is buggy [1] and ignores our
+# MSRV. It can trigger depending on the optimization level.
+# [1] https://github.com/rust-lang/rust-clippy/issues/17379
 export rust_common_flags := --edition=2021 \
 			    -Zbinary_dep_depinfo=y \
 			    -Astable_features \
@@ -497,6 +501,7 @@ export rust_common_flags := --edition=20
 			    -Aclippy::uninlined_format_args \
 			    -Wclippy::unnecessary_safety_comment \
 			    -Wclippy::unnecessary_safety_doc \
+			    -Aclippy::unwrap_or_default \
 			    -Wrustdoc::missing_crate_level_docs \
 			    -Wrustdoc::unescaped_backticks
 
author maintainer
#472 lore raw

PATCH 472/675 objtool/rust: add one more `noreturn` Rust function for Rust 1.99.0

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Miguel Ojeda <[email protected]>

commit 5a81c35c3b18cd59ded56171a6a9f643b92a6759 upstream.

Starting with Rust 1.99.0 (expected 2026-10-01), under CONFIG_RUST_DEBUG_ASSERTIONS=y, objtool may report:

rust/kernel.o: warning: objtool: _R..._6kernel12module_param9set_paramaEB4_() falls through to next function _R..._6kernel12module_param9set_paramhEB4_()

(and many others) due to calls to the noreturn symbol [1]:

core::panicking::panic_null_reference_constructed

Thus add the mangled one to the list so that objtool knows it is actually noreturn.

See commit 56d680dd23c3 ("objtool/rust: list noreturn Rust functions") for more details.

---
 tools/objtool/check.c |    1 +
 1 file changed, 1 insertion(+)
tools/objtool/check.c +1 -0
@@ -228,6 +228,7 @@ static bool is_rust_noreturn(const struc
 	       str_ends_with(func->name, "_4core9panicking18panic_nounwind_fmt")			||
 	       str_ends_with(func->name, "_4core9panicking19assert_failed_inner")			||
 	       str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference")		||
+	       str_ends_with(func->name, "_4core9panicking32panic_null_reference_constructed")		||
 	       str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference")	||
 	       str_ends_with(func->name, "_7___rustc17rust_begin_unwind")				||
 	       strstr(func->name, "_4core9panicking13assert_failed")					||
author maintainer
#473 lore raw

PATCH 473/675 LoongArch: Fix address space mismatch in kexec command line lookup

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: George Guo <[email protected]>

commit 485ed44db5694d8d2e5027f63ad608e705286f30 upstream.

When searching the loaded segments for the "kexec" command line marker, the kexec_load(2) path (file_mode == 0) passes the user-space segment buffer straight to strncmp() through a bogus (char __user *) cast. This dereferences a user pointer in kernel context, which is wrong and is flagged by sparse:

arch/loongarch/kernel/machine_kexec.c:84:51: sparse: incorrect type in argument 2 (different address spaces) @@ expected char const * @@ got char [noderef] __user *

Here copy the marker-sized prefix of each segment into a small on-stack buffer with copy_from_user() before comparing, and skip segments that fault. The subsequent copy_from_user() that stages the full command line into the safe area is left unchanged.

---
 arch/loongarch/kernel/machine_kexec.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
arch/loongarch/kernel/machine_kexec.c +4 -1
@@ -42,6 +42,7 @@ static unsigned long first_ind_entry;
 int machine_kexec_prepare(struct kimage *kimage)
 {
 	int i;
+	char head[8];
 	char *bootloader = "kexec";
 	void *cmdline_ptr = (void *)KEXEC_CMDLINE_ADDR;
 
@@ -59,7 +60,9 @@ int machine_kexec_prepare(struct kimage
 	} else {
 		/* Find the command line */
 		for (i = 0; i < kimage->nr_segments; i++) {
-			if (!strncmp(bootloader, (char __user *)kimage->segment[i].buf, strlen(bootloader))) {
+			if (copy_from_user(head, kimage->segment[i].buf, strlen(bootloader)))
+				continue;
+			if (!strncmp(bootloader, head, strlen(bootloader))) {
 				if (!copy_from_user(cmdline_ptr, kimage->segment[i].buf, COMMAND_LINE_SIZE))
 					kimage->arch.cmdline_ptr = (unsigned long)cmdline_ptr;
 				break;
author maintainer
#474 lore raw

PATCH 474/675 LoongArch: Fix oops during single-step debugging

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Haoran Jiang <[email protected]>

commit 73555fdab5e1e4f24ca000c41a616b34edf4b55d upstream.

When entering KDB via a breakpoint and then performing single-step debugging, an oops is triggered. Now during single-step debugging, kdb_local() expects the reason to be KDB_REASON_SSTEP, but it is actually KDB_REASON_OOPS. In kdb_stub(), when determining the reason, the ex_vector for single-step should be 0, as already implemented on other architectures such as arm64 and riscv.

Before the patch:
[112]kdb> ss

Entering kdb (current=0x900020009f520000, pid 10661) on processor 112 Oops: (null)
due to oops @ 0x90000000005b57a4

---
 arch/loongarch/kernel/kgdb.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/loongarch/kernel/kgdb.c +2 -1
@@ -252,7 +252,8 @@ static int kgdb_loongarch_notify(struct
 	if (atomic_read(&kgdb_active) != -1)
 		kgdb_nmicallback(smp_processor_id(), regs);
 
-	if (kgdb_handle_exception(args->trapnr, args->signr, cmd, regs))
+	if (kgdb_handle_exception(regs->csr_era == stepped_address ? 0 : args->trapnr,
+				  args->signr, cmd, regs))
 		return NOTIFY_DONE;
 
 	if (atomic_read(&kgdb_setting_breakpoint))
author maintainer
#475 lore raw

PATCH 475/675 LoongArch: Move jump_label_init() before parse_early_param()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kanglong Wang <[email protected]>

commit ea68d444a658783234a06f05414e41cf93a18fb2 upstream.

When enabling both CONFIG_MEM_ALLOC_PROFILING=y and CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT=y, then diabling memory profiling by adding the boot parameter 'sysctl.vm.mem_profiling=0' will cause the kernel failed to boot.

After analysis, this is because jump_label_init() must be called before parse_early_param(), the early param handlers may modify static keys by static_branch_enable/disable().

Fix this by moving jump_label_init() to before parse_early_param(). The solution is similar to other architectures.

---
 arch/loongarch/kernel/setup.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/loongarch/kernel/setup.c +1 -2
@@ -610,6 +610,7 @@ void __init setup_arch(char **cmdline_p)
 	memblock_init();
 	pagetable_init();
 	bootcmdline_init(cmdline_p);
+	jump_label_init(); /* Initialise the static keys for early params */
 	parse_early_param();
 	reserve_initrd_mem();
 
@@ -617,8 +618,6 @@ void __init setup_arch(char **cmdline_p)
 	arch_mem_init(cmdline_p);
 
 	resource_init();
-	jump_label_init(); /* Initialise the static keys for paravirtualization */
-
 #ifdef CONFIG_SMP
 	plat_smp_setup();
 	prefill_possible_map();
author maintainer
#476 lore raw

PATCH 476/675 LoongArch: Retrieve CPU package ID from PPTT when available

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Rong Bao <[email protected]>

commit 4e8f58620f6717f72f3d88a2c8f25c0c656d0ba7 upstream.

Currently, the LoongArch CPU topology initialization code calculates each core's package ID by dividing its physical ID by loongson_sysconf. cores_per_package. This relies on the assumption that cores_per_package counts in the same domain as physical IDs.

On Loongson-3B6000 (XB612B0V_1.2), cores_per_package matches the visible core count -- 24 in this case. However, the physical IDs range from 0 to 31 in a noncontinuous fashion:

        $ cat /proc/cpuinfo | grep -i -F 'global_id'
        global_id               : 0
        global_id               : 1
        global_id               : 4
        global_id               : 5
        global_id               : 6
        global_id               : 7
        global_id               : 8
        global_id               : 9
        global_id               : 10
        global_id               : 11
        global_id               : 14
        global_id               : 15
        global_id               : 16
        global_id               : 17
        global_id               : 20
        global_id               : 21
        global_id               : 22
        global_id               : 23
        global_id               : 26
        global_id               : 27
        global_id               : 28
        global_id               : 29
        global_id               : 30
        global_id               : 31

Retrieve the exact package ID from ACPI PPTT when available, in the same style as retrieving the core ID and thread ID in parse_acpi_topology(). Use this information in loongson_init_secondary() when the PPTT readout is successful. The original division logic is kept as a fallback.

Meanwhile, since some existing code paths like loongson3_cpufreq expect a continuous integer sequence of package IDs in [0, MAX_PACKAGES) when retrieving from cpu_data[], here we also canonicalize the package ID to be filled in parse_acpi_topology() to meet such an expectation.

---
 arch/loongarch/kernel/acpi.c |   27 ++++++++++++++++++++++++++-
 arch/loongarch/kernel/smp.c  |    4 ++--
 2 files changed, 28 insertions(+), 3 deletions(-)
arch/loongarch/kernel/acpi.c +26 -1
@@ -201,10 +201,12 @@ static void __init acpi_process_madt(voi
 }
 
 int pptt_enabled;
+static int acpi_nr_packages;
+static int acpi_package_ids[MAX_PACKAGES];
 
 int __init parse_acpi_topology(void)
 {
-	int cpu, topology_id;
+	int i, cpu, topology_id;
 
 	for_each_possible_cpu(cpu) {
 		topology_id = find_acpi_cpu_topology(cpu, 0);
@@ -222,6 +224,29 @@ int __init parse_acpi_topology(void)
 
 			cpu_data[cpu].core = topology_id;
 		}
+
+		topology_id = find_acpi_cpu_topology_package(cpu);
+		if (topology_id < 0) {
+			pr_warn("Invalid BIOS PPTT\n");
+			return -ENOENT;
+		}
+
+		for (i = 0; i < acpi_nr_packages; i++)
+			if (acpi_package_ids[i] == topology_id)
+				break;
+
+		if (i == acpi_nr_packages)
+			acpi_package_ids[acpi_nr_packages++] = topology_id;
+
+		cpu_data[cpu].package = topology_id;
+	}
+
+	for_each_possible_cpu(cpu) {
+		for (i = 0; i < acpi_nr_packages; i++)
+			if (cpu_data[cpu].package == acpi_package_ids[i]) {
+				cpu_data[cpu].package = i; /* Canonicalize */
+				break;
+			}
 	}
 
 	pptt_enabled = 1;
arch/loongarch/kernel/smp.c +2 -2
@@ -412,10 +412,10 @@ void loongson_init_secondary(void)
 	numa_add_cpu(cpu);
 #endif
 	per_cpu(cpu_state, cpu) = CPU_ONLINE;
-	cpu_data[cpu].package =
-		     cpu_logical_map(cpu) / loongson_sysconf.cores_per_package;
 	cpu_data[cpu].core = pptt_enabled ? cpu_data[cpu].core :
 		     cpu_logical_map(cpu) % loongson_sysconf.cores_per_package;
+	cpu_data[cpu].package = pptt_enabled ? cpu_data[cpu].package :
+		     cpu_logical_map(cpu) / loongson_sysconf.cores_per_package;
 	cpu_data[cpu].global_id = cpu_logical_map(cpu);
 }
 
author maintainer
#477 lore raw

PATCH 477/675 cdrom: fix stack out-of-bounds read in CDROMVOLCTRL

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xu Rao <[email protected]>

commit b27e195d4db8dea263050bdbeb11881b2999c9c6 upstream.

mmc_ioctl_cdrom_volume() first reads the audio control mode page into a 32-byte stack buffer with cgc->buflen set to 24. If the device reports a block descriptor, the function increases cgc->buflen to include that descriptor and reads the page again.

For CDROMVOLCTRL, the function then builds a MODE SELECT parameter list by moving cgc->buffer forward by offset - 8 bytes. This drops the block descriptor from the outgoing payload and leaves a new 8-byte mode parameter header in front of the audio control page. However, cgc->buflen is left unchanged.

With a standard 8-byte block descriptor, cgc->buffer points at buffer + 8 but cgc->buflen remains 32. cdrom_mode_select() therefore asks the low level packet path to write 32 bytes from that adjusted pointer, reading 8 bytes past the end of the 32-byte stack buffer.

This is not hit by CDROMVOLREAD, and CDROMVOLCTRL only triggers it on drives that return a non-zero block descriptor length, which helps explain why it has gone unnoticed. The overread is also sent to the device as extra MODE SELECT payload, so it may not produce an obvious local failure.

Reduce cgc->buflen by the same amount as the buffer pointer adjustment so the MODE SELECT transfer covers only the intended parameter list.

---
 drivers/cdrom/cdrom.c |    1 +
 1 file changed, 1 insertion(+)
drivers/cdrom/cdrom.c +1 -0
@@ -3187,6 +3187,7 @@ static noinline int mmc_ioctl_cdrom_volu
 
 	/* set volume */
 	cgc->buffer = buffer + offset - 8;
+	cgc->buflen -= offset - 8;
 	memset(cgc->buffer, 0, 8);
 	return cdrom_mode_select(cdi, cgc);
 }
author maintainer
#478 lore raw

PATCH 478/675 rhashtable: clear stale iter->p on table restart

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cen Zhang (Microsoft) <[email protected]>

commit 8173f7e2ce67e6ca1d4763f3da14e5b01ce77456 upstream.

rhashtable_walk_start_check() has two restart paths when resuming a walk. When iter->walker.tbl is valid, it re-validates iter->p against the table and sets iter->p = NULL if the object is gone. When iter->walker.tbl is NULL (table was freed during resize), it resets slot and skip but forgets to clear iter->p.

rhashtable_walk_next() then dereferences the stale iter->p, reading freed memory. This is a use-after-free.

Any caller that does multi-fragment rhashtable walks across walk_stop/walk_start boundaries is affected. Concrete cases include netlink_diag (__netlink_diag_dump in net/netlink/diag.c) and TIPC (tipc_nl_sk_walk in net/tipc/socket.c).

Crash stack (netlink_diag):

  BUG: KASAN: slab-use-after-free in rhashtable_walk_next+0x365/0x3c0
  Read of size 8 at addr ffff88801a9d2438 (freed kmalloc-2k, offset 1080)
  Call Trace:
   rhashtable_walk_next+0x365/0x3c0 (lib/rhashtable.c:1016)
   __netlink_diag_dump+0x160/0x760 (net/netlink/diag.c:122)
   netlink_diag_dump+0xc2/0x240
   netlink_dump+0x5bc/0x1270
   netlink_recvmsg+0x7a3/0x980
   sock_recvmsg+0x1bc/0x200
   __sys_recvfrom+0x1d4/0x2c0
---
 lib/rhashtable.c |    1 +
 1 file changed, 1 insertion(+)
lib/rhashtable.c +1 -0
@@ -742,6 +742,7 @@ int rhashtable_walk_start_check(struct r
 		iter->walker.tbl = rht_dereference_rcu(ht->tbl, ht);
 		iter->slot = 0;
 		iter->skip = 0;
+		iter->p = NULL;
 		return -EAGAIN;
 	}
 
author maintainer
#479 lore raw

PATCH 479/675 firmware: stratix10-svc: fix memory leaks and list corruption bugs

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tze Yee Ng <[email protected]>

commit 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c upstream.

Fix a memory leak when gen_pool_alloc() fails by freeing pmem on the error path. Switch pmem allocation from devm_kzalloc() to kzalloc() with explicit kfree() in the free path to match its list-managed lifetime. Remove the erroneous list_del(&svc_data_mem) which corrupted the list head on failed lookups.

Fixes 7ca5ce896524 ("firmware: add Intel Stratix10 service layer driver")
---
 drivers/firmware/stratix10-svc.c |   10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/firmware/stratix10-svc.c +6 -4
@@ -1080,14 +1080,16 @@ void *stratix10_svc_allocate_memory(stru
 	struct gen_pool *genpool = chan->ctrl->genpool;
 	size_t s = roundup(size, 1 << genpool->min_alloc_order);
 
-	pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL);
+	pmem = kzalloc_obj(*pmem);
 	if (!pmem)
 		return ERR_PTR(-ENOMEM);
 
 	guard(mutex)(&svc_mem_lock);
 	va = gen_pool_alloc(genpool, s);
-	if (!va)
+	if (!va) {
+		kfree(pmem);
 		return ERR_PTR(-ENOMEM);
+	}
 
 	memset((void *)va, 0, s);
 	pa = gen_pool_virt_to_phys(genpool, va);
@@ -1113,6 +1115,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_allocate
 void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr)
 {
 	struct stratix10_svc_data_mem *pmem;
+
 	guard(mutex)(&svc_mem_lock);
 
 	list_for_each_entry(pmem, &svc_data_mem, node)
@@ -1121,10 +1124,9 @@ void stratix10_svc_free_memory(struct st
 				       (unsigned long)kaddr, pmem->size);
 			pmem->vaddr = NULL;
 			list_del(&pmem->node);
+			kfree(pmem);
 			return;
 		}
-
-	list_del(&svc_data_mem);
 }
 EXPORT_SYMBOL_GPL(stratix10_svc_free_memory);
 
author maintainer
#480 lore raw

PATCH 480/675 x86/boot/compressed: Disable jump tables

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nathan Chancellor <[email protected]>

commit 4a9ec5ec9555ad62dc5b81a37ac946025c2ea002 upstream.

After a recent upstream LLVM change to start generating jump and lookup tables in switch statements in more instances [1], linking the compressed x86 boot image when CONFIG_KERNEL_ZSTD is enabled fails with:

ld.lld: error: Unexpected run-time relocations (.rela) detected!

Dumping the relocations in misc.o, which is the only file influenced by CONFIG_KERNEL_ZSTD in the decompressor, shows dynamic relocations to some string constants, which correspond to the string literals in the switch statement in handle_zstd_error():

Relocation section '.rela.data.rel.ro' at offset 0x277b0 contains 31 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend

  0000000000000000  0000006600000001 R_X86_64_64            0000000000000000 .rodata.str1.1 + 73a
  0000000000000008  0000006600000001 R_X86_64_64            0000000000000000 .rodata.str1.1 + 78e
  0000000000000010  0000006600000001 R_X86_64_64            0000000000000000 .rodata.str1.1 + 78e
  0000000000000018  0000006600000001 R_X86_64_64            0000000000000000 .rodata.str1.1 + 78e

...

This optimization is problematic for the decompressor environment, as it is built as -fPIE without any explicit absolute references (as described at the top of misc.c) while not applying any dynamic relocations, hence the linker assertion. To opt out of this optimization, which is of little value in this special early boot code, and to mirror the other x86 startup code in arch/x86/boot/startup, disable jump tables in the decompressor.

---
 arch/x86/boot/compressed/Makefile |    1 +
 1 file changed, 1 insertion(+)
arch/x86/boot/compressed/Makefile +1 -0
@@ -27,6 +27,7 @@ targets := vmlinux vmlinux.bin vmlinux.b
 KBUILD_CFLAGS := -m$(BITS) -O2 $(CLANG_FLAGS)
 KBUILD_CFLAGS += -std=gnu11
 KBUILD_CFLAGS += -fno-strict-aliasing -fPIE
+KBUILD_CFLAGS += -fno-jump-tables
 KBUILD_CFLAGS += -Wundef
 KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
 cflags-$(CONFIG_X86_32) := -march=i386
author maintainer
#481 lore raw

PATCH 481/675 comedi: comedi_parport: deal with premature interrupt

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ian Abbott <[email protected]>

commit 17221216ae8ce6a24e8a4e787382e3ebc81b88a8 upstream.

Syzbot reported a general protection fault in comedi_get_is_subdevice_running(), which was called from the interrupt handler parport_interrupt() in the "comedi_parport" driver, but it does not currently have a C reproducer for the problem. It's probably due to a premature interrupt for one of two reasons:

1. The driver sets up the interrupt handler before the comedi subdevices used by the interrupt handler have been allocated, but does not disable the interrupt in the parallel port's CTRL register first.
2. The driver uses a user-supplied I/O port base address which Syzbot would have supplied, but it might not be backed by real parallel port hardware.

Change the initialization order in the driver's comedi "attach" handler (parport_attach()) so that the hardware registers are initialized before the interrupt handler is requested. This should prevent premature interrupts occurring for real hardware.

Also add a test to the interrupt handler to ensure the comedi device is fully attached and return early if it isn't.

Fixes 241ab6ad7108e ("Staging: comedi: add comedi_parport driver")
Reported-by [email protected]
Cc stable <[email protected]>
---
 drivers/comedi/drivers/comedi_parport.c |   13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/comedi/drivers/comedi_parport.c +10 -3
@@ -211,6 +211,13 @@ static irqreturn_t parport_interrupt(int
 	unsigned int ctrl;
 	unsigned short val = 0;
 
+	/*
+	 * Check device is fully attached.  Device interrupts should have
+	 * been disabled, but do this in case of bad hardware.
+	 */
+	if (!dev->attached)
+		return IRQ_NONE;
+
 	ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
 	if (!(ctrl & PARPORT_CTRL_IRQ_ENA))
 		return IRQ_NONE;
@@ -231,6 +238,9 @@ static int parport_attach(struct comedi_
 	if (ret)
 		return ret;
 
+	outb(0, dev->iobase + PARPORT_DATA_REG);
+	outb(0, dev->iobase + PARPORT_CTRL_REG);
+
 	if (it->options[1]) {
 		ret = request_irq(it->options[1], parport_interrupt, 0,
 				  dev->board_name, dev);
@@ -286,9 +296,6 @@ static int parport_attach(struct comedi_
 		s->cancel	= parport_intr_cancel;
 	}
 
-	outb(0, dev->iobase + PARPORT_DATA_REG);
-	outb(0, dev->iobase + PARPORT_CTRL_REG);
-
 	return 0;
 }
 
author maintainer
#482 lore raw

PATCH 482/675 uio_hv_generic: Bind to FCopy device by default

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ben Hutchings <[email protected]>

commit 87d3621ccc63b3999d756bb59f0cedd738c28eb3 upstream.

The Hyper-V kernel-mode fcopy driver was removed in 6.10 and the new fcopy daemon requires this uio driver to function. However, by default the driver does not bind to any devices, and must be configured through the sysfs "new_id" file.

Since the FCopy device is now only usable through this driver, add its ID to the driver's ID table so that the daemon will work "out of the box".

Fixes ec314f61e4fc ("Drivers: hv: Remove fcopy driver")
Cc stable <[email protected]>
---
 drivers/uio/uio_hv_generic.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/uio/uio_hv_generic.c +7 -1
@@ -395,9 +395,15 @@ hv_uio_remove(struct hv_device *dev)
 	vmbus_free_ring(dev->channel);
 }
 
+static const struct hv_vmbus_device_id hv_uio_id_table[] = {
+	{ HV_FCOPY_GUID },
+	{}
+};
+MODULE_DEVICE_TABLE(vmbus, hv_uio_id_table);
+
 static struct hv_driver hv_uio_drv = {
 	.name = "uio_hv_generic",
-	.id_table = NULL, /* only dynamic id's */
+	.id_table = hv_uio_id_table,
 	.probe = hv_uio_probe,
 	.remove = hv_uio_remove,
 };
author maintainer
#483 lore raw

PATCH 483/675 serial: sc16is7xx: implement gpio get_direction() callback

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hugo Villeneuve <[email protected]>

commit af071d9e07e57cfff239e8d09d2f3b05ebc9c667 upstream.

It's strongly recommended for GPIO drivers to always implement the .get_direction() callback - even when the direction is tracked in software. The GPIO core emits a warning when the callback is missing and a user reads the direction of a line, e.g. via /sys/kernel/debug/gpio.

---
 drivers/tty/serial/sc16is7xx.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/tty/serial/sc16is7xx.c +12 -0
@@ -1328,6 +1328,17 @@ static int sc16is7xx_gpio_set(struct gpi
 	return 0;
 }
 
+static int sc16is7xx_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+	struct sc16is7xx_port *s = gpiochip_get_data(chip);
+	struct uart_port *port = &s->p[0].port;
+	unsigned int val;
+
+	val = sc16is7xx_port_read(port, SC16IS7XX_IODIR_REG);
+
+	return val & BIT(offset) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
 static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip,
 					  unsigned offset)
 {
@@ -1405,6 +1416,7 @@ static int sc16is7xx_setup_gpio_chip(str
 	s->gpio.parent		 = dev;
 	s->gpio.label		 = dev_name(dev);
 	s->gpio.init_valid_mask	 = sc16is7xx_gpio_init_valid_mask;
+	s->gpio.get_direction	 = sc16is7xx_gpio_get_direction;
 	s->gpio.direction_input	 = sc16is7xx_gpio_direction_input;
 	s->gpio.get		 = sc16is7xx_gpio_get;
 	s->gpio.direction_output = sc16is7xx_gpio_direction_output;
author maintainer
#484 lore raw

PATCH 484/675 serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jiangshan Yi <[email protected]>

commit 7fb13fd7e9a59a37cd911efff83abe19e3ee029d upstream.

Commit b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms") replaced the dnv_board setup and exit callbacks with PTR_IF(false, ...), which evaluates to NULL. However, the three call sites in mid8250_probe() and mid8250_remove() unconditionally dereference these function pointers without NULL checks, causing a NULL pointer dereference (kernel oops) on any Denverton (DNV), Ice Lake Xeon D (ICX-D/CDF), or Snowridge (SNR) platform.

Fix this by adding the missing NULL checks before calling the setup and exit callbacks.

Fixes b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms")
Cc stable <[email protected]>
---
 drivers/tty/serial/8250/8250_mid.c |   14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)
drivers/tty/serial/8250/8250_mid.c +9 -5
@@ -318,9 +318,11 @@ static int mid8250_probe(struct pci_dev
 	if (!uart.port.membase)
 		return -ENOMEM;
 
-	ret = mid->board->setup(mid, &uart.port);
-	if (ret)
-		return ret;
+	if (mid->board->setup) {
+		ret = mid->board->setup(mid, &uart.port);
+		if (ret)
+			return ret;
+	}
 
 	ret = mid8250_dma_setup(mid, &uart);
 	if (ret)
@@ -336,7 +338,8 @@ static int mid8250_probe(struct pci_dev
 	return 0;
 
 err:
-	mid->board->exit(mid);
+	if (mid->board->exit)
+		mid->board->exit(mid);
 	return ret;
 }
 
@@ -346,7 +349,8 @@ static void mid8250_remove(struct pci_de
 
 	serial8250_unregister_port(mid->line);
 
-	mid->board->exit(mid);
+	if (mid->board->exit)
+		mid->board->exit(mid);
 }
 
 static const struct mid8250_board pnw_board = {
author maintainer
#485 lore raw

PATCH 485/675 selftests: ntsync: correct CONFIG_NTSYNC name

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ethan Nelson-Moore <[email protected]>

commit f97752cfe511c1ed9933057455c73aaac07d6517 upstream.

The config fragment for these tests defines CONFIG_WINESYNC, which refers to an earlier name for the ntsync driver before it was merged [1]. Correct it to define CONFIG_NTSYNC instead.

[1] https://lore.kernel.org/all/[email protected]/

---
 tools/testing/selftests/drivers/ntsync/config |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/drivers/ntsync/config +1 -1
@@ -1 +1 @@
-CONFIG_WINESYNC=y
+CONFIG_NTSYNC=y
author maintainer
#486 lore raw

PATCH 486/675 mei: bus: access mei_device under device_lock on cleanup

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alexander Usyskin <[email protected]>

commit f112ea910e554d58b4b39a4492b7d302f0f4204f upstream.

Fix couple of problems in mei_cl_bus_dev_release():

mei_cl_flush_queues() is running without lock. bus->file_list access after mei_dev_bus_put(bus) can become a use-after-free if this was the last reference to bus.

Protect queues cleanup and WARN traversal by device lock there to avoid the concurrent access problems.
Move WARN traversal before mei_dev_bus_put(bus).

This file uses bus variable name for mei_device, adjust code of mei_cl_bus_dev_release() to use bus variable too.

Cc stable <[email protected]>
Fixes 35e8a426b16a ("mei: bus: Check for still connected devices in mei_cl_bus_dev_release()")
---
 drivers/misc/mei/bus.c |   16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)
drivers/misc/mei/bus.c +9 -7
@@ -4,6 +4,7 @@
  * Intel Management Engine Interface (Intel MEI) Linux driver
  */
 
+#include <linux/cleanup.h>
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
@@ -1317,15 +1318,16 @@ static void mei_dev_bus_put(struct mei_d
 static void mei_cl_bus_dev_release(struct device *dev)
 {
 	struct mei_cl_device *cldev = to_mei_cl_device(dev);
-	struct mei_device *mdev = cldev->cl->dev;
+	struct mei_device *bus = cldev->bus;
 	struct mei_cl *cl;
 
-	mei_cl_flush_queues(cldev->cl, NULL);
-	mei_me_cl_put(cldev->me_cl);
-	mei_dev_bus_put(cldev->bus);
-
-	list_for_each_entry(cl, &mdev->file_list, link)
-		WARN_ON(cl == cldev->cl);
+	scoped_guard(mutex, &bus->device_lock) {
+		mei_cl_flush_queues(cldev->cl, NULL);
+		mei_me_cl_put(cldev->me_cl);
+		list_for_each_entry(cl, &bus->file_list, link)
+			WARN_ON(cl == cldev->cl);
+	}
+	mei_dev_bus_put(bus);
 
 	kfree(cldev->cl);
 	kfree(cldev);
author maintainer
#487 lore raw

PATCH 487/675 intel_th: fix MSC output device reference leak

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guangshuo Li <[email protected]>

commit 761b785a0cfbce43761227bc42a7f984f31f8921 upstream.

intel_th_output_open() looks up the output device with bus_find_device_by_devt(), which returns the device with a reference that must be dropped after use.

commit 95fc36a234da ("intel_th: fix device leak on output open()") attempted to drop the reference from intel_th_output_release(). However, a successful open replaces file->f_op with the output driver file operations before returning, so close runs the output driver release callback instead.

For MSC outputs, close runs intel_th_msc_release(), which only removes the per-file iterator and does not drop the device reference taken by intel_th_output_open(). Consequently, every successful MSC output open leaks one device reference.

Drop the device reference from intel_th_msc_release(), which is the release path actually used for MSC output files. Remove the now-unused intel_th_output_release() callback from intel_th_output_fops.

---
 drivers/hwtracing/intel_th/core.c |   10 ----------
 drivers/hwtracing/intel_th/msu.c  |    2 ++
 2 files changed, 2 insertions(+), 10 deletions(-)
drivers/hwtracing/intel_th/core.c +0 -10
@@ -843,18 +843,8 @@ out_put_device:
 	return err;
 }
 
-static int intel_th_output_release(struct inode *inode, struct file *file)
-{
-	struct intel_th_device *thdev = file->private_data;
-
-	put_device(&thdev->dev);
-
-	return 0;
-}
-
 static const struct file_operations intel_th_output_fops = {
 	.open	= intel_th_output_open,
-	.release = intel_th_output_release,
 	.llseek	= noop_llseek,
 };
 
drivers/hwtracing/intel_th/msu.c +2 -0
@@ -1490,8 +1490,10 @@ static int intel_th_msc_release(struct i
 {
 	struct msc_iter *iter = file->private_data;
 	struct msc *msc = iter->msc;
+	struct intel_th_device *thdev = msc->thdev;
 
 	msc_iter_remove(iter, msc);
+	put_device(&thdev->dev);
 
 	return 0;
 }
author maintainer
#488 lore raw

PATCH 488/675 misc: nsm: only unlock nsm_dev on post-lock error paths

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Runyu Xiao <[email protected]>

commit ce1fed11d18e163baf7f875152a33bf80f625c1a upstream.

nsm_dev_ioctl() jumps to the common out label even when the initial copy_from_user() fails before nsm->lock has been taken. The error path then blindly unlocks a mutex that was never acquired.

This issue was found by our static analysis tool and then manually reviewed against the current tree.

The grounded PoC kept the miscdevice ioctl entry and the pre-lock copy_from_user(&raw, argp, _IOC_SIZE(cmd)) failure path by issuing NSM_IOCTL_RAW with an invalid user pointer. That failure reaches the shared out label before mutex_lock(&nsm->lock). Lockdep reported:

WARNING: bad unlock balance detected!
exploit/193 is trying to release lock (&global_nsm.lock) at:

  nsm_dev_ioctl+0x5f/0xcf [vuln_msv]
  but there are no more locks to release!
  no locks held by exploit/193.

Return immediately on the pre-lock copy_from_user() failure and keep the common unlock label for the post-lock paths only.

Fixes b9873755a6c8 ("misc: Add Nitro Secure Module driver")
Cc stable <[email protected]>
---
 drivers/misc/nsm.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/misc/nsm.c +1 -1
@@ -367,7 +367,7 @@ static long nsm_dev_ioctl(struct file *f
 	/* Copy user argument struct to kernel argument struct */
 	r = -EFAULT;
 	if (copy_from_user(&raw, argp, _IOC_SIZE(cmd)))
-		goto out;
+		return r;
 
 	mutex_lock(&nsm->lock);
 
author maintainer
#489 lore raw

PATCH 489/675 misc: nsm: pin the module while the device is open

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xu Rao <[email protected]>

commit 3b231f1e9990f4c21220d0a69733ce2105891ff9 upstream.

misc_open() installs a misc driver's file operations with fops_get(), which pins file_operations::owner before replacing the file's f_op. The NSM misc device leaves nsm_dev_fops.owner unset, so opening /dev/nsm does not take a module reference on the nsm driver.

If the driver is built as a module, an open file descriptor can therefore survive rmmod of the module that provides its ioctl callbacks. A later ioctl through that descriptor can call into unloaded module text.

Set nsm_dev_fops.owner to THIS_MODULE so the misc core holds the module while any /dev/nsm file descriptor is open, matching the lifetime expectation for the installed file operations.

Fixes b9873755a6c8 ("misc: Add Nitro Secure Module driver")
Cc stable <[email protected]>
---
 drivers/misc/nsm.c |    1 +
 1 file changed, 1 insertion(+)
drivers/misc/nsm.c +1 -0
@@ -413,6 +413,7 @@ static int nsm_device_init_vq(struct vir
 }
 
 static const struct file_operations nsm_dev_fops = {
+	.owner = THIS_MODULE,
 	.unlocked_ioctl = nsm_dev_ioctl,
 	.compat_ioctl = compat_ptr_ioctl,
 };
author maintainer
#490 lore raw

PATCH 490/675 tracing: Fix context switch counter truncation

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Usama Arif <[email protected]>

commit 8f76afb9b114bee1c1251e0e52553e9dc7c59f20 upstream.

trace_user_fault_read() samples nr_context_switches_cpu() before enabling preemption and retries the user copy if the counter changes. The helper returns unsigned long long because rq->nr_switches is u64, but the saved value is unsigned int.

Once a CPU has performed 2^32 context switches, assigning the counter to cnt discards its upper bits. The comparison after the copy promotes cnt back to unsigned long long, but the lost bits remain zero, so it reports a change even when the task was never scheduled out. Every retry then fails the same way until the 100-try guard warns and the user copy is abandoned.

This affects long-running systems and workloads with high context-switch rates. A CPU switching 1,000 times per second takes about 50 days.

Store the sampled count in unsigned long long so the full value is preserved.

---
 kernel/trace/trace.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace.c +1 -1
@@ -7325,7 +7325,7 @@ static char *trace_user_fault_read(struc
 {
 	int cpu = smp_processor_id();
 	char *buffer = per_cpu_ptr(tinfo->tbuf, cpu)->buf;
-	unsigned int cnt;
+	unsigned long long cnt;
 	int trys = 0;
 	int ret;
 
author maintainer
#491 lore raw

PATCH 491/675 tracing: Fix mmiotrace possible NULL dereferencing of hiter->dev

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Steven Rostedt <[email protected]>

commit 144f29e85702234b23d2a62abf723e6a17eb5427 upstream.

If the mmio_pipe_open() fails to find a PCI device, the hiter->dev will be assigned to NULL. The mmiotrace read() function dereferences the hiter->dev if hiter exists.

Change the test of the read to not only check hiter being NULL, but also the hiter->dev before dereferencing it.

---
 kernel/trace/trace_mmiotrace.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_mmiotrace.c +1 -1
@@ -146,7 +146,7 @@ static ssize_t mmio_read(struct trace_it
 		goto print_out;
 	}
 
-	if (!hiter)
+	if (!hiter || !hiter->dev)
 		return 0;
 
 	mmio_print_pcidev(s, hiter->dev);
author maintainer
#492 lore raw

PATCH 492/675 tracing: Fix resource leak on mmiotrace trace_pipe close

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: deepakraog <[email protected]>

commit c1d87e724ae55e781b7cc7ccafb34d9e668582b2 upstream.

The mmiotrace tracer was added May 12th 2008. At that time, resources created in pipe_open() could not be freed because there was not pipe_close function pointer of the tracer. The pipe_close function pointer was added in December 7th, 2009, but the mmiotrace tracer was not updated.

mmio_pipe_open() allocates a header_iter and takes a pci_dev reference when trace_pipe is opened. mmio_close() frees them, but it was only wired to the tracer's .close callback.

tracing_release_pipe() invokes .pipe_close, not .close, when the trace_pipe file is released. As a result, closing trace_pipe with the mmiotrace tracer active leaked the header_iter allocation and left a stale pci_dev reference.

Set .pipe_close to mmio_close, matching how function_graph wires both callbacks to the same handler.

Note, if the trace_pipe is read to completion, it will clean up the resources, but if one were to run:

  # head -n 1 /sys/kernel/tracing/trace_pipe
 VERSION 20070824

Over and over again, it would trigger a massive leak.

---
 kernel/trace/trace_mmiotrace.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_mmiotrace.c +1 -1
@@ -109,7 +109,6 @@ static void mmio_pipe_open(struct trace_
 	iter->private = hiter;
 }
 
-/* XXX: This is not called when the pipe is closed! */
 static void mmio_close(struct trace_iterator *iter)
 {
 	struct header_iter *hiter = iter->private;
@@ -279,6 +278,7 @@ static struct tracer mmio_tracer __read_
 	.start		= mmio_trace_start,
 	.pipe_open	= mmio_pipe_open,
 	.close		= mmio_close,
+	.pipe_close	= mmio_close,
 	.read		= mmio_read,
 	.print_line	= mmio_print_line,
 	.noboot		= true,
author maintainer
#493 lore raw

PATCH 493/675 tracing: Fix union collision of module and refcnt for dynamic events

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Masami Hiramatsu (Google) <[email protected]>

commit b4eb07bde606c2096b24252be589e735eff6d413 upstream.

In 'struct trace_event_call', the 'module' pointer and the 'refcnt' atomic variable share the same memory space in a union. For dynamic events, the union member is 'refcnt', which acts as an active reference counter.

When a dynamic event (such as kprobe, uprobe, fprobe, eprobe, or wprobe) has a non-zero reference count (e.g. due to active event triggers or perf attachments), its 'call->module' evaluates to a small non-zero integer instead of NULL.

When filtering or setting events for a specific module (e.g., writing ':mod:<module>' to 'set_event'), the code in '__ftrace_set_clr_event_nolock()' and 'update_event_fields()' reads 'call->module' directly without checking whether the event is dynamic. This causes the kernel to treat the small integer (refcnt) as a 'struct module' pointer, leading to a NULL/invalid pointer dereference (Oops) when dereferencing the module name.

Fix this by ensuring that the 'TRACE_EVENT_FL_DYNAMIC' flag is checked before treating 'call->module' as a valid pointer in these code paths.

Fixes 4c86bc531e60 ("tracing: Add :mod: command to enabled module events")
Assisted-by Antigravity:gemini-3.5-flash
---
 kernel/trace/trace_events.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
kernel/trace/trace_events.c +3 -1
@@ -1333,7 +1333,9 @@ __ftrace_set_clr_event_nolock(struct tra
 		call = file->event_call;
 
 		/* If a module is specified, skip events that are not that module */
-		if (module && (!call->module || strcmp(module_name(call->module), module)))
+		if (module &&
+		    ((call->flags & TRACE_EVENT_FL_DYNAMIC) ||
+		     !call->module || strcmp(module_name(call->module), module)))
 			continue;
 
 		name = trace_event_name(call);
author maintainer
#494 lore raw

PATCH 494/675 tracing/eprobe: Fix exact system name matching in eprobe_dyn_event_match()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Masami Hiramatsu (Google) <[email protected]>

commit f418d68d71fd4a0a9cef92377bc8c4c3334b5b53 upstream.

eprobe_dyn_event_match() checks if the target event system in argv[0] matches ep->event_system using strncmp(ep->event_system, argv[0], len). However, if ep->event_system is longer than len (e.g. "eprobes" vs "ep/event"), strncmp() still returns 0 because the first len characters match.

Check that ep->event_system[len] is '\0' to ensure exact system name matching.

Fixes 7d5fda1c841f ("tracing: Fix event probe removal from dynamic events")
Assisted-by Antigravity:gemini-3.5-flash
---
 kernel/trace/trace_eprobe.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/trace/trace_eprobe.c +2 -1
@@ -169,7 +169,8 @@ static bool eprobe_dyn_event_match(const
 	if (!slash)
 		return false;
 
-	if (strncmp(ep->event_system, argv[0], slash - argv[0]))
+	if (strncmp(ep->event_system, argv[0], slash - argv[0]) ||
+	    ep->event_system[slash - argv[0]] != '\0')
 		return false;
 	if (strcmp(ep->event_name, slash + 1))
 		return false;
author maintainer
#495 lore raw

PATCH 495/675 tracing/probes: Avoid temporary buffer truncation in trace_probe_match_command_args()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Masami Hiramatsu (Google) <[email protected]>

commit 15f197856d68882af9416fc97516bb55079b7677 upstream.

In trace_probe_match_command_args(), a stack buffer buf[MAX_ARGSTR_LEN + 1] (256 bytes) is used to format "<name>=<comm>". However, since name can be up to 32 bytes (MAX_ARG_NAME_LEN) and comm up to 255 bytes (MAX_ARGSTR_LEN), the formatted string can exceed 256 bytes and get truncated by snprintf(), causing spurious argument matching failures.

Instead of formatting into a temporary buffer on stack, compare the argument name, the '=' delimiter, and the comm expression directly.

Fixes eb5bf81330a7 ("tracing/kprobe: Add per-probe delete from event")
Assisted-by Antigravity:gemini-3.5-flash
---
 kernel/trace/trace_probe.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
kernel/trace/trace_probe.c +5 -4
@@ -2344,16 +2344,17 @@ int trace_probe_compare_arg_type(struct
 bool trace_probe_match_command_args(struct trace_probe *tp,
 				    int argc, const char **argv)
 {
-	char buf[MAX_ARGSTR_LEN + 1];
 	int i;
 
 	if (tp->nr_args < argc)
 		return false;
 
 	for (i = 0; i < argc; i++) {
-		snprintf(buf, sizeof(buf), "%s=%s",
-			 tp->args[i].name, tp->args[i].comm);
-		if (strcmp(buf, argv[i]))
+		int len = strlen(tp->args[i].name);
+
+		if (strncmp(argv[i], tp->args[i].name, len) ||
+		    argv[i][len] != '=' ||
+		    strcmp(argv[i] + len + 1, tp->args[i].comm))
 			return false;
 	}
 	return true;
author maintainer
#496 lore raw

PATCH 496/675 tracing/probes: Fix potential underflow in LEN_OR_ZERO macro

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Masami Hiramatsu (Google) <[email protected]>

commit 8ce20bfba48902e1382187cd1a852f7cf3a1e739 upstream.

In __set_print_fmt(), LEN_OR_ZERO is defined as (len ? len - pos : 0). If len is non-zero but smaller than pos, len - pos evaluates to a negative integer. When passed as a size argument to snprintf(), this negative value is cast to a large unsigned size_t, bypassing buffer size limits.

Ensure len > pos before subtracting to avoid integer underflow.

Fixes 5bf652aaf46c ("tracing/probes: Integrate duplicate set_print_fmt()")
Assisted-by Antigravity:gemini-3.5-flash
---
 kernel/trace/trace_probe.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_probe.c +1 -1
@@ -2018,7 +2018,7 @@ int traceprobe_update_arg(struct probe_a
 }
 
 /* When len=0, we just calculate the needed length */
-#define LEN_OR_ZERO (len ? len - pos : 0)
+#define LEN_OR_ZERO (len > pos ? len - pos : 0)
 static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
 			   enum probe_print_type ptype)
 {
author maintainer
#497 lore raw

PATCH 497/675 tracing/probes: Prevent out-of-bounds write in __trace_probe_log_err()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Masami Hiramatsu (Google) <[email protected]>

commit a9d6fb284039a5d3858a1d9f9a0d7e46cfb7c2d4 upstream.

If trace_probe_log.argc is 0 in __trace_probe_log_err(), the loop constructing the command string will not execute and p will remain equal to command. Writing to *(p - 1) will cause an out-of-bounds access before command. This should not happen, but better to be treated.

Reject if trace_probe_log.argc is 0.

Fixes ab105a4fb894 ("tracing: Use tracing error_log with probe events")
Assisted-by Antigravity:gemini-3.5-flash
---
 kernel/trace/trace_probe.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_probe.c +1 -1
@@ -187,7 +187,7 @@ void __trace_probe_log_err(int offset, i
 
 	lockdep_assert_held(&dyn_event_ops_mutex);
 
-	if (!trace_probe_log.argv)
+	if (!trace_probe_log.argv || !trace_probe_log.argc)
 		return;
 
 	/* Recalculate the length and allocate buffer */
author maintainer
#498 lore raw

PATCH 498/675 arm64: make huge_ptep_get handled unaligned addresses

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Dev Jain <[email protected]>

commit f73a8edc2ccc6ec72c37d5c578e7592d2e1f9922 upstream.

huge_ptep_get() can be handed a virtual address pointing to the middle of a contpmd/contpte mapped hugetlb folio (examples of callers are pagemap_hugetlb_range, page_mapped_in_vma).

The arm64 helper rewalks the pgtables in find_num_contig to answer whether the huge pte we have maps a contpmd or a contpte hugetlb folio, and returns CONT_PMDS or CONT_PTES, so that it can collect a/d bits over the contiguous ptes. We can falsely return CONT_PTES instead of CONT_PMDS if the addr is not aligned. On systems where CONT_PTES != CONT_PMDS (meaning page size is 16K), we could collect excess A/D bit state, meaning extra work for the kernel. Even worse, we may iterate beyond the PTE table and dereference a garbage ptep pointer to access physical memory we don't own. Since the ptep pointer is a linear map address, we may run off the end of the linear map or into a hole, dereference a VA not mapped into the kernel pgtables and cause kernel panic.

Fix this by aligning the pmdp pointer down to a contpmd base before checking equality with the passed huge pte pointer, to correctly answer whether the huge pte is the base of a contpmd block.

Fixes 29cb80519689 ("arm64: hugetlb: Cleanup huge_pte size discovery mechanisms")
---
 arch/arm64/mm/hugetlbpage.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/mm/hugetlbpage.c +1 -1
@@ -91,7 +91,7 @@ static int find_num_contig(struct mm_str
 	p4dp = p4d_offset(pgdp, addr);
 	pudp = pud_offset(p4dp, addr);
 	pmdp = pmd_offset(pudp, addr);
-	if ((pte_t *)pmdp == ptep) {
+	if ((pte_t *)PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS) == ptep) {
 		*pgsize = PMD_SIZE;
 		return CONT_PMDS;
 	}
author maintainer
#499 lore raw

PATCH 499/675 arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Will Deacon <[email protected]>

commit e057b94772328221405b067c3a85fe479b915dc8 upstream.

When seccomp support was originally added to arm64 in a1ae65b21941 ("arm64: add seccomp support"), seccomp was erroneously called _before_ the ptrace syscall-enter-stop and therefore the tracer could trivially manipulate the syscall register state after the seccomp check had passed. This was subsequently fixed in a5cd110cb836 ("arm64/ptrace: run seccomp after ptrace") by moving the seccomp check after the tracer has run. Unfortunately, a decade later, that fix has been reported to be incomplete.

On arm64, both the first argument to a syscall and its eventual return value are allocated to register x0. In order to facilitate syscall restarting and querying of syscall arguments on the syscall exit path, the original value of x0 is stashed in 'struct pt_regs::orig_x0' early during the syscall entry path and is returned for the first argument by syscall_get_arguments(). Unlike 32-bit Arm, this stashed value is not directly exposed via ptrace() and so changes to register x0 made by the tracer on a syscall-enter-stop are not reflected in 'orig_x0'. This means that seccomp, syscall tracepoints and audit can observe a stale value for the register compared to the argument that will be observed by the actual syscall.

Re-sync 'orig_x0' from x0 on the syscall entry path following a potential ptrace stop (i.e. PTRACE_EVENTMSG_SYSCALL_ENTRY or SECCOMP_RET_TRACE). This behaviour is limited to native tasks (because compat tasks expose 'orig_r0' to ptrace) where the syscall is not being skipped (because x0 is updated to hold the return value of -ENOSYS in that case).

---
 arch/arm64/kernel/ptrace.c |   29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)
arch/arm64/kernel/ptrace.c +29 -0
@@ -2362,6 +2362,21 @@ static void report_syscall(struct pt_reg
 	}
 }
 
+static void update_syscall_orig_x0_after_ptrace(struct pt_regs *regs)
+{
+	/*
+	 * Keep orig_x0 authoritative so that seccomp (via
+	 * syscall_get_arguments()), audit and the restart path all see the same
+	 * first argument the syscall is dispatched with, even if it has been
+	 * updated by a tracer. Skip this for NO_SYSCALL (set either by the user
+	 * or the tracer), as regs[0] holds the return value (see the comment in
+	 * el0_svc_common()) and can be unwound using syscall_rollback().
+	 * For compat tasks, orig_r0 is provided directly through GPR index 17.
+	 */
+	if (!is_compat_task() && regs->syscallno != NO_SYSCALL)
+		regs->orig_x0 = regs->regs[0];
+}
+
 int syscall_trace_enter(struct pt_regs *regs)
 {
 	unsigned long flags = read_thread_flags();
@@ -2370,12 +2385,26 @@ int syscall_trace_enter(struct pt_regs *
 		report_syscall(regs, PTRACE_SYSCALL_ENTER);
 		if (flags & _TIF_SYSCALL_EMU)
 			return NO_SYSCALL;
+
+		/*
+		 * Ensure ptrace changes to x0 during a regular
+		 * syscall-enter-stop (PTRACE_SYSCALL) are visible to
+		 * subsequent seccomp checks, tracepoints and audit.
+		 */
+		update_syscall_orig_x0_after_ptrace(regs);
 	}
 
 	/* Do the secure computing after ptrace; failures should be fast. */
 	if (secure_computing() == -1)
 		return NO_SYSCALL;
 
+	/*
+	 * Ensure tracer changes to x0 during seccomp ptrace exit
+	 * processing (SECCOMP_RET_TRACE) are visible to tracepoints and
+	 * audit.
+	 */
+	update_syscall_orig_x0_after_ptrace(regs);
+
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, regs->syscallno);
 
author maintainer
#500 lore raw

PATCH 500/675 Revert "arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates"

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Will Deacon <[email protected]>

commit 26b483d52417253d88a3a01262ac85914a7aec8e upstream.

This reverts commit e057b94772328221405b067c3a85fe479b915dc8.

Sashiko points out that updating 'orig_x0' after secure_computing() has returned is too late to handle the case where a seccomp filter is re-evaluated after initially returning SECCOMP_RET_TRACE. This means that a tracer can manipulate the first argument of the syscall behind seccomp's back.

For now, revert the initial fix and we'll have another crack at it soon. Since the incorrect fix was cc'd to stable, do the same here with an appropriate fixes tag.

Fixes e057b9477232 ("arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates")
---
 arch/arm64/kernel/ptrace.c |   29 -----------------------------
 1 file changed, 29 deletions(-)
arch/arm64/kernel/ptrace.c +0 -29
@@ -2362,21 +2362,6 @@ static void report_syscall(struct pt_reg
 	}
 }
 
-static void update_syscall_orig_x0_after_ptrace(struct pt_regs *regs)
-{
-	/*
-	 * Keep orig_x0 authoritative so that seccomp (via
-	 * syscall_get_arguments()), audit and the restart path all see the same
-	 * first argument the syscall is dispatched with, even if it has been
-	 * updated by a tracer. Skip this for NO_SYSCALL (set either by the user
-	 * or the tracer), as regs[0] holds the return value (see the comment in
-	 * el0_svc_common()) and can be unwound using syscall_rollback().
-	 * For compat tasks, orig_r0 is provided directly through GPR index 17.
-	 */
-	if (!is_compat_task() && regs->syscallno != NO_SYSCALL)
-		regs->orig_x0 = regs->regs[0];
-}
-
 int syscall_trace_enter(struct pt_regs *regs)
 {
 	unsigned long flags = read_thread_flags();
@@ -2385,26 +2370,12 @@ int syscall_trace_enter(struct pt_regs *
 		report_syscall(regs, PTRACE_SYSCALL_ENTER);
 		if (flags & _TIF_SYSCALL_EMU)
 			return NO_SYSCALL;
-
-		/*
-		 * Ensure ptrace changes to x0 during a regular
-		 * syscall-enter-stop (PTRACE_SYSCALL) are visible to
-		 * subsequent seccomp checks, tracepoints and audit.
-		 */
-		update_syscall_orig_x0_after_ptrace(regs);
 	}
 
 	/* Do the secure computing after ptrace; failures should be fast. */
 	if (secure_computing() == -1)
 		return NO_SYSCALL;
 
-	/*
-	 * Ensure tracer changes to x0 during seccomp ptrace exit
-	 * processing (SECCOMP_RET_TRACE) are visible to tracepoints and
-	 * audit.
-	 */
-	update_syscall_orig_x0_after_ptrace(regs);
-
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, regs->syscallno);
 
author maintainer
#501 lore raw

PATCH 501/675 mptcp: decrement subflows counter on failed passive join

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chenguang Zhao <[email protected]>

commit f3ca0ee2cc308e33896536789cbc5f3a12ca7b30 upstream.

mptcp_pm_allow_new_subflow() increments extra_subflows before __mptcp_finish_join() on the passive MP_JOIN path.

In case of race conditions, the subflow is dropped without calling mptcp_close_ssk(), so the counter is not rolled back.

Call mptcp_pm_close_subflow() when the join completion fails to decrement the subflows counter.

---
 net/mptcp/protocol.c |    1 +
 1 file changed, 1 insertion(+)
net/mptcp/protocol.c +1 -0
@@ -3758,6 +3758,7 @@ bool mptcp_finish_join(struct sock *ssk)
 	mptcp_data_unlock(parent);
 
 	if (!ret) {
+		mptcp_pm_close_subflow(msk);
 err_prohibited:
 		subflow->reset_reason = MPTCP_RST_EPROHIBIT;
 		return false;
author maintainer
#502 lore raw

PATCH 502/675 mptcp: only set DATA_FIN when a mapping is present

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Michael Bommarito <[email protected]>

commit b2ff91b752b0d85e8815e7f44fd85205c4268094 upstream.

mptcp_get_options() clears only the status group of struct mptcp_options_received; data_seq, subflow_seq and data_len are filled in by mptcp_parse_option() exclusively inside the DSS mapping block, which runs only when the DSS M (mapping present) bit is set.

A peer can send a DSS option with the DATA_FIN flag set but the mapping bit clear. The parser then records mp_opt->data_fin while leaving data_len and data_seq uninitialized. For a zero-length segment mptcp_incoming_options() evaluates

	if (mp_opt.data_fin && mp_opt.data_len == 1 &&
	    mptcp_update_rcv_data_fin(msk, mp_opt.data_seq, mp_opt.dsn64))

which reads the uninitialized data_len and data_seq; KMSAN reports an uninit-value in mptcp_incoming_options(). The stale data_seq can also be fed into the receive-side DATA_FIN sequence tracking.

Record the DATA_FIN flag only when the DSS option carries a mapping, so data_fin is never set without data_seq and data_len also being present. data_fin is part of the status group that mptcp_get_options() clears up front, so on the no-map path it stays zero and the zero-length DATA_FIN branch is simply skipped. A DATA_FIN is always transmitted together with a mapping (mptcp_write_data_fin() sets use_map along with data_seq and data_len), so legitimate DATA_FIN handling is unaffected.

Move the pr_debug() that logs the parsed DSS flags below the mapping block, so it reports the final data_fin value instead of the stale one it would otherwise print before the assignment.

---
 net/mptcp/options.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
net/mptcp/options.c +6 -6
@@ -157,17 +157,11 @@ static void mptcp_parse_option(const str
 		ptr++;
 
 		flags = (*ptr++) & MPTCP_DSS_FLAG_MASK;
-		mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0;
 		mp_opt->dsn64 = (flags & MPTCP_DSS_DSN64) != 0;
 		mp_opt->use_map = (flags & MPTCP_DSS_HAS_MAP) != 0;
 		mp_opt->ack64 = (flags & MPTCP_DSS_ACK64) != 0;
 		mp_opt->use_ack = (flags & MPTCP_DSS_HAS_ACK);
 
-		pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n",
-			 mp_opt->data_fin, mp_opt->dsn64,
-			 mp_opt->use_map, mp_opt->ack64,
-			 mp_opt->use_ack);
-
 		expected_opsize = TCPOLEN_MPTCP_DSS_BASE;
 
 		if (mp_opt->use_ack) {
@@ -178,12 +172,18 @@ static void mptcp_parse_option(const str
 		}
 
 		if (mp_opt->use_map) {
+			mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0;
 			if (mp_opt->dsn64)
 				expected_opsize += TCPOLEN_MPTCP_DSS_MAP64;
 			else
 				expected_opsize += TCPOLEN_MPTCP_DSS_MAP32;
 		}
 
+		pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n",
+			 mp_opt->data_fin, mp_opt->dsn64,
+			 mp_opt->use_map, mp_opt->ack64,
+			 mp_opt->use_ack);
+
 		/* Always parse any csum presence combination, we will enforce
 		 * RFC 8684 Section 3.3.0 checks later in subflow_data_ready
 		 */
author maintainer
#503 lore raw

PATCH 503/675 mptcp: pm: userspace: fix use-after-free in get_local_id

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Geliang Tang <[email protected]>

commit 9bc6d5e4ca9f3cbb41d43400b3a31cb0403796c9 upstream.

In mptcp_pm_userspace_get_local_id(), the address entry is looked up under spinlock, but its id is read after dropping the lock. A concurrent deletion can free the entry between the unlock and the read, leading to UAF.

The race window is narrow. It was reproduced only with a locally constructed stress test that repeatedly overlaps an MP_JOIN SYN with a MPTCP_PM_CMD_SUBFLOW_DESTROY request.

However, the KASAN report below confirms that the race is reachable:

  [  666.319376] BUG: KASAN: slab-use-after-free in mptcp_userspace_pm_get_local_id+0x1dc/0x1f0
  [  666.319386] Read of size 1 at addr ffff888124845610 by task swapper/0/0
  ...
  [  666.319401] Call Trace:
  [  666.319405]  <IRQ>
  [  666.319408]  dump_stack_lvl+0x53/0x70
  [  666.319412]  print_address_description.constprop.0+0x2c/0x3b0
  [  666.319418]  print_report+0xbe/0x2b0
  [  666.319421]  ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0
  [  666.319423]  kasan_report+0xce/0x100
  [  666.319426]  ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0
  [  666.319429]  mptcp_userspace_pm_get_local_id+0x1dc/0x1f0
  [  666.319433]  mptcp_pm_get_local_id+0x371/0x440
  ...
  [  666.319821] Allocated by task 45539:
  [  666.319844]  kasan_save_stack+0x33/0x60
  [  666.319855]  kasan_save_track+0x14/0x30
  [  666.319858]  __kasan_kmalloc+0x8f/0xa0
  [  666.319863]  __kmalloc_noprof+0x1e7/0x520
  [  666.319867]  sock_kmalloc+0xdf/0x130
  [  666.319885]  sock_kmemdup+0x1b/0x40
  [  666.319888]  mptcp_userspace_pm_append_new_local_addr+0x261/0x500
  [  666.319910]  mptcp_pm_nl_announce_doit+0x16a/0x610
  ...
  [  666.319967] Freed by task 45560:
  [  666.319988]  kasan_save_stack+0x33/0x60
  [  666.319991]  kasan_save_track+0x14/0x30
  [  666.319994]  kasan_save_free_info+0x3b/0x60
  [  666.319998]  __kasan_slab_free+0x43/0x70
  [  666.320000]  kfree+0x166/0x440
  [  666.320003]  sock_kfree_s+0x1d/0x50
  [  666.320007]  mptcp_userspace_pm_delete_local_addr.isra.0+0x157/0x200
  [  666.320011]  mptcp_pm_nl_subflow_destroy_doit+0x51d/0xea0

Fix by copying the id into a local variable while still holding the lock, and use -1 as a "not found" sentinel.

---
 net/mptcp/pm_userspace.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
net/mptcp/pm_userspace.c +5 -2
@@ -132,12 +132,15 @@ int mptcp_userspace_pm_get_local_id(stru
 	__be16 msk_sport =  ((struct inet_sock *)
 			     inet_sk((struct sock *)msk))->inet_sport;
 	struct mptcp_pm_addr_entry *entry;
+	int id;
 
 	spin_lock_bh(&msk->pm.lock);
 	entry = mptcp_userspace_pm_lookup_addr(msk, &skc->addr);
+	id = entry ? entry->addr.id : -1;
 	spin_unlock_bh(&msk->pm.lock);
-	if (entry)
-		return entry->addr.id;
+
+	if (id != -1)
+		return id;
 
 	if (skc->addr.port == msk_sport)
 		skc->addr.port = 0;
author maintainer
#504 lore raw

PATCH 504/675 afs: Fix afs_edit_dir_remove() to get, not find, block 0

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: David Howells <[email protected]>

commit 62d9853aa4ce6e9797b6949804891be14b219752 upstream.

Fix afs_edit_dir_remove() to use afs_dir_get_block() to get block 0 rather than afs_dir_find_block() as the latter caches the found block in the afs_dir_iter and may[*] switch out the page it's on if another afs_dir_find_block() is done. This parallels what afs_edit_dir_add() does.

[*] There's more than one block per page.

---
 fs/afs/dir_edit.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/afs/dir_edit.c +1 -1
@@ -415,7 +415,7 @@ void afs_edit_dir_remove(struct afs_vnod
 	if (!afs_dir_init_iter(&iter, name))
 		return;
 
-	meta = afs_dir_find_block(&iter, 0);
+	meta = afs_dir_get_block(&iter, 0);
 	if (!meta)
 		return;
 
author maintainer
#505 lore raw

PATCH 505/675 mm/kmemleak: fix checksum computation for per-cpu objects

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Breno Leitao <[email protected]>

commit 79c37ae3733e93d9d8ea12ecb44f717e61439024 upstream.

The per-cpu object checksum folds each CPU's CRC together with XOR and seeds every CRC with 0. Both choices make update_checksum() miss content changes:

- XOR is self-cancelling, so equal contents on two CPUs cancel out and simultaneous identical changes leave the checksum unchanged.
- crc32(0, ...) over all-zero content is 0, so a freshly allocated, zeroed per-cpu area checksums to 0, matching the initial value, and the object is never seen to change.

See discussions at [0].

When update_checksum() wrongly reports an actively modified object as unchanged, kmemleak stops greying it for an extra scan and can report a live per-cpu object as a leak.

Fold the per-cpu CRC as a single rolling checksum across all CPUs and initialise the object checksum to ~0 so the first computed value always registers as a change, even for content that hashes to 0. reset_checksum() is seeded the same way.

---
 mm/kmemleak.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
mm/kmemleak.c +4 -3
@@ -676,7 +676,7 @@ static struct kmemleak_object *__alloc_o
 	atomic_set(&object->use_count, 1);
 	object->excess_ref = 0;
 	object->count = 0;			/* white color initially */
-	object->checksum = 0;
+	object->checksum = ~0;
 	object->del_state = 0;
 
 	/* task information */
@@ -972,7 +972,7 @@ static void reset_checksum(unsigned long
 	}
 
 	raw_spin_lock_irqsave(&object->lock, flags);
-	object->checksum = 0;
+	object->checksum = ~0;
 	raw_spin_unlock_irqrestore(&object->lock, flags);
 	put_object(object);
 }
@@ -1401,7 +1401,8 @@ static bool update_checksum(struct kmeml
 		for_each_possible_cpu(cpu) {
 			void *ptr = per_cpu_ptr((void __percpu *)object->pointer, cpu);
 
-			object->checksum ^= crc32(0, kasan_reset_tag((void *)ptr), object->size);
+			object->checksum = crc32(object->checksum,
+						 kasan_reset_tag((void *)ptr), object->size);
 		}
 	} else {
 		object->checksum = crc32(0, kasan_reset_tag((void *)object->pointer), object->size);
author maintainer
#506 lore raw

PATCH 506/675 mm/huge_memory: set PG_has_hwpoisoned only after new folio head is established

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Rik van Riel <[email protected]>

commit e1cd30eceb6908fc13bebce41283b885d71ee8d6 upstream.

__split_folio_to_order() copies the hwpoison state onto each new sub-folio while splitting a folio to a non-zero order. It does so via

	if (handle_hwpoison && page_range_has_hwpoisoned(new_head, new_nr_pages))
		folio_set_has_hwpoisoned(new_folio);

*before* clear_compound_head(new_head)/prep_compound_page(new_head, ...) turns @new_head from a tail page into a proper folio head.

PG_has_hwpoisoned is a FOLIO_SECOND_PAGE flag, so folio_set_has_hwpoisoned() resolves to folio_flags(folio, 1). With the new compound_info-based page-flags layout, folio_flags() asserts the page is not a tail:

	VM_BUG_ON_PGFLAGS(page->compound_info & 1, page);
	VM_BUG_ON_PGFLAGS(n > 0 && !test_bit(PG_head, &page->flags.f), page);

At the current call site @new_head still has the tail marker (compound_info bit 0 set, PG_head clear), so on CONFIG_DEBUG_VM kernels this hits:

kernel BUG at include/linux/page-flags.h:354 folio_flags+0x82
folio_set_has_hwpoisoned
__split_folio_to_order
__split_unmapped_folio
__folio_split
truncate_inode_partial_folio (shmem hole-punch / MADV_REMOVE)

Reproduced by syzkaller: hwpoison-inject a few subpages of a large shmem folio, then MADV_REMOVE (fallocate punch hole) on the same range, which splits the partial folio to a non-zero order.

memory_failure() tries to split the poisoned folio to order 0 first, but that split is best-effort; when it fails the folio is left large with PG_has_hwpoisoned set, the case fa5a06170036 added this hwpoison copying for.

Move the folio_set_has_hwpoisoned() call to after clear_compound_head()/prep_compound_page(), where @new_folio is a real order-new_order head folio (handle_hwpoison implies new_order != 0, so a second page always exists). The flag still lands on the same struct page (page[1] of the new folio); only the ordering relative to compound-head setup changes, satisfying the FOLIO_SECOND_PAGE precondition.

Fixes fa5a06170036 ("mm/huge_memory: preserve PG_has_hwpoisoned if a folio is split to >0 order")
Assisted-by Claude:claude-opus-4-8
Cc Barry Song <[email protected]>
Cc Dev Jain <[email protected]>
Cc Lance Yang <[email protected]>
Cc Liam R. Howlett <[email protected]>
Cc Nico Pache <[email protected]>
Cc Ryan Roberts <[email protected]>
Cc Yang Shi <[email protected]>
---
 mm/huge_memory.c |   12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)
mm/huge_memory.c +8 -4
@@ -3344,10 +3344,6 @@ static void __split_folio_to_order(struc
 				 (1L << PG_dirty) |
 				 LRU_GEN_MASK | LRU_REFS_MASK));
 
-		if (handle_hwpoison &&
-		    page_range_has_hwpoisoned(new_head, new_nr_pages))
-			folio_set_has_hwpoisoned(new_folio);
-
 		new_folio->mapping = folio->mapping;
 		new_folio->index = folio->index + i;
 
@@ -3378,6 +3374,14 @@ static void __split_folio_to_order(struc
 			folio_set_large_rmappable(new_folio);
 		}
 
+		/*
+		 * PG_has_hwpoisoned is on the 2nd page, so set it after
+		 * the compound head is prepped.
+		 */
+		if (handle_hwpoison &&
+		    page_range_has_hwpoisoned(new_head, new_nr_pages))
+			folio_set_has_hwpoisoned(new_folio);
+
 		if (folio_test_young(folio))
 			folio_set_young(new_folio);
 		if (folio_test_idle(folio))
author maintainer
#507 lore raw

PATCH 507/675 sctp: dont free the ASCONFs own transport in DEL-IP processing

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jun Yang <[email protected]>

commit 9b2854f86f0b56e9027d68e7a3fc909d1a9b566f upstream.

sctp_process_asconf() caches the transport the ASCONF chunk is processed against in asconf->transport (== chunk->transport, set once in sctp_rcv()). For an ASCONF located through its Address Parameter by __sctp_rcv_asconf_lookup(), that cached transport corresponds to the Address Parameter, which need not be the packet's source address.

sctp_process_asconf_param() rejects a DEL-IP for the packet source address (ADDIP D8, SCTP_ERROR_DEL_SRC_IP), but nothing protects asconf->transport. A single ASCONF can therefore carry, in order:

    [Address Parameter L] [DEL-IP L] [DEL-IP 0.0.0.0]

where L differs from the source. The DEL-IP for L passes the D8 check and calls sctp_assoc_rm_peer() on the transport that asconf->transport still points at, freeing it (RCU-deferred). The following wildcard DEL-IP then reuses the now-dangling asconf->transport in sctp_assoc_set_primary() and sctp_assoc_del_nonprimary_peers(): set_primary() dereferences the freed transport (->ipaddr, ->state) and plants the dangling pointer into asoc->peer.primary_path / active_path, and del_nonprimary_peers(), keeping only the pointer that is no longer on the list, removes every real transport, leaving the association with a transport_count of 0 and primary_path/active_path pointing at freed memory.

Reject a DEL-IP that targets the transport the ASCONF is being processed against, mirroring the existing source-address guard, so the wildcard branch can never reuse a freed transport.

Fixes 42e30bf3463c ("[SCTP]: Handle the wildcard ADD-IP Address parameter")
---
 net/sctp/sm_make_chunk.c |    6 ++++++
 1 file changed, 6 insertions(+)
net/sctp/sm_make_chunk.c +6 -0
@@ -3153,6 +3153,12 @@ static __be16 sctp_process_asconf_param(
 		if (!peer)
 			return SCTP_ERROR_DNS_FAILED;
 
+		/* Don't free asconf->transport; a later wildcard DEL-IP
+		 * parameter reuses it.
+		 */
+		if (peer == asconf->transport)
+			return SCTP_ERROR_REQ_REFUSED;
+
 		sctp_assoc_rm_peer(asoc, peer);
 		break;
 	case SCTP_PARAM_SET_PRIMARY:
author maintainer
#508 lore raw

PATCH 508/675 sctp: avoid auth_enable sysctl UAF during netns teardown

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhiling Zou <[email protected]>

commit f8d5e7846025f4ab15a461235f8ebae9094a361a upstream.

proc_sctp_do_auth() updates the SCTP control socket after changing net.sctp.auth_enable. The handler gets the per-net SCTP state from ctl->data, so an already opened sysctl file can still target a network namespace while that namespace is being torn down.

SCTP previously registered its per-net sysctls from sctp_defaults_init(), while the control socket is created later from sctp_ctrlsock_init(). This exposed a window during initialization where auth_enable was writable before net->sctp.ctl_sock existed, and a teardown window where auth_enable stayed writable after inet_ctl_sock_destroy() had released the control socket.

Move the per-net SCTP sysctl registration into sctp_ctrlsock_init() after sctp_ctl_sock_init() succeeds, and unregister the sysctl table before destroying the control socket in sctp_ctrlsock_exit(). If sysctl registration fails after the control socket was created, destroy the control socket in the same init path.

Make sctp_sysctl_net_unregister() tolerate a missing header and clear the saved pointer so init-error and exit paths can safely share the unregister helper.

---
 net/sctp/protocol.c |   20 ++++++++++++--------
 net/sctp/sysctl.c   |    9 +++++++--
 2 files changed, 19 insertions(+), 10 deletions(-)
net/sctp/protocol.c +12 -8
@@ -1410,10 +1410,6 @@ static int __net_init sctp_defaults_init
 	net->sctp.l3mdev_accept = 1;
 #endif
 
-	status = sctp_sysctl_net_register(net);
-	if (status)
-		goto err_sysctl_register;
-
 	/* Allocate and initialise sctp mibs.  */
 	status = init_sctp_mibs(net);
 	if (status)
@@ -1447,8 +1443,6 @@ err_init_proc:
 	cleanup_sctp_mibs(net);
 #endif
 err_init_mibs:
-	sctp_sysctl_net_unregister(net);
-err_sysctl_register:
 	return status;
 }
 
@@ -1463,7 +1457,6 @@ static void __net_exit sctp_defaults_exi
 	net->sctp.proc_net_sctp = NULL;
 #endif
 	cleanup_sctp_mibs(net);
-	sctp_sysctl_net_unregister(net);
 }
 
 static struct pernet_operations sctp_defaults_ops = {
@@ -1477,16 +1470,27 @@ static int __net_init sctp_ctrlsock_init
 
 	/* Initialize the control inode/socket for handling OOTB packets.  */
 	status = sctp_ctl_sock_init(net);
-	if (status)
+	if (status) {
 		pr_err("Failed to initialize the SCTP control sock\n");
+		return status;
+	}
+
+	status = sctp_sysctl_net_register(net);
+	if (status) {
+		inet_ctl_sock_destroy(net->sctp.ctl_sock);
+		net->sctp.ctl_sock = NULL;
+	}
 
 	return status;
 }
 
 static void __net_exit sctp_ctrlsock_exit(struct net *net)
 {
+	sctp_sysctl_net_unregister(net);
+
 	/* Free the control endpoint.  */
 	inet_ctl_sock_destroy(net->sctp.ctl_sock);
+	net->sctp.ctl_sock = NULL;
 }
 
 static struct pernet_operations sctp_ctrlsock_ops = {
net/sctp/sysctl.c +7 -2
@@ -615,11 +615,16 @@ int sctp_sysctl_net_register(struct net
 
 void sctp_sysctl_net_unregister(struct net *net)
 {
+	struct ctl_table_header *header = net->sctp.sysctl_header;
 	const struct ctl_table *table;
 
-	table = net->sctp.sysctl_header->ctl_table_arg;
-	unregister_net_sysctl_table(net->sctp.sysctl_header);
+	if (!header)
+		return;
+
+	table = header->ctl_table_arg;
+	unregister_net_sysctl_table(header);
 	kfree(table);
+	net->sctp.sysctl_header = NULL;
 }
 
 static struct ctl_table_header *sctp_sysctl_header;
author maintainer
#509 lore raw

PATCH 509/675 sctp: close UDP tunnel sockets during netns teardown

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhiling Zou <[email protected]>

commit ffb2bd7ade36ec4da32c46a6eddbf4515316d08c upstream.

proc_sctp_do_udp_port() starts per-net SCTP UDP tunneling sockets when net.sctp.udp_port is set, and stops/restarts them when the sysctl value changes. The netns exit path does not stop these sockets, so a namespace can be torn down while its SCTP UDP tunnel sockets are still installed.

Close the UDP tunnel sockets from sctp_ctrlsock_exit() after unregistering the per-net sysctl table. This prevents new sysctl writes from racing in while the sockets are being released, and closes the sockets before the control socket is destroyed.

---
 net/sctp/protocol.c |    1 +
 1 file changed, 1 insertion(+)
net/sctp/protocol.c +1 -0
@@ -1487,6 +1487,7 @@ static int __net_init sctp_ctrlsock_init
 static void __net_exit sctp_ctrlsock_exit(struct net *net)
 {
 	sctp_sysctl_net_unregister(net);
+	sctp_udp_sock_stop(net);
 
 	/* Free the control endpoint.  */
 	inet_ctl_sock_destroy(net->sctp.ctl_sock);
author maintainer
#510 lore raw

PATCH 510/675 ceph: add owner/capability checks for CEPH_IOC_SET_LAYOUT*

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Max Kellermann <[email protected]>

commit cee38bbf5556a8e0a232ccae41649580827d7806 upstream.

These permission checks were already missing in the initial impementation of these ioctls. This Ceph allows any user who owns a file descriptor to manipulate the layout of any file, even if they don't have write permissions.

It might be a good idea to guard other ioctls with permission checks as well or even disallow regular users (even if they own the file) to manipulate layout settings completely, as this may be abused to DoS the Ceph servers, but right now, I find it most urgent to have setter checks at all.

---
 fs/ceph/ioctl.c |    6 ++++++
 1 file changed, 6 insertions(+)
fs/ceph/ioctl.c +6 -0
@@ -72,6 +72,9 @@ static long ceph_ioctl_set_layout(struct
 	struct ceph_ioctl_layout nl;
 	int err;
 
+	if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
+		return -EACCES;
+
 	if (copy_from_user(&l, arg, sizeof(l)))
 		return -EFAULT;
 
@@ -142,6 +145,9 @@ static long ceph_ioctl_set_layout_policy
 	int err;
 	struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc;
 
+	if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
+		return -EACCES;
+
 	/* copy and validate */
 	if (copy_from_user(&l, arg, sizeof(l)))
 		return -EFAULT;
#511 lore raw

Re: [PATCH 6.18 510/675] ceph: add owner/capability checks for CEPH_IOC_SET_LAYOUT*

Quoted 45 lines · Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Max Kellermann <[email protected]>

commit cee38bbf5556a8e0a232ccae41649580827d7806 upstream.

These permission checks were already missing in the initial impementation of these ioctls. This Ceph allows any user who owns a file descriptor to manipulate the layout of any file, even if they don't have write permissions.

It might be a good idea to guard other ioctls with permission checks as well or even disallow regular users (even if they own the file) to manipulate layout settings completely, as this may be abused to DoS the Ceph servers, but right now, I find it most urgent to have setter checks at all.

---
 fs/ceph/ioctl.c |    6 ++++++
 1 file changed, 6 insertions(+)
fs/ceph/ioctl.c +3 -0
@@ -72,6 +72,9 @@ static long ceph_ioctl_set_layout(struct
        struct ceph_ioctl_layout nl;
        int err;
 
+       if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
+               return -EACCES;
+
        if (copy_from_user(&l, arg, sizeof(l)))
                return -EFAULT;
 

fs/ceph/ioctl.c static long ceph_ioctl_set_layout_policy · line 153

Quoted 9 lines
fs/ceph/ioctl.c +3 -0
@@ -142,6 +145,9 @@ static long ceph_ioctl_set_layout_policy
        int err;
        struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc;
 
+       if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
+               return -EACCES;
+
        /* copy and validate */
        if (copy_from_user(&l, arg, sizeof(l)))
                return -EFAULT;

Hi Greg,

There is a concern about the correctness of this patch, please drop it for now.

Thanks,

Ilya

author maintainer
#512 lore raw

PATCH 511/675 ceph: fix pre-auth out-of-bounds read on snaptrace in ceph_handle_caps()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit 4dbc71bcaf9a30abf3920a4e2cc4ed33bba78c02 upstream.

ceph_handle_caps() reads snap_trace_len from the wire-format ceph_mds_caps header and uses it unconditionally to build a fake end pointer (snaptrace + snaptrace_len) that is later handed to ceph_update_snap_trace() in the CEPH_CAP_OP_IMPORT case:

    snaptrace     = h + 1;
    snaptrace_len = le32_to_cpu(h->snap_trace_len);
    p             = snaptrace + snaptrace_len;
    ...
    case CEPH_CAP_OP_IMPORT:
        if (snaptrace_len) {
            ...
            if (ceph_update_snap_trace(mdsc, snaptrace,
                                       snaptrace + snaptrace_len,
                                       false, &realm)) { ... }

ceph_update_snap_trace() then decodes a struct ceph_mds_snap_realm from snaptrace using ceph_decode_need(&p, e, sizeof(*ri), bad) with the attacker-supplied fake end e == snaptrace + snaptrace_len. With snaptrace_len == 0xFFFFFFFF the bound check is trivially satisfied, ri = p reads sizeof(struct ceph_mds_snap_realm) past the legitimate msg->front buffer, and ri->num_snaps / ri->num_prior_parent_snaps then drive further out-of-bounds reads of the encoded snap arrays.

The eleven msg_version >= 2 .. msg_version >= 12 decoder blocks above the op switch each catch this OOB through their ceph_decode_*_safe() / ceph_decode_need() helpers, but they sit behind a hdr.version-gated if, so a malicious or compromised MDS that sets msg->hdr.version = 1 reaches the IMPORT path with no version-gated decoder having validated snap_trace_len. The shape has been present since ceph_handle_caps() was introduced.

Validate snap_trace_len against the message front buffer before consuming it, using the canonical ceph_decode_need() / ceph_has_room() helper. The helper bounds the length with subtraction (n <= end - p, guarded by end >= p) rather than pointer addition, so it is wrap-safe for the attacker-controlled u32 length on 32-bit builds where p + snap_trace_len could overflow the address space. This matches the rest of the ceph decode path (e.g. the pool_ns_len check a few lines below), and the existing goto bad cleanup already covers this exit path.

---
 fs/ceph/caps.c |    1 +
 1 file changed, 1 insertion(+)
fs/ceph/caps.c +1 -0
@@ -4364,6 +4364,7 @@ void ceph_handle_caps(struct ceph_mds_se
 
 	snaptrace = h + 1;
 	snaptrace_len = le32_to_cpu(h->snap_trace_len);
+	ceph_decode_need(&snaptrace, end, snaptrace_len, bad);
 	p = snaptrace + snaptrace_len;
 
 	if (msg_version >= 2) {
author maintainer
#513 lore raw

PATCH 512/675 ceph: fix refcount leak in ceph_readdir()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: WenTao Liang <[email protected]>

commit c3e64079d8b9663e3998d0caac9aba915b6b93ae upstream.

The ceph_readdir() function allocates a ceph_mds_request via ceph_mdsc_create_request() and stores it in dfi->last_readdir. In the directory entry processing loop, if the entry's offset is less than ctx->pos or if the inode pointer is unexpectedly NULL, the function returns -EIO without releasing the reference held by dfi->last_readdir, causing a refcount leak.

Fix this by adding ceph_mdsc_put_request(dfi->last_readdir) before returning on these error paths. Also set dfi->last_readdir to NULL for safety, matching the cleanup done at the normal exit.

Fixes af9ffa6df7e3 ("ceph: add support to readdir for encrypted names")
---
 fs/ceph/dir.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
fs/ceph/dir.c +6 -1
@@ -546,11 +546,16 @@ more:
 			pr_warn_client(cl,
 				"%p %llx.%llx rde->offset 0x%llx ctx->pos 0x%llx\n",
 				inode, ceph_vinop(inode), rde->offset, ctx->pos);
+			ceph_mdsc_put_request(dfi->last_readdir);
+			dfi->last_readdir = NULL;
 			return -EIO;
 		}
 
-		if (WARN_ON_ONCE(!rde->inode.in))
+		if (WARN_ON_ONCE(!rde->inode.in)) {
+			ceph_mdsc_put_request(dfi->last_readdir);
+			dfi->last_readdir = NULL;
 			return -EIO;
+		}
 
 		ctx->pos = rde->offset;
 		doutc(cl, "%p %llx.%llx (%d/%d) -> %llx '%.*s' %p\n", inode,
author maintainer
#514 lore raw

PATCH 513/675 ceph: fix writeback_count leak in write_folio_nounlock()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Wentao Liang <[email protected]>

commit cbf59617cd715219e84c50d106a3d0e1e8ba054e upstream.

write_folio_nounlock() increments fsc->writeback_count to track in-flight writeback operations. On several error paths where the function returns early (folio lookup failure, snapshot context allocation failure, and writepages submission failure), the function returns without calling atomic_long_dec_return() to decrement the counter.

Each leaked increment keeps the counter above zero, which can prevent the filesystem from cleanly unmounting or suspending writes.

Add atomic_long_dec_return() calls on all error paths that currently return without decrementing the counter.

Fixes d55207717ded ("ceph: add encryption support to writepage and writepages")
---
 fs/ceph/addr.c |    9 +++++++++
 1 file changed, 9 insertions(+)
fs/ceph/addr.c +9 -0
@@ -785,6 +785,9 @@ static int write_folio_nounlock(struct f
 				    ceph_wbc.truncate_size, true);
 	if (IS_ERR(req)) {
 		folio_redirty_for_writepage(wbc, folio);
+		if (atomic_long_dec_return(&fsc->writeback_count) <
+				CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
+			fsc->write_congested = false;
 		return PTR_ERR(req);
 	}
 
@@ -804,6 +807,9 @@ static int write_folio_nounlock(struct f
 			folio_redirty_for_writepage(wbc, folio);
 			folio_end_writeback(folio);
 			ceph_osdc_put_request(req);
+			if (atomic_long_dec_return(&fsc->writeback_count) <
+					CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
+				fsc->write_congested = false;
 			return PTR_ERR(bounce_page);
 		}
 	}
@@ -838,6 +844,9 @@ static int write_folio_nounlock(struct f
 			      ceph_vinop(inode), folio);
 			folio_redirty_for_writepage(wbc, folio);
 			folio_end_writeback(folio);
+			if (atomic_long_dec_return(&fsc->writeback_count) <
+					CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
+				fsc->write_congested = false;
 			return err;
 		}
 		if (err == -EBLOCKLISTED)
author maintainer
#515 lore raw

PATCH 514/675 libceph: bound get_version reply decode to front len

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Douya Le <[email protected]>

commit d3c32939fa0e3ee9b883b9a0fd1972c5c444e3d0 upstream.

handle_get_version_reply() uses msg->front_alloc_len as the decode boundary for MON_GET_VERSION_REPLY. That is the size of the reused reply buffer, not the number of bytes actually received.

A truncated reply can therefore pass ceph_decode_need() and decode the second u64 from stale tail bytes left in the buffer by an earlier message, causing an uninitialized memory read.

Use msg->front.iov_len as the receive-side decode boundary, matching other libceph reply handlers and limiting decoding to the bytes that were actually read from the wire.

Fixes 513a8243d67f ("libceph: mon_get_version request infrastructure")
Assisted-by Codex:GPT-5.4
---
 net/ceph/mon_client.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ceph/mon_client.c +1 -1
@@ -821,7 +821,7 @@ static void handle_get_version_reply(str
 	struct ceph_mon_generic_request *req;
 	u64 tid = le64_to_cpu(msg->hdr.tid);
 	void *p = msg->front.iov_base;
-	void *end = p + msg->front_alloc_len;
+	void *const end = p + msg->front.iov_len;
 	u64 handle;
 
 	dout("%s msg %p tid %llu\n", __func__, msg, tid);
author maintainer
#516 lore raw

PATCH 515/675 libceph: Fix multiplication overflow in decode_new_up_state_weight()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Raphael Zimmer <[email protected]>

commit 98917a499ec7064c14fc56d180a4fd636fc2784c upstream.

If a message of type CEPH_MSG_OSD_MAP contains a (maliciously) corrupted osdmap, out-of-bounds memory accesses may occur in decode_new_up_state_weight(). This happens because the bounds check for the new_state part is based on calculating its length depending on a len value read from the incoming message. This calculation may overflow leading to an incorrect bounds check. Subsequently, out-of-bounds reads may occur when decoding this part.

This patch switches the multiplication to use check_mul_overflow() to abort processing the osdmap if an overflow occurred. Therefore, osdmaps/messages containing large values for len that result in a multiplication overflow are treated as invalid.

[ idryomov: rename new_state_len -> new_state_item_size, formatting ]

Fixes 930c53286977 ("libceph: apply new_state before new_up_client on incrementals")
---
 net/ceph/osdmap.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
net/ceph/osdmap.c +4 -1
@@ -1844,6 +1844,8 @@ static int decode_new_up_state_weight(vo
 	void *new_up_client;
 	void *new_state;
 	void *new_weight_end;
+	const u32 new_state_item_size =
+	    sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8));
 	u32 len;
 	int ret;
 	int i;
@@ -1864,7 +1866,8 @@ static int decode_new_up_state_weight(vo
 
 	new_state = *p;
 	ceph_decode_32_safe(p, end, len, e_inval);
-	len *= sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8));
+	if (check_mul_overflow(len, new_state_item_size, &len))
+		goto e_inval;
 	ceph_decode_need(p, end, len, e_inval);
 	*p += len;
 
author maintainer
#517 lore raw

PATCH 516/675 libceph: guard missing CRUSH type name lookup

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhao Zhang <[email protected]>

commit bbeae12fda3384a90fbebc8a19ba9d33f85b5361 upstream.

Localized read selection can walk a parent bucket whose name exists in the CRUSH map while its type has no matching entry in type_names. get_immediate_parent() then dereferences a NULL type_cn and passes an invalid pointer into strcmp(), causing a null-ptr-deref.

Skip such malformed parent buckets unless both the bucket name and type name metadata are present. This keeps malformed hierarchy data from crashing locality lookup and safely falls back to "not local".

[ idryomov: add WARN_ON_ONCE ]

Fixes 117d96a04f00 ("libceph: support for balanced and localized reads")
Assisted-by Codex:GPT-5.4
---
 net/ceph/osdmap.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
net/ceph/osdmap.c +4 -1
@@ -3060,8 +3060,11 @@ static int get_immediate_parent(struct c
 			if (b->items[j] != id)
 				continue;
 
-			*parent_type_id = b->type;
 			type_cn = lookup_crush_name(&c->type_names, b->type);
+			if (WARN_ON_ONCE(!type_cn))
+				continue;
+
+			*parent_type_id = b->type;
 			parent_loc->cl_type_name = type_cn->cn_name;
 			parent_loc->cl_name = cn->cn_name;
 			return b->id;
author maintainer
#518 lore raw

PATCH 517/675 libceph: refresh auth->authorizer_buf{,_len} after authorizer update

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shuangpeng Bai <[email protected]>

commit 937d61f86d377a3aa578adae7a3dfcecdddf9d89 upstream.

ceph_x_create_authorizer() caches au->buf->vec.iov_base and au->buf->vec.iov_len in struct ceph_auth_handshake. These cached values are then used by the messenger connect code when sending the authorizer.

ceph_x_update_authorizer() can rebuild the authorizer when a newer service ticket is available. If the rebuilt authorizer no longer fits in the existing buffer, ceph_x_build_authorizer() drops its reference to au->buf and allocates a new one. If this is the final reference, ceph_buffer_put() frees the old ceph_buffer and its vec.iov_base, but auth->authorizer_buf still points at that freed memory.

A subsequent msgr1 reconnect can therefore queue the stale pointer and trigger a KASAN slab-use-after-free in _copy_from_iter() while tcp_sendmsg() copies the authorizer.

Refresh auth->authorizer_buf and auth->authorizer_buf_len after a successful authorizer rebuild so the messenger sends the current buffer.

---
 net/ceph/auth_x.c |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
net/ceph/auth_x.c +8 -1
@@ -781,9 +781,16 @@ static int ceph_x_update_authorizer(
 
 	au = (struct ceph_x_authorizer *)auth->authorizer;
 	if (au->secret_id < th->secret_id) {
+		int ret;
+
 		dout("ceph_x_update_authorizer service %u secret %llu < %llu\n",
 		     au->service, au->secret_id, th->secret_id);
-		return ceph_x_build_authorizer(ac, th, au);
+		ret = ceph_x_build_authorizer(ac, th, au);
+		if (ret)
+			return ret;
+
+		auth->authorizer_buf = au->buf->vec.iov_base;
+		auth->authorizer_buf_len = au->buf->vec.iov_len;
 	}
 	return 0;
 }
author maintainer
#519 lore raw

PATCH 518/675 libceph: Reject monmaps advertising zero monitors

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Raphael Zimmer <[email protected]>

commit 40480eee361ed9676b3f844d532ac28b47251634 upstream.

A message of type CEPH_MSG_MON_MAP contains a monmap that is sent from a monitor to the client. This monmap contains information about the existing monitors in the cluster. Currently, a monmap indicating that there are zero monitors in the cluster is treated as valid. However, it is impossible to have zero monitors in the cluster and still receive a valid monmap from a monitor. Therefore, such a monmap must be corrupted and should be treated as invalid. Furthermore, a monmap with a monitor count of zero can subsequently crash the client when attempting to open a session with a monitor in __open_session(). This happens because the "BUG_ON(monc->monmap->num_mon < 1)" assertion in pick_new_mon() is triggered.

This patch extends a check in ceph_monmap_decode() to also reject arriving mon_maps with num_mon == 0 rather than only with num_mon > CEPH_MAX_MON.

[ idryomov: drop "log output for unusual values of num_mon" part ]

---
 net/ceph/mon_client.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ceph/mon_client.c +1 -1
@@ -114,7 +114,7 @@ static struct ceph_monmap *ceph_monmap_d
 
 	dout("%s fsid %pU epoch %u num_mon %u\n", __func__, &fsid, epoch,
 	     num_mon);
-	if (num_mon > CEPH_MAX_MON)
+	if (num_mon == 0 || num_mon > CEPH_MAX_MON)
 		goto e_inval;
 
 	monmap = kmalloc(struct_size(monmap, mon_inst, num_mon), GFP_NOIO);
author maintainer
#520 lore raw

PATCH 519/675 libceph: reject zero bucket types in crush_decode

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Douya Le <[email protected]>

commit 05f90284223381005d6bcddab3fda4a97f9c3401 upstream.

CRUSH bucket type 0 is reserved for devices. The mapper relies on that invariant and uses type 0 to identify leaf devices.

If crush_decode() accepts a bucket with type 0, a malformed CRUSH map can make the mapper treat a negative bucket ID as a device and pass it to is_out(), which then indexes the OSD weight array with a negative value.

Reject zero bucket types while decoding the CRUSH map so the invalid state never reaches the mapper.

---
 net/ceph/osdmap.c |    2 ++
 1 file changed, 2 insertions(+)
net/ceph/osdmap.c +2 -0
@@ -520,6 +520,8 @@ static struct crush_map *crush_decode(vo
 		ceph_decode_need(p, end, 4*sizeof(u32), bad);
 		b->id = ceph_decode_32(p);
 		b->type = ceph_decode_16(p);
+		if (b->type == 0)
+			goto bad;
 		b->alg = ceph_decode_8(p);
 		if (b->alg != alg) {
 			b->alg = 0;
author maintainer
#521 lore raw

PATCH 520/675 libceph: remove debugfs files before client teardown

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Douya Le <[email protected]>

commit e4c804726c4afce3ba648b982d564f6af2cfa328 upstream.

ceph_destroy_client() tears down the monitor client before removing the per-client debugfs files. A concurrent read of the monmap debugfs file can enter monmap_show() after ceph_monc_stop() has freed monc->monmap, triggering a use-after-free.

Remove the debugfs files before stopping the OSD and monitor clients. debugfs_remove() drains active handlers and prevents new accesses, so the debugfs callbacks can no longer race the rest of client teardown.

---
 net/ceph/ceph_common.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/ceph/ceph_common.c +2 -2
@@ -763,13 +763,13 @@ void ceph_destroy_client(struct ceph_cli
 
 	atomic_set(&client->msgr.stopping, 1);
 
+	ceph_debugfs_client_cleanup(client);
+
 	/* unmount */
 	ceph_osdc_stop(&client->osdc);
 	ceph_monc_stop(&client->monc);
 	ceph_messenger_fini(&client->msgr);
 
-	ceph_debugfs_client_cleanup(client);
-
 	ceph_destroy_options(client->options);
 
 	kfree(client);
author maintainer
#522 lore raw

PATCH 521/675 amt: fix use-after-free in AMT delayed works

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shihuang Liu <[email protected]>

commit ea20c44935d6142daecfa9b39d635033a7553e1b upstream.

When an AMT device is removed, pending delayed works can still access the freed amt_dev structure, which may result in kernel crashes or memory corruption.

amt_dev_stop() cancels req_wq and discovery_wq with cancel_delayed_work_sync(), but these works can be scheduled again from event_wq after the cancellation. This allows delayed works to access the freed amt_dev structure after the netdev has been released.

The following is a simple race scenario:

CPU0 CPU1

amt_dev_stop()
cancel_delayed_work_sync()
amt_event_work() mod_delayed_work(req_wq)
free netdev
req_wq accesses freed amt_dev

Use disable_delayed_work_sync() in amt_dev_stop() to prevent req_wq and discovery_wq from being queued again and wait for running work items to complete.

The delayed works are disabled after initialization in amt_newlink() and enabled only when the device is successfully opened. This keeps the delayed work lifecycle synchronized with the lifetime of the AMT device.

---
 drivers/net/amt.c |   14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)
drivers/net/amt.c +11 -3
@@ -3034,9 +3034,15 @@ static int amt_dev_open(struct net_devic
 	amt->event_idx = 0;
 	amt->nr_events = 0;
 
+	enable_delayed_work(&amt->discovery_wq);
+	enable_delayed_work(&amt->req_wq);
+
 	err = amt_socket_create(amt);
-	if (err)
+	if (err) {
+		disable_delayed_work(&amt->req_wq);
+		disable_delayed_work(&amt->discovery_wq);
 		return err;
+	}
 
 	amt->req_cnt = 0;
 	amt->remote_ip = 0;
@@ -3062,8 +3068,8 @@ static int amt_dev_stop(struct net_devic
 	struct sk_buff *skb;
 	int i;
 
-	cancel_delayed_work_sync(&amt->req_wq);
-	cancel_delayed_work_sync(&amt->discovery_wq);
+	disable_delayed_work_sync(&amt->req_wq);
+	disable_delayed_work_sync(&amt->discovery_wq);
 	cancel_delayed_work_sync(&amt->secret_wq);
 
 	/* shutdown */
@@ -3317,6 +3323,8 @@ static int amt_newlink(struct net_device
 	INIT_DELAYED_WORK(&amt->req_wq, amt_req_work);
 	INIT_DELAYED_WORK(&amt->secret_wq, amt_secret_work);
 	INIT_WORK(&amt->event_wq, amt_event_work);
+	disable_delayed_work(&amt->req_wq);
+	disable_delayed_work(&amt->discovery_wq);
 	INIT_LIST_HEAD(&amt->tunnel_list);
 	return 0;
 err:
author maintainer
#523 lore raw

PATCH 522/675 ASoC: fsl: imx-card: Skip sysclk reset for active DAIs in shutdown

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chancel Liu <[email protected]>

commit 9f86aea992568c2b4db78c80ff9508af9e050ff7 upstream.

In a full-duplex setup, when one direction (playback or capture) is closed while the other is still running, imx_aif_shutdown() was unconditionally calling snd_soc_dai_set_sysclk() with rate=0 for all cpu/codec DAIs, which would disable the clock still needed by the active stream.

Add snd_soc_dai_active() checks before clearing sysclk so that only truly inactive DAIs have their clocks reset.

---
 sound/soc/fsl/imx-card.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)
sound/soc/fsl/imx-card.c +8 -4
index a4518fefad69..43438af1e1c6 100644
@@ -496,11 +496,15 @@ static void imx_aif_shutdown(struct snd_pcm_substream *substream)
 	struct snd_soc_dai *codec_dai;
 	int i;
 
-	for_each_rtd_cpu_dais(rtd, i, cpu_dai)
-		snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT);
+	for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+		if (!snd_soc_dai_active(cpu_dai))
+			snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT);
+	}
 
-	for_each_rtd_codec_dais(rtd, i, codec_dai)
-		snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN);
+	for_each_rtd_codec_dais(rtd, i, codec_dai) {
+		if (!snd_soc_dai_active(codec_dai))
+			snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN);
+	}
 }
 
 static const struct snd_soc_ops imx_aif_ops = {
Signature · 1 line
2.55.0
author maintainer
#524 lore raw

PATCH 523/675 ASoC: fsl_sai: Fix spurious BCLK on resume by clearing BYP

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chancel Liu <[email protected]>

commit d091132889c1378dd0944a72f86eae3e4da1e4fa upstream.

When the BCLK divider ratio is 1:1, fsl_sai_set_bclk() enables bypass mode by setting BYP, but never clears the bit. The BYP=1 value remains in the regcache, and is restored by regcache_sync() on the next runtime resume.

Since BYP=1 combined with BCD=1 immediately outputs the ungated MCLK as BCLK without waiting for BCE/TE/RE to be enabled, the clock is driven prematurely before the stream is fully configured, causing noise on some codecs.

Fix this by clearing BYP and BCI in fsl_sai_hw_free() taking into account sync mode and the opposite stream's state, so that the regcache holds BYP=0 before runtime suspend and regcache_sync() on resume will not restore bypass mode prematurely.

Fixes a50b7926d015 ("ASoC: fsl_sai: implement 1:1 bclk:mclk ratio support")
---
 sound/soc/fsl/fsl_sai.c |   29 +++++++++++++++++++++++++----
 1 file changed, 25 insertions(+), 4 deletions(-)
sound/soc/fsl/fsl_sai.c +25 -4
@@ -757,6 +757,8 @@ static int fsl_sai_hw_free(struct snd_pc
 	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
 	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 	unsigned int ofs = sai->soc_data->reg_offset;
+	int adir = tx ? RX : TX;
+	int dir  = tx ? TX : RX;
 
 	/* Clear xMR to avoid channel swap with mclk_with_tere enabled case */
 	regmap_write(sai->regmap, FSL_SAI_xMR(tx), 0);
@@ -764,10 +766,29 @@ static int fsl_sai_hw_free(struct snd_pc
 	regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
 			   FSL_SAI_CR3_TRCE_MASK, 0);
 
-	if (!sai->is_consumer_mode[tx] &&
-	    sai->mclk_streams & BIT(substream->stream)) {
-		clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
-		sai->mclk_streams &= ~BIT(substream->stream);
+	if (!sai->is_consumer_mode[tx]) {
+		bool adir_active = !!(sai->mclk_streams & BIT(!substream->stream));
+		/*
+		 * If opposite stream provides clocks for synchronous mode and
+		 * it is inactive, Clear BYP and BCI
+		 */
+		if (fsl_sai_dir_is_synced(sai, adir) && !adir_active)
+			regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs),
+					   FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0);
+		/*
+		 * Clear BYP and BCI of current stream if either of:
+		 * 1. current stream doesn't provide clocks for synchronous mode
+		 * 2. current stream provides clocks for synchronous mode but no
+		 *    more stream is active.
+		 */
+		if (!fsl_sai_dir_is_synced(sai, dir) || !adir_active)
+			regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
+					   FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0);
+
+		if (sai->mclk_streams & BIT(substream->stream)) {
+			clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
+			sai->mclk_streams &= ~BIT(substream->stream);
+		}
 	}
 
 	return 0;
author maintainer
#525 lore raw

PATCH 524/675 binfmt_elf_fdpic: only honour the first PT_INTERP

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Christian Brauner <[email protected]>

commit 3349ef6a366a61d631f6a263d12cea240957719d upstream.

The program header scan handles PT_INTERP from a switch nested in the scan loop, so its break leaves the switch and not the loop. A binary carrying more than one PT_INTERP runs the case again and overwrites both interpreter_name and interpreter. The previous name allocation leaks and so does the previous interpreter reference, along with the write denial open_exec() took on it. The denial is never released, so the file stays unwritable for as long as the system runs.

An unprivileged caller reaches this with a crafted binary and repeats it at will. binfmt_elf stops at the first PT_INTERP. Do the same here.

The flaw dates back to the driver's introduction in the pre-git history tree introduced in v2.6.11 by 91808d6ebe39 ("[PATCH] FRV: Add FDPIC ELF binary format driver").

---
 fs/binfmt_elf_fdpic.c |    4 ++++
 1 file changed, 4 insertions(+)
fs/binfmt_elf_fdpic.c +4 -0
@@ -231,6 +231,10 @@ static int load_elf_fdpic_binary(struct
 	for (i = 0; i < exec_params.hdr.e_phnum; i++, phdr++) {
 		switch (phdr->p_type) {
 		case PT_INTERP:
+			/* elf ABI allows only one interpreter */
+			if (interpreter_name)
+				continue;
+
 			retval = -ENOMEM;
 			if (phdr->p_filesz > PATH_MAX)
 				goto error;
author maintainer
#526 lore raw

PATCH 525/675 fs/super: fix emergency thaw double-unlock of s_umount

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chen Changcheng <[email protected]>

commit 503d67fbaec6fdeaba391cb497675071db9d16ea upstream.

do_thaw_all() iterates over all superblocks via __iterate_supers() with SUPER_ITER_EXCL, which acquires s_umount exclusively before calling the callback and releases it afterwards. However, the callback do_thaw_all_callback() calls thaw_super_locked() which unconditionally releases s_umount on every code path. This results in a second unlock attempt in __iterate_supers() that corrupts the rwsem state, triggering a DEBUG_RWSEMS warning:

[  182.601148] sysrq: Emergency Thaw of all frozen filesystems
[  182.601865] ------------[ cut here ]------------
[  182.602375] DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) && !rwsem_test_oflags(sem, RWSEM_NONSPINNABLE)): count = 0x0, magic = 0xffff99b1011e5870, owner = 0x0, curr 0xffff99b101b06c80, list not empty
[  182.603817] WARNING: kernel/locking/rwsem.c:1412 at up_write+0xa3/0x170, CPU#2: kworker/2:1/53
[  182.604578] Modules linked in:
[  182.604864] CPU: 2 UID: 0 PID: 53 Comm: kworker/2:1 Not tainted 7.2.0-rc4-00001-gbd3bd93ea98a-dirty #4 PREEMPT(lazy)
[  182.605711] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1kylin1 04/01/2014
[  182.606417] Workqueue: events do_thaw_all
[  182.606750] RIP: 0010:up_write+0xaf/0x170
[  182.607076] Code: 19 3a 92 48 0f 44 c2 48 8b 55 08 48 8b 55 00 4c 8b 45 08 48 8b 55 00 48 8d 3d ad 91 e0 01 48 8b 4d 20 50 48 c7 c6 f0 8c 26 92 <67> 48 0f b9 3a e8 d7 93 4e 00 58 eb 81 48 83 7f 18 00 48 c7 c2 8d
[  182.608563] RSP: 0018:ffffb670001d7e08 EFLAGS: 00010246
[  182.609007] RAX: ffffffff92349e8d RBX: 0000000000000000 RCX: ffff99b1011e5870
[  182.609595] RDX: 0000000000000000 RSI: ffffffff92268cf0 RDI: ffffffff92914d10
[  182.610283] RBP: ffff99b1011e5870 R08: 0000000000000000 R09: ffff99b101b06c80
[  182.610847] R10: ffff99b10139a808 R11: fefefefefefefeff R12: 0000000000000000
[  182.611414] R13: ffffffff90cf74d0 R14: 0000000000000000 R15: ffff99b1011e5800
[  182.612009] FS:  0000000000000000(0000) GS:ffff99b1eaaee000(0000) knlGS:0000000000000000
[  182.612670] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  182.613146] CR2: 00000000005c631c CR3: 00000000013ee000 CR4: 00000000000006f0
[  182.613722] Call Trace:
[  182.613946]  <TASK>
[  182.614130]  __iterate_supers+0x128/0x150
[  182.614463]  do_thaw_all+0x1b/0x30
[  182.614759]  process_scheduled_works+0xbb/0x3f0
[  182.615150]  ? __pfx_worker_thread+0x10/0x10
[  182.615499]  worker_thread+0x129/0x270
[  182.615816]  ? __pfx_worker_thread+0x10/0x10
[  182.616201]  kthread+0xe2/0x120
[  182.616469]  ? __pfx_kthread+0x10/0x10
[  182.616792]  ret_from_fork+0x15b/0x240
[  182.617115]  ? __pfx_kthread+0x10/0x10
[  182.617426]  ret_from_fork_asm+0x1a/0x30
[  182.617761]  </TASK>
[  182.617968] ---[ end trace 0000000000000000 ]---
[  182.618412] Emergency Thaw complete

Fix this by switching to SUPER_ITER_UNLOCKED and acquiring s_umount in the callback via super_lock_excl() before calling thaw_super_locked(). This matches the locking pattern expected by thaw_super_locked() and eliminates the double unlock.

While at it, remove the dead 'return;' at the end of do_thaw_all_callback().

---
 fs/super.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
fs/super.c +5 -2
@@ -1141,16 +1141,19 @@ void emergency_remount(void)
 
 static void do_thaw_all_callback(struct super_block *sb, void *unused)
 {
+	if (!super_lock_excl(sb))
+		return;
+
 	if (IS_ENABLED(CONFIG_BLOCK))
 		while (sb->s_bdev && !bdev_thaw(sb->s_bdev))
 			pr_warn("Emergency Thaw on %pg\n", sb->s_bdev);
+
 	thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL);
-	return;
 }
 
 static void do_thaw_all(struct work_struct *work)
 {
-	__iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_EXCL);
+	__iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_UNLOCKED);
 	kfree(work);
 	printk(KERN_WARNING "Emergency Thaw complete\n");
 }
author maintainer
#527 lore raw

PATCH 526/675 fs: preserve ACL_DONT_CACHE state in forget_cached_acl()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Amir Goldstein <[email protected]>

commit 4b9a5458d02e214ef2b384124ca626e3e381d778 upstream.

The ACL_DONT_CACHE state is meant to be a constant state for the inode for filesystems that want to opt out of posix acl caching.

Commit facd61053cff1 ("fuse: fixes after adapting to new posix acl api") used this facility to opt out of posix acl caching for fuse inodes with fuse server that does not negotiate FUSE_POSIX_ACL (fc->posix_acl).

The commit also takes care to gate the forget_all_cached_acls() call in fuse_set_acl() on fc->posix_acl because there is no need for it, but there are other placed in fuse code which call forget_all_cached_acls() unconditional to fc->posix_acl and those cause the loss of the ACL_DONT_CACHE state.

This is not only a functional bug. Properly timed, a get_acl() from this fuse filesystem can return a stale cached value, as was observed in tests, because set_acl() does not invalidate the unintentional acl cache.

We could fix this in fuse, but it actually makes no sense for the vfs helper forget_cached_acl() to invalidate the ACL_DONT_CACHE state, so let it not do that to fix fuse and future users of ACL_DONT_CACHE.

---
 fs/posix_acl.c |    7 +++++++
 1 file changed, 7 insertions(+)
fs/posix_acl.c +7 -0
@@ -93,6 +93,13 @@ static void __forget_cached_acl(struct p
 {
 	struct posix_acl *old;
 
+	/*
+	 * ACL_DONT_CACHE is expected to be a "const" value and xchg it with
+	 * ACL_NOT_CACHED would enable acl caching for the inode -
+	 * clearly not what the caller has intended.
+	 */
+	if (READ_ONCE(*p) == ACL_DONT_CACHE)
+		return;
 	old = xchg(p, ACL_NOT_CACHED);
 	if (!is_uncached_acl(old))
 		posix_acl_release(old);
author maintainer
#528 lore raw

PATCH 527/675 fscrypt: Add missing superblock check in find_or_insert_direct_key()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Biggers <[email protected]>

commit b5fa40226e71c17847b9ff2816c6ca4133d0d994 upstream.

The legacy 'fscrypt_direct_keys' table caches master keys that are used by v1 encryption policies that have FSCRYPT_POLICY_FLAG_DIRECT_KEY. It's just a global table for all filesystems (since the keys can be provided by the legacy process-subscribed keyrings mechanism, which makes it difficult to reuse super_block::s_master_keys).

The entries in it ('struct fscrypt_direct_key') do contain a super_block pointer, though, for passing to fscrypt_destroy_inline_crypt_key() when the last inode that references the key is evicted.

However, when finding the fscrypt_direct_key for an inode, we weren't actually comparing the super_block pointer. As a result, inodes with different super_blocks could point to the same fscrypt_direct_key. That could extend the lifetime of a fscrypt_direct_key beyond the super_block it points to, causing a use-after-free later.

Fix this by creating distinct fscrypt_direct_key structs for distinct super_block structs.

Note that this problem doesn't exist in the v2 policy equivalent ("per-mode keys"), since the data structures there are per super_block.

---
 fs/crypto/keysetup_v1.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
fs/crypto/keysetup_v1.c +7 -1
@@ -190,13 +190,19 @@ find_or_insert_direct_key(struct fscrypt
 		if (memcmp(ci->ci_policy.v1.master_key_descriptor,
 			   dk->dk_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0)
 			continue;
+		/* The sb is used at eviction time, so it must be the same. */
+		if (ci->ci_inode->i_sb != dk->dk_sb)
+			continue;
 		if (ci->ci_mode != dk->dk_mode)
 			continue;
 		if (!fscrypt_is_key_prepared(&dk->dk_key, ci))
 			continue;
 		if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize))
 			continue;
-		/* using existing tfm with same (descriptor, mode, raw_key) */
+		/*
+		 * Use an existing prepared key with the same (descriptor, sb,
+		 * mode, inlinecrypt, raw_key) combination.
+		 */
 		refcount_inc(&dk->dk_refcount);
 		spin_unlock(&fscrypt_direct_keys_lock);
 		free_direct_key(to_insert);
author maintainer
#529 lore raw

PATCH 528/675 ftrace: Add global mutex to serialize trace_parser access

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Tengda Wu <[email protected]>

commit 7720b63bcef3f54c7fe288774b720a227d54a306 upstream.

In ftrace, the trace_parser structure is allocated and initialized when a trace file is opened, and is subsequently used across write and release handlers to parse user input.

The affected handler paths and their specific functions are:
- Open paths: ftrace_regex_open(), ftrace_graph_open()
- Write paths: ftrace_regex_write(), ftrace_graph_write()
- Release paths: ftrace_regex_release(), ftrace_graph_release()

If userspace opens a trace file descriptor and shares it across multiple threads, concurrent write calls will race on the parser's internal state, specifically the 'idx', 'cont', and 'buffer' fields, leading to corrupted input or undefined behavior.

Fix this by adding a global mutex, parser_lock, to serialize all access to trace_parser across write and release paths, preventing concurrent corruption of parser state.

Fixes e704eff3ff51 ("ftrace: Have set_graph_function handle multiple functions in one write")
Fixes 689fd8b65d66 ("tracing: trace parser support for function and graph")
---
 kernel/trace/ftrace.c |   13 +++++++++++++
 1 file changed, 13 insertions(+)
kernel/trace/ftrace.c +13 -0
@@ -1073,6 +1073,12 @@ struct ftrace_ops global_ops = {
 };
 
 /*
+ * parser_lock - Protects trace_parser state against concurrent operations.
+ * Held across trace_get_user() and subsequent buffer parsing to prevent races.
+ */
+static DEFINE_MUTEX(parser_lock);
+
+/*
  * Used by the stack unwinder to know about dynamic ftrace trampolines.
  */
 struct ftrace_ops *ftrace_ops_trampoline(unsigned long addr)
@@ -5783,6 +5789,8 @@ ftrace_regex_write(struct file *file, co
 	/* iter->hash is a local copy, so we don't need regex_lock */
 
 	parser = &iter->parser;
+
+	guard(mutex)(&parser_lock);
 	read = trace_get_user(parser, ubuf, cnt, ppos);
 
 	if (read >= 0 && trace_parser_loaded(parser) &&
@@ -6551,12 +6559,14 @@ int ftrace_regex_release(struct inode *i
 		iter = file->private_data;
 
 	parser = &iter->parser;
+	mutex_lock(&parser_lock);
 	if (trace_parser_loaded(parser)) {
 		int enable = !(iter->flags & FTRACE_ITER_NOTRACE);
 
 		ftrace_process_regex(iter, parser->buffer,
 				     parser->idx, enable);
 	}
+	mutex_unlock(&parser_lock);
 
 	trace_parser_put(parser);
 
@@ -6888,10 +6898,12 @@ ftrace_graph_release(struct inode *inode
 
 		parser = &fgd->parser;
 
+		mutex_lock(&parser_lock);
 		if (trace_parser_loaded((parser))) {
 			ret = ftrace_graph_set_hash(fgd->new_hash,
 						    parser->buffer);
 		}
+		mutex_unlock(&parser_lock);
 
 		trace_parser_put(parser);
 
@@ -7004,6 +7016,7 @@ ftrace_graph_write(struct file *file, co
 
 	parser = &fgd->parser;
 
+	guard(mutex)(&parser_lock);
 	read = trace_get_user(parser, ubuf, cnt, ppos);
 
 	if (read >= 0 && trace_parser_loaded(parser) &&
author maintainer
#530 lore raw

PATCH 529/675 io_uring/rw: fix missing ERESTARTSYS conversion in read paths

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yitang Yang <[email protected]>

commit ab05caca123c6d0b41850b7c05b246e4dca4a770 upstream.

Both read and write may receive internal restart error codes from the filesystem layer and should be converted to -EINTR. However, when multishot read support was added, the error code normalization was lost for both io_read() and io_read_mshot().

Extract the conversion into io_fixup_restart_res() and apply it in all three locations: io_rw_done(), io_read(), and io_read_mshot().

Fixes a08d195b586a ("io_uring/rw: split io_read() into a helper")
---
 io_uring/rw.c |   42 +++++++++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 17 deletions(-)
io_uring/rw.c +25 -17
@@ -625,6 +625,24 @@ static void io_complete_rw_iopoll(struct
 	smp_store_release(&req->iopoll_completed, 1);
 }
 
+static inline ssize_t io_fixup_restart_res(ssize_t ret)
+{
+	switch (ret) {
+	case -ERESTARTSYS:
+	case -ERESTARTNOINTR:
+	case -ERESTARTNOHAND:
+	case -ERESTART_RESTARTBLOCK:
+		/*
+		 * We can't just restart the syscall, since previously
+		 * submitted sqes may already be in progress. Just fail
+		 * this IO with EINTR.
+		 */
+		return -EINTR;
+	default:
+		return ret;
+	}
+}
+
 static inline void io_rw_done(struct io_kiocb *req, ssize_t ret)
 {
 	struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
@@ -634,21 +652,8 @@ static inline void io_rw_done(struct io_
 		return;
 
 	/* transform internal restart error codes */
-	if (unlikely(ret < 0)) {
-		switch (ret) {
-		case -ERESTARTSYS:
-		case -ERESTARTNOINTR:
-		case -ERESTARTNOHAND:
-		case -ERESTART_RESTARTBLOCK:
-			/*
-			 * We can't just restart the syscall, since previously
-			 * submitted sqes may already be in progress. Just fail
-			 * this IO with EINTR.
-			 */
-			ret = -EINTR;
-			break;
-		}
-	}
+	if (unlikely(ret < 0))
+		ret = io_fixup_restart_res(ret);
 
 	if (req->ctx->flags & IORING_SETUP_IOPOLL)
 		io_complete_rw_iopoll(&rw->kiocb, ret);
@@ -1044,7 +1049,8 @@ int io_read(struct io_kiocb *req, unsign
 
 	if (req->flags & REQ_F_BUFFERS_COMMIT)
 		io_kbuf_recycle(req, sel.buf_list, issue_flags);
-	return ret;
+
+	return io_fixup_restart_res(ret);
 }
 
 int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags)
@@ -1078,8 +1084,10 @@ int io_read_mshot(struct io_kiocb *req,
 		return IOU_RETRY;
 	} else if (ret <= 0) {
 		io_kbuf_recycle(req, sel.buf_list, issue_flags);
-		if (ret < 0)
+		if (ret < 0) {
+			ret = io_fixup_restart_res(ret);
 			req_set_fail(req);
+		}
 	} else if (!(req->flags & REQ_F_APOLL_MULTISHOT)) {
 		cflags = io_put_kbuf(req, ret, sel.buf_list);
 	} else {