blob: fb4f3d0149fbd3813adb5d7e2b4f484d4215271a [file] [log] [blame]
Ellyc2f2ca52024-08-27 17:56:451// Copyright 2024 The Chromium Authors
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 "base/strings/string_number_conversions.h"
6#include "base/test/bind.h"
7#include "base/test/scoped_feature_list.h"
8#include "base/values.h"
9#include "build/build_config.h"
10#include "content/browser/web_contents/web_contents_impl.h"
11#include "content/public/test/browser_test.h"
12#include "content/public/test/browser_test_base.h"
13#include "content/public/test/content_browser_test.h"
14#include "content/public/test/content_browser_test_content_browser_client.h"
15#include "content/public/test/content_browser_test_utils.h"
16#include "content/public/test/test_utils.h"
17#include "content/shell/browser/shell.h"
18#include "net/dns/mock_host_resolver.h"
19#include "sandbox/policy/features.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22#if BUILDFLAG(IS_WIN)
23#include "sandbox/policy/win/sandbox_win.h"
24#endif
25
26namespace content {
27
28namespace {
29
30constexpr char kEnabledDomain[] = "enable-jit.net";
31constexpr char kDisabledDomain[] = "disable-jit.net";
32
33bool RendererIsJitless(RenderProcessHost* rph) {
34 // It would be nice if we could also check the renderer process's actual
35 // command line here, but there's no portable interface to do so.
36 return rph->IsJitDisabled();
37}
38
39#if BUILDFLAG(IS_WIN)
40bool RendererHasDynamicCodeMitigation(RenderProcessHost* rph) {
41 // Multiple renderer processes might have started. Grab a reference to the
42 // base::Process itself as well as grabbing the process ID; this ensures that
43 // the process doesn't actually die during the RunLoop::Run() call below, so
44 // its pid cannot be reused and confuse the test.
45 base::Process proc = rph->GetProcess().Duplicate();
46 base::ProcessId renderer_process_id = proc.Pid();
47
48 base::RunLoop run_loop;
49 base::Value out_args;
50 sandbox::policy::SandboxWin::GetPolicyDiagnostics(
51 base::BindLambdaForTesting([&run_loop, &out_args](base::Value args) {
52 out_args = std::move(args);
53 run_loop.Quit();
54 }));
55 run_loop.Run();
56
57 const base::Value::List* process_list = out_args.GetIfList();
58 CHECK(process_list);
59
60 for (const base::Value& process_value : *process_list) {
61 const base::Value::Dict* process = process_value.GetIfDict();
62 CHECK(process);
63 double pid = *process->FindDouble("processId");
64 if (base::checked_cast<base::ProcessId>(pid) != renderer_process_id) {
65 continue;
66 }
67
68 std::string mitigations = *process->FindString("desiredMitigations");
69 uint64_t mask = 0;
70 CHECK(base::HexStringToUInt64(mitigations, &mask));
71
72 return !!(mask & sandbox::MITIGATION_DYNAMIC_CODE_DISABLE);
73 }
74
75 return false;
76}
77#endif // IS_WIN
78
79} // namespace
80
81class JitPolicyContentBrowserClient
82 : public ContentBrowserTestContentBrowserClient {
83 public:
84 bool IsJitDisabledForSite(BrowserContext* context, const GURL& url) override {
85 // This is a bit confusing: for kEnabledDomain, JIT is enabled, so
86 // IsJitDisabled() returns false. For kDisabledDomain, JIT is disabled, so
87 // IsJitDisabled() returns true. Otherwise, use the default policy.
88 if (url.DomainIs(kEnabledDomain)) {
89 return false;
90 } else if (url.DomainIs(kDisabledDomain)) {
91 return true;
92 } else {
93 return ContentBrowserTestContentBrowserClient::IsJitDisabledForSite(
94 context, url);
95 }
96 }
97
98 // Always enable renderer code integrity - without this, ProhibitDynamicCode
99 // never gets applied to the renderer anyway.
100#if BUILDFLAG(IS_WIN)
101 bool IsRendererCodeIntegrityEnabled() override { return true; }
102#endif // IS_WIN
103};
104
105// This test fixture installs a test ContentBrowserClient which enables JIT for
106// kEnabledDomain and disables JIT for kDisabledDomain.
107class JitPolicyBrowserTest : public ContentBrowserTest {
108 public:
109 JitPolicyBrowserTest() = default;
110
111 void SetUpCommandLine(base::CommandLine* command_line) override {
112 content::IsolateAllSitesForTesting(command_line);
113 }
114
115 void SetUpOnMainThread() override {
116 // Support multiple sites on the test server.
117 host_resolver()->AddRule("*", "127.0.0.1");
118
119 content_browser_client_ = std::make_unique<JitPolicyContentBrowserClient>();
120 }
121
122 protected:
123 WebContentsImpl* contents() const {
124 return static_cast<WebContentsImpl*>(shell()->web_contents());
125 }
126
127 private:
128 // Constructing this object causes it to register itself as a
129 // ContentBrowserClient.
130 std::unique_ptr<JitPolicyContentBrowserClient> content_browser_client_;
131};
132
133// This test asserts that navigating to a renderer which has JIT disabled yields
134// a jitless renderer process with the DynamicCode mitigation applied.
135IN_PROC_BROWSER_TEST_F(JitPolicyBrowserTest, JitDisabledImpliesJitless) {
136 ASSERT_TRUE(embedded_test_server()->Start());
137 EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
138 kDisabledDomain, "/title1.html")));
139
140 RenderProcessHost* rph = contents()->GetPrimaryMainFrame()->GetProcess();
141
Elly9ea831d2024-08-29 14:59:41142 // With JIT disabled, the renderer process should be jitless and have the
143 // DynamicCode mitigation applied.
Ellyc2f2ca52024-08-27 17:56:45144 EXPECT_TRUE(RendererIsJitless(rph));
145#if BUILDFLAG(IS_WIN)
146 EXPECT_TRUE(RendererHasDynamicCodeMitigation(rph));
147#endif
148}
149
150// This test asserts that navigating to a JIT-enabled site results in a renderer
151// process that is not jitless and does not have the dynamic code mitigation
152// enabled.
153IN_PROC_BROWSER_TEST_F(JitPolicyBrowserTest, JitEnabledImpliesNoJitless) {
154 ASSERT_TRUE(embedded_test_server()->Start());
155 EXPECT_TRUE(NavigateToURL(
156 shell(), embedded_test_server()->GetURL(kEnabledDomain, "/title1.html")));
157
158 RenderProcessHost* rph = contents()->GetPrimaryMainFrame()->GetProcess();
159
160 // With JIT enabled, the renderer process should have JIT and not have the
161 // DynamicCode mitigation applied.
162 EXPECT_FALSE(RendererIsJitless(rph));
163#if BUILDFLAG(IS_WIN)
164 EXPECT_FALSE(RendererHasDynamicCodeMitigation(rph));
165#endif
166}
167
168} // namespace content