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: 0 420 0.0 %
Date: 2025-08-05 05:04:49 Functions: 0 8 0.0 %

          Line data    Source code
       1             : #include "fd_bpf_loader_serialization.h"
       2             : #include "../fd_borrowed_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             :                       uchar                  is_writable,
      48           0 :                       uchar                  is_acct_data ) {
      49             : 
      50             :   /* The start vaddr of the new region should be equal to start of the previous
      51             :      region added to its size. */
      52           0 :   ulong vaddr_offset = *input_mem_regions_cnt==0UL ? 0UL : input_mem_regions[ *input_mem_regions_cnt-1U ].vaddr_offset +
      53           0 :                                                            input_mem_regions[ *input_mem_regions_cnt-1U ].region_sz;
      54           0 :   input_mem_regions[ *input_mem_regions_cnt ].is_writable  = is_writable;
      55           0 :   input_mem_regions[ *input_mem_regions_cnt ].haddr        = (ulong)buffer;
      56           0 :   input_mem_regions[ *input_mem_regions_cnt ].region_sz    = (uint)region_sz;
      57           0 :   input_mem_regions[ *input_mem_regions_cnt ].vaddr_offset = vaddr_offset;
      58           0 :   input_mem_regions[ *input_mem_regions_cnt ].is_acct_data = is_acct_data;
      59           0 :   (*input_mem_regions_cnt)++;
      60           0 : }
      61             : 
      62             : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L93-L130 */
      63             : /* This function handles casing for direct mapping being enabled as well as if
      64             :    the alignment is being stored. In the case where direct mapping is not
      65             :    enabled, we copy in the account data and a 10KiB buffer into the input region.
      66             :    These both go into the same memory buffer. However, when direct mapping is
      67             :    enabled, the account data and resizing buffers are represented by two
      68             :    different memory regions. In both cases, padding is used to maintain 8 byte
      69             :    alignment. If alignment is not required, then a resizing buffer is not used
      70             :    as the deprecated loader doesn't allow for resizing accounts. */
      71             : static void
      72             : write_account( fd_borrowed_account_t *   account,
      73             :                uchar                     instr_acc_idx,
      74             :                uchar * *                 serialized_params,
      75             :                uchar * *                 serialized_params_start,
      76             :                fd_vm_input_region_t *    input_mem_regions,
      77             :                uint *                    input_mem_regions_cnt,
      78             :                fd_vm_acc_region_meta_t * acc_region_metas,
      79             :                int                       is_aligned,
      80           0 :                int                       copy_account_data ) {
      81             : 
      82           0 :   uchar const * data = account ? fd_borrowed_account_get_data( account )     : NULL;
      83           0 :   ulong         dlen = account ? fd_borrowed_account_get_data_len( account ) : 0UL;
      84             : 
      85           0 :   acc_region_metas[instr_acc_idx].original_data_len = dlen;
      86           0 :   if( copy_account_data ) {
      87             :     /* Copy the account data into input region buffer */
      88           0 :     fd_memcpy( *serialized_params, data, dlen );
      89           0 :     *serialized_params += dlen;
      90             : 
      91           0 :     if( FD_LIKELY( is_aligned ) ) {
      92             :       /* Zero out padding bytes and max permitted data increase */
      93           0 :       ulong align_offset = fd_ulong_align_up( dlen, FD_BPF_ALIGN_OF_U128 ) - dlen;
      94           0 :       fd_memset( *serialized_params, 0, MAX_PERMITTED_DATA_INCREASE + align_offset );
      95           0 :       *serialized_params += MAX_PERMITTED_DATA_INCREASE + align_offset;
      96           0 :     }
      97             :     /* In the non-DM case, we don't bother with setting up mem regions.
      98             :        So has_data_region and has_resizing_region are set to 0. */
      99           0 :     acc_region_metas[instr_acc_idx].region_idx          = UINT_MAX;
     100           0 :     acc_region_metas[instr_acc_idx].has_data_region     = 0U;
     101           0 :     acc_region_metas[instr_acc_idx].has_resizing_region = 0U;
     102           0 :   } else { /* direct_mapping == true */
     103             :     /* First, push on the region for the metadata that has just been serialized.
     104             :        This function will push the metadata in the serialized_params from
     105             :        serialized_params_start to serialized_params as a region to the input
     106             :        memory regions array. */
     107             :     /* TODO: This region always has length of 96 and this can be set as a constant. */
     108             : 
     109           0 :     ulong region_sz = (ulong)(*serialized_params) - (ulong)(*serialized_params_start);
     110           0 :     new_input_mem_region( input_mem_regions, input_mem_regions_cnt, *serialized_params_start, region_sz, 1U, 0U );
     111             : 
     112             :     /* Next, push the region for the account data if there is account data. We
     113             :        intentionally omit copy on write as a region type. */
     114           0 :     int err = 0;
     115           0 :     uchar is_writable = !!(fd_borrowed_account_can_data_be_changed( account, &err ) && !err);
     116             : 
     117             :     /* Update the mapping from instruction account index to memory region index.
     118             :        This is an optimization to avoid redundant lookups to find accounts. */
     119           0 :     acc_region_metas[instr_acc_idx].region_idx          = *input_mem_regions_cnt;
     120           0 :     acc_region_metas[instr_acc_idx].has_data_region     = !!dlen;
     121           0 :     acc_region_metas[instr_acc_idx].has_resizing_region = (uchar)is_aligned;
     122             : 
     123           0 :     if( dlen ) {
     124           0 :       new_input_mem_region( input_mem_regions, input_mem_regions_cnt, data, dlen, is_writable, 1U );
     125           0 :     }
     126             : 
     127           0 :     if( FD_LIKELY( is_aligned ) ) {
     128             :       /* Finally, push a third region for the max resizing data region. This is
     129             :          done even if there is no account data. This must be aligned so padding
     130             :          bytes must be inserted. This resizing region is also padded to result
     131             :          in 8 byte alignment for the combination of the account data region with
     132             :          the resizing region.
     133             : 
     134             :          We add the max permitted resizing limit along with 8 bytes of padding
     135             :          to the serialization buffer. However, the padding bytes are used to
     136             :          maintain alignment in the VM virtual address space. */
     137           0 :       ulong align_offset = fd_ulong_align_up( dlen, FD_BPF_ALIGN_OF_U128 ) - dlen;
     138             : 
     139           0 :       fd_memset( *serialized_params, 0, MAX_PERMITTED_DATA_INCREASE + FD_BPF_ALIGN_OF_U128 );
     140             : 
     141             :       /* Leave a gap for alignment */
     142           0 :       uchar * region_buffer = *serialized_params + (FD_BPF_ALIGN_OF_U128 - align_offset);
     143           0 :       ulong   region_sz     = MAX_PERMITTED_DATA_INCREASE + align_offset;
     144           0 :       new_input_mem_region( input_mem_regions, input_mem_regions_cnt, region_buffer, region_sz, is_writable, 1U );
     145             : 
     146           0 :       *serialized_params += MAX_PERMITTED_DATA_INCREASE + FD_BPF_ALIGN_OF_U128;
     147           0 :     }
     148           0 :     *serialized_params_start = *serialized_params;
     149           0 :   }
     150           0 : }
     151             : 
     152             : static uchar *
     153             : fd_bpf_loader_input_serialize_aligned( fd_exec_instr_ctx_t *     ctx,
     154             :                                        ulong *                   sz,
     155             :                                        ulong *                   pre_lens,
     156             :                                        fd_vm_input_region_t *    input_mem_regions,
     157             :                                        uint *                    input_mem_regions_cnt,
     158             :                                        fd_vm_acc_region_meta_t * acc_region_metas,
     159             :                                        int                       copy_account_data,
     160           0 :                                        int                       mask_out_rent_epoch_in_vm_serialization ) {
     161           0 :   fd_pubkey_t * txn_accs = ctx->txn_ctx->account_keys;
     162             : 
     163           0 :   uchar acc_idx_seen[256] = {0};
     164           0 :   ushort dup_acc_idx[256] = {0};
     165             : 
     166             :   /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L429-L459 */
     167           0 :   ulong serialized_size = 0UL;
     168           0 :   serialized_size += sizeof(ulong); // acct_cnt
     169             :   /* First pass is to calculate size of buffer to allocate */
     170           0 :   for( ushort i=0; i<ctx->instr->acct_cnt; i++ ) {
     171           0 :     uchar acc_idx = (uchar)ctx->instr->accounts[i].index_in_transaction;
     172             : 
     173           0 :     serialized_size++; // dup byte
     174           0 :     if( FD_UNLIKELY( acc_idx_seen[acc_idx] ) ) {
     175           0 :       serialized_size += 7UL; // pad to 64-bit alignment
     176           0 :     } else {
     177           0 :       acc_idx_seen[acc_idx] = 1;
     178           0 :       dup_acc_idx[acc_idx]  = i;
     179             : 
     180             :       /* Borrow the account without checking the error, as it is guaranteed to exist
     181             :          https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/serialization.rs#L225 */
     182           0 :       fd_guarded_borrowed_account_t view_acc;
     183           0 :       fd_exec_instr_ctx_try_borrow_instr_account( ctx, i, &view_acc );
     184             : 
     185           0 :       ulong acc_data_len = fd_borrowed_account_get_data_len( &view_acc );
     186             : 
     187           0 :       serialized_size += sizeof(uchar)               // is_signer
     188           0 :                        + sizeof(uchar)               // is_writable
     189           0 :                        + sizeof(uchar)               // executable
     190           0 :                        + sizeof(uint)                // original_data_len
     191           0 :                        + sizeof(fd_pubkey_t)         // key
     192           0 :                        + sizeof(fd_pubkey_t)         // owner
     193           0 :                        + sizeof(ulong)               // lamports
     194           0 :                        + sizeof(ulong)               // data len
     195           0 :                        + MAX_PERMITTED_DATA_INCREASE
     196           0 :                        + sizeof(ulong);              // rent_epoch
     197           0 :       if( copy_account_data ) {
     198           0 :         serialized_size += fd_ulong_align_up( acc_data_len, FD_BPF_ALIGN_OF_U128 );
     199           0 :       } else {
     200           0 :         serialized_size += FD_BPF_ALIGN_OF_U128;
     201           0 :       }
     202           0 :     }
     203           0 :   }
     204             : 
     205           0 :   serialized_size += sizeof(ulong)        // data len
     206           0 :                   +  ctx->instr->data_sz
     207           0 :                   +  sizeof(fd_pubkey_t); // program id
     208             : 
     209             :   /* 16-byte aligned buffer:
     210             :      https://github.com/anza-xyz/agave/blob/v2.2.13/programs/bpf_loader/src/serialization.rs#L32
     211             :    */
     212           0 :   uchar * serialized_params            = fd_spad_alloc( ctx->txn_ctx->spad, FD_RUNTIME_INPUT_REGION_ALLOC_ALIGN_UP, fd_ulong_align_up( serialized_size, FD_RUNTIME_INPUT_REGION_ALLOC_ALIGN_UP ) );
     213           0 :   uchar * serialized_params_start      = serialized_params;
     214           0 :   uchar * curr_serialized_params_start = serialized_params;
     215             : 
     216           0 :   FD_STORE( ulong, serialized_params, ctx->instr->acct_cnt );
     217           0 :   serialized_params += sizeof(ulong);
     218             : 
     219             :   /* Second pass over the account is to serialize into the buffer. */
     220           0 :   for( ushort i=0; i<ctx->instr->acct_cnt; i++ ) {
     221           0 :     uchar         acc_idx = (uchar)ctx->instr->accounts[i].index_in_transaction;
     222           0 :     fd_pubkey_t * acc     = &txn_accs[acc_idx];
     223             : 
     224           0 :     if( FD_UNLIKELY( acc_idx_seen[acc_idx] && dup_acc_idx[acc_idx] != i ) ) {
     225             :       /* Duplicate. Store 8 byte buffer to maintain alignment but store the
     226             :          account index in the first byte.*/
     227           0 :       FD_STORE( ulong, serialized_params, 0UL );
     228           0 :       FD_STORE( uchar, serialized_params, (uchar)dup_acc_idx[acc_idx] );
     229           0 :       serialized_params += sizeof(ulong);
     230           0 :     } else {
     231             :       /* Calculate and store the start of the actual metadata region for this account,
     232             :          excluding any duplicate account markers at the beginning.
     233             : 
     234             :          We use this later for retrieving the serialized values later in the CPI security checks. */
     235           0 :       ulong metadata_region_offset_with_dups = *input_mem_regions_cnt==0UL ? 0UL :
     236           0 :         input_mem_regions[ *input_mem_regions_cnt-1U ].vaddr_offset +
     237           0 :         input_mem_regions[ *input_mem_regions_cnt-1U ].region_sz;
     238             : 
     239           0 :       acc_region_metas[i].metadata_region_offset = metadata_region_offset_with_dups +
     240           0 :         (ulong)(serialized_params - curr_serialized_params_start);
     241             : 
     242           0 :       FD_STORE( uchar, serialized_params, FD_NON_DUP_MARKER );
     243           0 :       serialized_params += sizeof(uchar);
     244             : 
     245             :       /* Borrow the account without checking the error, as it is guaranteed to exist
     246             :          https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/serialization.rs#L225 */
     247           0 :       fd_guarded_borrowed_account_t view_acc;
     248           0 :       fd_exec_instr_ctx_try_borrow_instr_account( ctx, i, &view_acc );
     249             : 
     250             :       /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L465 */
     251           0 :       fd_account_meta_t const * metadata = fd_borrowed_account_get_acc_meta( &view_acc );
     252             : 
     253           0 :       uchar is_signer = (uchar)fd_instr_acc_is_signer_idx( ctx->instr, (uchar)i );
     254           0 :       FD_STORE( uchar, serialized_params, is_signer );
     255           0 :       serialized_params += sizeof(uchar);
     256             : 
     257           0 :       uchar is_writable = (uchar)fd_instr_acc_is_writable_idx( ctx->instr, (uchar)i );
     258           0 :       FD_STORE( uchar, serialized_params, is_writable );
     259           0 :       serialized_params += sizeof(uchar);
     260             : 
     261           0 :       uchar is_executable = (uchar)metadata->info.executable;
     262           0 :       FD_STORE( uchar, serialized_params, is_executable );
     263           0 :       serialized_params += sizeof(uchar);
     264             : 
     265             :       /* The original data len field is intentionally NOT populated. */
     266           0 :       uint padding_0 = 0U;
     267           0 :       FD_STORE( uint, serialized_params, padding_0 );
     268           0 :       serialized_params += sizeof(uint);
     269             : 
     270           0 :       fd_pubkey_t key = *acc;
     271           0 :       FD_STORE( fd_pubkey_t, serialized_params, key );
     272           0 :       serialized_params += sizeof(fd_pubkey_t);
     273             : 
     274           0 :       fd_pubkey_t owner = *(fd_pubkey_t *)&metadata->info.owner;
     275           0 :       FD_STORE( fd_pubkey_t, serialized_params, owner );
     276           0 :       serialized_params += sizeof(fd_pubkey_t);
     277             : 
     278           0 :       ulong lamports = metadata->info.lamports;
     279           0 :       FD_STORE( ulong, serialized_params, lamports );
     280           0 :       serialized_params += sizeof(ulong);
     281             : 
     282           0 :       ulong acc_data_len = metadata->dlen;
     283           0 :       pre_lens[i] = acc_data_len;
     284             : 
     285           0 :       ulong data_len = acc_data_len;
     286           0 :       FD_STORE( ulong, serialized_params, data_len );
     287           0 :       serialized_params += sizeof(ulong);
     288             : 
     289           0 :       write_account( &view_acc, (uchar)i, &serialized_params, &curr_serialized_params_start,
     290           0 :                      input_mem_regions, input_mem_regions_cnt, acc_region_metas, 1, copy_account_data );
     291             : 
     292           0 :       ulong rent_epoch = mask_out_rent_epoch_in_vm_serialization ? ULONG_MAX : metadata->info.rent_epoch;
     293           0 :       FD_STORE( ulong, serialized_params, rent_epoch );
     294           0 :       serialized_params += sizeof(ulong);
     295           0 :     }
     296             : 
     297           0 :   }
     298             : 
     299           0 :   ulong instr_data_len = ctx->instr->data_sz;
     300           0 :   FD_STORE( ulong, serialized_params, instr_data_len );
     301           0 :   serialized_params += sizeof(ulong);
     302             : 
     303           0 :   uchar * instr_data = ctx->instr->data;
     304           0 :   fd_memcpy( serialized_params, instr_data, instr_data_len );
     305           0 :   serialized_params += instr_data_len;
     306             : 
     307           0 :   FD_STORE( fd_pubkey_t, serialized_params, txn_accs[ctx->instr->program_id] );
     308           0 :   serialized_params += sizeof(fd_pubkey_t);
     309             : 
     310           0 :   if( FD_UNLIKELY( serialized_params!=serialized_params_start+serialized_size ) ) {
     311           0 :     FD_LOG_ERR(( "Serializing error" )); /* TODO: we can likely get rid of this check altogether */
     312           0 :   }
     313             : 
     314             :   /* Write out the final region. */
     315           0 :   new_input_mem_region( input_mem_regions, input_mem_regions_cnt, curr_serialized_params_start,
     316           0 :                         (ulong)(serialized_params - curr_serialized_params_start), 1U, 0U );
     317             : 
     318           0 :   *sz = serialized_size;
     319             : 
     320           0 :   return serialized_params_start;
     321           0 : }
     322             : 
     323             : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L500-L603 */
     324             : static int
     325             : fd_bpf_loader_input_deserialize_aligned( fd_exec_instr_ctx_t * ctx,
     326             :                                          ulong const *         pre_lens,
     327             :                                          uchar *               buffer,
     328             :                                          ulong FD_FN_UNUSED    buffer_sz,
     329           0 :                                          int                   copy_account_data ) {
     330             :   /* TODO: An optimization would be to skip ahead through non-writable accounts */
     331             :   /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L507 */
     332           0 :   ulong start = 0UL;
     333             : 
     334           0 :   uchar acc_idx_seen[256] = {0};
     335             : 
     336           0 :   start += sizeof(ulong); // number of accounts
     337             :   /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L508-L600 */
     338           0 :   for( ushort i=0; i<ctx->instr->acct_cnt; i++ ) {
     339           0 :     uchar acc_idx = (uchar)ctx->instr->accounts[i].index_in_transaction;
     340             : 
     341           0 :     start++; // position
     342             : 
     343             :     /* get the borrowed account
     344             :        https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/serialization.rs#L519 */
     345           0 :     fd_guarded_borrowed_account_t view_acc;
     346           0 :     FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, i, &view_acc );
     347             : 
     348           0 :     if( FD_UNLIKELY( acc_idx_seen[acc_idx] ) ) {
     349             :       /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L515-517 */
     350           0 :       start += 7UL;
     351           0 :     } else {
     352             :       /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L518-524 */
     353           0 :       acc_idx_seen[acc_idx] = 1;
     354           0 :       start += sizeof(uchar)        // is_signer
     355           0 :              + sizeof(uchar)        // is_writable
     356           0 :              + sizeof(uchar)        // executable
     357           0 :              + sizeof(uint)         // original_data_len
     358           0 :              + sizeof(fd_pubkey_t); // key
     359             : 
     360             :       /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L525-548 */
     361             : 
     362           0 :       fd_pubkey_t * owner = (fd_pubkey_t *)(buffer+start);
     363           0 :       start += sizeof(fd_pubkey_t); // owner
     364             : 
     365           0 :       ulong lamports = FD_LOAD( ulong, buffer+start );
     366           0 :       if( lamports!=fd_borrowed_account_get_lamports( &view_acc ) ) {
     367           0 :         int err = fd_borrowed_account_set_lamports( &view_acc, lamports );
     368           0 :         if( FD_UNLIKELY( err ) ) {
     369           0 :           return err;
     370           0 :         }
     371           0 :       }
     372           0 :       start += sizeof(ulong); // lamports
     373             : 
     374           0 :       ulong post_len = FD_LOAD( ulong, buffer+start );
     375           0 :       start += sizeof(ulong); // data length
     376             : 
     377           0 :       ulong pre_len = pre_lens[i];
     378           0 :       ulong alignment_offset = fd_ulong_align_up( pre_len, FD_BPF_ALIGN_OF_U128 ) - pre_len;
     379             : 
     380           0 :       uchar * post_data = buffer+start;
     381             : 
     382           0 :       fd_account_meta_t const * metadata_check = fd_borrowed_account_get_acc_meta( &view_acc );
     383           0 :       if( FD_UNLIKELY( fd_ulong_sat_sub( post_len, metadata_check->dlen )>MAX_PERMITTED_DATA_INCREASE ||
     384           0 :                        post_len>MAX_PERMITTED_DATA_LENGTH ) ) {
     385           0 :         return FD_EXECUTOR_INSTR_ERR_INVALID_REALLOC;
     386           0 :       }
     387             : 
     388           0 :       if( copy_account_data ) {
     389             :         /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L551-563 */
     390           0 :         int err = 0;
     391           0 :         if( fd_borrowed_account_can_data_be_resized( &view_acc, post_len, &err ) &&
     392           0 :             fd_borrowed_account_can_data_be_changed( &view_acc, &err ) ) {
     393             : 
     394           0 :           int err = fd_borrowed_account_set_data_from_slice( &view_acc, post_data, post_len );
     395           0 :           if( FD_UNLIKELY( err ) ) {
     396           0 :             return err;
     397           0 :           }
     398             : 
     399           0 :         } else if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &view_acc )!=post_len ||
     400           0 :                                 memcmp( fd_borrowed_account_get_data( &view_acc ), post_data, post_len ) ) ) {
     401           0 :           return err;
     402           0 :         }
     403           0 :         start += pre_len;
     404           0 :       } else { /* If direct mapping is enabled */
     405             :         /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L564-587 */
     406           0 :         start += FD_BPF_ALIGN_OF_U128 - alignment_offset;
     407           0 :         int err = 0;
     408           0 :         if( fd_borrowed_account_can_data_be_resized( &view_acc, post_len, &err ) &&
     409           0 :             fd_borrowed_account_can_data_be_changed( &view_acc, &err ) ) {
     410             : 
     411           0 :           err = fd_borrowed_account_set_data_length( &view_acc, post_len );
     412           0 :           if( FD_UNLIKELY( err ) ) {
     413           0 :             return err;
     414           0 :           }
     415             : 
     416           0 :           ulong allocated_bytes = fd_ulong_sat_sub( post_len, pre_len );
     417           0 :           if( allocated_bytes ) {
     418           0 :             uchar * acc_data = NULL;
     419           0 :             ulong   acc_dlen = 0UL;
     420           0 :             err = fd_borrowed_account_get_data_mut( &view_acc, &acc_data, &acc_dlen );
     421           0 :             if( FD_UNLIKELY( err ) ) {
     422           0 :               return err;
     423           0 :             }
     424           0 :             if( FD_UNLIKELY( pre_len+allocated_bytes>acc_dlen ) ) {
     425           0 :               return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
     426           0 :             }
     427             :             /* We want to copy in the reallocated bytes from the input
     428             :                buffer directly into the borrowed account data buffer
     429             :                which has now been extended. */
     430           0 :               memcpy( acc_data+pre_len, buffer+start, allocated_bytes );
     431           0 :           }
     432           0 :         } else if( FD_UNLIKELY( fd_borrowed_account_get_data_len( &view_acc )!=post_len ) ) {
     433           0 :           return err;
     434           0 :         }
     435           0 :       }
     436             : 
     437             :       /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/programs/bpf_loader/src/serialization.rs#L593-598 */
     438           0 :       start += MAX_PERMITTED_DATA_INCREASE;
     439           0 :       start += alignment_offset;
     440           0 :       start += sizeof(ulong); // rent epoch
     441           0 :       if( memcmp( fd_borrowed_account_get_owner( &view_acc ), owner, sizeof(fd_pubkey_t) ) ) {
     442           0 :         int err = fd_borrowed_account_set_owner( &view_acc, owner );
     443           0 :         if( FD_UNLIKELY( err ) ) {
     444           0 :           return err;
     445           0 :         }
     446           0 :       }
     447           0 :     }
     448           0 :   }
     449             : 
     450           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     451           0 : }
     452             : 
     453             : static uchar *
     454             : fd_bpf_loader_input_serialize_unaligned( fd_exec_instr_ctx_t *     ctx,
     455             :                                          ulong *                   sz,
     456             :                                          ulong *                   pre_lens,
     457             :                                          fd_vm_input_region_t *    input_mem_regions,
     458             :                                          uint *                    input_mem_regions_cnt,
     459             :                                          fd_vm_acc_region_meta_t * acc_region_metas,
     460             :                                          int                       copy_account_data,
     461           0 :                                          int                       mask_out_rent_epoch_in_vm_serialization ) {
     462           0 :   ulong               serialized_size = 0UL;
     463           0 :   fd_pubkey_t const * txn_accs        = ctx->txn_ctx->account_keys;
     464             : 
     465           0 :   uchar acc_idx_seen[256] = {0};
     466           0 :   ushort dup_acc_idx[256] = {0};
     467             : 
     468           0 :   serialized_size += sizeof(ulong);
     469           0 :   for( ushort i=0; i<ctx->instr->acct_cnt; i++ ) {
     470           0 :     uchar acc_idx = (uchar)ctx->instr->accounts[i].index_in_transaction;
     471             : 
     472           0 :     serialized_size++; // dup
     473           0 :     if( FD_UNLIKELY( acc_idx_seen[acc_idx] ) ) {
     474           0 :       continue;
     475           0 :     }
     476             : 
     477           0 :     acc_idx_seen[acc_idx] = 1;
     478           0 :     dup_acc_idx[acc_idx]  = i;
     479             : 
     480             :     /* Borrow the account without checking the error, as it is guaranteed to exist
     481             :          https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/serialization.rs#L225 */
     482           0 :     fd_guarded_borrowed_account_t view_acc;
     483           0 :     fd_exec_instr_ctx_try_borrow_instr_account( ctx, i, &view_acc );
     484             : 
     485           0 :     ulong acc_data_len = fd_borrowed_account_get_data_len( &view_acc );
     486             : 
     487           0 :     pre_lens[i] = acc_data_len;
     488             : 
     489           0 :     serialized_size += sizeof(uchar)        // is_signer
     490           0 :                       + sizeof(uchar)       // is_writable
     491           0 :                       + sizeof(fd_pubkey_t) // key
     492           0 :                       + sizeof(ulong)       // lamports
     493           0 :                       + sizeof(ulong)       // data_len
     494           0 :                       + sizeof(fd_pubkey_t) // owner
     495           0 :                       + sizeof(uchar)       // executable
     496           0 :                       + sizeof(ulong);      // rent_epoch
     497           0 :     if( copy_account_data ) {
     498           0 :       serialized_size += acc_data_len;
     499           0 :     }
     500           0 :   }
     501             : 
     502           0 :   serialized_size += sizeof(ulong)        // instruction data len
     503           0 :                    + ctx->instr->data_sz  // instruction data
     504           0 :                    + sizeof(fd_pubkey_t); // program id
     505             : 
     506             :   /* 16-byte aligned buffer:
     507             :      https://github.com/anza-xyz/agave/blob/v2.2.13/programs/bpf_loader/src/serialization.rs#L32
     508             :    */
     509           0 :   uchar * serialized_params            = fd_spad_alloc( ctx->txn_ctx->spad, FD_RUNTIME_INPUT_REGION_ALLOC_ALIGN_UP, serialized_size );
     510           0 :   uchar * serialized_params_start      = serialized_params;
     511           0 :   uchar * curr_serialized_params_start = serialized_params;
     512             : 
     513           0 :   FD_STORE( ulong, serialized_params, ctx->instr->acct_cnt );
     514           0 :   serialized_params += sizeof(ulong);
     515             : 
     516           0 :   for( ushort i=0; i<ctx->instr->acct_cnt; i++ ) {
     517           0 :     uchar               acc_idx = (uchar)ctx->instr->accounts[i].index_in_transaction;
     518           0 :     fd_pubkey_t const * acc     = &txn_accs[acc_idx];
     519             : 
     520           0 :     if( FD_UNLIKELY( acc_idx_seen[acc_idx] && dup_acc_idx[acc_idx] != i ) ) {
     521             :       // Duplicate
     522           0 :       FD_STORE( uchar, serialized_params, (uchar)dup_acc_idx[acc_idx] );
     523           0 :       serialized_params += sizeof(uchar);
     524           0 :     } else {
     525             :       /* Calculate and store the start of the actual metadata region for this account,
     526             :          excluding any duplicate account markers at the beginning.
     527             : 
     528             :          We use this later for retrieving the serialized values later in the CPI security checks. */
     529           0 :       ulong metadata_region_offset_with_dups = *input_mem_regions_cnt==0UL ? 0UL :
     530           0 :         input_mem_regions[ *input_mem_regions_cnt-1U ].vaddr_offset +
     531           0 :         input_mem_regions[ *input_mem_regions_cnt-1U ].region_sz;
     532             : 
     533           0 :       acc_region_metas[i].metadata_region_offset = metadata_region_offset_with_dups +
     534           0 :         (ulong)(serialized_params - curr_serialized_params_start);
     535             : 
     536           0 :       FD_STORE( uchar, serialized_params, FD_NON_DUP_MARKER );
     537           0 :       serialized_params += sizeof(uchar);
     538             : 
     539             :       /* Borrow the account without checking the error, as it is guaranteed to exist
     540             :          https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/serialization.rs#L225 */
     541           0 :       fd_guarded_borrowed_account_t view_acc;
     542           0 :       fd_exec_instr_ctx_try_borrow_instr_account( ctx, i, &view_acc );
     543             : 
     544           0 :       fd_account_meta_t const * metadata = fd_borrowed_account_get_acc_meta( &view_acc );
     545             : 
     546           0 :       uchar is_signer = (uchar)fd_instr_acc_is_signer_idx( ctx->instr, (uchar)i );
     547           0 :       FD_STORE( uchar, serialized_params, is_signer );
     548           0 :       serialized_params += sizeof(uchar);
     549             : 
     550           0 :       uchar is_writable = (uchar)fd_instr_acc_is_writable_idx( ctx->instr, (uchar)i );
     551           0 :       FD_STORE( uchar, serialized_params, is_writable );
     552           0 :       serialized_params += sizeof(uchar);
     553             : 
     554           0 :       fd_pubkey_t key = *acc;
     555           0 :       FD_STORE( fd_pubkey_t, serialized_params, key );
     556           0 :       serialized_params += sizeof(fd_pubkey_t);
     557             : 
     558           0 :       ulong lamports = metadata->info.lamports;
     559           0 :       FD_STORE( ulong, serialized_params, lamports );
     560           0 :       serialized_params += sizeof(ulong);
     561             : 
     562           0 :       ulong acc_data_len = metadata->dlen;
     563           0 :       FD_STORE( ulong, serialized_params, acc_data_len );
     564           0 :       serialized_params += sizeof(ulong);
     565             : 
     566           0 :       write_account( &view_acc, (uchar)i, &serialized_params, &curr_serialized_params_start,
     567           0 :                      input_mem_regions, input_mem_regions_cnt, acc_region_metas, 0, copy_account_data );
     568             : 
     569           0 :       fd_pubkey_t owner = *(fd_pubkey_t *)&metadata->info.owner;
     570           0 :       FD_STORE( fd_pubkey_t, serialized_params, owner );
     571           0 :       serialized_params += sizeof(fd_pubkey_t);
     572             : 
     573           0 :       uchar is_executable = (uchar)metadata->info.executable;
     574           0 :       FD_STORE( uchar, serialized_params, is_executable );
     575           0 :       serialized_params += sizeof(uchar);
     576             : 
     577           0 :       ulong rent_epoch = mask_out_rent_epoch_in_vm_serialization ? ULONG_MAX : metadata->info.rent_epoch;
     578           0 :       FD_STORE( ulong, serialized_params, rent_epoch );
     579           0 :       serialized_params += sizeof(ulong);
     580           0 :     }
     581           0 :   }
     582             : 
     583           0 :   ulong instr_data_len = ctx->instr->data_sz;
     584           0 :   FD_STORE( ulong, serialized_params, instr_data_len );
     585           0 :   serialized_params += sizeof(ulong);
     586             : 
     587           0 :   uchar * instr_data = (uchar *)ctx->instr->data;
     588           0 :   fd_memcpy( serialized_params, instr_data, instr_data_len );
     589           0 :   serialized_params += instr_data_len;
     590             : 
     591           0 :   FD_STORE( fd_pubkey_t, serialized_params, txn_accs[ctx->instr->program_id] );
     592           0 :   serialized_params += sizeof(fd_pubkey_t);
     593             : 
     594           0 :   FD_TEST( serialized_params == serialized_params_start + serialized_size );
     595           0 :   *sz = serialized_size;
     596             : 
     597           0 :   new_input_mem_region( input_mem_regions, input_mem_regions_cnt, curr_serialized_params_start,
     598           0 :               (ulong)(serialized_params - curr_serialized_params_start), 1U, 0U );
     599             : 
     600           0 :   return serialized_params_start;
     601           0 : }
     602             : 
     603             : static int
     604             : fd_bpf_loader_input_deserialize_unaligned( fd_exec_instr_ctx_t * ctx,
     605             :                                            ulong const *         pre_lens,
     606             :                                            uchar *               input,
     607             :                                            ulong                 input_sz,
     608           0 :                                            int                   copy_account_data ) {
     609           0 :   uchar *       input_cursor      = input;
     610           0 :   uchar         acc_idx_seen[256] = {0};
     611             : 
     612           0 :   input_cursor += sizeof(ulong);
     613             : 
     614           0 :   for( ushort i=0; i<ctx->instr->acct_cnt; i++ ) {
     615           0 :     uchar acc_idx = (uchar)ctx->instr->accounts[i].index_in_transaction;
     616             : 
     617           0 :     input_cursor++; /* is_dup */
     618           0 :     if( FD_UNLIKELY( acc_idx_seen[acc_idx] ) ) {
     619             :       /* no-op */
     620           0 :     } else {
     621           0 :       acc_idx_seen[acc_idx] = 1;
     622           0 :       input_cursor += sizeof(uchar) +      /* is_signer */
     623           0 :                       sizeof(uchar) +      /* is_writable */
     624           0 :                       sizeof(fd_pubkey_t); /* key */
     625             : 
     626             :       /* https://github.com/anza-xyz/agave/blob/v2.1.4/programs/bpf_loader/src/serialization.rs#L378 */
     627           0 :       fd_guarded_borrowed_account_t view_acc;
     628           0 :       FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, i, &view_acc );
     629             : 
     630           0 :       ulong lamports = FD_LOAD( ulong, input_cursor );
     631           0 :       if( fd_borrowed_account_get_acc_meta( &view_acc ) && fd_borrowed_account_get_lamports( &view_acc )!=lamports ) {
     632           0 :         int err = fd_borrowed_account_set_lamports( &view_acc, lamports );
     633           0 :         if( FD_UNLIKELY( err ) ) {
     634           0 :           return err;
     635           0 :         }
     636           0 :       }
     637             : 
     638           0 :       input_cursor += sizeof(ulong); /* lamports */
     639           0 :       input_cursor += sizeof(ulong); /* data length */
     640             : 
     641           0 :       if( copy_account_data ) {
     642           0 :         ulong   pre_len   = pre_lens[i];
     643           0 :         uchar * post_data = input_cursor;
     644           0 :         if( fd_borrowed_account_get_acc_meta( &view_acc ) ) {
     645           0 :           int err = 0;
     646           0 :           if( fd_borrowed_account_can_data_be_resized( &view_acc, pre_len, &err ) &&
     647           0 :               fd_borrowed_account_can_data_be_changed( &view_acc, &err ) ) {
     648           0 :             err = fd_borrowed_account_set_data_from_slice( &view_acc, post_data, pre_len );
     649           0 :             if( FD_UNLIKELY( err ) ) {
     650           0 :               return err;
     651           0 :             }
     652           0 :           } else if( fd_borrowed_account_get_data_len( &view_acc ) != pre_len ||
     653           0 :                      memcmp( post_data, fd_borrowed_account_get_data( &view_acc ), pre_len ) ) {
     654           0 :             return err;
     655           0 :           }
     656           0 :         }
     657           0 :         input_cursor += pre_len;
     658           0 :       }
     659           0 :       input_cursor += sizeof(fd_pubkey_t) + /* owner */
     660           0 :                       sizeof(uchar) +       /* executable */
     661           0 :                       sizeof(ulong);        /* rent_epoch*/
     662           0 :     }
     663           0 :   }
     664             : 
     665           0 :   if( FD_UNLIKELY( input_cursor>input+input_sz ) ) {
     666           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
     667           0 :   }
     668             : 
     669           0 :   return 0;
     670           0 : }
     671             : 
     672             : /* https://github.com/anza-xyz/agave/blob/v2.1.11/programs/bpf_loader/src/serialization.rs#L191-L252 */
     673             : int
     674             : fd_bpf_loader_input_serialize_parameters( fd_exec_instr_ctx_t *     instr_ctx,
     675             :                                           ulong *                   sz,
     676             :                                           ulong *                   pre_lens,
     677             :                                           fd_vm_input_region_t *    input_mem_regions,
     678             :                                           uint *                    input_mem_regions_cnt,
     679             :                                           fd_vm_acc_region_meta_t * acc_region_metas,
     680             :                                           int                       direct_mapping,
     681             :                                           int                       mask_out_rent_epoch_in_vm_serialization,
     682             :                                           uchar                     is_deprecated,
     683           0 :                                           uchar **                  out /* output */ ) {
     684             : 
     685             :   /* https://github.com/anza-xyz/agave/blob/v2.1.11/programs/bpf_loader/src/serialization.rs#L203-L206 */
     686           0 :   ulong num_ix_accounts = instr_ctx->instr->acct_cnt;
     687           0 :   if( FD_UNLIKELY( num_ix_accounts>=FD_INSTR_ACCT_MAX ) ) {
     688           0 :     return FD_EXECUTOR_INSTR_ERR_MAX_ACCS_EXCEEDED;
     689           0 :   }
     690             : 
     691             :   /* TODO: Like Agave's serialization functions, ours should probably return error codes
     692             : 
     693             :      https://github.com/anza-xyz/agave/blob/v2.1.11/programs/bpf_loader/src/serialization.rs#L237-L251 */
     694           0 :   if( FD_UNLIKELY( is_deprecated ) ) {
     695           0 :     *out = fd_bpf_loader_input_serialize_unaligned( instr_ctx, sz, pre_lens,
     696           0 :                                                     input_mem_regions, input_mem_regions_cnt,
     697           0 :                                                     acc_region_metas, !direct_mapping,
     698           0 :                                                     mask_out_rent_epoch_in_vm_serialization );
     699           0 :   } else {
     700           0 :     *out = fd_bpf_loader_input_serialize_aligned( instr_ctx, sz, pre_lens,
     701           0 :                                                   input_mem_regions, input_mem_regions_cnt,
     702           0 :                                                   acc_region_metas, !direct_mapping,
     703           0 :                                                   mask_out_rent_epoch_in_vm_serialization );
     704           0 :   }
     705             : 
     706           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     707           0 : }
     708             : 
     709             : /* https://github.com/anza-xyz/agave/blob/v2.1.11/programs/bpf_loader/src/serialization.rs#L254-L283 */
     710             : int
     711             : fd_bpf_loader_input_deserialize_parameters( fd_exec_instr_ctx_t * ctx,
     712             :                                             ulong const *         pre_lens,
     713             :                                             uchar *               input,
     714             :                                             ulong                 input_sz,
     715             :                                             int                   direct_mapping,
     716           0 :                                             uchar                 is_deprecated ) {
     717           0 :   if( FD_UNLIKELY( is_deprecated ) ) {
     718           0 :     return fd_bpf_loader_input_deserialize_unaligned( ctx, pre_lens, input, input_sz, !direct_mapping );
     719           0 :   } else {
     720           0 :     return fd_bpf_loader_input_deserialize_aligned( ctx, pre_lens, input, input_sz, !direct_mapping );
     721           0 :   }
     722           0 : }

Generated by: LCOV version 1.14