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 */ 16 static int parse_build_id_buf(unsigned char *build_id, 17 __u32 *size, 18 const void *note_start, 19 Elf32_Word note_size) 20 { 21 const char note_name[] = "GNU"; 22 const size_t note_name_sz = sizeof(note_name); 23 u64 note_off = 0, new_off, name_sz, desc_sz; 24 const char *data; 25 26 while (note_off + sizeof(Elf32_Nhdr) < note_size && 27 note_off + sizeof(Elf32_Nhdr) > note_off /* overflow */) { 28 Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_off); 29 30 name_sz = READ_ONCE(nhdr->n_namesz); 31 desc_sz = READ_ONCE(nhdr->n_descsz); 32 33 new_off = note_off + sizeof(Elf32_Nhdr); 34 if (check_add_overflow(new_off, ALIGN(name_sz, 4), &new_off) || 35 check_add_overflow(new_off, ALIGN(desc_sz, 4), &new_off) || 36 new_off > note_size) 37 break; 38 39 if (nhdr->n_type == BUILD_ID && 40 name_sz == note_name_sz && 41 memcmp(nhdr + 1, note_name, note_name_sz) == 0 && 42 desc_sz > 0 && desc_sz <= BUILD_ID_SIZE_MAX) { 43 data = note_start + note_off + ALIGN(note_name_sz, 4); 44 memcpy(build_id, data, desc_sz); 45 memset(build_id + desc_sz, 0, BUILD_ID_SIZE_MAX - desc_sz); 46 if (size) 47 *size = desc_sz; 48 return 0; 49 } 50 51 note_off = new_off; 52 } 53 54 return -EINVAL; 55 } 56 57 static inline int parse_build_id(const void *page_addr, 58 unsigned char *build_id, 59 __u32 *size, 60 const void *note_start, 61 Elf32_Word note_size) 62 { 63 /* check for overflow */ 64 if (note_start < page_addr || note_start + note_size < note_start) 65 return -EINVAL; 66 67 /* only supports note that fits in the first page */ 68 if (note_start + note_size > page_addr + PAGE_SIZE) 69 return -EINVAL; 70 71 return parse_build_id_buf(build_id, size, note_start, note_size); 72 } 73 74 /* Parse build ID from 32-bit ELF */ 75 static int get_build_id_32(const void *page_addr, unsigned char *build_id, 76 __u32 *size) 77 { 78 Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr; 79 Elf32_Phdr *phdr; 80 __u32 i, phnum; 81 82 /* 83 * FIXME 84 * Neither ELF spec nor ELF loader require that program headers 85 * start immediately after ELF header. 86 */ 87 if (ehdr->e_phoff != sizeof(Elf32_Ehdr)) 88 return -EINVAL; 89 90 phnum = READ_ONCE(ehdr->e_phnum); 91 /* only supports phdr that fits in one page */ 92 if (phnum > (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr)) 93 return -EINVAL; 94 95 phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr)); 96 97 for (i = 0; i < phnum; ++i) { 98 if (phdr[i].p_type == PT_NOTE && 99 !parse_build_id(page_addr, build_id, size, 100 page_addr + READ_ONCE(phdr[i].p_offset), 101 READ_ONCE(phdr[i].p_filesz))) 102 return 0; 103 } 104 return -EINVAL; 105 } 106 107 /* Parse build ID from 64-bit ELF */ 108 static int get_build_id_64(const void *page_addr, unsigned char *build_id, 109 __u32 *size) 110 { 111 Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr; 112 Elf64_Phdr *phdr; 113 __u32 i, phnum; 114 115 /* 116 * FIXME 117 * Neither ELF spec nor ELF loader require that program headers 118 * start immediately after ELF header. 119 */ 120 if (ehdr->e_phoff != sizeof(Elf64_Ehdr)) 121 return -EINVAL; 122 123 phnum = READ_ONCE(ehdr->e_phnum); 124 /* only supports phdr that fits in one page */ 125 if (phnum > (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr)) 126 return -EINVAL; 127 128 phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr)); 129 130 for (i = 0; i < phnum; ++i) { 131 if (phdr[i].p_type == PT_NOTE && 132 !parse_build_id(page_addr, build_id, size, 133 page_addr + READ_ONCE(phdr[i].p_offset), 134 READ_ONCE(phdr[i].p_filesz))) 135 return 0; 136 } 137 return -EINVAL; 138 } 139 140 /* 141 * Parse build ID of ELF file mapped to vma 142 * @vma: vma object 143 * @build_id: buffer to store build id, at least BUILD_ID_SIZE long 144 * @size: returns actual build id size in case of success 145 * 146 * Return: 0 on success, -EINVAL otherwise 147 */ 148 int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id, 149 __u32 *size) 150 { 151 Elf32_Ehdr *ehdr; 152 struct page *page; 153 void *page_addr; 154 int ret; 155 156 /* only works for page backed storage */ 157 if (!vma->vm_file) 158 return -EINVAL; 159 160 page = find_get_page(vma->vm_file->f_mapping, 0); 161 if (!page) 162 return -EFAULT; /* page not mapped */ 163 if (!PageUptodate(page)) { 164 put_page(page); 165 return -EFAULT; 166 } 167 168 ret = -EINVAL; 169 page_addr = kmap_local_page(page); 170 ehdr = (Elf32_Ehdr *)page_addr; 171 172 /* compare magic x7f "ELF" */ 173 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) 174 goto out; 175 176 /* only support executable file and shared object file */ 177 if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) 178 goto out; 179 180 if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) 181 ret = get_build_id_32(page_addr, build_id, size); 182 else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) 183 ret = get_build_id_64(page_addr, build_id, size); 184 out: 185 kunmap_local(page_addr); 186 put_page(page); 187 return ret; 188 } 189 190 /** 191 * build_id_parse_buf - Get build ID from a buffer 192 * @buf: ELF note section(s) to parse 193 * @buf_size: Size of @buf in bytes 194 * @build_id: Build ID parsed from @buf, at least BUILD_ID_SIZE_MAX long 195 * 196 * Return: 0 on success, -EINVAL otherwise 197 */ 198 int build_id_parse_buf(const void *buf, unsigned char *build_id, u32 buf_size) 199 { 200 return parse_build_id_buf(build_id, NULL, buf, buf_size); 201 } 202 203 #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) || IS_ENABLED(CONFIG_VMCORE_INFO) 204 unsigned char vmlinux_build_id[BUILD_ID_SIZE_MAX] __ro_after_init; 205 206 /** 207 * init_vmlinux_build_id - Compute and stash the running kernel's build ID 208 */ 209 void __init init_vmlinux_build_id(void) 210 { 211 extern const void __start_notes; 212 extern const void __stop_notes; 213 unsigned int size = &__stop_notes - &__start_notes; 214 215 build_id_parse_buf(&__start_notes, vmlinux_build_id, size); 216 } 217 #endif 218
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.