mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git
synced 2026-10-28 21:02:43 +01:00
The syscall trace path reloads syscall arguments from pt_regs before
calling the syscall handler. On C-SKY ABIv2, the 5th and 6th syscall
arguments are prepared as stack arguments before invoking syscallid.
The current code adjusts sp before loading LSAVE_A4 and LSAVE_A5. Since
those offsets are relative to the original pt_regs base, loading them
after changing sp fetches the wrong slots. As a result, traced syscalls
that use the 5th or 6th argument may receive corrupted arguments.
This is visible with mmap2(), which takes six arguments. A small
PTRACE_SYSCALL reproducer opens a file and maps one page with:
mmap(NULL, 4096, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0)
Before the fix, the traced child fails the mmap and exits with 12.
After the fix, the mapping succeeds and the child exits with 0.
Fix the trace path by loading a4/a5 from pt_regs before changing sp.
Tested on: ck860f, linux-4.19.15, C-SKY abiv2
Fixes: e0bbb53843 ("csky: Fixup abiv2 syscall_trace break a4 & a5")
Suggested-by: Guo Ren <[email protected]>
Signed-off-by: Hanlin Song <[email protected]>
Signed-off-by: Guo Ren (Alibaba DAMO Academy) <[email protected]>
275 lines
4.9 KiB
ArmAsm
275 lines
4.9 KiB
ArmAsm
/* SPDX-License-Identifier: GPL-2.0 */
|
||
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
|
||
|
||
#include <linux/linkage.h>
|
||
#include <abi/entry.h>
|
||
#include <abi/pgtable-bits.h>
|
||
#include <asm/errno.h>
|
||
#include <asm/setup.h>
|
||
#include <asm/unistd.h>
|
||
#include <asm/asm-offsets.h>
|
||
#include <linux/threads.h>
|
||
#include <asm/page.h>
|
||
#include <asm/thread_info.h>
|
||
|
||
.macro zero_fp
|
||
#ifdef CONFIG_STACKTRACE
|
||
movi r8, 0
|
||
#endif
|
||
.endm
|
||
|
||
.macro context_tracking
|
||
#ifdef CONFIG_CONTEXT_TRACKING_USER
|
||
mfcr a0, epsr
|
||
btsti a0, 31
|
||
bt 1f
|
||
jbsr user_exit_callable
|
||
ldw a0, (sp, LSAVE_A0)
|
||
ldw a1, (sp, LSAVE_A1)
|
||
ldw a2, (sp, LSAVE_A2)
|
||
ldw a3, (sp, LSAVE_A3)
|
||
#if defined(__CSKYABIV1__)
|
||
ldw r6, (sp, LSAVE_A4)
|
||
ldw r7, (sp, LSAVE_A5)
|
||
#endif
|
||
1:
|
||
#endif
|
||
.endm
|
||
|
||
.text
|
||
ENTRY(csky_pagefault)
|
||
SAVE_ALL 0
|
||
zero_fp
|
||
context_tracking
|
||
psrset ee
|
||
mov a0, sp
|
||
jbsr do_page_fault
|
||
jmpi ret_from_exception
|
||
|
||
ENTRY(csky_systemcall)
|
||
SAVE_ALL TRAP0_SIZE
|
||
zero_fp
|
||
context_tracking
|
||
psrset ee, ie
|
||
|
||
lrw r9, __NR_syscalls
|
||
cmphs syscallid, r9 /* Check nr of syscall */
|
||
bt ret_from_exception
|
||
|
||
lrw r9, sys_call_table
|
||
ixw r9, syscallid
|
||
ldw syscallid, (r9)
|
||
cmpnei syscallid, 0
|
||
bf ret_from_exception
|
||
|
||
mov r9, sp
|
||
bmaski r10, THREAD_SHIFT
|
||
andn r9, r10
|
||
ldw r10, (r9, TINFO_FLAGS)
|
||
lrw r9, _TIF_SYSCALL_WORK
|
||
and r10, r9
|
||
cmpnei r10, 0
|
||
bt csky_syscall_trace
|
||
#if defined(__CSKYABIV2__)
|
||
subi sp, 8
|
||
stw r5, (sp, 0x4)
|
||
stw r4, (sp, 0x0)
|
||
jsr syscallid /* Do system call */
|
||
addi sp, 8
|
||
#else
|
||
jsr syscallid
|
||
#endif
|
||
stw a0, (sp, LSAVE_A0) /* Save return value */
|
||
jmpi ret_from_exception
|
||
|
||
csky_syscall_trace:
|
||
mov a0, sp /* sp = pt_regs pointer */
|
||
jbsr syscall_trace_enter
|
||
cmpnei a0, 0
|
||
bt 1f
|
||
/* Prepare args before do system call */
|
||
ldw a0, (sp, LSAVE_A0)
|
||
ldw a1, (sp, LSAVE_A1)
|
||
ldw a2, (sp, LSAVE_A2)
|
||
ldw a3, (sp, LSAVE_A3)
|
||
#if defined(__CSKYABIV2__)
|
||
ldw r9, (sp, LSAVE_A4)
|
||
ldw r10, (sp, LSAVE_A5)
|
||
subi sp, 8
|
||
stw r9, (sp, 0x0)
|
||
stw r10, (sp, 0x4)
|
||
jsr syscallid /* Do system call */
|
||
addi sp, 8
|
||
#else
|
||
ldw r6, (sp, LSAVE_A4)
|
||
ldw r7, (sp, LSAVE_A5)
|
||
jsr syscallid /* Do system call */
|
||
#endif
|
||
stw a0, (sp, LSAVE_A0) /* Save return value */
|
||
|
||
1:
|
||
mov a0, sp /* right now, sp --> pt_regs */
|
||
jbsr syscall_trace_exit
|
||
br ret_from_exception
|
||
|
||
ENTRY(ret_from_kernel_thread)
|
||
jbsr schedule_tail
|
||
mov a0, r10
|
||
jsr r9
|
||
jbsr ret_from_exception
|
||
|
||
ENTRY(ret_from_fork)
|
||
jbsr schedule_tail
|
||
mov r9, sp
|
||
bmaski r10, THREAD_SHIFT
|
||
andn r9, r10
|
||
ldw r10, (r9, TINFO_FLAGS)
|
||
lrw r9, _TIF_SYSCALL_WORK
|
||
and r10, r9
|
||
cmpnei r10, 0
|
||
bf ret_from_exception
|
||
mov a0, sp /* sp = pt_regs pointer */
|
||
jbsr syscall_trace_exit
|
||
|
||
ret_from_exception:
|
||
psrclr ie
|
||
ld r9, (sp, LSAVE_PSR)
|
||
btsti r9, 31
|
||
|
||
bt 1f
|
||
/*
|
||
* Load address of current->thread_info, Then get address of task_struct
|
||
* Get task_needreshed in task_struct
|
||
*/
|
||
mov r9, sp
|
||
bmaski r10, THREAD_SHIFT
|
||
andn r9, r10
|
||
|
||
ldw r10, (r9, TINFO_FLAGS)
|
||
lrw r9, _TIF_WORK_MASK
|
||
and r10, r9
|
||
cmpnei r10, 0
|
||
bt exit_work
|
||
#ifdef CONFIG_CONTEXT_TRACKING_USER
|
||
jbsr user_enter_callable
|
||
#endif
|
||
1:
|
||
#ifdef CONFIG_PREEMPTION
|
||
mov r9, sp
|
||
bmaski r10, THREAD_SHIFT
|
||
andn r9, r10
|
||
|
||
ldw r10, (r9, TINFO_PREEMPT)
|
||
cmpnei r10, 0
|
||
bt 2f
|
||
jbsr preempt_schedule_irq /* irq en/disable is done inside */
|
||
2:
|
||
#endif
|
||
|
||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||
ld r10, (sp, LSAVE_PSR)
|
||
btsti r10, 6
|
||
bf 2f
|
||
jbsr trace_hardirqs_on
|
||
2:
|
||
#endif
|
||
RESTORE_ALL
|
||
|
||
exit_work:
|
||
lrw r9, ret_from_exception
|
||
mov lr, r9
|
||
|
||
btsti r10, TIF_NEED_RESCHED
|
||
bt work_resched
|
||
|
||
psrset ie
|
||
mov a0, sp
|
||
mov a1, r10
|
||
jmpi do_notify_resume
|
||
|
||
work_resched:
|
||
jmpi schedule
|
||
|
||
ENTRY(csky_trap)
|
||
SAVE_ALL 0
|
||
zero_fp
|
||
context_tracking
|
||
psrset ee
|
||
mov a0, sp /* Push Stack pointer arg */
|
||
jbsr trap_c /* Call C-level trap handler */
|
||
jmpi ret_from_exception
|
||
|
||
/*
|
||
* Prototype from libc for abiv1:
|
||
* register unsigned int __result asm("a0");
|
||
* asm( "trap 3" :"=r"(__result)::);
|
||
*/
|
||
ENTRY(csky_get_tls)
|
||
USPTOKSP
|
||
|
||
RD_MEH a0
|
||
WR_MEH a0
|
||
|
||
/* increase epc for continue */
|
||
mfcr a0, epc
|
||
addi a0, TRAP0_SIZE
|
||
mtcr a0, epc
|
||
|
||
/* get current task thread_info with kernel 8K stack */
|
||
bmaski a0, THREAD_SHIFT
|
||
not a0
|
||
subi sp, 1
|
||
and a0, sp
|
||
addi sp, 1
|
||
|
||
/* get tls */
|
||
ldw a0, (a0, TINFO_TP_VALUE)
|
||
|
||
KSPTOUSP
|
||
rte
|
||
|
||
ENTRY(csky_irq)
|
||
SAVE_ALL 0
|
||
zero_fp
|
||
context_tracking
|
||
psrset ee
|
||
|
||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||
jbsr trace_hardirqs_off
|
||
#endif
|
||
|
||
|
||
mov a0, sp
|
||
jbsr generic_handle_arch_irq
|
||
|
||
jmpi ret_from_exception
|
||
|
||
/*
|
||
* a0 = prev task_struct *
|
||
* a1 = next task_struct *
|
||
* a0 = return next
|
||
*/
|
||
ENTRY(__switch_to)
|
||
lrw a3, TASK_THREAD
|
||
addu a3, a0
|
||
|
||
SAVE_SWITCH_STACK
|
||
|
||
stw sp, (a3, THREAD_KSP)
|
||
|
||
/* Set up next process to run */
|
||
lrw a3, TASK_THREAD
|
||
addu a3, a1
|
||
|
||
ldw sp, (a3, THREAD_KSP) /* Set next kernel sp */
|
||
|
||
#if defined(__CSKYABIV2__)
|
||
addi a3, a1, TASK_THREAD_INFO
|
||
ldw tls, (a3, TINFO_TP_VALUE)
|
||
#endif
|
||
|
||
RESTORE_SWITCH_STACK
|
||
|
||
rts
|
||
ENDPROC(__switch_to)
|