Skip to content

Commit e5e9ff7

Browse files
Amxx0xsambugsernestognwarr00famouswizard
authored
Release v5.2 audit fixes (#5330)
Signed-off-by: Hadrien Croubois <[email protected]> Co-authored-by: Sam Bugs <[email protected]> Co-authored-by: Ernesto García <[email protected]> Co-authored-by: Arr00 <[email protected]> Co-authored-by: wizard <[email protected]> Co-authored-by: leopardracer <[email protected]> Co-authored-by: cairo <[email protected]>
1 parent 98d28f9 commit e5e9ff7

26 files changed

+490
-152
lines changed

.changeset/proud-planes-arrive.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'openzeppelin-solidity': minor
33
---
44

5-
`Bytes`: Add a library of common operation that operate on `bytes` objects.
5+
`Bytes`: Add a library of common operations that operate on `bytes` objects.

GUIDELINES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ External contributions must be reviewed separately by multiple maintainers.
5555

5656
Automation should be used as much as possible to reduce the possibility of human error and forgetfulness.
5757

58-
Automations that make use of sensitive credentials must use secure secret management, and must be strengthened against attacks such as [those on GitHub Actions worklows](https://github.com/nikitastupin/pwnhub).
58+
Automations that make use of sensitive credentials must use secure secret management, and must be strengthened against attacks such as [those on GitHub Actions workflows](https://github.com/nikitastupin/pwnhub).
5959

6060
Some other examples of automation are:
6161

contracts/account/utils/draft-ERC4337Utils.sol

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
pragma solidity ^0.8.20;
44

5-
import {IEntryPoint, PackedUserOperation} from "../../interfaces/draft-IERC4337.sol";
5+
import {PackedUserOperation} from "../../interfaces/draft-IERC4337.sol";
66
import {Math} from "../../utils/math/Math.sol";
77
import {Packing} from "../../utils/Packing.sol";
88

@@ -24,9 +24,9 @@ library ERC4337Utils {
2424
function parseValidationData(
2525
uint256 validationData
2626
) internal pure returns (address aggregator, uint48 validAfter, uint48 validUntil) {
27-
validAfter = uint48(bytes32(validationData).extract_32_6(0x00));
28-
validUntil = uint48(bytes32(validationData).extract_32_6(0x06));
29-
aggregator = address(bytes32(validationData).extract_32_20(0x0c));
27+
validAfter = uint48(bytes32(validationData).extract_32_6(0));
28+
validUntil = uint48(bytes32(validationData).extract_32_6(6));
29+
aggregator = address(bytes32(validationData).extract_32_20(12));
3030
if (validUntil == 0) validUntil = type(uint48).max;
3131
}
3232

@@ -59,7 +59,8 @@ library ERC4337Utils {
5959
(address aggregator1, uint48 validAfter1, uint48 validUntil1) = parseValidationData(validationData1);
6060
(address aggregator2, uint48 validAfter2, uint48 validUntil2) = parseValidationData(validationData2);
6161

62-
bool success = aggregator1 == address(0) && aggregator2 == address(0);
62+
bool success = aggregator1 == address(uint160(SIG_VALIDATION_SUCCESS)) &&
63+
aggregator2 == address(uint160(SIG_VALIDATION_SUCCESS));
6364
uint48 validAfter = uint48(Math.max(validAfter1, validAfter2));
6465
uint48 validUntil = uint48(Math.min(validUntil1, validUntil2));
6566
return packValidationData(success, validAfter, validUntil);
@@ -71,12 +72,7 @@ library ERC4337Utils {
7172
return (aggregator_, block.timestamp < validAfter || validUntil < block.timestamp);
7273
}
7374

74-
/// @dev Computes the hash of a user operation with the current entrypoint and chainid.
75-
function hash(PackedUserOperation calldata self) internal view returns (bytes32) {
76-
return hash(self, address(this), block.chainid);
77-
}
78-
79-
/// @dev Sames as {hash}, but with a custom entrypoint and chainid.
75+
/// @dev Computes the hash of a user operation for a given entrypoint and chainid.
8076
function hash(
8177
PackedUserOperation calldata self,
8278
address entrypoint,
@@ -103,24 +99,34 @@ library ERC4337Utils {
10399
return result;
104100
}
105101

102+
/// @dev Returns `factory` from the {PackedUserOperation}, or address(0) if the initCode is empty or not properly formatted.
103+
function factory(PackedUserOperation calldata self) internal pure returns (address) {
104+
return self.initCode.length < 20 ? address(0) : address(bytes20(self.initCode[0:20]));
105+
}
106+
107+
/// @dev Returns `factoryData` from the {PackedUserOperation}, or empty bytes if the initCode is empty or not properly formatted.
108+
function factoryData(PackedUserOperation calldata self) internal pure returns (bytes calldata) {
109+
return self.initCode.length < 20 ? _emptyCalldataBytes() : self.initCode[20:];
110+
}
111+
106112
/// @dev Returns `verificationGasLimit` from the {PackedUserOperation}.
107113
function verificationGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
108-
return uint128(self.accountGasLimits.extract_32_16(0x00));
114+
return uint128(self.accountGasLimits.extract_32_16(0));
109115
}
110116

111117
/// @dev Returns `accountGasLimits` from the {PackedUserOperation}.
112118
function callGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
113-
return uint128(self.accountGasLimits.extract_32_16(0x10));
119+
return uint128(self.accountGasLimits.extract_32_16(16));
114120
}
115121

116122
/// @dev Returns the first section of `gasFees` from the {PackedUserOperation}.
117123
function maxPriorityFeePerGas(PackedUserOperation calldata self) internal pure returns (uint256) {
118-
return uint128(self.gasFees.extract_32_16(0x00));
124+
return uint128(self.gasFees.extract_32_16(0));
119125
}
120126

121127
/// @dev Returns the second section of `gasFees` from the {PackedUserOperation}.
122128
function maxFeePerGas(PackedUserOperation calldata self) internal pure returns (uint256) {
123-
return uint128(self.gasFees.extract_32_16(0x10));
129+
return uint128(self.gasFees.extract_32_16(16));
124130
}
125131

126132
/// @dev Returns the total gas price for the {PackedUserOperation} (ie. `maxFeePerGas` or `maxPriorityFeePerGas + basefee`).
@@ -129,22 +135,35 @@ library ERC4337Utils {
129135
// Following values are "per gas"
130136
uint256 maxPriorityFee = maxPriorityFeePerGas(self);
131137
uint256 maxFee = maxFeePerGas(self);
132-
return Math.ternary(maxFee == maxPriorityFee, maxFee, Math.min(maxFee, maxPriorityFee + block.basefee));
138+
return Math.min(maxFee, maxPriorityFee + block.basefee);
133139
}
134140
}
135141

136142
/// @dev Returns the first section of `paymasterAndData` from the {PackedUserOperation}.
137143
function paymaster(PackedUserOperation calldata self) internal pure returns (address) {
138-
return address(bytes20(self.paymasterAndData[0:20]));
144+
return self.paymasterAndData.length < 52 ? address(0) : address(bytes20(self.paymasterAndData[0:20]));
139145
}
140146

141147
/// @dev Returns the second section of `paymasterAndData` from the {PackedUserOperation}.
142148
function paymasterVerificationGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
143-
return uint128(bytes16(self.paymasterAndData[20:36]));
149+
return self.paymasterAndData.length < 52 ? 0 : uint128(bytes16(self.paymasterAndData[20:36]));
144150
}
145151

146152
/// @dev Returns the third section of `paymasterAndData` from the {PackedUserOperation}.
147153
function paymasterPostOpGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
148-
return uint128(bytes16(self.paymasterAndData[36:52]));
154+
return self.paymasterAndData.length < 52 ? 0 : uint128(bytes16(self.paymasterAndData[36:52]));
155+
}
156+
157+
/// @dev Returns the forth section of `paymasterAndData` from the {PackedUserOperation}.
158+
function paymasterData(PackedUserOperation calldata self) internal pure returns (bytes calldata) {
159+
return self.paymasterAndData.length < 52 ? _emptyCalldataBytes() : self.paymasterAndData[52:];
160+
}
161+
162+
// slither-disable-next-line write-after-write
163+
function _emptyCalldataBytes() private pure returns (bytes calldata result) {
164+
assembly ("memory-safe") {
165+
result.offset := 0
166+
result.length := 0
167+
}
149168
}
150169
}

