From 22aa9b075dbad703ccbb0093b2b061f307a37684 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Mar 2014 16:52:40 -0700 Subject: [PATCH] datalength sanity checking for AvatarData packet --- interface/src/avatar/Avatar.h | 2 +- interface/src/avatar/MyAvatar.cpp | 8 + interface/src/avatar/MyAvatar.h | 2 + libraries/avatars/src/AvatarData.cpp | 285 ++++++++++++++++++--------- libraries/avatars/src/AvatarData.h | 4 +- 5 files changed, 209 insertions(+), 92 deletions(-) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 30073c54d4..638bff6e32 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -133,7 +133,7 @@ public: void setShowDisplayName(bool showDisplayName); - int parseDataAtOffset(const QByteArray& packet, int offset); + virtual int parseDataAtOffset(const QByteArray& packet, int offset); static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fb0d704c6a..e21c179b5b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -551,6 +551,14 @@ void MyAvatar::loadData(QSettings* settings) { settings->endGroup(); } +int MyAvatar::parseDataAtOffset(const QByteArray& packet, int offset) { + qDebug() << "Error: ignoring update packet for MyAvatar" + << " packetLength = " << packet.size() + << " offset = " << offset; + // this packet is just bad, so we pretend that we unpacked it ALL + return packet.size() - offset; +} + void MyAvatar::sendKillAvatar() { QByteArray killPacket = byteArrayWithPopulatedHeader(PacketTypeKillAvatar); NodeList::getInstance()->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3544fb1a61..2363ee26e1 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -71,6 +71,8 @@ public: void jump() { _shouldJump = true; }; bool isMyAvatar() { return true; } + + virtual int parseDataAtOffset(const QByteArray& packet, int offset); static void sendKillAvatar(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4e57e311eb..35e9938dc7 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,8 @@ #include "AvatarData.h" +quint64 DEFAULT_FILTERED_LOG_EXPIRY = 20 * USECS_PER_SECOND; + using namespace std; QNetworkAccessManager* AvatarData::networkAccessManager = NULL; @@ -42,7 +45,8 @@ AvatarData::AvatarData() : _displayNameBoundingRect(), _displayNameTargetAlpha(0.0f), _displayNameAlpha(0.0f), - _billboard() + _billboard(), + _debugLogExpiry(0) { } @@ -176,7 +180,6 @@ QByteArray AvatarData::toByteArray() { // read data in packet starting at byte offset and return number of bytes parsed int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) { - // lazily allocate memory for HeadData in case we're not an Avatar instance if (!_headData) { _headData = new HeadData(this); @@ -189,102 +192,204 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) { const unsigned char* startPosition = reinterpret_cast(packet.data()) + offset; const unsigned char* sourceBuffer = startPosition; + + // 50 bytes of "plain old data" (POD) + // 1 byte for messageSize (0) + // 1 byte for pupilSize + // 1 byte for numJoints (0) + int minPossibleSize = 53; - // Body world position - memcpy(&_position, sourceBuffer, sizeof(float) * 3); - sourceBuffer += sizeof(float) * 3; - - // Body rotation (NOTE: This needs to become a quaternion to save two bytes) - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyYaw); - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyPitch); - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll); - - // Body scale - sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, _targetScale); - - // Head rotation (NOTE: This needs to become a quaternion to save two bytes) - float headYaw, headPitch, headRoll; - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw); - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headPitch); - sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headRoll); - _headData->setYaw(headYaw); - _headData->setPitch(headPitch); - _headData->setRoll(headRoll); - - // Head position relative to pelvis - memcpy(&_headData->_leanSideways, sourceBuffer, sizeof(_headData->_leanSideways)); - sourceBuffer += sizeof(float); - memcpy(&_headData->_leanForward, sourceBuffer, sizeof(_headData->_leanForward)); - sourceBuffer += sizeof(_headData->_leanForward); - - // Lookat Position - memcpy(&_headData->_lookAtPosition, sourceBuffer, sizeof(_headData->_lookAtPosition)); - sourceBuffer += sizeof(_headData->_lookAtPosition); - - // Instantaneous audio loudness (used to drive facial animation) - memcpy(&_headData->_audioLoudness, sourceBuffer, sizeof(float)); - sourceBuffer += sizeof(float); - - // the rest is a chat message - int chatMessageSize = *sourceBuffer++; - _chatMessage = string((char*)sourceBuffer, chatMessageSize); - sourceBuffer += chatMessageSize * sizeof(char); - - // voxel sending features... - unsigned char bitItems = 0; - bitItems = (unsigned char)*sourceBuffer++; - - // key state, stored as a semi-nibble in the bitItems - _keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT); - - // hand state, stored as a semi-nibble in the bitItems - _handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT); - - _headData->_isFaceshiftConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED); - - _isChatCirclingEnabled = oneAtBit(bitItems, IS_CHAT_CIRCLING_ENABLED); - - // If it is connected, pack up the data - if (_headData->_isFaceshiftConnected) { - memcpy(&_headData->_leftEyeBlink, sourceBuffer, sizeof(float)); - sourceBuffer += sizeof(float); - - memcpy(&_headData->_rightEyeBlink, sourceBuffer, sizeof(float)); - sourceBuffer += sizeof(float); - - memcpy(&_headData->_averageLoudness, sourceBuffer, sizeof(float)); - sourceBuffer += sizeof(float); - - memcpy(&_headData->_browAudioLift, sourceBuffer, sizeof(float)); - sourceBuffer += sizeof(float); - - _headData->_blendshapeCoefficients.resize(*sourceBuffer++); - memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer, - _headData->_blendshapeCoefficients.size() * sizeof(float)); - sourceBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float); + int maxAvailableSize = packet.size() - offset; + if (minPossibleSize > maxAvailableSize) { + // this packet is malformed so we pretend to read to the end + quint64 now = usecTimestampNow(); + if (now > _debugLogExpiry) { + qDebug() << "Malformed AvatarData packet at the start: minPossibleSize = " << minPossibleSize + << " but maxAvailableSize = " << maxAvailableSize; + _debugLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY; + } + return maxAvailableSize; } + + { // Body world position, rotation, and scale + // position + memcpy(&_position, sourceBuffer, sizeof(float) * 3); + sourceBuffer += sizeof(float) * 3; + + // rotation (NOTE: This needs to become a quaternion to save two bytes) + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyYaw); + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyPitch); + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll); + + // scale + sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, _targetScale); + } // 20 bytes - // pupil dilation - sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f); + { // Head rotation + //(NOTE: This needs to become a quaternion to save two bytes) + float headYaw, headPitch, headRoll; + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw); + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headPitch); + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headRoll); + _headData->setYaw(headYaw); + _headData->setPitch(headPitch); + _headData->setRoll(headRoll); + } // 6 bytes + + // Head lean (relative to pelvis) + { + memcpy(&_headData->_leanSideways, sourceBuffer, sizeof(_headData->_leanSideways)); + sourceBuffer += sizeof(float); + memcpy(&_headData->_leanForward, sourceBuffer, sizeof(_headData->_leanForward)); + sourceBuffer += sizeof(float); + } // 8 bytes + + { // Lookat Position + memcpy(&_headData->_lookAtPosition, sourceBuffer, sizeof(_headData->_lookAtPosition)); + sourceBuffer += sizeof(_headData->_lookAtPosition); + } // 12 bytes + + { // AudioLoudness + // Instantaneous audio loudness (used to drive facial animation) + memcpy(&_headData->_audioLoudness, sourceBuffer, sizeof(float)); + sourceBuffer += sizeof(float); + } // 4 bytes + + // chat + int chatMessageSize = *sourceBuffer++; + minPossibleSize += chatMessageSize; + if (minPossibleSize > maxAvailableSize) { + // this packet is malformed so we pretend to read to the end + quint64 now = usecTimestampNow(); + if (now > _debugLogExpiry) { + qDebug() << "Malformed AvatarData packet before ChatMessage: minPossibleSize = " << minPossibleSize + << " but maxAvailableSize = " << maxAvailableSize; + _debugLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY; + } + return maxAvailableSize; + } + { // chat payload + _chatMessage = string((char*)sourceBuffer, chatMessageSize); + sourceBuffer += chatMessageSize * sizeof(char); + } // 1 + chatMessageSize bytes + + { // bitFlags and face data + unsigned char bitItems = 0; + bitItems = (unsigned char)*sourceBuffer++; + + // key state, stored as a semi-nibble in the bitItems + _keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT); + + // hand state, stored as a semi-nibble in the bitItems + _handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT); + + _headData->_isFaceshiftConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED); + _isChatCirclingEnabled = oneAtBit(bitItems, IS_CHAT_CIRCLING_ENABLED); + + if (_headData->_isFaceshiftConnected) { + minPossibleSize += 4 * sizeof(float) + 1; // four floats + one byte for blendDataSize + if (minPossibleSize > maxAvailableSize) { + // this packet is malformed so we pretend to read to the end + quint64 now = usecTimestampNow(); + if (now > _debugLogExpiry) { + qDebug() << "Malformed AvatarData packet after BitItems: minPossibleSize = " << minPossibleSize + << " but maxAvailableSize = " << maxAvailableSize; + _debugLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY; + } + return maxAvailableSize; + } + // unpack face data + memcpy(&_headData->_leftEyeBlink, sourceBuffer, sizeof(float)); + sourceBuffer += sizeof(float); + + memcpy(&_headData->_rightEyeBlink, sourceBuffer, sizeof(float)); + sourceBuffer += sizeof(float); + + memcpy(&_headData->_averageLoudness, sourceBuffer, sizeof(float)); + sourceBuffer += sizeof(float); + + memcpy(&_headData->_browAudioLift, sourceBuffer, sizeof(float)); + sourceBuffer += sizeof(float); + + int numCoefficients = (int)(*sourceBuffer++); + int blendDataSize = numCoefficients * sizeof(float); + minPossibleSize += blendDataSize; + if (minPossibleSize > maxAvailableSize) { + // this packet is malformed so we pretend to read to the end + quint64 now = usecTimestampNow(); + if (now > _debugLogExpiry) { + qDebug() << "Malformed AvatarData packet after Blendshapes: minPossibleSize = " << minPossibleSize + << " but maxAvailableSize = " << maxAvailableSize; + _debugLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY; + } + return maxAvailableSize; + } + + _headData->_blendshapeCoefficients.resize(numCoefficients); + memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer, blendDataSize); + sourceBuffer += numCoefficients * sizeof(float); + + //bitItemsDataSize = 4 * sizeof(float) + 1 + blendDataSize; + } + } // 1 + bitItemsDataSize bytes + + { // pupil dilation + sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f); + } // 1 byte // joint data - int jointCount = *sourceBuffer++; - _jointData.resize(jointCount); - unsigned char validity = 0; - int validityBit = 0; - for (int i = 0; i < jointCount; i++) { - if (validityBit == 0) { - validity = *sourceBuffer++; - } - _jointData[i].valid = (bool)(validity & (1 << validityBit)); - validityBit = (validityBit + 1) % BITS_IN_BYTE; + int numJoints = *sourceBuffer++; + int bytesOfValidity = (int)ceil((float)numJoints / 8.f); + minPossibleSize += bytesOfValidity; + if (minPossibleSize > maxAvailableSize) { + // this packet is malformed so we pretend to read to the end + quint64 now = usecTimestampNow(); + if (now > _debugLogExpiry) { + qDebug() << "Malformed AvatarData packet after JointValidityBits: minPossibleSize = " << minPossibleSize + << " but maxAvailableSize = " << maxAvailableSize; + _debugLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY; + } + return maxAvailableSize; } - for (int i = 0; i < jointCount; i++) { - JointData& data = _jointData[i]; - if (data.valid) { - sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); + int numValidJoints = 0; + _jointData.resize(numJoints); + { // validity bits + unsigned char validity = 0; + int validityBit = 0; + for (int i = 0; i < numJoints; i++) { + if (validityBit == 0) { + validity = *sourceBuffer++; + } + bool valid = (bool)(validity & (1 << validityBit)); + if (valid) { + ++numValidJoints; + } + _jointData[i].valid = valid; + validityBit = (validityBit + 1) % BITS_IN_BYTE; } } + // 1 + bytesOfValidity bytes + + minPossibleSize += numValidJoints * 8; // 8 bytes per quaternion + if (minPossibleSize > maxAvailableSize) { + // this packet is malformed so we pretend to read to the end + quint64 now = usecTimestampNow(); + if (now > _debugLogExpiry) { + qDebug() << "Malformed AvatarData packet after JointData: minPossibleSize = " << minPossibleSize + << " but maxAvailableSize = " << maxAvailableSize; + _debugLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY; + } + return maxAvailableSize; + } + + { // joint data + for (int i = 0; i < numJoints; i++) { + JointData& data = _jointData[i]; + if (data.valid) { + sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); + } + } + } // numJoints * 8 bytes return sourceBuffer - startPosition; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c7a93daef5..4048b9bebe 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -33,10 +33,10 @@ typedef unsigned long long quint64; #include #include #include -#include #include #include #include +#include #include #include @@ -256,6 +256,8 @@ protected: static QNetworkAccessManager* networkAccessManager; + quint64 _debugLogExpiry; + private: // privatize the copy constructor and assignment operator so they cannot be called AvatarData(const AvatarData&);