| /* |
| * 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 |