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/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7XX XX
8XX GSChecks XX
9XX XX
10XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12*/
13
14#include "jitpch.h"
15#ifdef _MSC_VER
16#pragma hdrstop
17#endif
18
19/*****************************************************************************
20 * gsGSChecksInitCookie
21 * Grabs the cookie for detecting overflow of unsafe buffers.
22 */
23void Compiler::gsGSChecksInitCookie()
24{
25 var_types type = TYP_I_IMPL;
26
27 lvaGSSecurityCookie = lvaGrabTemp(false DEBUGARG("GSSecurityCookie"));
28
29 // Prevent cookie init/check from being optimized
30 lvaSetVarAddrExposed(lvaGSSecurityCookie);
31 lvaTable[lvaGSSecurityCookie].lvType = type;
32
33 info.compCompHnd->getGSCookie(&gsGlobalSecurityCookieVal, &gsGlobalSecurityCookieAddr);
34}
35
36const unsigned NO_SHADOW_COPY = UINT_MAX;
37
38/*****************************************************************************
39 * gsCopyShadowParams
40 * The current function has an unsafe buffer on the stack. Search for vulnerable
41 * parameters which could be used to modify a code address and take over the process
42 * in the case of a buffer overrun. Create a safe local copy for each vulnerable parameter,
43 * which will be allocated bellow the unsafe buffer. Change uses of the param to the
44 * shadow copy.
45 *
46 * A pointer under indirection is considered vulnerable. A malicious user could read from
47 * protected memory or write to it. If a parameter is assigned/computed into another variable,
48 * and is a pointer (i.e., under indirection), then we consider the variable to be part of the
49 * equivalence class with the parameter. All parameters in the equivalence class are shadowed.
50 */
51void Compiler::gsCopyShadowParams()
52{
53 if (info.compIsVarArgs)
54 {
55 return;
56 }
57
58 // Allocate array for shadow param info
59 gsShadowVarInfo = new (this, CMK_Unknown) ShadowParamVarInfo[lvaCount]();
60
61 // Find groups of variables assigned to each other, and also
62 // tracks variables which are dereferenced and marks them as ptrs.
63 // Look for assignments to *p, and ptrs passed to functions
64 if (gsFindVulnerableParams())
65 {
66 // Replace vulnerable params by shadow copies.
67 gsParamsToShadows();
68 }
69}
70
71// This struct tracks how a tree is being used
72
73struct MarkPtrsInfo
74{
75 Compiler* comp;
76 unsigned lvAssignDef; // Which local variable is the tree being assigned to?
77 bool isAssignSrc; // Is this the source value for an assignment?
78 bool isUnderIndir; // Is this a pointer value tree that is being dereferenced?
79 bool skipNextNode; // Skip a single node during the tree-walk
80
81#ifdef DEBUG
82 void Print()
83 {
84 printf(
85 "[MarkPtrsInfo] = {comp = %p, lvAssignDef = %d, isAssignSrc = %d, isUnderIndir = %d, skipNextNode = %d}\n",
86 comp, lvAssignDef, isAssignSrc, isUnderIndir, skipNextNode);
87 }
88#endif
89};
90
91/*****************************************************************************
92 * gsMarkPtrsAndAssignGroups
93 * Walk a tree looking for assignment groups, variables whose value is used
94 * in a *p store or use, and variable passed to calls. This info is then used
95 * to determine parameters which are vulnerable.
96 * This function carries a state to know if it is under an assign node, call node
97 * or indirection node. It starts a new tree walk for it's subtrees when the state
98 * changes.
99 */
100Compiler::fgWalkResult Compiler::gsMarkPtrsAndAssignGroups(GenTree** pTree, fgWalkData* data)
101{
102 struct MarkPtrsInfo* pState = (MarkPtrsInfo*)data->pCallbackData;
103 struct MarkPtrsInfo newState = *pState;
104 Compiler* comp = data->compiler;
105 GenTree* tree = *pTree;
106 ShadowParamVarInfo* shadowVarInfo = pState->comp->gsShadowVarInfo;
107 assert(shadowVarInfo);
108 bool fIsBlk = false;
109 unsigned lclNum;
110
111 assert(!pState->isAssignSrc || pState->lvAssignDef != (unsigned)-1);
112
113 if (pState->skipNextNode)
114 {
115 pState->skipNextNode = false;
116 return WALK_CONTINUE;
117 }
118
119 switch (tree->OperGet())
120 {
121 // Indirections - look for *p uses and defs
122 case GT_IND:
123 case GT_OBJ:
124 case GT_ARR_ELEM:
125 case GT_ARR_INDEX:
126 case GT_ARR_OFFSET:
127 case GT_FIELD:
128
129 newState.isUnderIndir = true;
130 {
131 newState.skipNextNode = true; // Don't have to worry about which kind of node we're dealing with
132 comp->fgWalkTreePre(&tree, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
133 }
134
135 return WALK_SKIP_SUBTREES;
136
137 // local vars and param uses
138 case GT_LCL_VAR:
139 case GT_LCL_FLD:
140 lclNum = tree->gtLclVarCommon.gtLclNum;
141
142 if (pState->isUnderIndir)
143 {
144 // The variable is being dereferenced for a read or a write.
145 comp->lvaTable[lclNum].lvIsPtr = 1;
146 }
147
148 if (pState->isAssignSrc)
149 {
150 //
151 // Add lvAssignDef and lclNum to a common assign group
152 if (shadowVarInfo[pState->lvAssignDef].assignGroup)
153 {
154 if (shadowVarInfo[lclNum].assignGroup)
155 {
156 // OR both bit vector
157 shadowVarInfo[pState->lvAssignDef].assignGroup->bitVectOr(shadowVarInfo[lclNum].assignGroup);
158 }
159 else
160 {
161 shadowVarInfo[pState->lvAssignDef].assignGroup->bitVectSet(lclNum);
162 }
163
164 // Point both to the same bit vector
165 shadowVarInfo[lclNum].assignGroup = shadowVarInfo[pState->lvAssignDef].assignGroup;
166 }
167 else if (shadowVarInfo[lclNum].assignGroup)
168 {
169 shadowVarInfo[lclNum].assignGroup->bitVectSet(pState->lvAssignDef);
170
171 // Point both to the same bit vector
172 shadowVarInfo[pState->lvAssignDef].assignGroup = shadowVarInfo[lclNum].assignGroup;
173 }
174 else
175 {
176 FixedBitVect* bv = FixedBitVect::bitVectInit(pState->comp->lvaCount, pState->comp);
177
178 // (shadowVarInfo[pState->lvAssignDef] == NULL && shadowVarInfo[lclNew] == NULL);
179 // Neither of them has an assign group yet. Make a new one.
180 shadowVarInfo[pState->lvAssignDef].assignGroup = bv;
181 shadowVarInfo[lclNum].assignGroup = bv;
182 bv->bitVectSet(pState->lvAssignDef);
183 bv->bitVectSet(lclNum);
184 }
185 }
186 return WALK_CONTINUE;
187
188 // Calls - Mark arg variables
189 case GT_CALL:
190
191 newState.isUnderIndir = false;
192 newState.isAssignSrc = false;
193 {
194 if (tree->gtCall.gtCallObjp)
195 {
196 newState.isUnderIndir = true;
197 comp->fgWalkTreePre(&tree->gtCall.gtCallObjp, gsMarkPtrsAndAssignGroups, (void*)&newState);
198 }
199
200 for (GenTreeArgList* args = tree->gtCall.gtCallArgs; args; args = args->Rest())
201 {
202 comp->fgWalkTreePre(&args->Current(), gsMarkPtrsAndAssignGroups, (void*)&newState);
203 }
204 for (GenTreeArgList* args = tree->gtCall.gtCallLateArgs; args; args = args->Rest())
205 {
206 comp->fgWalkTreePre(&args->Current(), gsMarkPtrsAndAssignGroups, (void*)&newState);
207 }
208
209 if (tree->gtCall.gtCallType == CT_INDIRECT)
210 {
211 newState.isUnderIndir = true;
212
213 // A function pointer is treated like a write-through pointer since
214 // it controls what code gets executed, and so indirectly can cause
215 // a write to memory.
216 comp->fgWalkTreePre(&tree->gtCall.gtCallAddr, gsMarkPtrsAndAssignGroups, (void*)&newState);
217 }
218 }
219 return WALK_SKIP_SUBTREES;
220
221 case GT_ADDR:
222 newState.isUnderIndir = false;
223 // We'll assume p in "**p = " can be vulnerable because by changing 'p', someone
224 // could control where **p stores to.
225 {
226 comp->fgWalkTreePre(&tree->gtOp.gtOp1, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
227 }
228 return WALK_SKIP_SUBTREES;
229
230 default:
231 // Assignments - track assign groups and *p defs.
232 if (tree->OperIs(GT_ASG))
233 {
234 bool isLocVar;
235 bool isLocFld;
236
237 if (tree->OperIsBlkOp())
238 {
239 // Blk assignments are always handled as if they have implicit indirections.
240 // TODO-1stClassStructs: improve this.
241 newState.isUnderIndir = true;
242 comp->fgWalkTreePre(&tree->gtOp.gtOp1, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
243
244 if (tree->OperIsInitBlkOp())
245 {
246 newState.isUnderIndir = false;
247 }
248 comp->fgWalkTreePre(&tree->gtOp.gtOp2, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
249 }
250 else
251 {
252 // Walk dst side
253 comp->fgWalkTreePre(&tree->gtOp.gtOp1, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
254
255 // Now handle src side
256 isLocVar = tree->gtOp.gtOp1->OperGet() == GT_LCL_VAR;
257 isLocFld = tree->gtOp.gtOp1->OperGet() == GT_LCL_FLD;
258
259 if ((isLocVar || isLocFld) && tree->gtOp.gtOp2)
260 {
261 lclNum = tree->gtOp.gtOp1->gtLclVarCommon.gtLclNum;
262 newState.lvAssignDef = lclNum;
263 newState.isAssignSrc = true;
264 }
265
266 comp->fgWalkTreePre(&tree->gtOp.gtOp2, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
267 }
268
269 return WALK_SKIP_SUBTREES;
270 }
271 }
272
273 return WALK_CONTINUE;
274}
275
276/*****************************************************************************
277 * gsFindVulnerableParams
278 * Walk all the trees looking for ptrs, args, assign groups, *p stores, etc.
279 * Then use that info to figure out vulnerable pointers.
280 *
281 * It returns true if it found atleast one vulnerable pointer parameter that
282 * needs to be shadow-copied.
283 */
284
285bool Compiler::gsFindVulnerableParams()
286{
287 MarkPtrsInfo info;
288
289 info.comp = this;
290 info.lvAssignDef = (unsigned)-1;
291 info.isUnderIndir = false;
292 info.isAssignSrc = false;
293 info.skipNextNode = false;
294
295 // Walk all the trees setting lvIsWritePtr, lvIsOutgoingArg, lvIsPtr and assignGroup.
296 fgWalkAllTreesPre(gsMarkPtrsAndAssignGroups, &info);
297
298 // Compute has vulnerable at the end of the loop.
299 bool hasOneVulnerable = false;
300
301 // Initialize propagated[v0...vn] = {0}^n, so we can skip the ones propagated through
302 // some assign group.
303 FixedBitVect* propagated = (lvaCount > 0) ? FixedBitVect::bitVectInit(lvaCount, this) : nullptr;
304
305 for (UINT lclNum = 0; lclNum < lvaCount; lclNum++)
306 {
307 LclVarDsc* varDsc = &lvaTable[lclNum];
308 ShadowParamVarInfo* shadowInfo = &gsShadowVarInfo[lclNum];
309
310 // If there was an indirection or if unsafe buffer, then we'd call it vulnerable.
311 if (varDsc->lvIsPtr || varDsc->lvIsUnsafeBuffer)
312 {
313 hasOneVulnerable = true;
314 }
315
316 // Now, propagate the info through the assign group (an equivalence class of vars transitively assigned.)
317 if (shadowInfo->assignGroup == nullptr || propagated->bitVectTest(lclNum))
318 {
319 continue;
320 }
321
322 // Propagate lvIsPtr, so that:
323 // 1. Any parameter in the equivalence class can be identified as lvIsPtr and hence shadowed.
324 // 2. Buffers with pointers are placed at lower memory addresses than buffers without pointers.
325 UINT isUnderIndir = varDsc->lvIsPtr;
326
327 // First pass -- find if any variable is vulnerable.
328 FixedBitVect* assignGroup = shadowInfo->assignGroup;
329 for (UINT lclNum = assignGroup->bitVectGetFirst(); lclNum != (unsigned)-1 && !isUnderIndir;
330 lclNum = assignGroup->bitVectGetNext(lclNum))
331 {
332 isUnderIndir |= lvaTable[lclNum].lvIsPtr;
333 }
334
335 // Vulnerable, so propagate to all members of the equivalence class.
336 if (isUnderIndir)
337 {
338 hasOneVulnerable = true;
339 }
340 // Nothing to propagate.
341 else
342 {
343 continue;
344 }
345
346 // Second pass -- mark all are vulnerable.
347 assert(isUnderIndir);
348 for (UINT lclNum = assignGroup->bitVectGetFirst(); lclNum != (unsigned)-1;
349 lclNum = assignGroup->bitVectGetNext(lclNum))
350 {
351 lvaTable[lclNum].lvIsPtr = TRUE;
352 propagated->bitVectSet(lclNum);
353 }
354
355#ifdef DEBUG
356 if (verbose)
357 {
358 printf("Equivalence assign group %s: ", isUnderIndir ? "isPtr " : "");
359 for (UINT lclNum = assignGroup->bitVectGetFirst(); lclNum != (unsigned)-1;
360 lclNum = assignGroup->bitVectGetNext(lclNum))
361 {
362 gtDispLclVar(lclNum, false);
363 printf(" ");
364 }
365 printf("\n");
366 }
367#endif
368 }
369
370 return hasOneVulnerable;
371}
372
373/*****************************************************************************
374 * gsParamsToShadows
375 * Copy each vulnerable param ptr or buffer to a local shadow copy and replace
376 * uses of the param by the shadow copy
377 */
378void Compiler::gsParamsToShadows()
379{
380 // Cache old count since we'll add new variables, and
381 // gsShadowVarInfo will not grow to accomodate the new ones.
382 UINT lvaOldCount = lvaCount;
383
384 // Create shadow copy for each param candidate
385 for (UINT lclNum = 0; lclNum < lvaOldCount; lclNum++)
386 {
387 LclVarDsc* varDsc = &lvaTable[lclNum];
388 gsShadowVarInfo[lclNum].shadowCopy = NO_SHADOW_COPY;
389
390 // Only care about params whose values are on the stack
391 if (!ShadowParamVarInfo::mayNeedShadowCopy(varDsc))
392 {
393 continue;
394 }
395
396 if (!varDsc->lvIsPtr && !varDsc->lvIsUnsafeBuffer)
397 {
398 continue;
399 }
400
401 int shadowVar = lvaGrabTemp(false DEBUGARG("shadowVar"));
402 // reload varDsc as lvaGrabTemp may realloc the lvaTable[]
403 varDsc = &lvaTable[lclNum];
404
405 // Copy some info
406
407 var_types type = varTypeIsSmall(varDsc->TypeGet()) ? TYP_INT : varDsc->TypeGet();
408 lvaTable[shadowVar].lvType = type;
409
410#ifdef FEATURE_SIMD
411 lvaTable[shadowVar].lvSIMDType = varDsc->lvSIMDType;
412 lvaTable[shadowVar].lvUsedInSIMDIntrinsic = varDsc->lvUsedInSIMDIntrinsic;
413 if (varDsc->lvSIMDType)
414 {
415 lvaTable[shadowVar].lvExactSize = varDsc->lvExactSize;
416 lvaTable[shadowVar].lvBaseType = varDsc->lvBaseType;
417 }
418#endif
419 lvaTable[shadowVar].lvRegStruct = varDsc->lvRegStruct;
420
421 lvaTable[shadowVar].lvAddrExposed = varDsc->lvAddrExposed;
422 lvaTable[shadowVar].lvDoNotEnregister = varDsc->lvDoNotEnregister;
423#ifdef DEBUG
424 lvaTable[shadowVar].lvVMNeedsStackAddr = varDsc->lvVMNeedsStackAddr;
425 lvaTable[shadowVar].lvLiveInOutOfHndlr = varDsc->lvLiveInOutOfHndlr;
426 lvaTable[shadowVar].lvLclFieldExpr = varDsc->lvLclFieldExpr;
427 lvaTable[shadowVar].lvLiveAcrossUCall = varDsc->lvLiveAcrossUCall;
428#endif
429 lvaTable[shadowVar].lvVerTypeInfo = varDsc->lvVerTypeInfo;
430 lvaTable[shadowVar].lvGcLayout = varDsc->lvGcLayout;
431 lvaTable[shadowVar].lvIsUnsafeBuffer = varDsc->lvIsUnsafeBuffer;
432 lvaTable[shadowVar].lvIsPtr = varDsc->lvIsPtr;
433
434#ifdef DEBUG
435 if (verbose)
436 {
437 printf("Var V%02u is shadow param candidate. Shadow copy is V%02u.\n", lclNum, shadowVar);
438 }
439#endif
440
441 gsShadowVarInfo[lclNum].shadowCopy = shadowVar;
442 }
443
444 // Replace param uses with shadow copy
445 fgWalkAllTreesPre(gsReplaceShadowParams, (void*)this);
446
447 // Now insert code to copy the params to their shadow copy.
448 for (UINT lclNum = 0; lclNum < lvaOldCount; lclNum++)
449 {
450 LclVarDsc* varDsc = &lvaTable[lclNum];
451
452 unsigned shadowVar = gsShadowVarInfo[lclNum].shadowCopy;
453 if (shadowVar == NO_SHADOW_COPY)
454 {
455 continue;
456 }
457
458 var_types type = lvaTable[shadowVar].TypeGet();
459
460 GenTree* src = gtNewLclvNode(lclNum, varDsc->TypeGet());
461 GenTree* dst = gtNewLclvNode(shadowVar, type);
462
463 src->gtFlags |= GTF_DONT_CSE;
464 dst->gtFlags |= GTF_DONT_CSE;
465
466 GenTree* opAssign = nullptr;
467 if (type == TYP_STRUCT)
468 {
469 CORINFO_CLASS_HANDLE clsHnd = varDsc->lvVerTypeInfo.GetClassHandle();
470
471 // We don't need unsafe value cls check here since we are copying the params and this flag
472 // would have been set on the original param before reaching here.
473 lvaSetStruct(shadowVar, clsHnd, false);
474
475 src = gtNewOperNode(GT_ADDR, TYP_BYREF, src);
476 dst = gtNewOperNode(GT_ADDR, TYP_BYREF, dst);
477
478 opAssign = gtNewCpObjNode(dst, src, clsHnd, false);
479 lvaTable[shadowVar].lvIsMultiRegArg = lvaTable[lclNum].lvIsMultiRegArg;
480 lvaTable[shadowVar].lvIsMultiRegRet = lvaTable[lclNum].lvIsMultiRegRet;
481 }
482 else
483 {
484 opAssign = gtNewAssignNode(dst, src);
485 }
486 fgEnsureFirstBBisScratch();
487 (void)fgInsertStmtAtBeg(fgFirstBB, fgMorphTree(opAssign));
488 }
489
490 // If the method has "Jmp CalleeMethod", then we need to copy shadow params back to original
491 // params before "jmp" to CalleeMethod.
492 if (compJmpOpUsed)
493 {
494 // There could be more than one basic block ending with a "Jmp" type tail call.
495 // We would have to insert assignments in all such blocks, just before GT_JMP stmnt.
496 for (BasicBlock* block = fgFirstBB; block; block = block->bbNext)
497 {
498 if (block->bbJumpKind != BBJ_RETURN)
499 {
500 continue;
501 }
502
503 if ((block->bbFlags & BBF_HAS_JMP) == 0)
504 {
505 continue;
506 }
507
508 for (UINT lclNum = 0; lclNum < info.compArgsCount; lclNum++)
509 {
510 LclVarDsc* varDsc = &lvaTable[lclNum];
511
512 unsigned shadowVar = gsShadowVarInfo[lclNum].shadowCopy;
513 if (shadowVar == NO_SHADOW_COPY)
514 {
515 continue;
516 }
517
518 GenTree* src = gtNewLclvNode(shadowVar, lvaTable[shadowVar].TypeGet());
519 GenTree* dst = gtNewLclvNode(lclNum, varDsc->TypeGet());
520
521 src->gtFlags |= GTF_DONT_CSE;
522 dst->gtFlags |= GTF_DONT_CSE;
523
524 GenTree* opAssign = nullptr;
525 if (varDsc->TypeGet() == TYP_STRUCT)
526 {
527 CORINFO_CLASS_HANDLE clsHnd = varDsc->lvVerTypeInfo.GetClassHandle();
528 src = gtNewOperNode(GT_ADDR, TYP_BYREF, src);
529 dst = gtNewOperNode(GT_ADDR, TYP_BYREF, dst);
530
531 opAssign = gtNewCpObjNode(dst, src, clsHnd, false);
532 }
533 else
534 {
535 opAssign = gtNewAssignNode(dst, src);
536 }
537
538 (void)fgInsertStmtNearEnd(block, fgMorphTree(opAssign));
539 }
540 }
541 }
542}
543
544/*****************************************************************************
545 * gsReplaceShadowParams (tree-walk call-back)
546 * Replace all vulnerable param uses by it's shadow copy.
547 */
548
549Compiler::fgWalkResult Compiler::gsReplaceShadowParams(GenTree** pTree, fgWalkData* data)
550{
551 Compiler* comp = data->compiler;
552 GenTree* tree = *pTree;
553 GenTree* asg = nullptr;
554
555 if (tree->gtOper == GT_ASG)
556 {
557 asg = tree; // "asg" is the assignment tree.
558 tree = tree->gtOp.gtOp1; // "tree" is the local var tree at the left-hand size of the assignment.
559 }
560
561 if (tree->gtOper == GT_LCL_VAR || tree->gtOper == GT_LCL_FLD)
562 {
563 UINT paramNum = tree->gtLclVarCommon.gtLclNum;
564
565 if (!ShadowParamVarInfo::mayNeedShadowCopy(&comp->lvaTable[paramNum]) ||
566 comp->gsShadowVarInfo[paramNum].shadowCopy == NO_SHADOW_COPY)
567 {
568 return WALK_CONTINUE;
569 }
570
571 tree->gtLclVarCommon.SetLclNum(comp->gsShadowVarInfo[paramNum].shadowCopy);
572
573 // In gsParamsToShadows(), we create a shadow var of TYP_INT for every small type param.
574 // Make sure we update the type of the local var tree as well.
575 if (varTypeIsSmall(comp->lvaTable[paramNum].TypeGet()))
576 {
577 tree->gtType = TYP_INT;
578 if (asg)
579 {
580 // If this is an assignment tree, propagate the type to it as well.
581 asg->gtType = TYP_INT;
582 }
583 }
584 }
585
586 return WALK_CONTINUE;
587}
588