Commit 083e14c09b7ae0247b9944a386fdc32cd0719da1

Authored by Martin Schwidefsky
1 parent 4d334fd155

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>

Showing 1 changed file with 89 additions and 51 deletions Side-by-side Diff

arch/s390/kernel/module.c
... ... @@ -65,8 +65,7 @@
65 65 vfree(module_region);
66 66 }
67 67  
68   -static void
69   -check_rela(Elf_Rela *rela, struct module *me)
  68 +static void check_rela(Elf_Rela *rela, struct module *me)
70 69 {
71 70 struct mod_arch_syminfo *info;
72 71  
... ... @@ -115,9 +114,8 @@
115 114 * Account for GOT and PLT relocations. We can't add sections for
116 115 * got and plt but we can increase the core module size.
117 116 */
118   -int
119   -module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
120   - char *secstrings, struct module *me)
  117 +int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
  118 + char *secstrings, struct module *me)
121 119 {
122 120 Elf_Shdr *symtab;
123 121 Elf_Sym *symbols;
124 122  
125 123  
... ... @@ -179,13 +177,52 @@
179 177 return 0;
180 178 }
181 179  
182   -static int
183   -apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
184   - struct module *me)
  180 +static int apply_rela_bits(Elf_Addr loc, Elf_Addr val,
  181 + int sign, int bits, int shift)