contracts/account/utils/draft-ERC7579Utils.sol

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,26 @@ library ERC7579Utils {
2222
using Packing for *;
2323

2424
/// @dev A single `call` execution.
25-
CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
25+
CallType internal constant CALLTYPE_SINGLE = CallType.wrap(0x00);
2626

2727
/// @dev A batch of `call` executions.
28-
CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
28+
CallType internal constant CALLTYPE_BATCH = CallType.wrap(0x01);
2929

3030
/// @dev A `delegatecall` execution.
31-
CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
31+
CallType internal constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
3232

3333
/// @dev Default execution type that reverts on failure.
34-
ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
34+
ExecType internal constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
3535

3636
/// @dev Execution type that does not revert on failure.
37-
ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);
38-
39-
/// @dev Emits when an {EXECTYPE_TRY} execution fails.
40-
event ERC7579TryExecuteFail(uint256 batchExecutionIndex, bytes result);
37+
ExecType internal constant EXECTYPE_TRY = ExecType.wrap(0x01);
38+
39+
/**
40+
* @dev Emits when an {EXECTYPE_TRY} execution fails.
41+
* @param batchExecutionIndex The index of the failed call in the execution batch.
42+
* @param returndata The returned data from the failed call.
43+
*/
44+
event ERC7579TryExecuteFail(uint256 batchExecutionIndex, bytes returndata);
4145

