From 18a335e062fba57fb21c3c183634d4fd7c5e5ae4 Mon Sep 17 00:00:00 2001 From: Tyler Yankee Date: Thu, 3 Sep 2026 09:31:14 -0400 Subject: [PATCH 1/2] cmPListParser: Hand JSONState a seekable stream --- Source/cmPlistParser.cxx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/cmPlistParser.cxx b/Source/cmPlistParser.cxx index af0962470c..d02eada329 100644 --- a/Source/cmPlistParser.cxx +++ b/Source/cmPlistParser.cxx @@ -2,6 +2,9 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmPlistParser.h" +#include +#include +#include #include #include @@ -25,10 +28,16 @@ cm::optional cmParsePlist(std::string const& filename) return cm::nullopt; } - Json::Value value; + // Buffer plutil's output into a seekable stream. cmJSONState must be able + // peek at a leading BOM, which the process pipe behind cmUVIStream can't + // handle. cmUVIStream outputStream(chain.OutputStream()); - cmJSONState parseState(outputStream, &value, - cmJSONState::StrictMode::Relaxed); + std::string output{ std::istreambuf_iterator(outputStream), + std::istreambuf_iterator() }; + std::istringstream jsonStream(output); + + Json::Value value; + cmJSONState parseState(jsonStream, &value, cmJSONState::StrictMode::Relaxed); if (!parseState.errors.empty()) { return cm::nullopt; } From afa4e5f749e99233d371346b2649101f4fb8fd82 Mon Sep 17 00:00:00 2001 From: Tyler Yankee Date: Wed, 2 Sep 2026 08:21:05 -0400 Subject: [PATCH 2/2] cmJSONState: Move BOM handling to shared helper Amend commit 397fcd4663 (cmJSONState: Allow input stream as input, 2026-02-06) so that all constructors which feed into `ReadJSONStream` bail on BOMs. Add a test case for dynamic analysis tools. --- Source/cmJSONState.cxx | 5 +++-- Tests/Fuzzing/corpus/json/.gitattributes | 1 + Tests/Fuzzing/corpus/json/bom.json | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Tests/Fuzzing/corpus/json/bom.json diff --git a/Source/cmJSONState.cxx b/Source/cmJSONState.cxx index b729009c08..bdc792df30 100644 --- a/Source/cmJSONState.cxx +++ b/Source/cmJSONState.cxx @@ -26,8 +26,6 @@ cmJSONState::cmJSONState(std::string jsonFile, Json::Value* root, this->AddError(cmStrCat("File not found: ", this->Filename)); return; } - // If there's a BOM, toss it. - cmsys::FStream::ReadBOM(fin); this->ReadJSONStream(fin, root, strictMode); } @@ -127,6 +125,9 @@ void cmJSONState::pop_stack() void cmJSONState::ReadJSONStream(std::istream& jsonIStream, Json::Value* root, StrictMode strictMode) { + // If there's a BOM, toss it. + cmsys::FStream::ReadBOM(jsonIStream); + // Save the entire document. std::streampos inBegin = jsonIStream.tellg(); this->doc = std::string(std::istreambuf_iterator(jsonIStream), diff --git a/Tests/Fuzzing/corpus/json/.gitattributes b/Tests/Fuzzing/corpus/json/.gitattributes index 65c9d8d3d0..4f1e99d427 100644 --- a/Tests/Fuzzing/corpus/json/.gitattributes +++ b/Tests/Fuzzing/corpus/json/.gitattributes @@ -1 +1,2 @@ array.json -text -whitespace +bom.json -text -whitespace diff --git a/Tests/Fuzzing/corpus/json/bom.json b/Tests/Fuzzing/corpus/json/bom.json new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/Tests/Fuzzing/corpus/json/bom.json @@ -0,0 +1 @@ + \ No newline at end of file