Skip to content

Commit 34dde0e

Browse files
committed
Invalidate ByRefLikeArguments at end method interception
... and this appears to already catch possible usage mistakes in some unit tests. Those need to be fixed next.
1 parent fa2dd58 commit 34dde0e

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

src/Castle.Core/DynamicProxy/AbstractInvocation.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,26 @@ public void Proceed()
129129
finally
130130
{
131131
currentInterceptorIndex--;
132+
133+
#if FEATURE_BYREFLIKE
134+
if (currentInterceptorIndex < 0)
135+
{
136+
// TODO: This will require further optimization;
137+
// we should not iterate through the arguments array on a hot code path!
138+
for (int i = 0; i < arguments.Length; ++i)
139+
{
140+
if (arguments[i] is ByRefLikeArgument byRefLikeArg)
141+
{
142+
// Invalidate the `ByRefLikeArgument` in case someone copied it away
143+
// (i. e. let it escape the lifetime of the invocation and its arguments on the stack):
144+
byRefLikeArg.Dispose();
145+
146+
// Reset the argument so the `ByRefLikeArgument` becomes eligible for GC sooner:
147+
arguments[i] = null;
148+
}
149+
}
150+
}
151+
#endif
132152
}
133153
}
134154

src/Castle.Core/DynamicProxy/ByRefLikeArgument.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ namespace Castle.DynamicProxy
2828
/// Wraps a byref-like (<c>ref struct</c>) method argument
2929
/// such that it can be placed in the <see cref="IInvocation.Arguments"/> array during interception.
3030
/// </summary>
31-
public unsafe class ByRefLikeArgument
31+
public unsafe class ByRefLikeArgument : IDisposable
3232
{
3333
private static readonly ConcurrentDictionary<Type, ConstructorInfo> constructorMap = new();
3434

@@ -95,12 +95,31 @@ public ByRefLikeArgument(void* ptr)
9595
/// will cause undefined behavior, or an <see cref="AccessViolationException"/> at best.
9696
/// </para>
9797
/// </remarks>
98+
/// <exception cref="ObjectDisposedException" />
9899
[CLSCompliant(false)]
99100
[EditorBrowsable(EditorBrowsableState.Advanced)]
100101
public void* GetPointer()
101102
{
103+
EnsureNotDisposed();
104+
102105
return ptr;
103106
}
107+
108+
public void Dispose()
109+
{
110+
ptr = null;
111+
}
112+
113+
protected void EnsureNotDisposed()
114+
{
115+
if (ptr == null)
116+
{
117+
throw new ObjectDisposedException(
118+
message: "Byref-like method arguments are only available during the method call. "
119+
+ "This reference has been invalidated to prevent potentially unsafe access.",
120+
objectName: null);
121+
}
122+
}
104123
}
105124

106125
#if FEATURE_ALLOWS_REF_STRUCT_ANTI_CONSTRAINT
@@ -124,8 +143,11 @@ public ByRefLikeArgument(void* ptr)
124143
/// <summary>
125144
/// Gets the byref-like (<c>ref struct</c>) argument.
126145
/// </summary>
146+
/// <exception cref="ObjectDisposedException" />
127147
public ref TByRefLike Get()
128148
{
149+
EnsureNotDisposed();
150+
129151
return ref Unsafe.AsRef<TByRefLike>(ptr);
130152
}
131153
}
@@ -165,8 +187,11 @@ public ReadOnlySpanArgument(void* ptr)
165187
/// <summary>
166188
/// Gets the byref-like (<c>ref struct</c>) argument.
167189
/// </summary>
190+
/// <exception cref="ObjectDisposedException" />
168191
public ref ReadOnlySpan<T> Get()
169192
{
193+
EnsureNotDisposed();
194+
170195
#pragma warning disable CS8500
171196
return ref *(ReadOnlySpan<T>*)ptr;
172197
#pragma warning restore CS8500
@@ -199,8 +224,11 @@ public SpanArgument(void* ptr)
199224
/// <summary>
200225
/// Gets the byref-like (<c>ref struct</c>) argument.
201226
/// </summary>
227+
/// <exception cref="ObjectDisposedException" />
202228
public ref Span<T> Get()
203229
{
230+
EnsureNotDisposed();
231+
204232
#pragma warning disable CS8500
205233
return ref *(Span<T>*)ptr;
206234
#pragma warning restore CS8500

0 commit comments

Comments
 (0)