Function initTickTracking initializes the tick tracking data structure, but does not validate that tick is within the min/max tick range for the pool. This could allow initializing invalid tick values.
Here is the line in initTickTracking that could initialize invalid tick values.
function initTickTracking(bytes32 poolIdx, int24 tick) internal {
StorageLayout.TickTracking memory tickTrackingData = StorageLayout
.TickTracking(uint32(block.timestamp), 0);
tickTracking_[poolIdx][tick].push(tickTrackingData);
}
The tick parameter is used to index into the tickTracking_ mapping without validating it is within the min/max tick range for the pool.
The initTickTracking function is used to initialize the tick tracking data structure for a specific tick in a pool. This data structure tracks when ticks become activated/deactivated over time.
It is defined as:
function initTickTracking(bytes32 poolIdx, int24 tick) internal {
//...
}
The key parameters are:
Within the function, it does the following:
Creates a TickTracking struct to represent the initial data for this tick:
StorageLayout.TickTracking memory tickTrackingData = StorageLayout
.TickTracking(uint32(block.timestamp), 0);
Pushes this tick tracking data into the mapping that stores the info per tick:
tickTracking_[poolIdx][tick].push(tickTrackingData);
The issue here is that tick is used to index directly into the tickTracking_ mapping without validating it is within the min/max range for the pool.
The min/max ticks are set for each pool based on the tick spacing (number of ticks in the entire range). This range is stored in the PoolRegistry.
By not validating tick is within range, it is possible to initialize tick tracking for arbitrary tick values, even those outside the min/max bounds for the pool.
This could then corrupt the tick tracking data structure by inserting invalid entries. Later accounting logic relies on this structure being valid.
> Here is a simple proof of concept that demonstrates initializing the tick tracking for an invalid, out-of-range tick value in the initTickTracking function:
contract Exploit {
address constant pool = 0x...; // some pool address
function exploitInitTickTracking() external {
IUniswapV3Pool poolContract = IUniswapV3Pool(pool);
bytes32 poolKey = poolContract.poolId();
// Let's say this pool has a min tick of -887272 and max tick of -887270
// Calling initTickTracking with a tick below the min
LiquidityMining(pool).initTickTracking(poolKey, -887300);
// This initializes tick tracking for an invalid tick below the min
// Now the tickTracking mapping has an invalid entry for tick -887300
}
}
Key points:
This simple exploit shows how failing to validate the tick input can lead to improper state in the contractβs data structures.
Vs
The min/max tick values for a pool can be obtained from the PoolRegistry contract using PoolRegistry.getPoolData(poolIdx). So to validate, you could add something like:
function initTickTracking(bytes32 poolIdx, int24 tick) internal {
(,,int24 tickSpacing,, int24 minTick, int24 maxTick,) = PoolRegistry.getPoolData(poolIdx);
require(tick >= minTick && tick < maxTick, "Invalid tick");
//...rest of function
}
This would prevent initializing the tick tracking data structure for invalid tick values outside the min/max range.
Invalid Validation
The text was updated successfully, but these errors were encountered:
All reactions