xref: /netbsd-src/tests/modules/t_modctl.c (revision 404ee5b9334f618040b6cdef96a0ff35a6fc4636)
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