Unification of regset and non-regset sides of ELF coredump

handling.  Collecting per-thread register values is the
 only thing that needs to be ifdefed there...
 
 Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQQqUNBr3gm4hGXdBJlZ7Krx/gZQ6wUCY5ZyNgAKCRBZ7Krx/gZQ
 63MZAQDZDE9Pk9EQ/3qOPNb2cuz8KSB3THUyotvustUUGPTUVAD/Ut1xD03jpWCY
 oQ6tM8dNyh3+Vsx6/XKNd1+pj6IgNQE=
 =XYkZ
 -----END PGP SIGNATURE-----

Merge tag 'pull-elfcore' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull elf coredumping updates from Al Viro:
 "Unification of regset and non-regset sides of ELF coredump handling.

  Collecting per-thread register values is the only thing that needs to
  be ifdefed there..."

* tag 'pull-elfcore' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  [elf] get rid of get_note_info_size()
  [elf] unify regset and non-regset cases
  [elf][non-regset] use elf_core_copy_task_regs() for dumper as well
  [elf][non-regset] uninline elf_core_copy_task_fpregs() (and lose pt_regs argument)
  elf_core_copy_task_regs(): task_pt_regs is defined everywhere
  [elf][regset] simplify thread list handling in fill_note_info()
  [elf][regset] clean fill_note_info() a bit
  kill extern of vsyscall32_sysctl
  kill coredump_params->regs
  kill signal_pt_regs()
This commit is contained in:
Linus Torvalds 2022-12-12 18:18:34 -08:00
commit 405b2fc663
15 changed files with 64 additions and 266 deletions

View file

@ -120,12 +120,6 @@ extern int dump_elf_task(elf_greg_t *dest, struct task_struct *task);
#define ELF_CORE_COPY_TASK_REGS(TASK, DEST) \ #define ELF_CORE_COPY_TASK_REGS(TASK, DEST) \
dump_elf_task(*(DEST), TASK) dump_elf_task(*(DEST), TASK)
/* Similar, but for the FP registers. */
extern int dump_elf_task_fp(elf_fpreg_t *dest, struct task_struct *task);
#define ELF_CORE_COPY_FPREGS(TASK, DEST) \
dump_elf_task_fp(*(DEST), TASK)
/* This yields a mask that user programs can use to figure out what /* This yields a mask that user programs can use to figure out what
instruction set this CPU supports. This is trivial on Alpha, instruction set this CPU supports. This is trivial on Alpha,
but not so on other machines. */ but not so on other machines. */

View file

@ -16,7 +16,6 @@
#define current_pt_regs() \ #define current_pt_regs() \
((struct pt_regs *) ((char *)current_thread_info() + 2*PAGE_SIZE) - 1) ((struct pt_regs *) ((char *)current_thread_info() + 2*PAGE_SIZE) - 1)
#define signal_pt_regs current_pt_regs
#define force_successful_syscall_return() (current_pt_regs()->r0 = 0) #define force_successful_syscall_return() (current_pt_regs()->r0 = 0)

View file

