 c25a785d66
			
		
	
	
	c25a785d66
	
	
	
		
			
			If the provided system call number is equal to __NR_syscalls, the current check will pass and a function pointer just after the system call table may be called, since sys_call_table is an array with total size __NR_syscalls. Whether or not this is a security bug depends on what the compiler puts immediately after the system call table. It's likely that this won't do anything bad because there is an additional NULL check on the syscall entry, but if there happens to be a non-NULL value immediately after the system call table, this may result in local privilege escalation. Signed-off-by: Dan Rosenberg <drosenberg@vsecurity.com> Cc: <stable@vger.kernel.org> Cc: Chen Liqin <liqin.chen@sunplusct.com> Cc: Lennox Wu <lennox.wu@gmail.com> Cc: Eugene Teo <eugeneteo@kernel.sg> Cc: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
		
			
				
	
	
		
			514 lines
		
	
	
	
		
			8.8 KiB
			
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			514 lines
		
	
	
	
		
			8.8 KiB
			
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| /*
 | |
|  * arch/score/kernel/entry.S
 | |
|  *
 | |
|  * Score Processor version.
 | |
|  *
 | |
|  * Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
 | |
|  *  Chen Liqin <liqin.chen@sunplusct.com>
 | |
|  *  Lennox Wu <lennox.wu@sunplusct.com>
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, see the file COPYING, or write
 | |
|  * to the Free Software Foundation, Inc.,
 | |
|  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | |
|  */
 | |
| 
 | |
| #include <linux/err.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/linkage.h>
 | |
| 
 | |
| #include <asm/asmmacro.h>
 | |
| #include <asm/thread_info.h>
 | |
| #include <asm/unistd.h>
 | |
| 
 | |
| /*
 | |
|  * disable interrupts.
 | |
|  */
 | |
| .macro	disable_irq
 | |
| 	mfcr	r8, cr0
 | |
| 	srli	r8, r8, 1
 | |
| 	slli	r8, r8, 1
 | |
| 	mtcr	r8, cr0
 | |
| 	nop
 | |
| 	nop
 | |
| 	nop
 | |
| 	nop
 | |
| 	nop
 | |
| .endm
 | |
| 
 | |
| /*
 | |
|  * enable interrupts.
 | |
|  */
 | |
| .macro	enable_irq
 | |
| 	mfcr	r8, cr0
 | |
| 	ori	r8, 1
 | |
| 	mtcr	r8, cr0
 | |
| 	nop
 | |
| 	nop
 | |
| 	nop
 | |
| 	nop
 | |
| 	nop
 | |
| .endm
 | |
| 
 | |
| __INIT
 | |
| ENTRY(debug_exception_vector)
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 
 | |
| ENTRY(general_exception_vector)			# should move to addr 0x200
 | |
| 	j	general_exception
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 
 | |
| ENTRY(interrupt_exception_vector)		# should move to addr 0x210
 | |
| 	j	interrupt_exception
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 	nop!
 | |
| 
 | |
| 	.section ".text", "ax"
 | |
| 	.align	2;
 | |
| general_exception:
 | |
| 	mfcr	r31, cr2
 | |
| 	nop
 | |
| 	la	r30, exception_handlers
 | |
| 	andi	r31, 0x1f			# get ecr.exc_code
 | |
| 	slli	r31, r31, 2
 | |
| 	add	r30, r30, r31
 | |
| 	lw	r30, [r30]
 | |
| 	br	r30
 | |
| 
 | |
| interrupt_exception:
 | |
| 	SAVE_ALL
 | |
| 	mfcr	r4, cr2
 | |
| 	nop
 | |
| 	lw	r16, [r28, TI_REGS]
 | |
| 	sw	r0, [r28, TI_REGS]
 | |
| 	la	r3, ret_from_irq
 | |
| 	srli	r4, r4, 18			# get ecr.ip[7:2], interrupt No.
 | |
