1 | /* $NetBSD: sys_module.c,v 1.21 2015/12/12 14:47:37 maxv 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.21 2015/12/12 14:47:37 maxv 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 | |
51 | /* |
52 | * Arbitrary limit to avoid DoS for excessive memory allocation. |
53 | */ |
54 | #define MAXPROPSLEN 4096 |
55 | |
56 | int |
57 | handle_modctl_load(const char *ml_filename, int ml_flags, const char *ml_props, |
58 | size_t ml_propslen) |
59 | { |
60 | char *path; |
61 | char *props; |
62 | int error; |
63 | prop_dictionary_t dict; |
64 | size_t propslen = 0; |
65 | |
66 | if ((ml_props != NULL && ml_propslen == 0) || |
67 | (ml_props == NULL && ml_propslen > 0)) { |
68 | return EINVAL; |
69 | } |
70 | |
71 | path = PNBUF_GET(); |
72 | error = copyinstr(ml_filename, path, MAXPATHLEN, NULL); |
73 | if (error != 0) |
74 | goto out1; |
75 | |
76 | if (ml_props != NULL) { |
77 | if (ml_propslen > MAXPROPSLEN) { |
78 | error = ENOMEM; |
79 | goto out1; |
80 | } |
81 | propslen = ml_propslen + 1; |
82 | |
83 | props = kmem_alloc(propslen, KM_SLEEP); |
84 | if (props == NULL) { |
85 | error = ENOMEM; |
86 | goto out1; |
87 | } |
88 | |
89 | error = copyinstr(ml_props, props, propslen, NULL); |
90 | if (error != 0) |
91 | goto out2; |
92 | |
93 | dict = prop_dictionary_internalize(props); |
94 | if (dict == NULL) { |
95 | error = EINVAL; |
96 | goto out2; |
97 | } |
98 | } else { |
99 | dict = NULL; |
100 | props = NULL; |
101 | } |
102 | |
103 | error = module_load(path, ml_flags, dict, MODULE_CLASS_ANY); |
104 | |
105 | if (dict != NULL) { |
106 | prop_object_release(dict); |
107 | } |
108 | |
109 | out2: |
110 | if (props != NULL) { |
111 | kmem_free(props, propslen); |
112 | } |
113 | out1: |
114 | PNBUF_PUT(path); |
115 | return error; |
116 | } |
117 | |
118 | static int |
119 | handle_modctl_stat(struct iovec *iov, void *arg) |
120 | { |
121 | modstat_t *ms, *mso; |
122 | modinfo_t *mi; |
123 | module_t *mod; |
124 | vaddr_t addr; |
125 | size_t size; |
126 | size_t mslen; |
127 | int error; |
128 | |
129 | kernconfig_lock(); |
130 | mslen = (module_count+module_builtinlist+1) * sizeof(modstat_t); |
131 | mso = kmem_zalloc(mslen, KM_SLEEP); |
132 | if (mso == NULL) { |
133 | kernconfig_unlock(); |
134 | return ENOMEM; |
135 | } |
136 | ms = mso; |
137 | TAILQ_FOREACH(mod, &module_list, mod_chain) { |
138 | mi = mod->mod_info; |
139 | strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name)); |
140 | if (mi->mi_required != NULL) { |
141 | strlcpy(ms->ms_required, mi->mi_required, |
142 | sizeof(ms->ms_required)); |
143 | } |
144 | if (mod->mod_kobj != NULL) { |
145 | kobj_stat(mod->mod_kobj, &addr, &size); |
146 | ms->ms_addr = addr; |
147 | ms->ms_size = size; |
148 | } |
149 | ms->ms_class = mi->mi_class; |
150 | ms->ms_refcnt = mod->mod_refcnt; |
151 | ms->ms_source = mod->mod_source; |
152 | ms->ms_flags = mod->mod_flags; |
153 | ms++; |
154 | } |
155 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { |
156 | mi = mod->mod_info; |
157 | strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name)); |
158 | if (mi->mi_required != NULL) { |
159 | strlcpy(ms->ms_required, mi->mi_required, |
160 | sizeof(ms->ms_required)); |
161 | } |
162 | if (mod->mod_kobj != NULL) { |
163 | kobj_stat(mod->mod_kobj, &addr, &size); |
164 | ms->ms_addr = addr; |
165 | ms->ms_size = size; |
166 | } |
167 | ms->ms_class = mi->mi_class; |
168 | ms->ms_refcnt = -1; |
169 | KASSERT(mod->mod_source == MODULE_SOURCE_KERNEL); |
170 | ms->ms_source = mod->mod_source; |
171 | ms++; |
172 | } |
173 | kernconfig_unlock(); |
174 | error = copyout(mso, iov->iov_base, |
175 | min(mslen - sizeof(modstat_t), iov->iov_len)); |
176 | kmem_free(mso, mslen); |
177 | if (error == 0) { |
178 | iov->iov_len = mslen - sizeof(modstat_t); |
179 | error = copyout(iov, arg, sizeof(*iov)); |
180 | } |
181 | |
182 | return error; |
183 | } |
184 | |
185 | int |
186 | sys_modctl(struct lwp *l, const struct sys_modctl_args *uap, |
187 | register_t *retval) |
188 | { |
189 | /* { |
190 | syscallarg(int) cmd; |
191 | syscallarg(void *) arg; |
192 | } */ |
193 | char buf[MAXMODNAME]; |
194 | struct iovec iov; |
195 | modctl_load_t ml; |
196 | int error; |
197 | void *arg; |
198 | #ifdef MODULAR |
199 | uintptr_t loadtype; |
200 | #endif |
201 | |
202 | arg = SCARG(uap, arg); |
203 | |
204 | switch (SCARG(uap, cmd)) { |
205 | case MODCTL_LOAD: |
206 | error = copyin(arg, &ml, sizeof(ml)); |
207 | if (error != 0) |
208 | break; |
209 | error = handle_modctl_load(ml.ml_filename, ml.ml_flags, |
210 | ml.ml_props, ml.ml_propslen); |
211 | break; |
212 | |
213 | case MODCTL_UNLOAD: |
214 | error = copyinstr(arg, buf, sizeof(buf), NULL); |
215 | if (error == 0) { |
216 | error = module_unload(buf); |
217 | } |
218 | break; |
219 | |
220 | case MODCTL_STAT: |
221 | error = copyin(arg, &iov, sizeof(iov)); |
222 | if (error != 0) { |
223 | break; |
224 | } |
225 | error = handle_modctl_stat(&iov, arg); |
226 | break; |
227 | |
228 | case MODCTL_EXISTS: |
229 | #ifndef MODULAR |
230 | error = ENOSYS; |
231 | #else |
232 | loadtype = (uintptr_t)arg; |
233 | switch (loadtype) { /* 0 = modload, 1 = autoload */ |
234 | case 0: /* FALLTHROUGH */ |
235 | case 1: |
236 | error = kauth_authorize_system(kauth_cred_get(), |
237 | KAUTH_SYSTEM_MODULE, 0, |
238 | (void *)(uintptr_t)MODCTL_LOAD, |
239 | (void *)loadtype, NULL); |
240 | break; |
241 | |
242 | default: |
243 | error = EINVAL; |
244 | break; |
245 | } |
246 | #endif |
247 | break; |
248 | |
249 | default: |
250 | error = EINVAL; |
251 | break; |
252 | } |
253 | |
254 | return error; |
255 | } |
256 | |