123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- // Copyright 2017 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 "sandbox/policy/fuchsia/sandbox_policy_fuchsia.h"
- #include <lib/fdio/spawn.h>
- #include <stdio.h>
- #include <zircon/processargs.h>
- #include <zircon/syscalls/policy.h>
- #include <fuchsia/buildinfo/cpp/fidl.h>
- #include <fuchsia/camera3/cpp/fidl.h>
- #include <fuchsia/fonts/cpp/fidl.h>
- #include <fuchsia/hwinfo/cpp/fidl.h>
- #include <fuchsia/intl/cpp/fidl.h>
- #include <fuchsia/logger/cpp/fidl.h>
- #include <fuchsia/media/cpp/fidl.h>
- #include <fuchsia/memorypressure/cpp/fidl.h>
- #include <fuchsia/net/interfaces/cpp/fidl.h>
- #include <fuchsia/sysmem/cpp/fidl.h>
- #include <fuchsia/ui/scenic/cpp/fidl.h>
- #include <lib/sys/cpp/component_context.h>
- #include <lib/sys/cpp/service_directory.h>
- #include <memory>
- #include <utility>
- #include "base/base_paths.h"
- #include "base/bind.h"
- #include "base/clang_profiling_buildflags.h"
- #include "base/command_line.h"
- #include "base/containers/span.h"
- #include "base/files/file_util.h"
- #include "base/fuchsia/default_job.h"
- #include "base/fuchsia/file_utils.h"
- #include "base/fuchsia/filtered_service_directory.h"
- #include "base/fuchsia/fuchsia_logging.h"
- #include "base/fuchsia/process_context.h"
- #include "base/no_destructor.h"
- #include "base/path_service.h"
- #include "base/process/launch.h"
- #include "base/process/process.h"
- #include "base/threading/thread.h"
- #include "printing/buildflags/buildflags.h"
- #include "sandbox/policy/mojom/sandbox.mojom.h"
- #include "sandbox/policy/switches.h"
- namespace sandbox {
- namespace policy {
- namespace {
- enum SandboxFeature {
- // Clones the job. This is required to start new processes (to make it useful
- // the process will also need access to the fuchsia.process.Launcher service).
- kCloneJob = 1 << 0,
- // Provides access to resources required by Vulkan.
- kProvideVulkanResources = 1 << 1,
- // Read only access to /config/ssl, which contains root certs info.
- kProvideSslConfig = 1 << 2,
- // Allows the process to use the ambient mark-vmo-as-executable capability.
- kAmbientMarkVmoAsExecutable = 1 << 3,
- };
- struct SandboxConfig {
- base::span<const char* const> services;
- uint32_t features;
- };
- // Services that are passed to all processes.
- // Prevent incorrect indentation due to the preprocessor lines within `({...})`:
- // clang-format off
- constexpr auto kMinimalServices = base::make_span((const char* const[]){
- // TODO(crbug.com/1286960): Remove this and/or intl below if an alternative
- // solution does not require access to the service in all processes. For now
- // these services are made available everywhere because they are required by
- // base::SysInfo.
- fuchsia::buildinfo::Provider::Name_,
- fuchsia::hwinfo::Product::Name_,
- // DebugData service is needed only for profiling.
- #if BUILDFLAG(CLANG_PROFILING)
- "fuchsia.debugdata.Publisher",
- #endif
- fuchsia::intl::PropertyProvider::Name_,
- fuchsia::logger::LogSink::Name_,
- });
- // clang-format on
- // For processes that only get kMinimalServices and no other capabilities.
- constexpr SandboxConfig kMinimalConfig = {
- base::span<const char* const>(),
- 0,
- };
- constexpr SandboxConfig kGpuConfig = {
- base::make_span((const char* const[]){
- // TODO(crbug.com/1224707): Use the fuchsia.scheduler API instead.
- fuchsia::media::ProfileProvider::Name_,
- fuchsia::sysmem::Allocator::Name_,
- "fuchsia.vulkan.loader.Loader",
- fuchsia::ui::composition::Allocator::Name_,
- fuchsia::ui::composition::Flatland::Name_,
- fuchsia::ui::scenic::Scenic::Name_,
- }),
- kProvideVulkanResources,
- };
- constexpr SandboxConfig kNetworkConfig = {
- base::make_span((const char* const[]){
- "fuchsia.net.name.Lookup",
- fuchsia::net::interfaces::State::Name_,
- "fuchsia.posix.socket.Provider",
- }),
- kProvideSslConfig,
- };
- constexpr SandboxConfig kRendererConfig = {
- base::make_span((const char* const[]){
- fuchsia::fonts::Provider::Name_,
- // TODO(crbug.com/1224707): Use the fuchsia.scheduler API instead.
- fuchsia::media::ProfileProvider::Name_,
- fuchsia::memorypressure::Provider::Name_,
- fuchsia::sysmem::Allocator::Name_,
- fuchsia::ui::composition::Allocator::Name_,
- }),
- kAmbientMarkVmoAsExecutable,
- };
- constexpr SandboxConfig kVideoCaptureConfig = {
- base::make_span((const char* const[]){
- fuchsia::camera3::DeviceWatcher::Name_,
- fuchsia::sysmem::Allocator::Name_,
- }),
- 0,
- };
- constexpr SandboxConfig kServiceWithJitConfig = {
- base::span<const char* const>(),
- kAmbientMarkVmoAsExecutable,
- };
- const SandboxConfig* GetConfigForSandboxType(sandbox::mojom::Sandbox type) {
- switch (type) {
- case sandbox::mojom::Sandbox::kNoSandbox:
- return nullptr;
- case sandbox::mojom::Sandbox::kGpu:
- return &kGpuConfig;
- case sandbox::mojom::Sandbox::kNetwork:
- return &kNetworkConfig;
- case sandbox::mojom::Sandbox::kRenderer:
- return &kRendererConfig;
- case sandbox::mojom::Sandbox::kVideoCapture:
- return &kVideoCaptureConfig;
- case sandbox::mojom::Sandbox::kServiceWithJit:
- return &kServiceWithJitConfig;
- // Remaining types receive no-access-to-anything.
- case sandbox::mojom::Sandbox::kAudio:
- case sandbox::mojom::Sandbox::kCdm:
- #if BUILDFLAG(ENABLE_PRINTING)
- case sandbox::mojom::Sandbox::kPrintBackend:
- #endif
- case sandbox::mojom::Sandbox::kPrintCompositor:
- case sandbox::mojom::Sandbox::kService:
- case sandbox::mojom::Sandbox::kSpeechRecognition:
- case sandbox::mojom::Sandbox::kUtility:
- return &kMinimalConfig;
- }
- }
- scoped_refptr<base::SequencedTaskRunner> GetServiceDirectoryTaskRunner() {
- static base::NoDestructor<base::Thread> service_directory_thread(
- "svc_directory");
- if (!service_directory_thread->IsRunning()) {
- base::Thread::Options options;
- options.message_pump_type = base::MessagePumpType::IO;
- CHECK(service_directory_thread->StartWithOptions(std::move(options)));
- }
- return service_directory_thread->task_runner();
- }
- void AddServiceCallback(const char* service_name, zx_status_t status) {
- ZX_CHECK(status == ZX_OK, status)
- << "AddService(" << service_name << ") failed";
- }
- void ConnectClientCallback(zx_status_t status) {
- ZX_CHECK(status == ZX_OK, status) << "ConnectClient failed";
- }
- } // namespace
- SandboxPolicyFuchsia::SandboxPolicyFuchsia(sandbox::mojom::Sandbox type) {
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoSandbox)) {
- type_ = sandbox::mojom::Sandbox::kNoSandbox;
- } else {
- type_ = type;
- }
- // If we need to pass some services for the given sandbox type then create
- // |sandbox_directory_| and initialize it with the corresponding list of
- // services. FilteredServiceDirectory must be initialized on a thread that has
- // an async_dispatcher.
- const SandboxConfig* config = GetConfigForSandboxType(type_);
- if (config) {
- filtered_service_directory_ =
- base::SequenceBound<base::FilteredServiceDirectory>(
- GetServiceDirectoryTaskRunner(),
- base::ComponentContextForProcess()->svc());
- for (const char* service_name : kMinimalServices) {
- // |service_name_| points to a compile-time constant in
- // |kMinimalServices|. It will remain valid for the duration of the task.
- filtered_service_directory_
- .AsyncCall(&base::FilteredServiceDirectory::AddService)
- .WithArgs(service_name)
- .Then(base::BindOnce(&AddServiceCallback, service_name));
- }
- for (const char* service_name : config->services) {
- // |service_name_| comes from |config|, which points to a compile-time
- // constant. It will remain valid for the duration of the task.
- filtered_service_directory_
- .AsyncCall(&base::FilteredServiceDirectory::AddService)
- .WithArgs(service_name)
- .Then(base::BindOnce(&AddServiceCallback, service_name));
- }
- filtered_service_directory_
- .AsyncCall(&base::FilteredServiceDirectory::ConnectClient)
- .WithArgs(service_directory_client_.NewRequest())
- .Then(base::BindOnce(&ConnectClientCallback));
- }
- }
- SandboxPolicyFuchsia::~SandboxPolicyFuchsia() = default;
- void SandboxPolicyFuchsia::UpdateLaunchOptionsForSandbox(
- base::LaunchOptions* options) {
- // Always clone stderr to get logs output.
- options->fds_to_remap.push_back(std::make_pair(STDERR_FILENO, STDERR_FILENO));
- options->fds_to_remap.push_back(std::make_pair(STDOUT_FILENO, STDOUT_FILENO));
- if (type_ == sandbox::mojom::Sandbox::kNoSandbox) {
- options->spawn_flags = FDIO_SPAWN_CLONE_NAMESPACE | FDIO_SPAWN_CLONE_JOB;
- options->clear_environment = false;
- return;
- }
- // Map /pkg (read-only files deployed from the package) into the child's
- // namespace.
- options->paths_to_clone.push_back(
- base::FilePath(base::kPackageRootDirectoryPath));
- // If /config/data/tzdata/icu/ exists then it contains up-to-date timezone
- // data which should be provided to all sub-processes, for consistency.
- const auto kIcuTimezoneDataPath = base::FilePath("/config/data/tzdata/icu");
- static bool icu_timezone_data_exists = base::PathExists(kIcuTimezoneDataPath);
- if (icu_timezone_data_exists)
- options->paths_to_clone.push_back(kIcuTimezoneDataPath);
- // Clear environmental variables to better isolate the child from
- // this process.
- options->clear_environment = true;
- // Don't clone anything by default.
- options->spawn_flags = 0;
- // Must get a config here as --no-sandbox bails out earlier.
- const SandboxConfig* config = GetConfigForSandboxType(type_);
- CHECK(config);
- if (config->features & kCloneJob)
- options->spawn_flags |= FDIO_SPAWN_CLONE_JOB;
- if (config->features & kProvideSslConfig)
- options->paths_to_clone.push_back(base::FilePath("/config/ssl"));
- if (config->features & kProvideVulkanResources) {
- static const char* const kPathsToCloneForVulkan[] = {
- // Used configure and access the GPU.
- "/dev/class/gpu", "/config/vulkan/icd.d",
- // Used for Fuchsia Emulator.
- "/dev/class/goldfish-address-space", "/dev/class/goldfish-control",
- "/dev/class/goldfish-pipe", "/dev/class/goldfish-sync"};
- for (const char* path_str : kPathsToCloneForVulkan) {
- base::FilePath path(path_str);
- // Vulkan paths aren't needed with newer Fuchsia versions, so they may not
- // be available.
- if (base::PathExists(path)) {
- options->paths_to_clone.push_back(path);
- }
- }
- }
- // If the process needs access to any services then transfer the
- // |service_directory_client_| handle for it to mount at "/svc".
- if (service_directory_client_) {
- options->paths_to_transfer.push_back(base::PathToTransfer{
- base::FilePath("/svc"),
- service_directory_client_.TakeChannel().release()});
- }
- // Isolate the child process from the call by launching it in its own job.
- zx_status_t status = zx::job::create(*base::GetDefaultJob(), 0, &job_);
- ZX_CHECK(status == ZX_OK, status) << "zx_job_create";
- options->job_handle = job_.get();
- // Only allow the ambient VMO mark-as-executable capability to be granted
- // to processes that need to JIT (i.e. run V8/WASM).
- zx_policy_basic_v2_t ambient_mark_vmo_exec{
- ZX_POL_AMBIENT_MARK_VMO_EXEC,
- // Kill processes which attempt to execute writable VMOs but lack the
- // right to do so.
- (config->features & kAmbientMarkVmoAsExecutable) ? ZX_POL_ACTION_ALLOW
- : ZX_POL_ACTION_KILL,
- // Only grant spawn-capable processes, such as the browser process, the
- // ability to override the execution policy.
- (config->features & kCloneJob) ? ZX_POL_OVERRIDE_ALLOW
- : ZX_POL_OVERRIDE_DENY};
- status = job_.set_policy(ZX_JOB_POL_ABSOLUTE, ZX_JOB_POL_BASIC_V2,
- &ambient_mark_vmo_exec, 1);
- ZX_CHECK(status == ZX_OK, status) << "zx_job_set_policy";
- }
- } // namespace policy
- } // namespace sandbox
|