/// <summary> /// Run method starts the libFuzzer runner. It repeatedly executes /// the passed action and reports the execution result to libFuzzer. /// If the executable that is calling it is not running under libFuzzer, /// the action will be executed normally, and will receive its input /// from the file specified in the first command line parameter. /// </summary> /// <param name="action"> /// Some action that calls the instrumented library. The span argument /// passed to the action contains the input data. If an uncaught /// exception escapes the call, crash is reported to libFuzzer. /// </param> public static unsafe void Run(ReadOnlySpanAction action) { ThrowIfNull(action, nameof(action)); try { using (var ipc = new FuzzerIpc()) { var sharedMem = ipc.InputPointer(); var trace = new TraceWrapper(sharedMem); ipc.SetStatus(0); try { var status = Fault.None; // The program instrumented with libFuzzer will exit // after the first error, so we should do the same. while (status != Fault.Crash) { trace.ResetPrevLocation(); var size = ipc.InputSize(); var data = new ReadOnlySpan <byte>(sharedMem + MapSize, size); try { action(data); } catch (Exception ex) { Console.Error.WriteLine(ex); status = Fault.Crash; } ipc.SetStatus(status); } } catch { // Error communicating with the parent process, most likely // because it was terminated after the timeout expired, or // it was killed by the user. In any case, the exception // details don't matter here, so we can just exit silently. return; } } } catch (FuzzerIpcEnvironmentException) { // Error establishing IPC with the parent process due to missing or // definitely-invalid environment variables. This may be intentional. // Instead of persistent fuzzing, fall back on testing a single input. RunWithoutLibFuzzer(action); return; } }
/// <summary> /// Run method starts the libFuzzer runner. It repeatedly executes /// the passed action and reports the execution result to libFuzzer. /// If the executable that is calling it is not running under libFuzzer, /// the action will be executed normally, and will receive its input /// from the file specified in the first command line parameter. /// </summary> /// <param name="action"> /// Some action that calls the instrumented library. The span argument /// passed to the action contains the input data. If an uncaught /// exception escapes the call, crash is reported to libFuzzer. /// </param> public static unsafe void Run(ReadOnlySpanAction action) { ThrowIfNull(action, nameof(action)); var s = Environment.GetEnvironmentVariable("__LIBFUZZER_SHM_ID"); if (s is null || !Int32.TryParse(s, out var shmid)) { RunWithoutLibFuzzer(action); return; } using (var shmaddr = Native.shmat(shmid, IntPtr.Zero, 0)) using (var r = new BinaryReader(new AnonymousPipeClientStream(PipeDirection.In, "198"))) using (var w = new BinaryWriter(new AnonymousPipeClientStream(PipeDirection.Out, "199"))) { var sharedMem = (byte *)shmaddr.DangerousGetHandle(); var trace = new TraceWrapper(sharedMem); w.Write(0); try { var status = Fault.None; // The program instrumented with libFuzzer will exit // after the first error, so we should do the same. while (status != Fault.Crash) { trace.ResetPrevLocation(); var size = r.ReadInt32(); var data = new ReadOnlySpan <byte>(sharedMem + MapSize, size); try { action(data); } catch (Exception ex) { Console.Error.WriteLine(ex); status = Fault.Crash; } w.Write(status); } } catch { // Error communicating with the parent process, most likely // because it was terminated after the timeout expired, or // it was killed by the user. In any case, the exception // details don't matter here, so we can just exit silently. return; } } }
/// <summary> /// Run method starts the .NET equivalent of AFL fork server. /// It repeatedly executes the passed action and reports the /// execution result to afl-fuzz. If the executable that is /// calling it is not running under afl-fuzz, the action will /// be executed only once. /// </summary> /// <param name="action"> /// Some action that calls the instrumented library. The stream /// argument passed to the action contains the input data. If an /// uncaught exception escapes the call, FAULT_CRASH execution /// status code is reported to afl-fuzz. /// </param> public static unsafe void Run(Action <Stream> action) { ThrowIfNull(action, nameof(action)); var s = Environment.GetEnvironmentVariable("__AFL_SHM_ID"); using (var stdin = Console.OpenStandardInput()) using (var stream = new UnclosableStreamWrapper(stdin)) { if (s is null || !Int32.TryParse(s, out var shmid)) { RunWithoutAflFuzz(action, stream); return; } using (var shmaddr = Native.shmat(shmid, IntPtr.Zero, 0)) using (var r = new BinaryReader(new AnonymousPipeClientStream(PipeDirection.In, "198"))) using (var w = new BinaryWriter(new AnonymousPipeClientStream(PipeDirection.Out, "199"))) { var sharedMem = (byte *)shmaddr.DangerousGetHandle(); var trace = new TraceWrapper(sharedMem); w.Write(0); var pid = Process.GetCurrentProcess().Id; using (var memory = new UnclosableStreamWrapper(new MemoryStream())) { // In the first run, we have to consume the input stream twice: // first time to run the Setup function, second time to actually // report the results. That's why we have to use the MemoryStream // in order to be able to seek back to the beginning of the input. stream.CopyTo(memory); memory.Seek(0, SeekOrigin.Begin); Setup(action, memory, sharedMem); memory.Seek(0, SeekOrigin.Begin); r.ReadInt32(); w.Write(pid); trace.ResetPrevLocation(); w.Write(Execute(action, memory)); } while (true) { r.ReadInt32(); w.Write(pid); trace.ResetPrevLocation(); w.Write(Execute(action, stream)); } } } }
/// <summary> /// Run method starts the .NET equivalent of SFZ fuzzer main loop. /// </summary> /// <param name="action"> /// Some action that calls the instrumented library. The stream /// argument passed to the action contains the serialized input data. /// </param> public static unsafe void RunSfz(string[] args, Action <byte[]> action) { Console.WriteLine("Starting Sfz Main"); int[] fileDescriptors = ReadFileDescriptors(args); int fdIn = fileDescriptors[0]; int fdOut = fileDescriptors[1]; Console.Write("GO_FUZZ_IN_FD: " + fdIn); Console.WriteLine(" GO_FUZZ_OUT_FD: " + fdOut); string[] commName = ReadSharedMemoryNames(args); using (SafeFileHandle inPipeHandle = new SafeFileHandle(new IntPtr(fdIn), true), outPipeHandle = new SafeFileHandle(new IntPtr(fdOut), true)) { // Use the filehandles Stream inStream = new FileStream(inPipeHandle, FileAccess.Read); Stream outStream = new FileStream(outPipeHandle, FileAccess.Write); Console.WriteLine("created inStream and outStream"); //MemoryMappedFileSecurity security = new MemoryMappedFileSecurity(); //security.AddAccessRule(new AccessRule<MemoryMappedFileRights>(("Everyone"), // MemoryMappedFileRights.FullControl, AccessControlType.Allow)); //Console.WriteLine("created security"); using ( MemoryMappedFile comm0 = MemoryMappedFile.OpenExisting(commName[0], MemoryMappedFileRights.ReadWrite, HandleInheritability.Inheritable), comm1 = MemoryMappedFile.OpenExisting(commName[1], MemoryMappedFileRights.ReadWrite, HandleInheritability.Inheritable), comm2 = MemoryMappedFile.OpenExisting(commName[2], MemoryMappedFileRights.ReadWrite, HandleInheritability.Inheritable), comm3 = MemoryMappedFile.OpenExisting(commName[3], MemoryMappedFileRights.ReadWrite, HandleInheritability.Inheritable)) { const int MaxInputSize = 1 << 24; const int ReturnResultSize = 1 << 25; const int CoverSize = 64 << 10; const int SonarRegionSize = 1 << 20; var cover = new byte[CoverSize]; fixed(byte *coverPtr = cover) { var trace = new TraceWrapper(coverPtr); Console.WriteLine("created commX objects"); var comm0Accessor = comm0.CreateViewAccessor(0, MaxInputSize); var comm1Accessor = comm1.CreateViewAccessor(0, ReturnResultSize); var comm2Accessor = comm2.CreateViewAccessor(0, CoverSize); var comm3Accessor = comm3.CreateViewAccessor(0, SonarRegionSize); Console.WriteLine("created commX accessors"); while (true) { int fnidx = 0; long inputLength = 0; (fnidx, inputLength) = ReadIndexAndLength(inStream); // read inputBuffer data from comm0 var inputBuffer = new byte[inputLength]; comm0Accessor.ReadArray(0, inputBuffer, 0, (int)inputLength); for (int i = 0; i < inputLength; i++) { inputBuffer[i] = comm0Accessor.ReadByte(i); } var inputString = Encoding.UTF8.GetString(inputBuffer); Console.WriteLine("downstream: " + Encoding.UTF8.GetString(inputBuffer)); var downstream = new Downstream(); try { downstream = JsonSerializer.Deserialize <Downstream>(inputString); } catch (Exception ex) { Console.WriteLine("downstream deserialization exception: " + ex.Message); } //var downstream = FlatBufferSerializer.Default.Parse<Downstream>(inputBuffer); Console.WriteLine("downstream deserialized"); Upstream upstream = NewUpstreamObj(); long res = 0; long ns = 0; long sonar = 0; var seed = downstream.Seed; if (seed != null && seed.Data != null && seed.Data.Length >= 1) { ConfigEntry entry = seed.Data[0]; var value = entry.Value; if (entry.Value != null) { Console.WriteLine("got entry value: " + value); // Start the clock var nsStart = DateTime.UtcNow.Ticks; try { // Actually run the function to fuzz byte[] buffer = Encoding.UTF8.GetBytes(value); action(buffer); Console.Write("exec fuzz method"); } catch (Exception exception) { ns = DateTime.UtcNow.Ticks - nsStart; Console.WriteLine("ns: " + ns); upstream.Crashed = true; upstream.HasFailed = true; upstream.ResultMessage = exception.ToString(); } } else { Console.WriteLine("null entry value!"); var nsStart = DateTime.UtcNow.Ticks; try { // Actually run the function to fuzz byte[] buffer = Encoding.UTF8.GetBytes("<html><body><h1>h1</h1><p>p</p></body></html>"); action(buffer); Console.Write("exec fuzz method"); } catch (Exception exception) { ns = DateTime.UtcNow.Ticks - nsStart; Console.WriteLine("ns: " + ns); upstream.Crashed = true; upstream.HasFailed = true; upstream.ResultMessage = exception.ToString(); } } } else { Console.WriteLine("zero entries!"); var nsStart = DateTime.UtcNow.Ticks; try { // Actually run the function to fuzz byte[] buffer = Encoding.UTF8.GetBytes("<html><body><h1>h1</h1><p>p</p></body></html>"); action(buffer); Console.Write("exec fuzz method"); } catch (Exception exception) { ns = DateTime.UtcNow.Ticks - nsStart; Console.WriteLine("ns: " + ns); upstream.Crashed = true; upstream.HasFailed = true; upstream.ResultMessage = exception.ToString(); } } for (int i = 0; i < cover.Length; i++) { if (cover[i] != 0) { Console.WriteLine("nonzero cover!"); } } // copy cover to shared memory for (int i = 0; i < cover.Length; i++) { comm2Accessor.Write(i, cover[i]); } comm2Accessor.Flush(); ReturnResult(comm1Accessor, outStream, res, ns, sonar, upstream); } } } } }