< Summary

Information
Class: DotnetFuzzing.Fuzzers.Base64Fuzzer
Assembly: DotnetFuzzing
File(s): D:\runner\runtime\src\libraries\Fuzzing\DotnetFuzzing\Fuzzers\Base64Fuzzer.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 235
Coverable lines: 235
Total lines: 333
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 30
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
FuzzTarget(...)100%110%
TestCases(...)100%110%
TestBase64(...)0%12120%
TestBase64Chars(...)0%12120%
TestToStringToCharArray(...)100%110%
ToBase64_CalculateOutputLength(...)0%660%

File(s)

D:\runner\runtime\src\libraries\Fuzzing\DotnetFuzzing\Fuzzers\Base64Fuzzer.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers;
 5using System.Buffers.Text;
 6
 7namespace DotnetFuzzing.Fuzzers
 8{
 9    internal sealed class Base64Fuzzer : IFuzzer
 10    {
 11        private const int Base64LineBreakPosition = 76; // Needs to be in sync with Convert.Base64LineBreakPosition
 12
 013        public string[] TargetAssemblies => [];
 14
 015        public string[] TargetCoreLibPrefixes => ["System.Buffers.Text.Base64", "System.Convert"];
 16
 17        public void FuzzTarget(ReadOnlySpan<byte> bytes)
 018        {
 019            using PooledBoundedMemory<byte> inputPoisonBefore = PooledBoundedMemory<byte>.Rent(bytes, PoisonPagePlacemen
 020            using PooledBoundedMemory<byte> inputPoisonAfter = PooledBoundedMemory<byte>.Rent(bytes, PoisonPagePlacement
 21
 022            TestCases(inputPoisonBefore.Span, PoisonPagePlacement.Before);
 023            TestCases(inputPoisonAfter.Span, PoisonPagePlacement.After);
 024        }
 25
 26        private void TestCases(Span<byte> input, PoisonPagePlacement poison)
 027        {
 028            TestBase64(input, poison);
 029            TestBase64Chars(input, poison);
 030            TestToStringToCharArray(input, Base64FormattingOptions.None);
 031            TestToStringToCharArray(input, Base64FormattingOptions.InsertLineBreaks);
 032        }
 33
 34        private void TestBase64(Span<byte> input, PoisonPagePlacement poison)
 035        {
 036            int maxEncodedLength = Base64.GetMaxEncodedToUtf8Length(input.Length);
 037            using PooledBoundedMemory<byte> destPoisoned = PooledBoundedMemory<byte>.Rent(maxEncodedLength, poison);
 038            Span<byte> encoderDest = destPoisoned.Span;
 039            using PooledBoundedMemory<byte> decoderDestPoisoned = PooledBoundedMemory<byte>.Rent(Base64.GetMaxDecodedFro
 040            Span<byte> decoderDest = decoderDestPoisoned.Span;
 041            { // IsFinalBlock = true
 042                OperationStatus status = Base64.EncodeToUtf8(input, encoderDest, out int bytesConsumed, out int bytesEnc
 043                Assert.Equal(OperationStatus.Done, status);
 044                Assert.Equal(input.Length, bytesConsumed);
 045                Assert.Equal(true, maxEncodedLength >= bytesEncoded && maxEncodedLength - 2 <= bytesEncoded);
 46
 047                status = Base64.DecodeFromUtf8(encoderDest.Slice(0, bytesEncoded), decoderDest, out int bytesRead, out i
 48
 049                Assert.Equal(OperationStatus.Done, status);
 050                Assert.Equal(input.Length, bytesDecoded);
 051                Assert.Equal(bytesEncoded, bytesRead);
 052                Assert.SequenceEqual(input, decoderDest.Slice(0, bytesDecoded));
 053            }
 54
 055            { // IsFinalBlock = false
 056                encoderDest.Clear();
 057                decoderDest.Clear();
 058                OperationStatus status = Base64.EncodeToUtf8(input, encoderDest, out int bytesConsumed, out int bytesEnc
 059                Span<byte> decodeInput = encoderDest.Slice(0, bytesEncoded);
 60
 061                if (input.Length % 3 == 0)
 062                {
 063                    Assert.Equal(OperationStatus.Done, status);
 064                    Assert.Equal(input.Length, bytesConsumed);
 065                    Assert.Equal(true, maxEncodedLength == bytesEncoded);
 66
 067                    status = Base64.DecodeFromUtf8(decodeInput, decoderDest, out int bytesRead, out int bytesDecoded, is
 68
 069                    Assert.Equal(OperationStatus.Done, status);
 070                    Assert.Equal(input.Length, bytesDecoded);
 071                    Assert.Equal(bytesEncoded, bytesRead);
 072                    Assert.SequenceEqual(input, decoderDest.Slice(0, bytesDecoded));
 073                }
 74                else
 075                {
 076                    Assert.Equal(OperationStatus.NeedMoreData, status);
 077                    Assert.Equal(true, input.Length / 3 * 4 == bytesEncoded);
 78
 079                    status = Base64.DecodeFromUtf8(decodeInput, decoderDest, out int bytesRead, out int bytesDecoded, is
 80
 081                    if (decodeInput.Length % 4 == 0)
 082                    {
 083                        Assert.Equal(OperationStatus.Done, status);
 084                        Assert.Equal(bytesConsumed, bytesDecoded);
 085                        Assert.Equal(bytesEncoded, bytesRead);
 086                    }
 87                    else
 088                    {
 089                        Assert.Equal(OperationStatus.NeedMoreData, status);
 090                    }
 91
 092                    Assert.SequenceEqual(input.Slice(0, bytesDecoded), decoderDest.Slice(0, bytesDecoded));
 093                }
 094            }
 95
 096            { // Encode / decode in place
 097                encoderDest.Clear();
 098                input.CopyTo(encoderDest);
 099                OperationStatus status = Base64.EncodeToUtf8InPlace(encoderDest, input.Length, out int bytesEncoded);
 100
 0101                Assert.Equal(OperationStatus.Done, status);
 0102                Assert.Equal(true, maxEncodedLength >= bytesEncoded && maxEncodedLength - 2 <= bytesEncoded);
 103
 0104                status = Base64.DecodeFromUtf8InPlace(encoderDest.Slice(0, bytesEncoded), out int bytesDecoded);
 105
 0106                Assert.Equal(OperationStatus.Done, status);
 0107                Assert.Equal(input.Length, bytesDecoded);
 0108                Assert.SequenceEqual(input, encoderDest.Slice(0, bytesDecoded));
 0109            }
 110
 0111            { // Decode the random input directly, Assert IsValid result matches with decoded result
 0112                decoderDest.Clear();
 0113                if (Base64.IsValid(input, out int decodedLength))
 0114                {
 0115                    int maxDecodedLength = Base64.GetMaxDecodedFromUtf8Length(input.Length);
 0116                    OperationStatus status = Base64.DecodeFromUtf8(input, decoderDest, out int bytesRead, out int bytesD
 0117                    Assert.Equal(OperationStatus.Done, status);
 0118                    Assert.Equal(input.Length, bytesRead);
 0119                    Assert.Equal(decodedLength, bytesDecoded);
 0120                    Assert.Equal(true, maxDecodedLength >= bytesDecoded);
 121
 0122                    status = Base64.DecodeFromUtf8InPlace(input, out int inPlaceDecoded);
 0123                    Assert.Equal(OperationStatus.Done, status);
 0124                    Assert.Equal(bytesDecoded, inPlaceDecoded);
 0125                    Assert.SequenceEqual<byte>(input.Slice(0, inPlaceDecoded), decoderDest.Slice(0, bytesDecoded));
 0126                }
 127                else
 0128                {
 0129                    Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(input, decoderDest, out int _, out i
 0130                    Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8InPlace(input, out int inPlaceDecoded
 0131                }
 0132            }
 133
 0134            { // Test new simplified UTF-8 APIs
 135                // Test EncodeToUtf8 returning byte[]
 0136                byte[] encodedArray = Base64.EncodeToUtf8(input);
 0137                Assert.Equal(true, maxEncodedLength >= encodedArray.Length && maxEncodedLength - 2 <= encodedArray.Lengt
 138
 139                // Test EncodeToUtf8 returning int
 0140                encoderDest.Clear();
 0141                int charsWritten = Base64.EncodeToUtf8(input, encoderDest);
 0142                Assert.SequenceEqual(encodedArray.AsSpan(), encoderDest.Slice(0, charsWritten));
 143
 144                // Test TryEncodeToUtf8
 0145                encoderDest.Clear();
 0146                Assert.Equal(true, Base64.TryEncodeToUtf8(input, encoderDest, out int tryCharsWritten));
 0147                Assert.Equal(charsWritten, tryCharsWritten);
 0148                Assert.SequenceEqual(encodedArray.AsSpan(), encoderDest.Slice(0, tryCharsWritten));
 149
 150                // Test DecodeFromUtf8 returning byte[]
 0151                byte[] decodedArray = Base64.DecodeFromUtf8(encodedArray);
 0152                Assert.SequenceEqual(input, decodedArray.AsSpan());
 153
 154                // Test DecodeFromUtf8 returning int
 0155                decoderDest.Clear();
 0156                int bytesWritten = Base64.DecodeFromUtf8(encodedArray, decoderDest);
 0157                Assert.Equal(input.Length, bytesWritten);
 0158                Assert.SequenceEqual(input, decoderDest.Slice(0, bytesWritten));
 159
 160                // Test TryDecodeFromUtf8
 0161                decoderDest.Clear();
 0162                Assert.Equal(true, Base64.TryDecodeFromUtf8(encodedArray, decoderDest, out int tryBytesWritten));
 0163                Assert.Equal(input.Length, tryBytesWritten);
 0164                Assert.SequenceEqual(input, decoderDest.Slice(0, tryBytesWritten));
 165
 166                // Test TryEncodeToUtf8InPlace
 0167                using PooledBoundedMemory<byte> inPlaceBuffer = PooledBoundedMemory<byte>.Rent(maxEncodedLength, poison)
 0168                Span<byte> inPlaceDest = inPlaceBuffer.Span;
 0169                input.CopyTo(inPlaceDest);
 0170                Assert.Equal(true, Base64.TryEncodeToUtf8InPlace(inPlaceDest, input.Length, out int inPlaceWritten));
 0171                Assert.SequenceEqual(encodedArray.AsSpan(), inPlaceDest.Slice(0, inPlaceWritten));
 172
 173                // Test GetEncodedLength matches GetMaxEncodedToUtf8Length
 0174                Assert.Equal(Base64.GetMaxEncodedToUtf8Length(input.Length), Base64.GetEncodedLength(input.Length));
 175
 176                // Test GetMaxDecodedLength matches GetMaxDecodedFromUtf8Length
 0177                Assert.Equal(Base64.GetMaxDecodedFromUtf8Length(maxEncodedLength), Base64.GetMaxDecodedLength(maxEncoded
 0178            }
 0179        }
 180
 181        private static void TestBase64Chars(Span<byte> input, PoisonPagePlacement poison)
 0182        {
 0183            int encodedLength = Base64.GetEncodedLength(input.Length);
 0184            int maxDecodedLength = Base64.GetMaxDecodedLength(encodedLength);
 185
 0186            using PooledBoundedMemory<char> destPoisoned = PooledBoundedMemory<char>.Rent(encodedLength, poison);
 0187            using PooledBoundedMemory<byte> decoderDestPoisoned = PooledBoundedMemory<byte>.Rent(maxDecodedLength, poiso
 188
 0189            Span<char> encoderDest = destPoisoned.Span;
 0190            Span<byte> decoderDest = decoderDestPoisoned.Span;
 191
 0192            { // IsFinalBlock = true
 0193                OperationStatus status = Base64.EncodeToChars(input, encoderDest, out int bytesConsumed, out int charsEn
 194
 0195                Assert.Equal(OperationStatus.Done, status);
 0196                Assert.Equal(input.Length, bytesConsumed);
 0197                Assert.Equal(encodedLength, charsEncoded);
 198
 0199                string encodedString = Base64.EncodeToString(input);
 0200                Assert.Equal(encodedString, new string(encoderDest.Slice(0, charsEncoded)));
 201
 0202                status = Base64.DecodeFromChars(encoderDest.Slice(0, charsEncoded), decoderDest, out int charsRead, out 
 203
 0204                Assert.Equal(OperationStatus.Done, status);
 0205                Assert.Equal(input.Length, bytesDecoded);
 0206                Assert.Equal(charsEncoded, charsRead);
 0207                Assert.SequenceEqual(input, decoderDest.Slice(0, bytesDecoded));
 0208            }
 209
 0210            { // IsFinalBlock = false
 0211                encoderDest.Clear();
 0212                decoderDest.Clear();
 0213                OperationStatus status = Base64.EncodeToChars(input, encoderDest, out int bytesConsumed, out int charsEn
 0214                Span<char> decodeInput = encoderDest.Slice(0, charsEncoded);
 215
 0216                if (input.Length % 3 == 0)
 0217                {
 0218                    Assert.Equal(OperationStatus.Done, status);
 0219                    Assert.Equal(input.Length, bytesConsumed);
 0220                    Assert.Equal(encodedLength, charsEncoded);
 221
 0222                    status = Base64.DecodeFromChars(decodeInput, decoderDest, out int charsRead, out int bytesDecoded, i
 223
 0224                    Assert.Equal(OperationStatus.Done, status);
 0225                    Assert.Equal(input.Length, bytesDecoded);
 0226                    Assert.Equal(charsEncoded, charsRead);
 0227                    Assert.SequenceEqual(input, decoderDest.Slice(0, bytesDecoded));
 0228                }
 229                else
 0230                {
 0231                    Assert.Equal(OperationStatus.NeedMoreData, status);
 0232                    Assert.Equal(true, input.Length / 3 * 4 == charsEncoded);
 233
 0234                    status = Base64.DecodeFromChars(decodeInput, decoderDest, out int charsRead, out int bytesDecoded, i
 235
 0236                    if (decodeInput.Length % 4 == 0)
 0237                    {
 0238                        Assert.Equal(OperationStatus.Done, status);
 0239                        Assert.Equal(bytesConsumed, bytesDecoded);
 0240                        Assert.Equal(charsEncoded, charsRead);
 0241                    }
 242                    else
 0243                    {
 0244                        Assert.Equal(OperationStatus.NeedMoreData, status);
 0245                    }
 246
 0247                    Assert.SequenceEqual(input.Slice(0, bytesDecoded), decoderDest.Slice(0, bytesDecoded));
 0248                }
 0249            }
 250
 0251            { // Test array-returning and int-returning overloads
 0252                char[] encodedChars = Base64.EncodeToChars(input);
 0253                Assert.Equal(encodedLength, encodedChars.Length);
 254
 0255                encoderDest.Clear();
 0256                int charsWritten = Base64.EncodeToChars(input, encoderDest);
 0257                Assert.Equal(encodedLength, charsWritten);
 0258                Assert.SequenceEqual(encodedChars.AsSpan(), encoderDest.Slice(0, charsWritten));
 259
 0260                byte[] decodedBytes = Base64.DecodeFromChars(encodedChars);
 0261                Assert.SequenceEqual(input, decodedBytes.AsSpan());
 262
 0263                decoderDest.Clear();
 0264                int bytesWritten = Base64.DecodeFromChars(encodedChars, decoderDest);
 0265                Assert.Equal(input.Length, bytesWritten);
 0266                Assert.SequenceEqual(input, decoderDest.Slice(0, bytesWritten));
 0267            }
 268
 0269            { // Test Try* variants
 0270                encoderDest.Clear();
 0271                Assert.Equal(true, Base64.TryEncodeToChars(input, encoderDest, out int charsWritten));
 0272                Assert.Equal(encodedLength, charsWritten);
 273
 0274                decoderDest.Clear();
 0275                Assert.Equal(true, Base64.TryDecodeFromChars(encoderDest.Slice(0, charsWritten), decoderDest, out int by
 0276                Assert.Equal(input.Length, bytesWritten);
 0277                Assert.SequenceEqual(input, decoderDest.Slice(0, bytesWritten));
 0278            }
 279
 0280            { // Decode the random chars directly (as chars, from the input bytes interpreted as UTF-16)
 281                // Create a char span from the input bytes for testing decode with random data
 0282                if (input.Length >= 2)
 0283                {
 0284                    ReadOnlySpan<char> inputChars = System.Runtime.InteropServices.MemoryMarshal.Cast<byte, char>(input)
 0285                    decoderDest.Clear();
 286
 287                    // Try decoding - may succeed or fail depending on if input is valid base64
 0288                    OperationStatus status = Base64.DecodeFromChars(inputChars, decoderDest, out int charsConsumed, out 
 289                    // Just verify we don't crash - the result depends on input validity
 0290                    Assert.Equal(true, status == OperationStatus.Done || status == OperationStatus.InvalidData ||
 0291                                       status == OperationStatus.NeedMoreData || status == OperationStatus.DestinationTo
 0292                }
 0293            }
 0294        }
 295
 296        private static void TestToStringToCharArray(Span<byte> input, Base64FormattingOptions options)
 0297        {
 0298            int encodedLength = ToBase64_CalculateOutputLength(input.Length, options == Base64FormattingOptions.InsertLi
 0299            char[] dest = new char[encodedLength];
 300
 0301            string toStringResult = Convert.ToBase64String(input, options);
 0302            byte[] decoded = Convert.FromBase64String(toStringResult);
 303
 0304            Assert.SequenceEqual(input, decoded);
 305
 0306            int written = Convert.ToBase64CharArray(input.ToArray(), 0, input.Length, dest, 0, options);
 0307            decoded = Convert.FromBase64CharArray(dest, 0, written);
 308
 0309            Assert.SequenceEqual(input, decoded);
 0310            Assert.SequenceEqual(toStringResult.AsSpan(), dest.AsSpan(0, written));
 0311        }
 312
 313        private static int ToBase64_CalculateOutputLength(int inputLength, bool insertLineBreaks)
 0314        {
 0315            uint outlen = ((uint)inputLength + 2) / 3 * 4;
 316
 0317            if (outlen == 0)
 0318                return 0;
 319
 0320            if (insertLineBreaks)
 0321            {
 0322                (uint newLines, uint remainder) = Math.DivRem(outlen, Base64LineBreakPosition);
 0323                if (remainder == 0)
 0324                {
 0325                    --newLines;
 0326                }
 0327                outlen += newLines * 2; // 2 line break chars added: "\r\n"
 0328            }
 329
 0330            return (int)outlen;
 0331        }
 332    }
 333}