]>
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" | |
25de7aa7 | 17 | #include "udev-rules.h" |
2e646cbe | 18 | |
fa6e5861 | 19 | static struct fakefs { |
2e646cbe EV |
20 | const char *target; |
21 | bool ignore_mount_error; | |
fa6e5861 | 22 | bool is_mounted; |
2e646cbe | 23 | } fakefss[] = { |
fa6e5861 EV |
24 | { "/sys", false, false }, |
25 | { "/dev", false, false }, | |
26 | { "/run", false, false }, | |
27 | { "/etc", false, false }, | |
28 | { UDEVLIBEXECDIR "/rules.d", true, false }, | |
2e646cbe EV |
29 | }; |
30 | ||
31 | static int setup_mount_namespace(void) { | |
32 | static thread_local bool is_namespaced = false; | |
33 | ||
34 | if (is_namespaced) | |
35 | return 1; | |
36 | ||
37 | if (unshare(CLONE_NEWNS) < 0) | |
38 | return log_error_errno(errno, "Failed to call unshare(): %m"); | |
39 | ||
40 | if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) | |
41 | return log_error_errno(errno, "Failed to mount / as private: %m"); | |
42 | ||
43 | is_namespaced = true; | |
44 | ||
45 | return 1; | |
46 | } | |
47 | ||
48 | static int setup_fake_filesystems(const char *runtime_dir) { | |
fa6e5861 | 49 | for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) { |
2e646cbe EV |
50 | if (mount(runtime_dir, fakefss[i].target, NULL, MS_BIND, NULL) < 0) { |
51 | log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to mount %s: %m", fakefss[i].target); | |
52 | if (!fakefss[i].ignore_mount_error) | |
53 | return -errno; | |
fa6e5861 EV |
54 | } else |
55 | fakefss[i].is_mounted = true; | |
56 | } | |
2e646cbe EV |
57 | |
58 | return 0; | |
59 | } | |
60 | ||
61 | static int cleanup_fake_filesystems(const char *runtime_dir) { | |
fa6e5861 EV |
62 | for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) { |
63 | if (!fakefss[i].is_mounted) | |
64 | continue; | |
65 | ||
2e646cbe EV |
66 | if (umount(fakefss[i].target) < 0) { |
67 | log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to umount %s: %m", fakefss[i].target); | |
68 | if (!fakefss[i].ignore_mount_error) | |
69 | return -errno; | |
fa6e5861 EV |
70 | } else |
71 | fakefss[i].is_mounted = false; | |
72 | } | |
2e646cbe EV |
73 | return 0; |
74 | } | |
75 | ||
76 | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | |
9a07157d | 77 | _cleanup_(udev_rules_freep) UdevRules *rules = NULL; |
2e646cbe EV |
78 | _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; |
79 | FILE *f = NULL; | |
80 | ||
8e96f161 EV |
81 | (void) setup_mount_namespace(); |
82 | ||
83 | assert_se(runtime_dir = setup_fake_runtime_dir()); | |
84 | ||
85 | if (setup_fake_filesystems(runtime_dir) < 0) { | |
86 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION | |
87 | return EXIT_TEST_SKIP; | |
8e96f161 EV |
88 | #endif |
89 | } | |
90 | ||
2e646cbe EV |
91 | if (!getenv("SYSTEMD_LOG_LEVEL")) { |
92 | log_set_max_level_realm(LOG_REALM_UDEV, LOG_CRIT); | |
93 | log_set_max_level_realm(LOG_REALM_SYSTEMD, LOG_CRIT); | |
94 | } | |
95 | ||
2e646cbe EV |
96 | assert_se(mkdir_p("/etc/udev/rules.d", 0755) >= 0); |
97 | f = fopen("/etc/udev/rules.d/fuzz.rules", "we"); | |
98 | assert_se(f); | |
99 | if (size != 0) | |
100 | assert_se(fwrite(data, size, 1, f) == 1); | |
101 | assert_se(fclose(f) == 0); | |
2e646cbe | 102 | |
1d791281 | 103 | assert_se(udev_rules_new(&rules, RESOLVE_NAME_EARLY) == 0); |
2e646cbe | 104 | |
1d791281 | 105 | assert_se(cleanup_fake_filesystems(runtime_dir) >= 0); |
2e646cbe EV |
106 | return 0; |
107 | } |