diff options
author | Dan Aloni <alonid@stratoscale.com> | 2013-09-30 16:45:02 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-10-13 19:08:31 -0400 |
commit | cc748eed10e7e91c4fbc3a4503537f5748342b16 (patch) | |
tree | d60e391fe804cec4b1465f3d2c4d74d6704a3316 /fs/binfmt_elf.c | |
parent | fd728b3e6bd0153c78425052da773c95f3fcf332 (diff) |
fs/binfmt_elf.c: prevent a coredump with a large vm_map_count from Oopsing
commit 72023656961b8c81a168a7a6762d589339d0d7ec upstream.
A high setting of max_map_count, and a process core-dumping with a large
enough vm_map_count could result in an NT_FILE note not being written,
and the kernel crashing immediately later because it has assumed
otherwise.
Reproduction of the oops-causing bug described here:
https://lkml.org/lkml/2013/8/30/50
Rge ussue originated in commit 2aa362c49c31 ("coredump: extend core dump
note section to contain file names of mapped file") from Oct 4, 2012.
This patch make that section optional in that case. fill_files_note()
should signify the error, and also let the info struct in
elf_core_dump() be zero-initialized so that we can check for the
optionally written note.
[akpm@linux-foundation.org: avoid abusing E2BIG, remove a couple of not-really-needed local variables]
[akpm@linux-foundation.org: fix sparse warning]
Signed-off-by: Dan Aloni <alonid@stratoscale.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Reported-by: Martin MOKREJS <mmokrejs@gmail.com>
Tested-by: Martin MOKREJS <mmokrejs@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/binfmt_elf.c')
-rw-r--r-- | fs/binfmt_elf.c | 30 |
1 files changed, 18 insertions, 12 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index f8a0b0efda44..3aac8e9edac3 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c | |||
@@ -1415,7 +1415,7 @@ static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata, | |||
1415 | * long file_ofs | 1415 | * long file_ofs |
1416 | * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... | 1416 | * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... |
1417 | */ | 1417 | */ |
1418 | static void fill_files_note(struct memelfnote *note) | 1418 | static int fill_files_note(struct memelfnote *note) |
1419 | { | 1419 | { |
1420 | struct vm_area_struct *vma; | 1420 | struct vm_area_struct *vma; |
1421 | unsigned count, size, names_ofs, remaining, n; | 1421 | unsigned count, size, names_ofs, remaining, n; |
@@ -1430,11 +1430,11 @@ static void fill_files_note(struct memelfnote *note) | |||
1430 | names_ofs = (2 + 3 * count) * sizeof(data[0]); | 1430 | names_ofs = (2 + 3 * count) * sizeof(data[0]); |
1431 | alloc: | 1431 | alloc: |
1432 | if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */ | 1432 | if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */ |
1433 | goto err; | 1433 | return -EINVAL; |
1434 | size = round_up(size, PAGE_SIZE); | 1434 | size = round_up(size, PAGE_SIZE); |
1435 | data = vmalloc(size); | 1435 | data = vmalloc(size); |
1436 | if (!data) | 1436 | if (!data) |
1437 | goto err; | 1437 | return -ENOMEM; |
1438 | 1438 | ||
1439 | start_end_ofs = data + 2; | 1439 | start_end_ofs = data + 2; |
1440 | name_base = name_curpos = ((char *)data) + names_ofs; | 1440 | name_base = name_curpos = ((char *)data) + names_ofs; |
@@ -1487,7 +1487,7 @@ static void fill_files_note(struct memelfnote *note) | |||
1487 | 1487 | ||
1488 | size = name_curpos - (char *)data; | 1488 | size = name_curpos - (char *)data; |
1489 | fill_note(note, "CORE", NT_FILE, size, data); | 1489 | fill_note(note, "CORE", NT_FILE, size, data); |
1490 | err: ; | 1490 | return 0; |
1491 | } | 1491 | } |
1492 | 1492 | ||
1493 | #ifdef CORE_DUMP_USE_REGSET | 1493 | #ifdef CORE_DUMP_USE_REGSET |
@@ -1688,8 +1688,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, | |||
1688 | fill_auxv_note(&info->auxv, current->mm); | 1688 | fill_auxv_note(&info->auxv, current->mm); |
1689 | info->size += notesize(&info->auxv); | 1689 | info->size += notesize(&info->auxv); |
1690 | 1690 | ||
1691 | fill_files_note(&info->files); | 1691 | if (fill_files_note(&info->files) == 0) |
1692 | info->size += notesize(&info->files); | 1692 | info->size += notesize(&info->files); |
1693 | 1693 | ||
1694 | return 1; | 1694 | return 1; |
1695 | } | 1695 | } |
@@ -1721,7 +1721,8 @@ static int write_note_info(struct elf_note_info *info, | |||
1721 | return 0; | 1721 | return 0; |
1722 | if (first && !writenote(&info->auxv, file, foffset)) | 1722 | if (first && !writenote(&info->auxv, file, foffset)) |
1723 | return 0; | 1723 | return 0; |
1724 | if (first && !writenote(&info->files, file, foffset)) | 1724 | if (first && info->files.data && |
1725 | !writenote(&info->files, file, foffset)) | ||
1725 | return 0; | 1726 | return 0; |
1726 | 1727 | ||
1727 | for (i = 1; i < info->thread_notes; ++i) | 1728 | for (i = 1; i < info->thread_notes; ++i) |
@@ -1808,6 +1809,7 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) | |||
1808 | 1809 | ||
1809 | struct elf_note_info { | 1810 | struct elf_note_info { |
1810 | struct memelfnote *notes; | 1811 | struct memelfnote *notes; |
1812 | struct memelfnote *notes_files; | ||
1811 | struct elf_prstatus *prstatus; /* NT_PRSTATUS */ | 1813 | struct elf_prstatus *prstatus; /* NT_PRSTATUS */ |
1812 | struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */ | 1814 | struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */ |
1813 | struct list_head thread_list; | 1815 | struct list_head thread_list; |
@@ -1898,9 +1900,12 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, | |||
1898 | 1900 | ||
1899 | fill_siginfo_note(info->notes + 2, &info->csigdata, siginfo); | 1901 | fill_siginfo_note(info->notes + 2, &info->csigdata, siginfo); |
1900 | fill_auxv_note(info->notes + 3, current->mm); | 1902 | fill_auxv_note(info->notes + 3, current->mm); |
1901 | fill_files_note(info->notes + 4); | 1903 | info->numnote = 4; |
1902 | 1904 | ||
1903 | info->numnote = 5; | 1905 | if (fill_files_note(info->notes + info->numnote) == 0) { |
1906 | info->notes_files = info->notes + info->numnote; | ||
1907 | info->numnote++; | ||
1908 | } | ||
1904 | 1909 | ||
1905 | /* Try to dump the FPU. */ | 1910 | /* Try to dump the FPU. */ |
1906 | info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, | 1911 | info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, |
@@ -1962,8 +1967,9 @@ static void free_note_info(struct elf_note_info *info) | |||
1962 | kfree(list_entry(tmp, struct elf_thread_status, list)); | 1967 | kfree(list_entry(tmp, struct elf_thread_status, list)); |
1963 | } | 1968 | } |
1964 | 1969 | ||
1965 | /* Free data allocated by fill_files_note(): */ | 1970 | /* Free data possibly allocated by fill_files_note(): */ |
1966 | vfree(info->notes[4].data); | 1971 | if (info->notes_files) |
1972 | vfree(info->notes_files->data); | ||
1967 | 1973 | ||
1968 | kfree(info->prstatus); | 1974 | kfree(info->prstatus); |
1969 | kfree(info->psinfo); | 1975 | kfree(info->psinfo); |
@@ -2046,7 +2052,7 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
2046 | struct vm_area_struct *vma, *gate_vma; | 2052 | struct vm_area_struct *vma, *gate_vma; |
2047 | struct elfhdr *elf = NULL; | 2053 | struct elfhdr *elf = NULL; |
2048 | loff_t offset = 0, dataoff, foffset; | 2054 | loff_t offset = 0, dataoff, foffset; |
2049 | struct elf_note_info info; | 2055 | struct elf_note_info info = { }; |
2050 | struct elf_phdr *phdr4note = NULL; | 2056 | struct elf_phdr *phdr4note = NULL; |
2051 | struct elf_shdr *shdr4extnum = NULL; | 2057 | struct elf_shdr *shdr4extnum = NULL; |
2052 | Elf_Half e_phnum; | 2058 | Elf_Half e_phnum; |