lib/nbt: finish implementation of validateRawNBTData() and fix a critical macro-induced bug

I did a `#define return` and then tried to `if () return;` everywhere...
Soda
BodgeMaster 2022-08-15 08:50:07 +02:00
parent a862590370
commit 9190cad80d
2 changed files with 140 additions and 11 deletions

View File

@ -569,6 +569,116 @@ namespace NBT {
}
}
bool validateRawList(uint8_t data[], uint64_t dataSize, uint64_t initialPosition, uint64_t* processedDataSize) {
ErrorOr<int32_t> elementCount = helper::containedDataLength(data, dataSize, initialPosition);
if (elementCount.isError) {
return false;
}
// there is no way this is an error bc it gets checked while trying
// to get the element count
int16_t nameSize = helper::readInt16(data, dataSize, initialPosition+1).value;
// type byte + two name size bytes = 3
uint8_t contentType = data[initialPosition + nameSize + 3];
// type byte + two name size bytes + contained type byte + 4 length bytes = 8
*processedDataSize = 8;
switch (contentType) {
case TagType::END:
// everything except content has been touched at this point
// and a list of end tags has no content
return true;
case TagType::INT8: {
*processedDataSize += (uint64_t) elementCount.value;
return initialPosition + *processedDataSize < dataSize;
}
case TagType::INT16: {
*processedDataSize += (uint64_t) elementCount.value * 2;
return initialPosition + *processedDataSize < dataSize;
}
case TagType::INT32:
case TagType::FLOAT: {
*processedDataSize += (uint64_t) elementCount.value * 4;
return initialPosition + *processedDataSize < dataSize;
}
case TagType::INT64:
case TagType::DOUBLE: {
*processedDataSize += (uint64_t) elementCount.value * 8;
return initialPosition + *processedDataSize < dataSize;
}
case TagType::INT8_ARRAY: {
for (int32_t i=0; i<elementCount.value; i++) {
ErrorOr<std::vector<int8_t>> nextArray = helper::readInt8Array(data, dataSize, initialPosition+*processedDataSize);
if (nextArray.isError) {
return false;
}
*processedDataSize += (uint64_t) nextArray.value.size();
}
return true;
}
case TagType::STRING: {
for (int32_t i=0; i<elementCount.value; i++) {
ErrorOr<tiny_utf8::string> nextString = helper::readString(data, dataSize, initialPosition+*processedDataSize);
if (nextString.isError) {
return false;
}
// this cannot be an error because it just got checked
int16_t nextStringSize = helper::readInt16(data, dataSize, initialPosition+*processedDataSize).value;
*processedDataSize += (uint64_t) nextStringSize + 2;
}
return true;
}
case TagType::LIST: {
uint64_t* containedDataSize = new uint64_t;
for (int32_t i=0; i<elementCount.value; i++) {
*containedDataSize = 0;
if (validateRawList(data, dataSize, initialPosition+*processedDataSize, containedDataSize)) {
*processedDataSize += *containedDataSize;
} else {
delete containedDataSize;
return false;
}
}
delete containedDataSize;
return true;
}
case TagType::COMPOUND: {
uint64_t* containedDataSize = new uint64_t;
for (int32_t i=0; i<elementCount.value; i++) {
*containedDataSize = 0;
if (validateRawNBTData(data, dataSize, initialPosition, containedDataSize)) {
*processedDataSize += *containedDataSize;
} else {
delete containedDataSize;
return false;
}
}
delete containedDataSize;
return true;
}
case TagType::INT32_ARRAY: {
for (int32_t i=0; i<elementCount.value; i++) {
ErrorOr<std::vector<int32_t>> nextArray = helper::readInt32Array(data, dataSize, initialPosition+*processedDataSize);
if (nextArray.isError) {
return false;
}
*processedDataSize += (uint64_t) nextArray.value.size() * 4;
}
return true;
}
case TagType::INT64_ARRAY: {
for (int32_t i=0; i<elementCount.value; i++) {
ErrorOr<std::vector<int64_t>> nextArray = helper::readInt64Array(data, dataSize, initialPosition+*processedDataSize);
if (nextArray.isError) {
return false;
}
*processedDataSize += (uint64_t) nextArray.value.size() * 8;
}
return true;
}
default:
return false;
}
}
bool validateRawNBTData(uint8_t data[], uint64_t dataSize, uint64_t initialPosition, uint64_t* processedDataSize){
if (initialPosition >= dataSize) {
// Yes, this *could* return an instance of ErrorOr with
@ -594,21 +704,27 @@ namespace NBT {
//
// - BodgeMaster
}
uint64_t currentPosition = initialPosition;
#define return if (processedDataSize!=nullptr) *processedDataSize = currentPosition-initialPosition; return
while (currentPosition<dataSize) {
ErrorOr<uint64_t> nextTagSize = helper::totalTagSize(data, dataSize, currentPosition);
if (nextTagSize.isError) {
if (nextTagSize.errorCode == ErrorCodes::NOT_YET_KNOWN) {
// attempt parsing the name
ErrorOr<tiny_utf8::string> tagName = helper::readString(data, dataSize, currentPosition+1);
if (tagName.isError) {
return false;
}
uint64_t* processedTagSize = new uint64_t;
*processedTagSize = 0;
// attempt parsing the name
ErrorOr<tiny_utf8::string> tagName = helper::readString(data, dataSize, currentPosition+1);
if (tagName.isError) return false;
if (data[currentPosition]==TagType::LIST) {
//TODO: handle list
if (!validateRawList(data, dataSize, currentPosition, processedTagSize)) {
delete processedTagSize;
return false;
}
}
if (data[currentPosition]==TagType::COMPOUND) {
// seek to the start of the compound's contents
@ -617,19 +733,28 @@ namespace NBT {
// checked while trying to parse the string above
int16_t nameSize = helper::readInt16(data, dataSize, currentPosition+1).value;
if (!validateRawNBTData(data, dataSize, currentPosition + (uint64_t) nameSize + 1, processedTagSize)) return false;
if (!validateRawNBTData(data, dataSize, currentPosition + (uint64_t) nameSize + 1, processedTagSize)) {
delete processedTagSize;
return false;
}
*processedTagSize += (uint64_t) nameSize + 1;
}
currentPosition += *processedTagSize;
delete processedTagSize;
continue;
}
return false;
}
if (currentPosition + nextTagSize.value > dataSize) return false;
if (currentPosition + nextTagSize.value > dataSize) {
return false;
}
// recursion abort condition
if (data[currentPosition]==TagType::END) return true;
if (data[currentPosition]==TagType::END) {
return true;
}
// nameSize cannot be an error here bc it got checked in
// nextTagSize() already
@ -643,7 +768,9 @@ namespace NBT {
// if (currentPosition + nextTagSize.value > dataSize) return false;
// It might, however, turn out to be a useful check in the future.
ErrorOr<tiny_utf8::string> name = helper::readString(data, dataSize, currentPosition+1);
if (name.isError) return false;
if (name.isError) {
return false;
}
switch (data[0]) {
case TagType::INT8:
@ -666,7 +793,9 @@ namespace NBT {
//
// type byte + two name size bytes = 3
ErrorOr<tiny_utf8::string> content = helper::readString(data, dataSize, currentPosition+nameSize+3);
if (content.isError) return false;
if (content.isError) {
return false;
}
break;
}
case TagType::INT32_ARRAY:

View File

@ -113,5 +113,5 @@ namespace NBT {
bool validate(uint8_t data[]);
};
bool validateRawNBTData(uint8_t data[], int length, uint64_t initialPosition, uint64_t* processedDataSize=nullptr);
bool validateRawNBTData(uint8_t data[], uint64_t dataSize, uint64_t initialPosition, uint64_t* processedDataSize=nullptr);
}