From 77fd87d709c05632d084c71661d90a6ec47cf201 Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 12 Dec 2025 18:23:43 +0800 Subject: [PATCH] 5.6.1.4: Use in-memory archive instead of file IO --- unrar/unrar/arccmt.cpp | 2 +- unrar/unrar/archive.cpp | 75 ++++++++++++++++++++++++++++++++---- unrar/unrar/archive.hpp | 9 ++++- unrar/unrar/arcmem.cpp | 62 +++++++++++++++++++++++++++++ unrar/unrar/arcmem.hpp | 22 +++++++++++ unrar/unrar/arcread.cpp | 4 +- unrar/unrar/cmddata.cpp | 8 ++-- unrar/unrar/compress.hpp | 9 +++++ unrar/unrar/file.hpp | 4 +- unrar/unrar/makefile | 2 +- unrar/unrar/options.hpp | 16 +++++++- unrar/unrar/qopen.cpp | 17 +++++++- unrar/unrar/rar.cpp | 7 ++++ unrar/unrar/rar.hpp | 3 ++ unrar/unrar/rardefs.hpp | 1 + unrar/unrar/recvol5.cpp | 4 ++ unrar/unrar/unicode.cpp | 5 ++- unrar/unrar/unpack.hpp | 2 +- unrar/unrar/unpack30.cpp | 4 +- unrar/unrar/unpack50.cpp | 2 +- unrar/unrar/unpack50mt.cpp | 4 +- unrar/unrar/unpackinline.cpp | 4 +- 22 files changed, 235 insertions(+), 31 deletions(-) create mode 100644 unrar/unrar/arcmem.cpp create mode 100644 unrar/unrar/arcmem.hpp diff --git a/unrar/unrar/arccmt.cpp b/unrar/unrar/arccmt.cpp index 47b352b2..9003069e 100644 --- a/unrar/unrar/arccmt.cpp +++ b/unrar/unrar/arccmt.cpp @@ -111,7 +111,7 @@ bool Archive::GetComment(Array *CmtData) // 4x memory for OEM to UTF-8 output here. OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]); #endif - CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtLength); + CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); CmtData->Alloc(wcslen(CmtData->Addr(0))); } #endif diff --git a/unrar/unrar/archive.cpp b/unrar/unrar/archive.cpp index e66bddb9..c76dd25e 100644 --- a/unrar/unrar/archive.cpp +++ b/unrar/unrar/archive.cpp @@ -3,6 +3,10 @@ #include "arccmt.cpp" +#ifdef USE_ARCMEM +#include "arcmem.cpp" +#endif + Archive::Archive(RAROptions *InitCmd) { Cmd=NULL; // Just in case we'll have an exception in 'new' below. @@ -48,6 +52,10 @@ Archive::Archive(RAROptions *InitCmd) SilentOpen=false; +#ifdef USE_QOPEN + ProhibitQOpen=false; +#endif + } @@ -294,39 +302,92 @@ uint Archive::FullHeaderSize(size_t Size) -#ifdef USE_QOPEN bool Archive::Open(const wchar *Name,uint Mode) { +#ifdef USE_QOPEN // Important if we reuse Archive object and it has virtual QOpen // file position not matching real. For example, for 'l -v volname'. QOpen.Unload(); +#endif + +#ifdef USE_ARCMEM + if (Cmd->ArcInMem) + { + wcsncpyz(FileName,Name,ASIZE(FileName)); + ArcMem.Load(Cmd->ArcMemData,Cmd->ArcMemSize); + Cmd->SetArcInMem(NULL,0); // Return in memory data for first volume only, not for next volumes. + return true; + } +#endif return File::Open(Name,Mode); } + +bool Archive::Close() +{ +#ifdef USE_ARCMEM + if (ArcMem.Unload()) + return true; +#endif + return File::Close(); +} + + + int Archive::Read(void *Data,size_t Size) { - size_t Result; - if (QOpen.Read(Data,Size,Result)) - return (int)Result; +#ifdef USE_QOPEN + size_t QResult; + if (QOpen.Read(Data,Size,QResult)) + return (int)QResult; +#endif +#ifdef USE_ARCMEM + size_t AResult; + if (ArcMem.Read(Data,Size,AResult)) + return (int)AResult; +#endif return File::Read(Data,Size); } void Archive::Seek(int64 Offset,int Method) { - if (!QOpen.Seek(Offset,Method)) - File::Seek(Offset,Method); +#ifdef USE_QOPEN + if (QOpen.Seek(Offset,Method)) + return; +#endif +#ifdef USE_ARCMEM + if (ArcMem.Seek(Offset,Method)) + return; +#endif + File::Seek(Offset,Method); } int64 Archive::Tell() { +#ifdef USE_QOPEN int64 QPos; if (QOpen.Tell(&QPos)) return QPos; +#endif +#ifdef USE_ARCMEM + int64 APos; + if (ArcMem.Tell(&APos)) + return APos; +#endif return File::Tell(); } -#endif + + +bool Archive::IsOpened() +{ +#ifdef USE_ARCMEM + if (ArcMem.IsLoaded()) + return true; +#endif + return File::IsOpened(); +}; diff --git a/unrar/unrar/archive.hpp b/unrar/unrar/archive.hpp index 6e85b9ff..473912fb 100644 --- a/unrar/unrar/archive.hpp +++ b/unrar/unrar/archive.hpp @@ -55,6 +55,10 @@ class Archive:public File bool SilentOpen; #ifdef USE_QOPEN QuickOpen QOpen; + bool ProhibitQOpen; +#endif +#ifdef USE_ARCMEM + ArcMemory ArcMem; #endif public: Archive(RAROptions *InitCmd=NULL); @@ -89,12 +93,15 @@ class Archive:public File #if 0 void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); #endif -#ifdef USE_QOPEN bool Open(const wchar *Name,uint Mode=FMF_READ); + bool Close(); int Read(void *Data,size_t Size); void Seek(int64 Offset,int Method); int64 Tell(); + bool IsOpened(); +#ifdef USE_QOPEN void QOpenUnload() {QOpen.Unload();} + void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;} #endif BaseBlock ShortBlock; diff --git a/unrar/unrar/arcmem.cpp b/unrar/unrar/arcmem.cpp new file mode 100644 index 00000000..a8497856 --- /dev/null +++ b/unrar/unrar/arcmem.cpp @@ -0,0 +1,62 @@ +ArcMemory::ArcMemory() +{ + Loaded=false; + SeekPos=0; +} + + +void ArcMemory::Load(const byte *Data,size_t Size) +{ + ArcData.Alloc(Size); + memcpy(&ArcData[0],Data,Size); + Loaded=true; + SeekPos=0; +} + + +bool ArcMemory::Unload() +{ + if (!Loaded) + return false; + Loaded=false; + return true; +} + + +bool ArcMemory::Read(void *Data,size_t Size,size_t &Result) +{ + if (!Loaded) + return false; + Result=(size_t)Min(Size,ArcData.Size()-SeekPos); + memcpy(Data,&ArcData[(size_t)SeekPos],Result); + SeekPos+=Result; + return true; +} + + +bool ArcMemory::Seek(int64 Offset,int Method) +{ + if (!Loaded) + return false; + if (Method==SEEK_SET) + SeekPos=Min(Offset,ArcData.Size()); + else + if (Method==SEEK_CUR || Method==SEEK_END) + { + if (Method==SEEK_END) + SeekPos=ArcData.Size(); + SeekPos+=(uint64)Offset; + if (SeekPos>ArcData.Size()) + SeekPos=Offset<0 ? 0 : ArcData.Size(); + } + return true; +} + + +bool ArcMemory::Tell(int64 *Pos) +{ + if (!Loaded) + return false; + *Pos=SeekPos; + return true; +} diff --git a/unrar/unrar/arcmem.hpp b/unrar/unrar/arcmem.hpp new file mode 100644 index 00000000..6fbe7c38 --- /dev/null +++ b/unrar/unrar/arcmem.hpp @@ -0,0 +1,22 @@ +#ifndef _RAR_ARCMEM_ +#define _RAR_ARCMEM_ + +// Memory interface for software fuzzers. + +class ArcMemory +{ + private: + bool Loaded; + Array ArcData; + uint64 SeekPos; + public: + ArcMemory(); + void Load(const byte *Data,size_t Size); + bool Unload(); + bool IsLoaded() {return Loaded;} + bool Read(void *Data,size_t Size,size_t &Result); + bool Seek(int64 Offset,int Method); + bool Tell(int64 *Pos); +}; + +#endif diff --git a/unrar/unrar/arcread.cpp b/unrar/unrar/arcread.cpp index 08f3628c..9b18216e 100644 --- a/unrar/unrar/arcread.cpp +++ b/unrar/unrar/arcread.cpp @@ -734,7 +734,7 @@ size_t Archive::ReadHeader50() ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead); #ifdef USE_QOPEN - if (MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE) + if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE) { // We seek to QO block in the end of archive when processing // QOpen.Load, so we need to preserve current block positions @@ -1070,7 +1070,7 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb) wchar VerText[20]; swprintf(VerText,ASIZE(VerText),L";%u",Version); - wcsncatz(FileHead.FileName,VerText,ASIZE(FileHead.FileName)); + wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName)); } } break; diff --git a/unrar/unrar/cmddata.cpp b/unrar/unrar/cmddata.cpp index ee5b42ea..ce5837ef 100644 --- a/unrar/unrar/cmddata.cpp +++ b/unrar/unrar/cmddata.cpp @@ -96,7 +96,7 @@ void CommandData::ParseArg(wchar *Arg) else if (*Command==0) { - wcsncpy(Command,Arg,ASIZE(Command)); + wcsncpyz(Command,Arg,ASIZE(Command)); *Command=toupperw(*Command); @@ -1208,8 +1208,8 @@ int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchTy { if (MatchedArg!=NULL && MatchedArgSize>0) *MatchedArg=0; - if (wcslen(FileHead.FileName)>=NM) - return 0; +// if (wcslen(FileHead.FileName)>=NM) +// return 0; bool Dir=FileHead.Dir; if (ExclCheck(FileHead.FileName,Dir,false,true)) return 0; @@ -1267,7 +1267,7 @@ void CommandData::ProcessCommand() wcsncpyz(ArcName,Name,ASIZE(ArcName)); } - if (wcschr(L"AFUMD",*Command)==NULL) + if (wcschr(L"AFUMD",*Command)==NULL && !ArcInMem) { if (GenerateArcName) GenerateArchiveName(ArcName,ASIZE(ArcName),GenerateMask,false); diff --git a/unrar/unrar/compress.hpp b/unrar/unrar/compress.hpp index 8e4f1ed3..73f7ee41 100644 --- a/unrar/unrar/compress.hpp +++ b/unrar/unrar/compress.hpp @@ -6,7 +6,16 @@ class PackDef { public: + // Maximum LZ match length we can encode even for short distances. static const uint MAX_LZ_MATCH = 0x1001; + + // We increment LZ match length for longer distances, because shortest + // matches are not allowed for them. Maximum length increment is 3 + // for distances larger than 256KB (0x40000). Here we define the maximum + // incremented LZ match. Normally packer does not use it, but we must be + // ready to process it in corrupt archives. + static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3; + static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. static const uint LOW_DIST_REP_COUNT = 16; diff --git a/unrar/unrar/file.hpp b/unrar/unrar/file.hpp index 9d8193ed..177c20db 100644 --- a/unrar/unrar/file.hpp +++ b/unrar/unrar/file.hpp @@ -75,7 +75,7 @@ class File bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); - bool Close(); + virtual bool Close(); bool Delete(); bool Rename(const wchar *NewName); bool Write(const void *Data,size_t Size); @@ -93,7 +93,7 @@ class File void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta); void GetOpenFileTime(RarTime *ft); - bool IsOpened() {return hFile!=FILE_BAD_HANDLE;}; + virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;}; int64 FileLength(); void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} FILE_HANDLETYPE GetHandleType() {return HandleType;} diff --git a/unrar/unrar/makefile b/unrar/unrar/makefile index 259b907e..4316d63b 100644 --- a/unrar/unrar/makefile +++ b/unrar/unrar/makefile @@ -3,7 +3,7 @@ # Linux using GCC CXX=c++ -CXXFLAGS=-O2 +CXXFLAGS=-O2 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else LIBFLAGS=-fPIC DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP STRIP=strip diff --git a/unrar/unrar/options.hpp b/unrar/unrar/options.hpp index f4198c38..c982af4d 100644 --- a/unrar/unrar/options.hpp +++ b/unrar/unrar/options.hpp @@ -85,10 +85,24 @@ class RAROptions bool InclAttrSet; size_t WinSize; wchar TempPath[NM]; -#ifdef USE_QOPEN wchar SFXModule[NM]; + +#ifdef USE_QOPEN QOPEN_MODE QOpenMode; #endif + + bool ArcInMem; +#ifdef USE_ARCMEM + void SetArcInMem(byte *Data,size_t Size) + { + ArcMemData=Data; + ArcMemSize=Size; + ArcInMem=Data!=NULL && Size>0; + } + byte *ArcMemData; + size_t ArcMemSize; +#endif + bool ConfigDisabled; // Switch -cfg-. wchar ExtrPath[NM]; wchar CommentFile[NM]; diff --git a/unrar/unrar/qopen.cpp b/unrar/unrar/qopen.cpp index 789e5c03..97851ca1 100644 --- a/unrar/unrar/qopen.cpp +++ b/unrar/unrar/qopen.cpp @@ -61,14 +61,27 @@ void QuickOpen::Close() void QuickOpen::Load(uint64 BlockPos) { - if (!Loaded) // If loading the first time, perform additional intialization. + if (!Loaded) { + // If loading for the first time, perform additional intialization. SeekPos=Arc->Tell(); UnsyncSeekPos=false; SaveFilePos SavePos(*Arc); Arc->Seek(BlockPos,SEEK_SET); - if (Arc->ReadHeader()==0 || Arc->GetHeaderType()!=HEAD_SERVICE || + + // If BlockPos points to original main header, we'll have the infinite + // recursion, because ReadHeader() for main header will attempt to load + // QOpen and call QuickOpen::Load again. If BlockPos points to long chain + // of other main headers, we'll have multiple recursive calls of this + // function wasting resources. So we prohibit QOpen temporarily to + // prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator + // and QOpenOffset fields, so we cannot use them to prohibit QOpen. + Arc->SetProhibitQOpen(true); + size_t ReadSize=Arc->ReadHeader(); + Arc->SetProhibitQOpen(false); + + if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE || !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN)) return; QLHeaderPos=Arc->CurBlockPos; diff --git a/unrar/unrar/rar.cpp b/unrar/unrar/rar.cpp index 69175b76..1ac37a12 100644 --- a/unrar/unrar/rar.cpp +++ b/unrar/unrar/rar.cpp @@ -76,6 +76,13 @@ int main(int argc, char *argv[]) ErrHandler.SetSilent(Cmd->AllYes || Cmd->MsgStream==MSG_NULL); Cmd->OutTitle(); +/* + byte Buf[10000]; + File Src; + Src.TOpen(L"123.rar"); + int Size=Src.Read(Buf,sizeof(Buf)); + Cmd->SetArcInMem(Buf,Size); +*/ Cmd->ProcessCommand(); delete Cmd; } diff --git a/unrar/unrar/rar.hpp b/unrar/unrar/rar.hpp index b4adb73d..903f6f5f 100644 --- a/unrar/unrar/rar.hpp +++ b/unrar/unrar/rar.hpp @@ -42,6 +42,9 @@ #ifdef USE_QOPEN #include "qopen.hpp" #endif +#ifdef USE_ARCMEM +#include "arcmem.hpp" +#endif #include "archive.hpp" #include "match.hpp" #include "cmddata.hpp" diff --git a/unrar/unrar/rardefs.hpp b/unrar/unrar/rardefs.hpp index 90d41204..aa7c329e 100644 --- a/unrar/unrar/rardefs.hpp +++ b/unrar/unrar/rardefs.hpp @@ -24,6 +24,7 @@ #ifndef SFX_MODULE #define USE_QOPEN #endif +#define USE_ARCMEM // Produce the value, which is equal or larger than 'v' and aligned to 'a'. #define ALIGN_VALUE(v,a) (size_t(v) + ( (~size_t(v) + 1) & (a - 1) ) ) diff --git a/unrar/unrar/recvol5.cpp b/unrar/unrar/recvol5.cpp index 8272b070..fd74c1b9 100644 --- a/unrar/unrar/recvol5.cpp +++ b/unrar/unrar/recvol5.cpp @@ -139,8 +139,12 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) wcsncpyz(ArcName,Name,ASIZE(ArcName)); wchar *Num=GetVolNumPart(ArcName); + if (Num==ArcName) + return false; // Number part is missing in the name. while (Num>ArcName && IsDigit(*(Num-1))) Num--; + if (Num==ArcName) + return false; // Entire volume name is numeric, not possible for REV file. wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName)); wchar FirstVolName[NM]; diff --git a/unrar/unrar/unicode.cpp b/unrar/unrar/unicode.cpp index e462906b..d8d11e9f 100644 --- a/unrar/unrar/unicode.cpp +++ b/unrar/unrar/unicode.cpp @@ -142,7 +142,7 @@ bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success) { mbstate_t ps; memset(&ps,0,sizeof(ps)); - if (wcrtomb(Dest+DestPos,Src[SrcPos],&ps)==-1) + if (wcrtomb(Dest+DestPos,Src[SrcPos],&ps)==(size_t)-1) Success=false; SrcPos++; memset(&ps,0,sizeof(ps)); @@ -176,7 +176,8 @@ void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success) } mbstate_t ps; memset(&ps,0,sizeof(ps)); - if (mbrtowc(Dest+DestPos,Src+SrcPos,MB_CUR_MAX,&ps)==-1) + size_t res=mbrtowc(Dest+DestPos,Src+SrcPos,MB_CUR_MAX,&ps); + if (res==(size_t)-1 || res==(size_t)-2) { // For security reasons we do not want to map low ASCII characters, // so we do not have additional .. and path separator codes. diff --git a/unrar/unrar/unpack.hpp b/unrar/unrar/unpack.hpp index ca5e66cf..5236f41d 100644 --- a/unrar/unrar/unpack.hpp +++ b/unrar/unrar/unpack.hpp @@ -316,7 +316,7 @@ class Unpack:PackDef bool ReadEndOfBlock(); bool ReadVMCode(); bool ReadVMCodePPM(); - bool AddVMCode(uint FirstByte,byte *Code,int CodeSize); + bool AddVMCode(uint FirstByte,byte *Code,uint CodeSize); int SafePPMDecodeChar(); bool ReadTables30(); bool UnpReadBuf30(); diff --git a/unrar/unrar/unpack30.cpp b/unrar/unrar/unpack30.cpp index d558806a..d3f91221 100644 --- a/unrar/unrar/unpack30.cpp +++ b/unrar/unrar/unpack30.cpp @@ -354,7 +354,7 @@ bool Unpack::ReadVMCodePPM() } -bool Unpack::AddVMCode(uint FirstByte,byte *Code,int CodeSize) +bool Unpack::AddVMCode(uint FirstByte,byte *Code,uint CodeSize) { VMCodeInp.InitBitInput(); memcpy(VMCodeInp.InBuf,Code,Min(BitInput::MAX_SIZE,CodeSize)); @@ -466,7 +466,7 @@ bool Unpack::AddVMCode(uint FirstByte,byte *Code,int CodeSize) if (NewFilter) { uint VMCodeSize=RarVM::ReadData(VMCodeInp); - if (VMCodeSize>=0x10000 || VMCodeSize==0) + if (VMCodeSize>=0x10000 || VMCodeSize==0 || VMCodeInp.InAddr+VMCodeSize>CodeSize) return false; Array VMCode(VMCodeSize); for (uint I=0;IDestUnpSize) diff --git a/unrar/unrar/unpack50mt.cpp b/unrar/unrar/unpack50mt.cpp index 6f786adc..8f192a81 100644 --- a/unrar/unrar/unpack50mt.cpp +++ b/unrar/unrar/unpack50mt.cpp @@ -449,7 +449,7 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D) while (ItemDestUnpSize) @@ -557,7 +557,7 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D) break; } } - if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) diff --git a/unrar/unrar/unpackinline.cpp b/unrar/unrar/unpackinline.cpp index 1dc6980c..dc0d4113 100644 --- a/unrar/unrar/unpackinline.cpp +++ b/unrar/unrar/unpackinline.cpp @@ -13,7 +13,7 @@ _forceinline void Unpack::InsertOldDist(uint Distance) _forceinline void Unpack::CopyString(uint Length,uint Distance) { size_t SrcPtr=UnpPtr-Distance; - if (SrcPtr