| 	mv	r5, r0
 | |
| 	j	do_IRQ
 | |
| 
 | |
| ENTRY(handle_nmi)				# NMI #1
 | |
| 	SAVE_ALL
 | |
| 	mv	r4, r0
 | |
| 	la	r8, nmi_exception_handler
 | |
| 	brl	r8
 | |
| 	j	restore_all
 | |
| 
 | |
| ENTRY(handle_adelinsn)				# AdEL-instruction #2
 | |
| 	SAVE_ALL
 | |
| 	mfcr	r8, cr6
 | |
| 	nop
 | |
| 	nop
 | |
| 	sw	r8, [r0, PT_EMA]
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_adelinsn
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| ENTRY(handle_ibe)				# BusEL-instruction #5
 | |
| 	SAVE_ALL
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_be
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| ENTRY(handle_pel)				# P-EL #6
 | |
| 	SAVE_ALL
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_pel
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| ENTRY(handle_ccu)				# CCU #8
 | |
| 	SAVE_ALL
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_ccu
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| ENTRY(handle_ri)				# RI #9
 | |
| 	SAVE_ALL
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_ri
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| ENTRY(handle_tr)				# Trap #10
 | |
| 	SAVE_ALL
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_tr
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| ENTRY(handle_adedata)				# AdES-instruction #12
 | |
| 	SAVE_ALL
 | |
| 	mfcr	r8, cr6
 | |
| 	nop
 | |
| 	nop
 | |
| 	sw	r8, [r0, PT_EMA]
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_adedata
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| ENTRY(handle_cee)				# CeE #16
 | |
| 	SAVE_ALL
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_cee
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| ENTRY(handle_cpe)				# CpE #17
 | |
| 	SAVE_ALL
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_cpe
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| ENTRY(handle_dbe)				# BusEL-data #18
 | |
| 	SAVE_ALL
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_be
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| ENTRY(handle_reserved)				# others
 | |
| 	SAVE_ALL
 | |
| 	mv	r4, r0
 | |
| 	la	r8, do_reserved
 | |
| 	brl	r8
 | |
| 	mv	r4, r0
 | |
| 	j	ret_from_exception
 | |
| 	nop
 | |
| 
 | |
| #ifndef CONFIG_PREEMPT
 | |
| #define resume_kernel	restore_all
 | |
| #else
 | |
| #define __ret_from_irq	ret_from_exception
 | |
| #endif
 | |
| 
 | |
| 	.align	2
 | |
| #ifndef CONFIG_PREEMPT
 | |
| ENTRY(ret_from_exception)
 | |
| 	disable_irq			# preempt stop
 | |
| 	nop
 | |
| 	j	__ret_from_irq
 | |
| 	nop
 | |
| #endif
 | |
| 
 | |
| ENTRY(ret_from_irq)
 | |
| 	sw	r16, [r28, TI_REGS]
 | |
| 
 | |
| ENTRY(__ret_from_irq)
 | |
| 	lw	r8, [r0, PT_PSR]	# returning to kernel mode?
 | |
| 	andri.c	r8, r8, KU_USER
 | |
| 	beq	resume_kernel
 | |
| 
 | |
| resume_userspace:
 | |
| 	disable_irq
 | |
| 	lw	r6, [r28, TI_FLAGS]	# current->work
 | |
| 	li	r8, _TIF_WORK_MASK
 | |
| 	and.c	r8, r8, r6		# ignoring syscall_trace
 | |
| 	bne	work_pending
 | |
| 	nop
 | |
| 	j	restore_all
 | |
| 	nop
 | |
| 
 | |
| #ifdef CONFIG_PREEMPT
 | |
| resume_kernel:
 | |
| 	disable_irq
 | |
| 	lw	r8, [r28, TI_PRE_COUNT]
 | |
| 	cmpz.c	r8
 | |
| 	bne	r8, restore_all
 | |
| need_resched:
 | |
