File CVE-2023-52168.patch of Package p7zip.34730

Index: b/CPP/7zip/Archive/NtfsHandler.cpp
===================================================================
--- a/CPP/7zip/Archive/NtfsHandler.cpp
+++ b/CPP/7zip/Archive/NtfsHandler.cpp
@@ -71,6 +71,7 @@ struct CHeader
 {
   unsigned SectorSizeLog;
   unsigned ClusterSizeLog;
+  unsigned MftRecordSizeLog;
   // Byte MediaType;
   UInt32 NumHiddenSectors;
   UInt64 NumSectors;
@@ -159,11 +160,44 @@ bool CHeader::Parse(const Byte *p)
   G64(p + 0x30, MftCluster);
   // G64(p + 0x38, Mft2Cluster);
   G64(p + 0x48, SerialNumber);
-  UInt32 numClustersInMftRec;
-  UInt32 numClustersInIndexBlock;
-  G32(p + 0x40, numClustersInMftRec); // -10 means 2 ^10 = 1024 bytes.
-  G32(p + 0x44, numClustersInIndexBlock);
-  return (numClustersInMftRec < 256 && numClustersInIndexBlock < 256);
+
+  /*
+    numClusters_per_MftRecord:
+    numClusters_per_IndexBlock:
+    only low byte from 4 bytes is used. Another 3 high bytes are zeros.
+      If the number is positive (number < 0x80),
+          then it represents the number of clusters.
+      If the number is negative (number >= 0x80),
+          then the size of the file record is 2 raised to the absolute value of this number.
+          example: (0xF6 == -10) means 2^10 = 1024 bytes.
+  */
+  {
+    UInt32 numClusters_per_MftRecord;
+    G32(p + 0x40, numClusters_per_MftRecord);
+    if (numClusters_per_MftRecord >= 0x100 || numClusters_per_MftRecord == 0)
+      return false;
+    if (numClusters_per_MftRecord < 0x80)
+    {
+      const int t = GetLog(numClusters_per_MftRecord);
+      if (t < 0)
+        return false;
+      MftRecordSizeLog = (unsigned)t + ClusterSizeLog;
+    }
+    else
+      MftRecordSizeLog = 0x100 - numClusters_per_MftRecord;
+    // what exact MFT record sizes are possible and supported by Windows?
+    // do we need to change this limit here?
+    const unsigned k_MftRecordSizeLog_MAX = 12;
+    if (MftRecordSizeLog > k_MftRecordSizeLog_MAX)
+      return false;
+    if (MftRecordSizeLog < SectorSizeLog)
+      return false;
+  }
+  {
+    UInt32 numClusters_per_IndexBlock;
+    G32(p + 0x44, numClusters_per_IndexBlock);
+    return (numClusters_per_IndexBlock < 0x100);
+  }
 }
 
 struct CMftRef
@@ -1314,7 +1348,6 @@ struct CDatabase
   CObjectVector<CMftRec> Recs;
   CMyComPtr<IInStream> InStream;
   CHeader Header;
-  unsigned RecSizeLog;
   UInt64 PhySize;
 
   IArchiveOpenCallback *OpenCallback;
@@ -1667,26 +1700,22 @@ HRESULT CDatabase::Open()
  
   SeekToCluster(Header.MftCluster);
 
-  CMftRec mftRec;
-  UInt32 numSectorsInRec;
-
+  // we use ByteBuf for records reading.
+  // so the size of ByteBuf must be >= mftRecordSize
+  const size_t recSize = (size_t)1 << Header.MftRecordSizeLog;
+  const size_t kBufSize = MyMax((size_t)(1 << 15), recSize);
+  ByteBuf.Alloc(kBufSize);
+  RINOK(ReadStream_FALSE(InStream, ByteBuf, recSize))
+  {
+    const UInt32 allocSize = Get32(ByteBuf + 0x1C);
+    if (allocSize != recSize)
+      return S_FALSE;
+  }
+  // MftRecordSizeLog >= SectorSizeLog
+  const UInt32 numSectorsInRec = 1u << (Header.MftRecordSizeLog - Header.SectorSizeLog);
   CMyComPtr<IInStream> mftStream;
+  CMftRec mftRec;
   {
-    UInt32 blockSize = 1 << 12;
-    ByteBuf.Alloc(blockSize);
-    RINOK(ReadStream_FALSE(InStream, ByteBuf, blockSize));
-    
-    {
-      UInt32 allocSize = Get32(ByteBuf + 0x1C);
-      int t = GetLog(allocSize);
-      if (t < (int)Header.SectorSizeLog)
-        return S_FALSE;
-      RecSizeLog = t;
-      if (RecSizeLog > 15)
-        return S_FALSE;
-    }
-
-    numSectorsInRec = 1 << (RecSizeLog - Header.SectorSizeLog);
     if (!mftRec.Parse(ByteBuf, Header.SectorSizeLog, numSectorsInRec, 0, NULL))
       return S_FALSE;
     if (!mftRec.IsFILE())
@@ -1701,25 +1730,18 @@ HRESULT CDatabase::Open()
 
   // CObjectVector<CAttr> SecurityAttrs;
 
-  UInt64 mftSize = mftRec.DataAttrs[0].Size;
+  const UInt64 mftSize = mftRec.DataAttrs[0].Size;
   if ((mftSize >> 4) > Header.GetPhySize_Clusters())
     return S_FALSE;
 
-  const size_t kBufSize = (1 << 15);
-  const size_t recSize = ((size_t)1 << RecSizeLog);
-  if (kBufSize < recSize)
-    return S_FALSE;
-
   {
-    const UInt64 numFiles = mftSize >> RecSizeLog;
+    const UInt64 numFiles = mftSize >> Header.MftRecordSizeLog;
     if (numFiles > (1 << 30))
       return S_FALSE;
     if (OpenCallback)
     {
       RINOK(OpenCallback->SetTotal(&numFiles, &mftSize));
     }
-    
-    ByteBuf.Alloc(kBufSize);
     Recs.ClearAndReserve((unsigned)numFiles);
   }
   
@@ -2363,7 +2385,7 @@ STDMETHODIMP CHandler::GetArchivePropert
       break;
     }
     case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break;
-    case kpidRecordSize: prop = (UInt32)1 << RecSizeLog; break;
+    case kpidRecordSize: prop = (UInt32)1 << Header.MftRecordSizeLog; break;
     case kpidId: prop = Header.SerialNumber; break;
 
     case kpidIsTree: prop = true; break;
openSUSE Build Service is sponsored by