Files
s390-tools/zdump/dfi.h
Alexander Egorenkov f15e32cdb1 zdump/dfi: Fix illegal memory access in dfi_cpu_add_from_lc()
Check that a CPU's lowcore address falls within a valid memory region
before accessing it. This prevents potential illegal memory accesses
in case a dump contains invalid CPU lowcore addresses.

This bug was found with AFL fuzzing and ASAN.

Starting program: /root/s390-tools/zdump/zgetdump -iVVVVV /root/zgetdump-fuzzing/findings/crashes/id:000004,sig:06,src:000005,op:flip32,pos:3055
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
TRACE: DFI initialization
DEBUG: DFI trying s390tape
DEBUG: DFI s390tape returned with rc -19
DEBUG: DFI trying devmem
DEBUG: DFI devmem returned with rc -19
DEBUG: DFI trying s390mv_ext
DEBUG: DFI s390mv_ext returned with rc -19
DEBUG: DFI trying s390mv
DEBUG: DFI s390mv returned with rc -19
DEBUG: DFI trying s390_ext
DEBUG: DFI S390 extended initialization
DEBUG: DFI s390_ext returned with rc -19
DEBUG: DFI trying s390
DEBUG: DFI S390 initialization
 INFO: DFI S390 version 5
DEBUG: DFI S390 mem_size 0x00000000000dbba0
DEBUG: DFI add vol mem chunk start 0x0000000000000000 size 0x00000000000dbba0 volnr 0

[snip]

TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x0000000000000000 size 0x0000000000002000
TRACE: DFI virt mem read addr 0x00000000000000ff size 0x0000000000002000
TRACE: DFI virt mem read addr 0x00000000ffffff00 size 0x0000000000002000

Program received signal SIGSEGV, Segmentation fault.
0x000000000100fbb0 in mem_read (cnt=<optimized out>, buf=0x3ffffffc7d0, addr=4294967040, mem=0x104b218 <l+152>) at dfi.c:339
339                     size = MIN(cnt - copied, mem_chunk->end - addr + 1);
Missing separate debuginfos, use: dnf debuginfo-install fuse-libs-2.9.9-11.fc34.s390x glibc-2.33-20.1.ibm.fc34.s390x libasan-11.0.1-0.3.1.ibm.fc34.s390x libgcc-11.0.1-0.3.1.ibm.fc34.s390x libstdc++-11.0.1-0.3.1.ibm.fc34.s390x zlib-1.2.11-26.fc34.s390x
(gdb) bt

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-10-01 14:59:56 +02:00

297 lines
8.3 KiB
C

