From 15260f0265ba3aff8de75caf0593b6a666d7ef6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Wed, 16 Oct 2024 17:39:54 +0200 Subject: [PATCH 1/9] First draft --- proposals/0185-stricter-vm-verification.md | 110 +++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 proposals/0185-stricter-vm-verification.md diff --git a/proposals/0185-stricter-vm-verification.md b/proposals/0185-stricter-vm-verification.md new file mode 100644 index 000000000..ddb17ea03 --- /dev/null +++ b/proposals/0185-stricter-vm-verification.md @@ -0,0 +1,110 @@ +--- +simd: '0185' +title: Stricter VM verification constraints +authors: + - Sean Young + - Alexander Meißner +category: Standard +type: Core +status: Idea +created: 2024-10-16 +feature: TBD +--- + +## Summary + +Removing pitfalls and foot-guns from the virtual machine. + +## Motivation + +There are a couple of interactions between dApps and the virtual machine which +are currently allowed but make no sense and are even dangerous for dApps to +go unnoticed. + +TODO: + +- CPI verification + - Allows accidentally using `AccountInfo` structures which the program + runtime never serialized + - `AccountInfo` structures can be overwritten by CPI during CPI, causing + complex side effects +- VM stack gaps + - Complicates virtual address calculations + - False sense of security, + dApps which overrun their stack go unnoticed anyway + - Unaligned accesses near the edge of a stack frame can bleed into the next +- VM write access + - Bad write accesses go unnoticed if another error corrects them +- Syscall virtual address ranges + - Bad read and write accesses go unnoticed + +## Alternatives Considered + +None. + +## New Terminology + +None. + +## Detailed Design + +### CPI verification + +- The special treatment during CPI of instruction accounts with the +`is_executable` flag set is removed +- The following pointers must be on the stack or heap, +meaning their virtual address is inside `0x200000000..0x400000000`, +otherwise `SyscallError::InvalidPointer` must be thrown: + - The pointer in the array of `&[AccountInfo]` / `SolAccountInfo*` + - The `AccountInfo::data` field, + which is a `RefCell<&[u8]>` in `sol_invoke_signed_rust` +- The following pointers must point to what was originally serialized in the +input regions by the program runtime, +otherwise `SyscallError::InvalidPointer` must be thrown: + - `AccountInfo::key` / `SolAccountInfo::key` + - `AccountInfo::owner` / `SolAccountInfo::owner` + - `AccountInfo::lamports` / `SolAccountInfo::lamports` + - `AccountInfo::data::ptr` / `SolAccountInfo::data` + +### VM stack + +The virtual address space of the stack frames must become consecutive: + +- From: `0x200000000..0x200001000`, `0x200002000..0x200003000`, ... +- To: `0x200000000..0x200001000`, `0x200001000..0x200002000`, ... + +This goes for all programs globally and is not opt-in. +Thus, this change is independent of SIMD-0166. + +### VM write access + +When a write access to the input region (`0x400000000..0x500000000`) happens, +which overlaps with a range in which an accounts payload, including its resize +padding but not its metadata, was serialized it must be checked that: + +- The account is flagged as writable, +otherwise `InstructionError::ReadonlyDataModified` must be thrown +- The account is owned by the currently executed program, +otherwise `InstructionError::ExternalAccountDataModified` must be thrown + +Thus, changing and later restoring data in unowned accounts is prohibited. + +### Syscall virtual address ranges + +When a range in virtual address space which: + +- crosses a 4 GiB aligned boundary +- starts in any accounts payload (including its resize padding) and leaves it + +is passed to any syscall (such as `memcpy`) it must throw +`SyscallError::InvalidLength`. + +## Impact + +These restrictions have been extensively tested by replay against MNB. +Most of the dApps devs whose dApps would fail have been contacted and had +their dApps fixed already. + +## Security Considerations + +None. From 75fed5547e2c6fd2f3280c40fa521e35a167379e Mon Sep 17 00:00:00 2001 From: Sean Young Date: Thu, 19 Dec 2024 09:52:22 +0000 Subject: [PATCH 2/9] Make SIMD match implementation --- proposals/0185-stricter-vm-verification.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0185-stricter-vm-verification.md b/proposals/0185-stricter-vm-verification.md index ddb17ea03..76fd7559e 100644 --- a/proposals/0185-stricter-vm-verification.md +++ b/proposals/0185-stricter-vm-verification.md @@ -50,14 +50,14 @@ None. ### CPI verification -- The special treatment during CPI of instruction accounts with the -`is_executable` flag set is removed - The following pointers must be on the stack or heap, meaning their virtual address is inside `0x200000000..0x400000000`, otherwise `SyscallError::InvalidPointer` must be thrown: - The pointer in the array of `&[AccountInfo]` / `SolAccountInfo*` - The `AccountInfo::data` field, which is a `RefCell<&[u8]>` in `sol_invoke_signed_rust` + - The `AccountInfo::lamports` field, + which is a `RefCell<&u64>` in `sol_invoke_signed_rust` - The following pointers must point to what was originally serialized in the input regions by the program runtime, otherwise `SyscallError::InvalidPointer` must be thrown: @@ -93,10 +93,10 @@ Thus, changing and later restoring data in unowned accounts is prohibited. When a range in virtual address space which: -- crosses a 4 GiB aligned boundary -- starts in any accounts payload (including its resize padding) and leaves it +- starts in any account data (including its resize padding) and leaves it +- starts outside account data and enters it -is passed to any syscall (such as `memcpy`) it must throw +is passed to a syscall `memcpy`, `memmove`, `memset`, and `memcmp`, it must throw `SyscallError::InvalidLength`. ## Impact From 1ddba58d9a6a45f4fda656a36e82d236a4602b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Fri, 3 Jan 2025 15:59:35 +0100 Subject: [PATCH 3/9] Cleanup for publishing. --- ...on.md => 0219-stricter-vm-verification.md} | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) rename proposals/{0185-stricter-vm-verification.md => 0219-stricter-vm-verification.md} (72%) diff --git a/proposals/0185-stricter-vm-verification.md b/proposals/0219-stricter-vm-verification.md similarity index 72% rename from proposals/0185-stricter-vm-verification.md rename to proposals/0219-stricter-vm-verification.md index 76fd7559e..f78693108 100644 --- a/proposals/0185-stricter-vm-verification.md +++ b/proposals/0219-stricter-vm-verification.md @@ -1,42 +1,40 @@ --- -simd: '0185' +simd: '0219' title: Stricter VM verification constraints authors: - Sean Young - Alexander Meißner category: Standard type: Core -status: Idea -created: 2024-10-16 -feature: TBD +status: Review +created: 2025-01-06 +feature: GJVDwRkUPNdk9QaK4VsU4g1N41QNxhy1hevjf8kz45Mq --- ## Summary -Removing pitfalls and foot-guns from the virtual machine. +Removing pitfalls and foot-guns from the virtual machine and syscalls. ## Motivation There are a couple of interactions between dApps and the virtual machine which -are currently allowed but make no sense and are even dangerous for dApps to -go unnoticed. - -TODO: +are currently allowed but make no sense and are even dangerous for dApps: - CPI verification - Allows accidentally using `AccountInfo` structures which the program runtime never serialized - `AccountInfo` structures can be overwritten by CPI during CPI, causing complex side effects -- VM stack gaps +- Gaps in between VM stack frames - Complicates virtual address calculations - - False sense of security, - dApps which overrun their stack go unnoticed anyway + - False sense of security, dApps which overrun their stack can go unnoticed + anyway if they overrun it by an entire frame - Unaligned accesses near the edge of a stack frame can bleed into the next - VM write access - - Bad write accesses go unnoticed if another error corrects them -- Syscall virtual address ranges - - Bad read and write accesses go unnoticed + - Bad write accesses to account payload go unnoticed as long as the original + value is restored +- Syscall slice parameters + - Bad read and write accesses which span nonsensical ranges go unnoticed ## Alternatives Considered @@ -66,7 +64,7 @@ otherwise `SyscallError::InvalidPointer` must be thrown: - `AccountInfo::lamports` / `SolAccountInfo::lamports` - `AccountInfo::data::ptr` / `SolAccountInfo::data` -### VM stack +### Gaps in between VM stack frames The virtual address space of the stack frames must become consecutive: @@ -89,16 +87,30 @@ otherwise `InstructionError::ExternalAccountDataModified` must be thrown Thus, changing and later restoring data in unowned accounts is prohibited. -### Syscall virtual address ranges +### Syscall slice parameters When a range in virtual address space which: - starts in any account data (including its resize padding) and leaves it - starts outside account data and enters it -is passed to a syscall `memcpy`, `memmove`, `memset`, and `memcmp`, it must throw +is passed to `memcpy`, `memmove`, `memset`, or `memcmp`, it must throw `SyscallError::InvalidLength`. +Except for CPI, all other syscalls which +act on ranges in the virtual address space are confined to a single +memory region for now. Meaning they have to stay within one of: + +- Readonly data +- Stack +- Heap +- Account meta data +- Account data without resize padding +- Account resize padding + +And can not cross into any other region. This restriction is planned to +be lifted in another SIMD. + ## Impact These restrictions have been extensively tested by replay against MNB. From 8677cf8be7a20f6cd8a28d524e2c51fcd7bddfb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Wed, 6 Aug 2025 21:26:31 +0200 Subject: [PATCH 4/9] Further changes to the way memory accesses are treated. --- proposals/0219-stricter-vm-verification.md | 73 +++++++++++++--------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/proposals/0219-stricter-vm-verification.md b/proposals/0219-stricter-vm-verification.md index f78693108..76ac8325b 100644 --- a/proposals/0219-stricter-vm-verification.md +++ b/proposals/0219-stricter-vm-verification.md @@ -8,7 +8,7 @@ category: Standard type: Core status: Review created: 2025-01-06 -feature: GJVDwRkUPNdk9QaK4VsU4g1N41QNxhy1hevjf8kz45Mq +feature: C37iaPi6VE4CZDueU1vL8y6pGp5i8amAbEsF31xzz723 --- ## Summary @@ -30,7 +30,10 @@ are currently allowed but make no sense and are even dangerous for dApps: - False sense of security, dApps which overrun their stack can go unnoticed anyway if they overrun it by an entire frame - Unaligned accesses near the edge of a stack frame can bleed into the next -- VM write access +- VM memory access + - Bad read accesses to account payload go unnoticed as long as they stay + within the reserved address space, even if they leave the actual account + payload - Bad write accesses to account payload go unnoticed as long as the original value is restored - Syscall slice parameters @@ -74,42 +77,50 @@ The virtual address space of the stack frames must become consecutive: This goes for all programs globally and is not opt-in. Thus, this change is independent of SIMD-0166. -### VM write access +### VM memory access -When a write access to the input region (`0x400000000..0x500000000`) happens, -which overlaps with a range in which an accounts payload, including its resize -padding but not its metadata, was serialized it must be checked that: +Memory accesses (both by the program and by syscalls) which span across memory +mapping regions are considered access violations. Accesses to multiple regions +(e.g. by memcpy syscalls) have to be split into multiple separate accesses, +one for each region: -- The account is flagged as writable, -otherwise `InstructionError::ReadonlyDataModified` must be thrown -- The account is owned by the currently executed program, -otherwise `InstructionError::ExternalAccountDataModified` must be thrown - -Thus, changing and later restoring data in unowned accounts is prohibited. +- Readonly data (`0x100000000..0x200000000`) +- Stack (`0x200000000..0x300000000`) +- Heap (`0x300000000..0x400000000`) +- Instruction meta data +- Account meta data +- Account payload address space +- Instruction payload -### Syscall slice parameters +The payload address space of an account is the range in the serialized input +region (`0x400000000..0x500000000`) which covers the payload and optionally the +10 KiB resize padding (if not a loader-v1 program), but not the accounts +metadata. -When a range in virtual address space which: +For all memory accesses to the payload address space of an account which is +flagged as writable and owned by the currently executed program, check that: -- starts in any account data (including its resize padding) and leaves it -- starts outside account data and enters it +- The access is completely within the maximum account length, +otherwise `InstructionError::InvalidRealloc` must be thrown. +- The access is completely within the rest of the account growth budget of the +transaction, otherwise `InstructionError::InvalidRealloc` must be thrown. +- The access is completely within the current length of the account, +otherwise extend the account with zeros to the maximum allowed by the previous +two checks. -is passed to `memcpy`, `memmove`, `memset`, or `memcmp`, it must throw -`SyscallError::InvalidLength`. +For loads / read accesses to the payload address space of an account check +that: -Except for CPI, all other syscalls which -act on ranges in the virtual address space are confined to a single -memory region for now. Meaning they have to stay within one of: +- The access is completely within the current length of the account, +otherwise `InstructionError::AccountDataTooSmall` must be thrown. -- Readonly data -- Stack -- Heap -- Account meta data -- Account data without resize padding -- Account resize padding +For stores / write accesses to the payload address space of an account check +that: -And can not cross into any other region. This restriction is planned to -be lifted in another SIMD. +- The account is flagged as writable, +otherwise `InstructionError::ReadonlyDataModified` must be thrown +- The account is owned by the currently executed program, +otherwise `InstructionError::ExternalAccountDataModified` must be thrown. ## Impact @@ -117,6 +128,10 @@ These restrictions have been extensively tested by replay against MNB. Most of the dApps devs whose dApps would fail have been contacted and had their dApps fixed already. +Programs which used the SDKs account realloc function, which is now deprecated, +should upgrade in order to avoid the read-before-write access to uninitialized +memory. + ## Security Considerations None. From 9435f0cac00aaec0ac0585e584d79a4c9c2c21ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Fri, 20 Jun 2025 18:16:34 +0200 Subject: [PATCH 5/9] Adds account data direct mapping to the "Motivation" section. --- proposals/0219-stricter-vm-verification.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/proposals/0219-stricter-vm-verification.md b/proposals/0219-stricter-vm-verification.md index 76ac8325b..4b7cf4158 100644 --- a/proposals/0219-stricter-vm-verification.md +++ b/proposals/0219-stricter-vm-verification.md @@ -39,6 +39,18 @@ are currently allowed but make no sense and are even dangerous for dApps: - Syscall slice parameters - Bad read and write accesses which span nonsensical ranges go unnoticed +Furthermore, at the moment all validator implementations have to copy +(and compare) data in and out of the virtual memory of the virtual machine. +There are four possible account data copy paths: + +- Serialization: Copy from program runtime (host) to virtual machine (guest) +- CPI call edge: Copy from virtual machine (guest) to program runtime (host) +- CPI return edge: Copy from program runtime (host) to virtual machine (guest) +- Deserialization: Copy from virtual machine (guest) to program runtime (host) + +By restricting the allowed behavior of dApps we enable the validator to map +account payload data directly, avoiding copies and compares. + ## Alternatives Considered None. From 5cff568c9ffe470c5f9d0e8aece1aae47f5a9b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Thu, 17 Jul 2025 20:04:50 +0200 Subject: [PATCH 6/9] Renames the proposal. --- ...cation.md => 0219-stricter-abi-and-runtime-constraints.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{0219-stricter-vm-verification.md => 0219-stricter-abi-and-runtime-constraints.md} (97%) diff --git a/proposals/0219-stricter-vm-verification.md b/proposals/0219-stricter-abi-and-runtime-constraints.md similarity index 97% rename from proposals/0219-stricter-vm-verification.md rename to proposals/0219-stricter-abi-and-runtime-constraints.md index 4b7cf4158..9e0f37c53 100644 --- a/proposals/0219-stricter-vm-verification.md +++ b/proposals/0219-stricter-abi-and-runtime-constraints.md @@ -1,6 +1,6 @@ --- simd: '0219' -title: Stricter VM verification constraints +title: Stricter ABI and Runtime Constraints authors: - Sean Young - Alexander Meißner @@ -13,7 +13,7 @@ feature: C37iaPi6VE4CZDueU1vL8y6pGp5i8amAbEsF31xzz723 ## Summary -Removing pitfalls and foot-guns from the virtual machine and syscalls. +Removing pitfalls and foot-guns from the ABI (including syscalls) and runtime. ## Motivation From 74e59a85d422987edb15c4c95eea76bb03ff4b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Wed, 6 Aug 2025 20:11:58 +0200 Subject: [PATCH 7/9] Reverts the removal of stack frame gaps. --- .../0219-stricter-abi-and-runtime-constraints.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/proposals/0219-stricter-abi-and-runtime-constraints.md b/proposals/0219-stricter-abi-and-runtime-constraints.md index 9e0f37c53..554148ed6 100644 --- a/proposals/0219-stricter-abi-and-runtime-constraints.md +++ b/proposals/0219-stricter-abi-and-runtime-constraints.md @@ -25,11 +25,6 @@ are currently allowed but make no sense and are even dangerous for dApps: runtime never serialized - `AccountInfo` structures can be overwritten by CPI during CPI, causing complex side effects -- Gaps in between VM stack frames - - Complicates virtual address calculations - - False sense of security, dApps which overrun their stack can go unnoticed - anyway if they overrun it by an entire frame - - Unaligned accesses near the edge of a stack frame can bleed into the next - VM memory access - Bad read accesses to account payload go unnoticed as long as they stay within the reserved address space, even if they leave the actual account @@ -79,16 +74,6 @@ otherwise `SyscallError::InvalidPointer` must be thrown: - `AccountInfo::lamports` / `SolAccountInfo::lamports` - `AccountInfo::data::ptr` / `SolAccountInfo::data` -### Gaps in between VM stack frames - -The virtual address space of the stack frames must become consecutive: - -- From: `0x200000000..0x200001000`, `0x200002000..0x200003000`, ... -- To: `0x200000000..0x200001000`, `0x200001000..0x200002000`, ... - -This goes for all programs globally and is not opt-in. -Thus, this change is independent of SIMD-0166. - ### VM memory access Memory accesses (both by the program and by syscalls) which span across memory From 361174e4ed439bbb46af985b7f2af5efff9e21ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Thu, 28 Aug 2025 22:02:17 +0200 Subject: [PATCH 8/9] Moves the definition of memory regions into the Terminology section. --- ...19-stricter-abi-and-runtime-constraints.md | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/proposals/0219-stricter-abi-and-runtime-constraints.md b/proposals/0219-stricter-abi-and-runtime-constraints.md index 554148ed6..611d455d6 100644 --- a/proposals/0219-stricter-abi-and-runtime-constraints.md +++ b/proposals/0219-stricter-abi-and-runtime-constraints.md @@ -52,7 +52,22 @@ None. ## New Terminology -None. +### VM memory regions + +The memory regions are (in ABI v0 and v1): + +- Readonly data (`0x100000000..0x200000000`) +- Stack (`0x200000000..0x300000000`) +- Heap (`0x300000000..0x400000000`) +- Instruction meta data +- Account meta data +- Account payload address space +- Instruction payload and program key + +The payload address space of an account is the range in the serialized input +region (`0x400000000..0x500000000`) which covers the payload and optionally the +10 KiB resize padding (if not a loader-v1 program), but not the accounts +metadata. ## Detailed Design @@ -79,20 +94,7 @@ otherwise `SyscallError::InvalidPointer` must be thrown: Memory accesses (both by the program and by syscalls) which span across memory mapping regions are considered access violations. Accesses to multiple regions (e.g. by memcpy syscalls) have to be split into multiple separate accesses, -one for each region: - -- Readonly data (`0x100000000..0x200000000`) -- Stack (`0x200000000..0x300000000`) -- Heap (`0x300000000..0x400000000`) -- Instruction meta data -- Account meta data -- Account payload address space -- Instruction payload - -The payload address space of an account is the range in the serialized input -region (`0x400000000..0x500000000`) which covers the payload and optionally the -10 KiB resize padding (if not a loader-v1 program), but not the accounts -metadata. +one for each region. For all memory accesses to the payload address space of an account which is flagged as writable and owned by the currently executed program, check that: From 53f1c2c884c558a3368bd49e0ecf0b501c1bec53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Thu, 28 Aug 2025 22:07:36 +0200 Subject: [PATCH 9/9] Adds sysvar syscall restrictions. --- proposals/0219-stricter-abi-and-runtime-constraints.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/proposals/0219-stricter-abi-and-runtime-constraints.md b/proposals/0219-stricter-abi-and-runtime-constraints.md index 611d455d6..b9c838df3 100644 --- a/proposals/0219-stricter-abi-and-runtime-constraints.md +++ b/proposals/0219-stricter-abi-and-runtime-constraints.md @@ -25,6 +25,10 @@ are currently allowed but make no sense and are even dangerous for dApps: runtime never serialized - `AccountInfo` structures can be overwritten by CPI during CPI, causing complex side effects +- Syscall alignment requirements + - In ABI v0 the account input region has no alignment guarantees (it is + 1 byte aligned) and ABI v1 has 8 byte alignment. However, there are some + syscalls such as the reading of sysvars which require 16 byte alignment. - VM memory access - Bad read accesses to account payload go unnoticed as long as they stay within the reserved address space, even if they leave the actual account @@ -71,11 +75,12 @@ metadata. ## Detailed Design -### CPI verification +### Syscall parameters - The following pointers must be on the stack or heap, meaning their virtual address is inside `0x200000000..0x400000000`, otherwise `SyscallError::InvalidPointer` must be thrown: + - The destination address of all sysvar related syscalls - The pointer in the array of `&[AccountInfo]` / `SolAccountInfo*` - The `AccountInfo::data` field, which is a `RefCell<&[u8]>` in `sol_invoke_signed_rust`