Skip to contentSkip to conversation

6.18.53-rc1 review

PATCH In review Greg Kroah-Hartman 8 participants 1,258 messages Sep 17 – Sep 19 Page 8 of 10

Subsystems (6)
lore
Share & tools
lore
author maintainer
#993 lore raw

PATCH 992/1250 tracing: Keep the entry count when the histogram stats allocation fails

In reply to Greg Kroah-Hartman

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

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

From: Donggeun Yoo <[email protected]>

commit 06f5634ec5584954177f9a22e36b3bfb398a971b upstream.

print_entries() uses n_entries both as the number of sort entries and as its own return value, so the -ENOMEM it stores when the stats allocation fails overwrites the count that the cleanup still needs:

	n_entries = tracing_map_sort_entries(map, ...);
	if (n_entries < 0)
		return n_entries;
	...
			if (!stats) {
				n_entries = -ENOMEM;
				goto out;
			}
	...
 out:
	tracing_map_destroy_sort_entries(sort_entries, n_entries);

tracing_map_destroy_sort_entries() takes an unsigned int and loops up to it, so -ENOMEM arrives as 4294967284. It walks an array of at most map->max_elts pointers and calls destroy_sort_entry(), which dereferences and frees, on whatever lies past the end.

Reading the hist file of a trigger with a .percent value, with that allocation forced to fail:

  BUG: KASAN: vmalloc-out-of-bounds in tracing_map_destroy_sort_entries+0xa0/0xb0
  Read of size 8 at addr ffffc90000045000 by task init/1
   tracing_map_destroy_sort_entries+0xa0/0xb0
   hist_show+0x6f7/0x1df0
   seq_read_iter+0x2b8/0x1190
   vfs_read+0x176/0xa40
  The buggy address belongs to a 4-page vmalloc region starting at
  ffffc90000041000 allocated at tracing_map_sort_entries+0x5c/0xd50

A few pages further the fault is fatal. The registers at the oops confirm the bound: the loop's end pointer less the array start, over the pointer size, is 4294967284.

Return the error in a separate variable and leave n_entries holding the count, the way tracing_map_sort_entries() does on its own error path.

The stats block is only entered for a value carrying .percent or .graph, which __create_val_field() has rejected since v6.3, so this cannot be reached in mainline as it stands. It becomes reachable again with "tracing: hist: let values keep the percent and graph modifiers", so it should be applied first.

---
 kernel/trace/trace_events_hist.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
kernel/trace/trace_events_hist.c +5 -3
@@ -5664,7 +5664,7 @@ static int print_entries(struct seq_file
 {
 	struct tracing_map_sort_entry **sort_entries = NULL;
 	struct tracing_map *map = hist_data->map;
-	int i, j, n_entries;
+	int i, j, n_entries, ret;
 	struct hist_val_stat *stats = NULL;
 	u64 val;
 
@@ -5674,6 +5674,8 @@ static int print_entries(struct seq_file
 	if (n_entries < 0)
 		return n_entries;
 
+	ret = n_entries;
+
 	/* Calculate the max and the total for each field if needed. */
 	for (j = 0; j < hist_data->n_vals; j++) {
 		if (!(hist_data->fields[j]->flags &
@@ -5683,7 +5685,7 @@ static int print_entries(struct seq_file
 			stats = kcalloc(hist_data->n_vals, sizeof(*stats),
 				       GFP_KERNEL);
 			if (!stats) {
-				n_entries = -ENOMEM;
+				ret = -ENOMEM;
 				goto out;
 			}
 		}
@@ -5704,7 +5706,7 @@ static int print_entries(struct seq_file
 out:
 	tracing_map_destroy_sort_entries(sort_entries, n_entries);
 
-	return n_entries;
+	return ret;
 }
 
 static void hist_trigger_show(struct seq_file *m,
author maintainer
#994 lore raw

PATCH 993/1250 accel/ivpu: Validate full buffer range in ivpu_to_cpu_addr

In reply to Greg Kroah-Hartman

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

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

From: Magdalena Schulfer <[email protected]>

commit 3837c3f29fbc3b8c12bebf5c62741e2befe3482a upstream.

Add a size parameter to ivpu_to_cpu_addr() and validate that the whole [vpu_addr, vpu_addr + size) range stays within the BO.

---
 drivers/accel/ivpu/ivpu_gem.h |   14 +++++++++++---
 drivers/accel/ivpu/ivpu_ipc.c |    7 ++++---
 2 files changed, 15 insertions(+), 6 deletions(-)
drivers/accel/ivpu/ivpu_gem.h +11 -3
@@ -74,15 +74,23 @@ static inline bool ivpu_bo_is_snooped(st
 	return ivpu_bo_cache_mode(bo) == DRM_IVPU_BO_CACHED;
 }
 
-static inline void *ivpu_to_cpu_addr(struct ivpu_bo *bo, u32 vpu_addr)
+static inline void *ivpu_to_cpu_addr(struct ivpu_bo *bo, u64 vpu_addr, u64 size)
 {
+	u64 bo_size = ivpu_bo_size(bo);
+	u64 offset;
+
 	if (vpu_addr < bo->vpu_addr)
 		return NULL;
 
-	if (vpu_addr >= (bo->vpu_addr + ivpu_bo_size(bo)))
+	if (size > bo_size)
+		return NULL;
+
+	offset = vpu_addr - bo->vpu_addr;
+
+	if (offset > bo_size - size)
 		return NULL;
 
-	return ivpu_bo_vaddr(bo) + (vpu_addr - bo->vpu_addr);
+	return ivpu_bo_vaddr(bo) + offset;
 }
 
 static inline u32 cpu_to_vpu_addr(struct ivpu_bo *bo, void *cpu_addr)
drivers/accel/ivpu/ivpu_ipc.c +4 -3
@@ -79,7 +79,7 @@ ivpu_ipc_tx_prepare(struct ivpu_device *
 		return -ENOMEM;
 	}
 
-	tx_buf = ivpu_to_cpu_addr(ipc->mem_tx, tx_buf_vpu_addr);
+	tx_buf = ivpu_to_cpu_addr(ipc->mem_tx, tx_buf_vpu_addr, sizeof(*tx_buf));
 	if (drm_WARN_ON(&vdev->drm, !tx_buf)) {
 		gen_pool_free(ipc->mm_tx, tx_buf_vpu_addr, sizeof(*tx_buf));
 		return -EIO;
@@ -416,7 +416,7 @@ void ivpu_ipc_irq_handler(struct ivpu_de
 			return;
 		}
 
-		ipc_hdr = ivpu_to_cpu_addr(ipc->mem_rx, vpu_addr);
+		ipc_hdr = ivpu_to_cpu_addr(ipc->mem_rx, vpu_addr, sizeof(*ipc_hdr));
 		if (!ipc_hdr) {
 			ivpu_warn_ratelimited(vdev, "IPC msg 0x%x out of range\n", vpu_addr);
 			continue;
@@ -425,7 +425,8 @@ void ivpu_ipc_irq_handler(struct ivpu_de
 
 		jsm_msg = NULL;
 		if (ipc_hdr->channel != IVPU_IPC_CHAN_BOOT_MSG) {
-			jsm_msg = ivpu_to_cpu_addr(ipc->mem_rx, ipc_hdr->data_addr);
+			jsm_msg = ivpu_to_cpu_addr(ipc->mem_rx, ipc_hdr->data_addr,
+						   sizeof(*jsm_msg));
 			if (!jsm_msg) {
 				ivpu_warn_ratelimited(vdev, "JSM msg 0x%x out of range\n",
 						      ipc_hdr->data_addr);
author maintainer
#995 lore raw

PATCH 994/1250 accel/ivpu: Validate firmware log buffer metadata

In reply to Greg Kroah-Hartman

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

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

From: Magdalena Schulfer <[email protected]>

commit 0724afc55c77c36c7feb9a7264b02aa7593c5c2d upstream.

The tracing log headers parsed by fw_log_print_buffer() reside in DMA-shared BOs that the NPU firmware can write to.

fw_log_from_bo() validated log->header_size and log->size, but fw_log_print_buffer() re-read those same fields from shared memory afterwards, allowing a TOCTOU where firmware changes them between the check and the use, and making the host dereference out-of-bounds addresses while printing logs.

Snapshot the validated values once with READ_ONCE() and pass them down explicitly in a new struct ivpu_fw_log_desc instead of re-reading them from the shared struct.

---
 drivers/accel/ivpu/ivpu_fw_log.c |   76 +++++++++++++++++++++++----------------
 1 file changed, 45 insertions(+), 31 deletions(-)
drivers/accel/ivpu/ivpu_fw_log.c +45 -31
@@ -26,10 +26,17 @@ MODULE_PARM_DESC(fw_log_level,
 		 " error=" __stringify(IVPU_FW_LOG_ERROR)
 		 " fatal=" __stringify(IVPU_FW_LOG_FATAL));
 
+struct ivpu_fw_log_desc {
+	struct vpu_tracing_buffer_header *log;
+	u32 header_size;
+	u32 size;
+};
+
 static int fw_log_from_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *offset,
-			  struct vpu_tracing_buffer_header **out_log)
+			  struct ivpu_fw_log_desc *desc)
 {
 	struct vpu_tracing_buffer_header *log;
+	u32 header_size, size;
 
 	if ((*offset + sizeof(*log)) > ivpu_bo_size(bo))
 		return -EINVAL;
@@ -39,26 +46,32 @@ static int fw_log_from_bo(struct ivpu_de
 	if (log->vpu_canary_start != VPU_TRACING_BUFFER_CANARY)
 		return -EINVAL;
 
-	if (log->header_size < sizeof(*log) || log->header_size > 1024) {
-		ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size);
+	header_size = READ_ONCE(log->header_size);
+	size = READ_ONCE(log->size);
+
+	if (header_size < sizeof(*log) || header_size > 1024) {
+		ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", header_size);
 		return -EINVAL;
 	}
-	if (log->size < log->header_size) {
-		ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
+	if ((char *)log + size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) {
+		ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", size);
 		return -EINVAL;
 	}
-	if ((char *)log + log->size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) {
-		ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
+	if (size < header_size) {
+		ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x < header size 0x%x\n",
+			 size, header_size);
 		return -EINVAL;
 	}
 
-	*out_log = log;
-	*offset += log->size;
+	desc->log = log;
+	desc->header_size = header_size;
+	desc->size = size;
+	*offset += size;
 
 	ivpu_dbg(vdev, FW_BOOT,
 		 "FW log name \"%s\", write offset 0x%x size 0x%x, wrap count %d, hdr version %d size %d format %d, alignment %d",
-		 log->name, log->write_index, log->size, log->wrap_count, log->header_version,
-		 log->header_size, log->format, log->alignment);
+		 log->name, log->write_index, size, log->wrap_count, log->header_version,
+		 header_size, log->format, log->alignment);
 
 	return 0;
 }
@@ -94,11 +107,12 @@ static void fw_log_print_lines(char *buf
 		drm_printf(p, "%s", line);
 }
 
-static void fw_log_print_buffer(struct vpu_tracing_buffer_header *log, const char *prefix,
+static void fw_log_print_buffer(struct ivpu_fw_log_desc *desc, const char *prefix,
 				bool only_new_msgs, struct drm_printer *p)
 {
-	char *log_data = (void *)log + log->header_size;
-	u32 data_size = log->size - log->header_size;
+	struct vpu_tracing_buffer_header *log = desc->log;
+	char *log_data = (void *)log + desc->header_size;
+	u32 data_size = desc->size - desc->header_size;
 	u32 log_start = only_new_msgs ? READ_ONCE(log->read_index) : 0;
 	u32 log_end = READ_ONCE(log->write_index);
 
@@ -134,11 +148,11 @@ static void
 fw_log_print_all_in_bo(struct ivpu_device *vdev, const char *name,
 		       struct ivpu_bo *bo, bool only_new_msgs, struct drm_printer *p)
 {
-	struct vpu_tracing_buffer_header *log;
+	struct ivpu_fw_log_desc desc;
 	u32 next = 0;
 
-	while (fw_log_from_bo(vdev, bo, &next, &log) == 0)
-		fw_log_print_buffer(log, name, only_new_msgs, p);
+	while (fw_log_from_bo(vdev, bo, &next, &desc) == 0)
+		fw_log_print_buffer(&desc, name, only_new_msgs, p);
 }
 
 void ivpu_fw_log_print(struct ivpu_device *vdev, bool only_new_msgs, struct drm_printer *p)
@@ -149,36 +163,36 @@ void ivpu_fw_log_print(struct ivpu_devic
 
 void ivpu_fw_log_mark_read(struct ivpu_device *vdev)
 {
-	struct vpu_tracing_buffer_header *log;
+	struct ivpu_fw_log_desc desc;
 	u32 next;
 
 	next = 0;
-	while (fw_log_from_bo(vdev, vdev->fw->mem_log_crit, &next, &log) == 0) {
-		log->read_index = READ_ONCE(log->write_index);
-		log->read_wrap_count = READ_ONCE(log->wrap_count);
+	while (fw_log_from_bo(vdev, vdev->fw->mem_log_crit, &next, &desc) == 0) {
+		desc.log->read_index = READ_ONCE(desc.log->write_index);
+		desc.log->read_wrap_count = READ_ONCE(desc.log->wrap_count);
 	}
 
 	next = 0;
-	while (fw_log_from_bo(vdev, vdev->fw->mem_log_verb, &next, &log) == 0) {
-		log->read_index = READ_ONCE(log->write_index);
-		log->read_wrap_count = READ_ONCE(log->wrap_count);
+	while (fw_log_from_bo(vdev, vdev->fw->mem_log_verb, &next, &desc) == 0) {
+		desc.log->read_index = READ_ONCE(desc.log->write_index);
+		desc.log->read_wrap_count = READ_ONCE(desc.log->wrap_count);
 	}
 }
 
 void ivpu_fw_log_reset(struct ivpu_device *vdev)
 {
-	struct vpu_tracing_buffer_header *log;
+	struct ivpu_fw_log_desc desc;
 	u32 next;
 
 	next = 0;
-	while (fw_log_from_bo(vdev, vdev->fw->mem_log_crit, &next, &log) == 0) {
-		log->read_index = 0;
-		log->read_wrap_count = 0;
+	while (fw_log_from_bo(vdev, vdev->fw->mem_log_crit, &next, &desc) == 0) {
+		desc.log->read_index = 0;
+		desc.log->read_wrap_count = 0;
 	}
 
 	next = 0;
-	while (fw_log_from_bo(vdev, vdev->fw->mem_log_verb, &next, &log) == 0) {
-		log->read_index = 0;
-		log->read_wrap_count = 0;
+	while (fw_log_from_bo(vdev, vdev->fw->mem_log_verb, &next, &desc) == 0) {
+		desc.log->read_index = 0;
+		desc.log->read_wrap_count = 0;
 	}
 }
author maintainer
#996 lore raw

PATCH 995/1250 accel/ivpu: Limit firmware log name prints to field size

In reply to Greg Kroah-Hartman

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

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

From: Dawid Osuchowski <[email protected]>

commit 95bf070f3225dc7175725438c916ad321d42fe45 upstream.

The name in struct vpu_tracing_buffer_header is a fixed-size array populated by the NPU firmware. It is expected to be NUL-terminated, but nothing on the host side enforces this, so printing it with an unbounded string conversion would read past the field if the terminator is ever missing and expose adjacent bytes of the shared tracing BO through dmesg and the debugfs FW log output.

Print at most as many characters as the name field holds, so the output never runs past it even if the string is not NUL-terminated.

---
 drivers/accel/ivpu/ivpu_fw_log.c |   15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/accel/ivpu/ivpu_fw_log.c +9 -6
@@ -69,9 +69,9 @@ static int fw_log_from_bo(struct ivpu_de
 	*offset += size;
 
 	ivpu_dbg(vdev, FW_BOOT,
-		 "FW log name \"%s\", write offset 0x%x size 0x%x, wrap count %d, hdr version %d size %d format %d, alignment %d",
-		 log->name, log->write_index, size, log->wrap_count, log->header_version,
-		 header_size, log->format, log->alignment);
+		 "FW log name \"%.*s\", write offset 0x%x size 0x%x, wrap count %d, hdr version %d size %d format %d, alignment %d",
+		 (int)ARRAY_SIZE(log->name), log->name, log->write_index, size, log->wrap_count,
+		 log->header_version, header_size, log->format, log->alignment);
 
 	return 0;
 }
@@ -123,7 +123,8 @@ static void fw_log_print_buffer(struct i
 
 	if (log->wrap_count == log->read_wrap_count) {
 		if (log_end <= log_start) {
-			drm_printf(p, "==== %s \"%s\" log empty ====\n", prefix, log->name);
+			drm_printf(p, "==== %s \"%.*s\" log empty ====\n", prefix,
+				   (int)ARRAY_SIZE(log->name), log->name);
 			return;
 		}
 	} else if (log->wrap_count == log->read_wrap_count + 1) {
@@ -133,7 +134,8 @@ static void fw_log_print_buffer(struct i
 		log_start = log_end;
 	}
 
-	drm_printf(p, "==== %s \"%s\" log start ====\n", prefix, log->name);
+	drm_printf(p, "==== %s \"%.*s\" log start ====\n", prefix, (int)ARRAY_SIZE(log->name),
+		   log->name);
 	if (log_end > log_start) {
 		fw_log_print_lines(log_data + log_start, log_end - log_start, p);
 	} else {
@@ -141,7 +143,8 @@ static void fw_log_print_buffer(struct i
 		fw_log_print_lines(log_data, log_end, p);
 	}
 	drm_printf(p, "\n\x1b[0m"); /* add new line and clear formatting */
-	drm_printf(p, "==== %s \"%s\" log end   ====\n", prefix, log->name);
+	drm_printf(p, "==== %s \"%.*s\" log end   ====\n", prefix, (int)ARRAY_SIZE(log->name),
+		   log->name);
 }
 
 static void
author maintainer
#997 lore raw

PATCH 996/1250 ASoC: sprd: validate compress buffer sizes against fixed allocations

In reply to Greg Kroah-Hartman

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

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

From: Tianchu Chen <[email protected]>

commit 7a4ce92d150b9e7ecf1a710a34d8cdeb590d3751 upstream.

sprd_platform_compr_open() allocates the stage 0 IRAM buffer (32K data area) and the stage 1 DDR buffer (2M data area) with fixed sizes, but sprd_platform_compr_copy() derives all copy lengths from the user controlled runtime->fragment_size and the write() count, never comparing them against the physical buffer sizes. The compress core only checks fragment_size * fragments for an u32 overflow in snd_compress_check_input(), so a local user can configure a logical buffer of up to ~4GB via SNDRV_COMPRESS_SET_PARAMS, far exceeding the fixed allocations.

A fragment_size larger than the 32K IRAM data area makes the stage 0 copy_from_user() overflow past the IRAM allocation, and a buffer_size larger than the 2M DDR buffer makes the wrapping copy at the end of sprd_platform_compr_copy() write fully user controlled data past the buffer. No SNDRV_PCM_TRIGGER_START is needed, a write() in SETUP state reaches the copy callback directly.

Reject parameters that do not fit into the fixed buffers in set_params(), and fix the advertised max fragment size: 128K never fitted into the 32K IRAM buffer. The caps values may have been carried over from the qdsp6 driver, which allocates its buffers according to the advertised maxima, unlike this driver. With 32K as max fragment size the advertised limits are self-consistent: 32K * 64 = 2M equals the DDR buffer size.

Discovered by Atuin - Automated Vulnerability Discovery Engine.

Fixes cce1396936ef ("ASoC: sprd: Add Spreadtrum audio compress offload support")
Assisted-by LLM
---
 sound/soc/sprd/sprd-pcm-compress.c |   15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)
sound/soc/sprd/sprd-pcm-compress.c +14 -1
@@ -17,7 +17,7 @@
 
 /* Default values if userspace does not set */
 #define SPRD_COMPR_MIN_FRAGMENT_SIZE	SZ_8K
-#define SPRD_COMPR_MAX_FRAGMENT_SIZE	SZ_128K
+#define SPRD_COMPR_MAX_FRAGMENT_SIZE	SZ_32K
 #define SPRD_COMPR_MIN_NUM_FRAGMENTS	4
 #define SPRD_COMPR_MAX_NUM_FRAGMENTS	64
 
@@ -272,6 +272,19 @@ static int sprd_platform_compr_set_param
 	int ret;
 
 	/*
+	 * The stage 0 IRAM buffer and the stage 1 DDR buffer are allocated
+	 * with fixed sizes at open time, so the requested fragment size and
+	 * fragments must fit into them, otherwise sprd_platform_compr_copy()
+	 * would overflow the buffers. Note the compress core only checks the
+	 * fragment size and fragments against an u32 overflow, not against
+	 * the buffer sizes advertised by get_caps.
+	 */
+	if (params->buffer.fragment_size > SPRD_COMPR_IRAM_BUF_SIZE ||
+	    (u64)params->buffer.fragment_size * params->buffer.fragments >
+	    SPRD_COMPR_AREA_BUF_SIZE)
+		return -EINVAL;
+
+	/*
 	 * Configure the DMA engine 2-stage transfer mode. Channel 1 set as the
 	 * destination channel, and channel 0 set as the source channel, that
 	 * means once the source channel's transaction is done, it will trigger
author maintainer
#998 lore raw

PATCH 997/1250 ASoC: sti: initialize IRQ lock before requesting IRQ

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit 04405aeef4f8d7bcac6dcb1947acafdb4420c2c3 upstream.

uni_reader_init() registers the shared IRQ before initializing reader->irq_lock. A pending interrupt can invoke the handler while the lock is still uninitialized.

Initialize the lock before registering the IRQ so the interrupt path always sees valid lock state.

Fixes d05d862ead8e ("ASoC: STI: Fix null ptr deference in IRQ handler")
Assisted-by Codex:GPT-5
---
 sound/soc/sti/uniperif_reader.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/sti/uniperif_reader.c +2 -2
@@ -422,6 +422,8 @@ int uni_reader_init(struct platform_devi
 	else
 		reader->hw = &uni_reader_pcm_hw;
 
+	spin_lock_init(&reader->irq_lock);
+
 	ret = devm_request_irq(&pdev->dev, reader->irq,
 			       uni_reader_irq_handler, IRQF_SHARED,
 			       dev_name(&pdev->dev), reader);
@@ -430,8 +432,6 @@ int uni_reader_init(struct platform_devi
 		return -EBUSY;
 	}
 
-	spin_lock_init(&reader->irq_lock);
-
 	return 0;
 }
 EXPORT_SYMBOL_GPL(uni_reader_init);
author maintainer
#999 lore raw

PATCH 998/1250 Bluetooth: btqcomsmd: destroy RPMsg endpoints before freeing hci_dev

In reply to Greg Kroah-Hartman

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

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

From: Xu Rao <[email protected]>

commit f5a427b16e45210dee656b0860728f3d496dee85 upstream.

The command and ACL RPMsg endpoints store struct btqcomsmd as their callback private data. The receive callbacks dereference btq->hdev without taking an hci_dev reference.

The current teardown order frees the hci_dev before destroying the RPMsg endpoints in both the hci_register_dev() error path and the driver remove path. If WCNSS delivers data in that window, the endpoint callback can run with an already freed hci_dev and pass it to the Bluetooth core.

For qcom_smd endpoints, rpmsg_destroy_ept() closes the channel and clears the callback under the channel recv_lock. The receive path holds the same lock while invoking the callback, so destroying the endpoints first both prevents new callbacks and serializes with any callback already running.

Destroy the command and ACL endpoints before hci_free_dev(). Keep hci_unregister_dev() first during remove so the HCI core stops issuing operations before the transport endpoints are shut down. In the full registration-error cleanup path, return directly after freeing the hci_dev to avoid falling through to the partial-construction labels and destroying the endpoints twice.

Fixes 5052de8deff5 ("soc: qcom: smd: Transition client drivers from smd to rpmsg")
Fixes 9a39a927be01 ("Bluetooth: btqcomsmd: Fix a resource leak in error handling paths in the probe function")
---
 drivers/bluetooth/btqcomsmd.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/bluetooth/btqcomsmd.c +5 -1
@@ -188,7 +188,10 @@ static int btqcomsmd_probe(struct platfo
 	return 0;
 
 hci_free_dev:
+	rpmsg_destroy_ept(btq->cmd_channel);
+	rpmsg_destroy_ept(btq->acl_channel);
 	hci_free_dev(hdev);
+	return ret;
 destroy_cmd_channel:
 	rpmsg_destroy_ept(btq->cmd_channel);
 destroy_acl_channel:
@@ -202,10 +205,11 @@ static void btqcomsmd_remove(struct plat
 	struct btqcomsmd *btq = platform_get_drvdata(pdev);
 
 	hci_unregister_dev(btq->hdev);
-	hci_free_dev(btq->hdev);
 
 	rpmsg_destroy_ept(btq->cmd_channel);
 	rpmsg_destroy_ept(btq->acl_channel);
+
+	hci_free_dev(btq->hdev);
 }
 
 static const struct of_device_id btqcomsmd_of_match[] = {
author maintainer
#1000 lore raw

PATCH 999/1250 Bluetooth: btrtl: Dont leak return code when parsing firmware format v2

In reply to Greg Kroah-Hartman

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

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

From: Rong Zhang <[email protected]>

commit 83e3e515fd261600ed8491fb0a8bcdfb115c904e upstream.

When key_id from chip is zero, rtlbt_parse_firmware_v2() intentionally ignores all security headers. However, the implementation simply breaks from a switch statement and leaks uninitialized return code rc' (if the first section is a security one) or the previous section's rc'.

Fix it by really skipping a loop with `continue'. For consistency and readability, also do the same for the default case.

Fixes 9a24ce5e29b1 ("Bluetooth: btrtl: Firmware format v2 support")
---
 drivers/bluetooth/btrtl.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/bluetooth/btrtl.c +2 -3
@@ -591,7 +591,7 @@ static int rtlbt_parse_firmware_v2(struc
 			 * headers.
 			 */
 			if (!key_id)
-				break;
+				continue;
 			rc = btrtl_parse_section(hdev, btrtl_dev, opcode,
 						 ptr, section_len);
 			break;
@@ -600,8 +600,7 @@ static int rtlbt_parse_firmware_v2(struc
 						 ptr, section_len);
 			break;
 		default:
-			rc = 0;
-			break;
+			continue;
 		}
 		if (rc < 0) {
 			rtl_dev_err(hdev, "RTL: Parse section (%u) err %d",
author maintainer
#1001 lore raw

PATCH 1000/1250 cpufreq: zero-initialize policy cpumask before sysfs publication

In reply to Greg Kroah-Hartman

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

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

From: Zhongqiu Han <[email protected]>

commit 54d37bcf2f497140b9207968557ddb484058e749 upstream.

cpufreq_policy_alloc() allocates policy->cpus with alloc_cpumask_var(), i.e. without __GFP_ZERO, unlike the sibling related_cpus and real_cpus masks. With CONFIG_CPUMASK_OFFSTACK=y the mask is a separate kmalloc_node() allocation, so its bitmap holds whatever the slab allocator left behind:

cpufreq_online()

    cpufreq_policy_alloc()
      alloc_cpumask_var(&policy->cpus)    /* bitmap is uninitialized */
      kobject_init_and_add()              /* policy%u/ appears in sysfs */
    cpufreq_policy_online()
      cpumask_copy(policy->cpus, cpumask_of(cpu))  /* first valid value */

This leaves a window in which the sysfs attributes are already reachable while policy->cpus is still garbage. show()/store() gate on policy_is_inactive(), i.e. cpumask_empty(policy->cpus), so a non-zero bitmap makes them run the attribute callbacks on a policy that is not initialized yet.

Fix this by using zalloc_cpumask_var() for policy->cpus.

Fixes 2fc3384dc75b ("cpufreq: Initialize policy->kobj while allocating policy")
Cc All applicable <[email protected]>
---
 drivers/cpufreq/cpufreq.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/cpufreq/cpufreq.c +1 -1
@@ -1261,7 +1261,7 @@ static struct cpufreq_policy *cpufreq_po
 	if (!policy)
 		return NULL;
 
-	if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL))
+	if (!zalloc_cpumask_var(&policy->cpus, GFP_KERNEL))
 		goto err_free_policy;
 
 	if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL))
author maintainer
#1002 lore raw

PATCH 1001/1250 cpufreq: initialize policy rwsem before sysfs publication

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit 3e5d1bf4bd687beb2cb4e32a07af695455925588 upstream.

cpufreq_policy_alloc() initializes policy->rwsem after kobject_init_and_add() has created the policy sysfs directory and its default attributes. A sysfs access can therefore reach a policy callback before the semaphore has been initialized.

Initialize policy->rwsem before publishing the policy kobject so sysfs callbacks always see an initialized semaphore.

---
 drivers/cpufreq/cpufreq.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/cpufreq/cpufreq.c +2 -2
@@ -1270,6 +1270,8 @@ static struct cpufreq_policy *cpufreq_po
 	if (!zalloc_cpumask_var(&policy->real_cpus, GFP_KERNEL))
 		goto err_free_rcpumask;
 
+	init_rwsem(&policy->rwsem);
+
 	init_completion(&policy->kobj_unregister);
 	ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq,
 				   cpufreq_global_kobject, "policy%u", cpu);
@@ -1284,8 +1286,6 @@ static struct cpufreq_policy *cpufreq_po
 		goto err_free_real_cpus;
 	}
 
-	init_rwsem(&policy->rwsem);
-
 	freq_constraints_init(&policy->constraints);
 
 	policy->nb_min.notifier_call = cpufreq_notifier_min;
author maintainer
#1003 lore raw

PATCH 1002/1250 exec: do_close_on_exec() before taking exec_update_lock

In reply to Greg Kroah-Hartman

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

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

From: Jann Horn <[email protected]>

commit e780259b54e618ceb4763fbc21314acf3565e813 upstream.

do_close_on_exec() currently happens while holding the exec_update_lock, which is used in a lot of places that access process state to synchronize access checks.
I recently added another such use of exec_update_lock, causing a regression.

do_close_on_exec() can block waiting for a reply from a filesystem. That means a hung filesystem can block codepaths that use exec_update_lock; and it also means that a FUSE filesystem which attempts to inspect the calling process can deadlock.

To avoid such problems, move do_close_on_exec() before the exec_update_lock is taken, but after the FD table has been copied if necessary.

I have looked through all the calls between the old and new position of the do_close_on_exec() call; there seems to be no file descriptor table access in between.

---
 fs/exec.c |   22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)
fs/exec.c +14 -8
@@ -1140,6 +1140,20 @@ int begin_new_exec(struct linux_binprm *
 		goto out;
 
 	/*
+	 * We have to apply CLOEXEC before we change whether the process is
+	 * dumpable (in setup_new_exec) to avoid a race with a process in userspace
+	 * trying to access the should-be-closed file descriptors of a process
+	 * undergoing exec(2).
+	 *
+	 * This can block on filesystem ->flush() handlers, including waiting
+	 * for FUSE daemons, so do it before exec_mmap takes the
+	 * exec_update_lock.
+	 * This must happen after the point of no return, and after unsharing
+	 * the FD table.
+	 */
+	do_close_on_exec(me->files);
+
+	/*
 	 * Must be called _before_ exec_mmap() as bprm->mm is
 	 * not visible until then. Doing it here also ensures
 	 * we don't race against replace_mm_exe_file().
@@ -1189,14 +1203,6 @@ int begin_new_exec(struct linux_binprm *
 
 	clear_syscall_work_syscall_user_dispatch(me);
 
-	/*
-	 * We have to apply CLOEXEC before we change whether the process is
-	 * dumpable (in setup_new_exec) to avoid a race with a process in userspace
-	 * trying to access the should-be-closed file descriptors of a process
-	 * undergoing exec(2).
-	 */
-	do_close_on_exec(me->files);
-
 	if (bprm->secureexec) {
 		/* Make sure parent cannot signal privileged process. */
 		me->pdeath_signal = 0;
author maintainer
#1004 lore raw

PATCH 1003/1250 fs: dont return -EINVAL for successful nested thaw

In reply to Greg Kroah-Hartman

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

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

From: Moritz Tanner <[email protected]>

commit fe967191e5851ea79818c5fe4e781c3882139218 upstream.

Commit 7366f8b6fc6a ("fs: handle freezing from multiple devices") replaced the freeze_holders bitmask with per-holder counters to allow nested freezes. In the bitmask version, a thaw that released a shared hold while another holder remained returned 0. Since the rework, thaw_super_locked() drops the freeze reference via freeze_dec() but then returns -EINVAL when other freezers remain, misinforming the
caller: the thaw did succeed, the superblock just stays frozen for the remaining holders.

This breaks bdev-initiated freezing. When a filesystem is frozen with FIFREEZE and additionally frozen via bdev_freeze() -- which nests by design, see fs_bdev_freeze() -- the subsequent bdev_thaw() receives -EINVAL from the holder op although its freeze reference was dropped, and therefore keeps bd_fsfreeze_count elevated. Then device-mapper's unlock_fs() ignores bdev_thaw()'s return value, so nothing rebalances the count. After the user's FITHAW and umount, the block device can never be mounted again:

dm-1: Can't mount, blockdev is frozen

There is no way for userspace to drop the leaked count; only destroying the block device (or a reboot) recovers the device.

Reproducer (any kernel since v6.8):

    dmsetup create dut --table "0 $(blockdev --getsz "$DEV") linear $DEV 0"
    mkfs.ext4 /dev/mapper/dut
    mount /dev/mapper/dut /mnt
    fsfreeze --freeze /mnt      # freeze_ucount == 1
    dmsetup suspend dut         # bd_fsfreeze_count == 1, ucount == 2
    dmsetup resume dut          # ucount 2 -> 1, but thaw_super()
                                # returns -EINVAL, so bdev_thaw()
                                # keeps bd_fsfreeze_count at 1
    fsfreeze --unfreeze /mnt    # filesystem thaws fine
    umount /mnt
    mount /dev/mapper/dut /mnt  # EBUSY, forever

The same happens with fsfreeze held across an LVM snapshot of the origin volume.

fs_bdev_thaw()'s documentation already describes the intended
semantics: "If this function returns zero it doesn't mean that the filesystem is unfrozen as it may have been frozen multiple times". Restore them by returning 0 when a nested thaw drops its hold while other freezers remain. Thawing without holding a freeze still fails with -EINVAL as may_unfreeze() rejects that case before the reference count is touched.

Fixes 7366f8b6fc6a ("fs: handle freezing from multiple devices")
Cc [email protected] # needs adjustments for < 6.17 (no may_unfreeze())
---
 fs/super.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
fs/super.c +6 -3
@@ -2187,11 +2187,14 @@ static int thaw_super_locked(struct supe
 		goto out_unlock;
 
 	/*
-	 * All freezers share a single active reference.
-	 * So just unlock in case there are any left.
+	 * All freezers share a single active reference. If other freezers
+	 * remain, drop our hold and report success; the superblock stays
+	 * frozen until the last holder thaws it.
 	 */
-	if (freeze_dec(sb, who))
+	if (freeze_dec(sb, who)) {
+		error = 0;
 		goto out_unlock;
+	}
 
 	if (sb_rdonly(sb)) {
 		sb->s_writers.frozen = SB_UNFROZEN;
author maintainer
#1005 lore raw

PATCH 1004/1250 function_graph: Use the saved entrys size when reprinting it

In reply to Greg Kroah-Hartman

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

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

From: Donggeun Yoo <[email protected]>

commit 0701995aaf8fc2281154db829ca85e231951e51d upstream.

When a graph entry does not fit in the trace_seq, print_graph_entry() saves it in the iterator's fgraph_data and reprints it on the next read. The entry has already been consumed from the ring buffer by then, so the copy is all that is left of it.

The copy is sized with iter->ent_size, which no longer describes the saved entry but whatever entry the iterator has moved on to. The argument count is derived from the same field, so a 72 byte entry saved and then reprinted ahead of a 48 byte return entry loses its arguments.

Record the size next to the failure flag, so that the two are always set together, and restore it before reprinting.

Fixes ff5c9c576e75 ("ftrace: Add support for function argument to graph tracer")
---
 kernel/trace/trace_functions_graph.c |    3 +++
 1 file changed, 3 insertions(+)
kernel/trace/trace_functions_graph.c +3 -0
@@ -44,6 +44,7 @@ struct fgraph_data {
 	};
 	struct ftrace_graph_ret_entry	ret;
 	int				failed;
+	int				ent_size;
 	int				cpu;
 };
 
@@ -1239,6 +1240,7 @@ print_graph_entry(struct ftrace_graph_en
 		if (s->full) {
 			data->failed = 1;
 			data->cpu = cpu;
+			data->ent_size = iter->ent_size;
 		} else
 			data->failed = 0;
 	}
@@ -1422,6 +1424,7 @@ print_graph_function_flags(struct trace_
 	if (data && data->failed) {
 		field = &data->ent.ent;
 		iter->cpu = data->cpu;
+		iter->ent_size = data->ent_size;
 		ret = print_graph_entry(field, s, iter, flags);
 		if (ret == TRACE_TYPE_HANDLED && iter->cpu != cpu) {
 			per_cpu_ptr(data->cpu_data, iter->cpu)->ignore = 1;
author maintainer
#1006 lore raw

PATCH 1005/1250 drm/bridge: tc358768: Enforce input bus flags via atomic_check

In reply to Greg Kroah-Hartman

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

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

From: Leonardo Costa <[email protected]>

commit ed761e0693950fcb4f6b0f60387a3961b972adf3 upstream.

The tc358768 declares static bridge timings requiring pixel data to be sampled on the positive clock edge.

However, the DRM core default propagation simply copies the output-side bus flags, coming from the next bridge, connector or panel, to the input side. If the propagated flags are incompatible with the bridge ones, the data is wrongly sampled, typically resulting in visual artifacts on the panel.

Implement the atomic_check hook, replacing the mutually exclusive mode_fixup, and set the bridge state input bus flags to the ones required by the tc358768. The sync polarity defaulting previously done in mode_fixup is carried over into atomic_check unchanged.

---
 drivers/gpu/drm/bridge/tc358768.c |   15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)
drivers/gpu/drm/bridge/tc358768.c +10 -5
@@ -1152,10 +1152,13 @@ tc358768_atomic_get_input_bus_fmts(struc
 	return input_fmts;
 }
 
-static bool tc358768_mode_fixup(struct drm_bridge *bridge,
-				const struct drm_display_mode *mode,
-				struct drm_display_mode *adjusted_mode)
+static int tc358768_bridge_atomic_check(struct drm_bridge *bridge,
+					struct drm_bridge_state *bridge_state,
+					struct drm_crtc_state *crtc_state,
+					struct drm_connector_state *conn_state)
 {
+	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+
 	/* Default to positive sync */
 
 	if (!(adjusted_mode->flags &
@@ -1166,13 +1169,15 @@ static bool tc358768_mode_fixup(struct d
 	      (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC)))
 		adjusted_mode->flags |= DRM_MODE_FLAG_PVSYNC;
 
-	return true;
+	bridge_state->input_bus_cfg.flags = bridge->timings->input_bus_flags;
+
+	return 0;
 }
 
 static const struct drm_bridge_funcs tc358768_bridge_funcs = {
 	.attach = tc358768_bridge_attach,
 	.mode_valid = tc358768_bridge_mode_valid,
-	.mode_fixup = tc358768_mode_fixup,
+	.atomic_check = tc358768_bridge_atomic_check,
 	.atomic_pre_enable = tc358768_bridge_atomic_pre_enable,
 	.atomic_enable = tc358768_bridge_atomic_enable,
 	.atomic_disable = tc358768_bridge_atomic_disable,
author maintainer
#1007 lore raw

PATCH 1006/1250 drm/drm_exec: fix up contended obj when num_objects is 0

In reply to Greg Kroah-Hartman

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

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

From: Sunil Khatri <[email protected]>

commit 159720704d9d652b64390c11fb971e15b0a78d23 upstream.

drm_exec_prepare_array() silently returns success without calling drm_exec_lock_contended() when num_objects is zero. This breaks the invariant upheld by drm_exec_lock_obj(), where every entry point into the locking sequence must first attempt to lock any previously contended object before proceeding.

Drivers that chain multiple drm_exec_prepare_array() calls per drm_exec_until_all_locked() iteration (e.g. amdgpu's userq signal/wait ioctls, which prepare separate read and write BO arrays) can pass an empty array for one of the two calls. If contention is hit while preparing the non-empty array, exec->contended is set and the loop retries; on retry, the empty-array call preceding it is a no-op that never clears exec->contended, so drm_exec_retry_on_contention() immediately jumps back to the top of the loop without ever reaching the call that would resolve the contention. This spins forever.

Fix it by having drm_exec_prepare_array() call drm_exec_lock_contended() directly when num_objects is zero, so a pending contended object dont loop infinitely.

---
 drivers/gpu/drm/drm_exec.c |   13 +++++++++++++
 1 file changed, 13 insertions(+)
drivers/gpu/drm/drm_exec.c +13 -0
@@ -325,6 +325,19 @@ int drm_exec_prepare_array(struct drm_ex
 {
 	int ret;
 
+	/*
+	 * Make sure to lock a contended object even when no objects are
+	 * given, otherwise drm_exec_retry_on_contention() would loop
+	 * forever on patterns like:
+	 *
+	 *	ret = drm_exec_prepare_array(exec, objs, num_objects, ...);
+	 *	drm_exec_retry_on_contention(exec);
+	 *
+	 * with num_objects == 0.
+	 */
+	if (!num_objects)
+		return drm_exec_lock_contended(exec);
+
 	for (unsigned int i = 0; i < num_objects; ++i) {
 		ret = drm_exec_prepare_obj(exec, objects[i], num_fences);
 		if (unlikely(ret))
author maintainer
#1008 lore raw

PATCH 1007/1250 drm/i915: Fix memory leak in query_perf_config_list()

In reply to Greg Kroah-Hartman

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

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

From: Thorsten Blum <[email protected]>

commit cbd3dafc2003db679ccd2f6c6a2551db79657049 upstream.

When krealloc() fails, free the original oa_config_ids before returning to avoid a memory leak.

Fixes 4f6ccc74a85c ("drm/i915: add support for perf configuration queries")
Cc <[email protected]> # v5.5+
(cherry picked from commit 9977e9d84f46d4f12ad35fbbc0ec4638554bce87)
---
 drivers/gpu/drm/i915/i915_query.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/i915/i915_query.c +3 -1
@@ -401,8 +401,10 @@ static int query_perf_config_list(struct
 		ids = krealloc(oa_config_ids,
 			       n_configs * sizeof(*oa_config_ids),
 			       GFP_KERNEL);
-		if (!ids)
+		if (!ids) {
+			kfree(oa_config_ids);
 			return -ENOMEM;
+		}
 
 		alloc = fetch_and_zero(&n_configs);
 
author maintainer
#1009 lore raw

PATCH 1008/1250 drm/rockchip: analogix_dp: fix unchecked bound endpoint name length

In reply to Greg Kroah-Hartman

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

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

From: Yudi Yang <[email protected]>

commit bc69439d983cc491cc86e01fafc1deb94e1bb85e upstream.

rockchip_dp_drm_encoder_enable() uses sprintf() to format a device tree path into a 32-byte stack buffer. Device tree paths are not limited to this size, so a sufficiently long path can overflow the buffer.

Use snprintf() with the destination size to truncate the generated name and keep the writes within bounds.

Fixes 729f8eefdcad ("drm/rockchip: analogix_dp: Add support for RK3588")
---
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +4 -3
@@ -258,10 +258,11 @@ static void rockchip_dp_drm_encoder_enab
 				of_graph_get_remote_port(endpoint.local_node);
 
 			of_property_read_u32(remote_port, "reg", &port_id);
-			sprintf(name, "%s vp%d", remote_port_parent->full_name, port_id);
+			snprintf(name, sizeof(name), "%s vp%d",
+				 remote_port_parent->full_name, port_id);
 		} else {
-			sprintf(name, "%s %s",
-				remote_port_parent->full_name, endpoint.id ? "vopl" : "vopb");
+			snprintf(name, sizeof(name), "%s %s",
+				 remote_port_parent->full_name, endpoint.id ? "vopl" : "vopb");
 		}
 
 		DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
author maintainer
#1010 lore raw

PATCH 1009/1250 drm/sched: Create a fake device for KUnit tests

In reply to Greg Kroah-Hartman

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

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

From: Shixiong Ou <[email protected]>

commit 28cc4d5a75bb07d0eb2fa178db355b04483f5aca upstream.

The DRM scheduler KUnit tests pass NULL for the dev field in drm_sched_init_args, which NULL-pointer dereferences in the drm_sched_job trace event via dev_name() on sched->dev.

Give the mock scheduler a device with kunit_device_register(), which is also cleaned up at test exit. A per-function counter keeps the device names unique, since some tests create several mock schedulers.

Fixes 5a99350794fe ("drm/sched: Add scheduler unit testing infrastructure and some basic tests")
[phasta: removed static variable init to 0 again]
---
 drivers/gpu/drm/scheduler/tests/mock_scheduler.c |   11 +++++++++++
 1 file changed, 11 insertions(+)
drivers/gpu/drm/scheduler/tests/mock_scheduler.c +11 -0
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2025 Valve Corporation */
 
+#include <kunit/device.h>
+
 #include "sched_tests.h"
 
 /*
@@ -288,6 +290,7 @@ static const struct drm_sched_backend_op
  */
 struct drm_mock_scheduler *drm_mock_sched_new(struct kunit *test, long timeout)
 {
+	static unsigned int instance;
 	struct drm_sched_init_args args = {
 		.ops		= &drm_mock_scheduler_ops,
 		.num_rqs	= DRM_SCHED_PRIORITY_COUNT,
@@ -297,11 +300,19 @@ struct drm_mock_scheduler *drm_mock_sche
 		.name		= "drm-mock-scheduler",
 	};
 	struct drm_mock_scheduler *sched;
+	struct device *dev;
+	char name[64];
 	int ret;
 
 	sched = kunit_kzalloc(test, sizeof(*sched), GFP_KERNEL);
 	KUNIT_ASSERT_NOT_NULL(test, sched);
 
+	snprintf(name, sizeof(name), "%s-%u", args.name, instance++);
+	dev = kunit_device_register(test, name);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+	args.dev = dev;
+
 	ret = drm_sched_init(&sched->base, &args);
 	KUNIT_ASSERT_EQ(test, ret, 0);
 
author maintainer
#1011 lore raw

PATCH 1010/1250 io_uring/net: let io_recv_buf_select return the length of the buffer region

In reply to Greg Kroah-Hartman

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

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

From: Gabriel Krisman Bertazi <[email protected]>

commit dcbd1c054848848a1937ca0768ce2bdbc31ae621 upstream.

In preparation to using this field as an upper limit to truncation, return the size of the allocated region.

Fixes ae98dbf43d75 ("io_uring/kbuf: add support for incremental buffer consumption")
---
 io_uring/net.c |   10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
io_uring/net.c +6 -4
@@ -1107,6 +1107,7 @@ static int io_recv_buf_select(struct io_
 			      struct io_br_sel *sel, unsigned int issue_flags)
 {
 	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);
+	size_t len;
 	int ret;
 
 	/*
@@ -1152,13 +1153,14 @@ static int io_recv_buf_select(struct io_
 		/* special case 1 vec, can be a fast path */
 		if (ret == 1) {
 			sr->buf = arg.iovs[0].iov_base;
-			sr->len = arg.iovs[0].iov_len;
+			len = sr->len = arg.iovs[0].iov_len;
 			goto map_ubuf;
 		}
 		iov_iter_init(&kmsg->msg.msg_iter, ITER_DEST, arg.iovs, ret,
-				arg.out_len);
+			      arg.out_len);
+		len = arg.out_len;
 	} else {
-		size_t len = sel->val;
+		len = sel->val;
 
 		*sel = io_buffer_select(req, &len, sr->buf_group, issue_flags);
 		if (!sel->addr)
@@ -1172,7 +1174,7 @@ map_ubuf:
 			return ret;
 	}
 
-	return 0;
+	return len;
 }
 
 int io_recv(struct io_kiocb *req, unsigned int issue_flags)
author maintainer
#1012 lore raw

PATCH 1011/1250 io_uring/net: dont overconsume buffers when using MSG_TRUNC

In reply to Greg Kroah-Hartman

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

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

From: Gabriel Krisman Bertazi <[email protected]>

commit 6028b543884f8735e057ec9eea4908cd61cab230 upstream.

When a recv/recvmsg is issued with MSG_TRUNC and the incoming packet is larger than the provided buffer, the net layer returns the full length of the packet rather than the number of bytes actually copied into the buffer. As a result, io_uring advances more of the provided buffer ring than was actually filled. Use the actual filled region size to consume the buffer, but still return the full size to preserve MSG_TRUNC semantics.

Take care with multishot, because that seems to already truncate the consumption based on the available payload size.

This was reported in https://github.com/axboe/liburing/issues/1619.

Fixes ae98dbf43d75 ("io_uring/kbuf: add support for incremental buffer consumption")
[axboe: fold in size_t unsigned fix]
---
 io_uring/net.c |   41 +++++++++++++++++++++++++++++++++--------
 1 file changed, 33 insertions(+), 8 deletions(-)
io_uring/net.c +33 -8
@@ -852,7 +852,7 @@ int io_recvmsg_prep(struct io_kiocb *req
 static inline bool io_recv_finish(struct io_kiocb *req,
 				  struct io_async_msghdr *kmsg,
 				  struct io_br_sel *sel, bool mshot_finished,
-				  unsigned issue_flags)
+				  unsigned issue_flags, int consumed)
 {
 	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);
 	unsigned int cflags = 0;
@@ -876,7 +876,7 @@ static inline bool io_recv_finish(struct
 	if (sr->flags & IORING_RECVSEND_BUNDLE) {
 		size_t this_ret = sel->val - sr->done_io;
 
-		cflags |= io_put_kbufs(req, this_ret, sel->buf_list, io_bundle_nbufs(kmsg, this_ret));
+		cflags |= io_put_kbufs(req, consumed, sel->buf_list, io_bundle_nbufs(kmsg, consumed));
 		if (sr->flags & IORING_RECV_RETRY)
 			cflags = req->cqe.flags | (cflags & CQE_F_MASK);
 		if (sr->mshot_len && sel->val >= sr->mshot_len)
@@ -898,7 +898,7 @@ static inline bool io_recv_finish(struct
 			return false;
 		}
 	} else {
-		cflags |= io_put_kbuf(req, sel->val, sel->buf_list);
+		cflags |= io_put_kbuf(req, consumed, sel->buf_list);
 	}
 
 	/*
@@ -1026,6 +1026,8 @@ int io_recvmsg(struct io_kiocb *req, uns
 	int ret, min_ret = 0;
 	bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
 	bool mshot_finished = true;
+	int consumed = 0;
+	size_t len;
 
 	sock = sock_from_file(req->file);
 	if (unlikely(!sock))
@@ -1041,9 +1043,8 @@ int io_recvmsg(struct io_kiocb *req, uns
 
 retry_multishot:
 	sel.buf_list = NULL;
+	len = sr->len;
 	if (io_do_buffer_select(req)) {
-		size_t len = sr->len;
-
 		sel = io_buffer_select(req, &len, sr->buf_group, issue_flags);
 		if (!sel.addr)
 			return -ENOBUFS;
@@ -1064,6 +1065,7 @@ retry_multishot:
 	if (req->flags & REQ_F_APOLL_MULTISHOT) {
 		ret = io_recvmsg_multishot(sock, sr, kmsg, flags,
 					   &mshot_finished);
+		consumed = ret;
 	} else {
 		/* disable partial retry for recvmsg with cmsg attached */
 		if (flags & MSG_WAITALL && !kmsg->msg.msg_controllen)
@@ -1071,6 +1073,15 @@ retry_multishot:
 
 		ret = __sys_recvmsg_sock(sock, &kmsg->msg, sr->umsg,
 					 kmsg->uaddr, flags);
+		/*
+		 * With MSG_TRUNC, the net layer will return the full size of
+		 * the packet, even if we only filled part of it in the buffers.
+		 * Adjust the returned size to consume only the real part of the
+		 * buffer.
+		 */
+		consumed = ret;
+		if (ret > 0)
+			consumed = min_t(size_t, ret, len);
 	}
 
 	if (ret < min_ret) {
@@ -1097,7 +1108,7 @@ retry_multishot:
 		io_kbuf_recycle(req, sel.buf_list, issue_flags);
 
 	sel.val = ret;
-	if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags))
+	if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags, consumed))
 		goto retry_multishot;
 
 	return sel.val;
@@ -1184,9 +1195,10 @@ int io_recv(struct io_kiocb *req, unsign
 	struct io_br_sel sel;
 	struct socket *sock;
 	unsigned flags;
-	int ret, min_ret = 0;
+	int ret, min_ret = 0, consumed = 0;
 	bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
 	bool mshot_finished;
+	size_t len = 0;
 
 	if (!(req->flags & REQ_F_POLLED) &&
 	    (sr->flags & IORING_RECVSEND_POLL_FIRST))
@@ -1202,6 +1214,7 @@ int io_recv(struct io_kiocb *req, unsign
 
 retry_multishot:
 	sel.buf_list = NULL;
+	len = sr->len;
 	if (io_do_buffer_select(req)) {
 		sel.val = sr->len;
 		ret = io_recv_buf_select(req, kmsg, &sel, issue_flags);
@@ -1209,6 +1222,7 @@ retry_multishot:
 			kmsg->msg.msg_inq = -1;
 			goto out_free;
 		}
+		len = ret;
 		sr->buf = NULL;
 	}
 
@@ -1239,6 +1253,17 @@ out_free:
 	}
 
 	mshot_finished = ret <= 0;
+
+	/*
+	 * With MSG_TRUNC, the net layer will return the full size of
+	 * the packet, even if we only filled part of it in the buffers.
+	 * Adjust the returned size to consume only the real part of the
+	 * buffer.
+	 */
+	consumed = ret;
+	if (ret > 0)
+		consumed = min_t(size_t, ret, len);
+
 	if (ret > 0)
 		ret += sr->done_io;
 	else if (sr->done_io)
@@ -1247,7 +1272,7 @@ out_free:
 		io_kbuf_recycle(req, sel.buf_list, issue_flags);
 
 	sel.val = ret;
-	if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags))
+	if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags, consumed))
 		goto retry_multishot;
 
 	return sel.val;
author maintainer
#1013 lore raw

PATCH 1012/1250 ring-buffer: Acquire the lock with irqsave in rb_wake_up_waiters()

In reply to Greg Kroah-Hartman

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

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

From: Sebastian Andrzej Siewior <[email protected]>

commit 815e07c8fe885a87751c2496a30ae0dcd4118210 upstream.

rb_wake_up_waiters() is a irq_work callback which is initialized with init_irq_work(). As such it will be invoked in thread context on PREEMPT_RT. Invoking the callback in IRQ context on PREEMPT_RT is not an option due its usage of wake_up_all(). Since this callback may run in thread context, it needs to acquire ring_buffer_per_cpu::reader_lock with disabling interrupts and may not assume that they are disabled.

Use raw_spinlock_irqsave() to acquire ring_buffer_per_cpu::reader_lock.

---
 kernel/trace/ring_buffer.c |   13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)
kernel/trace/ring_buffer.c +6 -7
@@ -875,14 +875,13 @@ static void rb_wake_up_waiters(struct ir
 		struct ring_buffer_per_cpu *cpu_buffer =
 			container_of(rbwork, struct ring_buffer_per_cpu, irq_work);
 
-		/* Called from interrupt context */
-		raw_spin_lock(&cpu_buffer->reader_lock);
-		rbwork->wakeup_full = false;
-		rbwork->full_waiters_pending = false;
+		scoped_guard(raw_spinlock_irqsave, &cpu_buffer->reader_lock) {
+			rbwork->wakeup_full = false;
+			rbwork->full_waiters_pending = false;
 
-		/* Waking up all waiters, they will reset the shortest full */
-		cpu_buffer->shortest_full = 0;
-		raw_spin_unlock(&cpu_buffer->reader_lock);
+			/* Waking up all waiters, they will reset the shortest full */
+			cpu_buffer->shortest_full = 0;
+		}
 
 		wake_up_all(&rbwork->full_waiters);
 	}
author maintainer
#1014 lore raw

PATCH 1013/1250 ring-buffer: Check resize_disabled before publishing the new subbuf order

In reply to Greg Kroah-Hartman

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

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

From: David Carlier <[email protected]>

commit d860c67c051685abb0460b593b193f0f45f4fa92 upstream.

ring_buffer_subbuf_order_set() stores the new order and only then walks the CPUs, returning -EBUSY if any of them has resizing disabled. A user mapped buffer has resizing disabled, and __rb_map_vma() reads buffer->subbuf_order without buffer->mutex, so an mmap of an already mapped CPU racing the failing order change sizes the mapping with the new order and inserts pages past the sub-buffer into the VMA.

Check the CPUs before storing the new order.

Fixes 117c39200d9d ("ring-buffer: Introducing ring-buffer mapping functions")
---
 kernel/trace/ring_buffer.c |    8 ++++++++
 1 file changed, 8 insertions(+)
kernel/trace/ring_buffer.c +8 -0
@@ -6932,6 +6932,14 @@ int ring_buffer_subbuf_order_set(struct
 
 	old_capacity = rb_subbuf_capacity(buffer);
 
+	/* The mmap fast path reads subbuf_order without buffer->mutex. */
+	for_each_buffer_cpu(buffer, cpu) {
+		if (!cpumask_test_cpu(cpu, buffer->cpumask))
+			continue;
+		if (atomic_read(&buffer->buffers[cpu]->resize_disabled))
+			return -EBUSY;
+	}
+
 	atomic_inc(&buffer->record_disabled);
 
 	/* Make sure all commits have finished */
author maintainer
#1015 lore raw

PATCH 1014/1250 net/sched: act_api: release all action references on NEWACTION failure

In reply to Greg Kroah-Hartman

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

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

From: Xuanqiang Luo <[email protected]>

commit 478eb5abb51931a152abab068f8a717b7ff480fd upstream.

When a batched RTM_NEWACTION request replaces an existing action, tcf_idr_check_alloc() takes a temporary reference on it. If a later action fails to initialize, tcf_action_destroy() uses strict release semantics to clean up the actions initialized so far. For an action bound to a filter, the strict check returns -EPERM without dropping the temporary reference.

This error also makes tcf_action_destroy() return before releasing subsequent entries. Any new action initialized between the bound action and the failing entry is leaked together with its reserved IDR slot, preventing reuse of its index.

Use tcf_idr_release() to drop each reference held by the batch without rejecting bound actions. This allows cleanup to continue through all initialized entries and preserves the module reference release when an action is destroyed. Explicit action deletion and flushing retain their separate bind-count checks.

---
 net/sched/act_api.c |    9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)
net/sched/act_api.c +2 -7
@@ -1200,18 +1200,13 @@ EXPORT_SYMBOL(tcf_action_exec);
 
 int tcf_action_destroy(struct tc_action *actions[], int bind)
 {
-	const struct tc_action_ops *ops;
 	struct tc_action *a;
 	int ret = 0, i;
 
 	tcf_act_for_each_action(i, a, actions) {
 		actions[i] = NULL;
-		ops = a->ops;
-		ret = __tcf_idr_release(a, bind, true);
-		if (ret == ACT_P_DELETED)
-			module_put(ops->owner);
-		else if (ret < 0)
-			return ret;
+		/* Drop our reference even if the action is still bound to a filter. */
+		ret = tcf_idr_release(a, bind);
 	}
 	return ret;
 }
author maintainer
#1016 lore raw

PATCH 1015/1250 net: bridge: use option bits for CFM/MRP frame handlers

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit 7a49e6b16f36b8e085521699adbca3e321b6dd0c upstream.

CFM and MRP register a global br_frame_type whose hlist_node is linked into the per-bridge frame_type_list when the first MEP/MRP instance is created. Enabling the protocol on multiple bridges therefore inserts the same node into multiple lists. Unregistering it on one bridge then corrupts list state belonging to another.

These handlers can only be installed once per bridge, and they are uncommon. Track their per-bridge enable state with net_bridge option bits, which already live on the Rx hot cache line, and dispatch the matching handler directly from the receive path. Check both bits together first as an unlikely case.

Remove the generic frame_type_list and br_frame_type helpers, which have had no other users since CFM and MRP were added. That shrinks struct net_bridge by 8 bytes and drops the list walk from the fast path. When neither protocol is compiled in, BR_CFM_MRP_OPTS is 0 and the compiler prunes the branch.

Fixes 90c628dd47ff ("net: bridge: extend the process of special frames")
Fixes dc32cbb3dbd7 ("bridge: cfm: Kernel space implementation of CFM. CCM frame RX added.")
---
 net/bridge/br_cfm.c     |   11 +++--------
 net/bridge/br_device.c  |    1 -
 net/bridge/br_input.c   |   35 ++++++++++++++---------------------
 net/bridge/br_mrp.c     |   13 +++----------
 net/bridge/br_private.h |   26 +++++++++++++++-----------
 5 files changed, 35 insertions(+), 51 deletions(-)
net/bridge/br_cfm.c +3 -8
@@ -367,7 +367,7 @@ static u32 ccm_tlv_extract(struct sk_buf
 }
 
 /* note: already called with rcu_read_lock */
-static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb)
+int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb)
 {
 	u32 mdlevel, interval, size, index, max;
 	const struct br_cfm_common_hdr *hdr;
@@ -489,11 +489,6 @@ static int br_cfm_frame_rx(struct net_br
 	return 1;
 }
 
-static struct br_frame_type cfm_frame_type __read_mostly = {
-	.type = cpu_to_be16(ETH_P_CFM),
-	.frame_handler = br_cfm_frame_rx,
-};
-
 int br_cfm_mep_create(struct net_bridge *br,
 		      const u32 instance,
 		      struct br_cfm_mep_create *const create,
@@ -559,7 +554,7 @@ int br_cfm_mep_create(struct net_bridge
 	INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
 
 	if (hlist_empty(&br->mep_list))
-		br_add_frame(br, &cfm_frame_type);
+		br_opt_toggle(br, BROPT_CFM_ENABLED, true);
 
 	hlist_add_tail_rcu(&mep->head, &br->mep_list);
 
@@ -588,7 +583,7 @@ static void mep_delete_implementation(st
 	kfree_rcu(mep, rcu);
 
 	if (hlist_empty(&br->mep_list))
-		br_del_frame(br, &cfm_frame_type);
+		br_opt_toggle(br, BROPT_CFM_ENABLED, false);
 }
 
 int br_cfm_mep_delete(struct net_bridge *br,
net/bridge/br_device.c +0 -1
@@ -503,7 +503,6 @@ void br_dev_setup(struct net_device *dev
 	spin_lock_init(&br->lock);
 	INIT_LIST_HEAD(&br->port_list);
 	INIT_HLIST_HEAD(&br->fdb_list);
-	INIT_HLIST_HEAD(&br->frame_type_list);
 #if IS_ENABLED(CONFIG_BRIDGE_MRP)
 	INIT_HLIST_HEAD(&br->mrp_list);
 #endif
net/bridge/br_input.c +14 -21
@@ -317,17 +317,25 @@ frame_finish:
 	return RX_HANDLER_CONSUMED;
 }
 
+#define BR_CFM_MRP_OPTS \
+	((IS_ENABLED(CONFIG_BRIDGE_CFM) ? BIT(BROPT_CFM_ENABLED) : 0UL) | \
+	 (IS_ENABLED(CONFIG_BRIDGE_MRP) ? BIT(BROPT_MRP_ENABLED) : 0UL))
+
 /* Return 0 if the frame was not processed otherwise 1
  * note: already called with rcu_read_lock
  */
 static int br_process_frame_type(struct net_bridge_port *p,
 				 struct sk_buff *skb)
 {
-	struct br_frame_type *tmp;
+	struct net_bridge *br = p->br;
 
-	hlist_for_each_entry_rcu(tmp, &p->br->frame_type_list, list)
-		if (unlikely(tmp->type == skb->protocol))
-			return tmp->frame_handler(p, skb);
+	if (skb->protocol == htons(ETH_P_CFM) &&
+	    br_opt_get(br, BROPT_CFM_ENABLED))
+		return br_cfm_frame_rx(p, skb);
+
+	if (skb->protocol == htons(ETH_P_MRP) &&
+	    br_opt_get(br, BROPT_MRP_ENABLED))
+		return br_mrp_process(p, skb);
 
 	return 0;
 }
@@ -425,7 +433,8 @@ static rx_handler_result_t br_handle_fra
 		}
 	}
 
-	if (unlikely(br_process_frame_type(p, skb)))
+	if (unlikely((READ_ONCE(p->br->options) & BR_CFM_MRP_OPTS) &&
+		     br_process_frame_type(p, skb)))
 		return RX_HANDLER_PASS;
 
 forward:
@@ -467,19 +476,3 @@ rx_handler_func_t *br_get_rx_handler(con
 
 	return br_handle_frame;
 }
-
-void br_add_frame(struct net_bridge *br, struct br_frame_type *ft)
-{
-	hlist_add_head_rcu(&ft->list, &br->frame_type_list);
-}
-
-void br_del_frame(struct net_bridge *br, struct br_frame_type *ft)
-{
-	struct br_frame_type *tmp;
-
-	hlist_for_each_entry(tmp, &br->frame_type_list, list)
-		if (ft == tmp) {
-			hlist_del_rcu(&ft->list);
-			return;
-		}
-}
net/bridge/br_mrp.c +3 -10
@@ -6,13 +6,6 @@
 static const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 };
 static const u8 mrp_in_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x3 };
 
-static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb);
-
-static struct br_frame_type mrp_frame_type __read_mostly = {
-	.type = cpu_to_be16(ETH_P_MRP),
-	.frame_handler = br_mrp_process,
-};
-
 static bool br_mrp_is_ring_port(struct net_bridge_port *p_port,
 				struct net_bridge_port *s_port,
 				struct net_bridge_port *port)
@@ -486,7 +479,7 @@ static void br_mrp_del_impl(struct net_b
 	kfree_rcu(mrp, rcu);
 
 	if (hlist_empty(&br->mrp_list))
-		br_del_frame(br, &mrp_frame_type);
+		br_opt_toggle(br, BROPT_MRP_ENABLED, false);
 }
 
 /* Adds a new MRP instance.
@@ -536,7 +529,7 @@ int br_mrp_add(struct net_bridge *br, st
 	rcu_assign_pointer(mrp->s_port, p);
 
 	if (hlist_empty(&br->mrp_list))
-		br_add_frame(br, &mrp_frame_type);
+		br_opt_toggle(br, BROPT_MRP_ENABLED, true);
 
 	INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired);
 	INIT_DELAYED_WORK(&mrp->in_test_work, br_mrp_in_test_work_expired);
@@ -1241,7 +1234,7 @@ no_forward:
  * normal forwarding.
  * note: already called with rcu_read_lock
  */
-static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
+int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
 {
 	/* If there is no MRP instance do normal forwarding */
 	if (likely(!(p->flags & BR_MRP_AWARE)))
net/bridge/br_private.h +15 -11
@@ -489,12 +489,13 @@ enum net_bridge_opts {
 	BROPT_MST_ENABLED,
 	BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION,
 	BROPT_FDB_LOCAL_VLAN_0,
+	BROPT_CFM_ENABLED,
+	BROPT_MRP_ENABLED,
 };
 
 struct net_bridge {
 	spinlock_t			lock;
 	spinlock_t			hash_lock;
-	struct hlist_head		frame_type_list;
 	struct net_device		*dev;
 	unsigned long			options;
 	/* These fields are accessed on each packet */
@@ -924,16 +925,6 @@ int nbp_backup_change(struct net_bridge_
 int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
 rx_handler_func_t *br_get_rx_handler(const struct net_device *dev);
 
-struct br_frame_type {
-	__be16			type;
-	int			(*frame_handler)(struct net_bridge_port *port,
-						 struct sk_buff *skb);
-	struct hlist_node	list;
-};
-
-void br_add_frame(struct net_bridge *br, struct br_frame_type *ft);
-void br_del_frame(struct net_bridge *br, struct br_frame_type *ft);
-
 static inline bool br_rx_handler_check_rcu(const struct net_device *dev)
 {
 	return rcu_dereference(dev->rx_handler) == br_get_rx_handler(dev);
@@ -2065,6 +2056,7 @@ int br_mrp_parse(struct net_bridge *br,
 bool br_mrp_enabled(struct net_bridge *br);
 void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p);
 int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br);
+int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb);
 #else
 static inline int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
 			       struct nlattr *attr, int cmd,
@@ -2088,6 +2080,11 @@ static inline int br_mrp_fill_info(struc
 	return 0;
 }
 
+static inline int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
+{
+	return 0;
+}
+
 #endif
 
 /* br_cfm.c */
@@ -2096,6 +2093,7 @@ int br_cfm_parse(struct net_bridge *br,
 		 struct nlattr *attr, int cmd, struct netlink_ext_ack *extack);
 bool br_cfm_created(struct net_bridge *br);
 void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *p);
+int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb);
 int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br);
 int br_cfm_status_fill_info(struct sk_buff *skb,
 			    struct net_bridge *br,
@@ -2120,6 +2118,12 @@ static inline void br_cfm_port_del(struc
 {
 }
 
+static inline int br_cfm_frame_rx(struct net_bridge_port *port,
+				  struct sk_buff *skb)
+{
+	return 0;
+}
+
 static inline int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br)
 {
 	return -EOPNOTSUPP;
author maintainer
#1017 lore raw

PATCH 1016/1250 net: dsa: tag_brcm: legacy FCS: request needed tailroom

In reply to Greg Kroah-Hartman

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

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

From: Weiming Shi <[email protected]>

commit 5be081b83abd3f17d908953b4bb77279f5a149e3 upstream.

The legacy FCS tagger calculates the CRC over skb->len bytes starting at skb->data. When a nonlinear skb reaches the tagger, this reads past the linear head into unrelated slab memory.

The tagger appends an Ethernet FCS but does not declare that tailroom. As a result, DSA leaves NETIF_F_SG and NETIF_F_FRAGLIST enabled on the user port, and nonlinear skbs can reach the CRC calculation.

Declare the required tailroom. DSA will then clear those features and the networking core will linearize skbs before the tagger runs.

A KASAN-enabled dsa_loop test using this tagger reports:

  BUG: KASAN: slab-out-of-bounds in crc32_le
  Read of size 1 at addr ffff8880397086c0 by task exp/135

  Call Trace:
   crc32_le (lib/crc/crc32-main.c:38)
   brcm_leg_fcs_tag_xmit (net/dsa/tag_brcm.c:343)
   dsa_user_xmit (net/dsa/user.c:942)
   dev_hard_start_xmit (net/core/dev.c:3937)
   __dev_queue_xmit (net/core/dev.c:4926)
   packet_sendmsg (net/packet/af_packet.c:3110)
   __sys_sendto (net/socket.c:2281)

  The buggy address belongs to the object at ffff888039708400
   which belongs to the cache skbuff_small_head of size 704
  The buggy address is located 0 bytes to the right of
   allocated 704-byte region [ffff888039708400, ffff8880397086c0)
---
 net/dsa/tag_brcm.c |    1 +
 1 file changed, 1 insertion(+)
net/dsa/tag_brcm.c +1 -0
@@ -364,6 +364,7 @@ static const struct dsa_device_ops brcm_
 	.xmit = brcm_leg_fcs_tag_xmit,
 	.rcv = brcm_leg_tag_rcv,
 	.needed_headroom = BRCM_LEG_TAG_LEN,
+	.needed_tailroom = ETH_FCS_LEN,
 };
 
 DSA_TAG_DRIVER(brcm_legacy_fcs_netdev_ops);
author maintainer
#1018 lore raw

PATCH 1017/1250 net: hso: fix TIOCMIWAIT race

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit 00f9fbc12320253bfc576fb7539d860029c82d0f upstream.

The task state must be updated before checking the wakeup condition to avoid missing a racing modem status update.

Fixes 542f54823614 ("tty: Modem functions for the HSO driver")
Cc [email protected] # 2.6.29
---
 drivers/net/usb/hso.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/usb/hso.c +1 -1
@@ -1549,10 +1549,10 @@ hso_wait_modem_status(struct hso_serial
 	spin_unlock_irq(&serial->serial_lock);
 	add_wait_queue(&tiocmget->waitq, &wait);
 	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
 		spin_lock_irq(&serial->serial_lock);
 		memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
 		spin_unlock_irq(&serial->serial_lock);
-		set_current_state(TASK_INTERRUPTIBLE);
 		if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
 		    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
 		    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd))) {
author maintainer
#1019 lore raw

PATCH 1018/1250 net: mana: Reserve extra CQ slot for the fence completion CQE

In reply to Greg Kroah-Hartman

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

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

From: Sahil Chandna <[email protected]>

commit 80dd7e754b3aa9637a0758ad93fa209f9650ec48 upstream.

The RX completion queue is sized to hold exactly one CQE per posted RX WQE. MANA_FENCE_RQ makes hardware post an additional CQE_RX_OBJECT_FENCE after the packet CQEs. The current sizing reserves no extra slot for it and in rare cases, CQ has no guaranteed slot for the fence CQE when it is full of packet CQEs. This can lead to dropping the fence completion while the driver waits holding RTNL lock throughout the timeout duration. Reserve one extra CQE slot for CQE_RX_OBJECT_FENCE. mana_gd_alloc_memory() requires queue_size to be a power-of-two and at least MANA_PAGE_SIZE; the reservation pushes cq_size past a power-of-two, so round up the CQ size in mana_create_rxq().

---
 drivers/net/ethernet/microsoft/mana/mana_en.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +5 -1
@@ -2616,6 +2616,10 @@ static int mana_alloc_rx_wqe(struct mana
 		*cq_size += COMP_ENTRY_SIZE;
 	}
 
+	/* Reserve an extra slot for Fence completion
+	 * event (CQE_RX_OBJECT_FENCE) in case RX CQ is full.
+	 */
+	*cq_size += COMP_ENTRY_SIZE;
 	return 0;
 }
 
@@ -2711,7 +2715,7 @@ static struct mana_rxq *mana_create_rxq(
 		goto out;
 
 	rq_size = MANA_PAGE_ALIGN(rq_size);
-	cq_size = MANA_PAGE_ALIGN(cq_size);
+	cq_size = MANA_PAGE_ALIGN(roundup_pow_of_two(cq_size));
 
 	/* Create RQ */
 	memset(&spec, 0, sizeof(spec));
author maintainer
#1020 lore raw

PATCH 1019/1250 net: mpls: clear inner_protocol when the last label is popped

In reply to Greg Kroah-Hartman

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

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

From: Fourie Zhang <[email protected]>

commit 78a86d75a70e1e227711c72865c59b1422d0a5ae upstream.

skb_mpls_push() records the pre-encapsulation network header once, gated on !skb->inner_protocol. skb_mpls_pop() never clears that record, so it outlives the encapsulation it describes.

Open vSwitch can then re-push MPLS onto a packet whose inner_network_header still points at the older, deeper offset: push a label, pop every label, recirculate (ovs_flow_key_update() re-derives key->eth.type and resets network_header, but leaves inner_*), then push again. ovs_fragment() trusts the record:

	skb->network_header = skb->inner_network_header;

so skb_network_offset() goes negative. The bound check is signed:

if (skb_network_offset(skb) > MAX_L2_LEN)

a negative offset passes it, and prepare_frag() widens the value:

	unsigned int hlen = skb_network_offset(skb);
	memcpy(&data->l2_data, skb->data, hlen);

which is a ~4GiB memcpy out of a 30-byte per-CPU buffer.

Reproduced on v7.3-rc1. RDX is the truncated length, (unsigned int)(-8):

  BUG: unable to handle page fault for address: ffffe8ffffc16000
  #PF: supervisor write access in kernel mode
  Oops: 0002 [#1] SMP KASAN NOPTI
  RIP: 0010:memcpy+0x8/0x20
  RDX: 00000000fffffff8 RSI: ffff888105d732db RDI: ffffe8ffffc16000
   prepare_frag+0x3df/0x4e0
   ovs_fragment+0x589/0x7e0
   do_output+0x4ce/0x5e0
   do_execute_actions+0x55d2/0x7b30
   ovs_execute_actions+0xea/0x450

Same root-cause shape as commit 975b5b067f52 ("ipv6: sr: restore network header before routing and forwarding"): a stale network header offset reaching a consumer that widens it. Here it originates in the MPLS push/pop path.

Clear inner_protocol once the packet is no longer MPLS, so a later push re-records the current header. net/sched/act_mpls.c is the only other skb_mpls_pop() caller and gets the same fix; sch_frag.c saves and restores inner_protocol around fragmentation in the same way OVS does.

---
 net/core/skbuff.c |    7 +++++++
 1 file changed, 7 insertions(+)
net/core/skbuff.c +7 -0
@@ -6624,6 +6624,13 @@ int skb_mpls_pop(struct sk_buff *skb, __
 	}
 	skb->protocol = next_proto;
 
+	/* The last label is gone, so the inner header recorded by
+	 * skb_mpls_push() no longer describes this packet. Drop it, or a
+	 * later push keeps the stale offset.
+	 */
+	if (!eth_p_mpls(next_proto))
+		skb->inner_protocol = 0;
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(skb_mpls_pop);
author maintainer
#1021 lore raw

PATCH 1020/1250 net: openvswitch: fix use-after-free of the flow table mask array

In reply to Greg Kroah-Hartman

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

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

From: Norbert Szetei <[email protected]>

commit ba4ba11ed6eb8972c69070417fc27b48deb002e8 upstream.

tbl_mask_array_realloc() retires the old mask_array before it stops being reachable:

	old = ovsl_dereference(tbl->mask_array);
	if (old) {
		...
		call_rcu(&old->rcu, mask_array_rcu_cb);
	}

	rcu_assign_pointer(tbl->mask_array, new);

call_rcu() only waits for read-side critical sections already in flight. tbl->mask_array still points at old between the call_rcu() and the rcu_assign_pointer(), so a reader entering ovs_flow_tbl_lookup_stats() in that window picks up old in a fresh critical section that the pending grace period does not cover.

tbl_mask_array_realloc() runs in process context under ovs_mutex, so the window is preemptible and can outlast the grace period. Then mask_array_rcu_cb() frees old before the swap runs:

  BUG: KASAN: slab-use-after-free in flow_lookup.constprop.0+0x2bf/0x2f0
  Read of size 8 at addr ffff888020b3e018 by task poc/741
   flow_lookup.constprop.0+0x2bf/0x2f0
   ovs_flow_tbl_lookup_stats+0x4a3/0x5c0
   ovs_dp_process_packet+0x19c/0x710
   ovs_vport_receive+0x243/0x390
   internal_dev_xmit+0x81/0x170
  Freed by task 728:
   kfree+0x16a/0x4e0
   rcu_core+0x853/0x1030

Publish the new array before retiring the old one. The kfree_rcu() that call_rcu() replaced ran after the swap.

---
 net/openvswitch/flow_table.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
net/openvswitch/flow_table.c +3 -1
@@ -258,11 +258,13 @@ static int tbl_mask_array_realloc(struct
 			if (ovsl_dereference(old->masks[i]))
 				new->masks[new->count++] = old->masks[i];
 		}
-		call_rcu(&old->rcu, mask_array_rcu_cb);
 	}
 
 	rcu_assign_pointer(tbl->mask_array, new);
 
+	if (old)
+		call_rcu(&old->rcu, mask_array_rcu_cb);
+
 	return 0;
 }
 
author maintainer
#1022 lore raw

PATCH 1021/1250 net: phy: dp83td510: handle the active-high LED polarity mode

In reply to Greg Kroah-Hartman

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

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

From: Donggeun Yoo <[email protected]>

commit 6ca81bbc31cdc964e4b74d17b86215d4a810a56f upstream.

dp83td510_led_polarity_set() only recognizes PHY_LED_ACTIVE_LOW, so PHY_LED_ACTIVE_HIGH falls through to the default case and returns -EINVAL. of_phy_led() propagates the error, of_phy_leds() drops the LEDs registered so far and passes it on, and phy_probe() returns it. A device tree marking a DP83TD510 LED as 'active-high', which leds/common.yaml allows and ethernet-phy.yaml references for led@N nodes, thus leaves the mdio device unbound, so phy_attach_direct() falls back to the genphy driver, which cannot drive this 10BASE-T1L single-mode PHY, so the interface has no usable link.

The callback initializes polarity to DP83TD510E_LED_POLARITY(index), which is the active-high setting, so the request is already satisfied and only the case label is missing.

---
 drivers/net/phy/dp83td510.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/net/phy/dp83td510.c +3 -0
@@ -380,6 +380,9 @@ static int dp83td510_led_polarity_set(st
 		case PHY_LED_ACTIVE_LOW:
 			polarity = 0;
 			break;
+		case PHY_LED_ACTIVE_HIGH:
+			polarity = DP83TD510E_LED_POLARITY(index);
+			break;
 		default:
 			return -EINVAL;
 		}
author maintainer
#1023 lore raw

PATCH 1022/1250 netfs: Fix uninitialized return value in netfs_unbuffered_write()

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

commit f18e8774f4d3137fa0a5fb8ffa83d59a719666d8 upstream.

If preparation of the first subrequest fails, netfs_unbuffered_write() exits its loop before ret is initialized. The empty-iterator check can do the same.

For synchronous writes, netfs_unbuffered_write_iter_locked() may then return an unrelated error instead of wreq->error. This is reachable through CIFS if cifs_prepare_write() fails to reopen the file or obtain credits.

Initialize ret to 0 so the caller returns wreq->error if no data was written, or the number of bytes already written otherwise.

Found with Clang's -Wconditional-uninitialized.

---
 fs/netfs/direct_write.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/netfs/direct_write.c +1 -1
index c16fbad286a1..b04019097ab8 100644
@@ -95,7 +95,7 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq)
 {
 	struct netfs_io_subrequest *subreq = NULL;
 	struct netfs_io_stream *stream = &wreq->io_streams[0];
-	int ret;
+	int ret = 0;
 
 	_enter("%llx", wreq->len);
 
Signature · 1 line
2.55.0
author maintainer
#1024 lore raw

PATCH 1023/1250 netfilter: nf_log: unregister loggers before per-net teardown

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 2c018cc4842c33f0c732962e2ab58635e8ae5823 upstream.

nf_log_syslog and nfnetlink_log unregister their per-network namespace operations before unregistering their global logger backends. This leaves a window where a sysctl or netlink writer can rebind the still- registered logger after the per-net pre-exit callback cleared the old selection.

The race looks like this:

CPU 0 CPU 1
---- ----
unregister_pernet_subsys()

    nf_log_unset(net, logger)
      net->nf.nf_loggers[pf] = NULL

                                        lock nf_log_mutex
                                        find logger in loggers[][]
                                        net->nf.nf_loggers[pf] = logger
                                        unlock nf_log_mutex

nf_log_unregister(logger)
lock nf_log_mutex
loggers[pf][type] = NULL
unlock nf_log_mutex
synchronize_rcu()
module exit returns
module core frees backend memory

Later, a sysctl read or packet logging operation can dereference the stale per-net logger pointer.

Fix this by unregistering the global logger backends before tearing down per-net state. Once the global registrations are gone, later writers can no longer rebind the logger. unregister_pernet_subsys() already waits for an RCU grace period after the pre-exit callback clears the per-net selection, while nf_log_unregister() continues to cover readers of the global logger table.

Apply this ordering fix to both nf_log backends that combine per-net teardown with global logger registration.

Fixes 5b023fc8d8e0 ("netfilter: enable per netns support for nf_loggers")
---
 net/netfilter/nf_log_syslog.c |    2 +-
 net/netfilter/nfnetlink_log.c |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
net/netfilter/nf_log_syslog.c +1 -1
@@ -1065,12 +1065,12 @@ err1:
 
 static void __exit nf_log_syslog_exit(void)
 {
-	unregister_pernet_subsys(&nf_log_syslog_net_ops);
 	nf_log_unregister(&nf_ip_logger);
 	nf_log_unregister(&nf_arp_logger);
 	nf_log_unregister(&nf_ip6_logger);
 	nf_log_unregister(&nf_netdev_logger);
 	nf_log_unregister(&nf_bridge_logger);
+	unregister_pernet_subsys(&nf_log_syslog_net_ops);
 }
 
 module_init(nf_log_syslog_init);
net/netfilter/nfnetlink_log.c +1 -1
@@ -1228,8 +1228,8 @@ static void __exit nfnetlink_log_fini(vo
 {
 	nfnetlink_subsys_unregister(&nfulnl_subsys);
 	netlink_unregister_notifier(&nfulnl_rtnl_notifier);
-	unregister_pernet_subsys(&nfnl_log_net_ops);
 	nf_log_unregister(&nfulnl_logger);
+	unregister_pernet_subsys(&nfnl_log_net_ops);
 }
 
 MODULE_DESCRIPTION("netfilter userspace logging");
author maintainer
#1025 lore raw

PATCH 1024/1250 netfilter: report NLM_F_DUMP_FILTERED when all is filtered out

In reply to Greg Kroah-Hartman

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

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

From: Ilya Maximets <[email protected]>

commit 7a099b347fef536a84068076e2d384f044e5cfc5 upstream.

NLM_F_DUMP_FILTERED is only set on data elements in the conntrack dump. But when everything is filtered out it is confusing for the user space, since the flag is not reported anymore and it looks like the table was empty, which may or may not be the case.

'answer_flags' were introduced precisely for this use case, and the conntrack dump should set the flag in there in case the filtering was applied.

This is important, for example, to be able to tell if the filters are supported or not by the kernel without modifying the kernel state.

With the proper reporting of NLM_F_DUMP_FILTERED on NLMSG_DONE, an application in user space can just try and dump with an arbitrary filter without worrying that there could be no matching entry. The reported flag will signal that the filtering was applied and therefore supported.

Fixes cb8aa9a3affb ("netfilter: ctnetlink: add kernel side filtering for dump")
---
 net/netfilter/nf_conntrack_netlink.c |    2 ++
 1 file changed, 2 insertions(+)
net/netfilter/nf_conntrack_netlink.c +2 -0
@@ -1077,6 +1077,8 @@ static int ctnetlink_start(struct netlin
 	}
 
 	cb->data = filter;
+	if (filter)
+		cb->answer_flags = NLM_F_DUMP_FILTERED;
 	return 0;
 }
 
author maintainer
#1026 lore raw

PATCH 1025/1250 vdpa: ifcvf: Put device on unsupported feature error

In reply to Greg Kroah-Hartman

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

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

From: Xiong Weimin <[email protected]>

commit 4d470be71196ca0ce302e6623454533dc31b465b upstream.

Route unsupported provisioned features through the common error path after vdpa_alloc_device() so the allocated device and adapter pointer are released consistently.

Fixes 46fc0917bbab ("vDPA/ifcvf: implement features provisioning")

Message-ID: <[email protected]>

---
 drivers/vdpa/ifcvf/ifcvf_main.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/vdpa/ifcvf/ifcvf_main.c +2 -1
@@ -724,7 +724,8 @@ static int ifcvf_vdpa_dev_add(struct vdp
 		if (config->device_features & ~device_features) {
 			IFCVF_ERR(pdev, "The provisioned features 0x%llx are not supported by this device with features 0x%llx\n",
 				  config->device_features, device_features);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto err;
 		}
 		device_features &= config->device_features;
 	}
author maintainer
#1027 lore raw

PATCH 1026/1250 vdpa: solidrun: Free IRQs after request failure

In reply to Greg Kroah-Hartman

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

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

From: Xiong Weimin <[email protected]>

commit e847542ab0545c73354849126150206c29d83929 upstream.

Unwind IRQs already requested by snet_request_irqs() before returning a VQ IRQ request error so a later DRIVER_OK retry starts from a clean state. The IRQs are requested and freed while the PCI device remains bound, so the driver cannot wait for devres cleanup at detach time.

Fixes 51a8f9d7f587 ("virtio: vdpa: new SolidNET DPU driver.")

Message-ID: <[email protected]>

---
 drivers/vdpa/solidrun/snet_main.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/vdpa/solidrun/snet_main.c +5 -1
@@ -418,11 +418,15 @@ static int snet_request_irqs(struct pci_
 				       snet->vqs[i]->irq_name, snet->vqs[i]);
 		if (ret) {
 			SNET_ERR(pdev, "Failed to request IRQ\n");
-			return ret;
+			goto err_free_irqs;
 		}
 		snet->vqs[i]->irq = irq;
 	}
 	return 0;
+
+err_free_irqs:
+	snet_free_irqs(snet);
+	return ret;
 }
 
 static void snet_set_status(struct vdpa_device *vdev, u8 status)
author maintainer
#1028 lore raw

PATCH 1027/1250 scripts/sorttable: Mark long_size as __maybe_unused

In reply to Greg Kroah-Hartman

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

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

From: Nathan Chancellor <[email protected]>

commit 4f73462856576797b8f3c55564a9be99f76dc67b upstream.

When building in a kernel tree prior to commit b055f4c431e3 ("sorttable:
Move ELF parsing into scripts/elf-parse.[ch]") with clang-23 or newer, which implements a new warning under -Wunused-but-set-variable for static global variable, there is a warning from sorttable because long_size is unused when MCOUNT_SORT_ENABLED is not set:

scripts/sorttable.c:452:12: error: variable 'long_size' set but not used [-Werror,-Wunused-but-set-global]

    452 | static int long_size;
        |            ^

Mark long_size as __maybe_unused to avoid inserting more ugly #ifdef directives while insuring the warning does not reappear, as the aforementioned change does not alter the uses of long_size, so it appears to be coincidence that the warning disappears after this refactoring.

---
 scripts/sorttable.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/sorttable.c +1 -1
@@ -449,7 +449,7 @@ static inline void *get_index(void *star
 }
 
 static int extable_ent_size;
-static int long_size;
+static int long_size __maybe_unused;
 
 #define ERRSTR_MAXSZ	256
 
author maintainer
#1029 lore raw

PATCH 1028/1250 fs: autofs: fix memory leak in autofs_fill_super()

In reply to Greg Kroah-Hartman

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

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

From: Jeffin Philip <[email protected]>

commit 5ab54837fce04a1c9923d0bfd3d5de51fdc768b3 upstream.

In autofs_fill_super(), we create a new inode using autofs_new_ino(), however, if we fail to create root_inode, (that is, root_inode failure path), we return -ENOMEM without freeing the new inode(ino) that we created causing a memory leak. Fix this by adding autofs_free_ino() to free the inode we created in root_inode failure path before returning ENOMEM.

---
 fs/autofs/inode.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
fs/autofs/inode.c +3 -1
@@ -322,8 +322,10 @@ static int autofs_fill_super(struct supe
 		return -ENOMEM;
 
 	root_inode = autofs_get_inode(s, S_IFDIR | 0755);
-	if (!root_inode)
+	if (!root_inode) {
+		autofs_free_ino(ino);
 		return -ENOMEM;
+	}
 
 	root_inode->i_uid = ctx->uid;
 	root_inode->i_gid = ctx->gid;
author maintainer
#1030 lore raw

PATCH 1029/1250 erofs: preserve LZMA decoders on resize failure

In reply to Greg Kroah-Hartman

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

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

From: Nikhil Gurudasani <[email protected]>

commit 617d0d8d199ba1790c94310fd75a22d01c97a8d6 upstream.

The pool-resize path frees each stream's old decoder before allocating its replacement. If an allocation fails after some streams have already been replaced, the failed stream is put back on the list with state == NULL. z_erofs_lzma_max_dictsize is still advanced as if the whole pool had been resized.

An existing LZMA mount can select the broken stream and pass NULL to xz_dec_microlzma_reset(). A retry at the same size also skip another resize attempt. Since the global maximum was advanced, thus, the invalid state is left unrepaired.

Allocate each replacement before freeing the old decoder, temporarily retaining one old decoder during allocation. Stop at the first failure and advance z_erofs_lzma_max_dictsize only after all streams satisfy the request.

Record each stream's dictionary capacity so retries can skip streams already enlarged before a partial failure.

Fixes 622ceaddb764 ("erofs: lzma compression support")
---
 fs/erofs/decompressor_lzma.c |   18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
fs/erofs/decompressor_lzma.c +14 -4
@@ -5,6 +5,7 @@
 struct z_erofs_lzma {
 	struct z_erofs_lzma *next;
 	struct xz_dec_microlzma *state;
+	unsigned int dict_size;
 	u8 bounce[PAGE_SIZE];
 };
 
@@ -128,11 +129,19 @@ again:
 	err = 0;
 	/* 2. walk each isolated stream and grow max dict_size if needed */
 	for (strm = head; strm; strm = strm->next) {
+		struct xz_dec_microlzma *state;
+
+		if (strm->dict_size >= dict_size)
+			continue;
+		state = xz_dec_microlzma_alloc(XZ_PREALLOC, dict_size);
+		if (!state) {
+			err = -ENOMEM;
+			break;
+		}
 		if (strm->state)
 			xz_dec_microlzma_end(strm->state);
-		strm->state = xz_dec_microlzma_alloc(XZ_PREALLOC, dict_size);
-		if (!strm->state)
-			err = -ENOMEM;
+		strm->state = state;
+		strm->dict_size = dict_size;
 	}
 
 	/* 3. push back all to the global list and update max dict_size */
@@ -142,7 +151,8 @@ again:
 	spin_unlock(&z_erofs_lzma_lock);
 	wake_up_all(&z_erofs_lzma_wq);
 
-	z_erofs_lzma_max_dictsize = dict_size;
+	if (!err)
+		z_erofs_lzma_max_dictsize = dict_size;
 	mutex_unlock(&lzma_resize_mutex);
 	return err;
 }
author maintainer
#1031 lore raw

PATCH 1030/1250 fbdev: vfb: defer cleanup until the last reference

In reply to Greg Kroah-Hartman

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

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

From: Weiming Shi <[email protected]>

commit a0a34a40ed299c9c7cff6af163a5b883ee9d6d73 upstream.

FBIOGETCMAP takes a shallow snapshot of info->cmap and performs the usercopy after dropping info->lock. vfb_remove() frees the colormap immediately after unregistering the framebuffer, even when an open file still holds a reference to fb_info. A concurrent driver unbind can therefore free the colormap while the ioctl copies it to userspace.

KASAN reports:

  BUG: KASAN: slab-use-after-free in _copy_to_user
  Read of size 512 by task poc/125

   _copy_to_user (./include/linux/instrumented.h:129 ./include/linux/uaccess.h:201 lib/usercopy.c:24)
   fb_cmap_to_user (./include/linux/uaccess.h:230 drivers/video/fbdev/core/fbcmap.c:211)
   do_fb_ioctl (drivers/video/fbdev/core/fb_chrdev.c:114)

   Allocated by task 1:
   fb_alloc_cmap_gfp (./include/linux/slab.h:973 ./include/linux/slab.h:1290 drivers/video/fbdev/core/fbcmap.c:108)
   vfb_probe (drivers/video/fbdev/vfb.c:459)

   Freed by task 124:
   fb_dealloc_cmap (drivers/video/fbdev/core/fbcmap.c:151)
   vfb_remove (drivers/video/fbdev/vfb.c:489)

unregister_framebuffer() drops the registration reference, and fbdev calls fb_destroy after the last put_fb_info(). Move the registered framebuffer's cleanup into an fb_destroy callback so its colormap and screen buffer stay alive until all file references have been released.

---
 drivers/video/fbdev/vfb.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/video/fbdev/vfb.c +8 -3
@@ -78,6 +78,13 @@ static int vfb_pan_display(struct fb_var
 static int vfb_mmap(struct fb_info *info,
 		    struct vm_area_struct *vma);
 
+static void vfb_destroy(struct fb_info *info)
+{
+	vfree(info->screen_buffer);
+	fb_dealloc_cmap(&info->cmap);
+	framebuffer_release(info);
+}
+
 static const struct fb_ops vfb_ops = {
 	.owner		= THIS_MODULE,
 	__FB_DEFAULT_SYSMEM_OPS_RDWR,
@@ -87,6 +94,7 @@ static const struct fb_ops vfb_ops = {
 	.fb_pan_display	= vfb_pan_display,
 	__FB_DEFAULT_SYSMEM_OPS_DRAW,
 	.fb_mmap	= vfb_mmap,
+	.fb_destroy	= vfb_destroy,
 };
 
     /*
@@ -485,9 +493,6 @@ static void vfb_remove(struct platform_d
 
 	if (info) {
 		unregister_framebuffer(info);
-		vfree(videomemory);
-		fb_dealloc_cmap(&info->cmap);
-		framebuffer_release(info);
 	}
 }
 
author maintainer
#1032 lore raw

PATCH 1031/1250 inet: frags: invalidate queues before flushing them

In reply to Greg Kroah-Hartman

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

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

From: Yilin Zhang <[email protected]>

commit b824476c56a153934c67c9e0f873e1fd967743d6 upstream.

fqdir_pre_exit() flushes the skbs from incomplete queues without changing their completion state. A fragment which found a queue before high_thresh was cleared can then acquire the queue lock and reuse stale reassembly metadata. A queue concurrently killed after fqdir->dead is set can instead become INET_FRAG_COMPLETE|INET_FRAG_HASH_DEAD while still holding its old skbs; skipping it because it is complete leaves those references behind until asynchronous fqdir teardown.

For IPv6, stale metadata can make ip6_frag_reasm() use the old nhoffset with a new skb and access memory out of bounds. The resulting heap corruption can be leveraged for local privilege escalation when unprivileged network namespaces are available. Unflushed fragments can also keep conntrack references alive after the conntrack per-net cleanup point.

Kill each incomplete queue, then flush every queue still owned by the dying rhashtable. HASH_DEAD identifies that ownership, while complete queues without it are already owned by another destroy path and must be left alone. Releasing a timer reference removed by inet_frag_kill() is deferred to inet_frag_putn(), after the queue lock is dropped.

KASAN report:

  BUG: KASAN: slab-out-of-bounds in ipv6_frag_rcv (net/ipv6/reassembly.c:289 (discriminator 2) net/ipv6/reassembly.c:229 (discriminator 2) net/ipv6/reassembly.c:391 (discriminator 2))
  Write of size 1 at addr ff110001039c6e00 by task poc/771
  Call Trace:
  ? ipv6_frag_rcv (net/ipv6/reassembly.c:289 (discriminator 2) net/ipv6/reassembly.c:229 (discriminator 2) net/ipv6/reassembly.c:391 (discriminator 2))
  ipv6_frag_rcv (net/ipv6/reassembly.c:289 (discriminator 2) net/ipv6/reassembly.c:229 (discriminator 2) net/ipv6/reassembly.c:391 (discriminator 2))
  ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:479 (discriminator 5))
  ip6_input_finish (net/ipv6/ip6_input.c:534)
  ipv6_rcv (include/net/dst.h:480 (discriminator 3) net/ipv6/ip6_input.c:119 (discriminator 3) net/ipv6/ip6_input.c:109 (discriminator 3) include/linux/netfilter.h:325 (discriminator 3) include/linux/netfilter.h:319 (discriminator 3) net/ipv6/ip6_input.c:351 (discriminator 3))
  packet_sendmsg (net/packet/af_packet.c:3110 net/packet/af_packet.c:3142)
  __x64_sys_sendmmsg (net/socket.c:2883 net/socket.c:2880 net/socket.c:2880)
  The buggy address belongs to the object at ff110001039c6b40
  which belongs to the cache skbuff_small_head of size 704
  The buggy address is located 0 bytes to the right of
  allocated 704-byte region [ff110001039c6b40, ff110001039c6e00)

  BUG: KASAN: slab-out-of-bounds in ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:423 (discriminator 1))
  Read of size 1 at addr ff110001039c6e08 by task poc/771
  Call Trace:
  ? ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:423 (discriminator 1))
  ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:423 (discriminator 1))
  ip6_input_finish (net/ipv6/ip6_input.c:534)
  ipv6_rcv (include/net/dst.h:480 (discriminator 3) net/ipv6/ip6_input.c:119 (discriminator 3) net/ipv6/ip6_input.c:109 (discriminator 3) include/linux/netfilter.h:325 (discriminator 3) include/linux/netfilter.h:319 (discriminator 3) net/ipv6/ip6_input.c:351 (discriminator 3))
  packet_sendmsg (net/packet/af_packet.c:3110 net/packet/af_packet.c:3142)
  __x64_sys_sendmmsg (net/socket.c:2883 net/socket.c:2880 net/socket.c:2880)
  packet_sendmsg (net/packet/af_packet.c:2959 net/packet/af_packet.c:3053 net/packet/af_packet.c:3142)
  __x64_sys_sendmmsg (net/socket.c:2883 net/socket.c:2880 net/socket.c:2880)
  The buggy address belongs to the object at ff110001039c6b40
  which belongs to the cache skbuff_small_head of size 704
  The buggy address is located 8 bytes to the right of
  allocated 704-byte region [ff110001039c6b40, ff110001039c6e00)
---
 net/ipv4/inet_fragment.c |    6 ++++++
 1 file changed, 6 insertions(+)
net/ipv4/inet_fragment.c +6 -0
@@ -237,6 +237,8 @@ void fqdir_pre_exit(struct fqdir *fqdir)
 	rhashtable_walk_start(&hti);
 
 	while ((fq = rhashtable_walk_next(&hti))) {
+		int refs = 0;
+
 		if (IS_ERR(fq)) {
 			if (PTR_ERR(fq) != -EAGAIN)
 				break;
@@ -244,8 +246,12 @@ void fqdir_pre_exit(struct fqdir *fqdir)
 		}
 		spin_lock_bh(&fq->lock);
 		if (!(fq->flags & INET_FRAG_COMPLETE))
+			inet_frag_kill(fq, &refs);
+
+		if (fq->flags & INET_FRAG_HASH_DEAD)
 			inet_frag_queue_flush(fq, 0);
 		spin_unlock_bh(&fq->lock);
+		inet_frag_putn(fq, refs);
 	}
 
 	rhashtable_walk_stop(&hti);
author maintainer
#1033 lore raw

PATCH 1032/1250 ieee802154: 6lowpan: fix NULL dereference in lowpan_newlink

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit bf79662bc85e820ac3b846e2f347da29fbf6ac95 upstream.

TUNSETLINK allows a TUN device to change its link-layer type to ARPHRD_IEEE802154 without initializing ieee802154_ptr. lowpan_newlink() checks only the device type before dereferencing the pointer, so an RTM_NEWLINK request can trigger a NULL pointer dereference.

Reject devices without ieee802154_ptr along with devices of the wrong type.

---
 net/ieee802154/6lowpan/core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ieee802154/6lowpan/core.c +1 -1
@@ -150,7 +150,7 @@ static int lowpan_newlink(struct net_dev
 	wdev = dev_get_by_index(dev_net(ldev), nla_get_u32(tb[IFLA_LINK]));
 	if (!wdev)
 		return -ENODEV;
-	if (wdev->type != ARPHRD_IEEE802154) {
+	if (wdev->type != ARPHRD_IEEE802154 || !wdev->ieee802154_ptr) {
 		dev_put(wdev);
 		return -EINVAL;
 	}
author maintainer
#1034 lore raw

PATCH 1033/1250 ieee802154: cc2520: fix FIFOP work use-after-free

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit ff5891b266a7fc6a062710836be84f1cc19338b5 upstream.

The FIFOP interrupt handler queues cc2520_fifop_irqwork. On removal, cc2520_remove() only flushes the work. The devm-managed FIFOP IRQ remains active until after ->remove() returns and can queue the work again after that flush, allowing it to run after the private data is released.

Disable the work with disable_work_sync() instead of flushing it, so the handler can no longer queue it once removal begins. Destroy the buffer mutex last, since the worker and the stop callback invoked through ieee802154_unregister_hw() both take it.

Found by an in-house static analysis tool.

Fixes 0da6bc8cc341 ("ieee802154: cc2520: adds driver for TI CC2520 radio")
Cc [email protected] # v6.10+
Assisted-by Codex:gpt-5.6
---
 drivers/net/ieee802154/cc2520.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/net/ieee802154/cc2520.c +2 -3
@@ -1156,11 +1156,10 @@ static void cc2520_remove(struct spi_dev
 {
 	struct cc2520_private *priv = spi_get_drvdata(spi);
 
-	mutex_destroy(&priv->buffer_mutex);
-	flush_work(&priv->fifop_irqwork);
-
+	disable_work_sync(&priv->fifop_irqwork);
 	ieee802154_unregister_hw(priv->hw);
 	ieee802154_free_hw(priv->hw);
+	mutex_destroy(&priv->buffer_mutex);
 }
 
 static const struct spi_device_id cc2520_ids[] = {
author maintainer
#1035 lore raw

PATCH 1034/1250 ieee802154: hwsim: serialize pib updates to fix double-free

In reply to Greg Kroah-Hartman

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

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

From: David Carlier <[email protected]>

commit 979d5b8de8ed4e1f997aef12da5694b99be7b871 upstream.

hwsim_update_pib() does an unserialized read-swap-free of phy->pib:

	pib_old = rtnl_dereference(phy->pib);
	...
	rcu_assign_pointer(phy->pib, pib);
	kfree_rcu(pib_old, rcu);

It assumes the RTNL is held, but ->set_channel is not always called under it: the mac802154 scan worker changes channels via drv_set_channel() without the RTNL. Such an update can race an RTNL-held one on the same phy; both read the same pib_old and both kfree_rcu() it, double-freeing the object. With SLUB percpu sheaves batching kfree_rcu(), this surfaces as a KASAN invalid-free in rcu_free_sheaf().

struct hwsim_phy has no lock for pib. Add one and make the swap atomic with rcu_replace_pointer() under it, dropping the misleading rtnl_dereference().

---
 drivers/net/ieee802154/mac802154_hwsim.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/net/ieee802154/mac802154_hwsim.c +7 -3
@@ -72,6 +72,8 @@ struct hwsim_phy {
 	struct ieee802154_hw *hw;
 	u32 idx;
 
+	/* Serializes phy->pib_updates. */
+	spinlock_t pib_lock;
 	struct hwsim_pib __rcu *pib;
 
 	bool suspended;
@@ -102,8 +104,6 @@ static int hwsim_update_pib(struct ieee8
 	if (!pib)
 		return -ENOMEM;
 
-	pib_old = rtnl_dereference(phy->pib);
-
 	pib->page = page;
 	pib->channel = channel;
 	pib->filt.short_addr = filt->short_addr;
@@ -112,7 +112,10 @@ static int hwsim_update_pib(struct ieee8
 	pib->filt.pan_coord = filt->pan_coord;
 	pib->filt_level = filt_level;
 
-	rcu_assign_pointer(phy->pib, pib);
+	spin_lock_bh(&phy->pib_lock);
+	pib_old = rcu_replace_pointer(phy->pib, pib,
+				      lockdep_is_held(&phy->pib_lock));
+	spin_unlock_bh(&phy->pib_lock);
 	kfree_rcu(pib_old, rcu);
 	return 0;
 }
@@ -952,6 +955,7 @@ static int hwsim_add_one(struct genl_inf
 		goto err_pib;
 	}
 
+	spin_lock_init(&phy->pib_lock);
 	pib->channel = 13;
 	pib->filt.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
 	pib->filt.pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
author maintainer
#1036 lore raw

PATCH 1035/1250 ipv4: fib: bound automatic table ID allocation

In reply to Greg Kroah-Hartman

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

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

From: Zihan Xi <[email protected]>

commit efdfb1e27a3328085b79540dfe781d537b576ea1 upstream.

fib_empty_table() probes every table ID from 1 until it finds a free one. IPv4 tables are stored in a 256-bucket hash table, so a dense set of IDs makes each probe walk a growing hash chain while RTNL is held.

Automatic table assignment ("ip rule ... table 0") is an IPv4-only legacy path. Bound the automatically allocated ID to 4096 so the RTNL hold stays bounded, without changing lookups of explicitly specified table IDs.

This changes user-visible behavior. A table-0 rule previously received the lowest free ID in 1..RT_TABLE_MAX (0xFFFFFFFF). After this patch the search stops at 4096 and the rule add fails with ENOBUFS if that range is fully occupied. Explicit table IDs above 4096 remain usable.

The automatic path is unused in practice: it is IPv4-only, not documented by ip-rule, uncovered by kernel selftests, and both NetworkManager and systemd refuse table 0.

---
 net/ipv4/fib_rules.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
net/ipv4/fib_rules.c +3 -1
@@ -214,6 +214,8 @@ INDIRECT_CALLABLE_SCOPE int fib4_rule_ma
 	return 1;
 }
 
+#define FIB_MAX_AUTO_TABLE_ID  4096
+
 static struct fib_table *fib_empty_table(struct net *net)
 {
 	u32 id = 1;
@@ -222,7 +224,7 @@ static struct fib_table *fib_empty_table
 		if (!fib_get_table(net, id))
 			return fib_new_table(net, id);
 
-		if (id++ == RT_TABLE_MAX)
+		if (id++ == FIB_MAX_AUTO_TABLE_ID)
 			break;
 	}
 	return NULL;
author maintainer
#1037 lore raw

PATCH 1036/1250 ipv6: flowlabel: cap duplicate leases per socket

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit 8d6cd188508513503805c156165de38e4e4a8615 upstream.

ipv6_flowlabel_get() allocates an ipv6_fl_socklist entry for every successful GET. The recheck path for a compatible existing flowlabel links another lease without applying any lease admission check. Repeated GET requests for one shareable label can therefore grow a socket's lease list without bound.

Reject a new unprivileged lease once the socket already holds FL_MAX_PER_SOCK leases. Check this on the shared recheck path so reuse of a globally interned label, including the fl_intern() collision path, is covered as well. New-label admission remains under the existing mem_check() policy.

Use capable(CAP_NET_ADMIN) rather than ns_capable(), matching mem_check(). An unprivileged user must not bypass the cap by creating a user namespace and a netns where they have CAP_NET_ADMIN, which would still consume host memory.

Check the capability only when the socket reaches the limit, so successful unprivileged GET requests below the cap do not generate a capability audit. Do the admission check before updating linger and expires so a rejected GET does not refresh the shared label, matching the existing socket-list allocation failure path.

---
 net/ipv6/ip6_flowlabel.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
net/ipv6/ip6_flowlabel.c +19 -0
@@ -462,6 +462,21 @@ done:
 	return NULL;
 }
 
+static bool fl_sock_at_lease_limit(const struct sock *sk)
+{
+	const struct ipv6_fl_socklist *sfl;
+	int count = 0;
+
+	rcu_read_lock();
+	for_each_sk_fl_rcu(sk, sfl) {
+		if (++count >= FL_MAX_PER_SOCK)
+			break;
+	}
+	rcu_read_unlock();
+
+	return count >= FL_MAX_PER_SOCK;
+}
+
 static int mem_check(struct sock *sk)
 {
 	int room = FL_MAX_SIZE - atomic_read(&fl_size);
@@ -671,6 +686,10 @@ recheck:
 			err = -ENOMEM;
 			if (!sfl1)
 				goto release;
+			err = -ENOBUFS;
+			if (fl_sock_at_lease_limit(sk) &&
+			    !capable(CAP_NET_ADMIN))
+				goto release;
 			if (fl->linger > fl1->linger)
 				fl1->linger = fl->linger;
 			if ((long)(fl->expires - fl1->expires) > 0)
author maintainer
#1038 lore raw

PATCH 1037/1250 ipvs: reject invalid states in connection template sync records

In reply to Greg Kroah-Hartman

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

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

From: Kyle Zeng <[email protected]>

commit 74cb39735b6cd0aff4b5584158f09376fd97aadf upstream.

IPVS sync receivers validate protocol states before creating or updating a connection. For connection templates, however, they only log states outside the template state range and still store the value in the connection.

A template can be returned by ordinary connection lookup. TCP and SCTP then use the invalid state as an index into their transition tables.

Reject invalid template states in both sync protocol versions before looking up or modifying a connection. The version 1 path handles both IPv4 and IPv6 records.

Fixes 275411430f89 ("ipvs: add assured state for conn templates")
Assisted-by Codex:gpt-5.6-sol
---
 net/netfilter/ipvs/ip_vs_sync.c |   16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
net/netfilter/ipvs/ip_vs_sync.c +8 -8
@@ -1000,10 +1000,10 @@ static void ip_vs_process_message_v0(str
 					pp->name, state);
 				continue;
 			}
-		} else {
-			if (state >= IP_VS_CTPL_S_LAST)
-				IP_VS_DBG(7, "BACKUP v0, Invalid tpl state %u\n",
-					  state);
+		} else if (state >= IP_VS_CTPL_S_LAST) {
+			IP_VS_DBG(7, "BACKUP v0, Invalid tpl state %u\n",
+				  state);
+			continue;
 		}
 
 		ip_vs_conn_fill_param(ipvs, AF_INET, s->protocol,
@@ -1160,10 +1160,10 @@ static inline int ip_vs_proc_sync_conn(s
 			retc = 40;
 			goto out;
 		}
-	} else {
-		if (state >= IP_VS_CTPL_S_LAST)
-			IP_VS_DBG(7, "BACKUP, Invalid tpl state %u\n",
-				  state);
+	} else if (state >= IP_VS_CTPL_S_LAST) {
+		IP_VS_DBG(7, "BACKUP, Invalid tpl state %u\n", state);
+		retc = 40;
+		goto out;
 	}
 	if (ip_vs_conn_fill_param_sync(ipvs, af, s, &param, pe_data,
 				       pe_data_len, pe_name, pe_name_len)) {
author maintainer
#1039 lore raw

PATCH 1038/1250 mac802154: fix use-after-free of sdata via queued RX frames

In reply to Greg Kroah-Hartman

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

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

From: Ibrahim Hashimov <[email protected]>

commit 2f37fba846c9fdff5fc15b6d93656057ccd13031 upstream.

The RX softirq producer ieee802154_subif_frame() queues received beacon and MAC-command frames onto local->rx_beacon_list / rx_mac_cmd_list and schedules a process-context worker, storing a raw mac_pkt->sdata (and skb->dev == sdata->dev) with neither a reference nor any locking:

- the lists have no lock: the softirq producer list_add_tail()s while the mac_wq worker list_del()s, so sibling interfaces on the same phy corrupt the list;

- the workers dereference the interface after it may have been freed. mac802154_rx_mac_cmd_worker() touches mac_pkt->sdata directly, and mac802154_rx_beacon_worker() -> mac802154_process_beacon() dereferences skb->dev (== sdata->dev). Removing an interface frees its sdata (netdev_priv) while a queued frame still points at it, so a later worker run is a use-after-free.

Reproduced under KASAN by flooding a victim interface with MAC command frames and removing it (the beacon path is the same class via skb->dev):

  BUG: KASAN: slab-use-after-free in mac802154_rx_mac_cmd_worker+0x463/0x630 [mac802154]
  Read of size 4 at addr ffff888002f9ea18 by task kworker/u8:1/31
  Workqueue: phy0-mac-cmds mac802154_rx_mac_cmd_worker [mac802154]
  Call Trace:
   mac802154_rx_mac_cmd_worker+0x463/0x630 [mac802154]
   process_one_work+0x611/0xe80
   worker_thread+0x52e/0xdc0
   kthread+0x30c/0x630
   ret_from_fork+0x2fd/0x3e0

Fix both lists together:

- add local->rx_lock and take it around every list access: the softirq producer (plain spin_lock, softirq context) and the workers and flush (spin_lock_bh, process context);

- pin the interface for the lifetime of a queued frame with netdev_hold()/netdev_put(), so the worker can safely dereference sdata / skb->dev even while the interface is being removed;

- dequeue under the lock at the head and loop-drain the whole list in the workers (they previously processed one frame per run and relied on a later enqueue to drain the rest);

- drop not-yet-started frames of an interface before it is unregistered, from ieee802154_if_remove() (after the RCU grace period) and from the ieee802154_remove_interfaces() loop -- the latter is the whole-phy teardown path, which does not go through ieee802154_if_remove().

An in-flight worker that already dequeued a frame keeps its own netdev reference; unregister_netdevice() then waits it out in netdev_run_todo(), which runs at rtnl_unlock() (rtnl released) and after the interface has been closed, so it does not pin rtnl. A worker blocked in an association TX only delays that one interface's unregister (the usual "waiting for %s to become free"), it does not hold rtnl. netdev_hold() is used for this reason instead of a cancel_work_sync() under rtnl, which would block on the worker's unbounded MLME TX wait via ieee802154_sync_queue().

The mac-command worker additionally skips processing for a stopped interface (ieee802154_sdata_running()), avoiding a needless association response during teardown.

Fixes 57588c71177f ("mac802154: Handle passive scanning")
Assisted-by AuditCode-AI:2026.07
---
 include/net/cfg802154.h      |    1 
 net/mac802154/ieee802154_i.h |    8 ++
 net/mac802154/iface.c        |    6 ++
 net/mac802154/main.c         |    1 
 net/mac802154/rx.c           |  120 +++++++++++++++++++++++++++++++++++--------
 net/mac802154/scan.c         |   10 +--
 6 files changed, 117 insertions(+), 29 deletions(-)
include/net/cfg802154.h +1 -0
@@ -376,6 +376,7 @@ struct cfg802154_mac_pkt {
 	struct list_head node;
 	struct sk_buff *skb;
 	struct ieee802154_sub_if_data *sdata;
+	netdevice_tracker dev_tracker;
 	u8 page;
 	u8 channel;
 };
net/mac802154/ieee802154_i.h +8 -0
@@ -74,6 +74,10 @@ struct ieee802154_local {
 	struct work_struct rx_beacon_work;
 	struct list_head rx_mac_cmd_list;
 	struct work_struct rx_mac_cmd_work;
+	/* Serializes rx_beacon_list and rx_mac_cmd_list against the RX
+	 * softirq producer, the mac_wq workers and the teardown flush.
+	 */
+	spinlock_t rx_lock;
 
 	/* Association */
 	struct ieee802154_pan_device *assoc_dev;
@@ -300,6 +304,10 @@ static inline bool mac802154_is_beaconin
 }
 
 void mac802154_rx_mac_cmd_worker(struct work_struct *work);
+void mac802154_flush_list(struct list_head *list,
+			  struct ieee802154_sub_if_data *sdata);
+void mac802154_flush_queued_pkts(struct ieee802154_local *local,
+				 struct ieee802154_sub_if_data *sdata);
 
 int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
 				  struct ieee802154_pan_device *coord,
net/mac802154/iface.c +6 -0
@@ -694,6 +694,7 @@ void ieee802154_if_remove(struct ieee802
 	mutex_unlock(&sdata->local->iflist_mtx);
 
 	synchronize_rcu();
+	mac802154_flush_queued_pkts(sdata->local, sdata);
 	unregister_netdevice(sdata->dev);
 }
 
@@ -705,6 +706,11 @@ void ieee802154_remove_interfaces(struct
 	list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
 		list_del_rcu(&sdata->list);
 
+		/* Best-effort: a frame the RX softirq queues for this sdata
+		 * after the flush still pins the netdev, so the
+		 * unregister_netdevice() below waits it out.
+		 */
+		mac802154_flush_queued_pkts(local, sdata);
 		unregister_netdevice(sdata->dev);
 	}
 	mutex_unlock(&local->iflist_mtx);
net/mac802154/main.c +1 -0
@@ -91,6 +91,7 @@ ieee802154_alloc_hw(size_t priv_data_len
 	INIT_LIST_HEAD(&local->interfaces);
 	INIT_LIST_HEAD(&local->rx_beacon_list);
 	INIT_LIST_HEAD(&local->rx_mac_cmd_list);
+	spin_lock_init(&local->rx_lock);
 	mutex_init(&local->iflist_mtx);
 
 	tasklet_setup(&local->tasklet, ieee802154_tasklet_handler);
net/mac802154/rx.c +98 -22

161 lines not on this page · raw message

net/mac802154/scan.c +3 -7
@@ -104,13 +104,9 @@ static unsigned int mac802154_scan_get_c
 
 static void mac802154_flush_queued_beacons(struct ieee802154_local *local)
 {
-	struct cfg802154_mac_pkt *mac_pkt, *tmp;
-
-	list_for_each_entry_safe(mac_pkt, tmp, &local->rx_beacon_list, node) {
-		list_del(&mac_pkt->node);
-		kfree_skb(mac_pkt->skb);
-		kfree(mac_pkt);
-	}
+	spin_lock_bh(&local->rx_lock);
+	mac802154_flush_list(&local->rx_beacon_list, NULL);
+	spin_unlock_bh(&local->rx_lock);
 }
 
 static void
author maintainer
#1040 lore raw

PATCH 1039/1250 KVM: PPC: Book3S HV: Set irqfd->producer only on success

In reply to Greg Kroah-Hartman

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

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

From: leixiang <[email protected]>

commit 1144454ea22290d7c6998a2af6239e5995476afc upstream.

Set irqfd->producer only after kvmppc_set_passthru_irq() succeeds to avoid leaving a dangling pointer on failure. The bypass manager does not register a failed producer, so the pointer is never cleared.

---
 arch/powerpc/kvm/book3s_hv.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/powerpc/kvm/book3s_hv.c +2 -2
@@ -6125,12 +6125,12 @@ static int kvmppc_irq_bypass_add_produce
 	struct kvm_kernel_irqfd *irqfd =
 		container_of(cons, struct kvm_kernel_irqfd, consumer);
 
-	irqfd->producer = prod;
-
 	ret = kvmppc_set_passthru_irq(irqfd->kvm, prod->irq, irqfd->gsi);
 	if (ret)
 		pr_info("kvmppc_set_passthru_irq (irq %d, gsi %d) fails: %d\n",
 			prod->irq, irqfd->gsi, ret);
+	else
+		irqfd->producer = prod;
 
 	return ret;
 }
author maintainer
#1041 lore raw

PATCH 1040/1250 iommu/s390: Fix NULL dereference in iova_to_phys() with ZPCI_TABLE_TYPE_RFX

In reply to Greg Kroah-Hartman

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

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

From: Niklas Schnelle <[email protected]>

commit 20db6573301e66cd65ebf6c130b6563c69374d9d upstream.

When using a 5-level translation table via ZPCI_TABLE_TYPE_RFX get_rso_from_iova() returns NULL when the region-first entry is invalid. Yet in get_rto_from_iova() the region-second origin rso is not checked to be non-NULL before accessing rso[rsx] leading to a NULL pointer dereference instead of a NULL return when iova_to_phys() is called on a unmapped IOVA. Fix this by adding the missing NULL check.

---
 drivers/iommu/s390-iommu.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/iommu/s390-iommu.c +2 -0
@@ -972,6 +972,8 @@ static unsigned long *get_rto_from_iova(
 	case ZPCI_TABLE_TYPE_RFX:
 	case ZPCI_TABLE_TYPE_RSX:
 		rso = get_rso_from_iova(domain, iova);
+		if (!rso)
+			return NULL;
 		rsx = calc_rsx(iova);
 		rse = READ_ONCE(rso[rsx]);
 		if (!reg_entry_isvalid(rse))
author maintainer
#1042 lore raw

PATCH 1041/1250 s390/qeth: allow bridgeport queries despite OS_MISMATCH

In reply to Greg Kroah-Hartman

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

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

From: Nagamani PV <[email protected]>

commit 74f27fc8642b7e8d139796f8c18ee46df393c2b2 upstream.

When HiperSockets interfaces on the same VCHID span different OS families, reads of the sysfs attributes bridge_role and bridge_state fail with -EPERM if bridge port ownership belongs to another OS family.

As a result, userspace tools such as 'lszdev -ii' cannot retrieve bridge_role and bridge_state, even though firmware returns valid bridge port data for QUERY_BRIDGE_PORTS requests.

The firmware reports IPA_RC_SBP_IQD_OS_MISMATCH (0x0010) to indicate that bridge port ownership belongs to a different OS family. For QUERY_BRIDGE_PORTS operations, firmware still returns valid bridge port data (role=none, state=inactive) together with a primary return code of 0x0000 (success).

Allow QUERY_BRIDGE_PORTS requests to return the bridge port data provided by the firmware despite OS_MISMATCH. To make the OS family mismatch visible to userspace, represent the firmware-reported role "none" as "none (OS family mismatch)" while preserving the reported bridge_state.

The behavior for non-QUERY bridge port commands is unchanged; SET operations continue to return -EPERM when another OS family owns the bridge port.

This restores readability of bridge_role and bridge_state.

---
 drivers/s390/net/qeth_l2.h      |    3 ++-
 drivers/s390/net/qeth_l2_main.c |   26 ++++++++++++++++++++++----
 drivers/s390/net/qeth_l2_sys.c  |    7 ++++++-
 3 files changed, 30 insertions(+), 6 deletions(-)
drivers/s390/net/qeth_l2.h +2 -1
@@ -13,7 +13,8 @@ extern const struct attribute_group *qet
 
 int qeth_bridgeport_query_ports(struct qeth_card *card,
 				enum qeth_sbp_roles *role,
-				enum qeth_sbp_states *state);
+				enum qeth_sbp_states *state,
+				bool *os_mismatch);
 int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role);
 int qeth_bridgeport_an_set(struct qeth_card *card, int enable);
 
drivers/s390/net/qeth_l2_main.c +22 -4
@@ -1159,7 +1159,7 @@ static void qeth_l2_setup_bridgeport_att
 		qeth_bridgeport_setrole(card, card->options.sbp.role);
 		/* Let the callback function refresh the stored role value. */
 		qeth_bridgeport_query_ports(card, &card->options.sbp.role,
-					    NULL);
+					    NULL, NULL);
 	}
 	if (card->options.sbp.hostnotification) {
 		if (qeth_bridgeport_an_set(card, 1))
@@ -1546,6 +1546,7 @@ struct _qeth_sbp_cbctl {
 		struct {
 			enum qeth_sbp_roles *role;
 			enum qeth_sbp_states *state;
+			bool *os_mismatch;
 		} qports;
 	} data;
 };
@@ -1722,10 +1723,19 @@ static int qeth_bridgeport_query_ports_c
 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
 	struct qeth_sbp_port_data *qports;
+	u16 sbp_rc;
 	int rc;
 
 	QETH_CARD_TEXT(card, 2, "brqprtcb");
-	rc = qeth_bridgeport_makerc(card, cmd);
+	sbp_rc = cmd->data.sbp.hdr.return_code;
+
+	/* on OS family mismatch, query still returns valid port data;
+	 * treat as success
+	 */
+	if (sbp_rc == IPA_RC_SBP_IQD_OS_MISMATCH && !cmd->hdr.return_code)
+		rc = 0;
+	else
+		rc = qeth_bridgeport_makerc(card, cmd);
 	if (rc)
 		return rc;
 
@@ -1741,6 +1751,9 @@ static int qeth_bridgeport_query_ports_c
 		if (cbctl->data.qports.state)
 			*cbctl->data.qports.state = qports->entry[0].state;
 	}
+	if (cbctl->data.qports.os_mismatch)
+		*cbctl->data.qports.os_mismatch =
+			(sbp_rc == IPA_RC_SBP_IQD_OS_MISMATCH);
 	return 0;
 }
 
@@ -1749,13 +1762,17 @@ static int qeth_bridgeport_query_ports_c
  * @card:			   qeth_card structure pointer.
  * @role:   Role of the port: 0-none, 1-primary, 2-secondary.
  * @state:  State of the port: 0-inactive, 1-standby, 2-active.
+ * @os_mismatch: if non-NULL, set to true when firmware reports
+ *		 OS family mismatch.
  *
  * Returns negative errno-compatible error indication or 0 on success.
  *
- * 'role' and 'state' are not updated in case of hardware operation failure.
+ * 'role', 'state' and 'os_mismatch' are not updated in case of
+ * hardware operation failure.
  */
 int qeth_bridgeport_query_ports(struct qeth_card *card,
-	enum qeth_sbp_roles *role, enum qeth_sbp_states *state)
+	enum qeth_sbp_roles *role, enum qeth_sbp_states *state,
+	bool *os_mismatch)
 {
 	struct qeth_cmd_buffer *iob;
 	struct _qeth_sbp_cbctl cbctl = {
@@ -1763,6 +1780,7 @@ int qeth_bridgeport_query_ports(struct q
 			.qports = {
 				.role = role,
 				.state = state,
+				.os_mismatch = os_mismatch,
 			},
 		},
 	};
drivers/s390/net/qeth_l2_sys.c +6 -1
@@ -15,6 +15,7 @@ static ssize_t qeth_bridge_port_role_sta
 {
 	struct qeth_card *card = dev_get_drvdata(dev);
 	enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE;
+	bool os_mismatch = false;
 	int rc = 0;
 	char *word;
 
@@ -25,7 +26,7 @@ static ssize_t qeth_bridge_port_role_sta
 	if (qeth_card_hw_is_reachable(card) &&
 					card->options.sbp.supported_funcs)
 		rc = qeth_bridgeport_query_ports(card,
-			&card->options.sbp.role, &state);
+			&card->options.sbp.role, &state, &os_mismatch);
 	if (!rc) {
 		if (show_state)
 			switch (state) {
@@ -52,6 +53,10 @@ static ssize_t qeth_bridge_port_role_sta
 		if (rc)
 			QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x",
 				card->options.sbp.role, state);
+		else if (!show_state &&
+			 card->options.sbp.role == QETH_SBP_ROLE_NONE &&
+			 os_mismatch)
+			rc = sysfs_emit(buf, "%s (OS family mismatch)\n", word);
 		else
 			rc = sysfs_emit(buf, "%s\n", word);
 	}
author maintainer
#1043 lore raw

PATCH 1042/1250 s390/crypto: Fix skcipher_walk return code handling in aes_s390

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit 15fa028589c3a2545f0bff355eff06d5844196bf upstream.

The return codes from skcipher_walk_virt() were not properly checked before entering the processing loops in ecb_aes_crypt() and ctr_aes_crypt(). If skcipher_walk_virt() fails, the walk structure may be in an undefined state, and attempting to process data could lead to incorrect behavior or accessing uninitialized memory.

Add proper return code checking to ensure correct handling of the walk initialization and walk advance and eventually return to the caller with that return code.

Fixes 7988fb2c03c8 ("crypto: s390/aes - convert to skcipher API")
---
 arch/s390/crypto/aes_s390.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
arch/s390/crypto/aes_s390.c +6 -6
@@ -235,7 +235,7 @@ static int ecb_aes_crypt(struct skcipher
 		return fallback_skcipher_crypt(sctx, req, modifier);
 
 	ret = skcipher_walk_virt(&walk, req, false);
-	while ((nbytes = walk.nbytes) != 0) {
+	while (!ret && ((nbytes = walk.nbytes) != 0)) {
 		/* only use complete blocks */
 		n = nbytes & ~(AES_BLOCK_SIZE - 1);
 		cpacf_km(sctx->fc | modifier, sctx->key,
@@ -339,7 +339,7 @@ static int cbc_aes_crypt(struct skcipher
 		return ret;
 	memcpy(param.iv, walk.iv, AES_BLOCK_SIZE);
 	memcpy(param.key, sctx->key, sctx->key_len);
-	while ((nbytes = walk.nbytes) != 0) {
+	while (!ret && ((nbytes = walk.nbytes) != 0)) {
 		/* only use complete blocks */
 		n = nbytes & ~(AES_BLOCK_SIZE - 1);
 		cpacf_kmc(sctx->fc | modifier, &param,
@@ -465,7 +465,7 @@ static int xts_aes_crypt(struct skcipher
 	memcpy(xts_param.key + offset, xts_ctx->key, xts_ctx->key_len);
 	memcpy(xts_param.init, pcc_param.xts, 16);
 
-	while ((nbytes = walk.nbytes) != 0) {
+	while (!ret && ((nbytes = walk.nbytes) != 0)) {
 		/* only use complete blocks */
 		n = nbytes & ~(AES_BLOCK_SIZE - 1);
 		cpacf_km(xts_ctx->fc | modifier, xts_param.key + offset,
@@ -593,7 +593,7 @@ static int fullxts_aes_crypt(struct skci
 	memcpy(fxts_param.tweak, req->iv, AES_BLOCK_SIZE);
 	fxts_param.nap[0] = 0x01; /* initial alpha power (1, little-endian) */
 
-	while ((nbytes = walk.nbytes) != 0) {
+	while (!ret && ((nbytes = walk.nbytes) != 0)) {
 		/* only use complete blocks */
 		n = nbytes & ~(AES_BLOCK_SIZE - 1);
 		cpacf_km(xts_ctx->fc | modifier, fxts_param.key + offset,
@@ -683,7 +683,7 @@ static int ctr_aes_crypt(struct skcipher
 	locked = mutex_trylock(&ctrblk_lock);
 
 	ret = skcipher_walk_virt(&walk, req, false);
-	while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) {
+	while (!ret && ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE)) {
 		n = AES_BLOCK_SIZE;
 
 		if (nbytes >= 2*AES_BLOCK_SIZE && locked)
@@ -702,7 +702,7 @@ static int ctr_aes_crypt(struct skcipher
 	/*
 	 * final block may be < AES_BLOCK_SIZE, copy only nbytes
 	 */
-	if (nbytes) {
+	if (!ret && nbytes) {
 		memset(buf, 0, AES_BLOCK_SIZE);
 		memcpy(buf, walk.src.virt.addr, nbytes);
 		cpacf_kmctr(sctx->fc, sctx->key, buf, buf,
author maintainer
#1044 lore raw

PATCH 1043/1250 s390/crypto: Fix use of mutex in atomic context

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit d1c44a7d085473173bb360b7218a43699c3f56c7 upstream.

The AES CTR implementation used a mutex to lock one page of exclusive memory for fast CTR processing. Unfortunately a mutex is not save to use in atomic or interrupt context. So use a binary semaphore instead which is save to use in such environments.

Furthermore rework the code to get rid of conditional locking. So restructure the AES CRT code by extracting the main loop into a separate function and just give in information about the (locked) page can be used or not (is not locked).

---
 arch/s390/crypto/aes_s390.c |   63 +++++++++++++++++++++++++++-----------------
 1 file changed, 39 insertions(+), 24 deletions(-)
arch/s390/crypto/aes_s390.c +39 -24
@@ -28,14 +28,14 @@
 #include <linux/module.h>
 #include <linux/cpufeature.h>
 #include <linux/init.h>
-#include <linux/mutex.h>
 #include <linux/fips.h>
+#include <linux/semaphore.h>
 #include <linux/string.h>
 #include <crypto/xts.h>
 #include <asm/cpacf.h>
 
 static u8 *ctrblk;
-static DEFINE_MUTEX(ctrblk_lock);
+static DEFINE_SEMAPHORE(ctrblk_sem, 1);
 
 static cpacf_mask_t km_functions, kmc_functions, kmctr_functions,
 		    kma_functions;
@@ -668,46 +668,61 @@ static unsigned int __ctrblk_init(u8 *ct
 	return n;
 }
 
+static int __ctr_aes_crypt(struct s390_aes_ctx *sctx,
+			   struct skcipher_walk *walk, bool locked)
+{
+	unsigned int n, nbytes;
+	int ret = 0;
+	u8 *ctrptr;
+
+	while (!ret && ((nbytes = walk->nbytes) >= AES_BLOCK_SIZE)) {
+		n = AES_BLOCK_SIZE;
+		if (nbytes >= 2 * AES_BLOCK_SIZE && locked)
+			n = __ctrblk_init(ctrblk, walk->iv, nbytes);
+		ctrptr = (n > AES_BLOCK_SIZE) ? ctrblk : walk->iv;
+		cpacf_kmctr(sctx->fc, sctx->key, walk->dst.virt.addr,
+			    walk->src.virt.addr, n, ctrptr);
+		if (ctrptr == ctrblk)
+			memcpy(walk->iv, ctrptr + n - AES_BLOCK_SIZE,
+			       AES_BLOCK_SIZE);
+		crypto_inc(walk->iv, AES_BLOCK_SIZE);
+		ret = skcipher_walk_done(walk, nbytes - n);
+	}
+
+	return ret;
+}
+
 static int ctr_aes_crypt(struct skcipher_request *req)
 {
 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
 	struct s390_aes_ctx *sctx = crypto_skcipher_ctx(tfm);
-	u8 buf[AES_BLOCK_SIZE], *ctrptr;
 	struct skcipher_walk walk;
-	unsigned int n, nbytes;
-	int ret, locked;
+	u8 buf[AES_BLOCK_SIZE];
+	int ret;
 
 	if (unlikely(!sctx->fc))
 		return fallback_skcipher_crypt(sctx, req, 0);
 
-	locked = mutex_trylock(&ctrblk_lock);
-
 	ret = skcipher_walk_virt(&walk, req, false);
-	while (!ret && ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE)) {
-		n = AES_BLOCK_SIZE;
+	if (ret)
+		return ret;
 
-		if (nbytes >= 2*AES_BLOCK_SIZE && locked)
-			n = __ctrblk_init(ctrblk, walk.iv, nbytes);
-		ctrptr = (n > AES_BLOCK_SIZE) ? ctrblk : walk.iv;
-		cpacf_kmctr(sctx->fc, sctx->key, walk.dst.virt.addr,
-			    walk.src.virt.addr, n, ctrptr);
-		if (ctrptr == ctrblk)
-			memcpy(walk.iv, ctrptr + n - AES_BLOCK_SIZE,
-			       AES_BLOCK_SIZE);
-		crypto_inc(walk.iv, AES_BLOCK_SIZE);
-		ret = skcipher_walk_done(&walk, nbytes - n);
+	if (down_trylock(&ctrblk_sem) == 0) {
+		ret = __ctr_aes_crypt(sctx, &walk, true);
+		up(&ctrblk_sem);
+	} else {
+		ret = __ctr_aes_crypt(sctx, &walk, false);
 	}
-	if (locked)
-		mutex_unlock(&ctrblk_lock);
+
 	/*
 	 * final block may be < AES_BLOCK_SIZE, copy only nbytes
 	 */
-	if (!ret && nbytes) {
+	if (!ret && walk.nbytes > 0) {
 		memset(buf, 0, AES_BLOCK_SIZE);
-		memcpy(buf, walk.src.virt.addr, nbytes);
+		memcpy(buf, walk.src.virt.addr, walk.nbytes);
 		cpacf_kmctr(sctx->fc, sctx->key, buf, buf,
 			    AES_BLOCK_SIZE, walk.iv);
-		memcpy(walk.dst.virt.addr, buf, nbytes);
+		memcpy(walk.dst.virt.addr, buf, walk.nbytes);
 		crypto_inc(walk.iv, AES_BLOCK_SIZE);
 		ret = skcipher_walk_done(&walk, 0);
 	}
author maintainer
#1045 lore raw

PATCH 1044/1250 s390/crypto: Fix missing scrub of temp buffers with AES ctr and gcm algorithm

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit 8b7c3b6914f19caf648d05726a86af6326d3c2c6 upstream.

In function ctr_aes_crypt() there is a buffer used to process remaining bytes < AES_BLOCK_SIZE. This buffer was not scrubbed and thus could lead to expose of unwanted data. When the buffer is used explicitly scrub it at the end of the code block to avoid exposure of maybe sensitive data.

In a similar way the function gcm_aes_crypt() hat an error path where the CPACF param block was not scrubbed. Instead of return early now these error paths go to end of function where explicit scrubbing is done. Similar with the buffers which are part of the gcm_sg_walk structs from the variables gw_in and gw_out.

Fixes d07f951903fa ("crypto: s390/aes - Fix buffer overread in CTR mode")
---
 arch/s390/crypto/aes_s390.c |   16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
arch/s390/crypto/aes_s390.c +12 -4
@@ -725,6 +725,7 @@ static int ctr_aes_crypt(struct skcipher
 		memcpy(walk.dst.virt.addr, buf, walk.nbytes);
 		crypto_inc(walk.iv, AES_BLOCK_SIZE);
 		ret = skcipher_walk_done(&walk, 0);
+		memzero_explicit(buf, sizeof(buf));
 	}
 
 	return ret;
@@ -1016,10 +1017,14 @@ static int gcm_aes_crypt(struct aead_req
 			  gw_in.ptr, aad_bytes);
 
 		n = aad_bytes + pc_bytes;
-		if (gcm_in_walk_done(&gw_in, n) != n)
-			return -ENOMEM;
-		if (gcm_out_walk_done(&gw_out, n) != n)
-			return -ENOMEM;
+		if (gcm_in_walk_done(&gw_in, n) != n) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		if (gcm_out_walk_done(&gw_out, n) != n) {
+			ret = -ENOMEM;
+			goto out;
+		}
 		aadlen -= aad_bytes;
 		pclen -= pc_bytes;
 	} while (aadlen + pclen > 0);
@@ -1031,7 +1036,10 @@ static int gcm_aes_crypt(struct aead_req
 	} else
 		scatterwalk_map_and_copy(param.t, req->dst, len, taglen, 1);
 
+out:
 	memzero_explicit(&param, sizeof(param));
+	memzero_explicit(gw_in.buf, sizeof(gw_in.buf));
+	memzero_explicit(gw_out.buf, sizeof(gw_out.buf));
 	return ret;
 }
 
author maintainer
#1046 lore raw

PATCH 1045/1250 s390/crypto: Fix missing cra_flags in paes_s390

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit 5b97b969030c099333d973be420edef0d6452e0f upstream.

The 4 algorithms implemented in paes_s390 never had any cra_flags set. So add code which sets the cra_flag to CRYPTO_ALG_ASYNC and CRYPTO_ALG_NO_FALLBACK.

Fixes 4ccd065a69df ("crypto: ahash - Add support for drivers with no fallback")
---
 arch/s390/crypto/paes_s390.c |    4 ++++
 1 file changed, 4 insertions(+)
arch/s390/crypto/paes_s390.c +4 -0
@@ -566,6 +566,7 @@ static struct skcipher_engine_alg ecb_pa
 		.base.cra_name	      = "ecb(paes)",
 		.base.cra_driver_name = "ecb-paes-s390",
 		.base.cra_priority    = 401,	/* combo: aes + ecb + 1 */
+		.base.cra_flags	      = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NO_FALLBACK,
 		.base.cra_blocksize   = AES_BLOCK_SIZE,
 		.base.cra_ctxsize     = sizeof(struct s390_paes_ctx),
 		.base.cra_module      = THIS_MODULE,
@@ -829,6 +830,7 @@ static struct skcipher_engine_alg cbc_pa
 		.base.cra_name	      = "cbc(paes)",
 		.base.cra_driver_name = "cbc-paes-s390",
 		.base.cra_priority    = 402,	/* cbc-paes-s390 + 1 */
+		.base.cra_flags	      = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NO_FALLBACK,
 		.base.cra_blocksize   = AES_BLOCK_SIZE,
 		.base.cra_ctxsize     = sizeof(struct s390_paes_ctx),
 		.base.cra_module      = THIS_MODULE,
@@ -1134,6 +1136,7 @@ static struct skcipher_engine_alg ctr_pa
 		.base.cra_name	      =	"ctr(paes)",
 		.base.cra_driver_name =	"ctr-paes-s390",
 		.base.cra_priority    =	402,	/* ecb-paes-s390 + 1 */
+		.base.cra_flags	      = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NO_FALLBACK,
 		.base.cra_blocksize   =	1,
 		.base.cra_ctxsize     =	sizeof(struct s390_paes_ctx),
 		.base.cra_module      =	THIS_MODULE,
@@ -1573,6 +1576,7 @@ static struct skcipher_engine_alg xts_pa
 		.base.cra_name	      =	"xts(paes)",
 		.base.cra_driver_name =	"xts-paes-s390",
 		.base.cra_priority    =	402,	/* ecb-paes-s390 + 1 */
+		.base.cra_flags	      = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NO_FALLBACK,
 		.base.cra_blocksize   =	AES_BLOCK_SIZE,
 		.base.cra_ctxsize     =	sizeof(struct s390_pxts_ctx),
 		.base.cra_module      =	THIS_MODULE,
author maintainer
#1047 lore raw

PATCH 1046/1250 s390/crypto: Fix handling of EBUSY in PHMAC when req is pushed to crypto engine

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit 330148371401de474b656eaf521861f12ec1a1ce upstream.

When a request is transferred to the engine via crypto_transfer_hash_request_to_engine() there are two return codes signaling a successful transfer: EINPROGRESS and EBUSY. However the correct handling of EBUSY was missing and has been added as a return code indicating a successful transfer to the crypto engine.

Fixes cbbc675506cc ("crypto: s390 - New s390 specific protected key hash phmac")
---
 arch/s390/crypto/phmac_s390.c |   30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)
arch/s390/crypto/phmac_s390.c +21 -9
@@ -59,8 +59,10 @@ static inline int hwh_prepare(struct aha
  */
 static inline int hwh_advance(struct hash_walk_helper *hwh, int n)
 {
-	if (n < 0)
+	if (n < 0) {
+		hwh->walkbytes = n;
 		return crypto_hash_walk_done(&hwh->walk, n);
+	}
 
 	hwh->walkbytes -= n;
 	hwh->walkaddr += n;
@@ -597,6 +599,7 @@ static int phmac_update(struct ahash_req
 	struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm);
 	struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx;
 	struct hash_walk_helper *hwh = &req_ctx->hwh;
+	bool cleanup = true;
 	int rc;
 
 	/* prep the walk in the request context */
@@ -620,12 +623,15 @@ static int phmac_update(struct ahash_req
 		req_ctx->async_op = OP_UPDATE;
 		atomic_inc(&tfm_ctx->via_engine_ctr);
 		rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req);
-		if (rc != -EINPROGRESS)
+		if (rc == -EINPROGRESS || rc == -EBUSY)
+			cleanup = false;
+		else
 			atomic_dec(&tfm_ctx->via_engine_ctr);
 	}
 
-	if (rc != -EINPROGRESS) {
-		hwh_advance(hwh, rc);
+	if (cleanup) {
+		if (hwh->walkbytes > 0)
+			hwh_advance(hwh, rc);
 		memzero_explicit(kmac_ctx, sizeof(*kmac_ctx));
 	}
 
@@ -640,6 +646,7 @@ static int phmac_final(struct ahash_requ
 	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
 	struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm);
 	struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx;
+	bool cleanup = true;
 	int rc = 0;
 
 	/* Try synchronous operation if no active engine usage */
@@ -658,12 +665,14 @@ static int phmac_final(struct ahash_requ
 		req_ctx->async_op = OP_FINAL;
 		atomic_inc(&tfm_ctx->via_engine_ctr);
 		rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req);
-		if (rc != -EINPROGRESS)
+		if (rc == -EINPROGRESS || rc == -EBUSY)
+			cleanup = false;
+		else
 			atomic_dec(&tfm_ctx->via_engine_ctr);
 	}
 
 out:
-	if (rc != -EINPROGRESS)
+	if (cleanup)
 		memzero_explicit(kmac_ctx, sizeof(*kmac_ctx));
 	pr_debug("rc=%d\n", rc);
 	return rc;
@@ -676,6 +685,7 @@ static int phmac_finup(struct ahash_requ
 	struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm);
 	struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx;
 	struct hash_walk_helper *hwh = &req_ctx->hwh;
+	bool cleanup = true;
 	int rc;
 
 	/* prep the walk in the request context */
@@ -707,15 +717,17 @@ static int phmac_finup(struct ahash_requ
 		/* req->async_op has been set to either OP_FINUP or OP_FINAL */
 		atomic_inc(&tfm_ctx->via_engine_ctr);
 		rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req);
-		if (rc != -EINPROGRESS)
+		if (rc == -EINPROGRESS || rc == -EBUSY)
+			cleanup = false;
+		else
 			atomic_dec(&tfm_ctx->via_engine_ctr);
 	}
 
-	if (rc != -EINPROGRESS)
+	if (cleanup && hwh->walkbytes > 0)
 		hwh_advance(hwh, rc);
 
 out:
-	if (rc != -EINPROGRESS)
+	if (cleanup)
 		memzero_explicit(kmac_ctx, sizeof(*kmac_ctx));
 	pr_debug("rc=%d\n", rc);
 	return rc;
author maintainer
#1048 lore raw

PATCH 1047/1250 s390/crypto: Fix missing scrub of temp buffers with PAES algorithm

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit 19a218b46b2471d370c11fb52040f36ac8d05d23 upstream.

In function ctr_paes_do_crypt() there is a buffer used to process remaining bytes < AES_BLOCK_SIZE. This buffer was not scrubbed and thus could lead to expose of unwanted data. Rework the code to explicitly scrub the buffer at the end of the function to avoid exposure of maybe sensitive data.

In function __xts_2keys_prep_param() change the existing scrub to clean the whole param block instead of just the key field.

Fixes 6cd87cb5ef6c ("s390/crypto: Rework protected key AES for true asynch support")
---
 arch/s390/crypto/paes_s390.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/s390/crypto/paes_s390.c +2 -1
@@ -1022,6 +1022,7 @@ static int ctr_paes_do_crypt(struct s390
 	}
 
 out:
+	memzero_explicit(buf, sizeof(buf));
 	pr_debug("rc=%d\n", rc);
 	return rc;
 }
@@ -1349,7 +1350,7 @@ static inline int __xts_2keys_prep_param
 		memcpy(param->init, pcc_param.xts, 16);
 	}
 
-	memzero_explicit(pcc_param.key, sizeof(pcc_param.key));
+	memzero_explicit(&pcc_param, sizeof(pcc_param));
 	return rc;
 }
 
author maintainer
#1049 lore raw

PATCH 1048/1250 s390/crypto: Fix wrong return code to engine in asynch callbacks

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit ac1481320110b803ab9b79ab4d2ca11a74fc05f2 upstream.

When crypto_finalize_hash_request() or crypto_finalize_skcipher_request() explicitly completes a request, the do_one_request callback must return 0 to indicate successful handling. Returning a negative error code causes the crypto engine to assume the driver failed to take ownership and triggers a second completion via crypto_request_complete(), resulting in a double completion. This pattern occurs in paes_s390.c 4 times and once in phmac_s390.c.

Fixed in phmac_do_one_request() and all four paes do_one_request callbacks (ecb, cbc, ctr, xts) by returning 0 after explicit finalization instead of propagating the error code.

Fixes 6cd87cb5ef6c ("s390/crypto: Rework protected key AES for true asynch support")
---
 arch/s390/crypto/paes_s390.c  |    8 ++++----
 arch/s390/crypto/phmac_s390.c |    2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)
arch/s390/crypto/paes_s390.c +4 -4
@@ -558,7 +558,7 @@ static int ecb_paes_do_one_request(struc
 	atomic_dec(&ctx->via_engine_ctr);
 	crypto_finalize_skcipher_request(engine, req, rc);
 	local_bh_enable();
-	return rc;
+	return 0;
 }
 
 static struct skcipher_engine_alg ecb_paes_alg = {
@@ -822,7 +822,7 @@ static int cbc_paes_do_one_request(struc
 	atomic_dec(&ctx->via_engine_ctr);
 	crypto_finalize_skcipher_request(engine, req, rc);
 	local_bh_enable();
-	return rc;
+	return 0;
 }
 
 static struct skcipher_engine_alg cbc_paes_alg = {
@@ -1129,7 +1129,7 @@ static int ctr_paes_do_one_request(struc
 	atomic_dec(&ctx->via_engine_ctr);
 	crypto_finalize_skcipher_request(engine, req, rc);
 	local_bh_enable();
-	return rc;
+	return 0;
 }
 
 static struct skcipher_engine_alg ctr_paes_alg = {
@@ -1569,7 +1569,7 @@ static int xts_paes_do_one_request(struc
 	atomic_dec(&ctx->via_engine_ctr);
 	crypto_finalize_skcipher_request(engine, req, rc);
 	local_bh_enable();
-	return rc;
+	return 0;
 }
 
 static struct skcipher_engine_alg xts_paes_alg = {
arch/s390/crypto/phmac_s390.c +1 -1
@@ -935,7 +935,7 @@ out:
 	atomic_dec(&tfm_ctx->via_engine_ctr);
 	crypto_finalize_hash_request(engine, req, rc);
 	local_bh_enable();
-	return rc;
+	return 0;
 }
 
 #define S390_ASYNC_PHMAC_ALG(x)						\
author maintainer
#1050 lore raw

PATCH 1049/1250 s390/crypto: Map EBUSY to EIO when key conversion fails repeatedly

In reply to Greg Kroah-Hartman

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

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

From: Harald Freudenberger <[email protected]>

commit 7a08507ea5b4d06ad8d269287913573f34467565 upstream.

When hardware persistently returns -EBUSY after exhausting retries, the error propagates to crypto_finalize_*_request(). The crypto API's completion wrapper treats -EBUSY as a queueing status and swallows it, preventing the completion callback from firing. This causes callers using crypto_wait_req() to block indefinitely.

Translate persistent -EBUSY to -EIO after retry exhaustion to ensure proper error propagation and callback invocation.

Fixes 6cd87cb5ef6c ("s390/crypto: Rework protected key AES for true asynch support")
---
 arch/s390/crypto/paes_s390.c  |    4 ++++
 arch/s390/crypto/phmac_s390.c |    4 ++++
 2 files changed, 8 insertions(+)
arch/s390/crypto/paes_s390.c +4 -0
@@ -213,6 +213,10 @@ static inline int convert_key(const u8 *
 				      PKEY_XFLAG_NOMEMALLOC);
 	}
 
+	/* But finally map -EBUSY to -EIO to indicate an IO failure */
+	if (rc == -EBUSY)
+		rc = -EIO;
+
 out:
 	pr_debug("rc=%d\n", rc);
 	return rc;
arch/s390/crypto/phmac_s390.c +4 -0
@@ -334,6 +334,10 @@ static inline int convert_key(const u8 *
 				      PKEY_XFLAG_NOMEMALLOC);
 	}
 
+	/* But finally map -EBUSY to -EIO to indicate an IO failure */
+	if (rc == -EBUSY)
+		rc = -EIO;
+
 out:
 	pr_debug("rc=%d\n", rc);
 	return rc;
author maintainer
#1051 lore raw

PATCH 1050/1250 perf: RISC-V: store available counter mask as bitmap

In reply to Greg Kroah-Hartman

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

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

From: Xixin Liu <[email protected]>

commit 6809da6e9c08ccc9a09cb0a48c61471679274aaa upstream.

The available-counter mask was a single unsigned long, but iteration uses RISCV_MAX_COUNTERS, which is 64. On RV32 that reads past the object. Filling with an unsigned-long bit at index 32 and above is also wrong.

Use DECLARE_BITMAP and set_bit/bitmap helpers. Walk each bitmap word into CFG_MATCH when checking events, when allocating an index, and when stopping all counters. Set the counter base to i times BITS_PER_LONG. Share the CFG_MATCH ecall through a small helper so the 32-bit argument split is not duplicated. On qemu-system-riscv32 the probe bitmap has bits above XLEN set, so the first word alone is not enough.

Fixes e9991434596f ("RISC-V: Add perf platform driver based on SBI PMU extension")
Assisted-by DeepSeek:deepseek-v3
[[email protected]: updated to apply; fixed checkpatch.pl issues]
---
 drivers/perf/riscv_pmu_legacy.c |    5 +-
 drivers/perf/riscv_pmu_sbi.c    |   88 ++++++++++++++++++++++++++++------------
 include/linux/perf/riscv_pmu.h  |    2 
 3 files changed, 66 insertions(+), 29 deletions(-)
drivers/perf/riscv_pmu_legacy.c +3 -2
@@ -111,8 +111,9 @@ static void pmu_legacy_init(struct riscv
 {
 	pr_info("Legacy PMU implementation is available\n");
 
-	pmu->cmask = BIT(RISCV_PMU_LEGACY_CYCLE) |
-		BIT(RISCV_PMU_LEGACY_INSTRET);
+	bitmap_zero(pmu->cmask, RISCV_MAX_COUNTERS);
+	set_bit(RISCV_PMU_LEGACY_CYCLE, pmu->cmask);
+	set_bit(RISCV_PMU_LEGACY_INSTRET, pmu->cmask);
 	pmu->ctr_start = pmu_legacy_ctr_start;
 	pmu->ctr_stop = NULL;
 	pmu->event_map = pmu_legacy_event_map;
drivers/perf/riscv_pmu_sbi.c +62 -26

157 lines not on this page · raw message

include/linux/perf/riscv_pmu.h +1 -1
@@ -55,7 +55,7 @@ struct riscv_pmu {
 
 	irqreturn_t	(*handle_irq)(int irq_num, void *dev);
 
-	unsigned long	cmask;
+	DECLARE_BITMAP(cmask, RISCV_MAX_COUNTERS);
 	u64		(*ctr_read)(struct perf_event *event);
 	int		(*ctr_get_idx)(struct perf_event *event);
 	int		(*ctr_get_width)(int idx);
author maintainer
#1052 lore raw

PATCH 1051/1250 perf: RISC-V: use BIT_ULL for u64 overflow masks

In reply to Greg Kroah-Hartman

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

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

From: Xixin Liu <[email protected]>

commit 6693171c8c540b3a54e671992f0561613076fc3b upstream.

Overflow status and restart masks are u64, but bits were built with BIT(). On RV32 that is an unsigned long shift, so indices >= 32 truncate or wrap and corrupt the mask.

Use BIT_ULL() for those u64 bitops.

Fixes a8625217a054 ("drivers/perf: riscv: Implement SBI PMU snapshot function")
Assisted-by DeepSeek:deepseek-v3
[[email protected]: updated to apply]
---
 drivers/perf/riscv_pmu_sbi.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/perf/riscv_pmu_sbi.c +3 -3
@@ -1039,7 +1039,7 @@ static inline void pmu_sbi_start_ovf_ctr
 	struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr;
 
 	for_each_set_bit(idx, cpu_hw_evt->used_hw_ctrs, RISCV_MAX_COUNTERS) {
-		if (ctr_ovf_mask & BIT(idx)) {
+		if (ctr_ovf_mask & BIT_ULL(idx)) {
 			event = cpu_hw_evt->events[idx];
 			hwc = &event->hw;
 			max_period = riscv_pmu_ctr_get_width_mask(event);
@@ -1148,14 +1148,14 @@ static irqreturn_t pmu_sbi_ovf_handler(i
 			hidx = info->csr - CSR_CYCLE;
 
 		/* check if the corresponding bit is set in scountovf or overflow mask in shmem */
-		if (!(overflow & BIT(hidx)))
+		if (!(overflow & BIT_ULL(hidx)))
 			continue;
 
 		/*
 		 * Keep a track of overflowed counters so that they can be started
 		 * with updated initial value.
 		 */
-		overflowed_ctrs |= BIT(lidx);
+		overflowed_ctrs |= BIT_ULL(lidx);
 		hw_evt = &event->hw;
 		/* Update the event states here so that we know the state while reading */
 		hw_evt->state |= PERF_HES_STOPPED;
author maintainer
#1053 lore raw

PATCH 1052/1250 afs: Fix missing kunmap in afs_dir_search_bucket()

In reply to Greg Kroah-Hartman

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

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

From: David Howells <[email protected]>

commit 950ae84b5cc944fbe27d81806d0b76af765f779c upstream.

Fix afs_dir_search_bucket() to kunmap the block it's using in the "bad:" path.

---
 fs/afs/dir_search.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
fs/afs/dir_search.c +1 -2
@@ -173,12 +173,11 @@ int afs_dir_search_bucket(struct afs_dir
 
 	ret = -ENOENT;
 found:
+bad:
 	if (iter->block) {
 		kunmap_local(iter->block);
 		iter->block = NULL;
 	}
-
-bad:
 	if (ret == -ESTALE)
 		afs_invalidate_dir(iter->dvnode, afs_dir_invalid_iter_stale);
 	_leave(" = %d", ret);
author maintainer
#1054 lore raw

PATCH 1053/1250 afs: Fix double-unmap of directory block

In reply to Greg Kroah-Hartman

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

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

From: David Howells <[email protected]>

commit e3cfd3eb7d5be7787cc69530b423f788f14d084f upstream.

Fix afs_edit_dir_remove() to use a cleanup function to unmap the block pointed to by afs_dir_iter::block if it's left pointing to something rather than manually kunmapping the blocks. Manually kunmapping without clearing iter.blocks can result in a double-kunmap if afs_dir_find_block() is called twice in a row (which would be the case if the block being modified is not first in the hash chain).

---
 fs/afs/dir_edit.c   |    9 ++-------
 fs/afs/dir_search.c |   10 ++--------
 fs/afs/internal.h   |    8 ++++++++
 3 files changed, 12 insertions(+), 15 deletions(-)
fs/afs/dir_edit.c +2 -7
@@ -442,7 +442,7 @@ void afs_edit_dir_remove(struct afs_vnod
 	/* Check and clear the entry. */
 	de = &block->dirents[slot];
 	if (de->u.valid != 1)
-		goto error_unmap;
+		goto error;
 
 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot,
 			   ntohl(de->u.vnode), ntohl(de->u.unique),
@@ -458,7 +458,6 @@ void afs_edit_dir_remove(struct afs_vnod
 	/* Clear the constituent entries. */
 	next = de->u.hash_next;
 	memset(de, 0, sizeof(*de) * iter.nr_slots);
-	kunmap_local(block);
 
 	/* Adjust the hash chain: if iter->prev_entry is 0, the hashtable head
 	 * index is previous; otherwise it's slot number of the previous entry.
@@ -485,7 +484,6 @@ void afs_edit_dir_remove(struct afs_vnod
 		pde = &pblock->dirents[ps];
 		prev_next = pde->u.hash_next;
 		if (prev_next != htons(entry)) {
-			kunmap_local(pblock);
 			pr_warn("%llx:%llx:%x: not prev in chain b=%x p=%x,%x e=%x %*s",
 				vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique,
 				iter.bucket, iter.prev_entry, prev_next, entry,
@@ -493,7 +491,6 @@ void afs_edit_dir_remove(struct afs_vnod
 			goto error;
 		}
 		pde->u.hash_next = next;
-		kunmap_local(pblock);
 	}
 
 	netfs_single_mark_inode_dirty(&vnode->netfs.inode);
@@ -503,18 +500,16 @@ void afs_edit_dir_remove(struct afs_vnod
 	_debug("Remove %s from %u[%u]", name->name, b, slot);
 
 out_unmap:
+	afs_dir_end_iter(&iter);
 	kunmap_local(meta);
 	_leave("");
 	return;
 
 already_invalidated:
-	kunmap_local(block);
 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval,
 			   0, 0, 0, 0, name->name);
 	goto out_unmap;
 
-error_unmap:
-	kunmap_local(block);
 error:
 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error,
 			   0, 0, 0, 0, name->name);
fs/afs/dir_search.c +2 -8
@@ -75,10 +75,7 @@ union afs_xdr_dir_block *afs_dir_find_bl
 
 	_enter("%zx,%d", block, slot);
 
-	if (iter->block) {
-		kunmap_local(iter->block);
-		iter->block = NULL;
-	}
+	afs_dir_end_iter(iter);
 
 	if (dvnode->directory_size < blend)
 		goto fail;
@@ -174,10 +171,7 @@ int afs_dir_search_bucket(struct afs_dir
 	ret = -ENOENT;
 found:
 bad:
-	if (iter->block) {
-		kunmap_local(iter->block);
-		iter->block = NULL;
-	}
+	afs_dir_end_iter(iter);
 	if (ret == -ESTALE)
 		afs_invalidate_dir(iter->dvnode, afs_dir_invalid_iter_stale);
 	_leave(" = %d", ret);
fs/afs/internal.h +8 -0
@@ -1129,6 +1129,14 @@ int afs_dir_search_bucket(struct afs_dir
 int afs_dir_search(struct afs_vnode *dvnode, const struct qstr *name,
 		   struct afs_fid *_fid, afs_dataversion_t *_dir_version);
 
+static inline void afs_dir_end_iter(struct afs_dir_iter *iter)
+{
+	if (iter->block) {
+		kunmap_local(iter->block);
+		iter->block = NULL;
+	}
+}
+
 /*
  * dir_silly.c
  */
author maintainer
#1055 lore raw

PATCH 1054/1250 afs: Fix incorrect free in candidate cleanup in afs_lookup_server()

In reply to Greg Kroah-Hartman

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

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

From: David Howells <[email protected]>

commit 044d596094af4b769fb8e1173dff0d08bd68db6c upstream.

Fix afs_lookup_server() to not free an existing server's endpoint state when cleaning up a candidate server. The candidate record doesn't have an endpoint state yet at this point, so the free for that can just be removed.

---
 fs/afs/server.c |    1 -
 1 file changed, 1 deletion(-)
fs/afs/server.c +0 -1
@@ -242,7 +242,6 @@ struct afs_server *afs_lookup_server(str
 out:
 	afs_put_addrlist(alist, afs_alist_trace_put_server_create);
 	if (candidate) {
-		kfree(rcu_access_pointer(server->endpoint_state));
 		kfree(candidate);
 		afs_dec_servers_outstanding(cell->net);
 	}
author maintainer
#1056 lore raw

PATCH 1055/1250 afs: Clear stale peer app data after address list changes

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit ba0623fc19a424f4745394c499f9f28a8d88d397 upstream.

afs_fs_probe_fileserver() fetches the current endpoint state under server->fs_lock, but leaves old_alist as NULL. Consequently, afs_set_peer_appdata() treats every address list replacement as initial setup and only binds the new peers; it never unbinds peers removed from the old list.

An address refresh can therefore proceed as follows. CPU 0 replaces server S's list and drops Pold without clearing Pold->app_data. The server destroyer then clears only S's current peers and lets S reach its RCU callback. After the callback frees S, CPU 1 handles a callback through an RxRPC connection that still pins Pold, reads Pold->app_data, and calls afs_use_server() on the freed object.

KASAN reported:

  BUG: KASAN: slab-use-after-free in afs_find_server+0x3c/0xa0
  Read of size 4 at addr ffff8881013e1af0 by task krxrpcio/7001/74
  Call Trace:
   afs_find_server+0x3c/0xa0
   afs_rx_new_call+0x15c/0x390
   rxrpc_new_incoming_call+0x97c/0x1730
   rxrpc_input_packet.constprop.0+0xd03/0xec0
   rxrpc_io_thread+0x967/0x1640
  Allocated by task 93:
   afs_lookup_server+0x1a7/0x14c0
   afs_alloc_server_list+0x43f/0xb60
   afs_create_volume+0x923/0x1490
   afs_get_tree+0x1c6/0x10a0
  Freed by task 0:
   kfree+0x131/0x3c0
   rcu_core+0x50a/0x1850
  Last potentially related work creation:
   __call_rcu_common.constprop.0+0x71/0xa10
   afs_put_server+0x213/0x2b0

Preserve old->addresses for the peer app-data update so that removed peers are cleared before the endpoint state is replaced. Also advance both cursors when the old and new lists share a peer; activating the old/new comparison without this would otherwise loop forever on the shared entry.

---
 fs/afs/addr_list.c |    5 ++++-
 fs/afs/fs_probe.c  |    1 +
 2 files changed, 5 insertions(+), 1 deletion(-)
fs/afs/addr_list.c +4 -1
@@ -394,8 +394,11 @@ void afs_set_peer_appdata(struct afs_ser
 		struct rxrpc_peer *pn = new_alist->addrs[n].peer;
 		struct rxrpc_peer *po = old_alist->addrs[o].peer;
 
-		if (pn == po)
+		if (pn == po) {
+			n++;
+			o++;
 			continue;
+		}
 		if (pn < po) {
 			rxrpc_kernel_set_peer_data(pn, data);
 			n++;
fs/afs/fs_probe.c +1 -0
@@ -258,6 +258,7 @@ int afs_fs_probe_fileserver(struct afs_n
 					lockdep_is_held(&server->fs_lock));
 	if (old) {
 		estate->responsive_set = old->responsive_set;
+		old_alist = old->addresses;
 		if (!new_alist)
 			new_alist = old->addresses;
 	}
author maintainer
#1057 lore raw

PATCH 1056/1250 hwmon: (applesmc) fix key backlight workqueue leak on register failure

In reply to Greg Kroah-Hartman

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

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

From: Cong Nguyen <[email protected]>

commit 5a0aacaa2d593d7582ecfe289529b937b6dc5d3c upstream.

applesmc_create_key_backlight() allocates applesmc_led_wq before calling led_classdev_register(). When register fails, the error is returned to applesmc_init(), which jumps to out_light_sysfs and skips applesmc_release_key_backlight(), leaking the workqueue.

Destroy the workqueue on the register failure path. The bug was introduced when the inline init block was refactored into a helper that returns errors directly, dropping the old out_light_wq unwind label.

Fixes 0b0b5dff8967 ("hwmon: (applesmc) Simplify feature sysfs handling")
Assisted-by Claude:claude-opus-4
---
 drivers/hwmon/applesmc.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/hwmon/applesmc.c +6 -1
@@ -1249,12 +1249,17 @@ static void applesmc_release_light_senso
 
 static int applesmc_create_key_backlight(void)
 {
+	int ret;
+
 	if (!smcreg.has_key_backlight)
 		return 0;
 	applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
 	if (!applesmc_led_wq)
 		return -ENOMEM;
-	return led_classdev_register(&pdev->dev, &applesmc_backlight);
+	ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
+	if (ret)
+		destroy_workqueue(applesmc_led_wq);
+	return ret;
 }
 
 static void applesmc_release_key_backlight(void)
author maintainer
#1058 lore raw

PATCH 1057/1250 hwmon: (chipcap2) fix channels in humidity alarm notifications

In reply to Greg Kroah-Hartman

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

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

From: Javier Carrasco <[email protected]>

commit 286b175bb03893949f24621f356abd9d20368b0a upstream.

hwmon_notify_event() expects the channel number as its last argument, taken into account with the type parameter that it is a humidity sensor type. Given that this device only provides one humidity channel, 0 must be passed. The custom construct to enumerate the channels makes wrong assumptions by listing all types together (temperature and humidity).

Remove the custom channel enumeration and pass the right channel to hwmon_notify_event() for hwmon_humidity_min_alarm and hwmon_humidity_max_alarm.

---
 drivers/hwmon/chipcap2.c |    9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)
drivers/hwmon/chipcap2.c +2 -7
@@ -90,11 +90,6 @@ struct cc2_data {
 	bool process_irqs;
 };
 
-enum cc2_chan_addr {
-	CC2_CHAN_TEMP = 0,
-	CC2_CHAN_HUMIDITY,
-};
-
 /* %RH as a per cent mille from a register value */
 static long cc2_rh_convert(u16 data)
 {
@@ -493,7 +488,7 @@ static irqreturn_t cc2_low_interrupt(int
 
 	if (cc2->process_irqs) {
 		hwmon_notify_event(cc2->hwmon, hwmon_humidity,
-				   hwmon_humidity_min_alarm, CC2_CHAN_HUMIDITY);
+				   hwmon_humidity_min_alarm, 0);
 		cc2->rh_alarm.low_alarm = true;
 	}
 
@@ -506,7 +501,7 @@ static irqreturn_t cc2_high_interrupt(in
 
 	if (cc2->process_irqs) {
 		hwmon_notify_event(cc2->hwmon, hwmon_humidity,
-				   hwmon_humidity_max_alarm, CC2_CHAN_HUMIDITY);
+				   hwmon_humidity_max_alarm, 0);
 		cc2->rh_alarm.high_alarm = true;
 	}
 
author maintainer
#1059 lore raw

PATCH 1058/1250 hwmon: (gpio-fan) Fix use-after-free in alarm work

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit a2471ed17b0e6ff7bfb6b2ea8e6e5b04c309d293 upstream.

fan_alarm_irq_handler() queues fan_data->alarm_work, but nothing cancels it. fan_alarm_notify() dereferences fan_data and its hwmon device. On unbind, devres frees the interrupt, which only waits for the handler itself, and then releases the hwmon device and fan_data, so a pending fan_alarm_notify() can run after those frees.

Replace INIT_WORK() with devm_work_autocancel(), registered before devm_request_irq(). The devres cleanup then frees the interrupt first, so no new work can be queued, and cancels the work while fan_data and the hwmon device are still alive.

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

Fixes d6fe1360f42e ("hwmon: add generic GPIO fan driver")
Assisted-by Codex:gpt-5.6
---
 drivers/hwmon/gpio-fan.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/hwmon/gpio-fan.c +7 -1
@@ -12,6 +12,7 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/devm-helpers.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
 #include <linux/kstrtox.h>
@@ -84,6 +85,7 @@ static DEVICE_ATTR_RO(fan1_alarm);
 static int fan_alarm_init(struct gpio_fan_data *fan_data)
 {
 	int alarm_irq;
+	int err;
 	struct device *dev = fan_data->dev;
 
 	/*
@@ -94,7 +96,11 @@ static int fan_alarm_init(struct gpio_fa
 	if (alarm_irq <= 0)
 		return 0;
 
-	INIT_WORK(&fan_data->alarm_work, fan_alarm_notify);
+	err = devm_work_autocancel(dev, &fan_data->alarm_work,
+				   fan_alarm_notify);
+	if (err)
+		return err;
+
 	irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
 	return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler,
 				IRQF_SHARED, "GPIO fan alarm", fan_data);
author maintainer
#1060 lore raw

PATCH 1059/1250 hwmon: (pmbus) Clear generic status alarms with CLEAR_FAULTS

In reply to Greg Kroah-Hartman

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

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

From: Vishnu Razdan <[email protected]>

commit 6d760f8b41aed74de4402440e4db663d261478bd upstream.

Some hwmon alarms fall back to STATUS_WORD summary bits when no individual limit alarm is available. On PMBus 1.2 and newer devices, pmbus_get_boolean() acknowledges these alarms with the same byte-data write used for detailed status registers. For example, PB_STATUS_INPUT is 0x2000, so it is truncated to zero when passed to _pmbus_write_byte_data(). The resulting write cannot acknowledge the input alarm.

PMBus 1.3 Part II, sections 10.2.4 and 10.2.5, excludes ordinary STATUS_BYTE and STATUS_WORD summary bits from individual clearing. Their summary bits clear when the underlying status bits clear, so changing this to a word-data write would not fix the generic input alarm either.

Use the existing page CLEAR_FAULTS path for generic STATUS_WORD alarms, including devices whose status accessor uses STATUS_BYTE. Keep individual byte writes for detailed status registers on PMBus 1.2 and newer devices. As with the existing older-device fallback, CLEAR_FAULTS can clear other latched status; an active condition can reassert its status.

---
 drivers/hwmon/pmbus/pmbus_core.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hwmon/pmbus/pmbus_core.c +3 -1
@@ -1169,7 +1169,9 @@ static int pmbus_get_boolean(struct i2c_
 
 	regval = status & mask;
 	if (regval) {
-		if (data->revision >= PMBUS_REV_12) {
+		/* Generic STATUS_WORD alarms are not individually clearable. */
+		if (data->revision >= PMBUS_REV_12 &&
+		    reg != PMBUS_STATUS_WORD) {
 			ret = _pmbus_write_byte_data(client, page, reg, regval);
 			if (ret)
 				return ret;
author maintainer
#1061 lore raw

PATCH 1060/1250 media: hevc: add bounded tile-count helpers

In reply to Greg Kroah-Hartman

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

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

From: Michael Bommarito <[email protected]>

commit 592dd4f8442a13bed6e946d73d3164ba38b33bbd upstream.

The stateless HEVC decoders compute the number of tile columns and rows from num_tile_columns_minus1 / num_tile_rows_minus1 and clamp it to the column_width_minus1[] / row_height_minus1[] capacity before using it as a loop bound. Add shared helpers in a new <media/v4l2-hevc.h> so the rkvdec and hantro drivers do not each open-code the min_t() clamp.

Assisted-by Claude:claude-opus-4-8
Fixes 256fa3920874 ("media: v4l: Add definitions for HEVC stateless decoding")
---
 include/media/v4l2-hevc.h |   41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 include/media/v4l2-hevc.h
include/media/v4l2-hevc.h new file +41 -0
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Helper functions for HEVC stateless codecs.
+ */
+
+#ifndef _MEDIA_V4L2_HEVC_H
+#define _MEDIA_V4L2_HEVC_H
+
+#include <linux/minmax.h>
+#include <media/v4l2-ctrls.h>
+
+/**
+ * v4l2_hevc_pps_num_tile_columns - number of HEVC tile columns, bounded
+ * @pps: the V4L2 HEVC PPS control
+ *
+ * Return the number of tile columns (num_tile_columns_minus1 + 1) clamped to
+ * the capacity of column_width_minus1[]. The control validation already
+ * rejects out-of-range counts; this keeps the consuming drivers bounded too.
+ */
+static inline unsigned int
+v4l2_hevc_pps_num_tile_columns(const struct v4l2_ctrl_hevc_pps *pps)
+{
+	return min_t(unsigned int, pps->num_tile_columns_minus1 + 1,
+		     ARRAY_SIZE(pps->column_width_minus1));
+}
+
+/**
+ * v4l2_hevc_pps_num_tile_rows - number of HEVC tile rows, bounded
+ * @pps: the V4L2 HEVC PPS control
+ *
+ * Return the number of tile rows (num_tile_rows_minus1 + 1) clamped to the
+ * capacity of row_height_minus1[].
+ */
+static inline unsigned int
+v4l2_hevc_pps_num_tile_rows(const struct v4l2_ctrl_hevc_pps *pps)
+{
+	return min_t(unsigned int, pps->num_tile_rows_minus1 + 1,
+		     ARRAY_SIZE(pps->row_height_minus1));
+}
+
+#endif /* _MEDIA_V4L2_HEVC_H */
author maintainer
#1062 lore raw

PATCH 1061/1250 media: verisilicon: hantro: bound G2 HEVC tile loop to the buffer capacity

In reply to Greg Kroah-Hartman

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

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

From: Michael Bommarito <[email protected]>

commit 06236b094c899c22c12ac5097935eb6719293de8 upstream.

prepare_tile_info_buffer() writes one entry per tile into the tile_sizes DMA buffer, sized for a grid equal to the PPS uAPI array capacity. Use the bounded v4l2_hevc_pps_num_tile_columns() / v4l2_hevc_pps_num_tile_rows() helpers so the loops stay inside the buffer.

Fixes cb5dd5a0fa51 ("media: hantro: Introduce G2/HEVC decoder")
Assisted-by Claude:claude-opus-4-8
---
 drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c +4 -2
@@ -5,6 +5,8 @@
  * Copyright (C) 2020 Safran Passenger Innovations LLC
  */
 
+#include <media/v4l2-hevc.h>
+
 #include "hantro_hw.h"
 #include "hantro_g2_regs.h"
 
@@ -15,8 +17,8 @@ static void prepare_tile_info_buffer(str
 	const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps;
 	const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps;
 	u16 *p = (u16 *)((u8 *)ctx->hevc_dec.tile_sizes.cpu);
-	unsigned int num_tile_rows = pps->num_tile_rows_minus1 + 1;
-	unsigned int num_tile_cols = pps->num_tile_columns_minus1 + 1;
+	unsigned int num_tile_rows = v4l2_hevc_pps_num_tile_rows(pps);
+	unsigned int num_tile_cols = v4l2_hevc_pps_num_tile_columns(pps);
 	unsigned int pic_width_in_ctbs, pic_height_in_ctbs;
 	unsigned int max_log2_ctb_size, ctb_size;
 	bool tiles_enabled, uniform_spacing;
author maintainer
#1063 lore raw

PATCH 1062/1250 media: mediatek: vcodec: bound AV1 tile-start copy to the array capacity

In reply to Greg Kroah-Hartman

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

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

From: Michael Bommarito <[email protected]>

commit 37bef2170d4c88fc3d708eecf3ef0f4032bc1372 upstream.

vdec_av1_slice_setup_tile() copies tile_cols + 1 / tile_rows + 1 entries into mi_col_starts[] / mi_row_starts[] from the bitstream tile_info. Bound the copy to the array capacity.

Fixes 0934d3759615 ("media: mediatek: vcodec: separate decoder and encoder")
Assisted-by Claude:claude-opus-4-8
---
 drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c +3 -2
@@ -1299,11 +1299,12 @@ static void vdec_av1_slice_setup_tile(st
 	tile->uniform_tile_spacing_flag =
 		BIT_FLAG(ctrl_tile, V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING);
 
-	for (i = 0; i < tile->tile_cols + 1; i++)
+	/* Bound the copy to the mi_col_starts[]/mi_row_starts[] capacity. */
+	for (i = 0; i < tile->tile_cols + 1 && i < V4L2_AV1_MAX_TILE_COLS + 1; i++)
 		tile->mi_col_starts[i] =
 			ALIGN(ctrl_tile->mi_col_starts[i], BIT(mib_size_log2)) >> mib_size_log2;
 
-	for (i = 0; i < tile->tile_rows + 1; i++)
+	for (i = 0; i < tile->tile_rows + 1 && i < V4L2_AV1_MAX_TILE_ROWS + 1; i++)
 		tile->mi_row_starts[i] =
 			ALIGN(ctrl_tile->mi_row_starts[i], BIT(mib_size_log2)) >> mib_size_log2;
 }
author maintainer
#1064 lore raw

PATCH 1063/1250 media: v4l2-h264: Fix memcmp() size in B1 reference list comparison

In reply to Greg Kroah-Hartman

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

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

From: Haotian Zhang <[email protected]>

commit 10e59fbdef13597836bd6459095caa02c80af3d7 upstream.

In v4l2_h264_build_b_ref_lists(), the B0/B1 list equality check passes the entry count builder->num_valid to memcmp() instead of a byte size. Since struct v4l2_h264_reference is two bytes (fields and index), only half of each list is compared, so distinct lists can be wrongly treated as equal and trigger an incorrect swap(b1_reflist[0], b1_reflist[1]).

Change the memcmp() size argument to sizeof(b1_reflist[0]) * builder->num_valid so that the full byte length of both reference lists is compared.

Fixes 624922a2739b ("media: v4l2-core: Add helpers to build the H264 P/B0/B1 reflists")
---
 drivers/media/v4l2-core/v4l2-h264.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/media/v4l2-core/v4l2-h264.c +2 -1
@@ -440,7 +440,8 @@ v4l2_h264_build_b_ref_lists(const struct
 	}
 
 	if (builder->num_valid > 1 &&
-	    !memcmp(b1_reflist, b0_reflist, builder->num_valid))
+	    !memcmp(b1_reflist, b0_reflist,
+			sizeof(b1_reflist[0]) * builder->num_valid))
 		swap(b1_reflist[0], b1_reflist[1]);
 
 	print_ref_list_b(builder, b0_reflist, 0);
author maintainer
#1065 lore raw

PATCH 1064/1250 media: verisilicon: rockchip: guard VPU981 AV1 divisor and tile buffer

In reply to Greg Kroah-Hartman

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

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

From: Michael Bommarito <[email protected]>

commit b84f6533a8ed2fd7b282fc7ab4b8efadc745a89c upstream.

rockchip_vpu981_av1_dec_set_tile_info() divides context_update_tile_id by tile_info->tile_cols and writes one descriptor per tile into the tile_info DMA buffer, which holds AV1_MAX_TILES entries; tile_cols and tile_rows come from the bitstream. Guard the division against a zero tile_cols by initialising the context-update values to zero and computing them only when tile_cols is non-zero, and stop the descriptor writes once the tile_info buffer is full. The tile geometry written to the hardware registers is left unmodified; the per-dimension and total tile bounds are enforced by the control validation.

Fixes 727a400686a2 ("media: verisilicon: Add Rockchip AV1 decoder")
Assisted-by Claude:claude-opus-4-8
---
 drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c |   32 ++++++++--
 1 file changed, 26 insertions(+), 6 deletions(-)
drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c +26 -6
@@ -578,16 +578,30 @@ static void rockchip_vpu981_av1_dec_set_
 	const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
 	const struct v4l2_ctrl_av1_tile_group_entry *group_entry =
 	    ctrls->tile_group_entry;
-	int context_update_y =
-	    tile_info->context_update_tile_id / tile_info->tile_cols;
-	int context_update_x =
-	    tile_info->context_update_tile_id % tile_info->tile_cols;
-	int context_update_tile_id =
-	    context_update_x * tile_info->tile_rows + context_update_y;
+	int context_update_y = 0;
+	int context_update_x = 0;
+	int context_update_tile_id = 0;
 	u8 *dst = av1_dec->tile_info.cpu;
+	u8 *dst_end = dst + av1_dec->tile_info.size;
 	struct hantro_dev *vpu = ctx->dev;
 	int tile0, tile1;
 
+	/*
+	 * tile_cols and tile_rows are bounded by the V4L2 control validation
+	 * (V4L2_AV1_MAX_TILE_{COLS,ROWS} and V4L2_AV1_MAX_TILE_COUNT). Guard
+	 * the divisor here, and keep the descriptor writes within the
+	 * AV1_MAX_TILES tile_info buffer below; the register values use the
+	 * unmodified tile geometry.
+	 */
+	if (tile_info->tile_cols) {
+		context_update_y =
+		    tile_info->context_update_tile_id / tile_info->tile_cols;
+		context_update_x =
+		    tile_info->context_update_tile_id % tile_info->tile_cols;
+		context_update_tile_id =
+		    context_update_x * tile_info->tile_rows + context_update_y;
+	}
+
 	memset(dst, 0, av1_dec->tile_info.size);
 
 	for (tile0 = 0; tile0 < tile_info->tile_cols; tile0++) {
@@ -598,6 +612,10 @@ static void rockchip_vpu981_av1_dec_set_
 			    tile_info->height_in_sbs_minus_1[tile1] + 1;
 			u32 x0 = tile_info->width_in_sbs_minus_1[tile0] + 1;
 
+			/* Stop once the tile_info descriptor buffer is full. */
+			if (dst + 16 > dst_end)
+				break;
+
 			/* tile size in SB units (width,height) */
 			*dst++ = x0;
 			*dst++ = 0;
@@ -622,6 +640,8 @@ static void rockchip_vpu981_av1_dec_set_
 			*dst++ = (end >> 16) & 255;
 			*dst++ = (end >> 24) & 255;
 		}
+		if (dst + 16 > dst_end)
+			break;
 	}
 
 	hantro_reg_write(vpu, &av1_multicore_expect_context_update, !!(context_update_x == 0));
author maintainer
#1066 lore raw

PATCH 1065/1250 media: verisilicon: rockchip: reject AV1 frames exceeding the tile capacity

In reply to Greg Kroah-Hartman

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

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

From: Michael Bommarito <[email protected]>

commit 367db8b23c26a913d76ed70457bbcd781c422b49 upstream.

rockchip_vpu981_av1_dec_set_tile_info() indexes the tile group entry array by tile1 * tile_cols + tile0, reading up to tile_cols * tile_rows entries, lays out one descriptor per tile in the AV1_MAX_TILES tile_info buffer, and programs the real tile_cols / tile_rows into the hardware.

The tile group entry control is a dynamic array sized to the number of entries userspace submitted, independent of tile_cols / tile_rows, so a frame that claims more tiles than entries reads past the array. A frame that claims more than AV1_MAX_TILES tiles also leaves the hardware programmed for more tiles than the descriptor buffer holds.

Reject both in prepare_run(): tile_cols * tile_rows must not exceed the submitted entry count or AV1_MAX_TILES. The entry count is read via v4l2_ctrl_find() (ctrl->elems). This mirrors the bound the mediatek AV1 decoder already enforces.

Fixes 727a400686a2 ("media: verisilicon: Add Rockchip AV1 decoder")
Assisted-by Claude:claude-opus-4-8
---
 drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c |   25 ++++++++--
 1 file changed, 22 insertions(+), 3 deletions(-)
drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c +22 -3
@@ -431,20 +431,39 @@ static int rockchip_vpu981_av1_dec_prepa
 {
 	struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
 	struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+	const struct v4l2_av1_tile_info *tile_info;
+	struct v4l2_ctrl *tge;
+	u32 num_tiles;
 
 	ctrls->sequence = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE);
 	if (WARN_ON(!ctrls->sequence))
 		return -EINVAL;
 
-	ctrls->tile_group_entry =
-	    hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
-	if (WARN_ON(!ctrls->tile_group_entry))
+	tge = v4l2_ctrl_find(&ctx->ctrl_handler,
+			     V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
+	if (WARN_ON(!tge))
 		return -EINVAL;
+	ctrls->tile_group_entry = tge->p_cur.p;
 
 	ctrls->frame = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FRAME);
 	if (WARN_ON(!ctrls->frame))
 		return -EINVAL;
 
+	/*
+	 * rockchip_vpu981_av1_dec_set_tile_info() indexes the tile group
+	 * entry array by tile1 * tile_cols + tile0, so it reads up to
+	 * tile_cols * tile_rows entries, and lays out one descriptor per tile
+	 * in the AV1_MAX_TILES tile_info buffer while programming the real
+	 * tile geometry into the hardware. Reject a frame that claims more
+	 * tiles than userspace submitted, or more than the hardware tile
+	 * buffer holds, so the read stays in bounds and the programmed
+	 * geometry matches the descriptors written.
+	 */
+	tile_info = &ctrls->frame->tile_info;
+	num_tiles = (u32)tile_info->tile_cols * tile_info->tile_rows;
+	if (num_tiles > tge->elems || num_tiles > AV1_MAX_TILES)
+		return -EINVAL;
+
 	ctrls->film_grain =
 	    hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FILM_GRAIN);
 
author maintainer
#1067 lore raw

PATCH 1066/1250 media: v4l2-ctrls: validate HEVC tile counts

In reply to Greg Kroah-Hartman

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

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

From: Michael Bommarito <[email protected]>

commit dc694a9929f7cb9c88ef91e45eb982b7bbe5a477 upstream.

The stateless HEVC decoders read num_tile_columns_minus1 + 1 entries from column_width_minus1[] and num_tile_rows_minus1 + 1 from row_height_minus1[] and use them as tile-loop bounds, but std_validate_compound() does not bound these u8 counts. Reject a V4L2_CTRL_TYPE_HEVC_PPS with tiling enabled whose tile counts exceed the uAPI array capacity, mirroring the existing compound-control range checks.

Fixes 256fa3920874 ("media: v4l: Add definitions for HEVC stateless decoding")
Assisted-by Claude:claude-opus-4-8
---
 drivers/media/v4l2-core/v4l2-ctrls-core.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/media/v4l2-core/v4l2-ctrls-core.c +12 -0
@@ -1154,6 +1154,18 @@ static int std_validate_compound(const s
 
 			p_hevc_pps->flags &=
 				~V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED;
+		} else {
+			/*
+			 * These count the entries the stateless HEVC drivers
+			 * read from column_width_minus1[] / row_height_minus1[]
+			 * and use as tile-loop bounds.
+			 */
+			if (p_hevc_pps->num_tile_columns_minus1 >=
+			    ARRAY_SIZE(p_hevc_pps->column_width_minus1))
+				return -EINVAL;
+			if (p_hevc_pps->num_tile_rows_minus1 >=
+			    ARRAY_SIZE(p_hevc_pps->row_height_minus1))
+				return -EINVAL;
 		}
 
 		if (p_hevc_pps->flags &
author maintainer
#1068 lore raw

PATCH 1067/1250 media: v4l2-ctrls: validate AV1 tile counts

In reply to Greg Kroah-Hartman

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

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

From: Michael Bommarito <[email protected]>

commit 439058ced617fbb3febc017b9e93bb7387f309e0 upstream.

The stateless AV1 decoders use tile_info.tile_cols and tile_rows as loop bounds and as indices into the mi_*_starts[] and *_in_sbs_minus_1[] arrays, as the divisor for context_update_tile_id, and their product bounds the per-tile descriptor buffers, but std_validate_compound() does not bound these u8 fields. Reject a V4L2_CTRL_TYPE_AV1_FRAME whose tile_cols or tile_rows exceeds V4L2_AV1_MAX_TILE_COLS / _ROWS, or whose product exceeds V4L2_AV1_MAX_TILE_COUNT. A zero tile count is left to the consuming driver so the zero-initialised control that existing userspace submits is still accepted.

Fixes 9de30f579980 ("media: Add AV1 uAPI")
Assisted-by Claude:claude-opus-4-8
---
 drivers/media/v4l2-core/v4l2-ctrls-core.c |   20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
drivers/media/v4l2-core/v4l2-ctrls-core.c +20 -0
@@ -778,10 +778,30 @@ static int validate_av1_film_grain(struc
 	return 0;
 }
 
+static int validate_av1_tile_info(struct v4l2_av1_tile_info *t)
+{
+	/*
+	 * tile_cols and tile_rows index the per-tile descriptor arrays and
+	 * bound the tile loops in the stateless AV1 drivers; the product
+	 * bounds the total tile descriptor count.
+	 */
+	if (t->tile_cols > V4L2_AV1_MAX_TILE_COLS ||
+	    t->tile_rows > V4L2_AV1_MAX_TILE_ROWS)
+		return -EINVAL;
+
+	if ((u32)t->tile_cols * t->tile_rows > V4L2_AV1_MAX_TILE_COUNT)
+		return -EINVAL;
+
+	return 0;
+}
+
 static int validate_av1_frame(struct v4l2_ctrl_av1_frame *f)
 {
 	int ret = 0;
 
+	ret = validate_av1_tile_info(&f->tile_info);
+	if (ret)
+		return ret;
 	ret = validate_av1_quantization(&f->quantization);
 	if (ret)
 		return ret;
author maintainer
#1069 lore raw

PATCH 1068/1250 bnxt_en: Only restore LRO if the device supports TPA

In reply to Greg Kroah-Hartman

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

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

From: Joe Damato <[email protected]>

commit 4e17b5007b6664559cdad2b2fe270526cf786b5b upstream.

With a P5+ device with firmware that reports max_aggs_supported == 0, it is possible to make LRO settable by attaching and detaching an XDP program even though the device does not support TPA.

Fix this by testing BNXT_SUPPORTS_TPA before restoring the feature bit.

---
 drivers/net/ethernet/broadcom/bnxt/bnxt.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +2 -1
@@ -4900,7 +4900,8 @@ void bnxt_set_rx_skb_mode(struct bnxt *b
 		bnxt_get_max_rings(bp, &rx, &tx, true);
 		if (rx > 1) {
 			bp->flags &= ~BNXT_FLAG_NO_AGG_RINGS;
-			bp->dev->hw_features |= NETIF_F_LRO;
+			if (BNXT_SUPPORTS_TPA(bp))
+				bp->dev->hw_features |= NETIF_F_LRO;
 		}
 	}
 
author maintainer
#1070 lore raw

PATCH 1069/1250 bnxt_en: Dont free the live rings TPA state on queue restart failure

In reply to Greg Kroah-Hartman

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

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

From: Joe Damato <[email protected]>

commit 5ce7f36c334d723954855ac769ede2fe0e8f89c8 upstream.

bnxt_queue_mem_alloc() shallow copies the live RX ring into the clone:

  memcpy(clone, rxr, sizeof(*rxr));

the code currently clears pointers that the clone owns (such as rx_agg_bmap), but rx_tpa and rx_tpa_idx_map are left pointing at memory of the live ring that was cloned.

If an allocation failure happens later and the err_free_tpa_info label is taken, the live ring's memory can be freed while still in use.

Fix this by initializing the clone's pointers to NULL to prevent live ring state from being freed inadvertently.

---
 drivers/net/ethernet/broadcom/bnxt/bnxt.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +2 -0
@@ -16054,6 +16054,8 @@ static int bnxt_queue_mem_alloc(struct n
 	clone->need_head_pool = false;
 	clone->rx_page_size = qcfg->rx_page_size;
 	clone->rx_agg_bmap = NULL;
+	clone->rx_tpa = NULL;
+	clone->rx_tpa_idx_map = NULL;
 
 	rc = bnxt_alloc_rx_page_pool(bp, clone, rxr->page_pool->p.nid);
 	if (rc)
author maintainer
#1071 lore raw

PATCH 1070/1250 bnxt_en: Propagate RX ring init failures in bnxt_init_nic()

In reply to Greg Kroah-Hartman

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

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

From: Joe Damato <[email protected]>

commit 8e6a850c0746bb4be167aedf1ee57469fcda09a9 upstream.

bnxt_init_rx_rings() returns an error when bnxt_alloc_one_rx_ring() fails, but bnxt_init_nic() discards that return value and calls bnxt_init_chip(), which enables TPA.

If an allocation fails, this could leave rxr->rx_tpa[] partially zeroed and TPA would be enabled over an array with zeroed entries. This would lead to a zeroed DMA address being handed out if the agg_idx is translated to a SW index at a zeroed entry.

Fix this by propagating the error out of bnxt_init_nic(). Both callers already check its return value and unwind with bnxt_free_skbs() and bnxt_free_mem(), which tolerate a partially initialized RX ring.

---
 drivers/net/ethernet/broadcom/bnxt/bnxt.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +6 -1
@@ -11192,8 +11192,13 @@ static int bnxt_shutdown_nic(struct bnxt
 
 static int bnxt_init_nic(struct bnxt *bp, bool irq_re_init)
 {
+	int rc;
+
 	bnxt_init_cp_rings(bp);
-	bnxt_init_rx_rings(bp);
+	rc = bnxt_init_rx_rings(bp);
+	if (rc)
+		return rc;
+
 	bnxt_init_tx_rings(bp);
 	bnxt_init_ring_grps(bp, irq_re_init);
 	bnxt_init_vnics(bp);
author maintainer
#1072 lore raw

PATCH 1071/1250 bnxt_en: Propagate TPA buffer allocation failures in bnxt_queue_mem_alloc()

In reply to Greg Kroah-Hartman

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

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

From: Joe Damato <[email protected]>

commit b814dfbfeb0a68c9a52073f2caa05a2d5247a329 upstream.

bnxt_alloc_one_tpa_info_data() returns -ENOMEM as soon as one allocation fails. This leaves the remaining rxr->rx_tpa[] entries zeroed.

bnxt_queue_mem_alloc() discards that return value, so the partially initialized ring is installed by bnxt_queue_start().

Since the agg_id is picked by the hardware and bnxt_alloc_agg_idx maps it to a SW index in rxr->rx_tpa[], it is possible that an uninitialized slot can be chosen which would hand a zero DMA address to the device.

Fix this by checking the return value of bnxt_alloc_one_tpa_info_data and unwinding, freeing the ring buffers.

---
 drivers/net/ethernet/broadcom/bnxt/bnxt.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/net/ethernet/broadcom/bnxt/bnxt.c +7 -2
@@ -16104,11 +16104,16 @@ static int bnxt_queue_mem_alloc(struct n
 	bnxt_alloc_one_rx_ring_skb(bp, clone, idx);
 	if (bp->flags & BNXT_FLAG_AGG_RINGS)
 		bnxt_alloc_one_rx_ring_netmem(bp, clone, idx);
-	if (bp->flags & BNXT_FLAG_TPA)
-		bnxt_alloc_one_tpa_info_data(bp, clone);
+	if (bp->flags & BNXT_FLAG_TPA) {
+		rc = bnxt_alloc_one_tpa_info_data(bp, clone);
+		if (rc)
+			goto err_free_rx_ring_skbs;
+	}
 
 	return 0;
 
+err_free_rx_ring_skbs:
+	bnxt_free_one_rx_ring_skbs(bp, clone);
 err_free_tpa_info:
 	bnxt_free_one_tpa_info(bp, clone);
 err_free_rx_agg_ring:
author maintainer
#1073 lore raw

PATCH 1072/1250 mptcp: options: handle MPC data + csum reqd + no csum

In reply to Greg Kroah-Hartman

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

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

From: Matthieu Baerts (NGI0) <[email protected]>

commit ab36b1a80942c78ddb04d006ff38aa7ed3ec0e5e upstream.

Before this modification, a remote peer could send an MP_CAPABLE with data, with the checksum flag set, but without adding the actual 2 bytes of checksum. As a result, uninitialised bytes could be used for the 'csum' field.

That was not a critical issue, because this 'csum' field is only used to compare with the expected one, if previously negotiated in the 3WHS. Worst case, the checksum is likely wrong, a fallback is done without a reject if the negotiation was done earlier. That's OK.

Yet, better to take the expected path with this case: only look at the checksum flag for MP_CAPABLEs not carrying a data-len.

Such packet can be seen as a 3rd or 4th ACK. The RFC8684 mentions [1] that the 3rd packet should have the checksum flag set. When an MPC + ACK contains data, the checksum flag is redundant with the checksum field. It is not clear what should be done for the 4th ACK, nor if the flag has to be set if the checksum field is set.

Therefore, it seems fine to only look at the presence of the checksum field, not to break the interaction with stacks that were not setting both.

Note that linked to this checksum flag on the 3rd ACK, with the current implementation, we can have a situation where the SYN packets have no checksum flag, but the 3rd ACK has one, and this is the one that will be taken into account. First, that's clearly not directly linked to this patch, but Clashiko forced us to look at that. At the end, that seems fine to act like that: yes that's not how the negotiation should work, but being flexible without introducing side effects is also fine: fixing this would mean increasing the complexity, and that's not worth it.

---
 net/mptcp/options.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
net/mptcp/options.c +2 -1
@@ -93,7 +93,8 @@ static void mptcp_parse_option(const str
 		 * In other words, the only way for checksums not to be used
 		 * is if both hosts in their SYNs set A=0."
 		 */
-		if (flags & MPTCP_CAP_CHECKSUM_REQD)
+		if ((flags & MPTCP_CAP_CHECKSUM_REQD) &&
+		    opsize < TCPOLEN_MPTCP_MPC_ACK_DATA)
 			mp_opt->suboptions |= OPTION_MPTCP_CSUMREQD;
 
 		mp_opt->deny_join_id0 = !!(flags & MPTCP_CAP_DENY_JOIN_ID0);
author maintainer
#1074 lore raw

PATCH 1073/1250 mptcp: subflow: no need to copy thmac during ulp_clone

In reply to Greg Kroah-Hartman

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

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

From: Matthieu Baerts (NGI0) <[email protected]>

commit 29f641951be0d91036d77edf677807f1447dbe65 upstream.

'thmac' is not used after that point.

Indeed, subflow_ulp_clone() is called when the request on the passive side is over, so when the truncated HMAC is no longer needed.

Note that in case of SYN cookies, thmac will not be initialised. So better to remove it to avoid a warning from debug tools like KMSAN for reading uninitialised data.

---
 net/mptcp/subflow.c |    1 -
 1 file changed, 1 deletion(-)
net/mptcp/subflow.c +0 -1
@@ -2065,7 +2065,6 @@ static void subflow_ulp_clone(const stru
 		new_ctx->request_bkup = subflow_req->request_bkup;
 		WRITE_ONCE(new_ctx->remote_id, subflow_req->remote_id);
 		new_ctx->token = subflow_req->token;
-		new_ctx->thmac = subflow_req->thmac;
 
 		/* the subflow req id is valid, fetched via subflow_check_req()
 		 * and subflow_token_join_request()
author maintainer
#1075 lore raw

PATCH 1074/1250 mptcp: syncookies: remember the request backup flag

In reply to Greg Kroah-Hartman

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

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

From: Matthieu Baerts (NGI0) <[email protected]>

commit b76c0e28b392620dfbaf92cdeedbf115820b44cb upstream.

Instead of using an uninitialised bit when copying the info in subflow_ulp_clone().

To fix this, no need to extend the join_entry structure: backup is coming from struct mptcp_subflow_request_sock, only one bit. Do the same here by using one bit for both.

---
 net/mptcp/syncookies.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
net/mptcp/syncookies.c +4 -1
@@ -26,7 +26,8 @@ struct join_entry {
 	u32 local_nonce;
 	u8 join_id;
 	u8 local_id;
-	u8 backup;
+	u8 backup:1,
+	   request_bkup:1;
 	u8 valid;
 };
 
@@ -63,6 +64,7 @@ static void mptcp_join_store_state(struc
 	entry->remote_nonce = subflow_req->remote_nonce;
 	entry->local_nonce = subflow_req->local_nonce;
 	entry->backup = subflow_req->backup;
+	entry->request_bkup = subflow_req->request_bkup;
 	entry->join_id = subflow_req->remote_id;
 	entry->local_id = subflow_req->local_id;
 	entry->valid = 1;
@@ -117,6 +119,7 @@ bool mptcp_token_join_cookie_init_state(
 	subflow_req->remote_nonce = e->remote_nonce;
 	subflow_req->local_nonce = e->local_nonce;
 	subflow_req->backup = e->backup;
+	subflow_req->request_bkup = e->request_bkup;
 	subflow_req->remote_id = e->join_id;
 	subflow_req->local_id = e->local_id;
 	subflow_req->token = e->token;
author maintainer
#1076 lore raw

PATCH 1075/1250 selftests: mptcp: fix an UAF in mptcp_connect.c

In reply to Greg Kroah-Hartman

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

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

From: Gang Yan <[email protected]>

commit 730444f094b12052916ebd7e14fe57bc3d47bf38 upstream.

At the end of 'sock_connect_mptcp()', it calls 'freeaddrinfo(addr)', the 'peer' pointer (which points into 'addr') remains. Later, the main loop uses this peer pointer for reconnection attempts. If the memory has been freed and reused, the address data could be overwritten, resulting in an invalid remote address.

This patch keeps the addrinfo list allocated for the whole process lifetime so "peer" remains valid across reconnects; the memory will be released at exit() time.

---
 tools/testing/selftests/net/mptcp/mptcp_connect.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
tools/testing/selftests/net/mptcp/mptcp_connect.c +3 -1
@@ -379,6 +379,9 @@ static int sock_connect_mptcp(const char
 
 	hints.ai_family = pf;
 
+	/* Keep the resolved address alive for the whole execution: it is
+	 * used again when reconnecting, and will be released at exit time.
+	 */
 	xgetaddrinfo(remoteaddr, port, &hints, &addr);
 	for (a = addr; a; a = a->ai_next) {
 		sock = socket(a->ai_family, a->ai_socktype, proto);
@@ -422,7 +425,6 @@ static int sock_connect_mptcp(const char
 		}
 	}
 
-	freeaddrinfo(addr);
 	if (sock != -1)
 		SOCK_TEST_TCPULP(sock, proto);
 	return sock;
author maintainer
#1077 lore raw

PATCH 1076/1250 mptcp: pm: kernel: drop pending ADD_ADDR when removing ID0

In reply to Greg Kroah-Hartman

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

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

From: Kalpan Jani <[email protected]>

commit 2ac7d6e620764f1fc79eb4edd3610a7a661981ca upstream.

The in-kernel MPTCP path manager can leave a stale ADD_ADDR announcement entry alive when removing the id 0 endpoint. This happens because the id 0 removal path does not tear down pending announcements, unlike the non-zero id path.

When the PM later reselects id 0 after adding another signal endpoint, it finds the stale anno_list entry and hits WARN_ON_ONCE(mptcp_pm_is_kernel()) in mptcp_pm_announced_alloc().

Root cause: asymmetry between removal paths.
- Non-zero id path: mptcp_nl_remove_subflow_and_signal_addr() calls mptcp_pm_remove_announced() to clean up.
- Id 0 path: mptcp_nl_remove_id_zero_address() skips cleanup entirely.

Fix by making the id 0 path symmetric: call mptcp_pm_announced_remove() and decrement add_addr_signaled before queuing the RM_ADDR.

Subtle detail: signal endpoints are stored in anno_list with port 0, but msk_local carries the connection's local port. In other words, entries linked to ID0 paths should have port == 0. A follow-up patch will ensure that. mptcp_pm_announced_remove() uses use_port=true for comparison. So clear the port before the lookup.

---
 net/mptcp/pm_kernel.c |    8 ++++++++
 1 file changed, 8 insertions(+)
net/mptcp/pm_kernel.c +8 -0
@@ -1113,6 +1113,8 @@ static int mptcp_nl_remove_id_zero_addre
 	while ((msk = mptcp_token_iter_next(net, &s_slot, &s_num)) != NULL) {
 		struct sock *sk = (struct sock *)msk;
 		struct mptcp_addr_info msk_local;
+		struct mptcp_addr_info anno_addr;
+		bool announced;
 
 		if (list_empty(&msk->conn_list) || mptcp_pm_is_userspace(msk))
 			goto next;
@@ -1122,7 +1124,13 @@ static int mptcp_nl_remove_id_zero_addre
 			goto next;
 
 		lock_sock(sk);
+		/* Drop a possibly pending ADD_ADDR for this address. */
+		anno_addr = msk_local;
+		anno_addr.port = 0;
+		announced = mptcp_pm_announced_remove(msk, &anno_addr);
 		spin_lock_bh(&msk->pm.lock);
+		if (announced)
+			msk->pm.add_addr_signaled--;
 		mptcp_pm_remove_addr(msk, &list);
 		mptcp_pm_rm_subflow(msk, &list);
 		__mark_subflow_endp_available(msk, 0);
author maintainer
#1078 lore raw

PATCH 1077/1250 mptcp: pm: userspace: fix address ID overflow

In reply to Greg Kroah-Hartman

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

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

From: Qing Luo <[email protected]>

commit f9f0068e8813d8c10d016b030fc3a320d0b6767c upstream.

When all MPTCP address IDs (1-255) are exhausted in the userspace PM, find_next_zero_bit() returns MPTCP_PM_MAX_ADDR_ID + 1 (256). This value overflows when stored in the u8 field e->addr.id, resulting in ID 0 being stored and the entry being incorrectly added to the list.

ID 0 is reserved for the initial connection in MPTCP, so this overflow can cause address conflicts.

Note: the in-kernel PM already has an 'endpoints == MPTCP_PM_MAX_ADDR_ID' check in mptcp_pm_nl_append_new_local_addr() that returns -ERANGE before reaching find_next_zero_bit(), preventing this overflow. So this fix only addresses the userspace PM path.

Check the find_next_zero_bit() result against MPTCP_PM_MAX_ADDR_ID and return -ENOSPC if all IDs are truly exhausted. Move the ID allocation check before the memory allocation so that the error path does not need to free the allocated entry.

---
 net/mptcp/pm_userspace.c |   18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
net/mptcp/pm_userspace.c +14 -4
@@ -69,6 +69,19 @@ static int mptcp_userspace_pm_append_new
 	}
 
 	if (!addr_match && !id_match) {
+		unsigned int id;
+
+		if (!entry->addr.id && needs_id) {
+			id = find_next_zero_bit(id_bitmap,
+						MPTCP_PM_MAX_ADDR_ID + 1, 1);
+			if (id > MPTCP_PM_MAX_ADDR_ID) {
+				ret = -ENOSPC;
+				goto append_err;
+			}
+		} else {
+			id = entry->addr.id;
+		}
+
 		/* Memory for the entry is allocated from the
 		 * sock option buffer.
 		 */
@@ -78,10 +91,7 @@ static int mptcp_userspace_pm_append_new
 			goto append_err;
 		}
 
-		if (!e->addr.id && needs_id)
-			e->addr.id = find_next_zero_bit(id_bitmap,
-							MPTCP_PM_MAX_ADDR_ID + 1,
-							1);
+		e->addr.id = id;
 		list_add_tail_rcu(&e->list, &msk->pm.userspace_pm_local_addr_list);
 		msk->pm.local_addr_used++;
 		ret = e->addr.id;
author maintainer
#1079 lore raw

PATCH 1078/1250 smb: client: reject userspace cifs.idmap descriptions

In reply to Greg Kroah-Hartman

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

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

From: Aohan Mei <[email protected]>

commit d9d7eeb0cea5b55b82888f443622fd8d4ee064f3 upstream.

cifs.idmap key descriptions carry authority-bearing fields (owner and group SIDs and uid/gid values in "os:"/"gs:"/"oi:"/"gi:" form) that the cifs.idmap upcall helper treats as kernel-originating inputs. Unlike its sibling cifs.spnego, the cifs.idmap key type has no vet_description hook, so userspace can create keys of this type through request_key(2)/add_key(2) and supply those fields without CIFS origin. A request_key(2) call with a non-NULL callout then drives a root usermodehelper upcall (/sbin/request-key -> cifs.idmap) that consumes the unvetted description in root context.

Only accept cifs.idmap descriptions while CIFS is using its private root_cred to request the key. id_to_sid()/sid_to_id() already run under override_creds(root_cred), so the kernel-originated path is unaffected.

This mirrors commit 3da1fdf4efbc ("smb: client: reject userspace cifs.spnego descriptions"), which applied the same restriction to cifs.spnego.

Fixes 4d79dba0e007 ("cifs: Add idmap key and related data structures and functions (try #17 repost)")
Assisted-by CodeBuddy:Kimi-K3
---
 fs/smb/client/cifsacl.c |   15 +++++++++++++++
 1 file changed, 15 insertions(+)
fs/smb/client/cifsacl.c +15 -0
@@ -101,8 +101,23 @@ cifs_idmap_key_destroy(struct key *key)
 		kfree(key->payload.data[0]);
 }
 
+static int
+cifs_idmap_key_vet_description(const char *description)
+{
+	/*
+	 * cifs.idmap descriptions are authority-bearing inputs to the
+	 * cifs.idmap upcall helper.  Only allow the kernel to create this
+	 * type of key using the private root_cred installed in
+	 * init_cifs_idmap; reject userspace request_key(2)/add_key(2).
+	 */
+	if (current_cred() != root_cred)
+		return -EPERM;
+	return 0;
+}
+
 static struct key_type cifs_idmap_key_type = {
 	.name        = "cifs.idmap",
+	.vet_description = cifs_idmap_key_vet_description,
 	.instantiate = cifs_idmap_key_instantiate,
 	.destroy     = cifs_idmap_key_destroy,
 	.describe    = user_describe,
author maintainer
#1080 lore raw

PATCH 1079/1250 smb: client: reject short READ responses in CIFSSMBRead()

In reply to Greg Kroah-Hartman

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

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

From: Diego Oliva <[email protected]>

commit e6142a8bfc230c7263eb8b0475249c958ce49367 upstream.

CIFSSMBRead() reads DataLengthHigh, DataLength and DataOffset out of the READ_RSP returned by the server without first checking that a whole READ_RSP was actually received. The length of the response is recorded in rsp_iov.iov_len, but nothing constrains it to be at least read_rsp_size before those fields are dereferenced.

A malicious or compromised SMB1 server can return a response shorter than the READ_RSP header, so that parsing the header itself reads past the end of the receive buffer. SMB1 is not negotiated by default; reaching this code requires an explicit vers=1.0 mount.

Reject the response unless it is at least read_rsp_size bytes long.

Fixes 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc [email protected] # 6.19.x
Assisted-by Bynario AI
---
 fs/smb/client/cifssmb.c |    8 ++++++++
 1 file changed, 8 insertions(+)
fs/smb/client/cifssmb.c +8 -0
@@ -1693,6 +1693,14 @@ CIFSSMBRead(const unsigned int xid, stru
 	pSMBr = (READ_RSP *)rsp_iov.iov_base;
 	if (rc) {
 		cifs_dbg(VFS, "Send error in read = %d\n", rc);
+	} else if (rsp_iov.iov_len < tcon->ses->server->vals->read_rsp_size) {
+		/* check that the received response can hold a whole READ_RSP */
+		cifs_dbg(FYI, "%s: server returned short header. got=%zu expected=%zu\n",
+			 __func__, rsp_iov.iov_len,
+			 tcon->ses->server->vals->read_rsp_size);
+		rc = smb_EIO2(smb_eio_trace_read_rsp_short,
+			      rsp_iov.iov_len, tcon->ses->server->vals->read_rsp_size);
+		*nbytes = 0;
 	} else {
 		int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
 		data_length = data_length << 16;
author maintainer
#1081 lore raw

PATCH 1080/1250 smb: client: pin DFS superblock in iterator callback

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

commit d806d5a85dcbe2a0f181b2f0f9f61ddfbefa1818 upstream.

tcon_super_cb() stores a raw superblock pointer, but __cifs_get_super() takes its active reference only after iterate_supers_type() has dropped s_umount and its passive reference. Concurrent DFS automount expiry can therefore free the superblock before cifs_sb_active() uses it.

A deterministic KASAN test reproduces the race as:

  BUG: KASAN: slab-use-after-free in cifs_sb_active+0x77/0x80

The same test passes with this change applied.

Take the active reference in the callback while iterate_supers_type() still holds s_umount shared. cifs_put_tcp_super() remains the matching release.

Fixes bacd704a95ad ("cifs: handle prefix paths in reconnect")
Assisted-by LLM
---
 fs/smb/client/misc.c |   17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)
fs/smb/client/misc.c +8 -9
@@ -1210,8 +1210,14 @@ static void tcon_super_cb(struct super_b
 	     t1->ses->dfs_root_ses == t2->ses->dfs_root_ses) &&
 	    t1->ses->server == t2->ses->server &&
 	    t2->origin_fullpath &&
-	    dfs_src_pathname_equal(t2->origin_fullpath, t1->origin_fullpath))
+	    dfs_src_pathname_equal(t2->origin_fullpath, t1->origin_fullpath)) {
+		/*
+		 * Take the active reference while iterate_supers_type() still
+		 * holds s_umount shared.
+		 */
+		cifs_sb_active(sb);
 		sd->sb = sb;
+	}
 	spin_unlock(&t2->tc_lock);
 }
 
@@ -1228,15 +1234,8 @@ static struct super_block *__cifs_get_su
 
 	for (; *fs_type; fs_type++) {
 		iterate_supers_type(*fs_type, f, &sd);
-		if (sd.sb) {
-			/*
-			 * Grab an active reference in order to prevent automounts (DFS links)
-			 * of expiring and then freeing up our cifs superblock pointer while
-			 * we're doing failover.
-			 */
-			cifs_sb_active(sd.sb);
+		if (sd.sb)
 			return sd.sb;
-		}
 	}
 	pr_warn_once("%s: could not find dfs superblock\n", __func__);
 	return ERR_PTR(-EINVAL);
author maintainer
#1082 lore raw

PATCH 1081/1250 smb: client: fix one-byte OOB read in smb2_parse_native_symlink()

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit cb26524ef4ac28fcfa554c0656e8dc412c38a8ff upstream.

When parsing a share-root relative native symlink, memcpy copies smb_target+1 (skipping the leading separator) but uses strlen(smb_target)+1 as the length, reading one byte past the allocated buffer.

This fixes the following KASAN splat when accessing an SMB symlink with a target of '\a\b':

  BUG: KASAN: slab-out-of-bounds in smb2_parse_native_symlink+0x4f5/0xca0
  Read of size 5 at addr ffff88800878fe21 by task netfsfuzz-execu/1
  CPU: 1 UID: 0 PID: 1 Comm: netfsfuzz-execu Tainted: G N
  7.2.0-11943-g2709dd5ae32f-dirty #1 PREEMPT(lazy)
  Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix,
  1996)
  Call Trace:
   <TASK>
   dump_stack_lvl+0x7b/0xa0
   print_report+0xd0/0x630
   kasan_report+0xe5/0x120
   kasan_check_range+0x105/0x1b0
   __asan_memcpy+0x23/0x60
   smb2_parse_native_symlink+0x4f5/0xca0
   parse_reparse_point+0x68a/0x1530
   reparse_info_to_fattr+0x752/0xa20
   cifs_get_fattr+0x873/0x15b0
   cifs_get_inode_info+0xc0/0x310
   cifs_lookup+0x308/0xa70
   __lookup_slow+0x122/0x2b0
   lookup_slow+0x50/0x70
   path_lookupat+0x525/0xaf0
   filename_lookup+0x1f2/0x550
   vfs_statx+0xd1/0x1a0
   vfs_fstatat+0x65/0xc0
   __do_sys_newfstatat+0x9a/0x120
   do_syscall_64+0xdd/0x4a0
   entry_SYSCALL_64_after_hwframe+0x77/0x7f
Fixes 723f4ef90452 ("cifs: Fix parsing native symlinks relative to the export")
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
---
 fs/smb/client/reparse.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
fs/smb/client/reparse.c +2 -1
@@ -953,7 +953,8 @@ globalroot:
 			linux_target[i*3 + 1] = '.';
 			linux_target[i*3 + 2] = sep;
 		}
-		memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */
+		/* +1 to skip leading sep */
+		memcpy(linux_target + levels*3, smb_target+1, smb_target_len-1);
 	} else {
 		/*
 		 * This is either an absolute symlink in POSIX-style format
author maintainer
#1083 lore raw

PATCH 1082/1250 smb: client: fix file type corruption in cifs_reparse_point_to_fattr()

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit 6bd360447941357e959414a525aa62576a448116 upstream.

Setting the file type in cf_mode without clearing the existing S_IFMT bits first is wrong as it corrupts the file type when cf_mode already has type bits set (e.g. S_IFREG | S_IFLNK == S_IFDIR | S_IFREG).

Clear S_IFMT before setting S_IFLNK for native and SMB1 symlinks.

---
 fs/smb/client/reparse.c |    1 +
 1 file changed, 1 insertion(+)
fs/smb/client/reparse.c +1 -0
@@ -1243,6 +1243,7 @@ bool cifs_reparse_point_to_fattr(struct
 		break;
 	case 0: /* SMB1 symlink */
 	case IO_REPARSE_TAG_SYMLINK:
+		fattr->cf_mode &= ~S_IFMT;
 		fattr->cf_mode |= S_IFLNK;
 		break;
 	default:
author maintainer
#1084 lore raw

PATCH 1083/1250 smb: client: fix file type corruption in posix_reparse_to_fattr()

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit 65d5dbdc089be42fc48a6f77bc6b648307f34b17 upstream.

Setting the file type in cf_mode without clearing the existing S_IFMT bits first is wrong as it corrupts the file type when cf_mode already has type bits set (e.g. S_IFREG | S_IFCHR == S_IFLNK).

Use a local ftype variable to collect the new file type and apply it after validation succeeds, clearing S_IFMT and setting the new type in a single assignment. This avoids stripping cf_mode on malformed reparse points where the function returns false early.

---
 fs/smb/client/reparse.c |   12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
fs/smb/client/reparse.c +7 -5
@@ -1177,6 +1177,7 @@ static bool posix_reparse_to_fattr(struc
 				   struct cifs_open_info_data *data)
 {
 	struct reparse_nfs_data_buffer *buf = (struct reparse_nfs_data_buffer *)data->reparse.buf;
+	umode_t ftype;
 
 	if (buf == NULL)
 		return true;
@@ -1192,7 +1193,7 @@ static bool posix_reparse_to_fattr(struc
 			WARN_ON_ONCE(1);
 			return false;
 		}
-		fattr->cf_mode |= S_IFCHR;
+		ftype = S_IFCHR;
 		fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
 		break;
 	case NFS_SPECFILE_BLK:
@@ -1200,22 +1201,23 @@ static bool posix_reparse_to_fattr(struc
 			WARN_ON_ONCE(1);
 			return false;
 		}
-		fattr->cf_mode |= S_IFBLK;
+		ftype = S_IFBLK;
 		fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
 		break;
 	case NFS_SPECFILE_FIFO:
-		fattr->cf_mode |= S_IFIFO;
+		ftype = S_IFIFO;
 		break;
 	case NFS_SPECFILE_SOCK:
-		fattr->cf_mode |= S_IFSOCK;
+		ftype = S_IFSOCK;
 		break;
 	case NFS_SPECFILE_LNK:
-		fattr->cf_mode |= S_IFLNK;
+		ftype = S_IFLNK;
 		break;
 	default:
 		WARN_ON_ONCE(1);
 		return false;
 	}
+	fattr->cf_mode = (fattr->cf_mode & ~S_IFMT) | ftype;
 	return true;
 }
 
author maintainer
#1085 lore raw

PATCH 1084/1250 smb: client: fix file type corruption in wsl_to_fattr()

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit fa7a2cfcf1e6117fc478cae6809c66c518740969 upstream.

Setting the file type in cf_mode without clearing the existing S_IFMT bits first is wrong as it corrupts the file type when cf_mode already has type bits set (e.g. S_IFREG | S_IFCHR == S_IFLNK).

Clear S_IFMT before the switch statement.

---
 fs/smb/client/reparse.c |    1 +
 1 file changed, 1 insertion(+)
fs/smb/client/reparse.c +1 -0
@@ -1113,6 +1113,7 @@ static bool wsl_to_fattr(struct cifs_ope
 	bool have_xattr_dev = false;
 	u32 next = 0;
 
+	fattr->cf_mode &= ~S_IFMT;
 	switch (tag) {
 	case IO_REPARSE_TAG_LX_SYMLINK:
 		fattr->cf_mode |= S_IFLNK;
author maintainer
#1086 lore raw

PATCH 1085/1250 smb: client: avoid leaking refcount in cifs_queue_oplock_break()

In reply to Greg Kroah-Hartman

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

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

From: Bjoern Doebel <[email protected]>

commit 9f2e63f1b2d5fc5b5423424902c091123e220e7e upstream.

cifs_queue_oplock_break() unconditionally takes a reference on the target file before queueing cifs_oplock_break(). Only that work item decreases the reference counter again.

If another oplock break arrives while that work is still queued, queue_work() will return false and not queue this second work item. As a result, we will never reach the point to drop the file reference again and are leaking this reference. This can be triggered when interacting with a slow-responding server.

As a result, later unmount operations for this file system will fail with

  BUG: Dentry ... still in use (1) [unmount of cifs cifs]
  VFS: Busy inodes after unmount of cifs (cifs)
  kernel BUG at fs/super.c:777!

Fix this by only incrementing the reference count if the work has been queued successfully. Taking it after queue_work() is safe because all three callers hold tcon->open_file_lock across the call and _cifsFileInfo_put() decrements under that same lock, so a worker that starts the handler in the window cannot drop the reference before it has been taken.

Fixes b98749cac4a69 ("CIFS: keep FileInfo handle live during oplock break")
Assisted-by Kiro:claude-opus-5
---
 fs/smb/client/misc.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
fs/smb/client/misc.c +4 -3
@@ -658,10 +658,11 @@ void cifs_queue_oplock_break(struct cifs
 	 * open_file_lock to enforce the validity of it for the oplock
 	 * break handler. The matching put is done at the end of the
 	 * handler.
+	 *
+	 * Only take a reference if the work is actually queued.
 	 */
-	cifsFileInfo_get(cfile);
-
-	queue_work(cifsoplockd_wq, &cfile->oplock_break);
+	if (queue_work(cifsoplockd_wq, &cfile->oplock_break))
+		cifsFileInfo_get(cfile);
 }
 
 void cifs_done_oplock_break(struct cifsInodeInfo *cinode)
author maintainer
#1087 lore raw

PATCH 1086/1250 smb: client: avoid leaking refcount when cifs_sb_tlink() fails

In reply to Greg Kroah-Hartman

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

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

From: Bjoern Doebel <[email protected]>

commit 23b26f4408ac3f35a482d2e5cf6fc865d4201b71 upstream.

cifs_oplock_break() takes over the reference that cifs_queue_oplock_break() acquired when it queued the work, and drops it with _cifsFileInfo_put() once the break has been processed.

Only in setups with "-o multiuser", cifs_sb_tlink() may fail, at which point cifs_oplock_break() returns without putting the file reference, mirroring the reference leak we already fixed in the companion patch to cifs_queue_oplock_break().

This would trigger a crash due to busy inodes on the next unmount:

  BUG: Dentry ... still in use (1) [unmount of cifs cifs]
  VFS: Busy inodes after unmount of cifs (cifs)

Drop the reference on that path as well. Doing so before the out label mirrors the normal path, which also puts the reference before cifs_done_oplock_break().

Found by Sashiko code review. The failure path was not exercised at runtime.

Fixes e8f5f849ffce2 ("cifs: fix potential oops in cifs_oplock_break")
Assisted-by Kiro:claude-opus-5
---
 fs/smb/client/file.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
fs/smb/client/file.c +4 -1
@@ -3151,8 +3151,11 @@ void cifs_oplock_break(struct work_struc
 			TASK_UNINTERRUPTIBLE);
 
 	tlink = cifs_sb_tlink(cifs_sb);
-	if (IS_ERR(tlink))
+	if (IS_ERR(tlink)) {
+		/* drop the reference taken when the break was queued */
+		_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
 		goto out;
+	}
 	tcon = tlink_tcon(tlink);
 	server = tcon->ses->server;
 
author maintainer
#1088 lore raw

PATCH 1087/1250 smb: client: fix heap overflow in DACL owner/group rewrite

In reply to Greg Kroah-Hartman

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

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

From: Bjoern Doebel <[email protected]>

commit 0ee150794c75bcd0be0e24ff3394f433cbae18cc upstream.

When id_mode_to_cifs_acl rewrites an existing DACL, it allocates a buffer sized according to the on-disk DACL length reported by dacl_ptr->size. However, replace_sids_and_copy_aces may rewrite each ACE with a new owner/group SID obtained from the cifs.idmap upcall. Those SIDs can have up to SID_MAX_SUB_AUTHORITIES (15) sub-authorities, making each ACE up to 76 bytes (sizeof(struct smb_ace)).

If the original DACL contains short SIDs (e.g., 1 sub-authority) while the replacement SIDs are long, the rewritten ACEs overflow the allocation.

Fix this by always budgeting for worst-case SID expansion: allocate sizeof(struct smb_acl) plus num_aces * sizeof(struct smb_ace), which covers the smb_acl header and room for every ACE at maximum SID size. This replaces the previous split logic that used dacl_ptr->size for cifsacl mounts but num_aces * sizeof(struct smb_ace) for mode_from_sid
mounts: both paths can trigger the same rewrite and need the same headroom.

KASAN reports this as:

  BUG: KASAN: slab-out-of-bounds in build_sec_desc+0x1e8a/0x2680 [cifs]
  Write of size 4 at addr ffff8881a5e25374 by task chown/5298
  ...
  The buggy address is located 0 bytes to the right of
   allocated 884-byte region [ffff8881a5e25000, ffff8881a5e25374)
Fixes bc3e9dd9d104 ("cifs: Change SIDs in ACEs while transferring file ownership.")
Assisted-by Kiro:claude-opus-4.6
Fixes 5c3564852c58 ("cifs: Minimize the number of cifs_acl memory allocations")
---
 fs/smb/client/cifsacl.c |   12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
fs/smb/client/cifsacl.c +7 -5
@@ -1770,11 +1770,13 @@ id_mode_to_cifs_acl(struct inode *inode,
 				cifs_put_tlink(tlink);
 				return rc;
 			}
-			if (mode_from_sid)
-				nsecdesclen +=
-					le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
-			else /* cifsacl */
-				nsecdesclen += le16_to_cpu(dacl_ptr->size);
+			/*
+			 * Worst case: every ACE is rewritten with a new SID of
+			 * SID_MAX_SUB_AUTHORITIES sub-auths -> sizeof(smb_ace) each,
+			 * plus the smb_acl header replace_sids_and_copy_aces() emits.
+			 */
+			nsecdesclen += sizeof(struct smb_acl) +
+				le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
 		}
 	}
 
author maintainer
#1089 lore raw

PATCH 1088/1250 xfs: use the rtgroup extent count to find rtrefcount gaps

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit c3085f6c7cca7c162248519ce8d763047cdd8acd upstream.

LOLLM noticed an anachronism from the early days of rtrefcount where the refcount btree would handle 64-bit block numbers -- we pass rtblocks into the gap finder, but rtrefcount btrees are sharded by rtgroup now. This isn't really a problem for us since we're only looking for overlapping rtrmap records to flag, but let's fix this sillyness.

Also fix some stale comments.

Fixes 30f47950dc2eba ("xfs: check reference counts of gaps between rt refcount records")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/rtrefcount.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
fs/xfs/scrub/rtrefcount.c +5 -5
@@ -429,7 +429,7 @@ static inline void
 xchk_rtrefcountbt_xref_gaps(
 	struct xfs_scrub	*sc,
 	struct xchk_rtrefcbt_records *rrc,
-	xfs_rtblock_t		bno)
+	xfs_rgblock_t		bno)
 {
 	struct xfs_rmap_irec	low;
 	struct xfs_rmap_irec	high;
@@ -539,7 +539,7 @@ xchk_refcount_xref_rmap(
 		xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0);
 }
 
-/* Scrub the refcount btree for some AG. */
+/* Scrub the refcount btree for some rtgroup. */
 int
 xchk_rtrefcountbt(
 	struct xfs_scrub	*sc)
@@ -565,10 +565,10 @@ xchk_rtrefcountbt(
 
 	/*
 	 * Check that all blocks between the last refcount > 1 record and the
-	 * end of the rt volume have at most one reverse mapping.
+	 * end of the rtgroup have at most one reverse mapping.
 	 */
-	xchk_rtrefcountbt_xref_gaps(sc, &rrc, sc->mp->m_sb.sb_rblocks);
-
+	xchk_rtrefcountbt_xref_gaps(sc, &rrc,
+			xfs_rtx_to_rgbno(sc->sr.rtg, sc->mp->m_sb.sb_rgextents));
 	xchk_refcount_xref_rmap(sc, &btree_oinfo, rrc.cow_blocks);
 
 	return 0;
author maintainer
#1090 lore raw

PATCH 1089/1250 xfs: truncate quota file correctly when repairing quota file

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 0fc67528f54bb91dac22093749b425207c0fc245 upstream.

LOLLM noticed that xrep_quota_data_fork screws up the unit handling when it computes the offset at which to start truncating the quota file. max_dquid_off is the file block offset containing the highest possible dquot, and xfs_bunmapi_range takes the starting file block offset. Therefore, it makes no sense to multiply max_dquid_off by the blocksize; all we need to do is start truncating at the next block.

Fixes a5b91555403e3a ("xfs: repair quotas")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/quota_repair.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
fs/xfs/scrub/quota_repair.c +1 -2
@@ -457,8 +457,7 @@ xrep_quota_data_fork(
 
 	if (truncate) {
 		/* Erase everything after the block containing the max dquot */
-		error = xfs_bunmapi_range(&sc->tp, sc->ip, 0,
-				max_dqid_off * sc->mp->m_sb.sb_blocksize,
+		error = xfs_bunmapi_range(&sc->tp, sc->ip, 0, max_dqid_off + 1,
 				XFS_MAX_FILEOFF);
 		if (error)
 			goto out;
author maintainer
#1091 lore raw

PATCH 1090/1250 xfs: strengthen the "is cow staging" helpers in scrub

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 0d43368844a75ad13561a1198a3b027940730756 upstream.

LOLLM pointed out a bug in both of the refcount scrub predicates that determine if a range of blocks is marked as CoW staging in the btree. While it compares blockcount < len, this isn't enough to determine that the CoW staging record is at least as large as the range passed into the helper. Fix both of them.

Fixes f6d5fc21fdc713 ("xfs: cross-reference refcount btree during scrub")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/refcount.c   |    6 +++++-
 fs/xfs/scrub/rtrefcount.c |    6 +++++-
 2 files changed, 10 insertions(+), 2 deletions(-)
fs/xfs/scrub/refcount.c +5 -1
@@ -582,8 +582,12 @@ xchk_xref_is_cow_staging(
 	if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
 		xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
 
+	/* Can't start after bno */
+	if (rc.rc_startblock > agbno)
+		xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
+
 	/* Must be at least as long as what was passed in */
-	if (rc.rc_blockcount < len)
+	if (rc.rc_startblock + rc.rc_blockcount < agbno + len)
 		xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
 }
 
fs/xfs/scrub/rtrefcount.c +5 -1
@@ -610,8 +610,12 @@ xchk_xref_is_rt_cow_staging(
 	if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
 		xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
 
+	/* Can't start after bno */
+	if (rc.rc_startblock > bno)
+		xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
+
 	/* Must be at least as long as what was passed in */
-	if (rc.rc_blockcount < len)
+	if (rc.rc_startblock + rc.rc_blockcount < bno + len)
 		xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0);
 }
 
author maintainer
#1092 lore raw

PATCH 1091/1250 xfs: snapshot scrub stats when rendering them

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 568a1588b906780dc3e9be56a61217afb4f7800e upstream.

LOLLM complains about concurrency problems in the scrub stats code because xchk_stats_format doesn't synchronize in any way with updates. These stats are only reported through debugfs so I don't think it really matters, but I guess I exist to make bots happy now.

Note: We snapshot the entire stats object with a spinlock so that we don't have to worry about users seeing slightly weird numbers (e.g. invocations has incremented but none of the outcomes have been yet) if we race with xchk_stats_merge_one. This isn't a hot path.

Fixes d7a74cad8f4513 ("xfs: track usage statistics of online fsck")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/stats.c |   32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)
fs/xfs/scrub/stats.c +19 -13
@@ -99,25 +99,31 @@ xchk_stats_format(
 	int			ret = 0;
 
 	for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {
+		struct xchk_scrub_stats	fss;
+
 		if (!name_map[i])
 			continue;
 
+		spin_lock(&css->css_lock);
+		memcpy(&fss, css, offsetof(struct xchk_scrub_stats, css_lock));
+		spin_unlock(&css->css_lock);
+
 		ret = scnprintf(buf, remaining,
  "%s %u %u %u %u %u %u %u %u %u %llu %u %u %llu\n",
 				name_map[i],
-				(unsigned int)css->invocations,
-				(unsigned int)css->clean,
-				(unsigned int)css->corrupt,
-				(unsigned int)css->preen,
-				(unsigned int)css->xfail,
-				(unsigned int)css->xcorrupt,
-				(unsigned int)css->incomplete,
-				(unsigned int)css->warning,
-				(unsigned int)css->retries,
-				(unsigned long long)css->checktime_us,
-				(unsigned int)css->repair_invocations,
-				(unsigned int)css->repair_success,
-				(unsigned long long)css->repairtime_us);
+				(unsigned int)fss.invocations,
+				(unsigned int)fss.clean,
+				(unsigned int)fss.corrupt,
+				(unsigned int)fss.preen,
+				(unsigned int)fss.xfail,
+				(unsigned int)fss.xcorrupt,
+				(unsigned int)fss.incomplete,
+				(unsigned int)fss.warning,
+				(unsigned int)fss.retries,
+				(unsigned long long)fss.checktime_us,
+				(unsigned int)fss.repair_invocations,
+				(unsigned int)fss.repair_success,
+				(unsigned long long)fss.repairtime_us);
 		if (ret <= 0)
 			break;
 
author maintainer
#1093 lore raw

PATCH 1092/1250 xfs: snapshot old AGFL before rewriting it

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 3466dfef0a20f842363958deea55be9f1d26818a upstream.

LOLLM complains that we can't undo an attempt at fixing the AGFL if anything goes wrong during the rewrite, so take a snapshot of the whole buffer so that we can restore it. Move the xrep_agfl_update_agf call so that we only update the AGF if the AGFL update is 100% successful.

While we're at it, fix leaking the used_extents bitmap if the disunion operation fails.

Fixes 0e93d3f43ec7d3 ("xfs: repair the AGFL")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/agheader_repair.c |   35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)
fs/xfs/scrub/agheader_repair.c +25 -10
@@ -668,14 +668,16 @@ xrep_agfl_init_header(
 	struct xfs_scrub	*sc,
 	struct xfs_buf		*agfl_bp,
 	struct xagb_bitmap	*agfl_extents,
-	xfs_agblock_t		flcount)
+	xfs_agblock_t		flcount,
+	struct xfs_agfl		*old_agfl)
 {
 	struct xrep_agfl_fill	af = {
 		.sc		= sc,
 		.flcount	= flcount,
 	};
 	struct xfs_mount	*mp = sc->mp;
-	struct xfs_agfl		*agfl;
+	struct xfs_agfl		*agfl = XFS_BUF_TO_AGFL(agfl_bp);
+	const size_t		agfl_sz = BBTOB(agfl_bp->b_length);
 	int			error;
 
 	ASSERT(flcount <= xfs_agfl_size(mp));
@@ -684,8 +686,8 @@ xrep_agfl_init_header(
 	 * Start rewriting the header by setting the bno[] array to
 	 * NULLAGBLOCK, then setting AGFL header fields.
 	 */
-	agfl = XFS_BUF_TO_AGFL(agfl_bp);
-	memset(agfl, 0xFF, BBTOB(agfl_bp->b_length));
+	memcpy(old_agfl, agfl, agfl_sz);
+	memset(agfl, 0xFF, agfl_sz);
 	agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
 	agfl->agfl_seqno = cpu_to_be32(pag_agno(sc->sa.pag));
 	uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
@@ -700,13 +702,18 @@ xrep_agfl_init_header(
 	xagb_bitmap_walk(agfl_extents, xrep_agfl_fill, &af);
 	error = xagb_bitmap_disunion(agfl_extents, &af.used_extents);
 	if (error)
-		return error;
+		goto err_undo;
 
 	/* Write new AGFL to disk. */
 	xfs_trans_buf_set_type(sc->tp, agfl_bp, XFS_BLFT_AGFL_BUF);
-	xfs_trans_log_buf(sc->tp, agfl_bp, 0, BBTOB(agfl_bp->b_length) - 1);
+	xfs_trans_log_buf(sc->tp, agfl_bp, 0, agfl_sz - 1);
 	xagb_bitmap_destroy(&af.used_extents);
 	return 0;
+
+err_undo:
+	xagb_bitmap_destroy(&af.used_extents);
+	memcpy(agfl, old_agfl, agfl_sz);
+	return error;
 }
 
 /* Repair the AGFL. */
@@ -718,6 +725,7 @@ xrep_agfl(
 	struct xfs_mount	*mp = sc->mp;
 	struct xfs_buf		*agf_bp;
 	struct xfs_buf		*agfl_bp;
+	struct xfs_agfl		*old_agfl;
 	xfs_agblock_t		flcount;
 	int			error;
 
@@ -725,6 +733,10 @@ xrep_agfl(
 	if (!xfs_has_rmapbt(mp))
 		return -EOPNOTSUPP;
 
+	old_agfl = kzalloc(BBTOB(XFS_FSS_TO_BB(mp, 1)), XCHK_GFP_FLAGS);
+	if (!old_agfl)
+		return -ENOMEM;
+
 	xagb_bitmap_init(&agfl_extents);
 
 	/*
@@ -734,7 +746,7 @@ xrep_agfl(
 	 */
 	error = xfs_alloc_read_agf(sc->sa.pag, sc->tp, 0, &agf_bp);
 	if (error)
-		return error;
+		goto err_old_agfl;
 
 	/*
 	 * Make sure we have the AGFL buffer, as scrub might have decided it
@@ -745,7 +757,7 @@ xrep_agfl(
 						XFS_AGFL_DADDR(mp)),
 			XFS_FSS_TO_BB(mp, 1), 0, &agfl_bp, NULL);
 	if (error)
-		return error;
+		goto err_old_agfl;
 	agfl_bp->b_ops = &xfs_agfl_buf_ops;
 
 	/* Gather all the extents we're going to put on the new AGFL. */
@@ -762,10 +774,11 @@ xrep_agfl(
 	 * we adjust the AGF flcount (which can fail) so avoid updating any
 	 * buffers until we know that part works.
 	 */
-	xrep_agfl_update_agf(sc, agf_bp, flcount);
-	error = xrep_agfl_init_header(sc, agfl_bp, &agfl_extents, flcount);
+	error = xrep_agfl_init_header(sc, agfl_bp, &agfl_extents, flcount,
+			old_agfl);
 	if (error)
 		goto err;
+	xrep_agfl_update_agf(sc, agf_bp, flcount);
 
 	/*
 	 * Ok, the AGFL should be ready to go now.  Roll the transaction to
@@ -785,6 +798,8 @@ xrep_agfl(
 
 err:
 	xagb_bitmap_destroy(&agfl_extents);
+err_old_agfl:
+	kfree(old_agfl);
 	return error;
 }
 
author maintainer
#1094 lore raw

PATCH 1093/1250 xfs: signal inode btree xref error if get_rec returns an error

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 8c71ad4d4f3e20c30b663bd292526fcbc4d3913f upstream.

LOLLM points out that xchk_finobt_xref_inobt and xchk_inobt_xref_finobt both ignore errors being returned from the xfs_btree_get_rec function and proceed with a (possibly stale) "true" value for has_record. If the *simple* btree record checks fail during cross-referencing, we can immediately conclude that there's a cross-referncing error in the other btree. On those grounds, we can bubble up the returned error instead of wasting time cross-referencing with garbage.

Fixes bc0f3b55467e1b ("xfs: directly cross-reference the inode btrees with each other")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/ialloc.c |    4 ++++
 1 file changed, 4 insertions(+)
fs/xfs/scrub/ialloc.c +4 -0
@@ -85,6 +85,8 @@ xchk_inobt_xref_finobt(
 		goto no_record;
 
 	error = xfs_inobt_get_rec(cur, &frec, &has_record);
+	if (error)
+		return error;
 	if (!has_record)
 		return -EFSCORRUPTED;
 
@@ -188,6 +190,8 @@ xchk_finobt_xref_inobt(
 		goto no_record;
 
 	error = xfs_inobt_get_rec(cur, &irec, &has_record);
+	if (error)
+		return error;
 	if (!has_record)
 		return -EFSCORRUPTED;
 
author maintainer
#1095 lore raw

PATCH 1094/1250 xfs: reset parent pointer args before each dir tree unlink repair

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 69e10c2b4a51b4ff3c88a70e90f5180ad58c758f upstream.

LOLLM noticed that xfs_parent_removename only partially initializes the passed-in parent pointer arguments object. In the directory tree repair code, we could decide to remove multiple links to a file, so we don't want state from one call to bleed into the next one. Zero the whole thing explicitly.

Fixes 3f31406aef493b ("xfs: fix corruptions in the directory tree")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/dirtree_repair.c |    1 +
 1 file changed, 1 insertion(+)
fs/xfs/scrub/dirtree_repair.c +1 -0
@@ -479,6 +479,7 @@ again:
 	}
 
 	if (xfs_has_parent(sc->mp)) {
+		memset(&dl->ppargs, 0, sizeof(dl->ppargs));
 		error = xfs_parent_removename(sc->tp, &dl->ppargs, dp,
 				&dl->xname, sc->ip);
 		if (error)
author maintainer
#1096 lore raw

PATCH 1095/1250 xfs: report nonexistent parents as a filesystem corruption

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 014c1aff607a839b8ddc732da919b94560ae58c2 upstream.

LOLLM noticed that when the directory tree scrubber tries to walk up a parent pointer but the parent inumber doesn't point to an allocated inode, we allow the EINVAL/ENOENT error code to bubble up to userspace. That's not right, we should be reporting that as a cross-referencing error so that someone runs the parent pointer checker.

Also add a termination check to xchk_dirpath_step_up because it's a loop body function.

Fixes 928b721a11789a ("xfs: teach online scrub to find directory tree structure problems")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/dirtree.c |   30 ++++++++++++++++++++++++++++--
 fs/xfs/scrub/trace.h   |   33 +++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 2 deletions(-)
fs/xfs/scrub/dirtree.c +28 -2
@@ -368,12 +368,38 @@ xchk_dirpath_step_up(
 	struct xfs_inode	*dp;
 	xfs_ino_t		parent_ino = be64_to_cpu(dl->pptr_rec.p_ino);
 	unsigned int		lock_mode;
-	int			error;
+	int			error = 0;
+
+	if (xchk_should_terminate(sc, &error))
+		return error;
 
 	/* Grab and lock the parent directory. */
 	error = xchk_iget(sc, parent_ino, &dp);
-	if (error)
+	switch (error) {
+	case -EINVAL:
+	case -ENOENT:
+		mutex_lock(&dl->lock);
+
+		if (dl->stale) {
+			/* live update detected a change in this path */
+			error = -ESTALE;
+		} else {
+			/* inode doesn't exist, path invalid */
+			error = -EFSCORRUPTED;
+
+			trace_xchk_dirpath_badino(dl->sc, path->path_nr,
+					path->nr_steps, &dl->xname,
+					&dl->pptr_rec);
+		}
+
+		mutex_unlock(&dl->lock);
+		return error;
+	case 0:
+		/* keep going */
+		break;
+	default:
 		return error;
+	}
 
 	lock_mode = xfs_ilock_attr_map_shared(dp);
 	mutex_lock(&dl->lock);
fs/xfs/scrub/trace.h +33 -0
@@ -1706,6 +1706,39 @@ DEFINE_EVENT(xchk_dirtree_class, name, \
 DEFINE_XCHK_DIRTREE_EVENT(xchk_dirtree_create_path);
 DEFINE_XCHK_DIRTREE_EVENT(xchk_dirpath_walk_upwards);
 
+TRACE_EVENT(xchk_dirpath_badino,
+	TP_PROTO(struct xfs_scrub *sc, unsigned int path_nr,
+		unsigned int step_nr, const struct xfs_name *name,
+		const struct xfs_parent_rec *pptr),
+	TP_ARGS(sc, path_nr, step_nr, name, pptr),
+	TP_STRUCT__entry(
+		__field(dev_t, dev)
+		__field(unsigned int, path_nr)
+		__field(unsigned int, step_nr)
+		__field(xfs_ino_t, parent_ino)
+		__field(unsigned int, parent_gen)
+		__field(unsigned int, namelen)
+		__dynamic_array(char, name, name->len)
+	),
+	TP_fast_assign(
+		__entry->dev = sc->mp->m_super->s_dev;
+		__entry->path_nr = path_nr;
+		__entry->step_nr = step_nr;
+		__entry->parent_ino = be64_to_cpu(pptr->p_ino);
+		__entry->parent_gen = be32_to_cpu(pptr->p_gen);
+		__entry->namelen = name->len;
+		memcpy(__get_str(name), name->name, name->len);
+	),
+	TP_printk("dev %d:%d path %u step %u parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->path_nr,
+		  __entry->step_nr,
+		  __entry->parent_ino,
+		  __entry->parent_gen,
+		  __entry->namelen,
+		  __get_str(name))
+);
+
 DECLARE_EVENT_CLASS(xchk_dirpath_class,
 	TP_PROTO(struct xfs_scrub *sc, struct xfs_inode *ip,
 		 unsigned int path_nr, unsigned int step_nr,
author maintainer
#1097 lore raw

PATCH 1096/1250 xfs: report healthy filesystem events in scrub stats

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 0ae61c331ec552ad0c278c5c48a1c4ccb90b4bab upstream.

LOLLM also notices that I forgot to expose the "clean bill of health" scrub stats. Fix that.

Fixes a1f3e0cca41036 ("xfs: update health status if we get a clean bill of health")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/stats.c |    1 +
 1 file changed, 1 insertion(+)
fs/xfs/scrub/stats.c +1 -0
@@ -84,6 +84,7 @@ static const char *name_map[XFS_SCRUB_TY
 	[XFS_SCRUB_TYPE_RGSUPER]	= "rgsuper",
 	[XFS_SCRUB_TYPE_RTRMAPBT]	= "rtrmapbt",
 	[XFS_SCRUB_TYPE_RTREFCBT]	= "rtrefcountbt",
+	[XFS_SCRUB_TYPE_HEALTHY]	= "healthy",
 };
 
 /* Format the scrub stats into a text buffer, similar to pcp style. */
author maintainer
#1098 lore raw

PATCH 1097/1250 xfs: release alleged child inode on metapath unlink error

In reply to Greg Kroah-Hartman

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

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

From: Anuj Gupta <[email protected]>

commit 48d2b8351bae6d40b44f544fe5540868a55872b2 upstream.

If xchk_metapath_ilock_parent_and_child() fails after xchk_iget() succeeds, release the inode reference before returning.

Fixes 0d2c636e489c ("xfs: repair metadata directory file path connectivity")
---
 fs/xfs/scrub/metapath.c |    2 ++
 1 file changed, 2 insertions(+)
fs/xfs/scrub/metapath.c +2 -0
@@ -556,6 +556,8 @@ xrep_metapath_try_unlink(
 	error = xchk_metapath_ilock_parent_and_child(mpath, ip);
 	if (error) {
 		xchk_trans_cancel(sc);
+		if (ip)
+			xchk_irele(sc, ip);
 		return error;
 	}
 	xfs_trans_ijoin(sc->tp, mpath->dp, 0);
author maintainer
#1099 lore raw

PATCH 1098/1250 xfs: preserve owner on in-memory btree creation

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 5287e56cba3be4a64bff9f73bce5964fda2590dc upstream.

LOLLM points out a minor bug where a higher level function creating an in-memory btree is required to pass in an owner number, but the creation function erases that. In-memory btrees are ephemeral so this really doesn't matter except for debugging. But let's fix this papercut.

Fixes a095686a238352 ("xfs: support in-memory btrees")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/libxfs/xfs_btree_mem.c |    2 ++
 1 file changed, 2 insertions(+)
fs/xfs/libxfs/xfs_btree_mem.c +2 -0
@@ -117,6 +117,7 @@ xfbtree_init(
 	struct xfs_buftarg		*btp,
 	const struct xfs_btree_ops	*ops)
 {
+	unsigned long long		owner = xfbt->owner;
 	unsigned int			blocklen = xfbtree_rec_bytes(mp, ops);
 	unsigned int			keyptr_len;
 	int				error;
@@ -133,6 +134,7 @@ xfbtree_init(
 
 	memset(xfbt, 0, sizeof(*xfbt));
 	xfbt->target = btp;
+	xfbt->owner = owner;
 
 	/* Set up min/maxrecs for this btree. */
 	keyptr_len = ops->key_len + sizeof(__be64);
author maintainer
#1100 lore raw

PATCH 1099/1250 xfs: make the rtsummary repair fix the file size too

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 6b760b3232b3efc8bcc7c165e76300bc1c9140c5 upstream.

LOLLM noticed that the rtsummary repair code will create a new rtsummary with the correct file size, but it won't force the new file size to be set on the existing rtsummary file, leaving the rtsummary corrupt. Fix this by setting up the tempfile mapping-exchange to run to the end of both files, which is the magic offset needed to reset the file size.

Fixes abf039e2e4afde ("xfs: online repair of realtime summaries")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/rtsummary_repair.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
fs/xfs/scrub/rtsummary_repair.c +2 -1
@@ -164,9 +164,10 @@ xrep_rtsummary(
 	/*
 	 * Now exchange the contents.  Nothing in repair uses the temporary
 	 * buffer, so we can reuse it for the tempfile exchrange information.
+	 * Use XFS_MAX_FILEOFF here so that we correct the rtsummary file size.
 	 */
 	error = xrep_tempexch_trans_reserve(sc, XFS_DATA_FORK, 0,
-			rts->rsumblocks, &rts->tempexch);
+			XFS_MAX_FILEOFF, &rts->tempexch);
 	if (error)
 		return error;
 
author maintainer
#1101 lore raw

PATCH 1100/1250 xfs: log the tempip after we convert it to extents format

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit ed799148e0d63ef41ab60ce98963a1dc423090d3 upstream.

LOLLM points out that xrep_symlink_swap_prep converts sc->tempip to an extents format file prior to the atomic swap, but incorrectly logs sc->ip immediately afterwards. Fix that, and the other problem that we're supposed to tell xfs_trans_log_inode what to log and don't.

Fixes 2651923d8d8db0 ("xfs: online repair of symbolic links")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/symlink_repair.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/symlink_repair.c +1 -1
@@ -291,7 +291,7 @@ xrep_symlink_swap_prep(
 		if (error)
 			return error;
 
-		xfs_trans_log_inode(sc->tp, sc->ip, 0);
+		xfs_trans_log_inode(sc->tp, sc->tempip, logflags);
 
 		error = xfs_defer_finish(&sc->tp);
 		if (error)
author maintainer
#1102 lore raw

PATCH 1101/1250 xfs: initialise error in xfs_defer_finish_one()

In reply to Greg Kroah-Hartman

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

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

From: Javier Tia <[email protected]>

commit 6176d21d7bd609632c5b7e87a3adb9be29ec1e72 upstream.

xfs_defer_finish_one() declares error without an initialiser and only assigns it inside the loop over dfp->dfp_work. When that list is empty the loop body never runs, control falls through to the "Done with the dfp, free it" path, and the function returns an indeterminate value.

An item-less pending item reaches this through xfs_defer_add_barrier(), which xfs_reap_ag_blocks() uses on any CONFIG_XFS_ONLINE_REPAIR kernel. xfs_defer_finish_noroll() treats any non-EAGAIN return as fatal, so a non-zero stack value turns a successful barrier into a SHUTDOWN_CORRUPT_INCORE in the middle of a repair. Zero is the correct
result: reaching the free path means the item loop drained without a non-zero error.

Fixes 3f3cec031099 ("xfs: force small EFIs for reaping btree extents")
---
 fs/xfs/libxfs/xfs_defer.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/libxfs/xfs_defer.c +1 -1
@@ -583,7 +583,7 @@ xfs_defer_finish_one(
 	const struct xfs_defer_op_type	*ops = dfp->dfp_ops;
 	struct xfs_btree_cur		*state = NULL;
 	struct list_head		*li, *n;
-	int				error;
+	int				error = 0;
 
 	trace_xfs_defer_pending_finish(tp->t_mountp, dfp);
 
author maintainer
#1103 lore raw

PATCH 1102/1250 xfs: fix xfs_rtrmapbt_mem_cursor for non-rmap filesystems

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 022d5f5fce7f0b6125d404eb74044e6238aef369 upstream.

It's possible to construct an in-memory rtrmap btree for filesystems that don't have the rmap feature enabled. The kernel doesn't do this, but xfs_repair will, if asked to reindex a filesystem that has rtreflink enabled but not rtrmap. Therefore, we must create the cursor with enough levels to handle a maximally sized btree possible.

Note that the rtrmapbt btree cursor slab creates objects large enough to handle xfs_rtrmap_maxlevels_ondisk() levels, so setting bc_nlevels to the same value isn't costing us any extra memory.

Fixes 4a61f12eb11958 ("xfs: create a shadow rmap btree during realtime rmap repair")
---
 fs/xfs/libxfs/xfs_rtrmap_btree.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/libxfs/xfs_rtrmap_btree.c +1 -1
index 0cb2113d5b40..a15e460a1ec7 100644
@@ -618,7 +618,7 @@ xfs_rtrmapbt_mem_cursor(
 	struct xfs_btree_cur	*cur;
 
 	cur = xfs_btree_alloc_cursor(mp, tp, &xfs_rtrmapbt_mem_ops,
-			mp->m_rtrmap_maxlevels, xfs_rtrmapbt_cur_cache);
+			xfs_rtrmapbt_maxlevels_ondisk(), xfs_rtrmapbt_cur_cache);
 	cur->bc_mem.xfbtree = xfbt;
 	cur->bc_nlevels = xfbt->nlevels;
 	cur->bc_group = xfs_group_hold(rtg_group(rtg));
Signature · 1 line
2.55.0
author maintainer
#1104 lore raw

PATCH 1103/1250 xfs: fix unit conversions in per_binval computation

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 05cff7c2b79f76c7cfe90613a60e16aaaa051ef7 upstream.

LOLLM noticed that we're doing the unit conversion in the per_binval computation backwards -- xfs_buf_inval_log_space's second parameter is supposed to be in bytes, but max_binval is in units of fsblocks. Hence the conversion should be FSB -> B, not the other way around.

Fixes b2311ec6778fcd ("xfs: compute per-AG extent reap limits dynamically")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/reap.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/xfs/scrub/reap.c +2 -2
index fcd14c1703ea..496c6eab555e 100644
@@ -601,7 +601,7 @@ xreap_configure_agextent_limits(
 
 	/* Maximum overhead of invalidating one buffer. */
 	const unsigned int	per_binval =
-		xfs_buf_inval_log_space(1, XFS_B_TO_FSBT(mp, max_binval));
+		xfs_buf_inval_log_space(1, XFS_FSB_TO_B(mp, max_binval));
 
 	/*
 	 * For each transaction in a reap chain, we can delete some number of
@@ -680,7 +680,7 @@ xreap_configure_agcow_limits(
 
 	/* Overhead of invalidating one buffer */
 	const unsigned int	per_binval =
-		xfs_buf_inval_log_space(1, XFS_B_TO_FSBT(mp, max_binval));
+		xfs_buf_inval_log_space(1, XFS_FSB_TO_B(mp, max_binval));
 
 	/*
 	 * For each transaction in a reap chain, we can delete some number of
Signature · 1 line
2.55.0
author maintainer
#1105 lore raw

PATCH 1104/1250 xfs: fix the rtrmap and rtrefcount _maxlevels_ondisk functions

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit aa301322f72f82f26e4ba0826018d41388ab9896 upstream.

The _maxlevels_ondisk functions are used to compute the size of in-memory btree cursors for each btree type. Unfortunately, LOLLM noticed that the rtrmap and rtrefcount versions of these functions forget to account for the inode root, which means that we could access beyond the end of the cursor given a sufficiently large btree. Fix this.

Fixes 9abe03a0e4f978 ("xfs: introduce realtime refcount btree ondisk definitions")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/libxfs/xfs_rtrefcount_btree.c |    7 +++++--
 fs/xfs/libxfs/xfs_rtrmap_btree.c     |    4 +++-
 2 files changed, 8 insertions(+), 3 deletions(-)
fs/xfs/libxfs/xfs_rtrefcount_btree.c +5 -2
@@ -489,8 +489,11 @@ xfs_rtrefcountbt_maxlevels_ondisk(void)
 	minrecs[0] = xfs_rtrefcountbt_block_maxrecs(blocklen, true) / 2;
 	minrecs[1] = xfs_rtrefcountbt_block_maxrecs(blocklen, false) / 2;
 
-	/* We need at most one record for every block in an rt group. */
-	return xfs_btree_compute_maxlevels(minrecs, XFS_MAX_RGBLOCKS);
+	/*
+	 * We need at most one record for every block in an rt group, and
+	 * one extra level for the inode root.
+	 */
+	return xfs_btree_compute_maxlevels(minrecs, XFS_MAX_RGBLOCKS) + 1;
 }
 
 int __init
fs/xfs/libxfs/xfs_rtrmap_btree.c +3 -1
@@ -716,10 +716,12 @@ xfs_rtrmapbt_maxlevels_ondisk(void)
 	 * happens, which means that we must compute the max height based on
 	 * what the btree will look like if it consumes almost all the blocks
 	 * in the data device due to maximal sharing factor.
+	 *
+	 * Add one extra level for the inode root.
 	 */
 	max_dblocks = -1U; /* max ag count */
 	max_dblocks *= XFS_MAX_CRC_AG_BLOCKS;
-	return xfs_btree_space_to_height(minrecs, max_dblocks);
+	return xfs_btree_space_to_height(minrecs, max_dblocks) + 1;
 }
 
 int __init
author maintainer
#1106 lore raw

PATCH 1105/1250 xfs: fix short ifork reaping computation in xreap_bmapi_binval

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit eacb8479507756c3305994e87fb2fb1183827e98 upstream.

LOLLM got really confused about the update to imap->br_blockcount in xreap_bmapi_binval if xreap_inc_binval returns false. The intent of this code is that we shorten the imap to whatever length of space we invalidated so that the next iteration through the loop will start wherever we left off. Unfortunately, the calculation sets br_blockcount to the amount of *unfinished* work, which means that we pointlessly re-scan blocks that we already reaped. This is benign, but we should fix the computation anyway.

Fixes 5befb047b9f4de ("xfs: add the ability to reap entire inode forks")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/reap.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/reap.c +1 -1
@@ -1400,7 +1400,7 @@ xreap_bmapi_binval(
 			 * far we've gotten.
 			 */
 			if (!xreap_inc_binval(rs)) {
-				imap->br_blockcount = agbno_next - bno;
+				imap->br_blockcount = bno - agbno;
 				goto out;
 			}
 		}
author maintainer
#1107 lore raw

PATCH 1106/1250 xfs: fix rtrmap cross-referencing elision logic

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 3bdbf472a608aeb7e8e4dc70ee86738ad5256356 upstream.

LOLLM points out that xchk_bmap_xref_rmap_cow skips the cross-reference if the data-section rmapbt cursor is not present. However, this is broken for realtime file data fork scanning, because they will have an rtrmapbt cursor and not an rmapbt cursor. Fix the behavior by removing the cursor checks because xchk_bmap_get_rmap already accounts for that.

Fixes 037a44d8277adf ("xfs: cross-reference the realtime rmapbt")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/bmap.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/bmap.c +1 -1
@@ -274,7 +274,7 @@ xchk_bmap_xref_rmap_cow(
 	unsigned long long	rmap_end;
 	uint64_t		owner = XFS_RMAP_OWN_COW;
 
-	if (!info->sc->sa.rmap_cur || xchk_skip_xref(info->sc->sm))
+	if (xchk_skip_xref(info->sc->sm))
 		return;
 
 	/* Find the rmap record for this irec. */
author maintainer
#1108 lore raw

PATCH 1107/1250 xfs: fix rtrefcount btree block counting in scrub

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 3f9fd694fa429e89fe6de51b22b0ed5fb8b2daf4 upstream.

LOLLM started on a long tangent about how xchk_refcount_xref_rmap shouldn't nope out if sc->sa.rmap_cur isn't set, because nothing ever sets that field. It's right about the condition, but misses the bigger problem, which is that to count the rtrefcount btree blocks, we have to walk all rmap records in each AG in the data section. That was papered over by the incorrect !sc->sa.rmap_cur test.

In other words, we need a perag iteration loop here. Restructure the code to do that, and now it'll all work properly. Fix the confusing function name prefix.

Fixes c27929670de144 ("xfs: scrub the realtime refcount btree")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/rtrefcount.c |   64 +++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 55 insertions(+), 9 deletions(-)
fs/xfs/scrub/rtrefcount.c +55 -9
@@ -20,6 +20,7 @@
 #include "xfs_metafile.h"
 #include "xfs_rtrefcount_btree.h"
 #include "xfs_rtalloc.h"
+#include "xfs_ag.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 #include "scrub/btree.h"
@@ -505,30 +506,75 @@ xchk_rtrefcountbt_rec(
 	return 0;
 }
 
+/* Count the number of blocks used by the rtrefcount btree file in this AG. */
+static int
+xchk_rtrefcount_count_agblocks(
+	struct xfs_scrub	*sc,
+	xfs_agnumber_t		agno,
+	const struct xfs_owner_info *btree_oinfo,
+	xfs_filblks_t		*blocks)
+{
+	xfs_filblks_t		agblocks = 0;
+	int			error;
+
+	error = xchk_ag_init_existing(sc, agno, &sc->sa);
+	if (error)
+		goto out_free;
+
+	/*
+	 * If we don't have an rmap cursor, we can't complete the cross
+	 * referencing, so return EFSCORRUPTED to end the loop and trigger the
+	 * XFAIL flag.
+	 */
+	if (!sc->sa.rmap_cur) {
+		error = -EFSCORRUPTED;
+		goto out_free;
+	}
+
+	error = xchk_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, btree_oinfo,
+			&agblocks);
+	if (error)
+		goto out_free;
+
+	*blocks += agblocks;
+out_free:
+	xchk_ag_free(sc, &sc->sa);
+	return error;
+}
+
 /* Make sure we have as many refc blocks as the rmap says. */
 STATIC void
-xchk_refcount_xref_rmap(
+xchk_rtrefcount_xref_rmap(
 	struct xfs_scrub	*sc,
 	const struct xfs_owner_info *btree_oinfo,
 	xfs_extlen_t		cow_blocks)
 {
 	xfs_filblks_t		refcbt_blocks = 0;
-	xfs_filblks_t		blocks;
-	int			error;
+	xfs_filblks_t		blocks = 1; /* one for the iroot */
+	xfs_agnumber_t		agno;
+	int			error = 0;
 
-	if (!sc->sr.rmap_cur || !sc->sa.rmap_cur || xchk_skip_xref(sc->sm))
+	if (!xfs_has_rmapbt(sc->mp) || xchk_skip_xref(sc->sm))
 		return;
 
 	/* Check that we saw as many refcbt blocks as the rmap knows about. */
 	error = xfs_btree_count_blocks(sc->sr.refc_cur, &refcbt_blocks);
 	if (!xchk_btree_process_error(sc, sc->sr.refc_cur, 0, &error))
 		return;
-	error = xchk_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, btree_oinfo,
-			&blocks);
-	if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur))
+
+	for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
+		error = xchk_rtrefcount_count_agblocks(sc, agno, btree_oinfo,
+				&blocks);
+		if (error)
+			break;
+	}
+	if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
 		return;
 	if (blocks != refcbt_blocks)
-		xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
+		xchk_fblock_xref_set_corrupt(sc, XFS_DATA_FORK, 0);
+
+	if (!sc->sr.rmap_cur || xchk_skip_xref(sc->sm))
+		return;
 
 	/* Check that we saw as many cow blocks as the rmap knows about. */
 	error = xchk_count_rmap_ownedby_ag(sc, sc->sr.rmap_cur,
@@ -569,7 +615,7 @@ xchk_rtrefcountbt(
 	 */
 	xchk_rtrefcountbt_xref_gaps(sc, &rrc,
 			xfs_rtx_to_rgbno(sc->sr.rtg, sc->mp->m_sb.sb_rgextents));
-	xchk_refcount_xref_rmap(sc, &btree_oinfo, rrc.cow_blocks);
+	xchk_rtrefcount_xref_rmap(sc, &btree_oinfo, rrc.cow_blocks);
 
 	return 0;
 }
author maintainer
#1109 lore raw

PATCH 1108/1250 xfs: fix replaying dirent removals into the temporary directory

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit e854f9a28b1fa08dfa5bf18ee4184fae90106180 upstream.

xrep_dir_replay_removename is the function that replays a directory entry removal from sc->ip into the temporary directory so that when we swap the contents of sc->tempip and sc->ip, the directory is correct. LOLLM noticed that we were passing the wrong inode pointer into xrep_dir_init_args. It doesn't make sense to set rd->args.dp to rd->args.dp so let's fix this.

Fixes 8559b21a64d983 ("xfs: implement live updates for directory repairs")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/dir_repair.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/dir_repair.c +1 -1
@@ -721,7 +721,7 @@ xrep_dir_replay_removename(
 	const struct xfs_name	*name,
 	xfs_extlen_t		total)
 {
-	struct xfs_inode	*dp = rd->args.dp;
+	struct xfs_inode	*dp = rd->sc->tempip;
 
 	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
 
author maintainer
#1110 lore raw

PATCH 1109/1250 xfs: fix reclaimed page accounting in xfs_buf_free

In reply to Greg Kroah-Hartman

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

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

From: Eric Sandeen <[email protected]>

commit 4164b1e3d728c7cc05e0c1171068aa046542ecc3 upstream.

To obtain nr. of pages in "size" bytes, we need howmany(size, PAGE_SIZE) not howmany(size, PAGE_SHIFT). This over-reports reclaim by orders of magnitude, up to 4096x on a 64k page system.

Fixes e2874632a621 ("xfs: use vmalloc instead of vm_map_area for buffer backing memory")
Cc [email protected] # v6.15+
---
 fs/xfs/xfs_buf.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_buf.c +1 -1
@@ -111,7 +111,7 @@ xfs_buf_free(
 	ASSERT(list_empty(&bp->b_lru));
 
 	if (!xfs_buftarg_is_mem(bp->b_target) && size >= PAGE_SIZE)
-		mm_account_reclaimed_pages(howmany(size, PAGE_SHIFT));
+		mm_account_reclaimed_pages(howmany(size, PAGE_SIZE));
 
 	if (is_vmalloc_addr(bp->b_addr))
 		vfree(bp->b_addr);
author maintainer
#1111 lore raw

PATCH 1110/1250 xfs: fix parent rec lookup initialization in xrep_metapath_unlink

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit cdc4a083adf7bf15a0722c5bd292a35e00112006 upstream.

LOLLM notices that xrep_metapath_unlink looks for a parent pointer in the child metafile that it's removing, but initializes the parent handle using the child. This is obviously incorrect, so fix that.

Fixes 0d2c636e489c11 ("xfs: repair metadata directory file path connectivity")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/metapath.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/metapath.c +1 -1
@@ -397,7 +397,7 @@ xrep_metapath_unlink(
 
 	/* Figure out if we're removing a parent pointer too. */
 	if (xfs_has_parent(mp)) {
-		xfs_inode_to_parent_rec(&rec, ip);
+		xfs_inode_to_parent_rec(&rec, mpath->dp);
 		error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec,
 				&mpath->pptr_args);
 		switch (error) {
author maintainer
#1112 lore raw

PATCH 1111/1250 xfs: fix name string recording in slowpath pptr tracepoints

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 72d0a3e4405c1353869bfbbc67c7b8a29df7afd0 upstream.

LOLLM observes that we memcpy from the xfs_name object, not the name string pointed to by the xfs_name. Fix that.

Fixes b961c8bf1fc3d0 ("xfs: deferred scrub of dirents")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/trace.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/trace.h +1 -1
@@ -1640,7 +1640,7 @@ DECLARE_EVENT_CLASS(xchk_pptr_class,
 		__entry->dev = ip->i_mount->m_super->s_dev;
 		__entry->ino = ip->i_ino;
 		__entry->namelen = name->len;
-		memcpy(__get_str(name), name, name->len);
+		memcpy(__get_str(name), name->name, name->len);
 		__entry->far_ino = far_ino;
 	),
 	TP_printk("dev %d:%d ino 0x%llx name '%.*s' far_ino 0x%llx",
author maintainer
#1113 lore raw

PATCH 1112/1250 xfs: fix exchange-range reflink flag clearing issue with INO1_WRITTEN

In reply to Greg Kroah-Hartman

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

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

From: Lin Jiapeng <[email protected]>

commit a23eca88448e52eb1a81549862df7adce794fafb upstream.

When exchanging two full-file ranges, xmi_can_exchange_reflink_flags() can move the reflink inode flag from the file that currently has it to the other file, as long as exactly one side is marked. This assumes that the file contents, and therefore all shared extents, are exchanged.

That assumption is not true when XFS_EXCHMAPS_INO1_WRITTEN is set. xfs_exchmaps_can_skip_mapping() can skip hole and unwritten mappings from file1, so an exchange can complete without moving every mapping that the earlier flag-swap decision accounted for. In that case the post-operation cleanup can clear the reflink flag from an inode that still owns shared written extents. Later writes then take the non-reflink write path and may update blocks that should still have been protected by CoW, which shows up as data corruption between reflink-related files.

Fix this by disabling the reflink flag exchange whenever XFS_EXCHMAPS_INO1_WRITTEN is requested. The contents exchange can still proceed; the conservative outcome is that both inodes keep the reflink flag. The regular reflink flag cleanup path can drop the extra flag later once the inode no longer has shared extents.

Fixes 966ceafc7a43 ("xfs: create deferred log items for file mapping exchanges")
---
 fs/xfs/libxfs/xfs_exchmaps.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
fs/xfs/libxfs/xfs_exchmaps.c +10 -0
@@ -969,6 +969,16 @@ xmi_can_exchange_reflink_flags(
 	if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN)
 		return false;
 
+	/*
+	 * The INO1_WRITTEN optimization can skip exchanging hole and
+	 * unwritten mappings, which means we cannot guarantee that all
+	 * shared extents actually moved to the other file.  Clearing the
+	 * reflink flag of an inode that still holds shared extents breaks
+	 * the CoW write path, so refuse to exchange the flags in that case.
+	 */
+	if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN)
+		return false;
+
 	if (hweight32(reflink_state) != 1)
 		return false;
 	if (req->startoff1 != 0 || req->startoff2 != 0)
author maintainer
#1114 lore raw

PATCH 1113/1250 xfs: fix bnobt repair space reservation disposal failure

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit c83d1ef97ee3b0b92797d4b4932a3a813ba05b86 upstream.

LOLLM complains that we don't bubble failures from xrep_abt_dispose_one upwards in the callstack. A failure to clean up the space used (or reserved but not used) by the new bnobt/cntbt should be reported.

Fixes 4bdfd7d15747b1 ("xfs: repair free space btrees")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/alloc_repair.c |   18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)
fs/xfs/scrub/alloc_repair.c +12 -6
@@ -571,7 +571,7 @@ xrep_abt_dispose_one(
  * allocation, and blocks that didn't get used can be freed via the usual
  * (deferred) means.
  */
-STATIC void
+STATIC int
 xrep_abt_dispose_reservations(
 	struct xrep_abt		*ra,
 	int			error)
@@ -582,9 +582,13 @@ xrep_abt_dispose_reservations(
 		goto junkit;
 
 	list_for_each_entry_safe(resv, n, &ra->new_bnobt.resv_list, list) {
-		error = xrep_abt_dispose_one(ra, resv);
-		if (error)
+		int		error2 = xrep_abt_dispose_one(ra, resv);
+
+		if (error2) {
+			if (!error)
+				error = error2;
 			goto junkit;
+		}
 	}
 
 junkit:
@@ -596,6 +600,7 @@ junkit:
 
 	xrep_newbt_cancel(&ra->new_bnobt);
 	xrep_newbt_cancel(&ra->new_cntbt);
+	return error;
 }
 
 /* Retrieve free space data for bulk load. */
@@ -801,7 +806,9 @@ xrep_abt_build_new_trees(
 		goto err_newbt;
 
 	/* Dispose of any unused blocks and the accounting information. */
-	xrep_abt_dispose_reservations(ra, error);
+	error = xrep_abt_dispose_reservations(ra, error);
+	if (error)
+		return error;
 
 	return xrep_roll_ag_trans(sc);
 
@@ -812,8 +819,7 @@ err_cur:
 	xfs_btree_del_cursor(cnt_cur, error);
 	xfs_btree_del_cursor(bno_cur, error);
 err_newbt:
-	xrep_abt_dispose_reservations(ra, error);
-	return error;
+	return xrep_abt_dispose_reservations(ra, error);
 }
 
 /*
author maintainer
#1115 lore raw

PATCH 1114/1250 xfs: fix backwards skipping logic in xrep_quota_block

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit d7f97be48cbe3751e44a45373c0e91e93b976f98 upstream.

LOLLM complains about the logic in xrep_quota_block that skips reinitializing the ondisk dquot if there aren't any problems that would impede a dqiterate walk later. I got the type checking logic backwards, which is the source of the problem. Fix that.

Fixes a5b91555403e3a ("xfs: repair quotas")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/quota_repair.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/quota_repair.c +1 -1
@@ -327,7 +327,7 @@ xrep_quota_block(
 		 * If there's nothing that would impede a dqiterate, we're
 		 * done.
 		 */
-		if ((ddq->d_type & XFS_DQTYPE_REC_MASK) != dqtype ||
+		if ((ddq->d_type & XFS_DQTYPE_REC_MASK) == dqtype &&
 		    id == be32_to_cpu(ddq->d_id)) {
 			xfs_trans_brelse(sc->tp, bp);
 			return 0;
author maintainer
#1116 lore raw

PATCH 1115/1250 xfs: fix backwards mergeability logic in refcount scrubber

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit e8b01aaafffe6b852325debdf1ef4b13ccea1cd7 upstream.

When we start the refcount or rtrefcount btree scanners, prev_rec is initialized to all zeroes. This is done so that the record mergeability checks skip the first record because you must have two records to compare. Unfortunately, I got the logic backwards, so scrub has never complained about mergeable refcountbt records. Fix this bug that LOLLM noticed.

Fixes db0502b39c21d1 ("xfs: flag refcount btree records that could be merged")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/refcount.c   |    2 +-
 fs/xfs/scrub/rtrefcount.c |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
fs/xfs/scrub/refcount.c +1 -1
@@ -411,7 +411,7 @@ xchk_refcount_mergeable(
 	const struct xfs_refcount_irec	*r1 = &rrc->prev_rec;
 
 	/* Ignore if prev_rec is not yet initialized. */
-	if (r1->rc_blockcount > 0)
+	if (r1->rc_blockcount == 0)
 		return false;
 
 	if (r1->rc_domain != r2->rc_domain)
fs/xfs/scrub/rtrefcount.c +1 -1
@@ -377,7 +377,7 @@ xchk_rtrefcount_mergeable(
 	const struct xfs_refcount_irec	*r1 = &rrc->prev_rec;
 
 	/* Ignore if prev_rec is not yet initialized. */
-	if (r1->rc_blockcount > 0)
+	if (r1->rc_blockcount == 0)
 		return false;
 
 	if (r1->rc_startblock + r1->rc_blockcount != r2->rc_startblock)
author maintainer
#1117 lore raw

PATCH 1116/1250 xfs: dont spin forever on zero-length dirents when salvaging them

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 9f84792b40d0c96833341602144574bf0bd14a6a upstream.

LOLLM noticed that xrep_dir_recover_data can spin forever if it encounters an unused dirent that claims to have length zero. Fix that, and prevent the same thing from happening with a zero-length entry.

Fixes b1991ee3e7cf85 ("xfs: online repair of directories")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/dir_repair.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
fs/xfs/scrub/dir_repair.c +7 -1
@@ -484,18 +484,24 @@ xrep_dir_recover_data(
 	while (offset < end) {
 		struct xfs_dir2_data_unused	*dup = bp->b_addr + offset;
 		struct xfs_dir2_data_entry	*dep = bp->b_addr + offset;
+		unsigned int			advance;
 
 		if (xchk_should_terminate(rd->sc, &error))
 			return error;
 
 		/* Skip unused entries. */
 		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
+			if (!dup->length)
+				break;
 			offset += be16_to_cpu(dup->length);
 			continue;
 		}
 
 		/* Don't walk off the end of the block. */
-		offset += xfs_dir2_data_entsize(rd->sc->mp, dep->namelen);
+		advance = xfs_dir2_data_entsize(rd->sc->mp, dep->namelen);
+		if (!advance)
+			break;
+		offset += advance;
 		if (offset > end)
 			break;
 
author maintainer
#1118 lore raw

PATCH 1117/1250 xfs: dont modify file attributes or poke fsnotify for dry runs

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 365fe37e10ea75840165f13322aa8481ea11dfef upstream.

I noticed that we shouldn't be removing file privileges when doing a dry run of an exchange-range operation. LOLLM also points out that a dry run shouldn't poke fsnotify because we don't actually change the files. Fix both by gating them on !DRY_RUN.

Fixes 42672471f938cd ("xfs: bind together the front and back ends of the file range exchange code")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/xfs_exchrange.c |   12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)
fs/xfs/xfs_exchrange.c +9 -3
@@ -504,6 +504,9 @@ xfs_exchange_range_finish(
 {
 	int			error;
 
+	if (fxr->flags & XFS_EXCHANGE_RANGE_DRY_RUN)
+		return 0;
+
 	error = file_remove_privs(fxr->file1);
 	if (error)
 		return error;
@@ -783,9 +786,12 @@ xfs_exchange_range(
 	if (ret)
 		return ret;
 
-	fsnotify_modify(fxr->file1);
-	if (fxr->file2 != fxr->file1)
-		fsnotify_modify(fxr->file2);
+	if (!(fxr->flags & XFS_EXCHANGE_RANGE_DRY_RUN)) {
+		fsnotify_modify(fxr->file1);
+		if (fxr->file2 != fxr->file1)
+			fsnotify_modify(fxr->file2);
+	}
+
 	return 0;
 }
 
author maintainer
#1119 lore raw

PATCH 1118/1250 xfs: dont leak new_bp if xfs_btree_bload_drop_buf fails

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit f1930bc578095409c2dcfca6e4e898b24f0f0de6 upstream.

LOLLM observes that in xfs_btree_bload_prep_block, xfs_btree_bload_drop_buf can hit an IO error if writing the delwri buffer list to disk fails. In this case, we fail to release new_bp, which means we lose a locked buffer. Fix that.

Fixes e069d549705e49 ("xfs: constrain dirty buffers while formatting a staged btree")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/libxfs/xfs_btree_staging.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
fs/xfs/libxfs/xfs_btree_staging.c +3 -1
@@ -337,8 +337,10 @@ xfs_btree_bload_prep_block(
 		xfs_btree_set_sibling(cur, *blockp, &new_ptr, XFS_BB_RIGHTSIB);
 
 	ret = xfs_btree_bload_drop_buf(bbl, buffers_list, bpp);
-	if (ret)
+	if (ret) {
+		xfs_buf_relse(new_bp);
 		return ret;
+	}
 
 	/* Initialize the new btree block. */
 	xfs_btree_init_block_cur(cur, new_bp, level, nr_this_block);
author maintainer
#1120 lore raw

PATCH 1119/1250 xfs: dont leak dqacct if rhashtable insertion fails

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 2eac8d01d2c776fc26b0ac74aebdf91bc4490891 upstream.

LOLLM observes that xqcheck_mod_live_ino_dqtrx doesn't free the newly allocated dqa object if rhashtable insertion fails. Fix this leak.

Fixes 200491875ce144 ("xfs: track quota updates during live quotacheck")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/quotacheck.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
fs/xfs/scrub/quotacheck.c +3 -1
@@ -263,8 +263,10 @@ xqcheck_mod_live_ino_dqtrx(
 		dqa->tx_id = p->tx_id;
 		error = rhashtable_insert_fast(&xqc->shadow_dquot_acct,
 				&dqa->hash, xqcheck_dqacct_hash_params);
-		if (error)
+		if (error) {
+			kfree(dqa);
 			goto out_abort;
+		}
 	}
 
 	/* Find the shadow dqtrx (or an empty slot) here. */
author maintainer
#1121 lore raw

PATCH 1120/1250 xfs: destroy seen inode bitmap when we fail to add a dirpath

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 1a441c6842da75c5b862cc1c9f7969d6a93b54b9 upstream.

LOLLM observes a memory leak in xchk_dirtree_create_path if we create the directory path object but appending the name to the path fails. When this happens, we don't tear down the (empty) seen inode bitmap. This is a pretty trivial error, but let's not leave logic bombs.

Do the same for a similar bug in xrep_dirtree_create_adoption_path.

Fixes 928b721a11789a ("xfs: teach online scrub to find directory tree structure problems")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/dirtree.c        |    1 +
 fs/xfs/scrub/dirtree_repair.c |    1 +
 2 files changed, 2 insertions(+)
fs/xfs/scrub/dirtree.c +1 -0
@@ -259,6 +259,7 @@ xchk_dirtree_create_path(
 	dl->nr_paths++;
 	return 0;
 out_path:
+	xino_bitmap_destroy(&path->seen_inodes);
 	kfree(path);
 	return error;
 }
fs/xfs/scrub/dirtree_repair.c +1 -0
@@ -619,6 +619,7 @@ xrep_dirtree_create_adoption_path(
 	return 0;
 
 out_path:
+	xino_bitmap_destroy(&path->seen_inodes);
 	kfree(path);
 	return error;
 }
author maintainer
#1122 lore raw

PATCH 1121/1250 xfs: cross-reference the rtgroup superblock extent, not block

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 79ab1af2034b2ad10c5f18910937b938d2ad2219 upstream.

LOLLM noticed that when libxfs creates a realtime superblock, it will create an rtrmapbt record covering the entire rtextent in which the superblock lives. However, the cross-referencing checks only look for the first block, which means that we can miss a corrupt rtrmap record. That will get picked up by the rtrmap scrubber, but we should make the rgsuper scrubber more robust anyway.

Fixes 3f1bdf50ab1b9c ("xfs: scrub the realtime group superblock")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/rgsuper.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
fs/xfs/scrub/rgsuper.c +4 -2
@@ -36,8 +36,10 @@ xchk_rgsuperblock_xref(
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		return;
 
-	xchk_xref_is_used_rt_space(sc, xfs_rgbno_to_rtb(sc->sr.rtg, 0), 1);
-	xchk_xref_is_only_rt_owned_by(sc, 0, 1, &XFS_RMAP_OINFO_FS);
+	xchk_xref_is_used_rt_space(sc, xfs_rgbno_to_rtb(sc->sr.rtg, 0),
+			sc->mp->m_sb.sb_rextsize);
+	xchk_xref_is_only_rt_owned_by(sc, 0, sc->mp->m_sb.sb_rextsize,
+			&XFS_RMAP_OINFO_FS);
 }
 
 int
author maintainer
#1123 lore raw

PATCH 1122/1250 xfs: count escaped corruption errors in scrub stats

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 4d0624679ae29b469016f1ce4714be58582ed06a upstream.

The main scrub code will quietly turn bubbled-up EFSCORRUPTED and EFSBADCRC errors into corruption errors. These aren't recorded in the scrub stats code (says LOLLM) so do that now.

Fixes d7a74cad8f4513 ("xfs: track usage statistics of online fsck")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/scrub.c |    3 +--
 fs/xfs/scrub/stats.c |   28 +++++++++++++++++++---------
 fs/xfs/scrub/stats.h |    4 ++--
 3 files changed, 22 insertions(+), 13 deletions(-)
fs/xfs/scrub/scrub.c +1 -2
@@ -765,8 +765,7 @@ out_nofix:
 out_teardown:
 	error = xchk_teardown(sc, error);
 out_sc:
-	if (error != -ENOENT)
-		xchk_stats_merge(mp, sm, &run);
+	xchk_stats_merge(mp, sm, error, &run);
 	kfree(sc);
 out:
 	trace_xchk_done(XFS_I(file_inode(file)), sm, error);
fs/xfs/scrub/stats.c +19 -9
@@ -195,31 +195,37 @@ STATIC void
 xchk_stats_merge_one(
 	struct xchk_stats		*cs,
 	const struct xfs_scrub_metadata	*sm,
+	int				error,
 	const struct xchk_stats_run	*run)
 {
 	struct xchk_scrub_stats		*css;
+	unsigned int			sm_flags = sm->sm_flags;
 
 	if (sm->sm_type >= XFS_SCRUB_TYPE_NR) {
 		ASSERT(sm->sm_type < XFS_SCRUB_TYPE_NR);
 		return;
 	}
 
+	/* caller applies this same transformation after we return */
+	if (error == -EFSCORRUPTED || error == -EFSBADCRC)
+		sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+
 	css = &cs->cs_stats[sm->sm_type];
 	spin_lock(&css->css_lock);
 	css->invocations++;
-	if (!(sm->sm_flags & XFS_SCRUB_OFLAG_UNCLEAN))
+	if (!(sm_flags & XFS_SCRUB_OFLAG_UNCLEAN))
 		css->clean++;
-	if (sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+	if (sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		css->corrupt++;
-	if (sm->sm_flags & XFS_SCRUB_OFLAG_PREEN)
+	if (sm_flags & XFS_SCRUB_OFLAG_PREEN)
 		css->preen++;
-	if (sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL)
+	if (sm_flags & XFS_SCRUB_OFLAG_XFAIL)
 		css->xfail++;
-	if (sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)
+	if (sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)
 		css->xcorrupt++;
-	if (sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)
+	if (sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)
 		css->incomplete++;
-	if (sm->sm_flags & XFS_SCRUB_OFLAG_WARNING)
+	if (sm_flags & XFS_SCRUB_OFLAG_WARNING)
 		css->warning++;
 	css->retries += run->retries;
 	css->checktime_us += howmany_64(run->scrub_ns, NSEC_PER_USEC);
@@ -237,10 +243,14 @@ void
 xchk_stats_merge(
 	struct xfs_mount		*mp,
 	const struct xfs_scrub_metadata	*sm,
+	int				error,
 	const struct xchk_stats_run	*run)
 {
-	xchk_stats_merge_one(&global_stats, sm, run);
-	xchk_stats_merge_one(mp->m_scrub_stats, sm, run);
+	if (error == -ENOENT)
+		return;
+
+	xchk_stats_merge_one(&global_stats, sm, error, run);
+	xchk_stats_merge_one(mp->m_scrub_stats, sm, error, run);
 }
 
 /* debugfs boilerplate */
fs/xfs/scrub/stats.h +2 -2
@@ -27,7 +27,7 @@ void xchk_stats_register(struct xchk_sta
 void xchk_stats_unregister(struct xchk_stats *cs);
 
 void xchk_stats_merge(struct xfs_mount *mp, const struct xfs_scrub_metadata *sm,
-		const struct xchk_stats_run *run);
+		int error, const struct xchk_stats_run *run);
 
 static inline u64 xchk_stats_now(void) { return ktime_get_ns(); }
 static inline u64 xchk_stats_elapsed_ns(u64 since)
@@ -53,7 +53,7 @@ static inline u64 xchk_stats_elapsed_ns(
 # define xchk_stats_unregister(cs)		((void)0)
 # define xchk_stats_now()			(0)
 # define xchk_stats_elapsed_ns(x)		(0 * (x))
-# define xchk_stats_merge(mp, sm, run)		((void)0)
+# define xchk_stats_merge(mp, sm, error, run)	((void)0)
 #endif /* CONFIG_XFS_ONLINE_SCRUB_STATS */
 
 #endif /* __XFS_SCRUB_STATS_H__ */
author maintainer
#1124 lore raw

PATCH 1123/1250 xfs: compute dquot checksum after resetting dd_lsn in repair

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit de20f7014917d661afc03d9a1c157d0ae2775b49 upstream.

LOLLM complains that xrep_quota_block updates dd_lsn after calculating the crc of the ondisk dquot. That's clearly broken, so fix that.

Fixes a5b91555403e3a ("xfs: repair quotas")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/quota_repair.c |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
fs/xfs/scrub/quota_repair.c +8 -1
@@ -365,11 +365,18 @@ xrep_quota_block(
 				ddq->d_rtbcount, &ddq->d_rtbtimer,
 				defq->rtb.time);
 
+		/*
+		 * This transaction operates on raw disk buffers, so we don't
+		 * have a dquot log item to assign the LSN for us.  Instead,
+		 * set it to zero so that log recovery will always replay any
+		 * logged dquot item atop this buffer.
+		 */
+		dqblk->dd_lsn = 0;
+
 		/* We only support v5 filesystems so always set these. */
 		uuid_copy(&dqblk->dd_uuid, &sc->mp->m_sb.sb_meta_uuid);
 		xfs_update_cksum((char *)dqblk, sizeof(struct xfs_dqblk),
 				 XFS_DQUOT_CRC_OFF);
-		dqblk->dd_lsn = 0;
 	}
 	switch (dqtype) {
 	case XFS_DQTYPE_USER:
author maintainer
#1125 lore raw

PATCH 1124/1250 xfs: bail out on bitmap errors in xrep_agfl_fill

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit eaf580538eb1be3d162400d04c4b7dc4c627296b upstream.

LOLLM also points out that the xagb_bitmap_set call in xrep_agfl_fill can fail, but we don't check the result of xagb_bitmap_walk, so we silently drop the error and proceed with inconsistent incore data. That shouldn't be allowed.

Fixes 014ad53732d2ba ("xfs: use per-AG bitmaps to reap unused AG metadata blocks during repair")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/agheader_repair.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
fs/xfs/scrub/agheader_repair.c +3 -1
@@ -699,7 +699,9 @@ xrep_agfl_init_header(
 	 */
 	xagb_bitmap_init(&af.used_extents);
 	af.agfl_bno = xfs_buf_to_agfl_bno(agfl_bp);
-	xagb_bitmap_walk(agfl_extents, xrep_agfl_fill, &af);
+	error = xagb_bitmap_walk(agfl_extents, xrep_agfl_fill, &af);
+	if (error && error != -ECANCELED)
+		goto err_undo;
 	error = xagb_bitmap_disunion(agfl_extents, &af.used_extents);
 	if (error)
 		goto err_undo;
author maintainer
#1126 lore raw

PATCH 1125/1250 xfs: advance the findparent inode scan cursor while holding ILOCK

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit ad4497a92caba4630f75c80d49cb947026213280 upstream.

LOLLM pointed out a race condition in xrep_findparent_scan -- the directory live update hook holds the directory ILOCK when it calls the xchk_iscan_want_live_update predicate to figure out if it needs to remember the live update, but xrep_findparent_scan drops the directory ILOCK before advancing the cursor. Therefore, it's possible for a live update to check the scan cursor after the scan drops the ILOCK but before the scan updates its cursor. If this happens, we'll fail to record the live update. Fix this by moving the cursor update logic inside xrep_findparent_walk_directory.

Note that for non-directories it's ok to advance the cursor without holding any ILOCK because the findparent scan only cares about directory parents, not the children.

Fixes a07b45576264e7 ("xfs: scan the filesystem to repair a directory dotdot entry")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/findparent.c |   54 ++++++++++++++++++++++++++++++----------------
 1 file changed, 36 insertions(+), 18 deletions(-)
fs/xfs/scrub/findparent.c +36 -18
@@ -139,32 +139,52 @@ xrep_findparent_dirent(
 	return 0;
 }
 
-/*
- * If this is a directory, walk the dirents looking for any that point to the
- * scrub target inode.
- */
-STATIC int
-xrep_findparent_walk_directory(
-	struct xrep_findparent_info	*fpi)
+static inline bool
+xrep_findparent_want_scan_file(
+	const struct xrep_findparent_info	*fpi)
 {
-	struct xfs_scrub		*sc = fpi->sc;
-	struct xfs_inode		*dp = fpi->dp;
-	unsigned int			lock_mode;
-	int				error = 0;
+	const struct xfs_scrub			*sc = fpi->sc;
+	const struct xfs_inode			*dp = fpi->dp;
+
+	/* Only directories can be parents */
+	if (!S_ISDIR(VFS_IC(dp)->i_mode))
+		return false;
 
 	/*
 	 * The inode being scanned cannot be its own parent, nor can any
 	 * temporary directory we created to stage this repair.
 	 */
 	if (dp == sc->ip || dp == sc->tempip)
-		return 0;
+		return false;
 
 	/*
 	 * Similarly, temporary files created to stage a repair cannot be the
 	 * parent of this inode.
 	 */
 	if (xrep_is_tempfile(dp))
+		return false;
+
+	return true;
+}
+
+/*
+ * If this is a directory, walk the dirents looking for any that point to the
+ * scrub target inode.
+ */
+STATIC int
+xrep_findparent_walk_file(
+	struct xrep_findparent_info	*fpi)
+{
+	struct xfs_scrub		*sc = fpi->sc;
+	struct xfs_inode		*dp = fpi->dp;
+	unsigned int			lock_mode;
+	int				error = 0;
+
+	if (!xrep_findparent_want_scan_file(fpi)) {
+		if (fpi->parent_scan)
+			xchk_iscan_mark_visited(&fpi->parent_scan->iscan, dp);
 		return 0;
+	}
 
 	/*
 	 * Scan the directory to see if there it contains an entry pointing to
@@ -201,6 +221,8 @@ xrep_findparent_walk_directory(
 		goto out_unlock;
 
 out_unlock:
+	if (fpi->parent_scan)
+		xchk_iscan_mark_visited(&fpi->parent_scan->iscan, dp);
 	xfs_iunlock(dp, lock_mode);
 	return error;
 }
@@ -309,11 +331,7 @@ xrep_findparent_scan(
 	ASSERT(S_ISDIR(VFS_IC(sc->ip)->i_mode));
 
 	while ((ret = xchk_iscan_iter(&pscan->iscan, &fpi.dp)) == 1) {
-		if (S_ISDIR(VFS_I(fpi.dp)->i_mode))
-			ret = xrep_findparent_walk_directory(&fpi);
-		else
-			ret = 0;
-		xchk_iscan_mark_visited(&pscan->iscan, fpi.dp);
+		ret = xrep_findparent_walk_file(&fpi);
 		xchk_irele(sc, fpi.dp);
 		if (ret)
 			break;
@@ -402,7 +420,7 @@ xrep_findparent_confirm(
 		goto out_rele;
 	}
 
-	error = xrep_findparent_walk_directory(&fpi);
+	error = xrep_findparent_walk_file(&fpi);
 	if (error)
 		goto out_rele;
 
author maintainer
#1127 lore raw

PATCH 1126/1250 xfs: actually recover intended file sizes in xfs_xmi_item_recover_intent

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit b71ae66863e4320a3b7313b53bb4d65f1718d58b upstream.

LOLLM points out that xfs_xmi_item_recover_intent doesn't actually restore the isize1 and isize2 fields that were recovered from an unfinished exchmaps log intent item. Instead, xfs_exchmaps_init_intent sets the wrong isize values from the recovered inodes, with the result that the file sizes are not set correctly when item recovery finishes. Fix this by restoring isize[12] from the log item.

Fixes 966ceafc7a4371 ("xfs: create deferred log items for file mapping exchanges")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/xfs_exchmaps_item.c |   19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)
fs/xfs/xfs_exchmaps_item.c +18 -1
@@ -347,7 +347,17 @@ xfs_xmi_validate(
 	if (!xfs_verify_fileext(mp, xlf->xmi_startoff1, xlf->xmi_blockcount))
 		return false;
 
-	return xfs_verify_fileext(mp, xlf->xmi_startoff2, xlf->xmi_blockcount);
+	if (!xfs_verify_fileext(mp, xlf->xmi_startoff2, xlf->xmi_blockcount))
+		return false;
+
+	if (xlf->xmi_flags & XFS_EXCHMAPS_SET_SIZES) {
+		if ((int64_t)xlf->xmi_isize1 < 0)
+			return false;
+		if ((int64_t)xlf->xmi_isize2 < 0)
+			return false;
+	}
+
+	return true;
 }
 
 /*
@@ -406,6 +416,13 @@ xfs_xmi_item_recover_intent(
 	*ipp1 = ip1;
 	*ipp2 = ip2;
 	xmi = xfs_exchmaps_init_intent(req);
+
+	/* Restore intended file sizes from recovered logged item */
+	if (req->flags & XFS_EXCHMAPS_SET_SIZES) {
+		xmi->xmi_isize1 = xlf->xmi_isize1;
+		xmi->xmi_isize2 = xlf->xmi_isize2;
+	}
+
 	xfs_defer_add_item(dfp, &xmi->xmi_list);
 	return xmi;
 
author maintainer
#1128 lore raw

PATCH 1127/1250 xfs: actually check internal-rtdev fields in the superblock

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 1ee2ce797c360785a3813fef62c90f427f3aed34 upstream.

LOLLM points out that the superblock scrubber doesn't check the new fields that were added for internal realtime volumes when we added zoned device support.

Fixes 2167eaabe2fadd ("xfs: define the zoned on-disk format")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/agheader.c | 7 +++++++
 1 file changed, 7 insertions(+)
fs/xfs/scrub/agheader.c +7 -0
index 1fa66aa68e16..fa5d32ec020a 100644
@@ -418,6 +418,13 @@ xchk_superblock(
 			xchk_block_set_corrupt(sc, bp);
 	}
 
+	if (xfs_has_zoned(mp)) {
+		if (sb->sb_rtstart != cpu_to_be64(mp->m_sb.sb_rtstart))
+			xchk_block_set_corrupt(sc, bp);
+		if (sb->sb_rtreserved != cpu_to_be64(mp->m_sb.sb_rtreserved))
+			xchk_block_set_corrupt(sc, bp);
+	}
+
 	/* Everything else must be zero. */
 	sblen = xchk_superblock_ondisk_size(mp);
 	if (memchr_inv((char *)sb + sblen, 0, BBTOB(bp->b_length) - sblen))
Signature · 1 line
2.55.0
author maintainer
#1129 lore raw

PATCH 1128/1250 smb: client: fix UBSAN array-index-out-of-bounds in smb2_copychunk_range

In reply to Greg Kroah-Hartman

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

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

From: Henrique Carvalho <[email protected]>

commit fa2fd0b10f66b08bc44745feed1761d7c1539d6e upstream.

struct copychunk_ioctl_req::ChunkCount is annotated with __counted_by_le() as the number of elements in Chunks[].

smb2_copychunk_range reuses ChunkCount to store the number of chunks sent in the current iteration. If a later iteration populates more chunks than a previous one, the stale smaller value trips UBSAN.

Set ChunkCount to chunk_count (allocated capacity) before populating Chunks[].

---
 fs/smb/client/smb2ops.c |    6 ++++++
 1 file changed, 6 insertions(+)
fs/smb/client/smb2ops.c +6 -0
@@ -1970,6 +1970,12 @@ retry:
 		src_off_prev = src_off;
 		dst_off_prev = dst_off;
 
+		/*
+		 * __counted_by_le(ChunkCount): set to allocated chunks before
+		 * populating Chunks[]
+		 */
+		cc_req->ChunkCount = cpu_to_le32(chunk_count);
+
 		chunks = 0;
 		copy_bytes = 0;
 		copy_bytes_left = umin(total_bytes_left, tcon->max_bytes_copy);
author maintainer
#1130 lore raw

PATCH 1129/1250 hwmon: (sht4x) Add missing locks

In reply to Greg Kroah-Hartman

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

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

From: Guenter Roeck <[email protected]>

commit 06b7cf395b1fb652a50db39674a759658fbfba0d upstream.

Sashiko reports:

Heater sysfs callbacks (heater_enable_store, heater_power_store, and heater_time_store) are exposed to data races without the hwmon lock.

If a user-space process reads hwmon data while another process enables the heater, heater_enable_store() executes without holding hwmon_lock(dev). This can interleave I2C commands and mutate shared state (data->heating_complete and data->data_pending) concurrently with sht4x_read_values(), leading to corrupted I2C sequences.

Fixes 53dfa12299c1 ("hwmon: (sht4x) Rely on subsystem locking")
Cc Alessandro Zini <[email protected]>
---
 drivers/hwmon/sht4x.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/hwmon/sht4x.c +6 -0
@@ -277,6 +277,8 @@ static ssize_t heater_enable_store(struc
 		heating_time_bound = 1100;
 	}
 
+	guard(hwmon_lock)(dev);
+
 	if (time_before(jiffies, data->heating_complete))
 		return -EBUSY;
 
@@ -314,6 +316,8 @@ static ssize_t heater_power_store(struct
 	if (power != 20 && power != 110 && power != 200)
 		return -EINVAL;
 
+	guard(hwmon_lock)(dev);
+
 	data->heater_power = power;
 
 	return count;
@@ -344,6 +348,8 @@ static ssize_t heater_time_store(struct
 	if (time != 100 && time != 1000)
 		return -EINVAL;
 
+	guard(hwmon_lock)(dev);
+
 	data->heater_time = time;
 
 	return count;
author maintainer
#1131 lore raw

PATCH 1130/1250 ksmbd: dont hold ci->m_lock while waiting for a lease break ack

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

commit 27a9bc968d4ae809a3f5ca2f2b136856ee613cb4 upstream.

When a cifs.ko client caches a read-handle (RH) lease via deferred close and a conflicting open arrives, ksmbd breaks the lease and waits for the acknowledgment in wait_for_break_ack() for up to OPLOCK_WAIT_TIME (35s). __smb_break_all_levII_oplock() runs that wait while holding ci->m_lock for read.

cifs.ko reacts to a handle-lease break by closing the deferred handle rather than sending a lease break acknowledgment. That close path (close_id_del_oplock() -> opinfo_del()) takes ci->m_lock for write and is exactly what would wake the waiter, but it blocks on the read lock held by the waiting thread. The break is then resolved only by the 35s timeout, so xfstests generic/001 takes ~78s with leases enabled versus ~4s with oplocks only.

Collect the target opinfos (each pinned with a reference) while holding ci->m_lock, then break them after releasing it, matching how smb_grant_oplock() already breaks a conflicting lease using only a reference. The reference keeps the opinfo (and its conn and lease) alive across the unlocked window, and a close racing the break is handled by the existing OPLOCK_CLOSING state check. Apply the same fix to the parent lease break paths.

[ This tree predates the deferred break machinery. The 6.18.y backport of the oplock break UAF fix (applied here as c8279ae8df68) calls oplock_break() directly while holding ci->m_lock, and its smb2_oplock_break_conn_get() takes down_read(ci->m_lock) internally, so the three walks self-nest the read lock and deadlock for up to OPLOCK_WAIT_TIME (35s) once a writer queues on the inode. Port the machinery above so the walks collect the breaks and drain them after releasing ci->m_lock, matching the 7.2.y shape of the fix. ]

---
 fs/smb/server/oplock.c | 63 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 58 insertions(+), 5 deletions(-)
fs/smb/server/oplock.c +58 -5
index 6fafcbd9aceb7..6183bd52ca9d1 100644
@@ -1033,6 +1033,43 @@ static int oplock_break(struct oplock_info *brk_opinfo, struct ksmbd_inode *ci,
 	return err;
 }
 
+struct oplock_break_entry {
+	struct list_head	list;
+	struct oplock_info	*opinfo;
+};
+
+/*
+ * Collect an oplock for a deferred break.  oplock_break() may block for
+ * the client's break acknowledgment, and the close that wakes that wait
+ * needs ci->m_lock for write, so the walks that hold ci->m_lock defer the
+ * break until the lock is released.
+ */
+static int oplock_break_add(struct list_head *head, struct oplock_info *opinfo)
+{
+	struct oplock_break_entry *ent;
+
+	ent = kmalloc_obj(struct oplock_break_entry, KSMBD_DEFAULT_GFP);
+	if (!ent)
+		return -ENOMEM;
+
+	ent->opinfo = opinfo;
+	list_add_tail(&ent->list, head);
+	return 0;
+}
+
+static void oplock_break_drain_none(struct list_head *head,
+				    struct ksmbd_inode *ci)
+{
+	struct oplock_break_entry *ent, *tmp;
+
+	list_for_each_entry_safe(ent, tmp, head, list) {
+		oplock_break(ent->opinfo, ci, SMB2_OPLOCK_LEVEL_NONE, NULL);
+		list_del(&ent->list);
+		opinfo_put(ent->opinfo);
+		kfree(ent);
+	}
+}
+
 void destroy_lease_table(struct ksmbd_conn *conn)
 {
 	struct lease_table *lb, *lbtmp;
@@ -1179,6 +1216,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
 {
 	struct oplock_info *opinfo;
 	struct ksmbd_inode *p_ci = NULL;
+	LIST_HEAD(brk_list);
 
 	if (lctx->version != 2)
 		return;
@@ -1204,12 +1242,14 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
 				continue;
 			}
 
-			oplock_break(opinfo, p_ci, SMB2_OPLOCK_LEVEL_NONE, NULL);
-			opinfo_put(opinfo);
+			if (oplock_break_add(&brk_list, opinfo))
+				opinfo_put(opinfo);
 		}
 	}
 	up_read(&p_ci->m_lock);
 
+	oplock_break_drain_none(&brk_list, p_ci);
+
 	ksmbd_inode_put(p_ci);
 }
 
@@ -1217,6 +1257,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
 {
 	struct oplock_info *opinfo;
 	struct ksmbd_inode *p_ci = NULL;
+	LIST_HEAD(brk_list);
 
 	rcu_read_lock();
 	opinfo = rcu_dereference(fp->f_opinfo);
@@ -1245,12 +1286,14 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
 				continue;
 			}
 
-			oplock_break(opinfo, p_ci, SMB2_OPLOCK_LEVEL_NONE, NULL);
-			opinfo_put(opinfo);
+			if (oplock_break_add(&brk_list, opinfo))
+				opinfo_put(opinfo);
 		}
 	}
 	up_read(&p_ci->m_lock);
 
+	oplock_break_drain_none(&brk_list, p_ci);
+
 	ksmbd_inode_put(p_ci);
 }
 
@@ -1461,6 +1504,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
 	struct oplock_info *op, *brk_op;
 	struct ksmbd_inode *ci;
 	struct ksmbd_conn *conn = work->conn;
+	LIST_HEAD(brk_list);
 
 	if (!test_share_config_flag(work->tcon->share_conf,
 				    KSMBD_SHARE_FLAG_OPLOCKS))
@@ -1502,12 +1546,21 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
 			    SMB2_LEASE_KEY_SIZE))
 			goto next;
 		brk_op->open_trunc = is_trunc;
-		oplock_break(brk_op, ci, SMB2_OPLOCK_LEVEL_NONE, NULL);
+
+		/*
+		 * Defer the break until ci->m_lock is released: oplock_break()
+		 * may block waiting for the break acknowledgment, and the
+		 * close that wakes that wait needs ci->m_lock for write.
+		 */
+		if (!oplock_break_add(&brk_list, brk_op))
+			continue;
 next:
 		opinfo_put(brk_op);
 	}
 	up_read(&ci->m_lock);
 
+	oplock_break_drain_none(&brk_list, ci);
+
 	if (op)
 		opinfo_put(op);
 }
Signature · 1 line
2.53.0
author maintainer
#1132 lore raw

PATCH 1131/1250 crypto: ccp - Fix possible deadlock in SEV init failure path

In reply to Greg Kroah-Hartman

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

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

From: Atish Patra <[email protected]>

[ Upstream commit b0e7ec0dab242c5e480121ba12400d0513e37ca2 ]

__sev_platform_init_handle_init_ex_path() calls rmp_mark_pages_firmware() with locked=false while the parent function of init_ex_path already acquired the sev_cmd_mutex.
In the case of an RMPUPDATE failure for any page after the first, the cleanup path would invoke reclaim pages which would result in a deadlock in sev_do_cmd.

Pass locked=true to honor the lock status of the parent function.

Fixes 7364a6fbca45 ("crypto: ccp: Handle non-volatile INIT_EX data when SNP is enabled")
Assisted-by Claude:claude-opus-4-6
Fixes 7364a6fbca45 ("crypto: ccp: Handle non-volatile INIT_EX data when SNP is enabled")
---
 drivers/crypto/ccp/sev-dev.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/crypto/ccp/sev-dev.c +1 -1
index 5aa0f158c43b8..8820efe5ef0af 100644
@@ -1531,7 +1531,7 @@ static int __sev_platform_init_handle_init_ex_path(struct sev_device *sev)
 		unsigned long npages;
 
 		npages = 1UL << get_order(NV_LENGTH);
-		if (rmp_mark_pages_firmware(__pa(sev_init_ex_buffer), npages, false)) {
+		if (rmp_mark_pages_firmware(__pa(sev_init_ex_buffer), npages, true)) {
 			dev_err(sev->dev, "SEV: INIT_EX NV memory page state change failed.\n");
 			rc = -ENOMEM;
 			/*
Signature · 1 line
2.53.0
author maintainer
#1133 lore raw

PATCH 1132/1250 Revert "arm64: dts: qcom: qcs6490-rb3gen2: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit 1a35eda51a50125d8e58b4f99f82034c58000630.

---
 arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)
arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts +9 -9
index 6be927c18482f..18cea88120014 100644
@@ -823,15 +823,15 @@ &pcie1 {
 	pinctrl-0 = <&pcie1_reset_n>, <&pcie1_wake_n>, <&pcie1_clkreq_n>;
 	pinctrl-names = "default";
 
-	iommu-map = <0x0 &apps_smmu 0x1c80 0x0 0x1>,
-		    <0x100 &apps_smmu 0x1c81 0x0 0x1>,
-		    <0x208 &apps_smmu 0x1c84 0x0 0x1>,
-		    <0x210 &apps_smmu 0x1c85 0x0 0x1>,
-		    <0x218 &apps_smmu 0x1c86 0x0 0x1>,
-		    <0x300 &apps_smmu 0x1c87 0x0 0x1>,
-		    <0x400 &apps_smmu 0x1c88 0x0 0x1>,
-		    <0x500 &apps_smmu 0x1c89 0x0 0x1>,
-		    <0x501 &apps_smmu 0x1c90 0x0 0x1>;
+	iommu-map = <0x0 &apps_smmu 0x1c80 0x1>,
+		    <0x100 &apps_smmu 0x1c81 0x1>,
+		    <0x208 &apps_smmu 0x1c84 0x1>,
+		    <0x210 &apps_smmu 0x1c85 0x1>,
+		    <0x218 &apps_smmu 0x1c86 0x1>,
+		    <0x300 &apps_smmu 0x1c87 0x1>,
+		    <0x400 &apps_smmu 0x1c88 0x1>,
+		    <0x500 &apps_smmu 0x1c89 0x1>,
+		    <0x501 &apps_smmu 0x1c90 0x1>;
 
 	status = "okay";
 };
Signature · 1 line
2.53.0
author maintainer
#1134 lore raw

PATCH 1133/1250 Revert "arm64: dts: qcom: lemans: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit e1c65abeb6aafa6daae8d183912f853a6e6a268f.

---
 arch/arm64/boot/dts/qcom/lemans.dtsi | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/boot/dts/qcom/lemans.dtsi +4 -4
index 806b6b5d4b205..0861e4ead93eb 100644
@@ -2739,8 +2739,8 @@ pcie0: pcie@1c00000 {
 					<&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_PCIE_0 0>;
 			interconnect-names = "pcie-mem", "cpu-pcie";
 
-			iommu-map = <0x0 &pcie_smmu 0x0000 0x0 0x1>,
-				    <0x100 &pcie_smmu 0x0001 0x0 0x1>;
+			iommu-map = <0x0 &pcie_smmu 0x0000 0x1>,
+				    <0x100 &pcie_smmu 0x0001 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>,
 				 <&gcc GCC_PCIE_0_LINK_DOWN_BCR>;
@@ -2912,8 +2912,8 @@ pcie1: pcie@1c10000 {
 					<&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_PCIE_1 0>;
 			interconnect-names = "pcie-mem", "cpu-pcie";
 
-			iommu-map = <0x0 &pcie_smmu 0x0080 0x0 0x1>,
-				    <0x100 &pcie_smmu 0x0081 0x0 0x1>;
+			iommu-map = <0x0 &pcie_smmu 0x0080 0x1>,
+				    <0x100 &pcie_smmu 0x0081 0x1>;
 
 			resets = <&gcc GCC_PCIE_1_BCR>,
 				 <&gcc GCC_PCIE_1_LINK_DOWN_BCR>;
Signature · 1 line
2.53.0
author maintainer
#1135 lore raw

PATCH 1134/1250 Revert "arm64: dts: qcom: talos: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit 349752c7ab6be4cc86034d98976d305cd8d986cd.

---
 arch/arm64/boot/dts/qcom/sm6150.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/boot/dts/qcom/sm6150.dtsi +2 -2
index ff3ae5688f03e..a066ad5ffde57 100644
@@ -1175,8 +1175,8 @@ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
 					 &config_noc SLAVE_PCIE_0 QCOM_ICC_TAG_ACTIVE_ONLY>;
 			interconnect-names = "pcie-mem", "cpu-pcie";
 
-			iommu-map = <0x0 &apps_smmu 0x400 0x0 0x1>,
-				    <0x100 &apps_smmu 0x401 0x0 0x1>;
+			iommu-map = <0x0 &apps_smmu 0x400 0x1>,
+				    <0x100 &apps_smmu 0x401 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
Signature · 1 line
2.53.0
author maintainer
#1136 lore raw

PATCH 1135/1250 Revert "arm64: dts: qcom: sm8750: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit fc2980ea692027f529afb7b127157d45650d6c6e.

---
 arch/arm64/boot/dts/qcom/sm8750.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/boot/dts/qcom/sm8750.dtsi +2 -2
index c6f4a43ff133e..b714207e4c158 100644
@@ -3393,8 +3393,8 @@ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
 			interconnect-names = "pcie-mem",
 					     "cpu-pcie";
 
-			iommu-map = <0x0   &apps_smmu 0x1400 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1401 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1400 0x1>,
+				    <0x100 &apps_smmu 0x1401 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
Signature · 1 line
2.53.0
author maintainer
#1137 lore raw

PATCH 1136/1250 Revert "arm64: dts: qcom: sm8650: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit f418e83ac1abec16df99e36185c44b7a08c2aaf7.

---
 arch/arm64/boot/dts/qcom/sm8650.dtsi | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/boot/dts/qcom/sm8650.dtsi +4 -4
index 349ce22c6b686..6c775ef20cb0a 100644
@@ -3626,8 +3626,8 @@ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
 
 			operating-points-v2 = <&pcie0_opp_table>;
 
-			iommu-map = <0     &apps_smmu 0x1400 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1401 0x0 0x1>;
+			iommu-map = <0     &apps_smmu 0x1400 0x1>,
+				    <0x100 &apps_smmu 0x1401 0x1>;
 
 			interrupt-map = <0 0 0 1 &intc 0 0 GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH 0>,
 					<0 0 0 2 &intc 0 0 GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH 0>,
@@ -3806,8 +3806,8 @@ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
 
 			operating-points-v2 = <&pcie1_opp_table>;
 
-			iommu-map = <0     &apps_smmu 0x1480 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1481 0x0 0x1>;
+			iommu-map = <0     &apps_smmu 0x1480 0x1>,
+				    <0x100 &apps_smmu 0x1481 0x1>;
 
 			interrupt-map = <0 0 0 1 &intc 0 0 GIC_SPI 434 IRQ_TYPE_LEVEL_HIGH 0>,
 					<0 0 0 2 &intc 0 0 GIC_SPI 435 IRQ_TYPE_LEVEL_HIGH 0>,
Signature · 1 line
2.53.0
author maintainer
#1138 lore raw

PATCH 1137/1250 Revert "arm64: dts: qcom: sm8550: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit e8a7928da474e5e944f4747b096cc8515b3ffb73.

---
 arch/arm64/boot/dts/qcom/sm8550.dtsi | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/boot/dts/qcom/sm8550.dtsi +4 -4
index e38d3ea19e7dd..479c94f5066f3 100644
@@ -2009,8 +2009,8 @@ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
 
 			msi-map = <0x0 &gic_its 0x1400 0x1>,
 				  <0x100 &gic_its 0x1401 0x1>;
-			iommu-map = <0x0   &apps_smmu 0x1400 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1401 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1400 0x1>,
+				    <0x100 &apps_smmu 0x1401 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
@@ -2175,8 +2175,8 @@ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
 
 			msi-map = <0x0 &gic_its 0x1480 0x1>,
 				  <0x100 &gic_its 0x1481 0x1>;
-			iommu-map = <0x0   &apps_smmu 0x1480 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1481 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1480 0x1>,
+				    <0x100 &apps_smmu 0x1481 0x1>;
 
 			resets = <&gcc GCC_PCIE_1_BCR>,
 				<&gcc GCC_PCIE_1_LINK_DOWN_BCR>;
Signature · 1 line
2.53.0
author maintainer
#1139 lore raw

PATCH 1138/1250 Revert "arm64: dts: qcom: sm8450: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit dbcbbb2033736a203230f80fe317be3a8badd79b.

---
 arch/arm64/boot/dts/qcom/sm8450.dtsi | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/boot/dts/qcom/sm8450.dtsi +4 -4
index cabd90b5024a3..991f3cbde1c8d 100644
@@ -2023,8 +2023,8 @@ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
 				      "aggre0",
 				      "aggre1";
 
-			iommu-map = <0x0   &apps_smmu 0x1c00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c01 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c00 0x1>,
+				    <0x100 &apps_smmu 0x1c01 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
@@ -2185,8 +2185,8 @@ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
 				      "ddrss_sf_tbu",
 				      "aggre1";
 
-			iommu-map = <0x0   &apps_smmu 0x1c80 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c81 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c80 0x1>,
+				    <0x100 &apps_smmu 0x1c81 0x1>;
 
 			resets = <&gcc GCC_PCIE_1_BCR>;
 			reset-names = "pci";
Signature · 1 line
2.53.0
author maintainer
#1140 lore raw

PATCH 1139/1250 Revert "arm64: dts: qcom: sm8350: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit 793dbaa91c3bfbd099b132cc4b8bb6e7856aa3e3.

---
 arch/arm64/boot/dts/qcom/sm8350.dtsi | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/boot/dts/qcom/sm8350.dtsi +4 -4
index 2bbc1f28be88b..af892b76322fd 100644
@@ -1575,8 +1575,8 @@ pcie0: pcie@1c00000 {
 				      "aggre1",
 				      "aggre0";
 
-			iommu-map = <0x0   &apps_smmu 0x1c00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c01 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c00 0x1>,
+				    <0x100 &apps_smmu 0x1c01 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
@@ -1684,8 +1684,8 @@ pcie1: pcie@1c08000 {
 				      "ddrss_sf_tbu",
 				      "aggre1";
 
-			iommu-map = <0x0   &apps_smmu 0x1c80 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c81 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c80 0x1>,
+				    <0x100 &apps_smmu 0x1c81 0x1>;
 
 			resets = <&gcc GCC_PCIE_1_BCR>;
 			reset-names = "pci";
Signature · 1 line
2.53.0
author maintainer
#1141 lore raw

PATCH 1140/1250 Revert "arm64: dts: qcom: sm8250: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit c8fd2cabd0b7c1cf68ca1d78cd23e71e245ae032.

---
 arch/arm64/boot/dts/qcom/sm8250.dtsi | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
arch/arm64/boot/dts/qcom/sm8250.dtsi +6 -6
index 26ae05ab663c6..dfba5bc3ba7cf 100644
@@ -2190,8 +2190,8 @@ pcie0: pcie@1c00000 {
 				      "tbu",
 				      "ddrss_sf_tbu";
 
-			iommu-map = <0x0   &apps_smmu 0x1c00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c01 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c00 0x1>,
+				    <0x100 &apps_smmu 0x1c01 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
@@ -2317,8 +2317,8 @@ pcie1: pcie@1c08000 {
 			assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>;
 			assigned-clock-rates = <19200000>;
 
-			iommu-map = <0x0   &apps_smmu 0x1c80 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c81 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c80 0x1>,
+				    <0x100 &apps_smmu 0x1c81 0x1>;
 
 			resets = <&gcc GCC_PCIE_1_BCR>;
 			reset-names = "pci";
@@ -2444,8 +2444,8 @@ pcie2: pcie@1c10000 {
 			assigned-clocks = <&gcc GCC_PCIE_2_AUX_CLK>;
 			assigned-clock-rates = <19200000>;
 
-			iommu-map = <0x0   &apps_smmu 0x1d00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1d01 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1d00 0x1>,
+				    <0x100 &apps_smmu 0x1d01 0x1>;
 
 			resets = <&gcc GCC_PCIE_2_BCR>;
 			reset-names = "pci";
Signature · 1 line
2.53.0
author maintainer
#1142 lore raw

PATCH 1141/1250 Revert "arm64: dts: qcom: sm8150: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit 152e82a60d70e6da88d784b305bae6f9171aca9f.

---
 arch/arm64/boot/dts/qcom/sm8150.dtsi | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/boot/dts/qcom/sm8150.dtsi +4 -4
index 35981d6026b3c..e1f480e3ed0b7 100644
@@ -1884,8 +1884,8 @@ pcie0: pcie@1c00000 {
 				      "bus_slave",
 				      "slave_q2a";
 
-			iommu-map = <0x0   &apps_smmu 0x1d80 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1d81 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1d80 0x1>,
+				    <0x100 &apps_smmu 0x1d81 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
@@ -2002,8 +2002,8 @@ pcie1: pcie@1c08000 {
 			assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>;
 			assigned-clock-rates = <19200000>;
 
-			iommu-map = <0x0   &apps_smmu 0x1e00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1e01 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1e00 0x1>,
+				    <0x100 &apps_smmu 0x1e01 0x1>;
 
 			resets = <&gcc GCC_PCIE_1_BCR>;
 			reset-names = "pci";
Signature · 1 line
2.53.0
author maintainer
#1143 lore raw

PATCH 1142/1250 Revert "arm64: dts: qcom: sdm845: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit a98b8bb0ca25f05b15267da97dc665dd9e13309b.

---
 arch/arm64/boot/dts/qcom/sdm845.dtsi | 64 ++++++++++++++--------------
 1 file changed, 32 insertions(+), 32 deletions(-)
arch/arm64/boot/dts/qcom/sdm845.dtsi +32 -32
index c49ef738d0947..13c9515260ef1 100644
@@ -2367,22 +2367,22 @@ pcie0: pcie@1c00000 {
 				      "slave_q2a",
 				      "tbu";
 
-			iommu-map = <0x0   &apps_smmu 0x1c10 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c11 0x0 0x1>,
-				    <0x200 &apps_smmu 0x1c12 0x0 0x1>,
-				    <0x300 &apps_smmu 0x1c13 0x0 0x1>,
-				    <0x400 &apps_smmu 0x1c14 0x0 0x1>,
-				    <0x500 &apps_smmu 0x1c15 0x0 0x1>,
-				    <0x600 &apps_smmu 0x1c16 0x0 0x1>,
-				    <0x700 &apps_smmu 0x1c17 0x0 0x1>,
-				    <0x800 &apps_smmu 0x1c18 0x0 0x1>,
-				    <0x900 &apps_smmu 0x1c19 0x0 0x1>,
-				    <0xa00 &apps_smmu 0x1c1a 0x0 0x1>,
-				    <0xb00 &apps_smmu 0x1c1b 0x0 0x1>,
-				    <0xc00 &apps_smmu 0x1c1c 0x0 0x1>,
-				    <0xd00 &apps_smmu 0x1c1d 0x0 0x1>,
-				    <0xe00 &apps_smmu 0x1c1e 0x0 0x1>,
-				    <0xf00 &apps_smmu 0x1c1f 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c10 0x1>,
+				    <0x100 &apps_smmu 0x1c11 0x1>,
+				    <0x200 &apps_smmu 0x1c12 0x1>,
+				    <0x300 &apps_smmu 0x1c13 0x1>,
+				    <0x400 &apps_smmu 0x1c14 0x1>,
+				    <0x500 &apps_smmu 0x1c15 0x1>,
+				    <0x600 &apps_smmu 0x1c16 0x1>,
+				    <0x700 &apps_smmu 0x1c17 0x1>,
+				    <0x800 &apps_smmu 0x1c18 0x1>,
+				    <0x900 &apps_smmu 0x1c19 0x1>,
+				    <0xa00 &apps_smmu 0x1c1a 0x1>,
+				    <0xb00 &apps_smmu 0x1c1b 0x1>,
+				    <0xc00 &apps_smmu 0x1c1c 0x1>,
+				    <0xd00 &apps_smmu 0x1c1d 0x1>,
+				    <0xe00 &apps_smmu 0x1c1e 0x1>,
+				    <0xf00 &apps_smmu 0x1c1f 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
@@ -2497,22 +2497,22 @@ pcie1: pcie@1c08000 {
 			assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>;
 			assigned-clock-rates = <19200000>;
 
-			iommu-map = <0x0   &apps_smmu 0x1c00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c01 0x0 0x1>,
-				    <0x200 &apps_smmu 0x1c02 0x0 0x1>,
-				    <0x300 &apps_smmu 0x1c03 0x0 0x1>,
-				    <0x400 &apps_smmu 0x1c04 0x0 0x1>,
-				    <0x500 &apps_smmu 0x1c05 0x0 0x1>,
-				    <0x600 &apps_smmu 0x1c06 0x0 0x1>,
-				    <0x700 &apps_smmu 0x1c07 0x0 0x1>,
-				    <0x800 &apps_smmu 0x1c08 0x0 0x1>,
-				    <0x900 &apps_smmu 0x1c09 0x0 0x1>,
-				    <0xa00 &apps_smmu 0x1c0a 0x0 0x1>,
-				    <0xb00 &apps_smmu 0x1c0b 0x0 0x1>,
-				    <0xc00 &apps_smmu 0x1c0c 0x0 0x1>,
-				    <0xd00 &apps_smmu 0x1c0d 0x0 0x1>,
-				    <0xe00 &apps_smmu 0x1c0e 0x0 0x1>,
-				    <0xf00 &apps_smmu 0x1c0f 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c00 0x1>,
+				    <0x100 &apps_smmu 0x1c01 0x1>,
+				    <0x200 &apps_smmu 0x1c02 0x1>,
+				    <0x300 &apps_smmu 0x1c03 0x1>,
+				    <0x400 &apps_smmu 0x1c04 0x1>,
+				    <0x500 &apps_smmu 0x1c05 0x1>,
+				    <0x600 &apps_smmu 0x1c06 0x1>,
+				    <0x700 &apps_smmu 0x1c07 0x1>,
+				    <0x800 &apps_smmu 0x1c08 0x1>,
+				    <0x900 &apps_smmu 0x1c09 0x1>,
+				    <0xa00 &apps_smmu 0x1c0a 0x1>,
+				    <0xb00 &apps_smmu 0x1c0b 0x1>,
+				    <0xc00 &apps_smmu 0x1c0c 0x1>,
+				    <0xd00 &apps_smmu 0x1c0d 0x1>,
+				    <0xe00 &apps_smmu 0x1c0e 0x1>,
+				    <0xf00 &apps_smmu 0x1c0f 0x1>;
 
 			resets = <&gcc GCC_PCIE_1_BCR>;
 			reset-names = "pci";
Signature · 1 line
2.53.0
author maintainer
#1144 lore raw

PATCH 1143/1250 Revert "arm64: dts: qcom: sc8180x: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit 8dfc4641001fb96db7ec5c8beb9db50379c5c543.

---
 arch/arm64/boot/dts/qcom/sc8180x.dtsi | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
arch/arm64/boot/dts/qcom/sc8180x.dtsi +8 -8
index faf12c6e679cb..c6d96022c666d 100644
@@ -1762,8 +1762,8 @@ pcie0: pcie@1c00000 {
 			assigned-clocks = <&gcc GCC_PCIE_0_AUX_CLK>;
 			assigned-clock-rates = <19200000>;
 
-			iommu-map = <0x0   &apps_smmu 0x1d80 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1d81 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1d80 0x1>,
+				    <0x100 &apps_smmu 0x1d81 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
@@ -1881,8 +1881,8 @@ pcie3: pcie@1c08000 {
 			assigned-clocks = <&gcc GCC_PCIE_3_AUX_CLK>;
 			assigned-clock-rates = <19200000>;
 
-			iommu-map = <0x0   &apps_smmu 0x1e00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1e01 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1e00 0x1>,
+				    <0x100 &apps_smmu 0x1e01 0x1>;
 
 			resets = <&gcc GCC_PCIE_3_BCR>;
 			reset-names = "pci";
@@ -2001,8 +2001,8 @@ pcie1: pcie@1c10000 {
 			assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>;
 			assigned-clock-rates = <19200000>;
 
-			iommu-map = <0x0   &apps_smmu 0x1c80 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c81 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c80 0x1>,
+				    <0x100 &apps_smmu 0x1c81 0x1>;
 
 			resets = <&gcc GCC_PCIE_1_BCR>;
 			reset-names = "pci";
@@ -2121,8 +2121,8 @@ pcie2: pcie@1c18000 {
 			assigned-clocks = <&gcc GCC_PCIE_2_AUX_CLK>;
 			assigned-clock-rates = <19200000>;
 
-			iommu-map = <0x0   &apps_smmu 0x1d00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1d01 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1d00 0x1>,
+				    <0x100 &apps_smmu 0x1d01 0x1>;
 
 			resets = <&gcc GCC_PCIE_2_BCR>;
 			reset-names = "pci";
Signature · 1 line
2.53.0
author maintainer
#1145 lore raw

PATCH 1144/1250 Revert "arm64: dts: qcom: sar2130p: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit 54176af35d989264e4dce0bee10dd3e4dafb4efb.

---
 arch/arm64/boot/dts/qcom/sar2130p.dtsi | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/boot/dts/qcom/sar2130p.dtsi +4 -4
index d7a80d832f080..d65ad0df68652 100644
@@ -1329,8 +1329,8 @@ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
 					 &config_noc SLAVE_PCIE_0 QCOM_ICC_TAG_ALWAYS>;
 			interconnect-names = "pcie-mem", "cpu-pcie";
 
-			iommu-map = <0x0   &apps_smmu 0x1c00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c01 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c00 0x1>,
+				    <0x100 &apps_smmu 0x1c01 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
@@ -1455,8 +1455,8 @@ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
 					 &config_noc SLAVE_PCIE_1 QCOM_ICC_TAG_ALWAYS>;
 			interconnect-names = "pcie-mem", "cpu-pcie";
 
-			iommu-map = <0x0   &apps_smmu 0x1e00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1e01 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1e00 0x1>,
+				    <0x100 &apps_smmu 0x1e01 0x1>;
 
 			resets = <&gcc GCC_PCIE_1_BCR>,
 				<&gcc GCC_PCIE_1_LINK_DOWN_BCR>;
Signature · 1 line
2.53.0
author maintainer
#1146 lore raw

PATCH 1145/1250 Revert "arm64: dts: qcom: kodiak: Fix the PCIe iommu-map entries"

In reply to Greg Kroah-Hartman

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

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

This reverts commit d2b404826bf93c6102ceb5d3186ef41675f0f6ad.

---
 arch/arm64/boot/dts/qcom/sc7280.dtsi | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/boot/dts/qcom/sc7280.dtsi +4 -4
index 7d53e06e97b07..0f566dd75c964 100644
@@ -2273,8 +2273,8 @@ pcie0: pcie@1c00000 {
 				      "aggre0",
 				      "aggre1";
 
-			iommu-map = <0x0   &apps_smmu 0x1c00 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c01 0x0 0x1>;
+			iommu-map = <0x0   &apps_smmu 0x1c00 0x1>,
+				    <0x100 &apps_smmu 0x1c01 0x1>;
 
 			resets = <&gcc GCC_PCIE_0_BCR>;
 			reset-names = "pci";
@@ -2419,8 +2419,8 @@ pcie1: pcie@1c08000 {
 
 			dma-coherent;
 
-			iommu-map = <0x0 &apps_smmu 0x1c80 0x0 0x1>,
-				    <0x100 &apps_smmu 0x1c81 0x0 0x1>;
+			iommu-map = <0x0 &apps_smmu 0x1c80 0x1>,
+				    <0x100 &apps_smmu 0x1c81 0x1>;
 
 			status = "disabled";
 
Signature · 1 line
2.53.0
author maintainer
#1147 lore raw

PATCH 1146/1250 xdrgen: Fix union declarations

In reply to Greg Kroah-Hartman

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

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

From: Chuck Lever <[email protected]>

[ Upstream commit f7cb94fad4e6cec354a3ea779f91fe5560fb72b6 ]

Add a missing template file. This file is used when a union is defined as a public API (ie, "pragma public <union name>;").

---
 .../net/sunrpc/xdrgen/templates/C/union/declaration/close.j2  | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 tools/net/sunrpc/xdrgen/templates/C/union/declaration/close.j2
tools/net/sunrpc/xdrgen/templates/C/union/declaration/close.j2 new file +4 -0
new file mode 100644
index 0000000000000..816291184e8c5
@@ -0,0 +1,4 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr);
+bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value);
Signature · 1 line
2.53.0
author maintainer
#1148 lore raw

PATCH 1147/1250 usb: xusbatm: dont rely on id table pointer arithmetic

In reply to Greg Kroah-Hartman

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

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

From: Gary Guo <[email protected]>

[ Upstream commit eb6cd6d3d8abeac5d7e8251b898067184afdad8a ]

The current code is broken when dynamic ID is involved; in such cases usb_device_id parameter of probe lives on the heap and the pointer arithmetic will get an index that is wildly out of bound. xusbatm initialize the USB device IDs dynamically so it can just use driver_info too.

Even with conversion, xusbatm still cannot support dynamic IDs, so also set no_dynamic_id.

---
 drivers/usb/atm/xusbatm.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/usb/atm/xusbatm.c +4 -2
index 0befbf63d1cc8..5c1e1f5215555 100644
@@ -79,7 +79,7 @@ static int xusbatm_bind(struct usbatm_data *usbatm,
 			struct usb_interface *intf, const struct usb_device_id *id)
 {
 	struct usb_device *usb_dev = interface_to_usbdev(intf);
-	int drv_ix = id - xusbatm_usb_ids;
+	int drv_ix = id->driver_info;
 	int rx_alt = rx_altsetting[drv_ix];
 	int tx_alt = tx_altsetting[drv_ix];
 	struct usb_interface *rx_intf = xusbatm_find_intf(usb_dev, rx_alt, rx_endpoint[drv_ix]);
@@ -168,7 +168,8 @@ static struct usb_driver xusbatm_usb_driver = {
 	.name		= xusbatm_driver_name,
 	.probe		= xusbatm_usb_probe,
 	.disconnect	= usbatm_usb_disconnect,
-	.id_table	= xusbatm_usb_ids
+	.id_table	= xusbatm_usb_ids,
+	.no_dynamic_id	= 1,
 };
 
 static int __init xusbatm_init(void)
@@ -190,6 +191,7 @@ static int __init xusbatm_init(void)
 		xusbatm_usb_ids[i].match_flags	= USB_DEVICE_ID_MATCH_DEVICE;
 		xusbatm_usb_ids[i].idVendor	= vendor[i];
 		xusbatm_usb_ids[i].idProduct	= product[i];
+		xusbatm_usb_ids[i].driver_info	= i;
 
 		xusbatm_drivers[i].driver_name	= xusbatm_driver_name;
 		xusbatm_drivers[i].bind		= xusbatm_bind;
Signature · 1 line
2.53.0
author maintainer
#1149 lore raw

PATCH 1148/1250 wifi: ath9k_htc: dont store usb_device_id

In reply to Greg Kroah-Hartman

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

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

From: Gary Guo <[email protected]>

[ Upstream commit 14d2ac442d660e112efc0ce87ad10085013ed2b1 ]

usb_device_id is not guaranteed to live longer than probe due to presence of dynamic ID. All information apart from driver_data can be easily retrieved from usb_device, so just store driver_data.

---
 drivers/net/wireless/ath/ath9k/hif_usb.c | 12 ++++++------
 drivers/net/wireless/ath/ath9k/hif_usb.h |  2 +-
 2 files changed, 7 insertions(+), 7 deletions(-)
drivers/net/wireless/ath/ath9k/hif_usb.c +6 -6
index 0a3d2190b9bee..c15219257499f 100644
@@ -1097,7 +1097,7 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev)
 	}
 	kfree(buf);
 
-	if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info))
+	if (IS_AR7010_DEVICE(hif_dev->id_info))
 		firm_offset = AR7010_FIRMWARE_TEXT;
 	else
 		firm_offset = AR9271_FIRMWARE_TEXT;
@@ -1192,7 +1192,7 @@ static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev,
 	if (MAJOR_VERSION_REQ == 1 && hif_dev->fw_minor_index == 3) {
 		const char *filename;
 
-		if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info))
+		if (IS_AR7010_DEVICE(hif_dev->id_info))
 			filename = FIRMWARE_AR7010_1_1;
 		else
 			filename = FIRMWARE_AR9271;
@@ -1208,7 +1208,7 @@ static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev,
 
 		return -ENOENT;
 	} else {
-		if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info))
+		if (IS_AR7010_DEVICE(hif_dev->id_info))
 			chip = "7010";
 		else
 			chip = "9271";
@@ -1265,9 +1265,9 @@ static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context)
 
 	ret = ath9k_htc_hw_init(hif_dev->htc_handle,
 				&hif_dev->interface->dev,
-				hif_dev->usb_device_id->idProduct,
+				le16_to_cpu(hif_dev->udev->descriptor.idProduct),
 				hif_dev->udev->product,
-				hif_dev->usb_device_id->driver_info);
+				hif_dev->id_info);
 	if (ret) {
 		ret = -EINVAL;
 		goto err_htc_hw_init;
@@ -1381,7 +1381,7 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface,
 
 	hif_dev->udev = udev;
 	hif_dev->interface = interface;
-	hif_dev->usb_device_id = id;
+	hif_dev->id_info = id->driver_info;
 #ifdef CONFIG_PM
 	udev->reset_resume = 1;
 #endif
drivers/net/wireless/ath/ath9k/hif_usb.h +1 -1
index b3e66b0485a56..02655ed4e66ff 100644
@@ -115,7 +115,7 @@ struct cmd_buf {
 struct hif_device_usb {
 	struct usb_device *udev;
 	struct usb_interface *interface;
-	const struct usb_device_id *usb_device_id;
+	int id_info;
 	const void *fw_data;
 	size_t fw_size;
 	struct completion fw_done;
Signature · 1 line
2.53.0
author maintainer
#1150 lore raw

PATCH 1149/1250 usb: usbtmc: dont store usb_device_id

In reply to Greg Kroah-Hartman

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

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

From: Gary Guo <[email protected]>

[ Upstream commit fc045acec1a501c97f84ae184aadce9dae4ba9b2 ]

usb_device_id is not guaranteed to live longer than probe due to presence of dynamic ID. This stored ID is unused so remove it.

---
 drivers/usb/class/usbtmc.c | 2 --
 1 file changed, 2 deletions(-)
drivers/usb/class/usbtmc.c +0 -2
index fe88d555142ad..5bfbf801f9c4f 100644
@@ -71,7 +71,6 @@ struct usbtmc_dev_capabilities {
  * allocated for each USBTMC device in the driver's probe function.
  */
 struct usbtmc_device_data {
-	const struct usb_device_id *id;
 	struct usb_device *usb_dev;
 	struct usb_interface *intf;
 	struct list_head file_list;
@@ -2398,7 +2397,6 @@ static int usbtmc_probe(struct usb_interface *intf,
 		return -ENOMEM;
 
 	data->intf = intf;
-	data->id = id;
 	data->usb_dev = usb_get_dev(interface_to_usbdev(intf));
 	usb_set_intfdata(intf, data);
 	kref_init(&data->kref);
Signature · 1 line
2.53.0
author maintainer
#1151 lore raw

PATCH 1150/1250 usb: serial: spcp8x5: dont store usb_device_id

In reply to Greg Kroah-Hartman

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

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

From: Gary Guo <[email protected]>

[ Upstream commit 934e1322f18c1b58bca431c0d5d01e002060c990 ]

USB probe functions should not keep usb_device_id for longer than probe due to presence of dynamic ID removal. USB serial does not support ID removal, however in this case only driver_data is ever needed, there is no reason keeping the usb_device_id in the first place, so convert it as well.

---
 drivers/usb/serial/spcp8x5.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/usb/serial/spcp8x5.c +3 -3
index aa8fe0dcb680e..8418d812eb3cd 100644
@@ -135,14 +135,14 @@ struct spcp8x5_private {
 static int spcp8x5_probe(struct usb_serial *serial,
 						const struct usb_device_id *id)
 {
-	usb_set_serial_data(serial, (void *)id);
+	usb_set_serial_data(serial, (void *)id->driver_info);
 
 	return 0;
 }
 
 static int spcp8x5_port_probe(struct usb_serial_port *port)
 {
-	const struct usb_device_id *id = usb_get_serial_data(port->serial);
+	unsigned int quirks = (unsigned int)(unsigned long)usb_get_serial_data(port->serial);
 	struct spcp8x5_private *priv;
 
 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -150,7 +150,7 @@ static int spcp8x5_port_probe(struct usb_serial_port *port)
 		return -ENOMEM;
 
 	spin_lock_init(&priv->lock);
-	priv->quirks = id->driver_info;
+	priv->quirks = quirks;
 
 	usb_set_serial_port_data(port, priv);
 
Signature · 1 line
2.53.0
author maintainer
#1152 lore raw

PATCH 1151/1250 media: as102: do not rely on id table address comparison

In reply to Greg Kroah-Hartman

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

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

From: Gary Guo <[email protected]>

[ Upstream commit 91a8c8c718889fc8ccf5c38b750d790e9f36f92d ]

The driver info should be retrieved using the driver_info field, not by address comparison.

---
 drivers/media/usb/as102/as102_usb_drv.c | 73 ++++++++++---------------
 1 file changed, 30 insertions(+), 43 deletions(-)
drivers/media/usb/as102/as102_usb_drv.c +30 -43
index 44565f0297cdb..01f6164002dcc 100644
@@ -24,35 +24,33 @@ static void as102_usb_stop_stream(struct as102_dev_t *dev);
 static int as102_open(struct inode *inode, struct file *file);
 static int as102_release(struct inode *inode, struct file *file);
 
-static const struct usb_device_id as102_usb_id_table[] = {
-	{ USB_DEVICE(AS102_USB_DEVICE_VENDOR_ID, AS102_USB_DEVICE_PID_0001) },
-	{ USB_DEVICE(PCTV_74E_USB_VID, PCTV_74E_USB_PID) },
-	{ USB_DEVICE(ELGATO_EYETV_DTT_USB_VID, ELGATO_EYETV_DTT_USB_PID) },
-	{ USB_DEVICE(NBOX_DVBT_DONGLE_USB_VID, NBOX_DVBT_DONGLE_USB_PID) },
-	{ USB_DEVICE(SKY_IT_DIGITAL_KEY_USB_VID, SKY_IT_DIGITAL_KEY_USB_PID) },
-	{ } /* Terminating entry */
+struct as102_dev_info {
+	const char *name;
+	/*
+	 * eLNA configuration: devices built on the reference design work best
+	 * with 0xA0, while custom designs seem to require 0xC0
+	 */
+	uint8_t elna_cfg;
 };
 
-/* Note that this table must always have the same number of entries as the
-   as102_usb_id_table struct */
-static const char * const as102_device_names[] = {
-	AS102_REFERENCE_DESIGN,
-	AS102_PCTV_74E,
-	AS102_ELGATO_EYETV_DTT_NAME,
-	AS102_NBOX_DVBT_DONGLE_NAME,
-	AS102_SKY_IT_DIGITAL_KEY_NAME,
-	NULL /* Terminating entry */
-};
+#define DRIVER_INFO(dev_name, dev_elna_cfg) \
+	.driver_info = (kernel_ulong_t)&(const struct as102_dev_info){ \
+		.name = (dev_name), \
+		.elna_cfg = (dev_elna_cfg), \
+	}
 
-/* eLNA configuration: devices built on the reference design work best
-   with 0xA0, while custom designs seem to require 0xC0 */
-static uint8_t const as102_elna_cfg[] = {
-	0xA0,
-	0xC0,
-	0xC0,
-	0xA0,
-	0xA0,
-	0x00 /* Terminating entry */
+static const struct usb_device_id as102_usb_id_table[] = {
+	{ USB_DEVICE(AS102_USB_DEVICE_VENDOR_ID, AS102_USB_DEVICE_PID_0001),
+	  DRIVER_INFO(AS102_REFERENCE_DESIGN, 0xA0) },
+	{ USB_DEVICE(PCTV_74E_USB_VID, PCTV_74E_USB_PID),
+	  DRIVER_INFO(AS102_PCTV_74E, 0xC0) },
+	{ USB_DEVICE(ELGATO_EYETV_DTT_USB_VID, ELGATO_EYETV_DTT_USB_PID),
+	  DRIVER_INFO(AS102_ELGATO_EYETV_DTT_NAME, 0xC0) },
+	{ USB_DEVICE(NBOX_DVBT_DONGLE_USB_VID, NBOX_DVBT_DONGLE_USB_PID),
+	  DRIVER_INFO(AS102_NBOX_DVBT_DONGLE_NAME, 0xA0) },
+	{ USB_DEVICE(SKY_IT_DIGITAL_KEY_USB_VID, SKY_IT_DIGITAL_KEY_USB_PID),
+	  DRIVER_INFO(AS102_SKY_IT_DIGITAL_KEY_NAME, 0xA0) },
+	{ } /* Terminating entry */
 };
 
 struct usb_driver as102_usb_driver = {
@@ -336,29 +334,18 @@ static int as102_usb_probe(struct usb_interface *intf,
 {
 	int ret;
 	struct as102_dev_t *as102_dev;
-	int i;
-
-	/* This should never actually happen */
-	if (ARRAY_SIZE(as102_usb_id_table) !=
-	    (sizeof(as102_device_names) / sizeof(const char *))) {
-		pr_err("Device names table invalid size");
-		return -EINVAL;
-	}
+	const struct as102_dev_info *info = (const struct as102_dev_info *)id->driver_info;
 
 	as102_dev = kzalloc(sizeof(struct as102_dev_t), GFP_KERNEL);
 	if (as102_dev == NULL)
 		return -ENOMEM;
 
-	/* Assign the user-friendly device name */
-	for (i = 0; i < ARRAY_SIZE(as102_usb_id_table); i++) {
-		if (id == &as102_usb_id_table[i]) {
-			as102_dev->name = as102_device_names[i];
-			as102_dev->elna_cfg = as102_elna_cfg[i];
-		}
-	}
-
-	if (as102_dev->name == NULL)
+	if (info) {
+		as102_dev->name = info->name;
+		as102_dev->elna_cfg = info->elna_cfg;
+	} else {
 		as102_dev->name = "Unknown AS102 device";
+	}
 
 	/* set private callback functions */
 	as102_dev->bus_adap.ops = &as102_priv_ops;
Signature · 1 line
2.53.0
author maintainer
#1153 lore raw

PATCH 1152/1250 net: usb: pegasus: dont rely on id table pointer arithmetic

In reply to Greg Kroah-Hartman

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

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

From: Gary Guo <[email protected]>

[ Upstream commit ce8101c331956bbd3e20681331dfd22eb7c1c1ea ]

The current code is broken when dynamic ID is involved; in such cases usb_device_id parameter of probe lives on the heap and the pointer arithmetic will get an index that is wildly out of bound. Instead of keeping a side table for additional information, use driver_info field of the usb_device_id.

The dynamic ID parsing code needs to be updated for this; convert it to just write to the reserved entry for dynamic ID and remove the weird loop.

---
 drivers/net/usb/pegasus.c | 54 ++++++++++++++++-----------------------
 drivers/net/usb/pegasus.h |  3 ---
 2 files changed, 22 insertions(+), 35 deletions(-)
drivers/net/usb/pegasus.c +22 -32
index 475b066081c7f..8ec798f97f99b 100644
@@ -46,21 +46,12 @@ static bool loopback;
 static bool mii_mode;
 static char *devid;
 
-static struct usb_eth_dev usb_dev_id[] = {
-#define	PEGASUS_DEV(pn, vid, pid, flags)	\
-	{.name = pn, .vendor = vid, .device = pid, .private = flags},
-#define PEGASUS_DEV_CLASS(pn, vid, pid, dclass, flags) \
-	PEGASUS_DEV(pn, vid, pid, flags)
-#include "pegasus.h"
-#undef	PEGASUS_DEV
-#undef	PEGASUS_DEV_CLASS
-	{NULL, 0, 0, 0},
-	{NULL, 0, 0, 0}
-};
+static struct usb_eth_dev dynamic_id_info = {};
 
 static struct usb_device_id pegasus_ids[] = {
 #define	PEGASUS_DEV(pn, vid, pid, flags) \
-	{.match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = vid, .idProduct = pid},
+	{.match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = vid, .idProduct = pid, \
+	 .driver_info = (kernel_ulong_t)&(const struct usb_eth_dev) {.name = pn, .private = flags}},
 /*
  * The Belkin F8T012xx1 bluetooth adaptor has the same vendor and product
  * IDs as the Belkin F5D5050, so we need to teach the pegasus driver to
@@ -69,7 +60,8 @@ static struct usb_device_id pegasus_ids[] = {
  */
 #define PEGASUS_DEV_CLASS(pn, vid, pid, dclass, flags) \
 	{.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_CLASS), \
-	.idVendor = vid, .idProduct = pid, .bDeviceClass = dclass},
+	.idVendor = vid, .idProduct = pid, .bDeviceClass = dclass, \
+	.driver_info = (kernel_ulong_t)&(const struct usb_eth_dev) {.name = pn, .private = flags}},
 #include "pegasus.h"
 #undef	PEGASUS_DEV
 #undef	PEGASUS_DEV_CLASS
@@ -405,12 +397,12 @@ static inline int reset_mac(pegasus_t *pegasus)
 	if (i == REG_TIMEOUT)
 		return -ETIMEDOUT;
 
-	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
-	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
+	if (le16_to_cpu(pegasus->usb->descriptor.idVendor) == VENDOR_LINKSYS ||
+	    le16_to_cpu(pegasus->usb->descriptor.idVendor) == VENDOR_DLINK) {
 		set_register(pegasus, Gpio0, 0x24);
 		set_register(pegasus, Gpio0, 0x26);
 	}
-	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) {
+	if (le16_to_cpu(pegasus->usb->descriptor.idVendor) == VENDOR_ELCON) {
 		__u16 auxmode;
 		ret = read_mii_word(pegasus, 3, 0x1b, &auxmode);
 		if (ret < 0)
@@ -448,9 +440,9 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)
 	memcpy(pegasus->eth_regs, data, sizeof(data));
 	ret = set_registers(pegasus, EthCtrl0, 3, data);
 
-	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
-	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS2 ||
-	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
+	if (le16_to_cpu(pegasus->usb->descriptor.idVendor) == VENDOR_LINKSYS ||
+	    le16_to_cpu(pegasus->usb->descriptor.idVendor) == VENDOR_LINKSYS2 ||
+	    le16_to_cpu(pegasus->usb->descriptor.idVendor) == VENDOR_DLINK) {
 		u16 auxmode;
 		ret = read_mii_word(pegasus, 0, 0x1b, &auxmode);
 		if (ret < 0)
@@ -1156,7 +1148,7 @@ static int pegasus_probe(struct usb_interface *intf,
 	struct usb_device *dev = interface_to_usbdev(intf);
 	struct net_device *net;
 	pegasus_t *pegasus;
-	int dev_index = id - pegasus_ids;
+	const struct usb_eth_dev *info = (const struct usb_eth_dev *)id->driver_info;
 	int res = -ENOMEM;
 	static const u8 bulk_ep_addr[] = {
 		PEGASUS_USB_EP_BULK_IN | USB_DIR_IN,
@@ -1181,7 +1173,6 @@ static int pegasus_probe(struct usb_interface *intf,
 		goto out;
 
 	pegasus = netdev_priv(net);
-	pegasus->dev_index = dev_index;
 	pegasus->intf = intf;
 
 	res = alloc_urbs(pegasus);
@@ -1209,7 +1200,7 @@ static int pegasus_probe(struct usb_interface *intf,
 	pegasus->msg_enable = netif_msg_init(msg_level, NETIF_MSG_DRV
 				| NETIF_MSG_PROBE | NETIF_MSG_LINK);
 
-	pegasus->features = usb_dev_id[dev_index].private;
+	pegasus->features = info ? info->private : DEFAULT_GPIO_RESET;
 	res = get_interrupt_interval(pegasus);
 	if (res)
 		goto out2;
@@ -1238,7 +1229,7 @@ static int pegasus_probe(struct usb_interface *intf,
 	queue_delayed_work(system_long_wq, &pegasus->carrier_check,
 			   CARRIER_CHECK_DELAY);
 	dev_info(&intf->dev, "%s, %s, %pM\n", net->name,
-		 usb_dev_id[dev_index].name, net->dev_addr);
+		 info ? info->name : "(unknown)", net->dev_addr);
 	return 0;
 
 out3:
@@ -1328,8 +1319,9 @@ static struct usb_driver pegasus_driver = {
 
 static void __init parse_id(char *id)
 {
-	unsigned int vendor_id = 0, device_id = 0, flags = 0, i = 0;
+	unsigned int vendor_id = 0, device_id = 0, flags = 0;
 	char *token, *name = NULL;
+	int dyn_id_index = ARRAY_SIZE(pegasus_ids) - 2;
 
 	if ((token = strsep(&id, ":")) != NULL)
 		name = token;
@@ -1347,14 +1339,12 @@ static void __init parse_id(char *id)
 	if (device_id > 0x10000 || device_id == 0)
 		return;
 
-	for (i = 0; usb_dev_id[i].name; i++);
-	usb_dev_id[i].name = name;
-	usb_dev_id[i].vendor = vendor_id;
-	usb_dev_id[i].device = device_id;
-	usb_dev_id[i].private = flags;
-	pegasus_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
-	pegasus_ids[i].idVendor = vendor_id;
-	pegasus_ids[i].idProduct = device_id;
+	dynamic_id_info.name = name;
+	dynamic_id_info.private = flags;
+	pegasus_ids[dyn_id_index].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+	pegasus_ids[dyn_id_index].idVendor = vendor_id;
+	pegasus_ids[dyn_id_index].idProduct = device_id;
+	pegasus_ids[dyn_id_index].driver_info = (kernel_ulong_t)&dynamic_id_info;
 }
 
 static int __init pegasus_init(void)
drivers/net/usb/pegasus.h +0 -3
index a05b143155ba8..ccdedcef52e76 100644
@@ -85,7 +85,6 @@ typedef struct pegasus {
 	unsigned		features;
 	u32			msg_enable;
 	u32			wolopts;
-	int			dev_index;
 	int			intr_interval;
 	struct tasklet_struct	rx_tl;
 	struct delayed_work	carrier_check;
@@ -102,8 +101,6 @@ typedef struct pegasus {
 
 struct usb_eth_dev {
 	char	*name;
-	__u16	vendor;
-	__u16	device;
 	__u32	private; /* LSB is gpio reset value */
 };
 
Signature · 1 line
2.53.0