ipc_file_operations.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "remoting/host/file_transfer/ipc_file_operations.h"
  5. #include <cstdint>
  6. #include <memory>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/files/file_path.h"
  10. #include "base/memory/ptr_util.h"
  11. #include "base/memory/weak_ptr.h"
  12. #include "base/sequence_checker.h"
  13. #include "mojo/public/cpp/bindings/associated_remote.h"
  14. #include "remoting/host/mojom/desktop_session.mojom.h"
  15. #include "remoting/protocol/file_transfer_helpers.h"
  16. namespace remoting {
  17. // This is an overview of how IpcFileOperations is integrated and used in the
  18. // multi-process host architecture. Reasoning about the lifetime and ownership
  19. // of the various pieces currently requires digging through the code so this
  20. // comment block describes the relationships and pieces involved at a high-level
  21. // to help those looking to understand the code.
  22. //
  23. // The IpcFileOperations and related classes are all used in the low-privilege
  24. // network process. They handle network communication with the website client
  25. // over a WebRTC data channel and proxy those requests using Mojo to the
  26. // SessionFileOperationsHandler (and friends) which lives in the high-privilege
  27. // desktop process and handles the actual file reading and writing.
  28. //
  29. // When a new file transfer data channel is opened by the client, the
  30. // ClientSession instance on the host (running in the network process) will
  31. // create a FileTransferMessageHandler (FTMH) instance to service it. As part of
  32. // the FTMH creation, ClientSession will ask the IpcDesktopEnvironment to create
  33. // a new IpcFileOperations instance. This instance will be provided with a
  34. // WeakPtr<IpcFileOperations::RequestHandler> which is used to start a file read
  35. // or write operation in the desktop process over an existing IPC channel owned
  36. // by the DesktopSessionProxy.
  37. //
  38. // After the FTMH receives the initial message indicating the type of operation
  39. // to perform, it creates an IpcFileReader or an IpcFileWriter instance. The
  40. // IpcFile{Reader|Writer} begins an operation by calling the appropriate method
  41. // on the IpcFileOperations::RequestHandler interface. This interface is
  42. // implemented by the DesktopSessionProxy (DSP) which in turn calls the
  43. // DesktopSessionAgent (DSA) via its mojom::DesktopSessionControl remote. The
  44. // DSA passes the request to its SessionFileOperationsHandler instance which, if
  45. // successful, will create a new IPC channel for the transfer and return a
  46. // remote to the IpcFile{Reader|Writer} to allow it to proceed with the file
  47. // operation. The receiver is owned by a MojoFileReader or MojoFileWriter
  48. // instance whose lifetime is tied to the Mojo channel meaning the
  49. // MojoFile{Reader|Writer} will be destroyed when the channel is disconnected.
  50. //
  51. // The lifetime of an FTMH instance is tied to the WebRTC file transfer data
  52. // channel that it was created to service. Each data channel exists for one
  53. // transfer request, so once the operation completes, or encounters an error,
  54. // the IpcFileOperations instance and the IpcFile{Reader|Writer} it created will
  55. // be destroyed (this will also trigger destruction of a MojoFile{Reader|Writer}
  56. // in the desktop process).
  57. //
  58. // The lifetime of the DesktopSessionProxy is a bit harder to reason about as a
  59. // number of classes and callbacks hold a scoped_refptr reference to it. At the
  60. // very earliest, the DSP will be destroyed when the chromoting session is
  61. // terminated. When this occurs, the scoped_refptr in ClientSession is released
  62. // and the IpcDesktopEnvironment and IpcFileOperationsFactory are destroyed.
  63. //
  64. // Because of the objects involved, the two UaF concerns are:
  65. // - Calling into |request_handler_| after the DSP has been destroyed.
  66. // This is unlikely given that a DSP lasts for the entire session but it
  67. // could occur if the timing was just right near the end of a session.
  68. // Mitigation: |request_handler_| is wrapped in a WeakPtr and provided to each
  69. // IpcFile{Reader|Writer} instance.
  70. // - The DSP could invoke a disconnect_handler on the IpcFile{Reader|Writer} if
  71. // the file transfer request was canceled just after the operation started.
  72. // Mitigation: The disconnect_handler callback provided to the BeginFileRead
  73. // BeginFileWrite method is bound with a WeakPtr.
  74. class IpcFileOperations::IpcReader : public FileOperations::Reader {
  75. public:
  76. explicit IpcReader(base::WeakPtr<RequestHandler> request_handler);
  77. IpcReader(const IpcReader&) = delete;
  78. IpcReader& operator=(const IpcReader&) = delete;
  79. ~IpcReader() override;
  80. // FileOperations::Reader implementation.
  81. void Open(OpenCallback callback) override;
  82. void ReadChunk(std::size_t size, ReadCallback callback) override;
  83. const base::FilePath& filename() const override;
  84. std::uint64_t size() const override;
  85. State state() const override;
  86. void OnChannelDisconnected();
  87. base::WeakPtr<IpcReader> GetWeakPtr() const;
  88. private:
  89. void OnOpenResult(mojom::BeginFileReadResultPtr result);
  90. void OnReadResult(
  91. const protocol::FileTransferResult<std::vector<std::uint8_t>>& result);
  92. State state_ GUARDED_BY_CONTEXT(sequence_checker_) = kCreated;
  93. base::FilePath filename_ GUARDED_BY_CONTEXT(sequence_checker_);
  94. std::uint64_t size_ GUARDED_BY_CONTEXT(sequence_checker_) = 0;
  95. OpenCallback pending_open_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
  96. ReadCallback pending_read_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
  97. base::WeakPtr<IpcFileOperations::RequestHandler> request_handler_;
  98. mojo::AssociatedRemote<mojom::FileReader> remote_file_reader_
  99. GUARDED_BY_CONTEXT(sequence_checker_);
  100. SEQUENCE_CHECKER(sequence_checker_);
  101. base::WeakPtrFactory<IpcReader> weak_ptr_factory_{this};
  102. };
  103. class IpcFileOperations::IpcWriter : public FileOperations::Writer {
  104. public:
  105. explicit IpcWriter(base::WeakPtr<RequestHandler> request_handler);
  106. IpcWriter(const IpcWriter&) = delete;
  107. IpcWriter& operator=(const IpcWriter&) = delete;
  108. ~IpcWriter() override;
  109. // FileOperations::Writer implementation.
  110. void Open(const base::FilePath& filename, Callback callback) override;
  111. void WriteChunk(std::vector<std::uint8_t> data, Callback callback) override;
  112. void Close(Callback callback) override;
  113. State state() const override;
  114. void OnChannelDisconnected();
  115. base::WeakPtr<IpcWriter> GetWeakPtr() const;
  116. private:
  117. void OnOpenResult(mojom::BeginFileWriteResultPtr result);
  118. void OnOperationResult(
  119. const absl::optional<::remoting::protocol::FileTransfer_Error>& error);
  120. void OnCloseResult(
  121. const absl::optional<::remoting::protocol::FileTransfer_Error>& error);
  122. State state_ GUARDED_BY_CONTEXT(sequence_checker_) = kCreated;
  123. Callback pending_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
  124. base::WeakPtr<IpcFileOperations::RequestHandler> request_handler_;
  125. mojo::AssociatedRemote<mojom::FileWriter> remote_file_writer_
  126. GUARDED_BY_CONTEXT(sequence_checker_);
  127. SEQUENCE_CHECKER(sequence_checker_);
  128. base::WeakPtrFactory<IpcWriter> weak_ptr_factory_{this};
  129. };
  130. IpcFileOperations::IpcFileOperations(
  131. base::WeakPtr<RequestHandler> request_handler)
  132. : request_handler_(std::move(request_handler)) {}
  133. IpcFileOperations::~IpcFileOperations() = default;
  134. std::unique_ptr<FileOperations::Reader> IpcFileOperations::CreateReader() {
  135. return std::make_unique<IpcReader>(request_handler_);
  136. }
  137. std::unique_ptr<FileOperations::Writer> IpcFileOperations::CreateWriter() {
  138. return std::make_unique<IpcWriter>(request_handler_);
  139. }
  140. IpcFileOperationsFactory::IpcFileOperationsFactory(
  141. IpcFileOperations::RequestHandler* request_handler)
  142. : request_handler_weak_ptr_factory_(request_handler) {}
  143. IpcFileOperationsFactory::~IpcFileOperationsFactory() = default;
  144. std::unique_ptr<FileOperations>
  145. IpcFileOperationsFactory::CreateFileOperations() {
  146. return base::WrapUnique(
  147. new IpcFileOperations(request_handler_weak_ptr_factory_.GetWeakPtr()));
  148. }
  149. IpcFileOperations::IpcReader::IpcReader(
  150. base::WeakPtr<RequestHandler> request_handler)
  151. : request_handler_(std::move(request_handler)) {}
  152. IpcFileOperations::IpcReader::~IpcReader() = default;
  153. void IpcFileOperations::IpcReader::Open(OpenCallback callback) {
  154. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  155. DCHECK_EQ(kCreated, state_);
  156. if (!request_handler_) {
  157. state_ = kFailed;
  158. std::move(callback).Run(protocol::MakeFileTransferError(
  159. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
  160. return;
  161. }
  162. state_ = kBusy;
  163. pending_open_callback_ = std::move(callback);
  164. request_handler_->BeginFileRead(
  165. base::BindOnce(&IpcReader::OnOpenResult, GetWeakPtr()),
  166. base::BindOnce(&IpcReader::OnChannelDisconnected, GetWeakPtr()));
  167. }
  168. void IpcFileOperations::IpcReader::ReadChunk(std::size_t size,
  169. ReadCallback callback) {
  170. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  171. if (state_ != kReady || !remote_file_reader_.is_connected()) {
  172. state_ = kFailed;
  173. std::move(callback).Run(protocol::MakeFileTransferError(
  174. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
  175. return;
  176. }
  177. state_ = kBusy;
  178. pending_read_callback_ = std::move(callback);
  179. // Unretained is sound because the remote is owned by this instance and will
  180. // be destroyed at the same time which will clear any callbacks.
  181. remote_file_reader_->ReadChunk(
  182. size, base::BindOnce(&IpcReader::OnReadResult, base::Unretained(this)));
  183. }
  184. const base::FilePath& IpcFileOperations::IpcReader::filename() const {
  185. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  186. return filename_;
  187. }
  188. std::uint64_t IpcFileOperations::IpcReader::size() const {
  189. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  190. return size_;
  191. }
  192. FileOperations::State IpcFileOperations::IpcReader::state() const {
  193. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  194. return state_;
  195. }
  196. void IpcFileOperations::IpcReader::OnChannelDisconnected() {
  197. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  198. state_ = kFailed;
  199. if (pending_open_callback_) {
  200. std::move(pending_open_callback_)
  201. .Run(protocol::MakeFileTransferError(
  202. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
  203. } else if (pending_read_callback_) {
  204. std::move(pending_read_callback_)
  205. .Run(protocol::MakeFileTransferError(
  206. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
  207. }
  208. }
  209. base::WeakPtr<IpcFileOperations::IpcReader>
  210. IpcFileOperations::IpcReader::GetWeakPtr() const {
  211. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  212. return weak_ptr_factory_.GetWeakPtr();
  213. }
  214. void IpcFileOperations::IpcReader::OnOpenResult(
  215. mojom::BeginFileReadResultPtr result) {
  216. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  217. if (result->is_error()) {
  218. state_ = kFailed;
  219. std::move(pending_open_callback_).Run(result->get_error());
  220. return;
  221. }
  222. state_ = kReady;
  223. auto& success_ptr = result->get_success();
  224. filename_ = std::move(success_ptr->filename);
  225. size_ = success_ptr->size;
  226. remote_file_reader_.Bind(std::move(success_ptr->file_reader));
  227. // base::Unretained is sound because this instance owns |remote_file_reader_|
  228. // and the handler will not run after it is destroyed.
  229. remote_file_reader_.set_disconnect_handler(base::BindOnce(
  230. &IpcReader::OnChannelDisconnected, base::Unretained(this)));
  231. std::move(pending_open_callback_).Run(kSuccessTag);
  232. }
  233. void IpcFileOperations::IpcReader::OnReadResult(
  234. const protocol::FileTransferResult<std::vector<std::uint8_t>>& result) {
  235. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  236. if (result) {
  237. state_ = result->size() == 0 ? kComplete : kReady;
  238. } else {
  239. state_ = kFailed;
  240. }
  241. if (state_ != kReady) {
  242. // Don't need the remote if we're done or an error occurred.
  243. remote_file_reader_.reset();
  244. }
  245. std::move(pending_read_callback_).Run(std::move(result));
  246. }
  247. IpcFileOperations::IpcWriter::IpcWriter(
  248. base::WeakPtr<RequestHandler> request_handler)
  249. : request_handler_(std::move(request_handler)) {}
  250. IpcFileOperations::IpcWriter::~IpcWriter() = default;
  251. void IpcFileOperations::IpcWriter::Open(const base::FilePath& filename,
  252. Callback callback) {
  253. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  254. DCHECK_EQ(kCreated, state_);
  255. if (!request_handler_) {
  256. state_ = kFailed;
  257. std::move(callback).Run(protocol::MakeFileTransferError(
  258. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
  259. return;
  260. }
  261. state_ = kBusy;
  262. pending_callback_ = std::move(callback);
  263. request_handler_->BeginFileWrite(
  264. filename, base::BindOnce(&IpcWriter::OnOpenResult, GetWeakPtr()),
  265. base::BindOnce(&IpcWriter::OnChannelDisconnected, GetWeakPtr()));
  266. }
  267. void IpcFileOperations::IpcWriter::WriteChunk(std::vector<std::uint8_t> data,
  268. Callback callback) {
  269. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  270. if (state_ != kReady) {
  271. state_ = kFailed;
  272. std::move(callback).Run(protocol::MakeFileTransferError(
  273. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
  274. return;
  275. }
  276. state_ = kBusy;
  277. pending_callback_ = std::move(callback);
  278. // Unretained is sound because the remote is owned by this instance and will
  279. // be destroyed at the same time which will clear this callback.
  280. remote_file_writer_->WriteChunk(
  281. std::move(data),
  282. base::BindOnce(&IpcWriter::OnOperationResult, base::Unretained(this)));
  283. }
  284. void IpcFileOperations::IpcWriter::Close(Callback callback) {
  285. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  286. if (state_ != kReady) {
  287. state_ = kFailed;
  288. std::move(callback).Run(protocol::MakeFileTransferError(
  289. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
  290. return;
  291. }
  292. state_ = kBusy;
  293. pending_callback_ = std::move(callback);
  294. // Unretained is sound because the remote is owned by this instance and will
  295. // be destroyed at the same time which will clear this callback.
  296. remote_file_writer_->CloseFile(
  297. base::BindOnce(&IpcWriter::OnCloseResult, base::Unretained(this)));
  298. }
  299. FileOperations::State IpcFileOperations::IpcWriter::state() const {
  300. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  301. return state_;
  302. }
  303. void IpcFileOperations::IpcWriter::OnChannelDisconnected() {
  304. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  305. state_ = kFailed;
  306. if (pending_callback_) {
  307. std::move(pending_callback_)
  308. .Run(protocol::MakeFileTransferError(
  309. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
  310. }
  311. }
  312. base::WeakPtr<IpcFileOperations::IpcWriter>
  313. IpcFileOperations::IpcWriter::GetWeakPtr() const {
  314. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  315. return weak_ptr_factory_.GetWeakPtr();
  316. }
  317. void IpcFileOperations::IpcWriter::OnOpenResult(
  318. mojom::BeginFileWriteResultPtr result) {
  319. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  320. if (result->is_error()) {
  321. state_ = kFailed;
  322. std::move(pending_callback_).Run(result->get_error());
  323. return;
  324. }
  325. state_ = kReady;
  326. auto& success_ptr = result->get_success();
  327. remote_file_writer_.Bind(std::move(success_ptr->file_writer));
  328. // base::Unretained is sound because this instance owns |remote_file_writer_|
  329. // and the handler will not run after it is destroyed.
  330. remote_file_writer_.set_disconnect_handler(base::BindOnce(
  331. &IpcWriter::OnChannelDisconnected, base::Unretained(this)));
  332. std::move(pending_callback_).Run(kSuccessTag);
  333. }
  334. void IpcFileOperations::IpcWriter::OnOperationResult(
  335. const absl::optional<::remoting::protocol::FileTransfer_Error>& error) {
  336. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  337. if (error) {
  338. state_ = kFailed;
  339. std::move(pending_callback_).Run(std::move(*error));
  340. remote_file_writer_.reset();
  341. return;
  342. }
  343. state_ = kReady;
  344. std::move(pending_callback_).Run({kSuccessTag});
  345. }
  346. void IpcFileOperations::IpcWriter::OnCloseResult(
  347. const absl::optional<::remoting::protocol::FileTransfer_Error>& error) {
  348. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  349. // We're done with the remote regardless of the result.
  350. remote_file_writer_.reset();
  351. if (error) {
  352. state_ = kFailed;
  353. std::move(pending_callback_).Run(std::move(*error));
  354. } else {
  355. state_ = kComplete;
  356. std::move(pending_callback_).Run({kSuccessTag});
  357. }
  358. }
  359. } // namespace remoting