aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc/kcore.c
diff options
context:
space:
mode:
authorOmar Sandoval <osandov@fb.com>2018-08-22 00:55:09 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-08-22 13:52:46 -0400
commit37e949bd5293ddb70acf236eedf2ae8caa1db57b (patch)
treeff48dc580f9ea7c89fd1677286984772634a7577 /fs/proc/kcore.c
parent3673fb08db73347332ab956d90090c1da6e610b7 (diff)
proc/kcore: clean up ELF header generation
Currently, the ELF file header, program headers, and note segment are allocated all at once, in some icky code dating back to 2.3. Programs tend to read the file header, then the program headers, then the note segment, all separately, so this is a waste of effort. It's cleaner and more efficient to handle the three separately. Link: http://lkml.kernel.org/r/19c92cbad0e11f6103ff3274b2e7a7e51a1eb74b.1531953780.git.osandov@fb.com Signed-off-by: Omar Sandoval <osandov@fb.com> Cc: Alexey Dobriyan <adobriyan@gmail.com> Cc: Bhupesh Sharma <bhsharma@redhat.com> Cc: Eric Biederman <ebiederm@xmission.com> Cc: James Morse <james.morse@arm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc/kcore.c')
-rw-r--r--fs/proc/kcore.c350
1 files changed, 141 insertions, 209 deletions
diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c
index dc34642bbdb7..808ef9afd084 100644
--- a/fs/proc/kcore.c
+++ b/fs/proc/kcore.c
@@ -49,15 +49,6 @@ static struct proc_dir_entry *proc_root_kcore;
49#define kc_offset_to_vaddr(o) ((o) + PAGE_OFFSET) 49#define kc_offset_to_vaddr(o) ((o) + PAGE_OFFSET)
50#endif 50#endif
51 51
52/* An ELF note in memory */
53struct memelfnote
54{
55 const char *name;
56 int type;
57 unsigned int datasz;
58 void *data;
59};
60
61static LIST_HEAD(kclist_head); 52static LIST_HEAD(kclist_head);
62static DECLARE_RWSEM(kclist_lock); 53static DECLARE_RWSEM(kclist_lock);
63static int kcore_need_update = 1; 54static int kcore_need_update = 1;
@@ -73,7 +64,8 @@ void __init kclist_add(struct kcore_list *new, void *addr, size_t size,
73 list_add_tail(&new->list, &kclist_head); 64 list_add_tail(&new->list, &kclist_head);
74} 65}
75 66
76static size_t get_kcore_size(int *nphdr, size_t *elf_buflen) 67static size_t get_kcore_size(int *nphdr, size_t *phdrs_len, size_t *notes_len,
68 size_t *data_offset)
77{ 69{
78 size_t try, size; 70 size_t try, size;
79 struct kcore_list *m; 71 struct kcore_list *m;
@@ -87,15 +79,15 @@ static size_t get_kcore_size(int *nphdr, size_t *elf_buflen)
87 size = try; 79 size = try;
88 *nphdr = *nphdr + 1; 80 *nphdr = *nphdr + 1;
89 } 81 }
90 *elf_buflen = sizeof(struct elfhdr) + 82
91 (*nphdr + 2)*sizeof(struct elf_phdr) + 83 *phdrs_len = *nphdr * sizeof(struct elf_phdr);
92 3 * ((sizeof(struct elf_note)) + 84 *notes_len = (3 * (sizeof(struct elf_note) + ALIGN(sizeof(CORE_STR), 4)) +
93 roundup(sizeof(CORE_STR), 4)) + 85 ALIGN(sizeof(struct elf_prstatus), 4) +
94 roundup(sizeof(struct elf_prstatus), 4) + 86 ALIGN(sizeof(struct elf_prpsinfo), 4) +
95 roundup(sizeof(struct elf_prpsinfo), 4) + 87 ALIGN(arch_task_struct_size, 4));
96 roundup(arch_task_struct_size, 4); 88 *data_offset = PAGE_ALIGN(sizeof(struct elfhdr) + *phdrs_len +
97 *elf_buflen = PAGE_ALIGN(*elf_buflen); 89 *notes_len);
98 return size + *elf_buflen; 90 return *data_offset + size;
99} 91}
100 92
101#ifdef CONFIG_HIGHMEM 93#ifdef CONFIG_HIGHMEM
@@ -241,7 +233,7 @@ static int kcore_update_ram(void)
241 LIST_HEAD(list); 233 LIST_HEAD(list);
242 LIST_HEAD(garbage); 234 LIST_HEAD(garbage);
243 int nphdr; 235 int nphdr;
244 size_t size; 236 size_t phdrs_len, notes_len, data_offset;
245 struct kcore_list *tmp, *pos; 237 struct kcore_list *tmp, *pos;
246 int ret = 0; 238 int ret = 0;
247 239
@@ -263,7 +255,8 @@ static int kcore_update_ram(void)
263 } 255 }
264 list_splice_tail(&list, &kclist_head); 256 list_splice_tail(&list, &kclist_head);
265 257
266 proc_root_kcore->size = get_kcore_size(&nphdr, &size); 258 proc_root_kcore->size = get_kcore_size(&nphdr, &phdrs_len, &notes_len,
259 &data_offset);
267 260
268out: 261out:
269 up_write(&kclist_lock); 262 up_write(&kclist_lock);
@@ -274,228 +267,168 @@ out:
274 return ret; 267 return ret;
275} 268}
276 269
277/*****************************************************************************/ 270static void append_kcore_note(char *notes, size_t *i, const char *name,
278/* 271 unsigned int type, const void *desc,
279 * determine size of ELF note 272 size_t descsz)
280 */
281static int notesize(struct memelfnote *en)
282{ 273{
283 int sz; 274 struct elf_note *note = (struct elf_note *)&notes[*i];
284 275
285 sz = sizeof(struct elf_note); 276 note->n_namesz = strlen(name) + 1;
286 sz += roundup((strlen(en->name) + 1), 4); 277 note->n_descsz = descsz;
287 sz += roundup(en->datasz, 4); 278 note->n_type = type;
288 279 *i += sizeof(*note);
289 return sz; 280 memcpy(&notes[*i], name, note->n_namesz);
290} /* end notesize() */ 281 *i = ALIGN(*i + note->n_namesz, 4);
291 282 memcpy(&notes[*i], desc, descsz);
292/*****************************************************************************/ 283 *i = ALIGN(*i + descsz, 4);
293/* 284}
294 * store a note in the header buffer
295 */
296static char *storenote(struct memelfnote *men, char *bufp)
297{
298 struct elf_note en;
299
300#define DUMP_WRITE(addr,nr) do { memcpy(bufp,addr,nr); bufp += nr; } while(0)
301
302 en.n_namesz = strlen(men->name) + 1;
303 en.n_descsz = men->datasz;
304 en.n_type = men->type;
305
306 DUMP_WRITE(&en, sizeof(en));
307 DUMP_WRITE(men->name, en.n_namesz);
308
309 /* XXX - cast from long long to long to avoid need for libgcc.a */
310 bufp = (char*) roundup((unsigned long)bufp,4);
311 DUMP_WRITE(men->data, men->datasz);
312 bufp = (char*) roundup((unsigned long)bufp,4);
313
314#undef DUMP_WRITE
315
316 return bufp;
317} /* end storenote() */
318
319/*
320 * store an ELF coredump header in the supplied buffer
321 * nphdr is the number of elf_phdr to insert
322 */
323static void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff)
324{
325 struct elf_prstatus prstatus; /* NT_PRSTATUS */
326 struct elf_prpsinfo prpsinfo; /* NT_PRPSINFO */
327 struct elf_phdr *nhdr, *phdr;
328 struct elfhdr *elf;
329 struct memelfnote notes[3];
330 off_t offset = 0;
331 struct kcore_list *m;
332
333 /* setup ELF header */
334 elf = (struct elfhdr *) bufp;
335 bufp += sizeof(struct elfhdr);
336 offset += sizeof(struct elfhdr);
337 memcpy(elf->e_ident, ELFMAG, SELFMAG);
338 elf->e_ident[EI_CLASS] = ELF_CLASS;
339 elf->e_ident[EI_DATA] = ELF_DATA;
340 elf->e_ident[EI_VERSION]= EV_CURRENT;
341 elf->e_ident[EI_OSABI] = ELF_OSABI;
342 memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
343 elf->e_type = ET_CORE;
344 elf->e_machine = ELF_ARCH;
345 elf->e_version = EV_CURRENT;
346 elf->e_entry = 0;
347 elf->e_phoff = sizeof(struct elfhdr);
348 elf->e_shoff = 0;
349 elf->e_flags = ELF_CORE_EFLAGS;
350 elf->e_ehsize = sizeof(struct elfhdr);
351 elf->e_phentsize= sizeof(struct elf_phdr);
352 elf->e_phnum = nphdr;
353 elf->e_shentsize= 0;
354 elf->e_shnum = 0;
355 elf->e_shstrndx = 0;
356
357 /* setup ELF PT_NOTE program header */
358 nhdr = (struct elf_phdr *) bufp;
359 bufp += sizeof(struct elf_phdr);
360 offset += sizeof(struct elf_phdr);
361 nhdr->p_type = PT_NOTE;
362 nhdr->p_offset = 0;
363 nhdr->p_vaddr = 0;
364 nhdr->p_paddr = 0;
365 nhdr->p_filesz = 0;
366 nhdr->p_memsz = 0;
367 nhdr->p_flags = 0;
368 nhdr->p_align = 0;
369
370 /* setup ELF PT_LOAD program header for every area */
371 list_for_each_entry(m, &kclist_head, list) {
372 phdr = (struct elf_phdr *) bufp;
373 bufp += sizeof(struct elf_phdr);
374 offset += sizeof(struct elf_phdr);
375
376 phdr->p_type = PT_LOAD;
377 phdr->p_flags = PF_R|PF_W|PF_X;
378 phdr->p_offset = kc_vaddr_to_offset(m->addr) + dataoff;
379 phdr->p_vaddr = (size_t)m->addr;
380 if (m->type == KCORE_RAM)
381 phdr->p_paddr = __pa(m->addr);
382 else if (m->type == KCORE_TEXT)
383 phdr->p_paddr = __pa_symbol(m->addr);
384 else
385 phdr->p_paddr = (elf_addr_t)-1;
386 phdr->p_filesz = phdr->p_memsz = m->size;
387 phdr->p_align = PAGE_SIZE;
388 }
389
390 /*
391 * Set up the notes in similar form to SVR4 core dumps made
392 * with info from their /proc.
393 */
394 nhdr->p_offset = offset;
395
396 /* set up the process status */
397 notes[0].name = CORE_STR;
398 notes[0].type = NT_PRSTATUS;
399 notes[0].datasz = sizeof(struct elf_prstatus);
400 notes[0].data = &prstatus;
401
402 memset(&prstatus, 0, sizeof(struct elf_prstatus));
403
404 nhdr->p_filesz = notesize(&notes[0]);
405 bufp = storenote(&notes[0], bufp);
406
407 /* set up the process info */
408 notes[1].name = CORE_STR;
409 notes[1].type = NT_PRPSINFO;
410 notes[1].datasz = sizeof(struct elf_prpsinfo);
411 notes[1].data = &prpsinfo;
412
413 memset(&prpsinfo, 0, sizeof(struct elf_prpsinfo));
414 prpsinfo.pr_state = 0;
415 prpsinfo.pr_sname = 'R';
416 prpsinfo.pr_zomb = 0;
417
418 strcpy(prpsinfo.pr_fname, "vmlinux");
419 strlcpy(prpsinfo.pr_psargs, saved_command_line, sizeof(prpsinfo.pr_psargs));
420
421 nhdr->p_filesz += notesize(&notes[1]);
422 bufp = storenote(&notes[1], bufp);
423
424 /* set up the task structure */
425 notes[2].name = CORE_STR;
426 notes[2].type = NT_TASKSTRUCT;
427 notes[2].datasz = arch_task_struct_size;
428 notes[2].data = current;
429
430 nhdr->p_filesz += notesize(&notes[2]);
431 bufp = storenote(&notes[2], bufp);
432
433} /* end elf_kcore_store_hdr() */
434 285
435/*****************************************************************************/
436/*
437 * read from the ELF header and then kernel memory
438 */
439static ssize_t 286static ssize_t
440read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos) 287read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
441{ 288{
442 char *buf = file->private_data; 289 char *buf = file->private_data;
443 size_t size, tsz; 290 size_t phdrs_offset, notes_offset, data_offset;
444 size_t elf_buflen; 291 size_t phdrs_len, notes_len;
292 struct kcore_list *m;
293 size_t tsz;
445 int nphdr; 294 int nphdr;
446 unsigned long start; 295 unsigned long start;
447 size_t orig_buflen = buflen; 296 size_t orig_buflen = buflen;
448 int ret = 0; 297 int ret = 0;
449 298
450 down_read(&kclist_lock); 299 down_read(&kclist_lock);
451 size = get_kcore_size(&nphdr, &elf_buflen);
452 300
453 if (buflen == 0 || *fpos >= size) 301 get_kcore_size(&nphdr, &phdrs_len, &notes_len, &data_offset);
454 goto out; 302 phdrs_offset = sizeof(struct elfhdr);
303 notes_offset = phdrs_offset + phdrs_len;
304
305 /* ELF file header. */
306 if (buflen && *fpos < sizeof(struct elfhdr)) {
307 struct elfhdr ehdr = {
308 .e_ident = {
309 [EI_MAG0] = ELFMAG0,
310 [EI_MAG1] = ELFMAG1,
311 [EI_MAG2] = ELFMAG2,
312 [EI_MAG3] = ELFMAG3,
313 [EI_CLASS] = ELF_CLASS,
314 [EI_DATA] = ELF_DATA,
315 [EI_VERSION] = EV_CURRENT,
316 [EI_OSABI] = ELF_OSABI,
317 },
318 .e_type = ET_CORE,
319 .e_machine = ELF_ARCH,
320 .e_version = EV_CURRENT,
321 .e_phoff = sizeof(struct elfhdr),
322 .e_flags = ELF_CORE_EFLAGS,
323 .e_ehsize = sizeof(struct elfhdr),
324 .e_phentsize = sizeof(struct elf_phdr),
325 .e_phnum = nphdr,
326 };
327
328 tsz = min_t(size_t, buflen, sizeof(struct elfhdr) - *fpos);
329 if (copy_to_user(buffer, (char *)&ehdr + *fpos, tsz)) {
330 ret = -EFAULT;
331 goto out;
332 }
455 333
456 /* trim buflen to not go beyond EOF */ 334 buffer += tsz;
457 if (buflen > size - *fpos) 335 buflen -= tsz;
458 buflen = size - *fpos; 336 *fpos += tsz;
337 }
459 338
460 /* construct an ELF core header if we'll need some of it */ 339 /* ELF program headers. */
461 if (*fpos < elf_buflen) { 340 if (buflen && *fpos < phdrs_offset + phdrs_len) {
462 char * elf_buf; 341 struct elf_phdr *phdrs, *phdr;
463 342
464 tsz = elf_buflen - *fpos; 343 phdrs = kzalloc(phdrs_len, GFP_KERNEL);
465 if (buflen < tsz) 344 if (!phdrs) {
466 tsz = buflen;
467 elf_buf = kzalloc(elf_buflen, GFP_KERNEL);
468 if (!elf_buf) {
469 ret = -ENOMEM; 345 ret = -ENOMEM;
470 goto out; 346 goto out;
471 } 347 }
472 elf_kcore_store_hdr(elf_buf, nphdr, elf_buflen); 348
473 if (copy_to_user(buffer, elf_buf + *fpos, tsz)) { 349 phdrs[0].p_type = PT_NOTE;
474 kfree(elf_buf); 350 phdrs[0].p_offset = notes_offset;
351 phdrs[0].p_filesz = notes_len;
352
353 phdr = &phdrs[1];
354 list_for_each_entry(m, &kclist_head, list) {
355 phdr->p_type = PT_LOAD;
356 phdr->p_flags = PF_R | PF_W | PF_X;
357 phdr->p_offset = kc_vaddr_to_offset(m->addr) + data_offset;
358 phdr->p_vaddr = (size_t)m->addr;
359 if (m->type == KCORE_RAM)
360 phdr->p_paddr = __pa(m->addr);
361 else if (m->type == KCORE_TEXT)
362 phdr->p_paddr = __pa_symbol(m->addr);
363 else
364 phdr->p_paddr = (elf_addr_t)-1;
365 phdr->p_filesz = phdr->p_memsz = m->size;
366 phdr->p_align = PAGE_SIZE;
367 phdr++;
368 }
369
370 tsz = min_t(size_t, buflen, phdrs_offset + phdrs_len - *fpos);
371 if (copy_to_user(buffer, (char *)phdrs + *fpos - phdrs_offset,
372 tsz)) {
373 kfree(phdrs);
475 ret = -EFAULT; 374 ret = -EFAULT;
476 goto out; 375 goto out;
477 } 376 }
478 kfree(elf_buf); 377 kfree(phdrs);
378
379 buffer += tsz;
479 buflen -= tsz; 380 buflen -= tsz;
480 *fpos += tsz; 381 *fpos += tsz;
481 buffer += tsz; 382 }
383
384 /* ELF note segment. */
385 if (buflen && *fpos < notes_offset + notes_len) {
386 struct elf_prstatus prstatus = {};
387 struct elf_prpsinfo prpsinfo = {
388 .pr_sname = 'R',
389 .pr_fname = "vmlinux",
390 };
391 char *notes;
392 size_t i = 0;
393
394 strlcpy(prpsinfo.pr_psargs, saved_command_line,
395 sizeof(prpsinfo.pr_psargs));
396
397 notes = kzalloc(notes_len, GFP_KERNEL);
398 if (!notes) {
399 ret = -ENOMEM;
400 goto out;
401 }
402
403 append_kcore_note(notes, &i, CORE_STR, NT_PRSTATUS, &prstatus,
404 sizeof(prstatus));
405 append_kcore_note(notes, &i, CORE_STR, NT_PRPSINFO, &prpsinfo,
406 sizeof(prpsinfo));
407 append_kcore_note(notes, &i, CORE_STR, NT_TASKSTRUCT, current,
408 arch_task_struct_size);
482 409
483 /* leave now if filled buffer already */ 410 tsz = min_t(size_t, buflen, notes_offset + notes_len - *fpos);
484 if (buflen == 0) 411 if (copy_to_user(buffer, notes + *fpos - notes_offset, tsz)) {
412 kfree(notes);
413 ret = -EFAULT;
485 goto out; 414 goto out;
415 }
416 kfree(notes);
417
418 buffer += tsz;
419 buflen -= tsz;
420 *fpos += tsz;
486 } 421 }
487 422
488 /* 423 /*
489 * Check to see if our file offset matches with any of 424 * Check to see if our file offset matches with any of
490 * the addresses in the elf_phdr on our list. 425 * the addresses in the elf_phdr on our list.
491 */ 426 */
492 start = kc_offset_to_vaddr(*fpos - elf_buflen); 427 start = kc_offset_to_vaddr(*fpos - data_offset);
493 if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen) 428 if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
494 tsz = buflen; 429 tsz = buflen;
495
496 while (buflen) {
497 struct kcore_list *m;
498 430
431 while (buflen) {
499 list_for_each_entry(m, &kclist_head, list) { 432 list_for_each_entry(m, &kclist_head, list) {
500 if (start >= m->addr && start < (m->addr+m->size)) 433 if (start >= m->addr && start < (m->addr+m->size))
501 break; 434 break;
@@ -557,7 +490,6 @@ out:
557 return orig_buflen - buflen; 490 return orig_buflen - buflen;
558} 491}
559 492
560
561static int open_kcore(struct inode *inode, struct file *filp) 493static int open_kcore(struct inode *inode, struct file *filp)
562{ 494{
563 if (!capable(CAP_SYS_RAWIO)) 495 if (!capable(CAP_SYS_RAWIO))