- 264 license table entries with exact download URLs (224/264 resolved) - Complete sources/ directory with all BitBake recipes - Build configuration: tqma6ul-multi-mba6ulx, spaetzle (musl) - Full traceability for Softwarefreigabeantrag - GCC 13.4.0, Linux 6.6.102, U-Boot 2023.04, musl 1.2.4 - License distribution: GPL-2.0 (24), MIT (23), GPL-2.0+ (18), BSD-3 (16)
456 lines
14 KiB
Diff
456 lines
14 KiB
Diff
From 1f266347b154ed90b8262126f04e8cc8f59fa617 Mon Sep 17 00:00:00 2001
|
|
From: Hongxu Jia <hongxu.jia@windriver.com>
|
|
Date: Fri, 22 Nov 2024 21:25:21 +0800
|
|
Subject: [PATCH] Fix two buffer overflow vulnerabilities
|
|
|
|
According to [1][2], Igor Pavlov, the author of 7-Zip, refused to
|
|
provide an advisory or any related change log entries. We have to
|
|
backport a part of ./CPP/7zip/Archive/NtfsHandler.cpp from upstream
|
|
big commit
|
|
|
|
Upstream-Status: Backport [https://github.com/ip7z/7zip/commit/fc662341e6f85da78ada0e443f6116b978f79f22]
|
|
|
|
[1] https://dfir.ru/2024/06/19/vulnerabilities-in-7-zip-and-ntfs3/
|
|
[2] https://dfir.ru/wp-content/uploads/2024/07/screenshot-2024-07-03-at-02-13-40-7-zip-_-bugs-_-2402-two-vulnerabilities-in-the-ntfs-handler.png
|
|
|
|
CVE: CVE-2023-52169
|
|
CVE: CVE-2023-52168
|
|
|
|
Signed-off-by: Hongxu Jia <hongxu.jia@windriver.com>
|
|
---
|
|
CPP/7zip/Archive/NtfsHandler.cpp | 229 ++++++++++++++++++++-----------
|
|
1 file changed, 151 insertions(+), 78 deletions(-)
|
|
|
|
diff --git a/CPP/7zip/Archive/NtfsHandler.cpp b/CPP/7zip/Archive/NtfsHandler.cpp
|
|
index 93e9f88..2701439 100644
|
|
--- a/CPP/7zip/Archive/NtfsHandler.cpp
|
|
+++ b/CPP/7zip/Archive/NtfsHandler.cpp
|
|
@@ -71,8 +71,9 @@ struct CHeader
|
|
{
|
|
unsigned SectorSizeLog;
|
|
unsigned ClusterSizeLog;
|
|
+ unsigned MftRecordSizeLog;
|
|
// Byte MediaType;
|
|
- UInt32 NumHiddenSectors;
|
|
+ //UInt32 NumHiddenSectors;
|
|
UInt64 NumSectors;
|
|
UInt64 NumClusters;
|
|
UInt64 MftCluster;
|
|
@@ -111,30 +112,42 @@ bool CHeader::Parse(const Byte *p)
|
|
if (memcmp(p + 3, "NTFS ", 8) != 0)
|
|
return false;
|
|
{
|
|
- int t = GetLog(Get16(p + 11));
|
|
- if (t < 9 || t > 12)
|
|
- return false;
|
|
- SectorSizeLog = t;
|
|
- t = GetLog(p[13]);
|
|
- if (t < 0)
|
|
- return false;
|
|
- sectorsPerClusterLog = t;
|
|
- ClusterSizeLog = SectorSizeLog + sectorsPerClusterLog;
|
|
- if (ClusterSizeLog > 30)
|
|
- return false;
|
|
+ {
|
|
+ const int t = GetLog(Get16(p + 11));
|
|
+ if (t < 9 || t > 12)
|
|
+ return false;
|
|
+ SectorSizeLog = (unsigned)t;
|
|
+ }
|
|
+ {
|
|
+ const unsigned v = p[13];
|
|
+ if (v <= 0x80)
|
|
+ {
|
|
+ const int t = GetLog(v);
|
|
+ if (t < 0)
|
|
+ return false;
|
|
+ sectorsPerClusterLog = (unsigned)t;
|
|
+ }
|
|
+ else
|
|
+ sectorsPerClusterLog = 0x100 - v;
|
|
+ ClusterSizeLog = SectorSizeLog + sectorsPerClusterLog;
|
|
+ if (ClusterSizeLog > 30)
|
|
+ return false;
|
|
+ }
|
|
}
|
|
|
|
for (int i = 14; i < 21; i++)
|
|
if (p[i] != 0)
|
|
return false;
|
|
|
|
+ // F8 : a hard disk
|
|
+ // F0 : high-density 3.5-inch floppy disk
|
|
if (p[21] != 0xF8) // MediaType = Fixed_Disk
|
|
return false;
|
|
if (Get16(p + 22) != 0) // NumFatSectors
|
|
return false;
|
|
- G16(p + 24, SectorsPerTrack); // 63 usually
|
|
- G16(p + 26, NumHeads); // 255
|
|
- G32(p + 28, NumHiddenSectors); // 63 (XP) / 2048 (Vista and win7) / (0 on media that are not partitioned ?)
|
|
+ // G16(p + 24, SectorsPerTrack); // 63 usually
|
|
+ // G16(p + 26, NumHeads); // 255
|
|
+ // G32(p + 28, NumHiddenSectors); // 63 (XP) / 2048 (Vista and win7) / (0 on media that are not partitioned ?)
|
|
if (Get32(p + 32) != 0) // NumSectors32
|
|
return false;
|
|
|
|
@@ -156,14 +169,47 @@ bool CHeader::Parse(const Byte *p)
|
|
|
|
NumClusters = NumSectors >> sectorsPerClusterLog;
|
|
|
|
- G64(p + 0x30, MftCluster);
|
|
+ G64(p + 0x30, MftCluster); // $MFT.
|
|
// 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);
|
|
+ G64(p + 0x48, SerialNumber); // $MFTMirr
|
|
+
|
|
+ /*
|
|
+ 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
|
|
@@ -235,7 +281,7 @@ struct CFileNameAttr
|
|
bool Parse(const Byte *p, unsigned size);
|
|
};
|
|
|
|
-static void GetString(const Byte *p, unsigned len, UString2 &res)
|
|
+static void GetString(const Byte *p, const unsigned len, UString2 &res)
|
|
{
|
|
if (len == 0 && res.IsEmpty())
|
|
return;
|
|
@@ -266,8 +312,8 @@ bool CFileNameAttr::Parse(const Byte *p, unsigned size)
|
|
G32(p + 0x38, Attrib);
|
|
// G16(p + 0x3C, PackedEaSize);
|
|
NameType = p[0x41];
|
|
- unsigned len = p[0x40];
|
|
- if (0x42 + len > size)
|
|
+ const unsigned len = p[0x40];
|
|
+ if (0x42 + len * 2 > size)
|
|
return false;
|
|
if (len != 0)
|
|
GetString(p + 0x42, len, Name);
|
|
@@ -954,6 +1000,14 @@ struct CDataRef
|
|
static const UInt32 kMagic_FILE = 0x454C4946;
|
|
static const UInt32 kMagic_BAAD = 0x44414142;
|
|
|
|
+// 22.02: we support some rare case magic values:
|
|
+static const UInt32 kMagic_INDX = 0x58444e49;
|
|
+static const UInt32 kMagic_HOLE = 0x454c4f48;
|
|
+static const UInt32 kMagic_RSTR = 0x52545352;
|
|
+static const UInt32 kMagic_RCRD = 0x44524352;
|
|
+static const UInt32 kMagic_CHKD = 0x444b4843;
|
|
+static const UInt32 kMagic_FFFFFFFF = 0xFFFFFFFF;
|
|
+
|
|
struct CMftRec
|
|
{
|
|
UInt32 Magic;
|
|
@@ -1030,6 +1084,26 @@ struct CMftRec
|
|
|
|
bool Parse(Byte *p, unsigned sectorSizeLog, UInt32 numSectors, UInt32 recNumber, CObjectVector<CAttr> *attrs);
|
|
|
|
+ bool Is_Magic_Empty() const
|
|
+ {
|
|
+ // what exact Magic values are possible for empty and unused records?
|
|
+ const UInt32 k_Magic_Unused_MAX = 5; // 22.02
|
|
+ return (Magic <= k_Magic_Unused_MAX);
|
|
+ }
|
|
+ bool Is_Magic_FILE() const { return (Magic == kMagic_FILE); }
|
|
+ // bool Is_Magic_BAAD() const { return (Magic == kMagic_BAAD); }
|
|
+ bool Is_Magic_CanIgnore() const
|
|
+ {
|
|
+ return Is_Magic_Empty()
|
|
+ || Magic == kMagic_BAAD
|
|
+ || Magic == kMagic_INDX
|
|
+ || Magic == kMagic_HOLE
|
|
+ || Magic == kMagic_RSTR
|
|
+ || Magic == kMagic_RCRD
|
|
+ || Magic == kMagic_CHKD
|
|
+ || Magic == kMagic_FFFFFFFF;
|
|
+ }
|
|
+
|
|
bool IsEmpty() const { return (Magic <= 2); }
|
|
bool IsFILE() const { return (Magic == kMagic_FILE); }
|
|
bool IsBAAD() const { return (Magic == kMagic_BAAD); }
|
|
@@ -1141,9 +1215,8 @@ bool CMftRec::Parse(Byte *p, unsigned sectorSizeLog, UInt32 numSectors, UInt32 r
|
|
CObjectVector<CAttr> *attrs)
|
|
{
|
|
G32(p, Magic);
|
|
- if (!IsFILE())
|
|
- return IsEmpty() || IsBAAD();
|
|
-
|
|
+ if (!Is_Magic_FILE())
|
|
+ return Is_Magic_CanIgnore();
|
|
|
|
{
|
|
UInt32 usaOffset;
|
|
@@ -1188,12 +1261,12 @@ bool CMftRec::Parse(Byte *p, unsigned sectorSizeLog, UInt32 numSectors, UInt32 r
|
|
G16(p + 0x10, SeqNumber);
|
|
// G16(p + 0x12, LinkCount);
|
|
// PRF(printf(" L=%d", LinkCount));
|
|
- UInt32 attrOffs = Get16(p + 0x14);
|
|
+ const UInt32 attrOffs = Get16(p + 0x14);
|
|
G16(p + 0x16, Flags);
|
|
PRF(printf(" F=%4X", Flags));
|
|
|
|
- UInt32 bytesInUse = Get32(p + 0x18);
|
|
- UInt32 bytesAlloc = Get32(p + 0x1C);
|
|
+ const UInt32 bytesInUse = Get32(p + 0x18);
|
|
+ const UInt32 bytesAlloc = Get32(p + 0x1C);
|
|
G64(p + 0x20, BaseMftRef.Val);
|
|
if (BaseMftRef.Val != 0)
|
|
{
|
|
@@ -1667,68 +1740,57 @@ 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())
|
|
+ if (!mftRec.Is_Magic_FILE())
|
|
return S_FALSE;
|
|
mftRec.ParseDataNames();
|
|
if (mftRec.DataRefs.IsEmpty())
|
|
return S_FALSE;
|
|
- RINOK(mftRec.GetStream(InStream, 0, Header.ClusterSizeLog, Header.NumClusters, &mftStream));
|
|
+ RINOK(mftRec.GetStream(InStream, 0, Header.ClusterSizeLog, Header.NumClusters, &mftStream))
|
|
if (!mftStream)
|
|
return S_FALSE;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
-
|
|
+
|
|
for (UInt64 pos64 = 0;;)
|
|
{
|
|
if (OpenCallback)
|
|
{
|
|
const UInt64 numFiles = Recs.Size();
|
|
- if ((numFiles & 0x3FF) == 0)
|
|
+ if ((numFiles & 0x3FFF) == 0)
|
|
{
|
|
RINOK(OpenCallback->SetCompleted(&numFiles, &pos64));
|
|
}
|
|
@@ -1817,12 +1879,18 @@ HRESULT CDatabase::Open()
|
|
for (i = 0; i < Recs.Size(); i++)
|
|
{
|
|
CMftRec &rec = Recs[i];
|
|
+ if (!rec.Is_Magic_FILE())
|
|
+ continue;
|
|
+
|
|
if (!rec.BaseMftRef.IsBaseItself())
|
|
{
|
|
- UInt64 refIndex = rec.BaseMftRef.GetIndex();
|
|
- if (refIndex > (UInt32)Recs.Size())
|
|
+ const UInt64 refIndex = rec.BaseMftRef.GetIndex();
|
|
+ if (refIndex >= Recs.Size())
|
|
return S_FALSE;
|
|
CMftRec &refRec = Recs[(unsigned)refIndex];
|
|
+ if (!refRec.Is_Magic_FILE())
|
|
+ continue;
|
|
+
|
|
bool moveAttrs = (refRec.SeqNumber == rec.BaseMftRef.GetNumber() && refRec.BaseMftRef.IsBaseItself());
|
|
if (rec.InUse() && refRec.InUse())
|
|
{
|
|
@@ -1837,12 +1905,17 @@ HRESULT CDatabase::Open()
|
|
}
|
|
|
|
for (i = 0; i < Recs.Size(); i++)
|
|
- Recs[i].ParseDataNames();
|
|
+ {
|
|
+ CMftRec &rec = Recs[i];
|
|
+ if (!rec.Is_Magic_FILE())
|
|
+ continue;
|
|
+ rec.ParseDataNames();
|
|
+ }
|
|
|
|
for (i = 0; i < Recs.Size(); i++)
|
|
{
|
|
CMftRec &rec = Recs[i];
|
|
- if (!rec.IsFILE() || !rec.BaseMftRef.IsBaseItself())
|
|
+ if (!rec.Is_Magic_FILE() || !rec.BaseMftRef.IsBaseItself())
|
|
continue;
|
|
if (i < kNumSysRecs && !_showSystemFiles)
|
|
continue;
|
|
@@ -1864,7 +1937,7 @@ HRESULT CDatabase::Open()
|
|
FOR_VECTOR (di, rec.DataRefs)
|
|
if (rec.DataAttrs[rec.DataRefs[di].Start].Name.IsEmpty())
|
|
{
|
|
- indexOfUnnamedStream = di;
|
|
+ indexOfUnnamedStream = (int)di;
|
|
break;
|
|
}
|
|
}
|
|
@@ -1922,14 +1995,14 @@ HRESULT CDatabase::Open()
|
|
indexOfUnnamedStream);
|
|
|
|
if (rec.MyItemIndex < 0)
|
|
- rec.MyItemIndex = Items.Size();
|
|
- item.ParentHost = Items.Add(item);
|
|
+ rec.MyItemIndex = (int)Items.Size();
|
|
+ item.ParentHost = (int)Items.Add(item);
|
|
|
|
/* we can use that code to reduce the number of alt streams:
|
|
it will not show how alt streams for hard links. */
|
|
// if (!isMainName) continue; isMainName = false;
|
|
|
|
- unsigned numAltStreams = 0;
|
|
+ // unsigned numAltStreams = 0;
|
|
|
|
FOR_VECTOR (di, rec.DataRefs)
|
|
{
|
|
@@ -1947,9 +2020,9 @@ HRESULT CDatabase::Open()
|
|
continue;
|
|
}
|
|
|
|
- numAltStreams++;
|
|
+ // numAltStreams++;
|
|
ThereAreAltStreams = true;
|
|
- item.DataIndex = di;
|
|
+ item.DataIndex = (int)di;
|
|
Items.Add(item);
|
|
}
|
|
}
|
|
@@ -1964,10 +2037,10 @@ HRESULT CDatabase::Open()
|
|
if (attr.Name == L"$SDS")
|
|
{
|
|
CMyComPtr<IInStream> sdsStream;
|
|
- RINOK(rec.GetStream(InStream, di, Header.ClusterSizeLog, Header.NumClusters, &sdsStream));
|
|
+ RINOK(rec.GetStream(InStream, (int)di, Header.ClusterSizeLog, Header.NumClusters, &sdsStream));
|
|
if (sdsStream)
|
|
{
|
|
- UInt64 size64 = attr.GetSize();
|
|
+ const UInt64 size64 = attr.GetSize();
|
|
if (size64 < (UInt32)1 << 29)
|
|
{
|
|
size_t size = (size_t)size64;
|
|
@@ -1997,7 +2070,7 @@ HRESULT CDatabase::Open()
|
|
const CMftRec &rec = Recs[item.RecIndex];
|
|
const CFileNameAttr &fn = rec.FileNames[item.NameIndex];
|
|
const CMftRef &parentDirRef = fn.ParentDirRef;
|
|
- UInt64 refIndex = parentDirRef.GetIndex();
|
|
+ const UInt64 refIndex = parentDirRef.GetIndex();
|
|
if (refIndex == kRecIndex_RootDir)
|
|
item.ParentFolder = -1;
|
|
else
|
|
@@ -2024,17 +2097,17 @@ HRESULT CDatabase::Open()
|
|
unsigned virtIndex = Items.Size();
|
|
if (_showSystemFiles)
|
|
{
|
|
- _systemFolderIndex = virtIndex++;
|
|
+ _systemFolderIndex = (int)(virtIndex++);
|
|
VirtFolderNames.Add(kVirtualFolder_System);
|
|
}
|
|
if (thereAreUnknownFolders_Normal)
|
|
{
|
|
- _lostFolderIndex_Normal = virtIndex++;
|
|
+ _lostFolderIndex_Normal = (int)(virtIndex++);
|
|
VirtFolderNames.Add(kVirtualFolder_Lost_Normal);
|
|
}
|
|
if (thereAreUnknownFolders_Deleted)
|
|
{
|
|
- _lostFolderIndex_Deleted = virtIndex++;
|
|
+ _lostFolderIndex_Deleted = (int)(virtIndex++);
|
|
VirtFolderNames.Add(kVirtualFolder_Lost_Deleted);
|
|
}
|
|
|
|
--
|
|
2.34.1
|
|
|