4246
/// @dev The provided {CallType} is not supported.
4347
error ERC7579UnsupportedCallType(CallType callType);

contracts/finance/VestingWallet.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ import {Ownable} from "../access/Ownable.sol";
2626
*
2727
* NOTE: When using this contract with any token whose balance is adjusted automatically (i.e. a rebase token), make
2828
* sure to account the supply/balance adjustment in the vesting schedule to ensure the vested amount is as intended.
29+
*
30+
* NOTE: Chains with support for native ERC20s may allow the vesting wallet to withdraw the underlying asset as both an
31+
* ERC20 and as native currency. For example, if chain C supports token A and the wallet gets deposited 100 A, then
32+
* at 50% of the vesting period, the beneficiary can withdraw 50 A as ERC20 and 25 A as native currency (totaling 75 A).
33+
* Consider disabling one of the withdrawal methods.
2934
*/
3035
contract VestingWallet is Context, Ownable {
3136
event EtherReleased(uint256 amount);

contracts/governance/extensions/GovernorCountingOverridable.sol

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import {VotesExtended} from "../utils/VotesExtended.sol";
88
import {GovernorVotes} from "./GovernorVotes.sol";
99

1010
/**
11-
* @dev Extension of {Governor} which enables delegatees to override the vote of their delegates. This module requires a
12-
* token token that inherits `VotesExtended`.
11+
* @dev Extension of {Governor} which enables delegators to override the vote of their delegates. This module requires a
12+
* token that inherits {VotesExtended}.
1313
*/
1414
abstract contract GovernorCountingOverridable is GovernorVotes {
1515
bytes32 public constant OVERRIDE_BALLOT_TYPEHASH =
@@ -35,7 +35,10 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
3535
mapping(address voter => VoteReceipt) voteReceipt;
3636
}
3737

38-
event VoteReduced(address indexed voter, uint256 proposalId, uint8 support, uint256 weight);
38+
/// @dev The votes casted by `delegate` were reduced by `weight` after an override vote was casted by the original token holder
39+
event VoteReduced(address indexed delegate, uint256 proposalId, uint8 support, uint256 weight);
40+
41+
/// @dev A delegated vote on `proposalId` was overridden by `weight`
3942
event OverrideVoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
4043

4144
error GovernorAlreadyOverridenVote(address account);
@@ -52,6 +55,11 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
5255

5356
/**
5457
* @dev See {IGovernor-hasVoted}.
58+
*
59+
* NOTE: Calling {castVote} (or similar) casts a vote using the voting power that is delegated to the voter.
60+
* Conversely, calling {castOverrideVote} (or similar) uses the voting power of the account itself, from its asset
61+
* balances. Casting an "override vote" does not count as voting and won't be reflected by this getter. Consider
62+
* using {hasVotedOverride} to check if an account has casted an "override vote" for a given proposal id.
5563
*/
5664
function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) {
5765
return _proposalVotes[proposalId].voteReceipt[account].casted != 0;
@@ -120,7 +128,11 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
120128
return totalWeight;
121129
}
122130