@ -333,14 +333,12 @@ dump_elf_task(elf_greg_t *dest, struct task_struct *task)
} }
EXPORT_SYMBOL(dump_elf_task); EXPORT_SYMBOL(dump_elf_task);
int int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu)
dump_elf_task_fp(elf_fpreg_t *dest, struct task_struct *task)
{ {
struct switch_stack *sw = (struct switch_stack *)task_pt_regs(task) - 1; struct switch_stack *sw = (struct switch_stack *)task_pt_regs(t) - 1;
memcpy(dest, sw->fp, 32 * 8); memcpy(fpu, sw->fp, 32 * 8);
return 1; return 1;
} }
EXPORT_SYMBOL(dump_elf_task_fp);
/* /*
* Return saved PC of a blocked thread. This assumes the frame * Return saved PC of a blocked thread. This assumes the frame

View file

@ -9,6 +9,7 @@
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/elfcore.h>
#include <asm/elf.h> #include <asm/elf.h>
#include <abi/reg_ops.h> #include <abi/reg_ops.h>
@ -69,12 +70,11 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
} }
/* Fill in the fpu structure for a core dump. */ /* Fill in the fpu structure for a core dump. */
int dump_fpu(struct pt_regs *regs, struct user_fp *fpu) int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu)
{ {
memcpy(fpu, &current->thread.user_fp, sizeof(*fpu)); memcpy(fpu, &current->thread.user_fp, sizeof(*fpu));
return 1; return 1;
} }
EXPORT_SYMBOL(dump_fpu);
int dump_task_regs(struct task_struct *tsk, elf_gregset_t *pr_regs) int dump_task_regs(struct task_struct *tsk, elf_gregset_t *pr_regs)
{ {

View file

@ -32,6 +32,7 @@
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/elfcore.h>
#include <asm/traps.h> #include <asm/traps.h>
#include <asm/machdep.h> #include <asm/machdep.h>
@ -213,7 +214,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
} }
/* Fill in the fpu structure for a core dump. */ /* Fill in the fpu structure for a core dump. */
int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu) int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu)
{ {
if (FPU_IS_EMU) { if (FPU_IS_EMU) {
int i; int i;
@ -262,7 +263,6 @@ int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu)
return 1; return 1;
} }
EXPORT_SYMBOL(dump_fpu);
unsigned long __get_wchan(struct task_struct *p) unsigned long __get_wchan(struct task_struct *p)
{ {

View file

@ -133,7 +133,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp)
/* /*
* Set up a thread for executing a new program * Set up a thread for executing a new program
*/ */
int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu)
{ {
return 0; /* MicroBlaze has no separate FPU registers */ return 0; /* MicroBlaze has no separate FPU registers */
} }

View file

@ -33,6 +33,7 @@
#include <skas.h> #include <skas.h>
#include <registers.h> #include <registers.h>
#include <linux/time-internal.h> #include <linux/time-internal.h>
#include <linux/elfcore.h>
/* /*
* This is a per-cpu array. A processor only modifies its entry and it only * This is a per-cpu array. A processor only modifies its entry and it only
@ -393,7 +394,7 @@ unsigned long __get_wchan(struct task_struct *p)
return 0; return 0;
} }
int elf_core_copy_fpregs(struct task_struct *t, elf_fpregset_t *fpu) int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu)
{ {
int cpu = current_thread_info()->cpu; int cpu = current_thread_info()->cpu;

View file

@ -222,7 +222,6 @@ do { \
/* I'm not sure if we can use '-' here */ /* I'm not sure if we can use '-' here */
#define ELF_PLATFORM ("x86_64") #define ELF_PLATFORM ("x86_64")
extern void set_personality_64bit(void); extern void set_personality_64bit(void);
extern unsigned int sysctl_vsyscall32;
extern int force_personality32; extern int force_personality32;
#endif /* !CONFIG_X86_32 */ #endif /* !CONFIG_X86_32 */

View file

@ -201,10 +201,6 @@ typedef struct user_i387_struct elf_fpregset_t;
struct task_struct; struct task_struct;
extern int elf_core_copy_fpregs(struct task_struct *t, elf_fpregset_t *fpu);
#define ELF_CORE_COPY_FPREGS(t, fpu) elf_core_copy_fpregs(t, fpu)
#define ELF_EXEC_PAGESIZE 4096 #define ELF_EXEC_PAGESIZE 4096
#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) #define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2)

View file

@ -1718,7 +1718,6 @@ static int fill_files_note(struct memelfnote *note, struct coredump_params *cprm
return 0; return 0;
} }
#ifdef CORE_DUMP_USE_REGSET
#include <linux/regset.h> #include <linux/regset.h>
struct elf_thread_core_info { struct elf_thread_core_info {
@ -1739,6 +1738,7 @@ struct elf_note_info {
int thread_notes; int thread_notes;
}; };
#ifdef CORE_DUMP_USE_REGSET
/* /*
* When a regset has a writeback hook, we call it on each thread before * When a regset has a writeback hook, we call it on each thread before
* dumping user memory. On register window machines, this makes sure the * dumping user memory. On register window machines, this makes sure the
@ -1818,34 +1818,58 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
return 1; return 1;
} }
#else
static int fill_thread_core_info(struct elf_thread_core_info *t,
const struct user_regset_view *view,
long signr, struct elf_note_info *info)
{
struct task_struct *p = t->task;
elf_fpregset_t *fpu;
fill_prstatus(&t->prstatus.common, p, signr);
elf_core_copy_task_regs(p, &t->prstatus.pr_reg);
fill_note(&t->notes[0], "CORE", NT_PRSTATUS, sizeof(t->prstatus),
&(t->prstatus));
info->size += notesize(&t->notes[0]);
fpu = kzalloc(sizeof(elf_fpregset_t), GFP_KERNEL);
if (!fpu || !elf_core_copy_task_fpregs(p, fpu)) {
kfree(fpu);
return 1;
}
t->prstatus.pr_fpvalid = 1;
fill_note(&t->notes[1], "CORE", NT_PRFPREG, sizeof(*fpu), fpu);
info->size += notesize(&t->notes[1]);
return 1;
}
#endif
static int fill_note_info(struct elfhdr *elf, int phdrs, static int fill_note_info(struct elfhdr *elf, int phdrs,
struct elf_note_info *info, struct elf_note_info *info,
struct coredump_params *cprm) struct coredump_params *cprm)
{ {
struct task_struct *dump_task = current; struct task_struct *dump_task = current;
const struct user_regset_view *view = task_user_regset_view(dump_task); const struct user_regset_view *view;
struct elf_thread_core_info *t; struct elf_thread_core_info *t;
struct elf_prpsinfo *psinfo; struct elf_prpsinfo *psinfo;
struct core_thread *ct; struct core_thread *ct;
unsigned int i;
info->size = 0;
info->thread = NULL;
psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL);
if (psinfo == NULL) { if (!psinfo)
info->psinfo.data = NULL; /* So we don't free this wrongly */
return 0; return 0;
}
fill_note(&info->psinfo, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); fill_note(&info->psinfo, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo);
#ifdef CORE_DUMP_USE_REGSET
view = task_user_regset_view(dump_task);
/* /*
* Figure out how many notes we're going to need for each thread. * Figure out how many notes we're going to need for each thread.
*/ */
info->thread_notes = 0; info->thread_notes = 0;
for (i = 0; i < view->n; ++i) for (int i = 0; i < view->n; ++i)
if (view->regsets[i].core_note_type != 0) if (view->regsets[i].core_note_type != 0)
++info->thread_notes; ++info->thread_notes;
@ -1864,11 +1888,23 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
*/ */
fill_elf_header(elf, phdrs, fill_elf_header(elf, phdrs,
view->e_machine, view->e_flags); view->e_machine, view->e_flags);
#else
view = NULL;
info->thread_notes = 2;
fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS);
#endif
/* /*
* Allocate a structure for each thread. * Allocate a structure for each thread.
*/ */
for (ct = &dump_task->signal->core_state->dumper; ct; ct = ct->next) { info->thread = kzalloc(offsetof(struct elf_thread_core_info,
notes[info->thread_notes]),
GFP_KERNEL);
if (unlikely(!info->thread))
return 0;
info->thread->task = dump_task;
for (ct = dump_task->signal->core_state->dumper.next; ct; ct = ct->next) {
t = kzalloc(offsetof(struct elf_thread_core_info, t = kzalloc(offsetof(struct elf_thread_core_info,
notes[info->thread_notes]), notes[info->thread_notes]),
GFP_KERNEL); GFP_KERNEL);
@ -1876,17 +1912,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
return 0; return 0;
t->task = ct->task; t->task = ct->task;
if (ct->task == dump_task || !info->thread) { t->next = info->thread->next;
t->next = info->thread; info->thread->next = t;
info->thread = t;
} else {
/*
* Make sure to keep the original task at
* the head of the list.
*/
t->next = info->thread->next;
info->thread->next = t;
}
} }
/* /*
@ -1914,11 +1941,6 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
return 1; return 1;
} }
static size_t get_note_info_size(struct elf_note_info *info)
{
return info->size;
}
/* /*
* Write all the notes for each thread. When writing the first thread, the * Write all the notes for each thread. When writing the first thread, the
* process-wide notes are interleaved after the first thread-specific note. * process-wide notes are interleaved after the first thread-specific note.
@ -1973,197 +1995,6 @@ static void free_note_info(struct elf_note_info *info)
kvfree(info->files.data); kvfree(info->files.data);
} }
#else
/* Here is the structure in which status of each thread is captured. */
struct elf_thread_status
{
struct list_head list;
struct elf_prstatus prstatus; /* NT_PRSTATUS */
elf_fpregset_t fpu; /* NT_PRFPREG */
struct task_struct *thread;
struct memelfnote notes[3];
int num_notes;
};
/*
* In order to add the specific thread information for the elf file format,
* we need to keep a linked list of every threads pr_status and then create
* a single section for them in the final core file.
*/
static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
{
int sz = 0;
struct task_struct *p = t->thread;
t->num_notes = 0;
fill_prstatus(&t->prstatus.common, p, signr);
elf_core_copy_task_regs(p, &t->prstatus.pr_reg);
fill_note(&t->notes[0], "CORE", NT_PRSTATUS, sizeof(t->prstatus),
&(t->prstatus));
t->num_notes++;
sz += notesize(&t->notes[0]);
if ((t->prstatus.pr_fpvalid = elf_core_copy_task_fpregs(p, NULL,
&t->fpu))) {
fill_note(&t->notes[1], "CORE", NT_PRFPREG, sizeof(t->fpu),
&(t->fpu));
t->num_notes++;
sz += notesize(&t->notes[1]);
}
return sz;
}
struct elf_note_info {
struct memelfnote *notes;
struct memelfnote *notes_files;
struct elf_prstatus *prstatus; /* NT_PRSTATUS */
struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */
struct list_head thread_list;
elf_fpregset_t *fpu;
user_siginfo_t csigdata;
int thread_status_size;
int numnote;
};
static int elf_note_info_init(struct elf_note_info *info)
{
memset(info, 0, sizeof(*info));
INIT_LIST_HEAD(&info->thread_list);
/* Allocate space for ELF notes */
info->notes = kmalloc_array(8, sizeof(struct memelfnote), GFP_KERNEL);
if (!info->notes)
return 0;
info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL);
if (!info->psinfo)
return 0;
info->prstatus = kmalloc(sizeof(*info->prstatus), GFP_KERNEL);
if (!info->prstatus)
return 0;
info->fpu = kmalloc(sizeof(*info->fpu), GFP_KERNEL);
if (!info->fpu)
return 0;
return 1;
}
static int fill_note_info(struct elfhdr *elf, int phdrs,
struct elf_note_info *info,
struct coredump_params *cprm)
{
struct core_thread *ct;
struct elf_thread_status *ets;
if (!elf_note_info_init(info))
return 0;
for (ct = current->signal->core_state->dumper.next;
ct; ct = ct->next) {
ets = kzalloc(sizeof(*ets), GFP_KERNEL);
if (!ets)
return 0;
ets->thread = ct->task;
list_add(&ets->list, &info->thread_list);
}
list_for_each_entry(ets, &info->thread_list, list) {
int sz;
sz = elf_dump_thread_status(cprm->siginfo->si_signo, ets);
info->thread_status_size += sz;
}
/* now collect the dump for the current */
memset(info->prstatus, 0, sizeof(*info->prstatus));
fill_prstatus(&info->prstatus->common, current, cprm->siginfo->si_signo);
elf_core_copy_regs(&info->prstatus->pr_reg, cprm->regs);
/* Set up header */
fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS);
/*
* Set up the notes in similar form to SVR4 core dumps made
* with info from their /proc.
*/
fill_note(info->notes + 0, "CORE", NT_PRSTATUS,
sizeof(*info->prstatus), info->prstatus);
fill_psinfo(info->psinfo, current->group_leader, current->mm);
fill_note(info->notes + 1, "CORE", NT_PRPSINFO,
sizeof(*info->psinfo), info->psinfo);
fill_siginfo_note(info->notes + 2, &info->csigdata, cprm->siginfo);
fill_auxv_note(info->notes + 3, current->mm);
info->numnote = 4;
if (fill_files_note(info->notes + info->numnote, cprm) == 0) {
info->notes_files = info->notes + info->numnote;
info->numnote++;
}
/* Try to dump the FPU. */
info->prstatus->pr_fpvalid =
elf_core_copy_task_fpregs(current, cprm->regs, info->fpu);
if (info->prstatus->pr_fpvalid)
fill_note(info->notes + info->numnote++,
"CORE", NT_PRFPREG, sizeof(*info->fpu), info->fpu);
return 1;
}
static size_t get_note_info_size(struct elf_note_info *info)
{
int sz = 0;
int i;
for (i = 0; i < info->numnote; i++)
sz += notesize(info->notes + i);
sz += info->thread_status_size;
return sz;
}
static int write_note_info(struct elf_note_info *info,
struct coredump_params *cprm)
{
struct elf_thread_status *ets;
int i;
for (i = 0; i < info->numnote; i++)
if (!writenote(info->notes + i, cprm))
return 0;
/* write out the thread status notes section */
list_for_each_entry(ets, &info->thread_list, list) {
for (i = 0; i < ets->num_notes; i++)
if (!writenote(&ets->notes[i], cprm))
return 0;
}
return 1;
}
static void free_note_info(struct elf_note_info *info)
{
while (!list_empty(&info->thread_list)) {
struct list_head *tmp = info->thread_list.next;
list_del(tmp);
kfree(list_entry(tmp, struct elf_thread_status, list));
}
/* Free data possibly allocated by fill_files_note(): */
if (info->notes_files)
kvfree(info->notes_files->data);
kfree(info->prstatus);
kfree(info->psinfo);
kfree(info->notes);
kfree(info->fpu);
}
#endif
static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum, static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum,
elf_addr_t e_shoff, int segs) elf_addr_t e_shoff, int segs)
{ {
@ -2227,7 +2058,7 @@ static int elf_core_dump(struct coredump_params *cprm)
/* Write notes phdr entry */ /* Write notes phdr entry */
{ {
size_t sz = get_note_info_size(&info); size_t sz = info.size;
/* For cell spufs */ /* For cell spufs */
sz += elf_coredump_extra_notes_size(); sz += elf_coredump_extra_notes_size();

View file

@ -529,7 +529,6 @@ void do_coredump(const kernel_siginfo_t *siginfo)
static atomic_t core_dump_count = ATOMIC_INIT(0); static atomic_t core_dump_count = ATOMIC_INIT(0);
struct coredump_params cprm = { struct coredump_params cprm = {
.siginfo = siginfo, .siginfo = siginfo,
.regs = signal_pt_regs(),
.limit = rlimit(RLIMIT_CORE), .limit = rlimit(RLIMIT_CORE),
/* /*
* We must use the same mm->flags while dumping core to avoid * We must use the same mm->flags while dumping core to avoid

View file

@ -18,7 +18,6 @@ struct core_vma_metadata {
struct coredump_params { struct coredump_params {
const kernel_siginfo_t *siginfo; const kernel_siginfo_t *siginfo;
struct pt_regs *regs;
struct file *file; struct file *file;
unsigned long limit; unsigned long limit;
unsigned long mm_flags; unsigned long mm_flags;

View file

@ -88,22 +88,13 @@ static inline int elf_core_copy_task_regs(struct task_struct *t, elf_gregset_t*
{ {
#if defined (ELF_CORE_COPY_TASK_REGS) #if defined (ELF_CORE_COPY_TASK_REGS)
return ELF_CORE_COPY_TASK_REGS(t, elfregs); return ELF_CORE_COPY_TASK_REGS(t, elfregs);
#elif defined (task_pt_regs) #else
elf_core_copy_regs(elfregs, task_pt_regs(t)); elf_core_copy_regs(elfregs, task_pt_regs(t));
#endif #endif
return 0; return 0;
} }
extern int dump_fpu (struct pt_regs *, elf_fpregset_t *); int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu);
static inline int elf_core_copy_task_fpregs(struct task_struct *t, struct pt_regs *regs, elf_fpregset_t *fpu)
{
#ifdef ELF_CORE_COPY_FPREGS
return ELF_CORE_COPY_FPREGS(t, fpu);
#else
return dump_fpu(regs, fpu);
#endif
}
#ifdef CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS #ifdef CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS
/* /*

View file

@ -389,15 +389,6 @@ static inline void user_single_step_report(struct pt_regs *regs)
#define current_pt_regs() task_pt_regs(current) #define current_pt_regs() task_pt_regs(current)
#endif #endif
/*
* unlike current_pt_regs(), this one is equal to task_pt_regs(current)
* on *all* architectures; the only reason to have a per-arch definition
* is optimisation.
*/
#ifndef signal_pt_regs
#define signal_pt_regs() task_pt_regs(current)
#endif
#ifndef current_user_stack_pointer #ifndef current_user_stack_pointer
#define current_user_stack_pointer() user_stack_pointer(current_pt_regs()) #define current_user_stack_pointer() user_stack_pointer(current_pt_regs())
#endif #endif

View file

@ -1255,7 +1255,7 @@ int send_signal_locked(int sig, struct kernel_siginfo *info,
static void print_fatal_signal(int signr) static void print_fatal_signal(int signr)
{ {
struct pt_regs *regs = signal_pt_regs(); struct pt_regs *regs = task_pt_regs(current);
pr_info("potentially unexpected fatal signal %d.\n", signr); pr_info("potentially unexpected fatal signal %d.\n", signr);
#if defined(__i386__) && !defined(__arch_um__) #if defined(__i386__) && !defined(__arch_um__)