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