PRBool nsSkeletonState::DecodeHeader(ogg_packet* aPacket) { if (IsSkeletonBOS(aPacket)) { PRUint16 verMajor = LEUint16(aPacket->packet + SKELETON_VERSION_MAJOR_OFFSET); PRUint16 verMinor = LEUint16(aPacket->packet + SKELETON_VERSION_MINOR_OFFSET); // Read the presentation time. We read this before the version check as the // presentation time exists in all versions. PRInt64 n = LEInt64(aPacket->packet + SKELETON_PRESENTATION_TIME_NUMERATOR_OFFSET); PRInt64 d = LEInt64(aPacket->packet + SKELETON_PRESENTATION_TIME_DENOMINATOR_OFFSET); mPresentationTime = d == 0 ? 0 : (static_cast<float>(n) / static_cast<float>(d)) * USECS_PER_S; mVersion = SKELETON_VERSION(verMajor, verMinor); if (mVersion < SKELETON_VERSION(4,0) || mVersion >= SKELETON_VERSION(5,0) || aPacket->bytes < SKELETON_4_0_MIN_HEADER_LEN) { // We can only care to parse Skeleton version 4.0+. mActive = PR_FALSE; return mDoneReadingHeaders = PR_TRUE; } // Extract the segment length. mLength = LEInt64(aPacket->packet + SKELETON_FILE_LENGTH_OFFSET); LOG(PR_LOG_DEBUG, ("Skeleton segment length: %lld", mLength)); // Initialize the serianlno-to-index map. PRBool init = mIndex.Init(); if (!init) { NS_WARNING("Failed to initialize Ogg skeleton serialno-to-index map"); mActive = PR_FALSE; return mDoneReadingHeaders = PR_TRUE; } mActive = PR_TRUE; } else if (IsSkeletonIndex(aPacket) && mVersion >= SKELETON_VERSION(4,0)) { if (!DecodeIndex(aPacket)) { // Failed to parse index, or invalid/hostile index. DecodeIndex() will // have deactivated the track. return mDoneReadingHeaders = PR_TRUE; } } else if (aPacket->e_o_s) { mDoneReadingHeaders = PR_TRUE; } return mDoneReadingHeaders; }
PRBool nsSkeletonState::DecodeHeader(ogg_packet* aPacket) { if (IsSkeletonBOS(aPacket)) { PRUint16 verMajor = LEUint16(aPacket->packet + SKELETON_VERSION_MAJOR_OFFSET); PRUint16 verMinor = LEUint16(aPacket->packet + SKELETON_VERSION_MINOR_OFFSET); mVersion = SKELETON_VERSION(verMajor, verMinor); if (mVersion < SKELETON_VERSION(4,0) || mVersion >= SKELETON_VERSION(5,0) || aPacket->bytes < SKELETON_4_0_MIN_HEADER_LEN) { // We can only care to parse Skeleton version 4.0+. mActive = PR_FALSE; return mDoneReadingHeaders = PR_TRUE; } // Extract the segment length. mLength = LEInt64(aPacket->packet + SKELETON_FILE_LENGTH_OFFSET); LOG(PR_LOG_DEBUG, ("Skeleton segment length: %lld", mLength)); // Initialize the serianlno-to-index map. PRBool init = mIndex.Init(); if (!init) { NS_WARNING("Failed to initialize Ogg skeleton serialno-to-index map"); mActive = PR_FALSE; return mDoneReadingHeaders = PR_TRUE; } mActive = PR_TRUE; } else if (IsSkeletonIndex(aPacket) && mVersion >= SKELETON_VERSION(4,0)) { if (!DecodeIndex(aPacket)) { // Failed to parse index, or invalid/hostile index. DecodeIndex() will // have deactivated the track. return mDoneReadingHeaders = PR_TRUE; } } else if (aPacket->e_o_s) { mDoneReadingHeaders = PR_TRUE; } return mDoneReadingHeaders; }
PRBool nsSkeletonState::DecodeIndex(ogg_packet* aPacket) { NS_ASSERTION(aPacket->bytes >= SKELETON_4_0_MIN_INDEX_LEN, "Index must be at least minimum size"); if (!mActive) { return PR_FALSE; } PRUint32 serialno = LEUint32(aPacket->packet + INDEX_SERIALNO_OFFSET); PRInt64 numKeyPoints = LEInt64(aPacket->packet + INDEX_NUM_KEYPOINTS_OFFSET); PRInt64 n = 0; PRInt64 endTime = 0, startTime = 0; const unsigned char* p = aPacket->packet; PRInt64 timeDenom = LEInt64(aPacket->packet + INDEX_TIME_DENOM_OFFSET); if (timeDenom == 0) { LOG(PR_LOG_DEBUG, ("Ogg Skeleton Index packet for stream %u has 0 " "timestamp denominator.", serialno)); return (mActive = PR_FALSE); } // Extract the start time. n = LEInt64(p + INDEX_FIRST_NUMER_OFFSET); PRInt64 t; if (!MulOverflow(n, 1000, t)) { return (mActive = PR_FALSE); } else { startTime = t / timeDenom; } // Extract the end time. n = LEInt64(p + INDEX_LAST_NUMER_OFFSET); if (!MulOverflow(n, 1000, t)) { return (mActive = PR_FALSE); } else { endTime = t / timeDenom; } // Check the numKeyPoints value read, ensure we're not going to run out of // memory while trying to decode the index packet. PRInt64 minPacketSize; if (!MulOverflow(numKeyPoints, MIN_KEY_POINT_SIZE, minPacketSize) || !AddOverflow(INDEX_KEYPOINT_OFFSET, minPacketSize, minPacketSize)) { return (mActive = PR_FALSE); } PRInt64 sizeofIndex = aPacket->bytes - INDEX_KEYPOINT_OFFSET; PRInt64 maxNumKeyPoints = sizeofIndex / MIN_KEY_POINT_SIZE; if (aPacket->bytes < minPacketSize || numKeyPoints > maxNumKeyPoints || numKeyPoints < 0) { // Packet size is less than the theoretical minimum size, or the packet is // claiming to store more keypoints than it's capable of storing. This means // that the numKeyPoints field is too large or small for the packet to // possibly contain as many packets as it claims to, so the numKeyPoints // field is possibly malicious. Don't try decoding this index, we may run // out of memory. LOG(PR_LOG_DEBUG, ("Possibly malicious number of key points reported " "(%lld) in index packet for stream %u.", numKeyPoints, serialno)); return (mActive = PR_FALSE); } nsAutoPtr<nsKeyFrameIndex> keyPoints(new nsKeyFrameIndex(startTime, endTime)); p = aPacket->packet + INDEX_KEYPOINT_OFFSET; const unsigned char* limit = aPacket->packet + aPacket->bytes; PRInt64 numKeyPointsRead = 0; PRInt64 offset = 0; PRInt64 time = 0; while (p < limit && numKeyPointsRead < numKeyPoints) { PRInt64 delta = 0; p = ReadVariableLengthInt(p, limit, delta); if (p == limit || !AddOverflow(offset, delta, offset) || offset > mLength || offset < 0) { return (mActive = PR_FALSE); } p = ReadVariableLengthInt(p, limit, delta); if (!AddOverflow(time, delta, time) || time > endTime || time < startTime) { return (mActive = PR_FALSE); } PRInt64 timeMs = 0; if (!MulOverflow(time, 1000, timeMs)) return mActive = PR_FALSE; timeMs /= timeDenom; keyPoints->Add(offset, timeMs); numKeyPointsRead++; } PRInt32 keyPointsRead = keyPoints->Length(); if (keyPointsRead > 0) { mIndex.Put(serialno, keyPoints.forget()); } LOG(PR_LOG_DEBUG, ("Loaded %d keypoints for Skeleton on stream %u", keyPointsRead, serialno)); return PR_TRUE; }