cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ptrace_32.c (11670B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * SuperH process tracing
      4 *
      5 * Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
      6 * Copyright (C) 2002 - 2009  Paul Mundt
      7 *
      8 * Audit support by Yuichi Nakamura <ynakam@hitachisoft.jp>
      9 */
     10#include <linux/kernel.h>
     11#include <linux/sched.h>
     12#include <linux/sched/task_stack.h>
     13#include <linux/mm.h>
     14#include <linux/smp.h>
     15#include <linux/errno.h>
     16#include <linux/ptrace.h>
     17#include <linux/user.h>
     18#include <linux/security.h>
     19#include <linux/signal.h>
     20#include <linux/io.h>
     21#include <linux/audit.h>
     22#include <linux/seccomp.h>
     23#include <linux/elf.h>
     24#include <linux/regset.h>
     25#include <linux/hw_breakpoint.h>
     26#include <linux/uaccess.h>
     27#include <asm/processor.h>
     28#include <asm/mmu_context.h>
     29#include <asm/syscalls.h>
     30#include <asm/fpu.h>
     31
     32#define CREATE_TRACE_POINTS
     33#include <trace/events/syscalls.h>
     34
     35/*
     36 * This routine will get a word off of the process kernel stack.
     37 */
     38static inline int get_stack_long(struct task_struct *task, int offset)
     39{
     40	unsigned char *stack;
     41
     42	stack = (unsigned char *)task_pt_regs(task);
     43	stack += offset;
     44	return (*((int *)stack));
     45}
     46
     47/*
     48 * This routine will put a word on the process kernel stack.
     49 */
     50static inline int put_stack_long(struct task_struct *task, int offset,
     51				 unsigned long data)
     52{
     53	unsigned char *stack;
     54
     55	stack = (unsigned char *)task_pt_regs(task);
     56	stack += offset;
     57	*(unsigned long *) stack = data;
     58	return 0;
     59}
     60
     61void ptrace_triggered(struct perf_event *bp,
     62		      struct perf_sample_data *data, struct pt_regs *regs)
     63{
     64	struct perf_event_attr attr;
     65
     66	/*
     67	 * Disable the breakpoint request here since ptrace has defined a
     68	 * one-shot behaviour for breakpoint exceptions.
     69	 */
     70	attr = bp->attr;
     71	attr.disabled = true;
     72	modify_user_hw_breakpoint(bp, &attr);
     73}
     74
     75static int set_single_step(struct task_struct *tsk, unsigned long addr)
     76{
     77	struct thread_struct *thread = &tsk->thread;
     78	struct perf_event *bp;
     79	struct perf_event_attr attr;
     80
     81	bp = thread->ptrace_bps[0];
     82	if (!bp) {
     83		ptrace_breakpoint_init(&attr);
     84
     85		attr.bp_addr = addr;
     86		attr.bp_len = HW_BREAKPOINT_LEN_2;
     87		attr.bp_type = HW_BREAKPOINT_R;
     88
     89		bp = register_user_hw_breakpoint(&attr, ptrace_triggered,
     90						 NULL, tsk);
     91		if (IS_ERR(bp))
     92			return PTR_ERR(bp);
     93
     94		thread->ptrace_bps[0] = bp;
     95	} else {
     96		int err;
     97
     98		attr = bp->attr;
     99		attr.bp_addr = addr;
    100		/* reenable breakpoint */
    101		attr.disabled = false;
    102		err = modify_user_hw_breakpoint(bp, &attr);
    103		if (unlikely(err))
    104			return err;
    105	}
    106
    107	return 0;
    108}
    109
    110void user_enable_single_step(struct task_struct *child)
    111{
    112	unsigned long pc = get_stack_long(child, offsetof(struct pt_regs, pc));
    113
    114	set_tsk_thread_flag(child, TIF_SINGLESTEP);
    115
    116	set_single_step(child, pc);
    117}
    118
    119void user_disable_single_step(struct task_struct *child)
    120{
    121	clear_tsk_thread_flag(child, TIF_SINGLESTEP);
    122}
    123
    124/*
    125 * Called by kernel/ptrace.c when detaching..
    126 *
    127 * Make sure single step bits etc are not set.
    128 */
    129void ptrace_disable(struct task_struct *child)
    130{
    131	user_disable_single_step(child);
    132}
    133
    134static int genregs_get(struct task_struct *target,
    135		       const struct user_regset *regset,
    136		       struct membuf to)
    137{
    138	const struct pt_regs *regs = task_pt_regs(target);
    139
    140	return membuf_write(&to, regs, sizeof(struct pt_regs));
    141}
    142
    143static int genregs_set(struct task_struct *target,
    144		       const struct user_regset *regset,
    145		       unsigned int pos, unsigned int count,
    146		       const void *kbuf, const void __user *ubuf)
    147{
    148	struct pt_regs *regs = task_pt_regs(target);
    149	int ret;
    150
    151	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
    152				 regs->regs,
    153				 0, 16 * sizeof(unsigned long));
    154	if (!ret && count > 0)
    155		ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
    156					 &regs->pc,
    157					 offsetof(struct pt_regs, pc),
    158					 sizeof(struct pt_regs));
    159	if (!ret)
    160		ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
    161						sizeof(struct pt_regs), -1);
    162
    163	return ret;
    164}
    165
    166#ifdef CONFIG_SH_FPU
    167static int fpregs_get(struct task_struct *target,
    168	       const struct user_regset *regset,
    169	       struct membuf to)
    170{
    171	int ret;
    172
    173	ret = init_fpu(target);
    174	if (ret)
    175		return ret;
    176
    177	return membuf_write(&to, target->thread.xstate,
    178			    sizeof(struct user_fpu_struct));
    179}
    180
    181static int fpregs_set(struct task_struct *target,
    182		       const struct user_regset *regset,
    183		       unsigned int pos, unsigned int count,
    184		       const void *kbuf, const void __user *ubuf)
    185{
    186	int ret;
    187
    188	ret = init_fpu(target);
    189	if (ret)
    190		return ret;
    191
    192	set_stopped_child_used_math(target);
    193
    194	if ((boot_cpu_data.flags & CPU_HAS_FPU))
    195		return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
    196					  &target->thread.xstate->hardfpu, 0, -1);
    197
    198	return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
    199				  &target->thread.xstate->softfpu, 0, -1);
    200}
    201
    202static int fpregs_active(struct task_struct *target,
    203			 const struct user_regset *regset)
    204{
    205	return tsk_used_math(target) ? regset->n : 0;
    206}
    207#endif
    208
    209#ifdef CONFIG_SH_DSP
    210static int dspregs_get(struct task_struct *target,
    211		       const struct user_regset *regset,
    212		       struct membuf to)
    213{
    214	const struct pt_dspregs *regs =
    215		(struct pt_dspregs *)&target->thread.dsp_status.dsp_regs;
    216
    217	return membuf_write(&to, regs, sizeof(struct pt_dspregs));
    218}
    219
    220static int dspregs_set(struct task_struct *target,
    221		       const struct user_regset *regset,
    222		       unsigned int pos, unsigned int count,
    223		       const void *kbuf, const void __user *ubuf)
    224{
    225	struct pt_dspregs *regs =
    226		(struct pt_dspregs *)&target->thread.dsp_status.dsp_regs;
    227	int ret;
    228
    229	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs,
    230				 0, sizeof(struct pt_dspregs));
    231	if (!ret)
    232		ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
    233						sizeof(struct pt_dspregs), -1);
    234
    235	return ret;
    236}
    237
    238static int dspregs_active(struct task_struct *target,
    239			  const struct user_regset *regset)
    240{
    241	struct pt_regs *regs = task_pt_regs(target);
    242
    243	return regs->sr & SR_DSP ? regset->n : 0;
    244}
    245#endif
    246
    247const struct pt_regs_offset regoffset_table[] = {
    248	REGS_OFFSET_NAME(0),
    249	REGS_OFFSET_NAME(1),
    250	REGS_OFFSET_NAME(2),
    251	REGS_OFFSET_NAME(3),
    252	REGS_OFFSET_NAME(4),
    253	REGS_OFFSET_NAME(5),
    254	REGS_OFFSET_NAME(6),
    255	REGS_OFFSET_NAME(7),
    256	REGS_OFFSET_NAME(8),
    257	REGS_OFFSET_NAME(9),
    258	REGS_OFFSET_NAME(10),
    259	REGS_OFFSET_NAME(11),
    260	REGS_OFFSET_NAME(12),
    261	REGS_OFFSET_NAME(13),
    262	REGS_OFFSET_NAME(14),
    263	REGS_OFFSET_NAME(15),
    264	REG_OFFSET_NAME(pc),
    265	REG_OFFSET_NAME(pr),
    266	REG_OFFSET_NAME(sr),
    267	REG_OFFSET_NAME(gbr),
    268	REG_OFFSET_NAME(mach),
    269	REG_OFFSET_NAME(macl),
    270	REG_OFFSET_NAME(tra),
    271	REG_OFFSET_END,
    272};
    273
    274/*
    275 * These are our native regset flavours.
    276 */
    277enum sh_regset {
    278	REGSET_GENERAL,
    279#ifdef CONFIG_SH_FPU
    280	REGSET_FPU,
    281#endif
    282#ifdef CONFIG_SH_DSP
    283	REGSET_DSP,
    284#endif
    285};
    286
    287static const struct user_regset sh_regsets[] = {
    288	/*
    289	 * Format is:
    290	 *	R0 --> R15
    291	 *	PC, PR, SR, GBR, MACH, MACL, TRA
    292	 */
    293	[REGSET_GENERAL] = {
    294		.core_note_type	= NT_PRSTATUS,
    295		.n		= ELF_NGREG,
    296		.size		= sizeof(long),
    297		.align		= sizeof(long),
    298		.regset_get		= genregs_get,
    299		.set		= genregs_set,
    300	},
    301
    302#ifdef CONFIG_SH_FPU
    303	[REGSET_FPU] = {
    304		.core_note_type	= NT_PRFPREG,
    305		.n		= sizeof(struct user_fpu_struct) / sizeof(long),
    306		.size		= sizeof(long),
    307		.align		= sizeof(long),
    308		.regset_get		= fpregs_get,
    309		.set		= fpregs_set,
    310		.active		= fpregs_active,
    311	},
    312#endif
    313
    314#ifdef CONFIG_SH_DSP
    315	[REGSET_DSP] = {
    316		.n		= sizeof(struct pt_dspregs) / sizeof(long),
    317		.size		= sizeof(long),
    318		.align		= sizeof(long),
    319		.regset_get		= dspregs_get,
    320		.set		= dspregs_set,
    321		.active		= dspregs_active,
    322	},
    323#endif
    324};
    325
    326static const struct user_regset_view user_sh_native_view = {
    327	.name		= "sh",
    328	.e_machine	= EM_SH,
    329	.regsets	= sh_regsets,
    330	.n		= ARRAY_SIZE(sh_regsets),
    331};
    332
    333const struct user_regset_view *task_user_regset_view(struct task_struct *task)
    334{
    335	return &user_sh_native_view;
    336}
    337
    338long arch_ptrace(struct task_struct *child, long request,
    339		 unsigned long addr, unsigned long data)
    340{
    341	unsigned long __user *datap = (unsigned long __user *)data;
    342	int ret;
    343
    344	switch (request) {
    345	/* read the word at location addr in the USER area. */
    346	case PTRACE_PEEKUSR: {
    347		unsigned long tmp;
    348
    349		ret = -EIO;
    350		if ((addr & 3) || addr < 0 ||
    351		    addr > sizeof(struct user) - 3)
    352			break;
    353
    354		if (addr < sizeof(struct pt_regs))
    355			tmp = get_stack_long(child, addr);
    356		else if (addr >= offsetof(struct user, fpu) &&
    357			 addr < offsetof(struct user, u_fpvalid)) {
    358			if (!tsk_used_math(child)) {
    359				if (addr == offsetof(struct user, fpu.fpscr))
    360					tmp = FPSCR_INIT;
    361				else
    362					tmp = 0;
    363			} else {
    364				unsigned long index;
    365				ret = init_fpu(child);
    366				if (ret)
    367					break;
    368				index = addr - offsetof(struct user, fpu);
    369				tmp = ((unsigned long *)child->thread.xstate)
    370					[index >> 2];
    371			}
    372		} else if (addr == offsetof(struct user, u_fpvalid))
    373			tmp = !!tsk_used_math(child);
    374		else if (addr == PT_TEXT_ADDR)
    375			tmp = child->mm->start_code;
    376		else if (addr == PT_DATA_ADDR)
    377			tmp = child->mm->start_data;
    378		else if (addr == PT_TEXT_END_ADDR)
    379			tmp = child->mm->end_code;
    380		else if (addr == PT_TEXT_LEN)
    381			tmp = child->mm->end_code - child->mm->start_code;
    382		else
    383			tmp = 0;
    384		ret = put_user(tmp, datap);
    385		break;
    386	}
    387
    388	case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
    389		ret = -EIO;
    390		if ((addr & 3) || addr < 0 ||
    391		    addr > sizeof(struct user) - 3)
    392			break;
    393
    394		if (addr < sizeof(struct pt_regs))
    395			ret = put_stack_long(child, addr, data);
    396		else if (addr >= offsetof(struct user, fpu) &&
    397			 addr < offsetof(struct user, u_fpvalid)) {
    398			unsigned long index;
    399			ret = init_fpu(child);
    400			if (ret)
    401				break;
    402			index = addr - offsetof(struct user, fpu);
    403			set_stopped_child_used_math(child);
    404			((unsigned long *)child->thread.xstate)
    405				[index >> 2] = data;
    406			ret = 0;
    407		} else if (addr == offsetof(struct user, u_fpvalid)) {
    408			conditional_stopped_child_used_math(data, child);
    409			ret = 0;
    410		}
    411		break;
    412
    413	case PTRACE_GETREGS:
    414		return copy_regset_to_user(child, &user_sh_native_view,
    415					   REGSET_GENERAL,
    416					   0, sizeof(struct pt_regs),
    417					   datap);
    418	case PTRACE_SETREGS:
    419		return copy_regset_from_user(child, &user_sh_native_view,
    420					     REGSET_GENERAL,
    421					     0, sizeof(struct pt_regs),
    422					     datap);
    423#ifdef CONFIG_SH_FPU
    424	case PTRACE_GETFPREGS:
    425		return copy_regset_to_user(child, &user_sh_native_view,
    426					   REGSET_FPU,
    427					   0, sizeof(struct user_fpu_struct),
    428					   datap);
    429	case PTRACE_SETFPREGS:
    430		return copy_regset_from_user(child, &user_sh_native_view,
    431					     REGSET_FPU,
    432					     0, sizeof(struct user_fpu_struct),
    433					     datap);
    434#endif
    435#ifdef CONFIG_SH_DSP
    436	case PTRACE_GETDSPREGS:
    437		return copy_regset_to_user(child, &user_sh_native_view,
    438					   REGSET_DSP,
    439					   0, sizeof(struct pt_dspregs),
    440					   datap);
    441	case PTRACE_SETDSPREGS:
    442		return copy_regset_from_user(child, &user_sh_native_view,
    443					     REGSET_DSP,
    444					     0, sizeof(struct pt_dspregs),
    445					     datap);
    446#endif
    447	default:
    448		ret = ptrace_request(child, request, addr, data);
    449		break;
    450	}
    451
    452	return ret;
    453}
    454
    455asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
    456{
    457	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
    458	    ptrace_report_syscall_entry(regs)) {
    459		regs->regs[0] = -ENOSYS;
    460		return -1;
    461	}
    462
    463	if (secure_computing() == -1)
    464		return -1;
    465
    466	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
    467		trace_sys_enter(regs, regs->regs[0]);
    468
    469	audit_syscall_entry(regs->regs[3], regs->regs[4], regs->regs[5],
    470			    regs->regs[6], regs->regs[7]);
    471
    472	return 0;
    473}
    474
    475asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
    476{
    477	int step;
    478
    479	audit_syscall_exit(regs);
    480
    481	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
    482		trace_sys_exit(regs, regs->regs[0]);
    483
    484	step = test_thread_flag(TIF_SINGLESTEP);
    485	if (step || test_thread_flag(TIF_SYSCALL_TRACE))
    486		ptrace_report_syscall_exit(regs, step);
    487}