diff --git a/contracts/v0.8/cbor/AccountCbor.sol b/contracts/v0.8/cbor/AccountCbor.sol index 9488a543..c75fd5c2 100644 --- a/contracts/v0.8/cbor/AccountCbor.sol +++ b/contracts/v0.8/cbor/AccountCbor.sol @@ -25,6 +25,7 @@ import "../types/AccountTypes.sol"; import "../utils/CborDecode.sol"; import "../utils/Misc.sol"; +import "../utils/Errors.sol"; /// @title This library is a set of functions meant to handle CBOR parameters serialization and return values deserialization for Account actor exported methods. /// @author Zondax AG @@ -59,7 +60,10 @@ library AccountCBOR { uint len; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } + (ret.signature, byteIdx) = rawResp.readBytes(byteIdx); (ret.message, byteIdx) = rawResp.readBytes(byteIdx); diff --git a/contracts/v0.8/cbor/BigIntCbor.sol b/contracts/v0.8/cbor/BigIntCbor.sol index 0fbca392..06df8827 100644 --- a/contracts/v0.8/cbor/BigIntCbor.sol +++ b/contracts/v0.8/cbor/BigIntCbor.sol @@ -49,6 +49,9 @@ library BigIntCBOR { return CommonTypes.BigInt(hex"00", false); } + // Validate the sign byte + require(raw[0] == 0x00 || raw[0] == 0x01, "Invalid sign byte: must be 0x00 or 0x01"); + bytes memory val = new bytes(raw.length - 1); bool neg = false; diff --git a/contracts/v0.8/cbor/DataCapCbor.sol b/contracts/v0.8/cbor/DataCapCbor.sol index e9de9ab7..60a7e864 100644 --- a/contracts/v0.8/cbor/DataCapCbor.sol +++ b/contracts/v0.8/cbor/DataCapCbor.sol @@ -87,7 +87,10 @@ library DataCapCBOR { bytes memory tmp; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 3); + if (!(len == 3)) { + revert Errors.InvalidArrayLength(3, len); + } + (tmp, byteIdx) = rawResp.readBytes(byteIdx); ret.from_balance = tmp.deserializeBigInt(); @@ -133,7 +136,9 @@ library DataCapCBOR { bytes memory tmp; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 4); + if (!(len == 4)) { + revert Errors.InvalidArrayLength(4, len); + } (tmp, byteIdx) = rawResp.readBytes(byteIdx); ret.from_balance = tmp.deserializeBigInt(); @@ -217,7 +222,10 @@ library DataCapCBOR { bytes memory tmp; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } + (tmp, byteIdx) = rawResp.readBytes(byteIdx); ret.balance = tmp.deserializeBigInt(); diff --git a/contracts/v0.8/cbor/FilecoinCbor.sol b/contracts/v0.8/cbor/FilecoinCbor.sol index 3c8ab44d..f020a293 100644 --- a/contracts/v0.8/cbor/FilecoinCbor.sol +++ b/contracts/v0.8/cbor/FilecoinCbor.sol @@ -24,6 +24,7 @@ import "@ensdomains/buffer/contracts/Buffer.sol"; import "../utils/CborDecode.sol"; import "../utils/Misc.sol"; +import "../utils/Errors.sol"; import "../types/CommonTypes.sol"; @@ -145,7 +146,10 @@ library FilecoinCBOR { bytes memory tmp; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 1); + if (!(len == 1)) { + revert Errors.InvalidArrayLength(1, len); + } + (tmp, byteIdx) = rawResp.readBytes(byteIdx); return tmp.deserializeBigInt(); diff --git a/contracts/v0.8/cbor/MarketCbor.sol b/contracts/v0.8/cbor/MarketCbor.sol index 9e16ba4c..dd34c11b 100644 --- a/contracts/v0.8/cbor/MarketCbor.sol +++ b/contracts/v0.8/cbor/MarketCbor.sol @@ -25,6 +25,7 @@ import "../types/MarketTypes.sol"; import "../types/CommonTypes.sol"; import "../utils/Misc.sol"; +import "../utils/Errors.sol"; import "../utils/FilAddresses.sol"; import "../utils/CborDecode.sol"; @@ -37,6 +38,7 @@ import "./FilecoinCbor.sol"; library MarketCBOR { using CBOR for CBOR.CBORBuffer; using CBORDecoder for bytes; + using BigIntCBOR for *; using FilecoinCBOR for *; @@ -68,7 +70,9 @@ library MarketCBOR { bytes memory tmp; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (tmp, byteIdx) = rawResp.readBytes(byteIdx); ret.balance = tmp.deserializeBigInt(); @@ -88,7 +92,10 @@ library MarketCBOR { (len, byteIdx) = rawResp.readFixedArray(byteIdx); - if (len > 0) { + // Ensure the array length is exactly 2 or 0 + require(len == 2 || len == 0, "Invalid array length: must be 0 or 2"); + + if (len == 2) { (ret.data, byteIdx) = rawResp.readBytes(byteIdx); (ret.size, byteIdx) = rawResp.readUInt64(byteIdx); } else { @@ -107,7 +114,9 @@ library MarketCBOR { uint len; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.start, byteIdx) = rawResp.readChainEpoch(byteIdx); (ret.duration, byteIdx) = rawResp.readChainEpoch(byteIdx); @@ -123,7 +132,9 @@ library MarketCBOR { uint len; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.activated, byteIdx) = rawResp.readChainEpoch(byteIdx); (ret.terminated, byteIdx) = rawResp.readChainEpoch(byteIdx); @@ -195,7 +206,9 @@ library MarketCBOR { uint len; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (len, byteIdx) = rawResp.readFixedArray(byteIdx); ret.ids = new uint64[](len); @@ -226,7 +239,9 @@ library MarketCBOR { uint len; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.dealProposal, byteIdx) = rawResp.readBytes(byteIdx); (ret.dealId, byteIdx) = rawResp.readUInt64(byteIdx); @@ -275,7 +290,9 @@ library MarketCBOR { bytes memory tmp; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 11); + if (!(len == 11)) { + revert Errors.InvalidArrayLength(11, len); + } (ret.piece_cid, byteIdx) = rawResp.readCid(byteIdx); (ret.piece_size, byteIdx) = rawResp.readUInt64(byteIdx); diff --git a/contracts/v0.8/cbor/MinerCbor.sol b/contracts/v0.8/cbor/MinerCbor.sol index 2e217d6f..a965dd45 100644 --- a/contracts/v0.8/cbor/MinerCbor.sol +++ b/contracts/v0.8/cbor/MinerCbor.sol @@ -23,18 +23,21 @@ import "solidity-cborutils/contracts/CBOR.sol"; import "./BigIntCbor.sol"; import "./FilecoinCbor.sol"; +import "./BytesCbor.sol"; import "../types/MinerTypes.sol"; import "../types/CommonTypes.sol"; import "../utils/CborDecode.sol"; import "../utils/Misc.sol"; +import "../utils/Errors.sol"; /// @title This library is a set of functions meant to handle CBOR parameters serialization and return values deserialization for Miner actor exported methods. /// @author Zondax AG library MinerCBOR { using CBOR for CBOR.CBORBuffer; using CBORDecoder for bytes; + using BytesCBOR for bytes; using BigIntCBOR for *; using FilecoinCBOR for *; @@ -67,7 +70,9 @@ library MinerCBOR { uint len; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.owner.data, byteIdx) = rawResp.readBytes(byteIdx); @@ -89,26 +94,32 @@ library MinerCBOR { uint len; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.active.beneficiary.data, byteIdx) = rawResp.readBytes(byteIdx); (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 3); + if (!(len == 3)) { + revert Errors.InvalidArrayLength(3, len); + } (tmp, byteIdx) = rawResp.readBytes(byteIdx); if (tmp.length > 0) { - ret.active.term.quota = tmp.deserializeBigInt(); + ret.active.term.quota = tmp.deserializeBytesBigInt(); } else { ret.active.term.quota = CommonTypes.BigInt(new bytes(0), false); } (tmp, byteIdx) = rawResp.readBytes(byteIdx); if (tmp.length > 0) { - ret.active.term.used_quota = tmp.deserializeBigInt(); + ret.active.term.used_quota = tmp.deserializeBytesBigInt(); } else { ret.active.term.used_quota = CommonTypes.BigInt(new bytes(0), false); } @@ -117,13 +128,15 @@ library MinerCBOR { if (!rawResp.isNullNext(byteIdx)) { (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 5); + if (!(len == 5)) { + revert Errors.InvalidArrayLength(5, len); + } (ret.proposed.new_beneficiary.data, byteIdx) = rawResp.readBytes(byteIdx); (tmp, byteIdx) = rawResp.readBytes(byteIdx); if (tmp.length > 0) { - ret.proposed.new_quota = tmp.deserializeBigInt(); + ret.proposed.new_quota = tmp.deserializeBytesBigInt(); } else { ret.proposed.new_quota = CommonTypes.BigInt(new bytes(0), false); } @@ -149,7 +162,9 @@ library MinerCBOR { uint leni; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 1); + if (len != 1) { + revert Errors.InvalidArrayLength(1, len); + } (len, byteIdx) = rawResp.readFixedArray(byteIdx); vesting_funds = new MinerTypes.VestingFunds[](len); @@ -161,7 +176,7 @@ library MinerCBOR { (epoch, byteIdx) = rawResp.readChainEpoch(byteIdx); (tmp, byteIdx) = rawResp.readBytes(byteIdx); - amount = tmp.deserializeBigInt(); + amount = tmp.deserializeBytesBigInt(); vesting_funds[i] = MinerTypes.VestingFunds(epoch, amount); } } @@ -171,20 +186,25 @@ library MinerCBOR { /// @return cbor serialized data as bytes function serializeChangeWorkerAddressParams(MinerTypes.ChangeWorkerAddressParams memory params) internal pure returns (bytes memory) { uint256 capacity = 0; + uint64 addressCount = uint64(params.new_control_addresses.length); + + // Safety check to prevent silent truncation + require(params.new_control_addresses.length == addressCount, "Address count exceeds uint64 limit"); capacity += Misc.getPrefixSize(2); capacity += Misc.getBytesSize(params.new_worker.data); - capacity += Misc.getPrefixSize(uint256(params.new_control_addresses.length)); - for (uint64 i = 0; i < params.new_control_addresses.length; i++) { + capacity += Misc.getPrefixSize(addressCount); + + for (uint64 i = 0; i < addressCount; i++) { capacity += Misc.getBytesSize(params.new_control_addresses[i].data); } CBOR.CBORBuffer memory buf = CBOR.create(capacity); buf.startFixedArray(2); buf.writeBytes(params.new_worker.data); - buf.startFixedArray(uint64(params.new_control_addresses.length)); + buf.startFixedArray(addressCount); - for (uint64 i = 0; i < params.new_control_addresses.length; i++) { + for (uint64 i = 0; i < addressCount; i++) { buf.writeBytes(params.new_control_addresses[i].data); } @@ -222,7 +242,9 @@ library MinerCBOR { uint len; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 1); + if (len != 1) { + revert Errors.InvalidArrayLength(1, len); + } (len, byteIdx) = rawResp.readFixedArray(byteIdx); multi_addrs = new CommonTypes.FilAddress[](len); diff --git a/contracts/v0.8/cbor/PowerCbor.sol b/contracts/v0.8/cbor/PowerCbor.sol index 81abd109..3023405a 100644 --- a/contracts/v0.8/cbor/PowerCbor.sol +++ b/contracts/v0.8/cbor/PowerCbor.sol @@ -25,13 +25,16 @@ import "../types/CommonTypes.sol"; import "../types/PowerTypes.sol"; import "../utils/CborDecode.sol"; import "../utils/Misc.sol"; +import "../utils/Errors.sol"; import "./BigIntCbor.sol"; +import "./BytesCbor.sol"; /// @title This library is a set of functions meant to handle CBOR parameters serialization and return values deserialization for Power actor exported methods. /// @author Zondax AG library PowerCBOR { using CBOR for CBOR.CBORBuffer; using CBORDecoder for bytes; + using BytesCBOR for bytes; using BigIntCBOR for CommonTypes.BigInt; using BigIntCBOR for bytes; @@ -74,7 +77,9 @@ library PowerCBOR { uint len; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.id_address.data, byteIdx) = rawResp.readBytes(byteIdx); (ret.robust_address.data, byteIdx) = rawResp.readBytes(byteIdx); @@ -90,12 +95,14 @@ library PowerCBOR { uint len; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } bytes memory tmp; (tmp, byteIdx) = rawResp.readBytes(byteIdx); if (tmp.length > 0) { - ret.raw_byte_power = tmp.deserializeBigInt(); + ret.raw_byte_power = tmp.deserializeBytesBigInt(); } else { ret.raw_byte_power = CommonTypes.BigInt(new bytes(0), false); } diff --git a/contracts/v0.8/cbor/VerifRegCbor.sol b/contracts/v0.8/cbor/VerifRegCbor.sol index 16df59be..5c506dbf 100644 --- a/contracts/v0.8/cbor/VerifRegCbor.sol +++ b/contracts/v0.8/cbor/VerifRegCbor.sol @@ -26,15 +26,18 @@ import "../types/VerifRegTypes.sol"; import "../utils/CborDecode.sol"; import "../utils/Misc.sol"; +import "../utils/Errors.sol"; import "./BigIntCbor.sol"; import "./FilecoinCbor.sol"; +import "./BytesCbor.sol"; /// @title This library is a set of functions meant to handle CBOR parameters serialization and return values deserialization for VerifReg actor exported methods. /// @author Zondax AG library VerifRegCBOR { using CBOR for CBOR.CBORBuffer; using CBORDecoder for bytes; + using BytesCBOR for bytes; using BigIntCBOR for *; using FilecoinCBOR for *; @@ -72,10 +75,14 @@ library VerifRegCBOR { uint ilen; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.batch_info.success_count, byteIdx) = rawResp.readUInt32(byteIdx); @@ -84,7 +91,9 @@ library VerifRegCBOR { for (uint i = 0; i < len; i++) { (ilen, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(ilen == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.batch_info.fail_codes[i].idx, byteIdx) = rawResp.readUInt32(byteIdx); (ret.batch_info.fail_codes[i].code, byteIdx) = rawResp.readUInt32(byteIdx); @@ -95,7 +104,9 @@ library VerifRegCBOR { for (uint i = 0; i < len; i++) { (ilen, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(ilen == 8); + if (!(len == 8)) { + revert Errors.InvalidArrayLength(8, len); + } (ret.claims[i].provider, byteIdx) = rawResp.readFilActorId(byteIdx); (ret.claims[i].client, byteIdx) = rawResp.readFilActorId(byteIdx); @@ -163,7 +174,9 @@ library VerifRegCBOR { uint ilen; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 3); + if (!(len == 3)) { + revert Errors.InvalidArrayLength(3, len); + } (len, byteIdx) = rawResp.readFixedArray(byteIdx); ret.considered = new CommonTypes.FilActorId[](len); @@ -173,7 +186,9 @@ library VerifRegCBOR { } (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.results.success_count, byteIdx) = rawResp.readUInt32(byteIdx); @@ -182,7 +197,9 @@ library VerifRegCBOR { for (uint i = 0; i < len; i++) { (ilen, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(ilen == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.results.fail_codes[i].idx, byteIdx) = rawResp.readUInt32(byteIdx); (ret.results.fail_codes[i].code, byteIdx) = rawResp.readUInt32(byteIdx); @@ -190,7 +207,7 @@ library VerifRegCBOR { bytes memory tmp; (tmp, byteIdx) = rawResp.readBytes(byteIdx); - ret.datacap_recovered = tmp.deserializeBigInt(); + ret.datacap_recovered = tmp.deserializeBytesBigInt(); return ret; } @@ -205,6 +222,7 @@ library VerifRegCBOR { capacity += Misc.getPrefixSize(1); capacity += Misc.getPrefixSize(termsLen); for (uint i = 0; i < termsLen; i++) { + capacity += Misc.getPrefixSize(3); capacity += Misc.getFilActorIdSize(terms[i].provider); capacity += Misc.getFilActorIdSize(terms[i].claim_id); capacity += Misc.getChainEpochSize(terms[i].term_max); @@ -232,7 +250,9 @@ library VerifRegCBOR { uint ilen; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.success_count, byteIdx) = rawResp.readUInt32(byteIdx); @@ -241,7 +261,9 @@ library VerifRegCBOR { for (uint i = 0; i < len; i++) { (ilen, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(ilen == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.fail_codes[i].idx, byteIdx) = rawResp.readUInt32(byteIdx); (ret.fail_codes[i].code, byteIdx) = rawResp.readUInt32(byteIdx); @@ -284,7 +306,9 @@ library VerifRegCBOR { uint ilen; (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (len, byteIdx) = rawResp.readFixedArray(byteIdx); ret.considered = new CommonTypes.FilActorId[](len); @@ -294,7 +318,9 @@ library VerifRegCBOR { } (len, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(len == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.results.success_count, byteIdx) = rawResp.readUInt32(byteIdx); @@ -303,7 +329,9 @@ library VerifRegCBOR { for (uint i = 0; i < len; i++) { (ilen, byteIdx) = rawResp.readFixedArray(byteIdx); - assert(ilen == 2); + if (!(len == 2)) { + revert Errors.InvalidArrayLength(2, len); + } (ret.results.fail_codes[i].idx, byteIdx) = rawResp.readUInt32(byteIdx); (ret.results.fail_codes[i].code, byteIdx) = rawResp.readUInt32(byteIdx); diff --git a/contracts/v0.8/utils/CborDecode.sol b/contracts/v0.8/utils/CborDecode.sol index 8595989e..f113a97f 100644 --- a/contracts/v0.8/utils/CborDecode.sol +++ b/contracts/v0.8/utils/CborDecode.sol @@ -18,6 +18,8 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.17; +import "./Errors.sol"; + // MajUnsignedInt = 0 // MajSignedInt = 1 // MajByteString = 2 @@ -64,7 +66,9 @@ library CBORDecoder { (maj, value, byteIdx) = parseCborHeader(cborData, byteIdx); require(maj == MajOther, "invalid maj (expected MajOther)"); - assert(value == True_Type || value == False_Type); + if (!(value == True_Type || value == False_Type)) { + revert Errors.InvalidBooleanType(); + } return (value != False_Type, byteIdx); } @@ -118,7 +122,9 @@ library CBORDecoder { if (maj == MajTag) { (maj, len, byteIdx) = parseCborHeader(cborData, byteIdx); - assert(maj == MajByteString); + if (!(maj == MajByteString)) { + revert Errors.ExpectedMajorByteString(); + } } uint max_len = byteIdx + len; @@ -195,7 +201,9 @@ library CBORDecoder { require(maj == MajTag || maj == MajSignedInt, "invalid maj (expected MajTag or MajSignedInt)"); if (maj == MajTag) { - assert(value == TagTypeNegativeBigNum); + if (!(value == TagTypeNegativeBigNum)) { + revert Errors.ExpectedNegativeBigNumTag(); + } uint len; (maj, len, byteIdx) = parseCborHeader(cborData, byteIdx); @@ -237,6 +245,9 @@ library CBORDecoder { (maj, value, byteIdx) = parseCborHeader(cborData, byteIdx); require(maj == MajUnsignedInt, "invalid maj (expected MajUnsignedInt)"); + // Validation to prevent truncation + require(value <= type(uint32).max, "value exceeds uint32 max limit"); + return (uint32(value), byteIdx); } @@ -412,7 +423,9 @@ library CBORDecoder { } // extra in next 8 bytes - assert(low == 27); + if (!(low == 27)) { + revert Errors.ExpectedLowValue27(); + } uint64 extra64 = sliceUInt64(cbor, byteIndex); byteIndex += 8; return (maj, extra64, byteIndex); diff --git a/contracts/v0.8/utils/Errors.sol b/contracts/v0.8/utils/Errors.sol index 5b535e5c..c43d3328 100644 --- a/contracts/v0.8/utils/Errors.sol +++ b/contracts/v0.8/utils/Errors.sol @@ -2,6 +2,13 @@ pragma solidity ^0.8.18; library Errors { + + error InvalidArrayLength(uint256 expected, uint256 actual); + error InvalidBooleanType(); + error ExpectedMajorByteString(); + error ExpectedNegativeBigNumTag(); + error ExpectedLowValue27(); + error NonZeroExitCode(int256 exit_code, string description); // Exit codes