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