-
Notifications
You must be signed in to change notification settings - Fork 571
/
Copy pathsynch.c
2287 lines (2169 loc) · 100 KB
/
synch.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* **********************************************************
* Copyright (c) 2012-2020 Google, Inc. All rights reserved.
* Copyright (c) 2008-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/*
* thread.c - thread synchronization
*/
#include "globals.h"
#include "synch.h"
#include "instrument.h" /* is_in_client_lib() */
#include "hotpatch.h" /* hotp_only_in_tramp() */
#include "fragment.h" /* get_at_syscall() */
#include "fcache.h" /* in_fcache() */
#include "translate.h"
#include "native_exec.h"
extern vm_area_vector_t *fcache_unit_areas; /* from fcache.c */
static bool started_detach = false; /* set before synchall */
bool doing_detach = false; /* set after synchall */
static void
synch_thread_yield(void);
/* Thread-local data
*/
typedef struct _thread_synch_data_t {
/* the following three fields are used to synchronize for detach, suspend
* thread, terminate thread, terminate process */
/* synch_lock and pending_synch_count act as a semaphore */
/* for check_wait_at_safe_spot() must use a spin_mutex_t */
spin_mutex_t *synch_lock;
/* we allow pending_synch_count to be read without holding the synch_lock
* so all updates should be ATOMIC as well as holding the lock */
int pending_synch_count;
/* To guarantee that the thread really has this permission you need to hold the
* synch_lock when you read this value. If the target thread is suspended, use a
* trylock, as it could have been suspended while holding synch_lock (i#2805).
*/
thread_synch_permission_t synch_perm;
/* Only valid while holding all_threads_synch_lock and thread_initexit_lock. Set
* to whether synch_with_all_threads was successful in synching this thread.
*/
bool synch_with_success;
/* Case 10101: allows threads waiting_at_safe_spot() to set their own
* contexts. This use sometimes requires a full os-specific context, which
* we hide behind a generic pointer and a size.
*/
priv_mcontext_t *set_mcontext;
void *set_context;
size_t set_context_size;
#ifdef X64
/* PR 263338: we have to pad for alignment */
byte *set_context_alloc;
#endif
} thread_synch_data_t;
/* This lock prevents more than one thread from being in the synch_with_all_
* threads method body at the same time (which would lead to deadlock as they
* tried to synchronize with each other)
*/
DECLARE_CXTSWPROT_VAR(mutex_t all_threads_synch_lock,
INIT_LOCK_FREE(all_threads_synch_lock));
/* pass either mc or both cxt and cxt_size */
static void
free_setcontext(priv_mcontext_t *mc, void *cxt, size_t cxt_size _IF_X64(byte *cxt_alloc))
{
if (mc != NULL) {
ASSERT(cxt == NULL);
global_heap_free(mc, sizeof(*mc) HEAPACCT(ACCT_OTHER));
} else if (cxt != NULL) {
ASSERT(cxt_size > 0);
global_heap_free(IF_X64_ELSE(cxt_alloc, cxt), cxt_size HEAPACCT(ACCT_OTHER));
}
}
static void
synch_thread_free_setcontext(thread_synch_data_t *tsd)
{
free_setcontext(tsd->set_mcontext, tsd->set_context,
tsd->set_context_size _IF_X64(tsd->set_context_alloc));
tsd->set_mcontext = NULL;
tsd->set_context = NULL;
}
void
synch_init(void)
{
}
void
synch_exit(void)
{
ASSERT(uninit_thread_count == 0);
DELETE_LOCK(all_threads_synch_lock);
}
void
synch_thread_init(dcontext_t *dcontext)
{
thread_synch_data_t *tsd = (thread_synch_data_t *)heap_alloc(
dcontext, sizeof(thread_synch_data_t) HEAPACCT(ACCT_OTHER));
dcontext->synch_field = (void *)tsd;
tsd->pending_synch_count = 0;
tsd->synch_perm = THREAD_SYNCH_NONE;
tsd->synch_with_success = false;
tsd->set_mcontext = NULL;
tsd->set_context = NULL;
/* the synch_lock is in unprotected memory so that check_wait_at_safe_spot
* can call the EXITING_DR hook before releasing it */
tsd->synch_lock = HEAP_TYPE_ALLOC(dcontext, spin_mutex_t, ACCT_OTHER, UNPROTECTED);
ASSIGN_INIT_SPINMUTEX_FREE(*tsd->synch_lock, synch_lock);
}
void
synch_thread_exit(dcontext_t *dcontext)
{
thread_synch_data_t *tsd = (thread_synch_data_t *)dcontext->synch_field;
/* Could be waiting at safe spot when we detach or exit */
synch_thread_free_setcontext(tsd);
DELETE_SPINMUTEX(*tsd->synch_lock);
/* Note that we do need to free this in non-debug builds since, despite
* appearances, UNPROTECTED_LOCAL is acutally allocated on a global
* heap. */
HEAP_TYPE_FREE(dcontext, tsd->synch_lock, spin_mutex_t, ACCT_OTHER, UNPROTECTED);
#ifdef DEBUG
/* for non-debug we do fast exit path and don't free local heap */
/* clean up tsd fields here */
heap_free(dcontext, tsd, sizeof(thread_synch_data_t) HEAPACCT(ACCT_OTHER));
#endif
}
/* Check for a no-xfer permission. Currently used only for case 6821,
* where we need to distinguish three groups: unsafe (wait for safe
* point), safe and translatable, and safe but not translatable.
*/
bool
thread_synch_state_no_xfer(dcontext_t *dcontext)
{
thread_synch_data_t *tsd = (thread_synch_data_t *)dcontext->synch_field;
/* We use a trylock in case the thread is suspended holding synch_lock (i#2805). */
if (spinmutex_trylock(tsd->synch_lock)) {
bool res = (tsd->synch_perm == THREAD_SYNCH_NO_LOCKS_NO_XFER ||
tsd->synch_perm == THREAD_SYNCH_VALID_MCONTEXT_NO_XFER);
spinmutex_unlock(tsd->synch_lock);
return res;
}
return false;
}
bool
thread_synch_check_state(dcontext_t *dcontext, thread_synch_permission_t desired_perm)
{
thread_synch_data_t *tsd = (thread_synch_data_t *)dcontext->synch_field;
/* We support calling this routine from our signal handler when it has interrupted
* DR and might be holding tsd->synch_lock or other locks.
* We first check synch_perm w/o a lock and if it's not at least
* THREAD_SYNCH_NO_LOCKS we do not attempt to grab synch_lock (we'd hit rank order
* violations). If that check passes, the only problematic lock is if we already
* hold synch_lock, so we use test and trylocks there.
*/
if (desired_perm < THREAD_SYNCH_NO_LOCKS) {
ASSERT(desired_perm == THREAD_SYNCH_NONE);
return true;
}
if (!THREAD_SYNCH_SAFE(tsd->synch_perm, desired_perm))
return false;
/* barrier to keep the 1st check above on this side of the lock below */
#ifdef WINDOWS
MemoryBarrier();
#else
__asm__ __volatile__("" : : : "memory");
#endif
/* We use a trylock in case the thread is suspended holding synch_lock (i#2805).
* We start with testlock to avoid recursive lock assertions.
*/
if (!spinmutex_testlock(tsd->synch_lock) && spinmutex_trylock(tsd->synch_lock)) {
bool res = THREAD_SYNCH_SAFE(tsd->synch_perm, desired_perm);
spinmutex_unlock(tsd->synch_lock);
return res;
}
return false;
}
/* Only valid while holding all_threads_synch_lock and thread_initexit_lock. Set to
* whether synch_with_all_threads was successful in synching this thread.
* Cannot be called when THREAD_SYNCH_*_AND_CLEANED was requested as the
* thread-local memory will be freed on success!
*/
bool
thread_synch_successful(thread_record_t *tr)
{
thread_synch_data_t *tsd;
ASSERT(tr != NULL && tr->dcontext != NULL);
ASSERT_OWN_MUTEX(true, &all_threads_synch_lock);
ASSERT_OWN_MUTEX(true, &thread_initexit_lock);
tsd = (thread_synch_data_t *)tr->dcontext->synch_field;
return tsd->synch_with_success;
}
#ifdef UNIX
/* i#2659: the kernel is now doing auto-restart so we have to check for the
* pc being at the syscall.
*/
static bool
is_after_or_restarted_do_syscall(dcontext_t *dcontext, app_pc pc, bool check_vsyscall)
{
if (is_after_do_syscall_addr(dcontext, pc))
return true;
if (check_vsyscall && pc == vsyscall_sysenter_return_pc)
return true;
if (!get_at_syscall(dcontext)) /* rule out having just reached the syscall */
return false;
int syslen = syscall_instr_length(dr_get_isa_mode(dcontext));
if (is_after_do_syscall_addr(dcontext, pc + syslen))
return true;
if (check_vsyscall && pc + syslen == vsyscall_sysenter_return_pc)
return true;
return false;
}
#endif
bool
is_at_do_syscall(dcontext_t *dcontext, app_pc pc, byte *esp)
{
app_pc buf[2];
bool res = d_r_safe_read(esp, sizeof(buf), buf);
if (!res) {
ASSERT(res); /* we expect the stack to always be readable */
return false;
}
if (does_syscall_ret_to_callsite()) {
#ifdef WINDOWS
if (get_syscall_method() == SYSCALL_METHOD_INT && DYNAMO_OPTION(sygate_int)) {
return (pc == after_do_syscall_addr(dcontext) &&
buf[0] == after_do_syscall_code(dcontext));
} else {
return pc == after_do_syscall_code(dcontext);
}
#else
return is_after_or_restarted_do_syscall(dcontext, pc, false /*!vsys*/);
#endif
} else if (get_syscall_method() == SYSCALL_METHOD_SYSENTER) {
#ifdef WINDOWS
if (pc == vsyscall_after_syscall) {
if (DYNAMO_OPTION(sygate_sysenter))
return buf[1] == after_do_syscall_code(dcontext);
else
return buf[0] == after_do_syscall_code(dcontext);
} else {
/* not at a system call, could still have tos match after_do_syscall
* either by chance or because we leak that value on the apps stack
* (a non transparency) */
ASSERT_CURIOSITY(buf[0] != after_do_syscall_code(dcontext));
return false;
}
#else
/* Even when the main syscall method is sysenter, we also have a
* do_int_syscall and do_clone_syscall that use int, so check both.
* Note that we don't modify the stack, so once we do sysenter syscalls
* inlined in the cache (PR 288101) we'll need some mechanism to
* distinguish those: but for now if a sysenter instruction is used it
* has to be do_syscall since DR's own syscalls are ints.
*/
return is_after_or_restarted_do_syscall(dcontext, pc, true /*vsys*/);
#endif
}
/* we can reach here w/ a fault prior to 1st syscall on Linux */
IF_WINDOWS(ASSERT_NOT_REACHED());
return false;
}
/* Helper function for at_safe_spot(). Note state for client-owned threads isn't
* considered valid since it may be holding client locks and doesn't correspond to
* an actual app state. Caller should handle client-owned threads appropriately. */
static bool
is_native_thread_state_valid(dcontext_t *dcontext, app_pc pc, byte *esp)
{
/* ref case 3675, the assumption is that if we aren't executing
* out of dr memory and our stack isn't in dr memory (to disambiguate
* pc in kernel32, ntdll etc.) then the app has a valid native context.
* However, we can't call is_dynamo_address() as it (and its children)
* grab too many different locks, all of which we would have to check
* here in the same manner as fcache_unit_areas.lock in at_safe_spot(). So
* instead we just check the pc for the dr dll, interception code, and
* do_syscall regions and check the stack against the thread's dr stack
* and the d_r_initstack, all of which we can do without grabbing any locks.
* That should be sufficient at this point, FIXME try to use something
* like is_dynamo_address() to make this more maintainable */
/* For sysenter system calls we also have to check the top of the stack
* for the after_do_syscall_address to catch the do_syscall @ syscall
* itself case. */
ASSERT(esp != NULL);
ASSERT(is_thread_currently_native(dcontext->thread_record));
#ifdef WINDOWS
if (pc == (app_pc)thread_attach_takeover) {
/* We are trying to take over this thread but it has not yet been
* scheduled. It was native, and can't hold any DR locks.
*/
return true;
}
#endif
return (!is_in_dynamo_dll(pc) &&
IF_WINDOWS(!is_part_of_interception(pc) &&)(
!in_generated_routine(dcontext, pc) ||
/* we allow native thread to be at do_syscall - for int syscalls the pc
* (syscall return point) will be in do_syscall (so in generated routine)
* xref case 9333 */
is_at_do_syscall(dcontext, pc, esp)) &&
!is_on_initstack(esp) && !is_on_dstack(dcontext, esp) &&
IF_CLIENT_INTERFACE(!is_in_client_lib(pc) &&)
/* xref PR 200067 & 222812 on client-owned native threads */
IF_CLIENT_INTERFACE(!IS_CLIENT_THREAD(dcontext) &&)
#ifdef HOT_PATCHING_INTERFACE
/* Shouldn't be in the middle of executing a hotp_only patch. The
* check for being in hotp_dll is DR_WHERE_HOTPATCH because the patch can
* change esp.
*/
(dcontext->whereami != DR_WHERE_HOTPATCH &&
/* dynamo dll check has been done */
!hotp_only_in_tramp(pc)) &&
#endif
true /* no effect, simplifies ifdef handling with && above */
);
}
/* Translates the context mcontext for the given thread trec. If
* restore_memory is true, also restores any memory values that were
* shifted (primarily due to clients). If restore_memory is true, the
* caller should always relocate the translated thread, as it may not
* execute properly if left at its current location (it could be in the
* middle of client code in the cache).
* If recreate_app_state() is called, f will be passed through to it.
*
* Like any instance where a thread_record_t is used by a thread other than its
* owner, the caller must hold the thread_initexit_lock to ensure that it
* remains valid.
* Requires thread trec is at_safe_spot().
*/
bool
translate_mcontext(thread_record_t *trec, priv_mcontext_t *mcontext, bool restore_memory,
fragment_t *f)
{
thread_synch_data_t *tsd = (thread_synch_data_t *)trec->dcontext->synch_field;
bool res;
recreate_success_t success;
bool native_translate = false;
ASSERT(tsd->pending_synch_count >= 0);
/* check if native thread */
if (is_thread_currently_native(trec)) {
/* running natively, no need to translate unless at do_syscall for an
* intercepted-via-trampoline syscall which we allow now for case 9333 */
#ifdef CLIENT_INTERFACE
if (IS_CLIENT_THREAD(trec->dcontext)) {
/* don't need to translate anything */
LOG(THREAD_GET, LOG_SYNCH, 1,
"translate context, thread " TIDFMT " is client "
"thread, no translation needed\n",
trec->id);
return true;
}
#endif
if (is_native_thread_state_valid(trec->dcontext, (app_pc)mcontext->pc,
(byte *)mcontext->xsp)) {
#ifdef WINDOWS
if ((app_pc)mcontext->pc == (app_pc)thread_attach_takeover) {
LOG(THREAD_GET, LOG_SYNCH, 1,
"translate context, thread " TIDFMT " at "
"takeover point\n",
trec->id);
thread_attach_translate(trec->dcontext, mcontext, restore_memory);
return true;
}
#endif
if (is_at_do_syscall(trec->dcontext, (app_pc)mcontext->pc,
(byte *)mcontext->xsp)) {
LOG(THREAD_GET, LOG_SYNCH, 1,
"translate context, thread " TIDFMT " running "
"natively, at do_syscall so translation needed\n",
trec->id);
native_translate = true;
} else {
LOG(THREAD_GET, LOG_SYNCH, 1,
"translate context, thread " TIDFMT " running "
"natively, no translation needed\n",
trec->id);
return true;
}
} else {
/* now that do_syscall is a safe spot for native threads we shouldn't get
* here for get context on self, FIXME - is however possible to get here
* via get_context on unsuspended thread (result of which is technically
* undefined according to MS), see get_context post sys comments
* (should prob. synch there in which case can assert here) */
ASSERT(trec->id != d_r_get_thread_id());
ASSERT_CURIOSITY(false &&
"translate failure, likely get context on "
"unsuspended native thread");
/* we'll just try to translate and hope for the best */
native_translate = true;
}
}
if (!native_translate) {
/* check if waiting at a good spot */
spinmutex_lock(tsd->synch_lock);
res = THREAD_SYNCH_SAFE(tsd->synch_perm, THREAD_SYNCH_VALID_MCONTEXT);
spinmutex_unlock(tsd->synch_lock);
if (res) {
LOG(THREAD_GET, LOG_SYNCH, 1,
"translate context, thread " TIDFMT " waiting at "
"valid mcontext point, copying over\n",
trec->id);
DOLOG(2, LOG_SYNCH, {
LOG(THREAD_GET, LOG_SYNCH, 2, "Thread State\n");
dump_mcontext(get_mcontext(trec->dcontext), THREAD_GET, DUMP_NOT_XML);
});
*mcontext = *get_mcontext(trec->dcontext);
#ifdef CLIENT_INTERFACE
if (dr_xl8_hook_exists()) {
if (!instrument_restore_nonfcache_state(trec->dcontext, true, mcontext))
return false;
}
#endif
return true;
}
}
/* In case 4148 we see a thread calling NtGetContextThread on itself, which
* is undefined according to MS but it does get the syscall address, so it's
* fine with us. For other threads the app shouldn't be asking about them
* unless they're suspended, and the same goes for us.
*/
ASSERT_CURIOSITY(trec->dcontext->whereami == DR_WHERE_FCACHE ||
trec->dcontext->whereami == DR_WHERE_SIGNAL_HANDLER ||
native_translate || trec->id == d_r_get_thread_id());
LOG(THREAD_GET, LOG_SYNCH, 2,
"translate context, thread " TIDFMT " at pc_recreatable spot translating\n",
trec->id);
success = recreate_app_state(trec->dcontext, mcontext, restore_memory, f);
if (success != RECREATE_SUCCESS_STATE) {
/* should never happen right?
* actually it does when deciding whether can deliver a signal
* immediately (PR 213040).
*/
LOG(THREAD_GET, LOG_SYNCH, 1,
"translate context, thread " TIDFMT " unable to translate context at pc"
" = " PFX "\n",
trec->id, mcontext->pc);
SYSLOG_INTERNAL_WARNING_ONCE("failed to translate");
return false;
}
return true;
}
static bool
waiting_at_safe_spot(thread_record_t *trec, thread_synch_state_t desired_state)
{
thread_synch_data_t *tsd = (thread_synch_data_t *)trec->dcontext->synch_field;
ASSERT(tsd->pending_synch_count >= 0);
/* Check if waiting at a good spot. We can't spin in case the suspended thread is
* holding this lock (e.g., i#2805). We only need the lock to check synch_perm.
*/
if (spinmutex_trylock(tsd->synch_lock)) {
thread_synch_permission_t perm = tsd->synch_perm;
bool res = THREAD_SYNCH_SAFE(perm, desired_state);
spinmutex_unlock(tsd->synch_lock);
if (res) {
LOG(THREAD_GET, LOG_SYNCH, 2,
"thread " TIDFMT " waiting at safe spot (synch_perm=%d)\n", trec->id,
perm);
return true;
}
} else {
LOG(THREAD_GET, LOG_SYNCH, 2,
"at_safe_spot unable to get locks to test if thread " TIDFMT " is waiting "
"at safe spot\n",
trec->id);
}
return false;
}
#ifdef CLIENT_SIDELINE
static bool
should_suspend_client_thread(dcontext_t *dcontext, thread_synch_state_t desired_state)
{
/* Marking un-suspendable does not apply to cleaning/terminating */
ASSERT(IS_CLIENT_THREAD(dcontext));
return (THREAD_SYNCH_IS_CLEANED(desired_state) || dcontext->client_data->suspendable);
}
#endif
/* checks whether the thread trec is at a spot suitable for requested define
* desired_state
* Requires that trec thread is suspended */
/* Note that since trec is potentially suspended at an arbitrary point,
* this function (and any function it calls) cannot call mutex_lock as
* trec thread may hold a lock. It is ok for at_safe_spot to return false if
* it can't obtain a lock on the first try. FIXME : in the long term we may
* want to go to a locking model that stores the thread id of the owner in
* which case we can check for this situation directly
*/
bool
at_safe_spot(thread_record_t *trec, priv_mcontext_t *mc,
thread_synch_state_t desired_state)
{
bool safe = false;
if (waiting_at_safe_spot(trec, desired_state))
return true;
#ifdef ARM
if (TESTANY(EFLAGS_IT, mc->cpsr)) {
LOG(THREAD_GET, LOG_SYNCH, 2,
"thread " TIDFMT " not at safe spot (pc=" PFX " in an IT block) for %d\n",
trec->id, mc->pc, desired_state);
return false;
}
#endif
/* check if suspended at good spot */
/* FIXME: right now don't distinguish between suspend and term privileges
* even though suspend is stronger requirement, are the checks below
* sufficient */
/* FIXME : check with respect to flush, should be ok */
/* test fcache_unit_areas.lock (from fcache.c) before calling recreate_app_state
* since it calls in_fcache() which uses the lock (if we are in_fcache()
* assume other locks are not a problem (so is_dynamo_address is fine)) */
/* Right now the only dr code that ends up in the cache is our DLL main
* (which we'll reduce/get rid of with libc independence), our takeover
* from preinject return stack, and the callback.c interception code.
* FIXME : test for just these and ASSERT(!is_dynamo_address) otherwise */
if (is_thread_currently_native(trec)) {
/* thread is running native, verify is not in dr code */
#ifdef CLIENT_INTERFACE
/* We treat client-owned threads (such as a client nudge thread) as native and
* consider them safe if they are in the client_lib. Since they might own client
* locks that could block application threads from progressing, we synchronize
* with them last. FIXME - xref PR 231301 - since we can't disambiguate
* client->ntdll/gencode which is safe from client->dr->ntdll/gencode which isn't
* we disallow both. This could hurt synchronization efficiency if the client
* owned thread spent most of its execution time calling out of its lib to ntdll
* routines or generated code. */
if (IS_CLIENT_THREAD(trec->dcontext)) {
safe = (trec->dcontext->client_data->client_thread_safe_for_synch ||
is_in_client_lib(mc->pc)) &&
/* Do not cleanup/terminate a thread holding a client lock (PR 558463) */
/* Actually, don't consider a thread holding a client lock to be safe
* at all (PR 609569): client should use
* dr_client_thread_set_suspendable(false) if its thread spends a lot
* of time holding locks.
*/
(!should_suspend_client_thread(trec->dcontext, desired_state) ||
trec->dcontext->client_data->mutex_count == 0);
}
#endif
if (is_native_thread_state_valid(trec->dcontext, mc->pc, (byte *)mc->xsp)) {
safe = true;
/* We should always be able to translate a valid native state, but be
* sure to check before thread_attach_exit().
*/
ASSERT(translate_mcontext(trec, mc, false /*just querying*/, NULL));
#ifdef WINDOWS
if (mc->pc == (app_pc)thread_attach_takeover &&
THREAD_SYNCH_IS_CLEANED(desired_state)) {
/* The takeover data will be freed at process exit, but we might
* clean up a thread mid-run, so make sure we free the data.
*/
thread_attach_exit(trec->dcontext, mc);
}
#endif
}
#ifdef CLIENT_INTERFACE
} else if (desired_state == THREAD_SYNCH_TERMINATED_AND_CLEANED &&
trec->dcontext->whereami == DR_WHERE_FCACHE &&
trec->dcontext->client_data->at_safe_to_terminate_syscall) {
/* i#1420: At safe to terminate syscall like dr_sleep in a clean call.
* XXX: A thread in dr_sleep might not be safe to terminate for some
* corner cases: for example, a client may hold a lock and then go sleep,
* terminating it may mess the client up for not releasing the lock.
* We limit this to the thread being in fcache (i.e., from a clean call)
* to rule out some corner cases.
*/
safe = true;
#endif
} else if ((!WRITE_LOCK_HELD(&fcache_unit_areas->lock) &&
/* even though we only need the read lock, if our target holds it
* and a 3rd thread requests the write lock, we'll hang if we
* ask for the read lock (case 7493)
*/
!READ_LOCK_HELD(&fcache_unit_areas->lock)) &&
recreate_app_state(trec->dcontext, mc, false /*just query*/, NULL) ==
RECREATE_SUCCESS_STATE &&
/* It's ok to call is_dynamo_address even though it grabs many
* locks because recreate_app_state succeeded.
*/
!is_dynamo_address(mc->pc)) {
safe = true;
}
if (safe) {
ASSERT(trec->dcontext->whereami == DR_WHERE_FCACHE ||
trec->dcontext->whereami == DR_WHERE_SIGNAL_HANDLER ||
is_thread_currently_native(trec));
LOG(THREAD_GET, LOG_SYNCH, 2,
"thread " TIDFMT " suspended at safe spot pc=" PFX "\n", trec->id, mc->pc);
return true;
}
LOG(THREAD_GET, LOG_SYNCH, 2,
"thread " TIDFMT " not at safe spot (pc=" PFX ") for %d\n", trec->id, mc->pc,
desired_state);
return false;
}
/* a fast way to tell a thread if it should call check_wait_at_safe_spot
* if translating context would be expensive */
bool
should_wait_at_safe_spot(dcontext_t *dcontext)
{
thread_synch_data_t *tsd = (thread_synch_data_t *)dcontext->synch_field;
return (tsd->pending_synch_count != 0);
}
/* use with care! normally check_wait_at_safe_spot() should be called instead */
void
set_synch_state(dcontext_t *dcontext, thread_synch_permission_t state)
{
if (state >= THREAD_SYNCH_NO_LOCKS)
ASSERT_OWN_NO_LOCKS();
thread_synch_data_t *tsd = (thread_synch_data_t *)dcontext->synch_field;
/* We have a wart in the settings here (i#2805): a caller can set
* THREAD_SYNCH_NO_LOCKS, yet here we're acquiring locks. In fact if this thread
* is suspended in between the lock and the unset of synch_perm from
* THREAD_SYNCH_NO_LOCKS back to THREAD_SYNCH_NONE, it can cause problems. We
* have everyone who might query in such a state use a trylock and assume
* synch_perm is THREAD_SYNCH_NONE if the lock cannot be acquired.
*/
spinmutex_lock(tsd->synch_lock);
tsd->synch_perm = state;
spinmutex_unlock(tsd->synch_lock);
}
/* checks to see if any threads are waiting to synch with this one and waits
* if they are
* cur_state - a given permission define from above that describes the current
* state of the caller
* NOTE - Requires the caller is !could_be_linking (i.e. not in an
* enter_couldbelinking state)
*/
void
check_wait_at_safe_spot(dcontext_t *dcontext, thread_synch_permission_t cur_state)
{
thread_synch_data_t *tsd = (thread_synch_data_t *)dcontext->synch_field;
app_pc pc;
byte cxt[MAX(CONTEXT_HEAP_SIZE_OPAQUE, sizeof(priv_mcontext_t))];
bool set_context = false;
bool set_mcontext = false;
if (tsd->pending_synch_count == 0 || cur_state == THREAD_SYNCH_NONE)
return;
ASSERT(tsd->pending_synch_count >= 0);
pc = get_mcontext(dcontext)->pc;
LOG(THREAD, LOG_SYNCH, 2, "waiting for synch with state %d (pc " PFX ")\n", cur_state,
pc);
if (cur_state == THREAD_SYNCH_VALID_MCONTEXT) {
ASSERT(!is_dynamo_address(pc));
/* for detach must set this here and now */
IF_WINDOWS(IF_CLIENT_INTERFACE(set_last_error(dcontext->app_errno)));
}
spinmutex_lock(tsd->synch_lock);
tsd->synch_perm = cur_state;
/* Since can be killed, suspended, etc. must call the exit dr hook. But, to
* avoid races, we must do so before giving up the synch_lock. This is why
* that lock has to be in unprotected memory. FIXME - for single thread in
* dr this will lead to rank order violation between dr exclusivity lock
* and the synch_lock with no easy workaround (real deadlocks possible).
* Luckily we'll prob. never use that option. */
if (INTERNAL_OPTION(single_thread_in_DR)) {
ASSERT_NOT_IMPLEMENTED(false);
}
EXITING_DR();
/* Ref case 5074, for us/app to successfully SetThreadContext at
* this synch point, this thread can NOT be at a system call. So, for
* case 10101, we instead have threads that are waiting_at_safe_spot()
* set their own contexts, allowing us to make system calls here.
* We don't yet handle the detach case, so it still requires no system
* calls, including the act of releasing the synch_lock
* which is why that lock has to be a user mode spin yield lock.
* FIXME: we could change tsd->synch_lock back to a regular lock
* once we have detach handling system calls here.
*/
spinmutex_unlock(tsd->synch_lock);
while (tsd->pending_synch_count > 0 && tsd->synch_perm != THREAD_SYNCH_NONE) {
STATS_INC_DC(dcontext, synch_loops_wait_safe);
#ifdef WINDOWS
if (started_detach) {
/* We spin for any non-detach synchs encountered during detach
* since we have no flag telling us this synch is for detach. */
/* Ref case 5074, can NOT use os_thread_yield here. This must be a user
* mode spin loop. */
SPINLOCK_PAUSE();
} else {
#endif
/* FIXME case 10100: replace this sleep/yield with a wait_for_event() */
synch_thread_yield();
#ifdef WINDOWS
}
#endif
}
/* Regain the synch_lock before ENTERING_DR to avoid races with getting
* suspended/killed in the middle of ENTERING_DR (before synch_perm is
* reset to NONE). */
/* Ref case 5074, for detach we still can NOT use os_thread_yield here (no system
* calls) so don't allow the spinmutex_lock to yield while grabbing the lock. */
spinmutex_lock_no_yield(tsd->synch_lock);
ENTERING_DR();
tsd->synch_perm = THREAD_SYNCH_NONE;
if (tsd->set_mcontext != NULL || tsd->set_context != NULL) {
IF_WINDOWS(ASSERT(!started_detach));
/* Make a local copy */
ASSERT(sizeof(cxt) >= sizeof(priv_mcontext_t));
if (tsd->set_mcontext != NULL) {
set_mcontext = true;
memcpy(cxt, tsd->set_mcontext, sizeof(*tsd->set_mcontext));
} else {
set_context = true;
memcpy(cxt, tsd->set_context, tsd->set_context_size);
}
synch_thread_free_setcontext(tsd); /* sets to NULL for us */
}
spinmutex_unlock(tsd->synch_lock);
LOG(THREAD, LOG_SYNCH, 2, "done waiting for synch with state %d (pc " PFX ")\n",
cur_state, pc);
if (set_mcontext || set_context) {
/* FIXME: see comment in dispatch.c check_wait_at_safe_spot() call
* about problems with KSTART(fcache_* differences bet the target
* being at the synch point vs in the cache.
*/
if (set_mcontext)
thread_set_self_mcontext((priv_mcontext_t *)cxt);
else
thread_set_self_context((void *)cxt);
ASSERT_NOT_REACHED();
}
}
/* adjusts the pending synch count */
void
adjust_wait_at_safe_spot(dcontext_t *dcontext, int amt)
{
thread_synch_data_t *tsd = (thread_synch_data_t *)dcontext->synch_field;
ASSERT(tsd->pending_synch_count >= 0);
spinmutex_lock(tsd->synch_lock);
ATOMIC_ADD(int, tsd->pending_synch_count, amt);
spinmutex_unlock(tsd->synch_lock);
}
/* Case 10101: Safely sets the context for a target thread that may be waiting at a
* safe spot, in which case we do not want to directly do a setcontext as the return
* from the yield or wait system call will mess up the state (case 5074).
* Assumes that cxt was allocated on the global heap, and frees it, rather than
* making its own copy (as an optimization).
* Does not work on the executing thread.
* Caller must hold thread_initexit_lock.
* If used on behalf of the app, it's up to the caller to check for privileges.
*/
bool
set_synched_thread_context(thread_record_t *trec,
/* pass either mc or both cxt and cxt_size */
priv_mcontext_t *mc, void *cxt, size_t cxt_size,
thread_synch_state_t desired_state _IF_X64(byte *cxt_alloc)
_IF_WINDOWS(NTSTATUS *status /*OUT*/))
{
bool res = true;
ASSERT(trec != NULL && trec->dcontext != NULL);
ASSERT(trec->dcontext != get_thread_private_dcontext());
ASSERT_OWN_MUTEX(true, &thread_initexit_lock);
#ifdef WINDOWS
if (status != NULL)
*status = STATUS_SUCCESS;
#endif
if (waiting_at_safe_spot(trec, desired_state)) {
/* case 10101: to allow system calls in check_wait_at_safe_spot() for
* performance reasons we have the waiting thread perform its own setcontext.
*/
thread_synch_data_t *tsd = (thread_synch_data_t *)trec->dcontext->synch_field;
spinmutex_lock(tsd->synch_lock);
if (tsd->set_mcontext != NULL || tsd->set_context != NULL) {
/* Two synchs in a row while still waiting; 2nd takes precedence */
STATS_INC(wait_multiple_setcxt);
synch_thread_free_setcontext(tsd);
}
#ifdef WINDOWS
LOG(THREAD_GET, LOG_SYNCH, 2,
"set_synched_thread_context %d to pc " PFX " via %s\n", trec->id,
(mc != NULL) ? mc->pc : (app_pc)((CONTEXT *)cxt)->CXT_XIP,
(mc != NULL) ? "mc" : "CONTEXT");
#else
ASSERT_NOT_IMPLEMENTED(mc != NULL); /* XXX: need sigcontext or sig_full_cxt_t */
#endif
if (mc != NULL)
tsd->set_mcontext = mc;
else {
ASSERT(cxt != NULL && cxt_size > 0);
tsd->set_context = cxt;
tsd->set_context_size = cxt_size;
}
IF_X64(tsd->set_context_alloc = cxt_alloc);
ASSERT(THREAD_SYNCH_SAFE(tsd->synch_perm, desired_state));
ASSERT(tsd->pending_synch_count >= 0);
/* Don't need to change pending_synch_count or anything; when thread is
* resumed it will properly reset everything itself */
spinmutex_unlock(tsd->synch_lock);
} else {
if (mc != NULL) {
res = thread_set_mcontext(trec, mc);
} else {
#ifdef WINDOWS
/* sort of ugly: but NtSetContextThread handling needs the status */
if (status != NULL) {
*status = nt_set_context(trec->handle, (CONTEXT *)cxt);
res = NT_SUCCESS(*status);
} else
res = thread_set_context(trec->handle, (CONTEXT *)cxt);
#else
/* currently there are no callers who don't pass mc: presumably
* PR 212090 will change that */
ASSERT_NOT_IMPLEMENTED(false);
#endif
}
free_setcontext(mc, cxt, cxt_size _IF_X64(cxt_alloc));
}
return res;
}
/* This is used to limit the maximum number of times synch_with_thread or
* synch_with_all_threads spin yield loops while waiting on an exiting thread.
* We assert if we ever break out of the loop because of this limit. FIXME make
* sure this limit is large enough that if it does ever trigger it's because
* of some kind of deadlock situation. Breaking out of the synchronization loop
* early is a correctness issue. Right now the limits are large but arbitrary.
* FIXME : once we are confident about thread synch get rid of these max loop checks.
* N.B.: the THREAD_SYNCH_SMALL_LOOP_MAX flag causes us to divide these by 10.
*/
#define SYNCH_ALL_THREADS_MAXIMUM_LOOPS (DYNAMO_OPTION(synch_all_threads_max_loops))
#define SYNCH_MAXIMUM_LOOPS (DYNAMO_OPTION(synch_thread_max_loops))
/* Amt of time in ms to wait for threads to get to a safe spot per a loop,
* see comments in synch_with_yield() on value. Our default value is 5ms which,
* depending on the tick resolution could end up being as long as 10 ms. */
#define SYNCH_WITH_WAIT_MS ((int)DYNAMO_OPTION(synch_with_sleep_time))
/* for use by synch_with_* routines to wait for thread(s) */
static void
synch_thread_yield()
{
/* xref 9400, 9488 - os_thread_yield() works ok on an UP machine, but on an MP machine
* yield might not actually do anything (in which case we burn through to the max
* loop counts pretty quick). We actually do want to wait a reasonable amt of time
* since the target thread might be doing some long latency dr operation (like
* dumping 500kb of registry into a forensics file) so we have the option to sleep
* instead. */
uint num_procs = get_num_processors();
ASSERT(num_procs != 0);
if ((num_procs == 1 && DYNAMO_OPTION(synch_thread_sleep_UP)) ||
(num_procs > 1 && DYNAMO_OPTION(synch_thread_sleep_MP))) {
os_thread_sleep(SYNCH_WITH_WAIT_MS);
} else {
os_thread_yield();
}
}
/* returns a thread_synch_result_t value
* id - the thread you want to synch with
* block - whether or not should spin until synch is successful
* hold_initexit_lock - whether or not the caller holds the thread_initexit_lock
* caller_state - a given permission define from above that describes the
* current state of the caller (note that holding the initexit
* lock is ok with respect to NO_LOCK
* desired_state - a requested state define from above that describes the
* desired synchronization
* flags - options from THREAD_SYNCH_ bitmask values
* NOTE - if you hold the initexit_lock and block with greater than NONE for
* caller state, then initexit_lock may be released and re-acquired
* NOTE - if any of the nt_ routines fails, it is assumed the thread no longer
* exists and returns true
* NOTE - if called directly (i.e. not through synch_with_all_threads)
* requires THREAD_SYNCH_IS_SAFE(caller_state, desired_state) to avoid deadlock
* NOTE - Requires the caller is !could_be_linking (i.e. not in an
* enter_couldbelinking state)
* NOTE - you can't call this with a thread that you've already suspended
*/
thread_synch_result_t
synch_with_thread(thread_id_t id, bool block, bool hold_initexit_lock,
thread_synch_permission_t caller_state,
thread_synch_state_t desired_state, uint flags)
{
thread_id_t my_id = d_r_get_thread_id();
uint loop_count = 0;
int expect_exiting = 0;
thread_record_t *my_tr = thread_lookup(my_id), *trec = NULL;
dcontext_t *dcontext = NULL;
priv_mcontext_t mc;
thread_synch_result_t res = THREAD_SYNCH_RESULT_NOT_SAFE;
bool first_loop = true;
IF_UNIX(bool actually_suspended = true;)
const uint max_loops = TEST(THREAD_SYNCH_SMALL_LOOP_MAX, flags)
? (SYNCH_MAXIMUM_LOOPS / 10)
: SYNCH_MAXIMUM_LOOPS;
ASSERT(id != my_id);
/* Must set ABORT or IGNORE. Only caller can RETRY as need a new
* set of threads for that, hoping problematic one is short-lived.
*/
ASSERT(
TESTANY(THREAD_SYNCH_SUSPEND_FAILURE_ABORT | THREAD_SYNCH_SUSPEND_FAILURE_IGNORE,
flags) &&
!TESTALL(THREAD_SYNCH_SUSPEND_FAILURE_ABORT | THREAD_SYNCH_SUSPEND_FAILURE_IGNORE,
flags));
if (my_tr != NULL) {
dcontext = my_tr->dcontext;
expect_exiting = dcontext->is_exiting ? 1 : 0;
ASSERT(exiting_thread_count >= expect_exiting);
} else {
/* calling thread should always be a known thread */
ASSERT_NOT_REACHED();
}
LOG(THREAD, LOG_SYNCH, 2,
"Synching with thread " TIDFMT ", giving %d, requesting %d, blocking=%d\n", id,
caller_state, desired_state, block);
if (!hold_initexit_lock)
d_r_mutex_lock(&thread_initexit_lock);
while (true) {
/* get thread record */
/* FIXME : thread id recycling is possible that this could be a
* different thread, perhaps we should take handle instead of id
* FIXME: use the new num field of thread_record_t?
*/
LOG(THREAD, LOG_SYNCH, 3, "Looping on synch with thread " TIDFMT "\n", id);
trec = thread_lookup(id);
/* We test the exiting thread count to avoid races between terminate/
* suspend thread (current thread, though we could be here for other
* reasons) and an exiting thread (who might no longer be on the all
* threads list) who is still using shared resources (ref case 3121) */
if ((trec == NULL && exiting_thread_count == expect_exiting) ||
loop_count++ > max_loops) {
/* make sure we didn't exit the loop without synchronizing, FIXME :
* in release builds we assume the synchronization is failing and
* continue without it, but that is dangerous.
* It is now up to the caller to handle this, and some use
* small loop counts and abort on failure, so only a curiosity. */
ASSERT_CURIOSITY(loop_count < max_loops);
LOG(THREAD, LOG_SYNCH, 3,
"Exceeded loop count synching with thread " TIDFMT "\n", id);
goto exit_synch_with_thread;
}
DOSTATS({
if (trec == NULL && exiting_thread_count > expect_exiting) {
LOG(THREAD, LOG_SYNCH, 2, "Waiting for an exiting thread\n");
STATS_INC(synch_yields_for_exiting_thread);
}
});
#ifdef UNIX
if (trec != NULL && trec->execve) {
/* i#237/PR 498284: clean up vfork "threads" that invoked execve.
* There should be no race since vfork suspends the parent.