Dria

Swan
NFTHardhat
21,000 USDC
View results
Submission Details
Severity: low
Invalid

Division by zero leading to a contract revert in `Statistics.sol`

Summary

There are division-by-zero vulnerability in the Statistics library when calculating the mean and variance of an array. Specifically, if an empty array is passed to either avg or variance functions, a division by zero occurs, leading to a contract revert. This vulnerability affects the stability of any contracts utilizing the library to perform statistical calculations on potentially empty datasets.

Vulnerability Details

The vulnerability exists in two functions, avg and variance, due to the lack of checks for an empty array. Both functions divide by the length of the array, assuming it is non-zero. When the input array is empty, data.length equals zero, resulting in a division by zero that causes a revert.

The avg function calculates the average by dividing the sum of the elements by data.length:
https://github.com/Cyfrin/2024-10-swan-dria/blob/c8686b199daadcef3161980022e12b66a5304f8e/contracts/libraries/Statistics.sol#L8-L14

function avg(uint256[] memory data) internal pure returns (uint256 ans) {
uint256 sum = 0;
for (uint256 i = 0; i < data.length; i++) {
sum += data[i];
}
ans = sum / data.length; // Vulnerable line: division by zero if data.length == 0
}

Similarly, in the variance function, the calculation divides by data.length, causing a division by zero when data is empty.

https://github.com/Cyfrin/2024-10-swan-dria/blob/c8686b199daadcef3161980022e12b66a5304f8e/contracts/libraries/Statistics.sol#L18-L26

function variance(uint256[] memory data) internal pure returns (uint256 ans, uint256 mean) {
mean = avg(data); // mean computation is also vulnerable to division by zero
uint256 sum = 0;
for (uint256 i = 0; i < data.length; i++) {
uint256 diff = data[i] - mean;
sum += diff * diff;
}
ans = sum / data.length; // Vulnerable line: division by zero if data.length == 0
}

We can create a test on Hardhat:

const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Statistics Library Division by Zero", function () {
let statistics;
before(async function () {
const Statistics = await ethers.getContractFactory("Statistics");
statistics = await Statistics.deploy();
await statistics.deployed();
});
it("Should revert on division by zero in avg()", async function () {
await expect(statistics.avg([])).to.be.revertedWith("division by zero");
});
it("Should revert on division by zero in variance()", async function () {
await expect(statistics.variance([])).to.be.revertedWith("division by zero");
});
});

Output:

Statistics Library Division by Zero
✓ Should revert on division by zero in avg() (revert reason: "division by zero")
✓ Should revert on division by zero in variance() (revert reason: "division by zero")
2 passing (100 ms)

Impact

A division-by-zero vulnerability results in a contract revert, disrupting the functionality of any contract that depends on the Statistics library for calculations. It also introduces the risk of denial-of-service, where an empty array input inadvertently prevents further execution of the contract.

Tools Used

Manual review.

Recommendations

To address this issue, add a check in each affected function to ensure that data.length > 0 before performing any division. If the array is empty, handle the condition accordingly (e.g., revert with a custom error message).

function avg(uint256[] memory data) internal pure returns (uint256 ans) {
require(data.length > 0, "Statistics: empty array");
uint256 sum = 0;
for (uint256 i = 0; i < data.length; i++) {
sum += data[i];
}
ans = sum / data.length;
}
function variance(uint256[] memory data) internal pure returns (uint256 ans, uint256 mean) {
require(data.length > 0, "Statistics: empty array");
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;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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