// 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 #include #include #include #include #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(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(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(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