1 // Copyright 2019 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
5 #include "chrome/chrome_cleaner/engines/target/sandboxed_test_helpers.h"
6
7 #include <utility>
8
9 #include "base/callback_helpers.h"
10 #include "base/task/single_thread_task_executor.h"
11 #include "chrome/chrome_cleaner/engines/common/engine_result_codes.h"
12 #include "chrome/chrome_cleaner/os/early_exit.h"
13 #include "chrome/chrome_cleaner/os/initializer.h"
14 #include "mojo/public/cpp/bindings/pending_receiver.h"
15 #include "mojo/public/cpp/system/message_pipe.h"
16
17 namespace chrome_cleaner {
18
19 const int SandboxChildProcess::kConnectionErrorExitCode = 0xDEAD;
20
21 // FakeEngineDelegate takes a base::Event that it signals once either
22 // Initialize, StartScan, or StartCleanup has been called to indicate when this
23 // class is fully functional (for either scanning or cleaning test). It does not
24 // invoke any actual engine commands.
25 class SandboxChildProcess::FakeEngineDelegate : public EngineDelegate {
26 public:
FakeEngineDelegate(base::WaitableEvent * event)27 explicit FakeEngineDelegate(base::WaitableEvent* event) : event_(event) {}
28
engine() const29 Engine::Name engine() const override { return Engine::TEST_ONLY; }
30
Initialize(const base::FilePath & log_directory_path,scoped_refptr<EngineFileRequestsProxy> privileged_file_calls,mojom::EngineCommands::InitializeCallback callback)31 void Initialize(const base::FilePath& log_directory_path,
32 scoped_refptr<EngineFileRequestsProxy> privileged_file_calls,
33 mojom::EngineCommands::InitializeCallback callback) override {
34 privileged_file_calls_ = privileged_file_calls;
35 event_->Signal();
36 std::move(callback).Run(EngineResultCode::kSuccess);
37 }
38
StartScan(const std::vector<UwSId> & enabled_uws,const std::vector<UwS::TraceLocation> & enabled_locations,bool include_details,scoped_refptr<EngineFileRequestsProxy> privileged_file_calls,scoped_refptr<EngineRequestsProxy> privileged_scan_calls,scoped_refptr<EngineScanResultsProxy>)39 uint32_t StartScan(
40 const std::vector<UwSId>& enabled_uws,
41 const std::vector<UwS::TraceLocation>& enabled_locations,
42 bool include_details,
43 scoped_refptr<EngineFileRequestsProxy> privileged_file_calls,
44 scoped_refptr<EngineRequestsProxy> privileged_scan_calls,
45 scoped_refptr<EngineScanResultsProxy> /*report_result_calls*/) override {
46 privileged_file_calls_ = privileged_file_calls;
47 privileged_scan_calls_ = privileged_scan_calls;
48 event_->Signal();
49 return EngineResultCode::kSuccess;
50 }
51
StartCleanup(const std::vector<UwSId> & enabled_uws,scoped_refptr<EngineFileRequestsProxy> privileged_file_calls,scoped_refptr<EngineRequestsProxy> privileged_scan_calls,scoped_refptr<CleanerEngineRequestsProxy> privileged_removal_calls,scoped_refptr<EngineCleanupResultsProxy>)52 uint32_t StartCleanup(
53 const std::vector<UwSId>& enabled_uws,
54 scoped_refptr<EngineFileRequestsProxy> privileged_file_calls,
55 scoped_refptr<EngineRequestsProxy> privileged_scan_calls,
56 scoped_refptr<CleanerEngineRequestsProxy> privileged_removal_calls,
57 scoped_refptr<EngineCleanupResultsProxy> /*report_result_calls*/)
58 override {
59 privileged_file_calls_ = privileged_file_calls;
60 privileged_scan_calls_ = privileged_scan_calls;
61 privileged_removal_calls_ = privileged_removal_calls;
62 event_->Signal();
63 return EngineResultCode::kSuccess;
64 }
65
Finalize()66 uint32_t Finalize() override { return EngineResultCode::kSuccess; }
67
GetFileRequestsProxy()68 scoped_refptr<EngineFileRequestsProxy> GetFileRequestsProxy() {
69 return privileged_file_calls_;
70 }
71
GetEngineRequestsProxy()72 scoped_refptr<EngineRequestsProxy> GetEngineRequestsProxy() {
73 return privileged_scan_calls_;
74 }
75
GetCleanerEngineRequestsProxy()76 scoped_refptr<CleanerEngineRequestsProxy> GetCleanerEngineRequestsProxy() {
77 return privileged_removal_calls_;
78 }
79
UnbindRequestsRemotes()80 void UnbindRequestsRemotes() {
81 if (privileged_scan_calls_) {
82 privileged_scan_calls_->UnbindRequestsRemote();
83 }
84 if (privileged_file_calls_) {
85 privileged_file_calls_->UnbindRequestsRemote();
86 }
87 }
88
89 private:
90 ~FakeEngineDelegate() override = default;
91
92 base::WaitableEvent* event_;
93 scoped_refptr<EngineFileRequestsProxy> privileged_file_calls_;
94 scoped_refptr<EngineRequestsProxy> privileged_scan_calls_;
95 scoped_refptr<CleanerEngineRequestsProxy> privileged_removal_calls_;
96 };
97
SandboxChildProcess(scoped_refptr<MojoTaskRunner> mojo_task_runner)98 SandboxChildProcess::SandboxChildProcess(
99 scoped_refptr<MojoTaskRunner> mojo_task_runner)
100 : ChildProcess(std::move(mojo_task_runner)) {
101 // This must be called before accessing Mojo, because the parent process is
102 // waiting on this and won't respond to Mojo calls.
103 NotifyInitializationDone();
104
105 mojo::ScopedMessagePipeHandle message_pipe_handle =
106 CreateMessagePipeFromCommandLine();
107 mojo::PendingReceiver<mojom::EngineCommands> engine_commands_receiver(
108 std::move(message_pipe_handle));
109 base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
110 base::WaitableEvent::InitialState::NOT_SIGNALED);
111 mojo_task_runner_->PostTask(
112 FROM_HERE,
113 base::BindOnce(&SandboxChildProcess::BindEngineCommandsReceiver,
114 base::Unretained(this),
115 std::move(engine_commands_receiver), &event));
116 event.Wait();
117 }
118
BindEngineCommandsReceiver(mojo::PendingReceiver<mojom::EngineCommands> receiver,base::WaitableEvent * event)119 void SandboxChildProcess::SandboxChildProcess::BindEngineCommandsReceiver(
120 mojo::PendingReceiver<mojom::EngineCommands> receiver,
121 base::WaitableEvent* event) {
122 fake_engine_delegate_ = base::MakeRefCounted<FakeEngineDelegate>(event);
123 engine_commands_impl_ = std::make_unique<EngineCommandsImpl>(
124 fake_engine_delegate_, std::move(receiver), mojo_task_runner_,
125 /*error_handler=*/base::BindOnce(&EarlyExit, kConnectionErrorExitCode));
126 }
127
128 scoped_refptr<EngineFileRequestsProxy>
GetFileRequestsProxy()129 SandboxChildProcess::GetFileRequestsProxy() {
130 return fake_engine_delegate_->GetFileRequestsProxy();
131 }
132
133 scoped_refptr<EngineRequestsProxy>
GetEngineRequestsProxy()134 SandboxChildProcess::GetEngineRequestsProxy() {
135 return fake_engine_delegate_->GetEngineRequestsProxy();
136 }
137
138 scoped_refptr<CleanerEngineRequestsProxy>
GetCleanerEngineRequestsProxy()139 SandboxChildProcess::GetCleanerEngineRequestsProxy() {
140 return fake_engine_delegate_->GetCleanerEngineRequestsProxy();
141 }
142
UnbindRequestsRemotes()143 void SandboxChildProcess::UnbindRequestsRemotes() {
144 base::SingleThreadTaskExecutor main_task_executor;
145 base::RunLoop run_loop;
146 if (GetCleanerEngineRequestsProxy()) {
147 mojo_task_runner_->PostTask(
148 FROM_HERE,
149 base::BindOnce(&CleanerEngineRequestsProxy::UnbindRequestsRemote,
150 GetCleanerEngineRequestsProxy()));
151 }
152
153 mojo_task_runner_->PostTask(
154 FROM_HERE, base::BindOnce(&FakeEngineDelegate::UnbindRequestsRemotes,
155 fake_engine_delegate_));
156
157 mojo_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(),
158 run_loop.QuitClosure());
159 run_loop.Run();
160 }
161
~SandboxChildProcess()162 SandboxChildProcess::~SandboxChildProcess() {
163 // |engine_commands_impl_| must be destroyed on the Mojo thread or it will
164 // crash.
165 mojo_task_runner_->PostTask(
166 FROM_HERE, base::BindOnce(
167 [](std::unique_ptr<EngineCommandsImpl> commands) {
168 commands.reset();
169 },
170 std::move(engine_commands_impl_)));
171 }
172
173 } // namespace chrome_cleaner
174