mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 17:03:58 +02:00
datalength sanity checking for AvatarData packet
This commit is contained in:
parent
6440dd1b5f
commit
22aa9b075d
5 changed files with 209 additions and 92 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -71,6 +71,8 @@ public:
|
|||
void jump() { _shouldJump = true; };
|
||||
|
||||
bool isMyAvatar() { return true; }
|
||||
|
||||
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
|
||||
|
||||
static void sendKillAvatar();
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
@ -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<const unsigned char*>(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;
|
||||
}
|
||||
|
|
|
@ -33,10 +33,10 @@ typedef unsigned long long quint64;
|
|||
#include <QtCore/QObject>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QVariantMap>
|
||||
#include <QRect>
|
||||
#include <QMap>
|
||||
|
||||
#include <CollisionInfo.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
@ -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&);
|
||||
|
|
Loading…
Reference in a new issue