]>
Commit | Line | Data |
---|---|---|
c430f7d2 GKH |
1 | From 72023656961b8c81a168a7a6762d589339d0d7ec Mon Sep 17 00:00:00 2001 |
2 | From: Dan Aloni <alonid@stratoscale.com> | |
3 | Date: Mon, 30 Sep 2013 13:45:02 -0700 | |
4 | Subject: fs/binfmt_elf.c: prevent a coredump with a large vm_map_count from Oopsing | |
5 | ||
6 | From: Dan Aloni <alonid@stratoscale.com> | |
7 | ||
8 | commit 72023656961b8c81a168a7a6762d589339d0d7ec upstream. | |
9 | ||
10 | A high setting of max_map_count, and a process core-dumping with a large | |
11 | enough vm_map_count could result in an NT_FILE note not being written, | |
12 | and the kernel crashing immediately later because it has assumed | |
13 | otherwise. | |
14 | ||
15 | Reproduction of the oops-causing bug described here: | |
16 | ||
17 | https://lkml.org/lkml/2013/8/30/50 | |
18 | ||
19 | Rge ussue originated in commit 2aa362c49c31 ("coredump: extend core dump | |
20 | note section to contain file names of mapped file") from Oct 4, 2012. | |
21 | ||
22 | This patch make that section optional in that case. fill_files_note() | |
23 | should signify the error, and also let the info struct in | |
24 | elf_core_dump() be zero-initialized so that we can check for the | |
25 | optionally written note. | |
26 | ||
27 | [akpm@linux-foundation.org: avoid abusing E2BIG, remove a couple of not-really-needed local variables] | |
28 | [akpm@linux-foundation.org: fix sparse warning] | |
29 | Signed-off-by: Dan Aloni <alonid@stratoscale.com> | |
30 | Cc: Al Viro <viro@zeniv.linux.org.uk> | |
31 | Cc: Denys Vlasenko <vda.linux@googlemail.com> | |
32 | Reported-by: Martin MOKREJS <mmokrejs@gmail.com> | |
33 | Tested-by: Martin MOKREJS <mmokrejs@gmail.com> | |
34 | Signed-off-by: Andrew Morton <akpm@linux-foundation.org> | |
35 | Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> | |
36 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
37 | ||
38 | --- | |
39 | fs/binfmt_elf.c | 30 ++++++++++++++++++------------ | |
40 | 1 file changed, 18 insertions(+), 12 deletions(-) | |
41 | ||
42 | --- a/fs/binfmt_elf.c | |
43 | +++ b/fs/binfmt_elf.c | |
44 | @@ -1415,7 +1415,7 @@ static void fill_siginfo_note(struct mem | |
45 | * long file_ofs | |
46 | * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... | |
47 | */ | |
48 | -static void fill_files_note(struct memelfnote *note) | |
49 | +static int fill_files_note(struct memelfnote *note) | |
50 | { | |
51 | struct vm_area_struct *vma; | |
52 | unsigned count, size, names_ofs, remaining, n; | |
53 | @@ -1430,11 +1430,11 @@ static void fill_files_note(struct memel | |
54 | names_ofs = (2 + 3 * count) * sizeof(data[0]); | |
55 | alloc: | |
56 | if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */ | |
57 | - goto err; | |
58 | + return -EINVAL; | |
59 | size = round_up(size, PAGE_SIZE); | |
60 | data = vmalloc(size); | |
61 | if (!data) | |
62 | - goto err; | |
63 | + return -ENOMEM; | |
64 | ||
65 | start_end_ofs = data + 2; | |
66 | name_base = name_curpos = ((char *)data) + names_ofs; | |
67 | @@ -1487,7 +1487,7 @@ static void fill_files_note(struct memel | |
68 | ||
69 | size = name_curpos - (char *)data; | |
70 | fill_note(note, "CORE", NT_FILE, size, data); | |
71 | - err: ; | |
72 | + return 0; | |
73 | } | |
74 | ||
75 | #ifdef CORE_DUMP_USE_REGSET | |
76 | @@ -1688,8 +1688,8 @@ static int fill_note_info(struct elfhdr | |
77 | fill_auxv_note(&info->auxv, current->mm); | |
78 | info->size += notesize(&info->auxv); | |
79 | ||
80 | - fill_files_note(&info->files); | |
81 | - info->size += notesize(&info->files); | |
82 | + if (fill_files_note(&info->files) == 0) | |
83 | + info->size += notesize(&info->files); | |
84 | ||
85 | return 1; | |
86 | } | |
87 | @@ -1721,7 +1721,8 @@ static int write_note_info(struct elf_no | |
88 | return 0; | |
89 | if (first && !writenote(&info->auxv, file, foffset)) | |
90 | return 0; | |
91 | - if (first && !writenote(&info->files, file, foffset)) | |
92 | + if (first && info->files.data && | |
93 | + !writenote(&info->files, file, foffset)) | |
94 | return 0; | |
95 | ||
96 | for (i = 1; i < info->thread_notes; ++i) | |
97 | @@ -1808,6 +1809,7 @@ static int elf_dump_thread_status(long s | |
98 | ||
99 | struct elf_note_info { | |
100 | struct memelfnote *notes; | |
101 | + struct memelfnote *notes_files; | |
102 | struct elf_prstatus *prstatus; /* NT_PRSTATUS */ | |
103 | struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */ | |
104 | struct list_head thread_list; | |
105 | @@ -1898,9 +1900,12 @@ static int fill_note_info(struct elfhdr | |
106 | ||
107 | fill_siginfo_note(info->notes + 2, &info->csigdata, siginfo); | |
108 | fill_auxv_note(info->notes + 3, current->mm); | |
109 | - fill_files_note(info->notes + 4); | |
110 | + info->numnote = 4; | |
111 | ||
112 | - info->numnote = 5; | |
113 | + if (fill_files_note(info->notes + info->numnote) == 0) { | |
114 | + info->notes_files = info->notes + info->numnote; | |
115 | + info->numnote++; | |
116 | + } | |
117 | ||
118 | /* Try to dump the FPU. */ | |
119 | info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, | |
120 | @@ -1962,8 +1967,9 @@ static void free_note_info(struct elf_no | |
121 | kfree(list_entry(tmp, struct elf_thread_status, list)); | |
122 | } | |
123 | ||
124 | - /* Free data allocated by fill_files_note(): */ | |
125 | - vfree(info->notes[4].data); | |
126 | + /* Free data possibly allocated by fill_files_note(): */ | |
127 | + if (info->notes_files) | |
128 | + vfree(info->notes_files->data); | |
129 | ||
130 | kfree(info->prstatus); | |
131 | kfree(info->psinfo); | |
132 | @@ -2046,7 +2052,7 @@ static int elf_core_dump(struct coredump | |
133 | struct vm_area_struct *vma, *gate_vma; | |
134 | struct elfhdr *elf = NULL; | |
135 | loff_t offset = 0, dataoff, foffset; | |
136 | - struct elf_note_info info; | |
137 | + struct elf_note_info info = { }; | |
138 | struct elf_phdr *phdr4note = NULL; | |
139 | struct elf_shdr *shdr4extnum = NULL; | |
140 | Elf_Half e_phnum; |