Commit 083e14c0 authored by Martin Schwidefsky's avatar Martin Schwidefsky

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: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 4d334fd1
...@@ -65,8 +65,7 @@ void module_free(struct module *mod, void *module_region) ...@@ -65,8 +65,7 @@ void module_free(struct module *mod, void *module_region)
vfree(module_region); vfree(module_region);
} }
static void static void check_rela(Elf_Rela *rela, struct module *me)
check_rela(Elf_Rela *rela, struct module *me)
{ {
struct mod_arch_syminfo *info; struct mod_arch_syminfo *info;
...@@ -115,8 +114,7 @@ check_rela(Elf_Rela *rela, struct module *me) ...@@ -115,8 +114,7 @@ check_rela(Elf_Rela *rela, struct module *me)
* Account for GOT and PLT relocations. We can't add sections for * Account for GOT and PLT relocations. We can't add sections for
* got and plt but we can increase the core module size. * got and plt but we can increase the core module size.
*/ */
int int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
char *secstrings, struct module *me) char *secstrings, struct module *me)
{ {
Elf_Shdr *symtab; Elf_Shdr *symtab;
...@@ -179,13 +177,52 @@ module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, ...@@ -179,13 +177,52 @@ module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
return 0; return 0;
} }
static int static int apply_rela_bits(Elf_Addr loc, Elf_Addr val,
apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, int sign, int bits, int shift)
struct module *me) {
unsigned long umax;
long min, max;
if (val & ((1UL << shift) - 1))
return -ENOEXEC;
if (sign) {
val = (Elf_Addr)(((long) val) >> shift);
min = -(1L << (bits - 1));
max = (1L << (bits - 1)) - 1;
if ((long) val < min || (long) val > max)
return -ENOEXEC;
} else {
val >>= shift;
umax = ((1UL << (bits - 1)) << 1) - 1;
if ((unsigned long) val > umax)
return -ENOEXEC;
}
if (bits == 8)
*(unsigned char *) loc = val;
else if (bits == 12)
*(unsigned short *) loc = (val & 0xfff) |
(*(unsigned short *) loc & 0xf000);
else if (bits == 16)
*(unsigned short *) loc = val;
else if (bits == 20)
*(unsigned int *) loc = (val & 0xfff) << 16 |
(val & 0xff000) >> 4 |
(*(unsigned int *) loc & 0xf00000ff);
else if (bits == 32)
*(unsigned int *) loc = val;
else if (bits == 64)
*(unsigned long *) loc = val;
return 0;
}
static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
const char *strtab, struct module *me)
{ {
struct mod_arch_syminfo *info; struct mod_arch_syminfo *info;
Elf_Addr loc, val; Elf_Addr loc, val;
int r_type, r_sym; int r_type, r_sym;
int rc;
/* This is where to make the change */ /* This is where to make the change */
loc = base + rela->r_offset; loc = base + rela->r_offset;
...@@ -205,20 +242,17 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, ...@@ -205,20 +242,17 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_64: /* Direct 64 bit. */ case R_390_64: /* Direct 64 bit. */
val += rela->r_addend; val += rela->r_addend;
if (r_type == R_390_8) if (r_type == R_390_8)
*(unsigned char *) loc = val; rc = apply_rela_bits(loc, val, 0, 8, 0);
else if (r_type == R_390_12) else if (r_type == R_390_12)
*(unsigned short *) loc = (val & 0xfff) | rc = apply_rela_bits(loc, val, 0, 12, 0);
(*(unsigned short *) loc & 0xf000);
else if (r_type == R_390_16) else if (r_type == R_390_16)
*(unsigned short *) loc = val; rc = apply_rela_bits(loc, val, 0, 16, 0);
else if (r_type == R_390_20) else if (r_type == R_390_20)
*(unsigned int *) loc = rc = apply_rela_bits(loc, val, 1, 20, 0);
(*(unsigned int *) loc & 0xf00000ff) |
(val & 0xfff) << 16 | (val & 0xff000) >> 4;
else if (r_type == R_390_32) else if (r_type == R_390_32)
*(unsigned int *) loc = val; rc = apply_rela_bits(loc, val, 0, 32, 0);
else if (r_type == R_390_64) else if (r_type == R_390_64)
*(unsigned long *) loc = val; rc = apply_rela_bits(loc, val, 0, 64, 0);
break; break;
case R_390_PC16: /* PC relative 16 bit. */ case R_390_PC16: /* PC relative 16 bit. */
case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */ 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,15 +261,15 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_PC64: /* PC relative 64 bit. */ case R_390_PC64: /* PC relative 64 bit. */
val += rela->r_addend - loc; val += rela->r_addend - loc;
if (r_type == R_390_PC16) if (r_type == R_390_PC16)
*(unsigned short *) loc = val; rc = apply_rela_bits(loc, val, 1, 16, 0);
else if (r_type == R_390_PC16DBL) else if (r_type == R_390_PC16DBL)
*(unsigned short *) loc = val >> 1; rc = apply_rela_bits(loc, val, 1, 16, 1);
else if (r_type == R_390_PC32DBL) else if (r_type == R_390_PC32DBL)
*(unsigned int *) loc = val >> 1; rc = apply_rela_bits(loc, val, 1, 32, 1);
else if (r_type == R_390_PC32) else if (r_type == R_390_PC32)
*(unsigned int *) loc = val; rc = apply_rela_bits(loc, val, 1, 32, 0);
else if (r_type == R_390_PC64) else if (r_type == R_390_PC64)
*(unsigned long *) loc = val; rc = apply_rela_bits(loc, val, 1, 64, 0);
break; break;
case R_390_GOT12: /* 12 bit GOT offset. */ case R_390_GOT12: /* 12 bit GOT offset. */
case R_390_GOT16: /* 16 bit GOT offset. */ case R_390_GOT16: /* 16 bit GOT offset. */
...@@ -260,26 +294,24 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, ...@@ -260,26 +294,24 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val = info->got_offset + rela->r_addend; val = info->got_offset + rela->r_addend;
if (r_type == R_390_GOT12 || if (r_type == R_390_GOT12 ||
r_type == R_390_GOTPLT12) r_type == R_390_GOTPLT12)
*(unsigned short *) loc = (val & 0xfff) | rc = apply_rela_bits(loc, val, 0, 12, 0);
(*(unsigned short *) loc & 0xf000);
else if (r_type == R_390_GOT16 || else if (r_type == R_390_GOT16 ||
r_type == R_390_GOTPLT16) r_type == R_390_GOTPLT16)
*(unsigned short *) loc = val; rc = apply_rela_bits(loc, val, 0, 16, 0);
else if (r_type == R_390_GOT20 || else if (r_type == R_390_GOT20 ||
r_type == R_390_GOTPLT20) r_type == R_390_GOTPLT20)
*(unsigned int *) loc = rc = apply_rela_bits(loc, val, 1, 20, 0);
(*(unsigned int *) loc & 0xf00000ff) |
(val & 0xfff) << 16 | (val & 0xff000) >> 4;
else if (r_type == R_390_GOT32 || else if (r_type == R_390_GOT32 ||
r_type == R_390_GOTPLT32) r_type == R_390_GOTPLT32)
*(unsigned int *) loc = val; rc = apply_rela_bits(loc, val, 0, 32, 0);
else if (r_type == R_390_GOTENT ||
r_type == R_390_GOTPLTENT)
*(unsigned int *) loc =
(val + (Elf_Addr) me->module_core - loc) >> 1;
else if (r_type == R_390_GOT64 || else if (r_type == R_390_GOT64 ||
r_type == R_390_GOTPLT64) r_type == R_390_GOTPLT64)
*(unsigned long *) loc = val; rc = apply_rela_bits(loc, val, 0, 64, 0);
else if (r_type == R_390_GOTENT ||
r_type == R_390_GOTPLTENT) {
val += (Elf_Addr) me->module_core - loc;
rc = apply_rela_bits(loc, val, 1, 32, 1);
}
break; break;
case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */ case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */
case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */ 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,17 +353,17 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val += rela->r_addend - loc; val += rela->r_addend - loc;
} }
if (r_type == R_390_PLT16DBL) if (r_type == R_390_PLT16DBL)
*(unsigned short *) loc = val >> 1; rc = apply_rela_bits(loc, val, 1, 16, 1);
else if (r_type == R_390_PLTOFF16) else if (r_type == R_390_PLTOFF16)
*(unsigned short *) loc = val; rc = apply_rela_bits(loc, val, 0, 16, 0);
else if (r_type == R_390_PLT32DBL) else if (r_type == R_390_PLT32DBL)
*(unsigned int *) loc = val >> 1; rc = apply_rela_bits(loc, val, 1, 32, 1);
else if (r_type == R_390_PLT32 || else if (r_type == R_390_PLT32 ||
r_type == R_390_PLTOFF32) r_type == R_390_PLTOFF32)
*(unsigned int *) loc = val; rc = apply_rela_bits(loc, val, 0, 32, 0);
else if (r_type == R_390_PLT64 || else if (r_type == R_390_PLT64 ||
r_type == R_390_PLTOFF64) r_type == R_390_PLTOFF64)
*(unsigned long *) loc = val; rc = apply_rela_bits(loc, val, 0, 64, 0);
break; break;
case R_390_GOTOFF16: /* 16 bit offset to GOT. */ case R_390_GOTOFF16: /* 16 bit offset to GOT. */
case R_390_GOTOFF32: /* 32 bit offset to GOT. */ 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,20 +371,20 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val = val + rela->r_addend - val = val + rela->r_addend -
((Elf_Addr) me->module_core + me->arch.got_offset); ((Elf_Addr) me->module_core + me->arch.got_offset);
if (r_type == R_390_GOTOFF16) if (r_type == R_390_GOTOFF16)
*(unsigned short *) loc = val; rc = apply_rela_bits(loc, val, 0, 16, 0);
else if (r_type == R_390_GOTOFF32) else if (r_type == R_390_GOTOFF32)
*(unsigned int *) loc = val; rc = apply_rela_bits(loc, val, 0, 32, 0);
else if (r_type == R_390_GOTOFF64) else if (r_type == R_390_GOTOFF64)
*(unsigned long *) loc = val; rc = apply_rela_bits(loc, val, 0, 64, 0);
break; break;
case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */ case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */
case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */ case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */
val = (Elf_Addr) me->module_core + me->arch.got_offset + val = (Elf_Addr) me->module_core + me->arch.got_offset +
rela->r_addend - loc; rela->r_addend - loc;
if (r_type == R_390_GOTPC) if (r_type == R_390_GOTPC)
*(unsigned int *) loc = val; rc = apply_rela_bits(loc, val, 1, 32, 0);
else if (r_type == R_390_GOTPCDBL) else if (r_type == R_390_GOTPCDBL)
*(unsigned int *) loc = val >> 1; rc = apply_rela_bits(loc, val, 1, 32, 1);
break; break;
case R_390_COPY: case R_390_COPY:
case R_390_GLOB_DAT: /* Create GOT entry. */ case R_390_GLOB_DAT: /* Create GOT entry. */
...@@ -360,17 +392,23 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, ...@@ -360,17 +392,23 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_RELATIVE: /* Adjust by program base. */ case R_390_RELATIVE: /* Adjust by program base. */
/* Only needed if we want to support loading of /* Only needed if we want to support loading of
modules linked with -shared. */ modules linked with -shared. */
break; return -ENOEXEC;
default: default:
printk(KERN_ERR "module %s: Unknown relocation: %u\n", printk(KERN_ERR "module %s: unknown relocation: %u\n",
me->name, r_type); me->name, r_type);
return -ENOEXEC; return -ENOEXEC;
} }
if (rc) {
printk(KERN_ERR "module %s: relocation error for symbol %s "
"(r_type %i, value 0x%lx)\n",
me->name, strtab + symtab[r_sym].st_name,
r_type, (unsigned long) val);
return rc;
}
return 0; return 0;
} }
int int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
unsigned int symindex, unsigned int relsec, unsigned int symindex, unsigned int relsec,
struct module *me) struct module *me)
{ {
...@@ -388,7 +426,7 @@ apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, ...@@ -388,7 +426,7 @@ apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
n = sechdrs[relsec].sh_size / sizeof(Elf_Rela); n = sechdrs[relsec].sh_size / sizeof(Elf_Rela);
for (i = 0; i < n; i++, rela++) { for (i = 0; i < n; i++, rela++) {
rc = apply_rela(rela, base, symtab, me); rc = apply_rela(rela, base, symtab, strtab, me);
if (rc) if (rc)
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment