/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #include "BytecodeVerifier.h" #include "FieldOrMethod.h" #include "MemoryAccess.h" #include "GraphUtils.h" static const VerificationEnv::BindingKind localAccessKinds[] = { VerificationEnv::bkInt, // bcILoad, bcIStore VerificationEnv::bkLong, // bcLLoad, bcLStore VerificationEnv::bkFloat, // bcFLoad, bcFStore VerificationEnv::bkDouble, // bcDLoad, bcDStore VerificationEnv::bkAddr, // bcALoad, bcAStore VerificationEnv::bkInt, // bcILoad_0, bcIStore_0 VerificationEnv::bkInt, // bcILoad_1, bcIStore_1 VerificationEnv::bkInt, // bcILoad_2, bcIStore_2 VerificationEnv::bkInt, // bcILoad_3, bcIStore_3 VerificationEnv::bkLong, // bcLLoad_0, bcLStore_0 VerificationEnv::bkLong, // bcLLoad_1, bcLStore_1 VerificationEnv::bkLong, // bcLLoad_2, bcLStore_2 VerificationEnv::bkLong, // bcLLoad_3, bcLStore_3 VerificationEnv::bkFloat, // bcFLoad_0, bcFStore_0 VerificationEnv::bkFloat, // bcFLoad_1, bcFStore_1 VerificationEnv::bkFloat, // bcFLoad_2, bcFStore_2 VerificationEnv::bkFloat, // bcFLoad_3, bcFStore_3 VerificationEnv::bkDouble, // bcDLoad_0, bcDStore_0 VerificationEnv::bkDouble, // bcDLoad_1, bcDStore_1 VerificationEnv::bkDouble, // bcDLoad_2, bcDStore_2 VerificationEnv::bkDouble, // bcDLoad_3, bcDStore_3 VerificationEnv::bkAddr, // bcALoad_0, bcAStore_0 VerificationEnv::bkAddr, // bcALoad_1, bcAStore_1 VerificationEnv::bkAddr, // bcALoad_2, bcAStore_2 VerificationEnv::bkAddr // bcALoad_3, bcAStore_3 }; static const VerificationEnv::BindingKind arrayAccessKinds[] = { VerificationEnv::bkInt, // bcIALoad, bcIAStore, bcIReturn VerificationEnv::bkLong, // bcLALoad, bcLAStore, bcLReturn VerificationEnv::bkFloat, // bcFALoad, bcFAStore, bcFReturn VerificationEnv::bkDouble, // bcDALoad, bcDAStore, bcDReturn VerificationEnv::bkAddr, // bcAALoad, bcAAStore, bcAReturn VerificationEnv::bkInt, // bcBALoad, bcBAStore VerificationEnv::bkInt, // bcCALoad, bcCAStore VerificationEnv::bkInt // bcSALoad, bcSAStore }; static const VerificationEnv::BindingKind bytecodeSignatures[][3] = { // Argument 1 Argument 2 Result {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIAdd {VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLAdd {VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkFloat}, // bcFAdd {VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkDouble}, // bcDAdd {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcISub {VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLSub {VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkFloat}, // bcFSub {VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkDouble}, // bcDSub {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIMul {VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLMul {VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkFloat}, // bcFMul {VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkDouble}, // bcDMul {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIDiv {VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLDiv {VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkFloat}, // bcFDiv {VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkDouble}, // bcDDiv {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIRem {VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLRem {VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkFloat}, // bcFRem {VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkDouble}, // bcDRem {VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcINeg {VerificationEnv::bkLong, VerificationEnv::bkVoid, VerificationEnv::bkLong}, // bcLNeg {VerificationEnv::bkFloat, VerificationEnv::bkVoid, VerificationEnv::bkFloat}, // bcFNeg {VerificationEnv::bkDouble, VerificationEnv::bkVoid, VerificationEnv::bkDouble}, // bcDNeg {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIShl {VerificationEnv::bkLong, VerificationEnv::bkInt, VerificationEnv::bkLong}, // bcLShl {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIShr {VerificationEnv::bkLong, VerificationEnv::bkInt, VerificationEnv::bkLong}, // bcLShr {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIUShr {VerificationEnv::bkLong, VerificationEnv::bkInt, VerificationEnv::bkLong}, // bcLUShr {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIAnd {VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLAnd {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIOr {VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLOr {VerificationEnv::bkInt, VerificationEnv::bkInt, VerificationEnv::bkInt}, // bcIXor {VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkLong}, // bcLXor {VerificationEnv::bkVoid, VerificationEnv::bkVoid, VerificationEnv::bkVoid}, // bcIInc {VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkLong}, // bcI2L {VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkFloat}, // bcI2F {VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkDouble}, // bcI2D {VerificationEnv::bkLong, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcL2I {VerificationEnv::bkLong, VerificationEnv::bkVoid, VerificationEnv::bkFloat}, // bcL2F {VerificationEnv::bkLong, VerificationEnv::bkVoid, VerificationEnv::bkDouble}, // bcL2D {VerificationEnv::bkFloat, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcF2I {VerificationEnv::bkFloat, VerificationEnv::bkVoid, VerificationEnv::bkLong}, // bcF2L {VerificationEnv::bkFloat, VerificationEnv::bkVoid, VerificationEnv::bkDouble}, // bcF2D {VerificationEnv::bkDouble, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcD2I {VerificationEnv::bkDouble, VerificationEnv::bkVoid, VerificationEnv::bkLong}, // bcD2L {VerificationEnv::bkDouble, VerificationEnv::bkVoid, VerificationEnv::bkFloat}, // bcD2F {VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcInt2Byte {VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcInt2Char {VerificationEnv::bkInt, VerificationEnv::bkVoid, VerificationEnv::bkInt}, // bcInt2Short {VerificationEnv::bkLong, VerificationEnv::bkLong, VerificationEnv::bkInt}, // bcLCmp {VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkInt}, // bcFCmpL {VerificationEnv::bkFloat, VerificationEnv::bkFloat, VerificationEnv::bkInt}, // bcFCmpG {VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkInt}, // bcDCmpL {VerificationEnv::bkDouble, VerificationEnv::bkDouble, VerificationEnv::bkInt} // bcDCmpG }; // ---------------------------------------------------------------------------- // SubroutineCallSiteList // // Return true if this Association is less than a2 in the "dictionary" ordering with // the subroutine being the major key and callSite being the minor key. A nil callSite // is considered to be less than ony other callSite. // bool SubroutineCallSiteList::Association::operator<(const Association &a2) const { // According to ANSI C++, we can't compare subroutine addresses directly because // they are not elements of the same array. Instead, we compare their bytecodesBegin // values, which do point into the same array. const bytecode *begin1 = subroutine.bytecodesBegin; const bytecode *begin2 = a2.subroutine.bytecodesBegin; return begin1 < begin2 || begin1 == begin2 && a2.callSite && (!callSite || callSite->bytecodesBegin < a2.callSite->bytecodesBegin); } // // Create an Iterator that will return all known call sites for the given subroutine. // SubroutineCallSiteList::Iterator::Iterator(SubroutineCallSiteList &csList, BytecodeBlock &subroutine): subroutine(subroutine) { Association a(subroutine); Node *n = csList.tree.findAfter(a); if (n && &n->getKey().subroutine != &subroutine) n = 0; node = n; } // // Advance the iterator to the next call site. // void SubroutineCallSiteList::Iterator::operator++() { assert(node); Node *n = node->next(); if (n && &n->getKey().subroutine != &subroutine) n = 0; node = n; } // // Add callSite to the list of subroutine's call sites if it is not there already. // Allocate additional storage from pool if needed. // void SubroutineCallSiteList::addAssociation(BytecodeBlock &subroutine, BytecodeBlock &callSite, Pool &pool) { Association a(subroutine, callSite); Node *where; bool right; if (!tree.find(a, where, right)) tree.attach(*new(pool) Node(a), where, right); } // // Remove callSite to the list of subroutine's call sites. callSite must have been // previously added (and not yet removed) to the list of subroutine's call sites. // void SubroutineCallSiteList::removeAssociation(BytecodeBlock &subroutine, BytecodeBlock &callSite) { Association a(subroutine, callSite); Node *n = tree.find(a); assert(n); tree.remove(*n); } // // Return a callSite and its subroutine of one association present in this // SubroutineCallSiteList. At the same time remove that association from this // SubroutineCallSiteList. Also return a flag that is true if this was the only // association for this subroutine. // If this SubroutineCallSiteList is already empty, return nil. // BytecodeBlock *SubroutineCallSiteList::popAssociation(BytecodeBlock *&subroutine, bool &onlyOneCallSite) { Node *n = tree.firstNode(); if (!n) { subroutine = 0; return 0; } subroutine = &n->getKey().subroutine; BytecodeBlock *callSite = n->getKey().callSite; Node *n2 = n->next(); onlyOneCallSite = !(n2 && &n2->getKey().subroutine == subroutine); tree.remove(*n); return callSite; } // ---------------------------------------------------------------------------- // BytecodeVerifier // // Initialize the BytecodeVerifier and set up all BasicBlocks in the bytecode // graph for verification. // BytecodeVerifier::BytecodeVerifier(BytecodeGraph &bytecodeGraph, Pool &envPool): VerificationEnv::Common(envPool, 0, bytecodeGraph.nLocals, bytecodeGraph.stackSize), bytecodeGraph(bytecodeGraph), classFileSummary(bytecodeGraph.classFileSummary) { BasicBlock **block = bytecodeGraph.getDFSList(); BasicBlock **dfsListEnd = block + bytecodeGraph.getDFSListLength(); while (block != dfsListEnd) (*block++)->initVerification(*this); } // // If stackNormalization is either sn0, sn1, or sn2, pop all except the top // zero, one, or two words from env, modifying it in place. // void BytecodeVerifier::normalizeEnv(VerificationEnv &env, BasicBlock::StackNormalization stackNormalization) { VerificationEnv::Binding tempBinding1; VerificationEnv::Binding tempBinding2; switch (stackNormalization) { case BasicBlock::snNoChange: return; case BasicBlock::sn0: break; case BasicBlock::sn1: tempBinding1 = env.pop1(); break; case BasicBlock::sn2: env.pop2(tempBinding1, tempBinding2); break; } env.dropAll(); switch (stackNormalization) { case BasicBlock::sn0: break; case BasicBlock::sn1: env.push1(tempBinding1); break; case BasicBlock::sn2: env.push2(tempBinding1, tempBinding2); break; default: trespass("Bad stackNormalization"); } } // // Intersect block's current incoming environment with predecessorEnv, updating the // block's incoming environment. Return true if both conditions below are true: // the block's incoming environment changed, and // the block's generation is already equal to the given generation. // When this method returns true, a block already examined on this pass through the // depth-first list has changed, so another pass is needed. // // Set this block's recompute flag if its incoming environment changed. // // If canOwnEnv is true, the BasicBlock can assume ownership of predecessorEnv // (thereby sometimes eliminating an unnecessary copy). // // Throw a verification error if the environments cannot be intersected. // bool BytecodeVerifier::predecessorChanged(BasicBlock &block, VerificationEnv &predecessorEnv, Uint32 generation, bool canOwnEnv) { assert(predecessorEnv.live()); BasicBlock::StackNormalization stackNormalization = block.stackNormalization; if (stackNormalization != BasicBlock::snNoChange && predecessorEnv.getSP() != (Uint32)(stackNormalization-BasicBlock::sn0)) // We have to normalize the stack inside predecessorEnv. if (canOwnEnv) normalizeEnv(predecessorEnv, stackNormalization); else { VerificationEnv envCopy(predecessorEnv); normalizeEnv(envCopy, stackNormalization); return predecessorChanged(block, envCopy, generation, true); } bool changed = block.getGeneration() == generation; if (block.getVerificationEnvIn().live()) if (block.getVerificationEnvIn().meet(predecessorEnv)) block.getRecompute() = true; else changed = false; else { block.getRecompute() = true; if (canOwnEnv) block.getVerificationEnvIn().move(predecessorEnv); else block.getVerificationEnvIn() = predecessorEnv; } return changed; } // // Call predecessorChanged on every BasicBlock between blocksBegin (inclusive) // and blocksEnd (exclusive). Return true if any predecessorChanged call returned // true. Of course, canOwnEnv applies only to the last call to predecessorChanged. // bool BytecodeVerifier::predecessorChanged(BasicBlock **blocksBegin, BasicBlock **blocksEnd, VerificationEnv &predecessorEnv, Uint32 generation, bool canOwnEnv) { bool changed = false; while (blocksBegin != blocksEnd) { BasicBlock *block = *blocksBegin++; changed |= predecessorChanged(*block, predecessorEnv, generation, blocksBegin == blocksEnd && canOwnEnv); } return changed; } // // Compute the verificationEnvIn, subroutineHeader, and subroutineRet fields of // the block. Verify the block's stack and type discpiline. Return true if // calling predecessorChanged on at least one of this block's successors returned true. // bool BytecodeVerifier::propagateDataflow(CatchBlock &block, Uint32 generation) { VerificationEnv env(block.getVerificationEnvIn()); env.push1(VerificationEnv::bkAddr); return predecessorChanged(block.getHandler(), env, generation, true); } // // Compute the verificationEnvIn, subroutineHeader, and subroutineRet fields of // the block. Verify the block's stack and type discipline. Return true if // calling predecessorChanged on at least one of this block's successors returned true. // bool BytecodeVerifier::propagateDataflow(BytecodeBlock &block, Uint32 generation) { VerificationEnv env(block.getVerificationEnvIn()); // Follow exception handlers from this block at the beginning of this block and // after each change of a local variable. bool changed = predecessorChanged(block.successorsEnd, block.handlersEnd, env, generation, false); const bytecode *const bytecodesEnd = block.bytecodesEnd; const bytecode *bc = block.bytecodesBegin; assert(bc != bytecodesEnd); while (bc != bytecodesEnd) { assert(bc < bytecodesEnd); bytecode opcode = *bc++; ValueKind vk; TypeKind tk; Value v; Uint32 i; ConstantPoolIndex cpi; const VerificationEnv::Binding *b; const VerificationEnv::BindingKind *bk; VerificationEnv::Binding b1; VerificationEnv::Binding b2; VerificationEnv::Binding b3; VerificationEnv::Binding b4; Signature sig; Uint32 offset; Uint32 interfaceNumber; addr address; bool isVolatile; bool isConstant; bool isInit; BytecodeBlock *sub; switch (opcode) { case bcNop: break; case bcAConst_Null: env.push1(VerificationEnv::bkAddr); break; case bcIConst_m1: case bcIConst_0: case bcIConst_1: case bcIConst_2: case bcIConst_3: case bcIConst_4: case bcIConst_5: pushInt: env.push1(VerificationEnv::bkInt); break; case bcLConst_0: case bcLConst_1: env.push2(VerificationEnv::bkLong); break; case bcFConst_0: case bcFConst_1: case bcFConst_2: env.push1(VerificationEnv::bkFloat); break; case bcDConst_0: case bcDConst_1: env.push2(VerificationEnv::bkDouble); break; case bcBIPush: bc++; goto pushInt; case bcSIPush: bc += 2; goto pushInt; case bcLdc: cpi = *(Uint8 *)bc; bc++; goto pushConst1; case bcLdc_W: cpi = readBigUHalfwordUnaligned(bc); bc += 2; pushConst1: vk = classFileSummary.lookupConstant(cpi, v); if (!isWordKind(vk)) verifyError(VerifyError::badType); env.push1(VerificationEnv::valueKindToBindingKind(vk)); break; case bcLdc2_W: vk = classFileSummary.lookupConstant(readBigUHalfwordUnaligned(bc), v); bc += 2; if (!isDoublewordKind(vk)) verifyError(VerifyError::badType); env.push2(VerificationEnv::valueKindToBindingKind(vk)); break; case bcILoad: case bcFLoad: case bcALoad: i = *(Uint8 *)bc; bc++; pushLoad1: b = &env.getLocal1(i); if (!b->hasKind(localAccessKinds[opcode - bcILoad])) verifyError(VerifyError::badType); env.push1(*b); break; case bcLLoad: case bcDLoad: i = *(Uint8 *)bc; bc++; pushLoad2: b = &env.getLocal2(i); if (!b->hasKind(localAccessKinds[opcode - bcILoad])) verifyError(VerifyError::badType); env.push2(*b); break; case bcILoad_0: case bcILoad_1: case bcILoad_2: case bcILoad_3: i = opcode - bcILoad_0; goto pushLoad1; case bcLLoad_0: case bcLLoad_1: case bcLLoad_2: case bcLLoad_3: i = opcode - bcLLoad_0; goto pushLoad2; case bcFLoad_0: case bcFLoad_1: case bcFLoad_2: case bcFLoad_3: i = opcode - bcFLoad_0; goto pushLoad1; case bcDLoad_0: case bcDLoad_1: case bcDLoad_2: case bcDLoad_3: i = opcode - bcDLoad_0; goto pushLoad2; case bcALoad_0: case bcALoad_1: case bcALoad_2: case bcALoad_3: i = opcode - bcALoad_0; goto pushLoad1; case bcIALoad: case bcLALoad: case bcFALoad: case bcDALoad: case bcAALoad: case bcBALoad: case bcCALoad: case bcSALoad: env.pop1(VerificationEnv::bkInt); env.pop1(VerificationEnv::bkAddr); env.push1or2(arrayAccessKinds[opcode - bcIALoad]); break; case bcIStore: case bcFStore: i = *(Uint8 *)bc; bc++; popStore1: env.setLocal1(i, env.pop1(localAccessKinds[opcode - bcIStore])); recheckHandlers: // We changed the local environment, so propagate the environment to the // exception handlers again. changed |= predecessorChanged(block.successorsEnd, block.handlersEnd, env, generation, false); break; case bcAStore: i = *(Uint8 *)bc; bc++; popAStore1: b = &env.pop1(); if (!(b->hasKind(VerificationEnv::bkAddr) || b->hasKind(VerificationEnv::bkReturn))) verifyError(VerifyError::badType); env.setLocal1(i, *b); goto recheckHandlers; case bcLStore: case bcDStore: i = *(Uint8 *)bc; bc++; popStore2: env.setLocal2(i, env.pop2(localAccessKinds[opcode - bcIStore])); goto recheckHandlers; case bcIStore_0: case bcIStore_1: case bcIStore_2: case bcIStore_3: i = opcode - bcIStore_0; goto popStore1; case bcLStore_0: case bcLStore_1: case bcLStore_2: case bcLStore_3: i = opcode - bcLStore_0; goto popStore2; case bcFStore_0: case bcFStore_1: case bcFStore_2: case bcFStore_3: i = opcode - bcFStore_0; goto popStore1; case bcDStore_0: case bcDStore_1: case bcDStore_2: case bcDStore_3: i = opcode - bcDStore_0; goto popStore2; case bcAStore_0: case bcAStore_1: case bcAStore_2: case bcAStore_3: i = opcode - bcAStore_0; goto popAStore1; case bcIAStore: case bcLAStore: case bcFAStore: case bcDAStore: case bcAAStore: case bcBAStore: case bcCAStore: case bcSAStore: env.pop1or2(arrayAccessKinds[opcode - bcIAStore]); env.pop1(VerificationEnv::bkInt); env.pop1(VerificationEnv::bkAddr); break; case bcPop: env.pop1(); break; case bcPop2: env.pop2(b1, b2); break; case bcDup: b1 = env.pop1(); env.push1(b1); env.push1(b1); break; case bcDup_x1: b1 = env.pop1(); b2 = env.pop1(); env.push1(b1); env.push1(b2); env.push1(b1); break; case bcDup_x2: b1 = env.pop1(); env.pop2(b2, b3); env.push1(b1); env.push2(b2, b3); env.push1(b1); break; case bcDup2: env.pop2(b1, b2); env.push2(b1, b2); env.push2(b1, b2); break; case bcDup2_x1: env.pop2(b1, b2); b3 = env.pop1(); env.push2(b1, b2); env.push1(b3); env.push2(b1, b2); break; case bcDup2_x2: env.pop2(b1, b2); env.pop2(b3, b4); env.push2(b1, b2); env.push2(b3, b4); env.push2(b1, b2); break; case bcSwap: b1 = env.pop1(); b2 = env.pop1(); env.push1(b1); env.push1(b2); break; case bcIAdd: case bcLAdd: case bcFAdd: case bcDAdd: case bcISub: case bcLSub: case bcFSub: case bcDSub: case bcIMul: case bcLMul: case bcFMul: case bcDMul: case bcIDiv: case bcLDiv: case bcFDiv: case bcDDiv: case bcIRem: case bcLRem: case bcFRem: case bcDRem: case bcIShl: case bcLShl: case bcIShr: case bcLShr: case bcIUShr: case bcLUShr: case bcIAnd: case bcLAnd: case bcIOr: case bcLOr: case bcIXor: case bcLXor: case bcLCmp: case bcFCmpL: case bcFCmpG: case bcDCmpL: case bcDCmpG: bk = bytecodeSignatures[opcode - bcIAdd]; env.pop1or2(bk[1]); goto popPushUnary; case bcINeg: case bcLNeg: case bcFNeg: case bcDNeg: case bcI2L: case bcI2F: case bcI2D: case bcL2I: case bcL2F: case bcL2D: case bcF2I: case bcF2L: case bcF2D: case bcD2I: case bcD2L: case bcD2F: case bcInt2Byte: case bcInt2Char: case bcInt2Short: bk = bytecodeSignatures[opcode - bcIAdd]; popPushUnary: env.pop1or2(bk[0]); env.push1or2(bk[2]); break; case bcIInc: i = *(Uint8 *)bc; bc += 2; incLocal: if (!env.getLocal1(i).hasKind(VerificationEnv::bkInt)) verifyError(VerifyError::badType); break; case bcIf_ICmpEq: case bcIf_ICmpNe: case bcIf_ICmpLt: case bcIf_ICmpGe: case bcIf_ICmpGt: case bcIf_ICmpLe: env.pop1(VerificationEnv::bkInt); case bcIfEq: case bcIfNe: case bcIfLt: case bcIfGe: case bcIfGt: case bcIfLe: env.pop1(VerificationEnv::bkInt); bc += 2; // We should be at the end of the basic block. assert(bc == bytecodesEnd); break; case bcIf_ACmpEq: case bcIf_ACmpNe: env.pop1(VerificationEnv::bkAddr); case bcIfNull: case bcIfNonnull: env.pop1(VerificationEnv::bkAddr); bc += 2; // We should be at the end of the basic block. assert(bc == bytecodesEnd); break; case bcGoto_W: bc += 2; case bcGoto: bc += 2; // We should be at the end of the basic block. assert(bc == bytecodesEnd); break; case bcTableSwitch: case bcLookupSwitch: env.pop1(VerificationEnv::bkInt); bc = bytecodesEnd; break; case bcIReturn: case bcLReturn: case bcFReturn: case bcDReturn: case bcAReturn: env.pop1or2(arrayAccessKinds[opcode - bcIReturn]); case bcReturn: // We should be at the end of the basic block. assert(bc == bytecodesEnd); break; case bcGetStatic: env.push1or2(VerificationEnv::typeKindToBindingKind( classFileSummary.lookupStaticField(readBigUHalfwordUnaligned(bc), vk, address, isVolatile, isConstant))); bc += 2; break; case bcPutStatic: env.pop1or2(VerificationEnv::typeKindToBindingKind( classFileSummary.lookupStaticField(readBigUHalfwordUnaligned(bc), vk, address, isVolatile, isConstant))); bc += 2; break; case bcGetField: env.pop1(VerificationEnv::bkAddr); env.push1or2(VerificationEnv::typeKindToBindingKind( classFileSummary.lookupInstanceField(readBigUHalfwordUnaligned(bc), vk, offset, isVolatile, isConstant))); bc += 2; break; case bcPutField: env.pop1or2(VerificationEnv::typeKindToBindingKind( classFileSummary.lookupInstanceField(readBigUHalfwordUnaligned(bc), vk, offset, isVolatile, isConstant))); env.pop1(VerificationEnv::bkAddr); bc += 2; break; case bcInvokeVirtual: classFileSummary.lookupVirtualMethod(readBigUHalfwordUnaligned(bc), sig, offset, address); popPushInvoke: bc += 2; i = sig.nArguments; while (i--) env.pop1or2(VerificationEnv::typeKindToBindingKind(sig.argumentTypes[i]->typeKind)); tk = sig.resultType->typeKind; if (tk != tkVoid) env.push1or2(VerificationEnv::typeKindToBindingKind(tk)); break; case bcInvokeSpecial: classFileSummary.lookupSpecialMethod(readBigUHalfwordUnaligned(bc), sig, isInit, address); goto popPushInvoke; case bcInvokeStatic: classFileSummary.lookupStaticMethod(readBigUHalfwordUnaligned(bc), sig, address); goto popPushInvoke; case bcInvokeInterface: classFileSummary.lookupInterfaceMethod(readBigUHalfwordUnaligned(bc), sig, offset, interfaceNumber, address, *(Uint8 *)(bc + 2)); bc += 2; goto popPushInvoke; case bcNew: classFileSummary.lookupClass(readBigUHalfwordUnaligned(bc)); bc += 2; env.push1(VerificationEnv::bkAddr); break; case bcNewArray: i = *(Uint8 *)bc; bc++; i -= natMin; if (i >= natLimit - natMin) verifyError(VerifyError::badNewArrayType); env.pop1(VerificationEnv::bkInt); env.push1(VerificationEnv::bkAddr); break; case bcANewArray: classFileSummary.lookupType(readBigUHalfwordUnaligned(bc)); bc += 2; env.pop1(VerificationEnv::bkInt); env.push1(VerificationEnv::bkAddr); break; case bcMultiANewArray: classFileSummary.lookupType(readBigUHalfwordUnaligned(bc)); i = *(Uint8 *)(bc+2); // Number of dimensions bc += 3; if (i == 0) verifyError(VerifyError::badNewArrayType); while (i--) env.pop1(VerificationEnv::bkInt); env.push1(VerificationEnv::bkAddr); break; case bcArrayLength: env.pop1(VerificationEnv::bkAddr); env.push1(VerificationEnv::bkInt); break; case bcAThrow: env.pop1(VerificationEnv::bkAddr); // The rest of this BytecodeBlock is dead, and we should be at the end of it. assert(bc == bytecodesEnd); break; case bcCheckCast: classFileSummary.lookupType(readBigUHalfwordUnaligned(bc)); bc += 2; env.pop1(VerificationEnv::bkAddr); env.push1(VerificationEnv::bkAddr); break; case bcInstanceOf: classFileSummary.lookupType(readBigUHalfwordUnaligned(bc)); bc += 2; env.pop1(VerificationEnv::bkAddr); env.push1(VerificationEnv::bkInt); break; case bcMonitorEnter: case bcMonitorExit: env.pop1(VerificationEnv::bkAddr); break; case bcWide: opcode = *bc++; i = readBigUHalfwordUnaligned(bc); bc += 2; switch (opcode) { case bcILoad: case bcFLoad: case bcALoad: goto pushLoad1; case bcLLoad: case bcDLoad: goto pushLoad2; case bcIStore: case bcFStore: goto popStore1; case bcLStore: case bcDStore: goto popStore2; case bcAStore: goto popAStore1; case bcIInc: bc += 2; goto incLocal; case bcRet: goto handleRet; default: verifyError(VerifyError::badBytecode); } break; case bcBreakpoint: // Ignore breakpoints. break; case bcJsr_W: bc += 2; case bcJsr: bc += 2; // We have two successors to a jsr bytecode's basic block. The first is the // code that follows the jsr bytecode, while the second is the beginning of the // subroutine. // At this point we do not propagate the environment to the code that follows // the jst bytecode because we don't know the state of the registers, and, besides, // the subroutine might not return at all. Instead, we only propagate the environment // into the subroutine and rely on ret to propagate it to the instructions after // all jsr's that could have called that subroutine. assert(block.hasSubkind(BytecodeBlock::skJsr)); sub = &block.getSuccessor(0); env.enterSubroutine(sub); b1.setReturn(sub); env.push1(b1); subroutineCallSiteList.addAssociation(*sub, block, envPool); // We should be at the end of the basic block. assert(bc == bytecodesEnd); changed |= predecessorChanged(*sub, env, generation, true); return changed; case bcRet: i = *(Uint8 *)bc; bc++; handleRet: b = &env.getLocal1(i); if (!b->hasKind(VerificationEnv::bkReturn)) verifyError(VerifyError::badType); sub = b->getSubroutine(); // We have to figure out the successors dynamically using the subroutine's address. assert(block.hasSubkind(BytecodeBlock::skRet) && block.nSuccessors() == 0); { // This can be an assert instead of an if/verifyError because if we really did have // two jsr's going to this ret, the return address local's BindingKind would become // bkVoid instead of bkReturn and we would catch this above. assert(!block.getSubroutineHeader() || block.getSubroutineHeader() == sub); block.getSubroutineHeader() = sub; BytecodeBlock *oldRet = sub->getSubroutineRet(); if (oldRet && oldRet != &block) verifyError(VerifyError::multipleRet); sub->getSubroutineRet() = █ for (SubroutineCallSiteList::Iterator iter(subroutineCallSiteList, *sub); iter.more(); ++iter) { BytecodeBlock &succ = *iter; assert(succ.hasSubkind(BytecodeBlock::skJsr)); VerificationEnv succEnv(env); succEnv.exitSubroutine(sub, succ.getVerificationEnvIn()); changed |= predecessorChanged(succ.getSuccessor(1), succEnv, generation, true); } } // We should be at the end of the basic block. assert(bc == bytecodesEnd); return changed; default: verifyError(VerifyError::badBytecode); } } // Propagate dataflow information to all non-exceptional successors. changed |= predecessorChanged(block.successorsBegin, block.successorsEnd, env, generation, true); return changed; } // // Compute the verificationEnvIn, subroutineHeader, and subroutineRet fields of // each block in the bytecode graph. This will verify the method's stack and // type discpiline and match each ret (and wide ret) bytecode in the method with // the entry address of the subroutine to which it corresponds. // Set all retReachable fields of Context structures in the verification environments // to false. // // depthFirstSearch should have been called on entry to this function. The // subroutineHeader and subroutineRet fields of each bytecode graph block should // be nil on entry. // // This function uses the generation and recompute fields of bytecode graph blocks // as temporaries. It expects all recompute flags to be false on entry, and it // leaves them that way on exit. // void BytecodeVerifier::computeDataflow() { Pool bindingPool; setBindingPool(bindingPool); BasicBlock **dfsList = bytecodeGraph.getDFSList(); BasicBlock **dfsListEnd = dfsList + bytecodeGraph.getDFSListLength() - 1; assert(bytecodeGraph.getDFSListLength() > 0); Uint32 generation = 0; // Set up the locals and arguments VerificationEnv env(*this); env.initLive(); uint nArgs = bytecodeGraph.nArguments; uint slotOffset = 0; const ValueKind *ak = bytecodeGraph.argumentKinds; for (uint n = 0; n != nArgs; n++) { VerificationEnv::Binding b; b.setKind(VerificationEnv::valueKindToBindingKind(*ak++)); if (b.isOneWord()) env.setLocal1(slotOffset++, b); else { env.setLocal2(slotOffset, b); slotOffset += 2; } } bool changed = predecessorChanged(*bytecodeGraph.beginBlock, env, generation, true); while (changed) { generation++; changed = false; BasicBlock **blockPtr = dfsList; while (blockPtr != dfsListEnd) { BasicBlock *block = *blockPtr++; block->getGeneration() = generation; if (block->getRecompute()) { block->getRecompute() = false; assert(block->getVerificationEnvIn().live()); assert(block->hasKind(BasicBlock::bbBytecode) || block->hasKind(BasicBlock::bbCatch)); if (block->hasKind(BasicBlock::bbBytecode)) changed |= propagateDataflow(*static_cast(block), generation); else changed |= propagateDataflow(*static_cast(block), generation); } } } assert((*dfsListEnd)->hasKind(BasicBlock::bbEnd)); (*dfsListEnd)->getRecompute() = false; #ifdef DEBUG // There shouldn't be any blocks left to recompute. for (BasicBlock **blockPtr = dfsList; blockPtr != dfsListEnd+1; blockPtr++) assert(!(*blockPtr)->getRecompute()); #endif clearBindingPool(); } // // Do two things: // // 1. Compute the retReachable fields of Context structures in the verification environments // in each block in the bytecode graph. The rest of the environments should have been // computed by computeDataflow. // // Let B be a block with one or more Contexts in its verification environment and let // C be one of those Contexts and S be C's subroutine. On output C's retReachable will // be set if S's ret can be reached from B without going through S's jsr again. // If C's retReachable is not set, we can consider B to not really be a part of S // because there is no way to reach S's ret from B; instead, we consider S to have // exited via a jump before it got to B. // // 2. Convert each BytecodeBlocks with a jsr's for which there is no ret into a // skJsrNoRet-subkind block. That block will have one successor instead of two because // the subroutine it calls cannot return. // // This function uses the generation fields of bytecode graph blocks as temporaries. // void BytecodeVerifier::computeRetReachables() { BasicBlock **dfsList = bytecodeGraph.getDFSList(); BasicBlock **dfsListEnd = dfsList + bytecodeGraph.getDFSListLength(); bool changed = false; BasicBlock **blockPtr = dfsList; while (blockPtr != dfsListEnd) { BasicBlock *block = *blockPtr++; block->getGeneration() = 0; if (block->hasKind(BasicBlock::bbBytecode) && block->getVerificationEnvIn().live()) { BytecodeBlock::Subkind subkind = static_cast(block)->subkind; if (subkind == BytecodeBlock::skRet) { block->getVerificationEnvIn().setRetReachable(block->getSubroutineHeader()); block->getGeneration() = 1; changed = true; } else if (subkind == BytecodeBlock::skJsr) { assert(block->nSuccessors() == 2); if (!block->getSuccessor(0).getSubroutineRet()) { static_cast(block)->transformToJsrNoRet(); subroutineCallSiteList.removeAssociation(block->getSuccessor(0), *static_cast(block)); } } } } dfsListEnd--; // Ignore the end block from now on. Uint32 generation = 1; while (changed) { Uint32 oldGeneration = generation; generation++; changed = false; // We're doing a backward analysis, so traverse the DFS list in reverse order. // This isn't quite as good as computing the DFS of the reverse graph, but it works; // figuring out the real DFS of the reverse graph would require reversing the graph's // edges and be quite messy. blockPtr = dfsListEnd; while (blockPtr != dfsList) { BasicBlock *block = *--blockPtr; // For each live block propagate retReachable information backward from successors // (including exceptional successors) that have changed in either this or the previous // generation. Set the generation of any blocks that change as a result to the current // generation so that they can be propagated further. if (block->getVerificationEnvIn().live()) { BasicBlock **succPtr = block->successorsBegin; BasicBlock **succEnd = block->handlersEnd; BytecodeBlock *sub = 0; if (block->hasKind(BasicBlock::bbBytecode) && static_cast(block)->hasSubkind(BytecodeBlock::skJsr)) sub = &block->getSuccessor(0); while (succPtr != succEnd) { BasicBlock *succ = *succPtr++; if (succ->getGeneration() >= oldGeneration) if (block->getVerificationEnvIn().mergeRetReachables(succ->getVerificationEnvIn(), sub)) { changed = true; block->getGeneration() = generation; } sub = 0; } } } } } struct DuplicateHelper { typedef BasicBlock *Successor; typedef BasicBlock *NodeRef; const Uint32 generation; // currentGeneration from BytecodeVerifier::duplicateSubroutines explicit DuplicateHelper(Uint32 generation): generation(generation) {} static Successor *getSuccessorsBegin(NodeRef n) {return n->successorsBegin;} static Successor *getSuccessorsEnd(NodeRef n) {return n->handlersEnd;} static NodeRef getNodeRef(Successor s) {return s;} }; struct DuplicatePass1Helper: DuplicateHelper { BytecodeGraph &bytecodeGraph; // The BytecodeGraph which we're examining BytecodeBlock *subroutine; // Header of the subroutine whose body we're marking DuplicatePass1Helper(BytecodeGraph &bytecodeGraph, BytecodeBlock *subroutine, Uint32 generation): DuplicateHelper(generation), bytecodeGraph(bytecodeGraph), subroutine(subroutine) {} bool isMarked(NodeRef n) {return n->getGeneration() == generation || !n->getVerificationEnvIn().isRetReachable(subroutine);} void setMarked(NodeRef n); }; // // Make a clone of block n and store a pointer to that clone in n's clone field. // Make the clone's successors and handlers point to the same blocks to which n's // successors and handlers point. // Also set n's generation to the current generation. // void DuplicatePass1Helper::setMarked(BasicBlock *n) { assert(n->getGeneration() != generation); n->getGeneration() = generation; BasicBlock *clone; Pool &bytecodeGraphPool = bytecodeGraph.bytecodeGraphPool; if (n->hasKind(BasicBlock::bbBytecode)) clone = new(bytecodeGraphPool) BytecodeBlock(bytecodeGraph, *static_cast(n)); else { assert(n->hasKind(BasicBlock::bbCatch)); clone = new(bytecodeGraphPool) CatchBlock(bytecodeGraph, *static_cast(n)); } n->getClone() = clone; } struct DuplicatePass2Helper: DuplicateHelper, Function1 { SubroutineCallSiteList &subroutineCallSiteList; // Reference to BytecodeVerifier::subroutineCallSiteList Pool &envPool; // Reference to BytecodeVerifier::envPool DuplicatePass2Helper(SubroutineCallSiteList &subroutineCallSiteList, Pool &envPool, Uint32 generation): DuplicateHelper(generation), subroutineCallSiteList(subroutineCallSiteList), envPool(envPool) {} bool isMarked(NodeRef n) {return n->getGeneration() != generation;} void setMarked(NodeRef n); BytecodeBlock *operator()(BytecodeBlock *arg); }; // // This block has an already created clone saved in its clone field. // For each of this block's successors and catch handlers s, if s points to // a block in this subroutine (which we can tell by checking whether s's generation // is greater than or equal to this DuplicatePass2Helper's generation), replace s // by s's clone. // If this block has subkind skJsr, update the subroutineCallSiteList to also include // this block's clone, which is a newly created call site. // // Set this block's generation to this DuplicatePass2Helper's generation plus one to // mark this block so that setMarked is not called on it again. // void DuplicatePass2Helper::setMarked(BasicBlock *n) { assert(n->getGeneration() == generation); n->getGeneration() = generation + 1; BasicBlock *const clone = n->getClone(); assert(clone); // Fix up the VerificationTemps. clone->initVerification(*n, *this); // Fix up the successor and handler pointers. BasicBlock **successor = clone->successorsBegin; BasicBlock **limit = clone->handlersEnd; while (successor != limit) { BasicBlock *s = *successor; assert(s); if (s->getGeneration() >= generation) *successor = s->getClone(); successor++; } if (clone->hasKind(BasicBlock::bbBytecode)) { BytecodeBlock *const bytecodeClone = static_cast(clone); // Fix up the catchBlock pointer. If it's non-nil, it must point inside this subroutine. CatchBlock *c = bytecodeClone->catchBlock; if (c) { assert(c->getGeneration() >= generation); bytecodeClone->catchBlock = static_cast(c->getClone()); } // If clone has subkind skJsr, update the subroutineCallSiteList. if (bytecodeClone->hasSubkind(BytecodeBlock::skJsr)) { subroutineCallSiteList.addAssociation(bytecodeClone->getSuccessor(0), *bytecodeClone, envPool); } } else assert(clone->hasKind(BasicBlock::bbCatch)); } // // If arg points inside the subroutine currently being duplicated, return its clone; // if arg is nil or points outside that subroutine, return it unchanged. // BytecodeBlock *DuplicatePass2Helper::operator()(BytecodeBlock *arg) { if (arg && arg->getGeneration() >= generation) arg = static_cast(arg->getClone()); return arg; } // // Duplicate each subroutine called by jsr's inside skJsr-kind (not skJsrNoRet-kind) blocks. // This function relies on the bytecode graph being set up by computeRetReachables. // Furthermore, no blocks should have been added to the graph since its last DFS search. // On exit the graph will contain no unforwarded ret's and every jsr will be inside a // skJsrNoRet-kind block. // // This function uses the generation and clone fields of bytecode graph blocks as temporaries. // void BytecodeVerifier::duplicateSubroutines() { Pool searchPool; Uint32 nGraphBlocks = bytecodeGraph.getDFSListLength(); // Upper bound on the current number of blocks in the graph SearchStackEntry *searchStack = 0; Uint32 searchStackSize = 0; BasicBlock **block = bytecodeGraph.getDFSList(); BasicBlock **dfsListEnd = block + nGraphBlocks; while (block != dfsListEnd) (*block++)->getGeneration() = 0; Uint32 currentGeneration = 2; BytecodeBlock *callSite; BytecodeBlock *subroutine; bool onlyOneCallSite; while ((callSite = subroutineCallSiteList.popAssociation(subroutine, onlyOneCallSite)) != 0) { BytecodeBlock *subroutineRet = subroutine->getSubroutineRet(); assert(subroutineRet && callSite->hasSubkind(BytecodeBlock::skJsr)); if (!onlyOneCallSite) { // There is more than one jsr remaining to this subroutine, so we copy the entire // subroutine. if (nGraphBlocks > searchStackSize) { searchStackSize = nGraphBlocks * 2; searchStack = new(searchPool) SearchStackEntry[searchStackSize]; } { // Make a clone of every BasicBlock in the subroutine and store a pointer to that // clone in the original BasicBlock's clone field. The clones' successors will still // point to the original blocks; we'll take care of that later. // To recognize which blocks we've cloned already we set the generation field of // every visited block in the subroutine to currentGeneration. DuplicatePass1Helper pass1Helper(bytecodeGraph, subroutine, currentGeneration); graphSimpleSearch(pass1Helper, static_cast(subroutine), searchStackSize, searchStack); } // Now change all successor and catch block pointers in the cloned blocks point to // the clones of their successors/catch blocks if there are any. Leave pointers to // blocks outside the subroutine as they are. Change the generation of each block // whose clone we transform this way to currentGeneration+1 to mark it so we don't // transform it twice. DuplicatePass2Helper pass2Helper(subroutineCallSiteList, envPool, currentGeneration); graphSimpleSearch(pass2Helper, static_cast(subroutine), searchStackSize, searchStack); // Change the subroutine pointer of our call site. assert(subroutine->getGeneration() >= currentGeneration && callSite->nSuccessors() == 2); callSite->successorsBegin[0] = subroutine->getClone(); // Set up subroutineRet to point to the clone of the original ret. assert(subroutineRet->getGeneration() >= currentGeneration); subroutineRet = static_cast(subroutineRet->getClone()); // Advance the generation. currentGeneration += 2; // Throw an error if we went through 2^32 generations so far (unlikely, but it's better to be safe!). if (currentGeneration < 2) verifyError(VerifyError::jsrNestingError); } // At this point we've already copied the subroutine if needed. Now we // just forward its ret to the successor of the jsr. subroutineRet->transformToForward(callSite->transformToJsrNoRet()); } } // // Inline all jsr-ret subroutines in this BytecodeGraph, eliminating all // jsr and ret bytecodes. depthFirstSearch should have been called on entry // to this function, and it needs to be called again after this function exits. // void BytecodeVerifier::inlineSubroutines(BytecodeGraph &bytecodeGraph, Pool &tempPool) { BytecodeVerifier verifier(bytecodeGraph, tempPool); verifier.computeDataflow(); verifier.computeRetReachables(); verifier.duplicateSubroutines(); bytecodeGraph.invalidateDFSList(); }