buildid.c (5104B)
1// SPDX-License-Identifier: GPL-2.0 2 3#include <linux/buildid.h> 4#include <linux/cache.h> 5#include <linux/elf.h> 6#include <linux/kernel.h> 7#include <linux/pagemap.h> 8 9#define BUILD_ID 3 10 11/* 12 * Parse build id from the note segment. This logic can be shared between 13 * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are 14 * identical. 15 */ 16static int parse_build_id_buf(unsigned char *build_id, 17 __u32 *size, 18 const void *note_start, 19 Elf32_Word note_size) 20{ 21 Elf32_Word note_offs = 0, new_offs; 22 23 while (note_offs + sizeof(Elf32_Nhdr) < note_size) { 24 Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs); 25 26 if (nhdr->n_type == BUILD_ID && 27 nhdr->n_namesz == sizeof("GNU") && 28 !strcmp((char *)(nhdr + 1), "GNU") && 29 nhdr->n_descsz > 0 && 30 nhdr->n_descsz <= BUILD_ID_SIZE_MAX) { 31 memcpy(build_id, 32 note_start + note_offs + 33 ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), 34 nhdr->n_descsz); 35 memset(build_id + nhdr->n_descsz, 0, 36 BUILD_ID_SIZE_MAX - nhdr->n_descsz); 37 if (size) 38 *size = nhdr->n_descsz; 39 return 0; 40 } 41 new_offs = note_offs + sizeof(Elf32_Nhdr) + 42 ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4); 43 if (new_offs <= note_offs) /* overflow */ 44 break; 45 note_offs = new_offs; 46 } 47 48 return -EINVAL; 49} 50 51static inline int parse_build_id(const void *page_addr, 52 unsigned char *build_id, 53 __u32 *size, 54 const void *note_start, 55 Elf32_Word note_size) 56{ 57 /* check for overflow */ 58 if (note_start < page_addr || note_start + note_size < note_start) 59 return -EINVAL; 60 61 /* only supports note that fits in the first page */ 62 if (note_start + note_size > page_addr + PAGE_SIZE) 63 return -EINVAL; 64 65 return parse_build_id_buf(build_id, size, note_start, note_size); 66} 67 68/* Parse build ID from 32-bit ELF */ 69static int get_build_id_32(const void *page_addr, unsigned char *build_id, 70 __u32 *size) 71{ 72 Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr; 73 Elf32_Phdr *phdr; 74 int i; 75 76 /* only supports phdr that fits in one page */ 77 if (ehdr->e_phnum > 78 (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr)) 79 return -EINVAL; 80 81 phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr)); 82 83 for (i = 0; i < ehdr->e_phnum; ++i) { 84 if (phdr[i].p_type == PT_NOTE && 85 !parse_build_id(page_addr, build_id, size, 86 page_addr + phdr[i].p_offset, 87 phdr[i].p_filesz)) 88 return 0; 89 } 90 return -EINVAL; 91} 92 93/* Parse build ID from 64-bit ELF */ 94static int get_build_id_64(const void *page_addr, unsigned char *build_id, 95 __u32 *size) 96{ 97 Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr; 98 Elf64_Phdr *phdr; 99 int i; 100 101 /* only supports phdr that fits in one page */ 102 if (ehdr->e_phnum > 103 (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr)) 104 return -EINVAL; 105 106 phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr)); 107 108 for (i = 0; i < ehdr->e_phnum; ++i) { 109 if (phdr[i].p_type == PT_NOTE && 110 !parse_build_id(page_addr, build_id, size, 111 page_addr + phdr[i].p_offset, 112 phdr[i].p_filesz)) 113 return 0; 114 } 115 return -EINVAL; 116} 117 118/* 119 * Parse build ID of ELF file mapped to vma 120 * @vma: vma object 121 * @build_id: buffer to store build id, at least BUILD_ID_SIZE long 122 * @size: returns actual build id size in case of success 123 * 124 * Return: 0 on success, -EINVAL otherwise 125 */ 126int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id, 127 __u32 *size) 128{ 129 Elf32_Ehdr *ehdr; 130 struct page *page; 131 void *page_addr; 132 int ret; 133 134 /* only works for page backed storage */ 135 if (!vma->vm_file) 136 return -EINVAL; 137 138 page = find_get_page(vma->vm_file->f_mapping, 0); 139 if (!page) 140 return -EFAULT; /* page not mapped */ 141 142 ret = -EINVAL; 143 page_addr = kmap_atomic(page); 144 ehdr = (Elf32_Ehdr *)page_addr; 145 146 /* compare magic x7f "ELF" */ 147 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) 148 goto out; 149 150 /* only support executable file and shared object file */ 151 if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) 152 goto out; 153 154 if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) 155 ret = get_build_id_32(page_addr, build_id, size); 156 else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) 157 ret = get_build_id_64(page_addr, build_id, size); 158out: 159 kunmap_atomic(page_addr); 160 put_page(page); 161 return ret; 162} 163 164/** 165 * build_id_parse_buf - Get build ID from a buffer 166 * @buf: Elf note section(s) to parse 167 * @buf_size: Size of @buf in bytes 168 * @build_id: Build ID parsed from @buf, at least BUILD_ID_SIZE_MAX long 169 * 170 * Return: 0 on success, -EINVAL otherwise 171 */ 172int build_id_parse_buf(const void *buf, unsigned char *build_id, u32 buf_size) 173{ 174 return parse_build_id_buf(build_id, NULL, buf, buf_size); 175} 176 177#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) || IS_ENABLED(CONFIG_CRASH_CORE) 178unsigned char vmlinux_build_id[BUILD_ID_SIZE_MAX] __ro_after_init; 179 180/** 181 * init_vmlinux_build_id - Compute and stash the running kernel's build ID 182 */ 183void __init init_vmlinux_build_id(void) 184{ 185 extern const void __start_notes __weak; 186 extern const void __stop_notes __weak; 187 unsigned int size = &__stop_notes - &__start_notes; 188 189 build_id_parse_buf(&__start_notes, vmlinux_build_id, size); 190} 191#endif