diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-18 03:11:27 +0300 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-18 03:11:27 +0300 | 
| commit | 0b86c75db6e7f68c22ac5d0dae0f551c4897cdf5 (patch) | |
| tree | d1be280c331fbd85c021b5686914d2cc21475f54 /kernel/module.c | |
| parent | 16bf8348055fe4615bd08ef50f9874f5dcc10268 (diff) | |
| parent | be69f70e6395a4ba9c178b2531433547e1955195 (diff) | |
| download | linux-0b86c75db6e7f68c22ac5d0dae0f551c4897cdf5.tar.xz | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching
Pull livepatching updates from Jiri Kosina:
 - remove of our own implementation of architecture-specific relocation
   code and leveraging existing code in the module loader to perform
   arch-dependent work, from Jessica Yu.
   The relevant patches have been acked by Rusty (for module.c) and
   Heiko (for s390).
 - live patching support for ppc64le, which is a joint work of Michael
   Ellerman and Torsten Duwe.  This is coming from topic branch that is
   share between livepatching.git and ppc tree.
 - addition of livepatching documentation from Petr Mladek
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching:
  livepatch: make object/func-walking helpers more robust
  livepatch: Add some basic livepatch documentation
  powerpc/livepatch: Add live patching support on ppc64le
  powerpc/livepatch: Add livepatch stack to struct thread_info
  powerpc/livepatch: Add livepatch header
  livepatch: Allow architectures to specify an alternate ftrace location
  ftrace: Make ftrace_location_range() global
  livepatch: robustify klp_register_patch() API error checking
  Documentation: livepatch: outline Elf format and requirements for patch modules
  livepatch: reuse module loader code to write relocations
  module: s390: keep mod_arch_specific for livepatch modules
  module: preserve Elf information for livepatch modules
  Elf: add livepatch-specific Elf constants
