Line data Source code
1 : #include "fd_bpf_loader_serialization.h"
2 : #include "../fd_account.h"
3 : #include "../fd_runtime.h"
4 :
5 : /* As a general note, copy_account_data implies that direct mapping is not being
6 : used/is inactive. This file is responsible for serializing and deserializing
7 : the input region of the BPF virtual machine. The input region contains
8 : instruction information, account metadata, and account data. The high level
9 : format is as follows:
10 :
11 : [ account 1 metadata, account 1 data, account 2 metadata, account 2 data, ...,
12 : account N metadata, account N data, instruction info. ]
13 :
14 : This format by no means comprehensive, but it should give an idea of how
15 : the input region is laid out. When direct mapping is not enabled, the input
16 : region is stored as a single contiguous buffer. This buffer in the host
17 : address space is then mapped to the VM virtual address space (the range
18 : starting with 0x400...). This means to serialize into the input region, we
19 : need to copy in the account metadata and account data into the buffer for
20 : each account. Everything must get copied out after execution is complete.
21 : A consequence of this is that a memcpy for the account data is required
22 : for each serialize and deserialize operation: this can potentially become
23 : expensive if there are many accounts and many nested CPI calls. Also, the
24 : entire memory region is treated as writable even though many accounts are
25 : read-only. This means that for all read-only accounts, a memcmp must be done
26 : while deserializing to make sure that the account (meta)data has not changed.
27 :
28 : Direct mapping offers a solution to this by introducing a more sophisticated
29 : memory translation protocol. Now the account data is not copied into a single
30 : contiguous buffer, but instead a borrowed account's data is directly mapped
31 : into the VM's virtual address space. The host memory for the input region is
32 : now represented by a list of fragmented memory regions. These sub regions
33 : also have different write permissions. This should solve the problem of
34 : having to memcpy/memcmp account data regions (which can be up to 10MiB each).
35 : There is some nuance to this, as the account data can be resized. This means
36 : that memcpys for account data regions can't totally be avoided. */
37 :
38 : /* Add a new memory region to represent the input region. All of the memory
39 : regions here have sorted virtual addresses. These regions may or may not
40 : correspond to an account's data region. If it corresponds to metadata,
41 : the pubkey for the region will be NULL. */
42 : static void
43 : new_input_mem_region( fd_vm_input_region_t * input_mem_regions,
44 : uint * input_mem_regions_cnt,
45 : const uchar * buffer,
46 : ulong region_sz,
47 2010 : uint is_writable ) {
48 :
49 : /* The start vaddr of the new region should be equal to start of the previous
50 : region added to its size. */
51 2010 : ulong vaddr_offset = *input_mem_regions_cnt==0UL ? 0UL : input_mem_regions[ *input_mem_regions_cnt-1U ].vaddr_offset +
52 981 : input_mem_regions[ *input_mem_regions_cnt-1U ].region_sz;
53 2010 : input_mem_regions[ *input_mem_regions_cnt ].is_writable = is_writable;
54 2010 : input_mem_regions[ *input_mem_regions_cnt ].haddr = (ulong)buffer;
55 2010 : input_mem_regions[ *input_mem_regions_cnt ].region_sz = (uint)region_sz;
56 2010 : input_mem_regions[ *input_mem_regions_cnt ].vaddr_offset = vaddr_offset;
57 2010 : (*input_mem_regions_cnt)++;
58 2010 : }
59 :
60 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L93-L130 */
61 : /* This function handles casing for direct mapping being enabled as well as if
62 : the alignment is being stored. In the case where direct mapping is not
63 : enabled, we copy in the account data and a 10KiB buffer into the input region.
64 : These both go into the same memory buffer. However, when direct mapping is
65 : enabled, the account data and resizing buffers are represented by two
66 : different memory regions. In both cases, padding is used to maintain 8 byte
67 : alignment. If alignment is not required, then a resizing buffer is not used
68 : as the deprecated loader doesn't allow for resizing accounts. */
69 : static void
70 : write_account( fd_exec_instr_ctx_t * instr_ctx,
71 : fd_borrowed_account_t * account,
72 : uchar instr_acc_idx,
73 : uchar * * serialized_params,
74 : uchar * * serialized_params_start,
75 : fd_vm_input_region_t * input_mem_regions,
76 : uint * input_mem_regions_cnt,
77 : fd_vm_acc_region_meta_t * acc_region_metas,
78 : int is_aligned,
79 3000 : int copy_account_data ) {
80 :
81 3000 : uchar const * data = account ? account->const_data : NULL;
82 3000 : ulong dlen = account ? account->const_meta->dlen : 0UL;
83 :
84 3000 : if( copy_account_data ) {
85 : /* Copy the account data into input region buffer */
86 2646 : fd_memcpy( *serialized_params, data, dlen );
87 2646 : *serialized_params += dlen;
88 :
89 2646 : if( FD_LIKELY( is_aligned ) ) {
90 : /* Zero out padding bytes and max permitted data increase */
91 2457 : ulong align_offset = fd_ulong_align_up( dlen, FD_BPF_ALIGN_OF_U128 ) - dlen;
92 2457 : fd_memset( *serialized_params, 0, MAX_PERMITTED_DATA_INCREASE + align_offset );
93 2457 : *serialized_params += MAX_PERMITTED_DATA_INCREASE + align_offset;
94 2457 : }
95 2646 : } else { /* direct_mapping == true */
96 : /* First, push on the region for the metadata that has just been serialized.
97 : This function will push the metadata in the serialized_params from
98 : serialized_params_start to serialized_params as a region to the input
99 : memory regions array. */
100 : /* TODO: This region always has length of 96 and this can be set as a constant. */
101 :
102 354 : ulong region_sz = (ulong)(*serialized_params) - (ulong)(*serialized_params_start);
103 354 : new_input_mem_region( input_mem_regions, input_mem_regions_cnt, *serialized_params_start, region_sz, 1L );
104 :
105 : /* Next, push the region for the account data if there is account data. We
106 : intentionally omit copy on write as a region type. */
107 354 : int err = 0;
108 354 : uint is_writable = (uint)(fd_account_can_data_be_changed( instr_ctx, instr_acc_idx, &err ) && !err);
109 :
110 : /* Update the mapping from instruction account index to memory region index.
111 : This is an optimization to avoid redundant lookups to find accounts. */
112 354 : acc_region_metas[instr_acc_idx].region_idx = *input_mem_regions_cnt;
113 354 : acc_region_metas[instr_acc_idx].has_data_region = !!dlen;
114 354 : acc_region_metas[instr_acc_idx].has_resizing_region = (uchar)is_aligned;
115 :
116 354 : if( dlen ) {
117 321 : new_input_mem_region( input_mem_regions, input_mem_regions_cnt, data, dlen, is_writable );
118 321 : }
119 :
120 354 : if( FD_LIKELY( is_aligned ) ) {
121 : /* Finally, push a third region for the max resizing data region. This is
122 : done even if there is no account data. This must be aligned so padding
123 : bytes must be inserted. This resizing region is also padded to result
124 : in 8 byte alignment for the combination of the account data region with
125 : the resizing region.
126 :
127 : We add the max permitted resizing limit along with 8 bytes of padding
128 : to the serialization buffer. However, the padding bytes are used to
129 : maintain alignment in the VM virtual address space. */
130 306 : ulong align_offset = fd_ulong_align_up( dlen, FD_BPF_ALIGN_OF_U128 ) - dlen;
131 :
132 306 : fd_memset( *serialized_params, 0, MAX_PERMITTED_DATA_INCREASE + FD_BPF_ALIGN_OF_U128 );
133 :
134 : /* Leave a gap for alignment */
135 306 : uchar * region_buffer = *serialized_params + (FD_BPF_ALIGN_OF_U128 - align_offset);
136 306 : ulong region_sz = MAX_PERMITTED_DATA_INCREASE + align_offset;
137 306 : new_input_mem_region( input_mem_regions, input_mem_regions_cnt, region_buffer, region_sz, is_writable );
138 :
139 306 : *serialized_params += MAX_PERMITTED_DATA_INCREASE + FD_BPF_ALIGN_OF_U128;
140 306 : }
141 354 : *serialized_params_start = *serialized_params;
142 354 : }
143 3000 : }
144 :
145 : uchar *
146 : fd_bpf_loader_input_serialize_aligned( fd_exec_instr_ctx_t ctx,
147 : ulong * sz,
148 : ulong * pre_lens,
149 : fd_vm_input_region_t * input_mem_regions,
150 : uint * input_mem_regions_cnt,
151 : fd_vm_acc_region_meta_t * acc_region_metas,
152 810 : int copy_account_data ) {
153 810 : uchar const * instr_acc_idxs = ctx.instr->acct_txn_idxs;
154 810 : fd_pubkey_t * txn_accs = ctx.txn_ctx->accounts;
155 :
156 810 : uchar acc_idx_seen[256] = {0};
157 810 : ushort dup_acc_idx[256] = {0};
158 :
159 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L429-L459 */
160 810 : ulong serialized_size = 0UL;
161 810 : serialized_size += sizeof(ulong); // acct_cnt
162 : /* First pass is to calculate size of buffer to allocate */
163 3852 : for( ushort i=0; i<ctx.instr->acct_cnt; i++ ) {
164 3042 : uchar acc_idx = instr_acc_idxs[i];
165 :
166 3042 : serialized_size++; // dup byte
167 3042 : if( FD_UNLIKELY( acc_idx_seen[acc_idx] ) ) {
168 279 : serialized_size += 7UL; // pad to 64-bit alignment
169 2763 : } else {
170 2763 : acc_idx_seen[acc_idx] = 1;
171 2763 : dup_acc_idx[acc_idx] = i;
172 :
173 2763 : fd_borrowed_account_t * view_acc = ctx.instr->borrowed_accounts[i];
174 2763 : ulong acc_data_len = view_acc->const_meta->dlen;
175 :
176 2763 : serialized_size += sizeof(uchar) // is_signer
177 2763 : + sizeof(uchar) // is_writable
178 2763 : + sizeof(uchar) // executable
179 2763 : + sizeof(uint) // original_data_len
180 2763 : + sizeof(fd_pubkey_t) // key
181 2763 : + sizeof(fd_pubkey_t) // owner
182 2763 : + sizeof(ulong) // lamports
183 2763 : + sizeof(ulong) // data len
184 2763 : + MAX_PERMITTED_DATA_INCREASE
185 2763 : + sizeof(ulong); // rent_epoch
186 2763 : if( copy_account_data ) {
187 2457 : serialized_size += fd_ulong_align_up( acc_data_len, FD_BPF_ALIGN_OF_U128 );
188 2457 : } else {
189 306 : serialized_size += FD_BPF_ALIGN_OF_U128;
190 306 : }
191 2763 : }
192 3042 : }
193 :
194 810 : serialized_size += sizeof(ulong) // data len
195 810 : + ctx.instr->data_sz
196 810 : + sizeof(fd_pubkey_t); // program id
197 :
198 810 : uchar * serialized_params = fd_spad_alloc( ctx.txn_ctx->spad, FD_BPF_ALIGN_OF_U128, fd_ulong_align_up( serialized_size, FD_RUNTIME_INPUT_REGION_ALLOC_ALIGN_UP ) );
199 810 : uchar * serialized_params_start = serialized_params;
200 810 : uchar * curr_serialized_params_start = serialized_params;
201 :
202 810 : FD_STORE( ulong, serialized_params, ctx.instr->acct_cnt );
203 810 : serialized_params += sizeof(ulong);
204 :
205 : /* Second pass over the account is to serialize into the buffer. */
206 3852 : for( ushort i=0; i<ctx.instr->acct_cnt; i++ ) {
207 3042 : uchar acc_idx = instr_acc_idxs[i];
208 3042 : fd_pubkey_t * acc = &txn_accs[acc_idx];
209 :
210 3042 : if( FD_UNLIKELY( acc_idx_seen[acc_idx] && dup_acc_idx[acc_idx] != i ) ) {
211 : /* Duplicate. Store 8 byte buffer to maintain alignment but store the
212 : account index in the first byte.*/
213 279 : FD_STORE( ulong, serialized_params, 0UL );
214 279 : FD_STORE( uchar, serialized_params, (uchar)dup_acc_idx[acc_idx] );
215 279 : serialized_params += sizeof(ulong);
216 2763 : } else {
217 : /* Calculate and store the start of the actual metadata region for this account,
218 : excluding any duplicate account markers at the beginning.
219 :
220 : We use this later for retrieving the serialized values later in the CPI security checks. */
221 2763 : ulong metadata_region_offset_with_dups = *input_mem_regions_cnt==0UL ? 0UL :
222 2763 : input_mem_regions[ *input_mem_regions_cnt-1U ].vaddr_offset +
223 150 : input_mem_regions[ *input_mem_regions_cnt-1U ].region_sz;
224 :
225 2763 : acc_region_metas[i].metadata_region_offset = metadata_region_offset_with_dups +
226 2763 : (ulong)(serialized_params - curr_serialized_params_start);
227 :
228 2763 : FD_STORE( uchar, serialized_params, FD_NON_DUP_MARKER );
229 2763 : serialized_params += sizeof(uchar);
230 :
231 2763 : fd_borrowed_account_t * view_acc = ctx.instr->borrowed_accounts[i];
232 :
233 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L465 */
234 2763 : fd_account_meta_t const * metadata = view_acc->const_meta;
235 :
236 2763 : uchar is_signer = (uchar)fd_instr_acc_is_signer_idx( ctx.instr, (uchar)i );
237 2763 : FD_STORE( uchar, serialized_params, is_signer );
238 2763 : serialized_params += sizeof(uchar);
239 :
240 2763 : uchar is_writable = (uchar)fd_instr_acc_is_writable_idx( ctx.instr, (uchar)i );
241 2763 : FD_STORE( uchar, serialized_params, is_writable );
242 2763 : serialized_params += sizeof(uchar);
243 :
244 2763 : uchar is_executable = (uchar)metadata->info.executable;
245 2763 : FD_STORE( uchar, serialized_params, is_executable );
246 2763 : serialized_params += sizeof(uchar);
247 :
248 : /* The original data len field is intentionally NOT populated. */
249 2763 : uint padding_0 = 0U;
250 2763 : FD_STORE( uint, serialized_params, padding_0 );
251 2763 : serialized_params += sizeof(uint);
252 :
253 2763 : fd_pubkey_t key = *acc;
254 2763 : FD_STORE( fd_pubkey_t, serialized_params, key );
255 2763 : serialized_params += sizeof(fd_pubkey_t);
256 :
257 2763 : fd_pubkey_t owner = *(fd_pubkey_t *)&metadata->info.owner;
258 2763 : FD_STORE( fd_pubkey_t, serialized_params, owner );
259 2763 : serialized_params += sizeof(fd_pubkey_t);
260 :
261 2763 : ulong lamports = metadata->info.lamports;
262 2763 : FD_STORE( ulong, serialized_params, lamports );
263 2763 : serialized_params += sizeof(ulong);
264 :
265 2763 : ulong acc_data_len = metadata->dlen;
266 2763 : pre_lens[i] = acc_data_len;
267 :
268 2763 : ulong data_len = acc_data_len;
269 2763 : FD_STORE( ulong, serialized_params, data_len );
270 2763 : serialized_params += sizeof(ulong);
271 :
272 2763 : write_account( &ctx, view_acc, (uchar)i, &serialized_params, &curr_serialized_params_start,
273 2763 : input_mem_regions, input_mem_regions_cnt, acc_region_metas, 1, copy_account_data );
274 :
275 2763 : ulong rent_epoch = metadata->info.rent_epoch;
276 2763 : FD_STORE( ulong, serialized_params, rent_epoch );
277 2763 : serialized_params += sizeof(ulong);
278 2763 : }
279 :
280 3042 : }
281 :
282 810 : ulong instr_data_len = ctx.instr->data_sz;
283 810 : FD_STORE( ulong, serialized_params, instr_data_len );
284 810 : serialized_params += sizeof(ulong);
285 :
286 810 : uchar * instr_data = ctx.instr->data;
287 810 : fd_memcpy( serialized_params, instr_data, instr_data_len );
288 810 : serialized_params += instr_data_len;
289 :
290 810 : FD_STORE( fd_pubkey_t, serialized_params, txn_accs[ctx.instr->program_id] );
291 810 : serialized_params += sizeof(fd_pubkey_t);
292 :
293 810 : if( FD_UNLIKELY( serialized_params!=serialized_params_start+serialized_size ) ) {
294 0 : FD_LOG_ERR(( "Serializing error" )); /* TODO: we can likely get rid of this check altogether */
295 0 : }
296 :
297 : /* Write out the final region. */
298 810 : new_input_mem_region( input_mem_regions, input_mem_regions_cnt, curr_serialized_params_start,
299 810 : (ulong)(serialized_params - curr_serialized_params_start), 1 );
300 :
301 810 : *sz = serialized_size;
302 :
303 810 : return serialized_params_start;
304 810 : }
305 :
306 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L500-L603 */
307 : int
308 : fd_bpf_loader_input_deserialize_aligned( fd_exec_instr_ctx_t ctx,
309 : ulong const * pre_lens,
310 : uchar * buffer,
311 : ulong FD_FN_UNUSED buffer_sz,
312 378 : int copy_account_data ) {
313 : /* TODO: An optimization would be to skip ahead through non-writable accounts */
314 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L507 */
315 378 : ulong start = 0UL;
316 :
317 378 : uchar acc_idx_seen[256] = {0};
318 :
319 378 : uchar const * instr_acc_idxs = ctx.instr->acct_txn_idxs;
320 :
321 378 : start += sizeof(ulong); // number of accounts
322 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L508-L600 */
323 1758 : for( ulong i=0UL; i<ctx.instr->acct_cnt; i++ ) {
324 1383 : uchar acc_idx = instr_acc_idxs[i];
325 :
326 1383 : start++; // position
327 1383 : fd_borrowed_account_t * view_acc = ctx.instr->borrowed_accounts[i];
328 1383 : if( FD_UNLIKELY( acc_idx_seen[acc_idx] ) ) {
329 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L515-517 */
330 9 : start += 7UL;
331 1374 : } else {
332 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L518-524 */
333 1374 : acc_idx_seen[acc_idx] = 1;
334 1374 : start += sizeof(uchar) // is_signer
335 1374 : + sizeof(uchar) // is_writable
336 1374 : + sizeof(uchar) // executable
337 1374 : + sizeof(uint) // original_data_len
338 1374 : + sizeof(fd_pubkey_t); // key
339 :
340 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L525-548 */
341 :
342 1374 : fd_pubkey_t * owner = (fd_pubkey_t *)(buffer+start);
343 1374 : start += sizeof(fd_pubkey_t); // owner
344 :
345 1374 : ulong lamports = FD_LOAD( ulong, buffer+start );
346 1374 : if( lamports!=view_acc->const_meta->info.lamports ) {
347 48 : int err = fd_account_set_lamports( &ctx, i, lamports );
348 48 : if( FD_UNLIKELY( err ) ) {
349 0 : return err;
350 0 : }
351 48 : }
352 1374 : start += sizeof(ulong); // lamports
353 :
354 1374 : ulong post_len = FD_LOAD( ulong, buffer+start );
355 1374 : start += sizeof(ulong); // data length
356 :
357 1374 : ulong pre_len = pre_lens[i];
358 1374 : ulong alignment_offset = fd_ulong_align_up( pre_len, FD_BPF_ALIGN_OF_U128 ) - pre_len;
359 :
360 1374 : uchar * post_data = buffer+start;
361 :
362 1374 : fd_account_meta_t const * metadata_check = view_acc->const_meta;
363 1374 : if( FD_UNLIKELY( fd_ulong_sat_sub( post_len, metadata_check->dlen )>MAX_PERMITTED_DATA_INCREASE ||
364 1374 : post_len>MAX_PERMITTED_DATA_LENGTH ) ) {
365 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
366 0 : }
367 :
368 1374 : if( copy_account_data ) {
369 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L551-563 */
370 1299 : int err = 0;
371 1299 : if( fd_account_can_data_be_resized( &ctx, view_acc->const_meta, post_len, &err ) &&
372 1299 : fd_account_can_data_be_changed( &ctx, i, &err ) ) {
373 :
374 468 : int err = fd_account_set_data_from_slice( &ctx, i, post_data, post_len );
375 468 : if( FD_UNLIKELY( err ) ) {
376 0 : return err;
377 0 : }
378 :
379 831 : } else if( FD_UNLIKELY( view_acc->const_meta->dlen!=post_len ||
380 831 : memcmp( view_acc->const_data, post_data, post_len ) ) ) {
381 3 : return err;
382 3 : }
383 1296 : start += pre_len;
384 1296 : } else { /* If direct mapping is enabled */
385 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L564-587 */
386 75 : start += FD_BPF_ALIGN_OF_U128 - alignment_offset;
387 75 : int err = 0;
388 75 : if( fd_account_can_data_be_resized( &ctx, view_acc->const_meta, post_len, &err ) &&
389 75 : fd_account_can_data_be_changed( &ctx, i, &err ) ) {
390 :
391 0 : err = fd_account_set_data_length( &ctx, i, post_len );
392 0 : if( FD_UNLIKELY( err ) ) {
393 0 : return err;
394 0 : }
395 :
396 0 : ulong allocated_bytes = fd_ulong_sat_sub( post_len, pre_len );
397 0 : if( allocated_bytes ) {
398 0 : uchar * acc_data = NULL;
399 0 : ulong acc_dlen = 0UL;
400 0 : err = fd_account_get_data_mut( &ctx, i, &acc_data, &acc_dlen );
401 0 : if( FD_UNLIKELY( err ) ) {
402 0 : return err;
403 0 : }
404 0 : if( FD_UNLIKELY( pre_len+allocated_bytes>acc_dlen ) ) {
405 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
406 0 : }
407 : /* We want to copy in the reallocated bytes from the input
408 : buffer directly into the borrowed account data buffer
409 : which has now been extended. */
410 0 : memcpy( acc_data+pre_len, buffer+start, allocated_bytes );
411 0 : }
412 75 : } else if( FD_UNLIKELY( view_acc->const_meta->dlen!=post_len ) ) {
413 0 : return err;
414 0 : }
415 75 : }
416 :
417 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L593-598 */
418 1371 : start += MAX_PERMITTED_DATA_INCREASE;
419 1371 : start += alignment_offset;
420 1371 : start += sizeof(ulong); // rent epoch
421 1371 : if( memcmp( view_acc->const_meta->info.owner, owner, sizeof(fd_pubkey_t) ) ) {
422 0 : int err = fd_account_set_owner( &ctx, i, owner );
423 0 : if( FD_UNLIKELY( err ) ) {
424 0 : return err;
425 0 : }
426 0 : }
427 1371 : }
428 1383 : }
429 :
430 375 : return FD_EXECUTOR_INSTR_SUCCESS;
431 378 : }
432 :
433 : uchar *
434 : fd_bpf_loader_input_serialize_unaligned( fd_exec_instr_ctx_t ctx,
435 : ulong * sz,
436 : ulong * pre_lens,
437 : fd_vm_input_region_t * input_mem_regions,
438 : uint * input_mem_regions_cnt,
439 : fd_vm_acc_region_meta_t * acc_region_metas,
440 219 : int copy_account_data ) {
441 219 : ulong serialized_size = 0UL;
442 219 : uchar const * instr_acc_idxs = ctx.instr->acct_txn_idxs;
443 219 : fd_pubkey_t const * txn_accs = ctx.txn_ctx->accounts;
444 :
445 219 : uchar acc_idx_seen[256] = {0};
446 219 : ushort dup_acc_idx[256] = {0};
447 :
448 219 : serialized_size += sizeof(ulong);
449 462 : for( ushort i=0; i<ctx.instr->acct_cnt; i++ ) {
450 243 : uchar acc_idx = instr_acc_idxs[i];
451 :
452 243 : serialized_size++; // dup
453 243 : if( FD_UNLIKELY( acc_idx_seen[acc_idx] ) ) {
454 6 : continue;
455 6 : }
456 :
457 237 : acc_idx_seen[acc_idx] = 1;
458 237 : dup_acc_idx[acc_idx] = i;
459 :
460 237 : fd_borrowed_account_t * view_acc = ctx.instr->borrowed_accounts[i];
461 237 : ulong acc_data_len = view_acc->const_meta->dlen;
462 :
463 237 : pre_lens[i] = acc_data_len;
464 :
465 237 : serialized_size += sizeof(uchar) // is_signer
466 237 : + sizeof(uchar) // is_writable
467 237 : + sizeof(fd_pubkey_t) // key
468 237 : + sizeof(ulong) // lamports
469 237 : + sizeof(ulong) // data_len
470 237 : + sizeof(fd_pubkey_t) // owner
471 237 : + sizeof(uchar) // executable
472 237 : + sizeof(ulong); // rent_epoch
473 237 : if( copy_account_data ) {
474 189 : serialized_size += acc_data_len;
475 189 : }
476 237 : }
477 :
478 219 : serialized_size += sizeof(ulong) // instruction data len
479 219 : + ctx.instr->data_sz // instruction data
480 219 : + sizeof(fd_pubkey_t); // program id
481 :
482 219 : uchar * serialized_params = fd_spad_alloc( ctx.txn_ctx->spad, 1UL, serialized_size );
483 219 : uchar * serialized_params_start = serialized_params;
484 219 : uchar * curr_serialized_params_start = serialized_params;
485 :
486 219 : FD_STORE( ulong, serialized_params, ctx.instr->acct_cnt );
487 219 : serialized_params += sizeof(ulong);
488 :
489 462 : for( ushort i=0; i<ctx.instr->acct_cnt; i++ ) {
490 243 : uchar acc_idx = instr_acc_idxs[i];
491 243 : fd_pubkey_t const * acc = &txn_accs[acc_idx];
492 :
493 243 : if( FD_UNLIKELY( acc_idx_seen[acc_idx] && dup_acc_idx[acc_idx] != i ) ) {
494 : // Duplicate
495 6 : FD_STORE( uchar, serialized_params, (uchar)dup_acc_idx[acc_idx] );
496 6 : serialized_params += sizeof(uchar);
497 237 : } else {
498 : /* Calculate and store the start of the actual metadata region for this account,
499 : excluding any duplicate account markers at the beginning.
500 :
501 : We use this later for retrieving the serialized values later in the CPI security checks. */
502 237 : ulong metadata_region_offset_with_dups = *input_mem_regions_cnt==0UL ? 0UL :
503 237 : input_mem_regions[ *input_mem_regions_cnt-1U ].vaddr_offset +
504 6 : input_mem_regions[ *input_mem_regions_cnt-1U ].region_sz;
505 :
506 237 : acc_region_metas[i].metadata_region_offset = metadata_region_offset_with_dups +
507 237 : (ulong)(serialized_params - curr_serialized_params_start);
508 :
509 237 : FD_STORE( uchar, serialized_params, FD_NON_DUP_MARKER );
510 237 : serialized_params += sizeof(uchar);
511 :
512 237 : fd_borrowed_account_t * view_acc = ctx.instr->borrowed_accounts[i];
513 237 : fd_account_meta_t const * metadata = view_acc->const_meta;
514 :
515 237 : uchar is_signer = (uchar)fd_instr_acc_is_signer_idx( ctx.instr, (uchar)i );
516 237 : FD_STORE( uchar, serialized_params, is_signer );
517 237 : serialized_params += sizeof(uchar);
518 :
519 237 : uchar is_writable = (uchar)fd_instr_acc_is_writable_idx( ctx.instr, (uchar)i );
520 237 : FD_STORE( uchar, serialized_params, is_writable );
521 237 : serialized_params += sizeof(uchar);
522 :
523 237 : fd_pubkey_t key = *acc;
524 237 : FD_STORE( fd_pubkey_t, serialized_params, key );
525 237 : serialized_params += sizeof(fd_pubkey_t);
526 :
527 237 : ulong lamports = metadata->info.lamports;
528 237 : FD_STORE( ulong, serialized_params, lamports );
529 237 : serialized_params += sizeof(ulong);
530 :
531 237 : ulong acc_data_len = metadata->dlen;
532 237 : FD_STORE( ulong, serialized_params, acc_data_len );
533 237 : serialized_params += sizeof(ulong);
534 :
535 237 : write_account( &ctx, view_acc, (uchar)i, &serialized_params, &curr_serialized_params_start,
536 237 : input_mem_regions, input_mem_regions_cnt, acc_region_metas, 0, copy_account_data );
537 :
538 237 : fd_pubkey_t owner = *(fd_pubkey_t *)&metadata->info.owner;
539 237 : FD_STORE( fd_pubkey_t, serialized_params, owner );
540 237 : serialized_params += sizeof(fd_pubkey_t);
541 :
542 237 : uchar is_executable = (uchar)metadata->info.executable;
543 237 : FD_STORE( uchar, serialized_params, is_executable );
544 237 : serialized_params += sizeof(uchar);
545 :
546 237 : ulong rent_epoch = metadata->info.rent_epoch;
547 237 : FD_STORE( ulong, serialized_params, rent_epoch );
548 237 : serialized_params += sizeof(ulong);
549 237 : }
550 243 : }
551 :
552 219 : ulong instr_data_len = ctx.instr->data_sz;
553 219 : FD_STORE( ulong, serialized_params, instr_data_len );
554 219 : serialized_params += sizeof(ulong);
555 :
556 219 : uchar * instr_data = (uchar *)ctx.instr->data;
557 219 : fd_memcpy( serialized_params, instr_data, instr_data_len );
558 219 : serialized_params += instr_data_len;
559 :
560 219 : FD_STORE( fd_pubkey_t, serialized_params, txn_accs[ctx.instr->program_id] );
561 219 : serialized_params += sizeof(fd_pubkey_t);
562 :
563 219 : FD_TEST( serialized_params == serialized_params_start + serialized_size );
564 219 : *sz = serialized_size;
565 :
566 219 : new_input_mem_region( input_mem_regions, input_mem_regions_cnt, curr_serialized_params_start,
567 219 : (ulong)(serialized_params - curr_serialized_params_start), 1 );
568 :
569 219 : return serialized_params_start;
570 219 : }
571 :
572 : int
573 : fd_bpf_loader_input_deserialize_unaligned( fd_exec_instr_ctx_t ctx,
574 : ulong const * pre_lens,
575 : uchar * input,
576 : ulong input_sz,
577 12 : int copy_account_data ) {
578 12 : uchar * input_cursor = input;
579 :
580 12 : uchar acc_idx_seen[256] = {0};
581 :
582 12 : uchar const * instr_acc_idxs = ctx.instr->acct_txn_idxs;
583 :
584 12 : input_cursor += sizeof(ulong);
585 :
586 30 : for( ulong i=0UL; i<ctx.instr->acct_cnt; i++ ) {
587 18 : uchar acc_idx = instr_acc_idxs[i];
588 :
589 18 : input_cursor++; /* is_dup */
590 18 : if( FD_UNLIKELY( acc_idx_seen[acc_idx] ) ) {
591 : /* no-op */
592 12 : } else {
593 12 : acc_idx_seen[acc_idx] = 1;
594 12 : input_cursor += sizeof(uchar) + /* is_signer */
595 12 : sizeof(uchar) + /* is_writable */
596 12 : sizeof(fd_pubkey_t); /* key */
597 :
598 12 : fd_borrowed_account_t * view_acc = ctx.instr->borrowed_accounts[i];
599 :
600 12 : ulong lamports = FD_LOAD( ulong, input_cursor );
601 12 : if( view_acc->const_meta && view_acc->const_meta->info.lamports!=lamports ) {
602 0 : int err = fd_account_set_lamports( &ctx, i, lamports );
603 0 : if( FD_UNLIKELY( err ) ) {
604 0 : return err;
605 0 : }
606 0 : }
607 12 : input_cursor += sizeof(ulong); /* lamports */
608 :
609 12 : input_cursor += sizeof(ulong); /* data length */
610 :
611 12 : if( copy_account_data ) {
612 6 : ulong pre_len = pre_lens[i];
613 6 : uchar * post_data = input_cursor;
614 6 : if( view_acc->const_meta ) {
615 6 : int err = 0;
616 6 : if( fd_account_can_data_be_resized( &ctx, view_acc->const_meta, pre_len, &err ) &&
617 6 : fd_account_can_data_be_changed( &ctx, i, &err ) ) {
618 0 : err = fd_account_set_data_from_slice( &ctx, i, post_data, pre_len );
619 0 : if( FD_UNLIKELY( err ) ) {
620 0 : return err;
621 0 : }
622 6 : } else if( view_acc->const_meta->dlen != pre_len ||
623 6 : memcmp( post_data, view_acc->const_data, pre_len ) ) {
624 0 : return err;
625 0 : }
626 6 : }
627 6 : input_cursor += pre_len;
628 6 : }
629 12 : input_cursor += sizeof(fd_pubkey_t) + /* owner */
630 12 : sizeof(uchar) + /* executable */
631 12 : sizeof(ulong); /* rent_epoch*/
632 12 : }
633 18 : }
634 :
635 12 : if( FD_UNLIKELY( input_cursor>input+input_sz ) ) {
636 0 : return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
637 0 : }
638 :
639 12 : return 0;
640 12 : }
|