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
5 #include <memory>
6 #include <string>
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/test/multiprocess_test.h"
14 #include "base/test/task_environment.h"
15 #include "chrome/chrome_cleaner/ipc/mojo_sandbox_hooks.h"
16 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
17 #include "chrome/chrome_cleaner/mojom/test_mojo_sandbox_hooks.mojom.h"
18 #include "chrome/chrome_cleaner/os/early_exit.h"
19 #include "mojo/public/cpp/bindings/pending_receiver.h"
20 #include "mojo/public/cpp/bindings/pending_remote.h"
21 #include "mojo/public/cpp/bindings/receiver.h"
22 #include "mojo/public/cpp/bindings/remote.h"
23 #include "mojo/public/cpp/system/message_pipe.h"
24 #include "sandbox/win/src/sandbox_factory.h"
25 #include "testing/multiprocess_func_list.h"
26
27 namespace chrome_cleaner {
28
29 namespace {
30
31 constexpr char kTestString[] = "Hello World";
32
33 using RemoteTestMojoSandboxHooksPtr =
34 std::unique_ptr<mojo::Remote<mojom::TestMojoSandboxHooks>,
35 base::OnTaskRunnerDeleter>;
36
37 class MojoSandboxHooksTest : public base::MultiProcessTest {
38 public:
MojoSandboxHooksTest()39 MojoSandboxHooksTest() : mojo_task_runner_(MojoTaskRunner::Create()) {}
40
41 protected:
42 scoped_refptr<MojoTaskRunner> mojo_task_runner_;
43
44 private:
45 base::test::TaskEnvironment task_environment_;
46 };
47
48 // |TestMojoSandboxHooksImpl| runs and handles mojo requests in the sandbox
49 // child process.
50 class TestMojoSandboxHooksImpl : mojom::TestMojoSandboxHooks {
51 public:
TestMojoSandboxHooksImpl(mojo::PendingReceiver<mojom::TestMojoSandboxHooks> receiver)52 explicit TestMojoSandboxHooksImpl(
53 mojo::PendingReceiver<mojom::TestMojoSandboxHooks> receiver)
54 : receiver_(this, std::move(receiver)) {
55 receiver_.set_disconnect_handler(base::BindOnce(&EarlyExit, 1));
56 }
57
EchoString(const std::string & input,EchoStringCallback callback)58 void EchoString(const std::string& input,
59 EchoStringCallback callback) override {
60 std::move(callback).Run(input);
61 }
62
63 private:
64 mojo::Receiver<mojom::TestMojoSandboxHooks> receiver_;
65 };
66
67 class TestSandboxSetupHooks : public MojoSandboxSetupHooks {
68 public:
TestSandboxSetupHooks(scoped_refptr<MojoTaskRunner> mojo_task_runner)69 explicit TestSandboxSetupHooks(scoped_refptr<MojoTaskRunner> mojo_task_runner)
70 : mojo_task_runner_(mojo_task_runner),
71 // Manually use |new| here because |make_unique| doesn't work with
72 // custom deleter.
73 test_mojo_(new mojo::Remote<mojom::TestMojoSandboxHooks>(),
74 base::OnTaskRunnerDeleter(mojo_task_runner_)) {}
75
UpdateSandboxPolicy(sandbox::TargetPolicy * policy,base::CommandLine * command_line)76 ResultCode UpdateSandboxPolicy(sandbox::TargetPolicy* policy,
77 base::CommandLine* command_line) override {
78 mojo::ScopedMessagePipeHandle pipe_handle =
79 SetupSandboxMessagePipe(policy, command_line);
80
81 // Unretained pointer of |test_mojo_| is safe because its deleter is run on
82 // the same task runner. So it won't be deleted before this task.
83 mojo_task_runner_->PostTask(
84 FROM_HERE,
85 base::BindOnce(TestSandboxSetupHooks::BindTestMojoSandboxHooksRemote,
86 base::Unretained(test_mojo_.get()),
87 std::move(pipe_handle)));
88
89 return RESULT_CODE_SUCCESS;
90 }
91
TakeTestMojoSandboxHooksRemote()92 RemoteTestMojoSandboxHooksPtr TakeTestMojoSandboxHooksRemote() {
93 return std::move(test_mojo_);
94 }
95
96 private:
BindTestMojoSandboxHooksRemote(mojo::Remote<mojom::TestMojoSandboxHooks> * test_mojo,mojo::ScopedMessagePipeHandle pipe_handle)97 static void BindTestMojoSandboxHooksRemote(
98 mojo::Remote<mojom::TestMojoSandboxHooks>* test_mojo,
99 mojo::ScopedMessagePipeHandle pipe_handle) {
100 test_mojo->Bind(mojo::PendingRemote<mojom::TestMojoSandboxHooks>(
101 std::move(pipe_handle), 0));
102 test_mojo->set_disconnect_handler(base::BindOnce(
103 [] { FAIL() << "Mojo sandbox setup connection error"; }));
104 }
105
106 scoped_refptr<MojoTaskRunner> mojo_task_runner_;
107 RemoteTestMojoSandboxHooksPtr test_mojo_;
108 };
109
110 class TestSandboxTargetHooks : public MojoSandboxTargetHooks {
111 public:
TargetDroppedPrivileges(const base::CommandLine & command_line)112 ResultCode TargetDroppedPrivileges(
113 const base::CommandLine& command_line) override {
114 scoped_refptr<MojoTaskRunner> mojo_task_runner = MojoTaskRunner::Create();
115 mojo::PendingReceiver<mojom::TestMojoSandboxHooks> receiver(
116 ExtractSandboxMessagePipe(command_line));
117
118 std::unique_ptr<TestMojoSandboxHooksImpl, base::OnTaskRunnerDeleter>
119 impl_ptr(nullptr, base::OnTaskRunnerDeleter(mojo_task_runner));
120
121 // This loop will run forever. Once the communication channel with the
122 // broker process is broken, mojo error handler will abort this process.
123 base::RunLoop loop;
124 mojo_task_runner->PostTask(
125 FROM_HERE,
126 base::BindOnce(CreateTestMojoSandboxHooksImpl,
127 base::Unretained(&impl_ptr), std::move(receiver)));
128 loop.Run();
129
130 return RESULT_CODE_SUCCESS;
131 }
132
133 private:
CreateTestMojoSandboxHooksImpl(std::unique_ptr<TestMojoSandboxHooksImpl,base::OnTaskRunnerDeleter> * impl_ptr,mojo::PendingReceiver<mojom::TestMojoSandboxHooks> receiver)134 static void CreateTestMojoSandboxHooksImpl(
135 std::unique_ptr<TestMojoSandboxHooksImpl, base::OnTaskRunnerDeleter>*
136 impl_ptr,
137 mojo::PendingReceiver<mojom::TestMojoSandboxHooks> receiver) {
138 (*impl_ptr).reset(new TestMojoSandboxHooksImpl(std::move(receiver)));
139 }
140
141 base::test::TaskEnvironment task_environment_;
142 };
143
RunEchoString(mojo::Remote<mojom::TestMojoSandboxHooks> * test_mojo,const std::string & input,mojom::TestMojoSandboxHooks::EchoStringCallback callback)144 void RunEchoString(mojo::Remote<mojom::TestMojoSandboxHooks>* test_mojo,
145 const std::string& input,
146 mojom::TestMojoSandboxHooks::EchoStringCallback callback) {
147 DCHECK(test_mojo);
148
149 (*test_mojo)->EchoString(input, std::move(callback));
150 }
151
OnEchoStringDone(std::string * result_string,base::OnceClosure done_callback,const std::string & output)152 void OnEchoStringDone(std::string* result_string,
153 base::OnceClosure done_callback,
154 const std::string& output) {
155 *result_string = output;
156 std::move(done_callback).Run();
157 }
158
159 } // namespace
160
MULTIPROCESS_TEST_MAIN(MojoSandboxHooksTargetMain)161 MULTIPROCESS_TEST_MAIN(MojoSandboxHooksTargetMain) {
162 sandbox::TargetServices* sandbox_target_services =
163 sandbox::SandboxFactory::GetTargetServices();
164 CHECK(sandbox_target_services);
165
166 TestSandboxTargetHooks target_hooks;
167 RunSandboxTarget(*base::CommandLine::ForCurrentProcess(),
168 sandbox_target_services, &target_hooks);
169
170 return 0;
171 }
172
TEST_F(MojoSandboxHooksTest,SpawnSandboxTarget)173 TEST_F(MojoSandboxHooksTest, SpawnSandboxTarget) {
174 TestSandboxSetupHooks setup_hooks(mojo_task_runner_.get());
175
176 ASSERT_EQ(RESULT_CODE_SUCCESS,
177 StartSandboxTarget(MakeCmdLine("MojoSandboxHooksTargetMain"),
178 &setup_hooks, SandboxType::kTest));
179
180 RemoteTestMojoSandboxHooksPtr test_mojo =
181 setup_hooks.TakeTestMojoSandboxHooksRemote();
182
183 std::string test_result_string;
184 base::RunLoop loop;
185 // Unretained pointers are safe because the test will wait until the task
186 // ends.
187 mojo_task_runner_->PostTask(
188 FROM_HERE,
189 base::BindOnce(RunEchoString, base::Unretained(test_mojo.get()),
190 kTestString,
191 base::BindOnce(OnEchoStringDone,
192 base::Unretained(&test_result_string),
193 loop.QuitClosure())));
194 loop.Run();
195 EXPECT_EQ(test_result_string, kTestString);
196 }
197
198 } // namespace chrome_cleaner
199