| 	lw	r8, [r28, TI_FLAGS]
 | |
| 	andri.c	r9, r8, _TIF_NEED_RESCHED
 | |
| 	beq	restore_all
 | |
| 	lw	r8, [r28, PT_PSR]		# Interrupts off?
 | |
| 	andri.c	r8, r8, 1
 | |
| 	beq	restore_all
 | |
| 	bl	preempt_schedule_irq
 | |
| 	nop
 | |
| 	j	need_resched
 | |
| 	nop
 | |
| #endif
 | |
| 
 | |
| ENTRY(ret_from_fork)
 | |
| 	bl	schedule_tail			# r4=struct task_struct *prev
 | |
| 
 | |
| ENTRY(syscall_exit)
 | |
| 	nop
 | |
| 	disable_irq
 | |
| 	lw	r6, [r28, TI_FLAGS]		# current->work
 | |
| 	li	r8, _TIF_WORK_MASK
 | |
| 	and.c	r8, r6, r8
 | |
| 	bne	syscall_exit_work
 | |
| 
 | |
| ENTRY(restore_all)					# restore full frame
 | |
| 	RESTORE_ALL_AND_RET
 | |
| 
 | |
| work_pending:
 | |
| 	andri.c	r8, r6, _TIF_NEED_RESCHED # r6 is preloaded with TI_FLAGS
 | |
| 	beq	work_notifysig
 | |
| work_resched:
 | |
| 	bl	schedule
 | |
| 	nop
 | |
| 	disable_irq
 | |
| 	lw	r6, [r28, TI_FLAGS]
 | |
| 	li	r8, _TIF_WORK_MASK
 | |
| 	and.c	r8, r6, r8	# is there any work to be done
 | |
| 				# other than syscall tracing?
 | |
| 	beq	restore_all
 | |
| 	andri.c	r8, r6, _TIF_NEED_RESCHED
 | |
| 	bne	work_resched
 | |
| 
 | |
| work_notifysig:
 | |
| 	mv	r4, r0
 | |
| 	li	r5, 0
 | |
| 	bl	do_notify_resume	# r6 already loaded
 | |
| 	nop
 | |
| 	j	resume_userspace
 | |
| 	nop
 | |
| 
 | |
| ENTRY(syscall_exit_work)
 | |
| 	li	r8, _TIF_SYSCALL_TRACE
 | |
| 	and.c	r8, r8, r6		# r6 is preloaded with TI_FLAGS
 | |
| 	beq	work_pending		# trace bit set?
 | |
| 	nop
 | |
| 	enable_irq
 | |
| 	mv	r4, r0
 | |
| 	li	r5, 1
 | |
| 	bl	do_syscall_trace
 | |
| 	nop
 | |
| 	b	resume_userspace
 | |
| 	nop
 | |
| 
 | |
| .macro	save_context	reg
 | |
| 	sw	r12, [\reg, THREAD_REG12];
 | |
| 	sw	r13, [\reg, THREAD_REG13];
 | |
| 	sw	r14, [\reg, THREAD_REG14];
 | |
| 	sw	r15, [\reg, THREAD_REG15];
 | |
| 	sw	r16, [\reg, THREAD_REG16];
 | |
| 	sw	r17, [\reg, THREAD_REG17];
 | |
| 	sw	r18, [\reg, THREAD_REG18];
 | |
| 	sw	r19, [\reg, THREAD_REG19];
 | |
| 	sw	r20, [\reg, THREAD_REG20];
 | |
| 	sw	r21, [\reg, THREAD_REG21];
 | |
| 	sw	r29, [\reg, THREAD_REG29];
 | |
| 	sw	r2, [\reg, THREAD_REG2];
 | |
| 	sw	r0, [\reg, THREAD_REG0]
 | |
| .endm
 | |
| 
 | |
| .macro	restore_context	reg
 | |
| 	lw	r12, [\reg, THREAD_REG12];
 | |
