xref: /netbsd/sys/kern/sys_module.c (revision b1483161)
1 /*	$NetBSD: sys_module.c,v 1.30 2022/05/24 06:20:05 andvar 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.30 2022/05/24 06:20:05 andvar Exp $");
35 
36 #ifdef _KERNEL_OPT
37 #include "opt_modular.h"
38 #endif
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/proc.h>
43 #include <sys/namei.h>
44 #include <sys/kauth.h>
45 #include <sys/kmem.h>
46 #include <sys/kobj.h>
47 #include <sys/module.h>
48 #include <sys/syscall.h>
49 #include <sys/syscallargs.h>
50 #include <sys/compat_stub.h>
51 
52 /*
53  * Arbitrary limit to avoid DoS for excessive memory allocation.
54  */
55 #define MAXPROPSLEN	4096
56 
57 int
handle_modctl_load(const char * ml_filename,int ml_flags,const char * ml_props,size_t ml_propslen)58 handle_modctl_load(const char *ml_filename, int ml_flags, const char *ml_props,
59     size_t ml_propslen)
60 {
61 	char *path;
62 	char *props;
63 	int error;
64 	prop_dictionary_t dict;
65 	size_t propslen = 0;
66 
67 	if ((ml_props != NULL && ml_propslen == 0) ||
68 	    (ml_props == NULL && ml_propslen > 0)) {
69 		return EINVAL;
70 	}
71 
72 	path = PNBUF_GET();
73 	error = copyinstr(ml_filename, path, MAXPATHLEN, NULL);
74 	if (error != 0)
75 		goto out1;
76 
77 	if (ml_props != NULL) {
78 		if (ml_propslen > MAXPROPSLEN) {
79 			error = ENOMEM;
80 			goto out1;
81 		}
82 		propslen = ml_propslen + 1;
83 
84 		props = kmem_alloc(propslen, KM_SLEEP);
85 		error = copyinstr(ml_props, props, propslen, NULL);
86 		if (error != 0)
87 			goto out2;
88 
89 		dict = prop_dictionary_internalize(props);
90 		if (dict == NULL) {
91 			error = EINVAL;
92 			goto out2;
93 		}
94 	} else {
95 		dict = NULL;
96 		props = NULL;
97 	}
98 
99 	error = module_load(path, ml_flags, dict, MODULE_CLASS_ANY);
100 
101 	if (dict != NULL) {
102 		prop_object_release(dict);
103 	}
104 
105 out2:
106 	if (props != NULL) {
107 		kmem_free(props, propslen);
108 	}
109 out1:
110 	PNBUF_PUT(path);
111 	return error;
112 }
113 
114 static int
handle_modctl_stat(struct iovec * iov,void * arg)115 handle_modctl_stat(struct iovec *iov, void *arg)
116 {
117 	int ms_cnt;
118 	modstat_t *ms, *mso;
119 	size_t ms_len;
120 	char *req, *reqo;
121 	size_t req_len;
122 	char *out_p;
123 	size_t out_s;
124 
125 	modinfo_t *mi;
126 	module_t *mod;
127 	vaddr_t addr;
128 	size_t size;
129 	size_t used;
130 	int off;
131 	int error;
132 	bool stataddr;
133 
134 	/* If not privileged, don't expose kernel addresses. */
135 	error = kauth_authorize_process(kauth_cred_get(), KAUTH_PROCESS_CANSEE,
136 	    curproc, KAUTH_ARG(KAUTH_REQ_PROCESS_CANSEE_KPTR), NULL, NULL);
137 	stataddr = (error == 0);
138 
139 	kernconfig_lock();
140 	ms_cnt = 0;
141 	req_len = 1;
142 
143 	/*
144 	 * Count up the number of modstat_t needed, and total size of
145 	 * require_module lists on both active and built-in lists
146 	 */
147 	TAILQ_FOREACH(mod, &module_list, mod_chain) {
148 		ms_cnt++;
149 		mi = mod->mod_info;
150 		if (mi->mi_required != NULL) {
151 			req_len += strlen(mi->mi_required) + 1;
152 		}
153 	}
154 	TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
155 		ms_cnt++;
156 		mi = mod->mod_info;
157 		if (mi->mi_required != NULL) {
158 			req_len += strlen(mi->mi_required) + 1;
159 		}
160 	}
161 
162 	/* Allocate internal buffers to hold all the output data */
163 	ms_len = ms_cnt * sizeof(modstat_t);
164 	ms = kmem_zalloc(ms_len, KM_SLEEP);
165 	req = kmem_zalloc(req_len, KM_SLEEP);
166 
167 	mso = ms;
168 	reqo = req++;
169 	off = 1;
170 
171 	/*
172 	 * Load data into our internal buffers for both active and
173 	 * built-in module lists
174 	 */
175 	TAILQ_FOREACH(mod, &module_list, mod_chain) {
176 		mi = mod->mod_info;
177 		strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name));
178 		if (mi->mi_required != NULL) {
179 			ms->ms_reqoffset = off;
180 			used = strlcpy(req,  mi->mi_required, req_len - off);
181 			KASSERTMSG(used < req_len - off, "reqlist grew!");
182 			off += used + 1;
183 			req += used + 1;
184 		} else
185 			ms->ms_reqoffset = 0;
186 		if (mod->mod_kobj != NULL && stataddr) {
187 			kobj_stat(mod->mod_kobj, &addr, &size);
188 			ms->ms_addr = addr;
189 			ms->ms_size = size;
190 		}
191 		ms->ms_class = mi->mi_class;
192 		ms->ms_refcnt = mod->mod_refcnt;
193 		ms->ms_source = mod->mod_source;
194 		ms->ms_flags = mod->mod_flags;
195 		ms++;
196 	}
197 	TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
198 		mi = mod->mod_info;
199 		strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name));
200 		if (mi->mi_required != NULL) {
201 			ms->ms_reqoffset = off;
202 			used = strlcpy(req,  mi->mi_required, req_len - off);
203 			KASSERTMSG(used < req_len - off, "reqlist grew!");
204 			off += used + 1;
205 			req += used + 1;
206 		} else
207 			ms->ms_reqoffset = 0;
208 		if (mod->mod_kobj != NULL && stataddr) {
209 			kobj_stat(mod->mod_kobj, &addr, &size);
210 			ms->ms_addr = addr;
211 			ms->ms_size = size;
212 		}
213 		ms->ms_class = mi->mi_class;
214 		ms->ms_refcnt = -1;
215 		KASSERT(mod->mod_source == MODULE_SOURCE_KERNEL);
216 		ms->ms_source = mod->mod_source;
217 		ms++;
218 	}
219 	kernconfig_unlock();
220 
221 	/*
222 	 * Now copyout our internal buffers back to userland
223 	 */
224 	out_p = iov->iov_base;
225 	out_s = iov->iov_len;
226 	size = sizeof(ms_cnt);
227 
228 	/* Copy out the count of modstat_t */
229 	if (out_s) {
230 		size = uimin(sizeof(ms_cnt), out_s);
231 		error = copyout(&ms_cnt, out_p, size);
232 		out_p += size;
233 		out_s -= size;
234 	}
235 	/* Copy out the modstat_t array */
236 	if (out_s && error == 0) {
237 		size = uimin(ms_len, out_s);
238 		error = copyout(mso, out_p, size);
239 		out_p += size;
240 		out_s -= size;
241 	}
242 	/* Copy out the "required" strings */
243 	if (out_s && error == 0) {
244 		size = uimin(req_len, out_s);
245 		error = copyout(reqo, out_p, size);
246 		out_p += size;
247 		out_s -= size;
248 	}
249 	kmem_free(mso, ms_len);
250 	kmem_free(reqo, req_len);
251 
252 	/* Finally, update the userland copy of the iovec's length */
253 	if (error == 0) {
254 		iov->iov_len = ms_len + req_len + sizeof(ms_cnt);
255 		error = copyout(iov, arg, sizeof(*iov));
256 	}
257 
258 	return error;
259 }
260 
261 int
sys_modctl(struct lwp * l,const struct sys_modctl_args * uap,register_t * retval)262 sys_modctl(struct lwp *l, const struct sys_modctl_args *uap,
263 	   register_t *retval)
264 {
265 	/* {
266 		syscallarg(int)		cmd;
267 		syscallarg(void *)	arg;
268 	} */
269 	char buf[MAXMODNAME];
270 	struct iovec iov;
271 	modctl_load_t ml;
272 	int error;
273 	void *arg;
274 #ifdef MODULAR
275 	uintptr_t loadtype;
276 #endif
277 
278 	arg = SCARG(uap, arg);
279 
280 	switch (SCARG(uap, cmd)) {
281 	case MODCTL_LOAD:
282 		error = copyin(arg, &ml, sizeof(ml));
283 		if (error != 0)
284 			break;
285 		error = handle_modctl_load(ml.ml_filename, ml.ml_flags,
286 		    ml.ml_props, ml.ml_propslen);
287 		break;
288 
289 	case MODCTL_UNLOAD:
290 		error = copyinstr(arg, buf, sizeof(buf), NULL);
291 		if (error == 0) {
292 			error = module_unload(buf);
293 		}
294 		break;
295 
296 	case MODCTL_STAT:
297 		error = copyin(arg, &iov, sizeof(iov));
298 		if (error != 0) {
299 			break;
300 		}
301 		error = handle_modctl_stat(&iov, arg);
302 		break;
303 
304 	case MODCTL_EXISTS:
305 #ifndef MODULAR
306 		error = ENOSYS;
307 #else
308 		loadtype = (uintptr_t)arg;
309 		switch (loadtype) {	/* 0 = modload, 1 = autoload */
310 		case 0:			/* FALLTHROUGH */
311 		case 1:
312 			error = kauth_authorize_system(kauth_cred_get(),
313 			     KAUTH_SYSTEM_MODULE, 0,
314 			     (void *)(uintptr_t)MODCTL_LOAD,
315 			     (void *)loadtype, NULL);
316 			break;
317 		default:
318 			error = EINVAL;
319 			break;
320 		}
321 #endif
322 		break;
323 
324 	default:
325 		(void)module_autoload("compat_80", MODULE_CLASS_EXEC);
326 		MODULE_HOOK_CALL(compat_modstat_80_hook,
327 		    (SCARG(uap, cmd), &iov, arg), enosys(), error);
328 		if (error == ENOSYS)
329 			error = EINVAL;
330 		break;
331 	}
332 
333 	return error;
334 }
335