mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Fix sequence number bug.
This bug resulted in incorrect storage access in some situations. The reason was that when intersecting states, the sequence numbers were not handled and thus some operations with too low sequence numbers were used during code generation.
This commit is contained in:
parent
6e98243ead
commit
1b1b6651cd
@ -221,22 +221,36 @@ void ControlFlowGraph::gatherKnowledge()
|
|||||||
KnownStatePointer emptyState = make_shared<KnownState>();
|
KnownStatePointer emptyState = make_shared<KnownState>();
|
||||||
bool unknownJumpEncountered = false;
|
bool unknownJumpEncountered = false;
|
||||||
|
|
||||||
vector<pair<BlockId, KnownStatePointer>> workQueue({make_pair(BlockId::initial(), emptyState->copy())});
|
struct WorkQueueItem {
|
||||||
|
BlockId blockId;
|
||||||
|
KnownStatePointer state;
|
||||||
|
set<BlockId> blocksSeen;
|
||||||
|
};
|
||||||
|
|
||||||
|
vector<WorkQueueItem> workQueue{WorkQueueItem{BlockId::initial(), emptyState->copy(), set<BlockId>()}};
|
||||||
|
auto addWorkQueueItem = [&](WorkQueueItem const& _currentItem, BlockId _to, KnownStatePointer const& _state)
|
||||||
|
{
|
||||||
|
WorkQueueItem item;
|
||||||
|
item.blockId = _to;
|
||||||
|
item.state = _state->copy();
|
||||||
|
item.blocksSeen = _currentItem.blocksSeen;
|
||||||
|
item.blocksSeen.insert(_currentItem.blockId);
|
||||||
|
workQueue.push_back(move(item));
|
||||||
|
};
|
||||||
|
|
||||||
while (!workQueue.empty())
|
while (!workQueue.empty())
|
||||||
{
|
{
|
||||||
|
WorkQueueItem item = move(workQueue.back());
|
||||||
|
workQueue.pop_back();
|
||||||
//@todo we might have to do something like incrementing the sequence number for each JUMPDEST
|
//@todo we might have to do something like incrementing the sequence number for each JUMPDEST
|
||||||
assertThrow(!!workQueue.back().first, OptimizerException, "");
|
assertThrow(!!item.blockId, OptimizerException, "");
|
||||||
if (!m_blocks.count(workQueue.back().first))
|
if (!m_blocks.count(item.blockId))
|
||||||
{
|
|
||||||
workQueue.pop_back();
|
|
||||||
continue; // too bad, we do not know the tag, probably an invalid jump
|
continue; // too bad, we do not know the tag, probably an invalid jump
|
||||||
}
|
BasicBlock& block = m_blocks.at(item.blockId);
|
||||||
BasicBlock& block = m_blocks.at(workQueue.back().first);
|
KnownStatePointer state = item.state;
|
||||||
KnownStatePointer state = workQueue.back().second;
|
|
||||||
workQueue.pop_back();
|
|
||||||
if (block.startState)
|
if (block.startState)
|
||||||
{
|
{
|
||||||
state->reduceToCommonKnowledge(*block.startState);
|
state->reduceToCommonKnowledge(*block.startState, !item.blocksSeen.count(item.blockId));
|
||||||
if (*state == *block.startState)
|
if (*state == *block.startState)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -270,12 +284,12 @@ void ControlFlowGraph::gatherKnowledge()
|
|||||||
unknownJumpEncountered = true;
|
unknownJumpEncountered = true;
|
||||||
for (auto const& it: m_blocks)
|
for (auto const& it: m_blocks)
|
||||||
if (it.second.begin < it.second.end && m_items[it.second.begin].type() == Tag)
|
if (it.second.begin < it.second.end && m_items[it.second.begin].type() == Tag)
|
||||||
workQueue.push_back(make_pair(it.first, emptyState->copy()));
|
workQueue.push_back(WorkQueueItem{it.first, emptyState, set<BlockId>()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
for (auto tag: tags)
|
for (auto tag: tags)
|
||||||
workQueue.push_back(make_pair(BlockId(tag), state->copy()));
|
addWorkQueueItem(item, BlockId(tag), state);
|
||||||
}
|
}
|
||||||
else if (block.begin <= pc && pc < block.end)
|
else if (block.begin <= pc && pc < block.end)
|
||||||
state->feedItem(m_items.at(pc++));
|
state->feedItem(m_items.at(pc++));
|
||||||
@ -287,7 +301,7 @@ void ControlFlowGraph::gatherKnowledge()
|
|||||||
block.endType == BasicBlock::EndType::HANDOVER ||
|
block.endType == BasicBlock::EndType::HANDOVER ||
|
||||||
block.endType == BasicBlock::EndType::JUMPI
|
block.endType == BasicBlock::EndType::JUMPI
|
||||||
)
|
)
|
||||||
workQueue.push_back(make_pair(block.next, state->copy()));
|
addWorkQueueItem(item, block.next, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all blocks we never visited here. This might happen because a tag is pushed but
|
// Remove all blocks we never visited here. This might happen because a tag is pushed but
|
||||||
|
@ -175,7 +175,7 @@ template <class _Mapping> void intersect(_Mapping& _this, _Mapping const& _other
|
|||||||
it = _this.erase(it);
|
it = _this.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KnownState::reduceToCommonKnowledge(KnownState const& _other)
|
void KnownState::reduceToCommonKnowledge(KnownState const& _other, bool _combineSequenceNumbers)
|
||||||
{
|
{
|
||||||
int stackDiff = m_stackHeight - _other.m_stackHeight;
|
int stackDiff = m_stackHeight - _other.m_stackHeight;
|
||||||
for (auto it = m_stackElements.begin(); it != m_stackElements.end();)
|
for (auto it = m_stackElements.begin(); it != m_stackElements.end();)
|
||||||
@ -213,9 +213,11 @@ void KnownState::reduceToCommonKnowledge(KnownState const& _other)
|
|||||||
|
|
||||||
intersect(m_storageContent, _other.m_storageContent);
|
intersect(m_storageContent, _other.m_storageContent);
|
||||||
intersect(m_memoryContent, _other.m_memoryContent);
|
intersect(m_memoryContent, _other.m_memoryContent);
|
||||||
|
if (_combineSequenceNumbers)
|
||||||
|
m_sequenceNumber = max(m_sequenceNumber, _other.m_sequenceNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KnownState::operator==(const KnownState& _other) const
|
bool KnownState::operator==(KnownState const& _other) const
|
||||||
{
|
{
|
||||||
if (m_storageContent != _other.m_storageContent || m_memoryContent != _other.m_memoryContent)
|
if (m_storageContent != _other.m_storageContent || m_memoryContent != _other.m_memoryContent)
|
||||||
return false;
|
return false;
|
||||||
|
@ -100,13 +100,12 @@ public:
|
|||||||
void reset() { resetStorage(); resetMemory(); resetStack(); }
|
void reset() { resetStorage(); resetMemory(); resetStack(); }
|
||||||
|
|
||||||
unsigned sequenceNumber() const { return m_sequenceNumber; }
|
unsigned sequenceNumber() const { return m_sequenceNumber; }
|
||||||
/// Manually increments the storage and memory sequence number.
|
|
||||||
void incrementSequenceNumber() { m_sequenceNumber += 2; }
|
|
||||||
|
|
||||||
/// Replaces the state by the intersection with _other, i.e. only equal knowledge is retained.
|
/// 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
|
/// If the stack heighht is different, the smaller one is used and the stack is compared
|
||||||
/// relatively.
|
/// relatively.
|
||||||
void reduceToCommonKnowledge(KnownState const& _other);
|
/// @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.
|
/// @returns a shared pointer to a copy of this state.
|
||||||
std::shared_ptr<KnownState> copy() const { return std::make_shared<KnownState>(*this); }
|
std::shared_ptr<KnownState> copy() const { return std::make_shared<KnownState>(*this); }
|
||||||
|
Loading…
Reference in New Issue
Block a user