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