| | | 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 | | |
| | | 4 | | using System.Buffers; |
| | | 5 | | using System.Runtime.InteropServices; |
| | | 6 | | |
| | | 7 | | namespace DotnetFuzzing.Fuzzers; |
| | | 8 | | |
| | | 9 | | internal sealed class SearchValuesStringFuzzer : IFuzzer |
| | | 10 | | { |
| | 0 | 11 | | public string[] TargetAssemblies => []; |
| | 0 | 12 | | public string[] TargetCoreLibPrefixes { get; } = ["System.Buffers", "System.Globalization"]; |
| | | 13 | | |
| | | 14 | | public void FuzzTarget(ReadOnlySpan<byte> bytes) |
| | 0 | 15 | | { |
| | 0 | 16 | | ReadOnlySpan<char> chars = MemoryMarshal.Cast<byte, char>(bytes); |
| | | 17 | | |
| | 0 | 18 | | int newLine = chars.IndexOf('\n'); |
| | 0 | 19 | | if (newLine < 0) |
| | 0 | 20 | | { |
| | 0 | 21 | | return; |
| | | 22 | | } |
| | | 23 | | |
| | 0 | 24 | | ReadOnlySpan<char> haystack = chars.Slice(newLine + 1); |
| | 0 | 25 | | string[] needles = chars.Slice(0, newLine).ToString().Split(','); |
| | | 26 | | |
| | 0 | 27 | | using var haystack0 = PooledBoundedMemory<char>.Rent(haystack, PoisonPagePlacement.Before); |
| | 0 | 28 | | using var haystack1 = PooledBoundedMemory<char>.Rent(haystack, PoisonPagePlacement.After); |
| | | 29 | | |
| | 0 | 30 | | Test(haystack0.Span, haystack1.Span, needles, StringComparison.Ordinal); |
| | 0 | 31 | | Test(haystack0.Span, haystack1.Span, needles, StringComparison.OrdinalIgnoreCase); |
| | 0 | 32 | | } |
| | | 33 | | |
| | | 34 | | private static void Test(ReadOnlySpan<char> haystack, ReadOnlySpan<char> haystackCopy, string[] needles, StringCompa |
| | 0 | 35 | | { |
| | 0 | 36 | | SearchValues<string> searchValues = SearchValues.Create(needles, comparisonType); |
| | | 37 | | |
| | 0 | 38 | | int index = haystack.IndexOfAny(searchValues); |
| | 0 | 39 | | AssertEqual(index, haystackCopy.IndexOfAny(searchValues), searchValues); |
| | 0 | 40 | | AssertEqual(index, IndexOfAnyReferenceImpl(haystack, needles, comparisonType), searchValues); |
| | 0 | 41 | | } |
| | | 42 | | |
| | | 43 | | private static int IndexOfAnyReferenceImpl(ReadOnlySpan<char> haystack, string[] needles, StringComparison compariso |
| | 0 | 44 | | { |
| | 0 | 45 | | int minIndex = int.MaxValue; |
| | | 46 | | |
| | 0 | 47 | | foreach (string needle in needles) |
| | 0 | 48 | | { |
| | 0 | 49 | | int i = haystack.IndexOf(needle, comparisonType); |
| | 0 | 50 | | if ((uint)i < minIndex) |
| | 0 | 51 | | { |
| | 0 | 52 | | minIndex = i; |
| | 0 | 53 | | } |
| | 0 | 54 | | } |
| | | 55 | | |
| | 0 | 56 | | return minIndex == int.MaxValue ? -1 : minIndex; |
| | 0 | 57 | | } |
| | | 58 | | |
| | | 59 | | private static void AssertEqual(int expected, int actual, SearchValues<string> searchValues) |
| | 0 | 60 | | { |
| | 0 | 61 | | if (expected != actual) |
| | 0 | 62 | | { |
| | 0 | 63 | | Type implType = searchValues.GetType(); |
| | 0 | 64 | | string impl = $"{implType.Name} [{string.Join(", ", implType.GenericTypeArguments.Select(t => t.Name))}]"; |
| | | 65 | | |
| | 0 | 66 | | throw new Exception($"Expected {expected}, got {actual} for impl='{impl}'"); |
| | | 67 | | } |
| | 0 | 68 | | } |
| | | 69 | | } |