You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

335 lines
10 KiB

// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <base/logging.h>
#include <base/strings/string_util.h>
#include <brillo/dbus/data_serialization.h>
#include <dbus/string_util.h>
#include <fuzzer/FuzzedDataProvider.h>
namespace {
constexpr int kRandomMaxContainerSize = 8;
constexpr int kRandomMaxDataLength = 128;
typedef enum DataType {
kUint8 = 0,
kUint16,
kUint32,
kUint64,
kInt16,
kInt32,
kInt64,
kBool,
kDouble,
kString,
kObjectPath,
// A couple vector types.
kVectorInt16,
kVectorString,
// A couple pair types.
kPairBoolInt64,
kPairUint32String,
// A couple tuple types.
kTupleUint16StringBool,
kTupleDoubleInt32ObjectPath,
// A couple map types.
kMapInt32String,
kMapDoubleBool,
kMaxValue = kMapDoubleBool,
} DataType;
template <typename T>
void AppendValue(dbus::MessageWriter* writer, bool variant, const T& value) {
if (variant)
brillo::dbus_utils::AppendValueToWriterAsVariant(writer, value);
else
brillo::dbus_utils::AppendValueToWriter(writer, value);
}
template <typename T>
void GenerateIntAndAppendValue(FuzzedDataProvider* data_provider,
dbus::MessageWriter* writer,
bool variant) {
AppendValue(writer, variant, data_provider->ConsumeIntegral<T>());
}
template <typename T>
void PopValue(dbus::MessageReader* reader, bool variant, T* value) {
if (variant)
brillo::dbus_utils::PopVariantValueFromReader(reader, value);
else
brillo::dbus_utils::PopValueFromReader(reader, value);
}
std::string GenerateValidUTF8(FuzzedDataProvider* data_provider) {
// >= 0x80
// Generates a random string and returns it if it is valid UTF8, if it is not
// then it will strip it down to all the 7-bit ASCII chars and just return
// that string.
std::string str =
data_provider->ConsumeRandomLengthString(kRandomMaxDataLength);
if (base::IsStringUTF8(str))
return str;
for (auto it = str.begin(); it != str.end(); it++) {
if (static_cast<uint8_t>(*it) >= 0x80) {
// Might be invalid, remove it.
it = str.erase(it);
it--;
}
}
return str;
}
} // namespace
class Environment {
public:
Environment() {
// Disable logging.
logging::SetMinLogLevel(logging::LOG_FATAL);
}
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
static Environment env;
FuzzedDataProvider data_provider(data, size);
// Consume a random fraction of our data writing random things to a D-Bus
// message, and then consume the remaining data reading randomly from that
// same D-Bus message. Given the templated nature of these functions and that
// they support essentially an infinite amount of types, we are constraining
// this to a fixed set of types defined above.
std::unique_ptr<dbus::Response> message = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(message.get());
int bytes_left_for_read =
static_cast<int>(data_provider.ConsumeProbability<float>() * size);
while (data_provider.remaining_bytes() > bytes_left_for_read) {
DataType curr_type = data_provider.ConsumeEnum<DataType>();
bool variant = data_provider.ConsumeBool();
switch (curr_type) {
case kUint8:
GenerateIntAndAppendValue<uint8_t>(&data_provider, &writer, variant);
break;
case kUint16:
GenerateIntAndAppendValue<uint16_t>(&data_provider, &writer, variant);
break;
case kUint32:
GenerateIntAndAppendValue<uint32_t>(&data_provider, &writer, variant);
break;
case kUint64:
GenerateIntAndAppendValue<uint64_t>(&data_provider, &writer, variant);
break;
case kInt16:
GenerateIntAndAppendValue<int16_t>(&data_provider, &writer, variant);
break;
case kInt32:
GenerateIntAndAppendValue<int32_t>(&data_provider, &writer, variant);
break;
case kInt64:
GenerateIntAndAppendValue<int64_t>(&data_provider, &writer, variant);
break;
case kBool:
AppendValue(&writer, variant, data_provider.ConsumeBool());
break;
case kDouble:
AppendValue(&writer, variant,
data_provider.ConsumeProbability<double>());
break;
case kString:
AppendValue(&writer, variant, GenerateValidUTF8(&data_provider));
break;
case kObjectPath: {
std::string object_path =
data_provider.ConsumeRandomLengthString(kRandomMaxDataLength);
// If this isn't valid we'll hit a CHECK failure.
if (dbus::IsValidObjectPath(object_path))
AppendValue(&writer, variant, dbus::ObjectPath(object_path));
break;
}
case kVectorInt16: {
int vec_size = data_provider.ConsumeIntegralInRange<int>(
0, kRandomMaxContainerSize);
std::vector<int16_t> vec(vec_size);
for (int i = 0; i < vec_size; i++)
vec[i] = data_provider.ConsumeIntegral<int16_t>();
AppendValue(&writer, variant, vec);
break;
}
case kVectorString: {
int vec_size = data_provider.ConsumeIntegralInRange<int>(
0, kRandomMaxContainerSize);
std::vector<std::string> vec(vec_size);
for (int i = 0; i < vec_size; i++)
vec[i] = GenerateValidUTF8(&data_provider);
AppendValue(&writer, variant, vec);
break;
}
case kPairBoolInt64:
AppendValue(
&writer, variant,
std::pair<bool, int64_t>{data_provider.ConsumeBool(),
data_provider.ConsumeIntegral<int64_t>()});
break;
case kPairUint32String:
AppendValue(&writer, variant,
std::pair<uint32_t, std::string>{
data_provider.ConsumeIntegral<uint32_t>(),
GenerateValidUTF8(&data_provider)});
break;
case kTupleUint16StringBool:
AppendValue(&writer, variant,
std::tuple<uint32_t, std::string, bool>{
data_provider.ConsumeIntegral<uint32_t>(),
GenerateValidUTF8(&data_provider),
data_provider.ConsumeBool()});
break;
case kTupleDoubleInt32ObjectPath: {
std::string object_path =
data_provider.ConsumeRandomLengthString(kRandomMaxDataLength);
// If this isn't valid we'll hit a CHECK failure.
if (dbus::IsValidObjectPath(object_path)) {
AppendValue(&writer, variant,
std::tuple<double, int32_t, dbus::ObjectPath>{
data_provider.ConsumeProbability<double>(),
data_provider.ConsumeIntegral<int32_t>(),
dbus::ObjectPath(object_path)});
}
break;
}
case kMapInt32String: {
int map_size = data_provider.ConsumeIntegralInRange<int>(
0, kRandomMaxContainerSize);
std::map<int32_t, std::string> map;
for (int i = 0; i < map_size; i++)
map[data_provider.ConsumeIntegral<int32_t>()] =
GenerateValidUTF8(&data_provider);
AppendValue(&writer, variant, map);
break;
}
case kMapDoubleBool: {
int map_size = data_provider.ConsumeIntegralInRange<int>(
0, kRandomMaxContainerSize);
std::map<double, bool> map;
for (int i = 0; i < map_size; i++)
map[data_provider.ConsumeProbability<double>()] =
data_provider.ConsumeBool();
AppendValue(&writer, variant, map);
break;
}
}
}
dbus::MessageReader reader(message.get());
while (data_provider.remaining_bytes()) {
DataType curr_type = data_provider.ConsumeEnum<DataType>();
bool variant = data_provider.ConsumeBool();
switch (curr_type) {
case kUint8: {
uint8_t value;
PopValue(&reader, variant, &value);
break;
}
case kUint16: {
uint16_t value;
PopValue(&reader, variant, &value);
break;
}
case kUint32: {
uint32_t value;
PopValue(&reader, variant, &value);
break;
}
case kUint64: {
uint64_t value;
PopValue(&reader, variant, &value);
break;
}
case kInt16: {
int16_t value;
PopValue(&reader, variant, &value);
break;
}
case kInt32: {
int32_t value;
PopValue(&reader, variant, &value);
break;
}
case kInt64: {
int64_t value;
PopValue(&reader, variant, &value);
break;
}
case kBool: {
bool value;
PopValue(&reader, variant, &value);
break;
}
case kDouble: {
double value;
PopValue(&reader, variant, &value);
break;
}
case kString: {
std::string value;
PopValue(&reader, variant, &value);
break;
}
case kObjectPath: {
dbus::ObjectPath value;
PopValue(&reader, variant, &value);
break;
}
case kVectorInt16: {
std::vector<int16_t> value;
PopValue(&reader, variant, &value);
break;
}
case kVectorString: {
std::vector<std::string> value;
PopValue(&reader, variant, &value);
break;
}
case kPairBoolInt64: {
std::pair<bool, int64_t> value;
PopValue(&reader, variant, &value);
break;
}
case kPairUint32String: {
std::pair<uint32_t, std::string> value;
PopValue(&reader, variant, &value);
break;
}
case kTupleUint16StringBool: {
std::tuple<uint16_t, std::string, bool> value;
break;
}
case kTupleDoubleInt32ObjectPath: {
std::tuple<double, int32_t, dbus::ObjectPath> value;
PopValue(&reader, variant, &value);
break;
}
case kMapInt32String: {
std::map<int32_t, std::string> value;
PopValue(&reader, variant, &value);
break;
}
case kMapDoubleBool: {
std::map<double, bool> value;
PopValue(&reader, variant, &value);
break;
}
}
}
return 0;
}