Bug 1618529 - Store clear crypto subsample info in uint32 rather than uint16. r=jolin

This avoids us risking an overflow when we convert encrypted media with
subsamples to AnnexB (since that conversion can grow the clear sizes of the
sample). See the test in the preceding patch for an example of how and why this
happens.

Differential Revision: https://phabricator.services.mozilla.com/D92300
This commit is contained in:
Bryce Seager van Dyk 2020-10-05 18:17:06 +00:00
Родитель 9dbc3391a2
Коммит 6e2d1d8482
5 изменённых файлов: 39 добавлений и 20 удалений

Просмотреть файл

@ -550,7 +550,16 @@ class CryptoTrack {
class CryptoSample : public CryptoTrack {
public:
CopyableTArray<uint16_t> mPlainSizes;
// The num clear bytes in each subsample. The nth element in the array is the
// number of clear bytes at the start of the nth subsample.
// Clear sizes are stored as uint16_t in containers per ISO/IEC
// 23001-7, but we store them as uint32_t for 2 reasons
// - The Widevine CDM accepts clear sizes as uint32_t.
// - When converting samples to Annex B we modify the clear sizes and
// clear sizes near UINT16_MAX can overflow if stored in a uint16_t.
CopyableTArray<uint32_t> mPlainSizes;
// The num encrypted bytes in each subsample. The nth element in the array is
// the number of encrypted bytes at the start of the nth subsample.
CopyableTArray<uint32_t> mEncryptedSizes;
CopyableTArray<uint8_t> mIV;
CopyableTArray<CopyableTArray<uint8_t>> mInitDatas;

Просмотреть файл

@ -54,7 +54,7 @@ struct CDMInputBuffer {
uint8_t[] mIV;
int64_t mTimestamp;
int64_t mDuration;
uint16_t[] mClearBytes;
uint32_t[] mClearBytes;
uint32_t[] mCipherBytes;
uint8_t mCryptByteBlock;
uint8_t mSkipByteBlock;

Просмотреть файл

@ -446,11 +446,10 @@ CryptoSample HLSTrackDemuxer::ExtractCryptoSample(
msg = "Error when extracting clear data.";
break;
}
// Data in mPlainSizes is uint16_t, NumBytesOfClearData is int32_t
// , so need a for loop to copy
for (const auto& b : clearData->GetElements()) {
crypto.mPlainSizes.AppendElement(b);
}
auto&& clearArr = clearData->GetElements();
// Data in mPlainSizes is uint32_t, NumBytesOfClearData is int32_t
crypto.mPlainSizes.AppendElements(reinterpret_cast<uint32_t*>(&clearArr[0]),
clearArr.Length());
mozilla::jni::IntArray::LocalRef encryptedData;
if (NS_FAILED(aCryptoInfo->NumBytesOfEncryptedData(&encryptedData))) {

Просмотреть файл

@ -72,11 +72,16 @@ Result<Ok, nsresult> AnnexB::ConvertSampleToAnnexB(
// will fail.
if (aSample->mCrypto.IsEncrypted()) {
if (aSample->mCrypto.mPlainSizes.Length() == 0) {
samplewriter->mCrypto.mPlainSizes.AppendElement(annexB->Length());
CheckedUint32 plainSize{annexB->Length()};
CheckedUint32 encryptedSize{samplewriter->Size()};
encryptedSize -= annexB->Length();
samplewriter->mCrypto.mPlainSizes.AppendElement(plainSize.value());
samplewriter->mCrypto.mEncryptedSizes.AppendElement(
samplewriter->Size() - annexB->Length());
encryptedSize.value());
} else {
samplewriter->mCrypto.mPlainSizes[0] += annexB->Length();
CheckedUint32 newSize{samplewriter->mCrypto.mPlainSizes[0]};
newSize += annexB->Length();
samplewriter->mCrypto.mPlainSizes[0] = newSize.value();
}
}
}

Просмотреть файл

@ -677,21 +677,26 @@ static java::sdk::CryptoInfo::LocalRef GetCryptoInfoFromSample(
cryptoObj.mPlainSizes.Length(), cryptoObj.mEncryptedSizes.Length());
uint32_t totalSubSamplesSize = 0;
for (auto& size : cryptoObj.mPlainSizes) {
totalSubSamplesSize += size;
}
for (auto& size : cryptoObj.mEncryptedSizes) {
totalSubSamplesSize += size;
}
// mPlainSizes is uint16_t, need to transform to uint32_t first.
nsTArray<uint32_t> plainSizes;
for (auto& size : cryptoObj.mPlainSizes) {
totalSubSamplesSize += size;
plainSizes.AppendElement(size);
}
// Deep copy the plain sizes so we can modify them.
nsTArray<uint32_t> plainSizes = cryptoObj.mPlainSizes.Clone();
uint32_t codecSpecificDataSize = aSample->Size() - totalSubSamplesSize;
// Size of codec specific data("CSD") for Android java::sdk::MediaCodec usage
// should be included in the 1st plain size.
plainSizes[0] += codecSpecificDataSize;
// should be included in the 1st plain size if it exists.
if (!plainSizes.IsEmpty()) {
// This shouldn't overflow as the the plain size should be UINT16_MAX at
// most, and the CSD should never be that large. Checked int acts like a
// diagnostic assert here to help catch if we ever have insane inputs.
CheckedUint32 newLeadingPlainSize{plainSizes[0]};
newLeadingPlainSize += codecSpecificDataSize;
plainSizes[0] = newLeadingPlainSize.value();
}
static const int kExpectedIVLength = 16;
auto tempIV(cryptoObj.mIV);
@ -703,11 +708,12 @@ static java::sdk::CryptoInfo::LocalRef GetCryptoInfoFromSample(
}
auto numBytesOfPlainData = mozilla::jni::IntArray::New(
reinterpret_cast<int32_t*>(&plainSizes[0]), plainSizes.Length());
reinterpret_cast<const int32_t*>(&plainSizes[0]), plainSizes.Length());
auto numBytesOfEncryptedData = mozilla::jni::IntArray::New(
reinterpret_cast<const int32_t*>(&cryptoObj.mEncryptedSizes[0]),
cryptoObj.mEncryptedSizes.Length());
auto iv = mozilla::jni::ByteArray::New(reinterpret_cast<int8_t*>(&tempIV[0]),
tempIV.Length());
auto keyId = mozilla::jni::ByteArray::New(