Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/gmp/ChromiumCDMAdapter.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "ChromiumCDMAdapter.h"
7
#include "content_decryption_module.h"
8
#include "content_decryption_module_ext.h"
9
#include "VideoUtils.h"
10
#include "gmp-api/gmp-entrypoints.h"
11
#include "gmp-api/gmp-video-codec.h"
12
#include "WidevineUtils.h"
13
#include "GMPLog.h"
14
#include "mozilla/Move.h"
15
16
#ifdef XP_WIN
17
#include "WinUtils.h"
18
#include "nsWindowsDllInterceptor.h"
19
#include <windows.h>
20
#include <strsafe.h>
21
#include <unordered_map>
22
#include <vector>
23
#else
24
#include <sys/types.h>
25
#include <sys/stat.h>
26
#include <unistd.h>
27
#include <fcntl.h>
28
#endif
29
30
const GMPPlatformAPI* sPlatform = nullptr;
31
32
namespace mozilla {
33
34
#ifdef XP_WIN
35
static void
36
InitializeHooks();
37
#endif
38
39
ChromiumCDMAdapter::ChromiumCDMAdapter(nsTArray<Pair<nsCString, nsCString>>&& aHostPathPairs)
40
0
{
41
#ifdef XP_WIN
42
  InitializeHooks();
43
#endif
44
  PopulateHostFiles(std::move(aHostPathPairs));
45
0
}
46
47
void
48
ChromiumCDMAdapter::SetAdaptee(PRLibrary* aLib)
49
0
{
50
0
  mLib = aLib;
51
0
}
52
53
void*
54
ChromiumCdmHost(int aHostInterfaceVersion, void* aUserData)
55
0
{
56
0
  GMP_LOG("ChromiumCdmHostFunc(%d, %p)", aHostInterfaceVersion, aUserData);
57
0
  if (aHostInterfaceVersion != cdm::Host_9::kVersion &&
58
0
      aHostInterfaceVersion != cdm::Host_10::kVersion) {
59
0
    return nullptr;
60
0
  }
61
0
  return aUserData;
62
0
}
63
64
0
#define STRINGIFY(s) _STRINGIFY(s)
65
0
#define _STRINGIFY(s) #s
66
67
#ifdef MOZILLA_OFFICIAL
68
static cdm::HostFile
69
TakeToCDMHostFile(HostFileData& aHostFileData)
70
{
71
  return cdm::HostFile(aHostFileData.mBinary.Path().get(),
72
                       aHostFileData.mBinary.TakePlatformFile(),
73
                       aHostFileData.mSig.TakePlatformFile());
74
}
75
#endif
76
77
GMPErr
78
ChromiumCDMAdapter::GMPInit(const GMPPlatformAPI* aPlatformAPI)
79
0
{
80
0
  GMP_LOG("ChromiumCDMAdapter::GMPInit");
81
0
  sPlatform = aPlatformAPI;
82
0
  if (!mLib) {
83
0
    return GMPGenericErr;
84
0
  }
85
0
86
#ifdef MOZILLA_OFFICIAL
87
  // Note: we must call the VerifyCdmHost_0 function if it's present before
88
  // we call the initialize function.
89
  auto verify = reinterpret_cast<decltype(::VerifyCdmHost_0)*>(
90
    PR_FindFunctionSymbol(mLib, STRINGIFY(VerifyCdmHost_0)));
91
  if (verify) {
92
    nsTArray<cdm::HostFile> files;
93
    for (HostFileData& hostFile : mHostFiles) {
94
      files.AppendElement(TakeToCDMHostFile(hostFile));
95
    }
96
    bool result = verify(files.Elements(), files.Length());
97
    GMP_LOG("%s VerifyCdmHost_0 returned %d", __func__, result);
98
  }
99
#endif
100
101
0
  auto init = reinterpret_cast<decltype(::INITIALIZE_CDM_MODULE)*>(
102
0
    PR_FindFunctionSymbol(mLib, STRINGIFY(INITIALIZE_CDM_MODULE)));
103
0
  if (!init) {
104
0
    return GMPGenericErr;
105
0
  }
106
0
107
0
  GMP_LOG(STRINGIFY(INITIALIZE_CDM_MODULE) "()");
108
0
  init();
109
0
110
0
  return GMPNoErr;
111
0
}
112
113
GMPErr
114
ChromiumCDMAdapter::GMPGetAPI(const char* aAPIName,
115
                              void* aHostAPI,
116
                              void** aPluginAPI,
117
                              uint32_t aDecryptorId)
118
0
{
119
0
  GMP_LOG("ChromiumCDMAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p",
120
0
          aAPIName,
121
0
          aHostAPI,
122
0
          aPluginAPI,
123
0
          aDecryptorId,
124
0
          this);
125
0
  bool isCDM10 = !strcmp(aAPIName, CHROMIUM_CDM_API);
126
0
  bool isCDM9 = !strcmp(aAPIName, CHROMIUM_CDM_API_BACKWARD_COMPAT);
127
0
  if (isCDM9 || isCDM10) {
128
0
    auto create = reinterpret_cast<decltype(::CreateCdmInstance)*>(
129
0
      PR_FindFunctionSymbol(mLib, "CreateCdmInstance"));
130
0
    if (!create) {
131
0
      GMP_LOG("ChromiumCDMAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p "
132
0
              "FAILED to find CreateCdmInstance",
133
0
              aAPIName,
134
0
              aHostAPI,
135
0
              aPluginAPI,
136
0
              aDecryptorId,
137
0
              this);
138
0
      return GMPGenericErr;
139
0
    }
140
0
141
0
    int version = isCDM9 ? cdm::ContentDecryptionModule_9::kVersion
142
0
                         : cdm::ContentDecryptionModule_10::kVersion;
143
0
    void* cdm =
144
0
      create(version,
145
0
             kEMEKeySystemWidevine.get(),
146
0
             kEMEKeySystemWidevine.Length(),
147
0
             &ChromiumCdmHost,
148
0
             aHostAPI);
149
0
    if (!cdm) {
150
0
      GMP_LOG("ChromiumCDMAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p "
151
0
              "FAILED to create cdm version %d",
152
0
              aAPIName,
153
0
              aHostAPI,
154
0
              aPluginAPI,
155
0
              aDecryptorId,
156
0
              this,
157
0
              version);
158
0
      return GMPGenericErr;
159
0
    }
160
0
    GMP_LOG("cdm: 0x%p, version: %d", cdm, version);
161
0
    *aPluginAPI = cdm;
162
0
  }
163
0
  return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
164
0
}
165
166
void
167
ChromiumCDMAdapter::GMPShutdown()
168
0
{
169
0
  GMP_LOG("ChromiumCDMAdapter::GMPShutdown()");
170
0
171
0
  decltype(::DeinitializeCdmModule)* deinit;
172
0
  deinit = (decltype(deinit))(PR_FindFunctionSymbol(mLib, "DeinitializeCdmModule"));
173
0
  if (deinit) {
174
0
    GMP_LOG("DeinitializeCdmModule()");
175
0
    deinit();
176
0
  }
177
0
}
178
179
/* static */
180
bool
181
ChromiumCDMAdapter::Supports(int32_t aModuleVersion,
182
                             int32_t aInterfaceVersion,
183
                             int32_t aHostVersion)
184
0
{
185
0
  return aModuleVersion == CDM_MODULE_VERSION &&
186
0
         (aInterfaceVersion == cdm::ContentDecryptionModule_9::kVersion ||
187
0
          aInterfaceVersion == cdm::ContentDecryptionModule_10::kVersion) &&
188
0
         (aHostVersion == cdm::Host_9::kVersion ||
189
0
          aHostVersion == cdm::Host_10::kVersion);
190
0
}
191
192
#ifdef XP_WIN
193
194
static WindowsDllInterceptor sKernel32Intercept;
195
196
typedef DWORD(WINAPI* QueryDosDeviceWFnPtr)(_In_opt_ LPCWSTR lpDeviceName,
197
                                            _Out_ LPWSTR lpTargetPath,
198
                                            _In_ DWORD ucchMax);
199
200
static WindowsDllInterceptor::FuncHookType<QueryDosDeviceWFnPtr>
201
  sOriginalQueryDosDeviceWFnPtr;
202
203
static std::unordered_map<std::wstring, std::wstring>* sDeviceNames = nullptr;
204
205
DWORD WINAPI
206
QueryDosDeviceWHook(LPCWSTR lpDeviceName, LPWSTR lpTargetPath, DWORD ucchMax)
207
{
208
  if (!sDeviceNames) {
209
    return 0;
210
  }
211
  std::wstring name = std::wstring(lpDeviceName);
212
  auto iter = sDeviceNames->find(name);
213
  if (iter == sDeviceNames->end()) {
214
    return 0;
215
  }
216
  const std::wstring& device = iter->second;
217
  if (device.size() + 1 > ucchMax) {
218
    return 0;
219
  }
220
  PodCopy(lpTargetPath, device.c_str(), device.size());
221
  lpTargetPath[device.size()] = 0;
222
  GMP_LOG("QueryDosDeviceWHook %S -> %S", lpDeviceName, lpTargetPath);
223
  return name.size();
224
}
225
226
static std::vector<std::wstring>
227
GetDosDeviceNames()
228
{
229
  std::vector<std::wstring> v;
230
  std::vector<wchar_t> buf;
231
  buf.resize(1024);
232
  DWORD rv = GetLogicalDriveStringsW(buf.size(), buf.data());
233
  if (rv == 0 || rv > buf.size()) {
234
    return v;
235
  }
236
237
  // buf will be a list of null terminated strings, with the last string
238
  // being 0 length.
239
  const wchar_t* p = buf.data();
240
  const wchar_t* end = &buf.back();
241
  size_t l;
242
  while (p < end && (l = wcsnlen_s(p, end - p)) > 0) {
243
    // The string is of the form "C:\". We need to strip off the trailing
244
    // backslash.
245
    std::wstring drive = std::wstring(p, p + l);
246
    if (drive.back() == '\\') {
247
      drive.erase(drive.end() - 1);
248
    }
249
    v.push_back(move(drive));
250
    p += l + 1;
251
  }
252
  return v;
253
}
254
255
static std::wstring
256
GetDeviceMapping(const std::wstring& aDosDeviceName)
257
{
258
  wchar_t buf[MAX_PATH] = { 0 };
259
  DWORD rv = QueryDosDeviceW(aDosDeviceName.c_str(), buf, MAX_PATH);
260
  if (rv == 0) {
261
    return std::wstring(L"");
262
  }
263
  return std::wstring(buf, buf + rv);
264
}
265
266
static void
267
InitializeHooks()
268
{
269
  static bool initialized = false;
270
  if (initialized) {
271
    return;
272
  }
273
  initialized = true;
274
  sDeviceNames = new std::unordered_map<std::wstring, std::wstring>();
275
  for (const std::wstring& name : GetDosDeviceNames()) {
276
    sDeviceNames->emplace(name, GetDeviceMapping(name));
277
  }
278
279
  sKernel32Intercept.Init("kernelbase.dll");
280
  sOriginalQueryDosDeviceWFnPtr.Set(sKernel32Intercept, "QueryDosDeviceW",
281
                                    &QueryDosDeviceWHook);
282
}
283
#endif
284
285
HostFile::HostFile(HostFile&& aOther)
286
  : mPath(aOther.mPath)
287
  , mFile(aOther.TakePlatformFile())
288
0
{
289
0
}
290
291
HostFile::~HostFile()
292
0
{
293
0
  if (mFile != cdm::kInvalidPlatformFile) {
294
#ifdef XP_WIN
295
    CloseHandle(mFile);
296
#else
297
    close(mFile);
298
0
#endif
299
0
    mFile = cdm::kInvalidPlatformFile;
300
0
  }
301
0
}
302
303
#ifdef XP_WIN
304
HostFile::HostFile(const nsCString& aPath)
305
  : mPath(NS_ConvertUTF8toUTF16(aPath))
306
{
307
  HANDLE handle = CreateFileW(mPath.get(),
308
                              GENERIC_READ,
309
                              FILE_SHARE_READ | FILE_SHARE_WRITE,
310
                              NULL,
311
                              OPEN_EXISTING,
312
                              0,
313
                              NULL);
314
  mFile = (handle == INVALID_HANDLE_VALUE) ? cdm::kInvalidPlatformFile : handle;
315
}
316
#endif
317
318
#ifndef XP_WIN
319
HostFile::HostFile(const nsCString& aPath)
320
  : mPath(aPath)
321
0
{
322
0
  // Note: open() returns -1 on failure; i.e. kInvalidPlatformFile.
323
0
  mFile = open(aPath.get(), O_RDONLY);
324
0
}
325
#endif
326
327
cdm::PlatformFile
328
HostFile::TakePlatformFile()
329
0
{
330
0
  cdm::PlatformFile f = mFile;
331
0
  mFile = cdm::kInvalidPlatformFile;
332
0
  return f;
333
0
}
334
335
void
336
ChromiumCDMAdapter::PopulateHostFiles(nsTArray<Pair<nsCString, nsCString>>&& aHostPathPairs)
337
0
{
338
0
  for (const auto& pair : aHostPathPairs) {
339
0
    mHostFiles.AppendElement(
340
0
      HostFileData(mozilla::HostFile(pair.first()),
341
0
                   mozilla::HostFile(pair.second())));
342
0
  }
343
0
}
344
345
} // namespace mozilla