mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

- Introduce regular REGSET note macros arch-wide (Dave Martin) - Remove arbitrary 4K limitation of program header size (Yin Fengwei) - Reorder function qualifiers for copy_clone_args_from_user() (Dishank Jogi) -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRSPkdeREjth1dHnSE2KwveOeQkuwUCaIVKiAAKCRA2KwveOeQk u4zBAP4zUNj2+XyixVPXCzv+Hkle6zWs7yrzdA2yLxe8Qtwj5AD+N2I6MUGcCFGW W+uWxlWTtGLDqh1CplIUqTlxMi39Og4= =vYnE -----END PGP SIGNATURE----- Merge tag 'execve-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux Pull execve updates from Kees Cook: - Introduce regular REGSET note macros arch-wide (Dave Martin) - Remove arbitrary 4K limitation of program header size (Yin Fengwei) - Reorder function qualifiers for copy_clone_args_from_user() (Dishank Jogi) * tag 'execve-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (25 commits) fork: reorder function qualifiers for copy_clone_args_from_user binfmt_elf: remove the 4k limitation of program header size binfmt_elf: Warn on missing or suspicious regset note names xtensa: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names um: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names x86/ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names sparc: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names sh: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names s390/ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names riscv: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names powerpc/ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names parisc: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names openrisc: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names nios2: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names MIPS: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names m68k: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names LoongArch: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names hexagon: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names csky: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names arm64: ptrace: Use USER_REGSET_NOTE_TYPE() to specify regset note names ...
305 lines
7.5 KiB
C
305 lines
7.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include <linux/sched.h>
|
|
#include <linux/elf.h>
|
|
#include <linux/regset.h>
|
|
#include <asm/user32.h>
|
|
#include <asm/sigcontext.h>
|
|
|
|
#ifdef CONFIG_X86_32
|
|
/*
|
|
* FPU tag word conversions.
|
|
*/
|
|
|
|
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
|
|
{
|
|
unsigned int tmp; /* to avoid 16 bit prefixes in the code */
|
|
|
|
/* Transform each pair of bits into 01 (valid) or 00 (empty) */
|
|
tmp = ~twd;
|
|
tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
|
|
/* and move the valid bits to the lower byte. */
|
|
tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
|
|
tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
|
|
tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
|
|
return tmp;
|
|
}
|
|
|
|
static inline unsigned long
|
|
twd_fxsr_to_i387(const struct user_fxsr_struct *fxsave)
|
|
{
|
|
struct _fpxreg *st = NULL;
|
|
unsigned long twd = (unsigned long) fxsave->twd;
|
|
unsigned long tag;
|
|
unsigned long ret = 0xffff0000;
|
|
int i;
|
|
|
|
#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16)
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
if (twd & 0x1) {
|
|
st = (struct _fpxreg *) FPREG_ADDR(fxsave, i);
|
|
|
|
switch (st->exponent & 0x7fff) {
|
|
case 0x7fff:
|
|
tag = 2; /* Special */
|
|
break;
|
|
case 0x0000:
|
|
if (!st->significand[0] &&
|
|
!st->significand[1] &&
|
|
!st->significand[2] &&
|
|
!st->significand[3]) {
|
|
tag = 1; /* Zero */
|
|
} else {
|
|
tag = 2; /* Special */
|
|
}
|
|
break;
|
|
default:
|
|
if (st->significand[3] & 0x8000)
|
|
tag = 0; /* Valid */
|
|
else
|
|
tag = 2; /* Special */
|
|
break;
|
|
}
|
|
} else {
|
|
tag = 3; /* Empty */
|
|
}
|
|
ret |= (tag << (2 * i));
|
|
twd = twd >> 1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Get/set the old 32bit i387 registers (pre-FPX)
|
|
*
|
|
* We provide simple wrappers for mcontext.c, they are only defined locally
|
|
* because mcontext.c is userspace facing and needs to a different definition
|
|
* of the structures.
|
|
*/
|
|
static int _um_i387_from_fxsr(struct membuf to,
|
|
const struct user_fxsr_struct *fxsave)
|
|
{
|
|
int i;
|
|
|
|
membuf_store(&to, (unsigned long)fxsave->cwd | 0xffff0000ul);
|
|
membuf_store(&to, (unsigned long)fxsave->swd | 0xffff0000ul);
|
|
membuf_store(&to, twd_fxsr_to_i387(fxsave));
|
|
membuf_store(&to, fxsave->fip);
|
|
membuf_store(&to, fxsave->fcs | ((unsigned long)fxsave->fop << 16));
|
|
membuf_store(&to, fxsave->foo);
|
|
membuf_store(&to, fxsave->fos);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
membuf_write(&to, (void *)fxsave->st_space + i * 16, 10);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int um_i387_from_fxsr(struct user_i387_struct *i387,
|
|
const struct user_fxsr_struct *fxsave);
|
|
|
|
int um_i387_from_fxsr(struct user_i387_struct *i387,
|
|
const struct user_fxsr_struct *fxsave)
|
|
{
|
|
struct membuf to = {
|
|
.p = i387,
|
|
.left = sizeof(*i387),
|
|
};
|
|
|
|
return _um_i387_from_fxsr(to, fxsave);
|
|
}
|
|
|
|
static int fpregs_legacy_get(struct task_struct *target,
|
|
const struct user_regset *regset,
|
|
struct membuf to)
|
|
{
|
|
struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
|
|
|
|
return _um_i387_from_fxsr(to, fxsave);
|
|
}
|
|
|
|
int um_fxsr_from_i387(struct user_fxsr_struct *fxsave,
|
|
const struct user_i387_struct *from);
|
|
|
|
int um_fxsr_from_i387(struct user_fxsr_struct *fxsave,
|
|
const struct user_i387_struct *from)
|
|
{
|
|
int i;
|
|
|
|
fxsave->cwd = (unsigned short)(from->cwd & 0xffff);
|
|
fxsave->swd = (unsigned short)(from->swd & 0xffff);
|
|
fxsave->twd = twd_i387_to_fxsr((unsigned short)(from->twd & 0xffff));
|
|
fxsave->fip = from->fip;
|
|
fxsave->fop = (unsigned short)((from->fcs & 0xffff0000ul) >> 16);
|
|
fxsave->fcs = (from->fcs & 0xffff);
|
|
fxsave->foo = from->foo;
|
|
fxsave->fos = from->fos;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
memcpy((void *)fxsave->st_space + i * 16,
|
|
(void *)from->st_space + i * 10, 10);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fpregs_legacy_set(struct task_struct *target,
|
|
const struct user_regset *regset,
|
|
unsigned int pos, unsigned int count,
|
|
const void *kbuf, const void __user *ubuf)
|
|
{
|
|
struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
|
|
const struct user_i387_struct *from;
|
|
struct user_i387_struct buf;
|
|
|
|
if (ubuf) {
|
|
if (copy_from_user(&buf, ubuf, sizeof(buf)))
|
|
return -EFAULT;
|
|
from = &buf;
|
|
} else {
|
|
from = kbuf;
|
|
}
|
|
|
|
return um_fxsr_from_i387(fxsave, from);
|
|
}
|
|
#endif
|
|
|
|
static int genregs_get(struct task_struct *target,
|
|
const struct user_regset *regset,
|
|
struct membuf to)
|
|
{
|
|
int reg;
|
|
|
|
for (reg = 0; to.left; reg++)
|
|
membuf_store(&to, getreg(target, reg * sizeof(unsigned long)));
|
|
return 0;
|
|
}
|
|
|
|
static int genregs_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 = 0;
|
|
|
|
if (kbuf) {
|
|
const unsigned long *k = kbuf;
|
|
|
|
while (count >= sizeof(*k) && !ret) {
|
|
ret = putreg(target, pos, *k++);
|
|
count -= sizeof(*k);
|
|
pos += sizeof(*k);
|
|
}
|
|
} else {
|
|
const unsigned long __user *u = ubuf;
|
|
|
|
while (count >= sizeof(*u) && !ret) {
|
|
unsigned long word;
|
|
|
|
ret = __get_user(word, u++);
|
|
if (ret)
|
|
break;
|
|
ret = putreg(target, pos, word);
|
|
count -= sizeof(*u);
|
|
pos += sizeof(*u);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset)
|
|
{
|
|
return regset->n;
|
|
}
|
|
|
|
static int generic_fpregs_get(struct task_struct *target,
|
|
const struct user_regset *regset,
|
|
struct membuf to)
|
|
{
|
|
void *fpregs = task_pt_regs(target)->regs.fp;
|
|
|
|
membuf_write(&to, fpregs, regset->size * regset->n);
|
|
return 0;
|
|
}
|
|
|
|
static int generic_fpregs_set(struct task_struct *target,
|
|
const struct user_regset *regset,
|
|
unsigned int pos, unsigned int count,
|
|
const void *kbuf, const void __user *ubuf)
|
|
{
|
|
void *fpregs = task_pt_regs(target)->regs.fp;
|
|
|
|
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
fpregs, 0, regset->size * regset->n);
|
|
}
|
|
|
|
static struct user_regset uml_regsets[] __ro_after_init = {
|
|
[REGSET_GENERAL] = {
|
|
USER_REGSET_NOTE_TYPE(PRSTATUS),
|
|
.n = sizeof(struct user_regs_struct) / sizeof(long),
|
|
.size = sizeof(long),
|
|
.align = sizeof(long),
|
|
.regset_get = genregs_get,
|
|
.set = genregs_set
|
|
},
|
|
#ifdef CONFIG_X86_32
|
|
/* Old FP registers, they are needed in signal frames */
|
|
[REGSET_FP_LEGACY] = {
|
|
USER_REGSET_NOTE_TYPE(PRFPREG),
|
|
.n = sizeof(struct user_i387_ia32_struct) / sizeof(long),
|
|
.size = sizeof(long),
|
|
.align = sizeof(long),
|
|
.active = generic_fpregs_active,
|
|
.regset_get = fpregs_legacy_get,
|
|
.set = fpregs_legacy_set,
|
|
},
|
|
#endif
|
|
[REGSET_FP] = {
|
|
#ifdef CONFIG_X86_32
|
|
USER_REGSET_NOTE_TYPE(PRXFPREG),
|
|
.n = sizeof(struct user32_fxsr_struct) / sizeof(long),
|
|
#else
|
|
USER_REGSET_NOTE_TYPE(PRFPREG),
|
|
.n = sizeof(struct user_i387_struct) / sizeof(long),
|
|
#endif
|
|
.size = sizeof(long),
|
|
.align = sizeof(long),
|
|
.active = generic_fpregs_active,
|
|
.regset_get = generic_fpregs_get,
|
|
.set = generic_fpregs_set,
|
|
},
|
|
[REGSET_XSTATE] = {
|
|
USER_REGSET_NOTE_TYPE(X86_XSTATE),
|
|
.size = sizeof(long),
|
|
.align = sizeof(long),
|
|
.active = generic_fpregs_active,
|
|
.regset_get = generic_fpregs_get,
|
|
.set = generic_fpregs_set,
|
|
},
|
|
/* TODO: Add TLS regset for 32bit */
|
|
};
|
|
|
|
static const struct user_regset_view user_uml_view = {
|
|
#ifdef CONFIG_X86_32
|
|
.name = "i386", .e_machine = EM_386,
|
|
#else
|
|
.name = "x86_64", .e_machine = EM_X86_64,
|
|
#endif
|
|
.regsets = uml_regsets, .n = ARRAY_SIZE(uml_regsets)
|
|
};
|
|
|
|
const struct user_regset_view *
|
|
task_user_regset_view(struct task_struct *tsk)
|
|
{
|
|
return &user_uml_view;
|
|
}
|
|
|
|
static int __init init_regset_xstate_info(void)
|
|
{
|
|
uml_regsets[REGSET_XSTATE].n =
|
|
host_fp_size / uml_regsets[REGSET_XSTATE].size;
|
|
|
|
return 0;
|
|
}
|
|
arch_initcall(init_regset_xstate_info);
|