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