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 "content/browser/plugin_service_impl.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/optional.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/task/post_task.h"
16 #include "build/build_config.h"
17 #include "content/browser/ppapi_plugin_process_host.h"
18 #include "content/public/browser/browser_task_traits.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/test/content_browser_test.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 namespace content {
24
25 namespace {
26
27 constexpr char kURL1[] = "http://google.com/";
28 constexpr char kURL2[] = "http://youtube.com/";
29
30 class TestPluginClient : public PpapiPluginProcessHost::PluginClient {
31 public:
GetPpapiChannelInfo(base::ProcessHandle * renderer_handle,int * renderer_id)32 void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
33 int* renderer_id) override {}
OnPpapiChannelOpened(const IPC::ChannelHandle & channel_handle,base::ProcessId plugin_pid,int plugin_child_id)34 void OnPpapiChannelOpened(const IPC::ChannelHandle& channel_handle,
35 base::ProcessId plugin_pid,
36 int plugin_child_id) override {
37 plugin_pid_ = plugin_pid;
38 run_loop_->Quit();
39 }
Incognito()40 bool Incognito() override { return false; }
41
plugin_pid() const42 base::ProcessId plugin_pid() const { return plugin_pid_; }
SetRunLoop(base::RunLoop * run_loop)43 void SetRunLoop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
WaitForQuit()44 void WaitForQuit() { run_loop_->Run(); }
45
46 private:
47 base::ProcessId plugin_pid_ = 0;
48 base::RunLoop* run_loop_ = nullptr;
49 };
50
51 } // anonymous namespace
52
53 class PluginServiceImplBrowserTest : public ContentBrowserTest {
54 public:
PluginServiceImplBrowserTest()55 PluginServiceImplBrowserTest()
56 : plugin_path_(FILE_PATH_LITERAL("internal-nonesuch")),
57 profile_dir_(FILE_PATH_LITERAL("/fake/user/foo/dir")) {}
58
59 ~PluginServiceImplBrowserTest() override = default;
60
RegisterFakePlugin()61 void RegisterFakePlugin() {
62 WebPluginInfo fake_info;
63 fake_info.name = base::ASCIIToUTF16("fake_plugin");
64 fake_info.path = plugin_path_;
65 fake_info.type = WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS;
66
67 PluginServiceImpl* service = PluginServiceImpl::GetInstance();
68 service->RegisterInternalPlugin(fake_info, true);
69 service->Init();
70
71 // Force plugins to load and wait for completion.
72 base::RunLoop run_loop;
73 service->GetPlugins(base::BindOnce(
74 [](base::OnceClosure callback,
75 const std::vector<WebPluginInfo>& ignore) {
76 std::move(callback).Run();
77 },
78 run_loop.QuitClosure()));
79 run_loop.Run();
80 }
81
OpenChannelToFakePlugin(const base::Optional<url::Origin> & origin,TestPluginClient * client)82 void OpenChannelToFakePlugin(const base::Optional<url::Origin>& origin,
83 TestPluginClient* client) {
84 base::RunLoop run_loop;
85 client->SetRunLoop(&run_loop);
86
87 PluginServiceImpl* service = PluginServiceImpl::GetInstance();
88 base::PostTask(
89 FROM_HERE, {BrowserThread::IO},
90 base::BindOnce(&PluginServiceImpl::OpenChannelToPpapiPlugin,
91 base::Unretained(service), /*render_process_id=*/0,
92 /*embedder_origin=*/url::Origin(), plugin_path_,
93 profile_dir_, origin, base::Unretained(client)));
94 client->WaitForQuit();
95 client->SetRunLoop(nullptr);
96 }
97
98 base::FilePath plugin_path_;
99 base::FilePath profile_dir_;
100 };
101
IN_PROC_BROWSER_TEST_F(PluginServiceImplBrowserTest,OriginLock)102 IN_PROC_BROWSER_TEST_F(PluginServiceImplBrowserTest, OriginLock) {
103 RegisterFakePlugin();
104
105 url::Origin origin1 = url::Origin::Create(GURL(kURL1));
106 url::Origin origin2 = url::Origin::Create(GURL(kURL2));
107
108 TestPluginClient client1;
109 OpenChannelToFakePlugin(origin1, &client1);
110 EXPECT_NE(base::kNullProcessId, client1.plugin_pid());
111
112 TestPluginClient client2a;
113 OpenChannelToFakePlugin(origin2, &client2a);
114 EXPECT_NE(base::kNullProcessId, client2a.plugin_pid());
115
116 TestPluginClient client2b;
117 OpenChannelToFakePlugin(origin2, &client2b);
118 EXPECT_NE(base::kNullProcessId, client2b.plugin_pid());
119
120 // Actual test: how plugins got lumped into two pids.
121 EXPECT_NE(client1.plugin_pid(), client2a.plugin_pid());
122 EXPECT_NE(client1.plugin_pid(), client2b.plugin_pid());
123 EXPECT_EQ(client2a.plugin_pid(), client2b.plugin_pid());
124
125 // Empty origins all go to same pid.
126 TestPluginClient client3a;
127 OpenChannelToFakePlugin(base::nullopt, &client3a);
128 EXPECT_NE(base::kNullProcessId, client3a.plugin_pid());
129
130 TestPluginClient client3b;
131 OpenChannelToFakePlugin(base::nullopt, &client3b);
132 EXPECT_NE(base::kNullProcessId, client3b.plugin_pid());
133
134 // Actual test: how empty origins got lumped into pids.
135 EXPECT_NE(client1.plugin_pid(), client3a.plugin_pid());
136 EXPECT_NE(client1.plugin_pid(), client3b.plugin_pid());
137 EXPECT_NE(client2a.plugin_pid(), client3a.plugin_pid());
138 EXPECT_NE(client2a.plugin_pid(), client3b.plugin_pid());
139 EXPECT_EQ(client3a.plugin_pid(), client3b.plugin_pid());
140 }
141
IN_PROC_BROWSER_TEST_F(PluginServiceImplBrowserTest,NoForkBombs)142 IN_PROC_BROWSER_TEST_F(PluginServiceImplBrowserTest, NoForkBombs) {
143 RegisterFakePlugin();
144
145 PluginServiceImpl* service = PluginServiceImpl::GetInstance();
146 service->SetMaxPpapiProcessesPerProfileForTesting(4);
147
148 const char* kFakeURLTemplate = "https://foo.fake%d.com/";
149 TestPluginClient client;
150 for (int i = 0; i < 4; ++i) {
151 std::string url = base::StringPrintf(kFakeURLTemplate, i);
152 url::Origin origin = url::Origin::Create(GURL(url));
153 OpenChannelToFakePlugin(origin, &client);
154 EXPECT_NE(base::kNullProcessId, client.plugin_pid());
155 }
156
157 // After a while we stop handing out processes per-origin.
158 for (int i = 4; i < 8; ++i) {
159 std::string url = base::StringPrintf(kFakeURLTemplate, i);
160 url::Origin origin = url::Origin::Create(GURL(url));
161 OpenChannelToFakePlugin(origin, &client);
162 EXPECT_EQ(base::kNullProcessId, client.plugin_pid());
163 }
164
165 // But there's always room for the empty origin case.
166 OpenChannelToFakePlugin(base::nullopt, &client);
167 EXPECT_NE(base::kNullProcessId, client.plugin_pid());
168
169 // And re-using existing processes is always possible.
170 for (int i = 0; i < 4; ++i) {
171 std::string url = base::StringPrintf(kFakeURLTemplate, i);
172 url::Origin origin = url::Origin::Create(GURL(url));
173 OpenChannelToFakePlugin(origin, &client);
174 EXPECT_NE(base::kNullProcessId, client.plugin_pid());
175 }
176 }
177
178 } // namespace content
179