]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fuzz/fuzz-udev-rules.c
udev: modernize udev-rules.c
[thirdparty/systemd.git] / src / fuzz / fuzz-udev-rules.c
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-rules.h"
18
19 static struct fakefs {
20 const char *target;
21 bool ignore_mount_error;
22 bool is_mounted;
23 } fakefss[] = {
24 { "/sys", false, false },
25 { "/dev", false, false },
26 { "/run", false, false },
27 { "/etc", false, false },
28 { UDEVLIBEXECDIR "/rules.d", true, false },
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) {
49 for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) {
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;
54 } else
55 fakefss[i].is_mounted = true;
56 }
57
58 return 0;
59 }
60
61 static int cleanup_fake_filesystems(const char *runtime_dir) {
62 for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) {
63 if (!fakefss[i].is_mounted)
64 continue;
65
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;
70 } else
71 fakefss[i].is_mounted = false;
72 }
73 return 0;
74 }
75
76 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
77 _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
78 _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
79 FILE *f = NULL;
80
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;
88 #endif
89 }
90
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
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);
102
103 assert_se(udev_rules_new(&rules, RESOLVE_NAME_EARLY) == 0);
104
105 assert_se(cleanup_fake_filesystems(runtime_dir) >= 0);
106 return 0;
107 }