From 211aa62129788b10c94fbce8e39168050ea83254 Mon Sep 17 00:00:00 2001 From: Softing Date: Fri, 7 Jul 2017 16:56:23 +0300 Subject: [PATCH 1/6] Limit the maximum nesting level foe Encoders and Decoders --- .../Types/Encoders/BinaryDecoder.cs | 1128 +++++++++-------- .../Types/Encoders/BinaryEncoder.cs | 341 ++--- .../Opc.Ua.Core/Types/Encoders/JsonDecoder.cs | 39 +- .../Opc.Ua.Core/Types/Encoders/JsonEncoder.cs | 43 +- .../Opc.Ua.Core/Types/Encoders/XmlDecoder.cs | 61 +- .../Opc.Ua.Core/Types/Encoders/XmlEncoder.cs | 60 +- .../Types/Utils/ServiceMessageContext.cs | 39 +- 7 files changed, 994 insertions(+), 717 deletions(-) diff --git a/Stack/Opc.Ua.Core/Types/Encoders/BinaryDecoder.cs b/Stack/Opc.Ua.Core/Types/Encoders/BinaryDecoder.cs index 0c450df7ae..757c9b6c76 100644 --- a/Stack/Opc.Ua.Core/Types/Encoders/BinaryDecoder.cs +++ b/Stack/Opc.Ua.Core/Types/Encoders/BinaryDecoder.cs @@ -40,6 +40,7 @@ namespace Opc.Ua m_istrm = new MemoryStream(buffer, start, count, false); m_reader = new BinaryReader(m_istrm); m_context = context; + m_nestingLevel = 0; } /// @@ -52,6 +53,7 @@ namespace Opc.Ua m_istrm = stream; m_reader = new BinaryReader(m_istrm); m_context = context; + m_nestingLevel = 0; } #endregion @@ -577,6 +579,17 @@ namespace Opc.Ua /// public DiagnosticInfo ReadDiagnosticInfo(string fieldName) { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + // read the encoding byte. byte encodingByte = m_reader.ReadByte(); @@ -618,6 +631,8 @@ namespace Opc.Ua value.InnerDiagnosticInfo = ReadDiagnosticInfo(null); } + m_nestingLevel--; + return value; } @@ -665,551 +680,22 @@ namespace Opc.Ua /// /// Reads an Variant from the stream. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] public Variant ReadVariant(string fieldName) { - // read the encoding byte. - byte encodingByte = m_reader.ReadByte(); - - Variant value = new Variant(); - - if ((encodingByte & (byte)VariantArrayEncodingBits.Array) != 0) + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) { - // read the array length. - int length = m_reader.ReadInt32(); - - if (length < 0) - { - return value; - } - - Array array = null; - - BuiltInType builtInType = (BuiltInType)(encodingByte & (byte)VariantArrayEncodingBits.TypeMask); - - switch (builtInType) - { - case BuiltInType.Boolean: - { - bool[] values = new bool[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadBoolean(null); - } - - array = values; - break; - } - - case BuiltInType.SByte: - { - sbyte[] values = new sbyte[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadSByte(null); - } - - array = values; - break; - } - - case BuiltInType.Byte: - { - byte[] values = new byte[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadByte(null); - } - - array = values; - break; - } - - case BuiltInType.Int16: - { - short[] values = new short[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadInt16(null); - } - - array = values; - break; - } - - case BuiltInType.UInt16: - { - ushort[] values = new ushort[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadUInt16(null); - } - - array = values; - break; - } - - case BuiltInType.Int32: - case BuiltInType.Enumeration: - { - int[] values = new int[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadInt32(null); - } - - array = values; - break; - } - - case BuiltInType.UInt32: - { - uint[] values = new uint[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadUInt32(null); - } - - array = values; - break; - } - - case BuiltInType.Int64: - { - long[] values = new long[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadInt64(null); - } - - array = values; - break; - } - - case BuiltInType.UInt64: - { - ulong[] values = new ulong[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadUInt64(null); - } - - array = values; - break; - } - - case BuiltInType.Float: - { - float[] values = new float[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadFloat(null); - } - - array = values; - break; - } - - case BuiltInType.Double: - { - double[] values = new double[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadDouble(null); - } - - array = values; - break; - } - - case BuiltInType.String: - { - string[] values = new string[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadString(null); - } - - array = values; - break; - } - - case BuiltInType.DateTime: - { - DateTime[] values = new DateTime[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadDateTime(null); - } - - array = values; - break; - } - - case BuiltInType.Guid: - { - Uuid[] values = new Uuid[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadGuid(null); - } - - array = values; - break; - } - - case BuiltInType.ByteString: - { - byte[][] values = new byte[length][]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadByteString(null); - } - - array = values; - break; - } - - case BuiltInType.XmlElement: - { - try - { - XmlElement[] values = new XmlElement[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadXmlElement(null); - } - - array = values; - } - catch (Exception ex) - { - Utils.Trace(ex, "Error reading variant."); - } - - break; - } - - case BuiltInType.NodeId: - { - NodeId[] values = new NodeId[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadNodeId(null); - } - - array = values; - break; - } - - case BuiltInType.ExpandedNodeId: - { - ExpandedNodeId[] values = new ExpandedNodeId[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadExpandedNodeId(null); - } - - array = values; - break; - } - - case BuiltInType.StatusCode: - { - StatusCode[] values = new StatusCode[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadStatusCode(null); - } - - array = values; - break; - } - - case BuiltInType.QualifiedName: - { - QualifiedName[] values = new QualifiedName[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadQualifiedName(null); - } - - array = values; - break; - } - - case BuiltInType.LocalizedText: - { - LocalizedText[] values = new LocalizedText[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadLocalizedText(null); - } - - array = values; - break; - } - - case BuiltInType.ExtensionObject: - { - ExtensionObject[] values = new ExtensionObject[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadExtensionObject(); - } - - array = values; - break; - } - - case BuiltInType.DataValue: - { - DataValue[] values = new DataValue[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadDataValue(null); - } - - array = values; - break; - } - - case BuiltInType.Variant: - { - Variant[] values = new Variant[length]; - - for (int ii = 0; ii < values.Length; ii++) - { - values[ii] = ReadVariant(null); - } - - array = values; - break; - } - - default: - { - throw new ServiceResultException( - StatusCodes.BadDecodingError, - Utils.Format("Cannot decode unknown type in Variant object (0x{0:X2}).", encodingByte)); - } - } - - if (array == null) - { - value = new Variant(StatusCodes.BadEncodingError); - } - else - { - // check for multi-dimensional arrays. - if ((encodingByte & (byte)VariantArrayEncodingBits.ArrayDimensions) != 0) - { - Int32Collection dimensions = ReadInt32Array(null); - - if (dimensions != null && dimensions.Count > 0) - { - value = new Variant(new Matrix(array, builtInType, dimensions.ToArray())); - } - else - { - value = new Variant(new Matrix(array, builtInType)); - } - } - else - { - value = new Variant(array); - } - } + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); } - else - { - switch ((BuiltInType)encodingByte) - { - case BuiltInType.Null: - { - value.Value = null; - break; - } + m_nestingLevel++; - case BuiltInType.Boolean: - { - value.Set(ReadBoolean(null)); - break; - } + Variant value = ReadVariantValue(fieldName); - case BuiltInType.SByte: - { - value.Set(ReadSByte(null)); - break; - } - - case BuiltInType.Byte: - { - value.Set(ReadByte(null)); - break; - } - - case BuiltInType.Int16: - { - value.Set(ReadInt16(null)); - break; - } - - case BuiltInType.UInt16: - { - value.Set(ReadUInt16(null)); - break; - } - - case BuiltInType.Int32: - case BuiltInType.Enumeration: - { - value.Set(ReadInt32(null)); - break; - } - - case BuiltInType.UInt32: - { - value.Set(ReadUInt32(null)); - break; - } - - case BuiltInType.Int64: - { - value.Set(ReadInt64(null)); - break; - } - - case BuiltInType.UInt64: - { - value.Set(ReadUInt64(null)); - break; - } - - case BuiltInType.Float: - { - value.Set(ReadFloat(null)); - break; - } - - case BuiltInType.Double: - { - value.Set(ReadDouble(null)); - break; - } - - case BuiltInType.String: - { - value.Set(ReadString(null)); - break; - } - - case BuiltInType.DateTime: - { - value.Set(ReadDateTime(null)); - break; - } - - case BuiltInType.Guid: - { - value.Set(ReadGuid(null)); - break; - } - - case BuiltInType.ByteString: - { - value.Set(ReadByteString(null)); - break; - } - - case BuiltInType.XmlElement: - { - try - { - value.Set(ReadXmlElement(null)); - } - catch (Exception ex) - { - Utils.Trace(ex, "Error reading xml element for variant."); - value.Set(StatusCodes.BadEncodingError); - } - break; - } - - case BuiltInType.NodeId: - { - value.Set(ReadNodeId(null)); - break; - } - - case BuiltInType.ExpandedNodeId: - { - value.Set(ReadExpandedNodeId(null)); - break; - } - - case BuiltInType.StatusCode: - { - value.Set(ReadStatusCode(null)); - break; - } - - case BuiltInType.QualifiedName: - { - value.Set(ReadQualifiedName(null)); - break; - } - - case BuiltInType.LocalizedText: - { - value.Set(ReadLocalizedText(null)); - break; - } - - case BuiltInType.ExtensionObject: - { - value.Set(ReadExtensionObject()); - break; - } - - case BuiltInType.DataValue: - { - value.Set(ReadDataValue(null)); - break; - } - - default: - { - throw new ServiceResultException( - StatusCodes.BadDecodingError, - Utils.Format("Cannot decode unknown type in Variant object (0x{0:X2}).", encodingByte)); - } - } - } + m_nestingLevel--; return value; } @@ -1282,8 +768,21 @@ namespace Opc.Ua Utils.Format("Cannot decode type '{0}'.", systemType.FullName)); } + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + encodeable.Decode(this); + m_nestingLevel--; + return encodeable; } @@ -2088,6 +1587,558 @@ namespace Opc.Ua extension.Body = encodeable; return extension; } + + /// + /// Reads an Variant from the stream. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private Variant ReadVariantValue(string fieldName) + { + // read the encoding byte. + byte encodingByte = m_reader.ReadByte(); + + Variant value = new Variant(); + + if ((encodingByte & (byte)VariantArrayEncodingBits.Array) != 0) + { + // read the array length. + int length = m_reader.ReadInt32(); + + if (length < 0) + { + return value; + } + + Array array = null; + + BuiltInType builtInType = (BuiltInType)(encodingByte & (byte)VariantArrayEncodingBits.TypeMask); + + switch (builtInType) + { + case BuiltInType.Boolean: + { + bool[] values = new bool[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadBoolean(null); + } + + array = values; + break; + } + + case BuiltInType.SByte: + { + sbyte[] values = new sbyte[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadSByte(null); + } + + array = values; + break; + } + + case BuiltInType.Byte: + { + byte[] values = new byte[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadByte(null); + } + + array = values; + break; + } + + case BuiltInType.Int16: + { + short[] values = new short[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadInt16(null); + } + + array = values; + break; + } + + case BuiltInType.UInt16: + { + ushort[] values = new ushort[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadUInt16(null); + } + + array = values; + break; + } + + case BuiltInType.Int32: + case BuiltInType.Enumeration: + { + int[] values = new int[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadInt32(null); + } + + array = values; + break; + } + + case BuiltInType.UInt32: + { + uint[] values = new uint[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadUInt32(null); + } + + array = values; + break; + } + + case BuiltInType.Int64: + { + long[] values = new long[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadInt64(null); + } + + array = values; + break; + } + + case BuiltInType.UInt64: + { + ulong[] values = new ulong[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadUInt64(null); + } + + array = values; + break; + } + + case BuiltInType.Float: + { + float[] values = new float[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadFloat(null); + } + + array = values; + break; + } + + case BuiltInType.Double: + { + double[] values = new double[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadDouble(null); + } + + array = values; + break; + } + + case BuiltInType.String: + { + string[] values = new string[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadString(null); + } + + array = values; + break; + } + + case BuiltInType.DateTime: + { + DateTime[] values = new DateTime[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadDateTime(null); + } + + array = values; + break; + } + + case BuiltInType.Guid: + { + Uuid[] values = new Uuid[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadGuid(null); + } + + array = values; + break; + } + + case BuiltInType.ByteString: + { + byte[][] values = new byte[length][]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadByteString(null); + } + + array = values; + break; + } + + case BuiltInType.XmlElement: + { + try + { + XmlElement[] values = new XmlElement[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadXmlElement(null); + } + + array = values; + } + catch (Exception ex) + { + Utils.Trace(ex, "Error reading variant."); + } + + break; + } + + case BuiltInType.NodeId: + { + NodeId[] values = new NodeId[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadNodeId(null); + } + + array = values; + break; + } + + case BuiltInType.ExpandedNodeId: + { + ExpandedNodeId[] values = new ExpandedNodeId[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadExpandedNodeId(null); + } + + array = values; + break; + } + + case BuiltInType.StatusCode: + { + StatusCode[] values = new StatusCode[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadStatusCode(null); + } + + array = values; + break; + } + + case BuiltInType.QualifiedName: + { + QualifiedName[] values = new QualifiedName[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadQualifiedName(null); + } + + array = values; + break; + } + + case BuiltInType.LocalizedText: + { + LocalizedText[] values = new LocalizedText[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadLocalizedText(null); + } + + array = values; + break; + } + + case BuiltInType.ExtensionObject: + { + ExtensionObject[] values = new ExtensionObject[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadExtensionObject(); + } + + array = values; + break; + } + + case BuiltInType.DataValue: + { + DataValue[] values = new DataValue[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadDataValue(null); + } + + array = values; + break; + } + + case BuiltInType.Variant: + { + Variant[] values = new Variant[length]; + + for (int ii = 0; ii < values.Length; ii++) + { + values[ii] = ReadVariant(null); + } + + array = values; + break; + } + + default: + { + throw new ServiceResultException( + StatusCodes.BadDecodingError, + Utils.Format("Cannot decode unknown type in Variant object (0x{0:X2}).", encodingByte)); + } + } + + if (array == null) + { + value = new Variant(StatusCodes.BadEncodingError); + } + else + { + // check for multi-dimensional arrays. + if ((encodingByte & (byte)VariantArrayEncodingBits.ArrayDimensions) != 0) + { + Int32Collection dimensions = ReadInt32Array(null); + + if (dimensions != null && dimensions.Count > 0) + { + value = new Variant(new Matrix(array, builtInType, dimensions.ToArray())); + } + else + { + value = new Variant(new Matrix(array, builtInType)); + } + } + else + { + value = new Variant(array); + } + } + } + + else + { + switch ((BuiltInType)encodingByte) + { + case BuiltInType.Null: + { + value.Value = null; + break; + } + + case BuiltInType.Boolean: + { + value.Set(ReadBoolean(null)); + break; + } + + case BuiltInType.SByte: + { + value.Set(ReadSByte(null)); + break; + } + + case BuiltInType.Byte: + { + value.Set(ReadByte(null)); + break; + } + + case BuiltInType.Int16: + { + value.Set(ReadInt16(null)); + break; + } + + case BuiltInType.UInt16: + { + value.Set(ReadUInt16(null)); + break; + } + + case BuiltInType.Int32: + case BuiltInType.Enumeration: + { + value.Set(ReadInt32(null)); + break; + } + + case BuiltInType.UInt32: + { + value.Set(ReadUInt32(null)); + break; + } + + case BuiltInType.Int64: + { + value.Set(ReadInt64(null)); + break; + } + + case BuiltInType.UInt64: + { + value.Set(ReadUInt64(null)); + break; + } + + case BuiltInType.Float: + { + value.Set(ReadFloat(null)); + break; + } + + case BuiltInType.Double: + { + value.Set(ReadDouble(null)); + break; + } + + case BuiltInType.String: + { + value.Set(ReadString(null)); + break; + } + + case BuiltInType.DateTime: + { + value.Set(ReadDateTime(null)); + break; + } + + case BuiltInType.Guid: + { + value.Set(ReadGuid(null)); + break; + } + + case BuiltInType.ByteString: + { + value.Set(ReadByteString(null)); + break; + } + + case BuiltInType.XmlElement: + { + try + { + value.Set(ReadXmlElement(null)); + } + catch (Exception ex) + { + Utils.Trace(ex, "Error reading xml element for variant."); + value.Set(StatusCodes.BadEncodingError); + } + break; + } + + case BuiltInType.NodeId: + { + value.Set(ReadNodeId(null)); + break; + } + + case BuiltInType.ExpandedNodeId: + { + value.Set(ReadExpandedNodeId(null)); + break; + } + + case BuiltInType.StatusCode: + { + value.Set(ReadStatusCode(null)); + break; + } + + case BuiltInType.QualifiedName: + { + value.Set(ReadQualifiedName(null)); + break; + } + + case BuiltInType.LocalizedText: + { + value.Set(ReadLocalizedText(null)); + break; + } + + case BuiltInType.ExtensionObject: + { + value.Set(ReadExtensionObject()); + break; + } + + case BuiltInType.DataValue: + { + value.Set(ReadDataValue(null)); + break; + } + + default: + { + throw new ServiceResultException( + StatusCodes.BadDecodingError, + Utils.Format("Cannot decode unknown type in Variant object (0x{0:X2}).", encodingByte)); + } + } + } + + return value; + } #endregion #region Private Fields @@ -2096,6 +2147,7 @@ namespace Opc.Ua private ServiceMessageContext m_context; private ushort[] m_namespaceMappings; private ushort[] m_serverMappings; + private uint m_nestingLevel; #endregion } } diff --git a/Stack/Opc.Ua.Core/Types/Encoders/BinaryEncoder.cs b/Stack/Opc.Ua.Core/Types/Encoders/BinaryEncoder.cs index 0dce860272..a1c397334f 100644 --- a/Stack/Opc.Ua.Core/Types/Encoders/BinaryEncoder.cs +++ b/Stack/Opc.Ua.Core/Types/Encoders/BinaryEncoder.cs @@ -33,6 +33,7 @@ namespace Opc.Ua m_ostrm = new MemoryStream(); m_writer = new BinaryWriter(m_ostrm); m_context = context; + m_nestingLevel = 0; } /// @@ -45,6 +46,7 @@ namespace Opc.Ua m_ostrm = new MemoryStream(buffer, start, count); m_writer = new BinaryWriter(m_ostrm); m_context = context; + m_nestingLevel = 0; } /// @@ -57,6 +59,7 @@ namespace Opc.Ua m_ostrm = stream; m_writer = new BinaryWriter(m_ostrm); m_context = context; + m_nestingLevel = 0; } #endregion @@ -644,6 +647,15 @@ namespace Opc.Ua /// public void WriteDiagnosticInfo(string fieldName, DiagnosticInfo value) { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + // check for null. if (value == null) { @@ -651,6 +663,8 @@ namespace Opc.Ua return; } + m_nestingLevel++; + // calculate the encoding. byte encoding = 0; @@ -727,6 +741,8 @@ namespace Opc.Ua { WriteDiagnosticInfo(null, value.InnerDiagnosticInfo); } + + m_nestingLevel--; } /// @@ -792,160 +808,24 @@ namespace Opc.Ua } /// - /// Writes an Variant array to the stream. + /// Writes an Variant to the stream. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] public void WriteVariant(string fieldName, Variant value) { - // check for null. - if (value.Value == null || value.TypeInfo == null || value.TypeInfo.BuiltInType == BuiltInType.Null) + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) { - WriteByte(null, 0); - return; - } - - // encode enums as int32. - byte encodingByte = (byte)value.TypeInfo.BuiltInType; - - if (value.TypeInfo.BuiltInType == BuiltInType.Enumeration) - { - encodingByte = (byte)BuiltInType.Int32; - } - - object valueToEncode = value.Value; - - if (value.TypeInfo.ValueRank < 0) - { - WriteByte(null, encodingByte); - - switch (value.TypeInfo.BuiltInType) - { - case BuiltInType.Boolean: { WriteBoolean(null, (bool)valueToEncode); return; } - case BuiltInType.SByte: { WriteSByte(null, (sbyte)valueToEncode); return; } - case BuiltInType.Byte: { WriteByte(null, (byte)valueToEncode); return; } - case BuiltInType.Int16: { WriteInt16(null, (short)valueToEncode); return; } - case BuiltInType.UInt16: { WriteUInt16(null, (ushort)valueToEncode); return; } - case BuiltInType.Int32: { WriteInt32(null, (int)valueToEncode); return; } - case BuiltInType.UInt32: { WriteUInt32(null, (uint)valueToEncode); return; } - case BuiltInType.Int64: { WriteInt64(null, (long)valueToEncode); return; } - case BuiltInType.UInt64: { WriteUInt64(null, (ulong)valueToEncode); return; } - case BuiltInType.Float: { WriteFloat(null, (float)valueToEncode); return; } - case BuiltInType.Double: { WriteDouble(null, (double)valueToEncode); return; } - case BuiltInType.String: { WriteString(null, (string)valueToEncode); return; } - case BuiltInType.DateTime: { WriteDateTime(null, (DateTime)valueToEncode); return; } - case BuiltInType.Guid: { WriteGuid(null, (Uuid)valueToEncode); return; } - case BuiltInType.ByteString: { WriteByteString(null, (byte[])valueToEncode); return; } - case BuiltInType.XmlElement: { WriteXmlElement(null, (XmlElement)valueToEncode); return; } - case BuiltInType.NodeId: { WriteNodeId(null, (NodeId)valueToEncode); return; } - case BuiltInType.ExpandedNodeId: { WriteExpandedNodeId(null, (ExpandedNodeId)valueToEncode); return; } - case BuiltInType.StatusCode: { WriteStatusCode(null, (StatusCode)valueToEncode); return; } - case BuiltInType.QualifiedName: { WriteQualifiedName(null, (QualifiedName)valueToEncode); return; } - case BuiltInType.LocalizedText: { WriteLocalizedText(null, (LocalizedText)valueToEncode); return; } - case BuiltInType.ExtensionObject: { WriteExtensionObject(null, (ExtensionObject)valueToEncode); return; } - case BuiltInType.DataValue: { WriteDataValue(null, (DataValue)valueToEncode); return; } - case BuiltInType.Enumeration: { WriteInt32(null, (int)valueToEncode); return; } - } - throw ServiceResultException.Create( - StatusCodes.BadEncodingError, - "Unexpected type encountered while encoding a Variant: {0}", - value.TypeInfo.BuiltInType); + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); } - if (value.TypeInfo.ValueRank >= 0) - { - Matrix matrix = null; + m_nestingLevel++; - encodingByte |= (byte)VariantArrayEncodingBits.Array; + WriteVariantValue(fieldName, value); - if (value.TypeInfo.ValueRank > 1) - { - encodingByte |= (byte)VariantArrayEncodingBits.ArrayDimensions; - matrix = (Matrix)valueToEncode; - valueToEncode = matrix.Elements; - } - - WriteByte(null, encodingByte); - - switch (value.TypeInfo.BuiltInType) - { - case BuiltInType.Boolean: { WriteBooleanArray(null, (bool[])valueToEncode); break; } - case BuiltInType.SByte: { WriteSByteArray(null, (sbyte[])valueToEncode); break; } - case BuiltInType.Byte: { WriteByteArray(null, (byte[])valueToEncode); break; } - case BuiltInType.Int16: { WriteInt16Array(null, (short[])valueToEncode); break; } - case BuiltInType.UInt16: { WriteUInt16Array(null, (ushort[])valueToEncode); break; } - case BuiltInType.Int32: { WriteInt32Array(null, (int[])valueToEncode); break; } - case BuiltInType.UInt32: { WriteUInt32Array(null, (uint[])valueToEncode); break; } - case BuiltInType.Int64: { WriteInt64Array(null, (long[])valueToEncode); break; } - case BuiltInType.UInt64: { WriteUInt64Array(null, (ulong[])valueToEncode); break; } - case BuiltInType.Float: { WriteFloatArray(null, (float[])valueToEncode); break; } - case BuiltInType.Double: { WriteDoubleArray(null, (double[])valueToEncode); break; } - case BuiltInType.String: { WriteStringArray(null, (string[])valueToEncode); break; } - case BuiltInType.DateTime: { WriteDateTimeArray(null, (DateTime[])valueToEncode); break; } - case BuiltInType.Guid: { WriteGuidArray(null, (Uuid[])valueToEncode); break; } - case BuiltInType.ByteString: { WriteByteStringArray(null, (byte[][])valueToEncode); break; } - case BuiltInType.XmlElement: { WriteXmlElementArray(null, (XmlElement[])valueToEncode); break; } - case BuiltInType.NodeId: { WriteNodeIdArray(null, (NodeId[])valueToEncode); break; } - case BuiltInType.ExpandedNodeId: { WriteExpandedNodeIdArray(null, (ExpandedNodeId[])valueToEncode); break; } - case BuiltInType.StatusCode: { WriteStatusCodeArray(null, (StatusCode[])valueToEncode); break; } - case BuiltInType.QualifiedName: { WriteQualifiedNameArray(null, (QualifiedName[])valueToEncode); break; } - case BuiltInType.LocalizedText: { WriteLocalizedTextArray(null, (LocalizedText[])valueToEncode); break; } - case BuiltInType.ExtensionObject: { WriteExtensionObjectArray(null, (ExtensionObject[])valueToEncode); break; } - case BuiltInType.DataValue: { WriteDataValueArray(null, (DataValue[])valueToEncode); break; } - - case BuiltInType.Enumeration: - { - Enum[] enums = valueToEncode as Enum[]; - int[] ints = new int[enums.Length]; - - for (int ii = 0; ii < enums.Length; ii++) - { - ints[ii] = (int)(object)enums[ii]; - } - - WriteInt32Array(null, ints); - return; - } - - case BuiltInType.Variant: - { - Variant[] variants = valueToEncode as Variant[]; - - if (variants != null) - { - WriteVariantArray(null, variants); - break; - } - - object[] objects = valueToEncode as object[]; - - if (objects != null) - { - WriteObjectArray(null, objects); - break; - } - - throw ServiceResultException.Create( - StatusCodes.BadEncodingError, - "Unexpected type encountered while encoding a Matrix: {0}", - valueToEncode.GetType()); - } - - default: - { - throw ServiceResultException.Create( - StatusCodes.BadEncodingError, - "Unexpected type encountered while encoding a Variant: {0}", - value.TypeInfo.BuiltInType); - } - } - - // write the dimensions. - if (value.TypeInfo.ValueRank > 1) - { - WriteInt32Array(null, (int[])matrix.Dimensions); - } - } + m_nestingLevel--; } /// @@ -1158,6 +1038,15 @@ namespace Opc.Ua /// public void WriteEncodeable(string fieldName, IEncodeable value, System.Type systemType) { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + // create a default object if a null object specified. if (value == null) { @@ -1165,11 +1054,15 @@ namespace Opc.Ua value = Activator.CreateInstance(systemType) as IEncodeable; } + m_nestingLevel++; + // encode the object. if (value != null) { value.Encode(this); } + + m_nestingLevel--; } /// @@ -1866,6 +1759,163 @@ namespace Opc.Ua } } } + + /// + /// Writes an Variant to the stream. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private void WriteVariantValue(string fieldName, Variant value) + { + // check for null. + if (value.Value == null || value.TypeInfo == null || value.TypeInfo.BuiltInType == BuiltInType.Null) + { + WriteByte(null, 0); + return; + } + + // encode enums as int32. + byte encodingByte = (byte)value.TypeInfo.BuiltInType; + + if (value.TypeInfo.BuiltInType == BuiltInType.Enumeration) + { + encodingByte = (byte)BuiltInType.Int32; + } + + object valueToEncode = value.Value; + + if (value.TypeInfo.ValueRank < 0) + { + WriteByte(null, encodingByte); + + switch (value.TypeInfo.BuiltInType) + { + case BuiltInType.Boolean: { WriteBoolean(null, (bool)valueToEncode); return; } + case BuiltInType.SByte: { WriteSByte(null, (sbyte)valueToEncode); return; } + case BuiltInType.Byte: { WriteByte(null, (byte)valueToEncode); return; } + case BuiltInType.Int16: { WriteInt16(null, (short)valueToEncode); return; } + case BuiltInType.UInt16: { WriteUInt16(null, (ushort)valueToEncode); return; } + case BuiltInType.Int32: { WriteInt32(null, (int)valueToEncode); return; } + case BuiltInType.UInt32: { WriteUInt32(null, (uint)valueToEncode); return; } + case BuiltInType.Int64: { WriteInt64(null, (long)valueToEncode); return; } + case BuiltInType.UInt64: { WriteUInt64(null, (ulong)valueToEncode); return; } + case BuiltInType.Float: { WriteFloat(null, (float)valueToEncode); return; } + case BuiltInType.Double: { WriteDouble(null, (double)valueToEncode); return; } + case BuiltInType.String: { WriteString(null, (string)valueToEncode); return; } + case BuiltInType.DateTime: { WriteDateTime(null, (DateTime)valueToEncode); return; } + case BuiltInType.Guid: { WriteGuid(null, (Uuid)valueToEncode); return; } + case BuiltInType.ByteString: { WriteByteString(null, (byte[])valueToEncode); return; } + case BuiltInType.XmlElement: { WriteXmlElement(null, (XmlElement)valueToEncode); return; } + case BuiltInType.NodeId: { WriteNodeId(null, (NodeId)valueToEncode); return; } + case BuiltInType.ExpandedNodeId: { WriteExpandedNodeId(null, (ExpandedNodeId)valueToEncode); return; } + case BuiltInType.StatusCode: { WriteStatusCode(null, (StatusCode)valueToEncode); return; } + case BuiltInType.QualifiedName: { WriteQualifiedName(null, (QualifiedName)valueToEncode); return; } + case BuiltInType.LocalizedText: { WriteLocalizedText(null, (LocalizedText)valueToEncode); return; } + case BuiltInType.ExtensionObject: { WriteExtensionObject(null, (ExtensionObject)valueToEncode); return; } + case BuiltInType.DataValue: { WriteDataValue(null, (DataValue)valueToEncode); return; } + case BuiltInType.Enumeration: { WriteInt32(null, (int)valueToEncode); return; } + } + + throw ServiceResultException.Create( + StatusCodes.BadEncodingError, + "Unexpected type encountered while encoding a Variant: {0}", + value.TypeInfo.BuiltInType); + } + + if (value.TypeInfo.ValueRank >= 0) + { + Matrix matrix = null; + + encodingByte |= (byte)VariantArrayEncodingBits.Array; + + if (value.TypeInfo.ValueRank > 1) + { + encodingByte |= (byte)VariantArrayEncodingBits.ArrayDimensions; + matrix = (Matrix)valueToEncode; + valueToEncode = matrix.Elements; + } + + WriteByte(null, encodingByte); + + switch (value.TypeInfo.BuiltInType) + { + case BuiltInType.Boolean: { WriteBooleanArray(null, (bool[])valueToEncode); break; } + case BuiltInType.SByte: { WriteSByteArray(null, (sbyte[])valueToEncode); break; } + case BuiltInType.Byte: { WriteByteArray(null, (byte[])valueToEncode); break; } + case BuiltInType.Int16: { WriteInt16Array(null, (short[])valueToEncode); break; } + case BuiltInType.UInt16: { WriteUInt16Array(null, (ushort[])valueToEncode); break; } + case BuiltInType.Int32: { WriteInt32Array(null, (int[])valueToEncode); break; } + case BuiltInType.UInt32: { WriteUInt32Array(null, (uint[])valueToEncode); break; } + case BuiltInType.Int64: { WriteInt64Array(null, (long[])valueToEncode); break; } + case BuiltInType.UInt64: { WriteUInt64Array(null, (ulong[])valueToEncode); break; } + case BuiltInType.Float: { WriteFloatArray(null, (float[])valueToEncode); break; } + case BuiltInType.Double: { WriteDoubleArray(null, (double[])valueToEncode); break; } + case BuiltInType.String: { WriteStringArray(null, (string[])valueToEncode); break; } + case BuiltInType.DateTime: { WriteDateTimeArray(null, (DateTime[])valueToEncode); break; } + case BuiltInType.Guid: { WriteGuidArray(null, (Uuid[])valueToEncode); break; } + case BuiltInType.ByteString: { WriteByteStringArray(null, (byte[][])valueToEncode); break; } + case BuiltInType.XmlElement: { WriteXmlElementArray(null, (XmlElement[])valueToEncode); break; } + case BuiltInType.NodeId: { WriteNodeIdArray(null, (NodeId[])valueToEncode); break; } + case BuiltInType.ExpandedNodeId: { WriteExpandedNodeIdArray(null, (ExpandedNodeId[])valueToEncode); break; } + case BuiltInType.StatusCode: { WriteStatusCodeArray(null, (StatusCode[])valueToEncode); break; } + case BuiltInType.QualifiedName: { WriteQualifiedNameArray(null, (QualifiedName[])valueToEncode); break; } + case BuiltInType.LocalizedText: { WriteLocalizedTextArray(null, (LocalizedText[])valueToEncode); break; } + case BuiltInType.ExtensionObject: { WriteExtensionObjectArray(null, (ExtensionObject[])valueToEncode); break; } + case BuiltInType.DataValue: { WriteDataValueArray(null, (DataValue[])valueToEncode); break; } + + case BuiltInType.Enumeration: + { + Enum[] enums = valueToEncode as Enum[]; + int[] ints = new int[enums.Length]; + + for (int ii = 0; ii < enums.Length; ii++) + { + ints[ii] = (int)(object)enums[ii]; + } + + WriteInt32Array(null, ints); + return; + } + + case BuiltInType.Variant: + { + Variant[] variants = valueToEncode as Variant[]; + + if (variants != null) + { + WriteVariantArray(null, variants); + break; + } + + object[] objects = valueToEncode as object[]; + + if (objects != null) + { + WriteObjectArray(null, objects); + break; + } + + throw ServiceResultException.Create( + StatusCodes.BadEncodingError, + "Unexpected type encountered while encoding a Matrix: {0}", + valueToEncode.GetType()); + } + + default: + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingError, + "Unexpected type encountered while encoding a Variant: {0}", + value.TypeInfo.BuiltInType); + } + } + + // write the dimensions. + if (value.TypeInfo.ValueRank > 1) + { + WriteInt32Array(null, (int[])matrix.Dimensions); + } + } + } #endregion #region Private Fields @@ -1874,6 +1924,7 @@ namespace Opc.Ua private ServiceMessageContext m_context; private ushort[] m_namespaceMappings; private ushort[] m_serverMappings; + private uint m_nestingLevel; #endregion } diff --git a/Stack/Opc.Ua.Core/Types/Encoders/JsonDecoder.cs b/Stack/Opc.Ua.Core/Types/Encoders/JsonDecoder.cs index 8d13db559c..9e6959bbfa 100644 --- a/Stack/Opc.Ua.Core/Types/Encoders/JsonDecoder.cs +++ b/Stack/Opc.Ua.Core/Types/Encoders/JsonDecoder.cs @@ -32,6 +32,7 @@ namespace Opc.Ua private ServiceMessageContext m_context; private ushort[] m_namespaceMappings; private ushort[] m_serverMappings; + private uint m_nestingLevel; #endregion #region Constructors @@ -40,8 +41,8 @@ namespace Opc.Ua if (context == null) throw new ArgumentNullException("context"); Initialize(); - m_context = context; + m_nestingLevel = 0; m_reader = new JsonTextReader(new StringReader(json)); m_root = ReadObject(); m_stack = new Stack(); @@ -53,6 +54,7 @@ namespace Opc.Ua Initialize(); m_context = context; + m_nestingLevel = 0; m_reader = reader; m_root = ReadObject(); m_stack = new Stack(); @@ -942,6 +944,16 @@ namespace Opc.Ua try { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; m_stack.Push(value); DiagnosticInfo di = new DiagnosticInfo(); @@ -985,6 +997,7 @@ namespace Opc.Ua } finally { + m_nestingLevel--; m_stack.Pop(); } } @@ -1150,6 +1163,16 @@ namespace Opc.Ua try { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; m_stack.Push(value); BuiltInType type = (BuiltInType)ReadByte("Type"); @@ -1180,6 +1203,7 @@ namespace Opc.Ua } finally { + m_nestingLevel--; m_stack.Pop(); } } @@ -1357,6 +1381,17 @@ namespace Opc.Ua throw new ServiceResultException(StatusCodes.BadDecodingError, Utils.Format("Type does not support IEncodeable interface: '{0}'", systemType.FullName)); } + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + try { m_stack.Push(token); @@ -1368,6 +1403,8 @@ namespace Opc.Ua m_stack.Pop(); } + m_nestingLevel--; + return value; } diff --git a/Stack/Opc.Ua.Core/Types/Encoders/JsonEncoder.cs b/Stack/Opc.Ua.Core/Types/Encoders/JsonEncoder.cs index 029a786146..c6283425eb 100644 --- a/Stack/Opc.Ua.Core/Types/Encoders/JsonEncoder.cs +++ b/Stack/Opc.Ua.Core/Types/Encoders/JsonEncoder.cs @@ -32,6 +32,7 @@ namespace Opc.Ua private ServiceMessageContext m_context; private ushort[] m_namespaceMappings; private ushort[] m_serverMappings; + private uint m_nestingLevel; #endregion public bool UseReversibleEncoding { get; private set; } @@ -45,6 +46,7 @@ namespace Opc.Ua Initialize(); m_context = context; + m_nestingLevel = 0; m_writer = writer; UseReversibleEncoding = useReversibleEncoding; @@ -729,6 +731,17 @@ namespace Opc.Ua /// public void WriteDiagnosticInfo(string fieldName, DiagnosticInfo value) { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + if (value == null) { WriteSimpleField(fieldName, null, false); @@ -773,6 +786,8 @@ namespace Opc.Ua } PopStructure(); + + m_nestingLevel--; } /// @@ -837,10 +852,21 @@ namespace Opc.Ua } /// - /// Writes an Variant array to the stream. + /// Writes an Variant to the stream. /// public void WriteVariant(string fieldName, Variant value) { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + if (Variant.Null == value) { WriteSimpleField(fieldName, null, false); @@ -881,6 +907,8 @@ namespace Opc.Ua PopStructure(); } + + m_nestingLevel--; } /// @@ -987,6 +1015,17 @@ namespace Opc.Ua /// public void WriteEncodeable(string fieldName, IEncodeable value, System.Type systemType) { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + if (value == null) { WriteSimpleField(fieldName, null, false); @@ -1001,6 +1040,8 @@ namespace Opc.Ua } PopStructure(); + + m_nestingLevel--; } /// diff --git a/Stack/Opc.Ua.Core/Types/Encoders/XmlDecoder.cs b/Stack/Opc.Ua.Core/Types/Encoders/XmlDecoder.cs index 8ffd0b10cc..2e02d76caf 100644 --- a/Stack/Opc.Ua.Core/Types/Encoders/XmlDecoder.cs +++ b/Stack/Opc.Ua.Core/Types/Encoders/XmlDecoder.cs @@ -33,8 +33,9 @@ namespace Opc.Ua if (context == null) throw new ArgumentNullException("context"); Initialize(); m_context = context; - } - + m_nestingLevel = 0; + } + /// /// Initializes the object with an XML element to parse. /// @@ -44,6 +45,7 @@ namespace Opc.Ua Initialize(); m_reader = XmlReader.Create(new StringReader(element.OuterXml)); m_context = context; + m_nestingLevel = 0; } /// @@ -55,6 +57,7 @@ namespace Opc.Ua m_reader = reader; m_context = context; + m_nestingLevel = 0; string ns = null; string name = null; @@ -82,11 +85,11 @@ namespace Opc.Ua PushNamespace(ns); BeginField(name, false); - } - - /// - /// Sets private members to default values. - /// + } + + /// + /// Sets private members to default values. + /// private void Initialize() { m_reader = null; @@ -1190,6 +1193,17 @@ namespace Opc.Ua /// public DiagnosticInfo ReadDiagnosticInfo() { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + DiagnosticInfo value = new DiagnosticInfo(); if (BeginField("SymbolicId", true)) @@ -1225,6 +1239,8 @@ namespace Opc.Ua EndField("InnerDiagnosticInfo"); } + m_nestingLevel--; + return value; } @@ -1321,6 +1337,17 @@ namespace Opc.Ua /// public Variant ReadVariant(string fieldName) { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + Variant value = new Variant(); if (BeginField(fieldName, true)) @@ -1348,6 +1375,8 @@ namespace Opc.Ua EndField(fieldName); } + m_nestingLevel--; + return value; } @@ -1443,6 +1472,17 @@ namespace Opc.Ua Utils.Format("Type does not support IEncodeable interface: '{0}'", systemType.FullName)); } + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + if (BeginField(fieldName, true)) { XmlQualifiedName xmlName = EncodeableFactory.GetXmlName(systemType); @@ -1466,10 +1506,12 @@ namespace Opc.Ua m_reader.Skip(); m_reader.MoveToContent(); } - + EndField(fieldName); } - + + m_nestingLevel--; + return value; } @@ -2742,6 +2784,7 @@ namespace Opc.Ua private ServiceMessageContext m_context; private ushort[] m_namespaceMappings; private ushort[] m_serverMappings; + private uint m_nestingLevel; #endregion } } diff --git a/Stack/Opc.Ua.Core/Types/Encoders/XmlEncoder.cs b/Stack/Opc.Ua.Core/Types/Encoders/XmlEncoder.cs index 3eba96c803..1a213910d8 100644 --- a/Stack/Opc.Ua.Core/Types/Encoders/XmlEncoder.cs +++ b/Stack/Opc.Ua.Core/Types/Encoders/XmlEncoder.cs @@ -35,6 +35,7 @@ namespace Opc.Ua m_destination = new StringBuilder(); m_context = context; + m_nestingLevel = 0; XmlWriterSettings settings = new XmlWriterSettings(); settings.CheckCharacters = false; @@ -72,6 +73,7 @@ namespace Opc.Ua Initialize(root.Name, root.Namespace); m_context = context; + m_nestingLevel = 0; } /// @@ -612,10 +614,21 @@ namespace Opc.Ua /// public void WriteDiagnosticInfo(string fieldName, DiagnosticInfo value) { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + if (BeginField(fieldName, value == null, true)) { PushNamespace(Namespaces.OpcUaXsd); - + if (value != null) { WriteInt32("SymbolicId", value.SymbolicId); @@ -628,9 +641,11 @@ namespace Opc.Ua } PopNamespace(); - + EndField(fieldName); } + + m_nestingLevel--; } /// @@ -687,18 +702,31 @@ namespace Opc.Ua /// public void WriteVariant(string fieldName, Variant value) { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + if (BeginField(fieldName, false, false)) { PushNamespace(Namespaces.OpcUaXsd); - - m_writer.WriteStartElement("Value", Namespaces.OpcUaXsd); - WriteVariantContents(value.Value, value.TypeInfo); + + m_writer.WriteStartElement("Value", Namespaces.OpcUaXsd); + WriteVariantContents(value.Value, value.TypeInfo); m_writer.WriteEndElement(); - + PopNamespace(); - + EndField(fieldName); } + + m_nestingLevel--; } /// @@ -804,7 +832,18 @@ namespace Opc.Ua /// Writes an encodeable object to the stream. /// public void WriteEncodeable(string fieldName, IEncodeable value, System.Type systemType) - { + { + // check the nesting level for avoiding a stack overflow. + if (m_nestingLevel > m_context.MaxEncodingNestingLevels) + { + throw ServiceResultException.Create( + StatusCodes.BadEncodingLimitsExceeded, + "Maximum nesting level of {0} was exceeded", + m_context.MaxEncodingNestingLevels); + } + + m_nestingLevel++; + if (BeginField(fieldName, value == null, true)) { if (value != null) @@ -814,7 +853,9 @@ namespace Opc.Ua EndField(fieldName); } - } + + m_nestingLevel--; + } /// /// Writes an enumerated value array to the stream. @@ -1959,6 +2000,7 @@ namespace Opc.Ua private ServiceMessageContext m_context; private ushort[] m_namespaceMappings; private ushort[] m_serverMappings; + private uint m_nestingLevel; #endregion } } diff --git a/Stack/Opc.Ua.Core/Types/Utils/ServiceMessageContext.cs b/Stack/Opc.Ua.Core/Types/Utils/ServiceMessageContext.cs index 23a6949dcb..0cd0a9a16a 100644 --- a/Stack/Opc.Ua.Core/Types/Utils/ServiceMessageContext.cs +++ b/Stack/Opc.Ua.Core/Types/Utils/ServiceMessageContext.cs @@ -31,24 +31,26 @@ namespace Opc.Ua /// public ServiceMessageContext() { - m_maxStringLength = UInt16.MaxValue; - m_maxByteStringLength = UInt16.MaxValue*16; - m_maxArrayLength = UInt16.MaxValue; - m_maxMessageSize = UInt16.MaxValue*32; - m_namespaceUris = new NamespaceTable(); - m_serverUris = new StringTable(); - m_factory = EncodeableFactory.GlobalFactory; + m_maxStringLength = UInt16.MaxValue; + m_maxByteStringLength = UInt16.MaxValue*16; + m_maxArrayLength = UInt16.MaxValue; + m_maxMessageSize = UInt16.MaxValue*32; + m_namespaceUris = new NamespaceTable(); + m_serverUris = new StringTable(); + m_factory = EncodeableFactory.GlobalFactory; + m_maxEncodingNestingLevels = 200; } private ServiceMessageContext(bool shared) : this() { - m_maxStringLength = UInt16.MaxValue; - m_maxByteStringLength = UInt16.MaxValue*16; - m_maxArrayLength = UInt16.MaxValue; - m_maxMessageSize = UInt16.MaxValue*32; - m_namespaceUris = new NamespaceTable(shared); - m_serverUris = new StringTable(shared); - m_factory = EncodeableFactory.GlobalFactory; + m_maxStringLength = UInt16.MaxValue; + m_maxByteStringLength = UInt16.MaxValue*16; + m_maxArrayLength = UInt16.MaxValue; + m_maxMessageSize = UInt16.MaxValue*32; + m_namespaceUris = new NamespaceTable(shared); + m_serverUris = new StringTable(shared); + m_factory = EncodeableFactory.GlobalFactory; + m_maxEncodingNestingLevels = 200; } #endregion @@ -123,6 +125,14 @@ namespace Opc.Ua set { lock (m_lock) { m_maxMessageSize = value; } } } + /// + /// The maximum nesting level accepted while encoding or decoding objects. + /// + public uint MaxEncodingNestingLevels + { + get { lock (m_lock) { return m_maxEncodingNestingLevels; } } + } + /// /// The table of namespaces used by the server. /// @@ -205,6 +215,7 @@ namespace Opc.Ua private int m_maxByteStringLength; private int m_maxArrayLength; private int m_maxMessageSize; + private uint m_maxEncodingNestingLevels; private NamespaceTable m_namespaceUris; private StringTable m_serverUris; private EncodeableFactory m_factory; From c05145156c412176f261a50b5e1fb5808fdb04d5 Mon Sep 17 00:00:00 2001 From: Softing Date: Fri, 7 Jul 2017 17:15:24 +0300 Subject: [PATCH 2/6] Implemented validations for ArrayDimensions when decoding Variant objects. --- .../Types/Encoders/BinaryDecoder.cs | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Stack/Opc.Ua.Core/Types/Encoders/BinaryDecoder.cs b/Stack/Opc.Ua.Core/Types/Encoders/BinaryDecoder.cs index 757c9b6c76..7daeda9aa0 100644 --- a/Stack/Opc.Ua.Core/Types/Encoders/BinaryDecoder.cs +++ b/Stack/Opc.Ua.Core/Types/Encoders/BinaryDecoder.cs @@ -1955,14 +1955,35 @@ namespace Opc.Ua { Int32Collection dimensions = ReadInt32Array(null); - if (dimensions != null && dimensions.Count > 0) + // check if ArrayDimensions are consistent with the ArrayLength. + if (dimensions == null || dimensions.Count == 0) { - value = new Variant(new Matrix(array, builtInType, dimensions.ToArray())); + throw new ServiceResultException( + StatusCodes.BadDecodingError, + "ArrayDimensions not specified when ArrayDimensions encoding bit was set in Variant object."); } - else + + int[] dimensionsArray = dimensions.ToArray(); + int matrixLength = 1; + + for (int ii = 0; ii < dimensionsArray.Length; ii++) { - value = new Variant(new Matrix(array, builtInType)); + if (dimensionsArray[ii] == 0 && length > 0) + { + throw new ServiceResultException( + StatusCodes.BadDecodingError, + Utils.Format("ArrayDimensions [{0}] is zero in Variant object.", ii)); + } + + matrixLength *= dimensionsArray[ii]; } + + if (matrixLength != length) + { + throw new ServiceResultException(StatusCodes.BadDecodingError, "ArrayDimensions does not match with the ArrayLength in Variant object."); + } + + value = new Variant(new Matrix(array, builtInType, dimensions.ToArray())); } else { From 78f97531ab01b722be9d84def5b2c494450db2dd Mon Sep 17 00:00:00 2001 From: Softing Date: Fri, 7 Jul 2017 17:18:47 +0300 Subject: [PATCH 3/6] Stop sending publish requests from client after a BadSessionIdInvalid error --- SampleApplications/SDK/Opc.Ua.Client/Session.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/SampleApplications/SDK/Opc.Ua.Client/Session.cs b/SampleApplications/SDK/Opc.Ua.Client/Session.cs index 7e908051fc..c58f9bc2f3 100644 --- a/SampleApplications/SDK/Opc.Ua.Client/Session.cs +++ b/SampleApplications/SDK/Opc.Ua.Client/Session.cs @@ -3764,6 +3764,7 @@ namespace Opc.Ua.Client { case StatusCodes.BadNoSubscription: case StatusCodes.BadSessionClosed: + case StatusCodes.BadSessionIdInvalid: case StatusCodes.BadTooManyPublishRequests: case StatusCodes.BadServerHalted: { From 261e39bd388defc7b83c74939f5f77973142e7a6 Mon Sep 17 00:00:00 2001 From: Softing Date: Mon, 10 Jul 2017 14:09:25 +0300 Subject: [PATCH 4/6] Fix for exception in Node(ReferenceDescription reference) constructor when reference contains an absolute ExpandedNodeId. --- SampleApplications/SDK/Opc.Ua.Client/NodeCache.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SampleApplications/SDK/Opc.Ua.Client/NodeCache.cs b/SampleApplications/SDK/Opc.Ua.Client/NodeCache.cs index 3ecfb5fb49..ea70c708f1 100644 --- a/SampleApplications/SDK/Opc.Ua.Client/NodeCache.cs +++ b/SampleApplications/SDK/Opc.Ua.Client/NodeCache.cs @@ -642,6 +642,12 @@ namespace Opc.Ua.Client // create a placeholder for the node if it does not already exist. if (!m_nodes.Exists(reference.NodeId)) { + // transform absolute identifiers. + if (reference.NodeId != null && reference.NodeId.IsAbsolute) + { + reference.NodeId = ExpandedNodeId.ToNodeId(reference.NodeId, NamespaceUris); + } + Node target = new Node(reference); m_nodes.Attach(target); } From e7b7650a406d30a1c57d12ba29200dbd8a8267fb Mon Sep 17 00:00:00 2001 From: Softing Date: Mon, 10 Jul 2017 14:19:44 +0300 Subject: [PATCH 5/6] Do not attempt to delete null diagnostics node in Subscription.Delete(). --- .../SDK/Opc.Ua.Server/Subscription/Subscription.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/SampleApplications/SDK/Opc.Ua.Server/Subscription/Subscription.cs b/SampleApplications/SDK/Opc.Ua.Server/Subscription/Subscription.cs index bf9fb9b222..dea0d9f14d 100644 --- a/SampleApplications/SDK/Opc.Ua.Server/Subscription/Subscription.cs +++ b/SampleApplications/SDK/Opc.Ua.Server/Subscription/Subscription.cs @@ -335,8 +335,11 @@ namespace Opc.Ua.Server public void Delete(OperationContext context) { // delete the diagnostics. - ServerSystemContext systemContext = m_server.DefaultSystemContext.Copy(m_session); - m_server.DiagnosticsNodeManager.DeleteSubscriptionDiagnostics(systemContext, m_diagnosticsId); + if (m_diagnosticsId != null && !m_diagnosticsId.IsNullNodeId) + { + ServerSystemContext systemContext = m_server.DefaultSystemContext.Copy(m_session); + m_server.DiagnosticsNodeManager.DeleteSubscriptionDiagnostics(systemContext, m_diagnosticsId); + } lock (m_lock) { From 01068efc66fce486bac4868e57b3566ceaa6a73f Mon Sep 17 00:00:00 2001 From: Softing Date: Mon, 10 Jul 2017 14:25:28 +0300 Subject: [PATCH 6/6] Fix for bug in ServerSystemContext.Copy() --- .../NodeManager/ServerSystemContext.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/SampleApplications/SDK/Opc.Ua.Server/NodeManager/ServerSystemContext.cs b/SampleApplications/SDK/Opc.Ua.Server/NodeManager/ServerSystemContext.cs index 596cf50438..50cb6db7f9 100644 --- a/SampleApplications/SDK/Opc.Ua.Server/NodeManager/ServerSystemContext.cs +++ b/SampleApplications/SDK/Opc.Ua.Server/NodeManager/ServerSystemContext.cs @@ -128,36 +128,36 @@ namespace Opc.Ua.Server /// /// The session to use. /// - /// A copy of the system context that references the new operation context. + /// A copy of the system context that references the new session. /// public ServerSystemContext Copy(Session session) { ServerSystemContext copy = (ServerSystemContext)MemberwiseClone(); - OperationContext = null; + copy.OperationContext = null; if (session != null) { - SessionId = session.Id; - UserIdentity = session.Identity; - PreferredLocales = session.PreferredLocales; + copy.SessionId = session.Id; + copy.UserIdentity = session.Identity; + copy.PreferredLocales = session.PreferredLocales; } else { - SessionId = null; - UserIdentity = null; - PreferredLocales = null; + copy.SessionId = null; + copy.UserIdentity = null; + copy.PreferredLocales = null; } return copy; } /// - /// Creates a copy of the context that can be used with the specified session. + /// Creates a copy of the context that can be used with the specified server context. /// - /// The session to use. + /// The server context to use. /// - /// A copy of the system context that references the new operation context. + /// A copy of the system context that references the new server context. /// public ServerSystemContext Copy(ServerSystemContext context) { @@ -165,14 +165,14 @@ namespace Opc.Ua.Server if (context != null) { - OperationContext = context.OperationContext; - SessionId = context.SessionId; - UserIdentity = context.UserIdentity; - PreferredLocales = context.PreferredLocales; - NamespaceUris = context.NamespaceUris; - ServerUris = context.ServerUris; - TypeTable = context.TypeTable; - EncodeableFactory = context.EncodeableFactory; + copy.OperationContext = context.OperationContext; + copy.SessionId = context.SessionId; + copy.UserIdentity = context.UserIdentity; + copy.PreferredLocales = context.PreferredLocales; + copy.NamespaceUris = context.NamespaceUris; + copy.ServerUris = context.ServerUris; + copy.TypeTable = context.TypeTable; + copy.EncodeableFactory = context.EncodeableFactory; } return copy;