1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <string.h>
4 
5 #include "bootspec.h"
6 #include "env-util.h"
7 #include "escape.h"
8 #include "fuzz.h"
9 #include "fd-util.h"
10 #include "json.h"
11 
json_dispatch_config(const char * name,JsonVariant * variant,JsonDispatchFlags flags,void * userdata)12 static int json_dispatch_config(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
13         BootConfig *config = ASSERT_PTR(userdata);
14 
15         const char *s = json_variant_string(variant);
16         if (!s)
17                 return -EINVAL;
18 
19         _cleanup_fclose_ FILE *f = NULL;
20         assert_se(f = data_to_file((const uint8_t*) s, strlen(s)));
21 
22         (void) boot_loader_read_conf(config, f, "memstream");
23         return 0;
24 }
25 
json_dispatch_entries(const char * name,JsonVariant * variant,JsonDispatchFlags flags,void * userdata)26 static int json_dispatch_entries(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
27         BootConfig *config = ASSERT_PTR(userdata);
28         JsonVariant *entry;
29 
30         JSON_VARIANT_ARRAY_FOREACH(entry, variant) {
31                 if (!json_variant_is_array(entry) ||
32                     json_variant_elements(entry) < 1)
33                         return -EINVAL;
34 
35                 JsonVariant *v;
36                 const char *id = NULL, *raw = NULL;
37                 _cleanup_free_ char *data = NULL;
38                 ssize_t len = -ENODATA;
39 
40                 v = json_variant_by_index(entry, 0);
41                 if (v)
42                         id = json_variant_string(v);
43                 if (!id)
44                         continue;
45 
46                 v = json_variant_by_index(entry, 1);
47                 if (v)
48                         raw = json_variant_string(v);
49                 if (raw)
50                         len = cunescape(raw, UNESCAPE_RELAX | UNESCAPE_ACCEPT_NUL, &data);
51                 if (len >= 0) {
52                         _cleanup_fclose_ FILE *f = NULL;
53                         assert_se(f = data_to_file((const uint8_t*) data, len));
54 
55                         assert_se(boot_config_load_type1(config, f, "/", "/entries", id) != -ENOMEM);
56                 }
57         }
58 
59         return 0;
60 }
61 
json_dispatch_loader(const char * name,JsonVariant * variant,JsonDispatchFlags flags,void * userdata)62 static int json_dispatch_loader(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
63         BootConfig *config = ASSERT_PTR(userdata);
64         _cleanup_strv_free_ char **entries = NULL;
65         int r;
66 
67         r = json_dispatch_strv(name, variant, flags, &entries);
68         if (r < 0)
69                 return r;
70 
71         (void) boot_config_augment_from_loader(config, entries, false);
72         return 0;
73 }
74 
75 static const JsonDispatch data_dispatch[] = {
76         { "config",  JSON_VARIANT_STRING, json_dispatch_config,  0, 0 },
77         { "entries", JSON_VARIANT_ARRAY,  json_dispatch_entries, 0, 0 },
78         { "loader",  JSON_VARIANT_ARRAY,  json_dispatch_loader,  0, 0 },
79         {}
80 };
81 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)82 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
83         _cleanup_free_ const char *datadup = NULL;
84         _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
85         int r;
86 
87         if (outside_size_range(size, 0, 65536))
88                 return 0;
89 
90         /* Disable most logging if not running standalone */
91         if (!getenv("SYSTEMD_LOG_LEVEL"))
92                 log_set_max_level(LOG_CRIT);
93 
94         assert_se(datadup = memdup_suffix0(data, size));
95 
96         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
97         r = json_parse(datadup, 0, &v, NULL, NULL);
98         if (r < 0)
99                 return 0;
100 
101         r = json_dispatch(v, data_dispatch, NULL, 0, &config);
102         if (r < 0)
103                 return 0;
104 
105         assert_se(boot_config_finalize(&config) >= 0);
106 
107         (void) boot_config_select_special_entries(&config);
108 
109         _cleanup_close_ int orig_stdout_fd = -1;
110         if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0) {
111                 orig_stdout_fd = fcntl(fileno(stdout), F_DUPFD_CLOEXEC, 3);
112                 if (orig_stdout_fd < 0)
113                         log_warning_errno(orig_stdout_fd, "Failed to duplicate fd 1: %m");
114                 else
115                         assert_se(freopen("/dev/null", "w", stdout));
116         }
117 
118         (void) show_boot_entries(&config, JSON_FORMAT_OFF);
119         (void) show_boot_entries(&config, JSON_FORMAT_PRETTY);
120 
121         if (orig_stdout_fd >= 0)
122                 assert_se(freopen(FORMAT_PROC_FD_PATH(orig_stdout_fd), "w", stdout));
123 
124         return 0;
125 }
126