< Summary

Information
Class: DotnetFuzzing.Fuzzers.WebSocketFuzzer
Assembly: DotnetFuzzing
File(s): D:\runner\runtime\src\libraries\Fuzzing\DotnetFuzzing\Fuzzers\WebSocketFuzzer.cs
Line coverage
91%
Covered lines: 116
Uncovered lines: 11
Coverable lines: 127
Total lines: 208
Line coverage: 91.3%
Branch coverage
100%
Covered branches: 38
Total branches: 38
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor()100%11100%
FuzzTarget(...)100%88100%
TestWebSocket()100%1616100%
TryCreateSegments(...)100%44100%
.ctor(...)100%11100%
Read(...)100%110%
ReadAsync(...)100%110%
ReadAsync(...)100%66100%
Flush()100%11100%
Write(...)100%110%
WriteAsync(...)100%110%
WriteAsync(...)100%11100%
SetLength(...)100%110%
Seek(...)100%110%

File(s)

D:\runner\runtime\src\libraries\Fuzzing\DotnetFuzzing\Fuzzers\WebSocketFuzzer.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;
 5using System.Buffers.Binary;
 6using System.Collections.Generic;
 7using System.Net.WebSockets;
 8using System.Text;
 9
 10namespace DotnetFuzzing.Fuzzers;
 11
 12internal sealed class WebSocketFuzzer : IFuzzer
 13{
 014    public string[] TargetAssemblies => ["System.Net.WebSockets"];
 015    public string[] TargetCoreLibPrefixes => [];
 16
 117    private readonly byte[] _inputBuffer = new byte[4096]; // Default max fuzzer input length
 118    private readonly ReadOnlyMemory<byte>[] _segments = new ReadOnlyMemory<byte>[4096];
 119    private readonly byte[] _receiveBuffer = new byte[ushort.MaxValue + 1];
 120    private readonly byte[] _clientOperationsBuffer = new byte[ushort.MaxValue + 1];
 21
 22    [Flags]
 23    private enum Options : byte
 24    {
 25        None = 0,
 26        IsServer = 1 << 0,
 27        EnableCompression = 1 << 1,
 28    }
 29
 30    public void FuzzTarget(ReadOnlySpan<byte> bytes)
 934931    {
 934932        if (bytes.Length < 5)
 433        {
 434            return;
 35        }
 36
 934537        Options options = (Options)bytes[0];
 934538        bytes = bytes.Slice(1);
 39
 934540        int receiveBufferLength = 1 + BinaryPrimitives.ReadUInt16BigEndian(bytes);
 934541        bytes = bytes.Slice(2);
 42
 934543        int clientOperationsBufferLength = BinaryPrimitives.ReadUInt16BigEndian(bytes);
 934544        bytes = bytes.Slice(2);
 45
 934546        if (bytes.Length < clientOperationsBufferLength)
 1047        {
 1048            return;
 49        }
 50
 933551        bytes.Slice(0, clientOperationsBufferLength).CopyTo(_clientOperationsBuffer);
 933552        bytes = bytes.Slice(clientOperationsBufferLength);
 933553        ReadOnlyMemory<byte> clientOperations = _clientOperationsBuffer.AsMemory(0, clientOperationsBufferLength);
 54
 933555        if (!TryCreateSegments(bytes, out Memory<ReadOnlyMemory<byte>> segments))
 2156        {
 2157            return;
 58        }
 59
 931460        var stream = new TrickleStream(segments);
 61
 931462        using WebSocket webSocket = WebSocket.CreateFromStream(stream, new WebSocketCreationOptions
 931463        {
 931464            KeepAliveInterval = Timeout.InfiniteTimeSpan,
 931465            KeepAliveTimeout = Timeout.InfiniteTimeSpan,
 931466            IsServer = options.HasFlag(Options.IsServer),
 931467            DangerousDeflateOptions = options.HasFlag(Options.EnableCompression) ? new WebSocketDeflateOptions() : null
 931468        });
 69
 931470        TestWebSocket(webSocket, stream, _receiveBuffer.AsMemory(0, receiveBufferLength), clientOperations).GetAwaiter()
 1866371    }
 72
 73    private async Task TestWebSocket(WebSocket webSocket, TrickleStream stream, Memory<byte> receiveBuffer, ReadOnlyMemo
 931474    {
 931475        using CancellationTokenSource cts = new();
 931476        cts.CancelAfter(TimeSpan.FromSeconds(30));
 77
 78        try
 931479        {
 6057880            while (true) // We should always exit from reading EOF
 6057881            {
 6057882                ValueWebSocketReceiveResult result = await webSocket.ReceiveAsync(ReadBool() ? Memory<byte>.Empty : rece
 83
 5578884                if (ReadBool())
 451085                {
 451086                    await webSocket.CloseAsync(WebSocketCloseStatus.PolicyViolation, ReadBool() ? "Close description" : 
 3187                }
 88
 5130989                if (ReadBool())
 9990                {
 9991                    await webSocket.CloseOutputAsync(WebSocketCloseStatus.PolicyViolation, ReadBool() ? "Close output de
 8292                }
 93
 5129294                if (ReadBool())
 296995                {
 296996                    await webSocket.SendAsync(
 296997                        buffer: receiveBuffer.Slice(0, Math.Min(receiveBuffer.Length, ReadByte())),
 296998                        messageType: ReadBool() ? WebSocketMessageType.Text : WebSocketMessageType.Binary,
 296999                        endOfMessage: ReadBool(),
 2969100                        cancellationToken: GetCancellationToken());
 2941101                }
 102
 51264103                if (ReadBool())
 26104                {
 26105                    webSocket.Dispose();
 26106                }
 51264107            }
 108        }
 9314109        catch (WebSocketException)
 9314110        {
 111            // Expected
 9314112        }
 113
 7578114        CancellationToken GetCancellationToken() => ReadBool() ? cts.Token : CancellationToken.None;
 115
 288356116        bool ReadBool() => ReadByte() != 0;
 117
 118        byte ReadByte()
 291325119        {
 522966120            if (clientOperations.IsEmpty) return 0;
 59684121            byte value = clientOperations.Span[0];
 59684122            clientOperations = clientOperations.Slice(1);
 59684123            return value;
 291325124        }
 9314125    }
 126
 127    private bool TryCreateSegments(ReadOnlySpan<byte> bytes, out Memory<ReadOnlyMemory<byte>> segments)
 9335128    {
 9335129        bytes.CopyTo(_inputBuffer);
 9335130        ReadOnlyMemory<byte> buffer = _inputBuffer.AsMemory(0, bytes.Length);
 131
 9335132        int segmentCount = 0;
 528010133        while (!buffer.IsEmpty)
 518696134        {
 518696135            int length = buffer.Span[0];
 518696136            buffer = buffer.Slice(1);
 137
 518696138            if (length > buffer.Length)
 21139            {
 21140                segments = default;
 21141                return false;
 142            }
 143
 518675144            _segments[segmentCount++] = buffer.Slice(0, length);
 518675145            buffer = buffer.Slice(length);
 518675146        }
 147
 9314148        segments = _segments.AsMemory(0, segmentCount);
 9314149        return true;
 9335150    }
 151
 152    // Allows controlling how the read data is chunked between Read calls.
 153    // All writes are ignored.
 154    private sealed class TrickleStream : Stream
 155    {
 156        private Memory<ReadOnlyMemory<byte>> _segments;
 157
 9314158        public TrickleStream(Memory<ReadOnlyMemory<byte>> segments)
 9314159        {
 9314160            _segments = segments;
 9314161        }
 162
 18628163        public override bool CanRead => true;
 18628164        public override bool CanWrite => true;
 0165        public override bool CanSeek => false;
 166
 0167        public override int Read(byte[] buffer, int offset, int count) => throw new NotImplementedException("Expected on
 168
 169        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =
 0170            ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
 171
 172        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
 32999173        {
 32999174            cancellationToken.ThrowIfCancellationRequested();
 175
 32999176            if (buffer.IsEmpty || _segments.IsEmpty)
 8020177            {
 8020178                return new ValueTask<int>(0);
 179            }
 180
 24979181            ReadOnlyMemory<byte> segment = _segments.Span[0];
 24979182            int toCopy = Math.Min(buffer.Length, segment.Length);
 183
 24979184            segment.Span.Slice(0, toCopy).CopyTo(buffer.Span);
 185
 24979186            if (toCopy == segment.Length)
 16918187            {
 16918188                _segments = _segments.Slice(1);
 16918189            }
 190            else
 8061191            {
 8061192                _segments.Span[0] = segment.Slice(toCopy);
 8061193            }
 194
 24979195            return new ValueTask<int>(toCopy);
 32999196        }
 197
 36554198        public override void Flush() { }
 0199        public override void Write(byte[] buffer, int offset, int count) { }
 0200        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => Ta
 18277201        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
 202
 0203        public override long Length => throw new NotImplementedException();
 0204        public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedExcep
 0205        public override void SetLength(long value) => throw new NotImplementedException();
 0206        public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException();
 207    }
 208}