1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138

media / gpu / sandbox / hardware_video_encoding_sandbox_hook_linux.cc [blame]

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.h"

#include <dlfcn.h>
#include <sys/stat.h>

#include "base/strings/stringprintf.h"
#include "media/gpu/buildflags.h"

#if BUILDFLAG(USE_VAAPI)
#include "media/gpu/vaapi/vaapi_wrapper.h"
#endif

#if BUILDFLAG(USE_V4L2_CODEC)
#include "media/gpu/v4l2/v4l2_device.h"
#endif

using sandbox::syscall_broker::BrokerFilePermission;

namespace media {

bool HardwareVideoEncodingPreSandboxHook(
    sandbox::policy::SandboxLinux::Options options) {
  sandbox::syscall_broker::BrokerCommandSet command_set;
  std::vector<BrokerFilePermission> permissions;

#if BUILDFLAG(USE_V4L2_CODEC)
  command_set.set(sandbox::syscall_broker::COMMAND_OPEN);

  // Device nodes for V4L2 video encode accelerator drivers.
  // We do not use a FileEnumerator because the device files may not exist
  // yet when the sandbox is created. But since we are restricting access
  // to the video-enc* prefix we know that we cannot
  // authorize a non-encoder device by accident.
  static constexpr size_t MAX_V4L2_ENCODERS = 5;
  static const base::FilePath::CharType kVideoEncBase[] = "/dev/video-enc";
  permissions.push_back(BrokerFilePermission::ReadWrite(kVideoEncBase));
  for (size_t i = 0; i < MAX_V4L2_ENCODERS; i++) {
    std::ostringstream encoderPath;
    encoderPath << kVideoEncBase << i;
    permissions.push_back(BrokerFilePermission::ReadWrite(encoderPath.str()));
  }

  // Image processor used on ARM platforms.
  // TODO(b/248528896): it's possible not all V4L2 platforms need an image
  // processor for video encoding. Look into whether we can restrict this
  // permission to only platforms that need it.
  static const char kDevImageProc0Path[] = "/dev/image-proc0";
  permissions.push_back(BrokerFilePermission::ReadWrite(kDevImageProc0Path));
#elif BUILDFLAG(USE_VAAPI)
  command_set.set(sandbox::syscall_broker::COMMAND_OPEN);
  command_set.set(sandbox::syscall_broker::COMMAND_STAT);
  command_set.set(sandbox::syscall_broker::COMMAND_ACCESS);

  if (options.use_amd_specific_policies) {
    command_set.set(sandbox::syscall_broker::COMMAND_READLINK);

    permissions.push_back(BrokerFilePermission::ReadOnly("/dev/dri"));

    static const char* kDevices[] = {"/sys/dev/char", "/sys/devices"};
    for (const char* item : kDevices) {
      std::string path(item);
      permissions.push_back(
          BrokerFilePermission::StatOnlyWithIntermediateDirs(path));
      permissions.push_back(
          BrokerFilePermission::ReadOnlyRecursive(path + "/"));
    }

    permissions.push_back(
        BrokerFilePermission::ReadOnly("/usr/share/vulkan/icd.d"));
    permissions.push_back(BrokerFilePermission::ReadOnly(
        "/usr/share/vulkan/icd.d/radeon_icd.x86_64.json"));
  }
#endif

  // TODO(b/248528896): figure out if the render node needs to be opened after
  // entering the sandbox or if this can be restricted based on API (VA-API vs.
  // V4L2) or use case (e.g., Chrome vs. ARC++/ARCVM).
  for (int i = 128; i <= 137; ++i) {
    const std::string path = base::StringPrintf("/dev/dri/renderD%d", i);
    struct stat st;
    if (stat(path.c_str(), &st) == 0) {
      permissions.push_back(options.use_amd_specific_policies
                                ? BrokerFilePermission::ReadWrite(path)
                                : BrokerFilePermission::ReadOnly(path));

#if BUILDFLAG(USE_VAAPI)
      uint32_t major = (static_cast<uint32_t>(st.st_rdev) >> 8) & 0xff;
      uint32_t minor = static_cast<uint32_t>(st.st_rdev) & 0xff;
      std::string char_device_path =
          base::StringPrintf("/sys/dev/char/%u:%u/", major, minor);
      permissions.push_back(
          BrokerFilePermission::ReadOnlyRecursive(char_device_path));
#endif  // BUILDFLAG(USE_VAAPI)
    }
  }

  sandbox::policy::SandboxLinux::GetInstance()->StartBrokerProcess(
      command_set, permissions, options);

  // TODO(b/248528896): the hardware video encoding sandbox is really only
  // useful when building with VA-API or V4L2 (otherwise, we're not really doing
  // hardware video encoding). Consider restricting the kHardwareVideoEncoding
  // sandbox type to exist only in those configurations so that the presandbox
  // hook is only reached in those scenarios. As it is now,
  // kHardwareVideoEncoding exists for all ash-chrome builds because
  // chrome/browser/ash/arc/video/gpu_arc_video_service_host.cc is expected to
  // depend on it eventually and that file is built for ash-chrome regardless
  // of VA-API/V4L2. That means that bots like linux-chromeos-rel would end up
  // reaching this presandbox hook.
#if BUILDFLAG(USE_VAAPI)
  VaapiWrapper::PreSandboxInitialization(/*allow_disabling_global_lock=*/true);

  if (options.use_amd_specific_policies) {
    constexpr int kDlopenFlags = RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE;
    const char* radeonsi_lib = "/usr/lib64/dri/radeonsi_dri.so";
#if defined(DRI_DRIVER_DIR)
    radeonsi_lib = DRI_DRIVER_DIR "/radeonsi_dri.so";
#endif
    if (nullptr == dlopen(radeonsi_lib, kDlopenFlags)) {
      LOG(ERROR) << "dlopen(radeonsi_dri.so) failed with error: " << dlerror();
      return false;
    }

    // minigbm may use the DRI driver (requires Mesa 24.0 or older) or the
    // Vulkan driver (requires VK_EXT_image_drm_format_modifier).  Preload the
    // Vulkan driver as well but ignore failures.
    dlopen("libvulkan.so.1", kDlopenFlags);
    dlopen("libvulkan_radeon.so", kDlopenFlags);
  }
#endif
  return true;
}

}  // namespace media