function hexStringToBytes32(
bytes memory str,
uint256 idx,
uint256 lastIdx
) internal pure returns (bytes32 r, bool valid) {
if (lastIdx - idx > 32) return (bytes32(0x0), false);
valid = true;
assembly {
// check that the index to read to is not past the end of the string
if gt(lastIdx, mload(str)) {
revert(0, 0)
}
function getHex(c) -> ascii {
// chars 48-57: 0-9
if and(gt(c, 47), lt(c, 58)) {
ascii := sub(c, 48)
leave
}
// chars 65-70: A-F
if and(gt(c, 64), lt(c, 71)) {
ascii := add(sub(c, 65), 10)
leave
}
// chars 97-102: a-f
if and(gt(c, 96), lt(c, 103)) {
ascii := add(sub(c, 97), 10)
leave
}
// invalid char
ascii := 0xff
}
let ptr := add(str, 32)
for {
let i := idx
} lt(i, lastIdx) {
i := add(i, 2)
} {
let byte1 := getHex(byte(0, mload(add(ptr, i))))
let byte2 := getHex(byte(0, mload(add(ptr, add(i, 1)))))
// if either byte is invalid, set invalid and break loop
if or(eq(byte1, 0xff), eq(byte2, 0xff)) {
valid := false
break
}
let combined := or(shl(4, byte1), byte2)
r := or(shl(8, r), combined)
}
}
Judge has assessed an item in Issue #49 as 2 risk. The relevant finding follows:
QA9. hexStringToBytes32() fails to check that range [idx, lastIdx] is within 32 bytes range and thus the returned r will fit into bytes32.
https://github.com/code-423n4/2023-04-ens/blob/45ea10bacb2a398e14d711fe28d1738271cd7640/contracts/utils/HexUtils.sol#L11-L60
Mitigation: Introduce the check:
function hexStringToBytes32( bytes memory str, uint256 idx, uint256 lastIdx ) internal pure returns (bytes32 r, bool valid) {
if (lastIdx - idx > 32) return (bytes32(0x0), false); valid = true; assembly { // check that the index to read to is not past the end of the string if gt(lastIdx, mload(str)) { revert(0, 0) }
}