FreeBSD/Linux Kernel Cross Reference
sys/kern/sys_module.c
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
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
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
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 }
Cache object: c2b050d0d29043e0fbdd5a988fd769fa
|