Commit 99cd1302 authored by Ram Pai's avatar Ram Pai Committed by Michael Ellerman

powerpc: Deliver SEGV signal on pkey violation

The value of the pkey, whose protection got violated,
is made available in si_pkey field of the siginfo structure.
Signed-off-by: default avatarRam Pai <linuxram@us.ibm.com>
Signed-off-by: default avatarThiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 087003e9
...@@ -133,6 +133,7 @@ struct pt_regs; ...@@ -133,6 +133,7 @@ struct pt_regs;
extern int do_page_fault(struct pt_regs *, unsigned long, unsigned long); extern int do_page_fault(struct pt_regs *, unsigned long, unsigned long);
extern void bad_page_fault(struct pt_regs *, unsigned long, int); extern void bad_page_fault(struct pt_regs *, unsigned long, int);
extern void _exception(int, struct pt_regs *, int, unsigned long); extern void _exception(int, struct pt_regs *, int, unsigned long);
extern void _exception_pkey(int, struct pt_regs *, int, unsigned long, int);
extern void die(const char *, struct pt_regs *, long); extern void die(const char *, struct pt_regs *, long);
extern bool die_will_crash(void); extern bool die_will_crash(void);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/sched/debug.h> #include <linux/sched/debug.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/pkeys.h>
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
...@@ -266,7 +267,9 @@ void user_single_step_siginfo(struct task_struct *tsk, ...@@ -266,7 +267,9 @@ void user_single_step_siginfo(struct task_struct *tsk,
info->si_addr = (void __user *)regs->nip; info->si_addr = (void __user *)regs->nip;
} }
void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
void _exception_pkey(int signr, struct pt_regs *regs, int code,
unsigned long addr, int key)
{ {
siginfo_t info; siginfo_t info;
const char fmt32[] = KERN_INFO "%s[%d]: unhandled signal %d " \ const char fmt32[] = KERN_INFO "%s[%d]: unhandled signal %d " \
...@@ -293,9 +296,16 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) ...@@ -293,9 +296,16 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
info.si_signo = signr; info.si_signo = signr;
info.si_code = code; info.si_code = code;
info.si_addr = (void __user *) addr; info.si_addr = (void __user *) addr;
info.si_pkey = key;
force_sig_info(signr, &info, current); force_sig_info(signr, &info, current);
} }
void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
{
_exception_pkey(signr, regs, code, addr, 0);
}
void system_reset_exception(struct pt_regs *regs) void system_reset_exception(struct pt_regs *regs)
{ {
/* /*
......
...@@ -107,7 +107,8 @@ static bool store_updates_sp(struct pt_regs *regs) ...@@ -107,7 +107,8 @@ static bool store_updates_sp(struct pt_regs *regs)
*/ */
static int static int
__bad_area_nosemaphore(struct pt_regs *regs, unsigned long address, int si_code) __bad_area_nosemaphore(struct pt_regs *regs, unsigned long address, int si_code,
int pkey)
{ {
/* /*
* If we are in kernel mode, bail out with a SEGV, this will * If we are in kernel mode, bail out with a SEGV, this will
...@@ -117,17 +118,18 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long address, int si_code) ...@@ -117,17 +118,18 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long address, int si_code)
if (!user_mode(regs)) if (!user_mode(regs))
return SIGSEGV; return SIGSEGV;
_exception(SIGSEGV, regs, si_code, address); _exception_pkey(SIGSEGV, regs, si_code, address, pkey);
return 0; return 0;
} }
static noinline int bad_area_nosemaphore(struct pt_regs *regs, unsigned long address) static noinline int bad_area_nosemaphore(struct pt_regs *regs, unsigned long address)
{ {
return __bad_area_nosemaphore(regs, address, SEGV_MAPERR); return __bad_area_nosemaphore(regs, address, SEGV_MAPERR, 0);
} }
static int __bad_area(struct pt_regs *regs, unsigned long address, int si_code) static int __bad_area(struct pt_regs *regs, unsigned long address, int si_code,
int pkey)
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
...@@ -137,12 +139,18 @@ static int __bad_area(struct pt_regs *regs, unsigned long address, int si_code) ...@@ -137,12 +139,18 @@ static int __bad_area(struct pt_regs *regs, unsigned long address, int si_code)
*/ */
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
return __bad_area_nosemaphore(regs, address, si_code); return __bad_area_nosemaphore(regs, address, si_code, pkey);
} }
static noinline int bad_area(struct pt_regs *regs, unsigned long address) static noinline int bad_area(struct pt_regs *regs, unsigned long address)
{ {
return __bad_area(regs, address, SEGV_MAPERR); return __bad_area(regs, address, SEGV_MAPERR, 0);
}
static int bad_key_fault_exception(struct pt_regs *regs, unsigned long address,
int pkey)
{
return __bad_area_nosemaphore(regs, address, SEGV_PKUERR, pkey);
} }
static int do_sigbus(struct pt_regs *regs, unsigned long address, static int do_sigbus(struct pt_regs *regs, unsigned long address,
...@@ -427,10 +435,9 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address, ...@@ -427,10 +435,9 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address,
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
if (error_code & DSISR_KEYFAULT) { if (error_code & DSISR_KEYFAULT)
_exception(SIGSEGV, regs, SEGV_PKUERR, address); return bad_key_fault_exception(regs, address,
return 0; get_mm_addr_key(mm, address));
}
/* /*
* We want to do this outside mmap_sem, because reading code around nip * We want to do this outside mmap_sem, because reading code around nip
...@@ -513,10 +520,18 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address, ...@@ -513,10 +520,18 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address,
if (unlikely(fault & VM_FAULT_SIGSEGV) && if (unlikely(fault & VM_FAULT_SIGSEGV) &&
!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE, !arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
is_exec, 0)) { is_exec, 0)) {
/*
* The PGD-PDT...PMD-PTE tree may not have been fully setup.
* Hence we cannot walk the tree to locate the PTE, to locate
* the key. Hence let's use vma_pkey() to get the key; instead
* of get_mm_addr_key().
*/
int pkey = vma_pkey(vma); int pkey = vma_pkey(vma);
if (likely(pkey)) if (likely(pkey)) {
return __bad_area(regs, address, SEGV_PKUERR); up_read(&mm->mmap_sem);
return bad_key_fault_exception(regs, address, pkey);
}
} }
#endif /* CONFIG_PPC_MEM_KEYS */ #endif /* CONFIG_PPC_MEM_KEYS */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment