mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1104970 - Store GMPStorage record names at the start of each record. r=jesup
This commit is contained in:
parent
7fdf6fc21a
commit
4c86e9edcd
@ -18,7 +18,6 @@
|
|||||||
#include "mozIGeckoMediaPluginService.h"
|
#include "mozIGeckoMediaPluginService.h"
|
||||||
#include "nsContentCID.h"
|
#include "nsContentCID.h"
|
||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "mozilla/Base64.h"
|
|
||||||
#include "nsISimpleEnumerator.h"
|
#include "nsISimpleEnumerator.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
@ -106,17 +105,9 @@ OpenStorageFile(const nsCString& aRecordName,
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAutoCString recordNameBase64;
|
nsAutoString recordNameHash;
|
||||||
rv = Base64Encode(aRecordName, recordNameBase64);
|
recordNameHash.AppendInt(HashString(aRecordName.get()));
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
f->Append(recordNameHash);
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base64 can encode to a '/' character, which will mess with file paths,
|
|
||||||
// so we need to replace that here with something that won't mess with paths.
|
|
||||||
recordNameBase64.ReplaceChar('/', '-');
|
|
||||||
|
|
||||||
f->AppendNative(recordNameBase64);
|
|
||||||
|
|
||||||
auto mode = PR_RDWR | PR_CREATE_FILE;
|
auto mode = PR_RDWR | PR_CREATE_FILE;
|
||||||
if (aMode == Truncate) {
|
if (aMode == Truncate) {
|
||||||
@ -162,24 +153,100 @@ public:
|
|||||||
return mFiles.Contains(aRecordName);
|
return mFiles.Contains(aRecordName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GMPErr ReadRecordMetadata(PRFileDesc* aFd,
|
||||||
|
int32_t& aOutFileLength,
|
||||||
|
int32_t& aOutRecordLength,
|
||||||
|
nsACString& aOutRecordName)
|
||||||
|
{
|
||||||
|
int32_t fileLength = PR_Seek(aFd, 0, PR_SEEK_END);
|
||||||
|
PR_Seek(aFd, 0, PR_SEEK_SET);
|
||||||
|
|
||||||
|
if (fileLength > GMP_MAX_RECORD_SIZE) {
|
||||||
|
// Refuse to read big records.
|
||||||
|
return GMPQuotaExceededErr;
|
||||||
|
}
|
||||||
|
aOutFileLength = fileLength;
|
||||||
|
aOutRecordLength = 0;
|
||||||
|
|
||||||
|
// At the start of the file the length of the record name is stored in a
|
||||||
|
// size_t (host byte order) followed by the record name at the start of
|
||||||
|
// the file. The record name is not null terminated. The remainder of the
|
||||||
|
// file is the record's data.
|
||||||
|
|
||||||
|
size_t recordNameLength = 0;
|
||||||
|
if (fileLength == 0 || sizeof(recordNameLength) >= (size_t)fileLength) {
|
||||||
|
// Record file is empty, or doesn't even have enough contents to
|
||||||
|
// store the record name length and/or record name. Report record
|
||||||
|
// as empty.
|
||||||
|
return GMPNoErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t bytesRead = PR_Read(aFd, &recordNameLength, sizeof(recordNameLength));
|
||||||
|
if (sizeof(recordNameLength) != bytesRead ||
|
||||||
|
recordNameLength > fileLength - sizeof(recordNameLength)) {
|
||||||
|
// Record file has invalid contents. Report record as empty.
|
||||||
|
return GMPNoErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCString recordName;
|
||||||
|
recordName.SetLength(recordNameLength);
|
||||||
|
bytesRead = PR_Read(aFd, recordName.BeginWriting(), recordNameLength);
|
||||||
|
if (bytesRead != (int32_t)recordNameLength) {
|
||||||
|
// Record file has invalid contents. Report record as empty.
|
||||||
|
return GMPGenericErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(fileLength > 0 && (size_t)fileLength >= sizeof(recordNameLength) + recordNameLength);
|
||||||
|
int32_t recordLength = fileLength - (sizeof(recordNameLength) + recordNameLength);
|
||||||
|
|
||||||
|
aOutRecordLength = recordLength;
|
||||||
|
aOutRecordName = recordName;
|
||||||
|
|
||||||
|
return GMPNoErr;
|
||||||
|
}
|
||||||
|
|
||||||
virtual GMPErr Read(const nsCString& aRecordName,
|
virtual GMPErr Read(const nsCString& aRecordName,
|
||||||
nsTArray<uint8_t>& aOutBytes) MOZ_OVERRIDE
|
nsTArray<uint8_t>& aOutBytes) MOZ_OVERRIDE
|
||||||
{
|
{
|
||||||
|
// Our error strategy is to report records with invalid contents as
|
||||||
|
// containing 0 bytes. Zero length records are considered "deleted" by
|
||||||
|
// the GMPStorage API.
|
||||||
|
aOutBytes.SetLength(0);
|
||||||
|
|
||||||
PRFileDesc* fd = mFiles.Get(aRecordName);
|
PRFileDesc* fd = mFiles.Get(aRecordName);
|
||||||
if (!fd) {
|
if (!fd) {
|
||||||
return GMPGenericErr;
|
return GMPGenericErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t len = PR_Seek(fd, 0, PR_SEEK_END);
|
int32_t fileLength = 0;
|
||||||
PR_Seek(fd, 0, PR_SEEK_SET);
|
int32_t recordLength = 0;
|
||||||
|
nsCString recordName;
|
||||||
if (len > GMP_MAX_RECORD_SIZE) {
|
GMPErr err = ReadRecordMetadata(fd,
|
||||||
// Refuse to read big records.
|
fileLength,
|
||||||
return GMPQuotaExceededErr;
|
recordLength,
|
||||||
|
recordName);
|
||||||
|
if (NS_WARN_IF(GMP_FAILED(err))) {
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
aOutBytes.SetLength(len);
|
|
||||||
auto bytesRead = PR_Read(fd, aOutBytes.Elements(), len);
|
if (recordLength == 0) {
|
||||||
return (bytesRead == len) ? GMPNoErr : GMPGenericErr;
|
// Record is empoty but not invalid, or it's invalid and we're going to
|
||||||
|
// just act like it's empty and let the client overwrite it.
|
||||||
|
return GMPNoErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aRecordName.Equals(recordName)) {
|
||||||
|
NS_WARNING("Hash collision in GMPStorage");
|
||||||
|
return GMPGenericErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After calling ReadRecordMetadata, we should be ready to read the
|
||||||
|
// record data.
|
||||||
|
MOZ_ASSERT(PR_Available(fd) == recordLength);
|
||||||
|
|
||||||
|
aOutBytes.SetLength(recordLength);
|
||||||
|
int32_t bytesRead = PR_Read(fd, aOutBytes.Elements(), recordLength);
|
||||||
|
return (bytesRead == recordLength) ? GMPNoErr : GMPGenericErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual GMPErr Write(const nsCString& aRecordName,
|
virtual GMPErr Write(const nsCString& aRecordName,
|
||||||
@ -199,7 +266,22 @@ public:
|
|||||||
}
|
}
|
||||||
mFiles.Put(aRecordName, fd);
|
mFiles.Put(aRecordName, fd);
|
||||||
|
|
||||||
int32_t bytesWritten = PR_Write(fd, aBytes.Elements(), aBytes.Length());
|
// Store the length of the record name followed by the record name
|
||||||
|
// at the start of the file.
|
||||||
|
int32_t bytesWritten = 0;
|
||||||
|
if (aBytes.Length() > 0) {
|
||||||
|
size_t recordNameLength = aRecordName.Length();
|
||||||
|
bytesWritten = PR_Write(fd, &recordNameLength, sizeof(recordNameLength));
|
||||||
|
if (NS_WARN_IF(bytesWritten != sizeof(recordNameLength))) {
|
||||||
|
return GMPGenericErr;
|
||||||
|
}
|
||||||
|
bytesWritten = PR_Write(fd, aRecordName.get(), recordNameLength);
|
||||||
|
if (NS_WARN_IF(bytesWritten != (int32_t)recordNameLength)) {
|
||||||
|
return GMPGenericErr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesWritten = PR_Write(fd, aBytes.Elements(), aBytes.Length());
|
||||||
return (bytesWritten == (int32_t)aBytes.Length()) ? GMPNoErr : GMPGenericErr;
|
return (bytesWritten == (int32_t)aBytes.Length()) ? GMPNoErr : GMPGenericErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,13 +317,31 @@ public:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The record's file name is the Base64 encode of the record name,
|
PRFileDesc* fd = nullptr;
|
||||||
// with '/' characters replaced with '-' characters. Base64 decode
|
if (NS_FAILED(dirEntry->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) {
|
||||||
// to extract the file name.
|
continue;
|
||||||
leafName.ReplaceChar('-', '/');
|
}
|
||||||
nsAutoCString recordName;
|
int32_t fileLength = 0;
|
||||||
rv = Base64Decode(leafName, recordName);
|
int32_t recordLength = 0;
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
nsCString recordName;
|
||||||
|
GMPErr err = ReadRecordMetadata(fd,
|
||||||
|
fileLength,
|
||||||
|
recordLength,
|
||||||
|
recordName);
|
||||||
|
PR_Close(fd);
|
||||||
|
if (NS_WARN_IF(GMP_FAILED(err))) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recordName.IsEmpty() || recordLength == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the file name is the hash of the record name stored in the
|
||||||
|
// record file. Otherwise it's not a valid record.
|
||||||
|
nsAutoCString recordNameHash;
|
||||||
|
recordNameHash.AppendInt(HashString(recordName.get()));
|
||||||
|
if (!recordNameHash.Equals(leafName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,11 +20,11 @@
|
|||||||
#include "gmp-errors.h"
|
#include "gmp-errors.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// Maximum size of a record, in bytes.
|
// Maximum size of a record, in bytes; 10 megabytes.
|
||||||
#define GMP_MAX_RECORD_SIZE (1024 * 1024 * 1024)
|
#define GMP_MAX_RECORD_SIZE (10 * 1024 * 1024)
|
||||||
|
|
||||||
// Maximum length of a record name.
|
// Maximum length of a record name in bytes.
|
||||||
#define GMP_MAX_RECORD_NAME_SIZE 200
|
#define GMP_MAX_RECORD_NAME_SIZE 2000
|
||||||
|
|
||||||
// Provides basic per-origin storage for CDMs. GMPRecord instances can be
|
// Provides basic per-origin storage for CDMs. GMPRecord instances can be
|
||||||
// retrieved by calling GMPPlatformAPI->openstorage. Multiple GMPRecords
|
// retrieved by calling GMPPlatformAPI->openstorage. Multiple GMPRecords
|
||||||
|
@ -641,6 +641,48 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
|||||||
TestGetRecordNames(false);
|
TestGetRecordNames(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestLongRecordNames() {
|
||||||
|
NS_NAMED_LITERAL_CSTRING(longRecordName,
|
||||||
|
"A_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
|
||||||
|
"long_record_name");
|
||||||
|
|
||||||
|
NS_NAMED_LITERAL_CSTRING(data, "Just_some_arbitrary_data.");
|
||||||
|
|
||||||
|
MOZ_ASSERT(longRecordName.Length() < GMP_MAX_RECORD_NAME_SIZE);
|
||||||
|
MOZ_ASSERT(longRecordName.Length() > 260); // Windows MAX_PATH
|
||||||
|
|
||||||
|
CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
|
||||||
|
NS_LITERAL_STRING("baz.com"),
|
||||||
|
false);
|
||||||
|
|
||||||
|
nsCString response("stored ");
|
||||||
|
response.Append(longRecordName);
|
||||||
|
response.AppendLiteral(" ");
|
||||||
|
response.Append(data);
|
||||||
|
Expect(response, NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
|
||||||
|
|
||||||
|
nsCString update("store ");
|
||||||
|
update.Append(longRecordName);
|
||||||
|
update.AppendLiteral(" ");
|
||||||
|
update.Append(data);
|
||||||
|
Update(update);
|
||||||
|
}
|
||||||
|
|
||||||
void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
|
void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
|
||||||
mExpected.AppendElement(ExpectedMessage(aMessage, aContinuation));
|
mExpected.AppendElement(ExpectedMessage(aMessage, aContinuation));
|
||||||
}
|
}
|
||||||
@ -822,3 +864,8 @@ TEST(GeckoMediaPlugins, GMPStorageGetRecordNamesPersistentStorage) {
|
|||||||
nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
|
nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
|
||||||
runner->DoTest(&GMPStorageTest::GetRecordNamesPersistentStorage);
|
runner->DoTest(&GMPStorageTest::GetRecordNamesPersistentStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(GeckoMediaPlugins, GMPStorageLongRecordNames) {
|
||||||
|
nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
|
||||||
|
runner->DoTest(&GMPStorageTest::TestLongRecordNames);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user