diff --git a/src/Tmds.DBus.Protocol/ProtocolConstants.cs b/src/Tmds.DBus.Protocol/ProtocolConstants.cs
index 651587f..387c07c 100644
--- a/src/Tmds.DBus.Protocol/ProtocolConstants.cs
+++ b/src/Tmds.DBus.Protocol/ProtocolConstants.cs
@@ -5,6 +5,7 @@ static class ProtocolConstants
public const int MaxSignatureLength = 256;
public const uint MaxArrayLength = 64 * 1024 * 1024;
public const uint MaxMessageLength = 128 * 1024 * 1024;
+ public const int MaxVariantRecursionDepth = 64;
public const int StructAlignment = 8;
public const int UInt32Alignment = 4;
diff --git a/src/Tmds.DBus.Protocol/Reader.Variant.cs b/src/Tmds.DBus.Protocol/Reader.Variant.cs
index 450d88b..18e129d 100644
--- a/src/Tmds.DBus.Protocol/Reader.Variant.cs
+++ b/src/Tmds.DBus.Protocol/Reader.Variant.cs
@@ -7,26 +7,31 @@ public ref partial struct Reader
///
/// The variant value.
public VariantValue ReadVariantValue()
- => ReadVariantValue(nesting: 0);
+ => ReadVariantValue(nesting: 0, recursionDepth: 0);
- private VariantValue ReadVariantValue(byte nesting)
+ private VariantValue ReadVariantValue(byte nesting, int recursionDepth)
{
ReadOnlySpan signature = ReadSignatureAsSpan();
- return ReadVariantValue(signature, nesting);
+ return ReadVariantValue(signature, nesting, recursionDepth);
}
- internal VariantValue ReadVariantValue(ReadOnlySpan signature, byte nesting = 0)
+ internal VariantValue ReadVariantValue(ReadOnlySpan signature, byte nesting = 0, int recursionDepth = 0)
{
SignatureReader sigReader = new(signature);
if (!sigReader.TryRead(out DBusType type, out ReadOnlySpan innerSignature))
{
ThrowInvalidSignature($"Invalid variant signature: {ThrowHelper.SignatureToStringNoThrow(signature)}");
}
- return ReadTypeAsVariantValue(type, innerSignature, nesting);
+ return ReadTypeAsVariantValue(type, innerSignature, nesting, recursionDepth);
}
- private VariantValue ReadTypeAsVariantValue(DBusType type, ReadOnlySpan innerSignature, byte nesting)
+ private VariantValue ReadTypeAsVariantValue(DBusType type, ReadOnlySpan innerSignature, byte nesting, int recursionDepth)
{
+ recursionDepth++;
+ if (recursionDepth > ProtocolConstants.MaxVariantRecursionDepth)
+ {
+ ThrowHelper.ThrowReaderRecursionDepthExceeded();
+ }
SignatureReader sigReader;
switch (type)
{
@@ -59,7 +64,7 @@ private VariantValue ReadTypeAsVariantValue(DBusType type, ReadOnlySpan in
return new VariantValue(_handles, idx, nesting);
case DBusType.Variant:
nesting += 1;
- return ReadVariantValue(nesting);
+ return ReadVariantValue(nesting, recursionDepth);
case DBusType.Array:
ReadOnlySpan itemSignature = innerSignature;
sigReader = new(innerSignature);
@@ -83,10 +88,10 @@ private VariantValue ReadTypeAsVariantValue(DBusType type, ReadOnlySpan in
while (HasNext(arrayEnd))
{
AlignStruct();
- VariantValue key = ReadTypeAsVariantValue(keyType, keyInnerSignature, nesting: 0);
+ VariantValue key = ReadTypeAsVariantValue(keyType, keyInnerSignature, nesting: 0, recursionDepth);
VariantValue value = valueType == DBusType.Variant
- ? ReadVariantValue() // unwrap
- : ReadTypeAsVariantValue(valueType, valueInnerSignature, nesting: 0);
+ ? ReadVariantValue(nesting: 0, recursionDepth) // unwrap
+ : ReadTypeAsVariantValue(valueType, valueInnerSignature, nesting: 0, recursionDepth);
items.Add(new KeyValuePair(key, value));
}
ReadOnlySpan valueSignature = itemSignature.Slice(2, itemSignature.Length - 3);
@@ -141,8 +146,8 @@ private VariantValue ReadTypeAsVariantValue(DBusType type, ReadOnlySpan in
while (HasNext(arrayEnd))
{
VariantValue value = type == DBusType.Variant
- ? ReadVariantValue() // unwrap
- : ReadTypeAsVariantValue(type, innerSignature, nesting: 0);
+ ? ReadVariantValue(nesting: 0, recursionDepth) // unwrap
+ : ReadTypeAsVariantValue(type, innerSignature, nesting: 0, recursionDepth);
items.Add(value);
}
return new VariantValue(ToVariantValueType(type, innerSignature), VariantValue.GetSignatureObject(items.Count, itemSignature), items.ToArray(), nesting);
@@ -161,11 +166,11 @@ private VariantValue ReadTypeAsVariantValue(DBusType type, ReadOnlySpan in
if (type == DBusType.Variant)
{
variantMask |= (1L << i);
- value = ReadVariantValue(); // unwrap
+ value = ReadVariantValue(nesting: 0, recursionDepth); // unwrap
}
else
{
- value = ReadTypeAsVariantValue(type, innerSignature, nesting: 0);
+ value = ReadTypeAsVariantValue(type, innerSignature, nesting: 0, recursionDepth);
}
items.Add(value);
i++;
diff --git a/src/Tmds.DBus.Protocol/ThrowHelper.cs b/src/Tmds.DBus.Protocol/ThrowHelper.cs
index 6c8be74..1bbb3e3 100644
--- a/src/Tmds.DBus.Protocol/ThrowHelper.cs
+++ b/src/Tmds.DBus.Protocol/ThrowHelper.cs
@@ -52,6 +52,12 @@ internal static void ThrowReaderInvalidUTF8()
throw new DBusReadException("Invalid UTF-8 sequence.");
}
+ [DoesNotReturn]
+ internal static void ThrowReaderRecursionDepthExceeded()
+ {
+ throw new DBusReadException($"Variant recursion exceeds the allowed ({ProtocolConstants.MaxVariantRecursionDepth}).");
+ }
+
[DoesNotReturn]
internal static void ThrowReaderNoFileHandle()
{
diff --git a/test/Tmds.DBus.Protocol.Tests/ReaderTests.cs b/test/Tmds.DBus.Protocol.Tests/ReaderTests.cs
index 575782f..236ed07 100644
--- a/test/Tmds.DBus.Protocol.Tests/ReaderTests.cs
+++ b/test/Tmds.DBus.Protocol.Tests/ReaderTests.cs
@@ -533,6 +533,85 @@ public static IEnumerable