]>
Commit | Line | Data |
---|---|---|
2e646cbe EV |
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | ||
3 | #include <errno.h> | |
4 | #include <sched.h> | |
5 | #include <sys/mount.h> | |
6 | #include <unistd.h> | |
7 | ||
8 | #include "fd-util.h" | |
9 | #include "fs-util.h" | |
10 | #include "fuzz.h" | |
11 | #include "log.h" | |
12 | #include "mkdir.h" | |
13 | #include "missing.h" | |
14 | #include "rm-rf.h" | |
15 | #include "string-util.h" | |
16 | #include "tests.h" | |
17 | #include "udev.h" | |
18 | ||
19 | static const struct fakefs { | |
20 | const char *target; | |
21 | bool ignore_mount_error; | |
22 | } fakefss[] = { | |
23 | { "/sys", false }, | |
24 | { "/dev", false }, | |
25 | { "/run", false }, | |
26 | { "/etc", false }, | |
27 | { UDEVLIBEXECDIR "/rules.d", true }, | |
28 | }; | |
29 | ||
30 | static int setup_mount_namespace(void) { | |
31 | static thread_local bool is_namespaced = false; | |
32 | ||
33 | if (is_namespaced) | |
34 | return 1; | |
35 | ||
36 | if (unshare(CLONE_NEWNS) < 0) | |
37 | return log_error_errno(errno, "Failed to call unshare(): %m"); | |
38 | ||
39 | if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) | |
40 | return log_error_errno(errno, "Failed to mount / as private: %m"); | |
41 | ||
42 | is_namespaced = true; | |
43 | ||
44 | return 1; | |
45 | } | |
46 | ||
47 | static int setup_fake_filesystems(const char *runtime_dir) { | |
48 | for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) | |
49 | if (mount(runtime_dir, fakefss[i].target, NULL, MS_BIND, NULL) < 0) { | |
50 | log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to mount %s: %m", fakefss[i].target); | |
51 | if (!fakefss[i].ignore_mount_error) | |
52 | return -errno; | |
53 | } | |
54 | ||
55 | return 0; | |
56 | } | |
57 | ||
58 | static int cleanup_fake_filesystems(const char *runtime_dir) { | |
59 | for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) | |
60 | if (umount(fakefss[i].target) < 0) { | |
61 | log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to umount %s: %m", fakefss[i].target); | |
62 | if (!fakefss[i].ignore_mount_error) | |
63 | return -errno; | |
64 | } | |
65 | return 0; | |
66 | } | |
67 | ||
68 | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | |
69 | _cleanup_(udev_rules_freep) struct udev_rules *rules = NULL; | |
70 | _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; | |
71 | FILE *f = NULL; | |
72 | ||
73 | if (!getenv("SYSTEMD_LOG_LEVEL")) { | |
74 | log_set_max_level_realm(LOG_REALM_UDEV, LOG_CRIT); | |
75 | log_set_max_level_realm(LOG_REALM_SYSTEMD, LOG_CRIT); | |
76 | } | |
77 | ||
78 | if (setup_mount_namespace() < 0) | |
79 | return EXIT_TEST_SKIP; | |
80 | ||
81 | assert_se(runtime_dir = setup_fake_runtime_dir()); | |
82 | ||
83 | assert_se(setup_fake_filesystems(runtime_dir) >= 0); | |
84 | assert_se(mkdir_p("/etc/udev/rules.d", 0755) >= 0); | |
85 | f = fopen("/etc/udev/rules.d/fuzz.rules", "we"); | |
86 | assert_se(f); | |
87 | if (size != 0) | |
88 | assert_se(fwrite(data, size, 1, f) == 1); | |
89 | assert_se(fclose(f) == 0); | |
90 | rules = udev_rules_new(RESOLVE_NAME_EARLY); | |
91 | ||
92 | assert_se(cleanup_fake_filesystems(runtime_dir) >= 0); | |
93 | ||
94 | return 0; | |
95 | } |