149 lines · 3.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0 #include <linux/buildid.h>#include <linux/elf.h>#include <linux/pagemap.h> #define BUILD_ID 3/* * Parse build id from the note segment. This logic can be shared between * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are * identical. */static inline int parse_build_id(void *page_addr,				 unsigned char *build_id,				 __u32 *size,				 void *note_start,				 Elf32_Word note_size){	Elf32_Word note_offs = 0, new_offs; 	/* check for overflow */	if (note_start < page_addr || note_start + note_size < note_start)		return -EINVAL; 	/* only supports note that fits in the first page */	if (note_start + note_size > page_addr + PAGE_SIZE)		return -EINVAL; 	while (note_offs + sizeof(Elf32_Nhdr) < note_size) {		Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs); 		if (nhdr->n_type == BUILD_ID &&		    nhdr->n_namesz == sizeof("GNU") &&		    nhdr->n_descsz > 0 &&		    nhdr->n_descsz <= BUILD_ID_SIZE_MAX) {			memcpy(build_id,			       note_start + note_offs +			       ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr),			       nhdr->n_descsz);			memset(build_id + nhdr->n_descsz, 0,			       BUILD_ID_SIZE_MAX - nhdr->n_descsz);			if (size)				*size = nhdr->n_descsz;			return 0;		}		new_offs = note_offs + sizeof(Elf32_Nhdr) +			ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);		if (new_offs <= note_offs)  /* overflow */			break;		note_offs = new_offs;	}	return -EINVAL;} /* Parse build ID from 32-bit ELF */static int get_build_id_32(void *page_addr, unsigned char *build_id,			   __u32 *size){	Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr;	Elf32_Phdr *phdr;	int i; 	/* only supports phdr that fits in one page */	if (ehdr->e_phnum >	    (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))		return -EINVAL; 	phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr)); 	for (i = 0; i < ehdr->e_phnum; ++i) {		if (phdr[i].p_type == PT_NOTE &&		    !parse_build_id(page_addr, build_id, size,				    page_addr + phdr[i].p_offset,				    phdr[i].p_filesz))			return 0;	}	return -EINVAL;} /* Parse build ID from 64-bit ELF */static int get_build_id_64(void *page_addr, unsigned char *build_id,			   __u32 *size){	Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr;	Elf64_Phdr *phdr;	int i; 	/* only supports phdr that fits in one page */	if (ehdr->e_phnum >	    (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))		return -EINVAL; 	phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr)); 	for (i = 0; i < ehdr->e_phnum; ++i) {		if (phdr[i].p_type == PT_NOTE &&		    !parse_build_id(page_addr, build_id, size,				    page_addr + phdr[i].p_offset,				    phdr[i].p_filesz))			return 0;	}	return -EINVAL;} /* * Parse build ID of ELF file mapped to vma * @vma:      vma object * @build_id: buffer to store build id, at least BUILD_ID_SIZE long * @size:     returns actual build id size in case of success * * Returns 0 on success, otherwise error (< 0). */int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,		   __u32 *size){	Elf32_Ehdr *ehdr;	struct page *page;	void *page_addr;	int ret; 	/* only works for page backed storage  */	if (!vma->vm_file)		return -EINVAL; 	page = find_get_page(vma->vm_file->f_mapping, 0);	if (!page)		return -EFAULT;	/* page not mapped */ 	ret = -EINVAL;	page_addr = kmap_atomic(page);	ehdr = (Elf32_Ehdr *)page_addr; 	/* compare magic x7f "ELF" */	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)		goto out; 	/* only support executable file and shared object file */	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)		goto out; 	if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)		ret = get_build_id_32(page_addr, build_id, size);	else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)		ret = get_build_id_64(page_addr, build_id, size);out:	kunmap_atomic(page_addr);	put_page(page);	return ret;}