linux-user/elfload: Write corefile elf header in one block

Fixes a bug in which write_note() wrote namesz_rounded
and datasz_rounded bytes, even though name and data
pointers contain only the unrounded number of bytes.

Instead of many small writes, allocate a block to contain all
of the elf headers and all of the notes.  Copy the data into the
block piecemeal and the write it to the file as a chunk.
This also avoids the need to lseek forward for alignment.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2024-02-27 06:34:18 -10:00
parent 2410d28dc9
commit 243c470662

View File

@ -4002,18 +4002,6 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
* Example for ARM target is provided in this file.
*/
/* An ELF note in memory */
struct memelfnote {
const char *name;
size_t namesz;
size_t namesz_rounded;
int type;
size_t datasz;
size_t datasz_rounded;
void *data;
size_t notesz;
};
struct target_elf_siginfo {
abi_int si_signo; /* signal number */
abi_int si_code; /* extra code */
@ -4053,40 +4041,6 @@ struct target_elf_prpsinfo {
char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */
};
/* Here is the structure in which status of each thread is captured. */
struct elf_thread_status {
QTAILQ_ENTRY(elf_thread_status) ets_link;
struct target_elf_prstatus prstatus; /* NT_PRSTATUS */
#if 0
elf_fpregset_t fpu; /* NT_PRFPREG */
struct task_struct *thread;
elf_fpxregset_t xfpu; /* ELF_CORE_XFPREG_TYPE */
#endif
struct memelfnote notes[1];
int num_notes;
};
#define NUMNOTES 3
struct elf_note_info {
struct memelfnote notes[NUMNOTES];
struct target_elf_prstatus prstatus; /* NT_PRSTATUS */
struct target_elf_prpsinfo psinfo; /* NT_PRPSINFO */
QTAILQ_HEAD(, elf_thread_status) thread_list;
#if 0
/*
* Current version of ELF coredump doesn't support
* dumping fp regs etc.
*/
elf_fpregset_t *fpu;
elf_fpxregset_t *xfpu;
int thread_status_size;
#endif
int notes_size;
int numnote;
};
struct vm_area_struct {
target_ulong vma_start; /* start vaddr of memory region */
target_ulong vma_end; /* end vaddr of memory region */
@ -4110,22 +4064,6 @@ static abi_ulong vma_dump_size(const struct vm_area_struct *);
static int vma_walker(void *priv, target_ulong start, target_ulong end,
unsigned long flags);
static void fill_elf_header(struct elfhdr *, int, uint16_t, uint32_t);
static void fill_note(struct memelfnote *, const char *, int,
unsigned int, void *);
static void fill_prstatus(struct target_elf_prstatus *, const TaskState *, int);
static int fill_psinfo(struct target_elf_prpsinfo *, const TaskState *);
static void fill_auxv_note(struct memelfnote *, const TaskState *);
static void fill_elf_note_phdr(struct elf_phdr *, int, off_t);
static size_t note_size(const struct memelfnote *);
static void free_note_info(struct elf_note_info *);
static void fill_note_info(struct elf_note_info *, int, const CPUArchState *);
static void fill_thread_info(struct elf_note_info *, const CPUArchState *);
static int dump_write(int, const void *, size_t);
static int write_note(struct memelfnote *, int);
static int write_note_info(struct elf_note_info *, int);
#ifdef BSWAP_NEEDED
static void bswap_prstatus(struct target_elf_prstatus *prstatus)
{
@ -4280,35 +4218,32 @@ static size_t size_note(const char *name, size_t datasz)
return sizeof(struct elf_note) + namesz + datasz;
}
static void fill_note(struct memelfnote *note, const char *name, int type,
unsigned int sz, void *data)
static void *fill_note(void **pptr, int type, const char *name, size_t datasz)
{
unsigned int namesz;
void *ptr = *pptr;
struct elf_note *n = ptr;
size_t namesz = strlen(name) + 1;
namesz = strlen(name) + 1;
note->name = name;
note->namesz = namesz;
note->namesz_rounded = roundup(namesz, sizeof (int32_t));
note->type = type;
note->datasz = sz;
note->datasz_rounded = roundup(sz, sizeof (int32_t));
n->n_namesz = namesz;
n->n_descsz = datasz;
n->n_type = type;
bswap_note(n);
note->data = data;
ptr += sizeof(*n);
memcpy(ptr, name, namesz);
/*
* We calculate rounded up note size here as specified by
* ELF document.
*/
note->notesz = sizeof (struct elf_note) +
note->namesz_rounded + note->datasz_rounded;
namesz = ROUND_UP(namesz, 4);
datasz = ROUND_UP(datasz, 4);
*pptr = ptr + namesz + datasz;
return ptr + namesz;
}
static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine,
uint32_t flags)
{
(void) memset(elf, 0, sizeof(*elf));
memcpy(elf->e_ident, ELFMAG, SELFMAG);
(void) memcpy(elf->e_ident, ELFMAG, SELFMAG);
elf->e_ident[EI_CLASS] = ELF_CLASS;
elf->e_ident[EI_DATA] = ELF_DATA;
elf->e_ident[EI_VERSION] = EV_CURRENT;
@ -4326,95 +4261,79 @@ static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine,
bswap_ehdr(elf);
}
static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset)
static void fill_elf_note_phdr(struct elf_phdr *phdr, size_t sz, off_t offset)
{
phdr->p_type = PT_NOTE;
phdr->p_offset = offset;
phdr->p_vaddr = 0;
phdr->p_paddr = 0;
phdr->p_filesz = sz;
phdr->p_memsz = 0;
phdr->p_flags = 0;
phdr->p_align = 0;
bswap_phdr(phdr, 1);
}
static size_t note_size(const struct memelfnote *note)
static void fill_prstatus_note(void *data, const TaskState *ts,
CPUState *cpu, int signr)
{
return (note->notesz);
/*
* Because note memory is only aligned to 4, and target_elf_prstatus
* may well have higher alignment requirements, fill locally and
* memcpy to the destination afterward.
*/
struct target_elf_prstatus prstatus = {
.pr_info.si_signo = signr,
.pr_cursig = signr,
.pr_pid = ts->ts_tid,
.pr_ppid = getppid(),
.pr_pgrp = getpgrp(),
.pr_sid = getsid(0),
};
elf_core_copy_regs(&prstatus.pr_reg, cpu_env(cpu));
bswap_prstatus(&prstatus);
memcpy(data, &prstatus, sizeof(prstatus));
}
static void fill_prstatus(struct target_elf_prstatus *prstatus,
const TaskState *ts, int signr)
{
(void) memset(prstatus, 0, sizeof (*prstatus));
prstatus->pr_info.si_signo = prstatus->pr_cursig = signr;
prstatus->pr_pid = ts->ts_tid;
prstatus->pr_ppid = getppid();
prstatus->pr_pgrp = getpgrp();
prstatus->pr_sid = getsid(0);
bswap_prstatus(prstatus);
}
static int fill_psinfo(struct target_elf_prpsinfo *psinfo, const TaskState *ts)
static void fill_prpsinfo_note(void *data, const TaskState *ts)
{
/*
* Because note memory is only aligned to 4, and target_elf_prpsinfo
* may well have higher alignment requirements, fill locally and
* memcpy to the destination afterward.
*/
struct target_elf_prpsinfo psinfo;
char *base_filename;
unsigned int i, len;
(void) memset(psinfo, 0, sizeof (*psinfo));
size_t len;
len = ts->info->env_strings - ts->info->arg_strings;
if (len >= ELF_PRARGSZ)
len = ELF_PRARGSZ - 1;
if (copy_from_user(&psinfo->pr_psargs, ts->info->arg_strings, len)) {
return -EFAULT;
len = MIN(len, ELF_PRARGSZ);
memcpy(&psinfo.pr_psargs, g2h_untagged(ts->info->arg_strings), len);
for (size_t i = 0; i < len; i++) {
if (psinfo.pr_psargs[i] == 0) {
psinfo.pr_psargs[i] = ' ';
}
}
for (i = 0; i < len; i++)
if (psinfo->pr_psargs[i] == 0)
psinfo->pr_psargs[i] = ' ';
psinfo->pr_psargs[len] = 0;
psinfo->pr_pid = getpid();
psinfo->pr_ppid = getppid();
psinfo->pr_pgrp = getpgrp();
psinfo->pr_sid = getsid(0);
psinfo->pr_uid = getuid();
psinfo->pr_gid = getgid();
psinfo.pr_pid = getpid();
psinfo.pr_ppid = getppid();
psinfo.pr_pgrp = getpgrp();
psinfo.pr_sid = getsid(0);
psinfo.pr_uid = getuid();
psinfo.pr_gid = getgid();
base_filename = g_path_get_basename(ts->bprm->filename);
/*
* Using strncpy here is fine: at max-length,
* this field is not NUL-terminated.
*/
(void) strncpy(psinfo->pr_fname, base_filename,
sizeof(psinfo->pr_fname));
strncpy(psinfo.pr_fname, base_filename, sizeof(psinfo.pr_fname));
g_free(base_filename);
bswap_psinfo(psinfo);
return (0);
bswap_psinfo(&psinfo);
memcpy(data, &psinfo, sizeof(psinfo));
}
static void fill_auxv_note(struct memelfnote *note, const TaskState *ts)
static void fill_auxv_note(void *data, const TaskState *ts)
{
elf_addr_t auxv = (elf_addr_t)ts->info->saved_auxv;
elf_addr_t orig_auxv = auxv;
void *ptr;
int len = ts->info->auxv_len;
/*
* Auxiliary vector is stored in target process stack. It contains
* {type, value} pairs that we need to dump into note. This is not
* strictly necessary but we do it here for sake of completeness.
*/
/* read in whole auxv vector and copy it to memelfnote */
ptr = lock_user(VERIFY_READ, orig_auxv, len, 0);
if (ptr != NULL) {
fill_note(note, "CORE", NT_AUXV, len, ptr);
unlock_user(ptr, auxv, len);
}
memcpy(data, g2h_untagged(ts->info->saved_auxv), ts->info->auxv_len);
}
/*
@ -4462,111 +4381,6 @@ static int dump_write(int fd, const void *ptr, size_t size)
return (0);
}
static int write_note(struct memelfnote *men, int fd)
{
struct elf_note en;
en.n_namesz = men->namesz;
en.n_type = men->type;
en.n_descsz = men->datasz;
bswap_note(&en);
if (dump_write(fd, &en, sizeof(en)) != 0)
return (-1);
if (dump_write(fd, men->name, men->namesz_rounded) != 0)
return (-1);
if (dump_write(fd, men->data, men->datasz_rounded) != 0)
return (-1);
return (0);
}
static void fill_thread_info(struct elf_note_info *info, const CPUArchState *env)
{
CPUState *cpu = env_cpu((CPUArchState *)env);
TaskState *ts = (TaskState *)cpu->opaque;
struct elf_thread_status *ets;
ets = g_malloc0(sizeof (*ets));
ets->num_notes = 1; /* only prstatus is dumped */
fill_prstatus(&ets->prstatus, ts, 0);
elf_core_copy_regs(&ets->prstatus.pr_reg, env);
fill_note(&ets->notes[0], "CORE", NT_PRSTATUS, sizeof (ets->prstatus),
&ets->prstatus);
QTAILQ_INSERT_TAIL(&info->thread_list, ets, ets_link);
info->notes_size += note_size(&ets->notes[0]);
}
static void fill_note_info(struct elf_note_info *info,
int signr, const CPUArchState *env)
{
CPUState *cpu = env_cpu((CPUArchState *)env);
TaskState *ts = (TaskState *)cpu->opaque;
memset(info, 0, sizeof (*info));
QTAILQ_INIT(&info->thread_list);
/*
* First fill in status (and registers) of current thread
* including process info & aux vector.
*/
fill_prstatus(&info->prstatus, ts, signr);
elf_core_copy_regs(&info->prstatus.pr_reg, env);
fill_note(&info->notes[0], "CORE", NT_PRSTATUS,
sizeof(info->prstatus), &info->prstatus);
fill_psinfo(&info->psinfo, ts);
fill_note(&info->notes[1], "CORE", NT_PRPSINFO,
sizeof(info->psinfo), &info->psinfo);
fill_auxv_note(&info->notes[2], ts);
info->numnote = 3;
info->notes_size = 0;
for (int i = 0; i < info->numnote; i++) {
info->notes_size += note_size(&info->notes[i]);
}
/* read and fill status of all threads */
CPU_FOREACH(cpu) {
if (cpu == thread_cpu) {
continue;
}
fill_thread_info(info, cpu_env(cpu));
}
}
static void free_note_info(struct elf_note_info *info)
{
struct elf_thread_status *ets;
while (!QTAILQ_EMPTY(&info->thread_list)) {
ets = QTAILQ_FIRST(&info->thread_list);
QTAILQ_REMOVE(&info->thread_list, ets, ets_link);
g_free(ets);
}
}
static int write_note_info(struct elf_note_info *info, int fd)
{
struct elf_thread_status *ets;
int i, error = 0;
/* write prstatus, psinfo and auxv for current thread */
for (i = 0; i < info->numnote; i++)
if ((error = write_note(&info->notes[i], fd)) != 0)
return (error);
/* write prstatus for each thread */
QTAILQ_FOREACH(ets, &info->thread_list, ets_link) {
if ((error = write_note(&ets->notes[0], fd)) != 0)
return (error);
}
return (0);
}
/*
* Write out ELF coredump.
*
@ -4615,14 +4429,13 @@ static int elf_core_dump(int signr, const CPUArchState *env)
const CPUState *cpu = env_cpu((CPUArchState *)env);
const TaskState *ts = (const TaskState *)cpu->opaque;
struct vm_area_struct *vma;
struct elf_note_info info;
struct elfhdr elf;
struct elf_phdr phdr;
struct rlimit dumpsize;
struct mm_struct mm;
off_t offset, note_offset, data_offset;
size_t note_size;
int segs, cpus, ret;
int fd = -1;
CPUState *cpu_iter;
if (prctl(PR_GET_DUMPABLE) == 0) {
return 0;
@ -4644,7 +4457,7 @@ static int elf_core_dump(int signr, const CPUArchState *env)
segs = vma_get_mapping_count(&mm);
cpus = 0;
CPU_FOREACH(cpu) {
CPU_FOREACH(cpu_iter) {
cpus++;
}
@ -4655,6 +4468,7 @@ static int elf_core_dump(int signr, const CPUArchState *env)
offset += size_note("CORE", ts->info->auxv_len);
offset += size_note("CORE", sizeof(struct target_elf_prpsinfo));
offset += size_note("CORE", sizeof(struct target_elf_prstatus)) * cpus;
note_size = offset - note_offset;
offset = ROUND_UP(offset, ELF_EXEC_PAGESIZE);
data_offset = offset;
@ -4678,61 +4492,64 @@ static int elf_core_dump(int signr, const CPUArchState *env)
}
/*
* Construct valid coredump ELF header. We also
* add one more segment for notes.
* There is a fair amount of alignment padding within the notes
* as well as preceeding the process memory. Allocate a zeroed
* block to hold it all. Write all of the headers directly into
* this buffer and then write it out as a block.
*/
fill_elf_header(&elf, segs + 1, ELF_MACHINE, 0);
if (dump_write(fd, &elf, sizeof (elf)) != 0)
goto out;
{
g_autofree void *header = g_malloc0(data_offset);
void *hptr, *dptr;
/* fill in the in-memory version of notes */
fill_note_info(&info, signr, env);
/* Create elf file header. */
hptr = header;
fill_elf_header(hptr, segs + 1, ELF_MACHINE, 0);
hptr += sizeof(struct elfhdr);
/* write out notes program header */
fill_elf_note_phdr(&phdr, info.notes_size, note_offset);
/* Create elf program headers. */
fill_elf_note_phdr(hptr, note_size, note_offset);
hptr += sizeof(struct elf_phdr);
if (dump_write(fd, &phdr, sizeof (phdr)) != 0)
goto out;
offset = data_offset;
for (vma = vma_first(&mm); vma != NULL; vma = vma_next(vma)) {
struct elf_phdr *phdr = hptr;
/*
* Write program headers for memory regions mapped in
* the target process.
*/
offset = data_offset;
for (vma = vma_first(&mm); vma != NULL; vma = vma_next(vma)) {
(void) memset(&phdr, 0, sizeof (phdr));
phdr->p_type = PT_LOAD;
phdr->p_offset = offset;
phdr->p_vaddr = vma->vma_start;
phdr->p_paddr = 0;
phdr->p_filesz = vma_dump_size(vma);
offset += phdr->p_filesz;
phdr->p_memsz = vma->vma_end - vma->vma_start;
phdr->p_flags = (vma->vma_flags & PROT_READ ? PF_R : 0)
| (vma->vma_flags & PROT_WRITE ? PF_W : 0)
| (vma->vma_flags & PROT_EXEC ? PF_X : 0);
phdr->p_align = ELF_EXEC_PAGESIZE;
phdr.p_type = PT_LOAD;
phdr.p_offset = offset;
phdr.p_vaddr = vma->vma_start;
phdr.p_paddr = 0;
phdr.p_filesz = vma_dump_size(vma);
offset += phdr.p_filesz;
phdr.p_memsz = vma->vma_end - vma->vma_start;
phdr.p_flags = vma->vma_flags & PROT_READ ? PF_R : 0;
if (vma->vma_flags & PROT_WRITE)
phdr.p_flags |= PF_W;
if (vma->vma_flags & PROT_EXEC)
phdr.p_flags |= PF_X;
phdr.p_align = ELF_EXEC_PAGESIZE;
bswap_phdr(phdr, 1);
hptr += sizeof(struct elf_phdr);
}
bswap_phdr(&phdr, 1);
if (dump_write(fd, &phdr, sizeof(phdr)) != 0) {
/* Create the notes. */
dptr = fill_note(&hptr, NT_AUXV, "CORE", ts->info->auxv_len);
fill_auxv_note(dptr, ts);
dptr = fill_note(&hptr, NT_PRPSINFO, "CORE",
sizeof(struct target_elf_prpsinfo));
fill_prpsinfo_note(dptr, ts);
CPU_FOREACH(cpu_iter) {
dptr = fill_note(&hptr, NT_PRSTATUS, "CORE",
sizeof(struct target_elf_prstatus));
fill_prstatus_note(dptr, ts, cpu_iter,
cpu_iter == cpu ? signr : 0);
}
if (dump_write(fd, header, data_offset) < 0) {
goto out;
}
}
/*
* Next we write notes just after program headers. No
* alignment needed here.
*/
if (write_note_info(&info, fd) < 0)
goto out;
/* align data to page boundary */
if (lseek(fd, data_offset, SEEK_SET) != data_offset)
goto out;
/*
* Finally we can dump process memory into corefile as well.
*/
@ -4768,7 +4585,6 @@ static int elf_core_dump(int signr, const CPUArchState *env)
ret = -errno;
mmap_unlock();
cpu_list_unlock();
free_note_info(&info);
vma_delete(&mm);
close(fd);
return ret;