From f46bea6a962e0dd8865b70fe7d303ac43150b5fb Mon Sep 17 00:00:00 2001 From: xermicus Date: Wed, 15 Oct 2025 22:39:42 +0200 Subject: [PATCH] llvm-context: do not trap zero length OOB heap access (#389) Fixes https://github.com/paritytech/contract-issues/issues/120 --------- Signed-off-by: xermicus --- CHANGELOG.md | 3 ++ Cargo.lock | 2 +- Cargo.toml | 2 +- crates/integration/codesize.json | 16 ++++----- crates/integration/contracts/MemoryBounds.sol | 36 +++++++++++++++++++ crates/integration/src/tests.rs | 1 + crates/llvm-context/Cargo.toml | 2 +- .../polkavm/context/function/runtime/sbrk.rs | 12 ++++++- 8 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 crates/integration/contracts/MemoryBounds.sol diff --git a/CHANGELOG.md b/CHANGELOG.md index 23cb6f1..5067089 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ This is a development pre-release. Supported `polkadot-sdk` rev: `2503.0.1` +### Chnaged +- Emulated EVM heap memory accesses of zero length are never out of bounds. + ## v0.4.1 This is a development pre-release. diff --git a/Cargo.lock b/Cargo.lock index 0c688c3..93a10f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8819,7 +8819,7 @@ dependencies = [ [[package]] name = "revive-llvm-context" -version = "0.4.1" +version = "0.5.0" dependencies = [ "anyhow", "hex", diff --git a/Cargo.toml b/Cargo.toml index 8993604..eae3b5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ revive-differential = { version = "0.2.0", path = "crates/differential" } revive-explorer = { version = "0.1.0", path = "crates/explore" } revive-integration = { version = "0.2.0", path = "crates/integration" } revive-linker = { version = "0.2.0", path = "crates/linker" } -revive-llvm-context = { version = "0.4.1", path = "crates/llvm-context" } +revive-llvm-context = { version = "0.5.0", path = "crates/llvm-context" } revive-runner = { version = "0.2.0", path = "crates/runner" } revive-runtime-api = { version = "0.3.0", path = "crates/runtime-api" } revive-solc-json-interface = { version = "0.4.0", path = "crates/solc-json-interface", default-features = false } diff --git a/crates/integration/codesize.json b/crates/integration/codesize.json index 67d85ad..c473b66 100644 --- a/crates/integration/codesize.json +++ b/crates/integration/codesize.json @@ -1,10 +1,10 @@ { - "Baseline": 932, - "Computation": 2313, - "DivisionArithmetics": 9103, - "ERC20": 17479, - "Events": 1692, - "FibonacciIterative": 1508, - "Flipper": 2098, - "SHA1": 8176 + "Baseline": 914, + "Computation": 2295, + "DivisionArithmetics": 9085, + "ERC20": 17499, + "Events": 1674, + "FibonacciIterative": 1490, + "Flipper": 2102, + "SHA1": 8158 } \ No newline at end of file diff --git a/crates/integration/contracts/MemoryBounds.sol b/crates/integration/contracts/MemoryBounds.sol new file mode 100644 index 0000000..3b094bc --- /dev/null +++ b/crates/integration/contracts/MemoryBounds.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.24; + +/* runner.json +{ + "differential": true, + "actions": [ + { + "Instantiate": { + "code": { + "Solidity": { + "contract": "MemoryBounds" + } + } + } + }, + { + "Call": { + "dest": { + "Instantiated": 0 + } + } + } + ] +} +*/ + +contract MemoryBounds { + fallback() external { + assembly { + // Accessing OOB offsets should always work when the length is 0. + return(100000, 0) + } + } +} diff --git a/crates/integration/src/tests.rs b/crates/integration/src/tests.rs index fed7209..4dbb5dc 100644 --- a/crates/integration/src/tests.rs +++ b/crates/integration/src/tests.rs @@ -62,6 +62,7 @@ test_spec!(function_type, "FunctionType", "FunctionType.sol"); test_spec!(layout_at, "LayoutAt", "LayoutAt.sol"); test_spec!(shift_arithmetic_right, "SAR", "SAR.sol"); test_spec!(add_mod_mul_mod, "AddModMulModTester", "AddModMulMod.sol"); +test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol"); fn instantiate(path: &str, contract: &str) -> Vec { vec![Instantiate { diff --git a/crates/llvm-context/Cargo.toml b/crates/llvm-context/Cargo.toml index 431455b..cda4e9e 100644 --- a/crates/llvm-context/Cargo.toml +++ b/crates/llvm-context/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revive-llvm-context" -version = "0.4.1" +version = "0.5.0" license.workspace = true edition.workspace = true repository.workspace = true diff --git a/crates/llvm-context/src/polkavm/context/function/runtime/sbrk.rs b/crates/llvm-context/src/polkavm/context/function/runtime/sbrk.rs index da8a8d6..3460088 100644 --- a/crates/llvm-context/src/polkavm/context/function/runtime/sbrk.rs +++ b/crates/llvm-context/src/polkavm/context/function/runtime/sbrk.rs @@ -47,6 +47,17 @@ impl RuntimeFunction for Sbrk { let offset = Self::paramater(context, 0).into_int_value(); let size = Self::paramater(context, 1).into_int_value(); + let return_block = context.append_basic_block("return_pointer"); + let body_block = context.append_basic_block("body"); + let is_size_zero = context.builder().build_int_compare( + inkwell::IntPredicate::EQ, + size, + context.xlen_type().const_zero(), + "is_size_zero", + )?; + context.build_conditional_branch(is_size_zero, return_block, body_block)?; + + context.set_basic_block(body_block); let trap_block = context.append_basic_block("trap"); let offset_in_bounds_block = context.append_basic_block("offset_in_bounds"); let is_offset_out_of_bounds = context.builder().build_int_compare( @@ -91,7 +102,6 @@ impl RuntimeFunction for Sbrk { )?; context.set_basic_block(size_in_bounds_block); - let return_block = context.append_basic_block("return_pointer"); let new_size_block = context.append_basic_block("new_size"); let is_new_size = context.builder().build_int_compare( inkwell::IntPredicate::UGT,