/*
* zgetdump - Tool for copying and converting System z dumps
*
* Generic input dump format functions (DFI - Dump Format Input)
*
* 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.
*/
#ifndef DFI_H
#define DFI_H
#include <linux/utsname.h>
#include "lib/zt_common.h"
#include "lib/util_list.h"
#include "zg.h"
/*
* CPU info functions and definitions
*/
enum dfi_arch {
DFI_ARCH_32 = 0,
DFI_ARCH_64 = 1,
DFI_ARCH_UNKNOWN = 2,
};
struct dfi_lowcore_32 {
u8 pad_0x0000[0x0084 - 0x0000]; /* 0x0000 */
u16 cpu_addr; /* 0x0084 */
u8 pad_0x0086[0x00d4 - 0x0086]; /* 0x0086 */
u32 extended_save_area_addr; /* 0x00d4 */
u32 timer_save_area[2]; /* 0x00d8 */
u32 clock_comp_save_area[2]; /* 0x00e0 */
u32 mcck_interruption_code[2]; /* 0x00e8 */
u8 pad_0x00f0[0x00f4-0x00f0]; /* 0x00f0 */
u32 external_damage_code; /* 0x00f4 */
u32 failing_storage_address; /* 0x00f8 */
u8 pad_0x00fc[0x0100-0x00fc]; /* 0x00fc */
u32 st_status_fixed_logout[2]; /* 0x0100 */
u32 prefixreg_save_area; /* 0x0108 */
u8 pad_0x0110[0x0120-0x010c]; /* 0x010c */
u32 access_regs_save_area[16]; /* 0x0120 */
u32 floating_pt_save_area[8]; /* 0x0160 */
u32 gpregs_save_area[16]; /* 0x0180 */
u32 cregs_save_area[16]; /* 0x01c0 */
u8 pad_0x0200[0x1000 - 0x0200]; /* 0x0200 */
};
struct dfi_lowcore_64 {
u8 pad_0x0000[0x0084 - 0x0000]; /* 0x0000 */
u16 cpu_addr; /* 0x0084 */
u8 pad_0x0086[0x11b0 - 0x0086]; /* 0x0086 */
u64 vector_save_area_addr; /* 0x11b0 */
u8 pad_0x11b8[0x1200 - 0x11b8]; /* 0x11b8 */
u64 floating_pt_save_area[16]; /* 0x1200 */
u64 gpregs_save_area[16]; /* 0x1280 */
u32 st_status_fixed_logout[4]; /* 0x1300 */
u8 pad_0x1310[0x1318-0x1310]; /* 0x1310 */
u32 prefixreg_save_area; /* 0x1318 */
u32 fpt_creg_save_area; /* 0x131c */
u8 pad_0x1320[0x1324-0x1320]; /* 0x1320 */
u32 tod_progreg_save_area; /* 0x1324 */
u32 timer_save_area[2]; /* 0x1328 */
u32 clock_comp_save_area[2]; /* 0x1330 */
u8 pad_0x1338[0x1340-0x1338]; /* 0x1338 */
u32 access_regs_save_area[16]; /* 0x1340 */
u64 cregs_save_area[16]; /* 0x1380 */
u8 pad_0x1400[0x2000-0x1400]; /* 0x1400 */
} __packed;
static inline u64 dfi_lc_size(enum dfi_arch arch)
{
if (arch == DFI_ARCH_64)
return 0x2000;
else
return 0x1000;
}
struct dfi_vxrs {
u64 low;
u64 high;
};
struct dfi_cpu {
struct util_list_node list;
u64 gprs[16];
u64 ctrs[16];
u32 acrs[16];
u64 fprs[16];
u32 fpc;
u64 psw[2];
u32 prefix;
u64 timer;
u64 todcmp;
u32 todpreg;
u64 vxrs_low[16];
struct dfi_vxrs vxrs_high[16];
u16 cpu_id;
};
struct dfi_cpu_32 {
u32 gprs[16];
u32 ctrs[16];
u32 acrs[16];
u64 fprs[4];
u32 psw[2];
u32 prefix;
u64 timer;
u64 todcmp;
u64 vxrs_low[16];
struct dfi_vxrs vxrs_high[16];
};
extern void dfi_cpu_64_to_32(struct dfi_cpu_32 *cpu_32, struct dfi_cpu *cpu_64);
extern enum dfi_arch dfi_arch(void);
extern void dfi_arch_set(enum dfi_arch arch);
extern const char *dfi_arch_str(enum dfi_arch arch);
enum dfi_cpu_content {
DFI_CPU_CONTENT_NONE, /* No register information available */
DFI_CPU_CONTENT_LC, /* Only lowcore information available */
DFI_CPU_CONTENT_ALL, /* Complete register information available */
};
#define DFI_CPU_CONTENT_FAC_VX 0x00000001
extern int dfi_cpu_content_fac_check(int flags);
extern void dfi_cpu_content_fac_add(int flags);
#define dfi_cpu_iterate(cpu) \
util_list_iterate(dfi_cpu_list(), cpu)
extern struct util_list *dfi_cpu_list(void);
extern void dfi_cpu_info_init(enum dfi_cpu_content content);
extern struct dfi_cpu *dfi_cpu_alloc(void);
extern struct dfi_cpu *dfi_cpu(unsigned int cpu_nr);
extern void dfi_cpu_add(struct dfi_cpu *cpu);
extern unsigned int dfi_cpu_cnt(void);
extern enum dfi_cpu_content dfi_cpu_content(void);
extern int dfi_cpu_add_from_lc(u32 lc_addr);
#define DFI_VX_SA_SIZE (32 * 16)
extern int dfi_cpu_lc_has_vx_sa(void *lc);
extern void dfi_cpu_vx_copy(void *buf, struct dfi_cpu *cpu);
/*
* Mem chunk functions and definitions
*/
struct dfi_mem_chunk;
typedef void (*dfi_mem_chunk_read_fn)(struct dfi_mem_chunk *mem_chunk,
u64 off, void *buf, u64 cnt);
typedef void (*dfi_mem_chunk_free_fn)(void *data);
struct dfi_mem_chunk {
struct util_list_node list; /* List */
u64 start; /* Start address in memory */
u64 end; /* End address in memory */
u64 size; /* Size of chunk in dump file */
dfi_mem_chunk_read_fn read_fn; /* Chunk read callback */
dfi_mem_chunk_free_fn free_fn; /* Free data callback */
void *data; /* Data for callback */
u32 volnr; /* Volume id where chunk resides */
};
extern void dfi_mem_chunk_read_zero(struct dfi_mem_chunk *UNUSED(mem_chunk),
u64 UNUSED(off), void *buf, u64 cnt);
extern void dfi_mem_chunk_add_vol(u64 start, u64 size, void *data,
dfi_mem_chunk_read_fn read_fn,
dfi_mem_chunk_free_fn free_fn,
u32 volnr);
extern void dfi_mem_chunk_add(u64 start, u64 size, void *data,
dfi_mem_chunk_read_fn read_fn,
dfi_mem_chunk_free_fn free_fn);
extern void dfi_mem_chunk_virt_add(u64 start, u64 size, void *data,
dfi_mem_chunk_read_fn read_fn,
dfi_mem_chunk_free_fn free_fn);
extern u64 dfi_mem_range(void);
extern int dfi_mem_range_valid(u64 addr, u64 len);
extern unsigned int dfi_mem_chunk_cnt(void);
extern struct dfi_mem_chunk *dfi_mem_chunk_first(void);
extern struct dfi_mem_chunk *dfi_mem_chunk_last(void);
extern struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *chunk);
extern struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *chunk);
extern struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr);
extern struct util_list *dfi_mem_chunk_list(void);
#define dfi_mem_chunk_iterate(mem_chunk) \
util_list_iterate(dfi_mem_chunk_list(), mem_chunk)
/*
* Dump header attribute set/get functions
*/
extern void dfi_attr_time_set(struct timeval *time);
extern struct timeval *dfi_attr_time(void);
extern void dfi_attr_time_end_set(struct timeval *time_end);
extern struct timeval *dfi_attr_time_end(void);
extern void dfi_attr_cpu_id_set(u64 cpu_id);
extern u64 *dfi_attr_cpu_id(void);
extern void dfi_attr_utsname_set(struct new_utsname *utsname);
extern struct new_utsname *dfi_attr_utsname(void);
extern void dfi_attr_dump_method_set(char *dump_method);
extern char *dfi_attr_dump_method(void);
extern void dfi_attr_mem_size_real_set(u64 mem_size_real);
extern u64 *dfi_attr_mem_size_real(void);
extern void dfi_attr_file_size_set(u64 dump_size);
extern u64 *dfi_attr_file_size(void);
extern void dfi_attr_vol_nr_set(unsigned int vol_nr);
extern unsigned int *dfi_attr_vol_nr(void);
extern void dfi_attr_version_set(unsigned int dfi_version);
extern unsigned int *dfi_attr_dfi_version(void);
extern void dfi_attr_build_arch_set(enum dfi_arch build_arch);
extern enum dfi_arch *dfi_attr_build_arch(void);
extern void dfi_attr_real_cpu_cnt_set(u32 real_cpu_cnt);
extern u32 *dfi_attr_real_cpu_cnt(void);
/*
* DFI external functions
*/
extern void dfi_mem_read(u64 addr, void *buf, size_t cnt);
extern int dfi_mem_read_rc(u64 addr, void *buf, size_t cnt);
extern void dfi_mem_phys_read(u64 addr, void *buf, size_t cnt);
extern void dfi_info_print(void);
/*
* DFI feature bits
*/
#define DFI_FEAT_SEEK 0x1 /* Necessary for fuse mount */
#define DFI_FEAT_COPY 0x2 /* Necessary for stdout */
extern int dfi_feat_seek(void);
extern int dfi_feat_copy(void);
/*
* DFI kdump functions
*/
extern unsigned long dfi_kdump_base(void);
/*
* DFI vmcoreinfo functions
*/
extern void dfi_vmcoreinfo_init(void);
extern char *dfi_vmcoreinfo_get(void);
extern int dfi_vmcoreinfo_tag(char *str, int len, const char *sym);
extern int dfi_vmcoreinfo_symbol(unsigned long *val, const char *sym);
extern int dfi_vmcoreinfo_offset(unsigned long *offs, const char *sym);
extern int dfi_vmcoreinfo_size(unsigned long *size, const char *sym);
extern int dfi_vmcoreinfo_length(unsigned long *len, const char *sym);
extern int dfi_vmcoreinfo_val(unsigned long *val, const char *sym);
/*
* DFI operations
*/
struct dfi {
const char *name;
int (*init)(void);
void (*exit)(void);
void (*info_dump)(void);
int feat_bits;
};
extern const char *dfi_name(void);
extern int dfi_init(void);
extern void dfi_exit(void);
/*
* Dump access
*/
extern struct zg_fh *dfi_dump_open(const char *path);
/*
* Live dump memory magic
*/
extern u64 dfi_live_dump_magic;
/*
* Dump methods
*/
#define DFI_DUMP_METHOD_LIVE "live"
#endif /* DFI_H */