blob: 9f22cc94001ed44723bf351b80a490ff332aaf44 [file] [log] [blame]
Tom Sepez8db30ad2018-03-01 21:38:541// 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>
Tom Sepez19fecb3d2018-03-02 18:40:218#include <string>
Tom Sepez8db30ad2018-03-01 21:38:549#include <utility>
10
Sebastien Marchandf8cbfab2019-01-25 16:02:3011#include "base/bind.h"
Tom Sepez8db30ad2018-03-01 21:38:5412#include "base/optional.h"
Tom Sepez19fecb3d2018-03-02 18:40:2113#include "base/strings/stringprintf.h"
Tom Sepez8db30ad2018-03-01 21:38:5414#include "base/strings/utf_string_conversions.h"
Eric Seckler8652dcd52018-09-20 10:42:2815#include "base/task/post_task.h"
Tom Sepez8db30ad2018-03-01 21:38:5416#include "build/build_config.h"
17#include "content/browser/ppapi_plugin_process_host.h"
Eric Seckler8652dcd52018-09-20 10:42:2818#include "content/public/browser/browser_task_traits.h"
Tom Sepez8db30ad2018-03-01 21:38:5419#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
23namespace content {
24
25namespace {
26
27constexpr char kURL1[] = "http://google.com/";
28constexpr char kURL2[] = "http://youtube.com/";
29
30class TestPluginClient : public PpapiPluginProcessHost::PluginClient {
31 public:
32 void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
33 int* renderer_id) override {}
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 }
40 bool Incognito() override { return false; }
41
42 base::ProcessId plugin_pid() const { return plugin_pid_; }
43 void SetRunLoop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
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
53class PluginServiceImplBrowserTest : public ContentBrowserTest {
54 public:
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
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
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();
Sami Kyostila8e4d5a92019-08-02 12:45:0588 base::PostTask(
Eric Seckler8652dcd52018-09-20 10:42:2889 FROM_HERE, {BrowserThread::IO},
Tom Sepez8db30ad2018-03-01 21:38:5490 base::BindOnce(&PluginServiceImpl::OpenChannelToPpapiPlugin,
91 base::Unretained(service), 0, plugin_path_, profile_dir_,
92 origin, base::Unretained(client)));
93 client->WaitForQuit();
Tom Sepez19fecb3d2018-03-02 18:40:2194 client->SetRunLoop(nullptr);
Tom Sepez8db30ad2018-03-01 21:38:5495 }
96
97 base::FilePath plugin_path_;
98 base::FilePath profile_dir_;
99};
100
101IN_PROC_BROWSER_TEST_F(PluginServiceImplBrowserTest, OriginLock) {
102 RegisterFakePlugin();
103
104 url::Origin origin1 = url::Origin::Create(GURL(kURL1));
105 url::Origin origin2 = url::Origin::Create(GURL(kURL2));
106
107 TestPluginClient client1;
108 OpenChannelToFakePlugin(origin1, &client1);
109 EXPECT_NE(base::kNullProcessId, client1.plugin_pid());
110
111 TestPluginClient client2a;
112 OpenChannelToFakePlugin(origin2, &client2a);
113 EXPECT_NE(base::kNullProcessId, client2a.plugin_pid());
114
115 TestPluginClient client2b;
116 OpenChannelToFakePlugin(origin2, &client2b);
117 EXPECT_NE(base::kNullProcessId, client2b.plugin_pid());
118
119 // Actual test: how plugins got lumped into two pids.
120 EXPECT_NE(client1.plugin_pid(), client2a.plugin_pid());
121 EXPECT_NE(client1.plugin_pid(), client2b.plugin_pid());
122 EXPECT_EQ(client2a.plugin_pid(), client2b.plugin_pid());
123
124 // Empty origins all go to same pid.
125 TestPluginClient client3a;
126 OpenChannelToFakePlugin(base::nullopt, &client3a);
127 EXPECT_NE(base::kNullProcessId, client3a.plugin_pid());
128
129 TestPluginClient client3b;
130 OpenChannelToFakePlugin(base::nullopt, &client3b);
131 EXPECT_NE(base::kNullProcessId, client3b.plugin_pid());
132
133 // Actual test: how empty origins got lumped into pids.
134 EXPECT_NE(client1.plugin_pid(), client3a.plugin_pid());
135 EXPECT_NE(client1.plugin_pid(), client3b.plugin_pid());
136 EXPECT_NE(client2a.plugin_pid(), client3a.plugin_pid());
137 EXPECT_NE(client2a.plugin_pid(), client3b.plugin_pid());
138 EXPECT_EQ(client3a.plugin_pid(), client3b.plugin_pid());
139}
140
Tom Sepez19fecb3d2018-03-02 18:40:21141IN_PROC_BROWSER_TEST_F(PluginServiceImplBrowserTest, NoForkBombs) {
142 RegisterFakePlugin();
143
144 PluginServiceImpl* service = PluginServiceImpl::GetInstance();
145 service->SetMaxPpapiProcessesPerProfileForTesting(4);
146
147 const char* kFakeURLTemplate = "https://foo.fake%d.com/";
148 TestPluginClient client;
149 for (int i = 0; i < 4; ++i) {
150 std::string url = base::StringPrintf(kFakeURLTemplate, i);
151 url::Origin origin = url::Origin::Create(GURL(url));
152 OpenChannelToFakePlugin(origin, &client);
153 EXPECT_NE(base::kNullProcessId, client.plugin_pid());
154 }
155
156 // After a while we stop handing out processes per-origin.
157 for (int i = 4; i < 8; ++i) {
158 std::string url = base::StringPrintf(kFakeURLTemplate, i);
159 url::Origin origin = url::Origin::Create(GURL(url));
160 OpenChannelToFakePlugin(origin, &client);
161 EXPECT_EQ(base::kNullProcessId, client.plugin_pid());
162 }
163
164 // But there's always room for the empty origin case.
165 OpenChannelToFakePlugin(base::nullopt, &client);
166 EXPECT_NE(base::kNullProcessId, client.plugin_pid());
167
168 // And re-using existing processes is always possible.
169 for (int i = 0; i < 4; ++i) {
170 std::string url = base::StringPrintf(kFakeURLTemplate, i);
171 url::Origin origin = url::Origin::Create(GURL(url));
172 OpenChannelToFakePlugin(origin, &client);
173 EXPECT_NE(base::kNullProcessId, client.plugin_pid());
174 }
175}
176
Tom Sepez8db30ad2018-03-01 21:38:54177} // namespace content