/// <summary> /// Checks the basic validity of the header, assuming the stream is at least the given size. /// Returns false (and the out exception) on failure. /// </summary> public static bool TryValidate(ref ModelHeader header, long size, out Exception ex) { Contracts.Check(size >= 0); try { Contracts.CheckDecode(header.Signature == SignatureValue, "Wrong file type"); Contracts.CheckDecode(header.VerReadable <= header.VerWritten, "Corrupt file header"); Contracts.CheckDecode(header.VerReadable <= VerWrittenCur, "File is too new"); Contracts.CheckDecode(header.VerWritten >= VerWeCanReadBack, "File is too old"); // Currently the model always comes immediately after the header. Contracts.CheckDecode(header.FpModel == Size); Contracts.CheckDecode(header.FpModel + header.CbModel >= header.FpModel); if (header.FpStringTable == 0) { // No strings. Contracts.CheckDecode(header.CbStringTable == 0); Contracts.CheckDecode(header.FpStringChars == 0); Contracts.CheckDecode(header.CbStringChars == 0); if (header.VerWritten < VerAssemblyNameSupported || header.FpAssemblyName == 0) { Contracts.CheckDecode(header.FpTail == header.FpModel + header.CbModel); } } else { // Currently the string table always comes immediately after the model block. Contracts.CheckDecode(header.FpStringTable == header.FpModel + header.CbModel); Contracts.CheckDecode(header.CbStringTable % sizeof(long) == 0); Contracts.CheckDecode(header.CbStringTable / sizeof(long) < int.MaxValue); Contracts.CheckDecode(header.FpStringTable + header.CbStringTable > header.FpStringTable); Contracts.CheckDecode(header.FpStringChars == header.FpStringTable + header.CbStringTable); Contracts.CheckDecode(header.CbStringChars % sizeof(char) == 0); Contracts.CheckDecode(header.FpStringChars + header.CbStringChars >= header.FpStringChars); if (header.VerWritten < VerAssemblyNameSupported || header.FpAssemblyName == 0) { Contracts.CheckDecode(header.FpTail == header.FpStringChars + header.CbStringChars); } } if (header.VerWritten >= VerAssemblyNameSupported) { if (header.FpAssemblyName == 0) { Contracts.CheckDecode(header.CbAssemblyName == 0); } else { // the assembly name always immediately after the string table, if there is one if (header.FpStringTable == 0) { Contracts.CheckDecode(header.FpAssemblyName == header.FpModel + header.CbModel); } else { Contracts.CheckDecode(header.FpAssemblyName == header.FpStringChars + header.CbStringChars); } Contracts.CheckDecode(header.CbAssemblyName % sizeof(char) == 0); Contracts.CheckDecode(header.FpTail == header.FpAssemblyName + header.CbAssemblyName); } } Contracts.CheckDecode(header.FpLim == header.FpTail + sizeof(ulong)); Contracts.CheckDecode(size == 0 || size >= header.FpLim); ex = null; return(true); } catch (Exception e) { ex = e; return(false); } }
/// <summary> /// Checks the validity of the header, reads the string table, etc. /// </summary> public static bool TryValidate(ref ModelHeader header, BinaryReader reader, long fpMin, out string[] strings, out string loaderAssemblyName, out Exception ex) { Contracts.CheckValue(reader, nameof(reader)); Contracts.Check(fpMin >= 0); if (!TryValidate(ref header, reader.BaseStream.Length - fpMin, out ex)) { strings = null; loaderAssemblyName = null; return(false); } try { long fpOrig = reader.FpCur(); StringBuilder sb = null; if (header.FpStringTable == 0) { // No strings. strings = null; if (header.VerWritten < VerAssemblyNameSupported) { // Before VerAssemblyNameSupported, if there were no strings in the model, // validation ended here. Specifically the FpTail checks below were skipped. // There are earlier versions of models that don't have strings, and 'reader' is // not at FpTail at this point. // Preserve the previous behavior by returning early here. loaderAssemblyName = null; ex = null; return(true); } } else { reader.Seek(header.FpStringTable + fpMin); Contracts.Assert(reader.FpCur() == header.FpStringTable + fpMin); long cstr = header.CbStringTable / sizeof(long); Contracts.Assert(cstr < int.MaxValue); long[] offsets = reader.ReadLongArray((int)cstr); Contracts.Assert(header.FpStringChars == reader.FpCur() - fpMin); Contracts.CheckDecode(offsets[cstr - 1] == header.CbStringChars); strings = new string[cstr]; long offset = 0; sb = new StringBuilder(); for (int i = 0; i < offsets.Length; i++) { Contracts.CheckDecode(header.FpStringChars + offset == reader.FpCur() - fpMin); long offsetPrev = offset; offset = offsets[i]; Contracts.CheckDecode(offsetPrev <= offset & offset <= header.CbStringChars); Contracts.CheckDecode(offset % sizeof(char) == 0); long cch = (offset - offsetPrev) / sizeof(char); Contracts.CheckDecode(cch < int.MaxValue); sb.Clear(); for (long ich = 0; ich < cch; ich++) { sb.Append((char)reader.ReadUInt16()); } strings[i] = sb.ToString(); } Contracts.CheckDecode(offset == header.CbStringChars); Contracts.CheckDecode(header.FpStringChars + header.CbStringChars == reader.FpCur() - fpMin); } if (header.VerWritten >= VerAssemblyNameSupported && header.FpAssemblyName != 0) { reader.Seek(header.FpAssemblyName + fpMin); int assemblyNameLength = (int)header.CbAssemblyName / sizeof(char); sb = sb != null?sb.Clear() : new StringBuilder(assemblyNameLength); for (long ich = 0; ich < assemblyNameLength; ich++) { sb.Append((char)reader.ReadUInt16()); } loaderAssemblyName = sb.ToString(); } else { loaderAssemblyName = null; } Contracts.CheckDecode(header.FpTail == reader.FpCur() - fpMin); ulong tail = reader.ReadUInt64(); Contracts.CheckDecode(tail == TailSignatureValue, "Corrupt model file tail"); ex = null; reader.Seek(fpOrig); return(true); } catch (Exception e) { strings = null; loaderAssemblyName = null; ex = e; return(false); } }
/// <summary> /// Finish reading. Checks that the current reader position is the end of the model blob. /// Seeks to the end of the entire model file (after the tail). /// </summary> public static void EndRead(long fpMin, ref ModelHeader header, BinaryReader reader) { Contracts.CheckDecode(header.FpModel + header.CbModel == reader.FpCur() - fpMin); reader.Seek(header.FpLim + fpMin); }