CNA-BUG-129: BlendMorphTargetsEXT indexes NormalDeltas and every per-vertex delta array without checking their lengths
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
The function validates only the weight count; a hand-built MorphTargetDataEXT whose NormalDeltas has fewer entries than targets, or whose delta arrays are shorter than the vertex count, is read out of range.
- Identifier
CNA-BUG-129- Category
- Bug
- Subsystem
- Models & glTF
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Microsoft::Xna::Framework::Graphics::BlendMorphTargetsEXT(const MorphTargetDataEXT&, const std::vector<float>&) and ApplyMorphWeightsEXT
Expected behaviour
BlendMorphTargetsEXT is a public CNAEXT function that already validates its input (it throws when the weight count differs from the target count). Inconsistent morph data should likewise be refused, not read out of range; the same function guards TangentDeltas with t < TangentDeltas.size().
Actual behaviour at TARGET
In MorphTargetEXT.cpp the loop reads morph.NormalDeltas[t] for every weighted target with no check of the outer size, and reads PositionDeltas[t][v], NormalDeltas[t][v] and TangentDeltas[t][v] for every vertex of BaseVertexBytes whenever the inner vector is non-empty, without checking it holds that many elements. MorphTargetEXT.hpp describes the arrays as per-target, per-vertex but states no precondition. The importers always build consistent arrays, so only application-built data reaches the out-of-range reads.
Source locations
modules/graphics/src/Xna/MorphTargetEXT.cpp— BlendMorphTargetsEXT: unchecked NormalDeltas[t] and per-vertex indexing; guarded TangentDeltasmodules/graphics/include/Microsoft/Xna/Framework/Graphics/MorphTargetEXT.hpp— MorphTargetDataEXT field documentationmodules/graphics/tests/Microsoft/Xna/Framework/Graphics/MorphTargetEXTTests.cpp— consistent fixtures only
Evidence
Checked by reading at 009d40f5; not executed.
Focused reproduction
// Illustrative; not compiled or run.
MorphTargetDataEXT morph;
morph.Stride = 12;
morph.BaseVertexBytes.resize(12 * 3); // three vertices
morph.PositionDeltas = {std::vector<Vector3>(3)}; // one target
// NormalDeltas left empty: NormalDeltas[0] is out of range
auto bytes = BlendMorphTargetsEXT(morph, {1.0f});
Current tests
MorphTargetEXTTests.cpp blends consistent data only; no test passes short or missing delta arrays.
Regression test
Cases with a missing NormalDeltas entry and a delta array shorter than the vertex count, expecting an exception (the weight-count check already sets the pattern).
Blast radius
Applications that build MorphTargetDataEXT by hand. Imported glTF, .cnj and .cnb morph targets are consistent by construction.
Workaround
Give NormalDeltas and TangentDeltas one (possibly empty) entry per target and size every non-empty delta array to the vertex count.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- Model loading: runtime glTF
- Deep dives
- Skinning and animation: morph targets
- Known issues
- Bug index