Security.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "BackEnd.h"
  6. void
  7. Security::EncodeLargeConstants()
  8. {
  9. #pragma prefast(suppress:6236 6285, "logical-or of constants is by design")
  10. if (PHASE_OFF(Js::EncodeConstantsPhase, this->func) || CONFIG_ISENABLED(Js::DebugFlag) || !MD_ENCODE_LG_CONSTS)
  11. {
  12. return;
  13. }
  14. FOREACH_REAL_INSTR_IN_FUNC_EDITING(instr, instrNext, this->func)
  15. {
  16. if (!instr->IsRealInstr())
  17. {
  18. continue;
  19. }
  20. IR::Opnd *dst = instr->GetDst();
  21. if (dst)
  22. {
  23. this->EncodeOpnd(instr, dst);
  24. }
  25. IR::Opnd *src1 = instr->GetSrc1();
  26. if (src1)
  27. {
  28. this->EncodeOpnd(instr, src1);
  29. IR::Opnd *src2 = instr->GetSrc2();
  30. if (src2)
  31. {
  32. this->EncodeOpnd(instr, src2);
  33. }
  34. }
  35. } NEXT_REAL_INSTR_IN_FUNC_EDITING;
  36. }
  37. int
  38. Security::GetNextNOPInsertPoint()
  39. {
  40. uint frequency = (1 << CONFIG_FLAG(NopFrequency)) - 1;
  41. return (Math::Rand() & frequency) + 1;
  42. }
  43. void
  44. Security::InsertRandomFunctionPad(IR::Instr * instrBeforeInstr)
  45. {
  46. if (PHASE_OFF(Js::InsertNOPsPhase, instrBeforeInstr->m_func->GetTopFunc())
  47. || CONFIG_ISENABLED(Js::DebugFlag) || CONFIG_ISENABLED(Js::BenchmarkFlag))
  48. {
  49. return;
  50. }
  51. DWORD randomPad = Math::Rand() & ((0 - INSTR_ALIGNMENT) & 0xF);
  52. #ifndef _M_ARM
  53. if (randomPad == 1)
  54. {
  55. InsertSmallNOP(instrBeforeInstr, 1);
  56. return;
  57. }
  58. if (randomPad & 1)
  59. {
  60. InsertSmallNOP(instrBeforeInstr, 3);
  61. randomPad -= 3;
  62. }
  63. #endif
  64. Assert((randomPad & 1) == 0);
  65. while (randomPad >= 4)
  66. {
  67. InsertSmallNOP(instrBeforeInstr, 4);
  68. randomPad -= 4;
  69. }
  70. Assert(randomPad == 2 || randomPad == 0);
  71. if (randomPad == 2)
  72. {
  73. InsertSmallNOP(instrBeforeInstr, 2);
  74. }
  75. }
  76. void
  77. Security::InsertNOPs()
  78. {
  79. if (PHASE_OFF(Js::InsertNOPsPhase, this->func) || CONFIG_ISENABLED(Js::DebugFlag) || CONFIG_ISENABLED(Js::BenchmarkFlag))
  80. {
  81. return;
  82. }
  83. int count = 0;
  84. IR::Instr *instr = this->func->m_headInstr;
  85. while(true)
  86. {
  87. count = this->GetNextNOPInsertPoint();
  88. while(instr && count--)
  89. {
  90. instr = instr->GetNextRealInstr();
  91. }
  92. if (instr == nullptr)
  93. {
  94. break;
  95. }
  96. this->InsertNOPBefore(instr);
  97. };
  98. }
  99. void
  100. Security::InsertNOPBefore(IR::Instr *instr)
  101. {
  102. InsertSmallNOP(instr, (Math::Rand() & 0x3) + 1);
  103. }
  104. void
  105. Security::InsertSmallNOP(IR::Instr * instr, DWORD nopSize)
  106. {
  107. #if defined(_M_IX86) || defined(_M_X64)
  108. #ifdef _M_IX86
  109. if (AutoSystemInfo::Data.SSE2Available())
  110. { // on x86 system that has SSE2, encode fast NOPs as x64 does
  111. #endif
  112. Assert(nopSize >= 1 || nopSize <= 4);
  113. IR::Instr *nop = IR::Instr::New(Js::OpCode::NOP, instr->m_func);
  114. // Let the encoder know what the size of the NOP needs to be.
  115. if (nopSize > 1)
  116. {
  117. // 2, 3 or 4 byte NOP.
  118. IR::IntConstOpnd *nopSizeOpnd = IR::IntConstOpnd::New(nopSize, TyInt8, instr->m_func);
  119. nop->SetSrc1(nopSizeOpnd);
  120. }
  121. instr->InsertBefore(nop);
  122. #ifdef _M_IX86
  123. }
  124. else
  125. {
  126. IR::Instr *nopInstr = nullptr;
  127. IR::RegOpnd *regOpnd;
  128. IR::IndirOpnd *indirOpnd;
  129. switch (nopSize)
  130. {
  131. case 1:
  132. // nop
  133. nopInstr = IR::Instr::New(Js::OpCode::NOP, instr->m_func);
  134. break;
  135. case 2:
  136. // mov edi, edi ; 2 bytes
  137. regOpnd = IR::RegOpnd::New(nullptr, RegEDI, TyInt32, instr->m_func);
  138. nopInstr = IR::Instr::New(Js::OpCode::MOV, regOpnd, regOpnd, instr->m_func);
  139. break;
  140. case 3:
  141. // lea ecx, [ecx+00] ; 3 bytes
  142. regOpnd = IR::RegOpnd::New(nullptr, RegECX, TyInt32, instr->m_func);
  143. indirOpnd = IR::IndirOpnd::New(regOpnd, (int32)0, TyInt32, instr->m_func);
  144. nopInstr = IR::Instr::New(Js::OpCode::LEA, regOpnd, indirOpnd, instr->m_func);
  145. break;
  146. case 4:
  147. // lea esp, [esp+00] ; 4 bytes
  148. regOpnd = IR::RegOpnd::New(nullptr, RegESP, TyInt32, instr->m_func);
  149. indirOpnd = IR::IndirOpnd::New(regOpnd, (int32)0, TyInt32, instr->m_func);
  150. nopInstr = IR::Instr::New(Js::OpCode::LEA, regOpnd, indirOpnd, instr->m_func);
  151. break;
  152. default:
  153. Assert(false);
  154. break;
  155. }
  156. instr->InsertBefore(nopInstr);
  157. }
  158. #endif
  159. #elif defined(_M_ARM)
  160. // Can't insert 3 bytes, must choose between 2 and 4.
  161. IR::Instr *nopInstr = nullptr;
  162. switch(nopSize)
  163. {
  164. case 1:
  165. case 2:
  166. nopInstr = IR::Instr::New(Js::OpCode::NOP, instr->m_func);
  167. break;
  168. case 3:
  169. case 4:
  170. nopInstr = IR::Instr::New(Js::OpCode::NOP_W, instr->m_func);
  171. break;
  172. default:
  173. Assert(false);
  174. break;
  175. }
  176. instr->InsertBefore(nopInstr);
  177. #else
  178. AssertMsg(false, "Unimplemented");
  179. #endif
  180. }
  181. bool
  182. Security::DontEncode(IR::Opnd *opnd)
  183. {
  184. switch (opnd->GetKind())
  185. {
  186. case IR::OpndKindIntConst:
  187. {
  188. IR::IntConstOpnd *intConstOpnd = opnd->AsIntConstOpnd();
  189. return intConstOpnd->m_dontEncode;
  190. }
  191. case IR::OpndKindAddr:
  192. {
  193. IR::AddrOpnd *addrOpnd = opnd->AsAddrOpnd();
  194. return (addrOpnd->m_dontEncode ||
  195. !addrOpnd->IsVar() ||
  196. addrOpnd->m_address == nullptr ||
  197. !Js::TaggedNumber::Is(addrOpnd->m_address));
  198. }
  199. case IR::OpndKindHelperCall:
  200. // Never encode helper call addresses, as these are always internal constants.
  201. return true;
  202. }
  203. return false;
  204. }
  205. void
  206. Security::EncodeOpnd(IR::Instr *instr, IR::Opnd *opnd)
  207. {
  208. IR::RegOpnd *newOpnd;
  209. bool isSrc2 = false;
  210. if (Security::DontEncode(opnd))
  211. {
  212. return;
  213. }
  214. switch(opnd->GetKind())
  215. {
  216. case IR::OpndKindIntConst:
  217. {
  218. IR::IntConstOpnd *intConstOpnd = opnd->AsIntConstOpnd();
  219. if (!this->IsLargeConstant(intConstOpnd->AsInt32()))
  220. {
  221. return;
  222. }
  223. if (opnd != instr->GetSrc1())
  224. {
  225. Assert(opnd == instr->GetSrc2());
  226. isSrc2 = true;
  227. instr->UnlinkSrc2();
  228. }
  229. else
  230. {
  231. instr->UnlinkSrc1();
  232. }
  233. #if DBG_DUMP || defined(ENABLE_IR_VIEWER)
  234. intConstOpnd->decodedValue = intConstOpnd->GetValue();
  235. #endif
  236. intConstOpnd->SetValue(EncodeValue(instr, intConstOpnd, intConstOpnd->GetValue(), &newOpnd));
  237. }
  238. break;
  239. case IR::OpndKindAddr:
  240. {
  241. IR::AddrOpnd *addrOpnd = opnd->AsAddrOpnd();
  242. // Only encode large constants. Small ones don't allow control of enough bits
  243. if (Js::TaggedInt::Is(addrOpnd->m_address) && !this->IsLargeConstant(Js::TaggedInt::ToInt32(addrOpnd->m_address)))
  244. {
  245. return;
  246. }
  247. if (opnd != instr->GetSrc1())
  248. {
  249. Assert(opnd == instr->GetSrc2());
  250. isSrc2 = true;
  251. instr->UnlinkSrc2();
  252. }
  253. else
  254. {
  255. instr->UnlinkSrc1();
  256. }
  257. #ifdef _M_X64
  258. addrOpnd->SetEncodedValue((Js::Var)this->EncodeAddress(instr, addrOpnd, (size_t)addrOpnd->m_address, &newOpnd), addrOpnd->GetAddrOpndKind());
  259. #else
  260. addrOpnd->SetEncodedValue((Js::Var)this->EncodeValue(instr, addrOpnd, (IntConstType)addrOpnd->m_address, &newOpnd), addrOpnd->GetAddrOpndKind());
  261. #endif
  262. }
  263. break;
  264. case IR::OpndKindIndir:
  265. {
  266. IR::IndirOpnd *indirOpnd = opnd->AsIndirOpnd();
  267. if (!this->IsLargeConstant(indirOpnd->GetOffset()) || indirOpnd->m_dontEncode)
  268. {
  269. return;
  270. }
  271. AssertMsg(indirOpnd->GetIndexOpnd() == nullptr, "Code currently doesn't support indir with offset and indexOpnd");
  272. IR::IntConstOpnd *indexOpnd = IR::IntConstOpnd::New(indirOpnd->GetOffset(), TyInt32, instr->m_func);
  273. #if DBG_DUMP || defined(ENABLE_IR_VIEWER)
  274. indexOpnd->decodedValue = indexOpnd->GetValue();
  275. #endif
  276. indexOpnd->SetValue(EncodeValue(instr, indexOpnd, indexOpnd->GetValue(), &newOpnd));
  277. indirOpnd->SetOffset(0);
  278. indirOpnd->SetIndexOpnd(newOpnd);
  279. }
  280. return;
  281. default:
  282. return;
  283. }
  284. IR::Opnd *dst = instr->GetDst();
  285. if (dst)
  286. {
  287. #if _M_X64
  288. // Ensure the left and right operand has the same type (that might not be true for constants on x64)
  289. newOpnd = (IR::RegOpnd *)newOpnd->UseWithNewType(dst->GetType(), instr->m_func);
  290. #endif
  291. if (dst->IsRegOpnd())
  292. {
  293. IR::RegOpnd *dstRegOpnd = dst->AsRegOpnd();
  294. StackSym *dstSym = dstRegOpnd->m_sym;
  295. if (dstSym)
  296. {
  297. dstSym->m_isConst = false;
  298. dstSym->m_isIntConst = false;
  299. dstSym->m_isTaggableIntConst = false;
  300. dstSym->m_isFltConst = false;
  301. }
  302. }
  303. }
  304. LowererMD::ImmedSrcToReg(instr, newOpnd, isSrc2 ? 2 : 1);
  305. }
  306. IntConstType
  307. Security::EncodeValue(IR::Instr *instr, IR::Opnd *opnd, IntConstType constValue, IR::RegOpnd **pNewOpnd)
  308. {
  309. if (opnd->GetType() == TyInt32 || opnd->GetType() == TyInt16 || opnd->GetType() == TyInt8
  310. #if _M_IX86
  311. || opnd->GetType() == TyVar
  312. #endif
  313. )
  314. {
  315. int32 cookie = (int32)Math::Rand();
  316. IR::RegOpnd *regOpnd = IR::RegOpnd::New(StackSym::New(opnd->GetType(), instr->m_func), opnd->GetType(), instr->m_func);
  317. IR::Instr * instrNew = LowererMD::CreateAssign(regOpnd, opnd, instr);
  318. IR::IntConstOpnd * cookieOpnd = IR::IntConstOpnd::New(cookie, TyInt32, instr->m_func);
  319. #if DBG_DUMP
  320. cookieOpnd->name = L"cookie";
  321. #endif
  322. instrNew = IR::Instr::New(Js::OpCode::Xor_I4, regOpnd, regOpnd, cookieOpnd, instr->m_func);
  323. instr->InsertBefore(instrNew);
  324. LowererMD::EmitInt4Instr(instrNew);
  325. StackSym * stackSym = regOpnd->m_sym;
  326. Assert(!stackSym->m_isSingleDef);
  327. Assert(stackSym->m_instrDef == nullptr);
  328. stackSym->m_isEncodedConstant = true;
  329. stackSym->constantValue = (int32)constValue;
  330. *pNewOpnd = regOpnd;
  331. int32 value = (int32)constValue;
  332. value = value ^ cookie;
  333. return value;
  334. }
  335. else if (opnd->GetType() == TyUint32 || opnd->GetType() == TyUint16 || opnd->GetType() == TyUint8)
  336. {
  337. uint32 cookie = (uint32)Math::Rand();
  338. IR::RegOpnd *regOpnd = IR::RegOpnd::New(StackSym::New(opnd->GetType(), instr->m_func), opnd->GetType(), instr->m_func);
  339. IR::Instr * instrNew = LowererMD::CreateAssign(regOpnd, opnd, instr);
  340. IR::IntConstOpnd * cookieOpnd = IR::IntConstOpnd::New(cookie, TyUint32, instr->m_func);
  341. #if DBG_DUMP
  342. cookieOpnd->name = L"cookie";
  343. #endif
  344. instrNew = IR::Instr::New(Js::OpCode::Xor_I4, regOpnd, regOpnd, cookieOpnd, instr->m_func);
  345. instr->InsertBefore(instrNew);
  346. LowererMD::EmitInt4Instr(instrNew);
  347. StackSym * stackSym = regOpnd->m_sym;
  348. Assert(!stackSym->m_isSingleDef);
  349. Assert(stackSym->m_instrDef == nullptr);
  350. stackSym->m_isEncodedConstant = true;
  351. stackSym->constantValue = (uint32)constValue;
  352. *pNewOpnd = regOpnd;
  353. uint32 value = (uint32)constValue;
  354. value = value ^ cookie;
  355. return (IntConstType)value;
  356. }
  357. else
  358. {
  359. #ifdef _M_X64
  360. return this->EncodeAddress(instr, opnd, constValue, pNewOpnd);
  361. #else
  362. Assert(false);
  363. return 0;
  364. #endif
  365. }
  366. }
  367. #ifdef _M_X64
  368. size_t
  369. Security::EncodeAddress(IR::Instr *instr, IR::Opnd *opnd, size_t value, IR::RegOpnd **pNewOpnd)
  370. {
  371. IR::Instr *instrNew = nullptr;
  372. IR::RegOpnd *regOpnd = IR::RegOpnd::New(TyMachReg, instr->m_func);
  373. instrNew = LowererMD::CreateAssign(regOpnd, opnd, instr);
  374. size_t cookie = (size_t)Math::Rand();
  375. IR::AddrOpnd *cookieOpnd = IR::AddrOpnd::New((Js::Var)cookie, IR::AddrOpndKindConstant, instr->m_func);
  376. instrNew = IR::Instr::New(Js::OpCode::XOR, regOpnd, regOpnd, cookieOpnd, instr->m_func);
  377. instr->InsertBefore(instrNew);
  378. LowererMD::Legalize(instrNew);
  379. StackSym * stackSym = regOpnd->m_sym;
  380. Assert(!stackSym->m_isSingleDef);
  381. Assert(stackSym->m_instrDef == nullptr);
  382. stackSym->m_isEncodedConstant = true;
  383. stackSym->constantValue = value;
  384. *pNewOpnd = regOpnd;
  385. return value ^ cookie;
  386. }
  387. #endif