| 	lw	r13, [\reg, THREAD_REG13];
 | |
| 	lw	r14, [\reg, THREAD_REG14];
 | |
| 	lw	r15, [\reg, THREAD_REG15];
 | |
| 	lw	r16, [\reg, THREAD_REG16];
 | |
| 	lw	r17, [\reg, THREAD_REG17];
 | |
| 	lw	r18, [\reg, THREAD_REG18];
 | |
| 	lw	r19, [\reg, THREAD_REG19];
 | |
| 	lw	r20, [\reg, THREAD_REG20];
 | |
| 	lw	r21, [\reg, THREAD_REG21];
 | |
| 	lw	r29, [\reg, THREAD_REG29];
 | |
| 	lw	r0, [\reg, THREAD_REG0];
 | |
| 	lw	r2, [\reg, THREAD_REG2];
 | |
| 	lw	r3, [\reg, THREAD_REG3]
 | |
| .endm
 | |
| 
 | |
| /*
 | |
|  * task_struct *resume(task_struct *prev, task_struct *next,
 | |
|  *			struct thread_info *next_ti)
 | |
|  */
 | |
| ENTRY(resume)
 | |
| 	mfcr	r9, cr0
 | |
| 	nop
 | |
| 	nop
 | |
| 	sw	r9, [r4, THREAD_PSR]
 | |
| 	save_context	r4
 | |
| 	sw	r3, [r4, THREAD_REG3]
 | |
| 
 | |
| 	mv	r28, r6
 | |
| 	restore_context	r5
 | |
| 	mv	r8, r6
 | |
| 	addi	r8, KERNEL_STACK_SIZE
 | |
| 	subi	r8, 32
 | |
| 	la 	r9, kernelsp;
 | |
| 	sw 	r8, [r9];
 | |
| 
 | |
| 	mfcr	r9, cr0
 | |
| 	ldis	r7, 0x00ff
 | |
| 	nop
 | |
| 	and	r9, r9, r7
 | |
| 	lw	r6, [r5, THREAD_PSR]
 | |
| 	not	r7, r7
 | |
| 	and	r6, r6, r7
 | |
| 	or	r6, r6, r9
 | |
| 	mtcr	r6, cr0
 | |
| 	nop; nop; nop; nop; nop
 | |
| 	br	r3
 | |
| 
 | |
| ENTRY(handle_sys)
 | |
| 	SAVE_ALL
 | |
| 	sw	r8, [r0, 16]		# argument 5 from user r8
 | |
| 	sw	r9, [r0, 20]		# argument 6 from user r9
 | |
| 	enable_irq
 | |
| 
 | |
| 	sw	r4, [r0, PT_ORIG_R4]	#for restart syscall
 | |
| 	sw	r7, [r0, PT_ORIG_R7]	#for restart syscall
 | |
| 	sw	r27, [r0, PT_IS_SYSCALL] # it from syscall
 | |
| 
 | |
| 	lw	r9, [r0, PT_EPC]	# skip syscall on return
 | |
| 	addi	r9, 4
 | |
| 	sw	r9, [r0, PT_EPC]
 | |
| 
 | |
| 	cmpi.c	r27, __NR_syscalls 	# check syscall number
 | |
| 	bgeu	illegal_syscall
 | |
| 
 | |
| 	slli	r8, r27, 2		# get syscall routine
 | |
| 	la	r11, sys_call_table
 | |
| 	add	r11, r11, r8
 | |
| 	lw	r10, [r11]		# get syscall entry
 | |
| 
 | |
| 	cmpz.c	r10
 | |
| 	beq	illegal_syscall
 | |
| 
 | |
| 	lw	r8, [r28, TI_FLAGS]
 | |
| 	li	r9, _TIF_SYSCALL_TRACE
 | |
| 	and.c	r8, r8, r9
 | |
| 	bne	syscall_trace_entry
 | |
