diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2012-08-13 18:34:18 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2012-08-17 04:57:28 -0400 |
commit | c54de490a2e4e74164f747925ff05c00dfa153cd (patch) | |
tree | 8a9a2d3717725661a0430966db5bd674db4d4dfb | |
parent | 861667dc82f561e65336ea67f73021b782b4ff74 (diff) |
MIPS: Module: Deal with malformed HI16/LO16 relocation sequences.
In case a series of R_MIPS_HI16 relocations was not followed by an
R_MIPS_LO16 relocation we were leaking the hi16 relocation chain.
Handle that error and return an error.
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r-- | arch/mips/kernel/module.c | 35 |
1 files changed, 28 insertions, 7 deletions
diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c index 8e1fb802c3e2..4f8c3cba8c0c 100644 --- a/arch/mips/kernel/module.c +++ b/arch/mips/kernel/module.c | |||
@@ -140,19 +140,30 @@ static int apply_r_mips_hi16_rela(struct module *me, u32 *location, Elf_Addr v) | |||
140 | return 0; | 140 | return 0; |
141 | } | 141 | } |
142 | 142 | ||
143 | static void free_relocation_chain(struct mips_hi16 *l) | ||
144 | { | ||
145 | struct mips_hi16 *next; | ||
146 | |||
147 | while (l) { | ||
148 | next = l->next; | ||
149 | kfree(l); | ||
150 | l = next; | ||
151 | } | ||
152 | } | ||
153 | |||
143 | static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v) | 154 | static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v) |
144 | { | 155 | { |
145 | unsigned long insnlo = *location; | 156 | unsigned long insnlo = *location; |
157 | struct mips_hi16 *l; | ||
146 | Elf_Addr val, vallo; | 158 | Elf_Addr val, vallo; |
147 | struct mips_hi16 *l, *next; | ||
148 | 159 | ||
149 | /* Sign extend the addend we extract from the lo insn. */ | 160 | /* Sign extend the addend we extract from the lo insn. */ |
150 | vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; | 161 | vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; |
151 | 162 | ||
152 | if (me->arch.r_mips_hi16_list != NULL) { | 163 | if (me->arch.r_mips_hi16_list != NULL) { |
153 | |||
154 | l = me->arch.r_mips_hi16_list; | 164 | l = me->arch.r_mips_hi16_list; |
155 | while (l != NULL) { | 165 | while (l != NULL) { |
166 | struct mips_hi16 *next; | ||
156 | unsigned long insn; | 167 | unsigned long insn; |
157 | 168 | ||
158 | /* | 169 | /* |
@@ -198,11 +209,8 @@ static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v) | |||
198 | return 0; | 209 | return 0; |
199 | 210 | ||
200 | out_danger: | 211 | out_danger: |
201 | while (l) { | 212 | free_relocation_chain(l); |
202 | next = l->next; | 213 | me->arch.r_mips_hi16_list = NULL; |
203 | kfree(l); | ||
204 | l = next; | ||
205 | } | ||
206 | 214 | ||
207 | pr_err("module %s: dangerous R_MIPS_LO16 REL relocation\n", me->name); | 215 | pr_err("module %s: dangerous R_MIPS_LO16 REL relocation\n", me->name); |
208 | 216 | ||
@@ -300,6 +308,19 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, | |||
300 | return res; | 308 | return res; |
301 | } | 309 | } |
302 | 310 | ||
311 | /* | ||
312 | * Normally the hi16 list should be deallocated at this point. A | ||
313 | * malformed binary however could contain a series of R_MIPS_HI16 | ||
314 | * relocations not followed by a R_MIPS_LO16 relocation. In that | ||
315 | * case, free up the list and return an error. | ||
316 | */ | ||
317 | if (me->arch.r_mips_hi16_list) { | ||
318 | free_relocation_chain(me->arch.r_mips_hi16_list); | ||
319 | me->arch.r_mips_hi16_list = NULL; | ||
320 | |||
321 | return -ENOEXEC; | ||
322 | } | ||
323 | |||
303 | return 0; | 324 | return 0; |
304 | } | 325 | } |
305 | 326 | ||