diff options
| author | Mark Johnston <markj@FreeBSD.org> | 2026-03-31 13:37:43 +0000 |
|---|---|---|
| committer | Mark Johnston <markj@FreeBSD.org> | 2026-04-21 15:45:06 +0000 |
| commit | 5787df30dc3ee1d8389f2fcfef324d0f6e086779 (patch) | |
| tree | 01b54272dbb79e141c926f8d2af535d314a5666e /sys | |
| parent | af294329c57f9bb9874411bc603f4c46a85c68e9 (diff) | |
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/amd64/amd64/pmap.c | 20 | ||||
| -rw-r--r-- | sys/amd64/amd64/sys_machdep.c | 43 | ||||
| -rw-r--r-- | sys/vm/vm_map.c | 32 | ||||
| -rw-r--r-- | sys/vm/vm_map.h | 1 |
4 files changed, 84 insertions, 12 deletions
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c index 9fd6c4091c95..bae3735eeafe 100644 --- a/sys/amd64/amd64/pmap.c +++ b/sys/amd64/amd64/pmap.c @@ -11598,7 +11598,7 @@ pmap_pkru_update_range(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, u_int keyidx) { pml4_entry_t *pml4e; - pdp_entry_t *pdpe; + pdp_entry_t newpdpe, *pdpe; pd_entry_t newpde, ptpaddr, *pde; pt_entry_t newpte, *ptep, pte; vm_offset_t va, va_next; @@ -11624,6 +11624,22 @@ pmap_pkru_update_range(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, va_next = eva; continue; } + if ((*pdpe & PG_PS) != 0) { + va_next = (va + NBPDP) & ~PDPMASK; + if (va_next < va) + va_next = eva; + KASSERT(va_next <= eva, + ("partial update of non-transparent 1G mapping " + "pdpe %#lx va %#lx eva %#lx va_next %#lx", + *pdpe, va, eva, va_next)); + newpdpe = (*pdpe & ~X86_PG_PKU_MASK) | + X86_PG_PKU(keyidx); + if (newpdpe != *pdpe) { + *pdpe = newpdpe; + changed = true; + } + continue; + } va_next = (va + NBPDR) & ~PDRMASK; if (va_next < va) @@ -11676,8 +11692,6 @@ pmap_pkru_check_uargs(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, if (pmap->pm_type != PT_X86 || keyidx > PMAP_MAX_PKRU_IDX || (flags & ~(AMD64_PKRU_PERSIST | AMD64_PKRU_EXCL)) != 0) return (EINVAL); - if (eva <= sva || eva > VM_MAXUSER_ADDRESS) - return (EFAULT); if ((cpu_stdext_feature2 & CPUID_STDEXT2_PKU) == 0) return (ENOTSUP); return (0); diff --git a/sys/amd64/amd64/sys_machdep.c b/sys/amd64/amd64/sys_machdep.c index a7ee1f49887c..74b182e73dc2 100644 --- a/sys/amd64/amd64/sys_machdep.c +++ b/sys/amd64/amd64/sys_machdep.c @@ -32,7 +32,6 @@ * from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91 */ -#include <sys/cdefs.h> #include "opt_capsicum.h" #include "opt_ktrace.h" @@ -371,32 +370,58 @@ sysarch(struct thread *td, struct sysarch_args *uap) break; case I386_SET_PKRU: - case AMD64_SET_PKRU: + case AMD64_SET_PKRU: { + vm_offset_t addr, start, end; + vm_size_t len; + + addr = (uintptr_t)a64pkru.addr; + len = a64pkru.len; + /* * Read-lock the map to synchronize with parallel * pmap_vmspace_copy() on fork. */ map = &td->td_proc->p_vmspace->vm_map; vm_map_lock_read(map); - error = pmap_pkru_set(PCPU_GET(curpmap), - (vm_offset_t)a64pkru.addr, (vm_offset_t)a64pkru.addr + - a64pkru.len, a64pkru.keyidx, a64pkru.flags); + if (len == 0 || !vm_map_check_boundary(map, addr, addr + len)) { + vm_map_unlock_read(map); + error = EINVAL; + break; + } + start = trunc_page(addr); + end = round_page(addr + len); + error = pmap_pkru_set(PCPU_GET(curpmap), start, end, + a64pkru.keyidx, a64pkru.flags); vm_map_unlock_read(map); break; + } case I386_CLEAR_PKRU: - case AMD64_CLEAR_PKRU: + case AMD64_CLEAR_PKRU: { + vm_offset_t addr, start, end; + vm_size_t len; + if (a64pkru.flags != 0 || a64pkru.keyidx != 0) { error = EINVAL; break; } + + addr = (uintptr_t)a64pkru.addr; + len = a64pkru.len; + map = &td->td_proc->p_vmspace->vm_map; vm_map_lock_read(map); - error = pmap_pkru_clear(PCPU_GET(curpmap), - (vm_offset_t)a64pkru.addr, - (vm_offset_t)a64pkru.addr + a64pkru.len); + if (len == 0 || !vm_map_check_boundary(map, addr, addr + len)) { + vm_map_unlock_read(map); + error = EINVAL; + break; + } + start = trunc_page(addr); + end = round_page(addr + len); + error = pmap_pkru_clear(PCPU_GET(curpmap), start, end); vm_map_unlock_read(map); break; + } case AMD64_DISABLE_TLSBASE: clear_pcb_flags(pcb, PCB_TLSBASE); diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index cf028c0c47ef..631a5e302879 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -4141,6 +4141,38 @@ vm_map_check_protection(vm_map_t map, vm_offset_t start, vm_offset_t end, } /* + * Check whether the specified range partially overlaps a map entry with + * fixed boundaries, and return false if so. + * + * The map must be locked. + */ +bool +vm_map_check_boundary(vm_map_t map, vm_offset_t start, vm_offset_t end) +{ + vm_map_entry_t entry; + int bdry_idx; + + if (!vm_map_range_valid(map, start, end)) + return (false); + if (start == end) + return (true); + + if (vm_map_lookup_entry(map, start, &entry)) { + bdry_idx = MAP_ENTRY_SPLIT_BOUNDARY_INDEX(entry); + if (bdry_idx != 0 && + (start & (pagesizes[bdry_idx] - 1)) != 0) + return (false); + } + if (vm_map_lookup_entry(map, end - 1, &entry)) { + bdry_idx = MAP_ENTRY_SPLIT_BOUNDARY_INDEX(entry); + if (bdry_idx != 0 && + (end & (pagesizes[bdry_idx] - 1)) != 0) + return (false); + } + return (true); +} + +/* * * vm_map_copy_swap_object: * diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h index 5d41f892e83d..66a787b0d2d8 100644 --- a/sys/vm/vm_map.h +++ b/sys/vm/vm_map.h @@ -471,6 +471,7 @@ vm_map_entry_read_succ(void *token, struct vm_map_entry *const clone, #endif /* ! _KERNEL */ #ifdef _KERNEL +bool vm_map_check_boundary(vm_map_t, vm_offset_t, vm_offset_t); boolean_t vm_map_check_protection (vm_map_t, vm_offset_t, vm_offset_t, vm_prot_t); int vm_map_delete(vm_map_t, vm_offset_t, vm_offset_t); int vm_map_find(vm_map_t, vm_object_t, vm_ooffset_t, vm_offset_t *, vm_size_t, |
