From d68cb07f361b29ceb2e39983930296c3caa70cd7 Mon Sep 17 00:00:00 2001 From: Anna Date: Sun, 24 Mar 2024 20:32:55 +0100 Subject: [PATCH 01/13] Allocate on the stack --- .../src/Datadog.Trace/AppSec/Waf/Context.cs | 48 +- .../Waf/Initialization/WafConfigurator.cs | 4 +- .../Waf/NativeBindings/WafLibraryInvoker.cs | 124 ++--- .../AppSec/WafEncoding/Encoder.cs | 15 +- .../AppSec/WafEncoding/EncoderLegacy.cs | 516 +++++++++--------- .../AppSec/WafEncoding/IEncodeResult.cs | 4 +- 6 files changed, 362 insertions(+), 349 deletions(-) diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs index 92755374c187..1a6c14cb2105 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs @@ -25,7 +25,7 @@ internal class Context : IContext private readonly Waf _waf; - private readonly List _argCache; + private readonly List _encodeResults; private readonly Stopwatch _stopwatch; private readonly WafLibraryInvoker _wafLibraryInvoker; private readonly IEncoder _encoder; @@ -41,7 +41,7 @@ private Context(IntPtr contextHandle, Waf waf, WafLibraryInvoker wafLibraryInvok _wafLibraryInvoker = wafLibraryInvoker; _encoder = encoder; _stopwatch = new Stopwatch(); - _argCache = new(64); + _encodeResults = new(64); } ~Context() => Dispose(false); @@ -100,29 +100,47 @@ private Context(IntPtr contextHandle, Waf waf, WafLibraryInvoker wafLibraryInvok // Calling _encoder.Encode(null) results in a null object that will cause the WAF to error // The WAF can be called with an empty dictionary (though we should avoid doing this). - var pwPersistentArgs = IntPtr.Zero; + DdwafObjectStruct? pwPersistentArgs = null; if (persistentAddressData != null) { var persistentArgs = _encoder.Encode(persistentAddressData, applySafetyLimits: true); - pwPersistentArgs = persistentArgs.Result; - _argCache.Add(persistentArgs); + pwPersistentArgs = persistentArgs.ResultDdwafObject; + _encodeResults.Add(persistentArgs); } // pwEphemeralArgs follow a different lifecycle and should be disposed immediately - using var ephemeralArgs = - ephemeralAddressData is { Count: > 0 } - ? _encoder.Encode(ephemeralAddressData, applySafetyLimits: true) - : null; - var pwEphemeralArgs = ephemeralArgs?.Result ?? IntPtr.Zero; + using var ephemeralArgs = ephemeralAddressData is { Count: > 0 } + ? _encoder.Encode(ephemeralAddressData, applySafetyLimits: true) + : null; + var pwEphemeralArgs = ephemeralArgs?.ResultDdwafObject; - if (pwPersistentArgs == IntPtr.Zero && pwEphemeralArgs == IntPtr.Zero) + if (pwPersistentArgs is null && pwEphemeralArgs is null) { Log.Error("Both pwPersistentArgs and pwEphemeralArgs are null"); return null; } - // WARNING: DO NOT DISPOSE pwPersistentArgs until the end of this class's lifecycle, i.e in the dispose. Otherwise waf might crash with fatal exception. - code = _waf.Run(_contextHandle, pwPersistentArgs, pwEphemeralArgs, ref retNative, timeoutMicroSeconds); + unsafe + { + var pwEphemeralArgsValue = IntPtr.Zero; + if (pwEphemeralArgs.HasValue) + { + var val = pwEphemeralArgs.Value; + var pointer = &val; + pwEphemeralArgsValue = (IntPtr)pointer; + } + + var pwPersistentArgsValue = IntPtr.Zero; + if (pwPersistentArgs.HasValue) + { + var val = pwPersistentArgs.Value; + var pointer = &val; + pwPersistentArgsValue = (IntPtr)pointer; + } + + // WARNING: DO NOT DISPOSE pwPersistentArgs until the end of this class's lifecycle, i.e in the dispose. Otherwise waf might crash with fatal exception. + code = _waf.Run(_contextHandle, pwPersistentArgsValue, pwEphemeralArgsValue, ref retNative, timeoutMicroSeconds); + } } _stopwatch.Stop(); @@ -151,9 +169,9 @@ public void Dispose(bool disposing) _disposed = true; // WARNING do not move this above, this should only be disposed in the end of the context's life - foreach (var arg in _argCache) + foreach (var encodeResult in _encodeResults) { - arg.Dispose(); + encodeResult.Dispose(); } lock (_stopwatch) diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs index 7287b8efb10e..a9ee7513ee49 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs @@ -112,9 +112,9 @@ private static void LogRuleDetailsIfDebugEnabled(JToken root) return root; } - internal InitResult Configure(IntPtr rulesObj, IEncoder encoder, DdwafConfigStruct configStruct, ref DdwafObjectStruct diagnostics, string? rulesFile) + internal InitResult Configure(DdwafObjectStruct rulesObj, IEncoder encoder, DdwafConfigStruct configStruct, ref DdwafObjectStruct diagnostics, string? rulesFile) { - var wafHandle = _wafLibraryInvoker.Init(rulesObj, ref configStruct, ref diagnostics); + var wafHandle = _wafLibraryInvoker.Init(ref rulesObj, ref configStruct, ref diagnostics); if (wafHandle == IntPtr.Zero) { Log.Warning("DDAS-0005-00: WAF initialization failed."); diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs b/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs index 37a7d7eb386f..4ea7e79c38df 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs @@ -9,6 +9,7 @@ using Datadog.Trace.AppSec.Waf.Initialization; using Datadog.Trace.Configuration; using Datadog.Trace.Logging; +#pragma warning disable SA1401 namespace Datadog.Trace.AppSec.Waf.NativeBindings { @@ -19,7 +20,6 @@ internal class WafLibraryInvoker #else private const string DllName = "ddwaf"; #endif - private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(WafLibraryInvoker)); private readonly GetVersionDelegate _getVersionField; private readonly InitDelegate _initField; @@ -41,12 +41,13 @@ internal class WafLibraryInvoker private readonly ObjectMapAddDelegateX64 _objectMapAddFieldX64; private readonly ObjectMapAddDelegateX86 _objectMapAddFieldX86; private readonly FreeResultDelegate _freeResultField; - private readonly FreeObjectDelegate _freeObjectield; + private readonly FreeObjectDelegate _freeObjectField; private readonly IntPtr _freeObjectFuncField; private readonly SetupLoggingDelegate _setupLogging; private readonly SetupLogCallbackDelegate _setupLogCallbackField; private readonly UpdateDelegate _updateField; private string _version = null; + internal static int SizeOfDdWafObject = Marshal.SizeOf(typeof(DdwafObjectStruct)); private WafLibraryInvoker(IntPtr libraryHandle) { @@ -72,7 +73,7 @@ private WafLibraryInvoker(IntPtr libraryHandle) Environment.Is64BitProcess ? GetDelegateForNativeFunction(libraryHandle, "ddwaf_object_map_addl") : null; _objectMapAddFieldX86 = Environment.Is64BitProcess ? null : GetDelegateForNativeFunction(libraryHandle, "ddwaf_object_map_addl"); - _freeObjectield = GetDelegateForNativeFunction(libraryHandle, "ddwaf_object_free", out _freeObjectFuncField); + _freeObjectField = GetDelegateForNativeFunction(libraryHandle, "ddwaf_object_free", out _freeObjectFuncField); _freeResultField = GetDelegateForNativeFunction(libraryHandle, "ddwaf_result_free"); _getVersionField = GetDelegateForNativeFunction(libraryHandle, "ddwaf_get_version"); // setup logging @@ -85,9 +86,9 @@ private WafLibraryInvoker(IntPtr libraryHandle) private delegate void FreeResultDelegate(ref DdwafResultStruct output); - private delegate IntPtr InitDelegate(IntPtr wafRule, ref DdwafConfigStruct config, ref DdwafObjectStruct diagnostics); + private delegate IntPtr InitDelegate(ref DdwafObjectStruct wafRule, ref DdwafConfigStruct config, ref DdwafObjectStruct diagnostics); - private delegate IntPtr UpdateDelegate(IntPtr oldWafHandle, IntPtr wafRule, ref DdwafObjectStruct diagnostics); + private delegate IntPtr UpdateDelegate(IntPtr oldWafHandle, ref DdwafObjectStruct wafRule, ref DdwafObjectStruct diagnostics); private delegate IntPtr InitContextDelegate(IntPtr wafHandle); @@ -97,31 +98,31 @@ private WafLibraryInvoker(IntPtr libraryHandle) private delegate void ContextDestroyDelegate(IntPtr context); - private delegate IntPtr ObjectInvalidDelegate(IntPtr emptyObjPtr); + private delegate IntPtr ObjectInvalidDelegate(ref DdwafObjectStruct emptyObjPtr); - private delegate IntPtr ObjectStringLengthDelegate(IntPtr emptyObjPtr, string s, ulong length); + private delegate IntPtr ObjectStringLengthDelegate(ref DdwafObjectStruct emptyObjPtr, string s, ulong length); - private delegate IntPtr ObjectBoolDelegate(IntPtr emptyObjPtr, bool b); + private delegate IntPtr ObjectBoolDelegate(ref DdwafObjectStruct emptyObjPtr, bool b); - private delegate IntPtr ObjectDoubleDelegate(IntPtr emptyObjPtr, double value); + private delegate IntPtr ObjectDoubleDelegate(ref DdwafObjectStruct emptyObjPtr, double value); - private delegate IntPtr ObjectNullDelegate(IntPtr emptyObjPtr); + private delegate IntPtr ObjectNullDelegate(ref DdwafObjectStruct emptyObjPtr); - private delegate IntPtr ObjectUlongDelegate(IntPtr emptyObjPtr, ulong value); + private delegate IntPtr ObjectUlongDelegate(ref DdwafObjectStruct emptyObjPtr, ulong value); - private delegate IntPtr ObjectLongDelegate(IntPtr emptyObjPtr, long value); + private delegate IntPtr ObjectLongDelegate(ref DdwafObjectStruct emptyObjPtr, long value); - private delegate IntPtr ObjectArrayDelegate(IntPtr emptyObjPtr); + private delegate IntPtr ObjectArrayDelegate(ref DdwafObjectStruct emptyObjPtr); - private delegate IntPtr ObjectMapDelegate(IntPtr emptyObjPtr); + private delegate IntPtr ObjectMapDelegate(ref DdwafObjectStruct emptyObjPtr); - private delegate bool ObjectArrayAddDelegate(IntPtr array, IntPtr entry); + private delegate bool ObjectArrayAddDelegate(ref DdwafObjectStruct array, ref DdwafObjectStruct entry); - private delegate IntPtr ObjectArrayGetAtIndexDelegate(IntPtr array, long index); + private delegate IntPtr ObjectArrayGetAtIndexDelegate(ref DdwafObjectStruct array, long index); - private delegate bool ObjectMapAddDelegateX64(IntPtr map, string entryName, ulong entryNameLength, IntPtr entry); + private delegate bool ObjectMapAddDelegateX64(ref DdwafObjectStruct map, string entryName, ulong entryNameLength, ref DdwafObjectStruct entry); - private delegate bool ObjectMapAddDelegateX86(IntPtr map, string entryName, uint entryNameLength, IntPtr entry); + private delegate bool ObjectMapAddDelegateX86(ref DdwafObjectStruct map, string entryName, uint entryNameLength, ref DdwafObjectStruct entry); private delegate void FreeObjectDelegate(IntPtr input); @@ -245,7 +246,7 @@ internal string GetVersion() return _version; } - internal IntPtr Init(IntPtr wafRule, ref DdwafConfigStruct config, ref DdwafObjectStruct diagnostics) => _initField(wafRule, ref config, ref diagnostics); + internal IntPtr Init(ref DdwafObjectStruct wafRule, ref DdwafConfigStruct config, ref DdwafObjectStruct diagnostics) => _initField(ref wafRule, ref config, ref diagnostics); /// /// Only give a non null ruleSetInfo when updating rules. When updating rules overrides, rules datas, the ruleSetInfo will return no error and no diagnostics, even if there are, it's misleading, so give null in this case. @@ -254,7 +255,7 @@ internal string GetVersion() /// a pointer to the new waf data (rules or overrides or other) /// errors and diagnostics of the update, only for valid for new rules /// the new waf handle, if error, will be a nullptr - internal IntPtr Update(IntPtr oldWafHandle, IntPtr wafData, ref DdwafObjectStruct diagnostics) => _updateField(oldWafHandle, wafData, ref diagnostics); + internal IntPtr Update(IntPtr oldWafHandle, ref DdwafObjectStruct wafData, ref DdwafObjectStruct diagnostics) => _updateField(oldWafHandle, ref wafData, ref diagnostics); internal IntPtr InitContext(IntPtr powerwafHandle) => _initContextField(powerwafHandle); @@ -274,80 +275,79 @@ internal WafReturnCode Run(IntPtr context, IntPtr rawPersistentData, IntPtr rawE internal void ContextDestroy(IntPtr handle) => _contextDestroyField(handle); - internal IntPtr ObjectArrayGetIndex(IntPtr array, long index) => _objectArrayGetIndex(array, index); + internal IntPtr ObjectArrayGetIndex(ref DdwafObjectStruct array, long index) => _objectArrayGetIndex(ref array, index); - internal IntPtr ObjectInvalid() + internal DdwafObjectStruct ObjectInvalid() { - var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DdwafObjectStruct))); - _objectInvalidField(ptr); - return ptr; + var item = new DdwafObjectStruct(); + _objectInvalidField(ref item); + return item; } - internal IntPtr ObjectStringLength(string s, ulong length) + internal DdwafObjectStruct ObjectStringLength(string s, ulong length) { - var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DdwafObjectStruct))); - _objectStringLengthField(ptr, s, length); - return ptr; + var item = new DdwafObjectStruct(); + _objectStringLengthField(ref item, s, length); + return item; } - internal IntPtr ObjectBool(bool b) + internal DdwafObjectStruct ObjectBool(bool b) { - var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DdwafObjectStruct))); - _objectBoolField(ptr, b); - return ptr; + var item = new DdwafObjectStruct(); + _objectBoolField(ref item, b); + return item; } - internal IntPtr ObjectLong(long l) + internal DdwafObjectStruct ObjectLong(long l) { - var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DdwafObjectStruct))); - _objectLongField(ptr, l); - return ptr; + var item = new DdwafObjectStruct(); + _objectLongField(ref item, l); + return item; } - internal IntPtr ObjectUlong(ulong l) + internal DdwafObjectStruct ObjectUlong(ulong l) { - var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DdwafObjectStruct))); - _objectUlongField(ptr, l); - return ptr; + var item = new DdwafObjectStruct(); + _objectUlongField(ref item, l); + return item; } - internal IntPtr ObjectDouble(double b) + internal DdwafObjectStruct ObjectDouble(double b) { - var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DdwafObjectStruct))); - _objectDoubleField(ptr, b); - return ptr; + var item = new DdwafObjectStruct(); + _objectDoubleField(ref item, b); + return item; } - internal IntPtr ObjectNull() + internal DdwafObjectStruct ObjectNull() { - var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DdwafObjectStruct))); - _objectNullField(ptr); - return ptr; + var item = new DdwafObjectStruct(); + _objectNullField(ref item); + return item; } - internal IntPtr ObjectArray() + internal DdwafObjectStruct ObjectArray() { - var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DdwafObjectStruct))); - _objectArrayField(ptr); - return ptr; + var item = new DdwafObjectStruct(); + _objectArrayField(ref item); + return item; } - internal IntPtr ObjectMap() + internal DdwafObjectStruct ObjectMap() { - var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DdwafObjectStruct))); - _objectMapField(ptr); - return ptr; + var item = new DdwafObjectStruct(); + _objectMapField(ref item); + return item; } - internal bool ObjectArrayAdd(IntPtr array, IntPtr entry) => _objectArrayAddField(array, entry); + internal bool ObjectArrayAdd(ref DdwafObjectStruct array, ref DdwafObjectStruct entry) => _objectArrayAddField(ref array, ref entry); // Setting entryNameLength to 0 will result in the entryName length being re-computed with strlen - internal bool ObjectMapAdd(IntPtr map, string entryName, ulong entryNameLength, IntPtr entry) => Environment.Is64BitProcess ? _objectMapAddFieldX64!(map, entryName, entryNameLength, entry) : _objectMapAddFieldX86!(map, entryName, (uint)entryNameLength, entry); + internal bool ObjectMapAdd(ref DdwafObjectStruct map, string entryName, ulong entryNameLength, ref DdwafObjectStruct entry) => Environment.Is64BitProcess ? _objectMapAddFieldX64!(ref map, entryName, entryNameLength, ref entry) : _objectMapAddFieldX86!(ref map, entryName, (uint)entryNameLength, ref entry); - internal void ObjectFreePtr(ref IntPtr input) + internal void ObjectFreePtr(IntPtr input) { - _freeObjectield(input); - input = IntPtr.Zero; + _freeObjectField(input); } internal void ResultFree(ref DdwafResultStruct output) => _freeResultField(ref output); diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/Encoder.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/Encoder.cs index e22ca5e36c11..66356fa52986 100644 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/Encoder.cs +++ b/tracer/src/Datadog.Trace/AppSec/WafEncoding/Encoder.cs @@ -68,7 +68,8 @@ public IEncodeResult Encode(TInstance? o, int remainingDepth = WafCon { var lstPointers = new List(); var pool = Pool; - return new EncodeResult(lstPointers, pool, Encode(o, lstPointers, remainingDepth, key, applySafetyLimits, pool: pool)); + var result = Encode(o, lstPointers, remainingDepth, key, applySafetyLimits, pool: pool); + return new EncodeResult(lstPointers, pool, ref result); } public unsafe DdwafObjectStruct Encode(TInstance? o, List argToFree, int remainingDepth = WafConstants.MaxContainerDepth, string? key = null, bool applySafetyLimits = true, UnmanagedMemoryPool? pool = null) @@ -648,23 +649,19 @@ public class EncodeResult : IEncodeResult { private readonly List _pointers; private readonly UnmanagedMemoryPool _innerPool; - private readonly GCHandle _handle; + private DdwafObjectStruct _result; - internal EncodeResult(List pointers, UnmanagedMemoryPool pool, DdwafObjectStruct result) + internal EncodeResult(List pointers, UnmanagedMemoryPool pool, ref DdwafObjectStruct result) { _pointers = pointers; _innerPool = pool; - ResultDdwafObject = result; - _handle = GCHandle.Alloc(result, GCHandleType.Pinned); + _result = result; } - public IntPtr Result => _handle.AddrOfPinnedObject(); - - public DdwafObjectStruct ResultDdwafObject { get; } + public ref DdwafObjectStruct ResultDdwafObject => ref _result; public void Dispose() { - _handle.Free(); _innerPool.Return(_pointers); _pointers.Clear(); } diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs index 19adad530e3e..d91b9b2e2aed 100644 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs +++ b/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs @@ -19,347 +19,347 @@ using Datadog.Trace.Vendors.Newtonsoft.Json.Linq; using Datadog.Trace.Vendors.Serilog.Events; -namespace Datadog.Trace.AppSec.WafEncoding +namespace Datadog.Trace.AppSec.WafEncoding; + +internal class EncoderLegacy : IEncoder { - internal class EncoderLegacy : IEncoder + private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(EncoderLegacy)); + private readonly WafLibraryInvoker _wafLibraryInvoker; + + public EncoderLegacy(WafLibraryInvoker wafLibraryInvoker) { - private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(EncoderLegacy)); - private readonly WafLibraryInvoker _wafLibraryInvoker; + _wafLibraryInvoker = wafLibraryInvoker; + } - public EncoderLegacy(WafLibraryInvoker wafLibraryInvoker) + public static ObjType DecodeArgsType(DDWAF_OBJ_TYPE t) + { + return t switch { - _wafLibraryInvoker = wafLibraryInvoker; - } + DDWAF_OBJ_TYPE.DDWAF_OBJ_INVALID => ObjType.Invalid, + DDWAF_OBJ_TYPE.DDWAF_OBJ_SIGNED => ObjType.SignedNumber, + DDWAF_OBJ_TYPE.DDWAF_OBJ_UNSIGNED => ObjType.UnsignedNumber, + DDWAF_OBJ_TYPE.DDWAF_OBJ_STRING => ObjType.String, + DDWAF_OBJ_TYPE.DDWAF_OBJ_BOOL => ObjType.Bool, + DDWAF_OBJ_TYPE.DDWAF_OBJ_DOUBLE => ObjType.Double, + DDWAF_OBJ_TYPE.DDWAF_OBJ_ARRAY => ObjType.Array, + DDWAF_OBJ_TYPE.DDWAF_OBJ_MAP => ObjType.Map, + DDWAF_OBJ_TYPE.DDWAF_OBJ_NULL => ObjType.Null, + _ => throw new Exception($"Invalid DDWAF_INPUT_TYPE {t}") + }; + } + + private static string TruncateLongString(string s) => s.Length > WafConstants.MaxStringLength ? s.Substring(0, WafConstants.MaxStringLength) : s; - public static ObjType DecodeArgsType(DDWAF_OBJ_TYPE t) + public IEncodeResult Encode(TInstance? o, int remainingDepth = WafConstants.MaxContainerDepth, string? key = null, bool applySafetyLimits = true) + { + var result = EncodeInternal(o, remainingDepth, applySafetyLimits, _wafLibraryInvoker, parentObj: true); + return new EncodeResult(result, _wafLibraryInvoker); + } + + private static Obj EncodeUnknownType(object? o, WafLibraryInvoker wafLibraryInvoker) + { + if (Log.IsEnabled(LogEventLevel.Debug)) { - return t switch - { - DDWAF_OBJ_TYPE.DDWAF_OBJ_INVALID => ObjType.Invalid, - DDWAF_OBJ_TYPE.DDWAF_OBJ_SIGNED => ObjType.SignedNumber, - DDWAF_OBJ_TYPE.DDWAF_OBJ_UNSIGNED => ObjType.UnsignedNumber, - DDWAF_OBJ_TYPE.DDWAF_OBJ_STRING => ObjType.String, - DDWAF_OBJ_TYPE.DDWAF_OBJ_BOOL => ObjType.Bool, - DDWAF_OBJ_TYPE.DDWAF_OBJ_DOUBLE => ObjType.Double, - DDWAF_OBJ_TYPE.DDWAF_OBJ_ARRAY => ObjType.Array, - DDWAF_OBJ_TYPE.DDWAF_OBJ_MAP => ObjType.Map, - DDWAF_OBJ_TYPE.DDWAF_OBJ_NULL => ObjType.Null, - _ => throw new Exception($"Invalid DDWAF_INPUT_TYPE {t}") - }; + Log.Debug("Couldn't encode object of unknown type {Type}, falling back to ToString", o?.GetType()); } - private static string TruncateLongString(string s) => s.Length > WafConstants.MaxStringLength ? s.Substring(0, WafConstants.MaxStringLength) : s; + var s = o?.ToString() ?? string.Empty; + + return CreateNativeString(s, applyLimits: true, wafLibraryInvoker); + } - public IEncodeResult Encode(TInstance? o, int remainingDepth = WafConstants.MaxContainerDepth, string? key = null, bool applySafetyLimits = true) + private static Obj EncodeInternal(T o, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker, bool parentObj = false) + { + object args = o!; + var value = + args switch + { + null => CreateNativeNull(wafLibraryInvoker), + string s => CreateNativeString(s, applyLimits, wafLibraryInvoker), + JValue jv => CreateNativeString(jv.Value?.ToString() ?? string.Empty, applyLimits, wafLibraryInvoker), + int i => CreateNativeLong(i, wafLibraryInvoker), + uint i => CreateNativeUlong(i, wafLibraryInvoker), + long i => CreateNativeLong(i, wafLibraryInvoker), + ulong i => CreateNativeUlong(i, wafLibraryInvoker), + float i => CreateNativeDouble(i, wafLibraryInvoker), + double i => CreateNativeDouble(i, wafLibraryInvoker), + decimal i => CreateNativeDouble((double)i, wafLibraryInvoker), + bool b => CreateNativeBool(b, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable>> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable>> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IEnumerable> objDict => EncodeDictionary(objDict, remainingDepth, applyLimits, wafLibraryInvoker), + IList objs => EncodeList(objs, remainingDepth, applyLimits, wafLibraryInvoker), + IList objs => EncodeList(objs, remainingDepth, applyLimits, wafLibraryInvoker), + IList objs => EncodeList(objs, remainingDepth, applyLimits, wafLibraryInvoker), + IList objs => EncodeList(objs, remainingDepth, applyLimits, wafLibraryInvoker), + IList objs => EncodeList(objs, remainingDepth, applyLimits, wafLibraryInvoker), + IList objs => EncodeList(objs, remainingDepth, applyLimits, wafLibraryInvoker), + IList objs => EncodeList(objs, remainingDepth, applyLimits, wafLibraryInvoker), + IList objs => EncodeList(objs, remainingDepth, applyLimits, wafLibraryInvoker), + IList objs => EncodeList(objs, remainingDepth, applyLimits, wafLibraryInvoker), + IList objs => EncodeList(objs, remainingDepth, applyLimits, wafLibraryInvoker), + ArrayList objs => EncodeList(objs.ToArray(), remainingDepth, applyLimits, wafLibraryInvoker), + _ => EncodeUnknownType(args, wafLibraryInvoker) + }; + + if (parentObj) { - var argCache = new List(); - var result = EncodeInternal(o, argCache, remainingDepth, applySafetyLimits, _wafLibraryInvoker); - return new EncodeResult(result, argCache); + return new Obj(ref value.InnerStruct, parentObj); } - private static Obj EncodeUnknownType(object o, WafLibraryInvoker wafLibraryInvoker) + return value; + } + + private static Obj EncodeList(IEnumerable objEnumerator, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) + { + var arrNat = wafLibraryInvoker.ObjectArray(); + + if (applyLimits && remainingDepth-- <= 0) { + TelemetryFactory.Metrics.RecordCountInputTruncated(MetricTags.TruncationReason.ObjectTooDeep); if (Log.IsEnabled(LogEventLevel.Debug)) { - Log.Debug("Couldn't encode object of unknown type {Type}, falling back to ToString", o.GetType()); + Log.Debug("EncodeList: object graph too deep, truncating nesting {Items}", string.Join(", ", objEnumerator)); } - var s = o.ToString() ?? string.Empty; - - return CreateNativeString(s, applyLimits: true, wafLibraryInvoker); + return new Obj(ref arrNat); } - private static Obj EncodeInternal(T o, List? argCache, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) + var count = objEnumerator is IList objs ? objs.Count : objEnumerator.Count(); + if (applyLimits && count > WafConstants.MaxContainerSize) { - object args = o!; - if (o is ArrayList arrayList) + TelemetryFactory.Metrics.RecordCountInputTruncated(MetricTags.TruncationReason.ListOrMapTooLarge); + if (Log.IsEnabled(LogEventLevel.Debug)) { - var list = new List(); - foreach (var item in arrayList) - { - if (item is not null) - { - list.Add(item); - } - } - - args = list; + Log.Debug("EncodeList: list too long, it will be truncated, count: {Count}, MaxMapOrArrayLength {MaxMapOrArrayLength}", count, WafConstants.MaxContainerSize); } - var value = - args switch - { - null => CreateNativeNull(wafLibraryInvoker), - string s => CreateNativeString(s, applyLimits, wafLibraryInvoker), - JValue jv => CreateNativeString(jv.Value?.ToString() ?? string.Empty, applyLimits, wafLibraryInvoker), - int i => CreateNativeLong(i, wafLibraryInvoker), - uint i => CreateNativeUlong(i, wafLibraryInvoker), - long i => CreateNativeLong(i, wafLibraryInvoker), - ulong i => CreateNativeUlong(i, wafLibraryInvoker), - float i => CreateNativeDouble(i, wafLibraryInvoker), - double i => CreateNativeDouble(i, wafLibraryInvoker), - decimal i => CreateNativeDouble((double)i, wafLibraryInvoker), - bool b => CreateNativeBool(b, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable>> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable>> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IEnumerable> objDict => EncodeDictionary(objDict, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IList objs => EncodeList(objs, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IList objs => EncodeList(objs, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IList objs => EncodeList(objs, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IList objs => EncodeList(objs, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IList objs => EncodeList(objs, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IList objs => EncodeList(objs, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IList objs => EncodeList(objs, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IList objs => EncodeList(objs, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IList objs => EncodeList(objs, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - IList objs => EncodeList(objs, argCache, remainingDepth, applyLimits, wafLibraryInvoker), - ArrayList objs => EncodeList(objs.ToArray(), argCache, remainingDepth, applyLimits, wafLibraryInvoker), - _ => EncodeUnknownType(args, wafLibraryInvoker) - }; - - argCache?.Add(value); - - return value; + objEnumerator = objEnumerator.Take(WafConstants.MaxContainerSize); } - private static Obj EncodeList(IEnumerable objEnumerator, List? argCache, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) + foreach (var o in objEnumerator) { - var arrNat = wafLibraryInvoker.ObjectArray(); + var value = EncodeInternal(o, remainingDepth, applyLimits, wafLibraryInvoker); + var innerStruct = value.InnerStruct; + wafLibraryInvoker.ObjectArrayAdd(ref arrNat, ref innerStruct); + } - if (applyLimits && remainingDepth-- <= 0) - { - TelemetryFactory.Metrics.RecordCountInputTruncated(MetricTags.TruncationReason.ObjectTooDeep); - if (Log.IsEnabled(LogEventLevel.Debug)) - { - Log.Debug("EncodeList: object graph too deep, truncating nesting {Items}", string.Join(", ", objEnumerator)); - } + return new Obj(ref arrNat); + } - return new Obj(arrNat); - } + private static Obj EncodeDictionary(IEnumerable> objDictEnumerator, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) + { + var mapNat = wafLibraryInvoker.ObjectMap(); - var count = objEnumerator is IList objs ? objs.Count : objEnumerator.Count(); - if (applyLimits && count > WafConstants.MaxContainerSize) + if (applyLimits && remainingDepth-- <= 0) + { + TelemetryFactory.Metrics.RecordCountInputTruncated(MetricTags.TruncationReason.ObjectTooDeep); + if (Log.IsEnabled(LogEventLevel.Debug)) { - TelemetryFactory.Metrics.RecordCountInputTruncated(MetricTags.TruncationReason.ListOrMapTooLarge); - if (Log.IsEnabled(LogEventLevel.Debug)) - { - Log.Debug("EncodeList: list too long, it will be truncated, count: {Count}, MaxMapOrArrayLength {MaxMapOrArrayLength}", count, WafConstants.MaxContainerSize); - } - - objEnumerator = objEnumerator.Take(WafConstants.MaxContainerSize); + Log.Debug("EncodeDictionary: object graph too deep, truncating nesting {Items}", string.Join(", ", objDictEnumerator.Select(x => $"{x.Key}, {x.Value}"))); } - foreach (var o in objEnumerator) + return new Obj(ref mapNat); + } + + var count = objDictEnumerator is IDictionary objDict ? objDict.Count : objDictEnumerator.Count(); + + if (applyLimits && count > WafConstants.MaxContainerSize) + { + TelemetryFactory.Metrics.RecordCountInputTruncated(MetricTags.TruncationReason.ListOrMapTooLarge); + if (Log.IsEnabled(LogEventLevel.Debug)) { - var value = EncodeInternal(o, argCache, remainingDepth, applyLimits, wafLibraryInvoker); - wafLibraryInvoker.ObjectArrayAdd(arrNat, value.RawPtr); + Log.Debug("EncodeDictionary: list too long, it will be truncated, count: {Count}, MaxMapOrArrayLength {MaxMapOrArrayLength}", count, WafConstants.MaxContainerSize); } - return new Obj(arrNat); + objDictEnumerator = objDictEnumerator.Take(WafConstants.MaxContainerSize); } - private static Obj EncodeDictionary(IEnumerable> objDictEnumerator, List? argCache, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) + foreach (var o in objDictEnumerator) { - var mapNat = wafLibraryInvoker.ObjectMap(); - - if (applyLimits && remainingDepth-- <= 0) + var name = o.Key; + if (name != null) { - TelemetryFactory.Metrics.RecordCountInputTruncated(MetricTags.TruncationReason.ObjectTooDeep); - if (Log.IsEnabled(LogEventLevel.Debug)) - { - Log.Debug("EncodeDictionary: object graph too deep, truncating nesting {Items}", string.Join(", ", objDictEnumerator.Select(x => $"{x.Key}, {x.Value}"))); - } - - return new Obj(mapNat); + var value = EncodeInternal(o.Value, remainingDepth, applyLimits, wafLibraryInvoker); + wafLibraryInvoker.ObjectMapAdd(ref mapNat, name, Convert.ToUInt64(name.Length), ref value.InnerStruct); } - - var count = objDictEnumerator is IDictionary objDict ? objDict.Count : objDictEnumerator.Count(); - - if (applyLimits && count > WafConstants.MaxContainerSize) + else { - TelemetryFactory.Metrics.RecordCountInputTruncated(MetricTags.TruncationReason.ListOrMapTooLarge); if (Log.IsEnabled(LogEventLevel.Debug)) { - Log.Debug("EncodeDictionary: list too long, it will be truncated, count: {Count}, MaxMapOrArrayLength {MaxMapOrArrayLength}", count, WafConstants.MaxContainerSize); + Log.Debug("EncodeDictionary: ignoring dictionary member with null name"); } - - objDictEnumerator = objDictEnumerator.Take(WafConstants.MaxContainerSize); } + } - foreach (var o in objDictEnumerator) - { - var name = o.Key; - if (name != null) - { - var value = EncodeInternal(o.Value, argCache, remainingDepth, applyLimits, wafLibraryInvoker); - wafLibraryInvoker.ObjectMapAdd(mapNat, name, Convert.ToUInt64(name.Length), value.RawPtr); - } - else - { - if (Log.IsEnabled(LogEventLevel.Debug)) - { - Log.Debug("EncodeDictionary: ignoring dictionary member with null name"); - } - } - } + return new Obj(ref mapNat); + } - return new Obj(mapNat); - } + private static Obj CreateNativeString(string s, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) + { + var encodeString = + applyLimits + ? TruncateLongString(s) + : s; + var objectStringLength = wafLibraryInvoker.ObjectStringLength(encodeString, Convert.ToUInt64(encodeString.Length)); + return new Obj(ref objectStringLength); + } - private static Obj CreateNativeString(string s, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) - { - var encodeString = - applyLimits - ? TruncateLongString(s) - : s; - return new Obj(wafLibraryInvoker.ObjectStringLength(encodeString, Convert.ToUInt64(encodeString.Length))); - } + private static Obj CreateNativeBool(bool b, WafLibraryInvoker wafLibraryInvoker) + { + var ddwafObjectStruct = wafLibraryInvoker.ObjectBool(b); + return new(ref ddwafObjectStruct); + } - private static Obj CreateNativeBool(bool b, WafLibraryInvoker wafLibraryInvoker) => new(wafLibraryInvoker.ObjectBool(b)); + private static Obj CreateNativeLong(long value, WafLibraryInvoker wafLibraryInvoker) + { + var ddwafObjectStruct = wafLibraryInvoker.ObjectLong(value); + return new(ref ddwafObjectStruct); + } - private static Obj CreateNativeLong(long value, WafLibraryInvoker wafLibraryInvoker) => new(wafLibraryInvoker.ObjectLong(value)); + private static Obj CreateNativeNull(WafLibraryInvoker wafLibraryInvoker) + { + var ddwafObjectStruct = wafLibraryInvoker.ObjectNull(); + return new(ref ddwafObjectStruct); + } - private static Obj CreateNativeNull(WafLibraryInvoker wafLibraryInvoker) => new(wafLibraryInvoker.ObjectNull()); + private static Obj CreateNativeUlong(ulong value, WafLibraryInvoker wafLibraryInvoker) + { + var ddwafObjectStruct = wafLibraryInvoker.ObjectUlong(value); + return new(ref ddwafObjectStruct); + } - private static Obj CreateNativeUlong(ulong value, WafLibraryInvoker wafLibraryInvoker) => new(wafLibraryInvoker.ObjectUlong(value)); + private static Obj CreateNativeDouble(double value, WafLibraryInvoker wafLibraryInvoker) + { + var ddwafObjectStruct = wafLibraryInvoker.ObjectDouble(value); + return new(ref ddwafObjectStruct); + } - private static Obj CreateNativeDouble(double value, WafLibraryInvoker wafLibraryInvoker) => new(wafLibraryInvoker.ObjectDouble(value)); + public static string FormatArgs(object o) + { + // zero capacity because we don't know the size in advance + var sb = StringBuilderCache.Acquire(0); + FormatArgsInternal(o, sb); + return StringBuilderCache.GetStringAndRelease(sb); + } - public static string FormatArgs(object o) + private static void FormatArgsInternal(object o, StringBuilder sb) + { + _ = + o switch + { + string s => sb.Append(s), + int i => sb.Append(i), + float i => sb.Append(i), + long i => sb.Append(i), + uint i => sb.Append(i), + ulong i => sb.Append(i), + double i => sb.Append(i), + IEnumerable> objDict => FormatDictionary(objDict.Select(x => new KeyValuePair(x.Key, x.Value)), sb), + IEnumerable> objDict => FormatDictionary(objDict.Select(x => new KeyValuePair(x.Key, x.Value)), sb), + IEnumerable>> objDict => FormatDictionary(objDict.Select(x => new KeyValuePair(x.Key, x.Value)), sb), + // dont remove IEnumerable>, it is used for logging cookies which are this type in debug mode + IEnumerable> objDict => FormatDictionary(objDict.Select(x => new KeyValuePair(x.Key, x.Value)), sb), + IEnumerable> objDict => FormatDictionary(objDict, sb), + IList objs => FormatList(objs, sb), + IList objs => FormatList(objs, sb), + // this becomes ugly but this should change once PR improving marshalling of the waf is merged + IList objs => FormatList(objs, sb), + IList objs => FormatList(objs, sb), + IList objs => FormatList(objs, sb), + IList objs => FormatList(objs, sb), + IList objs => FormatList(objs, sb), + IList objs => FormatList(objs, sb), + IList objs => FormatList(objs, sb), + IList objs => FormatList(objs, sb), + _ => sb.Append($"Error: couldn't format type: {o?.GetType()}") + }; + } + + private static StringBuilder FormatDictionary(IEnumerable> objDict, StringBuilder sb) + { + sb.Append("{ "); + using var enumerator = objDict.GetEnumerator(); + if (!enumerator.MoveNext()) { - // zero capacity because we don't know the size in advance - var sb = StringBuilderCache.Acquire(0); - FormatArgsInternal(o, sb); - return StringBuilderCache.GetStringAndRelease(sb); + sb.Append(" }"); + return sb; } - private static void FormatArgsInternal(object o, StringBuilder sb) + sb.Append(enumerator.Current.Key); + sb.Append(": "); + if (enumerator.Current.Value != null) { - _ = - o switch - { - string s => sb.Append(s), - int i => sb.Append(i), - float i => sb.Append(i), - long i => sb.Append(i), - uint i => sb.Append(i), - ulong i => sb.Append(i), - double i => sb.Append(i), - IEnumerable> objDict => FormatDictionary(objDict.Select(x => new KeyValuePair(x.Key, x.Value)), sb), - IEnumerable> objDict => FormatDictionary(objDict.Select(x => new KeyValuePair(x.Key, x.Value)), sb), - IEnumerable>> objDict => FormatDictionary(objDict.Select(x => new KeyValuePair(x.Key, x.Value)), sb), - // dont remove IEnumerable>, it is used for logging cookies which are this type in debug mode - IEnumerable> objDict => FormatDictionary(objDict.Select(x => new KeyValuePair(x.Key, x.Value)), sb), - IEnumerable> objDict => FormatDictionary(objDict, sb), - IList objs => FormatList(objs, sb), - IList objs => FormatList(objs, sb), - // this becomes ugly but this should change once PR improving marshalling of the waf is merged - IList objs => FormatList(objs, sb), - IList objs => FormatList(objs, sb), - IList objs => FormatList(objs, sb), - IList objs => FormatList(objs, sb), - IList objs => FormatList(objs, sb), - IList objs => FormatList(objs, sb), - IList objs => FormatList(objs, sb), - IList objs => FormatList(objs, sb), - _ => sb.Append($"Error: couldn't format type: {o?.GetType()}") - }; + FormatArgsInternal(enumerator.Current.Value, sb); } - private static StringBuilder FormatDictionary(IEnumerable> objDict, StringBuilder sb) + while (enumerator.MoveNext()) { - sb.Append("{ "); - using var enumerator = objDict.GetEnumerator(); - if (!enumerator.MoveNext()) - { - sb.Append(" }"); - return sb; - } - + sb.Append(", "); sb.Append(enumerator.Current.Key); sb.Append(": "); if (enumerator.Current.Value != null) { FormatArgsInternal(enumerator.Current.Value, sb); } + } - while (enumerator.MoveNext()) - { - sb.Append(", "); - sb.Append(enumerator.Current.Key); - sb.Append(": "); - if (enumerator.Current.Value != null) - { - FormatArgsInternal(enumerator.Current.Value, sb); - } - } + sb.Append(" }"); + return sb; + } - sb.Append(" }"); + private static StringBuilder FormatList(IEnumerable objs, StringBuilder sb) + { + sb.Append("[ "); + using var enumerator = objs.GetEnumerator(); + if (!enumerator.MoveNext()) + { + sb.Append(" ]"); return sb; } - private static StringBuilder FormatList(IEnumerable objs, StringBuilder sb) + if (enumerator.Current != null) { - sb.Append("[ "); - using var enumerator = objs.GetEnumerator(); - if (!enumerator.MoveNext()) - { - sb.Append(" ]"); - return sb; - } + FormatArgsInternal(enumerator.Current, sb); + } + while (enumerator.MoveNext()) + { if (enumerator.Current != null) { FormatArgsInternal(enumerator.Current, sb); } - - while (enumerator.MoveNext()) - { - if (enumerator.Current != null) - { - FormatArgsInternal(enumerator.Current, sb); - } - } - - sb.Append(" ]"); - return sb; } - public class EncodeResult : IEncodeResult - { - private readonly Obj _obj; - private readonly IList _argCache; + sb.Append(" ]"); + return sb; + } - internal EncodeResult(Obj obj, IList argCache) - { - _obj = obj; - _argCache = argCache; - Result = _obj.RawPtr; - } + private class EncodeResult : IEncodeResult + { + private readonly Obj _obj; + private readonly WafLibraryInvoker _wafLibraryInvoker; - public IntPtr Result { get; } + internal EncodeResult(Obj obj, WafLibraryInvoker wafLibraryInvoker) + { + _obj = obj; + _wafLibraryInvoker = wafLibraryInvoker; + } - public DdwafObjectStruct ResultDdwafObject => _obj.InnerStruct; + public ref DdwafObjectStruct ResultDdwafObject => ref _obj.InnerStruct; - public void Dispose() - { - foreach (var arg in _argCache) - { - arg.Dispose(); - } - } - } + public void Dispose() => _obj.DisposeAllChildren(_wafLibraryInvoker); } } diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/IEncodeResult.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/IEncodeResult.cs index d8579953de13..757e9407e7db 100644 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/IEncodeResult.cs +++ b/tracer/src/Datadog.Trace/AppSec/WafEncoding/IEncodeResult.cs @@ -10,7 +10,5 @@ namespace Datadog.Trace.AppSec.WafEncoding; internal interface IEncodeResult : IDisposable { - internal IntPtr Result { get; } - - public DdwafObjectStruct ResultDdwafObject { get; } + public ref DdwafObjectStruct ResultDdwafObject { get; } } From 26cb1cff0c88069fc463202517a12751e53fabcc Mon Sep 17 00:00:00 2001 From: Anna Date: Sun, 24 Mar 2024 20:33:34 +0100 Subject: [PATCH 02/13] Allocate on the stack: change Obj accordingly --- .../Datadog.Trace/AppSec/WafEncoding/Obj.cs | 84 +++++++------------ 1 file changed, 29 insertions(+), 55 deletions(-) diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs index dc691fb3fda5..22a33b99cf1c 100644 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs +++ b/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs @@ -3,92 +3,66 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // -using System; +#nullable enable using System.Runtime.InteropServices; -using Datadog.Trace.AppSec.Waf; using Datadog.Trace.AppSec.Waf.NativeBindings; namespace Datadog.Trace.AppSec.WafEncoding { // NOTE: this is referred to as ddwaf_object in the C++ code, we call it Obj to avoid a naming clash - internal class Obj : IDisposable + internal class Obj { - private IntPtr ptr; - private DdwafObjectStruct innerObj; - private bool innerObjInitialized; - private bool _disposed; + private GCHandle? _handle; + private DdwafObjectStruct _innerObj; - public Obj(IntPtr ptr) => this.ptr = ptr; - - public ObjType ArgsType + /// + /// Initializes a new instance of the class. + /// Obj encapsulates a ddwaf_object struct + /// + /// the ddwaf struct + /// if it's the top parent obj, we need to call the waf to dispose it otherwise we dont + public Obj(ref DdwafObjectStruct innerObj, bool parentObj = false) { - get + _innerObj = innerObj; + if (parentObj) { - Initialize(); - return EncoderLegacy.DecodeArgsType(innerObj.Type); + // we pin only the parent and the waf will dispose it as well as its children + _handle = GCHandle.Alloc(_innerObj, GCHandleType.Pinned); } } - public long IntValue + public ObjType ArgsType { - get - { - Initialize(); - return innerObj.IntValue; - } + get { return EncoderLegacy.DecodeArgsType(_innerObj.Type); } } - public ulong UintValue + public long IntValue { - get - { - Initialize(); - return innerObj.UintValue; - } + get { return _innerObj.IntValue; } } - public nint InnerPtr + public ulong UintValue { - get - { - Initialize(); - return innerObj.Array; - } + get { return _innerObj.UintValue; } } - public DdwafObjectStruct InnerStruct + public nint InnerPtr { - get - { - Initialize(); - return innerObj; - } + get { return _innerObj.Array; } } - public IntPtr RawPtr => ptr; - - public void Dispose() + public ref DdwafObjectStruct InnerStruct { - if (!_disposed) - { - _disposed = true; - if (ptr != IntPtr.Zero) - { - Marshal.FreeHGlobal(ptr); - ptr = IntPtr.Zero; - } - } + get { return ref _innerObj; } } - private void Initialize() + public void DisposeAllChildren(WafLibraryInvoker wafLibraryInvoker) { - if (innerObjInitialized) + if (_handle is not null) { - return; + wafLibraryInvoker.ObjectFreePtr(_handle.Value.AddrOfPinnedObject()); + _handle.Value.Free(); } - - innerObjInitialized = true; - innerObj = (DdwafObjectStruct)Marshal.PtrToStructure(ptr, typeof(DdwafObjectStruct)); } } } From 32562ce7681eada58c4c1661e6390bea31411c2c Mon Sep 17 00:00:00 2001 From: Anna Date: Sun, 24 Mar 2024 20:33:53 +0100 Subject: [PATCH 03/13] Configure no free function and manually free like for the other encoder --- tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs index 8911e2617ca2..22053e586b3f 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs @@ -79,7 +79,7 @@ internal static InitResult Create( return InitResult.FromUnusableRuleFile(); } - IntPtr rulesObj; + DdwafObjectStruct rulesObj; DdwafConfigStruct configWafStruct = default; IEncodeResult? result = null; IEncoder encoder; @@ -87,19 +87,19 @@ internal static InitResult Create( var valueRegex = Marshal.StringToHGlobalAnsi(obfuscationParameterValueRegex); configWafStruct.KeyRegex = keyRegex; configWafStruct.ValueRegex = valueRegex; + // here we decide not to configure any free function like `configWafStruct.FreeWafFunction = wafLibraryInvoker.ObjectFreeFuncPtr` + // as we free the object ourselves in both cases calling for the legacy encoder wafLibraryInvoker.ObjectFreeFuncPtr manually and for the other ones, handling our own allocations if (useUnsafeEncoder) { encoder = new Encoder(); result = encoder.Encode(jtokenRoot, applySafetyLimits: false); - rulesObj = result.Result; - configWafStruct.FreeWafFunction = IntPtr.Zero; + rulesObj = result.ResultDdwafObject; } else { encoder = new EncoderLegacy(wafLibraryInvoker); var configObjWrapper = encoder.Encode(jtokenRoot, applySafetyLimits: false); - rulesObj = configObjWrapper.Result; - configWafStruct.FreeWafFunction = wafLibraryInvoker.ObjectFreeFuncPtr; + rulesObj = configObjWrapper.ResultDdwafObject; } var diagnostics = new DdwafObjectStruct { Type = DDWAF_OBJ_TYPE.DDWAF_OBJ_MAP }; @@ -122,10 +122,7 @@ internal static InitResult Create( Marshal.FreeHGlobal(valueRegex); } - if (diagnostics.Array != IntPtr.Zero) - { - wafLibraryInvoker.ObjectFreePtr(ref diagnostics.Array); - } + // wafLibraryInvoker.ObjectFreePtr(Marshal.StructureToPtr()); if (useUnsafeEncoder) { @@ -142,7 +139,7 @@ private UpdateResult UpdateWafAndDispose(IEncodeResult updateData) { diagnostics = new DdwafObjectStruct { Type = DDWAF_OBJ_TYPE.DDWAF_OBJ_MAP }; var diagnosticsValue = diagnostics.Value; - var newHandle = _wafLibraryInvoker.Update(_wafHandle, updateData.Result, ref diagnosticsValue); + var newHandle = _wafLibraryInvoker.Update(_wafHandle, ref updateData.ResultDdwafObject, ref diagnosticsValue); if (newHandle != IntPtr.Zero) { var oldHandle = _wafHandle; @@ -165,10 +162,10 @@ private UpdateResult UpdateWafAndDispose(IEncodeResult updateData) { res ??= new(diagnostics, false); - if (diagnostics?.Array != IntPtr.Zero) + if (diagnostics is not null) { var diagValue = diagnostics!.Value; - _wafLibraryInvoker.ObjectFreePtr(ref diagValue.Array); + // _wafLibraryInvoker.ObjectFreePtr(ref diagValue); } updateData.Dispose(); From 10edd153872e80ba05353c5dcb711436ad67693c Mon Sep 17 00:00:00 2001 From: Anna Date: Sun, 24 Mar 2024 20:35:31 +0100 Subject: [PATCH 04/13] adapt tests --- .../FuzzEncoder.cs | 5 ----- .../WafTests.cs | 2 +- .../Asm/AppSecEncodeBenchmark.cs | 16 ++++++++++++---- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/tracer/test/Datadog.Trace.Security.Unit.Tests/FuzzEncoder.cs b/tracer/test/Datadog.Trace.Security.Unit.Tests/FuzzEncoder.cs index 05a986ed7631..fa5e64ec3175 100644 --- a/tracer/test/Datadog.Trace.Security.Unit.Tests/FuzzEncoder.cs +++ b/tracer/test/Datadog.Trace.Security.Unit.Tests/FuzzEncoder.cs @@ -58,12 +58,7 @@ public void LetsFuzz() // check the object is valid Assert.NotEqual(DDWAF_OBJ_TYPE.DDWAF_OBJ_INVALID, result.ResultDdwafObject.Type); - var argCache = new List(); using var resultObj = _encoderLegacy.Encode(root, applySafetyLimits: true); - foreach (var arg in argCache) - { - arg.Dispose(); - } Assert.NotEqual(DDWAF_OBJ_TYPE.DDWAF_OBJ_INVALID, result.ResultDdwafObject.Type); } diff --git a/tracer/test/Datadog.Trace.Security.Unit.Tests/WafTests.cs b/tracer/test/Datadog.Trace.Security.Unit.Tests/WafTests.cs index b63d3b537c70..3fa4cf28a2d7 100644 --- a/tracer/test/Datadog.Trace.Security.Unit.Tests/WafTests.cs +++ b/tracer/test/Datadog.Trace.Security.Unit.Tests/WafTests.cs @@ -124,8 +124,8 @@ public void SchemaRequestExtraction() private void Execute(string address, object value, string flow = null, string rule = null, string schemaExtraction = null) { - ExecuteInternal(address, value, flow, rule, schemaExtraction, true); ExecuteInternal(address, value, flow, rule, schemaExtraction, false); + ExecuteInternal(address, value, flow, rule, schemaExtraction, true); } private void ExecuteInternal(string address, object value, string flow, string rule, string schemaExtraction, bool newEncoder) diff --git a/tracer/test/benchmarks/Benchmarks.Trace/Asm/AppSecEncodeBenchmark.cs b/tracer/test/benchmarks/Benchmarks.Trace/Asm/AppSecEncodeBenchmark.cs index 20479732bd34..fa758da687d3 100644 --- a/tracer/test/benchmarks/Benchmarks.Trace/Asm/AppSecEncodeBenchmark.cs +++ b/tracer/test/benchmarks/Benchmarks.Trace/Asm/AppSecEncodeBenchmark.cs @@ -18,6 +18,7 @@ namespace Benchmarks.Trace.Asm; [MemoryDiagnoser] [BenchmarkAgent7] +[BenchmarkCategory(Constants.AppSecCategory)] public class AppSecEncoderBenchmark { private static readonly Encoder _encoder; @@ -30,7 +31,6 @@ static AppSecEncoderBenchmark() _encoder = new Encoder(); var wafLibraryInvoker = AppSecBenchmarkUtils.CreateWafLibraryInvoker(); _encoderLegacy = new EncoderLegacy(wafLibraryInvoker); - _args = MakeNestedMap(20); } @@ -74,6 +74,13 @@ private static NestedMap MakeNestedMap(int nestingDepth, bool withAttack = false "lorem", "ipsum", "dolor", + "sit", + "amet", + "lorem", + "ipsum", + "dolor", + "sit", + "amet", AddressesConstants.RequestCookies, new Dictionary { { "something", ".htaccess" }, { "something2", ";shutdown--" } } }; @@ -84,6 +91,7 @@ private static NestedMap MakeNestedMap(int nestingDepth, bool withAttack = false { { "lorem", "ipsum" }, { "dolor", "sit" }, + { "ipsum", "sit" }, { "amet", "amet" }, { "lorem2", "dolor2" }, { "sit2", true }, @@ -96,16 +104,16 @@ private static NestedMap MakeNestedMap(int nestingDepth, bool withAttack = false return new NestedMap(root, nestingDepth, withAttack); } - // [Benchmark] + [Benchmark] public void EncodeArgs() { - using var pwArgs = _encoder.Encode(_args.Map, applySafetyLimits: true); + using var _ = _encoder.Encode(_args.Map, applySafetyLimits: true); } [Benchmark] public void EncodeLegacyArgs() { - using var pwArgs = _encoderLegacy.Encode(_args.Map, applySafetyLimits: true); + using var _ = _encoderLegacy.Encode(_args.Map, applySafetyLimits: true); } public record NestedMap(Dictionary Map, int NestingDepth, bool IsAttack = false) From cc1e5d06a93eb257e6a54fc2b723c2bd61181c5c Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 25 Mar 2024 09:37:02 +0100 Subject: [PATCH 05/13] Obj isnt nullable anymore --- tracer/missing-nullability-files.csv | 1 - 1 file changed, 1 deletion(-) diff --git a/tracer/missing-nullability-files.csv b/tracer/missing-nullability-files.csv index 87b0177daf3e..57888699136b 100644 --- a/tracer/missing-nullability-files.csv +++ b/tracer/missing-nullability-files.csv @@ -297,7 +297,6 @@ src/Datadog.Trace/AppSec/Concurrency/ReaderWriterLock.Framework.cs src/Datadog.Trace/AppSec/Waf/IWaf.cs src/Datadog.Trace/AppSec/Waf/WafConstants.cs src/Datadog.Trace/AppSec/Waf/WafReturnCode.cs -src/Datadog.Trace/AppSec/WafEncoding/Obj.cs src/Datadog.Trace/AppSec/WafEncoding/ObjType.cs src/Datadog.Trace/Ci/Agent/ApmAgentWriter.cs src/Datadog.Trace/Ci/Agent/CIVisibilityProtocolWriter.cs From c858fb77364e5aecb5628c1acee3a9f8cf8aef7b Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 25 Mar 2024 12:41:20 +0100 Subject: [PATCH 06/13] remove nullables --- .../src/Datadog.Trace/AppSec/Waf/Context.cs | 49 +++++++------------ 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs index 1a6c14cb2105..da485e1f9a3c 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs @@ -64,7 +64,7 @@ private Context(IntPtr contextHandle, Waf waf, WafLibraryInvoker wafLibraryInvok public IResult? RunWithEphemeral(IDictionary ephemeralAddressData, ulong timeoutMicroSeconds) => RunInternal(null, ephemeralAddressData, timeoutMicroSeconds); - private IResult? RunInternal(IDictionary? persistentAddressData, IDictionary? ephemeralAddressData, ulong timeoutMicroSeconds) + private unsafe IResult? RunInternal(IDictionary? persistentAddressData, IDictionary? ephemeralAddressData, ulong timeoutMicroSeconds) { if (_disposed) { @@ -100,47 +100,32 @@ private Context(IntPtr contextHandle, Waf waf, WafLibraryInvoker wafLibraryInvok // Calling _encoder.Encode(null) results in a null object that will cause the WAF to error // The WAF can be called with an empty dictionary (though we should avoid doing this). - DdwafObjectStruct? pwPersistentArgs = null; + var pwPersistentArgsPtr = IntPtr.Zero; + var pwEphemeralArgsPtr = IntPtr.Zero; + + DdwafObjectStruct ddwafObjectPersistent; if (persistentAddressData != null) { var persistentArgs = _encoder.Encode(persistentAddressData, applySafetyLimits: true); - pwPersistentArgs = persistentArgs.ResultDdwafObject; + ddwafObjectPersistent = persistentArgs.ResultDdwafObject; + pwPersistentArgsPtr = (IntPtr)(&ddwafObjectPersistent); _encodeResults.Add(persistentArgs); } + IEncodeResult? ephemeralArgs = null; // pwEphemeralArgs follow a different lifecycle and should be disposed immediately - using var ephemeralArgs = ephemeralAddressData is { Count: > 0 } - ? _encoder.Encode(ephemeralAddressData, applySafetyLimits: true) - : null; - var pwEphemeralArgs = ephemeralArgs?.ResultDdwafObject; - - if (pwPersistentArgs is null && pwEphemeralArgs is null) + DdwafObjectStruct ddwafObjectEphemeral; + if (ephemeralAddressData is { Count: > 0 }) { - Log.Error("Both pwPersistentArgs and pwEphemeralArgs are null"); - return null; + var ephemeralArgsResult = _encoder.Encode(ephemeralAddressData, applySafetyLimits: true); + ddwafObjectEphemeral = ephemeralArgsResult.ResultDdwafObject; + pwEphemeralArgsPtr = (IntPtr)(&ddwafObjectEphemeral); + ephemeralArgs = ephemeralArgsResult; } - unsafe - { - var pwEphemeralArgsValue = IntPtr.Zero; - if (pwEphemeralArgs.HasValue) - { - var val = pwEphemeralArgs.Value; - var pointer = &val; - pwEphemeralArgsValue = (IntPtr)pointer; - } - - var pwPersistentArgsValue = IntPtr.Zero; - if (pwPersistentArgs.HasValue) - { - var val = pwPersistentArgs.Value; - var pointer = &val; - pwPersistentArgsValue = (IntPtr)pointer; - } - - // WARNING: DO NOT DISPOSE pwPersistentArgs until the end of this class's lifecycle, i.e in the dispose. Otherwise waf might crash with fatal exception. - code = _waf.Run(_contextHandle, pwPersistentArgsValue, pwEphemeralArgsValue, ref retNative, timeoutMicroSeconds); - } + // WARNING: DO NOT DISPOSE pwPersistentArgs until the end of this class's lifecycle, i.e in the dispose. Otherwise waf might crash with fatal exception. + code = _waf.Run(_contextHandle, pwPersistentArgsPtr, pwEphemeralArgsPtr, ref retNative, timeoutMicroSeconds); + ephemeralArgs?.Dispose(); } _stopwatch.Stop(); From 5442b0b92fb18c13e35b28dd57cd587c82a96f4a Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 25 Mar 2024 13:30:54 +0100 Subject: [PATCH 07/13] be mindful of the scope of objects --- .../src/Datadog.Trace/AppSec/Waf/Context.cs | 33 ++++++++++--------- tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs | 2 +- .../Waf/NativeBindings/WafLibraryInvoker.cs | 4 +-- tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs | 2 +- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs index da485e1f9a3c..c1e4e283d718 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Context.cs @@ -100,32 +100,35 @@ private Context(IntPtr contextHandle, Waf waf, WafLibraryInvoker wafLibraryInvok // Calling _encoder.Encode(null) results in a null object that will cause the WAF to error // The WAF can be called with an empty dictionary (though we should avoid doing this). - var pwPersistentArgsPtr = IntPtr.Zero; - var pwEphemeralArgsPtr = IntPtr.Zero; + DdwafObjectStruct pwPersistentArgs = default; + DdwafObjectStruct pwEphemeralArgsValue = default; - DdwafObjectStruct ddwafObjectPersistent; - if (persistentAddressData != null) + if (persistentAddressData is not null) { var persistentArgs = _encoder.Encode(persistentAddressData, applySafetyLimits: true); - ddwafObjectPersistent = persistentArgs.ResultDdwafObject; - pwPersistentArgsPtr = (IntPtr)(&ddwafObjectPersistent); + pwPersistentArgs = persistentArgs.ResultDdwafObject; _encodeResults.Add(persistentArgs); } - IEncodeResult? ephemeralArgs = null; // pwEphemeralArgs follow a different lifecycle and should be disposed immediately - DdwafObjectStruct ddwafObjectEphemeral; - if (ephemeralAddressData is { Count: > 0 }) + using var ephemeralArgs = ephemeralAddressData is { Count: > 0 } + ? _encoder.Encode(ephemeralAddressData, applySafetyLimits: true) + : null; + + if (persistentAddressData is null && ephemeralArgs is null) + { + Log.Error("Both pwPersistentArgs and pwEphemeralArgs are null"); + return null; + } + + if (ephemeralArgs is not null) { - var ephemeralArgsResult = _encoder.Encode(ephemeralAddressData, applySafetyLimits: true); - ddwafObjectEphemeral = ephemeralArgsResult.ResultDdwafObject; - pwEphemeralArgsPtr = (IntPtr)(&ddwafObjectEphemeral); - ephemeralArgs = ephemeralArgsResult; + // WARNING: Don't use ref here, we need to make a copy because ephemeralArgs is on the heap + pwEphemeralArgsValue = ephemeralArgs.ResultDdwafObject; } // WARNING: DO NOT DISPOSE pwPersistentArgs until the end of this class's lifecycle, i.e in the dispose. Otherwise waf might crash with fatal exception. - code = _waf.Run(_contextHandle, pwPersistentArgsPtr, pwEphemeralArgsPtr, ref retNative, timeoutMicroSeconds); - ephemeralArgs?.Dispose(); + code = _waf.Run(_contextHandle, persistentAddressData != null ? &pwPersistentArgs : null, ephemeralArgs != null ? &pwEphemeralArgsValue : null, ref retNative, timeoutMicroSeconds); } _stopwatch.Stop(); diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs b/tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs index d639c1f18543..47939941a3a8 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs @@ -19,7 +19,7 @@ internal interface IWaf : IDisposable public IContext CreateContext(); - internal WafReturnCode Run(IntPtr contextHandle, IntPtr rawPersistentData, IntPtr rawEphemeralData, ref DdwafResultStruct retNative, ulong timeoutMicroSeconds); + internal unsafe WafReturnCode Run(IntPtr contextHandle, DdwafObjectStruct* rawPersistentData, DdwafObjectStruct* rawEphemeralData, ref DdwafResultStruct retNative, ulong timeoutMicroSeconds); UpdateResult UpdateWafFromConfigurationStatus(ConfigurationStatus configurationStatus); } diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs b/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs index 4ea7e79c38df..4ff5319b61be 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs @@ -92,7 +92,7 @@ private WafLibraryInvoker(IntPtr libraryHandle) private delegate IntPtr InitContextDelegate(IntPtr wafHandle); - private delegate WafReturnCode RunDelegate(IntPtr context, IntPtr rawPersistentData, IntPtr rawEphemeralData, ref DdwafResultStruct result, ulong timeLeftInUs); + private unsafe delegate WafReturnCode RunDelegate(IntPtr context, DdwafObjectStruct* rawPersistentData, DdwafObjectStruct* rawEphemeralData, ref DdwafResultStruct result, ulong timeLeftInUs); private delegate void DestroyDelegate(IntPtr handle); @@ -268,7 +268,7 @@ internal string GetVersion() /// Result /// timeout /// Return waf code - internal WafReturnCode Run(IntPtr context, IntPtr rawPersistentData, IntPtr rawEphemeralData, ref DdwafResultStruct result, ulong timeLeftInUs) + internal unsafe WafReturnCode Run(IntPtr context, DdwafObjectStruct* rawPersistentData, DdwafObjectStruct* rawEphemeralData, ref DdwafResultStruct result, ulong timeLeftInUs) => _runField(context, rawPersistentData, rawEphemeralData, ref result, timeLeftInUs); internal void Destroy(IntPtr wafHandle) => _destroyField(wafHandle); diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs index 22053e586b3f..16df46dc56a7 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs @@ -237,7 +237,7 @@ private UpdateResult Update(IDictionary arguments) } // Doesn't require a non disposed waf handle, but as the WAF instance needs to be valid for the lifetime of the context, if waf is disposed, don't run (unpredictable) - public WafReturnCode Run(IntPtr contextHandle, IntPtr rawPersistentData, IntPtr rawEphemeralData, ref DdwafResultStruct retNative, ulong timeoutMicroSeconds) + public unsafe WafReturnCode Run(IntPtr contextHandle, DdwafObjectStruct* rawPersistentData, DdwafObjectStruct* rawEphemeralData, ref DdwafResultStruct retNative, ulong timeoutMicroSeconds) => _wafLibraryInvoker.Run(contextHandle, rawPersistentData, rawEphemeralData, ref retNative, timeoutMicroSeconds); internal static List MergeRuleData(IEnumerable res) From 339fb83beb7da41a2128013a1e74058e07c053bc Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 25 Mar 2024 13:47:29 +0100 Subject: [PATCH 08/13] dont forget dispose of diagnostics in create and update --- tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs index 16df46dc56a7..027e45ca1bcd 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs @@ -58,7 +58,7 @@ internal Waf(IntPtr wafHandle, WafLibraryInvoker wafLibraryInvoker, IEncoder enc /// use legacy encoder /// if debug level logs should be enabled for the WAF /// the waf wrapper around waf native - internal static InitResult Create( + internal static unsafe InitResult Create( WafLibraryInvoker wafLibraryInvoker, string obfuscationParameterKeyRegex, string obfuscationParameterValueRegex, @@ -122,7 +122,7 @@ internal static InitResult Create( Marshal.FreeHGlobal(valueRegex); } - // wafLibraryInvoker.ObjectFreePtr(Marshal.StructureToPtr()); + wafLibraryInvoker.ObjectFreePtr((IntPtr)(&diagnostics)); if (useUnsafeEncoder) { @@ -131,14 +131,12 @@ internal static InitResult Create( } } - private UpdateResult UpdateWafAndDispose(IEncodeResult updateData) + private unsafe UpdateResult UpdateWafAndDispose(IEncodeResult updateData) { UpdateResult? res = null; - DdwafObjectStruct? diagnostics = null; + var diagnosticsValue = new DdwafObjectStruct { Type = DDWAF_OBJ_TYPE.DDWAF_OBJ_MAP }; try { - diagnostics = new DdwafObjectStruct { Type = DDWAF_OBJ_TYPE.DDWAF_OBJ_MAP }; - var diagnosticsValue = diagnostics.Value; var newHandle = _wafLibraryInvoker.Update(_wafHandle, ref updateData.ResultDdwafObject, ref diagnosticsValue); if (newHandle != IntPtr.Zero) { @@ -160,14 +158,8 @@ private UpdateResult UpdateWafAndDispose(IEncodeResult updateData) } finally { - res ??= new(diagnostics, false); - - if (diagnostics is not null) - { - var diagValue = diagnostics!.Value; - // _wafLibraryInvoker.ObjectFreePtr(ref diagValue); - } - + res ??= new(diagnosticsValue, false); + _wafLibraryInvoker.ObjectFreePtr((IntPtr)(&diagnosticsValue)); updateData.Dispose(); } From d1b4f9c6f522aebc6260d71aaaad10a5d3b2f63d Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 27 Mar 2024 17:17:16 +0100 Subject: [PATCH 09/13] answer to Kevin's comments --- .../Waf/Initialization/WafConfigurator.cs | 2 +- .../Waf/NativeBindings/WafLibraryInvoker.cs | 14 +++-------- tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs | 6 ++--- .../AppSec/WafEncoding/EncoderLegacy.cs | 25 ++----------------- .../Datadog.Trace/AppSec/WafEncoding/Obj.cs | 5 ++-- 5 files changed, 12 insertions(+), 40 deletions(-) diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs index a9ee7513ee49..593b3ec5a1b1 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs @@ -112,7 +112,7 @@ private static void LogRuleDetailsIfDebugEnabled(JToken root) return root; } - internal InitResult Configure(DdwafObjectStruct rulesObj, IEncoder encoder, DdwafConfigStruct configStruct, ref DdwafObjectStruct diagnostics, string? rulesFile) + internal InitResult Configure(ref DdwafObjectStruct rulesObj, IEncoder encoder, DdwafConfigStruct configStruct, ref DdwafObjectStruct diagnostics, string? rulesFile) { var wafHandle = _wafLibraryInvoker.Init(ref rulesObj, ref configStruct, ref diagnostics); if (wafHandle == IntPtr.Zero) diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs b/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs index 4ff5319b61be..e159f0e56183 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/NativeBindings/WafLibraryInvoker.cs @@ -7,7 +7,6 @@ using System.Reflection; using System.Runtime.InteropServices; using Datadog.Trace.AppSec.Waf.Initialization; -using Datadog.Trace.Configuration; using Datadog.Trace.Logging; #pragma warning disable SA1401 @@ -42,12 +41,10 @@ internal class WafLibraryInvoker private readonly ObjectMapAddDelegateX86 _objectMapAddFieldX86; private readonly FreeResultDelegate _freeResultField; private readonly FreeObjectDelegate _freeObjectField; - private readonly IntPtr _freeObjectFuncField; private readonly SetupLoggingDelegate _setupLogging; private readonly SetupLogCallbackDelegate _setupLogCallbackField; private readonly UpdateDelegate _updateField; private string _version = null; - internal static int SizeOfDdWafObject = Marshal.SizeOf(typeof(DdwafObjectStruct)); private WafLibraryInvoker(IntPtr libraryHandle) { @@ -73,7 +70,7 @@ private WafLibraryInvoker(IntPtr libraryHandle) Environment.Is64BitProcess ? GetDelegateForNativeFunction(libraryHandle, "ddwaf_object_map_addl") : null; _objectMapAddFieldX86 = Environment.Is64BitProcess ? null : GetDelegateForNativeFunction(libraryHandle, "ddwaf_object_map_addl"); - _freeObjectField = GetDelegateForNativeFunction(libraryHandle, "ddwaf_object_free", out _freeObjectFuncField); + _freeObjectField = GetDelegateForNativeFunction(libraryHandle, "ddwaf_object_free"); _freeResultField = GetDelegateForNativeFunction(libraryHandle, "ddwaf_result_free"); _getVersionField = GetDelegateForNativeFunction(libraryHandle, "ddwaf_get_version"); // setup logging @@ -124,7 +121,7 @@ private WafLibraryInvoker(IntPtr libraryHandle) private delegate bool ObjectMapAddDelegateX86(ref DdwafObjectStruct map, string entryName, uint entryNameLength, ref DdwafObjectStruct entry); - private delegate void FreeObjectDelegate(IntPtr input); + private delegate void FreeObjectDelegate(ref DdwafObjectStruct input); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void SetupLogCallbackDelegate( @@ -149,8 +146,6 @@ private enum DDWAF_LOG_LEVEL internal bool ExportErrorHappened { get; private set; } - internal IntPtr ObjectFreeFuncPtr => _freeObjectFuncField; - /// /// Initializes static members of the class. /// @@ -345,10 +340,7 @@ internal DdwafObjectStruct ObjectMap() // Setting entryNameLength to 0 will result in the entryName length being re-computed with strlen internal bool ObjectMapAdd(ref DdwafObjectStruct map, string entryName, ulong entryNameLength, ref DdwafObjectStruct entry) => Environment.Is64BitProcess ? _objectMapAddFieldX64!(ref map, entryName, entryNameLength, ref entry) : _objectMapAddFieldX86!(ref map, entryName, (uint)entryNameLength, ref entry); - internal void ObjectFreePtr(IntPtr input) - { - _freeObjectField(input); - } + internal void ObjectFree(ref DdwafObjectStruct input) => _freeObjectField(ref input); internal void ResultFree(ref DdwafResultStruct output) => _freeResultField(ref output); diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs index 027e45ca1bcd..1b6e76757922 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs @@ -106,7 +106,7 @@ internal static unsafe InitResult Create( try { - var initResult = wafConfigurator.Configure(rulesObj, encoder, configWafStruct, ref diagnostics, rulesFromRcm == null ? embeddedRulesetPath : "RemoteConfig"); + var initResult = wafConfigurator.Configure(ref rulesObj, encoder, configWafStruct, ref diagnostics, rulesFromRcm == null ? embeddedRulesetPath : "RemoteConfig"); initResult.EmbeddedRules = jtokenRoot; return initResult; } @@ -122,7 +122,7 @@ internal static unsafe InitResult Create( Marshal.FreeHGlobal(valueRegex); } - wafLibraryInvoker.ObjectFreePtr((IntPtr)(&diagnostics)); + wafLibraryInvoker.ObjectFree(ref diagnostics); if (useUnsafeEncoder) { @@ -159,7 +159,7 @@ private unsafe UpdateResult UpdateWafAndDispose(IEncodeResult updateData) finally { res ??= new(diagnosticsValue, false); - _wafLibraryInvoker.ObjectFreePtr((IntPtr)(&diagnosticsValue)); + _wafLibraryInvoker.ObjectFree(ref diagnosticsValue); updateData.Dispose(); } diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs index d91b9b2e2aed..38b9a224cffa 100644 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs +++ b/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs @@ -31,22 +31,7 @@ public EncoderLegacy(WafLibraryInvoker wafLibraryInvoker) _wafLibraryInvoker = wafLibraryInvoker; } - public static ObjType DecodeArgsType(DDWAF_OBJ_TYPE t) - { - return t switch - { - DDWAF_OBJ_TYPE.DDWAF_OBJ_INVALID => ObjType.Invalid, - DDWAF_OBJ_TYPE.DDWAF_OBJ_SIGNED => ObjType.SignedNumber, - DDWAF_OBJ_TYPE.DDWAF_OBJ_UNSIGNED => ObjType.UnsignedNumber, - DDWAF_OBJ_TYPE.DDWAF_OBJ_STRING => ObjType.String, - DDWAF_OBJ_TYPE.DDWAF_OBJ_BOOL => ObjType.Bool, - DDWAF_OBJ_TYPE.DDWAF_OBJ_DOUBLE => ObjType.Double, - DDWAF_OBJ_TYPE.DDWAF_OBJ_ARRAY => ObjType.Array, - DDWAF_OBJ_TYPE.DDWAF_OBJ_MAP => ObjType.Map, - DDWAF_OBJ_TYPE.DDWAF_OBJ_NULL => ObjType.Null, - _ => throw new Exception($"Invalid DDWAF_INPUT_TYPE {t}") - }; - } + public static ObjType DecodeArgsType(DDWAF_OBJ_TYPE t) => (ObjType)t; private static string TruncateLongString(string s) => s.Length > WafConstants.MaxStringLength ? s.Substring(0, WafConstants.MaxStringLength) : s; @@ -64,7 +49,6 @@ private static Obj EncodeUnknownType(object? o, WafLibraryInvoker wafLibraryInvo } var s = o?.ToString() ?? string.Empty; - return CreateNativeString(s, applyLimits: true, wafLibraryInvoker); } @@ -114,12 +98,7 @@ private static Obj EncodeInternal(T o, int remainingDepth, bool applyLimits, _ => EncodeUnknownType(args, wafLibraryInvoker) }; - if (parentObj) - { - return new Obj(ref value.InnerStruct, parentObj); - } - - return value; + return parentObj ? new Obj(ref value.InnerStruct, parentObj) : value; } private static Obj EncodeList(IEnumerable objEnumerator, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs index 22a33b99cf1c..e712f77efa27 100644 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs +++ b/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs @@ -58,9 +58,10 @@ public ref DdwafObjectStruct InnerStruct public void DisposeAllChildren(WafLibraryInvoker wafLibraryInvoker) { - if (_handle is not null) + if (_handle is not null && _handle.Value.Target is not null) { - wafLibraryInvoker.ObjectFreePtr(_handle.Value.AddrOfPinnedObject()); + var item = (DdwafObjectStruct)_handle.Value.Target; + wafLibraryInvoker.ObjectFree(ref item); _handle.Value.Free(); } } From a7a9dbffd2c842689c2e1c1f7127216ed30047bb Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 28 Mar 2024 11:21:13 +0100 Subject: [PATCH 10/13] dont need to pin object --- .../AppSec/WafEncoding/EncoderLegacy.cs | 2 +- .../Datadog.Trace/AppSec/WafEncoding/Obj.cs | 22 ++----------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs index 38b9a224cffa..680c9098758a 100644 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs +++ b/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs @@ -98,7 +98,7 @@ private static Obj EncodeInternal(T o, int remainingDepth, bool applyLimits, _ => EncodeUnknownType(args, wafLibraryInvoker) }; - return parentObj ? new Obj(ref value.InnerStruct, parentObj) : value; + return value; } private static Obj EncodeList(IEnumerable objEnumerator, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs index e712f77efa27..49d9cf62cc37 100644 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs +++ b/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs @@ -12,7 +12,6 @@ namespace Datadog.Trace.AppSec.WafEncoding // NOTE: this is referred to as ddwaf_object in the C++ code, we call it Obj to avoid a naming clash internal class Obj { - private GCHandle? _handle; private DdwafObjectStruct _innerObj; /// @@ -20,16 +19,7 @@ internal class Obj /// Obj encapsulates a ddwaf_object struct /// /// the ddwaf struct - /// if it's the top parent obj, we need to call the waf to dispose it otherwise we dont - public Obj(ref DdwafObjectStruct innerObj, bool parentObj = false) - { - _innerObj = innerObj; - if (parentObj) - { - // we pin only the parent and the waf will dispose it as well as its children - _handle = GCHandle.Alloc(_innerObj, GCHandleType.Pinned); - } - } + public Obj(ref DdwafObjectStruct innerObj) => _innerObj = innerObj; public ObjType ArgsType { @@ -56,14 +46,6 @@ public ref DdwafObjectStruct InnerStruct get { return ref _innerObj; } } - public void DisposeAllChildren(WafLibraryInvoker wafLibraryInvoker) - { - if (_handle is not null && _handle.Value.Target is not null) - { - var item = (DdwafObjectStruct)_handle.Value.Target; - wafLibraryInvoker.ObjectFree(ref item); - _handle.Value.Free(); - } - } + public void DisposeAllChildren(WafLibraryInvoker wafLibraryInvoker) => wafLibraryInvoker.ObjectFree(ref _innerObj); } } From 152aa324374c52e4d8b5ae87025a8623081b944b Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 28 Mar 2024 11:52:01 +0100 Subject: [PATCH 11/13] Remove Obj and the need of gchandle --- tracer/missing-nullability-files.csv | 1 - .../AppSec/WafEncoding/EncoderLegacy.cs | 57 +++++++++---------- .../Datadog.Trace/AppSec/WafEncoding/Obj.cs | 51 ----------------- .../AppSec/WafEncoding/ObjType.cs | 20 ------- 4 files changed, 27 insertions(+), 102 deletions(-) delete mode 100644 tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs delete mode 100644 tracer/src/Datadog.Trace/AppSec/WafEncoding/ObjType.cs diff --git a/tracer/missing-nullability-files.csv b/tracer/missing-nullability-files.csv index 57888699136b..bce6fd263980 100644 --- a/tracer/missing-nullability-files.csv +++ b/tracer/missing-nullability-files.csv @@ -297,7 +297,6 @@ src/Datadog.Trace/AppSec/Concurrency/ReaderWriterLock.Framework.cs src/Datadog.Trace/AppSec/Waf/IWaf.cs src/Datadog.Trace/AppSec/Waf/WafConstants.cs src/Datadog.Trace/AppSec/Waf/WafReturnCode.cs -src/Datadog.Trace/AppSec/WafEncoding/ObjType.cs src/Datadog.Trace/Ci/Agent/ApmAgentWriter.cs src/Datadog.Trace/Ci/Agent/CIVisibilityProtocolWriter.cs src/Datadog.Trace/Ci/Agent/CIWriterFileSender.cs diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs index 680c9098758a..bb32347c7366 100644 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs +++ b/tracer/src/Datadog.Trace/AppSec/WafEncoding/EncoderLegacy.cs @@ -31,8 +31,6 @@ public EncoderLegacy(WafLibraryInvoker wafLibraryInvoker) _wafLibraryInvoker = wafLibraryInvoker; } - public static ObjType DecodeArgsType(DDWAF_OBJ_TYPE t) => (ObjType)t; - private static string TruncateLongString(string s) => s.Length > WafConstants.MaxStringLength ? s.Substring(0, WafConstants.MaxStringLength) : s; public IEncodeResult Encode(TInstance? o, int remainingDepth = WafConstants.MaxContainerDepth, string? key = null, bool applySafetyLimits = true) @@ -41,7 +39,7 @@ public IEncodeResult Encode(TInstance? o, int remainingDepth = WafCon return new EncodeResult(result, _wafLibraryInvoker); } - private static Obj EncodeUnknownType(object? o, WafLibraryInvoker wafLibraryInvoker) + private static DdwafObjectStruct EncodeUnknownType(object? o, WafLibraryInvoker wafLibraryInvoker) { if (Log.IsEnabled(LogEventLevel.Debug)) { @@ -52,7 +50,7 @@ private static Obj EncodeUnknownType(object? o, WafLibraryInvoker wafLibraryInvo return CreateNativeString(s, applyLimits: true, wafLibraryInvoker); } - private static Obj EncodeInternal(T o, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker, bool parentObj = false) + private static DdwafObjectStruct EncodeInternal(T o, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker, bool parentObj = false) { object args = o!; var value = @@ -101,7 +99,7 @@ private static Obj EncodeInternal(T o, int remainingDepth, bool applyLimits, return value; } - private static Obj EncodeList(IEnumerable objEnumerator, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) + private static DdwafObjectStruct EncodeList(IEnumerable objEnumerator, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) { var arrNat = wafLibraryInvoker.ObjectArray(); @@ -113,7 +111,7 @@ private static Obj EncodeList(IEnumerable objEnumerator, int remainingDept Log.Debug("EncodeList: object graph too deep, truncating nesting {Items}", string.Join(", ", objEnumerator)); } - return new Obj(ref arrNat); + return arrNat; } var count = objEnumerator is IList objs ? objs.Count : objEnumerator.Count(); @@ -131,14 +129,13 @@ private static Obj EncodeList(IEnumerable objEnumerator, int remainingDept foreach (var o in objEnumerator) { var value = EncodeInternal(o, remainingDepth, applyLimits, wafLibraryInvoker); - var innerStruct = value.InnerStruct; - wafLibraryInvoker.ObjectArrayAdd(ref arrNat, ref innerStruct); + wafLibraryInvoker.ObjectArrayAdd(ref arrNat, ref value); } - return new Obj(ref arrNat); + return arrNat; } - private static Obj EncodeDictionary(IEnumerable> objDictEnumerator, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) + private static DdwafObjectStruct EncodeDictionary(IEnumerable> objDictEnumerator, int remainingDepth, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) { var mapNat = wafLibraryInvoker.ObjectMap(); @@ -150,7 +147,7 @@ private static Obj EncodeDictionary(IEnumerable> objD Log.Debug("EncodeDictionary: object graph too deep, truncating nesting {Items}", string.Join(", ", objDictEnumerator.Select(x => $"{x.Key}, {x.Value}"))); } - return new Obj(ref mapNat); + return mapNat; } var count = objDictEnumerator is IDictionary objDict ? objDict.Count : objDictEnumerator.Count(); @@ -172,7 +169,7 @@ private static Obj EncodeDictionary(IEnumerable> objD if (name != null) { var value = EncodeInternal(o.Value, remainingDepth, applyLimits, wafLibraryInvoker); - wafLibraryInvoker.ObjectMapAdd(ref mapNat, name, Convert.ToUInt64(name.Length), ref value.InnerStruct); + wafLibraryInvoker.ObjectMapAdd(ref mapNat, name, Convert.ToUInt64(name.Length), ref value); } else { @@ -183,47 +180,47 @@ private static Obj EncodeDictionary(IEnumerable> objD } } - return new Obj(ref mapNat); + return mapNat; } - private static Obj CreateNativeString(string s, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) + private static DdwafObjectStruct CreateNativeString(string s, bool applyLimits, WafLibraryInvoker wafLibraryInvoker) { var encodeString = applyLimits ? TruncateLongString(s) : s; var objectStringLength = wafLibraryInvoker.ObjectStringLength(encodeString, Convert.ToUInt64(encodeString.Length)); - return new Obj(ref objectStringLength); + return objectStringLength; } - private static Obj CreateNativeBool(bool b, WafLibraryInvoker wafLibraryInvoker) + private static DdwafObjectStruct CreateNativeBool(bool b, WafLibraryInvoker wafLibraryInvoker) { var ddwafObjectStruct = wafLibraryInvoker.ObjectBool(b); - return new(ref ddwafObjectStruct); + return ddwafObjectStruct; } - private static Obj CreateNativeLong(long value, WafLibraryInvoker wafLibraryInvoker) + private static DdwafObjectStruct CreateNativeLong(long value, WafLibraryInvoker wafLibraryInvoker) { var ddwafObjectStruct = wafLibraryInvoker.ObjectLong(value); - return new(ref ddwafObjectStruct); + return ddwafObjectStruct; } - private static Obj CreateNativeNull(WafLibraryInvoker wafLibraryInvoker) + private static DdwafObjectStruct CreateNativeNull(WafLibraryInvoker wafLibraryInvoker) { var ddwafObjectStruct = wafLibraryInvoker.ObjectNull(); - return new(ref ddwafObjectStruct); + return ddwafObjectStruct; } - private static Obj CreateNativeUlong(ulong value, WafLibraryInvoker wafLibraryInvoker) + private static DdwafObjectStruct CreateNativeUlong(ulong value, WafLibraryInvoker wafLibraryInvoker) { var ddwafObjectStruct = wafLibraryInvoker.ObjectUlong(value); - return new(ref ddwafObjectStruct); + return ddwafObjectStruct; } - private static Obj CreateNativeDouble(double value, WafLibraryInvoker wafLibraryInvoker) + private static DdwafObjectStruct CreateNativeDouble(double value, WafLibraryInvoker wafLibraryInvoker) { var ddwafObjectStruct = wafLibraryInvoker.ObjectDouble(value); - return new(ref ddwafObjectStruct); + return ddwafObjectStruct; } public static string FormatArgs(object o) @@ -328,17 +325,17 @@ private static StringBuilder FormatList(IEnumerable objs, StringBuilder sb private class EncodeResult : IEncodeResult { - private readonly Obj _obj; private readonly WafLibraryInvoker _wafLibraryInvoker; + private DdwafObjectStruct _resultDdwafObject; - internal EncodeResult(Obj obj, WafLibraryInvoker wafLibraryInvoker) + internal EncodeResult(DdwafObjectStruct obj, WafLibraryInvoker wafLibraryInvoker) { - _obj = obj; + _resultDdwafObject = obj; _wafLibraryInvoker = wafLibraryInvoker; } - public ref DdwafObjectStruct ResultDdwafObject => ref _obj.InnerStruct; + public ref DdwafObjectStruct ResultDdwafObject => ref _resultDdwafObject; - public void Dispose() => _obj.DisposeAllChildren(_wafLibraryInvoker); + public void Dispose() => _wafLibraryInvoker.ObjectFree(ref _resultDdwafObject); } } diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs deleted file mode 100644 index 49d9cf62cc37..000000000000 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/Obj.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// 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. -// - -#nullable enable -using System.Runtime.InteropServices; -using Datadog.Trace.AppSec.Waf.NativeBindings; - -namespace Datadog.Trace.AppSec.WafEncoding -{ - // NOTE: this is referred to as ddwaf_object in the C++ code, we call it Obj to avoid a naming clash - internal class Obj - { - private DdwafObjectStruct _innerObj; - - /// - /// Initializes a new instance of the class. - /// Obj encapsulates a ddwaf_object struct - /// - /// the ddwaf struct - public Obj(ref DdwafObjectStruct innerObj) => _innerObj = innerObj; - - public ObjType ArgsType - { - get { return EncoderLegacy.DecodeArgsType(_innerObj.Type); } - } - - public long IntValue - { - get { return _innerObj.IntValue; } - } - - public ulong UintValue - { - get { return _innerObj.UintValue; } - } - - public nint InnerPtr - { - get { return _innerObj.Array; } - } - - public ref DdwafObjectStruct InnerStruct - { - get { return ref _innerObj; } - } - - public void DisposeAllChildren(WafLibraryInvoker wafLibraryInvoker) => wafLibraryInvoker.ObjectFree(ref _innerObj); - } -} diff --git a/tracer/src/Datadog.Trace/AppSec/WafEncoding/ObjType.cs b/tracer/src/Datadog.Trace/AppSec/WafEncoding/ObjType.cs deleted file mode 100644 index bccda276f12d..000000000000 --- a/tracer/src/Datadog.Trace/AppSec/WafEncoding/ObjType.cs +++ /dev/null @@ -1,20 +0,0 @@ -// -// 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. -// - -namespace Datadog.Trace.AppSec.WafEncoding -{ - internal enum ObjType - { - Invalid = 0, - SignedNumber = 1 << 0, - UnsignedNumber = 1 << 1, - String = 1 << 2, - Array = 1 << 3, - Map = 1 << 4, - Bool = 1 << 5, - Double = 1 << 6, - Null = 1 << 7 - } -} From f3958bc69c48058147986e788b2338867204b9ae Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 28 Mar 2024 13:16:00 +0100 Subject: [PATCH 12/13] dispose encodeResult in all cases --- tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs | 25 ++++------------------ 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs index 1b6e76757922..add1b039e172 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs @@ -81,26 +81,13 @@ internal static unsafe InitResult Create( DdwafObjectStruct rulesObj; DdwafConfigStruct configWafStruct = default; - IEncodeResult? result = null; - IEncoder encoder; var keyRegex = Marshal.StringToHGlobalAnsi(obfuscationParameterKeyRegex); var valueRegex = Marshal.StringToHGlobalAnsi(obfuscationParameterValueRegex); configWafStruct.KeyRegex = keyRegex; configWafStruct.ValueRegex = valueRegex; - // here we decide not to configure any free function like `configWafStruct.FreeWafFunction = wafLibraryInvoker.ObjectFreeFuncPtr` - // as we free the object ourselves in both cases calling for the legacy encoder wafLibraryInvoker.ObjectFreeFuncPtr manually and for the other ones, handling our own allocations - if (useUnsafeEncoder) - { - encoder = new Encoder(); - result = encoder.Encode(jtokenRoot, applySafetyLimits: false); - rulesObj = result.ResultDdwafObject; - } - else - { - encoder = new EncoderLegacy(wafLibraryInvoker); - var configObjWrapper = encoder.Encode(jtokenRoot, applySafetyLimits: false); - rulesObj = configObjWrapper.ResultDdwafObject; - } + IEncoder encoder = useUnsafeEncoder ? new Encoder() : new EncoderLegacy(wafLibraryInvoker); + var result = encoder.Encode(jtokenRoot, applySafetyLimits: false); + rulesObj = result.ResultDdwafObject; var diagnostics = new DdwafObjectStruct { Type = DDWAF_OBJ_TYPE.DDWAF_OBJ_MAP }; @@ -123,11 +110,7 @@ internal static unsafe InitResult Create( } wafLibraryInvoker.ObjectFree(ref diagnostics); - - if (useUnsafeEncoder) - { - result?.Dispose(); - } + result.Dispose(); } } From bb6eb68762894bd4642092bd1a0b7ac315d974ad Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 28 Mar 2024 17:19:17 +0100 Subject: [PATCH 13/13] cleaning up after some rebase --- tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs index add1b039e172..8ca60261b490 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs @@ -15,7 +15,6 @@ using Datadog.Trace.AppSec.Waf.NativeBindings; using Datadog.Trace.AppSec.Waf.ReturnTypes.Managed; using Datadog.Trace.AppSec.WafEncoding; -using Datadog.Trace.Configuration; using Datadog.Trace.Logging; using Datadog.Trace.Telemetry; using Datadog.Trace.Vendors.Newtonsoft.Json.Linq; @@ -58,7 +57,7 @@ internal Waf(IntPtr wafHandle, WafLibraryInvoker wafLibraryInvoker, IEncoder enc /// use legacy encoder /// if debug level logs should be enabled for the WAF /// the waf wrapper around waf native - internal static unsafe InitResult Create( + internal static InitResult Create( WafLibraryInvoker wafLibraryInvoker, string obfuscationParameterKeyRegex, string obfuscationParameterValueRegex, @@ -79,7 +78,6 @@ internal static unsafe InitResult Create( return InitResult.FromUnusableRuleFile(); } - DdwafObjectStruct rulesObj; DdwafConfigStruct configWafStruct = default; var keyRegex = Marshal.StringToHGlobalAnsi(obfuscationParameterKeyRegex); var valueRegex = Marshal.StringToHGlobalAnsi(obfuscationParameterValueRegex); @@ -87,7 +85,7 @@ internal static unsafe InitResult Create( configWafStruct.ValueRegex = valueRegex; IEncoder encoder = useUnsafeEncoder ? new Encoder() : new EncoderLegacy(wafLibraryInvoker); var result = encoder.Encode(jtokenRoot, applySafetyLimits: false); - rulesObj = result.ResultDdwafObject; + var rulesObj = result.ResultDdwafObject; var diagnostics = new DdwafObjectStruct { Type = DDWAF_OBJ_TYPE.DDWAF_OBJ_MAP };