mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-20 12:12:15 +00:00
8739f6c13e
A coverage-guided libFuzzer harness (src/ifcfuzz/ifcparse_fuzzer.cpp) that constructs IfcFile directly from in-memory input and calls toString() on every parsed instance to force full lazy attribute evaluation, rather than only observing IfcConvert's exit code from a fuzzed subprocess. Gated behind a new BUILD_FUZZERS option (OFF by default) so it has no effect on existing builds; enabling it requires a Clang toolchain built with -fsanitize=fuzzer. -fsanitize=fuzzer itself stays scoped to the one new target rather than going into the global compiler flags, since it supplies its own main() and would otherwise break every other target including CMake's own compiler checks. Already found and fixed three real bugs this way: two null-pointer dereferences (in header parsing and reference resolution) and a leak of IfcSpfLexer on early return/exception during file scanning. See src/ifcfuzz/README.md for build and usage instructions.
63 lines
3.0 KiB
C++
63 lines
3.0 KiB
C++
/********************************************************************************
|
|
* *
|
|
* This file is part of IfcOpenShell. *
|
|
* *
|
|
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
|
* it under the terms of the Lesser GNU General Public License as published by *
|
|
* the Free Software Foundation, either version 3.0 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* IfcOpenShell is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* Lesser GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the Lesser GNU General Public License *
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
|
* *
|
|
********************************************************************************/
|
|
|
|
// libFuzzer entry point for IfcParse::IfcFile. Parses the input entirely
|
|
// in-memory (no subprocess, no temp files) so a coverage-guided fuzzer can
|
|
// reach the tokenizer and argument parser directly instead of only ever
|
|
// observing IfcConvert's exit code.
|
|
|
|
#include "ifcparse/IfcFile.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <sstream>
|
|
|
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
|
if (size == 0 || size > static_cast<size_t>(std::numeric_limits<int>::max())) {
|
|
return 0;
|
|
}
|
|
|
|
try {
|
|
IfcParse::IfcFile file(const_cast<void*>(static_cast<const void*>(data)), static_cast<int>(size));
|
|
|
|
if (file.good()) {
|
|
// IfcOpenShell parses lazily: merely constructing IfcFile only
|
|
// tokenizes the header and indexes instance byte offsets.
|
|
// toString() forces every attribute of every instance to be
|
|
// fully parsed, which is where most tokenizer/argument bugs
|
|
// would actually be reachable.
|
|
std::ostringstream discard;
|
|
for (const auto& entity : file) {
|
|
try {
|
|
entity.second->toString(discard);
|
|
} catch (const std::exception&) {
|
|
// Malformed attributes are expected on fuzzed input.
|
|
}
|
|
}
|
|
}
|
|
} catch (const std::exception&) {
|
|
// IfcException (and friends) is expected control flow for malformed
|
|
// input, not a bug. Only crashes caught by ASan/UBSan/libFuzzer
|
|
// itself - which bypass try/catch - are findings.
|
|
}
|
|
|
|
return 0;
|
|
}
|