/* ### * IP: GHIDRA * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "varnode.hh" #include "funcdata.hh" namespace ghidra { AttributeId ATTRIB_ADDRTIED = AttributeId("addrtied",30); AttributeId ATTRIB_GRP = AttributeId("grp",31); AttributeId ATTRIB_INPUT = AttributeId("input",32); AttributeId ATTRIB_PERSISTS = AttributeId("persists",33); AttributeId ATTRIB_UNAFF = AttributeId("unaff",34); /// Compare by location then by definition. /// This is the same as the normal varnode compare, but we distinguish identical frees by their /// pointer address. Thus varsets defined with this comparison act like multisets for free varnodes /// and like unique sets for everything else (with respect to the standard varnode comparison) /// \param a is the first Varnode to compare /// \param b is the second Varnode to compare /// \return true if \b a occurs earlier than \b b inline bool VarnodeCompareLocDef::operator()(const Varnode *a,const Varnode *b) const { uint4 f1,f2; if (a->getAddr() != b->getAddr()) return (a->getAddr() < b->getAddr()); if (a->getSize() != b->getSize()) return (a->getSize() < b->getSize()); f1 = a->getFlags()&(Varnode::input|Varnode::written); f2 = b->getFlags()&(Varnode::input|Varnode::written); if (f1!=f2) return ((f1-1)<(f2-1)); // -1 forces free varnodes to come last if (f1==Varnode::written) { if (a->getDef()->getSeqNum() != b->getDef()->getSeqNum()) return (a->getDef()->getSeqNum() < b->getDef()->getSeqNum()); } else if (f1 == 0) // both are free // return (a < b); // compare pointers return (a->getCreateIndex() < b->getCreateIndex()); return false; } /// Compare by definition then by location. /// This is different than the standard ordering but we still allow multiple identical frees. /// \param a is the first Varnode to compare /// \param b is the second Varnode to compare /// \return true if \b a occurs earlier than \b b inline bool VarnodeCompareDefLoc::operator()(const Varnode *a,const Varnode *b) const { uint4 f1,f2; f1 = (a->getFlags() & (Varnode::input|Varnode::written)); f2 = (b->getFlags() & (Varnode::input|Varnode::written)); if (f1!=f2) return ((f1-1)<(f2-1)); // NOTE: The -1 forces free varnodes to come last if (f1==Varnode::written) { if (a->getDef()->getSeqNum() != b->getDef()->getSeqNum()) return (a->getDef()->getSeqNum() < b->getDef()->getSeqNum()); } if (a->getAddr() != b->getAddr()) return (a->getAddr() < b->getAddr()); if (a->getSize() != b->getSize()) return (a->getSize() < b->getSize()); if (f1==0) // both are free // return (agetCreateIndex() < b->getCreateIndex()); return false; } /// During the course of analysis Varnodes are merged into high-level variables that are intended /// to be closer to the concept of variables in C source code. For a large portion of the decompiler /// analysis this concept hasn't been built yet, and this routine will return \b null. /// But after a certain point, every Varnode managed by the Funcdata object, with the exception /// of ones that are marked as \e annotations, is associated with some HighVariable /// and will return a non-null result. /// \return the associated HighVariable HighVariable *Varnode::getHigh(void) const { if (high==(HighVariable *)0) throw LowlevelError("Requesting non-existent high-level"); return high; } /// Return various values depending on the containment of another Varnode within \b this. /// Return /// - -1 if op.loc starts before -this- /// - 0 if op is contained in -this- /// - 1 if op.start is contained in -this- /// - 2 if op.loc comes after -this- or /// - 3 if op and -this- are in non-comparable spaces /// \param op is the Varnode to test for containment /// \return the integer containment code int4 Varnode::contains(const Varnode &op) const { if (loc.getSpace() != op.loc.getSpace()) return 3; if (loc.getSpace()->getType()==IPTR_CONSTANT) return 3; uintb a = loc.getOffset(); uintb b = op.loc.getOffset(); if (b=a+size) return 2; if (b+op.size > a+size) return 1; return 0; } /// Check whether the storage locations of two varnodes intersect /// \param op is the Varnode to compare with \b this /// \return \b true if the locations intersect bool Varnode::intersects(const Varnode &op) const { if (loc.getSpace() != op.loc.getSpace()) return false; if (loc.getSpace()->getType()==IPTR_CONSTANT) return false; uintb a = loc.getOffset(); uintb b = op.loc.getOffset(); if (b=b+op.size) return false; return true; } if (b>=a+size) return false; return true; } /// Check if \b this intersects the given Address range /// \param op2loc is the start of the range /// \param op2size is the size of the range in bytes /// \return \b true if \b this intersects the range bool Varnode::intersects(const Address &op2loc,int4 op2size) const { if (loc.getSpace() != op2loc.getSpace()) return false; if (loc.getSpace()->getType()==IPTR_CONSTANT) return false; uintb a = loc.getOffset(); uintb b = op2loc.getOffset(); if (b=b+op2size) return false; return true; } if (b>=a+size) return false; return true; } int4 Varnode::characterizeOverlap(const Varnode &op) const { if (loc.getSpace() != op.loc.getSpace()) return 0; if (loc.getOffset() == op.loc.getOffset()) // Left sides match return (size == op.size) ? 2 : 1; // Either total match or partial else if (loc.getOffset() < op.loc.getOffset()) { uintb thisright = loc.getOffset() + (size-1); return (thisright < op.loc.getOffset()) ? 0: 1; // Test if this ends before op begins } else { uintb opright = op.loc.getOffset() + (op.size-1); return (opright < loc.getOffset()) ? 0: 1; // Test if op ends before this begins } } /// Return whether \e Least \e Signifigant \e Byte of \b this occurs in \b op /// I.e. return /// - 0 if it overlaps op's lsb /// - 1 if it overlaps op's second lsb and so on /// \param op is the Varnode to test for overlap /// \return the relative overlap point or -1 int4 Varnode::overlap(const Varnode &op) const { if (!loc.isBigEndian()) // Little endian return loc.overlap(0,op.loc,op.size); else { // Big endian int4 over = loc.overlap(size-1,op.loc,op.size); if (over != -1) return op.size-1-over; } return -1; } /// Return whether \e Least \e Signifigant \e Byte of \b this occurs in \b op. /// If \b op is in the \e join space, \b this can be in one of the pieces associated with the \e join range, and /// the offset returned will take into account the relative position of the piece within the whole \e join. /// Otherwise, this method is equivalent to Varnode::overlap. /// \param op is the Varnode to test for overlap /// \return the relative overlap point or -1 int4 Varnode::overlapJoin(const Varnode &op) const { if (!loc.isBigEndian()) // Little endian return loc.overlapJoin(0,op.loc,op.size); else { // Big endian int4 over = loc.overlapJoin(size-1,op.loc,op.size); if (over != -1) return op.size-1-over; } return -1; } /// Return whether \e Least \e Signifigant \e Byte of \b this occurs in an Address range /// I.e. return /// - 0 if it overlaps op's lsb /// - 1 if it overlaps op's second lsb and so on /// \param op2loc is the starting Address of the range /// \param op2size is the size of the range in bytes /// \return the relative overlap point or -1 int4 Varnode::overlap(const Address &op2loc,int4 op2size) const { if (!loc.isBigEndian()) // Little endian return loc.overlap(0,op2loc,op2size); else { // Big endian int4 over = loc.overlap(size-1,op2loc,op2size); if (over != -1) return op2size-1-over; } return -1; } /// Rebuild variable cover based on where the Varnode /// is defined and read. This is \e only called by the /// Merge class which knows when to call it properly void Varnode::updateCover(void) const { if ((flags & Varnode::coverdirty)!=0) { if (hasCover()&&(cover!=(Cover *)0)) cover->rebuild(this); clearFlags(Varnode::coverdirty); } } /// Delete the Cover object. Used for dead Varnodes before full deletion. void Varnode::clearCover(void) const { if (cover != (Cover *)0) { delete cover; cover = (Cover *)0; } } /// Initialize a new Cover and set dirty bit so that updateCover will rebuild void Varnode::calcCover(void) const { if (hasCover()) { if (cover != (Cover *)0) delete cover; cover = new Cover; setFlags(Varnode::coverdirty); } } /// Print, to a stream, textual information about where \b this Varnode is in scope within its /// particular Funcdata. This amounts to a list of address ranges bounding the writes and reads /// of the Varnode /// \param s is the output stream void Varnode::printCover(ostream &s) const { if (cover == (Cover *)0) throw LowlevelError("No cover to print"); if ((flags & Varnode::coverdirty)!=0) s << "Cover is dirty" << endl; else cover->print(s); } /// Print boolean attribute information about \b this as keywords to a stream /// \param s is the output stream void Varnode::printInfo(ostream &s) const { type->printRaw(s); s << " = "; printRaw(s); if (isAddrTied()) s << " tied"; if (isMapped()) s << " mapped"; if (isPersist()) s << " persistent"; if (isTypeLock()) s << " tlock"; if (isNameLock()) s << " nlock"; if (isSpacebase()) s << " base"; if (isUnaffected()) s << " unaff"; if (isImplied()) s << " implied"; if (isAddrForce()) s << " addrforce"; if (isReadOnly()) s << " readonly"; s << " (consumed=0x" << hex << consumed << ')'; s << " (internal=" << hex << this << ')'; s << " (create=0x" << hex << create_index << ')'; s << endl; } /// Erase the operation from our descendant list and set the cover dirty flag /// \param op is the PcodeOp to remove void Varnode::eraseDescend(PcodeOp *op) { list::iterator iter; iter = descend.begin(); while (*iter != op) // Find this op in list of vn's descendants iter++; descend.erase(iter); // Remove it from list setFlags(Varnode::coverdirty); } /// Put a new operator in the descendant list and set the cover dirty flag /// \param op is PcodeOp to add void Varnode::addDescend(PcodeOp *op) { // if (!heritageknown()) { if (isFree()&&(!isSpacebase())) { if (!descend.empty()) throw LowlevelError("Free varnode has multiple descendants"); } descend.push_back(op); setFlags(Varnode::coverdirty); } /// Completely clear the descendant list /// Only called if Varnode is about to be irrevocably destroyed void Varnode::destroyDescend(void) { descend.clear(); } /// Set desired boolean attributes on this Varnode and then set dirty bits if appropriate /// \param fl is the mask containing the list of attributes to set void Varnode::setFlags(uint4 fl) const { flags |= fl; if (high != (HighVariable *)0) { high->flagsDirty(); if ((fl&Varnode::coverdirty)!=0) high->coverDirty(); } } /// Clear desired boolean attributes on this Varnode and then set dirty bits if appropriate /// \param fl is the mask containing the list of attributes to clear void Varnode::clearFlags(uint4 fl) const { flags &= ~fl; if (high != (HighVariable *)0) { high->flagsDirty(); if ((fl&Varnode::coverdirty)!=0) high->coverDirty(); } } /// For \b this Varnode and any others attached to the same HighVariable, /// remove any SymbolEntry reference and associated properties. void Varnode::clearSymbolLinks(void) { bool foundEntry = false; for(int4 i=0;inumInstances();++i) { Varnode *vn = high->getInstance(i); foundEntry = foundEntry || (vn->mapentry != (SymbolEntry *)0); vn->mapentry = (SymbolEntry *)0; vn->clearFlags(Varnode::namelock | Varnode::typelock | Varnode::mapped); } if (foundEntry) high->symbolDirty(); } /// Directly change the defining PcodeOp and set appropriate dirty bits /// \param op is the pointer to the new PcodeOp, which can be \b null void Varnode::setDef(PcodeOp *op) { // Set the defining op def = op; if (op==(PcodeOp *)0) { setFlags(Varnode::coverdirty); clearFlags(Varnode::written); } else setFlags(Varnode::coverdirty|Varnode::written); } /// The given Symbol's data-type and flags are inherited by \b this Varnode. /// If the Symbol is \e type-locked, a reference to the Symbol is set on \b this Varnode. /// \param entry is a mapping to the given Symbol /// \return \b true if any properties have changed bool Varnode::setSymbolProperties(SymbolEntry *entry) { bool res = entry->updateType(this); if (entry->getSymbol()->isTypeLocked()) { if (mapentry != entry) { mapentry = entry; if (high != (HighVariable *)0) high->setSymbol(this); res = true; } } setFlags(entry->getAllFlags() & ~Varnode::typelock); return res; } /// A reference to the given Symbol is set on \b this Varnode. /// The data-type on \b this Varnode is not changed. /// \param entry is a mapping to the given Symbol void Varnode::setSymbolEntry(SymbolEntry *entry) { mapentry = entry; uint4 fl = Varnode::mapped; // Flags are generally not changed, but we do mark this as mapped if (entry->getSymbol()->isNameLocked()) fl |= Varnode::namelock; setFlags(fl); if (high != (HighVariable *)0) high->setSymbol(this); } /// Link Symbol information to \b this as a \b reference. This only works for a constant Varnode. /// This used when there is a constant address reference to the Symbol and the Varnode holds the /// reference, not the actual value of the Symbol. /// \param entry is a mapping to the given Symbol /// \param off is the byte offset into the Symbol of the reference void Varnode::setSymbolReference(SymbolEntry *entry,int4 off) { if (high != (HighVariable *)0) { high->setSymbolReference(entry->getSymbol(), off); } } /// \param ct is the Datatype to change to /// \return \b true if the Datatype changed bool Varnode::updateType(Datatype *ct) { if (type == ct || isTypeLock()) return false; type = ct; if (high != (HighVariable *)0) high->typeDirty(); return true; } /// Change the Datatype and lock state associated with this Varnode if various conditions are met /// - Don't change a previously locked Datatype (unless \b override flag is \b true) /// - Don't consider an \b undefined type to be locked /// - Don't change to an identical Datatype /// \param ct is the Datatype to change to /// \param lock is \b true if the new Datatype should be locked /// \param override is \b true if an old lock should be overridden /// \return \b true if the Datatype or the lock setting was changed bool Varnode::updateType(Datatype *ct,bool lock,bool override) { if (ct->getMetatype() == TYPE_UNKNOWN) // Unknown data type is ALWAYS unlocked lock = false; if (isTypeLock()&&(!override)) return false; // Type is locked if ((type == ct)&&(isTypeLock()==lock)) return false; // No change flags &= ~Varnode::typelock; if (lock) flags |= Varnode::typelock; type = ct; if (high != (HighVariable *)0) high->typeDirty(); return true; } /// Copy any symbol and type information from -vn- into this /// \param vn is the Varnode to copy from void Varnode::copySymbol(const Varnode *vn) { type = vn->type; // Copy any type mapentry = vn->mapentry; // Copy any symbol flags &= ~(Varnode::typelock | Varnode::namelock); flags |= (Varnode::typelock | Varnode::namelock) & vn->flags; if (high != (HighVariable *)0) { high->typeDirty(); if (mapentry != (SymbolEntry *)0) high->setSymbol(this); } } /// Symbol information (if present) is copied from the given constant Varnode into \b this, /// which also must be constant, but only if the two constants are \e close in the sense of an equate. /// \param vn is the given constant Varnode void Varnode::copySymbolIfValid(const Varnode *vn) { SymbolEntry *mapEntry = vn->getSymbolEntry(); if (mapEntry == (SymbolEntry *)0) return; EquateSymbol *sym = dynamic_cast(mapEntry->getSymbol()); if (sym == (EquateSymbol *) 0) return; if (sym->isValueClose(loc.getOffset(), size)) { copySymbol(vn); // Propagate the markup into our new constant } } /// Compare two Varnodes /// - First by storage location /// - Second by size /// - Then by defining PcodeOp SeqNum if appropriate /// /// \e Input Varnodes come before \e written Varnodes /// \e Free Varnodes come after everything else /// \param op2 is the Varnode to compare \b this to /// \return \b true if \b this is less than \b op2 bool Varnode::operator<(const Varnode &op2) const { uint4 f1,f2; if (loc != op2.loc) return (loc < op2.loc); if (size != op2.size) return (size < op2.size); f1 = flags&(Varnode::input|Varnode::written); f2 = op2.flags&(Varnode::input|Varnode::written); if (f1!=f2) return ((f1-1)<(f2-1)); // -1 forces free varnodes to come last if (f1==Varnode::written) if (def->getSeqNum() != op2.def->getSeqNum()) return (def->getSeqNum() < op2.def->getSeqNum()); return false; } /// Determine if two Varnodes are equivalent. They must match /// - Storage location /// - Size /// - Defining PcodeOp if it exists /// /// \param op2 is the Varnode to compare \b this to /// \return true if they are equivalent bool Varnode::operator==(const Varnode &op2) const { // Compare two varnodes uint4 f1,f2; if (loc != op2.loc) return false; if (size != op2.size) return false; f1 = flags&(Varnode::input|Varnode::written); f2 = op2.flags&(Varnode::input|Varnode::written); if (f1!=f2) return false; if (f1==Varnode::written) if (def->getSeqNum() != op2.def->getSeqNum()) return false; return true; } /// This is the constructor for making an unmanaged Varnode /// It creates a \b free Varnode with possibly a Datatype attribute. /// Most applications create Varnodes through the Funcdata interface /// \param s is the size of the new Varnode /// \param m is the starting storage Address /// \param dt is the Datatype Varnode::Varnode(int4 s,const Address &m,Datatype *dt) : loc(m) { // Construct a varnode size = s; def = (PcodeOp *)0; // No defining op yet type = dt; high = (HighVariable *)0; mapentry = (SymbolEntry *)0; consumed = ~((uintb)0); cover = (Cover *)0; mergegroup = 0; addlflags = 0; if (m.getSpace() == (AddrSpace *)0) { flags = 0; return; } spacetype tp = m.getSpace()->getType(); if (tp==IPTR_CONSTANT) { flags = Varnode::constant; nzm = m.getOffset(); } else if ((tp==IPTR_FSPEC)||(tp==IPTR_IOP)) { flags = Varnode::annotation|Varnode::coverdirty; nzm = ~((uintb)0); } else { flags = Varnode::coverdirty; nzm = ~((uintb)0); } } /// Delete the Varnode object. This routine assumes all other cross-references have been removed. Varnode::~Varnode(void) { if (cover != (Cover *)0) delete cover; if (high != (HighVariable *)0) { high->remove(this); if (high->isUnattached()) delete high; } } /// This generally just returns the data-type of the Varnode itself unless it is a \e union data-type. /// In this case, the data-type of the resolved field of the \e union, associated with writing to the Varnode, /// is returned. The Varnode \b must be written to, to call this method. /// \return the resolved data-type Datatype *Varnode::getTypeDefFacing(void) const { if (!type->needsResolution()) return type; return type->findResolve(def,-1); } /// This generally just returns the data-type of the Varnode itself unless it is a \e union data-type. /// In this case, the data-type of the resolved field of the \e union, associated with reading the Varnode, /// is returned. /// \param op is the PcodeOp reading \b this Varnode /// \return the resolved data-type Datatype *Varnode::getTypeReadFacing(const PcodeOp *op) const { if (!type->needsResolution()) return type; return type->findResolve(op, op->getSlot(this)); } /// This generally just returns the data-type of the HighVariable associated with \b this, unless it is a /// \e union data-type. In this case, the data-type of the resolved field of the \e union, associated with /// writing to the Varnode, is returned. /// \return the resolved data-type Datatype *Varnode::getHighTypeDefFacing(void) const { Datatype *ct = high->getType(); if (!ct->needsResolution()) return ct; return ct->findResolve(def,-1); } /// This generally just returns the data-type of the HighVariable associated with \b this, unless it is a /// \e union data-type. In this case, the data-type of the resolved field of the \e union, associated with /// reading the Varnode, is returned. /// \param op is the PcodeOp reading \b this Varnode /// \return the resolved data-type Datatype *Varnode::getHighTypeReadFacing(const PcodeOp *op) const { Datatype *ct = high->getType(); if (!ct->needsResolution()) return ct; return ct->findResolve(op, op->getSlot(this)); } /// This is a convenience method for quickly finding the unique PcodeOp that reads this Varnode /// \return only descendant (if there is 1 and ONLY 1) or \b null otherwise PcodeOp *Varnode::loneDescend(void) const { PcodeOp *op; if (descend.empty()) return (PcodeOp *)0; // No descendants list::const_iterator iter; iter = descend.begin(); op = *iter++; // First descendant if (iter != descend.end()) return (PcodeOp *)0; // More than 1 descendant return op; } /// A Varnode can be defined as "coming into scope" at the Address of the first PcodeOp that /// writes to that storage location. Within SSA form this \b first-use address always exists and /// is unique if we consider inputs to come into scope at the start Address of the function they are in /// \param fd is the Funcdata containing the tree /// \return the first-use Address Address Varnode::getUsePoint(const Funcdata &fd) const { if (isWritten()) return def->getAddr(); return fd.getAddress()+-1; // return loc.getSpace()->getTrans()->constant(0); } /// Print to the stream either the name of the Varnode, such as a register name, if it exists /// or print a shortcut character representing the AddrSpace and a hex representation of the offset. /// This function also computes and returns the \e expected size of the identifier it prints /// to facilitate the printing of size modifiers by other print routines /// \param s is the output stream /// \return the expected size int4 Varnode::printRawNoMarkup(ostream &s) const { AddrSpace *spc = loc.getSpace(); const Translate *trans = spc->getTrans(); string name; int4 expect; name = trans->getRegisterName(spc,loc.getOffset(),size); if (name.size()!=0) { const VarnodeData &point(trans->getRegister(name)); uintb off = loc.getOffset()-point.offset; s << name; expect = point.size; if (off != 0) s << '+' << dec << off; } else { s << loc.getShortcut(); // Print type shortcut character expect = trans->getDefaultSize(); loc.printRaw(s); } return expect; } /// Print textual information about this Varnode including a base identifier along with enough /// size and attribute information to uniquely identify the Varnode within a text SSA listing /// In particular, the identifiers have either "i" or defining op SeqNum information appended /// to them in parantheses. /// \param s is the output stream void Varnode::printRaw(ostream &s) const { int4 expect = printRawNoMarkup(s); if (expect != size) s << ':' << setw(1) << size; if ((flags&Varnode::input)!=0) s << "(i)"; if (isWritten()) s << '(' << def->getSeqNum() << ')'; if ((flags&(Varnode::insert|Varnode::constant))==0) { s << "(free)"; return; } } /// Recursively print a terse textual representation of the data-flow (SSA) tree rooted at this Varnode /// \param s is the output stream /// \param depth is the current depth of the tree we are at void Varnode::printRawHeritage(ostream &s,int4 depth) const { for(int4 i=0;iprintRaw(s); else printRaw(s); if ((flags & Varnode::input)!=0) s << " Input"; if ((flags & Varnode::constant)!=0) s << " Constant"; if ((flags & Varnode::annotation)!=0) s << " Code"; if (def != (PcodeOp *)0) { s << "\t\t" << def->getSeqNum() << endl; for(int4 i=0;inumInput();++i) def->getIn(i)->printRawHeritage(s,depth+5); } else s << endl; } /// If \b this is a constant, or is extended (INT_ZEXT,INT_SEXT,PIECE) from a constant, /// the \e value of the constant (currently up to 128 bits) is passed back and \b true is returned. /// \param val will hold the 128-bit constant value /// \return \b true if a constant was recovered bool Varnode::isConstantExtended(uint8 *val) const { if (isConstant()) { val[0] = getOffset(); val[1] = 0; return true; } if (!isWritten() || size <= 8) return false; if (size > 16) return false; // Currently only up to 128-bit values OpCode opc = def->code(); if (opc == CPUI_INT_ZEXT) { Varnode *vn0 = def->getIn(0); if (vn0->isConstant()) { val[0] = vn0->getOffset(); val[1] = 0; return true; } } else if (opc == CPUI_INT_SEXT) { Varnode *vn0 = def->getIn(0); if (vn0->isConstant()) { val[0] = vn0->getOffset(); if (vn0->getSize() < 8) val[0] = sign_extend(val[0], vn0->getSize(), size); val[1] = (signbit_negative(val[0], 8)) ? 0xffffffffffffffffL : 0; return true; } } else if (opc == CPUI_PIECE) { Varnode *vnlo = def->getIn(1); if (vnlo->isConstant()) { val[0] = vnlo->getOffset(); Varnode *vnhi = def->getIn(0); if (vnhi->isConstant()) { val[1] = vnhi->getOffset(); if (vnlo->getSize() == 8) return true; val[0] |= val[1] << 8*vnlo->getSize(); val[1] >>= 8*(8-vnlo->getSize()); return true; } } } return false; } /// Recursively check if the Varnode is either: /// - Copied or extended from a constant /// - The result of arithmetic or logical operations on constants /// - Loaded from a pointer that is a constant /// /// \param maxBinary is the maximum depth of binary operations to inspect (before giving up) /// \param maxLoad is the maximum number of CPUI_LOAD operations to allow in a sequence /// \return \b true if the Varnode (might) collapse to a constant bool Varnode::isEventualConstant(int4 maxBinary,int4 maxLoad) const { const Varnode *curVn = this; while(!curVn->isConstant()) { if (!curVn->isWritten()) return false; const PcodeOp *op = curVn->getDef(); switch(op->code()) { case CPUI_LOAD: if (maxLoad == 0) return false; maxLoad -= 1; curVn = op->getIn(1); break; case CPUI_INT_ADD: case CPUI_INT_SUB: case CPUI_INT_XOR: case CPUI_INT_OR: case CPUI_INT_AND: if (maxBinary == 0) return false; if (!op->getIn(0)->isEventualConstant(maxBinary-1,maxLoad)) return false; return op->getIn(1)->isEventualConstant(maxBinary-1,maxLoad); case CPUI_INT_ZEXT: case CPUI_INT_SEXT: case CPUI_COPY: curVn = op->getIn(0); break; case CPUI_INT_LEFT: case CPUI_INT_RIGHT: case CPUI_INT_SRIGHT: case CPUI_INT_MULT: if (!op->getIn(1)->isConstant()) return false; curVn = op->getIn(0); break; default: return false; } } return true; } /// Make an initial determination of the Datatype of this Varnode. If a Datatype is already /// set and locked return it. Otherwise look through all the read PcodeOps and the write PcodeOp /// to determine if the Varnode is getting used as an \b int, \b float, or \b pointer, etc. /// Throw an exception if no Datatype can be found at all. /// \return the determined Datatype Datatype *Varnode::getLocalType(bool &blockup) const { Datatype *ct; Datatype *newct; if (isTypeLock()) // Our type is locked, don't change return type; // Not a partial lock, return the locked type ct = (Datatype *)0; if (def != (PcodeOp *)0) { ct = def->outputTypeLocal(); if (def->stopsTypePropagation()) { blockup = true; return ct; } } list::const_iterator iter; PcodeOp *op; int4 i; for(iter=descend.begin();iter!=descend.end();++iter) { op = *iter; i = op->getSlot(this); newct = op->inputTypeLocal(i); if (ct == (Datatype *)0) ct = newct; else { if (0>newct->typeOrder(*ct)) ct = newct; } } if (ct == (Datatype *)0) throw LowlevelError("NULL local type"); return ct; } /// If \b this varnode is produced by an operation with a boolean output, or if it is /// formally marked with a boolean data-type, return \b true. The parameter \b trustAnnotation /// toggles whether or not the formal data-type is trusted. /// \return \b true if \b this is a formal boolean, \b false otherwise bool Varnode::isBooleanValue(bool useAnnotation) const { if (isWritten()) return def->isCalculatedBool(); if (!useAnnotation) return false; if ((flags & (input | typelock)) == (input | typelock)) { if (size == 1 && type->getMetatype() == TYPE_BOOL) return true; } return false; } /// If we can prove that the upper bits of \b this are zero, return \b true. /// \param baseSize is the maximum number of least significant bytes that are allowed to be non-zero /// \return \b true if all the most significant bytes are zero bool Varnode::isZeroExtended(int4 baseSize) const { if (baseSize >= size) return false; if (size > sizeof(uintb)) { if (!isWritten()) return false; if (def->code() != CPUI_INT_ZEXT) return false; if (def->getIn(0)->getSize() > baseSize) return false; return true; } uintb mask = nzm >> 8*baseSize; return (mask == 0); } /// Make a local determination if \b this and \b op2 hold the same value. We check if /// there is a common ancester for which both \b this and \b op2 are created from a direct /// sequence of COPY operations. NOTE: This is a transitive relationship /// \param op2 is the Varnode to compare to \b this /// \return \b true if the Varnodes are copied from a common ancestor bool Varnode::copyShadow(const Varnode *op2) const { const Varnode *vn; if (this==op2) return true; // Trace -this- to the source of the copy chain vn = this; while( (vn->isWritten()) && (vn->getDef()->code() == CPUI_COPY)) { vn = vn->getDef()->getIn(0); if (vn == op2) return true; // If we hit op2 then this and op2 must be the same } // Trace op2 to the source of copy chain while( (op2->isWritten()) && (op2->getDef()->code() == CPUI_COPY)) { op2 = op2->getDef()->getIn(0); if (vn == op2) return true; // If the source is the same then this and op2 are same } return false; } /// \brief Try to find a SUBPIECE operation producing the value in \b this from the given \b whole Varnode /// /// The amount of truncation producing \b this must be known apriori. Allow for COPY and MULTIEQUAL operations /// in the flow path from \b whole to \b this. This method will search recursively through branches /// of MULTIEQUAL up to a maximum depth. /// \param leastByte is the number of least significant bytes being truncated from \b whole to get \b this /// \param whole is the given whole Varnode /// \param recurse is the current depth of recursion /// \return \b true if \b this and \b whole have the prescribed SUBPIECE relationship bool Varnode::findSubpieceShadow(int4 leastByte,const Varnode *whole,int4 recurse) const { const Varnode *vn = this; while( vn->isWritten() && vn->getDef()->code() == CPUI_COPY) vn = vn->getDef()->getIn(0); if (!vn->isWritten()) { if (vn->isConstant()) { while( whole->isWritten() && whole->getDef()->code() == CPUI_COPY) whole = whole->getDef()->getIn(0); if (!whole->isConstant()) return false; uintb off = whole->getOffset() >> leastByte*8; off &= calc_mask(vn->getSize()); return (off == vn->getOffset()); } return false; } OpCode opc = vn->getDef()->code(); if (opc == CPUI_SUBPIECE) { const Varnode *tmpvn = vn->getDef()->getIn(0); int4 off = (int4)vn->getDef()->getIn(1)->getOffset(); if (off != leastByte || tmpvn->getSize() != whole->getSize()) return false; if (tmpvn == whole) return true; while(tmpvn->isWritten() && tmpvn->getDef()->code() == CPUI_COPY) { tmpvn = tmpvn->getDef()->getIn(0); if (tmpvn == whole) return true; } } else if (opc == CPUI_MULTIEQUAL) { recurse += 1; if (recurse > 1) return false; // Truncate the recursion at maximum depth while( whole->isWritten() && whole->getDef()->code() == CPUI_COPY) whole = whole->getDef()->getIn(0); if (!whole->isWritten()) return false; const PcodeOp *bigOp = whole->getDef(); if (bigOp->code() != CPUI_MULTIEQUAL) return false; const PcodeOp *smallOp = vn->getDef(); if (bigOp->getParent() != smallOp->getParent()) return false; // Recurse search through all branches of the two MULTIEQUALs for(int4 i=0;inumInput();++i) { if (!smallOp->getIn(i)->findSubpieceShadow(leastByte, bigOp->getIn(i), recurse)) return false; } return true; // All branches were copy shadows } return false; } /// \brief Try to find a PIECE operation that produces \b this from a given Varnode \b piece /// /// \param leastByte is the number of least significant bytes being truncated from the /// putative \b this to get \b piece. The routine can backtrack through COPY operations and /// more than one PIECE operations to verify that \b this is formed out of \b piece. /// \param piece is the given Varnode piece /// \return \b true if \b this and \b whole have the prescribed PIECE relationship bool Varnode::findPieceShadow(int4 leastByte,const Varnode *piece) const { const Varnode *vn = this; while( vn->isWritten() && vn->getDef()->code() == CPUI_COPY) vn = vn->getDef()->getIn(0); if (!vn->isWritten()) return false; OpCode opc = vn->getDef()->code(); if (opc == CPUI_PIECE) { const Varnode *tmpvn = vn->getDef()->getIn(1); // Least significant part if (leastByte >= tmpvn->getSize()) { leastByte -= tmpvn->getSize(); tmpvn = vn->getDef()->getIn(0); } else { if (piece->getSize() + leastByte > tmpvn->getSize()) return false; } if (leastByte == 0 && tmpvn->getSize() == piece->getSize()) { if (tmpvn == piece) return true; while(tmpvn->isWritten() && tmpvn->getDef()->code() == CPUI_COPY) { tmpvn = tmpvn->getDef()->getIn(0); if (tmpvn == piece) return true; } return false; } // CPUI_PIECE input is too big, recursively search for another CPUI_PIECE return tmpvn->findPieceShadow(leastByte, piece); } return false; } /// For \b this and another Varnode, establish that either: /// - bigger = CONCAT(smaller,..) or /// - smaller = SUBPIECE(bigger) /// /// Check through COPY chains and verify that the form of the CONCAT or SUBPIECE matches /// a given relative offset between the Varnodes. /// \param op2 is the Varnode to compare to \b this /// \param relOff is the putative relative byte offset of \b this to \b op2 /// \return \b true if one Varnode is contained, as a value, in the other bool Varnode::partialCopyShadow(const Varnode *op2,int4 relOff) const { const Varnode *vn; if (size < op2->size) { vn = this; } else if (size > op2->size) { vn = op2; op2 = this; relOff = -relOff; } else return false; if (relOff < 0) return false; // Not proper containment if (relOff + vn->getSize() > op2->getSize()) return false; // Not proper containment bool bigEndian = getSpace()->isBigEndian(); int4 leastByte = bigEndian ? (op2->getSize() - vn->getSize()) - relOff : relOff; if (vn->findSubpieceShadow(leastByte, op2, 0)) return true; if (op2->findPieceShadow(leastByte, vn)) return true; return false; } /// If \b this has a data-type built out of separate pieces, return it. /// If \b this is mapped as a partial to a symbol with one of these data-types, return it. /// Return null otherwise. /// \return the associated structured data-type or null Datatype *Varnode::getStructuredType(void) const { Datatype *ct; if (mapentry != (SymbolEntry *)0) ct = mapentry->getSymbol()->getType(); else ct = type; if (ct->isPieceStructured()) return ct; return (Datatype *)0; } /// Compare term order of two Varnodes. Used in Term Rewriting strategies to order operands of commutative ops /// \param op is the Varnode to order against \b this /// \return -1 if \b this comes before \b op, 1 if op before this, or 0 int4 Varnode::termOrder(const Varnode *op) const { if (isConstant()) { if (!op->isConstant()) return 1; } else { if (op->isConstant()) return -1; const Varnode *vn = this; if (vn->isWritten()&&(vn->getDef()->code() == CPUI_INT_MULT)) if (vn->getDef()->getIn(1)->isConstant()) vn = vn->getDef()->getIn(0); if (op->isWritten()&&(op->getDef()->code() == CPUI_INT_MULT)) if (op->getDef()->getIn(1)->isConstant()) op = op->getDef()->getIn(0); if (vn->getAddr() < op->getAddr()) return -1; if (op->getAddr() < vn->getAddr()) return 1; } return 0; } /// Encode \b this as an \ element, with at least the following attributes: /// - \b space describes the AddrSpace /// - \b offset of the Varnode within the space /// - \b size of the Varnode is bytes /// /// Additionally the element will contain other optional attributes. /// \param encoder is the stream encoder void Varnode::encode(Encoder &encoder) const { encoder.openElement(ELEM_ADDR); loc.getSpace()->encodeAttributes(encoder,loc.getOffset(),size); encoder.writeUnsignedInteger(ATTRIB_REF, getCreateIndex()); if (mergegroup != 0) encoder.writeSignedInteger(ATTRIB_GRP, getMergeGroup()); if (isPersist()) encoder.writeBool(ATTRIB_PERSISTS, true); if (isAddrTied()) encoder.writeBool(ATTRIB_ADDRTIED, true); if (isUnaffected()) encoder.writeBool(ATTRIB_UNAFF, true); if (isInput()) encoder.writeBool(ATTRIB_INPUT, true); if (isVolatile()) encoder.writeBool(ATTRIB_VOLATILE, true); encoder.closeElement(ELEM_ADDR); } /// Invoke the printRaw method on the given Varnode pointer, but take into account that it /// might be null. /// \param s is the output stream to write to /// \param vn is the given Varnode pointer (may be null) void Varnode::printRaw(ostream &s,const Varnode *vn) { if (vn == (const Varnode *)0) { s << ""; return; } vn->printRaw(s); } /// \param m is the underlying address space manager VarnodeBank::VarnodeBank(AddrSpaceManager *m) : searchvn(0,Address(Address::m_minimal),(Datatype *)0) { manage = m; searchvn.flags = Varnode::input; // searchvn is always an input varnode of size 0 uniq_space = m->getUniqueSpace(); uniqbase = uniq_space->getTrans()->getUniqueStart(Translate::ANALYSIS); uniqid = uniqbase; create_index = 0; } void VarnodeBank::clear(void) { VarnodeLocSet::iterator iter; for(iter=loc_tree.begin();iter!=loc_tree.end();++iter) delete *iter; loc_tree.clear(); def_tree.clear(); uniqid = uniqbase; // Reset counter to base value create_index = 0; // Reset varnode creation index } /// The Varnode is created and inserted into the maps as \e free: not /// defined as the output of a p-code op or the input to a function. /// \param s is the size of the Varnode in bytes /// \param m is the starting address /// \param ct is the data-type of the new varnode (must not be NULL) /// \return the newly allocated Varnode object Varnode *VarnodeBank::create(int4 s,const Address &m,Datatype *ct) { Varnode *vn = new Varnode(s,m,ct); vn->create_index = create_index++; vn->lociter = loc_tree.insert(vn).first; // Frees can always be inserted without duplication vn->defiter = def_tree.insert(vn).first; return vn; } /// The Varnode is allocated in the \e unique space and automatically /// assigned an offset. The Varnode is initially \e free. /// \param s is the size of the Varnode in bytes /// \param ct is the data-type to assign (must not be NULL) Varnode *VarnodeBank::createUnique(int4 s,Datatype *ct) { Address addr(uniq_space,uniqid); // Generate a unique address uniqid += s; // Update counter for next call return create(s,addr,ct); // Build varnode with our generated address } /// The Varnode object is removed from the sorted lists and /// its memory reclaimed /// \param vn is the Varnode to remove void VarnodeBank::destroy(Varnode *vn) { if ((vn->getDef() != (PcodeOp *)0)||(!vn->hasNoDescend())) throw LowlevelError("Deleting integrated varnode"); loc_tree.erase(vn->lociter); def_tree.erase(vn->defiter); delete vn; } /// Enter the Varnode into both the \e location and \e definition based trees. /// Update the Varnode iterators and flags /// \param vn is the Varnode object to insert /// \return the inserted object, which may not be the same as the input Varnode Varnode *VarnodeBank::xref(Varnode *vn) { pair check; Varnode *othervn; check = loc_tree.insert( vn ); if (!check.second) { // Set already contains this varnode othervn = *(check.first); replace(vn,othervn); // Patch ops using the old varnode delete vn; return othervn; } // Otherwise a new insertion vn->lociter = check.first; vn->setFlags(Varnode::insert); vn->defiter = def_tree.insert(vn).first; // Insertion should also be new in def_tree return vn; } /// The Varnode is removed from the cross-referencing lists and reinserted as /// as if it were not defined by any PcodeOp and not an input to the function. /// If the Varnode was originally a PcodeOp output, this must be explicitly cleared. /// \param vn is the Varnode to modify void VarnodeBank::makeFree(Varnode *vn) { loc_tree.erase(vn->lociter); def_tree.erase(vn->defiter); vn->setDef((PcodeOp *)0); // Clear things that make vn non-free vn->clearFlags(Varnode::insert|Varnode::input|Varnode::indirect_creation); vn->lociter = loc_tree.insert(vn).first; // Re-insert as free varnode vn->defiter = def_tree.insert(vn).first; } /// Any PcodeOps that read \b oldvn are changed to read \b newvn /// \param oldvn is the old Varnode /// \param newvn is the Varnode to replace it with void VarnodeBank::replace(Varnode *oldvn,Varnode *newvn) { list::iterator iter,tmpiter; PcodeOp *op; int4 i; iter = oldvn->descend.begin(); while(iter!=oldvn->descend.end()) { op = *iter; tmpiter = iter++; if (op->output == newvn) continue; // Cannot be input to your own definition i = op->getSlot(oldvn); oldvn->descend.erase(tmpiter); // Sever the link fully op->clearInput(i); // Before attempting to build the new link newvn->addDescend(op); op->setInput(newvn,i); // This must be called AFTER descend is updated } oldvn->setFlags(Varnode::coverdirty); newvn->setFlags(Varnode::coverdirty); } /// Define the Varnode as an input formally; it is no longer considered \e free. /// Its position in the cross-referencing lists will change /// \param vn is the Varnode to mark /// \return the modified Varnode, which be a different object than the original Varnode *VarnodeBank::setInput(Varnode *vn) { if (!vn->isFree()) throw LowlevelError("Making input out of varnode which is not free"); if (vn->isConstant()) throw LowlevelError("Making input out of constant varnode"); loc_tree.erase(vn->lociter); // Erase the free version of varnode def_tree.erase(vn->defiter); vn->setInput(); // Set the input flag return xref(vn); } /// The Varnode must initially be \e free. It will be removed /// from the cross-referencing lists and reinserted as if its were /// the output of the given PcodeOp. It still must be explicitly set /// as the output. /// \param vn is the Varnode to modify /// \param op is the given PcodeOp /// \return the modified Varnode, which may be a different object than the original Varnode *VarnodeBank::setDef(Varnode *vn,PcodeOp *op) { if (!vn->isFree()) { ostringstream s; const Address &addr(op->getAddr()); s << "Defining varnode which is not free at " << addr.getShortcut(); addr.printRaw(s); throw LowlevelError(s.str()); } if (vn->isConstant()) { ostringstream s; const Address &addr(op->getAddr()); s << "Assignment to constant at " << addr.getShortcut(); addr.printRaw(s); throw LowlevelError(s.str()); } loc_tree.erase(vn->lociter); def_tree.erase(vn->defiter); vn->setDef(op); // Change the varnode to be defined return xref(vn); } /// The new Varnode object will already be put in the \e definition list as if /// it were the output of the given PcodeOp. The Varnode must still be set as the output. /// \param s is the size in bytes /// \param m is the starting address /// \param ct is the data-type to associate /// \param op is the given PcodeOp Varnode *VarnodeBank::createDef(int4 s,const Address &m, Datatype *ct,PcodeOp *op) { Varnode *vn = new Varnode(s,m,ct); vn->create_index = create_index++; vn->setDef(op); return xref(vn); } /// The new Varnode will be assigned from the \e unique space, and /// it will already be put in the \e definition list as if /// it were the output of the given PcodeOp. The Varnode must still be set as the output. /// \param s is the size in bytes /// \param ct is the data-type to associate /// \param op is the given PcodeOp Varnode *VarnodeBank::createDefUnique(int4 s,Datatype *ct,PcodeOp *op) { // Create unique varnode as output of op Address addr(uniq_space,uniqid); uniqid += s; return createDef(s,addr,ct,op); } /// Find a Varnode given its (loc,size) and the address where it is defined. /// \param s is the size of the Varnode /// \param loc is its starting address /// \param pc is the address where it is defined /// \param uniq is the sequence number or -1 if not specified /// \return the matching Varnode or NULL Varnode *VarnodeBank::find(int4 s,const Address &loc,const Address &pc,uintm uniq) const { VarnodeLocSet::const_iterator iter; Varnode *vn; PcodeOp *op; iter = beginLoc(s,loc,pc,uniq); while(iter != loc_tree.end()) { vn = *iter; if (vn->getSize() != s) break; if (vn->getAddr() != loc) break; op = vn->getDef(); if ((op!=(PcodeOp *)0)&&(op->getAddr() == pc)) { if ((uniq==~((uintm)0))||(op->getTime()==uniq)) return vn; } ++iter; } return (Varnode *)0; } /// Find a Varnode marked as a function input given its size and address /// \param s is the size /// \param loc is the starting address /// \return the match Varnode object or NULL Varnode *VarnodeBank::findInput(int4 s,const Address &loc) const { VarnodeLocSet::const_iterator iter; Varnode *vn; iter = beginLoc(s,loc,Varnode::input); if (iter != loc_tree.end()) { // There is only one possible varnode matching this vn = *iter; if (vn->isInput() && (vn->getSize()==s) && (vn->getAddr()==loc)) return vn; } return (Varnode *)0; } /// Find the first Varnode completely contained within the given range, which is /// also marked as a function input. /// \param s is the size of the range /// \param loc is the starting address of the range /// \return the Varnode object or NULL if no Varnode met the conditions Varnode *VarnodeBank::findCoveredInput(int4 s,const Address &loc) const { VarnodeDefSet::const_iterator iter,enditer; Varnode *vn; uintb highest = loc.getSpace()->getHighest(); uintb end = loc.getOffset() + s - 1; iter = beginDef(Varnode::input,loc); if (end==highest) { // Check for wrap around of address Address tmp(loc.getSpace(),highest); enditer = endDef(Varnode::input,tmp); } else enditer = beginDef(Varnode::input,loc+s); while(iter!=enditer) { vn = *iter++; // we know vn is input with vn->Loc in (loc,loc+s) if (vn->getOffset()+vn->getSize()-1 <= end) // vn is completely contained return vn; } return (Varnode *)0; } /// Search for the Varnode that completely contains the given range and is marked /// as an input to the function. If it exists, it is unique. /// \param s is the size of the range /// \param loc is the starting address of the range Varnode *VarnodeBank::findCoveringInput(int4 s,const Address &loc) const { VarnodeDefSet::const_iterator iter; Varnode *vn; iter = beginDef(Varnode::input,loc); if (iter != def_tree.end()) { vn = *iter; if ((vn->getAddr() != loc)&&(iter!=def_tree.begin())) { --iter; vn = *iter; } if (vn->isInput() && (vn->getSpace() == loc.getSpace()) && (vn->getOffset() <= loc.getOffset()) && (vn->getOffset() + vn->getSize()-1 >= loc.getOffset() + s -1)) return vn; } return (Varnode *)0; } /// \brief Beginning of Varnodes in given address space sorted by location /// /// \param spaceid is the given address space /// \return the beginning iterator VarnodeLocSet::const_iterator VarnodeBank::beginLoc(AddrSpace *spaceid) const { searchvn.loc = Address(spaceid,0); return loc_tree.lower_bound(&searchvn); } /// \brief Ending of Varnodes in given address space sorted by location /// /// \param spaceid is the given address space /// \return the ending iterator VarnodeLocSet::const_iterator VarnodeBank::endLoc(AddrSpace *spaceid) const { searchvn.loc = Address(manage->getNextSpaceInOrder(spaceid),0); return loc_tree.lower_bound(&searchvn); } /// \brief Beginning of Varnodes starting at a given address sorted by location /// /// \param addr is the given starting address /// \return the beginning iterator VarnodeLocSet::const_iterator VarnodeBank::beginLoc(const Address &addr) const { searchvn.loc = addr; return loc_tree.lower_bound(&searchvn); } /// \brief End of Varnodes starting at a given address sorted by location /// /// \param addr is the given starting address /// \return the ending iterator VarnodeLocSet::const_iterator VarnodeBank::endLoc(const Address &addr) const { if (addr.getOffset() == addr.getSpace()->getHighest()) { AddrSpace* space = addr.getSpace(); searchvn.loc = Address(manage->getNextSpaceInOrder(space),0); } else searchvn.loc = addr+1; return loc_tree.lower_bound(&searchvn); } /// \brief Beginning of Varnodes of given size and starting address sorted by location /// /// \param s is the given size /// \param addr is the given starting address /// \return the beginning iterator VarnodeLocSet::const_iterator VarnodeBank::beginLoc(int4 s,const Address &addr) const { searchvn.size = s; searchvn.loc = addr; VarnodeLocSet::const_iterator iter = loc_tree.lower_bound(&searchvn); searchvn.size = 0; // Return size to 0 return iter; } /// \brief End of Varnodes of given size and starting address sorted by location /// /// \param s is the given size /// \param addr is the given starting address /// \return the ending iterator VarnodeLocSet::const_iterator VarnodeBank::endLoc(int4 s,const Address &addr) const { searchvn.size = s+1; searchvn.loc = addr; VarnodeLocSet::const_iterator iter = loc_tree.lower_bound(&searchvn); searchvn.size = 0; // Return size to 0 return iter; } /// \brief Beginning of Varnodes sorted by location /// /// Varnodes are restricted by a given size and location and by the property /// - Varnode::input for Varnodes that are inputs to the function /// - Varnode::written for Varnodes that are defined by a PcodeOp /// - 0 for \e free Varnodes /// \param s is the given size /// \param addr is the given starting address /// \param fl is the property restriction /// \return the beginning iterator VarnodeLocSet::const_iterator VarnodeBank::beginLoc(int4 s,const Address &addr, uint4 fl) const { VarnodeLocSet::const_iterator iter; if (fl == Varnode::input) { searchvn.size = s; searchvn.loc = addr; iter = loc_tree.lower_bound(&searchvn); searchvn.size = 0; return iter; } if (fl == Varnode::written) { SeqNum sq(Address::m_minimal); // Minimal sequence number PcodeOp searchop(0,sq); searchvn.size = s; searchvn.loc = addr; searchvn.flags = Varnode::written; searchvn.def = &searchop; iter = loc_tree.lower_bound(&searchvn); searchvn.size = 0; searchvn.flags = Varnode::input; return iter; } SeqNum sq(Address::m_maximal); // Maximal sequence number PcodeOp searchop(0,sq); searchvn.size = s; searchvn.loc = addr; searchvn.flags = Varnode::written; searchvn.def = &searchop; iter = loc_tree.upper_bound(&searchvn); searchvn.size = 0; searchvn.flags = Varnode::input; return iter; } /// \brief End of Varnodes sorted by location /// /// Varnodes are restricted by a given size and location and by the property /// - Varnode::input for Varnodes that are inputs to the function /// - Varnode::written for Varnodes that are defined by a PcodeOp /// - 0 for \e free Varnodes /// \param s is the given size /// \param addr is the given starting address /// \param fl is the property restriction /// \return the ending iterator VarnodeLocSet::const_iterator VarnodeBank::endLoc(int4 s,const Address &addr, uint4 fl) const { VarnodeLocSet::const_iterator iter; searchvn.loc = addr; if (fl == Varnode::written) { searchvn.size = s; searchvn.flags = Varnode::written; SeqNum sq(Address::m_maximal); // Maximal sequence number PcodeOp searchop(0,sq); searchvn.def = &searchop; iter = loc_tree.upper_bound(&searchvn); searchvn.size = 0; searchvn.flags = Varnode::input; return iter; } else if (fl == Varnode::input) { searchvn.size = s; iter = loc_tree.upper_bound(&searchvn); searchvn.size = 0; return iter; } searchvn.size = s+1; iter = loc_tree.lower_bound(&searchvn); // Find following input varnode searchvn.size = 0; return iter; } /// \brief Beginning of Varnodes sorted by location /// /// Varnodes are restricted by a given size and location and by the /// sequence number of the PcodeOp defining it /// \param s is the given size /// \param addr is the given starting address /// \param pc is the address of the PcodeOp defining the Varnode /// \param uniq is the sequence number of the PcodeOp or -1 for now sequence number restriction /// \return the beginning iterator VarnodeLocSet::const_iterator VarnodeBank::beginLoc(int4 s,const Address &addr, const Address &pc,uintm uniq) const { // Find first varnode of given loc and size // defined at a particular location VarnodeLocSet::const_iterator iter; searchvn.size = s; searchvn.loc = addr; searchvn.flags = Varnode::written; if (uniq==~((uintm)0)) // If don't care about uniq uniq = 0; // find earliest SeqNum sq(pc,uniq); PcodeOp searchop(0,sq); searchvn.def = &searchop; iter = loc_tree.lower_bound(&searchvn); searchvn.size = 0; searchvn.flags = Varnode::input; return iter; } /// \brief End of Varnodes sorted by location /// /// Varnodes are restricted by a given size and location and by the /// sequence number of the PcodeOp defining it /// \param s is the given size /// \param addr is the given starting address /// \param pc is the address of the PcodeOp defining the Varnode /// \param uniq is the sequence number of the PcodeOp or -1 for now sequence number restriction /// \return the ending iterator VarnodeLocSet::const_iterator VarnodeBank::endLoc(int4 s,const Address &addr, const Address &pc,uintm uniq) const { VarnodeLocSet::const_iterator iter; searchvn.size = s; searchvn.loc = addr; searchvn.flags = Varnode::written; // if (uniq==~((uintm)0)) // uniq = 0; SeqNum sq(pc,uniq); PcodeOp searchop(0,sq); searchvn.def = &searchop; iter = loc_tree.upper_bound(&searchvn); searchvn.size = 0; searchvn.flags = Varnode::input; return iter; } /// \brief Given start, return maximal range of overlapping Varnodes /// /// Advance the iterator until no Varnodes after the iterator intersect any Varnodes /// from the initial Varnode through the current iterator. The range is returned as pairs /// of iterators to subranges. One subrange for each set of Varnodes with the same size and starting address. /// A final iterator to the next Varnode after the overlapping set is also passed back. /// \param iter is an iterator to the given start Varnode /// \param bounds holds the array of iterator pairs passed back /// \return the union of Varnode flags across the range uint4 VarnodeBank::overlapLoc(VarnodeLocSet::const_iterator iter,vector &bounds) const { Varnode *vn = *iter; AddrSpace *spc = vn->getSpace(); uintb off = vn->getOffset(); uintb maxOff = off + (vn->getSize() - 1); uint4 flags = vn->getFlags(); bounds.push_back(iter); iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written); bounds.push_back(iter); while(iter != loc_tree.end()) { vn = *iter; if (vn->getSpace() != spc || vn->getOffset() > maxOff) break; if (vn->isFree()) { iter = endLoc(vn->getSize(),vn->getAddr(),0); continue; } uintb endOff = vn->getOffset() + (vn->getSize() - 1); if (endOff > maxOff) maxOff = endOff; flags |= vn->getFlags(); bounds.push_back(iter); iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written); bounds.push_back(iter); } bounds.push_back(iter); return flags; } /// \brief Beginning of varnodes with set definition property /// /// Get an iterator to Varnodes in definition order restricted with the /// following properties: /// - Varnode::input for Varnodes which are inputs to the function /// - Varnode::written for Varnodes that are defined by a PcodeOp /// - 0 for \e free Varnodes /// \param fl is the property restriction /// \return the beginning iterator VarnodeDefSet::const_iterator VarnodeBank::beginDef(uint4 fl) const { VarnodeDefSet::const_iterator iter; if (fl == Varnode::input) return def_tree.begin(); // Inputs occur first with def_tree else if (fl == Varnode::written) { searchvn.loc = Address(Address::m_minimal); // Lowest possible location searchvn.flags = Varnode::written; SeqNum sq(Address::m_minimal); // Lowest possible seqnum PcodeOp searchop(0,sq); searchvn.def = &searchop; iter = def_tree.lower_bound(&searchvn); searchvn.flags = Varnode::input; // Reset flags return iter; } // Find the start of the frees searchvn.loc = Address(Address::m_maximal); // Maximal possible location searchvn.flags = Varnode::written; SeqNum sq(Address::m_maximal); // Maximal seqnum PcodeOp searchop(0,sq); searchvn.def = &searchop; iter = def_tree.upper_bound(&searchvn); searchvn.flags = Varnode::input; // Reset flags return iter; } /// \brief End of varnodes with set definition property /// /// Get an iterator to Varnodes in definition order restricted with the /// following properties: /// - Varnode::input for Varnodes which are inputs to the function /// - Varnode::written for Varnodes that are defined by a PcodeOp /// - 0 for \e free Varnodes /// \param fl is the property restriction /// \return the ending iterator VarnodeDefSet::const_iterator VarnodeBank::endDef(uint4 fl) const { VarnodeDefSet::const_iterator iter; if (fl == Varnode::input) { // Highest input is lowest written searchvn.loc = Address(Address::m_minimal); // Lowest possible location searchvn.flags = Varnode::written; SeqNum sq(Address::m_minimal); // Lowest possible seqnum PcodeOp searchop(0,sq); searchvn.def = &searchop; iter = def_tree.lower_bound(&searchvn); searchvn.flags = Varnode::input; // Reset flags return iter; } else if (fl == Varnode::written) { // Highest written searchvn.loc = Address(Address::m_maximal); // Maximal possible location searchvn.flags = Varnode::written; SeqNum sq(Address::m_maximal); // Maximal seqnum PcodeOp searchop(0,sq); searchvn.def = &searchop; iter = def_tree.upper_bound(&searchvn); searchvn.flags = Varnode::input; // Reset flags return iter; } return def_tree.end(); // Highest free is end of def_tree } /// \brief Beginning of varnodes starting at a given address with a set definition property /// /// Get an iterator to Varnodes in definition order. The starting address of the Varnodes /// must match the given address, and they are further restricted by the /// following properties: /// - Varnode::input for Varnodes which are inputs to the function /// - Varnode::written for Varnodes that are defined by a PcodeOp /// - 0 for \e free Varnodes /// \param fl is the property restriction /// \param addr is the given starting address /// \return the beginning iterator VarnodeDefSet::const_iterator VarnodeBank::beginDef(uint4 fl,const Address &addr) const { // Get varnodes with addr and with definition type VarnodeDefSet::const_iterator iter; if (fl == Varnode::written) throw LowlevelError("Cannot get contiguous written AND addressed"); else if (fl == Varnode::input) { searchvn.loc = addr; iter = def_tree.lower_bound(&searchvn); return iter; } // Find the start of the frees with a given address searchvn.loc = addr; searchvn.flags = 0; // Since a size 0 object shouldn't exist, an upper bound // should bump up to first free of addr with non-zero size iter = def_tree.upper_bound(&searchvn); searchvn.flags = Varnode::input; // Reset flags return iter; } /// \brief End of varnodes starting at a given address with a set definition property /// /// Get an iterator to Varnodes in definition order. The starting address of the Varnodes /// must match the given address, and they are further restricted by the /// following properties: /// - Varnode::input for Varnodes which are inputs to the function /// - Varnode::written for Varnodes that are defined by a PcodeOp /// - 0 for \e free Varnodes /// \param fl is the property restriction /// \param addr is the given starting address /// \return the ending iterator VarnodeDefSet::const_iterator VarnodeBank::endDef(uint4 fl,const Address &addr) const { VarnodeDefSet::const_iterator iter; if (fl == Varnode::written) throw LowlevelError("Cannot get contiguous written AND addressed"); else if (fl == Varnode::input) { searchvn.loc = addr; searchvn.size = 1000000; iter = def_tree.lower_bound(&searchvn); searchvn.size = 0; return iter; } // Find the start of the frees with a given address searchvn.loc = addr; searchvn.size = 1000000; searchvn.flags = 0; // Since a size 0 object shouldn't exist, an upper bound // should bump up to first free of addr with non-zero size iter = def_tree.lower_bound(&searchvn); searchvn.flags = Varnode::input; // Reset flags searchvn.size = 0; return iter; } #ifdef VARBANK_DEBUG /// Check tree order is still accurate void VarnodeBank::verifyIntegrity(void) const { VarnodeLocSet::iterator iter; Varnode *vn,*lastvn; if (loc_tree.empty()) return; iter = loc_tree.begin(); lastvn = *iter++; if (def_tree.end() == def_tree.find(lastvn)) throw LowlevelError("Varbank first loc missing in def"); for(;iter!=loc_tree.end();++iter) { vn = *iter; if (def_tree.end() == def_tree.find(vn)) throw LowlevelError("Varbank loc missing in def"); if (*vn < *lastvn) throw LowlevelError("Varbank locdef integrity test failed"); lastvn = vn; } VarnodeDefSet::iterator diter; VarnodeCompareDefLoc cmp; diter = def_tree.begin(); lastvn = *diter++; if (loc_tree.end() == loc_tree.find(lastvn)) throw LowlevelError("Varbank first def missing in loc"); for(;diter!=def_tree.end();++diter) { vn = *diter; if (loc_tree.end() == loc_tree.find(vn)) throw LowlevelError("Varbank def missing in loc"); if (cmp(vn,lastvn)) throw LowlevelError("Varbank defloc integrity test failed"); lastvn = vn; } } #endif /// \brief Return \b true if the alternate path looks more valid than the main path. /// /// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN. /// Evaluate which path most likely represents actual parameter/return value passing, /// based on traversal information about each path. /// \param vn is the Varnode terminating the \e alternate path /// \param flags indicates traversals for both paths /// \return \b true if the alternate path is preferred bool TraverseNode::isAlternatePathValid(const Varnode *vn,uint4 flags) { if ((flags & (indirect | indirectalt)) == indirect) // If main path traversed an INDIRECT but the alternate did not return true; // Main path traversed INDIRECT, alternate did not if ((flags & (indirect | indirectalt)) == indirectalt) return false; // Alternate path traversed INDIRECT, main did not if ((flags & actionalt) != 0) return true; // Alternate path traversed a dedicated COPY if (vn->loneDescend() == (PcodeOp*)0) return false; const PcodeOp *op = vn->getDef(); if (op == (PcodeOp*)0) return true; while(op->isIncidentalCopy() && op->code() == CPUI_COPY) { // Skip any incidental COPY vn = op->getIn(0); if (vn->loneDescend() == (PcodeOp *)0) return false; op = vn->getDef(); if (op == (PcodeOp *)0) return true; } return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values } /// Return true if \b vn1 contains the high part and \b vn2 the low part /// of what was(is) a single value. /// \param vn1 is the putative high Varnode /// \param vn2 is the putative low Varnode /// \return \b true if they are pieces of a whole bool contiguous_test(Varnode *vn1,Varnode *vn2) { if (vn1->isInput()||vn2->isInput()) { return false; } if ((!vn1->isWritten())||(!vn2->isWritten())) return false; PcodeOp *op1 = vn1->getDef(); PcodeOp *op2 = vn2->getDef(); Varnode *vnwhole; switch(op1->code()) { case CPUI_SUBPIECE: if (op2->code() != CPUI_SUBPIECE) return false; vnwhole = op1->getIn(0); if (op2->getIn(0) != vnwhole) return false; if (op2->getIn(1)->getOffset() != 0) return false; // Must be least sig if (op1->getIn(1)->getOffset() != vn2->getSize()) return false; // Must be contiguous return true; default: return false; } } /// Assuming vn1,vn2 has passed the contiguous_test(), return /// the Varnode containing the whole value. /// \param data is the underlying function /// \param vn1 is the high Varnode /// \param vn2 is the low Varnode /// \return the whole Varnode Varnode *findContiguousWhole(Funcdata &data,Varnode *vn1,Varnode *vn2) { if (vn1->isWritten()) if (vn1->getDef()->code() == CPUI_SUBPIECE) return vn1->getDef()->getIn(0); return (Varnode *)0; } } // End namespace ghidra