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.
178 lines
5.9 KiB
178 lines
5.9 KiB
// 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/runner_result.h"
|
|
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <string_view>
|
|
|
|
#include "./centipede/execution_metadata.h"
|
|
#include "./centipede/feature.h"
|
|
#include "./centipede/shared_memory_blob_sequence.h"
|
|
#include "./common/defs.h"
|
|
|
|
namespace fuzztest::internal {
|
|
|
|
namespace {
|
|
|
|
// Tags used for both the execution and mutation results. We use the same enum
|
|
// to make the sets of tags disjoint.
|
|
enum Tags : Blob::SizeAndTagT {
|
|
kTagInvalid, // 0 is an invalid tag.
|
|
|
|
// Execution result tags.
|
|
kTagFeatures,
|
|
kTagInputBegin,
|
|
kTagInputEnd,
|
|
kTagStats,
|
|
kTagMetadata,
|
|
|
|
// Mutation result tags.
|
|
kTagHasCustomMutator,
|
|
kTagMutant,
|
|
};
|
|
|
|
} // namespace
|
|
|
|
bool BatchResult::WriteOneFeatureVec(const feature_t *vec, size_t size,
|
|
BlobSequence &blobseq) {
|
|
return blobseq.Write({kTagFeatures, size * sizeof(vec[0]),
|
|
reinterpret_cast<const uint8_t *>(vec)});
|
|
}
|
|
|
|
bool BatchResult::WriteInputBegin(BlobSequence &blobseq) {
|
|
return blobseq.Write({kTagInputBegin, 0, nullptr});
|
|
}
|
|
|
|
bool BatchResult::WriteInputEnd(BlobSequence &blobseq) {
|
|
return blobseq.Write({kTagInputEnd, 0, nullptr});
|
|
}
|
|
|
|
bool BatchResult::WriteStats(const ExecutionResult::Stats &stats,
|
|
BlobSequence &blobseq) {
|
|
return blobseq.Write(
|
|
{kTagStats, sizeof(stats), reinterpret_cast<const uint8_t *>(&stats)});
|
|
}
|
|
|
|
bool BatchResult::WriteMetadata(const ExecutionMetadata &metadata,
|
|
BlobSequence &blobseq) {
|
|
return metadata.Write(kTagMetadata, blobseq);
|
|
}
|
|
|
|
// The sequence we expect to receive is
|
|
// InputBegin, Features, Stats, InputEnd, InputBegin, ...
|
|
// with a total of results().size() tuples (InputBegin ... InputEnd).
|
|
// Blobs between InputBegin/InputEnd may go in any order.
|
|
// If the execution failed on some input, we will see InputBegin,
|
|
// but will not see all or some other blobs.
|
|
bool BatchResult::Read(BlobSequence &blobseq) {
|
|
size_t num_begins = 0;
|
|
size_t num_ends = 0;
|
|
const size_t num_expected_tuples = results().size();
|
|
ExecutionResult *current_execution_result = nullptr;
|
|
while (true) {
|
|
auto blob = blobseq.Read();
|
|
if (!blob.IsValid()) break;
|
|
if (blob.tag == kTagInputBegin) {
|
|
if (num_begins != num_ends) return false;
|
|
++num_begins;
|
|
if (num_begins > num_expected_tuples) return false;
|
|
current_execution_result = &results()[num_ends];
|
|
current_execution_result->clear();
|
|
continue;
|
|
}
|
|
if (blob.tag == kTagInputEnd) {
|
|
++num_ends;
|
|
if (num_ends != num_begins) return false;
|
|
current_execution_result = nullptr;
|
|
continue;
|
|
}
|
|
if (blob.tag == kTagMetadata) {
|
|
if (current_execution_result == nullptr) return false;
|
|
current_execution_result->metadata().Read(blob);
|
|
continue;
|
|
}
|
|
if (blob.tag == kTagStats) {
|
|
if (current_execution_result == nullptr) return false;
|
|
if (blob.size != sizeof(ExecutionResult::Stats)) return false;
|
|
memcpy(¤t_execution_result->stats(), blob.data, blob.size);
|
|
continue;
|
|
}
|
|
if (blob.tag == kTagFeatures) {
|
|
if (current_execution_result == nullptr) return false;
|
|
const size_t features_size = blob.size / sizeof(feature_t);
|
|
FeatureVec &features = current_execution_result->mutable_features();
|
|
features.resize(features_size);
|
|
std::memcpy(features.data(), blob.data,
|
|
features_size * sizeof(feature_t));
|
|
}
|
|
}
|
|
num_outputs_read_ = num_ends;
|
|
return true;
|
|
}
|
|
|
|
bool BatchResult::IsIgnoredFailure() const {
|
|
constexpr std::string_view kIgnoredFailurePrefix = "IGNORED FAILURE:";
|
|
return exit_code_ != EXIT_SUCCESS &&
|
|
std::string_view(failure_description_)
|
|
.substr(0, kIgnoredFailurePrefix.size()) ==
|
|
kIgnoredFailurePrefix;
|
|
}
|
|
|
|
bool BatchResult::IsSetupFailure() const {
|
|
constexpr std::string_view kSetupFailurePrefix = "SETUP FAILURE:";
|
|
return exit_code_ != EXIT_SUCCESS &&
|
|
std::string_view(failure_description_)
|
|
.substr(0, kSetupFailurePrefix.size()) == kSetupFailurePrefix;
|
|
}
|
|
|
|
bool BatchResult::IsSkippedTest() const {
|
|
constexpr std::string_view kSkippedTestPrefix = "SKIPPED TEST:";
|
|
return exit_code_ != EXIT_SUCCESS &&
|
|
std::string_view(failure_description_)
|
|
.substr(0, kSkippedTestPrefix.size()) == kSkippedTestPrefix;
|
|
}
|
|
|
|
bool MutationResult::WriteHasCustomMutator(bool has_custom_mutator,
|
|
BlobSequence &blobseq) {
|
|
return blobseq.Write(
|
|
{kTagHasCustomMutator, sizeof(has_custom_mutator),
|
|
reinterpret_cast<const uint8_t *>(&has_custom_mutator)});
|
|
}
|
|
|
|
bool MutationResult::WriteMutant(ByteSpan mutant, BlobSequence &blobseq) {
|
|
return blobseq.Write({kTagMutant, mutant.size(), mutant.data()});
|
|
}
|
|
|
|
bool MutationResult::Read(size_t num_mutants, BlobSequence &blobseq) {
|
|
const Blob blob = blobseq.Read();
|
|
if (blob.tag != kTagHasCustomMutator) return false;
|
|
if (blob.size != sizeof(has_custom_mutator_)) return false;
|
|
std::memcpy(&has_custom_mutator_, blob.data, blob.size);
|
|
if (!has_custom_mutator_) return true;
|
|
|
|
mutants_.clear();
|
|
mutants_.reserve(num_mutants);
|
|
for (size_t i = 0; i < num_mutants; ++i) {
|
|
const Blob blob = blobseq.Read();
|
|
if (blob.tag != kTagMutant) return false;
|
|
if (blob.size == 0) break;
|
|
mutants_.emplace_back(blob.data, blob.data + blob.size);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace fuzztest::internal
|
|
|