mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
arm64/mm: restrict transparent contpte mappings to valid PTEs
The transparent contpte layer treats PTE_CONT as software grouping state.
When a valid PTE has PTE_CONT set, the contpte helpers may align an
operation to a CONT_PTE_SIZE block and operate on the whole block. The
current single-PTE helpers use pte_valid_cont() to identify such groups;
this is a helper contract, not an architectural restriction on using
PTE_CONT with present-invalid descriptors. Explicit users such as hugetlb
manage their contiguous encodings separately.
contpte_set_ptes() currently sets PTE_CONT from alignment alone. Batched
set_ptes() users can therefore install present-invalid PTEs with
PTE_CONT set, for example mprotect(PROT_NONE), NUMA hinting through
modify_prot_commit_ptes(), fork() copying a PROT_NONE large folio, or a
PMD split installing PAGE_NONE PTEs.
This is not a hardware programming problem: invalid descriptors are not
translated, so the hardware ignores descriptor bits such as PTE_CONT. It
is a software-state problem. Some arm64 fast paths, such as
test_and_clear_young_ptes(), test pte_cont() directly instead of
pte_valid_cont().
In the observed failure, mprotect(PROT_NONE) installed a full
CONT_PTE_SIZE range of present-invalid PTEs with PTE_CONT set.
MADV_GUARD_INSTALL then zapped one PTE. The nr == 1 clear_full_ptes()
path only unfolds pte_valid_cont() entries, so the neighbouring
present-invalid entries kept PTE_CONT while the target entry became a
guard marker.
clear_refs_pte_range() then reached the marker through:
ptep_test_and_clear_young()
test_and_clear_young_ptes()
contpte_test_and_clear_young_ptes()
contpte_align_addr_ptep()
__ptep_test_and_clear_young()
The marker changed from old_pte=0x00000000000047c0 to
new_pte=0x00000000000043c0 by clearing PTE_AF. For swap PTEs, bit 10 is
part of the swap type field, bits 6-10: 0x47c0 is SWP_PTE_MARKER with
offset 4, while 0x43c0 has type 15 and is decoded as an ordinary swap
entry. Zap later warned in swap_put_entries_direct() with a bad swap
entry.
Present-invalid PTEs could retain PTE_CONT as software grouping state,
but their single-PTE modification paths would then need to recognize
and unfold those groups consistently. Since current helpers mostly use
pte_valid_cont() for membership, avoid creating present-invalid
transparent contpte groups in contpte_set_ptes(). They bring no hardware
benefit while invalid, and eligible valid ranges can be grouped again
when they are installed.
The public set_ptes() wrapper already clears PTE_CONT before calling
contpte_set_ptes(). Return invalid PTEs through __set_ptes() directly,
and warn if a future direct caller unexpectedly passes an invalid PTE
with PTE_CONT set.
This is triggerable by a user process against its own address space. On
systems running with panic_on_warn, the warning becomes a denial of
service.
Fixes: 4602e5757b ("arm64/mm: wire up PTE_CONT for user mappings")
Cc: stable@vger.kernel.org
Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
Tested-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Zhao Gongyi <zhaogongyi@bytedance.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
parent
643b640256
commit
486d396b19
|
|
@ -1651,6 +1651,9 @@ extern void modify_prot_commit_ptes(struct vm_area_struct *vma, unsigned long ad
|
|||
* The contpte APIs are used to transparently manage the contiguous bit in ptes
|
||||
* where it is possible and makes sense to do so. The PTE_CONT bit is considered
|
||||
* a private implementation detail of the public ptep API (see below).
|
||||
* Single-PTE transparent contpte helpers use pte_valid_cont() to identify
|
||||
* grouped PTEs; explicit contiguous-encoding users such as hugetlb manage
|
||||
* their entries separately.
|
||||
*/
|
||||
extern void __contpte_try_fold(struct mm_struct *mm, unsigned long addr,
|
||||
pte_t *ptep, pte_t pte);
|
||||
|
|
|
|||
|
|
@ -460,8 +460,10 @@ void contpte_set_ptes(struct mm_struct *mm, unsigned long addr,
|
|||
*/
|
||||
VM_WARN_ON(nr == 1);
|
||||
|
||||
if (!mm_is_user(mm))
|
||||
if (!mm_is_user(mm) || !pte_valid(pte)) {
|
||||
VM_WARN_ON_ONCE(!pte_valid(pte) && pte_cont(pte));
|
||||
return __set_ptes(mm, addr, ptep, pte, nr);
|
||||
}
|
||||
|
||||
end = addr + (nr << PAGE_SHIFT);
|
||||
pfn = pte_pfn(pte);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user