referrerpolicy=no-referrer-when-downgrade

pallet_revive/
limits.rs

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//! Limits that are observeable by contract code.
19//!
20//! It is important to never change this limits without supporting the old limits
21//! for already deployed contracts. This is what the behaviour version stored with a contract's
22//! code (`CodeInfo::behaviour_version`) is meant for. This is true for either increasing or
23//! decreasing the limit.
24//!
25//! Limits in this file are different from the limits configured on the [`Config`] trait which are
26//! generally only affect actions that cannot be performed by a contract: For example things related
27//! to deposits and weights are allowed to be changed as they are paid by root callers which
28//! are not contracts.
29//!
30//! Exceptions to this rule apply: Limits in the [`code`] module can be increased
31//! without emulating the old values for existing contracts. Reason is that those limits are only
32//! applied **once** at code upload time. Since this action cannot be performed by contracts we
33//! can change those limits without breaking existing contracts. Please keep in mind that we should
34//! only ever **increase** those values but never decrease.
35//!
36//! Exception: [`MAX_RECEIPT_LOGS_BYTES`], see its documentation.
37
38/// The amount of total memory we require to safely operate.
39///
40/// This is not a config knob but derived from the limits in this file.
41pub const MEMORY_REQUIRED: u32 = memory_required();
42
43/// The maximum depth of the call stack.
44///
45/// A 0 means that no callings of other contracts are possible. In other words only the origin
46/// called "root contract" is allowed to execute then.
47pub const CALL_STACK_DEPTH: u32 = 25;
48
49/// The maximum number of topics a call to [`crate::SyscallDoc::deposit_event`] can emit.
50///
51/// We set it to the same limit that ethereum has. It is unlikely to change.
52pub const NUM_EVENT_TOPICS: u32 = 4;
53
54/// Maximum size of an ethereum transaction's RLP encoded payload.
55///
56/// Room for the largest code blob and call data the pallet accepts, plus 64 KiB for the rest of
57/// the transaction: its other fields, signature, access list and authorization list. An
58/// `eth_transact` with a larger payload is rejected as an invalid transaction.
59pub const MAX_TRANSACTION_PAYLOAD_SIZE: u32 = code::BLOB_BYTES + CALLDATA_BYTES + 64 * 1024;
60
61/// Maximum size of storage items.
62pub const STORAGE_BYTES: u32 = 416;
63
64/// Maximum payload size of events.
65pub const EVENT_BYTES: u32 = 64 * 1024;
66
67/// Maximum number of bytes the logs of one ethereum transaction's receipt may take up.
68///
69/// Counted over the RLP encoding of every log the transaction keeps: address, topics and data,
70/// each with its RLP headers. A log that would take the receipt past this limit fails the frame
71/// that emits it with [`crate::Error::ReceiptTooLarge`], and the frame's state changes roll back
72/// as for any other error. This includes a log the runtime mirrors on behalf of a contract inside
73/// a frame. A mirrored log emitted outside any frame, such as while storage deposits are paid,
74/// fails the whole transaction instead. If a storage layer inside the frame or the transaction
75/// records the overflow and is then rolled back, the overflow flag rolls back with that layer's
76/// other changes and fails nothing outside it.
77///
78/// The block's synthetic transaction, which collects logs emitted outside any ethereum
79/// transaction, has the same limit. Nothing fails there: a log that does not fit stays a
80/// substrate-only `ContractEmitted` event.
81///
82/// An Ethereum transaction stays below this limit: at the EIP-7825 cap of 2^24 gas it can log at
83/// most about 2,092,700 bytes, by repeating `LOG0` over the same zeroed memory at 375 gas plus 8
84/// gas per byte. This chain enforces neither that cap nor that `LOG` price, so a transaction here
85/// can reach the limit.
86///
87/// The limit applies to all contracts, including those deployed before it, and is not selected by
88/// a behaviour version. It counts the logs of every contract a transaction calls, and the Ethereum
89/// block builder's memory and storage budget (`BLOCK_BUILDER_BYTES`) is derived from it.
90pub const MAX_RECEIPT_LOGS_BYTES: u32 = 2 * 1024 * 1024;
91
92/// The extra ref time charged per byte of an event's topics (32 bytes each) and data.
93///
94/// It bounds the event bytes (topics and data) a block can deposit; `integrity_test` checks that
95/// this fits the storage available to the runtime. The Ethereum block builder is sized from
96/// [`MAX_TRANSACTION_PAYLOAD_SIZE`] and [`MAX_RECEIPT_LOGS_BYTES`] instead.
97pub const EXTRA_EVENT_CHARGE_PER_BYTE: u64 = 256 * 1024;
98
99/// The maximum size for calldata and return data.
100///
101/// Please note that the calldata is limited to 128KB on geth anyways.
102pub const CALLDATA_BYTES: u32 = 128 * 1024;
103
104/// The maximum size of the transient storage in bytes.
105///
106/// This includes keys, values, and previous entries used for storage rollback.
107pub const TRANSIENT_STORAGE_BYTES: u32 = 4 * 1024;
108
109/// The maximum allowable length in bytes for (transient) storage keys.
110pub const STORAGE_KEY_BYTES: u32 = 128;
111
112/// The page size in which PolkaVM should allocate memory chunks.
113pub const PAGE_SIZE: u32 = 4 * 1024;
114
115/// The maximum amount of immutable bytes a single contract can store.
116///
117/// The current limit of 4kb allows storing up 16 U256 immutable variables.
118/// Which should always be enough because Solidity allows for 16 local (stack) variables.
119pub const IMMUTABLE_BYTES: u32 = 4 * 1024;
120
121/// upperbound of memory that can be used by the EVM interpreter.
122pub const EVM_MEMORY_BYTES: u32 = 1024 * 1024;
123
124/// EVM interpreter stack limit.
125pub const EVM_STACK_LIMIT: u32 = 1024;
126
127/// Limits that are only enforced on code upload.
128///
129/// # Note
130///
131/// This limit can be increased later without breaking existing contracts
132/// as it is only enforced at code upload time. Code already uploaded
133/// will not be affected by those limits.
134pub mod code {
135	use super::PAGE_SIZE;
136	use crate::{Config, Error, LOG_TARGET};
137	use alloc::vec::Vec;
138	use sp_runtime::{DispatchError, SaturatedConversion};
139
140	/// The maximum length of a code blob in bytes.
141	///
142	/// This mostly exist to prevent parsing too big blobs and to
143	/// have a maximum encoded length.
144	pub const BLOB_BYTES: u32 = 1024 * 1024;
145
146	/// The maximum amount of memory the interpreter is allowed to use for compilation artifacts.
147	pub const INTERPRETER_CACHE_BYTES: u32 = 1024 * 1024;
148
149	/// The maximum size of a basic block in number of instructions.
150	///
151	/// We need to limit the size of basic blocks because the interpreters lazy compilation
152	/// compiles one basic block at a time. A malicious program could trigger the compilation
153	/// of the whole program by creating one giant basic block otherwise.
154	pub const BASIC_BLOCK_SIZE: u32 = 1000;
155
156	/// The limit for memory that can be purged on demand.
157	///
158	/// It is the interpreter cache and the map of compiled blocks of a PVM frame. When a PVM
159	/// frame starts, the innermost PVM frame that waits on a sub-call purges it. Hence we
160	/// effectively only need to hold it once in RAM.
161	pub const PURGABLE_MEMORY_LIMIT: u32 = INTERPRETER_CACHE_BYTES + 2 * 1024 * 1024;
162
163	/// The limit for memory that needs to be kept alive for a contracts whole life time.
164	///
165	/// This means tuning this number affects the call stack depth.
166	pub const BASELINE_MEMORY_LIMIT: u32 = BLOB_BYTES + 512 * 1024;
167
168	/// Why [`check_pvm_code`] rejected a code blob.
169	///
170	/// The limits that were exceeded are reported alongside the measured value. They are not
171	/// meant to be hard-coded by tooling since they may be raised in future releases.
172	#[derive(Debug, PartialEq)]
173	#[non_exhaustive]
174	pub enum CodeRejection {
175		/// The blob is larger than allowed. Sizes are in bytes.
176		BlobTooLarge { size: u32, limit: u32 },
177		/// A basic block is larger than allowed. Sizes are in number of instructions.
178		BasicBlockTooLarge { size: u32, limit: u32 },
179		/// The program needs more purgeable memory than allowed. Sizes are in bytes.
180		PurgeableMemoryTooLarge { size: u32, limit: u32 },
181		/// The program needs more baseline memory than allowed. Sizes are in bytes.
182		BaselineMemoryTooLarge { size: u32, limit: u32 },
183		/// The program contains an instruction that contracts are not allowed to use.
184		InvalidInstruction,
185		/// The blob failed to parse, is a 32 bit program, uses the wrong instruction set, or
186		/// contains a malformed or unknown import.
187		Malformed,
188	}
189
190	impl CodeRejection {
191		/// The error the pallet returns on code upload for this rejection.
192		fn into_error<T: Config>(self) -> Error<T> {
193			match self {
194				Self::BlobTooLarge { .. } => Error::<T>::BlobTooLarge,
195				Self::BasicBlockTooLarge { .. } => Error::<T>::BasicBlockTooLarge,
196				Self::PurgeableMemoryTooLarge { .. } | Self::BaselineMemoryTooLarge { .. } => {
197					Error::<T>::StaticMemoryTooLarge
198				},
199				Self::InvalidInstruction => Error::<T>::InvalidInstruction,
200				Self::Malformed => Error::<T>::CodeRejected,
201			}
202		}
203	}
204
205	/// Make sure that the various program parts are within the defined limits.
206	///
207	/// This is the check the pallet runs on code upload. It is not generic over the runtime so
208	/// that tooling can find out ahead of deployment whether a blob would be accepted.
209	///
210	/// The verdict only matches a production runtime when `pallet-revive` is built without the
211	/// `runtime-benchmarks` feature. With it, a blob that uses the reserved `SENTINEL` ecalli
212	/// passes here but is rejected with [`CodeRejection::InvalidInstruction`] on upload, and a
213	/// blob that imports the benchmark-only `noop` syscall passes here but is rejected with
214	/// [`CodeRejection::Malformed`]. Features unify across the build, so a dependency can
215	/// enable it unasked.
216	pub fn check_pvm_code(pvm_blob: &[u8]) -> Result<(), CodeRejection> {
217		use polkavm_common::program::{
218			EstimateInterpreterMemoryUsageArgs, ISA_ReviveV1, InstructionSetKind,
219		};
220
221		check_blob_size(pvm_blob)?;
222
223		let available_syscalls = crate::vm::pvm::env::list_syscalls();
224
225		let program = polkavm::ProgramBlob::parse(pvm_blob.into()).map_err(|err| {
226			log::debug!(target: LOG_TARGET, "failed to parse polkavm blob: {err:?}");
227			CodeRejection::Malformed
228		})?;
229
230		if !program.is_64_bit() {
231			log::debug!(target: LOG_TARGET, "32bit programs are not supported.");
232			Err(CodeRejection::Malformed)?;
233		}
234
235		if program.isa() != InstructionSetKind::ReviveV1 {
236			log::debug!(target: LOG_TARGET, "Program instruction set '{}' is not '{}'", program.isa().name(), InstructionSetKind::ReviveV1.name());
237			Err(CodeRejection::Malformed)?;
238		}
239
240		// Need to check that no non-existent syscalls are used. This allows us to add
241		// new syscalls later without affecting already deployed code.
242		for (idx, import) in program.imports().iter().enumerate() {
243			// We are being defensive in case an attacker is able to somehow include
244			// a lot of imports. This is important because we search the array of host
245			// functions for every import.
246			if idx == available_syscalls.len() {
247				log::debug!(target: LOG_TARGET, "Program contains too many imports.");
248				Err(CodeRejection::Malformed)?;
249			}
250			let Some(import) = import else {
251				log::debug!(target: LOG_TARGET, "Program contains malformed import.");
252				return Err(CodeRejection::Malformed);
253			};
254			if !available_syscalls.contains(&import.as_bytes()) {
255				log::debug!(target: LOG_TARGET, "Program references unknown syscall: {}", import);
256				Err(CodeRejection::Malformed)?;
257			}
258		}
259
260		// This scans the whole program but we only do it once on code deployment.
261		// It is safe to do unchecked math in u32 because the size of the program
262		// was already checked above.
263		let mut max_block_size: u32 = 0;
264		let mut block_size: u32 = 0;
265		let mut basic_block_count: u32 = 0;
266		let mut instruction_count: u32 = 0;
267		for inst in program.instructions_with_isa(ISA_ReviveV1) {
268			use polkavm::program::Instruction;
269			block_size += 1;
270			instruction_count += 1;
271			if inst.kind.opcode().starts_new_basic_block() {
272				max_block_size = max_block_size.max(block_size);
273				block_size = 0;
274				basic_block_count += 1;
275			}
276			match inst.kind {
277				Instruction::invalid => {
278					log::debug!(target: LOG_TARGET, "invalid instruction at offset {}", inst.offset);
279					return Err(CodeRejection::InvalidInstruction);
280				},
281				// Since polkavm `0.30.0` linker will fail if it detects sbrk instruction.
282				// So this branch is never reached for programs built with polkavm >= 0.30.0.
283				//
284				// # Note
285				//
286				// The decoder might decode sbrk to `invalid` under our Revive isa. In this case,
287				// this branch is _not_ hit for sbrk but the one above instead.
288				Instruction::sbrk(_, _) => {
289					log::debug!(target: LOG_TARGET, "sbrk instruction is not allowed. offset {}", inst.offset);
290					return Err(CodeRejection::InvalidInstruction);
291				},
292				// Only benchmarking code is allowed to circumvent the import table. We might want
293				// to remove this magic syscall number later. Hence we need to prevent contracts
294				// from using it.
295				//
296				// The `as` conversion is fine here because we are just testing equality.
297				#[cfg(not(feature = "runtime-benchmarks"))]
298				Instruction::ecalli(idx) if idx as u32 == crate::SENTINEL => {
299					log::debug!(target: LOG_TARGET, "reserved syscall idx {idx}. offset {}", inst.offset);
300					return Err(CodeRejection::InvalidInstruction);
301				},
302				_ => (),
303			}
304		}
305		max_block_size = max_block_size.max(block_size);
306
307		if max_block_size > BASIC_BLOCK_SIZE {
308			log::debug!(target: LOG_TARGET, "basic block too large: {max_block_size} limit: {BASIC_BLOCK_SIZE}");
309			return Err(CodeRejection::BasicBlockTooLarge {
310				size: max_block_size,
311				limit: BASIC_BLOCK_SIZE,
312			});
313		}
314
315		let usage_args = EstimateInterpreterMemoryUsageArgs::BoundedCache {
316			max_cache_size_bytes: INTERPRETER_CACHE_BYTES,
317			instruction_count,
318			max_block_size,
319			basic_block_count,
320			page_size: PAGE_SIZE,
321		};
322
323		let program_info =
324			program.estimate_interpreter_memory_usage(usage_args).map_err(|err| {
325				log::debug!(target: LOG_TARGET, "failed to estimate memory usage of program: {err:?}");
326				CodeRejection::Malformed
327			})?;
328
329		log::trace!(
330			target: LOG_TARGET, "Contract memory usage: purgable={}/{} KB baseline={}/{}",
331			program_info.purgeable_ram_consumption, PURGABLE_MEMORY_LIMIT,
332			program_info.baseline_ram_consumption, BASELINE_MEMORY_LIMIT,
333		);
334
335		if program_info.purgeable_ram_consumption > PURGABLE_MEMORY_LIMIT {
336			log::debug!(target: LOG_TARGET, "contract uses too much purgeable memory: {} limit: {}",
337				program_info.purgeable_ram_consumption,
338				PURGABLE_MEMORY_LIMIT,
339			);
340			return Err(CodeRejection::PurgeableMemoryTooLarge {
341				size: program_info.purgeable_ram_consumption,
342				limit: PURGABLE_MEMORY_LIMIT,
343			});
344		}
345
346		if program_info.baseline_ram_consumption > BASELINE_MEMORY_LIMIT {
347			log::debug!(target: LOG_TARGET, "contract uses too much baseline memory: {} limit: {}",
348				program_info.baseline_ram_consumption,
349				BASELINE_MEMORY_LIMIT,
350			);
351			return Err(CodeRejection::BaselineMemoryTooLarge {
352				size: program_info.baseline_ram_consumption,
353				limit: BASELINE_MEMORY_LIMIT,
354			});
355		}
356
357		Ok(())
358	}
359
360	/// Runs [`check_pvm_code`] and turns a rejection into the pallet's dispatch error.
361	pub fn enforce<T: Config>(pvm_blob: Vec<u8>) -> Result<Vec<u8>, DispatchError> {
362		#[cfg(feature = "std")]
363		if std::env::var_os("REVIVE_SKIP_VALIDATION").is_some() {
364			check_blob_size(&pvm_blob).map_err(CodeRejection::into_error::<T>)?;
365			log::warn!(target: LOG_TARGET, "Skipping validation because env var REVIVE_SKIP_VALIDATION is set");
366			return Ok(pvm_blob);
367		}
368
369		check_pvm_code(&pvm_blob).map_err(CodeRejection::into_error::<T>)?;
370		Ok(pvm_blob)
371	}
372
373	fn check_blob_size(pvm_blob: &[u8]) -> Result<(), CodeRejection> {
374		let len: u64 = pvm_blob.len() as u64;
375		if len > BLOB_BYTES.into() {
376			log::debug!(target: LOG_TARGET, "contract blob too large: {len} limit: {BLOB_BYTES}");
377			return Err(CodeRejection::BlobTooLarge {
378				size: len.saturated_into(),
379				limit: BLOB_BYTES,
380			});
381		}
382		Ok(())
383	}
384
385	#[cfg(test)]
386	mod tests {
387		use super::CodeRejection;
388		use crate::{Error, tests::Test};
389		use sp_runtime::DispatchError;
390
391		fn to_error(rejection: CodeRejection) -> DispatchError {
392			rejection.into_error::<Test>().into()
393		}
394
395		#[test]
396		fn blob_too_large_maps_to_blob_too_large() {
397			let rejection = CodeRejection::BlobTooLarge { size: 2, limit: 1 };
398			assert_eq!(to_error(rejection), Error::<Test>::BlobTooLarge.into());
399		}
400
401		#[test]
402		fn basic_block_too_large_maps_to_basic_block_too_large() {
403			let rejection = CodeRejection::BasicBlockTooLarge { size: 2, limit: 1 };
404			assert_eq!(to_error(rejection), Error::<Test>::BasicBlockTooLarge.into());
405		}
406
407		#[test]
408		fn purgeable_memory_too_large_maps_to_static_memory_too_large() {
409			let rejection = CodeRejection::PurgeableMemoryTooLarge { size: 2, limit: 1 };
410			assert_eq!(to_error(rejection), Error::<Test>::StaticMemoryTooLarge.into());
411		}
412
413		#[test]
414		fn baseline_memory_too_large_maps_to_static_memory_too_large() {
415			let rejection = CodeRejection::BaselineMemoryTooLarge { size: 2, limit: 1 };
416			assert_eq!(to_error(rejection), Error::<Test>::StaticMemoryTooLarge.into());
417		}
418
419		#[test]
420		fn invalid_instruction_maps_to_invalid_instruction() {
421			assert_eq!(
422				to_error(CodeRejection::InvalidInstruction),
423				Error::<Test>::InvalidInstruction.into()
424			);
425		}
426
427		#[test]
428		fn malformed_maps_to_code_rejected() {
429			assert_eq!(to_error(CodeRejection::Malformed), Error::<Test>::CodeRejected.into());
430		}
431	}
432}
433
434/// The amount of total memory we require.
435///
436/// Unchecked math is okay since we evaluate at compile time.
437const fn memory_required() -> u32 {
438	// The root frame is not accounted for in CALL_STACK_DEPTH
439	let max_call_depth = CALL_STACK_DEPTH + 1;
440
441	let per_stack_memory = code::PURGABLE_MEMORY_LIMIT +
442		TRANSIENT_STORAGE_BYTES * 2 +
443		crate::access_list::MAX_ACCESS_LIST_BYTES;
444
445	let evm_max_initcode_size = revm::primitives::eip3860::MAX_INITCODE_SIZE as u32;
446	let evm_overhead = EVM_MEMORY_BYTES + evm_max_initcode_size + EVM_STACK_LIMIT * 32;
447	let per_frame_memory = if code::BASELINE_MEMORY_LIMIT > evm_overhead {
448		code::BASELINE_MEMORY_LIMIT
449	} else {
450		evm_overhead
451	} + CALLDATA_BYTES * 2;
452
453	per_stack_memory + max_call_depth * per_frame_memory
454}