Skip to content

display of a destroyed object dereferences null and crashes the interpreter #62

Description

@gitosaurus

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions