Skip to content

Commit ba0f5b0

Browse files
pallet-revive: Raise contract size limit to one megabyte and raise call depth to 25 (#9267)
This PR changes the contract code limit from roughly 100KiB to exactly 1MiB. It also raises the call stack depth from 5 to 25. Those limits were in place because of memory constraints within the runtime. We work around them in those ways: 1) Removing the 4x safety margin for allocations which is no longer needed due to the new allocator. 2) Limiting the size of the compilation cache to a fixed size. 3) Resetting the compilation cache and flat map every time we call into a new contract. 4) Limiting the size of calldata and return data to 128KiB (only capped by tx size and RAM before). While this is a breaking change nobody will be affected since Geth effectively limits the call data to 128KiB. ## 1MiB contracts This is large enough so that all known contracts won't fail for size issues anymore. The new limit is also much simpler to understand since it does not depend on the number of instructions. Just those two constraints: ``` PVM_BLOB.len() < 1 MiB PVM_BLOB.len() + (rw/ro/stack) < 1MiB + 512KiB ``` This means: 1) A contract is guaranteed to have at least 512KiB of memory available. 2) A contract that is smaller in code can use more memory. 3) Limit is exactly 1MiB unless a user manually increase the memory usage of a contract to be larger than 512KiB. ## Call stack depth `5 -> 25` The limit of `5` was problematic because there are use cases which require deeper stacks. With the raise to `25` there should be no benign use cases anymore that won't work. Please note that even with the low limit of `25` contracts are not vulnerable to stack depth exhaustion attacks: We do trap the caller's context when the depth limit is reached. This is different from Eth where this error can be handled and failure to do so leaves the contract vulnerable. --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 29a0c4a commit ba0f5b0

23 files changed

+1280
-790
lines changed

Cargo.lock

Lines changed: 95 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

prdoc/pr_9267.prdoc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
title: 'pallet-revive: Raise contract size limit to one megabyte and raise call depth
2+
to 25'
3+
doc:
4+
- audience: Runtime Dev
5+
description: |-
6+
This PR changes the contract code limit from roughly 100KiB to exactly 1MiB. It also raises the call stack depth from 5 to 25.
7+
8+
Those limits were in place because of memory constraints within the runtime. We work around them in those ways:
9+
1) Removing the 4x safety margin for allocations which is no longer needed due to the new allocator.
10+
2) Limiting the size of the compilation cache to a fixed size.
11+
3) Resetting the compilation cache and flat map every time we call into a new contract.
12+
4) Limiting the size of calldata and return data to 128KiB (only capped by tx size and RAM before). While this is a breaking change nobody will be affected since Geth effectively limits the call data to 128KiB.
13+
14+
## 1MiB contracts
15+
16+
This is large enough so that all known contracts won't fail for size issues anymore.
17+
18+
The new limit is also much simpler to understand since it does not depend on the number of instructions. Just those two constraints:
19+
```
20+
PVM_BLOB.len() < 1 MiB
21+
PVM_BLOB.len() + (rw/ro/stack) < 1MiB + 512KiB
22+
```
23+
24+
This means:
25+
1) A contract is guaranteed to have at least 512KiB of memory available.
26+
2) A contract that is smaller in code can use more memory.
27+
3) Limit is exactly 1MiB unless a user manually increase the memory usage of a contract to be larger than 512KiB.
28+
29+
## Call stack depth `5 -> 25`
30+
31+
The limit of `5` was problematic because there are use cases which require deeper stacks. With the raise to `25` there should be no benign use cases anymore that won't work.
32+
33+
Please note that even with the low limit of `25` contracts are not vulnerable to stack depth exhaustion attacks: We do trap the caller's context when the depth limit is reached. This is different from Eth where this error can be handled and failure to do so leaves the contract vulnerable.
34+
crates:
35+
- name: pallet-revive-fixtures
36+
bump: patch
37+
- name: pallet-revive
38+
bump: patch

