Skip to content

Commit 000c116

Browse files
anamnavianamnavi
andauthored
Add Fuzz Tests (PowerShell#26384)
Co-authored-by: anamnavi <annavied@microsoft.com>
1 parent ac55117 commit 000c116

File tree

8 files changed

+200
-6
lines changed

8 files changed

+200
-6
lines changed

src/System.Management.Automation/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
[assembly: InternalsVisibleTo("powershell-tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
99
[assembly: InternalsVisibleTo("powershell-perf,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
10+
[assembly: InternalsVisibleTo("powershell-fuzz-tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
1011

1112
[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.Commands.Utility" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
1213
[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.Commands.Management" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]

src/System.Management.Automation/engine/remoting/common/RemoteSessionHyperVSocket.cs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,10 @@ internal static void ValidateToken(Socket socket, string token, DateTimeOffset t
434434
// Final check if we got the token before the timeout
435435
cancellationToken.ThrowIfCancellationRequested();
436436

437-
if (string.IsNullOrEmpty(responseString) || !responseString.StartsWith("TOKEN ", StringComparison.Ordinal))
437+
ReadOnlySpan<byte> responseBytes = Encoding.UTF8.GetBytes(responseString);
438+
string responseToken = RemoteSessionHyperVSocketClient.ExtractToken(responseBytes);
439+
440+
if (responseToken == null)
438441
{
439442
socket.Send("FAIL"u8);
440443
// If the response is not in the expected format, we throw an exception.
@@ -444,9 +447,6 @@ internal static void ValidateToken(Socket socket, string token, DateTimeOffset t
444447
PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVInvalidResponse, "Client", "Token Response"));
445448
}
446449

447-
// Extract the token from the response.
448-
string responseToken = responseString.Substring(6).Trim();
449-
450450
if (!string.Equals(responseToken, token, StringComparison.Ordinal))
451451
{
452452
socket.Send("FAIL"u8);
@@ -1059,14 +1059,18 @@ public static (bool success, string authenticationToken) ExchangeCredentialsAndC
10591059
// allowing a significant larger size, allows the broker to make almost arbitrary changes,
10601060
// without breaking the client.
10611061
string token = ReceiveResponse(HyperVSocket, 1024); // either "PASS" or "FAIL"
1062-
if (token == null || !token.StartsWith("TOKEN ", StringComparison.Ordinal))
1062+
1063+
ReadOnlySpan<byte> tokenResponseBytes = Encoding.UTF8.GetBytes(token);
1064+
string extractedToken = ExtractToken(tokenResponseBytes);
1065+
1066+
if (extractedToken == null)
10631067
{
10641068
s_tracer.WriteLine("ExchangeCredentialsAndConfiguration: Server did not respond with a valid token. Response: {0}", token);
10651069
throw new PSDirectException(
10661070
PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVInvalidResponse, "Broker", "Token " + token));
10671071
}
10681072

1069-
token = token.Substring(6); // remove "TOKEN " prefix
1073+
token = extractedToken;
10701074

10711075
HyperVSocket.Send("PASS"u8); // acknowledge the token
10721076
return (true, token);
@@ -1122,6 +1126,25 @@ internal static string ReceiveResponse(Socket socket, int bufferSize)
11221126
}
11231127
}
11241128

1129+
internal static string ExtractToken(ReadOnlySpan<byte> tokenResponse)
1130+
{
1131+
string token = Encoding.UTF8.GetString(tokenResponse);
1132+
1133+
if (token == null || !token.StartsWith("TOKEN ", StringComparison.Ordinal))
1134+
{
1135+
return null; // caller method will write trace (and determine when to expose token info as appropriate)
1136+
}
1137+
1138+
token = token.Substring(6).Trim(); // remove "TOKEN " prefix
1139+
1140+
if (token.Length == 0)
1141+
{
1142+
return null;
1143+
}
1144+
1145+
return token;
1146+
}
1147+
11251148
/// <summary>
11261149
/// Sends user data (domain, username, etc.) over the HyperVSocket using Unicode encoding.
11271150
/// </summary>

test/fuzzing/FuzzingApp/Program.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Text;
6+
using SharpFuzz;
7+
using System.Management.Automation.Remoting;
8+
9+
namespace FuzzTests
10+
{
11+
public static class Program
12+
{
13+
public static void Main(string[] args)
14+
{
15+
FuzzTargetMethod(args);
16+
}
17+
18+
public static void FuzzTargetMethod(string[] args)
19+
{
20+
if (args == null)
21+
{
22+
Console.WriteLine("args was null");
23+
args = Array.Empty<string>();
24+
}
25+
26+
try
27+
{
28+
Fuzzer.LibFuzzer.Run(Target.ExtractToken);
29+
}
30+
catch (System.ArgumentNullException nex)
31+
{
32+
Console.WriteLine($"ArgumentNullException in main: {nex.Message}");
33+
Console.WriteLine($"Stack Trace: {nex.StackTrace}");
34+
Environment.Exit(1);
35+
}
36+
catch (Exception ex)
37+
{
38+
Console.WriteLine($"Exception in main: {ex.Message}");
39+
Console.WriteLine($"Exception type: {ex.GetType()}");
40+
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
41+
Environment.Exit(1);
42+
}
43+
}
44+
}
45+
}

test/fuzzing/FuzzingApp/Target.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Text;
6+
using SharpFuzz;
7+
using System.Management.Automation.Remoting;
8+
9+
namespace FuzzTests
10+
{
11+
public static class Target
12+
{
13+
public static void ExtractToken(ReadOnlySpan<byte> tokenResponse)
14+
{
15+
RemoteSessionHyperVSocketClient.ExtractToken(tokenResponse);
16+
}
17+
}
18+
}
19+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>PowerShell Fuzzing</Description>
5+
<AssemblyName>powershell-fuzz-tests</AssemblyName>
6+
</PropertyGroup>
7+
8+
<PropertyGroup>
9+
<OutputType>Exe</OutputType>
10+
<TargetFramework>net10.0</TargetFramework>
11+
<ImplicitUsings>enable</ImplicitUsings>
12+
<Nullable>enable</Nullable>
13+
</PropertyGroup>
14+
15+
<PropertyGroup>
16+
<DelaySign>true</DelaySign>
17+
<AssemblyOriginatorKeyFile>../../../src/signing/visualstudiopublic.snk</AssemblyOriginatorKeyFile>
18+
<SignAssembly>true</SignAssembly>
19+
</PropertyGroup>
20+
21+
<ItemGroup>
22+
<ProjectReference Include="../../../src/System.Management.Automation/System.Management.Automation.csproj" />
23+
</ItemGroup>
24+
25+
<ItemGroup>
26+
<PackageReference Include="SharpFuzz" Version="2.2.0" />
27+
</ItemGroup>
28+
29+
</Project>

test/fuzzing/inputs/maxinput

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TOKEN abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=

test/fuzzing/inputs/mininput

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TOKEN TQ

test/fuzzing/runFuzzer.ps1

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
param (
5+
[string]$libFuzzer = ".\libfuzzer-dotnet-windows.exe",
6+
[string]$project = ".\FuzzingApp\powershell-fuzz-tests.csproj",
7+
[Parameter(Mandatory=$true)]
8+
[string]$corpus,
9+
[string]$command = "sharpfuzz.exe"
10+
)
11+
12+
Set-StrictMode -Version Latest
13+
14+
$outputDir = "out"
15+
16+
if (Test-Path $outputDir) {
17+
Remove-Item -Recurse -Force $outputDir
18+
}
19+
20+
Write-Host "dotnet publish $project -c release -o $outputDir"
21+
dotnet publish $project -c release -o $outputDir
22+
Write-Host "build completed"
23+
24+
$projectName = (Get-Item $project).BaseName
25+
$projectDll = "$projectName.dll"
26+
$project = Join-Path $outputDir $projectDll
27+
28+
$exclusions = @(
29+
"dnlib.dll",
30+
"SharpFuzz.dll",
31+
"SharpFuzz.Common.dll"
32+
)
33+
34+
$exclusions += $projectDll
35+
36+
Write-Host "instrumenting: $project"
37+
& $command $project Target
38+
Write-Host "done instrumenting $project"
39+
40+
$fuzzingTargets = Get-ChildItem $outputDir -Filter *.dll `
41+
| Where-Object { $_.Name -notin $exclusions } `
42+
| Where-Object { $_.Name -notlike "System.*.dll" }
43+
| Where-Object { $_.Name -notlike "Newtonsoft.*.dll" }
44+
| Where-Object { $_.Name -notlike "Microsoft.*.dll" }
45+
46+
foreach ($fuzzingTarget in $fuzzingTargets) {
47+
Write-Output "Instrumenting $fuzzingTarget"
48+
& $command $fuzzingTarget.FullName
49+
50+
if ($LastExitCode -ne 0) {
51+
Write-Error "An error occurred while instrumenting $fuzzingTarget"
52+
exit 1
53+
}
54+
}
55+
56+
$smaDllPath = Join-Path $outputDir "System.Management.Automation.dll"
57+
58+
Write-Host "instrumenting: $smaDllPath"
59+
& $command $smaDllPath Remoting
60+
Write-Host "done instrumenting: $smaDllPath"
61+
62+
$fuzzingTargets += $projectDll
63+
$fuzzingTargets += $smaDllPath
64+
65+
if (($fuzzingTargets | Measure-Object).Count -eq 0) {
66+
Write-Error "No fuzzing targets found"
67+
exit 1
68+
}
69+
70+
$outputPath = Join-Path $outputDir "output.txt"
71+
72+
Write-Host "launching fuzzer on $project"
73+
Write-Host "$libFuzzer --target_path=dotnet --target_arg=$project $corpus"
74+
& $libFuzzer --target_path=dotnet --target_arg=$project $corpus -max_len=1024 2>&1 `
75+
| Tee-Object -FilePath $outputPath

0 commit comments

Comments
 (0)