sandbox_policy_fuchsia.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. // Copyright 2017 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. #include "sandbox/policy/fuchsia/sandbox_policy_fuchsia.h"
  5. #include <lib/fdio/spawn.h>
  6. #include <stdio.h>
  7. #include <zircon/processargs.h>
  8. #include <zircon/syscalls/policy.h>
  9. #include <fuchsia/buildinfo/cpp/fidl.h>
  10. #include <fuchsia/camera3/cpp/fidl.h>
  11. #include <fuchsia/fonts/cpp/fidl.h>
  12. #include <fuchsia/hwinfo/cpp/fidl.h>
  13. #include <fuchsia/intl/cpp/fidl.h>
  14. #include <fuchsia/logger/cpp/fidl.h>
  15. #include <fuchsia/media/cpp/fidl.h>
  16. #include <fuchsia/memorypressure/cpp/fidl.h>
  17. #include <fuchsia/net/interfaces/cpp/fidl.h>
  18. #include <fuchsia/sysmem/cpp/fidl.h>
  19. #include <fuchsia/ui/scenic/cpp/fidl.h>
  20. #include <lib/sys/cpp/component_context.h>
  21. #include <lib/sys/cpp/service_directory.h>
  22. #include <memory>
  23. #include <utility>
  24. #include "base/base_paths.h"
  25. #include "base/bind.h"
  26. #include "base/clang_profiling_buildflags.h"
  27. #include "base/command_line.h"
  28. #include "base/containers/span.h"
  29. #include "base/files/file_util.h"
  30. #include "base/fuchsia/default_job.h"
  31. #include "base/fuchsia/file_utils.h"
  32. #include "base/fuchsia/filtered_service_directory.h"
  33. #include "base/fuchsia/fuchsia_logging.h"
  34. #include "base/fuchsia/process_context.h"
  35. #include "base/no_destructor.h"
  36. #include "base/path_service.h"
  37. #include "base/process/launch.h"
  38. #include "base/process/process.h"
  39. #include "base/threading/thread.h"
  40. #include "printing/buildflags/buildflags.h"
  41. #include "sandbox/policy/mojom/sandbox.mojom.h"
  42. #include "sandbox/policy/switches.h"
  43. namespace sandbox {
  44. namespace policy {
  45. namespace {
  46. enum SandboxFeature {
  47. // Clones the job. This is required to start new processes (to make it useful
  48. // the process will also need access to the fuchsia.process.Launcher service).
  49. kCloneJob = 1 << 0,
  50. // Provides access to resources required by Vulkan.
  51. kProvideVulkanResources = 1 << 1,
  52. // Read only access to /config/ssl, which contains root certs info.
  53. kProvideSslConfig = 1 << 2,
  54. // Allows the process to use the ambient mark-vmo-as-executable capability.
  55. kAmbientMarkVmoAsExecutable = 1 << 3,
  56. };
  57. struct SandboxConfig {
  58. base::span<const char* const> services;
  59. uint32_t features;
  60. };
  61. // Services that are passed to all processes.
  62. // Prevent incorrect indentation due to the preprocessor lines within `({...})`:
  63. // clang-format off
  64. constexpr auto kMinimalServices = base::make_span((const char* const[]){
  65. // TODO(crbug.com/1286960): Remove this and/or intl below if an alternative
  66. // solution does not require access to the service in all processes. For now
  67. // these services are made available everywhere because they are required by
  68. // base::SysInfo.
  69. fuchsia::buildinfo::Provider::Name_,
  70. fuchsia::hwinfo::Product::Name_,
  71. // DebugData service is needed only for profiling.
  72. #if BUILDFLAG(CLANG_PROFILING)
  73. "fuchsia.debugdata.Publisher",
  74. #endif
  75. fuchsia::intl::PropertyProvider::Name_,
  76. fuchsia::logger::LogSink::Name_,
  77. });
  78. // clang-format on
  79. // For processes that only get kMinimalServices and no other capabilities.
  80. constexpr SandboxConfig kMinimalConfig = {
  81. base::span<const char* const>(),
  82. 0,
  83. };
  84. constexpr SandboxConfig kGpuConfig = {
  85. base::make_span((const char* const[]){
  86. // TODO(crbug.com/1224707): Use the fuchsia.scheduler API instead.
  87. fuchsia::media::ProfileProvider::Name_,
  88. fuchsia::sysmem::Allocator::Name_,
  89. "fuchsia.vulkan.loader.Loader",
  90. fuchsia::ui::composition::Allocator::Name_,
  91. fuchsia::ui::composition::Flatland::Name_,
  92. fuchsia::ui::scenic::Scenic::Name_,
  93. }),
  94. kProvideVulkanResources,
  95. };
  96. constexpr SandboxConfig kNetworkConfig = {
  97. base::make_span((const char* const[]){
  98. "fuchsia.net.name.Lookup",
  99. fuchsia::net::interfaces::State::Name_,
  100. "fuchsia.posix.socket.Provider",
  101. }),
  102. kProvideSslConfig,
  103. };
  104. constexpr SandboxConfig kRendererConfig = {
  105. base::make_span((const char* const[]){
  106. fuchsia::fonts::Provider::Name_,
  107. // TODO(crbug.com/1224707): Use the fuchsia.scheduler API instead.
  108. fuchsia::media::ProfileProvider::Name_,
  109. fuchsia::memorypressure::Provider::Name_,
  110. fuchsia::sysmem::Allocator::Name_,
  111. fuchsia::ui::composition::Allocator::Name_,
  112. }),
  113. kAmbientMarkVmoAsExecutable,
  114. };
  115. constexpr SandboxConfig kVideoCaptureConfig = {
  116. base::make_span((const char* const[]){
  117. fuchsia::camera3::DeviceWatcher::Name_,
  118. fuchsia::sysmem::Allocator::Name_,
  119. }),
  120. 0,
  121. };
  122. constexpr SandboxConfig kServiceWithJitConfig = {
  123. base::span<const char* const>(),
  124. kAmbientMarkVmoAsExecutable,
  125. };
  126. const SandboxConfig* GetConfigForSandboxType(sandbox::mojom::Sandbox type) {
  127. switch (type) {
  128. case sandbox::mojom::Sandbox::kNoSandbox:
  129. return nullptr;
  130. case sandbox::mojom::Sandbox::kGpu:
  131. return &kGpuConfig;
  132. case sandbox::mojom::Sandbox::kNetwork:
  133. return &kNetworkConfig;
  134. case sandbox::mojom::Sandbox::kRenderer:
  135. return &kRendererConfig;
  136. case sandbox::mojom::Sandbox::kVideoCapture:
  137. return &kVideoCaptureConfig;
  138. case sandbox::mojom::Sandbox::kServiceWithJit:
  139. return &kServiceWithJitConfig;
  140. // Remaining types receive no-access-to-anything.
  141. case sandbox::mojom::Sandbox::kAudio:
  142. case sandbox::mojom::Sandbox::kCdm:
  143. #if BUILDFLAG(ENABLE_PRINTING)
  144. case sandbox::mojom::Sandbox::kPrintBackend:
  145. #endif
  146. case sandbox::mojom::Sandbox::kPrintCompositor:
  147. case sandbox::mojom::Sandbox::kService:
  148. case sandbox::mojom::Sandbox::kSpeechRecognition:
  149. case sandbox::mojom::Sandbox::kUtility:
  150. return &kMinimalConfig;
  151. }
  152. }
  153. scoped_refptr<base::SequencedTaskRunner> GetServiceDirectoryTaskRunner() {
  154. static base::NoDestructor<base::Thread> service_directory_thread(
  155. "svc_directory");
  156. if (!service_directory_thread->IsRunning()) {
  157. base::Thread::Options options;
  158. options.message_pump_type = base::MessagePumpType::IO;
  159. CHECK(service_directory_thread->StartWithOptions(std::move(options)));
  160. }
  161. return service_directory_thread->task_runner();
  162. }
  163. void AddServiceCallback(const char* service_name, zx_status_t status) {
  164. ZX_CHECK(status == ZX_OK, status)
  165. << "AddService(" << service_name << ") failed";
  166. }
  167. void ConnectClientCallback(zx_status_t status) {
  168. ZX_CHECK(status == ZX_OK, status) << "ConnectClient failed";
  169. }
  170. } // namespace
  171. SandboxPolicyFuchsia::SandboxPolicyFuchsia(sandbox::mojom::Sandbox type) {
  172. if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoSandbox)) {
  173. type_ = sandbox::mojom::Sandbox::kNoSandbox;
  174. } else {
  175. type_ = type;
  176. }
  177. // If we need to pass some services for the given sandbox type then create
  178. // |sandbox_directory_| and initialize it with the corresponding list of
  179. // services. FilteredServiceDirectory must be initialized on a thread that has
  180. // an async_dispatcher.
  181. const SandboxConfig* config = GetConfigForSandboxType(type_);
  182. if (config) {
  183. filtered_service_directory_ =
  184. base::SequenceBound<base::FilteredServiceDirectory>(
  185. GetServiceDirectoryTaskRunner(),
  186. base::ComponentContextForProcess()->svc());
  187. for (const char* service_name : kMinimalServices) {
  188. // |service_name_| points to a compile-time constant in
  189. // |kMinimalServices|. It will remain valid for the duration of the task.
  190. filtered_service_directory_
  191. .AsyncCall(&base::FilteredServiceDirectory::AddService)
  192. .WithArgs(service_name)
  193. .Then(base::BindOnce(&AddServiceCallback, service_name));
  194. }
  195. for (const char* service_name : config->services) {
  196. // |service_name_| comes from |config|, which points to a compile-time
  197. // constant. It will remain valid for the duration of the task.
  198. filtered_service_directory_
  199. .AsyncCall(&base::FilteredServiceDirectory::AddService)
  200. .WithArgs(service_name)
  201. .Then(base::BindOnce(&AddServiceCallback, service_name));
  202. }
  203. filtered_service_directory_
  204. .AsyncCall(&base::FilteredServiceDirectory::ConnectClient)
  205. .WithArgs(service_directory_client_.NewRequest())
  206. .Then(base::BindOnce(&ConnectClientCallback));
  207. }
  208. }
  209. SandboxPolicyFuchsia::~SandboxPolicyFuchsia() = default;
  210. void SandboxPolicyFuchsia::UpdateLaunchOptionsForSandbox(
  211. base::LaunchOptions* options) {
  212. // Always clone stderr to get logs output.
  213. options->fds_to_remap.push_back(std::make_pair(STDERR_FILENO, STDERR_FILENO));
  214. options->fds_to_remap.push_back(std::make_pair(STDOUT_FILENO, STDOUT_FILENO));
  215. if (type_ == sandbox::mojom::Sandbox::kNoSandbox) {
  216. options->spawn_flags = FDIO_SPAWN_CLONE_NAMESPACE | FDIO_SPAWN_CLONE_JOB;
  217. options->clear_environment = false;
  218. return;
  219. }
  220. // Map /pkg (read-only files deployed from the package) into the child's
  221. // namespace.
  222. options->paths_to_clone.push_back(
  223. base::FilePath(base::kPackageRootDirectoryPath));
  224. // If /config/data/tzdata/icu/ exists then it contains up-to-date timezone
  225. // data which should be provided to all sub-processes, for consistency.
  226. const auto kIcuTimezoneDataPath = base::FilePath("/config/data/tzdata/icu");
  227. static bool icu_timezone_data_exists = base::PathExists(kIcuTimezoneDataPath);
  228. if (icu_timezone_data_exists)
  229. options->paths_to_clone.push_back(kIcuTimezoneDataPath);
  230. // Clear environmental variables to better isolate the child from
  231. // this process.
  232. options->clear_environment = true;
  233. // Don't clone anything by default.
  234. options->spawn_flags = 0;
  235. // Must get a config here as --no-sandbox bails out earlier.
  236. const SandboxConfig* config = GetConfigForSandboxType(type_);
  237. CHECK(config);
  238. if (config->features & kCloneJob)
  239. options->spawn_flags |= FDIO_SPAWN_CLONE_JOB;
  240. if (config->features & kProvideSslConfig)
  241. options->paths_to_clone.push_back(base::FilePath("/config/ssl"));
  242. if (config->features & kProvideVulkanResources) {
  243. static const char* const kPathsToCloneForVulkan[] = {
  244. // Used configure and access the GPU.
  245. "/dev/class/gpu", "/config/vulkan/icd.d",
  246. // Used for Fuchsia Emulator.
  247. "/dev/class/goldfish-address-space", "/dev/class/goldfish-control",
  248. "/dev/class/goldfish-pipe", "/dev/class/goldfish-sync"};
  249. for (const char* path_str : kPathsToCloneForVulkan) {
  250. base::FilePath path(path_str);
  251. // Vulkan paths aren't needed with newer Fuchsia versions, so they may not
  252. // be available.
  253. if (base::PathExists(path)) {
  254. options->paths_to_clone.push_back(path);
  255. }
  256. }
  257. }
  258. // If the process needs access to any services then transfer the
  259. // |service_directory_client_| handle for it to mount at "/svc".
  260. if (service_directory_client_) {
  261. options->paths_to_transfer.push_back(base::PathToTransfer{
  262. base::FilePath("/svc"),
  263. service_directory_client_.TakeChannel().release()});
  264. }
  265. // Isolate the child process from the call by launching it in its own job.
  266. zx_status_t status = zx::job::create(*base::GetDefaultJob(), 0, &job_);
  267. ZX_CHECK(status == ZX_OK, status) << "zx_job_create";
  268. options->job_handle = job_.get();
  269. // Only allow the ambient VMO mark-as-executable capability to be granted
  270. // to processes that need to JIT (i.e. run V8/WASM).
  271. zx_policy_basic_v2_t ambient_mark_vmo_exec{
  272. ZX_POL_AMBIENT_MARK_VMO_EXEC,
  273. // Kill processes which attempt to execute writable VMOs but lack the
  274. // right to do so.
  275. (config->features & kAmbientMarkVmoAsExecutable) ? ZX_POL_ACTION_ALLOW
  276. : ZX_POL_ACTION_KILL,
  277. // Only grant spawn-capable processes, such as the browser process, the
  278. // ability to override the execution policy.
  279. (config->features & kCloneJob) ? ZX_POL_OVERRIDE_ALLOW
  280. : ZX_POL_OVERRIDE_DENY};
  281. status = job_.set_policy(ZX_JOB_POL_ABSOLUTE, ZX_JOB_POL_BASIC_V2,
  282. &ambient_mark_vmo_exec, 1);
  283. ZX_CHECK(status == ZX_OK, status) << "zx_job_set_policy";
  284. }
  285. } // namespace policy
  286. } // namespace sandbox