mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
dfo_elf_init() allocates the output ELF header buffer based on HDR_BASE_SIZE, per-CPU note sizes, and per-memory-chunk overhead, but does not account for the vmcoreinfo note. notes_init() then writes the full vmcoreinfo string into the buffer via nt_vmcoreinfo(), overflowing the heap allocation for any dump with a vmcoreinfo blob larger than what fits in HDR_BASE_SIZE. The following ABORT check fires after the heap was already corrupted. Fix by extracting the allocation size calculation into a new hdr_alloc_size() function that explicitly accounts for all items written into the header buffer. Remove the opaque HDR_BASE_SIZE and HDR_PER_MEMC_SIZE macros. Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com> Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
172 lines
4.2 KiB
C
172 lines
4.2 KiB
C
/*
|
|
* zgetdump - Tool for copying and converting System z dumps
|
|
*
|
|
* ELF core dump output format
|
|
*
|
|
* Copyright IBM Corp. 2001, 2018
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <elf.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "df_elf.h"
|
|
#include "dfi.h"
|
|
#include "dfi_mem_chunk.h"
|
|
#include "dfo_mem_chunk.h"
|
|
#include "dfi_vmcoreinfo.h"
|
|
#include "dfo.h"
|
|
|
|
/*
|
|
* Initialize ELF loads program headers
|
|
*/
|
|
static u64 load_phdrs_init(Elf64_Phdr *phdr, u64 elf_offset)
|
|
{
|
|
struct dfi_mem_chunk *mem_chunk;
|
|
u64 mem_size = 0;
|
|
|
|
dfi_mem_chunk_iterate(mem_chunk) {
|
|
phdr->p_type = PT_LOAD;
|
|
phdr->p_offset = elf_offset;
|
|
phdr->p_vaddr = mem_chunk->start;
|
|
phdr->p_paddr = phdr->p_vaddr;
|
|
phdr->p_memsz = mem_chunk->size;
|
|
if (mem_chunk->read_fn == dfi_mem_chunk_read_zero)
|
|
/* Zero memory chunk */
|
|
phdr->p_filesz = 0;
|
|
else
|
|
phdr->p_filesz = phdr->p_memsz;
|
|
phdr->p_flags = PF_R | PF_W | PF_X;
|
|
phdr->p_align = PAGE_SIZE;
|
|
elf_offset += phdr->p_filesz;
|
|
mem_size += phdr->p_memsz;
|
|
phdr++;
|
|
}
|
|
return mem_size;
|
|
}
|
|
|
|
/*
|
|
* Initialize the program header entries for the notes and the related segment
|
|
* data.
|
|
*/
|
|
static void *notes_init(Elf64_Phdr *phdr, void *segment_start, u64 elf_offset)
|
|
{
|
|
void *ptr = segment_start;
|
|
struct dfi_cpu *cpu;
|
|
|
|
ptr = nt_prpsinfo(ptr);
|
|
|
|
if (dfi_cpu_content() != DFI_CPU_CONTENT_ALL)
|
|
goto out;
|
|
|
|
dfi_cpu_iterate(cpu) {
|
|
ptr = nt_prstatus(ptr, cpu);
|
|
ptr = nt_fpregset(ptr, cpu);
|
|
ptr = nt_s390_timer(ptr, cpu);
|
|
ptr = nt_s390_tod_cmp(ptr, cpu);
|
|
ptr = nt_s390_tod_preg(ptr, cpu);
|
|
ptr = nt_s390_ctrs(ptr, cpu);
|
|
ptr = nt_s390_prefix(ptr, cpu);
|
|
if (dfi_cpu_content_fac_check(DFI_CPU_CONTENT_FAC_VX)) {
|
|
ptr = nt_s390_vxrs_low(ptr, cpu);
|
|
ptr = nt_s390_vxrs_high(ptr, cpu);
|
|
}
|
|
if (dfi_cpu_content_fac_check(DFI_CPU_CONTENT_FAC_GS))
|
|
ptr = nt_s390_gs_cb(ptr, cpu);
|
|
}
|
|
out:
|
|
ptr = nt_vmcoreinfo(ptr, dfi_vmcoreinfo_get());
|
|
memset(phdr, 0, sizeof(*phdr));
|
|
phdr->p_type = PT_NOTE;
|
|
phdr->p_offset = elf_offset;
|
|
phdr->p_filesz = PTR_DIFF(ptr, segment_start);
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
* Setup dump chunks
|
|
*/
|
|
static void dump_chunks_init(void *hdr, u64 hdr_size)
|
|
{
|
|
struct dfi_mem_chunk *mem_chunk;
|
|
u64 off = 0;
|
|
|
|
dfo_chunk_add(off, hdr_size, hdr, dfo_chunk_buf_fn);
|
|
off += hdr_size;
|
|
dfi_mem_chunk_iterate(mem_chunk) {
|
|
if (mem_chunk->read_fn == dfi_mem_chunk_read_zero)
|
|
/* Zero memory chunk */
|
|
continue;
|
|
dfo_chunk_add(off, mem_chunk->size, mem_chunk,
|
|
dfo_chunk_mem_fn);
|
|
off += mem_chunk->size;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate the size of the ELF header buffer.
|
|
*
|
|
* The buffer contains the following fixed-size items:
|
|
* - Elf64_Ehdr
|
|
* - PT_NOTE program header (1x Elf64_Phdr)
|
|
* - prpsinfo note
|
|
* Plus variable-size items:
|
|
* - PT_LOAD program headers (one Elf64_Phdr per memory chunk)
|
|
* - per-CPU notes
|
|
* - vmcoreinfo note (if present)
|
|
*/
|
|
static u32 hdr_alloc_size(const char *vmcoreinfo)
|
|
{
|
|
return sizeof(Elf64_Ehdr) +
|
|
sizeof(Elf64_Phdr) +
|
|
ELF64_NOTE_SIZE(NOTE_NAME_CORE, sizeof(struct nt_prpsinfo_64)) +
|
|
dfi_mem_chunk_cnt() * sizeof(Elf64_Phdr) +
|
|
dfi_cpu_cnt() * get_max_note_size_per_cpu() +
|
|
(vmcoreinfo ? ELF64_NOTE_SIZE(NOTE_NAME_VMCOREINFO, strlen(vmcoreinfo)) : 0);
|
|
}
|
|
|
|
/*
|
|
* Initialize ELF output dump format
|
|
*/
|
|
static void dfo_elf_init(void)
|
|
{
|
|
Elf64_Phdr *phdr_notes, *phdr_loads;
|
|
u32 alloc_size;
|
|
void *buf, *ptr;
|
|
u64 hdr_off;
|
|
const char *vmcoreinfo = dfi_vmcoreinfo_get();
|
|
|
|
df_elf_ensure_s390x();
|
|
alloc_size = hdr_alloc_size(vmcoreinfo);
|
|
buf = zg_alloc(alloc_size);
|
|
/* Init elf header */
|
|
ptr = ehdr_init(buf, dfi_mem_chunk_cnt() + 1);
|
|
/* Init program headers */
|
|
phdr_notes = ptr;
|
|
ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr));
|
|
phdr_loads = ptr;
|
|
ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr) * dfi_mem_chunk_cnt());
|
|
/* Init notes */
|
|
hdr_off = PTR_DIFF(ptr, buf);
|
|
ptr = notes_init(phdr_notes, ptr, hdr_off);
|
|
hdr_off = PTR_DIFF(ptr, buf);
|
|
load_phdrs_init(phdr_loads, hdr_off);
|
|
if (hdr_off > alloc_size)
|
|
ABORT("hdr_size=%llu alloc_size=%u", hdr_off, alloc_size);
|
|
dump_chunks_init(buf, hdr_off);
|
|
}
|
|
|
|
/*
|
|
* ELF DFO operations
|
|
*/
|
|
struct dfo dfo_elf = {
|
|
.name = "elf",
|
|
.init = dfo_elf_init,
|
|
};
|