From 92a60bf4d5b0fe4705d9aa83e318f02d5ed2c0a0 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Tue, 2 Sep 2014 22:40:24 +0200 Subject: Added strict error reporting to chunk loading. This should help with #1307. --- src/WorldStorage/WSSAnvil.cpp | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'src/WorldStorage/WSSAnvil.cpp') diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index e79cc291d..0c31d8b02 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -69,6 +69,18 @@ Since only the header is actually in the memory, this number can be high, but st /// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities #define CHUNK_INFLATE_MAX 256 KiB +#define LOAD_FAILED(CHX, CHZ) \ + { \ + const int RegionX = FAST_FLOOR_DIV(CHX, 32); \ + const int RegionZ = FAST_FLOOR_DIV(CHZ, 32); \ + LOGERROR("%s (%d): Loading chunk [%d, %d] from file r.%d.%d.mca failed. " \ + "The server will now abort in order to avoid further data loss. " \ + "Please add the reported file and this message to the issue report.", \ + __FUNCTION__, __LINE__, CHX, CHZ, RegionX, RegionZ \ + ); \ + *((int *)0) = 0; /* Crash intentionally */ \ + } + @@ -263,6 +275,7 @@ bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & inflateEnd(&strm); if (res != Z_STREAM_END) { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } @@ -271,6 +284,7 @@ bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & if (!NBT.IsValid()) { // NBT Parsing failed + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } @@ -317,11 +331,13 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT int Level = a_NBT.FindChildByName(0, "Level"); if (Level < 0) { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } int Sections = a_NBT.FindChildByName(Level, "Sections"); if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List) || (a_NBT.GetChildrenType(Sections) != TAG_Compound)) { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } for (int Child = a_NBT.GetFirstChild(Sections); Child >= 0; Child = a_NBT.GetNextSibling(Child)) @@ -2811,30 +2827,42 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a } unsigned ChunkLocation = ntohl(m_Header[LocalX + 32 * LocalZ]); unsigned ChunkOffset = ChunkLocation >> 8; + if (ChunkOffset <= 2) + { + return false; + } m_File.Seek((int)ChunkOffset * 4096); int ChunkSize = 0; if (m_File.Read(&ChunkSize, 4) != 4) { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } ChunkSize = ntohl((u_long)ChunkSize); char CompressionType = 0; if (m_File.Read(&CompressionType, 1) != 1) { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } if (CompressionType != 2) { // Chunk is in an unknown compression + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } ChunkSize--; // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly a_Data.assign(ChunkSize, '\0'); - return (m_File.Read((void *)a_Data.data(), ChunkSize) == ChunkSize); + if (m_File.Read((void *)a_Data.data(), ChunkSize) == ChunkSize) + { + return true; + } + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; } -- cgit v1.2.3 From a0687b42e44df65cf4774b40af59ad763c48fa1a Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Tue, 2 Sep 2014 23:05:24 +0200 Subject: Clang wants volatile... --- src/WorldStorage/WSSAnvil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/WorldStorage/WSSAnvil.cpp') diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 0c31d8b02..815b6544f 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -78,7 +78,7 @@ Since only the header is actually in the memory, this number can be high, but st "Please add the reported file and this message to the issue report.", \ __FUNCTION__, __LINE__, CHX, CHZ, RegionX, RegionZ \ ); \ - *((int *)0) = 0; /* Crash intentionally */ \ + *((volatile int *)0) = 0; /* Crash intentionally */ \ } -- cgit v1.2.3 From 44c1d9c2480fe6ea37dbed6199b51acf035de0c2 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 3 Sep 2014 19:36:53 +0200 Subject: Anvil: switched inflate to stream mode. This removes the fixed-size buffer which could have caused #1307 and #1366. --- src/WorldStorage/WSSAnvil.cpp | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'src/WorldStorage/WSSAnvil.cpp') diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 815b6544f..239f4a155 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -66,9 +66,6 @@ Since only the header is actually in the memory, this number can be high, but st */ #define MAX_MCA_FILES 32 -/// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities -#define CHUNK_INFLATE_MAX 256 KiB - #define LOAD_FAILED(CHX, CHZ) \ { \ const int RegionX = FAST_FLOOR_DIV(CHX, 32); \ @@ -260,27 +257,18 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data) { - // Decompress the data: - char Uncompressed[CHUNK_INFLATE_MAX]; - z_stream strm; - strm.zalloc = (alloc_func)NULL; - strm.zfree = (free_func)NULL; - strm.opaque = NULL; - inflateInit(&strm); - strm.next_out = (Bytef *)Uncompressed; - strm.avail_out = sizeof(Uncompressed); - strm.next_in = (Bytef *)a_Data.data(); - strm.avail_in = (uInt)a_Data.size(); - int res = inflate(&strm, Z_FINISH); - inflateEnd(&strm); - if (res != Z_STREAM_END) + // Uncompress the data: + AString Uncompressed; + int res = InflateString(a_Data.data(), a_Data.size(), Uncompressed); + if (res != Z_OK) { + LOGWARNING("Uncompressing chunk [%d, %d] failed: %d", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, res); LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; } // Parse the NBT data: - cParsedNBT NBT(Uncompressed, strm.total_out); + cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); if (!NBT.IsValid()) { // NBT Parsing failed -- cgit v1.2.3 From d77221c7157a7a371cdbc7b00abe287199be3c86 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 4 Sep 2014 14:00:54 +0200 Subject: Anvil: Cleanly refuse to store data that is too large. Each chunk in MCA needs to be less than 1 MiB compressed; chunks that are larger will be refused with a log message. --- src/WorldStorage/WSSAnvil.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/WorldStorage/WSSAnvil.cpp') diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 239f4a155..4d2f92173 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -2905,7 +2905,13 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri // Store the header: ChunkSize = ((u_long)a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number - ASSERT(ChunkSize < 256); + if (ChunkSize > 255) + { + LOGWARNING("Cannot save chunk [%d, %d], the data is too large (%u KiB, maximum is 1024 KiB). Remove some entities and retry.", + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, (unsigned)(ChunkSize * 4) + ); + return false; + } m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize); if (m_File.Seek(0) < 0) { -- cgit v1.2.3 From 254c8c9154e8355f139e43cf482366bb4855a4f8 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 5 Sep 2014 16:40:03 +0300 Subject: Anvil: Fixed loading block entities with invalid Y coord. --- src/WorldStorage/WSSAnvil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/WorldStorage/WSSAnvil.cpp') diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 4d2f92173..b8f8ba019 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -589,7 +589,7 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con // Get the BlockEntity's position int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, Child, x, y, z)) + if (!GetBlockEntityNBTPos(a_NBT, Child, x, y, z) || (y < 0) || (y >= cChunkDef::Height)) { LOGWARNING("Bad block entity, missing the coords. Will be ignored."); continue; -- cgit v1.2.3 From 4230eb3d924a2230a20e5044dfffa118ad090429 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 5 Sep 2014 22:55:39 +0200 Subject: Fixed loading empty chunks. Reported on the Dropper map in #1307. --- src/WorldStorage/WSSAnvil.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/WorldStorage/WSSAnvil.cpp') diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index b8f8ba019..f78ee405b 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -323,7 +323,13 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT return false; } int Sections = a_NBT.FindChildByName(Level, "Sections"); - if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List) || (a_NBT.GetChildrenType(Sections) != TAG_Compound)) + if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List)) + { + LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + eTagType SectionsType = a_NBT.GetChildrenType(Sections); + if ((SectionsType != TAG_Compound) && (SectionsType != TAG_End)) { LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); return false; -- cgit v1.2.3 From 9f9302f470d2adc1ef4018474c4f03a72ca723ed Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 6 Sep 2014 13:32:16 +0200 Subject: Anvil: Fixed an off-by-one error in the loader. Fixes #1307. --- src/WorldStorage/WSSAnvil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/WorldStorage/WSSAnvil.cpp') diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index f78ee405b..2500b5dea 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -2821,7 +2821,7 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a } unsigned ChunkLocation = ntohl(m_Header[LocalX + 32 * LocalZ]); unsigned ChunkOffset = ChunkLocation >> 8; - if (ChunkOffset <= 2) + if (ChunkOffset < 2) { return false; } -- cgit v1.2.3 From 0a651b9fd83f078a73429f02603144e80a05148d Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 6 Sep 2014 18:59:17 +0200 Subject: Added Y-wise asserts to signs. This should help detect #1313's second case. --- src/WorldStorage/WSSAnvil.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/WorldStorage/WSSAnvil.cpp') diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 2500b5dea..fe309ce4e 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -623,6 +623,8 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con cBlockEntity * cWSSAnvil::LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a_Tag, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { + ASSERT((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height)); + // Load the specific BlockEntity type: switch (a_BlockType) { -- cgit v1.2.3