/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ProtoFuzzerMutator.h"
using std::cerr;
using std::endl;
namespace android {
namespace vts {
namespace fuzzer {
// Creates an inital stub of mutation/random generation result.
static VarInstance VarInstanceStubFromSpec(const VarSpec &var_spec) {
VarInstance result{};
if (var_spec.has_type()) {
result.set_type(var_spec.type());
} else {
cerr << "VarInstance with no type field: " << var_spec.DebugString();
std::abort();
}
if (var_spec.has_name()) {
result.set_name(var_spec.name());
}
if (var_spec.has_predefined_type()) {
result.set_predefined_type(var_spec.predefined_type());
}
return result;
}
VarInstance ProtoFuzzerMutator::ArrayRandomGen(const VarSpec &var_spec) {
VarInstance result{VarInstanceStubFromSpec(var_spec)};
size_t vector_size = var_spec.vector_size();
result.set_vector_size(vector_size);
for (size_t i = 0; i < vector_size; ++i) {
*result.add_vector_value() = this->RandomGen(var_spec.vector_value(0));
}
return result;
}
VarInstance ProtoFuzzerMutator::ArrayMutate(const VarInstance &var_instance) {
VarInstance result{var_instance};
size_t vector_size = result.vector_size();
size_t idx = rand_(vector_size);
*result.mutable_vector_value(idx) = this->Mutate(result.vector_value(idx));
return result;
}
VarInstance ProtoFuzzerMutator::EnumRandomGen(const VarSpec &var_spec) {
VarInstance result{VarInstanceStubFromSpec(var_spec)};
const EnumData &blueprint =
FindPredefinedType(result.predefined_type()).enum_value();
size_t size = blueprint.enumerator_size();
size_t idx = rand_(size);
ScalarData scalar_value = blueprint.scalar_value(idx);
string scalar_type = blueprint.scalar_type();
// Mutate this enum like a scalar with probability
// odds_for/(odds_for + odds_against).
uint64_t odds_for = (this->mutator_config_).enum_bias_.first;
uint64_t odds_against = (this->mutator_config_).enum_bias_.second;
uint64_t rand_num = rand_(odds_for + odds_against);
if (rand_num < odds_for) {
scalar_value = Mutate(scalar_value, scalar_type);
}
*(result.mutable_scalar_value()) = scalar_value;
result.set_scalar_type(scalar_type);
return result;
}
VarInstance ProtoFuzzerMutator::EnumMutate(const VarInstance &var_instance) {
// For TYPE_ENUM, VarInstance contains superset info of VarSpec.
return RandomGen(var_instance);
}
VarInstance ProtoFuzzerMutator::ScalarRandomGen(const VarSpec &var_spec) {
VarInstance result{VarInstanceStubFromSpec(var_spec)};
result.set_scalar_type(var_spec.scalar_type());
(*result.mutable_scalar_value()) =
RandomGen(result.scalar_value(), result.scalar_type());
return result;
}
VarInstance ProtoFuzzerMutator::ScalarMutate(const VarInstance &var_instance) {
VarInstance result{var_instance};
(*result.mutable_scalar_value()) =
Mutate(result.scalar_value(), result.scalar_type());
return result;
}
VarInstance ProtoFuzzerMutator::StringRandomGen(const VarSpec &var_spec) {
VarInstance result{VarInstanceStubFromSpec(var_spec)};
size_t str_size = mutator_config_.default_string_size_;
string str(str_size, 0);
auto rand_char = std::bind(&ProtoFuzzerMutator::RandomAsciiChar, this);
std::generate_n(str.begin(), str_size, rand_char);
StringDataValueMessage string_data;
string_data.set_message(str.c_str());
string_data.set_length(str_size);
*result.mutable_string_value() = string_data;
return result;
}
VarInstance ProtoFuzzerMutator::StringMutate(const VarInstance &var_instance) {
VarInstance result{var_instance};
string str = result.string_value().message();
size_t str_size = result.string_value().length();
// Three things can happen when mutating a string:
// 1. A random char is inserted into a random position.
// 2. A randomly selected char is removed from the string.
// 3. A randomly selected char in the string is replaced by a random char.
size_t dice_roll = str.empty() ? 0 : rand_(3);
size_t idx = rand_(str_size);
switch (dice_roll) {
case 0:
// Insert a random char.
str.insert(str.begin() + idx, RandomAsciiChar());
++str_size;
break;
case 1:
// Remove a randomly selected char.
str.erase(str.begin() + idx);
--str_size;
break;
case 2:
// Replace a randomly selected char.
str[idx] = RandomAsciiChar();
break;
default:
// Do nothing.
break;
}
result.mutable_string_value()->set_message(str);
result.mutable_string_value()->set_length(str_size);
return result;
}
VarInstance ProtoFuzzerMutator::StructRandomGen(const VarSpec &var_spec) {
VarInstance result{VarInstanceStubFromSpec(var_spec)};
const TypeSpec &blueprint = FindPredefinedType(result.predefined_type());
for (const VarSpec &struct_value : blueprint.struct_value()) {
*result.add_struct_value() = this->RandomGen(struct_value);
}
return result;
}
VarInstance ProtoFuzzerMutator::StructMutate(const VarInstance &var_instance) {
VarInstance result{var_instance};
size_t size = result.struct_value_size();
size_t idx = rand_(size);
*result.mutable_struct_value(idx) = this->Mutate(result.struct_value(idx));
return result;
}
VarInstance ProtoFuzzerMutator::UnionRandomGen(const VarSpec &var_spec) {
VarInstance result{VarInstanceStubFromSpec(var_spec)};
const TypeSpec &blueprint = FindPredefinedType(result.predefined_type());
size_t size = blueprint.union_value_size();
for (size_t i = 0; i < size; ++i) {
result.add_union_value();
}
size_t idx = rand_(size);
*result.mutable_union_value(idx) =
this->RandomGen(blueprint.union_value(idx));
return result;
}
VarInstance ProtoFuzzerMutator::UnionMutate(const VarInstance &var_instance) {
VarInstance result{var_instance};
size_t size = result.union_value_size();
for (size_t i = 0; i < size; ++i) {
if (result.union_value(i).has_name()) {
*result.mutable_union_value(i) = this->Mutate(result.union_value(i));
}
}
return result;
}
VarInstance ProtoFuzzerMutator::VectorRandomGen(const VarSpec &var_spec) {
VarInstance result{VarInstanceStubFromSpec(var_spec)};
size_t size = mutator_config_.default_vector_size_;
for (size_t i = 0; i < size; ++i) {
*result.add_vector_value() = this->RandomGen(var_spec.vector_value(0));
}
return result;
}
VarInstance ProtoFuzzerMutator::VectorMutate(const VarInstance &var_instance) {
VarInstance result{var_instance};
size_t size = result.vector_size();
size_t idx = rand_(size);
*result.mutable_vector_value(idx) = this->Mutate(result.vector_value(idx));
return result;
}
ScalarData ProtoFuzzerMutator::RandomGen(const ScalarData &scalar_value,
const string &scalar_type) {
ScalarData result{};
if (scalar_type == "bool_t") {
result.set_bool_t(RandomGen(static_cast<bool>(scalar_value.bool_t())));
} else if (scalar_type == "int8_t") {
result.set_int8_t(RandomGen(scalar_value.int8_t()));
} else if (scalar_type == "uint8_t") {
result.set_uint8_t(RandomGen(scalar_value.uint8_t()));
} else if (scalar_type == "int16_t") {
result.set_int16_t(RandomGen(scalar_value.int16_t()));
} else if (scalar_type == "uint16_t") {
result.set_uint16_t(RandomGen(scalar_value.uint16_t()));
} else if (scalar_type == "int32_t") {
result.set_int32_t(RandomGen(scalar_value.int32_t()));
} else if (scalar_type == "uint32_t") {
result.set_uint32_t(RandomGen(scalar_value.uint32_t()));
} else if (scalar_type == "int64_t") {
result.set_int64_t(RandomGen(scalar_value.int64_t()));
} else if (scalar_type == "uint64_t") {
result.set_uint64_t(RandomGen(scalar_value.uint64_t()));
} else if (scalar_type == "float_t") {
result.set_float_t(RandomGen(scalar_value.float_t()));
} else if (scalar_type == "double_t") {
result.set_double_t(RandomGen(scalar_value.double_t()));
} else {
cout << scalar_type << " not supported.\n";
}
return result;
}
ScalarData ProtoFuzzerMutator::Mutate(const ScalarData &scalar_value,
const string &scalar_type) {
ScalarData result{};
if (scalar_type == "bool_t") {
result.set_bool_t(Mutate(static_cast<bool>(scalar_value.bool_t())));
} else if (scalar_type == "int8_t") {
result.set_int8_t(Mutate(scalar_value.int8_t()));
} else if (scalar_type == "uint8_t") {
result.set_uint8_t(Mutate(scalar_value.uint8_t()));
} else if (scalar_type == "int16_t") {
result.set_int16_t(Mutate(scalar_value.int16_t()));
} else if (scalar_type == "uint16_t") {
result.set_uint16_t(Mutate(scalar_value.uint16_t()));
} else if (scalar_type == "int32_t") {
result.set_int32_t(Mutate(scalar_value.int32_t()));
} else if (scalar_type == "uint32_t") {
result.set_uint32_t(Mutate(scalar_value.uint32_t()));
} else if (scalar_type == "int64_t") {
result.set_int64_t(Mutate(scalar_value.int64_t()));
} else if (scalar_type == "uint64_t") {
result.set_uint64_t(Mutate(scalar_value.uint64_t()));
} else if (scalar_type == "float_t") {
result.set_float_t(Mutate(scalar_value.float_t()));
} else if (scalar_type == "double_t") {
result.set_double_t(Mutate(scalar_value.double_t()));
} else {
cout << scalar_type << " not supported.\n";
}
return result;
}
template <typename T>
T ProtoFuzzerMutator::RandomGen(T value) {
// Generate a biased random scalar.
uint64_t rand_int = mutator_config_.scalar_bias_(rand_);
T result;
memcpy(&result, &rand_int, sizeof(T));
return result;
}
bool ProtoFuzzerMutator::RandomGen(bool value) {
return static_cast<bool>(rand_(2));
}
template <typename T>
T ProtoFuzzerMutator::Mutate(T value) {
size_t num_bits = 8 * sizeof(T);
T mask = static_cast<T>(1) << rand_(num_bits);
return value ^ mask;
}
bool ProtoFuzzerMutator::Mutate(bool value) { return RandomGen(value); }
float ProtoFuzzerMutator::Mutate(float value) {
uint32_t copy;
memcpy(©, &value, sizeof(float));
uint32_t mask = static_cast<uint32_t>(1) << rand_(32);
copy ^= mask;
memcpy(&value, ©, sizeof(float));
return value;
}
double ProtoFuzzerMutator::Mutate(double value) {
uint64_t copy;
memcpy(©, &value, sizeof(double));
uint64_t mask = static_cast<uint64_t>(1) << rand_(64);
copy ^= mask;
memcpy(&value, ©, sizeof(double));
return value;
}
char ProtoFuzzerMutator::RandomAsciiChar() {
const char char_set[] =
"0123456789"
"`~!@#$%^&*()-_=+[{]};:',<.>/? "
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
size_t num_chars = sizeof(char_set) - 1;
return char_set[rand_(num_chars)];
}
} // namespace fuzzer
} // namespace vts
} // namespace android