Skip to content

Commit 0fb8f1f

Browse files
committed
Adjust liveness to prevent stack overflows (case 935563). 1. Add kMaxTraverseRecursionDepth limit for array element chunk processing. 2. Track if array elements are actually added to list for processing via 'items_processed' variables. 3. Bump the kArrayElementsPerChunk of items to process at a time to 256 from 128.
1 parent 4c43ff3 commit 0fb8f1f

File tree

1 file changed

+41
-13
lines changed

1 file changed

+41
-13
lines changed

unity/unity_liveness.c

+41-13
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,18 @@ struct _LivenessState
7272
register_object_callback filter_callback;
7373
WorldStateChanged onWorldStartCallback;
7474
WorldStateChanged onWorldStopCallback;
75+
guint traverse_depth; // track recursion. Prevent stack overflow by limiting recurion
7576
};
7677

78+
/* number of sub elements of an array to process before recursing
79+
* we take a depth first approach to use stack space rather than re-allocating
80+
* processing array which requires restarting world to ensure allocator lock is not held
81+
*/
82+
const int kArrayElementsPerChunk = 256;
83+
84+
/* how far we recurse processing array elements before we stop. Prevents stack overflow */
85+
const int kMaxTraverseRecursionDepth = 128;
86+
7787
/* Liveness calculation */
7888
LivenessState* mono_unity_liveness_allocate_struct (MonoClass* filter, guint max_count, register_object_callback callback, void* callback_userdata, WorldStateChanged onWorldStartCallback, WorldStateChanged onWorldStopCallback);
7989
void mono_unity_liveness_stop_gc_world (LivenessState* state);
@@ -161,7 +171,7 @@ static void mono_traverse_generic_object( MonoObject* object, LivenessState* sta
161171
}
162172

163173

164-
static void mono_add_process_object (MonoObject* object, LivenessState* state)
174+
static gboolean mono_add_process_object (MonoObject* object, LivenessState* state)
165175
{
166176
if (object && !IS_MARKED(object))
167177
{
@@ -179,8 +189,11 @@ static void mono_add_process_object (MonoObject* object, LivenessState* state)
179189
if(array_is_full(state->process_array))
180190
array_safe_grow(state, state->process_array);
181191
array_push_back(state->process_array, object);
192+
return TRUE;
182193
}
183194
}
195+
196+
return FALSE;
184197
}
185198

186199
static gboolean mono_field_can_contain_references(MonoClassField* field)
@@ -194,11 +207,12 @@ static gboolean mono_field_can_contain_references(MonoClassField* field)
194207
return MONO_TYPE_IS_REFERENCE(field->type);
195208
}
196209

197-
static void mono_traverse_object_internal (MonoObject* object, gboolean isStruct, MonoClass* klass, LivenessState* state)
210+
static gboolean mono_traverse_object_internal (MonoObject* object, gboolean isStruct, MonoClass* klass, LivenessState* state)
198211
{
199212
int i;
200213
MonoClassField *field;
201214
MonoClass *p;
215+
gboolean added_objects = FALSE;
202216

203217
g_assert (object);
204218

@@ -226,10 +240,10 @@ static void mono_traverse_object_internal (MonoObject* object, gboolean isStruct
226240
if (field->type->type == MONO_TYPE_GENERICINST)
227241
{
228242
g_assert(field->type->data.generic_class->cached_class);
229-
mono_traverse_object_internal((MonoObject*)offseted, TRUE, field->type->data.generic_class->cached_class, state);
243+
added_objects |= mono_traverse_object_internal((MonoObject*)offseted, TRUE, field->type->data.generic_class->cached_class, state);
230244
}
231245
else
232-
mono_traverse_object_internal((MonoObject*)offseted, TRUE, field->type->data.klass, state);
246+
added_objects |= mono_traverse_object_internal((MonoObject*)offseted, TRUE, field->type->data.klass, state);
233247
continue;
234248
}
235249

@@ -239,10 +253,12 @@ static void mono_traverse_object_internal (MonoObject* object, gboolean isStruct
239253
MonoObject* val = NULL;
240254
MonoVTable *vtable = NULL;
241255
mono_field_get_value (object, field, &val);
242-
mono_add_process_object (val, state);
256+
added_objects |= mono_add_process_object (val, state);
243257
}
244258
}
245259
}
260+
261+
return added_objects;
246262
}
247263

