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;