arm64: kernel: Save and restore UAO and addr_limit on exception entry
If we take an exception while at EL1, the exception handler inherits the original context's addr_limit and PSTATE.UAO values. To be consistent always reset addr_limit and PSTATE.UAO on (re-)entry to EL1. This prevents accidental re-use of the original context's addr_limit. Based on a similar patch for arm from Russell King. Cc: <stable@vger.kernel.org> # 4.6- Acked-by: Will Deacon <will.deacon@arm.com> Reviewed-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: James Morse <james.morse@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
This commit is contained in:
		
							parent
							
								
									4c2e07c6a2
								
							
						
					
					
						commit
						e19a6ee246
					
				| @ -117,6 +117,8 @@ struct pt_regs { | ||||
| 	}; | ||||
| 	u64 orig_x0; | ||||
| 	u64 syscallno; | ||||
| 	u64 orig_addr_limit; | ||||
| 	u64 unused;	// maintain 16 byte alignment
 | ||||
| }; | ||||
| 
 | ||||
| #define arch_has_single_step()	(1) | ||||
|  | ||||
| @ -60,6 +60,7 @@ int main(void) | ||||
|   DEFINE(S_PC,			offsetof(struct pt_regs, pc)); | ||||
|   DEFINE(S_ORIG_X0,		offsetof(struct pt_regs, orig_x0)); | ||||
|   DEFINE(S_SYSCALLNO,		offsetof(struct pt_regs, syscallno)); | ||||
|   DEFINE(S_ORIG_ADDR_LIMIT,	offsetof(struct pt_regs, orig_addr_limit)); | ||||
|   DEFINE(S_FRAME_SIZE,		sizeof(struct pt_regs)); | ||||
|   BLANK(); | ||||
|   DEFINE(MM_CONTEXT_ID,		offsetof(struct mm_struct, context.id.counter)); | ||||
|  | ||||
| @ -28,6 +28,7 @@ | ||||
| #include <asm/errno.h> | ||||
| #include <asm/esr.h> | ||||
| #include <asm/irq.h> | ||||
| #include <asm/memory.h> | ||||
| #include <asm/thread_info.h> | ||||
| #include <asm/unistd.h> | ||||
| 
 | ||||
| @ -97,7 +98,14 @@ | ||||
| 	mov	x29, xzr			// fp pointed to user-space | ||||
| 	.else | ||||
| 	add	x21, sp, #S_FRAME_SIZE | ||||
| 	.endif | ||||
| 	get_thread_info tsk | ||||
| 	/* Save the task's original addr_limit and set USER_DS (TASK_SIZE_64) */ | ||||
| 	ldr	x20, [tsk, #TI_ADDR_LIMIT] | ||||
| 	str	x20, [sp, #S_ORIG_ADDR_LIMIT] | ||||
| 	mov	x20, #TASK_SIZE_64 | ||||
| 	str	x20, [tsk, #TI_ADDR_LIMIT] | ||||
| 	ALTERNATIVE(nop, SET_PSTATE_UAO(0), ARM64_HAS_UAO, CONFIG_ARM64_UAO) | ||||
| 	.endif /* \el == 0 */ | ||||
| 	mrs	x22, elr_el1 | ||||
| 	mrs	x23, spsr_el1 | ||||
| 	stp	lr, x21, [sp, #S_LR] | ||||
| @ -128,6 +136,14 @@ | ||||
| 	.endm | ||||
| 
 | ||||
| 	.macro	kernel_exit, el | ||||
| 	.if	\el != 0 | ||||
| 	/* Restore the task's original addr_limit. */ | ||||
| 	ldr	x20, [sp, #S_ORIG_ADDR_LIMIT] | ||||
| 	str	x20, [tsk, #TI_ADDR_LIMIT] | ||||
| 
 | ||||
| 	/* No need to restore UAO, it will be restored from SPSR_EL1 */ | ||||
| 	.endif | ||||
| 
 | ||||
| 	ldp	x21, x22, [sp, #S_PC]		// load ELR, SPSR | ||||
| 	.if	\el == 0 | ||||
| 	ct_user_enter | ||||
| @ -406,7 +422,6 @@ el1_irq: | ||||
| 	bl	trace_hardirqs_off | ||||
| #endif | ||||
| 
 | ||||
| 	get_thread_info tsk | ||||
| 	irq_handler | ||||
| 
 | ||||
| #ifdef CONFIG_PREEMPT | ||||
|  | ||||
| @ -280,7 +280,8 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr, | ||||
| 	} | ||||
| 
 | ||||
| 	if (permission_fault(esr) && (addr < USER_DS)) { | ||||
| 		if (get_fs() == KERNEL_DS) | ||||
| 		/* regs->orig_addr_limit may be 0 if we entered from EL0 */ | ||||
| 		if (regs->orig_addr_limit == KERNEL_DS) | ||||
| 			die("Accessing user space memory with fs=KERNEL_DS", regs, esr); | ||||
| 
 | ||||
| 		if (!search_exception_tables(regs->pc)) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user