Skip to content

Commit 22f3a4f

Browse files
kevin-brodsky-armwilldeacon
authored andcommitted
arm64: poe: Handle spurious Overlay faults
We do not currently issue an ISB after updating POR_EL0 when context-switching it, for instance. The rationale is that if the old value of POR_EL0 is more restrictive and causes a fault during uaccess, the access will be retried [1]. In other words, we are trading an ISB on every context-switching for the (unlikely) possibility of a spurious fault. We may also miss faults if the new value of POR_EL0 is more restrictive, but that's considered acceptable. However, as things stand, a spurious Overlay fault results in uaccess failing right away since it causes fault_from_pkey() to return true. If an Overlay fault is reported, we therefore need to double check POR_EL0 against vma_pkey(vma) - this is what arch_vma_access_permitted() already does. As it turns out, we already perform that explicit check if no Overlay fault is reported, and we need to keep that check (see comment added in fault_from_pkey()). Net result: the Overlay ISS2 bit isn't of much help to decide whether a pkey fault occurred. Remove the check for the Overlay bit from fault_from_pkey() and add a comment to try and explain the situation. While at it, also add a comment to permission_overlay_switch() in case anyone gets surprised by the lack of ISB. [1] https://lore.kernel.org/linux-arm-kernel/[email protected]/ Fixes: 160a8e1 ("arm64: context switch POR_EL0 register") Signed-off-by: Kevin Brodsky <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent a75ad2f commit 22f3a4f

File tree

2 files changed

+26
-9
lines changed

2 files changed

+26
-9
lines changed

arch/arm64/kernel/process.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,11 @@ static void permission_overlay_switch(struct task_struct *next)
673673
current->thread.por_el0 = read_sysreg_s(SYS_POR_EL0);
674674
if (current->thread.por_el0 != next->thread.por_el0) {
675675
write_sysreg_s(next->thread.por_el0, SYS_POR_EL0);
676+
/*
677+
* No ISB required as we can tolerate spurious Overlay faults -
678+
* the fault handler will check again based on the new value
679+
* of POR_EL0.
680+
*/
676681
}
677682
}
678683

arch/arm64/mm/fault.c

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -487,17 +487,29 @@ static void do_bad_area(unsigned long far, unsigned long esr,
487487
}
488488
}
489489

490-
static bool fault_from_pkey(unsigned long esr, struct vm_area_struct *vma,
491-
unsigned int mm_flags)
490+
static bool fault_from_pkey(struct vm_area_struct *vma, unsigned int mm_flags)
492491
{
493-
unsigned long iss2 = ESR_ELx_ISS2(esr);
494-
495492
if (!system_supports_poe())
496493
return false;
497494

498-
if (esr_fsc_is_permission_fault(esr) && (iss2 & ESR_ELx_Overlay))
499-
return true;
500-
495+
/*
496+
* We do not check whether an Overlay fault has occurred because we
497+
* cannot make a decision based solely on its value:
498+
*
499+
* - If Overlay is set, a fault did occur due to POE, but it may be
500+
* spurious in those cases where we update POR_EL0 without ISB (e.g.
501+
* on context-switch). We would then need to manually check POR_EL0
502+
* against vma_pkey(vma), which is exactly what
503+
* arch_vma_access_permitted() does.
504+
*
505+
* - If Overlay is not set, we may still need to report a pkey fault.
506+
* This is the case if an access was made within a mapping but with no
507+
* page mapped, and POR_EL0 forbids the access (according to
508+
* vma_pkey()). Such access will result in a SIGSEGV regardless
509+
* because core code checks arch_vma_access_permitted(), but in order
510+
* to report the correct error code - SEGV_PKUERR - we must handle
511+
* that case here.
512+
*/
501513
return !arch_vma_access_permitted(vma,
502514
mm_flags & FAULT_FLAG_WRITE,
503515
mm_flags & FAULT_FLAG_INSTRUCTION,
@@ -635,7 +647,7 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
635647
goto bad_area;
636648
}
637649

638-
if (fault_from_pkey(esr, vma, mm_flags)) {
650+
if (fault_from_pkey(vma, mm_flags)) {
639651
pkey = vma_pkey(vma);
640652
vma_end_read(vma);
641653
fault = 0;
@@ -679,7 +691,7 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
679691
goto bad_area;
680692
}
681693

682-
if (fault_from_pkey(esr, vma, mm_flags)) {
694+
if (fault_from_pkey(vma, mm_flags)) {
683695
pkey = vma_pkey(vma);
684696
mmap_read_unlock(mm);
685697
fault = 0;

0 commit comments

Comments
 (0)