Skip to content

Commit fe4a685

Browse files
kvaneeshmpe
authored andcommitted
powerpc/pkeys: Avoid using lockless page table walk
Fetch pkey from vma instead of linux page table. Also document the fact that in some cases the pkey returned in siginfo won't be the same as the one we took keyfault on. Even with linux page table walk, we can end up in a similar scenario. Signed-off-by: Aneesh Kumar K.V <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 93a9869 commit fe4a685

File tree

3 files changed

+60
-56
lines changed

3 files changed

+60
-56
lines changed

arch/powerpc/include/asm/mmu.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -291,15 +291,6 @@ static inline bool early_radix_enabled(void)
291291
}
292292
#endif
293293

294-
#ifdef CONFIG_PPC_MEM_KEYS
295-
extern u16 get_mm_addr_key(struct mm_struct *mm, unsigned long address);
296-
#else
297-
static inline u16 get_mm_addr_key(struct mm_struct *mm, unsigned long address)
298-
{
299-
return 0;
300-
}
301-
#endif /* CONFIG_PPC_MEM_KEYS */
302-
303294
#ifdef CONFIG_STRICT_KERNEL_RWX
304295
static inline bool strict_kernel_rwx_enabled(void)
305296
{

arch/powerpc/mm/book3s64/hash_utils.c

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,30 +1671,6 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
16711671
hash_preload(vma->vm_mm, address, is_exec, trap);
16721672
}
16731673

1674-
#ifdef CONFIG_PPC_MEM_KEYS
1675-
/*
1676-
* Return the protection key associated with the given address and the
1677-
* mm_struct.
1678-
*/
1679-
u16 get_mm_addr_key(struct mm_struct *mm, unsigned long address)
1680-
{
1681-
pte_t *ptep;
1682-
u16 pkey = 0;
1683-
unsigned long flags;
1684-
1685-
if (!mm || !mm->pgd)
1686-
return 0;
1687-
1688-
local_irq_save(flags);
1689-
ptep = find_linux_pte(mm->pgd, address, NULL, NULL);
1690-
if (ptep)
1691-
pkey = pte_to_pkey_bits(pte_val(READ_ONCE(*ptep)));
1692-
local_irq_restore(flags);
1693-
1694-
return pkey;
1695-
}
1696-
#endif /* CONFIG_PPC_MEM_KEYS */
1697-
16981674
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
16991675
static inline void tm_flush_hash_page(int local)
17001676
{

arch/powerpc/mm/fault.c

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,34 @@ static noinline int bad_area(struct pt_regs *regs, unsigned long address)
118118
return __bad_area(regs, address, SEGV_MAPERR);
119119
}
120120

121-
static int bad_key_fault_exception(struct pt_regs *regs, unsigned long address,
122-
int pkey)
121+
#ifdef CONFIG_PPC_MEM_KEYS
122+
static noinline int bad_access_pkey(struct pt_regs *regs, unsigned long address,
123+
struct vm_area_struct *vma)
123124
{
125+
struct mm_struct *mm = current->mm;
126+
int pkey;
127+
128+
/*
129+
* We don't try to fetch the pkey from page table because reading
130+
* page table without locking doesn't guarantee stable pte value.
131+
* Hence the pkey value that we return to userspace can be different
132+
* from the pkey that actually caused access error.
133+
*
134+
* It does *not* guarantee that the VMA we find here
135+
* was the one that we faulted on.
136+
*
137+
* 1. T1 : mprotect_key(foo, PAGE_SIZE, pkey=4);
138+
* 2. T1 : set AMR to deny access to pkey=4, touches, page
139+
* 3. T1 : faults...
140+
* 4. T2: mprotect_key(foo, PAGE_SIZE, pkey=5);
141+
* 5. T1 : enters fault handler, takes mmap_sem, etc...
142+
* 6. T1 : reaches here, sees vma_pkey(vma)=5, when we really
143+
* faulted on a pte with its pkey=4.
144+
*/
145+
pkey = vma_pkey(vma);
146+
147+
up_read(&mm->mmap_sem);
148+
124149
/*
125150
* If we are in kernel mode, bail out with a SEGV, this will
126151
* be caught by the assembly which will restore the non-volatile
@@ -133,6 +158,7 @@ static int bad_key_fault_exception(struct pt_regs *regs, unsigned long address,
133158

134159
return 0;
135160
}
161+
#endif
136162

137163
static noinline int bad_access(struct pt_regs *regs, unsigned long address)
138164
{
@@ -289,8 +315,31 @@ static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address,
289315
return false;
290316
}
291317

292-
static bool access_error(bool is_write, bool is_exec,
293-
struct vm_area_struct *vma)
318+
#ifdef CONFIG_PPC_MEM_KEYS
319+
static bool access_pkey_error(bool is_write, bool is_exec, bool is_pkey,
320+
struct vm_area_struct *vma)
321+
{
322+
/*
323+
* Read or write was blocked by protection keys. This is
324+
* always an unconditional error and can never result in
325+
* a follow-up action to resolve the fault, like a COW.
326+
*/
327+
if (is_pkey)
328+
return true;
329+
330+
/*
331+
* Make sure to check the VMA so that we do not perform
332+
* faults just to hit a pkey fault as soon as we fill in a
333+
* page. Only called for current mm, hence foreign == 0
334+
*/
335+
if (!arch_vma_access_permitted(vma, is_write, is_exec, 0))
336+
return true;
337+
338+
return false;
339+
}
340+
#endif
341+
342+
static bool access_error(bool is_write, bool is_exec, struct vm_area_struct *vma)
294343
{
295344
/*
296345
* Allow execution from readable areas if the MMU does not
@@ -483,10 +532,6 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address,
483532

484533
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
485534

486-
if (error_code & DSISR_KEYFAULT)
487-
return bad_key_fault_exception(regs, address,
488-
get_mm_addr_key(mm, address));
489-
490535
/*
491536
* We want to do this outside mmap_sem, because reading code around nip
492537
* can result in fault, which will cause a deadlock when called with
@@ -555,6 +600,13 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address,
555600
return bad_area(regs, address);
556601

557602
good_area:
603+
604+
#ifdef CONFIG_PPC_MEM_KEYS
605+
if (unlikely(access_pkey_error(is_write, is_exec,
606+
(error_code & DSISR_KEYFAULT), vma)))
607+
return bad_access_pkey(regs, address, vma);
608+
#endif /* CONFIG_PPC_MEM_KEYS */
609+
558610
if (unlikely(access_error(is_write, is_exec, vma)))
559611
return bad_access(regs, address);
560612

@@ -565,21 +617,6 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address,
565617
*/
566618
fault = handle_mm_fault(vma, address, flags);
567619

568-
#ifdef CONFIG_PPC_MEM_KEYS
569-
/*
570-
* we skipped checking for access error due to key earlier.
571-
* Check that using handle_mm_fault error return.
572-
*/
573-
if (unlikely(fault & VM_FAULT_SIGSEGV) &&
574-
!arch_vma_access_permitted(vma, is_write, is_exec, 0)) {
575-
576-
int pkey = vma_pkey(vma);
577-
578-
up_read(&mm->mmap_sem);
579-
return bad_key_fault_exception(regs, address, pkey);
580-
}
581-
#endif /* CONFIG_PPC_MEM_KEYS */
582-
583620
major |= fault & VM_FAULT_MAJOR;
584621

585622
if (fault_signal_pending(fault, regs))

0 commit comments

Comments
 (0)