substrate/frame/revive/Cargo.toml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ num-bigint = { workspace = true }
3131
num-integer = { workspace = true }
3232
num-traits = { workspace = true }
3333
paste = { workspace = true }
34-
polkavm = { version = "0.26.0", default-features = false }
35-
polkavm-common = { version = "0.26.0", default-features = false, optional = true }
34+
polkavm = { version = "0.27.0", default-features = false }
35+
polkavm-common = { version = "0.27.0", default-features = false, features = ["alloc"] }
3636
rand = { workspace = true, optional = true }
3737
rand_pcg = { workspace = true, optional = true }
3838
rlp = { workspace = true }
@@ -62,7 +62,6 @@ subxt-signer = { workspace = true, optional = true, features = ["unstable-eth"]
6262
[dev-dependencies]
6363
array-bytes = { workspace = true, default-features = true }
6464
assert_matches = { workspace = true }
65-
polkavm-common = { version = "0.26.0" }
6665
pretty_assertions = { workspace = true }
6766
secp256k1 = { workspace = true, features = ["recovery"] }
6867
serde_json = { workspace = true }
@@ -96,7 +95,7 @@ std = [
9695
"pallet-timestamp/std",
9796
"pallet-transaction-payment/std",
9897
"pallet-utility/std",
99-
"polkavm-common?/std",
98+
"polkavm-common/std",
10099
"polkavm/std",
101100
"rand?/std",
102101
"ripemd/std",
@@ -126,7 +125,6 @@ runtime-benchmarks = [
126125
"pallet-timestamp/runtime-benchmarks",
127126
"pallet-transaction-payment/runtime-benchmarks",
128127
"pallet-utility/runtime-benchmarks",
129-
"polkavm-common/alloc",
130128
"rand",
131129
"rand_pcg",
132130
"sp-consensus-aura",

substrate/frame/revive/fixtures/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ sp-io = { workspace = true, default-features = true, optional = true }
2424
anyhow = { workspace = true, default-features = true }
2525
cargo_metadata = { workspace = true }
2626
pallet-revive-uapi = { workspace = true }
27-
polkavm-linker = { version = "0.26.0" }
27+
polkavm-linker = { version = "0.27.0" }
2828
toml = { workspace = true }
2929

3030
[features]

substrate/frame/revive/fixtures/build/_Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ edition = "2021"
1414
[dependencies]
1515
uapi = { package = 'pallet-revive-uapi', features = ["unstable-hostfn"], default-features = false }
1616
hex-literal = { version = "0.4.1", default-features = false }
17-
polkavm-derive = { version = "0.25.0" }
17+
polkavm-derive = { version = "0.27.0" }
1818

1919
[profile.release]
2020
opt-level = 3

substrate/frame/revive/fixtures/contracts/call_and_returncode.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#![no_main]
2222
include!("../panic_handler.rs");
2323

24+
polkavm_derive::min_stack_size!(256 * 1024);
25+
2426
use uapi::{input, u256_bytes, HostFn, HostFnImpl as api};
2527

2628
#[no_mangle]
@@ -38,7 +40,7 @@ pub extern "C" fn call() {
3840
);
3941

4042
// the first 4 bytes are reserved for the return code
41-
let mut output = [0u8; 512];
43+
let mut output = [0u8; 128 * 1024];
4244
let output_ptr = &mut &mut output[4..];
4345

4446
let code = match api::call(
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// This file is part of Substrate.
2+
3+
// Copyright (C) Parity Technologies (UK) Ltd.
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
#![no_std]
19+
#![no_main]
20+
include!("../panic_handler.rs");
21+
22+
polkavm_derive::min_stack_size!(512 * 1024);
23+
24+
use uapi::{input, HostFn, HostFnImpl as api};
25+
26+
#[no_mangle]
27+
#[polkavm_derive::polkavm_export]
28+
pub extern "C" fn deploy() {}
29+
30+
#[no_mangle]
31+
#[polkavm_derive::polkavm_export]
32+
pub extern "C" fn call() {
33+
input!(
34+
input_size: u32,
35+
);
36+
37+
let input_buf = [0u8; 256 * 1024];
38+
let address = [1u8; 20];
39+
40+
// Call the callee
41+
api::call(
42+
uapi::CallFlags::empty(),
43+
&address,
44+
u64::MAX, // How much ref_time to devote for the execution. u64::MAX = use all.
45+
u64::MAX, // How much proof_size to devote for the execution. u64::MAX = use all.
46+
&[u8::MAX; 32], // No deposit limit.
47+
&[0; 32], // Value transferred to the contract.
48+
&input_buf[..input_size as usize],
49+
None,
50+
).unwrap();
51+
}

substrate/frame/revive/fixtures/contracts/oom_ro.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ include!("../panic_handler.rs");
2525

2626
use uapi::{HostFn, HostFnImpl as api, ReturnFlags};
2727

28-
static BUFFER: [u8; 1025 * 1024] = [0; 1025 * 1024];
28+
static BUFFER: [u8; 1024 * 1024] = [0; 1024 * 1024];
2929

3030
#[no_mangle]
3131
#[polkavm_derive::polkavm_export]

substrate/frame/revive/fixtures/contracts/oom_rw_included.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ include!("../panic_handler.rs");
2525

2626
use uapi::{HostFn, HostFnImpl as api, ReturnFlags};
2727

28-
static mut BUFFER: [u8; 513 * 1024] = [42; 513 * 1024];
28+
static mut BUFFER: [u8; 1024 * 1024] = [42; 1024 * 1024];
2929

30-
unsafe fn buffer() -> &'static [u8; 513 * 1024] {
30+
unsafe fn buffer() -> &'static [u8; 1024 * 1024] {
3131
let ptr = core::ptr::addr_of!(BUFFER);
3232
&*ptr
3333
}

0 commit comments

Comments
 (0)