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

[Fuzz] Add Fuzz testing for RegistryPreview #37607

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Changes from 17 commits
Commits
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
11 changes: 11 additions & 0 deletions PowerToys.sln
Original file line number Diff line number Diff line change
@@ -646,6 +646,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hosts.FuzzTests", "src\modu
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hosts.UITests", "src\modules\Hosts\Hosts.UITests\Hosts.UITests.csproj", "{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegistryPreview.FuzzTests", "src\modules\registrypreview\RegistryPreview.FuzzTests\RegistryPreview.FuzzTests.csproj", "{F99690AC-D81C-4371-81F2-E2E1A62A28F1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2294,6 +2296,14 @@ Global
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}.Release|ARM64.Build.0 = Release|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}.Release|x64.ActiveCfg = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}.Release|x64.Build.0 = Release|x64
{F99690AC-D81C-4371-81F2-E2E1A62A28F1}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F99690AC-D81C-4371-81F2-E2E1A62A28F1}.Debug|ARM64.Build.0 = Debug|ARM64
{F99690AC-D81C-4371-81F2-E2E1A62A28F1}.Debug|x64.ActiveCfg = Debug|x64
{F99690AC-D81C-4371-81F2-E2E1A62A28F1}.Debug|x64.Build.0 = Debug|x64
{F99690AC-D81C-4371-81F2-E2E1A62A28F1}.Release|ARM64.ActiveCfg = Release|ARM64
{F99690AC-D81C-4371-81F2-E2E1A62A28F1}.Release|ARM64.Build.0 = Release|ARM64
{F99690AC-D81C-4371-81F2-E2E1A62A28F1}.Release|x64.ActiveCfg = Release|x64
{F99690AC-D81C-4371-81F2-E2E1A62A28F1}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2534,6 +2544,7 @@ Global
{4382A954-179A-4078-92AF-715187DFFF50} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
{EBED240C-8702-452D-B764-6DB9DA9179AF} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{F99690AC-D81C-4371-81F2-E2E1A62A28F1} = {929C1324-22E8-4412-A9A8-80E85F3985A5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
1 change: 1 addition & 0 deletions deps/cxxopts
Submodule cxxopts added at 12e496
2 changes: 1 addition & 1 deletion deps/cziplib
2 changes: 1 addition & 1 deletion deps/spdlog
Submodule spdlog updated 74 files
+1 −1 .clang-tidy
+4 −47 CMakeLists.txt
+2 −6 README.md
+7 −27 appveyor.yml
+1 −18 bench/CMakeLists.txt
+0 −5 bench/async_bench.cpp
+0 −5 bench/bench.cpp
+5 −5 bench/formatter-bench.cpp
+0 −14 cmake/spdlogCPack.cmake
+0 −1 cmake/utils.cmake
+2 −2 example/example.cpp
+0 −1 include/spdlog/cfg/helpers-inl.h
+10 −12 include/spdlog/common-inl.h
+7 −40 include/spdlog/common.h
+1 −2 include/spdlog/details/backtracer.h
+2 −17 include/spdlog/details/file_helper-inl.h
+1 −3 include/spdlog/details/log_msg_buffer-inl.h
+0 −6 include/spdlog/details/mpmc_blocking_q.h
+22 −67 include/spdlog/details/os-inl.h
+2 −9 include/spdlog/details/os.h
+1 −1 include/spdlog/details/registry.h
+1 −2 include/spdlog/details/synchronous_factory.h
+1 −1 include/spdlog/details/tcp_client-windows.h
+2 −2 include/spdlog/details/tcp_client.h
+1 −6 include/spdlog/details/thread_pool-inl.h
+0 −1 include/spdlog/details/thread_pool.h
+42 −37 include/spdlog/fmt/bundled/chrono.h
+10 −47 include/spdlog/fmt/bundled/color.h
+23 −59 include/spdlog/fmt/bundled/compile.h
+104 −344 include/spdlog/fmt/bundled/core.h
+273 −1,621 include/spdlog/fmt/bundled/format-inl.h
+463 −694 include/spdlog/fmt/bundled/format.h
+29 −15 include/spdlog/fmt/bundled/locale.h
+37 −67 include/spdlog/fmt/bundled/os.h
+9 −19 include/spdlog/fmt/bundled/ostream.h
+2 −2 include/spdlog/fmt/bundled/printf.h
+6 −16 include/spdlog/fmt/bundled/ranges.h
+1 −2 include/spdlog/fmt/fmt.h
+23 −24 include/spdlog/logger.h
+4 −26 include/spdlog/pattern_formatter-inl.h
+2 −2 include/spdlog/pattern_formatter.h
+0 −2 include/spdlog/sinks/ansicolor_sink-inl.h
+1 −1 include/spdlog/sinks/base_sink.h
+1 −2 include/spdlog/sinks/basic_file_sink.h
+0 −34 include/spdlog/sinks/daily_file_sink.h
+1 −1 include/spdlog/sinks/dup_filter_sink.h
+0 −194 include/spdlog/sinks/hourly_file_sink.h
+1 −2 include/spdlog/sinks/stdout_color_sinks-inl.h
+3 −19 include/spdlog/sinks/stdout_sinks-inl.h
+5 −15 include/spdlog/sinks/win_eventlog_sink.h
+54 −59 include/spdlog/sinks/wincolor_sink-inl.h
+19 −10 include/spdlog/sinks/wincolor_sink.h
+17 −17 include/spdlog/spdlog.h
+1 −51 include/spdlog/tweakme.h
+1 −1 include/spdlog/version.h
+8 −8 scripts/extract_version.py
+1 −2 src/async.cpp
+1 −2 src/cfg.cpp
+0 −1 src/color_sinks.cpp
+1 −2 src/file_sinks.cpp
+21 −61 src/fmt.cpp
+1 −2 src/spdlog.cpp
+1 −2 src/stdout_sinks.cpp
+6 −8 tests/test_async.cpp
+41 −35 tests/test_create_dir.cpp
+18 −32 tests/test_daily_logger.cpp
+8 −11 tests/test_errors.cpp
+3 −3 tests/test_eventlog.cpp
+28 −26 tests/test_file_helper.cpp
+14 −16 tests/test_file_logging.cpp
+5 −7 tests/test_macros.cpp
+1 −1 tests/test_pattern_formatter.cpp
+1 −1 tests/test_registry.cpp
+3 −5 tests/utils.cpp
198 changes: 198 additions & 0 deletions src/modules/registrypreview/RegistryPreview.FuzzTests/FuzzTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Globalization;
using System.Resources;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Win32;
using RegistryPreviewUILib;

namespace RegistryPreview.FuzzTests
{
public class FuzzTests
{
private const string REGISTRYHEADER4 = "regedit4";
private const string REGISTRYHEADER5 = "windows registry editor version 5.00";
private const string KEYIMAGE = "ms-appx:///Assets/RegistryPreview/folder32.png";
private const string DELETEDKEYIMAGE = "ms-appx:///Assets/RegistryPreview/deleted-folder32.png";

// Case 1: Fuzz test for CheckKeyLineForBrackets
public static void FuzzCheckKeyLineForBrackets(ReadOnlySpan<byte> input)
{
string registryLine;

// Simulate registry file content as filenameText
var registryContent = GenerateRegistryHeader(input);

string[] registryLines = registryContent.Split("\r");

if (registryLines.Length <= 1)
{
return;
}

// REG files have to start with one of two headers and it's case-insensitive
// The header in the registry file is either REGISTRYHEADER4 or REGISTRYHEADER5
registryLine = registryLines[0];

// Check if the registry header is valid
if (!IsValidRegistryHeader(registryLine))
{
return;
}

int index = 1;
registryLine = registryLines[index]; // Extract content after the header

ParseHelper.ProcessRegistryLine(registryLine);
if (registryLine.StartsWith("[-", StringComparison.InvariantCulture))
{
// remove the - as we won't need it but it will get special treatment in the UI
registryLine = registryLine.Remove(1, 1);

string imageName = DELETEDKEYIMAGE;

// Fuzz test for the CheckKeyLineForBrackets method
ParseHelper.CheckKeyLineForBrackets(ref registryLine, ref imageName);
}
else if (registryLine.StartsWith('['))
{
string imageName = KEYIMAGE;

// Fuzz test for the CheckKeyLineForBrackets method
ParseHelper.CheckKeyLineForBrackets(ref registryLine, ref imageName);
}
else
{
return;
}
}

// Case 2: Fuzz test for StripFirstAndLast
public static void FuzzStripFirstAndLast(ReadOnlySpan<byte> input)
{
string registryLine;

var regisrtyContent = GenerateRegistryHeader(input);

string[] registryLines = regisrtyContent.Split("\r");

if (registryLines.Length <= 1)
{
return;
}

// REG files have to start with one of two headers and it's case-insensitive
registryLine = registryLines[0];

if (!IsValidRegistryHeader(registryLine))
{
return;
}

int index = 1;
registryLine = registryLines[index];

ParseHelper.ProcessRegistryLine(registryLine);

if (registryLine.StartsWith("[-", StringComparison.InvariantCulture))
{
// remove the - as we won't need it but it will get special treatment in the UI
registryLine = registryLine.Remove(1, 1);

string imageName = DELETEDKEYIMAGE;
ParseHelper.CheckKeyLineForBrackets(ref registryLine, ref imageName);

// Fuzz test for the StripFirstAndLast method
registryLine = ParseHelper.StripFirstAndLast(registryLine);
}
else if (registryLine.StartsWith('['))
{
string imageName = KEYIMAGE;
ParseHelper.CheckKeyLineForBrackets(ref registryLine, ref imageName);

// Fuzz test for the StripFirstAndLast method
registryLine = ParseHelper.StripFirstAndLast(registryLine);
}
else if (registryLine.StartsWith('"') && registryLine.EndsWith("=-", StringComparison.InvariantCulture))
{
registryLine = registryLine.Replace("=-", string.Empty);

// remove the "'s without removing all of them
// Fuzz test for the StripFirstAndLast method
registryLine = ParseHelper.StripFirstAndLast(registryLine);
}
else if (registryLine.StartsWith('"'))
{
int equal = registryLine.IndexOf('=');
if ((equal < 0) || (equal > registryLine.Length - 1))
{
// something is very wrong
return;
}

// set the name and the value
string name = registryLine.Substring(0, equal);

// trim the whitespace and quotes from the name
name = name.Trim();

// Fuzz test for the StripFirstAndLast method
name = ParseHelper.StripFirstAndLast(name);

// Clean out any escaped characters in the value, only for the preview
name = ParseHelper.StripEscapedCharacters(name);

// set the value
string value = registryLine.Substring(equal + 1);

// trim the whitespace from the value
value = value.Trim();

// if the first character is a " then this is a string value, so find the last most " which will avoid comments
if (value.StartsWith('"'))
{
int last = value.LastIndexOf('"');
if (last >= 0)
{
value = value.Substring(0, last + 1);
}
}

if (value.StartsWith('"') && value.EndsWith('"'))
{
value = ParseHelper.StripFirstAndLast(value);
}
}
else
{
return;
}
}

public static string GenerateRegistryHeader(ReadOnlySpan<byte> input)
{
string header = new Random().Next(2) == 0 ? REGISTRYHEADER4 : REGISTRYHEADER5;

string inputText = System.Text.Encoding.UTF8.GetString(input);
string registryContent = header + "\r" + inputText;

return registryContent;
}

private static bool IsValidRegistryHeader(string line)
{
// Convert the line to lowercase once for comparison
switch (line)
Copy link
Contributor

Choose a reason for hiding this comment

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

I noticed that you're using switch (line), which relies on the original line, but you convert lineLower to lowercase for comparison. Which one do you actually intend to use?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I intend to use the line, and the lineLower needs to be removed.

{
case REGISTRYHEADER4:
case REGISTRYHEADER5:
return true;
default:
return false;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
{
"configVersion": 3,
"entries": [
{
"fuzzer": {
"$type": "libfuzzerDotNet",
"dll": "RegistryPreview.FuzzTests.dll",
"class": "RegistryPreview.FuzzTests.FuzzTests",
"method": "FuzzCheckKeyLineForBrackets",
"FuzzingTargetBinaries": [
"PowerToys.RegistryPreview.dll"
]
},
"adoTemplate": {
// supply the values appropriate to your
// project, where bugs will be filed
"org": "microsoft",
"project": "OS",
"AssignedTo": "mengyuanchen@microsoft.com",
"AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys",
"IterationPath": "OS\\Future"
},
"jobNotificationEmail": "mengyuanchen@microsoft.com",
"skip": false,
"rebootAfterSetup": false,
"oneFuzzJobs": [
// at least one job is required
{
"projectName": "RegistryPreview",
"targetName": "RegistryPreview-dotnet-CheckKeyLineForBrackets-fuzzer"
}
],
"jobDependencies": [
// this should contain, at minimum,
// the DLL and PDB files
// you will need to add any other files required
// (globs are supported)
"RegistryPreview.FuzzTests.dll",
"RegistryPreview.FuzzTests.pdb",
"Microsoft.Windows.SDK.NET.dll",
"WinRT.Runtime.dll"
],
"SdlWorkItemId": 49911822
},
{
"fuzzer": {
"$type": "libfuzzerDotNet",
"dll": "RegistryPreview.FuzzTests.dll",
"class": "RegistryPreview.FuzzTests.FuzzTests",
"method": "FuzzStripFirstAndLast",
"FuzzingTargetBinaries": [
"PowerToys.RegistryPreview.dll"
]
},
"adoTemplate": {
// supply the values appropriate to your
// project, where bugs will be filed
"org": "microsoft",
"project": "OS",
"AssignedTo": "mengyuanchen@microsoft.com",
"AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys",
"IterationPath": "OS\\Future"
},
"jobNotificationEmail": "mengyuanchen@microsoft.com",
"skip": false,
"rebootAfterSetup": false,
"oneFuzzJobs": [
// at least one job is required
{
"projectName": "RegistryPreview",
"targetName": "RegistryPreview-dotnet-StripFirstAndLasts-fuzzer"
}
],
"jobDependencies": [
// this should contain, at minimum,
// the DLL and PDB files
// you will need to add any other files required
// (globs are supported)
"RegistryPreview.FuzzTests.dll",
"RegistryPreview.FuzzTests.pdb",
"Microsoft.Windows.SDK.NET.dll",
"WinRT.Runtime.dll"
],
"SdlWorkItemId": 49911822
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<Platforms>x64;ARM64</Platforms>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\RegistryPreview.FuzzTests\</OutputPath>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\RegistryPreviewUILib\ParseHelper.cs" Link="ParseHelper.cs" />
</ItemGroup>

<ItemGroup>
<Content Include="OneFuzzConfig.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="MSTest" />
</ItemGroup>

<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>

</Project>
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.