Skip to content

Commit b7457e8

Browse files
authored
Fix issue 47805 (#52183)
Fix issue #47805 - in this case, the BGC_MARKED_BY_FGC bit (0x2) set in the method table leaked out and caused issues for the user program. In the cases that I've been able to repro, this happened because the bit got set for a short object right in front of a pinned plug, and then saved away by enque_pinned_plug. Later on, in the case of mark & sweep, we check for the bit and reset it, but later we copy the saved object back by calling recover_saved_pinned_info which calls recover_plug_info to do the actual work. This isn't a problem for the compact case, because we copy the saved object back during compact_plug, turn the bit off, then save it again at the end of compact_plug. The fix is to turn off the extra bits at the beginning of enque_pinned_plug and save_post_plug_info for the copy that is later restored in mark & sweep (there are actually two copies saved, one for use during compact and one for use during mark & sweep). This builds on an earlier fix by Maoni for a similar problem with another bit.
1 parent 788a85e commit b7457e8

File tree

1 file changed

+63
-26
lines changed

1 file changed

+63
-26
lines changed

src/coreclr/gc/gc.cpp

+63-26
Original file line numberDiff line numberDiff line change
@@ -4165,7 +4165,16 @@ size_t gcard_of ( uint8_t*);
41654165
// We only do this when we decide to compact.
41664166
#define BGC_MARKED_BY_FGC (size_t)0x2
41674167
#define MAKE_FREE_OBJ_IN_COMPACT (size_t)0x4
4168-
#endif //DOUBLY_LINKED_FL
4168+
#define ALLOWED_SPECIAL_HEADER_BITS (GC_MARKED|BGC_MARKED_BY_FGC|MAKE_FREE_OBJ_IN_COMPACT)
4169+
#else //DOUBLY_LINKED_FL
4170+
#define ALLOWED_SPECIAL_HEADER_BITS (GC_MARKED)
4171+
#endif //!DOUBLY_LINKED_FL
4172+
4173+
#ifdef HOST_64BIT
4174+
#define SPECIAL_HEADER_BITS (0x7)
4175+
#else
4176+
#define SPECIAL_HEADER_BITS (0x3)
4177+
#endif
41694178

41704179
#define slot(i, j) ((uint8_t**)(i))[(j)+1]
41714180

@@ -4267,11 +4276,7 @@ class CObjectHeader : public Object
42674276

42684277
MethodTable *GetMethodTable() const
42694278
{
4270-
return( (MethodTable *) (((size_t) RawGetMethodTable()) & (~(GC_MARKED
4271-
#ifdef DOUBLY_LINKED_FL
4272-
| BGC_MARKED_BY_FGC | MAKE_FREE_OBJ_IN_COMPACT
4273-
#endif //DOUBLY_LINKED_FL
4274-
))));
4279+
return( (MethodTable *) (((size_t) RawGetMethodTable()) & (~SPECIAL_HEADER_BITS)));
42754280
}
42764281

42774282
void SetMarked()
@@ -4339,6 +4344,26 @@ class CObjectHeader : public Object
43394344
}
43404345
#endif //DOUBLY_LINKED_FL
43414346

