Skip to content

Commit 53976d3

Browse files
[release/5.0-preview7] Disallow unrestricted polymorphic deserialization in DataSet (#39314)
Fixes CVE-2020-1147 https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-1147 See also https://go.microsoft.com/fwlink/?linkid=2132227.
1 parent fe9e53e commit 53976d3

12 files changed

+824
-13
lines changed

src/libraries/System.Data.Common/src/Resources/Strings.resx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
<data name="Data_ArgumentOutOfRange" xml:space="preserve"><value>'{0}' argument is out of range.</value></data>
166166
<data name="Data_ArgumentNull" xml:space="preserve"><value>'{0}' argument cannot be null.</value></data>
167167
<data name="Data_ArgumentContainsNull" xml:space="preserve"><value>'{0}' argument contains null value.</value></data>
168+
<data name="Data_TypeNotAllowed" xml:space="preserve"><value>Type '{0}' is not allowed here. See https://go.microsoft.com/fwlink/?linkid=2132227 for more details.</value></data>
168169
<data name="DataColumns_OutOfRange" xml:space="preserve"><value>Cannot find column {0}.</value></data>
169170
<data name="DataColumns_Add1" xml:space="preserve"><value>Column '{0}' already belongs to this DataTable.</value></data>
170171
<data name="DataColumns_Add2" xml:space="preserve"><value>Column '{0}' already belongs to another DataTable.</value></data>

src/libraries/System.Data.Common/src/System.Data.Common.csproj

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<AssemblyName>System.Data.Common</AssemblyName>
44
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
5-
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
5+
<TargetFrameworks>$(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Unix</TargetFrameworks>
66
</PropertyGroup>
77
<ItemGroup>
88
<Compile Include="System.Data.Common.TypeForwards.cs" />
@@ -123,6 +123,10 @@
123123
<Compile Include="System\Data\KeyRestrictionBehavior.cs" />
124124
<Compile Include="System\Data\LinqDataView.cs" />
125125
<Compile Include="System\Data\LoadOption.cs" />
126+
<Compile Include="System\Data\LocalAppContextSwitches.cs" />
127+
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
128+
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
129+
</Compile>
126130
<Compile Include="System\Data\MappingType.cs" />
127131
<Compile Include="System\Data\MergeFailedEvent.cs" />
128132
<Compile Include="System\Data\MergeFailedEventHandler.cs" />
@@ -156,6 +160,7 @@
156160
<Compile Include="System\Data\StrongTypingException.cs" />
157161
<Compile Include="System\Data\TypedTableBase.cs" />
158162
<Compile Include="System\Data\TypedTableBaseExtensions.cs" />
163+
<Compile Include="System\Data\TypeLimiter.cs" />
159164
<Compile Include="System\Data\UniqueConstraint.cs" />
160165
<Compile Include="System\Data\UpdateRowSource.cs" />
161166
<Compile Include="System\Data\Common\UInt64Storage.cs" />
@@ -295,25 +300,23 @@
295300
<Compile Include="System\Data\ProviderBase\SchemaMapping.cs" />
296301
</ItemGroup>
297302
<ItemGroup>
298-
<Reference Include="System.Collections" />
303+
<ProjectReference Include="$(CoreLibProject)" />
304+
<ProjectReference Include="..\..\System.Collections\src\System.Collections.csproj" />
305+
<ProjectReference Include="..\..\System.Collections.NonGeneric\src\System.Collections.NonGeneric.csproj" />
306+
<ProjectReference Include="..\..\System.ComponentModel.TypeConverter\src\System.ComponentModel.TypeConverter.csproj" />
307+
<ProjectReference Include="..\..\System.Runtime\src\System.Runtime.csproj" />
308+
<ProjectReference Include="..\..\System.Runtime.Extensions\src\System.Runtime.Extensions.csproj" />
309+
<ProjectReference Include="..\..\System.Private.Uri\src\System.Private.Uri.csproj" />
299310
<Reference Include="System.Collections.Concurrent" />
300-
<Reference Include="System.Collections.NonGeneric" />
301311
<Reference Include="System.ComponentModel" />
302312
<Reference Include="System.ComponentModel.Primitives" />
303-
<Reference Include="System.ComponentModel.TypeConverter" />
304-
<Reference Include="System.Diagnostics.Tracing" />
313+
<Reference Include="System.Drawing.Primitives" />
305314
<Reference Include="System.Linq" />
306315
<Reference Include="System.Linq.Expressions" />
307-
<Reference Include="System.Memory" />
308316
<Reference Include="System.ObjectModel" />
309-
<Reference Include="System.Runtime" />
310-
<Reference Include="System.Runtime.Extensions" />
311317
<Reference Include="System.Runtime.Numerics" />
312318
<Reference Include="System.Runtime.Serialization.Formatters" />
313-
<Reference Include="System.Text.Encoding.Extensions" />
314319
<Reference Include="System.Text.RegularExpressions" />
315-
<Reference Include="System.Threading" />
316-
<Reference Include="System.Threading.Thread" />
317320
<Reference Include="System.Transactions.Local" />
318321
<Reference Include="System.Xml.ReaderWriter" />
319322
<Reference Include="System.Xml.XmlSerializer" />

src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,9 @@ public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute
406406

407407
if (type == typeof(object))
408408
throw ExceptionBuilder.CanNotDeserializeObjectType();
409+
410+
TypeLimiter.EnsureTypeIsAllowed(type);
411+
409412
if (!isBaseCLRType)
410413
{
411414
retValue = System.Activator.CreateInstance(type, true);

src/libraries/System.Data.Common/src/System/Data/DataColumn.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ public DataColumn(string columnName, Type dataType, string expr, MappingType typ
143143

144144
private void UpdateColumnType(Type type, StorageType typeCode)
145145
{
146+
TypeLimiter.EnsureTypeIsAllowed(type);
146147
_dataType = type;
147148
_storageType = typeCode;
148149
if (StorageType.DateTime != typeCode)

src/libraries/System.Data.Common/src/System/Data/DataException.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ private static void ThrowDataException(string error, Exception innerException)
350350
public static Exception ArgumentOutOfRange(string paramName) => _ArgumentOutOfRange(paramName, SR.Format(SR.Data_ArgumentOutOfRange, paramName));
351351
public static Exception BadObjectPropertyAccess(string error) => _InvalidOperation(SR.Format(SR.DataConstraint_BadObjectPropertyAccess, error));
352352
public static Exception ArgumentContainsNull(string paramName) => _Argument(paramName, SR.Format(SR.Data_ArgumentContainsNull, paramName));
353+
public static Exception TypeNotAllowed(Type type) => _InvalidOperation(SR.Format(SR.Data_TypeNotAllowed, type.AssemblyQualifiedName));
353354

354355

355356
//

src/libraries/System.Data.Common/src/System/Data/DataSet.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,9 +1961,11 @@ private void WriteXmlSchema(XmlWriter writer, SchemaFormat schemaFormat, Convert
19611961

19621962
internal XmlReadMode ReadXml(XmlReader reader, bool denyResolving)
19631963
{
1964+
IDisposable restrictedScope = null;
19641965
long logScopeId = DataCommonEventSource.Log.EnterScope("<ds.DataSet.ReadXml|INFO> {0}, denyResolving={1}", ObjectID, denyResolving);
19651966
try
19661967
{
1968+
restrictedScope = TypeLimiter.EnterRestrictedScope(this);
19671969
DataTable.DSRowDiffIdUsageSection rowDiffIdUsage = default;
19681970
try
19691971
{
@@ -2231,6 +2233,7 @@ internal XmlReadMode ReadXml(XmlReader reader, bool denyResolving)
22312233
}
22322234
finally
22332235
{
2236+
restrictedScope?.Dispose();
22342237
DataCommonEventSource.Log.ExitScope(logScopeId);
22352238
}
22362239
}
@@ -2467,9 +2470,11 @@ private void ReadXmlDiffgram(XmlReader reader)
24672470

24682471
internal XmlReadMode ReadXml(XmlReader reader, XmlReadMode mode, bool denyResolving)
24692472
{
2473+
IDisposable restictedScope = null;
24702474
long logScopeId = DataCommonEventSource.Log.EnterScope("<ds.DataSet.ReadXml|INFO> {0}, mode={1}, denyResolving={2}", ObjectID, mode, denyResolving);
24712475
try
24722476
{
2477+
restictedScope = TypeLimiter.EnterRestrictedScope(this);
24732478
XmlReadMode ret = mode;
24742479

24752480
if (reader == null)
@@ -2711,6 +2716,7 @@ internal XmlReadMode ReadXml(XmlReader reader, XmlReadMode mode, bool denyResolv
27112716
}
27122717
finally
27132718
{
2719+
restictedScope?.Dispose();
27142720
DataCommonEventSource.Log.ExitScope(logScopeId);
27152721
}
27162722
}

src/libraries/System.Data.Common/src/System/Data/DataTable.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5659,9 +5659,11 @@ private bool IsEmptyXml(XmlReader reader)
56595659

56605660
internal XmlReadMode ReadXml(XmlReader reader, bool denyResolving)
56615661
{
5662+
IDisposable restrictedScope = null;
56625663
long logScopeId = DataCommonEventSource.Log.EnterScope("<ds.DataTable.ReadXml|INFO> {0}, denyResolving={1}", ObjectID, denyResolving);
56635664
try
56645665
{
5666+
restrictedScope = TypeLimiter.EnterRestrictedScope(this);
56655667
RowDiffIdUsageSection rowDiffIdUsage = default;
56665668
try
56675669
{
@@ -5896,15 +5898,18 @@ internal XmlReadMode ReadXml(XmlReader reader, bool denyResolving)
58965898
}
58975899
finally
58985900
{
5901+
restrictedScope?.Dispose();
58995902
DataCommonEventSource.Log.ExitScope(logScopeId);
59005903
}
59015904
}
59025905

59035906
internal XmlReadMode ReadXml(XmlReader reader, XmlReadMode mode, bool denyResolving)
59045907
{
5908+
IDisposable restrictedScope = null;
59055909
RowDiffIdUsageSection rowDiffIdUsage = default;
59065910
try
59075911
{
5912+
restrictedScope = TypeLimiter.EnterRestrictedScope(this);
59085913
bool fSchemaFound = false;
59095914
bool fDataFound = false;
59105915
bool fIsXdr = false;
@@ -6190,6 +6195,7 @@ internal XmlReadMode ReadXml(XmlReader reader, XmlReadMode mode, bool denyResolv
61906195
}
61916196
finally
61926197
{
6198+
restrictedScope?.Dispose();
61936199
// prepare and cleanup rowDiffId hashtable
61946200
rowDiffIdUsage.Cleanup();
61956201
}

src/libraries/System.Data.Common/src/System/Data/Filter/FunctionNode.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Data.Common;
77
using System.Data.SqlTypes;
88
using System.Diagnostics;
9+
using System.Runtime.Serialization;
910

1011
namespace System.Data
1112
{
@@ -16,6 +17,7 @@ internal sealed class FunctionNode : ExpressionNode
1617
internal int _argumentCount = 0;
1718
internal const int initialCapacity = 1;
1819
internal ExpressionNode[] _arguments;
20+
private readonly TypeLimiter _capturedLimiter = null;
1921

2022
private static readonly Function[] s_funcs = new Function[] {
2123
new Function("Abs", FunctionId.Abs, typeof(object), true, false, 1, typeof(object), null, null),
@@ -40,6 +42,12 @@ internal sealed class FunctionNode : ExpressionNode
4042

4143
internal FunctionNode(DataTable table, string name) : base(table)
4244
{
45+
// Because FunctionNode instances are created eagerly but evaluated lazily,
46+
// we need to capture the deserialization scope here. The scope could be
47+
// null if no deserialization is in progress.
48+
49+
_capturedLimiter = TypeLimiter.Capture();
50+
4351
_name = name;
4452
for (int i = 0; i < s_funcs.Length; i++)
4553
{
@@ -289,6 +297,11 @@ private Type GetDataType(ExpressionNode node)
289297
throw ExprException.InvalidType(typeName);
290298
}
291299

300+
// ReadXml might not be on the current call stack. So we'll use the TypeLimiter
301+
// that was captured when this FunctionNode instance was created.
302+
303+
TypeLimiter.EnsureTypeIsAllowed(dataType, _capturedLimiter);
304+
292305
return dataType;
293306
}
294307

@@ -494,10 +507,17 @@ private object EvalFunction(FunctionId id, object[] argumentValues, DataRow row,
494507
{
495508
return SqlConvert.ChangeType2((decimal)SqlConvert.ChangeType2(argumentValues[0], StorageType.Decimal, typeof(decimal), FormatProvider), mytype, type, FormatProvider);
496509
}
497-
return SqlConvert.ChangeType2(argumentValues[0], mytype, type, FormatProvider);
498510
}
499511

500-
return SqlConvert.ChangeType2(argumentValues[0], mytype, type, FormatProvider);
512+
// The Convert function can be called lazily, outside of a previous Serialization Guard scope.
513+
// If there was a type limiter scope on the stack at the time this Convert function was created,
514+
// we must manually re-enter the Serialization Guard scope.
515+
516+
DeserializationToken deserializationToken = (_capturedLimiter != null) ? SerializationInfo.StartDeserialization() : default;
517+
using (deserializationToken)
518+
{
519+
return SqlConvert.ChangeType2(argumentValues[0], mytype, type, FormatProvider);
520+
}
501521
}
502522

503523
return argumentValues[0];
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Runtime.CompilerServices;
6+
7+
namespace System
8+
{
9+
internal static partial class LocalAppContextSwitches
10+
{
11+
private static int s_allowArbitraryTypeInstantiation;
12+
public static bool AllowArbitraryTypeInstantiation
13+
{
14+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
15+
get => GetCachedSwitchValue("Switch.System.Data.AllowArbitraryDataSetTypeInstantiation", ref s_allowArbitraryTypeInstantiation);
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)