From 1e9e3942c38b22de264971d5fa0e8d50167b6b98 Mon Sep 17 00:00:00 2001 From: almalinux-bot-kernel Date: Sun, 6 Sep 2026 04:15:47 +0000 Subject: [PATCH] Import of kernel-6.12.0-211.50.1.el10_2 --- ....49.1.el10 => COPYING-6.12.0-211.50.1.el10 | 0 Makefile.rhelver | 2 +- arch/s390/Kconfig | 1 - arch/s390/include/asm/asm-extable.h | 4 - arch/s390/include/asm/word-at-a-time.h | 22 - arch/s390/mm/extable.c | 18 - arch/s390/mm/pfault.c | 4 +- arch/x86/entry/entry_64.S | 8 +- arch/x86/include/asm/nospec-branch.h | 58 ++ arch/x86/kernel/cpu/bugs.c | 39 + arch/x86/lib/retpoline.S | 20 + configs/kernel-6.12.0-s390x-debug.config | 1 - configs/kernel-6.12.0-s390x-zfcpdump.config | 1 - configs/kernel-6.12.0-s390x.config | 1 - drivers/block/zram/zram_drv.c | 2 +- .../intel/qat/qat_common/adf_accel_devices.h | 2 + drivers/crypto/intel/qat/qat_common/adf_aer.c | 2 + .../intel/qat/qat_common/adf_common_drv.h | 2 + drivers/crypto/intel/qat/qat_common/adf_isr.c | 39 + .../crypto/intel/qat/qat_common/adf_sriov.c | 20 +- .../intel/qat/qat_common/qat_asym_algs.c | 10 +- drivers/input/rmi4/rmi_f30.c | 2 +- drivers/input/rmi4/rmi_f3a.c | 2 +- drivers/iommu/amd/iommu.c | 7 +- drivers/net/bonding/bond_main.c | 6 +- .../net/ethernet/microsoft/mana/hw_channel.c | 6 + drivers/net/ethernet/qlogic/qede/qede_fp.c | 5 + drivers/nvme/target/discovery.c | 23 +- drivers/nvme/target/fabrics-cmd-auth.c | 26 +- drivers/usb/serial/io_ti.c | 11 + fs/ceph/debugfs.c | 4 +- fs/ceph/dir.c | 2 +- fs/ceph/file.c | 4 +- fs/ceph/inode.c | 2 +- fs/ceph/mds_client.c | 12 +- fs/nfs/dir.c | 2 + fs/nfs/flexfilelayout/flexfilelayout.c | 4 + fs/nfs/pnfs.c | 2 +- fs/nfs/pnfs_nfs.c | 4 +- fs/nfsd/nfs2acl.c | 17 +- fs/nfsd/nfs3acl.c | 17 +- fs/nfsd/nfs4layouts.c | 4 +- fs/nfsd/nfs4state.c | 4 +- fs/smb/client/smb2pdu.c | 2 + fs/smb/client/smb2transport.c | 2 + include/linux/userfaultfd_k.h | 5 +- include/net/sctp/sctp.h | 3 +- kernel.sbat | 2 +- kernel/locking/spinlock_rt.c | 27 +- kernel/seccomp.c | 12 + mm/huge_memory.c | 2 + mm/khugepaged.c | 41 +- mm/list_lru.c | 21 +- mm/shrinker.c | 13 +- mm/shrinker_debug.c | 10 +- net/ipv6/ip6_gre.c | 5 +- net/ipv6/ip6_tunnel.c | 2 +- net/netfilter/ipset/ip_set_core.c | 8 +- net/netfilter/nf_tables_api.c | 2 +- net/netfilter/nfnetlink_cthelper.c | 8 +- net/netfilter/nft_chain_filter.c | 2 +- net/sctp/diag.c | 67 +- net/sctp/socket.c | 29 +- redhat/Makefile | 1 + redhat/kernel.changelog-10.2 | 46 + redhat/kernel.spec.template | 83 ++ redhat/scripts/kmap.py | 874 ++++++++++++++++++ sound/virtio/virtio_kctl.c | 50 + uki-addons.sbat | 2 +- uki.sbat | 2 +- 70 files changed, 1556 insertions(+), 187 deletions(-) rename COPYING-6.12.0-211.49.1.el10 => COPYING-6.12.0-211.50.1.el10 (100%) create mode 100755 redhat/scripts/kmap.py diff --git a/COPYING-6.12.0-211.49.1.el10 b/COPYING-6.12.0-211.50.1.el10 similarity index 100% rename from COPYING-6.12.0-211.49.1.el10 rename to COPYING-6.12.0-211.50.1.el10 diff --git a/Makefile.rhelver b/Makefile.rhelver index 0353508f70..53ba46ee80 100644 --- a/Makefile.rhelver +++ b/Makefile.rhelver @@ -12,7 +12,7 @@ RHEL_MINOR = 2 # # Use this spot to avoid future merge conflicts. # Do not trim this comment. -RHEL_RELEASE = 211.49.1 +RHEL_RELEASE = 211.50.1 # # RHEL_REBASE_NUM diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 098779c7a0..44bfff6c5e 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -140,7 +140,6 @@ config S390 select ARCH_WANT_LD_ORPHAN_WARN select BUILDTIME_TABLE_SORT select CLONE_BACKWARDS2 - select DCACHE_WORD_ACCESS if !KMSAN select DYNAMIC_FTRACE if FUNCTION_TRACER select FUNCTION_ALIGNMENT_8B if CC_IS_GCC select FUNCTION_ALIGNMENT_16B if !CC_IS_GCC diff --git a/arch/s390/include/asm/asm-extable.h b/arch/s390/include/asm/asm-extable.h index 4a6b0a8b64..d1ecc2b6e5 100644 --- a/arch/s390/include/asm/asm-extable.h +++ b/arch/s390/include/asm/asm-extable.h @@ -13,7 +13,6 @@ #define EX_TYPE_UA_LOAD_MEM 4 #define EX_TYPE_UA_LOAD_REG 5 #define EX_TYPE_UA_LOAD_REGPAIR 6 -#define EX_TYPE_ZEROPAD 7 #define EX_DATA_REG_ERR_SHIFT 0 #define EX_DATA_REG_ERR GENMASK(3, 0) @@ -81,7 +80,4 @@ #define EX_TABLE_UA_LOAD_REGPAIR(_fault, _target, _regerr, _regzero) \ __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_REGPAIR, _regerr, _regzero, 0) -#define EX_TABLE_ZEROPAD(_fault, _target, _regdata, _regaddr) \ - __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_ZEROPAD, _regdata, _regaddr, 0) - #endif /* __ASM_EXTABLE_H */ diff --git a/arch/s390/include/asm/word-at-a-time.h b/arch/s390/include/asm/word-at-a-time.h index 203acd6e43..e928703639 100644 --- a/arch/s390/include/asm/word-at-a-time.h +++ b/arch/s390/include/asm/word-at-a-time.h @@ -4,7 +4,6 @@ #include #include -#include #include struct word_at_a_time { @@ -41,25 +40,4 @@ static inline unsigned long zero_bytemask(unsigned long data) return ~1UL << data; } -/* - * Load an unaligned word from kernel space. - * - * In the (very unlikely) case of the word being a page-crosser - * and the next page not being mapped, take the exception and - * return zeroes in the non-existing part. - */ -static inline unsigned long load_unaligned_zeropad(const void *addr) -{ - unsigned long data; - - asm volatile( - "0: lg %[data],0(%[addr])\n" - "1: nopr %%r7\n" - EX_TABLE_ZEROPAD(0b, 1b, %[data], %[addr]) - EX_TABLE_ZEROPAD(1b, 1b, %[data], %[addr]) - : [data] "=d" (data) - : [addr] "a" (addr), "m" (*(unsigned long *)addr)); - return data; -} - #endif /* _ASM_WORD_AT_A_TIME_H */ diff --git a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c index 0a0738a473..fe87291df9 100644 --- a/arch/s390/mm/extable.c +++ b/arch/s390/mm/extable.c @@ -61,22 +61,6 @@ static bool ex_handler_ua_load_reg(const struct exception_table_entry *ex, return true; } -static bool ex_handler_zeropad(const struct exception_table_entry *ex, struct pt_regs *regs) -{ - unsigned int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data); - unsigned int reg_data = FIELD_GET(EX_DATA_REG_ERR, ex->data); - unsigned long data, addr, offset; - - addr = regs->gprs[reg_addr]; - offset = addr & (sizeof(unsigned long) - 1); - addr &= ~(sizeof(unsigned long) - 1); - data = *(unsigned long *)addr; - data <<= BITS_PER_BYTE * offset; - regs->gprs[reg_data] = data; - regs->psw.addr = extable_fixup(ex); - return true; -} - bool fixup_exception(struct pt_regs *regs) { const struct exception_table_entry *ex; @@ -97,8 +81,6 @@ bool fixup_exception(struct pt_regs *regs) return ex_handler_ua_load_reg(ex, false, regs); case EX_TYPE_UA_LOAD_REGPAIR: return ex_handler_ua_load_reg(ex, true, regs); - case EX_TYPE_ZEROPAD: - return ex_handler_zeropad(ex, regs); } panic("invalid exception table entry"); } diff --git a/arch/s390/mm/pfault.c b/arch/s390/mm/pfault.c index e5bc754b9c..1a293be83a 100644 --- a/arch/s390/mm/pfault.c +++ b/arch/s390/mm/pfault.c @@ -62,7 +62,7 @@ int __pfault_init(void) "0: nopr %%r7\n" EX_TABLE(0b, 0b) : [rc] "+d" (rc) - : [refbk] "a" (&pfault_init_refbk), "m" (pfault_init_refbk) + : [refbk] "a" (virt_to_phys(&pfault_init_refbk)), "m" (pfault_init_refbk) : "cc"); return rc; } @@ -84,7 +84,7 @@ void __pfault_fini(void) "0: nopr %%r7\n" EX_TABLE(0b, 0b) : - : [refbk] "a" (&pfault_fini_refbk), "m" (pfault_fini_refbk) + : [refbk] "a" (virt_to_phys(&pfault_fini_refbk)), "m" (pfault_fini_refbk) : "cc"); } diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index b4cd6ddde9..a14e425554 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S @@ -935,6 +935,8 @@ SYM_CODE_START(paranoid_entry) IBRS_ENTER save_reg=%r15 UNTRAIN_RET_FROM_CALL + HANDLE_INTR_SAFERET 8(%rsp) + RET SYM_CODE_END(paranoid_entry) @@ -1037,6 +1039,11 @@ SYM_CODE_START(error_entry) movl %ecx, %eax /* zero extend */ cmpq %rax, RIP+8(%rsp) je .Lbstep_iret + + VALIDATE_UNRET_END + + HANDLE_INTR_SAFERET 8(%rsp) + cmpq $.Lgs_change, RIP+8(%rsp) jne .Lerror_entry_done_lfence @@ -1055,7 +1062,6 @@ SYM_CODE_START(error_entry) FENCE_SWAPGS_KERNEL_ENTRY CALL_DEPTH_ACCOUNT leaq 8(%rsp), %rax /* return pt_regs pointer */ - VALIDATE_UNRET_END RET .Lbstep_iret: diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index deb105c07d..8c4ae1ef5c 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -12,6 +12,7 @@ #include #include #include +#include #include /* @@ -177,6 +178,50 @@ add $(BITS_PER_LONG/8), %_ASM_SP; \ lfence; +/* + * Helper for detecting if an interrupt occurred at an unsafe location within + * Safe-RET. If Safe-RET is interrupted after the CALL or LEA the RSB may get + * poisoned by the interrupt handler. + * + * The Safe-RET sequence is: + * + * CALL + * LEA 8(%RSP), %RSP + * RET + * + * The two CMPs below check whether RIP points to after the CALL or after the + * LEA. + * + * The LFENCE below is to address this particular speculation case: + * + * 1. Userspace runs and poisons the BTB around the safe-RET routine + * + * 2. Userspace triggers some kind of exception + * + * 3. Kernel executes error_entry() and mis-speculates the branch into thinking + * it actually came from kernel space + * + * 4. The kernel then further mis-speculates that the exception occurred due + * to an interrupted safe-RET + * + * 5. The handle_interrupted_saferet() routine speculatively executes and + * speculatively does a safe-RET. But this is unsafe since it was never + * untrained. + * + * The LFENCE fixes this by ensuring step 5 is never reached speculatively. + * Note that this LFENCE only occurs if safe-RET was actually interrupted (so + * it's outside of the normal path). + */ +#define __HANDLE_INTR_SAFERET(name, pt_regs) \ + cmpq $(name), RIP+pt_regs; \ + jb 1f; \ + cmpq $(name)+5, RIP+pt_regs; \ + ja 1f; \ + lfence; \ + leaq pt_regs, %rdi; \ + call handle_interrupted_saferet; \ + 1: + #ifdef __ASSEMBLY__ /* @@ -295,6 +340,14 @@ #define UNTRAIN_RET_FROM_CALL \ __UNTRAIN_RET X86_FEATURE_ENTRY_IBPB, __stringify(RESET_CALL_DEPTH_FROM_CALL) +.macro HANDLE_INTR_SAFERET pt_regs +#ifdef CONFIG_MITIGATION_SRSO + ALTERNATIVE_2 "", \ + __stringify(__HANDLE_INTR_SAFERET(srso_safe_ret, \pt_regs)), X86_FEATURE_SRSO, \ + __stringify(__HANDLE_INTR_SAFERET(srso_alias_safe_ret, \pt_regs)), X86_FEATURE_SRSO_ALIAS + +#endif +.endm .macro CALL_DEPTH_ACCOUNT #ifdef CONFIG_MITIGATION_CALL_DEPTH_TRACKING @@ -618,6 +671,11 @@ static __always_inline void x86_idle_clear_cpu_buffers(void) x86_clear_cpu_buffers(); } +struct pt_regs; +void srso_safe_ret(void); +void srso_alias_safe_ret(void); +void handle_interrupted_saferet(struct pt_regs *regs); + #endif /* __ASSEMBLY__ */ #endif /* _ASM_X86_NOSPEC_BRANCH_H_ */ diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index b8daba654d..c722fcc1cd 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -3794,3 +3794,42 @@ void __warn_thunk(void) { WARN_ONCE(1, "Unpatched return thunk in use. This should not happen!\n"); } + +#ifdef CONFIG_MITIGATION_SRSO +/* + * Called during exception/interrupt entry if interrupted during the + * safe-RET sequence. The safe-RET sequence consists of 3 instructions: + * + * CALL + * LEA 8(%RSP), %RSP + * RET + * + * An interrupt after the CALL or after the LEA could potentially lead + * to branch predictor poisoning and results in the sequence not being + * able to be safely resumed. + * + * Therefore, modify the regs state as if the remaining part of the + * safe-RET sequence executed so the interrupt returns back to the + * desired return target, instead of the to the safe-RET sequence. + */ +void noinstr handle_interrupted_saferet(struct pt_regs *regs) +{ + unsigned long rip = regs->ip; + + if (rip == (unsigned long) srso_safe_ret || + rip == (unsigned long) srso_alias_safe_ret) { + /* Modify stack pointer as if LEA executed: */ + regs->sp += 8; + } + + /* + * Adjust registers as if RET executed: + * + * 1. Read the return address off the stack and into rIP: + */ + regs->ip = *(unsigned long *)(regs->sp); + + /* 2. Pop rIP off the stack: */ + regs->sp += 8; +} +#endif /* CONFIG_MITIGATION_SRSO */ diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S index 614fb9aee2..bc66ce29cc 100644 --- a/arch/x86/lib/retpoline.S +++ b/arch/x86/lib/retpoline.S @@ -168,10 +168,24 @@ __EXPORT_THUNK(srso_alias_untrain_ret) .pushsection .text..__x86.rethunk_safe SYM_CODE_START_NOALIGN(srso_alias_safe_ret) + + /* + * Tell objtool that those are not function pointers referenced by + * __HANDLE_INTR_SAFERET(). Below too. + */ + ANNOTATE_NOENDBR + + /* + * Safe-RET sequence. If you need to change it, adjust + * handle_interrupted_saferet() too. + */ lea 8(%_ASM_SP), %_ASM_SP UNWIND_HINT_FUNC + + ANNOTATE_NOENDBR ANNOTATE_UNRET_SAFE ret + /* End of Safe-RET sequence */ int3 SYM_FUNC_END(srso_alias_safe_ret) @@ -206,8 +220,14 @@ SYM_CODE_START_LOCAL_NOALIGN(srso_untrain_ret) * the stack. */ SYM_INNER_LABEL(srso_safe_ret, SYM_L_GLOBAL) + /* + * Safe-RET sequence. If you need to change it, adjust + * handle_interrupted_saferet() too. + */ lea 8(%_ASM_SP), %_ASM_SP ret + /* End of Safe-RET sequence */ + int3 int3 /* end of movabs */ diff --git a/configs/kernel-6.12.0-s390x-debug.config b/configs/kernel-6.12.0-s390x-debug.config index 71121f3eef..10ef373fb4 100644 --- a/configs/kernel-6.12.0-s390x-debug.config +++ b/configs/kernel-6.12.0-s390x-debug.config @@ -3180,7 +3180,6 @@ CONFIG_INTERCONNECT=y # # File systems # -CONFIG_DCACHE_WORD_ACCESS=y # CONFIG_VALIDATE_FS_PARSER is not set CONFIG_FS_IOMAP=y CONFIG_FS_STACK=y diff --git a/configs/kernel-6.12.0-s390x-zfcpdump.config b/configs/kernel-6.12.0-s390x-zfcpdump.config index 17dd5224f7..ec5e261548 100644 --- a/configs/kernel-6.12.0-s390x-zfcpdump.config +++ b/configs/kernel-6.12.0-s390x-zfcpdump.config @@ -1326,7 +1326,6 @@ CONFIG_INTERCONNECT=y # # File systems # -CONFIG_DCACHE_WORD_ACCESS=y # CONFIG_VALIDATE_FS_PARSER is not set CONFIG_FS_IOMAP=y # CONFIG_EXT2_FS is not set diff --git a/configs/kernel-6.12.0-s390x.config b/configs/kernel-6.12.0-s390x.config index 2102bf3821..c6dd659c07 100644 --- a/configs/kernel-6.12.0-s390x.config +++ b/configs/kernel-6.12.0-s390x.config @@ -3204,7 +3204,6 @@ CONFIG_INTERCONNECT=y # # File systems # -CONFIG_DCACHE_WORD_ACCESS=y # CONFIG_VALIDATE_FS_PARSER is not set CONFIG_FS_IOMAP=y CONFIG_FS_STACK=y diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index f2b9ff4094..22928ee038 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -1635,7 +1635,7 @@ static int zram_bvec_write_partial(struct zram *zram, struct bio_vec *bvec, if (!page) return -ENOMEM; - ret = zram_read_page(zram, page, index, bio); + ret = zram_read_page(zram, page, index, NULL); if (!ret) { memcpy_from_bvec(page_address(page) + offset, bvec); ret = zram_write_page(zram, page, index); diff --git a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h index 9fe3239f01..41f6db98cd 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h +++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h @@ -472,6 +472,8 @@ struct adf_accel_dev { struct { /* protects VF2PF interrupts access */ spinlock_t vf2pf_ints_lock; + /* prevents VF2PF handling from racing with VF state teardown */ + bool vf2pf_disabled; /* vf_info is non-zero when SR-IOV is init'ed */ struct adf_accel_vf_info *vf_info; } pf; diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c index c75a998478..f0a2d1380e 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_aer.c +++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c @@ -140,6 +140,8 @@ static void adf_device_reset_worker(struct work_struct *work) queue_work(device_sriov_wq, &sriov_data.sriov_work); if (wait_for_completion_timeout(&sriov_data.compl, wait_jiffies)) adf_pf2vf_notify_restarted(accel_dev); + else + cancel_work_sync(&sriov_data.sriov_work); adf_dev_restarted_notify(accel_dev); clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status); diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h index 6cf3a95489..4e36d9e0d9 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h @@ -121,6 +121,7 @@ void qat_comp_alg_callback(void *resp); int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev); void adf_isr_resource_free(struct adf_accel_dev *accel_dev); +void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev); int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev); void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev); @@ -194,6 +195,7 @@ int adf_sriov_configure(struct pci_dev *pdev, int numvfs); void adf_disable_sriov(struct adf_accel_dev *accel_dev); void adf_reenable_sriov(struct adf_accel_dev *accel_dev); void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask); +void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs); void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev); bool adf_recv_and_handle_pf2vf_msg(struct adf_accel_dev *accel_dev); bool adf_recv_and_handle_vf2pf_msg(struct adf_accel_dev *accel_dev, u32 vf_nr); diff --git a/drivers/crypto/intel/qat/qat_common/adf_isr.c b/drivers/crypto/intel/qat/qat_common/adf_isr.c index 4639d7fd93..159e91a501 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_isr.c +++ b/drivers/crypto/intel/qat/qat_common/adf_isr.c @@ -62,6 +62,23 @@ void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask) unsigned long flags; spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + if (!READ_ONCE(accel_dev->pf.vf2pf_disabled)) + GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask); + spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); +} + +void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs) +{ + void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev); + unsigned long flags; + u32 vf_mask; + + vf_mask = BIT_ULL(num_vfs) - 1; + if (!vf_mask) + return; + + spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + WRITE_ONCE(accel_dev->pf.vf2pf_disabled, false); GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask); spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); } @@ -72,6 +89,7 @@ void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev) unsigned long flags; spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); + WRITE_ONCE(accel_dev->pf.vf2pf_disabled, true); GET_PFVF_OPS(accel_dev)->disable_all_vf2pf_interrupts(pmisc_addr); spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); } @@ -174,6 +192,27 @@ static irqreturn_t adf_msix_isr_ae(int irq, void *dev_ptr) return IRQ_NONE; } +void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev) +{ + struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; + struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); + u32 num_entries = pci_dev_info->msix_entries.num_entries; + struct adf_irq *irqs = pci_dev_info->msix_entries.irqs; + u32 irq_idx; + int irq; + + if (!test_bit(ADF_STATUS_IRQ_ALLOCATED, &accel_dev->status) || !irqs) + return; + + irq_idx = num_entries > 1 ? hw_data->num_banks : 0; + if (irq_idx >= num_entries || !irqs[irq_idx].enabled) + return; + + irq = pci_irq_vector(pci_dev_info->pci_dev, hw_data->num_banks); + if (irq > 0) + synchronize_irq(irq); +} + static void adf_free_irqs(struct adf_accel_dev *accel_dev) { struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; diff --git a/drivers/crypto/intel/qat/qat_common/adf_sriov.c b/drivers/crypto/intel/qat/qat_common/adf_sriov.c index bb904ba4bf..740a2325e7 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_sriov.c +++ b/drivers/crypto/intel/qat/qat_common/adf_sriov.c @@ -26,6 +26,9 @@ static void adf_iov_send_resp(struct work_struct *work) u32 vf_nr = vf_info->vf_nr; bool ret; + if (READ_ONCE(accel_dev->pf.vf2pf_disabled)) + goto out; + mutex_lock(&vf_info->pfvf_mig_lock); ret = adf_recv_and_handle_vf2pf_msg(accel_dev, vf_nr); if (ret) @@ -33,13 +36,18 @@ static void adf_iov_send_resp(struct work_struct *work) adf_enable_vf2pf_interrupts(accel_dev, 1 << vf_nr); mutex_unlock(&vf_info->pfvf_mig_lock); +out: kfree(pf2vf_resp); } void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info) { + struct adf_accel_dev *accel_dev = vf_info->accel_dev; struct adf_pf2vf_resp *pf2vf_resp; + if (READ_ONCE(accel_dev->pf.vf2pf_disabled)) + return; + pf2vf_resp = kzalloc(sizeof(*pf2vf_resp), GFP_ATOMIC); if (!pf2vf_resp) return; @@ -49,6 +57,12 @@ void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info) queue_work(pf2vf_resp_wq, &pf2vf_resp->pf2vf_resp_work); } +static void adf_flush_pf2vf_resp_wq(void) +{ + if (pf2vf_resp_wq) + flush_workqueue(pf2vf_resp_wq); +} + static int adf_enable_sriov(struct adf_accel_dev *accel_dev) { struct pci_dev *pdev = accel_to_pci_dev(accel_dev); @@ -75,7 +89,7 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev) hw_data->configure_iov_threads(accel_dev, true); /* Enable VF to PF interrupts for all VFs */ - adf_enable_vf2pf_interrupts(accel_dev, BIT_ULL(totalvfs) - 1); + adf_enable_all_vf2pf_interrupts(accel_dev, totalvfs); /* * Due to the hardware design, when SR-IOV and the ring arbiter @@ -249,8 +263,10 @@ void adf_disable_sriov(struct adf_accel_dev *accel_dev) adf_pf2vf_wait_for_restarting_complete(accel_dev); pci_disable_sriov(accel_to_pci_dev(accel_dev)); - /* Disable VF to PF interrupts */ + /* Block VF2PF work and disable VF to PF interrupts */ adf_disable_all_vf2pf_interrupts(accel_dev); + adf_isr_sync_ae_cluster(accel_dev); + adf_flush_pf2vf_resp_wq(); /* Clear Valid bits in AE Thread to PCIe Function Mapping */ if (hw_data->configure_iov_threads) diff --git a/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c b/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c index e09b9edfce..75c15c8e41 100644 --- a/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c +++ b/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c @@ -1085,7 +1085,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->p; len = rsa_key->p_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto err; ctx->p = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_p, GFP_KERNEL); if (!ctx->p) @@ -1096,7 +1096,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->q; len = rsa_key->q_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_p; ctx->q = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_q, GFP_KERNEL); if (!ctx->q) @@ -1107,7 +1107,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->dp; len = rsa_key->dp_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_q; ctx->dp = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dp, GFP_KERNEL); @@ -1119,7 +1119,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->dq; len = rsa_key->dq_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_dp; ctx->dq = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dq, GFP_KERNEL); @@ -1131,7 +1131,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key) ptr = rsa_key->qinv; len = rsa_key->qinv_sz; qat_rsa_drop_leading_zeros(&ptr, &len); - if (!len) + if (!len || len > half_key_sz) goto free_dq; ctx->qinv = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_qinv, GFP_KERNEL); diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index 35045f161d..b2155c8e20 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -233,7 +233,7 @@ static int rmi_f30_map_gpios(struct rmi_function *fn, int button_count = min_t(u8, f30->gpioled_count, TRACKSTICK_RANGE_END); f30->gpioled_key_map = devm_kcalloc(&fn->dev, - button_count, + f30->gpioled_count, sizeof(f30->gpioled_key_map[0]), GFP_KERNEL); if (!f30->gpioled_key_map) { diff --git a/drivers/input/rmi4/rmi_f3a.c b/drivers/input/rmi4/rmi_f3a.c index 0e8baed84d..a0777644ee 100644 --- a/drivers/input/rmi4/rmi_f3a.c +++ b/drivers/input/rmi4/rmi_f3a.c @@ -132,7 +132,7 @@ static int rmi_f3a_map_gpios(struct rmi_function *fn, struct f3a_data *f3a, int button_count = min_t(u8, f3a->gpio_count, TRACKSTICK_RANGE_END); f3a->gpio_key_map = devm_kcalloc(&fn->dev, - button_count, + f3a->gpio_count, sizeof(f3a->gpio_key_map[0]), GFP_KERNEL); if (!f3a->gpio_key_map) { diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index adb9514d18..7c502c7d51 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -380,11 +380,12 @@ struct iommu_dev_data *search_dev_data(struct amd_iommu *iommu, u16 devid) return NULL; } -static int clone_alias(struct pci_dev *pdev, u16 alias, void *data) +static int clone_alias(struct pci_dev *pdev_origin, u16 alias, void *data) { struct dev_table_entry new; struct amd_iommu *iommu; struct iommu_dev_data *dev_data, *alias_data; + struct pci_dev *pdev = data; u16 devid = pci_dev_id(pdev); int ret = 0; @@ -431,9 +432,9 @@ static void clone_aliases(struct amd_iommu *iommu, struct device *dev) * part of the PCI DMA aliases if it's bus differs * from the original device. */ - clone_alias(pdev, iommu->pci_seg->alias_table[pci_dev_id(pdev)], NULL); + clone_alias(pdev, iommu->pci_seg->alias_table[pci_dev_id(pdev)], pdev); - pci_for_each_dma_alias(pdev, clone_alias, NULL); + pci_for_each_dma_alias(pdev, clone_alias, pdev); } static void setup_aliases(struct amd_iommu *iommu, struct device *dev) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 67fd0c4cfb..7b73c6c099 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4481,9 +4481,13 @@ static int bond_close(struct net_device *bond_dev) bond_work_cancel_all(bond); bond->send_peer_notif = 0; + WRITE_ONCE(bond->recv_probe, NULL); + + /* Wait for any in-flight RX handlers */ + synchronize_net(); + if (bond_is_lb(bond)) bond_alb_deinitialize(bond); - bond->recv_probe = NULL; if (BOND_MODE(bond) == BOND_MODE_8023AD && bond->params.broadcast_neighbor) diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c index aa4e2731e2..4456b882f7 100644 --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c @@ -266,6 +266,12 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, rq_base_addr = hwc_rxq->msg_buf->mem_info.dma_handle; rx_req_idx = (sge->address - rq_base_addr) / hwc->max_req_msg_size; + if (rx_req_idx >= hwc_rxq->msg_buf->num_reqs) { + dev_err(hwc->dev, "HWC RX: wrong rx_req_idx=%llu, num_reqs=%u\n", + rx_req_idx, hwc_rxq->msg_buf->num_reqs); + return; + } + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; resp = (struct gdma_resp_hdr *)rx_req->buf_va; diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index e338bfc8b7..09482e5f15 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -765,6 +765,9 @@ qede_tpa_rx_build_skb(struct qede_dev *edev, struct sk_buff *skb; skb = qede_build_skb(rxq, bd, len, pad); + if (unlikely(!skb)) + return NULL; + bd->page_offset += rxq->rx_buf_seg_size; if (bd->page_offset == PAGE_SIZE) { @@ -812,6 +815,8 @@ qede_rx_build_skb(struct qede_dev *edev, } skb = qede_build_skb(rxq, bd, len, pad); + if (unlikely(!skb)) + return NULL; if (unlikely(qede_realloc_rx_buffer(rxq, bd))) { /* Incr page ref count to reuse on allocation failure so diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c index c06f3e0429..1d22291bd8 100644 --- a/drivers/nvme/target/discovery.c +++ b/drivers/nvme/target/discovery.c @@ -166,6 +166,7 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req) u64 offset = nvmet_get_log_page_offset(req->cmd); size_t data_len = nvmet_get_log_page_len(req->cmd); size_t alloc_len; + size_t copy_len; struct nvmet_subsys_link *p; struct nvmet_port *r; u32 numrec = 0; @@ -242,7 +243,27 @@ static void nvmet_execute_disc_get_log_page(struct nvmet_req *req) up_read(&nvmet_config_sem); - status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len); + /* + * Validate the host-supplied log page offset before copying out. + * Without this check, the host controls a 64-bit byte offset into + * a small kzalloc'd buffer: a value past the log page lets the + * subsequent memcpy read adjacent kernel heap, and a value aimed + * at unmapped kernel memory faults the in-kernel copy and crashes + * the target host. The Discovery controller is unauthenticated, + * so the bug is reachable from any reachable fabric peer. + */ + if (offset > alloc_len) { + req->error_loc = + offsetof(struct nvme_get_log_page_command, lpo); + status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR; + goto out_free_buffer; + } + + copy_len = min_t(size_t, data_len, alloc_len - offset); + status = nvmet_copy_to_sgl(req, 0, buffer + offset, copy_len); + if (!status && copy_len < data_len) + status = nvmet_zero_sgl(req, copy_len, data_len - copy_len); +out_free_buffer: kfree(buffer); out: nvmet_req_complete(req, status); diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index 5946681cb0..76552dc279 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -485,7 +485,31 @@ static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al) u32 nvmet_auth_receive_data_len(struct nvmet_req *req) { - return le32_to_cpu(req->cmd->auth_receive.al); + struct nvmet_ctrl *ctrl = req->sq->ctrl; + u32 al = le32_to_cpu(req->cmd->auth_receive.al); + u32 min_len; + + /* + * Reject too-short al before kmalloc(al), since the SUCCESS1 and + * FAILURE1/default builders write fixed response headers into it. + */ + switch (req->sq->dhchap_step) { + case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE: + return al; + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1: + min_len = sizeof(struct nvmf_auth_dhchap_success1_data); + if (req->sq->dhchap_c2) + min_len += nvme_auth_hmac_hash_len(ctrl->shash_id); + break; + default: + min_len = sizeof(struct nvmf_auth_dhchap_failure_data); + break; + } + + if (al < min_len) + return 0; + + return al; } void nvmet_execute_auth_receive(struct nvmet_req *req) diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 7d0584b2a2..bae3c72f77 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -773,6 +773,12 @@ static int get_manuf_info(struct edgeport_serial *serial, u8 *buffer) } /* Read the descriptor data */ + if (le16_to_cpu(rom_desc->Size) != sizeof(struct edge_ti_manuf_descriptor)) { + dev_err(dev, "unexpected Edge descriptor length: %u\n", + le16_to_cpu(rom_desc->Size)); + status = -EINVAL; + goto exit; + } status = read_rom(serial, start_address+sizeof(struct ti_i2c_desc), le16_to_cpu(rom_desc->Size), buffer); if (status) @@ -838,6 +844,11 @@ static int build_i2c_fw_hdr(u8 *header, const struct firmware *fw) /* Pointer to fw_down memory image */ img_header = (struct ti_i2c_image_header *)&fw->data[4]; + if (le16_to_cpu(img_header->Length) > + buffer_size - sizeof(struct ti_i2c_firmware_rec)) { + kfree(buffer); + return -EINVAL; + } memcpy(buffer + sizeof(struct ti_i2c_firmware_rec), &fw->data[4 + sizeof(struct ti_i2c_image_header)], le16_to_cpu(img_header->Length)); diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 74297fd2a3..c79a11ce7a 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -79,7 +79,7 @@ static int mdsc_show(struct seq_file *s, void *p) if (req->r_inode) { seq_printf(s, " #%llx", ceph_ino(req->r_inode)); } else if (req->r_dentry) { - struct ceph_path_info path_info; + struct ceph_path_info path_info = {0}; path = ceph_mdsc_build_path(mdsc, req->r_dentry, &path_info, 0); if (IS_ERR(path)) path = NULL; @@ -98,7 +98,7 @@ static int mdsc_show(struct seq_file *s, void *p) } if (req->r_old_dentry) { - struct ceph_path_info path_info; + struct ceph_path_info path_info = {0}; path = ceph_mdsc_build_path(mdsc, req->r_old_dentry, &path_info, 0); if (IS_ERR(path)) path = NULL; diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 16c25c4846..f4817668bb 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -1355,7 +1355,7 @@ static int ceph_unlink(struct inode *dir, struct dentry *dentry) if (!dn) { try_async = false; } else { - struct ceph_path_info path_info; + struct ceph_path_info path_info = {0}; path = ceph_mdsc_build_path(mdsc, dn, &path_info, 0); if (IS_ERR(path)) { try_async = false; diff --git a/fs/ceph/file.c b/fs/ceph/file.c index ab3f3a80dd..fc274fec4a 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -397,7 +397,7 @@ int ceph_open(struct inode *inode, struct file *file) if (!dentry) { do_sync = true; } else { - struct ceph_path_info path_info; + struct ceph_path_info path_info = {0}; path = ceph_mdsc_build_path(mdsc, dentry, &path_info, 0); if (IS_ERR(path)) { do_sync = true; @@ -809,7 +809,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, if (!dn) { try_async = false; } else { - struct ceph_path_info path_info; + struct ceph_path_info path_info = {0}; path = ceph_mdsc_build_path(mdsc, dn, &path_info, 0); if (IS_ERR(path)) { try_async = false; diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index f6e7f680dc..01951068c1 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -2548,7 +2548,7 @@ int __ceph_setattr(struct mnt_idmap *idmap, struct inode *inode, if (!dentry) { do_sync = true; } else { - struct ceph_path_info path_info; + struct ceph_path_info path_info = {0}; path = ceph_mdsc_build_path(mdsc, dentry, &path_info, 0); if (IS_ERR(path)) { do_sync = true; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index c2e3d4fbec..ca8e1e7ba0 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2766,6 +2766,7 @@ retry: if (ret < 0) { dput(parent); dput(cur); + __putname(path); return ERR_PTR(ret); } @@ -2775,6 +2776,7 @@ retry: if (len < 0) { dput(parent); dput(cur); + __putname(path); return ERR_PTR(len); } } @@ -2807,12 +2809,12 @@ retry: if (pos < 0) { /* - * A rename didn't occur, but somehow we didn't end up where - * we thought we would. Throw a warning and try again. + * The path is longer than PATH_MAX and this function + * cannot ever succeed. Creating paths that long is + * possible with Ceph, but Linux cannot use them. */ - pr_warn_client(cl, "did not end path lookup where expected (pos = %d)\n", - pos); - goto retry; + __putname(path); + return ERR_PTR(-ENAMETOOLONG); } /* Initialize the output structure */ diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 079ddd3c6c..0cccc99b6b 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -3319,6 +3319,8 @@ static int nfs_open_permission_mask(int openflags) mask |= MAY_READ; if ((openflags & O_ACCMODE) != O_RDONLY) mask |= MAY_WRITE; + if (openflags & O_TRUNC) + mask |= MAY_WRITE; } return mask; diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 9056f05a67..bb3f6c1863 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -552,6 +552,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, if (!p) goto out_err_free; fh_count = be32_to_cpup(p); + if (fh_count == 0) { + rc = -EINVAL; + goto out_err_free; + } dss_info->fh_versions = kcalloc(fh_count, sizeof(struct nfs_fh), diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index b3cb5ee9d8..d3c1f49f39 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -2217,11 +2217,11 @@ lookup_again: dprintk("%s wait for layoutreturn\n", __func__); lseg = ERR_PTR(pnfs_prepare_to_retry_layoutget(lo)); if (!IS_ERR(lseg)) { - pnfs_put_layout_hdr(lo); dprintk("%s retrying\n", __func__); trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg, PNFS_UPDATE_LAYOUT_RETRY); + pnfs_put_layout_hdr(lo); goto lookup_again; } trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg, diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c index 9976cc16b6..37cf4020f3 100644 --- a/fs/nfs/pnfs_nfs.c +++ b/fs/nfs/pnfs_nfs.c @@ -1075,14 +1075,14 @@ nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags) /* r_netid */ nlen = xdr_stream_decode_string_dup(xdr, &netid, XDR_MAX_NETOBJ, gfp_flags); - if (unlikely(nlen < 0)) + if (unlikely(nlen <= 0)) goto out_err; /* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */ /* port is ".ABC.DEF", 8 chars max */ rlen = xdr_stream_decode_string_dup(xdr, &buf, INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN + 8, gfp_flags); - if (unlikely(rlen < 0)) + if (unlikely(rlen <= 0)) goto out_free_netid; /* replace port '.' with '-' */ diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index 5fb202acb0..d46ec63e5b 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -131,10 +131,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp) resp->status = fh_getattr(fh, &resp->stat); out: - /* argp->acl_{access,default} may have been allocated in - nfssvc_decode_setaclargs. */ - posix_acl_release(argp->acl_access); - posix_acl_release(argp->acl_default); + /* argp->acl_{access,default} are released in nfsaclsvc_release_setacl. */ return rpc_success; out_drop_lock: @@ -310,6 +307,16 @@ static void nfsaclsvc_release_access(struct svc_rqst *rqstp) fh_put(&resp->fh); } +static void nfsaclsvc_release_setacl(struct svc_rqst *rqstp) +{ + struct nfsd3_setaclargs *argp = rqstp->rq_argp; + struct nfsd_attrstat *resp = rqstp->rq_resp; + + fh_put(&resp->fh); + posix_acl_release(argp->acl_access); + posix_acl_release(argp->acl_default); +} + #define ST 1 /* status*/ #define AT 21 /* attributes */ #define pAT (1+AT) /* post attributes - conditional */ @@ -343,7 +350,7 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = { .pc_func = nfsacld_proc_setacl, .pc_decode = nfsaclsvc_decode_setaclargs, .pc_encode = nfssvc_encode_attrstatres, - .pc_release = nfssvc_release_attrstat, + .pc_release = nfsaclsvc_release_setacl, .pc_argsize = sizeof(struct nfsd3_setaclargs), .pc_argzero = sizeof(struct nfsd3_setaclargs), .pc_ressize = sizeof(struct nfsd_attrstat), diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index 7b5433bd30..e87731380b 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -118,10 +118,7 @@ out_drop_lock: out_errno: resp->status = nfserrno(error); out: - /* argp->acl_{access,default} may have been allocated in - nfs3svc_decode_setaclargs. */ - posix_acl_release(argp->acl_access); - posix_acl_release(argp->acl_default); + /* argp->acl_{access,default} are released in nfs3svc_release_setacl. */ return rpc_success; } @@ -223,6 +220,16 @@ static void nfs3svc_release_getacl(struct svc_rqst *rqstp) posix_acl_release(resp->acl_default); } +static void nfs3svc_release_setacl(struct svc_rqst *rqstp) +{ + struct nfsd3_setaclargs *argp = rqstp->rq_argp; + struct nfsd3_attrstat *resp = rqstp->rq_resp; + + fh_put(&resp->fh); + posix_acl_release(argp->acl_access); + posix_acl_release(argp->acl_default); +} + #define ST 1 /* status*/ #define AT 21 /* attributes */ #define pAT (1+AT) /* post attributes - conditional */ @@ -256,7 +263,7 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = { .pc_func = nfsd3_proc_setacl, .pc_decode = nfs3svc_decode_setaclargs, .pc_encode = nfs3svc_encode_setaclres, - .pc_release = nfs3svc_release_fhandle, + .pc_release = nfs3svc_release_setacl, .pc_argsize = sizeof(struct nfsd3_setaclargs), .pc_argzero = sizeof(struct nfsd3_setaclargs), .pc_ressize = sizeof(struct nfsd3_attrstat), diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index 683bd1130a..62762e43f8 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -256,9 +256,7 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, BUG_ON(!ls->ls_file); if (nfsd4_layout_setlease(ls)) { - nfsd_file_put(ls->ls_file); - put_nfs4_file(fp); - kmem_cache_free(nfs4_layout_stateid_cache, ls); + nfs4_put_stid(stp); return NULL; } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d1aa6a6bfb..6254f58f6e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -6224,12 +6224,12 @@ nfsd4_add_rdaccess_to_wrdeleg(struct svc_rqst *rqstp, struct nfsd4_open *open, return (false); fp = stp->st_stid.sc_file; spin_lock(&fp->fi_lock); - __nfs4_file_get_access(fp, NFS4_SHARE_ACCESS_READ); if (!fp->fi_fds[O_RDONLY]) { + __nfs4_file_get_access(fp, NFS4_SHARE_ACCESS_READ); fp->fi_fds[O_RDONLY] = nf; + fp->fi_rdeleg_file = nfsd_file_get(fp->fi_fds[O_RDONLY]); nf = NULL; } - fp->fi_rdeleg_file = nfsd_file_get(fp->fi_fds[O_RDONLY]); spin_unlock(&fp->fi_lock); if (nf) nfsd_file_put(nf); diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index f6cadb30f4..d1a8801e35 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4093,6 +4093,8 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c index aca8c0146d..e28c51c587 100644 --- a/fs/smb/client/smb2transport.c +++ b/fs/smb/client/smb2transport.c @@ -213,7 +213,9 @@ smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid) list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { if (tcon->tid != tid) continue; + spin_lock(&tcon->tc_lock); ++tcon->tc_count; + spin_unlock(&tcon->tc_lock); trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, netfs_trace_tcon_ref_get_find_sess_tcon); return tcon; diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index 75342022d1..9de8250f1a 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -218,7 +218,10 @@ static inline bool vma_can_userfault(struct vm_area_struct *vma, { vm_flags &= __VM_UFFD_FLAGS; - if (vm_flags & VM_DROPPABLE) + if (vma->vm_flags & (VM_DROPPABLE | VM_SHADOW_STACK)) + return false; + + if (!is_vm_hugetlb_page(vma) && (vma->vm_flags & VM_SPECIAL)) return false; if ((vm_flags & VM_UFFD_MINOR) && diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index e96d1bd087..bf1e63d841 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -112,7 +112,8 @@ int sctp_transport_lookup_process(sctp_callback_t cb, struct net *net, const union sctp_addr *paddr, void *p, int dif); int sctp_transport_traverse_process(sctp_callback_t cb, sctp_callback_t cb_done, struct net *net, int *pos, void *p); -int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), void *p); +int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), + struct net *net, int *pos, void *p); int sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc, struct sctp_info *info); diff --git a/kernel.sbat b/kernel.sbat index de618d7587..6605dea0a6 100644 --- a/kernel.sbat +++ b/kernel.sbat @@ -1,2 +1,2 @@ sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md -kernel.almalinux,1,AlmaLinux,kernel-core,6.12.0-211.49.1.el10.x86_64,mailto:security@almalinux.org +kernel.almalinux,1,AlmaLinux,kernel-core,6.12.0-211.50.1.el10.x86_64,mailto:security@almalinux.org diff --git a/kernel/locking/spinlock_rt.c b/kernel/locking/spinlock_rt.c index db1e11b45d..1d5e1b3c60 100644 --- a/kernel/locking/spinlock_rt.c +++ b/kernel/locking/spinlock_rt.c @@ -79,10 +79,27 @@ void __sched rt_spin_unlock(spinlock_t *lock) __releases(RCU) { spin_release(&lock->dep_map, _RET_IP_); migrate_enable(); - rcu_read_unlock(); if (unlikely(!rt_mutex_cmpxchg_release(&lock->lock, current, NULL))) rt_mutex_slowunlock(&lock->lock); + + /* + * This must be last to prevent the following UAF: + * + * T1 T2 + * spin_lock(&p->lock); rcu_read_lock(); + * invalidate(p); p = rcu_dereference(ptr); + * rcu_assign_pointer(ptr, NULL); if (!p) return; + * spin_unlock(&p->lock); spin_lock(&p->lock); + * kfree_rcu(p); rcu_read_unlock(); + * .... + * spin_unlock(&p->lock) + * rcu_read_unlock(); // Ends grace period + * rcu_do_batch() + * kfree(p); + * UAF -> rt_mutex_cmpxchg_release(&p->lock.lock...) + */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_spin_unlock); @@ -262,17 +279,21 @@ void __sched rt_read_unlock(rwlock_t *rwlock) __releases(RCU) { rwlock_release(&rwlock->dep_map, _RET_IP_); migrate_enable(); - rcu_read_unlock(); rwbase_read_unlock(&rwlock->rwbase, TASK_RTLOCK_WAIT); + + /* This must be last. See comment in rt_spin_unlock() */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_read_unlock); void __sched rt_write_unlock(rwlock_t *rwlock) __releases(RCU) { rwlock_release(&rwlock->dep_map, _RET_IP_); - rcu_read_unlock(); migrate_enable(); rwbase_write_unlock(&rwlock->rwbase); + + /* This must be last. See comment in rt_spin_unlock() */ + rcu_read_unlock(); } EXPORT_SYMBOL(rt_write_unlock); diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 385d48293a..0cd1f8b5a1 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -749,6 +749,15 @@ static bool seccomp_is_const_allow(struct sock_fprog_kern *fprog, if (WARN_ON_ONCE(!fprog)) return false; + /* Our single exception to filtering. */ +#ifdef __NR_uretprobe +#ifdef SECCOMP_ARCH_COMPAT + if (sd->arch == SECCOMP_ARCH_NATIVE) +#endif + if (sd->nr == __NR_uretprobe) + return true; +#endif + for (pc = 0; pc < fprog->len; pc++) { struct sock_filter *insn = &fprog->filter[pc]; u16 code = insn->code; @@ -1023,6 +1032,9 @@ static inline void seccomp_log(unsigned long syscall, long signr, u32 action, */ static const int mode1_syscalls[] = { __NR_seccomp_read, __NR_seccomp_write, __NR_seccomp_exit, __NR_seccomp_sigreturn, +#ifdef __NR_uretprobe + __NR_uretprobe, +#endif -1, /* negative terminated */ }; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 3b996dedfe..e95020149d 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2778,7 +2778,9 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd, if (!folio_test_referenced(folio) && pmd_young(old_pmd)) folio_set_referenced(folio); folio_remove_rmap_pmd(folio, page, vma); + add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR); folio_put(folio); + return; } add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR); return; diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 5f0be13414..85225a417d 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -2052,21 +2052,6 @@ out_unlock: goto xa_unlocked; } - if (!is_shmem) { - filemap_nr_thps_inc(mapping); - /* - * Paired with the fence in do_dentry_open() -> get_write_access() - * to ensure i_writecount is up to date and the update to nr_thps - * is visible. Ensures the page cache will be truncated if the - * file is opened writable. - */ - smp_mb(); - if (inode_is_open_for_write(mapping->host)) { - result = SCAN_FAIL; - filemap_nr_thps_dec(mapping); - } - } - xa_locked: xas_unlock_irq(&xas); xa_unlocked: @@ -2078,6 +2063,32 @@ xa_unlocked: */ try_to_unmap_flush(); + if (result == SCAN_SUCCEED && !is_shmem && !mapping_large_folio_support(mapping)) { + /* + * invalidate_lock as shared excludes against concurrent opens + * in do_dentry_open() truncating the page cache. This is + * particularly important if there are dirty folios in transit. + */ + filemap_invalidate_lock_shared(mapping); + filemap_nr_thps_inc(mapping); + /* + * Paired with the fence in do_dentry_open() -> get_write_access() + * to ensure i_writecount is up to date and the update to nr_thps + * is visible. Ensures the page cache will be truncated if the + * file is opened writable. If collapse looks to be successful, + * flush any dirty pages out the page cache. With the nr_thps + * incremented, there won't be any new writers (nor new dirties). + */ + smp_mb(); + if (inode_is_open_for_write(mapping->host) || filemap_write_and_wait(mapping)) { + result = SCAN_FAIL; + filemap_nr_thps_dec(mapping); + filemap_invalidate_unlock_shared(mapping); + goto rollback; + } + filemap_invalidate_unlock_shared(mapping); + } + if (result == SCAN_SUCCEED && nr_none && !shmem_charge(mapping->host, nr_none)) result = SCAN_FAIL; diff --git a/mm/list_lru.c b/mm/list_lru.c index 7d69434c70..dba0f975b1 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -468,26 +468,29 @@ void memcg_reparent_list_lrus(struct mem_cgroup *memcg, struct mem_cgroup *paren mutex_lock(&list_lrus_mutex); list_for_each_entry(lru, &memcg_list_lrus, list) { struct list_lru_memcg *mlru; - XA_STATE(xas, &lru->xa, memcg->kmemcg_id); /* - * Lock the Xarray to ensure no on going list_lru_memcg - * allocation and further allocation will see css_is_dying(). + * css_is_dying() check in memcg_list_lru_alloc() avoids + * allocating a new mlru since CSS_DYING is already set for this + * memcg a rcu grace period ago. */ - xas_lock_irq(&xas); - mlru = xas_store(&xas, NULL); - xas_unlock_irq(&xas); + mlru = xa_load(&lru->xa, memcg->kmemcg_id); if (!mlru) continue; /* - * With Xarray value set to NULL, holding the lru lock below - * prevents list_lru_{add,del,isolate} from touching the lru, - * safe to reparent. + * Reparent each per-node list and mark the child dead + * (LONG_MIN) before clearing xarray entry otherwise a + * concurrent list_lru_del() may corrupt the list if it arrives + * after xarray clear but before reparenting as + * lock_list_lru_of_memcg will acquire parent's lock while the + * item is still on child's list. */ for_each_node(i) memcg_reparent_list_lru_one(lru, i, &mlru->node[i], parent); + xa_erase_irq(&lru->xa, memcg->kmemcg_id); + /* * Here all list_lrus corresponding to the cgroup are guaranteed * to remain empty, we can safely free this lru, any further diff --git a/mm/shrinker.c b/mm/shrinker.c index 4a93fd4336..cf730374fb 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -59,12 +59,14 @@ static inline int shrinker_unit_alloc(struct shrinker_info *new, return 0; } -void free_shrinker_info(struct mem_cgroup *memcg) +static void __free_shrinker_info(struct mem_cgroup *memcg) { struct mem_cgroup_per_node *pn; struct shrinker_info *info; int nid; + lockdep_assert_held(&shrinker_mutex); + for_each_node(nid) { pn = memcg->nodeinfo[nid]; info = rcu_dereference_protected(pn->shrinker_info, true); @@ -74,6 +76,13 @@ void free_shrinker_info(struct mem_cgroup *memcg) } } +void free_shrinker_info(struct mem_cgroup *memcg) +{ + mutex_lock(&shrinker_mutex); + __free_shrinker_info(memcg); + mutex_unlock(&shrinker_mutex); +} + int alloc_shrinker_info(struct mem_cgroup *memcg) { int nid, ret = 0; @@ -98,8 +107,8 @@ int alloc_shrinker_info(struct mem_cgroup *memcg) return ret; err: + __free_shrinker_info(memcg); mutex_unlock(&shrinker_mutex); - free_shrinker_info(memcg); return -ENOMEM; } diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c index 794bd433cc..d29c50e65b 100644 --- a/mm/shrinker_debug.c +++ b/mm/shrinker_debug.c @@ -186,10 +186,12 @@ int shrinker_debugfs_add(struct shrinker *shrinker) } shrinker->debugfs_entry = entry; - debugfs_create_file("count", 0440, entry, shrinker, - &shrinker_debugfs_count_fops); - debugfs_create_file("scan", 0220, entry, shrinker, - &shrinker_debugfs_scan_fops); + if (shrinker->count_objects) + debugfs_create_file("count", 0440, entry, shrinker, + &shrinker_debugfs_count_fops); + if (shrinker->scan_objects) + debugfs_create_file("scan", 0220, entry, shrinker, + &shrinker_debugfs_scan_fops); return 0; } diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 1f82f9da2f..5b4bd3ac5f 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -2292,10 +2292,11 @@ static int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { - struct ip6gre_net *ign = net_generic(dev_net(dev), ip6gre_net_id); + struct ip6_tnl *t = netdev_priv(dev); struct __ip6_tnl_parm p; - struct ip6_tnl *t; + struct ip6gre_net *ign; + ign = net_generic(t->net, ip6gre_net_id); t = ip6gre_changelink_common(dev, tb, data, &p, extack); if (IS_ERR(t)) return PTR_ERR(t); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index d69c027bee..e807c5e15b 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -850,7 +850,7 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, skb_reset_network_header(skb); - if (!pskb_inet_may_pull(skb)) { + if (skb_vlan_inet_prepare(skb, true)) { DEV_STATS_INC(tunnel->dev, rx_length_errors); DEV_STATS_INC(tunnel->dev, rx_errors); goto drop; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index a4e1d7951b..94157104d8 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1480,7 +1480,11 @@ ip_set_dump_done(struct netlink_callback *cb) struct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET]; ip_set_id_t index = (ip_set_id_t)cb->args[IPSET_CB_INDEX]; - struct ip_set *set = ip_set_ref_netlink(inst, index); + struct ip_set *set; + + rcu_read_lock(); + set = ip_set_ref_netlink(inst, index); + rcu_read_unlock(); if (set->variant->uref) set->variant->uref(set, cb, false); @@ -1685,7 +1689,9 @@ next_set: release_refcount: /* If there was an error or set is done, release set */ if (ret || !cb->args[IPSET_CB_ARG0]) { + rcu_read_lock(); set = ip_set_ref_netlink(inst, index); + rcu_read_unlock(); if (set->variant->uref) set->variant->uref(set, cb, false); pr_debug("release set %s\n", set->name); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 9883e64d78..1024e0fd2b 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -9968,7 +9968,7 @@ static int nft_flowtable_event(unsigned long event, struct net_device *dev, break; case NETDEV_REGISTER: /* NOP if not matching or already registered */ - if (!match || (changename && ops)) + if (!match || ops) continue; ops = kzalloc(sizeof(struct nf_hook_ops), diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 97248963a7..71a248cca7 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -603,10 +603,10 @@ restart: goto out; } } - } - if (cb->args[1]) { - cb->args[1] = 0; - goto restart; + if (cb->args[1]) { + cb->args[1] = 0; + goto restart; + } } out: rcu_read_unlock(); diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c index b16185e9a6..041426e3bd 100644 --- a/net/netfilter/nft_chain_filter.c +++ b/net/netfilter/nft_chain_filter.c @@ -344,7 +344,7 @@ static int nft_netdev_event(unsigned long event, struct net_device *dev, break; case NETDEV_REGISTER: /* NOP if not matching or already registered */ - if (!match || (changename && ops)) + if (!match || ops) continue; ops = kmemdup(&basechain->ops, diff --git a/net/sctp/diag.c b/net/sctp/diag.c index 5a43f25478..94e2a74c8d 100644 --- a/net/sctp/diag.c +++ b/net/sctp/diag.c @@ -92,6 +92,7 @@ static int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb, if (!--addrcnt) break; } + WARN_ON_ONCE(addrcnt); rcu_read_unlock(); return 0; @@ -372,42 +373,39 @@ static int sctp_ep_dump(struct sctp_endpoint *ep, void *p) struct sk_buff *skb = commp->skb; struct netlink_callback *cb = commp->cb; const struct inet_diag_req_v2 *r = commp->r; - struct net *net = sock_net(skb->sk); struct inet_sock *inet = inet_sk(sk); int err = 0; - if (!net_eq(sock_net(sk), net)) + lock_sock(sk); + if (ep->base.dead) goto out; - if (cb->args[4] < cb->args[1]) - goto next; - - if (!(r->idiag_states & TCPF_LISTEN) && !list_empty(&ep->asocs)) - goto next; + /* Skip eps with assocs if non-LISTEN states were requested, since + * they'll be dumped by sctp_sock_dump() during assoc traversal. + */ + if ((r->idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE)) && + !list_empty(&ep->asocs)) + goto out; if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family) - goto next; + goto out; if (r->id.idiag_sport != inet->inet_sport && r->id.idiag_sport) - goto next; + goto out; if (r->id.idiag_dport != inet->inet_dport && r->id.idiag_dport) - goto next; - - if (inet_sctp_diag_fill(sk, NULL, skb, r, - sk_user_ns(NETLINK_CB(cb->skb).sk), - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - cb->nlh, commp->net_admin) < 0) { - err = 2; goto out; - } -next: - cb->args[4]++; + + err = inet_sctp_diag_fill(sk, NULL, skb, r, + sk_user_ns(NETLINK_CB(cb->skb).sk), + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + cb->nlh, commp->net_admin); out: + release_sock(sk); return err; } @@ -478,41 +476,40 @@ static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, .r = r, .net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN), }; - int pos = cb->args[2]; + int pos; /* eps hashtable dumps * args: * 0 : if it will traversal listen sock * 1 : to record the sock pos of this time's traversal - * 4 : to work as a temporary variable to traversal list */ if (cb->args[0] == 0) { - if (!(idiag_states & TCPF_LISTEN)) - goto skip; - if (sctp_for_each_endpoint(sctp_ep_dump, &commp)) - goto done; -skip: + if (idiag_states & TCPF_LISTEN) { + pos = cb->args[1]; + if (sctp_for_each_endpoint(sctp_ep_dump, net, &pos, + &commp)) { + cb->args[1] = pos; + return; + } + } cb->args[0] = 1; cb->args[1] = 0; - cb->args[4] = 0; } + if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE))) + return; + /* asocs by transport hashtable dump * args: * 1 : to record the assoc pos of this time's traversal * 2 : to record the transport pos of this time's traversal * 3 : to mark if we have dumped the ep info of the current asoc - * 4 : to work as a temporary variable to traversal list - * 5 : to save the sk we get from travelsing the tsp list. + * 4 : to track position within ep->asocs list in sctp_sock_dump() */ - if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE))) - goto done; - + pos = cb->args[2]; sctp_transport_traverse_process(sctp_sock_filter, sctp_sock_dump, net, &pos, &commp); cb->args[2] = pos; - -done: cb->args[1] = cb->args[4]; cb->args[4] = 0; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index c6a9fb8705..2b6f2db20f 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5323,24 +5323,39 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net, } int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), - void *p) { - int err = 0; - int hash = 0; - struct sctp_endpoint *ep; + struct net *net, int *pos, void *p) { + int err, hash = 0, idx = 0, start; struct sctp_hashbucket *head; + struct sctp_endpoint *ep; for (head = sctp_ep_hashtable; hash < sctp_ep_hashsize; hash++, head++) { + start = idx; +again: read_lock_bh(&head->lock); sctp_for_each_hentry(ep, &head->chain) { - err = cb(ep, p); - if (err) + if (sock_net(ep->base.sk) != net) + continue; + if (idx++ >= *pos) { + sctp_endpoint_hold(ep); break; + } } read_unlock_bh(&head->lock); + + if (ep) { + err = cb(ep, p); + sctp_endpoint_put(ep); + if (err) + return err; + (*pos)++; + + idx = start; + goto again; + } } - return err; + return 0; } EXPORT_SYMBOL_GPL(sctp_for_each_endpoint); diff --git a/redhat/Makefile b/redhat/Makefile index 6825802b06..ccfe08b448 100644 --- a/redhat/Makefile +++ b/redhat/Makefile @@ -768,6 +768,7 @@ sources-rh: $(TARBALL) $(KABI_TARBALL) $(KABIDW_TARBALL) generate-testpatch-tmp scripts/mod/mod-sign.sh \ scripts/uki_addons/uki_create_addons.py \ scripts/uki_addons/uki_addons.json \ + scripts/kmap.py \ configs/flavors \ configs/generate_all_configs.sh \ configs/merge.py \ diff --git a/redhat/kernel.changelog-10.2 b/redhat/kernel.changelog-10.2 index 556f4b3dab..5e2353719d 100644 --- a/redhat/kernel.changelog-10.2 +++ b/redhat/kernel.changelog-10.2 @@ -1,3 +1,49 @@ +* Mon Aug 31 2026 CKI KWF Bot [6.12.0-211.50.1.el10_2] +- redhat: add kmap.py tool and kernel-kmap-internal package (Rado Vrbovsky) +- nvmet-auth: reject short AUTH_RECEIVE buffers (CKI Backport Bot) [RHEL-244915] {CVE-2026-72130} +- locking/rt: Fix the incorrect RCU protection in rt_spin_unlock() (CKI Backport Bot) [RHEL-242861] {CVE-2026-72069} +- s390: Revert support for DCACHE_WORD_ACCESS (John J Coleman) [RHEL-188180] +- NFSv4: include MAY_WRITE in open permission mask for O_TRUNC (CKI Backport Bot) [RHEL-234051] {CVE-2026-64298} +- nfsd: release layout stid on setlease failure (Scott Mayhew) [RHEL-227794] {CVE-2026-53399} +- NFSv4/flexfiles: reject zero filehandle version count (CKI Backport Bot) [RHEL-229415] {CVE-2026-53392} +- NFSv4/pNFS: reject zero-length r_addr in nfs4_decode_mp_ds_addr (CKI Backport Bot) [RHEL-228036] {CVE-2026-53391} +- NFSD: fix nfs4_file access extra count in nfsd4_add_rdaccess_to_wrdeleg (CKI Backport Bot) [RHEL-227946] {CVE-2026-53026} +- pNFS: Fix use-after-free in pnfs_update_layout() (CKI Backport Bot) [RHEL-226322] {CVE-2026-63800} +- nfsd: fix posix_acl leak on SETACL decode failure (CKI Backport Bot) [RHEL-225522] {CVE-2026-53397} +- mm/khugepaged: write all dirty file folios when collapsing (Rafael Aquini) [RHEL-236329] {CVE-2026-68086} +- userfaultfd: prevent registration of special VMAs (Rafael Aquini) [RHEL-237862] {CVE-2026-68166} +- userfaultfd: correctly prevent registering VM_DROPPABLE regions (Rafael Aquini) [RHEL-237862] {CVE-2026-68166} +- crypto: qat - fix VF2PF work teardown race in adf_disable_sriov() (Vladislav Dronov) [RHEL-234477] {CVE-2026-64438} +- mm: shrinker: fix NULL pointer dereference in debugfs (Rafael Aquini) [RHEL-230833] {CVE-2026-64417} +- mm: shrinker: fix shrinker_info teardown race with expansion (Rafael Aquini) [RHEL-230833] {CVE-2026-64418} +- x86/bugs: Make Safe-RET robust against interrupt injection (Waiman Long) [RHEL-230475] {CVE-2026-68480} +- crypto: qat - validate RSA CRT component lengths (CKI Backport Bot) [RHEL-234546] {CVE-2026-64304} +- Input: synaptics-rmi4 - bound the F3A keymap to the GPIO count (CKI Backport Bot) [RHEL-231446] {CVE-2026-64277} +- mm/huge_memory: update file PMD counter before folio_put() (CKI Backport Bot) [RHEL-231228] {CVE-2026-53189} +- Input: synaptics-rmi4 - bound the F30 keymap to the GPIO/LED count (CKI Backport Bot) [RHEL-230263] {CVE-2026-64276} +- ALSA: virtio: Validate control metadata from the device (CKI Backport Bot) [RHEL-230142] {CVE-2026-64490} +- net: mana: validate rx_req_idx to prevent out-of-bounds array access (CKI Backport Bot) [RHEL-229231] {CVE-2026-64018} +- smb: client: protect tc_count increment in smb2_find_smb_sess_tcon_unlocked() (CKI Backport Bot) [RHEL-228550] {CVE-2026-64136} +- bonding: alb: fix UAF in rlb_arp_recv during bond up/down (CKI Backport Bot) [RHEL-225292] {CVE-2026-45970} +- netfilter: ipset: fix race between dump and ip_set_list resize (CKI Backport Bot) [RHEL-227657] {CVE-2026-64189} +- iommu/amd: Fix clone_alias() to use the original device's devid (CKI Backport Bot) [RHEL-227452] {CVE-2026-53053} +- smb: client: fix change notify replay double-free (CKI Backport Bot) [RHEL-226988] {CVE-2026-64384} +- mm/list_lru: drain before clearing xarray entry on reparent (Rafael Aquini) [RHEL-227399] {CVE-2026-53153} +- s390/pfault: Fix virtual vs physical address confusion (Ramesh Chhetri) [RHEL-222507] +- crypto: qat - cancel work on re-enable SR-IOV timeout (CKI Backport Bot) [RHEL-218627] +- nvmet: fix pre-auth out-of-bounds heap read in Discovery Get Log Page (CKI Backport Bot) [RHEL-219623] {CVE-2026-64320} +- sctp: hold socket lock when dumping endpoints in sctp_diag (CKI Backport Bot) [RHEL-212393] +- seccomp: passthrough uretprobe systemcall without filtering (Ricardo Robaina) [RHEL-210908] {CVE-2025-21834} +- qede: fix off-by-one in BD ring consumption on build_skb failure (CKI Backport Bot) [RHEL-193043] +- zram: fix use-after-free in zram_bvec_write_partial() (CKI Backport Bot) [RHEL-191442] {CVE-2026-53185} +- USB: serial: io_ti: fix heap overflow in build_i2c_fw_hdr() (Desnes Nunes) [RHEL-191042] {CVE-2026-53195} +- USB: serial: io_ti: fix heap overflow in get_manuf_info() (Desnes Nunes) [RHEL-191042] {CVE-2026-53196} +- ip6_tunnel: use skb_vlan_inet_prepare() in __ip6_tnl_rcv() (CKI Backport Bot) [RHEL-189631] {CVE-2026-23003} +- ip6_gre: Use cached t->net in ip6erspan_changelink(). (CKI Backport Bot) [RHEL-180136] {CVE-2026-46120} +- netfilter: nf_tables: Fix for duplicate device in netdev hooks (CKI Backport Bot) [RHEL-179761] {CVE-2026-43454} +- netfilter: nfnetlink_cthelper: fix OOB read in nfnl_cthelper_dump_table() (CKI Backport Bot) [RHEL-179744] {CVE-2026-43450} +Resolves: RHEL-179744, RHEL-179761, RHEL-180136, RHEL-188180, RHEL-189631, RHEL-191042, RHEL-191442, RHEL-193043, RHEL-210908, RHEL-212393, RHEL-218627, RHEL-219623, RHEL-222507, RHEL-225292, RHEL-225522, RHEL-226322, RHEL-226988, RHEL-227399, RHEL-227452, RHEL-227657, RHEL-227794, RHEL-227946, RHEL-228036, RHEL-228550, RHEL-229231, RHEL-229415, RHEL-230142, RHEL-230263, RHEL-230475, RHEL-230833, RHEL-231228, RHEL-231446, RHEL-234051, RHEL-234477, RHEL-234546, RHEL-236329, RHEL-237862, RHEL-242861, RHEL-244915 + * Wed Aug 19 2026 CKI KWF Bot [6.12.0-211.49.1.el10_2] - udf: fix partition descriptor append bookkeeping (CKI Backport Bot) [RHEL-179570] {CVE-2026-45991} - cifs: fix time_last_write stamp placement in setattr/truncate paths (Paulo Alcantara) [RHEL-235459] diff --git a/redhat/kernel.spec.template b/redhat/kernel.spec.template index 70971b5d96..f3fd938f9d 100644 --- a/redhat/kernel.spec.template +++ b/redhat/kernel.spec.template @@ -250,6 +250,8 @@ Summary: The Linux kernel %define with_ynl %{?_without_ynl: 0} %{?!_without_ynl: 1} # kernel-debuginfo %define with_debuginfo %{?_without_debuginfo: 0} %{?!_without_debuginfo: 1} +# kernel-kmap-internal: source-to-module mapping data (JSON) +%define with_kmap %{?_without_kmap: 0} %{?!_without_kmap: 1} # kernel-abi-stablelists %define with_kernel_abi_stablelists %{?_without_kernel_abi_stablelists: 0} %{?!_without_kernel_abi_stablelists: 1} # internal samples and selftests @@ -631,6 +633,10 @@ Summary: The Linux kernel %define _enable_debug_packages 0 %endif +%ifnarch x86_64 ppc64le s390x aarch64 riscv64 +%define with_kmap 0 +%endif + # Architectures we build tools/cpupower on %if 0%{?fedora} %define cpupowerarchs %{ix86} x86_64 ppc64le aarch64 @@ -1103,6 +1109,9 @@ Source489: %{name}-x86_64-automotive-debug-rhel.config # Sources for kernel-tools Source2002: kvm_stat.logrotate +# Sources for kernel-kmap-internal +Source2100: kmap.py + # Some people enjoy building customized kernels from the dist-git in Fedora and # use this to override configuration options. One day they may all use the # source tree, but in the mean time we carry this to support the legacy workflow @@ -1377,6 +1386,27 @@ analysing the logical and timing behavior of Linux. # with_tools %endif +%if %{with_kmap} && %{with_base} +%package -n %{name}-kmap-internal +Summary: Kernel source-to-module mapping data and module list +Group: Development/System +BuildRequires: python3 +%description -n %{name}-kmap-internal +The %{name}-kmap-internal package contains a JSON mapping file that describes +which C source files contributed to each kernel module and to the built-in +vmlinux image, and which RPM package each module is shipped in. + +Files installed under /usr/share/%{name}-kmap-internal/: + kernel-map-.json - unified mapping file for all kernel variants containing: + - variants: list of variant names (e.g., ["stock", "rt", "automotive"]) + - source-map: source file mappings + - obj-src: maps kernel objects to source files with variant indices + - src-obj: maps source files to kernel objects with variant indices + - module-map: module to RPM mappings + - module-rpm: maps module names to RPM package names with variant indices + - rpm-modules: maps RPM package names to module names with variant indices +%endif + %if %{with_selftests} %package selftests-internal @@ -2940,6 +2970,44 @@ BuildKernel() { %endif fi # $DoModules -eq 1 +%if %{with_kmap} && %{with_base} + # Generate source-to-module mapping data using kmap.py. + # Must run before the next BuildKernel call issues make mrproper, which + # would wipe the .cmd files that kmap.py reads. Skip debug variants + # (same source mapping as the base kernel). + if [[ "$Variant" != *debug* ]]; then + _kmap_bdir=$(pwd) + _kmap_basedir=%{_builddir}/kmap-data + _kmap_merged=$_kmap_basedir/kernel-map.json + _kmap_variant=${Variant:-stock} + _kmap_listprefix=../kernel${Variant:+-${Variant}} + mkdir -p $_kmap_basedir + + # Build kmap.py arguments; merge with existing data if present + _kmap_args="--directory $_kmap_bdir --outputdir $_kmap_basedir --rhel upstream --variant $_kmap_variant --time" + _kmap_args="$_kmap_args --vmlinux-rpm %{name}-core-%{KVERREL}.rpm" + if [ -f "$_kmap_merged" ]; then + _kmap_args="$_kmap_args --input kernel-map.json" + fi + + # Add module list files for module-to-RPM mapping + for _kmap_type in modules-core modules modules-extra modules-internal; do + if [ -f "${_kmap_listprefix}-${_kmap_type}.list" ]; then + _kmap_rpm=%{name}-${_kmap_type}-%{KVERREL}.rpm + _kmap_args="$_kmap_args --module-list ${_kmap_rpm}:${_kmap_listprefix}-${_kmap_type}.list" + fi + done +%if 0%{!?fedora:1} + if [ -f "${_kmap_listprefix}-modules-partner.list" ]; then + _kmap_rpm=%{name}-modules-partner-%{KVERREL}.rpm + _kmap_args="$_kmap_args --module-list ${_kmap_rpm}:${_kmap_listprefix}-modules-partner.list" + fi +%endif + + python3 %{SOURCE2100} $_kmap_args + fi +%endif + remove_depmod_files() { # remove files that will be auto generated by depmod at rpm -i time @@ -3829,6 +3897,14 @@ find -type f ! -executable -exec install -D -m644 {} %{buildroot}%{_libexecdir}/ popd %endif +%if %{with_kmap} && %{with_base} +# Install kmap data files produced during %build. +_kmap_basedir=%{_builddir}/kmap-data +_kmap_destbase=$RPM_BUILD_ROOT%{_datadir}/%{name}-kmap-internal +mkdir -p $_kmap_destbase +install -m 644 $_kmap_basedir/kernel-map.json $_kmap_destbase/kernel-map-%{KVERREL}.json +%endif + ### ### clean ### @@ -4320,6 +4396,13 @@ fi\ %{_libexecdir}/kselftests %endif +%if %{with_kmap} && %{with_base} +%files -n %{name}-kmap-internal +%defattr(-,root,root) +%dir %{_datadir}/%{name}-kmap-internal +%{_datadir}/%{name}-kmap-internal/kernel-map-%{KVERREL}.json +%endif + # empty meta-package %if %{with_up_base} %ifnarch %nobuildarches noarch diff --git a/redhat/scripts/kmap.py b/redhat/scripts/kmap.py new file mode 100755 index 0000000000..70af504d1b --- /dev/null +++ b/redhat/scripts/kmap.py @@ -0,0 +1,874 @@ +#!/usr/bin/env python3 + +""" +kmap.py - Kernel Source-to-Binary Mapping Tool + +DESCRIPTION + This tool parses Linux kernel build artifacts (.cmd files, archives) to + create a JSON mapping of which source files contribute to which kernel + binaries (modules and vmlinux). It supports merging data from multiple + kernel build variants (e.g., stock, debug, rt) into a single output + file using variant indices to track per-variant mappings. + + Supported RHEL versions: 7, 8, 9, upstream + +FEATURES + - Parses .cmd files generated by kbuild to extract compilation commands + - Supports C (gcc/clang) and Rust (rustc) source files + - Handles built-in objects via built-in.o.cmd (RHEL 7), built-in.a + (RHEL 8/9), or vmlinux.a (upstream) + - Merges multiple kernel variants into single output with variant indices + - Maps modules to their containing RPM packages + +OUTPUT FORMAT + The output is a JSON file with the following structure: + + { + "variants": ["stock", "rt", ...], + "source-map": { + "obj-src": {: {: []}}, + "src-obj": {: {: []}} + }, + "module-map": { + "module-rpm": {: {: []}}, + "rpm-modules": {: {: []}} + } + } + +PARSING THE OUTPUT + import json + + with open('kernel-map.json') as f: + data = json.load(f) + + variants = data['variants'] + obj_src = data['source-map']['obj-src'] + src_obj = data['source-map']['src-obj'] + module_rpm = data['module-map']['module-rpm'] + rpm_modules = data['module-map']['rpm-modules'] + + # Get sources for vmlinux in variant "stock" (index 0) + vmlinux_sources = [src for src, indices in obj_src['vmlinux'].items() + if 0 in indices] + + # Find which object a source contributes to + fork_objects = [obj for obj, indices in src_obj['kernel/fork.c'].items() + if 0 in indices] + + # Get RPM for a module + def get_rpm(module, variant_idx): + for rpm, indices in module_rpm.get(module, {}).items(): + if variant_idx in indices: + return rpm + return None + +USAGE + # Single variant + ./kmap.py -d /build/stock -r upstream -v stock -o /output + + # Merge multiple variants + ./kmap.py -d /build/stock -r upstream -v stock -o /output + ./kmap.py -d /build/debug -r upstream -v debug -o /output -i kernel-map.json + ./kmap.py -d /build/rt -r upstream -v rt -o /output -i kernel-map.json + + # With module-to-RPM mapping + ./kmap.py -d /build -r upstream -v stock -o /output \\ + --vmlinux-rpm kernel-core-5.14.0.rpm \\ + --module-list kernel-modules-5.14.0.rpm:modules.list + +CREDITS + Inspired by gen_compile_commands.py and Joe Lawrence. + Time measurement option added for Jan Stancek. +""" + +import re +import os +import pathlib +import json +import argparse +import subprocess +import time + + +# ============================================================================= +# Constants +# ============================================================================= + +VMLINUX = 'vmlinux' +KERNEL_PREFIX = 'kernel/' + +# Supported compilers and linkers +COMPILERS = ('gcc', 'clang') +LINKER = 'ld' +RUST_COMPILER = 'rustc' + + +# ============================================================================= +# Regex Patterns +# +# These patterns parse the kernel build .cmd files to extract: +# - What compiler/linker was used +# - What source files were compiled +# - What object files were linked +# ============================================================================= + +# Pattern for .ko.cmd files (module linking) +# Matches: cmd_path/to/module.ko := ld -o +# Note: upstream kernels use 'savedcmd_' prefix instead of 'cmd_' +_KO_CMD_PATTERN = ( + r'^(saved)?cmd_[^ ]*\.ko\s*:=\s*ld\s+' + r'(?P.*) -o (?P\S+) (?P[^;]+?)\s*(?:;|$)' +) + +# Pattern for .o.cmd files (object compilation) +# Matches multiple formats: +# - C/asm: cmd_*.o := gcc/clang -o +# - Rust: cmd_*.o := rustc --emit=obj= +# - Rust source mapping: source_*.o := +_O_CMD_PATTERN = ( + r'^(saved)?(?:cmd_|source_)[^ ]*\.o\s+:=\s+' + r'(?:' + r'(?P(?:\S+=\S+\s+)*)?' + r'(?Pgcc|clang|ld|rustc)\s+' + r'(?P.*?)' + r'(?:' + r'\s+-o\s+(?P\S+)\s+(?P[^;]+?)' + r'|' + r'\s+--emit=obj=(?P\S+).*?\s+(?P\S+\.rs)' + r')' + r'|' + r'(?P\S+\.rs)' + r')\s*(?:;|$)' +) + +ko_matcher = re.compile(_KO_CMD_PATTERN) +o_matcher = re.compile(_O_CMD_PATTERN) + + +# ============================================================================= +# Utility Functions +# ============================================================================= + +def parse_args(): + """ + Parse command line arguments + """ + parser = argparse.ArgumentParser( + prog='Kernel Build Mapper', + description='Scrape Linux Kernel .cmd files to map contribution ' + 'of individual source files to kernel binaries.', + ) + + parser.add_argument('-d', '--directory', + help='Directory with kernel build artifacts.', + default='', + type=str) + + parser.add_argument('-o', '--outputdir', + help='Directory where to put generated files.', + default='', + type=str) + + parser.add_argument('-p', '--prefix', + help='Name prefix to the generated files.', + default='', + type=str) + + parser.add_argument('-r', '--rhel', + help='Specify to what version of RHEL artifacts belong (upstream for RHEL 10+/kernel-ark)', + choices=['7', '8', '9', 'upstream'], + required=True) + + parser.add_argument('-m', '--no-modules', + help='*SKIP* collecting information about modules', + action='store_true') + + parser.add_argument('-b', '--no-builtin', + help='*SKIP* collecting information about built-in', + action='store_true') + + parser.add_argument('-t', '--time', + help='Print elapsed time at the end', + action='store_true') + + parser.add_argument('-v', '--variant', + help='Name of this kernel variant (e.g., stock, debug, rt)', + required=True, + type=str) + + parser.add_argument('-i', '--input', + help='Existing JSON file to merge with (from previous variant runs)', + default='', + type=str) + + parser.add_argument('--module-list', + help='Module list in format : (can be specified multiple times)', + action='append', + default=[], + metavar='RPM:PATH') + + parser.add_argument('--vmlinux-rpm', + help='RPM package name for vmlinux (e.g., "kernel-5.14.0.rpm")', + default='', + type=str) + + return parser.parse_args() + + +def load_file(filename, build_dir=''): + """ + Load a text file and return non-empty lines as a list + """ + full_path = os.path.join(build_dir, filename) + + try: + with open(full_path, 'rt', encoding='utf-8') as file: + return [line.rstrip() for line in file if line.rstrip()] + except FileNotFoundError: + print(f'File "{full_path}" not found.') + return [] + + +def name_to_cmd(filename, suffix): + """ + Convert object filename to corresponding .cmd script filename + + Example: kernel/fork.o -> kernel/.fork.o.cmd + """ + path, _ = os.path.splitext(filename) + dirname = os.path.dirname(path) + basename = os.path.basename(path) + + return os.path.join(dirname, '.' + basename + suffix) + + +def strip_path_prefixes(path, build_dir): + """ + Normalize path by removing build directory prefix and other artifacts + """ + path = pathlib.Path(path) + + if build_dir: + try: + return str(path.resolve().relative_to(pathlib.Path(build_dir).resolve())) + except ValueError: + pass + + if path.is_absolute(): + path = pathlib.Path(*path.parts[1:]) + + while path.parts and path.parts[0] == 'repos': + path = pathlib.Path(*path.parts[1:]) + + return str(path) + + +def is_external_source(path): + """ + Check if path should be excluded from source mapping + + Filters out: + - Rust toolchain sources (not part of kernel source) + - Generated assembly files (e.g., initramfs_data.S) + """ + if 'rustlib' in path: + return True + if path.endswith('_data.S'): + return True + return False + + +def is_mod_file_reference(ld_input): + """ + Check if linker input is a .mod file reference (RHEL 9+ module linking) + + In RHEL 9+, module linking uses @path/to/module.mod files that contain + the list of object files to link. + """ + files = ld_input.split() + return len(files) == 1 and files[0].startswith('@') and files[0].endswith('.mod') + + +# ============================================================================= +# SourceMap Class +# ============================================================================= + +class SourceMap: + """ + Collects and manages kernel source-to-binary mappings. + + This class parses kernel build artifacts (.cmd files, archives) to determine + which source files contribute to which kernel binaries (modules and vmlinux). + + Data structures: + obj_to_src: {object_name: {source_file: [variant_indices]}} + src_to_obj: {source_file: {object_name: [variant_indices]}} + module_rpm: {module_name: {rpm_name: [variant_indices]}} + rpm_to_modules: {rpm_name: {module_name: [variant_indices]}} + + Variant indices correspond to positions in the 'variants' list. + """ + + # Dispatch table for RHEL-specific builtin collection methods + BUILTIN_COLLECTORS = { + '7': '_collect_builtin_rhel7', + '8': '_collect_builtin_archive', + '9': '_collect_builtin_archive', + 'upstream': '_collect_builtin_vmlinux_archive', + } + + def __init__(self, build_dir='', output_dir='', outprefix='', rhel='7', variant=''): + # Source mappings + self.obj_to_src = {} + self.src_to_obj = {} + # Module-to-RPM mappings + self.module_rpm = {} + self.rpm_to_modules = {} + + # Configuration + self.build_dir = build_dir + self.output_dir = output_dir + self.outprefix = outprefix + self.rhel = rhel + + # Module list + self.modlist = set() + + # Variant tracking for multi-variant merging + self.variants = [] + self.variant = variant + self.variant_idx = 0 + + # ------------------------------------------------------------------------- + # Variant Management + # ------------------------------------------------------------------------- + + def load_existing(self, filename): + """ + Load existing merged data from previous variant runs + """ + if not filename: + return + + full_path = os.path.join(self.output_dir, filename) if self.output_dir else filename + + if not os.path.exists(full_path): + return + + with open(full_path, 'rt', encoding='utf-8') as f: + data = json.load(f) + + self.variants = data.get('variants', []) + + module_map = data.get('module-map', {}) + self.module_rpm = module_map.get('module-rpm', {}) + self.rpm_to_modules = module_map.get('rpm-modules', {}) + + source_map = data.get('source-map', {}) + self.obj_to_src = source_map.get('obj-src', {}) + self.src_to_obj = source_map.get('src-obj', {}) + + def add_variant(self): + """ + Register current variant and store its index + """ + if self.variant in self.variants: + self.variant_idx = self.variants.index(self.variant) + else: + self.variant_idx = len(self.variants) + self.variants.append(self.variant) + + # ------------------------------------------------------------------------- + # Source Merging Helpers + # ------------------------------------------------------------------------- + + def _merge_sources(self, target, source): + """ + Merge source dict into target dict by combining variant index lists + """ + for src, indices in source.items(): + if src not in target: + target[src] = [] + for idx in indices: + if idx not in target[src]: + target[src].append(idx) + + def _add_source(self, sources, source_path): + """ + Add a source file to the sources dict with current variant index + + Returns True if source was added, False if filtered out + """ + source_path = os.path.normpath(source_path) + source_path = strip_path_prefixes(source_path, self.build_dir) + + if is_external_source(source_path): + return False + + sources[source_path] = [self.variant_idx] + return True + + # ------------------------------------------------------------------------- + # .cmd File Parsing + # ------------------------------------------------------------------------- + + def _parse_ko_cmd(self, ko_name): + """ + Parse .ko.cmd file to find the primary object file linked into the module + """ + ko_script = name_to_cmd(ko_name, '.ko.cmd') + lines = load_file(ko_script, build_dir=self.build_dir) + + for line in lines: + result = ko_matcher.match(line) + if result: + input_files = result.group('input_files').split() + return input_files[0] + + return None + + def _parse_o_cmd(self, o_name, translate=True): + """ + Parse .o.cmd file to extract source files that contribute to the object + + Handles: + - C/assembly compilation (gcc, clang) + - Linking (ld) - recursively parses linked objects + - Rust compilation (rustc) + - Rust source file mappings + + Args: + o_name: Object file name or .cmd file path + translate: If True, convert object name to .cmd path (e.g., kernel/fork.o + becomes kernel/.fork.o.cmd). If False, use o_name as-is. + This parameter exists to handle different kbuild behaviors + across RHEL versions - some code paths may already have .cmd + paths while others have object names that need translation. + """ + sources = {} + + o_script = name_to_cmd(o_name, '.o.cmd') if translate else o_name + lines = load_file(o_script, build_dir=self.build_dir) + + for line in lines: + result = o_matcher.match(line) + if not result: + continue + + command = result.group('command') + + if command in COMPILERS: + self._handle_compiler(result, sources) + break + + elif command == LINKER: + self._handle_linker(result, sources) + break + + elif command == RUST_COMPILER: + self._handle_rustc(result, sources) + break + + else: + self._handle_rust_source_mapping(result, sources) + break + + return sources + + def _handle_compiler(self, match_result, sources): + """ + Handle gcc/clang compilation - extract the input source file + """ + obj_input = match_result.group('input_files') + if obj_input: + self._add_source(sources, obj_input) + + def _handle_linker(self, match_result, sources): + """ + Handle ld linking - recursively parse linked objects + """ + obj_input = match_result.group('input_files') + if not obj_input: + return + + if is_mod_file_reference(obj_input): + self._merge_sources(sources, self._parse_mod_cmd(obj_input)) + else: + for obj_file in obj_input.split(): + self._merge_sources(sources, self._parse_o_cmd(obj_file)) + + def _handle_rustc(self, match_result, sources): + """ + Handle rustc compilation - extract the Rust source file + """ + rust_input = match_result.group('rust_input_file') + if rust_input: + self._add_source(sources, rust_input) + + def _handle_rust_source_mapping(self, match_result, sources): + """ + Handle Rust source file mapping (source_*.o := *.rs) + """ + source_file = match_result.group('source_file') + if source_file and source_file.endswith('.rs'): + self._add_source(sources, source_file) + + def _parse_mod_cmd(self, mod_name): + """ + Parse .mod file used by RHEL 9+ for linking modules + + These files contain a list of object files to link into the module. + """ + sources = {} + mod_name = mod_name.strip('@') + lines = load_file(mod_name, build_dir=self.build_dir) + + for line in lines: + self._merge_sources(sources, self._parse_o_cmd(line)) + + return sources + + def _parse_a_cmd(self, a_name): + """ + Parse archive .cmd file (RHEL 7 built-in.o.cmd files) + """ + sources = {} + lines = load_file(a_name, build_dir=self.build_dir) + + for line in lines: + result = o_matcher.match(line) + if not result: + continue + + ar_input = result.group('input_files') + for filename in ar_input.rsplit(): + if filename.endswith('.o'): + self._merge_sources(sources, self._parse_o_cmd(filename)) + elif filename.endswith('.a'): + self._merge_sources(sources, self._parse_a_cmd(filename)) + + return sources + + # ------------------------------------------------------------------------- + # Module Collection + # ------------------------------------------------------------------------- + + def _load_module_list(self, modorder='modules.order'): + """ + Load modules.order file and normalize paths + + RHEL 7, 8: modules.order has 'kernel/' prefix that needs to be stripped + RHEL 9, upstream: modules.order paths are used as-is + """ + lines = load_file(modorder, build_dir=self.build_dir) + + if not lines: + print(f'Warning: modules.order is empty or not found') + self.modlist = set() + return + + if self.rhel in ['9', 'upstream']: + self.modlist = set(lines) + else: + # RHEL 7, 8: strip fake 'kernel/' prefix added by kbuild + self.modlist = { + path[len(KERNEL_PREFIX):] if path.startswith(KERNEL_PREFIX) else path + for path in lines + } + + def _parse_module_objects(self): + """ + Parse all module .cmd files and extract source mappings. + + Note on RHEL version differences: + - RHEL 7/8/9: modules.order contains .ko paths (e.g., fs/exfat/exfat.ko) + - upstream: modules.order contains .o paths (e.g., fs/exfat/exfat.o) + + The normalization below ensures consistent .ko keys in output regardless + of the modules.order format. + """ + for kobj in self.modlist: + # Normalize to .ko - upstream has .o paths in modules.order + if self.rhel == 'upstream' and kobj.endswith('.o'): + module_name = kobj[:-2] + '.ko' + else: + module_name = kobj + + obj_name = self._parse_ko_cmd(module_name) + if obj_name is None: + print(f'Warning: Could not parse .ko.cmd for "{module_name}", skipping.') + continue + + obj_source = self._parse_o_cmd(obj_name) + if module_name not in self.obj_to_src: + self.obj_to_src[module_name] = {} + self._merge_sources(self.obj_to_src[module_name], obj_source) + + def collect_modules(self, no_modules=False, modorder='modules.order'): + """ + Collect source files for kernel modules + """ + if no_modules: + return + + self._load_module_list(modorder) + self._parse_module_objects() + + # ------------------------------------------------------------------------- + # Builtin Collection (RHEL version-specific) + # ------------------------------------------------------------------------- + + def _collect_builtin_rhel7(self): + """ + Collect built-in objects for RHEL 7 using *built-in.o.cmd files + """ + pattern = '*built-in.o.cmd' + cmd_list = { + strip_path_prefixes(path, self.build_dir) + for path in pathlib.Path(self.build_dir).rglob(pattern) + } + + for entry in cmd_list: + sources = self._parse_a_cmd(entry) + self._merge_sources(self.obj_to_src[VMLINUX], sources) + + def _collect_builtin_archive(self): + """ + Collect built-in objects for RHEL 8/9 using *built-in.a archives + """ + pattern = '*built-in.a' + archive_list = { + strip_path_prefixes(path, self.build_dir) + for path in pathlib.Path(self.build_dir).rglob(pattern) + } + + for entry in archive_list: + path = os.path.join(self.build_dir, entry) + objects = subprocess.check_output(['ar', 't', path]).decode().split() + + for obj in objects: + obj_path = strip_path_prefixes(obj, build_dir=self.build_dir) + sources = self._parse_o_cmd(obj_path) + self._merge_sources(self.obj_to_src[VMLINUX], sources) + + def _collect_builtin_vmlinux_archive(self): + """ + Collect built-in objects for upstream kernels using single vmlinux.a archive + """ + path = os.path.join(self.build_dir, 'vmlinux.a') + objects = subprocess.check_output(['ar', 't', path]).decode().split() + + for obj in objects: + obj_path = strip_path_prefixes(obj, build_dir=self.build_dir) + sources = self._parse_o_cmd(obj_path) + self._merge_sources(self.obj_to_src[VMLINUX], sources) + + def collect_builtin(self, no_builtin=False): + """ + Collect source files for vmlinux built-in objects + + Uses RHEL version-specific collection method via dispatch table. + """ + if VMLINUX not in self.obj_to_src: + self.obj_to_src[VMLINUX] = {} + + if no_builtin: + return + + collector_name = self.BUILTIN_COLLECTORS.get(self.rhel, '_collect_builtin_archive') + collector = getattr(self, collector_name) + collector() + + # ------------------------------------------------------------------------- + # Module-to-RPM Mapping + # ------------------------------------------------------------------------- + + def _parse_module_list_file(self, filename, rpm_name): + """ + Parse a module list file and add entries to module_rpm mapping + + Handles various formats: + - Absolute paths: /lib/modules/5.14.0/kernel/drivers/net/e1000.ko.xz + - Relative paths: kernel/drivers/net/e1000.ko + - Skips RPM spec directives (%dir, %defattr, etc.) + + Args: + filename: Path to the module list file + rpm_name: Literal RPM package name (e.g., "kernel-modules-5.14.0.rpm") + """ + if not filename or not os.path.exists(filename): + return + + with open(filename, 'rt', encoding='utf-8') as f: + for line in f: + line = line.strip() + + # Skip empty lines, comments, and RPM spec directives + if not line or line.startswith('#') or line.startswith('%'): + continue + + # Skip non-module files + if '.ko' not in line: + continue + + # Extract module name from path + module = os.path.basename(line) + # Remove .ko and any compression suffix + module = re.sub(r'\.ko(\.[^.]+)?$', '', module) + + if not module: + continue + + if module not in self.module_rpm: + self.module_rpm[module] = {} + + if rpm_name not in self.module_rpm[module]: + self.module_rpm[module][rpm_name] = [] + if self.variant_idx not in self.module_rpm[module][rpm_name]: + self.module_rpm[module][rpm_name].append(self.variant_idx) + + def collect_module_rpm(self, module_lists, vmlinux_rpm=''): + """ + Collect module-to-RPM name mappings from module list files + + Args: + module_lists: List of "rpm_name:path" strings + vmlinux_rpm: RPM package name for vmlinux + """ + if not module_lists and not vmlinux_rpm: + return + + # Add vmlinux to specified RPM + if vmlinux_rpm: + if VMLINUX not in self.module_rpm: + self.module_rpm[VMLINUX] = {} + if vmlinux_rpm not in self.module_rpm[VMLINUX]: + self.module_rpm[VMLINUX][vmlinux_rpm] = [] + if self.variant_idx not in self.module_rpm[VMLINUX][vmlinux_rpm]: + self.module_rpm[VMLINUX][vmlinux_rpm].append(self.variant_idx) + + # Process module list files + for entry in module_lists: + if ':' not in entry: + print(f'Warning: Invalid --module-list format "{entry}", expected RPM:PATH') + continue + + rpm_name, path = entry.split(':', 1) + self._parse_module_list_file(path, rpm_name) + + # ------------------------------------------------------------------------- + # Output + # ------------------------------------------------------------------------- + + def _build_reverse_map(self): + """ + Create reverse mapping (source -> {obj: [indices]}) from obj_to_src + """ + for obj, sources in self.obj_to_src.items(): + for source, indices in sources.items(): + if source not in self.src_to_obj: + self.src_to_obj[source] = {} + if obj not in self.src_to_obj[source]: + self.src_to_obj[source][obj] = [] + for idx in indices: + if idx not in self.src_to_obj[source][obj]: + self.src_to_obj[source][obj].append(idx) + + def _build_rpm_reverse_map(self): + """ + Create reverse mapping (rpm -> {module: [indices]}) from module_rpm + """ + for module, rpms in self.module_rpm.items(): + for rpm, indices in rpms.items(): + if rpm not in self.rpm_to_modules: + self.rpm_to_modules[rpm] = {} + if module not in self.rpm_to_modules[rpm]: + self.rpm_to_modules[rpm][module] = [] + for idx in indices: + if idx not in self.rpm_to_modules[rpm][module]: + self.rpm_to_modules[rpm][module].append(idx) + + def save_data(self): + """ + Save collected data to JSON file + + Output structure: + - variants: list of variant names + - source-map: source file mappings + - obj-src: object/module -> {source: [variant_indices]} + - src-obj: source -> {object/module: [variant_indices]} + - module-map: module to RPM mappings + - module-rpm: module name -> {rpm-name: [variant_indices]} + - rpm-modules: rpm-name -> {module name: [variant_indices]} + + Variant indices correspond to positions in the 'variants' list. + """ + output = { + 'variants': self.variants, + 'source-map': { + 'obj-src': self.obj_to_src, + 'src-obj': self.src_to_obj + }, + 'module-map': { + 'module-rpm': self.module_rpm, + 'rpm-modules': self.rpm_to_modules + } + } + + filename = self.outprefix + 'kernel-map.json' + with open(os.path.join(self.output_dir, filename), 'wt', encoding='utf-8') as f: + json.dump(output, f, indent=2, sort_keys=True) + + # ------------------------------------------------------------------------- + # Main Entry Point + # ------------------------------------------------------------------------- + + def collect_data(self, no_modules=False, no_builtin=False, modorder='modules.order', + input_file='', module_lists=None, vmlinux_rpm=''): + """ + Main entry point - collect all data and save to JSON + """ + self.load_existing(input_file) + self.add_variant() + self.collect_modules(no_modules, modorder) + self.collect_builtin(no_builtin) + self.collect_module_rpm(module_lists or [], vmlinux_rpm) + self._build_reverse_map() + self._build_rpm_reverse_map() + self.save_data() + + +# ============================================================================= +# Main +# ============================================================================= + +def main(): + """ + Main entry point + """ + args = parse_args() + + start = time.monotonic() + + source_map = SourceMap( + build_dir=args.directory, + output_dir=args.outputdir, + outprefix=args.prefix, + rhel=args.rhel, + variant=args.variant) + + source_map.collect_data( + no_modules=args.no_modules, + no_builtin=args.no_builtin, + input_file=args.input, + module_lists=args.module_list, + vmlinux_rpm=args.vmlinux_rpm) + + if args.time: + elapsed = time.monotonic() - start + minutes, seconds = divmod(int(elapsed), 60) + print(f'Elapsed time: {minutes:02d}:{seconds:02d}') + + +if __name__ == '__main__': + main() diff --git a/sound/virtio/virtio_kctl.c b/sound/virtio/virtio_kctl.c index ffb903d562..45f7b6a5b3 100644 --- a/sound/virtio/virtio_kctl.c +++ b/sound/virtio/virtio_kctl.c @@ -18,6 +18,21 @@ static const snd_ctl_elem_type_t g_v2a_type_map[] = { [VIRTIO_SND_CTL_TYPE_IEC958] = SNDRV_CTL_ELEM_TYPE_IEC958 }; +/* Map for converting VirtIO types to maximum value counts. */ +static const unsigned int g_v2a_count_map[] = { + [VIRTIO_SND_CTL_TYPE_BOOLEAN] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.integer), + [VIRTIO_SND_CTL_TYPE_INTEGER] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.integer), + [VIRTIO_SND_CTL_TYPE_INTEGER64] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.integer64), + [VIRTIO_SND_CTL_TYPE_ENUMERATED] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.enumerated), + [VIRTIO_SND_CTL_TYPE_BYTES] = + ARRAY_SIZE(((struct virtio_snd_ctl_value *)0)->value.bytes), + [VIRTIO_SND_CTL_TYPE_IEC958] = 1 +}; + /* Map for converting VirtIO access rights to ALSA access rights. */ static const unsigned int g_v2a_access_map[] = { [VIRTIO_SND_CTL_ACCESS_READ] = SNDRV_CTL_ELEM_ACCESS_READ, @@ -36,6 +51,37 @@ static const unsigned int g_v2a_mask_map[] = { [VIRTIO_SND_CTL_EVT_MASK_TLV] = SNDRV_CTL_EVENT_MASK_TLV }; +static int virtsnd_kctl_validate_info(struct virtio_snd *snd, u32 cid, + struct virtio_snd_ctl_info *kinfo) +{ + struct virtio_device *vdev = snd->vdev; + unsigned int type = le32_to_cpu(kinfo->type); + unsigned int count = le32_to_cpu(kinfo->count); + + if (type >= ARRAY_SIZE(g_v2a_type_map)) { + dev_err(&vdev->dev, "control #%u: unknown type %u\n", + cid, type); + return -EINVAL; + } + + if (count > g_v2a_count_map[type] || + (type == VIRTIO_SND_CTL_TYPE_IEC958 && count != 1)) { + dev_err(&vdev->dev, "control #%u: invalid count %u for type %u\n", + cid, count, type); + return -EINVAL; + } + + if (type == VIRTIO_SND_CTL_TYPE_ENUMERATED && + !le32_to_cpu(kinfo->value.enumerated.items)) { + dev_err(&vdev->dev, + "control #%u: no items for enumerated control\n", + cid); + return -EINVAL; + } + + return 0; +} + /** * virtsnd_kctl_info() - Returns information about the control. * @kcontrol: ALSA control element. @@ -385,6 +431,10 @@ int virtsnd_kctl_parse_cfg(struct virtio_snd *snd) struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[i]; unsigned int type = le32_to_cpu(kinfo->type); + rc = virtsnd_kctl_validate_info(snd, i, kinfo); + if (rc) + return rc; + if (type == VIRTIO_SND_CTL_TYPE_ENUMERATED) { rc = virtsnd_kctl_get_enum_items(snd, i); if (rc) diff --git a/uki-addons.sbat b/uki-addons.sbat index e252a28bf3..ce18ca5a4b 100644 --- a/uki-addons.sbat +++ b/uki-addons.sbat @@ -1,2 +1,2 @@ sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md -kernel-uki-virt-addons.almalinux,1,AlmaLinux,kernel-uki-virt-addons,6.12.0-211.49.1.el10.x86_64,mailto:security@almalinux.org +kernel-uki-virt-addons.almalinux,1,AlmaLinux,kernel-uki-virt-addons,6.12.0-211.50.1.el10.x86_64,mailto:security@almalinux.org diff --git a/uki.sbat b/uki.sbat index c405c8300e..dd7a44436f 100644 --- a/uki.sbat +++ b/uki.sbat @@ -1,2 +1,2 @@ sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md -kernel-uki-virt.almalinux,1,AlmaLinux,kernel-uki-virt,6.12.0-211.49.1.el10.x86_64,mailto:security@almalinux.org +kernel-uki-virt.almalinux,1,AlmaLinux,kernel-uki-virt,6.12.0-211.50.1.el10.x86_64,mailto:security@almalinux.org