Skip to contentSkip to conversation

7.1.8-rc1 review

PATCH In review Greg Kroah-Hartman 18 participants 457 messages Aug 7 – Aug 10 Page 3 of 4

Subsystems (3)
lore
Share & tools
lore
author maintainer
#263 lore raw

PATCH 263/438 ata: libata-eh: Increase STANDBY IMMEDIATE timeout

In reply to Greg Kroah-Hartman

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

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

From: Matt Vollrath <[email protected]>

commit 1e024d2b41ee32bc06818f7f09a3562c58842cf9 upstream.

Correct a previous change (see Fixes) which reduced the standby timeout from 30 to 5 seconds. Increase it to 15 seconds.

I was troubleshooting an error spotted during system suspend:

    [ 1217.152867] ata1.00: Entering standby power mode
    [ 1222.322948] ata1.00: qc timeout after 5000 msecs (cmd 0xe0)
    [ 1222.324010] ata1.00: STANDBY IMMEDIATE failed (err_mask=0x4)

This drive is a Samsung 870 EVO SSD in good SMART standing, and I wasn't aware of any reason it should be taking so long to standby. The issue is intermittent, but I observed it sometimes taking 7 seconds to manually standby. I assume this was interruption of background maintenance after a power outage.

As a desktop user, I would prefer to wait the extra 2 seconds at suspend to let the drive finish its business rather than drop the rails from under it.

The change from 30 to 5 seconds was implicit when switching suspend from START STOP UNIT to an internal command with no timeout table entry. No reason was stated for the change.

Fixes aa3998dbeb3a ("ata: libata-scsi: Disable scsi device manage_system_start_stop")
Assisted-by Claude:claude-5-fable
---
 drivers/ata/libata-eh.c |    8 ++++++++
 include/linux/libata.h  |    2 +-
 2 files changed, 9 insertions(+), 1 deletion(-)
drivers/ata/libata-eh.c +8 -0
@@ -106,6 +106,12 @@ static const unsigned int ata_eh_flush_t
 	UINT_MAX,
 };
 
+static const unsigned int ata_eh_standby_timeouts[] = {
+	15000,	/* Some drives may be slow to standby */
+	/* but don't hold up a suspend too long waiting for them */
+	UINT_MAX,
+};
+
 static const unsigned int ata_eh_other_timeouts[] = {
 	 5000,	/* same rationale as identify timeout */
 	10000,	/* ditto */
@@ -147,6 +153,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIME
 	  .timeouts = ata_eh_other_timeouts, },
 	{ .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT),
 	  .timeouts = ata_eh_flush_timeouts },
+	{ .commands = CMDS(ATA_CMD_STANDBYNOW1),
+	  .timeouts = ata_eh_standby_timeouts },
 	{ .commands = CMDS(ATA_CMD_VERIFY),
 	  .timeouts = ata_eh_reset_timeouts },
 };
include/linux/libata.h +1 -1
@@ -425,7 +425,7 @@ enum {
 	/* This should match the actual table size of
 	 * ata_eh_cmd_timeout_table in libata-eh.c.
 	 */
-	ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 8,
+	ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 9,
 
 	/* User visible DMA mask for DMA control. DO NOT renumber. */
 	ATA_DMA_MASK_ATA	= (1 << 0),	/* DMA on ATA Disk */
author maintainer
#264 lore raw

PATCH 264/438 ata: libata-sata: fix ata_scsi_lpm_supported() iteration

In reply to Greg Kroah-Hartman

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

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

From: Niklas Cassel <[email protected]>

commit 3fd70e96914d761c17c376aadd0b0d1a3c9badba upstream.

The inner loop of ata_scsi_lpm_supported() uses the wrong variable when iterating.

It should obviously use the link that we are currently iterating over, rather than always using the host link.

ata_scsi_lpm_supported() is used to control if a user should be allowed to change lpm policy (from the default) via sysfs.

Thus, this bug could potentially disallow users to change the LPM policy for certain SATA devices via sysfs.

Fixes 0060beec0bfa ("ata: libata-sata: Add link_power_management_supported sysfs attribute")
---
 drivers/ata/libata-sata.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/ata/libata-sata.c +1 -1
@@ -913,7 +913,7 @@ static bool ata_scsi_lpm_supported(struc
 		return false;
 
 	ata_for_each_link(link, ap, EDGE) {
-		ata_for_each_dev(dev, &ap->link, ENABLED) {
+		ata_for_each_dev(dev, link, ENABLED) {
 			if (dev->quirks & ATA_QUIRK_NOLPM)
 				return false;
 		}
author maintainer
#265 lore raw

PATCH 265/438 ata: libata-scsi: terminate deferred commands on time out

In reply to Greg Kroah-Hartman

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

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

From: Damien Le Moal <[email protected]>

commit 2e1d2e65e773d67dab163127f11a47dab0fbca9f upstream.

If a command times out while we have deferred non-NCQ commands waiting to be issued, the SCSI EH task is not immediately woken up as the waiting deferred commands are never issued nor completed, thus leaving the SCSI host in a busy state (shost->host_failed != scsi_host_busy(shost)) which prevents the SCSI EH task from being woken up. Eventually, when the deferred commands also time out, the SCSI EH task is woken up and the timeout processing occurs.

Avoid this unnecessary SCSI EH task wake-up additional time by scheduling a retry of all waiting deferred QCs, using the eh_timed_out SCSI host template operation. The function ata_scsi_eh_timed_out() is introduced to implement this operation.

However, terminating deferred commands with DID_REQUEUE to force a retry by calling the function ata_scsi_requeue_deferred_qc() may still keep the SCSI host in a busy state because the block layer may immediately re-issue these commands. The solution to this is to schedule libata EH for the port which suffered the command timeout to prevent accepting any new command. ata_scsi_requeue_deferred_qc() is modified to add a call to ata_port_schedule_eh() for this purpose.

In addition to this change, ata_scsi_requeue_deferred_qc() is also modified to take a new timedout_scmd scsi command argument which indicates the SCSI command that timed out. With this additional argument, ata_scsi_requeue_deferred_qc() can now also terminate with DID_TIME_OUT any timed out deferred qc, which simplifies ata_scsi_cmd_error_handler(). In this case, ata_scsi_requeue_deferred_qc() returns SCSI_EH_DONE, with this return value propagated back to the ata_scsi_eh_timed_out() operation to indicate to scsi_timeout() that the timed out command was handled and no further processing is needed.

For non-timed out deferred qc that need to be retried, ata_scsi_requeue_deferred_qc() returns SCSI_EH_NOT_HANDLED, thus indicating to scsi_timeout() that the timed out command needs to go through the SCSI EH (and libata EH) processing by adding it to the EH work queue with scsi_eh_scmd_add().

One side effect of these changes is that the function atapi_qc_complete() needs to be modified to ensure that a deferred ATAPI command that needs to be retried is completed with DID_REQUEUE instead of the default SAM_STAT_GOOD status, and a command that timed out is completed with DID_TIME_OUT instead of SAM_STAT_CHECK_CONDITION.

---
 drivers/ata/libata-eh.c   |   29 ++---------
 drivers/ata/libata-scsi.c |  112 +++++++++++++++++++++++++++++++++++++++++-----
 drivers/ata/libata.h      |    3 -
 include/linux/libata.h    |    2 
 4 files changed, 111 insertions(+), 35 deletions(-)
drivers/ata/libata-eh.c +6 -23
@@ -656,29 +656,12 @@ int ata_scsi_cmd_error_handler(struct Sc
 		set_host_byte(scmd, DID_OK);
 
 		ata_qc_for_each_raw(ap, qc, i) {
-			if (qc->scsicmd != scmd)
-				continue;
-			if ((qc->flags & ATA_QCFLAG_ACTIVE) ||
-			    qc == qc->dev->link->deferred_qc)
+			if (qc->scsicmd == scmd &&
+			    qc->flags & ATA_QCFLAG_ACTIVE)
 				break;
 		}
 
-		if (i < ATA_MAX_QUEUE && qc == qc->dev->link->deferred_qc) {
-			/*
-			 * This is a deferred command that timed out while
-			 * waiting for the command queue to drain. Since the qc
-			 * is not active yet (deferred_qc is still set, so the
-			 * deferred qc work has not issued the command yet),
-			 * simply signal the timeout by finishing the SCSI
-			 * command and clear the deferred qc to prevent the
-			 * deferred qc work from issuing this qc.
-			 */
-			WARN_ON_ONCE(qc->flags & ATA_QCFLAG_ACTIVE);
-			qc->dev->link->deferred_qc = NULL;
-			cancel_work(&qc->dev->link->deferred_qc_work);
-			set_host_byte(scmd, DID_TIME_OUT);
-			scsi_eh_finish_cmd(scmd, &ap->eh_done_q);
-		} else if (i < ATA_MAX_QUEUE) {
+		if (i < ATA_MAX_QUEUE) {
 			/* the scmd has an associated qc */
 			if (!(qc->flags & ATA_QCFLAG_EH)) {
 				/* which hasn't failed yet, timeout */
@@ -954,10 +937,10 @@ static void ata_eh_set_pending(struct at
 	ap->pflags |= ATA_PFLAG_EH_PENDING;
 
 	/*
-	 * If we have a deferred qc, requeue it so that it is retried once EH
-	 * completes.
+	 * If we have deferred QCs, requeue them so that the SCSI EH task can
+	 * run.
 	 */
-	ata_scsi_requeue_deferred_qc(ap);
+	ata_scsi_requeue_deferred_qc(ap, NULL);
 
 	if (!fastdrain)
 		return;
drivers/ata/libata-scsi.c +101 -11

147 lines not on this page · raw message

drivers/ata/libata.h +2 -1
@@ -177,7 +177,8 @@ int ata_scsi_dev_config(struct scsi_devi
 enum scsi_qc_status __ata_scsi_queuecmd(struct scsi_cmnd *scmd,
 					struct ata_device *dev);
 void ata_scsi_deferred_qc_work(struct work_struct *work);
-void ata_scsi_requeue_deferred_qc(struct ata_port *ap);
+enum scsi_timeout_action ata_scsi_requeue_deferred_qc(struct ata_port *ap,
+						      struct scsi_cmnd *scmd);
 
 /* libata-eh.c */
 extern unsigned int ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd);
include/linux/libata.h +2 -0
@@ -1152,6 +1152,7 @@ extern int ata_scsi_ioctl(struct scsi_de
 #endif
 extern enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *h,
 					     struct scsi_cmnd *cmd);
+enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *cmd);
 #if IS_REACHABLE(CONFIG_ATA)
 bool ata_scsi_dma_need_drain(struct request *rq);
 #else
@@ -1461,6 +1462,7 @@ extern const struct attribute_group *ata
 	.ioctl			= ata_scsi_ioctl,		\
 	ATA_SCSI_COMPAT_IOCTL					\
 	.queuecommand		= ata_scsi_queuecmd,		\
+	.eh_timed_out		= ata_scsi_eh_timed_out,	\
 	.dma_need_drain		= ata_scsi_dma_need_drain,	\
 	.this_id		= ATA_SHT_THIS_ID,		\
 	.emulated		= ATA_SHT_EMULATED,		\
author maintainer
#266 lore raw

PATCH 266/438 ata: libata-scsi: schedule deferred atapi command

In reply to Greg Kroah-Hartman

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

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

From: Damien Le Moal <[email protected]>

commit e7468b3f9b404ac7c1329ef07c146c3356dfbd01 upstream.

Modify atapi_qc_complete() to call ata_scsi_schedule_deferred_qc() to ensure that any deferred queued command can execute. This is similar to ata_scsi_qc_complete() function for regular ATA devices.

Fixes 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation")
---
 drivers/ata/libata-scsi.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/ata/libata-scsi.c +6 -2
@@ -2991,6 +2991,7 @@ static void atapi_fixup_inquiry(struct s
 
 static void atapi_qc_complete(struct ata_queued_cmd *qc)
 {
+	struct ata_link *link = qc->dev->link;
 	struct scsi_cmnd *cmd = qc->scsicmd;
 	unsigned int err_mask = qc->err_mask;
 
@@ -3020,12 +3021,12 @@ static void atapi_qc_complete(struct ata
 			ata_scsi_qc_done(qc, false, 0);
 		else
 			ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION);
-		return;
+		goto schedule_deferred;
 	}
 
 	if (cmd->result) {
 		ata_scsi_qc_done(qc, false, 0);
-		return;
+		goto schedule_deferred;
 	}
 
 	/* successful completion path */
@@ -3033,6 +3034,9 @@ static void atapi_qc_complete(struct ata
 		atapi_fixup_inquiry(cmd);
 
 	ata_scsi_qc_done(qc, true, SAM_STAT_GOOD);
+
+schedule_deferred:
+	ata_scsi_schedule_deferred_qc(link);
 }
 /**
  *	atapi_xlat - Initialize PACKET taskfile
author maintainer
#267 lore raw

PATCH 267/438 ALSA: usb-audio: fix use-after-free in ump_to_endpoint()

In reply to Greg Kroah-Hartman

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

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

From: Baul Lee <[email protected]>

commit 4a05b2d1b4642df74f30b6f54843e825c4a2bfd3 upstream.

create_midi2_ump() registers a card-owned snd_ump_endpoint and stores a back-pointer to its per-interface snd_usb_midi2_ump object in ump->private_data, but it never installs an ump->private_free hook and never clears that pointer.

If a later step of snd_usb_midi_v2_create() fails, its error path calls free_all_midi2_umps(), which kfree()s the snd_usb_midi2_ump object while the already-registered endpoint keeps pointing at it. The created /dev/snd/umpC*D* node stays exposed, so the first operation of any UMP open, ump_to_endpoint(), dereferences the dangling ump->private_data and reads rmidi->eps[dir] out of freed memory.

A malicious USB MIDI 2.0 device that makes creation fail after the endpoint is registered can thus trigger a slab use-after-free read on a subsequent open of the UMP node.

Clear the endpoint's back-pointer before freeing the object, and let ump_to_endpoint() tolerate a NULL private_data so the open/close/trigger callbacks fail cleanly (their callers already handle a NULL endpoint) instead of dereferencing a stale pointer.

Discovered by XBOW, triaged by Baul Lee <[email protected]>

---
 sound/usb/midi2.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
sound/usb/midi2.c +3 -1
@@ -329,7 +329,7 @@ ump_to_endpoint(struct snd_ump_endpoint
 {
 	struct snd_usb_midi2_ump *rmidi = ump->private_data;
 
-	return rmidi->eps[dir];
+	return rmidi ? rmidi->eps[dir] : NULL;
 }
 
 /* ump open callback */
@@ -685,6 +685,8 @@ static void free_all_midi2_umps(struct s
 		rmidi = list_first_entry(&umidi->rawmidi_list,
 					 struct snd_usb_midi2_ump, list);
 		list_del(&rmidi->list);
+		if (rmidi->ump)
+			rmidi->ump->private_data = NULL;
 		kfree(rmidi);
 	}
 }
author maintainer
#268 lore raw

PATCH 268/438 ALSA: usb-audio: fix stack info leak in RME Digiface status

In reply to Greg Kroah-Hartman

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

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

From: Baul Lee <[email protected]>

commit 441aaad150c57edaf57ee482a79a3bf4c5b7e353 upstream.

snd_rme_digiface_read_status() reads a four-word status block from the device into an uninitialised on-stack __le32 buf[4] and, whenever the vendor control-IN transfer does not return a negative error, copies all four words into the caller's status[].

snd_usb_ctl_msg() copies the full requested size back into the caller's buffer regardless of how many bytes the data stage actually delivered:

	buf = kmemdup(data, size, GFP_KERNEL);
	err = usb_control_msg(dev, pipe, request, requesttype,
			      value, index, buf, size, timeout);
	memcpy(data, buf, size);

usb_control_msg() returns the transferred length on a short control-IN, which is a non-negative value, and writes only that many bytes. The remainder of the copy back is the kmemdup()ed image of the caller's buffer, so a device answering with a short data stage leaves the trailing words of buf[] holding leftover kernel stack. The only guard in the caller is err < 0, so those words are stored into status[].

They then reach user space: snd_rme_digiface_get_status_val() selects a 16-bit halfword of status[] per the control's reg/mask, and the eight Digiface status controls together expose the whole 16-byte frame to an unprivileged reader of /dev/snd/controlC*.

Zero-initialise the buffer so a short read yields zeros instead of stack residue. This mirrors snd_rme_get_status1(), which already clears its output word before the same kind of vendor read.

Discovered by XBOW, triaged by Baul Lee <[email protected]>

---
 sound/usb/mixer_quirks.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/usb/mixer_quirks.c +1 -1
@@ -3454,7 +3454,7 @@ static int snd_rme_digiface_read_status(
 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
 	struct snd_usb_audio *chip = list->mixer->chip;
 	struct usb_device *dev = chip->dev;
-	__le32 buf[4];
+	__le32 buf[4] = {};
 	int err;
 
 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
author maintainer
#269 lore raw

PATCH 269/438 ALSA: usb-audio: fix OOB write in snd_usbmidi_akai_output()

In reply to Greg Kroah-Hartman

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

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

From: Baul Lee <[email protected]>

commit 0970274613fb463d376211450cab066d34ebfe6a upstream.

snd_usbmidi_akai_output() computes its fill-loop bound

	buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1;

as a signed int, so a small device-advertised bulk-OUT max_transfer makes buf_end negative. The loop guard then compares the u32 urb->transfer_buffer_length against that negative int: the usual arithmetic conversion turns buf_end into a large unsigned value, so the guard stays true and each iteration keeps appending SysEx framing and payload bytes past the end of the URB transfer buffer, which is only max_transfer bytes long.

A USB device that advertises a tiny bulk-OUT endpoint can therefore trigger an attacker-length- and content-controlled heap out-of-bounds write when a process writes to the created /dev/snd/midiC*D* node.

Return early when there is no room for even one SysEx, so the loop is never entered with a bound that would wrap. The loop is the last statement of the function, so bailing out is equivalent to it not running.

Discovered by XBOW, triaged by Baul Lee <[email protected]>

---
 sound/usb/midi.c |    2 ++
 1 file changed, 2 insertions(+)
sound/usb/midi.c +2 -0
@@ -797,6 +797,8 @@ static void snd_usbmidi_akai_output(stru
 
 	msg = urb->transfer_buffer + urb->transfer_buffer_length;
 	buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1;
+	if (buf_end <= 0)
+		return;
 
 	/* only try adding more data when there's space for at least 1 SysEx */
 	while (urb->transfer_buffer_length < buf_end) {
author maintainer
#270 lore raw

PATCH 270/438 ALSA: usb-audio: Fix DMA buffer out-of-bounds write when fill_max is set

In reply to Greg Kroah-Hartman

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

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

From: Sonali Pradhan <[email protected]>

commit d0199ae1666ff9ae2d1d568d64c3430d4c47f0e5 upstream.

When a USB audio endpoint requests full packet transfers via the fill_max descriptor flag, data_ep_set_params() promotes ep->curpacksize to ep->maxpacksize. However, maxsize is left at the original sample-rate derived value.

Since u->buffer_size is allocated as maxsize * packets, the resulting DMA buffer is far too small for the requested transfer length. When the USB host controller streams up to curpacksize bytes per packet, it writes past the end of the buffer via DMA, corrupting kernel heap memory.

Update maxsize to curpacksize when fill_max is set so that the allocated DMA buffer size matches the actual transfer request size.

[ changed to reassign maxsize only when ep->fill_max is set -- tiwai ]

Fixes 8fdff6a319e7 ("ALSA: snd-usb: implement new endpoint streaming model")
Assisted-by Jetski:Gemini-3.6-Flash
---
 sound/usb/endpoint.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
sound/usb/endpoint.c +4 -2
@@ -1168,10 +1168,12 @@ static int data_ep_set_params(struct snd
 				<< (16 - ep->datainterval);
 	}
 
-	if (ep->fill_max)
+	if (ep->fill_max) {
 		ep->curpacksize = ep->maxpacksize;
-	else
+		maxsize = ep->curpacksize;
+	} else {
 		ep->curpacksize = maxsize;
+	}
 
 	if (snd_usb_get_speed(chip->dev) != USB_SPEED_FULL) {
 		packs_per_ms = 8 >> ep->datainterval;
author maintainer
#271 lore raw

PATCH 271/438 ALSA: usb-audio: Clamp frame size in implicit-feedback mode

In reply to Greg Kroah-Hartman

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

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

From: Sonali Pradhan <[email protected]>

commit 8d7a30c50c2e58a6839634ed0acde14466d1dc61 upstream.

snd_usb_handle_sync_urb() scales received sync packet sizes by the sender's stride and stores the result directly in out_packet->packet_size[i]. If a connected USB device sends an oversized sync packet, this frame count can exceed ep->maxframesize.

The un-clamped frame count then propagates to the playback endpoint queue, potentially driving packet transfers beyond the endpoint's hardware frame limits.

Cap the calculated frame count against ep->maxframesize in snd_usb_handle_sync_urb() to prevent oversized packets from entering the playback queue.

Fixes 28acb12014fb ("ALSA: usb-audio: use sender stride for implicit feedback")
Assisted-by Jetski:Gemini-3.6-Flash
---
 sound/usb/endpoint.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
sound/usb/endpoint.c +5 -3
@@ -1819,11 +1819,13 @@ static void snd_usb_handle_sync_urb(stru
 
 		out_packet->packets = in_ctx->packets;
 		for (i = 0; i < in_ctx->packets; i++) {
-			if (urb->iso_frame_desc[i].status == 0)
-				out_packet->packet_size[i] =
+			if (urb->iso_frame_desc[i].status == 0) {
+				unsigned int frames =
 					urb->iso_frame_desc[i].actual_length / sender->stride;
-			else
+				out_packet->packet_size[i] = min(frames, ep->maxframesize);
+			} else {
 				out_packet->packet_size[i] = 0;
+			}
 		}
 
 		spin_unlock_irqrestore(&ep->lock, flags);
author maintainer
#272 lore raw

PATCH 272/438 dmaengine: qcom: bam_dma: Fix command element mask field for BAM v1.6.0+

In reply to Greg Kroah-Hartman

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

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

From: Md Sadre Alam <[email protected]>

commit 867621ba203027338b525af6729719c544135336 upstream.

BAM version 1.6.0 and later changed the behavior of the mask field in command elements for read operations.

In older BAM versions, or prior implementation assumptions, the mask field was effectively ignored for read commands. However, starting from BAM v1.6.0, the mask field for read commands is repurposed to carry the upper 4 bits of the destination address, enabling support for 36-bit addressing. For write commands, the mask field continues to function as a traditional write mask.

The current driver sets mask = 0xffffffff for all command elements. While this works for write operations, it breaks read operations on BAM v1.6.0+ hardware. In such cases, the hardware interprets the upper address bits as 0xf, resulting in an invalid destination address (0xf_xxxxxxxx instead of 0x0_xxxxxxxx).

This leads to failures such as NAND enumeration issues observed on platforms like IPQ5424.

Fix this by assigning the mask field based on command type:
- For read commands: set mask = 0 (upper address bits = 0)
- For write commands: retain mask = 0xffffffff

Also update the bam_cmd_element structure documentation to reflect the dual purpose of the mask field across BAM versions.

This ensures correct behavior on BAM v1.6.0+ while maintaining backward compatibility with older hardware.

---
 include/linux/dma/qcom_bam_dma.h |   21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)
include/linux/dma/qcom_bam_dma.h +16 -5
@@ -13,9 +13,12 @@
  * supported by BAM DMA Engine.
  *
  * @cmd_and_addr - upper 8 bits command and lower 24 bits register address.
- * @data - for write command: content to be written into peripheral register.
- *	   for read command: dest addr to write peripheral register value.
- * @mask - register mask.
+ * @data - For write command: content to be written into peripheral register.
+ *	   For read command: lower 32 bits of destination address.
+ * @mask - For write command: register write mask.
+ *	   For read command on BAM v1.6.0+: upper 4 bits of destination address.
+ *	   For read command on BAM < v1.6.0: ignored by hardware.
+ *	   Setting to 0 ensures 32-bit addressing compatibility.
  * @reserved - for future usage.
  *
  */
@@ -42,6 +45,10 @@ enum bam_command_type {
  * @addr: target address
  * @cmd: BAM command
  * @data: actual data for write and dest addr for read in le32
+ *
+ * For BAM v1.6.0+, the mask field behavior depends on command type:
+ * - Write commands: mask = write mask (typically 0xffffffff)
+ * - Read commands: mask = upper 4 bits of destination address (0 for 32-bit)
  */
 static inline void
 bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr,
@@ -50,7 +57,11 @@ bam_prep_ce_le32(struct bam_cmd_element
 	bam_ce->cmd_and_addr =
 		cpu_to_le32((addr & 0xffffff) | ((cmd & 0xff) << 24));
 	bam_ce->data = data;
-	bam_ce->mask = cpu_to_le32(0xffffffff);
+	if (cmd == BAM_READ_COMMAND)
+		bam_ce->mask = cpu_to_le32(0x0); /* 32-bit addressing */
+	else
+		bam_ce->mask = cpu_to_le32(0xffffffff); /* Write mask */
+	bam_ce->reserved = 0;
 }
 
 /*
@@ -60,7 +71,7 @@ bam_prep_ce_le32(struct bam_cmd_element
  * @bam_ce: BAM command element
  * @addr: target address
  * @cmd: BAM command
- * @data: actual data for write and dest addr for read
+ * @data: actual data for write and destination address for read
  */
 static inline void
 bam_prep_ce(struct bam_cmd_element *bam_ce, u32 addr,
author maintainer
#273 lore raw

PATCH 273/438 e1000: fix memory leak in e1000_probe()

In reply to Greg Kroah-Hartman

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

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

From: Dawei Feng <[email protected]>

commit 816419dfea5c88126f35eb7a1b429a1bf546665e upstream.

In the e1000_probe() path, e1000_sw_init() allocates adapter->tx_ring and adapter->rx_ring. If the subsequent CE4100-specific MDIO BAR mapping fails, the error handling jumps past the ring cleanup code, leaking both allocations.

Fix this leak by moving the err_mdio_ioremap label above the ring deallocation logic. This guarantees the proper release of these resources and prevents the memory leak.

The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc6.

An x86_64 allyesconfig build showed no new warnings. As we do not have a CE4100 reference platform to test with, no runtime testing was able to be performed.

Fixes 5377a4160bb65 ("e1000: Add support for the CE4100 reference platform")
---
 drivers/net/ethernet/intel/e1000/e1000_main.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/intel/e1000/e1000_main.c +1 -1
@@ -1222,11 +1222,11 @@ err_eeprom:
 
 	if (hw->flash_address)
 		iounmap(hw->flash_address);
+err_mdio_ioremap:
 	kfree(adapter->tx_ring);
 	kfree(adapter->rx_ring);
 err_dma:
 err_sw_init:
-err_mdio_ioremap:
 	iounmap(hw->ce4100_gbe_mdio_base_virt);
 	iounmap(hw->hw_addr);
 err_ioremap:
author maintainer
#274 lore raw

PATCH 274/438 fou: Fix use-after-free in fou_create()

In reply to Greg Kroah-Hartman

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

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

From: Xuanqiang Luo <[email protected]>

commit b14361aca6350ff7907b0e9903c7b94dc7d5d4a0 upstream.

fou_create() publishes struct fou through sk_user_data before adding the new FOU port to the per-netns list. If fou_add_to_port_list() fails, the error path frees fou while it is still reachable through sk_user_data. A concurrent receive can then dereference the freed object in fou_from_sock().

This ordering issue was previously noted in the linked discussion.

The failure is reachable when local port 0 is requested. Each socket binds to a different ephemeral port, but fou_cfg_cmp() compares the requested port 0 and reports -EALREADY once an entry already exists.

Release the tunnel socket before freeing fou so sk_user_data is cleared first, and defer reclamation with kfree_rcu() to protect concurrent RCU readers. This matches the lifetime handling in fou_release().

---
 net/ipv4/fou_core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/fou_core.c +1 -1
@@ -629,9 +629,9 @@ static int fou_create(struct net *net, s
 	return 0;
 
 error:
-	kfree(fou);
 	if (sock)
 		udp_tunnel_sock_release(sock->sk);
+	kfree_rcu(fou, rcu);
 
 	return err;
 }
author maintainer
#275 lore raw

PATCH 275/438 igbvf: Fix leak in TX DMA error cleanup

In reply to Greg Kroah-Hartman

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

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

From: Matt Vollrath <[email protected]>

commit 0565052b7e2f436b7f1541f4849da96dc0aa7a0e upstream.

If an error is encountered while mapping TX buffers, the driver should unmap any buffers already mapped for that skb.

Because count is incremented before each frag mapping, it will always match the correct number of unmappings needed when dma_error is reached. Decrementing count before the while loop in dma_error causes an off-by-one error. If any mapping was successful before an unsuccessful mapping, exactly one DMA mapping (the head) would leak.

This bug was introduced by a 2010 fix for an endless loop in dma_error. All other affected drivers have already been fixed.

Fixes c1fa347f20f1 ("e1000/e1000e/igb/igbvf/ixgb/ixgbe: Fix tests of unsigned in *_tx_map()")
Assisted-by Claude:claude-4-7-opus
---
 drivers/net/ethernet/intel/igbvf/netdev.c |    2 --
 1 file changed, 2 deletions(-)
drivers/net/ethernet/intel/igbvf/netdev.c +0 -2
@@ -2190,8 +2190,6 @@ dma_error:
 	buffer_info->time_stamp = 0;
 	buffer_info->length = 0;
 	buffer_info->mapped_as_page = false;
-	if (count)
-		count--;
 
 	/* clear timestamp and dma mappings for remaining portion of packet */
 	while (count--) {
author maintainer
#276 lore raw

PATCH 276/438 igc: remove napi_synchronize() in igc_down()

In reply to Greg Kroah-Hartman

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

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

From: David Carlier <[email protected]>

commit 5ffab5b9589c50e4cfc0cf36ffd76c89422d4019 upstream.

When an AF_XDP zero-copy application is killed abruptly, the XSK pool is torn down but NAPI keeps polling. igc_clean_rx_irq_zc() then returns the full budget on every poll, so napi_complete_done() never clears NAPI_STATE_SCHED.

igc_down() calls napi_synchronize() before napi_disable(), so it spins forever waiting for that bit and the interface never goes down. Drop the napi_synchronize() and let napi_disable() do the job -- it sets NAPI_STATE_DISABLE, which forces the stuck poll to complete. Reorder it ahead of igc_set_queue_napi() so the NAPI mapping is cleared only after polling has stopped, matching the recent igb fix b1e067240379.

---
 drivers/net/ethernet/intel/igc/igc_main.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/net/ethernet/intel/igc/igc_main.c +1 -2
@@ -5352,9 +5352,8 @@ void igc_down(struct igc_adapter *adapte
 
 	for (i = 0; i < adapter->num_q_vectors; i++) {
 		if (adapter->q_vector[i]) {
-			napi_synchronize(&adapter->q_vector[i]->napi);
-			igc_set_queue_napi(adapter, i, NULL);
 			napi_disable(&adapter->q_vector[i]->napi);
+			igc_set_queue_napi(adapter, i, NULL);
 		}
 	}
 
author maintainer
#277 lore raw

PATCH 277/438 ipvs: do not propagate one-packet flag to synced conns

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit a63d2dbaeb50a85d4c976b15a36e6b0c7113db5b upstream.

Synced connections can be created before their destination exists. When the destination is later added, ip_vs_bind_dest() copies connection flags from the destination into cp->flags.

IP_VS_CONN_F_ONE_PACKET connections are not synced. If a synced connection inherits IP_VS_CONN_F_ONE_PACKET while it is already hashed, expiry can treat it as a one-packet connection and skip unlinking the existing conn_tab node, leaving stale hash nodes pointing at a freed struct ip_vs_conn.

Drop IP_VS_CONN_F_ONE_PACKET from destination flags when binding synced connections.

---
 net/netfilter/ipvs/ip_vs_conn.c |    3 +++
 1 file changed, 3 insertions(+)
net/netfilter/ipvs/ip_vs_conn.c +3 -0
@@ -1110,6 +1110,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, s
 	flags = cp->flags;
 	/* Bind with the destination and its corresponding transmitter */
 	if (flags & IP_VS_CONN_F_SYNC) {
+		/* Synced conns are hashed, so they can not get this flag */
+		conn_flags &= ~IP_VS_CONN_F_ONE_PACKET;
+
 		/* if the connection is not template and is created
 		 * by sync, preserve the activity flag.
 		 */
author maintainer
#278 lore raw

PATCH 278/438 ksmbd: reject repeated SMB2 NEGOTIATE requests

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

commit cb469993b3a61a72653770856d37af616d72d05f upstream.

Unauthenticated client can send multiple successful SMB2 NEGOTIATE requests on one connection before SESSION_SETUP. While the connection is in KSMBD_SESS_NEED_SETUP, smb2_handle_negotiate() accepts another SMB3.1.1 NEGOTIATE and overwrites conn->preauth_info with a new allocation. Only the final allocation is freed when the connection is released, leaking one object for every additional successful request.

A repeated SMB2 NEGOTIATE after a dialect has been selected is a protocol violation. MS-SMB2 section 3.3.5.4 requires the server to disconnect without replying in this case. Set the connection exiting when rejecting the request, in addition to suppressing the response.

Reject SMB2 NEGOTIATE unless the connection is new or is waiting for the SMB2 NEGOTIATE that follows an SMB1 multi-protocol negotiate. Serialize both SMB1 and SMB2 negotiation paths under conn->srv_mutex, since they update connection-wide dialect and negotiation state.

Move the locking contract to ksmbd_smb_negotiate_common(), where the state and dialect are selected, and add ksmbd_conn_new() for consistent state access.

Fixes e2f34481b24d ("cifsd: add server-side procedures for SMB3")
---
 fs/smb/server/connection.h |    5 +++++
 fs/smb/server/smb2pdu.c    |   10 ++--------
 fs/smb/server/smb_common.c |   37 ++++++++++++++++++++++++++++++-------
 3 files changed, 37 insertions(+), 15 deletions(-)
fs/smb/server/connection.h +5 -0
@@ -197,6 +197,11 @@ void ksmbd_conn_r_count_dec(struct ksmbd
  * This is a hack. We will move status to a proper place once we land
  * a multi-sessions support.
  */
+static inline bool ksmbd_conn_new(struct ksmbd_conn *conn)
+{
+	return READ_ONCE(conn->status) == KSMBD_SESS_NEW;
+}
+
 static inline bool ksmbd_conn_good(struct ksmbd_conn *conn)
 {
 	return READ_ONCE(conn->status) == KSMBD_SESS_GOOD;
fs/smb/server/smb2pdu.c +2 -8
@@ -1143,6 +1143,8 @@ static __le32 deassemble_neg_contexts(st
  * smb2_handle_negotiate() - handler for smb2 negotiate command
  * @work:	smb work containing smb request buffer
  *
+ * The caller holds conn->srv_mutex.
+ *
  * Return:      0
  */
 int smb2_handle_negotiate(struct ksmbd_work *work)
@@ -1156,13 +1158,6 @@ int smb2_handle_negotiate(struct ksmbd_w
 
 	ksmbd_debug(SMB, "Received negotiate request\n");
 	conn->need_neg = false;
-	if (ksmbd_conn_good(conn)) {
-		pr_err("conn->tcp_status is already in CifsGood State\n");
-		work->send_no_response = 1;
-		return rc;
-	}
-
-	ksmbd_conn_lock(conn);
 	smb2_buf_len = get_rfc1002_len(work->request_buf);
 	smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects);
 	if (smb2_neg_size > smb2_buf_len) {
@@ -1313,7 +1308,6 @@ int smb2_handle_negotiate(struct ksmbd_w
 	ksmbd_conn_set_need_setup(conn);
 
 err_out:
-	ksmbd_conn_unlock(conn);
 	if (rc)
 		rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
 
fs/smb/server/smb_common.c +30 -7
@@ -616,23 +616,46 @@ int ksmbd_smb_negotiate_common(struct ks
 	struct ksmbd_conn *conn = work->conn;
 	int ret;
 
-	conn->dialect =
-		ksmbd_negotiate_smb_dialect(work->request_buf);
-	ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
-
 	if (command == SMB2_NEGOTIATE_HE) {
+		/*
+		 * An SMB2 NEGOTIATE is valid for a new connection, or after an
+		 * SMB1 multi-protocol negotiate has selected SMB2. Do not allow
+		 * a second SMB2 NEGOTIATE to replace connection-wide state
+		 * while a session setup is pending. KSMBD_SESS_NEED_RECONNECT
+		 * is a transient session state and does not restart transport
+		 * negotiation.
+		 */
+		ksmbd_conn_lock(conn);
+		if (!ksmbd_conn_new(conn) &&
+		    !ksmbd_conn_need_negotiate(conn)) {
+			work->send_no_response = 1;
+			ksmbd_conn_set_exiting(conn);
+			ksmbd_conn_unlock(conn);
+			return 0;
+		}
+
+		conn->dialect =
+			ksmbd_negotiate_smb_dialect(work->request_buf);
+		ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
 		ret = smb2_handle_negotiate(work);
+		ksmbd_conn_unlock(conn);
 		return ret;
 	}
 
 	if (command == SMB_COM_NEGOTIATE) {
+		ksmbd_conn_lock(conn);
+		conn->dialect =
+			ksmbd_negotiate_smb_dialect(work->request_buf);
+		ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
 		if (__smb2_negotiate(conn)) {
 			init_smb3_11_server(conn);
-			init_smb2_neg_rsp(work);
+			ret = init_smb2_neg_rsp(work);
 			ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n");
-			return 0;
+		} else {
+			ret = smb_handle_negotiate(work);
 		}
-		return smb_handle_negotiate(work);
+		ksmbd_conn_unlock(conn);
+		return ret;
 	}
 
 	pr_err("Unknown SMB negotiation command: %u\n", command);
author maintainer
#279 lore raw

PATCH 279/438 mshv: fix hv_input_get_system_property struct

In reply to Greg Kroah-Hartman

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

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

From: Wei Liu <[email protected]>

commit d6f0248f04a96249660591e47fcf37ba98ac7ea3 upstream.

Keep it in sync with the correct definition.

The old code worked by chance.

Fixes e68bda71a2384 ("hyperv: Add new Hyper-V headers in include/hyperv")
---
 include/hyperv/hvhdk_mini.h |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
include/hyperv/hvhdk_mini.h +2 -1
@@ -184,8 +184,9 @@ enum hv_dynamic_processor_feature_proper
 
 struct hv_input_get_system_property {
 	u32 property_id; /* enum hv_system_property */
+	u32 reserved;
 	union {
-		u32 as_uint32;
+		u64 as_uint64;
 #if IS_ENABLED(CONFIG_X86)
 		/* enum hv_dynamic_processor_feature_property */
 		u32 hv_processor_feature;
author maintainer
#280 lore raw

PATCH 280/438 net/smc: fix socket use-after-free during link group termination

In reply to Greg Kroah-Hartman

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

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

From: Xuanqiang Luo <[email protected]>

commit f621d6ebeebb6374342571e4ddf45fdbc420f6cd upstream.

__smc_lgr_terminate() drops conns_lock after finding a connection in lgr->conns_all, but before taking a reference on its socket. The connection is embedded in the socket, and its registration reference protects it only while the connection remains in the tree.

A concurrent close can unregister the connection and drop that reference, freeing the socket before the termination worker reaches sock_hold().

The race is reachable when close overlaps link group termination. Local stress testing reproduced the use-after-free and KASAN reported:

  BUG: KASAN: slab-use-after-free in __smc_lgr_terminate.part.0 [smc]
  Write of size 4 by task kworker/3:3
  Workqueue: events smc_lgr_terminate_work [smc]
  __smc_lgr_terminate.part.0 [smc]

The socket was allocated by smc_create(), freed through slab_free_after_rcu_debug(), and was followed by:

refcount_t: addition on 0; use-after-free.
__smc_lgr_terminate.part.0 [smc]

Take the socket reference while conns_lock still protects the tree entry. The unregister path then cannot drop the last reference until termination has finished using the socket.

---
 net/smc/smc_core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/smc/smc_core.c +1 -1
@@ -1572,10 +1572,10 @@ static void __smc_lgr_terminate(struct s
 	read_lock_bh(&lgr->conns_lock);
 	node = rb_first(&lgr->conns_all);
 	while (node) {
-		read_unlock_bh(&lgr->conns_lock);
 		conn = rb_entry(node, struct smc_connection, alert_node);
 		smc = container_of(conn, struct smc_sock, conn);
 		sock_hold(&smc->sk); /* sock_put below */
+		read_unlock_bh(&lgr->conns_lock);
 		lock_sock(&smc->sk);
 		smc_conn_kill(conn, soft);
 		release_sock(&smc->sk);
author maintainer
#281 lore raw

PATCH 281/438 netfilter: ipset: do not update comments from kernel-side hash adds

In reply to Greg Kroah-Hartman

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

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

From: David Lee <[email protected]>

commit f30415929be8aeb002d557c8d3f7ab2d2188003a upstream.

mtype_resize() copies comment pointers with memcpy(), not the comment objects themselves. During the window after an entry has been copied but before the table swap and backlog replay, the old table is still published for packet-side updates while the replacement-table entry already holds the same ip_set_comment_rcu pointer.

If xt_SET --add-set ... --exist hits that old entry in this window, mtype_add() calls ip_set_init_comment() even though packet-side adds carry no comment payload. That call frees the shared comment through the old entry, so the replacement-table entry now holds a stale pointer. When the queued add is replayed on the new table, mtype_add() calls ip_set_init_comment() again and strlen() dereferences the stale pointer.

Fix this in mtype_add() by skipping ip_set_init_comment() when ext->target marks a packet-side add. Userspace adds still update comments, while packet-side adds can no longer free comment storage shared with a resize copy.

Fixes f66ee0410b1c ("netfilter: ipset: Fix "INFO: rcu detected stall in hash_xxx" reports")
Assisted-by Codex:gpt-5.5
---
 net/netfilter/ipset/ip_set_hash_gen.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/ipset/ip_set_hash_gen.h +1 -1
@@ -984,7 +984,7 @@ overwrite_extensions:
 #endif
 	if (SET_WITH_COUNTER(set))
 		ip_set_init_counter(ext_counter(data, set), ext);
-	if (SET_WITH_COMMENT(set))
+	if (SET_WITH_COMMENT(set) && !ext->target)
 		ip_set_init_comment(set, ext_comment(data, set), ext);
 	if (SET_WITH_SKBINFO(set))
 		ip_set_init_skbinfo(ext_skbinfo(data, set), ext);
author maintainer
#282 lore raw

PATCH 282/438 of/address: Fix NULL bus dereference in of_pci_range_parser_one()

In reply to Greg Kroah-Hartman

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

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

From: Carlo Caione <[email protected]>

commit bba13ad17b1a11b3f1ed9b3a5d556191d7755a59 upstream.

The bus matching rework made of_match_bus() return NULL for nodes with ranges/dma-ranges but no local #address-cells. parser_init() stored that NULL bus, and the range iterator later dereferenced it.

Reject such nodes in parser_init(), leaving an explicit empty iterator for callers that ignore the init return, and make of_dma_get_max_cpu_address() honour the init failure so a rejected node cannot clamp the DMA limit.

Fixes 64ee3cf096ac ("of/address: Rework bus matching to avoid warnings")
---
 drivers/of/address.c |   22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)
drivers/of/address.c +16 -6
@@ -753,6 +753,7 @@ EXPORT_SYMBOL(of_property_read_reg);
 static int parser_init(struct of_pci_range_parser *parser,
 			struct device_node *node, const char *name)
 {
+	const __be32 *range;
 	int rlen;
 
 	parser->node = node;
@@ -761,12 +762,20 @@ static int parser_init(struct of_pci_ran
 	parser->ns = of_bus_n_size_cells(node);
 	parser->dma = !strcmp(name, "dma-ranges");
 	parser->bus = of_match_bus(node);
+	parser->range = NULL;
+	parser->end = NULL;
 
-	parser->range = of_get_property(node, name, &rlen);
-	if (parser->range == NULL)
+	range = of_get_property(node, name, &rlen);
+	if (!range)
 		return -ENOENT;
 
-	parser->end = parser->range + rlen / sizeof(__be32);
+	if (!parser->bus ||
+	    !OF_CHECK_COUNTS(parser->na, parser->ns) ||
+	    !OF_CHECK_ADDR_COUNT(parser->pna))
+		return -EINVAL;
+
+	parser->range = range;
+	parser->end = range + rlen / sizeof(__be32);
 
 	return 0;
 }
@@ -792,7 +801,7 @@ struct of_pci_range *of_pci_range_parser
 	int na = parser->na;
 	int ns = parser->ns;
 	int np = parser->pna + na + ns;
-	int busflag_na = parser->bus->flag_cells;
+	int busflag_na;
 
 	if (!range)
 		return NULL;
@@ -800,6 +809,8 @@ struct of_pci_range *of_pci_range_parser
 	if (!parser->range || parser->range + np > parser->end)
 		return NULL;
 
+	busflag_na = parser->bus->flag_cells;
+
 	range->flags = parser->bus->get_flags(parser->range);
 
 	range->bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na);
@@ -976,8 +987,7 @@ phys_addr_t __init of_dma_get_max_cpu_ad
 		np = of_root;
 
 	ranges = of_get_property(np, "dma-ranges", &len);
-	if (ranges && len) {
-		of_dma_range_parser_init(&parser, np);
+	if (ranges && len && !of_dma_range_parser_init(&parser, np)) {
 		for_each_of_range(&parser, &range)
 			if (range.cpu_addr + range.size > cpu_end)
 				cpu_end = range.cpu_addr + range.size - 1;
author maintainer
#283 lore raw

PATCH 283/438 PCI: imx6: Keep i.MX6 Root Port MSI/MSI-X Capabilities with iMSI-RX to work around hardware bug

In reply to Greg Kroah-Hartman

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

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

From: Soeren Moch <[email protected]>

commit b4bee12ebeccfcd5959ace2c3a4af08f5a917d4c upstream.

On some NXP chipsets, disabling Root Port MSI/MSI-X Capabilities blocks MSIs originating from Endpoints from reaching the iMSI-RX controller.

To address this hardware bug, commit 3a4e8302e72f ("PCI: imx6: Keep Root Port MSI capability with iMSI-RX to work around hardware bug") preserves Root Port MSI and MSI-X Capabilities on i.MX7D, i.MX8MM, and i.MX8MQ when iMSI-RX is in use.

The same applies to i.MX6Q, i.MX6QP, and i.MX6SX, so preserve Root Port MSI/MSI-X Capabilities there as well.

Note that preserving these Capabilities means Root Port-originated MSIs such as AER and PME won't be received due to separate hardware limitations. Users may need to use workarounds such as passing the 'pcie_pme=nomsi' command-line parameter.

Fixes f5cd8a929c825 ("PCI: dwc: Remove MSI/MSIX capability for Root Port if iMSI-RX is used as MSI controller")
---
 drivers/pci/controller/dwc/pci-imx6.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/pci/controller/dwc/pci-imx6.c +6 -3
@@ -1884,7 +1884,8 @@ static const struct imx_pcie_drvdata drv
 		.flags = IMX_PCIE_FLAG_IMX_PHY |
 			 IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
 			 IMX_PCIE_FLAG_BROKEN_SUSPEND |
-			 IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
+			 IMX_PCIE_FLAG_SUPPORTS_SUSPEND |
+			 IMX_PCIE_FLAG_KEEP_MSI_CAP,
 		.dbi_length = 0x200,
 		.gpr = "fsl,imx6q-iomuxc-gpr",
 		.ltssm_off = IOMUXC_GPR12,
@@ -1900,7 +1901,8 @@ static const struct imx_pcie_drvdata drv
 		.flags = IMX_PCIE_FLAG_IMX_PHY |
 			 IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
 			 IMX_PCIE_FLAG_SKIP_L23_READY |
-			 IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
+			 IMX_PCIE_FLAG_SUPPORTS_SUSPEND |
+			 IMX_PCIE_FLAG_KEEP_MSI_CAP,
 		.gpr = "fsl,imx6q-iomuxc-gpr",
 		.ltssm_off = IOMUXC_GPR12,
 		.ltssm_mask = IMX6Q_GPR12_PCIE_CTL_2,
@@ -1915,7 +1917,8 @@ static const struct imx_pcie_drvdata drv
 		.flags = IMX_PCIE_FLAG_IMX_PHY |
 			 IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
 			 IMX_PCIE_FLAG_SKIP_L23_READY |
-			 IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
+			 IMX_PCIE_FLAG_SUPPORTS_SUSPEND |
+			 IMX_PCIE_FLAG_KEEP_MSI_CAP,
 		.dbi_length = 0x200,
 		.gpr = "fsl,imx6q-iomuxc-gpr",
 		.ltssm_off = IOMUXC_GPR12,
author maintainer
#284 lore raw

PATCH 284/438 tipc: avoid use-after-free in poll trace queue dumps

In reply to Greg Kroah-Hartman

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

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

From: Zihan Xi <[email protected]>

commit b4f1719dfea023220e0e6bd892b087d76b2a6a49 upstream.

TIPC socket tracepoints dump queue state through tipc_sk_dump(). Most queue-dump callsites already serialize that walk under the socket lock or sk->sk_lock.slock, but tipc_poll() calls trace_tipc_sk_poll(..., TIPC_DUMP_ALL, ...) without holding either lock.

That lets the poll trace path reach tipc_list_dump() and backlog head/tail dumping while another context dequeues and frees an skb, leaving the trace helper dereferencing a stale queue entry.

Stop the unlocked poll trace site from requesting queue dumps. Other queue dump trace callsites keep their existing output under the locking they already provide, while poll still emits the event itself without walking live queue members from an unlocked context.

---
 net/tipc/socket.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/tipc/socket.c +1 -1
@@ -795,7 +795,7 @@ static __poll_t tipc_poll(struct file *f
 	__poll_t revents = 0;
 
 	sock_poll_wait(file, sock, wait);
-	trace_tipc_sk_poll(sk, NULL, TIPC_DUMP_ALL, " ");
+	trace_tipc_sk_poll(sk, NULL, TIPC_DUMP_NONE, " ");
 
 	if (sk->sk_shutdown & RCV_SHUTDOWN)
 		revents |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM;
author maintainer
#285 lore raw

PATCH 285/438 x86/CPU/AMD: Carve out a Zen5 models range

In reply to Greg Kroah-Hartman

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

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

From: Pratik Vishwakarma <[email protected]>

commit 52075128273ace53e6254e37899a47d40d4baf45 upstream.

Family 0x1a, model 0xd0..0xd7 belongs to the Zen5 generation. Carve it out from the larger, Zen6 range where former doesn't belong.

[ bp: Rewrite commit message, add tags. ]

---
 arch/x86/kernel/cpu/amd.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
arch/x86/kernel/cpu/amd.c +3 -1
@@ -514,11 +514,13 @@ static void bsp_init_amd(struct cpuinfo_
 		case 0x00 ... 0x2f:
 		case 0x40 ... 0x4f:
 		case 0x60 ... 0x7f:
+		case 0xd0 ... 0xd7:
 			setup_force_cpu_cap(X86_FEATURE_ZEN5);
 			break;
 		case 0x50 ... 0x5f:
 		case 0x80 ... 0xaf:
-		case 0xc0 ... 0xef:
+		case 0xc0 ... 0xcf:
+		case 0xd8 ... 0xef:
 			setup_force_cpu_cap(X86_FEATURE_ZEN6);
 			break;
 		default:
author maintainer
#286 lore raw

PATCH 286/438 wifi: mac80211: fix tid_tx use-after-free on BA session stop

In reply to Greg Kroah-Hartman

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

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

From: Zhao Li <[email protected]>

commit 2f067f5a450ea07efd249142a11d940a068fe29c upstream.

ieee80211_stop_tx_ba_cb() hands tid_tx to kfree_rcu() through ieee80211_remove_tid_tx(), and then reads tid_tx->ndp after dropping sta->lock:

	ieee80211_remove_tid_tx(sta, tid);	/* kfree_rcu(tid_tx, rcu_head) */
	...
	spin_unlock_bh(&sta->lock);

	if (start_txq)
		ieee80211_agg_start_txq(sta, tid, false);

	if (send_delba)
		ieee80211_send_delba(..., tid_tx->ndp);

That read is not covered by an RCU read-side critical section, and it runs in preemptible process context: both callers hold the wiphy mutex, reaching it either from the ieee80211_ba_session_work() wiphy work or from ieee80211_sta_tear_down_BA_sessions() during station teardown. Softirqs can run in that window too, both from the local_bh_enable() that ends ieee80211_agg_start_txq() and from any interrupt exit, so the RCU callback can free tid_tx before the read.

Driving the function from a test module with the grace period forced into that window, KASAN reports the read, and the free arrives on the ordinary RCU softirq path:

  BUG: KASAN: slab-use-after-free in ieee80211_stop_tx_ba_cb+0x3cd/0x400
  Read of size 1 at addr ffff888002b9f52e by task kworker/0:1/10
  [...]
  Freed by task 57:
   __kasan_slab_free+0x47/0x70
   __rcu_free_sheaf_prepare+0x70/0x250
   rcu_free_sheaf_nobarn+0x18/0x40
   rcu_core+0x426/0x1310
   handle_softirqs+0x144/0x590
   __irq_exit_rcu+0xea/0x150
   irq_exit_rcu+0x9/0x20
   sysvec_apic_timer_interrupt+0x6b/0x80
   asm_sysvec_apic_timer_interrupt+0x1a/0x20

send_delba is only set when tx_stop is set, which happens for AGG_STOP_LOCAL_REQUEST alone, so this is reached on local teardown - session idle timeout, PTK rekey, suspend, HW reconfig - and not from a peer's DELBA.

Read ndp into a local before the session is freed, while sta->lock is still held. tid_tx->ndp has a single writer, in ieee80211_tx_ba_session_handle_start(), which cannot run concurrently here:
both paths are serialised by the wiphy mutex, and the session is already marked HT_AGG_STATE_STOPPING at this point. tid_tx->ndp is also the only tid_tx dereference left after ieee80211_remove_tid_tx() in this function.

Fixes 98acd4c1d9f7 ("wifi: mac80211: add support for NDP ADDBA/DELBA for S1G")
Assisted-by Codex:gpt-5.6-sol
Assisted-by Kimi:K3

[move/change the comment a bit to be more general not just on ndp,
initialize ndp directly]

---
 net/mac80211/agg-tx.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
net/mac80211/agg-tx.c +3 -1
@@ -915,6 +915,7 @@ void ieee80211_stop_tx_ba_cb(struct sta_
 			     struct tid_ampdu_tx *tid_tx)
 {
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
+	bool ndp = ndp = tid_tx->ndp;
 	bool send_delba = false;
 	bool start_txq = false;
 
@@ -934,6 +935,7 @@ void ieee80211_stop_tx_ba_cb(struct sta_
 		send_delba = true;
 
 	ieee80211_remove_tid_tx(sta, tid);
+	/* tid_tx is now invalid since ieee80211_remove_tid_tx() frees it */
 	start_txq = true;
 
  unlock_sta:
@@ -946,7 +948,7 @@ void ieee80211_stop_tx_ba_cb(struct sta_
 		ieee80211_send_delba(sdata, sta->sta.addr, tid,
 				     WLAN_BACK_INITIATOR,
 				     WLAN_REASON_QSTA_NOT_USE,
-				     tid_tx->ndp);
+				     ndp);
 }
 
 void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,
author maintainer
#287 lore raw

PATCH 287/438 wifi: mwifiex: use the subframe length when parsing A-MSDU TDLS frames

In reply to Greg Kroah-Hartman

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

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

From: Zhao Li <[email protected]>

commit 99a948382af8a225e2d5e54a7052158cd6281cc6 upstream.

mwifiex_11n_dispatch_amsdu_pkt() splits an A-MSDU with ieee80211_amsdu_to_8023s() and walks the resulting subframes. For each subframe it passes the subframe data pointer to mwifiex_process_tdls_action_frame(), but pairs it with skb->len, the length of the A-MSDU parent, instead of rx_skb->len:

	rx_skb = __skb_dequeue(&list);
	rx_hdr = (struct rx_packet_hdr *)rx_skb->data;
	if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
	    ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
		mwifiex_process_tdls_action_frame(priv, (u8 *)rx_hdr,
						  skb->len);
	}

The parent is not a valid description of that buffer, and may not be valid memory at all. ieee80211_amsdu_to_8023s() ends with

	if (!reuse_skb)
		dev_kfree_skb(skb);

and it only sets reuse_skb when the parent is linear, is not a head_frag, and is being consumed as the *last* subframe. So when the parent does not qualify for reuse it has already been freed, and the read of skb->len is a use-after-free. When it is reused, skb->len is the length of the last subframe, applied to every earlier subframe, which over-states the buffer whenever an earlier subframe is shorter.

The callee cannot absorb a wrong length, because it derives its own ceiling from the value it is given. Each frame type computes

	ies_len = len - sizeof(struct ethhdr) - TDLS_*_FIX_LEN;

and the element walk is then bounded entirely against that ceiling,

	for (end = pos + ies_len; pos + 1 < end; pos += 2 + pos[1]) {
		u8 ie_len = pos[1];

		if (pos + 2 + ie_len > end)
			break;

so a too-large len moves end past the end of the subframe and the walk reads and copies beyond it. The A-MSDU layout is chosen by the sender, which makes the difference between the last subframe and a shorter earlier one remotely selectable. Reaching this requires TDLS support in firmware and the TDLS ethertype on the subframe.

The other caller, mwifiex_process_rx_packet(), is correct: it passes a pointer and a length that describe the same region of the RX buffer.

Pass rx_skb->len, the length of the subframe actually being parsed.

Fixes 776f742040ca ("mwifiex: fix AMPDU not setup on TDLS link problem")
Assisted-by Codex:gpt-5.6-sol
Assisted-by Kimi:K3
---
 drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +1 -1
@@ -44,7 +44,7 @@ static int mwifiex_11n_dispatch_amsdu_pk
 			    ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
 				mwifiex_process_tdls_action_frame(priv,
 								  (u8 *)rx_hdr,
-								  skb->len);
+								  rx_skb->len);
 			}
 
 			if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
author maintainer
#288 lore raw

PATCH 288/438 binfmt_misc: restore write access when removing an entry

In reply to Greg Kroah-Hartman

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

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

From: Christian Brauner <[email protected]>

commit db1856ea9196cf6e015d12199a34c0b9313c7bfa upstream.

Registering an entry with the MISC_FMT_OPEN_FILE flag opens the interpreter via open_exec() which denies write access to it for as long as the entry exists. Removing the entry closes the interpreter file via filp_close() but never restores write access, leaving the inode's i_writecount permanently negative. Opening the interpreter for writing keeps failing with ETXTBSY long after the entry is gone until the inode is evicted from the inode cache.

Commit 90f601b497d7 ("binfmt_misc: restore write access before closing files opened by open_exec()") fixed the same imbalance in the error path of bm_register_write() but the actual removal path has been leaking the write denial since the introduction of the flag.

Restore write access in put_binfmt_handler() before closing the interpreter file.

Fixes 948b701a607f ("binfmt_misc: add persistent opened binary handler for containers")
---
 fs/binfmt_misc.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
fs/binfmt_misc.c +3 -1
@@ -162,8 +162,10 @@ static Node *get_binfmt_handler(struct b
 static void put_binfmt_handler(Node *e)
 {
 	if (refcount_dec_and_test(&e->users)) {
-		if (e->flags & MISC_FMT_OPEN_FILE)
+		if (e->flags & MISC_FMT_OPEN_FILE) {
+			exe_file_allow_write_access(e->interp_file);
 			filp_close(e->interp_file, NULL);
+		}
 		kfree(e);
 	}
 }
author maintainer
#289 lore raw

PATCH 289/438 binfmt_misc: use exe_file_deny_write_access() for the interpreter clone

In reply to Greg Kroah-Hartman

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

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

From: Christian Brauner <[email protected]>

commit fa5990ca8fd917003e526036bcc50413edb9722c upstream.

For MISC_FMT_OPEN_FILE entries load_misc_binary() clones the registered interpreter file and denies write access to the clone via plain deny_write_access(). The clone is installed as bprm->interpreter and later released by the exec machinery through exe_file_allow_write_access() which skips the i_writecount increment for files with FMODE_FSNOTIFY_HSM set.

The deny and allow side can therefore come to different conclusions when pre-content watches are in play: if a pre-content watch is added to the interpreter after registration every subsequent exec through that entry takes a write denial on the clone that is never paired with a write allowance, driving the interpreter inode's i_writecount further down with each exec and leaving the interpreter unwritable even after the entry and all its users are gone.

Take the write denial via exe_file_deny_write_access() so both sides of the pairing base their decision on the same file mode, and propagate failure instead of silently ignoring it: an interpreter that is concurrently open for writing now fails the exec with ETXTBSY, exactly like an interpreter freshly opened via open_exec() would.

Fixes 0357ef03c94e ("fs: don't block write during exec on pre-content watched files")
---
 fs/binfmt_misc.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
fs/binfmt_misc.c +8 -2
@@ -249,8 +249,14 @@ static int load_misc_binary(struct linux
 
 	if (fmt->flags & MISC_FMT_OPEN_FILE) {
 		interp_file = file_clone_open(fmt->interp_file);
-		if (!IS_ERR(interp_file))
-			deny_write_access(interp_file);
+		if (!IS_ERR(interp_file)) {
+			int err = exe_file_deny_write_access(interp_file);
+
+			if (err) {
+				fput(interp_file);
+				interp_file = ERR_PTR(err);
+			}
+		}
 	} else {
 		interp_file = open_exec(fmt->interpreter);
 	}
author maintainer
#290 lore raw

PATCH 290/438 binfmt_misc: reject a flag character as the field delimiter

In reply to Greg Kroah-Hartman

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

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

From: Christian Brauner <[email protected]>

commit 8e85d50ba1117fd446bf9a250bd8a97d48384bdc upstream.

The registration string starts with a user chosen delimiter that separates the individual fields. So that the field parsers terminate even on a truncated string create_entry() pads the buffer with that same delimiter:

	memset(buf + count, del, 8);

Most fields are scanned for the delimiter with strchr()/scanarg() and happily stop on the padding. The flags field is different: instead of scanning for the delimiter check_special_flags() consumes the flag characters 'P', 'O', 'C' and 'F' and stops at the first byte that is none of them, relying on the trailing delimiter to end the scan.

If the delimiter is itself a flag character the padding no longer acts as a terminator. The scan swallows all eight padding bytes and keeps reading past the end of the allocation until it hits a byte that is not a flag character. For example registering

PaPEPPxPPiP

with 'P' as the delimiter (name "a", type extension, magic "x", interpreter "i", empty flags) leaves the flag scan running off the end of the buffer. The registration is rejected in the end because the parser does not stop exactly at buf + count, but only after the out of bounds read has already happened. With an unlucky allocation layout the scan can walk into an unmapped page; under KASAN it is reported as a slab out of bounds read. binfmt_misc mounts are available to unprivileged users in a user namespace so the read is reachable without privileges.

Reject a delimiter that is one of the flag characters up front. Such a registration was always rejected anyway, only after the out of bounds read, so no valid registration string changes meaning.

---
 fs/binfmt_misc.c |    4 ++++
 1 file changed, 4 insertions(+)
fs/binfmt_misc.c +4 -0
@@ -384,6 +384,10 @@ static Node *create_entry(const char __u
 
 	pr_debug("register: delim: %#x {%c}\n", del, del);
 
+	/* A flag-char delimiter runs the flag scan off the buffer. */
+	if (del == 'P' || del == 'O' || del == 'C' || del == 'F')
+		goto einval;
+
 	/* Pad the buffer with the delim to simplify parsing below. */
 	memset(buf + count, del, 8);
 
author maintainer
#291 lore raw

PATCH 291/438 binfmt_misc: dont let an F entry pin its own instance

In reply to Greg Kroah-Hartman

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

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

From: Christian Brauner <[email protected]>

commit 79055d82772b9584f259b747fe40ff56a076678d upstream.

An entry registered with 'F' opens its interpreter at registration time and holds that file until the entry is freed. Any entry nobody removes by hand only gets closed once the binfmt_misc superblock is shut down. If the interpreter lives on a mount that keeps that superblock alive the two pin each other:

    binfmt_misc sb -> inode -> entry -> interp_file -> vfsmount -> binfmt_misc sb

TL;DR the file is never closed. Once the mount namespace is gone there is nothing left to unregister through either.

There are two ways to trigger this bug:

- Point the interpreter at the instance itself. Its files are regular files owned by the mounter and both bm_get_inode() and simple_fill_super() leave i_op at empty_iops. So notify_change() falls back to simple_setattr() and chmod +x works. We never set SB_I_NOEXEC and so open_exec() accepts it.

- Use the instance as an overlayfs lower layer. The overlay superblock holds a clone_private_mount() of every layer until it is destroyed and that clone is in no namespace. So umount_tree() never reaches it.

That's a DoS. And it isn't only the superblock that leaks. It pins the user namespace it was mounted in, so every iteration permanently eats one of the caller's user namespace charges.

So let's just do the sane thing. SB_I_NOEXEC makes open_exec() fail on the instance's own files and s_stack_depth makes overlayfs reject the layer before it ever takes a clone. That also covers the ecryptfs and fuse passthrough variants. What 'F' promises is unchanged.

The stable tag is narrower than the Fixes tags on purpose. Before sandboxed mounts this needed global root against the single instance everyone shares, and the change doesn't apply to those trees anyway.

Note that SB_I_NODEV is implicitly raised for userns mounts but raise it explicitly here as well.

Fixes 948b701a607f ("binfmt_misc: add persistent opened binary handler for containers")
Fixes 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts")
---
 fs/binfmt_misc.c |    4 ++++
 1 file changed, 4 insertions(+)
fs/binfmt_misc.c +4 -0
@@ -949,6 +949,10 @@ static int bm_fill_super(struct super_bl
 	if (WARN_ON(user_ns != current_user_ns()))
 		return -EINVAL;
 
+	/* Never exec off this instance and never let anything stack on it. */
+	sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV;
+	sb->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
+
 	/*
 	 * Lazily allocate a new binfmt_misc instance for this namespace, i.e.
 	 * do it here during the first mount of binfmt_misc. We don't need to
author maintainer
#292 lore raw

PATCH 292/438 binfmt_misc: dont leak the user namespace when the mount fails

In reply to Greg Kroah-Hartman

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

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

From: Christian Brauner <[email protected]>

commit b8206f516fe7cbe785cf44bf09c17c438d7c3cad upstream.

bm_get_tree() takes a reference to the user namespace and hands it to get_tree_keyed() as the sget key. sget_fc() moves that reference into sb->s_fs_info and clears fc->s_fs_info, so from that point on the superblock owns it and bm_free() doesn't see it anymore.

The superblock drops it in ->put_super(). But generic_shutdown_super() only calls ->put_super() from inside the if (sb->s_root) branch, so nothing releases it when bm_fill_super() fails:

- The kzalloc_obj() failure leaves s_root NULL and the whole branch is skipped.

- A simple_fill_super() failure in the file loop leaves s_root set, but s_op still points at simple_super_operations, which has no ->put_super(). bm_fill_super() installs s_ops only once simple_fill_super() returned success, and installing it earlier wouldn't help either because simple_fill_super() overwrites s_op.

Either way vfs_get_super() calls deactivate_locked_super() and the reference is gone for good. binfmt_misc mounts are available in a user namespace and both the inode and the dentry cache are SLAB_ACCOUNT, so an unprivileged caller under a tight memory cgroup can fail simple_fill_super() on demand and leak one user namespace per attempt.

Drop the reference in ->kill_sb() instead, which runs unconditionally, the same way nfsd and rpc_pipefs release their keyed s_fs_info.

That also stops ->put_super() from clearing s_fs_info while the superblock is still on @fs_supers. generic_shutdown_super() leaves it there on purpose so that sget_fc() keeps finding it until kill_sb() has run, but a NULL s_fs_info makes test_keyed_super() miss it, so a concurrent mount for the same user namespace skips the grab_super() wait and creates a second superblock for a namespace that is still being torn down.

---
 fs/binfmt_misc.c |   32 +++++++++++++++-----------------
 1 file changed, 15 insertions(+), 17 deletions(-)
fs/binfmt_misc.c +15 -17
@@ -921,18 +921,9 @@ static const struct file_operations bm_s
 
 /* Superblock handling */
 
-static void bm_put_super(struct super_block *sb)
-{
-	struct user_namespace *user_ns = sb->s_fs_info;
-
-	sb->s_fs_info = NULL;
-	put_user_ns(user_ns);
-}
-
 static const struct super_operations s_ops = {
 	.statfs		= simple_statfs,
 	.evict_inode	= bm_evict_inode,
-	.put_super	= bm_put_super,
 };
 
 static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
@@ -990,13 +981,12 @@ static int bm_fill_super(struct super_bl
 	/*
 	 * When the binfmt_misc superblock for this userns is shutdown
 	 * ->enabled might have been set to false and we don't reinitialize
-	 * ->enabled again in put_super() as someone might already be mounting
-	 * binfmt_misc again. It also would be pointless since by the time
-	 * ->put_super() is called we know that the binary type list for this
-	 * bintfmt_misc mount is empty making load_misc_binary() return
-	 * -ENOEXEC independent of whether ->enabled is true. Instead, if
-	 * someone mounts binfmt_misc for the first time or again we simply
-	 * reset ->enabled to true.
+	 * ->enabled again during shutdown as someone might already be mounting
+	 * binfmt_misc again. It also would be pointless since by then we know
+	 * that the binary type list for this binfmt_misc mount is empty making
+	 * load_misc_binary() return -ENOEXEC independent of whether ->enabled
+	 * is true. Instead, if someone mounts binfmt_misc for the first time or
+	 * again we simply reset ->enabled to true.
 	 */
 	misc->enabled = true;
 
@@ -1022,6 +1012,14 @@ static const struct fs_context_operation
 	.get_tree	= bm_get_tree,
 };
 
+static void bm_kill_sb(struct super_block *sb)
+{
+	struct user_namespace *user_ns = sb->s_fs_info;
+
+	kill_anon_super(sb);
+	put_user_ns(user_ns);
+}
+
 static int bm_init_fs_context(struct fs_context *fc)
 {
 	fc->ops = &bm_context_ops;
@@ -1038,7 +1036,7 @@ static struct file_system_type bm_fs_typ
 	.name		= "binfmt_misc",
 	.init_fs_context = bm_init_fs_context,
 	.fs_flags	= FS_USERNS_MOUNT,
-	.kill_sb	= kill_anon_super,
+	.kill_sb	= bm_kill_sb,
 };
 MODULE_ALIAS_FS("binfmt_misc");
 
author maintainer
#293 lore raw

PATCH 293/438 io_uring/net: initialize mshot_len for send

In reply to Greg Kroah-Hartman

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

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

From: Jens Axboe <[email protected]>

commit c77ffbc980efb337fd750c337d8157d532ea14e5 upstream.

Commit:

6a8afb9fff64 ("io_uring/net: allow multishot receive per-invocation cap")

changed how io_mshot_prep_retry() set sr->len, and added the same initialization in io_mshot_prep_retry(). But it neglected to touch the send path, which may also uses the mshot retry path. Ensure that sr->mshot_len always gets initialized correctly.

Fixes 6a8afb9fff64 ("io_uring/net: allow multishot receive per-invocation cap")
---
 io_uring/net.c |    1 +
 1 file changed, 1 insertion(+)
io_uring/net.c +1 -0
@@ -436,6 +436,7 @@ int io_sendmsg_prep(struct io_kiocb *req
 		req->flags |= REQ_F_NOWAIT;
 	if (req->flags & REQ_F_BUFFER_SELECT)
 		sr->buf_group = req->buf_index;
+	sr->mshot_total_len = sr->mshot_len = 0;
 	if (sr->flags & IORING_RECVSEND_BUNDLE) {
 		if (req->opcode == IORING_OP_SENDMSG)
 			return -EINVAL;
author maintainer
#294 lore raw

PATCH 294/438 io_uring: preserve task restrictions across exec

In reply to Greg Kroah-Hartman

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

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

From: Kyumin Lee <[email protected]>

commit bc0e8faf90e776a2f1f3967a04e8091e6bdb4977 upstream.

Per-task restrictions apply to all rings created by a task. Once installed, they should not be dropped across exec.

For a task that has used io_uring, the exec cancellation path calls __io_uring_free(). This frees both the task context and the per-task restriction, so a ring created after exec is unrestricted.

Split task context cleanup into io_uring_free_tctx(), and use it from the exec cancellation path. Keep __io_uring_free() for final task cleanup, where both the context and restriction are released.

Fixes ed82f35b926b ("io_uring: allow registration of per-task restrictions")
---
 io_uring/cancel.c |    2 +-
 io_uring/tctx.c   |    7 ++++++-
 io_uring/tctx.h   |    1 +
 3 files changed, 8 insertions(+), 2 deletions(-)
io_uring/cancel.c +1 -1
@@ -662,6 +662,6 @@ end_wait:
 		 */
 		atomic_dec(&tctx->in_cancel);
 		/* for exec all current's requests should be gone, kill tctx */
-		__io_uring_free(current);
+		io_uring_free_tctx(current);
 	}
 }
io_uring/tctx.c +6 -1
@@ -43,7 +43,7 @@ static struct io_wq *io_init_wq_offload(
 	return io_wq_create(concurrency, &data);
 }
 
-void __io_uring_free(struct task_struct *tsk)
+void io_uring_free_tctx(struct task_struct *tsk)
 {
 	struct io_uring_task *tctx = tsk->io_uring;
 	struct io_tctx_node *node;
@@ -67,6 +67,11 @@ void __io_uring_free(struct task_struct
 		kfree(tctx);
 		tsk->io_uring = NULL;
 	}
+}
+
+void __io_uring_free(struct task_struct *tsk)
+{
+	io_uring_free_tctx(tsk);
 	if (tsk->io_uring_restrict) {
 		io_put_bpf_filters(tsk->io_uring_restrict);
 		kfree(tsk->io_uring_restrict);
io_uring/tctx.h +1 -0
@@ -12,6 +12,7 @@ void io_uring_del_tctx_node(unsigned lon
 int __io_uring_add_tctx_node(struct io_ring_ctx *ctx);
 int __io_uring_add_tctx_node_from_submit(struct io_ring_ctx *ctx);
 void io_uring_clean_tctx(struct io_uring_task *tctx);
+void io_uring_free_tctx(struct task_struct *tsk);
 
 void io_uring_unreg_ringfd(void);
 int io_ringfd_register(struct io_ring_ctx *ctx, void __user *__arg,
author maintainer
#295 lore raw

PATCH 295/438 mm/huge_memory: unlock i_mmap_rwsem before releasing after-split folios

In reply to Greg Kroah-Hartman

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

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

From: Kiryl Shutsemau (Meta) <[email protected]>

commit e923bd21058ea02fd0dcd3549d151d143fd036e5 upstream.

__folio_split() keeps dereferencing the mapping after the split:
shmem_uncharge(mapping->host) and remap_page() while the folios are still frozen/locked, and i_mmap_unlock_read(mapping) at the very end, after the after-split folios have been unlocked and freed.

Nothing holds an inode reference across that. The split relies on @folio -- which the beyond-EOF drop loop never removes, as it starts at folio_next(folio) -- staying locked and in the page cache to hold off eviction. But the unlock loop unlocks @folio before i_mmap_unlock_read() runs. If the caller's @lock_at is a tail beyond EOF, as memory_failure() passes when splitting a poisoned tail of a shmem THP that reaches past i_size during truncation, it too is gone from the page cache; so once @folio is unlocked no locked, in-cache folio pins the inode, and a concurrent final iput() can evict and RCU-free it before i_mmap_unlock_read() touches i_mmap_rwsem:

  BUG: KASAN: slab-use-after-free in __up_read+0x634/0x790
   i_mmap_unlock_read include/linux/fs.h:537 [inline]
   __folio_split+0x732/0x1640 mm/huge_memory.c:4100
   try_to_split_thp_page+0xab/0x390 mm/memory-failure.c:1675
   memory_failure+0x1394/0x26e0 mm/memory-failure.c:2470

  Freed by task 4601:
   shmem_free_in_core_inode+0x54/0xb0 mm/shmem.c:5177
   evict+0x57f/0xac0 fs/inode.c:870

Do every mapping dereference while @folio still pins the inode: drop i_mmap_rwsem right after remap_page(), before the loop that unlocks and frees the after-split folios, and clear @mapping so the exit path does not unlock it again. shmem_uncharge() and remap_page() already run before that point, so after this nothing past the unlock loop touches the inode or the mapping.

This is now a rule the split depends on, alongside keeping @folio frozen until the page cache is updated: no inode or mapping dereference once the after-split folios start being unlocked.

---
 mm/huge_memory.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
mm/huge_memory.c +12 -0
@@ -4189,6 +4189,18 @@ fail:
 	remap_page(folio, 1 << old_order, ttu_flags);
 
 	/*
+	 * Drop the mapping while the inode is still pinned. @folio stays
+	 * locked and present in the page cache until the loop below, so
+	 * eviction cannot free the inode yet; @lock_at is not enough, it may
+	 * be a tail beyond EOF that the split already dropped from the page
+	 * cache. Nothing past this point may touch the inode or the mapping.
+	 */
+	if (mapping) {
+		i_mmap_unlock_read(mapping);
+		mapping = NULL;
+	}
+
+	/*
 	 * Unlock all after-split folios except the one containing
 	 * @lock_at page. If @folio is not split, it will be kept locked.
 	 */
author maintainer
#296 lore raw

PATCH 296/438 mm/page_reporting: use system_freezable_wq to fix UAF during suspend

In reply to Greg Kroah-Hartman

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

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

From: Link Lin <[email protected]>

commit 0b45f6927a14914ff685fe0e6f9d11232a1e03df upstream.

During PM freeze (e.g. S3 suspend or S4 hibernation), device drivers like virtio_balloon reset their underlying virtio devices and delete their virtqueues via vdev->config->del_vqs().

However, page reporting work (page_reporting_process) was scheduled on the global system_wq. Because system_wq lacks the WQ_FREEZABLE flag, the PM freezer skips it, leaving page_reporting_process active during suspend.

If pages are freed into the buddy allocator while suspending (for example, when core MM invokes the balloon shrinker during S4 hibernation image saving), page reporting triggers virtballoon_free_page_report() on deleted virtqueues, resulting in a Use-After-Free / General Protection Fault:

    [  196.795226] general protection fault, probably for non-canonical address 0xaa1436fe70dae6df: 0000 [#1] SMP NOPTI
    [  196.825967] Workqueue: events page_reporting_process
    [  196.831038] RIP: 0010:virtqueue_add_split+0x233/0x4c0 [virtio_ring]
    [  196.927073] virtballoon_free_page_report+0x3a/0xe0 [virtio_balloon]
    [  196.946943] page_reporting_process+0x370/0x4f0

Fix this by switching page reporting work to system_freezable_wq. This ensures that the PM freezer pauses page_reporting_process before device drivers destroy their reporting virtqueues. Because the reporting worker is frozen, memory reclamation/freeing (e.g. via shrinker execution) can safely return pages to MM during freeze without triggering unfrozen reporting work on deleted virtqueues.

This aligns with the driver's existing design. The comment in virtballoon_freeze() states:
/*
* The workqueue is already frozen by the PM core before this
* function is called.
*/

Testing:
I have verified these fixes using Google’s virtualization infrastructure by running continuous suspend/resume iterations (40+ cycles) while churning memory using stress-ng (stress-ng --vm 4 --vm-bytes 60% --timeout 1) to constantly create free pages for the buddy allocator. We also set the page_reporting_order parameter to 0 to make the page reporting worker highly sensitive, forcing it to pick up any 4K free pages. This confirmed that the UAF crashes are no longer reproducible.

---
 mm/page_reporting.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
mm/page_reporting.c +4 -2
@@ -80,7 +80,8 @@ __page_reporting_request(struct page_rep
 	 * now we are limiting this to running no more than once every
 	 * couple of seconds.
 	 */
-	schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY);
+	queue_delayed_work(system_freezable_wq, &prdev->work,
+			   PAGE_REPORTING_DELAY);
 }
 
 /* notify prdev of free page reporting request */
@@ -343,7 +344,8 @@ err_out:
 	 */
 	state = atomic_cmpxchg(&prdev->state, state, PAGE_REPORTING_IDLE);
 	if (state == PAGE_REPORTING_REQUESTED)
-		schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY);
+		queue_delayed_work(system_freezable_wq, &prdev->work,
+				   PAGE_REPORTING_DELAY);
 }
 
 static DEFINE_MUTEX(page_reporting_mutex);
author maintainer
#297 lore raw

PATCH 297/438 mm: memcg: initialize *locked in memcg1_oom_prepare() stub

In reply to Greg Kroah-Hartman

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

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

From: Breno Leitao <[email protected]>

commit 1833ce36b35426504c64600c94f322437ea44bb2 upstream.

mem_cgroup_oom() passes an uninitialized "locked" to memcg1_oom_prepare() and reads it back in memcg1_oom_finish():

	bool locked, ret;
	...
	if (!memcg1_oom_prepare(memcg, &locked))
		return false;
	ret = mem_cgroup_out_of_memory(memcg, mask, order);
	memcg1_oom_finish(memcg, locked);

This relies on memcg1_oom_prepare() setting *locked whenever it returns true. The CONFIG_MEMCG_V1=y version does, but the stub used when CONFIG_MEMCG_V1=n returns true without touching *locked, so memcg1_oom_finish() consumes an uninitialized value. On a memcg OOM this is reported by UBSAN:

  UBSAN: invalid-load in mm/memcontrol.c:1932:27
  load of value 0 is not a valid value for type 'bool' (aka '_Bool')

Initialize *locked to false in the stub; with cgroup v1 compiled out there is no OOM lock to take.

---
 mm/memcontrol-v1.h |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
mm/memcontrol-v1.h +5 -1
@@ -107,7 +107,11 @@ static inline void memcg1_remove_from_tr
 static inline void memcg1_soft_limit_reset(struct mem_cgroup *memcg) {}
 static inline void memcg1_css_offline(struct mem_cgroup *memcg) {}
 
-static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked) { return true; }
+static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked)
+{
+	*locked = false;
+	return true;
+}
 static inline void memcg1_oom_finish(struct mem_cgroup *memcg, bool locked) {}
 static inline void memcg1_oom_recover(struct mem_cgroup *memcg) {}
 
author maintainer
#298 lore raw

PATCH 298/438 net: bridge: stop fast-leave after deleting a port group

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit a39789f211b8a4125f0c70e05b30cf715f4f187d upstream.

br_multicast_leave_group() iterates mp->ports with pp = &p->next in its fast-leave path. After br_multicast_del_pg() removes p, continuing the loop advances pp through the deleted entry.

If multicast-to-unicast was enabled, the bridge can hold multiple port groups for the same port and group with different source MAC addresses. Once multicast-to-unicast is disabled, br_port_group_equal() matches those entries by port only. A fast leave can then delete one entry and continue from its stale next pointer, leaving mp->ports pointing at a deleted port group.

Fast leave only needs to remove one matching port group. Break after br_multicast_del_pg() so the loop stops before dereferencing the removed entry.

---
 net/bridge/br_multicast.c |    1 +
 1 file changed, 1 insertion(+)
net/bridge/br_multicast.c +1 -0
@@ -3686,6 +3686,7 @@ br_multicast_leave_group(struct net_brid
 
 			p->flags |= MDB_PG_FLAGS_FAST_LEAVE;
 			br_multicast_del_pg(mp, p, pp);
+			break;
 		}
 		goto out;
 	}
author maintainer
#299 lore raw

PATCH 299/438 net: ipv6: clear suppressed fib6 rule result

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit 6aea62e433fe1b586202a5fee8b5807ce635e1d7 upstream.

fib6_rule_suppress() drops a suppressed route with ip6_rt_put_flags(), but leaves res->rt6 pointing at the released rt6_info.

If no later rule supplies a replacement, fib6_rule_lookup() still sees res.rt6 and returns that stale dst to its caller. A suppressing rule can therefore leak a released route back to rt6_lookup(), and the next put hits rcuref_put_slowpath() from dst_release().

Clear res->rt6 when suppressing the route so suppressed lookups fall through to the null dst instead of reusing the released one.

---
 net/ipv6/fib6_rules.c |    1 +
 1 file changed, 1 insertion(+)
net/ipv6/fib6_rules.c +1 -0
@@ -308,6 +308,7 @@ INDIRECT_CALLABLE_SCOPE bool fib6_rule_s
 
 suppress_route:
 	ip6_rt_put_flags(rt, flags);
+	res->rt6 = NULL;
 	return true;
 }
 
author maintainer
#300 lore raw

PATCH 300/438 net: pktgen: fix proc entry use-after-free

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 817ff6efdb7f484ea547218e11e17d8e43daa3b4 upstream.

pktgen_change_name() replaces pkt_dev->entry while holding t->if_lock. pktgen_remove_device() removes the same entry before _rem_dev_from_if_list() takes that lock.

This allows the following interleaving:

CPU 0 (NETDEV_CHANGENAME) CPU 1 (kpktgend)
if_lock(t)
proc_remove(pkt_dev->entry)

                                  proc_remove(pkt_dev->entry)

pkt_dev->entry = proc_create_data(...)
if_unlock(t)

The kthread can pass the stale proc_dir_entry to proc_remove() after the rename path has freed it. A reproducer with a widened race window reports:

  BUG: KASAN: slab-use-after-free in proc_remove+0x78/0x80
  Read of size 8 at addr ffff8881478fea70 by task kpktgend_0/67
  Call Trace:
   proc_remove+0x78/0x80
   pktgen_remove_device.isra.0+0x11c/0x4c0
   pktgen_thread_worker+0x1214/0x6bc0
   kthread+0x2c6/0x3b0
  Allocated by task 95:
   __proc_create+0x204/0x790
   proc_create_data+0x72/0xe0
   pktgen_thread_write+0xd61/0x1510
  Freed by task 28:
   kmem_cache_free+0xcb/0x3d0
   proc_free_inode+0x5b/0x80
   rcu_core+0x50a/0x1850
  The buggy address belongs to the object at ffff8881478fea00
   which belongs to the cache proc_dir_entry of size 192

Move proc_remove() into the if_lock-protected list removal helper. Keep it before list_del_rcu() to preserve the ordering required by add_device(). The rename path must then finish replacing the entry before removal, or it observes that the device is no longer on the list.

---
 net/core/pktgen.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
net/core/pktgen.c +1 -3
@@ -3972,6 +3972,7 @@ static void _rem_dev_from_if_list(struct
 	struct pktgen_dev *p;
 
 	if_lock(t);
+	proc_remove(pkt_dev->entry);
 	list_for_each_safe(q, n, &t->if_list) {
 		p = list_entry(q, struct pktgen_dev, list);
 		if (p == pkt_dev)
@@ -4001,9 +4002,6 @@ static int pktgen_remove_device(struct p
 	 * list to determine if interface already exist, avoid race
 	 * with proc_create_data()
 	 */
-	proc_remove(pkt_dev->entry);
-
-	/* And update the thread if_list */
 	_rem_dev_from_if_list(t, pkt_dev);
 
 #ifdef CONFIG_XFRM
author maintainer
#301 lore raw

PATCH 301/438 riscv/mm: use physical alignment for vmemmap_start_pfn

In reply to Greg Kroah-Hartman

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

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

From: Jiakai Xu <[email protected]>

commit 45ebe4540817cb540a59e4dc04014ac5dddc9990 upstream.

RISC-V computes vmemmap_start_pfn by rounding phys_ram_base down to VMEMMAP_ADDR_ALIGN. That alignment must therefore be expressed in the physical-address domain.

Commit 476849b0fba4 ("riscv/mm: align vmemmap to maximal folio size") attempted to account for the maximal folio alignment by feeding MAX_FOLIO_VMEMMAP_ALIGN directly into VMEMMAP_ADDR_ALIGN. However, MAX_FOLIO_VMEMMAP_ALIGN is measured in bytes of struct page storage, whereas VMEMMAP_ADDR_ALIGN is used to align a physical address.

The mask-based compound_info encoding requires pfn_to_page(0) to be naturally aligned to MAX_FOLIO_VMEMMAP_ALIGN. Commit 9f94db4c7eaa ("mm/sparse: check memmap alignment for compound_info_has_mask()") added a check for that requirement and exposed the unit mismatch on systems such as QEMU virt, where the DRAM base is not aligned to MAX_FOLIO_NR_PAGES * PAGE_SIZE.

Here is the log:

[    0.000000][    C0] ------------[ cut here ]------------
[    0.000000][    C0] WARNING: mm/sparse.c:365 at sparse_init+0x58a/0x6fe, CPU#0: swapper/0
[    0.000000][    C0] Modules linked in:
[    0.000000][    C0] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 7.2.0-rc3-g1d8304bdd65f #2 PREEMPT
[    0.000000][    C0] Hardware name: riscv-virtio,qemu (DT)
[    0.000000][    C0] epc : sparse_init+0x58a/0x6fe
[    0.000000][    C0]  ra : sparse_init+0x58a/0x6fe
[    0.000000][    C0] epc : ffffffff86851c88 ra : ffffffff86851c88 sp : ffffffff88807a30
[    0.000000][    C0]  gp : ffffffff8a3bf240 tp : ffffffff88842080 t0 : ff600000ffab6000
[    0.000000][    C0]  t1 : 000000017fab6000 t2 : 65203a6573726363 s0 : ffffffff88807bc0
[    0.000000][    C0]  s1 : 000000000e000000 a0 : 0000000000000007 a1 : 0000000000000000
[    0.000000][    C0]  a2 : 0000000000000002 a3 : ffffffff86851c88 a4 : 0000000000000000
[    0.000000][    C0]  a5 : ffffffff88843080 a6 : 0000000000000003 a7 : 0000000000000000
[    0.000000][    C0]  s2 : ff60000000000000 s3 : 0040000000000000 s4 : 0004000000000000
[    0.000000][    C0]  s5 : ffffffff8a4d92e0 s6 : ff600000ffab55e0 s7 : ffffffff88384d00
[    0.000000][    C0]  s8 : 0000000000000003 s9 : ffffffff88384cc1 s10: ffffffff88384cc0
[    0.000000][    C0]  s11: ffffffff8a4daae0 t3 : ffffffff915e8b20 t4 : ffffffff915e8b20
[    0.000000][    C0]  t5 : ffffffff915e8b20 t6 : ffffffff915e8bc8 ssp : 0000000000000000
[    0.000000][    C0] status: 0000000200000100 badaddr: ffffffff86851c88 cause: 0000000000000003
[    0.000000][    C0] [<ffffffff86851c88>] sparse_init+0x58a/0x6fe
[    0.000000][    C0] [<ffffffff8683d396>] mm_core_init_early+0x116/0x1e30
[    0.000000][    C0] [<ffffffff86801edc>] start_kernel+0xd2/0x848

Convert MAX_FOLIO_VMEMMAP_ALIGN to the equivalent physical alignment before using it in VMEMMAP_ADDR_ALIGN. This keeps the existing round_down() logic while making the resulting vmemmap base satisfy the mask-alignment requirement.

Fixes 476849b0fba4 ("riscv/mm: align vmemmap to maximal folio size")
Cc Albert Ou <[email protected]>
Cc Alexandre Ghiti <[email protected]>
Cc David Hildenbrand <[email protected]>
Cc Guo Ren <[email protected]>
Cc Mike Rapoport <[email protected]>
Cc Muchun Song <[email protected]>
Cc Nam Cao <[email protected]>
Cc Palmer Dabbelt <[email protected]>
Cc Vishal Moola (Oracle) <[email protected]>
Assisted-by YuanSheng:DeepSeek-V4-Flash
---
 arch/riscv/mm/init.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/riscv/mm/init.c +2 -1
@@ -61,7 +61,8 @@ EXPORT_SYMBOL(phys_ram_base);
 
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
 #define VMEMMAP_ADDR_ALIGN	max(1ULL << SECTION_SIZE_BITS, \
-				    MAX_FOLIO_VMEMMAP_ALIGN)
+				    PFN_PHYS(MAX_FOLIO_VMEMMAP_ALIGN / \
+					     sizeof(struct page)))
 
 unsigned long vmemmap_start_pfn __ro_after_init;
 EXPORT_SYMBOL(vmemmap_start_pfn);
author maintainer
#302 lore raw

PATCH 302/438 powerpc/ps3: Fix map failure path in dma_ioc0_map_pages()

In reply to Greg Kroah-Hartman

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

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

From: Thorsten Blum <[email protected]>

commit 0bb024f11d120abff3e8db9144a585b9d7fb8459 upstream.

If lv1_put_iopte() fails in dma_ioc0_map_pages(), the error path decrements iopage but keeps using the failed mapping's offset. As a result, it repeatedly tries to invalidate the failed IOPTE slot and leaves the already installed IOPTEs valid.

Recompute offset and invalidate the installed IOPTEs instead.

---
 arch/powerpc/platforms/ps3/mm.c |    1 +
 1 file changed, 1 insertion(+)
arch/powerpc/platforms/ps3/mm.c +1 -0
@@ -615,6 +615,7 @@ static int dma_ioc0_map_pages(struct ps3
 
 fail_map:
 	for (iopage--; 0 <= iopage; iopage--) {
+		offset = (1 << r->page_size) * iopage;
 		lv1_put_iopte(0,
 			      c->bus_addr + offset,
 			      c->lpar_addr + offset,
author maintainer
#303 lore raw

PATCH 303/438 um: vector: fix use-after-free in vector_mmsg_rx()

In reply to Greg Kroah-Hartman

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

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

From: Michael Bommarito <[email protected]>

commit af421e9aed3920c7ac88c24daa48606c7112feca upstream.

When vector_mmsg_rx() discards a packet whose overlay header fails verify_header(), it frees the skb and continues the loop:

	if (header_check < 0) {
		dev_kfree_skb_irq(skb);
		vp->estats.rx_encaps_errors++;
		continue;
	}

The normal and short-packet paths fall through to the bottom of the loop body, which clears the consumed slot and advances the cursors:

	(*skbuff_vector) = NULL;
	mmsg_vector++;
	skbuff_vector++;

The verify_header() < 0 path skips that via continue, so the freed skb is left in skbuff_vector[] and the cursors do not advance. The next iteration reads the same slot, gets the freed skb, and frees it again, producing a refcount underflow / use-after-free in the RX path.

Discard the slot the same way the other paths do before continuing.

Only transports whose verify_header() can return negative are affected:
GRE and L2TPv3 do so on a cookie/session-id mismatch (raw/tap do not), so any peer on such a transport can trigger it without authentication.

Fixes 49da7e64f33e ("High Performance UML Vector Network Driver")
Assisted-by Claude:claude-opus-4-8
---
 arch/um/drivers/vector_kern.c |    3 +++
 1 file changed, 3 insertions(+)
arch/um/drivers/vector_kern.c +3 -0
@@ -995,6 +995,9 @@ static int vector_mmsg_rx(struct vector_
 				 */
 					dev_kfree_skb_irq(skb);
 					vp->estats.rx_encaps_errors++;
+					(*skbuff_vector) = NULL;
+					mmsg_vector++;
+					skbuff_vector++;
 					continue;
 				}
 				if (header_check > 0) {
author maintainer
#304 lore raw

PATCH 304/438 uprobes: Fix NULL pointer dereference in hprobe_expire()

In reply to Greg Kroah-Hartman

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

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

From: Breno Leitao <[email protected]>

commit cc679d7a6303e84d769f2afcde1fc51c51f127cd upstream.

Forking a task that has a pending uretprobe can oops the kernel with a NULL pointer dereference in the clone() path:

  BUG: kernel NULL pointer dereference, address: 0000000000000018
  Oops: 0002 [#1] SMP NOPTI
  RIP: 0010:hprobe_expire
  CR2: 0000000000000018
  Call Trace:
   uprobe_copy_process
   copy_process
   kernel_clone
   __x64_sys_clone
   do_syscall_64
   entry_SYSCALL_64_after_hwframe

This was found on real hosts on Meta fleet.

I've got the impression that this is what is happening:

CPU 1 CPU 2 (traced task)
----- -------------------
hit uprobe, prepare_uretprobe():
hprobe LEASED, refcount >= 1
uprobe_unregister()

    put_uprobe(): refcount -> 0
                                 fork() -> dup_utask()
                                   hprobe_expire(hprobe, true)
                                     try_get_uprobe() -> NULL
                                     get_uprobe(NULL)   <-- Oops

Only take the extra reference when the uprobe is non-NULL; a NULL means it is gone and is the correct value to return.

---
 kernel/events/uprobes.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/events/uprobes.c +1 -1
@@ -830,7 +830,7 @@ static struct uprobe *hprobe_expire(stru
 		if (try_cmpxchg(&hprobe->state, &hstate, uprobe ? HPROBE_STABLE : HPROBE_GONE)) {
 			/* We won the race, we are the ones to unlock SRCU */
 			__srcu_read_unlock(&uretprobes_srcu, hprobe->srcu_idx);
-			return get ? get_uprobe(uprobe) : uprobe;
+			return get && uprobe ? get_uprobe(uprobe) : uprobe;
 		}
 
 		/*
author maintainer
#305 lore raw

PATCH 305/438 veth: convert frag_list skbs before running XDP

In reply to Greg Kroah-Hartman

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

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

From: Matt Fleming <[email protected]>

commit d0d6415963040c401e7a7e4e482a698ba52448cb upstream.

A frag_list skb can reach veth with data_len set but nr_frags zero. veth_convert_skb_to_xdp_buff() only converts skbs that are shared, locked, have frags[], or do not have enough headroom. It later uses skb_is_nonlinear() to decide whether to set XDP_FLAGS_HAS_FRAGS and xdp_frags_size.

That exposes frag_list data to XDP as if it were stored in frags[], but frags[] is empty. AF_XDP copy mode can then trust the bogus XDP fragment metadata, walk an empty fragment entry, and crash in memcpy() from __xsk_rcv().

Route non-linear skbs through skb_pp_cow_data() before exposing them to XDP, and only advertise XDP frags when the resulting skb has frags[]. skb_copy_bits() already handles frag_list input, and skb_pp_cow_data() builds frags[] output with skb_add_rx_frag(), which is the representation XDP multi-buffer expects.

---
 drivers/net/veth.c |    4 ++--
 net/core/skbuff.c  |   18 ++++++++++++------
 2 files changed, 14 insertions(+), 8 deletions(-)
drivers/net/veth.c +2 -2
@@ -756,7 +756,7 @@ static int veth_convert_skb_to_xdp_buff(
 	u32 frame_sz;
 
 	if (skb_shared(skb) || skb_head_is_locked(skb) ||
-	    skb_shinfo(skb)->nr_frags ||
+	    skb_is_nonlinear(skb) ||
 	    skb_headroom(skb) < XDP_PACKET_HEADROOM) {
 		if (skb_pp_cow_data(rq->page_pool, pskb, XDP_PACKET_HEADROOM))
 			goto drop;
@@ -771,7 +771,7 @@ static int veth_convert_skb_to_xdp_buff(
 	xdp_prepare_buff(xdp, skb->head, skb_headroom(skb),
 			 skb_headlen(skb), true);
 
-	if (skb_is_nonlinear(skb)) {
+	if (skb_shinfo(skb)->nr_frags) {
 		skb_shinfo(skb)->xdp_frags_size = skb->data_len;
 		xdp_buff_set_frags_flag(xdp);
 	} else {
net/core/skbuff.c +12 -6
@@ -925,6 +925,18 @@ static void skb_clone_fraglist(struct sk
 		skb_get(list);
 }
 
+/**
+ * skb_pp_cow_data() - copy skb data into page-pool backed storage
+ * @pool: page pool to allocate from
+ * @pskb: pointer to skb pointer, replaced with the copied skb on success
+ * @headroom: headroom to reserve in the copied skb
+ *
+ * skb_copy_bits() handles both frags[] and frag_list input. If the copied
+ * skb remains non-linear, it uses frags[], which is the representation used
+ * by XDP multi-buffer.
+ *
+ * Return: 0 on success or a negative errno on failure.
+ */
 int skb_pp_cow_data(struct page_pool *pool, struct sk_buff **pskb,
 		    unsigned int headroom)
 {
@@ -934,12 +946,6 @@ int skb_pp_cow_data(struct page_pool *po
 	int err, i, head_off;
 	void *data;
 
-	/* XDP does not support fraglist so we need to linearize
-	 * the skb.
-	 */
-	if (skb_has_frag_list(skb))
-		return -EOPNOTSUPP;
-
 	max_head_size = SKB_WITH_OVERHEAD(PAGE_SIZE - headroom);
 	if (skb->len > max_head_size + MAX_SKB_FRAGS * PAGE_SIZE)
 		return -ENOMEM;
author maintainer
#306 lore raw

PATCH 306/438 vxlan: re-fetch eth header after route_shortcircuit()

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

commit 1395a676ec15a0a02a2a6d86602324f2d5fd41d5 upstream.

Before route_shortcircuit(), the eth header pointer is cached from eth_hdr(skb).

Inside route_shortcircuit(), pskb_may_pull() can be called, which may reallocate skb->head.

In this case, returning to vxlan_xmit() leaves the cached eth pointer pointing to freed memory, leading to a use-after-free when dereferencing eth->h_dest.

Fix this by updating eth = eth_hdr(skb) after calling route_shortcircuit().

---
 drivers/net/vxlan/vxlan_core.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/vxlan/vxlan_core.c +1 -0
@@ -2797,6 +2797,7 @@ static netdev_tx_t vxlan_xmit(struct sk_
 	    (ntohs(eth->h_proto) == ETH_P_IP ||
 	     ntohs(eth->h_proto) == ETH_P_IPV6)) {
 		did_rsc = route_shortcircuit(dev, skb);
+		eth = eth_hdr(skb);
 		if (did_rsc)
 			f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni);
 	}
author maintainer
#307 lore raw

PATCH 307/438 vxlan: unclone skb head before modifying eth header in route_shortcircuit()

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

commit 760d36e737f2b3867762f42af36c663f55babcc4 upstream.

When route_shortcircuit() performs L3 short-circuit routing, it modifies the Ethernet header of the skb in-place:

    memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, dev->addr_len);
    memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len);

If the incoming skb is cloned (for example by packet sockets, tcpdump, or dev_queue_xmit), modifying the Ethernet header without uncloning can corrupt the packet header for other readers holding a reference to the cloned skb.

Ensure the skb header is writable and unshared by calling skb_cow_head(skb, 0) prior to updating the Ethernet header. If skb_cow_head() fails, abort short-circuiting and return false to allow standard packet processing fallback.

---
 drivers/net/vxlan/vxlan_core.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/net/vxlan/vxlan_core.c +4 -0
@@ -2164,6 +2164,10 @@ static bool route_shortcircuit(struct ne
 
 		diff = !ether_addr_equal(eth_hdr(skb)->h_dest, n->ha);
 		if (diff) {
+			if (skb_cow_head(skb, 0)) {
+				neigh_release(n);
+				return false;
+			}
 			memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
 				dev->addr_len);
 			memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len);
author maintainer
#308 lore raw

PATCH 308/438 vxlan: use neigh_ha_snapshot() in route_shortcircuit()

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

commit 8eca411347e1d38964f9ed2c8d3b6ab0e7e4473d upstream.

The neighbour hardware address n->ha can be updated asynchronously by the neighbour subsystem, protected by n->ha_lock seqlock. Reading n->ha without holding the seqlock loop can lead to torn reads or reading a partially updated MAC address.

Use neigh_ha_snapshot() in route_shortcircuit() to safely copy n->ha under read_seqbegin()/read_seqretry() lock protection before using it.

Note that arp_reduce() and neigh_reduce() seem to have the same issue left for future patches.

---
 drivers/net/vxlan/vxlan_core.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/net/vxlan/vxlan_core.c +4 -2
@@ -2160,9 +2160,11 @@ static bool route_shortcircuit(struct ne
 	}
 
 	if (n) {
+		u8 haddr[ETH_ALEN];
 		bool diff;
 
-		diff = !ether_addr_equal(eth_hdr(skb)->h_dest, n->ha);
+		neigh_ha_snapshot(haddr, n, dev);
+		diff = !ether_addr_equal_unaligned(eth_hdr(skb)->h_dest, haddr);
 		if (diff) {
 			if (skb_cow_head(skb, 0)) {
 				neigh_release(n);
@@ -2170,7 +2172,7 @@ static bool route_shortcircuit(struct ne
 			}
 			memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
 				dev->addr_len);
-			memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len);
+			memcpy(eth_hdr(skb)->h_dest, haddr, dev->addr_len);
 		}
 		neigh_release(n);
 		return diff;
author maintainer
#309 lore raw

PATCH 309/438 vxlan: use pskb_network_may_pull() for transmit path header pulls

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

commit b9553558b48db54ac9273e6b98d7263ef5c1a329 upstream.

In vxlan_xmit(), arp_reduce(), and vxlan_mdb_entry_skb_get(), pskb_may_pull() was being called to verify the availability of network layer headers (ARP, IPv6/ND, IP/IPv6 MDB keys).

However, during transmit skb->data points to the MAC header, so skb_network_offset(skb) is ETH_HLEN (14 bytes). Using pskb_may_pull(skb, len) only checks len bytes from skb->data rather than skb_network_offset(skb) + len, which can leave part of the network header in non-linear frags.

Replace these remaining pskb_may_pull() calls with pskb_network_may_pull() to properly account for the MAC header offset.

Fixes e4f67addf158 ("add DOVE extensions for VXLAN")
Fixes f564f45c4518 ("vxlan: add ipv6 proxy support")
Fixes 0f83e69f44bf ("vxlan: Add MDB data path support")
---
 drivers/net/vxlan/vxlan_core.c |    6 +++---
 drivers/net/vxlan/vxlan_mdb.c  |    4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)
drivers/net/vxlan/vxlan_core.c +3 -3
@@ -1851,7 +1851,7 @@ static int arp_reduce(struct net_device
 	if (dev->flags & IFF_NOARP)
 		goto out;
 
-	if (!pskb_may_pull(skb, arp_hdr_len(dev))) {
+	if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) {
 		dev_dstats_tx_dropped(dev);
 		vxlan_vnifilter_count(vxlan, vni, NULL,
 				      VXLAN_VNI_STATS_TX_DROPS, 0);
@@ -2764,8 +2764,8 @@ static netdev_tx_t vxlan_xmit(struct sk_
 			return arp_reduce(dev, skb, vni);
 #if IS_ENABLED(CONFIG_IPV6)
 		else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
-			 pskb_may_pull(skb, sizeof(struct ipv6hdr) +
-					    sizeof(struct nd_msg)) &&
+			 pskb_network_may_pull(skb, sizeof(struct ipv6hdr) +
+						    sizeof(struct nd_msg)) &&
 			 ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
 			struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1);
 
drivers/net/vxlan/vxlan_mdb.c +2 -2
@@ -1631,7 +1631,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_
 
 	switch (skb->protocol) {
 	case htons(ETH_P_IP):
-		if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
 			return NULL;
 		group.dst.sa.sa_family = AF_INET;
 		group.dst.sin.sin_addr.s_addr = ip_hdr(skb)->daddr;
@@ -1640,7 +1640,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_
 		break;
 #if IS_ENABLED(CONFIG_IPV6)
 	case htons(ETH_P_IPV6):
-		if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+		if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
 			return NULL;
 		group.dst.sa.sa_family = AF_INET6;
 		group.dst.sin6.sin6_addr = ipv6_hdr(skb)->daddr;
author maintainer
#310 lore raw

PATCH 310/438 vxlan: use pskb_network_may_pull() in route_shortcircuit()

In reply to Greg Kroah-Hartman

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

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

From: Eric Dumazet <[email protected]>

commit 26bb2dd0a8839617e2c79ffbbe1923f8e4bab9fb upstream.

route_shortcircuit() currently calls pskb_may_pull(skb, sizeof(struct iphdr)) (or ipv6hdr), which checks if bytes are available starting from skb->data.

However, in vxlan_xmit(), skb->data points to the MAC header, so skb_network_offset(skb) is ETH_HLEN (14 bytes). Using pskb_may_pull(skb, 20) only checks 20 bytes from skb->data (which is 14 bytes MAC header + 6 bytes of IP header), leaving the rest of the IP header potentially un-pulled in non-linear frags. Subsequent dereferences of ip_hdr(skb)->daddr can read beyond the pulled linear buffer length.

Fix this by using pskb_network_may_pull(), which adds skb_network_offset(skb) to the length check to ensure the full network header is present in the linear buffer.

---
 drivers/net/vxlan/vxlan_core.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/vxlan/vxlan_core.c +2 -2
@@ -2112,7 +2112,7 @@ static bool route_shortcircuit(struct ne
 	{
 		struct iphdr *pip;
 
-		if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
 			return false;
 		pip = ip_hdr(skb);
 		n = neigh_lookup(&arp_tbl, &pip->daddr, dev);
@@ -2138,7 +2138,7 @@ static bool route_shortcircuit(struct ne
 		 */
 		if (!ipv6_mod_enabled())
 			return false;
-		if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+		if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
 			return false;
 		pip6 = ipv6_hdr(skb);
 		n = neigh_lookup(&nd_tbl, &pip6->daddr, dev);
author maintainer
#311 lore raw

PATCH 311/438 ublk: reset kernel-owned dev_info fields in ublk_ctrl_add_dev()

In reply to Greg Kroah-Hartman

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

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

From: Ming Lei <[email protected]>

commit e65848e4ce352bac9e3465099354c8b8f845391f upstream.

ublk_ctrl_add_dev() memcpy()s the userspace ublksrv_ctrl_dev_info into ub->dev_info and then fixes up the fields the driver owns, but misses ->state and ->ublksrv_pid.

A device added with ->state = UBLK_S_DEV_LIVE passes the "->state != UBLK_S_DEV_DEAD" test that ublk_stop_dev_unlocked() uses as its proxy for "a disk is attached", while ->ub_disk is still NULL, so DEL_DEV right after ADD_DEV oopses in del_gendisk(). UBLK_S_DEV_QUIESCED plus UBLK_F_USER_RECOVERY dies one step earlier, in ublk_force_abort_dev(). A poisoned ->state also gets START_USER_RECOVERY and the char device read/write path onto a device that was never started, and wedges START_DEV at -EEXIST. A poisoned ->ublksrv_pid just makes GET_DEV_INFO report an unrelated task as the ublk server.

Reset both after the memcpy(), as ublk_detach_disk() does. Userspace only ever reads these back, so correcting them silently breaks nothing.

ADD_DEV has copied ->state in unsanitized since ublk was merged, but back then it was harmless: the gendisk was allocated during ADD_DEV, and both teardown and the START_DEV -EEXIST check keyed off disk_live() rather than ->state. The oops became reachable once the disk allocation moved to START_DEV and those checks switched to ->state.

---
 drivers/block/ublk_drv.c |    9 +++++++++
 1 file changed, 9 insertions(+)
drivers/block/ublk_drv.c +9 -0
@@ -4758,6 +4758,15 @@ static int ublk_ctrl_add_dev(const struc
 	ub->dev_info.dev_id = ub->ub_number;
 
 	/*
+	 * ->state and ->ublksrv_pid are owned by the driver and only read back
+	 * by userspace, but they come from the copied-in dev_info, so reset
+	 * them. Otherwise a device added with ->state != DEAD looks live while
+	 * ->ub_disk is still NULL.
+	 */
+	ub->dev_info.state = UBLK_S_DEV_DEAD;
+	ub->dev_info.ublksrv_pid = -1;
+
+	/*
 	 * 64bit flags will be copied back to userspace as feature
 	 * negotiation result, so have to clear flags which driver
 	 * doesn't support yet, then userspace can get correct flags
author maintainer
#312 lore raw

PATCH 312/438 tracing: Check return value of __register_event() in trace_module_add_events()

In reply to Greg Kroah-Hartman

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

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

From: Masami Hiramatsu (Google) <[email protected]>

commit ac8719969e6c3c54e939834df812bc41f25453cf upstream.

trace_module_add_events() ignores the return value of __register_event() and unconditionally calls __add_event_to_tracers() for each event.

If __register_event() fails (for example, if event_init() fails), the trace_event_call is not added to ftrace_events list, but __add_event_to_tracers() still creates a trace_event_file pointing to it. If module loading subsequently fails and module memory is freed, tracing state retains a stale trace_event_call pointer in trace_event_file, leading to a use-after-free when tracefs or tracing subsystem operations are later executed.

Fix this by checking the return value of __register_event() and only calling __add_event_to_tracers() if event registration succeeded.

Fixes ae63b31e4d0e ("tracing: Separate out trace events from global variables")
Assisted-by Antigravity:gemini-3.5-flash
---
 kernel/trace/trace_events.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/trace/trace_events.c +2 -2
@@ -3933,8 +3933,8 @@ static void trace_module_add_events(stru
 	end = mod->trace_events + mod->num_trace_events;
 
 	for_each_event(call, start, end) {
-		__register_event(*call, mod);
-		__add_event_to_tracers(*call);
+		if (!__register_event(*call, mod))
+			__add_event_to_tracers(*call);
 	}
 
 	update_cache_events(mod);
author maintainer
#313 lore raw

PATCH 313/438 tracing/filters: Fix false positive match in regex_match_full()

In reply to Greg Kroah-Hartman

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

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

From: Masami Hiramatsu (Google) <[email protected]>

commit c22c7b735f9810ad276014f788f9aa5c879ec238 upstream.

regex_match_full() calls strncmp(str, r->pattern, len) where len is the target field buffer size. When len is smaller than r->len (the filter pattern length), strncmp() checks only len bytes of r->pattern against str. If those len bytes match, strncmp() returns 0, resulting in a false-positive match where a shorter string in a fixed-size field matches a longer filter pattern.

For example, a 4-byte static string field containing "abcd" matched the filter pattern "abcdefgh" because strncmp("abcd", "abcdefgh", 4) returned 0. In this case, @len does NOT include '\0' because it is fixed-size array.

Fix this by returning 0 (no match) early when len < r->len.

Fixes 1889d20922d1 ("tracing/filters: Provide basic regex support")
Assisted-by Antigravity:gemini-3.5-flash
---
 kernel/trace/trace_events_filter.c |    3 +++
 1 file changed, 3 insertions(+)
kernel/trace/trace_events_filter.c +3 -0
@@ -1027,6 +1027,9 @@ static int regex_match_full(char *str, s
 	if (!len)
 		return strcmp(str, r->pattern) == 0;
 
+	if (len < r->len)
+		return 0;
+
 	return strncmp(str, r->pattern, len) == 0;
 }
 
author maintainer
#314 lore raw

PATCH 314/438 spi: spi-qpic-snand: write the feature value before executing SET_FEATURE

In reply to Greg Kroah-Hartman

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

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

From: Stanislaw Pal <[email protected]>

commit 8fd62901d6bf03f274a49dd0060793cc07dd51b0 upstream.

qcom_spi_send_cmdaddr() programs NAND_FLASH_CMD/NAND_EXEC_CMD and submits the descriptors, which makes the controller execute the command immediately. For SPINAND_SET_FEATURE the value to be written is only placed into NAND_FLASH_FEATURES afterwards, by qcom_spi_io_op(), in a second submission - so the chip is programmed with whatever that register happened to hold from a previous operation, and the intended value is only applied by the *next* SET_FEATURE.

Measured on a TP-Link Archer AX55 v1 (IPQ5018, ESMT F50L1G41LB): writing 0x40 to the configuration register (0xb0) leaves the chip at 0x00, and the subsequent write of 0x00 leaves it at 0x40 - every write lands one operation late.

This stayed unnoticed until v6.18 added SPI-NAND OTP support together with OTP entries for ESMT chips. spinand_otp_rw() enables OTP mode, reads, and disables it again, and mtd_otp_nvmem_add() does this during MTD registration. With the off-by-one, the "disable" write actually applies the previously requested value, so CFG_OTP_ENABLE ends up set:
the chip stays in OTP mode, every subsequent array read returns the OTP area instead of the array (UBI reports an empty device) and all writes fail with -EIO because the OTP area is write protected. On this board that makes the whole flash unusable and the device unbootable.

Write the feature value into NAND_FLASH_FEATURES as part of the same transaction, before NAND_EXEC_CMD. While at it, copy only the bytes the operation actually carries - the previous code dereferenced a 4-byte pointer on a one-byte buffer (spinand->scratchbuf).

With this patch the flash contents read back bit-identical to a known-good dump of the same board taken under the vendor firmware (md5-verified across partitions), and writes work.

Fixes 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface")
---
 drivers/spi/spi-qpic-snand.c |   22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)
drivers/spi/spi-qpic-snand.c +18 -4
@@ -1358,6 +1358,22 @@ static int qcom_spi_send_cmdaddr(struct
 	snandc->regs->addr0 = cpu_to_le32(op->addr.val);
 	snandc->regs->addr1 = cpu_to_le32(0);
 
+	/*
+	 * The feature value has to reach NAND_FLASH_FEATURES before the
+	 * command is executed, otherwise the controller programs the chip
+	 * with whatever the register happened to hold from a previous
+	 * operation.
+	 */
+	if (opcode == SPINAND_SET_FEATURE) {
+		u32 ftr = 0;
+
+		memcpy(&ftr, op->data.buf.out,
+		       min_t(size_t, op->data.nbytes, sizeof(ftr)));
+		snandc->regs->flash_feature = cpu_to_le32(ftr);
+		qcom_write_reg_dma(snandc, &snandc->regs->flash_feature,
+				   NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL);
+	}
+
 	qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
 	qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
 
@@ -1395,10 +1411,8 @@ static int qcom_spi_io_op(struct qcom_na
 		copy_ftr = true;
 		break;
 	case SPINAND_SET_FEATURE:
-		snandc->regs->flash_feature = cpu_to_le32(*(u32 *)op->data.buf.out);
-		qcom_write_reg_dma(snandc, &snandc->regs->flash_feature,
-				   NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL);
-		break;
+		/* fully handled by qcom_spi_send_cmdaddr() */
+		return 0;
 	case SPINAND_PROGRAM_EXECUTE:
 	case SPINAND_WRITE_EN:
 	case SPINAND_RESET:
author maintainer
#315 lore raw

PATCH 315/438 spi: qcom-qspi: Correct max DMA length to avoid 64K boundary failure

In reply to Greg Kroah-Hartman

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

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

From: Vijaya Krishna Nivarthi <[email protected]>

commit 90ef2f2961c2dc55957dafe2f53b3efdb4675efc upstream.

The maximum size for a DMA data descriptor is 64KB-1 because the size field in HW is 16 bits wide. For this reason, transfers fail at 64KB and beyond.

Lower max_dma_len to 60KB so larger transfers are split into multiple DMA blocks and do not hit the failing 64KB boundary. 60KB is chosen as a safe round number below the 64KB-1 hardware limit while satisfying alignment requirements.

Tested on x1e80100 (Hamoa) with SPI-NOR flash (/dev/mtd0):

Without patch:
dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=32768 count=2 # works dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=65536 count=1 # fails

With patch:
dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=65536 count=1 # works

---
 drivers/spi/spi-qcom-qspi.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/spi/spi-qcom-qspi.c +2 -1
@@ -759,7 +759,8 @@ static int qcom_qspi_probe(struct platfo
 		return dev_err_probe(dev, ret, "could not set DMA mask\n");
 
 	host->max_speed_hz = 300000000;
-	host->max_dma_len = 65536; /* as per HPG */
+	/* as per HPG, it is 64KB, limit to 60KB to avoid boundary condition failures */
+	host->max_dma_len = 0xf000;
 	host->dma_alignment = QSPI_ALIGN_REQ;
 	host->num_chipselect = QSPI_NUM_CS;
 	host->bus_num = -1;
author maintainer
#316 lore raw

PATCH 316/438 selftests/mm: fix potential wild pointer access of getline due to missing init

In reply to Greg Kroah-Hartman

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

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

From: Chris Gellermann <[email protected]>

commit 9f1d75a4ce04095afdb63d8e540092ff8151dacf upstream.

This is another occurrence of using getline where the code assumes that getline allocates memory to store the line, but the pointer passed to it is uninitialized and potentially a non-null pointer. This violates the Open Group Spec[1] and caused a segfault in a similar situation in selftest/clone3/clone3_set_tid. Fix it by initializing the line pointer to NULL.

The issue has been found by simply grepping through the selftest code after running into the issue in clone3_set_tid. Whether it segfaults in its current state is unknown to me. But it's good to be addressed due to defensive reasons.

---
 tools/testing/selftests/mm/mlock-random-test.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/mm/mlock-random-test.c +1 -1
@@ -84,7 +84,7 @@ int get_proc_locked_vm_size(void)
 int get_proc_page_size(unsigned long addr)
 {
 	FILE *smaps;
-	char *line;
+	char *line = NULL;
 	unsigned long mmupage_size = 0;
 	size_t size;
 
author maintainer
#317 lore raw

PATCH 317/438 selftests/clone3: fix wild pointer access of getline due to missing init

In reply to Greg Kroah-Hartman

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

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

From: Chris Gellermann <[email protected]>

commit 8f6f9fd93cd7a5dd607ad5cd910476dd68fff3ed upstream.

Patch series "selftests: Add missing initalization of pointer passed to getline", v2.

This patch (of 2):

Clone3_set_tid uses getline(&line, ...) in a loop to read the child's process status. The code expects that getline allocates the buffer for the line on the first loop iteration. According to the Open Group Spec[1], char *line has to be null pointer for this:

Quoted 3 lines

ssize_t getline(char **restrict lineptr, ...);
If *lineptr is a null pointer or if the object pointed to by *lineptr is of insufficient size, an object shall be allocated as if by

malloc()

Quoted 1 line

or the object shall be reallocated as if by realloc()[...].

However, char *line is only declared, leading to an undefined value that is potentially non-null. In an example run with Musl v1.2.6, the realloc call[2] of getdelim, which implements getline, triggers a segfault:

./run_kselftest.sh --test clone3:clone3_set_tid

[ 1366.165898] kselftest: Running tests in clone3
...
[ 1367.799244] clone3_set_tid[811]: unhandled signal 11 code 0x1 at
0x0000000000000000 in libc.so[68184,3fbf69f000+4c000]
[ 1367.802808] CPU: 0 UID: 0 PID: 811 Comm: clone3_set_tid Not tainted
..
[ 1367.804188]  epc: 0x0000003fbf6b0184
[ 1367.804188]  ra : 0x0000003fbf6d4664
[ 1367.804188]  sp : 0x0000003fce5f2e40
[ 1367.805314]  gp : 0x0000002aaab0dfb8
[ 1367.805314]  tp : 0x0000003fbf6f14a8
[ 1367.805314]  t0 : 0x0000003fbf63d000

...

Looking at the realloc implementation, Musl mallocs for a null pointer memory. But for a non-null pointer, it assumes it's passed a valid pointer to the heap and tries to access its meta-data. This leads to the segfault we see:

void *realloc(void *p, size_t n)

{
        if (!p) return malloc(n);
        if (size_overflows(n)) return 0;

        struct meta *g = get_meta(p);
        ...
}

Fix this by properly initializing the line pointer to NULL.

---
 tools/testing/selftests/clone3/clone3_set_tid.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/clone3/clone3_set_tid.c +1 -1
@@ -141,7 +141,7 @@ int main(int argc, char *argv[])
 {
 	FILE *f;
 	char buf;
-	char *line;
+	char *line = NULL;
 	int status;
 	int ret = -1;
 	size_t len = 0;
author maintainer
#318 lore raw

PATCH 318/438 scsi: libsas: terminate deferred commands on time out

In reply to Greg Kroah-Hartman

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

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

From: Damien Le Moal <[email protected]>

commit de202d2251bc181c6019b1ad3c0ba8133e5ec68d upstream.

If a command times out while we have deferred non-NCQ commands waiting to be issued, the SCSI EH task is not immediately woken up as the waiting deferred commands are never issued nor completed, thus leaving the SCSI host in a busy state (shost->host_failed != scsi_host_busy(shost)) which prevents the SCSI EH task from being woken up. Eventually, when the deferred commands also time out, the SCSI EH task is woken up and the timeout processing occurs.

Avoid this unnecessary additional SCSI EH wake up time with the same method as implemented in libata-scsi, using the eh_timed_out SCSI host template operation. The function sas_eh_timed_out() implements this operation and executes the function ata_scsi_retry_deferred_qc() for SATA devices.

---
 drivers/ata/libata-scsi.c           |    5 +++--
 drivers/scsi/libsas/sas_scsi_host.c |   15 +++++++++++++++
 include/linux/libata.h              |    2 ++
 include/scsi/libsas.h               |    2 ++
 4 files changed, 22 insertions(+), 2 deletions(-)
drivers/ata/libata-scsi.c +3 -2
@@ -1784,8 +1784,8 @@ static void ata_scsi_schedule_deferred_q
 		queue_work(system_highpri_wq, &link->deferred_qc_work);
 }
 
-static enum scsi_timeout_action
-ata_scsi_retry_deferred_qc(struct ata_port *ap, struct scsi_cmnd *scmd)
+enum scsi_timeout_action ata_scsi_retry_deferred_qc(struct ata_port *ap,
+						    struct scsi_cmnd *scmd)
 {
 	enum scsi_timeout_action action;
 	unsigned long flags;
@@ -1796,6 +1796,7 @@ ata_scsi_retry_deferred_qc(struct ata_po
 
 	return action;
 }
+EXPORT_SYMBOL_GPL(ata_scsi_retry_deferred_qc);
 
 enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *scmd)
 {
drivers/scsi/libsas/sas_scsi_host.c +15 -0
@@ -502,6 +502,21 @@ int sas_eh_target_reset_handler(struct s
 }
 EXPORT_SYMBOL_GPL(sas_eh_target_reset_handler);
 
+/*
+ * Handle deferred QCs in case of a command timeout.
+ * See ata_scsi_eh_timed_out() for details.
+ */
+enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd)
+{
+	struct domain_device *dev = cmd_to_domain_dev(cmd);
+
+	if (dev_is_sata(dev))
+		return ata_scsi_retry_deferred_qc(dev->sata_dev.ap, cmd);
+
+	return SCSI_EH_NOT_HANDLED;
+}
+EXPORT_SYMBOL_GPL(sas_eh_timed_out);
+
 /* Try to reset a device */
 static int try_to_reset_cmd_device(struct scsi_cmnd *cmd)
 {
include/linux/libata.h +2 -0
@@ -1152,6 +1152,8 @@ extern int ata_scsi_ioctl(struct scsi_de
 #endif
 extern enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *h,
 					     struct scsi_cmnd *cmd);
+enum scsi_timeout_action ata_scsi_retry_deferred_qc(struct ata_port *ap,
+						    struct scsi_cmnd *scmd);
 enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *cmd);
 #if IS_REACHABLE(CONFIG_ATA)
 bool ata_scsi_dma_need_drain(struct request *rq);
include/scsi/libsas.h +2 -0
@@ -704,6 +704,7 @@ void sas_task_abort(struct sas_task *);
 int sas_eh_abort_handler(struct scsi_cmnd *cmd);
 int sas_eh_device_reset_handler(struct scsi_cmnd *cmd);
 int sas_eh_target_reset_handler(struct scsi_cmnd *cmd);
+enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd);
 
 extern void sas_target_destroy(struct scsi_target *);
 extern int sas_sdev_init(struct scsi_device *);
@@ -742,6 +743,7 @@ void sas_notify_phy_event(struct asd_sas
 	.this_id			= -1,				\
 	.eh_device_reset_handler	= sas_eh_device_reset_handler,	\
 	.eh_target_reset_handler	= sas_eh_target_reset_handler,	\
+	.eh_timed_out			= sas_eh_timed_out,		\
 	.target_destroy			= sas_target_destroy,		\
 	.ioctl				= sas_ioctl,			\
 
author maintainer
#319 lore raw

PATCH 319/438 scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write

In reply to Greg Kroah-Hartman

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

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

From: Ibrahim Hashimov <[email protected]>

commit 93dde0bf2f39a0f9f57fd610aa3201ce5b753433 upstream.

resp_report_zones() sizes the reply buffer from the CDB allocation length. The v3 fix rounds alloc_len up with ALIGN() before deriving the descriptor count:

	rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) -
			 RZONES_DESC_HD) >> ilog2(RZONES_DESC_HD);
	arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1);

For alloc_len in 0xFFFFFFC1..0xFFFFFFFF, ALIGN() rounds up to 0x100000000, so arr_len is 4 GB. On 32-bit, kzalloc()'s size_t is 32-bit and truncates 0x100000000 to 0; kzalloc(0) returns ZERO_SIZE_PTR, which passes the !arr check, and desc = arr + 64 is then dereferenced in the loop -> out-of-bounds write / panic.

Clamp rep_max_zones to devip->nr_zones. The loop already stops at sdebug_capacity (after nr_zones zones), so a report can never hold more than nr_zones descriptors; the clamp does not change the report, it only bounds arr_len to (nr_zones + 1) * RZONES_DESC_HD, a real device property that can never reach 0x100000000.

Fixes 7db0e0c8190a ("scsi: scsi_debug: Fix buffer size of REPORT ZONES command")
Assisted-by AuditCode-AI:2026.07
---
 drivers/scsi/scsi_debug.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/scsi/scsi_debug.c +6 -2
@@ -5898,6 +5898,7 @@ static int resp_report_zones(struct scsi
 	u32 alloc_len, rep_opts, rep_len;
 	bool partial;
 	u64 lba, zs_lba;
+	u64 arr_len;
 	u8 *arr = NULL, *desc;
 	u8 *cmd = scp->cmnd;
 	struct sdeb_zone_state *zsp = NULL;
@@ -5919,9 +5920,12 @@ static int resp_report_zones(struct scsi
 		return check_condition_result;
 	}
 
-	rep_max_zones = (alloc_len - 64) >> ilog2(RZONES_DESC_HD);
+	rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >>
+			ilog2(RZONES_DESC_HD);
+	rep_max_zones = min_t(unsigned int, rep_max_zones, devip->nr_zones);
+	arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1);
 
-	arr = kzalloc(alloc_len, GFP_ATOMIC | __GFP_NOWARN);
+	arr = kzalloc(arr_len, GFP_ATOMIC | __GFP_NOWARN);
 	if (!arr) {
 		mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
 				INSUFF_RES_ASCQ);
author maintainer
#320 lore raw

PATCH 320/438 scsi: ufs: dt-bindings: Add missing mcq reg for qcom,sa8255p-ufshc

In reply to Greg Kroah-Hartman

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

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

From: Shawn Guo <[email protected]>

commit 4d5282c06ca198319c2de41b10511ddcb8068f42 upstream.

Qualcomm UFS controller found on SoCs SA8255P/SA8797P has a MCQ I/O address space. It should be defined in the bindings even though Linux driver currently doesn't utilize it. Fix the binding before it gets adopted by DTS.

Fixes e2725ed2a7fb ("scsi: ufs: dt-bindings: Document bindings for SA8255P UFS Host Controller")
---
 Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml +8 -1
@@ -14,7 +14,14 @@ properties:
     const: qcom,sa8255p-ufshc
 
   reg:
-    maxItems: 1
+    minItems: 1
+    maxItems: 2
+
+  reg-names:
+    minItems: 1
+    items:
+      - const: std
+      - const: mcq
 
   interrupts:
     maxItems: 1
author maintainer
#321 lore raw

PATCH 321/438 sctp: reject stale cookies with mismatched verification tags

In reply to Greg Kroah-Hartman

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

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

From: Yuxiang Yang <[email protected]>

commit 9d8da8e0a9bce4a340af60dd0446bc7eb8d07587 upstream.

sctp_unpack_cookie() skips cookie expiration checks whenever an association already exists. This is broader than the exception in RFC 9260 Section 5.2.4.

For an existing association, Section 5.2.4 permits an expired State Cookie only when both Verification Tags in the cookie match the current association. Otherwise, the packet SHOULD be discarded and a Stale Cookie ERROR MUST be sent.

The broad check lets an expired Action A restart cookie reach sctp_sf_do_dupcook_a(). In a runtime test with the default 60 second cookie lifetime, replaying such a cookie after 65 seconds returned a COOKIE-ACK and restarted the association.

Check cookie expiration unless both Verification Tags match. This preserves the Action D exception for a lost COOKIE ACK while rejecting expired cookies in all other cases.

---
 net/sctp/sm_make_chunk.c |   11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
net/sctp/sm_make_chunk.c +7 -4
@@ -1802,9 +1802,9 @@ struct sctp_association *sctp_unpack_coo
 		goto fail;
 	}
 
-	/* Check to see if the cookie is stale.  If there is already
-	 * an association, there is no need to check cookie's expiration
-	 * for init collision case of lost COOKIE ACK.
+	/* Check to see if the cookie is stale.  RFC 9260 Section 5.2.4
+	 * exempts an expired cookie only when both Verification Tags match
+	 * the current association.
 	 * If skb has been timestamped, then use the stamp, otherwise
 	 * use current time.  This introduces a small possibility that
 	 * a cookie may be considered expired, but this would only slow
@@ -1815,7 +1815,10 @@ struct sctp_association *sctp_unpack_coo
 	else
 		kt = ktime_get_real();
 
-	if (!asoc && ktime_before(bear_cookie->expiration, kt)) {
+	if ((!asoc ||
+	     asoc->c.my_vtag != bear_cookie->my_vtag ||
+	     asoc->c.peer_vtag != bear_cookie->peer_vtag) &&
+	    ktime_before(bear_cookie->expiration, kt)) {
 		suseconds_t usecs = ktime_to_us(ktime_sub(kt, bear_cookie->expiration));
 		__be32 n = htonl(usecs);
 
author maintainer
#322 lore raw

PATCH 322/438 sctp: prevent peer transport count overflow

In reply to Greg Kroah-Hartman

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

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

From: Asim Viladi Oglu Manizada <[email protected]>

commit bd0e9289e2642f6a5c54faad304ce0f41e926d22 upstream.

sctp_assoc_add_peer() increments the association's 16-bit transport_count for every new unique peer. Adding the 65,536th transport wraps the count to zero.

SCTP sock_diag uses transport_count to reserve the INET_DIAG_PEERS payload, then copies one sockaddr_storage for every entry in transport_addr_list. After the wrap, a diagnostic dump reserves an empty payload and writes 8 MiB of peer addresses past the skb tail.

Reject a new unique peer when transport_count has reached U16_MAX. Perform the check after the existing-peer lookup so a duplicate address continues to return its existing transport at the limit.

---
 net/sctp/associola.c |    3 +++
 1 file changed, 3 insertions(+)
net/sctp/associola.c +3 -0
@@ -614,6 +614,9 @@ struct sctp_transport *sctp_assoc_add_pe
 		return peer;
 	}
 
+	if (asoc->peer.transport_count == U16_MAX)
+		return NULL;
+
 	peer = sctp_transport_new(asoc->base.net, addr, gfp);
 	if (!peer)
 		return NULL;
author maintainer
#323 lore raw

PATCH 323/438 hwmon: (npcm750-pwm-fan): stop fan timer on device detach

In reply to Greg Kroah-Hartman

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

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

From: Hongyan Xu <[email protected]>

commit f27f6976ea269219c1259a7c2f8c6dfe782540a3 upstream.

When a fan tach channel is present, npcm7xx_pwm_fan_probe() starts fan_timer. The timer callback polls tach state and rearms the timer, but the driver has no remove callback or devm cleanup action to stop it. On device detach, the devm-managed driver data and I/O mappings can be released while the timer is still pending or running.

Register a devm cleanup action before starting the timer and shut the timer down synchronously from that action.

This issue was found by a static analysis tool.

---
 drivers/hwmon/npcm750-pwm-fan.c |   11 +++++++++++
 1 file changed, 11 insertions(+)
drivers/hwmon/npcm750-pwm-fan.c +11 -0
@@ -358,6 +358,11 @@ static void npcm7xx_fan_polling(struct t
 	add_timer(&data->fan_timer);
 }
 
+static void npcm7xx_fan_cleanup(void *timer)
+{
+	timer_shutdown_sync(timer);
+}
+
 static inline void npcm7xx_fan_compute(struct npcm7xx_pwm_fan_data *data,
 				       u8 fan, u8 cmp, u8 fan_id, u8 flag_int,
 				       u8 flag_mode, u8 flag_clear)
@@ -1018,6 +1023,12 @@ static int npcm7xx_pwm_fan_probe(struct
 				msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
 			timer_setup(&data->fan_timer,
 				    npcm7xx_fan_polling, 0);
+			ret = devm_add_action_or_reset(dev,
+						       npcm7xx_fan_cleanup,
+						       &data->fan_timer);
+			if (ret)
+				return ret;
+
 			add_timer(&data->fan_timer);
 			break;
 		}
author maintainer
#324 lore raw

PATCH 324/438 hwmon: (pmbus/core) notify on the hwmon device, not the i2c client

In reply to Greg Kroah-Hartman

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

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

From: Vincent Jardin <[email protected]>

commit a64a7e8a0b012ba81b0eadbd7afc84ab0dbfd70c upstream.

pmbus_notify() calls sysfs_notify() and kobject_uevent() on the i2c client's kobject, but the alarm attributes live on the hwmon class device registered by pmbus_do_probe(). Notifying the parent i2c device is a no-op for both poll(POLLPRI) waiters and udev listeners: the named attribute does not exist on that kobject.

Notify the hwmon device instead, so poll() wakes up and "change" uevents fire on the inX_alarm/tempX_alarm attributes when SMBALERT# reports a fault.

Fixes f469bde9afd1 ("hwmon: (pmbus/core) Notify hwmon events")
---
 drivers/hwmon/pmbus/pmbus_core.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/hwmon/pmbus/pmbus_core.c +3 -2
@@ -2985,8 +2985,9 @@ static void pmbus_notify(struct pmbus_da
 
 		if (reg == sreg && page == spage && (smask & flags)) {
 			dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
-			sysfs_notify(&data->dev->kobj, NULL, da->attr.name);
-			kobject_uevent(&data->dev->kobj, KOBJ_CHANGE);
+			sysfs_notify(&data->hwmon_dev->kobj, NULL,
+				     da->attr.name);
+			kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
 			flags &= ~smask;
 		}
 
author maintainer
#325 lore raw

PATCH 325/438 i2c: amd-mp2: Unregister callback on adapter add failure

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 82048795242f04275a3f49ffc66ad851b6120954 upstream.

amd_mp2_register_cb() stores the platform I2C context in the MP2 PCI driver's callback table before the adapter is registered. If i2c_add_adapter() fails, probe returns and devres frees the context, but the PCI driver can still dereference the stale pointer from its IRQ and system-sleep callbacks.

Unregister the callback before returning the adapter registration error.

Fixes 529766e0a011 ("i2c: Add drivers for the AMD PCIe MP2 I2C controller")
Cc <[email protected]> # v5.2+
---
 drivers/i2c/busses/i2c-amd-mp2-plat.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-amd-mp2-plat.c +3 -1
@@ -316,8 +316,10 @@ static int i2c_amd_probe(struct platform
 
 	amd_mp2_pm_runtime_put(mp2_dev);
 
-	if (ret < 0)
+	if (ret < 0) {
 		dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret);
+		amd_mp2_unregister_cb(&i2c_dev->common);
+	}
 
 	return ret;
 }
author maintainer
#326 lore raw

PATCH 326/438 i2c: designware: defer probe if child GpioInt controllers are not bound

In reply to Greg Kroah-Hartman

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

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

From: Hardik Prakash <[email protected]>

commit 0a4bb2abc3e56d7be6e69b050c88ba52c87e22bf upstream.

I2C controllers may have child devices with GpioInt resources that depend on GPIO controllers being fully initialized. If the I2C controller probes and enumerates children before the referenced GPIO controller has completed probe, GPIO interrupts may not be properly configured, leading to device failures.

On Lenovo Yoga 7 14AGP11, the WACF2200 touchscreen (child of AMDI0010:02) has a GpioInt resource pointing to GPIO 157 on the pinctrl-amd controller (AMDI0030:00). When i2c-designware probes AMDI0010:02 before pinctrl-amd finishes initializing, I2C transactions fail with lost arbitration errors:

  0.285952  amd_gpio_probe: registering gpiochip  <- GPIO chip visible
  0.287121  amd_gpio_probe: requesting parent IRQ <- probe still running
  0.301454  AMDI0010:02 dw_i2c_plat_probe: start  <- races here
  2.348157  lost arbitration

Add a dependency check that walks ACPI child devices and defers probe until any referenced GPIO controller is bound.

Fixes 3812a9e84265 ("pinctrl-amd: enable IRQ for WACF2200 touchscreen on Lenovo Yoga 7 14AGP11")
Assisted-by Claude:claude-sonnet-5
Assisted-by DeepSeek:deepseek-v4-pro
Cc <[email protected]> # v7.1+
---
 drivers/i2c/busses/i2c-designware-platdrv.c |   80 ++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)
drivers/i2c/busses/i2c-designware-platdrv.c +80 -0
@@ -8,12 +8,14 @@
  * Copyright (C) 2007 MontaVista Software Inc.
  * Copyright (C) 2009 Provigent Ltd.
  */
+#include <linux/acpi.h>
 #include <linux/clk-provider.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/dmi.h>
 #include <linux/err.h>
 #include <linux/errno.h>
+#include <linux/gpio/driver.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -130,6 +132,80 @@ static int i2c_dw_probe_lock_support(str
 	return 0;
 }
 
+#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB)
+/*
+ * Check whether an ACPI GpioInt resource's referenced GPIO controller
+ * has finished probing. Resources with no named controller (resource
+ * source string) are skipped, since they can't be resolved to a
+ * struct device.
+ */
+static int check_gpioint_resource(struct acpi_resource *ares, void *data)
+{
+	struct acpi_resource_gpio *agpio;
+	struct acpi_device *gpio_adev;
+	struct device *gpio_dev;
+	acpi_handle handle;
+	acpi_status status;
+
+	if (!acpi_gpio_get_irq_resource(ares, &agpio))
+		return 1; /* not a GpioInt resource, skip */
+
+	if (!agpio->resource_source.string_length)
+		return 1; /* no named controller, skip */
+
+	status = acpi_get_handle(NULL, agpio->resource_source.string_ptr, &handle);
+	if (ACPI_FAILURE(status))
+		return 1;
+
+	gpio_adev = acpi_fetch_acpi_dev(handle);
+	if (!gpio_adev)
+		return 1;
+
+	struct gpio_device *gdev __free(gpio_device_put) =
+		gpio_device_find_by_fwnode(acpi_fwnode_handle(gpio_adev));
+	if (!gdev)
+		return -EPROBE_DEFER; /* controller not registered yet: abort walk */
+
+	gpio_dev = gpio_device_to_device(gdev)->parent;
+
+	guard(device)(gpio_dev);
+	if (!device_is_bound(gpio_dev))
+		return -EPROBE_DEFER; /* controller not bound yet: abort walk */
+
+	return 1; /* bound, skip adding to resource list, continue walk */
+}
+
+static int check_child_gpioint(struct acpi_device *adev, void *data)
+{
+	LIST_HEAD(res_list);
+	int ret;
+
+	ret = acpi_dev_get_resources(adev, &res_list, check_gpioint_resource, NULL);
+	if (ret < 0)
+		return ret;
+
+	acpi_dev_free_resource_list(&res_list);
+
+	return 0;
+}
+
+static int i2c_dw_check_gpio_dependencies(struct device *dev)
+{
+	struct acpi_device *adev;
+
+	adev = ACPI_COMPANION(dev);
+	if (!adev)
+		return 0;
+
+	return acpi_dev_for_each_child(adev, check_child_gpioint, NULL);
+}
+#else
+static int i2c_dw_check_gpio_dependencies(struct device *dev)
+{
+	return 0;
+}
+#endif /* CONFIG_ACPI && CONFIG_GPIOLIB */
+
 static int dw_i2c_plat_probe(struct platform_device *pdev)
 {
 	u32 flags = (uintptr_t)device_get_match_data(&pdev->dev);
@@ -138,6 +214,10 @@ static int dw_i2c_plat_probe(struct plat
 	struct dw_i2c_dev *dev;
 	int irq, ret;
 
+	ret = i2c_dw_check_gpio_dependencies(device);
+	if (ret)
+		return ret;
+
 	irq = platform_get_irq_optional(pdev, 0);
 	if (irq == -ENXIO)
 		flags |= ACCESS_POLLING;
#327 lore raw

Re: [PATCH 7.1 326/438] i2c: designware: defer probe if child GpioInt controllers are not bound

Quoted 6 lines · Greg Kroah-Hartman

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

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

From: Hardik Prakash <[email protected]>

Quoted 7 lines

commit 0a4bb2abc3e56d7be6e69b050c88ba52c87e22bf upstream.

I2C controllers may have child devices with GpioInt resources that depend on GPIO controllers being fully initialized. If the I2C controller probes and enumerates children before the referenced GPIO controller has completed probe, GPIO interrupts may not be properly configured, leading to device failures.> [...]

Hi Greg, just noticed yesterday with mainline that this change broke the touchpad of my ThinkPad T14s Gen 4 (AMD), so you might want to consider not backporting it for now:
https://lore.kernel.org/all/[email protected]/

Ciao, Thorsten

author maintainer
#328 lore raw

Re: [PATCH 7.1 326/438] i2c: designware: defer probe if child GpioInt controllers are not bound

Quoted 15 lines · Thorsten Leemhuis
Quoted 6 lines · Greg Kroah-Hartman

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

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

From: Hardik Prakash <[email protected]>

Quoted 7 lines

commit 0a4bb2abc3e56d7be6e69b050c88ba52c87e22bf upstream.

I2C controllers may have child devices with GpioInt resources that depend on GPIO controllers being fully initialized. If the I2C controller probes and enumerates children before the referenced GPIO controller has completed probe, GPIO interrupts may not be properly configured, leading to device failures.> [...]

Quoted 4 lines

Hi Greg, just noticed yesterday with mainline that this change broke the touchpad of my ThinkPad T14s Gen 4 (AMD), so you might want to consider not backporting it for now:
https://lore.kernel.org/all/[email protected]/

Now dropped, thanks.

greg k-h

author maintainer
#329 lore raw

PATCH 327/438 gpiolib: tolerate gpio-hogs lacking a hogging state

In reply to Greg Kroah-Hartman

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

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

From: Daniel Golle <[email protected]>

commit e1cc8fa0fb9e5112d15f2c310b68ac316981c06c upstream.

Commit d1d564ec4992 ("gpio: move hogs into GPIO core") made gpiochip_add_hog() return -EINVAL for hog nodes lacking any of the 'input', 'output-low' or 'output-high' properties. The error is propagated by gpiochip_hog_lines() and fails registration of the whole GPIO chip.

The previous OF-specific implementation tolerated such nodes:
of_parse_own_gpio() warned "no hogging state specified, bailing out" and of_gpiochip_add_hog() stopped processing the node without failing chip registration.

Some boards deliberately ship hog nodes without a hogging state in their base devicetree and supply the state via overlay, e.g. the PCIe slot key selection hogs on the BananaPi R4 Pro added in commit e309fa232d12 ("arm64: dts: mediatek: mt7988a-bpi-r4pro: rework pcie gpio-hog handling"), as the polarity set in the base devicetree could not be overridden from an overlay.

Booting such a board without an overlay applied now fails to register the gpiochip. On the BananaPi R4 Pro this means the MT7988A pinctrl device fails to probe, all peripherals including the console UART defer forever, and the board finally hangs when clk_disable_unused() gates the clocks of the UART still in use by earlycon:

gpiochip_add_data_with_key: GPIOs 512..595 (pinctrl_moore) failed to register, -22 mt7988-pinctrl 1001f000.pinctrl: error -EINVAL: Failed to add gpio_chip
...
clk: Disabling unused clocks
(hangs)

Restore the previous behaviour by warning about hog nodes lacking a hogging state and skipping them instead of failing the registration of the whole GPIO chip.

---
 drivers/gpio/gpiolib.c |   13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)
drivers/gpio/gpiolib.c +8 -5
@@ -975,14 +975,17 @@ int gpiochip_add_hog(struct gpio_chip *g
 	if (ret < 0)
 		return ret;
 
-	if (fwnode_property_present(fwnode, "input"))
+	if (fwnode_property_present(fwnode, "input")) {
 		dflags |= GPIOD_IN;
-	else if (fwnode_property_present(fwnode, "output-low"))
+	} else if (fwnode_property_present(fwnode, "output-low")) {
 		dflags |= GPIOD_OUT_LOW;
-	else if (fwnode_property_present(fwnode, "output-high"))
+	} else if (fwnode_property_present(fwnode, "output-high")) {
 		dflags |= GPIOD_OUT_HIGH;
-	else
-		return -EINVAL;
+	} else {
+		gpiochip_warn(gc, "%pfwP: no hogging state specified, bailing out\n",
+			      fwnode);
+		return 0;
+	}
 
 	fwnode_property_read_string(fwnode, "line-name", &name);
 
author maintainer
#330 lore raw

PATCH 328/438 gpio: pca953x: fix cache_only and IRQ state on restore_context() failure

In reply to Greg Kroah-Hartman

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

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

From: bui duc phuc <[email protected]>

commit d233087c19f6607ef926ac3f47d776e2406ffd1f upstream.

When pca953x_restore_context() fails, cache_only is left disabled and the IRQ left enabled, even though register synchronization may not have completed successfully. Restore cache_only and disable the IRQ again on failure, matching the state set by pca953x_save_context().

Fixes ec5bde62019b ("gpio: pca953x: Split pca953x_restore_context() and pca953x_save_context()")
Fixes 3e38f946062b ("gpio: pca953x: fix IRQ storm on system wake up")
---
 drivers/gpio/gpio-pca953x.c |   15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
drivers/gpio/gpio-pca953x.c +13 -2
@@ -1373,9 +1373,20 @@ static int pca953x_restore_context(struc
 	regcache_mark_dirty(chip->regmap);
 	ret = pca953x_regcache_sync(chip);
 	if (ret)
-		return ret;
+		goto err;
 
-	return regcache_sync(chip->regmap);
+	ret = regcache_sync(chip->regmap);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	if (chip->client->irq > 0)
+		disable_irq(chip->client->irq);
+	regcache_cache_only(chip->regmap, true);
+
+	return ret;
 }
 
 static void pca953x_save_context(struct pca953x_chip *chip)
author maintainer
#331 lore raw

PATCH 329/438 gpio: pch: use raw_spinlock_t for the register lock

In reply to Greg Kroah-Hartman

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

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

From: Junjie Cao <[email protected]>

commit a02b8950d619123da64f69b70fe1dadef217dfe4 upstream.

pch_irq_type() is registered as the irq_chip .irq_set_type callback and takes chip->spinlock with spin_lock_irqsave(). This callback is reached from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is an rtmutex-backed sleeping lock, so acquiring it there is invalid.

This was confirmed on a PREEMPT_RT kernel with lockdep (PROVE_RAW_LOCK_NESTING and DEBUG_ATOMIC_SLEEP). A grounded PoC mirrored pch_irq_type()'s locking and drove it through the real genirq carrier irq_set_irq_type() -> __irq_set_trigger() -> chip->irq_set_type(), i.e. the same __irq_set_trigger() edge that __setup_irq() takes for a requested IRQ. With the original spin_lock_irqsave() edge lockdep reported an invalid wait context, immediately followed by:

  BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48
  in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 95, name: insmod
  hardirqs last disabled at (3784): _raw_spin_lock_irqsave+0x4f/0x60
   rt_spin_lock+0x3a/0x1c0
   repro_irq_set_type+0x64/0xa0 [pch_repro]
   __irq_set_trigger+0x69/0x140
   irq_set_irq_type+0x78/0xd0

Switching the mirrored lock to raw_spinlock_t made both splats go away.

Convert the register lock to raw_spinlock_t. The same lock also serializes the GPIO direction/value callbacks and the suspend/resume register save/restore, but all of those critical sections only perform MMIO register accesses (ioread32()/iowrite32()) and irq_set_handler_locked(); none of them contain sleepable operations. Keeping this register lock non-sleeping is therefore appropriate for the irqchip callbacks and does not change the GPIO-side locking contract.

This is the same class of issue and fix as recently addressed for other GPIO controllers, e.g. commit 286533cb14a3 ("gpio: sch: use raw_spinlock_t in the irq startup path") and commit 90f0109019e6 ("gpio: eic-sprd: use raw_spinlock_t in the irq startup path").

---
 drivers/gpio/gpio-pch.c |   28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)
drivers/gpio/gpio-pch.c +14 -14
@@ -96,7 +96,7 @@ struct pch_gpio {
 	struct pch_gpio_reg_data pch_gpio_reg;
 	int irq_base;
 	enum pch_type_t ioh;
-	spinlock_t spinlock;
+	raw_spinlock_t spinlock;
 };
 
 static int pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val)
@@ -105,7 +105,7 @@ static int pch_gpio_set(struct gpio_chip
 	struct pch_gpio *chip =	gpiochip_get_data(gpio);
 	unsigned long flags;
 
-	spin_lock_irqsave(&chip->spinlock, flags);
+	raw_spin_lock_irqsave(&chip->spinlock, flags);
 	reg_val = ioread32(&chip->reg->po);
 	if (val)
 		reg_val |= BIT(nr);
@@ -113,7 +113,7 @@ static int pch_gpio_set(struct gpio_chip
 		reg_val &= ~BIT(nr);
 
 	iowrite32(reg_val, &chip->reg->po);
-	spin_unlock_irqrestore(&chip->spinlock, flags);
+	raw_spin_unlock_irqrestore(&chip->spinlock, flags);
 
 	return 0;
 }
@@ -133,7 +133,7 @@ static int pch_gpio_direction_output(str
 	u32 reg_val;
 	unsigned long flags;
 
-	spin_lock_irqsave(&chip->spinlock, flags);
+	raw_spin_lock_irqsave(&chip->spinlock, flags);
 
 	reg_val = ioread32(&chip->reg->po);
 	if (val)
@@ -147,7 +147,7 @@ static int pch_gpio_direction_output(str
 	pm |= BIT(nr);
 	iowrite32(pm, &chip->reg->pm);
 
-	spin_unlock_irqrestore(&chip->spinlock, flags);
+	raw_spin_unlock_irqrestore(&chip->spinlock, flags);
 
 	return 0;
 }
@@ -158,12 +158,12 @@ static int pch_gpio_direction_input(stru
 	u32 pm;
 	unsigned long flags;
 
-	spin_lock_irqsave(&chip->spinlock, flags);
+	raw_spin_lock_irqsave(&chip->spinlock, flags);
 	pm = ioread32(&chip->reg->pm);
 	pm &= BIT(gpio_pins[chip->ioh]) - 1;
 	pm &= ~BIT(nr);
 	iowrite32(pm, &chip->reg->pm);
-	spin_unlock_irqrestore(&chip->spinlock, flags);
+	raw_spin_unlock_irqrestore(&chip->spinlock, flags);
 
 	return 0;
 }
@@ -265,7 +265,7 @@ static int pch_irq_type(struct irq_data
 		return 0;
 	}
 
-	spin_lock_irqsave(&chip->spinlock, flags);
+	raw_spin_lock_irqsave(&chip->spinlock, flags);
 
 	/* Set interrupt mode */
 	im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4));
@@ -277,7 +277,7 @@ static int pch_irq_type(struct irq_data
 	else if (type & IRQ_TYPE_EDGE_BOTH)
 		irq_set_handler_locked(d, handle_edge_irq);
 
-	spin_unlock_irqrestore(&chip->spinlock, flags);
+	raw_spin_unlock_irqrestore(&chip->spinlock, flags);
 	return 0;
 }
 
@@ -374,7 +374,7 @@ static int pch_gpio_probe(struct pci_dev
 	chip->ioh = id->driver_data;
 	chip->reg = chip->base;
 	pci_set_drvdata(pdev, chip);
-	spin_lock_init(&chip->spinlock);
+	raw_spin_lock_init(&chip->spinlock);
 	pch_gpio_setup(chip);
 
 	ret = devm_gpiochip_add_data(dev, &chip->gpio, chip);
@@ -407,9 +407,9 @@ static int pch_gpio_suspend(struct devic
 	struct pch_gpio *chip = dev_get_drvdata(dev);
 	unsigned long flags;
 
-	spin_lock_irqsave(&chip->spinlock, flags);
+	raw_spin_lock_irqsave(&chip->spinlock, flags);
 	pch_gpio_save_reg_conf(chip);
-	spin_unlock_irqrestore(&chip->spinlock, flags);
+	raw_spin_unlock_irqrestore(&chip->spinlock, flags);
 
 	return 0;
 }
@@ -419,11 +419,11 @@ static int pch_gpio_resume(struct device
 	struct pch_gpio *chip = dev_get_drvdata(dev);
 	unsigned long flags;
 
-	spin_lock_irqsave(&chip->spinlock, flags);
+	raw_spin_lock_irqsave(&chip->spinlock, flags);
 	iowrite32(0x01, &chip->reg->reset);
 	iowrite32(0x00, &chip->reg->reset);
 	pch_gpio_restore_reg_conf(chip);
-	spin_unlock_irqrestore(&chip->spinlock, flags);
+	raw_spin_unlock_irqrestore(&chip->spinlock, flags);
 
 	return 0;
 }
author maintainer
#332 lore raw

PATCH 330/438 cifs: add fscache_resize_cookie() to cifs_setsize()

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit fa724e235cfdb0fb0bb427d0f9dfe864ae27403e upstream.

Several code paths update the VFS inode size by calling netfs_resize_file() and cifs_setsize(), but omit the corresponding fscache_resize_cookie() call, leaving the fscache cookie out of sync with the actual file size:

- cifs_file_set_size() in inode.c: server-side truncation via setattr
- cifs_do_truncate() in file.c: truncates to zero on O_TRUNC open
- smb2_duplicate_extents() in smb2ops.c: file clone extending EOF
- smb3_simple_falloc() in smb2ops.c: two branches that extend EOF via write-range and SMB2_set_eof respectively

Since every caller of cifs_setsize() must resize the fscache cookie, add the call to cifs_setsize() itself, consistent with how truncate_pagecache() is already consolidated there.

Fixes 70431bfd825d ("cifs: Support fscache indexing rewrite")
Fixes 93a43155127f ("cifs: Fix missing set of remote_i_size")
Fixes 110fee6b9bb5 ("smb: client: fix missing timestamp updates with O_TRUNC")
Fixes 7a06d3b816d7 ("smb/client: emulate small EOF-extending mode 0 fallocate ranges")
Cc David Howells <[email protected]>
Cc Paulo Alcantara <[email protected]>
Cc Huiwen He <[email protected]>
---
 fs/smb/client/inode.c |    1 +
 1 file changed, 1 insertion(+)
fs/smb/client/inode.c +1 -0
@@ -3051,6 +3051,7 @@ void cifs_setsize(struct inode *inode, l
 	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
 	truncate_pagecache(inode, offset);
 	netfs_wait_for_outstanding_io(inode);
+	fscache_resize_cookie(cifs_inode_cookie(inode), offset);
 }
 
 int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
author maintainer
#333 lore raw

PATCH 331/438 cpufreq: cppc: Sanitize lockless policy limit snapshots

In reply to Greg Kroah-Hartman

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

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

From: Christian Loehle <[email protected]>

commit 9753c0ab89b7516aba4884dc3cc725ca33c2e3da upstream.

cppc_cpufreq_update_perf_limits() reads policy->min and policy->max without holding the policy lock. The cpufreq core updates those fields with separate stores, so a reader can observe the old minimum together with the new maximum and construct MIN_PERF greater than MAX_PERF.

Read both fields once and, if the lockless snapshot is inconsistent, reduce the minimum to the observed maximum. This matches the conservative correction used by cpufreq_driver_resolve_freq() and ensures that CPPC never receives an inverted limit pair.

Fixes ea3db45ae476 ("cpufreq: cppc: Update MIN_PERF/MAX_PERF in target callbacks")
---
 drivers/cpufreq/cppc_cpufreq.c |   31 ++++++++++++++++++++++---------
 1 file changed, 22 insertions(+), 9 deletions(-)
drivers/cpufreq/cppc_cpufreq.c +22 -9
@@ -290,19 +290,32 @@ static inline void cppc_freq_invariance_
 }
 #endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */
 
-static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data,
-					    struct cpufreq_policy *policy)
+static void cppc_cpufreq_get_perf_limits(struct cppc_cpudata *cpu_data,
+					 struct cpufreq_policy *policy,
+					 u32 *min_perf, u32 *max_perf)
 {
 	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
-	u32 min_perf, max_perf;
+	unsigned int min_freq, max_freq;
+	u32 min, max;
+
+	min_freq = READ_ONCE(policy->min);
+	max_freq = READ_ONCE(policy->max);
+	if (unlikely(min_freq > max_freq))
+		min_freq = max_freq;
+
+	min = cppc_khz_to_perf(caps, min_freq);
+	max = cppc_khz_to_perf(caps, max_freq);
 
-	min_perf = cppc_khz_to_perf(caps, policy->min);
-	max_perf = cppc_khz_to_perf(caps, policy->max);
+	*min_perf = clamp_t(u32, min, caps->lowest_perf, caps->highest_perf);
+	*max_perf = clamp_t(u32, max, caps->lowest_perf, caps->highest_perf);
+}
 
-	cpu_data->perf_ctrls.min_perf =
-		clamp_t(u32, min_perf, caps->lowest_perf, caps->highest_perf);
-	cpu_data->perf_ctrls.max_perf =
-		clamp_t(u32, max_perf, caps->lowest_perf, caps->highest_perf);
+static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data,
+					    struct cpufreq_policy *policy)
+{
+	cppc_cpufreq_get_perf_limits(cpu_data, policy,
+				     &cpu_data->perf_ctrls.min_perf,
+				     &cpu_data->perf_ctrls.max_perf);
 }
 
 static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
author maintainer
#334 lore raw

PATCH 332/438 cpufreq: powernow-k8: Fix possible memory leak in powernowk8_cpu_init()

In reply to Greg Kroah-Hartman

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

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

From: Abdun Nihaal <[email protected]>

commit d5f8e5f6040d052d44fcbf4f31dd35145c0c8d7d upstream.

The memory allocated for data->powernow_table inside powernow_k8_cpu_init_acpi() or find_psb_table() is not freed in one of the error paths in powernowk8_cpu_init(). Fix that by adding a kfree().

---
 drivers/cpufreq/powernow-k8.c |    1 +
 1 file changed, 1 insertion(+)
drivers/cpufreq/powernow-k8.c +1 -0
@@ -1083,6 +1083,7 @@ static int powernowk8_cpu_init(struct cp
 
 err_out_exit_acpi:
 	powernow_k8_cpu_exit_acpi(data);
+	kfree(data->powernow_table);
 
 err_out:
 	kfree(data);
author maintainer
#335 lore raw

PATCH 333/438 cpufreq: schedutil: Publish util hooks only after all sg_cpu are initialized

In reply to Greg Kroah-Hartman

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

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

From: Zhongqiu Han <[email protected]>

commit f0a3f042293a8c5a2152346b3637ea60866c503a upstream.

Commit 16a03c71bba0 ("cpufreq: schedutil: Merge initialization code of sg_cpu in single loop") merged the per-CPU initialization and the utilization-hook registration into a single loop in sugov_start().

For a shared cpufreq policy this re-introduces the race originally fixed by commit ab2f7cf141aa ("cpufreq: schedutil: Fix sugov_start() versus sugov_update_shared() race").

The scheduler's util path reaches the hook under RCU-sched and never takes policy->rwsem, so the rwsem held across sugov_start() cannot serialize the two. Once the first CPU's hook is published, sugov_update_shared() may run and, via sugov_next_freq_shared(), read/write each sibling sugov_cpu (iowait_boost, util, bw_min, ...) concurrently with the memset() still initializing them, with no lock common to both sides: the update side holds sg_policy->update_lock while the init side holds only policy->rwsem, which the scheduler's util path never takes.

The walk only accesses scalar members, never a pointer like ->sg_policy, so it does not crash today; it merely uses stale (or zero on first start) values that skew the frequency selection and tracepoints. It is still a genuine data race, and a latent crash once any pointer member is dereferenced there.

Restore the two-phase approach: initialize all per-CPU structures first, and only then publish the per-CPU utilization update hooks.

Fixes 16a03c71bba0 ("cpufreq: schedutil: Merge initialization code of sg_cpu in single loop")
---
 kernel/sched/cpufreq_schedutil.c |   11 +++++++++++
 1 file changed, 11 insertions(+)
kernel/sched/cpufreq_schedutil.c +11 -0
@@ -870,8 +870,19 @@ static int sugov_start(struct cpufreq_po
 		memset(sg_cpu, 0, sizeof(*sg_cpu));
 		sg_cpu->cpu = cpu;
 		sg_cpu->sg_policy = sg_policy;
+	}
+
+	/*
+	 * Publish the hooks only after all per-CPU data is initialized, so a
+	 * shared policy's sugov_update_shared() never reads an uninitialized
+	 * sibling sugov_cpu.
+	 */
+	for_each_cpu(cpu, policy->cpus) {
+		struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu);
+
 		cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, uu);
 	}
+
 	return 0;
 }
 
author maintainer
#336 lore raw

PATCH 334/438 power: supply: bq25890: fix the -10 C NTC lookup entry

In reply to Greg Kroah-Hartman

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

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

From: Xu Rao <[email protected]>

commit 160a783aa65b74782bc17cb874af1a6d3f5fba3c upstream.

The TSPCT lookup table is monotonically decreasing except for ADC code 121, where the sequence reads -9.0 C, -1.0 C, -12.0 C. This makes the reported battery temperature jump upward by eight degrees for one code and then downward by eleven degrees for the next code.

The entry is a missing zero: use -10.0 C so the sequence remains monotonic between -9.0 C and -12.0 C.

Fixes 9652c02428f3 ("power: bq25890: add POWER_SUPPLY_PROP_TEMP")
---
 drivers/power/supply/bq25890_charger.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/power/supply/bq25890_charger.c +1 -1
@@ -320,7 +320,7 @@ static const u32 bq25890_tspct_tbl[] = {
 	145, 140, 130, 120, 115, 110, 100, 90,
 	80, 70, 60, 50, 40, 30, 20, 10,
 	0, -10, -20, -30, -40, -60, -70, -80,
-	-90, -10, -120, -140, -150, -170, -190, -210,
+	-90, -100, -120, -140, -150, -170, -190, -210,
 };
 
 #define BQ25890_TSPCT_TBL_SIZE		ARRAY_SIZE(bq25890_tspct_tbl)
author maintainer
#337 lore raw

PATCH 335/438 power: supply: macsmc: Support macOS 27 SMC firmware

In reply to Greg Kroah-Hartman

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

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

From: Sasha Finkelstein <[email protected]>

commit f7b253a6e217f71d754d70c525e9b4c1dcbd4414 upstream.

The SMC firmware included in macOS 27 changed the size of BCF0 key from 4 to 1 bytes. This key is used for indicating that battery state is critically low. In addition, B0RM key has changed endianness.

---
 drivers/power/supply/macsmc-power.c |   52 +++++++++++++++++++++++++++++++-----
 1 file changed, 45 insertions(+), 7 deletions(-)
drivers/power/supply/macsmc-power.c +45 -7
@@ -86,6 +86,11 @@ struct macsmc_power {
 	bool has_ch0i; /* Force discharge (Older firmware) */
 	bool has_ch0c; /* Inhibit charge (Older firmware) */
 	bool has_chte; /* Inhibit charge (Modern firmware) */
+	/*
+	 * Battery critical key is 1 byte and charge key is little endian
+	 * (Modern firmware)
+	 */
+	bool fw_ge_27;
 
 	u8 num_cells;
 	int nominal_voltage_mv;
@@ -273,6 +278,20 @@ static int macsmc_battery_get_date(const
 	return 0;
 }
 
+static int macsmc_battery_read_bcf0(struct macsmc_power *power, u32 *val)
+{
+	u8 tval = 0;
+	int ret;
+
+	if (power->fw_ge_27) {
+		ret = apple_smc_read_u8(power->smc, SMC_KEY(BCF0), &tval);
+		*val = tval;
+		return ret;
+	}
+
+	return apple_smc_read_u32(power->smc, SMC_KEY(BCF0), val);
+}
+
 static int macsmc_battery_get_capacity_level(struct macsmc_power *power)
 {
 	bool flag;
@@ -280,7 +299,7 @@ static int macsmc_battery_get_capacity_l
 	int ret;
 
 	/* Check for emergency shutdown condition */
-	if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val) >= 0 && val)
+	if (macsmc_battery_read_bcf0(power, &val) >= 0 && val)
 		return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
 
 	/* Check AC status for whether we could boot in this state */
@@ -303,6 +322,12 @@ static int macsmc_battery_get_capacity_l
 		return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
 }
 
+static s16 macsmc_swap_b0rm(struct macsmc_power *power, s16 b0rm)
+{
+	/* B0RM was Big Endian, likely pass through from TI gas gauge */
+	return power->fw_ge_27 ? b0rm : (s16)swab16(b0rm);
+}
+
 static int macsmc_battery_get_property(struct power_supply *psy,
 				       enum power_supply_property psp,
 				       union power_supply_propval *val)
@@ -397,8 +422,7 @@ static int macsmc_battery_get_property(s
 		break;
 	case POWER_SUPPLY_PROP_CHARGE_NOW:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16);
-		/* B0RM is Big Endian, likely pass through from TI gas gauge */
-		val->intval = (s16)swab16(vu16) * 1000;
+		val->intval = macsmc_swap_b0rm(power, vu16) * 1000;
 		break;
 	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16);
@@ -410,8 +434,7 @@ static int macsmc_battery_get_property(s
 		break;
 	case POWER_SUPPLY_PROP_ENERGY_NOW:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16);
-		/* B0RM is Big Endian, likely pass through from TI gas gauge */
-		val->intval = (s16)swab16(vu16) * power->nominal_voltage_mv;
+		val->intval = macsmc_swap_b0rm(power, vu16) * power->nominal_voltage_mv;
 		break;
 	case POWER_SUPPLY_PROP_TEMP:
 		ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AT), &vu16);
@@ -577,7 +600,7 @@ static void macsmc_power_critical_work(s
 	 * Check if SMC flagged the battery as empty.
 	 * We trigger a graceful shutdown to let the OS save data.
 	 */
-	if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &bcf0) == 0 && bcf0 != 0) {
+	if (macsmc_battery_read_bcf0(power, &bcf0) == 0 && bcf0 != 0) {
 		power->orderly_shutdown_triggered = true;
 		dev_crit(power->dev, "Battery critical (empty flag set). Triggering orderly shutdown.\n");
 		orderly_poweroff(true);
@@ -616,6 +639,7 @@ static int macsmc_power_probe(struct pla
 	struct device *dev = &pdev->dev;
 	struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
 	struct power_supply_config psy_cfg = {};
+	struct apple_smc_key_info info;
 	struct macsmc_power *power;
 	bool has_battery = false;
 	bool has_ac_adapter = false;
@@ -714,6 +738,20 @@ static int macsmc_power_probe(struct pla
 		if (apple_smc_key_exists(smc, SMC_KEY(CH0I)))
 			power->has_ch0i = true;
 
+		ret = apple_smc_get_key_info(power->smc, SMC_KEY(BCF0), &info);
+		if (ret) {
+			dev_err(&pdev->dev, "Failed to determine BCF0 key size\n");
+			return ret;
+		}
+		if (info.size == 1)
+			power->fw_ge_27 = true;
+		else if (info.size == 4)
+			power->fw_ge_27 = false;
+		else {
+			dev_err(&pdev->dev, "Unexpected BCF0 key size %d\n", info.size);
+			return -EIO;
+		}
+
 		/* Reset "Optimised Battery Charging" flags to default state */
 		if (power->has_chte)
 			apple_smc_write_u32(smc, SMC_KEY(CHTE), 0);
@@ -766,7 +804,7 @@ static int macsmc_power_probe(struct pla
 		power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV * power->num_cells;
 
 		/* Enable critical shutdown notifications by reading status once */
-		apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val32);
+		macsmc_battery_read_bcf0(power, &val32);
 
 		psy_cfg.drv_data = power;
 		power->batt = devm_power_supply_register(dev, &power->batt_desc, &psy_cfg);
Tested-by on v1 335/438
#338 lore raw

Re: [PATCH 7.1 335/438] power: supply: macsmc: Support macOS 27 SMC firmware

Quoted 2 lines · Greg Kroah-Hartman

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

Not an objection, but this did fix my problems with emergency shutdowns. I'm not sure if you can promote from Reviewed -> Tested by but if you would like so for the record:

on a M1 MBP, after applying patch following macOS 27 update:

author maintainer
#339 lore raw

PATCH 336/438 power: supply: max17040: handle missing status supplier

In reply to Greg Kroah-Hartman

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

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

From: Jianing Li <[email protected]>

commit 725668c6b6aa3971fe850659102c250d0d676e18 upstream.

MAX17040 does not report charger state itself, so the driver forwards POWER_SUPPLY_PROP_STATUS to a supplier power supply. If no supplier is registered, power_supply_get_property_from_supplier() returns -ENODEV and leaves the output value untouched.

max17040_get_property() currently ignores that error and returns success, so userspace can read an uninitialized status value from the battery power supply. This happens on systems that use the fuel gauge without a charger supplier relationship in firmware.

Return POWER_SUPPLY_STATUS_UNKNOWN when no supplier provides STATUS, and propagate other supplier lookup errors.

Fixes f4b782af61ae ("power: max17040: pass status property from supplier")
---
 drivers/power/supply/max17040_battery.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/power/supply/max17040_battery.c +5 -1
@@ -405,7 +405,11 @@ static int max17040_get_property(struct
 		val->intval = chip->low_soc_alert;
 		break;
 	case POWER_SUPPLY_PROP_STATUS:
-		power_supply_get_property_from_supplier(psy, psp, val);
+		ret = power_supply_get_property_from_supplier(psy, psp, val);
+		if (ret == -ENODEV)
+			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+		else if (ret)
+			return ret;
 		break;
 	case POWER_SUPPLY_PROP_TEMP:
 		if (!chip->channel_temp)
author maintainer
#340 lore raw

PATCH 337/438 s390/pci: Fix s390_pci_mmio_write syscall error return without MIO

In reply to Greg Kroah-Hartman

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

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

From: Niklas Schnelle <[email protected]>

commit b7ab86bdc65eadcfc43a0e3faf682a3f750cfb96 upstream.

On a machine without PCI memory-I/O (MIO) support or when running with pci=nomio the s390 specific PCI MMIO write syscall checks if the MMIO cookie is above ZPCI_IOMAP_ADDR_BASE as a sanity check before even trying to perform the MMIO. If this check fails the return value was left unchanged and thus 0 from prior operations falsely indicating success. This could potentially confuse user-space into falsely believing the MMIO, on a mapping not valid for MMIO was successful.

Fix this by setting the return value to -EFAULT prior to the check following the same pattern as elsewhere in the same function.

Fixes a67a88b0b8de ("s390/pci: remove races against pte updates")
---
 arch/s390/pci/pci_mmio.c |    1 +
 1 file changed, 1 insertion(+)
arch/s390/pci/pci_mmio.c +1 -0
@@ -188,6 +188,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_write, uns
 			goto out_unlock_mmap;
 	}
 
+	ret = -EFAULT;
 	io_addr = (void __iomem *)((args.pfn << PAGE_SHIFT) |
 			(mmio_addr & ~PAGE_MASK));
 
author maintainer
#341 lore raw

PATCH 338/438 s390/qeth: Check CAP_NET_ADMIN for private ioctls

In reply to Greg Kroah-Hartman

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

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

From: Aswin Karuvally <[email protected]>

commit d211028bac1bd0fff0026bfa2a8328e5b78cd0e6 upstream.

Gate the SIOCDEVPRIVATE ioctl commands SIOC_QETH_ADP_SET_SNMP_CONTROL, SIOC_QETH_GET_CARD_TYPE and SIOC_QETH_QUERY_OAT with CAP_NET_ADMIN capable check to ensure unprivileged users cannot invoke them.

---
 drivers/s390/net/qeth_core_main.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/s390/net/qeth_core_main.c +3 -0
@@ -6525,6 +6525,9 @@ int qeth_siocdevprivate(struct net_devic
 	struct qeth_card *card = dev->ml_priv;
 	int rc = 0;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	switch (cmd) {
 	case SIOC_QETH_ADP_SET_SNMP_CONTROL:
 		rc = qeth_snmp_command(card, data);
author maintainer
#342 lore raw

PATCH 339/438 s390/dasd: Fix potential NULL pointer dereference

In reply to Greg Kroah-Hartman

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

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

From: Jan Höppner <[email protected]>

commit 9973026f572db6b67570cadc30942f3014e41079 upstream.

dasd_release_space() checks the implementation of the is_ese() discipline function before calling it to determine if a given device is an ESE DASD.

The current usage of the logical AND operator will lead to a NULL pointer dereference as the function is called even if the function pointer is NULL.

Fix this by using the logical OR operator.

---
 drivers/s390/block/dasd_ioctl.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/s390/block/dasd_ioctl.c +1 -1
@@ -324,7 +324,7 @@ out_err:
 static int dasd_release_space(struct dasd_device *device,
 			      struct format_data_t *rdata)
 {
-	if (!device->discipline->is_ese && !device->discipline->is_ese(device))
+	if (!device->discipline->is_ese || !device->discipline->is_ese(device))
 		return -ENOTSUPP;
 	if (!device->discipline->release_space)
 		return -ENOTSUPP;
author maintainer
#343 lore raw

PATCH 340/438 s390/dasd: Fix undersized format-check buffer

In reply to Greg Kroah-Hartman

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

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

From: Stefan Haberland <[email protected]>

commit 7f40b346462f563a0d6e841a77b5163d2a882a04 upstream.

fmt_buffer_size in dasd_eckd_check_device_format() is declared as int, even though one of the multiplicands, sizeof(struct eckd_count), is a size_t. The expression

trkcount * rpt_max * sizeof(struct eckd_count)

is therefore correctly evaluated at 64-bit width, but the result is silently truncated when it is stored back into the 32-bit fmt_buffer_size variable. For a sufficiently large track range (start_unit/stop_unit are caller-controlled) this truncation yields a buffer size far smaller than the number of tracks actually requested. kzalloc() then succeeds with an undersized allocation, while the subsequent channel program build still operates on the untruncated track count and writes past the end of that buffer.

Compute the buffer size with check_mul_overflow() and keep it in a size_t, so that a value that no longer fits results in -EINVAL instead of a silently truncated allocation size.

---
 drivers/s390/block/dasd_eckd.c |   11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/s390/block/dasd_eckd.c +7 -4
@@ -20,6 +20,7 @@
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
 #include <linux/io.h>
+#include <linux/overflow.h>
 
 #include <asm/css_chars.h>
 #include <asm/machine.h>
@@ -3475,11 +3476,11 @@ static int dasd_eckd_check_device_format
 {
 	struct dasd_eckd_private *private = base->private;
 	struct eckd_count *fmt_buffer;
-	struct irb irb;
+	size_t fmt_buffer_size;
+	unsigned int trkcount;
 	int rpt_max, rpt_exp;
-	int fmt_buffer_size;
+	struct irb irb;
 	int trk_per_cyl;
-	int trkcount;
 	int tpm = 0;
 	int rc;
 
@@ -3490,7 +3491,9 @@ static int dasd_eckd_check_device_format
 	rpt_exp = recs_per_track(&private->rdc_data, 0, cdata->expect.blksize);
 
 	trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1;
-	fmt_buffer_size = trkcount * rpt_max * sizeof(struct eckd_count);
+	if (check_mul_overflow(trkcount, rpt_max, &fmt_buffer_size) ||
+	    check_mul_overflow(fmt_buffer_size, sizeof(struct eckd_count), &fmt_buffer_size))
+		return -EINVAL;
 
 	fmt_buffer = kzalloc(fmt_buffer_size, GFP_KERNEL | GFP_DMA);
 	if (!fmt_buffer)
author maintainer
#344 lore raw

PATCH 341/438 s390/zcrypt: Fix wrong domain value verification with EP11 CPRBs

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit 983279d7f86ade73db86f886e09172dd567031b5 upstream.

There is a wrong upper limit check for the domain value when an EP11 CPRB is processed for sending to a crypto card. This check is only active on custom device nodes but may lead to access heap memory behind perms->adm when an administrative CPRB is sent. Add correct limit (AP_DOMAINS = 256) checking to fix this.

Fixes cfd68b33094e ("s390/zcrypt: Filter admin CPRBs on custom devices")
---
 drivers/s390/crypto/zcrypt_api.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/s390/crypto/zcrypt_api.c +1 -1
@@ -1077,7 +1077,7 @@ static long _zcrypt_send_ep11_cprb(u32 x
 	print_hex_dump_debug("ep11req: ", DUMP_PREFIX_ADDRESS, 16, 1,
 			     ap_msg.msg, ap_msg.len, false);
 
-	if (perms != &ap_perms && domain < AUTOSEL_DOM) {
+	if (perms != &ap_perms && domain < AP_DOMAINS) {
 		if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
 			if (!test_bit_inv(domain, perms->adm)) {
 				rc = -ENODEV;
author maintainer
#345 lore raw

PATCH 342/438 s390/zcrypt: Close speculative mem read possibility

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit e935cd525af4c6ed2e2c6404aa27ca19c7f39ddb upstream.

The domain value is extracted from a given CCA or EP11 ioctl struct when a CPRB is about to be sent. Thus this is a user controlled value. Under some special conditions (custom device node used, administrative load) this value is used as an array index after bounds checking, but without speculation barrier.

Add the missing array_index_nospec() call to prevent speculative execution where this domain value is used.

Fixes cfd68b33094e ("s390/zcrypt: Filter admin CPRBs on custom devices")
---
 drivers/s390/crypto/zcrypt_api.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/s390/crypto/zcrypt_api.c +2 -0
@@ -879,6 +879,7 @@ static long _zcrypt_send_cprb(u32 xflags
 
 	if (perms != &ap_perms && domain < AP_DOMAINS) {
 		if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
+			domain = array_index_nospec(domain, AP_DOMAINS);
 			if (!test_bit_inv(domain, perms->adm)) {
 				rc = -ENODEV;
 				goto out;
@@ -1079,6 +1080,7 @@ static long _zcrypt_send_ep11_cprb(u32 x
 
 	if (perms != &ap_perms && domain < AP_DOMAINS) {
 		if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
+			domain = array_index_nospec(domain, AP_DOMAINS);
 			if (!test_bit_inv(domain, perms->adm)) {
 				rc = -ENODEV;
 				goto out;
author maintainer
#346 lore raw

PATCH 343/438 s390/zcrypt: Fix buffer over-read in cca_cipher2protkey

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit 36b230835b8a008266aad22168ca52afacc8a58d upstream.

Add validation of both the actual key buffer size and token length fields in all the cca_check_sec*token() functions. Additionally check in cca_gencipherkey() for possible underflow with returned key size.

The CCA token structures contain user-controlled len fields that were used in operations without proper validation against both the actual buffer size and minimum token structure size. An attacker could set this field larger than the actual buffer size, leading to reading beyond buffer boundaries. This may result in a kernel crash or exposure of memory via sending this as part of a request down to the crypto card. Also an attacker could have used a very small len value and thus enforce a buffer under-run which may produce similar effects as a over-read.

So now a key must
- key buf length must be at least sizeof the token struct
- the key len field inside the token must fit into the range of sizeof key token struct ... key buf length

Fixes 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys")
---
 drivers/s390/crypto/pkey_cca.c       |   15 ++------
 drivers/s390/crypto/zcrypt_ccamisc.c |   64 +++++++++++++++++++++++++++++------
 drivers/s390/crypto/zcrypt_ccamisc.h |    6 +--
 3 files changed, 62 insertions(+), 23 deletions(-)
drivers/s390/crypto/pkey_cca.c +5 -10
@@ -236,22 +236,16 @@ static int cca_key2protkey(const struct
 	if (hdr->type == TOKTYPE_CCA_INTERNAL &&
 	    hdr->version == TOKVER_CCA_AES) {
 		/* CCA AES data key */
-		if (keylen < sizeof(struct secaeskeytoken))
-			return -EINVAL;
-		if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0))
+		if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0))
 			return -EINVAL;
 	} else if (hdr->type == TOKTYPE_CCA_INTERNAL &&
 		   hdr->version == TOKVER_CCA_VLSC) {
 		/* CCA AES cipher key */
-		if (keylen < hdr->len)
-			return -EINVAL;
 		if (cca_check_secaescipherkey(pkey_dbf_info,
-					      3, key, 0, 1))
+					      3, key, keylen, 0, 1))
 			return -EINVAL;
 	} else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) {
 		/* CCA ECC (private) key */
-		if (keylen < sizeof(struct eccprivkeytoken))
-			return -EINVAL;
 		if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1))
 			return -EINVAL;
 	} else {
@@ -484,7 +478,7 @@ static int cca_verifykey(const u8 *key,
 	    hdr->version == TOKVER_CCA_AES) {
 		struct secaeskeytoken *t = (struct secaeskeytoken *)key;
 
-		rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0);
+		rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0);
 		if (rc)
 			goto out;
 		*keytype = PKEY_TYPE_CCA_DATA;
@@ -512,7 +506,8 @@ static int cca_verifykey(const u8 *key,
 		   hdr->version == TOKVER_CCA_VLSC) {
 		struct cipherkeytoken *t = (struct cipherkeytoken *)key;
 
-		rc = cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1);
+		rc = cca_check_secaescipherkey(pkey_dbf_info, 3,
+					       key, keylen, 0, 1);
 		if (rc)
 			goto out;
 		*keytype = PKEY_TYPE_CCA_CIPHER;
drivers/s390/crypto/zcrypt_ccamisc.c +54 -10

141 lines not on this page · raw message

drivers/s390/crypto/zcrypt_ccamisc.h +3 -3
@@ -136,7 +136,7 @@ struct eccprivkeytoken {
  * also checked. Returns 0 on success or errno value on failure.
  */
 int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
-			     const u8 *token, int keybitsize);
+			     const u8 *token, u32 keysize, int keybitsize);
 
 /*
  * Simple check if the token is a valid CCA secure AES cipher key
@@ -146,8 +146,8 @@ int cca_check_secaeskeytoken(debug_info_
  * Returns 0 on success or errno value on failure.
  */
 int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
-			      const u8 *token, int keybitsize,
-			      int checkcpacfexport);
+			      const u8 *token, u32 keysize,
+			      int keybitsize, int checkcpacfexport);
 
 /*
  * Simple check if the token is a valid CCA secure ECC private
author maintainer
#347 lore raw

PATCH 344/438 s390/zcrypt: Fix missing mem scrub at clear key import in cca_clr2cipherkey()

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit 01476391aecef36a3b789ee844357b22fbc90665 upstream.

The helper function _ip_cprb_helper() uses internal buffer memory for building and processing CPRBs. After use this buffer was never scrubbed which could lead to leaving for example clear key material in memory which could be exposed via tricky reuse of this same memory.

Extend the _ip_cprb_helper() function with another parameter 'scrub' used to steer scrubbing of this buffer. So now the caller has the opportunity to decide if scrubbing is needed or not.

Extend the clear key to secure key token import process in function cca_clr2cipherkey() to tell the helper function from above to scrub the cprb buffer when the clear key value is part of the request data.

Add explicit scrubbing on return from function cca_clr2cipherkey() for the random EXOR buffer and the cprb buffer.

Overall this cleans the internal used buffer in case of clear key import to prevent sensitive data to get exposed.

Fixes 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys")
---
 drivers/s390/crypto/zcrypt_ccamisc.c |   19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/s390/crypto/zcrypt_ccamisc.c +13 -6
@@ -1015,7 +1015,8 @@ static int _ip_cprb_helper(u16 cardnr, u
 			   int clr_key_bit_size,
 			   u8 *key_token,
 			   int *key_token_size,
-			   u32 xflags)
+			   u32 xflags,
+			   bool scrub)
 {
 	int rc, n;
 	u8 *mem, *ptr;
@@ -1155,7 +1156,7 @@ static int _ip_cprb_helper(u16 cardnr, u
 	*key_token_size = t->len;
 
 out:
-	free_cprbmem(mem, PARMBSIZE, false, xflags);
+	free_cprbmem(mem, PARMBSIZE, scrub, xflags);
 	return rc;
 }
 
@@ -1206,28 +1207,32 @@ int cca_clr2cipherkey(u16 card, u16 dom,
 	 * 4/4 COMPLETE the secure cipher key import
 	 */
 	rc = _ip_cprb_helper(card, dom, "AES     ", "FIRST   ", "MIN3PART",
-			     exorbuf, keybitsize, token, &tokensize, xflags);
+			     exorbuf, keybitsize, token, &tokensize,
+			     xflags, true);
 	if (rc) {
 		ZCRYPT_DBF_ERR("%s clear key import 1/4 with CSNBKPI2 failed, rc=%d\n",
 			       __func__, rc);
 		goto out;
 	}
 	rc = _ip_cprb_helper(card, dom, "AES     ", "ADD-PART", NULL,
-			     clrkey, keybitsize, token, &tokensize, xflags);
+			     clrkey, keybitsize, token, &tokensize,
+			     xflags, true);
 	if (rc) {
 		ZCRYPT_DBF_ERR("%s clear key import 2/4 with CSNBKPI2 failed, rc=%d\n",
 			       __func__, rc);
 		goto out;
 	}
 	rc = _ip_cprb_helper(card, dom, "AES     ", "ADD-PART", NULL,
-			     exorbuf, keybitsize, token, &tokensize, xflags);
+			     exorbuf, keybitsize, token, &tokensize,
+			     xflags, true);
 	if (rc) {
 		ZCRYPT_DBF_ERR("%s clear key import 3/4 with CSNBKPI2 failed, rc=%d\n",
 			       __func__, rc);
 		goto out;
 	}
 	rc = _ip_cprb_helper(card, dom, "AES     ", "COMPLETE", NULL,
-			     NULL, keybitsize, token, &tokensize, xflags);
+			     NULL, keybitsize, token, &tokensize,
+			     xflags, true);
 	if (rc) {
 		ZCRYPT_DBF_ERR("%s clear key import 4/4 with CSNBKPI2 failed, rc=%d\n",
 			       __func__, rc);
@@ -1244,6 +1249,8 @@ int cca_clr2cipherkey(u16 card, u16 dom,
 	*keybufsize = tokensize;
 
 out:
+	memzero_explicit(exorbuf, sizeof(exorbuf));
+	memzero_explicit(mem, CPRB_MEMPOOL_ITEM_SIZE);
 	mempool_free(mem, cprb_mempool);
 	return rc;
 }
author maintainer
#348 lore raw

PATCH 345/438 s390/zcrypt: Validate length for CCA AES cipher key requests

In reply to Greg Kroah-Hartman

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

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

From: Holger Dengler <[email protected]>

commit 06afe425d5283b9764303de47f554da5a808ce8a upstream.

cca_cipher2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block.

Fixes 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys")
---
 drivers/s390/crypto/zcrypt_ccamisc.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/s390/crypto/zcrypt_ccamisc.c +3 -0
@@ -1312,6 +1312,9 @@ int cca_cipher2protkey(u16 cardnr, u16 d
 	} __packed * prepparm;
 	int keytoklen = ((struct cipherkeytoken *)ckey)->len;
 
+	if (keytoklen > PARMBSIZE - sizeof(struct aureqparm))
+		return -EINVAL;
+
 	/* get already prepared memory for 2 cprbs with param block each */
 	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
 				    &preqcblk, &prepcblk, xflags);
author maintainer
#349 lore raw

PATCH 346/438 s390/zcrypt: Validate length for CCA ECC private key requests

In reply to Greg Kroah-Hartman

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

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

From: Holger Dengler <[email protected]>

commit a9ae0f6dd45c3ccc1d69363f7aea8af179122730 upstream.

cca_ecc2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block.

Fixes fa6999e326fe ("s390/pkey: support CCA and EP11 secure ECC private keys")
---
 drivers/s390/crypto/zcrypt_ccamisc.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/s390/crypto/zcrypt_ccamisc.c +3 -0
@@ -1479,6 +1479,9 @@ int cca_ecc2protkey(u16 cardnr, u16 doma
 	} __packed * prepparm;
 	int keylen = ((struct eccprivkeytoken *)key)->len;
 
+	if (keylen > PARMBSIZE - sizeof(struct aureqparm))
+		return -EINVAL;
+
 	/* get already prepared memory for 2 cprbs with param block each */
 	rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
 				    &preqcblk, &prepcblk, xflags);
author maintainer
#350 lore raw

PATCH 347/438 phy: zynqmp: fix L0_TM_DISABLE_SCRAMBLE_ENCODER mask

In reply to Greg Kroah-Hartman

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

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

From: Nava kishore Manne <[email protected]>

commit 6cb22477929489a412df8d153e550e77a012e701 upstream.

The L0_TX_DIG_61 register bit 2 is a reserved read-only field. The previous mask value 0x0f incorrectly included bit 2, causing unintended writes to a reserved bit on every scrambler bypass operation.

Correct the mask to (BIT(3) | GENMASK(1, 0)) to cover only the valid scramble bypass control bits.

---
 drivers/phy/xilinx/phy-zynqmp.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/phy/xilinx/phy-zynqmp.c +1 -1
@@ -53,7 +53,7 @@
 #define L0_TM_DIG_6			0x106c
 #define L0_TM_DIS_DESCRAMBLE_DECODER	0x0f
 #define L0_TX_DIG_61			0x00f4
-#define L0_TM_DISABLE_SCRAMBLE_ENCODER	0x0f
+#define L0_TM_DISABLE_SCRAMBLE_ENCODER	(BIT(3) | GENMASK(1, 0))
 
 /* PLL Test Mode register parameters */
 #define L0_TM_PLL_DIG_37		0x2094
author maintainer
#351 lore raw

PATCH 348/438 phy: zynqmp: use read-modify-write for SERDES scrambler bypass

In reply to Greg Kroah-Hartman

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

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

From: Nava kishore Manne <[email protected]>

commit 21e0749f931702765b9d52d05740092bc87fcd8d upstream.

xpsgtr_bypass_scrambler_8b10b() used xpsgtr_write_phy() which performs a full register write, silently clearing any bits beyond the intended bypass control fields.

Switch to xpsgtr_clr_set_phy() with clr=mask, set=mask to set only the bypass bits while preserving the remaining bits in each register.

---
 drivers/phy/xilinx/phy-zynqmp.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +6 -2
@@ -505,8 +505,12 @@ static void xpsgtr_lane_set_protocol(str
 /* Bypass (de)scrambler and 8b/10b decoder and encoder. */
 static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy)
 {
-	xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER);
-	xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER);
+	xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6,
+			   L0_TM_DIS_DESCRAMBLE_DECODER,
+			   L0_TM_DIS_DESCRAMBLE_DECODER);
+	xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61,
+			   L0_TM_DISABLE_SCRAMBLE_ENCODER,
+			   L0_TM_DISABLE_SCRAMBLE_ENCODER);
 }
 
 /* DP-specific initialization. */
author maintainer
#352 lore raw

PATCH 349/438 phy: zynqmp: keep SERDES scrambler and 8b/10b enabled for USB

In reply to Greg Kroah-Hartman

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

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

From: Nava kishore Manne <[email protected]>

commit 7eb61caf45607e1e1270f51f8f93f0ded53146da upstream.

USB Gen1 requires scrambling and 8b/10b encoding to be performed in the physical layer. Do not bypass PHY-side scrambler or encoder/decoder for USB operation, as mandated by the USB 3.x specification.

Scrambler and 8b/10b bypass remain restricted to SATA and SGMII modes, where encoding is handled in the controller.

---
 drivers/phy/xilinx/phy-zynqmp.c |   39 +++++++++++++++++++++++++++++----------
 1 file changed, 29 insertions(+), 10 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +29 -10
@@ -502,15 +502,30 @@ static void xpsgtr_lane_set_protocol(str
 	}
 }
 
-/* Bypass (de)scrambler and 8b/10b decoder and encoder. */
-static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy)
+/**
+ * xpsgtr_bypass_scrambler_8b10b - Configure scrambler/encoder behavior
+ * @gtr_phy: pointer to lane context
+ * @bypass: true to enable scrambler/encoder bypass (SATA/SGMII),
+ *          false to disable scrambler/encoder bypass (USB3)
+ *
+ * Uses RMW to preserve reserved and unrelated register fields.
+ */
+static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy,
+					  bool bypass)
 {
-	xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6,
-			   L0_TM_DIS_DESCRAMBLE_DECODER,
-			   L0_TM_DIS_DESCRAMBLE_DECODER);
-	xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61,
-			   L0_TM_DISABLE_SCRAMBLE_ENCODER,
-			   L0_TM_DISABLE_SCRAMBLE_ENCODER);
+	if (bypass) {
+		xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6,
+				   L0_TM_DIS_DESCRAMBLE_DECODER,
+				   L0_TM_DIS_DESCRAMBLE_DECODER);
+		xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61,
+				   L0_TM_DISABLE_SCRAMBLE_ENCODER,
+				   L0_TM_DISABLE_SCRAMBLE_ENCODER);
+	} else {
+		xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6,
+				   L0_TM_DIS_DESCRAMBLE_DECODER, 0);
+		xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61,
+				   L0_TM_DISABLE_SCRAMBLE_ENCODER, 0);
+	}
 }
 
 /* DP-specific initialization. */
@@ -531,7 +546,7 @@ static void xpsgtr_phy_init_sata(struct
 {
 	struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
 
-	xpsgtr_bypass_scrambler_8b10b(gtr_phy);
+	xpsgtr_bypass_scrambler_8b10b(gtr_phy, true);
 
 	writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET);
 }
@@ -547,7 +562,7 @@ static void xpsgtr_phy_init_sgmii(struct
 	xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val);
 	xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val);
 
-	xpsgtr_bypass_scrambler_8b10b(gtr_phy);
+	xpsgtr_bypass_scrambler_8b10b(gtr_phy, true);
 }
 
 /* Configure TX de-emphasis and margining for DP. */
@@ -708,6 +723,10 @@ static int xpsgtr_phy_init(struct phy *p
 	case ICM_PROTOCOL_SGMII:
 		xpsgtr_phy_init_sgmii(gtr_phy);
 		break;
+
+	case ICM_PROTOCOL_USB:
+		xpsgtr_bypass_scrambler_8b10b(gtr_phy, false);
+		break;
 	}
 
 	goto out;
author maintainer
#353 lore raw

PATCH 350/438 net: openvswitch: fix potential UAF on meter attach failure

In reply to Greg Kroah-Hartman

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

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

From: Ilya Maximets <[email protected]>

commit a58a2b0ce354df531ebc71fc870058c2feb59f6b upstream.

While attaching a newly created meter attach_meter() function makes the new meter visible to other CPUs but can still fail afterwards. On failure, it detaches the meter back and returns an error.

However, this is an unexpected behavior for the ovs_meter_cmd_set() that uses a plain kfree(meter) on attach failure without waiting for RCU readers to stop using it, assuming it was never visible.

This is never a problem for ovs-vswitchd as it always creates meters before creating any flows that use them. But the UAF can be triggered with a custom application using uAPI:

 BUG: KASAN: slab-use-after-free in ovs_meter_execute (net/openvswitch/meter.c:653)
 Read of size 8 at addr ffff88810d152650 by task meter/2508

 Call Trace:
  ovs_meter_execute (net/openvswitch/meter.c:653)
  do_execute_actions (net/openvswitch/actions.c:1407)
  ovs_execute_actions (net/openvswitch/actions.c:1584)
  ovs_packet_cmd_execute (net/openvswitch/datapath.c:703)
  ...
  netlink_sendmsg (af_netlink.c:1900)

 Allocated by task 2519:
  __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415)
  ovs_meter_cmd_set (net/openvswitch/meter.c:422)
  ...
  netlink_sendmsg (af_netlink.c:1900)

 Freed by task 2519:
  kfree (mm/slub.c:2705 mm/slub.c:6405 mm/slub.c:6720)
  ovs_meter_cmd_set (net/openvswitch/meter.c:479)
  ...
  netlink_sendmsg (af_netlink.c:1900)

Fix that by making sure attach_meter() doesn't make the meter visible until all the checks are done and the function can't fail anymore.

This also makes sure the "hash" value is calculated after the potential re-sizing of the table.

Reported by Trend Micro's Zero Day Initiative as ZDI-CAN-31642.

---
 net/openvswitch/meter.c |   33 +++++++++++++++++++--------------
 1 file changed, 19 insertions(+), 14 deletions(-)
net/openvswitch/meter.c +19 -14
@@ -133,18 +133,10 @@ static void dp_meter_instance_remove(str
 
 static int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter)
 {
-	struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti);
-	u32 hash = meter_hash(ti, meter->id);
+	struct dp_meter_instance *ti;
+	u32 hash;
 	int err;
 
-	/* In generally, slots selected should be empty, because
-	 * OvS uses id-pool to fetch a available id.
-	 */
-	if (unlikely(rcu_dereference_ovsl(ti->dp_meters[hash])))
-		return -EBUSY;
-
-	dp_meter_instance_insert(ti, meter);
-
 	/* That function is thread-safe. */
 	tbl->count++;
 	if (tbl->count >= tbl->max_meters_allowed) {
@@ -152,16 +144,29 @@ static int attach_meter(struct dp_meter_
 		goto attach_err;
 	}
 
-	if (tbl->count >= ti->n_meters &&
-	    dp_meter_instance_realloc(tbl, ti->n_meters * 2)) {
-		err = -ENOMEM;
+	ti = rcu_dereference_ovsl(tbl->ti);
+	if (tbl->count >= ti->n_meters) {
+		err = dp_meter_instance_realloc(tbl, ti->n_meters * 2);
+		if (err)
+			goto attach_err;
+
+		ti = rcu_dereference_ovsl(tbl->ti);
+	}
+
+	hash = meter_hash(ti, meter->id);
+
+	/* In general, selected slots should be empty, because
+	 * OvS uses id-pool to fetch available ids.
+	 */
+	if (unlikely(rcu_dereference_ovsl(ti->dp_meters[hash]))) {
+		err = -EBUSY;
 		goto attach_err;
 	}
 
+	dp_meter_instance_insert(ti, meter);
 	return 0;
 
 attach_err:
-	dp_meter_instance_remove(ti, meter);
 	tbl->count--;
 	return err;
 }
author maintainer
#354 lore raw

PATCH 351/438 net: openvswitch: fix skb leak on flow key update failure during recirculation

In reply to Greg Kroah-Hartman

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

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

From: Ilya Maximets <[email protected]>

commit e1cf066244dad576221b7123a0e5005967f25a20 upstream.

do_execute_actions() returns right away when execute_recirc() fails on the last action as it assumes this function always takes ownership of the skb when 'last' is true. But when the flow key update fails, the function doesn't free the skb and it ends up leaked.

This is a very unlikely scenario as it requires the packet to become unparseable by applying a set of actions on a previously parseable skb, but should be fixed nevertheless.

Reported by Sashiko.

---
 net/openvswitch/actions.c |   10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
net/openvswitch/actions.c +9 -1
@@ -1108,6 +1108,10 @@ static int execute_masked_set_action(str
 	return err;
 }
 
+/* When 'last' is true, recirc() should always consume the 'skb'.
+ * Otherwise, recirc() should keep 'skb' intact regardless what
+ * actions are executed on recirculation.
+ */
 static int execute_recirc(struct datapath *dp, struct sk_buff *skb,
 			  struct sw_flow_key *key,
 			  const struct nlattr *a, bool last)
@@ -1118,8 +1122,12 @@ static int execute_recirc(struct datapat
 		int err;
 
 		err = ovs_flow_key_update(skb, key);
-		if (err)
+		if (err) {
+			if (last)
+				ovs_kfree_skb_reason(skb,
+						     OVS_DROP_ACTION_ERROR);
 			return err;
+		}
 	}
 	BUG_ON(!is_flow_key_valid(key));
 
author maintainer
#355 lore raw

PATCH 352/438 net: openvswitch: fix skb leak on flow key update failure during ct

In reply to Greg Kroah-Hartman

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

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

From: Ilya Maximets <[email protected]>

commit bc62e843bc48f933da765ce47079fd992e535794 upstream.

ovs_ct_execute() always steals or frees the skb on failure while ovs_flow_key_update() does not. So, if it fails and we return right away, the skb ends up leaked.

Fix that by breaking instead and letting the common error handling code at the bottom of the loop to free the skb properly.

This is a very unlikely scenario as it requires the packet to become unparseable by applying a set of actions on a previously parseable skb, but should be fixed nevertheless.

Reported by Sashiko.

---
 net/openvswitch/actions.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/openvswitch/actions.c +1 -1
@@ -1380,7 +1380,7 @@ static int do_execute_actions(struct dat
 			if (!is_flow_key_valid(key)) {
 				err = ovs_flow_key_update(skb, key);
 				if (err)
-					return err;
+					break;
 			}
 
 			err = ovs_ct_execute(ovs_dp_get_net(dp), skb, key,
author maintainer
#356 lore raw

PATCH 353/438 ice: wait for reset completion in ice_resume()

In reply to Greg Kroah-Hartman

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

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

From: Aaron Ma <[email protected]>

commit c2816d613f388814d27bc9fd6dbd931a88056e19 upstream.

ice_resume() schedules an asynchronous PF reset and returns immediately. The reset runs later in ice_service_task(). If userspace tries to bring up the net device before the reset finishes, ice_open() fails with -EBUSY:

ice_resume()
ice_schedule_reset() # sets ICE_PFR_REQ, returns
...
ice_open()
ice_is_reset_in_progress() # ICE_PFR_REQ still set, -EBUSY
...
ice_service_task()
ice_do_reset()
ice_rebuild() # clears ICE_PFR_REQ, too late

Reproduced on E800 series NICs during suspend/resume with irdma enabled, where the aux device probe widens the race window.

ice 0000:81:00.0: can't open net device while reset is in progress

Add a best-effort wait (10s timeout, matching ice_devlink_info_get()) for the reset to complete before returning from ice_resume(). In practice the reset completes in ~300ms.

---
 drivers/net/ethernet/intel/ice/ice_main.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/net/ethernet/intel/ice/ice_main.c +10 -0
@@ -5637,6 +5637,16 @@ static int ice_resume(struct device *dev
 	/* Restart the service task */
 	mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period));
 
+	/* Best-effort wait for the scheduled reset to finish so that the
+	 * device is operational before returning. Without this, userspace
+	 * (e.g. NetworkManager) may try to open the net device while the
+	 * asynchronous reset is still in progress, hitting -EBUSY.
+	 */
+	ret = ice_wait_for_reset(pf, secs_to_jiffies(10));
+	if (ret)
+		dev_err(dev, "Wait for reset timed out (10s) during resume: %d\n",
+			ret);
+
 	return 0;
 }
 
author maintainer
#357 lore raw

PATCH 354/438 ice: fix VF interrupts cleanup

In reply to Greg Kroah-Hartman

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

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

From: Dawid Osuchowski <[email protected]>

commit fb096882095e5a8d6b5159e43793d4a38a0c5b1f upstream.

When a virtual function sends an IRQ map command, the PF will set up interrupts according to that request. However, because these interrupts are never reset, the next time Virtual Function initializes, the interrupts are still enabled for a given VF, which leads to performance degradation in certain cases due to interrupts being unexpectedly enabled and thus causing interrupt floods.

---
 drivers/net/ethernet/intel/ice/ice_vf_lib.c         |   27 ++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_vf_lib_private.h |    1 
 drivers/net/ethernet/intel/ice/virt/queues.c        |   21 +++++++++++++++
 3 files changed, 49 insertions(+)
drivers/net/ethernet/intel/ice/ice_vf_lib.c +27 -0
@@ -849,6 +849,30 @@ static void ice_notify_vf_reset(struct i
 }
 
 /**
+ * ice_reset_interrupts - clear all queue interrupt configuration for a VSI
+ * @vsi: the VSI whose interrupt registers should be cleared
+ *
+ * Zero the QINT_RQCTL and QINT_TQCTL registers for all allocated queues
+ * in the VSI. This clears the entire register including MSIX_INDX, ITR_INDX,
+ * CAUSE_ENA and NEXTQ fields, unlike ice_vf_dis_rxq_interrupt() which only
+ * clears the CAUSE_ENA bit.
+ */
+void ice_reset_interrupts(struct ice_vsi *vsi)
+{
+	struct ice_pf *pf = vsi->back;
+	struct ice_hw *hw = &pf->hw;
+	int i;
+
+	ice_for_each_alloc_rxq(vsi, i)
+		wr32(hw, QINT_RQCTL(vsi->rxq_map[i]), 0);
+
+	ice_for_each_alloc_txq(vsi, i)
+		wr32(hw, QINT_TQCTL(vsi->txq_map[i]), 0);
+
+	ice_flush(hw);
+}
+
+/**
  * ice_reset_vf - Reset a particular VF
  * @vf: pointer to the VF structure
  * @flags: flags controlling behavior of the reset
@@ -919,6 +943,9 @@ int ice_reset_vf(struct ice_vf *vf, u32
 
 	ice_dis_vf_qs(vf);
 
+	/* cleanup interrupt registers */
+	ice_reset_interrupts(vsi);
+
 	/* Call Disable LAN Tx queue AQ whether or not queues are
 	 * enabled. This is needed for successful completion of VFR.
 	 */
drivers/net/ethernet/intel/ice/ice_vf_lib_private.h +1 -0
@@ -26,6 +26,7 @@
 void ice_initialize_vf_entry(struct ice_vf *vf);
 void ice_deinitialize_vf_entry(struct ice_vf *vf);
 void ice_dis_vf_qs(struct ice_vf *vf);
+void ice_reset_interrupts(struct ice_vsi *vsi);
 int ice_check_vf_init(struct ice_vf *vf);
 enum virtchnl_status_code ice_err_to_virt_err(int err);
 struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf);
drivers/net/ethernet/intel/ice/virt/queues.c +21 -0
@@ -225,6 +225,24 @@ void ice_vf_ena_rxq_interrupt(struct ice
 }
 
 /**
+ * ice_vf_dis_rxq_interrupt - disable Rx queue interrupt via QINT_RQCTL
+ * @vsi: VSI of the VF to configure
+ * @q_idx: VF queue index used to determine the queue in the PF's space
+ */
+static void ice_vf_dis_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx)
+{
+	struct ice_hw *hw = &vsi->back->hw;
+	u32 pfq = vsi->rxq_map[q_idx];
+	u32 reg;
+
+	reg = rd32(hw, QINT_RQCTL(pfq));
+	reg &= ~QINT_RQCTL_CAUSE_ENA_M;
+	wr32(hw, QINT_RQCTL(pfq), reg);
+
+	ice_flush(hw);
+}
+
+/**
  * ice_vc_ena_qs_msg
  * @vf: pointer to the VF info
  * @msg: pointer to the msg buffer
@@ -416,6 +434,8 @@ int ice_vc_dis_qs_msg(struct ice_vf *vf,
 			goto error_param;
 		}
 
+		for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF)
+			ice_vf_dis_rxq_interrupt(vsi, vf_q_id);
 		bitmap_zero(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF);
 	} else if (q_map) {
 		for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) {
@@ -436,6 +456,7 @@ int ice_vc_dis_qs_msg(struct ice_vf *vf,
 				goto error_param;
 			}
 
+			ice_vf_dis_rxq_interrupt(vsi, vf_q_id);
 			/* Clear enabled queues flag */
 			clear_bit(vf_q_id, vf->rxq_ena);
 		}
author maintainer
#358 lore raw

PATCH 355/438 ice: fix memory leak in ice_lbtest_prepare_rings()

In reply to Greg Kroah-Hartman

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

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

From: Dawei Feng <[email protected]>

commit 3a9de5590da4ffd9e9c541c4c4d492aa2b54cf6e upstream.

ice_lbtest_prepare_rings() frees Rx rings only when ice_vsi_start_all_rx_rings() fails. If ice_vsi_setup_rx_rings() fails after allocating some descriptors, or if ice_vsi_cfg_lan() fails after the Rx rings were prepared, the function reaches the Tx cleanup path without releasing the initialized Rx resources.

Fix this by adding separate unwind paths for Rx setup failure and LAN configuration failure. The Rx setup failure path releases the partially prepared Rx rings before freeing Tx rings, while later failures first undo the LAN Tx configuration and then release the Rx rings in reverse setup order.

The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc7.

An x86_64 allyesconfig build showed no new warnings. As we do not have an Intel E800 Series adapter available to run the ethtool offline loopback selftest, no runtime testing was able to be performed.

---
 drivers/net/ethernet/intel/ice/ice_ethtool.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/net/ethernet/intel/ice/ice_ethtool.c +5 -5
@@ -1069,18 +1069,18 @@ static int ice_lbtest_prepare_rings(stru
 
 	status = ice_vsi_cfg_lan(vsi);
 	if (status)
-		goto err_setup_rx_ring;
+		goto err_cfg_lan;
 
 	status = ice_vsi_start_all_rx_rings(vsi);
 	if (status)
-		goto err_start_rx_ring;
+		goto err_cfg_lan;
 
 	return 0;
 
-err_start_rx_ring:
-	ice_vsi_free_rx_rings(vsi);
-err_setup_rx_ring:
+err_cfg_lan:
 	ice_vsi_stop_lan_tx_rings(vsi, ICE_NO_RESET, 0);
+err_setup_rx_ring:
+	ice_vsi_free_rx_rings(vsi);
 err_setup_tx_ring:
 	ice_vsi_free_tx_rings(vsi);
 
author maintainer
#359 lore raw

PATCH 356/438 i2c: spacemit: request IRQ after controller initialization

In reply to Greg Kroah-Hartman

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

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

From: Linmao Li <[email protected]>

commit cdac670237258c8ca063aa8a16998f680d81b80d upstream.

spacemit_i2c_probe() requests the IRQ before it enables the clocks, resets the controller and runs init_completion(). If an interrupt is already pending, the handler runs too early: it reads registers while the clocks are still off and calls complete() on an uninitialized completion. Request the IRQ after the controller and completion are initialized, but still before the adapter is registered.

---
 drivers/i2c/busses/i2c-k1.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/i2c/busses/i2c-k1.c +5 -5
@@ -723,11 +723,6 @@ static int spacemit_i2c_probe(struct pla
 	if (i2c->irq < 0)
 		return dev_err_probe(dev, i2c->irq, "failed to get irq resource");
 
-	ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler,
-			       IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c);
-	if (ret)
-		return dev_err_probe(dev, ret, "failed to request irq");
-
 	clk = devm_clk_get_enabled(dev, "func");
 	if (IS_ERR(clk))
 		return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock");
@@ -755,6 +750,11 @@ static int spacemit_i2c_probe(struct pla
 
 	init_completion(&i2c->complete);
 
+	ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler,
+			       IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to request irq");
+
 	platform_set_drvdata(pdev, i2c);
 
 	ret = i2c_add_numbered_adapter(&i2c->adapt);
author maintainer
#360 lore raw

PATCH 357/438 i2c: qcom-cci: drop custom suspend/resume and rely on runtime PM helpers

In reply to Greg Kroah-Hartman

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

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

From: Wenmeng Liu <[email protected]>

commit b08c9857aa1f5f3a81d375d6d4bb1d8b92f22ebc upstream.

cci_resume() unconditionally calls cci_resume_runtime() regardless of the runtime PM state.

If the device is already runtime-suspended before system suspend, the clock is re-enabled while runtime_status remains RPM_SUSPENDED. As a result, pm_request_autosuspend() does not arm the timer, leaving the clock permanently enabled.

---
 drivers/i2c/busses/i2c-qcom-cci.c |   18 +-----------------
 1 file changed, 1 insertion(+), 17 deletions(-)
drivers/i2c/busses/i2c-qcom-cci.c +1 -17
@@ -493,24 +493,8 @@ static int __maybe_unused cci_resume_run
 	return 0;
 }
 
-static int __maybe_unused cci_suspend(struct device *dev)
-{
-	if (!pm_runtime_suspended(dev))
-		return cci_suspend_runtime(dev);
-
-	return 0;
-}
-
-static int __maybe_unused cci_resume(struct device *dev)
-{
-	cci_resume_runtime(dev);
-	pm_request_autosuspend(dev);
-
-	return 0;
-}
-
 static const struct dev_pm_ops qcom_cci_pm = {
-	SET_SYSTEM_SLEEP_PM_OPS(cci_suspend, cci_resume)
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
 	SET_RUNTIME_PM_OPS(cci_suspend_runtime, cci_resume_runtime, NULL)
 };
 
author maintainer
#361 lore raw

PATCH 358/438 i2c: jz4780: Cache host clock rate at probe to prevent CCF prepare_lock deadlock

In reply to Greg Kroah-Hartman

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

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

From: H. Nikolaus Schaller <[email protected]>

commit d99607c888f26e8a4e9fe9772860cef4aff86bb4 upstream.

Fix a severe AB/BA deadlock between the Common Clock Framework (CCF) and the I2C adapter lock, which triggers when an I2C-controlled clock generator client (like the Si5351) is registered or modified under the CCF.

During an i2c client clock (generator) frequency change, the CCF acquires its global 'prepare_lock' mutex and the driver calls i2c_transfer() to update the client's chip registers, stalling for the adapter's I2C bus lock.

Concurrently, an independent, parallel transfer on the same bus (e.g., a GPIO expander handling LEDs) can hold the I2C adapter lock. Inside this parallel transfer path, jz4780_i2c_set_speed() calls clk_get_rate() on the host controller's input clock to calculate bus timings. This call attempts to acquire the blocked CCF 'prepare_lock', creating a circular dependency that freezes the system.

The jz4780 host controller clock itself is static and never changes at runtime.

However, calling clk_get_rate() inside the active transfer path introduces an unnecessary dependency on the CCF internal locks.

Eliminate this synchronous clk_get_rate() call from the active transfer path by caching the static host peripheral clock rate once - inside the private jz4780_i2c structure during jz4780_i2c_probe(). Update jz4780_i2c_set_speed() to use this cached value, safely decoupling active I2C transactions from the CCF internal locks without any risk of stale timings.

Assisted-by web based Google AI (pinpointing the bug and writing the message).

---
 drivers/i2c/busses/i2c-jz4780.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-jz4780.c +4 -1
@@ -141,6 +141,7 @@ struct jz4780_i2c {
 	void __iomem		*iomem;
 	int			 irq;
 	struct clk		*clk;
+	unsigned long		 clk_rate_khz;
 	struct i2c_adapter	 adap;
 	const struct ingenic_i2c_config *cdata;
 
@@ -246,7 +247,7 @@ static int jz4780_i2c_set_target(struct
 
 static int jz4780_i2c_set_speed(struct jz4780_i2c *i2c)
 {
-	int dev_clk_khz = clk_get_rate(i2c->clk) / 1000;
+	int dev_clk_khz = i2c->clk_rate_khz;
 	int cnt_high = 0;	/* HIGH period count of the SCL clock */
 	int cnt_low = 0;	/* LOW period count of the SCL clock */
 	int cnt_period = 0;	/* period count of the SCL clock */
@@ -796,6 +797,8 @@ static int jz4780_i2c_probe(struct platf
 	if (IS_ERR(i2c->clk))
 		return PTR_ERR(i2c->clk);
 
+	i2c->clk_rate_khz = clk_get_rate(i2c->clk) / 1000;
+
 	ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
 				   &clk_freq);
 	if (ret) {
author maintainer
#362 lore raw

PATCH 359/438 i2c: iproc: reset bus after timeout if START_BUSY is stuck

In reply to Greg Kroah-Hartman

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

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

From: Jonas Gorski <[email protected]>

commit 98f2e9e6d6f91a6abb43f166b244b428ba85fa2b upstream.

If a transaction times out, the START_BUSY signal can stay up, and subsequent transactaction attempts will fail as the bus is still considered busy.

I can easily trigger this by attempting to read from an address with no device, e.g. when running i2cdetect. After the first read times out, all subsequent read attempts return busy.

To get to a working state again, the controller needs to be reset to clear the START_BUSY signal. So check for START_BUSY still asserted on a timeout, and do reset in case it is,

This is also done by the original non-upstream iproc-smbus driver implementation [1].

Works around situations like:

    bcm-iproc-2c 1803b000.i2c: transaction timed out
    bcm-iproc-2c 1803b000.i2c: bus is busy
    bcm-iproc-2c 1803b000.i2c: bus is busy
    bcm-iproc-2c 1803b000.i2c: bus is busy
    bcm-iproc-2c 1803b000.i2c: bus is busy
    bcm-iproc-2c 1803b000.i2c: bus is busy
    ...

where the bus never recovers after a timeout.

[1] https://github.com/opencomputeproject/onie/blob/master/patches/kernel/3.2.69/driver-iproc-smbus.patch

---
 drivers/i2c/busses/i2c-bcm-iproc.c |   11 +++++++++++
 1 file changed, 11 insertions(+)
drivers/i2c/busses/i2c-bcm-iproc.c +11 -0
@@ -803,6 +803,17 @@ static int bcm_iproc_i2c_xfer_wait(struc
 	}
 
 	if (!time_left && !iproc_i2c->xfer_is_done) {
+		/*
+		 * The controller may fail to clear START_BUSY after a timeout,
+		 * reset the controller to recover in that case.
+		 */
+		if (!!(iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET) &
+		       BIT(M_CMD_START_BUSY_SHIFT))) {
+			bcm_iproc_i2c_enable_disable(iproc_i2c, false);
+			bcm_iproc_i2c_init(iproc_i2c);
+			bcm_iproc_i2c_enable_disable(iproc_i2c, true);
+		}
+
 		/* flush both TX/RX FIFOs */
 		val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
 		iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
author maintainer
#363 lore raw

PATCH 360/438 i2c: imx: mark I2C adapter when hardware is powered down

In reply to Greg Kroah-Hartman

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

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

From: Carlos Song <[email protected]>

commit 00d86dd5c2034e0e139e4806137b3b43e07ddd83 upstream.

On some i.MX platforms, certain I2C client drivers keep a periodic workqueue which continues to trigger I2C transfers.

During system suspend/resume, there exists a time window between:
- suspend_noirq and the system entering suspend
- the system starting to resume and resume_noirq

In this window, the I2C controller resources such as clock and pinctrl may already be disabled or not yet restored.

If a workqueue triggers an I2C transfer in this period, the driver attempts to access I2C registers while the hardware resources are unavailable, which may lead to system hang.

Mark the I2C adapter as suspended during noirq suspend and block new transfers until resume, ensuring that I2C transfers are only issued when hardware resources are available.

Fixes 358025ac091e ("i2c: imx: make controller available until system suspend_noirq() and from resume_noirq()")
Cc <[email protected]> # v6.14+
---
 drivers/i2c/busses/i2c-imx.c |   45 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 2 deletions(-)
drivers/i2c/busses/i2c-imx.c +43 -2
@@ -1952,6 +1952,47 @@ static int i2c_imx_runtime_resume(struct
 	return 0;
 }
 
+static int __maybe_unused i2c_imx_suspend_noirq(struct device *dev)
+{
+	struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
+	int ret;
+
+	i2c_mark_adapter_suspended(&i2c_imx->adapter);
+
+	/*
+	 * Cancel the slave timer before powering down to prevent
+	 * i2c_imx_slave_timeout() from accessing hardware registers
+	 * while the clock is disabled.
+	 */
+	hrtimer_cancel(&i2c_imx->slave_timer);
+
+	ret = pm_runtime_force_suspend(dev);
+	if (ret) {
+		i2c_mark_adapter_resumed(&i2c_imx->adapter);
+		if (i2c_imx->slave) {
+			hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY);
+			hrtimer_restart(&i2c_imx->slave_timer);
+		}
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused i2c_imx_resume_noirq(struct device *dev)
+{
+	struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
+	int ret;
+
+	ret = pm_runtime_force_resume(dev);
+	if (ret)
+		return ret;
+
+	i2c_mark_adapter_resumed(&i2c_imx->adapter);
+
+	return 0;
+}
+
 static int i2c_imx_suspend(struct device *dev)
 {
 	/*
@@ -1985,8 +2026,8 @@ static int i2c_imx_resume(struct device
 }
 
 static const struct dev_pm_ops i2c_imx_pm_ops = {
-	NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
-				  pm_runtime_force_resume)
+	NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend_noirq,
+				  i2c_imx_resume_noirq)
 	SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
 	RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL)
 };
author maintainer
#364 lore raw

PATCH 361/438 i2c: imx: Fix slave registration race and error handling

In reply to Greg Kroah-Hartman

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

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

From: Liem <[email protected]>

commit d64ec362c369bbc33833f7936d5f3a706b0d5c45 upstream.

In i2c_imx_reg_slave(), the slave pointer was assigned before pm_runtime_resume_and_get(). If pm_runtime_resume_and_get() failed, the error path returned without clearing i2c_imx->slave, leaving it non-NULL and causing all subsequent registration attempts to fail with -EBUSY.

Additionally, because this driver uses a shared IRQ, the interrupt handler i2c_imx_isr() can execute concurrently and, after acquiring slave_lock, dereference i2c_imx->slave. The previous fix attempt added a lockless i2c_imx->slave = NULL on the error path, but that could race with the ISR under the lock and still cause a NULL pointer dereference.

Fix both issues by deferring the assignment of i2c_imx->slave and i2c_imx->last_slave_event to after a successful resume, and by performing the assignment inside the slave_lock critical section. This guarantees that the slave pointer is never left stale on the error path and is always valid when observed by the interrupt handler.

Fixes f7414cd6923f ("i2c: imx: support slave mode for imx I2C driver")
Cc <[email protected]> # v5.11+
---
 drivers/i2c/busses/i2c-imx.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/i2c/busses/i2c-imx.c +5 -3
@@ -930,9 +930,6 @@ static int i2c_imx_reg_slave(struct i2c_
 	if (i2c_imx->slave)
 		return -EBUSY;
 
-	i2c_imx->slave = client;
-	i2c_imx->last_slave_event = I2C_SLAVE_STOP;
-
 	/* Resume */
 	ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
 	if (ret < 0) {
@@ -940,6 +937,11 @@ static int i2c_imx_reg_slave(struct i2c_
 		return ret;
 	}
 
+	scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) {
+		i2c_imx->slave = client;
+		i2c_imx->last_slave_event = I2C_SLAVE_STOP;
+	}
+
 	i2c_imx_slave_init(i2c_imx);
 
 	return 0;
author maintainer
#365 lore raw

PATCH 362/438 i2c: imx: Cancel hrtimer before clearing slave pointer

In reply to Greg Kroah-Hartman

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

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

From: Liem <[email protected]>

commit 6ac7702b6cc2b94aaed9ef2d95bfbefcdc90061f upstream.

In i2c_imx_unreg_slave(), the slave pointer is set to NULL after disabling interrupts. However, a pending interrupt might already have started the hrtimer (i2c_imx_slave_timeout) before the pointer was cleared. If the hrtimer fires after i2c_imx->slave is set to NULL, the timer callback i2c_imx_slave_finish_op() will call i2c_imx_slave_event() with a NULL slave pointer, which results in a use-after-free / NULL pointer dereference.

Fix by canceling the hrtimer and waiting for it to complete after disabling interrupts, before clearing the slave pointer.

Fixes f7414cd6923f ("i2c: imx: support slave mode for imx I2C driver")
Cc <[email protected]> # v5.11+
---
 drivers/i2c/busses/i2c-imx.c |    1 +
 1 file changed, 1 insertion(+)
drivers/i2c/busses/i2c-imx.c +1 -0
@@ -960,6 +960,7 @@ static int i2c_imx_unreg_slave(struct i2
 
 	i2c_imx_reset_regs(i2c_imx);
 
+	hrtimer_cancel(&i2c_imx->slave_timer);
 	i2c_imx->slave = NULL;
 
 	/* Suspend */
author maintainer
#366 lore raw

PATCH 363/438 can: c_can: c_can_chip_config(): keep controller in init mode until bittiming is configured

In reply to Greg Kroah-Hartman

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

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

From: Lucas Martins Alves <[email protected]>

commit 26504844613fb44c7cab1c5f6fcff77861709baa upstream.

c_can_chip_config() was programming C_CAN_CTRL_REG without CONTROL_INIT, which may allow the controller to become active before c_can_set_bittiming() finishes.

That creates a short timing window where the peripheral can interact with the bus using a different/default bitrate, potentially generating bus errors and corrupting traffic.

Set CONTROL_INIT together with the control-mode writes in c_can_chip_config() (normal, loopback and listen-only paths), so the controller stays halted until bit timing is fully programmed.

This prevents transient bus disturbance during startup when the configured bitrate differs from the active bus bitrate.

Fixes 881ff67ad450 ("can: c_can: Added support for Bosch C_CAN controller")
[mkl: remove space before close parenthesis]
---
 drivers/net/can/c_can/c_can_main.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/net/can/c_can/c_can_main.c +4 -4
@@ -597,20 +597,20 @@ static int c_can_chip_config(struct net_
 		return err;
 
 	/* enable automatic retransmission */
-	priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR);
+	priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR | CONTROL_INIT);
 
 	if ((priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) &&
 	    (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)) {
 		/* loopback + silent mode : useful for hot self-test */
-		priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST);
+		priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT);
 		priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK | TEST_SILENT);
 	} else if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
 		/* loopback mode : useful for self-test function */
-		priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST);
+		priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT);
 		priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK);
 	} else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) {
 		/* silent mode : bus-monitoring mode */
-		priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST);
+		priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT);
 		priv->write_reg(priv, C_CAN_TEST_REG, TEST_SILENT);
 	}
 
author maintainer
#367 lore raw

PATCH 364/438 can: ems_usb: validate CPC message lengths

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

commit 02925f51377f2a42a6724f00549167499c9302e5 upstream.

ems_usb_read_bulk_callback() walks CPC messages packed in one USB receive buffer.

Check that each declared message fits in the URB payload. Also require the type-specific payload to cover the fields used by the CAN, state, error and overrun handlers.

Fixes 702171adeed3 ("ems_usb: Added support for EMS CPC-USB/ARM7 CAN/USB interface")
---
 drivers/net/can/usb/ems_usb.c |   43 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)
drivers/net/can/usb/ems_usb.c +43 -0
@@ -409,6 +409,40 @@ static void ems_usb_rx_err(struct ems_us
 		netif_rx(skb);
 }
 
+static bool ems_usb_rx_msg_len_valid(struct ems_cpc_msg *msg)
+{
+	size_t len = msg->length;
+	size_t can_len;
+
+	switch (msg->type) {
+	case CPC_MSG_TYPE_CAN_STATE:
+		return len >= sizeof(msg->msg.can_state);
+
+	case CPC_MSG_TYPE_CAN_FRAME:
+	case CPC_MSG_TYPE_EXT_CAN_FRAME:
+	case CPC_MSG_TYPE_RTR_FRAME:
+	case CPC_MSG_TYPE_EXT_RTR_FRAME:
+		if (len < CPC_CAN_MSG_MIN_SIZE)
+			return false;
+
+		if (msg->type == CPC_MSG_TYPE_RTR_FRAME ||
+		    msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME)
+			return true;
+
+		can_len = can_cc_dlc2len(msg->msg.can_msg.length & 0xf);
+		return len >= CPC_CAN_MSG_MIN_SIZE + can_len;
+
+	case CPC_MSG_TYPE_CAN_FRAME_ERROR:
+		return len >= sizeof(msg->msg.error);
+
+	case CPC_MSG_TYPE_OVERRUN:
+		return len >= sizeof(msg->msg.overrun);
+
+	default:
+		return true;
+	}
+}
+
 /*
  * callback for bulk IN urb
  */
@@ -451,6 +485,15 @@ static void ems_usb_read_bulk_callback(s
 			}
 
 			msg = (struct ems_cpc_msg *)&ibuf[start];
+			if (msg->length >
+			    urb->actual_length - start - CPC_MSG_HEADER_LEN) {
+				netdev_err(netdev, "format error\n");
+				break;
+			}
+			if (!ems_usb_rx_msg_len_valid(msg)) {
+				netdev_err(netdev, "format error\n");
+				break;
+			}
 
 			switch (msg->type) {
 			case CPC_MSG_TYPE_CAN_STATE:
author maintainer
#368 lore raw

PATCH 365/438 can: etas_es58x: es58x_read_bulk_callback(): fix RX buffer leak on URB resubmit failure

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 7a0cf2b2497c757c3cb1286eddf2986abb0d387b upstream.

es58x_read_bulk_callback() resubmits the RX URB after processing a received packet. If the resubmit succeeds, the URB remains anchored and will be handled by the normal RX path or by teardown.

However, if usb_submit_urb() fails, the callback unanchors the URB and then returns directly. This skips the existing free_urb path, so the coherent transfer buffer allocated with usb_alloc_coherent() is not released.

Reuse the existing free_urb path after a resubmit failure so that the RX coherent buffer is freed before leaving the callback.

Fixes 5eaad4f76826 ("can: usb: etas_es58x: correctly anchor the urb in the read bulk callback")
---
 drivers/net/can/usb/etas_es58x/es58x_core.c |    1 -
 1 file changed, 1 deletion(-)
drivers/net/can/usb/etas_es58x/es58x_core.c +0 -1
@@ -1476,7 +1476,6 @@ static void es58x_read_bulk_callback(str
 		dev_err_ratelimited(dev,
 				    "Failed resubmitting read bulk urb: %pe\n",
 				    ERR_PTR(ret));
-	return;
 
  free_urb:
 	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
author maintainer
#369 lore raw

PATCH 366/438 can: gs_usb: gs_usb_receive_bulk_callback(): resubmit URB on skb allocation failure

In reply to Greg Kroah-Hartman

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

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

From: Marc Kleine-Budde <[email protected]>

commit 68c5724ecd159992f76edb7b57dc508a44c8b7da upstream.

If the allocation of the SKB in gs_usb_receive_bulk_callback() fails, the driver returns from the callback without resubmitting the URB in order to receive further USB in URBs.

This results in a silent performance degradation which, if it occurs repeatedly, results in starvation of USB in traffic.

Instead of returning immediately, try to resend the URB. If this also fails, this is logged as an info message.

Fixes d08e973a77d1 ("can: gs_usb: Added support for the GS_USB CAN devices")
Fixes 26949ac935e3 ("can: gs_usb: add CAN-FD support")
---
 drivers/net/can/usb/gs_usb.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/can/usb/gs_usb.c +2 -2
@@ -674,7 +674,7 @@ static void gs_usb_receive_bulk_callback
 		if (hf->flags & GS_CAN_FLAG_FD) {
 			skb = alloc_canfd_skb(netdev, &cfd);
 			if (!skb)
-				return;
+				goto resubmit_urb;
 
 			cfd->can_id = le32_to_cpu(hf->can_id);
 			cfd->len = data_length;
@@ -687,7 +687,7 @@ static void gs_usb_receive_bulk_callback
 		} else {
 			skb = alloc_can_skb(netdev, &cf);
 			if (!skb)
-				return;
+				goto resubmit_urb;
 
 			cf->can_id = le32_to_cpu(hf->can_id);
 			can_frame_set_cc_len(cf, hf->can_dlc, dev->can.ctrlmode);
author maintainer
#370 lore raw

PATCH 367/438 can: isotp: fix timer drain order, wakeup handling and tx_gen ordering

In reply to Greg Kroah-Hartman

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

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

From: Oliver Hartkopp <[email protected]>

commit 050f010f920da17c1044a4f174766ad553e770b6 upstream.

This patch is a follow-up to commit cf070fe33bfb ("can: isotp: serialize TX state transitions under so->rx_lock") which addresses following sashiko-bot findings:

- isotp_sendmsg(): drain so->txfrtimer first so a stale callback can't re-arm echotimer after the claim

- isotp_release(): wake so->wait after forcing ISOTP_SHUTDOWN so a sleeping sendmsg() claim isn't stranded

- isotp_sendmsg(): have both wait_event_interruptible() calls in isotp_sendmsg() also wake on ISOTP_SHUTDOWN and do not return claim to IDLE to avoid corrupting a concurrent isotp_release() process.

- isotp_sendmsg(): handle potential claim of a new transfer when the wait_event_interruptible() call returns in CAN_ISOTP_WAIT_TX_DONE mode. Don't touch timers and states of the new transfer if a new thread incremented so->tx_gen before getting the lock at err_event_drop.

- isotp_sendmsg(): handle a stuck can_send() and omit timer and state changes if a new transfer was claimed. wait_tx_done() returns the error recorded in so->tx_result[], tagged with the caller's own generation.

- isotp_tx_timeout(): on a claimed timeout, record the ECOMM error for the timed-out transfer's own generation in so->tx_result[]; sk->sk_err is raised unconditionally, same as every other error path here.

- isotp_tx_gen_done()/isotp_tx_timeout(): always read tx.state (acquire) before tx_gen - the reverse order let a weakly ordered CPU pair a fresh tx.state with a stale tx_gen/tx_result slot.

- isotp_sendmsg(): wait_tx_done: drain sk_err via sock_error() once we have read the result from so->tx_result[], so an already-reported error doesn't stay latched for a later poll()/SO_ERROR.

Also align the remaining lock-free so->tx.state/rx.state/cfecho accesses and use skb->hash as unique loopback echo frame indicator.

Fixes cf070fe33bfb ("can: isotp: serialize TX state transitions under so->rx_lock")
---
 net/can/isotp.c |  317 ++++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 230 insertions(+), 87 deletions(-)
net/can/isotp.c +230 -87
@@ -127,6 +127,15 @@ MODULE_PARM_DESC(max_pdu_size, "maximum
 #define ISOTP_FC_TIMEOUT 1	/* 1 sec */
 #define ISOTP_ECHO_TIMEOUT 2	/* 2 secs */
 
+/* so->tx_result[so->tx_gen % ISOTP_TX_RESULT_SLOTS] holds the packed value
+ * (err << ISOTP_TX_RESULT_GEN_BITS | gen) for each tx generation slot, so it
+ * can be handled with a single READ_ONCE()/WRITE_ONCE() access.
+ */
+#define ISOTP_TX_RESULT_SLOTS 4
+#define ISOTP_TX_RESULT_GEN_BITS 24
+#define ISOTP_TX_RESULT_GEN_MASK ((1U << ISOTP_TX_RESULT_GEN_BITS) - 1)
+#define ISOTP_TX_RESULT_ERR_MASK 0xFF
+
 enum {
 	ISOTP_IDLE = 0,
 	ISOTP_WAIT_FIRST_FC,
@@ -166,7 +175,8 @@ struct isotp_sock {
 	u32 force_tx_stmin;
 	u32 force_rx_stmin;
 	u32 cfecho; /* consecutive frame echo tag */
-	u32 tx_gen; /* generation, bumped per new tx transfer */
+	u32 tx_gen; /* transfer generation, increased per new tx transfer */
+	u32 tx_result[ISOTP_TX_RESULT_SLOTS]; /* per-generation result slots */
 	struct tpcon rx, tx;
 	struct list_head notifier;
 	wait_queue_head_t wait;
@@ -177,6 +187,65 @@ static LIST_HEAD(isotp_notifier_list);
 static DEFINE_SPINLOCK(isotp_notifier_lock);
 static struct isotp_sock *isotp_busy_notifier;
 
+/* increase (24 bit) tx generation value */
+static u32 isotp_inc_tx_gen(u32 gen)
+{
+	return (gen + 1) & ISOTP_TX_RESULT_GEN_MASK;
+}
+
+/* store 8 bit error and 24 bit tx generation values in packed u32 element */
+static u32 isotp_pack_tx_result(u32 gen, int err)
+{
+	return gen | ((u32)err << ISOTP_TX_RESULT_GEN_BITS);
+}
+
+/* get the 24 bit tx generation value from the tx result */
+static u32 isotp_get_tx_gen(u32 gen_err)
+{
+	return gen_err & ISOTP_TX_RESULT_GEN_MASK;
+}
+
+/* get the 8 bit error value from the tx result */
+static u32 isotp_get_tx_err(u32 gen_err)
+{
+	return (gen_err >> ISOTP_TX_RESULT_GEN_BITS) & ISOTP_TX_RESULT_ERR_MASK;
+}
+
+/* store transfer result in per-generation%4 so->tx_result[] slot */
+static void isotp_set_tx_result(struct isotp_sock *so, u32 gen, int err)
+{
+	WRITE_ONCE(so->tx_result[gen % ISOTP_TX_RESULT_SLOTS],
+		   isotp_pack_tx_result(gen, err));
+}
+
+/* fetch the result recorded for 'gen', as a (negative) errno (0 for success) */
+static int isotp_get_tx_result(struct isotp_sock *so, u32 gen)
+{
+	u32 result = READ_ONCE(so->tx_result[gen % ISOTP_TX_RESULT_SLOTS]);
+
+	if (isotp_get_tx_gen(result) != gen) {
+		pr_notice_once("can-isotp: tx_result[] slot reused before read\n");
+
+		/* report failure rather than risk a false success */
+		return -ECOMM;
+	}
+
+	return -(isotp_get_tx_err(result));
+}
+
+/* true if done, shut down or superseded ('gen' is no longer the active
+ * transfer). Reads tx.state first (acquire) so tx_gen/tx_result reads
+ * below see at least what that state write published (common sequence).
+ */
+static bool isotp_tx_gen_done(struct isotp_sock *so, u32 gen)
+{
+	/* read tx.state first for the common sequence */
+	u32 state = smp_load_acquire(&so->tx.state);
+
+	return state == ISOTP_IDLE || state == ISOTP_SHUTDOWN ||
+	       READ_ONCE(so->tx_gen) != gen;
+}
+
 static inline struct isotp_sock *isotp_sk(const struct sock *sk)
 {
 	return (struct isotp_sock *)sk;
@@ -199,7 +268,7 @@ static enum hrtimer_restart isotp_rx_tim
 					     rxtimer);
 	struct sock *sk = &so->sk;
 
-	if (so->rx.state == ISOTP_WAIT_DATA) {
+	if (READ_ONCE(so->rx.state) == ISOTP_WAIT_DATA) {
 		/* we did not get new data frames in time */
 
 		/* report 'connection timed out' */
@@ -208,7 +277,7 @@ static enum hrtimer_restart isotp_rx_tim
 			sk_error_report(sk);
 
 		/* reset rx state */
-		so->rx.state = ISOTP_IDLE;
+		WRITE_ONCE(so->rx.state, ISOTP_IDLE);
 	}
 
 	return HRTIMER_NORESTART;
@@ -372,20 +441,19 @@ static void isotp_send_cframe(struct iso
 static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
 {
 	struct sock *sk = &so->sk;
+	int tx_err = EBADMSG; /* default for unknown FC status */
 
-	if (so->tx.state != ISOTP_WAIT_FC &&
-	    so->tx.state != ISOTP_WAIT_FIRST_FC)
+	if (READ_ONCE(so->tx.state) != ISOTP_WAIT_FC &&
+	    READ_ONCE(so->tx.state) != ISOTP_WAIT_FIRST_FC)
 		return 0;
 
 	hrtimer_cancel(&so->txtimer);
 
 	/* isotp_tx_timeout() may have given up on this job while
-	 * hrtimer_cancel() above waited for it to finish; so->rx_lock
-	 * (held by our caller isotp_rcv()) rules out a concurrent claim,
-	 * so a plain recheck is enough here.
+	 * hrtimer_cancel() above waited for it to finish => recheck
 	 */
-	if (so->tx.state != ISOTP_WAIT_FC &&
-	    so->tx.state != ISOTP_WAIT_FIRST_FC)
+	if (READ_ONCE(so->tx.state) != ISOTP_WAIT_FC &&
+	    READ_ONCE(so->tx.state) != ISOTP_WAIT_FIRST_FC)
 		return 1;
 
 	if ((cf->len < ae + FC_CONTENT_SZ) ||
@@ -396,13 +464,15 @@ static int isotp_rcv_fc(struct isotp_soc
 		if (!sock_flag(sk, SOCK_DEAD))
 			sk_error_report(sk);
 
-		so->tx.state = ISOTP_IDLE;
+		isotp_set_tx_result(so, so->tx_gen, EBADMSG);
+		/* set to IDLE after publishing tx_result */
+		smp_store_release(&so->tx.state, ISOTP_IDLE);
 		wake_up_interruptible(&so->wait);
 		return 1;
 	}
 
 	/* get static/dynamic communication params from first/every FC frame */
-	if (so->tx.state == ISOTP_WAIT_FIRST_FC ||
+	if (READ_ONCE(so->tx.state) == ISOTP_WAIT_FIRST_FC ||
 	    so->opt.flags & CAN_ISOTP_DYN_FC_PARMS) {
 		so->txfc.bs = cf->data[ae + 1];
 		so->txfc.stmin = cf->data[ae + 2];
@@ -426,13 +496,13 @@ static int isotp_rcv_fc(struct isotp_soc
 			so->tx_gap = ktime_add_ns(so->tx_gap,
 						  (so->txfc.stmin - 0xF0)
 						  * 100000);
-		so->tx.state = ISOTP_WAIT_FC;
+		WRITE_ONCE(so->tx.state, ISOTP_WAIT_FC);
 	}
 
 	switch (cf->data[ae] & 0x0F) {
 	case ISOTP_FC_CTS:
 		so->tx.bs = 0;
-		so->tx.state = ISOTP_SENDING;
+		WRITE_ONCE(so->tx.state, ISOTP_SENDING);
 		/* send CF frame and enable echo timeout handling */
 		hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
 			      HRTIMER_MODE_REL_SOFT);
@@ -447,14 +517,19 @@ static int isotp_rcv_fc(struct isotp_soc
 
 	case ISOTP_FC_OVFLW:
 		/* overflow on receiver side - report 'message too long' */
-		sk->sk_err = EMSGSIZE;
-		if (!sock_flag(sk, SOCK_DEAD))
-			sk_error_report(sk);
+		tx_err = EMSGSIZE;
 		fallthrough;
 
 	default:
-		/* stop this tx job */
-		so->tx.state = ISOTP_IDLE;
+		/* reserved/unknown flow status (tx_err defaults to EBADMSG) */
+
+		sk->sk_err = tx_err;
+		if (!sock_flag(sk, SOCK_DEAD))
+			sk_error_report(sk);
+
+		isotp_set_tx_result(so, so->tx_gen, tx_err);
+		/* set to IDLE after publishing tx_result */
+		smp_store_release(&so->tx.state, ISOTP_IDLE);
 		wake_up_interruptible(&so->wait);
 	}
 	return 0;
@@ -467,7 +542,7 @@ static int isotp_rcv_sf(struct sock *sk,
 	struct sk_buff *nskb;
 
 	hrtimer_cancel(&so->rxtimer);
-	so->rx.state = ISOTP_IDLE;
+	WRITE_ONCE(so->rx.state, ISOTP_IDLE);
 
 	if (!len || len > cf->len - pcilen)
 		return 1;
@@ -501,7 +576,7 @@ static int isotp_rcv_ff(struct sock *sk,
 	int ff_pci_sz;
 
 	hrtimer_cancel(&so->rxtimer);
-	so->rx.state = ISOTP_IDLE;
+	WRITE_ONCE(so->rx.state, ISOTP_IDLE);
 
 	/* get the used sender LL_DL from the (first) CAN frame data length */
 	so->rx.ll_dl = padlen(cf->len);
@@ -555,7 +630,7 @@ static int isotp_rcv_ff(struct sock *sk,
 
 	/* initial setup for this pdu reception */
 	so->rx.sn = 1;
-	so->rx.state = ISOTP_WAIT_DATA;
+	WRITE_ONCE(so->rx.state, ISOTP_WAIT_DATA);
 
 	/* no creation of flow control frames */
 	if (so->opt.flags & CAN_ISOTP_LISTEN_MODE)
@@ -573,7 +648,7 @@ static int isotp_rcv_cf(struct sock *sk,
 	struct sk_buff *nskb;
 	int i;
 
-	if (so->rx.state != ISOTP_WAIT_DATA)
+	if (READ_ONCE(so->rx.state) != ISOTP_WAIT_DATA)
 		return 0;
 
 	/* drop if timestamp gap is less than force_rx_stmin nano secs */
@@ -588,11 +663,9 @@ static int isotp_rcv_cf(struct sock *sk,
 	hrtimer_cancel(&so->rxtimer);
 
 	/* isotp_rx_timer_handler() may have raced us for so->rx.state
-	 * while hrtimer_cancel() above waited for it to finish, already
-	 * reporting ETIMEDOUT and resetting the reception; don't process
-	 * this CF into a reassembly that has already been given up on.
+	 * while hrtimer_cancel() above waited for it to finish => recheck
 	 */
-	if (so->rx.state != ISOTP_WAIT_DATA)
+	if (READ_ONCE(so->rx.state) != ISOTP_WAIT_DATA)
 		return 1;
 
 	/* CFs are never longer than the FF */
@@ -613,7 +686,7 @@ static int isotp_rcv_cf(struct sock *sk,
 			sk_error_report(sk);
 
 		/* reset rx state */
-		so->rx.state = ISOTP_IDLE;
+		WRITE_ONCE(so->rx.state, ISOTP_IDLE);
 		return 1;
 	}
 	so->rx.sn++;
@@ -627,7 +700,7 @@ static int isotp_rcv_cf(struct sock *sk,
 
 	if (so->rx.idx >= so->rx.len) {
 		/* we are done */
-		so->rx.state = ISOTP_IDLE;
+		WRITE_ONCE(so->rx.state, ISOTP_IDLE);
 
 		if ((so->opt.flags & ISOTP_CHECK_PADDING) &&
 		    check_pad(so, cf, i + 1, so->opt.rxpad_content)) {
@@ -698,8 +771,10 @@ static void isotp_rcv(struct sk_buff *sk
 
 	if (so->opt.flags & CAN_ISOTP_HALF_DUPLEX) {
 		/* check rx/tx path half duplex expectations */
-		if ((so->tx.state != ISOTP_IDLE && n_pci_type != N_PCI_FC) ||
-		    (so->rx.state != ISOTP_IDLE && n_pci_type == N_PCI_FC))
+		if ((READ_ONCE(so->tx.state) != ISOTP_IDLE &&
+		     n_pci_type != N_PCI_FC) ||
+		    (READ_ONCE(so->rx.state) != ISOTP_IDLE &&
+		     n_pci_type == N_PCI_FC))
 			goto out_unlock;
 	}
 
@@ -794,6 +869,7 @@ static void isotp_send_cframe(struct iso
 	struct canfd_frame *cf;
 	int can_send_ret;
 	int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
+	u32 old_cfecho;
 
 	dev = dev_get_by_index(sock_net(sk), so->ifindex);
 	if (!dev)
@@ -814,6 +890,9 @@ static void isotp_send_cframe(struct iso
 
 	csx->can_iif = dev->ifindex;
 
+	/* set uid in tx skb to identify CF echo frames */
+	can_set_skb_uid(skb);
+
 	cf = (struct canfd_frame *)skb->data;
 	skb_put_zero(skb, so->ll.mtu);
 
@@ -830,12 +909,15 @@ static void isotp_send_cframe(struct iso
 	skb->dev = dev;
 	can_skb_set_owner(skb, sk);
 
-	/* cfecho should have been zero'ed by init/isotp_rcv_echo() */
-	if (so->cfecho)
-		pr_notice_once("can-isotp: cfecho is %08X != 0\n", so->cfecho);
+	/* zero'ed by init/isotp_rcv_echo(); reached lock-free via
+	 * isotp_txfr_timer_handler() too, so use READ_ONCE()/WRITE_ONCE()
+	 */
+	old_cfecho = READ_ONCE(so->cfecho);
+	if (old_cfecho)
+		pr_notice_once("can-isotp: cfecho is %08X != 0\n", old_cfecho);
 
 	/* set consecutive frame echo tag */
-	so->cfecho = *(u32 *)cf->data;
+	WRITE_ONCE(so->cfecho, skb->hash);
 
 	/* send frame with local echo enabled */
 	can_send_ret = can_send(skb, 1);
@@ -887,7 +969,6 @@ static void isotp_rcv_echo(struct sk_buf
 {
 	struct sock *sk = (struct sock *)data;
 	struct isotp_sock *so = isotp_sk(sk);
-	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
 
 	/* only handle my own local echo CF/SF skb's (no FF!) */
 	if (skb->sk != sk)
@@ -899,32 +980,35 @@ static void isotp_rcv_echo(struct sk_buf
 	spin_lock(&so->rx_lock);
 
 	/* so->cfecho may since belong to a new transfer; recheck under lock */
-	if (so->cfecho != *(u32 *)cf->data)
+	if (READ_ONCE(so->cfecho) != skb->hash)
 		goto out_unlock;
 
 	/* cancel local echo timeout */
 	hrtimer_cancel(&so->echotimer);
 
 	/* local echo skb with consecutive frame has been consumed */
-	so->cfecho = 0;
+	WRITE_ONCE(so->cfecho, 0);
 
 	/* claiming a transfer also takes so->rx_lock, so a plain recheck
 	 * is enough: so->tx.state can't have flipped to ISOTP_SENDING for
 	 * a new claim while we're still in here
 	 */
-	if (so->tx.state != ISOTP_SENDING)
+	if (READ_ONCE(so->tx.state) != ISOTP_SENDING)
 		goto out_unlock;
 
 	if (so->tx.idx >= so->tx.len) {
 		/* we are done */
-		so->tx.state = ISOTP_IDLE;
+
+		isotp_set_tx_result(so, so->tx_gen, 0);
+		/* set to IDLE after publishing tx_result */
+		smp_store_release(&so->tx.state, ISOTP_IDLE);
 		wake_up_interruptible(&so->wait);
 		goto out_unlock;
 	}
 
 	if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
 		/* stop and wait for FC with timeout */
-		so->tx.state = ISOTP_WAIT_FC;
+		WRITE_ONCE(so->tx.state, ISOTP_WAIT_FC);
 		hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
 			      HRTIMER_MODE_REL_SOFT);
 		goto out_unlock;
@@ -946,16 +1030,20 @@ out_unlock:
 	spin_unlock(&so->rx_lock);
 }
 
-/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get
- * cancelled under so->rx_lock elsewhere, so this must stay lock-free to
- * avoid deadlocking with that; uses so->tx_gen instead to avoid tainting
- * a new transfer with an error from the one that just timed out.
+/* isotp_tx_timeout: we did not get any flow control or echo frame in time
+ *
+ * Shared by so->txtimer's and so->echotimer's callbacks. Both timers get
+ * cancelled under so->rx_lock elsewhere, so this must stay lock-free.
+ *
+ * tx.state is acquired before tx_gen. Common sequence in isotp_tx_gen_done().
+ * cmpxchg() only orders itself, not the two preceding loads.
  */
 static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so)
 {
 	struct sock *sk = &so->sk;
+	/* read tx.state first for the common sequence */
+	u32 old_state = smp_load_acquire(&so->tx.state);
 	u32 gen = READ_ONCE(so->tx_gen);
-	u32 old_state = READ_ONCE(so->tx.state);
 
 	/* don't handle timeouts in IDLE or SHUTDOWN state */
 	if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN)
@@ -965,14 +1053,14 @@ static enum hrtimer_restart isotp_tx_tim
 	if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state)
 		return HRTIMER_NORESTART;
 
-	/* we did not get any flow control or echo frame in time */
+	/* detected timeout: report 'communication error on send' */
 
-	if (READ_ONCE(so->tx_gen) == gen) {
-		/* report 'communication error on send' */
-		sk->sk_err = ECOMM;
-		if (!sock_flag(sk, SOCK_DEAD))
-			sk_error_report(sk);
-	}
+	/* a stale read of this slot by a waiter still falls back to ECOMM */
+	isotp_set_tx_result(so, gen, ECOMM);
+
+	sk->sk_err = ECOMM;
+	if (!sock_flag(sk, SOCK_DEAD))
+		sk_error_report(sk);
 
 	wake_up_interruptible(&so->wait);
 
@@ -1007,7 +1095,7 @@ static enum hrtimer_restart isotp_txfr_t
 		      HRTIMER_MODE_REL_SOFT);
 
 	/* cfecho should be consumed by isotp_rcv_echo() here */
-	if (so->tx.state == ISOTP_SENDING && !so->cfecho)
+	if (READ_ONCE(so->tx.state) == ISOTP_SENDING && !READ_ONCE(so->cfecho))
 		isotp_send_cframe(so);
 
 	return HRTIMER_NORESTART;
@@ -1026,10 +1114,12 @@ static int isotp_sendmsg(struct socket *
 	s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
 	struct hrtimer *tx_hrt = &so->echotimer;
 	u32 new_state = ISOTP_SENDING;
+	u32 my_gen;
+	u32 old_cfecho;
 	int off;
 	int err;
 
-	if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
+	if (!so->bound || READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN)
 		return -EADDRNOTAVAIL;
 
 	/* claim the socket under so->rx_lock: this serializes the claim
@@ -1046,29 +1136,33 @@ static int isotp_sendmsg(struct socket *
 		if (msg->msg_flags & MSG_DONTWAIT)
 			return -EAGAIN;
 
-		if (so->tx.state == ISOTP_SHUTDOWN)
+		if (READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN)
 			return -EADDRNOTAVAIL;
 
 		/* wait for complete transmission of current pdu */
 		err = wait_event_interruptible(so->wait,
-					       so->tx.state == ISOTP_IDLE);
+					       READ_ONCE(so->tx.state) == ISOTP_IDLE ||
+					       READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN);
 		if (err)
 			return err;
 	}
 
-	/* new transfer: bump so->tx_gen and drain the old one's timers,
-	 * still under the so->rx_lock we just claimed the socket with
-	 */
-	WRITE_ONCE(so->tx.state, ISOTP_SENDING);
-	WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1);
+	/* txfrtimer's callback re-arms echotimer lock-free: drain it first */
+	hrtimer_cancel(&so->txfrtimer);
 	hrtimer_cancel(&so->txtimer);
 	hrtimer_cancel(&so->echotimer);
-	hrtimer_cancel(&so->txfrtimer);
-	so->cfecho = 0;
+
+	/* new transfer: increment so->tx_gen and set tx.state after barrier */
+	my_gen = isotp_inc_tx_gen(READ_ONCE(so->tx_gen));
+	isotp_set_tx_result(so, my_gen, ECOMM); /* prevent stale slot matching */
+	WRITE_ONCE(so->tx_gen, my_gen);
+	smp_wmb(); /* see smp_load_acquire() in isotp_tx_[timeout|gen_done] */
+	WRITE_ONCE(so->tx.state, ISOTP_SENDING);
+	WRITE_ONCE(so->cfecho, 0);
 	spin_unlock_bh(&so->rx_lock);
 
 	/* so->bound is only checked once above - a wakeup may have
-	 * unbound/rebound the socket meanwhile, so re-validate it
+	 * unbound/rebound the socket meanwhile => recheck
 	 */
 	if (!so->bound) {
 		err = -EADDRNOTAVAIL;
@@ -1127,6 +1221,9 @@ static int isotp_sendmsg(struct socket *
 
 	csx->can_iif = dev->ifindex;
 
+	/* set uid in tx skb to identify CF echo frames */
+	can_set_skb_uid(skb);
+
 	so->tx.len = size;
 	so->tx.idx = 0;
 
@@ -1134,8 +1231,9 @@ static int isotp_sendmsg(struct socket *
 	skb_put_zero(skb, so->ll.mtu);
 
 	/* cfecho should have been zero'ed by init / former isotp_rcv_echo() */
-	if (so->cfecho)
-		pr_notice_once("can-isotp: uninit cfecho %08X\n", so->cfecho);
+	old_cfecho = READ_ONCE(so->cfecho);
+	if (old_cfecho)
+		pr_notice_once("can-isotp: uninit cfecho %08X\n", old_cfecho);
 
 	/* check for single frame transmission depending on TX_DL */
 	if (size <= so->tx.ll_dl - SF_PCI_SZ4 - ae - off) {
@@ -1163,7 +1261,7 @@ static int isotp_sendmsg(struct socket *
 			cf->data[ae] |= size;
 
 		/* set CF echo tag for isotp_rcv_echo() (SF-mode) */
-		so->cfecho = *(u32 *)cf->data;
+		WRITE_ONCE(so->cfecho, skb->hash);
 	} else {
 		/* send first frame */
 
@@ -1180,7 +1278,7 @@ static int isotp_sendmsg(struct socket *
 			so->txfc.bs = 0;
 
 			/* set CF echo tag for isotp_rcv_echo() (CF-mode) */
-			so->cfecho = *(u32 *)cf->data;
+			WRITE_ONCE(so->cfecho, skb->hash);
 		} else {
 			/* standard flow control check */
 			new_state = ISOTP_WAIT_FIRST_FC;
@@ -1190,12 +1288,12 @@ static int isotp_sendmsg(struct socket *
 			tx_hrt = &so->txtimer;
 
 			/* no CF echo tag for isotp_rcv_echo() (FF-mode) */
-			so->cfecho = 0;
+			WRITE_ONCE(so->cfecho, 0);
 		}
 	}
 
 	spin_lock_bh(&so->rx_lock);
-	if (so->tx.state == ISOTP_SHUTDOWN) {
+	if (READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN) {
 		/* isotp_release() has since taken over and already drained
 		 * our timers - don't send into a socket that's going away
 		 */
@@ -1206,7 +1304,7 @@ static int isotp_sendmsg(struct socket *
 		return -EADDRNOTAVAIL;
 	}
 	/* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */
-	so->tx.state = new_state;
+	WRITE_ONCE(so->tx.state, new_state);
 	hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0),
 		      HRTIMER_MODE_REL_SOFT);
 	spin_unlock_bh(&so->rx_lock);
@@ -1223,20 +1321,49 @@ static int isotp_sendmsg(struct socket *
 			       __func__, ERR_PTR(err));
 
 		spin_lock_bh(&so->rx_lock);
+
+		/* new transfer already claimed by a concurrent completion,
+		 * timeout or sendmsg() while we were stuck in can_send()?
+		 */
+		if (READ_ONCE(so->tx_gen) != my_gen) {
+			/* don't touch timers and state of the new transfer */
+			spin_unlock_bh(&so->rx_lock);
+			return err;
+		}
+
 		/* no transmission -> no timeout monitoring */
 		hrtimer_cancel(tx_hrt);
 		goto err_out_drop_locked;
 	}
 
 	if (wait_tx_done) {
-		/* wait for complete transmission of current pdu */
-		err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
+		/* wake up for:
+		 * - concurrent sendmsg() claiming a new transfer
+		 * - complete transmission of current PDU
+		 * - shutdown state change in isotp_release()
+		 * isotp_tx_gen_done() uses common tx.state/tx_gen read sequence
+		 */
+		err = wait_event_interruptible(so->wait,
+					       isotp_tx_gen_done(so, my_gen));
 		if (err)
 			goto err_event_drop;
 
-		err = sock_error(sk);
-		if (err)
-			return err;
+		/* still our claim, but isotp_release() force-shut it down */
+		if (smp_load_acquire(&so->tx.state) == ISOTP_SHUTDOWN &&
+		    READ_ONCE(so->tx_gen) == my_gen) {
+			err = -EADDRNOTAVAIL;
+			goto err_event_drop;
+		}
+
+		/* own completion, or tx_gen moved on - either way this is
+		 * what isotp_get_tx_result() recorded for my_gen
+		 */
+		err = isotp_get_tx_result(so, my_gen);
+
+		/* drain to avoid stale error for a later poll()/SO_ERROR */
+		sock_error(sk);
+
+		return err ? err : size;
 	}
 
 	return size;
@@ -1246,15 +1373,26 @@ err_out_drop:
 	spin_lock_bh(&so->rx_lock);
 	goto err_out_drop_locked;
 err_event_drop:
-	/* interrupted waiting on our own transfer - drain its timers */
+	/* interrupted or shut down while waiting on our own transfer */
 	spin_lock_bh(&so->rx_lock);
+
+	/* new transfer already started by concurrent sendmsg()? */
+	if (READ_ONCE(so->tx_gen) != my_gen) {
+		/* don't touch timers and states of the new transfer */
+		spin_unlock_bh(&so->rx_lock);
+		return err;
+	}
+
 	hrtimer_cancel(&so->txfrtimer);
 	hrtimer_cancel(&so->txtimer);
 	hrtimer_cancel(&so->echotimer);
 err_out_drop_locked:
 	/* release the claim; so->rx_lock still held from above */
-	so->cfecho = 0;
-	so->tx.state = ISOTP_IDLE;
+	WRITE_ONCE(so->cfecho, 0);
+
+	/* only claim to IDLE if isotp_release() has not taken over */
+	if (READ_ONCE(so->tx.state) != ISOTP_SHUTDOWN)
+		WRITE_ONCE(so->tx.state, ISOTP_IDLE);
 	spin_unlock_bh(&so->rx_lock);
 	wake_up_interruptible(&so->wait);
 
@@ -1320,8 +1458,9 @@ static int isotp_release(struct socket *
 	/* best-effort: wait for a running pdu to finish, but don't block on
 	 * it forever - give up after the first signal
 	 */
-	while (so->tx.state != ISOTP_IDLE &&
-	       wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0)
+	while (READ_ONCE(so->tx.state) != ISOTP_IDLE &&
+	       wait_event_interruptible(so->wait,
+					READ_ONCE(so->tx.state) == ISOTP_IDLE) == 0)
 		;
 
 	/* claim the socket under so->rx_lock like sendmsg() does, so its
@@ -1329,9 +1468,12 @@ static int isotp_release(struct socket *
 	 * unconditionally, even when a signal cut the wait above short
 	 */
 	spin_lock_bh(&so->rx_lock);
-	so->tx.state = ISOTP_SHUTDOWN;
+	WRITE_ONCE(so->tx.state, ISOTP_SHUTDOWN);
 	spin_unlock_bh(&so->rx_lock);
-	so->rx.state = ISOTP_IDLE;
+	WRITE_ONCE(so->rx.state, ISOTP_IDLE);
+
+	/* forced SHUTDOWN may have skipped IDLE (gave up on a signal) */
+	wake_up_interruptible(&so->wait);
 
 	spin_lock(&isotp_notifier_lock);
 	while (isotp_busy_notifier == so) {
@@ -1447,7 +1589,8 @@ static int isotp_bind(struct socket *soc
 	 * with so->bound in the same lock_sock() section above, so there is
 	 * no window in which a concurrent isotp_notify() could be missed.
 	 */
-	if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) {
+	if (READ_ONCE(so->tx.state) != ISOTP_IDLE ||
+	    READ_ONCE(so->rx.state) != ISOTP_IDLE) {
 		err = -EAGAIN;
 		goto out;
 	}
@@ -1481,7 +1624,7 @@ static int isotp_bind(struct socket *soc
 				isotp_rcv, sk, "isotp", sk);
 
 	/* no consecutive frame echo skb in flight */
-	so->cfecho = 0;
+	WRITE_ONCE(so->cfecho, 0);
 
 	/* register for echo skb's */
 	can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
@@ -1847,7 +1990,7 @@ static __poll_t isotp_poll(struct file *
 	poll_wait(file, &so->wait, wait);
 
 	/* Check for false positives due to TX state */
-	if ((mask & EPOLLWRNORM) && (so->tx.state != ISOTP_IDLE))
+	if ((mask & EPOLLWRNORM) && (READ_ONCE(so->tx.state) != ISOTP_IDLE))
 		mask &= ~(EPOLLOUT | EPOLLWRNORM);
 
 	return mask;
author maintainer
#371 lore raw

PATCH 368/438 can: j1939: transport: j1939_session_fresh_new(): initialize receive buffer

In reply to Greg Kroah-Hartman

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

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

From: Oleksij Rempel <[email protected]>

commit eb96c58907922546e415e545fe9a14ea63b02719 upstream.

Zero the allocated buffer in j1939_session_fresh_new() to ensure it contains no residual data.

While there is a potential performance impact if users allocate maximum sized ETP buffers, most real-world use cases are not noticeably affected since the maximum known buffer size is typically around 65K.

Fixes 9d71dd0c7009 ("can: add support of SAE J1939 protocol")

Message-ID: <CAPAUci5dykCLjoijqkUtFqJFesgncrD7+S6y_V=gjbFkY2Tifg@mail.gmail.com>

---
 net/can/j1939/transport.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/can/j1939/transport.c +1 -1
@@ -1579,7 +1579,7 @@ j1939_session *j1939_session_fresh_new(s
 	}
 
 	/* alloc data area */
-	skb_put(skb, size);
+	skb_put_zero(skb, size);
 	/* skb is recounted in j1939_session_new() */
 	return session;
 }
author maintainer
#372 lore raw

PATCH 369/438 can: j1939: use netdevice_tracker for j1939_{priv,session,ecu} tracking

In reply to Greg Kroah-Hartman

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

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

From: Tetsuo Handa <[email protected]>

commit d2fb981384b3a45f690616d550b29046e8ad16a4 upstream.

syzbot is still reporting

unregister_netdevice: waiting for vcan0 to become free. Usage count = 2

problem. A debug printk() patch in linux-next-20260508 identified that there is dev_hold()/dev_put() imbalance in j1939_priv management.

Call trace for vcan0[26] +4 at

     __dev_hold include/linux/netdevice.h:4470 [inline]
     netdev_hold include/linux/netdevice.h:4513 [inline]
     dev_hold include/linux/netdevice.h:4536 [inline]
     j1939_priv_create net/can/j1939/main.c:140 [inline]
     j1939_netdev_start+0x36b/0xc10 net/can/j1939/main.c:268
     j1939_sk_bind+0x853/0xb30 net/can/j1939/socket.c:506
     __sys_bind_socket net/socket.c:1948 [inline]
     __sys_bind+0x2e9/0x410 net/socket.c:1979

Call trace for vcan0[28] -3 at

     __dev_put include/linux/netdevice.h:4456 [inline]
     netdev_put include/linux/netdevice.h:4523 [inline]
     dev_put include/linux/netdevice.h:4548 [inline]
     __j1939_priv_release net/can/j1939/main.c:166 [inline]
     kref_put include/linux/kref.h:65 [inline]
     j1939_priv_put+0x128/0x270 net/can/j1939/main.c:172
     j1939_sk_sock_destruct+0x52/0x90 net/can/j1939/socket.c:388
     __sk_destruct+0x8d/0x9d0 net/core/sock.c:2352
     rcu_do_batch kernel/rcu/tree.c:2617 [inline]
     rcu_core kernel/rcu/tree.c:2869 [inline]
     rcu_cpu_kthread+0x99e/0x1470 kernel/rcu/tree.c:2957
     smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160
     kthread+0x388/0x470 kernel/kthread.c:436
     ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
     ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245

This refcount leak in j1939_priv might be caused by a refcount leak in j1939_{session,ecu} because j1939_{session,ecu} holds a ref on j1939_priv. For further investigation using upstream kernels, enable netdevice_tracker in j1939_{priv,session,ecu} management.

---
 net/can/j1939/bus.c        |    2 ++
 net/can/j1939/j1939-priv.h |    3 +++
 net/can/j1939/main.c       |    8 ++++----
 net/can/j1939/transport.c  |    2 ++
 4 files changed, 11 insertions(+), 4 deletions(-)
net/can/j1939/bus.c +2 -0
@@ -20,6 +20,7 @@ static void __j1939_ecu_release(struct k
 	struct j1939_priv *priv = ecu->priv;
 
 	list_del(&ecu->list);
+	netdev_put(priv->ndev, &ecu->priv_dev_tracker);
 	kfree(ecu);
 	j1939_priv_put(priv);
 }
@@ -155,6 +156,7 @@ struct j1939_ecu *j1939_ecu_create_locke
 	if (!ecu)
 		return ERR_PTR(-ENOMEM);
 	kref_init(&ecu->kref);
+	netdev_hold(priv->ndev, &ecu->priv_dev_tracker, gfp_any());
 	ecu->addr = J1939_IDLE_ADDR;
 	ecu->name = name;
 
net/can/j1939/j1939-priv.h +3 -0
@@ -38,6 +38,7 @@ struct j1939_ecu {
 	struct hrtimer ac_timer;
 	struct kref kref;
 	struct j1939_priv *priv;
+	netdevice_tracker priv_dev_tracker;
 
 	/* count users, to help transport protocol decide for interaction */
 	int nusers;
@@ -60,6 +61,7 @@ struct j1939_priv {
 	rwlock_t lock;
 
 	struct net_device *ndev;
+	netdevice_tracker dev_tracker;
 
 	/* list of 256 ecu ptrs, that cache the claimed addresses.
 	 * also protected by the above lock
@@ -230,6 +232,7 @@ enum j1939_session_state {
 
 struct j1939_session {
 	struct j1939_priv *priv;
+	netdevice_tracker priv_dev_tracker;
 	struct list_head active_session_list_entry;
 	struct list_head sk_session_queue_entry;
 	struct kref kref;
net/can/j1939/main.c +4 -4
@@ -137,7 +137,7 @@ static struct j1939_priv *j1939_priv_cre
 	priv->ndev = ndev;
 	kref_init(&priv->kref);
 	kref_init(&priv->rx_kref);
-	dev_hold(ndev);
+	netdev_hold(ndev, &priv->dev_tracker, GFP_KERNEL);
 
 	netdev_dbg(priv->ndev, "%s : 0x%p\n", __func__, priv);
 
@@ -163,7 +163,7 @@ static void __j1939_priv_release(struct
 	WARN_ON_ONCE(!list_empty(&priv->ecus));
 	WARN_ON_ONCE(!list_empty(&priv->j1939_socks));
 
-	dev_put(ndev);
+	netdev_put(ndev, &priv->dev_tracker);
 	kfree(priv);
 }
 
@@ -281,7 +281,7 @@ struct j1939_priv *j1939_netdev_start(st
 		 */
 		kref_get(&priv_new->rx_kref);
 		mutex_unlock(&j1939_netdev_lock);
-		dev_put(ndev);
+		netdev_put(ndev, &priv->dev_tracker);
 		kfree(priv);
 		return priv_new;
 	}
@@ -298,7 +298,7 @@ struct j1939_priv *j1939_netdev_start(st
 	j1939_priv_set(ndev, NULL);
 	mutex_unlock(&j1939_netdev_lock);
 
-	dev_put(ndev);
+	netdev_put(ndev, &priv->dev_tracker);
 	kfree(priv);
 
 	return ERR_PTR(ret);
net/can/j1939/transport.c +2 -0
@@ -283,6 +283,7 @@ static void j1939_session_destroy(struct
 		kfree_skb(skb);
 	}
 	__j1939_session_drop(session);
+	netdev_put(session->priv->ndev, &session->priv_dev_tracker);
 	j1939_priv_put(session->priv);
 	kfree(session);
 }
@@ -1526,6 +1527,7 @@ static struct j1939_session *j1939_sessi
 	INIT_LIST_HEAD(&session->active_session_list_entry);
 	INIT_LIST_HEAD(&session->sk_session_queue_entry);
 	kref_init(&session->kref);
+	netdev_hold(priv->ndev, &session->priv_dev_tracker, gfp_any());
 
 	j1939_priv_get(priv);
 	session->priv = priv;
author maintainer
#373 lore raw

PATCH 370/438 can: kvaser_usb: kvaser_usb_hydra_get_busparams(): fix memory leak in kvaser_usb_hydra_get_busparams()

In reply to Greg Kroah-Hartman

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

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

From: Abdun Nihaal <[email protected]>

commit 941eaf9a6d3b33dea49f2c0a1da7546a03b6ff71 upstream.

The memory allocated for cmd is not freed after the call to kvaser_usb_send_cmd() in both the normal and error paths. Fix that by adding a kfree() immediately after the call.

Fixes 39d3df6b0ea8 ("can: kvaser_usb: Compare requested bittiming parameters with actual parameters in do_set_{,data}_bittiming")
---
 drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +1 -0
@@ -1626,6 +1626,7 @@ static int kvaser_usb_hydra_get_busparam
 	reinit_completion(&priv->get_busparams_comp);
 
 	err = kvaser_usb_send_cmd(dev, cmd, cmd_len);
+	kfree(cmd);
 	if (err)
 		return err;
 
author maintainer
#374 lore raw

PATCH 371/438 can: kvaser_usb_leaf: kvaser_usb_leaf_wait_cmd(): validate received command extents

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

commit 0293dd153f9dbc1ddf5dacdccc76b363bce4a8ee upstream.

The wait and bulk receive paths walk variable-length commands from a USB buffer. A nonzero command shorter than CMD_HEADER_LEN can still be dispatched, and the wait path copies a matching command into a fixed caller-owned struct kvaser_cmd using the device-provided length.

Reject nonzero commands that do not contain the fixed header or that extend beyond the current USB buffer item. In the wait path, also reject a matching command that exceeds the destination before copying it.

Fixes 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices")
---
 drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +11 -2
@@ -691,13 +691,22 @@ static int kvaser_usb_leaf_wait_cmd(cons
 				continue;
 			}
 
-			if (pos + tmp->len > actual_len) {
+			if (tmp->len < CMD_HEADER_LEN ||
+			    tmp->len > actual_len - pos) {
 				dev_err_ratelimited(&dev->intf->dev,
 						    "Format error\n");
 				break;
 			}
 
 			if (tmp->id == id) {
+				if (tmp->len > sizeof(*cmd)) {
+					dev_err_ratelimited(&dev->intf->dev,
+							    "Received command %u too large (%u)\n",
+							    tmp->id, tmp->len);
+					err = -EIO;
+					goto end;
+				}
+
 				memcpy(cmd, tmp, tmp->len);
 				goto end;
 			}
@@ -1737,7 +1746,7 @@ static void kvaser_usb_leaf_read_bulk_ca
 			continue;
 		}
 
-		if (pos + cmd->len > len) {
+		if (cmd->len < CMD_HEADER_LEN || cmd->len > len - pos) {
 			dev_err_ratelimited(&dev->intf->dev, "Format error\n");
 			break;
 		}
author maintainer
#375 lore raw

PATCH 372/438 can: rcar_canfd: change the initializing flow for clocks and resets

In reply to Greg Kroah-Hartman

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

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

From: Tu Nguyen <[email protected]>

commit bef9004c5b91debfceaea2841855a4ebe81ff2b3 upstream.

Testing CANFD on RZ/G3E shows that many registers do not reset to their initial values with the current flow of deasserting resets first and then enabling clocks.

Based on the HW manual, clocks should be supplied first and the resets deasserted afterward.

section 7.4.3 Procedure for Activating Modules: RZ/G2L section 4.4.9.3 Procedure for Starting up Units: RZ/G3E

So, update the order of the initializing flow for resets and clocks to match the hardware manual, resetting all CANFD registers to their initial values. Also update rcar_canfd_global_deinit() to assert resets before disabling clocks, so the teardown path mirrors the new init ordering.

---
 drivers/net/can/rcar/rcar_canfd.c |   32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
drivers/net/can/rcar/rcar_canfd.c +16 -16
@@ -2003,20 +2003,12 @@ static int rcar_canfd_global_init(struct
 	u32 ch, sts;
 	int err;
 
-	err = reset_control_reset(gpriv->rstc1);
-	if (err)
-		return err;
-
-	err = reset_control_reset(gpriv->rstc2);
-	if (err)
-		goto fail_reset1;
-
 	/* Enable peripheral clock for register access */
 	err = clk_prepare_enable(gpriv->clkp);
 	if (err) {
 		dev_err(dev, "failed to enable peripheral clock: %pe\n",
 			ERR_PTR(err));
-		goto fail_reset2;
+		return err;
 	}
 
 	/* Enable RAM clock */
@@ -2027,10 +2019,18 @@ static int rcar_canfd_global_init(struct
 		goto fail_clk;
 	}
 
+	err = reset_control_reset(gpriv->rstc1);
+	if (err)
+		goto fail_ram_clk;
+
+	err = reset_control_reset(gpriv->rstc2);
+	if (err)
+		goto fail_reset1;
+
 	err = rcar_canfd_reset_controller(gpriv);
 	if (err) {
 		dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err));
-		goto fail_ram_clk;
+		goto fail_reset2;
 	}
 
 	/* Controller in Global reset & Channel reset mode */
@@ -2068,14 +2068,14 @@ static int rcar_canfd_global_init(struct
 
 fail_mode:
 	rcar_canfd_disable_global_interrupts(gpriv);
-fail_ram_clk:
-	clk_disable_unprepare(gpriv->clk_ram);
-fail_clk:
-	clk_disable_unprepare(gpriv->clkp);
 fail_reset2:
 	reset_control_assert(gpriv->rstc2);
 fail_reset1:
 	reset_control_assert(gpriv->rstc1);
+fail_ram_clk:
+	clk_disable_unprepare(gpriv->clk_ram);
+fail_clk:
+	clk_disable_unprepare(gpriv->clkp);
 	return err;
 }
 
@@ -2090,10 +2090,10 @@ static void rcar_canfd_global_deinit(str
 		rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR);
 	}
 
-	clk_disable_unprepare(gpriv->clk_ram);
-	clk_disable_unprepare(gpriv->clkp);
 	reset_control_assert(gpriv->rstc2);
 	reset_control_assert(gpriv->rstc1);
+	clk_disable_unprepare(gpriv->clk_ram);
+	clk_disable_unprepare(gpriv->clkp);
 }
 
 static int rcar_canfd_probe(struct platform_device *pdev)
author maintainer
#376 lore raw

PATCH 373/438 can: softing: fw_parse(): validate firmware record spans

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

commit 856d6cb04e5407523566b075841dcd6423757d1c upstream.

fw_parse() reads a fixed record header, a firmware-provided payload, and a trailing checksum without knowing the end of the firmware blob. A truncated record can therefore make those reads exceed the blob.

The same record also supplies addresses and lengths for writes into DPRAM. The generic loader uses wrap-prone mixed signed arithmetic for its bounds check, while the application loader does not bound the staging copy at all.

Pass the firmware end to the parser and validate the full source record. Use a signed wide offset for generic DPRAM records and validate the application staging span against the mapped DPRAM before copying.

---
 drivers/net/can/softing/softing_fw.c |   46 ++++++++++++++++++++++++-----------
 1 file changed, 32 insertions(+), 14 deletions(-)
drivers/net/can/softing/softing_fw.c +32 -14
@@ -91,12 +91,12 @@ int softing_bootloader_command(struct so
 	return ret;
 }
 
-static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr,
-		uint16_t *plen, const uint8_t **pdat)
+static int fw_parse(const u8 **pmem, const u8 *limit, u16 *ptype,
+		    u32 *paddr, u16 *plen, const u8 **pdat)
 {
 	uint16_t checksum[2];
-	const uint8_t *mem;
-	const uint8_t *end;
+	const u8 *mem;
+	const u8 *record_end;
 
 	/*
 	 * firmware records are a binary, unaligned stream composed of:
@@ -114,14 +114,21 @@ static int fw_parse(const uint8_t **pmem
 	 * endianness & alignment.
 	 */
 	mem = *pmem;
+	/* A record needs an 8-byte prefix and a 2-byte checksum. */
+	if (mem > limit || limit - mem < 10)
+		return -EINVAL;
+
 	*ptype = le16_to_cpup((void *)&mem[0]);
 	*paddr = le32_to_cpup((void *)&mem[2]);
 	*plen = le16_to_cpup((void *)&mem[6]);
+	if (*plen > limit - mem - 10)
+		return -EINVAL;
+
 	*pdat = &mem[8];
 	/* verify checksum */
-	end = &mem[8 + *plen];
-	checksum[0] = le16_to_cpup((void *)end);
-	for (checksum[1] = 0; mem < end; ++mem)
+	record_end = &mem[8 + *plen];
+	checksum[0] = le16_to_cpup((void *)record_end);
+	for (checksum[1] = 0; mem < record_end; ++mem)
 		checksum[1] += *mem;
 	if (checksum[0] != checksum[1])
 		return -EINVAL;
@@ -139,6 +146,7 @@ int softing_load_fw(const char *file, st
 	uint16_t type, len;
 	uint32_t addr;
 	uint8_t *buf = NULL, *new_buf;
+	s64 dpram_offset;
 	int buflen = 0;
 	int8_t type_end = 0;
 
@@ -153,7 +161,7 @@ int softing_load_fw(const char *file, st
 	mem = fw->data;
 	end = &mem[fw->size];
 	/* look for header record */
-	ret = fw_parse(&mem, &type, &addr, &len, &dat);
+	ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
 	if (ret < 0)
 		goto failed;
 	if (type != 0xffff)
@@ -164,7 +172,7 @@ int softing_load_fw(const char *file, st
 	}
 	/* ok, we had a header */
 	while (mem < end) {
-		ret = fw_parse(&mem, &type, &addr, &len, &dat);
+		ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
 		if (ret < 0)
 			goto failed;
 		if (type == 3) {
@@ -179,9 +187,13 @@ int softing_load_fw(const char *file, st
 			goto failed;
 		}
 
-		if ((addr + len + offset) > size)
+		dpram_offset = (s64)addr + offset;
+		if (dpram_offset < 0 || dpram_offset > size ||
+		    len > size - dpram_offset) {
+			ret = -EINVAL;
 			goto failed;
-		memcpy_toio(&dpram[addr + offset], dat, len);
+		}
+		memcpy_toio(&dpram[dpram_offset], dat, len);
 		/* be sure to flush caches from IO space */
 		mb();
 		if (len > buflen) {
@@ -195,7 +207,7 @@ int softing_load_fw(const char *file, st
 			buf = new_buf;
 		}
 		/* verify record data */
-		memcpy_fromio(buf, &dpram[addr + offset], len);
+		memcpy_fromio(buf, &dpram[dpram_offset], len);
 		if (memcmp(buf, dat, len)) {
 			/* is not ok */
 			dev_alert(&card->pdev->dev, "DPRAM readback failed\n");
@@ -237,7 +249,7 @@ int softing_load_app_fw(const char *file
 	mem = fw->data;
 	end = &mem[fw->size];
 	/* look for header record */
-	ret = fw_parse(&mem, &type, &addr, &len, &dat);
+	ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
 	if (ret)
 		goto failed;
 	ret = -EINVAL;
@@ -253,7 +265,7 @@ int softing_load_app_fw(const char *file
 	}
 	/* ok, we had a header */
 	while (mem < end) {
-		ret = fw_parse(&mem, &type, &addr, &len, &dat);
+		ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
 		if (ret)
 			goto failed;
 
@@ -279,6 +291,12 @@ int softing_load_app_fw(const char *file
 		/* work in 16bit (target) */
 		sum &= 0xffff;
 
+		if (card->pdat->app.offs > card->dpram_size ||
+		    len > card->dpram_size - card->pdat->app.offs) {
+			ret = -EINVAL;
+			goto failed;
+		}
+
 		memcpy_toio(&card->dpram[card->pdat->app.offs], dat, len);
 		iowrite32(card->pdat->app.offs + card->pdat->app.addr,
 				&card->dpram[DPRAM_COMMAND + 2]);
author maintainer
#377 lore raw

PATCH 374/438 can: peak_usb: add bounds check for USB channel index

In reply to Greg Kroah-Hartman

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

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

From: James Gao <[email protected]>

commit 39132f166ca8ce00ae60d8a9068e06a60943cc4b upstream.

The channel control index ctrl_idx is derived from rx->len which comes directly from a device USB payload. The mask 0x0f allows values 0-15, but the array size of usb_if->dev[] is only 2. Values 2-15 cause heap out-of-bounds read, eventually causing kernel panic in the IRQ context.

Add bounds checking for ctrl_idx before the array access in both pcan_usb_pro_handle_canmsg() and pcan_usb_pro_handle_error().

---
 drivers/net/can/usb/peak_usb/pcan_usb_pro.c |   20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)
drivers/net/can/usb/peak_usb/pcan_usb_pro.c +16 -4
@@ -534,12 +534,18 @@ static int pcan_usb_pro_handle_canmsg(st
 				      struct pcan_usb_pro_rxmsg *rx)
 {
 	const unsigned int ctrl_idx = (rx->len >> 4) & 0x0f;
-	struct peak_usb_device *dev = usb_if->dev[ctrl_idx];
-	struct net_device *netdev = dev->netdev;
+	struct peak_usb_device *dev;
+	struct net_device *netdev;
 	struct can_frame *can_frame;
 	struct sk_buff *skb;
 	struct skb_shared_hwtstamps *hwts;
 
+	if (ctrl_idx >= ARRAY_SIZE(usb_if->dev))
+		return -EINVAL;
+
+	dev = usb_if->dev[ctrl_idx];
+	netdev = dev->netdev;
+
 	skb = alloc_can_skb(netdev, &can_frame);
 	if (!skb)
 		return -ENOMEM;
@@ -573,14 +579,20 @@ static int pcan_usb_pro_handle_error(str
 {
 	const u16 raw_status = le16_to_cpu(er->status);
 	const unsigned int ctrl_idx = (er->channel >> 4) & 0x0f;
-	struct peak_usb_device *dev = usb_if->dev[ctrl_idx];
-	struct net_device *netdev = dev->netdev;
+	struct peak_usb_device *dev;
+	struct net_device *netdev;
 	struct can_frame *can_frame;
 	enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
 	u8 err_mask = 0;
 	struct sk_buff *skb;
 	struct skb_shared_hwtstamps *hwts;
 
+	if (ctrl_idx >= ARRAY_SIZE(usb_if->dev))
+		return -EINVAL;
+
+	dev = usb_if->dev[ctrl_idx];
+	netdev = dev->netdev;
+
 	/* nothing should be sent while in BUS_OFF state */
 	if (dev->can.state == CAN_STATE_BUS_OFF)
 		return 0;
author maintainer
#378 lore raw

PATCH 375/438 can: peak_usb: peak_usb_start(): fix double free of transfer buffer on URB submit error

In reply to Greg Kroah-Hartman

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

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

From: Maoyi Xie <[email protected]>

commit 9b3d5a6d952c38bbcf07f903cbeadefdb56b9bc9 upstream.

In peak_usb_start(), each RX URB transfer buffer is allocated with kmalloc() and the URB is flagged URB_FREE_BUFFER so that the final usb_free_urb() also frees the transfer buffer.

If usb_submit_urb() fails, the error path frees the buffer explicitly with kfree(buf) and then calls usb_free_urb(urb). Because URB_FREE_BUFFER is set, usb_free_urb() -> urb_destroy() frees the same buffer a second time, a double free of the transfer buffer.

  BUG: KASAN: double-free in usb_free_urb.part.0+0x91/0xb0
  Free of addr ffff8881069ccb80 by task trigger.sh/285

  Call Trace:
   kfree+0x113/0x3c0
   usb_free_urb.part.0+0x91/0xb0

Drop the redundant kfree(buf); usb_free_urb() already releases the transfer buffer. This mirrors commit 03819abbeb11 ("net: usb: lan78xx: Fix double free issue with interrupt buffer allocation").

---
 drivers/net/can/usb/peak_usb/pcan_usb_core.c |    1 -
 1 file changed, 1 deletion(-)
drivers/net/can/usb/peak_usb/pcan_usb_core.c +0 -1
@@ -470,7 +470,6 @@ static int peak_usb_start(struct peak_us
 				netif_device_detach(dev->netdev);
 
 			usb_unanchor_urb(urb);
-			kfree(buf);
 			usb_free_urb(urb);
 			break;
 		}
author maintainer
#379 lore raw

PATCH 376/438 can: peak_usb: validate uCAN receive record lengths

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

commit 93fcab2c6968446316bbb49548848df604d6346f upstream.

pcan_usb_fd_decode_buf() walks uCAN records packed in one USB receive buffer.

Require each record to contain the fixed header for its type, and verify CAN payload bytes before copying them into the skb.

Fixes 0a25e1f4f185 ("can: peak_usb: add support for PEAK new CANFD USB adapters")
---
 drivers/net/can/usb/peak_usb/pcan_usb_fd.c |   40 ++++++++++++++++++++++++++++-
 1 file changed, 39 insertions(+), 1 deletion(-)
drivers/net/can/usb/peak_usb/pcan_usb_fd.c +39 -1
@@ -566,6 +566,13 @@ static int pcan_usb_fd_decode_canmsg(str
 				     dev->can.ctrlmode);
 	}
 
+	if (!(rx_msg_flags & PUCAN_MSG_RTR) &&
+	    le16_to_cpu(rx_msg->size) - offsetof(struct pucan_rx_msg, d) <
+	    cfd->len) {
+		kfree_skb(skb);
+		return -EBADMSG;
+	}
+
 	cfd->can_id = le32_to_cpu(rm->can_id);
 
 	if (rx_msg_flags & PUCAN_MSG_EXT_ID)
@@ -714,6 +721,24 @@ static void pcan_usb_fd_decode_ts(struct
 		peak_usb_set_ts_now(&usb_if->time_ref, le32_to_cpu(ts->ts_low));
 }
 
+static size_t pcan_usb_fd_rx_msg_min_size(u16 rx_msg_type)
+{
+	switch (rx_msg_type) {
+	case PUCAN_MSG_CAN_RX:
+		return offsetof(struct pucan_rx_msg, d);
+	case PCAN_UFD_MSG_CALIBRATION:
+		return sizeof(struct pcan_ufd_ts_msg);
+	case PUCAN_MSG_ERROR:
+		return sizeof(struct pucan_error_msg);
+	case PUCAN_MSG_STATUS:
+		return sizeof(struct pucan_status_msg);
+	case PCAN_UFD_MSG_OVERRUN:
+		return sizeof(struct pcan_ufd_ovr_msg);
+	default:
+		return sizeof(struct pucan_msg);
+	}
+}
+
 /* callback for bulk IN urb */
 static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb)
 {
@@ -728,6 +753,12 @@ static int pcan_usb_fd_decode_buf(struct
 	msg_end = urb->transfer_buffer + urb->actual_length;
 	for (; msg_ptr < msg_end;) {
 		u16 rx_msg_type, rx_msg_size;
+		size_t rx_msg_min_size;
+
+		if (msg_end - msg_ptr < sizeof(*rx_msg)) {
+			err = -EBADMSG;
+			break;
+		}
 
 		rx_msg = (struct pucan_msg *)msg_ptr;
 		if (!rx_msg->size) {
@@ -739,12 +770,19 @@ static int pcan_usb_fd_decode_buf(struct
 		rx_msg_type = le16_to_cpu(rx_msg->type);
 
 		/* check if the record goes out of current packet */
-		if (msg_ptr + rx_msg_size > msg_end) {
+		if (rx_msg_size > msg_end - msg_ptr) {
 			netdev_err(netdev,
 				   "got frag rec: should inc usb rx buf sze\n");
 			err = -EBADMSG;
 			break;
 		}
+
+		rx_msg_min_size = pcan_usb_fd_rx_msg_min_size(rx_msg_type);
+		if (rx_msg_size < rx_msg_min_size) {
+			netdev_err(netdev, "got short rec\n");
+			err = -EBADMSG;
+			break;
+		}
 
 		switch (rx_msg_type) {
 		case PUCAN_MSG_CAN_RX:
author maintainer
#380 lore raw

PATCH 377/438 can: ctucanfd: add missing MODULE_DEVICE_TABLE()

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

commit d937bdb244a751fe5967052ea2d64a7b2c476cc0 upstream.

The driver has a match table for the pci bus wired into its driver structure, but the table is not exported with MODULE_DEVICE_TABLE().

Add the missing MODULE_DEVICE_TABLE() entry so module alias information is generated for automatic module loading.

This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the driver registration structure, and the missing module alias publication.

Fixes 792a5b678e81 ("can: ctucanfd: CTU CAN FD open-source IP core - PCI bus support.")
---
 drivers/net/can/ctucanfd/ctucanfd_pci.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/can/ctucanfd/ctucanfd_pci.c +1 -0
@@ -266,6 +266,7 @@ static const struct pci_device_id ctucan
 		CTUCAN_WITH_CTUCAN_ID)},
 	{},
 };
+MODULE_DEVICE_TABLE(pci, ctucan_pci_tbl);
 
 static struct pci_driver ctucan_pci_driver = {
 	.name = KBUILD_MODNAME,
author maintainer
#381 lore raw

PATCH 378/438 can: ctucanfd: use self-test mode for PRESUME_ACK

In reply to Greg Kroah-Hartman

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

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

From: Avi Weiss <[email protected]>

commit c31a435933f18be0f874302161333e9f16e200a0 upstream.

Use self-test mode for CAN_CTRLMODE_PRESUME_ACK so transmitted frames can complete without receiving an ACK.

ACK forbidden mode prevents the controller from acknowledging received frames and does not implement the presume-ack behavior.

Fixes 2dcb8e8782d8 ("can: ctucanfd: add support for CTU CAN FD open-source IP core - bus independent part.")
---
 drivers/net/can/ctucanfd/ctucanfd_base.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/can/ctucanfd/ctucanfd_base.c +2 -2
@@ -340,8 +340,8 @@ static void ctucan_set_mode(struct ctuca
 			(mode_reg & ~REG_MODE_FDE);
 
 	mode_reg = (mode->flags & CAN_CTRLMODE_PRESUME_ACK) ?
-			(mode_reg | REG_MODE_ACF) :
-			(mode_reg & ~REG_MODE_ACF);
+			(mode_reg | REG_MODE_STM) :
+			(mode_reg & ~REG_MODE_STM);
 
 	mode_reg = (mode->flags & CAN_CTRLMODE_FD_NON_ISO) ?
 			(mode_reg | REG_MODE_NISOFD) :
author maintainer
#382 lore raw

PATCH 379/438 can: ctucanfd: unmap BAR0 using base address

In reply to Greg Kroah-Hartman

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

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

From: Avi Weiss <[email protected]>

commit a6873910f983096746d1a2e0af94f36b8003e839 upstream.

BAR0 is mapped into bar0_base, while cra_addr points to an offset within that mapping and is used for other purposes.

Pass bar0_base to pci_iounmap(), instead of cra_addr, on the probe error path so the address returned by pci_iomap() is used for unmapping.

Fixes 792a5b678e81 ("can: ctucanfd: CTU CAN FD open-source IP core - PCI bus support.")
---
 drivers/net/can/ctucanfd/ctucanfd_pci.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/can/ctucanfd/ctucanfd_pci.c +1 -1
@@ -194,7 +194,7 @@ err_free_board:
 	pci_set_drvdata(pdev, NULL);
 	kfree(bdata);
 err_pci_iounmap_bar0:
-	pci_iounmap(pdev, cra_addr);
+	pci_iounmap(pdev, bar0_base);
 err_pci_iounmap_bar1:
 	pci_iounmap(pdev, addr);
 err_release_regions:
author maintainer
#383 lore raw

PATCH 380/438 can: ctucanfd: handle bus error interrupts

In reply to Greg Kroah-Hartman

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

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

From: Avi Weiss <[email protected]>

commit e74bae899529f49c0f375307983d12e8ecad7d4b upstream.

Include REG_INT_STAT_BEI in the top-level error interrupt condition.

BEI is enabled when CAN_CTRLMODE_BERR_REPORTING is requested and ctucan_err_interrupt() already handles it. Without checking and clearing BEI in the top-level handler, bus error interrupts are not handled or acknowledged.

Fixes 2dcb8e8782d8 ("can: ctucanfd: add support for CTU CAN FD open-source IP core - bus independent part.")
---
 drivers/net/can/ctucanfd/ctucanfd_base.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/net/can/ctucanfd/ctucanfd_base.c +6 -2
@@ -1136,8 +1136,12 @@ static irqreturn_t ctucan_interrupt(int
 		/* Error interrupts */
 		if (FIELD_GET(REG_INT_STAT_EWLI, isr) ||
 		    FIELD_GET(REG_INT_STAT_FCSI, isr) ||
-		    FIELD_GET(REG_INT_STAT_ALI, isr)) {
-			icr = isr & (REG_INT_STAT_EWLI | REG_INT_STAT_FCSI | REG_INT_STAT_ALI);
+		    FIELD_GET(REG_INT_STAT_ALI, isr) ||
+		    FIELD_GET(REG_INT_STAT_BEI, isr)) {
+			icr = isr & (REG_INT_STAT_EWLI |
+				     REG_INT_STAT_FCSI |
+				     REG_INT_STAT_ALI |
+				     REG_INT_STAT_BEI);
 
 			ctucan_netdev_dbg(ndev, "some ERR interrupt: clearing 0x%08x\n", icr);
 			ctucan_write32(priv, CTUCANFD_INT_STAT, icr);
author maintainer
#384 lore raw

PATCH 381/438 can: ctucanfd: mark error-active controller status valid

In reply to Greg Kroah-Hartman

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

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

From: Avi Weiss <[email protected]>

commit 4e735cbe3affe88001428fdd9cae8e685ce92f21 upstream.

In the CAN_STATE_ERROR_ACTIVE case, cf->data[1] is set to CAN_ERR_CRTL_ACTIVE, but cf->can_id is not set with CAN_ERR_CRTL in that path.

Set CAN_ERR_CRTL so consumers know the controller-status information in cf->data[1] is valid.

Fixes 9bd24927e3ee ("can: ctucanfd: handle skb allocation failure")
---
 drivers/net/can/ctucanfd/ctucanfd_base.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/can/ctucanfd/ctucanfd_base.c +1 -1
@@ -869,7 +869,7 @@ static void ctucan_err_interrupt(struct
 			break;
 		case CAN_STATE_ERROR_ACTIVE:
 			if (skb) {
-				cf->can_id |= CAN_ERR_CNT;
+				cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
 				cf->data[1] = CAN_ERR_CRTL_ACTIVE;
 				cf->data[6] = bec.txerr;
 				cf->data[7] = bec.rxerr;
author maintainer
#385 lore raw

PATCH 382/438 drm/dp: Read the PCON max FRL bandwidth only for HDMI DFPs

In reply to Greg Kroah-Hartman

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

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

From: Alexander Kaplan <[email protected]>

commit e40e20ac089e32f1d910636155dc82e61e61dcf3 upstream.

The PCON max FRL bandwidth field lives in byte 2 of the DFP Detailed Capability Info (DPCD 0x82 for the first DFP).
The DP standard defines the meaning of descriptor bytes 1-3 strictly per DFP type, and for a DisplayPort type DFP all of them are reserved, with "read all 0s" semantics (DP v2.0, section 2.12.3, Table 2-183).
The FRL bandwidth field is an HDMI DFP extension added by the VESA DP-to-HDMI PCON specification. drm_dp_get_pcon_max_frl_bw() however parses the byte without checking the DFP type, the branch presence or DETAILED_CAP_INFO_AVAILABLE. Without the latter the port descriptors are one byte wide and port_cap[2] is not even the right register.

All neighbouring helpers parsing the same descriptor are scoped by the DFP type already, see for instance drm_dp_downstream_max_bpc() reading the same byte and returning 0 for a DP type DFP. amdgpu's DC parses the field only for HDMI(/DP++) detailed types as well.

This is not theoretical.
A Synaptics VMM7100 based USB-C to HDMI adapter with a macOS targeted firmware advertises a DisplayPort type DFP with the type byte replicated across the whole descriptor (08 08 08 08).
i915 decodes that as "PCON limited to 18 Gbps FRL" and prunes every mode above ~750 MHz dotclock, including all the 4k@100/120 modes the sink EDID offers, while macOS drives 4k@120 through the same adapter just fine via DP DSC (and amdgpu's type-scoped parser would ignore the bogus field as well).

Only parse the field for an HDMI DFP behind a DPCD 1.1+ branch device that reports detailed cap info, matching the type-scoped field layout of the spec and the rest of the helpers.

Fixes ce32a6239de6 ("drm/dp_helper: Add Helpers for FRL Link Training support for DP-HDMI2.1 PCON")
Cc Ankit Nautiyal <[email protected]>
Cc Uma Shankar <[email protected]> (v2)
Cc Jani Nikula <[email protected]>
Cc Maarten Lankhorst <[email protected]>
Cc <[email protected]> # v5.12+
---
 drivers/gpu/drm/display/drm_dp_helper.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/gpu/drm/display/drm_dp_helper.c +12 -0
@@ -3651,6 +3651,18 @@ int drm_dp_get_pcon_max_frl_bw(const u8
 	int bw;
 	u8 buf;
 
+	if (!drm_dp_is_branch(dpcd))
+		return 0;
+
+	if (dpcd[DP_DPCD_REV] < 0x11)
+		return 0;
+
+	if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0)
+		return 0;
+
+	if ((port_cap[0] & DP_DS_PORT_TYPE_MASK) != DP_DS_PORT_TYPE_HDMI)
+		return 0;
+
 	buf = port_cap[2];
 	bw = buf & DP_PCON_MAX_FRL_BW;
 
author maintainer
#386 lore raw

PATCH 383/438 drm/vc4: Supply the overflow slot size in BPOS, not the whole bin BO size

In reply to Greg Kroah-Hartman

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

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

From: Jose Maria Casanova Crespo <[email protected]>

commit 6395789e4739aa5177bbec0fa0f07ccc38d249b0 upstream.

vc4_overflow_mem_work() points BPOA at a 512KB slot inside the 16MB binner BO, but writes the size of the whole BO to BPOS. On every binner out-of-memory event the PTB is therefore authorized to write tile lists across all the other slots (which may hold the tile state, tile alloc and overflow memory of in-flight jobs) and, for any slot but the first, past the end of the binner BO into unrelated CMA memory.

Since CMA pages are recycled into page cache and user allocations, this is arbitrary memory corruption by GPU DMA. In practice it shows up as GPU hangs with corrupted control list pointers, userspace heap corruption, a GPU that stays permanently wedged after the first hang, and occasional full system crashes, whenever a job overflows the initial binner slot.

The bug dates back to the conversion from a dedicated overflow BO (where writing the full BO size was correct) to the slotted binner BO.

Fixes 553c942f8b2c ("drm/vc4: Allow using more than 256MB of CMA memory.")
Assisted-by Claude:claude-opus-4.8
---
 drivers/gpu/drm/vc4/vc4_irq.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/vc4/vc4_irq.c +1 -1
@@ -105,7 +105,7 @@ vc4_overflow_mem_work(struct work_struct
 	vc4->bin_alloc_overflow = BIT(bin_bo_slot);
 
 	V3D_WRITE(V3D_BPOA, bo->base.dma_addr + bin_bo_slot * vc4->bin_alloc_size);
-	V3D_WRITE(V3D_BPOS, bo->base.base.size);
+	V3D_WRITE(V3D_BPOS, vc4->bin_alloc_size);
 	V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
 	V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
 	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
author maintainer
#387 lore raw

PATCH 384/438 drm/vc4: Zero the tile state data array before each BIN job

In reply to Greg Kroah-Hartman

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

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

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

commit 48a570c964d8e37d353381e4195106277e17f5cb upstream.

The binner BO is a single 16MB buffer split into 512KB slots that are handed out to jobs at submission time and recycled as jobs complete, without ever being cleared. Each slot holds the job's Tile State Data Array (TSDA) at its start, followed by the tile allocation pool.

While the tile allocation pool is only walked by the render thread through branches the binner generated during the current job, the TSDA is the PTB's own per-tile bookkeeping and is consumed by the hardware itself. Although the kernel sets the "Auto-initialise Tile State Data Array" flag in the tile binning mode configuration, the PTB demonstrably still acts on stale tile state left by the slot's previous user: the binner ends up creating invalid command streams with invalid primitive streams and branches, which can cause GPU hangs as observed in [1][2].

Zero the TSDA when the job's binning slot is configured. This clears 48 bytes per tile (~24KB for a 1080p frame) in the submission path, and guarantees the PTB never sees another job's tile state.

The tile count is only checked for being non-zero today, so the 8-bit fields it comes from can describe a tile state array almost six times larger than the slot it has to live in. Bound it before the slot is handed out, since such size decides how much of the slot is left for the tile alloc pool.

---
 drivers/gpu/drm/vc4/vc4_validate.c |   29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)
drivers/gpu/drm/vc4/vc4_validate.c +23 -6
@@ -386,6 +386,23 @@ validate_tile_binning_config(VALIDATE_AR
 		return -EINVAL;
 	}
 
+	/* The tile state data array is 48 bytes per tile, and we put it at
+	 * the start of a BO containing both it and the tile alloc.
+	 */
+	tile_state_size = 48 * tile_count;
+
+	/* Since the tile alloc array will follow us, align. */
+	tile_state_size = roundup(tile_state_size, 4096);
+
+	/* Reject configurations whose tile state would leave no room for
+	 * the tile alloc pool that follows it in the slot.
+	 */
+	if (tile_state_size >= vc4->bin_alloc_size) {
+		DRM_DEBUG("Tile binning config of %dx%d too large\n",
+			  exec->bin_tiles_x, exec->bin_tiles_y);
+		return -EINVAL;
+	}
+
 	bin_slot = vc4_v3d_get_bin_slot(vc4);
 	if (bin_slot < 0) {
 		if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) {
@@ -401,13 +418,13 @@ validate_tile_binning_config(VALIDATE_AR
 	exec->bin_slots |= BIT(bin_slot);
 	bin_addr = vc4->bin_bo->base.dma_addr + bin_slot * vc4->bin_alloc_size;
 
-	/* The tile state data array is 48 bytes per tile, and we put it at
-	 * the start of a BO containing both it and the tile alloc.
-	 */
-	tile_state_size = 48 * tile_count;
+	exec->tile_alloc_offset = bin_addr + tile_state_size;
 
-	/* Since the tile alloc array will follow us, align. */
-	exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096);
+	/* The TSDA area must be zeroed out before use, otherwise the PTB might
+	 * consume a stale tile state.
+	 */
+	memset(vc4->bin_bo->base.vaddr + bin_slot * vc4->bin_alloc_size, 0,
+	       tile_state_size);
 
 	*(uint8_t *)(validated + 14) =
 		((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
author maintainer
#388 lore raw

PATCH 385/438 drm/bridge: display-connector: Fix I2C adapter resource leak

In reply to Greg Kroah-Hartman

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

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

From: Laurent Pinchart <[email protected]>

commit 6a39ca1286dd97ad02bb8e03bbb65ee05368d778 upstream.

If the probe function returns an error after getting the I2C adapter for DDC, the reference to the adapter is never released. Fix it by releasing it in the bridge .destroy() handler.

There is no need to test the ddc pointer with !IS_ERR(), as of_get_i2c_adapter_by_node() returns NULL on error.

---
 drivers/gpu/drm/bridge/display-connector.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/gpu/drm/bridge/display-connector.c +8 -3
@@ -40,6 +40,13 @@ static int display_connector_attach(stru
 	return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
 }
 
+static void display_connector_destroy(struct drm_bridge *bridge)
+{
+	struct display_connector *conn = to_display_connector(bridge);
+
+	i2c_put_adapter(conn->bridge.ddc);
+}
+
 static enum drm_connector_status display_connector_detect(struct drm_bridge *bridge)
 {
 	struct display_connector *conn = to_display_connector(bridge);
@@ -177,6 +184,7 @@ static u32 *display_connector_get_input_
 
 static const struct drm_bridge_funcs display_connector_bridge_funcs = {
 	.attach = display_connector_attach,
+	.destroy = display_connector_destroy,
 	.detect = display_connector_bridge_detect,
 	.edid_read = display_connector_edid_read,
 	.atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts,
@@ -403,9 +411,6 @@ static void display_connector_remove(str
 		regulator_disable(conn->supply);
 
 	drm_bridge_remove(&conn->bridge);
-
-	if (!IS_ERR(conn->bridge.ddc))
-		i2c_put_adapter(conn->bridge.ddc);
 }
 
 static const struct of_device_id display_connector_match[] = {
author maintainer
#389 lore raw

PATCH 386/438 drm/panthor: reject firmware sections with oversized data

In reply to Greg Kroah-Hartman

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

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

From: Osama Abdelkader <[email protected]>

commit a3caaa06809248b996254be5b47e10804a3494e2 upstream.

In panthor_fw_load_section_entry(), the data size to copy is calculated without validating it against the allocated section_size:

    section->data.size = hdr.data.end - hdr.data.start;

If a crafted firmware sets data.size larger than the allocated memory, this could cause a heap buffer overflow in panthor_fw_init_section_mem()

    memcpy(section->mem->kmap, section->data.buf, section->data.size);

Additionally, if the section->data.size exceeds the BO size, could this memset underflow the size calculation, leading to a massive out-of-bounds zeroing of kernel memory?

    memset(section->mem->kmap + section->data.size, 0,
           panthor_kernel_bo_size(section->mem) - section->data.size);

Reject section entries whose initial data is larger than the section size.

---
 drivers/gpu/drm/panthor/panthor_fw.c |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/gpu/drm/panthor/panthor_fw.c +9 -2
@@ -542,6 +542,7 @@ static int panthor_fw_load_section_entry
 	struct panthor_fw_binary_section_entry_hdr hdr;
 	struct panthor_fw_section *section;
 	u32 section_size;
+	u32 data_size;
 	u32 name_len;
 	int ret;
 
@@ -592,6 +593,13 @@ static int panthor_fw_load_section_entry
 		return -EINVAL;
 	}
 
+	section_size = hdr.va.end - hdr.va.start;
+	data_size = hdr.data.end - hdr.data.start;
+	if (data_size > section_size) {
+		drm_err(&ptdev->base, "Firmware corrupted, section data exceeds section size\n");
+		return -EINVAL;
+	}
+
 	name_len = iter->size - iter->offset;
 
 	section = drmm_kzalloc(&ptdev->base, sizeof(*section), GFP_KERNEL);
@@ -600,7 +608,7 @@ static int panthor_fw_load_section_entry
 
 	list_add_tail(&section->node, &ptdev->fw->sections);
 	section->flags = hdr.flags;
-	section->data.size = hdr.data.end - hdr.data.start;
+	section->data.size = data_size;
 
 	if (section->data.size > 0) {
 		void *data = drmm_kmalloc(&ptdev->base, section->data.size, GFP_KERNEL);
@@ -623,7 +631,6 @@ static int panthor_fw_load_section_entry
 		section->name = name;
 	}
 
-	section_size = hdr.va.end - hdr.va.start;
 	if (section_size) {
 		u32 cache_mode = hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK;
 		struct panthor_gem_object *bo;
author maintainer
#390 lore raw

PATCH 387/438 drm/panthor: validate firmware interface structure sizes

In reply to Greg Kroah-Hartman

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

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

From: Osama Abdelkader <[email protected]>

commit b921b8613790a3f9e78ab64017fa7149ef0b750c upstream.

iface_fw_to_cpu_addr() only checks that the firmware-provided MCU virtual address points inside the shared section. The returned pointer is later used as a full firmware interface structure, so accepting an address near the end of the shared section can still lead to out-of-bounds accesses.

Pass the expected object size to iface_fw_to_cpu_addr() and reject ranges that do not fit entirely in the shared section.

---
 drivers/gpu/drm/panthor/panthor_fw.c |   36 +++++++++++++++++++++++------------
 1 file changed, 24 insertions(+), 12 deletions(-)
drivers/gpu/drm/panthor/panthor_fw.c +24 -12
@@ -859,18 +859,24 @@ out:
  * iface_fw_to_cpu_addr() - Turn an MCU address into a CPU address
  * @ptdev: Device.
  * @mcu_va: MCU address.
+ * @size: Size of the object pointed to by @mcu_va.
  *
- * Return: NULL if the address is not part of the shared section, non-NULL otherwise.
+ * Return: NULL if the object is not part of the shared section, non-NULL otherwise.
  */
-static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va)
+static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va, size_t size)
 {
 	u64 shared_mem_start = panthor_kernel_bo_gpuva(ptdev->fw->shared_section->mem);
-	u64 shared_mem_end = shared_mem_start +
-			     panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
-	if (mcu_va < shared_mem_start || mcu_va >= shared_mem_end)
+	size_t shared_mem_size = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
+	u64 offset;
+
+	if (mcu_va < shared_mem_start)
+		return NULL;
+
+	offset = mcu_va - shared_mem_start;
+	if (offset > shared_mem_size || size > shared_mem_size - offset)
 		return NULL;
 
-	return ptdev->fw->shared_section->mem->kmap + (mcu_va - shared_mem_start);
+	return ptdev->fw->shared_section->mem->kmap + offset;
 }
 
 static int panthor_init_cs_iface(struct panthor_device *ptdev,
@@ -892,8 +898,10 @@ static int panthor_init_cs_iface(struct
 
 	spin_lock_init(&cs_iface->lock);
 	cs_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset;
-	cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va);
-	cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va);
+	cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va,
+					       sizeof(*cs_iface->input));
+	cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va,
+						sizeof(*cs_iface->output));
 
 	if (!cs_iface->input || !cs_iface->output) {
 		drm_err(&ptdev->base, "Invalid stream control interface input/output VA");
@@ -943,8 +951,10 @@ static int panthor_init_csg_iface(struct
 
 	spin_lock_init(&csg_iface->lock);
 	csg_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset;
-	csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va);
-	csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va);
+	csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va,
+						sizeof(*csg_iface->input));
+	csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va,
+						 sizeof(*csg_iface->output));
 
 	if (csg_iface->control->stream_num < MIN_CS_PER_CSG ||
 	    csg_iface->control->stream_num > MAX_CS_PER_CSG)
@@ -1001,8 +1011,10 @@ static int panthor_fw_init_ifaces(struct
 		return -EINVAL;
 	}
 
-	glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va);
-	glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va);
+	glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va,
+						sizeof(*glb_iface->input));
+	glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va,
+						 sizeof(*glb_iface->output));
 	if (!glb_iface->input || !glb_iface->output) {
 		drm_err(&ptdev->base, "Invalid global control interface input/output VA");
 		return -EINVAL;
author maintainer
#391 lore raw

PATCH 388/438 drm/mediatek: mtk_hdmi: Fix DDC adapter double put in v2

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit d19d8542808c3be5f4af849da6509e877b74d811 upstream.

mtk_hdmi_common_probe() gets the DDC adapter with of_find_i2c_adapter_by_node() and registers a devm action to release the adapter device reference with put_device().

The HDMI v2 remove callback also calls i2c_put_adapter() on the same DDC adapter. This is not paired with of_find_i2c_adapter_by_node(): it drops the adapter device reference before the devm action drops it again, and it also puts a module reference that was never taken.

Remove the extra i2c_put_adapter() call and drop the now-empty HDMI v2 remove callback. The common devm action releases the adapter device reference.

---
 drivers/gpu/drm/mediatek/mtk_hdmi_v2.c |    8 --------
 1 file changed, 8 deletions(-)
drivers/gpu/drm/mediatek/mtk_hdmi_v2.c +0 -8
@@ -1499,13 +1499,6 @@ static int mtk_hdmi_v2_probe(struct plat
 	return 0;
 }
 
-static void mtk_hdmi_v2_remove(struct platform_device *pdev)
-{
-	struct mtk_hdmi *hdmi = platform_get_drvdata(pdev);
-
-	i2c_put_adapter(hdmi->ddc_adpt);
-}
-
 static const struct of_device_id mtk_drm_hdmi_v2_of_ids[] = {
 	{ .compatible = "mediatek,mt8188-hdmi-tx", .data = &mtk_hdmi_conf_mt8188 },
 	{ .compatible = "mediatek,mt8195-hdmi-tx", .data = &mtk_hdmi_conf_mt8195 },
@@ -1515,7 +1508,6 @@ MODULE_DEVICE_TABLE(of, mtk_drm_hdmi_v2_
 
 static struct platform_driver mtk_hdmi_v2_driver = {
 	.probe = mtk_hdmi_v2_probe,
-	.remove = mtk_hdmi_v2_remove,
 	.driver = {
 		.name = "mediatek-drm-hdmi-v2",
 		.of_match_table = mtk_drm_hdmi_v2_of_ids,
author maintainer
#392 lore raw

PATCH 389/438 drm/mediatek: ovl_adaptor: balance component registrations

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 533e3469a57996905cdb95f178e7efe38c21aeb2 upstream.

The OVL adaptor registers both an aggregate driver for its child devices and a component for the main DRM aggregate. Probe currently ignores an error from registering the child aggregate and leaves that aggregate registered if registering the DRM component fails. The remove callback also leaves the DRM component registered.

These imbalances can leave component framework entries referring to a device whose probe failed or whose driver has been detached. The aggregate unbind callback also fails to undo component_bind_all(), leaving its child components marked as bound when the aggregate is removed.

Check the aggregate registration result, unwind it when the component registration fails, and unregister the component before the aggregate on remove. Keep runtime PM enabled until both framework registrations have been removed, and unbind all child components from the aggregate unbind callback.

---
 drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c +6 -1
@@ -625,6 +625,7 @@ static void mtk_disp_ovl_adaptor_master_
 	struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev);
 
 	priv->children_bound = false;
+	component_unbind_all(dev, priv->mmsys_dev);
 }
 
 static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = {
@@ -651,12 +652,15 @@ static int mtk_disp_ovl_adaptor_probe(st
 
 	priv->mmsys_dev = pdev->dev.platform_data;
 
-	component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match);
+	ret = component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add component master\n");
 
 	pm_runtime_enable(dev);
 
 	ret = component_add(dev, &mtk_disp_ovl_adaptor_comp_ops);
 	if (ret != 0) {
+		component_master_del(dev, &mtk_disp_ovl_adaptor_master_ops);
 		pm_runtime_disable(dev);
 		return dev_err_probe(dev, ret, "Failed to add component\n");
 	}
@@ -666,6 +670,7 @@ static int mtk_disp_ovl_adaptor_probe(st
 
 static void mtk_disp_ovl_adaptor_remove(struct platform_device *pdev)
 {
+	component_del(&pdev->dev, &mtk_disp_ovl_adaptor_comp_ops);
 	component_master_del(&pdev->dev, &mtk_disp_ovl_adaptor_master_ops);
 	pm_runtime_disable(&pdev->dev);
 }
author maintainer
#393 lore raw

PATCH 390/438 drm/amdgpu: move debug_vm handling to amdgpu_cs_parser_fini

In reply to Greg Kroah-Hartman

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

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

From: Pierre-Eric Pelloux-Prayer <[email protected]>

commit d8726ef11512754a68c0ab53c57634a569b8feff upstream.

The commit referenced below restarts the CS if the validation is still in progress. When debug_vm is enabled, all BOs from the CS are invalidated so we will hit an infinite loop.

To avoid that, defer BO invalidation to amdgpu_cs_parser_fini.

Fixes 59720bfd8c6d ("drm/amdgpu: restart the CS if some parts of the VM are still invalidated")
(cherry picked from commit 8c990ee9daa295462df24982ce6878db997a380a)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c |   30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +16 -14
@@ -1182,19 +1182,6 @@ static int amdgpu_cs_vm_handling(struct
 		job->vm_pd_addr = amdgpu_gmc_pd_addr(vm->root.bo);
 	}
 
-	if (adev->debug_vm) {
-		/* Invalidate all BOs to test for userspace bugs */
-		amdgpu_bo_list_for_each_entry(e, p->bo_list) {
-			struct amdgpu_bo *bo = e->bo;
-
-			/* ignore duplicates */
-			if (!bo)
-				continue;
-
-			amdgpu_vm_bo_invalidate(bo, false);
-		}
-	}
-
 	return 0;
 }
 
@@ -1384,6 +1371,8 @@ static int amdgpu_cs_submit(struct amdgp
 /* Cleanup the parser structure */
 static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser)
 {
+	struct amdgpu_device *adev = parser->adev;
+	struct amdgpu_bo_list_entry *e;
 	unsigned int i;
 
 	amdgpu_sync_free(&parser->sync);
@@ -1399,8 +1388,21 @@ static void amdgpu_cs_parser_fini(struct
 
 	if (parser->ctx)
 		amdgpu_ctx_put(parser->ctx);
-	if (parser->bo_list)
+	if (parser->bo_list) {
+		if (adev->debug_vm) {
+			/* Invalidate all BOs to test for userspace bugs */
+			amdgpu_bo_list_for_each_entry(e, parser->bo_list) {
+				struct amdgpu_bo *bo = e->bo;
+
+				/* ignore duplicates */
+				if (!bo)
+					continue;
+
+				amdgpu_vm_bo_invalidate(bo, false);
+			}
+		}
 		amdgpu_bo_list_put(parser->bo_list);
+	}
 
 	for (i = 0; i < parser->nchunks; i++)
 		kvfree(parser->chunks[i].kdata);
author maintainer
#394 lore raw

PATCH 391/438 drm/amdgpu: restore UMD profile pstate after runtime resume

In reply to Greg Kroah-Hartman

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

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

From: Candice Li <[email protected]>

commit f931c54b241ce2f36bfc34955aec43a188276b8d upstream.

Runtime suspend runs GFX hw_fini and clears perfmon clock gating while the UMD profile DPM level remains set in software. Re-apply stable pstate after a successful runtime resume when a profile mode is active.

(cherry picked from commit 138531c8850cc247aa12b104bb29ea387bcdcbb1)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c |   15 +++++++++++++++
 1 file changed, 15 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +15 -0
@@ -2876,6 +2876,19 @@ static int amdgpu_pmops_runtime_suspend(
 	return 0;
 }
 
+static void amdgpu_restore_umd_profile_pstate_after_runpm(struct amdgpu_device *adev)
+{
+	enum amd_dpm_forced_level level;
+	uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD |
+		AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK |
+		AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK |
+		AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
+
+	level = amdgpu_dpm_get_performance_level(adev);
+	if (level & profile_mode_mask)
+		amdgpu_asic_update_umd_stable_pstate(adev, true);
+}
+
 static int amdgpu_pmops_runtime_resume(struct device *dev)
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
@@ -2920,6 +2933,8 @@ static int amdgpu_pmops_runtime_resume(s
 
 	if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX)
 		drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
+
+	amdgpu_restore_umd_profile_pstate_after_runpm(adev);
 	adev->in_runpm = false;
 	return 0;
 }
author maintainer
#395 lore raw

PATCH 392/438 drm/amdgpu: cap GTT size to physical RAM on APUs

In reply to Greg Kroah-Hartman

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

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

From: Harkirat Gill <[email protected]>

commit 5e70f6804b4d6256058c360b10e044ee04ea4a4e upstream.

On APUs, the GTT pool is backed by system RAM, but its size is not bound to the non-carveout memory that actually backs it. A user can end up with GTT + VRAM exceeding total physical memory through the following sequence:

- Have a large non-carveout memory space (~128GB) and accordingly set a large GTT (~100GB) via the ttm module parameter.
- Lower the non-carveout memory space in BIOS by increasing the UMA Frame Buffer Size (VRAM) to 64GB.
- The previously set GTT value (~100GB) persists, even though the new non-carveout space (64GB) can no longer back it.

This leads to a case where kernel reports GTT (100GB) + VRAM (64GB) despite the sum being greater than total physical memory (128GB).

Cap the GTT size to totalram_pages() on APUs. totalram_pages() already excludes the VRAM carveout, so the resulting GTT can never exceed the system RAM that actually backs it.

Assisted-by Claude:claude-opus-4
(cherry picked from commit 5dafdd649280c7dc6c22c8f877da3f54fcc441e1)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +12 -0
@@ -2174,6 +2174,18 @@ int amdgpu_ttm_init(struct amdgpu_device
 		gtt_size = configured_size;
 	}
 
+	/* Cap GTT so that it does not exceed total physical RAM. */
+	if (adev->flags & AMD_IS_APU) {
+		u64 phys_ram = (u64)totalram_pages() << PAGE_SHIFT;
+
+		if (gtt_size > phys_ram) {
+			gtt_size = phys_ram;
+			dev_info(adev->dev,
+				 "Capping GTT to %uM to not exceed available system memory\n",
+				 (unsigned int)(gtt_size / (1024 * 1024)));
+		}
+	}
+
 	/* Initialize GTT memory pool */
 	r = amdgpu_gtt_mgr_init(adev, gtt_size);
 	if (r) {
author maintainer
#396 lore raw

PATCH 393/438 drm/amd/pm: fix torn gpu metrics reads

In reply to Greg Kroah-Hartman

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

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

From: Yang Wang <[email protected]>

commit 048f4541b71fb19645fb79d6e62e6e4da23a4035 upstream.

amdgpu_dpm_get_gpu_metrics() returns a pointer to the shared metrics cache after dropping adev->pm.mutex. The sysfs path then copies from that pointer. Another reader can refresh the cache in place during the copy and return a snapshot containing data from two generations.

Pass caller-provided storage through the DPM interface and copy the metrics while the mutex is held. This keeps the cache pointer private and makes each sysfs read observe one complete sample.

Fixes 25c933b1c4fc ("drm/amd/powerplay: add new sysfs interface for retrieving gpu metrics(V2)")
(cherry picked from commit 862333bb48693ecafcae25af0c9d9ec31015ac77)
---
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c     |   12 +++++++++---
 drivers/gpu/drm/amd/pm/amdgpu_pm.c      |    8 +-------
 drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h |    3 ++-
 3 files changed, 12 insertions(+), 11 deletions(-)
drivers/gpu/drm/amd/pm/amdgpu_dpm.c +9 -3
@@ -1426,17 +1426,23 @@ int amdgpu_dpm_set_power_profile_mode(st
 	return ret;
 }
 
-int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table)
+ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf,
+				   size_t size)
 {
 	const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
-	int ret = 0;
+	void *table;
+	ssize_t ret;
 
 	if (!pp_funcs->get_gpu_metrics)
 		return 0;
 
 	mutex_lock(&adev->pm.mutex);
 	ret = pp_funcs->get_gpu_metrics(adev->powerplay.pp_handle,
-					table);
+					&table);
+	if (ret > 0) {
+		ret = min_t(ssize_t, ret, size);
+		memcpy(buf, table, ret);
+	}
 	mutex_unlock(&adev->pm.mutex);
 
 	return ret;
drivers/gpu/drm/amd/pm/amdgpu_pm.c +1 -7
@@ -1773,7 +1773,6 @@ static ssize_t amdgpu_get_gpu_metrics(st
 {
 	struct drm_device *ddev = dev_get_drvdata(dev);
 	struct amdgpu_device *adev = drm_to_adev(ddev);
-	void *gpu_metrics;
 	ssize_t size = 0;
 	int ret;
 
@@ -1781,15 +1780,10 @@ static ssize_t amdgpu_get_gpu_metrics(st
 	if (ret)
 		return ret;
 
-	size = amdgpu_dpm_get_gpu_metrics(adev, &gpu_metrics);
+	size = amdgpu_dpm_get_gpu_metrics(adev, buf, PAGE_SIZE - 1);
 	if (size <= 0)
 		goto out;
 
-	if (size >= PAGE_SIZE)
-		size = PAGE_SIZE - 1;
-
-	memcpy(buf, gpu_metrics, size);
-
 out:
 	amdgpu_pm_put_access(adev);
 
drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +2 -1
@@ -517,7 +517,8 @@ int amdgpu_dpm_get_power_profile_mode(st
 				      char *buf);
 int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev,
 				      long *input, uint32_t size);
-int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table);
+ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf,
+				   size_t size);
 ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id,
 				   void *table);
 ssize_t amdgpu_dpm_get_temp_metrics(struct amdgpu_device *adev,
author maintainer
#397 lore raw

PATCH 394/438 drm/amd/pm: fix pptable use-after-free

In reply to Greg Kroah-Hartman

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

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

From: Yang Wang <[email protected]>

commit bb493058c35c8676e48269ab6732688ea733d23c upstream.

amdgpu_dpm_get_pp_table() returns a pointer to a driver-owned power table after dropping adev->pm.mutex. The sysfs path then copies from that pointer. A concurrent pp_table write can replace and free the allocation during the copy, causing a use-after-free.

Change the DPM interface to copy into caller-provided storage while the mutex is held. Keep the size-only query for attribute discovery without exposing the driver-owned pointer.

Fixes 1684d3ba4885 ("drm/amd/amdgpu: change pptable output format from ASCII to binary")
(cherry picked from commit f6eed7acfd30099ef7baeb6ba45bb59daad80631)
---
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c     |   14 +++++++++++---
 drivers/gpu/drm/amd/pm/amdgpu_pm.c      |   13 +++----------
 drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h |    3 ++-
 3 files changed, 16 insertions(+), 14 deletions(-)
drivers/gpu/drm/amd/pm/amdgpu_dpm.c +11 -3
@@ -1183,12 +1183,14 @@ int amdgpu_dpm_dispatch_task(struct amdg
 	return ret;
 }
 
-int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table)
+int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table,
+			    size_t size)
 {
 	const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+	char *pptable = NULL;
 	int ret = 0;
 
-	if (!table)
+	if ((!table && size) || (table && !size))
 		return -EINVAL;
 
 	if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled)
@@ -1196,7 +1198,13 @@ int amdgpu_dpm_get_pp_table(struct amdgp
 
 	mutex_lock(&adev->pm.mutex);
 	ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle,
-				     table);
+				     &pptable);
+	if (ret > 0 && !pptable) {
+		ret = -EINVAL;
+	} else if (ret > 0 && table) {
+		ret = min_t(size_t, ret, size);
+		memcpy(table, pptable, ret);
+	}
 	mutex_unlock(&adev->pm.mutex);
 
 	return ret;
drivers/gpu/drm/amd/pm/amdgpu_pm.c +3 -10
@@ -564,25 +564,19 @@ static ssize_t amdgpu_get_pp_table(struc
 {
 	struct drm_device *ddev = dev_get_drvdata(dev);
 	struct amdgpu_device *adev = drm_to_adev(ddev);
-	char *table = NULL;
 	int size, ret;
 
 	ret = amdgpu_pm_get_access_if_active(adev);
 	if (ret)
 		return ret;
 
-	size = amdgpu_dpm_get_pp_table(adev, &table);
+	size = amdgpu_dpm_get_pp_table(adev, buf, PAGE_SIZE - 1);
 
 	amdgpu_pm_put_access(adev);
 
 	if (size <= 0)
 		return size;
 
-	if (size >= PAGE_SIZE)
-		size = PAGE_SIZE - 1;
-
-	memcpy(buf, table, size);
-
 	return size;
 }
 
@@ -2704,10 +2698,9 @@ static int default_attr_update(struct am
 			*states = ATTR_STATE_UNSUPPORTED;
 	} else if (DEVICE_ATTR_IS(pp_table)) {
 		int ret;
-		char *tmp = NULL;
 
-		ret = amdgpu_dpm_get_pp_table(adev, &tmp);
-		if (ret == -EOPNOTSUPP || !tmp)
+		ret = amdgpu_dpm_get_pp_table(adev, NULL, 0);
+		if (ret <= 0)
 			*states = ATTR_STATE_UNSUPPORTED;
 		else
 			*states = ATTR_STATE_SUPPORTED;
drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +2 -1
@@ -487,7 +487,8 @@ int amdgpu_dpm_get_pp_num_states(struct
 int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev,
 			      enum amd_pp_task task_id,
 			      enum amd_pm_state_type *user_state);
-int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table);
+int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table,
+			    size_t size);
 int amdgpu_dpm_set_fine_grain_clk_vol(struct amdgpu_device *adev,
 				      uint32_t type,
 				      long *input,
author maintainer
#398 lore raw

PATCH 395/438 drm/amd/pm: hide pp_table sysfs on APUs

In reply to Greg Kroah-Hartman

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

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

From: Yang Wang <[email protected]>

commit a65f5179d3f0c93da31b450aeb416eaa22f1912b upstream.

APUs use firmware-owned DPM tables and do not support replacement through pp_table. Generic callbacks can nevertheless expose the sysfs file and accept an upload before resetting the power management stack.

Treat pp_table as unsupported on APUs. Use the same platform check in the get and set paths to hide the file and reject uploads.

Fixes 289921b03fe5 ("drm/amd/powerplay: implement sysfs of pp_table for smu11 (v2)")
(cherry picked from commit 74f28db2db69777cd2f059d50fe34e365ddd5add)
---
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/pm/amdgpu_dpm.c +11 -2
@@ -1183,6 +1183,13 @@ int amdgpu_dpm_dispatch_task(struct amdg
 	return ret;
 }
 
+static bool amdgpu_dpm_is_pp_table_allowed(struct amdgpu_device *adev)
+{
+	return !amdgpu_sriov_vf(adev) &&
+	       !(adev->flags & AMD_IS_APU) &&
+	       !adev->scpm_enabled;
+}
+
 int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table,
 			    size_t size)
 {
@@ -1193,7 +1200,8 @@ int amdgpu_dpm_get_pp_table(struct amdgp
 	if ((!table && size) || (table && !size))
 		return -EINVAL;
 
-	if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled)
+	if (!amdgpu_dpm_is_pp_table_allowed(adev) ||
+	    !pp_funcs->get_pp_table)
 		return -EOPNOTSUPP;
 
 	mutex_lock(&adev->pm.mutex);
@@ -1717,7 +1725,8 @@ int amdgpu_dpm_set_pp_table(struct amdgp
 	if (!buf || !size)
 		return -EINVAL;
 
-	if (amdgpu_sriov_vf(adev) || !pp_funcs->set_pp_table || adev->scpm_enabled)
+	if (!amdgpu_dpm_is_pp_table_allowed(adev) ||
+	    !pp_funcs->set_pp_table)
 		return -EOPNOTSUPP;
 
 	mutex_lock(&adev->pm.mutex);
author maintainer
#399 lore raw

PATCH 396/438 drm/amd/pm: use milliwatts for GPU power sensors

In reply to Greg Kroah-Hartman

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

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

From: Yang Wang <[email protected]>

commit 1849a64165ccc23d3e5fc22b8be19227c21c1871 upstream.

GPU average and input power backends report a mix of whole watts, milliwatts, Q24.8 watts and decimal-packed fractions. Q24.8 is inherited from the legacy PowerPlay sensor format. Milliwatts are a more natural unit for the hwmon and pm_info consumers in amdgpu_pm.c. A common decoder cannot distinguish these formats, and converting native milliwatts through Q24.8 also loses precision.

Use milliwatts as the internal unit across all PPT and PowerPlay backends. Decode Q24.8 only at the legacy smu7 input boundary and encode it only for the raw amdgpu_sensors debugfs interface. This gives hwmon, pm_info and the sensor ioctl one unambiguous unit while preserving the format used by UMR.

Fixes 5b79d0482f3c ("drm/amd/pp: Remove struct pp_gpu_power")
Fixes 01992b121fb6 ("drm/amd/pm: fix amdgpu_pm_info power display units")
(cherry picked from commit 757ba0790bafec47a507e9662bf380f2e027d420)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c             |    9 +++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c                 |    4 ++--
 drivers/gpu/drm/amd/include/kgd_pp_interface.h          |    6 ++++--
 drivers/gpu/drm/amd/pm/amdgpu_pm.c                      |    8 +++-----
 drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c     |    4 ++--
 drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c   |    4 ++--
 drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c   |    2 +-
 drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c   |    5 +++--
 drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h            |    5 +++++
 drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c       |    2 +-
 drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c |    6 ++----
 drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c         |    8 ++++----
 drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c |    7 ++++---
 drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c        |    9 +++------
 drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c         |    5 +++--
 drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c      |    3 ++-
 drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c    |    2 +-
 drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c   |    3 ++-
 drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c    |    4 ++--
 drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c    |    2 +-
 drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c    |    3 ++-
 drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c    |    2 +-
 drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c    |    2 +-
 drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c    |    3 +--
 drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c    |    2 +-
 drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c    |    3 +--
 drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c    |    3 ++-
 27 files changed, 65 insertions(+), 51 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +9 -0
@@ -43,6 +43,11 @@
 
 #if defined(CONFIG_DEBUG_FS)
 
+/* Encode milliwatts in the raw Q24.8 sensor report format used by UMR. */
+#define AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(power_mw) \
+	DIV_ROUND_CLOSEST_ULL((u64)(power_mw) * BIT(8), \
+			      MILLIWATT_PER_WATT)
+
 /**
  * amdgpu_debugfs_process_reg_op - Handle MMIO register reads/writes
  *
@@ -1104,6 +1109,10 @@ static ssize_t amdgpu_debugfs_sensor_rea
 		return r;
 	}
 
+	if (idx == AMDGPU_PP_SENSOR_GPU_AVG_POWER ||
+	    idx == AMDGPU_PP_SENSOR_GPU_INPUT_POWER)
+		values[0] = AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(values[0]);
+
 	if (size > valuesize) {
 		amdgpu_virt_disable_access_debugfs(adev);
 		return -EINVAL;
drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +2 -2
@@ -1210,7 +1210,7 @@ int amdgpu_info_ioctl(struct drm_device
 					return -EINVAL;
 				}
 			}
-			ui32 >>= 8;
+			ui32 /= MILLIWATT_PER_WATT;
 			break;
 		case AMDGPU_INFO_SENSOR_GPU_INPUT_POWER:
 			/* get input GPU power */
@@ -1219,7 +1219,7 @@ int amdgpu_info_ioctl(struct drm_device
 						   (void *)&ui32, &ui32_size)) {
 				return -EINVAL;
 			}
-			ui32 >>= 8;
+			ui32 /= MILLIWATT_PER_WATT;
 			break;
 		case AMDGPU_INFO_SENSOR_VDDNB:
 			/* get VDDNB in millivolts */
drivers/gpu/drm/amd/include/kgd_pp_interface.h +4 -2
@@ -24,6 +24,8 @@
 #ifndef __KGD_PP_INTERFACE_H__
 #define __KGD_PP_INTERFACE_H__
 
+#include <linux/units.h>
+
 extern const struct amdgpu_ip_block_version pp_smu_ip_block;
 extern const struct amdgpu_ip_block_version smu_v11_0_ip_block;
 extern const struct amdgpu_ip_block_version smu_v12_0_ip_block;
@@ -150,8 +152,8 @@ enum amd_pp_sensors {
 	AMDGPU_PP_SENSOR_MEM_TEMP,
 	AMDGPU_PP_SENSOR_VCE_POWER,
 	AMDGPU_PP_SENSOR_UVD_POWER,
-	AMDGPU_PP_SENSOR_GPU_AVG_POWER,
-	AMDGPU_PP_SENSOR_GPU_INPUT_POWER,
+	AMDGPU_PP_SENSOR_GPU_AVG_POWER, /* milliwatts */
+	AMDGPU_PP_SENSOR_GPU_INPUT_POWER, /* milliwatts */
 	AMDGPU_PP_SENSOR_SS_APU_SHARE,
 	AMDGPU_PP_SENSOR_SS_DGPU_SHARE,
 	AMDGPU_PP_SENSOR_STABLE_PSTATE_SCLK,
drivers/gpu/drm/amd/pm/amdgpu_pm.c +3 -5
@@ -41,8 +41,6 @@
 
 #define DEVICE_ATTR_IS(_name)		(attr_id == device_attr_id__##_name)
 
-#define power_2_mwatt(power)	(((power) >> 8) * 1000 + ((power) & 0xff))
-
 struct od_attribute {
 	struct kobj_attribute	attribute;
 	struct list_head	entry;
@@ -3335,7 +3333,7 @@ static int amdgpu_hwmon_get_power(struct
 		return r;
 
 	/* convert to microwatts */
-	return power_2_mwatt(query) * 1000;
+	return query * 1000;
 }
 
 static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev,
@@ -4898,7 +4896,7 @@ static int amdgpu_debugfs_pm_info_pp(str
 		seq_printf(m, "\t%u mV (VDDNB)\n", value);
 	size = sizeof(uint32_t);
 	if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) {
-		mwatt = power_2_mwatt(query);
+		mwatt = query;
 		centiwatt = DIV_ROUND_CLOSEST(mwatt, 10);
 		if (adev->flags & AMD_IS_APU)
 			seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100);
@@ -4907,7 +4905,7 @@ static int amdgpu_debugfs_pm_info_pp(str
 	}
 	size = sizeof(uint32_t);
 	if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) {
-		mwatt = power_2_mwatt(query);
+		mwatt = query;
 		centiwatt = DIV_ROUND_CLOSEST(mwatt, 10);
 		if (adev->flags & AMD_IS_APU)
 			seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100);
drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +2 -2
@@ -4062,7 +4062,7 @@ static int smu7_get_gpu_power(struct pp_
 	    (adev->asic_type != CHIP_FIJI) &&
 	    (adev->asic_type != CHIP_TONGA)) {
 		smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetCurrPkgPwr, 0, &tmp);
-		*query = tmp;
+		*query = PP_PWR_Q24_8_TO_MW(tmp);
 
 		if (tmp != 0)
 			return 0;
@@ -4081,7 +4081,7 @@ static int smu7_get_gpu_power(struct pp_
 		if (tmp != 0)
 			break;
 	}
-	*query = tmp;
+	*query = PP_PWR_Q24_8_TO_MW(tmp);
 
 	return 0;
 }
drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c +2 -2
@@ -3937,8 +3937,8 @@ static int vega10_get_gpu_power(struct p
 	if (ret)
 		return ret;
 
-	/* SMC returning actual watts, keep consistent with legacy asics, low 8 bit as 8 fractional bits */
-	*query = value << 8;
+	/* SMC returns whole Watts, while power sensors use milliwatts. */
+	*query = value * MILLIWATT_PER_WATT;
 
 	return 0;
 }
drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c +1 -1
@@ -1419,7 +1419,7 @@ static int vega12_get_gpu_power(struct p
 	if (ret)
 		return ret;
 
-	*query = metrics_table.CurrSocketPower << 8;
+	*query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT;
 
 	return ret;
 }
drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c +3 -2
@@ -2154,12 +2154,13 @@ static int vega20_get_gpu_power(struct p
 	switch (idx) {
 	case AMDGPU_PP_SENSOR_GPU_AVG_POWER:
 		if (hwmgr->smu_version == 0x282e00)
-			*query = metrics_table.AverageSocketPower << 8;
+			*query = metrics_table.AverageSocketPower *
+				 MILLIWATT_PER_WATT;
 		else
 			ret = -EOPNOTSUPP;
 		break;
 	case AMDGPU_PP_SENSOR_GPU_INPUT_POWER:
-		*query = metrics_table.CurrSocketPower << 8;
+		*query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT;
 		break;
 	}
 
drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h +5 -0
@@ -35,6 +35,11 @@ struct pp_hwmgr;
 struct phm_fan_speed_info;
 struct pp_atomctrl_voltage_table;
 
+/* Decode legacy unsigned Q24.8 watts to internal milliwatts. */
+#define PP_PWR_Q24_8_TO_MW(power) \
+	DIV_ROUND_CLOSEST_ULL((u64)(power) * MILLIWATT_PER_WATT, \
+			      BIT(8))
+
 #define VOLTAGE_SCALE 4
 #define VOLTAGE_VID_OFFSET_SCALE1   625
 #define VOLTAGE_VID_OFFSET_SCALE2   100
drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c +1 -1
@@ -658,7 +658,7 @@ static int arcturus_get_smu_metrics_data
 		*value = metrics->VcnActivityPercentage;
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = metrics->AverageSocketPower << 8;
+		*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->TemperatureEdge *
drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c +2 -4
@@ -158,12 +158,10 @@ cyan_skillfish_get_smu_metrics_data(stru
 		*value = metrics->Current.MemclkFrequency;
 		break;
 	case METRICS_CURR_SOCKETPOWER:
-		*value = (metrics->Current.CurrentSocketPower << 8) /
-				1000;
+		*value = metrics->Current.CurrentSocketPower;
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = (metrics->Average.CurrentSocketPower << 8) /
-				1000;
+		*value = metrics->Average.CurrentSocketPower;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->Current.GfxTemperature / 100 *
drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +4 -4
@@ -602,7 +602,7 @@ static int navi10_get_legacy_smu_metrics
 		*value = metrics->AverageUclkActivity;
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = metrics->AverageSocketPower << 8;
+		*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->TemperatureEdge *
@@ -691,7 +691,7 @@ static int navi10_get_smu_metrics_data(s
 		*value = metrics->AverageUclkActivity;
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = metrics->AverageSocketPower << 8;
+		*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->TemperatureEdge *
@@ -777,7 +777,7 @@ static int navi12_get_legacy_smu_metrics
 		*value = metrics->AverageUclkActivity;
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = metrics->AverageSocketPower << 8;
+		*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->TemperatureEdge *
@@ -866,7 +866,7 @@ static int navi12_get_smu_metrics_data(s
 		*value = metrics->AverageUclkActivity;
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = metrics->AverageSocketPower << 8;
+		*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->TemperatureEdge *
drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +4 -3
@@ -855,9 +855,10 @@ static int sienna_cichlid_get_smu_metric
 			metrics->AverageUclkActivity;
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = use_metrics_v3 ? metrics_v3->AverageSocketPower << 8 :
-			use_metrics_v2 ? metrics_v2->AverageSocketPower << 8 :
-			metrics->AverageSocketPower << 8;
+		*value = use_metrics_v3 ? metrics_v3->AverageSocketPower :
+			use_metrics_v2 ? metrics_v2->AverageSocketPower :
+			metrics->AverageSocketPower;
+		*value *= MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = (use_metrics_v3 ? metrics_v3->TemperatureEdge :
drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c +3 -6
@@ -310,8 +310,7 @@ static int vangogh_get_legacy_smu_metric
 		*value = metrics->UvdActivity / 100;
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = (metrics->CurrentSocketPower << 8) /
-		1000 ;
+		*value = metrics->CurrentSocketPower;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->GfxTemperature / 100 *
@@ -379,12 +378,10 @@ static int vangogh_get_smu_metrics_data(
 		*value = metrics->Current.UvdActivity;
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = (metrics->Average.CurrentSocketPower << 8) /
-		1000;
+		*value = metrics->Average.CurrentSocketPower;
 		break;
 	case METRICS_CURR_SOCKETPOWER:
-		*value = (metrics->Current.CurrentSocketPower << 8) /
-		1000;
+		*value = metrics->Current.CurrentSocketPower;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->Current.GfxTemperature / 100 *
drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c +3 -2
@@ -1215,9 +1215,10 @@ static int renoir_get_smu_metrics_data(s
 		    ((amdgpu_ip_version(adev, MP1_HWIP, 0) ==
 		      IP_VERSION(12, 0, 0)) &&
 		     (adev->pm.fw_version >= 0x373200)))
-			*value = metrics->CurrentSocketPower << 8;
+			*value = metrics->CurrentSocketPower *
+				 MILLIWATT_PER_WATT;
 		else
-			*value = (metrics->CurrentSocketPower << 8) / 1000;
+			*value = metrics->CurrentSocketPower;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = (metrics->GfxTemperature / 100) *
drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c +2 -1
@@ -677,7 +677,8 @@ static int aldebaran_get_smu_metrics_dat
 	case METRICS_AVERAGE_SOCKETPOWER:
 		/* Valid power data is available only from primary die */
 		if (aldebaran_is_primary(smu))
-			*value = metrics->AverageSocketPower << 8;
+			*value = metrics->AverageSocketPower *
+				 MILLIWATT_PER_WATT;
 		else
 			ret = -EOPNOTSUPP;
 		break;
drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +1 -1
@@ -807,7 +807,7 @@ static int smu_v13_0_0_get_smu_metrics_d
 			     metrics->Vcn1ActivityPercentage);
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = metrics->AverageSocketPower << 8;
+		*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->AvgTemperature[TEMP_EDGE] *
drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c +2 -1
@@ -434,7 +434,8 @@ int smu_v13_0_12_get_smu_metrics_data(st
 		*value = SMUQ10_ROUND(metrics->DramBandwidthUtilization);
 		break;
 	case METRICS_CURR_SOCKETPOWER:
-		*value = SMUQ10_ROUND(metrics->SocketPower) << 8;
+		*value = SMUQ10_ROUND(metrics->SocketPower) *
+			 MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_HOTSPOT:
 		*value = SMUQ10_ROUND(metrics->MaxSocketTemperature) *
drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c +2 -2
@@ -338,10 +338,10 @@ static int smu_v13_0_4_get_smu_metrics_d
 		*value = metrics->UvdActivity / 100;
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = (metrics->AverageSocketPower << 8) / 1000;
+		*value = metrics->AverageSocketPower;
 		break;
 	case METRICS_CURR_SOCKETPOWER:
-		*value = (metrics->CurrentSocketPower << 8) / 1000;
+		*value = metrics->CurrentSocketPower;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->GfxTemperature / 100 *
drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c +1 -1
@@ -295,7 +295,7 @@ static int smu_v13_0_5_get_smu_metrics_d
 		*value = metrics->UvdActivity / 100;
 		break;
 	case METRICS_CURR_SOCKETPOWER:
-		*value = (metrics->CurrentSocketPower << 8) / 1000;
+		*value = metrics->CurrentSocketPower;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->GfxTemperature / 100 *
drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +2 -1
@@ -1317,7 +1317,8 @@ static int smu_v13_0_6_get_smu_metrics_d
 		*value = SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization, version));
 		break;
 	case METRICS_CURR_SOCKETPOWER:
-		*value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) << 8;
+		*value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) *
+			 MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_HOTSPOT:
 		*value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, version)) *
drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +1 -1
@@ -817,7 +817,7 @@ static int smu_v13_0_7_get_smu_metrics_d
 		*value = smu_safe_u16_nn(metrics->AverageUclkActivity);
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = metrics->AverageSocketPower << 8;
+		*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->AvgTemperature[TEMP_EDGE] *
drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c +1 -1
@@ -372,7 +372,7 @@ static int yellow_carp_get_smu_metrics_d
 		*value = metrics->UvdActivity / 100;
 		break;
 	case METRICS_CURR_SOCKETPOWER:
-		*value = (metrics->CurrentSocketPower << 8) / 1000;
+		*value = metrics->CurrentSocketPower;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->GfxTemperature / 100 *
drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c +1 -2
@@ -312,8 +312,7 @@ static int smu_v14_0_0_get_smu_metrics_d
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
 	case METRICS_CURR_SOCKETPOWER:
-		*value = (metrics->SocketPower / 1000 << 8) +
-		(metrics->SocketPower % 1000 / 10);
+		*value = metrics->SocketPower;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->GfxTemperature / 100 *
drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +1 -1
@@ -695,7 +695,7 @@ static int smu_v14_0_2_get_smu_metrics_d
 			     metrics->Vcn1ActivityPercentage);
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
-		*value = metrics->AverageSocketPower << 8;
+		*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->AvgTemperature[TEMP_EDGE] *
drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c +1 -2
@@ -383,8 +383,7 @@ static int smu_v15_0_0_get_smu_metrics_d
 		break;
 	case METRICS_AVERAGE_SOCKETPOWER:
 	case METRICS_CURR_SOCKETPOWER:
-		*value = (metrics->SocketPower / 1000 << 8) +
-		(metrics->SocketPower % 1000 / 10);
+		*value = metrics->SocketPower;
 		break;
 	case METRICS_TEMPERATURE_EDGE:
 		*value = metrics->GfxTemperature / 100 *
drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c +2 -1
@@ -402,7 +402,8 @@ static int smu_v15_0_8_get_smu_metrics_d
 		*value = SMUQ10_ROUND(metrics->DramBandwidthUtilization);
 		break;
 	case METRICS_CURR_SOCKETPOWER:
-		*value = SMUQ10_ROUND(metrics->SocketPower) << 8;
+		*value = SMUQ10_ROUND(metrics->SocketPower) *
+			 MILLIWATT_PER_WATT;
 		break;
 	case METRICS_TEMPERATURE_HOTSPOT:
 		*value = SMUQ10_ROUND(metrics->MaxSocketTemperature) *
author maintainer
#400 lore raw

PATCH 397/438 drm/amd/display: check if dml21_add_phantom_plane() is successful

In reply to Greg Kroah-Hartman

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

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

From: Alex Deucher <[email protected]>

commit 000acb4ce7fb9feba3072ce468ad681f6585cd5d upstream.

Verify that the phantom plane was allocated to avoid a later segfault.

Fixes 70839da63605 ("drm/amd/display: Add new DCN401 sources")
(cherry picked from commit 5adb54abe5a8e82cbff7f8806db30a5f4924329f)
---
 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c |   15 ++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)
drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c +7 -8
@@ -359,14 +359,13 @@ void dml21_handle_phantom_streams_planes
 					main_plane = main_stream_status->plane_states[dc_plane_index];
 
 					/* create phantom planes for subvp enabled plane */
-					dml21_add_phantom_plane(dml_ctx,
-							dc,
-							context,
-							phantom_stream,
-							main_plane,
-							&dml_ctx->v21.mode_programming.programming->plane_programming[dml_plane_index]);
-
-					phantoms_added = true;
+					if (dml21_add_phantom_plane(dml_ctx,
+								    dc,
+								    context,
+								    phantom_stream,
+								    main_plane,
+								    &dml_ctx->v21.mode_programming.programming->plane_programming[dml_plane_index]))
+						phantoms_added = true;
 				}
 			}
 		}
author maintainer
#401 lore raw

PATCH 398/438 drm/amd/display: Fix divide-by-zero in calculate_mcache_setting on zero viewport

In reply to Greg Kroah-Hartman

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

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

From: George Zhang <[email protected]>

commit f327e389c07cfc3a2f6ff54f6214e1a52d457edc upstream.

If a plane reaches calculate_mcache_setting with a zero-area viewport, calculate_mcache_setting exits early with num_mcaches == 0 and mvmpg_width/height == 0. This will cause a divide-by-zero panic and can also cause an underflow on num_mcaches.

Fix this by changing calculate_mcache_setting to bool and adding guards after each calculate_mcache_row_bytes call. If num_mcaches or mvmpg_width/height is zero, return a false. Callers will propagate the failure as a rejected mode, which prevents the panic.

---
 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c |   31 ++++++++--
 1 file changed, 25 insertions(+), 6 deletions(-)
drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c +25 -6
@@ -2425,7 +2425,7 @@ static void calculate_mcache_row_bytes(
 	DML_ASSERT(*p->num_mcaches > 0);
 }
 
-static void calculate_mcache_setting(
+static bool calculate_mcache_setting(
 	struct dml2_core_internal_scratch *scratch,
 	struct dml2_core_calcs_calculate_mcache_setting_params *p)
 {
@@ -2451,7 +2451,7 @@ static void calculate_mcache_setting(
 	*p->lc_comb_mcache = 0;
 
 	if (!p->dcc_enable)
-		return;
+		return true;
 
 	l->is_dual_plane = dml_is_420(p->source_format) || p->source_format == dml2_rgbe_alpha;
 
@@ -2488,7 +2488,14 @@ static void calculate_mcache_setting(
 	l->l_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_l;
 
 	calculate_mcache_row_bytes(scratch, &l->l_p);
-	DML_ASSERT(*p->num_mcaches_l > 0);
+	if (*p->num_mcaches_l == 0 ||
+	    (p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l) == 0) {
+		DML_LOG_VERBOSE("DML::%s: degenerate luma viewport (num_mcaches_l=%u mvmpg_%s_l=%u) — mode not supported\n",
+			__func__, *p->num_mcaches_l,
+			p->surf_vert ? "height" : "width",
+			p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l);
+		return false;
+	}
 
 	if (l->is_dual_plane) {
 		l->c_p.num_chans = p->num_chans;
@@ -2524,7 +2531,14 @@ static void calculate_mcache_setting(
 		l->c_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_c;
 
 		calculate_mcache_row_bytes(scratch, &l->c_p);
-		DML_ASSERT(*p->num_mcaches_c > 0);
+		if (*p->num_mcaches_c == 0 ||
+		    (p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c) == 0) {
+			DML_LOG_VERBOSE("DML::%s: degenerate chroma viewport (num_mcaches_c=%u mvmpg_%s_c=%u) — mode not supported\n",
+				__func__, *p->num_mcaches_c,
+				p->surf_vert ? "height" : "width",
+				p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c);
+			return false;
+		}
 	}
 
 	// Sharing for iMALL access
@@ -2634,6 +2648,7 @@ static void calculate_mcache_setting(
 
 	*p->mcache_shift_granularity_l = l->mvmpg_access_width_l;
 	*p->mcache_shift_granularity_c = l->mvmpg_access_width_c;
+	return true;
 }
 
 static void calculate_mall_bw_overhead_factor(
@@ -9430,7 +9445,10 @@ static bool dml_core_mode_support(struct
 			calculate_mcache_setting_params->mall_comb_mcache_c = &mode_lib->ms.mall_comb_mcache_c[k];
 			calculate_mcache_setting_params->lc_comb_mcache = &mode_lib->ms.lc_comb_mcache[k];
 
-			calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params);
+			if (!calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params)) {
+				mode_lib->ms.support.ModeSupport = false;
+				return false;
+			}
 		}
 
 		calculate_mall_bw_overhead_factor(
@@ -10906,7 +10924,8 @@ static bool dml_core_mode_programming(st
 			calculate_mcache_setting_params->mall_comb_mcache_l = &mode_lib->mp.mall_comb_mcache_l[k];
 			calculate_mcache_setting_params->mall_comb_mcache_c = &mode_lib->mp.mall_comb_mcache_c[k];
 			calculate_mcache_setting_params->lc_comb_mcache = &mode_lib->mp.lc_comb_mcache[k];
-			calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params);
+			if (!calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params))
+				return false;
 		}
 
 		calculate_mall_bw_overhead_factor(
author maintainer
#402 lore raw

PATCH 399/438 drm/amd/display: Increase HDMI AV mute wait from 2 to 3 frames

In reply to Greg Kroah-Hartman

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

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

From: Ray Wu <[email protected]>

commit c216b39fbbc4b007fd6984cffd85039d49a55154 upstream.

Some HDMI sinks need additional GCP packets to properly process the mute state before the timing generator is disabled, especially after link re-establishment with HDMI 2.0 scrambling enabled. Waiting for only 2 frames is insufficient for certain monitor firmware, resulting in garbled display output on resume from suspend.

Increase the AV mute wait in dcn30_set_avmute() from 2 to 3 frames to ensure the sink receives enough GCP packets.

Assisted-by Cursor:Claude-Opus-4.6
(cherry picked from commit 0c0d5174b09640d8b560764aa5a177630e076e93)
---
 drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c |   16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)
drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +11 -5
@@ -841,13 +841,19 @@ void dcn30_set_avmute(struct pipe_ctx *p
 				pipe_ctx->stream_res.stream_enc,
 				enable);
 
-		/* Wait for two frame to make sure AV mute is sent out */
+		/* Wait for three frames to make sure AV mute is sent out.
+		 * Some HDMI sinks need additional GCP packets to properly
+		 * process the mute state, especially after link re-establishment
+		 * with HDMI 2.0 scrambling enabled.
+		 */
 		if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
+			int i;
+
 			pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
-			pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
-			pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
-			pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
-			pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
+			for (i = 0; i < 3; i++) {
+				pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
+				pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
+			}
 		}
 	}
 }
author maintainer
#403 lore raw

PATCH 400/438 drm/amd/display: Silence link_dpms I2C retimer failures

In reply to Greg Kroah-Hartman

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

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

From: Alan Swanson <[email protected]>

commit 8ccb87b1c9be594fc2c36b0a4006a66f08dee1c8 upstream.

Commit a4f01bf729b2 ("drm/amd/display: Refactor and fix link_dpms I2C") had also changed the "Set retimer failed" messages from DC_LOG_DEBUG() to DC_LOG_ERROR(). This unfortunately can create log spam.

Change those back to DC_LOG_DEBUG() only.

Fixes a4f01bf729b2 ("drm/amd/display: Refactor and fix link_dpms I2C")
(cherry picked from commit da8609eef18b0a3490d0e1fa9440659fadc8194d)
---
 drivers/gpu/drm/amd/display/dc/link/link_dpms.c |   16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/gpu/drm/amd/display/dc/link/link_dpms.c +8 -8
@@ -379,7 +379,7 @@ static bool write_i2c_retimer_vga(
 
 	for (size_t i = 0; i < ARRAY_SIZE(vga_data); i++) {
 		if (!write_i2c_retimer_offset_value(link, address, vga_data[i][0], vga_data[i][1])) {
-			DC_LOG_ERROR("Set retimer failed, vga index: %zu\n", i);
+			DC_LOG_DEBUG("Set retimer failed, vga index: %zu\n", i);
 			return false;
 		}
 	}
@@ -400,7 +400,7 @@ static bool write_i2c_retimer_byte(
 		return true;
 
 	if (!write_i2c_retimer_offset_value(link, address, index, value)) {
-		DC_LOG_ERROR("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value);
+		DC_LOG_DEBUG("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value);
 		return false;
 	}
 
@@ -416,14 +416,14 @@ static bool write_i2c_retimer_byte(
 			if (!link_query_ddc_data(
 					link->ddc, address, &offset, 1, &value, 1
 			)) {
-				DC_LOG_ERROR("Set retimer failed, link_query_ddc_data\n");
+				DC_LOG_DEBUG("Set retimer failed, link_query_ddc_data\n");
 				return false;
 			}
 		}
 
 		value |= apply_rx_tx_change;
 		if (!write_i2c_retimer_offset_value(link, address, offset, value)) {
-			DC_LOG_ERROR("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value);
+			DC_LOG_DEBUG("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value);
 			return false;
 		}
 	}
@@ -444,7 +444,7 @@ static bool write_i2c_retimer_setting(
 		uint8_t value = settings->reg_settings[i].i2c_reg_val;
 
 		if (!write_i2c_retimer_byte(link, address, index, value)) {
-			DC_LOG_ERROR("Set retimer failed, index: %zu\n", i);
+			DC_LOG_DEBUG("Set retimer failed, index: %zu\n", i);
 			return false;
 		}
 	}
@@ -455,7 +455,7 @@ static bool write_i2c_retimer_setting(
 			uint8_t value = settings->reg_settings_6g[i].i2c_reg_val;
 
 			if (!write_i2c_retimer_byte(link, address, index, value)) {
-				DC_LOG_ERROR("Set retimer failed, 6g index: %zu\n", i);
+				DC_LOG_DEBUG("Set retimer failed, 6g index: %zu\n", i);
 				return false;
 			}
 		}
@@ -487,7 +487,7 @@ static bool write_i2c_default_retimer_se
 
 	for (size_t i = 0; i < ARRAY_SIZE(data); i++) {
 		if (!write_i2c_retimer_offset_value(link, address, data[i][0], data[i][1])) {
-			DC_LOG_ERROR("Set default retimer failed, index: %zu\n", i);
+			DC_LOG_DEBUG("Set default retimer failed, index: %zu\n", i);
 			return false;
 		}
 	}
@@ -519,7 +519,7 @@ static bool write_i2c_redriver_setting(
 	);
 
 	if (!success)
-		DC_LOG_ERROR("Set redriver failed");
+		DC_LOG_DEBUG("Set redriver failed");
 	return success;
 }