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 <file>`), 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 <noreply@anthropic.com>
This commit is contained in:
Bruno Postle
2026-07-21 23:34:06 +01:00
parent 75cc63a669
commit f47e332e2a
2 changed files with 34 additions and 0 deletions
+8
View File
@@ -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 <file>`), 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
+26
View File
@@ -23,11 +23,37 @@
// observing IfcConvert's exit code.
#include "ifcparse/file.h"
#include "ifcparse/logger.h"
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <limits>
#include <sstream>
#include <sys/stat.h>
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/<hash>/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<size_t>(std::numeric_limits<int>::max())) {