| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuzzer/FuzzedDataProvider.h> |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <string> |
| #include <tuple> |
| |
| #include "base/check.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| |
| namespace base { |
| |
| namespace { |
| |
| CommandLine::StringType GenerateNativeString(FuzzedDataProvider& provider) { |
| const std::string raw_string = provider.ConsumeRandomLengthString(); |
| #if BUILDFLAG(IS_WIN) |
| return UTF8ToWide(raw_string); |
| #else |
| return raw_string; |
| #endif |
| } |
| |
| CommandLine::StringVector GenerateNativeStringVector( |
| FuzzedDataProvider& provider) { |
| CommandLine::StringVector strings( |
| provider.ConsumeIntegralInRange<int>(0, 100)); |
| for (auto& item : strings) |
| item = GenerateNativeString(provider); |
| return strings; |
| } |
| |
| FilePath GenerateFilePath(FuzzedDataProvider& provider) { |
| return FilePath(GenerateNativeString(provider)); |
| } |
| |
| bool IsForbiddenSwitchCharacter(char c) { |
| return IsAsciiWhitespace(c) || c == '=' || c != ToLowerASCII(c); |
| } |
| |
| bool IsValidSwitchName(const std::string& text) { |
| // This duplicates the logic in command_line.cc, but it's not exposed in form |
| // of public interface. |
| return !text.empty() && !ranges::any_of(text, IsForbiddenSwitchCharacter) && |
| !StartsWith(text, "-") && !StartsWith(text, "/"); |
| } |
| |
| } // namespace |
| |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| FuzzedDataProvider provider(data, size); |
| |
| // Create a randomly initialized command line. |
| CommandLine command_line(CommandLine::NO_PROGRAM); |
| switch (provider.ConsumeIntegralInRange<int>(0, 3)) { |
| case 0: |
| // Leave empty. |
| break; |
| case 1: |
| command_line = CommandLine(GenerateFilePath(provider)); |
| break; |
| case 2: |
| command_line = CommandLine(GenerateNativeStringVector(provider)); |
| break; |
| case 3: |
| #if BUILDFLAG(IS_WIN) |
| command_line.ParseFromString(GenerateNativeString(provider)); |
| #endif |
| break; |
| } |
| |
| // Do a few mutations of the command line. |
| while (provider.remaining_bytes() > 0) { |
| switch (provider.ConsumeIntegralInRange<int>(0, 4)) { |
| case 0: { |
| // Add a switch. |
| std::string name = provider.ConsumeRandomLengthString(); |
| if (IsValidSwitchName(name)) { |
| CommandLine::StringType value = GenerateNativeString(provider); |
| command_line.AppendSwitchNative(name, value); |
| CHECK(command_line.HasSwitch(name)); |
| CHECK(command_line.GetSwitchValueNative(name) == value); |
| } |
| break; |
| } |
| case 1: { |
| // Remove a switch. |
| std::string name = provider.ConsumeRandomLengthString(); |
| if (IsValidSwitchName(name)) { |
| command_line.RemoveSwitch(name); |
| CHECK(!command_line.HasSwitch(name)); |
| CHECK(command_line.GetSwitchValueNative(name).empty()); |
| } |
| break; |
| } |
| case 2: { |
| // Add an argument. |
| CommandLine::StringType arg = GenerateNativeString(provider); |
| if (!arg.empty() && IsStringASCII(arg)) |
| command_line.AppendArgNative(arg); |
| break; |
| } |
| case 3: { |
| // Add a wrapper. |
| CommandLine::StringType wrapper = GenerateNativeString(provider); |
| if (!wrapper.empty()) |
| command_line.PrependWrapper(wrapper); |
| break; |
| } |
| case 4: { |
| // Check a switch. |
| std::string name = provider.ConsumeRandomLengthString(); |
| if (IsValidSwitchName(name)) { |
| std::ignore = command_line.HasSwitch(name); |
| std::ignore = command_line.GetSwitchValueNative(name); |
| } |
| break; |
| } |
| } |
| |
| // Smoke-test various accessors. |
| std::ignore = command_line.GetCommandLineString(); |
| std::ignore = command_line.GetArgumentsString(); |
| #if BUILDFLAG(IS_WIN) |
| std::ignore = command_line.GetCommandLineStringForShell(); |
| std::ignore = command_line.GetCommandLineStringWithUnsafeInsertSequences(); |
| #endif |
| } |
| |
| return 0; |
| } |
| |
| } // namespace base |