Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add .NET Libraries fuzzing targets and automation #101993

Merged
merged 37 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2b30105
Initial fuzzing setup
MihaZupan May 2, 2024
1ea296f
Initial pipeline
MihaZupan May 2, 2024
c8b351e
Install sharpfuzz to the working directory
MihaZupan May 3, 2024
8f5945a
Initial instructions
MihaZupan May 3, 2024
4358dda
Add a few helpful links
MihaZupan May 3, 2024
26c6820
Enable OneFuzz deployment task
MihaZupan May 3, 2024
b9a83a9
Remove BlameAlias default
MihaZupan May 4, 2024
0b21ae4
Speed up headers fuzzer
MihaZupan May 4, 2024
acd6d02
Add BoundedMemory reference
MihaZupan May 4, 2024
f5f8ddf
Use BoundedMemory in SearchValues targets
MihaZupan May 4, 2024
35276be
Swap property order
MihaZupan May 4, 2024
4db747e
Add a UTF8 fuzzing target
MihaZupan May 4, 2024
702545d
Reuse instrumented assemblies when unchanged
MihaZupan May 4, 2024
64e1932
Add support for using dictionaries
MihaZupan May 4, 2024
3b8e258
Add simple json fuzzing target
MihaZupan May 4, 2024
eda23f4
Mention SharpFuzz in THIRD-PARTY-NOTICES.TXT
MihaZupan May 4, 2024
b428dd9
Tweak readme
MihaZupan May 4, 2024
aa3efe6
Don't reuse assembly if prefixes changed
MihaZupan May 6, 2024
9923f0c
Temporarily disable dictionary files
MihaZupan May 6, 2024
cd6161d
Avoid name conflicts between CI jobs and test submissions
MihaZupan May 7, 2024
0b2cd98
Add some basic OneFuzz docs
MihaZupan May 7, 2024
8113b8d
Add PooledBoundedMemory to fuzzer sample
MihaZupan May 7, 2024
6603ff2
Typo
MihaZupan May 7, 2024
eaf3d60
More docs
MihaZupan May 7, 2024
078d217
Avoid transcoding overhead in Json fuzzer
MihaZupan May 7, 2024
dcbda32
Enable cron schedule
MihaZupan May 7, 2024
53bab43
Tweak docs
MihaZupan May 7, 2024
03f5d8b
Fix OneFuzz dictionary file paths
MihaZupan May 7, 2024
12004ef
Workaround OneFuzz issue with multiple jobs in deployment
MihaZupan May 7, 2024
d209cf7
Clarify what alias to use
MihaZupan May 7, 2024
9fe39da
Test JsonDocument instead of JsonSerializer
MihaZupan May 9, 2024
02034a4
Get rid of BlameAlias
MihaZupan May 17, 2024
44116ff
Tweak getters
MihaZupan May 17, 2024
6f4cea3
Remove a few unused helpers
MihaZupan May 17, 2024
70857b2
Tweak OneFuzz workaround comment
MihaZupan May 17, 2024
fc79990
Remove StringBuilder use
MihaZupan May 20, 2024
fe24c98
Avoid misaligned-span UB
MihaZupan May 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions THIRD-PARTY-NOTICES.TXT
Original file line number Diff line number Diff line change
Expand Up @@ -1345,3 +1345,32 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
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.

License for SharpFuzz and related samples
--------------------------------------

https://github.com/Metalnem/sharpfuzz
https://github.com/Metalnem/dotnet-fuzzers
https://github.com/Metalnem/libfuzzer-dotnet

MIT License

Copyright (c) 2018 Nemanja Mijailovic

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.
107 changes: 107 additions & 0 deletions eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
trigger: none

schedules:
- cron: "0 10 * * *" # 10 AM UTC
displayName: OneFuzz deployment nightly run
branches:
include:
- main

variables:
- template: ../variables.yml
- name: fuzzerProject
value: $(Build.SourcesDirectory)/src/libraries/Fuzzing/DotnetFuzzing
- name: dotnetPath
value: $(Build.SourcesDirectory)/.dotnet/dotnet

extends:
template: /eng/pipelines/common/templates/pipeline-with-resources.yml
parameters:
stages:
- stage: Build
jobs:
- job: windows
displayName: Build & Deploy to OneFuzz
timeoutInMinutes: 120
pool:
name: $(DncEngInternalBuildPool)
demands: ImageOverride -equals windows.vs2022.amd64

steps:
- checkout: self
clean: true
fetchDepth: 1
lfs: false

