Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+89 −51
Original line number Original line Diff line number Diff line
@@ -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)
 * 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,
	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,
	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,
	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,
		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,
			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,
		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,
	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,
	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;
	}
	}