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 
5 #include "content/gpu/gpu_sandbox_hook_linux.h"
6 
7 #include <dlfcn.h>
8 #include <errno.h>
9 #include <sys/stat.h>
10 
11 #include <memory>
12 #include <sstream>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/bind.h"
17 #include "base/files/file_enumerator.h"
18 #include "base/files/scoped_file.h"
19 #include "base/logging.h"
20 #include "base/strings/stringprintf.h"
21 #include "build/build_config.h"
22 #include "build/buildflag.h"
23 #include "content/common/set_process_title.h"
24 #include "content/public/common/content_switches.h"
25 #include "media/gpu/buildflags.h"
26 #include "sandbox/linux/bpf_dsl/policy.h"
27 #include "sandbox/linux/syscall_broker/broker_command.h"
28 #include "sandbox/linux/syscall_broker/broker_file_permission.h"
29 #include "sandbox/linux/syscall_broker/broker_process.h"
30 #include "sandbox/policy/chromecast_sandbox_allowlist_buildflags.h"
31 #include "sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.h"
32 #include "sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.h"
33 #include "sandbox/policy/linux/bpf_gpu_policy_linux.h"
34 #include "sandbox/policy/linux/sandbox_linux.h"
35 
36 using sandbox::bpf_dsl::Policy;
37 using sandbox::syscall_broker::BrokerFilePermission;
38 using sandbox::syscall_broker::BrokerProcess;
39 
40 namespace content {
41 namespace {
42 
IsChromeOS()43 inline bool IsChromeOS() {
44 #if defined(OS_CHROMEOS)
45   return true;
46 #else
47   return false;
48 #endif
49 }
50 
UseChromecastSandboxAllowlist()51 inline bool UseChromecastSandboxAllowlist() {
52 #if BUILDFLAG(ENABLE_CHROMECAST_GPU_SANDBOX_ALLOWLIST)
53   return true;
54 #else
55   return false;
56 #endif
57 }
58 
IsArchitectureArm()59 inline bool IsArchitectureArm() {
60 #if defined(ARCH_CPU_ARM_FAMILY)
61   return true;
62 #else
63   return false;
64 #endif
65 }
66 
UseV4L2Codec()67 inline bool UseV4L2Codec() {
68 #if BUILDFLAG(USE_V4L2_CODEC)
69   return true;
70 #else
71   return false;
72 #endif
73 }
74 
UseLibV4L2()75 inline bool UseLibV4L2() {
76 #if BUILDFLAG(USE_LIBV4L2)
77   return true;
78 #else
79   return false;
80 #endif
81 }
82 
83 #if defined(OS_CHROMEOS) && defined(__aarch64__)
84 static const char kLibGlesPath[] = "/usr/lib64/libGLESv2.so.2";
85 static const char kLibEglPath[] = "/usr/lib64/libEGL.so.1";
86 static const char kLibMaliPath[] = "/usr/lib64/libmali.so";
87 static const char kLibTegraPath[] = "/usr/lib64/libtegrav4l2.so";
88 static const char kLibV4l2Path[] = "/usr/lib64/libv4l2.so";
89 static const char kLibV4lEncPluginPath[] =
90     "/usr/lib64/libv4l/plugins/libv4l-encplugin.so";
91 #else
92 static const char kLibGlesPath[] = "/usr/lib/libGLESv2.so.2";
93 static const char kLibEglPath[] = "/usr/lib/libEGL.so.1";
94 static const char kLibMaliPath[] = "/usr/lib/libmali.so";
95 static const char kLibTegraPath[] = "/usr/lib/libtegrav4l2.so";
96 static const char kLibV4l2Path[] = "/usr/lib/libv4l2.so";
97 static const char kLibV4lEncPluginPath[] =
98     "/usr/lib/libv4l/plugins/libv4l-encplugin.so";
99 #endif
100 
101 constexpr int dlopen_flag = RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE;
102 
AddV4L2GpuPermissions(std::vector<BrokerFilePermission> * permissions,const sandbox::policy::SandboxSeccompBPF::Options & options)103 void AddV4L2GpuPermissions(
104     std::vector<BrokerFilePermission>* permissions,
105     const sandbox::policy::SandboxSeccompBPF::Options& options) {
106   if (options.accelerated_video_decode_enabled) {
107     // Device nodes for V4L2 video decode accelerator drivers.
108     // We do not use a FileEnumerator because the device files may not exist
109     // yet when the sandbox is created. But since we are restricting access
110     // to the video-dec* and media-dec* prefixes we know that we cannot
111     // authorize a non-decoder device by accident.
112     static constexpr size_t MAX_V4L2_DECODERS = 5;
113     static const base::FilePath::CharType kDevicePath[] =
114         FILE_PATH_LITERAL("/dev/");
115     static const base::FilePath::CharType kVideoDecBase[] = "video-dec";
116     static const base::FilePath::CharType kMediaDecBase[] = "media-dec";
117     for (size_t i = 0; i < MAX_V4L2_DECODERS; i++) {
118       std::ostringstream decoderPath;
119       decoderPath << kDevicePath << kVideoDecBase << i;
120       permissions->push_back(
121           BrokerFilePermission::ReadWrite(decoderPath.str()));
122 
123       std::ostringstream mediaDevicePath;
124       mediaDevicePath << kDevicePath << kMediaDecBase << i;
125       permissions->push_back(
126           BrokerFilePermission::ReadWrite(mediaDevicePath.str()));
127     }
128   }
129 
130   // Image processor used on ARM platforms.
131   static const char kDevImageProc0Path[] = "/dev/image-proc0";
132   permissions->push_back(BrokerFilePermission::ReadWrite(kDevImageProc0Path));
133 
134   if (options.accelerated_video_encode_enabled) {
135     // Device node for V4L2 video encode accelerator drivers.
136     // See comments above for why we don't use a FileEnumerator.
137     static constexpr size_t MAX_V4L2_ENCODERS = 5;
138     static const base::FilePath::CharType kVideoEncBase[] = "/dev/video-enc";
139     permissions->push_back(BrokerFilePermission::ReadWrite(kVideoEncBase));
140     for (size_t i = 0; i < MAX_V4L2_ENCODERS; i++) {
141       std::ostringstream encoderPath;
142       encoderPath << kVideoEncBase << i;
143       permissions->push_back(
144           BrokerFilePermission::ReadWrite(encoderPath.str()));
145     }
146   }
147 
148   // Device node for V4L2 JPEG decode accelerator drivers.
149   static const char kDevJpegDecPath[] = "/dev/jpeg-dec";
150   permissions->push_back(BrokerFilePermission::ReadWrite(kDevJpegDecPath));
151 
152   // Device node for V4L2 JPEG encode accelerator drivers.
153   static const char kDevJpegEncPath[] = "/dev/jpeg-enc";
154   permissions->push_back(BrokerFilePermission::ReadWrite(kDevJpegEncPath));
155 
156   if (UseChromecastSandboxAllowlist()) {
157     static const char kAmlogicAvcEncoderPath[] = "/dev/amvenc_avc";
158     permissions->push_back(
159         BrokerFilePermission::ReadWrite(kAmlogicAvcEncoderPath));
160   }
161 }
162 
AddArmMaliGpuPermissions(std::vector<BrokerFilePermission> * permissions)163 void AddArmMaliGpuPermissions(std::vector<BrokerFilePermission>* permissions) {
164   // Device file needed by the ARM GPU userspace.
165   static const char kMali0Path[] = "/dev/mali0";
166 
167   permissions->push_back(BrokerFilePermission::ReadWrite(kMali0Path));
168 
169   // Non-privileged render nodes for format enumeration.
170   // https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#render-nodes
171   base::FileEnumerator enumerator(
172       base::FilePath(FILE_PATH_LITERAL("/dev/dri/")), false /* recursive */,
173       base::FileEnumerator::FILES, FILE_PATH_LITERAL("renderD*"));
174   for (base::FilePath name = enumerator.Next(); !name.empty();
175        name = enumerator.Next()) {
176     permissions->push_back(BrokerFilePermission::ReadWrite(name.value()));
177   }
178 }
179 
AddImgPvrGpuPermissions(std::vector<BrokerFilePermission> * permissions)180 void AddImgPvrGpuPermissions(std::vector<BrokerFilePermission>* permissions) {
181   // Device node needed by the IMG GPU userspace.
182   static const char kPvrSyncPath[] = "/dev/pvr_sync";
183 
184   permissions->push_back(BrokerFilePermission::ReadWrite(kPvrSyncPath));
185 }
186 
AddAmdGpuPermissions(std::vector<BrokerFilePermission> * permissions)187 void AddAmdGpuPermissions(std::vector<BrokerFilePermission>* permissions) {
188   static const char* const kReadOnlyList[] = {"/etc/ld.so.cache",
189                                               "/usr/lib64/libEGL.so.1",
190                                               "/usr/lib64/libGLESv2.so.2"};
191   for (const char* item : kReadOnlyList)
192     permissions->push_back(BrokerFilePermission::ReadOnly(item));
193 
194   static const char* const kReadWriteList[] = {
195       "/dev/dri",
196       "/dev/dri/card0",
197       "/dev/dri/controlD64",
198       "/dev/dri/renderD128",
199       "/sys/class/drm/card0/device/config",
200       "/sys/class/drm/controlD64/device/config",
201       "/sys/class/drm/renderD128/device/config",
202       "/usr/share/libdrm/amdgpu.ids"};
203   for (const char* item : kReadWriteList)
204     permissions->push_back(BrokerFilePermission::ReadWrite(item));
205 
206   static const char* kDevices[] = {"/sys/dev/char", "/sys/devices"};
207   for (const char* item : kDevices) {
208     std::string path(item);
209     permissions->push_back(
210         BrokerFilePermission::StatOnlyWithIntermediateDirs(path));
211     permissions->push_back(BrokerFilePermission::ReadOnlyRecursive(path + "/"));
212   }
213 }
214 
AddIntelGpuPermissions(std::vector<BrokerFilePermission> * permissions)215 void AddIntelGpuPermissions(std::vector<BrokerFilePermission>* permissions) {
216   static const char* const kReadOnlyList[] = {
217       "/dev/dri",
218       "/usr/share/vulkan/icd.d",
219       "/usr/share/vulkan/icd.d/intel_icd.x86_64.json"};
220   for (const char* item : kReadOnlyList)
221     permissions->push_back(BrokerFilePermission::ReadOnly(item));
222 
223   // TODO(hob): Allow all valid render node paths.
224   static const char kRenderNodePath[] = "/dev/dri/renderD128";
225   struct stat st;
226   if (stat(kRenderNodePath, &st) == 0) {
227     permissions->push_back(BrokerFilePermission::ReadWrite(kRenderNodePath));
228 
229     uint32_t major = (static_cast<uint32_t>(st.st_rdev) >> 8) & 0xff;
230     uint32_t minor = static_cast<uint32_t>(st.st_rdev) & 0xff;
231     std::string char_device_path =
232         base::StringPrintf("/sys/dev/char/%u:%u/", major, minor);
233     permissions->push_back(
234         BrokerFilePermission::ReadOnlyRecursive(char_device_path));
235   }
236 }
237 
AddArmGpuPermissions(std::vector<BrokerFilePermission> * permissions)238 void AddArmGpuPermissions(std::vector<BrokerFilePermission>* permissions) {
239   // On ARM we're enabling the sandbox before the X connection is made,
240   // so we need to allow access to |.Xauthority|.
241   static const char kXAuthorityPath[] = "/home/chronos/.Xauthority";
242   static const char kLdSoCache[] = "/etc/ld.so.cache";
243 
244   // Files needed by the ARM GPU userspace.
245   permissions->push_back(BrokerFilePermission::ReadOnly(kXAuthorityPath));
246   permissions->push_back(BrokerFilePermission::ReadOnly(kLdSoCache));
247   permissions->push_back(BrokerFilePermission::ReadOnly(kLibGlesPath));
248   permissions->push_back(BrokerFilePermission::ReadOnly(kLibEglPath));
249 
250   AddArmMaliGpuPermissions(permissions);
251 }
252 
253 // Need to look in vendor paths for custom vendor implementations.
254 static const char* const kAllowedChromecastPaths[] = {
255     "/oem_cast_shlib/", "/system/vendor/lib/", "/system/lib/",
256     "/system/chrome/lib/"};
257 
AddChromecastArmGpuPermissions(std::vector<BrokerFilePermission> * permissions)258 void AddChromecastArmGpuPermissions(
259     std::vector<BrokerFilePermission>* permissions) {
260   // Device file needed by the ARM GPU userspace.
261   static const char kMali0Path[] = "/dev/mali0";
262   permissions->push_back(BrokerFilePermission::ReadWrite(kMali0Path));
263 
264   // Files needed by the ARM GPU userspace.
265   static const char* const kReadOnlyLibraries[] = {"libGLESv2.so.2",
266                                                    "libEGL.so.1",
267                                                    // Allow ANGLE libraries.
268                                                    "libGLESv2.so", "libEGL.so"};
269 
270   for (const char* library : kReadOnlyLibraries) {
271     for (const char* path : kAllowedChromecastPaths) {
272       const std::string library_path(std::string(path) + std::string(library));
273       permissions->push_back(BrokerFilePermission::ReadOnly(library_path));
274     }
275   }
276 
277   static const char kLdSoCache[] = "/etc/ld.so.cache";
278   permissions->push_back(BrokerFilePermission::ReadOnly(kLdSoCache));
279 
280   base::FileEnumerator enumerator(
281       base::FilePath(FILE_PATH_LITERAL("/dev/dri/")), false /* recursive */,
282       base::FileEnumerator::FILES, FILE_PATH_LITERAL("renderD*"));
283   for (base::FilePath name = enumerator.Next(); !name.empty();
284        name = enumerator.Next()) {
285     permissions->push_back(BrokerFilePermission::ReadWrite(name.value()));
286   }
287 }
288 
AddStandardGpuPermissions(std::vector<BrokerFilePermission> * permissions)289 void AddStandardGpuPermissions(std::vector<BrokerFilePermission>* permissions) {
290   static const char kDriCardBasePath[] = "/dev/dri/card";
291   static const char kNvidiaCtlPath[] = "/dev/nvidiactl";
292   static const char kNvidiaDeviceBasePath[] = "/dev/nvidia";
293   static const char kNvidiaDeviceModeSetPath[] = "/dev/nvidia-modeset";
294   static const char kNvidiaParamsPath[] = "/proc/driver/nvidia/params";
295   static const char kDevShm[] = "/dev/shm/";
296   static const char kVulkanIcdPath[] = "/usr/share/vulkan/icd.d";
297   static const char kNvidiaVulkanIcd[] =
298       "/usr/share/vulkan/icd.d/nvidia_icd.json";
299 
300   // For shared memory.
301   permissions->push_back(
302       BrokerFilePermission::ReadWriteCreateTemporaryRecursive(kDevShm));
303 
304   // For DRI cards.
305   for (int i = 0; i <= 9; ++i) {
306     permissions->push_back(BrokerFilePermission::ReadWrite(
307         base::StringPrintf("%s%d", kDriCardBasePath, i)));
308   }
309 
310   // For Nvidia GLX driver.
311   permissions->push_back(BrokerFilePermission::ReadWrite(kNvidiaCtlPath));
312   for (int i = 0; i < 10; ++i) {
313     permissions->push_back(BrokerFilePermission::ReadWrite(
314         base::StringPrintf("%s%d", kNvidiaDeviceBasePath, i)));
315   }
316   permissions->push_back(
317       BrokerFilePermission::ReadWrite(kNvidiaDeviceModeSetPath));
318   permissions->push_back(BrokerFilePermission::ReadOnly(kNvidiaParamsPath));
319 
320   permissions->push_back(BrokerFilePermission::ReadOnly(kVulkanIcdPath));
321   permissions->push_back(BrokerFilePermission::ReadOnly(kNvidiaVulkanIcd));
322 }
323 
FilePermissionsForGpu(const sandbox::policy::SandboxSeccompBPF::Options & options)324 std::vector<BrokerFilePermission> FilePermissionsForGpu(
325     const sandbox::policy::SandboxSeccompBPF::Options& options) {
326   // All GPU process policies need this file brokered out.
327   static const char kDriRcPath[] = "/etc/drirc";
328   std::vector<BrokerFilePermission> permissions = {
329       BrokerFilePermission::ReadOnly(kDriRcPath)};
330 
331   if (IsChromeOS()) {
332     if (UseV4L2Codec())
333       AddV4L2GpuPermissions(&permissions, options);
334     if (IsArchitectureArm()) {
335       AddImgPvrGpuPermissions(&permissions);
336       AddArmGpuPermissions(&permissions);
337       return permissions;
338     }
339     if (options.use_amd_specific_policies) {
340       AddAmdGpuPermissions(&permissions);
341       return permissions;
342     }
343     if (options.use_intel_specific_policies) {
344       AddIntelGpuPermissions(&permissions);
345       return permissions;
346     }
347   }
348 
349   if (UseChromecastSandboxAllowlist()) {
350     if (UseV4L2Codec())
351       AddV4L2GpuPermissions(&permissions, options);
352 
353     if (IsArchitectureArm()) {
354       AddChromecastArmGpuPermissions(&permissions);
355       return permissions;
356     }
357   }
358 
359   AddStandardGpuPermissions(&permissions);
360   return permissions;
361 }
362 
LoadArmGpuLibraries()363 void LoadArmGpuLibraries() {
364 #if !defined(OS_BSD)
365   // Preload the Mali library.
366   if (UseChromecastSandboxAllowlist()) {
367     for (const char* path : kAllowedChromecastPaths) {
368       const std::string library_path(std::string(path) +
369                                      std::string("libMali.so"));
370       if (dlopen(library_path.c_str(), dlopen_flag))
371         break;
372     }
373   } else {
374     dlopen(kLibMaliPath, dlopen_flag);
375 
376     // Preload the Tegra V4L2 (video decode acceleration) library.
377     dlopen(kLibTegraPath, dlopen_flag);
378   }
379 #endif
380 }
381 
LoadAmdGpuLibraries()382 bool LoadAmdGpuLibraries() {
383   // Preload the amdgpu-dependent libraries.
384   if (nullptr == dlopen("libglapi.so", dlopen_flag)) {
385     LOG(ERROR) << "dlopen(libglapi.so) failed with error: " << dlerror();
386     return false;
387   }
388 
389   const char* radeonsi_lib = "/usr/lib64/dri/radeonsi_dri.so";
390 #if defined(DRI_DRIVER_DIR)
391   radeonsi_lib = DRI_DRIVER_DIR "/radeonsi_dri.so";
392 #endif
393   if (nullptr == dlopen(radeonsi_lib, dlopen_flag)) {
394     LOG(ERROR) << "dlopen(radeonsi_dri.so) failed with error: " << dlerror();
395     return false;
396   }
397   return true;
398 }
399 
LoadNvidiaLibraries()400 bool LoadNvidiaLibraries() {
401   // The driver may lazily load libxcb-glx. It's not an error on wayland-only
402   // systems for the library to be missing.
403   if (!dlopen("libxcb-glx.so.0", dlopen_flag))
404     LOG(WARNING) << "dlopen(libxcb-glx.so.0) failed with error: " << dlerror();
405   return true;
406 }
407 
IsAcceleratedVideoEnabled(const sandbox::policy::SandboxSeccompBPF::Options & options)408 bool IsAcceleratedVideoEnabled(
409     const sandbox::policy::SandboxSeccompBPF::Options& options) {
410   return options.accelerated_video_encode_enabled ||
411          options.accelerated_video_decode_enabled;
412 }
413 
LoadV4L2Libraries(const sandbox::policy::SandboxSeccompBPF::Options & options)414 void LoadV4L2Libraries(
415     const sandbox::policy::SandboxSeccompBPF::Options& options) {
416   if (IsAcceleratedVideoEnabled(options) && UseLibV4L2()) {
417     dlopen(kLibV4l2Path, dlopen_flag);
418 
419     if (options.accelerated_video_encode_enabled) {
420       // This is a device-specific encoder plugin.
421       dlopen(kLibV4lEncPluginPath, dlopen_flag);
422     }
423   }
424 }
425 
LoadChromecastV4L2Libraries()426 void LoadChromecastV4L2Libraries() {
427   for (const char* path : kAllowedChromecastPaths) {
428     const std::string library_path(std::string(path) +
429                                    std::string("libvpcodec.so"));
430     if (dlopen(library_path.c_str(), dlopen_flag))
431       break;
432   }
433 }
434 
LoadLibrariesForGpu(const sandbox::policy::SandboxSeccompBPF::Options & options)435 bool LoadLibrariesForGpu(
436     const sandbox::policy::SandboxSeccompBPF::Options& options) {
437   if (IsChromeOS()) {
438     if (UseV4L2Codec())
439       LoadV4L2Libraries(options);
440     if (IsArchitectureArm()) {
441       LoadArmGpuLibraries();
442       return true;
443     }
444     if (options.use_amd_specific_policies)
445       return LoadAmdGpuLibraries();
446   } else {
447     if (UseChromecastSandboxAllowlist() && IsArchitectureArm()) {
448       LoadArmGpuLibraries();
449       if (UseV4L2Codec())
450         LoadChromecastV4L2Libraries();
451     }
452     if (options.use_nvidia_specific_policies)
453       return LoadNvidiaLibraries();
454   }
455   return true;
456 }
457 
CommandSetForGPU(const sandbox::policy::SandboxLinux::Options & options)458 sandbox::syscall_broker::BrokerCommandSet CommandSetForGPU(
459     const sandbox::policy::SandboxLinux::Options& options) {
460   sandbox::syscall_broker::BrokerCommandSet command_set;
461   command_set.set(sandbox::syscall_broker::COMMAND_ACCESS);
462   command_set.set(sandbox::syscall_broker::COMMAND_OPEN);
463   command_set.set(sandbox::syscall_broker::COMMAND_STAT);
464   if (IsChromeOS() && (options.use_amd_specific_policies ||
465                        options.use_intel_specific_policies)) {
466     command_set.set(sandbox::syscall_broker::COMMAND_READLINK);
467   }
468   return command_set;
469 }
470 
BrokerProcessPreSandboxHook(sandbox::policy::SandboxLinux::Options options)471 bool BrokerProcessPreSandboxHook(
472     sandbox::policy::SandboxLinux::Options options) {
473   // Oddly enough, we call back into gpu to invoke this service manager
474   // method, since it is part of the embedder component, and the service
475   // mananger's sandbox component is a lower layer that can't depend on it.
476   SetProcessTitleFromCommandLine(nullptr);
477   return true;
478 }
479 
480 }  // namespace
481 
GpuProcessPreSandboxHook(sandbox::policy::SandboxLinux::Options options)482 bool GpuProcessPreSandboxHook(sandbox::policy::SandboxLinux::Options options) {
483   sandbox::policy::SandboxLinux::GetInstance()->StartBrokerProcess(
484       CommandSetForGPU(options), FilePermissionsForGpu(options),
485       base::BindOnce(BrokerProcessPreSandboxHook), options);
486 
487   if (!LoadLibrariesForGpu(options))
488     return false;
489 
490   // TODO(tsepez): enable namspace sandbox here once crashes are understood.
491 
492   errno = 0;
493   return true;
494 }
495 
496 }  // namespace content
497