diff options
Diffstat (limited to 'arch/powerpc/mm/fault.c')
| -rw-r--r-- | arch/powerpc/mm/fault.c | 41 | 
1 files changed, 27 insertions, 14 deletions
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 229951ffc351..8726779e1409 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -32,6 +32,7 @@  #include <linux/perf_event.h>  #include <linux/magic.h>  #include <linux/ratelimit.h> +#include <linux/context_tracking.h>  #include <asm/firmware.h>  #include <asm/page.h> @@ -196,6 +197,7 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)  int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,  			    unsigned long error_code)  { +	enum ctx_state prev_state = exception_enter();  	struct vm_area_struct * vma;  	struct mm_struct *mm = current->mm;  	unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; @@ -204,6 +206,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,  	int trap = TRAP(regs);   	int is_exec = trap == 0x400;  	int fault; +	int rc = 0;  #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))  	/* @@ -230,28 +233,30 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,  	 * look at it  	 */  	if (error_code & ICSWX_DSI_UCT) { -		int rc = acop_handle_fault(regs, address, error_code); +		rc = acop_handle_fault(regs, address, error_code);  		if (rc) -			return rc; +			goto bail;  	}  #endif /* CONFIG_PPC_ICSWX */  	if (notify_page_fault(regs)) -		return 0; +		goto bail;  	if (unlikely(debugger_fault_handler(regs))) -		return 0; +		goto bail;  	/* On a kernel SLB miss we can only check for a valid exception entry */ -	if (!user_mode(regs) && (address >= TASK_SIZE)) -		return SIGSEGV; +	if (!user_mode(regs) && (address >= TASK_SIZE)) { +		rc = SIGSEGV; +		goto bail; +	}  #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE) || \  			     defined(CONFIG_PPC_BOOK3S_64))    	if (error_code & DSISR_DABRMATCH) {  		/* breakpoint match */  		do_break(regs, address, error_code); -		return 0; +		goto bail;  	}  #endif @@ -260,8 +265,10 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,  		local_irq_enable();  	if (in_atomic() || mm == NULL) { -		if (!user_mode(regs)) -			return SIGSEGV; +		if (!user_mode(regs)) { +			rc = SIGSEGV; +			goto bail; +		}  		/* in_atomic() in user mode is really bad,  		   as is current->mm == NULL. */  		printk(KERN_EMERG "Page fault in user mode with " @@ -417,9 +424,11 @@ good_area:  	 */  	fault = handle_mm_fault(mm, vma, address, flags);  	if (unlikely(fault & (VM_FAULT_RETRY|VM_FAULT_ERROR))) { -		int rc = mm_fault_error(regs, address, fault); +		rc = mm_fault_error(regs, address, fault);  		if (rc >= MM_FAULT_RETURN) -			return rc; +			goto bail; +		else +			rc = 0;  	}  	/* @@ -454,7 +463,7 @@ good_area:  	}  	up_read(&mm->mmap_sem); -	return 0; +	goto bail;  bad_area:  	up_read(&mm->mmap_sem); @@ -463,7 +472,7 @@ bad_area_nosemaphore:  	/* User mode accesses cause a SIGSEGV */  	if (user_mode(regs)) {  		_exception(SIGSEGV, regs, code, address); -		return 0; +		goto bail;  	}  	if (is_exec && (error_code & DSISR_PROTFAULT)) @@ -471,7 +480,11 @@ bad_area_nosemaphore:  				   " page (%lx) - exploit attempt? (uid: %d)\n",  				   address, from_kuid(&init_user_ns, current_uid())); -	return SIGSEGV; +	rc = SIGSEGV; + +bail: +	exception_exit(prev_state); +	return rc;  }  | 
