LCOV - code coverage report
Current view: top level - flamenco/runtime/program - fd_bpf_loader_serialization.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 343 388 88.4 %
Date: 2025-01-08 12:08:44 Functions: 6 6 100.0 %

          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 : }

Generated by: LCOV version 1.14