Bug: Hash-collision CPU amplification parsing FBX node/property names
The FBX importer stores node names and property names read from the model file into default-hashed std::unordered_maps. On builds with predictable standard-library string hashing, an attacker-supplied .fbx whose node/property names are chosen to land in the same bucket makes insertion (and lookup) during import approach quadratic CPU work. Loading an untrusted model is a normal operation for any application that uses Assimp (game engines, DCC tools, asset pipelines, thumbnailers), so this is reachable without authentication from a single file.
Affected code
std::fbx_unordered_map expands to std::unordered_map on modern builds (code/AssetLib/FBX/FBXCompileConfig.h:70), so the maps below use the default std::hash<std::string>.
| Map | Key (from the FBX file) | Declaration / insertion |
|---|---|---|
FBXConverter::mNodeNames (NodeNameCache) |
model node name | decl FBXConverter.h:455-456; inserted in GetUniqueName at FBXConverter.cpp:512 (and 519 for the de-duplicated name) |
PropertyTable::lazyProps (LazyPropertyMap) |
property name | decl FBXProperties.h:96,122; batch-filled from the file at FBXProperties.cpp:180 (lazyProps[name] = ... for every property record) |
// FBXCompileConfig.h
# define fbx_unordered_map unordered_map // -> std::unordered_map<std::string, ...>
// FBXProperties.cpp — every property name in the file is inserted into lazyProps
lazyProps[name] = v.second; // name = FBX property name
// FBXConverter.cpp — node name from the file; duplicates get a numbered suffix
void FBXConverter::GetUniqueName(const std::string& name, std::string& uniqueName) {
auto it_pair = mNodeNames.insert({ name, 0 }); // name = FBX node name
// on a duplicate, a suffixed uniqueName is generated and inserted instead
}Scope note: PropertyTable::props (PropertyMap) is not part of this finding. It is a lazy per-query cache — PropertyTable::Get(name) inserts a single entry props[name] = ReadTypedProperty(...) only when the caller looks that name up (FBXProperties.cpp:194-201). It is not batch-filled from the file (constructing a table with N file properties leaves lazyProps = N and props = 0), so only lazyProps and mNodeNames receive attacker names in bulk.
Node names are de-duplicated with a numbered suffix in GetUniqueName, so a collision set must consist of distinct node names (each accepted name is inserted once); the collision target is the stored node name.
Trigger and impact
- Reachable by importing a single untrusted FBX file; no authentication and no accumulation across requests is required.
- The cited insertion sites have no per-file cap on the number of node/property names.
- Impact is CPU amplification during import: building
lazyProps/mNodeNamesfrom many colliding names is O(n²) (each insert probes the growing bucket), and laterPropertyTable::Getlookups are O(bucket size). This report demonstrates map-level degradation, not a specific end-to-end outage.
Suggested fix
- Use a hash-flooding-resistant (per-process-seeded) hasher for these file-derived name maps, and/or bound the number of node/property names accepted per file.
Source: assimp/assimp