Dria

Swan
NFTHardhat
21,000 USDC
View results
Submission Details
Severity: high
Valid

Underflow in Variance Calculation in `finalizeValidation` Function makes the Oracle Validation System Completely Non-Functional

Summary

The variance calculation in the Statistics library always reverts when processing normal validation score distributions, making the entire oracle validation system non-functional.

This causes the finalizeValidation() function to fail in all realistic scenarios, preventing tasks from completing and permanently locking funds in the contract.

Vulnerability Details

The issue stems from using unchecked subtraction with uint256 in the variance calculation:

function variance(uint256[] memory data) internal pure returns (uint256 ans, uint256 mean) {
mean = avg(data);
uint256 sum = 0;
for (uint256 i = 0; i < data.length; i++) {
@ uint256 diff = data[i] - mean;
sum += diff * diff;
}
ans = sum / data.length;
}

This variance calculation is used in the task validation process:

function finalizeValidation(uint256 taskId) private {
TaskRequest storage task = requests[taskId];
// compute score for each generation
for (uint256 g_i = 0; g_i < task.parameters.numGenerations; g_i++) {
// get the scores for this generation, i.e. the g_i-th element of each validation
uint256[] memory scores = new uint256[]();
for (uint256 v_i = 0; v_i < task.parameters.numValidations; v_i++) {
scores[v_i] = validations[taskId][v_i].scores[g_i];
}
// compute the mean and standard deviation
@ (uint256 _stddev, uint256 _mean) = Statistics.stddev(scores);
// compute the score for this generation as the "inner-mean"
// and send rewards to validators that are within the range
uint256 innerSum = 0;
uint256 innerCount = 0;
for (uint256 v_i = 0; v_i < task.parameters.numValidations; ++v_i) {
uint256 score = scores[v_i];
if ((score >= _mean - _stddev) && (score <= _mean + _stddev)) {
innerSum += score;
innerCount++;
// send validation fee to the validator
_increaseAllowance(validations[taskId][v_i].validator, task.validatorFee);
}
}
// set score for this generation as the average of inner scores
uint256 inner_score = innerCount == 0 ? 0 : innerSum / innerCount;
responses[taskId][g_i].score = inner_score;
}

In any normal distribution of validator scores, some scores will be below the mean. Due to Solidity 0.8.x's checked arithmetic, this causes the transaction to revert. For example:

// Example validator scores
scores = [60, 70, 80, 90, 100] // mean = 80
// When calculating variance:
60 - 80 = REVERT (underflow protection)

The only scenario where validation can complete is if all validators submit exactly the same score, which defeats the purpose of having multiple validators or there is only 1 validator

Impact

The impact is critical because:

All tasks requiring validation will fail to complete

The validation system cannot process any realistic score distributions

Generator fees are locked until validation completes

Validator fees are locked until validation completes

Since validation cannot complete, these funds become permanently locked

Tools Used

Manual Review

Recommendations

function variance(uint256[] memory data) internal pure returns (uint256 ans, uint256 mean) {
mean = avg(data);
uint256 sum = 0;
for (uint256 i = 0; i < data.length; i++) {
- uint256 diff = data[i] - mean;
+ uint256 diff = data[i] > mean ? data[i] - mean : mean - data[i];
sum += diff * diff;
}
ans = sum / data.length;
}

Before subtracting mean from data[i] , make sure data[i] > mean and subtract data[i] from mean in the other case

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Underflow in computing variance

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.