chromium/remoting/host/file_transfer/ipc_file_operations.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/file_transfer/ipc_file_operations.h"

#include <cstdint>
#include <memory>
#include <utility>

#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "remoting/host/mojom/desktop_session.mojom.h"
#include "remoting/protocol/file_transfer_helpers.h"

namespace remoting {

// This is an overview of how IpcFileOperations is integrated and used in the
// multi-process host architecture. Reasoning about the lifetime and ownership
// of the various pieces currently requires digging through the code so this
// comment block describes the relationships and pieces involved at a high-level
// to help those looking to understand the code.
//
// The IpcFileOperations and related classes are all used in the low-privilege
// network process. They handle network communication with the website client
// over a WebRTC data channel and proxy those requests using Mojo to the
// SessionFileOperationsHandler (and friends) which lives in the high-privilege
// desktop process and handles the actual file reading and writing.
//
// When a new file transfer data channel is opened by the client, the
// ClientSession instance on the host (running in the network process) will
// create a FileTransferMessageHandler (FTMH) instance to service it. As part of
// the FTMH creation, ClientSession will ask the IpcDesktopEnvironment to create
// a new IpcFileOperations instance. This instance will be provided with a
// WeakPtr<IpcFileOperations::RequestHandler> which is used to start a file read
// or write operation in the desktop process over an existing IPC channel owned
// by the DesktopSessionProxy.
//
// After the FTMH receives the initial message indicating the type of operation
// to perform, it creates an IpcFileReader or an IpcFileWriter instance. The
// IpcFile{Reader|Writer} begins an operation by calling the appropriate method
// on the IpcFileOperations::RequestHandler interface. This interface is
// implemented by the DesktopSessionProxy (DSP) which in turn calls the
// DesktopSessionAgent (DSA) via its mojom::DesktopSessionControl remote. The
// DSA passes the request to its SessionFileOperationsHandler instance which, if
// successful, will create a new IPC channel for the transfer and return a
// remote to the IpcFile{Reader|Writer} to allow it to proceed with the file
// operation. The receiver is owned by a MojoFileReader or MojoFileWriter
// instance whose lifetime is tied to the Mojo channel meaning the
// MojoFile{Reader|Writer} will be destroyed when the channel is disconnected.
//
// The lifetime of an FTMH instance is tied to the WebRTC file transfer data
// channel that it was created to service. Each data channel exists for one
// transfer request, so once the operation completes, or encounters an error,
// the IpcFileOperations instance and the IpcFile{Reader|Writer} it created will
// be destroyed (this will also trigger destruction of a MojoFile{Reader|Writer}
// in the desktop process).
//
// The lifetime of the DesktopSessionProxy is a bit harder to reason about as a
// number of classes and callbacks hold a scoped_refptr reference to it. At the
// very earliest, the DSP will be destroyed when the chromoting session is
// terminated. When this occurs, the scoped_refptr in ClientSession is released
// and the IpcDesktopEnvironment and IpcFileOperationsFactory are destroyed.
//
// Because of the objects involved, the two UaF concerns are:
// - Calling into |request_handler_| after the DSP has been destroyed.
//   This is unlikely given that a DSP lasts for the entire session but it
//   could occur if the timing was just right near the end of a session.
//   Mitigation: |request_handler_| is wrapped in a WeakPtr and provided to each
//               IpcFile{Reader|Writer} instance.
// - The DSP could invoke a disconnect_handler on the IpcFile{Reader|Writer} if
//   the file transfer request was canceled just after the operation started.
//   Mitigation: The disconnect_handler callback provided to the BeginFileRead
//               BeginFileWrite method is bound with a WeakPtr.
class IpcFileOperations::IpcReader : public FileOperations::Reader {};

class IpcFileOperations::IpcWriter : public FileOperations::Writer {};

IpcFileOperations::IpcFileOperations(
    base::WeakPtr<RequestHandler> request_handler)
    :{}

IpcFileOperations::~IpcFileOperations() = default;

std::unique_ptr<FileOperations::Reader> IpcFileOperations::CreateReader() {}

std::unique_ptr<FileOperations::Writer> IpcFileOperations::CreateWriter() {}

IpcFileOperationsFactory::IpcFileOperationsFactory(
    IpcFileOperations::RequestHandler* request_handler)
    :{}

IpcFileOperationsFactory::~IpcFileOperationsFactory() = default;

std::unique_ptr<FileOperations>
IpcFileOperationsFactory::CreateFileOperations() {}

IpcFileOperations::IpcReader::IpcReader(
    base::WeakPtr<RequestHandler> request_handler)
    :{}

IpcFileOperations::IpcReader::~IpcReader() = default;

void IpcFileOperations::IpcReader::Open(OpenCallback callback) {}

void IpcFileOperations::IpcReader::ReadChunk(std::size_t size,
                                             ReadCallback callback) {}

const base::FilePath& IpcFileOperations::IpcReader::filename() const {}

std::uint64_t IpcFileOperations::IpcReader::size() const {}

FileOperations::State IpcFileOperations::IpcReader::state() const {}

void IpcFileOperations::IpcReader::OnChannelDisconnected() {}

base::WeakPtr<IpcFileOperations::IpcReader>
IpcFileOperations::IpcReader::GetWeakPtr() {}

void IpcFileOperations::IpcReader::OnOpenResult(
    mojom::BeginFileReadResultPtr result) {}

void IpcFileOperations::IpcReader::OnReadResult(
    const protocol::FileTransferResult<std::vector<std::uint8_t>>& result) {}

IpcFileOperations::IpcWriter::IpcWriter(
    base::WeakPtr<RequestHandler> request_handler)
    :{}

IpcFileOperations::IpcWriter::~IpcWriter() = default;

void IpcFileOperations::IpcWriter::Open(const base::FilePath& filename,
                                        Callback callback) {}

void IpcFileOperations::IpcWriter::WriteChunk(std::vector<std::uint8_t> data,
                                              Callback callback) {}

void IpcFileOperations::IpcWriter::Close(Callback callback) {}

FileOperations::State IpcFileOperations::IpcWriter::state() const {}

void IpcFileOperations::IpcWriter::OnChannelDisconnected() {}

base::WeakPtr<IpcFileOperations::IpcWriter>
IpcFileOperations::IpcWriter::GetWeakPtr() {}

void IpcFileOperations::IpcWriter::OnOpenResult(
    mojom::BeginFileWriteResultPtr result) {}

void IpcFileOperations::IpcWriter::OnOperationResult(
    const std::optional<::remoting::protocol::FileTransfer_Error>& error) {}

void IpcFileOperations::IpcWriter::OnCloseResult(
    const std::optional<::remoting::protocol::FileTransfer_Error>& error) {}

}  // namespace remoting