mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-08 03:03:50 +00:00
Threat model: parser is intended for server-side processing of attacker- supplied glTF/GLB. Two adversarial review rounds plus a 1-hour libFuzzer run (4 workers, ASan+UBSan, ~420M execs total, zero new artifacts) drove this set of fixes. Concrete PoCs in tests/v3/security/ confirmed each issue was exploitable on the prior code. Path traversal (CRITICAL): tg3__load_external_file concatenated base_dir with the JSON-supplied URI verbatim. A glTF with "uri":"../../../tmp/secret" successfully loaded the file from outside base_dir (verified by FNV64 match). New tg3__uri_is_safe rejects empty, NUL, leading / or \\, Windows drive prefixes, and any '..' segment. Path-buffer length checks switched to saturating subtraction so 32-bit size_t cannot wrap. Sign-coercion in byteStride: int32_t -1 was cast directly to uint32_t, producing 0xFFFFFFFF and propagating into downstream count*stride math. Restrict to glTF spec range: 0 (tightly packed) or [4, 252]. Index validation: parsed int32 index fields (accessor.bufferView, primitive.indices/material/attributes, node.mesh/skin/camera/light, scene.nodes[], skin.joints[], animation channel/sampler refs, MSFT_lod ids, KHR_audio emitter/source refs, etc.) were stored unchecked. New tg3__validate_indices walks every index field and returns TG3_ERR_INVALID_INDEX on out-of-range. Gated by tg3_parse_options.validate_indices, defaulting to 1. Use-after-free on parse failure (PRE-EXISTING, surfaced by ASan during fix verification): tg3_parse and tg3_parse_glb destroyed model->arena_ on error paths, but error messages on the user-facing tg3_error_stack were arena-allocated. Any caller reading errors.entries[i].message after parse failure read freed memory. tg3_model_free is now sole arena owner; arena lives across error paths so messages stay valid until the caller frees the model. Other fixes: - tg3_parse_glb: hoist tg3__model_init before header parse so callers can safely tg3_model_free on header failure. - tg3__parse_primitive morph targets: when arena alloc returns NULL, pair with target_counts[ti]=0 so validators do not deref. - Defensive 'if (!tarr) continue' in the morph-target validator loop. - New Security Considerations block in tiny_gltf_v3.h documents the threat model, default-on validation, fs-callback contract, and error message lifetime. Verification: 13 internal tests in tester_v3_c (incl. 7 new security regressions covering path traversal absolute and relative, fs-callback no-call assertion, byteStride wrap, OOB index, opt-in raw mode, ext fields, and arena-message lifetime), 134/134 Khronos sample models match v1 ground truth digest, 1-hour ASan+UBSan fuzz on the final code clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>