Skip to content

Commit 8cce437

Browse files
Philipp RudoMartin Schwidefsky
authored andcommitted
s390/kdump: Fix elfcorehdr size calculation
Before the memory for the elfcorehdr is allocated the required size is estimated with alloc_size = 0x1000 + get_cpu_cnt() * 0x4a0 + mem_chunk_cnt * sizeof(Elf64_Phdr); Where 0x4a0 is used as size for the ELF notes to store the register contend. This size is 8 bytes too small. Usually this does not immediately cause a problem because the page reserved for overhead (Elf_Ehdr, vmcoreinfo, etc.) is pretty generous. So usually there is enough spare memory to counter the mis-calculated per cpu size. However, with growing overhead and/or a huge cpu count the allocated size gets too small for the elfcorehdr. Ultimately a BUG_ON is triggered causing the crash kernel to panic. Fix this by properly calculating the required size instead of relying on magic numbers. Fixes: a62bc07 ("s390/kdump: add support for vector extension") Signed-off-by: Philipp Rudo <[email protected]> Signed-off-by: Martin Schwidefsky <[email protected]>
1 parent 5223c67 commit 8cce437

File tree

1 file changed

+98
-6
lines changed

1 file changed

+98
-6
lines changed

arch/s390/kernel/crash_dump.c

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,15 @@ static void *kzalloc_panic(int len)
306306
return rc;
307307
}
308308

309+
static const char *nt_name(Elf64_Word type)
310+
{
311+
const char *name = "LINUX";
312+
313+
if (type == NT_PRPSINFO || type == NT_PRSTATUS || type == NT_PRFPREG)
314+
name = KEXEC_CORE_NOTE_NAME;
315+
return name;
316+
}
317+
309318
/*
310319
* Initialize ELF note
311320
*/
@@ -332,11 +341,26 @@ static void *nt_init_name(void *buf, Elf64_Word type, void *desc, int d_len,
332341

333342
static inline void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len)
334343
{
335-
const char *note_name = "LINUX";
344+
return nt_init_name(buf, type, desc, d_len, nt_name(type));
345+
}
346+
347+
/*
348+
* Calculate the size of ELF note
349+
*/
350+
static size_t nt_size_name(int d_len, const char *name)
351+
{
352+
size_t size;
336353

337-
if (type == NT_PRPSINFO || type == NT_PRSTATUS || type == NT_PRFPREG)
338-
note_name = KEXEC_CORE_NOTE_NAME;
339-
return nt_init_name(buf, type, desc, d_len, note_name);
354+
size = sizeof(Elf64_Nhdr);
355+
size += roundup(strlen(name) + 1, 4);
356+
size += roundup(d_len, 4);
357+
358+
return size;
359+
}
360+
361+
static inline size_t nt_size(Elf64_Word type, int d_len)
362+
{
363+
return nt_size_name(d_len, nt_name(type));
340364
}
341365

342366
/*
@@ -374,6 +398,29 @@ static void *fill_cpu_elf_notes(void *ptr, int cpu, struct save_area *sa)
374398
return ptr;
375399
}
376400

401+
/*
402+
* Calculate size of ELF notes per cpu
403+
*/
404+
static size_t get_cpu_elf_notes_size(void)
405+
{
406+
struct save_area *sa = NULL;
407+
size_t size;
408+
409+
size = nt_size(NT_PRSTATUS, sizeof(struct elf_prstatus));
410+
size += nt_size(NT_PRFPREG, sizeof(elf_fpregset_t));
411+
size += nt_size(NT_S390_TIMER, sizeof(sa->timer));
412+
size += nt_size(NT_S390_TODCMP, sizeof(sa->todcmp));
413+
size += nt_size(NT_S390_TODPREG, sizeof(sa->todpreg));
414+
size += nt_size(NT_S390_CTRS, sizeof(sa->ctrs));
415+
size += nt_size(NT_S390_PREFIX, sizeof(sa->prefix));
416+
if (MACHINE_HAS_VX) {
417+
size += nt_size(NT_S390_VXRS_HIGH, sizeof(sa->vxrs_high));
418+
size += nt_size(NT_S390_VXRS_LOW, sizeof(sa->vxrs_low));
419+
}
420+
421+
return size;
422+
}
423+
377424
/*
378425
* Initialize prpsinfo note (new kernel)
379426
*/
@@ -429,6 +476,30 @@ static void *nt_vmcoreinfo(void *ptr)
429476
return nt_init_name(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
430477
}
431478

479+
static size_t nt_vmcoreinfo_size(void)
480+
{
481+
const char *name = "VMCOREINFO";
482+
char nt_name[11];
483+
Elf64_Nhdr note;
484+
void *addr;
485+
486+
if (copy_oldmem_kernel(&addr, &S390_lowcore.vmcore_info, sizeof(addr)))
487+
return 0;
488+
489+
if (copy_oldmem_kernel(&note, addr, sizeof(note)))
490+
return 0;
491+
492+
memset(nt_name, 0, sizeof(nt_name));
493+
if (copy_oldmem_kernel(nt_name, addr + sizeof(note),
494+
sizeof(nt_name) - 1))
495+
return 0;
496+
497+
if (strcmp(nt_name, name) != 0)
498+
return 0;
499+
500+
return nt_size_name(note.n_descsz, name);
501+
}
502+
432503
/*
433504
* Initialize final note (needed for /proc/vmcore code)
434505
*/
@@ -539,6 +610,27 @@ static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
539610
return ptr;
540611
}
541612

613+
static size_t get_elfcorehdr_size(int mem_chunk_cnt)
614+
{
615+
size_t size;
616+
617+
size = sizeof(Elf64_Ehdr);
618+
/* PT_NOTES */
619+
size += sizeof(Elf64_Phdr);
620+
/* nt_prpsinfo */
621+
size += nt_size(NT_PRPSINFO, sizeof(struct elf_prpsinfo));
622+
/* regsets */
623+
size += get_cpu_cnt() * get_cpu_elf_notes_size();
624+
/* nt_vmcoreinfo */
625+
size += nt_vmcoreinfo_size();
626+
/* nt_final */
627+
size += sizeof(Elf64_Nhdr);
628+
/* PT_LOADS */
629+
size += mem_chunk_cnt * sizeof(Elf64_Phdr);
630+
631+
return size;
632+
}
633+
542634
/*
543635
* Create ELF core header (new kernel)
544636
*/
@@ -566,8 +658,8 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
566658

567659
mem_chunk_cnt = get_mem_chunk_cnt();
568660

569-
alloc_size = 0x1000 + get_cpu_cnt() * 0x4a0 +
570-
mem_chunk_cnt * sizeof(Elf64_Phdr);
661+
alloc_size = get_elfcorehdr_size(mem_chunk_cnt);
662+
571663
hdr = kzalloc_panic(alloc_size);
572664
/* Init elf header */
573665
ptr = ehdr_init(hdr, mem_chunk_cnt);

0 commit comments

Comments
 (0)