1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <fuzzer/FuzzedDataProvider.h>
6 #include <stdint.h>
7
8 #include <algorithm>
9 #include <string>
10 #include <tuple>
11
12 #include "base/check.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/ranges/algorithm.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "build/build_config.h"
19
20 namespace base {
21
22 namespace {
23
GenerateNativeString(FuzzedDataProvider & provider)24 CommandLine::StringType GenerateNativeString(FuzzedDataProvider& provider) {
25 const std::string raw_string = provider.ConsumeRandomLengthString();
26 #if BUILDFLAG(IS_WIN)
27 return UTF8ToWide(raw_string);
28 #else
29 return raw_string;
30 #endif
31 }
32
GenerateNativeStringVector(FuzzedDataProvider & provider)33 CommandLine::StringVector GenerateNativeStringVector(
34 FuzzedDataProvider& provider) {
35 CommandLine::StringVector strings(
36 provider.ConsumeIntegralInRange<int>(0, 100));
37 for (auto& item : strings)
38 item = GenerateNativeString(provider);
39 return strings;
40 }
41
GenerateFilePath(FuzzedDataProvider & provider)42 FilePath GenerateFilePath(FuzzedDataProvider& provider) {
43 return FilePath(GenerateNativeString(provider));
44 }
45
IsForbiddenSwitchCharacter(char c)46 bool IsForbiddenSwitchCharacter(char c) {
47 return IsAsciiWhitespace(c) || c == '=' || c != ToLowerASCII(c);
48 }
49
IsValidSwitchName(const std::string & text)50 bool IsValidSwitchName(const std::string& text) {
51 // This duplicates the logic in command_line.cc, but it's not exposed in form
52 // of public interface.
53 return !text.empty() && !ranges::any_of(text, IsForbiddenSwitchCharacter) &&
54 !StartsWith(text, "-") && !StartsWith(text, "/");
55 }
56
57 } // namespace
58
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)59 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
60 FuzzedDataProvider provider(data, size);
61
62 // Create a randomly initialized command line.
63 CommandLine command_line(CommandLine::NO_PROGRAM);
64 switch (provider.ConsumeIntegralInRange<int>(0, 3)) {
65 case 0:
66 // Leave empty.
67 break;
68 case 1:
69 command_line = CommandLine(GenerateFilePath(provider));
70 break;
71 case 2:
72 command_line = CommandLine(GenerateNativeStringVector(provider));
73 break;
74 case 3:
75 #if BUILDFLAG(IS_WIN)
76 command_line.ParseFromString(GenerateNativeString(provider));
77 #endif
78 break;
79 }
80
81 // Do a few mutations of the command line.
82 while (provider.remaining_bytes() > 0) {
83 switch (provider.ConsumeIntegralInRange<int>(0, 4)) {
84 case 0: {
85 // Add a switch.
86 std::string name = provider.ConsumeRandomLengthString();
87 if (IsValidSwitchName(name)) {
88 CommandLine::StringType value = GenerateNativeString(provider);
89 command_line.AppendSwitchNative(name, value);
90 CHECK(command_line.HasSwitch(name));
91 CHECK(command_line.GetSwitchValueNative(name) == value);
92 }
93 break;
94 }
95 case 1: {
96 // Remove a switch.
97 std::string name = provider.ConsumeRandomLengthString();
98 if (IsValidSwitchName(name)) {
99 command_line.RemoveSwitch(name);
100 CHECK(!command_line.HasSwitch(name));
101 CHECK(command_line.GetSwitchValueNative(name).empty());
102 }
103 break;
104 }
105 case 2: {
106 // Add an argument.
107 CommandLine::StringType arg = GenerateNativeString(provider);
108 if (!arg.empty() && IsStringASCII(arg))
109 command_line.AppendArgNative(arg);
110 break;
111 }
112 case 3: {
113 // Add a wrapper.
114 CommandLine::StringType wrapper = GenerateNativeString(provider);
115 if (!wrapper.empty())
116 command_line.PrependWrapper(wrapper);
117 break;
118 }
119 case 4: {
120 // Check a switch.
121 std::string name = provider.ConsumeRandomLengthString();
122 if (IsValidSwitchName(name)) {
123 std::ignore = command_line.HasSwitch(name);
124 std::ignore = command_line.GetSwitchValueNative(name);
125 }
126 break;
127 }
128 }
129
130 // Smoke-test various accessors.
131 std::ignore = command_line.GetCommandLineString();
132 std::ignore = command_line.GetArgumentsString();
133 #if BUILDFLAG(IS_WIN)
134 std::ignore = command_line.GetCommandLineStringForShell();
135 std::ignore = command_line.GetCommandLineStringWithUnsafeInsertSequences();
136 #endif
137 }
138
139 return 0;
140 }
141
142 } // namespace base
143