xref: /netbsd-src/sys/kern/sys_module.c (revision 957a9c6750ac39e43dff7783e05a36467cf6f60a)
1*957a9c67Sandvar /*	$NetBSD: sys_module.c,v 1.30 2022/05/24 06:20:05 andvar Exp $	*/
2bbc79e58Sad 
3bbc79e58Sad /*-
4bbc79e58Sad  * Copyright (c) 2008 The NetBSD Foundation, Inc.
5bbc79e58Sad  * All rights reserved.
6bbc79e58Sad  *
7bbc79e58Sad  * Redistribution and use in source and binary forms, with or without
8bbc79e58Sad  * modification, are permitted provided that the following conditions
9bbc79e58Sad  * are met:
10bbc79e58Sad  * 1. Redistributions of source code must retain the above copyright
11bbc79e58Sad  *    notice, this list of conditions and the following disclaimer.
12bbc79e58Sad  * 2. Redistributions in binary form must reproduce the above copyright
13bbc79e58Sad  *    notice, this list of conditions and the following disclaimer in the
14bbc79e58Sad  *    documentation and/or other materials provided with the distribution.
15bbc79e58Sad  *
16bbc79e58Sad  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17bbc79e58Sad  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18bbc79e58Sad  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19bbc79e58Sad  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20bbc79e58Sad  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21bbc79e58Sad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22bbc79e58Sad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23bbc79e58Sad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24bbc79e58Sad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25bbc79e58Sad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26bbc79e58Sad  * POSSIBILITY OF SUCH DAMAGE.
27bbc79e58Sad  */
28bbc79e58Sad 
29bbc79e58Sad /*
30bbc79e58Sad  * System calls relating to loadable modules.
31bbc79e58Sad  */
32bbc79e58Sad 
33bbc79e58Sad #include <sys/cdefs.h>
34*957a9c67Sandvar __KERNEL_RCSID(0, "$NetBSD: sys_module.c,v 1.30 2022/05/24 06:20:05 andvar Exp $");
35d8e04c90Spooka 
36d8e04c90Spooka #ifdef _KERNEL_OPT
37d8e04c90Spooka #include "opt_modular.h"
38d8e04c90Spooka #endif
39bbc79e58Sad 
40bbc79e58Sad #include <sys/param.h>
41bbc79e58Sad #include <sys/systm.h>
42bbc79e58Sad #include <sys/proc.h>
433d82d542Srumble #include <sys/namei.h>
44942121f5Sjnemeth #include <sys/kauth.h>
45bbc79e58Sad #include <sys/kmem.h>
4667280de1Sad #include <sys/kobj.h>
47bbc79e58Sad #include <sys/module.h>
48bbc79e58Sad #include <sys/syscall.h>
49bbc79e58Sad #include <sys/syscallargs.h>
50d91f98a8Spgoyette #include <sys/compat_stub.h>
51bbc79e58Sad 
522d6d2921Smaxv /*
532d6d2921Smaxv  * Arbitrary limit to avoid DoS for excessive memory allocation.
542d6d2921Smaxv  */
552d6d2921Smaxv #define MAXPROPSLEN	4096
562d6d2921Smaxv 
57c41d48b8Smartin int
handle_modctl_load(const char * ml_filename,int ml_flags,const char * ml_props,size_t ml_propslen)58c41d48b8Smartin handle_modctl_load(const char *ml_filename, int ml_flags, const char *ml_props,
59c41d48b8Smartin     size_t ml_propslen)
603fd8e29aSjmmv {
613fd8e29aSjmmv 	char *path;
623fd8e29aSjmmv 	char *props;
633fd8e29aSjmmv 	int error;
643fd8e29aSjmmv 	prop_dictionary_t dict;
65bc7506b1Smrg 	size_t propslen = 0;
663fd8e29aSjmmv 
67c41d48b8Smartin 	if ((ml_props != NULL && ml_propslen == 0) ||
68c41d48b8Smartin 	    (ml_props == NULL && ml_propslen > 0)) {
69d319c468Smaxv 		return EINVAL;
703fd8e29aSjmmv 	}
713fd8e29aSjmmv 
723fd8e29aSjmmv 	path = PNBUF_GET();
73c41d48b8Smartin 	error = copyinstr(ml_filename, path, MAXPATHLEN, NULL);
743fd8e29aSjmmv 	if (error != 0)
75d319c468Smaxv 		goto out1;
763fd8e29aSjmmv 
77c41d48b8Smartin 	if (ml_props != NULL) {
78c41d48b8Smartin 		if (ml_propslen > MAXPROPSLEN) {
792d6d2921Smaxv 			error = ENOMEM;
80d319c468Smaxv 			goto out1;
812d6d2921Smaxv 		}
82c41d48b8Smartin 		propslen = ml_propslen + 1;
832d6d2921Smaxv 
84e65c85beSchristos 		props = kmem_alloc(propslen, KM_SLEEP);
85c41d48b8Smartin 		error = copyinstr(ml_props, props, propslen, NULL);
863fd8e29aSjmmv 		if (error != 0)
87d319c468Smaxv 			goto out2;
883fd8e29aSjmmv 
893fd8e29aSjmmv 		dict = prop_dictionary_internalize(props);
903fd8e29aSjmmv 		if (dict == NULL) {
913fd8e29aSjmmv 			error = EINVAL;
92d319c468Smaxv 			goto out2;
933fd8e29aSjmmv 		}
9430d0592bSjnemeth 	} else {
9530d0592bSjnemeth 		dict = NULL;
9630d0592bSjnemeth 		props = NULL;
9730d0592bSjnemeth 	}
983fd8e29aSjmmv 
99c41d48b8Smartin 	error = module_load(path, ml_flags, dict, MODULE_CLASS_ANY);
1003fd8e29aSjmmv 
10130d0592bSjnemeth 	if (dict != NULL) {
1023fd8e29aSjmmv 		prop_object_release(dict);
10330d0592bSjnemeth 	}
1043fd8e29aSjmmv 
105d319c468Smaxv out2:
10630d0592bSjnemeth 	if (props != NULL) {
10767280de1Sad 		kmem_free(props, propslen);
10830d0592bSjnemeth 	}
1093fd8e29aSjmmv out1:
110d319c468Smaxv 	PNBUF_PUT(path);
1113fd8e29aSjmmv 	return error;
1123fd8e29aSjmmv }
1133fd8e29aSjmmv 
11465028d03Smaxv static int
handle_modctl_stat(struct iovec * iov,void * arg)11565028d03Smaxv handle_modctl_stat(struct iovec *iov, void *arg)
116bbc79e58Sad {
117d91f98a8Spgoyette 	int ms_cnt;
118bbc79e58Sad 	modstat_t *ms, *mso;
119d91f98a8Spgoyette 	size_t ms_len;
120d91f98a8Spgoyette 	char *req, *reqo;
121d91f98a8Spgoyette 	size_t req_len;
122d91f98a8Spgoyette 	char *out_p;
123d91f98a8Spgoyette 	size_t out_s;
124d91f98a8Spgoyette 
12565028d03Smaxv 	modinfo_t *mi;
12665028d03Smaxv 	module_t *mod;
127bbc79e58Sad 	vaddr_t addr;
128bbc79e58Sad 	size_t size;
129d91f98a8Spgoyette 	size_t used;
130d91f98a8Spgoyette 	int off;
131bbc79e58Sad 	int error;
1329b17ef8eSmaxv 	bool stataddr;
1339b17ef8eSmaxv 
1349b17ef8eSmaxv 	/* If not privileged, don't expose kernel addresses. */
135fdec83dcSmaxv 	error = kauth_authorize_process(kauth_cred_get(), KAUTH_PROCESS_CANSEE,
136fdec83dcSmaxv 	    curproc, KAUTH_ARG(KAUTH_REQ_PROCESS_CANSEE_KPTR), NULL, NULL);
1379b17ef8eSmaxv 	stataddr = (error == 0);
138bbc79e58Sad 
13923d5409eSpgoyette 	kernconfig_lock();
140d91f98a8Spgoyette 	ms_cnt = 0;
141d91f98a8Spgoyette 	req_len = 1;
142d91f98a8Spgoyette 
143d91f98a8Spgoyette 	/*
144d91f98a8Spgoyette 	 * Count up the number of modstat_t needed, and total size of
145d91f98a8Spgoyette 	 * require_module lists on both active and built-in lists
146d91f98a8Spgoyette 	 */
147d91f98a8Spgoyette 	TAILQ_FOREACH(mod, &module_list, mod_chain) {
148d91f98a8Spgoyette 		ms_cnt++;
149d91f98a8Spgoyette 		mi = mod->mod_info;
150d91f98a8Spgoyette 		if (mi->mi_required != NULL) {
151d91f98a8Spgoyette 			req_len += strlen(mi->mi_required) + 1;
152d91f98a8Spgoyette 		}
153d91f98a8Spgoyette 	}
154d91f98a8Spgoyette 	TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
155d91f98a8Spgoyette 		ms_cnt++;
156d91f98a8Spgoyette 		mi = mod->mod_info;
157d91f98a8Spgoyette 		if (mi->mi_required != NULL) {
158d91f98a8Spgoyette 			req_len += strlen(mi->mi_required) + 1;
159d91f98a8Spgoyette 		}
160d91f98a8Spgoyette 	}
161d91f98a8Spgoyette 
162d91f98a8Spgoyette 	/* Allocate internal buffers to hold all the output data */
163d91f98a8Spgoyette 	ms_len = ms_cnt * sizeof(modstat_t);
164d91f98a8Spgoyette 	ms = kmem_zalloc(ms_len, KM_SLEEP);
165d91f98a8Spgoyette 	req = kmem_zalloc(req_len, KM_SLEEP);
166d91f98a8Spgoyette 
167d91f98a8Spgoyette 	mso = ms;
168d91f98a8Spgoyette 	reqo = req++;
169d91f98a8Spgoyette 	off = 1;
170d91f98a8Spgoyette 
171d91f98a8Spgoyette 	/*
172d91f98a8Spgoyette 	 * Load data into our internal buffers for both active and
173*957a9c67Sandvar 	 * built-in module lists
174d91f98a8Spgoyette 	 */
175bbc79e58Sad 	TAILQ_FOREACH(mod, &module_list, mod_chain) {
176bbc79e58Sad 		mi = mod->mod_info;
177bbc79e58Sad 		strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name));
178bbc79e58Sad 		if (mi->mi_required != NULL) {
179d91f98a8Spgoyette 			ms->ms_reqoffset = off;
180d91f98a8Spgoyette 			used = strlcpy(req,  mi->mi_required, req_len - off);
181d91f98a8Spgoyette 			KASSERTMSG(used < req_len - off, "reqlist grew!");
182d91f98a8Spgoyette 			off += used + 1;
183d91f98a8Spgoyette 			req += used + 1;
184d91f98a8Spgoyette 		} else
185d91f98a8Spgoyette 			ms->ms_reqoffset = 0;
1869b17ef8eSmaxv 		if (mod->mod_kobj != NULL && stataddr) {
187bbc79e58Sad 			kobj_stat(mod->mod_kobj, &addr, &size);
188bbc79e58Sad 			ms->ms_addr = addr;
189bbc79e58Sad 			ms->ms_size = size;
190bbc79e58Sad 		}
191bbc79e58Sad 		ms->ms_class = mi->mi_class;
192bbc79e58Sad 		ms->ms_refcnt = mod->mod_refcnt;
193bbc79e58Sad 		ms->ms_source = mod->mod_source;
1943e202925Spgoyette 		ms->ms_flags = mod->mod_flags;
195bbc79e58Sad 		ms++;
196bbc79e58Sad 	}
197ee7bfacdSpooka 	TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
198ee7bfacdSpooka 		mi = mod->mod_info;
199ee7bfacdSpooka 		strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name));
200ee7bfacdSpooka 		if (mi->mi_required != NULL) {
201d91f98a8Spgoyette 			ms->ms_reqoffset = off;
202d91f98a8Spgoyette 			used = strlcpy(req,  mi->mi_required, req_len - off);
203d91f98a8Spgoyette 			KASSERTMSG(used < req_len - off, "reqlist grew!");
204d91f98a8Spgoyette 			off += used + 1;
205d91f98a8Spgoyette 			req += used + 1;
206d91f98a8Spgoyette 		} else
207d91f98a8Spgoyette 			ms->ms_reqoffset = 0;
2089b17ef8eSmaxv 		if (mod->mod_kobj != NULL && stataddr) {
209ee7bfacdSpooka 			kobj_stat(mod->mod_kobj, &addr, &size);
210ee7bfacdSpooka 			ms->ms_addr = addr;
211ee7bfacdSpooka 			ms->ms_size = size;
212ee7bfacdSpooka 		}
213ee7bfacdSpooka 		ms->ms_class = mi->mi_class;
214ee7bfacdSpooka 		ms->ms_refcnt = -1;
215ee7bfacdSpooka 		KASSERT(mod->mod_source == MODULE_SOURCE_KERNEL);
216ee7bfacdSpooka 		ms->ms_source = mod->mod_source;
217ee7bfacdSpooka 		ms++;
218ee7bfacdSpooka 	}
21923d5409eSpgoyette 	kernconfig_unlock();
220d91f98a8Spgoyette 
221d91f98a8Spgoyette 	/*
222d91f98a8Spgoyette 	 * Now copyout our internal buffers back to userland
223d91f98a8Spgoyette 	 */
224d91f98a8Spgoyette 	out_p = iov->iov_base;
225d91f98a8Spgoyette 	out_s = iov->iov_len;
226d91f98a8Spgoyette 	size = sizeof(ms_cnt);
227d91f98a8Spgoyette 
228d91f98a8Spgoyette 	/* Copy out the count of modstat_t */
229d91f98a8Spgoyette 	if (out_s) {
230d91f98a8Spgoyette 		size = uimin(sizeof(ms_cnt), out_s);
231d91f98a8Spgoyette 		error = copyout(&ms_cnt, out_p, size);
232d91f98a8Spgoyette 		out_p += size;
233d91f98a8Spgoyette 		out_s -= size;
234d91f98a8Spgoyette 	}
235d91f98a8Spgoyette 	/* Copy out the modstat_t array */
236d91f98a8Spgoyette 	if (out_s && error == 0) {
237d91f98a8Spgoyette 		size = uimin(ms_len, out_s);
238d91f98a8Spgoyette 		error = copyout(mso, out_p, size);
239d91f98a8Spgoyette 		out_p += size;
240d91f98a8Spgoyette 		out_s -= size;
241d91f98a8Spgoyette 	}
242d91f98a8Spgoyette 	/* Copy out the "required" strings */
243d91f98a8Spgoyette 	if (out_s && error == 0) {
244d91f98a8Spgoyette 		size = uimin(req_len, out_s);
245d91f98a8Spgoyette 		error = copyout(reqo, out_p, size);
246d91f98a8Spgoyette 		out_p += size;
247d91f98a8Spgoyette 		out_s -= size;
248d91f98a8Spgoyette 	}
249d91f98a8Spgoyette 	kmem_free(mso, ms_len);
250d91f98a8Spgoyette 	kmem_free(reqo, req_len);
251d91f98a8Spgoyette 
252d91f98a8Spgoyette 	/* Finally, update the userland copy of the iovec's length */
253bbc79e58Sad 	if (error == 0) {
254d91f98a8Spgoyette 		iov->iov_len = ms_len + req_len + sizeof(ms_cnt);
25565028d03Smaxv 		error = copyout(iov, arg, sizeof(*iov));
256bbc79e58Sad 	}
25765028d03Smaxv 
25865028d03Smaxv 	return error;
25965028d03Smaxv }
26065028d03Smaxv 
26165028d03Smaxv int
sys_modctl(struct lwp * l,const struct sys_modctl_args * uap,register_t * retval)26265028d03Smaxv sys_modctl(struct lwp *l, const struct sys_modctl_args *uap,
26365028d03Smaxv 	   register_t *retval)
26465028d03Smaxv {
26565028d03Smaxv 	/* {
26665028d03Smaxv 		syscallarg(int)		cmd;
26765028d03Smaxv 		syscallarg(void *)	arg;
26865028d03Smaxv 	} */
26965028d03Smaxv 	char buf[MAXMODNAME];
27065028d03Smaxv 	struct iovec iov;
27165028d03Smaxv 	modctl_load_t ml;
27265028d03Smaxv 	int error;
27365028d03Smaxv 	void *arg;
27465028d03Smaxv #ifdef MODULAR
27565028d03Smaxv 	uintptr_t loadtype;
27665028d03Smaxv #endif
27765028d03Smaxv 
27865028d03Smaxv 	arg = SCARG(uap, arg);
27965028d03Smaxv 
28065028d03Smaxv 	switch (SCARG(uap, cmd)) {
28165028d03Smaxv 	case MODCTL_LOAD:
28265028d03Smaxv 		error = copyin(arg, &ml, sizeof(ml));
28365028d03Smaxv 		if (error != 0)
28465028d03Smaxv 			break;
28565028d03Smaxv 		error = handle_modctl_load(ml.ml_filename, ml.ml_flags,
28665028d03Smaxv 		    ml.ml_props, ml.ml_propslen);
28765028d03Smaxv 		break;
28865028d03Smaxv 
28965028d03Smaxv 	case MODCTL_UNLOAD:
29065028d03Smaxv 		error = copyinstr(arg, buf, sizeof(buf), NULL);
29165028d03Smaxv 		if (error == 0) {
29265028d03Smaxv 			error = module_unload(buf);
29365028d03Smaxv 		}
29465028d03Smaxv 		break;
29565028d03Smaxv 
29665028d03Smaxv 	case MODCTL_STAT:
29765028d03Smaxv 		error = copyin(arg, &iov, sizeof(iov));
29865028d03Smaxv 		if (error != 0) {
29965028d03Smaxv 			break;
30065028d03Smaxv 		}
30165028d03Smaxv 		error = handle_modctl_stat(&iov, arg);
302bbc79e58Sad 		break;
303bbc79e58Sad 
304942121f5Sjnemeth 	case MODCTL_EXISTS:
305942121f5Sjnemeth #ifndef MODULAR
306942121f5Sjnemeth 		error = ENOSYS;
307942121f5Sjnemeth #else
308942121f5Sjnemeth 		loadtype = (uintptr_t)arg;
309942121f5Sjnemeth 		switch (loadtype) {	/* 0 = modload, 1 = autoload */
310942121f5Sjnemeth 		case 0:			/* FALLTHROUGH */
311942121f5Sjnemeth 		case 1:
312942121f5Sjnemeth 			error = kauth_authorize_system(kauth_cred_get(),
313942121f5Sjnemeth 			     KAUTH_SYSTEM_MODULE, 0,
314942121f5Sjnemeth 			     (void *)(uintptr_t)MODCTL_LOAD,
315942121f5Sjnemeth 			     (void *)loadtype, NULL);
316942121f5Sjnemeth 			break;
317942121f5Sjnemeth 		default:
318942121f5Sjnemeth 			error = EINVAL;
319942121f5Sjnemeth 			break;
320942121f5Sjnemeth 		}
321942121f5Sjnemeth #endif
322942121f5Sjnemeth 		break;
323942121f5Sjnemeth 
324bbc79e58Sad 	default:
3258c89d501Schristos 		(void)module_autoload("compat_80", MODULE_CLASS_EXEC);
3268c2f80f1Spgoyette 		MODULE_HOOK_CALL(compat_modstat_80_hook,
327d91f98a8Spgoyette 		    (SCARG(uap, cmd), &iov, arg), enosys(), error);
328d91f98a8Spgoyette 		if (error == ENOSYS)
329bbc79e58Sad 			error = EINVAL;
330bbc79e58Sad 		break;
331bbc79e58Sad 	}
332bbc79e58Sad 
333bbc79e58Sad 	return error;
334bbc79e58Sad }
335