Skip to content

Commit 6eb0ab5

Browse files
Avoid attempting to parse future versions
1 parent 88f8c2f commit 6eb0ab5

2 files changed

Lines changed: 86 additions & 15 deletions

File tree

Analyzer.Tests/FileDetectionTests.cs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,35 @@ public void TryDetectSerializedFile_NonExistentFile_ReturnsFalse()
171171

172172
#region SerializedFile Metadata Parsing Tests
173173

174+
[Test]
175+
public void TryParseMetadata_VersionTooOld_ReturnsFalseWithMessage()
176+
{
177+
var headerInfo = new SerializedFileInfo { Version = 18 };
178+
179+
bool result = SerializedFileDetector.TryParseMetadata("irrelevant", headerInfo, out var metadata, out var errorMessage);
180+
181+
Assert.IsFalse(result);
182+
Assert.IsNull(metadata);
183+
Assert.IsNotNull(errorMessage);
184+
Assert.That(errorMessage, Does.Contain("18"), "Error should mention the actual version");
185+
Assert.That(errorMessage, Does.Contain("19"), "Error should mention the minimum supported version");
186+
}
187+
188+
[Test]
189+
public void TryParseMetadata_VersionTooNew_ReturnsFalseWithMessage()
190+
{
191+
var headerInfo = new SerializedFileInfo { Version = 24 };
192+
193+
bool result = SerializedFileDetector.TryParseMetadata("irrelevant", headerInfo, out var metadata, out var errorMessage);
194+
195+
Assert.IsFalse(result);
196+
Assert.IsNull(metadata);
197+
Assert.IsNotNull(errorMessage);
198+
Assert.That(errorMessage, Does.Contain("24"), "Error should mention the actual version");
199+
Assert.That(errorMessage, Does.Contain("23"), "Error should mention the maximum supported version");
200+
Assert.That(errorMessage, Does.Contain("UnityDataTool"), "Error should mention UnityDataTool");
201+
}
202+
174203
[Test]
175204
public void TryParseMetadata_PlayerDataLevel0_ReturnsExpectedValues()
176205
{
@@ -211,7 +240,7 @@ public void TryParseMetadata_PlayerDataLevel0_ReturnsExpectedValues()
211240
$"TypeTreeSerializedSize should be non-zero (persistentTypeID={entry.PersistentTypeID})");
212241
Assert.Greater(entry.PersistentTypeID, 0,
213242
$"PersistentTypeID should be positive for native types (got {entry.PersistentTypeID})");
214-
Assert.AreNotEqual(114, entry.PersistentTypeID,
243+
Assert.That(entry.PersistentTypeID, Is.Not.EqualTo(114),
215244
"No MonoBehaviour types expected in this scene");
216245
Assert.That(entry.ScriptTypeIndex, Is.EqualTo((short)-1),
217246
$"ScriptTypeIndex should be -1 for native types (persistentTypeID={entry.PersistentTypeID})");
@@ -344,6 +373,43 @@ public void TryParseMetadata_V22PrefabWithSerializedReference_ReturnsExpectedTyp
344373
Assert.That(refType.AssemblyName, Is.EqualTo("Assembly-CSharp"));
345374
}
346375

376+
[Test]
377+
public void TryParseMetadata_V23ExtractedMonoscriptBundle_ReturnsExpectedTypeTreeData()
378+
{
379+
// This is a v23 (kExtractedTypeTreeSupport) file where the TypeTree blobs have been
380+
// extracted to a shared external store. The metadata records a non-zero TypeTreeContentHash
381+
// as a cache key, but typeTreeSerializedSize == 0 and InlineTypeTree == false for every entry.
382+
var testFile = Path.Combine(m_TestDataPath, "AssetBundleTypeTreeVariations", "v23_extracted",
383+
"monoscriptbundle.serializedfile");
384+
385+
bool headerResult = SerializedFileDetector.TryDetectSerializedFile(testFile, out var headerInfo);
386+
Assert.IsTrue(headerResult, "File should be detected as a valid SerializedFile");
387+
388+
bool result = SerializedFileDetector.TryParseMetadata(testFile, headerInfo, out var metadata, out var errorMessage);
389+
Assert.IsTrue(result, $"Metadata parsing should succeed. Error: {errorMessage}");
390+
Assert.IsNotNull(metadata);
391+
392+
// --- Initial metadata fields ---
393+
Assert.IsTrue(metadata.EnableTypeTree, "EnableTypeTree should be true");
394+
395+
// --- Type counts ---
396+
Assert.That(metadata.TypeTreeCount, Is.EqualTo(2), "Should have 2 regular type entries");
397+
Assert.That(metadata.SerializedReferenceTypeTreeCount, Is.EqualTo(0), "Should have 0 SerializeReference type entries");
398+
Assert.IsNotNull(metadata.TypeTrees, "TypeTrees array should be populated");
399+
Assert.That(metadata.TypeTrees.Length, Is.EqualTo(2));
400+
401+
// --- All TypeTree blobs are extracted: non-zero content hash, zero size, not inline ---
402+
foreach (var entry in metadata.TypeTrees)
403+
{
404+
Assert.IsFalse(entry.TypeTreeContentHash.IsZero,
405+
$"TypeTreeContentHash should be non-zero for extracted v23 entry (persistentTypeID={entry.PersistentTypeID})");
406+
Assert.That(entry.TypeTreeSerializedSize, Is.EqualTo(0u),
407+
$"TypeTreeSerializedSize should be 0 for extracted entry (persistentTypeID={entry.PersistentTypeID})");
408+
Assert.IsFalse(entry.InlineTypeTree,
409+
$"InlineTypeTree should be false for extracted entry (persistentTypeID={entry.PersistentTypeID})");
410+
}
411+
}
412+
347413
#endregion
348414

