From 7d22e0f42c01c54aae7a0fda764e8ab21bfab36f Mon Sep 17 00:00:00 2001 From: Sergio Ricardo Zerbetto Masson Date: Thu, 23 Apr 2026 17:46:58 -0300 Subject: [PATCH 1/6] Added checks for rapidjson bugs --- .github/workflows/ci.yml | 5 +- External/RapidJSON/CMakeLists.txt | 16 ++++--- .../RapidJSON/CMakeRapidJSONDownload.txt.in | 3 +- GLTFSDK.Test/Source/DeserializeTests.cpp | 46 +++++++++++++++++++ 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e16679..683bd24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,4 +35,7 @@ jobs: with: cmake-version: '3.31.0' platforms: "['Linux']" - configurations: "['RelWithDebInfo', 'Debug']" \ No newline at end of file + configurations: "['RelWithDebInfo', 'Debug']" + # Sanitizer tests (Clang + UBSAN on Linux) + sanitizer: + uses: ./.github/workflows/sanitizer.yml \ No newline at end of file diff --git a/External/RapidJSON/CMakeLists.txt b/External/RapidJSON/CMakeLists.txt index 47ee2bd..9760331 100644 --- a/External/RapidJSON/CMakeLists.txt +++ b/External/RapidJSON/CMakeLists.txt @@ -26,9 +26,13 @@ add_subdirectory(${CMAKE_BINARY_DIR}/RapidJSON-src ${CMAKE_BINARY_DIR}/RapidJSON-build EXCLUDE_FROM_ALL) -# Set the RapidJSONConfig.cmake path and make find_package to work in config mode explicitly. -set(RapidJSON_DIR "${CMAKE_BINARY_DIR}/RapidJSON-build" CACHE LOCATION "Specific configuration file location" FORCE) -find_package(RapidJSON REQUIRED CONFIG) - -add_library(RapidJSON INTERFACE IMPORTED GLOBAL) -set_target_properties(RapidJSON PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${RapidJSON_INCLUDE_DIRS}) +# Get include directories directly from the target created by add_subdirectory. +# Previous versions used find_package(RapidJSON REQUIRED CONFIG) here, but newer +# RapidJSON versions generate a config that requires a build-step-generated targets +# file. Since we use BUILD_COMMAND "", we get the include dirs from the target directly. +# Note: newer RapidJSON only sets INSTALL_INTERFACE, so we must add BUILD_INTERFACE +# include dirs explicitly for subdirectory usage. +set(RapidJSON_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/RapidJSON-src/include") +target_include_directories(RapidJSON INTERFACE + $ +) diff --git a/External/RapidJSON/CMakeRapidJSONDownload.txt.in b/External/RapidJSON/CMakeRapidJSONDownload.txt.in index 9d00253..6a938dd 100644 --- a/External/RapidJSON/CMakeRapidJSONDownload.txt.in +++ b/External/RapidJSON/CMakeRapidJSONDownload.txt.in @@ -5,9 +5,10 @@ project(RapidJSON-download NONE) include(ExternalProject) ExternalProject_Add(RapidJSON GIT_REPOSITORY https://github.com/Tencent/rapidjson.git - GIT_TAG 232389d4f1012dddec4ef84861face2d2ba85709 + GIT_TAG 24b5e7a8b27f42fa16b96fc70aade9106cf7102f SOURCE_DIR "${CMAKE_BINARY_DIR}/RapidJSON-src" BINARY_DIR "${CMAKE_BINARY_DIR}/RapidJSON-build" + PATCH_COMMAND "${CMAKE_COMMAND}" -DSOURCE_DIR=${CMAKE_BINARY_DIR}/RapidJSON-src -P "${CMAKE_CURRENT_SOURCE_DIR}/patches/fix-null-allocator-deref.cmake" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/GLTFSDK.Test/Source/DeserializeTests.cpp b/GLTFSDK.Test/Source/DeserializeTests.cpp index f0dfe9b..f316fb3 100644 --- a/GLTFSDK.Test/Source/DeserializeTests.cpp +++ b/GLTFSDK.Test/Source/DeserializeTests.cpp @@ -10,6 +10,32 @@ using namespace glTF::UnitTest; namespace { + // Malformed glTF JSON inputs that trigger schema dependency validation errors. + // These exercise the EndMissingDependentProperties code path in RapidJSON's + // schema validator which previously crashed with a null pointer dereference + // (CWE-476) when GetInvalidSchemaPointer().GetAllocator() was called on a + // pointer with a null allocator. + + // Root-level dependency: "scene" requires "scenes" to be present + const char* c_missingDependentPropertyScenes = R"({ + "asset": {"version": "2.0"}, + "scene": 0 +})"; + + // Accessor-level dependency: "byteOffset" requires "bufferView" to be present + // (same as c_invalidAccessorDependency but kept separate for regression clarity) + const char* c_missingDependentPropertyBufferView = R"({ + "accessors": [ + { + "byteOffset": 0, + "componentType": 5123, + "count": 1, + "type": "SCALAR" + } + ], + "asset": {"version": "2.0"} +})"; + const char* c_validPrimitiveNoIndices = R"({ "meshes": [ { @@ -540,6 +566,26 @@ namespace Microsoft Assert::AreEqual(doc.samplers[1].wrapS, Wrap_MIRRORED_REPEAT, L"Sampler wrapS property was not deserialized correctly"); Assert::AreEqual(doc.samplers[1].wrapT, Wrap_CLAMP_TO_EDGE, L"Sampler wrapT property was not deserialized correctly"); } + + // Regression test for CWE-476: null pointer dereference in RapidJSON + // schema validator's EndMissingDependentProperties(). A missing + // dependent property must throw ValidationException rather than + // crashing with a null pointer dereference. + GLTFSDK_TEST_METHOD(DeserializeTests, DeserializeFail_MissingDependentPropertyScenes) + { + Assert::ExpectException([]() + { + Deserialize(c_missingDependentPropertyScenes); + }); + } + + GLTFSDK_TEST_METHOD(DeserializeTests, DeserializeFail_MissingDependentPropertyBufferView) + { + Assert::ExpectException([]() + { + Deserialize(c_missingDependentPropertyBufferView); + }); + } }; } } From 7b0f4caf67aa6c49e262fb7536c496c0724605bc Mon Sep 17 00:00:00 2001 From: Sergio Ricardo Zerbetto Masson Date: Thu, 23 Apr 2026 17:49:07 -0300 Subject: [PATCH 2/6] Added checks for rapidjson bugs --- .github/workflows/sanitizer.yml | 55 +++++++++++++++ .../patches/fix-null-allocator-deref.cmake | 34 +++++++++ GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp | 69 +++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 .github/workflows/sanitizer.yml create mode 100644 External/RapidJSON/patches/fix-null-allocator-deref.cmake create mode 100644 GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer.yml new file mode 100644 index 0000000..16bdc38 --- /dev/null +++ b/.github/workflows/sanitizer.yml @@ -0,0 +1,55 @@ +name: 'sanitizer tests' + +on: + workflow_call: + +jobs: + ubsan: + name: UBSAN - RapidJSON schema validator + runs-on: ubuntu-latest + steps: + - name: Git Checkout + uses: actions/checkout@v4 + with: + submodules: 'true' + + - name: Install Clang + run: | + sudo apt-get update -qq + sudo apt-get install -y clang + + - name: Download RapidJSON source + run: | + # Extract GIT_TAG from the CMake download template + RAPIDJSON_TAG=$(grep 'GIT_TAG' External/RapidJSON/CMakeRapidJSONDownload.txt.in | head -1 | awk '{print $2}') + echo "RapidJSON tag: $RAPIDJSON_TAG" + + # Clone RapidJSON at the pinned version + git clone --depth 1 https://github.com/Tencent/rapidjson.git /tmp/rapidjson-src + cd /tmp/rapidjson-src + git fetch --depth 1 origin "$RAPIDJSON_TAG" + git checkout "$RAPIDJSON_TAG" + + # Apply the patch if it exists (mirrors the CMake PATCH_COMMAND) + PATCH_SCRIPT="${GITHUB_WORKSPACE}/External/RapidJSON/patches/fix-null-allocator-deref.cmake" + if [ -f "$PATCH_SCRIPT" ]; then + echo "Applying patch via CMake script..." + cmake -DSOURCE_DIR=/tmp/rapidjson-src -P "$PATCH_SCRIPT" + else + echo "No patch script found, testing unpatched code" + fi + + - name: Build UBSAN reproducer + run: | + clang++ -std=c++14 -O1 -g \ + -fsanitize=undefined -fno-sanitize-recover=undefined \ + -fno-omit-frame-pointer \ + -I/tmp/rapidjson-src/include \ + GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp \ + -o ubsan_schema_test + + - name: Run UBSAN reproducer + run: | + echo "Running schema validator UBSAN test..." + ./ubsan_schema_test + echo "UBSAN test completed successfully" diff --git a/External/RapidJSON/patches/fix-null-allocator-deref.cmake b/External/RapidJSON/patches/fix-null-allocator-deref.cmake new file mode 100644 index 0000000..f7cd1ea --- /dev/null +++ b/External/RapidJSON/patches/fix-null-allocator-deref.cmake @@ -0,0 +1,34 @@ +# fix-null-allocator-deref.cmake +# +# Patches RapidJSON schema.h to fix CWE-476: null pointer dereference in +# EndMissingDependentProperties(). Replaces calls to +# GetInvalidSchemaPointer().GetAllocator() (which dereferences a null +# allocator_ pointer) with GetStateAllocator() (which is always initialized). +# +# Usage: cmake -DSOURCE_DIR= -P fix-null-allocator-deref.cmake + +if(NOT DEFINED SOURCE_DIR) + message(FATAL_ERROR "SOURCE_DIR must be defined") +endif() + +set(SCHEMA_FILE "${SOURCE_DIR}/include/rapidjson/schema.h") + +if(NOT EXISTS "${SCHEMA_FILE}") + message(FATAL_ERROR "schema.h not found at ${SCHEMA_FILE}") +endif() + +file(READ "${SCHEMA_FILE}" content) + +string(FIND "${content}" "&GetInvalidSchemaPointer().GetAllocator()" match_pos) +if(match_pos EQUAL -1) + message(STATUS "Patch already applied or pattern not found in schema.h") + return() +endif() + +string(REPLACE + "&GetInvalidSchemaPointer().GetAllocator()" + "&GetStateAllocator()" + content "${content}") + +file(WRITE "${SCHEMA_FILE}" "${content}") +message(STATUS "Patched schema.h: replaced GetInvalidSchemaPointer().GetAllocator() with GetStateAllocator()") diff --git a/GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp b/GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp new file mode 100644 index 0000000..870a740 --- /dev/null +++ b/GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Standalone reproducer for CWE-476: null pointer dereference in RapidJSON +// schema validator's EndMissingDependentProperties(). +// +// This test is compiled with Clang + UBSAN (-fsanitize=undefined) to detect +// undefined behavior that may not manifest as a crash on all platforms. +// +// The vulnerability: schema.h's EndMissingDependentProperties() calls +// GetInvalidSchemaPointer().GetAllocator() which dereferences a null +// allocator_ pointer when the schema pointer hasn't been initialized. +// +// This test creates a schema with a "dependencies" constraint and validates +// a document that violates it, exercising the vulnerable code path. + +#include +#include +#include +#include + +int main() { + // Schema with a "dependencies" constraint: if "scene" is present, + // "scenes" must also be present. + const char* schemaJson = R"({ + "type": "object", + "properties": { + "scene": { "type": "integer" }, + "scenes": { "type": "array" } + }, + "dependencies": { + "scene": ["scenes"] + } + })"; + + // Document that violates the dependency: has "scene" but NOT "scenes". + const char* docJson = R"({ "scene": 0 })"; + + rapidjson::Document schemaDoc; + schemaDoc.Parse(schemaJson); + if (schemaDoc.HasParseError()) { + fprintf(stderr, "Schema parse error\n"); + return 1; + } + + rapidjson::Document doc; + doc.Parse(docJson); + if (doc.HasParseError()) { + fprintf(stderr, "Document parse error\n"); + return 1; + } + + rapidjson::SchemaDocument schema(schemaDoc); + rapidjson::SchemaValidator validator(schema); + + // This triggers EndMissingDependentProperties which, in unpatched code, + // dereferences a null allocator pointer (undefined behavior). + bool valid = doc.Accept(validator); + + if (!valid) { + printf("Validation failed as expected: %s\n", + validator.GetInvalidSchemaKeyword()); + printf("UBSAN test passed: no undefined behavior detected\n"); + return 0; + } + + fprintf(stderr, "ERROR: Validation unexpectedly passed\n"); + return 1; +} From 6cb50034c79b15b71cc08c44db8d5d89267ad7de Mon Sep 17 00:00:00 2001 From: Sergio Ricardo Zerbetto Masson Date: Thu, 23 Apr 2026 18:01:58 -0300 Subject: [PATCH 3/6] Added checks for rapidjson bugs --- .github/workflows/ci.yml | 6 +- .github/workflows/sanitizer.yml | 84 ++++++++++--------- External/RapidJSON/CMakeLists.txt | 16 ++-- .../RapidJSON/CMakeRapidJSONDownload.txt.in | 3 +- .../patches/fix-null-allocator-deref.cmake | 34 -------- GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp | 69 --------------- 6 files changed, 55 insertions(+), 157 deletions(-) delete mode 100644 External/RapidJSON/patches/fix-null-allocator-deref.cmake delete mode 100644 GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 683bd24..352b934 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,8 @@ jobs: cmake-version: '3.31.0' platforms: "['Linux']" configurations: "['RelWithDebInfo', 'Debug']" - # Sanitizer tests (Clang + UBSAN on Linux) + # Sanitizer tests (Clang + ASAN/UBSAN on Linux) sanitizer: - uses: ./.github/workflows/sanitizer.yml \ No newline at end of file + uses: ./.github/workflows/sanitizer.yml + with: + cmake-version: '3.31.0' \ No newline at end of file diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer.yml index 16bdc38..474dbbd 100644 --- a/.github/workflows/sanitizer.yml +++ b/.github/workflows/sanitizer.yml @@ -2,54 +2,58 @@ name: 'sanitizer tests' on: workflow_call: + inputs: + cmake-version: + required: true + type: string jobs: - ubsan: - name: UBSAN - RapidJSON schema validator + build: runs-on: ubuntu-latest steps: - name: Git Checkout uses: actions/checkout@v4 with: - submodules: 'true' + submodules: 'true' + + - name: Setup CMake + uses: jwlawson/actions-setup-cmake@v2.0.2 + with: + cmake-version: ${{ inputs.cmake-version }} - name: Install Clang run: | sudo apt-get update -qq - sudo apt-get install -y clang - - - name: Download RapidJSON source - run: | - # Extract GIT_TAG from the CMake download template - RAPIDJSON_TAG=$(grep 'GIT_TAG' External/RapidJSON/CMakeRapidJSONDownload.txt.in | head -1 | awk '{print $2}') - echo "RapidJSON tag: $RAPIDJSON_TAG" - - # Clone RapidJSON at the pinned version - git clone --depth 1 https://github.com/Tencent/rapidjson.git /tmp/rapidjson-src - cd /tmp/rapidjson-src - git fetch --depth 1 origin "$RAPIDJSON_TAG" - git checkout "$RAPIDJSON_TAG" - - # Apply the patch if it exists (mirrors the CMake PATCH_COMMAND) - PATCH_SCRIPT="${GITHUB_WORKSPACE}/External/RapidJSON/patches/fix-null-allocator-deref.cmake" - if [ -f "$PATCH_SCRIPT" ]; then - echo "Applying patch via CMake script..." - cmake -DSOURCE_DIR=/tmp/rapidjson-src -P "$PATCH_SCRIPT" - else - echo "No patch script found, testing unpatched code" - fi - - - name: Build UBSAN reproducer - run: | - clang++ -std=c++14 -O1 -g \ - -fsanitize=undefined -fno-sanitize-recover=undefined \ - -fno-omit-frame-pointer \ - -I/tmp/rapidjson-src/include \ - GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp \ - -o ubsan_schema_test - - - name: Run UBSAN reproducer - run: | - echo "Running schema validator UBSAN test..." - ./ubsan_schema_test - echo "UBSAN test completed successfully" + sudo apt-get install -y clang lld + + - name: CMake Configure + run: > + cmake -G "Unix Makefiles" + -B ./Built/Int/cmake_Linux_Sanitizer + -D CMAKE_BUILD_TYPE=Debug + -D CMAKE_C_COMPILER=clang + -D CMAKE_CXX_COMPILER=clang++ + -D CMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer" + -D CMAKE_C_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer" + -D CMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined" + -D CMAKE_SHARED_LINKER_FLAGS="-fsanitize=address,undefined" + -D ENABLE_SAMPLES=OFF + . + + - name: CMake Build + run: cmake --build . --target install --config Debug + working-directory: ./Built/Int/cmake_Linux_Sanitizer + + - name: Running Unit Tests + run: ./GLTFSDK.Test --gtest_output=xml:GLTFSDK.Test.Sanitizer.log + working-directory: ./Built/Out/Linux/Debug/GLTFSDK.Test + env: + ASAN_OPTIONS: "detect_leaks=0:halt_on_error=1" + UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1" + + - uses: actions/upload-artifact@v4 + name: "Upload test results" + if: always() + with: + name: GLTFSDK.Test.Sanitizer + path: ${{ github.workspace }}/Built/Out/Linux/Debug/GLTFSDK.Test/GLTFSDK.Test.Sanitizer.log diff --git a/External/RapidJSON/CMakeLists.txt b/External/RapidJSON/CMakeLists.txt index 9760331..47ee2bd 100644 --- a/External/RapidJSON/CMakeLists.txt +++ b/External/RapidJSON/CMakeLists.txt @@ -26,13 +26,9 @@ add_subdirectory(${CMAKE_BINARY_DIR}/RapidJSON-src ${CMAKE_BINARY_DIR}/RapidJSON-build EXCLUDE_FROM_ALL) -# Get include directories directly from the target created by add_subdirectory. -# Previous versions used find_package(RapidJSON REQUIRED CONFIG) here, but newer -# RapidJSON versions generate a config that requires a build-step-generated targets -# file. Since we use BUILD_COMMAND "", we get the include dirs from the target directly. -# Note: newer RapidJSON only sets INSTALL_INTERFACE, so we must add BUILD_INTERFACE -# include dirs explicitly for subdirectory usage. -set(RapidJSON_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/RapidJSON-src/include") -target_include_directories(RapidJSON INTERFACE - $ -) +# Set the RapidJSONConfig.cmake path and make find_package to work in config mode explicitly. +set(RapidJSON_DIR "${CMAKE_BINARY_DIR}/RapidJSON-build" CACHE LOCATION "Specific configuration file location" FORCE) +find_package(RapidJSON REQUIRED CONFIG) + +add_library(RapidJSON INTERFACE IMPORTED GLOBAL) +set_target_properties(RapidJSON PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${RapidJSON_INCLUDE_DIRS}) diff --git a/External/RapidJSON/CMakeRapidJSONDownload.txt.in b/External/RapidJSON/CMakeRapidJSONDownload.txt.in index 6a938dd..9d00253 100644 --- a/External/RapidJSON/CMakeRapidJSONDownload.txt.in +++ b/External/RapidJSON/CMakeRapidJSONDownload.txt.in @@ -5,10 +5,9 @@ project(RapidJSON-download NONE) include(ExternalProject) ExternalProject_Add(RapidJSON GIT_REPOSITORY https://github.com/Tencent/rapidjson.git - GIT_TAG 24b5e7a8b27f42fa16b96fc70aade9106cf7102f + GIT_TAG 232389d4f1012dddec4ef84861face2d2ba85709 SOURCE_DIR "${CMAKE_BINARY_DIR}/RapidJSON-src" BINARY_DIR "${CMAKE_BINARY_DIR}/RapidJSON-build" - PATCH_COMMAND "${CMAKE_COMMAND}" -DSOURCE_DIR=${CMAKE_BINARY_DIR}/RapidJSON-src -P "${CMAKE_CURRENT_SOURCE_DIR}/patches/fix-null-allocator-deref.cmake" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/External/RapidJSON/patches/fix-null-allocator-deref.cmake b/External/RapidJSON/patches/fix-null-allocator-deref.cmake deleted file mode 100644 index f7cd1ea..0000000 --- a/External/RapidJSON/patches/fix-null-allocator-deref.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# fix-null-allocator-deref.cmake -# -# Patches RapidJSON schema.h to fix CWE-476: null pointer dereference in -# EndMissingDependentProperties(). Replaces calls to -# GetInvalidSchemaPointer().GetAllocator() (which dereferences a null -# allocator_ pointer) with GetStateAllocator() (which is always initialized). -# -# Usage: cmake -DSOURCE_DIR= -P fix-null-allocator-deref.cmake - -if(NOT DEFINED SOURCE_DIR) - message(FATAL_ERROR "SOURCE_DIR must be defined") -endif() - -set(SCHEMA_FILE "${SOURCE_DIR}/include/rapidjson/schema.h") - -if(NOT EXISTS "${SCHEMA_FILE}") - message(FATAL_ERROR "schema.h not found at ${SCHEMA_FILE}") -endif() - -file(READ "${SCHEMA_FILE}" content) - -string(FIND "${content}" "&GetInvalidSchemaPointer().GetAllocator()" match_pos) -if(match_pos EQUAL -1) - message(STATUS "Patch already applied or pattern not found in schema.h") - return() -endif() - -string(REPLACE - "&GetInvalidSchemaPointer().GetAllocator()" - "&GetStateAllocator()" - content "${content}") - -file(WRITE "${SCHEMA_FILE}" "${content}") -message(STATUS "Patched schema.h: replaced GetInvalidSchemaPointer().GetAllocator() with GetStateAllocator()") diff --git a/GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp b/GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp deleted file mode 100644 index 870a740..0000000 --- a/GLTFSDK.Test/Sanitizer/UBSanSchemaTest.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// Standalone reproducer for CWE-476: null pointer dereference in RapidJSON -// schema validator's EndMissingDependentProperties(). -// -// This test is compiled with Clang + UBSAN (-fsanitize=undefined) to detect -// undefined behavior that may not manifest as a crash on all platforms. -// -// The vulnerability: schema.h's EndMissingDependentProperties() calls -// GetInvalidSchemaPointer().GetAllocator() which dereferences a null -// allocator_ pointer when the schema pointer hasn't been initialized. -// -// This test creates a schema with a "dependencies" constraint and validates -// a document that violates it, exercising the vulnerable code path. - -#include -#include -#include -#include - -int main() { - // Schema with a "dependencies" constraint: if "scene" is present, - // "scenes" must also be present. - const char* schemaJson = R"({ - "type": "object", - "properties": { - "scene": { "type": "integer" }, - "scenes": { "type": "array" } - }, - "dependencies": { - "scene": ["scenes"] - } - })"; - - // Document that violates the dependency: has "scene" but NOT "scenes". - const char* docJson = R"({ "scene": 0 })"; - - rapidjson::Document schemaDoc; - schemaDoc.Parse(schemaJson); - if (schemaDoc.HasParseError()) { - fprintf(stderr, "Schema parse error\n"); - return 1; - } - - rapidjson::Document doc; - doc.Parse(docJson); - if (doc.HasParseError()) { - fprintf(stderr, "Document parse error\n"); - return 1; - } - - rapidjson::SchemaDocument schema(schemaDoc); - rapidjson::SchemaValidator validator(schema); - - // This triggers EndMissingDependentProperties which, in unpatched code, - // dereferences a null allocator pointer (undefined behavior). - bool valid = doc.Accept(validator); - - if (!valid) { - printf("Validation failed as expected: %s\n", - validator.GetInvalidSchemaKeyword()); - printf("UBSAN test passed: no undefined behavior detected\n"); - return 0; - } - - fprintf(stderr, "ERROR: Validation unexpectedly passed\n"); - return 1; -} From d2018513890318ce5d17057e1e21dc986ab077a4 Mon Sep 17 00:00:00 2001 From: Sergio Ricardo Zerbetto Masson Date: Fri, 24 Apr 2026 14:39:55 -0300 Subject: [PATCH 4/6] Moved project to use vcpkg --- .github/workflows/ios.yml | 13 +++++-- .github/workflows/linux.yml | 10 +++-- .github/workflows/macos.yml | 9 +++-- .github/workflows/sanitizer.yml | 9 ++++- .github/workflows/windows.yml | 10 +++-- Build/vcpkg-triplets/x64-linux-asan.cmake | 15 ++++++++ CMakeLists.txt | 30 +++------------ External/RapidJSON/CMakeLists.txt | 34 ----------------- .../RapidJSON/CMakeRapidJSONDownload.txt.in | 15 -------- .../googletest/CMakeGoogleTestDownload.txt.in | 15 -------- External/googletest/CMakeLists.txt | 37 ------------------- vcpkg.json | 22 +++++++++++ 12 files changed, 79 insertions(+), 140 deletions(-) create mode 100644 Build/vcpkg-triplets/x64-linux-asan.cmake delete mode 100644 External/RapidJSON/CMakeLists.txt delete mode 100644 External/RapidJSON/CMakeRapidJSONDownload.txt.in delete mode 100644 External/googletest/CMakeGoogleTestDownload.txt.in delete mode 100644 External/googletest/CMakeLists.txt create mode 100644 vcpkg.json diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 8e6385e..d2aa48b 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -23,14 +23,21 @@ jobs: steps: - name: Git Checkout uses: actions/checkout@v4 - with: - submodules: 'true' - name: Setup CMake uses: jwlawson/actions-setup-cmake@v2.0.2 with: cmake-version: ${{ inputs.cmake-version }} - name: CMake Configure - run: cmake -G Xcode -DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/Build/CMake/ios.toolchain.cmake" -DPLATFORM=OS -DDEPLOYMENT_TARGET="10.11" -DENABLE_UNIT_TESTS="OFF" -B ./Built/Int/cmake_${{ matrix.platform }} . + run: > + cmake -G Xcode + -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake + -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=${{ github.workspace }}/Build/CMake/ios.toolchain.cmake + -DVCPKG_TARGET_TRIPLET=arm64-ios + -DPLATFORM=OS + -DDEPLOYMENT_TARGET="10.11" + -DENABLE_UNIT_TESTS="OFF" + -B ./Built/Int/cmake_${{ matrix.platform }} + . - name: CMake Build run: cmake --build . --target install --config ${{ matrix.config }} working-directory: ./Built/Int/cmake_${{ matrix.platform }} \ No newline at end of file diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 0589dcd..3505ae2 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -23,14 +23,18 @@ jobs: steps: - name: Git Checkout uses: actions/checkout@v4 - with: - submodules: 'true' - name: Setup CMake uses: jwlawson/actions-setup-cmake@v2.0.2 with: cmake-version: ${{ inputs.cmake-version }} - name: CMake Configure - run: cmake -G "Unix Makefiles" -B ./Built/Int/cmake_${{ matrix.platform }} -D CMAKE_BUILD_TYPE=${{ matrix.config }} . + run: > + cmake -G "Unix Makefiles" + -B ./Built/Int/cmake_${{ matrix.platform }} + -D CMAKE_BUILD_TYPE=${{ matrix.config }} + -D CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake + -D VCPKG_MANIFEST_FEATURES="tests" + . - name: CMake Build run: cmake --build . --target install --config ${{ matrix.config }} working-directory: ./Built/Int/cmake_${{ matrix.platform }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index f90529d..8de02a5 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -23,14 +23,17 @@ jobs: steps: - name: Git Checkout uses: actions/checkout@v4 - with: - submodules: 'true' - name: Setup CMake uses: jwlawson/actions-setup-cmake@v2.0.2 with: cmake-version: ${{ inputs.cmake-version }} - name: CMake Configure - run: cmake -G Xcode -B ./Built/Int/cmake_${{ matrix.platform }} . + run: > + cmake -G Xcode + -B ./Built/Int/cmake_${{ matrix.platform }} + -D CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake + -D VCPKG_MANIFEST_FEATURES="tests" + . - name: CMake Build run: cmake --build . --target install --config ${{ matrix.config }} working-directory: ./Built/Int/cmake_${{ matrix.platform }} diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer.yml index 474dbbd..f97ceb8 100644 --- a/.github/workflows/sanitizer.yml +++ b/.github/workflows/sanitizer.yml @@ -13,8 +13,6 @@ jobs: steps: - name: Git Checkout uses: actions/checkout@v4 - with: - submodules: 'true' - name: Setup CMake uses: jwlawson/actions-setup-cmake@v2.0.2 @@ -27,6 +25,9 @@ jobs: sudo apt-get install -y clang lld - name: CMake Configure + env: + CC: clang + CXX: clang++ run: > cmake -G "Unix Makefiles" -B ./Built/Int/cmake_Linux_Sanitizer @@ -38,6 +39,10 @@ jobs: -D CMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined" -D CMAKE_SHARED_LINKER_FLAGS="-fsanitize=address,undefined" -D ENABLE_SAMPLES=OFF + -D CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake + -D VCPKG_TARGET_TRIPLET=x64-linux-asan + -D VCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/Build/vcpkg-triplets + -D VCPKG_MANIFEST_FEATURES="tests" . - name: CMake Build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c7c84ea..d6e1f63 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -23,14 +23,18 @@ jobs: steps: - name: Git Checkout uses: actions/checkout@v4 - with: - submodules: 'true' - name: Setup CMake uses: jwlawson/actions-setup-cmake@v2.0.2 with: cmake-version: ${{ inputs.cmake-version }} - name: CMake Configure - run: cmake -G "Visual Studio 17 2022" -A ${{ matrix.platform }} -B ./Built/Int/cmake_${{ matrix.platform }} . + shell: bash + run: > + cmake -G "Visual Studio 17 2022" -A ${{ matrix.platform }} + -B ./Built/Int/cmake_${{ matrix.platform }} + -D CMAKE_TOOLCHAIN_FILE="$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" + -D VCPKG_MANIFEST_FEATURES="tests" + . - name: CMake Build run: cmake --build . --target install --config ${{ matrix.config }} working-directory: ./Built/Int/cmake_${{ matrix.platform }} diff --git a/Build/vcpkg-triplets/x64-linux-asan.cmake b/Build/vcpkg-triplets/x64-linux-asan.cmake new file mode 100644 index 0000000..13147c9 --- /dev/null +++ b/Build/vcpkg-triplets/x64-linux-asan.cmake @@ -0,0 +1,15 @@ +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) +set(VCPKG_CMAKE_SYSTEM_NAME Linux) + +# Address and undefined-behavior sanitizer flags applied to vcpkg +# dependencies so they are built with the same instrumentation as +# the main project. +set(VCPKG_CXX_FLAGS "-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer") +set(VCPKG_C_FLAGS "-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer") +set(VCPKG_LINKER_FLAGS "-fsanitize=address,undefined") + +# Let CC / CXX env vars propagate so vcpkg uses the same compiler +# (clang) as the main project. +set(VCPKG_ENV_PASSTHROUGH_UNTRACKED CC CXX) diff --git a/CMakeLists.txt b/CMakeLists.txt index a835855..5be693d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,36 +33,16 @@ endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Build/CMake/Modules") -if(NOT IOS) - # This variable is set by ios.toolchain.cmake. - # Notice that External/RapidJSON uses `find_package` for external project is ready. - # We have to prevent modification of RapidJSON_XXX variables in cache at this moment - find_package(RapidJSON CONFIG) -endif() -if(NOT RapidJSON_FOUND) - add_subdirectory(External/RapidJSON) -endif() -find_package(GTest CONFIG) -if(NOT GTest_FOUND) - # import and alias just like it is already installed via VcPkg - add_subdirectory(External/googletest) - add_library(GTest::gtest_main ALIAS gtest_main) -endif() +find_package(RapidJSON CONFIG REQUIRED) add_subdirectory(GLTFSDK) -if(RapidJSON_FOUND) - target_include_directories(GLTFSDK - PUBLIC $ - ) -elseif(TARGET RapidJSON) - get_target_property(RapidJSON_INCLUDE_DIRS RapidJSON INTERFACE_INCLUDE_DIRECTORIES) - target_include_directories(GLTFSDK - PUBLIC $ - ) -endif() +target_include_directories(GLTFSDK + PUBLIC $ +) if(ENABLE_UNIT_TESTS AND (NOT DEFINED EMSCRIPTEN)) + find_package(GTest CONFIG REQUIRED) add_subdirectory(GLTFSDK.TestUtils) add_subdirectory(GLTFSDK.Test) endif() diff --git a/External/RapidJSON/CMakeLists.txt b/External/RapidJSON/CMakeLists.txt deleted file mode 100644 index 47ee2bd..0000000 --- a/External/RapidJSON/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -# Check if the RapidJSON target has already been defined -if (TARGET RapidJSON) - message(AUTHOR_WARNING "RapidJSON target already defined, skipping") - return() -endif() - -# Download and unpack RapidJSON at configure time -configure_file(CMakeRapidJSONDownload.txt.in ${CMAKE_BINARY_DIR}/RapidJSON-download/CMakeLists.txt) -execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/RapidJSON-download ) -if(result) - message(FATAL_ERROR "CMake step for RapidJSON failed: ${result}") -endif() -execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/RapidJSON-download ) -if(result) - message(FATAL_ERROR "Build step for RapidJSON failed: ${result}") -endif() - -SET(RAPIDJSON_BUILD_TESTS OFF CACHE BOOL "Build rapidjson perftests and unittests." FORCE) - -# Add RapidJSON directly to our build. -add_subdirectory(${CMAKE_BINARY_DIR}/RapidJSON-src - ${CMAKE_BINARY_DIR}/RapidJSON-build - EXCLUDE_FROM_ALL) - -# Set the RapidJSONConfig.cmake path and make find_package to work in config mode explicitly. -set(RapidJSON_DIR "${CMAKE_BINARY_DIR}/RapidJSON-build" CACHE LOCATION "Specific configuration file location" FORCE) -find_package(RapidJSON REQUIRED CONFIG) - -add_library(RapidJSON INTERFACE IMPORTED GLOBAL) -set_target_properties(RapidJSON PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${RapidJSON_INCLUDE_DIRS}) diff --git a/External/RapidJSON/CMakeRapidJSONDownload.txt.in b/External/RapidJSON/CMakeRapidJSONDownload.txt.in deleted file mode 100644 index 9d00253..0000000 --- a/External/RapidJSON/CMakeRapidJSONDownload.txt.in +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 2.8.2) - -project(RapidJSON-download NONE) - -include(ExternalProject) -ExternalProject_Add(RapidJSON - GIT_REPOSITORY https://github.com/Tencent/rapidjson.git - GIT_TAG 232389d4f1012dddec4ef84861face2d2ba85709 - SOURCE_DIR "${CMAKE_BINARY_DIR}/RapidJSON-src" - BINARY_DIR "${CMAKE_BINARY_DIR}/RapidJSON-build" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" -) diff --git a/External/googletest/CMakeGoogleTestDownload.txt.in b/External/googletest/CMakeGoogleTestDownload.txt.in deleted file mode 100644 index c55d79e..0000000 --- a/External/googletest/CMakeGoogleTestDownload.txt.in +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 2.8.2) - -project(googletest-download NONE) - -include(ExternalProject) -ExternalProject_Add(googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.17.0 - SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" - BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" -) diff --git a/External/googletest/CMakeLists.txt b/External/googletest/CMakeLists.txt deleted file mode 100644 index 8595c82..0000000 --- a/External/googletest/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -# Check if the gtest target has already been defined -if (TARGET gtest) - message(AUTHOR_WARNING "gtest target already defined, skipping") - return() -endif() - -# Download and unpack googletest at configure time -configure_file(CMakeGoogleTestDownload.txt.in ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt) -execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) -if(result) - message(FATAL_ERROR "CMake step for googletest failed: ${result}") -endif() -execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) -if(result) - message(FATAL_ERROR "Build step for googletest failed: ${result}") -endif() - -# Prevent overriding the parent project's compiler/linker -# settings on Windows -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - -# Add googletest directly to our build. This defines -# the gtest and gtest_main targets. -add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src - ${CMAKE_BINARY_DIR}/googletest-build - EXCLUDE_FROM_ALL) - -# The gtest/gtest_main targets carry header search path -# dependencies automatically when using CMake 2.8.11 or -# later. Otherwise we have to add them here ourselves. -if (CMAKE_VERSION VERSION_LESS 2.8.11) - include_directories("${gtest_SOURCE_DIR}/include") -endif() diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..7fa0d2a --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,22 @@ +{ + "name": "gltf-sdk", + "version-string": "0.0.1", + "builtin-baseline": "c3867e714dd3a51c272826eea77267876517ed99", + "dependencies": [ + "rapidjson" + ], + "features": { + "tests": { + "description": "Build unit tests", + "dependencies": [ + "gtest" + ] + } + }, + "overrides": [ + { + "name": "rapidjson", + "version": "2025-02-26" + } + ] +} From c210c1d87e5a297c78cd12caf5c0e2fe87f2028f Mon Sep 17 00:00:00 2001 From: Sergio Ricardo Zerbetto Masson Date: Fri, 24 Apr 2026 14:47:36 -0300 Subject: [PATCH 5/6] Added custom vcpkg triplets --- .github/workflows/windows.yml | 1 + Build/vcpkg-triplets/arm64-windows.cmake | 3 +++ Build/vcpkg-triplets/x64-windows.cmake | 3 +++ Build/vcpkg-triplets/x86-windows.cmake | 3 +++ 4 files changed, 10 insertions(+) create mode 100644 Build/vcpkg-triplets/arm64-windows.cmake create mode 100644 Build/vcpkg-triplets/x64-windows.cmake create mode 100644 Build/vcpkg-triplets/x86-windows.cmake diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d6e1f63..4931f97 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -33,6 +33,7 @@ jobs: cmake -G "Visual Studio 17 2022" -A ${{ matrix.platform }} -B ./Built/Int/cmake_${{ matrix.platform }} -D CMAKE_TOOLCHAIN_FILE="$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" + -D VCPKG_OVERLAY_TRIPLETS=./Build/vcpkg-triplets -D VCPKG_MANIFEST_FEATURES="tests" . - name: CMake Build diff --git a/Build/vcpkg-triplets/arm64-windows.cmake b/Build/vcpkg-triplets/arm64-windows.cmake new file mode 100644 index 0000000..714b382 --- /dev/null +++ b/Build/vcpkg-triplets/arm64-windows.cmake @@ -0,0 +1,3 @@ +set(VCPKG_TARGET_ARCHITECTURE arm64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) diff --git a/Build/vcpkg-triplets/x64-windows.cmake b/Build/vcpkg-triplets/x64-windows.cmake new file mode 100644 index 0000000..63d6cde --- /dev/null +++ b/Build/vcpkg-triplets/x64-windows.cmake @@ -0,0 +1,3 @@ +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) diff --git a/Build/vcpkg-triplets/x86-windows.cmake b/Build/vcpkg-triplets/x86-windows.cmake new file mode 100644 index 0000000..2496b16 --- /dev/null +++ b/Build/vcpkg-triplets/x86-windows.cmake @@ -0,0 +1,3 @@ +set(VCPKG_TARGET_ARCHITECTURE x86) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) From 93c38298119bd7c892e80d94c38157c7913ae0d1 Mon Sep 17 00:00:00 2001 From: Sergio Ricardo Zerbetto Masson Date: Fri, 1 May 2026 13:54:03 -0300 Subject: [PATCH 6/6] Revert vcpkg migration; fix RapidJSON null allocator deref via patch Restore master's ExternalProject_Add-based dependency management for RapidJSON and googletest. Keep master's pinned RapidJSON tag and apply a small CMake patch script during the ExternalProject_Add download to fix CWE-476: null pointer dereference in GenericSchemaValidator::EndMissingDependentProperties() (replaces &GetInvalidSchemaPointer().GetAllocator() with &GetStateAllocator()). Retains the regression tests and the Linux ASAN/UBSAN sanitizer CI workflow (now without vcpkg). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ios.yml | 13 ++----- .github/workflows/linux.yml | 10 ++--- .github/workflows/macos.yml | 9 ++--- .github/workflows/sanitizer.yml | 4 -- .github/workflows/windows.yml | 11 ++---- Build/vcpkg-triplets/arm64-windows.cmake | 3 -- Build/vcpkg-triplets/x64-linux-asan.cmake | 15 -------- Build/vcpkg-triplets/x64-windows.cmake | 3 -- Build/vcpkg-triplets/x86-windows.cmake | 3 -- CMakeLists.txt | 30 ++++++++++++--- External/RapidJSON/CMakeLists.txt | 34 +++++++++++++++++ .../RapidJSON/CMakeRapidJSONDownload.txt.in | 16 ++++++++ .../patches/fix-null-allocator-deref.cmake | 34 +++++++++++++++++ .../googletest/CMakeGoogleTestDownload.txt.in | 15 ++++++++ External/googletest/CMakeLists.txt | 37 +++++++++++++++++++ vcpkg.json | 22 ----------- 16 files changed, 173 insertions(+), 86 deletions(-) delete mode 100644 Build/vcpkg-triplets/arm64-windows.cmake delete mode 100644 Build/vcpkg-triplets/x64-linux-asan.cmake delete mode 100644 Build/vcpkg-triplets/x64-windows.cmake delete mode 100644 Build/vcpkg-triplets/x86-windows.cmake create mode 100644 External/RapidJSON/CMakeLists.txt create mode 100644 External/RapidJSON/CMakeRapidJSONDownload.txt.in create mode 100644 External/RapidJSON/patches/fix-null-allocator-deref.cmake create mode 100644 External/googletest/CMakeGoogleTestDownload.txt.in create mode 100644 External/googletest/CMakeLists.txt delete mode 100644 vcpkg.json diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index d2aa48b..8e6385e 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -23,21 +23,14 @@ jobs: steps: - name: Git Checkout uses: actions/checkout@v4 + with: + submodules: 'true' - name: Setup CMake uses: jwlawson/actions-setup-cmake@v2.0.2 with: cmake-version: ${{ inputs.cmake-version }} - name: CMake Configure - run: > - cmake -G Xcode - -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake - -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=${{ github.workspace }}/Build/CMake/ios.toolchain.cmake - -DVCPKG_TARGET_TRIPLET=arm64-ios - -DPLATFORM=OS - -DDEPLOYMENT_TARGET="10.11" - -DENABLE_UNIT_TESTS="OFF" - -B ./Built/Int/cmake_${{ matrix.platform }} - . + run: cmake -G Xcode -DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/Build/CMake/ios.toolchain.cmake" -DPLATFORM=OS -DDEPLOYMENT_TARGET="10.11" -DENABLE_UNIT_TESTS="OFF" -B ./Built/Int/cmake_${{ matrix.platform }} . - name: CMake Build run: cmake --build . --target install --config ${{ matrix.config }} working-directory: ./Built/Int/cmake_${{ matrix.platform }} \ No newline at end of file diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 3505ae2..0589dcd 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -23,18 +23,14 @@ jobs: steps: - name: Git Checkout uses: actions/checkout@v4 + with: + submodules: 'true' - name: Setup CMake uses: jwlawson/actions-setup-cmake@v2.0.2 with: cmake-version: ${{ inputs.cmake-version }} - name: CMake Configure - run: > - cmake -G "Unix Makefiles" - -B ./Built/Int/cmake_${{ matrix.platform }} - -D CMAKE_BUILD_TYPE=${{ matrix.config }} - -D CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake - -D VCPKG_MANIFEST_FEATURES="tests" - . + run: cmake -G "Unix Makefiles" -B ./Built/Int/cmake_${{ matrix.platform }} -D CMAKE_BUILD_TYPE=${{ matrix.config }} . - name: CMake Build run: cmake --build . --target install --config ${{ matrix.config }} working-directory: ./Built/Int/cmake_${{ matrix.platform }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 8de02a5..f90529d 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -23,17 +23,14 @@ jobs: steps: - name: Git Checkout uses: actions/checkout@v4 + with: + submodules: 'true' - name: Setup CMake uses: jwlawson/actions-setup-cmake@v2.0.2 with: cmake-version: ${{ inputs.cmake-version }} - name: CMake Configure - run: > - cmake -G Xcode - -B ./Built/Int/cmake_${{ matrix.platform }} - -D CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake - -D VCPKG_MANIFEST_FEATURES="tests" - . + run: cmake -G Xcode -B ./Built/Int/cmake_${{ matrix.platform }} . - name: CMake Build run: cmake --build . --target install --config ${{ matrix.config }} working-directory: ./Built/Int/cmake_${{ matrix.platform }} diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer.yml index f97ceb8..7d08cca 100644 --- a/.github/workflows/sanitizer.yml +++ b/.github/workflows/sanitizer.yml @@ -39,10 +39,6 @@ jobs: -D CMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined" -D CMAKE_SHARED_LINKER_FLAGS="-fsanitize=address,undefined" -D ENABLE_SAMPLES=OFF - -D CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake - -D VCPKG_TARGET_TRIPLET=x64-linux-asan - -D VCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/Build/vcpkg-triplets - -D VCPKG_MANIFEST_FEATURES="tests" . - name: CMake Build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4931f97..c7c84ea 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -23,19 +23,14 @@ jobs: steps: - name: Git Checkout uses: actions/checkout@v4 + with: + submodules: 'true' - name: Setup CMake uses: jwlawson/actions-setup-cmake@v2.0.2 with: cmake-version: ${{ inputs.cmake-version }} - name: CMake Configure - shell: bash - run: > - cmake -G "Visual Studio 17 2022" -A ${{ matrix.platform }} - -B ./Built/Int/cmake_${{ matrix.platform }} - -D CMAKE_TOOLCHAIN_FILE="$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" - -D VCPKG_OVERLAY_TRIPLETS=./Build/vcpkg-triplets - -D VCPKG_MANIFEST_FEATURES="tests" - . + run: cmake -G "Visual Studio 17 2022" -A ${{ matrix.platform }} -B ./Built/Int/cmake_${{ matrix.platform }} . - name: CMake Build run: cmake --build . --target install --config ${{ matrix.config }} working-directory: ./Built/Int/cmake_${{ matrix.platform }} diff --git a/Build/vcpkg-triplets/arm64-windows.cmake b/Build/vcpkg-triplets/arm64-windows.cmake deleted file mode 100644 index 714b382..0000000 --- a/Build/vcpkg-triplets/arm64-windows.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(VCPKG_TARGET_ARCHITECTURE arm64) -set(VCPKG_CRT_LINKAGE dynamic) -set(VCPKG_LIBRARY_LINKAGE static) diff --git a/Build/vcpkg-triplets/x64-linux-asan.cmake b/Build/vcpkg-triplets/x64-linux-asan.cmake deleted file mode 100644 index 13147c9..0000000 --- a/Build/vcpkg-triplets/x64-linux-asan.cmake +++ /dev/null @@ -1,15 +0,0 @@ -set(VCPKG_TARGET_ARCHITECTURE x64) -set(VCPKG_CRT_LINKAGE dynamic) -set(VCPKG_LIBRARY_LINKAGE static) -set(VCPKG_CMAKE_SYSTEM_NAME Linux) - -# Address and undefined-behavior sanitizer flags applied to vcpkg -# dependencies so they are built with the same instrumentation as -# the main project. -set(VCPKG_CXX_FLAGS "-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer") -set(VCPKG_C_FLAGS "-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer") -set(VCPKG_LINKER_FLAGS "-fsanitize=address,undefined") - -# Let CC / CXX env vars propagate so vcpkg uses the same compiler -# (clang) as the main project. -set(VCPKG_ENV_PASSTHROUGH_UNTRACKED CC CXX) diff --git a/Build/vcpkg-triplets/x64-windows.cmake b/Build/vcpkg-triplets/x64-windows.cmake deleted file mode 100644 index 63d6cde..0000000 --- a/Build/vcpkg-triplets/x64-windows.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(VCPKG_TARGET_ARCHITECTURE x64) -set(VCPKG_CRT_LINKAGE dynamic) -set(VCPKG_LIBRARY_LINKAGE static) diff --git a/Build/vcpkg-triplets/x86-windows.cmake b/Build/vcpkg-triplets/x86-windows.cmake deleted file mode 100644 index 2496b16..0000000 --- a/Build/vcpkg-triplets/x86-windows.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(VCPKG_TARGET_ARCHITECTURE x86) -set(VCPKG_CRT_LINKAGE dynamic) -set(VCPKG_LIBRARY_LINKAGE static) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5be693d..a835855 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,16 +33,36 @@ endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Build/CMake/Modules") -find_package(RapidJSON CONFIG REQUIRED) +if(NOT IOS) + # This variable is set by ios.toolchain.cmake. + # Notice that External/RapidJSON uses `find_package` for external project is ready. + # We have to prevent modification of RapidJSON_XXX variables in cache at this moment + find_package(RapidJSON CONFIG) +endif() +if(NOT RapidJSON_FOUND) + add_subdirectory(External/RapidJSON) +endif() +find_package(GTest CONFIG) +if(NOT GTest_FOUND) + # import and alias just like it is already installed via VcPkg + add_subdirectory(External/googletest) + add_library(GTest::gtest_main ALIAS gtest_main) +endif() add_subdirectory(GLTFSDK) -target_include_directories(GLTFSDK - PUBLIC $ -) +if(RapidJSON_FOUND) + target_include_directories(GLTFSDK + PUBLIC $ + ) +elseif(TARGET RapidJSON) + get_target_property(RapidJSON_INCLUDE_DIRS RapidJSON INTERFACE_INCLUDE_DIRECTORIES) + target_include_directories(GLTFSDK + PUBLIC $ + ) +endif() if(ENABLE_UNIT_TESTS AND (NOT DEFINED EMSCRIPTEN)) - find_package(GTest CONFIG REQUIRED) add_subdirectory(GLTFSDK.TestUtils) add_subdirectory(GLTFSDK.Test) endif() diff --git a/External/RapidJSON/CMakeLists.txt b/External/RapidJSON/CMakeLists.txt new file mode 100644 index 0000000..47ee2bd --- /dev/null +++ b/External/RapidJSON/CMakeLists.txt @@ -0,0 +1,34 @@ +# Check if the RapidJSON target has already been defined +if (TARGET RapidJSON) + message(AUTHOR_WARNING "RapidJSON target already defined, skipping") + return() +endif() + +# Download and unpack RapidJSON at configure time +configure_file(CMakeRapidJSONDownload.txt.in ${CMAKE_BINARY_DIR}/RapidJSON-download/CMakeLists.txt) +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/RapidJSON-download ) +if(result) + message(FATAL_ERROR "CMake step for RapidJSON failed: ${result}") +endif() +execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/RapidJSON-download ) +if(result) + message(FATAL_ERROR "Build step for RapidJSON failed: ${result}") +endif() + +SET(RAPIDJSON_BUILD_TESTS OFF CACHE BOOL "Build rapidjson perftests and unittests." FORCE) + +# Add RapidJSON directly to our build. +add_subdirectory(${CMAKE_BINARY_DIR}/RapidJSON-src + ${CMAKE_BINARY_DIR}/RapidJSON-build + EXCLUDE_FROM_ALL) + +# Set the RapidJSONConfig.cmake path and make find_package to work in config mode explicitly. +set(RapidJSON_DIR "${CMAKE_BINARY_DIR}/RapidJSON-build" CACHE LOCATION "Specific configuration file location" FORCE) +find_package(RapidJSON REQUIRED CONFIG) + +add_library(RapidJSON INTERFACE IMPORTED GLOBAL) +set_target_properties(RapidJSON PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${RapidJSON_INCLUDE_DIRS}) diff --git a/External/RapidJSON/CMakeRapidJSONDownload.txt.in b/External/RapidJSON/CMakeRapidJSONDownload.txt.in new file mode 100644 index 0000000..ed9135e --- /dev/null +++ b/External/RapidJSON/CMakeRapidJSONDownload.txt.in @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.8.2) + +project(RapidJSON-download NONE) + +include(ExternalProject) +ExternalProject_Add(RapidJSON + GIT_REPOSITORY https://github.com/Tencent/rapidjson.git + GIT_TAG 232389d4f1012dddec4ef84861face2d2ba85709 + SOURCE_DIR "${CMAKE_BINARY_DIR}/RapidJSON-src" + BINARY_DIR "${CMAKE_BINARY_DIR}/RapidJSON-build" + PATCH_COMMAND "${CMAKE_COMMAND}" -DSOURCE_DIR=${CMAKE_BINARY_DIR}/RapidJSON-src -P "${CMAKE_CURRENT_SOURCE_DIR}/patches/fix-null-allocator-deref.cmake" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/External/RapidJSON/patches/fix-null-allocator-deref.cmake b/External/RapidJSON/patches/fix-null-allocator-deref.cmake new file mode 100644 index 0000000..f7cd1ea --- /dev/null +++ b/External/RapidJSON/patches/fix-null-allocator-deref.cmake @@ -0,0 +1,34 @@ +# fix-null-allocator-deref.cmake +# +# Patches RapidJSON schema.h to fix CWE-476: null pointer dereference in +# EndMissingDependentProperties(). Replaces calls to +# GetInvalidSchemaPointer().GetAllocator() (which dereferences a null +# allocator_ pointer) with GetStateAllocator() (which is always initialized). +# +# Usage: cmake -DSOURCE_DIR= -P fix-null-allocator-deref.cmake + +if(NOT DEFINED SOURCE_DIR) + message(FATAL_ERROR "SOURCE_DIR must be defined") +endif() + +set(SCHEMA_FILE "${SOURCE_DIR}/include/rapidjson/schema.h") + +if(NOT EXISTS "${SCHEMA_FILE}") + message(FATAL_ERROR "schema.h not found at ${SCHEMA_FILE}") +endif() + +file(READ "${SCHEMA_FILE}" content) + +string(FIND "${content}" "&GetInvalidSchemaPointer().GetAllocator()" match_pos) +if(match_pos EQUAL -1) + message(STATUS "Patch already applied or pattern not found in schema.h") + return() +endif() + +string(REPLACE + "&GetInvalidSchemaPointer().GetAllocator()" + "&GetStateAllocator()" + content "${content}") + +file(WRITE "${SCHEMA_FILE}" "${content}") +message(STATUS "Patched schema.h: replaced GetInvalidSchemaPointer().GetAllocator() with GetStateAllocator()") diff --git a/External/googletest/CMakeGoogleTestDownload.txt.in b/External/googletest/CMakeGoogleTestDownload.txt.in new file mode 100644 index 0000000..c55d79e --- /dev/null +++ b/External/googletest/CMakeGoogleTestDownload.txt.in @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.17.0 + SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" + BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/External/googletest/CMakeLists.txt b/External/googletest/CMakeLists.txt new file mode 100644 index 0000000..8595c82 --- /dev/null +++ b/External/googletest/CMakeLists.txt @@ -0,0 +1,37 @@ +# Check if the gtest target has already been defined +if (TARGET gtest) + message(AUTHOR_WARNING "gtest target already defined, skipping") + return() +endif() + +# Download and unpack googletest at configure time +configure_file(CMakeGoogleTestDownload.txt.in ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt) +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) +if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") +endif() +execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) +if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") +endif() + +# Prevent overriding the parent project's compiler/linker +# settings on Windows +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +# Add googletest directly to our build. This defines +# the gtest and gtest_main targets. +add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src + ${CMAKE_BINARY_DIR}/googletest-build + EXCLUDE_FROM_ALL) + +# The gtest/gtest_main targets carry header search path +# dependencies automatically when using CMake 2.8.11 or +# later. Otherwise we have to add them here ourselves. +if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") +endif() diff --git a/vcpkg.json b/vcpkg.json deleted file mode 100644 index 7fa0d2a..0000000 --- a/vcpkg.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "gltf-sdk", - "version-string": "0.0.1", - "builtin-baseline": "c3867e714dd3a51c272826eea77267876517ed99", - "dependencies": [ - "rapidjson" - ], - "features": { - "tests": { - "description": "Build unit tests", - "dependencies": [ - "gtest" - ] - } - }, - "overrides": [ - { - "name": "rapidjson", - "version": "2025-02-26" - } - ] -}