diff options
Diffstat (limited to 'arch/x86/kernel/module.c')
| -rw-r--r-- | arch/x86/kernel/module.c | 97 | 
1 files changed, 60 insertions, 37 deletions
diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c index 705fb2a41d7d..84ad0e61ba6e 100644 --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c @@ -129,22 +129,27 @@ int apply_relocate(Elf32_Shdr *sechdrs,  	return 0;  }  #else /*X86_64*/ -static int __apply_relocate_add(Elf64_Shdr *sechdrs, +static int __write_relocate_add(Elf64_Shdr *sechdrs,  		   const char *strtab,  		   unsigned int symindex,  		   unsigned int relsec,  		   struct module *me, -		   void *(*write)(void *dest, const void *src, size_t len)) +		   void *(*write)(void *dest, const void *src, size_t len), +		   bool apply)  {  	unsigned int i;  	Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;  	Elf64_Sym *sym;  	void *loc;  	u64 val; +	u64 zero = 0ULL; -	DEBUGP("Applying relocate section %u to %u\n", +	DEBUGP("%s relocate section %u to %u\n", +	       apply ? "Applying" : "Clearing",  	       relsec, sechdrs[relsec].sh_info);  	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { +		size_t size; +  		/* This is where to make the change */  		loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr  			+ rel[i].r_offset; @@ -162,56 +167,53 @@ static int __apply_relocate_add(Elf64_Shdr *sechdrs,  		switch (ELF64_R_TYPE(rel[i].r_info)) {  		case R_X86_64_NONE: -			break; +			continue;  /* nothing to write */  		case R_X86_64_64: -			if (*(u64 *)loc != 0) -				goto invalid_relocation; -			write(loc, &val, 8); +			size = 8;  			break;  		case R_X86_64_32: -			if (*(u32 *)loc != 0) -				goto invalid_relocation; -			write(loc, &val, 4); -			if (val != *(u32 *)loc) +			if (val != *(u32 *)&val)  				goto overflow; +			size = 4;  			break;  		case R_X86_64_32S: -			if (*(s32 *)loc != 0) -				goto invalid_relocation; -			write(loc, &val, 4); -			if ((s64)val != *(s32 *)loc) +			if ((s64)val != *(s32 *)&val)  				goto overflow; +			size = 4;  			break;  		case R_X86_64_PC32:  		case R_X86_64_PLT32: -			if (*(u32 *)loc != 0) -				goto invalid_relocation;  			val -= (u64)loc; -			write(loc, &val, 4); -#if 0 -			if ((s64)val != *(s32 *)loc) -				goto overflow; -#endif +			size = 4;  			break;  		case R_X86_64_PC64: -			if (*(u64 *)loc != 0) -				goto invalid_relocation;  			val -= (u64)loc; -			write(loc, &val, 8); +			size = 8;  			break;  		default:  			pr_err("%s: Unknown rela relocation: %llu\n",  			       me->name, ELF64_R_TYPE(rel[i].r_info));  			return -ENOEXEC;  		} + +		if (apply) { +			if (memcmp(loc, &zero, size)) { +				pr_err("x86/modules: Invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n", +				       (int)ELF64_R_TYPE(rel[i].r_info), loc, val); +				return -ENOEXEC; +			} +			write(loc, &val, size); +		} else { +			if (memcmp(loc, &val, size)) { +				pr_warn("x86/modules: Invalid relocation target, existing value does not match expected value for type %d, loc %p, val %Lx\n", +					(int)ELF64_R_TYPE(rel[i].r_info), loc, val); +				return -ENOEXEC; +			} +			write(loc, &zero, size); +		}  	}  	return 0; -invalid_relocation: -	pr_err("x86/modules: Skipping invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n", -	       (int)ELF64_R_TYPE(rel[i].r_info), loc, val); -	return -ENOEXEC; -  overflow:  	pr_err("overflow in relocation type %d val %Lx\n",  	       (int)ELF64_R_TYPE(rel[i].r_info), val); @@ -220,11 +222,12 @@ overflow:  	return -ENOEXEC;  } -int apply_relocate_add(Elf64_Shdr *sechdrs, -		   const char *strtab, -		   unsigned int symindex, -		   unsigned int relsec, -		   struct module *me) +static int write_relocate_add(Elf64_Shdr *sechdrs, +			      const char *strtab, +			      unsigned int symindex, +			      unsigned int relsec, +			      struct module *me, +			      bool apply)  {  	int ret;  	bool early = me->state == MODULE_STATE_UNFORMED; @@ -235,8 +238,8 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,  		mutex_lock(&text_mutex);  	} -	ret = __apply_relocate_add(sechdrs, strtab, symindex, relsec, me, -				   write); +	ret = __write_relocate_add(sechdrs, strtab, symindex, relsec, me, +				   write, apply);  	if (!early) {  		text_poke_sync(); @@ -246,6 +249,26 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,  	return ret;  } +int apply_relocate_add(Elf64_Shdr *sechdrs, +		   const char *strtab, +		   unsigned int symindex, +		   unsigned int relsec, +		   struct module *me) +{ +	return write_relocate_add(sechdrs, strtab, symindex, relsec, me, true); +} + +#ifdef CONFIG_LIVEPATCH +void clear_relocate_add(Elf64_Shdr *sechdrs, +			const char *strtab, +			unsigned int symindex, +			unsigned int relsec, +			struct module *me) +{ +	write_relocate_add(sechdrs, strtab, symindex, relsec, me, false); +} +#endif +  #endif  int module_finalize(const Elf_Ehdr *hdr,  |