Skip to content

Commit 9446132

Browse files
authored
Emit consume_all_gas in invalid and bounds checks (#433)
Closes #374 Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
1 parent 91bd1b0 commit 9446132

File tree

7 files changed

+69
-16
lines changed

7 files changed

+69
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Supported `polkadot-sdk` rev: `unstable2507`
1515
- Instruct the LLVM backend and linker to `--relax` (may lead to smaller contract code size).
1616
- Standard JSON mode: Don't forward EVM bytecode related output selections to solc.
1717
- The supported `polkadot-sdk` release is `unstable2507`.
18+
- The `INVALID` opcode and OOB memory accesses now consume all remaining gas.
1819

1920
### Fixed:
2021
- The missing `STOP` instruction at the end of `code` blocks.

crates/integration/codesize.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"Baseline": 911,
3-
"Computation": 2293,
4-
"DivisionArithmetics": 14353,
5-
"ERC20": 16936,
3+
"Computation": 2337,
4+
"DivisionArithmetics": 14488,
5+
"ERC20": 17041,
66
"Events": 1672,
77
"FibonacciIterative": 1454,
8-
"Flipper": 2083,
9-
"SHA1": 7727
8+
"Flipper": 2106,
9+
"SHA1": 7814
1010
}

crates/integration/src/tests.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,3 +669,48 @@ fn sbrk_bounds_checks() {
669669
"not seeing a trap means the contract did not catch the OOB"
670670
);
671671
}
672+
673+
#[test]
674+
fn invalid_opcode_works() {
675+
let code = &build_yul(&[(
676+
"invalid.yul",
677+
r#"object "Test" {
678+
code {
679+
invalid()
680+
}
681+
object "Test_deployed" {
682+
code {
683+
invalid()
684+
}
685+
}
686+
}"#,
687+
)])
688+
.unwrap()["invalid.yul:Test"];
689+
690+
let results = Specs {
691+
actions: vec![
692+
Instantiate {
693+
origin: TestAddress::Alice,
694+
value: 0,
695+
gas_limit: Some(GAS_LIMIT),
696+
storage_deposit_limit: None,
697+
code: Code::Bytes(code.to_vec()),
698+
data: Default::default(),
699+
salt: OptionalHex::default(),
700+
},
701+
VerifyCall(VerifyCallExpectation {
702+
success: false,
703+
..Default::default()
704+
}),
705+
],
706+
differential: false,
707+
..Default::default()
708+
}
709+
.run();
710+
711+
let CallResult::Instantiate { result, .. } = results.last().unwrap() else {
712+
unreachable!()
713+
};
714+
715+
assert_eq!(result.weight_consumed, GAS_LIMIT);
716+
}

crates/llvm-context/src/polkavm/context/function/runtime/revive.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ impl RuntimeFunction for WordToPointer {
5151
)?;
5252

5353
let block_continue = context.append_basic_block("offset_pointer_ok");
54-
let block_trap = context.append_basic_block("offset_pointer_overflow");
55-
context.build_conditional_branch(is_overflow, block_trap, block_continue)?;
54+
let block_invalid = context.append_basic_block("offset_pointer_overflow");
55+
context.build_conditional_branch(is_overflow, block_invalid, block_continue)?;
5656

57-
context.set_basic_block(block_trap);
58-
context.build_call(context.intrinsics().trap, &[], "invalid_trap");
57+
context.set_basic_block(block_invalid);
58+
context.build_runtime_call(revive_runtime_api::polkavm_imports::INVALID, &[]);
5959
context.build_unreachable();
6060

6161
context.set_basic_block(block_continue);

crates/llvm-context/src/polkavm/evm/return.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,14 @@ pub fn stop(context: &mut Context) -> anyhow::Result<()> {
5252
/// Translates the `invalid` instruction.
5353
/// Burns all gas using an out-of-bounds memory store, causing a panic.
5454
pub fn invalid(context: &mut Context) -> anyhow::Result<()> {
55-
crate::polkavm::evm::memory::store(
56-
context,
57-
context.word_type().const_all_ones(),
58-
context.word_const(0),
59-
)?;
60-
context.build_call(context.intrinsics().trap, &[], "invalid_trap");
55+
let invalid_block = context.append_basic_block("explicit_invalid");
56+
context.build_unconditional_branch(invalid_block);
57+
context.set_basic_block(invalid_block);
58+
context.build_runtime_call(revive_runtime_api::polkavm_imports::INVALID, &[]);
59+
context.build_unreachable();
60+
61+
context.set_basic_block(context.append_basic_block("dead_code"));
62+
6163
Ok(())
6264
}
6365

crates/runtime-api/src/polkavm_imports.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ POLKAVM_IMPORT(uint64_t, code_size, uint32_t)
6969

7070
POLKAVM_IMPORT(void, code_hash, uint32_t, uint32_t)
7171

72+
POLKAVM_IMPORT(void, consume_all_gas)
73+
7274
POLKAVM_IMPORT(uint32_t, delegate_call, uint64_t, uint64_t, uint64_t, uint32_t, uint64_t, uint64_t)
7375

7476
POLKAVM_IMPORT(void, deposit_event, uint32_t, uint32_t, uint32_t, uint32_t)

crates/runtime-api/src/polkavm_imports.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub static HASH_KECCAK_256: &str = "hash_keccak_256";
4848

4949
pub static INSTANTIATE: &str = "instantiate";
5050

51+
pub static INVALID: &str = "consume_all_gas";
52+
5153
pub static NOW: &str = "now";
5254

5355
pub static ORIGIN: &str = "origin";
@@ -70,7 +72,7 @@ pub static VALUE_TRANSFERRED: &str = "value_transferred";
7072

7173
/// All imported runtime API symbols.
7274
/// Useful for configuring common attributes and linkage.
73-
pub static IMPORTS: [&str; 33] = [
75+
pub static IMPORTS: [&str; 34] = [
7476
ADDRESS,
7577
BALANCE,
7678
BALANCE_OF,
@@ -94,6 +96,7 @@ pub static IMPORTS: [&str; 33] = [
9496
GET_STORAGE,
9597
HASH_KECCAK_256,
9698
INSTANTIATE,
99+
INVALID,
97100
NOW,
98101
ORIGIN,
99102
REF_TIME_LEFT,

0 commit comments

Comments
 (0)