summaryrefslogtreecommitdiffstats
path: root/chromium/content/browser/plugin_service_impl_browsertest.cc
blob: e7e530ff2322a167a8e54b47127299fe962ef6f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/plugin_service_impl.h"

#include <memory>
#include <string>
#include <utility>

#include "base/bind.h"
#include "base/optional.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "content/browser/ppapi_plugin_process_host.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

namespace {

constexpr char kURL1[] = "http://google.com/";
constexpr char kURL2[] = "http://youtube.com/";

class TestPluginClient : public PpapiPluginProcessHost::PluginClient {
 public:
  void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
                           int* renderer_id) override {}
  void OnPpapiChannelOpened(const IPC::ChannelHandle& channel_handle,
                            base::ProcessId plugin_pid,
                            int plugin_child_id) override {
    plugin_pid_ = plugin_pid;
    run_loop_->Quit();
  }
  bool Incognito() override { return false; }

  base::ProcessId plugin_pid() const { return plugin_pid_; }
  void SetRunLoop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
  void WaitForQuit() { run_loop_->Run(); }

 private:
  base::ProcessId plugin_pid_ = 0;
  base::RunLoop* run_loop_ = nullptr;
};

}  // anonymous namespace

class PluginServiceImplBrowserTest : public ContentBrowserTest {
 public:
  PluginServiceImplBrowserTest()
      : plugin_path_(FILE_PATH_LITERAL("internal-nonesuch")),
        profile_dir_(FILE_PATH_LITERAL("/fake/user/foo/dir")) {}

  ~PluginServiceImplBrowserTest() override = default;

  void RegisterFakePlugin() {
    WebPluginInfo fake_info;
    fake_info.name = base::ASCIIToUTF16("fake_plugin");
    fake_info.path = plugin_path_;
    fake_info.type = WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS;

    PluginServiceImpl* service = PluginServiceImpl::GetInstance();
    service->RegisterInternalPlugin(fake_info, true);
    service->Init();

    // Force plugins to load and wait for completion.
    base::RunLoop run_loop;
    service->GetPlugins(base::BindOnce(
        [](base::OnceClosure callback,
           const std::vector<WebPluginInfo>& ignore) {
          std::move(callback).Run();
        },
        run_loop.QuitClosure()));
    run_loop.Run();
  }

  void OpenChannelToFakePlugin(const base::Optional<url::Origin>& origin,
                               TestPluginClient* client) {
    base::RunLoop run_loop;
    client->SetRunLoop(&run_loop);

    PluginServiceImpl* service = PluginServiceImpl::GetInstance();
    GetIOThreadTaskRunner({})->PostTask(
        FROM_HERE,
        base::BindOnce(&PluginServiceImpl::OpenChannelToPpapiPlugin,
                       base::Unretained(service), /*render_process_id=*/0,
                       /*embedder_origin=*/url::Origin(), plugin_path_,
                       profile_dir_, origin, base::Unretained(client)));
    client->WaitForQuit();
    client->SetRunLoop(nullptr);
  }

  base::FilePath plugin_path_;
  base::FilePath profile_dir_;
};

IN_PROC_BROWSER_TEST_F(PluginServiceImplBrowserTest, OriginLock) {
  RegisterFakePlugin();

  url::Origin origin1 = url::Origin::Create(GURL(kURL1));
  url::Origin origin2 = url::Origin::Create(GURL(kURL2));

  TestPluginClient client1;
  OpenChannelToFakePlugin(origin1, &client1);
  EXPECT_NE(base::kNullProcessId, client1.plugin_pid());

  TestPluginClient client2a;
  OpenChannelToFakePlugin(origin2, &client2a);
  EXPECT_NE(base::kNullProcessId, client2a.plugin_pid());

  TestPluginClient client2b;
  OpenChannelToFakePlugin(origin2, &client2b);
  EXPECT_NE(base::kNullProcessId, client2b.plugin_pid());

  // Actual test: how plugins got lumped into two pids.
  EXPECT_NE(client1.plugin_pid(), client2a.plugin_pid());
  EXPECT_NE(client1.plugin_pid(), client2b.plugin_pid());
  EXPECT_EQ(client2a.plugin_pid(), client2b.plugin_pid());

  // Empty origins all go to same pid.
  TestPluginClient client3a;
  OpenChannelToFakePlugin(base::nullopt, &client3a);
  EXPECT_NE(base::kNullProcessId, client3a.plugin_pid());

  TestPluginClient client3b;
  OpenChannelToFakePlugin(base::nullopt, &client3b);
  EXPECT_NE(base::kNullProcessId, client3b.plugin_pid());

  // Actual test: how empty origins got lumped into pids.
  EXPECT_NE(client1.plugin_pid(), client3a.plugin_pid());
  EXPECT_NE(client1.plugin_pid(), client3b.plugin_pid());
  EXPECT_NE(client2a.plugin_pid(), client3a.plugin_pid());
  EXPECT_NE(client2a.plugin_pid(), client3b.plugin_pid());
  EXPECT_EQ(client3a.plugin_pid(), client3b.plugin_pid());
}

IN_PROC_BROWSER_TEST_F(PluginServiceImplBrowserTest, NoForkBombs) {
  RegisterFakePlugin();

  PluginServiceImpl* service = PluginServiceImpl::GetInstance();
  service->SetMaxPpapiProcessesPerProfileForTesting(4);

  const char* kFakeURLTemplate = "https://foo.fake%d.com/";
  TestPluginClient client;
  for (int i = 0; i < 4; ++i) {
    std::string url = base::StringPrintf(kFakeURLTemplate, i);
    url::Origin origin = url::Origin::Create(GURL(url));
    OpenChannelToFakePlugin(origin, &client);
    EXPECT_NE(base::kNullProcessId, client.plugin_pid());
  }

  // After a while we stop handing out processes per-origin.
  for (int i = 4; i < 8; ++i) {
    std::string url = base::StringPrintf(kFakeURLTemplate, i);
    url::Origin origin = url::Origin::Create(GURL(url));
    OpenChannelToFakePlugin(origin, &client);
    EXPECT_EQ(base::kNullProcessId, client.plugin_pid());
  }

  // But there's always room for the empty origin case.
  OpenChannelToFakePlugin(base::nullopt, &client);
  EXPECT_NE(base::kNullProcessId, client.plugin_pid());

  // And re-using existing processes is always possible.
  for (int i = 0; i < 4; ++i) {
    std::string url = base::StringPrintf(kFakeURLTemplate, i);
    url::Origin origin = url::Origin::Create(GURL(url));
    OpenChannelToFakePlugin(origin, &client);
    EXPECT_NE(base::kNullProcessId, client.plugin_pid());
  }
}

}  // namespace content