// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/web_package/shared_file.h" #include #include "base/numerics/safe_math.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "mojo/public/cpp/system/file_data_source.h" namespace web_package { SharedFile::SharedFile(std::unique_ptr file) : task_runner_( base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})), file_(std::move(file)) {} void SharedFile::DuplicateFile(base::OnceCallback callback) { DCHECK(file_); task_runner_->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce( [](base::File* file) -> base::File { return file->Duplicate(); }, file_.get()), std::move(callback)); } base::File* SharedFile::operator->() { DCHECK(file_); return file_.get(); } SharedFile::~SharedFile() { // Move the last reference to `file_` whose destructor leads to blocking call // that is not permitted here. // It is important that this runs on the same `base::SequencedTaskRunner` that // the callback scheduled in `DuplicateFile` runs on. Otherwise deleting the // file might lead to a UAF inside the file duplication callback if that one // happened to run after the deletion of the file. task_runner_->DeleteSoon(FROM_HERE, std::move(file_)); } std::unique_ptr SharedFile::CreateDataSource( uint64_t offset, uint64_t length) { return std::make_unique(this, offset, length); } SharedFile::SharedFileDataSource::SharedFileDataSource( scoped_refptr file, uint64_t offset, uint64_t length) : file_(std::move(file)), offset_(offset), length_(length) { error_ = mojo::FileDataSource::ConvertFileErrorToMojoResult( (*file_)->error_details()); // base::File::Read takes int64_t as an offset. So, offset + length should // not overflow in int64_t. uint64_t max_offset; if (!base::CheckAdd(offset, length).AssignIfValid(&max_offset) || (std::numeric_limits::max() < max_offset)) { error_ = MOJO_RESULT_INVALID_ARGUMENT; } } SharedFile::SharedFileDataSource::~SharedFileDataSource() = default; uint64_t SharedFile::SharedFileDataSource::GetLength() const { return length_; } SharedFile::SharedFileDataSource::ReadResult SharedFile::SharedFileDataSource::Read(uint64_t offset, base::span buffer) { ReadResult result; result.result = error_; if (length_ < offset) result.result = MOJO_RESULT_INVALID_ARGUMENT; if (result.result != MOJO_RESULT_OK) return result; uint64_t readable_size = length_ - offset; uint64_t writable_size = buffer.size(); uint64_t copyable_size = std::min(std::min(readable_size, writable_size), static_cast(std::numeric_limits::max())); int bytes_read = (*file_)->Read(offset_ + offset, buffer.data(), copyable_size); if (bytes_read < 0) { result.result = mojo::FileDataSource::ConvertFileErrorToMojoResult( (*file_)->GetLastFileError()); } else { result.bytes_read = bytes_read; } return result; } } // namespace web_package