Skip to content

Commit

Permalink
s390/modules: add relocation overflow checking
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Martin Schwidefsky committed Feb 14, 2013
1 parent 4d334fd commit 083e14c
Showing 1 changed file with 89 additions and 51 deletions.
140 changes: 89 additions & 51 deletions arch/s390/kernel/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ void module_free(struct module *mod, void *module_region)
vfree(module_region);
}

static void
check_rela(Elf_Rela *rela, struct module *me)
static void check_rela(Elf_Rela *rela, struct module *me)
{
struct mod_arch_syminfo *info;

Expand Down Expand Up @@ -115,9 +114,8 @@ check_rela(Elf_Rela *rela, struct module *me)
* Account for GOT and PLT relocations. We can't add sections for
* got and plt but we can increase the core module size.
*/
int
module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
char *secstrings, struct module *me)
int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
char *secstrings, struct module *me)
{
Elf_Shdr *symtab;
Elf_Sym *symbols;
Expand Down Expand Up @@ -179,13 +177,52 @@ module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
return 0;
}

static int
apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
struct module *me)
static int apply_rela_bits(Elf_Addr loc, Elf_Addr val,
int sign, int bits, int shift)
{
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;
Elf_Addr loc, val;
int r_type, r_sym;
int rc;

/* This is where to make the change */
loc = base + rela->r_offset;
Expand All @@ -205,20 +242,17 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_64: /* Direct 64 bit. */
val += rela->r_addend;
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)
*(unsigned short *) loc = (val & 0xfff) |
(*(unsigned short *) loc & 0xf000);
rc = apply_rela_bits(loc, val, 0, 12, 0);
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)
*(unsigned int *) loc =
(*(unsigned int *) loc & 0xf00000ff) |
(val & 0xfff) << 16 | (val & 0xff000) >> 4;
rc = apply_rela_bits(loc, val, 1, 20, 0);
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)
*(unsigned long *) loc = val;
rc = apply_rela_bits(loc, val, 0, 64, 0);
break;
case R_390_PC16: /* PC relative 16 bit. */
case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */
Expand All @@ -227,15 +261,15 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_PC64: /* PC relative 64 bit. */
val += rela->r_addend - loc;
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)
*(unsigned short *) loc = val >> 1;
rc = apply_rela_bits(loc, val, 1, 16, 1);
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)
*(unsigned int *) loc = val;
rc = apply_rela_bits(loc, val, 1, 32, 0);
else if (r_type == R_390_PC64)
*(unsigned long *) loc = val;
rc = apply_rela_bits(loc, val, 1, 64, 0);
break;
case R_390_GOT12: /* 12 bit GOT offset. */
case R_390_GOT16: /* 16 bit GOT offset. */
Expand All @@ -260,26 +294,24 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val = info->got_offset + rela->r_addend;
if (r_type == R_390_GOT12 ||
r_type == R_390_GOTPLT12)
*(unsigned short *) loc = (val & 0xfff) |
(*(unsigned short *) loc & 0xf000);
rc = apply_rela_bits(loc, val, 0, 12, 0);
else if (r_type == R_390_GOT16 ||
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 ||
r_type == R_390_GOTPLT20)
*(unsigned int *) loc =
(*(unsigned int *) loc & 0xf00000ff) |
(val & 0xfff) << 16 | (val & 0xff000) >> 4;
rc = apply_rela_bits(loc, val, 1, 20, 0);
else if (r_type == R_390_GOT32 ||
r_type == R_390_GOTPLT32)
*(unsigned int *) loc = val;
else if (r_type == R_390_GOTENT ||
r_type == R_390_GOTPLTENT)
*(unsigned int *) loc =
(val + (Elf_Addr) me->module_core - loc) >> 1;
rc = apply_rela_bits(loc, val, 0, 32, 0);
else if (r_type == R_390_GOT64 ||
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;
case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */
case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */
Expand Down Expand Up @@ -321,58 +353,64 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val += rela->r_addend - loc;
}
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)
*(unsigned short *) loc = val;
rc = apply_rela_bits(loc, val, 0, 16, 0);
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 ||
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 ||
r_type == R_390_PLTOFF64)
*(unsigned long *) loc = val;
rc = apply_rela_bits(loc, val, 0, 64, 0);
break;
case R_390_GOTOFF16: /* 16 bit offset to GOT. */
case R_390_GOTOFF32: /* 32 bit offset to GOT. */
case R_390_GOTOFF64: /* 64 bit offset to GOT. */
val = val + rela->r_addend -
((Elf_Addr) me->module_core + me->arch.got_offset);
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)
*(unsigned int *) loc = val;
rc = apply_rela_bits(loc, val, 0, 32, 0);
else if (r_type == R_390_GOTOFF64)
*(unsigned long *) loc = val;
rc = apply_rela_bits(loc, val, 0, 64, 0);
break;
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. */
val = (Elf_Addr) me->module_core + me->arch.got_offset +
rela->r_addend - loc;
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)
*(unsigned int *) loc = val >> 1;
rc = apply_rela_bits(loc, val, 1, 32, 1);
break;
case R_390_COPY:
case R_390_GLOB_DAT: /* Create GOT entry. */
case R_390_JMP_SLOT: /* Create PLT entry. */
case R_390_RELATIVE: /* Adjust by program base. */
/* Only needed if we want to support loading of
modules linked with -shared. */
break;
return -ENOEXEC;
default:
printk(KERN_ERR "module %s: Unknown relocation: %u\n",
printk(KERN_ERR "module %s: unknown relocation: %u\n",
me->name, r_type);
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;
}

int
apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
unsigned int symindex, unsigned int relsec,
struct module *me)
int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
unsigned int symindex, unsigned int relsec,
struct module *me)
{
Elf_Addr base;
Elf_Sym *symtab;
Expand All @@ -388,7 +426,7 @@ apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
n = sechdrs[relsec].sh_size / sizeof(Elf_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)
return rc;
}
Expand Down

0 comments on commit 083e14c

Please sign in to comment.