248264
static void mono_traverse_object (MonoObject* object, LivenessState* state)
@@ -274,16 +290,25 @@ static void mono_traverse_objects (LivenessState* state)
274290
int i = 0;
275291
MonoObject* object = NULL;
276292

293+
state->traverse_depth++;
277294
while (state->process_array->len > 0)
278295
{
279296
object = array_pop_back(state->process_array);
280297
mono_traverse_generic_object(object, state);
281298
}
299+
state->traverse_depth--;
300+
}
301+
302+
static gboolean should_traverse_objects (size_t index, gint32 recursion_depth)
303+
{
304+
// Add kArrayElementsPerChunk objects at a time and then traverse
305+
return ((index + 1) & (kArrayElementsPerChunk - 1)) == 0 &&
306+
recursion_depth < kMaxTraverseRecursionDepth;
282307
}
283308

284309
static void mono_traverse_array (MonoArray* array, LivenessState* state)
285310
{
286-
int i = 0;
311+
size_t i = 0;
287312
gboolean has_references;
288313
MonoObject* object = (MonoObject*)array;
289314
MonoClass* element_class;
@@ -309,26 +334,28 @@ static void mono_traverse_array (MonoArray* array, LivenessState* state)
309334
array_length = mono_array_length (array);
310335
if (element_class->valuetype)
311336
{
337+
size_t items_processed = 0;
312338
elementClassSize = mono_class_array_element_size (element_class);
313339
for (i = 0; i < array_length; i++)
314340
{
315341
MonoObject* object = (MonoObject*)mono_array_addr_with_size (array, elementClassSize, i);
316-
mono_traverse_object_internal (object, 1, element_class, state);
342+
if (mono_traverse_object_internal (object, 1, element_class, state))
343+
items_processed++;
317344

318-
// Add 128 objects at a time and then traverse, 64 seems not be enough
319-
if( ((i+1) & 127) == 0)
345+
if(should_traverse_objects (items_processed, state->traverse_depth))
320346
mono_traverse_objects(state);
321347
}
322348
}
323349
else
324350
{
351+
size_t items_processed = 0;
325352
for (i = 0; i < array_length; i++)
326353
{
327354
MonoObject* val = mono_array_get(array, MonoObject*, i);
328-
mono_add_process_object(val, state);
355+
if (mono_add_process_object (val, state))
356+
items_processed++;
329357

330-
// Add 128 objects at a time and then traverse, 64 seems not be enough
331-
if( ((i+1) & 127) == 0)
358+
if (should_traverse_objects (items_processed, state->traverse_depth))
332359
mono_traverse_objects(state);
333360
}
334361
}
@@ -541,13 +568,14 @@ LivenessState* mono_unity_liveness_allocate_struct (MonoClass* filter, guint max
541568
// process_array. array that contains the objcets that should be processed. this should run depth first to reduce memory usage
542569
// if all_objects run out of space, run through list, add objects that match the filter, clear bit in vtable and then clear the array.
543570

544-
state = g_new(LivenessState, 1);
571+
state = g_new0(LivenessState, 1);
545572
max_count = max_count < 1000 ? 1000 : max_count;
546573
state->all_objects = array_create_and_initialize(max_count*4);
547574
state->process_array = array_create_and_initialize (max_count);
548575

549576
state->first_index_in_all_objects = 0;
550577
state->filter = filter;
578+
state->traverse_depth = 0;
551579

552580
state->callback_userdata = callback_userdata;
553581
state->filter_callback = callback;

0 commit comments

Comments
 (0)