Diffstat (limited to 'kernel/module.c')
| -rw-r--r-- | kernel/module.c | 125 | 
1 files changed, 122 insertions, 3 deletions
diff --git a/kernel/module.c b/kernel/module.c index 041200ca4a2d..5f71aa63ed2a 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1973,6 +1973,83 @@ static void module_enable_nx(const struct module *mod) { }  static void module_disable_nx(const struct module *mod) { }  #endif +#ifdef CONFIG_LIVEPATCH +/* + * Persist Elf information about a module. Copy the Elf header, + * section header table, section string table, and symtab section + * index from info to mod->klp_info. + */ +static int copy_module_elf(struct module *mod, struct load_info *info) +{ +	unsigned int size, symndx; +	int ret; + +	size = sizeof(*mod->klp_info); +	mod->klp_info = kmalloc(size, GFP_KERNEL); +	if (mod->klp_info == NULL) +		return -ENOMEM; + +	/* Elf header */ +	size = sizeof(mod->klp_info->hdr); +	memcpy(&mod->klp_info->hdr, info->hdr, size); + +	/* Elf section header table */ +	size = sizeof(*info->sechdrs) * info->hdr->e_shnum; +	mod->klp_info->sechdrs = kmalloc(size, GFP_KERNEL); +	if (mod->klp_info->sechdrs == NULL) { +		ret = -ENOMEM; +		goto free_info; +	} +	memcpy(mod->klp_info->sechdrs, info->sechdrs, size); + +	/* Elf section name string table */ +	size = info->sechdrs[info->hdr->e_shstrndx].sh_size; +	mod->klp_info->secstrings = kmalloc(size, GFP_KERNEL); +	if (mod->klp_info->secstrings == NULL) { +		ret = -ENOMEM; +		goto free_sechdrs; +	} +	memcpy(mod->klp_info->secstrings, info->secstrings, size); + +	/* Elf symbol section index */ +	symndx = info->index.sym; +	mod->klp_info->symndx = symndx; + +	/* +	 * For livepatch modules, core_kallsyms.symtab is a complete +	 * copy of the original symbol table. Adjust sh_addr to point +	 * to core_kallsyms.symtab since the copy of the symtab in module +	 * init memory is freed at the end of do_init_module(). +	 */ +	mod->klp_info->sechdrs[symndx].sh_addr = \ +		(unsigned long) mod->core_kallsyms.symtab; + +	return 0; + +free_sechdrs: +	kfree(mod->klp_info->sechdrs); +free_info: +	kfree(mod->klp_info); +	return ret; +} + +static void free_module_elf(struct module *mod) +{ +	kfree(mod->klp_info->sechdrs); +	kfree(mod->klp_info->secstrings); +	kfree(mod->klp_info); +} +#else /* !CONFIG_LIVEPATCH */ +static int copy_module_elf(struct module *mod, struct load_info *info) +{ +	return 0; +} + +static void free_module_elf(struct module *mod) +{ +} +#endif /* CONFIG_LIVEPATCH */ +  void __weak module_memfree(void *module_region)  {  	vfree(module_region); @@ -2011,6 +2088,9 @@ static void free_module(struct module *mod)  	/* Free any allocated parameters. */  	destroy_params(mod->kp, mod->num_kp); +	if (is_livepatch_module(mod)) +		free_module_elf(mod); +  	/* Now we can delete it from the lists */  	mutex_lock(&module_mutex);  	/* Unlink carefully: kallsyms could be walking list. */ @@ -2126,6 +2206,10 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)  			       (long)sym[i].st_value);  			break; +		case SHN_LIVEPATCH: +			/* Livepatch symbols are resolved by livepatch */ +			break; +  		case SHN_UNDEF:  			ksym = resolve_symbol_wait(mod, info, name);  			/* Ok if resolved.  */ @@ -2174,6 +2258,10 @@ static int apply_relocations(struct module *mod, const struct load_info *info)  		if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC))  			continue; +		/* Livepatch relocation sections are applied by livepatch */ +		if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) +			continue; +  		if (info->sechdrs[i].sh_type == SHT_REL)  			err = apply_relocate(info->sechdrs, info->strtab,  					     info->index.sym, i, mod); @@ -2469,7 +2557,7 @@ static void layout_symtab(struct module *mod, struct load_info *info)  	/* Compute total space required for the core symbols' strtab. */  	for (ndst = i = 0; i < nsrc; i++) { -		if (i == 0 || +		if (i == 0 || is_livepatch_module(mod) ||  		    is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum,  				   info->index.pcpu)) {  			strtab_size += strlen(&info->strtab[src[i].st_name])+1; @@ -2528,7 +2616,7 @@ static void add_kallsyms(struct module *mod, const struct load_info *info)  	mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs;  	src = mod->kallsyms->symtab;  	for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) { -		if (i == 0 || +		if (i == 0 || is_livepatch_module(mod) ||  		    is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum,  				   info->index.pcpu)) {  			dst[ndst] = src[i]; @@ -2667,6 +2755,26 @@ static int copy_chunked_from_user(void *dst, const void __user *usrc, unsigned l  	return 0;  } +#ifdef CONFIG_LIVEPATCH +static int find_livepatch_modinfo(struct module *mod, struct load_info *info) +{ +	mod->klp = get_modinfo(info, "livepatch") ? true : false; + +	return 0; +} +#else /* !CONFIG_LIVEPATCH */ +static int find_livepatch_modinfo(struct module *mod, struct load_info *info) +{ +	if (get_modinfo(info, "livepatch")) { +		pr_err("%s: module is marked as livepatch module, but livepatch support is disabled", +		       mod->name); +		return -ENOEXEC; +	} + +	return 0; +} +#endif /* CONFIG_LIVEPATCH */ +  /* Sets info->hdr and info->len. */  static int copy_module_from_user(const void __user *umod, unsigned long len,  				  struct load_info *info) @@ -2821,6 +2929,10 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags)  			"is unknown, you have been warned.\n", mod->name);  	} +	err = find_livepatch_modinfo(mod, info); +	if (err) +		return err; +  	/* Set up license info based on the info section */  	set_license(mod, get_modinfo(info, "license")); @@ -3494,6 +3606,12 @@ static int load_module(struct load_info *info, const char __user *uargs,  	if (err < 0)  		goto coming_cleanup; +	if (is_livepatch_module(mod)) { +		err = copy_module_elf(mod, info); +		if (err < 0) +			goto sysfs_cleanup; +	} +  	/* Get rid of temporary copy. */  	free_copy(info); @@ -3502,11 +3620,12 @@ static int load_module(struct load_info *info, const char __user *uargs,  	return do_init_module(mod); + sysfs_cleanup: +	mod_sysfs_teardown(mod);   coming_cleanup:  	blocking_notifier_call_chain(&module_notify_list,  				     MODULE_STATE_GOING, mod);  	klp_module_going(mod); -   bug_cleanup:  	/* module_bug_cleanup needs module_mutex protection */  	mutex_lock(&module_mutex);  | 
