
Add fuzzer for testing string and internal state manipulation logic in base::CommandLine. Bug: none Change-Id: I00179c0cd6b3411ba0260a5ae31b6106bf49bb91 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4004740 Reviewed-by: Lei Zhang <thestig@chromium.org> Commit-Queue: Maksim Ivanov <emaxx@chromium.org> Cr-Commit-Position: refs/heads/main@{#1067389}
143 lines
4.1 KiB
C++
143 lines
4.1 KiB
C++
// 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
|