ObjectValue::display looks up the object it names and dereferences the result
without checking it:
out << "<object " << objectId_ << ", type ";
ObjectPtr obj = Universe::instance().getObject(objectId_);
ObjectPtr parent = obj->parent(); // obj is null for a destroyed object
getObject returns an empty ObjectPtr once destroy has removed the object,
so obj->parent() dereferences null and the interpreter dies on SIGSEGV.
Only nameless objects reach that line. A named object takes the early return
three lines above, having found an identifier for itself, so the crash is
reachable exactly through the create/destroy pair — which is to say through
the dynamic-object feature the reference manual documents with a linked-list
example.
Reproducing
type node based on null
refer : UNDEFINED
end
null main
temp : UNDEFINED
methods
'START' : {
create node named temp
destroy temp
display temp
}
end
$ archetype --source=crash.arch
$ echo $?
139
write temp on the same value is unaffected: it goes through
stringConversion, not display, and answers UNDEFINED as it should. So the
crash needs a display statement, the 'DEBUG EXPRESSIONS' toggle, or
anything else that renders a value in diagnostic form — every one of which is a
debugging aid, which is a mean place to keep a crash.
Age
Present since the C++ implementation; the code has had this shape since 3.0.
Whether the Turbo Pascal original had the same hole is not known.
Fix
Guard the lookup and render the dangling reference as what it is rather than
following it. Something in the shape of:
ObjectPtr obj = Universe::instance().getObject(objectId_);
if (not obj) {
out << "<destroyed object " << objectId_ << '>';
return;
}
A test belongs with it: create, destroy, then display, asserting that the
statement completes and says something sensible. TestObject.cc already builds
and tears down small universes, so it is the natural home.
Two adjacent questions worth settling in the same pass, though neither needs to
block the fix:
ObjectValue::asRDF calls identifier_of on the same id. Worth checking
whether it has the same exposure through --inspect on a universe holding a
destroyed reference.
- After
destroy, the referring attribute still holds an object value rather
than reading as UNDEFINED, which is what the manual has always promised.
That is a separate defect from this crash, and possibly the more interesting
one, but fixing the null dereference should not wait on it.
ObjectValue::displaylooks up the object it names and dereferences the resultwithout checking it:
getObjectreturns an emptyObjectPtroncedestroyhas removed the object,so
obj->parent()dereferences null and the interpreter dies on SIGSEGV.Only nameless objects reach that line. A named object takes the early return
three lines above, having found an identifier for itself, so the crash is
reachable exactly through the
create/destroypair — which is to say throughthe dynamic-object feature the reference manual documents with a linked-list
example.
Reproducing
write tempon the same value is unaffected: it goes throughstringConversion, notdisplay, and answers UNDEFINED as it should. So thecrash needs a
displaystatement, the'DEBUG EXPRESSIONS'toggle, oranything else that renders a value in diagnostic form — every one of which is a
debugging aid, which is a mean place to keep a crash.
Age
Present since the C++ implementation; the code has had this shape since 3.0.
Whether the Turbo Pascal original had the same hole is not known.
Fix
Guard the lookup and render the dangling reference as what it is rather than
following it. Something in the shape of:
A test belongs with it: create, destroy, then
display, asserting that thestatement completes and says something sensible.
TestObject.ccalready buildsand tears down small universes, so it is the natural home.
Two adjacent questions worth settling in the same pass, though neither needs to
block the fix:
ObjectValue::asRDFcallsidentifier_ofon the same id. Worth checkingwhether it has the same exposure through
--inspecton a universe holding adestroyed reference.
destroy, the referring attribute still holds an object value ratherthan reading as UNDEFINED, which is what the manual has always promised.
That is a separate defect from this crash, and possibly the more interesting
one, but fixing the null dereference should not wait on it.