4347+
size_t ClearSpecialBits()
4348+
{
4349+
size_t special_bits = ((size_t)RawGetMethodTable()) & SPECIAL_HEADER_BITS;
4350+
if (special_bits != 0)
4351+
{
4352+
assert ((special_bits & (~ALLOWED_SPECIAL_HEADER_BITS)) == 0);
4353+
RawSetMethodTable ((MethodTable*)(((size_t)RawGetMethodTable()) & ~(SPECIAL_HEADER_BITS)));
4354+
}
4355+
return special_bits;
4356+
}
4357+
4358+
void SetSpecialBits (size_t special_bits)
4359+
{
4360+
assert ((special_bits & (~ALLOWED_SPECIAL_HEADER_BITS)) == 0);
4361+
if (special_bits != 0)
4362+
{
4363+
RawSetMethodTable ((MethodTable*)(((size_t)RawGetMethodTable()) | special_bits));
4364+
}
4365+
}
4366+
43424367
CGCDesc *GetSlotMap ()
43434368
{
43444369
assert (GetMethodTable()->ContainsPointers());
@@ -4506,6 +4531,17 @@ inline
45064531
BOOL is_plug_padded (uint8_t* node){return FALSE;}
45074532
#endif //SHORT_PLUGS
45084533

4534+
inline
4535+
size_t clear_special_bits (uint8_t* node)
4536+
{
4537+
return header(node)->ClearSpecialBits();
4538+
}
4539+
4540+
inline
4541+
void set_special_bits (uint8_t* node, size_t special_bits)
4542+
{
4543+
header(node)->SetSpecialBits (special_bits);
4544+
}
45094545

45104546
inline size_t unused_array_size(uint8_t * p)
45114547
{
@@ -20978,16 +21014,16 @@ void gc_heap::enque_pinned_plug (uint8_t* plug,
2097821014

2097921015
if (save_pre_plug_info_p)
2098021016
{
20981-
#ifdef SHORT_PLUGS
20982-
BOOL is_padded = is_plug_padded (last_object_in_last_plug);
20983-
if (is_padded)
20984-
clear_plug_padded (last_object_in_last_plug);
20985-
#endif //SHORT_PLUGS
21017+
// In the case of short plugs or doubly linked free lists, there may be extra bits
21018+
// set in the method table pointer.
21019+
// Clear these bits for the copy saved in saved_pre_plug, but not for the copy
21020+
// saved in saved_pre_plug_reloc.
21021+
// This is because we need these bits for compaction, but not for mark & sweep.
21022+
size_t special_bits = clear_special_bits (last_object_in_last_plug);
21023+
// now copy the bits over
2098621024
memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
20987-
#ifdef SHORT_PLUGS
20988-
if (is_padded)
20989-
set_plug_padded (last_object_in_last_plug);
20990-
#endif //SHORT_PLUGS
21025+
// restore the bits in the original
21026+
set_special_bits (last_object_in_last_plug, special_bits);
2099121027

2099221028
memcpy (&(m.saved_pre_plug_reloc), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
2099321029

@@ -20997,7 +21033,7 @@ void gc_heap::enque_pinned_plug (uint8_t* plug,
2099721033
{
2099821034
record_interesting_data_point (idp_pre_short);
2099921035
#ifdef SHORT_PLUGS
21000-
if (is_padded)
21036+
if (is_plug_padded (last_object_in_last_plug))
2100121037
record_interesting_data_point (idp_pre_short_padded);
2100221038
#endif //SHORT_PLUGS
2100321039
dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!",
@@ -21041,16 +21077,17 @@ void gc_heap::save_post_plug_info (uint8_t* last_pinned_plug, uint8_t* last_obje
2104121077
assert (last_pinned_plug == m.first);
2104221078
m.saved_post_plug_info_start = (uint8_t*)&(((plug_and_gap*)post_plug)[-1]);
2104321079

21044-
#ifdef SHORT_PLUGS
21045-
BOOL is_padded = is_plug_padded (last_object_in_last_plug);
21046-
if (is_padded)
21047-
clear_plug_padded (last_object_in_last_plug);
21048-
#endif //SHORT_PLUGS
21080+
// In the case of short plugs or doubly linked free lists, there may be extra bits
21081+
// set in the method table pointer.
21082+
// Clear these bits for the copy saved in saved_post_plug, but not for the copy
21083+
// saved in saved_post_plug_reloc.
21084+
// This is because we need these bits for compaction, but not for mark & sweep.
21085+
// Note that currently none of these bits will ever be set in the object saved *after*
21086+
// a pinned plug - this object is currently pinned along with the pinned object before it
21087+
size_t special_bits = clear_special_bits (last_object_in_last_plug);
2104921088
memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
21050-
#ifdef SHORT_PLUGS
21051-
if (is_padded)
21052-
set_plug_padded (last_object_in_last_plug);
21053-
#endif //SHORT_PLUGS
21089+
// restore the bits in the original
21090+
set_special_bits (last_object_in_last_plug, special_bits);
2105421091

2105521092
memcpy (&(m.saved_post_plug_reloc), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
2105621093

@@ -21069,7 +21106,7 @@ void gc_heap::save_post_plug_info (uint8_t* last_pinned_plug, uint8_t* last_obje
2106921106
dprintf (3, ("PP %Ix last obj %Ix is too short", last_pinned_plug, last_object_in_last_plug));
2107021107
record_interesting_data_point (idp_post_short);
2107121108
#ifdef SHORT_PLUGS
21072-
if (is_padded)
21109+
if (is_plug_padded (last_object_in_last_plug))
2107321110
record_interesting_data_point (idp_post_short_padded);
2107421111
#endif //SHORT_PLUGS
2107521112
m.set_post_short();

0 commit comments

Comments
 (0)