StackProtector.cpp revision 7205677a46d02867004826218942dab3b466c926
18c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen//===-- StackProtector.cpp - Stack Protector Insertion --------------------===// 28c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen// 3ecc6b8c25a7e8d9d2b78889e88224354a1cc3160tuexen// The LLVM Compiler Infrastructure 4ecc6b8c25a7e8d9d2b78889e88224354a1cc3160tuexen// 58c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen// This file is distributed under the University of Illinois Open Source 68c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen// License. See LICENSE.TXT for details. 78c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen// 88c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen//===----------------------------------------------------------------------===// 98c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen// 100ac02f34d6041cd0018437596a5a9a94685e6919tuexen// This pass inserts stack protectors into functions which need them. A variable 118c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen// with a random value in it is stored onto the stack before the local variables 128c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen// are allocated. Upon exiting the block, the stored value is checked. If it's 138c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen// changed, then there was some sort of violation and the program aborts. 140ac02f34d6041cd0018437596a5a9a94685e6919tuexen// 158c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen//===----------------------------------------------------------------------===// 168c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 178c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#define DEBUG_TYPE "stack-protector" 188c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/CodeGen/Passes.h" 198c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/Constants.h" 208c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/DerivedTypes.h" 218c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/Function.h" 228c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/Instructions.h" 238c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/Intrinsics.h" 248c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/Module.h" 258c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/Pass.h" 268c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/Support/CommandLine.h" 278c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/Target/TargetData.h" 288c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen#include "llvm/Target/TargetLowering.h" 298c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenusing namespace llvm; 308c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 318c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen// SSPBufferSize - The lower bound for a buffer to be considered for stack 328c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen// smashing protection. 338c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenstatic cl::opt<unsigned> 348c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenSSPBufferSize("stack-protector-buffer-size", cl::init(8), 351b77b4778bf4ea247d4e18d1694d8dc2d3d1d150t cl::desc("The lower bound for a buffer to be considered for " 368c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen "stack smashing protection.")); 37ecc6b8c25a7e8d9d2b78889e88224354a1cc3160tuexen 388c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexennamespace { 398c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen class VISIBILITY_HIDDEN StackProtector : public FunctionPass { 408c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// Level - The level of stack protection. 418c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen SSP::StackProtectorLevel Level; 428c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 438c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// TLI - Keep a pointer of a TargetLowering to consult for determining 448c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// target type sizes. 458c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen const TargetLowering *TLI; 468c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 478c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen Function *F; 488c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen Module *M; 498c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 508c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// InsertStackProtectors - Insert code into the prologue and epilogue of 518c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// the function. 528c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// 538c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// - The prologue code loads and stores the stack guard onto the stack. 548c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// - The epilogue checks the value stored in the prologue against the 558c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// original value. It calls __stack_chk_fail if they differ. 568c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen bool InsertStackProtectors(); 578c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 588c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// CreateFailBB - Create a basic block to jump to when the stack protector 59e056c2b95d882d42490e3cc86c7fbd6134c79b32tuexen /// check fails. 608c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen BasicBlock *CreateFailBB(); 618c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 628c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// RequiresStackProtector - Check whether or not this function needs a 638c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen /// stack protector based upon the stack protector level. 648c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen bool RequiresStackProtector() const; 658c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen public: 668c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen static char ID; // Pass identification, replacement for typeid. 678c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen StackProtector() : FunctionPass(&ID), Level(SSP::OFF), TLI(0) {} 688c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen StackProtector(SSP::StackProtectorLevel lvl, const TargetLowering *tli) 698c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen : FunctionPass(&ID), Level(lvl), TLI(tli) {} 708c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 718c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen virtual bool runOnFunction(Function &Fn); 728c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen }; 738c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen} // end anonymous namespace 748c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 758c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenchar StackProtector::ID = 0; 768c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenstatic RegisterPass<StackProtector> 778c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenX("stack-protector", "Insert stack protectors"); 788c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 798c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenFunctionPass *llvm::createStackProtectorPass(SSP::StackProtectorLevel lvl, 808c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen const TargetLowering *tli) { 818c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen return new StackProtector(lvl, tli); 828c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen} 838c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 848c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenbool StackProtector::runOnFunction(Function &Fn) { 858c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen F = &Fn; 868c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen M = F->getParent(); 878c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 8868beeca578347438d9c434680197647ed551935ft if (!RequiresStackProtector()) return false; 898c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 908c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen return InsertStackProtectors(); 918c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen} 92b7ebbfc1bd4420174e2a11b3b2bfd64281f44032tuexen 938c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// InsertStackProtectors - Insert code into the prologue and epilogue of the 948c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// function. 958c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// 968c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// - The prologue code loads and stores the stack guard onto the stack. 978c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// - The epilogue checks the value stored in the prologue against the original 988c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// value. It calls __stack_chk_fail if they differ. 998c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenbool StackProtector::InsertStackProtectors() { 1008c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // Loop through the basic blocks that have return instructions. Convert this: 101b7ebbfc1bd4420174e2a11b3b2bfd64281f44032tuexen // 1028c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // return: 1038c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // ... 1048c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // ret ... 1058c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // 1068c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // into this: 1078c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // 1088c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // return: 1098c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // ... 1108c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // %1 = load __stack_chk_guard 1118c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // %2 = load <stored stack guard> 1128c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // %3 = cmp i1 %1, %2 1138c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // br i1 %3, label %SP_return, label %CallStackCheckFailBlk 1148c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // 1158c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // SP_return: 11668beeca578347438d9c434680197647ed551935ft // ret ... 1178c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // 1188c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // CallStackCheckFailBlk: 1198c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // call void @__stack_chk_fail() 120b7ebbfc1bd4420174e2a11b3b2bfd64281f44032tuexen // unreachable 1218c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // 1228c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen BasicBlock *FailBB = 0; // The basic block to jump to if check fails. 1238c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen AllocaInst *AI = 0; // Place on stack that stores the stack guard. 1248c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen Constant *StackGuardVar = 0; // The stack guard variable. 1258c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 1268c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen for (Function::iterator I = F->begin(), E = F->end(); I != E; ) { 1278c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen BasicBlock *BB = I; 1288c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 1298c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen if (ReturnInst *RI = dyn_cast<ReturnInst>(BB->getTerminator())) { 1308c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen if (!FailBB) { 1318c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // Insert code into the entry block that stores the __stack_chk_guard 1328c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // variable onto the stack. 1338c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen PointerType *PtrTy = PointerType::getUnqual(Type::Int8Ty); 1348c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen StackGuardVar = M->getOrInsertGlobal("__stack_chk_guard", PtrTy); 1358c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 1368c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen BasicBlock &Entry = F->getEntryBlock(); 137b7ebbfc1bd4420174e2a11b3b2bfd64281f44032tuexen Instruction *InsPt = &Entry.front(); 1388c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 1398c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen AI = new AllocaInst(PtrTy, "StackGuardSlot", InsPt); 1408c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen LoadInst *LI = new LoadInst(StackGuardVar, "StackGuard", false, InsPt); 1418c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 1428c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen Value *Args[] = { LI, AI }; 1438c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen CallInst:: 1448c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen Create(Intrinsic::getDeclaration(M, Intrinsic::stackprotector_create), 1458c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen &Args[0], array_endof(Args), "", InsPt); 1468c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 147b7ebbfc1bd4420174e2a11b3b2bfd64281f44032tuexen // Create the basic block to jump to when the guard check fails. 1488c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen FailBB = CreateFailBB(); 1498c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen } 1508c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 151e056c2b95d882d42490e3cc86c7fbd6134c79b32tuexen Function::iterator InsPt = BB; ++InsPt; // Insertion point for new BB. 1528c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen ++I; // Skip to the next block so that we don't resplit the return block. 1538c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 1548c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // Split the basic block before the return instruction. 1558c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen BasicBlock *NewBB = BB->splitBasicBlock(RI, "SP_return"); 156bfb1bf7e665a02b48026482bf33d05c83dfad73bt 1578c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // Move the newly created basic block to the point right after the old basic 158facb9a509f7ae3621b7d2771319811985d9c46b7t // block so that it's in the "fall through" position. 159facb9a509f7ae3621b7d2771319811985d9c46b7t NewBB->removeFromParent(); 160facb9a509f7ae3621b7d2771319811985d9c46b7t F->getBasicBlockList().insert(InsPt, NewBB); 161facb9a509f7ae3621b7d2771319811985d9c46b7t 1628c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // Generate the stack protector instructions in the old basic block. 1638c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen LoadInst *LI1 = new LoadInst(StackGuardVar, "", false, BB); 1648c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen CallInst *CI = CallInst:: 1658c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen Create(Intrinsic::getDeclaration(M, Intrinsic::stackprotector_check), 1668c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen AI, "", BB); 1678c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen ICmpInst *Cmp = new ICmpInst(CmpInst::ICMP_EQ, CI, LI1, "", BB); 1688c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen BranchInst::Create(NewBB, FailBB, Cmp, BB); 1698c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen } else { 1708c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen ++I; 1718c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen } 1728c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen } 1738c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 1748c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // Return if we didn't modify any basic blocks. I.e., there are no return 1758c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // statements in the function. 1768c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen if (!FailBB) return false; 177facb9a509f7ae3621b7d2771319811985d9c46b7t 1788c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen return true; 179facb9a509f7ae3621b7d2771319811985d9c46b7t} 180bfb1bf7e665a02b48026482bf33d05c83dfad73bt 1818c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// CreateFailBB - Create a basic block to jump to when the stack protector 1828c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// check fails. 1838c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenBasicBlock *StackProtector::CreateFailBB() { 1848c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen BasicBlock *FailBB = BasicBlock::Create("CallStackCheckFailBlk", F); 1858c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen Constant *StackChkFail = 186b7ebbfc1bd4420174e2a11b3b2bfd64281f44032tuexen M->getOrInsertFunction("__stack_chk_fail", Type::VoidTy, NULL); 1878c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen CallInst::Create(StackChkFail, "", FailBB); 1888c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen new UnreachableInst(FailBB); 189bfb1bf7e665a02b48026482bf33d05c83dfad73bt return FailBB; 1908c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen} 1918c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 1927b0ab5c1c85787647428afafeff9491e9b6a60c7t/// RequiresStackProtector - Check whether or not this function needs a stack 1938c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// protector based upon the stack protector level. The heuristic we use is to 1948c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// add a guard variable to functions that call alloca, and functions with 1958c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen/// buffers larger than 8 bytes. 1968c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexenbool StackProtector::RequiresStackProtector() const { 1978c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen switch (Level) { 1988c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen default: return false; 1998c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen case SSP::ALL: return true; 2008c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen case SSP::SOME: { 2018c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen const TargetData *TD = TLI->getTargetData(); 2028c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 2038c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen for (Function::iterator I = F->begin(), E = F->end(); I != E; ++I) { 2048c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen BasicBlock *BB = I; 2058c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 2068c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen for (BasicBlock::iterator 2078c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen II = BB->begin(), IE = BB->end(); II != IE; ++II) 2088c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen if (AllocaInst *AI = dyn_cast<AllocaInst>(II)) { 2098c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen if (AI->isArrayAllocation()) 2108c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // This is a call to alloca with a variable size. Emit stack 211b7ebbfc1bd4420174e2a11b3b2bfd64281f44032tuexen // protectors. 2128c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen return true; 2138c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 214bfb1bf7e665a02b48026482bf33d05c83dfad73bt if (const ArrayType *AT = dyn_cast<ArrayType>(AI->getAllocatedType())) 2158c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // If an array has more than 8 bytes of allocated space, then we 2168c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen // emit stack protectors. 2177b0ab5c1c85787647428afafeff9491e9b6a60c7t if (SSPBufferSize <= TD->getABITypeSize(AT)) 2188c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen return true; 2198c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen } 2208c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen } 2218c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen 2228c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen return false; 2238c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen } 2248c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen } 2258c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen} 2268c8a4cae58a2deed148551d56cb1ab315a55dbbdtuexen