- powershell: |
cd $(Build.SourcesDirectory)
./build.cmd clr+libs+packs+host -rc Checked -c Debug
displayName: Build runtime (checked + debug)

- powershell: |
cd $(fuzzerProject)
$(dotnetPath) publish -o publish
displayName: Build Fuzzing targets

- powershell: |
cd $(fuzzerProject)
$(dotnetPath) tool install --tool-path . SharpFuzz.CommandLine
displayName: Install SharpFuzz.CommandLine

- powershell: |
cd $(fuzzerProject)
publish/DotnetFuzzing.exe prepare-onefuzz deployment
displayName: Prepare OneFuzz deployment

# OneFuzz can't currently handle a single deployment where multiple jobs share similar assemblies/pdbs.
# As a workaround, we emit a task for every fuzzing target individually.
# https://fuzzfest.visualstudio.com/Onefuzz/_workitems/edit/191504/ is tracking this issue.
# - task: onefuzz-task@0
# inputs:
# onefuzzOSes: 'Windows'
# env:
# onefuzzDropDirectory: $(fuzzerProject)/deployment
# SYSTEM_ACCESSTOKEN: $(System.AccessToken)
# displayName: Send to OneFuzz

# ONEFUZZ_TASK_WORKAROUND_START
- task: onefuzz-task@0
inputs:
onefuzzOSes: 'Windows'
env:
onefuzzDropDirectory: $(fuzzerProject)/deployment/HttpHeadersFuzzer
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Send HttpHeadersFuzzer to OneFuzz

- task: onefuzz-task@0
inputs:
onefuzzOSes: 'Windows'
env:
onefuzzDropDirectory: $(fuzzerProject)/deployment/JsonDocumentFuzzer
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Send JsonDocumentFuzzer to OneFuzz

- task: onefuzz-task@0
inputs:
onefuzzOSes: 'Windows'
env:
onefuzzDropDirectory: $(fuzzerProject)/deployment/SearchValuesByteCharFuzzer
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Send SearchValuesByteCharFuzzer to OneFuzz

- task: onefuzz-task@0
inputs:
onefuzzOSes: 'Windows'
env:
onefuzzDropDirectory: $(fuzzerProject)/deployment/SearchValuesStringFuzzer
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Send SearchValuesStringFuzzer to OneFuzz

- task: onefuzz-task@0
inputs:
onefuzzOSes: 'Windows'
env:
onefuzzDropDirectory: $(fuzzerProject)/deployment/UTF8Fuzzer
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Send UTF8Fuzzer to OneFuzz
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So for right now, until the workaround is no longer needed, to add new tests one adds a new test file and then also updates this file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

When you add a new target and test it locally, the prepare-onefuzz part of
dotnet publish -o publish && publish/DotnetFuzzing.exe prepare-onefuzz deployment
will also update this file for you, so you should only have to:

  • Add 1 file
  • Test changes
  • Commit the 2 changed files

# ONEFUZZ_TASK_WORKAROUND_END
5 changes: 5 additions & 0 deletions src/libraries/Fuzzing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
TempNugetCache
publish
deployment
inputs
crash-*
8 changes: 8 additions & 0 deletions src/libraries/Fuzzing/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<NetCoreAppCurrentVersion>9.0</NetCoreAppCurrentVersion>
<NetCoreAppCurrent>net$(NetCoreAppCurrentVersion)</NetCoreAppCurrent>
Comment on lines +3 to +4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can't share the same settings that the rest of the repo uses?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be possible, I just gave up after spending a few hours bashing my head against different configurations that wouldn't compile.
We have similar isolated configurations for HttpStress / SslStress.

<ProductVersion>$(NetCoreAppCurrentVersion).0</ProductVersion>
<TestUtilities>..\..\Common\tests\TestUtilities</TestUtilities>
</PropertyGroup>
</Project>
2 changes: 2 additions & 0 deletions src/libraries/Fuzzing/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<Project>
</Project>
36 changes: 36 additions & 0 deletions src/libraries/Fuzzing/DotnetFuzzing.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.34807.36
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetFuzzing", "DotnetFuzzing\DotnetFuzzing.csproj", "{002673BF-11AE-4072-9CBE-FC312BF68613}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6DDAE3F1-325F-468C-92DB-5947A3AD757E}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
..\..\..\eng\pipelines\libraries\fuzzing\deploy-to-onefuzz.yml = ..\..\..\eng\pipelines\libraries\fuzzing\deploy-to-onefuzz.yml
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
nuget.config = nuget.config
README.md = README.md
OneFuzz.md = OneFuzz.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{002673BF-11AE-4072-9CBE-FC312BF68613}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{002673BF-11AE-4072-9CBE-FC312BF68613}.Debug|Any CPU.Build.0 = Debug|Any CPU
{002673BF-11AE-4072-9CBE-FC312BF68613}.Release|Any CPU.ActiveCfg = Release|Any CPU
{002673BF-11AE-4072-9CBE-FC312BF68613}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0BAD3B6E-67D4-418D-A45F-49B3F79168DF}
EndGlobalSection
EndGlobal
53 changes: 53 additions & 0 deletions src/libraries/Fuzzing/DotnetFuzzing/Assert.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;

