Line data Source code
1 : #include "fd_vm_private.h"
2 : #include "../runtime/context/fd_exec_epoch_ctx.h"
3 : #include "../runtime/context/fd_exec_slot_ctx.h"
4 : #include "../features/fd_features.h"
5 :
6 : /* fd_vm_syscall_strerror() returns the error message corresponding to err,
7 : intended to be logged by log_collector, or an empty string if the error code
8 : should be omitted in logs for whatever reason. Omitted examples are success,
9 : panic (logged in place)...
10 : See also fd_log_collector_program_failure(). */
11 : char const *
12 183 : fd_vm_syscall_strerror( int err ) {
13 :
14 183 : switch( err ) {
15 :
16 6 : case FD_VM_ERR_SYSCALL_INVALID_STRING: return "invalid utf-8 sequence"; // truncated
17 9 : case FD_VM_ERR_SYSCALL_ABORT: return "SBF program panicked";
18 24 : case FD_VM_ERR_SYSCALL_PANIC: return "SBF program Panicked in..."; // truncated
19 0 : case FD_VM_ERR_SYSCALL_INVOKE_CONTEXT_BORROW_FAILED: return "Cannot borrow invoke context";
20 0 : case FD_VM_ERR_SYSCALL_MALFORMED_SIGNER_SEED: return "Malformed signer seed"; // truncated
21 3 : case FD_VM_ERR_SYSCALL_BAD_SEEDS: return "Could not create program address with signer seeds"; // truncated
22 0 : case FD_VM_ERR_SYSCALL_PROGRAM_NOT_SUPPORTED: return "Program not supported by inner instructions"; // truncated
23 24 : case FD_VM_ERR_SYSCALL_UNALIGNED_POINTER: return "Unaligned pointer";
24 0 : case FD_VM_ERR_SYSCALL_TOO_MANY_SIGNERS: return "Too many signers";
25 0 : case FD_VM_ERR_SYSCALL_INSTRUCTION_TOO_LARGE: return "Instruction passed to inner instruction is too large"; // truncated
26 0 : case FD_VM_ERR_SYSCALL_TOO_MANY_ACCOUNTS: return "Too many accounts passed to inner instruction";
27 57 : case FD_VM_ERR_SYSCALL_COPY_OVERLAPPING: return "Overlapping copy";
28 0 : case FD_VM_ERR_SYSCALL_RETURN_DATA_TOO_LARGE: return "Return data too large"; // truncated
29 9 : case FD_VM_ERR_SYSCALL_TOO_MANY_SLICES: return "Hashing too many sequences";
30 21 : case FD_VM_ERR_SYSCALL_INVALID_LENGTH: return "InvalidLength";
31 0 : case FD_VM_ERR_SYSCALL_MAX_INSTRUCTION_DATA_LEN_EXCEEDED: return "Invoked an instruction with data that is too large"; // truncated
32 0 : case FD_VM_ERR_SYSCALL_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED: return "Invoked an instruction with too many accounts"; // truncated
33 0 : case FD_VM_ERR_SYSCALL_MAX_INSTRUCTION_ACCOUNT_INFOS_EXCEEDED: return "Invoked an instruction with too many account info's"; // truncated
34 24 : case FD_VM_ERR_SYSCALL_INVALID_ATTRIBUTE: return "InvalidAttribute";
35 0 : case FD_VM_ERR_SYSCALL_INVALID_POINTER: return "Invalid pointer";
36 0 : case FD_VM_ERR_SYSCALL_ARITHMETIC_OVERFLOW: return "Arithmetic overflow";
37 :
38 3 : case FD_VM_ERR_SYSCALL_POSEIDON_INVALID_PARAMS: return "Syscall error: Invalid parameters.";
39 3 : case FD_VM_ERR_SYSCALL_POSEIDON_INVALID_ENDIANNESS: return "Syscall error: Invalid endianness.";
40 :
41 0 : default: break;
42 183 : }
43 :
44 0 : return "";
45 183 : }
46 :
47 : /* fd_vm_ebpf_strerror() returns the error message corresponding to err,
48 : intended to be logged by log_collector, or an empty string if the error code
49 : should be omitted in logs for whatever reason.
50 : See also fd_log_collector_program_failure(). */
51 : char const *
52 774 : fd_vm_ebpf_strerror( int err ) {
53 :
54 774 : switch( err ) {
55 :
56 0 : case FD_VM_ERR_EBPF_ELF_ERROR: return "ELF error"; // truncated
57 0 : case FD_VM_ERR_EBPF_FUNCTION_ALREADY_REGISTERED: return "function was already registered"; // truncated
58 0 : case FD_VM_ERR_EBPF_CALL_DEPTH_EXCEEDED: return "exceeded max BPF to BPF call depth";
59 0 : case FD_VM_ERR_EBPF_EXIT_ROOT_CALL_FRAME: return "attempted to exit root call frame";
60 0 : case FD_VM_ERR_EBPF_DIVIDE_BY_ZERO: return "divide by zero at BPF instruction";
61 0 : case FD_VM_ERR_EBPF_DIVIDE_OVERFLOW: return "division overflow at BPF instruction";
62 0 : case FD_VM_ERR_EBPF_EXECUTION_OVERRUN: return "attempted to execute past the end of the text segment at BPF instruction";
63 0 : case FD_VM_ERR_EBPF_CALL_OUTSIDE_TEXT_SEGMENT: return "callx attempted to call outside of the text segment";
64 0 : case FD_VM_ERR_EBPF_EXCEEDED_MAX_INSTRUCTIONS: return "exceeded CUs meter at BPF instruction";
65 0 : case FD_VM_ERR_EBPF_JIT_NOT_COMPILED: return "program has not been JIT-compiled";
66 0 : case FD_VM_ERR_EBPF_INVALID_VIRTUAL_ADDRESS: return "invalid virtual address"; // truncated
67 0 : case FD_VM_ERR_EBPF_INVALID_MEMORY_REGION: return "Invalid memory region at index"; // truncated
68 774 : case FD_VM_ERR_EBPF_ACCESS_VIOLATION: return "Access violation"; // truncated
69 0 : case FD_VM_ERR_EBPF_STACK_ACCESS_VIOLATION: return "Access violation in stack frame"; // truncated
70 0 : case FD_VM_ERR_EBPF_INVALID_INSTRUCTION: return "invalid BPF instruction";
71 0 : case FD_VM_ERR_EBPF_UNSUPPORTED_INSTRUCTION: return "unsupported BPF instruction";
72 0 : case FD_VM_ERR_EBPF_EXHAUSTED_TEXT_SEGMENT: return "Compilation exhausted text segment at BPF instruction"; // truncated
73 0 : case FD_VM_ERR_EBPF_LIBC_INVOCATION_FAILED: return "Libc calling returned error code"; // truncated
74 0 : case FD_VM_ERR_EBPF_VERIFIER_ERROR: return "Verifier error"; // truncated
75 0 : case FD_VM_ERR_EBPF_SYSCALL_ERROR: return ""; // handled explicitly via fd_vm_syscall_strerror()
76 :
77 0 : default: break;
78 774 : }
79 :
80 0 : return "";
81 774 : }
82 :
83 : /* fd_vm_strerror() returns the error message corresponding to err, used internally
84 : for system logs, NOT for log_collector / transaction logs. */
85 : char const *
86 99 : fd_vm_strerror( int err ) {
87 :
88 99 : switch( err ) {
89 :
90 : /* "Standard" Firedancer error codes */
91 :
92 3 : case FD_VM_SUCCESS: return "SUCCESS success";
93 3 : case FD_VM_ERR_INVAL: return "INVAL invalid request";
94 3 : case FD_VM_ERR_UNSUP: return "UNSUP unsupported request";
95 3 : case FD_VM_ERR_PERM: return "PERM unauthorized request";
96 3 : case FD_VM_ERR_FULL: return "FULL storage full";
97 3 : case FD_VM_ERR_EMPTY: return "EMPTY nothing to do";
98 3 : case FD_VM_ERR_IO: return "IO input-output error";
99 3 : case FD_VM_ERR_AGAIN: return "AGAIN try again later";
100 :
101 : /* VM exec error codes */
102 :
103 3 : case FD_VM_ERR_SIGTEXT: return "SIGTEXT illegal program counter";
104 3 : case FD_VM_ERR_SIGSPLIT: return "SIGSPLIT split multiword instruction";
105 3 : case FD_VM_ERR_SIGCALL: return "unsupported BPF instruction";
106 3 : case FD_VM_ERR_SIGSTACK: return "SIGSTACK call depth limit exceeded";
107 3 : case FD_VM_ERR_SIGILL: return "SIGILL illegal instruction";
108 3 : case FD_VM_ERR_SIGSEGV: return "SIGSEGV illegal memory address";
109 3 : case FD_VM_ERR_SIGBUS: return "SIGBUS misaligned memory address";
110 3 : case FD_VM_ERR_SIGRDONLY: return "SIGRDONLY illegal write";
111 3 : case FD_VM_ERR_SIGCOST: return "SIGCOST compute unit limit exceeded";
112 0 : case FD_VM_ERR_SIGFPE: return "SIGFPE division by zero";
113 :
114 : /* VM syscall error codes */
115 : /* https://github.com/anza-xyz/agave/blob/v2.0.6/programs/bpf_loader/src/syscalls/mod.rs#L81 */
116 :
117 3 : case FD_VM_ERR_ABORT: return "SBF program panicked";
118 3 : case FD_VM_ERR_PANIC: return "PANIC"; /* FIXME: description */
119 3 : case FD_VM_ERR_MEM_OVERLAP: return "MEM_OVERLAP"; /* FIXME: description */
120 3 : case FD_VM_ERR_INSTR_ERR: return "INSTR_ERR"; /* FIXME: description */
121 3 : case FD_VM_ERR_RETURN_DATA_TOO_LARGE: return "RETURN_DATA_TOO_LARGE"; /* FIXME: description */
122 3 : case FD_VM_ERR_INVOKE_CONTEXT_BORROW_FAILED: return "INVOKE_CONTEXT_BORROW_FAILED"; /* FIXME: description */
123 :
124 : /* VM validate error codes */
125 :
126 3 : case FD_VM_ERR_INVALID_OPCODE: return "INVALID_OPCODE detected an invalid opcode";
127 3 : case FD_VM_ERR_INVALID_SRC_REG: return "INVALID_SRC_REG detected an invalid source register";
128 3 : case FD_VM_ERR_INVALID_DST_REG: return "INVALID_DST_REG detected an invalid destination register";
129 3 : case FD_VM_ERR_INF_LOOP: return "INF_LOOP detected an infinite loop";
130 3 : case FD_VM_ERR_JMP_OUT_OF_BOUNDS: return "JMP_OUT_OF_BOUNDS detected an out of bounds jump";
131 3 : case FD_VM_ERR_JMP_TO_ADDL_IMM: return "JMP_TO_ADDL_IMM detected a jump to an addl imm";
132 3 : case FD_VM_ERR_INVALID_END_IMM: return "INVALID_END_IMM detected an invalid immediate for an endianness conversion instruction";
133 3 : case FD_VM_ERR_INCOMPLETE_LDQ: return "INCOMPLETE_LDQ detected an incomplete ldq at program end";
134 3 : case FD_VM_ERR_LDQ_NO_ADDL_IMM: return "LDQ_NO_ADDL_IMM detected a ldq without an addl imm following it";
135 3 : case FD_VM_ERR_NO_SUCH_EXT_CALL: return "NO_SUCH_EXT_CALL detected a call imm with no function was registered for that immediate";
136 0 : case FD_VM_ERR_INVALID_REG: return "INVALID_REG detected an invalid register number in a callx instruction";
137 0 : case FD_VM_ERR_BAD_TEXT: return "BAD_TEXT detected a bad text section";
138 0 : case FD_VM_SH_OVERFLOW: return "SH_OVERFLOW detected a shift overflow in an instruction";
139 0 : case FD_VM_TEXT_SZ_UNALIGNED: return "TEXT_SZ_UNALIGNED detected an unaligned text section size (not a multiple of 8)";
140 :
141 0 : default: break;
142 99 : }
143 :
144 0 : return "UNKNOWN probably not a FD_VM_ERR code";
145 99 : }
146 :
147 : /* FIXME: add a pedantic version of this validation that does things
148 : like:
149 : - only 0 imms when the instruction does not use an imm
150 : - same as above but for src/dst reg, offset */
151 : /* FIXME: HANDLING OF LDQ ON NEWER SBPF VERSUS OLDER SBPF (AND WITH CALL
152 : REG) */
153 : /* FIXME: LINK TO SOLANA CODE VALIDATE AND DOUBLE CHECK THIS BEHAVES
154 : IDENTICALLY! */
155 :
156 : int
157 6651 : fd_vm_validate( fd_vm_t const * vm ) {
158 :
159 : /* A mapping of all the possible 1-byte sBPF opcodes to their
160 : validation criteria. */
161 :
162 673603557 : # define FD_VALID ((uchar)0) /* Valid opcode */
163 5251095 : # define FD_CHECK_JMP ((uchar)1) /* Validation should check that the instruction is a valid jump */
164 14805 : # define FD_CHECK_END ((uchar)2) /* Validation should check that the instruction is a valid endianness conversion */
165 856138323 : # define FD_CHECK_ST ((uchar)3) /* Validation should check that the instruction is a valid store */
166 1355256 : # define FD_CHECK_LDQ ((uchar)4) /* Validation should check that the instruction is a valid load-quad */
167 64473162 : # define FD_CHECK_DIV ((uchar)5) /* Validation should check that the instruction is a valid division by immediate */
168 48328023 : # define FD_CHECK_SH32 ((uchar)6) /* Validation should check that the immediate is a valid 32-bit shift exponent */
169 49413999 : # define FD_CHECK_SH64 ((uchar)7) /* Validation should check that the immediate is a valid 64-bit shift exponent */
170 1098279 : # define FD_INVALID ((uchar)8) /* The opcode is invalid */
171 123828 : # define FD_CHECK_CALLX ((uchar)9) /* Validation should check that callx has valid register number */
172 :
173 6651 : static uchar const validation_map[ 256 ] = {
174 : /* 0x00 */ FD_INVALID, /* 0x01 */ FD_INVALID, /* 0x02 */ FD_INVALID, /* 0x03 */ FD_INVALID,
175 : /* 0x04 */ FD_VALID, /* 0x05 */ FD_CHECK_JMP, /* 0x06 */ FD_INVALID, /* 0x07 */ FD_VALID,
176 : /* 0x08 */ FD_INVALID, /* 0x09 */ FD_INVALID, /* 0x0a */ FD_INVALID, /* 0x0b */ FD_INVALID,
177 : /* 0x0c */ FD_VALID, /* 0x0d */ FD_INVALID, /* 0x0e */ FD_INVALID, /* 0x0f */ FD_VALID,
178 : /* 0x10 */ FD_INVALID, /* 0x11 */ FD_INVALID, /* 0x12 */ FD_INVALID, /* 0x13 */ FD_INVALID,
179 : /* 0x14 */ FD_VALID, /* 0x15 */ FD_CHECK_JMP, /* 0x16 */ FD_INVALID, /* 0x17 */ FD_VALID,
180 : /* 0x18 */ FD_CHECK_LDQ, /* 0x19 */ FD_INVALID, /* 0x1a */ FD_INVALID, /* 0x1b */ FD_INVALID,
181 : /* 0x1c */ FD_VALID, /* 0x1d */ FD_CHECK_JMP, /* 0x1e */ FD_INVALID, /* 0x1f */ FD_VALID,
182 : /* 0x20 */ FD_INVALID, /* 0x21 */ FD_INVALID, /* 0x22 */ FD_INVALID, /* 0x23 */ FD_INVALID,
183 : /* 0x24 */ FD_VALID, /* 0x25 */ FD_CHECK_JMP, /* 0x26 */ FD_INVALID, /* 0x27 */ FD_VALID,
184 : /* 0x28 */ FD_INVALID, /* 0x29 */ FD_INVALID, /* 0x2a */ FD_INVALID, /* 0x2b */ FD_INVALID,
185 : /* 0x2c */ FD_VALID, /* 0x2d */ FD_CHECK_JMP, /* 0x2e */ FD_INVALID, /* 0x2f */ FD_VALID,
186 : /* 0x30 */ FD_INVALID, /* 0x31 */ FD_INVALID, /* 0x32 */ FD_INVALID, /* 0x33 */ FD_INVALID,
187 : /* 0x34 */ FD_CHECK_DIV, /* 0x35 */ FD_CHECK_JMP, /* 0x36 */ FD_INVALID, /* 0x37 */ FD_CHECK_DIV,
188 : /* 0x38 */ FD_INVALID, /* 0x39 */ FD_INVALID, /* 0x3a */ FD_INVALID, /* 0x3b */ FD_INVALID,
189 : /* 0x3c */ FD_VALID, /* 0x3d */ FD_CHECK_JMP, /* 0x3e */ FD_INVALID, /* 0x3f */ FD_VALID,
190 : /* 0x40 */ FD_INVALID, /* 0x41 */ FD_INVALID, /* 0x42 */ FD_INVALID, /* 0x43 */ FD_INVALID,
191 : /* 0x44 */ FD_VALID, /* 0x45 */ FD_CHECK_JMP, /* 0x46 */ FD_INVALID, /* 0x47 */ FD_VALID,
192 : /* 0x48 */ FD_INVALID, /* 0x49 */ FD_INVALID, /* 0x4a */ FD_INVALID, /* 0x4b */ FD_INVALID,
193 : /* 0x4c */ FD_VALID, /* 0x4d */ FD_CHECK_JMP, /* 0x4e */ FD_INVALID, /* 0x4f */ FD_VALID,
194 : /* 0x50 */ FD_INVALID, /* 0x51 */ FD_INVALID, /* 0x52 */ FD_INVALID, /* 0x53 */ FD_INVALID,
195 : /* 0x54 */ FD_VALID, /* 0x55 */ FD_CHECK_JMP, /* 0x56 */ FD_INVALID, /* 0x57 */ FD_VALID,
196 : /* 0x58 */ FD_INVALID, /* 0x59 */ FD_INVALID, /* 0x5a */ FD_INVALID, /* 0x5b */ FD_INVALID,
197 : /* 0x5c */ FD_VALID, /* 0x5d */ FD_CHECK_JMP, /* 0x5e */ FD_INVALID, /* 0x5f */ FD_VALID,
198 : /* 0x60 */ FD_INVALID, /* 0x61 */ FD_VALID, /* 0x62 */ FD_CHECK_ST, /* 0x63 */ FD_CHECK_ST,
199 : /* 0x64 */ FD_CHECK_SH32, /* 0x65 */ FD_CHECK_JMP, /* 0x66 */ FD_INVALID, /* 0x67 */ FD_CHECK_SH64,
200 : /* 0x68 */ FD_INVALID, /* 0x69 */ FD_VALID, /* 0x6a */ FD_CHECK_ST, /* 0x6b */ FD_CHECK_ST,
201 : /* 0x6c */ FD_VALID, /* 0x6d */ FD_CHECK_JMP, /* 0x6e */ FD_INVALID, /* 0x6f */ FD_VALID,
202 : /* 0x70 */ FD_INVALID, /* 0x71 */ FD_VALID, /* 0x72 */ FD_CHECK_ST, /* 0x73 */ FD_CHECK_ST,
203 : /* 0x74 */ FD_CHECK_SH32, /* 0x75 */ FD_CHECK_JMP, /* 0x76 */ FD_INVALID, /* 0x77 */ FD_CHECK_SH64,
204 : /* 0x78 */ FD_INVALID, /* 0x79 */ FD_VALID, /* 0x7a */ FD_CHECK_ST, /* 0x7b */ FD_CHECK_ST,
205 : /* 0x7c */ FD_VALID, /* 0x7d */ FD_CHECK_JMP, /* 0x7e */ FD_INVALID, /* 0x7f */ FD_VALID,
206 : /* 0x80 */ FD_INVALID, /* 0x81 */ FD_INVALID, /* 0x82 */ FD_INVALID, /* 0x83 */ FD_INVALID,
207 : /* 0x84 */ FD_VALID, /* 0x85 */ FD_VALID, /* 0x86 */ FD_INVALID, /* 0x87 */ FD_VALID,
208 : /* 0x88 */ FD_INVALID, /* 0x89 */ FD_INVALID, /* 0x8a */ FD_INVALID, /* 0x8b */ FD_INVALID,
209 : /* 0x8c */ FD_INVALID, /* 0x8d */ FD_CHECK_CALLX,/* 0x8e */ FD_INVALID, /* 0x8f */ FD_INVALID,
210 : /* 0x90 */ FD_INVALID, /* 0x91 */ FD_INVALID, /* 0x92 */ FD_INVALID, /* 0x93 */ FD_INVALID,
211 : /* 0x94 */ FD_CHECK_DIV, /* 0x95 */ FD_VALID, /* 0x96 */ FD_INVALID, /* 0x97 */ FD_CHECK_DIV,
212 : /* 0x98 */ FD_INVALID, /* 0x99 */ FD_INVALID, /* 0x9a */ FD_INVALID, /* 0x9b */ FD_INVALID,
213 : /* 0x9c */ FD_VALID, /* 0x9d */ FD_INVALID, /* 0x9e */ FD_INVALID, /* 0x9f */ FD_VALID,
214 : /* 0xa0 */ FD_INVALID, /* 0xa1 */ FD_INVALID, /* 0xa2 */ FD_INVALID, /* 0xa3 */ FD_INVALID,
215 : /* 0xa4 */ FD_VALID, /* 0xa5 */ FD_CHECK_JMP, /* 0xa6 */ FD_INVALID, /* 0xa7 */ FD_VALID,
216 : /* 0xa8 */ FD_INVALID, /* 0xa9 */ FD_INVALID, /* 0xaa */ FD_INVALID, /* 0xab */ FD_INVALID,
217 : /* 0xac */ FD_VALID, /* 0xad */ FD_CHECK_JMP, /* 0xae */ FD_INVALID, /* 0xaf */ FD_VALID,
218 : /* 0xb0 */ FD_INVALID, /* 0xb1 */ FD_INVALID, /* 0xb2 */ FD_INVALID, /* 0xb3 */ FD_INVALID,
219 : /* 0xb4 */ FD_VALID, /* 0xb5 */ FD_CHECK_JMP, /* 0xb6 */ FD_INVALID, /* 0xb7 */ FD_VALID,
220 : /* 0xb8 */ FD_INVALID, /* 0xb9 */ FD_INVALID, /* 0xba */ FD_INVALID, /* 0xbb */ FD_INVALID,
221 : /* 0xbc */ FD_VALID, /* 0xbd */ FD_CHECK_JMP, /* 0xbe */ FD_INVALID, /* 0xbf */ FD_VALID,
222 : /* 0xc0 */ FD_INVALID, /* 0xc1 */ FD_INVALID, /* 0xc2 */ FD_INVALID, /* 0xc3 */ FD_INVALID,
223 : /* 0xc4 */ FD_CHECK_SH32, /* 0xc5 */ FD_CHECK_JMP, /* 0xc6 */ FD_INVALID, /* 0xc7 */ FD_CHECK_SH64,
224 : /* 0xc8 */ FD_INVALID, /* 0xc9 */ FD_INVALID, /* 0xca */ FD_INVALID, /* 0xcb */ FD_INVALID,
225 : /* 0xcc */ FD_VALID, /* 0xcd */ FD_CHECK_JMP, /* 0xce */ FD_INVALID, /* 0xcf */ FD_VALID,
226 : /* 0xd0 */ FD_INVALID, /* 0xd1 */ FD_INVALID, /* 0xd2 */ FD_INVALID, /* 0xd3 */ FD_INVALID,
227 : /* 0xd4 */ FD_CHECK_END, /* 0xd5 */ FD_CHECK_JMP, /* 0xd6 */ FD_INVALID, /* 0xd7 */ FD_INVALID,
228 : /* 0xd8 */ FD_INVALID, /* 0xd9 */ FD_INVALID, /* 0xda */ FD_INVALID, /* 0xdb */ FD_INVALID,
229 : /* 0xdc */ FD_CHECK_END, /* 0xdd */ FD_CHECK_JMP, /* 0xde */ FD_INVALID, /* 0xdf */ FD_INVALID,
230 : /* 0xe0 */ FD_INVALID, /* 0xe1 */ FD_INVALID, /* 0xe2 */ FD_INVALID, /* 0xe3 */ FD_INVALID,
231 : /* 0xe4 */ FD_INVALID, /* 0xe5 */ FD_INVALID, /* 0xe6 */ FD_INVALID, /* 0xe7 */ FD_INVALID,
232 : /* 0xe8 */ FD_INVALID, /* 0xe9 */ FD_INVALID, /* 0xea */ FD_INVALID, /* 0xeb */ FD_INVALID,
233 : /* 0xec */ FD_INVALID, /* 0xed */ FD_INVALID, /* 0xee */ FD_INVALID, /* 0xef */ FD_INVALID,
234 : /* 0xf0 */ FD_INVALID, /* 0xf1 */ FD_INVALID, /* 0xf2 */ FD_INVALID, /* 0xf3 */ FD_INVALID,
235 : /* 0xf4 */ FD_INVALID, /* 0xf5 */ FD_INVALID, /* 0xf6 */ FD_INVALID, /* 0xf7 */ FD_INVALID,
236 : /* 0xf8 */ FD_INVALID, /* 0xf9 */ FD_INVALID, /* 0xfa */ FD_INVALID, /* 0xfb */ FD_INVALID,
237 : /* 0xfc */ FD_INVALID, /* 0xfd */ FD_INVALID, /* 0xfe */ FD_INVALID, /* 0xff */ FD_INVALID,
238 6651 : };
239 :
240 : /* FIXME: These checks are not necessary assuming fd_vm_t is populated by metadata
241 : generated in fd_sbpf_elf_peek (which performs these checks). But there is no guarantee, and
242 : this non-guarantee is (rightfully) exploited by the fuzz harnesses.
243 : Agave doesn't perform these checks explicitly due to Rust's guarantees */
244 6651 : if( FD_UNLIKELY( vm->text_sz / 8UL != vm->text_cnt ||
245 6651 : (const uchar *)vm->text < vm->rodata ||
246 6651 : (ulong)vm->text > (ulong)vm->text + vm->text_sz || /* Overflow chk */
247 6651 : (const uchar *)vm->text + vm->text_sz > vm->rodata + vm->rodata_sz ) )
248 162 : return FD_VM_ERR_BAD_TEXT;
249 :
250 6489 : if( FD_UNLIKELY( !fd_ulong_is_aligned( vm->text_sz, 8UL ) ) ) /* https://github.com/solana-labs/rbpf/blob/v0.8.0/src/verifier.rs#L109 */
251 3 : return FD_VM_TEXT_SZ_UNALIGNED;
252 :
253 6486 : if ( FD_UNLIKELY( vm->text_cnt == 0UL ) ) /* https://github.com/solana-labs/rbpf/blob/v0.8.0/src/verifier.rs#L112 */
254 45 : return FD_VM_ERR_EMPTY;
255 :
256 : /* FIXME: CLEAN UP LONG / ULONG TYPE CONVERSION */
257 6441 : ulong const * text = vm->text;
258 6441 : ulong text_cnt = vm->text_cnt;
259 849054258 : for( ulong i=0UL; i<text_cnt; i++ ) {
260 849049566 : fd_sbpf_instr_t instr = fd_sbpf_instr( text[i] );
261 :
262 849049566 : uchar validation_code = validation_map[ instr.opcode.raw ];
263 849049566 : switch( validation_code ) {
264 :
265 673297611 : case FD_VALID: break;
266 :
267 5098122 : case FD_CHECK_JMP: {
268 5098122 : long jmp_dst = (long)i + (long)instr.offset + 1L;
269 5098122 : if( FD_UNLIKELY( (jmp_dst<0) | (jmp_dst>=(long)text_cnt) ) ) return FD_VM_ERR_JMP_OUT_OF_BOUNDS;
270 5097969 : if( FD_UNLIKELY( fd_sbpf_instr( text[ jmp_dst ] ).opcode.raw==FD_SBPF_OP_ADDL_IMM ) ) return FD_VM_ERR_JMP_TO_ADDL_IMM;
271 5097846 : break;
272 5097969 : }
273 :
274 5097846 : case FD_CHECK_END: {
275 1503 : if( FD_UNLIKELY( !((instr.imm==16) | (instr.imm==32) | (instr.imm==64)) ) ) return FD_VM_ERR_INVALID_END_IMM;
276 1479 : break;
277 1503 : }
278 :
279 7037010 : case FD_CHECK_ST: break; /* FIXME: HMMM ... */
280 :
281 : /* https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/verifier.rs#L244 */
282 1348605 : case FD_CHECK_LDQ: {
283 : /* https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/verifier.rs#L131 */
284 1348605 : if( FD_UNLIKELY( (i+1UL)>=text_cnt ) ) return FD_VM_ERR_INCOMPLETE_LDQ;
285 :
286 : /* https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/verifier.rs#L137-L139 */
287 1348602 : fd_sbpf_instr_t addl_imm = fd_sbpf_instr( text[ i+1UL ] );
288 1348602 : if( FD_UNLIKELY( addl_imm.opcode.raw!=FD_SBPF_OP_ADDL_IMM ) ) return FD_VM_ERR_LDQ_NO_ADDL_IMM;
289 :
290 : /* FIXME: SET A BIT MAP HERE OF ADDL_IMM TO DENOTE * AS FORBIDDEN
291 : BRANCH TARGETS OF CALL_REG?? */
292 :
293 1348599 : i++; /* Skip the addl imm */
294 1348599 : break;
295 1348602 : }
296 :
297 64446558 : case FD_CHECK_DIV: {
298 64446558 : if( FD_UNLIKELY( instr.imm==0 ) ) return FD_VM_ERR_SIGFPE; /* FIXME: SIGILL? */
299 64446537 : break;
300 64446558 : }
301 :
302 64446537 : case FD_CHECK_SH32: {
303 48308070 : if( FD_UNLIKELY( instr.imm>=32 ) ) return FD_VM_SH_OVERFLOW;
304 48308046 : break;
305 48308070 : }
306 :
307 49394046 : case FD_CHECK_SH64: {
308 49394046 : if( FD_UNLIKELY( instr.imm>=64 ) ) return FD_VM_SH_OVERFLOW;
309 49394010 : break;
310 49394046 : }
311 :
312 49394010 : case FD_CHECK_CALLX: {
313 : /* The register number to read is stored in the immediate.
314 : https://github.com/solana-labs/rbpf/blob/v0.8.1/src/verifier.rs#L218 */
315 117177 : if( FD_UNLIKELY( instr.imm > 9 ) ) {
316 12 : return FD_VM_ERR_INVALID_REG;
317 12 : }
318 117165 : break;
319 117177 : }
320 :
321 117165 : case FD_INVALID: default: return FD_VM_ERR_INVALID_OPCODE;
322 849049566 : }
323 :
324 849048303 : if( FD_UNLIKELY( instr.src_reg>10 ) ) return FD_VM_ERR_INVALID_SRC_REG; /* FIXME: MAGIC NUMBER */
325 :
326 849048105 : int is_invalid_dst_reg = instr.dst_reg > ((validation_code == FD_CHECK_ST) ? 10 : 9); /* FIXME: MAGIC NUMBER */
327 849048105 : if( FD_UNLIKELY( is_invalid_dst_reg ) ) return FD_VM_ERR_INVALID_DST_REG;
328 849048105 : }
329 :
330 4692 : return FD_VM_SUCCESS;
331 6441 : }
332 :
333 : FD_FN_CONST ulong
334 12075 : fd_vm_align( void ) {
335 12075 : return FD_VM_ALIGN;
336 12075 : }
337 :
338 : FD_FN_CONST ulong
339 12075 : fd_vm_footprint( void ) {
340 12075 : return FD_VM_FOOTPRINT;
341 12075 : }
342 :
343 : void *
344 15348 : fd_vm_new( void * shmem ) {
345 :
346 15348 : if( FD_UNLIKELY( !shmem ) ) {
347 0 : FD_LOG_WARNING(( "NULL shmem" ));
348 0 : return NULL;
349 0 : }
350 :
351 15348 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_vm_align() ) ) ) {
352 0 : FD_LOG_WARNING(( "misaligned shmem" ));
353 0 : return NULL;
354 0 : }
355 :
356 15348 : fd_vm_t * vm = (fd_vm_t *)shmem;
357 15348 : fd_memset( vm, 0, fd_vm_footprint() );
358 :
359 15348 : FD_COMPILER_MFENCE();
360 15348 : FD_VOLATILE( vm->magic ) = FD_VM_MAGIC;
361 15348 : FD_COMPILER_MFENCE();
362 :
363 15348 : return shmem;
364 15348 : }
365 :
366 : fd_vm_t *
367 15348 : fd_vm_join( void * shmem ) {
368 :
369 15348 : if( FD_UNLIKELY( !shmem ) ) {
370 0 : FD_LOG_WARNING(( "NULL shmem" ));
371 0 : return NULL;
372 0 : }
373 :
374 15348 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_vm_align() ) ) ) {
375 0 : FD_LOG_WARNING(( "misaligned shmem" ));
376 0 : return NULL;
377 0 : }
378 :
379 15348 : fd_vm_t * vm = (fd_vm_t *)shmem;
380 :
381 15348 : if( FD_UNLIKELY( vm->magic!=FD_VM_MAGIC ) ) {
382 0 : FD_LOG_WARNING(( "bad magic" ));
383 0 : return NULL;
384 0 : }
385 :
386 15348 : return vm;
387 15348 : }
388 :
389 : void *
390 867 : fd_vm_leave( fd_vm_t * vm ) {
391 :
392 867 : if( FD_UNLIKELY( !vm ) ) {
393 0 : FD_LOG_WARNING(( "NULL vm" ));
394 0 : return NULL;
395 0 : }
396 :
397 867 : return (void *)vm;
398 867 : }
399 :
400 : void *
401 867 : fd_vm_delete( void * shmem ) {
402 :
403 867 : if( FD_UNLIKELY( !shmem ) ) {
404 0 : FD_LOG_WARNING(( "NULL shmem" ));
405 0 : return NULL;
406 0 : }
407 :
408 867 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_vm_align() ) ) ) {
409 0 : FD_LOG_WARNING(( "misaligned shmem" ));
410 0 : return NULL;
411 0 : }
412 :
413 867 : fd_vm_t * vm = (fd_vm_t *)shmem;
414 :
415 867 : if( FD_UNLIKELY( vm->magic!=FD_VM_MAGIC ) ) {
416 0 : FD_LOG_WARNING(( "bad magic" ));
417 0 : return NULL;
418 0 : }
419 :
420 867 : FD_COMPILER_MFENCE();
421 867 : FD_VOLATILE( vm->magic ) = 0UL;
422 867 : FD_COMPILER_MFENCE();
423 :
424 867 : return (void *)vm;
425 867 : }
426 :
427 : fd_vm_t *
428 : fd_vm_init(
429 : fd_vm_t * vm,
430 : fd_exec_instr_ctx_t *instr_ctx,
431 : ulong heap_max,
432 : ulong entry_cu,
433 : uchar const * rodata,
434 : ulong rodata_sz,
435 : ulong const * text,
436 : ulong text_cnt,
437 : ulong text_off,
438 : ulong text_sz,
439 : ulong entry_pc,
440 : ulong * calldests,
441 : fd_sbpf_syscalls_t * syscalls,
442 : fd_vm_trace_t * trace,
443 : fd_sha256_t * sha,
444 : fd_vm_input_region_t * mem_regions,
445 : uint mem_regions_cnt,
446 : fd_vm_acc_region_meta_t * acc_region_metas,
447 : uchar is_deprecated,
448 18885 : int direct_mapping ) {
449 :
450 18885 : if ( FD_UNLIKELY( vm == NULL ) ) {
451 0 : FD_LOG_WARNING(( "NULL vm" ));
452 0 : return NULL;
453 0 : }
454 :
455 18885 : if ( FD_UNLIKELY( vm->magic != FD_VM_MAGIC ) ) {
456 0 : FD_LOG_WARNING(( "bad magic" ));
457 0 : return NULL;
458 0 : }
459 :
460 18885 : if ( FD_UNLIKELY( instr_ctx == NULL ) ) {
461 0 : FD_LOG_WARNING(( "NULL instr_ctx" ));
462 0 : return NULL;
463 0 : }
464 :
465 18885 : if ( FD_UNLIKELY( heap_max > FD_VM_HEAP_MAX ) ) {
466 0 : FD_LOG_WARNING(( "heap_max > FD_VM_HEAP_MAX" ));
467 0 : return NULL;
468 0 : }
469 :
470 : // Set the vm fields
471 18885 : vm->instr_ctx = instr_ctx;
472 18885 : vm->heap_max = heap_max;
473 18885 : vm->entry_cu = entry_cu;
474 18885 : vm->rodata = rodata;
475 18885 : vm->rodata_sz = rodata_sz;
476 18885 : vm->text = text;
477 18885 : vm->text_cnt = text_cnt;
478 18885 : vm->text_off = text_off;
479 18885 : vm->text_sz = text_sz;
480 18885 : vm->entry_pc = entry_pc;
481 18885 : vm->calldests = calldests;
482 18885 : vm->syscalls = syscalls;
483 18885 : vm->trace = trace;
484 18885 : vm->sha = sha;
485 18885 : vm->input_mem_regions = mem_regions;
486 18885 : vm->input_mem_regions_cnt = mem_regions_cnt;
487 18885 : vm->acc_region_metas = acc_region_metas;
488 18885 : vm->is_deprecated = is_deprecated;
489 18885 : vm->direct_mapping = direct_mapping;
490 18885 : vm->stack_frame_size = FD_VM_STACK_FRAME_SZ + ( direct_mapping ? 0UL : FD_VM_STACK_GUARD_SZ );
491 18885 : vm->segv_store_vaddr = ULONG_MAX;
492 :
493 : /* Unpack the configuration */
494 18885 : int err = fd_vm_setup_state_for_execution( vm );
495 18885 : if( FD_UNLIKELY( err != FD_VM_SUCCESS ) ) {
496 0 : return NULL;
497 0 : }
498 :
499 18885 : return vm;
500 18885 : }
501 :
502 : int
503 30096 : fd_vm_setup_state_for_execution( fd_vm_t * vm ) {
504 :
505 30096 : if ( FD_UNLIKELY( !vm ) ) {
506 0 : FD_LOG_WARNING(( "NULL vm" ));
507 0 : return FD_VM_ERR_INVAL;
508 0 : }
509 :
510 : /* Unpack input and rodata */
511 30096 : fd_vm_mem_cfg( vm );
512 :
513 : /* Initialize registers */
514 : /* FIXME: Zero out shadow, stack and heap here? */
515 30096 : fd_memset( vm->reg, 0, FD_VM_REG_MAX * sizeof(ulong) );
516 30096 : vm->reg[ 1] = FD_VM_MEM_MAP_INPUT_REGION_START;
517 30096 : vm->reg[10] = FD_VM_MEM_MAP_STACK_REGION_START + 0x1000;
518 :
519 : /* Set execution state */
520 30096 : vm->pc = vm->entry_pc;
521 30096 : vm->ic = 0UL;
522 30096 : vm->cu = vm->entry_cu;
523 30096 : vm->frame_cnt = 0UL;
524 :
525 30096 : vm->heap_sz = 0UL;
526 :
527 : /* Do NOT reset logs */
528 :
529 30096 : return FD_VM_SUCCESS;
530 30096 : }
|