-
Notifications
You must be signed in to change notification settings - Fork 955
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add UA Fuzzer solution for afl-fuzz and libfuzzer (#2603)
- Add a solution to provide fuzzer support for afl-fuzz and libfuzzer. - Add fuzz targets for Binary, Json and Xml encoder and decoder fuzzing. For afl-fuzz and libfuzzer. - Fix issues in encoders and decoders when unexpected exceptions are returned. The expectation is to return only ServiceResultException with BadDecodingError, BadEncodingError or BadEncodingLimitsExceeded. - Add a tool to create Testcases. - Add a tool to playback crashes and timeouts. - Solution is extensible with common files to discover new fuzz targets by reflection. New fuzz projects can share many files with the first project. - Breaking change: Fix how BinaryDecoder is used: Previously, calling close on the BinaryDecoder implicitly disposed the stream. Change behavior so that `using` pattern can be applied across the board.
- Loading branch information
Showing
65 changed files
with
3,468 additions
and
876 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>$(AppTargetFramework)</TargetFramework> | ||
<AssemblyName>Encoders.Fuzz.Tools</AssemblyName> | ||
<RootNamespace>Encoders.Fuzz.Tools</RootNamespace> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="..\Fuzz\FuzzableCode.cs" Link="FuzzableCode.cs" /> | ||
<Compile Include="..\Fuzz\FuzzableCode.BinaryDecoder.cs" Link="FuzzableCode.BinaryDecoder.cs" /> | ||
<Compile Include="..\Fuzz\FuzzableCode.JsonDecoder.cs" Link="FuzzableCode.JsonDecoder.cs" /> | ||
<Compile Include="..\..\common\Fuzz\FuzzMethods.cs" Link="FuzzMethods.cs" /> | ||
<Compile Include="..\..\common\Fuzz.Tools\Program.cs" Link="Program.cs" /> | ||
<Compile Include="..\..\common\Fuzz.Tools\Playback.cs" Link="Playback.cs" /> | ||
<Compile Include="..\..\common\Fuzz.Tools\Testcases.cs" Link="Testcases.cs" /> | ||
<Compile Include="..\..\common\Fuzz.Tools\Logging.cs" Link="Logging.cs" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="SharpFuzz" Version="2.1.1" /> | ||
<PackageReference Include="Mono.Options" Version="6.12.0.148" /> | ||
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" /> | ||
<PackageReference Include="Serilog" Version="3.1.1" /> | ||
<PackageReference Include="Serilog.Expressions" Version="4.0.0" /> | ||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" /> | ||
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" /> | ||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> | ||
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" /> | ||
<ProjectReference Include="..\..\..\Libraries\Opc.Ua.Security.Certificates\Opc.Ua.Security.Certificates.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* ======================================================================== | ||
* Copyright (c) 2005-2024 The OPC Foundation, Inc. All rights reserved. | ||
* | ||
* OPC Foundation MIT License 1.00 | ||
* | ||
* Permission is hereby granted, free of charge, to any person | ||
* obtaining a copy of this software and associated documentation | ||
* files (the "Software"), to deal in the Software without | ||
* restriction, including without limitation the rights to use, | ||
* copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the | ||
* Software is furnished to do so, subject to the following | ||
* conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be | ||
* included in all copies or substantial portions of the Software. | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
* OTHER DEALINGS IN THE SOFTWARE. | ||
* | ||
* The complete license agreement can be found here: | ||
* http://opcfoundation.org/License/MIT/1.00/ | ||
* ======================================================================*/ | ||
|
||
using System.IO; | ||
using System.Text; | ||
using Opc.Ua; | ||
|
||
public static partial class Testcases | ||
{ | ||
|
||
public enum TestCaseEncoders : int | ||
{ | ||
Binary = 0, | ||
Json = 1, | ||
Xml = 2 | ||
}; | ||
|
||
public static string[] TestcaseEncoderSuffixes = new string[] { ".Binary", ".Json", ".Xml" }; | ||
|
||
public static void Run(string workPath) | ||
{ | ||
// Create the Testcases for the binary decoder. | ||
string pathSuffix = TestcaseEncoderSuffixes[(int)TestCaseEncoders.Binary]; | ||
string pathTarget = workPath + pathSuffix + Path.DirectorySeparatorChar; | ||
foreach (var messageEncoder in MessageEncoders) | ||
{ | ||
byte[] message; | ||
using (var encoder = new BinaryEncoder(MessageContext)) | ||
{ | ||
messageEncoder(encoder); | ||
message = encoder.CloseAndReturnBuffer(); | ||
} | ||
|
||
// Test the fuzz targets with the message. | ||
FuzzableCode.LibfuzzBinaryDecoder(message); | ||
FuzzableCode.LibfuzzBinaryEncoder(message); | ||
using (var stream = new MemoryStream(message)) | ||
{ | ||
FuzzableCode.AflfuzzBinaryDecoder(stream); | ||
} | ||
using (var stream = new MemoryStream(message)) | ||
{ | ||
FuzzableCode.AflfuzzBinaryEncoder(stream); | ||
} | ||
using (var stream = new MemoryStream(message)) | ||
{ | ||
FuzzableCode.FuzzBinaryDecoderCore(stream, true); | ||
} | ||
|
||
string fileName = Path.Combine(pathTarget, $"{messageEncoder.Method.Name}.bin".ToLowerInvariant()); | ||
File.WriteAllBytes(fileName, message); | ||
} | ||
|
||
// Create the Testcases for the json decoder. | ||
pathSuffix = TestcaseEncoderSuffixes[(int)TestCaseEncoders.Json]; | ||
pathTarget = workPath + pathSuffix + Path.DirectorySeparatorChar; | ||
foreach (var messageEncoder in MessageEncoders) | ||
{ | ||
byte[] message; | ||
using (var memoryStream = new MemoryStream(0x1000)) | ||
using (var encoder = new JsonEncoder(MessageContext, true, false, memoryStream)) | ||
{ | ||
messageEncoder(encoder); | ||
encoder.Close(); | ||
message = memoryStream.ToArray(); | ||
} | ||
|
||
|
||
// Test the fuzz targets with the message. | ||
FuzzableCode.LibfuzzJsonDecoder(message); | ||
FuzzableCode.LibfuzzJsonEncoder(message); | ||
string json = Encoding.UTF8.GetString(message); | ||
FuzzableCode.AflfuzzJsonDecoder(json); | ||
FuzzableCode.AflfuzzJsonEncoder(json); | ||
FuzzableCode.FuzzJsonDecoderCore(json, true); | ||
|
||
string fileName = Path.Combine(pathTarget, $"{messageEncoder.Method.Name}.json".ToLowerInvariant()); | ||
File.WriteAllBytes(fileName, message); | ||
} | ||
|
||
// Create the Testcases for the xml decoder. | ||
pathSuffix = TestcaseEncoderSuffixes[(int)TestCaseEncoders.Xml]; | ||
pathTarget = workPath + pathSuffix + Path.DirectorySeparatorChar; | ||
foreach (var messageEncoder in MessageEncoders) | ||
{ | ||
string message; | ||
using (var encoder = new XmlEncoder(MessageContext)) | ||
{ | ||
encoder.SetMappingTables(MessageContext.NamespaceUris, MessageContext.ServerUris); | ||
messageEncoder(encoder); | ||
message = encoder.CloseAndReturnText(); | ||
} | ||
|
||
// Test the fuzz targets with the message. | ||
|
||
string fileName = Path.Combine(pathTarget, $"{messageEncoder.Method.Name}.xml".ToLowerInvariant()); | ||
File.WriteAllBytes(fileName, Encoding.UTF8.GetBytes(message)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>$(AppTargetFramework)</TargetFramework> | ||
<AssemblyName>Encoders.Fuzz</AssemblyName> | ||
<RootNamespace>Encoders.Fuzz</RootNamespace> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(LibFuzzer)' == 'true'"> | ||
<DefineConstants>$(DefineConstants);LIBFUZZER</DefineConstants> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="..\..\common\Fuzz\Program.cs" Link="Program.cs" /> | ||
<Compile Include="..\..\common\Fuzz\FuzzMethods.cs" Link="FuzzMethods.cs" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="SharpFuzz" Version="2.1.1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" /> | ||
<ProjectReference Include="..\..\..\Libraries\Opc.Ua.Security.Certificates\Opc.Ua.Security.Certificates.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/* ======================================================================== | ||
* Copyright (c) 2005-2024 The OPC Foundation, Inc. All rights reserved. | ||
* | ||
* OPC Foundation MIT License 1.00 | ||
* | ||
* Permission is hereby granted, free of charge, to any person | ||
* obtaining a copy of this software and associated documentation | ||
* files (the "Software"), to deal in the Software without | ||
* restriction, including without limitation the rights to use, | ||
* copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the | ||
* Software is furnished to do so, subject to the following | ||
* conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be | ||
* included in all copies or substantial portions of the Software. | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
* OTHER DEALINGS IN THE SOFTWARE. | ||
* | ||
* The complete license agreement can be found here: | ||
* http://opcfoundation.org/License/MIT/1.00/ | ||
* ======================================================================*/ | ||
|
||
using System; | ||
using System.IO; | ||
using Opc.Ua; | ||
|
||
/// <summary> | ||
/// Fuzzing code for the binary decoder and encoder. | ||
/// </summary> | ||
public static partial class FuzzableCode | ||
{ | ||
/// <summary> | ||
/// The binary decoder fuzz target for afl-fuzz. | ||
/// </summary> | ||
/// <param name="stream">The stdin stream from the afl-fuzz process.</param> | ||
public static void AflfuzzBinaryDecoder(Stream stream) | ||
{ | ||
using (var memoryStream = PrepareArraySegmentStream(stream)) | ||
{ | ||
FuzzBinaryDecoderCore(memoryStream); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// The binary encoder fuzz target for afl-fuzz. | ||
/// </summary> | ||
/// <param name="stream">The stdin stream from the afl-fuzz process.</param> | ||
public static void AflfuzzBinaryEncoder(Stream stream) | ||
{ | ||
IEncodeable encodeable = null; | ||
using (var memoryStream = PrepareArraySegmentStream(stream)) | ||
{ | ||
try | ||
{ | ||
encodeable = FuzzBinaryDecoderCore(memoryStream); | ||
} | ||
catch | ||
{ | ||
return; | ||
} | ||
} | ||
|
||
// encode the fuzzed object and see if it crashes | ||
if (encodeable != null) | ||
{ | ||
_ = BinaryEncoder.EncodeMessage(encodeable, messageContext); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// The binary decoder fuzz target for libfuzzer. | ||
/// </summary> | ||
public static void LibfuzzBinaryDecoder(ReadOnlySpan<byte> input) | ||
{ | ||
using (var memoryStream = new MemoryStream(input.ToArray())) | ||
{ | ||
_ = FuzzBinaryDecoderCore(memoryStream); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// The binary encoder fuzz target for afl-fuzz. | ||
/// </summary> | ||
public static void LibfuzzBinaryEncoder(ReadOnlySpan<byte> input) | ||
{ | ||
IEncodeable encodeable = null; | ||
using (var memoryStream = new MemoryStream(input.ToArray())) | ||
{ | ||
try | ||
{ | ||
encodeable = FuzzBinaryDecoderCore(memoryStream); | ||
} | ||
catch | ||
{ | ||
return; | ||
} | ||
} | ||
|
||
// encode the fuzzed object and see if it crashes | ||
if (encodeable != null) | ||
{ | ||
_ = BinaryEncoder.EncodeMessage(encodeable, messageContext); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// The fuzz target for the BinaryDecoder. | ||
/// </summary> | ||
/// <param name="stream">A memory stream with fuzz content.</param> | ||
internal static IEncodeable FuzzBinaryDecoderCore(MemoryStream stream, bool throwAll = false) | ||
{ | ||
try | ||
{ | ||
using (var decoder = new BinaryDecoder(stream, messageContext)) | ||
{ | ||
return decoder.DecodeMessage(null); | ||
} | ||
} | ||
catch (ServiceResultException sre) | ||
{ | ||
switch (sre.StatusCode) | ||
{ | ||
case StatusCodes.BadEncodingLimitsExceeded: | ||
case StatusCodes.BadDecodingError: | ||
if (!throwAll) | ||
{ | ||
return null; | ||
} | ||
break; | ||
} | ||
|
||
throw; | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.