// Copyright 2020 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <string> #include <vector> #include "base/command_line.h" #include "base/functional/bind.h" #include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "content/browser/utility_process_host.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_features.h" #include "content/public/test/browser_test.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/test_service.mojom.h" #include "content/test/sandbox_status.test-mojom.h" #include "media/gpu/buildflags.h" #include "mojo/public/cpp/bindings/remote.h" #include "ppapi/buildflags/buildflags.h" #include "sandbox/policy/linux/sandbox_linux.h" #include "sandbox/policy/mojom/sandbox.mojom.h" #include "sandbox/policy/sandbox_type.h" #include "sandbox/policy/switches.h" #if BUILDFLAG(IS_CHROMEOS) #include "chromeos/ash/components/assistant/buildflags.h" #endif // BUILDFLAG(IS_CHROMEOS) using sandbox::mojom::Sandbox; using sandbox::policy::SandboxLinux; namespace { std::vector<Sandbox> GetSandboxTypesToTest() { std::vector<Sandbox> types; // We need the standard sandbox config to run this test. if (base::CommandLine::ForCurrentProcess()->HasSwitch( sandbox::policy::switches::kNoSandbox)) { return types; } for (Sandbox t = Sandbox::kNoSandbox; t <= Sandbox::kMaxValue; t = static_cast<Sandbox>(static_cast<int>(t) + 1)) { // These sandbox types can't be spawned in a utility process. if (t == Sandbox::kRenderer || t == Sandbox::kGpu || t == Sandbox::kZygoteIntermediateSandbox) { continue; } types.push_back(t); } return types; } } // namespace namespace content { constexpr char kTestProcessName[] = "sandbox_test_process"; class UtilityProcessSandboxBrowserTest : public ContentBrowserTest, public ::testing::WithParamInterface<Sandbox> { public: UtilityProcessSandboxBrowserTest() = default; ~UtilityProcessSandboxBrowserTest() override = default; protected: void RunUtilityProcess() { DCHECK_CURRENTLY_ON(BrowserThread::UI); base::RunLoop run_loop; done_closure_ = base::BindOnce(&UtilityProcessSandboxBrowserTest::DoneRunning, base::Unretained(this), run_loop.QuitClosure()); UtilityProcessHost* host = new UtilityProcessHost(); host->SetSandboxType(GetParam()); host->SetName(u"SandboxTestProcess"); host->SetMetricsName(kTestProcessName); EXPECT_TRUE(host->Start()); host->GetChildProcess()->BindReceiver( service_.BindNewPipeAndPassReceiver()); service_->GetSandboxStatus( base::BindOnce(&UtilityProcessSandboxBrowserTest::OnGotSandboxStatus, base::Unretained(this))); run_loop.Run(); } private: void OnGotSandboxStatus(int32_t sandbox_status) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Aside from kNoSandbox, every utility process launched explicitly with a // sandbox type should always end up with a sandbox. switch (GetParam()) { case Sandbox::kNoSandbox: EXPECT_EQ(sandbox_status, 0); break; case Sandbox::kCdm: #if BUILDFLAG(ENABLE_PPAPI) case Sandbox::kPpapi: #endif case Sandbox::kOnDeviceModelExecution: case Sandbox::kPrintCompositor: case Sandbox::kService: case Sandbox::kServiceWithJit: case Sandbox::kUtility: { constexpr int kExpectedFullSandboxFlags = SandboxLinux::kPIDNS | SandboxLinux::kNetNS | SandboxLinux::kSeccompBPF | SandboxLinux::kYama | SandboxLinux::kSeccompTSYNC | SandboxLinux::kUserNS; EXPECT_EQ(sandbox_status, kExpectedFullSandboxFlags); break; } case Sandbox::kAudio: #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) case Sandbox::kHardwareVideoDecoding: #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #if BUILDFLAG(IS_CHROMEOS) case Sandbox::kIme: case Sandbox::kTts: case Sandbox::kNearby: #if BUILDFLAG(ENABLE_CROS_LIBASSISTANT) case Sandbox::kLibassistant: #endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS) #if BUILDFLAG(IS_LINUX) case Sandbox::kVideoEffects: case Sandbox::kOnDeviceTranslation: #endif case Sandbox::kHardwareVideoEncoding: case Sandbox::kNetwork: case Sandbox::kPrintBackend: case Sandbox::kScreenAI: case Sandbox::kSpeechRecognition: { constexpr int kExpectedPartialSandboxFlags = SandboxLinux::kSeccompBPF | SandboxLinux::kYama | SandboxLinux::kSeccompTSYNC; EXPECT_EQ(sandbox_status, kExpectedPartialSandboxFlags); break; } case Sandbox::kGpu: case Sandbox::kRenderer: case Sandbox::kZygoteIntermediateSandbox: NOTREACHED(); } service_.reset(); GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(done_closure_)); } void DoneRunning(base::OnceClosure quit_closure) { DCHECK_CURRENTLY_ON(BrowserThread::UI); std::move(quit_closure).Run(); } mojo::Remote<mojom::SandboxStatusService> service_; base::OnceClosure done_closure_; }; IN_PROC_BROWSER_TEST_P(UtilityProcessSandboxBrowserTest, VerifySandboxType) { #if BUILDFLAG(IS_LINUX) || (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(USE_VAAPI) && \ !BUILDFLAG(USE_V4L2_CODEC)) if (GetParam() == Sandbox::kHardwareVideoDecoding) { // TODO(b/195769334): On Linux, this test fails with // Sandbox::kHardwareVideoDecoding because the pre-sandbox hook needs Ozone // which is not available in the utility process that this test starts. We // need to remove the Ozone dependency and re-enable this test. // // TODO(b/195769334): this test fails on linux-chromeos-rel because neither // USE_VAAPI nor USE_V4L2_CODEC are set and the sandbox policy doesn't like // that. In ChromeOS builds for real devices, one of the two flags is set, // so this is not a big problem. However, we should consider making // kHardwareVideoDecoding exist only when either USE_VAAPI or USE_V4L2_CODEC // are set. GTEST_SKIP(); } #endif #if BUILDFLAG(IS_LINUX) if (GetParam() == Sandbox::kHardwareVideoEncoding) { // TODO(b/248540499): On Linux, this test fails with // Sandbox::kHardwareVideoEncoding because the pre-sandbox hook needs Ozone // which is not available in the utility process that this test starts. We // need to remove the Ozone dependency and re-enable this test. GTEST_SKIP(); } #endif RunUtilityProcess(); } INSTANTIATE_TEST_SUITE_P( All, UtilityProcessSandboxBrowserTest, testing::ValuesIn(GetSandboxTypesToTest()), [](const testing::TestParamInfo< UtilityProcessSandboxBrowserTest::ParamType>& info) { auto name = sandbox::policy::StringFromUtilitySandboxType(info.param); name[0] = base::ToUpperASCII(name[0]); return name; }); // In some configurations (e.g. Linux ASAN) GetSandboxTypesToTest() returns an // empty list. Suppress runtime warnings about unparameterized tests. See // https://crbug.com/1192206 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UtilityProcessSandboxBrowserTest); } // namespace content