/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ // SPDX-License-Identifier: GPL-3.0 /** * @file KnownState.h * @author Christian * @date 2015 * Contains knowledge about the state of the virtual machine at a specific instruction. */ #pragma once #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wredeclared-class-member" #endif // defined(__clang__) // Disable warning about nodiscard. Could be a compiler bug, might be removed in the future. #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4834) #endif #include #if defined(_MSC_VER) #pragma warning(pop) #endif #if defined(__clang__) #pragma clang diagnostic pop #endif // defined(__clang__) #include #include #include #include #include #include #include #include #include #include #include #include namespace solidity::langutil { struct SourceLocation; } namespace solidity::evmasm { class AssemblyItem; using AssemblyItems = std::vector; /** * Class to infer and store knowledge about the state of the virtual machine at a specific * instruction. * * The general workings are that for each assembly item that is fed, an equivalence class is * derived from the operation and the equivalence class of its arguments. DUPi, SWAPi and some * arithmetic instructions are used to infer equivalences while these classes are determined. */ class KnownState { public: using Id = ExpressionClasses::Id; struct StoreOperation { enum Target { Invalid, Memory, Storage }; bool isValid() const { return target != Invalid; } Target target{Invalid}; Id slot{std::numeric_limits::max()}; unsigned sequenceNumber{std::numeric_limits::max()}; Id expression{std::numeric_limits::max()}; }; explicit KnownState( std::shared_ptr _expressionClasses = std::make_shared() ): m_expressionClasses(std::move(_expressionClasses)) { } /// Streams debugging information to @a _out. std::ostream& stream(std::ostream& _out) const; /// Feeds the item into the system for analysis. /// @returns a possible store operation StoreOperation feedItem(AssemblyItem const& _item, bool _copyItem = false); /// Resets any knowledge about storage. void resetStorage() { m_storageContent.clear(); } /// Resets any knowledge about storage. void resetMemory() { m_memoryContent.clear(); } /// Resets known Keccak-256 hashes void resetKnownKeccak256Hashes() { m_knownKeccak256Hashes.clear(); } /// Resets any knowledge about the current stack. void resetStack() { m_stackElements.clear(); m_stackHeight = 0; } /// Resets any knowledge. void reset() { resetStorage(); resetMemory(); resetKnownKeccak256Hashes(); resetStack(); } unsigned sequenceNumber() const { return m_sequenceNumber; } /// Replaces the state by the intersection with _other, i.e. only equal knowledge is retained. /// If the stack heighht is different, the smaller one is used and the stack is compared /// relatively. /// @param _combineSequenceNumbers if true, sets the sequence number to the maximum of both void reduceToCommonKnowledge(KnownState const& _other, bool _combineSequenceNumbers); /// @returns a shared pointer to a copy of this state. std::shared_ptr copy() const { return std::make_shared(*this); } /// @returns true if the knowledge about the state of both objects is (known to be) equal. bool operator==(KnownState const& _other) const; /// Retrieves the current equivalence class fo the given stack element (or generates a new /// one if it does not exist yet). Id stackElement(int _stackHeight, langutil::SourceLocation const& _location); /// @returns the stackElement relative to the current stack height. Id relativeStackElement(int _stackOffset, langutil::SourceLocation const& _location = {}); /// @returns its set of tags if the given expression class is a known tag union; returns a set /// containing the tag if it is a PushTag expression and the empty set otherwise. std::set tagsInExpression(Id _expressionId); /// During analysis, different tags on the stack are partially treated as the same class. /// This removes such classes not to confuse later analyzers. void clearTagUnions(); int stackHeight() const { return m_stackHeight; } std::map const& stackElements() const { return m_stackElements; } ExpressionClasses& expressionClasses() const { return *m_expressionClasses; } std::map const& storageContent() const { return m_storageContent; } private: /// Assigns a new equivalence class to the next sequence number of the given stack element. void setStackElement(int _stackHeight, Id _class); /// Swaps the given stack elements in their next sequence number. void swapStackElements(int _stackHeightA, int _stackHeightB, langutil::SourceLocation const& _location); /// Increments the sequence number, deletes all storage information that might be overwritten /// and stores the new value at the given slot. /// @returns the store operation, which might be invalid if storage was not modified StoreOperation storeInStorage(Id _slot, Id _value, langutil::SourceLocation const& _location); /// Retrieves the current value at the given slot in storage or creates a new special sload class. Id loadFromStorage(Id _slot, langutil::SourceLocation const& _location); /// Increments the sequence number, deletes all memory information that might be overwritten /// and stores the new value at the given slot. /// @returns the store operation, which might be invalid if memory was not modified StoreOperation storeInMemory(Id _slot, Id _value, langutil::SourceLocation const& _location); /// Retrieves the current value at the given slot in memory or creates a new special mload class. Id loadFromMemory(Id _slot, langutil::SourceLocation const& _location); /// Finds or creates a new expression that applies the Keccak-256 hash function to the contents in memory. Id applyKeccak256(Id _start, Id _length, langutil::SourceLocation const& _location); /// @returns a new or already used Id representing the given set of tags. Id tagUnion(std::set _tags); /// Current stack height, can be negative. int m_stackHeight = 0; /// Current stack layout, mapping stack height -> equivalence class std::map m_stackElements; /// Current sequence number, this is incremented with each modification to storage or memory. unsigned m_sequenceNumber = 1; /// Knowledge about storage content. std::map m_storageContent; /// Knowledge about memory content. Keys are memory addresses, note that the values overlap /// and are not contained here if they are not completely known. std::map m_memoryContent; /// Keeps record of all Keccak-256 hashes that are computed. The first parameter in the /// std::pair corresponds to memory content and the second parameter corresponds to the length /// that is accessed. std::map, unsigned>, Id> m_knownKeccak256Hashes; /// Structure containing the classes of equivalent expressions. std::shared_ptr m_expressionClasses; /// Container for unions of tags stored on the stack. boost::bimap> m_tagUnions; }; }