Skip to content

Commit 83e6079

Browse files
Merge pull request #12 from rainlanguage/2026-02-11-audit
prevent set overflow
2 parents 7dc72b7 + a76d321 commit 83e6079

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

src/lib/LibMemoryKV.sol

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ type MemoryKVVal is bytes32;
1616

1717
/// @title LibMemoryKV
1818
library LibMemoryKV {
19+
/// Thrown when the memory allocation for a new key/value pair would exceed
20+
/// the maximum pointer value of `0xFFFF` which would cause corruption of
21+
/// the linked list and potentially overwriting of unrelated memory.
22+
error MemoryKVOverflow(uint256 pointer);
23+
1924
/// Gets the value associated with a given key.
2025
/// The value returned will be `0` if the key exists and was set to zero OR
2126
/// the key DOES NOT exist, i.e. was never set.
@@ -59,6 +64,7 @@ library LibMemoryKV {
5964
/// @return The final value of `kv` as it MAY be modified if the upsert
6065
/// resulted in an insert operation.
6166
function set(MemoryKV kv, MemoryKVKey key, MemoryKVVal value) internal pure returns (MemoryKV) {
67+
uint256 pointer;
6268
assembly ("memory-safe") {
6369
// Hash to spread inserts across internal lists.
6470
// This MUST remain in sync with `get` logic.
@@ -70,7 +76,7 @@ library LibMemoryKV {
7076
let startPointer := and(shr(bitOffset, kv), 0xFFFF)
7177

7278
// Find a key match then break so that we populate a nonzero pointer.
73-
let pointer := startPointer
79+
pointer := startPointer
7480
for {} iszero(iszero(pointer)) { pointer := mload(add(pointer, 0x40)) } {
7581
if eq(key, mload(pointer)) { break }
7682
}
@@ -106,6 +112,9 @@ library LibMemoryKV {
106112
)
107113
}
108114
}
115+
if (pointer > 0xFFFF) {
116+
revert MemoryKVOverflow(pointer);
117+
}
109118
return kv;
110119
}
111120

test/src/lib/LibMemoryKV.getset.t.sol

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@ import {LibPointer, Pointer} from "rain.solmem/lib/LibPointer.sol";
88
import {LibMemoryKV, MemoryKVKey, MemoryKVVal, MemoryKV} from "src/lib/LibMemoryKV.sol";
99

1010
contract LibMemoryKVGetSetTest is Test {
11+
function setOverflowExternal(MemoryKV kv, MemoryKVKey key, MemoryKVVal value) external pure returns (MemoryKV) {
12+
assembly ("memory-safe") {
13+
// Set the pointer past 0xFFFF to cause an overflow on the next
14+
// insert.
15+
mstore(0x40, 0x10000)
16+
}
17+
18+
return LibMemoryKV.set(kv, key, value);
19+
}
20+
21+
function testSetOverflow(MemoryKVKey key, MemoryKVVal value) external {
22+
MemoryKV kv = MemoryKV.wrap(0);
23+
// The next set should revert with a MemoryKVOverflow error.
24+
vm.expectRevert(abi.encodeWithSelector(LibMemoryKV.MemoryKVOverflow.selector, 0x10000));
25+
this.setOverflowExternal(kv, key, value);
26+
}
27+
1128
function testSetGet0(MemoryKVKey key, MemoryKVVal value) public pure {
1229
MemoryKV kv = MemoryKV.wrap(0);
1330

0 commit comments

Comments
 (0)