mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
686c331b69
The dfi_vmcoreinfo_init() function might be called on a dump of a non-ELF format because the DFI goes through all supported dump formats when first trying to identify of what dump format the given input is. Therefore, we must be very careful in interpreting read data and ensure that it makes sense before accessing or using it. This commit prevents a potential overflow of a stack buffer in dfi_vmcoreinfo_init() if note.n_namesz is bigger than the stack buffer. Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
215 lines
4.5 KiB
C
215 lines
4.5 KiB
C
/*
|
|
* zgetdump - Tool for copying and converting System z dumps
|
|
*
|
|
* vmcoreinfo access functions
|
|
*
|
|
* Copyright IBM Corp. 2011, 2017
|
|
*
|
|
* 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 <elf.h>
|
|
|
|
#include "lib/zt_common.h"
|
|
|
|
#include "zgetdump.h"
|
|
|
|
#ifdef __s390x__
|
|
#define LC_VMCORE_INFO 0xe0c
|
|
#else
|
|
#define LC_VMCORE_INFO 0xe08
|
|
#endif
|
|
|
|
#define LC_OS_INFO 0xe18
|
|
#define OS_INFO_MAGIC 0x4f53494e464f535aULL /* OSINFOSZ */
|
|
|
|
struct os_info {
|
|
u64 magic;
|
|
u32 csum;
|
|
u16 version_major;
|
|
u16 version_minor;
|
|
u64 crashkernel_addr;
|
|
u64 crashkernel_size;
|
|
u64 vmcoreinfo_addr;
|
|
u64 vmcoreinfo_size;
|
|
u32 vmcoreinfo_csum;
|
|
u64 reipl_block_addr;
|
|
u64 reipl_block_size;
|
|
u32 reipl_block_csum;
|
|
u64 init_fn_addr;
|
|
u64 init_fn_size;
|
|
u32 init_fn_csum;
|
|
u8 reserved[4004];
|
|
} __packed;
|
|
|
|
/*
|
|
* File local static data
|
|
*/
|
|
static struct {
|
|
char *vmcoreinfo;
|
|
struct os_info *os_info;
|
|
} l;
|
|
|
|
static u32 os_info_csum(struct os_info *os_info)
|
|
{
|
|
int size = sizeof(*os_info) - offsetof(struct os_info, version_major);
|
|
return zg_csum_partial(&os_info->version_major, size, 0);
|
|
}
|
|
|
|
static struct os_info *os_info_get(void)
|
|
{
|
|
static struct os_info os_info;
|
|
unsigned long addr;
|
|
|
|
dfi_mem_read(LC_OS_INFO, &addr, sizeof(addr));
|
|
if (addr % 0x1000)
|
|
return NULL;
|
|
if (dfi_mem_read_rc(addr, &os_info, sizeof(os_info)))
|
|
return NULL;
|
|
if (os_info.magic != OS_INFO_MAGIC)
|
|
return NULL;
|
|
if (os_info.csum != os_info_csum(&os_info))
|
|
return NULL;
|
|
return &os_info;
|
|
}
|
|
|
|
/*
|
|
* Initialize vmcoreinfo
|
|
*/
|
|
void dfi_vmcoreinfo_init(void)
|
|
{
|
|
unsigned long addr, size;
|
|
Elf64_Nhdr note;
|
|
char str[128];
|
|
|
|
l.os_info = os_info_get();
|
|
|
|
if (l.os_info && l.os_info->vmcoreinfo_size) {
|
|
addr = l.os_info->vmcoreinfo_addr;
|
|
size = l.os_info->vmcoreinfo_size;
|
|
} else {
|
|
dfi_mem_read(LC_VMCORE_INFO, &addr, sizeof(addr));
|
|
if (addr == 0)
|
|
return;
|
|
if (dfi_mem_read_rc(addr, ¬e, sizeof(note)))
|
|
return;
|
|
if (note.n_namesz == 0 || note.n_namesz > sizeof(str))
|
|
return;
|
|
memset(str, 0, sizeof(str));
|
|
if (dfi_mem_read_rc(addr + sizeof(note), str, note.n_namesz))
|
|
return;
|
|
if (memcmp(str, "VMCOREINFO", sizeof("VMCOREINFO")) != 0)
|
|
return;
|
|
size = note.n_descsz;
|
|
addr += 24;
|
|
}
|
|
l.vmcoreinfo = zg_alloc(size + 1);
|
|
if (dfi_mem_read_rc(addr, l.vmcoreinfo, size)) {
|
|
zg_free(l.vmcoreinfo);
|
|
l.vmcoreinfo = NULL;
|
|
return;
|
|
}
|
|
l.vmcoreinfo[size] = 0;
|
|
}
|
|
|
|
/*
|
|
* Return vmcoreinfo data
|
|
*/
|
|
char *dfi_vmcoreinfo_get(void)
|
|
{
|
|
return l.vmcoreinfo;
|
|
}
|
|
|
|
/*
|
|
* Generic function: Return vmcoreinfo item (-1 on failure)
|
|
*/
|
|
static int vmcoreinfo_item(char *buf, int UNUSED(len), const char *fmt,
|
|
const char *sym)
|
|
{
|
|
|
|
char str[1024], *sym_str, *sym_str_end;
|
|
|
|
if (!l.vmcoreinfo)
|
|
return -1;
|
|
if (fmt)
|
|
snprintf(str, sizeof(str), "%s(%s)=", fmt, sym);
|
|
else
|
|
snprintf(str, sizeof(str), "%s=", sym);
|
|
sym_str = strstr(l.vmcoreinfo, str);
|
|
if (!sym_str)
|
|
return -1;
|
|
sym_str += strlen(str);
|
|
sym_str_end = strchr(sym_str, '\n');
|
|
if (!sym_str_end)
|
|
sym_str_end = strchr(sym_str, '\0');
|
|
memset(str, 0, sizeof(str));
|
|
memcpy(str, sym_str, (unsigned long) (sym_str_end - sym_str));
|
|
strcpy(buf, str);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Generic function: Return vmcoreinfo ulong item (-1 on failure)
|
|
*/
|
|
static int vmcoreinfo_item_ulong(unsigned long *val, const char *fmt,
|
|
const char *sym, unsigned long base)
|
|
{
|
|
char str[1024];
|
|
int rc;
|
|
|
|
rc = vmcoreinfo_item(str, sizeof(str), fmt, sym);
|
|
if (rc)
|
|
return rc;
|
|
*val = strtoul(str, NULL, base);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Return vmcoreinfo tag (-1 on failure)
|
|
*/
|
|
int dfi_vmcoreinfo_tag(char *str, int len, const char *sym)
|
|
{
|
|
return vmcoreinfo_item(str, len, NULL, sym);
|
|
}
|
|
|
|
/*
|
|
* Return vmcoreinfo symbol address (-1 on failure)
|
|
*/
|
|
int dfi_vmcoreinfo_symbol(unsigned long *addr, const char *sym)
|
|
{
|
|
return vmcoreinfo_item_ulong(addr, "SYMBOL", sym, 16);
|
|
}
|
|
|
|
/*
|
|
* Return vmcoreinfo offset of a member of a datastructure (-1 on failure)
|
|
*/
|
|
int dfi_vmcoreinfo_offset(unsigned long *off, const char *sym)
|
|
{
|
|
return vmcoreinfo_item_ulong(off, "OFFSET", sym, 10);
|
|
}
|
|
|
|
/*
|
|
* Return vmcoreinfo datatype size (-1 on failure)
|
|
*/
|
|
int dfi_vmcoreinfo_size(unsigned long *size, const char *sym)
|
|
{
|
|
return vmcoreinfo_item_ulong(size, "SIZE", sym, 10);
|
|
}
|
|
|
|
/*
|
|
* Return vmcoreinfo symbol length (-1 on failure)
|
|
*/
|
|
int dfi_vmcoreinfo_length(unsigned long *len, const char *sym)
|
|
{
|
|
return vmcoreinfo_item_ulong(len, "LENGTH", sym, 10);
|
|
}
|
|
|
|
/*
|
|
* Return vmcoreinfo number (-1 on failure)
|
|
*/
|
|
int dfi_vmcoreinfo_val(unsigned long *val, const char *sym)
|
|
{
|
|
return vmcoreinfo_item_ulong(val, NULL, sym, 10);
|
|
}
|