# HG changeset patch # User Adam Kaminski # Date 1611519024 18000 # Sun Jan 24 15:10:24 2021 -0500 # Node ID 428638a294bfda192b39ff5e5258953a0af8b7f9 # Parent 0af8e01641ef5153b1c4afb954c2e90d8de49a16 Refined the duplicate lump check inside ZIP/PK3 files. Now, a fatal error only occurs if any duplicate lumps that are found need to be authenticated by the server. Otherwise, a console error message will be printed during startup and the file will be allowed to load. diff -r 0af8e01641ef -r 428638a294bf docs/zandronum-history.txt --- a/docs/zandronum-history.txt Sun Jan 24 14:35:52 2021 -0500 +++ b/docs/zandronum-history.txt Sun Jan 24 15:10:24 2021 -0500 @@ -61,6 +61,7 @@ ! - GAMEEVENT_CAPTURES scripts now pass the number of points earned as 'arg2'. [Kaminsky] ! - gl_lights_size is now forced to its default value when sv_forcevideodefaults is true. [Kaminsky] ! - The Player_SetTeam line special now accepts a second parameter to enable/disable the broadcast message. By default, the message is still printed. [Kaminsky] +! - ZIP/PK3 files containing duplicate lumps will now print warning messages in the console when they're found, and a fatal error will occur if any of these duplicate lumps need to be authenticated on the server. [Kaminsky] 3.0.1 diff -r 0af8e01641ef -r 428638a294bf src/network.cpp --- a/src/network.cpp Sun Jan 24 14:35:52 2021 -0500 +++ b/src/network.cpp Sun Jan 24 15:10:24 2021 -0500 @@ -359,6 +359,20 @@ for ( unsigned int i = 0; i < lumpsToAuthenticate.size(); i++ ) { + // [AK] Check if we're authenticating any duplicate lumps on the server. + if ( NETWORK_GetState() == NETSTATE_SERVER ) + { + for ( unsigned int j = 0; j < DuplicateLumps.Size(); j++ ) + { + // [AK] If there's a match, throw a fatal error instead. + if ( stricmp( DuplicateLumps[j], lumpsToAuthenticate[i].c_str() ) == 0 ) + { + I_Error( "Attempt to authenticate duplicate protected lump '%s'.\n", lumpsToAuthenticate[i].c_str() ); + return; + } + } + } + switch ( lumpsToAuthenticateMode[i] ){ case LAST_LUMP: int lump; @@ -988,6 +1002,24 @@ if ( LumpNumber == -1 ) return; + // [AK] Check if we're trying to authenticate a duplicate lump on the server. + if ( NETWORK_GetState() == NETSTATE_SERVER ) + { + const char *lumpName = Wads.GetLumpFullName( LumpNumber ); + for ( unsigned int i = 0; i < DuplicateLumps.Size(); i++ ) + { + // [AK] If there's a match, throw a fatal error instead. + if ( DuplicateLumps[i].CompareNoCase( lumpName ) == 0 ) + { + FString fullPath = Wads.GetLumpFullPath( LumpNumber ); + const char *fileName = fullPath.Left( fullPath.Len() - strlen( lumpName ) - 1 ); + + I_Error( "Attempt to authenticate duplicate lump '%s' found in '%s'.\n", lumpName, fileName ); + return; + } + } + } + g_LumpNumsToAuthenticate.Push ( LumpNumber ); } diff -r 0af8e01641ef -r 428638a294bf src/resourcefiles/file_7z.cpp --- a/src/resourcefiles/file_7z.cpp Sun Jan 24 14:35:52 2021 -0500 +++ b/src/resourcefiles/file_7z.cpp Sun Jan 24 15:10:24 2021 -0500 @@ -263,7 +263,6 @@ Lumps = new F7ZLump[NumLumps]; - FString oldName; // [AK] Store the name of the last lump we scanned. F7ZLump *lump_p = Lumps; TArray nameUTF16; TArray nameASCII; @@ -298,22 +297,12 @@ FString name = &nameASCII[0]; name.ToLower(); - // [AK] Check for any duplicate lumps in the file. If we find any, then throw an error. We - // shouldn't be loading any malformed 7z files, as this can lead to authentication issues. - if ((oldName.IsNotEmpty()) && (oldName.CompareNoCase(name) == 0)) - { - I_Error("Couldn't load file %s: duplicate lump '%s' detected.\n", Filename, name.GetChars()); - return false; - } - lump_p->LumpNameSetup(name); lump_p->LumpSize = int(file->Size); lump_p->Owner = this; lump_p->Flags = LUMPF_ZIPFILE; lump_p->Position = i; lump_p->CheckEmbedded(); - - oldName = name; // [AK] lump_p++; } // Resize the lump record array to its actual size diff -r 0af8e01641ef -r 428638a294bf src/resourcefiles/file_zip.cpp --- a/src/resourcefiles/file_zip.cpp Sun Jan 24 14:35:52 2021 -0500 +++ b/src/resourcefiles/file_zip.cpp Sun Jan 24 15:10:24 2021 -0500 @@ -205,7 +205,10 @@ Reader->Seek(LittleLong(info.DirectoryOffset), SEEK_SET); Reader->Read(directory, dirsize); - FString oldName; // [AK] Store the name of the last lump we scanned. + // [AK] Store the names of all the lumps in the current directory. + TArray oldNames; + FString currentDirectory; + char *dirptr = (char*)directory; FZipLump *lump_p = Lumps; for (DWORD i = 0; i < NumLumps; i++) @@ -233,14 +236,6 @@ continue; } - // [AK] Check for any duplicate lumps in the file. If we find any, then throw an error. We - // shouldn't be loading any malformed zip files, as this can lead to authentication issues. - if ((oldName.IsNotEmpty()) && (oldName.CompareNoCase(name) == 0)) - { - I_Error("Couldn't load file %s: duplicate lump '%s' detected.\n", Filename, name.GetChars()); - return false; - } - // Ignore unknown compression formats zip_fh->Method = LittleShort(zip_fh->Method); if (zip_fh->Method != METHOD_STORED && @@ -283,7 +278,60 @@ memset(lump_p->Name, 0, sizeof(lump_p->Name)); } - oldName = name; // [AK] + // [AK] Get the full name of the directory that the lump is located in. + const char *directory = strrchr(name, '/'); + FString directoryName = "/"; + + if (directory != NULL) + { + directoryName = name.Left(ULONG(directory - name.GetChars())); + } + + // [AK] Check if we're in the same directory. If not, clear the list of old lump names. + if ((currentDirectory.IsEmpty()) || (currentDirectory.CompareNoCase(directoryName) != 0)) + { + oldNames.Clear(); + currentDirectory = directoryName; + } + + // [AK] Check for any duplicate lumps in the current directory. + for (int i = oldNames.Size() - 1; i > -1; i--) + { + if (oldNames[i].CompareNoCase(name) == 0) + { + if (!quiet) Printf(TEXTCOLOR_YELLOW "\n%s: duplicate lump '%s' detected.\n", Filename, name.GetChars()); + + // [AK] We only want to keep track of duplicate lumps that may cause authentication + // failures if the file is loaded. Therefore, ignore any lumps that aren't part of + // the global or ACS namespaces. + if ((lump_p->Namespace != -1 ) && (lump_p->Namespace != ns_global) && (lump_p->Namespace != ns_acslibrary)) + break; + + bool addDuplicate = true; + + // [AK] To keep the list of duplicate lumps as small as possible, first check if the + // name of the lump isn't already in the list. If nothing was found, then add the + // full name of the lump to it. + for (unsigned int j = 0; j < DuplicateLumps.Size(); j++) + { + if (DuplicateLumps[j].CompareNoCase(lump_p->FullName) == 0) + { + addDuplicate = false; + break; + } + } + + if (addDuplicate) + { + DuplicateLumps.Push(lump_p->FullName); + } + + break; + } + } + + // [AK] Add this lump name to the list. + oldNames.Push(name); lump_p++; } // Resize the lump record array to its actual size @@ -291,7 +339,7 @@ free(directory); if (!quiet) Printf(TEXTCOLOR_NORMAL ", %d lumps\n", NumLumps); - + // Entries in Zips are sorted alphabetically. qsort(Lumps, NumLumps, sizeof(FZipLump), lumpcmp); return true; diff -r 0af8e01641ef -r 428638a294bf src/w_wad.cpp --- a/src/w_wad.cpp Sun Jan 24 14:35:52 2021 -0500 +++ b/src/w_wad.cpp Sun Jan 24 15:10:24 2021 -0500 @@ -93,6 +93,9 @@ FWadCollection Wads; +// [AK] A list of all duplicate lumps found during startup. +TArray DuplicateLumps; + // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- diff -r 0af8e01641ef -r 428638a294bf src/w_wad.h --- a/src/w_wad.h Sun Jan 24 14:35:52 2021 -0500 +++ b/src/w_wad.h Sun Jan 24 15:10:24 2021 -0500 @@ -249,4 +249,7 @@ extern FWadCollection Wads; +// [AK] A list of all duplicate lumps found during startup. +extern TArray DuplicateLumps; + #endif