diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2012-10-04 20:15:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-05 14:05:17 -0400 |
commit | 2aa362c49c314a98fb9aebbd7760a461667bac05 (patch) | |
tree | 5edfc83b26950bae8543b75343aca37fed6b3f71 /fs/binfmt_elf.c | |
parent | 49ae4d4b113be03dc4a2ec5f2a1f573ff0fcddb3 (diff) |
coredump: extend core dump note section to contain file names of mapped files
This note has the following format:
long count -- how many files are mapped
long page_size -- units for file_ofs
array of [COUNT] elements of
long start
long end
long file_ofs
followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Amerigo Wang <amwang@redhat.com>
Cc: "Jonathan M. Foote" <jmfoote@cert.org>
Cc: Roland McGrath <roland@hack.frob.com>
Cc: Pedro Alves <palves@redhat.com>
Cc: Fengguang Wu <fengguang.wu@intel.com>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/binfmt_elf.c')
-rw-r--r-- | fs/binfmt_elf.c | 110 |
1 files changed, 106 insertions, 4 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 865f9be6a2d3..28a64e769527 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/compiler.h> | 27 | #include <linux/compiler.h> |
28 | #include <linux/highmem.h> | 28 | #include <linux/highmem.h> |
29 | #include <linux/pagemap.h> | 29 | #include <linux/pagemap.h> |
30 | #include <linux/vmalloc.h> | ||
30 | #include <linux/security.h> | 31 | #include <linux/security.h> |
31 | #include <linux/random.h> | 32 | #include <linux/random.h> |
32 | #include <linux/elf.h> | 33 | #include <linux/elf.h> |
@@ -37,6 +38,9 @@ | |||
37 | #include <asm/page.h> | 38 | #include <asm/page.h> |
38 | #include <asm/exec.h> | 39 | #include <asm/exec.h> |
39 | 40 | ||
41 | #ifndef user_long_t | ||
42 | #define user_long_t long | ||
43 | #endif | ||
40 | #ifndef user_siginfo_t | 44 | #ifndef user_siginfo_t |
41 | #define user_siginfo_t siginfo_t | 45 | #define user_siginfo_t siginfo_t |
42 | #endif | 46 | #endif |
@@ -1386,6 +1390,93 @@ static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata, | |||
1386 | fill_note(note, "CORE", NT_SIGINFO, sizeof(*csigdata), csigdata); | 1390 | fill_note(note, "CORE", NT_SIGINFO, sizeof(*csigdata), csigdata); |
1387 | } | 1391 | } |
1388 | 1392 | ||
1393 | #define MAX_FILE_NOTE_SIZE (4*1024*1024) | ||
1394 | /* | ||
1395 | * Format of NT_FILE note: | ||
1396 | * | ||
1397 | * long count -- how many files are mapped | ||
1398 | * long page_size -- units for file_ofs | ||
1399 | * array of [COUNT] elements of | ||
1400 | * long start | ||
1401 | * long end | ||
1402 | * long file_ofs | ||
1403 | * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... | ||
1404 | */ | ||
1405 | static void fill_files_note(struct memelfnote *note) | ||
1406 | { | ||
1407 | struct vm_area_struct *vma; | ||
1408 | unsigned count, size, names_ofs, remaining, n; | ||
1409 | user_long_t *data; | ||
1410 | user_long_t *start_end_ofs; | ||
1411 | char *name_base, *name_curpos; | ||
1412 | |||
1413 | /* *Estimated* file count and total data size needed */ | ||
1414 | count = current->mm->map_count; | ||
1415 | size = count * 64; | ||
1416 | |||
1417 | names_ofs = (2 + 3 * count) * sizeof(data[0]); | ||
1418 | alloc: | ||
1419 | if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */ | ||
1420 | goto err; | ||
1421 | size = round_up(size, PAGE_SIZE); | ||
1422 | data = vmalloc(size); | ||
1423 | if (!data) | ||
1424 | goto err; | ||
1425 | |||
1426 | start_end_ofs = data + 2; | ||
1427 | name_base = name_curpos = ((char *)data) + names_ofs; | ||
1428 | remaining = size - names_ofs; | ||
1429 | count = 0; | ||
1430 | for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) { | ||
1431 | struct file *file; | ||
1432 | const char *filename; | ||
1433 | |||
1434 | file = vma->vm_file; | ||
1435 | if (!file) | ||
1436 | continue; | ||
1437 | filename = d_path(&file->f_path, name_curpos, remaining); | ||
1438 | if (IS_ERR(filename)) { | ||
1439 | if (PTR_ERR(filename) == -ENAMETOOLONG) { | ||
1440 | vfree(data); | ||
1441 | size = size * 5 / 4; | ||
1442 | goto alloc; | ||
1443 | } | ||
1444 | continue; | ||
1445 | } | ||
1446 | |||
1447 | /* d_path() fills at the end, move name down */ | ||
1448 | /* n = strlen(filename) + 1: */ | ||
1449 | n = (name_curpos + remaining) - filename; | ||
1450 | remaining = filename - name_curpos; | ||
1451 | memmove(name_curpos, filename, n); | ||
1452 | name_curpos += n; | ||
1453 | |||
1454 | *start_end_ofs++ = vma->vm_start; | ||
1455 | *start_end_ofs++ = vma->vm_end; | ||
1456 | *start_end_ofs++ = vma->vm_pgoff; | ||
1457 | count++; | ||
1458 | } | ||
1459 | |||
1460 | /* Now we know exact count of files, can store it */ | ||
1461 | data[0] = count; | ||
1462 | data[1] = PAGE_SIZE; | ||
1463 | /* | ||
1464 | * Count usually is less than current->mm->map_count, | ||
1465 | * we need to move filenames down. | ||
1466 | */ | ||
1467 | n = current->mm->map_count - count; | ||
1468 | if (n != 0) { | ||
1469 | unsigned shift_bytes = n * 3 * sizeof(data[0]); | ||
1470 | memmove(name_base - shift_bytes, name_base, | ||
1471 | name_curpos - name_base); | ||
1472 | name_curpos -= shift_bytes; | ||
1473 | } | ||
1474 | |||
1475 | size = name_curpos - (char *)data; | ||
1476 | fill_note(note, "CORE", NT_FILE, size, data); | ||
1477 | err: ; | ||
1478 | } | ||
1479 | |||
1389 | #ifdef CORE_DUMP_USE_REGSET | 1480 | #ifdef CORE_DUMP_USE_REGSET |
1390 | #include <linux/regset.h> | 1481 | #include <linux/regset.h> |
1391 | 1482 | ||
@@ -1401,6 +1492,7 @@ struct elf_note_info { | |||
1401 | struct memelfnote psinfo; | 1492 | struct memelfnote psinfo; |
1402 | struct memelfnote signote; | 1493 | struct memelfnote signote; |
1403 | struct memelfnote auxv; | 1494 | struct memelfnote auxv; |
1495 | struct memelfnote files; | ||
1404 | user_siginfo_t csigdata; | 1496 | user_siginfo_t csigdata; |
1405 | size_t size; | 1497 | size_t size; |
1406 | int thread_notes; | 1498 | int thread_notes; |
@@ -1581,6 +1673,9 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, | |||
1581 | fill_auxv_note(&info->auxv, current->mm); | 1673 | fill_auxv_note(&info->auxv, current->mm); |
1582 | info->size += notesize(&info->auxv); | 1674 | info->size += notesize(&info->auxv); |
1583 | 1675 | ||
1676 | fill_files_note(&info->files); | ||
1677 | info->size += notesize(&info->files); | ||
1678 | |||
1584 | return 1; | 1679 | return 1; |
1585 | } | 1680 | } |
1586 | 1681 | ||
@@ -1611,6 +1706,8 @@ static int write_note_info(struct elf_note_info *info, | |||
1611 | return 0; | 1706 | return 0; |
1612 | if (first && !writenote(&info->auxv, file, foffset)) | 1707 | if (first && !writenote(&info->auxv, file, foffset)) |
1613 | return 0; | 1708 | return 0; |
1709 | if (first && !writenote(&info->files, file, foffset)) | ||
1710 | return 0; | ||
1614 | 1711 | ||
1615 | for (i = 1; i < info->thread_notes; ++i) | 1712 | for (i = 1; i < info->thread_notes; ++i) |
1616 | if (t->notes[i].data && | 1713 | if (t->notes[i].data && |
@@ -1637,6 +1734,7 @@ static void free_note_info(struct elf_note_info *info) | |||
1637 | kfree(t); | 1734 | kfree(t); |
1638 | } | 1735 | } |
1639 | kfree(info->psinfo.data); | 1736 | kfree(info->psinfo.data); |
1737 | vfree(info->files.data); | ||
1640 | } | 1738 | } |
1641 | 1739 | ||
1642 | #else | 1740 | #else |
@@ -1713,7 +1811,7 @@ static int elf_note_info_init(struct elf_note_info *info) | |||
1713 | INIT_LIST_HEAD(&info->thread_list); | 1811 | INIT_LIST_HEAD(&info->thread_list); |
1714 | 1812 | ||
1715 | /* Allocate space for ELF notes */ | 1813 | /* Allocate space for ELF notes */ |
1716 | info->notes = kmalloc(7 * sizeof(struct memelfnote), GFP_KERNEL); | 1814 | info->notes = kmalloc(8 * sizeof(struct memelfnote), GFP_KERNEL); |
1717 | if (!info->notes) | 1815 | if (!info->notes) |
1718 | return 0; | 1816 | return 0; |
1719 | info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL); | 1817 | info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL); |
@@ -1783,10 +1881,11 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, | |||
1783 | fill_note(info->notes + 1, "CORE", NT_PRPSINFO, | 1881 | fill_note(info->notes + 1, "CORE", NT_PRPSINFO, |
1784 | sizeof(*info->psinfo), info->psinfo); | 1882 | sizeof(*info->psinfo), info->psinfo); |
1785 | 1883 | ||
1786 | info->numnote = 2; | 1884 | fill_siginfo_note(info->notes + 2, &info->csigdata, siginfo); |
1885 | fill_auxv_note(info->notes + 3, current->mm); | ||
1886 | fill_files_note(info->notes + 4); | ||
1787 | 1887 | ||
1788 | fill_siginfo_note(&info->notes[info->numnote++], &info->csigdata, siginfo); | 1888 | info->numnote = 5; |
1789 | fill_auxv_note(&info->notes[info->numnote++], current->mm); | ||
1790 | 1889 | ||
1791 | /* Try to dump the FPU. */ | 1890 | /* Try to dump the FPU. */ |
1792 | info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, | 1891 | info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, |
@@ -1848,6 +1947,9 @@ static void free_note_info(struct elf_note_info *info) | |||
1848 | kfree(list_entry(tmp, struct elf_thread_status, list)); | 1947 | kfree(list_entry(tmp, struct elf_thread_status, list)); |
1849 | } | 1948 | } |
1850 | 1949 | ||
1950 | /* Free data allocated by fill_files_note(): */ | ||
1951 | vfree(info->notes[4].data); | ||
1952 | |||
1851 | kfree(info->prstatus); | 1953 | kfree(info->prstatus); |
1852 | kfree(info->psinfo); | 1954 | kfree(info->psinfo); |
1853 | kfree(info->notes); | 1955 | kfree(info->notes); |