Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
8 changes: 8 additions & 0 deletions eng/pipelines/libraries/fuzzing/deploy-to-onefuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ extends:
# displayName: Send to OneFuzz

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

- task: onefuzz-task@0
inputs:
onefuzzOSes: 'Windows'
Expand Down
105 changes: 105 additions & 0 deletions src/libraries/Fuzzing/DotnetFuzzing/Fuzzers/Base64Fuzzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.


using System.Buffers;
using System.Buffers.Text;

namespace DotnetFuzzing.Fuzzers
{
internal class Base64Fuzzer : IFuzzer
{
public string[] TargetAssemblies => [];

public string[] TargetCoreLibPrefixes => ["System.Buffers.Text"];

public void FuzzTarget(ReadOnlySpan<byte> bytes)
{
using PooledBoundedMemory<byte> inputPoisoned = PooledBoundedMemory<byte>.Rent(bytes, PoisonPagePlacement.After);
Span<byte> input = inputPoisoned.Span;
int maxEncodedLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length);
using PooledBoundedMemory<byte> destPoisoned = PooledBoundedMemory<byte>.Rent(maxEncodedLength, PoisonPagePlacement.After);
Span<byte> encoderDest = destPoisoned.Span;

{ // IsFinalBlock = true
OperationStatus status = Base64.EncodeToUtf8(input, encoderDest, out int bytesConsumed, out int bytesEncoded);

Assert.Equal(OperationStatus.Done, status);
Assert.Equal(bytes.Length, bytesConsumed);
Assert.Equal(true, maxEncodedLength >= bytesEncoded);

using PooledBoundedMemory<byte> decoderDestPoisoned = PooledBoundedMemory<byte>.Rent(Base64.GetMaxDecodedFromUtf8Length(bytesEncoded), PoisonPagePlacement.After);
status = Base64.DecodeFromUtf8(encoderDest.Slice(0, bytesEncoded), decoderDestPoisoned.Span, out int bytesRead, out int bytesDecoded);

Assert.Equal(OperationStatus.Done, status);
Assert.Equal(bytes.Length, bytesDecoded);
Assert.Equal(bytesEncoded, bytesRead);
Assert.SequenceEqual(bytes, decoderDestPoisoned.Span.Slice(0, bytesDecoded));
}

{ // IsFinalBlock = false
encoderDest.Clear();
OperationStatus status = Base64.EncodeToUtf8(input, encoderDest, out int bytesConsumed, out int bytesEncoded, isFinalBlock: false);
Span<byte> decodeInput = encoderDest.Slice(0, bytesEncoded);
using PooledBoundedMemory<byte> decoderDestPoisoned = PooledBoundedMemory<byte>.Rent(Base64.GetMaxDecodedFromUtf8Length(bytesEncoded), PoisonPagePlacement.After);
Span<byte> decoderDest = decoderDestPoisoned.Span;

if (bytes.Length % 3 == 0)
{
Assert.Equal(OperationStatus.Done, status);
Assert.Equal(bytes.Length, bytesConsumed);
Assert.Equal(true, maxEncodedLength >= bytesEncoded);


status = Base64.DecodeFromUtf8(decodeInput, decoderDest, out int bytesRead, out int bytesDecoded, isFinalBlock: false);

Assert.Equal(OperationStatus.Done, status);
Assert.Equal(bytes.Length, bytesDecoded);
Assert.Equal(bytesEncoded, bytesRead);
Assert.SequenceEqual(bytes, decoderDest.Slice(0, bytesDecoded));
}
else
{
Assert.Equal(OperationStatus.NeedMoreData, status);
Assert.Equal(true, maxEncodedLength >= bytesEncoded);

status = Base64.DecodeFromUtf8(decodeInput, decoderDest, out int bytesRead, out int bytesDecoded, isFinalBlock: false);

if (decodeInput.Length % 4 == 0)
{
Assert.Equal(OperationStatus.Done, status);
Assert.Equal(bytesConsumed, bytesDecoded);
Assert.Equal(bytesEncoded, bytesRead);
}
else
{
Assert.Equal(OperationStatus.NeedMoreData, status);
Assert.SequenceEqual(bytes.Slice(0, bytesDecoded), decoderDest.Slice(0, bytesDecoded));
}
}
}

{ // Encode / decode in place
encoderDest.Clear();
input.CopyTo(encoderDest);
OperationStatus status = Base64.EncodeToUtf8InPlace(encoderDest, input.Length, out int bytesEncoded);

Assert.Equal(OperationStatus.Done, status);
Assert.Equal(true, maxEncodedLength >= bytesEncoded);

status = Base64.DecodeFromUtf8InPlace(encoderDest.Slice(0, bytesEncoded), out int bytesDecoded);

Assert.Equal(OperationStatus.Done, status);
Assert.Equal(bytes.Length, bytesDecoded);
Assert.SequenceEqual(bytes, encoderDest.Slice(0, bytesDecoded));
}

{ // Run IsValid, Decode overloads with the random input values
encoderDest.Clear();
Base64.IsValid(input);
Base64.DecodeFromUtf8(input, encoderDest, out _, out _);
Base64.DecodeFromUtf8InPlace(input, out _);
}
}
}
}