t_modctl.c revision 1.14 1 /* $NetBSD: t_modctl.c,v 1.14 2019/04/21 11:45:09 maya Exp $ */
2 /*
3 * Copyright (c) 2008 The NetBSD Foundation, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
16 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
22 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
24 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: t_modctl.c,v 1.14 2019/04/21 11:45:09 maya Exp $");
31
32 #include <sys/module.h>
33 #include <sys/sysctl.h>
34
35 #include <assert.h>
36 #include <errno.h>
37 #include <stdarg.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #include <prop/proplib.h>
44
45 #include <atf-c.h>
46
47 enum presence_check { both_checks, stat_check, sysctl_check };
48
49 static void check_permission(void);
50 static bool get_modstat_info(const char *, modstat_t *);
51 static bool get_sysctl(const char *, void *buf, const size_t);
52 static bool k_helper_is_present_stat(void);
53 static bool k_helper_is_present_sysctl(void);
54 static bool k_helper_is_present(enum presence_check);
55 static int load(prop_dictionary_t, bool, const char *, ...);
56 static int unload(const char *, bool);
57 static void unload_cleanup(const char *);
58
59 /* --------------------------------------------------------------------- */
60 /* Auxiliary functions */
61 /* --------------------------------------------------------------------- */
62
63 /*
64 * A function checking whether we are allowed to load modules currently
65 * (either the kernel is not modular, or securelevel may prevent it)
66 */
67 static void
68 check_permission(void)
69 {
70 int err;
71
72 err = modctl(MODCTL_EXISTS, 0);
73 if (err == 0) return;
74 if (errno == ENOSYS)
75 atf_tc_skip("Kernel does not have 'options MODULAR'.");
76 else if (errno == EPERM)
77 atf_tc_skip("Module loading administratively forbidden");
78 ATF_REQUIRE_EQ_MSG(errno, 0, "unexpected error %d from "
79 "modctl(MODCTL_EXISTS, 0)", errno);
80 }
81
82 static bool
83 get_modstat_info(const char *name, modstat_t *msdest)
84 {
85 bool found;
86 size_t len;
87 int count;
88 struct iovec iov;
89 modstat_t *ms;
90
91 check_permission();
92 for (len = 8192; ;) {
93 iov.iov_base = malloc(len);
94 iov.iov_len = len;
95
96 errno = 0;
97
98 if (modctl(MODCTL_STAT, &iov) != 0) {
99 int err = errno;
100 fprintf(stderr, "modctl(MODCTL_STAT) failed: %s\n",
101 strerror(err));
102 atf_tc_fail("Failed to query module status");
103 }
104 if (len >= iov.iov_len)
105 break;
106 free(iov.iov_base);
107 len = iov.iov_len;
108 }
109
110 found = false;
111 count = *(int *)iov.iov_base;
112 ms = (modstat_t *)((char *)iov.iov_base + sizeof(int));
113 while ( count ) {
114 if (strcmp(ms->ms_name, name) == 0) {
115 if (msdest != NULL)
116 *msdest = *ms;
117 found = true;
118 break;
119 }
120 ms++;
121 count--;
122 }
123
124 free(iov.iov_base);
125
126 return found;
127 }
128
129 /*
130 * Queries a sysctl property.
131 */
132 static bool
133 get_sysctl(const char *name, void *buf, const size_t len)
134 {
135 size_t len2 = len;
136 printf("Querying sysctl variable: %s\n", name);
137 int ret = sysctlbyname(name, buf, &len2, NULL, 0);
138 if (ret == -1 && errno != ENOENT) {
139 fprintf(stderr, "sysctlbyname(2) failed: %s\n",
140 strerror(errno));
141 atf_tc_fail("Failed to query %s", name);
142 }
143 return ret != -1;
144 }
145
146 /*
147 * Returns a boolean indicating if the k_helper module was loaded
148 * successfully. This implementation uses modctl(2)'s MODCTL_STAT
149 * subcommand to do the check.
150 */
151 static bool
152 k_helper_is_present_stat(void)
153 {
154
155 return get_modstat_info("k_helper", NULL);
156 }
157
158 /*
159 * Returns a boolean indicating if the k_helper module was loaded
160 * successfully. This implementation uses the module's sysctl
161 * installed node to do the check.
162 */
163 static bool
164 k_helper_is_present_sysctl(void)
165 {
166 size_t present;
167
168 return get_sysctl("vendor.k_helper.present", &present,
169 sizeof(present));
170 }
171
172 /*
173 * Returns a boolean indicating if the k_helper module was loaded
174 * successfully. The 'how' parameter specifies the implementation to
175 * use to do the check.
176 */
177 static bool
178 k_helper_is_present(enum presence_check how)
179 {
180 bool found;
181
182 switch (how) {
183 case both_checks:
184 found = k_helper_is_present_stat();
185 ATF_CHECK(k_helper_is_present_sysctl() == found);
186 break;
187
188 case stat_check:
189 found = k_helper_is_present_stat();
190 break;
191
192 case sysctl_check:
193 found = k_helper_is_present_sysctl();
194 break;
195
196 default:
197 found = false;
198 assert(found);
199 }
200
201 return found;
202 }
203
204 /*
205 * Loads the specified module from a file. If fatal is set and an error
206 * occurs when loading the module, an error message is printed and the
207 * test case is aborted.
208 */
209 static __printflike(3, 4) int
210 load(prop_dictionary_t props, bool fatal, const char *fmt, ...)
211 {
212 int err;
213 va_list ap;
214 char filename[MAXPATHLEN], *propsstr;
215 modctl_load_t ml;
216
217 check_permission();
218 if (props == NULL) {
219 props = prop_dictionary_create();
220 propsstr = prop_dictionary_externalize(props);
221 ATF_CHECK(propsstr != NULL);
222 prop_object_release(props);
223 } else {
224 propsstr = prop_dictionary_externalize(props);
225 ATF_CHECK(propsstr != NULL);
226 }
227
228 va_start(ap, fmt);
229 vsnprintf(filename, sizeof(filename), fmt, ap);
230 va_end(ap);
231
232 ml.ml_filename = filename;
233 ml.ml_flags = 0;
234 ml.ml_props = propsstr;
235 ml.ml_propslen = strlen(propsstr);
236
237 printf("Loading module %s\n", filename);
238 errno = err = 0;
239
240 if (modctl(MODCTL_LOAD, &ml) == -1) {
241 err = errno;
242 fprintf(stderr, "modctl(MODCTL_LOAD, %s), failed: %s\n",
243 filename, strerror(err));
244 if (fatal)
245 atf_tc_fail("Module load failed");
246 }
247
248 free(propsstr);
249
250 return err;
251 }
252
253 /*
254 * Unloads the specified module. If silent is true, nothing will be
255 * printed and no errors will be raised if the unload was unsuccessful.
256 */
257 static int
258 unload(const char *name, bool fatal)
259 {
260 int err;
261
262 check_permission();
263 printf("Unloading module %s\n", name);
264 errno = err = 0;
265
266 if (modctl(MODCTL_UNLOAD, __UNCONST(name)) == -1) {
267 err = errno;
268 fprintf(stderr, "modctl(MODCTL_UNLOAD, %s) failed: %s\n",
269 name, strerror(err));
270 if (fatal)
271 atf_tc_fail("Module unload failed");
272 }
273 return err;
274 }
275
276 /*
277 * A silent version of unload, to be called as part of the cleanup
278 * process only.
279 */
280 static void
281 unload_cleanup(const char *name)
282 {
283
284 (void)modctl(MODCTL_UNLOAD, __UNCONST(name));
285 }
286
287 /* --------------------------------------------------------------------- */
288 /* Test cases */
289 /* --------------------------------------------------------------------- */
290
291 ATF_TC_WITH_CLEANUP(cmd_load);
292 ATF_TC_HEAD(cmd_load, tc)
293 {
294 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command");
295 atf_tc_set_md_var(tc, "require.user", "root");
296 }
297 ATF_TC_BODY(cmd_load, tc)
298 {
299 char longname[MAXPATHLEN];
300 size_t i;
301
302 ATF_CHECK(load(NULL, false, " ") == ENOENT);
303 ATF_CHECK(load(NULL, false, "non-existent.o") == ENOENT);
304
305 for (i = 0; i < MAXPATHLEN - 1; i++)
306 longname[i] = 'a';
307 longname[MAXPATHLEN - 1] = '\0';
308 ATF_CHECK(load(NULL, false, "%s", longname) == ENAMETOOLONG);
309
310 ATF_CHECK(!k_helper_is_present(stat_check));
311 load(NULL, true, "%s/k_helper/k_helper.kmod",
312 atf_tc_get_config_var(tc, "srcdir"));
313 printf("Checking if load was successful\n");
314 ATF_CHECK(k_helper_is_present(stat_check));
315 }
316 ATF_TC_CLEANUP(cmd_load, tc)
317 {
318 unload_cleanup("k_helper");
319 }
320
321 ATF_TC_WITH_CLEANUP(cmd_load_props);
322 ATF_TC_HEAD(cmd_load_props, tc)
323 {
324 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command, "
325 "providing extra load-time properties");
326 atf_tc_set_md_var(tc, "require.user", "root");
327 }
328 ATF_TC_BODY(cmd_load_props, tc)
329 {
330 prop_dictionary_t props;
331
332 printf("Loading module without properties\n");
333 props = prop_dictionary_create();
334 load(props, true, "%s/k_helper/k_helper.kmod",
335 atf_tc_get_config_var(tc, "srcdir"));
336 prop_object_release(props);
337 {
338 int ok;
339 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok",
340 &ok, sizeof(ok)));
341 ATF_CHECK(!ok);
342 }
343 unload("k_helper", true);
344
345 printf("Loading module with a string property\n");
346 props = prop_dictionary_create();
347 prop_dictionary_set(props, "prop_str",
348 prop_string_create_cstring("1st string"));
349 load(props, true, "%s/k_helper/k_helper.kmod",
350 atf_tc_get_config_var(tc, "srcdir"));
351 prop_object_release(props);
352 {
353 int ok;
354 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok",
355 &ok, sizeof(ok)));
356 ATF_CHECK(ok);
357
358 char val[128];
359 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val",
360 &val, sizeof(val)));
361 ATF_CHECK(strcmp(val, "1st string") == 0);
362 }
363 unload("k_helper", true);
364
365 printf("Loading module with a different string property\n");
366 props = prop_dictionary_create();
367 prop_dictionary_set(props, "prop_str",
368 prop_string_create_cstring("2nd string"));
369 load(props, true, "%s/k_helper/k_helper.kmod",
370 atf_tc_get_config_var(tc, "srcdir"));
371 prop_object_release(props);
372 {
373 int ok;
374 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_ok",
375 &ok, sizeof(ok)));
376 ATF_CHECK(ok);
377
378 char val[128];
379 ATF_CHECK(get_sysctl("vendor.k_helper.prop_str_val",
380 &val, sizeof(val)));
381 ATF_CHECK(strcmp(val, "2nd string") == 0);
382 }
383 unload("k_helper", true);
384 }
385 ATF_TC_CLEANUP(cmd_load_props, tc)
386 {
387 unload_cleanup("k_helper");
388 }
389
390 ATF_TC_WITH_CLEANUP(cmd_load_recurse);
391 ATF_TC_HEAD(cmd_load_recurse, tc)
392 {
393 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_LOAD command, "
394 "with recursive module_load()");
395 atf_tc_set_md_var(tc, "require.user", "root");
396 }
397 ATF_TC_BODY(cmd_load_recurse, tc)
398 {
399 prop_dictionary_t props;
400 char filename[MAXPATHLEN];
401
402 printf("Loading module with request to load another module\n");
403 props = prop_dictionary_create();
404 snprintf(filename, sizeof(filename), "%s/k_helper2/k_helper2.kmod",
405 atf_tc_get_config_var(tc, "srcdir"));
406 prop_dictionary_set(props, "prop_recurse",
407 prop_string_create_cstring(filename));
408 load(props, true, "%s/k_helper/k_helper.kmod",
409 atf_tc_get_config_var(tc, "srcdir"));
410 {
411 int ok;
412 ATF_CHECK(get_sysctl("vendor.k_helper.prop_int_load",
413 &ok, sizeof(ok)));
414 ATF_CHECK(ok == 0);
415 ATF_CHECK(get_sysctl("vendor.k_helper2.present",
416 &ok, sizeof(ok)));
417 ATF_CHECK(ok);
418 }
419 unload("k_helper", true);
420 unload("k_helper2", true);
421 }
422 ATF_TC_CLEANUP(cmd_load_recurse, tc)
423 {
424 unload_cleanup("k_helper");
425 unload_cleanup("k_helper2");
426 }
427
428 ATF_TC_WITH_CLEANUP(cmd_stat);
429 ATF_TC_HEAD(cmd_stat, tc)
430 {
431 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_STAT command");
432 atf_tc_set_md_var(tc, "require.user", "root");
433 }
434 ATF_TC_BODY(cmd_stat, tc)
435 {
436 ATF_CHECK(!k_helper_is_present(both_checks));
437
438 load(NULL, true, "%s/k_helper/k_helper.kmod",
439 atf_tc_get_config_var(tc, "srcdir"));
440 ATF_CHECK(k_helper_is_present(both_checks));
441 {
442 modstat_t ms;
443 ATF_CHECK(get_modstat_info("k_helper", &ms));
444
445 ATF_CHECK(ms.ms_class == MODULE_CLASS_MISC);
446 ATF_CHECK(ms.ms_source == MODULE_SOURCE_FILESYS);
447 ATF_CHECK(ms.ms_refcnt == 0);
448 }
449 unload("k_helper", true);
450
451 ATF_CHECK(!k_helper_is_present(both_checks));
452 }
453 ATF_TC_CLEANUP(cmd_stat, tc)
454 {
455 unload_cleanup("k_helper");
456 }
457
458 ATF_TC_WITH_CLEANUP(cmd_unload);
459 ATF_TC_HEAD(cmd_unload, tc)
460 {
461 atf_tc_set_md_var(tc, "descr", "Tests for the MODCTL_UNLOAD command");
462 atf_tc_set_md_var(tc, "require.user", "root");
463 }
464 ATF_TC_BODY(cmd_unload, tc)
465 {
466 load(NULL, true, "%s/k_helper/k_helper.kmod",
467 atf_tc_get_config_var(tc, "srcdir"));
468
469 ATF_CHECK(unload("", false) == ENOENT);
470 ATF_CHECK(unload("non-existent.kmod", false) == ENOENT);
471 ATF_CHECK(unload("k_helper.kmod", false) == ENOENT);
472
473 ATF_CHECK(k_helper_is_present(stat_check));
474 unload("k_helper", true);
475 printf("Checking if unload was successful\n");
476 ATF_CHECK(!k_helper_is_present(stat_check));
477 }
478 ATF_TC_CLEANUP(cmd_unload, tc)
479 {
480 unload_cleanup("k_helper");
481 }
482
483 /* --------------------------------------------------------------------- */
484 /* Main */
485 /* --------------------------------------------------------------------- */
486
487 ATF_TP_ADD_TCS(tp)
488 {
489
490 ATF_TP_ADD_TC(tp, cmd_load);
491 ATF_TP_ADD_TC(tp, cmd_load_props);
492 ATF_TP_ADD_TC(tp, cmd_stat);
493 ATF_TP_ADD_TC(tp, cmd_load_recurse);
494 ATF_TP_ADD_TC(tp, cmd_unload);
495
496 return atf_no_error();
497 }
498