// Licensed to the Apache Software Foundation(ASF) under one // or more contributor license agreements.See the NOTICE file // distributed with this work for additional information // regarding copyright ownership.The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. using System; using System.IO; using SharpFuzz; using Thrift.Protocol; using Thrift.Transport; using Thrift.Transport.Client; namespace Thrift.Tests.Protocols.Fuzzers { /// /// Base class for protocol fuzzers that handles the common fuzzing logic. /// /// The type of protocol to use for deserialization. public abstract class ProtocolFuzzerBase where FuzzProtocol : TProtocol { /// /// Environment variable that controls whether to use in-process fuzzing for AFL. /// When set to "1", uses Fuzzer.Run instead of Fuzzer.OutOfProcess.Run. /// protected const string UseInProcessFuzzingEnvVar = "THRIFT_AFL_IN_PROCESS"; /// /// 10MB message size limit to prevent over-allocation during fuzzing /// protected const int FUZZ_MAX_MESSAGE_SIZE = 10 * 1024 * 1024; /// /// Creates a new instance of the protocol for the given transport. /// protected abstract FuzzProtocol CreateProtocol(TTransport transport); /// /// Helper method that contains the core fuzzing logic. /// private void ProcessFuzzStream(Stream stream) { try { var config = new TConfiguration(); config.MaxMessageSize = FUZZ_MAX_MESSAGE_SIZE; var transport = new TStreamTransport(stream, null, config); var protocol = CreateProtocol(transport); var obj = new FuzzTest(); obj.ReadAsync(protocol, default).GetAwaiter().GetResult(); } // Narrow catch list: these are the exception families we expect from the // protocol/transport layer on malformed fuzzer input. Do NOT widen to // catch (Exception) — a broad catch would swallow legitimate bugs and // defeat the purpose of fuzzing. catch (TProtocolException) { /* Expected for malformed fuzzer input */ } catch (TTransportException) { /* Expected for malformed fuzzer input */ } catch (TException) { /* Expected for malformed fuzzer input */ } catch (EndOfStreamException) { /* Expected for malformed fuzzer input */ } catch (IOException) { /* Expected for malformed fuzzer input */ } } /// /// The core fuzzing logic that processes a single input. /// protected void ProcessFuzzInput(ReadOnlySpan span) { using var stream = new MemoryStream(span.ToArray()); ProcessFuzzStream(stream); } /// /// Runs the fuzzer with LibFuzzer. /// protected void RunLibFuzzer() { Fuzzer.LibFuzzer.Run(ProcessFuzzInput); } /// /// Runs the fuzzer with AFL. /// protected void RunAFL() { var useInProcess = Environment.GetEnvironmentVariable(UseInProcessFuzzingEnvVar) == "1"; if (useInProcess) { Fuzzer.Run(ProcessFuzzStream); } else { Fuzzer.OutOfProcess.Run(ProcessFuzzStream); } } } }