123-
/// @dev Variant of {Governor-_countVote} that deals with vote overrides.
131+
/**
132+
* @dev Variant of {Governor-_countVote} that deals with vote overrides.
133+
*
134+
* NOTE: See {hasVoted} for more details about the difference between {castVote} and {castOverrideVote}.
135+
*/
124136
function _countOverride(uint256 proposalId, address account, uint8 support) internal virtual returns (uint256) {
125137
ProposalVote storage proposalVote = _proposalVotes[proposalId];
126138

@@ -132,9 +144,9 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
132144
revert GovernorAlreadyOverridenVote(account);
133145
}
134146

135-
uint256 proposalSnapshot = proposalSnapshot(proposalId);
136-
uint256 overridenWeight = VotesExtended(address(token())).getPastBalanceOf(account, proposalSnapshot);
137-
address delegate = VotesExtended(address(token())).getPastDelegate(account, proposalSnapshot);
147+
uint256 snapshot = proposalSnapshot(proposalId);
148+
uint256 overridenWeight = VotesExtended(address(token())).getPastBalanceOf(account, snapshot);
149+
address delegate = VotesExtended(address(token())).getPastDelegate(account, snapshot);
138150
uint8 delegateCasted = proposalVote.voteReceipt[delegate].casted;
139151

140152
proposalVote.voteReceipt[account].hasOverriden = true;
@@ -150,7 +162,7 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
150162
return overridenWeight;
151163
}
152164

153-
/// @dev variant of {Governor-_castVote} that deals with vote overrides.
165+
/// @dev Variant of {Governor-_castVote} that deals with vote overrides. Returns the overridden weight.
154166
function _castOverride(
155167
uint256 proposalId,
156168
address account,
@@ -168,7 +180,7 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
168180
return overridenWeight;
169181
}
170182

171-
/// @dev Public function for casting an override vote
183+
/// @dev Public function for casting an override vote. Returns the overridden weight.
172184
function castOverrideVote(
173185
uint256 proposalId,
174186
uint8 support,
@@ -178,7 +190,7 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
178190
return _castOverride(proposalId, voter, support, reason);
179191
}
180192

181-
/// @dev Public function for casting an override vote using a voter's signature
193+
/// @dev Public function for casting an override vote using a voter's signature. Returns the overridden weight.
182194
function castOverrideVoteBySig(
183195
uint256 proposalId,
184196
uint8 support,

contracts/governance/utils/Votes.sol

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ abstract contract Votes is Context, EIP712, Nonces, IERC5805 {
7171
return "mode=blocknumber&from=default";
7272
}
7373

74+
/**
75+
* @dev Validate that a timepoint is in the past, and return it as a uint48.
76+
*/
77+
function _validateTimepoint(uint256 timepoint) internal view returns (uint48) {
78+
uint48 currentTimepoint = clock();
79+
if (timepoint >= currentTimepoint) revert ERC5805FutureLookup(timepoint, currentTimepoint);
80+
return SafeCast.toUint48(timepoint);
81+
}
82+
7483
/**
7584
* @dev Returns the current amount of votes that `account` has.
7685
*/
@@ -87,11 +96,7 @@ abstract contract Votes is Context, EIP712, Nonces, IERC5805 {
8796
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
8897
*/
8998
function getPastVotes(address account, uint256 timepoint) public view virtual returns (uint256) {
90-
uint48 currentTimepoint = clock();
91-
if (timepoint >= currentTimepoint) {
92-
revert ERC5805FutureLookup(timepoint, currentTimepoint);
93-
}
94-
return _delegateCheckpoints[account].upperLookupRecent(SafeCast.toUint48(timepoint));
99+
return _delegateCheckpoints[account].upperLookupRecent(_validateTimepoint(timepoint));
95100
}
96101

97102
/**
@@ -107,11 +112,7 @@ abstract contract Votes is Context, EIP712, Nonces, IERC5805 {
107112
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
108113
*/
109114
function getPastTotalSupply(uint256 timepoint) public view virtual returns (uint256) {
110-
uint48 currentTimepoint = clock();
111-
if (timepoint >= currentTimepoint) {
112-
revert ERC5805FutureLookup(timepoint, currentTimepoint);
113-
}
114-
return _totalCheckpoints.upperLookupRecent(SafeCast.toUint48(timepoint));
115+
return _totalCheckpoints.upperLookupRecent(_validateTimepoint(timepoint));
115116
}
116117

117118
/**

0 commit comments

Comments
 (0)