diff options
Diffstat (limited to 'kernel/module')
| -rw-r--r-- | kernel/module/internal.h | 12 | ||||
| -rw-r--r-- | kernel/module/kallsyms.c | 28 | ||||
| -rw-r--r-- | kernel/module/main.c | 120 | 
3 files changed, 109 insertions, 51 deletions
diff --git a/kernel/module/internal.h b/kernel/module/internal.h index dc7b0160c480..c8b7b4dcf782 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -32,6 +32,18 @@  /* Maximum number of characters written by module_flags() */  #define MODULE_FLAGS_BUF_SIZE (TAINT_FLAGS_COUNT + 4) +struct kernel_symbol { +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS +	int value_offset; +	int name_offset; +	int namespace_offset; +#else +	unsigned long value; +	const char *name; +	const char *namespace; +#endif +}; +  extern struct mutex module_mutex;  extern struct list_head modules; diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c index c550d7d45f2f..ef73ae7c8909 100644 --- a/kernel/module/kallsyms.c +++ b/kernel/module/kallsyms.c @@ -381,34 +381,6 @@ out:  	return -ERANGE;  } -int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, -			       unsigned long *offset, char *modname, char *name) -{ -	struct module *mod; - -	preempt_disable(); -	list_for_each_entry_rcu(mod, &modules, list) { -		if (mod->state == MODULE_STATE_UNFORMED) -			continue; -		if (within_module(addr, mod)) { -			const char *sym; - -			sym = find_kallsyms_symbol(mod, addr, size, offset); -			if (!sym) -				goto out; -			if (modname) -				strscpy(modname, mod->name, MODULE_NAME_LEN); -			if (name) -				strscpy(name, sym, KSYM_NAME_LEN); -			preempt_enable(); -			return 0; -		} -	} -out: -	preempt_enable(); -	return -ERANGE; -} -  int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,  		       char *name, char *module_name, int *exported)  { diff --git a/kernel/module/main.c b/kernel/module/main.c index 4e2cf784cf8c..834de86ebe35 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -820,10 +820,8 @@ static struct module_attribute modinfo_refcnt =  void __module_get(struct module *module)  {  	if (module) { -		preempt_disable();  		atomic_inc(&module->refcnt);  		trace_module_get(module, _RET_IP_); -		preempt_enable();  	}  }  EXPORT_SYMBOL(__module_get); @@ -833,15 +831,12 @@ bool try_module_get(struct module *module)  	bool ret = true;  	if (module) { -		preempt_disable();  		/* Note: here, we can fail to get a reference */  		if (likely(module_is_live(module) &&  			   atomic_inc_not_zero(&module->refcnt) != 0))  			trace_module_get(module, _RET_IP_);  		else  			ret = false; - -		preempt_enable();  	}  	return ret;  } @@ -852,11 +847,9 @@ void module_put(struct module *module)  	int ret;  	if (module) { -		preempt_disable();  		ret = atomic_dec_if_positive(&module->refcnt);  		WARN_ON(ret < 0);	/* Failed to put refcount */  		trace_module_put(module, _RET_IP_); -		preempt_enable();  	}  }  EXPORT_SYMBOL(module_put); @@ -3057,26 +3050,83 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,  	return load_module(&info, uargs, 0);  } -SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) +struct idempotent { +	const void *cookie; +	struct hlist_node entry; +	struct completion complete; +	int ret; +}; + +#define IDEM_HASH_BITS 8 +static struct hlist_head idem_hash[1 << IDEM_HASH_BITS]; +static DEFINE_SPINLOCK(idem_lock); + +static bool idempotent(struct idempotent *u, const void *cookie) +{ +	int hash = hash_ptr(cookie, IDEM_HASH_BITS); +	struct hlist_head *head = idem_hash + hash; +	struct idempotent *existing; +	bool first; + +	u->ret = 0; +	u->cookie = cookie; +	init_completion(&u->complete); + +	spin_lock(&idem_lock); +	first = true; +	hlist_for_each_entry(existing, head, entry) { +		if (existing->cookie != cookie) +			continue; +		first = false; +		break; +	} +	hlist_add_head(&u->entry, idem_hash + hash); +	spin_unlock(&idem_lock); + +	return !first; +} + +/* + * We were the first one with 'cookie' on the list, and we ended + * up completing the operation. We now need to walk the list, + * remove everybody - which includes ourselves - fill in the return + * value, and then complete the operation. + */ +static void idempotent_complete(struct idempotent *u, int ret) +{ +	const void *cookie = u->cookie; +	int hash = hash_ptr(cookie, IDEM_HASH_BITS); +	struct hlist_head *head = idem_hash + hash; +	struct hlist_node *next; +	struct idempotent *pos; + +	spin_lock(&idem_lock); +	hlist_for_each_entry_safe(pos, next, head, entry) { +		if (pos->cookie != cookie) +			continue; +		hlist_del(&pos->entry); +		pos->ret = ret; +		complete(&pos->complete); +	} +	spin_unlock(&idem_lock); +} + +static int init_module_from_file(struct file *f, const char __user * uargs, int flags)  { +	struct idempotent idem;  	struct load_info info = { };  	void *buf = NULL; -	int len; -	int err; - -	err = may_init_module(); -	if (err) -		return err; +	int len, ret; -	pr_debug("finit_module: fd=%d, uargs=%p, flags=%i\n", fd, uargs, flags); +	if (!f || !(f->f_mode & FMODE_READ)) +		return -EBADF; -	if (flags & ~(MODULE_INIT_IGNORE_MODVERSIONS -		      |MODULE_INIT_IGNORE_VERMAGIC -		      |MODULE_INIT_COMPRESSED_FILE)) -		return -EINVAL; +	if (idempotent(&idem, file_inode(f))) { +		wait_for_completion(&idem.complete); +		return idem.ret; +	} -	len = kernel_read_file_from_fd(fd, 0, &buf, INT_MAX, NULL, -				       READING_MODULE); +	len = kernel_read_file(f, 0, &buf, INT_MAX, NULL, READING_MODULE);  	if (len < 0) {  		mod_stat_inc(&failed_kreads);  		mod_stat_add_long(len, &invalid_kread_bytes); @@ -3084,7 +3134,7 @@ SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)  	}  	if (flags & MODULE_INIT_COMPRESSED_FILE) { -		err = module_decompress(&info, buf, len); +		int err = module_decompress(&info, buf, len);  		vfree(buf); /* compressed data is no longer needed */  		if (err) {  			mod_stat_inc(&failed_decompress); @@ -3096,7 +3146,31 @@ SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)  		info.len = len;  	} -	return load_module(&info, uargs, flags); +	ret = load_module(&info, uargs, flags); +	idempotent_complete(&idem, ret); +	return ret; +} + +SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) +{ +	int err; +	struct fd f; + +	err = may_init_module(); +	if (err) +		return err; + +	pr_debug("finit_module: fd=%d, uargs=%p, flags=%i\n", fd, uargs, flags); + +	if (flags & ~(MODULE_INIT_IGNORE_MODVERSIONS +		      |MODULE_INIT_IGNORE_VERMAGIC +		      |MODULE_INIT_COMPRESSED_FILE)) +		return -EINVAL; + +	f = fdget(fd); +	err = init_module_from_file(f.file, uargs, flags); +	fdput(f); +	return err;  }  /* Keep in sync with MODULE_FLAGS_BUF_SIZE !!! */  |