185 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)
  221 +{
186 222 struct mod_arch_syminfo *info;
187 223 Elf_Addr loc, val;
188 224 int r_type, r_sym;
  225 + int rc;
189 226  
190 227 /* This is where to make the change */
191 228 loc = base + rela->r_offset;
192 229  
193 230  
194 231  
195 232  
196 233  
... ... @@ -205,20 +242,17 @@
205 242 case R_390_64: /* Direct 64 bit. */
206 243 val += rela->r_addend;
207 244 if (r_type == R_390_8)
208   - *(unsigned char *) loc = val;
  245 + rc = apply_rela_bits(loc, val, 0, 8, 0);
209 246 else if (r_type == R_390_12)
210   - *(unsigned short *) loc = (val & 0xfff) |
211   - (*(unsigned short *) loc & 0xf000);
  247 + rc = apply_rela_bits(loc, val, 0, 12, 0);
212 248 else if (r_type == R_390_16)
213   - *(unsigned short *) loc = val;
  249 + rc = apply_rela_bits(loc, val, 0, 16, 0);
214 250 else if (r_type == R_390_20)
215   - *(unsigned int *) loc =
216   - (*(unsigned int *) loc & 0xf00000ff) |
217   - (val & 0xfff) << 16 | (val & 0xff000) >> 4;
  251 + rc = apply_rela_bits(loc, val, 1, 20, 0);
218 252 else if (r_type == R_390_32)
219   - *(unsigned int *) loc = val;
  253 + rc = apply_rela_bits(loc, val, 0, 32, 0);
220 254 else if (r_type == R_390_64)
221   - *(unsigned long *) loc = val;
  255 + rc = apply_rela_bits(loc, val, 0, 64, 0);
222 256 break;
223 257 case R_390_PC16: /* PC relative 16 bit. */
224 258 case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */
225 259  
226 260  
227 261  
228 262  
... ... @@ -227,15 +261,15 @@
227 261 case R_390_PC64: /* PC relative 64 bit. */
228 262 val += rela->r_addend - loc;
229 263 if (r_type == R_390_PC16)
230   - *(unsigned short *) loc = val;
  264 + rc = apply_rela_bits(loc, val, 1, 16, 0);
231 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 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 269 else if (r_type == R_390_PC32)
236   - *(unsigned int *) loc = val;
  270 + rc = apply_rela_bits(loc, val, 1, 32, 0);
237 271 else if (r_type == R_390_PC64)
238   - *(unsigned long *) loc = val;
  272 + rc = apply_rela_bits(loc, val, 1, 64, 0);
239 273 break;
240 274 case R_390_GOT12: /* 12 bit GOT offset. */
241 275 case R_390_GOT16: /* 16 bit GOT offset. */
242 276  
243 277  
244 278  
245 279  
... ... @@ -260,26 +294,24 @@
260 294 val = info->got_offset + rela->r_addend;
261 295 if (r_type == R_390_GOT12 ||
262 296 r_type == R_390_GOTPLT12)
263   - *(unsigned short *) loc = (val & 0xfff) |
264   - (*(unsigned short *) loc & 0xf000);
  297 + rc = apply_rela_bits(loc, val, 0, 12, 0);
265 298 else if (r_type == R_390_GOT16 ||
266 299 r_type == R_390_GOTPLT16)
267   - *(unsigned short *) loc = val;
  300 + rc = apply_rela_bits(loc, val, 0, 16, 0);
268 301 else if (r_type == R_390_GOT20 ||
269 302 r_type == R_390_GOTPLT20)
270   - *(unsigned int *) loc =
271   - (*(unsigned int *) loc & 0xf00000ff) |
272   - (val & 0xfff) << 16 | (val & 0xff000) >> 4;
  303 + rc = apply_rela_bits(loc, val, 1, 20, 0);
273 304 else if (r_type == R_390_GOT32 ||
274 305 r_type == R_390_GOTPLT32)
275   - *(unsigned int *) loc = val;
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;
  306 + rc = apply_rela_bits(loc, val, 0, 32, 0);
280 307 else if (r_type == R_390_GOT64 ||
281 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 315 break;
284 316 case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */
285 317 case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */
286 318  
287 319  
288 320  
289 321  
... ... @@ -321,17 +353,17 @@
321 353 val += rela->r_addend - loc;
322 354 }
323 355 if (r_type == R_390_PLT16DBL)
324   - *(unsigned short *) loc = val >> 1;
  356 + rc = apply_rela_bits(loc, val, 1, 16, 1);
325 357 else if (r_type == R_390_PLTOFF16)
326   - *(unsigned short *) loc = val;
  358 + rc = apply_rela_bits(loc, val, 0, 16, 0);
327 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 361 else if (r_type == R_390_PLT32 ||
330 362 r_type == R_390_PLTOFF32)
331   - *(unsigned int *) loc = val;
  363 + rc = apply_rela_bits(loc, val, 0, 32, 0);
332 364 else if (r_type == R_390_PLT64 ||
333 365 r_type == R_390_PLTOFF64)
334   - *(unsigned long *) loc = val;
  366 + rc = apply_rela_bits(loc, val, 0, 64, 0);
335 367 break;
336 368 case R_390_GOTOFF16: /* 16 bit offset to GOT. */
337 369 case R_390_GOTOFF32: /* 32 bit offset to GOT. */
338 370  
339 371  
340 372  
341 373  
... ... @@ -339,20 +371,20 @@
339 371 val = val + rela->r_addend -
340 372 ((Elf_Addr) me->module_core + me->arch.got_offset);
341 373 if (r_type == R_390_GOTOFF16)
342   - *(unsigned short *) loc = val;
  374 + rc = apply_rela_bits(loc, val, 0, 16, 0);
343 375 else if (r_type == R_390_GOTOFF32)
344   - *(unsigned int *) loc = val;
  376 + rc = apply_rela_bits(loc, val, 0, 32, 0);
345 377 else if (r_type == R_390_GOTOFF64)
346   - *(unsigned long *) loc = val;
  378 + rc = apply_rela_bits(loc, val, 0, 64, 0);
347 379 break;
348 380 case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */
349 381 case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */
350 382 val = (Elf_Addr) me->module_core + me->arch.got_offset +
351 383 rela->r_addend - loc;
352 384 if (r_type == R_390_GOTPC)
353   - *(unsigned int *) loc = val;
  385 + rc = apply_rela_bits(loc, val, 1, 32, 0);
354 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 388 break;
357 389 case R_390_COPY:
358 390 case R_390_GLOB_DAT: /* Create GOT entry. */
359 391  
360 392  
361 393  
... ... @@ -360,19 +392,25 @@
360 392 case R_390_RELATIVE: /* Adjust by program base. */
361 393 /* Only needed if we want to support loading of
362 394 modules linked with -shared. */
363   - break;
  395 + return -ENOEXEC;
364 396 default:
365   - printk(KERN_ERR "module %s: Unknown relocation: %u\n",
  397 + printk(KERN_ERR "module %s: unknown relocation: %u\n",
366 398 me->name, r_type);
367 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 408 return 0;
370 409 }
371 410  
372   -int
373   -apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
374   - unsigned int symindex, unsigned int relsec,
375   - struct module *me)
  411 +int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
  412 + unsigned int symindex, unsigned int relsec,
  413 + struct module *me)
376 414 {
377 415 Elf_Addr base;
378 416 Elf_Sym *symtab;
... ... @@ -388,7 +426,7 @@
388 426 n = sechdrs[relsec].sh_size / sizeof(Elf_Rela);
389 427  
390 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 430 if (rc)
393 431 return rc;
394 432 }