Skip to content

Commit 1032d71

Browse files
authored
More fuzz targets for binary and Xml decoders (#2621)
- let all Xml encoders and decoders follow the dispose pattern, similar to all the other encoders. - Add fuzz targets for Xml and binary indempotent fuzzer. - add a ReadOnlySpan<byte> signature for WriteByteString - add ArrayPool based IBufferWriter<byte> to support encoding of large byte string with buffers - add Azure DevOps ci pipeline tests with known artifacts that caused fuzzing errors, to catch regressions.
1 parent 84628ee commit 1032d71

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2349
-374
lines changed

.azurepipelines/test.yml

+14
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ jobs:
5959
inputs:
6060
targetType: filePath
6161
filePath: ./.azurepipelines/set-version.ps1
62+
- task: DownloadSecureFile@1
63+
name: fuzzingartifacts
64+
displayName: 'Download Fuzzing artifacts'
65+
inputs:
66+
secureFile: 'FuzzingArtifacts.zip'
67+
- task: ExtractFiles@1
68+
displayName: 'Extract Fuzzing artifacts'
69+
condition: ne(variables['fuzzingartifacts.secureFilePath'], '')
70+
inputs:
71+
archiveFilePatterns: '$(Agent.TempDirectory)/FuzzingArtifacts.zip'
72+
destinationFolder: ./Fuzzing/Encoders/Fuzz.Tests
73+
cleanDestinationFolder: false
74+
overwriteExistingFiles: true
75+
continueOnError: true
6276
- task: DotNetCoreCLI@2
6377
displayName: Restore ${{ parameters.configuration }}
6478
inputs:

.azurepipelines/testcc.yml

+15-4
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,25 @@ jobs:
3737
- task: NuGetToolInstaller@1
3838
inputs:
3939
versionSpec: '>=5.8.x'
40+
- task: DownloadSecureFile@1
41+
name: fuzzingartifacts
42+
displayName: 'Download Fuzzing artifacts'
43+
inputs:
44+
secureFile: 'FuzzingArtifacts.zip'
4045
- task: PowerShell@2
4146
displayName: Versioning
4247
inputs:
4348
targetType: filePath
4449
filePath: ./.azurepipelines/set-version.ps1
50+
- task: ExtractFiles@1
51+
displayName: 'Extract Fuzzing artifacts'
52+
condition: ne(variables['fuzzingartifacts.secureFilePath'], '')
53+
inputs:
54+
archiveFilePatterns: '$(Agent.TempDirectory)/FuzzingArtifacts.zip'
55+
destinationFolder: ./Fuzzing/Encoders/Fuzz.Tests
56+
cleanDestinationFolder: false
57+
overwriteExistingFiles: true
58+
continueOnError: true
4559
- task: DotNetCoreCLI@2
4660
displayName: Restore ${{ parameters.framework }}
4761
inputs:
@@ -77,13 +91,10 @@ jobs:
7791
7892
displayName: Create Code Coverage Report
7993
continueOnError: true
80-
- task: PublishCodeCoverageResults@1
94+
- task: PublishCodeCoverageResults@2
8195
displayName: 'Publish code coverage'
8296
inputs:
83-
codeCoverageTool: Cobertura
8497
summaryFileLocation: '$(Build.SourcesDirectory)/CodeCoverage/Cobertura.xml'
85-
reportDirectory: '$(Build.SourcesDirectory)/CodeCoverage'
86-
8798
continueOnError: true
8899
- script: |
89100
bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r $REPORT_LOCATION --commit-uuid $COMMIT_UUID
Binary file not shown.
+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/* ========================================================================
2+
* Copyright (c) 2005-2024 The OPC Foundation, Inc. All rights reserved.
3+
*
4+
* OPC Foundation MIT License 1.00
5+
*
6+
* Permission is hereby granted, free of charge, to any person
7+
* obtaining a copy of this software and associated documentation
8+
* files (the "Software"), to deal in the Software without
9+
* restriction, including without limitation the rights to use,
10+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the
12+
* Software is furnished to do so, subject to the following
13+
* conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be
16+
* included in all copies or substantial portions of the Software.
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24+
* OTHER DEALINGS IN THE SOFTWARE.
25+
*
26+
* The complete license agreement can be found here:
27+
* http://opcfoundation.org/License/MIT/1.00/
28+
* ======================================================================*/
29+
30+
using System.IO;
31+
using System.Text;
32+
using NUnit.Framework;
33+
using System.Linq;
34+
using System;
35+
using Opc.Ua.Tests;
36+
using System.Reflection;
37+
38+
namespace Opc.Ua.Fuzzing
39+
{
40+
41+
[TestFixture]
42+
public class EncoderTests
43+
{
44+
#region DataPointSources
45+
public static readonly TestcaseAsset[] GoodTestcases = new AssetCollection<TestcaseAsset>(TestUtils.EnumerateTestAssets("Testcases", "*.*")).ToArray();
46+
public static readonly TestcaseAsset[] CrashAssets = new AssetCollection<TestcaseAsset>(TestUtils.EnumerateTestAssets("Assets", "crash*.*")).ToArray();
47+
public static readonly TestcaseAsset[] TimeoutAssets = new AssetCollection<TestcaseAsset>(TestUtils.EnumerateTestAssets("Assets", "timeout*.*")).ToArray();
48+
public static readonly TestcaseAsset[] SlowAssets = new AssetCollection<TestcaseAsset>(TestUtils.EnumerateTestAssets("Assets", "slow*.*")).ToArray();
49+
50+
[DatapointSource]
51+
public static readonly string[] TestcaseEncoderSuffixes = new string[] { ".Binary", ".Json", ".Xml" };
52+
53+
[DatapointSource]
54+
public static readonly FuzzTargetFunction[] FuzzableFunctions = typeof(FuzzableCode).GetMethods(BindingFlags.Static | BindingFlags.Public)
55+
.Where(f => f.GetParameters().Length == 1)
56+
.Select(f => new FuzzTargetFunction(f)).ToArray();
57+
#endregion
58+
59+
[Theory]
60+
public void FuzzGoodTestcases(
61+
FuzzTargetFunction fuzzableCode,
62+
[ValueSource(nameof(GoodTestcases))] TestcaseAsset messageEncoder)
63+
{
64+
FuzzTarget(fuzzableCode, messageEncoder.Testcase);
65+
}
66+
67+
[Theory]
68+
public void FuzzCrashAssets(FuzzTargetFunction fuzzableCode)
69+
{
70+
// note: too many crash files can take forever to create
71+
// all permutations with nunit, so just run all in one batch
72+
foreach (var messageEncoder in CrashAssets)
73+
{
74+
FuzzTarget(fuzzableCode, messageEncoder.Testcase);
75+
}
76+
}
77+
78+
[Theory]
79+
[CancelAfter(1000)]
80+
public void FuzzTimeoutAssets(
81+
FuzzTargetFunction fuzzableCode,
82+
[ValueSource(nameof(TimeoutAssets))] TestcaseAsset messageEncoder)
83+
{
84+
FuzzTarget(fuzzableCode, messageEncoder.Testcase);
85+
}
86+
87+
[Theory]
88+
[CancelAfter(1000)]
89+
public void FuzzSlowAssets(
90+
FuzzTargetFunction fuzzableCode,
91+
[ValueSource(nameof(SlowAssets))] TestcaseAsset messageEncoder)
92+
{
93+
FuzzTarget(fuzzableCode, messageEncoder.Testcase);
94+
}
95+
96+
delegate void LibFuzzTemplate(ReadOnlySpan<byte> span);
97+
98+
private void FuzzTarget(FuzzTargetFunction fuzzableCode, byte[] blob)
99+
{
100+
var parameters = fuzzableCode.MethodInfo.GetParameters();
101+
if (parameters.Length != 1)
102+
{
103+
throw new InvalidOperationException("Fuzzable function must have exactly one parameter.");
104+
}
105+
if (parameters[0].ParameterType == typeof(string))
106+
{
107+
string text = Encoding.UTF8.GetString(blob);
108+
_ = fuzzableCode.MethodInfo.Invoke(null, new object[] { text });
109+
}
110+
else if (typeof(Stream).IsAssignableFrom(parameters[0].ParameterType))
111+
{
112+
using (var stream = new MemoryStream(blob))
113+
{
114+
_ = fuzzableCode.MethodInfo.Invoke(null, new object[] { stream });
115+
}
116+
}
117+
else if (parameters[0].ParameterType == typeof(ReadOnlySpan<byte>))
118+
{
119+
var span = new ReadOnlySpan<byte>(blob);
120+
LibFuzzTemplate fuzzFunction = (LibFuzzTemplate)fuzzableCode.MethodInfo.CreateDelegate(typeof(LibFuzzTemplate));
121+
fuzzFunction(span);
122+
}
123+
}
124+
}
125+
126+
/// <summary>
127+
/// A Testcase as test asset.
128+
/// </summary>
129+
public class TestcaseAsset : IAsset, IFormattable
130+
{
131+
public TestcaseAsset() { }
132+
133+
public string Path { get; private set; }
134+
public byte[] Testcase { get; private set; }
135+
136+
public void Initialize(byte[] blob, string path)
137+
{
138+
Path = path;
139+
Testcase = blob;
140+
}
141+
142+
public string ToString(string format, IFormatProvider formatProvider)
143+
{
144+
var file = System.IO.Path.GetFileName(Path);
145+
return $"{file}";
146+
}
147+
}
148+
149+
/// <summary>
150+
/// A Testcase as test asset.
151+
/// </summary>
152+
public class FuzzTargetFunction : IFormattable
153+
{
154+
public FuzzTargetFunction(MethodInfo methodInfo)
155+
{
156+
MethodInfo = methodInfo;
157+
}
158+
159+
public MethodInfo MethodInfo { get; private set; }
160+
161+
public string ToString(string format, IFormatProvider formatProvider)
162+
{
163+
var name = MethodInfo.Name;
164+
return $"{name}";
165+
}
166+
}
167+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFrameworks>$(TestsTargetFrameworks)</TargetFrameworks>
6+
<AssemblyName>Opc.Ua.Encoders.Fuzz.Tests</AssemblyName>
7+
<RootNamespace>Encoders.Fuzz.Tests</RootNamespace>
8+
<GenerateProgramFile>false</GenerateProgramFile>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<Compile Include="..\Fuzz\FuzzableCode.cs" Link="FuzzableCode.cs" />
13+
<Compile Include="..\Fuzz\FuzzableCode.BinaryDecoder.cs" Link="FuzzableCode.BinaryDecoder.cs" />
14+
<Compile Include="..\Fuzz\FuzzableCode.JsonDecoder.cs" Link="FuzzableCode.JsonDecoder.cs" />
15+
<Compile Include="..\Fuzz\FuzzableCode.XmlDecoder.cs" Link="FuzzableCode.XmlDecoder.cs" />
16+
<Compile Include="..\..\common\Fuzz\FuzzMethods.cs" Link="FuzzMethods.cs" />
17+
<Compile Include="..\..\common\Fuzz.Tools\Testcases.cs" Link="Testcases.cs" />
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
22+
<PackageReference Include="NUnit" Version="4.1.0" />
23+
<PackageReference Include="NUnit.Console" Version="3.17.0" />
24+
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0">
25+
<PrivateAssets>all</PrivateAssets>
26+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
27+
</PackageReference>
28+
<PackageReference Include="coverlet.collector" Version="6.0.2">
29+
<PrivateAssets>all</PrivateAssets>
30+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
31+
</PackageReference>
32+
<PackageReference Include="SharpFuzz" Version="2.1.1" />
33+
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
34+
</ItemGroup>
35+
36+
<ItemGroup>
37+
<ProjectReference Include="..\..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
38+
<ProjectReference Include="..\..\..\Libraries\Opc.Ua.Security.Certificates\Opc.Ua.Security.Certificates.csproj" />
39+
</ItemGroup>
40+
41+
<ItemGroup>
42+
<Compile Include="..\..\..\Tests\Common\Main.cs" />
43+
</ItemGroup>
44+
45+
<ItemGroup>
46+
<None Include="..\Fuzz\Testcases.Binary\**\*.*">
47+
<Link>Testcases\%(RecursiveDir)%(FileName)%(Extension)</Link>
48+
</None>
49+
<None Include="..\Fuzz\Testcases.Json\**\*.*">
50+
<Link>Testcases\%(RecursiveDir)%(FileName)%(Extension)</Link>
51+
</None>
52+
<None Include="..\Fuzz\Testcases.Xml\**\*.*">
53+
<Link>Testcases\%(RecursiveDir)%(FileName)%(Extension)</Link>
54+
</None>
55+
</ItemGroup>
56+
57+
<ItemGroup>
58+
<None Update="..\Fuzz\Testcases.Binary\**\*.*">
59+
<Link>Testcases\%(RecursiveDir)%(FileName)%(Extension)</Link>
60+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
61+
</None>
62+
<None Update="..\Fuzz\Testcases.Json\**\*.*">
63+
<Link>Testcases\%(RecursiveDir)%(FileName)%(Extension)</Link>
64+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
65+
</None>
66+
<None Update="..\Fuzz\Testcases.Xml\**\*.*">
67+
<Link>Testcases\%(RecursiveDir)%(FileName)%(Extension)</Link>
68+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
69+
</None>
70+
<None Update="Assets\**">
71+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
72+
</None>
73+
</ItemGroup>
74+
75+
</Project>

0 commit comments

Comments
 (0)