1/*	$NetBSD: sys_module.c,v 1.13 2011/07/08 09:32:45 mrg Exp $	*/
2
3/*-
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * System calls relating to loadable modules.
31 */
32
33#include <sys/cdefs.h>
34__KERNEL_RCSID(0, "$NetBSD: sys_module.c,v 1.13 2011/07/08 09:32:45 mrg Exp $");
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/proc.h>
39#include <sys/namei.h>
40#include <sys/kmem.h>
41#include <sys/kobj.h>
42#include <sys/module.h>
43#include <sys/syscall.h>
44#include <sys/syscallargs.h>
45
46/*
47 * Arbitrary limit to avoid DoS for excessive memory allocation.
48 */
49#define MAXPROPSLEN	4096
50
51static int
52handle_modctl_load(modctl_load_t *ml)
53{
54	char *path;
55	char *props;
56	int error;
57	prop_dictionary_t dict;
58	size_t propslen = 0;
59
60	if ((ml->ml_props != NULL && ml->ml_propslen == 0) ||
61	    (ml->ml_props == NULL && ml->ml_propslen > 0)) {
62		error = EINVAL;
63		goto out1;
64	}
65
66	path = PNBUF_GET();
67	error = copyinstr(ml->ml_filename, path, MAXPATHLEN, NULL);
68	if (error != 0)
69		goto out2;
70
71	if (ml->ml_props != NULL) {
72		if (ml->ml_propslen > MAXPROPSLEN) {
73			error = ENOMEM;
74			goto out2;
75		}
76		propslen = ml->ml_propslen + 1;
77
78		props = (char *)kmem_alloc(propslen, KM_SLEEP);
79		if (props == NULL) {
80			error = ENOMEM;
81			goto out2;
82		}
83
84		error = copyinstr(ml->ml_props, props, propslen, NULL);
85		if (error != 0)
86			goto out3;
87
88		dict = prop_dictionary_internalize(props);
89		if (dict == NULL) {
90			error = EINVAL;
91			goto out3;
92		}
93	} else {
94		dict = NULL;
95		props = NULL;
96	}
97
98	error = module_load(path, ml->ml_flags, dict, MODULE_CLASS_ANY);
99
100	if (dict != NULL) {
101		prop_object_release(dict);
102	}
103
104out3:
105	if (props != NULL) {
106		kmem_free(props, propslen);
107	}
108out2:
109	PNBUF_PUT(path);
110out1:
111
112	return error;
113}
114
115int
116sys_modctl(struct lwp *l, const struct sys_modctl_args *uap,
117	   register_t *retval)
118{
119	/* {
120		syscallarg(int)		cmd;
121		syscallarg(void *)	arg;
122	} */
123	char buf[MAXMODNAME];
124	size_t mslen;
125	module_t *mod;
126	modinfo_t *mi;
127	modstat_t *ms, *mso;
128	vaddr_t addr;
129	size_t size;
130	struct iovec iov;
131	modctl_load_t ml;
132	int error;
133	void *arg;
134
135	arg = SCARG(uap, arg);
136
137	switch (SCARG(uap, cmd)) {
138	case MODCTL_LOAD:
139		error = copyin(arg, &ml, sizeof(ml));
140		if (error != 0)
141			break;
142		error = handle_modctl_load(&ml);
143		break;
144
145	case MODCTL_UNLOAD:
146		error = copyinstr(arg, buf, sizeof(buf), NULL);
147		if (error == 0) {
148			error = module_unload(buf);
149		}
150		break;
151
152	case MODCTL_STAT:
153		error = copyin(arg, &iov, sizeof(iov));
154		if (error != 0) {
155			break;
156		}
157		kernconfig_lock();
158		mslen = (module_count+module_builtinlist+1) * sizeof(modstat_t);
159		mso = kmem_zalloc(mslen, KM_SLEEP);
160		if (mso == NULL) {
161			kernconfig_unlock();
162			return ENOMEM;
163		}
164		ms = mso;
165		TAILQ_FOREACH(mod, &module_list, mod_chain) {
166			mi = mod->mod_info;
167			strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name));
168			if (mi->mi_required != NULL) {
169				strlcpy(ms->ms_required, mi->mi_required,
170				    sizeof(ms->ms_required));
171			}
172			if (mod->mod_kobj != NULL) {
173				kobj_stat(mod->mod_kobj, &addr, &size);
174				ms->ms_addr = addr;
175				ms->ms_size = size;
176			}
177			ms->ms_class = mi->mi_class;
178			ms->ms_refcnt = mod->mod_refcnt;
179			ms->ms_source = mod->mod_source;
180			ms++;
181		}
182		TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
183			mi = mod->mod_info;
184			strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name));
185			if (mi->mi_required != NULL) {
186				strlcpy(ms->ms_required, mi->mi_required,
187				    sizeof(ms->ms_required));
188			}
189			if (mod->mod_kobj != NULL) {
190				kobj_stat(mod->mod_kobj, &addr, &size);
191				ms->ms_addr = addr;
192				ms->ms_size = size;
193			}
194			ms->ms_class = mi->mi_class;
195			ms->ms_refcnt = -1;
196			KASSERT(mod->mod_source == MODULE_SOURCE_KERNEL);
197			ms->ms_source = mod->mod_source;
198			ms++;
199		}
200		kernconfig_unlock();
201		error = copyout(mso, iov.iov_base,
202		    min(mslen - sizeof(modstat_t), iov.iov_len));
203		kmem_free(mso, mslen);
204		if (error == 0) {
205			iov.iov_len = mslen - sizeof(modstat_t);
206			error = copyout(&iov, arg, sizeof(iov));
207		}
208		break;
209
210	default:
211		error = EINVAL;
212		break;
213	}
214
215	return error;
216}
217