diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-01-11 07:15:35 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-02-14 09:55:03 -0500 |
commit | 083e14c09b7ae0247b9944a386fdc32cd0719da1 (patch) | |
tree | 0ad777d2fbe57289c011130a51478a108237c55b /arch/s390/kernel/module.c | |
parent | 4d334fd155b53adfe78393e66850ff4bb0aa8406 (diff) |
s390/modules: add relocation overflow checking
Given enough debug options some modules can grow large enough
that the GOT table gets bigger than 4K. On s390 the modules
are compiled with -fpic which limits the GOT to 4K. The end
result is a module that is loaded but won't work.
Add a sanity check to apply_rela and return with an error if
a relocation error is detected for a module.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/module.c')
-rw-r--r-- | arch/s390/kernel/module.c | 140 |
1 files changed, 89 insertions, 51 deletions
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index 4610deafd953..06f17311628a 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c | |||
@@ -65,8 +65,7 @@ void module_free(struct module *mod, void *module_region) | |||
65 | vfree(module_region); | 65 | vfree(module_region); |
66 | } | 66 | } |
67 | 67 | ||
68 | static void | 68 | static void check_rela(Elf_Rela *rela, struct module *me) |
69 | check_rela(Elf_Rela *rela, struct module *me) | ||
70 | { | 69 | { |
71 | struct mod_arch_syminfo *info; | 70 | struct mod_arch_syminfo *info; |
72 | 71 | ||
@@ -115,9 +114,8 @@ check_rela(Elf_Rela *rela, struct module *me) | |||
115 | * Account for GOT and PLT relocations. We can't add sections for | 114 | * Account for GOT and PLT relocations. We can't add sections for |
116 | * got and plt but we can increase the core module size. | 115 | * got and plt but we can increase the core module size. |
117 | */ | 116 | */ |
118 | int | 117 | int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, |
119 | module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, | 118 | char *secstrings, struct module *me) |
120 | char *secstrings, struct module *me) | ||
121 | { | 119 | { |
122 | Elf_Shdr *symtab; | 120 | Elf_Shdr *symtab; |
123 | Elf_Sym *symbols; | 121 | Elf_Sym *symbols; |
@@ -179,13 +177,52 @@ module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, | |||
179 | return 0; | 177 | return 0; |
180 | } | 178 | } |
181 | 179 | ||
182 | static int | 180 | static int apply_rela_bits(Elf_Addr loc, Elf_Addr val, |
183 | apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, | 181 | int sign, int bits, int shift) |
184 | struct module *me) | 182 | { |
183 | unsigned long umax; | ||
184 | long min, max; | ||
185 | |||
186 | if (val & ((1UL << shift) - 1)) | ||
187 | return -ENOEXEC; | ||
188 | if (sign) { | ||
189 | val = (Elf_Addr)(((long) val) >> shift); | ||
190 | min = -(1L << (bits - 1)); | ||
191 | max = (1L << (bits - 1)) - 1; | ||
192 | if ((long) val < min || (long) val > max) | ||
193 | return -ENOEXEC; | ||
194 | } else { | ||
195 | val >>= shift; | ||
196 | umax = ((1UL << (bits - 1)) << 1) - 1; | ||
197 | if ((unsigned long) val > umax) | ||
198 | return -ENOEXEC; | ||
199 | } | ||
200 | |||
201 | if (bits == 8) | ||
202 | *(unsigned char *) loc = val; | ||
203 | else if (bits == 12) | ||
204 | *(unsigned short *) loc = (val & 0xfff) | | ||
205 | (*(unsigned short *) loc & 0xf000); | ||
206 | else if (bits == 16) | ||
207 | *(unsigned short *) loc = val; | ||
208 | else if (bits == 20) | ||
209 | *(unsigned int *) loc = (val & 0xfff) << 16 | | ||
210 | (val & 0xff000) >> 4 | | ||
211 | (*(unsigned int *) loc & 0xf00000ff); | ||
212 | else if (bits == 32) | ||
213 | *(unsigned int *) loc = val; | ||
214 | else if (bits == 64) | ||
215 | *(unsigned long *) loc = val; | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, | ||
220 | const char *strtab, struct module *me) | ||
185 | { | 221 | { |
186 | struct mod_arch_syminfo *info; | 222 | struct mod_arch_syminfo *info; |
187 | Elf_Addr loc, val; | 223 | Elf_Addr loc, val; |
188 | int r_type, r_sym; | 224 | int r_type, r_sym; |
225 | int rc; | ||
189 | 226 | ||
190 | /* This is where to make the change */ | 227 | /* This is where to make the change */ |
191 | loc = base + rela->r_offset; | 228 | loc = base + rela->r_offset; |
@@ -205,20 +242,17 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, | |||
205 | case R_390_64: /* Direct 64 bit. */ | 242 | case R_390_64: /* Direct 64 bit. */ |
206 | val += rela->r_addend; | 243 | val += rela->r_addend; |
207 | if (r_type == R_390_8) | 244 | if (r_type == R_390_8) |
208 | *(unsigned char *) loc = val; | 245 | rc = apply_rela_bits(loc, val, 0, 8, 0); |
209 | else if (r_type == R_390_12) | 246 | else if (r_type == R_390_12) |
210 | *(unsigned short *) loc = (val & 0xfff) | | 247 | rc = apply_rela_bits(loc, val, 0, 12, 0); |
211 | (*(unsigned short *) loc & 0xf000); | ||
212 | else if (r_type == R_390_16) | 248 | else if (r_type == R_390_16) |
213 | *(unsigned short *) loc = val; | 249 | rc = apply_rela_bits(loc, val, 0, 16, 0); |
214 | else if (r_type == R_390_20) | 250 | else if (r_type == R_390_20) |
215 | *(unsigned int *) loc = | 251 | rc = apply_rela_bits(loc, val, 1, 20, 0); |
216 | (*(unsigned int *) loc & 0xf00000ff) | | ||
217 | (val & 0xfff) << 16 | (val & 0xff000) >> 4; | ||
218 | else if (r_type == R_390_32) | 252 | else if (r_type == R_390_32) |
219 | *(unsigned int *) loc = val; | 253 | rc = apply_rela_bits(loc, val, 0, 32, 0); |
220 | else if (r_type == R_390_64) | 254 | else if (r_type == R_390_64) |
221 | *(unsigned long *) loc = val; | 255 | rc = apply_rela_bits(loc, val, 0, 64, 0); |
222 | break; | 256 | break; |
223 | case R_390_PC16: /* PC relative 16 bit. */ | 257 | case R_390_PC16: /* PC relative 16 bit. */ |
224 | case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */ | 258 | case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */ |
@@ -227,15 +261,15 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, | |||
227 | case R_390_PC64: /* PC relative 64 bit. */ | 261 | case R_390_PC64: /* PC relative 64 bit. */ |
228 | val += rela->r_addend - loc; | 262 | val += rela->r_addend - loc; |
229 | if (r_type == R_390_PC16) | 263 | if (r_type == R_390_PC16) |
230 | *(unsigned short *) loc = val; | 264 | rc = apply_rela_bits(loc, val, 1, 16, 0); |
231 | else if (r_type == R_390_PC16DBL) | 265 | else if (r_type == R_390_PC16DBL) |
232 | *(unsigned short *) loc = val >> 1; | 266 | rc = apply_rela_bits(loc, val, 1, 16, 1); |
233 | else if (r_type == R_390_PC32DBL) | 267 | else if (r_type == R_390_PC32DBL) |
234 | *(unsigned int *) loc = val >> 1; | 268 | rc = apply_rela_bits(loc, val, 1, 32, 1); |
235 | else if (r_type == R_390_PC32) | 269 | else if (r_type == R_390_PC32) |
236 | *(unsigned int *) loc = val; | 270 | rc = apply_rela_bits(loc, val, 1, 32, 0); |
237 | else if (r_type == R_390_PC64) | 271 | else if (r_type == R_390_PC64) |
238 | *(unsigned long *) loc = val; | 272 | rc = apply_rela_bits(loc, val, 1, 64, 0); |
239 | break; | 273 | break; |
240 | case R_390_GOT12: /* 12 bit GOT offset. */ | 274 | case R_390_GOT12: /* 12 bit GOT offset. */ |
241 | case R_390_GOT16: /* 16 bit GOT offset. */ | 275 | case R_390_GOT16: /* 16 bit GOT offset. */ |
@@ -260,26 +294,24 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, | |||
260 | val = info->got_offset + rela->r_addend; | 294 | val = info->got_offset + rela->r_addend; |
261 | if (r_type == R_390_GOT12 || | 295 | if (r_type == R_390_GOT12 || |
262 | r_type == R_390_GOTPLT12) | 296 | r_type == R_390_GOTPLT12) |
263 | *(unsigned short *) loc = (val & 0xfff) | | 297 | rc = apply_rela_bits(loc, val, 0, 12, 0); |
264 | (*(unsigned short *) loc & 0xf000); | ||
265 | else if (r_type == R_390_GOT16 || | 298 | else if (r_type == R_390_GOT16 || |
266 | r_type == R_390_GOTPLT16) | 299 | r_type == R_390_GOTPLT16) |
267 | *(unsigned short *) loc = val; | 300 | rc = apply_rela_bits(loc, val, 0, 16, 0); |
268 | else if (r_type == R_390_GOT20 || | 301 | else if (r_type == R_390_GOT20 || |
269 | r_type == R_390_GOTPLT20) | 302 | r_type == R_390_GOTPLT20) |
270 | *(unsigned int *) loc = | 303 | rc = apply_rela_bits(loc, val, 1, 20, 0); |
271 | (*(unsigned int *) loc & 0xf00000ff) | | ||
272 | (val & 0xfff) << 16 | (val & 0xff000) >> 4; | ||
273 | else if (r_type == R_390_GOT32 || | 304 | else if (r_type == R_390_GOT32 || |
274 | r_type == R_390_GOTPLT32) | 305 | r_type == R_390_GOTPLT32) |
275 | *(unsigned int *) loc = val; | 306 | rc = apply_rela_bits(loc, val, 0, 32, 0); |
276 | else if (r_type == R_390_GOTENT || | ||
277 | r_type == R_390_GOTPLTENT) | ||
278 | *(unsigned int *) loc = | ||
279 | (val + (Elf_Addr) me->module_core - loc) >> 1; | ||
280 | else if (r_type == R_390_GOT64 || | 307 | else if (r_type == R_390_GOT64 || |
281 | r_type == R_390_GOTPLT64) | 308 | r_type == R_390_GOTPLT64) |
282 | *(unsigned long *) loc = val; | 309 | rc = apply_rela_bits(loc, val, 0, 64, 0); |
310 | else if (r_type == R_390_GOTENT || | ||
311 | r_type == R_390_GOTPLTENT) { | ||
312 | val += (Elf_Addr) me->module_core - loc; | ||
313 | rc = apply_rela_bits(loc, val, 1, 32, 1); | ||
314 | } | ||
283 | break; | 315 | break; |
284 | case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */ | 316 | case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */ |
285 | case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */ | 317 | case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */ |
@@ -321,17 +353,17 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, | |||
321 | val += rela->r_addend - loc; | 353 | val += rela->r_addend - loc; |
322 | } | 354 | } |
323 | if (r_type == R_390_PLT16DBL) | 355 | if (r_type == R_390_PLT16DBL) |
324 | *(unsigned short *) loc = val >> 1; | 356 | rc = apply_rela_bits(loc, val, 1, 16, 1); |
325 | else if (r_type == R_390_PLTOFF16) | 357 | else if (r_type == R_390_PLTOFF16) |
326 | *(unsigned short *) loc = val; | 358 | rc = apply_rela_bits(loc, val, 0, 16, 0); |
327 | else if (r_type == R_390_PLT32DBL) | 359 | else if (r_type == R_390_PLT32DBL) |
328 | *(unsigned int *) loc = val >> 1; | 360 | rc = apply_rela_bits(loc, val, 1, 32, 1); |
329 | else if (r_type == R_390_PLT32 || | 361 | else if (r_type == R_390_PLT32 || |
330 | r_type == R_390_PLTOFF32) | 362 | r_type == R_390_PLTOFF32) |
331 | *(unsigned int *) loc = val; | 363 | rc = apply_rela_bits(loc, val, 0, 32, 0); |
332 | else if (r_type == R_390_PLT64 || | 364 | else if (r_type == R_390_PLT64 || |
333 | r_type == R_390_PLTOFF64) | 365 | r_type == R_390_PLTOFF64) |
334 | *(unsigned long *) loc = val; | 366 | rc = apply_rela_bits(loc, val, 0, 64, 0); |
335 | break; | 367 | break; |
336 | case R_390_GOTOFF16: /* 16 bit offset to GOT. */ | 368 | case R_390_GOTOFF16: /* 16 bit offset to GOT. */ |
337 | case R_390_GOTOFF32: /* 32 bit offset to GOT. */ | 369 | case R_390_GOTOFF32: /* 32 bit offset to GOT. */ |
@@ -339,20 +371,20 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, | |||
339 | val = val + rela->r_addend - | 371 | val = val + rela->r_addend - |
340 | ((Elf_Addr) me->module_core + me->arch.got_offset); | 372 | ((Elf_Addr) me->module_core + me->arch.got_offset); |
341 | if (r_type == R_390_GOTOFF16) | 373 | if (r_type == R_390_GOTOFF16) |
342 | *(unsigned short *) loc = val; | 374 | rc = apply_rela_bits(loc, val, 0, 16, 0); |
343 | else if (r_type == R_390_GOTOFF32) | 375 | else if (r_type == R_390_GOTOFF32) |
344 | *(unsigned int *) loc = val; | 376 | rc = apply_rela_bits(loc, val, 0, 32, 0); |
345 | else if (r_type == R_390_GOTOFF64) | 377 | else if (r_type == R_390_GOTOFF64) |
346 | *(unsigned long *) loc = val; | 378 | rc = apply_rela_bits(loc, val, 0, 64, 0); |
347 | break; | 379 | break; |
348 | case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */ | 380 | case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */ |
349 | case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */ | 381 | case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */ |
350 | val = (Elf_Addr) me->module_core + me->arch.got_offset + | 382 | val = (Elf_Addr) me->module_core + me->arch.got_offset + |
351 | rela->r_addend - loc; | 383 | rela->r_addend - loc; |
352 | if (r_type == R_390_GOTPC) | 384 | if (r_type == R_390_GOTPC) |
353 | *(unsigned int *) loc = val; | 385 | rc = apply_rela_bits(loc, val, 1, 32, 0); |
354 | else if (r_type == R_390_GOTPCDBL) | 386 | else if (r_type == R_390_GOTPCDBL) |
355 | *(unsigned int *) loc = val >> 1; | 387 | rc = apply_rela_bits(loc, val, 1, 32, 1); |
356 | break; | 388 | break; |
357 | case R_390_COPY: | 389 | case R_390_COPY: |
358 | case R_390_GLOB_DAT: /* Create GOT entry. */ | 390 | case R_390_GLOB_DAT: /* Create GOT entry. */ |
@@ -360,19 +392,25 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, | |||
360 | case R_390_RELATIVE: /* Adjust by program base. */ | 392 | case R_390_RELATIVE: /* Adjust by program base. */ |
361 | /* Only needed if we want to support loading of | 393 | /* Only needed if we want to support loading of |
362 | modules linked with -shared. */ | 394 | modules linked with -shared. */ |
363 | break; | 395 | return -ENOEXEC; |
364 | default: | 396 | default: |
365 | printk(KERN_ERR "module %s: Unknown relocation: %u\n", | 397 | printk(KERN_ERR "module %s: unknown relocation: %u\n", |
366 | me->name, r_type); | 398 | me->name, r_type); |
367 | return -ENOEXEC; | 399 | return -ENOEXEC; |
368 | } | 400 | } |
401 | if (rc) { | ||
402 | printk(KERN_ERR "module %s: relocation error for symbol %s " | ||
403 | "(r_type %i, value 0x%lx)\n", | ||
404 | me->name, strtab + symtab[r_sym].st_name, | ||
405 | r_type, (unsigned long) val); | ||
406 | return rc; | ||
407 | } | ||
369 | return 0; | 408 | return 0; |
370 | } | 409 | } |
371 | 410 | ||
372 | int | 411 | int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, |
373 | apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, | 412 | unsigned int symindex, unsigned int relsec, |
374 | unsigned int symindex, unsigned int relsec, | 413 | struct module *me) |
375 | struct module *me) | ||
376 | { | 414 | { |
377 | Elf_Addr base; | 415 | Elf_Addr base; |
378 | Elf_Sym *symtab; | 416 | Elf_Sym *symtab; |
@@ -388,7 +426,7 @@ apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, | |||
388 | n = sechdrs[relsec].sh_size / sizeof(Elf_Rela); | 426 | n = sechdrs[relsec].sh_size / sizeof(Elf_Rela); |
389 | 427 | ||
390 | for (i = 0; i < n; i++, rela++) { | 428 | for (i = 0; i < n; i++, rela++) { |
391 | rc = apply_rela(rela, base, symtab, me); | 429 | rc = apply_rela(rela, base, symtab, strtab, me); |
392 | if (rc) | 430 | if (rc) |
393 | return rc; | 431 | return rc; |
394 | } | 432 | } |