namespace DotnetFuzzing;

internal static class Assert
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use xunit's asserts directly, and/or AssertExtensions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd have to figure out #101993 (comment) first.
Given it's just helpers for Equal(T, T) and Equal(Span<T>, Span<T>) at this point, I didn't think it mattered enough.

{
// Feel free to add any other helpers as needed.

public static void Equal<T>(T expected, T actual)
{
if (!EqualityComparer<T>.Default.Equals(expected, actual))
{
Throw(expected, actual);
}

static void Throw(T expected, T actual) =>
throw new Exception($"Expected={expected} Actual={actual}");
}

public static void SequenceEqual<T>(ReadOnlySpan<T> expected, ReadOnlySpan<T> actual)
{
if (!expected.SequenceEqual(actual))
{
Throw(expected, actual);
}

static void Throw(ReadOnlySpan<T> expected, ReadOnlySpan<T> actual)
{
Equal(expected.Length, actual.Length);

int diffIndex = expected.CommonPrefixLength(actual);

throw new Exception($"Expected={expected[diffIndex]} Actual={actual[diffIndex]} at index {diffIndex}");
}
}

public static void SequenceEqual(ReadOnlySpan<char> expected, StringBuilder actual)
{
Equal(expected.Length, actual.Length);

foreach (ReadOnlyMemory<char> chunk in actual.GetChunks())
{
SequenceEqual(expected.Slice(0, chunk.Length), chunk.Span);
MihaZupan marked this conversation as resolved.
Show resolved Hide resolved

expected = expected.Slice(chunk.Length);
}

Equal(0, expected.Length);
}
}
73 changes: 73 additions & 0 deletions src/libraries/Fuzzing/DotnetFuzzing/Dictionaries/json.dict
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"0"
"7"
","
":"
"2.1e24"

"true"
"false"
"null"

"\"\""
"\"\":"

"{}"
",{}"
":{}"
"{\"\":0}"
"{{}}"

"[]"
",[]"
":[]"
"[0]"
"[[]]"

"''"
"\\"
"\\b"
"\\f"
"\\n"
"\\r"
"\\t"
"\\u0000"
"\\x00"
"\\0"
"\\uD800\\uDC00"
"\\uDBFF\\uDFFF"

"\"\":0"
"//"
"/**/"


# Things like geojson, json-ld, ...
"$ref"
"type"
"coordinates"
"@context"
"@id"
"@type"

# Strings with truncated special values
"{\"foo\":fa"
"{\"foo\":t"
"{\"foo\":nul"

"{"
"}"
"\"qty\": 1, \"qty\": -1"
"\"qty\": 1, \"qty\\ud800\": -1"
"\"qty\": 1, \"qt\\y\": -1"
"/*"
"*/"
"\""
"1.7976931348623157e+308"
"5e-324"
"9007199254740991"
"-9007199254740991"

"}="

",,"
"{\"\":"
33 changes: 33 additions & 0 deletions src/libraries/Fuzzing/DotnetFuzzing/DotnetFuzzing.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(NetCoreAppCurrent)-windows</TargetFramework>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only works on Windows?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As-is, yes.

We could make it work on Linux as well if needed, it's "just work". Namely updating the pipeline, and tweaking how we consume local bits to avoid the limitation in SharpFuzz to instrument mixed-mode assemblies only on Windows.

<PublishSelfContained>true</PublishSelfContained>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnablePreviewFeatures>True</EnablePreviewFeatures>
<NoWarn>CA2252</NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="SharpFuzz" Version="2.1.1" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Update="Microsoft.NETCore.App" RuntimeFrameworkVersion="$(ProductVersion)-dev" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(TestUtilities)\System\Buffers\BoundedMemory.*" Link="TestUtilities\%(Filename)%(Extension)" />
<Compile Include="$(TestUtilities)\System\Buffers\PoisonPagePlacement.cs" Link="TestUtilities\PoisonPagePlacement.cs" />
</ItemGroup>

<ItemGroup>
<None Update="Dictionaries\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Loading