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 (
  220. #if TARGET_64
  221. IRType_IsInt64(intConstOpnd->GetType()) ? !this->IsLargeConstant(intConstOpnd->GetValue()) :
  222. #endif
  223. !this->IsLargeConstant(intConstOpnd->AsInt32()))
  224. {
  225. return;
  226. }
  227. if (opnd != instr->GetSrc1())
  228. {
  229. Assert(opnd == instr->GetSrc2());
  230. isSrc2 = true;
  231. instr->UnlinkSrc2();
  232. }
  233. else
  234. {
  235. instr->UnlinkSrc1();
  236. }
  237. #if DBG_DUMP || defined(ENABLE_IR_VIEWER)
  238. intConstOpnd->decodedValue = intConstOpnd->GetValue();
  239. #endif
  240. intConstOpnd->SetValue(EncodeValue(instr, intConstOpnd, intConstOpnd->GetValue(), &newOpnd));
  241. }
  242. break;
  243. case IR::OpndKindAddr:
  244. {
  245. IR::AddrOpnd *addrOpnd = opnd->AsAddrOpnd();
  246. // Only encode large constants. Small ones don't allow control of enough bits
  247. if (Js::TaggedInt::Is(addrOpnd->m_address) && !this->IsLargeConstant(Js::TaggedInt::ToInt32(addrOpnd->m_address)))
  248. {
  249. return;
  250. }
  251. if (opnd != instr->GetSrc1())
  252. {
  253. Assert(opnd == instr->GetSrc2());
  254. isSrc2 = true;
  255. instr->UnlinkSrc2();
  256. }
  257. else
  258. {
  259. instr->UnlinkSrc1();
  260. }
  261. addrOpnd->SetEncodedValue((Js::Var)this->EncodeValue(instr, addrOpnd, (IntConstType)addrOpnd->m_address, &newOpnd), addrOpnd->GetAddrOpndKind());
  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_isInt64Const = false;
  300. dstSym->m_isTaggableIntConst = false;
  301. dstSym->m_isFltConst = false;
  302. }
  303. }
  304. }
  305. LowererMD::ImmedSrcToReg(instr, newOpnd, isSrc2 ? 2 : 1);
  306. }
  307. IntConstType
  308. Security::EncodeValue(IR::Instr *instr, IR::Opnd *opnd, IntConstType constValue, IR::RegOpnd **pNewOpnd)
  309. {
  310. if (opnd->GetType() == TyInt32 || opnd->GetType() == TyInt16 || opnd->GetType() == TyInt8
  311. #if _M_IX86
  312. || opnd->GetType() == TyVar
  313. #endif
  314. )
  315. {
  316. int32 cookie = (int32)Math::Rand();
  317. IR::RegOpnd *regOpnd = IR::RegOpnd::New(StackSym::New(TyInt32, instr->m_func), TyInt32, instr->m_func);
  318. IR::Instr * instrNew = LowererMD::CreateAssign(regOpnd, opnd, instr);
  319. IR::IntConstOpnd * cookieOpnd = IR::IntConstOpnd::New(cookie, TyInt32, instr->m_func);
  320. #if DBG_DUMP
  321. cookieOpnd->name = _u("cookie");
  322. #endif
  323. instrNew = IR::Instr::New(Js::OpCode::Xor_I4, regOpnd, regOpnd, cookieOpnd, instr->m_func);
  324. instr->InsertBefore(instrNew);
  325. LowererMD::EmitInt4Instr(instrNew);
  326. StackSym * stackSym = regOpnd->m_sym;
  327. Assert(!stackSym->m_isSingleDef);
  328. Assert(stackSym->m_instrDef == nullptr);
  329. stackSym->m_isEncodedConstant = true;
  330. stackSym->constantValue = (int32)constValue;
  331. *pNewOpnd = regOpnd;
  332. int32 value = (int32)constValue;
  333. value = value ^ cookie;
  334. return value;
  335. }
  336. else if (opnd->GetType() == TyUint32 || opnd->GetType() == TyUint16 || opnd->GetType() == TyUint8)
  337. {
  338. uint32 cookie = (uint32)Math::Rand();
  339. IR::RegOpnd *regOpnd = IR::RegOpnd::New(StackSym::New(TyUint32, instr->m_func), TyUint32, instr->m_func);
  340. IR::Instr * instrNew = LowererMD::CreateAssign(regOpnd, opnd, instr);
  341. IR::IntConstOpnd * cookieOpnd = IR::IntConstOpnd::New(cookie, TyUint32, instr->m_func);
  342. #if DBG_DUMP
  343. cookieOpnd->name = _u("cookie");
  344. #endif
  345. instrNew = IR::Instr::New(Js::OpCode::Xor_I4, regOpnd, regOpnd, cookieOpnd, instr->m_func);
  346. instr->InsertBefore(instrNew);
  347. LowererMD::EmitInt4Instr(instrNew);
  348. StackSym * stackSym = regOpnd->m_sym;
  349. Assert(!stackSym->m_isSingleDef);
  350. Assert(stackSym->m_instrDef == nullptr);
  351. stackSym->m_isEncodedConstant = true;
  352. stackSym->constantValue = (uint32)constValue;
  353. *pNewOpnd = regOpnd;
  354. uint32 value = (uint32)constValue;
  355. value = value ^ cookie;
  356. return (IntConstType)value;
  357. }
  358. else
  359. {
  360. #ifdef _M_X64
  361. return this->EncodeAddress(instr, opnd, constValue, pNewOpnd);
  362. #else
  363. Assert(false);
  364. return 0;
  365. #endif
  366. }
  367. }
  368. #ifdef _M_X64
  369. size_t
  370. Security::EncodeAddress(IR::Instr *instr, IR::Opnd *opnd, size_t value, IR::RegOpnd **pNewOpnd)
  371. {
  372. IR::Instr *instrNew = nullptr;
  373. IR::RegOpnd *regOpnd = IR::RegOpnd::New(TyMachReg, instr->m_func);
  374. instrNew = LowererMD::CreateAssign(regOpnd, opnd, instr);
  375. size_t cookie = (size_t)Math::Rand();
  376. IR::IntConstOpnd *cookieOpnd = IR::IntConstOpnd::New(cookie, TyMachReg, instr->m_func);
  377. instrNew = IR::Instr::New(Js::OpCode::XOR, regOpnd, regOpnd, cookieOpnd, instr->m_func);
  378. instr->InsertBefore(instrNew);
  379. LowererMD::Legalize(instrNew);
  380. StackSym * stackSym = regOpnd->m_sym;
  381. Assert(!stackSym->m_isSingleDef);
  382. Assert(stackSym->m_instrDef == nullptr);
  383. stackSym->m_isEncodedConstant = true;
  384. stackSym->constantValue = value;
  385. *pNewOpnd = regOpnd;
  386. return value ^ cookie;
  387. }
  388. #endif