diff --git a/src/systemctl/fuzz-systemctl-parse-argv.c b/src/systemctl/fuzz-systemctl-parse-argv.c new file mode 100644 index 00000000000..f884917e17f --- /dev/null +++ b/src/systemctl/fuzz-systemctl-parse-argv.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "env-util.h" +#include "fd-util.h" +#include "fuzz.h" +#include "stdio-util.h" +#include "strv.h" +#include "systemctl.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_strv_free_ char **argv = NULL; + _cleanup_close_ int orig_stdout_fd = -1; + int r; + + /* We don't want to fill the logs with messages about parse errors. + * Disable most logging if not running standalone */ + if (!getenv("SYSTEMD_LOG_LEVEL")) + log_set_max_level(LOG_CRIT); + + arg_pager_flags = PAGER_DISABLE; /* We shouldn't execute the pager */ + + argv = strv_parse_nulstr((const char *)data, size); + if (!argv) + return log_oom(); + + if (!argv[0]) + return 0; /* argv[0] should always be present, but may be zero-length. */ + + if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0) { + orig_stdout_fd = fcntl(fileno(stdout), F_DUPFD_CLOEXEC, 3); + if (orig_stdout_fd < 0) + log_warning_errno(orig_stdout_fd, "Failed to duplicate fd 1: %m"); + else + assert_se(freopen("/dev/null", "w", stdout)); + + opterr = 0; /* do not print errors */ + } + + optind = 0; /* this tells the getopt machinery to reinitialize */ + + r = systemctl_dispatch_parse_argv(strv_length(argv), argv); + if (r < 0) + log_error_errno(r, "Failed to parse args: %m"); + else + log_info(r == 0 ? "Done!" : "Action!"); + + if (orig_stdout_fd >= 0) { + char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + + xsprintf(path, "/proc/self/fd/%d", orig_stdout_fd); + assert_se(freopen(path, "w", stdout)); + } + + return 0; +} diff --git a/src/systemctl/meson.build b/src/systemctl/meson.build index a6e254a14f6..38bf33d49a7 100644 --- a/src/systemctl/meson.build +++ b/src/systemctl/meson.build @@ -81,3 +81,9 @@ else libshared_static, libbasic_gcrypt] endif + +fuzzers += [ + [['src/systemctl/fuzz-systemctl-parse-argv.c', + systemctl_sources], + systemctl_link_with, + [], [], ['-DFUZZ_SYSTEMCTL_PARSE_ARGV']]] diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index cb901881c96..8c89646a777 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -926,7 +926,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return 1; } -static int parse_argv(int argc, char *argv[]) { +int systemctl_dispatch_parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); @@ -987,6 +987,7 @@ static int parse_argv(int argc, char *argv[]) { return systemctl_parse_argv(argc, argv); } +#ifndef FUZZ_SYSTEMCTL_PARSE_ARGV static int systemctl_main(int argc, char *argv[]) { static const Verb verbs[] = { { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, list_units }, @@ -1090,7 +1091,7 @@ static int run(int argc, char *argv[]) { sigbus_install(); - r = parse_argv(argc, argv); + r = systemctl_dispatch_parse_argv(argc, argv); if (r <= 0) goto finish; @@ -1167,3 +1168,4 @@ finish: } DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); +#endif diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h index f7ee358ce12..722853db2a6 100644 --- a/src/systemctl/systemctl.h +++ b/src/systemctl/systemctl.h @@ -93,3 +93,5 @@ extern char **arg_clean_what; extern TimestampStyle arg_timestamp_style; extern bool arg_read_only; extern bool arg_mkdir; + +int systemctl_dispatch_parse_argv(int argc, char *argv[]); diff --git a/test/fuzz/fuzz-systemctl-parse-argv/help.input b/test/fuzz/fuzz-systemctl-parse-argv/help.input new file mode 100644 index 00000000000..95626348b06 Binary files /dev/null and b/test/fuzz/fuzz-systemctl-parse-argv/help.input differ