mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-20 08:38:24 +08:00
[S390] Improve address space check.
A data access in access-register mode always is a user mode access, the code to inspect the access-registers can be removed. The second change is to use a different test to check for no-execute fault. The third change is to pass the translation exception identification as parameter, in theory the trans_exc_code in the lowcore could have been overwritten by the time the call to check_space from do_no_context is done. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
d40f7b75a2
commit
61365e132e
@ -100,39 +100,28 @@ void bust_spinlocks(int yes)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the address space associated with the fault.
|
* Returns the address space associated with the fault.
|
||||||
* Returns 0 for kernel space, 1 for user space and
|
* Returns 0 for kernel space and 1 for user space.
|
||||||
* 2 for code execution in user space with noexec=on.
|
|
||||||
*/
|
*/
|
||||||
static inline int check_space(struct task_struct *tsk)
|
static inline int user_space_fault(unsigned long trans_exc_code)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The lowest two bits of S390_lowcore.trans_exc_code
|
* The lowest two bits of the translation exception
|
||||||
* indicate which paging table was used.
|
* identification indicate which paging table was used.
|
||||||
*/
|
*/
|
||||||
int desc = S390_lowcore.trans_exc_code & 3;
|
trans_exc_code &= 3;
|
||||||
|
if (trans_exc_code == 2)
|
||||||
if (desc == 3) /* Home Segment Table Descriptor */
|
/* Access via secondary space, set_fs setting decides */
|
||||||
return switch_amode == 0;
|
return current->thread.mm_segment.ar4;
|
||||||
if (desc == 2) /* Secondary Segment Table Descriptor */
|
if (!switch_amode)
|
||||||
return tsk->thread.mm_segment.ar4;
|
/* User space if the access has been done via home space. */
|
||||||
#ifdef CONFIG_S390_SWITCH_AMODE
|
return trans_exc_code == 3;
|
||||||
if (unlikely(desc == 1)) { /* STD determined via access register */
|
|
||||||
/* %a0 always indicates primary space. */
|
|
||||||
if (S390_lowcore.exc_access_id != 0) {
|
|
||||||
save_access_regs(tsk->thread.acrs);
|
|
||||||
/*
|
/*
|
||||||
* An alet of 0 indicates primary space.
|
* If the user space is not the home space the kernel runs in home
|
||||||
* An alet of 1 indicates secondary space.
|
* space. Access via secondary space has already been covered,
|
||||||
* Any other alet values generate an
|
* access via primary space or access register is from user space
|
||||||
* alen-translation exception.
|
* and access via home space is from the kernel.
|
||||||
*/
|
*/
|
||||||
if (tsk->thread.acrs[S390_lowcore.exc_access_id])
|
return trans_exc_code != 3;
|
||||||
return tsk->thread.mm_segment.ar4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* Primary Segment Table Descriptor */
|
|
||||||
return switch_amode << s390_noexec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -162,9 +151,10 @@ static void do_sigsegv(struct pt_regs *regs, unsigned long error_code,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void do_no_context(struct pt_regs *regs, unsigned long error_code,
|
static void do_no_context(struct pt_regs *regs, unsigned long error_code,
|
||||||
unsigned long address)
|
unsigned long trans_exc_code)
|
||||||
{
|
{
|
||||||
const struct exception_table_entry *fixup;
|
const struct exception_table_entry *fixup;
|
||||||
|
unsigned long address;
|
||||||
|
|
||||||
/* Are we prepared to handle this kernel fault? */
|
/* Are we prepared to handle this kernel fault? */
|
||||||
fixup = search_exception_tables(regs->psw.addr & __FIXUP_MASK);
|
fixup = search_exception_tables(regs->psw.addr & __FIXUP_MASK);
|
||||||
@ -177,7 +167,8 @@ static void do_no_context(struct pt_regs *regs, unsigned long error_code,
|
|||||||
* Oops. The kernel tried to access some bad page. We'll have to
|
* Oops. The kernel tried to access some bad page. We'll have to
|
||||||
* terminate things with extreme prejudice.
|
* terminate things with extreme prejudice.
|
||||||
*/
|
*/
|
||||||
if (check_space(current) == 0)
|
address = trans_exc_code & __FAIL_ADDR_MASK;
|
||||||
|
if (user_space_fault(trans_exc_code) == 0)
|
||||||
printk(KERN_ALERT "Unable to handle kernel pointer dereference"
|
printk(KERN_ALERT "Unable to handle kernel pointer dereference"
|
||||||
" at virtual kernel address %p\n", (void *)address);
|
" at virtual kernel address %p\n", (void *)address);
|
||||||
else
|
else
|
||||||
@ -188,7 +179,8 @@ static void do_no_context(struct pt_regs *regs, unsigned long error_code,
|
|||||||
do_exit(SIGKILL);
|
do_exit(SIGKILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_low_address(struct pt_regs *regs, unsigned long error_code)
|
static void do_low_address(struct pt_regs *regs, unsigned long error_code,
|
||||||
|
unsigned long trans_exc_code)
|
||||||
{
|
{
|
||||||
/* Low-address protection hit in kernel mode means
|
/* Low-address protection hit in kernel mode means
|
||||||
NULL pointer write access in kernel mode. */
|
NULL pointer write access in kernel mode. */
|
||||||
@ -198,11 +190,11 @@ static void do_low_address(struct pt_regs *regs, unsigned long error_code)
|
|||||||
do_exit(SIGKILL);
|
do_exit(SIGKILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
do_no_context(regs, error_code, 0);
|
do_no_context(regs, error_code, trans_exc_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_sigbus(struct pt_regs *regs, unsigned long error_code,
|
static void do_sigbus(struct pt_regs *regs, unsigned long error_code,
|
||||||
unsigned long address)
|
unsigned long trans_exc_code)
|
||||||
{
|
{
|
||||||
struct task_struct *tsk = current;
|
struct task_struct *tsk = current;
|
||||||
struct mm_struct *mm = tsk->mm;
|
struct mm_struct *mm = tsk->mm;
|
||||||
@ -212,13 +204,13 @@ static void do_sigbus(struct pt_regs *regs, unsigned long error_code,
|
|||||||
* Send a sigbus, regardless of whether we were in kernel
|
* Send a sigbus, regardless of whether we were in kernel
|
||||||
* or user mode.
|
* or user mode.
|
||||||
*/
|
*/
|
||||||
tsk->thread.prot_addr = address;
|
tsk->thread.prot_addr = trans_exc_code & __FAIL_ADDR_MASK;
|
||||||
tsk->thread.trap_no = error_code;
|
tsk->thread.trap_no = error_code;
|
||||||
force_sig(SIGBUS, tsk);
|
force_sig(SIGBUS, tsk);
|
||||||
|
|
||||||
/* Kernel mode? Handle exceptions or die */
|
/* Kernel mode? Handle exceptions or die */
|
||||||
if (!(regs->psw.mask & PSW_MASK_PSTATE))
|
if (!(regs->psw.mask & PSW_MASK_PSTATE))
|
||||||
do_no_context(regs, error_code, address);
|
do_no_context(regs, error_code, trans_exc_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_S390_EXEC_PROTECT
|
#ifdef CONFIG_S390_EXEC_PROTECT
|
||||||
@ -272,13 +264,13 @@ static int signal_return(struct mm_struct *mm, struct pt_regs *regs,
|
|||||||
* 3b Region third trans. -> Not present (nullification)
|
* 3b Region third trans. -> Not present (nullification)
|
||||||
*/
|
*/
|
||||||
static inline void
|
static inline void
|
||||||
do_exception(struct pt_regs *regs, unsigned long error_code, int write)
|
do_exception(struct pt_regs *regs, unsigned long error_code, int write,
|
||||||
|
unsigned long trans_exc_code)
|
||||||
{
|
{
|
||||||
struct task_struct *tsk;
|
struct task_struct *tsk;
|
||||||
struct mm_struct *mm;
|
struct mm_struct *mm;
|
||||||
struct vm_area_struct *vma;
|
struct vm_area_struct *vma;
|
||||||
unsigned long address;
|
unsigned long address;
|
||||||
int space;
|
|
||||||
int si_code;
|
int si_code;
|
||||||
int fault;
|
int fault;
|
||||||
|
|
||||||
@ -288,18 +280,15 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int write)
|
|||||||
tsk = current;
|
tsk = current;
|
||||||
mm = tsk->mm;
|
mm = tsk->mm;
|
||||||
|
|
||||||
/* get the failing address and the affected space */
|
|
||||||
address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK;
|
|
||||||
space = check_space(tsk);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Verify that the fault happened in user space, that
|
* Verify that the fault happened in user space, that
|
||||||
* we are not in an interrupt and that there is a
|
* we are not in an interrupt and that there is a
|
||||||
* user context.
|
* user context.
|
||||||
*/
|
*/
|
||||||
if (unlikely(space == 0 || in_atomic() || !mm))
|
if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
|
||||||
goto no_context;
|
goto no_context;
|
||||||
|
|
||||||
|
address = trans_exc_code & __FAIL_ADDR_MASK;
|
||||||
/*
|
/*
|
||||||
* When we get here, the fault happened in the current
|
* When we get here, the fault happened in the current
|
||||||
* task's user address space, so we can switch on the
|
* task's user address space, so we can switch on the
|
||||||
@ -315,7 +304,8 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int write)
|
|||||||
goto bad_area;
|
goto bad_area;
|
||||||
|
|
||||||
#ifdef CONFIG_S390_EXEC_PROTECT
|
#ifdef CONFIG_S390_EXEC_PROTECT
|
||||||
if (unlikely((space == 2) && !(vma->vm_flags & VM_EXEC)))
|
if (unlikely((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_SECONDARY &&
|
||||||
|
(trans_exc_code & 3) == 0 && !(vma->vm_flags & VM_EXEC)))
|
||||||
if (!signal_return(mm, regs, address, error_code))
|
if (!signal_return(mm, regs, address, error_code))
|
||||||
/*
|
/*
|
||||||
* signal_return() has done an up_read(&mm->mmap_sem)
|
* signal_return() has done an up_read(&mm->mmap_sem)
|
||||||
@ -397,12 +387,14 @@ bad_area:
|
|||||||
}
|
}
|
||||||
|
|
||||||
no_context:
|
no_context:
|
||||||
do_no_context(regs, error_code, address);
|
do_no_context(regs, error_code, trans_exc_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __kprobes do_protection_exception(struct pt_regs *regs,
|
void __kprobes do_protection_exception(struct pt_regs *regs,
|
||||||
long error_code)
|
long error_code)
|
||||||
{
|
{
|
||||||
|
unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
|
||||||
|
|
||||||
/* Protection exception is supressing, decrement psw address. */
|
/* Protection exception is supressing, decrement psw address. */
|
||||||
regs->psw.addr -= (error_code >> 16);
|
regs->psw.addr -= (error_code >> 16);
|
||||||
/*
|
/*
|
||||||
@ -410,31 +402,30 @@ void __kprobes do_protection_exception(struct pt_regs *regs,
|
|||||||
* as a special case because the translation exception code
|
* as a special case because the translation exception code
|
||||||
* field is not guaranteed to contain valid data in this case.
|
* field is not guaranteed to contain valid data in this case.
|
||||||
*/
|
*/
|
||||||
if (unlikely(!(S390_lowcore.trans_exc_code & 4))) {
|
if (unlikely(!(trans_exc_code & 4))) {
|
||||||
do_low_address(regs, error_code);
|
do_low_address(regs, error_code, trans_exc_code);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
do_exception(regs, 4, 1);
|
do_exception(regs, 4, 1, trans_exc_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __kprobes do_dat_exception(struct pt_regs *regs, long error_code)
|
void __kprobes do_dat_exception(struct pt_regs *regs, long error_code)
|
||||||
{
|
{
|
||||||
do_exception(regs, error_code & 0xff, 0);
|
do_exception(regs, error_code & 0xff, 0, S390_lowcore.trans_exc_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_64BIT
|
#ifdef CONFIG_64BIT
|
||||||
void __kprobes do_asce_exception(struct pt_regs *regs, unsigned long error_code)
|
void __kprobes do_asce_exception(struct pt_regs *regs, unsigned long error_code)
|
||||||
{
|
{
|
||||||
|
unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
|
||||||
struct mm_struct *mm;
|
struct mm_struct *mm;
|
||||||
struct vm_area_struct *vma;
|
struct vm_area_struct *vma;
|
||||||
unsigned long address;
|
unsigned long address;
|
||||||
int space;
|
|
||||||
|
|
||||||
mm = current->mm;
|
mm = current->mm;
|
||||||
address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK;
|
address = trans_exc_code & __FAIL_ADDR_MASK;
|
||||||
space = check_space(current);
|
|
||||||
|
|
||||||
if (unlikely(space == 0 || in_atomic() || !mm))
|
if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
|
||||||
goto no_context;
|
goto no_context;
|
||||||
|
|
||||||
local_irq_enable();
|
local_irq_enable();
|
||||||
@ -457,7 +448,7 @@ void __kprobes do_asce_exception(struct pt_regs *regs, unsigned long error_code)
|
|||||||
}
|
}
|
||||||
|
|
||||||
no_context:
|
no_context:
|
||||||
do_no_context(regs, error_code, address);
|
do_no_context(regs, error_code, trans_exc_code);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user