349415
#region YAML SerializedFile Detection Tests

Analyzer/Util/SerializedFileDetector.cs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ public static class SerializedFileDetector
211211
// Older files have format differences that we do not attempt to support.
212212
private const uint MinMetadataParseVersion = 19;
213213

214+
// Maximum version for metadata section parsing (kExtractedTypeTreeSupport = 23, Unity 6000.4).
215+
// Files newer than this version may have an unknown format and cannot be parsed safely.
216+
private const uint MaxMetadataParseVersion = 23;
217+
214218
// Reasonable version range for SerializedFiles
215219
// Unity versions currently use values in the 20s-30s range
216220
private const uint MinVersion = 1;
@@ -485,6 +489,17 @@ public static bool TryParseMetadata(string filePath, SerializedFileInfo headerIn
485489
return false;
486490
}
487491

492+
// Reject versions beyond the highest known format. Future Unity versions may change the
493+
// metadata layout in ways that would cause incorrect results or a parse failure.
494+
// A newer version of UnityDataTool is required to read these files.
495+
if (headerInfo.Version > MaxMetadataParseVersion)
496+
{
497+
errorMessage = $"SerializedFile version {headerInfo.Version} is not supported. " +
498+
$"UnityDataTool supports up to version {MaxMetadataParseVersion}. " +
499+
$"Please use a newer version of UnityDataTool to read this file.";
500+
return false;
501+
}
502+
488503
try
489504
{
490505
long metadataOffset = headerInfo.IsLegacyFormat ? LegacyHeaderSize : ModernHeaderSize;
@@ -518,9 +533,9 @@ public static bool TryParseMetadata(string filePath, SerializedFileInfo headerIn
518533
EnableTypeTree = enableTypeTree,
519534
};
520535

521-
// Parse the TypeTree section. Protected by its own try/catch so that any
536+
// Parse the rest of the metadata section. Protected by its own try/catch so that any
522537
// failure there still returns a partially-populated metadata struct.
523-
ParseTypeTreeMetadata(reader, headerInfo, swap, metadataOffset, metadata);
538+
ParseExtendedMetadata(reader, headerInfo, swap, metadataOffset, metadata);
524539

525540
return true;
526541
}
@@ -532,19 +547,9 @@ public static bool TryParseMetadata(string filePath, SerializedFileInfo headerIn
532547
}
533548

534549
/// <summary>
535-
/// Parses the TypeTree section of the metadata, populating the type-list fields of
536-
/// <paramref name="metadata"/>. Any parse failure is silently caught so the caller
537-
/// always receives at least the three initial metadata fields.
538-
///
539-
/// Layout after the three initial fields:
540-
/// [int32 typeCount]
541-
/// [SerializedType * typeCount] -- regular object types (m_Types)
542-
///
543-
/// Then, after the object list, script type list, and externals list, for version >= 20:
544-
/// [int32 refTypeCount]
545-
/// [RefSerializedType * refTypeCount] -- SerializeReference types (m_RefTypes)
550+
/// Parses the TypeTree and other arrays that are stored in the metadata,
546551
/// </summary>
547-
private static void ParseTypeTreeMetadata(BinaryReader reader, SerializedFileInfo headerInfo,
552+
private static void ParseExtendedMetadata(BinaryReader reader, SerializedFileInfo headerInfo,
548553
bool swap, long metadataOffset, SerializedFileMetadata metadata)
549554
{
550555
try

0 commit comments

Comments
 (0)