Commit e63b3706 authored by Istiaque Ahmed's avatar Istiaque Ahmed Committed by Commit Bot

Simplify ContentHashReader lifetime and creation.

Instead of creating CHR and then calling CHR::Init(), create it
in one go (in a blocking allowed thread). The instance is immuatable and
stored inside ContentVerifyJob, hence CHR doens't need to be
RefCountedThreadSafe anymore.

ContentHashReader only needs extension related information on creation,
so move all such params from the class members (to ContentVerifyJob).

Bug: 796395
Change-Id: Ia65938d96e99d5c95562c75920cd614935026240
Reviewed-on: https://chromium-review.googlesource.com/917007Reviewed-by: default avatarIstiaque Ahmed <lazyboy@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Commit-Queue: Istiaque Ahmed <lazyboy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#538994}
parent 1a648192
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "extensions/browser/content_hash_reader.h" #include "extensions/browser/content_hash_reader.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/timer/elapsed_timer.h" #include "base/timer/elapsed_timer.h"
...@@ -17,41 +18,36 @@ ...@@ -17,41 +18,36 @@
namespace extensions { namespace extensions {
ContentHashReader::ContentHashReader(const std::string& extension_id, ContentHashReader::ContentHashReader() {}
const base::Version& extension_version,
const base::FilePath& extension_root, ContentHashReader::~ContentHashReader() {}
const base::FilePath& relative_path,
const ContentVerifierKey& key)
: extension_id_(extension_id),
extension_version_(extension_version.GetString()),
extension_root_(extension_root),
relative_path_(relative_path),
key_(key) {}
ContentHashReader::~ContentHashReader() {
}
bool ContentHashReader::Init() { // static.
std::unique_ptr<const ContentHashReader> ContentHashReader::Create(
const ExtensionId& extension_id,
const base::Version& extension_version,
const base::FilePath& extension_root,
const base::FilePath& relative_path,
const ContentVerifierKey& key) {
base::ElapsedTimer timer; base::ElapsedTimer timer;
DCHECK_EQ(status_, NOT_INITIALIZED);
status_ = FAILURE;
std::unique_ptr<ContentHash> content_hash = std::unique_ptr<ContentHash> content_hash =
ContentHash::Create(ContentHash::ExtensionKey( ContentHash::Create(ContentHash::ExtensionKey(
extension_id_, extension_root_, extension_version_, key_)); extension_id, extension_root, extension_version, key));
auto hash_reader = base::WrapUnique(new ContentHashReader);
if (!content_hash->succeeded()) if (!content_hash->succeeded())
return false; return hash_reader; // FAILURE.
has_content_hashes_ = true; hash_reader->has_content_hashes_ = true;
const VerifiedContents& verified_contents = content_hash->verified_contents(); const VerifiedContents& verified_contents = content_hash->verified_contents();
// Extensions sometimes request resources that do not have an entry in // Extensions sometimes request resources that do not have an entry in
// verified_contents.json. This can happen when an extension sends an XHR to a // verified_contents.json. This can happen when an extension sends an XHR to a
// resource. // resource.
if (!verified_contents.HasTreeHashRoot(relative_path_)) { if (!verified_contents.HasTreeHashRoot(relative_path)) {
base::FilePath full_path = extension_root_.Append(relative_path_); base::FilePath full_path = extension_root.Append(relative_path);
// Making a request to a non-existent file or to a directory should not // Making a request to a non-existent file or to a directory should not
// result in content verification failure. // result in content verification failure.
// TODO(proberge): This logic could be simplified if |content_verify_job| // TODO(proberge): This logic could be simplified if |content_verify_job|
...@@ -59,35 +55,34 @@ bool ContentHashReader::Init() { ...@@ -59,35 +55,34 @@ bool ContentHashReader::Init() {
// A content verification failure should be triggered if there is a mismatch // A content verification failure should be triggered if there is a mismatch
// between the file read state and the existence of verification hashes. // between the file read state and the existence of verification hashes.
if (!base::PathExists(full_path) || base::DirectoryExists(full_path)) if (!base::PathExists(full_path) || base::DirectoryExists(full_path))
file_missing_from_verified_contents_ = true; hash_reader->file_missing_from_verified_contents_ = true;
return false; return hash_reader; // FAILURE.
} }
const ComputedHashes::Reader& reader = content_hash->computed_hashes(); const ComputedHashes::Reader& reader = content_hash->computed_hashes();
if (!reader.GetHashes(relative_path_, &block_size_, &hashes_) || if (!reader.GetHashes(relative_path, &hash_reader->block_size_,
block_size_ % crypto::kSHA256Length != 0) { &hash_reader->hashes_) ||
return false; hash_reader->block_size_ % crypto::kSHA256Length != 0) {
return hash_reader;
} }
std::string root = std::string root = ComputeTreeHashRoot(
ComputeTreeHashRoot(hashes_, block_size_ / crypto::kSHA256Length); hash_reader->hashes_, hash_reader->block_size_ / crypto::kSHA256Length);
if (!verified_contents.TreeHashRootEquals(relative_path_, root)) if (!verified_contents.TreeHashRootEquals(relative_path, root))
return false; return hash_reader;
status_ = SUCCESS; hash_reader->status_ = SUCCESS;
UMA_HISTOGRAM_TIMES("ExtensionContentHashReader.InitLatency", UMA_HISTOGRAM_TIMES("ExtensionContentHashReader.InitLatency",
timer.Elapsed()); timer.Elapsed());
return true; return hash_reader; // SUCCESS.
} }
int ContentHashReader::block_count() const { int ContentHashReader::block_count() const {
DCHECK(status_ != NOT_INITIALIZED);
return hashes_.size(); return hashes_.size();
} }
int ContentHashReader::block_size() const { int ContentHashReader::block_size() const {
DCHECK(status_ != NOT_INITIALIZED);
return block_size_; return block_size_;
} }
......
...@@ -13,35 +13,35 @@ ...@@ -13,35 +13,35 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/version.h" #include "base/version.h"
#include "extensions/browser/content_verifier_delegate.h" #include "extensions/browser/content_verifier/content_verifier_key.h"
#include "extensions/common/extension_id.h"
namespace extensions { namespace extensions {
// This class creates an object that will read expected hashes that may have // This class creates an object that will read expected hashes that may have
// been fetched/calculated by the ContentHashFetcher, and vends them out for // been fetched/calculated by the ContentHashFetcher, and vends them out for
// use in ContentVerifyJob's. // use in ContentVerifyJob's.
class ContentHashReader : public base::RefCountedThreadSafe<ContentHashReader> { class ContentHashReader {
public: public:
// Create one of these to get expected hashes for the file at |relative_path| ~ContentHashReader();
// within an extension.
ContentHashReader(const std::string& extension_id, // Factory to create ContentHashReader to get expected hashes for the file at
const base::Version& extension_version, // |relative_path| within an extension.
const base::FilePath& extension_root, // Must be called on a thread that is allowed to do file I/O. Returns an
const base::FilePath& relative_path, // instance whose succees/failure can be determined by calling succeeded()
const ContentVerifierKey& key); // method. On failure, this object should likely be discarded.
static std::unique_ptr<const ContentHashReader> Create(
const std::string& extension_id() const { return extension_id_; } const ExtensionId& extension_id,
const base::FilePath& relative_path() const { return relative_path_; } const base::Version& extension_version,
const base::FilePath& extension_root,
// This should be called to initialize this object (reads the expected hashes const base::FilePath& relative_path,
// from storage, etc.). Must be called on a thread that is allowed to do file const ContentVerifierKey& key);
// I/O. Returns a boolean indicating success/failure. On failure, this object
// should likely be discarded. bool succeeded() const { return status_ == SUCCESS; }
bool Init();
// Returns true if we found valid verified_contents.json and // Returns true if we found valid verified_contents.json and
// computed_hashes.json files. Note that this can be true even if we didn't // computed_hashes.json files. Note that this can be true even if we didn't
// find an entry for |relative_path_| in them. // find an entry for |relative_path| in them.
bool has_content_hashes() const { return has_content_hashes_; } bool has_content_hashes() const { return has_content_hashes_; }
// Returns whether or not this resource's entry exists in // Returns whether or not this resource's entry exists in
// verified_contents.json (given that |has_content_hashes_| is true. // verified_contents.json (given that |has_content_hashes_| is true.
...@@ -59,18 +59,11 @@ class ContentHashReader : public base::RefCountedThreadSafe<ContentHashReader> { ...@@ -59,18 +59,11 @@ class ContentHashReader : public base::RefCountedThreadSafe<ContentHashReader> {
bool GetHashForBlock(int block_index, const std::string** result) const; bool GetHashForBlock(int block_index, const std::string** result) const;
private: private:
friend class base::RefCountedThreadSafe<ContentHashReader>; enum InitStatus { SUCCESS, FAILURE };
virtual ~ContentHashReader();
enum InitStatus { NOT_INITIALIZED, SUCCESS, FAILURE }; ContentHashReader();
std::string extension_id_; InitStatus status_ = FAILURE;
base::Version extension_version_;
base::FilePath extension_root_;
base::FilePath relative_path_;
ContentVerifierKey key_;
InitStatus status_ = NOT_INITIALIZED;
bool has_content_hashes_ = false; bool has_content_hashes_ = false;
bool file_missing_from_verified_contents_ = false; bool file_missing_from_verified_contents_ = false;
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h" #include "content/public/browser/storage_partition.h"
#include "extensions/browser/content_hash_fetcher.h" #include "extensions/browser/content_hash_fetcher.h"
#include "extensions/browser/content_hash_reader.h"
#include "extensions/browser/content_verifier_delegate.h" #include "extensions/browser/content_verifier_delegate.h"
#include "extensions/browser/content_verifier_io_data.h" #include "extensions/browser/content_verifier_io_data.h"
#include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry.h"
...@@ -124,8 +123,8 @@ ContentVerifyJob* ContentVerifier::CreateJobFor( ...@@ -124,8 +123,8 @@ ContentVerifyJob* ContentVerifier::CreateJobFor(
// TODO(asargent) - we can probably get some good performance wins by having // TODO(asargent) - we can probably get some good performance wins by having
// a cache of ContentHashReader's that we hold onto past the end of each job. // a cache of ContentHashReader's that we hold onto past the end of each job.
return new ContentVerifyJob( return new ContentVerifyJob(
new ContentHashReader(extension_id, data->version, extension_root, extension_id, data->version, extension_root, normalized_unix_path,
normalized_unix_path, delegate_->GetPublicKey()), delegate_->GetPublicKey(),
base::BindOnce(&ContentVerifier::VerifyFailed, this, extension_id)); base::BindOnce(&ContentVerifier::VerifyFailed, this, extension_id));
} }
......
...@@ -40,14 +40,22 @@ class ScopedElapsedTimer { ...@@ -40,14 +40,22 @@ class ScopedElapsedTimer {
} // namespace } // namespace
ContentVerifyJob::ContentVerifyJob(ContentHashReader* hash_reader, ContentVerifyJob::ContentVerifyJob(const ExtensionId& extension_id,
const base::Version& extension_version,
const base::FilePath& extension_root,
const base::FilePath& relative_path,
const ContentVerifierKey& key,
FailureCallback failure_callback) FailureCallback failure_callback)
: done_reading_(false), : done_reading_(false),
hashes_ready_(false), hashes_ready_(false),
total_bytes_read_(0), total_bytes_read_(0),
current_block_(0), current_block_(0),
current_hash_byte_count_(0), current_hash_byte_count_(0),
hash_reader_(hash_reader), extension_id_(extension_id),
extension_version_(extension_version),
extension_root_(extension_root),
relative_path_(relative_path),
content_verifier_key_(key),
failure_callback_(std::move(failure_callback)), failure_callback_(std::move(failure_callback)),
failed_(false) {} failed_(false) {}
...@@ -59,12 +67,14 @@ ContentVerifyJob::~ContentVerifyJob() { ...@@ -59,12 +67,14 @@ ContentVerifyJob::~ContentVerifyJob() {
void ContentVerifyJob::Start() { void ContentVerifyJob::Start() {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
if (g_content_verify_job_test_observer) if (g_content_verify_job_test_observer)
g_content_verify_job_test_observer->JobStarted( g_content_verify_job_test_observer->JobStarted(extension_id_,
hash_reader_->extension_id(), hash_reader_->relative_path()); relative_path_);
base::PostTaskWithTraitsAndReplyWithResult( base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE}, FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::Bind(&ContentHashReader::Init, hash_reader_), base::BindOnce(&ContentHashReader::Create, extension_id_,
base::Bind(&ContentVerifyJob::OnHashesReady, this)); extension_version_, extension_root_, relative_path_,
content_verifier_key_),
base::BindOnce(&ContentVerifyJob::OnHashesReady, this));
} }
void ContentVerifyJob::BytesRead(int count, const char* data) { void ContentVerifyJob::BytesRead(int count, const char* data) {
...@@ -84,8 +94,8 @@ void ContentVerifyJob::DoneReading() { ...@@ -84,8 +94,8 @@ void ContentVerifyJob::DoneReading() {
if (!FinishBlock()) { if (!FinishBlock()) {
DispatchFailureCallback(HASH_MISMATCH); DispatchFailureCallback(HASH_MISMATCH);
} else if (g_content_verify_job_test_observer) { } else if (g_content_verify_job_test_observer) {
g_content_verify_job_test_observer->JobFinished( g_content_verify_job_test_observer->JobFinished(extension_id_,
hash_reader_->extension_id(), hash_reader_->relative_path(), NONE); relative_path_, NONE);
} }
} }
} }
...@@ -161,18 +171,19 @@ bool ContentVerifyJob::FinishBlock() { ...@@ -161,18 +171,19 @@ bool ContentVerifyJob::FinishBlock() {
return true; return true;
} }
void ContentVerifyJob::OnHashesReady(bool success) { void ContentVerifyJob::OnHashesReady(
std::unique_ptr<const ContentHashReader> hash_reader) {
base::AutoLock auto_lock(lock_);
const bool success = hash_reader->succeeded();
hash_reader_ = std::move(hash_reader);
if (g_ignore_verification_for_tests) if (g_ignore_verification_for_tests)
return; return;
base::AutoLock auto_lock(lock_);
if (g_content_verify_job_test_observer) { if (g_content_verify_job_test_observer) {
g_content_verify_job_test_observer->OnHashesReady( g_content_verify_job_test_observer->OnHashesReady(extension_id_,
hash_reader_->extension_id(), hash_reader_->relative_path(), success); relative_path_, success);
} }
if (!success) { if (!success) {
// TODO(lazyboy): Make ContentHashReader::Init return an enum instead of
// bool. This should make the following checks on |hash_reader_| easier
// to digest and will avoid future bugs from creeping up.
if (!hash_reader_->has_content_hashes()) { if (!hash_reader_->has_content_hashes()) {
DispatchFailureCallback(MISSING_ALL_HASHES); DispatchFailureCallback(MISSING_ALL_HASHES);
return; return;
...@@ -181,8 +192,8 @@ void ContentVerifyJob::OnHashesReady(bool success) { ...@@ -181,8 +192,8 @@ void ContentVerifyJob::OnHashesReady(bool success) {
if (hash_reader_->file_missing_from_verified_contents()) { if (hash_reader_->file_missing_from_verified_contents()) {
// Ignore verification of non-existent resources. // Ignore verification of non-existent resources.
if (g_content_verify_job_test_observer) { if (g_content_verify_job_test_observer) {
g_content_verify_job_test_observer->JobFinished( g_content_verify_job_test_observer->JobFinished(extension_id_,
hash_reader_->extension_id(), hash_reader_->relative_path(), NONE); relative_path_, NONE);
} }
return; return;
} }
...@@ -205,8 +216,8 @@ void ContentVerifyJob::OnHashesReady(bool success) { ...@@ -205,8 +216,8 @@ void ContentVerifyJob::OnHashesReady(bool success) {
if (!FinishBlock()) { if (!FinishBlock()) {
DispatchFailureCallback(HASH_MISMATCH); DispatchFailureCallback(HASH_MISMATCH);
} else if (g_content_verify_job_test_observer) { } else if (g_content_verify_job_test_observer) {
g_content_verify_job_test_observer->JobFinished( g_content_verify_job_test_observer->JobFinished(extension_id_,
hash_reader_->extension_id(), hash_reader_->relative_path(), NONE); relative_path_, NONE);
} }
} }
} }
...@@ -229,14 +240,13 @@ void ContentVerifyJob::DispatchFailureCallback(FailureReason reason) { ...@@ -229,14 +240,13 @@ void ContentVerifyJob::DispatchFailureCallback(FailureReason reason) {
DCHECK(!failed_); DCHECK(!failed_);
failed_ = true; failed_ = true;
if (!failure_callback_.is_null()) { if (!failure_callback_.is_null()) {
VLOG(1) << "job failed for " << hash_reader_->extension_id() << " " VLOG(1) << "job failed for " << extension_id_ << " "
<< hash_reader_->relative_path().MaybeAsASCII() << relative_path_.MaybeAsASCII() << " reason:" << reason;
<< " reason:" << reason;
std::move(failure_callback_).Run(reason); std::move(failure_callback_).Run(reason);
} }
if (g_content_verify_job_test_observer) { if (g_content_verify_job_test_observer) {
g_content_verify_job_test_observer->JobFinished( g_content_verify_job_test_observer->JobFinished(extension_id_,
hash_reader_->extension_id(), hash_reader_->relative_path(), reason); relative_path_, reason);
} }
} }
......
...@@ -11,9 +11,12 @@ ...@@ -11,9 +11,12 @@
#include <string> #include <string>
#include "base/callback.h" #include "base/callback.h"
#include "base/files/file_path.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/version.h"
#include "extensions/browser/content_verifier/content_verifier_key.h"
#include "extensions/common/extension_id.h" #include "extensions/common/extension_id.h"
namespace base { namespace base {
...@@ -52,7 +55,11 @@ class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> { ...@@ -52,7 +55,11 @@ class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> {
using FailureCallback = base::OnceCallback<void(FailureReason)>; using FailureCallback = base::OnceCallback<void(FailureReason)>;
// The |failure_callback| will be called at most once if there was a failure. // The |failure_callback| will be called at most once if there was a failure.
ContentVerifyJob(ContentHashReader* hash_reader, ContentVerifyJob(const ExtensionId& extension_id,
const base::Version& extension_version,
const base::FilePath& extension_root,
const base::FilePath& relative_path,
const ContentVerifierKey& content_verifier_key,
FailureCallback failure_callback); FailureCallback failure_callback);
// This begins the process of getting expected hashes, so it should be called // This begins the process of getting expected hashes, so it should be called
...@@ -106,7 +113,7 @@ class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> { ...@@ -106,7 +113,7 @@ class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> {
void DispatchFailureCallback(FailureReason reason); void DispatchFailureCallback(FailureReason reason);
// Called when our ContentHashReader has finished initializing. // Called when our ContentHashReader has finished initializing.
void OnHashesReady(bool success); void OnHashesReady(std::unique_ptr<const ContentHashReader> hash_reader);
// Indicates whether the caller has told us they are done calling BytesRead. // Indicates whether the caller has told us they are done calling BytesRead.
bool done_reading_; bool done_reading_;
...@@ -130,7 +137,16 @@ class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> { ...@@ -130,7 +137,16 @@ class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> {
// The number of bytes we've already input into |current_hash_|. // The number of bytes we've already input into |current_hash_|.
int current_hash_byte_count_; int current_hash_byte_count_;
scoped_refptr<ContentHashReader> hash_reader_; // Valid and set after |hashes_ready_| is set to true.
std::unique_ptr<const ContentHashReader> hash_reader_;
// Resource info for this verify job.
const ExtensionId extension_id_;
const base::Version extension_version_;
const base::FilePath extension_root_;
const base::FilePath relative_path_;
const ContentVerifierKey content_verifier_key_;
base::TimeDelta time_spent_; base::TimeDelta time_spent_;
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include "base/version.h" #include "base/version.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_browser_thread_bundle.h"
#include "extensions/browser/content_hash_reader.h"
#include "extensions/browser/content_verifier/test_utils.h" #include "extensions/browser/content_verifier/test_utils.h"
#include "extensions/browser/extensions_test.h" #include "extensions/browser/extensions_test.h"
#include "extensions/common/constants.h" #include "extensions/common/constants.h"
...@@ -37,16 +36,6 @@ enum ContentVerifyJobAsyncRunMode { ...@@ -37,16 +36,6 @@ enum ContentVerifyJobAsyncRunMode {
kHashesReadyBeforeContentRead, kHashesReadyBeforeContentRead,
}; };
scoped_refptr<ContentHashReader> CreateContentHashReader(
const Extension& extension,
const base::FilePath& extension_resource_path) {
return base::MakeRefCounted<ContentHashReader>(
extension.id(), extension.version(), extension.path(),
extension_resource_path,
ContentVerifierKey(kWebstoreSignaturesPublicKey,
kWebstoreSignaturesPublicKeySize));
}
} // namespace } // namespace
class ContentVerifyJobUnittest : public ExtensionsTest { class ContentVerifyJobUnittest : public ExtensionsTest {
...@@ -73,10 +62,11 @@ class ContentVerifyJobUnittest : public ExtensionsTest { ...@@ -73,10 +62,11 @@ class ContentVerifyJobUnittest : public ExtensionsTest {
std::string& resource_contents, std::string& resource_contents,
ContentVerifyJobAsyncRunMode run_mode) { ContentVerifyJobAsyncRunMode run_mode) {
TestContentVerifyJobObserver observer(extension.id(), resource_path); TestContentVerifyJobObserver observer(extension.id(), resource_path);
scoped_refptr<ContentHashReader> content_hash_reader = scoped_refptr<ContentVerifyJob> verify_job = new ContentVerifyJob(
CreateContentHashReader(extension, resource_path); extension.id(), extension.version(), extension.path(), resource_path,
scoped_refptr<ContentVerifyJob> verify_job = ContentVerifierKey(kWebstoreSignaturesPublicKey,
new ContentVerifyJob(content_hash_reader.get(), base::DoNothing()); kWebstoreSignaturesPublicKeySize),
base::DoNothing());
auto run_content_read_step = [](ContentVerifyJob* verify_job, auto run_content_read_step = [](ContentVerifyJob* verify_job,
std::string* resource_contents) { std::string* resource_contents) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment