CVE-2017-20006: Out-of-bounds Write (CWE-787)
value: UnRAR 5.6.1.2 and 5.6.1.3 has a heap-based buffer overflow in Unpack::CopyString (called from Unpack::Unpack5 and CmdExtract::ExtractCurrentFile).
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.8
Published: 2021-07-01T03:15Z
Repository: https://github.com/aawc/unrar
Language: C++
Files changed: arccmt.cpp, archive.cpp, archive.hpp, arcmem.cpp, arcmem.hpp, arcread.cpp, cmddata.cpp, compress.hpp, file.hpp, makefile, options.hpp, qopen.cpp, rar.cpp, rar.hpp, rardefs.hpp, recvol5.cpp, unicode.cpp, unpack.hpp, unpack30.cpp, unpack50.cpp, unpack50mt.cpp, unpackinline.cpp
Commit date: 2018-01-04 12:07:56 -0800
Commit message: 5.6.1.4: Use in-memory archive instead of file IO

Diff: diff --git a/arccmt.cpp b/arccmt.cpp
index e0db392..33f2204 100644
--- a/arccmt.cpp
+++ b/arccmt.cpp
@@ -118,11 +118,11 @@ bool Archive::GetComment(Array<wchar> *CmtData)
 #ifdef _WIN_ALL
     // If we ever decide to extend it to Android, we'll need to alloc
     // 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
   return CmtData->Size() > 0;
 }
diff --git a/archive.cpp b/archive.cpp
index 2abe60d..401fc34 100644
--- a/archive.cpp
+++ b/archive.cpp
@@ -1,10 +1,14 @@
 #include "rar.hpp"
 
 #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.
 
   DummyCmd=(InitCmd==NULL);
@@ -46,10 +50,14 @@ Archive::Archive(RAROptions *InitCmd)
   Splitting=false;
   NewArchive=false;
 
   SilentOpen=false;
 
+#ifdef USE_QOPEN
+  ProhibitQOpen=false;
+#endif
+
 }
 
 
 Archive::~Archive()
 {
@@ -292,41 +300,94 @@ 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/archive.hpp b/archive.hpp
index 6e85b9f..473912f 100644
--- a/archive.hpp
+++ b/archive.hpp
@@ -53,10 +53,14 @@ class Archive:public File
     HEADER_TYPE CurHeaderType;
 
     bool SilentOpen;
 #ifdef USE_QOPEN
     QuickOpen QOpen;
+    bool ProhibitQOpen;
+#endif
+#ifdef USE_ARCMEM
+    ArcMemory ArcMem;
 #endif
   public:
     Archive(RAROptions *InitCmd=NULL);
     ~Archive();
     static RARFORMAT IsSignature(const byte *D,size_t Size);
@@ -87,16 +91,19 @@ class Archive:public File
     RAROptions* GetRAROptions() {return Cmd;}
     void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
 #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;
     MarkHeader MarkHead;
     MainHeader MainHead;
diff --git a/arcmem.cpp b/arcmem.cpp
new file mode 100644
index 0000000..a849785
--- /dev/null
+++ b/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/arcmem.hpp b/arcmem.hpp
new file mode 100644
index 0000000..6fbe7c3
--- /dev/null
+++ b/arcmem.hpp
@@ -0,0 +1,22 @@
+#ifndef _RAR_ARCMEM_
+#define _RAR_ARCMEM_
+
+// Memory interface for software fuzzers.
+
+class ArcMemory
+{
+  private:
+    bool Loaded;
+    Array<byte> 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/arcread.cpp b/arcread.cpp
index a4900f9..6b3de71 100644
--- a/arcread.cpp
+++ b/arcread.cpp
@@ -755,11 +755,11 @@ size_t Archive::ReadHeader50()
 
         if (ExtraSize!=0)
           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
           // to not break normal archive processing by calling function.
           int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
@@ -1089,11 +1089,11 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
             {
               hd->Version=true;
 
               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;
         case FHEXTRA_REDIR:
           {
diff --git a/cmddata.cpp b/cmddata.cpp
index 1b6da65..baa3eb6 100644
--- a/cmddata.cpp
+++ b/cmddata.cpp
@@ -95,11 +95,11 @@ void CommandData::ParseArg(wchar *Arg)
     else
       ProcessSwitch(Arg+1);
   else
     if (*Command==0)
     {
-      wcsncpy(Command,Arg,ASIZE(Command));
+      wcsncpyz(Command,Arg,ASIZE(Command));
 
 
       *Command=toupperw(*Command);
       // 'I' and 'S' commands can contain case sensitive strings after
       // the first character, so we must not modify their case.
@@ -1192,12 +1192,12 @@ bool CommandData::SizeCheck(int64 Size)
 int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
                                wchar *MatchedArg,uint MatchedArgSize)
 {
   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;
 #ifndef SFX_MODULE
   if (TimeCheck(FileHead.mtime))
@@ -1247,11 +1247,11 @@ void CommandData::ProcessCommand()
     wcsncatz(Name,L".rar",ASIZE(Name));
     if (FileExist(Name))
       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);
 
     StringList ArcMasks;
diff --git a/compress.hpp b/compress.hpp
index 8e4f1ed..73f7ee4 100644
--- a/compress.hpp
+++ b/compress.hpp
@@ -4,11 +4,20 @@
 // Combine pack and unpack constants to class to avoid polluting global
 // namespace with numerous short names.
 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;
 
     static const uint NC    = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */
     static const uint DC    = 64;
diff --git a/file.hpp b/file.hpp
index f8cff4f..20734e2 100644
--- a/file.hpp
+++ b/file.hpp
@@ -76,11 +76,11 @@ class File
     void TOpen(const wchar *Name);
     bool WOpen(const wchar *Name);
     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);
     virtual int Read(void *Data,size_t Size);
     int DirectRead(void *Data,size_t Size);
@@ -94,11 +94,11 @@ class File
     void Flush();
     void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
     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;}
     bool IsDevice();
     static bool RemoveCreated();
diff --git a/makefile b/makefile
index 4f46c2a..f70755f 100644
--- a/makefile
+++ b/makefile
@@ -1,11 +1,11 @@
 #
 # Makefile for UNIX - unrar
 
 # 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
 AR=ar
 LDFLAGS=-pthread
diff --git a/options.hpp b/options.hpp
index b4fed13..781a59e 100644
--- a/options.hpp
+++ b/options.hpp
@@ -87,14 +87,28 @@ class RAROptions
     uint ExclFileAttr;
     uint InclFileAttr;
     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];
     RAR_CHARSET CommentCharset;
     RAR_CHARSET FilelistCharset;
diff --git a/qopen.cpp b/qopen.cpp
index 789e5c0..97851ca 100644
--- a/qopen.cpp
+++ b/qopen.cpp
@@ -59,18 +59,31 @@ 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;
     RawDataStart=Arc->Tell();
     RawDataSize=Arc->SubHead.UnpSize;
diff --git a/rar.cpp b/rar.cpp
index 326bf90..a410576 100644
--- a/rar.cpp
+++ b/rar.cpp
@@ -73,10 +73,17 @@ int main(int argc, char *argv[])
     uiInit(Cmd->Sound);
     InitLogOptions(Cmd->LogName,Cmd->ErrlogCharset);
     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;
   }
   catch (RAR_EXIT ErrCode)
   {
diff --git a/rar.hpp b/rar.hpp
index 65e0496..99b3ee7 100644
--- a/rar.hpp
+++ b/rar.hpp
@@ -43,10 +43,13 @@
 #include "getbits.hpp"
 #include "rdwrfn.hpp"
 #ifdef USE_QOPEN
 #include "qopen.hpp"
 #endif
+#ifdef USE_ARCMEM
+#include "arcmem.hpp"
+#endif
 #include "archive.hpp"
 #include "match.hpp"
 #include "cmddata.hpp"
 #include "filcreat.hpp"
 #include "consio.hpp"
diff --git a/rardefs.hpp b/rardefs.hpp
index 90d4120..aa7c329 100644
--- a/rardefs.hpp
+++ b/rardefs.hpp
@@ -22,10 +22,11 @@
 
 
 #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) ) )
 
 #endif
diff --git a/recvol5.cpp b/recvol5.cpp
index fddc25d..fd74c1b 100644
--- a/recvol5.cpp
+++ b/recvol5.cpp
@@ -141,10 +141,12 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
   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];
   *FirstVolName=0;
 
diff --git a/unicode.cpp b/unicode.cpp
index 8ce752a..9f002ac 100644
--- a/unicode.cpp
+++ b/unicode.cpp
@@ -153,11 +153,11 @@ bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success)
       Dest[DestPos++]=char(uint(Src[SrcPos++])-MapAreaStart);
     else
     {
       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)
       {
         Dest[DestPos]='_';
         Success=false;
       }
       SrcPos++;
@@ -190,11 +190,12 @@ void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success)
       Success=true;
       break;
     }
     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.
       if (byte(Src[SrcPos])>=0x80)
       {
diff --git a/unpack.hpp b/unpack.hpp
index cd71106..93d5b87 100644
--- a/unpack.hpp
+++ b/unpack.hpp
@@ -327,11 +327,11 @@ class Unpack:PackDef
     void Unpack29(bool Solid);
     void InitFilters30(bool Solid);
     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();
     void UnpWriteBuf30();
     void ExecuteCode(VM_PreparedProgram *Prg);
diff --git a/unpack30.cpp b/unpack30.cpp
index 515fff1..6a8efa2 100644
--- a/unpack30.cpp
+++ b/unpack30.cpp
@@ -356,11 +356,11 @@ bool Unpack::ReadVMCodePPM()
   }
   return AddVMCode(FirstByte,&VMCode[0],Length);
 }
 
 
-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));
   VM.Init();
 
@@ -468,11 +468,11 @@ 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<byte> VMCode(VMCodeSize);
     for (uint I=0;I<VMCodeSize;I++)
     {
       if (VMCodeInp.Overflow(3))
diff --git a/unpack50.cpp b/unpack50.cpp
index d8a5959..dac1f6f 100644
--- a/unpack50.cpp
+++ b/unpack50.cpp
@@ -40,11 +40,11 @@ void Unpack::Unpack5(bool Solid)
       }
       if (FileDone || !UnpReadBuf())
         break;
     }
 
-    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_LZ_MATCH+3 && WriteBorder!=UnpPtr)
+    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
     {
       UnpWriteBuf();
       if (WrittenFileSize>DestUnpSize)
         return;
       if (Suspended)
diff --git a/unpack50mt.cpp b/unpack50mt.cpp
index c4acaf1..59e111b 100644
--- a/unpack50mt.cpp
+++ b/unpack50mt.cpp
@@ -449,11 +449,11 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D)
 {
   UnpackDecodedItem *Item=D.Decoded,*Border=D.Decoded+D.DecodedSize;
   while (Item<Border)
   {
     UnpPtr&=MaxWinMask;
-    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_LZ_MATCH+3 && WriteBorder!=UnpPtr)
+    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
     {
       UnpWriteBuf();
       if (WrittenFileSize>DestUnpSize)
         return false;
     }
@@ -557,11 +557,11 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D)
       {
         D.Incomplete=true;
         break;
       }
     }
-    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_LZ_MATCH+3 && WriteBorder!=UnpPtr)
+    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
     {
       UnpWriteBuf();
       if (WrittenFileSize>DestUnpSize)
         return false;
     }
diff --git a/unpackinline.cpp b/unpackinline.cpp
index c12c77d..04c3d1f 100644
--- a/unpackinline.cpp
+++ b/unpackinline.cpp
@@ -11,11 +11,11 @@ _forceinline void Unpack::InsertOldDist(uint Distance)
 #endif
 
 _forceinline void Unpack::CopyString(uint Length,uint Distance)
 {
   size_t SrcPtr=UnpPtr-Distance;
-  if (SrcPtr<MaxWinSize-MAX_LZ_MATCH && UnpPtr<MaxWinSize-MAX_LZ_MATCH)
+  if (SrcPtr<MaxWinSize-MAX_INC_LZ_MATCH && UnpPtr<MaxWinSize-MAX_INC_LZ_MATCH)
   {
     // If we are not close to end of window, we do not need to waste time
     // to "& MaxWinMask" pointer protection.
 
     byte *Src=Window+SrcPtr;
@@ -44,11 +44,11 @@ _forceinline void Unpack::CopyString(uint Length,uint Distance)
     else
       while (Length>=8)
       {
         // In theory we still could overlap here.
         // Supposing Distance == MaxWinSize - 1 we have memcpy(Src, Src + 1, 8).
-        // But for real RAR archives Distance <= MaxWinSize - MAX_LZ_MATCH
+        // But for real RAR archives Distance <= MaxWinSize - MAX_INC_LZ_MATCH
         // always, so overlap here is impossible.
 
         // This memcpy expanded inline by MSVC. We could also use uint64
         // assignment, which seems to provide about the same speed.
         memcpy(Dest,Src,8); 

Vulnerable code:     CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtLength);
#ifdef USE_QOPEN
  size_t Result;
  if (QOpen.Read(Data,Size,Result))
    return (int)Result;
  if (!QOpen.Seek(Offset,Method))
    File::Seek(Offset,Method);
#endif
#ifdef USE_QOPEN
        if (MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE)
              wcsncatz(FileHead.FileName,VerText,ASIZE(FileHead.FileName));
      wcsncpy(Command,Arg,ASIZE(Command));
  if (wcslen(FileHead.FileName)>=NM)
    return 0;
  if (wcschr(L"AFUMD",*Command)==NULL)
    bool Close();
    bool IsOpened() {return hFile!=FILE_BAD_HANDLE;};
CXXFLAGS=-O2
#ifdef USE_QOPEN
  if (!Loaded) // If loading the first time, perform additional intialization.
    if (Arc->ReadHeader()==0 || Arc->GetHeaderType()!=HEAD_SERVICE ||
      if (wcrtomb(Dest+DestPos,Src[SrcPos],&ps)==-1)
    if (mbrtowc(Dest+DestPos,Src+SrcPos,MB_CUR_MAX,&ps)==-1)
    bool AddVMCode(uint FirstByte,byte *Code,int CodeSize);
bool Unpack::AddVMCode(uint FirstByte,byte *Code,int CodeSize)
    if (VMCodeSize>=0x10000 || VMCodeSize==0)
    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_LZ_MATCH+3 && WriteBorder!=UnpPtr)
    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_LZ_MATCH+3 && WriteBorder!=UnpPtr)
    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_LZ_MATCH+3 && WriteBorder!=UnpPtr)
  if (SrcPtr<MaxWinSize-MAX_LZ_MATCH && UnpPtr<MaxWinSize-MAX_LZ_MATCH)
        // But for real RAR archives Distance <= MaxWinSize - MAX_LZ_MATCH
Fixed code:     CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
#ifdef USE_ARCMEM
#include "arcmem.cpp"
#endif

#ifdef USE_QOPEN
  ProhibitQOpen=false;
#endif

#ifdef USE_QOPEN
#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

bool Archive::Close()
{
#ifdef USE_ARCMEM
  if (ArcMem.Unload())
    return true;
#endif
  return File::Close();
}



#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
#ifdef USE_QOPEN
  if (QOpen.Seek(Offset,Method))
    return;
#endif
#ifdef USE_ARCMEM
  if (ArcMem.Seek(Offset,Method))
    return;
#endif
  File::Seek(Offset,Method);
#ifdef USE_QOPEN
#endif
#ifdef USE_ARCMEM
  int64 APos;
  if (ArcMem.Tell(&APos))
    return APos;
#endif


bool Archive::IsOpened()
{
#ifdef USE_ARCMEM
  if (ArcMem.IsLoaded())
    return true;
#endif
  return File::IsOpened();
};
    bool ProhibitQOpen;
#endif
#ifdef USE_ARCMEM
    ArcMemory ArcMem;
    bool Close();
    bool IsOpened();
#ifdef USE_QOPEN
    void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;}
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;
}
#ifndef _RAR_ARCMEM_
#define _RAR_ARCMEM_

// Memory interface for software fuzzers.

class ArcMemory
{
  private:
    bool Loaded;
    Array<byte> 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
        if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE)
              wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName));
      wcsncpyz(Command,Arg,ASIZE(Command));
//  if (wcslen(FileHead.FileName)>=NM)
//    return 0;
  if (wcschr(L"AFUMD",*Command)==NULL && !ArcInMem)
    // Maximum LZ match length we can encode even for short distances.

    // 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;

    virtual bool Close();
    virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;};
CXXFLAGS=-O2 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else

#ifdef USE_QOPEN

    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

  if (!Loaded)
    // If loading for the first time, perform additional intialization.

    // 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 ||
/*
    byte Buf[10000];
    File Src;
    Src.TOpen(L"123.rar");
    int Size=Src.Read(Buf,sizeof(Buf));
    Cmd->SetArcInMem(Buf,Size);
*/
#ifdef USE_ARCMEM
#include "arcmem.hpp"
#endif
#define USE_ARCMEM
  if (Num==ArcName)
    return false; // Entire volume name is numeric, not possible for REV file.
      if (wcrtomb(Dest+DestPos,Src[SrcPos],&ps)==(size_t)-1)
    size_t res=mbrtowc(Dest+DestPos,Src+SrcPos,MB_CUR_MAX,&ps);
    if (res==(size_t)-1 || res==(size_t)-2)
    bool AddVMCode(uint FirstByte,byte *Code,uint CodeSize);
bool Unpack::AddVMCode(uint FirstByte,byte *Code,uint CodeSize)
    if (VMCodeSize>=0x10000 || VMCodeSize==0 || VMCodeInp.InAddr+VMCodeSize>CodeSize)
    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
    if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
  if (SrcPtr<MaxWinSize-MAX_INC_LZ_MATCH && UnpPtr<MaxWinSize-MAX_INC_LZ_MATCH)
        // But for real RAR archives Distance <= MaxWinSize - MAX_INC_LZ_MATCH