| // 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/coverage.h" |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstdlib> |
| #include <filesystem> // NOLINT |
| #include <sstream> |
| #include <string> |
| #include <string_view> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "./centipede/binary_info.h" |
| #include "./centipede/control_flow.h" |
| #include "./centipede/environment.h" |
| #include "./centipede/feature.h" |
| #include "./centipede/pc_info.h" |
| #include "./centipede/symbol_table.h" |
| #include "./centipede/test_coverage_util.h" |
| #include "./centipede/thread_pool.h" |
| #include "./centipede/util.h" |
| #include "./common/test_util.h" |
| |
| namespace fuzztest::internal { |
| namespace { |
| |
| // llvm-symbolizer output for a binary with 3 functions: |
| // A, BB, CCC. |
| // A and BB have one control flow edge each. |
| // CCC has 3 edges. |
| const char *symbolizer_output = |
| "A\n" |
| "a.cc:1:0\n" |
| "\n" |
| "BB\n" |
| "bb.cc:1:0\n" |
| "\n" |
| "CCC\n" |
| "ccc.cc:1:0\n" |
| "\n" |
| "CCC\n" |
| "ccc.cc:2:0\n" |
| "\n" |
| "CCC\n" |
| "ccc.cc:3:0\n" |
| "\n" |
| "CCC\n" |
| "ccc.cc:3:0\n" // same as the previous entry |
| "\n"; |
| |
| // PCTable that corresponds to symbolizer_output above. |
| static const PCTable g_pc_table = { |
| {100, PCInfo::kFuncEntry}, |
| {200, PCInfo::kFuncEntry}, |
| {300, PCInfo::kFuncEntry}, |
| {400, 0}, |
| {500, 0}, |
| {600, 0}, |
| }; |
| |
| // Tests Coverage and SymbolTable together. |
| TEST(Coverage, SymbolTable) { |
| const std::filesystem::path test_dir = GetTestTempDir(test_info_->name()); |
| |
| // Initialize and test SymbolTable. |
| SymbolTable symbols; |
| std::istringstream iss(symbolizer_output); |
| symbols.ReadFromLLVMSymbolizer(iss); |
| EXPECT_EQ(symbols.size(), 6U); |
| EXPECT_EQ(symbols.func(1), "BB"); |
| EXPECT_EQ(symbols.location(2), "ccc.cc:1:0"); |
| EXPECT_EQ(symbols.full_description(0), "A a.cc:1:0"); |
| EXPECT_EQ(symbols.full_description(4), "CCC ccc.cc:3:0"); |
| |
| { |
| // Tests coverage output for PCIndexVec = {0, 2}, |
| // i.e. the covered edges are 'A' and the entry of 'CCC'. |
| Coverage cov(g_pc_table, {0, 2}); |
| cov.DumpReportToFile(symbols, (test_dir / "coverage.txt").string()); |
| std::string str; |
| ReadFromLocalFile((test_dir / "coverage.txt").string(), str); |
| EXPECT_THAT(str, testing::HasSubstr("FULL: A a.cc:1:0")); |
| EXPECT_THAT(str, testing::HasSubstr("NONE: BB bb.cc:1:0")); |
| EXPECT_THAT(str, testing::HasSubstr("PARTIAL: CCC ccc.cc:1:0")); |
| EXPECT_THAT(str, testing::HasSubstr("+ CCC ccc.cc:1:0")); |
| EXPECT_THAT(str, testing::HasSubstr("- CCC ccc.cc:2:0")); |
| EXPECT_THAT(str, testing::HasSubstr("- CCC ccc.cc:3:0")); |
| } |
| { |
| // Same as above, but for PCIndexVec = {1, 2, 3}, |
| Coverage cov(g_pc_table, {1, 2, 3}); |
| cov.DumpReportToFile(symbols, (test_dir / "coverage.txt").string()); |
| std::string str; |
| ReadFromLocalFile((test_dir / "coverage.txt").string(), str); |
| EXPECT_THAT(str, testing::HasSubstr("FULL: BB bb.cc:1:0")); |
| EXPECT_THAT(str, testing::HasSubstr("NONE: A a.cc:1:0")); |
| EXPECT_THAT(str, testing::HasSubstr("PARTIAL: CCC ccc.cc:1:0")); |
| EXPECT_THAT(str, testing::HasSubstr("+ CCC ccc.cc:1:0")); |
| EXPECT_THAT(str, testing::HasSubstr("+ CCC ccc.cc:2:0")); |
| EXPECT_THAT(str, testing::HasSubstr("- CCC ccc.cc:3:0")); |
| } |
| |
| symbols.SetAllToUnknown(2); |
| EXPECT_EQ(symbols.size(), 2); |
| EXPECT_EQ(symbols.full_description(0), "? ?"); |
| EXPECT_EQ(symbols.full_description(1), "? ?"); |
| } |
| |
| TEST(Coverage, CoverageLoad) { |
| Coverage cov(g_pc_table, {0, 2, 4, 5}); |
| |
| EXPECT_TRUE(cov.BlockIsCovered(0)); |
| EXPECT_FALSE(cov.BlockIsCovered(1)); |
| EXPECT_TRUE(cov.BlockIsCovered(2)); |
| EXPECT_FALSE(cov.BlockIsCovered(3)); |
| EXPECT_TRUE(cov.BlockIsCovered(4)); |
| EXPECT_TRUE(cov.BlockIsCovered(5)); |
| |
| EXPECT_TRUE(cov.FunctionIsFullyCovered(0)); |
| EXPECT_FALSE(cov.FunctionIsFullyCovered(1)); |
| EXPECT_FALSE(cov.FunctionIsFullyCovered(2)); |
| } |
| |
| TEST(Coverage, CoverageLogger) { |
| SymbolTable symbols; |
| std::istringstream iss(symbolizer_output); |
| symbols.ReadFromLLVMSymbolizer(iss); |
| CoverageLogger logger(g_pc_table, symbols); |
| // First time logging pc_index=0. |
| EXPECT_EQ(logger.ObserveAndDescribeIfNew(0), "FUNC: A a.cc:1:0"); |
| // Second time logger pc_index=0. |
| EXPECT_EQ(logger.ObserveAndDescribeIfNew(0), ""); |
| // First time logging pc_index=4. |
| EXPECT_EQ(logger.ObserveAndDescribeIfNew(4), "EDGE: CCC ccc.cc:3:0"); |
| // First time logging pc_index=5, but it produces the same description as |
| // pc_index=4, and so the result is empty. |
| EXPECT_EQ(logger.ObserveAndDescribeIfNew(5), ""); |
| |
| // Logging with pc_index out of bounds. Second time gives empty result. |
| EXPECT_EQ(logger.ObserveAndDescribeIfNew(42), "FUNC/EDGE index: 42"); |
| EXPECT_EQ(logger.ObserveAndDescribeIfNew(42), ""); |
| |
| CoverageLogger concurrently_used_logger(g_pc_table, symbols); |
| auto cb = [&]() { |
| for (int i = 0; i < 1000; i++) { |
| PCIndex pc_index = i % g_pc_table.size(); |
| logger.ObserveAndDescribeIfNew(pc_index); |
| } |
| }; |
| { |
| ThreadPool threads{2}; |
| threads.Schedule(cb); |
| threads.Schedule(cb); |
| } // The threads join here. |
| } |
| |
| // Returns path to test_fuzz_target. |
| static std::string GetTargetPath() { |
| return GetDataDependencyFilepath("centipede/testing/test_fuzz_target"); |
| } |
| |
| // Returns path to threaded_fuzz_target. |
| static std::string GetThreadedTargetPath() { |
| return GetDataDependencyFilepath("centipede/testing/threaded_fuzz_target"); |
| } |
| |
| // Tests coverage collection on test_fuzz_target |
| // using two inputs that trigger different code paths. |
| TEST(Coverage, CoverageFeatures) { |
| // Prepare the inputs. |
| Environment env; |
| env.binary = GetTargetPath(); |
| auto features = RunInputsAndCollectCoverage(env, {"func1", "func2-A"}); |
| EXPECT_EQ(features.size(), 2); |
| EXPECT_NE(features[0], features[1]); |
| // Get pc_table and symbols. |
| bool uses_legacy_trace_pc_instrumentation = {}; |
| BinaryInfo binary_info; |
| binary_info.InitializeFromSanCovBinary( |
| GetTargetPath(), GetObjDumpPath(), GetLLVMSymbolizerPath(), |
| GetTestTempDir(test_info_->name()).string()); |
| const auto &pc_table = binary_info.pc_table; |
| EXPECT_FALSE(uses_legacy_trace_pc_instrumentation); |
| const SymbolTable &symbols = binary_info.symbols; |
| // pc_table and symbols should have the same size. |
| EXPECT_EQ(pc_table.size(), symbols.size()); |
| // Check what's covered. |
| // Both inputs should cover LLVMFuzzerTestOneInput. |
| // Input[0] should cover SingleEdgeFunc and not MultiEdgeFunc. |
| // Input[1] - the other way around. |
| for (size_t input_idx = 0; input_idx < 2; input_idx++) { |
| size_t llvm_fuzzer_test_one_input_num_edges = 0; |
| size_t single_edge_func_num_edges = 0; |
| size_t multi_edge_func_num_edges = 0; |
| for (auto feature : features[input_idx]) { |
| if (!feature_domains::kPCs.Contains(feature)) continue; |
| auto pc_index = ConvertPCFeatureToPcIndex(feature); |
| single_edge_func_num_edges += symbols.func(pc_index) == "SingleEdgeFunc"; |
| multi_edge_func_num_edges += symbols.func(pc_index) == "MultiEdgeFunc"; |
| llvm_fuzzer_test_one_input_num_edges += |
| symbols.func(pc_index) == "LLVMFuzzerTestOneInput"; |
| } |
| EXPECT_GT(llvm_fuzzer_test_one_input_num_edges, 1); |
| if (input_idx == 0) { |
| // This input calls SingleEdgeFunc, but not MultiEdgeFunc. |
| EXPECT_EQ(single_edge_func_num_edges, 1); |
| EXPECT_EQ(multi_edge_func_num_edges, 0); |
| } else { |
| // This input calls MultiEdgeFunc, but not SingleEdgeFunc. |
| EXPECT_EQ(single_edge_func_num_edges, 0); |
| EXPECT_GT(multi_edge_func_num_edges, 1); |
| } |
| } |
| } |
| |
| static FeatureVec ExtractDomainFeatures(const FeatureVec &features, |
| const feature_domains::Domain &domain) { |
| FeatureVec result; |
| for (auto feature : features) { |
| if (domain.Contains(feature)) { |
| result.push_back(feature); |
| } |
| } |
| return result; |
| } |
| |
| // Tests data flow instrumentation and feature collection. |
| TEST(Coverage, DataFlowFeatures) { |
| Environment env; |
| env.binary = GetTargetPath(); |
| auto features_g = RunInputsAndCollectCoverage(env, {"glob1", "glob2"}); |
| auto features_c = RunInputsAndCollectCoverage(env, {"cons1", "cons2"}); |
| for (auto &features : {features_g, features_c}) { |
| EXPECT_EQ(features.size(), 2); |
| // Dataflow features should be different. |
| EXPECT_NE(ExtractDomainFeatures(features[0], feature_domains::kDataFlow), |
| ExtractDomainFeatures(features[1], feature_domains::kDataFlow)); |
| // But control flow features should be the same. |
| EXPECT_EQ( |
| ExtractDomainFeatures(features[0], feature_domains::k8bitCounters), |
| ExtractDomainFeatures(features[1], feature_domains::k8bitCounters)); |
| } |
| } |
| |
| // Tests feature collection for counters (--use_counter_features). |
| TEST(Coverage, CounterFeatures) { |
| Environment env; |
| env.binary = GetTargetPath(); |
| |
| // Inputs that generate the same PC coverage but different counters. |
| std::vector<std::string> inputs = {"cnt\x01", "cnt\x02", "cnt\x04", "cnt\x08", |
| "cnt\x10"}; |
| const size_t n = inputs.size(); |
| |
| // Run with use_counter_features = true. |
| env.use_counter_features = true; |
| auto features = RunInputsAndCollectCoverage(env, inputs); |
| EXPECT_EQ(features.size(), n); |
| // Counter features should be different. |
| for (size_t i = 0; i < n; ++i) { |
| for (size_t j = i + 1; j < n; ++j) { |
| EXPECT_NE( |
| ExtractDomainFeatures(features[i], feature_domains::k8bitCounters), |
| ExtractDomainFeatures(features[j], feature_domains::k8bitCounters)); |
| } |
| } |
| |
| // Run with use_counter_features = false. |
| env.use_counter_features = false; |
| features = RunInputsAndCollectCoverage(env, inputs); |
| EXPECT_EQ(features.size(), n); |
| // Counter features should be the same now. |
| for (size_t i = 0; i < n; ++i) { |
| for (size_t j = i + 1; j < n; ++j) { |
| EXPECT_EQ( |
| ExtractDomainFeatures(features[i], feature_domains::k8bitCounters), |
| ExtractDomainFeatures(features[j], feature_domains::k8bitCounters)); |
| } |
| } |
| } |
| |
| // For each of {ABToCmpModDiff, ABToCmpHamming, ABToCmpDiffLog} verify that |
| // a) they create all possible values in [0,64) |
| // b) they don't create any other values. |
| // c) they are sufficiently different from each other, i.e. not using one of |
| // them as coverage signal may reduce the overall quality of signal. |
| TEST(Coverage, CMPFeatures) { |
| absl::flat_hash_set<uintptr_t> moddiff, hamming, difflog; |
| |
| // clear all hash sets. |
| auto clear = [&]() { |
| moddiff.clear(); |
| hamming.clear(); |
| difflog.clear(); |
| }; |
| |
| // verifies `value` < 64 and returns it. |
| auto must_be_6bit = [](uintptr_t value) { |
| EXPECT_LT(value, 64); |
| return value; |
| }; |
| |
| // inserts a value into all hash sets. |
| auto update = [&](uintptr_t a, uintptr_t b) { |
| moddiff.insert(must_be_6bit(ABToCmpModDiff(a, b))); |
| hamming.insert(must_be_6bit(ABToCmpHamming(a, b))); |
| difflog.insert(must_be_6bit(ABToCmpDiffLog(a, b))); |
| }; |
| |
| // Check moddiff. |
| clear(); |
| for (uintptr_t a = 0; a <= 128; ++a) { |
| uintptr_t b = 64; |
| if (a == b) continue; |
| update(a, b); |
| } |
| EXPECT_EQ(moddiff.size(), 64); |
| EXPECT_EQ(hamming.size(), 7); |
| EXPECT_EQ(difflog.size(), 7); |
| |
| // Check hamming. |
| clear(); |
| for (uintptr_t bits = 0; bits < 64; ++bits) { |
| uintptr_t minus_one = -1; |
| uintptr_t a = minus_one << bits; |
| update(a, 0); |
| } |
| EXPECT_EQ(moddiff.size(), 1); |
| EXPECT_EQ(hamming.size(), 64); |
| EXPECT_EQ(difflog.size(), 1); |
| |
| // Check difflog. |
| clear(); |
| for (uintptr_t bits = 0; bits < 64; ++bits) { |
| uintptr_t a = 1ULL << bits; |
| uintptr_t b = 0; |
| update(a, b); |
| } |
| EXPECT_EQ(moddiff.size(), 7); |
| EXPECT_EQ(hamming.size(), 1); |
| EXPECT_EQ(difflog.size(), 64); |
| } |
| |
| // Tests CMP tracing and feature collection. |
| TEST(Coverage, CMPFeaturesExecute) { |
| Environment env; |
| env.binary = GetTargetPath(); |
| auto features = |
| RunInputsAndCollectCoverage(env, {"cmpAAAAAAAA", "cmpAAAABBBB"}); |
| EXPECT_EQ(features.size(), 2); |
| // CMP features should be different. |
| EXPECT_NE(ExtractDomainFeatures(features[0], feature_domains::kCMPEq), |
| ExtractDomainFeatures(features[1], feature_domains::kCMPEq)); |
| EXPECT_NE(ExtractDomainFeatures(features[0], feature_domains::kCMPModDiff), |
| ExtractDomainFeatures(features[1], feature_domains::kCMPModDiff)); |
| EXPECT_NE(ExtractDomainFeatures(features[0], feature_domains::kCMPHamming), |
| ExtractDomainFeatures(features[1], feature_domains::kCMPHamming)); |
| EXPECT_NE(ExtractDomainFeatures(features[0], feature_domains::kCMPDiffLog), |
| ExtractDomainFeatures(features[1], feature_domains::kCMPDiffLog)); |
| |
| // But control flow features should be the same. |
| EXPECT_EQ(ExtractDomainFeatures(features[0], feature_domains::k8bitCounters), |
| ExtractDomainFeatures(features[1], feature_domains::k8bitCounters)); |
| } |
| |
| // Tests memcmp interceptor. |
| TEST(Coverage, CMPFeaturesFromMemcmp) { |
| Environment env; |
| env.binary = GetTargetPath(); |
| auto features = |
| RunInputsAndCollectCoverage(env, {"mcmpAAAAAAAA", "mcmpAAAABBBB"}); |
| EXPECT_EQ(features.size(), 2); |
| // CMP mod diff score features should be different. |
| EXPECT_NE(ExtractDomainFeatures(features[0], feature_domains::kCMPModDiff), |
| ExtractDomainFeatures(features[1], feature_domains::kCMPModDiff)); |
| // But other CMP score features should be the same. |
| EXPECT_EQ(ExtractDomainFeatures(features[0], feature_domains::kCMPHamming), |
| ExtractDomainFeatures(features[1], feature_domains::kCMPHamming)); |
| EXPECT_EQ(ExtractDomainFeatures(features[0], feature_domains::kCMPDiffLog), |
| ExtractDomainFeatures(features[1], feature_domains::kCMPDiffLog)); |
| // But control flow features should be the same. |
| EXPECT_EQ(ExtractDomainFeatures(features[0], feature_domains::k8bitCounters), |
| ExtractDomainFeatures(features[1], feature_domains::k8bitCounters)); |
| } |
| |
| TEST(Coverage, PathFeatures) { |
| Environment env; |
| env.binary = GetTargetPath(); |
| env.path_level = 10; |
| // Inputs "pth123" and "pth321" generate different call sequences but exactly |
| // the same edge coverage. This test verifies that we can capture this. |
| auto features = RunInputsAndCollectCoverage(env, {"pth123", "pth321"}); |
| EXPECT_EQ(features.size(), 2); |
| // Path features should be different. |
| EXPECT_NE(ExtractDomainFeatures(features[0], feature_domains::kBoundedPath), |
| ExtractDomainFeatures(features[1], feature_domains::kBoundedPath)); |
| // But control flow features should be the same. |
| EXPECT_EQ(ExtractDomainFeatures(features[0], feature_domains::k8bitCounters), |
| ExtractDomainFeatures(features[1], feature_domains::k8bitCounters)); |
| } |
| |
| TEST(Coverage, FunctionFilter) { |
| // Initialize coverage data. |
| BinaryInfo binary_info; |
| binary_info.InitializeFromSanCovBinary( |
| GetTargetPath(), GetObjDumpPath(), GetLLVMSymbolizerPath(), |
| GetTestTempDir(test_info_->name()).string()); |
| |
| const PCTable &pc_table = binary_info.pc_table; |
| EXPECT_FALSE(binary_info.uses_legacy_trace_pc_instrumentation); |
| const DsoTable dso_table = {{GetTargetPath(), pc_table.size()}}; |
| SymbolTable symbols; |
| symbols.GetSymbolsFromBinary(pc_table, dso_table, GetLLVMSymbolizerPath(), |
| GetTestTempDir(test_info_->name()).string()); |
| // Empty filter. |
| FunctionFilter empty_filter("", symbols); |
| EXPECT_EQ(empty_filter.count(), 0); |
| |
| // Single-function filter. The function has one PC. |
| FunctionFilter sing_edge_func_filter("SingleEdgeFunc", symbols); |
| EXPECT_EQ(sing_edge_func_filter.count(), 1); |
| |
| // Another single-function filter. This function has several PCs. |
| FunctionFilter multi_edge_func_filter("MultiEdgeFunc", symbols); |
| EXPECT_GT(multi_edge_func_filter.count(), 1); |
| |
| // Two-function-filter. |
| FunctionFilter both_func_filter("MultiEdgeFunc,SingleEdgeFunc", symbols); |
| EXPECT_GT(both_func_filter.count(), multi_edge_func_filter.count()); |
| |
| // Collect features from the test target by running 3 different inputs. |
| Environment env; |
| env.binary = GetTargetPath(); |
| std::vector<FeatureVec> features = |
| RunInputsAndCollectCoverage(env, {"func1", "func2-A", "other"}); |
| EXPECT_EQ(features.size(), 3); |
| auto &single = features[0]; |
| auto &multi = features[1]; |
| auto &other = features[2]; |
| |
| // Check the features against the different filters. |
| EXPECT_TRUE(empty_filter.filter(single)); |
| EXPECT_TRUE(empty_filter.filter(multi)); |
| EXPECT_TRUE(empty_filter.filter(other)); |
| |
| EXPECT_TRUE(sing_edge_func_filter.filter(single)); |
| EXPECT_FALSE(sing_edge_func_filter.filter(multi)); |
| EXPECT_FALSE(sing_edge_func_filter.filter(other)); |
| |
| EXPECT_FALSE(multi_edge_func_filter.filter(single)); |
| EXPECT_TRUE(multi_edge_func_filter.filter(multi)); |
| EXPECT_FALSE(multi_edge_func_filter.filter(other)); |
| |
| EXPECT_TRUE(both_func_filter.filter(single)); |
| EXPECT_TRUE(both_func_filter.filter(multi)); |
| EXPECT_FALSE(both_func_filter.filter(other)); |
| } |
| |
| TEST(Coverage, ThreadedTest) { |
| Environment env; |
| env.path_level = 10; |
| env.binary = GetThreadedTargetPath(); |
| |
| std::vector<FeatureVec> features = |
| RunInputsAndCollectCoverage(env, {"f", "fu", "fuz", "fuzz"}); |
| EXPECT_EQ(features.size(), 4); |
| // For several pairs of inputs, check that their features in |
| // kPC and kBoundedPath are different. |
| for (size_t idx0 = 0; idx0 < 3; ++idx0) { |
| for (size_t idx1 = idx0 + 1; idx1 < 4; ++idx1) { |
| EXPECT_NE(ExtractDomainFeatures(features[idx0], feature_domains::kPCs), |
| ExtractDomainFeatures(features[idx1], |
| feature_domains::k8bitCounters)); |
| EXPECT_NE( |
| ExtractDomainFeatures(features[idx0], feature_domains::kBoundedPath), |
| ExtractDomainFeatures(features[idx1], feature_domains::kBoundedPath)); |
| } |
| } |
| } |
| |
| TEST(FrontierWeight, ComputeFrontierWeight) { |
| PCTable g_pc_table{{0, PCInfo::kFuncEntry}, |
| {1, PCInfo::kFuncEntry}, |
| {2, 0}, |
| {3, PCInfo::kFuncEntry}, |
| {4, PCInfo::kFuncEntry}}; |
| // A simple CF table, to get cyclomatic complexity of 1 for all functions. |
| CFTable g_cf_table{ |
| 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, |
| }; |
| |
| Coverage g_coverage(g_pc_table, {0, 1}); |
| ControlFlowGraph cfg; |
| cfg.InitializeControlFlowGraph(g_cf_table, g_pc_table); |
| |
| std::vector<uintptr_t> callees1 = {0, 1, 3, 4}; |
| std::vector<uintptr_t> callees2 = {0, 1}; |
| std::vector<uintptr_t> callees3 = {0}; |
| // PC 99 should have no effect on computed weight. |
| std::vector<uintptr_t> callees4 = {1, 3, 99}; |
| |
| auto weight1 = ComputeFrontierWeight(g_coverage, cfg, callees1); |
| ASSERT_EQ(weight1, 408); |
| |
| auto weight2 = ComputeFrontierWeight(g_coverage, cfg, callees2); |
| ASSERT_EQ(weight2, 102); |
| |
| auto weight3 = ComputeFrontierWeight(g_coverage, cfg, callees3); |
| ASSERT_EQ(weight3, 25); |
| |
| auto weight4 = ComputeFrontierWeight(g_coverage, cfg, callees4); |
| ASSERT_EQ(weight4, 230); |
| } |
| |
| TEST(FrontierWeightDeath, InvalidCallee) { |
| // Makes call to ComputeFrontierWeight with some non-function PCs. |
| PCTable g_pc_table{{0, PCInfo::kFuncEntry}, {1, 0}, {2, 0}}; |
| CFTable g_cf_table{0, 1, 0, 0, 1, 2, 0, 0, 2, 0, 0}; |
| Coverage g_coverage(g_pc_table, {0, 1}); |
| ControlFlowGraph cfg; |
| cfg.InitializeControlFlowGraph(g_cf_table, g_pc_table); |
| EXPECT_DEATH(ComputeFrontierWeight(g_coverage, cfg, {0, 1}), ""); |
| EXPECT_DEATH(ComputeFrontierWeight(g_coverage, cfg, {1, 2}), ""); |
| } |
| |
| } // namespace |
| } // namespace fuzztest::internal |