xref: /kernel/linux/linux-5.10/arch/x86/um/ptrace_32.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
38c2ecf20Sopenharmony_ci * Licensed under the GPL
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/mm.h>
78c2ecf20Sopenharmony_ci#include <linux/sched.h>
88c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
98c2ecf20Sopenharmony_ci#include <asm/ptrace-abi.h>
108c2ecf20Sopenharmony_ci#include <skas.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ciextern int arch_switch_tls(struct task_struct *to);
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_civoid arch_switch_to(struct task_struct *to)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	int err = arch_switch_tls(to);
178c2ecf20Sopenharmony_ci	if (!err)
188c2ecf20Sopenharmony_ci		return;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	if (err != -EINVAL)
218c2ecf20Sopenharmony_ci		printk(KERN_WARNING "arch_switch_tls failed, errno %d, "
228c2ecf20Sopenharmony_ci		       "not EINVAL\n", -err);
238c2ecf20Sopenharmony_ci	else
248c2ecf20Sopenharmony_ci		printk(KERN_WARNING "arch_switch_tls failed, errno = EINVAL\n");
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciint is_syscall(unsigned long addr)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	unsigned short instr;
308c2ecf20Sopenharmony_ci	int n;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	n = copy_from_user(&instr, (void __user *) addr, sizeof(instr));
338c2ecf20Sopenharmony_ci	if (n) {
348c2ecf20Sopenharmony_ci		/* access_process_vm() grants access to vsyscall and stub,
358c2ecf20Sopenharmony_ci		 * while copy_from_user doesn't. Maybe access_process_vm is
368c2ecf20Sopenharmony_ci		 * slow, but that doesn't matter, since it will be called only
378c2ecf20Sopenharmony_ci		 * in case of singlestepping, if copy_from_user failed.
388c2ecf20Sopenharmony_ci		 */
398c2ecf20Sopenharmony_ci		n = access_process_vm(current, addr, &instr, sizeof(instr),
408c2ecf20Sopenharmony_ci				FOLL_FORCE);
418c2ecf20Sopenharmony_ci		if (n != sizeof(instr)) {
428c2ecf20Sopenharmony_ci			printk(KERN_ERR "is_syscall : failed to read "
438c2ecf20Sopenharmony_ci			       "instruction from 0x%lx\n", addr);
448c2ecf20Sopenharmony_ci			return 1;
458c2ecf20Sopenharmony_ci		}
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci	/* int 0x80 or sysenter */
488c2ecf20Sopenharmony_ci	return (instr == 0x80cd) || (instr == 0x340f);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* determines which flags the user has access to. */
528c2ecf20Sopenharmony_ci/* 1 = access 0 = no access */
538c2ecf20Sopenharmony_ci#define FLAG_MASK 0x00044dd5
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic const int reg_offsets[] = {
568c2ecf20Sopenharmony_ci	[EBX] = HOST_BX,
578c2ecf20Sopenharmony_ci	[ECX] = HOST_CX,
588c2ecf20Sopenharmony_ci	[EDX] = HOST_DX,
598c2ecf20Sopenharmony_ci	[ESI] = HOST_SI,
608c2ecf20Sopenharmony_ci	[EDI] = HOST_DI,
618c2ecf20Sopenharmony_ci	[EBP] = HOST_BP,
628c2ecf20Sopenharmony_ci	[EAX] = HOST_AX,
638c2ecf20Sopenharmony_ci	[DS] = HOST_DS,
648c2ecf20Sopenharmony_ci	[ES] = HOST_ES,
658c2ecf20Sopenharmony_ci	[FS] = HOST_FS,
668c2ecf20Sopenharmony_ci	[GS] = HOST_GS,
678c2ecf20Sopenharmony_ci	[EIP] = HOST_IP,
688c2ecf20Sopenharmony_ci	[CS] = HOST_CS,
698c2ecf20Sopenharmony_ci	[EFL] = HOST_EFLAGS,
708c2ecf20Sopenharmony_ci	[UESP] = HOST_SP,
718c2ecf20Sopenharmony_ci	[SS] = HOST_SS,
728c2ecf20Sopenharmony_ci	[ORIG_EAX] = HOST_ORIG_AX,
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ciint putreg(struct task_struct *child, int regno, unsigned long value)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	regno >>= 2;
788c2ecf20Sopenharmony_ci	switch (regno) {
798c2ecf20Sopenharmony_ci	case EBX:
808c2ecf20Sopenharmony_ci	case ECX:
818c2ecf20Sopenharmony_ci	case EDX:
828c2ecf20Sopenharmony_ci	case ESI:
838c2ecf20Sopenharmony_ci	case EDI:
848c2ecf20Sopenharmony_ci	case EBP:
858c2ecf20Sopenharmony_ci	case EAX:
868c2ecf20Sopenharmony_ci	case EIP:
878c2ecf20Sopenharmony_ci	case UESP:
888c2ecf20Sopenharmony_ci		break;
898c2ecf20Sopenharmony_ci	case ORIG_EAX:
908c2ecf20Sopenharmony_ci		/* Update the syscall number. */
918c2ecf20Sopenharmony_ci		UPT_SYSCALL_NR(&child->thread.regs.regs) = value;
928c2ecf20Sopenharmony_ci		break;
938c2ecf20Sopenharmony_ci	case FS:
948c2ecf20Sopenharmony_ci		if (value && (value & 3) != 3)
958c2ecf20Sopenharmony_ci			return -EIO;
968c2ecf20Sopenharmony_ci		break;
978c2ecf20Sopenharmony_ci	case GS:
988c2ecf20Sopenharmony_ci		if (value && (value & 3) != 3)
998c2ecf20Sopenharmony_ci			return -EIO;
1008c2ecf20Sopenharmony_ci		break;
1018c2ecf20Sopenharmony_ci	case DS:
1028c2ecf20Sopenharmony_ci	case ES:
1038c2ecf20Sopenharmony_ci		if (value && (value & 3) != 3)
1048c2ecf20Sopenharmony_ci			return -EIO;
1058c2ecf20Sopenharmony_ci		value &= 0xffff;
1068c2ecf20Sopenharmony_ci		break;
1078c2ecf20Sopenharmony_ci	case SS:
1088c2ecf20Sopenharmony_ci	case CS:
1098c2ecf20Sopenharmony_ci		if ((value & 3) != 3)
1108c2ecf20Sopenharmony_ci			return -EIO;
1118c2ecf20Sopenharmony_ci		value &= 0xffff;
1128c2ecf20Sopenharmony_ci		break;
1138c2ecf20Sopenharmony_ci	case EFL:
1148c2ecf20Sopenharmony_ci		value &= FLAG_MASK;
1158c2ecf20Sopenharmony_ci		child->thread.regs.regs.gp[HOST_EFLAGS] |= value;
1168c2ecf20Sopenharmony_ci		return 0;
1178c2ecf20Sopenharmony_ci	default :
1188c2ecf20Sopenharmony_ci		panic("Bad register in putreg() : %d\n", regno);
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci	child->thread.regs.regs.gp[reg_offsets[regno]] = value;
1218c2ecf20Sopenharmony_ci	return 0;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ciint poke_user(struct task_struct *child, long addr, long data)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	if ((addr & 3) || addr < 0)
1278c2ecf20Sopenharmony_ci		return -EIO;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (addr < MAX_REG_OFFSET)
1308c2ecf20Sopenharmony_ci		return putreg(child, addr, data);
1318c2ecf20Sopenharmony_ci	else if ((addr >= offsetof(struct user, u_debugreg[0])) &&
1328c2ecf20Sopenharmony_ci		 (addr <= offsetof(struct user, u_debugreg[7]))) {
1338c2ecf20Sopenharmony_ci		addr -= offsetof(struct user, u_debugreg[0]);
1348c2ecf20Sopenharmony_ci		addr = addr >> 2;
1358c2ecf20Sopenharmony_ci		if ((addr == 4) || (addr == 5))
1368c2ecf20Sopenharmony_ci			return -EIO;
1378c2ecf20Sopenharmony_ci		child->thread.arch.debugregs[addr] = data;
1388c2ecf20Sopenharmony_ci		return 0;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci	return -EIO;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ciunsigned long getreg(struct task_struct *child, int regno)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	unsigned long mask = ~0UL;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	regno >>= 2;
1488c2ecf20Sopenharmony_ci	switch (regno) {
1498c2ecf20Sopenharmony_ci	case FS:
1508c2ecf20Sopenharmony_ci	case GS:
1518c2ecf20Sopenharmony_ci	case DS:
1528c2ecf20Sopenharmony_ci	case ES:
1538c2ecf20Sopenharmony_ci	case SS:
1548c2ecf20Sopenharmony_ci	case CS:
1558c2ecf20Sopenharmony_ci		mask = 0xffff;
1568c2ecf20Sopenharmony_ci		break;
1578c2ecf20Sopenharmony_ci	case EIP:
1588c2ecf20Sopenharmony_ci	case UESP:
1598c2ecf20Sopenharmony_ci	case EAX:
1608c2ecf20Sopenharmony_ci	case EBX:
1618c2ecf20Sopenharmony_ci	case ECX:
1628c2ecf20Sopenharmony_ci	case EDX:
1638c2ecf20Sopenharmony_ci	case ESI:
1648c2ecf20Sopenharmony_ci	case EDI:
1658c2ecf20Sopenharmony_ci	case EBP:
1668c2ecf20Sopenharmony_ci	case EFL:
1678c2ecf20Sopenharmony_ci	case ORIG_EAX:
1688c2ecf20Sopenharmony_ci		break;
1698c2ecf20Sopenharmony_ci	default:
1708c2ecf20Sopenharmony_ci		panic("Bad register in getreg() : %d\n", regno);
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	return mask & child->thread.regs.regs.gp[reg_offsets[regno]];
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/* read the word at location addr in the USER area. */
1768c2ecf20Sopenharmony_ciint peek_user(struct task_struct *child, long addr, long data)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	unsigned long tmp;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if ((addr & 3) || addr < 0)
1818c2ecf20Sopenharmony_ci		return -EIO;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	tmp = 0;  /* Default return condition */
1848c2ecf20Sopenharmony_ci	if (addr < MAX_REG_OFFSET) {
1858c2ecf20Sopenharmony_ci		tmp = getreg(child, addr);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci	else if ((addr >= offsetof(struct user, u_debugreg[0])) &&
1888c2ecf20Sopenharmony_ci		 (addr <= offsetof(struct user, u_debugreg[7]))) {
1898c2ecf20Sopenharmony_ci		addr -= offsetof(struct user, u_debugreg[0]);
1908c2ecf20Sopenharmony_ci		addr = addr >> 2;
1918c2ecf20Sopenharmony_ci		tmp = child->thread.arch.debugregs[addr];
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci	return put_user(tmp, (unsigned long __user *) data);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	int err, n, cpu = task_cpu(child);
1998c2ecf20Sopenharmony_ci	struct user_i387_struct fpregs;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	err = save_i387_registers(userspace_pid[cpu],
2028c2ecf20Sopenharmony_ci				  (unsigned long *) &fpregs);
2038c2ecf20Sopenharmony_ci	if (err)
2048c2ecf20Sopenharmony_ci		return err;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	n = copy_to_user(buf, &fpregs, sizeof(fpregs));
2078c2ecf20Sopenharmony_ci	if(n > 0)
2088c2ecf20Sopenharmony_ci		return -EFAULT;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return n;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	int n, cpu = task_cpu(child);
2168c2ecf20Sopenharmony_ci	struct user_i387_struct fpregs;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	n = copy_from_user(&fpregs, buf, sizeof(fpregs));
2198c2ecf20Sopenharmony_ci	if (n > 0)
2208c2ecf20Sopenharmony_ci		return -EFAULT;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return restore_i387_registers(userspace_pid[cpu],
2238c2ecf20Sopenharmony_ci				    (unsigned long *) &fpregs);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	int err, n, cpu = task_cpu(child);
2298c2ecf20Sopenharmony_ci	struct user_fxsr_struct fpregs;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	err = save_fpx_registers(userspace_pid[cpu], (unsigned long *) &fpregs);
2328c2ecf20Sopenharmony_ci	if (err)
2338c2ecf20Sopenharmony_ci		return err;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	n = copy_to_user(buf, &fpregs, sizeof(fpregs));
2368c2ecf20Sopenharmony_ci	if(n > 0)
2378c2ecf20Sopenharmony_ci		return -EFAULT;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return n;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic int set_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	int n, cpu = task_cpu(child);
2458c2ecf20Sopenharmony_ci	struct user_fxsr_struct fpregs;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	n = copy_from_user(&fpregs, buf, sizeof(fpregs));
2488c2ecf20Sopenharmony_ci	if (n > 0)
2498c2ecf20Sopenharmony_ci		return -EFAULT;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return restore_fpx_registers(userspace_pid[cpu],
2528c2ecf20Sopenharmony_ci				     (unsigned long *) &fpregs);
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cilong subarch_ptrace(struct task_struct *child, long request,
2568c2ecf20Sopenharmony_ci		    unsigned long addr, unsigned long data)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	int ret = -EIO;
2598c2ecf20Sopenharmony_ci	void __user *datap = (void __user *) data;
2608c2ecf20Sopenharmony_ci	switch (request) {
2618c2ecf20Sopenharmony_ci	case PTRACE_GETFPREGS: /* Get the child FPU state. */
2628c2ecf20Sopenharmony_ci		ret = get_fpregs(datap, child);
2638c2ecf20Sopenharmony_ci		break;
2648c2ecf20Sopenharmony_ci	case PTRACE_SETFPREGS: /* Set the child FPU state. */
2658c2ecf20Sopenharmony_ci		ret = set_fpregs(datap, child);
2668c2ecf20Sopenharmony_ci		break;
2678c2ecf20Sopenharmony_ci	case PTRACE_GETFPXREGS: /* Get the child FPU state. */
2688c2ecf20Sopenharmony_ci		ret = get_fpxregs(datap, child);
2698c2ecf20Sopenharmony_ci		break;
2708c2ecf20Sopenharmony_ci	case PTRACE_SETFPXREGS: /* Set the child FPU state. */
2718c2ecf20Sopenharmony_ci		ret = set_fpxregs(datap, child);
2728c2ecf20Sopenharmony_ci		break;
2738c2ecf20Sopenharmony_ci	default:
2748c2ecf20Sopenharmony_ci		ret = -EIO;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci	return ret;
2778c2ecf20Sopenharmony_ci}
278