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