Skip to content

Barrier the pointer stores of a cppia field set by name - #1408

Open
Tutez64 wants to merge 2 commits into
HaxeFoundation:masterfrom
Tutez64:bugfix/cppia-set-field-barrier
Open

Tutez64 wants to merge 2 commits into
HaxeFoundation:masterfrom
Tutez64:bugfix/cppia-set-field-barrier

Conversation

@Tutez64

@Tutez64 Tutez64 commented Sep 30, 2026

Copy link
Copy Markdown

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.setProperty on a cppia object end in CppiaClassInfo::setField, which stores a member variable through CppiaVar::setValue. That function writes object and string pointers with a plain assignment, without a write barrier. The JIT and interpreted field setters (MemReferenceSetter) and FieldMapSet already 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_GENERATIONAL loads a small ASCII cppia script, lets the script's objects grow old with full collections, then has a closure set an array field through Reflect.setField and another read it, with only generational collections in between. On master the first read crashes, every run; with this change all five rounds pass. hxcpp-cppia-set-field-barrier.zip

The fix adds HX_OBJ_WB_GET after the fsString store and after every fsObject store (each case stores one object pointer at the field's offset), under HXCPP_GC_GENERATIONAL.

Tutez64 and others added 2 commits September 30, 2026 18:45
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>
@Tutez64

Tutez64 commented Oct 3, 2026

Copy link
Copy Markdown
Author

Added a test to test/cppia in 68322a0. Its -jit run also needs #1411, which fixes an unrelated JIT nursery-alignment bug the test exposed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant