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