When a STEP instance has fewer attribute tokens than its schema declares
(commonly from corrupted/malformed syntax), parse_context::construct()
sized the in-memory attribute storage to the smaller token count instead
of the schema's attribute count. This left the storage's last N attribute
slots simply nonexistent rather than blank, so any later read of one of
those trailing attributes by index threw an uncaught IfcParse::IfcException
("Index N is out of range for storage of size N") that terminated the
whole process (SIGABRT) instead of being handled as a parse warning.
Fix: when the schema declaration is known, size the storage to the
schema's attribute count. Indices beyond the number of tokens found are
left at their existing default-constructed blank value (the storage
constructor already blank-initializes every slot), so a truncated
instance now degrades to blank values for its missing trailing
attributes, matching the parser's existing "expected N attribute values,
found M" warning intent instead of crashing.
Reproduced with the fuzzing script attached to #5679: single-byte
mutations of a minimal IFC4 file that corrupt the IFCPROJECT instance's
token stream reliably aborted IfcConvert with this exact exception before
the fix, and now parse with a logged syntax error and exit code 0.
Fixes #5679
Generated with the assistance of an AI coding tool.
Problem
A single byte corruption of a valid IFC file's STEP syntax (e.g. dropping a trailing attribute token) reliably crashes IfcConvert with an uncaught exception and SIGABRT, instead of being handled as a parse warning. Found via the fuzzing script attached to #5679.
Root cause
When a STEP instance has fewer attribute tokens than its schema declares, parse_context::construct() sized the in memory attribute storage to the smaller token count instead of the schema's attribute count. This left the storage's trailing attribute slots nonexistent rather than blank, so any later read of one of those attributes by index threw IfcParse::IfcException ("Index N is out of range for storage of size N") that escaped every catch block and terminated the process.
Fix
When the schema declaration is known, size the storage to the schema's attribute count. The storage constructor already blank initializes every slot, and this matches an established pattern already used elsewhere in the same file (in_memory_attribute_storage(T::Class().attribute_count())). The attribute population loop is bounded by the actual token count regardless of storage size, so trailing indices beyond the tokens found simply stay blank instead of being read out of bounds.
Testing
Reproduced with the fuzzing script attached to #5679: the exact crash files (single byte corruption of #1=IFCPROJECT(...)) aborted with the out of range exception before the fix; after the fix, both parse with a logged SYN012 error and exit code 0. Re ran a 1500 iteration fuzz sweep against the patched binary with no regressions, and confirmed a real world 24,305 entity IFC4 file converts identically (614 objects) before and after.
Fixes #5679.
Generated with the assistance of an AI coding tool.