blob: 88aab6f6140d57cc4239a1358e7d7b9260a8b53a [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/storage/blobfs/blob_verifier.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/trace/event.h>
#include <zircon/status.h>
#include <safemath/checked_math.h>
#include "src/lib/digest/digest.h"
#include "src/lib/digest/merkle-tree.h"
#include "src/storage/blobfs/blob_layout.h"
namespace blobfs {
BlobVerifier::BlobVerifier(digest::Digest digest, std::shared_ptr<BlobfsMetrics> metrics)
: digest_(std::move(digest)), metrics_(std::move(metrics)) {}
zx::result<std::unique_ptr<BlobVerifier>> BlobVerifier::Create(
digest::Digest digest, std::shared_ptr<BlobfsMetrics> metrics,
cpp20::span<const uint8_t> merkle_data_blocks, const BlobLayout& layout) {
std::unique_ptr<BlobVerifier> verifier(new BlobVerifier(std::move(digest), std::move(metrics)));
verifier->tree_verifier_.SetUseCompactFormat(ShouldUseCompactMerkleTreeFormat(layout.Format()));
if (zx_status_t status = verifier->tree_verifier_.SetDataLength(layout.FileSize());
status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to set merkle data length: " << zx_status_get_string(status);
return zx::error(status);
}
size_t actual_merkle_length = verifier->tree_verifier_.GetTreeLength();
if (actual_merkle_length > layout.MerkleTreeSize() ||
layout.MerkleTreeOffsetWithinBlockOffset() + actual_merkle_length >
merkle_data_blocks.size()) {
FX_LOGS(ERROR) << "merkle too small for data";
return zx::error(ZX_ERR_BUFFER_TOO_SMALL);
}
const uint8_t* merkle_tree_data =
merkle_data_blocks.data() + layout.MerkleTreeOffsetWithinBlockOffset();
verifier->merkle_data_ = std::make_unique<uint8_t[]>(actual_merkle_length);
memcpy(verifier->merkle_data_.get(), merkle_tree_data, actual_merkle_length);
if (zx_status_t status =
verifier->tree_verifier_.SetTree(verifier->merkle_data_.get(), actual_merkle_length,
verifier->digest_.get(), verifier->digest_.len());
status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to create merkle verifier: " << zx_status_get_string(status);
return zx::error(status);
}
return zx::ok(std::move(verifier));
}
zx::result<std::unique_ptr<BlobVerifier>> BlobVerifier::CreateWithoutTree(
digest::Digest digest, std::shared_ptr<BlobfsMetrics> metrics, size_t data_size) {
std::unique_ptr<BlobVerifier> verifier(new BlobVerifier(std::move(digest), std::move(metrics)));
if (zx_status_t status = verifier->tree_verifier_.SetDataLength(data_size); status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to set merkle data length: " << zx_status_get_string(status);
return zx::error(status);
} else if (verifier->tree_verifier_.GetTreeLength() > 0) {
FX_LOGS(ERROR) << "Failed to create merkle verifier -- data too big for empty tree";
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (zx_status_t status = verifier->tree_verifier_.SetTree(nullptr, 0, verifier->digest_.get(),
verifier->digest_.len());
status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to create merkle verifier: " << zx_status_get_string(status);
return zx::error(status);
}
return zx::ok(std::move(verifier));
}
zx_status_t VerifyTailZeroed(const void* data, size_t data_size, size_t buffer_size) {
size_t tail;
if (!safemath::CheckSub(buffer_size, data_size).AssignIfValid(&tail)) {
return ZX_ERR_INVALID_ARGS;
}
if (tail == 0) {
return ZX_OK;
}
// Check unaligned part first.
const uint8_t* u8_ptr = static_cast<const uint8_t*>(data) + data_size;
while (tail & 7) {
if (*u8_ptr) {
return ZX_ERR_IO_DATA_INTEGRITY;
}
++u8_ptr;
--tail;
}
// Check remaining aligned part.
const uint64_t* u64_ptr = reinterpret_cast<const uint64_t*>(u8_ptr);
while (tail > 0) {
if (*u64_ptr) {
return ZX_ERR_IO_DATA_INTEGRITY;
}
++u64_ptr;
tail -= 8;
}
return ZX_OK;
}
zx_status_t BlobVerifier::Verify(const void* data, size_t data_size, size_t buffer_size) {
TRACE_DURATION("blobfs", "BlobVerifier::Verify", "data_size", data_size);
fs::Ticker ticker;
zx_status_t status;
{
std::lock_guard l(verification_lock_);
status = tree_verifier_.Verify(data, data_size, 0);
}
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Verify(" << digest_.ToString() << ", " << data_size << ", " << buffer_size
<< ") failed: " << zx_status_get_string(status);
} else {
status = VerifyTailZeroed(data, data_size, buffer_size);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "VerifyTailZeroed(" << digest_.ToString() << ", " << data_size << ", "
<< buffer_size << ") failed: " << zx_status_get_string(status);
}
}
metrics_->verification_metrics().Increment(data_size, tree_verifier_.GetTreeLength(),
ticker.End());
return status;
}
zx_status_t BlobVerifier::VerifyPartial(const void* data, size_t length, size_t data_offset,
size_t buffer_size) {
TRACE_DURATION("blobfs", "BlobVerifier::VerifyPartial", "length", length, "offset", data_offset);
fs::Ticker ticker;
zx_status_t status;
{
std::lock_guard l(verification_lock_);
status = tree_verifier_.Verify(data, length, data_offset);
}
if (status != ZX_OK) {
FX_LOGS(ERROR) << "VerifyPartial(" << digest_.ToString() << ", " << data_offset << ", "
<< length << ", " << buffer_size << ") failed: " << zx_status_get_string(status);
} else {
status = VerifyTailZeroed(data, length, buffer_size);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "VerifyTailZeroed(" << digest_.ToString() << ", " << length << ", "
<< buffer_size << ") failed: " << zx_status_get_string(status);
}
}
metrics_->verification_metrics().Increment(length, tree_verifier_.GetTreeLength(), ticker.End());
return status;
}
} // namespace blobfs