6.6.157-rc1 review
Subsystems (5)
Greg Kroah-Hartman
maintainerPATCH 108/1424 fpga: stratix10-soc: Fix SVC mailbox handling during reconfiguration
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tien Sung Ang <[email protected]>
commit c14a8b15c87b49efc3ef898cec8ac7c30336a080 upstream.
Fix incorrect stratix10_svc_done() usage during FPGA reconfiguration.
Do not call stratix10_svc_done() at the end of write_init() on success, so the SVC session remains active through write() and write_complete(). Call stratix10_svc_done() on failure in write_init() and write() so the shared SVC mailbox is released when reconfiguration aborts, allowing coexistence with other SVC clients such as soc64-hwmon.
--- drivers/fpga/stratix10-soc.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-)
drivers/fpga/stratix10-soc.c +12 -9
@@ -195,20 +195,18 @@ static int s10_ops_write_init(struct fpg ret =s10_svc_send_msg (priv,COMMAND_RECONFIG , &ctype,sizeof (ctype));if (ret <0 ) - goto init_done; + goto init_error; - ret =wait_for_completion_timeout ( - &priv->status_return_completion,S10_RECONFIG_TIMEOUT ); -if (!ret) { +if (!wait_for_completion_timeout (&priv->status_return_completion, +S10_RECONFIG_TIMEOUT )) {dev_err (dev,"timeout waiting for RECONFIG_REQUEST\n" ); ret = -ETIMEDOUT ; - goto init_done; + goto init_error; } - ret =0 ;if (!test_and_clear_bit (SVC_STATUS_OK , &priv->status)) { ret = -ETIMEDOUT ; - goto init_done; + goto init_error; }/* Allocate buffers from the service layer's pool. */
@@ -217,14 +215,16 @@ static int s10_ops_write_init(struct fpgif (IS_ERR (kbuf)) {s10_free_buffers (mgr); ret =PTR_ERR (kbuf); - goto init_done; + goto init_error; } priv->svc_bufs[i].buf = kbuf; priv->svc_bufs[i].lock =0 ; } -init_done: +return 0 ; + +init_error:stratix10_svc_done (priv->chan);return ret; }
@@ -342,6 +342,9 @@ static int s10_ops_write(struct fpga_manif (!s10_free_buffers (mgr))dev_err (dev,"%s not all buffers were freed\n" , __func__); +if (ret <0 ) +stratix10_svc_done (priv->chan); +return ret; }
Greg Kroah-Hartman
maintainerPATCH 109/1424 hsi: omap_ssi_core: fix missing DMA mask setup for SSI controller device
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivaylo Dimitrov <[email protected]>
commit e81250ec6b69248b00d38c523dc6a13efaf38aab upstream.
The OMAP SSI driver uses a synthetic HSI controller device allocated via hsi_alloc_controller(), which does not go through the normal OF/platform device initialization path.
As a result, the embedded struct device does not have a DMA mask initialized by default.
After recent DMA API hardening changes, dma_map_sg() and related helpers now require a valid dma_mask to be present, otherwise the driver may crash or trigger warnings when attempting DMA mapping operations.
Fix this by explicitly initializing the DMA mask for the SSI controller device and setting a 32-bit DMA mask, which matches the hardware capabilities.
--- drivers/hsi/controllers/omap_ssi_core.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/hsi/controllers/omap_ssi_core.c +6 -0
@@ -511,6 +511,12 @@ static int ssi_probe(struct platform_devpm_runtime_enable (&pd->dev); + ssi->device.dma_mask = &ssi->device.coherent_dma_mask; + + err =dma_set_mask_and_coherent (&ssi->device,DMA_BIT_MASK (32 )); +if (err) + goto out2; + err =ssi_hw_init (ssi);if (err <0 ) goto out2;
Greg Kroah-Hartman
maintainerPATCH 110/1424 ACPI: pfr_update: fix stack buffer overflow in query_capability()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anirudh Prasad <[email protected]>
commit ced45be0073a8a31b30b4a7f68cd3a15734515de upstream.
query_capability() copies four ACPI buffer objects returned by the firmware _DSM into fixed-size u8[16] fields in struct pfru_update_cap_info using memcpy with the firmware-supplied length:
memcpy(&cap_hdr->code_type,
elements[CAP_CODE_TYPE_IDX].buffer.pointer,
elements[CAP_CODE_TYPE_IDX].buffer.length);The same pattern repeats for drv_type, platform_id, and oem_id. If the firmware returns buffer.length > 16 for any of these fields, memcpy writes past the destination array.
struct pfru_update_cap_info is stack-allocated in pfru_ioctl().
Confirmed with KASAN on 7.2-rc6: three stack-out-of-bounds reports are generated when a DSM returns 64-byte buffers, with writes reaching 44 bytes past the end of cap_hdr's [64, 156) frame window into adjacent stack redzones.
Introduce a helper pointer to out_obj->package.elements and use it to validate each buffer length against its destination field size before copying, returning -EINVAL if the firmware supplies an oversized buffer.
--- drivers/acpi/pfr_update.c | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-)
drivers/acpi/pfr_update.c +24 -21
@@ -120,7 +120,7 @@ static int query_capability(struct pfru_ struct pfru_device *pfru_dev) {acpi_handle handle =ACPI_HANDLE (pfru_dev->parent_dev); -union acpi_object *out_obj; +union acpi_object *out_obj, *elem;int ret = -EINVAL ; out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid,
@@ -144,36 +144,39 @@ static int query_capability(struct pfru_ out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER) goto free_acpi_buffer; - cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX ].integer.value; + elem = out_obj->package.elements; + + cap_hdr->status = elem[CAP_STATUS_IDX ].integer.value;if (cap_hdr->status !=DSM_SUCCEED ) { ret = -EBUSY ;dev_dbg (pfru_dev->parent_dev,"Error Status:%d\n" , cap_hdr->status); goto free_acpi_buffer; } - cap_hdr->update_cap = out_obj->package.elements[CAP_UPDATE_IDX ].integer.value; +if (elem[CAP_CODE_TYPE_IDX ].buffer.length >sizeof (cap_hdr->code_type) || + elem[CAP_DRV_TYPE_IDX ].buffer.length >sizeof (cap_hdr->drv_type) || + elem[CAP_PLAT_ID_IDX ].buffer.length >sizeof (cap_hdr->platform_id) || + elem[CAP_OEM_ID_IDX ].buffer.length >sizeof (cap_hdr->oem_id)) + goto free_acpi_buffer; + + cap_hdr->update_cap = elem[CAP_UPDATE_IDX ].integer.value;memcpy (&cap_hdr->code_type, - out_obj->package.elements[CAP_CODE_TYPE_IDX ].buffer.pointer, - out_obj->package.elements[CAP_CODE_TYPE_IDX ].buffer.length); - cap_hdr->fw_version = - out_obj->package.elements[CAP_FW_VER_IDX ].integer.value; - cap_hdr->code_rt_version = - out_obj->package.elements[CAP_CODE_RT_VER_IDX ].integer.value; + elem[CAP_CODE_TYPE_IDX ].buffer.pointer, + elem[CAP_CODE_TYPE_IDX ].buffer.length); + cap_hdr->fw_version = elem[CAP_FW_VER_IDX ].integer.value; + cap_hdr->code_rt_version = elem[CAP_CODE_RT_VER_IDX ].integer.value;memcpy (&cap_hdr->drv_type, - out_obj->package.elements[CAP_DRV_TYPE_IDX ].buffer.pointer, - out_obj->package.elements[CAP_DRV_TYPE_IDX ].buffer.length); - cap_hdr->drv_rt_version = - out_obj->package.elements[CAP_DRV_RT_VER_IDX ].integer.value; - cap_hdr->drv_svn = - out_obj->package.elements[CAP_DRV_SVN_IDX ].integer.value; + elem[CAP_DRV_TYPE_IDX ].buffer.pointer, + elem[CAP_DRV_TYPE_IDX ].buffer.length); + cap_hdr->drv_rt_version = elem[CAP_DRV_RT_VER_IDX ].integer.value; + cap_hdr->drv_svn = elem[CAP_DRV_SVN_IDX ].integer.value;memcpy (&cap_hdr->platform_id, - out_obj->package.elements[CAP_PLAT_ID_IDX ].buffer.pointer, - out_obj->package.elements[CAP_PLAT_ID_IDX ].buffer.length); + elem[CAP_PLAT_ID_IDX ].buffer.pointer, + elem[CAP_PLAT_ID_IDX ].buffer.length);memcpy (&cap_hdr->oem_id, - out_obj->package.elements[CAP_OEM_ID_IDX ].buffer.pointer, - out_obj->package.elements[CAP_OEM_ID_IDX ].buffer.length); - cap_hdr->oem_info_len = - out_obj->package.elements[CAP_OEM_INFO_IDX ].buffer.length; + elem[CAP_OEM_ID_IDX ].buffer.pointer, + elem[CAP_OEM_ID_IDX ].buffer.length); + cap_hdr->oem_info_len = elem[CAP_OEM_INFO_IDX ].buffer.length; ret =0 ;
Greg Kroah-Hartman
maintainerPATCH 111/1424 alpha/PCI: Fix I/O port accessor argument order in pci_legacy_write()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit 651fb94aaf245430590216d497fb8b02dd73d5f9 upstream.
pci_legacy_write() in arch/alpha/kernel/pci-sysfs.c passes its arguments to outb(), outw() and outl() in the wrong order:
outb(port, val);
The Alpha I/O accessors in arch/alpha/include/asm/io.h take the value first and the port second:
extern void outb(u8 b, unsigned long port);
So the port number is written as data to the I/O address taken from the user-supplied value, and the intended write to the requested port never happens.
The arguments have been reversed since the file was added, and the function returns the access size regardless, so the caller sees success while the requested port is left untouched.
--- arch/alpha/kernel/pci-sysfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/alpha/kernel/pci-sysfs.c +3 -3
@@ -364,17 +364,17 @@ int pci_legacy_write(struct pci_bus *busswitch (size) {case 1 : -outb (port, val); +outb (val, port);return 1 ;case 2 :if (port &1 )return -EINVAL ; -outw (port, val); +outw (val, port);return 2 ;case 4 :if (port &3 )return -EINVAL ; -outl (port, val); +outl (val, port);return 4 ; }return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 112/1424 alpha: marvel: Fix irq_set_status_flags to use correct IRQ number
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matt Turner <[email protected]>
commit 3a3ac1f6c6a67b3803f2643584310f78301e58a8 upstream.
Pass base + i to irq_set_status_flags() to match the IRQ number used in irq_set_chip_and_handler(). Previously, IRQ_LEVEL was set on the wrong (low-numbered) IRQ descriptors rather than the IO7 IRQs at base + i.
--- arch/alpha/kernel/sys_marvel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/alpha/kernel/sys_marvel.c +2 -2
@@ -275,7 +275,7 @@ init_io7_irqs(struct io7 *io7,/* Set up the lsi irqs. */ for (i =0 ; i <128 ; ++i) {irq_set_chip_and_handler (base + i, lsi_ops, handle_level_irq); -irq_set_status_flags (i,IRQ_LEVEL ); +irq_set_status_flags (base + i,IRQ_LEVEL ); }/* Disable the implemented irqs in hardware. */
@@ -289,7 +289,7 @@ init_io7_irqs(struct io7 *io7,/* Set up the msi irqs. */ for (i =128 ; i < (128 +512 ); ++i) {irq_set_chip_and_handler (base + i, msi_ops, handle_level_irq); -irq_set_status_flags (i,IRQ_LEVEL ); +irq_set_status_flags (base + i,IRQ_LEVEL ); }for (i =0 ; i <16 ; ++i)
Greg Kroah-Hartman
maintainerPATCH 113/1424 alpha: marvel: Fix lock ordering in init_io7_irqs()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matt Turner <[email protected]>
commit 24d68db713d63dfe3660c56b50e887784844baea upstream.
Move irq_set_chip_and_handler() and irq_set_status_flags() calls outside the io7->irq_lock raw spinlock. These functions take sparse_irq_lock, which is a mutex, and taking a sleeping lock while holding a raw spinlock is invalid. The raw spinlock only needs to protect the hardware CSR accesses.
This fixes the following lockdep splat during boot:
[ BUG: Invalid wait context ]
swapper/0/0 is trying to lock:
sparse_irq_lock{....}-{4:4}, at: irq_mark_irq
other info that might help us debug this:
context-{5:5}
1 lock held by swapper/0/0:
#0: &io7->irq_lock{....}-{2:2}, at: init_io7_irqs.constprop.0
--- arch/alpha/kernel/sys_marvel.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-)
arch/alpha/kernel/sys_marvel.c +12 -13
@@ -263,6 +263,18 @@ init_io7_irqs(struct io7 *io7,*/ printk (" Interrupts reported to CPU at PE %u\n" , boot_cpuid); +/* Set up the lsi irqs. */ +for (i =0 ; i <128 ; ++i) { +irq_set_chip_and_handler (base + i, lsi_ops, handle_level_irq); +irq_set_status_flags (base + i,IRQ_LEVEL ); + } + +/* Set up the msi irqs. */ +for (i =128 ; i < (128 +512 ); ++i) { +irq_set_chip_and_handler (base + i, msi_ops, handle_level_irq); +irq_set_status_flags (base + i,IRQ_LEVEL ); + } +raw_spin_lock (&io7->irq_lock);/* set up the error irqs */
@@ -272,12 +284,6 @@ init_io7_irqs(struct io7 *io7,io7_redirect_irq (io7, &io7->csrs->STV_CTL.csr, boot_cpuid);io7_redirect_irq (io7, &io7->csrs->HEI_CTL.csr, boot_cpuid); -/* Set up the lsi irqs. */ -for (i =0 ; i <128 ; ++i) { -irq_set_chip_and_handler (base + i, lsi_ops, handle_level_irq); -irq_set_status_flags (base + i,IRQ_LEVEL ); - } -/* Disable the implemented irqs in hardware. */ for (i =0 ; i <0x60 ; ++i)init_one_io7_lsi (io7, i, boot_cpuid);
@@ -285,13 +291,6 @@ init_io7_irqs(struct io7 *io7,init_one_io7_lsi (io7,0x74 , boot_cpuid);init_one_io7_lsi (io7,0x75 , boot_cpuid); - -/* Set up the msi irqs. */ -for (i =128 ; i < (128 +512 ); ++i) { -irq_set_chip_and_handler (base + i, msi_ops, handle_level_irq); -irq_set_status_flags (base + i,IRQ_LEVEL ); - } -for (i =0 ; i <16 ; ++i)init_one_io7_msi (io7, i, boot_cpuid);
Greg Kroah-Hartman
maintainerPATCH 114/1424 ARM: 9477/1: Disable broken eBPF JIT on the Risc PC
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ethan Nelson-Moore <[email protected]>
commit 7e8ee82e69fde9d589272ec5e6f702358903be1f upstream.
The eBPF JIT unconditionally generates ldrh/strh instructions, which do not function correctly on the Risc PC because its bus is unable to signal half-word accesses. Work around this issue by disabling the eBPF JIT when building for ARMv3 (the Risc PC is the only currently supported machine whose kernel is built for ARMv3).
Comments from Ethan Nelson-Moore:
From LKML: https://lore.kernel.org/all/CAD++jL=0qYGoygUwGEXQL7C_ROnC7kfpRv8RA+H5tNWwYu+pQA@mail.gmail.com/
The commit message has been updated slightly relative to the version on LKML to clarify that the Risc PC is not actually ARMv3.
--- arch/arm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm/Kconfig +1 -1
@@ -87,7 +87,7 @@ config ARM select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE if ARM_LPAE select HAVE_ARM_SMCCC if CPU_V7 - select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 + select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 && !CPU_32v3 select HAVE_CONTEXT_TRACKING_USER select HAVE_C_RECORDMCOUNT select HAVE_BUILDTIME_MCOUNT_SORT
Greg Kroah-Hartman
maintainerPATCH 115/1424 ata: libata-scsi: fix DSM TRIM for sector sizes larger than 2048 bytes
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Niklas Cassel <[email protected]>
commit 79cce911e623c0baa0fde307ce3a434e084b881a upstream.
ata_scsi_write_same_xlat() translates a SCSI WRITE SAME command with the UNMAP bit set into an ATA DATA SET MANAGEMENT TRIM command. The TRIM descriptor is built by ata_format_dsm_trim_descr() into the 2048-byte ata_scsi_rbuf staging buffer, and the number of bytes copied is compared against the logical sector size by the caller:
size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block); if (size != len) /* len == sdp->sector_size */ goto invalid_param_len;
ata_format_dsm_trim_descr() clamps the copy length to ATA_SCSI_RBUF_SIZE (2048). On a device whose logical sector size exceeds that (e.g. a 4Kn device, where sector_size == 4096) the function can never return more than 2048, while the caller expects it to return sector_size. The comparison therefore always fails, so every TRIM is rejected with "Parameter list length error" and WARN_ON() splats on each attempt. TRIM / discard is thus completely broken on such devices.
The descriptor was incorrectly sized from the logical sector size. A DSM TRIM payload is a list of 512-byte pages, each holding up to ATA_MAX_TRIM_RNUM (64) LBA Range Entries, and is independent of the logical sector size. The Block Limits VPD page already advertises a single such page as the maximum WRITE SAME length (65535 * ATA_MAX_TRIM_RNUM logical blocks), so the block layer never sends a request that needs more than one page.
Emit exactly one 512-byte page, independent of the logical sector size, and transfer only that page (COUNT == 1). For a 512-byte-sector device this is unchanged; devices with larger logical sectors now work instead of failing every TRIM.
--- drivers/ata/libata-scsi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
drivers/ata/libata-scsi.c +14 -14
@@ -3222,17 +3222,13 @@ static unsigned int ata_scsi_pass_thru(sstatic size_t ata_format_dsm_trim_descr (struct scsi_cmnd *cmd,u32 trmax,u64 sector,u32 count) { -struct scsi_device *sdp = cmd->device; -size_t len = sdp->sector_size; +size_t len =ATA_SECT_SIZE ;size_t r;__le64 *buf;u32 i =0 ;unsigned long flags; -WARN_ON (len >ATA_SCSI_RBUF_SIZE ); - -if (len >ATA_SCSI_RBUF_SIZE ) - len =ATA_SCSI_RBUF_SIZE ; +BUILD_BUG_ON (ATA_SECT_SIZE >ATA_SCSI_RBUF_SIZE );spin_lock_irqsave (&ata_scsi_rbuf_lock, flags); buf = ((void *)ata_scsi_rbuf);
@@ -3267,13 +3263,11 @@ static unsigned int ata_scsi_write_same_ {struct ata_taskfile *tf = &qc->tf;struct scsi_cmnd *scmd = qc->scsicmd; -struct scsi_device *sdp = scmd->device; -size_t len = sdp->sector_size;struct ata_device *dev = qc->dev;const u8 *cdb = scmd->cmnd;u64 block;u32 n_block; -const u32 trmax = len >>3 ; +const u32 trmax =ATA_MAX_TRIM_RNUM ;u32 size;u16 fp;u8 bp =0xff ;
@@ -3318,13 +3312,13 @@ static unsigned int ata_scsi_write_same_ goto invalid_param_len;/* -* size must match sector size in bytes -* For DATA SET MANAGEMENT TRIM in ACS-2 nsect (aka count) -* is defined as number of 512 byte blocks to be transferred. +* The TRIM descriptor is a single 512-byte page, which is the maximum +* WRITE SAME length advertised in the Block Limits VPD page. For DATA +* SET MANAGEMENT TRIM the COUNT field (aka nsect) is the number of +* 512-byte blocks to be transferred. */ - size =ata_format_dsm_trim_descr (scmd, trmax, block, n_block); -if (size != len) +if (size !=ATA_SECT_SIZE ) goto invalid_param_len;if (ata_ncq_enabled (dev) &&ata_fpdma_dsm_supported (dev)) {
@@ -3350,6 +3344,12 @@ static unsigned int ata_scsi_write_same_ATA_TFLAG_WRITE ;ata_qc_set_pc_nbytes (qc); +/* +* The DSM TRIM payload is a single 512-byte page, which may be smaller +* than the WRITE SAME data-out buffer (one logical block); only +* transfer that page so the length matches the COUNT field. +*/ + qc->nbytes = size;return 0 ;
Greg Kroah-Hartman
maintainerPATCH 116/1424 auxdisplay: charlcd: cancel backlight work on registration failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongyan Xu <[email protected]>
commit e3e3bf40916c1e810df03958cfa7ba6883cdce79 upstream.
With CONFIG_CHARLCD_BL_FLASH, charlcd_init() schedules bl_work before charlcd_register() calls misc_register(). If registration fails, the caller frees the charlcd object while delayed work still contains its address.
Add charlcd_deinit() to cancel the delayed work and turn the backlight off. Use it for both registration rollback and normal unregistration.
--- drivers/auxdisplay/charlcd.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-)
drivers/auxdisplay/charlcd.c +14 -7
@@ -594,6 +594,16 @@ static int charlcd_init(struct charlcd *return 0 ; } +static void charlcd_deinit (struct charlcd *lcd) +{ +struct charlcd_priv *priv =charlcd_to_priv (lcd); + +if (lcd->ops->backlight) { +cancel_delayed_work_sync (&priv->bl_work); + lcd->ops->backlight (lcd,CHARLCD_OFF ); + } +} +struct charlcd *charlcd_alloc (unsigned int drvdata_size) {struct charlcd_priv *priv;
@@ -653,8 +663,10 @@ int charlcd_register(struct charlcd *lcdreturn ret; ret =misc_register (&charlcd_dev); -if (ret) +if (ret) { +charlcd_deinit (lcd);return ret; + } the_charlcd = lcd;register_reboot_notifier (&panel_notifier);
@@ -664,16 +676,11 @@ EXPORT_SYMBOL_GPL(charlcd_register);int charlcd_unregister (struct charlcd *lcd) { -struct charlcd_priv *priv =charlcd_to_priv (lcd); -unregister_reboot_notifier (&panel_notifier);charlcd_puts (lcd,"\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-" );misc_deregister (&charlcd_dev); the_charlcd =NULL ; -if (lcd->ops->backlight) { -cancel_delayed_work_sync (&priv->bl_work); - priv->lcd.ops->backlight (&priv->lcd,CHARLCD_OFF ); - } +charlcd_deinit (lcd);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 117/1424 block: set QUEUE_FLAG_DYING unconditionally in blk_mark_disk_dead()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: 胡连勤 <[email protected]>
commit 7e9a46004b471eaf69b082c473d865316a4158e0 upstream.
Disks created via blk_mq_alloc_disk_for_queue() (e.g. SCSI SD disks) do not have GD_OWNS_QUEUE set. Currently __blk_mark_disk_dead() only sets QUEUE_FLAG_DYING when GD_OWNS_QUEUE is set, so for such disks blk_queue_enter() and __bio_queue_enter() cannot detect the dying state via blk_queue_dying() and remain blocked waiting for I/O that will never complete after surprise removal.
blk_mark_disk_dead() is the explicit "surprise removal" API -- the caller has already decided the disk is dead. Setting QUEUE_FLAG_DYING unconditionally here is appropriate: any in-flight I/O from other threads should get -ENODEV immediately from blk_queue_enter() regardless of GD_OWNS_QUEUE ownership.
For disks that already have GD_OWNS_QUEUE set, __blk_mark_disk_dead() will set the flag again which is harmless.
--- block/genhd.c | 1 + 1 file changed, 1 insertion(+)
block/genhd.c +1 -0
@@ -604,6 +604,7 @@ static void __blk_mark_disk_dead(struct*/ void blk_mark_disk_dead (struct gendisk *disk) { +blk_queue_flag_set (QUEUE_FLAG_DYING , disk->queue);__blk_mark_disk_dead (disk);blk_report_disk_dead (disk, true); }
Greg Kroah-Hartman
maintainerPATCH 118/1424 Bluetooth: btusb: Add ASUS USB-BT540 for Realtek 8761CU
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christoph Zwerschke <[email protected]>
commit 980084de4d9b25193398d89a1c0430ba3501b683 upstream.
Add the vendor/product ID (0x0b05, 0x1bef) to the usb_device_id table for the Realtek RTL8761CU-based ASUS USB-BT540 adapter. It binds via the generic Bluetooth class today, so BTUSB_REALTEK is never set and the rtl8761cu firmware is not loaded, leaving the controller non-functional. With the entry the driver loads rtl_bt/rtl8761cu_fw.bin (already shipped by linux-firmware) and the adapter works (tested: A2DP and ASHA).
Similar to commit bc597f0cc44f
("Bluetooth: btusb: Add TP-Link UB600 for Realtek 8761BUV").
Device info from /sys/kernel/debug/usb/devices:
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 22 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0b05 ProdID=1bef Rev= 2.00 S: Manufacturer=Realtek S: Product=Bluetooth Controller C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms
--- drivers/bluetooth/btusb.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/bluetooth/btusb.c +4 -0
@@ -747,6 +747,10 @@ static const struct usb_device_id quirks { USB_DEVICE(0x2b89, 0x6275), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + /* Additional Realtek 8761CU Bluetooth devices */ + { USB_DEVICE(0x0b05, 0x1bef), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
Greg Kroah-Hartman
maintainerPATCH 119/1424 Bluetooth: btusb: Add ASUS USB-BT600 for Realtek 8761CU
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christoph Zwerschke <[email protected]>
commit 6f0624b4427e38c3bb63a951c536cf8adaee1238 upstream.
Add the vendor/product ID (0x0b05, 0x1d70) to the usb_device_id table for the Realtek RTL8761CU-based ASUS USB-BT600 adapter. It binds via the generic Bluetooth class today, so BTUSB_REALTEK is never set and the rtl8761cu firmware is not loaded, leaving the controller non-functional. With the entry the driver loads rtl_bt/rtl8761cu_fw.bin (already shipped by linux-firmware) and the adapter works (tested: A2DP and ASHA).
Similar to commit bc597f0cc44f
("Bluetooth: btusb: Add TP-Link UB600 for Realtek 8761BUV").
Device info from /sys/kernel/debug/usb/devices:
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 23 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0b05 ProdID=1d70 Rev= 2.00 S: Manufacturer=Realtek S: Product=Bluetooth Controller C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms
--- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/bluetooth/btusb.c +2 -0
@@ -750,6 +750,8 @@ static const struct usb_device_id quirks/* Additional Realtek 8761CU Bluetooth devices */ { USB_DEVICE(0x0b05, 0x1bef), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0b05, 0x1d70), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
Greg Kroah-Hartman
maintainerPATCH 120/1424 Bluetooth: eir: Fix OOB read in eir_get_service_data()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit 4beb198bc59b242404a47c21990bc84165052c8a upstream.
eir_get_service_data() walks the advertising data for a Service Data field with a matching UUID. On a mismatch it advances:
eir += dlen;
eir_len -= dlen;eir_get_data() reports dlen as the field's data length, but the field spans dlen + 2 bytes once its length and type bytes count, and more when non-Service-Data fields were skipped to reach it. The pointer lands correctly on the next field. eir_len does not, and the shortfall compounds across fields until eir_get_data() reads the length and type bytes of a "field" past the end of the buffer.
For an ISO broadcast sink that buffer is hcon->le_per_adv_data[], filled from the periodic advertising reports of a remote broadcaster. A PA payload packed with mismatching Service Data fields walks off the array into the rest of struct hci_conn. A drifted field that matches the BAA UUID puts those bytes in iso_pi(sk)->base, where user space reads them back with getsockopt(BT_ISO_BASE).
Recompute eir_len from the end of the buffer each iteration.
--- net/bluetooth/eir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/bluetooth/eir.c +2 -1
@@ -366,6 +366,7 @@ u8 eir_create_scan_rsp(struct hci_dev *hvoid *eir_get_service_data (u8 *eir,size_t eir_len,u16 uuid,size_t *len) { +const u8 *eir_end = eir + eir_len;size_t dlen;while ((eir =eir_get_data (eir, eir_len,EIR_SERVICE_DATA , &dlen))) {
@@ -378,7 +379,7 @@ void *eir_get_service_data(u8 *eir, size } eir += dlen; - eir_len -= dlen; + eir_len = eir_end - eir; }return NULL ;
Greg Kroah-Hartman
maintainerPATCH 121/1424 bnx2x: fix double free in bnx2x_init_firmware() error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
commit d2796ffe38cb4155afe0eab23636295b096c27a5 upstream.
bnx2x_init_firmware() frees bp->init_ops, bp->init_data and bp->init_ops_offsets in its error path without setting them to NULL. The cleanup function bnx2x_release_firmware() frees the same three pointers unconditionally, so if init_firmware fails and release_firmware is later called (e.g. from __bnx2x_remove or through the function state machine), all three are freed a second time.
Set each pointer to NULL after kfree() in the error path so that the subsequent kfree(NULL) in bnx2x_release_firmware() is a safe no-op.
--- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +3 -0
@@ -13475,10 +13475,13 @@ static int bnx2x_init_firmware(struct bn iro_alloc_err:kfree (bp->init_ops_offsets); + bp->init_ops_offsets =NULL ; init_offsets_alloc_err:kfree (bp->init_ops); + bp->init_ops =NULL ; init_ops_alloc_err:kfree (bp->init_data); + bp->init_data =NULL ; request_firmware_exit:release_firmware (bp->firmware); bp->firmware =NULL ;
Greg Kroah-Hartman
maintainerPATCH 122/1424 bpf: Harden bloom filter sizing and indexing on 32-bit kernels
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jérémy Jean <[email protected]>
commit 11c1e836710dcba03e50454a4eedfdbaf8d3050e upstream.
bloom_map_alloc() has two 32-bit-specific problems when the computed bitmap reaches the U32_MAX fallback case.
First, BITS_TO_BYTES(U32_MAX) is evaluated with 32-bit arithmetic. The addition performed by DIV_ROUND_UP wraps, so the map allocates only the fixed-size bloom filter object while keeping bitset_mask == U32_MAX. Subsequent updates can then write past the allocated object.
Second, fixing only the allocation size is not sufficient. The bloom hash is a u32, but set_bit() takes a signed long bit number and x86 test_bit() eventually feeds the index to variable_test_bit(long, ...). On 32-bit kernels, hashes in [0x80000000, U32_MAX] therefore become negative bit offsets. x86 bt/bts with a memory operand interpret those offsets relative to the supplied base, so a map with bitset_mask == U32_MAX can read or write before bloom->bitset even after allocating the full 512 MiB bitmap.
Keep the U32_MAX fallback, but split each hash into a word pointer and an in-word bit number before calling test_bit() or set_bit(). The bitops argument is then always in [0, BITS_PER_LONG - 1], while BIT_WORD(h) still selects the intended word in the full bitmap.
Compute the bitset size from (u64)bitset_mask + 1 before passing the final size to bpf_map_area_alloc(). This fixes the original under-allocation and keeps the allocated storage consistent with the addressable bitset.
Exploitation note: local privilege escalation is possible on a 32-bit x86 kernel using the under-allocation bug from a binary with CAP_BPF.
--- kernel/bpf/bloom_filter.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-)
kernel/bpf/bloom_filter.c +9 -10
@@ -41,7 +41,7 @@ static long bloom_map_peek_elem(struct bfor (i =0 ; i < bloom->nr_hash_funcs; i++) { h =hash (bloom, value, map->value_size, i); -if (!test_bit (h, bloom->bitset)) +if (!test_bit (h %BITS_PER_LONG , bloom->bitset +BIT_WORD (h)))return -ENOENT ; }
@@ -57,9 +57,13 @@ static long bloom_map_push_elem(struct bif (flags !=BPF_ANY )return -EINVAL ; +/* +* On 32-bit architectures, hashes larger than INT_MAX would be +* treated as negative by set_bit(). +*/ for (i =0 ; i < bloom->nr_hash_funcs; i++) { h =hash (bloom, value, map->value_size, i); -set_bit (h, bloom->bitset); +set_bit (h %BITS_PER_LONG , bloom->bitset +BIT_WORD (h)); }return 0 ;
@@ -94,9 +98,10 @@ static int bloom_map_alloc_check(union bstatic struct bpf_map *bloom_map_alloc (union bpf_attr *attr) { -u32 bitset_bytes, bitset_mask, nr_hash_funcs, nr_bits; +u32 bitset_mask, nr_hash_funcs, nr_bits;int numa_node =bpf_map_attr_numa_node (attr);struct bpf_bloom_filter *bloom; +u64 bitset_bytes; if (attr->key_size != 0 || attr->value_size == 0 || attr->max_entries == 0 ||
@@ -127,22 +132,16 @@ static struct bpf_map *bloom_map_alloc(uif (check_mul_overflow (attr->max_entries, nr_hash_funcs, &nr_bits) ||check_mul_overflow (nr_bits /5 , (u32 )7 , &nr_bits) || nr_bits > (1UL <<31 )) { -/* The bit array size is 2^32 bits but to avoid overflowing the -* u32, we use U32_MAX, which will round up to the equivalent -* number of bytes -*/ - bitset_bytes =BITS_TO_BYTES (U32_MAX ); bitset_mask =U32_MAX ; }else {if (nr_bits <=BITS_PER_LONG ) nr_bits =BITS_PER_LONG ;else nr_bits =roundup_pow_of_two (nr_bits); - bitset_bytes =BITS_TO_BYTES (nr_bits); bitset_mask = nr_bits -1 ; } - bitset_bytes =roundup (bitset_bytes,sizeof (unsigned long )); + bitset_bytes =BITS_TO_LONGS ((u64 )bitset_mask +1 ) *sizeof (unsigned long ); bloom =bpf_map_area_alloc (sizeof (*bloom) + bitset_bytes, numa_node);if (!bloom)
Greg Kroah-Hartman
maintainerPATCH 123/1424 dm-era: fix shadowed superblock leak on take-snap failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: liyouhong <[email protected]>
commit 39c5aa3bd8ec3912d2cd0b3fe092642b0d2b0713 upstream.
metadata_take_snap() bumps the live superblock refcount and then dm_tm_shadow_block() allocates a new block for the metadata snapshot. If the subsequent dm_sm_inc_block() of writeset_tree_root or era_array_root fails, the function only unlocks the clone and returns. The newly allocated shadow block is never returned to the metadata space map, so each failed take-snap permanently leaks one metadata block.
Free the clone with dm_sm_dec_block() on those error paths, matching the final step of metadata_drop_snap().
--- drivers/md/dm-era-target.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/md/dm-era-target.c +5 -0
@@ -1034,6 +1034,7 @@ static int metadata_checkpoint(struct erstatic int metadata_take_snap (struct era_metadata *md) {int r, inc; +dm_block_t location;struct dm_block *clone;if (md->metadata_snap !=SUPERBLOCK_LOCATION ) {
@@ -1071,7 +1072,9 @@ static int metadata_take_snap(struct era r =dm_sm_inc_block (md->sm, md->writeset_tree_root);if (r) {DMERR ("%s: couldn't inc writeset tree root" , __func__); + location =dm_block_location (clone);dm_tm_unlock (md->tm, clone); +dm_sm_dec_block (md->sm, location);return r; }
@@ -1079,7 +1082,9 @@ static int metadata_take_snap(struct eraif (r) {DMERR ("%s: couldn't inc era tree root" , __func__);dm_sm_dec_block (md->sm, md->writeset_tree_root); + location =dm_block_location (clone);dm_tm_unlock (md->tm, clone); +dm_sm_dec_block (md->sm, location);return r; }
Greg Kroah-Hartman
maintainerPATCH 124/1424 dm raid1: reserve space for NUL-terminator in build_constructor_string()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Krutskih <[email protected]>
commit 73c37fe54cd056d07461b142ab0b8b81e1ef6ad8 upstream.
Reserve space for the termination NUL after the maximum 20 decimal digits of a long long value to avoid buffer overflow in sprintf().
--- drivers/md/dm-log-userspace-base.c | 1 + 1 file changed, 1 insertion(+)
drivers/md/dm-log-userspace-base.c +1 -0
@@ -139,6 +139,7 @@ static int build_constructor_string(stru str_size +=strlen (argv[i]) +1 ;/* +1 for space between args */ str_size +=20 ;/* Max number of chars in a printed u64 number */ + str_size++;/* For NUL-terminator */ str =kzalloc (str_size,GFP_KERNEL );if (!str) {
Greg Kroah-Hartman
maintainerPATCH 125/1424 dm array: reject an array block whose value size is not the callers
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 4538a287bdf5d0f9a379c678e5262b9f5783f547 upstream.
array_block_check() can only compare the header against itself, so a block with value_size 4 and max_entries 1018 is internally consistent and passes. dm-cache keeps two arrays -- mappings at 8 bytes and hints at 4 -- and the roots for both live in the superblock. Point the mappings root at a hint block and __load_mappings() walks it through an info whose value size is 8, so element_at() strides 8 bytes over 4-byte entries and reaches offset 8160 of a 4096-byte block.
get_ablock() and __shadow_ablock() are the two places that hold the block and the caller at once. Reject there when the two value sizes disagree. Arrays only ever read their own blocks, so this fires on crafted metadata only.
--- drivers/md/persistent-data/dm-array.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
drivers/md/persistent-data/dm-array.c +16 -0
@@ -225,6 +225,14 @@ static int get_ablock(struct dm_array_inreturn r; *ab =dm_block_data (*block); +if (le32_to_cpu ((*ab)->value_size) != info->value_type.size) { +DMERR_LIMIT ("%s failed: value_size %u != wanted %u" , __func__, +le32_to_cpu ((*ab)->value_size), + info->value_type.size); +dm_tm_unlock (info->btree_info.tm, *block); +return -EILSEQ ; + } +return 0 ; }
@@ -287,6 +295,14 @@ static int __shadow_ablock(struct dm_arrreturn r; *ab =dm_block_data (*block); +if (le32_to_cpu ((*ab)->value_size) != info->value_type.size) { +DMERR_LIMIT ("%s failed: value_size %u != wanted %u" , __func__, +le32_to_cpu ((*ab)->value_size), + info->value_type.size); +dm_tm_unlock (info->btree_info.tm, *block); +return -EILSEQ ; + } +if (inc)inc_ablock_entries (info, *ab);
Greg Kroah-Hartman
maintainerPATCH 126/1424 cpufreq: schedutil: Fix rate limit overflow
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Su <[email protected]>
commit 3bff8f8e95fdc6ad19c8a1a8f87029094747e4bf upstream.
rate_limit_us is an unsigned int, while NSEC_PER_USEC is defined as 1000L. On 32-bit systems, the multiplication is therefore performed using 32-bit unsigned arithmetic before the result is assigned to freq_update_delay_ns.
For example, writing 4294968 to rate_limit_us wraps the delay from 4294968000 ns to 704 ns. This makes schedutil update far more often than configured.
Add sugov_update_rate_limit_us() to widen rate_limit_us to s64 before converting it to nanoseconds. Use the helper when updating the tunable through sysfs and when starting the governor, so both paths perform the conversion without overflow.
--- kernel/sched/cpufreq_schedutil.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
kernel/sched/cpufreq_schedutil.c +13 -2
@@ -59,6 +59,17 @@ static DEFINE_PER_CPU(struct sugov_cpu,/************************ Governor internals ***********************/ +static void sugov_update_rate_limit_us (struct sugov_policy *sg_policy) +{ +/* +* Cast rate_limit_us before multiplication to force 64-bit arithmetic. +* Otherwise, on 32-bit platforms, both operands are converted to +* 32-bit unsigned long and the multiplication may overflow. +*/ + sg_policy->freq_update_delay_ns = + (s64 )sg_policy->tunables->rate_limit_us *NSEC_PER_USEC ; +} +static bool sugov_should_update_freq (struct sugov_policy *sg_policy,u64 time) {s64 delta_ns;
@@ -592,7 +603,7 @@ rate_limit_us_store(struct gov_attr_set tunables->rate_limit_us = rate_limit_us;list_for_each_entry (sg_policy, &attr_set->policy_list, tunables_hook) - sg_policy->freq_update_delay_ns = rate_limit_us *NSEC_PER_USEC ; +sugov_update_rate_limit_us (sg_policy);return count; }
@@ -823,7 +834,7 @@ static int sugov_start(struct cpufreq_povoid (*uu)(struct update_util_data *data,u64 time,unsigned int flags);unsigned int cpu; - sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us *NSEC_PER_USEC ; +sugov_update_rate_limit_us (sg_policy); sg_policy->last_freq_update_time =0 ; sg_policy->next_freq =0 ; sg_policy->work_in_progress = false;
Greg Kroah-Hartman
maintainerPATCH 127/1424 cxl/pmem: Format the nvdimm serial number as unsigned decimal
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alison Schofield <[email protected]>
commit 8a80d3d65cd06ee35b913d8517fb2f2319f8e70c upstream.
The CXL NVDIMM security passphrase key description and the nvdimm 'id' sysfs attribute are both derived from the CXL device serial number, but the serial number is not formatted consistently.
The key description is formatted in hexadecimal while the 'id' attribute is formatted in decimal. As a result, ndctl stores the key using a decimal description while the kernel later looks it up using a hexadecimal description. For serial numbers of 10 and above, the descriptions no longer match, preventing automatic unlock after reboot.
The decimal formatting has a second problem: both the key description and the 'id' attribute use the signed %lld format for a u64 PCIe Device Serial Number. Devices whose vendor OUI sets bit 63, such as Montage CXL devices, appear with negative decimal serial numbers.
Format the security key description and 'id' attribute as unsigned decimal, %llu, and document that the 'id' attribute is an unsigned decimal value.
The key lookup mismatch was exposed by CXL unit test cxl-security.sh when cxl_test mock serial numbers were extended to 10 and above.
A work around is described for ndctl load-key users here:
https://github.com/pmem/ndctl/issues/299
--- Documentation/ABI/testing/sysfs-bus-nvdimm | 3 ++- drivers/cxl/core/pmem.c | 10 ++++++---- drivers/cxl/cxl.h | 3 ++- drivers/cxl/pmem.c | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-)
Documentation/ABI/testing/sysfs-bus-nvdimm +2 -1
@@ -48,7 +48,8 @@ What: /sys/bus/nd/devices/nmemX/cxl/id Date: November 2022 KernelVersion: 6.2 Contact: Dave Jiang <[email protected]> -Description: (RO) Show the id (serial) of the device. This is CXL specific. +Description: (RO) Show the id (serial) of the device, formatted as an + unsigned 64-bit decimal value. This is CXL specific. What: /sys/bus/nd/devices/nmemX/cxl/provider Date: November 2022
drivers/cxl/core/pmem.c +6 -4
@@ -217,12 +217,14 @@ static struct cxl_nvdimm *cxl_nvdimm_all dev->bus = &cxl_bus_type; dev->type = &cxl_nvdimm_type;/* -* A "%llx" string is 17-bytes vs dimm_id that is max -* NVDIMM_KEY_DESC_LEN +* dev_id is the nvdimm dimm_id used for security key lookup. +* It must match id_show(), which emits the CXL serial as an +* unsigned decimal. A u64 decimal string is at most 20 digits +* plus NUL. */ -BUILD_BUG_ON (sizeof (cxl_nvd->dev_id) <17 || +BUILD_BUG_ON (sizeof (cxl_nvd->dev_id) <21 ||sizeof (cxl_nvd->dev_id) >NVDIMM_KEY_DESC_LEN ); -sprintf (cxl_nvd->dev_id,"%llx" , cxlmd->cxlds->serial); +sprintf (cxl_nvd->dev_id,"%llu" , cxlmd->cxlds->serial);return cxl_nvd; }
drivers/cxl/cxl.h +2 -1
@@ -528,7 +528,8 @@ struct cxl_nvdimm_bridge {struct nvdimm_bus_descriptor nd_desc; }; -#define CXL_DEV_ID_LEN 19 +/* Holds a u64 serial as a decimal string: up to 20 digits + NUL */ +#define CXL_DEV_ID_LEN 21struct cxl_nvdimm {struct device dev;
drivers/cxl/pmem.c +1 -1
@@ -40,7 +40,7 @@ static ssize_t id_show(struct device *destruct cxl_nvdimm *cxl_nvd =nvdimm_provider_data (nvdimm);struct cxl_dev_state *cxlds = cxl_nvd->cxlmd->cxlds; -return sysfs_emit (buf,"%lld\n" , cxlds->serial); +return sysfs_emit (buf,"%llu\n" , cxlds->serial); }static DEVICE_ATTR_RO (id);
Greg Kroah-Hartman
maintainerPATCH 128/1424 Bluetooth: hci_bcm: fix usage_count leak when autosuspend_delay is negative
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit dc6b7c771a963e20aedf4a21ffa22543b9837ba8 upstream.
bcm_request_irq() calls pm_runtime_use_autosuspend(), but bcm_close() does not call the matching pm_runtime_dont_use_autosuspend() when tearing down runtime PM.
If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during driver teardown, this reference is not dropped and usage_count remains unbalanced.
Add the missing pm_runtime_dont_use_autosuspend() call before disabling runtime PM.
This issue was found by manual code inspection.
--- drivers/bluetooth/hci_bcm.c | 1 + 1 file changed, 1 insertion(+)
drivers/bluetooth/hci_bcm.c +1 -0
@@ -548,6 +548,7 @@ static int bcm_close(struct hci_uart *huif (IS_ENABLED (CONFIG_PM ) && bdev->irq_acquired) {devm_free_irq (bdev->dev, bdev->irq, bdev);device_init_wakeup (bdev->dev, false); +pm_runtime_dont_use_autosuspend (bdev->dev);pm_runtime_disable (bdev->dev); }
Greg Kroah-Hartman
maintainerPATCH 129/1424 Bluetooth: hci_uart: Fix false success return in hci_uart_setup()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gongwei Li <[email protected]>
commit a9355799343e10014f2acfd4b6844d2335ecafea upstream.
When reading the local version information for vendor detection fails, the error is only printed and 0 is returned, which masks the setup failure from the HCI core.
Return PTR_ERR(skb) instead.
--- drivers/bluetooth/hci_ldisc.c | 2 +- drivers/bluetooth/hci_serdev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/bluetooth/hci_ldisc.c +1 -1
@@ -445,7 +445,7 @@ static int hci_uart_setup(struct hci_devif (IS_ERR (skb)) {BT_ERR ("%s: Reading local version information failed (%ld)" , hdev->name,PTR_ERR (skb)); -return 0 ; +return PTR_ERR (skb); }if (skb->len !=sizeof (*ver)) {
drivers/bluetooth/hci_serdev.c +1 -1
@@ -221,7 +221,7 @@ static int hci_uart_setup(struct hci_devif (IS_ERR (skb)) {bt_dev_err (hdev,"Reading local version info failed (%ld)" ,PTR_ERR (skb)); -return 0 ; +return PTR_ERR (skb); }if (skb->len !=sizeof (*ver))
Greg Kroah-Hartman
maintainerPATCH 130/1424 Bluetooth: ISO: fix use-after-free of listener socket in iso_conn_ready
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hang Nan <[email protected]>
commit 560bef609fa5992745929e8d7d458b9d88dd2830 upstream.
iso_conn_ready() looks up the BIS listener socket with iso_get_sock(), which takes a reference, and then, without re-checking its state, creates a child socket from it:
parent = iso_get_sock(hdev, ...);
if (!parent)
return;
lock_sock(parent);
sk = iso_sock_alloc(sock_net(parent), NULL, BTPROTO_ISO, ...);
...
iso_chan_add(conn, sk, parent);
...
release_sock(parent);
sock_put(parent);If the listener socket is closed concurrently, between iso_get_sock() and lock_sock(), the reference taken by iso_get_sock() may be the last
one: the close path drops the link-list reference, and once iso_conn_ready() drops its own reference at the end of the function the socket is freed. The child socket, however, is already linked to the freed parent, and a later disconnect of the child runs iso_chan_del() -> bt_accept_unlink(), which dereferences the dangling parent pointer into the freed accept queue (a use-after-free). The same dangling pointer is also dereferenced through parent->***() in iso_chan_del().
Fix it the same way the connected (non-BIS) path was fixed in commit 0d255e63fcf3 ("Bluetooth: ISO: hold sk properly in iso_conn_ready"):
after taking the socket lock, re-check that the parent is still a listening, alive socket, and bail out otherwise.
--- net/bluetooth/iso.c | 8 ++++++++ 1 file changed, 8 insertions(+)
net/bluetooth/iso.c +8 -0
@@ -1760,6 +1760,14 @@ static void iso_conn_ready(struct iso_colock_sock (parent); +/* The listener may have been closed concurrently. */ +if (parent->sk_state !=BT_LISTEN || +sock_flag (parent,SOCK_ZAPPED )) { +release_sock (parent); +sock_put (parent); +return ; + } + sk =iso_sock_alloc (sock_net (parent),NULL ,BTPROTO_ISO ,GFP_ATOMIC ,0 );if (!sk) {
Greg Kroah-Hartman
maintainerPATCH 131/1424 Bluetooth: RFCOMM: serialize security confirmation handling
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
commit 759c185d0bbdb131357408f50b8735e04ed3caff upstream.
rfcomm_security_cfm() looks up a session on session_list and then walks its DLC list without holding rfcomm_mutex. Since RFCOMM session teardown uses rfcomm_mutex, krfcommd can close and free the same session and DLCs concurrently:
hci_rx_work krfcommd
----------- ---------
rfcomm_session_get()
rfcomm_lock() rfcomm_session_close()
rfcomm_dlc_unlink() rfcomm_session_del()
kfree(s)
rfcomm_unlock()
walk s->dlcs
The callback can then read a freed session list head and touch freed DLCs while updating their flags or timers.
Serialize the session lookup and DLC traversal in rfcomm_security_cfm() with rfcomm_mutex. This matches the existing RFCOMM session lifetime rules and prevents concurrent rfcomm_session_del() / rfcomm_dlc_unlink() from tearing the objects down while the callback is using them.
KASAN reported:
BUG: KASAN: slab-use-after-free in rfcomm_security_cfm+0x41c/0x440 Read of size 8 at addr ffff888111fb3960 by task kworker/u17:1/89 Workqueue: hci0 hci_rx_work Call Trace: rfcomm_security_cfm+0x41c/0x440 hci_encrypt_cfm+0x139/0x590 hci_encrypt_change_evt+0x37b/0xc40 hci_event_packet+0x71b/0xb20 hci_rx_work+0x293/0x730 Allocated by task 69: rfcomm_session_add+0x9e/0x2f0 rfcomm_run+0x44b/0x41e0 Freed by task 69: kfree+0x131/0x3c0 rfcomm_session_del+0x188/0x220 rfcomm_run+0x1985/0x41e0
--- net/bluetooth/rfcomm/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
net/bluetooth/rfcomm/core.c +7 -1
@@ -2207,9 +2207,13 @@ static void rfcomm_security_cfm(struct hBT_DBG ("conn %p status 0x%02x encrypt 0x%02x" , conn, status, encrypt); +rfcomm_lock (); + s =rfcomm_session_get (&conn->hdev->bdaddr, &conn->dst); -if (!s) +if (!s) { +rfcomm_unlock ();return ; + }list_for_each_entry_safe (d, n, &s->dlcs, list) {if (test_and_clear_bit (RFCOMM_SEC_PENDING , &d->flags)) {
@@ -2241,6 +2245,8 @@ static void rfcomm_security_cfm(struct hset_bit (RFCOMM_AUTH_REJECT , &d->flags); } +rfcomm_unlock (); +rfcomm_schedule (); }
Greg Kroah-Hartman
maintainerPATCH 132/1424 Bluetooth: hci_conn: re-enable advertising only for peripheral role
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Valentin Kindschi <[email protected]>
commit ed5fb41d3b6b6e665e7f97fd54bd1f9531c7477f upstream.
hci_le_conn_failed() unconditionally calls hci_enable_advertising(), although its own comment states advertising should be re-enabled only when the failed attempt was made as a peripheral.
hci_le_conn_failed() is reached from hci_conn_failed() for every failed LE connection, including outgoing central connections. For a central attempt this enable is redundant: hci_le_create_conn_sync() already restores advertising via hci_resume_advertising_sync() in its done:
block. Because hci_enable_advertising() only queues the work on cmd_sync_work, it runs *after* that resume has already succeeded and set HCI_LE_ADV.
The resulting HCI sequence, captured on a BCM43455 (no LE Extended Advertising, so legacy advertising is used):
LE Create Connection Status Success
... 13.8 s, peer never answers ...
LE Set Advertising Parameters (0x2006) Success <- done: resume, LE Set Advertising Enable (0x200a) Success HCI_LE_ADV set LE Create Connection Cancel (0x200e) Success LE Connection Complete Unknown Conn Id LE Set Advertising Parameters (0x2006) Command Disallowed (0x0c)
The last command is the queued enable from hci_le_conn_failed() running as a second hci_enable_advertising_sync() pass. It clears HCI_LE_ADV (hci_sync.c, "Clear the HCI_LE_ADV bit temporarily"), then sends LE Set Advertising Parameters while the controller is still advertising, which the controller correctly rejects with Command Disallowed.
The disable-first call at the top of hci_enable_advertising_sync() cannot prevent this: hci_disable_advertising_sync() returns early without sending anything when HCI_LE_ADV is clear, so it is a no-op exactly when the flag is wrong.
hci_enable_advertising_sync() then returns without sending LE Set Advertising Enable, so HCI_LE_ADV is never set again. The legacy software rotation loop re-arms hci_schedule_adv_instance_sync() every HCI_DEFAULT_ADV_DURATION (2 s), and its "already advertising" shortcut tests HCI_LE_ADV, which can no longer become true. The command is therefore retried every 2 s indefinitely:
Bluetooth: hci0: Opcode 0x2006 failed: -16
Observed on a gateway as 5326 occurrences over 3 hours, ending only when bluetoothd was restarted. Connection attempts that succeed do not call hci_le_conn_failed() and never trigger this.
Add the role test the comment already describes. Both other hci_enable_advertising() call sites reached from a failed/closed LE connection (hci_cs_disconnect() and hci_disconn_complete_evt()) already guard on conn->role == HCI_ROLE_SLAVE; this one was missed.
Reproducing needs legacy advertising (ext_adv_capable() false, so the software rotation loop is used), simultaneous peripheral advertising and outgoing central connects, and a central connect that times out rather than failing fast.
The Fixes tag points at the commit that introduced the advertising restart into this path for the directed-advertising (peripheral) case; the role test that the later commit 0b1db38ca26b ("Bluetooth: Fix check for direct advertising") added to the sibling paths was never applied here.
--- net/bluetooth/hci_conn.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/bluetooth/hci_conn.c +2 -1
@@ -1229,7 +1229,8 @@ static void hci_le_conn_failed(struct hc/* Enable advertising in case this was a failed connection * attempt as a peripheral. */ -hci_enable_advertising (hdev); +if (conn->role ==HCI_ROLE_SLAVE ) +hci_enable_advertising (hdev); }/* This function requires the caller holds hdev->lock */
Greg Kroah-Hartman
maintainerPATCH 133/1424 Bluetooth: hci_core: use skb_get() instead of skb_clone() for req_skb
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xin Chen <[email protected]>
commit f5afdff569a09d1cb8cf19826199d024725576cb upstream.
BT enable fails intermittently with -ETIMEDOUT (-110). The kernel log shows the HCI Read Local Version command was sent and the firmware replied with status 0x00 (logged by hci_req_cmd_complete() BT_DBG), but the waiter in __hci_cmd_sync_sk() never woke up and timed out after 10 s:
bluetooth hci0: Opcode 0xfc00 // __hci_cmd_sync_sk bluetooth hci0: opcode 0xfc00 plen 1 // hci_cmd_sync_add bluetooth hci0: skb len 4 // hci_cmd_sync_alloc bluetooth hci0: length 1 // hci_req_sync_run Bluetooth: hci0 cmd_cnt 1 cmd queued 1 // hci_cmd_work Bluetooth: hci0 type 1 len 4 // hci_send_frame Bluetooth: opcode 0xfc00 status 0x00 // hci_req_cmd_complete
<-- req_skb NULL: req_complete_skb not set,
hci_cmd_sync_complete() never called,
req_status stays HCI_REQ_PEND -->
<-- 10 s later: wait_event_interruptible_timeout expires -->
bluetooth hci0: end: err -110 // __hci_cmd_sync_sk
The root cause is that hci_send_cmd_sync() clones the sent command into hdev->req_skb so that hci_req_cmd_complete() can locate the registered completion callback. Under memory pressure this skb_clone() fails, leaving hdev->req_skb NULL. The firmware reply is received and processed, but hci_req_cmd_complete() finds NULL req_skb, so hci_cmd_sync_complete() is never called, req_status stays HCI_REQ_PEND, and the waiter times out with -ETIMEDOUT.
req_skb is only used to read bt_cb(skb)->hci callbacks and opcode -- it is never modified. Replace skb_clone() with skb_get(), which simply increments the reference count of hdev->sent_cmd without allocating new memory and therefore cannot fail.
This issue was first observed as a use-after-free in ttyport_close() when ttyport_open() failed, which was investigated in an earlier patch series [1]. That investigation led to the discovery of the true root cause described above.
[1] https://lore.kernel.org/all/[email protected]/
--- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/bluetooth/hci_core.c +1 -1
@@ -4063,7 +4063,7 @@ static void hci_send_cmd_sync(struct hciif (READ_ONCE (hdev->req_status) ==HCI_REQ_PEND && !hci_dev_test_and_set_flag (hdev,HCI_CMD_PENDING )) {kfree_skb (hdev->req_skb); - hdev->req_skb =skb_clone (hdev->sent_cmd,GFP_KERNEL ); + hdev->req_skb =skb_get (hdev->sent_cmd); }atomic_dec (&hdev->cmd_cnt);
Greg Kroah-Hartman
maintainerPATCH 134/1424 Bluetooth: hci_event: clear HCI_LE_ADV only on a created connection
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Valentin Kindschi <[email protected]>
commit 941929abe5feaed672b9a52e330e547d333240c6 upstream.
le_conn_complete_evt() clears HCI_LE_ADV before looking at the event status, on the premise stated in its comment that all controllers stop advertising when a connection is created.
That premise only holds when a connection was actually created. On a non-zero status none was, and the controller is still advertising: after the host issues LE Create Connection Cancel the event arrives with Unknown Connection Identifier (0x02), and a connection timeout behaves the same way. Clearing the flag there leaves the host believing advertising is off while the controller has it on.
It is also wrong for extended advertising, where several sets can be advertising at once. hci_cc_le_set_ext_adv_enable() is careful about this - on disabling one set it walks hdev->adv_instances and only clears HCI_LE_ADV once no instance is still enabled. The unconditional clear here discards that bookkeeping, so one set connecting drops the flag while the others keep advertising.
The direction of the error matters. A flag left set is self-correcting:hci_disable_advertising_sync() sends LE Set Advertising Enable(0) and the command complete puts the state back. A flag left clear is not, because that same function returns early without sending anything while the flag is clear:
- LE Set Advertising Parameters is then sent to a controller that is still advertising, and is correctly rejected with Command Disallowed (0x0c);
- hci_enable_advertising_sync() returns at that point, before the LE Set Advertising Enable that would set HCI_LE_ADV again.
On a controller without LE Extended Advertising that is reachable from
here: hci_schedule_adv_instance_sync() re-arms adv_instance_expire every HCI_DEFAULT_ADV_DURATION (2 s) and its "already advertising" shortcut tests HCI_LE_ADV, which can no longer become true, so the parameter write is retried for as long as advertising is configured:
Bluetooth: hci0: Opcode 0x2006 failed: -16
Only clear the flag when a connection was established.
Note this is not on its own sufficient to stop that retry loop - the redundant enable queued by hci_le_conn_failed() clears HCI_LE_ADV itself and recreates the same mismatch, which patch 1 addresses. This patch fixes the event handler reporting a state the controller is not in.
Verified on the affected device (BCM43455, legacy advertising only) with this patch and patch 1 applied. A 221 s btmon capture with an out-of-range peer at -90 dBm contains two outgoing connection attempts that the host cancelled, each producing exactly the event this patch changes:
< LE Set Advertising Parameters 0x2006 Success < LE Set Advertising Enable 0x200a Success < LE Create Connection Cancel 0x200e Success
Quoted 1 line
LE Connection Complete Unknown Connection Identifier (
0x02), central
Nothing follows either one; the next command is an unrelated scan restart 70 ms later. Over the whole capture: 7 LE Set Advertising Parameters sent, all Success; 10 LE Set Advertising Enable, all Success; no Command Disallowed of any opcode, and no 2 s cadence anywhere. Two central connections to other peers completed normally afterwards, with feature exchange and a connection parameter update, so advertising was still live across the cancelled attempts.
The extended advertising case above is a code argument, not a measurement:
this controller has no LE Extended Advertising, so that path is not exercised by the capture.
--- net/bluetooth/hci_event.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
net/bluetooth/hci_event.c +4 -3
@@ -5646,10 +5646,11 @@ static void le_conn_complete_evt(structhci_dev_lock (hdev); -/* All controllers implicitly stop advertising in the event of a -* connection, so ensure that the state bit is cleared. +/* Advertising stops when a connection is created. On a failed +* connection it keeps running, so leave the state bit alone. */ -hci_dev_clear_flag (hdev,HCI_LE_ADV ); +if (!status) +hci_dev_clear_flag (hdev,HCI_LE_ADV );/* Check for existing connection: *
Greg Kroah-Hartman
maintainerPATCH 135/1424 Bluetooth: hci_h5: fix usage_count leak when autosuspend_delay is negative
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 853a92b97ca547a7ddd9790ff90651b2fd943498 upstream.
h5_btrtl_open() calls pm_runtime_use_autosuspend(), but h5_btrtl_close() does not call the matching pm_runtime_dont_use_autosuspend() when tearing down runtime PM.
If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during driver teardown, this reference is not dropped and usage_count remains unbalanced.
Add the missing pm_runtime_dont_use_autosuspend() call before disabling runtime PM.
This issue was found by manual code inspection.
--- drivers/bluetooth/hci_h5.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/bluetooth/hci_h5.c +3 -1
@@ -987,8 +987,10 @@ static void h5_btrtl_open(struct h5 *h5)static void h5_btrtl_close (struct h5 *h5) { -if (!test_bit (H5_WAKEUP_DISABLE , &h5->flags)) +if (!test_bit (H5_WAKEUP_DISABLE , &h5->flags)) { +pm_runtime_dont_use_autosuspend (&h5->hu->serdev->dev);pm_runtime_disable (&h5->hu->serdev->dev); + }gpiod_set_value_cansleep (h5->device_wake_gpio,0 );gpiod_set_value_cansleep (h5->enable_gpio,0 );
Greg Kroah-Hartman
maintainerPATCH 136/1424 Bluetooth: hci_intel: fix usage_count leak when autosuspend_delay is negative
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit c7e9a8cb6918656884a0757c92465075c7555ffa upstream.
intel_set_power() calls pm_runtime_use_autosuspend() when powering on the device, but the power-off path does not call the matching pm_runtime_dont_use_autosuspend() before disabling runtime PM.
If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during teardown, this reference is not dropped and usage_count remains unbalanced.
Add the missing pm_runtime_dont_use_autosuspend() call before disabling runtime PM.
This issue was found by manual code inspection.
--- drivers/bluetooth/hci_intel.c | 1 + 1 file changed, 1 insertion(+)
drivers/bluetooth/hci_intel.c +1 -0
@@ -348,6 +348,7 @@ static int intel_set_power(struct hci_uadevm_free_irq (&idev->pdev->dev, idev->irq, idev);device_wakeup_disable (&idev->pdev->dev); +pm_runtime_dont_use_autosuspend (&idev->pdev->dev);pm_runtime_disable (&idev->pdev->dev); } }
Greg Kroah-Hartman
maintainerPATCH 137/1424 Bluetooth: hci_sync: Clear HCI_CMD_PENDING when dropping the last request
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Abdelkader <[email protected]>
commit cb19774faa57c51efa189d8b8606aeabccebc53b upstream.
A synchronous HCI command that never receives a response leaves HCI_CMD_PENDING set: hci_req_cmd_complete() is the only place that clears it, and it only runs when a response matching the last command sent arrives.
hci_send_cmd_sync() populates hdev->req_skb only when the flag transitions from clear to set, while hci_dev_open_sync() and hci_dev_close_sync() drop req_skb without clearing the flag. After a timeout followed by either, the two disagree: the flag claims a request is outstanding while req_skb is NULL. Subsequent synchronous commands are then sent with no req_skb, so hci_event_packet() has nothing to match an arriving event against, and the caller times out even though the controller answered.
Commands answered by Command Complete recover on their own, since hci_req_cmd_complete() clears the flag as a side effect. Drivers using __hci_cmd_sync_ev() with a custom event do not, because a vendor event never reaches that path. On a WCN3988 (hci_qca over UART) this makes a controller firmware hang unrecoverable: the driver injects a hardware error and re-runs qca_setup(), qca_read_soc_version() waits for HCI_EV_VENDOR, the reply arrives within 4 ms and is discarded, and every retry fails the same way. The adapter is left down until the driver is unbound and rebound, or power is removed.
Clear the flag wherever the last request is dropped, restoring the invariant that req_skb is non-NULL exactly when HCI_CMD_PENDING is set. Verified on hardware by forcing a command timeout: without this change setup fails on every attempt, with it setup succeeds on the first.
--- net/bluetooth/hci_sync.c | 2 ++ 1 file changed, 2 insertions(+)
net/bluetooth/hci_sync.c +2 -0
@@ -5274,6 +5274,7 @@ int hci_dev_open_sync(struct hci_dev *hdif (hdev->req_skb) {kfree_skb (hdev->req_skb); hdev->req_skb =NULL ; +hci_dev_clear_flag (hdev,HCI_CMD_PENDING ); }clear_bit (HCI_RUNNING , &hdev->flags);
@@ -5442,6 +5443,7 @@ int hci_dev_close_sync(struct hci_dev *hif (hdev->req_skb) {kfree_skb (hdev->req_skb); hdev->req_skb =NULL ; +hci_dev_clear_flag (hdev,HCI_CMD_PENDING ); }clear_bit (HCI_RUNNING , &hdev->flags);
Greg Kroah-Hartman
maintainerPATCH 138/1424 kasan: fix cache shrink race with CPU hotplug
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Su <[email protected]>
commit 8790303cbaac52a11dfed4aab261f8ea60682525 upstream.
kasan_quarantine_remove_cache() first invokes per_cpu_remove_cache() on all online CPUs. Each callback moves objects belonging to the cache from cpu_quarantine to the CPU's shrink_qlist, where they can later be freed from task context.
kmem_cache_destroy() invokes the quarantine removal path while holding cpus_read_lock(), but kmem_cache_shrink() does not. The latter can therefore race with CPU offlining as follows:
kmem_cache_shrink() CPU hotplug
------------------- -----------
on_each_cpu()
CPU1 moves objects to
CPU1's shrink_qlist
on_each_cpu() returns
CPU1 goes offline kasan_cpu_offline()
drains cpu_quarantine leaves shrink_qlist untouched
for_each_online_cpu()
skips CPU1
The objects left on CPU1's shrink_qlist are not returned to the slab allocator. This may prevent kmem_cache_shrink() from releasing slabs that would otherwise become empty. If CPU1 remains offline, a later kmem_cache_destroy() also skips the list and can report that the cache still contains objects.
An intermittent occurrence was observed with a virtio-9p filesystem. The mount and umount commands both returned 0, but the kernel logged the following during the userspace-triggered teardown:
[ 2994.380134][ T111] BUG 9p-fcall-cache-1 (Tainted: G B ): Objects remaining on __kmem_cache_shutdown() [ 2994.381140][ T111] Object 0xff11000004361118 @offset=4376 [ 2994.381607][ T111] Allocated in p9_fcall_init+0x201/0x400 age=19564 cpu=1 pid=104 [ 2994.382591][ T111] p9_fcall_init+0x201/0x400 [ 2994.382810][ T111] p9_tag_alloc+0x12f/0x700 [ 2994.382982][ T111] p9_client_prepare_req+0x102/0x3e0 [ 2994.383165][ T111] p9_client_rpc+0x1ab/0xa50 [ 2994.383334][ T111] p9_client_getattr_dotl+0xb0/0x1a0 [ 2994.383515][ T111] v9fs_vfs_getattr_dotl+0x115/0x360 [ 2994.383719][ T111] vfs_getattr_nosec+0x22c/0x3a0 [ 2994.383910][ T111] vfs_statx+0xd7/0x170 [ 2994.384062][ T111] vfs_fstatat+0x45/0x80 [ 2994.384215][ T111] __do_sys_newfstatat+0x84/0xe0 [ 2994.384386][ T111] do_syscall_64+0x115/0x6a0 [ 2994.384566][ T111] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 2994.399720][ T111] WARNING: mm/slub.c:1244 at __kmem_cache_shutdown+0x363/0x500, CPU#0: busybox/111 [ 2994.405655][ T111] Call Trace: [ 2994.406325][ T111] kmem_cache_destroy+0x73/0x1b0 [ 2994.406630][ T111] p9_client_destroy+0x271/0x3c0 [ 2994.407210][ T111] v9fs_session_close+0x3c/0x260 [ 2994.407409][ T111] v9fs_kill_super+0x48/0x90 [ 2994.407584][ T111] deactivate_locked_super+0xa3/0x160 [ 2994.407778][ T111] cleanup_mnt+0x1dd/0x3e0
Thus, a successful umount left objects in the 9p fcall cache and prevented the cache from being destroyed cleanly.
Per-CPU shrink_qlist storage exists for every possible CPU, and each list is protected by its own raw spinlock. Iterate over possible CPUs so that a list populated before its CPU went offline is drained as well.
for_each_possible_cpu() can do more work than for_each_online_cpu(), but this change only affects CONFIG_KASAN_GENERIC kernels. The extra work is limited to cache shrink and cache destruction paths and does not affect the normal allocation/free fast path. It adds one raw-spinlock-protected scan of each possible CPU's shrink list. These lists are normally empty; a non-empty list is traversed to remove objects belonging to the cache being shrunk or destroyed.
--- mm/kasan/quarantine.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
mm/kasan/quarantine.c +6 -1
@@ -361,7 +361,12 @@ void kasan_quarantine_remove_cache(struc*/ on_each_cpu (per_cpu_remove_cache, cache,1 ); -for_each_online_cpu (cpu) { +/* +* A CPU can go offline after on_each_cpu() returns, leaving cache +* objects on that CPU's shrink list. Scan all possible CPUs to +* drain those lists. +*/ +for_each_possible_cpu (cpu) { sq =per_cpu_ptr (&shrink_qlist, cpu);raw_spin_lock_irqsave (&sq->lock, flags);qlist_move_cache (&sq->qlist, &to_free, cache);
Greg Kroah-Hartman
maintainerPATCH 139/1424 ipip: fix skb leak in collect_md mode when metadata_dst allocation fails
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anton Danilov <[email protected]>
commit 6776efe4a52f289a3fc18f8adf19b035a7d8e1bb upstream.
In collect_md mode ipip_tunnel_rcv() returns 0 without freeing the skb when ip_tun_rx_dst() fails to allocate the metadata_dst. ipip_rcv() and mplsip_rcv() are registered as xfrm_tunnel handlers, so tunnel4_rcv() and tunnelmpls4_rcv() read the zero return as "the packet has been consumed" and do not free it either. The skb is leaked.
The other tunnel drivers all dispose of the packet at this point:ip6_tunnel.c jumps to its drop label, ip_gre.c and ip6_gre.c return PACKET_REJECT, which makes gre_rcv() free the skb. Only ipip returns 0.
Jump to the existing drop label instead. It frees the skb and still returns 0, so the packet keeps being reported as consumed, which is what we want here: the outer header has already been pulled, and neither the remaining handlers nor an ICMP unreachable have any use for it.
Triggering this needs an ipip or mplsip tunnel in collect_md mode and an atomic allocation failure, which is why it has gone unnoticed.
--- net/ipv4/ipip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/ipip.c +1 -1
@@ -240,7 +240,7 @@ static int ipip_tunnel_rcv(struct sk_bufif (tunnel->collect_md) { tun_dst =ip_tun_rx_dst (skb,0 ,0 ,0 );if (!tun_dst) -return 0 ; + goto drop;ip_tunnel_md_udp_encap (skb, &tun_dst->u.tun_info); }skb_reset_mac_header (skb);
Greg Kroah-Hartman
maintainerPATCH 140/1424 ip6_tunnel: use skb_cow_head() in ip6_tnl_xmit()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit 87f21b59ddc618eff9670c174842964ad65fdade upstream.
ip6_tnl_xmit() may need to expand headroom before it can push the outer IPv6 and optional encap headers. It currently does that with skb_realloc_headroom(), copies skb->sk ownership, consumes the original skb, and then continues processing with the replacement skb kept only in its local variable.
That is safe only if the helper cannot fail afterwards. But this helper still has post-reallocation error exits. collect_md tunnels reject non-NONE encap after the replacement, and ip6_tnl_encap() can also fail later. In those cases the helper returns an error to its callers while the caller still only has the original skb pointer.
Both ip6_tnl_start_xmit() and the IPv6 GRE paths free the caller skb on error, so they can end up freeing an skb that ip6_tnl_xmit() already consumed.
Use skb_cow_head() instead. It provides the required headroom and writability without privately replacing the caller-owned skb, so later error returns cannot leave callers with a stale pointer.
The Ethernet users, ip6gretap and ip6erspan, clear IFF_TX_SKB_SHARING and already call skb_cow_head() before entering ip6_tnl_xmit(). They do not rely on the removed skb_shared() reallocation. This also makes the IPv6 tunnel path consistent with ip_tunnel_xmit().
--- net/ipv6/ip6_tunnel.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-)
net/ipv6/ip6_tunnel.c +2 -13
@@ -1226,19 +1226,8 @@ route_lookup:*/ max_headroom +=LL_RESERVED_SPACE (tdev); -if (skb_headroom (skb) < max_headroom ||skb_shared (skb) || - (skb_cloned (skb) && !skb_clone_writable (skb,0 ))) { -struct sk_buff *new_skb; - - new_skb =skb_realloc_headroom (skb, max_headroom); -if (!new_skb) - goto tx_err_dst_release; - -if (skb->sk) -skb_set_owner_w (new_skb, skb->sk); -consume_skb (skb); - skb = new_skb; - } +if (skb_cow_head (skb, max_headroom)) + goto tx_err_dst_release;if (t->parms.collect_md) {if (t->encap.type !=TUNNEL_ENCAP_NONE )
Greg Kroah-Hartman
maintainerPATCH 141/1424 ip6_gre: fix hardware header length for NBMA tunnels
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit 505b6d296c486ef7d1274f279d4c43a172f63224 upstream.
ip6gre_tnl_link_config_route() accumulates the lower device's hardware header length into dev->hard_header_len whenever header_ops is set. This is incorrect for both users of header_ops.
ip6gretap and ip6erspan have a fixed Ethernet hardware header length. For an NBMA ip6gre tunnel, ip6gre_header() creates only the GRE header, the optional FOU or GUE header, and the outer IPv6 header. The lower device header is headroom needed later, not part of the tunnel device's hardware header.
Keep the lower device header in needed_headroom. Set hard_header_len to the tunnel header length only for ARPHRD_IP6GRE devices with header_ops, and leave the fixed Ethernet header length unchanged for tap and erspan devices.
--- net/ipv6/ip6_gre.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-)
net/ipv6/ip6_gre.c +4 -9
@@ -1150,13 +1150,8 @@ static void ip6gre_tnl_link_config_routereturn ;if (rt->dst.dev) { -unsigned short dst_len = rt->dst.dev->hard_header_len + - t_hlen; - -if (t->dev->header_ops) - dev->hard_header_len = dst_len; -else - dev->needed_headroom = dst_len; + dev->needed_headroom = rt->dst.dev->hard_header_len + + t_hlen;if (set_mtu) {int mtu = rt->dst.dev->mtu - t_hlen;
@@ -1184,8 +1179,8 @@ static int ip6gre_calc_hlen(struct ip6_t t_hlen = tunnel->hlen +sizeof (struct ipv6hdr ); -if (tunnel->dev->header_ops) - tunnel->dev->hard_header_len =LL_MAX_HEADER + t_hlen; +if (tunnel->dev->header_ops && tunnel->dev->type ==ARPHRD_IP6GRE ) + tunnel->dev->hard_header_len = t_hlen;else tunnel->dev->needed_headroom =LL_MAX_HEADER + t_hlen;
Greg Kroah-Hartman
maintainerPATCH 142/1424 ipv6: use RCU iterator to dump route exceptions
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuyang Huang <[email protected]>
commit 47cdab0d51aaa9bd85f8e4904585bd5bd4df4488 upstream.
rt6_nh_dump_exceptions() uses hlist_for_each_entry() to iterate over RCU-protected exception lists. The caller holds rcu_read_lock(), but does not hold rt6_exception_lock, so rt6_insert_exception() can concurrently add an entry with hlist_add_head_rcu().
KCSAN reports this race (irrelevant details omitted):
==================================================================
BUG: KCSAN: data-race in rt6_insert_exception / rt6_nh_dump_exceptions
write (marked) to 0xffff8a7c44c59620 of 8 bytes by interrupt on cpu 5:
rt6_insert_exception+0x3bb/0x760
__ip6_rt_update_pmtu+0x4fe/0x750
ip6_sk_update_pmtu+0x19a/0x3b0
udpv6_err+0x3ff/0x800
icmpv6_notify+0x1e1/0x440
icmpv6_rcv+0x8c0/0xab0
ip6_protocol_deliver_rcu+0x616/0x840
ip6_input_finish+0xb9/0x160
...
entry_SYSCALL_64_after_hwframe+0x77/0x7f read to 0xffff8a7c44c59620 of 8 bytes by task 549 on cpu 14:
rt6_nh_dump_exceptions+0xb3/0x260
rt6_dump_route+0x53e/0x5f0
fib6_dump_node+0x6d/0xf0
fib6_walk_continue+0x290/0x2d0
fib6_dump_table+0x28d/0x360
inet6_dump_fib+0x37d/0x620
rtnl_dumpit+0x7b/0xd0
netlink_dump+0x3ae/0x7e0
...
entry_SYSCALL_64_after_hwframe+0x77/0x7f4 locks held by dumper/549:
...
#1: (rcu_read_lock){....}-{1:3}, at: inet6_dump_fib+0x88/0x620
#2: (&tb->tb6_lock){+.-.}-{3:3}, at: fib6_dump_table+0x1e9/0x360
#3: (rcu_read_lock){....}-{1:3}, at: rt6_dump_route+0x483/0x5f0 value changed: 0xffff8a7c44e05700 -> 0xffff8a7c45d60100
Reported by Kernel Concurrency Sanitizer on:
CPU: 14 UID: 0 PID: 549 Comm: dumper Not tainted 7.2.0-rc7-virtme #38 PREEMPT(lazy) ...
Use hlist_for_each_entry_rcu() to safely iterate over the exception list.
--- net/ipv6/route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv6/route.c +1 -1
@@ -5922,7 +5922,7 @@ static int rt6_nh_dump_exceptions(structreturn 0 ;for (i =0 ; i <FIB6_EXCEPTION_BUCKET_SIZE ; i++) { -hlist_for_each_entry (rt6_ex, &bucket->chain, hlist) { +hlist_for_each_entry_rcu (rt6_ex, &bucket->chain, hlist) {if (w->skip) { w->skip--;continue ;
Greg Kroah-Hartman
maintainerPATCH 143/1424 libnvdimm/labels: Prevent integer overflow in __nd_label_validate()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 037770686126155eafc44501312989e2837b9659 upstream.
The on-media namespace index field nslot is a u32 read from the DIMM label storage area. __nd_label_validate() bounds it against the config area size, but sizeof_namespace_label() returns unsigned, so the product nslot * label_size is evaluated in 32-bit and wraps modulo 2^32 before the comparison. A crafted nslot passes the bound and is then used as the loop trip count in nd_label_data_init(), whose memset() walks off the end of the config_size buffer: an out-of-bounds write.
The field is not trusted -- it comes from the medium, or from userspace via ND_CMD_SET_CONFIG_DATA. Evaluate the product in 64-bit so the bound check is exact; conforming labels are unaffected.
The check was safe when introduced by commit 4a826c83db4e ("libnvdimm:
namespace indices: read and validate"): it multiplied by sizeof(struct nd_namespace_label), a size_t, so on a 64-bit build the product did not wrap. Commit 564e871aa66f ("libnvdimm, label: add v1.2 nvdimm label definitions") narrowed it to 32 bits when the label size became a runtime value read via sizeof_namespace_label().
--- drivers/nvdimm/label.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvdimm/label.c +1 -1
@@ -202,7 +202,7 @@ static int __nd_label_validate(struct nv } nslot =__le32_to_cpu (nsindex[i]->nslot); -if (nslot *sizeof_namespace_label (ndd) +if ((u64 )nslot *sizeof_namespace_label (ndd) +2 *sizeof_namespace_index (ndd) > ndd->nsarea.config_size) { dev_dbg(dev, "nsindex%d nslot: %u invalid, config_size: %#x\n",
Greg Kroah-Hartman
maintainerPATCH 144/1424 mailbox: qcom-ipcc: fix duplicate channel allocation across holes
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anup Vishwakarma <[email protected]>
commit 66c7bcad72430a02c860521031350b84b31ad9a8 upstream.
The IPCC of_xlate() both scans for a free mailbox channel and checks for duplicate references to the same underlying IPCC channel. When a channel has been shutdown it might have left a hole in the channel list, which would terminate the search without considering duplicates later in the list.
Continue the traversal of the channel list to detect and reject duplicates, while keeping track of the first free channel.
--- drivers/mailbox/qcom-ipcc.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
drivers/mailbox/qcom-ipcc.c +11 -6
@@ -165,7 +165,7 @@ static struct mbox_chan *qcom_ipcc_mbox_ {struct qcom_ipcc *ipcc =to_qcom_ipcc (mbox);struct qcom_ipcc_chan_info *mchan; -struct mbox_chan *chan; +struct mbox_chan *chan, *free_chan =NULL ;struct device *dev;int chan_id;
@@ -178,16 +178,21 @@ static struct mbox_chan *qcom_ipcc_mbox_ chan = &ipcc->chans[chan_id]; mchan = chan->con_priv; -if (!mchan) -break ; -else if (mchan->client_id == ph->args[0 ] && - mchan->signal_id == ph->args[1 ]) +if (!mchan) { +/* Keep scanning past holes to reject duplicate channel requests. */ +if (!free_chan) + free_chan = chan; + }else if (mchan->client_id == ph->args[0 ] && + mchan->signal_id == ph->args[1 ]) {return ERR_PTR (-EBUSY ); + } } -if (chan_id >= mbox->num_chans) +if (!free_chan)return ERR_PTR (-EBUSY ); + chan = free_chan; + mchan =devm_kzalloc (dev,sizeof (*mchan),GFP_KERNEL );if (!mchan)return ERR_PTR (-ENOMEM );
Greg Kroah-Hartman
maintainerPATCH 145/1424 md: do overflow check for sb->bblog_shift in super_1_load()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Coly Li <[email protected]>
commit 35d522bd32462afcf1981dab6da8a9256c26c1e0 upstream.
In super_1_load(), sb->bblog_shift is an __u8 type value loaded from on- disk superblock. It is used for badblocks API badblocks_set() by the following sequence,
1930 rdev->badblocks.shift = sb->bblog_shift;
1931 for (i = 0 ; i < (sectors << (9-3)) ; i++, bbp++) {
1932 u64 bb = le64_to_cpu(*bbp); 1933 int count = bb & (0x3ff); 1934 u64 sector = bb >> 10; 1935 sector <<= sb->bblog_shift; 1936 count <<= sb->bblog_shift; 1937 if (bb + 1 == 0)
1938 break;
1939 if (!badblocks_set(&rdev->badblocks, sector, count, 1))
1940 return -EINVAL;
1941 }
bb->bblog_shit is in range of 0-255, variable sector is 64bit width, for an invalid bb->bblog_shit, it is possible to make sector be overflowed by the following calculation,
1935 sector <<= sb->bblog_shift;
Then in turn when call badblocks_set() at line 1939 with the invalid rdev->badblocks.shift set at line 1930, may result an overflow inside _badblocks_clear() in block/badblocks.c.
Although there are many places to call badblocks APIs, the non-zero shift value is only used in super_1_load(), other places always use 0 as the shift value. Therefore it is unnecessary to do a general shift value overflow check inside badblock API, and just check here as the caller.
This may avoid unnecessary check, make the badblocks API code more simple and elegant.
--- drivers/md/md.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/md/md.c +7 -0
@@ -1726,6 +1726,13 @@ static int super_1_load(struct md_rdev * rdev->bb_page, REQ_OP_READ, true))return -EIO ; bbp = (__le64 *)page_address (rdev->bb_page); + +/* check for badblocks api. */ +if (sb->bblog_shift >=BITS_PER_TYPE (sector_t)) { +pr_err ("md: %pg: bogus bblog_shift %u for badblocks.\n" , + rdev->bdev, sb->bblog_shift); +return -EINVAL ; + } rdev->badblocks.shift = sb->bblog_shift;for (i =0 ; i < (sectors << (9 -3 )) ; i++, bbp++) {u64 bb =le64_to_cpu (*bbp);
Greg Kroah-Hartman
maintainerPATCH 146/1424 mpls: reload header after pskb_may_pull()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qing Ming <[email protected]>
commit 29e63b8d9fc150cc191b1c6eb7e16e1247e1b650 upstream.
mpls_select_multipath() calls mpls_multipath_hash() to choose a nexthop when an MPLS route has multiple nexthops. While walking the MPLS label stack, the hash routine caches hdr for the current label. After finding the bottom-of-stack label, it calls pskb_may_pull() before reading the inner IP header.
If an skb is constructed with the inner IP header in nonlinear data and insufficient tailroom in the linear head, pskb_may_pull() calls pskb_expand_head() to replace the skb head and free the old one. This leaves hdr pointing to freed memory. The IPv6 path can invalidate hdr again when it performs a second pull for the larger header.
The issue was found through static analysis. A reproducer sending a legal Geneve packet through a bareudp/MPLS multipath setup triggered the same KASAN report in 2 of 2 unpatched runs:
BUG: KASAN: slab-use-after-free in mpls_select_multipath Read of size 1 at addr ffff88800ecc6e20 by task ksoftirqd/1/23 Call Trace: mpls_select_multipath mpls_forward __netif_receive_skb_list_core netif_receive_skb_list_internal napi_complete_done gro_cell_poll __napi_poll net_rx_action Freed by task 23: kfree pskb_expand_head __pskb_pull_tail mpls_select_multipath
Reload hdr from the current skb head after each successful pull before deriving the inner IPv4 or IPv6 header pointer.
--- net/mpls/af_mpls.c | 2 ++ 1 file changed, 2 insertions(+)
net/mpls/af_mpls.c +2 -0
@@ -198,6 +198,7 @@ static u32 mpls_multipath_hash(struct mpif (pskb_may_pull (skb, mpls_hdr_len +sizeof (struct iphdr ))) {const struct iphdr *v4hdr; + hdr =mpls_hdr (skb) + label_index; v4hdr = (const struct iphdr *)(hdr +1 );if (v4hdr->version ==4 ) { hash = jhash_3words(ntohl(v4hdr->saddr),
@@ -208,6 +209,7 @@ static u32 mpls_multipath_hash(struct mp sizeof(struct ipv6hdr))) {const struct ipv6hdr *v6hdr; + hdr =mpls_hdr (skb) + label_index; v6hdr = (const struct ipv6hdr *)(hdr +1 ); hash =__ipv6_addr_jhash (&v6hdr->saddr, hash); hash =__ipv6_addr_jhash (&v6hdr->daddr, hash);
Greg Kroah-Hartman
maintainerPATCH 147/1424 mptcp: fix uninitialized local_id in syncookie MP_JOIN reconstruction
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harshit Varu <[email protected]>
commit b878dfdd12d7a5b8722a78d35e313506140ca3d9 upstream.
mptcp_token_join_cookie_init_state() restores remote_nonce, local_nonce, backup, join_id, token and msk from the saved cookie entry when rebuilding the request socket for a MP_JOIN 4th-ACK handled under SYN cookies, but it does not restore local_id, even though the SYN path saved it. subflow_ulp_clone() then reads that uninitialized field and stores it as the joined subflow's address-ID. Because the request-sock slab is SLAB_TYPESAFE_BY_RCU and not zeroed on allocation, the value is the stale byte of a previously freed request socket, which an off-path peer can influence by sending concurrent MP_JOIN SYNs. This corrupts the path manager's id-based subflow bookkeeping for the connection.
Restore subflow_req->local_id from the cookie entry, as done for the other fields.
--- net/mptcp/syncookies.c | 1 + 1 file changed, 1 insertion(+)
net/mptcp/syncookies.c +1 -0
@@ -118,6 +118,7 @@ bool mptcp_token_join_cookie_init_state( subflow_req->local_nonce = e->local_nonce; subflow_req->backup = e->backup; subflow_req->remote_id = e->join_id; + subflow_req->local_id = e->local_id; subflow_req->token = e->token; subflow_req->msk = msk;spin_unlock_bh (&join_entry_locks[i]);
Greg Kroah-Hartman
maintainerPATCH 148/1424 nouveau/gem: reserve the bo in the info ioctl around the vma lookup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Airlie <[email protected]>
commit 5e17160d41d92823f3379c1982e1369680c5ce4d upstream.
In the non-uvmm path, there could be a race between the info lookup finding the vma, and the gem close path closing the vma leading to a use-after-free.
Spotted with the help of Opus 4.6.
--- drivers/gpu/drm/nouveau/nouveau_gem.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nouveau_gem.c +10 -1
@@ -307,11 +307,20 @@ nouveau_gem_info(struct drm_file *file_p rep->offset = nvbo->offset;if (vmm->vmm.object.oclass >=NVIF_CLASS_VMM_NV50 && !nouveau_cli_uvmm (cli)) { +int ret; + + ret =ttm_bo_reserve (&nvbo->bo, false, false,NULL ); +if (ret) +return ret; + vma =nouveau_vma_find (nvbo, vmm); -if (!vma) +if (!vma) { +ttm_bo_unreserve (&nvbo->bo);return -EINVAL ; + } rep->offset = vma->addr; +ttm_bo_unreserve (&nvbo->bo); }else rep->offset =0 ;
Greg Kroah-Hartman
maintainerPATCH 149/1424 SUNRPC: xdr_buf_trim: clamp buf->len to avoid underflow
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 3f491306dcb673ff5e78e1044ba450c58978774e upstream.
xdr_buf_trim() trims len bytes from the tail of an xdr_buf by walking the tail, pages, and head iovecs. Each per-section step uses min_t() so it never removes more bytes than that section holds, but the final accounting at the fix_len label subtracts the total bytes actually consumed from buf->len without any clamp:
fix_len:
buf->len -= (len - trim);When the caller has set buf->len to a value smaller than the sum of the iov_lens, (len - trim) can exceed buf->len and the unsigned subtraction wraps to near UINT_MAX. gss_krb5_unwrap_v2() reaches xdr_buf_trim() in exactly that state:
buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);buf->len is a small wire-derived value while the iov_lens are at page scale, so the per-section loops legitimately consume far more bytes than buf->len records. The wrapped buf->len then propagates as the authoritative stream bound into every downstream XDR decoder.
Fix by clamping the decrement so buf->len bottoms out at zero:
buf->len -= min_t(unsigned int, buf->len, len - trim);
On the normal path where the iov_lens sum to buf->len, (len - trim) is always <= buf->len and the result is identical to before. No callers change behavior outside the underflow case.
--- net/sunrpc/xdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sunrpc/xdr.c +1 -1
@@ -2073,7 +2073,7 @@ void xdr_buf_trim(struct xdr_buf *buf, u trim -= cur; } fix_len: - buf->len -= (len - trim); + buf->len -= min_t(unsigned int, buf->len, len - trim); }EXPORT_SYMBOL_GPL (xdr_buf_trim);
Greg Kroah-Hartman
maintainerPATCH 150/1424 SUNRPC: Zero rpc_gss_wire_cred at svcauth_gss_decode_credbody() entry
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 11539e8fcce0b0af062ae5fecf7b3676c2f7aeed upstream.
svcauth_gss_decode_credbody() writes the caller's rpc_gss_wire_cred field by field and assigns gc_ctx.len only on the success tail. The caller storage is svcdata->clcred, which lives in the per-svc_rqst gss_svc_data and is reused across requests. Early decode failures leave partially decoded state mixed with residue from the prior request.
The trailing body_len tightness check is the sharpest case:xdr_stream_decode_opaque_inline() has already written gc_ctx.data with a borrowed inline pointer into the current request's XDR pages, but gc_ctx.len retains its prior value. Once the request pages are released the pooled clcred carries a dangling pointer paired with a stale length.
Zero the caller's rpc_gss_wire_cred at function entry so that every early-return path leaves a deterministic all-zero cred. On the trailing tightness-check path, gc_ctx.len is now zero instead of stale, which neuters length-driven consumers such as gss_svc_searchbyctx() that would otherwise walk the dangling data pointer.
--- net/sunrpc/auth_gss/svcauth_gss.c | 3 +++ 1 file changed, 3 insertions(+)
net/sunrpc/auth_gss/svcauth_gss.c +3 -0
@@ -1583,6 +1583,9 @@ svcauth_gss_decode_credbody(struct xdr_su32 body_len;__be32 *p; +/* Early-return paths leave deterministic state, not stale residue. */ +memset (gc,0 ,sizeof (*gc)); + p =xdr_inline_decode (xdr,XDR_UNIT );if (!p)return false;
Greg Kroah-Hartman
maintainerPATCH 151/1424 SUNRPC: svcauth_gss: enforce krb5 token minimum length
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit a919c5c88769cf8fb3ec071e6078d830bf512489 upstream.
svcauth_gss_unwrap_priv() validates only an upper bound on the wire-supplied opaque length before handing the buffer to gss_unwrap():
if (len > xdr_stream_remaining(xdr))
goto unwrap_failed;
offset = xdr_stream_pos(xdr);
...
maj_stat = gss_unwrap(ctx, offset, offset + len, buf);The wire value len flows unchanged as the upper bound into the krb5 unwrap path, so a len in [0, 16] passes this check and is handed to gss_unwrap(). For a krb5 v2 context that lands in gss_krb5_unwrap_v2(), which reads the 16-byte RFC 4121 token header fields at ptr+4 and ptr+6 and then calls rotate_left() before any integrity check. With a sub-header length the header reads run past the token, and _rotate_left()'s shift %= buf->len path can divide by zero when buf->len has been driven to zero by the truncated token. A header-only token (len == 16) is equally
invalid: with a non-zero RRC field and the opaque blob ending at the XDR buffer boundary, rotate_left() builds a zero-length subbuffer, reaching the same division.
Reject the token at the server entry point before it reaches the krb5 unwrap core. A valid sealed RFC 4121 token must contain the 16-byte header plus at least some encrypted payload.
Fix by adding a minimum-length check immediately after the existing upper-bound check:
if (len <= GSS_KRB5_TOK_HDR_LEN)
goto unwrap_failed;--- net/sunrpc/auth_gss/svcauth_gss.c | 2 ++ 1 file changed, 2 insertions(+)
net/sunrpc/auth_gss/svcauth_gss.c +2 -0
@@ -959,6 +959,8 @@ svcauth_gss_unwrap_priv(struct svc_rqst }if (len >xdr_stream_remaining (xdr)) goto unwrap_failed; +if (len <=GSS_KRB5_TOK_HDR_LEN ) + goto unwrap_failed; offset =xdr_stream_pos (xdr); saved_len = buf->len;
Greg Kroah-Hartman
maintainerPATCH 152/1424 sunrpc: route to a populated pool in svc_pool_for_cpu()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
commit f6310491c4cdb88af73aa551ec9df1f10a90c709 upstream.
svc_set_num_threads() spreads the requested threads evenly across the service's pools (base = nrservs / sv_nrpools). When a service runs fewer threads than it has pools -- e.g. an nfsd configured with fewer threads than the host has NUMA nodes while running in "pernode" or "percpu" mode -- the trailing pools are left with no threads at all.
svc_xprt_enqueue() selects a pool from the CPU servicing the transport, queues the transport on that pool's sp_xprts, and only wakes a thread from the same pool. Each thread services exclusively its own pool, so a transport that lands on a threadless pool is enqueued on sp_xprts and never picked up: the connection hangs indefinitely.
Have svc_pool_for_cpu() skip pools that currently have no threads, falling back to the next populated pool. This trades NUMA locality for a guarantee that the work is actually serviced. sp_nrthreads is only updated under the service mutex; the lockless read here is a best-effort routing hint, so annotate it with data_race().
--- net/sunrpc/svc.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-)
net/sunrpc/svc.c +28 -1
@@ -372,6 +372,7 @@ struct svc_pool *svc_pool_for_cpu(structstruct svc_pool_map *m = &svc_pool_map;int cpu =raw_smp_processor_id ();unsigned int pidx =0 ; +unsigned int i;if (serv->sv_nrpools <=1 )return serv->sv_pools;
@@ -384,8 +385,34 @@ struct svc_pool *svc_pool_for_cpu(struct pidx = m->to_pool[cpu_to_node (cpu)];break ; } + pidx %= serv->sv_nrpools; -return &serv->sv_pools[pidx % serv->sv_nrpools]; +/* +* It's possible to have a pool with no threads. Userland can just set +* things up this way directly. Also, when threads are autodistributed +* they are spread evenly across the pools, but when there are fewer +* threads than pools some pools can end up with none. +* +* A transport enqueued on a threadless pool would never be picked up, +* since each thread only services its own pool. Fall back to the next +* populated pool, trading NUMA locality for a guarantee that the +* transport is serviced. +*/ +for (i =0 ; i < serv->sv_nrpools; i++) { +struct svc_pool *pool = &serv->sv_pools[pidx]; + +/* This is set under the service mutex and rarely ever +* changes. A data race here is harmless. +*/ +if (data_race (pool->sp_nrthreads)) +return pool; + +if (++pidx >= serv->sv_nrpools) + pidx =0 ; + } + +/* No pool has any threads; nothing can service the transport. */ +return &serv->sv_pools[pidx]; }int svc_rpcb_setup (struct svc_serv *serv,struct net *net)
Greg Kroah-Hartman
maintainerPATCH 153/1424 SUNRPC: always drain cache_cleaner before destroying a cache_detail
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <[email protected]>
commit f42d0fda0c67695db6bc704b04b7c10240805377 upstream.
sunrpc_destroy_cache_detail() only cancels the global cache_cleaner delayed_work when cache_list is empty. During per-netns teardown cache_list is never empty because init_net's caches remain registered, so the cancel never fires. After unlink, the caller proceeds to cache_destroy_net() which kfrees the cache_detail while cache_clean() may still hold a dangling pointer to it. The result is a
use-after-free: cache_dequeue() takes cd->queue_lock on freed memory, and cache_put() dereferences cd->cache_put as a function pointer from freed slab.
Drop the list_empty guard so that cancel_delayed_work_sync() always runs, ensuring any in-flight cache_clean() completes before the cache_detail is freed. Re-arm the cleaner afterwards if other caches are still registered.
--- net/sunrpc/cache.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
net/sunrpc/cache.c +3 -4
@@ -410,10 +410,9 @@ void sunrpc_destroy_cache_detail(structlist_del_init (&cd->others);spin_unlock (&cd->hash_lock);spin_unlock (&cache_list_lock); -if (list_empty (&cache_list)) { -/* module must be being unloaded so its safe to kill the worker */ -cancel_delayed_work_sync (&cache_cleaner); - } +cancel_delayed_work_sync (&cache_cleaner); +if (!list_empty (&cache_list)) +queue_delayed_work (system_power_efficient_wq, &cache_cleaner,0 ); }EXPORT_SYMBOL_GPL (sunrpc_destroy_cache_detail);
Greg Kroah-Hartman
maintainerPATCH 154/1424 SUNRPC: Check svc pool percpu counter allocation
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit 43e11e164704dde975c9edb370de1a06bec67270 upstream.
__svc_create() initializes three per-pool percpu_counter stats and ignores every return value. On SMP, percpu_counter_init() fails when __alloc_percpu_gfp() cannot satisfy the allocation, leaving the failed counter with fbc->counters == NULL and its embedded raw_spinlock_t, list_head, and count never initialized. __svc_create() returns the half-constructed svc_serv to nfsd, lockd, or the NFS callback service anyway.
Once that service is live, the hot-path increments in svc_xprt_enqueue(), svc_handle_xprt(), and svc_pool_wake_idle_thread() reach a counter whose backing pointer is NULL. The pointer is a per-cpu offset, so the access does not fault:
it resolves to offset zero of the current CPU's per-cpu area and silently corrupts whatever variable lives there. A /proc/fs/nfsd/pool_stats read walks the same NULL per-cpu storage and returns garbage, and on CONFIG_DEBUG_SPINLOCK or lockdep it splats on the never-initialized lock.
Creating the broken service requires a percpu allocation failure during RPC server startup, so it is reachable only by a local administrator under memory pressure or fault injection; a remote peer cannot induce the bad state on its own.
Check each percpu_counter_init() return value in __svc_create() and fail when an allocation fails, unwinding the counters already set up in the current pool and in every pool initialized before it. A discrete percpu_counter_destroy() per counter at teardown frees each per-cpu allocation exactly once.
--- net/sunrpc/svc.c | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-)
net/sunrpc/svc.c +39 -6
@@ -476,6 +476,35 @@ __svc_init_bc(struct svc_serv *serv) }#endif +static int svc_pool_init_counters (struct svc_pool *pool) +{ +int err; + + err =percpu_counter_init (&pool->sp_messages_arrived,0 ,GFP_KERNEL ); +if (err) +return err; + err =percpu_counter_init (&pool->sp_sockets_queued,0 ,GFP_KERNEL ); +if (err) + goto err_sockets; + err =percpu_counter_init (&pool->sp_threads_woken,0 ,GFP_KERNEL ); +if (err) + goto err_threads; +return 0 ; + +err_threads: +percpu_counter_destroy (&pool->sp_sockets_queued); +err_sockets: +percpu_counter_destroy (&pool->sp_messages_arrived); +return err; +} + +static void svc_pool_destroy_counters (struct svc_pool *pool) +{ +percpu_counter_destroy (&pool->sp_messages_arrived); +percpu_counter_destroy (&pool->sp_sockets_queued); +percpu_counter_destroy (&pool->sp_threads_woken); +} +/* * Create an RPC service */
@@ -540,12 +569,18 @@ __svc_create(struct svc_program *prog, sINIT_LIST_HEAD (&pool->sp_all_threads);spin_lock_init (&pool->sp_lock); -percpu_counter_init (&pool->sp_messages_arrived,0 ,GFP_KERNEL ); -percpu_counter_init (&pool->sp_sockets_queued,0 ,GFP_KERNEL ); -percpu_counter_init (&pool->sp_threads_woken,0 ,GFP_KERNEL ); +if (svc_pool_init_counters (pool)) + goto out_err; }return serv; + +out_err: +while (i--) +svc_pool_destroy_counters (&serv->sv_pools[i]); +kfree (serv->sv_pools); +kfree (serv); +return NULL ; }/**
@@ -618,9 +653,7 @@ svc_destroy(struct kref *ref)for (i =0 ; i < serv->sv_nrpools; i++) {struct svc_pool *pool = &serv->sv_pools[i]; -percpu_counter_destroy (&pool->sp_messages_arrived); -percpu_counter_destroy (&pool->sp_sockets_queued); -percpu_counter_destroy (&pool->sp_threads_woken); +svc_pool_destroy_counters (pool); }kfree (serv->sv_pools);kfree (serv);
Greg Kroah-Hartman
maintainerPATCH 155/1424 SUNRPC: Guard svcauth_gss_release() dispatch on rq_auth_stat
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit f8870b9b75afb77986bc65940a231d54068ff2b1 upstream.
svcauth_gss_release() reads gc_proc and switches on gc_svc before consulting rq_auth_stat. On the SVC_DENIED path after a failed svcauth_gss_accept(), those fields may hold stale values from a prior request or uninitialized slab residue: svcauth_gss_accept() allocates gss_svc_data with non-zeroing kmalloc and clears only gsd_databody_offset and rsci per request, not clcred.
Because RPC_GSS_PROC_DATA is zero, a zeroed or stale-zero gc_proc passes the existing guard and falls through into the gc_svc switch, which can dispatch to svcauth_gss_wrap_integ() or svcauth_gss_wrap_priv(). Both wrap helpers call svcauth_gss_prepare_to_wrap() before any rsci->mechctx dereference, and that helper already returns early when rq_auth_stat is not rpc_auth_ok, so the downstream NULL dereference is blocked. The dispatch itself remains structurally wrong: it reads scalars that the caller has no contract to have initialized after a failed authentication.
Mirror the existing rq_auth_stat gate in svcauth_gss_prepare_to_wrap() one frame up, so svcauth_gss_release() skips the clcred dispatch entirely when authentication has not succeeded. The cleanup tail that releases rq_client, rq_gssclient, cr_group_info, and rsci still runs.
--- net/sunrpc/auth_gss/svcauth_gss.c | 2 ++ 1 file changed, 2 insertions(+)
net/sunrpc/auth_gss/svcauth_gss.c +2 -0
@@ -1956,6 +1956,8 @@ svcauth_gss_release(struct svc_rqst *rqsif (!gsd) goto out; +if (rqstp->rq_auth_stat != rpc_auth_ok) + goto out; gc = &gsd->clcred;if (gc->gc_proc !=RPC_GSS_PROC_DATA ) goto out;
Greg Kroah-Hartman
maintainerPATCH 156/1424 SUNRPC: harden gss_krb5_unwrap_v2 against short tokens
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 6959297aaa9572783d620a226d73c3fb94494888 upstream.
gss_krb5_unwrap_v2() reads the EC and RRC header fields at ptr+4 and ptr+6 before validating that the token is at least GSS_KRB5_TOK_HDR_LEN
(16) bytes long, and its rotate_left() helper passes buf->len - base to xdr_buf_subsegment() without verifying that base <= buf->len. When a caller hands in a sub-16-byte token, or a token whose declared len leaves base past the end of the buffer, three distinct failures follow:
gss_krb5_unwrap_v2(offset, len, buf)
ptr = buf->head[0].iov_base + offset
ec = *(ptr + 4) /* OOB read on short head */
rrc = *(ptr + 6) /* OOB read on short head */
rotate_left(offset + 16, buf, rrc)
xdr_buf_subsegment(buf, &subbuf,
base, buf->len - base) /* u32 wrap when base > len */
_rotate_left(&subbuf, shift)
shift %= buf->len /* divide-by-zero when base == len */
After decryption, the cleanup arithmetic has the same shape:
movelen = min_t(unsigned int, buf->head[0].iov_len, len);
movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen >
buf->head[0].iov_len);The BUG_ON re-adds the value just subtracted, so it reduces to min(A, B) > A and is permanently false; it cannot catch the unsigned underflow of movelen, which then drives a ~UINT_MAX-byte memmove().
Add four defense-in-depth guards inside the unwrap core so it is safe regardless of what its callers validate:
- reject tokens with len - offset < GSS_KRB5_TOK_HDR_LEN before
touching ptr+4/ptr+6;
- bail from rotate_left() when buf->len <= base, covering both the
underflow and zero-length cases;
- return early from _rotate_left() when buf->len is zero, so the
shift %= buf->len modulo cannot fault; - replace the dead BUG_ON with a live check that returns GSS_S_DEFECTIVE_TOKEN before the movelen subtraction.
--- net/sunrpc/auth_gss/gss_krb5_wrap.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
net/sunrpc/auth_gss/gss_krb5_wrap.c +9 -2
@@ -74,6 +74,8 @@ static void _rotate_left(struct xdr_bufint shifted =0 ;int this_shift; +if (!buf->len) +return ; shift %= buf->len;while (shifted < shift) { this_shift =min (shift - shifted,LOCAL_BUF_LEN );
@@ -86,6 +88,8 @@ static void rotate_left(u32 base, struct {struct xdr_buf subbuf; +if (buf->len <= base) +return ;xdr_buf_subsegment (buf, &subbuf, base, buf->len - base);_rotate_left (&subbuf, shift); }
@@ -155,6 +159,9 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctxdprintk ("RPC: %s\n" , __func__); +if (len - offset <=GSS_KRB5_TOK_HDR_LEN ) +return GSS_S_DEFECTIVE_TOKEN ; + ptr = buf->head[0 ].iov_base + offset;if (be16_to_cpu (*((__be16 *)ptr)) !=KG2_TOK_WRAP )
@@ -221,9 +228,9 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx* head buffer space rather than that actually occupied. */ movelen = min_t(unsigned int, buf->head[0].iov_len, len); +if (movelen < offset +GSS_KRB5_TOK_HDR_LEN + headskip) +return GSS_S_DEFECTIVE_TOKEN ; movelen -= offset +GSS_KRB5_TOK_HDR_LEN + headskip; -BUG_ON (offset +GSS_KRB5_TOK_HDR_LEN + headskip + movelen > - buf->head[0 ].iov_len);memmove (ptr, ptr +GSS_KRB5_TOK_HDR_LEN + headskip, movelen); buf->head[0 ].iov_len -=GSS_KRB5_TOK_HDR_LEN + headskip; buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
Greg Kroah-Hartman
maintainerPATCH 157/1424 SUNRPC: harden gss_unwrap_resp_priv length checks
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 87831b92112c81db251d46756d65daa4f91af6a2 upstream.
gss_unwrap_resp_priv() validates the RPCSEC_GSS opaque length with
offset = (u8 *)(p) - (u8 *)head->iov_base;
if (offset + opaque_len > rcv_buf->len)
goto unwrap_failed;
maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset,
offset + opaque_len, rcv_buf);Both operands are u32 and the sum is computed in u32. A reply with opaque_len near 0xffffffff makes offset + opaque_len wrap to a small value that is below rcv_buf->len, so the bound check passes and gss_unwrap() is called with end < begin. The check also lacks a lower bound, so any opaque_len in [0, GSS_KRB5_TOK_HDR_LEN) is accepted and forwarded to gss_krb5_unwrap_v2(), whose pre-decrypt header reads at ptr+4 and ptr+6 then run past the token.
A krb5p NFS server returning a crafted RPCSEC_GSS reply can drive the client into out-of-bounds reads in gss_krb5_unwrap_v2() and the rotate_left() loop that follows.
Fix by replacing the single combined check with three guards that are safe in u32 arithmetic and that enforce the RFC 4121 minimum outer token length:
if (offset > rcv_buf->len)
goto unwrap_failed;
if (opaque_len > rcv_buf->len - offset)
goto unwrap_failed;
if (opaque_len < GSS_KRB5_TOK_HDR_LEN)
goto unwrap_failed;The first guard makes the subtraction in the second guard unconditionally safe; offset is derived from a successful xdr_inline_decode() in the head kvec, so in practice it already satisfies the bound. The floor mirrors the server-side check added in commit 5b757c2e57a5 ("SUNRPC: svcauth_gss: enforce krb5 token minimum length").
--- net/sunrpc/auth_gss/auth_gss.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
net/sunrpc/auth_gss/auth_gss.c +5 -1
@@ -2070,7 +2070,11 @@ gss_unwrap_resp_priv(struct rpc_task *ta goto unwrap_failed; opaque_len =be32_to_cpup (p++); offset = (u8 *)(p) - (u8 *)head->iov_base; -if (offset + opaque_len > rcv_buf->len) +if (offset > rcv_buf->len) + goto unwrap_failed; +if (opaque_len > rcv_buf->len - offset) + goto unwrap_failed; +if (opaque_len <=GSS_KRB5_TOK_HDR_LEN ) goto unwrap_failed; maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset,
Greg Kroah-Hartman
maintainerPATCH 158/1424 sunrpc: init gssp_lock before publishing proc entry
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 5ce1ed6159731a41fdd0b03eedbed4e147036a5a upstream.
create_use_gss_proxy_proc_entry() publishes /proc/net/rpc/use-gss-proxy via proc_create_data() before init_gssp_clnt() runs mutex_init() on sn->gssp_lock. Once the dentry is linked under proc_subdir_lock it is immediately reachable from userspace, so a write that lands in the window drives set_gssp_clnt() into mutex_lock() on a zero-initialized struct mutex.
create_use_gss_proxy_proc_entry(net)
proc_create_data("use-gss-proxy", ...) /* dentry live */
init_gssp_clnt(sn)
mutex_init(&sn->gssp_lock) /* too late */
write_gssp()
set_gssp_clnt(net)
mutex_lock(&sn->gssp_lock) /* uninitialized */
gssp_rpc_create(...)
sn->gssp_clnt = clnt
mutex_unlock(&sn->gssp_lock)The window spans only the two statements between proc_create_data() returning and init_gssp_clnt(), so a writer reaches it only if the registering thread is preempted there while another task is already opening the freshly published file. register_pernet_subsys() runs in preemptible context under pernet_ops_rwsem, so that preemption is possible, and the window widens on auth_rpcgss module load, when the proc entry is created for every live net namespace whose tasks are already running. A writer that wins the race locks a zero-filled struct mutex. On CONFIG_DEBUG_MUTEXES the missing magic value trips a "lock used without init" splat; on a production kernel the fast path acquires the lock via CMPXCHG(owner, 0, current). In the latter case a second writer that arrives before init_gssp_clnt() re-zeroes owner can enter set_gssp_clnt() concurrently, shut down the first writer's clnt while it is still in use, and leak the loser's clnt.
Fix by initializing sn->gssp_lock in sunrpc_init_net() so its lifetime matches the sunrpc_net it lives in. sn->gssp_clnt is already NULL from the kzalloc that backs net_generic storage, so the lazy helper is no longer needed; drop init_gssp_clnt(), its prototype, and the call from create_use_gss_proxy_proc_entry(). sunrpc.ko is a build-time dependency of auth_rpcgss.ko, so sunrpc_init_net() has always run on every netns before any auth_gss pernet init can publish the proc entry.
--- net/sunrpc/auth_gss/gss_rpc_upcall.c | 6 ------ net/sunrpc/auth_gss/gss_rpc_upcall.h | 1 - net/sunrpc/auth_gss/svcauth_gss.c | 1 - net/sunrpc/sunrpc_syms.c | 1 + 4 files changed, 1 insertion(+), 8 deletions(-)
net/sunrpc/auth_gss/gss_rpc_upcall.c +0 -6
@@ -121,12 +121,6 @@ out:return result; } -void init_gssp_clnt (struct sunrpc_net *sn) -{ -mutex_init (&sn->gssp_lock); - sn->gssp_clnt =NULL ; -} -int set_gssp_clnt (struct net *net) {struct sunrpc_net *sn =net_generic (net, sunrpc_net_id);
net/sunrpc/auth_gss/gss_rpc_upcall.h +0 -1
@@ -29,7 +29,6 @@ int gssp_accept_sec_context_upcall(struc struct gssp_upcall_data *data);void gssp_free_upcall_data (struct gssp_upcall_data *data); -void init_gssp_clnt (struct sunrpc_net *);int set_gssp_clnt (struct net *);void clear_gssp_clnt (struct sunrpc_net *);
net/sunrpc/auth_gss/svcauth_gss.c +0 -1
@@ -1478,7 +1478,6 @@ static int create_use_gss_proxy_proc_ent &use_gss_proxy_proc_ops, net);if (!*p)return -ENOMEM ; -init_gssp_clnt (sn);return 0 ; }
net/sunrpc/sunrpc_syms.c +1 -0
@@ -54,6 +54,7 @@ static __net_init int sunrpc_init_net(stINIT_LIST_HEAD (&sn->all_clients);spin_lock_init (&sn->rpc_client_lock);spin_lock_init (&sn->rpcb_clnt_lock); +mutex_init (&sn->gssp_lock);return 0 ; err_pipefs:
Greg Kroah-Hartman
maintainerPATCH 159/1424 SUNRPC: Reject krb5 v2 wrap tokens with oversized ec field
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit ad484748eec0a66eac0f13ab53b3fbedb7333c91 upstream.
gss_krb5_unwrap_v2() sets buf->len to a logical length, which can be much smaller than head[0].iov_len (the allocated receive-page capacity). It then calls xdr_buf_trim() with a trim length derived from the 16-bit "extra count" (ec) field in the Kerberos v2 token header.
The ec field is authenticated by the post-decrypt memcmp() against the encrypted header copy, so a randomly-mutated value is rejected. However, any peer holding a valid GSS context can legitimately encrypt a token whose ec exceeds the plaintext length. Per RFC 4121, such a token is structurally malformed.
Although xdr_buf_trim() now clamps the buf->len subtraction to avoid unsigned underflow, the buffer is still left in a semantically invalid state (zero length, inconsistent iov lengths) when ec is oversized.
Reject these tokens before calling xdr_buf_trim(), giving callers a well-defined GSS_S_DEFECTIVE_TOKEN error and keeping the xdr_buf internally consistent. The wrapped blob begins at a nonzero offset -- both callers pass len as offset + opaque_len -- so buf->len still counts the offset bytes that precede the blob. Compare the trim length against the remaining wrapped segment, buf->len - offset, rather than the whole buffer; comparing against buf->len alone leaves an offset-wide window in which an oversized ec passes the test and xdr_buf_trim() cuts into the bytes ahead of the blob.
--- net/sunrpc/auth_gss/gss_krb5_wrap.c | 2 ++ 1 file changed, 2 insertions(+)
net/sunrpc/auth_gss/gss_krb5_wrap.c +2 -0
@@ -236,6 +236,8 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);/* Trim off the trailing "extra count" and checksum blob */ +if (ec +GSS_KRB5_TOK_HDR_LEN + tailskip > buf->len - offset) +return GSS_S_DEFECTIVE_TOKEN ;xdr_buf_trim (buf, ec +GSS_KRB5_TOK_HDR_LEN + tailskip); *align =XDR_QUADLEN (GSS_KRB5_TOK_HDR_LEN + headskip);
Greg Kroah-Hartman
maintainerPATCH 160/1424 SUNRPC: wait for in-flight client TLS handshake callback
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jérémy Jean <[email protected]>
commit a89dd597458848b463d284b15e42a8078beeb046 upstream.
xs_tls_handshake_sync() gives xs_tls_handshake_done() a reference to the lower transport before submitting the handshake request. On timeout or signal, the synchronous waiter drops that reference after calling tls_handshake_cancel().
handshake_req_cancel() returns false when handshake_complete() has already marked the request complete. In that case the completion callback can still be running, so dropping the callback-owned reference in the waiter can free the lower transport before xs_tls_handshake_done() stores xprt_err or drops its own reference.
If cancellation loses to completion, wait until xs_tls_handshake_done() signals handshake_done and let the callback release its reference. This mirrors the server-side handshake lifetime handling and keeps the timeout or signal return value unchanged.
--- net/sunrpc/xprtsock.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
net/sunrpc/xprtsock.c +11 -1
@@ -2627,7 +2627,17 @@ static int xs_tls_handshake_sync(struct rc =wait_for_completion_interruptible_timeout (&lower_transport->handshake_done,XS_TLS_HANDSHAKE_TO );if (rc <=0 ) { -tls_handshake_cancel (sk); +if (!tls_handshake_cancel (sk)) { +/* +* Cancellation lost to handshake_complete(): the +* callback still owns its xprt reference and is in +* flight. Wait for it to finish before returning. +*/ +wait_for_completion (&lower_transport->handshake_done); +if (rc ==0 ) + rc = -ETIMEDOUT ; + goto out; + }if (rc ==0 ) rc = -ETIMEDOUT ; goto out_put_xprt;
Greg Kroah-Hartman
maintainerPATCH 161/1424 svcrdma: Fix offset arithmetic in read_chunk_range
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 4a44c140cc2f3643a39e258bb0c0ab9d0f494f5e upstream.
svc_rdma_read_chunk_range() walks a Read chunk's segment list to build a sub-range starting at byte offset and spanning length bytes for a Position-Zero or Call chunk. Two arithmetic defects in the per-segment loop produce wrong DMA lengths and a u32 underflow:
pcl_for_each_segment(segment, chunk) {
if (offset > segment->rs_length) {
offset -= segment->rs_length;
continue;
}
dummy.rs_handle = segment->rs_handle;
dummy.rs_length = min_t(u32, length,
segment->rs_length) - offset;
dummy.rs_offset = segment->rs_offset + offset;First, the skip predicate uses '>' instead of '>='. When offset equals the segment's full rs_length, the segment is fully consumed and should be skipped, but the loop falls through into the body. The resulting dummy.rs_length is min_t(u32, length, rs_length) - rs_length, which underflows to a near-UINT_MAX u32 when length is smaller than rs_length, or is zero otherwise.
Second, the length formula subtracts offset from the min_t() result rather than from segment->rs_length before the cap. For offset > 0 the segment's residual is rs_length - offset, not rs_length, so the cap must be applied to the residual. With the current bracketing, whenever length is smaller than rs_length - offset the per-segment length becomes length - offset instead of length, silently dropping offset bytes from the rebuilt chunk. Combined with the boundary case above it also enables the u32 underflow path, which propagates a huge nr_bvec into svc_rdma_build_read_segment() and a multi-MiB kmalloc_array_node() in svc_rdma_get_rw_ctxt().
Additionally, svc_rdma_read_call_chunk() can invoke this function with length == 0 when the last Read chunk ends exactly at the end of the Call chunk. With the corrected >= predicate, every segment is skipped and the function returns the initial -EINVAL, rejecting a valid request. Return success immediately when length is zero. Also break out of the loop once length is fully consumed to avoid passing zero-length segments to svc_rdma_build_read_segment().
Fix by using '>=' so a fully-consumed segment is skipped, by moving '- offset' inside min_t() so the cap is applied to the segment's residual length, by returning success for zero-length requests, and by stopping iteration when the requested range has been consumed.
--- net/sunrpc/xprtrdma/svc_rdma_rw.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_rw.c +7 -2
@@ -981,17 +981,20 @@ static int svc_rdma_read_chunk_range(strconst struct svc_rdma_segment *segment;int ret; +if (!length) +return 0 ; + ret = -EINVAL ;pcl_for_each_segment (segment, chunk) {struct svc_rdma_segment dummy; -if (offset > segment->rs_length) { +if (offset >= segment->rs_length) { offset -= segment->rs_length;continue ; } dummy.rs_handle = segment->rs_handle; - dummy.rs_length =min_t (u32, length, segment->rs_length) - offset; + dummy.rs_length =min_t (u32, length, segment->rs_length - offset); dummy.rs_offset = segment->rs_offset + offset; ret =svc_rdma_build_read_segment (info, &dummy);
@@ -1000,6 +1003,8 @@ static int svc_rdma_read_chunk_range(str info->ri_totalbytes += dummy.rs_length; length -= dummy.rs_length; +if (!length) +break ; offset =0 ; }return ret;
Greg Kroah-Hartman
maintainerPATCH 162/1424 svcrdma: Fix pcl_for_each_segment for empty chunks
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit b7713a784c59515d0aba558c8f5df6a0164dd3a9 upstream.
When a parsed chunk list contains a chunk whose ch_segcount is zero, pcl_for_each_segment computes its inclusive upper bound as &chunk->ch_segments[ch_segcount - 1]. ch_segcount is u32, so the subtraction wraps to 0xFFFFFFFF and the bound lands far past the ch_segments flex array. The loop body then walks unrelated memory at sizeof(struct svc_rdma_segment) stride until it faults.
A zero-segcount chunk is reachable from the wire:xdr_check_write_chunk() only rejects segcount values greater than rc_maxpages, and pcl_alloc_write() links a freshly allocated chunk onto rc_write_pcl/rc_reply_pcl before its segment-fill loop runs, so a Write or Reply chunk advertising zero segments leaves ch_segcount == 0 on the list. When the transport has negotiated Send-With-Invalidate, svc_rdma_get_inv_rkey() iterates all four PCLs with pcl_for_each_segment and dereferences segment->rs_handle on each iteration, turning the underflow into an out-of-bounds read and a general protection fault.
xdr_check_write_list / xdr_check_reply_chunk
pcl_alloc_write()
chunk = pcl_alloc_chunk(...) /* ch_segcount = 0 */
list_add_tail(&chunk->ch_list, &pcl->cl_chunks)
/* fill loop iterates zero times for wire segcount 0 */
svc_rdma_get_inv_rkey()
pcl_for_each_chunk(rc_write_pcl)
pcl_for_each_segment(segment, chunk)
pos <= &ch_segments[0u - 1u] /* 0xFFFFFFFF */
segment->rs_handle /* OOB read -> GPF */Fix by switching the macro to a half-open upper bound that uses ch_segcount directly. For ch_segcount == 0 the loop start equals the loop end and the body is skipped; for ch_segcount > 0 the iteration range is unchanged. All six existing call sites in net/sunrpc/xprtrdma/svc_rdma_recvfrom.c and net/sunrpc/xprtrdma/svc_rdma_rw.c remain correct under the new bound, so no caller changes are needed.
--- include/linux/sunrpc/svc_rdma_pcl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/sunrpc/svc_rdma_pcl.h +1 -1
@@ -97,7 +97,7 @@ pcl_next_chunk(const struct svc_rdma_pcl*/ #define pcl_for_each_segment (pos, chunk) \ for (pos = &(chunk)->ch_segments[0]; \ - pos <= &(chunk)->ch_segments[(chunk)->ch_segcount - 1]; \ + pos < &(chunk)->ch_segments[(chunk)->ch_segcount]; \ pos++)/**
Greg Kroah-Hartman
maintainerPATCH 163/1424 udf: reject VAT indexes equal to the entry count
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Lee <[email protected]>
commit cac0cb07f29ccfb373fd4a36c81e908ef3ce608c upstream.
UDF 1.50 virtual partition mapping uses the VAT as an array of physical block mappings. s_num_entries stores the number of entries in that array, not the highest valid index. The valid VAT indexes are therefore below s_num_entries.
udf_get_pblock_virt15() currently rejects only indexes greater than s_num_entries. A crafted image can request index s_num_entries, pass the bounds check, and make the kernel read one entry past the allocated VAT table.
Change the check to reject block >= s_num_entries, so the count is handled as an exclusive upper bound.
A crafted UDF image reproduced this on origin/master commit 0e35b9b6ec0ffcc5e23cbdec09f5c622ad532b53 with a KASAN slab-out-of-bounds report in udf_get_pblock_virt15().
Trail of Bits has a reproducer that triggers kernel panic demonstrating the bug, and can share it if needed.
--- fs/udf/partition.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/udf/partition.c +1 -1
@@ -55,7 +55,7 @@ uint32_t udf_get_pblock_virt15(struct su map = &sbi->s_partmaps[partition]; vdata = &map->s_type_specific.s_virtual; -if (block > vdata->s_num_entries) { +if (block >= vdata->s_num_entries) {udf_debug ("Trying to access block beyond end of VAT (%u max %u)\n" , block, vdata->s_num_entries);return 0xFFFFFFFF ;
Greg Kroah-Hartman
maintainerPATCH 164/1424 wifi: ath6kl: clamp assoc request/response lengths before subtracting IE offsets
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 3bbd05723d15dd06f0560bcd94fbf9a91b5f5613 upstream.
ath6kl_cfg80211_connect_event() subtracts fixed IE offsets from assoc_req_len (-= 4) and assoc_resp_len (-= 6), both u8, with no lower bound. The aggregate check recently added to ath6kl_wmi_connect_event_rx() bounds the declared lengths from above (their sum must fit the received event), but an assoc request/response shorter than its fixed offset still underflows here: the u8 wraps to ~250, and cfg80211_connect_result() / cfg80211_roamed() then treat that wrapped value as the IE length and copy that many bytes out of the small assoc_info buffer to user space via nl80211, disclosing adjacent slab memory.
Clamp both lengths to their offsets before subtracting.
Found by 0sec (https://0sec.ai) using automated source analysis; the missing lower bound is evident from source. Compile-tested.
--- drivers/net/wireless/ath/ath6kl/cfg80211.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/net/wireless/ath/ath6kl/cfg80211.c +5 -0
@@ -753,6 +753,11 @@ void ath6kl_cfg80211_connect_event(strucu8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len + assoc_resp_ie_offset; +if (assoc_req_len < assoc_req_ie_offset) + assoc_req_len = assoc_req_ie_offset; +if (assoc_resp_len < assoc_resp_ie_offset) + assoc_resp_len = assoc_resp_ie_offset; + assoc_req_len -= assoc_req_ie_offset; assoc_resp_len -= assoc_resp_ie_offset;
Greg Kroah-Hartman
maintainerPATCH 165/1424 staging: media: tegra-video: fix of_node_put() on VIP parse errors
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hao-Qun Huang <[email protected]>
commit 7393372f79db940acff206b43e2905685a0c57ad upstream.
tegra_vip_channel_of_parse() initializes np from dev->of_node without taking a reference, but its error paths drop one through the err_node_put label. This underflows the refcount of the VIP device's OF node when endpoint parsing fails on a malformed device tree.
The only reference the function takes on np is the success-path of_node_get() stored in vip->chan.of_node, and that one is already released by the tegra_vip_init() error path and by tegra_vip_exit().
Return errors directly instead of jumping to the bogus cleanup label.
--- drivers/staging/media/tegra-video/vip.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
drivers/staging/media/tegra-video/vip.c +3 -7
@@ -126,7 +126,7 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip)if (!ep) { err = -EINVAL ;dev_err_probe (dev, err,"%pOF: error getting endpoint node\n" , np); - goto err_node_put; +return err; } fwh =of_fwnode_handle (ep);
@@ -134,14 +134,14 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip)of_node_put (ep);if (err) {dev_err_probe (dev, err,"%pOF: failed to parse v4l2 endpoint\n" , np); - goto err_node_put; +return err; } num_pads =of_graph_get_endpoint_count (np);if (num_pads !=TEGRA_VIP_PADS_NUM ) { err = -EINVAL ;dev_err_probe (dev, err,"%pOF: need 2 pads, got %d\n" , np, num_pads); - goto err_node_put; +return err; } vip->chan.of_node =of_node_get (np);
@@ -149,10 +149,6 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip) vip->chan.pads[TEGRA_VIP_PAD_SOURCE ].flags =MEDIA_PAD_FL_SOURCE ;return 0 ; - -err_node_put: -of_node_put (np); -return err; }static int tegra_vip_channel_init (struct tegra_vip *vip)
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 166/1424 staging: media: tegra-video: vi: fix probe failure on skipped last port
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hao-Qun Huang <[email protected]>
commit ae15adeed9f7ec54989175fe3c9e0815186821bc upstream.
tegra_vi_channels_alloc() iterates over port nodes and skips those whose reg property cannot be read or whose remote endpoint fails v4l2_fwnode_endpoint_parse(), leaving the negative result of the failed call in ret. If that happens on the last port node, the loop ends with ret still negative and tegra_vi_init() fails the whole VI probe.
The same defective port earlier in the ports node is skipped silently, so probing succeeds or fails depending on the order of the port nodes. The CSI equivalent, tegra_csi_channels_alloc(), returns 0 unconditionally after its loop and does not have this problem.
Use a separate variable for the per-port checks so that only fatal errors end up in ret.
--- drivers/staging/media/tegra-video/vi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/staging/media/tegra-video/vi.c +5 -4
@@ -1262,6 +1262,7 @@ static int tegra_vi_channels_alloc(strucstruct device_node *parent;struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type =0 };unsigned int lanes; +int err;int ret =0 ; ports =of_get_child_by_name (node,"ports" );
@@ -1272,8 +1273,8 @@ static int tegra_vi_channels_alloc(strucif (!of_node_name_eq (port,"port" ))continue ; - ret =of_property_read_u32 (port,"reg" , &port_num); -if (ret <0 ) + err =of_property_read_u32 (port,"reg" , &port_num); +if (err <0 )continue ;if (port_num > vi->soc->vi_max_channels) {
@@ -1294,10 +1295,10 @@ static int tegra_vi_channels_alloc(struc ep =of_graph_get_endpoint_by_regs (parent,0 ,0 );of_node_put (parent); - ret =v4l2_fwnode_endpoint_parse (of_fwnode_handle (ep), + err =v4l2_fwnode_endpoint_parse (of_fwnode_handle (ep), &v4l2_ep);of_node_put (ep); -if (ret) +if (err)continue ; lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
Greg Kroah-Hartman
maintainerPATCH 167/1424 rpmsg: glink: smem: order FIFO read after availability check
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chunkai Deng <[email protected]>
commit 786439ad58763e04b91bc2ec5f590e463939f197 upstream.
glink_smem_rx_peek() reads the RX FIFO payload after the caller has determined data is available via glink_smem_rx_avail(), which reads the remote-updated head index. A control dependency between the head read and the subsequent payload read does not order the two loads, so the CPU may speculatively read the FIFO before observing the head update and consume stale data the remote has not yet published.
Add rmb() in glink_smem_rx_peek() before the memcpy_fromio() so the availability (head) read is ordered ahead of the FIFO payload read, matching the consumer pattern in Documentation/core-api/circular-buffers.rst.
--- drivers/rpmsg/qcom_glink_smem.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/rpmsg/qcom_glink_smem.c +7 -0
@@ -103,6 +103,13 @@ static void glink_smem_rx_peek(struct qcif (tail >= pipe->native.length) tail -= pipe->native.length; +/* +* Order the availability (head) read in glink_smem_rx_avail() +* against the FIFO payload read below, so APPS never consumes +* stale data the remote has not yet published. +*/ +rmb (); + len =min_t (size_t, count, pipe->native.length - tail);if (len)memcpy_fromio (data, pipe->fifo + tail, len);
Greg Kroah-Hartman
maintainerPATCH 168/1424 arm64: dts: rockchip: fix eMMC reset polarity on PX30 Ringneck
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Quentin Schulz <[email protected]>
commit dfe078755706ed50651ebbe0442843ecd4ae8389 upstream.
According to the Jedec 5.1 specification, the device is held in reset when RST_n is low, therefore the polarity of the line must be that, as specified in the Device Tree binding (mmc/mmc-pwrseq-emmc.yaml).
Due to the wrong polarity, eMMC devices with RST_n_FUNCTION[162] bitfield [1:0] set to 0x1 (the default is 0x0) will be held in reset forever.
--- arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi +1 -1
@@ -19,7 +19,7 @@ compatible = "mmc-pwrseq-emmc"; pinctrl-0 = <&emmc_reset>; pinctrl-names = "default"; - reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>; }; leds {
Greg Kroah-Hartman
maintainerPATCH 169/1424 arm64: dts: rockchip: Fix rk3399-roc-pc-plus analog audio
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fabio Estevam <[email protected]>
commit 4f7259ebe1eba4778768a4f5a0bbbe439d10f3f3 upstream.
The ES8388 sound card on the rk3399-roc-pc-plus fails to probe because i2s1 cannot claim its MCLK pin:
pinctrl: pin gpio4-0 already requested by ff880000.i2s; cannot claim for ff890000.i2s
pinctrl: error -EINVAL: pin-128 (ff890000.i2s)
pinctrl: error -EINVAL: could not request pin 128 (gpio4-0) from group i2s-8ch-mclk-pin on device rockchip-pinctrl
GPIO4_A0 is routed as SCLK_I2S_8CH_OUT and is used by i2s1 as the external MCLK for the ES8388 codec. The board dts already removes GPIO4_A0 from the i2s0_8ch_bus pin group, but i2s0 still claims the same pin through its bclk_off state.
Since the i2s driver requests both states, this blocks i2s1 pinctrl setup and leaves the simple-audio-card deferred with a parse error.
Override i2s0_8ch_bus_bclk_off as well, matching the existing i2s0_8ch_bus override, so GPIO4_A0 is left for i2s1/ES8388 audio.
--- arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts | 12 ++++++++++++ 1 file changed, 12 insertions(+)
arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts +12 -0
@@ -133,6 +133,18 @@ <3 RK_PD7 1 &pcfg_pull_none>; }; +&i2s0_8ch_bus_bclk_off { + rockchip,pins = + <3 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>, + <3 RK_PD1 1 &pcfg_pull_none>, + <3 RK_PD2 1 &pcfg_pull_none>, + <3 RK_PD3 1 &pcfg_pull_none>, + <3 RK_PD4 1 &pcfg_pull_none>, + <3 RK_PD5 1 &pcfg_pull_none>, + <3 RK_PD6 1 &pcfg_pull_none>, + <3 RK_PD7 1 &pcfg_pull_none>; +}; + &i2s1 { pinctrl-names = "default"; pinctrl-0 = <&i2s_8ch_mclk_pin>, <&i2s1_2ch_bus>;
Greg Kroah-Hartman
maintainerPATCH 170/1424 remoteproc: scp: Fix device reference leak on failed lookup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 22f9efb3ae07f966a1901d929d16df1388cce65c upstream.
Make sure to drop the reference taken to the SCP device when attempting to look up its driver data before the driver has been bound.
Note that holding a reference to a device does not prevent its driver data from going away.
--- drivers/remoteproc/mtk_scp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/remoteproc/mtk_scp.c +8 -1
@@ -36,6 +36,7 @@ struct mtk_scp *scp_get(struct platform_struct device *dev = &pdev->dev;struct device_node *scp_node;struct platform_device *scp_pdev; +struct mtk_scp *scp; scp_node =of_parse_phandle (dev->of_node,"mediatek,scp" ,0 );if (!scp_node) {
@@ -51,7 +52,13 @@ struct mtk_scp *scp_get(struct platform_return NULL ; } -return platform_get_drvdata (scp_pdev); + scp =platform_get_drvdata (scp_pdev); +if (!scp) { +put_device (&scp_pdev->dev); +return NULL ; + } + +return scp; }EXPORT_SYMBOL_GPL (scp_get);
Greg Kroah-Hartman
maintainerPATCH 171/1424 qede: Fix NULL pointer dereference in TPA fragment processing
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vaibhav Nagare <[email protected]>
commit 06aa3d26327f24edd039ff249672fdf6f2ba5695 upstream.
Under memory pressure, the qede driver encounters NULL pointer dereferences when processing TPA continuation fragments.
Commit 8a8633978b84 ("qede: Add build_skb() support.") accidentally dropped the assignment of tpa_info->buffer.data in qede_tpa_start().
When memory pressure causes an SKB allocation failure in qede_tpa_start(), the driver sets tpa_start_fail = true and attempts to recycle the physical page later in qede_tpa_end() via qede_reuse_page(). However, because buffer.data was left uninitialized (NULL), qede_reuse_page() pushes a "ghost" BD (valid DMA mapping but NULL data pointer) back into the active Rx ring.
The next time the hardware uses this ring slot, it passes a NULL page to qede_fill_frag_skb(), causing a kernel panic.
Example crash from production system:
BUG: unable to handle kernel NULL pointer dereference at 0x8 RIP: qede_fill_frag_skb+0x96/0x430 [qede] Call Trace: qede_rx_int+0xb06/0x1de0 qede_poll+0x2f4/0x6c0 __napi_poll+0x2d/0x130
Fix the root cause by restoring the tpa_info->buffer.data assignment in qede_tpa_start(), ensuring valid pages are correctly tracked and recycled. Additionally, update the stale comment for struct qede_agg_info::buffer to reflect its current usage.
--- drivers/net/ethernet/qlogic/qede/qede.h | 8 ++++---- drivers/net/ethernet/qlogic/qede/qede_fp.c | 1 + 2 files changed, 5 insertions(+), 4 deletions(-)
drivers/net/ethernet/qlogic/qede/qede.h +4 -4
@@ -303,10 +303,10 @@ enum qede_agg_state { };struct qede_agg_info { -/* rx_buf is a data buffer that can be placed / consumed from rx bd -* chain. It has two purposes: We will preallocate the data buffer -* for each aggregation when we open the interface and will place this -* buffer on the rx-bd-ring when we receive TPA_START. We don't want +/* buffer is used to retain the Rx consumer descriptor when a TPA +* session starts. If the SKB allocation fails during TPA_START, +* we use this saved buffer to safely recycle the physical page +* back into the rx-bd-ring via qede_reuse_page(). We don't want * to be in a state where allocation fails, as we can't reuse the * consumer buffer in the rx-chain since FW may still be writing to it * (since header needs to be modified for TPA).
drivers/net/ethernet/qlogic/qede/qede_fp.c +1 -0
@@ -850,6 +850,7 @@ static void qede_tpa_start(struct qede_d pad, false); tpa_info->buffer.page_offset = sw_rx_data_cons->page_offset; tpa_info->buffer.mapping = sw_rx_data_cons->mapping; + tpa_info->buffer.data = sw_rx_data_cons->data;if (unlikely (!tpa_info->skb)) {DP_NOTICE (edev,"Failed to allocate SKB for gro\n" );
Greg Kroah-Hartman
maintainerPATCH 172/1424 RDMA/cxgb4: Cancel reg_work before freeing device on remove
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit a7100601aa1a39f799a566acce10db20eaf4b7f2 upstream.
c4iw_uld_state_change() queues reg_work to register the RDMA device. c4iw_remove() can free ctx->dev while this work is pending or running, leaving c4iw_register_device() accessing the freed device.
Cancel reg_work before removing the device. The registration work can tear down ctx->dev when registration fails, so do not unregister or deallocate it again in that case.
This issue was found by an in-house static analysis tool.
--- drivers/infiniband/hw/cxgb4/device.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/infiniband/hw/cxgb4/device.c +6 -0
@@ -953,6 +953,12 @@ void c4iw_dealloc(struct uld_ctx *ctx)static void c4iw_remove (struct uld_ctx *ctx) {pr_debug ("c4iw_dev %p\n" , ctx->dev); + +/* c4iw_register_device() may still be using ctx->dev. */ +cancel_work_sync (&ctx->reg_work); +if (!ctx->dev) +return ; +debugfs_remove_recursive (ctx->dev->debugfs_root);c4iw_unregister_device (ctx->dev);c4iw_dealloc (ctx);
Greg Kroah-Hartman
maintainerPATCH 173/1424 RDMA/ucma: Lock the handler in ucma_set_ib_path()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
commit ecbe7d36dc2de07e5dfbb4a8ff5b315ab43de820 upstream.
ucma_set_ib_path() calls ucma_event_handler() straight from the write() path, without the handler lock that keeps ctx->file stable while a uevent is queued. The handler re-reads ctx->file for every dereference:
mutex_lock(&ctx->file->mut); /* file A */ list_add_tail(&uevent->list, &ctx->file->event_list); /* file B */ mutex_unlock(&ctx->file->mut); /* file B */ wake_up_interruptible(&ctx->file->poll_wait); /* file B */
A concurrent ucma_migrate_id() reassigns ctx->file while the SET_OPTION caller sleeps in mutex_lock(), so the list_add_tail() lands on file B's event_list while only file A's mutex is held, racing every other user of that list:
BUG: KASAN: slab-use-after-free in __list_add_valid_or_report+0x1aa/0x1c0 Read of size 8 at addr ffff888153c6a418 by task poc_corr/486 Call Trace: __list_add_valid_or_report+0x1aa/0x1c0 ucma_event_handler+0x1be/0xc00 ucma_set_ib_path+0x45e/0x710 ucma_set_option+0x32e/0x590 ucma_write+0x1f9/0x330 Allocated by task 505: ucma_write_cm_event+0x1a1/0x660 Freed by task 505: kfree+0x1da/0x4c0 ucma_get_event+0x5d5/0x7e0
The freed object is a ucma_event that another thread dequeued from file B's list under file B's mutex. File A's mut is left held on top of that, wedging its next writer in uninterruptible sleep.
This path needs a bound and address-resolved cm_id, so it requires an RDMA device to be present.
Take the handler lock around the call.
--- drivers/infiniband/core/ucma.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/infiniband/core/ucma.c +4 -1
@@ -1336,7 +1336,10 @@ static int ucma_set_ib_path(struct ucma_memset (&event,0 ,sizeof event); event.event =RDMA_CM_EVENT_ROUTE_RESOLVED ; -return ucma_event_handler (ctx->cm_id, &event); +rdma_lock_handler (ctx->cm_id); + ret =ucma_event_handler (ctx->cm_id, &event); +rdma_unlock_handler (ctx->cm_id); +return ret; } static int ucma_set_option_ib(struct ucma_context *ctx, int optname,
Greg Kroah-Hartman
maintainerPATCH 174/1424 regulator: as3722_get_regulator_dt_data: fix premature of_node_put leaving dangling of_node pointer
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: WenTao Liang <[email protected]>
commit f9324d670ae0b88cbfb0aa48fcaefa5baeb8da4c upstream.
In as3722_get_regulator_dt_data(), of_get_child_by_name() acquires a reference on np, which is then assigned to pdev->dev.of_node. The function immediately calls of_node_put(np), releasing the reference and leaving pdev->dev.of_node as a dangling pointer.
Remove the of_node_put(np) call to let the device hold the reference.
--- drivers/regulator/as3722-regulator.c | 1 - 1 file changed, 1 deletion(-)
drivers/regulator/as3722-regulator.c +0 -1
@@ -600,7 +600,6 @@ static int as3722_get_regulator_dt_data( ret =of_regulator_match (&pdev->dev, np, as3722_regulator_matches,ARRAY_SIZE (as3722_regulator_matches)); -of_node_put (np);if (ret <0 ) {dev_err (&pdev->dev,"Parsing of regulator node failed: %d\n" , ret);
Greg Kroah-Hartman
maintainerPATCH 175/1424 regulator: max8998_pmic_dt_parse_pdata: of_node_put on reg_np after ownership transferred to rdata
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: WenTao Liang <[email protected]>
commit 7c8cc25d8d86f9eb3979255935cfdc7d062ad746 upstream.
In max8998_pmic_dt_parse_pdata(), of_get_child_by_name() acquires a reference on reg_np which is then stored in rdata->reg_node, transferring ownership to the regulator data array. The subsequent of_node_put(reg_np) at the end of the function releases the last matched regulator node's reference, leaving rdata->reg_node as a dangling pointer for the last entry.
Remove the spurious of_node_put(reg_np) call.
--- drivers/regulator/max8998.c | 1 - 1 file changed, 1 deletion(-)
drivers/regulator/max8998.c +0 -1
@@ -611,7 +611,6 @@ static int max8998_pmic_dt_parse_pdata(s } pdata->num_regulators = rdata - pdata->regulators; -of_node_put (reg_np);of_node_put (regulators_np); ret =max8998_pmic_dt_parse_dvs_gpio (iodev, pdata, pmic_np);
Greg Kroah-Hartman
maintainerPATCH 176/1424 regulator: qcom-refgen: correct the regulator type to CURRENT
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kathiravan Thirumoorthy <[email protected]>
commit 05dfeb2d0ccf87a7b92cd149a393b8423a26a04e upstream.
As per the REFGEN IP team, this block supplies the reference current to the PHYs in the SoC. So, correct the regulator type to REGULATOR_CURRENT to match with the HW behavior.
--- drivers/regulator/qcom-refgen-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/regulator/qcom-refgen-regulator.c +2 -2
@@ -66,7 +66,7 @@ static struct regulator_desc sdm845_refg .enable_time = 5, .name = "refgen", .owner = THIS_MODULE, - .type = REGULATOR_VOLTAGE, + .type = REGULATOR_CURRENT, .ops = &(const struct regulator_ops ) { .enable = qcom_sdm845_refgen_enable, .disable = qcom_sdm845_refgen_disable,
@@ -82,7 +82,7 @@ static struct regulator_desc sm8250_refg .enable_time = 5, .name = "refgen", .owner = THIS_MODULE, - .type = REGULATOR_VOLTAGE, + .type = REGULATOR_CURRENT, .ops = &(const struct regulator_ops ) { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap,
Greg Kroah-Hartman
maintainerPATCH 177/1424 orangefs: fix double-free of trailer_buf on readdir copy failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yifei Gao <[email protected]>
commit f574296be7f46eb60beca851240b526df232f480 upstream.
On a readdir downcall, orangefs_devreq_write_iter() frees op->downcall.trailer_buf with vfree() when copy_from_iter_full() fails, but does not clear the pointer before goto Efault. The waiter in do_readdir() is then woken with a negative status and frees the same pointer again on its r < 0 path, causing a deterministic double-free. A client holding /dev/pvfs2-req triggers it by sending a readdir downcall whose declared trailer_size exceeds the bytes it supplies.
Clear the pointer after freeing so the readdir-side vfree() becomes a no-op.
--- fs/orangefs/devorangefs-req.c | 1 + 1 file changed, 1 insertion(+)
fs/orangefs/devorangefs-req.c +1 -0
@@ -474,6 +474,7 @@ static ssize_t orangefs_devreq_write_ite op->downcall.trailer_size, iter)) {gossip_err ("%s: failed to copy trailer.\n" , __func__);vfree (op->downcall.trailer_buf); + op->downcall.trailer_buf =NULL ; goto Efault; }
Greg Kroah-Hartman
maintainerPATCH 178/1424 orangefs: skip leading spaces before parsing client debug masks
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit d410cd5303ec59c7cf23dd61423752ce8e9ecb59 upstream.
orangefs_prepare_cdm_array() sizes each client debug keyword buffer with strcspn(cds_head, " "), but then parses the keyword with %s. The %s conversion skips leading whitespace, while strcspn() does not.
If a client debug entry starts with a space, the allocation can be sized for an empty keyword while sscanf() copies the following non-empty token. This can write past the end of the allocated keyword buffer.
Skip leading spaces before computing the keyword length so the allocation matches the string parsed by sscanf().
--- fs/orangefs/orangefs-debugfs.c | 1 + 1 file changed, 1 insertion(+)
fs/orangefs/orangefs-debugfs.c +1 -0
@@ -529,6 +529,7 @@ static int orangefs_prepare_cdm_array(ch cds_delimiter =strchr (cds_head,'\n' ); *cds_delimiter ='\0' ; + cds_head =skip_spaces (cds_head); keyword_len =strcspn (cds_head," " ); cdm_array[i].keyword =kzalloc (keyword_len +1 ,GFP_KERNEL );
Greg Kroah-Hartman
maintainerPATCH 179/1424 ocfs2: always run deallocs on copy-on-write completion
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Antipov <[email protected]>
commit 82ea9d4fc05fb7a387db547c6a7c0aa6a3719616 upstream.
Local fuzzing of 6.12.94 has found the following memory leak caused by doing 'copy_file_range()' within the same filesystem:
unreferenced object 0xffff88812192c980 (size 32):
comm "syz.0.49", pid 12095, jiffies 4294964143 hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
c0 c5 92 21 81 88 ff ff 00 02 00 00 00 06 00 00 ...!............
backtrace (crc 7068d63f):
kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline]
slab_post_alloc_hook mm/slub.c:4152 [inline]
slab_alloc_node mm/slub.c:4197 [inline]
__kmalloc_cache_noprof+0x168/0x2c0 mm/slub.c:4358
kmalloc_noprof include/linux/slab.h:878 [inline]
ocfs2_find_per_slot_free_list fs/ocfs2/alloc.c:6618 [inline]
ocfs2_cache_block_dealloc+0x155/0x4b0 fs/ocfs2/alloc.c:6786
ocfs2_cache_extent_block_free fs/ocfs2/alloc.c:6819 [inline]
ocfs2_unlink_path+0x286/0x450 fs/ocfs2/alloc.c:2613
ocfs2_rotate_subtree_left fs/ocfs2/alloc.c:2779 [inline]
__ocfs2_rotate_tree_left+0x1f6f/0x2da0 fs/ocfs2/alloc.c:2985
ocfs2_rotate_tree_left+0x283/0xe00 fs/ocfs2/alloc.c:3237
ocfs2_try_to_merge_extent+0xf56/0x1a20 fs/ocfs2/alloc.c:3825
ocfs2_split_extent+0x15f4/0x2940 fs/ocfs2/alloc.c:5138
ocfs2_clear_ext_refcount+0x2f6/0x550 fs/ocfs2/refcounttree.c:3098
ocfs2_replace_clusters fs/ocfs2/refcounttree.c:3131 [inline]
ocfs2_make_clusters_writable fs/ocfs2/refcounttree.c:3255 [inline]
ocfs2_replace_cow+0x991/0x1660 fs/ocfs2/refcounttree.c:3349
ocfs2_refcount_cow_hunk fs/ocfs2/refcounttree.c:3427 [inline]
ocfs2_refcount_cow+0x5e1/0x9f0 fs/ocfs2/refcounttree.c:3470
ocfs2_prepare_inode_for_write fs/ocfs2/file.c:2340 [inline]
ocfs2_file_write_iter+0xbda/0x1880 fs/ocfs2/file.c:2451
iter_file_splice_write+0x890/0xf60 fs/splice.c:743
do_splice_from fs/splice.c:944 [inline]
direct_splice_actor+0x232/0x480 fs/splice.c:1167
splice_direct_to_actor+0x4b4/0xb60 fs/splice.c:1111
do_splice_direct_actor fs/splice.c:1210 [inline]
do_splice_direct+0x10f/0x1c0 fs/splice.c:1236
do_sendfile+0x430/0xbf0 fs/read_write.c:1388unreferenced object 0xffff88812192c5c0 (size 32):
comm "syz.0.49", pid 12095, jiffies 4294964143 hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
29 70 00 00 00 00 00 00 19 00 00 00 00 00 00 00 )p..............
backtrace (crc afec850f):
kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline]
slab_post_alloc_hook mm/slub.c:4152 [inline]
slab_alloc_node mm/slub.c:4197 [inline]
__kmalloc_cache_noprof+0x168/0x2c0 mm/slub.c:4358
kmalloc_noprof include/linux/slab.h:878 [inline]
kzalloc_noprof include/linux/slab.h:1014 [inline]
ocfs2_cache_block_dealloc+0x25c/0x4b0 fs/ocfs2/alloc.c:6793
ocfs2_cache_extent_block_free fs/ocfs2/alloc.c:6819 [inline]
ocfs2_unlink_path+0x286/0x450 fs/ocfs2/alloc.c:2613
ocfs2_rotate_subtree_left fs/ocfs2/alloc.c:2779 [inline]
__ocfs2_rotate_tree_left+0x1f6f/0x2da0 fs/ocfs2/alloc.c:2985
ocfs2_rotate_tree_left+0x283/0xe00 fs/ocfs2/alloc.c:3237
ocfs2_try_to_merge_extent+0xf56/0x1a20 fs/ocfs2/alloc.c:3825
ocfs2_split_extent+0x15f4/0x2940 fs/ocfs2/alloc.c:5138
ocfs2_clear_ext_refcount+0x2f6/0x550 fs/ocfs2/refcounttree.c:3098
ocfs2_replace_clusters fs/ocfs2/refcounttree.c:3131 [inline]
ocfs2_make_clusters_writable fs/ocfs2/refcounttree.c:3255 [inline]
ocfs2_replace_cow+0x991/0x1660 fs/ocfs2/refcounttree.c:3349
ocfs2_refcount_cow_hunk fs/ocfs2/refcounttree.c:3427 [inline]
ocfs2_refcount_cow+0x5e1/0x9f0 fs/ocfs2/refcounttree.c:3470
ocfs2_prepare_inode_for_write fs/ocfs2/file.c:2340 [inline]
ocfs2_file_write_iter+0xbda/0x1880 fs/ocfs2/file.c:2451
iter_file_splice_write+0x890/0xf60 fs/splice.c:743
do_splice_from fs/splice.c:944 [inline]
direct_splice_actor+0x232/0x480 fs/splice.c:1167
splice_direct_to_actor+0x4b4/0xb60 fs/splice.c:1111
do_splice_direct_actor fs/splice.c:1210 [inline]
do_splice_direct+0x10f/0x1c0 fs/splice.c:1236
do_sendfile+0x430/0xbf0 fs/read_write.c:1388This happens when 'ocfs2_cache_block_dealloc()' called from 'ocfs2_cache_extent_block_free()' uses the suballocator to schedule extent removal, so 'ocfs2_run_deallocs()' should be run unconditionally to complete the removal with 'ocfs2_free_cached_blocks()'. An extra semi-automated static analysis [1] suspects that the same scenario looks possible in 'ocfs2_attach_refcount_tree()' and 'ocfs2_reflink_remap_blocks()' as well, but, since 'ocfs2_run_deallocs()' is a safe no-op for an empty dealloc context, 'ocfs2_create_reflink_node()' and 'ocfs2_reflink_xattrs()' may be adjusted in the same way too, thus keeping the code pattern consistent.
--- fs/ocfs2/refcounttree.c | 20 ++++++++------------ fs/ocfs2/xattr.c | 5 ++--- 2 files changed, 10 insertions(+), 15 deletions(-)
fs/ocfs2/refcounttree.c +8 -12
@@ -3365,10 +3365,9 @@ static int ocfs2_replace_cow(struct ocfs cow_start += num_clusters; } -if (ocfs2_dealloc_has_cluster (&context->dealloc)) { +if (ocfs2_dealloc_has_cluster (&context->dealloc))ocfs2_schedule_truncate_log_flush (osb,1 ); -ocfs2_run_deallocs (osb, &context->dealloc); - } +ocfs2_run_deallocs (osb, &context->dealloc);return ret; }
@@ -3851,10 +3850,9 @@ unlock:ocfs2_unlock_refcount_tree (osb, ref_tree,1 );brelse (ref_root_bh); -if (!ret &&ocfs2_dealloc_has_cluster (&dealloc)) { +if (!ret &&ocfs2_dealloc_has_cluster (&dealloc))ocfs2_schedule_truncate_log_flush (osb,1 ); -ocfs2_run_deallocs (osb, &dealloc); - } +ocfs2_run_deallocs (osb, &dealloc); out:/* * Empty the extent map so that we may get the right extent
@@ -4140,10 +4138,9 @@ out_unlock_refcount:ocfs2_unlock_refcount_tree (osb, ref_tree,1 );brelse (ref_root_bh); out: -if (ocfs2_dealloc_has_cluster (&dealloc)) { +if (ocfs2_dealloc_has_cluster (&dealloc))ocfs2_schedule_truncate_log_flush (osb,1 ); -ocfs2_run_deallocs (osb, &dealloc); - } +ocfs2_run_deallocs (osb, &dealloc);return ret; }
@@ -4696,10 +4693,9 @@ loff_t ocfs2_reflink_remap_blocks(struct } out: -if (ocfs2_dealloc_has_cluster (&dealloc)) { +if (ocfs2_dealloc_has_cluster (&dealloc))ocfs2_schedule_truncate_log_flush (osb,1 ); -ocfs2_run_deallocs (osb, &dealloc); - } +ocfs2_run_deallocs (osb, &dealloc);return ret; }
fs/ocfs2/xattr.c +2 -3
@@ -7196,10 +7196,9 @@ out_unlock: ref_tree, 1);brelse (ref_root_bh); -if (ocfs2_dealloc_has_cluster (&dealloc)) { +if (ocfs2_dealloc_has_cluster (&dealloc))ocfs2_schedule_truncate_log_flush (OCFS2_SB (old_inode->i_sb),1 ); -ocfs2_run_deallocs (OCFS2_SB (old_inode->i_sb), &dealloc); - } +ocfs2_run_deallocs (OCFS2_SB (old_inode->i_sb), &dealloc); out:return ret;
Greg Kroah-Hartman
maintainerPATCH 180/1424 ocfs2: bound namelen in dlm_migrate_request_handler
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit ea5b5609305a8437bc955a0834a530c12246d78f upstream.
Patch series "ocfs2/dlm: bound peer-controlled lengths in the o2dlm".
The o2dlm receive handlers trust u8 length and count fields from the wire without bounding them, so a node in a DLM domain can corrupt or panic any other node with a malformed message. Three defects:
- dlm_migrate_request_handler() passes migrate->namelen unchecked to dlm_init_mle(), which memcpy()s it into the 32-byte mname[] of an o2dlm_mle slab object: a heap out-of-bounds write of up to ~215 attacker-controlled bytes.
- dlm_mig_lockres_handler() passes mres->lockname_len unchecked to dlm_init_lockres(), which memcpy()s it into the 32-byte o2dlm_lockname slab object: a heap out-of-bounds write of up to ~223 bytes.
- the same handler trusts mres->num_locks without checking that the message is large enough to hold that many entries, so dlm_process_recovery_data() walks mres->ml[] past the kmalloc(data_len) copy and trips a BUG_ON (an out-of-bounds read ending in a panic).
The other o2dlm receive handlers already reject an oversized name; the migration and recovery handlers have omitted it since the DLM was added (see the Fixes tags). Patch 1 bounds namelen; patch 2 validates lockname_len, num_locks, and the payload size. Conforming recovery and migration traffic is unaffected.
o2net authenticates peers only by the DLM domain key, so any node that has joined the domain -- including a compromised or malicious member -- can send these messages. There is no local trigger; the attacker must already be a member of the cluster.
Each sink was confirmed under KASAN with an out-of-tree module mirroring it exactly -- a kmem_cache/kmalloc of the real destination size, then the same unclamped memcpy/loop: slab-out-of-bounds Write for the two writes, Read for the recovery walk, and a panic. A userspace AddressSanitizer build faults identically under -m32 and -m64. Scrubbed logs are available on request.
I reported this privately to [email protected] and the ocfs2 maintainers on 2026-06-20; with no response after the standard embargo period I am posting the fix publicly. I have no embargo requirement.
This patch (of 2):
A node receiving a DLM_MIGRATE_REQUEST message trusts the peer-supplied name length (migrate->namelen) without bounding it. dlm_init_mle() then copies that many bytes into the fixed DLM_LOCKID_NAME_MAX-byte mname[] array of an o2dlm_mle slab object, so a malformed message from a cluster peer overflows the slab object by up to ~215 bytes: a heap out-of-bounds write of attacker-controlled data, reachable by any node in the domain.
Reject an oversized name, the way dlm_master_request_handler() and the other o2dlm receive handlers already do; the migration handler omits the check entirely. Conforming messages are unaffected.
--- fs/ocfs2/dlm/dlmmaster.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/ocfs2/dlm/dlmmaster.c +6 -0
@@ -3112,6 +3112,12 @@ int dlm_migrate_request_handler(struct o name = migrate->name; namelen = migrate->namelen; +if (namelen >DLM_LOCKID_NAME_MAX ) { +mlog (ML_ERROR ,"%s: invalid name length %u in migrate request\n" , + dlm->name, namelen); + ret = -EINVAL ; + goto leave; + } hash =dlm_lockid_hash (name, namelen);/* preallocate.. if this fails, abort */
Greg Kroah-Hartman
maintainerPATCH 181/1424 ocfs2: validate lengths in dlm_mig_lockres_handler
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit b54e03d9b3697d25f4a0063cf717d459c5e3ad94 upstream.
A node receiving a DLM_MIG_LOCKRES message trusts several fields of the peer-supplied dlm_migratable_lockres without validation. num_locks and lockname_len are bounded only on the sending side, and the message is never checked to actually carry num_locks migratable_lock entries. As a result dlm_process_recovery_data() walks mres->ml[0..num_locks) past the kmalloc(data_len) copy of the message (an out-of-bounds read that ends in a BUG_ON panic), and dlm_init_lockres() copies lockname_len bytes into the fixed 32-byte o2dlm_lockname slab object (a heap out-of-bounds write). Both are reachable by any node in the domain.
Validate these fields right after dlm_grab(), before anything uses them -- including the not-joined error path, which already prints mres->lockname with the unbounded lockname_len as a %.*s precision. Reject the message unless lockname_len <= DLM_LOCKID_NAME_MAX, num_locks <= DLM_MAX_MIGRATABLE_LOCKS (the bound the sender already asserts), and the payload is large enough to hold the claimed locks. Conforming recovery and migration messages are unaffected.
--- fs/ocfs2/dlm/dlmrecovery.c | 9 +++++++++ 1 file changed, 9 insertions(+)
fs/ocfs2/dlm/dlmrecovery.c +9 -0
@@ -1359,6 +1359,15 @@ int dlm_mig_lockres_handler(struct o2netif (!dlm_grab (dlm))return -EINVAL ; +if (mres->lockname_len >DLM_LOCKID_NAME_MAX || + mres->num_locks >DLM_MAX_MIGRATABLE_LOCKS || +be16_to_cpu (msg->data_len) <struct_size (mres, ml, mres->num_locks)) { +mlog (ML_ERROR ,"%s: invalid lockres migration message from %u\n" , + dlm->name, mres->master); +dlm_put (dlm); +return -EINVAL ; + } +if (!dlm_joined (dlm)) { mlog(ML_ERROR, "Domain %s not joined! " "lockres %.*s, master %u\n",
Greg Kroah-Hartman
maintainerPATCH 182/1424 ocfs2: validate rl_used against rl_count in refcount block validator
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Hashimov <[email protected]>
commit 4ca62df6bc0708947b48da3f6a712ecb8e73929c upstream.
ocfs2_find_refcount_rec_in_rl() walks the on-disk refcount record array with:
for (; i < le16_to_cpu(rb->rf_records.rl_used); i++) {
rec = &rb->rf_records.rl_recs[i];
...rl_recs[] lives in a single metadata block (4096 bytes on the common configuration), so its real capacity is fixed by ocfs2_refcount_recs_per_rb(sb) (247 records for a 4K block with the 16-byte ocfs2_refcount_rec). rl_used and rl_count are both read directly off disk by ocfs2_validate_refcount_block() and are never checked against that capacity, nor against each other, before any refcount/reflink/CoW operation walks the array.
A crafted (or corrupted) refcount block with rl_used == 0xffff makes the loop above walk far past the end of the block, dereferencing rl_recs[i] for i up to 65534. The resulting index is then handed to the sibling ocfs2_insert_refcount_rec(), whose insert-shift does:
if (index < le16_to_cpu(rf_list->rl_used)) memmove(&rf_list->rl_recs[index + 1], &rf_list->rl_recs[index], (le16_to_cpu(rf_list->rl_used) - index) * sizeof(struct ocfs2_refcount_rec));
i.e. a memmove() of up to (0xffff - index) * 16 bytes (~1 MiB) from an offset already past the block. This is reachable from an ordinary reflink (FICLONE) against a crafted/corrupted ocfs2 image: attaching an extent whose cpos sorts past every real record in the leaf forces the lookup to run off the end instead of returning early on a match. The attacker model is local: CAP_SYS_ADMIN mounting a crafted or corrupted ocfs2 image, or a raw write to the block device backing an already-mounted ocfs2 filesystem.
ocfs2_validate_refcount_block() already validates the block's ECC, signature, rf_blkno and rf_fs_generation, but never rl_count/rl_used against the block's actual on-disk capacity. This is the same class of gap that ocfs2_validate_extent_block() (fs/ocfs2/alloc.c) already closes for the sibling extent-list header, which checks both the record capacity and the "used" bound before any code walks h_list.l_recs[]:
if (le16_to_cpu(eb->h_list.l_count) != ocfs2_extent_recs_per_eb(sb)) {
rc = ocfs2_error(...);
goto bail;
}
if (le16_to_cpu(eb->h_list.l_next_free_rec) >
le16_to_cpu(eb->h_list.l_count)) {
rc = ocfs2_error(...);
goto bail;
}Add the equivalent pair of checks to ocfs2_validate_refcount_block():
reject a refcount block whose rl_count does not match the fixed per-block capacity returned by ocfs2_refcount_recs_per_rb(), and reject rl_used > rl_count. Both checks are skipped when OCFS2_REFCOUNT_TREE_FL is set, because in that case the same union bytes hold an ocfs2_extent_list (rf_list), not the refcount record list (rf_records) -- that layout is already validated separately by ocfs2_validate_extent_block() when the referenced extent block is read. This mirrors the existing "!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL)" guard used elsewhere in this file (e.g. ocfs2_get_refcount_rec()) to decide whether rf_records or rf_list is the live member of the union.
With this in place, a forged rl_used/rl_count is caught at block validation time (ocfs2_error()), consistent with every other corruption check in this function, instead of driving an out-of-bounds read in ocfs2_find_refcount_rec_in_rl() and a subsequent out-of-bounds memmove() in ocfs2_insert_refcount_rec().
Verified against a crafted image on a v6.19 KASAN (KASAN_GENERIC) build:
replaying the same reflink (FICLONE) reliably hit a KASAN report in __ocfs2_increase_refcount()/ocfs2_insert_refcount_rec() before this patch, and triggers no report once ocfs2_validate_refcount_block() rejects the forged rl_used/rl_count.
--- fs/ocfs2/refcounttree.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
fs/ocfs2/refcounttree.c +27 -0
@@ -116,6 +116,33 @@ static int ocfs2_validate_refcount_block le32_to_cpu(rb->rf_fs_generation)); goto out; } + +/* +* rf_records (rl_count/rl_used/rl_recs[]) is only meaningful when +* this block is not an interior tree block (OCFS2_REFCOUNT_TREE_FL); +* in that case the same union bytes hold an extent list (rf_list) +* instead, which is validated by ocfs2_validate_extent_block(). +*/ +if (!(le32_to_cpu (rb->rf_flags) &OCFS2_REFCOUNT_TREE_FL )) { +if (le16_to_cpu (rb->rf_records.rl_count) != +ocfs2_refcount_recs_per_rb (sb)) { + rc =ocfs2_error (sb, +"Refcount block #%llu has an invalid rl_count of %u\n" , + (unsigned long long )bh->b_blocknr, +le16_to_cpu (rb->rf_records.rl_count)); + goto out; + } + +if (le16_to_cpu (rb->rf_records.rl_used) > +le16_to_cpu (rb->rf_records.rl_count)) { + rc =ocfs2_error (sb, +"Refcount block #%llu has an invalid rl_used of %u (rl_count %u)\n" , + (unsigned long long )bh->b_blocknr, +le16_to_cpu (rb->rf_records.rl_used), +le16_to_cpu (rb->rf_records.rl_count)); + goto out; + } + } out:return rc; }
Greg Kroah-Hartman
maintainerPATCH 183/1424 ocfs2: cluster: dont sleep while holding o2hb_live_lock in o2hb_region_pin()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joseph Qi <[email protected]>
commit af09df89db9a68a1d76df0f75667998135bc8d65 upstream.
Patch series "ocfs2: cluster: o2hb_region_pin() fixes", v2.
This series fixes three related issues in o2hb_region_pin(), all are from the original implementation in commit: 58a3158a5d17 ("ocfs2/cluster:
Pin/unpin o2hb regions"):
1) It is called with o2hb_live_lock (a spinlock) held, but the underlying configfs_depend_item() sleeps (takes inode rwsem and pins the filesystem). This triggers BUG under CONFIG_DEBUG_ATOMIC_SLEEP.
2) When called from the configfs drop_item callback, it creates a lock order inversion: parent inode_lock -> configfs root inode_lock, which can deadlock against subsystem unregistration paths taking root -> parent.
3) If pinning fails partway through o2hb_region_inc_user(), the o2hb_dependent_users counter is leaked and partially-pinned regions are never released, leaving heartbeat regions unprotected on subsequent mounts.
Patch 1 reworks o2hb_region_pin() to drop o2hb_live_lock across each sleeping configfs_depend_item() call, using a config_item reference to keep the region alive while unlocked.
Patch 2 adds a from_callback parameter to select configfs_depend_item_unlocked() when called from configfs context, avoiding the inode_lock nesting.
Patch 3 fixes the error path in o2hb_region_inc_user() to unpin and decrement the counter on failure.
This patch (of 3):
o2hb_region_pin() is always called with the o2hb_live_lock spinlock held (from o2hb_region_inc_user() and o2hb_heartbeat_group_drop_item()), but it calls o2nm_depend_item() -> configfs_depend_item(), which sleeps: it pins the configfs filesystem and takes the configfs root inode rwsem. Under CONFIG_DEBUG_ATOMIC_SLEEP this triggers:
BUG: sleeping function called from invalid context at kernel/locking/rwsem.c
in_atomic(): 1, ... name: mount.ocfs2
down_write
configfs_depend_item
o2hb_region_pin
o2hb_region_inc_user
o2hb_register_callback
dlm_register_domain_handlers
...
ocfs2_dlm_init
ocfs2_mount_volume
ocfs2_fill_superRework o2hb_region_pin() to pin one region at a time with the lock dropped across the sleeping call: under o2hb_live_lock find the next eligible region and take a config_item reference to keep it alive, drop the lock, call o2nm_depend_item(), then retake the lock and record the pin. The config_item_put() is done with the lock released as well, since o2hb_region_release() also acquires o2hb_live_lock and can sleep. The region list may change while unlocked, so the scan restarts from the top after each pin. Local heartbeat still pins only the matching region; global heartbeat pins all eligible regions.
The unpin path is unaffected: configfs_undepend_item() only takes a spinlock and does not sleep.
--- fs/ocfs2/cluster/heartbeat.c | 126 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 25 deletions(-)
fs/ocfs2/cluster/heartbeat.c +101 -25
@@ -42,6 +42,14 @@ static DECLARE_RWSEM(o2hb_callback_sem);* whenever any of the threads sees activity from the node in its region. */ static DEFINE_SPINLOCK (o2hb_live_lock); +/* +* Serializes region pin/unpin dependency management (o2hb_dependent_users +* and the o2nm_depend_item()/o2nm_undepend_item() calls). o2hb_region_pin() +* has to drop o2hb_live_lock across the sleeping o2nm_depend_item(), so the +* spinlock alone can no longer keep pin and unpin mutually exclusive; this +* mutex, taken outside o2hb_live_lock, does. +*/ +static DEFINE_MUTEX (o2hb_dependency_mutex);static struct list_head o2hb_live_slots[O2NM_MAX_NODES ];static unsigned long o2hb_live_node_bitmap[BITS_TO_LONGS (O2NM_MAX_NODES )];static LIST_HEAD (o2hb_node_events);
@@ -2107,6 +2115,7 @@ static void o2hb_heartbeat_group_drop_it* If global heartbeat active and there are dependent users, * pin all regions if quorum region count <= CUT_OFF */ +mutex_lock (&o2hb_dependency_mutex);spin_lock (&o2hb_live_lock);if (!o2hb_dependent_users)
@@ -2118,6 +2127,7 @@ static void o2hb_heartbeat_group_drop_it unlock:spin_unlock (&o2hb_live_lock); +mutex_unlock (&o2hb_dependency_mutex); } static ssize_t o2hb_heartbeat_group_dead_threshold_show(struct config_item *item,
@@ -2256,46 +2266,108 @@ EXPORT_SYMBOL_GPL(o2hb_setup_callback);*/ static int o2hb_region_pin (const char *region_uuid) { -int ret =0 , found =0 ; -struct o2hb_region *reg; +int ret =0 , found; +struct o2hb_region *reg, *pinned;char *uuid;assert_spin_locked (&o2hb_live_lock); -list_for_each_entry (reg, &o2hb_all_regions, hr_all_item) { -if (reg->hr_item_dropped) -continue ; +do { + found =0 ; + pinned =NULL ; - uuid =config_item_name (®->hr_item); +list_for_each_entry (reg, &o2hb_all_regions, hr_all_item) { +if (reg->hr_item_dropped) +continue ; -/* local heartbeat */ -if (region_uuid) { -if (strcmp (region_uuid, uuid)) + uuid =config_item_name (®->hr_item); + +/* local heartbeat */ +if (region_uuid) { +if (strcmp (region_uuid, uuid)) +continue ; + found =1 ; + } + +if (reg->hr_item_pinned || reg->hr_item_dropped) { +if (found) +break ;continue ; - found =1 ; + } + +/* +* Found a region that needs pinning. Take a reference +* so it stays alive while we drop the lock below. +*/ + pinned = reg; +config_item_get (®->hr_item); +break ; } -if (reg->hr_item_pinned || reg->hr_item_dropped) - goto skip_pin; +if (!pinned) +break ; + + uuid =config_item_name (&pinned->hr_item); + +/* +* o2nm_depend_item() -> configfs_depend_item() can sleep (it +* takes the configfs root inode rwsem), so it must not run +* under o2hb_live_lock. Drop the lock across it; @pinned is +* kept alive by the reference taken above. The region list may +* change while unlocked, so we rescan from the top afterwards. +*/ +spin_unlock (&o2hb_live_lock);/* Ignore ENOENT only for local hb (userdlm domain) */ - ret =o2nm_depend_item (®->hr_item); + ret =o2nm_depend_item (&pinned->hr_item); + +spin_lock (&o2hb_live_lock);if (!ret) { -mlog (ML_CLUSTER ,"Pin region %s\n" , uuid); - reg->hr_item_pinned =1 ; - }else { -if (ret == -ENOENT && found) - ret =0 ; -else { -mlog (ML_ERROR ,"Pin region %s fails with %d\n" , - uuid, ret); +/* +* o2hb_live_lock was dropped across o2nm_depend_item(). +* o2hb_set_quorum_device() runs in the heartbeat thread +* without o2hb_dependency_mutex, so for global heartbeat +* it may have crossed O2HB_PIN_CUT_OFF and unpinned the +* regions while we slept. If that happened this pin is +* no longer wanted; undo it and stop rather than +* resurrecting it on the rescan below. +*/ +if (!region_uuid && +bitmap_weight (o2hb_quorum_region_bitmap, +O2NM_MAX_REGIONS ) >O2HB_PIN_CUT_OFF ) { +o2nm_undepend_item (&pinned->hr_item); +spin_unlock (&o2hb_live_lock); +config_item_put (&pinned->hr_item); +spin_lock (&o2hb_live_lock);break ; } +mlog (ML_CLUSTER ,"Pin region %s\n" , uuid); + pinned->hr_item_pinned =1 ; + }else if (ret == -ENOENT && (found || !region_uuid)) { +/* +* For local hb (found): ignore ENOENT from userdlm +* domains as before. For global hb (!region_uuid): +* the region may have been detached from configfs +* while the lock was dropped — skip it and continue +* pinning the remaining regions. +*/ + ret =0 ; + }else { +mlog (ML_ERROR ,"Pin region %s fails with %d\n" , + uuid, ret); } -skip_pin: -if (found) -break ; - } + +/* +* config_item_put() may drop the last reference and run +* o2hb_region_release(), which also grabs o2hb_live_lock and +* can sleep, so it must happen with the lock released. +*/ +spin_unlock (&o2hb_live_lock); +config_item_put (&pinned->hr_item); +spin_lock (&o2hb_live_lock); + +/* local hb pins a single matching region */ + }while (!ret && !region_uuid);return ret; }
@@ -2340,6 +2412,7 @@ static int o2hb_region_inc_user(const ch {int ret =0 ; +mutex_lock (&o2hb_dependency_mutex);spin_lock (&o2hb_live_lock);/* local heartbeat */
@@ -2362,11 +2435,13 @@ static int o2hb_region_inc_user(const ch unlock:spin_unlock (&o2hb_live_lock); +mutex_unlock (&o2hb_dependency_mutex);return ret; }static void o2hb_region_dec_user (const char *region_uuid) { +mutex_lock (&o2hb_dependency_mutex);spin_lock (&o2hb_live_lock);/* local heartbeat */
@@ -2385,6 +2460,7 @@ static void o2hb_region_dec_user(const c unlock:spin_unlock (&o2hb_live_lock); +mutex_unlock (&o2hb_dependency_mutex); } int o2hb_register_callback(const char *region_uuid,
Greg Kroah-Hartman
maintainerPATCH 184/1424 ocfs2: cluster: avoid lock order inversion in o2hb_region_pin() from drop_item
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joseph Qi <[email protected]>
commit cd789996db3c87427343f54f509d17810bd7ba7c upstream.
o2hb_heartbeat_group_drop_item() is called from configfs rmdir with the parent directory's inode_lock held. It calls o2hb_region_pin() -> o2nm_depend_item() -> configfs_depend_item(), which acquires the configfs root inode_lock. This creates a parent -> root inode_lock nesting that could deadlock against paths taking root -> parent (e.g. subsystem unregistration).
Fix this by using configfs_depend_item_unlocked() when o2hb_region_pin() is called from a configfs callback context. This variant skips the root inode_lock when caller and target are in the same subsystem, which is safe because VFS already holds a lock preventing unregistration.
Add o2nm_depend_item_unlocked() wrapper and a from_callback parameter to o2hb_region_pin() to select the appropriate variant.
--- fs/ocfs2/cluster/heartbeat.c | 17 ++++++++++------- fs/ocfs2/cluster/nodemanager.c | 6 ++++++ fs/ocfs2/cluster/nodemanager.h | 1 + 3 files changed, 17 insertions(+), 7 deletions(-)
fs/ocfs2/cluster/heartbeat.c +10 -7
@@ -145,7 +145,7 @@ static unsigned int o2hb_dependent_users* In global heartbeat mode, we pin/unpin all o2hb regions. This solution * works for both file system and userdlm domains. */ -static int o2hb_region_pin (const char *region_uuid); +static int o2hb_region_pin (const char *region_uuid,bool from_callback);static void o2hb_region_unpin (const char *region_uuid);/* Only sets a new threshold if there are no active regions.
@@ -2123,7 +2123,7 @@ static void o2hb_heartbeat_group_drop_itif (bitmap_weight (o2hb_quorum_region_bitmap,O2NM_MAX_REGIONS ) <=O2HB_PIN_CUT_OFF ) -o2hb_region_pin (NULL ); +o2hb_region_pin (NULL , true); unlock:spin_unlock (&o2hb_live_lock);
@@ -2264,7 +2264,7 @@ EXPORT_SYMBOL_GPL(o2hb_setup_callback);* In local, we only pin the matching region. In global we pin all the active * regions. */ -static int o2hb_region_pin (const char *region_uuid) +static int o2hb_region_pin (const char *region_uuid,bool from_callback) {int ret =0 , found;struct o2hb_region *reg, *pinned;
@@ -2319,7 +2319,10 @@ static int o2hb_region_pin(const char *rspin_unlock (&o2hb_live_lock);/* Ignore ENOENT only for local hb (userdlm domain) */ - ret =o2nm_depend_item (&pinned->hr_item); +if (from_callback) + ret =o2nm_depend_item_unlocked (&pinned->hr_item); +else + ret =o2nm_depend_item (&pinned->hr_item);spin_lock (&o2hb_live_lock);if (!ret) {
@@ -2417,8 +2420,8 @@ static int o2hb_region_inc_user(const ch/* local heartbeat */ if (!o2hb_global_heartbeat_active ()) { - ret =o2hb_region_pin (region_uuid); - goto unlock; + ret =o2hb_region_pin (region_uuid, false); + goto unlock; }/*
@@ -2431,7 +2434,7 @@ static int o2hb_region_inc_user(const chif (bitmap_weight (o2hb_quorum_region_bitmap,O2NM_MAX_REGIONS ) <=O2HB_PIN_CUT_OFF ) - ret =o2hb_region_pin (NULL ); + ret =o2hb_region_pin (NULL , false); unlock:spin_unlock (&o2hb_live_lock);
fs/ocfs2/cluster/nodemanager.c +6 -0
@@ -776,6 +776,12 @@ int o2nm_depend_item(struct config_itemreturn configfs_depend_item (&o2nm_cluster_group.cs_subsys, item); } +int o2nm_depend_item_unlocked (struct config_item *item) +{ +return configfs_depend_item_unlocked (&o2nm_cluster_group.cs_subsys, + item); +} +void o2nm_undepend_item (struct config_item *item) {configfs_undepend_item (item);
fs/ocfs2/cluster/nodemanager.h +1 -0
@@ -64,6 +64,7 @@ void o2nm_node_get(struct o2nm_node *nodvoid o2nm_node_put (struct o2nm_node *node);int o2nm_depend_item (struct config_item *item); +int o2nm_depend_item_unlocked (struct config_item *item);void o2nm_undepend_item (struct config_item *item);int o2nm_depend_this_node (void );void o2nm_undepend_this_node (void );
Greg Kroah-Hartman
maintainerPATCH 185/1424 ocfs2: cluster: fix o2hb_dependent_users leak on pin failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joseph Qi <[email protected]>
commit 12c2ab42dbe227956c765e2674364bfca5de0533 upstream.
In o2hb_region_inc_user(), o2hb_dependent_users is incremented unconditionally before calling o2hb_region_pin(). If the pin fails, the counter is never decremented and any partially-pinned regions are never unpinned, since the caller does not call o2hb_region_dec_user() on error.
The leaked counter causes subsequent o2hb_region_inc_user() calls to skip pinning entirely (the > 1 check), leaving heartbeat regions unprotected.
Fix by rolling back on failure: call o2hb_region_unpin(NULL) to release any partially-pinned regions and decrement o2hb_dependent_users to restore the pre-increment state.
--- fs/ocfs2/cluster/heartbeat.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
fs/ocfs2/cluster/heartbeat.c +6 -1
@@ -2433,8 +2433,13 @@ static int o2hb_region_inc_user(const ch goto unlock;if (bitmap_weight (o2hb_quorum_region_bitmap, -O2NM_MAX_REGIONS ) <=O2HB_PIN_CUT_OFF ) +O2NM_MAX_REGIONS ) <=O2HB_PIN_CUT_OFF ) { ret =o2hb_region_pin (NULL , false); +if (ret) { +o2hb_region_unpin (NULL ); + o2hb_dependent_users--; + } + } unlock:spin_unlock (&o2hb_live_lock);
Greg Kroah-Hartman
maintainerPATCH 186/1424 ocfs2: fix readdir position truncation on 32-bit kernels
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
commit a63308ab426f3a3c7e33b02c150ea59054620261 upstream.
In ocfs2_dir_foreach_blk_el(), the directory cookie position is rebuilt with
ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1)) | offset;
ctx->pos is loff_t (signed 64-bit), while sb->s_blocksize is unsigned long. On 32-bit kernels unsigned long is 32-bit, so the mask
~(sb->s_blocksize - 1)
is computed as a 32-bit unsigned value (e.g. 0xfffff000 for a 4 KiB block size). In the AND expression with the 64-bit ctx->pos, that unsigned operand is zero-extended to 64 bits per the usual arithmetic conversions, yielding 0x00000000fffff000. The high 32 bits of ctx->pos are silently cleared, even though directory size is allowed to exceed 4 GiB.
When readdir() crosses the 4 GiB boundary on a 32-bit kernel the position is reset back into the first 4 GiB block, making the re-validation path re-enumerate already-returned dirents indefinitely.
This is ocfs2_dir_foreach_blk_el(), the extent-list readdir path taken for all non-inline directories, so a directory large enough to cross 4 GiB reaches it.
This is the same class of bug that commit 3dce5bb82c97 ("exfat: Fix bitwise operation having different size") fixed in exfat, and the fix mirrors the equivalent ext4 fix in this series. Cast the operand to loff_t so the mask is 64-bit before the AND:
ctx->pos = (ctx->pos & ~((loff_t)sb->s_blocksize - 1)) | offset;
64-bit kernels are unaffected.
--- fs/ocfs2/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/ocfs2/dir.c +1 -1
@@ -1883,7 +1883,7 @@ static int ocfs2_dir_foreach_blk_el(stru i +=le16_to_cpu (de->rec_len); } offset = i; - ctx->pos = (ctx->pos & ~(sb->s_blocksize -1 )) + ctx->pos = (ctx->pos & ~((loff_t )sb->s_blocksize -1 )) | offset; *f_version =inode_query_iversion (inode); }
Greg Kroah-Hartman
maintainerPATCH 187/1424 openrisc: fix arbitrary kernel memory access via or1k_atomic syscall
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Ahmet Memis <[email protected]>
commit 78004e9a87f240df03e2f73120d291763c32e0a7 upstream.
sys_or1k_atomic() (syscall 244 in the "or1k" ABI) takes two user pointers, v1 and v2, and swaps the words they point to in hand-written assembly.
l.lwz r29,0(r4)
l.lwz r27,0(r5)
l.sw 0(r4),r27
l.sw 0(r5),r29The pointers are not checked with access_ok(). The four memory accesses also have no exception table entries.
A caller passes a kernel address as either pointer, and the syscall reads from and writes to it directly.
This gives an unprivileged process a kernel read/write primitive. It overwrites kernel data such as the sys_call_table, gaining code execution in kernel context.
Check both pointers before entering the critical section. Add fixups for the four memory accesses so faults on valid but unmapped user addresses return -EFAULT.
--- arch/openrisc/kernel/entry.S | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-)
arch/openrisc/kernel/entry.S +39 -4
@@ -1230,15 +1230,50 @@ _no_syscall_trace: * */ +/* Keep this literal; hi()/lo() can't use the UL-suffixed TASK_SIZE. */ +#define OR1K_ATOMIC_ADDR_LIMIT 0x7ffffffc + ENTRY(sys_or1k_atomic) /* FIXME: This ignores r3 and always does an XCHG */ + + /* Check both user pointers before accessing them. */ + l.movhi r13,hi(OR1K_ATOMIC_ADDR_LIMIT) + l.ori r13,r13,lo(OR1K_ATOMIC_ADDR_LIMIT) + l.sfgtu r4,r13 + l.bf 9f + l.nop + l.sfgtu r5,r13 + l.bf 9f + l.nop + DISABLE_INTERRUPTS(r17,r19) - l.lwz r29,0(r4) - l.lwz r27,0(r5) - l.sw 0(r4),r27 - l.sw 0(r5),r29 +10: l.lwz r29,0(r4) +11: l.lwz r27,0(r5) +12: l.sw 0(r4),r27 +13: l.sw 0(r5),r29 ENABLE_INTERRUPTS(r17) l.jr r9 l.or r11,r0,r0 + /* + * Either pointer was outside user space, or turned out to be + * unmapped/inaccessible when we actually touched it. + */ +9: l.jr r9 + l.addi r11,r0,-EFAULT + + .section .fixup, "ax" +14: + ENABLE_INTERRUPTS(r17) + l.j 9b + l.nop + .previous + + .section __ex_table, "a" + .long 10b, 14b + .long 11b, 14b + .long 12b, 14b + .long 13b, 14b + .previous + /* ============================================================[ EOF ]=== */
Greg Kroah-Hartman
maintainerPATCH 188/1424 openvswitch: only skb_tx_error() a packet we are about to drop
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
commit 0dbc2398fca3bb33eda963849f865ddb1b3aa05e upstream.
queue_userspace_packet() borrows the packet skb -- it only copies it into a private netlink message (user_skb) and does not own it; on return do_execute_actions() keeps forwarding it through the flow's remaining actions. Its error path nevertheless calls skb_tx_error(skb), which via skb_zcopy_clear() does skb_shinfo(skb)->flags &= ~SKBFL_ALL_ZEROCOPY, stripping SKBFL_SHARED_FRAG from that live skb (skb_tx_error()'s kerneldoc says "skb must be freed afterwards").
For a MSG_ZEROCOPY skb carrying page-cache frags, SKBFL_SHARED_FRAG is what makes esp_input() skb_cow_data() before in-place AEAD; once it is stripped a later local ESP-in-UDP delivery decrypts in place over pages the sender does not own -- an unprivileged page-cache write (the "Fragnesia" primitive).do_execute_actions() ignores output_userspace()'s return value, so any action after a failed USERSPACE upcall inherits the stripped skb.
Move the skb_tx_error() to the flow-miss drop path - the "default" branch of ovs_dp_process_packet()'s switch(error), before kfree_skb().
The call has been here since commit 36d5fe6a0007 ("core, nfqueue,
openvswitch: Orphan frags in skb_zerocopy and handle errors") but was harmless until esp_input() began relying on SKBFL_SHARED_FRAG to gate in-place decrypt; only then did stripping it on a still-forwarded skb become a page-cache write primitive.
--- net/openvswitch/datapath.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
net/openvswitch/datapath.c +1 -2
@@ -282,6 +282,7 @@ void ovs_dp_process_packet(struct sk_bufconsume_skb (skb);break ;default : +skb_tx_error (skb);kfree_skb (skb);break ; }
@@ -582,8 +583,6 @@ static int queue_userspace_packet(struct err =genlmsg_unicast (ovs_dp_get_net (dp), user_skb, upcall_info->portid); user_skb =NULL ; out: -if (err) -skb_tx_error (skb);consume_skb (user_skb);consume_skb (nskb);
Greg Kroah-Hartman
maintainerPATCH 189/1424 ALSA: ump: Fix corrupted data bytes at MIDI 1.0 SysEx to UMP conversion
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit 8a906c0b4f1ba123a95c166f644d2383bf30a420 upstream.
The cvt_legacy_sysex_to_ump() initialises only the first word of the output packet and ORs the data bytes into it. The second word is left alone, and the conversion context is kept across calls, so it still carries the previous packet's bytes. Those stale bits corrupt the new data. Any SysEx longer than six data bytes is affected.
A SysEx with the twelve data bytes 01..0c comes out as:
30160102 03040506
30260708 0b0e0f0e
The second packet declares six data bytes and four of them are wrong, inside the declared length.
The sibling cvt_legacy_cmd_to_ump() already clears the second word. Do the same here.
--- sound/core/ump_convert.c | 1 + 1 file changed, 1 insertion(+)
sound/core/ump_convert.c +1 -0
@@ -258,6 +258,7 @@ static int cvt_legacy_sysex_to_ump(struct ump_cvt_to_ump *cvt,else status =UMP_SYSEX_STATUS_CONTINUE ; *data =ump_compose (UMP_MSG_TYPE_DATA , group, status, cvt->len); + data[1 ] =0 ; offset =8 ;for (i =0 ; i < cvt->len; i++) { *data |= cvt->buf[i] << offset;
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 190/1424 arm64: compat: Fix decrementing LDM/STM alignment emulation
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
commit f5b8b9037df387394a73aab47c5437bbac975077 upstream.
The compat alignment emulator inherited unsigned long data addresses from the 32-bit ARM implementation.
In do_alignment_ldmstm(), nr_regs is an unsigned int holding the transfer size. The function uses the same address addition for both transfer directions, negating nr_regs first for a decrementing LDM or STM. The 32-bit negation wraps before the addition, so the handler adds nearly 4 GiB instead of subtracting the transfer size.
The resulting address lies outside the compat task's address space, so decrementing LDM/STM emulation fails, while incrementing forms work.
For example, a backwards-moving copy routine using decrementing LDM/STM can take an alignment fault when called with unaligned pointers. The compat handler should emulate the transfer, but this bug instead causes SIGBUS.
The offset negated in do_alignment_finish_ldst() is offset_union.un, which is already unsigned long and does not have this width mismatch.
Make nr_regs unsigned long so its negation and the address arithmetic use the same width.
--- arch/arm64/kernel/compat_alignment.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/kernel/compat_alignment.c +2 -2
@@ -114,8 +114,8 @@ do_alignment_ldrdstrd(unsigned long addrstatic int do_alignment_ldmstm (unsigned long addr,u32 instr,struct pt_regs *regs) { -unsigned int rd, rn, nr_regs, regbits; -unsigned long eaddr, newaddr; +unsigned int rd, rn, regbits; +unsigned long eaddr, newaddr, nr_regs;unsigned int val;/* count the number of registers in the mask to be transferred */
Greg Kroah-Hartman
maintainerPATCH 191/1424 ASoC: amd: yc: Add DMI entry for MSI Thin A15 B7UC
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christopher Tolang <[email protected]>
commit e2aa5ad3be41accfcdcccc62348f21af7baa3a38 upstream.
This model requires an additional detection quirk to enable the internal microphone.
--- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+)
sound/soc/amd/yc/acp6x-mach.c +7 -0
@@ -742,6 +742,13 @@ static const struct dmi_system_id yc_acp .driver_data = &acp6x_card, .matches = {DMI_MATCH (DMI_BOARD_VENDOR ,"Micro-Star International Co., Ltd." ), + DMI_MATCH(DMI_PRODUCT_NAME, "Thin A15 B7UC"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { +DMI_MATCH (DMI_BOARD_VENDOR ,"Micro-Star International Co., Ltd." ), DMI_MATCH(DMI_PRODUCT_NAME, "Thin A15 B7VE"), } },
Greg Kroah-Hartman
maintainerPATCH 192/1424 hwmon: (max6621) fix negative temperature offset and crit readings
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cong Nguyen <[email protected]>
commit acc52bd431e2d8698fae8d82a74ac45d79b62e0a upstream.
max6621_read() reads the CONFIG2 offset and the critical alert threshold registers into a u32 and scales them without sign extension:
/* offset */ *val = (regval >> MAX6621_REG_TEMP_SHIFT) * 1000L; /* crit */ *val = regval * 1000L;
Both attributes are writable and their write paths clamp to a negative minimum and encode negative values, so a value written as negative is read back as a large positive number. For example, writing a -10 degrees C offset stores max6621_temp_mc2reg(-10000) = (-10 << 6) = 0xfd80; the read then computes 0xfd80 >> 6 = 1014 -> 1014000 instead of -10000.
Cast the register value to s16 before scaling so the read preserves the sign the write path encodes. The temperature input path already uses an s8 intermediate and is left unchanged.
--- drivers/hwmon/max6621.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hwmon/max6621.c +2 -2
@@ -239,7 +239,7 @@ max6621_read(struct device *dev, enum hwif (ret)return ret; - *val = (regval >>MAX6621_REG_TEMP_SHIFT ) * + *val = ((s16 )regval >>MAX6621_REG_TEMP_SHIFT ) *1000L ;break ;
@@ -254,7 +254,7 @@ max6621_read(struct device *dev, enum hwif (ret)return ret; - *val = regval *1000L ; + *val = (s16 )regval *1000L ;break ;case hwmon_temp_crit_alarm:
Greg Kroah-Hartman
maintainerPATCH 193/1424 hwmon: (max6621) fix temperature clamp range
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cong Nguyen <[email protected]>
commit 24fbeb83d9b750a36da42cb835a154d80fd3d495 upstream.
MAX6621_TEMP_INPUT_MIN and MAX6621_TEMP_INPUT_MAX are used to clamp the writable offset and critical thresholds. They are defined as -127000 and 128000.
The driver decodes the temperature through an s8 and its own comment in max6621_read() documents an 8-bit two's complement value, whose range is -128 to +127 degrees C. The current limits therefore reject the valid -128 degrees C and accept +128 degrees C, which does not fit the 8-bit range.
Correct the limits to -128000 and 127000.
--- drivers/hwmon/max6621.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hwmon/max6621.c +2 -2
@@ -17,8 +17,8 @@#define MAX6621_DRV_NAME "max6621"#define MAX6621_TEMP_INPUT_REG_NUM 9 -#define MAX6621_TEMP_INPUT_MIN -127000 -#define MAX6621_TEMP_INPUT_MAX 128000 +#define MAX6621_TEMP_INPUT_MIN -128000 +#define MAX6621_TEMP_INPUT_MAX 127000#define MAX6621_TEMP_ALERT_CHAN_SHIFT 1#define MAX6621_TEMP_S0D0_REG 0x00
Greg Kroah-Hartman
maintainerPATCH 194/1424 lockd: pin next file across nlm_inspect_file lock-drop
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 526c49cff3f72c3ec74752016380c7567040581b upstream.
nlm_traverse_files() pins the current file with f_count++ across a mutex_unlock for nlm_inspect_file(), but nothing pins the saved next pointer. A concurrent nlm_release_file() can kfree the next file during the unlock window, and the iterator dereferences freed memory on the next loop step.
Pin both current and next before the lock-drop. Advance by swapping the pinned cursors at the end of each iteration so next is always held alive across the unlock.
Always call nlm_file_release() after dropping the iteration pin, regardless of whether the file matched the predicate. Use nlm_file_inuse(), which does a live walk of the inode lock list, rather than the cached f_locks field, so skipped files that never ran nlm_inspect_file() are evaluated correctly.
Because every file in a hash bucket is now pinned and released, files skipped by the is_failover_file predicate that have no locks, blocks, shares, or external references are deleted during traversal. The old code never evaluated skipped files for cleanup. The new behavior is intentional: such files are stale and should not persist in the table.
--- fs/lockd/svcsubs.c | 53 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 23 deletions(-)
fs/lockd/svcsubs.c +30 -23
@@ -286,12 +286,10 @@ nlm_file_inuse(struct nlm_file *file)return 0 ; } -static void nlm_close_files (struct nlm_file *file) +static void nlm_file_release (struct nlm_file *file) { -if (file->f_file[O_RDONLY ]) - nlmsvc_ops->fclose (file->f_file[O_RDONLY ]); -if (file->f_file[O_WRONLY ]) - nlmsvc_ops->fclose (file->f_file[O_WRONLY ]); +if (!nlm_file_inuse (file)) +nlm_delete_file (file); }/*
@@ -301,32 +299,41 @@ static int nlm_traverse_files(void *data, nlm_host_match_fn_t match, int (*is_failover_file)(void *data, struct nlm_file *file)) { -struct hlist_node *next; -struct nlm_file *file; +struct nlm_file *file, *next;int i, ret =0 ;mutex_lock (&nlm_file_mutex);for (i =0 ; i <FILE_NRHASH ; i++) { -hlist_for_each_entry_safe (file, next, &nlm_files[i], f_list) { -if (is_failover_file && !is_failover_file (data, file)) -continue ; + file =hlist_entry_safe (nlm_files[i].first, + struct nlm_file, f_list); +if (file) file->f_count++; -mutex_unlock (&nlm_file_mutex); +while (file) { +/* +* Pin the next neighbour before we drop the mutex +* for nlm_inspect_file(); a concurrent +* nlm_release_file() under the same mutex would +* otherwise be free to unlink and kfree it during +* the unlock window, leaving us to dereference a +* freed slab when we walked to next afterwards. +*/ + next =hlist_entry_safe (file->f_list.next, + struct nlm_file, f_list); +if (next) + next->f_count++; -/* Traverse locks, blocks and shares of this file -* and update file->f_locks count */ -if (nlm_inspect_file (data, file, match)) - ret =1 ; +if (!is_failover_file ||is_failover_file (data, file)) { +mutex_unlock (&nlm_file_mutex); -mutex_lock (&nlm_file_mutex); - file->f_count--; -/* No more references to this file. Let go of it. */ -if (list_empty (&file->f_blocks) && !file->f_locks - && !file->f_shares && !file->f_count) { -hlist_del (&file->f_list); -nlm_close_files (file); -kfree (file); +if (nlm_inspect_file (data, file, match)) + ret =1 ; + +mutex_lock (&nlm_file_mutex); } + + file->f_count--; +nlm_file_release (file); + file = next; } }mutex_unlock (&nlm_file_mutex);
Greg Kroah-Hartman
maintainerPATCH 195/1424 nvme: nvme-fc: Fix nvme_fc_create_hw_io_queues() queue deletion in error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ewan D. Milne <[email protected]>
commit 22eb631bf86ee3246f47885e4fa94154a46863e4 upstream.
nvme_fc_create_hw_io_queues() will call __nvme_fc_delete_hw_queue() for the last queue on which __nvme_fc_create_hw_queue() reported an error when deleting all the io queues if they cannot all be created. This is incorrect since the last queue did not actually get created.
The most recent change to this code was commit 17a1ec08ce70 ("nvme/fc: simplify error handling of nvme_fc_create_hw_io_queues") which moved the cleanup to thedelete_queues: label and changed the loop bounds, however the code was not correct prior to this change in a different way. The original commit e399441de911 ("nvme-fabrics: Add host support for FC transport") had a different error which called __nvme_fc_delete_hw_queue() on queue index 0 which is used for the admin queue.
Fix this by correcting the initial loop index when deleting the io queues.
--- drivers/nvme/host/fc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/host/fc.c +1 -1
@@ -2310,7 +2310,7 @@ nvme_fc_create_hw_io_queues(struct nvme_return 0 ; delete_queues: -for (; i >0 ; i--) +for (--i; i >0 ; i--)__nvme_fc_delete_hw_queue (ctrl, &ctrl->queues[i], i);return ret; }
Greg Kroah-Hartman
maintainerPATCH 196/1424 nvme: zero the discard fallback page
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yehyeong Lee <[email protected]>
commit bededeaaeff404978a5a8e2a605a6c3017cddd3e upstream.
nvme_setup_discard() always maps sizeof(struct nvme_dsm_range) * NVME_DSM_MAX_RANGES = 4096 bytes as the DSM payload however many ranges the command declares, because some devices ignore the 'Number of Ranges' field - the Fixes: commit records two that read past the declared ranges. A single-range discard fills only the first 16 bytes.
Normally the buffer comes from kzalloc() and the other 4080 bytes are zero. When that allocation fails the code falls back to the per-controller ctrl->discard_page, which nvme_init_ctrl() obtains with alloc_page(GFP_KERNEL) and nothing ever zeroes, so those 4080 bytes are whatever the page last held and are handed to the controller. Reaching it requires the kzalloc(GFP_ATOMIC | __GFP_NOWARN) to fail, that is memory pressure; it is not remotely triggerable. Failing the allocation under KMSAN reproduces it, with the leaked tail full of vmemmap struct page pointers. The extent in the report is a partial transfer of the payload, not the whole 4096 bytes; the 16-byte boundary in it is the one declared range:
[ 11.991601] BUG: KMSAN: uninit-value in dma_map_phys+0x14c8/0x1900 [ 11.991969] dma_map_phys+0x14c8/0x1900 [ 11.992220] dma_map_page_attrs+0xcf/0x130 [ 11.992485] e1000_xmit_frame+0x4099/0x6d10 [ 11.992768] dev_hard_start_xmit+0x22f/0xa80 [ 11.993068] sch_direct_xmit+0x35c/0xcb0 [ 11.993315] __dev_queue_xmit+0x1ee5/0x5eb0 [ 11.993608] ip_finish_output2+0x1903/0x1c30 [ 11.993881] ip_finish_output+0x288/0x870 [ 11.994125] ip_output+0x15e/0x400 [ 11.994365] __ip_queue_xmit+0x1e85/0x1fb0 [ 11.994639] ip_queue_xmit+0x60/0x80 [ 11.994899] __tcp_transmit_skb+0x4e71/0x5fa0 [ 11.995210] tcp_write_xmit+0x3a36/0x9160 [ 11.995533] __tcp_push_pending_frames+0xc5/0x3c0 [ 11.995854] tcp_push+0x7dc/0x840 [ 11.996076] tcp_sendmsg_locked+0x766c/0x8400 [ 11.996371] tcp_sendmsg+0x4b/0x90 [ 11.996572] inet_sendmsg+0x134/0x2a0 [ 11.996823] __sock_sendmsg+0x265/0x360 [ 11.997076] sock_sendmsg+0x100/0x1e0 [ 11.997293] nvme_tcp_try_send+0x196f/0x6370 [ 11.997605] nvme_tcp_queue_rq+0x1d54/0x20b0 [ 11.997882] blk_mq_dispatch_rq_list+0x5ee/0x2e50 [ 11.998175] __blk_mq_sched_dispatch_requests+0x16dc/0x24a0 [ 11.998539] blk_mq_sched_dispatch_requests+0x11b/0x2c0 [ 11.998865] blk_mq_run_work_fn+0x13b/0x280 [ 11.999146] process_scheduled_works+0x966/0x1ad0 [ 11.999465] worker_thread+0xe44/0x1480 [ 11.999709] kthread+0x53b/0x600 [ 11.999927] ret_from_fork+0x29f/0x7c0 [ 12.000191] ret_from_fork_asm+0x1a/0x30 [ 12.000460] [ 12.000558] Uninit was created at: [ 12.000788] __alloc_frozen_pages_noprof+0x8bf/0xd30 [ 12.001096] alloc_pages_mpol+0x1d0/0x5f0 [ 12.001326] alloc_pages_noprof+0x102/0x290 [ 12.001627] nvme_init_ctrl+0x5a3/0x9f0 [ 12.001891] nvme_tcp_create_ctrl+0xd75/0x19b0 [ 12.002170] nvmf_dev_write+0x4c68/0x4fd0 [ 12.002426] vfs_write+0x587/0x1a10 [ 12.002636] __x64_sys_write+0x207/0x4f0 [ 12.002874] x64_sys_call+0x2ff0/0x3ea0 [ 12.003123] do_syscall_64+0x147/0x3b0 [ 12.003400] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 12.003680] [ 12.003777] Bytes 16-2843 of 2844 are uninitialized [ 12.004068] Memory access of size 2844 starts at ffff888109f82000 [ 12.004412] [ 12.004530] CPU: 0 UID: 0 PID: 101 Comm: kworker/0:1H Not tainted 7.2.0-rc5-NVMECTL-gf5098b6bae76 #1 PREEMPT(lazy) [ 12.005127] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 12.005762] Workqueue: kblockd blk_mq_run_work_fn [ 12.006073] =====================================================
Allocate the page with __GFP_ZERO. The single allocation site covers every use of it: bytes no discard has written stay zero, and bytes one did write hold that controller's own range list, which it has already been sent.
--- drivers/nvme/host/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/host/core.c +1 -1
@@ -4534,7 +4534,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrBUILD_BUG_ON (NVME_DSM_MAX_RANGES *sizeof (struct nvme_dsm_range ) >PAGE_SIZE ); - ctrl->discard_page =alloc_page (GFP_KERNEL ); + ctrl->discard_page =alloc_page (GFP_KERNEL | __GFP_ZERO);if (!ctrl->discard_page) { ret = -ENOMEM ; goto out;
Greg Kroah-Hartman
maintainerPATCH 197/1424 nvme-pci: disable controller on admin queue IRQ setup failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 08660a5c8d497f43191635d97efd31cd35051f15 upstream.
nvme_pci_configure_admin_queue() enables the controller and then requests the admin queue interrupt. If queue_request_irq() fails it returns without disabling the controller, and no caller compensates: nvme_pci_enable() only frees the IRQ vectors and calls pci_disable_device(), after which nvme_dev_disable() treats the controller as dead and skips nvme_disable_ctrl(). The controller is left enabled (CC.EN set) on this error path.
Disable it in the failure path, while the PCI device is still enabled so the CC.EN clear handshake completes.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/nvme/host/pci.c | 1 + 1 file changed, 1 insertion(+)
drivers/nvme/host/pci.c +1 -0
@@ -1843,6 +1843,7 @@ static int nvme_pci_configure_admin_queu result =queue_request_irq (nvmeq);if (result) { dev->online_queues--; +nvme_disable_ctrl (&dev->ctrl, false);return result; }
Greg Kroah-Hartman
maintainerPATCH 198/1424 nvme-tcp: do not accept C2HData based on blk_rq_payload_bytes() alone
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yehyeong Lee <[email protected]>
commit 3a4aa9e6ad3e35f8e24d5eaf38ee4d437075fb36 upstream.
Commit 25e5cb780e62 ("nvme-tcp: fix possible crash in write_zeroes processing") established that blk_rq_payload_bytes() must not be read without first checking blk_rq_nr_phys_segments(), and recorded the result in nvme_tcp_setup_cmd_pdu() as req->data_len. The receive side was left as it was.
The two differ for REQ_OP_WRITE_ZEROES, which has no physical segments but a non-zero blk_rq_bytes(), so setup leaves req->iter untouched while the receive gate lets a C2HData through and nvme_tcp_recv_data() copies into whatever the previous command on that tag left there. The driver-private area is zeroed only when the tag set is allocated.
Reproduced with a test target that leaves a residual iterator on a tag and then sends a C2HData for a WRITE_ZEROES command on the same tag:
BUG: KASAN: wild-memory-access in _copy_to_iter+0x642/0x1330 Write of size 512 at addr ffe728c2175dfa81 by task kworker/0:1H/103 CPU: 0 UID: 0 PID: 103 Comm: kworker/0:1H Not tainted 7.2.0-rc5-NVMETCP-gf5098b6bae76 #1 PREEMPT(lazy) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Workqueue: nvme_tcp_wq nvme_tcp_io_work Call Trace: <TASK> dump_stack_lvl+0x53/0x70 kasan_report+0xce/0x100 ? _copy_to_iter+0x642/0x1330 kasan_check_range+0x105/0x1b0 __asan_memcpy+0x3c/0x60 _copy_to_iter+0x642/0x1330 ? __pfx_sock_has_perm+0x10/0x10 ? worker_thread+0x45b/0xd10 ? __pfx__copy_to_iter+0x10/0x10 ? _raw_spin_lock_bh+0x83/0xe0 ? __pfx__raw_spin_lock_bh+0x10/0x10 __skb_datagram_iter+0xf3/0x820 ? __pfx_simple_copy_to_iter+0x10/0x10 ? __asan_memcpy+0x3c/0x60 ? skb_copy_bits+0x58d/0x830 skb_copy_datagram_iter+0x37/0x120 nvme_tcp_recv_skb+0xa07/0x4320 ? __pfx_nvme_tcp_recv_skb+0x10/0x10 __tcp_read_sock+0x1ab/0x810 ? __pfx_nvme_tcp_recv_skb+0x10/0x10 ? __pfx_lock_sock_nested+0x10/0x10 ? __pfx___tcp_read_sock+0x10/0x10 nvme_tcp_try_recv+0x152/0x1e0 ? __pfx_nvme_tcp_try_recv+0x10/0x10 ? __pfx_mutex_unlock+0x10/0x10 nvme_tcp_io_work+0x1e4/0x6c0 ? __schedule+0x181a/0x49f0 ? __pfx_nvme_tcp_io_work+0x10/0x10 process_one_work+0x633/0x1030
Keep the blk_rq_payload_bytes() test and add req->data_len to it. The old test is what rejects a C2HData naming a tag that is no longer in flight, because blk_update_request() zeroes rq->__data_len on completion; req->data_len and req->curr_bio are driver-private and survive completion, so they cannot stand in for it. Setup initialises the iterator only when both req->curr_bio and req->data_len are set, so the gate now tests the same two.
--- drivers/nvme/host/tcp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/nvme/host/tcp.c +3 -1
@@ -571,6 +571,7 @@ static int nvme_tcp_process_nvme_cqe(strstatic int nvme_tcp_handle_c2h_data (struct nvme_tcp_queue *queue,struct nvme_tcp_data_pdu *pdu) { +struct nvme_tcp_request *req;struct request *rq; rq =nvme_find_rq (nvme_tcp_tagset (queue), pdu->command_id);
@@ -581,7 +582,8 @@ static int nvme_tcp_handle_c2h_data(strureturn -ENOENT ; } -if (!blk_rq_payload_bytes (rq)) { + req =blk_mq_rq_to_pdu (rq); +if (!blk_rq_payload_bytes (rq) || !req->curr_bio || !req->data_len) {dev_err (queue->ctrl->ctrl.device,"queue %d tag %#x unexpected data\n" ,nvme_tcp_queue_id (queue), rq->tag);
Greg Kroah-Hartman
maintainerPATCH 199/1424 nvme-tcp: fix host memory disclosure on R2T for a read command
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yehyeong Lee <[email protected]>
commit 6efbc52237facda35d2d874fe1765bb4839275d8 upstream.
nvme_tcp_handle_r2t() does not check the direction of the request the R2T refers to. A malicious controller can send an R2T for a READ and the host will answer it: nvme_tcp_setup_h2c_data_pdu() builds the H2CData header and nvme_tcp_try_send_data() sends the request's data buffer. That buffer is the READ destination, so its contents go to the controller.
The command then completes normally and nothing is logged.
Against a test controller that answers every READ with an R2T, a 4096 byte buffered read returned all 4096 bytes, split over two R2Ts. The pages contained stale kernel data, including an array of struct page pointers.
Reject an R2T for a request that is not a write.
--- drivers/nvme/host/tcp.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/nvme/host/tcp.c +7 -0
@@ -677,6 +677,13 @@ static int nvme_tcp_handle_r2t(struct nv } req =blk_mq_rq_to_pdu (rq); +if (unlikely (rq_data_dir (rq) !=WRITE )) { +dev_err (queue->ctrl->ctrl.device, +"req %d unexpected r2t for a non-write command\n" , + rq->tag); +return -EPROTO ; + } +if (unlikely (!r2t_length)) { dev_err(queue->ctrl->ctrl.device, "req %d r2t len is %u, probably a bug...\n",
Greg Kroah-Hartman
maintainerPATCH 200/1424 nvme-tcp: reject a read that transferred too few bytes
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yehyeong Lee <[email protected]>
commit 7fa3f73f6c8ddc5f0425b50fb2a626a782ef7d12 upstream.
nvme_tcp_recv_data() completes a request once the current C2HData PDU has been consumed. Nothing compares the total bytes received against the length the command asked for: struct nvme_tcp_request has no receive-side counter, queue->data_remaining is per queue, and blk_mq_end_request() completes for blk_rq_bytes(rq) unconditionally with no residual concept anywhere above.
A controller can therefore answer a 4096-byte read with 512 bytes and have it reported as a complete read; user space then gets 4096 bytes of which 3584 are whatever was already in the page. I reproduced that with a test target.
Count the bytes received and refuse to complete a successful read whose count does not match, at the two NVME_TCP_F_DATA_SUCCESS paths and in nvme_tcp_process_nvme_cqe(). The success test shifts req->status right by one, because the driver keeps the wire value there and shifts it on completion, so the check must see what the completion path will see. Only REQ_OP_READ is checked, because there the length comes from the sectors the request covers; a passthrough command is built by its submitter, which picks both command and buffer, so the kernel has nothing to compare against.
--- drivers/nvme/host/tcp.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+)
drivers/nvme/host/tcp.c +34 -0
@@ -93,6 +93,7 @@ struct nvme_tcp_request {struct bio *curr_bio;struct iov_iter iter; +u32 data_recvd;/* send state */ size_t offset;
@@ -542,6 +543,29 @@ static void nvme_tcp_error_recovery(struqueue_work (nvme_reset_wq, &to_tcp_ctrl (ctrl)->err_work); } +/* +* NVMe has no short read: a read that completes successfully must +* have transferred everything it asked for. +*/ +static bool nvme_tcp_data_in_short (struct nvme_tcp_queue *queue, +struct request *rq) +{ +struct nvme_tcp_request *req =blk_mq_rq_to_pdu (rq); + +if (le16_to_cpu (req->status) >>1 ) +return false; +if (req_op (rq) !=REQ_OP_READ || !req->data_len) +return false; +if (likely (req->data_recvd == req->data_len)) +return false; + +dev_err (queue->ctrl->ctrl.device, +"queue %d tag %#x short data-in: got %u of %u\n" , +nvme_tcp_queue_id (queue), rq->tag, + req->data_recvd, req->data_len); +return true; +} +static int nvme_tcp_process_nvme_cqe (struct nvme_tcp_queue *queue,struct nvme_completion *cqe) {
@@ -561,6 +585,9 @@ static int nvme_tcp_process_nvme_cqe(strif (req->status ==cpu_to_le16 (NVME_SC_SUCCESS )) req->status = cqe->status; +if (unlikely (nvme_tcp_data_in_short (queue, rq))) +return -EPROTO ; +if (!nvme_try_complete_req (rq, req->status, cqe->result))nvme_complete_rq (rq); queue->nr_cqe++;
@@ -869,6 +896,7 @@ static int nvme_tcp_recv_data(struct nvm *len -= recv_len; *offset += recv_len; queue->data_remaining -= recv_len; + req->data_recvd += recv_len; }if (!queue->data_remaining) {
@@ -877,6 +905,8 @@ static int nvme_tcp_recv_data(struct nvm queue->ddgst_remaining =NVME_TCP_DIGEST_LENGTH ; }else {if (pdu->hdr.flags &NVME_TCP_F_DATA_SUCCESS ) { +if (unlikely (nvme_tcp_data_in_short (queue, rq))) +return -EPROTO ;nvme_tcp_end_request (rq,le16_to_cpu (req->status)); queue->nr_cqe++;
@@ -925,6 +955,9 @@ static int nvme_tcp_recv_ddgst(struct nv pdu->command_id);struct nvme_tcp_request *req =blk_mq_rq_to_pdu (rq); +if (unlikely (nvme_tcp_data_in_short (queue, rq))) +return -EPROTO ; +nvme_tcp_end_request (rq,le16_to_cpu (req->status)); queue->nr_cqe++; }
@@ -2423,6 +2456,7 @@ static blk_status_t nvme_tcp_setup_cmd_p req->status =cpu_to_le16 (NVME_SC_SUCCESS ); req->offset =0 ; req->data_sent =0 ; + req->data_recvd =0 ; req->pdu_len =0 ; req->pdu_sent =0 ; req->h2cdata_left =0 ;
Greg Kroah-Hartman
maintainerPATCH 201/1424 sctp: stop processing a packet once its association is deleted
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunwoo Kim <[email protected]>
commit 47e15a8d12e366d0d261bcbc394394f44418938d upstream.
sctp_endpoint_bh_rcv() looks the association up only when chunk->asoc is NULL, and caches the result in chunk->asoc and chunk->transport without taking a reference.
A packet that matches no association is handed to the endpoint, so a peer can bundle COOKIE ECHO, SHUTDOWN and SHUTDOWN ACK in one packet. The COOKIE ECHO creates the association, the SHUTDOWN chunk caches it, and with the outqueue empty the SHUTDOWN ACK reaches sctp_sf_do_9_2_final(), so the association and its transports are freed.
The endpoint loop has no counterpart to the asoc->base.dead check in sctp_assoc_bh_rcv(). The next chunk writes to last_time_heard in the freed transport and is then passed to sctp_do_sm() with the freed association. The transport is freed through RCU, so this needs the packet to come off the socket backlog, where the loop runs in task context.
The endpoint loop cannot do the same check: it holds no reference on the association, so reading asoc->base.dead would itself be a use-after-free. Mark the packet for discard in the command interpreter, just before it deletes the association. That is also before sctp_inq_free() releases the chunk on the association receive path.
sctp_sf_do_5_2_4_dupcook() issues SCTP_CMD_DELETE_TCB for the temporary association, while the one the packet belongs to stays alive. A restarting peer can bundle DATA behind its COOKIE ECHO, so compare against chunk->asoc and leave that case alone.
--- net/sctp/sm_sideeffect.c | 4 ++++ 1 file changed, 4 insertions(+)
net/sctp/sm_sideeffect.c +4 -0
@@ -1326,6 +1326,10 @@ static int sctp_cmd_interpreter(enum sctsctp_outq_uncork (&asoc->outqueue, gfp); local_cork =0 ; } +/* No chunk left in this packet may use this asoc. */ +if (event_type ==SCTP_EVENT_T_CHUNK && + chunk->asoc == asoc) + chunk->pdiscard =1 ;/* Delete the current association. */ sctp_cmd_delete_tcb (commands, asoc); asoc =NULL ;
Greg Kroah-Hartman
maintainerPATCH 202/1424 sctp: drop a chunk if its transport was removed
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunwoo Kim <[email protected]>
commit 03a9d10ecf71f54b2af8020935f2033d4a132be5 upstream.
sctp_rcv() resolves the transport once per packet and leaves it in chunk->transport. The lookup reference, or the one sctp_add_backlog() takes if the socket is owned by userspace, keeps it around until the chunk has been processed.
An authenticated ASCONF DEL-IP can remove it in the meantime. sctp_assoc_rm_peer() takes the transport out of the association and calls sctp_transport_free(), which tags it dead and drops the reference the association held. There is a window on both paths: the packet can sit on the socket backlog, and on the direct path the lookup completes before bh_lock_sock().
The DATA chunk in that packet puts the removed transport back into asoc->peer.last_data_from. Once the packet is done that reference goes away and the transport is freed by RCU, so the next delayed SACK carries the pointer into the SACK chunk and sctp_outq_select_transport() reads the freed transport's state.
Drop the chunk in sctp_inq_push(), next to the existing rcvr->dead check. Both paths reach it with the association's socket lock held. The peer retransmits it.
--- net/sctp/inqueue.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
net/sctp/inqueue.c +5 -2
@@ -71,8 +71,11 @@ void sctp_inq_free(struct sctp_inq *queu*/ void sctp_inq_push (struct sctp_inq *q,struct sctp_chunk *chunk) { -/* Directly call the packet handling routine. */ -if (chunk->rcvr->dead) { +/* Directly call the packet handling routine. Drop the chunk if the +* receiver or the transport it was looked up on is gone. +*/ +if (chunk->rcvr->dead || + (chunk->transport && chunk->transport->dead)) {sctp_chunk_free (chunk);return ; }
Greg Kroah-Hartman
maintainerPATCH 203/1424 sctp: fix NULL deref on untransmitted RECONF completion
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit 2db9bfa3e27bdea15e05ea70b56bad3d21e570ec upstream.
sctp_process_strreset_outreq(), sctp_process_strreset_addstrm_out() and sctp_process_strreset_resp() complete a pending stream reconfiguration request by stopping the reconf timer on the transport it was sent on:
t = asoc->strreset_chunk->transport; if (timer_delete(&t->reconf_timer)) sctp_transport_put(t);
chunk->transport is assigned by __sctp_packet_append_chunk() when the chunk is appended to an outbound packet, and sctp_outq_flush_ctrl() arms the reconf timer at that same point. A request already published in asoc->strreset_chunk but not yet transmitted has neither, so completing it dereferences NULL.
Two ways to get there. sctp_send_asconf_del_ip() sets asoc->src_out_of_asoc_ok without sending anything when the address being removed is the association's last one, and sctp_outq_flush_ctrl() then leaves every non-ASCONF control chunk queued; as only sctp_process_asconf_ack() clears that flag, it persists. An unprivileged process that removes such an address and then asks for a stream reset panics the kernel from softirq. A peer needs neither ASCONF nor local
help: sctp_cmd_interpreter() uncorks the outqueue only once the whole packet has been processed, so a reply built while walking a RECONF chunk stays untransmitted for the rest of that walk, and one RECONF chunk carrying [Incoming SSN Reset Request, Outgoing SSN Reset Request, Response] -- or two RECONF chunks in one packet -- reaches the same dereference.
KASAN: null-ptr-deref in range [0x00000000000001e8-0x00000000000001ef] RIP: 0010:timer_delete+0x67/0x110 Call Trace: <IRQ> sctp_process_strreset_addstrm_out (net/sctp/stream.c:832) sctp_sf_do_reconf (net/sctp/sm_statefuns.c:4212) sctp_do_sm (net/sctp/sm_sideeffect.c:1172) sctp_assoc_bh_rcv (net/sctp/associola.c:1044) sctp_rcv (net/sctp/input.c:243) ip_local_deliver (net/ipv4/ip_input.c:262) process_backlog (net/core/dev.c:6680) </IRQ>
A response can only acknowledge a request that was actually sent, so do not match asoc->strreset_chunk while chunk->transport is NULL. Guarding the lookup covers all three completion sites.
--- net/sctp/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sctp/stream.c +1 -1
@@ -488,7 +488,7 @@ static struct sctp_paramhdr *sctp_chunk_struct sctp_reconf_chunk *hdr;union sctp_params param; -if (!chunk) +if (!chunk || !chunk->transport)return NULL ; hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
Greg Kroah-Hartman
maintainerPATCH 204/1424 sctp: distinguish sequence zero from wildcard in reconf lookup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jun Yang <[email protected]>
commit 63f44178f0a0f86060c9b576d6efab8a3ffa403e upstream.
Zero is a valid response sequence after strreset_outseq wraps, but sctp_chunk_lookup_strreset_param() currently treats it as a wildcard.
Add match_seq so response lookups match zero exactly while the one type-only lookup can still ignore the sequence.
--- net/sctp/stream.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
net/sctp/stream.c +6 -5
@@ -482,7 +482,7 @@ out:static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param (struct sctp_association *asoc,__be32 resp_seq, -__be16 type) +__be16 type,bool match_seq) {struct sctp_chunk *chunk = asoc->strreset_chunk;struct sctp_reconf_chunk *hdr;
@@ -499,7 +499,7 @@ static struct sctp_paramhdr *sctp_chunk_*/ struct sctp_strreset_tsnreq *req = param.v; -if ((!resp_seq || req->request_seq == resp_seq) && +if ((!match_seq || req->request_seq == resp_seq) && (!type || type == req->param_hdr.type))return param.v; }
@@ -564,7 +564,7 @@ struct sctp_chunk *sctp_process_strresetif (asoc->strreset_chunk) {if (!sctp_chunk_lookup_strreset_param ( asoc, outreq->response_seq, -SCTP_PARAM_RESET_IN_REQUEST )) { +SCTP_PARAM_RESET_IN_REQUEST , true)) {/* same process with outstanding isn't 0 */ result =SCTP_STRRESET_ERR_IN_PROGRESS ; goto out;
@@ -816,7 +816,7 @@ struct sctp_chunk *sctp_process_strresetif (asoc->strreset_chunk) {if (!sctp_chunk_lookup_strreset_param ( - asoc,0 ,SCTP_PARAM_RESET_ADD_IN_STREAMS )) { + asoc,0 ,SCTP_PARAM_RESET_ADD_IN_STREAMS , false)) {/* same process with outstanding isn't 0 */ result =SCTP_STRRESET_ERR_IN_PROGRESS ; goto out;
@@ -927,7 +927,8 @@ struct sctp_chunk *sctp_process_strresetstruct sctp_paramhdr *req;__u32 result; - req =sctp_chunk_lookup_strreset_param (asoc, resp->response_seq,0 ); + req =sctp_chunk_lookup_strreset_param (asoc, resp->response_seq,0 , + true);if (!req)return NULL ;
Greg Kroah-Hartman
maintainerPATCH 205/1424 sctp: fix stream->outcnt underflow on duplicate RECONF responses
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jun Yang <[email protected]>
commit 3faf13aff243ca9f78d08b1a2956ef5a6fc77b6e upstream.
A cached RECONF chunk may contain more than one request parameter. A duplicate response can therefore find and process the same ADD_OUT request again while another parameter is still outstanding, rolling back outcnt twice and possibly underflowing it.
Track outstanding request types as bits and clear each bit after its first response. Later responses for the same request are then ignored.
--- include/net/sctp/structs.h | 2 +- net/sctp/stream.c | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 12 deletions(-)
include/net/sctp/structs.h +1 -1
@@ -2088,7 +2088,7 @@ struct sctp_association { force_delay:1 ;__u8 strreset_enable; -__u8 strreset_outstanding;/* request param count on the fly */ +__u8 strreset_outstanding;/* request param bitmask on the fly */ __u32 strreset_outseq;/* Update after receiving response */ __u32 strreset_inseq;/* Update after receiving request */
net/sctp/stream.c +28 -11
@@ -22,6 +22,15 @@#include <net/sctp/sm.h> #include <net/sctp/stream_sched.h> +#define SCTP_STRRESET_MASK (type) \ + BIT(ntohs(type) - ntohs(SCTP_PARAM_RESET_OUT_REQUEST)) +#define SCTP_STRRESET_TEST (asoc, type) \ + ((asoc)->strreset_outstanding & SCTP_STRRESET_MASK(type)) +#define SCTP_STRRESET_SET (asoc, type) \ + ((asoc)->strreset_outstanding |= SCTP_STRRESET_MASK(type)) +#define SCTP_STRRESET_CLEAR (asoc, type) \ + ((asoc)->strreset_outstanding &= ~SCTP_STRRESET_MASK(type)) +static void sctp_stream_shrink_out (struct sctp_stream *stream,__u16 outcnt) {struct sctp_association *asoc;
@@ -372,7 +381,10 @@ int sctp_send_reset_streams(struct sctp_ goto out; } - asoc->strreset_outstanding = out + in; +if (out) +SCTP_STRRESET_SET (asoc,SCTP_PARAM_RESET_OUT_REQUEST ); +if (in) +SCTP_STRRESET_SET (asoc,SCTP_PARAM_RESET_IN_REQUEST ); out:return retval;
@@ -417,7 +429,7 @@ int sctp_send_reset_assoc(struct sctp_asreturn retval; } - asoc->strreset_outstanding =1 ; +SCTP_STRRESET_SET (asoc,SCTP_PARAM_RESET_TSN_REQUEST );return 0 ; }
@@ -474,7 +486,10 @@ int sctp_send_add_streams(struct sctp_as goto out; } - asoc->strreset_outstanding = !!out + !!in; +if (out) +SCTP_STRRESET_SET (asoc,SCTP_PARAM_RESET_ADD_OUT_STREAMS ); +if (in) +SCTP_STRRESET_SET (asoc,SCTP_PARAM_RESET_ADD_IN_STREAMS ); out:return retval;
@@ -564,13 +579,14 @@ struct sctp_chunk *sctp_process_strresetif (asoc->strreset_chunk) {if (!sctp_chunk_lookup_strreset_param ( asoc, outreq->response_seq, -SCTP_PARAM_RESET_IN_REQUEST , true)) { +SCTP_PARAM_RESET_IN_REQUEST , true) || + !SCTP_STRRESET_TEST (asoc,SCTP_PARAM_RESET_IN_REQUEST )) {/* same process with outstanding isn't 0 */ result =SCTP_STRRESET_ERR_IN_PROGRESS ; goto out; } - asoc->strreset_outstanding--; +SCTP_STRRESET_CLEAR (asoc,SCTP_PARAM_RESET_IN_REQUEST ); asoc->strreset_outseq++;if (!asoc->strreset_outstanding) {
@@ -669,7 +685,7 @@ struct sctp_chunk *sctp_process_strresetSCTP_SO (stream, i)->state =SCTP_STREAM_CLOSED ; asoc->strreset_chunk = chunk; - asoc->strreset_outstanding =1 ; +SCTP_STRRESET_SET (asoc,SCTP_PARAM_RESET_OUT_REQUEST );sctp_chunk_hold (asoc->strreset_chunk); result =SCTP_STRRESET_PERFORMED ;
@@ -816,13 +832,14 @@ struct sctp_chunk *sctp_process_strresetif (asoc->strreset_chunk) {if (!sctp_chunk_lookup_strreset_param ( - asoc,0 ,SCTP_PARAM_RESET_ADD_IN_STREAMS , false)) { + asoc,0 ,SCTP_PARAM_RESET_ADD_IN_STREAMS , false) || + !SCTP_STRRESET_TEST (asoc,SCTP_PARAM_RESET_ADD_IN_STREAMS )) {/* same process with outstanding isn't 0 */ result =SCTP_STRRESET_ERR_IN_PROGRESS ; goto out; } - asoc->strreset_outstanding--; +SCTP_STRRESET_CLEAR (asoc,SCTP_PARAM_RESET_ADD_IN_STREAMS ); asoc->strreset_outseq++;if (!asoc->strreset_outstanding) {
@@ -899,7 +916,7 @@ struct sctp_chunk *sctp_process_strreset goto out; asoc->strreset_chunk = chunk; - asoc->strreset_outstanding =1 ; +SCTP_STRRESET_SET (asoc,SCTP_PARAM_RESET_ADD_OUT_STREAMS );sctp_chunk_hold (asoc->strreset_chunk); stream->outcnt = outcnt;
@@ -929,7 +946,7 @@ struct sctp_chunk *sctp_process_strreset req =sctp_chunk_lookup_strreset_param (asoc, resp->response_seq,0 , true); -if (!req) +if (!req || !SCTP_STRRESET_TEST (asoc, req->type))return NULL ; result =ntohl (resp->result);
@@ -1079,7 +1096,7 @@ struct sctp_chunk *sctp_process_strreset nums, 0, GFP_ATOMIC); } - asoc->strreset_outstanding--; +SCTP_STRRESET_CLEAR (asoc, req->type); asoc->strreset_outseq++;/* remove everything for this reconf request */
Greg Kroah-Hartman
maintainerPATCH 206/1424 power: supply: bq24257: fix use-after-free on remove
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 9d34c9d660c3d0931d2cc749c46c47cf31f96e48 upstream.
The STAT-pin interrupt is devm-managed, so it stays armed until the devm cleanup that runs after remove() returns. remove() cancels bq->iilimit_setup_work while the threaded handler can still fire; that handler reschedules the work and dereferences bq, so the work runs against freed memory once devm frees bq.
Make the delayed work device-managed with devm_delayed_work_autocancel(), registered before the interrupt request. The devm cleanup then releases the interrupt first, so the handler can no longer reschedule the work, and cancels the work before bq is freed. The explicit cancel_delayed_work_sync() in remove() is no longer needed and is dropped.
Found by static analysis.
--- drivers/power/supply/bq24257_charger.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
drivers/power/supply/bq24257_charger.c +9 -7
@@ -18,6 +18,7 @@#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/delay.h> +#include <linux/devm-helpers.h> #include <linux/acpi.h> #include <linux/of.h>
@@ -1018,10 +1019,6 @@ static int bq24257_probe(struct i2c_clieif (bq->chip ==BQ24250 ) bq->iilimit_autoset_enable = false; -if (bq->iilimit_autoset_enable) -INIT_DELAYED_WORK (&bq->iilimit_setup_work, - bq24257_iilimit_setup_work); -/* * The BQ24250 doesn't have a dedicated Power Good (PG) pin so let's * not probe for it and instead use a SW-based approach to determine
@@ -1062,6 +1059,14 @@ static int bq24257_probe(struct i2c_cliereturn ret; } +if (bq->iilimit_autoset_enable) { + ret =devm_delayed_work_autocancel (dev, + &bq->iilimit_setup_work, + bq24257_iilimit_setup_work); +if (ret) +return ret; + } + ret = devm_request_threaded_irq(dev, client->irq, NULL, bq24257_irq_handler_thread, IRQF_TRIGGER_FALLING |
@@ -1079,9 +1084,6 @@ static void bq24257_remove(struct i2c_cl {struct bq24257_device *bq =i2c_get_clientdata (client); -if (bq->iilimit_autoset_enable) -cancel_delayed_work_sync (&bq->iilimit_setup_work); -bq24257_field_write (bq,F_RESET ,1 );/* reset to defaults */ }
Greg Kroah-Hartman
maintainerPATCH 207/1424 power: supply: bq256xx: drain usb_work before freeing the charger
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 2dd6cd823777bea6d9a880a12a92a73ec76aee0b upstream.
The USB-PHY notifier queues usb_work, whose handler calls power_supply_changed(bq->charger). The reset devm action only unregisters the notifier and was registered before the power supplies, so devm frees bq->charger on unwind before the action runs; a usb_work still queued can then dereference it.
Register the reset action after the power supplies, so it unregisters the notifiers and drains usb_work before the supplies are released. Initialize usb_work and obtain the PHY references before registering the notifiers, so the worker cannot run before the supplies exist.
Found by static analysis.
--- drivers/power/supply/bq256xx_charger.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-)
drivers/power/supply/bq256xx_charger.c +15 -14
@@ -905,6 +905,8 @@ static void bq256xx_charger_reset(void *if (!IS_ERR_OR_NULL (bq->usb3_phy))usb_unregister_notifier (bq->usb3_phy, &bq->usb_nb); + +cancel_work_sync (&bq->usb_work); } static int bq256xx_set_charger_property(struct power_supply *psy,
@@ -1727,24 +1729,12 @@ static int bq256xx_probe(struct i2c_cliereturn ret; } - ret =devm_add_action_or_reset (dev, bq256xx_charger_reset, bq); -if (ret) -return ret; +INIT_WORK (&bq->usb_work, bq256xx_usb_work); + bq->usb_nb.notifier_call = bq256xx_usb_notifier;/* OTG reporting */ bq->usb2_phy =devm_usb_get_phy (dev,USB_PHY_TYPE_USB2 ); -if (!IS_ERR_OR_NULL (bq->usb2_phy)) { -INIT_WORK (&bq->usb_work, bq256xx_usb_work); - bq->usb_nb.notifier_call = bq256xx_usb_notifier; -usb_register_notifier (bq->usb2_phy, &bq->usb_nb); - } - bq->usb3_phy =devm_usb_get_phy (dev,USB_PHY_TYPE_USB3 ); -if (!IS_ERR_OR_NULL (bq->usb3_phy)) { -INIT_WORK (&bq->usb_work, bq256xx_usb_work); - bq->usb_nb.notifier_call = bq256xx_usb_notifier; -usb_register_notifier (bq->usb3_phy, &bq->usb_nb); - } ret =bq256xx_power_supply_init (bq, &psy_cfg, dev);if (ret) {
@@ -1752,6 +1742,17 @@ static int bq256xx_probe(struct i2c_cliereturn ret; } +/* Register after the power supplies so devm runs it first. */ + ret =devm_add_action_or_reset (dev, bq256xx_charger_reset, bq); +if (ret) +return ret; + +if (!IS_ERR_OR_NULL (bq->usb2_phy)) +usb_register_notifier (bq->usb2_phy, &bq->usb_nb); + +if (!IS_ERR_OR_NULL (bq->usb3_phy)) +usb_register_notifier (bq->usb3_phy, &bq->usb_nb); +if (client->irq) { ret = devm_request_threaded_irq(dev, client->irq, NULL, bq256xx_irq_handler_thread,
Greg Kroah-Hartman
maintainerPATCH 208/1424 power: supply: bq25890: Fix power_supply reference leak
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ma Ke <[email protected]>
commit 863c32a83e4235eb0cbf6106f2b124e645302156 upstream.
bq25890_fw_probe() acquires a reference to a secondary charger using power_supply_get_by_name(), but the reference is not released on later probe failures or on driver detach.
In particular, failures after bq25890_fw_probe() returns successfully, such as a failure in bq25890_hw_init(), also leak the reference.
Register a device-managed cleanup action immediately after acquiring the secondary charger. This releases the reference on all subsequent probe failures and on driver detach.
Found by code review.
--- drivers/power/supply/bq25890_charger.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/power/supply/bq25890_charger.c +12 -0
@@ -1389,6 +1389,14 @@ static int bq25890_fw_read_u32_props(strreturn 0 ; } +static void bq25890_release_secondary_chrg (void *data) +{ +struct bq25890_device *bq = data; + +power_supply_put (bq->secondary_chrg); + bq->secondary_chrg =NULL ; +} +static int bq25890_fw_probe (struct bq25890_device *bq) {int ret;
@@ -1401,6 +1409,10 @@ static int bq25890_fw_probe(struct bq258 bq->secondary_chrg =power_supply_get_by_name (str);if (!bq->secondary_chrg)return -EPROBE_DEFER ; + + ret =devm_add_action_or_reset (bq->dev, bq25890_release_secondary_chrg, bq); +if (ret) +return ret; }/* Optional, left at 0 if property is not present */
Greg Kroah-Hartman
maintainerPATCH 209/1424 power: supply: cros_usbpd-charger: bound the EC-reported port count
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 48355ce49359740f52e94d3623f6fc557ce341f0 upstream.
cros_usbpd_charger_probe() reads two port counts from the EC and uses one of them, num_charger_ports, as the loop bound when populating a fixed-size array:
struct port_data *ports[EC_USB_PD_MAX_PORTS]; /* 8 entries */ ... for (i = 0; i < charger->num_charger_ports; i++) charger->ports[charger->num_registered_psy++] = port;
Both num_usbpd_ports (from EC_CMD_USB_PD_PORTS) and num_charger_ports (from EC_CMD_CHARGE_PORT_COUNT) are u8 values reported by the EC. The only validation is a sanity check that compares the two EC-reported values against each other:
if (num_charger_ports < num_usbpd_ports || num_charger_ports > num_usbpd_ports + 1) return -EPROTO;
It never checks either count against EC_USB_PD_MAX_PORTS, the size of the ports[] array. A malfunctioning, malicious or compromised EC that reports num_usbpd_ports == num_charger_ports == N for any N > 8 (for example both 255) passes this check, and the loop then writes N pointers into the 8-entry ports[] array embedded in the devm_kzalloc()'d charger_data, overflowing it by up to 255 - 8 = 247 entries (~1976 bytes): a slab out-of-bounds write.
Reject a port count larger than the ports[] array can hold.
--- drivers/power/supply/cros_usbpd-charger.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/power/supply/cros_usbpd-charger.c +5 -2
@@ -600,10 +600,13 @@ static int cros_usbpd_charger_probe(stru/* * Sanity checks on the number of ports: -* there should be at most 1 dedicated port +* there should be at most 1 dedicated port, and the count must +* not exceed the maximum number of supported ports +* (EC_USB_PD_MAX_PORTS). */ if (charger->num_charger_ports < charger->num_usbpd_ports || - charger->num_charger_ports > (charger->num_usbpd_ports +1 )) { + charger->num_charger_ports > (charger->num_usbpd_ports +1 ) || + charger->num_charger_ports >EC_USB_PD_MAX_PORTS ) {dev_err (dev,"Unexpected number of charge port count\n" ); ret = -EPROTO ; goto fail_nowarn;
Greg Kroah-Hartman
maintainerPATCH 210/1424 power: supply: cros_usbpd: Limit port counts to EC_USB_PD_MAX_PORTS
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jameson Thies <[email protected]>
commit 657cd3a42e937276262c0a8ae6b01a87004309de upstream.
Currently the cros_usbpd-charger driver probe iterates based on raw charger port count returned by the embedded controller. The only check is against the number of USB PD ports which the embedded controller also defines. A malicious embedded controller could return an inaccurate port count (up to 255) resulting in an out of bounds write and subsequent memory corruption.
Update helper functions in cros_usbpd-charger to limit port counts to EC_USB_PD_MAX_PORTS.
--- drivers/power/supply/cros_usbpd-charger.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/power/supply/cros_usbpd-charger.c +10 -0
@@ -136,6 +136,11 @@ static int cros_usbpd_charger_get_num_poif (ret <0 )return ret; +if (resp.port_count >EC_USB_PD_MAX_PORTS ) { +dev_warn (charger->dev,"Charge port count out of bounds\n" ); +return EC_USB_PD_MAX_PORTS ; + } +return resp.port_count; }
@@ -149,6 +154,11 @@ static int cros_usbpd_charger_get_usbpd_if (ret <0 )return ret; +if (resp.num_ports >EC_USB_PD_MAX_PORTS ) { +dev_warn (charger->dev,"USB PD port count out of bounds\n" ); +return EC_USB_PD_MAX_PORTS ; + } +return resp.num_ports; }
Greg Kroah-Hartman
maintainerPATCH 211/1424 power: supply: lp8727: fix use-after-free in lp8727_release_irq()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit ceb6ac43b0f591722401922ceb958ce2616935e0 upstream.
lp8727_isr_func(), the threaded IRQ handler, is the only caller that arms pchg->work via schedule_delayed_work(). lp8727_release_irq() currently cancels the work before freeing the IRQ, so an IRQ delivered in between can re-arm the work through the threaded handler. After .remove returns the devm layer frees pchg while lp8727_delayed_func() may still run and dereference it.
Free the IRQ first so the threaded handler is quiesced and can no longer queue work, then cancel the delayed work to drain the final generation.
This issue was found by an in-house static analysis tool.
--- drivers/power/supply/lp8727_charger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/power/supply/lp8727_charger.c +2 -2
@@ -280,10 +280,10 @@ static int lp8727_setup_irq(struct lp872static void lp8727_release_irq (struct lp8727_chg *pchg) { -cancel_delayed_work_sync (&pchg->work); -if (pchg->irq)free_irq (pchg->irq, pchg); + +cancel_delayed_work_sync (&pchg->work); }static enum power_supply_property lp8727_charger_prop[] = {
Greg Kroah-Hartman
maintainerPATCH 212/1424 power: supply: qcom_battmgr: terminate the strings from firmware
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit ab1112df8f4ffa88cb024dd370c432ced80f77d8 upstream.
The qcom_battmgr_sc8280xp_strcpy() takes a Pascal-style string when the firmware sends one. Otherwise it copies all BATTMGR_STRING_LEN bytes and leaves the destination without a terminator.
Those destinations are model_number, serial_number and oem_info, each BATTMGR_STRING_LEN and declared next to each other. They go out to user space as val->strval, which power_supply_format_property() prints with "%s", so a firmware string that fills the whole field makes that read run into the following members.
Use strscpy() so the copy always terminates, the way the SM8350 path already does for the same field.
--- drivers/power/supply/qcom_battmgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/power/supply/qcom_battmgr.c +1 -1
@@ -972,7 +972,7 @@ static void qcom_battmgr_sc8280xp_strcpymemcpy (dest, src +1 , len); dest[len] ='\0' ; }else { -memcpy (dest, src,BATTMGR_STRING_LEN ); +strscpy (dest, src,BATTMGR_STRING_LEN ); } }
Greg Kroah-Hartman
maintainerPATCH 213/1424 power: supply: rt9455: quiesce delayed work before teardown
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 3e7a1ebc32fad5a558254a478efd401c17a24381 upstream.
The threaded IRQ handler can queue pwr_rdy_work, max_charging_time_work and batt_presence_work. pwr_rdy_work and batt_presence_work can also queue max_charging_time_work, while batt_presence_work can requeue itself.
rt9455_remove() cancels max_charging_time_work before batt_presence_work. The latter can therefore queue max_charging_time_work after it has already been cancelled:
rt9455_remove() workqueue
cancel pwr_rdy_work
cancel max_charging_time_work
batt_presence_work queues
max_charging_time_work
cancel batt_presence_work
return
devres frees rt9455_info
max_charging_time_work dereferences
rt9455_info
The IRQ also remains registered until devres cleanup and can queue more work after any of the cancellation calls. If rt9455_hw_init() fails after the IRQ has been requested, probe returns without cancelling work that may already have been queued. A pending callback can then access rt9455_info after it has been freed.
Register rt9455_cancel_all_delayed_works() through devm_add_action_or_reset() right after devm_power_supply_register(). devres invokes the action in reverse registration order, after the managed IRQ has been freed and before rt9455_info is released, so the delayed works are drained in both rt9455_remove() and the probe error path. Cancel pwr_rdy_work and batt_presence_work before max_charging_time_work because both can queue the latter.
This issue was found by an in-house static analysis tool.
--- drivers/power/supply/rt9455_charger.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-)
drivers/power/supply/rt9455_charger.c +17 -4
@@ -1582,6 +1582,19 @@ static const struct regmap_config rt9455 .cache_type = REGCACHE_RBTREE, }; +static void rt9455_cancel_all_delayed_works (void *data) +{ +struct rt9455_info *info = data; + +/* +* Both pwr_rdy_work and batt_presence_work can queue +* max_charging_time_work, so cancel them first. +*/ +cancel_delayed_work_sync (&info->pwr_rdy_work); +cancel_delayed_work_sync (&info->batt_presence_work); +cancel_delayed_work_sync (&info->max_charging_time_work); +} +static int rt9455_probe (struct i2c_client *client) {struct i2c_adapter *adapter = client->adapter;
@@ -1672,6 +1685,10 @@ static int rt9455_probe(struct i2c_clien goto put_usb_notifier; } + ret =devm_add_action_or_reset (dev, rt9455_cancel_all_delayed_works, info); +if (ret) + goto put_usb_notifier; + ret = devm_request_threaded_irq(dev, client->irq, NULL, rt9455_irq_handler_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
@@ -1712,10 +1729,6 @@ static void rt9455_remove(struct i2c_cliif (info->nb.notifier_call)usb_unregister_notifier (info->usb_phy, &info->nb);#endif - -cancel_delayed_work_sync (&info->pwr_rdy_work); -cancel_delayed_work_sync (&info->max_charging_time_work); -cancel_delayed_work_sync (&info->batt_presence_work); }static const struct i2c_device_id rt9455_i2c_id_table[] = {
Greg Kroah-Hartman
maintainerPATCH 214/1424 power: supply: twl4030_charger: cancel workers via devm
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
commit 6eba34732524067da2aad5ddfdfbc641ded10e9e upstream.
bci is devm-allocated. Two workers (bci->work and bci->current_worker) dereference it. twl4030_bci_remove() disables charging and masks interrupts. It cancels neither worker. A worker pending at remove() can run after devm frees bci.
The USB transceiver comes from devm_usb_get_phy_by_node(). devm unregisters its notifier only after remove() returns. A cancel_work_sync() in remove() can then race a notifier reschedule. devm_work_autocancel() and devm_delayed_work_autocancel() avoid that. They cancel the workers during devm release, before bci is freed.
The current_worker is registered first, since devm will cancel in reverse order and bci->work can reschedule current_worker.
--- drivers/power/supply/twl4030_charger.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
drivers/power/supply/twl4030_charger.c +10 -2
@@ -14,6 +14,7 @@#include <linux/err.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/devm-helpers.h> #include <linux/interrupt.h> #include <linux/mfd/twl.h> #include <linux/power_supply.h>
@@ -1003,8 +1004,15 @@ static int twl4030_bci_probe(struct platplatform_set_drvdata (pdev, bci); -INIT_WORK (&bci->work, twl4030_bci_usb_work); -INIT_DELAYED_WORK (&bci->current_worker, twl4030_current_worker); + ret =devm_delayed_work_autocancel (&pdev->dev, &bci->current_worker, + twl4030_current_worker); +if (ret) +return ret; + + ret =devm_work_autocancel (&pdev->dev, &bci->work, + twl4030_bci_usb_work); +if (ret) +return ret; bci->channel_vac =devm_iio_channel_get (&pdev->dev,"vac" );if (IS_ERR (bci->channel_vac)) {
Greg Kroah-Hartman
maintainerPATCH 215/1424 power: supply: ucs1002: fix use-after-free on remove
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 609af0ceeaefdfa42cd01dd060b20f2e41f9a232 upstream.
ucs1002 has no remove callback, so unbind runs entirely through devm. The alert IRQ handler queues the health_poll delayed work, and the work reschedules itself while the chip reports a bad-health condition. devm frees the alert IRQ, which only synchronizes the handler; it does not cancel the delayed work, which can then run after devm frees the driver data and dereference it.
Register health_poll with devm_delayed_work_autocancel() before the alert IRQ is requested. devm then frees the IRQ before cancelling the work, so the handler can no longer queue it and the work is cancelled before the driver data is freed.
This issue was found by an in-house static analysis tool.
--- drivers/power/supply/ucs1002_power.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/power/supply/ucs1002_power.c +5 -1
@@ -12,6 +12,7 @@#include <linux/kernel.h> #include <linux/kthread.h> #include <linux/device.h> +#include <linux/devm-helpers.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h>
@@ -643,7 +644,10 @@ static int ucs1002_probe(struct i2c_clie } info->health =POWER_SUPPLY_HEALTH_GOOD ; -INIT_DELAYED_WORK (&info->health_poll, ucs1002_health_poll); + ret =devm_delayed_work_autocancel (dev, &info->health_poll, + ucs1002_health_poll); +if (ret) +return ret;if (irq_a_det >0 ) { ret = devm_request_threaded_irq(dev, irq_a_det, NULL,
Greg Kroah-Hartman
maintainerPATCH 216/1424 power: supply: max17040: drop incorrect I2C functionality check
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jianing Li <[email protected]>
commit 4e4b9f5ce9dfb8ed4b8d1262a504b8043ac09d87 upstream.
max17040_probe() rejects adapters that do not advertise I2C_FUNC_SMBUS_BYTE. The driver does not issue SMBus byte transactions, however. Its regmap has 8-bit registers and 16-bit big-endian values, for which regmap-i2c supports either raw I2C transfers or SMBus word-data transactions.
Consequently, an adapter providing raw I2C transfers or SMBus word data but not SMBus byte transactions is rejected even though regmap can access the device. Conversely, the current check can pass an adapter that regmap cannot use.
Drop the stale check and let devm_regmap_init_i2c() validate and select the supported transfer method.
--- drivers/power/supply/max17040_battery.c | 4 ---- 1 file changed, 4 deletions(-)
drivers/power/supply/max17040_battery.c +0 -4
@@ -433,16 +433,12 @@ static const struct power_supply_desc mastatic int max17040_probe (struct i2c_client *client) {const struct i2c_device_id *id =i2c_client_get_device_id (client); -struct i2c_adapter *adapter = client->adapter;struct power_supply_config psy_cfg = {};struct max17040_chip *chip;enum chip_id chip_id;bool enable_irq = false;int ret; -if (!i2c_check_functionality (adapter,I2C_FUNC_SMBUS_BYTE )) -return -EIO ; - chip =devm_kzalloc (&client->dev,sizeof (*chip),GFP_KERNEL );if (!chip)return -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 217/1424 power: supply: max17040: synchronize work cancellation on suspend
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jianing Li <[email protected]>
commit 86a3a8a926aa5969c329d1df2d3259f189961bbc upstream.
max17040_work() requeues itself after every poll. cancel_delayed_work() only cancels a pending instance and does not wait for a callback that is already running.
If system suspend races with the polling callback, the callback can continue accessing the fuel gauge and requeue itself after the suspend callback returns.
Use cancel_delayed_work_sync() to ensure polling is quiesced before suspend completes.
--- drivers/power/supply/max17040_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/power/supply/max17040_battery.c +1 -1
@@ -528,7 +528,7 @@ static int max17040_suspend(struct devic// disable soc alert to prevent wakeup max17040_set_soc_alert (chip,0 ); else - cancel_delayed_work(&chip->work); + cancel_delayed_work_sync(&chip->work);if (client->irq &&device_may_wakeup (dev))enable_irq_wake (client->irq);
Greg Kroah-Hartman
maintainerPATCH 218/1424 s390/cpum_cf: Handle CPU hotplug via prepare/dead callbacks
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Richter <[email protected]>
commit 337bd95507a16063687cfc286ea90de5cca48c37 upstream.
The command 'perf stat -e cycles -- <command>' crashes the kernel when CPUs are hotplug added during that run.
Root cause is the allocation of struct cpu_cf_events at first event initialization. The allocation is dynamic and the first event that has task context creates such a structure for each online CPU. This is not sufficient. CPUs may be offline during event creation and can be set online during the perf run time. For example commands
# echo 0 > /sys/devices/system/cpu/cpu1/online # perf stat -e cycles -i -- stress-ng -t10s --matrix X # sleep 1 # echo 1 > /sys/devices/system/cpu/cpu1/online
create an event for CPUs 0,2-X. Since the events are created with task-context, the scheduler will eventually schedule the program on CPU1. This CPU has not created and initialized any per CPU event infrastructure as that CPU was not online at the time of the perf invocation. Thus when the scheduler runs stress-ng on CPU1, the function cpumf_pmu_add() refers to a NULL pointer:
struct cpu_cf_events *cpuhw = this_cpu_cfhw();
This function call is invoked after the task stress-ng has been made runnable on CPU1. And this_cpu_cfhw() returns NULL.
The result is a panic:
Unable to handle kernel pointer dereference in virtual kernel address space
Failing address: 0000000000000000 TEID: 0000000000000483
....
Krnl PSW : 0404d00180000000 000003ef8291fd0c (cpumf_pmu_add+0x3c/0x80)
....
Call Trace: [<000003ef8291fd0c>] cpumf_pmu_add+0x3c/0x80 [<000003ef82bb5e3e>] event_sched_in+0xae/0x190 [<000003ef82bb60d6>] merge_sched_in+0x1b6/0x390 [<000003ef82bb65b8>] visit_groups_merge.constprop.0.isra.0+0x308/0x5b0 [<000003ef82bb689a>] pmu_groups_sched_in+0x3a/0x50 [<000003ef82bb6a30>] ctx_sched_in+0x180/0x260 [<000003ef82bb780c>] perf_event_context_sched_in+0x11c/0x2d0 [<000003ef82bb79ee>] __perf_event_task_sched_in+0x2e/0xc0 [<000003ef82994834>] finish_task_switch.isra.0+0x1a4/0x250
....
Last Breaking-Event-Address:
[<000003ef8291f1d8>] this_cpu_cfhw+0x38/0x40
The issue arises only in per-task context when the CPUMF facility is used and the scheduler picks a random CPU for such a process to run on. The scheduler enables the CPUMF infrastructure via PMU callback functions pmu::add() and pmu::del().
Introduce a CPU hotplug prepare/dead callback pair which creates and removes the per CPU counter data while the CPU is offline. Count the users which track every CPU (cpu == -1), that is perf_event_open() events with task context and /dev/hwctr device sessions, in the new counter cpu_cf_root::tskcnt, protected by pmc_reserve_mutex. This ensures the infrastructure is available when
new CPU is selected to run the per-task context process.
In cpum_cf_free_root() and cpum_cf_free_cpu() ensure the reference pointer to data structures is set to NULL before the data is freed to prevent interrupt handlers to access stale data.
--- arch/s390/kernel/perf_cpum_cf.c | 215 ++++++++++++++++++++++++++-------------- 1 file changed, 142 insertions(+), 73 deletions(-)
arch/s390/kernel/perf_cpum_cf.c +142 -73
@@ -108,6 +108,7 @@ struct cpu_cf_ptr {static struct cpu_cf_root {/* Anchor to per CPU data */ refcount_t refcnt;/* Overall active events */ +unsigned int tskctx;/* Users tracking all CPUs (cpu == -1) */ struct cpu_cf_ptr __percpu *cfptr; } cpu_cf_root;
@@ -116,13 +117,15 @@ static struct cpu_cf_root { /* Anchor t* user space in task context with perf_event_open() and close() * system calls. * -* This mutex serializes functions cpum_cf_alloc_cpu() called at event -* initialization via cpumf_pmu_event_init() and function cpum_cf_free_cpu() -* called at event removal via call back function hw_perf_event_destroy() -* when the event is deleted. They are serialized to enforce correct -* bookkeeping of pointer and reference counts anchored by -* struct cpu_cf_root and the access to cpu_cf_root::refcnt and the -* per CPU pointers stored in cpu_cf_root::cfptr. +* This mutex serializes the allocation and removal of the per CPU counter +* data via cpum_cf_alloc_cpu() and cpum_cf_free_cpu(). They are called with +* this mutex held at event initialization via cpumf_pmu_event_init(), at +* event removal via call back function hw_perf_event_destroy() when the +* event is deleted, and from the CPU hotplug prepare/dead callbacks. The +* mutex enforces correct bookkeeping of pointer and reference counts +* anchored by struct cpu_cf_root and protects the access to +* cpu_cf_root::refcnt, cpu_cf_root::tskctx and the per CPU pointers +* stored in cpu_cf_root::cfptr. */ static DEFINE_MUTEX (pmc_reserve_mutex);
@@ -165,12 +168,14 @@ static void cpum_cf_reset_cpu(void *flag }/* Free per CPU data when the last event is removed. */ -static void cpum_cf_free_root (void ) +static void cpum_cf_free_root (unsigned int num) { -if (!refcount_dec_and_test (&cpu_cf_root.refcnt)) +struct cpu_cf_ptr __percpu *p = cpu_cf_root.cfptr; + +if (!refcount_sub_and_test (num, &cpu_cf_root.refcnt))return ; -free_percpu (cpu_cf_root.cfptr); cpu_cf_root.cfptr =NULL ; +free_percpu (p);irq_subclass_unregister (IRQ_SUBCLASS_MEASUREMENT_ALERT );on_each_cpu (cpum_cf_reset_cpu,NULL ,1 ); debug_sprintf_event(cf_dbg, 4, "%s root.refcnt %u cfptr %d\n",
@@ -184,17 +189,17 @@ static void cpum_cf_free_root(void)* CPUs possible, which might be larger than the number of CPUs currently * online. */ -static int cpum_cf_alloc_root (void ) +static int cpum_cf_alloc_root (unsigned int num) {int rc =0 ; -if (refcount_inc_not_zero (&cpu_cf_root.refcnt)) +if (refcount_add_not_zero (num, &cpu_cf_root.refcnt))return rc;/* The memory is already zeroed. */ cpu_cf_root.cfptr = alloc_percpu(struct cpu_cf_ptr);if (cpu_cf_root.cfptr) { -refcount_set (&cpu_cf_root.refcnt,1 ); +refcount_set (&cpu_cf_root.refcnt, num);on_each_cpu (cpum_cf_reset_cpu,NULL ,1 );irq_subclass_register (IRQ_SUBCLASS_MEASUREMENT_ALERT ); }else {
@@ -204,20 +209,23 @@ static int cpum_cf_alloc_root(void)return rc; } -/* Free CPU counter data structure for a PMU */ -static void cpum_cf_free_cpu (int cpu) +/* +* Remove num references to the CPU counter data structure of a PMU. +* Called with pmc_reserve_mutex held. +*/ +static void cpum_cf_free_cpu (int cpu,unsigned int num) {struct cpu_cf_events *cpuhw;struct cpu_cf_ptr *p; -mutex_lock (&pmc_reserve_mutex); +lockdep_assert_held (&pmc_reserve_mutex);/* * When invoked via CPU hotplug handler, there might be no events * installed or that particular CPU might not have an * event installed. This anchor pointer can be NULL! */ if (!cpu_cf_root.cfptr) - goto out; +return ; p =per_cpu_ptr (cpu_cf_root.cfptr, cpu); cpuhw = p->cpucf;/*
@@ -225,28 +233,29 @@ static void cpum_cf_free_cpu(int cpu)* installed on that CPU, but on different CPUs. */ if (!cpuhw) - goto out; +return ; -if (refcount_dec_and_test (&cpuhw->refcnt)) { -kfree (cpuhw); +if (refcount_sub_and_test (num, &cpuhw->refcnt)) { p->cpucf =NULL ; +kfree (cpuhw); } -cpum_cf_free_root (); -out: -mutex_unlock (&pmc_reserve_mutex); +cpum_cf_free_root (num); } -/* Allocate CPU counter data structure for a PMU. Called under mutex lock. */ -static int cpum_cf_alloc_cpu (int cpu) +/* +* Add num references to the CPU counter data structure of a PMU and +* allocate it when necessary. Called with pmc_reserve_mutex held. +*/ +static int cpum_cf_alloc_cpu (int cpu,unsigned int num) {struct cpu_cf_events *cpuhw;struct cpu_cf_ptr *p;int rc; -mutex_lock (&pmc_reserve_mutex); - rc =cpum_cf_alloc_root (); +lockdep_assert_held (&pmc_reserve_mutex); + rc =cpum_cf_alloc_root (num);if (rc) - goto unlock; +return rc; p =per_cpu_ptr (cpu_cf_root.cfptr, cpu); cpuhw = p->cpucf;
@@ -254,12 +263,12 @@ static int cpum_cf_alloc_cpu(int cpu) cpuhw =kzalloc (sizeof (*cpuhw),GFP_KERNEL );if (cpuhw) { p->cpucf = cpuhw; -refcount_set (&cpuhw->refcnt,1 ); +refcount_set (&cpuhw->refcnt, num); }else { rc = -ENOMEM ; } }else { -refcount_inc (&cpuhw->refcnt); +refcount_add (num, &cpuhw->refcnt); }if (rc) {/*
@@ -267,10 +276,8 @@ static int cpum_cf_alloc_cpu(int cpu)* cpu_cf_event in not created, its destroy() function is not * invoked. Adjust the reference counter for the anchor. */ -cpum_cf_free_root (); +cpum_cf_free_root (num); } -unlock: -mutex_unlock (&pmc_reserve_mutex);return rc; }
@@ -282,39 +289,70 @@ unlock:* perf_event_open() with task context and /dev/hwctr interface. * If cpu is non-zero install event on this CPU only. This setup handles * perf_event_open() with CPU context. +* Users with cpu == -1 are counted in cpu_cf_root::tskctx. The CPU hotplug +* prepare and dead callbacks use this count to install and remove the per +* CPU counter data on a new or dying CPU. */ -static int cpum_cf_alloc (int cpu) +static int cpum_cf_alloc_cpuslocked (int cpu) {cpumask_var_t mask;int rc; +lockdep_assert_cpus_held ();if (cpu ==-1 ) {if (!zalloc_cpumask_var (&mask,GFP_KERNEL ))return -ENOMEM ; +mutex_lock (&pmc_reserve_mutex);for_each_online_cpu (cpu) { - rc =cpum_cf_alloc_cpu (cpu); + rc =cpum_cf_alloc_cpu (cpu,1 );if (rc) {for_each_cpu (cpu, mask) -cpum_cf_free_cpu (cpu); +cpum_cf_free_cpu (cpu,1 );break ; }cpumask_set_cpu (cpu, mask); } +if (!rc) + cpu_cf_root.tskctx++; +mutex_unlock (&pmc_reserve_mutex);free_cpumask_var (mask); }else { - rc =cpum_cf_alloc_cpu (cpu); +mutex_lock (&pmc_reserve_mutex); + rc =cpum_cf_alloc_cpu (cpu,1 ); +mutex_unlock (&pmc_reserve_mutex); }return rc; } -static void cpum_cf_free (int cpu) +static int cpum_cf_alloc (int cpu) +{ +int rc; + +cpus_read_lock (); + rc =cpum_cf_alloc_cpuslocked (cpu); +cpus_read_unlock (); +return rc; +} + +static void cpum_cf_free_cpuslocked (int cpu) { +lockdep_assert_cpus_held (); +mutex_lock (&pmc_reserve_mutex);if (cpu ==-1 ) { + cpu_cf_root.tskctx--;for_each_online_cpu (cpu) -cpum_cf_free_cpu (cpu); +cpum_cf_free_cpu (cpu,1 ); }else { -cpum_cf_free_cpu (cpu); +cpum_cf_free_cpu (cpu,1 ); } +mutex_unlock (&pmc_reserve_mutex); +} + +static void cpum_cf_free (int cpu) +{ +cpus_read_lock (); +cpum_cf_free_cpuslocked (cpu); +cpus_read_unlock (); }#define CF_DIAG_CTRSET_DEF 0xfeef/* Counter set header mark */
@@ -1093,53 +1131,67 @@ static refcount_t cfset_opencnt = REFCOUstatic DEFINE_MUTEX (cfset_ctrset_mutex);/* -* CPU hotplug handles only /dev/hwctr device. -* For perf_event_open() the CPU hotplug handling is done on kernel common -* code: +* CPU hotplug handling: +* +* cpum_cf_prepare_cpu() and cpum_cf_dead_cpu() run while the new or dying +* CPU is offline. They create and remove the per CPU counter data for all +* users tracking every CPU (cpu == -1), that is perf_event_open() events +* with task context and /dev/hwctr device sessions. Each such user holds +* one reference to the per CPU counter data of each CPU. Therefore install +* and remove one reference per user, tracked in cpu_cf_root::tskctx. This +* guarantees the per CPU counter data exists before the new CPU executes +* its first task and is removed only after the dying CPU is gone. +* +* cpum_cf_online_cpu() and cpum_cf_offline_cpu() run while the new or +* dying CPU is online. They handle only the counter set state of open +* /dev/hwctr device sessions on that CPU. For perf_event_open() events +* nothing is done: * - CPU add: Nothing is done since a file descriptor can not be created * and returned to the user. * - CPU delete: Handled by common code via pmu_disable(), pmu_stop() and -* pmu_delete(). The event itself is removed when the file descriptor is -* closed. +* pmu_delete(). During task exit processing of grouped perf events +* triggered by CPU hotplug processing, pmu_disable() is called as part +* of perf context removal process. The event itself is removed when the +* event file descriptor is closed. */ +static int cpum_cf_prepare_cpu (unsigned int cpu) +{ +int rc =0 ; + +mutex_lock (&pmc_reserve_mutex); +if (cpu_cf_root.tskctx) + rc =cpum_cf_alloc_cpu (cpu, cpu_cf_root.tskctx); +mutex_unlock (&pmc_reserve_mutex); +return rc; +} + +static int cpum_cf_dead_cpu (unsigned int cpu) +{ +mutex_lock (&pmc_reserve_mutex); +if (cpu_cf_root.tskctx) +cpum_cf_free_cpu (cpu, cpu_cf_root.tskctx); +mutex_unlock (&pmc_reserve_mutex); +return 0 ; +} +static int cfset_online_cpu (unsigned int cpu);static int cpum_cf_online_cpu (unsigned int cpu) { -int rc =0 ; - -/* -* Ignore notification for perf_event_open(). -* Handle only /dev/hwctr device sessions. -*/ mutex_lock (&cfset_ctrset_mutex); -if (refcount_read (&cfset_opencnt)) { - rc =cpum_cf_alloc_cpu (cpu); -if (!rc) -cfset_online_cpu (cpu); - } +if (refcount_read (&cfset_opencnt)) +cfset_online_cpu (cpu);mutex_unlock (&cfset_ctrset_mutex); -return rc; +return 0 ; }static int cfset_offline_cpu (unsigned int cpu);static int cpum_cf_offline_cpu (unsigned int cpu) { -/* -* During task exit processing of grouped perf events triggered by CPU -* hotplug processing, pmu_disable() is called as part of perf context -* removal process. Therefore do not trigger event removal now for -* perf_event_open() created events. Perf common code triggers event -* destruction when the event file descriptor is closed. -* -* Handle only /dev/hwctr device sessions. -*/ mutex_lock (&cfset_ctrset_mutex); -if (refcount_read (&cfset_opencnt)) { +if (refcount_read (&cfset_opencnt))cfset_offline_cpu (cpu); -cpum_cf_free_cpu (cpu); - }mutex_unlock (&cfset_ctrset_mutex);return 0 ; }
@@ -1186,7 +1238,7 @@ static void cpumf_measurement_alert(strustatic int cfset_init (void );static int __init cpumf_pmu_init (void ) { -int rc; +int state, rc;/* Extract counter measurement facility information */ if (!cpum_cf_avail () ||qctri (&cpumf_ctr_info))
@@ -1228,11 +1280,24 @@ static int __init cpumf_pmu_init(void)cfset_init (); } + rc =cpuhp_setup_state (CPUHP_BP_PREPARE_DYN , +"perf/s390/cf:prepare" , + cpum_cf_prepare_cpu, cpum_cf_dead_cpu); +if (rc <0 ) + goto out3; + state = rc; + rc =cpuhp_setup_state (CPUHP_AP_PERF_S390_CF_ONLINE ,"perf/s390/cf:online" , cpum_cf_online_cpu, cpum_cf_offline_cpu); -return rc; +if (rc <0 ) + goto out4; +return 0 ; +out4: +cpuhp_remove_state (state); +out3: +perf_pmu_unregister (&cpumf_pmu); out2:debug_unregister_view (cf_dbg, &debug_sprintf_view);debug_unregister (cf_dbg);
@@ -1388,6 +1453,7 @@ static void cfset_all_stop(struct cfset_*/ static int cfset_release (struct inode *inode,struct file *file) { +cpus_read_lock ();mutex_lock (&cfset_ctrset_mutex);/* Open followed by close/exit has no private_data */ if (file->private_data) {
@@ -1398,9 +1464,10 @@ static int cfset_release(struct inode *i }if (refcount_dec_and_test (&cfset_opencnt)) {/* Last close */ on_each_cpu (cfset_release_cpu,NULL ,1 ); -cpum_cf_free (-1 ); +cpum_cf_free_cpuslocked (-1 ); }mutex_unlock (&cfset_ctrset_mutex); +cpus_read_unlock ();return 0 ; }
@@ -1419,15 +1486,17 @@ static int cfset_open(struct inode *inodreturn -EPERM ; file->private_data =NULL ; +cpus_read_lock ();mutex_lock (&cfset_ctrset_mutex);if (!refcount_inc_not_zero (&cfset_opencnt)) {/* First open */ - rc =cpum_cf_alloc (-1 ); + rc =cpum_cf_alloc_cpuslocked (-1 );if (!rc) {cfset_session_init ();refcount_set (&cfset_opencnt,1 ); } }mutex_unlock (&cfset_ctrset_mutex); +cpus_read_unlock ();/* nonseekable_open() never fails */ return rc ?:nonseekable_open (inode, file);
Greg Kroah-Hartman
maintainerPATCH 219/1424 s390/dasd: Do not complete a failed ESE read as successful
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Haberland <[email protected]>
commit cddb447c62466f3076938ce120028d7b591f9f37 upstream.
dasd_int_handler() completes an NRF read of an unallocated ESE track by calling ese_read() and unconditionally marking the request DASD_CQR_SUCCESS. dasd_eckd_ese_read() can return an error before it has zeroed the destination buffer: a failed sense-data parse or a current track outside the requested range both return early, leaving the destination pages untouched. The request is still completed successfully, so the block layer is handed stale / uninitialized memory instead of zeros.
Check the ese_read() return value and fail the request through the normal error path instead of forcing DASD_CQR_SUCCESS.
--- drivers/s390/block/dasd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/s390/block/dasd.c +4 -2
@@ -1737,8 +1737,10 @@ void dasd_int_handler(struct ccw_devicereturn ; }if (rq_data_dir (req) ==READ ) { - device->discipline->ese_read (cqr, irb); - cqr->status =DASD_CQR_SUCCESS ; +if (device->discipline->ese_read (cqr, irb)) + cqr->status =DASD_CQR_ERROR ; +else + cqr->status =DASD_CQR_SUCCESS ; cqr->stopclk = now;dasd_device_clear_timer (device);dasd_schedule_device_bh (device);
Greg Kroah-Hartman
maintainerPATCH 220/1424 s390/dasd: Guard sysfs discipline callbacks against unallocated private data
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Haberland <[email protected]>
commit 2a1780f9fc2493bd34c418a0be6fc58943afcecf upstream.
Several sysfs show/store handlers call a discipline callback that dereferences device->private, either directly or through the DASD_DEFINE_ATTR() macro. During dasd_generic_set_online() the discipline is assigned before check_device() allocates device->private, so an unprivileged read of one of these world-readable attributes in that window dereferences a NULL pointer and panics.
Guard the dereference inside each callback that actually touches device->private.
--- drivers/s390/block/dasd_eckd.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-)
drivers/s390/block/dasd_eckd.c +38 -2
@@ -1506,6 +1506,8 @@ static void dasd_eckd_reset_path(structstruct dasd_eckd_private *private = device->private;unsigned long flags; +if (!private) +return ;if (!private->fcx_max_data) private->fcx_max_data =get_fcx_max_data (device);spin_lock_irqsave (get_ccwdev_lock (device->cdev), flags);
@@ -1661,6 +1663,9 @@ static int dasd_eckd_is_ese(struct dasd_ {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->vsq.vol_info.ese; }
@@ -1668,6 +1673,9 @@ static int dasd_eckd_ext_pool_id(struct {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->vsq.extent_pool_id; }
@@ -1681,6 +1689,9 @@ static int dasd_eckd_space_configured(ststruct dasd_eckd_private *private = device->private;int rc; +if (!private) +return 0 ; + rc =dasd_eckd_read_vol_info (device);return rc ? : private->vsq.space_configured;
@@ -1695,6 +1706,9 @@ static int dasd_eckd_space_allocated(strstruct dasd_eckd_private *private = device->private;int rc; +if (!private) +return 0 ; + rc =dasd_eckd_read_vol_info (device);return rc ? : private->vsq.space_allocated;
@@ -1704,6 +1718,9 @@ static int dasd_eckd_logical_capacity(st {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->vsq.logical_capacity; }
@@ -1846,7 +1863,11 @@ static int dasd_eckd_read_ext_pool_info(static int dasd_eckd_ext_size (struct dasd_device *device) {struct dasd_eckd_private *private = device->private; -struct dasd_ext_pool_sum eps = private->eps; +struct dasd_ext_pool_sum eps; + +if (!private) +return 0 ; + eps = private->eps;if (!eps.flags.extent_size_valid)return 0 ;
@@ -1862,6 +1883,9 @@ static int dasd_eckd_ext_pool_warn_thrsh {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->eps.warn_thrshld; }
@@ -1869,6 +1893,9 @@ static int dasd_eckd_ext_pool_cap_at_war {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->eps.flags.capacity_at_warnlevel; }
@@ -1879,6 +1906,9 @@ static int dasd_eckd_ext_pool_oos(struct {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->eps.flags.pool_oos; }
@@ -5988,8 +6018,11 @@ static int dasd_eckd_query_host_access(sstruct ccw1 *ccw;int rc; +if (!private) +return -ENODEV ; +/* not available for HYPER PAV alias devices */ -if (!device->block && private->lcu->pav ==HYPER_PAV ) +if (!device->block && private->lcu && private->lcu->pav ==HYPER_PAV )return -EOPNOTSUPP ;/* may not be supported by the storage server */
@@ -6854,6 +6887,9 @@ static int dasd_eckd_hpf_enabled(struct {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->fcx_max_data ?1 :0 ; }
Greg Kroah-Hartman
maintainerPATCH 221/1424 s390/dasd: Propagate partial completion length across ERP recovery
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Haberland <[email protected]>
commit 6fb5ba2e7e43173a3761e46f091070a8185efa14 upstream.
dasd_default_erp_postaction() copies the timing and device state from the finished ERP request back to the original request but drops proc_bytes. A request that was partially completed, an ESE read of a not-yet-allocated track returns fewer bytes than requested, and then recovered through the ERP chain loses its partial-completion length. __dasd_cleanup_cqr() then sees proc_bytes == 0 and completes the whole request instead of requeueing the remainder, silently returning zeroed data for the part that was never read.
Carry proc_bytes over to the original request like the other per-request state.
--- drivers/s390/block/dasd_erp.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/s390/block/dasd_erp.c +3 -0
@@ -127,6 +127,7 @@ struct dasd_ccw_req *dasd_default_erp_point success;unsigned long startclk, stopclk;struct dasd_device *startdev; +unsigned int proc_bytes;BUG_ON (cqr->refers ==NULL || cqr->function ==NULL );
@@ -134,6 +135,7 @@ struct dasd_ccw_req *dasd_default_erp_po startclk = cqr->startclk; stopclk = cqr->stopclk; startdev = cqr->startdev; + proc_bytes = cqr->proc_bytes;/* free all ERPs - but NOT the original cqr */ while (cqr->refers !=NULL ) {
@@ -151,6 +153,7 @@ struct dasd_ccw_req *dasd_default_erp_po cqr->startclk = startclk; cqr->stopclk = stopclk; cqr->startdev = startdev; + cqr->proc_bytes = proc_bytes;if (success) cqr->status =DASD_CQR_DONE ;else {
Greg Kroah-Hartman
maintainerPATCH 222/1424 PCI: Fix 32-bit config write in Intel PCH Root Port MPC ACS quirk
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mohamad Raizudeen <[email protected]>
commit 23d7eed5974989de56273c964d7e510e4aad91e8 upstream.
pci_quirk_enable_intel_rp_mpc_acs() reads a 32-bit DWORD from the MPC register, sets bit 26 (INTEL_MPC_REG_IRBNCE), but it writes it back using pci_write_config_word().
Because bit 26 resides in the upper 16 bits of the 32-bit register, a 16-bit write drops the newly set bit. The quirk logs that it is enabling IRBNCE, but the hardware never actually receives the command.
Use pci_write_config_dword() to ensure the full 32-bit value is written back to the hardware.
--- drivers/pci/quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/quirks.c +1 -1
@@ -5325,7 +5325,7 @@ static void pci_quirk_enable_intel_rp_mpif (!(mpc &INTEL_MPC_REG_IRBNCE )) {pci_info (dev,"Enabling MPC IRBNCE\n" ); mpc |=INTEL_MPC_REG_IRBNCE ; -pci_write_config_word (dev,INTEL_MPC_REG , mpc); +pci_write_config_dword (dev,INTEL_MPC_REG , mpc); } }
Greg Kroah-Hartman
maintainerPATCH 223/1424 PCI: meson: Fix GPIO state while requesting PERST#
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ronald Claveau <[email protected]>
commit 40fb390cbcc11797c44c16dabdf763ec87643671 upstream.
Meson devicetree defines the PERST# GPIO as 'reset' GPIO. Commit 4d3186a525b3 ("PCI: amlogic: Fix reset assertion via gpio descriptor") inverted the PERST# assertion logic to use proper GPIO descriptor semantics and moved the polarity configuration to the device tree as GPIO_ACTIVE_LOW. However, the initial PERST# GPIO state "GPIOD_OUT_LOW" was not updated accordingly.
This results in the enumeration failure of the endpoint devices as PERST# would get deasserted while requesting the GPIO even before power and REFCLK becomes stable.
Without this fix:
ahci 0000:01:00.0: enabling device (0000 -> 0002) ahci 0000:01:00.0: SSS flag set, parallel bus scan disabled ahci 0000:01:00.0: Controller reset failed (0xffffffff) ahci 0000:01:00.0: probe with driver ahci failed with error -5
With this fix:
ahci 0000:01:00.0: enabling device (0000 -> 0002)
ahci 0000:01:00.0: AHCI vers 0001.0300, 32 command slots, 6 Gbps, SATA mode ahci 0000:01:00.0: 1/1 ports implemented (port mask 0x1)
ahci 0000:01:00.0: flags: 64bit ncq led clo only pio ccc
Change the GPIO request flag from GPIOD_OUT_LOW to GPIOD_OUT_HIGH to get the right behaviour.
--- drivers/pci/controller/dwc/pci-meson.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/controller/dwc/pci-meson.c +1 -1
@@ -422,7 +422,7 @@ static int meson_pcie_probe(struct platfreturn PTR_ERR (mp->phy); } - mp->reset_gpio =devm_gpiod_get (dev,"reset" ,GPIOD_OUT_LOW ); + mp->reset_gpio =devm_gpiod_get (dev,"reset" ,GPIOD_OUT_HIGH );if (IS_ERR (mp->reset_gpio)) {dev_err (dev,"get reset gpio failed\n" );return PTR_ERR (mp->reset_gpio);
Greg Kroah-Hartman
maintainerPATCH 224/1424 PCI: Add ACS quirk for Pericom PI7C9X2G608 switches [12d8:2608]
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tim Harvey <[email protected]>
commit 062fb7f816439da6bf3860386889343482a66bd4 upstream.
The Pericom PI7C9X2G608 6-port Gen2 PCIe switch is also affected by the PI7C9X2G errata per the errata document:
E2: ACS P2P Request Redirect Is Not Functional
Apply the same quirk to this PCI ID as well to apply the workaround required if using ACS.
--- drivers/pci/quirks.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/pci/quirks.c +4 -0
@@ -6203,6 +6203,10 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_P pci_fixup_pericom_acs_store_forward);DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_PERICOM ,0xb404 , pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_ENABLE (PCI_VENDOR_ID_PERICOM ,0x2608 , + pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_PERICOM ,0x2608 , + pci_fixup_pericom_acs_store_forward);static void nvidia_ion_ahci_fixup (struct pci_dev *pdev) {
Greg Kroah-Hartman
maintainerPATCH 225/1424 PCI/sysfs: Avoid spurious runtime PM wakeup on config space accesses
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit b14b2bab88d7099ab4447560cbe4b40945e5c069 upstream.
Currently, the boundary checks in pci_read_config() and pci_write_config() reject only offsets beyond the effective configuration space size.
An access at an offset exactly equal to that size passes the check, has its length clamped to zero, and then invokes pci_config_pm_runtime_get() and pci_config_pm_runtime_put() around transfer blocks that do nothing.
This is a problem because pci_config_pm_runtime_get() synchronously resumes the upstream bridge through pm_runtime_get_sync() and resumes the device itself through pm_runtime_resume() when it is in D3cold, only for the handler to return zero immediately afterwards. Such a spurious wakeup wastes power and adds needless resume latency.
The sysfs core already clamps accesses against the attribute size set through the bin_size() callback, which reports either 256 or 4096 bytes. As such, the affected accesses are reads at offset 64 (or 128 for CardBus devices) through files opened without CAP_SYS_ADMIN, and reads and writes at the exact configuration space size on devices where a quirk sets a non-standard size.
Reject accesses at the boundary offset as well, so they return early before any runtime PM involvement, matching the procfs implementations in proc_bus_pci_read() and proc_bus_pci_write().
The value returned to userspace at these offsets remains zero, so the change is not visible to userspace.
--- drivers/pci/pci-sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/pci/pci-sysfs.c +2 -2
@@ -728,7 +728,7 @@ static ssize_t pci_read_config(struct fielse if (dev->hdr_type ==PCI_HEADER_TYPE_CARDBUS ) size =128 ; -if (off > size) +if (off >= size)return 0 ;if (off + count > size) { size -= off;
@@ -809,7 +809,7 @@ static ssize_t pci_write_config(struct fadd_taint (TAINT_USER ,LOCKDEP_STILL_OK ); } -if (off > dev->cfg_size) +if (off >= dev->cfg_size)return 0 ;if (off + count > dev->cfg_size) { size = dev->cfg_size - off;
Greg Kroah-Hartman
maintainerPATCH 226/1424 PCI/MSI: Enable memory decoding before restoring MSI-X messages
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Farhan Ali <[email protected]>
commit 231c7a57d19304beb0931e6cbe3a4929daf49747 upstream.
The current MSI-X restoration path assumes the Command register Memory bit is enabled when writing MSI-X messages. But it's possible the last saved and restored state of a device may not have the Memory bit enabled, even if a device driver later enables Memory bit and MSI-X. Attempting to access Memory space without Memory bit enabled can lead to Unsupported Request (UR) from the device. Fix this by enabling Memory bit and restore it afterwards.
--- drivers/pci/msi/msi.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/pci/msi/msi.c +10 -0
@@ -858,6 +858,7 @@ void __pci_restore_msix_state(struct pci {struct msi_desc *entry;bool write_msg; +u16 cmd;if (!dev->msix_enabled)return ;
@@ -867,6 +868,14 @@ void __pci_restore_msix_state(struct pcipci_msix_clear_and_set_ctrl (dev,0 ,PCI_MSIX_FLAGS_ENABLE |PCI_MSIX_FLAGS_MASKALL ); +/* +* The restored device state may not have Memory Space enabled. +* Since the MSI-X Table and PBA are in Memory Space, enable it +* while restoring them. +*/ +pci_read_config_word (dev,PCI_COMMAND , &cmd); +pci_write_config_word (dev,PCI_COMMAND , cmd |PCI_COMMAND_MEMORY ); + write_msg =arch_restore_msi_irqs (dev);msi_lock_descs (&dev->dev);
@@ -877,6 +886,7 @@ void __pci_restore_msix_state(struct pci }msi_unlock_descs (&dev->dev); +pci_write_config_word (dev,PCI_COMMAND , cmd);pci_msix_clear_and_set_ctrl (dev,PCI_MSIX_FLAGS_MASKALL ,0 ); }
Greg Kroah-Hartman
maintainerPATCH 227/1424 PCI/proc: Avoid spurious runtime PM wakeup on config space accesses
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit 4ff664a81d729b37f2eb65de80a670abfb61c9a0 upstream.
Currently, proc_bus_pci_read() and proc_bus_pci_write() do not return early for zero-length configuration space accesses at valid offsets.
Such an access invokes pci_config_pm_runtime_get() and pci_config_pm_runtime_put() around transfer blocks that do nothing.
This is a problem because pci_config_pm_runtime_get() synchronously resumes the upstream bridge through pm_runtime_get_sync(), and resumes the device itself through pm_runtime_resume() when it is in D3cold, only for the handler to return zero immediately afterwards. Such a spurious wakeup wastes power and adds needless resume latency.
The sysfs core already returns early for in-range zero-length binary attribute accesses before pci_read_config() or pci_write_config() is invoked. In contrast, the VFS forwards zero-length requests to the procfs callbacks, where they continue into runtime PM handling.
Return early from proc_bus_pci_read() and proc_bus_pci_write() when nbytes is zero, before any runtime PM involvement.
The value returned to userspace at these offsets remains zero, so the change is not visible to userspace.
--- drivers/pci/proc.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/pci/proc.c +6 -0
@@ -45,6 +45,9 @@ static ssize_t proc_bus_pci_read(structelse size =64 ; +if (!nbytes) +return 0 ; +if (pos >= size)return 0 ;if (nbytes >= size)
@@ -121,6 +124,9 @@ static ssize_t proc_bus_pci_write(structif (ret)return ret; +if (!nbytes) +return 0 ; +if (pos >= size)return 0 ;if (nbytes >= size)
Greg Kroah-Hartman
maintainerPATCH 228/1424 PCI/proc: Use file_ns_capable() when checking config space read access
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit f82f53e75eff382fc8f56b73279b54f7cf5a5c65 upstream.
proc_bus_pci_read() decides how much of the config space is readable based on capable(CAP_SYS_ADMIN), which checks the credentials of the task calling read(), not the credentials of the process that opened the file.
The sysfs equivalent, pci_read_config(), has checked the credentials of the opening process since commit de139a339395 ("pci: check caps from sysfs file open to read device dependent config space"), so a privileged process can open the config space file and pass the file descriptor to an unprivileged process (for example, a process running a KVM guest with an assigned device), which can then read the entire config space. The check was subsequently routed through the LSM framework in commit 47970b1b2aa6 ("pci:
use security_capable() when checking capablities during config space read") and converted to the dedicated helper in commit ab0fa82b2df9 ("pci-sysfs:
use proper file capability helper function").
Thus, the two interfaces check the same capability against different credentials. Checking the credentials of the task calling read() makes the outcome depend on who reads rather than who opened, so the restriction is bypassed whenever a more privileged process reads through the descriptor. Checking the credentials recorded in file->f_cred settles the decision at open() time and ties it to the file, where it cannot change with the caller.
Use file_ns_capable() to check CAP_SYS_ADMIN against the credentials in effect when the file was opened, bringing the procfs interface in line with the sysfs behaviour.
As a result, a file descriptor opened by a privileged process and passed to an unprivileged one now allows the entire config space to be read through procfs, matching sysfs.
--- drivers/pci/proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/proc.c +1 -1
@@ -38,7 +38,7 @@ static ssize_t proc_bus_pci_read(struct* undefined locations (think of Intel PIIX4 as a typical example). */ -if (capable (CAP_SYS_ADMIN )) +if (file_ns_capable (file, &init_user_ns,CAP_SYS_ADMIN )) size = dev->cfg_size;else if (dev->hdr_type ==PCI_HEADER_TYPE_CARDBUS ) size =128 ;
Greg Kroah-Hartman
maintainerPATCH 229/1424 PCI/proc: Warn on writes to kernel-exclusive config space regions
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit 3359e044d597dd5344f17613e4be6b6e12067f60 upstream.
Currently, a driver can claim a region of a device's config space as exclusive using pci_request_config_region_exclusive(), after which a write to that region originating from user space is expected to emit a warning and taint the kernel. The check is advisory only, as the write itself is still allowed to proceed.
Since commit 278294798ac9 ("PCI: Allow drivers to request exclusive config regions"), the sysfs config space attribute performs this check in pci_write_config(), but the procfs interface was never updated. A write performed through /proc/bus/pci/BB/DD.F therefore bypasses the detection entirely, even though both interfaces offer the same level of access.
Add the same resource_is_exclusive() check to proc_bus_pci_write().
--- drivers/pci/proc.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/pci/proc.c +8 -0
@@ -14,6 +14,8 @@#include <linux/capability.h> #include <linux/uaccess.h> #include <linux/security.h> +#include <linux/panic.h> +#include <linux/sched.h> #include <asm/byteorder.h> #include "pci.h"
@@ -127,6 +129,12 @@ static ssize_t proc_bus_pci_write(structif (!nbytes)return 0 ; +if (resource_is_exclusive (&dev->driver_exclusive_resource, pos, nbytes)) { +pci_warn_once (dev,"%s: Unexpected write to kernel-exclusive config offset %x" , + current->comm, pos); +add_taint (TAINT_USER ,LOCKDEP_STILL_OK ); + } +if (pos >= size)return 0 ;if (nbytes >= size)
Greg Kroah-Hartman
maintainerPATCH 230/1424 iommu/vt-d: Fix no_iommu to disable platform opt-in
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Tian <[email protected]>
commit 219cc978d69ce9b538d0d73936c569d4ca5b0a24 upstream.
If user explicitly requests to disable iommu (via "iommu=off" or "intel_iommu=off"), there is no reason to force enabling it due to platform opt-in (for external-facing devices). User should be aware of any security implication of doing so.
"intel_iommu=off" implements this policy by setting no_platform_optin to skip platform opt-in in platform_optin_force_iommu().
However, "iommu=off" (no_iommu=1) doesn't set no_platform_optin hence is broken in this aspect:
- detect_intel_iommu() doesn't request ACS if no_iommu=1
- platform_optin_force_iommu() forces iommu on if external-facing devices exist and no_platform_optin is not set
This leads to a bad configuration with ACS disabled while DMA remapping is enabled.
Instead of setting no_platform_optin (will soon be removed) for no_iommu=1, directly check no_iommu in platform_optin_force_iommu().
--- drivers/iommu/intel/iommu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/iommu/intel/iommu.c +3 -3
@@ -3723,10 +3723,11 @@ static inline bool has_external_pci(voidstatic int __init platform_optin_force_iommu (void ) { -if (!dmar_platform_optin () || no_platform_optin || !has_external_pci ()) +if (no_iommu || !dmar_platform_optin () || no_platform_optin || + !has_external_pci ())return 0 ; -if (no_iommu || dmar_disabled) +if (dmar_disabled)pr_info ("Intel-IOMMU force enabled due to platform opt in\n" );/*
@@ -3737,7 +3738,6 @@ static int __init platform_optin_force_iiommu_set_default_passthrough (false); dmar_disabled =0 ; - no_iommu =0 ;return 1 ; }
Greg Kroah-Hartman
maintainerPATCH 231/1424 iommu/vt-d: Force requesting ACS when tboot is enabled
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Tian <[email protected]>
commit 607432b2618b61df81134be0ef2562b8300c1216 upstream.
Currently the conditions of requesting ACS in detect_intel_iommu() don't include tboot, leading to a possible misconfiguration with ACS disabled (e.g. due to user opts) while iommu is later forced on by tboot_force_iommu().
Fix it by checking tboot in detect_intel_iommu().
--- drivers/iommu/intel/dmar.c | 15 +++++++++++++-- drivers/iommu/intel/iommu.c | 2 +- drivers/iommu/intel/iommu.h | 2 ++ 3 files changed, 16 insertions(+), 3 deletions(-)
drivers/iommu/intel/dmar.c +13 -2
@@ -914,6 +914,18 @@ dmar_validate_one_drhd(struct acpi_dmar_return 0 ; } +static bool dmar_required (void ) +{ +/* tboot supersedes any user/platform opt */ +if (!intel_iommu_tboot_noforce &&tboot_enabled ()) +return true; + +if (!no_iommu && (!dmar_disabled ||dmar_platform_optin ())) +return true; + +return false; +} +void __init detect_intel_iommu (void ) {int ret;
@@ -927,8 +939,7 @@ void __init detect_intel_iommu(void)if (!ret) ret =dmar_walk_dmar_table ((struct acpi_table_dmar *)dmar_tbl, &validate_drhd_cb); -if (!ret && !no_iommu && !iommu_detected && - (!dmar_disabled ||dmar_platform_optin ())) { +if (!ret && !iommu_detected &&dmar_required ()) { iommu_detected =1 ;/* Make sure ACS will be enabled */ pci_request_acs ();
drivers/iommu/intel/iommu.c +1 -1
@@ -139,7 +139,7 @@ static int rwbf_quirk;* (used when kernel is launched w/ TXT) */ static int force_on =0 ; -static int intel_iommu_tboot_noforce; +int intel_iommu_tboot_noforce;static int no_platform_optin;#define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry))
drivers/iommu/intel/iommu.h +2 -0
@@ -910,6 +910,7 @@ static inline bool ecmd_has_pmu_essentiaextern int dmar_disabled;extern int intel_iommu_enabled; +extern int intel_iommu_tboot_noforce;#else static inline int iommu_calculate_agaw (struct intel_iommu *iommu) {
@@ -922,6 +923,7 @@ static inline int iommu_calculate_max_sa#define dmar_disabled (1)#define intel_iommu_enabled (0)#define intel_iommu_sm (0) +#define intel_iommu_tboot_noforce (0)#endif static inline const char *decode_prq_descriptor(char *str, size_t size,
Greg Kroah-Hartman
maintainerPATCH 232/1424 platform/x86: dell-wmi-sysman: Dont hex dump attribute security buffer
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit 83c80495e45eddf64c6525fb582d8db68f256b71 upstream.
set_attribute() populates the security area of the BIOS attribute request buffer with the current admin password via populate_security_buffer(), then dumps the whole request buffer with print_hex_dump_bytes(). This can expose the plaintext admin password in the kernel log.
The same issue was fixed for the password attribute path by commit d1a196e0a6dc ("platform/x86: dell-wmi-sysman: Don't hex dump plaintext password data"). Remove the remaining dump from the BIOS attribute path.
--- drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c | 1 - 1 file changed, 1 deletion(-)
drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c +0 -1
@@ -84,7 +84,6 @@ int set_attribute(const char *a_name, coif (ret <0 ) goto out; -print_hex_dump_bytes ("set attribute data: " ,DUMP_PREFIX_NONE , buffer, buffer_size); ret =call_biosattributes_interface (wmi_priv.bios_attr_wdev, buffer, buffer_size,SETATTRIBUTE_METHOD_ID );
Greg Kroah-Hartman
maintainerPATCH 233/1424 platform/x86: ISST: Validate level in perf mask ioctls
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit 80e0d353c86a9a168ad6d213f494796294381538 upstream.
isst_if_get_perf_level_mask() and isst_if_get_base_freq_mask() use the user-provided level as an index into perf_levels[] via _read_pp_level_info() and _read_bf_level_info(), but neither helper validates it first.
The adjacent level-info helpers reject levels above max_level before reading the same per-level register block. Add the same bounds checks to the mask helpers, and reject disabled SST-PP levels in isst_if_get_perf_level_mask() to match isst_if_get_perf_level_info().
This prevents out-of-bounds reads from the per-level offset table on invalid ioctl input.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +9 -0
@@ -1007,6 +1007,12 @@ static int isst_if_get_perf_level_mask(vif (!power_domain_info)return -EINVAL ; +if (cpumask.level > power_domain_info->max_level) +return -EINVAL ; + +if (!(power_domain_info->pp_header.level_en_mask &BIT (cpumask.level))) +return -EINVAL ; +_read_pp_level_info ("mask" , mask, cpumask.level,SST_PP_INFO_2_OFFSET ,SST_PP_RSLVD_CORE_MASK_START ,SST_PP_RSLVD_CORE_MASK_WIDTH ,SST_MUL_FACTOR_NONE )
@@ -1088,6 +1094,9 @@ static int isst_if_get_base_freq_mask(voif (!power_domain_info)return -EINVAL ; +if (cpumask.level > power_domain_info->max_level) +return -EINVAL ; +_read_bf_level_info ("BF-cpumask" , mask, cpumask.level,SST_BF_INFO_1_OFFSET ,P1_HI_CORE_MASK_START ,P1_HI_CORE_MASK_WIDTH ,SST_MUL_FACTOR_NONE )
Greg Kroah-Hartman
maintainerPATCH 234/1424 platform/x86: ISST: Validate socket ID in clos_assoc ioctl
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit a89f07db0cb95c54dac4a8406c79a04e44a73c3c upstream.
isst_if_clos_assoc() validates the user-supplied socket_id with 'socket_id > topology_max_packages()', but isst_common.sst_inst[] is allocated with topology_max_packages() entries, so the valid index range is [0, topology_max_packages()). The '>' comparison lets socket_id == topology_max_packages() pass and index one entry past the array.
In addition, isst_common.sst_inst[socket_id] is NULL for an in-range package that has no bound TPMI SST instance, and the pointer is used without a NULL check. Both the out-of-bounds entry and the NULL pointer are then dereferenced by map_partition_power_domain_id() and the following power_domain_info access.
Reject socket_id >= topology_max_packages() and a NULL sst_inst, matching the checks already performed by get_instance().
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +3 -1
@@ -573,7 +573,7 @@ static long isst_if_clos_assoc(void __usif (copy_from_user (&clos_assoc, ptr,sizeof (clos_assoc)))return -EFAULT ; -if (clos_assoc.socket_id >topology_max_packages ()) +if (clos_assoc.socket_id >=topology_max_packages ())return -EINVAL ; cpu = clos_assoc.logical_cpu;
@@ -591,6 +591,8 @@ static long isst_if_clos_assoc(void __us pkg_id = clos_assoc.socket_id; sst_inst = isst_common.sst_inst[pkg_id]; +if (!sst_inst) +return -EINVAL ;if (clos_assoc.power_domain_id > sst_inst->number_of_power_domains)return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 235/1424 mmc: via-sdmmc: stop card-detect handling on probe failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 088eaa92fcebaa6b957ccf9635afdf39643a577d upstream.
request_irq() registers the SD card-detect interrupt and the probe enables it before mmc_add_host() runs. If mmc_add_host() fails, the error path only unmaps the registers and returns: the interrupt stays registered, so the handler keeps running against the host once it is freed. via_sdc_isr() dereferences sdhost and its MMIO base and schedules carddet_work, which via_sdc_card_detect() also runs against freed memory through its container_of() dereference.
Add a probe-error path that disables and frees the interrupt and cancels carddet_work before unmapping. carddet_work can re-enable the device interrupt via via_reset_pcictrl(), which restores PCIINTCTRL, so mask it again after cancelling the work.
This issue was found by an in-house static analysis tool and confirmed by manual code review.
--- drivers/mmc/host/via-sdmmc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/mmc/host/via-sdmmc.c +7 -1
@@ -1153,10 +1153,16 @@ static int via_sd_probe(struct pci_dev * ret =mmc_add_host (mmc);if (ret) - goto unmap; + goto free_irq;return 0 ; +free_irq: +writeb (0x0 , sdhost->pcictrl_mmiobase +VIA_CRDR_PCIINTCTRL ); +free_irq (pcidev->irq, sdhost); +cancel_work_sync (&sdhost->carddet_work); +/* carddet_work may re-enable the interrupt via via_reset_pcictrl(). */ +writeb (0x0 , sdhost->pcictrl_mmiobase +VIA_CRDR_PCIINTCTRL ); unmap:iounmap (sdhost->mmiobase); free_mmc_host:
Greg Kroah-Hartman
maintainerPATCH 236/1424 platform/x86: ISST: Just allow 2 bits for SST feature enable
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit 0f377f2b47646abe6ec3616ae6a8670d9ff7eb86 upstream.
Currently only 2 features SST-TF and SST-BF are supported, so only allow bit 0 and bit 1.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +4 -0
@@ -686,6 +686,7 @@ static long isst_if_clos_assoc(void __us#define SST_PP_FEATURE_STATE_START 8#define SST_PP_FEATURE_STATE_WIDTH 8 +#define SST_PP_FEATURE_STATE_VALID_MASK GENMASK(1, 0)#define SST_BF_FEATURE_SUPPORTED_START 12#define SST_BF_FEATURE_SUPPORTED_WIDTH 1
@@ -806,6 +807,9 @@ static int isst_if_set_perf_feature(voidif (!power_domain_info)return -EINVAL ; +if (perf_feature.feature & ~SST_PP_FEATURE_STATE_VALID_MASK ) +return -EINVAL ; +_write_pp_info ("perf_feature" , perf_feature.feature,SST_PP_CONTROL_OFFSET ,SST_PP_FEATURE_STATE_START ,SST_PP_FEATURE_STATE_WIDTH ,SST_MUL_FACTOR_NONE )
Greg Kroah-Hartman
maintainerPATCH 237/1424 platform/x86: ISST: Validate logical CPU id and clos id
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit 124e2dbabe460c2a6e7440f4ad8af560131295c9 upstream.
Validate max CLOS ID and logical CPU ID for core power feature. Reject any clos level or logical CPU number greater than the supported maximum. These are used to calculate MMIO offset.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 13 ++++++++++++ 1 file changed, 13 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +13 -0
@@ -497,6 +497,8 @@ static long isst_if_core_power_state(voi#define SST_CLOS_CONFIG_MAX_START 16#define SST_CLOS_CONFIG_MAX_WIDTH 8 +#define SST_MAX_CLOS 3 +static long isst_if_clos_param (void __user *argp) {struct tpmi_per_power_domain_info *power_domain_info;
@@ -505,6 +507,9 @@ static long isst_if_clos_param(void __usif (copy_from_user (&clos_param, argp,sizeof (clos_param)))return -EFAULT ; +if (clos_param.clos >SST_MAX_CLOS ) +return -EINVAL ; + power_domain_info =get_instance (clos_param.socket_id, clos_param.power_domain_id);if (!power_domain_info)return -EINVAL ;
@@ -548,6 +553,8 @@ static long isst_if_clos_param(void __us#define SST_CLOS_ASSOC_CPUS_PER_REG 16#define SST_CLOS_ASSOC_BITS_PER_CPU 4 +#define SST_CLOS_ASSOC_MAX_LOGICAL_CPU 63 +static long isst_if_clos_assoc (void __user *argp) {struct isst_if_clos_assoc_cmds assoc_cmds;
@@ -573,9 +580,15 @@ static long isst_if_clos_assoc(void __usif (copy_from_user (&clos_assoc, ptr,sizeof (clos_assoc)))return -EFAULT ; +if (clos_assoc.clos >SST_MAX_CLOS ) +return -EINVAL ; +if (clos_assoc.socket_id >=topology_max_packages ())return -EINVAL ; +if (clos_assoc.logical_cpu >SST_CLOS_ASSOC_MAX_LOGICAL_CPU ) +return -EINVAL ; + cpu = clos_assoc.logical_cpu; clos = clos_assoc.clos;
Greg Kroah-Hartman
maintainerPATCH 238/1424 platform/x86: ishtp_eclite: Fix ACPI device reference leak in probe error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ma Ke <[email protected]>
commit 62b57396c26a1ce54963709928ea0d01fa522eea upstream.
ecl_ishtp_cl_probe() acquires a reference to an ACPI device via acpi_find_eclite_device() but fails to release it in the error path when acpi_opregion_init() fails. This results in a reference count leak, preventing proper cleanup of the ACPI device.
Calling path: acpi_find_eclite_device() -> acpi_dev_get_first_match_dev() -> acpi_dev_get_next_match_dev() -> bus_find_device() -> get_device().
Found by code review.
--- drivers/platform/x86/intel/ishtp_eclite.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/platform/x86/intel/ishtp_eclite.c +4 -1
@@ -600,13 +600,16 @@ static int ecl_ishtp_cl_probe(struct ish rv =acpi_opregion_init (opr_dev);if (rv) {dev_err (cl_data_to_dev (opr_dev),"ACPI opregion init failed\n" ); - goto err_exit; + goto err_put; }/* Reprobe devices depending on ECLite - battery, fan, etc. */ acpi_dev_clear_dependencies (opr_dev->adev);return 0 ; + +err_put: +acpi_dev_put (opr_dev->adev); err_exit:ishtp_set_connection_state (ecl_ishtp_cl,ISHTP_CL_DISCONNECTING );ishtp_cl_disconnect (ecl_ishtp_cl);
Greg Kroah-Hartman
maintainerPATCH 239/1424 platform/chrome: sensorhub: Bound the EC-reported sensor number
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 833740a2333c2e4db4e02e3d0ffba04e8718a5f3 upstream.
Each EC FIFO event carries an 8-bit sensor number (in->sensor_num). cros_ec_sensorhub_ring_handler() validates the FIFO event count, the per-read count and the ring bound, but not the sensor number, which cros_ec_sensor_ring_process_event() then uses unchecked to index sensorhub->batch_state[] - allocated with only sensorhub->sensor_num entries. A sensor number of sensor_num or larger is an out-of-bounds read and write of batch_state[].
Validate the sensor number in the ring handler, where each event is read from the EC, and drop a malformed event before it is used.
--- drivers/platform/chrome/cros_ec_sensorhub_ring.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/platform/chrome/cros_ec_sensorhub_ring.c +8 -0
@@ -851,6 +851,14 @@ static void cros_ec_sensorhub_ring_handlfor (in = sensorhub->resp->fifo_read.data, j =0 ; j < number_data; j++, in++) { +/* Skip event if sensor_num from EC is out of bounds. */ +if (in->sensor_num >= sensorhub->sensor_num) { +dev_warn_ratelimited (sensorhub->dev, +"Invalid sensor number %u from EC\n" , + in->sensor_num); +continue ; + } + if (cros_ec_sensor_ring_process_event( sensorhub, fifo_info, fifo_timestamp,
Greg Kroah-Hartman
maintainerPATCH 240/1424 platform/x86: hp-bioscfg: accept reduced ACPI packages from older HP BIOS
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 40e10e6cc8f70c041431a1e30186807e28ec46e0 upstream.
hp_init_bios_package_attribute() hard-fails when a WMI ACPI package contains fewer elements than the type-specific expected count (e.g. 11 elements instead of 13 for INTEGER or ENUMERATION attributes). This causes the entire hp_bioscfg driver to skip attribute enumeration on older HP hardware whose BIOS returns shortened packages when optional fields like prerequisites or possible values are absent.
Observed on HP EliteBook 840 G2 (BIOS M71 Ver. 01.31):
hp_bioscfg: ACPI-package does not have enough elements: 11 < 13
The element layout has two tiers:
- Elements 0-9 (SECURITY_LEVEL+1 = 10): common to all attribute types
- Elements 10-N: type-specific (bounds, values, encodings, ...)
The per-type populate functions (hp_populate_*_elements_from_package) already handle sparse packages correctly via their own elem < count loop guards and inner-loop bounds checks. The only unsafe case is when we lack even the common elements needed to register the attribute.
Fix by introducing COMMON_ELEM_CNT to mark the hard minimum (10), and splitting the check into two tiers:
- Fewer than COMMON_ELEM_CNT elements: hard fail, can't proceed.
- Fewer than expected type-specific elements: warn, but let the populate function parse what is available.
--- drivers/platform/x86/hp/hp-bioscfg/bioscfg.c | 11 ++++++++--- drivers/platform/x86/hp/hp-bioscfg/bioscfg.h | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +8 -3
@@ -666,12 +666,17 @@ static int hp_init_bios_package_attributint ret =0 ;/* Take action appropriate to each ACPI TYPE */ -if (obj->package.count < min_elements) { -pr_err ("ACPI-package does not have enough elements: %d < %d\n" , - obj->package.count, min_elements); +if (obj->package.count <COMMON_ELEM_CNT ) { +pr_err ("ACPI-package is missing common elements: %d < %d\n" , + obj->package.count,COMMON_ELEM_CNT ); goto pack_attr_exit; } +if (obj->package.count < min_elements) { +pr_warn ("ACPI-package has fewer elements than expected: %d < %d, parsing available elements\n" , + obj->package.count, min_elements); + } + elements = obj->package.elements;/* sanity checking */
drivers/platform/x86/hp/hp-bioscfg/bioscfg.h +3 -0
@@ -279,6 +279,9 @@ enum hp_wmi_data_elements {PSWD_ENCODINGS =13 ,PSWD_IS_SET =14 ,PSWD_ELEM_CNT =15 , + +/* Minimum elements shared by all attribute types (NAME..SECURITY_LEVEL) */ + COMMON_ELEM_CNT = SECURITY_LEVEL + 1, };#define GET_INSTANCE_ID (type) \
Greg Kroah-Hartman
maintainerPATCH 241/1424 platform/x86: hp-bioscfg: advance elem past consumed array elements
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 05c808362e808e196f75696b8a64f7aa8b2245ce upstream.
The outer parsing loop in each attribute-type parser advances "elem" (the index into the ACPI package element array) by exactly one per iteration, but cases that consume multi-element arrays (PREREQUISITES, ENUM_POSSIBLE_VALUES, PSWD_ENCODINGS) read "size" consecutive elements without adjusting "elem" for the extra entries consumed beyond the first. The next outer iteration then re-reads a leftover element from the array just consumed instead of the next real property, and the type check fails on that stale element, aborting the parse with -EIO.
This produces exactly the failure visible in dmesg on the test hardware, on every boot:
Error expected type 2 for elem 13, but got type 1 instead
hp_bioscfg: Returned error 0x3, "Invalid command value/Feature not supported"
Fix by advancing "elem" by (size - 1) after each array-consuming loop, so the outer loop's own "elem++" lands on the correct next element. "eloc" is intentionally left alone: it indexes the logical property schema, not the physical element array, and each array case is still exactly one logical property regardless of how many physical elements it spans.
The defect is identical across all five attribute-type parsers (enum, integer, string, ordered-list, password), which were copy-pasted from the same template when the driver was introduced.
--- drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c | 4 ++++ drivers/platform/x86/hp/hp-bioscfg/int-attributes.c | 2 ++ drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c | 2 ++ drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c | 4 ++++ drivers/platform/x86/hp/hp-bioscfg/string-attributes.c | 2 ++ 5 files changed, 14 insertions(+)
drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +4 -0
@@ -232,6 +232,8 @@ static int hp_populate_enumeration_elemekfree (str_value); str_value =NULL ; } +if (size) + elem += size -1 ;break ;case SECURITY_LEVEL :
@@ -288,6 +290,8 @@ static int hp_populate_enumeration_elemekfree (str_value); str_value =NULL ; } +if (size) + elem += (size <MAX_VALUES_SIZE ? size :MAX_VALUES_SIZE ) -1 ;break ;default :pr_warn ("Invalid element: %d found in Enumeration attribute or data may be malformed\n" , elem);
drivers/platform/x86/hp/hp-bioscfg/int-attributes.c +2 -0
@@ -246,6 +246,8 @@ static int hp_populate_integer_elements_kfree (str_value); str_value =NULL ; } +if (size) + elem += size -1 ;break ;case SECURITY_LEVEL :
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +2 -0
@@ -239,6 +239,8 @@ static int hp_populate_ordered_list_elemkfree (str_value); str_value =NULL ; } +if (size) + elem += size -1 ;break ;case SECURITY_LEVEL :
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +4 -0
@@ -329,6 +329,8 @@ static int hp_populate_password_elements str_value =NULL ; } +if (size) + elem += size -1 ;break ;case SECURITY_LEVEL : password_data->common.security_level = int_value;
@@ -372,6 +374,8 @@ static int hp_populate_password_elements str_value =NULL ; } +if (size) + elem += size -1 ;break ;case PSWD_IS_SET : password_data->is_enabled = int_value;
drivers/platform/x86/hp/hp-bioscfg/string-attributes.c +2 -0
@@ -237,6 +237,8 @@ static int hp_populate_string_elements_fkfree (str_value); str_value =NULL ; } +if (size) + elem += size -1 ;break ;case SECURITY_LEVEL :
Greg Kroah-Hartman
maintainerPATCH 242/1424 platform/x86: hp-bioscfg: bound ordered-list parsing by the package count
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 1d143d78299d0eb4536698bf98c1815ec69f22a9 upstream.
hp_populate_ordered_list_elements_from_package() differs from the other per-type parsers: its main loop is bounded only by the fixed per-type count and never checks elem against the number of elements actually present in the package,
for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT; elem++, eloc++)
whereas the string, integer, enumeration and password parsers bound their main loop with "elem < count" as well.
This is safe today because hp_init_bios_package_attribute() rejects any package with fewer than ORD_ELEM_CNT elements before the parser runs. An upcoming change, however, relaxes that check to accept shorter packages.
Bound the loop by the validated element count as well, so it stops at whichever comes first, the per-type count or the real package size,
for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT && elem < order_obj_count;
elem++, eloc++)order_obj_count is the validated element count, now correctly forwarded from the caller. No functional change for packages that enumerate correctly today.
--- drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +1 -1
@@ -148,7 +148,7 @@ static int hp_populate_ordered_list_elemif (!order_obj)return -EINVAL ; -for (elem =1 , eloc =1 ; eloc <ORD_ELEM_CNT ; elem++, eloc++) { +for (elem =1 , eloc =1 ; eloc <ORD_ELEM_CNT && elem < order_obj_count; elem++, eloc++) {switch (order_obj[elem].type) {case ACPI_TYPE_STRING :
Greg Kroah-Hartman
maintainerPATCH 243/1424 platform/x86: hp-bioscfg: fix heap OOB read in sk_store() and kek_store()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit a7508c7959ff8d037327d377ed21a9c0eabe4674 upstream.
sk_store() and kek_store() strip a trailing newline from the sysfs write before allocating the key buffer:
length = count; if (buf[length - 1] == '\n') length--; bioscfg_drv.spm_data.signing_key = kmemdup(buf, length, GFP_KERNEL);
but then pass the original "count" (not "length") as the copy size to hp_wmi_perform_query(), which memcpy()s that many bytes out of the "length"-sized allocation, reading one byte past it whenever the write ends in a newline, the normal case for a shell "echo" into sysfs.
KASAN confirms this directly:
BUG: KASAN: slab-out-of-bounds in hp_wmi_perform_query+0x1e9/0x460 [hp_bioscfg] Read of size 28 at addr ffff88813c8e2b80 by task python3/16022 ... sk_store+0xa7/0x240 [hp_bioscfg] kernfs_fop_write_iter+0x3e1/0x5d0 ... The buggy address is located 0 bytes inside of allocated 27-byte region [ffff88813c8e2b80, ffff88813c8e2b9b)
Reproduced identically for kek_store, and at multiple write sizes (28, 57, 201 bytes), each time reading exactly one byte past a kmemdup() allocation one byte smaller than the write.
Fix by passing "length" instead of "count" to hp_wmi_perform_query() in both functions.
--- drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c +2 -2
@@ -238,7 +238,7 @@ static ssize_t sk_store(struct kobject * ret =hp_wmi_perform_query (HPWMI_SECUREPLATFORM_SET_SK ,HPWMI_SECUREPLATFORM , (void *)bioscfg_drv.spm_data.signing_key, - count,0 ); + length,0 );if (!ret) { bioscfg_drv.spm_data.mechanism =SIGNING_KEY ;
@@ -274,7 +274,7 @@ static ssize_t kek_store(struct kobject ret =hp_wmi_perform_query (HPWMI_SECUREPLATFORM_SET_KEK ,HPWMI_SECUREPLATFORM , (void *)bioscfg_drv.spm_data.endorsement_key, - count,0 ); + length,0 );if (!ret) { bioscfg_drv.spm_data.mechanism =ENDORSEMENT_KEY ;
Greg Kroah-Hartman
maintainerPATCH 244/1424 platform/x86: hp-bioscfg: fix heap OOB read on empty password write
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 2b2ec354f905c14e3270e8ec3ab50f7d8ad73bab upstream.
validate_password_input() computes length = strlen(buf) and then checks buf[length - 1] to strip a trailing newline, without checking that length is nonzero first. Writing an empty string (a bare '\n') to current_password or new_password gives length == 0, and buf[length - 1] reads buf[-1], one byte before the heap allocation holding the copied input.
KASAN confirms this directly:
BUG: KASAN: slab-out-of-bounds in store_password_instance.constprop.0+0x223/0x2a0 [hp_bioscfg] Read of size 1 at addr ffff88811bd8da9f by task sh/13740 ... store_password_instance.constprop.0+0x223/0x2a0 [hp_bioscfg] current_password_store+0x14/0x20 [hp_bioscfg] ... The buggy address is located 23 bytes to the right of allocated 8-byte region [ffff88811bd8da80, ffff88811bd8da88)
Reproduced identically via new_password_store. Execution continues past the bad read (the garbage byte only affects whether "length" is decremented by one), so the write completes and returns success; this is a pure information read past the buffer, not a crash, but it is still an out-of-bounds access KASAN correctly flags.
Fix by only checking buf[length - 1] when length is nonzero.
--- drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +1 -1
@@ -67,7 +67,7 @@ static int validate_password_input(int istruct password_data *password_data = &bioscfg_drv.password_data[instance_id]; length =strlen (buf); -if (buf[length -1 ] =='\n' ) +if (length >0 && buf[length -1 ] =='\n' ) length--;if (length >MAX_PASSWD_SIZE )
Greg Kroah-Hartman
maintainerPATCH 245/1424 platform/x86: hp-bioscfg: fix new_password_store() overwriting current_password
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 2ea12a467a9cb12170417b30784fe26a243a75fe upstream.
current_password_store() and new_password_store() both call store_password_instance() with is_current = true:
static ssize_t new_password_store(...)
{
return store_password_instance(kobj, buf, count, true);
}so a write to new_password is routed to current_password instead, and the new_password field is never written by either sysfs entry point.
Fix by passing false from new_password_store(), matching what the is_current parameter is meant to select.
--- drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +1 -1
@@ -128,7 +128,7 @@ static ssize_t new_password_store(structstruct kobj_attribute *attr, const char *buf, size_t count) { - return store_password_instance(kobj, buf, count, true); + return store_password_instance(kobj, buf, count, false); }static struct kobj_attribute password_new_password =__ATTR_WO (new_password);
Greg Kroah-Hartman
maintainerPATCH 246/1424 platform/x86: hp-bioscfg: fix off-by-one write in hp_get_string_from_buffer()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit dc03f05e419f3460342fb7564884f244622634b6 upstream.
hp_get_string_from_buffer() clamps the converted string length against the destination buffer size with "size > dst_size", so when the converted length is exactly equal to dst_size, conv_dst_size is left at dst_size and the unconditional NUL terminator write
dst[conv_dst_size] = 0;
lands one byte past the destination buffer. This is the same shape of bug as the previously fixed off-by-one in hp_convert_hexstr_to_str():
the buffer is sized correctly for the content, but the terminator write is never checked against that size.
Fix by changing the comparison to ">=" so conv_dst_size is always left with room for the terminator.
All fixed-size destinations that reach this function (path[512], current_value[512], current_password/current_value[64], and the per-entry buffers in encodings[][512] and prerequisites[][512]) are affected.
--- drivers/platform/x86/hp/hp-bioscfg/bioscfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +1 -1
@@ -85,7 +85,7 @@ int hp_get_string_from_buffer(u8 **buffe* bytes. */ conv_dst_size = size; -if (size > dst_size) +if (size >= dst_size) conv_dst_size = dst_size -1 ;/*
Greg Kroah-Hartman
maintainerPATCH 247/1424 platform/x86: hp-bioscfg: fix ORD_LIST_ELEMENTS never being parsed
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit cb6b1b0fb236a9581cae213c2a9182e68cc3ffe5 upstream.
The ACPI_TYPE_STRING case explicitly skips the string conversion for elem == ORD_LIST_ELEMENTS:
if (elem != PREREQUISITES && elem != ORD_LIST_ELEMENTS) {
ret = hp_convert_hexstr_to_str(..., &str_value, &value_len);
if (ret)
continue;
}so by the time the ORD_LIST_ELEMENTS case in the eloc switch runs, str_value is NULL (it was freed and reset to NULL at the end of the previous iteration). That case then does:
ret = hp_convert_hexstr_to_str(str_value, value_len, &tmpstr, &tmp_len);
hp_convert_hexstr_to_str() rejects a NULL input with -EINVAL, which sends this function to exit_list, and exit_list unconditionally returns 0. The net effect is that any ordered-list attribute with elements present silently ends up with an empty elements list, with no error surfaced anywhere.
Fix by converting the current element directly, order_obj[elem], the same way the PREREQUISITES case already handles its own array elements, instead of reusing the unrelated str_value/value_len left over from earlier processing.
--- drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +3 -1
@@ -270,7 +270,9 @@ static int hp_populate_ordered_list_elem* Ordered list data is stored in hex and comma separated format * Convert the data and split it to show each element */ - ret =hp_convert_hexstr_to_str (str_value, value_len, &tmpstr, &tmp_len); + ret =hp_convert_hexstr_to_str (order_obj[elem].string.pointer, + order_obj[elem].string.length, + &tmpstr, &tmp_len);if (ret) goto exit_list;
Greg Kroah-Hartman
maintainerPATCH 248/1424 platform/x86: hp-bioscfg: pass validated element count to package parsers
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit e0ddfd77c0c320b7d12b6c9169303b140b798775 upstream.
The per-type package parsers are handed the wrong element count.
hp_init_bios_package_attribute() validates obj->package.count and then calls one of the five hp_populate_*_package_data() wrappers (string, integer, enumeration, ordered list, password). Each wrapper forwards a count to its hp_populate_*_elements_from_package() parser, but instead of forwarding the validated obj->package.count it derives the count from elements[0]. elements[0] is the NAME field and is always an ACPI_TYPE_STRING, so reading ->package.count from it in fact reads ->string.length through the union acpi_object. The parsers thus bound themselves against the length of the name string rather than against the real number of elements in the package.
This is safe today because hp_init_bios_package_attribute() refuses any package that has fewer than the type's element count, so a parser only ever runs on a full package and never reads past it regardless of the bogus bound.
An upcoming change relaxes that check to accept shorter packages. Once a parser can receive fewer elements than its per-type count, a bound taken from the name length no longer reflects the array size, and the "elem < count" loop conditions and "elem + n >= count" sub-loop guards read past the end of elements[] - an out-of-bounds heap read.
Forward the validated obj->package.count to every *_package_data() wrapper so the parsers bound themselves against the real package size. This does not change behaviour for the packages that enumerate correctly today and is a prerequisite for accepting shorter packages safely.
--- drivers/platform/x86/hp/hp-bioscfg/bioscfg.c | 5 +++++ drivers/platform/x86/hp/hp-bioscfg/bioscfg.h | 5 +++++ drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c | 4 +++- drivers/platform/x86/hp/hp-bioscfg/int-attributes.c | 4 +++- drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c | 6 ++++-- drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c | 6 ++++-- drivers/platform/x86/hp/hp-bioscfg/string-attributes.c | 4 +++- 7 files changed, 27 insertions(+), 7 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +5 -0
@@ -741,26 +741,31 @@ static int hp_init_bios_package_attributswitch (attr_type) {case HPWMI_STRING_TYPE : ret =hp_populate_string_package_data (elements, + obj->package.count, instance_id, attr_name_kobj);break ;case HPWMI_INTEGER_TYPE : ret =hp_populate_integer_package_data (elements, + obj->package.count, instance_id, attr_name_kobj);break ;case HPWMI_ENUMERATION_TYPE : ret =hp_populate_enumeration_package_data (elements, + obj->package.count, instance_id, attr_name_kobj);break ;case HPWMI_ORDERED_LIST_TYPE : ret =hp_populate_ordered_list_package_data (elements, + obj->package.count, instance_id, attr_name_kobj);break ;case HPWMI_PASSWORD_TYPE : ret =hp_populate_password_package_data (elements, + obj->package.count, instance_id, attr_name_kobj);break ;
drivers/platform/x86/hp/hp-bioscfg/bioscfg.h +5 -0
@@ -404,6 +404,7 @@ int hp_populate_string_buffer_data(u8 *bint hp_alloc_string_data (void );void hp_exit_string_attributes (void );int hp_populate_string_package_data (union acpi_object *str_obj, +int str_obj_count,int instance_id,struct kobject *attr_name_kobj);
@@ -414,6 +415,7 @@ int hp_populate_integer_buffer_data(u8 *int hp_alloc_integer_data (void );void hp_exit_integer_attributes (void );int hp_populate_integer_package_data (union acpi_object *integer_obj, +int integer_obj_count,int instance_id,struct kobject *attr_name_kobj);
@@ -424,6 +426,7 @@ int hp_populate_enumeration_buffer_data(int hp_alloc_enumeration_data (void );void hp_exit_enumeration_attributes (void );int hp_populate_enumeration_package_data (union acpi_object *enum_obj, +int enum_obj_count,int instance_id,struct kobject *attr_name_kobj);
@@ -435,6 +438,7 @@ int hp_populate_ordered_list_buffer_dataint hp_alloc_ordered_list_data (void );void hp_exit_ordered_list_attributes (void );int hp_populate_ordered_list_package_data (union acpi_object *order_obj, +int order_obj_count,int instance_id,struct kobject *attr_name_kobj);
@@ -443,6 +447,7 @@ int hp_populate_password_buffer_data(u8int instance_id, struct kobject *attr_name_kobj);int hp_populate_password_package_data (union acpi_object *password_obj, +int password_obj_count,int instance_id,struct kobject *attr_name_kobj);int hp_alloc_password_data (void );
drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +3 -1
@@ -312,10 +312,12 @@ exit_enumeration_package:* Populate all properties of an instance under enumeration attribute * * @enum_obj: ACPI object with enumeration data +* @enum_obj_count: Number of elements in @enum_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ int hp_populate_enumeration_package_data (union acpi_object *enum_obj, +int enum_obj_count,int instance_id,struct kobject *attr_name_kobj) {
@@ -324,7 +326,7 @@ int hp_populate_enumeration_package_data enum_data->attr_name_kobj = attr_name_kobj;hp_populate_enumeration_elements_from_package (enum_obj, - enum_obj->package.count, + enum_obj_count, instance_id);hp_update_attribute_permissions (enum_data->common.is_readonly, &enumeration_current_val);
drivers/platform/x86/hp/hp-bioscfg/int-attributes.c +3 -1
@@ -280,10 +280,12 @@ exit_integer_package:* Populate all properties of an instance under integer attribute * * @integer_obj: ACPI object with integer data +* @integer_obj_count: Number of elements in @integer_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ int hp_populate_integer_package_data (union acpi_object *integer_obj, +int integer_obj_count,int instance_id,struct kobject *attr_name_kobj) {
@@ -291,7 +293,7 @@ int hp_populate_integer_package_data(uni integer_data->attr_name_kobj = attr_name_kobj;hp_populate_integer_elements_from_package (integer_obj, - integer_obj->package.count, + integer_obj_count, instance_id);hp_update_attribute_permissions (integer_data->common.is_readonly, &integer_current_val);
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +4 -2
@@ -311,10 +311,12 @@ exit_list:* Populate all properties of an instance under ordered_list attribute * * @order_obj: ACPI object with ordered_list data +* @order_obj_count: Number of elements in @order_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ -int hp_populate_ordered_list_package_data (union acpi_object *order_obj,int instance_id, +int hp_populate_ordered_list_package_data (union acpi_object *order_obj,int order_obj_count, +int instance_id,struct kobject *attr_name_kobj) {struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];
@@ -322,7 +324,7 @@ int hp_populate_ordered_list_package_dat ordered_list_data->attr_name_kobj = attr_name_kobj;hp_populate_ordered_list_elements_from_package (order_obj, - order_obj->package.count, + order_obj_count, instance_id);hp_update_attribute_permissions (ordered_list_data->common.is_readonly, &ordered_list_current_val);
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +4 -2
@@ -399,10 +399,12 @@ exit_package:* Populate all properties for an instance under password attribute * * @password_obj: ACPI object with password data +* @password_obj_count: Number of elements in @password_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ -int hp_populate_password_package_data (union acpi_object *password_obj,int instance_id, +int hp_populate_password_package_data (union acpi_object *password_obj,int password_obj_count, +int instance_id,struct kobject *attr_name_kobj) {struct password_data *password_data = &bioscfg_drv.password_data[instance_id];
@@ -410,7 +412,7 @@ int hp_populate_password_package_data(un password_data->attr_name_kobj = attr_name_kobj;hp_populate_password_elements_from_package (password_obj, - password_obj->package.count, + password_obj_count, instance_id); hp_friendly_user_name_update(password_data->common.path,
drivers/platform/x86/hp/hp-bioscfg/string-attributes.c +3 -1
@@ -269,10 +269,12 @@ exit_string_package:* Populate all properties of an instance under string attribute * * @string_obj: ACPI object with string data +* @string_obj_count: Number of elements in @string_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ int hp_populate_string_package_data (union acpi_object *string_obj, +int string_obj_count,int instance_id,struct kobject *attr_name_kobj) {
@@ -281,7 +283,7 @@ int hp_populate_string_package_data(unio string_data->attr_name_kobj = attr_name_kobj;hp_populate_string_elements_from_package (string_obj, - string_obj->package.count, + string_obj_count, instance_id); hp_update_attribute_permissions(string_data->common.is_readonly,
Greg Kroah-Hartman
maintainerPATCH 249/1424 platform/x86: hp-bioscfg: warn on element type mismatch instead of failing
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit b0e2af3ec94e0431adb59d9f249ebbd3b7285158 upstream.
hp_populate_enumeration_elements_from_package() returns -EIO and aborts enumeration of the entire attribute when any single element has an unexpected ACPI type. This is observed on HP EliteBook 840 G2 when the BIOS returns malformed ACPI data following a failed WMI query:
ACPI BIOS Error (bug): AE_AML_BUFFER_LIMIT, Index (0x000000032)
is beyond end of object (length 0x32)
ACPI Error: Aborting method \_SB.WMID.WQBE due to previous error Error expected type 2 for elem 13, but got type 1 instead
hp_bioscfg: Returned error 0x3,
"Invalid command value/Feature not supported"
Aborting immediately discards the attribute entirely.
Warn about the unexpected element type, free the temporary string, skip the offending element, and continue parsing the remaining package instead of failing the whole attribute.
--- drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +4 -3
@@ -165,10 +165,11 @@ static int hp_populate_enumeration_eleme/* Check that both expected and read object type match */ if (expected_enum_types[eloc] != enum_obj[elem].type) { -pr_err ("Error expected type %d for elem %d, but got type %d instead\n" , - expected_enum_types[eloc], elem, enum_obj[elem].type); +pr_warn ("Unexpected element type at elem %d: expected %d, got %d, skipping\n" , + elem, expected_enum_types[eloc], enum_obj[elem].type);kfree (str_value); -return -EIO ; + str_value =NULL ; +continue ; }/* Assign appropriate element value to corresponding field */
Greg Kroah-Hartman
maintainerPATCH 250/1424 interconnect: Fix use after free in icc_get() and of_icc_get_by_index()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuan-Wei Chiu <[email protected]>
commit 25c7e242aca084fdc1098248194032317dca625d upstream.
In of_icc_get_by_index() and icc_get(), if the dynamic allocation for path->name fails via kasprintf(), the error handling path directly calls kfree(path) to free the path object and returns an error.
However, prior to this point, path_find() calls path_init(), which already links the path's requests into the req_list of the respective interconnect nodes via hlist_add_head(). Directly invoking kfree(path) leaves dangling pointers in the hlist. A subsequent call to icc_get() or icc_set_bw() will traverse or modify these corrupted lists, triggering a slab use afterfree.
KASAN report showing the vulnerability when reproducing via debugfs:
BUG: KASAN: slab-use-after-free in path_find+0x6f8/0xcfc Write of size 8 at addr fff000000d43f748 by task sh/1 ... Call trace: kasan_report+0xac/0xfc path_find+0x6f8/0xcfc icc_get+0x148/0x380 icc_get_set+0xf8/0x2d0 ... Freed by task 1: kfree+0x1a0/0x4a4 icc_get+0x2cc/0x380 icc_get_set+0xf8/0x2d0
Fix this by replacing kfree(path) with the proper teardown function, icc_put(path), which safely removes the requests from the req_list using hlist_del() and drops the provider usage references before freeing the memory.
Additionally, in icc_get(), ensure that the icc_lock mutex is released prior to calling icc_put(path) to avoid a deadlock, as icc_put() internally acquires the same lock.
--- drivers/interconnect/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/interconnect/core.c +4 -3
@@ -526,7 +526,7 @@ struct icc_path *of_icc_get_by_index(str path->name =kasprintf (GFP_KERNEL ,"%s-%s" , src_data->node->name, dst_data->node->name);if (!path->name) { -kfree (path); +icc_put (path); path =ERR_PTR (-ENOMEM ); }
@@ -624,8 +624,9 @@ struct icc_path *icc_get(struct device * path->name =kasprintf (GFP_KERNEL ,"%s-%s" , src_node->name, dst_node->name);if (!path->name) { -kfree (path); - path =ERR_PTR (-ENOMEM ); +mutex_unlock (&icc_lock); +icc_put (path); +return ERR_PTR (-ENOMEM ); } out:mutex_unlock (&icc_lock);
Greg Kroah-Hartman
maintainerPATCH 251/1424 ipmi: ipmb: validate write message length
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yousef Alhouseen <[email protected]>
commit 53637506884dbd5c91a89b1a3547d99d80f8ed2c upstream.
ipmb_write() read message fields before validating the length byte.
A zero or short write can read uninitialized stack bytes.
A length smaller than the SMBus header underflows the block write length.
Require a non-empty buffer and the minimum IPMB request length.
Also require the length byte plus payload before parsing the message.
Message-ID: <[email protected]>
--- drivers/char/ipmi/ipmb_dev_int.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/char/ipmi/ipmb_dev_int.c +3 -2
@@ -141,13 +141,14 @@ static ssize_t ipmb_write(struct file *fu8 msg[MAX_MSG_LEN ];ssize_t ret; -if (count >sizeof (msg)) +if (!count || count >sizeof (msg))return -EINVAL ;if (copy_from_user (&msg, buf, count))return -EFAULT ; -if (count < msg[0 ]) +if (msg[IPMB_MSG_LEN_IDX ] <IPMB_REQUEST_LEN_MIN || + count < (size_t )msg[IPMB_MSG_LEN_IDX ] +1 )return -EINVAL ; rq_sa =GET_7BIT_ADDR (msg[RQ_SA_8BIT_IDX ]);
Greg Kroah-Hartman
maintainerPATCH 252/1424 ipmi: si: Fix NULL pointer dereference after failed registration
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Seiji Nishikawa <[email protected]>
commit 6d920a75df9a83ab096b3cde7a643b656e4fdfeb upstream.
try_smi_init() allocates new_smi->si_sm and later calls ipmi_register_smi_mod(), which maps to ipmi_add_smi().
During ipmi_add_smi(), the upper IPMI message handler obtains the initial BMC device information through __bmc_get_device_id(). This can fail if the BMC does not return a successful response to the Get Device ID command.
When the BMC returns a nonzero completion code, the device-id helper retries the command and eventually returns -EIO if the device ID still cannot be fetched.
On this failure path, ipmi_add_smi() logs "Unable to get the device id" and goes to out_err_started, where it invokes the lower driver's shutdown callback. try_smi_init() then logs the returned registration failure:
ipmi_si IPI0001:00: IPMI message handler: Unable to get the device id: -5 ipmi_si IPI0001:00: Unable to register device: error -5
For ipmi_si, the shutdown callback is shutdown_smi(), which cleans up the SI state machine data, frees smi_info->si_sm, and sets smi_info->si_sm and smi_info->intf to NULL.
However, intf->in_shutdown is not set on this failed-registration rollback path. Therefore, the asynchronous redo_bmc_reg work item can still retry BMC device-id probing after the lower driver has already cleared its SI state machine data. In the observed case, that retry path reached start_next_msg(), which passed the NULL smi_info->si_sm pointer to the selected KCS state machine handler:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 Workqueue: events redo_bmc_reg [ipmi_msghandler] RIP: start_kcs_transaction+0x2c/0x190 [ipmi_si] Call Trace: start_next_msg+0x50/0x80 [ipmi_si] check_start_timer_thread.part.9+0x3b/0x50 [ipmi_si] sender+0x69/0x80 [ipmi_si] i_ipmi_request+0x2ac/0x9d0 [ipmi_msghandler] __get_device_id.isra.29+0xaa/0x180 [ipmi_msghandler] __bmc_get_device_id+0xef/0x950 [ipmi_msghandler] redo_bmc_reg+0x52/0x60 [ipmi_msghandler] process_one_work+0x1a7/0x360
Set intf->in_shutdown on the out_err_started path before invoking the lower driver's shutdown callback. This prevents later redo_bmc_reg retries from using an interface whose lower driver state has been cleaned up, and applies the same shutdown state to other IPMI interfaces as well.
Message-ID: <[email protected]>
--- drivers/char/ipmi/ipmi_msghandler.c | 1 + 1 file changed, 1 insertion(+)
drivers/char/ipmi/ipmi_msghandler.c +1 -0
@@ -3693,6 +3693,7 @@ int ipmi_add_smi(struct module * out_err_bmc_reg:ipmi_bmc_unregister (intf); out_err_started: + intf->in_shutdown = true;if (intf->handlers->shutdown) intf->handlers->shutdown (intf->send_info); out_err:
Greg Kroah-Hartman
maintainerPATCH 253/1424 net/iucv: filter frames in afiucv_hs_rcv() by ingress device
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandra Winter <[email protected]>
commit 80230a18c164a4b5bbc048fe2768b219ac17bc5a upstream.
afiucv_hs_rcv() selects a socket from iucv_sk_list by matching four 8-byte name fields in the transport header alone. No check is made against the net_device the frame arrived on.
This can cause a frame arriving on any netdev to be delivered to an AF_IUCV socket. Three problems follow.
First, a frame arriving over HiperSockets can be delivered to a socket bound to the classic z/VM IUCV transport, which has iucv->hs_dev == NULL. iucv_sock_bind() takes the classic path whenever the requested userid matches iucv_userid, even on a guest that also has a HiperSockets device carrying the same identifier. The child socket created by afiucv_hs_callback_syn() for such a match inherits hs_dev = NULL and transport = AF_IUCV_TRANS_HIPER, so the first send() on it returns -ENODEV. The socket delivered to accept() is unusable.
Second, a frame arriving on one netdev can be delivered to a socket bound to a different IQD device. Which can lead to
- Accept-queue exhaustion (DoS)
- Attacker-controlled peer identity in the child socket
- Data injection into existing sockets
- Fabric noise on the IQD fabric, where bogus replies are sent
- killing established connections
Third, all AF_IUCV sockets live in init_net, as iucv_sock_alloc() calls sk_alloc(&init_net, ...). But even frames arriving on netdev devices in a namespace can be delivered to an IUCV socket. So a process in an unprivileged user and network namespace holding only the CAP_NET_RAW capability valid within that namespace can send a raw ETH_P_AF_IUCV frame on its own lo device and have it matched against init_net sockets.
Fix all three by skipping any socket whose hs_dev does not match the ingress device. A classic z/VM IUCV socket has hs_dev == NULL; the ingress dev is never NULL, so classic sockets are skipped automatically. An unbound HIPER socket also has hs_dev == NULL and is skipped. A bound HIPER socket is only reachable from the exact IQD device it was bound to. Because hs_dev is always a device in init_net (iucv_sock_bind() scans for_each_netdev_rcu(&init_net, ...) exclusively), a frame whose ingress device belongs to another namespace never matches any socket.
Note that AF_IUCV over HiperSockets provides no per-connection
authentication: no sequence numbers, no TLS, no nonce. The four name fields identifying a connection are exchanged in plaintext on the shared HiperSockets segment (VCHID). Any host on the same HiperSockets segment could spoof any frame type against an existing connection. That is a protocol-level property unchanged by this patch. The fix reduces the attack surface to peers present on the same HiperSockets segment.
--- net/iucv/af_iucv.c | 2 ++ 1 file changed, 2 insertions(+)
net/iucv/af_iucv.c +2 -0
@@ -2080,6 +2080,8 @@ static int afiucv_hs_rcv(struct sk_buff sk =NULL ;read_lock (&iucv_sk_list.lock);sk_for_each (sk, &iucv_sk_list.head) { +if (iucv_sk (sk)->hs_dev != dev) +continue ;if (trans_hdr->flags ==AF_IUCV_FLAG_SYN ) { if ((!memcmp(&iucv_sk(sk)->src_name, trans_hdr->destAppName, 8)) &&
Greg Kroah-Hartman
maintainerPATCH 254/1424 xdp: fix zero-copy frame layout
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit 71283aaa6c65b3cec84caf1dc78560985737641f upstream.
xdp_convert_zc_to_xdp_frame() clones an XSK packet into an order-0 page and advertises PAGE_SIZE as its frame size. It allows the copied frame to occupy the page tail needed by skb_shared_info and records zero headroom even when metadata separates the frame header from packet data. An AF_XDP zero-copy packet redirected through cpumap can therefore make the skb overlap skb_shared_info or place it beyond the allocated page.
Limit the copied layout to SKB_WITH_OVERHEAD(PAGE_SIZE) and include the metadata length in frame headroom. Redirect callers already handle a NULL conversion result.
BUG: KASAN: slab-out-of-bounds in skb_gro_receive Write of size 4 at addr ffff88800cf37004 by task cpumap/1/map:1/146 Call Trace: skb_gro_receive (net/core/gro.c:174) udp_gro_receive (net/ipv4/udp_offload.c:812) inet_gro_receive (net/ipv4/af_inet.c:1539) dev_gro_receive (net/core/gro.c:515) gro_receive_skb (net/core/gro.c:633) cpu_map_kthread_run (kernel/bpf/cpumap.c:395) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:164) ret_from_fork_asm (arch/x86/entry/entry_64.S:255) Kernel panic - not syncing: KASAN: panic_on_warn set ...
--- net/core/xdp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/core/xdp.c +2 -2
@@ -549,7 +549,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_ xdp->data - xdp->data_meta; totsize = xdp->data_end - xdp->data + metasize; -if (sizeof (*xdpf) + totsize >PAGE_SIZE ) +if (sizeof (*xdpf) + totsize >SKB_WITH_OVERHEAD (PAGE_SIZE ))return NULL ; page =dev_alloc_page ();
@@ -566,7 +566,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_ xdpf->data = addr + metasize; xdpf->len = totsize - metasize; - xdpf->headroom =0 ; + xdpf->headroom = metasize; xdpf->metasize = metasize; xdpf->frame_sz =PAGE_SIZE ; xdpf->mem.type =MEM_TYPE_PAGE_ORDER0 ;
Greg Kroah-Hartman
maintainerPATCH 255/1424 slip: fix use-after-free in sl_sync()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksandr Khromov <[email protected]>
commit 2c4e7c42d77e78ad595dbb9e4b5886b58b45d89d upstream.
slip_devs[] stores bare net_device pointers and takes no reference on them. sl_sync() and sl_alloc() walk that table from slip_open() under rtnl_lock(), while an entry is dropped by sl_free_netdev(), which sl_setup() installs as dev->priv_destructor.
priv_destructor is called from netdev_run_todo(), which deliberately runs with the RTNL semaphore released so that it can sleep while waiting for the device refcount to drop:
/* Snapshot list, allow later requests */ list_replace_init(&net_todo_list, &list); __rtnl_unlock(); ... if (dev->priv_destructor) dev->priv_destructor(dev); /* slip_devs[i] = NULL */ if (dev->needs_free_netdev) free_netdev(dev); ... /* Free network device */ kobject_put(&dev->dev.kobj);
So rtnl_lock() does not serialise slip_open() against the teardown at all. sl_sync() can load slip_devs[i] while the entry is still published and dereference it after netdev_run_todo() has run the destructor and released the device:
CPU0 (slip_open) CPU1 (slip_close)
unregister_netdev()
rtnl_unlock()
netdev_run_todo()
__rtnl_unlock()
rtnl_lock()
sl_sync()
dev = slip_devs[i]
priv_destructor(dev)
slip_devs[i] = NULL
kobject_put(&dev->dev.kobj)
/* dev is freed */
sl = netdev_priv(dev)
if (sl->tty || sl->leased) /* use-after-free */BUG: KASAN: use-after-free in sl_sync drivers/net/slip/slip.c:730 [inline] BUG: KASAN: use-after-free in slip_open+0xef4/0x1210 drivers/net/slip/slip.c:806 Read of size 1 at addr ffff8880712dac71 by task syz-executor.2/6506 CPU: 2 PID: 6506 Comm: syz-executor.2 Not tainted 6.1.134-syzkaller-00260-g0c8fc3469765 #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 Call Trace: sl_sync drivers/net/slip/slip.c:730 [inline] slip_open+0xef4/0x1210 drivers/net/slip/slip.c:806 tty_ldisc_open+0xa2/0x120 drivers/tty/tty_ldisc.c:433 tty_set_ldisc+0x324/0x720 drivers/tty/tty_ldisc.c:564 tiocsetd drivers/tty/tty_io.c:2428 [inline] tty_ioctl+0x5f0/0x1530 drivers/tty/tty_io.c:2712 Allocated by task 6502: alloc_netdev_mqs+0x98/0xfe0 net/core/dev.c:10719 sl_alloc drivers/net/slip/slip.c:756 [inline] slip_open+0x36d/0x1210 drivers/net/slip/slip.c:817 tty_ldisc_open+0xa2/0x120 drivers/tty/tty_ldisc.c:433 tty_set_ldisc+0x324/0x720 drivers/tty/tty_ldisc.c:564 Freed by task 6497: device_release+0xa2/0x240 drivers/base/core.c:2507 kobject_put+0x179/0x280 lib/kobject.c:729 netdev_run_todo+0x6c8/0xef0 net/core/dev.c:10509 slip_close+0x166/0x1c0 drivers/net/slip/slip.c:906 tty_ldisc_close+0x113/0x1a0 drivers/tty/tty_ldisc.c:456 tty_ldisc_kill+0x94/0x160 drivers/tty/tty_ldisc.c:614 tty_ldisc_release+0xe3/0x2b0 drivers/tty/tty_ldisc.c:782 tty_release+0xbcc/0xe70 drivers/tty/tty_io.c:1860
Commit e58c19124189 ("slip: Fix use-after-free Read in slip_open") fixed a different source of stale entries - a device left in slip_devs[] after slip_open() freed it on the registration error path - and does not address this race, which is why the report survives it.
Drop the entry from ndo_uninit instead. unregister_netdevice() calls ndo_uninit under RTNL, before the device is queued to netdev_run_todo(), so an entry that sl_sync() can still see while holding RTNL belongs to a device that cannot be freed until RTNL is dropped. sl_free_netdev() stays only for the slip_open() error path, where register_netdevice() may have failed before ndo_init and ndo_uninit is then not called either. Both running for the same device is harmless: they run under the same RTNL section, so the slot cannot have been reused in between.
This also removes the second symptom of the missing exclusion: a destructor running after sl_alloc() had already handed the slot out to another channel used to clear a live entry, so sl_sync() stopped at that NULL, sl_alloc() returned the same index again, and register_netdevice() failed with -EEXIST because slN was still there.
Reproduced on x86_64 with several threads looping over open("/dev/ptmx") + ioctl(TIOCSETD, N_SLIP) + close().
Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
--- drivers/net/slip/slip.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/net/slip/slip.c +8 -3
@@ -628,9 +628,15 @@ static void sl_uninit(struct net_devicestruct slip *sl =netdev_priv (dev);sl_free_bufs (sl); +/* Drop the slip_devs[] entry here rather than from the destructor: +* ndo_uninit runs under RTNL, so it cannot race sl_sync(). +*/ + slip_devs[dev->base_addr] =NULL ; } -/* Hook the destructor so we can free slip devices at the right point in time */ +/* Only for the slip_open() error path: register_netdevice() can fail before +* ndo_init, and then ndo_uninit is not called either. +*/ static void sl_free_netdev (struct net_device *dev) {int i = dev->base_addr;
@@ -657,7 +663,6 @@ static void sl_setup(struct net_device * { dev->netdev_ops = &sl_netdev_ops; dev->needs_free_netdev = true; - dev->priv_destructor = sl_free_netdev; dev->hard_header_len =0 ; dev->addr_len =0 ;
@@ -908,7 +913,7 @@ static void slip_close(struct tty_struct#endif /* Flush network side */ unregister_netdev (sl->dev); -/* This will complete via sl_free_netdev */ +/* sl_uninit() has dropped the slip_devs[] entry by now */ }static void slip_hangup (struct tty_struct *tty)