Platform: Code4rena
Start Date: 15/03/2024
Pot Size: $60,500 USDC
Total HM: 16
Participants: 43
Period: 21 days
Judge: hansfriese
Total Solo HM: 5
Id: 348
League: ETH
Rank: 15/43
Findings: 1
Award: $379.61
🌟 Selected for report: 0
🚀 Solo Findings: 0
379.6101 USDC - $379.61
The way addresses and integers are handled within the assembly block may not correctly respect their types, potentially leading to runtime errors or incorrect data interpretation. This will have downstream effects in the claimRedemption, disputeRedemption and claimRemainingCollateral functions
https://github.com/code-423n4/2024-03-dittoeth/blob/91faf46078bb6fe8ce9f55bcb717e5d2d302d22e/contracts/libraries/LibBytes.sol#L11C5-L53C1
colRedeemed := add(0xffffffffffffffffffffff, shr(80, fullWord)) // (256-88-88 = 80), mask of bytes11 = 0xff * 11}
This line is adding a large constant (0xffffffffffffffffffffff) to the shifted value, which is incorrect. It should be using a bitwise AND operation with the appropriate mask to extract the colRedeemed value
fullWord := mload(add(slate, add(offset, 29))) // (29 offset)
The offset of 29 is incorrect. Since the first fullWord reads 32 bytes (20 bytes for shorter, 1 byte for shortId, 8 bytes for CR, and 3 unused bytes), the second fullWord should start at an offset of 32 from the initial offset.
Manual Review
Addressing these issues would require a thorough review of the data layout, ensuring that the size and offset calculations align with the actual structure of ProposalData.
-
for (uint256 i = 0; i < slateLength; i++) {
// 32 offset for array length, mulitply by each ProposalData
uint256 offset = i * 51 + 32;
address shorter; // bytes20 uint8 shortId; // bytes1 uint64 CR; // bytes8 uint88 ercDebtRedeemed; // bytes11 uint88 colRedeemed; // bytes11 assembly { // mload works 32 bytes at a time let fullWord := mload(add(slate, offset)) // read 20 bytes shorter := shr(96, fullWord) // 0x60 = 96 (256-160) // read 8 bytes shortId := and(0xff, shr(88, fullWord)) // 0x58 = 88 (96-8), mask of bytes1 = 0xff * 1 // read 64 bytes CR := and(0xffffffffffffffff, shr(24, fullWord)) // 0x18 = 24 (88-64), mask of bytes8 = 0xff * 8 fullWord := mload(add(slate, add(offset, 29))) // (29 offset) // read 88 bytes ercDebtRedeemed := shr(168, fullWord) // (256-88 = 168) // read 88 bytes colRedeemed := add(0xffffffffffffffffffffff, shr(80, fullWord)) // (256-88-88 = 80), mask of bytes11 = 0xff * 11 }
+
for (uint256 i = 0; i < slateLength; i++) {
uint256 offset = i * 51;
address shorter; // bytes20 uint8 shortId; // bytes1 uint64 CR; // bytes8 uint88 ercDebtRedeemed; // bytes11 uint88 colRedeemed; // bytes11 assembly { // mload works 32 bytes at a time let fullWord := mload(add(slate, add(offset, 32))) // read 20 bytes shorter := shr(96, fullWord) // 0x60 = 96 (256-160) // read 8 bytes shortId := and(0xff, shr(88, fullWord)) // 0x58 = 88 (96-8), mask of bytes1 = 0xff * 1 // read 64 bytes CR := and(0xffffffffffffffff, shr(24, fullWord)) // 0x18 = 24 (88-64), mask of bytes8 = 0xff * 8 fullWord := mload(add(slate, add(offset, 64))) // read 88 bytes ercDebtRedeemed := shr(168, fullWord) // (256-88 = 168) // read 88 bytes colRedeemed := and(0xffffffffffffffffff, shr(80, fullWord)) // (256-88-88 = 80), mask of bytes11 = 0xff * 11 }
Error
#0 - c4-pre-sort
2024-04-07T04:24:37Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2024-04-07T04:24:41Z
raymondfam marked the issue as primary issue
#2 - c4-pre-sort
2024-04-07T04:27:09Z
raymondfam marked the issue as sufficient quality report
#3 - c4-pre-sort
2024-04-07T04:31:00Z
raymondfam marked the issue as duplicate of #221
#4 - raymondfam
2024-04-07T04:31:43Z
See #221.
#5 - c4-judge
2024-04-17T08:12:10Z
hansfriese changed the severity to 2 (Med Risk)
#6 - c4-judge
2024-04-17T08:12:16Z
hansfriese marked the issue as satisfactory