2019-05-29 17:18:00 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-07-11 04:07:09 +03:00
/*
* Copyright 2010 Tilera Corporation . All Rights Reserved .
* Copyright 2015 Regents of the University of California
* Copyright 2017 SiFive
*
* Copied from arch / tile / kernel / ptrace . c
*/
# include <asm/ptrace.h>
# include <asm/syscall.h>
# include <asm/thread_info.h>
2021-08-03 12:27:51 +03:00
# include <asm/switch_to.h>
2018-10-29 13:48:54 +03:00
# include <linux/audit.h>
2017-07-11 04:07:09 +03:00
# include <linux/ptrace.h>
# include <linux/elf.h>
# include <linux/regset.h>
# include <linux/sched.h>
# include <linux/sched/task_stack.h>
2018-12-10 23:43:55 +03:00
# define CREATE_TRACE_POINTS
2017-07-11 04:07:09 +03:00
# include <trace/events/syscalls.h>
enum riscv_regset {
REGSET_X ,
2018-10-18 03:59:05 +03:00
# ifdef CONFIG_FPU
REGSET_F ,
# endif
2017-07-11 04:07:09 +03:00
} ;
static int riscv_gpr_get ( struct task_struct * target ,
const struct user_regset * regset ,
2020-06-16 21:04:53 +03:00
struct membuf to )
2017-07-11 04:07:09 +03:00
{
2020-06-16 21:04:53 +03:00
return membuf_write ( & to , task_pt_regs ( target ) ,
sizeof ( struct user_regs_struct ) ) ;
2017-07-11 04:07:09 +03:00
}
static int riscv_gpr_set ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
{
struct pt_regs * regs ;
regs = task_pt_regs ( target ) ;
2022-01-12 11:27:29 +03:00
return user_regset_copyin ( & pos , & count , & kbuf , & ubuf , regs , 0 , - 1 ) ;
2017-07-11 04:07:09 +03:00
}
2018-10-18 03:59:05 +03:00
# ifdef CONFIG_FPU
static int riscv_fpr_get ( struct task_struct * target ,
const struct user_regset * regset ,
2020-06-16 21:04:53 +03:00
struct membuf to )
2018-10-18 03:59:05 +03:00
{
struct __riscv_d_ext_state * fstate = & target - > thread . fstate ;
2021-08-03 12:27:51 +03:00
if ( target = = current )
fstate_save ( current , task_pt_regs ( current ) ) ;
2020-06-16 21:04:53 +03:00
membuf_write ( & to , fstate , offsetof ( struct __riscv_d_ext_state , fcsr ) ) ;
membuf_store ( & to , fstate - > fcsr ) ;
return membuf_zero ( & to , 4 ) ; // explicitly pad
2018-10-18 03:59:05 +03:00
}
static int riscv_fpr_set ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
{
int ret ;
struct __riscv_d_ext_state * fstate = & target - > thread . fstate ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf , fstate , 0 ,
offsetof ( struct __riscv_d_ext_state , fcsr ) ) ;
if ( ! ret ) {
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf , fstate , 0 ,
offsetof ( struct __riscv_d_ext_state , fcsr ) +
sizeof ( fstate - > fcsr ) ) ;
}
return ret ;
}
# endif
2017-07-11 04:07:09 +03:00
static const struct user_regset riscv_user_regset [ ] = {
[ REGSET_X ] = {
. core_note_type = NT_PRSTATUS ,
. n = ELF_NGREG ,
. size = sizeof ( elf_greg_t ) ,
. align = sizeof ( elf_greg_t ) ,
2020-06-16 21:04:53 +03:00
. regset_get = riscv_gpr_get ,
. set = riscv_gpr_set ,
2017-07-11 04:07:09 +03:00
} ,
2018-10-18 03:59:05 +03:00
# ifdef CONFIG_FPU
[ REGSET_F ] = {
. core_note_type = NT_PRFPREG ,
. n = ELF_NFPREG ,
. size = sizeof ( elf_fpreg_t ) ,
. align = sizeof ( elf_fpreg_t ) ,
2020-06-16 21:04:53 +03:00
. regset_get = riscv_fpr_get ,
. set = riscv_fpr_set ,
2018-10-18 03:59:05 +03:00
} ,
# endif
2017-07-11 04:07:09 +03:00
} ;
static const struct user_regset_view riscv_user_native_view = {
. name = " riscv " ,
. e_machine = EM_RISCV ,
. regsets = riscv_user_regset ,
. n = ARRAY_SIZE ( riscv_user_regset ) ,
} ;
const struct user_regset_view * task_user_regset_view ( struct task_struct * task )
{
return & riscv_user_native_view ;
}
2020-12-17 19:01:37 +03:00
struct pt_regs_offset {
const char * name ;
int offset ;
} ;
# define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
# define REG_OFFSET_END {.name = NULL, .offset = 0}
static const struct pt_regs_offset regoffset_table [ ] = {
REG_OFFSET_NAME ( epc ) ,
REG_OFFSET_NAME ( ra ) ,
REG_OFFSET_NAME ( sp ) ,
REG_OFFSET_NAME ( gp ) ,
REG_OFFSET_NAME ( tp ) ,
REG_OFFSET_NAME ( t0 ) ,
REG_OFFSET_NAME ( t1 ) ,
REG_OFFSET_NAME ( t2 ) ,
REG_OFFSET_NAME ( s0 ) ,
REG_OFFSET_NAME ( s1 ) ,
REG_OFFSET_NAME ( a0 ) ,
REG_OFFSET_NAME ( a1 ) ,
REG_OFFSET_NAME ( a2 ) ,
REG_OFFSET_NAME ( a3 ) ,
REG_OFFSET_NAME ( a4 ) ,
REG_OFFSET_NAME ( a5 ) ,
REG_OFFSET_NAME ( a6 ) ,
REG_OFFSET_NAME ( a7 ) ,
REG_OFFSET_NAME ( s2 ) ,
REG_OFFSET_NAME ( s3 ) ,
REG_OFFSET_NAME ( s4 ) ,
REG_OFFSET_NAME ( s5 ) ,
REG_OFFSET_NAME ( s6 ) ,
REG_OFFSET_NAME ( s7 ) ,
REG_OFFSET_NAME ( s8 ) ,
REG_OFFSET_NAME ( s9 ) ,
REG_OFFSET_NAME ( s10 ) ,
REG_OFFSET_NAME ( s11 ) ,
REG_OFFSET_NAME ( t3 ) ,
REG_OFFSET_NAME ( t4 ) ,
REG_OFFSET_NAME ( t5 ) ,
REG_OFFSET_NAME ( t6 ) ,
REG_OFFSET_NAME ( status ) ,
REG_OFFSET_NAME ( badaddr ) ,
REG_OFFSET_NAME ( cause ) ,
REG_OFFSET_NAME ( orig_a0 ) ,
REG_OFFSET_END ,
} ;
/**
* regs_query_register_offset ( ) - query register offset from its name
* @ name : the name of a register
*
* regs_query_register_offset ( ) returns the offset of a register in struct
* pt_regs from its name . If the name is invalid , this returns - EINVAL ;
*/
int regs_query_register_offset ( const char * name )
{
const struct pt_regs_offset * roff ;
for ( roff = regoffset_table ; roff - > name ! = NULL ; roff + + )
if ( ! strcmp ( roff - > name , name ) )
return roff - > offset ;
return - EINVAL ;
}
/**
* regs_within_kernel_stack ( ) - check the address in the stack
* @ regs : pt_regs which contains kernel stack pointer .
* @ addr : address which is checked .
*
* regs_within_kernel_stack ( ) checks @ addr is within the kernel stack page ( s ) .
* If @ addr is within the kernel stack , it returns true . If not , returns false .
*/
static bool regs_within_kernel_stack ( struct pt_regs * regs , unsigned long addr )
{
return ( addr & ~ ( THREAD_SIZE - 1 ) ) = =
( kernel_stack_pointer ( regs ) & ~ ( THREAD_SIZE - 1 ) ) ;
}
/**
* regs_get_kernel_stack_nth ( ) - get Nth entry of the stack
* @ regs : pt_regs which contains kernel stack pointer .
* @ n : stack entry number .
*
* regs_get_kernel_stack_nth ( ) returns @ n th entry of the kernel stack which
* is specified by @ regs . If the @ n th entry is NOT in the kernel stack ,
* this returns 0.
*/
unsigned long regs_get_kernel_stack_nth ( struct pt_regs * regs , unsigned int n )
{
unsigned long * addr = ( unsigned long * ) kernel_stack_pointer ( regs ) ;
addr + = n ;
if ( regs_within_kernel_stack ( regs , ( unsigned long ) addr ) )
return * addr ;
else
return 0 ;
}
2017-07-11 04:07:09 +03:00
void ptrace_disable ( struct task_struct * child )
{
clear_tsk_thread_flag ( child , TIF_SYSCALL_TRACE ) ;
}
long arch_ptrace ( struct task_struct * child , long request ,
unsigned long addr , unsigned long data )
{
long ret = - EIO ;
switch ( request ) {
default :
ret = ptrace_request ( child , request , addr , data ) ;
break ;
}
return ret ;
}
/*
* Allows PTRACE_SYSCALL to work . These are called from entry . S in
* { handle , ret_from } _syscall .
*/
riscv: fix seccomp reject syscall code path
If secure_computing() rejected a system call, we were previously setting
the system call number to -1, to indicate to later code that the syscall
failed. However, if something (e.g. a user notification) was sleeping, and
received a signal, we may set a0 to -ERESTARTSYS and re-try the system call
again.
In this case, seccomp "denies" the syscall (because of the signal), and we
would set a7 to -1, thus losing the value of the system call we want to
restart.
Instead, let's return -1 from do_syscall_trace_enter() to indicate that the
syscall was rejected, so we don't clobber the value in case of -ERESTARTSYS
or whatever.
This commit fixes the user_notification_signal seccomp selftest on riscv to
no longer hang. That test expects the system call to be re-issued after the
signal, and it wasn't due to the above bug. Now that it is, everything
works normally.
Note that in the ptrace (tracer) case, the tracer can set the register
values to whatever they want, so we still need to keep the code that
handles out-of-bounds syscalls. However, we can drop the comment.
We can also drop syscall_set_nr(), since it is no longer used anywhere, and
the code that re-loads the value in a7 because of it.
Reported in: https://lore.kernel.org/bpf/CAEn-LTp=ss0Dfv6J00=rCAy+N78U2AmhqJNjfqjr2FDpPYjxEQ@mail.gmail.com/
Reported-by: David Abdurachmanov <david.abdurachmanov@gmail.com>
Signed-off-by: Tycho Andersen <tycho@tycho.ws>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
2020-02-08 18:18:17 +03:00
__visible int do_syscall_trace_enter ( struct pt_regs * regs )
2017-07-11 04:07:09 +03:00
{
if ( test_thread_flag ( TIF_SYSCALL_TRACE ) )
2022-01-27 20:46:37 +03:00
if ( ptrace_report_syscall_entry ( regs ) )
riscv: fix seccomp reject syscall code path
If secure_computing() rejected a system call, we were previously setting
the system call number to -1, to indicate to later code that the syscall
failed. However, if something (e.g. a user notification) was sleeping, and
received a signal, we may set a0 to -ERESTARTSYS and re-try the system call
again.
In this case, seccomp "denies" the syscall (because of the signal), and we
would set a7 to -1, thus losing the value of the system call we want to
restart.
Instead, let's return -1 from do_syscall_trace_enter() to indicate that the
syscall was rejected, so we don't clobber the value in case of -ERESTARTSYS
or whatever.
This commit fixes the user_notification_signal seccomp selftest on riscv to
no longer hang. That test expects the system call to be re-issued after the
signal, and it wasn't due to the above bug. Now that it is, everything
works normally.
Note that in the ptrace (tracer) case, the tracer can set the register
values to whatever they want, so we still need to keep the code that
handles out-of-bounds syscalls. However, we can drop the comment.
We can also drop syscall_set_nr(), since it is no longer used anywhere, and
the code that re-loads the value in a7 because of it.
Reported in: https://lore.kernel.org/bpf/CAEn-LTp=ss0Dfv6J00=rCAy+N78U2AmhqJNjfqjr2FDpPYjxEQ@mail.gmail.com/
Reported-by: David Abdurachmanov <david.abdurachmanov@gmail.com>
Signed-off-by: Tycho Andersen <tycho@tycho.ws>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
2020-02-08 18:18:17 +03:00
return - 1 ;
2017-07-11 04:07:09 +03:00
2019-10-05 03:12:22 +03:00
/*
* Do the secure computing after ptrace ; failures should be fast .
* If this fails we might have return value in a0 from seccomp
* ( via SECCOMP_RET_ERRNO / TRACE ) .
*/
riscv: fix seccomp reject syscall code path
If secure_computing() rejected a system call, we were previously setting
the system call number to -1, to indicate to later code that the syscall
failed. However, if something (e.g. a user notification) was sleeping, and
received a signal, we may set a0 to -ERESTARTSYS and re-try the system call
again.
In this case, seccomp "denies" the syscall (because of the signal), and we
would set a7 to -1, thus losing the value of the system call we want to
restart.
Instead, let's return -1 from do_syscall_trace_enter() to indicate that the
syscall was rejected, so we don't clobber the value in case of -ERESTARTSYS
or whatever.
This commit fixes the user_notification_signal seccomp selftest on riscv to
no longer hang. That test expects the system call to be re-issued after the
signal, and it wasn't due to the above bug. Now that it is, everything
works normally.
Note that in the ptrace (tracer) case, the tracer can set the register
values to whatever they want, so we still need to keep the code that
handles out-of-bounds syscalls. However, we can drop the comment.
We can also drop syscall_set_nr(), since it is no longer used anywhere, and
the code that re-loads the value in a7 because of it.
Reported in: https://lore.kernel.org/bpf/CAEn-LTp=ss0Dfv6J00=rCAy+N78U2AmhqJNjfqjr2FDpPYjxEQ@mail.gmail.com/
Reported-by: David Abdurachmanov <david.abdurachmanov@gmail.com>
Signed-off-by: Tycho Andersen <tycho@tycho.ws>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
2020-02-08 18:18:17 +03:00
if ( secure_computing ( ) = = - 1 )
return - 1 ;
2019-10-05 03:12:22 +03:00
2017-07-11 04:07:09 +03:00
# ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
if ( test_thread_flag ( TIF_SYSCALL_TRACEPOINT ) )
trace_sys_enter ( regs , syscall_get_nr ( current , regs ) ) ;
# endif
2018-10-29 13:48:54 +03:00
audit_syscall_entry ( regs - > a7 , regs - > a0 , regs - > a1 , regs - > a2 , regs - > a3 ) ;
riscv: fix seccomp reject syscall code path
If secure_computing() rejected a system call, we were previously setting
the system call number to -1, to indicate to later code that the syscall
failed. However, if something (e.g. a user notification) was sleeping, and
received a signal, we may set a0 to -ERESTARTSYS and re-try the system call
again.
In this case, seccomp "denies" the syscall (because of the signal), and we
would set a7 to -1, thus losing the value of the system call we want to
restart.
Instead, let's return -1 from do_syscall_trace_enter() to indicate that the
syscall was rejected, so we don't clobber the value in case of -ERESTARTSYS
or whatever.
This commit fixes the user_notification_signal seccomp selftest on riscv to
no longer hang. That test expects the system call to be re-issued after the
signal, and it wasn't due to the above bug. Now that it is, everything
works normally.
Note that in the ptrace (tracer) case, the tracer can set the register
values to whatever they want, so we still need to keep the code that
handles out-of-bounds syscalls. However, we can drop the comment.
We can also drop syscall_set_nr(), since it is no longer used anywhere, and
the code that re-loads the value in a7 because of it.
Reported in: https://lore.kernel.org/bpf/CAEn-LTp=ss0Dfv6J00=rCAy+N78U2AmhqJNjfqjr2FDpPYjxEQ@mail.gmail.com/
Reported-by: David Abdurachmanov <david.abdurachmanov@gmail.com>
Signed-off-by: Tycho Andersen <tycho@tycho.ws>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
2020-02-08 18:18:17 +03:00
return 0 ;
2017-07-11 04:07:09 +03:00
}
2019-10-18 08:20:05 +03:00
__visible void do_syscall_trace_exit ( struct pt_regs * regs )
2017-07-11 04:07:09 +03:00
{
2018-10-29 13:48:54 +03:00
audit_syscall_exit ( regs ) ;
2017-07-11 04:07:09 +03:00
if ( test_thread_flag ( TIF_SYSCALL_TRACE ) )
2022-01-27 20:46:37 +03:00
ptrace_report_syscall_exit ( regs , 0 ) ;
2017-07-11 04:07:09 +03:00
# ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
if ( test_thread_flag ( TIF_SYSCALL_TRACEPOINT ) )
2018-12-06 18:26:34 +03:00
trace_sys_exit ( regs , regs_return_value ( regs ) ) ;
2017-07-11 04:07:09 +03:00
# endif
}