From 7dafcd696c0828932d4cff81d26fae0a7b91fed3 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 9 Apr 2026 09:11:39 +0000 Subject: [PATCH 1/5] riscv: errata: Fix bitwise vs logical AND in MIPS errata patching The condition checking whether a specific errata needs patching uses logical AND (&&) instead of bitwise AND (&). Since logical AND only checks that both operands are non-zero, this causes all errata patches to be applied whenever any single errata is detected, rather than only applying the matching one. The SiFive errata implementation correctly uses bitwise AND for the same check. Fixes: 0b0ca959d2 ("riscv: errata: Fix the PAUSE Opcode for MIPS P8700") Signed-off-by: Michael Neuling Assisted-by: Cursor:claude-4.6-opus-high-thinking Signed-off-by: Linux RISC-V bot --- arch/riscv/errata/mips/errata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/errata/mips/errata.c b/arch/riscv/errata/mips/errata.c index e984a8152208c3..2c3dc2259e93e9 100644 --- a/arch/riscv/errata/mips/errata.c +++ b/arch/riscv/errata/mips/errata.c @@ -57,7 +57,7 @@ void mips_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, } tmp = (1U << alt->patch_id); - if (cpu_req_errata && tmp) { + if (cpu_req_errata & tmp) { mutex_lock(&text_mutex); patch_text_nosync(ALT_OLD_PTR(alt), ALT_ALT_PTR(alt), alt->alt_len); From f675f55462721772806a422b580fb55b0c05f806 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 9 Apr 2026 09:11:40 +0000 Subject: [PATCH 2/5] riscv: ptrace: Fix register corruption in compat_riscv_gpr_set on error compat_riscv_gpr_set() calls cregs_to_regs() unconditionally, even when user_regset_copyin() fails. Since cregs is an uninitialized stack variable, a copyin failure causes uninitialized stack data to be written into the target task's pt_regs, corrupting its register state and potentially leaking kernel stack contents. Only call cregs_to_regs() when user_regset_copyin() succeeds. Fixes: 4608c15959 ("riscv: compat: ptrace: Add compat_arch_ptrace implement") Signed-off-by: Michael Neuling Assisted-by: Cursor:claude-4.6-opus-high-thinking Signed-off-by: Linux RISC-V bot --- arch/riscv/kernel/ptrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 93de2e7a30747d..793bcee4618282 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -577,8 +577,8 @@ static int compat_riscv_gpr_set(struct task_struct *target, struct compat_user_regs_struct cregs; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &cregs, 0, -1); - - cregs_to_regs(&cregs, task_pt_regs(target)); + if (!ret) + cregs_to_regs(&cregs, task_pt_regs(target)); return ret; } From f27e58caa7b30eff9d7904eb90fbb7635f7be077 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 9 Apr 2026 09:11:41 +0000 Subject: [PATCH 3/5] riscv: mm: Fix NULL pointer dereference in __set_memory find_vm_area() can return NULL if no vm_struct covers the given address. The code immediately dereferences area->addr without a NULL check. While is_vmalloc_or_module_addr() confirms the address falls within the vmalloc/module address range, it does not guarantee the address belongs to an active allocation, so find_vm_area() may still return NULL. Add the missing NULL check. Fixes: 311cd2f6e2 ("riscv: Fix set_memory_XX() and set_direct_map_XX() by splitting huge linear mappings") Signed-off-by: Michael Neuling Assisted-by: Cursor:claude-4.6-opus-high-thinking Signed-off-by: Linux RISC-V bot --- arch/riscv/mm/pageattr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c index 3f76db3d276992..46a999c86b26e6 100644 --- a/arch/riscv/mm/pageattr.c +++ b/arch/riscv/mm/pageattr.c @@ -289,6 +289,10 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, int i, page_start; area = find_vm_area((void *)start); + if (!area) { + ret = -EINVAL; + goto unlock; + } page_start = (start - (unsigned long)area->addr) >> PAGE_SHIFT; for (i = page_start; i < page_start + numpages; ++i) { From 4b5ced257037e37343ce5b83a07714bb2230981d Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 9 Apr 2026 09:11:42 +0000 Subject: [PATCH 4/5] riscv: mm: Fix NULL dereferences in napot hugetlb functions huge_pte_offset() can return NULL when any level of the page table walk encounters a non-present entry. Both huge_ptep_set_access_flags() and huge_ptep_set_wrprotect() re-derive ptep via huge_pte_offset() in the napot path but use the result without a NULL check, leading to NULL pointer dereferences in get_clear_contig_flush() and set_pte_at(). Add NULL checks after huge_pte_offset() in both functions. Fixes: 82a1a1f3bf ("riscv: mm: support Svnapot in hugetlb page") Signed-off-by: Michael Neuling Assisted-by: Cursor:claude-4.6-opus-high-thinking Signed-off-by: Linux RISC-V bot --- arch/riscv/mm/hugetlbpage.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/mm/hugetlbpage.c b/arch/riscv/mm/hugetlbpage.c index a6d217112cf465..7d155341cffa46 100644 --- a/arch/riscv/mm/hugetlbpage.c +++ b/arch/riscv/mm/hugetlbpage.c @@ -288,6 +288,8 @@ int huge_ptep_set_access_flags(struct vm_area_struct *vma, order = napot_cont_order(pte); pte_num = napot_pte_num(order); ptep = huge_pte_offset(mm, addr, napot_cont_size(order)); + if (!ptep) + return 0; orig_pte = get_clear_contig_flush(mm, addr, ptep, pte_num); if (pte_dirty(orig_pte)) @@ -335,6 +337,8 @@ void huge_ptep_set_wrprotect(struct mm_struct *mm, order = napot_cont_order(pte); pte_num = napot_pte_num(order); ptep = huge_pte_offset(mm, addr, napot_cont_size(order)); + if (!ptep) + return; orig_pte = get_clear_contig_flush(mm, addr, ptep, pte_num); orig_pte = pte_wrprotect(orig_pte); From 6efb04b58714fe37870ad5242cf9edfefac4e6c4 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 9 Apr 2026 09:11:43 +0000 Subject: [PATCH 5/5] riscv: mm: Fix TOCTOU race in remove_pte_mapping remove_pte_mapping() reads the PTE via ptep_get() (a READ_ONCE) into a local variable, but then checks pte_present(*ptep) by dereferencing the pointer directly, reading the PTE a second time. If another CPU modifies the PTE between the two reads, pte_present may check a different value than what was captured, and the subsequent pte_page() could derive the wrong page to free. Use the already-captured local pte variable for the pte_present check. Fixes: c75a74f4ba ("riscv: mm: Add memory hotplugging support") Signed-off-by: Michael Neuling Assisted-by: Cursor:claude-4.6-opus-high-thinking Signed-off-by: Linux RISC-V bot --- arch/riscv/mm/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 811e03786c560f..0369e902f2c0de 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1660,7 +1660,7 @@ static void __meminit remove_pte_mapping(pte_t *pte_base, unsigned long addr, un ptep = pte_base + pte_index(addr); pte = ptep_get(ptep); - if (!pte_present(*ptep)) + if (!pte_present(pte)) continue; pte_clear(&init_mm, addr, ptep);