Skip to content

Commit cfad706

Browse files
brooniectmarinas
authored andcommitted
arm64/mm: Handle GCS data aborts
All GCS operations at EL0 must happen on a page which is marked as having UnprivGCS access, including read operations. If a GCS operation attempts to access a page without this then it will generate a data abort with the GCS bit set in ESR_EL1.ISS2. EL0 may validly generate such faults, for example due to copy on write which will cause the GCS data to be stored in a read only page with no GCS permissions until the actual copy happens. Since UnprivGCS allows both reads and writes to the GCS (though only through GCS operations) we need to ensure that the memory management subsystem handles GCS accesses as writes at all times. Do this by adding FAULT_FLAG_WRITE to any GCS page faults, adding handling to ensure that invalid cases are identfied as such early so the memory management core does not think they will succeed. The core cannot distinguish between VMAs which are generally writeable and VMAs which are only writeable through GCS operations. EL1 may validly write to EL0 GCS for management purposes (eg, while initialising with cap tokens). We also report any GCS faults in VMAs not marked as part of a GCS as access violations, causing a fault to be delivered to userspace if it attempts to do GCS operations outside a GCS. Reviewed-by: Thiago Jung Bauermann <[email protected]> Reviewed-by: Catalin Marinas <[email protected]> Signed-off-by: Mark Brown <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Catalin Marinas <[email protected]>
1 parent 8ce71d2 commit cfad706

File tree

1 file changed

+40
-0
lines changed

1 file changed

+40
-0
lines changed

arch/arm64/mm/fault.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,14 @@ static bool fault_from_pkey(unsigned long esr, struct vm_area_struct *vma,
504504
false);
505505
}
506506

507+
static bool is_gcs_fault(unsigned long esr)
508+
{
509+
if (!esr_is_data_abort(esr))
510+
return false;
511+
512+
return ESR_ELx_ISS2(esr) & ESR_ELx_GCS;
513+
}
514+
507515
static bool is_el0_instruction_abort(unsigned long esr)
508516
{
509517
return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_LOW;
@@ -518,6 +526,23 @@ static bool is_write_abort(unsigned long esr)
518526
return (esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM);
519527
}
520528

529+
static bool is_invalid_gcs_access(struct vm_area_struct *vma, u64 esr)
530+
{
531+
if (!system_supports_gcs())
532+
return false;
533+
534+
if (unlikely(is_gcs_fault(esr))) {
535+
/* GCS accesses must be performed on a GCS page */
536+
if (!(vma->vm_flags & VM_SHADOW_STACK))
537+
return true;
538+
} else if (unlikely(vma->vm_flags & VM_SHADOW_STACK)) {
539+
/* Only GCS operations can write to a GCS page */
540+
return esr_is_data_abort(esr) && is_write_abort(esr);
541+
}
542+
543+
return false;
544+
}
545+
521546
static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
522547
struct pt_regs *regs)
523548
{
@@ -554,6 +579,14 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
554579
/* It was exec fault */
555580
vm_flags = VM_EXEC;
556581
mm_flags |= FAULT_FLAG_INSTRUCTION;
582+
} else if (is_gcs_fault(esr)) {
583+
/*
584+
* The GCS permission on a page implies both read and
585+
* write so always handle any GCS fault as a write fault,
586+
* we need to trigger CoW even for GCS reads.
587+
*/
588+
vm_flags = VM_WRITE;
589+
mm_flags |= FAULT_FLAG_WRITE;
557590
} else if (is_write_abort(esr)) {
558591
/* It was write fault */
559592
vm_flags = VM_WRITE;
@@ -587,6 +620,13 @@ static int __kprobes do_page_fault(unsigned long far, unsigned long esr,
587620
if (!vma)
588621
goto lock_mmap;
589622

623+
if (is_invalid_gcs_access(vma, esr)) {
624+
vma_end_read(vma);
625+
fault = 0;
626+
si_code = SEGV_ACCERR;
627+
goto bad_area;
628+
}
629+
590630
if (!(vma->vm_flags & vm_flags)) {
591631
vma_end_read(vma);
592632
fault = 0;

0 commit comments

Comments
 (0)