/// <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); } }
protected LoadableClassAttributeBase(string summary, Type instType, Type loaderType, Type argType, Type[] sigTypes, string userName, params string[] loadNames) { Contracts.CheckValueOrNull(summary); Contracts.CheckValue(instType, nameof(instType)); Contracts.CheckValue(loaderType, nameof(loaderType)); Contracts.CheckNonEmpty(sigTypes, nameof(sigTypes)); if (Utils.Size(loadNames) == 0) { loadNames = new string[] { userName } } ; if (loadNames.Any(s => string.IsNullOrWhiteSpace(s))) { throw Contracts.ExceptEmpty(nameof(loadNames), "LoadableClass loadName parameter can't be empty"); } var sigType = sigTypes[0]; Contracts.CheckValue(sigType, nameof(sigTypes)); Type[] types; Contracts.CheckParam(sigType.BaseType == typeof(System.MulticastDelegate), nameof(sigTypes), "LoadableClass signature type must be a delegate type"); var meth = sigType.GetMethod("Invoke"); Contracts.CheckParam(meth != null, nameof(sigTypes), "LoadableClass signature type must be a delegate type"); Contracts.CheckParam(meth.ReturnType == typeof(void), nameof(sigTypes), "LoadableClass signature type must be a delegate type with void return"); var parms = meth.GetParameters(); int itypeBase = 0; if (argType != null) { types = new Type[1 + parms.Length]; types[itypeBase++] = argType; } else if (parms.Length > 0) { types = new Type[parms.Length]; } else { types = Type.EmptyTypes; } for (int itype = 0; itype < parms.Length; itype++) { var parm = parms[itype]; if ((parm.Attributes & (ParameterAttributes.Out | ParameterAttributes.Retval)) != 0) { throw Contracts.Except("Invalid signature parameter attributes"); } types[itypeBase + itype] = parm.ParameterType; } for (int i = 1; i < sigTypes.Length; i++) { sigType = sigTypes[i]; Contracts.CheckValue(sigType, nameof(sigTypes)); Contracts.Check(sigType.BaseType == typeof(System.MulticastDelegate), "LoadableClass signature type must be a delegate type"); meth = sigType.GetMethod("Invoke"); Contracts.CheckParam(meth != null, nameof(sigTypes), "LoadableClass signature type must be a delegate type"); Contracts.CheckParam(meth.ReturnType == typeof(void), nameof(sigTypes), "LoadableClass signature type must be a delegate type with void return"); parms = meth.GetParameters(); Contracts.CheckParam(parms.Length + itypeBase == types.Length, nameof(sigTypes), "LoadableClass signatures must have the same number of parameters"); for (int itype = 0; itype < parms.Length; itype++) { var parm = parms[itype]; if ((parm.Attributes & (ParameterAttributes.Out | ParameterAttributes.Retval)) != 0) { throw Contracts.ExceptParam(nameof(sigTypes), "Invalid signature parameter attributes"); } Contracts.CheckParam(types[itypeBase + itype] == parm.ParameterType, nameof(sigTypes), "LoadableClass signatures must have the same set of parameters"); } } InstanceType = instType; LoaderType = loaderType; ArgType = argType; SigTypes = sigTypes; CtorTypes = types; Summary = summary; UserName = userName; LoadNames = loadNames; } }