Skip to content

Commit 3ee35d5

Browse files
committed
removed cloning of CriticalFinalizerObjects (usually it is SafeHandle). Clone of handles cause unpredictable results
1 parent d2b573e commit 3ee35d5

File tree

6 files changed

+60
-10
lines changed

6 files changed

+60
-10
lines changed

DeepCloner.Tests/SystemTypesSpec.cs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
using System.IO;
44
using System.Reflection;
55
using System.Runtime.InteropServices;
6+
using System.Security.Cryptography.X509Certificates;
67
using System.Text;
78

9+
using Microsoft.Win32.SafeHandles;
10+
811
using NUnit.Framework;
912

1013
namespace Force.DeepCloner.Tests
@@ -50,9 +53,11 @@ public void Type_With_Native_Resource_Should_Be_Cloned()
5053
Console.WriteLine(f2.GetValue(f.GetValue(writer.BaseStream)));
5154
Console.WriteLine(f2.GetValue(f.GetValue(cloned.BaseStream)));
5255
writer.Close();
53-
cloned.Close();
54-
// this was a bug, we should not throw there
55-
// Assert.Throws<ObjectDisposedException>(cloned.Close);
56+
// cloned.Close();
57+
// not a bug anymore, this is feature: we don't duplicate handles, because it can cause unpredictable results
58+
// ~~this was a bug, we should not throw there~~
59+
60+
Assert.Throws<ObjectDisposedException>(cloned.Close);
5661
var res = File.ReadAllText(fileName);
5762
Assert.That(res, Is.EqualTo("123"));
5863
}
@@ -114,5 +119,40 @@ public void Events_Should_Be_Cloned()
114119
Assert.That(summ[0], Is.EqualTo(2)); // nothing to increment
115120
Assert.That(clone.Call(1), Is.EqualTo(2));
116121
}
122+
123+
private const string CertData =
124+
@"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";
125+
126+
[Test(Description = "Without special handling it causes exception on destruction due native resources usage")]
127+
public void Certificate_Should_Be_Cloned()
128+
{
129+
var cert = new X509Certificate2(Convert.FromBase64String(CertData), "1");
130+
cert.DeepClone();
131+
cert.DeepClone();
132+
GC.Collect();
133+
GC.WaitForFullGCComplete();
134+
}
135+
136+
[Test(Description = "Without special handling it causes exception on destruction due native resources usage")]
137+
public void ObjectHandle_Should_Be_Cloned()
138+
{
139+
var cert = new X509Certificate2(Convert.FromBase64String(CertData), "1");
140+
var handle = (SafeHandleZeroOrMinusOneIsInvalid)
141+
cert.GetType().GetField("m_safeCertContext", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(cert);
142+
handle.DeepClone();
143+
handle.DeepClone();
144+
GC.Collect();
145+
GC.WaitForFullGCComplete();
146+
}
147+
148+
[Test(Description = "Without special handling it causes exception on destruction due native resources usage")]
149+
public void Certificate_Should_Be_Shallow_Cloned()
150+
{
151+
var cert = new X509Certificate2(Convert.FromBase64String(CertData), "1");
152+
cert.ShallowClone();
153+
cert.ShallowClone();
154+
GC.Collect();
155+
GC.WaitForFullGCComplete();
156+
}
117157
}
118158
}

DeepCloner.nuspec

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<metadata>
44
<id>DeepCloner</id>
55
<title>DeepCloner</title>
6-
<version>0.9.2</version>
6+
<version>0.9.3</version>
77
<authors>force</authors>
88
<owners>force</owners>
99
<licenseUrl>https://github.com/force-net/DeepCloner/blob/develop/LICENSE</licenseUrl>
@@ -12,10 +12,7 @@
1212
<requireLicenseAcceptance>false</requireLicenseAcceptance>
1313
<description>Small Library for fast deep or shallow cloning .NET objects. It allows to copy everything and has a lot of performance tricks for fast copying.</description>
1414
<releaseNotes>
15-
Seriously improved cloning of simple object (constructor analyzing + reference counting)
16-
Fixed small issue with possible multiple copying of same fields in object
17-
Safe variant now correctly clones readonly fields
18-
Fixed small issue with cloning primitive types casted as an object
15+
Changed behaviour of cloning resources with handles. Now, they don't clone (same object is returned) due unpredictable clone results.
1916
</releaseNotes>
2017
<copyright>Copyright by Force 2016</copyright>
2118
<tags>.NET shallow deep clone DeepClone fast</tags>

DeepCloner/Helpers/DeepClonerExprGenerator.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ internal static class DeepClonerExprGenerator
99
{
1010
internal static object GenerateClonerInternal(Type realType, bool asObject)
1111
{
12+
if (DeepClonerSafeTypes.IsTypeSafe(realType, null)) return null;
13+
1214
return GenerateProcessMethod(realType, asObject && realType.IsValueType);
1315
}
1416

DeepCloner/Helpers/DeepClonerSafeTypes.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Linq;
55
using System.Reflection;
6+
using System.Runtime.ConstrainedExecution;
67

78
namespace Force.DeepCloner.Helpers
89
{
@@ -40,6 +41,13 @@ internal static bool IsTypeSafe(Type type, HashSet<Type> processingTypes)
4041
return true;
4142
}
4243

44+
// this types are serious native resources, it is better not to clone it
45+
if (type.IsSubclassOf(typeof(CriticalFinalizerObject)))
46+
{
47+
KnownTypes.TryAdd(type, true);
48+
return true;
49+
}
50+
4351
// classes are always unsafe (we should copy it fully to count references)
4452
if (!type.IsValueType)
4553
{

DeepCloner/Helpers/ShallowObjectCloner.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq.Expressions;
33
using System.Reflection;
44
using System.Reflection.Emit;
5+
using System.Runtime.ConstrainedExecution;
56

67
namespace Force.DeepCloner.Helpers
78
{
@@ -23,6 +24,8 @@ public static object CloneObject(object obj)
2324
{
2425
if (obj == null) return null;
2526
if (obj is string) return obj;
27+
// do not clone such native-resource bounded types!
28+
if (obj is CriticalFinalizerObject) return obj;
2629
return _instance.DoCloneObject(obj);
2730
}
2831

DeepCloner/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
// by using the '*' as shown below:
3333

3434
[assembly: AssemblyVersion("0.8.0.0")] // change this value only when api is changing
35-
[assembly: AssemblyFileVersion("0.9.2.0")]
36-
[assembly: AssemblyInformationalVersion("0.9.2.0")]
35+
[assembly: AssemblyFileVersion("0.9.3.0")]
36+
[assembly: AssemblyInformationalVersion("0.9.3.0")]
3737

3838
#if !DEBUG
3939
[assembly: AssemblyKeyFileAttribute("..\\public.snk")]

0 commit comments

Comments
 (0)