1 // SPDX-License-Identifier: GPL-2.0 1 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 #include <linux/secretmem.h> 9 10 #define BUILD_ID 3 11 12 #define MAX_PHDR_CNT 256 13 14 struct freader { 15 void *buf; 16 u32 buf_sz; 17 int err; 18 union { 19 struct { 20 struct file *file; 21 struct folio *folio; 22 void *addr; 23 loff_t folio_off; 24 bool may_fault; 25 }; 26 struct { 27 const char *data; 28 u64 data_sz; 29 }; 30 }; 31 }; 32 33 static void freader_init_from_file(struct frea 34 struct file 35 { 36 memset(r, 0, sizeof(*r)); 37 r->buf = buf; 38 r->buf_sz = buf_sz; 39 r->file = file; 40 r->may_fault = may_fault; 41 } 42 43 static void freader_init_from_mem(struct fread 44 { 45 memset(r, 0, sizeof(*r)); 46 r->data = data; 47 r->data_sz = data_sz; 48 } 49 50 static void freader_put_folio(struct freader * 51 { 52 if (!r->folio) 53 return; 54 kunmap_local(r->addr); 55 folio_put(r->folio); 56 r->folio = NULL; 57 } 58 59 static int freader_get_folio(struct freader *r 60 { 61 /* check if we can just reuse current 62 if (r->folio && file_off >= r->folio_o 63 file_off < r->folio_off + folio_si 64 return 0; 65 66 freader_put_folio(r); 67 68 /* reject secretmem folios created wit 69 if (secretmem_mapping(r->file->f_mappi 70 return -EFAULT; 71 72 r->folio = filemap_get_folio(r->file-> 73 74 /* if sleeping is allowed, wait for th 75 if (r->may_fault && (IS_ERR(r->folio) 76 filemap_invalidate_lock_shared 77 r->folio = read_cache_folio(r- 78 NU 79 filemap_invalidate_unlock_shar 80 } 81 82 if (IS_ERR(r->folio) || !folio_test_up 83 if (!IS_ERR(r->folio)) 84 folio_put(r->folio); 85 r->folio = NULL; 86 return -EFAULT; 87 } 88 89 r->folio_off = folio_pos(r->folio); 90 r->addr = kmap_local_folio(r->folio, 0 91 92 return 0; 93 } 94 95 static const void *freader_fetch(struct freade 96 { 97 size_t folio_sz; 98 99 /* provided internal temporary buffer 100 if (WARN_ON(r->buf && sz > r->buf_sz)) 101 r->err = -E2BIG; 102 return NULL; 103 } 104 105 if (unlikely(file_off + sz < file_off) 106 r->err = -EOVERFLOW; 107 return NULL; 108 } 109 110 /* working with memory buffer is much 111 if (!r->buf) { 112 if (file_off + sz > r->data_sz 113 r->err = -ERANGE; 114 return NULL; 115 } 116 return r->data + file_off; 117 } 118 119 /* fetch or reuse folio for given file 120 r->err = freader_get_folio(r, file_off 121 if (r->err) 122 return NULL; 123 124 /* if requested data is crossing folio 125 * everything into our local buffer to 126 * access interface 127 */ 128 folio_sz = folio_size(r->folio); 129 if (file_off + sz > r->folio_off + fol 130 int part_sz = r->folio_off + f 131 132 /* copy the part that resides 133 memcpy(r->buf, r->addr + (file 134 135 /* fetch next folio */ 136 r->err = freader_get_folio(r, 137 if (r->err) 138 return NULL; 139 140 /* copy the rest of requested 141 memcpy(r->buf + part_sz, r->ad 142 143 return r->buf; 144 } 145 146 /* if data fits in a single folio, jus 147 return r->addr + (file_off - r->folio_ 148 } 149 150 static void freader_cleanup(struct freader *r) 151 { 152 if (!r->buf) 153 return; /* non-file-backed mod 154 155 freader_put_folio(r); 156 } 157 158 /* 159 * Parse build id from the note segment. This 160 * 32-bit and 64-bit system, because Elf32_Nhd 161 * identical. 162 */ 163 static int parse_build_id(struct freader *r, u 164 loff_t note_off, Elf 165 { 166 const char note_name[] = "GNU"; 167 const size_t note_name_sz = sizeof(not 168 u32 build_id_off, new_off, note_end, n 169 const Elf32_Nhdr *nhdr; 170 const char *data; 171 172 if (check_add_overflow(note_off, note_ 173 return -EINVAL; 174 175 while (note_end - note_off > sizeof(El 176 nhdr = freader_fetch(r, note_o 177 if (!nhdr) 178 return r->err; 179 180 name_sz = READ_ONCE(nhdr->n_na 181 desc_sz = READ_ONCE(nhdr->n_de 182 183 new_off = note_off + sizeof(El 184 if (check_add_overflow(new_off 185 check_add_overflow(new_off 186 new_off > note_end) 187 break; 188 189 if (nhdr->n_type == BUILD_ID & 190 name_sz == note_name_sz && 191 memcmp(nhdr + 1, note_name 192 desc_sz > 0 && desc_sz <= 193 build_id_off = note_of 194 195 /* freader_fetch() wil 196 data = freader_fetch(r 197 if (!data) 198 return r->err; 199 200 memcpy(build_id, data, 201 memset(build_id + desc 202 if (size) 203 *size = desc_s 204 return 0; 205 } 206 207 note_off = new_off; 208 } 209 210 return -EINVAL; 211 } 212 213 /* Parse build ID from 32-bit ELF */ 214 static int get_build_id_32(struct freader *r, 215 { 216 const Elf32_Ehdr *ehdr; 217 const Elf32_Phdr *phdr; 218 __u32 phnum, phoff, i; 219 220 ehdr = freader_fetch(r, 0, sizeof(Elf3 221 if (!ehdr) 222 return r->err; 223 224 /* subsequent freader_fetch() calls in 225 phnum = READ_ONCE(ehdr->e_phnum); 226 phoff = READ_ONCE(ehdr->e_phoff); 227 228 /* set upper bound on amount of segmen 229 if (phnum > MAX_PHDR_CNT) 230 phnum = MAX_PHDR_CNT; 231 232 /* check that phoff is not large enoug 233 if (phoff + phnum * sizeof(Elf32_Phdr) 234 return -EINVAL; 235 236 for (i = 0; i < phnum; ++i) { 237 phdr = freader_fetch(r, phoff 238 if (!phdr) 239 return r->err; 240 241 if (phdr->p_type == PT_NOTE && 242 !parse_build_id(r, build_i 243 READ_ONCE( 244 return 0; 245 } 246 return -EINVAL; 247 } 248 249 /* Parse build ID from 64-bit ELF */ 250 static int get_build_id_64(struct freader *r, 251 { 252 const Elf64_Ehdr *ehdr; 253 const Elf64_Phdr *phdr; 254 __u32 phnum, i; 255 __u64 phoff; 256 257 ehdr = freader_fetch(r, 0, sizeof(Elf6 258 if (!ehdr) 259 return r->err; 260 261 /* subsequent freader_fetch() calls in 262 phnum = READ_ONCE(ehdr->e_phnum); 263 phoff = READ_ONCE(ehdr->e_phoff); 264 265 /* set upper bound on amount of segmen 266 if (phnum > MAX_PHDR_CNT) 267 phnum = MAX_PHDR_CNT; 268 269 /* check that phoff is not large enoug 270 if (phoff + phnum * sizeof(Elf64_Phdr) 271 return -EINVAL; 272 273 for (i = 0; i < phnum; ++i) { 274 phdr = freader_fetch(r, phoff 275 if (!phdr) 276 return r->err; 277 278 if (phdr->p_type == PT_NOTE && 279 !parse_build_id(r, build_i 280 READ_ONCE( 281 return 0; 282 } 283 284 return -EINVAL; 285 } 286 287 /* enough for Elf64_Ehdr, Elf64_Phdr, and all 288 #define MAX_FREADER_BUF_SZ 64 289 290 static int __build_id_parse(struct vm_area_str 291 __u32 *size, bool 292 { 293 const Elf32_Ehdr *ehdr; 294 struct freader r; 295 char buf[MAX_FREADER_BUF_SZ]; 296 int ret; 297 298 /* only works for page backed storage 299 if (!vma->vm_file) 300 return -EINVAL; 301 302 freader_init_from_file(&r, buf, sizeof 303 304 /* fetch first 18 bytes of ELF header 305 ehdr = freader_fetch(&r, 0, offsetofen 306 if (!ehdr) { 307 ret = r.err; 308 goto out; 309 } 310 311 ret = -EINVAL; 312 313 /* compare magic x7f "ELF" */ 314 if (memcmp(ehdr->e_ident, ELFMAG, SELF 315 goto out; 316 317 /* only support executable file and sh 318 if (ehdr->e_type != ET_EXEC && ehdr->e 319 goto out; 320 321 if (ehdr->e_ident[EI_CLASS] == ELFCLAS 322 ret = get_build_id_32(&r, buil 323 else if (ehdr->e_ident[EI_CLASS] == EL 324 ret = get_build_id_64(&r, buil 325 out: 326 freader_cleanup(&r); 327 return ret; 328 } 329 330 /* 331 * Parse build ID of ELF file mapped to vma 332 * @vma: vma object 333 * @build_id: buffer to store build id, at lea 334 * @size: returns actual build id size in 335 * 336 * Assumes no page fault can be taken, so if r 337 * not already paged in, fetching of build ID 338 * 339 * Return: 0 on success; negative error, other 340 */ 341 int build_id_parse_nofault(struct vm_area_stru 342 { 343 return __build_id_parse(vma, build_id, 344 } 345 346 /* 347 * Parse build ID of ELF file mapped to VMA 348 * @vma: vma object 349 * @build_id: buffer to store build id, at lea 350 * @size: returns actual build id size in 351 * 352 * Assumes faultable context and can cause pag 353 * into page cache. 354 * 355 * Return: 0 on success; negative error, other 356 */ 357 int build_id_parse(struct vm_area_struct *vma, 358 { 359 return __build_id_parse(vma, build_id, 360 } 361 362 /** 363 * build_id_parse_buf - Get build ID from a bu 364 * @buf: ELF note section(s) to parse 365 * @buf_size: Size of @buf in bytes 366 * @build_id: Build ID parsed from @buf, at le 367 * 368 * Return: 0 on success, -EINVAL otherwise 369 */ 370 int build_id_parse_buf(const void *buf, unsign 371 { 372 struct freader r; 373 int err; 374 375 freader_init_from_mem(&r, buf, buf_siz 376 377 err = parse_build_id(&r, build_id, NUL 378 379 freader_cleanup(&r); 380 return err; 381 } 382 383 #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) || 384 unsigned char vmlinux_build_id[BUILD_ID_SIZE_M 385 386 /** 387 * init_vmlinux_build_id - Compute and stash t 388 */ 389 void __init init_vmlinux_build_id(void) 390 { 391 extern const void __start_notes; 392 extern const void __stop_notes; 393 unsigned int size = &__stop_notes - &_ 394 395 build_id_parse_buf(&__start_notes, vml 396 } 397 #endif 398
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.