1313#include " NanaZip.Codecs.SevenZipWrapper.h"
1414
1515#include < map>
16+ #include < unordered_set>
17+ #include < deque>
1618
1719#include " Mile.Helpers.Portable.Base.Unstaged.h"
1820
@@ -56,6 +58,11 @@ namespace
5658 // ROMFS cases.
5759 const std::size_t g_RomfsMaximumPathLength = MAX_PATH;
5860
61+ // Limit resource consumption.
62+ const std::size_t g_RomfsMaximumEntries = 10000 ;
63+ // Protect against excessively-deep directory structures.
64+ const std::size_t g_RomfsMaximumVisitDepth = 1000 ;
65+
5966 struct RomfsHeader
6067 {
6168 // The ASCII representation of those bytes (i.e. "-rom1fs-")
@@ -123,6 +130,12 @@ namespace NanaZip::Codecs::Archive
123130 IInStream* m_FileStream = nullptr ;
124131 std::uint32_t m_FullSize = 0 ;
125132 std::string m_VolumeName;
133+
134+ // For GetAllPaths.
135+ std::deque<std::pair<std::uint32_t , std::string>> m_VisitQueue;
136+ // Protect against recursive structures.
137+ std::unordered_set<std::uint32_t > m_VisitedOffsets;
138+
126139 std::map<std::string, RomfsFilePathInformation> m_TemporaryFilePaths;
127140 std::vector<RomfsFilePathInformation> m_FilePaths;
128141 bool m_IsInitialized = false ;
@@ -176,15 +189,28 @@ namespace NanaZip::Codecs::Archive
176189
177190 private:
178191
179- void GetAllPaths (
192+ HRESULT GetOnePath (
180193 std::uint32_t Offset,
181- std::string const & RootPath )
194+ std::string const & Path )
182195 {
183196 while (Offset)
184197 {
185198 RomfsFilePathInformation Information;
186199 Information.Inode = Offset;
187200
201+ if (m_VisitedOffsets.size () >= g_RomfsMaximumEntries)
202+ {
203+ // Too many files, softly bail out.
204+ return S_OK;
205+ }
206+
207+ auto [Iterator, Inserted] = m_VisitedOffsets.insert (Offset);
208+ if (!Inserted)
209+ {
210+ // We have visited recursively. Bail out.
211+ return HRESULT_FROM_WIN32 (ERROR_FILE_CORRUPT);
212+ }
213+
188214 std::uint8_t FileHeaderBuffer[
189215 offsetof (RomfsFileHeader, FileName)] = {};
190216 if (FAILED (this ->ReadFileStream (
@@ -225,11 +251,21 @@ namespace NanaZip::Codecs::Archive
225251 if (" ." != FileName && " .." != FileName)
226252 {
227253 Information.Offset = Offset;
228- Information.Path = RootPath + FileName;
254+ Information.Path = Path + FileName;
229255
230256 if (RomfsFileType::Directory == Information.Type )
231257 {
232- this ->GetAllPaths (Offset, Information.Path + " /" );
258+ if (m_VisitQueue.size () < g_RomfsMaximumVisitDepth)
259+ {
260+ m_VisitQueue.emplace_back (
261+ Offset,
262+ Information.Path + " /" );
263+ }
264+ else
265+ {
266+ // Exceeded our internal limit, bail out.
267+ return HRESULT_FROM_WIN32 (ERROR_FILE_CORRUPT);
268+ }
233269 }
234270 else if (RomfsFileType::HardLink == Information.Type )
235271 {
@@ -244,6 +280,31 @@ namespace NanaZip::Codecs::Archive
244280
245281 Offset = NextOffset;
246282 }
283+
284+ return S_OK;
285+ }
286+
287+ HRESULT GetAllPaths (std::uint32_t RootOffset)
288+ {
289+ m_VisitQueue.clear ();
290+ m_VisitedOffsets.clear ();
291+ m_VisitQueue.emplace_back (RootOffset, " " );
292+
293+ while (!m_VisitQueue.empty ())
294+ {
295+ HRESULT Result;
296+
297+ // Get the entry out before visiting.
298+ auto [Offset, Path] = std::move (m_VisitQueue.front ());
299+ m_VisitQueue.pop_front ();
300+ Result = this ->GetOnePath (Offset, Path);
301+ if (S_OK != Result)
302+ {
303+ return Result;
304+ }
305+ }
306+
307+ return S_OK;
247308 }
248309
249310 public:
@@ -342,7 +403,11 @@ namespace NanaZip::Codecs::Archive
342403 OpenCallback->SetTotal (&TotalFiles, &TotalBytes);
343404 }
344405
345- this ->GetAllPaths (Offset, " " );
406+ hr = this ->GetAllPaths (Offset);
407+ if (FAILED (hr))
408+ {
409+ break ;
410+ }
346411
347412 TotalFiles = this ->m_TemporaryFilePaths .size ();
348413 if (OpenCallback)
0 commit comments