-
Notifications
You must be signed in to change notification settings - Fork 148
Fix: Rabbitmq7 header injection overwrites user supplied basic properties #6730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,43 @@ | ||
// <copyright file="CachedBasicPropertiesHelper.cs" company="Datadog"> | ||
// <copyright file="CachedBasicPropertiesHelper.cs" company="Datadog"> | ||
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. | ||
// </copyright> | ||
|
||
#nullable enable | ||
using System; | ||
using System.Reflection.Emit; | ||
using System.Runtime.InteropServices; | ||
using Datadog.Trace.DuckTyping; | ||
using Datadog.Trace.Util; | ||
|
||
namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.RabbitMQ; | ||
|
||
internal static class CachedBasicPropertiesHelper<TBasicProperties> | ||
{ | ||
// ReSharper disable once StaticMemberInGenericType | ||
private static readonly ActivatorHelper Activator; | ||
private static readonly Func<object, object> Activator; | ||
|
||
static CachedBasicPropertiesHelper() | ||
{ | ||
Activator = new ActivatorHelper(typeof(TBasicProperties).Assembly.GetType("RabbitMQ.Client.BasicProperties")!); | ||
var targetType = typeof(TBasicProperties).Assembly.GetType("RabbitMQ.Client.BasicProperties")!; | ||
var parameterType = typeof(TBasicProperties).Assembly.GetType("RabbitMQ.Client.IReadOnlyBasicProperties")!; | ||
|
||
var constructor = targetType.GetConstructor([parameterType])!; | ||
|
||
var createBasicPropertiesMethod = new DynamicMethod( | ||
$"TypeActivator_{targetType.Name}_{parameterType.Name}", | ||
targetType, | ||
[parameterType], | ||
typeof(DuckType).Module, | ||
true); | ||
|
||
var il = createBasicPropertiesMethod.GetILGenerator(); | ||
il.Emit(OpCodes.Ldarg_0); | ||
il.Emit(OpCodes.Newobj, constructor); | ||
il.Emit(OpCodes.Ret); | ||
Activator = (Func<object, object>)createBasicPropertiesMethod.CreateDelegate(typeof(Func<object, object>), targetType); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @johang88: After some debugging, we figured out that the call to We had to copy your commits into a new branch in the repository to run all our tests: see #6753. Once we get that new PR reviewed and approved, and all tests pass, we will merge that one in favor of this one. Your commits are still attributed to you as the author, though. Thanks for your help with this issue. |
||
|
||
public static TBasicProperties CreateHeaders() | ||
=> (TBasicProperties)Activator.CreateInstance(); | ||
public static TBasicProperties CreateHeaders(object readonlyBasicProperties) | ||
=> (TBasicProperties)Activator(readonlyBasicProperties); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
// <copyright file="PopulateBasicPropertiesHeadersIntegration.cs" company="Datadog"> | ||
// <copyright file="PopulateBasicPropertiesHeadersIntegration.cs" company="Datadog"> | ||
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. | ||
// </copyright> | ||
|
@@ -31,6 +31,12 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.RabbitMQ; | |
[EditorBrowsable(EditorBrowsableState.Never)] | ||
public class PopulateBasicPropertiesHeadersIntegration | ||
{ | ||
internal static CallTargetState OnMethodBegin<TTarget, TBasicProperties, TActivity>(TTarget instance, TBasicProperties basicProperties, TActivity sendActivity, ulong publishSequenceNumber) | ||
where TBasicProperties : IReadOnlyBasicProperties, IDuckType | ||
{ | ||
return new CallTargetState(null, basicProperties); | ||
} | ||
|
||
internal static CallTargetReturn<TReturn?> OnMethodEnd<TTarget, TReturn>(TTarget instance, TReturn returnValue, Exception? exception, in CallTargetState state) | ||
{ | ||
var tracer = Tracer.Instance; | ||
|
@@ -45,7 +51,29 @@ public class PopulateBasicPropertiesHeadersIntegration | |
return new CallTargetReturn<TReturn?>(returnValue); | ||
} | ||
|
||
returnValue ??= CachedBasicPropertiesHelper<TReturn>.CreateHeaders(); | ||
// PopulateBasicPropertiesHeaders returns null if the supplied IReadOnlyBasicProperties | ||
// does not have to be modified or if it's a writable instance. | ||
// If that is the case then we have to fetch IReadOnlyBasicProperties from the argument | ||
// list instead of creating a new instance that overwrites the supplied properties. | ||
if (returnValue is null) | ||
{ | ||
// Use the existing basic properties if possible, | ||
// if not create new instance using the copy constructor on BasicProperties. | ||
if (state.State.DuckIs<IBasicProperties>()) | ||
{ | ||
returnValue = (TReturn)state.State!; | ||
} | ||
else if (state.State.DuckIs<IReadOnlyBasicProperties>()) | ||
{ | ||
returnValue = CachedBasicPropertiesHelper<TReturn>.CreateHeaders(state.State!); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we need to preserve / copy headers here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll take a look and see if I can sort it out. We definitely need to copy the basic properties and headers as they will be dropped completely from the message otherwise. For example the current implementation completely breaks our rabbitmq message sender if instrumentation is enabled as the messageid, timestame, persistance settings will be removed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added a commit that should fix the activator, not super happy about the cast but also not sure how to get rid of it. But I suppose optimizations can come after it's working properly :) |
||
else | ||
{ | ||
// This case cannot happen as argument is not nullable. | ||
return new CallTargetReturn<TReturn?>(returnValue); | ||
} | ||
} | ||
|
||
var duckType = returnValue.DuckCast<IBasicProperties>()!; | ||
|
||
// add distributed tracing headers to the message | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately it looks like this is throwing an exception when attempting to run the tests / sample