| 
 | |
| 	brl	r10			# Do The Real system call
 | |
| 
 | |
| 	cmpi.c	r4, 0
 | |
| 	blt	1f
 | |
| 	ldi	r8, 0
 | |
| 	sw	r8, [r0, PT_R7]
 | |
| 	b 2f
 | |
| 1:
 | |
| 	cmpi.c	r4, -MAX_ERRNO - 1
 | |
| 	ble	2f
 | |
| 	ldi	r8, 0x1;
 | |
| 	sw	r8, [r0, PT_R7]
 | |
| 	neg	r4, r4
 | |
| 2:
 | |
| 	sw	r4, [r0, PT_R4]		# save result
 | |
| 
 | |
| syscall_return:
 | |
| 	disable_irq
 | |
| 	lw	r6, [r28, TI_FLAGS]	# current->work
 | |
| 	li	r8, _TIF_WORK_MASK
 | |
| 	and.c	r8, r6, r8
 | |
| 	bne	syscall_return_work
 | |
| 	j	restore_all
 | |
| 
 | |
| syscall_return_work:
 | |
| 	j	syscall_exit_work
 | |
| 
 | |
| syscall_trace_entry:
 | |
| 	mv	r16, r10
 | |
| 	mv	r4, r0
 | |
| 	li	r5, 0
 | |
| 	bl	do_syscall_trace
 | |
| 
 | |
| 	mv	r8, r16
 | |
| 	lw	r4, [r0, PT_R4]		# Restore argument registers
 | |
| 	lw	r5, [r0, PT_R5]
 | |
| 	lw	r6, [r0, PT_R6]
 | |
| 	lw	r7, [r0, PT_R7]
 | |
| 	brl	r8
 | |
| 
 | |
| 	li	r8, -MAX_ERRNO - 1
 | |
| 	sw	r8, [r0, PT_R7]		# set error flag
 | |
| 
 | |
| 	neg	r4, r4			# error
 | |
| 	sw	r4, [r0, PT_R0]		# set flag for syscall
 | |
| 					# restarting
 | |
| 1:	sw	r4, [r0, PT_R2]		# result
 | |
| 	j	syscall_exit
 | |
| 
 | |
| illegal_syscall:
 | |
| 	ldi	r4, -ENOSYS		# error
 | |
| 	sw	r4, [r0, PT_ORIG_R4]
 | |
| 	sw	r4, [r0, PT_R4]
 | |
| 	ldi	r9, 1			# set error flag
 | |
| 	sw	r9, [r0, PT_R7]
 | |
| 	j	syscall_return
 | |
| 
 | |
| ENTRY(sys_execve)
 | |
| 	mv	r4, r0
 | |
| 	la	r8, score_execve
 | |
| 	br	r8
 | |
| 
 | |
| ENTRY(sys_clone)
 | |
| 	mv	r4, r0
 | |
| 	la	r8, score_clone
 | |
| 	br	r8
 | |
| 
 | |
| ENTRY(sys_rt_sigreturn)
 | |
| 	mv	r4, r0
 | |
| 	la	r8, score_rt_sigreturn
 | |
| 	br	r8
 | |
| 
 | |
| ENTRY(sys_sigaltstack)
 | |
| 	mv	r4, r0
 | |
| 	la	r8, score_sigaltstack
 | |
| 	br	r8
 | |
| 
 | |
| #ifdef __ARCH_WANT_SYSCALL_DEPRECATED
 | |
| ENTRY(sys_fork)
 | |
| 	mv	r4, r0
 | |
| 	la	r8, score_fork
 | |
| 	br	r8
 | |
| 
 | |
| ENTRY(sys_vfork)
 | |
| 	mv	r4, r0
 | |
| 	la	r8, score_vfork
 | |
| 	br	r8
 | |
| #endif /* __ARCH_WANT_SYSCALL_DEPRECATED */
 | |
| 
 |