Conversation
CppiaVar::setValue, behind Reflect.setField and Reflect.setProperty on a cppia instance, stored object and string pointers without a generational write barrier. A young value set on an old instance was reachable only through it, so the next minor collection freed it, and the instance then pointed at reused memory. The JIT and interpreted field setters already had the barrier. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cppia host now builds with HXCPP_GC_GENERATIONAL. ClientHolder grows old through full collections, then the host sets its array field with Reflect.setField, and a weak reference checks that a generational collection keeps the array. Without the write barrier, it is freed while the holder still points at it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With
HXCPP_GC_GENERATIONAL, a value set by name on a cppia instance can be freed while the instance still points at it.Reflect.setField/Reflect.setPropertyon a cppia object end inCppiaClassInfo::setField, which stores a member variable throughCppiaVar::setValue. That function writes object and string pointers with a plain assignment, without a write barrier. The JIT and interpreted field setters (MemReferenceSetter) andFieldMapSetalready have one.When the instance is old and the value young, the value is then reachable only from the old instance. A generational collection does not traverse it, so the next one frees the value, and a later read of the field reads reused memory. In practice, a closure in a loaded script that reassigned an array field (compiled to a set by name) crashed on the next read of that array after a minor collection, or during a later full collection that followed the dangling pointer.
A standalone reproduction is attached: a scriptable host with
HXCPP_GC_GENERATIONALloads a small ASCII cppia script, lets the script's objects grow old with full collections, then has a closure set an array field throughReflect.setFieldand another read it, with only generational collections in between. Onmasterthe first read crashes, every run; with this change all five rounds pass. hxcpp-cppia-set-field-barrier.zipThe fix adds
HX_OBJ_WB_GETafter thefsStringstore and after everyfsObjectstore (each case stores one object pointer at the field's offset), underHXCPP_GC_GENERATIONAL.