1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5// ---------------------------------------------------------------------------
6// EEContract.cpp
7//
8
9// ! I am the owner for issues in the contract *infrastructure*, not for every
10// ! CONTRACT_VIOLATION dialog that comes up. If you interrupt my work for a routine
11// ! CONTRACT_VIOLATION, you will become the new owner of this file.
12// ---------------------------------------------------------------------------
13
14
15#include "common.h"
16#include "dbginterface.h"
17
18
19#ifdef ENABLE_CONTRACTS
20
21void EEContract::Disable()
22{
23 BaseContract::Disable();
24}
25
26void EEContract::DoChecks(UINT testmask, __in_z const char *szFunction, __in_z const char *szFile, int lineNum)
27{
28 SCAN_IGNORE_THROW; // Tell the static contract analyzer to ignore contract violations
29 SCAN_IGNORE_FAULT; // due to the contract checking logic itself.
30 SCAN_IGNORE_TRIGGER;
31 SCAN_IGNORE_LOCK;
32 SCAN_IGNORE_SO;
33
34 // Many of the checks below result in calls to GetThread()
35 // that work just fine if GetThread() returns NULL, so temporarily
36 // allow such calls.
37 BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION;
38 m_pThread = GetThread();
39 if (m_pThread != NULL)
40 {
41 m_pClrDebugState = m_pThread->GetClrDebugState();
42 }
43
44 // Call our base DoChecks.
45 BaseContract::DoChecks(testmask, szFunction, szFile, lineNum);
46
47 m_testmask = testmask;
48 m_contractStackRecord.m_testmask = testmask;
49
50 // GC mode check
51 switch (testmask & MODE_Mask)
52 {
53 case MODE_Coop:
54 if (m_pThread == NULL || !m_pThread->PreemptiveGCDisabled())
55 {
56 //
57 // Check if this is the debugger helper thread and has the runtime
58 // stoppped. If both of these things are true, then we do not care
59 // whether we are in COOP mode or not.
60 //
61 if ((g_pDebugInterface != NULL) &&
62 g_pDebugInterface->ThisIsHelperThread() &&
63 g_pDebugInterface->IsStopped())
64 {
65 break;
66 }
67
68 // Pretend that the threads doing GC are in cooperative mode so that code with
69 // MODE_COOPERATIVE contract works fine on them.
70 if (IsGCThread())
71 {
72 break;
73 }
74
75 if (!( (ModeViolation|BadDebugState) & m_pClrDebugState->ViolationMask()))
76 {
77 if (m_pThread == NULL)
78 {
79 CONTRACT_ASSERT("You must have called SetupThread in order to be in GC Cooperative mode.",
80 Contract::MODE_Preempt,
81 Contract::MODE_Mask,
82 m_contractStackRecord.m_szFunction,
83 m_contractStackRecord.m_szFile,
84 m_contractStackRecord.m_lineNum
85 );
86 }
87 else
88 {
89 CONTRACT_ASSERT("MODE_COOPERATIVE encountered while thread is in preemptive state.",
90 Contract::MODE_Preempt,
91 Contract::MODE_Mask,
92 m_contractStackRecord.m_szFunction,
93 m_contractStackRecord.m_szFile,
94 m_contractStackRecord.m_lineNum
95 );
96 }
97 }
98 }
99 break;
100
101 case MODE_Preempt:
102 // Unmanaged threads are considered permanently preemptive so a NULL thread amounts to a passing case here.
103 if (m_pThread != NULL && m_pThread->PreemptiveGCDisabled())
104 {
105 if (!( (ModeViolation|BadDebugState) & m_pClrDebugState->ViolationMask()))
106 {
107 CONTRACT_ASSERT("MODE_PREEMPTIVE encountered while thread is in cooperative state.",
108 Contract::MODE_Coop,
109 Contract::MODE_Mask,
110 m_contractStackRecord.m_szFunction,
111 m_contractStackRecord.m_szFile,
112 m_contractStackRecord.m_lineNum
113 );
114 }
115 }
116 break;
117
118 case MODE_Disabled:
119 // Nothing
120 break;
121
122 default:
123 UNREACHABLE();
124 }
125
126 // GC Trigger check
127 switch (testmask & GC_Mask)
128 {
129 case GC_Triggers:
130 // We don't want to do a full TRIGGERSGC here as this could corrupt
131 // OBJECTREF-typed arguments to the function.
132 {
133 if (m_pClrDebugState->GetGCNoTriggerCount())
134 {
135 if (!( (GCViolation|BadDebugState) & m_pClrDebugState->ViolationMask()))
136 {
137 CONTRACT_ASSERT("GC_TRIGGERS encountered in a GC_NOTRIGGER scope",
138 Contract::GC_NoTrigger,
139 Contract::GC_Mask,
140 m_contractStackRecord.m_szFunction,
141 m_contractStackRecord.m_szFile,
142 m_contractStackRecord.m_lineNum
143 );
144 }
145 }
146 }
147 break;
148
149 case GC_NoTrigger:
150 m_pClrDebugState->ViolationMaskReset( GCViolation );
151
152 // Inlined BeginNoTriggerGC
153 m_pClrDebugState->IncrementGCNoTriggerCount();
154 if (m_pThread && m_pThread->m_fPreemptiveGCDisabled)
155 {
156 m_pClrDebugState->IncrementGCForbidCount();
157 }
158
159 break;
160
161 case GC_Disabled:
162 // Nothing
163 break;
164
165 default:
166 UNREACHABLE();
167 }
168
169 // Host Triggers check
170 switch (testmask & HOST_Mask)
171 {
172 case HOST_Calls:
173 {
174 if (!m_pClrDebugState->IsHostCaller())
175 {
176 if (!( (HostViolation|BadDebugState) & m_pClrDebugState->ViolationMask()))
177 {
178 // Avoid infinite recursion by temporarily allowing HOST_CALLS
179 // violations so that we don't get contract asserts in anything
180 // called downstream of CONTRACT_ASSERT. If we unwind out of
181 // here, our dtor will reset our state to what it was on entry.
182 CONTRACT_VIOLATION(HostViolation);
183 CONTRACT_ASSERT("HOST_CALLS encountered in a HOST_NOCALLS scope",
184 Contract::HOST_NoCalls,
185 Contract::HOST_Mask,
186 m_contractStackRecord.m_szFunction,
187 m_contractStackRecord.m_szFile,
188 m_contractStackRecord.m_lineNum
189 );
190 }
191 }
192 }
193 break;
194
195 case HOST_NoCalls:
196 // m_pClrDebugState->ViolationMaskReset( HostViolation );
197 m_pClrDebugState->ResetHostCaller();
198 break;
199
200 case HOST_Disabled:
201 // Nothing
202 break;
203
204 default:
205 UNREACHABLE();
206 }
207 END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION;
208
209 // EE Thread-required check
210 // NOTE: The following must NOT be inside BEGIN/END_GETTHREAD_ALLOWED,
211 // as the change to m_pClrDebugState->m_allowGetThread below would be
212 // overwritten by END_GETTHREAD_ALLOWED.
213 switch (testmask & EE_THREAD_Mask)
214 {
215 case EE_THREAD_Required:
216 if (!((EEThreadViolation|BadDebugState) & m_pClrDebugState->ViolationMask()))
217 {
218 if (m_pThread == NULL)
219 {
220 CONTRACT_ASSERT("EE_THREAD_REQUIRED encountered with no current EE Thread object in TLS.",
221 Contract::EE_THREAD_Required,
222 Contract::EE_THREAD_Mask,
223 m_contractStackRecord.m_szFunction,
224 m_contractStackRecord.m_szFile,
225 m_contractStackRecord.m_lineNum
226 );
227 }
228 else if (!m_pClrDebugState->IsGetThreadAllowed())
229 {
230 // In general, it's unsafe for an EE_THREAD_NOT_REQUIRED function to
231 // call an EE_THREAD_REQUIRED function. In cases where it is safe,
232 // you may wrap the call to the EE_THREAD_REQUIRED function inside a
233 // BEGIN/END_GETTHREAD_ALLOWED block, but you may only do so if the
234 // case where GetThread() == NULL is clearly handled in a way that
235 // prevents entry into the BEGIN/END_GETTHREAD_ALLOWED block.
236 CONTRACT_ASSERT("EE_THREAD_REQUIRED encountered in an EE_THREAD_NOT_REQUIRED scope, without an intervening BEGIN/END_GETTHREAD_ALLOWED block.",
237 Contract::EE_THREAD_Required,
238 Contract::EE_THREAD_Mask,
239 m_contractStackRecord.m_szFunction,
240 m_contractStackRecord.m_szFile,
241 m_contractStackRecord.m_lineNum
242 );
243 }
244 }
245 m_pClrDebugState->SetGetThreadAllowed();
246 break;
247
248 case EE_THREAD_Not_Required:
249 m_pClrDebugState->ResetGetThreadAllowed();
250 break;
251
252 case EE_THREAD_Disabled:
253 break;
254
255 default:
256 UNREACHABLE();
257 }
258}
259#endif // ENABLE_CONTRACTS
260
261
262BYTE* __stdcall GetAddrOfContractShutoffFlag()
263{
264 LIMITED_METHOD_CONTRACT;
265
266 // Exposed entrypoint where we cannot probe or do anything TLS
267 // related
268 static BYTE gContractShutoffFlag = 0;
269
270 return &gContractShutoffFlag;
271}
272
273