Merge pull request #2698 from esdrubal/iosxmlarray
[mono.git] / mono / utils / checked-build.c
1 /*
2  * checked-build.c: Expensive asserts used when mono is built with --with-checked-build=yes
3  *
4  * Author:
5  *      Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * (C) 2015 Xamarin
8  */
9 #include <config.h>
10
11 #ifdef ENABLE_CHECKED_BUILD
12
13 #include <mono/utils/checked-build.h>
14 #include <mono/utils/mono-threads.h>
15 #include <mono/utils/mono-tls.h>
16 #include <mono/metadata/mempool.h>
17 #include <mono/metadata/metadata-internals.h>
18 #include <mono/metadata/image-internals.h>
19 #include <mono/metadata/class-internals.h>
20 #include <mono/metadata/reflection-internals.h>
21 #include <glib.h>
22
23 #ifdef HAVE_BACKTRACE_SYMBOLS
24 #include <execinfo.h>
25 #endif
26
27 // Selective-enable support
28
29 // Returns true for check modes which are allowed by both the current DISABLE_ macros and the MONO_CHECK_MODE env var.
30 // Argument may be a bitmask; if so, result is true if at least one specified mode is enabled.
31 mono_bool
32 mono_check_mode_enabled (MonoCheckMode query)
33 {
34         static MonoCheckMode check_mode = MONO_CHECK_MODE_UNKNOWN;
35         if (G_UNLIKELY (check_mode == MONO_CHECK_MODE_UNKNOWN))
36         {
37                 MonoCheckMode env_check_mode = MONO_CHECK_MODE_NONE;
38                 const gchar *env_string = g_getenv ("MONO_CHECK_MODE");
39
40                 if (env_string)
41                 {
42                         gchar **env_split = g_strsplit (env_string, ",", 0);
43                         for (gchar **env_component = env_split; *env_component; env_component++)
44                         {
45                                 mono_bool check_all = g_str_equal (*env_component, "all");
46 #ifdef ENABLE_CHECKED_BUILD_GC
47                                 if (check_all || g_str_equal (*env_component, "gc"))
48                                         env_check_mode |= MONO_CHECK_MODE_GC;
49 #endif
50 #ifdef ENABLE_CHECKED_BUILD_METADATA
51                                 if (check_all || g_str_equal (*env_component, "metadata"))
52                                         env_check_mode |= MONO_CHECK_MODE_METADATA;
53 #endif
54 #ifdef ENABLE_CHECKED_BUILD_THREAD
55                                 if (check_all || g_str_equal (*env_component, "thread"))
56                                         env_check_mode |= MONO_CHECK_MODE_THREAD;
57 #endif
58                         }
59                         g_strfreev (env_split);
60                 }
61
62                 check_mode = env_check_mode;
63         }
64         return check_mode & query;
65 }
66
67 static int
68 mono_check_transition_limit (void)
69 {
70         static int transition_limit = -1;
71         if (transition_limit < 0) {
72                 const gchar *env_string = g_getenv ("MONO_CHECK_THREAD_TRANSITION_HISTORY");
73                 if (env_string)
74                         transition_limit = atoi (env_string);
75                 else
76                         transition_limit = 3;
77         }
78         return transition_limit;
79 }
80
81 typedef struct {
82         GPtrArray *transitions;
83         guint32 in_gc_critical_region;
84 } CheckState;
85
86 static MonoNativeTlsKey thread_status;
87
88 void
89 checked_build_init (void)
90 {
91         // Init state for get_state, which can be called either by gc or thread mode
92         if (mono_check_mode_enabled (MONO_CHECK_MODE_GC | MONO_CHECK_MODE_THREAD))
93                 mono_native_tls_alloc (&thread_status, NULL);
94 }
95
96 static CheckState*
97 get_state (void)
98 {
99         CheckState *state = mono_native_tls_get_value (thread_status);
100         if (!state) {
101                 state = g_new0 (CheckState, 1);
102                 state->transitions = g_ptr_array_new ();
103                 mono_native_tls_set_value (thread_status, state);
104         }
105
106         return state;
107 }
108
109 #ifdef ENABLE_CHECKED_BUILD_THREAD
110
111 #define MAX_NATIVE_BT 6
112 #define MAX_NATIVE_BT_PROBE (MAX_NATIVE_BT + 5)
113 #define MAX_TRANSITIONS (mono_check_transition_limit ())
114
115 #ifdef HAVE_BACKTRACE_SYMBOLS
116
117 //XXX We should collect just the IPs and lazily symbolificate them.
118 static int
119 collect_backtrace (gpointer out_data[])
120 {
121         return backtrace (out_data, MAX_NATIVE_BT_PROBE);
122 }
123
124 static char*
125 translate_backtrace (gpointer native_trace[], int size)
126 {
127         char **names = backtrace_symbols (native_trace, size);
128         GString* bt = g_string_sized_new (100);
129
130         int i, j = -1;
131
132         //Figure out the cut point of useless backtraces
133         //We'll skip up to the caller of checked_build_thread_transition
134         for (i = 0; i < size; ++i) {
135                 if (strstr (names [i], "checked_build_thread_transition")) {
136                         j = i + 1;
137                         break;
138                 }
139         }
140
141         if (j == -1)
142                 j = 0;
143         for (i = j; i < size; ++i) {
144                 if (i - j <= MAX_NATIVE_BT)
145                         g_string_append_printf (bt, "\tat %s\n", names [i]);
146         }
147
148         free (names);
149         return g_string_free (bt, FALSE);
150 }
151
152 #else
153
154 static int
155 collect_backtrace (gpointer out_data[])
156 {
157         return 0;
158 }
159
160 static char*
161 translate_backtrace (gpointer native_trace[], int size)
162 {
163         return g_strdup ("\tno backtrace available\n");
164 }
165
166 #endif
167
168 typedef struct {
169         const char *name;
170         int from_state, next_state, suspend_count, suspend_count_delta, size;
171         gpointer backtrace [MAX_NATIVE_BT_PROBE];
172 } ThreadTransition;
173
174 static void
175 free_transition (ThreadTransition *t)
176 {
177         g_free (t);
178 }
179
180 void
181 checked_build_thread_transition (const char *transition, void *info, int from_state, int suspend_count, int next_state, int suspend_count_delta)
182 {
183         if (!mono_check_mode_enabled (MONO_CHECK_MODE_THREAD))
184                 return;
185
186         MonoThreadInfo *cur = mono_thread_info_current_unchecked ();
187         CheckState *state = get_state ();
188         /* We currently don't record external changes as those are hard to reason about. */
189         if (cur != info)
190                 return;
191
192         if (state->transitions->len >= MAX_TRANSITIONS)
193                 free_transition (g_ptr_array_remove_index (state->transitions, 0));
194
195         ThreadTransition *t = g_new0 (ThreadTransition, 1);
196         t->name = transition;
197         t->from_state = from_state;
198         t->next_state = next_state;
199         t->suspend_count = suspend_count;
200         t->suspend_count_delta = suspend_count_delta;
201         t->size = collect_backtrace (t->backtrace);
202         g_ptr_array_add (state->transitions, t);
203 }
204
205 void
206 mono_fatal_with_history (const char *msg, ...)
207 {
208         int i;
209         GString* err = g_string_sized_new (100);
210
211         g_string_append_printf (err, "Assertion failure in thread %p due to: ", mono_native_thread_id_get ());
212
213         va_list args;
214         va_start (args, msg);
215         g_string_append_vprintf (err, msg, args);
216         va_end (args);
217
218         if (mono_check_mode_enabled (MONO_CHECK_MODE_THREAD))
219         {
220                 CheckState *state = get_state ();
221
222                 g_string_append_printf (err, "\nLast %d state transitions: (most recent first)\n", state->transitions->len);
223
224                 for (i = state->transitions->len - 1; i >= 0; --i) {
225                         ThreadTransition *t = state->transitions->pdata [i];
226                         char *bt = translate_backtrace (t->backtrace, t->size);
227                         g_string_append_printf (err, "[%s] %s -> %s (%d) %s%d at:\n%s",
228                                 t->name,
229                                 mono_thread_state_name (t->from_state),
230                                 mono_thread_state_name (t->next_state),
231                                 t->suspend_count,
232                                 t->suspend_count_delta > 0 ? "+" : "", //I'd like to see this sort of values: -1, 0, +1
233                                 t->suspend_count_delta,
234                                 bt);
235                         g_free (bt);
236                 }
237         }
238
239         g_error (err->str);
240         g_string_free (err, TRUE);
241 }
242
243 #endif /* defined(ENABLE_CHECKED_BUILD_THREAD) */
244
245 #ifdef ENABLE_CHECKED_BUILD_GC
246
247 void
248 assert_gc_safe_mode (void)
249 {
250         if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
251                 return;
252
253         MonoThreadInfo *cur = mono_thread_info_current ();
254         int state;
255
256         if (!cur)
257                 mono_fatal_with_history ("Expected GC Safe mode but thread is not attached");
258
259         switch (state = mono_thread_info_current_state (cur)) {
260         case STATE_BLOCKING:
261         case STATE_BLOCKING_AND_SUSPENDED:
262                 break;
263         default:
264                 mono_fatal_with_history ("Expected GC Safe mode but was in %s state", mono_thread_state_name (state));
265         }
266 }
267
268 void
269 assert_gc_unsafe_mode (void)
270 {
271         if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
272                 return;
273
274         MonoThreadInfo *cur = mono_thread_info_current ();
275         int state;
276
277         if (!cur)
278                 mono_fatal_with_history ("Expected GC Unsafe mode but thread is not attached");
279
280         switch (state = mono_thread_info_current_state (cur)) {
281         case STATE_RUNNING:
282         case STATE_ASYNC_SUSPEND_REQUESTED:
283         case STATE_SELF_SUSPEND_REQUESTED:
284                 break;
285         default:
286                 mono_fatal_with_history ("Expected GC Unsafe mode but was in %s state", mono_thread_state_name (state));
287         }
288 }
289
290 void
291 assert_gc_neutral_mode (void)
292 {
293         if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
294                 return;
295
296         MonoThreadInfo *cur = mono_thread_info_current ();
297         int state;
298
299         if (!cur)
300                 mono_fatal_with_history ("Expected GC Neutral mode but thread is not attached");
301
302         switch (state = mono_thread_info_current_state (cur)) {
303         case STATE_RUNNING:
304         case STATE_ASYNC_SUSPEND_REQUESTED:
305         case STATE_SELF_SUSPEND_REQUESTED:
306         case STATE_BLOCKING:
307         case STATE_BLOCKING_AND_SUSPENDED:
308                 break;
309         default:
310                 mono_fatal_with_history ("Expected GC Neutral mode but was in %s state", mono_thread_state_name (state));
311         }
312 }
313
314 void *
315 critical_gc_region_begin(void)
316 {
317         if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
318                 return NULL;
319
320         CheckState *state = get_state ();
321         state->in_gc_critical_region++;
322         return state;
323 }
324
325
326 void
327 critical_gc_region_end(void* token)
328 {
329         if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
330                 return;
331
332         CheckState *state = get_state();
333         g_assert (state == token);
334         state->in_gc_critical_region--;
335 }
336
337 void
338 assert_not_in_gc_critical_region(void)
339 {
340         if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
341                 return;
342
343         CheckState *state = get_state();
344         if (state->in_gc_critical_region > 0) {
345                 mono_fatal_with_history("Expected GC Unsafe mode, but was in %s state", mono_thread_state_name (mono_thread_info_current_state (mono_thread_info_current ())));
346         }
347 }
348
349 void
350 assert_in_gc_critical_region (void)
351 {
352         if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
353                 return;
354
355         CheckState *state = get_state();
356         if (state->in_gc_critical_region == 0)
357                 mono_fatal_with_history("Expected GC critical region");
358 }
359
360 #endif /* defined(ENABLE_CHECKED_BUILD_GC) */
361
362 #ifdef ENABLE_CHECKED_BUILD_METADATA
363
364 // check_metadata_store et al: The goal of these functions is to verify that if there is a pointer from one mempool into
365 // another, that the pointed-to memory is protected by the reference count mechanism for MonoImages.
366 //
367 // Note: The code below catches only some kinds of failures. Failures outside its scope notably incode:
368 // * Code below absolutely assumes that no mempool is ever held as "mempool" member by more than one Image or ImageSet at once
369 // * Code below assumes reference counts never underflow (ie: if we have a pointer to something, it won't be deallocated while we're looking at it)
370 // Locking strategy is a little slapdash overall.
371
372 // Reference audit support
373 #define check_mempool_assert_message(...) \
374         g_assertion_message("Mempool reference violation: " __VA_ARGS__)
375
376 typedef struct
377 {
378         MonoImage *image;
379         MonoImageSet *image_set;
380 } MonoMemPoolOwner;
381
382 static MonoMemPoolOwner mono_mempool_no_owner = {NULL,NULL};
383
384 static gboolean
385 check_mempool_owner_eq (MonoMemPoolOwner a, MonoMemPoolOwner b)
386 {
387         return a.image == b.image && a.image_set == b.image_set;
388 }
389
390 // Say image X "references" image Y if X either contains Y in its modules field, or X’s "references" field contains an
391 // assembly whose image is Y.
392 // Say image X transitively references image Y if there is any chain of images-referencing-images which leads from X to Y.
393 // Once the mempools for two pointers have been looked up, there are four possibilities:
394
395 // Case 1. Image FROM points to Image TO: Legal if FROM transitively references TO
396
397 // We'll do a simple BFS graph search on images. For each image we visit:
398 static void
399 check_image_search (GHashTable *visited, GPtrArray *next, MonoImage *candidate, MonoImage *goal, gboolean *success)
400 {
401         // Image hasn't even been loaded-- ignore it
402         if (!candidate)
403                 return;
404
405         // Image has already been visited-- ignore it
406         if (g_hash_table_lookup_extended (visited, candidate, NULL, NULL))
407                 return;
408
409         // Image is the target-- mark success
410         if (candidate == goal)
411         {
412                 *success = TRUE;
413                 return;
414         }
415
416         // Unvisited image, queue it to have its children visited
417         g_hash_table_insert (visited, candidate, NULL);
418         g_ptr_array_add (next, candidate);
419         return;
420 }
421
422 static gboolean
423 check_image_may_reference_image(MonoImage *from, MonoImage *to)
424 {
425         if (to == from) // Shortcut
426                 return TRUE;
427
428         // Corlib is never unloaded, and all images implicitly reference it.
429         // Some images avoid explicitly referencing it as an optimization, so special-case it here.
430         if (to == mono_defaults.corlib)
431                 return TRUE;
432
433         // Non-dynamic images may NEVER reference dynamic images
434         if (to->dynamic && !from->dynamic)
435                 return FALSE;
436
437         // FIXME: We currently give a dynamic images a pass on the reference rules.
438         // Dynamic images may ALWAYS reference non-dynamic images.
439         // We allow this because the dynamic image code is known "messy", and in theory it is already
440         // protected because dynamic images can only reference classes their assembly has retained.
441         // However, long term, we should make this rigorous.
442         if (from->dynamic && !to->dynamic)
443                 return TRUE;
444
445         gboolean success = FALSE;
446
447         // Images to inspect on this pass, images to inspect on the next pass
448         GPtrArray *current = g_ptr_array_sized_new (1), *next = g_ptr_array_new ();
449
450         // Because in practice the image graph contains cycles, we must track which images we've visited
451         GHashTable *visited = g_hash_table_new (NULL, NULL);
452
453         #define CHECK_IMAGE_VISIT(i) check_image_search (visited, next, (i), to, &success)
454
455         CHECK_IMAGE_VISIT (from); // Initially "next" contains only from node
456
457         // For each pass exhaust the "to check" queue while filling up the "check next" queue
458         while (!success && next->len > 0) // Halt on success or when out of nodes to process
459         {
460                 // Swap "current" and "next" and clear next
461                 GPtrArray *temp = current;
462                 current = next;
463                 next = temp;
464                 g_ptr_array_set_size (next, 0);
465
466                 int current_idx;
467                 for(current_idx = 0; current_idx < current->len; current_idx++)
468                 {
469                         MonoImage *checking = g_ptr_array_index (current, current_idx); // CAST?
470
471                         mono_image_lock (checking);
472
473                         // For each queued image visit all directly referenced images
474                         int inner_idx;
475
476                         for (inner_idx = 0; !success && inner_idx < checking->module_count; inner_idx++)
477                         {
478                                 CHECK_IMAGE_VISIT (checking->modules[inner_idx]);
479                         }
480
481                         for (inner_idx = 0; !success && inner_idx < checking->nreferences; inner_idx++)
482                         {
483                                 // References are lazy-loaded and thus allowed to be NULL.
484                                 // If they are NULL, we don't care about them for this search, because they haven't impacted ref_count yet.
485                                 if (checking->references[inner_idx])
486                                 {
487                                         CHECK_IMAGE_VISIT (checking->references[inner_idx]->image);
488                                 }
489                         }
490
491                         mono_image_unlock (checking);
492                 }
493         }
494
495         g_ptr_array_free (current, TRUE); g_ptr_array_free (next, TRUE); g_hash_table_destroy (visited);
496
497         return success;
498 }
499
500 // Case 2. ImageSet FROM points to Image TO: One of FROM's "images" either is, or transitively references, TO.
501 static gboolean
502 check_image_set_may_reference_image (MonoImageSet *from, MonoImage *to)
503 {
504         // See above-- All images implicitly reference corlib
505         if (to == mono_defaults.corlib)
506                 return TRUE;
507
508         int idx;
509         gboolean success = FALSE;
510         mono_image_set_lock (from);
511         for (idx = 0; !success && idx < from->nimages; idx++)
512         {
513                 if (check_image_may_reference_image (from->images[idx], to))
514                         success = TRUE;
515         }
516         mono_image_set_unlock (from);
517
518         return success; // No satisfying image found in from->images
519 }
520
521 // Case 3. ImageSet FROM points to ImageSet TO: The images in TO are a strict subset of FROM (no transitive relationship is important here)
522 static gboolean
523 check_image_set_may_reference_image_set (MonoImageSet *from, MonoImageSet *to)
524 {
525         if (to == from)
526                 return TRUE;
527
528         gboolean valid = TRUE; // Until proven otherwise
529
530         mono_image_set_lock (from); mono_image_set_lock (to);
531
532         int to_idx, from_idx;
533         for (to_idx = 0; valid && to_idx < to->nimages; to_idx++)
534         {
535                 gboolean seen = FALSE;
536
537                 // If TO set includes corlib, the FROM set may
538                 // implicitly reference corlib, even if it's not
539                 // present in the set explicitly.
540                 if (to->images[to_idx] == mono_defaults.corlib)
541                         seen = TRUE;
542
543                 // For each item in to->images, scan over from->images looking for it.
544                 for (from_idx = 0; !seen && from_idx < from->nimages; from_idx++)
545                 {
546                         if (to->images[to_idx] == from->images[from_idx])
547                                 seen = TRUE;
548                 }
549
550                 // If the to->images item is not found in from->images, the subset check has failed
551                 if (!seen)
552                         valid = FALSE;
553         }
554
555         mono_image_set_unlock (from); mono_image_set_unlock (to);
556
557         return valid; // All items in "to" were found in "from"
558 }
559
560 // Case 4. Image FROM points to ImageSet TO: FROM transitively references *ALL* of the “images” listed in TO
561 static gboolean
562 check_image_may_reference_image_set (MonoImage *from, MonoImageSet *to)
563 {
564         if (to->nimages == 0) // Malformed image_set
565                 return FALSE;
566
567         gboolean valid = TRUE;
568
569         mono_image_set_lock (to);
570         int idx;
571         for (idx = 0; valid && idx < to->nimages; idx++)
572         {
573                 if (!check_image_may_reference_image (from, to->images[idx]))
574                         valid = FALSE;
575         }
576         mono_image_set_unlock (to);
577
578         return valid; // All images in to->images checked out
579 }
580
581 // Small helper-- get a descriptive string for a MonoMemPoolOwner
582 // Callers are obligated to free buffer with g_free after use
583 static const char *
584 check_mempool_owner_name (MonoMemPoolOwner owner)
585 {
586         GString *result = g_string_new (NULL);
587         if (owner.image)
588         {
589                 if (owner.image->dynamic)
590                         g_string_append (result, "(Dynamic)");
591                 g_string_append (result, owner.image->name);
592         }
593         else if (owner.image_set)
594         {
595                 char *temp = mono_image_set_description (owner.image_set);
596                 g_string_append (result, "(Image set)");
597                 g_string_append (result, temp);
598                 g_free (temp);
599         }
600         else
601         {
602                 g_string_append (result, "(Non-image memory)");
603         }
604         return g_string_free (result, FALSE);
605 }
606
607 // Helper -- surf various image-locating functions looking for the owner of this pointer
608 static MonoMemPoolOwner
609 mono_find_mempool_owner (void *ptr)
610 {
611         MonoMemPoolOwner owner = mono_mempool_no_owner;
612
613         owner.image = mono_find_image_owner (ptr);
614         if (!check_mempool_owner_eq (owner, mono_mempool_no_owner))
615                 return owner;
616
617         owner.image_set = mono_find_image_set_owner (ptr);
618         if (!check_mempool_owner_eq (owner, mono_mempool_no_owner))
619                 return owner;
620
621         owner.image = mono_find_dynamic_image_owner (ptr);
622
623         return owner;
624 }
625
626 // Actually perform reference audit
627 static void
628 check_mempool_may_reference_mempool (void *from_ptr, void *to_ptr, gboolean require_local)
629 {
630         if (!mono_check_mode_enabled (MONO_CHECK_MODE_METADATA))
631                 return;
632
633         // Null pointers are OK
634         if (!to_ptr)
635                 return;
636
637         MonoMemPoolOwner from = mono_find_mempool_owner (from_ptr), to = mono_find_mempool_owner (to_ptr);
638
639         if (require_local)
640         {
641                 if (!check_mempool_owner_eq (from,to))
642                         check_mempool_assert_message ("Pointer in image %s should have been internal, but instead pointed to image %s", check_mempool_owner_name (from), check_mempool_owner_name (to));
643         }
644
645         // Writing into unknown mempool
646         else if (check_mempool_owner_eq (from, mono_mempool_no_owner))
647         {
648                 check_mempool_assert_message ("Non-image memory attempting to write pointer to image %s", check_mempool_owner_name (to));
649         }
650
651         // Reading from unknown mempool
652         else if (check_mempool_owner_eq (to, mono_mempool_no_owner))
653         {
654                 check_mempool_assert_message ("Attempting to write pointer from image %s to non-image memory", check_mempool_owner_name (from));
655         }
656
657         // Split out the four cases described above:
658         else if (from.image && to.image)
659         {
660                 if (!check_image_may_reference_image (from.image, to.image))
661                         check_mempool_assert_message ("Image %s tried to point to image %s, but does not retain a reference", check_mempool_owner_name (from), check_mempool_owner_name (to));
662         }
663
664         else if (from.image && to.image_set)
665         {
666                 if (!check_image_may_reference_image_set (from.image, to.image_set))
667                         check_mempool_assert_message ("Image %s tried to point to image set %s, but does not retain a reference", check_mempool_owner_name (from), check_mempool_owner_name (to));
668         }
669
670         else if (from.image_set && to.image_set)
671         {
672                 if (!check_image_set_may_reference_image_set (from.image_set, to.image_set))
673                         check_mempool_assert_message ("Image set %s tried to point to image set %s, but does not retain a reference", check_mempool_owner_name (from), check_mempool_owner_name (to));
674         }
675
676         else if (from.image_set && to.image)
677         {
678                 if (!check_image_set_may_reference_image (from.image_set, to.image))
679                         check_mempool_assert_message ("Image set %s tried to point to image %s, but does not retain a reference", check_mempool_owner_name (from), check_mempool_owner_name (to));
680         }
681
682         else
683         {
684                 check_mempool_assert_message ("Internal logic error: Unreachable code");
685         }
686 }
687
688 void
689 check_metadata_store (void *from, void *to)
690 {
691     check_mempool_may_reference_mempool (from, to, FALSE);
692 }
693
694 void
695 check_metadata_store_local (void *from, void *to)
696 {
697     check_mempool_may_reference_mempool (from, to, TRUE);
698 }
699
700 #endif /* defined(ENABLE_CHECKED_BUILD_METADATA) */
701
702 #endif /* ENABLE_CHECKED_BUILD */