contrib/elf2dmp: Clamp QEMU note to file size
This fixes crashes with truncated dumps. Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Tested-by: Viktor Prutyanov <viktor.prutyanov@phystech.edu> Message-id: 20240307-elf2dmp-v4-18-4f324ad4d99d@daynix.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
0c94e32dff
commit
9de37c2883
@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/host-utils.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
#include "qemu_elf.h"
|
#include "qemu_elf.h"
|
||||||
|
|
||||||
@ -15,36 +16,11 @@
|
|||||||
#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
|
#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DIV_ROUND_UP
|
|
||||||
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \
|
|
||||||
((DIV_ROUND_UP((hdr_size), 4) + \
|
|
||||||
DIV_ROUND_UP((name_size), 4) + \
|
|
||||||
DIV_ROUND_UP((desc_size), 4)) * 4)
|
|
||||||
|
|
||||||
int is_system(QEMUCPUState *s)
|
int is_system(QEMUCPUState *s)
|
||||||
{
|
{
|
||||||
return s->gs.base >> 63;
|
return s->gs.base >> 63;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *nhdr_get_name(Elf64_Nhdr *nhdr)
|
|
||||||
{
|
|
||||||
return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *nhdr_get_desc(Elf64_Nhdr *nhdr)
|
|
||||||
{
|
|
||||||
return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr)
|
|
||||||
{
|
|
||||||
return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr),
|
|
||||||
nhdr->n_namesz, nhdr->n_descsz));
|
|
||||||
}
|
|
||||||
|
|
||||||
Elf64_Phdr *elf64_getphdr(void *map)
|
Elf64_Phdr *elf64_getphdr(void *map)
|
||||||
{
|
{
|
||||||
Elf64_Ehdr *ehdr = map;
|
Elf64_Ehdr *ehdr = map;
|
||||||
@ -60,13 +36,35 @@ Elf64_Half elf_getphdrnum(void *map)
|
|||||||
return ehdr->e_phnum;
|
return ehdr->e_phnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool advance_note_offset(uint64_t *offsetp, uint64_t size, uint64_t end)
|
||||||
|
{
|
||||||
|
uint64_t offset = *offsetp;
|
||||||
|
|
||||||
|
if (uadd64_overflow(offset, size, &offset) || offset > UINT64_MAX - 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = ROUND_UP(offset, 4);
|
||||||
|
|
||||||
|
if (offset > end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*offsetp = offset;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool init_states(QEMU_Elf *qe)
|
static bool init_states(QEMU_Elf *qe)
|
||||||
{
|
{
|
||||||
Elf64_Phdr *phdr = elf64_getphdr(qe->map);
|
Elf64_Phdr *phdr = elf64_getphdr(qe->map);
|
||||||
Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset);
|
|
||||||
Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz);
|
|
||||||
Elf64_Nhdr *nhdr;
|
Elf64_Nhdr *nhdr;
|
||||||
GPtrArray *states;
|
GPtrArray *states;
|
||||||
|
QEMUCPUState *state;
|
||||||
|
uint32_t state_size;
|
||||||
|
uint64_t offset;
|
||||||
|
uint64_t end_offset;
|
||||||
|
char *name;
|
||||||
|
|
||||||
if (phdr[0].p_type != PT_NOTE) {
|
if (phdr[0].p_type != PT_NOTE) {
|
||||||
eprintf("Failed to find PT_NOTE\n");
|
eprintf("Failed to find PT_NOTE\n");
|
||||||
@ -74,15 +72,40 @@ static bool init_states(QEMU_Elf *qe)
|
|||||||
}
|
}
|
||||||
|
|
||||||
qe->has_kernel_gs_base = 1;
|
qe->has_kernel_gs_base = 1;
|
||||||
|
offset = phdr[0].p_offset;
|
||||||
states = g_ptr_array_new();
|
states = g_ptr_array_new();
|
||||||
|
|
||||||
for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
|
if (uadd64_overflow(offset, phdr[0].p_memsz, &end_offset) ||
|
||||||
if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
|
end_offset > qe->size) {
|
||||||
QEMUCPUState *state = nhdr_get_desc(nhdr);
|
end_offset = qe->size;
|
||||||
|
}
|
||||||
|
|
||||||
if (state->size < sizeof(*state)) {
|
while (offset < end_offset) {
|
||||||
|
nhdr = (void *)((uint8_t *)qe->map + offset);
|
||||||
|
|
||||||
|
if (!advance_note_offset(&offset, sizeof(*nhdr), end_offset)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = (char *)qe->map + offset;
|
||||||
|
|
||||||
|
if (!advance_note_offset(&offset, nhdr->n_namesz, end_offset)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = (void *)((uint8_t *)qe->map + offset);
|
||||||
|
|
||||||
|
if (!advance_note_offset(&offset, nhdr->n_descsz, end_offset)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(name, QEMU_NOTE_NAME) &&
|
||||||
|
nhdr->n_descsz >= offsetof(QEMUCPUState, kernel_gs_base)) {
|
||||||
|
state_size = MIN(state->size, nhdr->n_descsz);
|
||||||
|
|
||||||
|
if (state_size < sizeof(*state)) {
|
||||||
eprintf("CPU #%u: QEMU CPU state size %u doesn't match\n",
|
eprintf("CPU #%u: QEMU CPU state size %u doesn't match\n",
|
||||||
states->len, state->size);
|
states->len, state_size);
|
||||||
/*
|
/*
|
||||||
* We assume either every QEMU CPU state has KERNEL_GS_BASE or
|
* We assume either every QEMU CPU state has KERNEL_GS_BASE or
|
||||||
* no one has.
|
* no one has.
|
||||||
|
Loading…
Reference in New Issue
Block a user