< Summary

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

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor()100%110%
FuzzTarget(...)0%880%
TestWebSocket()0%16160%
TryCreateSegments(...)0%440%
.ctor(...)100%110%
Read(...)100%110%
ReadAsync(...)100%110%
ReadAsync(...)0%660%
Flush()100%110%
Write(...)100%110%
WriteAsync(...)100%110%
WriteAsync(...)100%110%
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
 017    private readonly byte[] _inputBuffer = new byte[4096]; // Default max fuzzer input length
 018    private readonly ReadOnlyMemory<byte>[] _segments = new ReadOnlyMemory<byte>[4096];
 019    private readonly byte[] _receiveBuffer = new byte[ushort.MaxValue + 1];
 020    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)
 031    {
 032        if (bytes.Length < 5)
 033        {
 034            return;
 35        }
 36
 037        Options options = (Options)bytes[0];
 038        bytes = bytes.Slice(1);
 39
 040        int receiveBufferLength = 1 + BinaryPrimitives.ReadUInt16BigEndian(bytes);
 041        bytes = bytes.Slice(2);
 42
 043        int clientOperationsBufferLength = BinaryPrimitives.ReadUInt16BigEndian(bytes);
 044        bytes = bytes.Slice(2);
 45
 046        if (bytes.Length < clientOperationsBufferLength)
 047        {
 048            return;
 49        }
 50
 051        bytes.Slice(0, clientOperationsBufferLength).CopyTo(_clientOperationsBuffer);
 052        bytes = bytes.Slice(clientOperationsBufferLength);
 053        ReadOnlyMemory<byte> clientOperations = _clientOperationsBuffer.AsMemory(0, clientOperationsBufferLength);
 54
 055        if (!TryCreateSegments(bytes, out Memory<ReadOnlyMemory<byte>> segments))
 056        {
 057            return;
 58        }
 59
 060        var stream = new TrickleStream(segments);
 61
 062        using WebSocket webSocket = WebSocket.CreateFromStream(stream, new WebSocketCreationOptions
 063        {
 064            KeepAliveInterval = Timeout.InfiniteTimeSpan,
 065            KeepAliveTimeout = Timeout.InfiniteTimeSpan,
 066            IsServer = options.HasFlag(Options.IsServer),
 067            DangerousDeflateOptions = options.HasFlag(Options.EnableCompression) ? new WebSocketDeflateOptions() : null
 068        });
 69
 070        TestWebSocket(webSocket, stream, _receiveBuffer.AsMemory(0, receiveBufferLength), clientOperations).GetAwaiter()
 071    }
 72
 73    private async Task TestWebSocket(WebSocket webSocket, TrickleStream stream, Memory<byte> receiveBuffer, ReadOnlyMemo
 074    {
 075        using CancellationTokenSource cts = new();
 076        cts.CancelAfter(TimeSpan.FromSeconds(30));
 77
 78        try
 079        {
 080            while (true) // We should always exit from reading EOF
 081            {
 082                ValueWebSocketReceiveResult result = await webSocket.ReceiveAsync(ReadBool() ? Memory<byte>.Empty : rece
 83
 084                if (ReadBool())
 085                {
 086                    await webSocket.CloseAsync(WebSocketCloseStatus.PolicyViolation, ReadBool() ? "Close description" : 
 087                }
 88
 089                if (ReadBool())
 090                {
 091                    await webSocket.CloseOutputAsync(WebSocketCloseStatus.PolicyViolation, ReadBool() ? "Close output de
 092                }
 93
 094                if (ReadBool())
 095                {
 096                    await webSocket.SendAsync(
 097                        buffer: receiveBuffer.Slice(0, Math.Min(receiveBuffer.Length, ReadByte())),
 098                        messageType: ReadBool() ? WebSocketMessageType.Text : WebSocketMessageType.Binary,
 099                        endOfMessage: ReadBool(),
 0100                        cancellationToken: GetCancellationToken());
 0101                }
 102
 0103                if (ReadBool())
 0104                {
 0105                    webSocket.Dispose();
 0106                }
 0107            }
 108        }
 0109        catch (WebSocketException)
 0110        {
 111            // Expected
 0112        }
 113
 0114        CancellationToken GetCancellationToken() => ReadBool() ? cts.Token : CancellationToken.None;
 115
 0116        bool ReadBool() => ReadByte() != 0;
 117
 118        byte ReadByte()
 0119        {
 0120            if (clientOperations.IsEmpty) return 0;
 0121            byte value = clientOperations.Span[0];
 0122            clientOperations = clientOperations.Slice(1);
 0123            return value;
 0124        }
 0125    }
 126
 127    private bool TryCreateSegments(ReadOnlySpan<byte> bytes, out Memory<ReadOnlyMemory<byte>> segments)
 0128    {
 0129        bytes.CopyTo(_inputBuffer);
 0130        ReadOnlyMemory<byte> buffer = _inputBuffer.AsMemory(0, bytes.Length);
 131
 0132        int segmentCount = 0;
 0133        while (!buffer.IsEmpty)
 0134        {
 0135            int length = buffer.Span[0];
 0136            buffer = buffer.Slice(1);
 137
 0138            if (length > buffer.Length)
 0139            {
 0140                segments = default;
 0141                return false;
 142            }
 143
 0144            _segments[segmentCount++] = buffer.Slice(0, length);
 0145            buffer = buffer.Slice(length);
 0146        }
 147
 0148        segments = _segments.AsMemory(0, segmentCount);
 0149        return true;
 0150    }
 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
 0158        public TrickleStream(Memory<ReadOnlyMemory<byte>> segments)
 0159        {
 0160            _segments = segments;
 0161        }
 162
 0163        public override bool CanRead => true;
 0164        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)
 0173        {
 0174            cancellationToken.ThrowIfCancellationRequested();
 175
 0176            if (buffer.IsEmpty || _segments.IsEmpty)
 0177            {
 0178                return new ValueTask<int>(0);
 179            }
 180
 0181            ReadOnlyMemory<byte> segment = _segments.Span[0];
 0182            int toCopy = Math.Min(buffer.Length, segment.Length);
 183
 0184            segment.Span.Slice(0, toCopy).CopyTo(buffer.Span);
 185
 0186            if (toCopy == segment.Length)
 0187            {
 0188                _segments = _segments.Slice(1);
 0189            }
 190            else
 0191            {
 0192                _segments.Span[0] = segment.Slice(toCopy);
 0193            }
 194
 0195            return new ValueTask<int>(toCopy);
 0196        }
 197
 0198        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
 0201        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}