ANDROID: mm: handle SPF using a copy of the original vma

When introducing vma refcounting to stabilize the faulting vma, the
change also stopped copying the original vma. While refcounting
protects the vma from being destroyed, it does not protect it from
concurrent changes. This results in possible unexpected vma mutations
while page fault is being handled. To prevent that, revert back to
handling speculative page faults using a stable copy of the original
vma which can't be concurrently changed.

Fixes: d9d7f843da ("ANDROID: mm: introduce vma refcounting to protect vma during SPF")
Bug: 271817185
Change-Id: If4ec5a6282ddc037c96addb19510cc83ccd7b2ea
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
This commit is contained in:
Suren Baghdasaryan
2023-03-17 13:01:45 -07:00
parent 521f3bc70d
commit 7ba7908a8d
3 changed files with 13 additions and 10 deletions

View File

@@ -543,6 +543,7 @@ static int __kprobes do_page_fault(unsigned long far, unsigned int esr,
unsigned long addr = untagged_addr(far);
#ifdef CONFIG_SPECULATIVE_PAGE_FAULT
struct vm_area_struct *vma;
struct vm_area_struct pvma;
unsigned long seq;
#endif
@@ -626,17 +627,17 @@ static int __kprobes do_page_fault(unsigned long far, unsigned int esr,
count_vm_spf_event(SPF_ABORT_NO_SPECULATE);
goto spf_abort;
}
pvma = *vma;
if (!mmap_seq_read_check(mm, seq, SPF_ABORT_VMA_COPY)) {
put_vma(vma);
goto spf_abort;
}
if (!(vma->vm_flags & vm_flags)) {
if (!(pvma.vm_flags & vm_flags)) {
put_vma(vma);
count_vm_spf_event(SPF_ABORT_ACCESS_ERROR);
goto spf_abort;
}
fault = do_handle_mm_fault(vma, addr & PAGE_MASK,
fault = do_handle_mm_fault(&pvma, addr & PAGE_MASK,
mm_flags | FAULT_FLAG_SPECULATIVE, seq, regs);
put_vma(vma);

View File

@@ -395,6 +395,7 @@ static int ___do_page_fault(struct pt_regs *regs, unsigned long address,
vm_fault_t fault, major = 0;
bool kprobe_fault = kprobe_page_fault(regs, 11);
#ifdef CONFIG_SPECULATIVE_PAGE_FAULT
struct vm_area_struct pvma;
unsigned long seq;
#endif
@@ -477,25 +478,25 @@ static int ___do_page_fault(struct pt_regs *regs, unsigned long address,
count_vm_spf_event(SPF_ABORT_NO_SPECULATE);
goto spf_abort;
}
pvma = *vma;
if (!mmap_seq_read_check(mm, seq, SPF_ABORT_VMA_COPY)) {
put_vma(vma);
goto spf_abort;
}
#ifdef CONFIG_PPC_MEM_KEYS
if (unlikely(access_pkey_error(is_write, is_exec,
(error_code & DSISR_KEYFAULT), vma))) {
(error_code & DSISR_KEYFAULT), &pvma))) {
put_vma(vma);
count_vm_spf_event(SPF_ABORT_ACCESS_ERROR);
goto spf_abort;
}
#endif /* CONFIG_PPC_MEM_KEYS */
if (unlikely(access_error(is_write, is_exec, vma))) {
if (unlikely(access_error(is_write, is_exec, &pvma))) {
put_vma(vma);
count_vm_spf_event(SPF_ABORT_ACCESS_ERROR);
goto spf_abort;
}
fault = do_handle_mm_fault(vma, address,
fault = do_handle_mm_fault(&pvma, address,
flags | FAULT_FLAG_SPECULATIVE, seq, regs);
put_vma(vma);
major |= fault & VM_FAULT_MAJOR;

View File

@@ -1227,6 +1227,7 @@ void do_user_addr_fault(struct pt_regs *regs,
vm_fault_t fault;
unsigned int flags = FAULT_FLAG_DEFAULT;
#ifdef CONFIG_SPECULATIVE_PAGE_FAULT
struct vm_area_struct pvma;
unsigned long seq;
#endif
@@ -1351,17 +1352,17 @@ void do_user_addr_fault(struct pt_regs *regs,
count_vm_spf_event(SPF_ABORT_NO_SPECULATE);
goto spf_abort;
}
pvma = *vma;
if (!mmap_seq_read_check(mm, seq, SPF_ABORT_VMA_COPY)) {
put_vma(vma);
goto spf_abort;
}
if (unlikely(access_error(error_code, vma))) {
if (unlikely(access_error(error_code, &pvma))) {
put_vma(vma);
count_vm_spf_event(SPF_ABORT_ACCESS_ERROR);
goto spf_abort;
}
fault = do_handle_mm_fault(vma, address,
fault = do_handle_mm_fault(&pvma, address,
flags | FAULT_FLAG_SPECULATIVE, seq, regs);
put_vma(vma);