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: 388 468 82.9 %
Date: 2024-11-13 11:58:15 Functions: 6 6 100.0 %

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

Generated by: LCOV version 1.14