Skip to content

Commit

Permalink
Revert "[ASM] New marshalling system for Waf.Run calls to improve spe… (
Browse files Browse the repository at this point in the history
DataDog#4891)

* Revert "[ASM] New marshalling system for Waf.Run calls to improve speed and reduce allocations (DataDog#4302)"

This reverts commit 096f106.

* fix schema test by syncing with master
  • Loading branch information
robertpi authored Nov 20, 2023
1 parent c00ed77 commit 457af03
Show file tree
Hide file tree
Showing 20 changed files with 381 additions and 755 deletions.
6 changes: 1 addition & 5 deletions tracer/src/Datadog.Trace/AppSec/Security.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,11 +337,7 @@ void SetHtmlResponseContent()
}

/// <summary> Frees resources </summary>
public void Dispose()
{
_waf?.Dispose();
Encoder.Pool.Dispose();
}
public void Dispose() => _waf?.Dispose();

internal void SetDebugEnabled(bool enabled)
{
Expand Down
27 changes: 12 additions & 15 deletions tracer/src/Datadog.Trace/AppSec/Waf/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ internal class Context : IContext

// the context handle should be locked, it is not safe for concurrent access and two
// waf events may be processed at the same time due to code being run asynchronously
private readonly object _sync = new object();
private readonly IntPtr _contextHandle;

private readonly Waf _waf;

private readonly List<IntPtr> _argCache;
private readonly List<Obj> _argCache = new();
private readonly Stopwatch _stopwatch;
private readonly WafLibraryInvoker _wafLibraryInvoker;

Expand All @@ -38,7 +39,6 @@ private Context(IntPtr contextHandle, Waf waf, WafLibraryInvoker wafLibraryInvok
_waf = waf;
_wafLibraryInvoker = wafLibraryInvoker;
_stopwatch = new Stopwatch();
_argCache = new(64);
}

~Context() => Dispose(false);
Expand Down Expand Up @@ -78,21 +78,13 @@ private Context(IntPtr contextHandle, Waf waf, WafLibraryInvoker wafLibraryInvok

// not restart cause it's the total runtime over runs, and we run several * during request
_stopwatch.Start();
using var pwArgs = Encoder.Encode(addresses, _wafLibraryInvoker, _argCache, applySafetyLimits: true);
var rawArgs = pwArgs.RawPtr;

WafReturnCode code;
lock (_stopwatch)
lock (_sync)
{
var pool = Encoder.Pool;
try
{
var pwArgs = Encoder.Encode(addresses, applySafetyLimits: true, argToFree: _argCache, pool: pool);
code = _waf.Run(_contextHandle, ref pwArgs, ref retNative, timeoutMicroSeconds);
}
finally
{
pool.Return(_argCache);
_argCache.Clear();
}
code = _waf.Run(_contextHandle, rawArgs, ref retNative, timeoutMicroSeconds);
}

_stopwatch.Stop();
Expand Down Expand Up @@ -120,7 +112,12 @@ public void Dispose(bool disposing)

_disposed = true;

lock (_stopwatch)
foreach (var arg in _argCache)
{
arg.Dispose();
}

lock (_sync)
{
_wafLibraryInvoker.ContextDestroy(_contextHandle);
}
Expand Down
560 changes: 120 additions & 440 deletions tracer/src/Datadog.Trace/AppSec/Waf/Encoder.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal interface IWaf : IDisposable

public IContext CreateContext();

internal WafReturnCode Run(IntPtr contextHandle, ref DdwafObjectStruct args, ref DdwafResultStruct retNative, ulong timeoutMicroSeconds);
internal WafReturnCode Run(IntPtr contextHandle, IntPtr rawArgs, ref DdwafResultStruct retNative, ulong timeoutMicroSeconds);

UpdateResult UpdateWafFromConfigurationStatus(ConfigurationStatus configurationStatus);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,17 +155,17 @@ private static void LogRuleDetailsIfDebugEnabled(JToken root)
return root;
}

internal InitResult ConfigureAndDispose(DdwafObjectStruct? rulesObj, string? rulesFile, string obfuscationParameterKeyRegex, string obfuscationParameterValueRegex)
internal InitResult ConfigureAndDispose(Obj? rulesObj, string? rulesFile, List<Obj> argsToDispose, string obfuscationParameterKeyRegex, string obfuscationParameterValueRegex)
{
if (rulesObj == null)
{
Log.Error("Waf couldn't initialize properly because of an unusable rule file. If you set the environment variable {AppsecruleEnv}, check the path and content of the file are correct.", ConfigurationKeys.AppSec.Rules);
return InitResult.FromUnusableRuleFile();
}

Obj? diagnostics = null;
var keyRegex = IntPtr.Zero;
var valueRegex = IntPtr.Zero;
var diagnostics = new DdwafObjectStruct { Type = DDWAF_OBJ_TYPE.DDWAF_OBJ_MAP };

try
{
Expand All @@ -174,10 +174,10 @@ internal InitResult ConfigureAndDispose(DdwafObjectStruct? rulesObj, string? rul
valueRegex = Marshal.StringToHGlobalAnsi(obfuscationParameterValueRegex);
args.KeyRegex = keyRegex;
args.ValueRegex = valueRegex;
args.FreeWafFunction = IntPtr.Zero;
args.FreeWafFunction = _wafLibraryInvoker.ObjectFreeFuncPtr;

var rules = rulesObj.Value;
var wafHandle = _wafLibraryInvoker.Init(ref rules, ref args, ref diagnostics);
diagnostics = new Obj(_wafLibraryInvoker.ObjectMap());
var wafHandle = _wafLibraryInvoker.Init(rulesObj.RawPtr, ref args, diagnostics.RawPtr);
if (wafHandle == IntPtr.Zero)
{
Log.Warning("DDAS-0005-00: WAF initialization failed.");
Expand Down Expand Up @@ -222,9 +222,11 @@ internal InitResult ConfigureAndDispose(DdwafObjectStruct? rulesObj, string? rul
Marshal.FreeHGlobal(valueRegex);
}

if (diagnostics.Array != IntPtr.Zero)
diagnostics?.Dispose(_wafLibraryInvoker);
rulesObj.Dispose(_wafLibraryInvoker);
foreach (var arg in argsToDispose)
{
_wafLibraryInvoker.ObjectFreePtr(ref diagnostics.Array);
arg.Dispose();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ private WafLibraryInvoker(IntPtr libraryHandle)

private delegate void FreeResultDelegate(ref DdwafResultStruct output);

private delegate IntPtr InitDelegate(ref DdwafObjectStruct wafRule, ref DdwafConfigStruct config, ref DdwafObjectStruct diagnostics);
private delegate IntPtr InitDelegate(IntPtr wafRule, ref DdwafConfigStruct config, IntPtr diagnostics);

private delegate IntPtr UpdateDelegate(IntPtr oldWafHandle, ref DdwafObjectStruct wafRule, ref DdwafObjectStruct diagnostics);
private delegate IntPtr UpdateDelegate(IntPtr oldWafHandle, IntPtr wafRule, IntPtr diagnostics);

private delegate IntPtr InitContextDelegate(IntPtr wafHandle);

private delegate WafReturnCode RunDelegate(IntPtr context, ref DdwafObjectStruct newArgs, ref DdwafResultStruct result, ulong timeLeftInUs);
private delegate WafReturnCode RunDelegate(IntPtr context, IntPtr newArgs, ref DdwafResultStruct result, ulong timeLeftInUs);

private delegate void DestroyDelegate(IntPtr handle);

Expand Down Expand Up @@ -204,7 +204,7 @@ internal string GetVersion()
return _version;
}

internal IntPtr Init(ref DdwafObjectStruct wafRule, ref DdwafConfigStruct config, ref DdwafObjectStruct diagnostics) => _initField(ref wafRule, ref config, ref diagnostics);
internal IntPtr Init(IntPtr wafRule, ref DdwafConfigStruct config, IntPtr diagnostics) => _initField(wafRule, ref config, diagnostics);

/// <summary>
/// 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.
Expand All @@ -213,11 +213,11 @@ internal string GetVersion()
/// <param name="wafData">a pointer to the new waf data (rules or overrides or other)</param>
/// <param name="diagnostics">errors and diagnostics of the update, only for valid for new rules</param>
/// <returns>the new waf handle, if error, will be a nullptr</returns>
internal IntPtr Update(IntPtr oldWafHandle, ref DdwafObjectStruct wafData, ref DdwafObjectStruct diagnostics) => _updateField(oldWafHandle, ref wafData, ref diagnostics);
internal IntPtr Update(IntPtr oldWafHandle, IntPtr wafData, IntPtr diagnostics) => _updateField(oldWafHandle, wafData, diagnostics);

internal IntPtr InitContext(IntPtr powerwafHandle) => _initContextField(powerwafHandle);

internal WafReturnCode Run(IntPtr context, ref DdwafObjectStruct newArgs, ref DdwafResultStruct result, ulong timeLeftInUs) => _runField(context, ref newArgs, ref result, timeLeftInUs);
internal WafReturnCode Run(IntPtr context, IntPtr newArgs, ref DdwafResultStruct result, ulong timeLeftInUs) => _runField(context, newArgs, ref result, timeLeftInUs);

internal void Destroy(IntPtr wafHandle) => _destroyField(wafHandle);

Expand Down
118 changes: 118 additions & 0 deletions tracer/src/Datadog.Trace/AppSec/Waf/Obj.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// <copyright file="Obj.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using System;
using System.Runtime.InteropServices;
using Datadog.Trace.AppSec.Waf.NativeBindings;

namespace Datadog.Trace.AppSec.Waf
{
// 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
{
private IntPtr ptr;
private DdwafObjectStruct innerObj;
private bool innerObjInitialized;
private bool disposed;

public Obj(IntPtr ptr) => this.ptr = ptr;

~Obj()
{
Dispose(false);
}

public ObjType ArgsType
{
get
{
Initialize();
return Encoder.DecodeArgsType(innerObj.Type);
}
}

public long IntValue
{
get
{
Initialize();
return innerObj.IntValue;
}
}

public ulong UintValue
{
get
{
Initialize();
return innerObj.UintValue;
}
}

public nint InnerPtr
{
get
{
Initialize();
return innerObj.Array;
}
}

public DdwafObjectStruct InnerStruct
{
get
{
Initialize();
return innerObj;
}
}

public IntPtr RawPtr => ptr;

public void Dispose(WafLibraryInvoker libraryInvoker)
{
if (libraryInvoker != null)
{
var rawPtr = ptr;
libraryInvoker.ObjectFreePtr(ref rawPtr);
Dispose();
}
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
if (disposed)
{
ptr = IntPtr.Zero;
return;
}

disposed = true;

if (ptr != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
}
}

private void Initialize()
{
if (innerObjInitialized)
{
return;
}

innerObjInitialized = true;
innerObj = (DdwafObjectStruct)Marshal.PtrToStructure(ptr, typeof(DdwafObjectStruct));
}
}
}
24 changes: 24 additions & 0 deletions tracer/src/Datadog.Trace/AppSec/Waf/ObjType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// <copyright file="ObjType.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;

namespace Datadog.Trace.AppSec.Waf
{
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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using Datadog.Trace.AppSec.Waf.NativeBindings;

namespace Datadog.Trace.AppSec.Waf.ReturnTypes.Managed;

Expand All @@ -19,9 +18,9 @@ internal class DiagnosticResult
private readonly Lazy<DiagnosticFeatureResult?> _rulesData;
private readonly Lazy<DiagnosticFeatureResult?> _rulesOverride;

public DiagnosticResult(DdwafObjectStruct diagObject)
public DiagnosticResult(Obj diagObject)
{
_diagnosticsData = diagObject.DecodeMap();
_diagnosticsData = diagObject.InnerStruct.DecodeMap();
_customRules = MakeLazy("custom_rules");
_exclusions = MakeLazy("exclusions");
_rules = MakeLazy("rules");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using Datadog.Trace.AppSec.Waf.NativeBindings;
using Datadog.Trace.Vendors.Serilog;

namespace Datadog.Trace.AppSec.Waf.ReturnTypes.Managed;
Expand All @@ -15,15 +14,15 @@ namespace Datadog.Trace.AppSec.Waf.ReturnTypes.Managed;

internal static class DiagnosticResultUtils
{
internal static ReportedDiagnostics ExtractReportedDiagnostics(DdwafObjectStruct diagObject, bool noRuleDiagnoticsIsError)
internal static ReportedDiagnostics ExtractReportedDiagnostics(Obj diagObject, bool noRuleDiagnoticsIsError)
{
ushort failedCount = 0;
ushort loadedCount = 0;
var rulesetVersion = string.Empty;
string rulesetVersion = string.Empty;
IReadOnlyDictionary<string, object>? errors = null;
try
{
if (diagObject.Type == DDWAF_OBJ_TYPE.DDWAF_OBJ_INVALID)
if (diagObject.ArgsType == ObjType.Invalid)
{
errors = new Dictionary<string, object> { { "diagnostics-error", "Waf didn't provide a valid diagnostics object at initialization, most likely due to an older waf version < 1.11.0" } };
return new ReportedDiagnostics { FailedCount = failedCount, LoadedCount = loadedCount, RulesetVersion = rulesetVersion, Errors = errors };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ private InitResult(ushort failedToLoadRules, ushort loadedRules, string ruleFile

internal static InitResult FromIncompatibleWaf() => new(0, 0, string.Empty, new Dictionary<string, object>(), incompatibleWaf: true);

internal static InitResult From(DdwafObjectStruct diagObject, IntPtr? wafHandle, WafLibraryInvoker? wafLibraryInvoker)
internal static InitResult From(Obj diagObject, IntPtr? wafHandle, WafLibraryInvoker? wafLibraryInvoker)
{
var reportedDiag = DiagnosticResultUtils.ExtractReportedDiagnostics(diagObject, true);

Expand Down
Loading

0 comments on commit 457af03

Please sign in to comment.