mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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: a62bc07392 ("s390/kdump: add support for vector extension")
Signed-off-by: Philipp Rudo <prudo@linux.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
			
			
This commit is contained in:
		
							parent
							
								
									5223c67167
								
							
						
					
					
						commit
						8cce437fbb
					
				
					 1 changed files with 98 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -306,6 +306,15 @@ static void *kzalloc_panic(int len)
 | 
			
		|||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *nt_name(Elf64_Word type)
 | 
			
		||||
{
 | 
			
		||||
	const char *name = "LINUX";
 | 
			
		||||
 | 
			
		||||
	if (type == NT_PRPSINFO || type == NT_PRSTATUS || type == NT_PRFPREG)
 | 
			
		||||
		name = KEXEC_CORE_NOTE_NAME;
 | 
			
		||||
	return name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize ELF note
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -332,11 +341,26 @@ static void *nt_init_name(void *buf, Elf64_Word type, void *desc, int d_len,
 | 
			
		|||
 | 
			
		||||
static inline void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len)
 | 
			
		||||
{
 | 
			
		||||
	const char *note_name = "LINUX";
 | 
			
		||||
	return nt_init_name(buf, type, desc, d_len, nt_name(type));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	if (type == NT_PRPSINFO || type == NT_PRSTATUS || type == NT_PRFPREG)
 | 
			
		||||
		note_name = KEXEC_CORE_NOTE_NAME;
 | 
			
		||||
	return nt_init_name(buf, type, desc, d_len, note_name);
 | 
			
		||||
/*
 | 
			
		||||
 * Calculate the size of ELF note
 | 
			
		||||
 */
 | 
			
		||||
static size_t nt_size_name(int d_len, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	size_t size;
 | 
			
		||||
 | 
			
		||||
	size = sizeof(Elf64_Nhdr);
 | 
			
		||||
	size += roundup(strlen(name) + 1, 4);
 | 
			
		||||
	size += roundup(d_len, 4);
 | 
			
		||||
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline size_t nt_size(Elf64_Word type, int d_len)
 | 
			
		||||
{
 | 
			
		||||
	return nt_size_name(d_len, nt_name(type));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -374,6 +398,29 @@ static void *fill_cpu_elf_notes(void *ptr, int cpu, struct save_area *sa)
 | 
			
		|||
	return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Calculate size of ELF notes per cpu
 | 
			
		||||
 */
 | 
			
		||||
static size_t get_cpu_elf_notes_size(void)
 | 
			
		||||
{
 | 
			
		||||
	struct save_area *sa = NULL;
 | 
			
		||||
	size_t size;
 | 
			
		||||
 | 
			
		||||
	size =	nt_size(NT_PRSTATUS, sizeof(struct elf_prstatus));
 | 
			
		||||
	size +=  nt_size(NT_PRFPREG, sizeof(elf_fpregset_t));
 | 
			
		||||
	size +=  nt_size(NT_S390_TIMER, sizeof(sa->timer));
 | 
			
		||||
	size +=  nt_size(NT_S390_TODCMP, sizeof(sa->todcmp));
 | 
			
		||||
	size +=  nt_size(NT_S390_TODPREG, sizeof(sa->todpreg));
 | 
			
		||||
	size +=  nt_size(NT_S390_CTRS, sizeof(sa->ctrs));
 | 
			
		||||
	size +=  nt_size(NT_S390_PREFIX, sizeof(sa->prefix));
 | 
			
		||||
	if (MACHINE_HAS_VX) {
 | 
			
		||||
		size += nt_size(NT_S390_VXRS_HIGH, sizeof(sa->vxrs_high));
 | 
			
		||||
		size += nt_size(NT_S390_VXRS_LOW, sizeof(sa->vxrs_low));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize prpsinfo note (new kernel)
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -429,6 +476,30 @@ static void *nt_vmcoreinfo(void *ptr)
 | 
			
		|||
	return nt_init_name(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t nt_vmcoreinfo_size(void)
 | 
			
		||||
{
 | 
			
		||||
	const char *name = "VMCOREINFO";
 | 
			
		||||
	char nt_name[11];
 | 
			
		||||
	Elf64_Nhdr note;
 | 
			
		||||
	void *addr;
 | 
			
		||||
 | 
			
		||||
	if (copy_oldmem_kernel(&addr, &S390_lowcore.vmcore_info, sizeof(addr)))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (copy_oldmem_kernel(¬e, addr, sizeof(note)))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	memset(nt_name, 0, sizeof(nt_name));
 | 
			
		||||
	if (copy_oldmem_kernel(nt_name, addr + sizeof(note),
 | 
			
		||||
			       sizeof(nt_name) - 1))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(nt_name, name) != 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	return nt_size_name(note.n_descsz, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize final note (needed for /proc/vmcore code)
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -539,6 +610,27 @@ static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
 | 
			
		|||
	return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t get_elfcorehdr_size(int mem_chunk_cnt)
 | 
			
		||||
{
 | 
			
		||||
	size_t size;
 | 
			
		||||
 | 
			
		||||
	size = sizeof(Elf64_Ehdr);
 | 
			
		||||
	/* PT_NOTES */
 | 
			
		||||
	size += sizeof(Elf64_Phdr);
 | 
			
		||||
	/* nt_prpsinfo */
 | 
			
		||||
	size += nt_size(NT_PRPSINFO, sizeof(struct elf_prpsinfo));
 | 
			
		||||
	/* regsets */
 | 
			
		||||
	size += get_cpu_cnt() * get_cpu_elf_notes_size();
 | 
			
		||||
	/* nt_vmcoreinfo */
 | 
			
		||||
	size += nt_vmcoreinfo_size();
 | 
			
		||||
	/* nt_final */
 | 
			
		||||
	size += sizeof(Elf64_Nhdr);
 | 
			
		||||
	/* PT_LOADS */
 | 
			
		||||
	size += mem_chunk_cnt * sizeof(Elf64_Phdr);
 | 
			
		||||
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Create ELF core header (new kernel)
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -566,8 +658,8 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
 | 
			
		|||
 | 
			
		||||
	mem_chunk_cnt = get_mem_chunk_cnt();
 | 
			
		||||
 | 
			
		||||
	alloc_size = 0x1000 + get_cpu_cnt() * 0x4a0 +
 | 
			
		||||
		mem_chunk_cnt * sizeof(Elf64_Phdr);
 | 
			
		||||
	alloc_size = get_elfcorehdr_size(mem_chunk_cnt);
 | 
			
		||||
 | 
			
		||||
	hdr = kzalloc_panic(alloc_size);
 | 
			
		||||
	/* Init elf header */
 | 
			
		||||
	ptr = ehdr_init(hdr, mem_chunk_cnt);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue