blob: 531f28b7a87fdb4c7c5f84a4725603409a6f13af [file] [log] [blame]
Maksim Ivanov71181d32022-11-04 03:22:311// 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
Peter Kasting134ef9af2024-12-28 02:30:095#include "base/command_line.h"
6
Maksim Ivanov71181d32022-11-04 03:22:317#include <fuzzer/FuzzedDataProvider.h>
8#include <stdint.h>
9
10#include <algorithm>
11#include <string>
12#include <tuple>
13
14#include "base/check.h"
Maksim Ivanov71181d32022-11-04 03:22:3115#include "base/files/file_path.h"
Maksim Ivanov71181d32022-11-04 03:22:3116#include "base/strings/string_util.h"
17#include "base/strings/utf_string_conversions.h"
18#include "build/build_config.h"
19
20namespace base {
21
22namespace {
23
24CommandLine::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
33CommandLine::StringVector GenerateNativeStringVector(
34 FuzzedDataProvider& provider) {
35 CommandLine::StringVector strings(
36 provider.ConsumeIntegralInRange<int>(0, 100));
Peter Kasting134ef9af2024-12-28 02:30:0937 for (auto& item : strings) {
Maksim Ivanov71181d32022-11-04 03:22:3138 item = GenerateNativeString(provider);
Peter Kasting134ef9af2024-12-28 02:30:0939 }
Maksim Ivanov71181d32022-11-04 03:22:3140 return strings;
41}
42
43FilePath GenerateFilePath(FuzzedDataProvider& provider) {
44 return FilePath(GenerateNativeString(provider));
45}
46
47bool IsForbiddenSwitchCharacter(char c) {
48 return IsAsciiWhitespace(c) || c == '=' || c != ToLowerASCII(c);
49}
50
51bool IsValidSwitchName(const std::string& text) {
52 // This duplicates the logic in command_line.cc, but it's not exposed in form
53 // of public interface.
Peter Kasting025a94252025-01-29 21:28:3754 return !text.empty() &&
55 !std::ranges::any_of(text, IsForbiddenSwitchCharacter) &&
Maksim Ivanov71181d32022-11-04 03:22:3156 !StartsWith(text, "-") && !StartsWith(text, "/");
57}
58
59} // namespace
60
61extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
62 FuzzedDataProvider provider(data, size);
63
64 // Create a randomly initialized command line.
65 CommandLine command_line(CommandLine::NO_PROGRAM);
66 switch (provider.ConsumeIntegralInRange<int>(0, 3)) {
67 case 0:
68 // Leave empty.
69 break;
70 case 1:
71 command_line = CommandLine(GenerateFilePath(provider));
72 break;
73 case 2:
74 command_line = CommandLine(GenerateNativeStringVector(provider));
75 break;
76 case 3:
77#if BUILDFLAG(IS_WIN)
78 command_line.ParseFromString(GenerateNativeString(provider));
79#endif
80 break;
81 }
82
83 // Do a few mutations of the command line.
84 while (provider.remaining_bytes() > 0) {
85 switch (provider.ConsumeIntegralInRange<int>(0, 4)) {
86 case 0: {
87 // Add a switch.
88 std::string name = provider.ConsumeRandomLengthString();
89 if (IsValidSwitchName(name)) {
90 CommandLine::StringType value = GenerateNativeString(provider);
91 command_line.AppendSwitchNative(name, value);
92 CHECK(command_line.HasSwitch(name));
93 CHECK(command_line.GetSwitchValueNative(name) == value);
94 }
95 break;
96 }
97 case 1: {
98 // Remove a switch.
99 std::string name = provider.ConsumeRandomLengthString();
100 if (IsValidSwitchName(name)) {
101 command_line.RemoveSwitch(name);
102 CHECK(!command_line.HasSwitch(name));
103 CHECK(command_line.GetSwitchValueNative(name).empty());
104 }
105 break;
106 }
107 case 2: {
108 // Add an argument.
109 CommandLine::StringType arg = GenerateNativeString(provider);
Peter Kasting134ef9af2024-12-28 02:30:09110 if (!arg.empty() && IsStringASCII(arg)) {
Maksim Ivanov71181d32022-11-04 03:22:31111 command_line.AppendArgNative(arg);
Peter Kasting134ef9af2024-12-28 02:30:09112 }
Maksim Ivanov71181d32022-11-04 03:22:31113 break;
114 }
115 case 3: {
116 // Add a wrapper.
117 CommandLine::StringType wrapper = GenerateNativeString(provider);
Peter Kasting134ef9af2024-12-28 02:30:09118 if (!wrapper.empty()) {
Maksim Ivanov71181d32022-11-04 03:22:31119 command_line.PrependWrapper(wrapper);
Peter Kasting134ef9af2024-12-28 02:30:09120 }
Maksim Ivanov71181d32022-11-04 03:22:31121 break;
122 }
123 case 4: {
124 // Check a switch.
125 std::string name = provider.ConsumeRandomLengthString();
126 if (IsValidSwitchName(name)) {
127 std::ignore = command_line.HasSwitch(name);
128 std::ignore = command_line.GetSwitchValueNative(name);
129 }
130 break;
131 }
132 }
133
134 // Smoke-test various accessors.
135 std::ignore = command_line.GetCommandLineString();
136 std::ignore = command_line.GetArgumentsString();
137#if BUILDFLAG(IS_WIN)
138 std::ignore = command_line.GetCommandLineStringForShell();
139 std::ignore = command_line.GetCommandLineStringWithUnsafeInsertSequences();
140#endif
141 }
142
143 return 0;
144}
145
146} // namespace base