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
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;
|
|
}
|