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

step.c (6399B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * x86 single-step support code, common to 32-bit and 64-bit.
      4 */
      5#include <linux/sched.h>
      6#include <linux/sched/task_stack.h>
      7#include <linux/mm.h>
      8#include <linux/ptrace.h>
      9#include <asm/desc.h>
     10#include <asm/mmu_context.h>
     11
     12unsigned long convert_ip_to_linear(struct task_struct *child, struct pt_regs *regs)
     13{
     14	unsigned long addr, seg;
     15
     16	addr = regs->ip;
     17	seg = regs->cs;
     18	if (v8086_mode(regs)) {
     19		addr = (addr & 0xffff) + (seg << 4);
     20		return addr;
     21	}
     22
     23#ifdef CONFIG_MODIFY_LDT_SYSCALL
     24	/*
     25	 * We'll assume that the code segments in the GDT
     26	 * are all zero-based. That is largely true: the
     27	 * TLS segments are used for data, and the PNPBIOS
     28	 * and APM bios ones we just ignore here.
     29	 */
     30	if ((seg & SEGMENT_TI_MASK) == SEGMENT_LDT) {
     31		struct desc_struct *desc;
     32		unsigned long base;
     33
     34		seg >>= 3;
     35
     36		mutex_lock(&child->mm->context.lock);
     37		if (unlikely(!child->mm->context.ldt ||
     38			     seg >= child->mm->context.ldt->nr_entries))
     39			addr = -1L; /* bogus selector, access would fault */
     40		else {
     41			desc = &child->mm->context.ldt->entries[seg];
     42			base = get_desc_base(desc);
     43
     44			/* 16-bit code segment? */
     45			if (!desc->d)
     46				addr &= 0xffff;
     47			addr += base;
     48		}
     49		mutex_unlock(&child->mm->context.lock);
     50	}
     51#endif
     52
     53	return addr;
     54}
     55
     56static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs)
     57{
     58	int i, copied;
     59	unsigned char opcode[15];
     60	unsigned long addr = convert_ip_to_linear(child, regs);
     61
     62	copied = access_process_vm(child, addr, opcode, sizeof(opcode),
     63			FOLL_FORCE);
     64	for (i = 0; i < copied; i++) {
     65		switch (opcode[i]) {
     66		/* popf and iret */
     67		case 0x9d: case 0xcf:
     68			return 1;
     69
     70			/* CHECKME: 64 65 */
     71
     72		/* opcode and address size prefixes */
     73		case 0x66: case 0x67:
     74			continue;
     75		/* irrelevant prefixes (segment overrides and repeats) */
     76		case 0x26: case 0x2e:
     77		case 0x36: case 0x3e:
     78		case 0x64: case 0x65:
     79		case 0xf0: case 0xf2: case 0xf3:
     80			continue;
     81
     82#ifdef CONFIG_X86_64
     83		case 0x40 ... 0x4f:
     84			if (!user_64bit_mode(regs))
     85				/* 32-bit mode: register increment */
     86				return 0;
     87			/* 64-bit mode: REX prefix */
     88			continue;
     89#endif
     90
     91			/* CHECKME: f2, f3 */
     92
     93		/*
     94		 * pushf: NOTE! We should probably not let
     95		 * the user see the TF bit being set. But
     96		 * it's more pain than it's worth to avoid
     97		 * it, and a debugger could emulate this
     98		 * all in user space if it _really_ cares.
     99		 */
    100		case 0x9c:
    101		default:
    102			return 0;
    103		}
    104	}
    105	return 0;
    106}
    107
    108/*
    109 * Enable single-stepping.  Return nonzero if user mode is not using TF itself.
    110 */
    111static int enable_single_step(struct task_struct *child)
    112{
    113	struct pt_regs *regs = task_pt_regs(child);
    114	unsigned long oflags;
    115
    116	/*
    117	 * If we stepped into a sysenter/syscall insn, it trapped in
    118	 * kernel mode; do_debug() cleared TF and set TIF_SINGLESTEP.
    119	 * If user-mode had set TF itself, then it's still clear from
    120	 * do_debug() and we need to set it again to restore the user
    121	 * state so we don't wrongly set TIF_FORCED_TF below.
    122	 * If enable_single_step() was used last and that is what
    123	 * set TIF_SINGLESTEP, then both TF and TIF_FORCED_TF are
    124	 * already set and our bookkeeping is fine.
    125	 */
    126	if (unlikely(test_tsk_thread_flag(child, TIF_SINGLESTEP)))
    127		regs->flags |= X86_EFLAGS_TF;
    128
    129	/*
    130	 * Always set TIF_SINGLESTEP.  This will also
    131	 * cause us to set TF when returning to user mode.
    132	 */
    133	set_tsk_thread_flag(child, TIF_SINGLESTEP);
    134
    135	/*
    136	 * Ensure that a trap is triggered once stepping out of a system
    137	 * call prior to executing any user instruction.
    138	 */
    139	set_task_syscall_work(child, SYSCALL_EXIT_TRAP);
    140
    141	oflags = regs->flags;
    142
    143	/* Set TF on the kernel stack.. */
    144	regs->flags |= X86_EFLAGS_TF;
    145
    146	/*
    147	 * ..but if TF is changed by the instruction we will trace,
    148	 * don't mark it as being "us" that set it, so that we
    149	 * won't clear it by hand later.
    150	 *
    151	 * Note that if we don't actually execute the popf because
    152	 * of a signal arriving right now or suchlike, we will lose
    153	 * track of the fact that it really was "us" that set it.
    154	 */
    155	if (is_setting_trap_flag(child, regs)) {
    156		clear_tsk_thread_flag(child, TIF_FORCED_TF);
    157		return 0;
    158	}
    159
    160	/*
    161	 * If TF was already set, check whether it was us who set it.
    162	 * If not, we should never attempt a block step.
    163	 */
    164	if (oflags & X86_EFLAGS_TF)
    165		return test_tsk_thread_flag(child, TIF_FORCED_TF);
    166
    167	set_tsk_thread_flag(child, TIF_FORCED_TF);
    168
    169	return 1;
    170}
    171
    172void set_task_blockstep(struct task_struct *task, bool on)
    173{
    174	unsigned long debugctl;
    175
    176	/*
    177	 * Ensure irq/preemption can't change debugctl in between.
    178	 * Note also that both TIF_BLOCKSTEP and debugctl should
    179	 * be changed atomically wrt preemption.
    180	 *
    181	 * NOTE: this means that set/clear TIF_BLOCKSTEP is only safe if
    182	 * task is current or it can't be running, otherwise we can race
    183	 * with __switch_to_xtra(). We rely on ptrace_freeze_traced().
    184	 */
    185	local_irq_disable();
    186	debugctl = get_debugctlmsr();
    187	if (on) {
    188		debugctl |= DEBUGCTLMSR_BTF;
    189		set_tsk_thread_flag(task, TIF_BLOCKSTEP);
    190	} else {
    191		debugctl &= ~DEBUGCTLMSR_BTF;
    192		clear_tsk_thread_flag(task, TIF_BLOCKSTEP);
    193	}
    194	if (task == current)
    195		update_debugctlmsr(debugctl);
    196	local_irq_enable();
    197}
    198
    199/*
    200 * Enable single or block step.
    201 */
    202static void enable_step(struct task_struct *child, bool block)
    203{
    204	/*
    205	 * Make sure block stepping (BTF) is not enabled unless it should be.
    206	 * Note that we don't try to worry about any is_setting_trap_flag()
    207	 * instructions after the first when using block stepping.
    208	 * So no one should try to use debugger block stepping in a program
    209	 * that uses user-mode single stepping itself.
    210	 */
    211	if (enable_single_step(child) && block)
    212		set_task_blockstep(child, true);
    213	else if (test_tsk_thread_flag(child, TIF_BLOCKSTEP))
    214		set_task_blockstep(child, false);
    215}
    216
    217void user_enable_single_step(struct task_struct *child)
    218{
    219	enable_step(child, 0);
    220}
    221
    222void user_enable_block_step(struct task_struct *child)
    223{
    224	enable_step(child, 1);
    225}
    226
    227void user_disable_single_step(struct task_struct *child)
    228{
    229	/*
    230	 * Make sure block stepping (BTF) is disabled.
    231	 */
    232	if (test_tsk_thread_flag(child, TIF_BLOCKSTEP))
    233		set_task_blockstep(child, false);
    234
    235	/* Always clear TIF_SINGLESTEP... */
    236	clear_tsk_thread_flag(child, TIF_SINGLESTEP);
    237	clear_task_syscall_work(child, SYSCALL_EXIT_TRAP);
    238
    239	/* But touch TF only if it was set by us.. */
    240	if (test_and_clear_tsk_thread_flag(child, TIF_FORCED_TF))
    241		task_pt_regs(child)->flags &= ~X86_EFLAGS_TF;
    242}