Skip to content

Commit

Permalink
a test for the bug in #4925
Browse files Browse the repository at this point in the history
basically if you do things in just the wrong order, then you can indeed
get a black object pointing to a white (prebuilt) object.

so far this is only reproducing the crashing assertion. I think with
some more careful manipulation it should be possible to also get
the gc to collect an object that is still in use with this approach
(which explains the memory corruption problems that some people are
seeing).
  • Loading branch information
cfbolz committed Mar 18, 2024
1 parent 9913645 commit e97413e
Showing 1 changed file with 61 additions and 0 deletions.
61 changes: 61 additions & 0 deletions rpython/memory/gc/test/test_direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,3 +844,64 @@ def test_collect_step(self, debuglog):
(incminimark.STATE_SWEEPING, incminimark.STATE_FINALIZING),
(incminimark.STATE_FINALIZING, incminimark.STATE_SCANNING)
]

def test_gc_debug_crash_with_prebuilt_objects(self):
from rpython.rlib import rgc
def flags(obj):
return self.gc.header(llmemory.cast_ptr_to_adr(obj)).tid.rest

prebuilt = lltype.malloc(S, immortal=True)
prebuilt.x = 42
self.consider_constant(prebuilt)

self.gc.DEBUG = 2

old2 = self.malloc(S)
old2.x = 45
self.stackroots.append(old2)
old = self.malloc(S)
old.x = 43
self.write(old, 'next', prebuilt)
self.stackroots.append(old)
val = self.gc.collect_step()
assert rgc.old_state(val) == incminimark.STATE_SCANNING
assert rgc.new_state(val) == incminimark.STATE_MARKING
old2 = self.stackroots[0] # reload
old = self.stackroots[1]

# now a major next collection starts
# run things with TEST_VISIT_SINGLE_STEP = True so we can control
# the timing correctly
self.gc.TEST_VISIT_SINGLE_STEP = True
import pdb;pdb.set_trace()
# run two marking steps, the first one marks obj, the second one
# prebuilt (which does nothing), but obj2 is left so we aren't done
# with marking
val = self.gc.collect_step()
val = self.gc.collect_step()
assert rgc.old_state(val) == incminimark.STATE_MARKING
assert rgc.new_state(val) == incminimark.STATE_MARKING
assert flags(old) & incminimark.GCFLAG_VISITED
assert (flags(old2) & incminimark.GCFLAG_VISITED) == 0
# prebuilt counts as grey but for prebuilt reasons
assert (flags(prebuilt) & incminimark.GCFLAG_VISITED) == 0
assert flags(prebuilt) & incminimark.GCFLAG_NO_HEAP_PTRS
# its write barrier is active
assert flags(prebuilt) & incminimark.GCFLAG_TRACK_YOUNG_PTRS

# now lets write a newly allocated object into prebuilt
new = self.malloc(S)
new.x = -10
# write barrier of prebuilt triggers
self.write(prebuilt, 'next', new)
# prebuilt got added both to old_objects_pointing_to_young and
# prebuilt_root_objects, so those flags get cleared
assert (flags(prebuilt) & incminimark.GCFLAG_NO_HEAP_PTRS) == 0
assert (flags(prebuilt) & incminimark.GCFLAG_TRACK_YOUNG_PTRS) == 0
# thus the prebuilt object now counts as white!
assert (flags(prebuilt) & incminimark.GCFLAG_VISITED) == 0

# this triggers the assertion black -> white pointer
# for the reference obj -> prebuilt
self.gc.collect_step()

0 comments on commit e97413e

Please sign in to comment.