LCOV - code coverage report
Current view: top level - ballet/shred - fd_shred.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 52 53 98.1 %
Date: 2024-11-13 11:58:15 Functions: 1 1 100.0 %

          Line data    Source code
       1             : #include "fd_shred.h"
       2             : 
       3             : fd_shred_t const *
       4             : fd_shred_parse( uchar const * const buf,
       5       10089 :                 ulong         const sz ) {
       6       10089 :   ulong total_shred_sz = sz;
       7             :   /* Initial bounds check */
       8       10089 :   if( FD_UNLIKELY( total_shred_sz<fd_ulong_min( FD_SHRED_DATA_HEADER_SZ, FD_SHRED_CODE_HEADER_SZ ) ) ) return NULL;
       9             : 
      10       10089 :   fd_shred_t const * shred = (fd_shred_t *)buf;
      11             : 
      12             :   /* Validate shred type.
      13             :      Safe to access because `variant` ends at 0x41, which is <= 0x58 */
      14       10089 :   uchar variant = shred->variant;
      15       10089 :   uchar type = fd_shred_type( variant );
      16       10089 :   if( FD_UNLIKELY( (type!=FD_SHRED_TYPE_MERKLE_DATA) &
      17       10089 :                    (type!=FD_SHRED_TYPE_MERKLE_CODE) &
      18       10089 :                    (type!=FD_SHRED_TYPE_MERKLE_DATA_CHAINED) &
      19       10089 :                    (type!=FD_SHRED_TYPE_MERKLE_CODE_CHAINED) &
      20       10089 :                    (type!=FD_SHRED_TYPE_MERKLE_DATA_CHAINED_RESIGNED) &
      21       10089 :                    (type!=FD_SHRED_TYPE_MERKLE_CODE_CHAINED_RESIGNED) &
      22       10089 :                    (variant!=0xa5 /*FD_SHRED_TYPE_LEGACY_DATA*/ ) &
      23       10089 :                    (variant!=0x5a /*FD_SHRED_TYPE_LEGACY_CODE*/ ) ) )
      24         474 :     return NULL;
      25             : 
      26             :   /* There are six sections of a shred that can contribute to the size:
      27             :      header, payload, zero-padding, Merkle root of previous erasure
      28             :      batch, Merkle proof, and retransmitter signature.
      29             :      Some of these may have 0 size in certain cases.  sz is the sum of
      30             :      all 5, while for data shreds, shred->data.size == header+payload.
      31             :      We'll call the last three section the trailer. */
      32        9615 :   ulong header_sz       = fd_shred_header_sz( variant ); /* between 88 and 89 bytes */
      33        9615 :   ulong trailer_sz      = fd_shred_merkle_sz( shred->variant )   /* between 0 and 300 bytes */
      34        9615 :                            + fd_ulong_if( fd_shred_is_resigned( type ), FD_SHRED_SIGNATURE_SZ, 0UL ) /* 0 or 64 */
      35        9615 :                            + fd_ulong_if( fd_shred_is_chained( type ),  FD_SHRED_MERKLE_ROOT_SZ, 0UL ); /* 0 or 32 */
      36        9615 :   ulong zero_padding_sz;
      37        9615 :   ulong payload_sz;
      38             : 
      39        9615 :   if( FD_LIKELY( type & FD_SHRED_TYPEMASK_DATA ) ) {
      40        4377 :     if( FD_UNLIKELY( shred->data.size<header_sz ) ) return NULL;
      41        4377 :     payload_sz = (ulong)shred->data.size - header_sz; /* between 0 and USHORT_MAX */
      42        4377 :     if( FD_UNLIKELY( (type!=FD_SHRED_TYPE_LEGACY_DATA) & (sz<FD_SHRED_MIN_SZ) ) ) return NULL;
      43             : 
      44             :     /* legacy data shreds might be shorter than the normal
      45             :        FD_SHRED_MIN_SZ, but they don't have Merkle proofs, so everything
      46             :        after the payload is zero-padding/ignored.  On the other hand,
      47             :        Merkle data shreds might have some zero-padding, but anything
      48             :        between [FD_SHRED_MIN_SZ, sz) is extra bytes after the shred
      49             :        (which we don't care about the contents of but also tolerate).
      50             :        The Merkle proof is not in bytes [sz-merkle_proof_sz, sz) but in
      51             :        [FD_SHRED_MIN_SZ-merkle_proof_sz, FD_SHRED_MIN_SZ).  From above,
      52             :        we know sz >= FD_SHRED_MIN_SZ in this case. */
      53        4377 :     uchar is_legacy_data_shred = type==FD_SHRED_TYPE_LEGACY_DATA;
      54        4377 :     ulong effective_sz = fd_ulong_if( is_legacy_data_shred, sz, FD_SHRED_MIN_SZ );
      55        4377 :     if( FD_UNLIKELY( effective_sz < header_sz+payload_sz+trailer_sz ) ) return NULL;
      56        4377 :     zero_padding_sz = effective_sz - header_sz - payload_sz - trailer_sz;
      57        4377 :   }
      58        5238 :   else if( FD_LIKELY( type & FD_SHRED_TYPEMASK_CODE ) ) {
      59        5238 :     zero_padding_sz = 0UL;
      60             :     /* Payload size is not specified directly, but the whole shred must
      61             :        be FD_SHRED_MAX_SZ. */
      62        5238 :     if( FD_UNLIKELY( header_sz+zero_padding_sz+trailer_sz > FD_SHRED_MAX_SZ ) ) return NULL;
      63        5238 :     payload_sz      = FD_SHRED_MAX_SZ - header_sz - zero_padding_sz - trailer_sz;
      64        5238 :   }
      65           0 :   else return NULL;
      66             : 
      67        9615 :   if( FD_UNLIKELY( sz < header_sz + payload_sz + zero_padding_sz + trailer_sz ) ) return NULL;
      68             : 
      69             :   /* At this point we know all the fields exist, but we need to sanity
      70             :      check a few fields that would make a shred illegal. */
      71        9615 :   if( FD_LIKELY( type & FD_SHRED_TYPEMASK_DATA ) ) {
      72        4377 :     ulong parent_off = (ulong)shred->data.parent_off;
      73        4377 :     ulong slot       = shred->slot;
      74        4377 :     if( FD_UNLIKELY( (shred->data.flags&0xC0)==0x80                              ) ) return NULL;
      75        4374 :     if( FD_UNLIKELY( parent_off>slot                                             ) ) return NULL;
      76             :     /* The property we want to enforce is
      77             :            slot==0 <=> parent_off==0 <=> slot==parent_off,
      78             :        where <=> means if and only if.  It's a strange expression
      79             :        though, because any two of the statements automatically imply the
      80             :        other one, so it's logically equivalent to:
      81             :             (slot==0 or parent_off==0)   <=> slot==parent_off
      82             :        We want the complement though, so that we can return NULL, and
      83             :        the complement of iff is xor. */
      84        4368 :     if( FD_UNLIKELY( ((parent_off==0) | (slot==0UL)) ^ (slot==parent_off)        ) ) return NULL;
      85        4362 :     if( FD_UNLIKELY( shred->idx<shred->fec_set_idx                               ) ) return NULL;
      86        5238 :   } else {
      87        5238 :     if( FD_UNLIKELY( shred->code.idx>=shred->code.code_cnt                       ) ) return NULL;
      88        5232 :     if( FD_UNLIKELY( shred->code.idx> shred->idx                                 ) ) return NULL;
      89        5229 :     if( FD_UNLIKELY( (shred->code.data_cnt==0)|(shred->code.code_cnt==0)         ) ) return NULL;
      90        5226 :     if( FD_UNLIKELY( shred->code.code_cnt>256                                    ) ) return NULL;
      91        5226 :     if( FD_UNLIKELY( (ulong)shred->code.data_cnt+(ulong)shred->code.code_cnt>256 ) ) return NULL; /* I don't see this check in Agave, but it seems necessary */
      92        5226 :   }
      93             : 
      94        9576 :   return shred;
      95        9615 : }

Generated by: LCOV version 1.14