blob: 2952077f8c5a8fe1f8c327ae95aae63c621e3475 [file] [log] [blame]
// Copyright 2022 The Centipede Authors.
//
// 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
//
// https://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 "./centipede/byte_array_mutator.h"
#include <cstddef>
#include <limits>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_set.h"
#include "./centipede/defs.h"
namespace centipede {
// Tests that when alignment is not 1 byte, adding bytes to an input will result
// in a size-aligned mutant (even if the input is not size-aligned).
//
// Note: This test cannot be in an anonymous namespace due to the FRIEND_TEST in
// ByteArrayMutator.
TEST(ByteArrayMutator, RoundUpToAddCorrectly) {
Knobs knobs;
ByteArrayMutator mutator(knobs, /*seed=*/1);
EXPECT_TRUE(mutator.set_size_alignment(4));
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/0, /*to_add=*/0), 0);
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/4, /*to_add=*/0), 0);
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/4, /*to_add=*/3), 4);
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/5, /*to_add=*/0), 3);
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/5, /*to_add=*/2), 3);
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/5, /*to_add=*/18), 19);
// Check that max length is also respected.
EXPECT_TRUE(mutator.set_max_len(12));
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/5, /*to_add=*/0), 3);
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/5, /*to_add=*/2), 3);
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/5, /*to_add=*/18), 7);
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/11, /*to_add=*/5), 1);
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/12, /*to_add=*/5), 0);
EXPECT_EQ(mutator.RoundUpToAdd(/*curr_size=*/13, /*to_add=*/5), 0);
}
// Tests that when alignment is not 1 byte, removing bytes from an input will
// result in a size-aligned mutant (even if the input is not size-aligned).
//
// Note: This test cannot be in an anonymous namespace due to the FRIEND_TEST in
// ByteArrayMutator.
TEST(ByteArrayMutator, RoundDownToRemoveCorrectly) {
Knobs knobs;
ByteArrayMutator mutator(knobs, /*seed=*/1);
EXPECT_TRUE(mutator.set_size_alignment(4));
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/0, /*to_remove=*/0), 0);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/0, /*to_remove=*/1), 0);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/1, /*to_remove=*/0), 0);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/1, /*to_remove=*/1), 0);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/4, /*to_remove=*/0), 0);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/4, /*to_remove=*/3), 0);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/5, /*to_remove=*/0), 1);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/5, /*to_remove=*/2), 1);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/7, /*to_remove=*/2), 3);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/23, /*to_remove=*/4), 7);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/23, /*to_remove=*/20), 19);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/23, /*to_remove=*/24), 19);
// Check that max length is also respected.
EXPECT_TRUE(mutator.set_max_len(12));
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/7, /*to_remove=*/2), 3);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/23, /*to_remove=*/4), 11);
EXPECT_EQ(mutator.RoundDownToRemove(/*curr_size=*/23, /*to_remove=*/20), 19);
}
namespace {
TEST(DictEntry, DictEntry) {
uint8_t bytes[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
DictEntry a_0_10({bytes + 0, 10});
DictEntry a_0_4({bytes + 0, 4});
DictEntry a_1_8({bytes + 1, 8});
EXPECT_LT(a_0_4, a_0_10);
EXPECT_LT(a_0_10, a_1_8);
EXPECT_EQ(memcmp(a_0_10.begin(), bytes, a_0_10.end() - a_0_10.begin()), 0);
EXPECT_DEATH({ DictEntry a_0_10({bytes, 16}); }, "");
}
TEST(CmpDictionary, CmpDictionary) {
CmpDictionary dict;
ExecutionMetadata metadata{.cmp_data = {
2, // size
1, 2, // a
3, 4, // b
3, // size
5, 6, 7, // a
8, 9, 10, // b
4, // size
11, 12, 13, 14, // a
15, 16, 17, 18, // b
3, // size
20, 21, 22, // a
15, 16, 17, // b
3, // size
15, 16, 20, // a
30, 40, 50, // b
}};
EXPECT_TRUE(dict.SetFromMetadata(metadata));
using S = ByteSpan;
std::vector<ByteSpan> suggestions;
suggestions.reserve(5);
dict.SuggestReplacement({42, 43}, suggestions);
EXPECT_TRUE(suggestions.empty());
dict.SuggestReplacement({1, 2, 3}, suggestions);
EXPECT_THAT(suggestions, testing::ElementsAre(S({3, 4})));
dict.SuggestReplacement({5, 6, 7, 8}, suggestions);
EXPECT_THAT(suggestions, testing::ElementsAre(S({8, 9, 10})));
dict.SuggestReplacement({15, 16, 17, 18, 0, 0}, suggestions);
EXPECT_THAT(suggestions, testing::UnorderedElementsAre(S({11, 12, 13, 14}),
S({20, 21, 22})));
dict.SuggestReplacement({15, 16, 20}, suggestions);
EXPECT_THAT(suggestions, testing::UnorderedElementsAre(S({30, 40, 50})));
// check that we don't exceed capacity.
std::vector<ByteSpan> capacity1;
capacity1.reserve(1);
EXPECT_EQ(capacity1.capacity(), 1);
dict.SuggestReplacement({15, 16, 17, 18, 0, 0}, capacity1);
EXPECT_EQ(capacity1.size(), 1);
EXPECT_EQ(capacity1.capacity(), 1);
}
// Tests that two mutators seeded with different rng seeds produce different
// results.
TEST(ByteArrayMutator, Randomness) {
Knobs knobs;
ByteArrayMutator mutator[2]{{knobs, 1}, {knobs, 2}};
std::vector<ByteArray> res[2];
for (size_t i = 0; i < 2; i++) {
ByteArray seed = {0};
// Just run a few iterations.
for (size_t iter = 0; iter < 100; iter++) {
mutator[i].Mutate(seed);
res[i].push_back(seed);
}
}
EXPECT_NE(res[0], res[1]);
}
// Tests that max length is always a multiple of size alignment.
TEST(ByteArrayMutator, CheckSizeAlignmentWithMaxLength) {
Knobs knobs;
ByteArrayMutator mutator(knobs, /*seed=*/1);
EXPECT_TRUE(mutator.set_size_alignment(1000));
EXPECT_TRUE(mutator.set_size_alignment(4));
EXPECT_TRUE(mutator.set_max_len(4));
EXPECT_TRUE(mutator.set_max_len(16));
EXPECT_FALSE(mutator.set_max_len(2));
EXPECT_FALSE(mutator.set_max_len(10));
EXPECT_TRUE(mutator.set_size_alignment(8));
EXPECT_FALSE(mutator.set_size_alignment(12));
EXPECT_FALSE(mutator.set_size_alignment(15));
}
// Tests a callback `fn`: mutations of `seed` are expected to eventually
// match all of `expected_mutants`, but never any of `unexpected_mutants`.
// Mutators that do a single-step can be tested for `unexpected_mutants`,
// while for more complicated mutators `unexpected_mutants` should be empty.
void TestMutatorFn(ByteArrayMutator::Fn fn, const ByteArray &seed,
const std::vector<ByteArray> &expected_mutants,
const std::vector<ByteArray> &unexpected_mutants,
size_t size_alignment = 1,
size_t max_len = std::numeric_limits<size_t>::max(),
const std::vector<ByteArray> &dictionary = {},
ByteSpan cmp_data = {}, size_t num_iterations = 100000000) {
Knobs knobs;
ByteArrayMutator mutator(knobs, 1);
EXPECT_TRUE(mutator.set_size_alignment(size_alignment));
EXPECT_TRUE(mutator.set_max_len(max_len));
mutator.AddToDictionary(dictionary);
mutator.SetMetadata({.cmp_data = {cmp_data.begin(), cmp_data.end()}});
absl::flat_hash_set<ByteArray> expected(expected_mutants.begin(),
expected_mutants.end());
absl::flat_hash_set<ByteArray> unexpected(unexpected_mutants.begin(),
unexpected_mutants.end());
ByteArray mutant; // create outside the loop to avoid malloc in the loop.
for (size_t i = 0; i < num_iterations; i++) {
mutant = seed;
(mutator.*fn)(mutant);
expected.erase(mutant);
if (expected.empty()) break;
EXPECT_FALSE(unexpected.contains(mutant));
}
EXPECT_TRUE(expected.empty());
}
TEST(ByteArrayMutator, ChangeByte) {
TestMutatorFn(&ByteArrayMutator::ChangeByte, {1, 2, 3},
/*expected_mutants=*/
{
{1, 2, 4},
{42, 2, 3},
{1, 66, 3},
},
/*unexpected_mutants=*/
{
{9, 9, 3},
{1, 8, 8},
{7, 2, 7},
});
}
TEST(ByteArrayMutator, FlipBit) {
TestMutatorFn(&ByteArrayMutator::FlipBit, {0, 7, 10},
/*expected_mutants=*/
{
{1, 7, 10},
{0, 6, 10},
{0, 7, 11},
},
/*unexpected_mutants=*/
{
{1, 6, 10},
{0, 6, 11},
});
}
TEST(ByteArrayMutator, SwapBytes) {
TestMutatorFn(&ByteArrayMutator::SwapBytes, {0, 1, 2},
/*expected_mutants=*/
{
{0, 2, 1},
{1, 0, 2},
{2, 1, 0},
},
/*unexpected_mutants=*/
{
{2, 0, 1},
});
}
TEST(ByteArrayMutator, InsertBytes) {
TestMutatorFn(&ByteArrayMutator::InsertBytes, {0, 1, 2},
/*expected_mutants=*/
{
{0, 1, 2, 3},
{0, 3, 1, 2},
{3, 0, 1, 2},
{0, 1, 2, 3, 4},
{0, 3, 4, 1, 2},
{3, 4, 0, 1, 2},
},
/*unexpected_mutants=*/
{
{0, 1},
{0, 1, 2},
{0, 3, 1, 4, 2},
});
}
TEST(ByteArrayMutator, InsertBytesWithAlignment) {
TestMutatorFn(&ByteArrayMutator::InsertBytes, {0, 1, 2},
/*expected_mutants=*/
{
{0, 1, 2, 3},
{0, 3, 1, 2},
{3, 0, 1, 2},
},
/*unexpected_mutants=*/
{
{0, 1},
{0, 1, 2},
{0, 1, 2, 3, 4},
{0, 3, 1, 4, 2},
{0, 3, 4, 1, 2},
{3, 4, 0, 1, 2},
},
/*size_alignment=*/4);
}
TEST(ByteArrayMutator, InsertBytesWithMaxLen) {
TestMutatorFn(&ByteArrayMutator::InsertBytes, {0, 1, 2},
/*expected_mutants=*/
{
{0, 1, 2, 3},
{0, 3, 1, 2},
{3, 0, 1, 2},
},
/*unexpected_mutants=*/
{
{0, 1, 2, 3, 4},
{0, 3, 4, 1, 2},
{3, 4, 0, 1, 2},
},
/*size_alignment=*/1,
/*max_len=*/4);
}
// Currently, same as for InsertBytes. Will change in future as we add more
// mutators.
TEST(ByteArrayMutator, MutateIncreaseSize) {
TestMutatorFn(&ByteArrayMutator::MutateIncreaseSize, {0, 1, 2},
/*expected_mutants=*/
{
{0, 1, 2, 3},
{0, 3, 1, 2},
{3, 0, 1, 2},
{0, 1, 2, 3, 4},
{0, 3, 4, 1, 2},
{3, 4, 0, 1, 2},
},
/*unexpected_mutants=*/
{
{0, 1},
{0, 3, 1, 4, 2},
});
}
TEST(ByteArrayMutator, MutateIncreaseSizeWithAlignment) {
TestMutatorFn(&ByteArrayMutator::MutateIncreaseSize, {0, 1, 2},
/*expected_mutants=*/
{
{0, 1, 2, 3},
{0, 3, 1, 2},
{3, 0, 1, 2},
},
/*unexpected_mutants=*/
{
{0, 1},
{0, 1, 2, 3, 4},
{0, 3, 1, 4, 2},
{0, 3, 4, 1, 2},
{3, 4, 0, 1, 2},
},
/*size_alignment=*/4);
}
TEST(ByteArrayMutator, EraseBytes) {
TestMutatorFn(&ByteArrayMutator::EraseBytes, {0, 1, 2, 3},
/*expected_mutants=*/
{
{0, 1, 2},
{0, 1, 3},
{0, 2, 3},
{1, 2, 3},
{0, 1},
{0, 3},
{2, 3},
},
/*unexpected_mutants=*/
{
{0},
{1},
{2},
});
}
TEST(ByteArrayMutator, EraseBytesWithAlignment) {
TestMutatorFn(&ByteArrayMutator::EraseBytes, {0, 1, 2, 3},
/*expected_mutants=*/
{
{0, 1, 2, 3},
},
/*unexpected_mutants=*/
{
{0, 1, 2},
{0, 1, 3},
{0, 2, 3},
{1, 2, 3},
{0, 1},
{0, 3},
{2, 3},
{0},
{1},
{2},
},
/*size_alignment=*/4);
TestMutatorFn(&ByteArrayMutator::EraseBytes, {0, 1, 2, 3, 4},
/*expected_mutants=*/
{
{0, 1, 2, 3},
{1, 2, 3, 4},
{0, 1, 3, 4},
},
/*unexpected_mutants=*/
{
{0, 1, 2, 3, 4},
{0, 1, 2},
{0, 1, 3},
{0, 2, 3},
{1, 2, 3},
{0, 1},
{0},
},
/*size_alignment=*/4);
}
// Currently, same as EraseBytes. Will change in future as we add more mutators.
TEST(ByteArrayMutator, MutateDecreaseSize) {
TestMutatorFn(&ByteArrayMutator::MutateDecreaseSize, {0, 1, 2, 3},
/*expected_mutants=*/
{
{0, 1, 2},
{0, 1, 3},
{0, 2, 3},
{1, 2, 3},
{0, 1},
{0, 3},
{2, 3},
},
/*unexpected_mutants=*/
{
{0},
{1},
{2},
});
}
TEST(ByteArrayMutator, MutateDecreaseSizeWithAlignment) {
TestMutatorFn(&ByteArrayMutator::MutateDecreaseSize, {0, 1, 2, 3},
/*expected_mutants=*/
{
{0, 1, 2, 3},
},
/*unexpected_mutants=*/
{
{0, 1, 2},
{0, 1, 3},
{0, 2, 3},
{1, 2, 3},
{0, 1},
{0, 3},
{2, 3},
{0},
{1},
{2},
},
/*size_alignment=*/4);
TestMutatorFn(&ByteArrayMutator::MutateDecreaseSize, {0, 1, 2, 3, 4},
/*expected_mutants=*/
{
{0, 1, 2, 3},
{1, 2, 3, 4},
{0, 1, 3, 4},
},
/*unexpected_mutants=*/
{
{0, 1, 2, 3, 4},
{0, 1, 2},
{0, 1, 3},
{0, 2, 3},
{1, 2, 3},
{0, 1},
{0},
},
/*size_alignment=*/4);
}
TEST(ByteArrayMutator, MutateDecreaseSizeWithAlignmentAndMaxLen) {
TestMutatorFn(&ByteArrayMutator::MutateDecreaseSize, {0, 1, 2, 3},
/*expected_mutants=*/
{
{0, 1},
{2, 3},
},
/*unexpected_mutants=*/
{
{0},
{1},
{2},
{1, 2},
{0, 1, 2},
},
/*size_alignment=*/2,
/*max_len=*/2);
}
// Tests that MutateSameSize will eventually produce all possible mutants of
// size 1 and 2. Also tests some of the 3-byte mutants.
TEST(ByteArrayMutator, MutateSameSize) {
Knobs knobs;
ByteArrayMutator mutator(knobs, 1);
for (size_t size = 1; size <= 2; size++) {
ByteArray data(size);
absl::flat_hash_set<ByteArray> set;
size_t expected_set_size = 1 << (8 * size);
for (size_t iter = 0; iter < 2000000ULL; iter++) {
mutator.MutateSameSize(data);
EXPECT_EQ(data.size(), size);
set.insert(data);
if (set.size() == expected_set_size) break;
}
EXPECT_EQ(expected_set_size, set.size());
}
// One step of MutateSameSize may generate any mutant that can be generated by
// one step of its submutants. No mutant of other length may appear.
const std::vector<ByteArray> kUnexpectedMutants = {
{1, 2},
{1, 2, 3, 4},
};
TestMutatorFn(&ByteArrayMutator::MutateSameSize, {1, 2, 3},
/*expected_mutants=*/
{
{1, 2, 4},
{42, 2, 3},
{1, 66, 3},
},
kUnexpectedMutants);
TestMutatorFn(&ByteArrayMutator::MutateSameSize, {0, 7, 10},
/*expected_mutants=*/
{
{1, 7, 10},
{0, 6, 10},
{0, 7, 11},
},
kUnexpectedMutants);
TestMutatorFn(&ByteArrayMutator::MutateSameSize, {0, 1, 2},
/*expected_mutants=*/
{
{0, 2, 1},
{1, 0, 2},
{2, 1, 0},
},
kUnexpectedMutants);
}
TEST(ByteArrayMutator, Mutate) {
TestMutatorFn(&ByteArrayMutator::Mutate, {1, 2, 3},
/*expected_mutants=*/
{
{1, 2, 4},
{1, 2},
{1, 2, 3, 4},
},
/*unexpected_mutants=*/
{
{},
});
}
TEST(ByteArrayMutator, OverwriteFromDictionary) {
TestMutatorFn(&ByteArrayMutator::OverwriteFromDictionary, {1, 2, 3, 4, 5},
/*expected_mutants=*/
{
{1, 2, 7, 8, 9},
{1, 7, 8, 9, 5},
{7, 8, 9, 4, 5},
{1, 2, 3, 0, 6},
{1, 2, 0, 6, 5},
{1, 0, 6, 4, 5},
{0, 6, 3, 4, 5},
{42, 2, 3, 4, 5},
{1, 42, 3, 4, 5},
{1, 2, 42, 4, 5},
{1, 2, 3, 42, 5},
{1, 2, 3, 4, 42},
},
/*unexpected_mutants=*/
{
{1, 2, 3, 7, 8},
{8, 9, 3, 4, 5},
{6, 2, 3, 4, 5},
{1, 2, 3, 4, 0},
{42, 42, 3, 4, 5},
},
/*size_alignment=*/1,
/*max_len=*/std::numeric_limits<size_t>::max(),
/*dictionary=*/
{
{7, 8, 9},
{0, 6},
{42},
});
}
TEST(ByteArrayMutator, OverwriteFromCmpDictionary) {
TestMutatorFn(&ByteArrayMutator::OverwriteFromCmpDictionary,
{1, 2, 40, 50, 60},
/*expected_mutants=*/
{
{3, 4, 40, 50, 60},
{1, 2, 10, 20, 30},
},
/*unexpected_mutants=*/
{
{3, 4, 10, 20, 30},
},
/*size_alignment=*/1,
/*max_len=*/std::numeric_limits<size_t>::max(),
/*dictionary=*/
{},
/*cmp_data=*/
{/*args1*/ 2, 1, 2, 3, 4, /*args2*/ 3, 10, 20, 30, 40, 50, 60});
}
TEST(ByteArrayMutator, OverwriteFromCmpDictionaryAndSkipLongEntry) {
TestMutatorFn(
&ByteArrayMutator::OverwriteFromCmpDictionary,
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},
/*expected_mutants=*/
{{100, 101, 102, 103, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19}},
/*unexpected_mutants=*/
{{100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119}},
/*size_alignment=*/1,
/*max_len=*/std::numeric_limits<size_t>::max(),
/*dictionary=*/
{},
/*cmp_data=*/
{/*size*/ 20, /*lhs*/ 0, 1, 2, 3, 4, 5,
6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19,
/*rhs*/ 100, 101, 102, 103, 104, 105, 106,
107, 108, 109, 110, 111, 112, 113,
114, 115, 116, 117, 118, 119,
/*size*/ 4, /*lhs*/ 0, 1, 2, 3, /*rhs*/ 100, 101,
102, 103});
}
TEST(ByteArrayMutator, InsertFromDictionary) {
TestMutatorFn(&ByteArrayMutator::InsertFromDictionary, {1, 2, 3},
/*expected_mutants=*/
{
{1, 2, 3, 4, 5},
{1, 2, 4, 5, 3},
{1, 4, 5, 2, 3},
{4, 5, 1, 2, 3},
{1, 2, 3, 6, 7, 8},
{1, 2, 6, 7, 8, 3},
{1, 6, 7, 8, 2, 3},
{6, 7, 8, 1, 2, 3},
},
/*unexpected_mutants=*/
{
{1, 2, 3, 7, 8},
{7, 8, 1, 2, 3},
},
/*size_alignment=*/1,
/*max_len=*/std::numeric_limits<size_t>::max(),
/*dictionary=*/
{
{4, 5},
{6, 7, 8},
});
}
// Tests CrossOver* mutations.
// With CrossOver, no random values are involved, only random offsets,
// and so we can test for all possible expected mutants.
void TestCrossOver(void (ByteArrayMutator::*fn)(ByteArray &, const ByteArray &),
const ByteArray &seed, const ByteArray &other,
const std::vector<ByteArray> &all_possible_mutants,
size_t size_alignment = 1) {
Knobs knobs;
ByteArrayMutator mutator(knobs, 1);
EXPECT_TRUE(mutator.set_size_alignment(size_alignment));
absl::flat_hash_set<ByteArray> expected(all_possible_mutants.begin(),
all_possible_mutants.end());
absl::flat_hash_set<ByteArray> found;
const int kNumIter = 10000;
// Run for some number of iterations, make sure we saw all expected mutations
// and nothing else.
for (int i = 0; i < kNumIter; i++) {
ByteArray mutant = seed;
(mutator.*fn)(mutant, other);
EXPECT_EQ(expected.count(mutant), 1);
found.insert(mutant);
}
EXPECT_EQ(expected, found);
}
TEST(ByteArrayMutator, CrossOverInsert) {
TestCrossOver(&ByteArrayMutator::CrossOverInsert, {1}, {2},
/*all_possible_mutants=*/
{
{1, 2},
{2, 1},
});
TestCrossOver(&ByteArrayMutator::CrossOverInsert, {1, 2}, {3},
/*all_possible_mutants=*/
{
{1, 2, 3},
{1, 3, 2},
{3, 1, 2},
});
TestCrossOver(&ByteArrayMutator::CrossOverInsert, {1}, {2, 3},
/*all_possible_mutants=*/
{
{1, 2, 3},
{2, 3, 1},
{2, 1},
{1, 2},
{3, 1},
{1, 3},
});
TestCrossOver(&ByteArrayMutator::CrossOverInsert, {1, 2}, {3, 4},
/*all_possible_mutants=*/
{
{1, 2, 3, 4},
{1, 3, 4, 2},
{3, 4, 1, 2},
{1, 2, 3},
{1, 3, 2},
{3, 1, 2},
{1, 2, 4},
{1, 4, 2},
{4, 1, 2},
});
}
TEST(ByteArrayMutator, CrossOverInsertWithAlignment) {
TestCrossOver(&ByteArrayMutator::CrossOverInsert, {1}, {2},
/*all_possible_mutants=*/
{
{1},
},
/*size_alignment=*/4);
TestCrossOver(&ByteArrayMutator::CrossOverInsert, {1, 2}, {3, 4},
/*all_possible_mutants=*/
{
{1, 2, 3, 4},
{1, 3, 4, 2},
{3, 4, 1, 2},
},
/*size_alignment=*/4);
TestCrossOver(&ByteArrayMutator::CrossOverInsert, {1, 2}, {3, 4, 5},
/*all_possible_mutants=*/
{
{1, 2, 3, 4},
{1, 3, 4, 2},
{3, 4, 1, 2},
{1, 2, 4, 5},
{1, 4, 5, 2},
{4, 5, 1, 2},
},
/*size_alignment=*/4);
TestCrossOver(&ByteArrayMutator::CrossOverInsert, {1, 2, 3, 4, 5}, {6},
/*all_possible_mutants=*/
{
{1, 2, 3, 4, 5},
},
/*size_alignment=*/4);
TestCrossOver(&ByteArrayMutator::CrossOverInsert, {1, 2, 3}, {4, 5, 6, 7},
/*all_possible_mutants=*/
{
{4, 1, 2, 3},
{5, 1, 2, 3},
{6, 1, 2, 3},
{7, 1, 2, 3},
{1, 4, 2, 3},
{1, 5, 2, 3},
{1, 6, 2, 3},
{1, 7, 2, 3},
{1, 2, 4, 3},
{1, 2, 5, 3},
{1, 2, 6, 3},
{1, 2, 7, 3},
{1, 2, 3, 4},
{1, 2, 3, 5},
{1, 2, 3, 6},
{1, 2, 3, 7},
},
/*size_alignment=*/4);
}
TEST(ByteArrayMutator, CrossOverOverwrite) {
TestCrossOver(&ByteArrayMutator::CrossOverOverwrite, {1}, {2},
/*all_possible_mutants=*/
{
{2},
});
TestCrossOver(&ByteArrayMutator::CrossOverOverwrite, {1, 2}, {3},
/*all_possible_mutants=*/
{
{1, 3},
{3, 2},
});
TestCrossOver(&ByteArrayMutator::CrossOverOverwrite, {1}, {2, 3},
/*all_possible_mutants=*/
{
{2},
{3},
});
TestCrossOver(&ByteArrayMutator::CrossOverOverwrite, {1, 2}, {3, 4},
/*all_possible_mutants=*/
{
{1, 3},
{3, 2},
{1, 4},
{4, 2},
});
TestCrossOver(&ByteArrayMutator::CrossOverOverwrite, {1, 2, 3, 4, 5, 6},
{7, 8},
/*all_possible_mutants=*/
{
// overwrite with {7}
{7, 2, 3, 4, 5, 6},
{1, 7, 3, 4, 5, 6},
{1, 2, 7, 4, 5, 6},
{1, 2, 3, 7, 5, 6},
{1, 2, 3, 4, 7, 6},
{1, 2, 3, 4, 5, 7},
// overwrite with {8}
{8, 2, 3, 4, 5, 6},
{1, 8, 3, 4, 5, 6},
{1, 2, 8, 4, 5, 6},
{1, 2, 3, 8, 5, 6},
{1, 2, 3, 4, 8, 6},
{1, 2, 3, 4, 5, 8},
// overwrite with {7, 8}
{7, 8, 3, 4, 5, 6},
{1, 7, 8, 4, 5, 6},
{1, 2, 7, 8, 5, 6},
{1, 2, 3, 7, 8, 6},
{1, 2, 3, 4, 7, 8},
});
}
TEST(ByteArrayMutator, CrossOver) {
// Most of CrossOver is tested above in CrossOverOverwrite/CrossOverInsert.
// Here just test one set of inputs to ensure CrossOver calls the other two
// functions correctly.
TestCrossOver(&ByteArrayMutator::CrossOver, {1, 2}, {3, 4},
/*all_possible_mutants=*/
{
// CrossOverInsert
{1, 2, 3, 4},
{1, 3, 4, 2},
{3, 4, 1, 2},
{1, 2, 3},
{1, 3, 2},
{3, 1, 2},
{1, 2, 4},
{1, 4, 2},
{4, 1, 2},
// CrossOverOverwrite
{1, 3},
{3, 2},
{1, 4},
{4, 2},
});
}
TEST(ByteArrayMutator, FailedMutations) {
const int kNumIter = 1000000;
ByteArray data = {1, 2, 3, 4, 5};
Knobs knobs;
ByteArrayMutator mutator(knobs, 1);
size_t num_failed_erase = 0;
size_t num_failed_generic = 0;
for (int i = 0; i < kNumIter; i++) {
num_failed_erase += !mutator.EraseBytes(data);
num_failed_generic += !mutator.Mutate(data);
}
// EraseBytes() will fail sometimes, but should not fail too often.
EXPECT_GT(num_failed_erase, 0);
EXPECT_LT(num_failed_erase, kNumIter / 2);
// The generic Mutate() should fail very infrequently.
EXPECT_LT(num_failed_generic, kNumIter / 1000);
}
TEST(ByteArrayMutator, MutateManyWithAlignedInputs) {
constexpr size_t kSizeAlignment = 4;
Knobs knobs;
ByteArrayMutator mutator(knobs, /*seed=*/1);
EXPECT_TRUE(mutator.set_size_alignment(kSizeAlignment));
constexpr size_t kNumMutantsToGenerate = 10000;
std::vector<ByteArray> mutants;
// If all inputs are aligned, expect all generated mutants to be aligned.
const std::vector<ByteArray> aligned_inputs = {
{0, 1, 2, 3},
{0, 1, 2, 3, 4, 5, 6, 7},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
};
mutator.MutateMany(GetMutationInputRefsFromDataInputs(aligned_inputs),
kNumMutantsToGenerate, mutants);
EXPECT_EQ(mutants.size(), kNumMutantsToGenerate);
for (const ByteArray &mutant : mutants) {
EXPECT_EQ(mutant.size() % kSizeAlignment, 0);
}
}
TEST(ByteArrayMutator, MutateManyWithUnalignedInputs) {
constexpr size_t kSizeAlignment = 4;
Knobs knobs;
ByteArrayMutator mutator(knobs, /*seed=*/1);
EXPECT_TRUE(mutator.set_size_alignment(kSizeAlignment));
constexpr size_t kNumMutantsToGenerate = 10000;
std::vector<ByteArray> mutants;
// If there are unaligned inputs, most mutants should be aligned, but the ones
// that are unaligned should be the same size as the unaligned inputs (as they
// resulted from mutators that did not change the size of the inputs).
const std::vector<ByteArray> unaligned_inputs = {
{0},
{0, 1},
{0, 1, 2},
{0, 1, 2, 3, 4},
{0, 1, 2, 3, 4, 5},
{0, 1, 2, 3, 4, 5, 6},
{0, 1, 2, 3, 4, 5, 6, 7, 8},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
};
mutator.MutateMany(GetMutationInputRefsFromDataInputs(unaligned_inputs),
kNumMutantsToGenerate, mutants);
EXPECT_EQ(mutants.size(), kNumMutantsToGenerate);
for (const ByteArray &mutant : mutants) {
if (mutant.size() % kSizeAlignment != 0) {
EXPECT_LE(mutant.size(), 11);
}
}
}
TEST(ByteArrayMutator, MutateManyWithMaxLen) {
constexpr size_t kMaxLen = 4;
Knobs knobs;
ByteArrayMutator mutator(knobs, /*seed=*/1);
EXPECT_TRUE(mutator.set_max_len(kMaxLen));
constexpr size_t kNumMutantsToGenerate = 10000;
std::vector<ByteArray> mutants;
const std::vector<ByteArray> inputs = {
{0},
{0, 1},
{0, 1, 2},
{0, 1, 2, 3},
};
mutator.MutateMany(GetMutationInputRefsFromDataInputs(inputs),
kNumMutantsToGenerate, mutants);
EXPECT_EQ(mutants.size(), kNumMutantsToGenerate);
for (const ByteArray &mutant : mutants) {
EXPECT_LE(mutant.size(), kMaxLen);
}
}
TEST(ByteArrayMutator, MutateManyWithMaxLenWithStartingLargeInput) {
constexpr size_t kMaxLen = 4;
Knobs knobs;
ByteArrayMutator mutator(knobs, /*seed=*/1);
EXPECT_TRUE(mutator.set_max_len(kMaxLen));
constexpr size_t kNumMutantsToGenerate = 10000;
std::vector<ByteArray> mutants;
const std::vector<ByteArray> large_input = {
{0, 1, 2, 3, 4, 5, 6, 7}, {0}, {0, 1}, {0, 1, 2}, {0, 1, 2, 3},
};
mutator.MutateMany(GetMutationInputRefsFromDataInputs(large_input),
kNumMutantsToGenerate, mutants);
EXPECT_EQ(mutants.size(), kNumMutantsToGenerate);
for (const ByteArray &mutant : mutants) {
if (mutant.size() > kMaxLen) {
// The only mutant larger than max length should be the same large input
// that mutation originally started with. All other mutants should be
// within the maximum length specified.
EXPECT_EQ(mutant, large_input[0]);
}
}
}
} // namespace
} // namespace centipede