From f47e332e2ad2a1a69cd0895d2c2419c346cfefa0 Mon Sep 17 00:00:00 2001 From: Bruno Postle Date: Tue, 21 Jul 2026 23:34:06 +0100 Subject: [PATCH] fuzz: wire Logger output for single-input repro runs Logger::SetOutput was never called by the harness, so every parse warning/error (e.g. "Overwriting instance with name #N") was silently discarded. This directly cost time root-causing a leak: grepping stdout for an expected warning found nothing, looking like it ruled out a hypothesis that was actually correct, because the message was just never printed anywhere. Only enable it when the binary is given an explicit file argument (single-input repro, e.g. `-runs=1 `), not during a real campaign against a corpus directory, where logging on every execution would dominate the runtime. Verified with -fsyntax-only against this branch's headers (a real CMake build of ifcviewer-wgpu wasn't attempted, same as the harness's prior namespace-rename commit). Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Sonnet 5 --- src/ifcfuzz/README.md | 8 ++++++++ src/ifcfuzz/ifcparse_fuzzer.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/ifcfuzz/README.md b/src/ifcfuzz/README.md index a6bbe04aac..3ed11e3df7 100644 --- a/src/ifcfuzz/README.md +++ b/src/ifcfuzz/README.md @@ -56,6 +56,14 @@ invalid `.ifc` files works as a starting corpus; a dictionary of STEP/IFC tokens (`ISO-10303-21`, `HEADER`, common `IFCxxx` entity names, etc.) passed via `-dict=` measurably helps the mutator get past the header boilerplate. +### Log output + +`Logger` output is only wired up when the binary is run against an explicit +file argument (e.g. `-runs=1 `), not during a real campaign against a +corpus directory - logging every parse warning on every execution of a +fuzzing campaign would dominate the runtime. Repro runs print +`[Warning]`/`[Error]` messages to stderr. + ### Leak detection `run.sh` sets `ASAN_OPTIONS=detect_leaks=0` by default. A leak that used to diff --git a/src/ifcfuzz/ifcparse_fuzzer.cpp b/src/ifcfuzz/ifcparse_fuzzer.cpp index 2b4468e5b8..01b175234a 100644 --- a/src/ifcfuzz/ifcparse_fuzzer.cpp +++ b/src/ifcfuzz/ifcparse_fuzzer.cpp @@ -23,11 +23,37 @@ // observing IfcConvert's exit code. #include "ifcparse/file.h" +#include "ifcparse/logger.h" #include #include +#include #include #include +#include + +namespace { +bool is_regular_file(const char* path) { + struct stat st; + return ::stat(path, &st) == 0 && S_ISREG(st.st_mode); +} +} // namespace + +// libFuzzer runs in two modes: a real fuzzing campaign (given corpus +// directories to mutate from, executed millions of times) and a +// single-input repro (given one or more explicit file paths, e.g. +// `-runs=1 crashes//input`). logger::set_output is only wired up for +// the latter -- logging every parse warning to a stream on every execution +// of a real campaign would dominate the runtime. +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + for (int i = 1; i < *argc; ++i) { + if (is_regular_file((*argv)[i])) { + logger::root().set_output(&std::cerr, &std::cerr); + break; + } + } + return 0; +} extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size == 0 || size > static_cast(std::numeric_limits::max())) {