LCOV - code coverage report
Current view: top level - app/test - test_bpf_validate.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 415 472 87.9 %
Date: 2026-08-01 17:54:00 Functions: 65 68 95.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 259 426 60.8 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2025 Huawei Technologies Co., Ltd
       3                 :            :  */
       4                 :            : 
       5                 :            : #include "test.h"
       6                 :            : 
       7                 :            : #include <bpf_def.h>
       8                 :            : #include <rte_bpf.h>
       9                 :            : #include <rte_bpf_validate_debug.h>
      10                 :            : #include <rte_errno.h>
      11                 :            : 
      12                 :            : /*
      13                 :            :  * Tests of BPF validation.
      14                 :            :  */
      15                 :            : 
      16                 :            : extern int test_bpf_validate_logtype;
      17                 :            : #define RTE_LOGTYPE_TEST_BPF_VALIDATE test_bpf_validate_logtype
      18                 :            : #define TEST_LOG_LINE(level, ...) \
      19                 :            :         RTE_LOG_LINE(level, TEST_BPF_VALIDATE, "" __VA_ARGS__)
      20                 :            : 
      21         [ -  + ]:        303 : RTE_LOG_REGISTER(test_bpf_validate_logtype, test.bpf_validate, NOTICE);
      22                 :            : 
      23                 :            : /* Special value indicating that program counter variable is not being used. */
      24                 :            : #define NO_PROGRAM_COUNTER UINT32_MAX
      25                 :            : 
      26                 :            : /* Special value indicating that register variable is not being used. */
      27                 :            : #define NO_REGISTER UINT8_MAX
      28                 :            : 
      29                 :            : /* Sizes of text buffers used for formatting various debug outputs. */
      30                 :            : #define VALUE_FORMAT_BUFFER_SIZE 32
      31                 :            : #define INTERVAL_FORMAT_BUFFER_SIZE 64
      32                 :            : #define REGISTER_FORMAT_BUFFER_SIZE 256
      33                 :            : #define DISASSEMBLY_FORMAT_BUFFER_SIZE 64
      34                 :            : 
      35                 :            : #define COMPARISON_INDEX_IMMEDIATE RTE_BIT32(0)
      36                 :            : #define COMPARISON_INDEX_GREATER   RTE_BIT32(1)
      37                 :            : #define COMPARISON_INDEX_INCLUSIVE RTE_BIT32(2)
      38                 :            : #define COMPARISON_INDEX_SIGNED    RTE_BIT32(3)
      39                 :            : 
      40                 :            : /* List comparison opcodes to make their index bits match constants above.  */
      41                 :            : static const uint8_t comparisons_opcode[] = {
      42                 :            :         (BPF_JMP | EBPF_JLT  | BPF_X),
      43                 :            :         (BPF_JMP | EBPF_JLT  | BPF_K),
      44                 :            :         (BPF_JMP |  BPF_JGT  | BPF_X),
      45                 :            :         (BPF_JMP |  BPF_JGT  | BPF_K),
      46                 :            :         (BPF_JMP | EBPF_JLE  | BPF_X),
      47                 :            :         (BPF_JMP | EBPF_JLE  | BPF_K),
      48                 :            :         (BPF_JMP |  BPF_JGE  | BPF_X),
      49                 :            :         (BPF_JMP |  BPF_JGE  | BPF_K),
      50                 :            :         (BPF_JMP | EBPF_JSLT | BPF_X),
      51                 :            :         (BPF_JMP | EBPF_JSLT | BPF_K),
      52                 :            :         (BPF_JMP | EBPF_JSGT | BPF_X),
      53                 :            :         (BPF_JMP | EBPF_JSGT | BPF_K),
      54                 :            :         (BPF_JMP | EBPF_JSLE | BPF_X),
      55                 :            :         (BPF_JMP | EBPF_JSLE | BPF_K),
      56                 :            :         (BPF_JMP | EBPF_JSGE | BPF_X),
      57                 :            :         (BPF_JMP | EBPF_JSGE | BPF_K),
      58                 :            : };
      59                 :            : 
      60                 :            : /* Interval bounded by two signed values, inclusive; min <= max. */
      61                 :            : struct signed_interval {
      62                 :            :         int64_t min;
      63                 :            :         int64_t max;
      64                 :            : };
      65                 :            : 
      66                 :            : /* Interval bounded by two unsigned values, inclusive; min <= max. */
      67                 :            : struct unsigned_interval {
      68                 :            :         uint64_t min;
      69                 :            :         uint64_t max;
      70                 :            : };
      71                 :            : 
      72                 :            : /*
      73                 :            :  * Expected interval of register values.
      74                 :            :  *
      75                 :            :  * If `is_defined` is not set, domain is considered to be unused in verification
      76                 :            :  * parameters (instruction is not accessing corresponding register).
      77                 :            :  * It's not the same as `unknown` domain which describes register that is being
      78                 :            :  * used but can hold any value.
      79                 :            :  *
      80                 :            :  * Flag `is_pointer` tells if the interval is relative to some memory area base.
      81                 :            :  */
      82                 :            : struct domain {
      83                 :            :         bool is_defined;
      84                 :            :         bool is_pointer;
      85                 :            :         struct signed_interval s;
      86                 :            :         struct unsigned_interval u;
      87                 :            : };
      88                 :            : 
      89                 :            : /* Expected validation state at certain point. */
      90                 :            : struct state {
      91                 :            :         /* Specifies that the branch is dynamically unreachable. */
      92                 :            :         bool is_unreachable;
      93                 :            :         struct domain dst;
      94                 :            :         struct domain src;
      95                 :            : };
      96                 :            : 
      97                 :            : /* Instruction verification parameters. */
      98                 :            : struct verify_instruction_param {
      99                 :            :         struct ebpf_insn tested_instruction;
     100                 :            :         size_t area_size;
     101                 :            :         /* States just before the tested instruction, just after, or if jumped. */
     102                 :            :         struct state pre;
     103                 :            :         struct state post;
     104                 :            :         struct state jump;
     105                 :            : };
     106                 :            : 
     107                 :            : /* Point (pre/post/jump) specific verification context. */
     108                 :            : struct point_context {
     109                 :            :         uint32_t program_counter;
     110                 :            :         uint32_t hit_count;
     111                 :            :         char formatted_dst[REGISTER_FORMAT_BUFFER_SIZE];
     112                 :            :         char formatted_src[REGISTER_FORMAT_BUFFER_SIZE];
     113                 :            : };
     114                 :            : 
     115                 :            : /* Verification context. */
     116                 :            : struct verify_instruction_context {
     117                 :            :         struct verify_instruction_param prm;
     118                 :            :         /* Allocation of registers in the generated program. */
     119                 :            :         uint8_t base_reg;
     120                 :            :         uint8_t dst_reg;
     121                 :            :         uint8_t src_reg;
     122                 :            :         uint8_t tmp_reg;
     123                 :            :         /* Number of times invalid state callback was called. */
     124                 :            :         uint32_t invalid_state_count;
     125                 :            :         /* Contexts just before the tested instruction, just after, or if jumped. */
     126                 :            :         struct point_context pre;
     127                 :            :         struct point_context post;
     128                 :            :         struct point_context jump;
     129                 :            : };
     130                 :            : 
     131                 :            : /* Domain with both signed and unsigned interval having maximum size. */
     132                 :            : static const struct domain unknown = {
     133                 :            :         .is_defined = true,
     134                 :            :         .s = { .min = INT64_MIN, .max = INT64_MAX },
     135                 :            :         .u = { .min = 0, .max = UINT64_MAX },
     136                 :            : };
     137                 :            : 
     138                 :            : /* Unreachable state. */
     139                 :            : static const struct state unreachable = {
     140                 :            :         .is_unreachable = true,
     141                 :            : };
     142                 :            : 
     143                 :            : 
     144                 :            : /* BUILDING DOMAINS */
     145                 :            : 
     146                 :            : /* Create domain from singleton interval. */
     147                 :            : static struct domain
     148                 :            : make_singleton_domain(uint64_t value)
     149                 :            : {
     150                 :         60 :         return (struct domain){
     151                 :            :                 .is_defined = true,
     152                 :            :                 .s = { .min = value, .max = value },
     153                 :            :                 .u = { .min = value, .max = value },
     154                 :            :         };
     155                 :            : }
     156                 :            : 
     157                 :            : /* Create domain from signed interval. */
     158                 :            : static struct domain
     159                 :         60 : make_signed_domain(int64_t min, int64_t max)
     160                 :            : {
     161         [ -  + ]:         60 :         RTE_VERIFY(min <= max);
     162                 :        107 :         return (struct domain){
     163                 :            :                 .is_defined = true,
     164                 :            :                 .s = { .min = min, .max = max },
     165                 :         60 :                 .u = (min ^ max) >= 0 ?
     166         [ +  + ]:         60 :                         (struct unsigned_interval){ .min = min, .max = max } :
     167                 :            :                         unknown.u,
     168                 :            :         };
     169                 :            : }
     170                 :            : 
     171                 :            : /* Create domain from unsigned interval. */
     172                 :            : static struct domain
     173                 :         39 : make_unsigned_domain(uint64_t min, uint64_t max)
     174                 :            : {
     175         [ -  + ]:         39 :         RTE_VERIFY(min <= max);
     176                 :         59 :         return (struct domain){
     177                 :            :                 .is_defined = true,
     178                 :         39 :                 .s = (int64_t)(min ^ max) >= 0 ?
     179         [ +  + ]:         39 :                         (struct signed_interval){ .min = min, .max = max } :
     180                 :            :                         unknown.s,
     181                 :            :                 .u = { .min = min, .max = max },
     182                 :            :         };
     183                 :            : }
     184                 :            : 
     185                 :            : /* Create domain from signed interval. */
     186                 :            : static struct domain
     187                 :            : make_pointer_domain(int64_t min, int64_t max)
     188                 :            : {
     189                 :          9 :         struct domain result = make_signed_domain(min, max);
     190                 :            :         result.is_pointer = true;
     191                 :          4 :         return result;
     192                 :            : }
     193                 :            : 
     194                 :            : /* Return true if domain is a scalar or pointer singleton. */
     195                 :            : static bool
     196                 :            : domain_is_singleton(const struct domain *domain)
     197                 :            : {
     198                 :       1583 :         return domain->s.min == domain->s.max &&
     199   [ +  +  +  -  :        629 :                 (uint64_t)domain->s.max == domain->u.min &&
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     200   [ -  +  -  +  :        389 :                 domain->u.min == domain->u.max;
             -  +  -  + ]
     201                 :            : }
     202                 :            : 
     203                 :            : /* Print error message into buffer if rc signifies error or overflow. */
     204                 :            : static void
     205                 :       1833 : handle_format_errors(char *buffer, size_t bufsz, int rc)
     206                 :            : {
     207         [ -  + ]:       1833 :         if (rc < 0)
     208                 :          0 :                 snprintf(buffer, bufsz, "FORMAT ERROR %d!", -rc);
     209         [ -  + ]:       1833 :         else if ((unsigned int)rc >= bufsz)
     210                 :            :                 snprintf(buffer, bufsz, "FORMAT OVERFLOW!");
     211                 :       1833 : }
     212                 :            : 
     213                 :            : /* Format register information into provided buffer and return the buffer. */
     214                 :            : static const char *
     215                 :            : format_value(char *buffer, size_t bufsz, char format, uint64_t value)
     216                 :            : {
     217                 :          0 :         handle_format_errors(buffer, bufsz,
     218                 :            :                 rte_bpf_validate_debug_format_value(buffer, bufsz, format, value));
     219                 :            :         return buffer;
     220                 :            : }
     221                 :            : 
     222                 :            : /* Format register information into provided buffer and return the buffer. */
     223                 :            : static const char *
     224                 :            : format_interval(char *buffer, size_t bufsz, char format, uint64_t min, uint64_t max)
     225                 :            : {
     226                 :          0 :         handle_format_errors(buffer, bufsz,
     227                 :            :                 rte_bpf_validate_debug_format_interval(buffer, bufsz, format, min, max));
     228                 :            :         return buffer;
     229                 :            : }
     230                 :            : 
     231                 :            : /* Format domain information into provided buffer and return the buffer. */
     232                 :            : static const char *
     233                 :          0 : format_domain(char *buffer, size_t bufsz, const struct domain *domain)
     234                 :            : {
     235                 :            :         char signed_buffer[INTERVAL_FORMAT_BUFFER_SIZE];
     236                 :            :         char unsigned_buffer[INTERVAL_FORMAT_BUFFER_SIZE];
     237                 :            : 
     238                 :          0 :         const int rc = !domain->is_defined ?
     239         [ #  # ]:          0 :                 snprintf(buffer, bufsz, "UNDEFINED") :
     240                 :          0 :                 snprintf(buffer, bufsz, "%s %s INTERSECT %s",
     241         [ #  # ]:          0 :                         domain->is_pointer ? "pointer" : "scalar",
     242                 :            :                         format_interval(signed_buffer, sizeof(signed_buffer), 'd',
     243                 :          0 :                                 domain->s.min, domain->s.max),
     244                 :            :                         format_interval(unsigned_buffer, sizeof(unsigned_buffer), 'x',
     245                 :          0 :                                 domain->u.min, domain->u.max));
     246                 :            : 
     247         [ #  # ]:          0 :         handle_format_errors(buffer, bufsz, rc < 0 ? -errno : rc);
     248                 :            : 
     249                 :          0 :         return buffer;
     250                 :            : }
     251                 :            : 
     252                 :            : /* Format register information into provided buffer and return the buffer. */
     253                 :            : static const char *
     254                 :       1833 : format_register(struct rte_bpf_validate_debug *debug, char *buffer, size_t bufsz, uint8_t reg)
     255                 :            : {
     256                 :       1833 :         handle_format_errors(buffer, bufsz,
     257                 :            :                 rte_bpf_validate_debug_format_register_info(debug, buffer, bufsz, reg));
     258                 :       1833 :         return buffer;
     259                 :            : }
     260                 :            : 
     261                 :            : 
     262                 :            : /* CHECKING REGISTER ACTUAL DOMAINS */
     263                 :            : 
     264                 :            : /* Return true the specified conditional jump _may_ occur at current state. */
     265                 :            : static bool
     266                 :      14608 : may_jump(const struct rte_bpf_validate_debug *debug,
     267                 :            :         const struct ebpf_insn *jump, uint64_t imm64)
     268                 :            : {
     269                 :      14608 :         const int result = rte_bpf_validate_debug_may_jump(debug, jump, imm64);
     270         [ -  + ]:      14608 :         RTE_VERIFY(result >= 0);
     271                 :      14608 :         return (result & RTE_BPF_VALIDATE_DEBUG_MAY_BE_TRUE) != 0;
     272                 :            : }
     273                 :            : 
     274                 :            : /* Check interval of the register interpreted as signed scalar. */
     275                 :            : static int
     276                 :       1820 : check_signed_interval(struct rte_bpf_validate_debug *debug,
     277                 :            :         uint8_t reg, struct signed_interval interval)
     278                 :            : {
     279                 :            :         char buffer[VALUE_FORMAT_BUFFER_SIZE];
     280                 :            : 
     281         [ -  + ]:       1820 :         TEST_ASSERT_EQUAL(may_jump(debug,
     282                 :            :                 &(struct ebpf_insn){
     283                 :            :                         .code = (BPF_JMP | EBPF_JSLT | BPF_K),
     284                 :            :                         .dst_reg = reg,
     285                 :            :                 }, interval.min),
     286                 :            :                 false,
     287                 :            :                 "r%hhu s< %s is impossible", reg,
     288                 :            :                 format_value(buffer, sizeof(buffer), 'd', interval.min));
     289                 :            : 
     290         [ -  + ]:       1820 :         TEST_ASSERT_EQUAL(may_jump(debug,
     291                 :            :                 &(struct ebpf_insn){
     292                 :            :                         .code = (BPF_JMP | BPF_JEQ | BPF_K),
     293                 :            :                         .dst_reg = reg,
     294                 :            :                 }, interval.min),
     295                 :            :                 true,
     296                 :            :                 "r%hhu == %s is possible", reg,
     297                 :            :                 format_value(buffer, sizeof(buffer), 'd', interval.min));
     298                 :            : 
     299         [ -  + ]:       1820 :         TEST_ASSERT_EQUAL(may_jump(debug,
     300                 :            :                 &(struct ebpf_insn){
     301                 :            :                         .code = (BPF_JMP | BPF_JEQ | BPF_K),
     302                 :            :                         .dst_reg = reg,
     303                 :            :                 }, interval.max),
     304                 :            :                 true,
     305                 :            :                 "r%hhu == %s is possible", reg,
     306                 :            :                 format_value(buffer, sizeof(buffer), 'd', interval.max));
     307                 :            : 
     308         [ -  + ]:       1820 :         TEST_ASSERT_EQUAL(may_jump(debug,
     309                 :            :                 &(struct ebpf_insn){
     310                 :            :                         .code = (BPF_JMP | EBPF_JSGT | BPF_K),
     311                 :            :                         .dst_reg = reg,
     312                 :            :                 }, interval.max),
     313                 :            :                 false,
     314                 :            :                 "r%hhu s> %s is impossible", reg,
     315                 :            :                 format_value(buffer, sizeof(buffer), 'd', interval.max));
     316                 :            : 
     317                 :       1820 :         return TEST_SUCCESS;
     318                 :            : }
     319                 :            : 
     320                 :            : /* Check interval of the register interpreted as unsigned scalar. */
     321                 :            : static int
     322                 :       1820 : check_unsigned_interval(struct rte_bpf_validate_debug *debug,
     323                 :            :         uint8_t reg, struct unsigned_interval interval)
     324                 :            : {
     325                 :            :         char buffer[VALUE_FORMAT_BUFFER_SIZE];
     326                 :            : 
     327         [ -  + ]:       1820 :         TEST_ASSERT_EQUAL(may_jump(debug,
     328                 :            :                 &(struct ebpf_insn){
     329                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_K),
     330                 :            :                         .dst_reg = reg,
     331                 :            :                 }, interval.min),
     332                 :            :                 false,
     333                 :            :                 "r%hhu u< %s is impossible", reg,
     334                 :            :                 format_value(buffer, sizeof(buffer), 'x', interval.min));
     335                 :            : 
     336         [ -  + ]:       1820 :         TEST_ASSERT_EQUAL(may_jump(debug,
     337                 :            :                 &(struct ebpf_insn){
     338                 :            :                         .code = (BPF_JMP | BPF_JEQ | BPF_K),
     339                 :            :                         .dst_reg = reg,
     340                 :            :                 }, interval.min),
     341                 :            :                 true,
     342                 :            :                 "r%hhu == %s is possible", reg,
     343                 :            :                 format_value(buffer, sizeof(buffer), 'x', interval.min));
     344                 :            : 
     345         [ -  + ]:       1820 :         TEST_ASSERT_EQUAL(may_jump(debug,
     346                 :            :                 &(struct ebpf_insn){
     347                 :            :                         .code = (BPF_JMP | BPF_JEQ | BPF_K),
     348                 :            :                         .dst_reg = reg,
     349                 :            :                 }, interval.max),
     350                 :            :                 true,
     351                 :            :                 "r%hhu == %s is possible", reg,
     352                 :            :                 format_value(buffer, sizeof(buffer), 'x', interval.max));
     353                 :            : 
     354         [ -  + ]:       1820 :         TEST_ASSERT_EQUAL(may_jump(debug,
     355                 :            :                 &(struct ebpf_insn){
     356                 :            :                         .code = (BPF_JMP | BPF_JGT | BPF_K),
     357                 :            :                         .dst_reg = reg,
     358                 :            :                 }, interval.max),
     359                 :            :                 false,
     360                 :            :                 "r%hhu u> %s is impossible", reg,
     361                 :            :                 format_value(buffer, sizeof(buffer), 'x', interval.max));
     362                 :            : 
     363                 :       1820 :         return TEST_SUCCESS;
     364                 :            : }
     365                 :            : 
     366                 :            : /* Check interval of the register relative to the base register. */
     367                 :            : static int
     368                 :         12 : check_relative_interval(struct rte_bpf_validate_debug *debug,
     369                 :            :         uint8_t reg, struct signed_interval interval, uint8_t base_reg)
     370                 :            : {
     371                 :            :         char buffer[VALUE_FORMAT_BUFFER_SIZE];
     372                 :            : 
     373         [ -  + ]:         12 :         TEST_ASSERT_EQUAL(may_jump(debug,
     374                 :            :                 &(struct ebpf_insn){
     375                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_X),
     376                 :            :                         .dst_reg = reg,
     377                 :            :                         .src_reg = base_reg,
     378                 :            :                 }, interval.min),
     379                 :            :                 false,
     380                 :            :                 "r%hhu u< r%hhu + %s is impossible", reg, base_reg,
     381                 :            :                 format_value(buffer, sizeof(buffer), 'd', interval.min));
     382                 :            : 
     383         [ -  + ]:         12 :         TEST_ASSERT_EQUAL(may_jump(debug,
     384                 :            :                 &(struct ebpf_insn){
     385                 :            :                         .code = (BPF_JMP | BPF_JEQ | BPF_X),
     386                 :            :                         .dst_reg = reg,
     387                 :            :                         .src_reg = base_reg,
     388                 :            :                 }, interval.min),
     389                 :            :                 true,
     390                 :            :                 "r%hhu == r%hhu + %s is possible", reg, base_reg,
     391                 :            :                 format_value(buffer, sizeof(buffer), 'd', interval.min));
     392                 :            : 
     393         [ -  + ]:         12 :         TEST_ASSERT_EQUAL(may_jump(debug,
     394                 :            :                 &(struct ebpf_insn){
     395                 :            :                         .code = (BPF_JMP | BPF_JEQ | BPF_X),
     396                 :            :                         .dst_reg = reg,
     397                 :            :                         .src_reg = base_reg,
     398                 :            :                 }, interval.max),
     399                 :            :                 true,
     400                 :            :                 "r%hhu == r%hhu + %s is possible", reg, base_reg,
     401                 :            :                 format_value(buffer, sizeof(buffer), 'd', interval.max));
     402                 :            : 
     403         [ -  + ]:         12 :         TEST_ASSERT_EQUAL(may_jump(debug,
     404                 :            :                 &(struct ebpf_insn){
     405                 :            :                         .code = (BPF_JMP | BPF_JGT | BPF_X),
     406                 :            :                         .dst_reg = reg,
     407                 :            :                         .src_reg = base_reg,
     408                 :            :                 }, interval.max),
     409                 :            :                 false,
     410                 :            :                 "r%hhu u> r%hhu + %s is impossible", reg, base_reg,
     411                 :            :                 format_value(buffer, sizeof(buffer), 'd', interval.max));
     412                 :            : 
     413                 :         12 :         return TEST_SUCCESS;
     414                 :            : }
     415                 :            : 
     416                 :            : /*
     417                 :            :  * Check access of the register interpreted as pointer.
     418                 :            :  *
     419                 :            :  * Unlike other similar functions, min > max is not a problem here,
     420                 :            :  * so either signed or unsigned pair can be passed without any issues.
     421                 :            :  *
     422                 :            :  * This is the reason we are not using signed_interval or unsigned_interval here
     423                 :            :  * to avoid confusion.
     424                 :            :  */
     425                 :            : static int
     426                 :         24 : check_pointer_access(struct rte_bpf_validate_debug *debug, uint8_t reg,
     427                 :            :         uint64_t min, uint64_t max, size_t area_size)
     428                 :            : {
     429                 :            :         char buffer[VALUE_FORMAT_BUFFER_SIZE];
     430                 :            : 
     431                 :            :         /* Start and end of the valid offsets window (unless empty). */
     432                 :         24 :         const uint64_t window_begin = -min;
     433                 :         24 :         const uint64_t window_end = area_size - max;
     434                 :            : 
     435                 :            :         /* Only have accessible bytes if the interval is smaller than the area. */
     436                 :         24 :         const uint64_t interval_size = max - min;
     437                 :            :         const bool window_empty = (interval_size >= area_size);
     438                 :            : 
     439         [ -  + ]:         24 :         TEST_ASSERT_EQUAL(rte_bpf_validate_debug_can_access(debug,
     440                 :            :                 &(struct ebpf_insn){
     441                 :            :                         .code = (BPF_LDX | BPF_B | BPF_MEM),
     442                 :            :                         .src_reg = reg
     443                 :            :                 }, window_begin - 1),
     444                 :            :                 false,
     445                 :            :                 "r%hhu + %s (before window begin) dereference is invalid", reg,
     446                 :            :                 format_value(buffer, sizeof(buffer), 'd', window_begin - 1));
     447                 :            : 
     448   [ -  +  -  - ]:         24 :         TEST_ASSERT_EQUAL(rte_bpf_validate_debug_can_access(debug,
     449                 :            :                 &(struct ebpf_insn){
     450                 :            :                         .code = (BPF_LDX | BPF_B | BPF_MEM),
     451                 :            :                         .src_reg = reg
     452                 :            :                 }, window_begin),
     453                 :            :                 !window_empty,
     454                 :            :                 "r%hhu + %s (after window begin) dereference is %s", reg,
     455                 :            :                 format_value(buffer, sizeof(buffer), 'd', window_begin),
     456                 :            :                 window_empty ? "invalid for empty window" : "valid");
     457                 :            : 
     458   [ -  +  -  - ]:         24 :         TEST_ASSERT_EQUAL(rte_bpf_validate_debug_can_access(debug,
     459                 :            :                 &(struct ebpf_insn){
     460                 :            :                         .code = (BPF_LDX | BPF_B | BPF_MEM),
     461                 :            :                         .src_reg = reg
     462                 :            :                 }, window_end - 1),
     463                 :            :                 !window_empty,
     464                 :            :                 "r%hhu + %s (before window end) dereference is %s", reg,
     465                 :            :                 format_value(buffer, sizeof(buffer), 'd', window_end - 1),
     466                 :            :                 window_empty ? "invalid for empty window" : "valid");
     467                 :            : 
     468         [ -  + ]:         24 :         TEST_ASSERT_EQUAL(rte_bpf_validate_debug_can_access(debug,
     469                 :            :                 &(struct ebpf_insn){
     470                 :            :                         .code = (BPF_LDX | BPF_B | BPF_MEM),
     471                 :            :                         .src_reg = reg
     472                 :            :                 }, window_end),
     473                 :            :                 false,
     474                 :            :                 "r%hhu + %s (after window end) dereference is invalid", reg,
     475                 :            :                 format_value(buffer, sizeof(buffer), 'd', window_end));
     476                 :            : 
     477                 :         24 :         return TEST_SUCCESS;
     478                 :            : }
     479                 :            : 
     480                 :            : /* Check domain of the register interpreted as absolute value. */
     481                 :            : static int
     482                 :       1820 : check_scalar_domain(struct rte_bpf_validate_debug *debug, uint8_t reg,
     483                 :            :         const struct domain *domain)
     484                 :            : {
     485         [ -  + ]:       1820 :         TEST_ASSERT_SUCCESS(
     486                 :            :                 check_signed_interval(debug, reg, domain->s),
     487                 :            :                 "absolute signed interval check");
     488                 :            : 
     489         [ -  + ]:       1820 :         TEST_ASSERT_SUCCESS(
     490                 :            :                 check_unsigned_interval(debug, reg, domain->u),
     491                 :            :                 "absolute unsigned interval check");
     492                 :            : 
     493                 :            :         return TEST_SUCCESS;
     494                 :            : }
     495                 :            : 
     496                 :            : /* Check domain of the register interpreted as relative pointer. */
     497                 :            : static int
     498                 :         12 : check_pointer_domain(struct rte_bpf_validate_debug *debug, uint8_t reg,
     499                 :            :         const struct domain *domain, uint8_t base_reg, size_t area_size)
     500                 :            : {
     501         [ -  + ]:         12 :         TEST_ASSERT_SUCCESS(
     502                 :            :                 check_relative_interval(debug, reg, domain->s, base_reg),
     503                 :            :                 "relative interval check");
     504                 :            : 
     505         [ -  + ]:         12 :         TEST_ASSERT_SUCCESS(
     506                 :            :                 check_pointer_access(debug, reg, domain->s.min, domain->s.max,
     507                 :            :                         area_size),
     508                 :            :                 "pointer signed access check");
     509                 :            : 
     510         [ -  + ]:         12 :         TEST_ASSERT_SUCCESS(
     511                 :            :                 check_pointer_access(debug, reg, domain->u.min, domain->u.max,
     512                 :            :                         area_size),
     513                 :            :                 "pointer unsigned access check");
     514                 :            : 
     515                 :            :         return TEST_SUCCESS;
     516                 :            : }
     517                 :            : 
     518                 :            : /* Check domain of the register and format the values in case of an error. */
     519                 :            : static int
     520                 :       1832 : check_domain(struct rte_bpf_validate_debug *debug, uint8_t reg,
     521                 :            :         const struct domain *domain, uint8_t base_reg, size_t area_size)
     522                 :            : {
     523                 :            :         char buffer[REGISTER_FORMAT_BUFFER_SIZE];
     524                 :            : 
     525                 :       1832 :         const int rc = domain->is_pointer ?
     526         [ +  + ]:       1832 :                 check_pointer_domain(debug, reg, domain, base_reg, area_size) :
     527                 :       1820 :                 check_scalar_domain(debug, reg, domain);
     528                 :            : 
     529         [ -  + ]:       1832 :         if (rc != TEST_SUCCESS) {
     530                 :          0 :                 TEST_LOG_LINE(WARNING, "\tExpected: r%hhu = %s", reg,
     531                 :            :                         format_domain(buffer, sizeof(buffer), domain));
     532                 :            : 
     533                 :          0 :                 TEST_LOG_LINE(WARNING, "\tFound: r%hhu = %s", reg,
     534                 :            :                         format_register(debug, buffer, sizeof(buffer), reg));
     535                 :            :         }
     536                 :            : 
     537                 :       1832 :         return rc;
     538                 :            : }
     539                 :            : 
     540                 :            : 
     541                 :            : /* GENERATING TEST PROGRAM */
     542                 :            : 
     543                 :            : static bool
     544                 :            : fits_in_imm32(int64_t value)
     545                 :            : {
     546                 :       2406 :         return value >= INT32_MIN && value <= INT32_MAX;
     547                 :            : }
     548                 :            : 
     549                 :            : /* Load constant into the register.  */
     550                 :            : static void
     551                 :        262 : load_constant(struct ebpf_insn **ins, uint8_t reg, int64_t value)
     552                 :            : {
     553         [ +  + ]:        262 :         if (fits_in_imm32(value)) {
     554                 :       1296 :                 *(*ins)++ = (struct ebpf_insn){
     555                 :            :                         .code = (EBPF_ALU64 | EBPF_MOV | BPF_K),
     556                 :            :                         .dst_reg = reg,
     557                 :        144 :                         .imm = (int32_t)value,
     558                 :            :                 };
     559                 :            :         } else {
     560                 :            :                 /* Load imm64 into tmp_reg using wide load, lower bits first... */
     561                 :        118 :                 *(*ins)++ = (struct ebpf_insn){
     562                 :            :                         .code = (BPF_LD | BPF_IMM | EBPF_DW),
     563                 :            :                         .dst_reg = reg,
     564                 :        118 :                         .imm = (uint32_t)value,
     565                 :            :                 };
     566                 :            :                 /* ... then higher bits. */
     567                 :        118 :                 *(*ins)++ = (struct ebpf_insn){
     568                 :        118 :                         .imm = (uint32_t)(value >> 32),
     569                 :            :                 };
     570                 :            :         }
     571                 :        262 : }
     572                 :            : 
     573                 :            : /*
     574                 :            :  * Compare specified register to value and jump.
     575                 :            :  *
     576                 :            :  * Jump offset is not filled and should be patched in by the caller.
     577                 :            :  */
     578                 :            : static void
     579                 :       2126 : compare_and_jump(struct ebpf_insn **ins, uint8_t op, uint8_t reg,
     580                 :            :         int64_t value, uint8_t tmp_reg)
     581                 :            : {
     582         [ +  + ]:       2126 :         if (fits_in_imm32(value)) {
     583                 :            :                 /* Jump on specified condition between reg and immediate. */
     584                 :       2061 :                 *(*ins)++ = (struct ebpf_insn){
     585                 :            :                         .code = (BPF_JMP | op | BPF_K),
     586                 :            :                         .dst_reg = reg,
     587                 :       2061 :                         .imm = (int32_t)value,
     588                 :            :                 };
     589                 :            :         } else {
     590                 :            :                 /* Load value into tmp_reg. */
     591                 :         65 :                 load_constant(ins, tmp_reg, value);
     592                 :            : 
     593                 :            :                 /* Jump on specified condition between reg and tmp_reg. */
     594                 :         65 :                 *(*ins)++ = (struct ebpf_insn){
     595                 :            :                         .code = (BPF_JMP | op | BPF_X),
     596                 :            :                         .dst_reg = reg,
     597                 :            :                         .src_reg = tmp_reg,
     598                 :            :                 };
     599                 :            :         }
     600                 :       2126 : }
     601                 :            : 
     602                 :            : /*
     603                 :            :  * Prepare register to be in the specified scalar domain.
     604                 :            :  *
     605                 :            :  * Unless singleton, load unknown value into it and clamp it with conditional jumps.
     606                 :            :  * (Jump offsets are not filled and should be patched in by the caller.)
     607                 :            :  */
     608                 :            : static void
     609         [ +  + ]:        762 : prepare_scalar_domain(struct ebpf_insn **ins, uint8_t reg,
     610                 :            :         const struct domain *domain, uint8_t base_reg, int *service_cell_count,
     611                 :            :         uint8_t tmp_reg)
     612                 :            : {
     613         [ +  + ]:        762 :         if (domain_is_singleton(domain)) {
     614                 :            :                 /* Don't need any uncertainty for a singleton. */
     615                 :        197 :                 load_constant(ins, reg, domain->s.min);
     616                 :            :                 return;
     617                 :            :         }
     618                 :            : 
     619                 :            :         /* Load value from memory area into the register. */
     620                 :        565 :         *(*ins)++ = (struct ebpf_insn){
     621                 :            :                 .code = (BPF_LDX | EBPF_DW | BPF_MEM),
     622                 :            :                 .dst_reg = reg,
     623                 :            :                 .src_reg = base_reg,
     624                 :        565 :                 .off = sizeof(uint64_t) * (*service_cell_count)++,
     625                 :            :         };
     626                 :            : 
     627                 :            :         /*
     628                 :            :          * Use both signed and unsigned conditions, even if redundant.
     629                 :            :          * It makes it more robust if conditional jump verification itself
     630                 :            :          * contains bugs like not updating the other type of interval.
     631                 :            :          * Jump instructions themselves can be tested separately to catch
     632                 :            :          * these bugs, this preparation phase is not a test for them.
     633                 :            :          */
     634         [ +  + ]:        565 :         if (domain->u.min > unknown.u.min)
     635                 :        522 :                 compare_and_jump(ins, EBPF_JLT, reg, domain->u.min, tmp_reg);
     636         [ +  + ]:        565 :         if (domain->u.max < unknown.u.max)
     637                 :        548 :                 compare_and_jump(ins, BPF_JGT, reg, domain->u.max, tmp_reg);
     638         [ +  + ]:        565 :         if (domain->s.min > unknown.s.min)
     639                 :        522 :                 compare_and_jump(ins, EBPF_JSLT, reg, domain->s.min, tmp_reg);
     640         [ +  + ]:        565 :         if (domain->s.max < unknown.s.max)
     641                 :        534 :                 compare_and_jump(ins, EBPF_JSGT, reg, domain->s.max, tmp_reg);
     642                 :            : }
     643                 :            : 
     644                 :            : /*
     645                 :            :  * Prepare register to be in the specified scalar or pointer domain, if any.
     646                 :            :  *
     647                 :            :  * If `domain` is NULL, do nothing. Otherwise prepare scalar domain,
     648                 :            :  * and then add base register to it to convert it to a pointer, if needed.
     649                 :            :  */
     650                 :            : static void
     651                 :        762 : prepare_domain(struct ebpf_insn **ins, uint8_t reg,
     652                 :            :         const struct domain *domain, uint8_t base_reg, int *service_cell_count,
     653                 :            :         uint8_t tmp_reg)
     654                 :            : {
     655                 :        762 :         prepare_scalar_domain(ins, reg, domain, base_reg, service_cell_count, tmp_reg);
     656                 :            : 
     657         [ +  + ]:        762 :         if (domain->is_pointer)
     658                 :            :                 /* Add base_reg to convert resulting scalar into a pointer. */
     659                 :          6 :                 *(*ins)++ = (struct ebpf_insn){
     660                 :            :                         .code = (EBPF_ALU64 | BPF_ADD | BPF_X),
     661                 :            :                         .dst_reg = reg,
     662                 :            :                         .src_reg = base_reg,
     663                 :            :                 };
     664                 :        762 : }
     665                 :            : 
     666                 :            : static void
     667                 :        454 : fill_verify_instruction_defaults(struct verify_instruction_param *prm)
     668                 :            : {
     669                 :            : 
     670         [ +  + ]:        454 :         if (BPF_CLASS(prm->tested_instruction.code) != BPF_JMP)
     671                 :         90 :                 prm->jump.is_unreachable = true;
     672                 :            : 
     673         [ -  + ]:        454 :         RTE_VERIFY(!prm->pre.is_unreachable);
     674         [ +  + ]:        454 :         if (prm->post.is_unreachable) {
     675         [ -  + ]:         90 :                 RTE_VERIFY(!prm->post.dst.is_defined);
     676         [ -  + ]:         90 :                 RTE_VERIFY(!prm->post.src.is_defined);
     677                 :            :         } else {
     678         [ +  + ]:        364 :                 if (!prm->post.dst.is_defined)
     679                 :         20 :                         prm->post.dst = prm->pre.dst;
     680         [ +  + ]:        364 :                 if (!prm->post.src.is_defined)
     681                 :        133 :                         prm->post.src = prm->pre.src;
     682                 :            :         }
     683                 :            : 
     684         [ +  + ]:        454 :         if (prm->jump.is_unreachable) {
     685         [ -  + ]:        184 :                 RTE_VERIFY(!prm->jump.dst.is_defined);
     686         [ -  + ]:        184 :                 RTE_VERIFY(!prm->jump.src.is_defined);
     687                 :            :         } else {
     688         [ +  + ]:        270 :                 if (!prm->jump.dst.is_defined)
     689                 :         16 :                         prm->jump.dst = prm->pre.dst;
     690         [ +  + ]:        270 :                 if (!prm->jump.src.is_defined)
     691                 :         39 :                         prm->jump.src = prm->pre.src;
     692                 :            :         }
     693                 :        454 : }
     694                 :            : 
     695                 :            : /* Generate program for the tested instruction and domains from the context.
     696                 :            :  *
     697                 :            :  * Return number of instructions.
     698                 :            :  *
     699                 :            :  * Destination and source registers in tested_instruction should not be specified,
     700                 :            :  * they are filled in by the function as long as domains for them are specified.
     701                 :            :  * Jump offset should not be specified, it is filled in by the function.
     702                 :            :  *
     703                 :            :  * If `pre.dst` or `pre.src` domain is not defined, corresponding register
     704                 :            :  * is not prepared.
     705                 :            :  *
     706                 :            :  * For non-jump instructions `jump.is_unreachable` is always set automatically.
     707                 :            :  *
     708                 :            :  * If any of the post or jump domains are not defined, they are copied from src
     709                 :            :  * unless corresponding branch is unreachable.
     710                 :            :  *
     711                 :            :  * Memory area size is automatically expanded to have enough space for loading
     712                 :            :  * unknown dst and src register values, thus testing sizes less than 16 bytes is
     713                 :            :  * not guaranteed.
     714                 :            :  *
     715                 :            :  * Limitations:
     716                 :            :  * - Support for jump instructions is incomplete (e.g. exit, ja).
     717                 :            :  * - Wide instructions are not supported yet.
     718                 :            :  */
     719                 :            : static uint32_t
     720                 :        414 : generate_program(struct verify_instruction_context *ctx, struct ebpf_insn *ins)
     721                 :            : {
     722                 :        414 :         struct ebpf_insn *const ins_buf = ins;
     723                 :            :         /* Number of double words used for service purposes. */
     724                 :        414 :         int service_cell_count = 0;
     725                 :            : 
     726                 :            :         /* Make sure we actually support provided instruction. */
     727         [ -  + ]:        414 :         switch (BPF_CLASS(ctx->prm.tested_instruction.code)) {
     728                 :          0 :         case BPF_LD:
     729                 :            :                 /* Wide instructions are not supported yet. */
     730         [ #  # ]:          0 :                 RTE_VERIFY(!rte_bpf_insn_is_wide(&ctx->prm.tested_instruction));
     731                 :            :                 break;
     732                 :            :         }
     733                 :            : 
     734                 :        414 :         fill_verify_instruction_defaults(&ctx->prm);
     735                 :            : 
     736                 :            :         /* Allocate registers, base_reg is received as program argument. */
     737                 :        414 :         ctx->base_reg = EBPF_REG_1;
     738   [ +  +  -  + ]:        414 :         ctx->dst_reg = (ctx->prm.pre.dst.is_defined || ctx->prm.post.dst.is_defined ||
     739         [ #  # ]:          0 :                 ctx->prm.jump.dst.is_defined) ? EBPF_REG_2 : NO_REGISTER;
     740   [ +  +  +  - ]:        414 :         ctx->src_reg = (ctx->prm.pre.src.is_defined || ctx->prm.post.src.is_defined ||
     741         [ +  - ]:         65 :                 ctx->prm.jump.src.is_defined) ? EBPF_REG_3 : NO_REGISTER;
     742                 :        414 :         ctx->tmp_reg = EBPF_REG_4;
     743                 :            : 
     744                 :            :         /* Clear r0 to make it eligible as a return value. */
     745                 :            :         load_constant(&ins, EBPF_REG_0, 0);
     746                 :            : 
     747                 :            :         /* Fill dst register in the instruction if defined anywhere, prepare if needed. */
     748         [ +  - ]:        414 :         if (ctx->dst_reg != NO_REGISTER) {
     749         [ -  + ]:        414 :                 RTE_VERIFY(ctx->prm.tested_instruction.dst_reg == 0);
     750                 :        414 :                 ctx->prm.tested_instruction.dst_reg = ctx->dst_reg;
     751                 :            : 
     752         [ +  + ]:        414 :                 if (ctx->prm.pre.dst.is_defined)
     753                 :        413 :                         prepare_domain(&ins, ctx->dst_reg, &ctx->prm.pre.dst,
     754                 :        413 :                                 ctx->base_reg, &service_cell_count, ctx->tmp_reg);
     755                 :            :                 else
     756                 :          1 :                         TEST_LOG_LINE(DEBUG, "Not preparing undefined r%hhu", ctx->dst_reg);
     757                 :            :         }
     758                 :            : 
     759                 :            :         /* Fill src register in the instruction if defined anywhere, prepare if needed. */
     760         [ +  + ]:        414 :         if (ctx->src_reg != NO_REGISTER) {
     761         [ -  + ]:        349 :                 RTE_VERIFY(ctx->prm.tested_instruction.src_reg == 0);
     762                 :        349 :                 ctx->prm.tested_instruction.src_reg = ctx->src_reg;
     763                 :            : 
     764         [ +  - ]:        349 :                 if (ctx->prm.pre.src.is_defined)
     765                 :        349 :                         prepare_domain(&ins, ctx->src_reg, &ctx->prm.pre.src,
     766                 :        349 :                                 ctx->base_reg, &service_cell_count, ctx->tmp_reg);
     767                 :            :                 else
     768                 :          0 :                         TEST_LOG_LINE(DEBUG, "Not preparing undefined r%hhu", ctx->src_reg);
     769                 :            :         }
     770                 :            : 
     771                 :            :         /* Automatically increase area size if needed. */
     772                 :        414 :         ctx->prm.area_size = RTE_MAX(ctx->prm.area_size, service_cell_count * sizeof(uint64_t));
     773                 :            : 
     774                 :            :         /* Issue tested instruction. */
     775                 :        414 :         ctx->pre.program_counter = ins - ins_buf;
     776                 :        414 :         *ins++ = ctx->prm.tested_instruction;
     777                 :            : 
     778                 :            :         /* Issue post instruction (for setting post breakpoint). */
     779                 :        414 :         ctx->post.program_counter = ins - ins_buf;
     780                 :            :         load_constant(&ins, EBPF_REG_0, 1);
     781                 :            : 
     782                 :            :         /* Issue jump branch for the jump instruction, even if dynamically unreachable. */
     783         [ +  + ]:        414 :         if (BPF_CLASS(ctx->prm.tested_instruction.code) != BPF_JMP)
     784                 :         90 :                 ctx->jump.program_counter = NO_PROGRAM_COUNTER;
     785                 :            :         else {
     786                 :            :                 /* Finish previous branch by issuing exit. */
     787                 :        324 :                 *ins++ = (struct ebpf_insn){ .code = (BPF_JMP | EBPF_EXIT) };
     788                 :            : 
     789                 :            :                 /* Issue jump target instruction (for setting jump breakpoint). */
     790                 :        324 :                 ctx->jump.program_counter = ins - ins_buf;
     791                 :            :                 load_constant(&ins, EBPF_REG_0, 2);
     792                 :            : 
     793                 :            :                 /* Patch jump in tested jump instruction. */
     794         [ -  + ]:        324 :                 RTE_VERIFY(ins_buf[ctx->pre.program_counter].off == 0);
     795                 :        324 :                 ins_buf[ctx->pre.program_counter].off =
     796                 :        324 :                         ctx->jump.program_counter - ctx->post.program_counter;
     797                 :            :         }
     798                 :            : 
     799                 :            :         /* Issue exit instruction. */
     800                 :        414 :         const uint32_t exit_pc = ins - ins_buf;
     801                 :        414 :         *ins++ = (struct ebpf_insn){ .code = (BPF_JMP | EBPF_EXIT) };
     802                 :            : 
     803                 :            :         /* Patch all jumps to point to exit. */
     804         [ +  + ]:       3905 :         for (uint32_t pc = 0; pc != ctx->pre.program_counter; ++pc)
     805         [ +  + ]:       3491 :                 if (BPF_CLASS(ins_buf[pc].code) == BPF_JMP) {
     806                 :            :                         RTE_ASSERT(ins_buf[pc].off == 0);
     807                 :       2126 :                         ins_buf[pc].off = exit_pc - (pc + 1);
     808                 :            :                 }
     809                 :            : 
     810                 :        414 :         const uint32_t nb_ins = ins - ins_buf;
     811                 :        414 :         return nb_ins;
     812                 :            : }
     813                 :            : 
     814                 :            : 
     815                 :            : /* VERIFICATION OF AN ARBITRARY INSTRUCTION */
     816                 :            : 
     817                 :            : /* Invoked when invalid state is detected. */
     818                 :            : static int
     819                 :       5160 : invalid_state_cb(struct rte_bpf_validate_debug *debug, void *void_ctx)
     820                 :            : {
     821                 :            :         struct verify_instruction_context *const ctx = void_ctx;
     822                 :            : 
     823                 :       5160 :         ++ctx->invalid_state_count;
     824                 :            : 
     825                 :       5160 :         TEST_LOG_LINE(WARNING,
     826                 :            :                 "Invalid state detected at pc %u",
     827                 :            :                 rte_bpf_validate_debug_get_pc(debug));
     828                 :            : 
     829                 :            :         RTE_SET_USED(debug);
     830                 :            : 
     831                 :       5160 :         return TEST_SUCCESS;
     832                 :            : }
     833                 :            : 
     834                 :            : static int
     835                 :        990 : point_callback(struct rte_bpf_validate_debug *debug, const struct verify_instruction_context *ctx,
     836                 :            :         struct point_context *point_ctx, const struct state *state)
     837                 :            : {
     838         [ -  + ]:        990 :         TEST_ASSERT_EQUAL(point_ctx->hit_count, 0, "not called before");
     839                 :            : 
     840                 :        990 :         const uint32_t pc = rte_bpf_validate_debug_get_pc(debug);
     841         [ -  + ]:        990 :         TEST_ASSERT_EQUAL(pc, point_ctx->program_counter,
     842                 :            :                 "Expected program counter: %" PRIu32 ", found: %" PRIu32,
     843                 :            :                         point_ctx->program_counter, pc);
     844                 :            : 
     845         [ +  - ]:        990 :         if (ctx->dst_reg != NO_REGISTER) {
     846                 :        990 :                 format_register(debug, point_ctx->formatted_dst,
     847                 :            :                         sizeof(point_ctx->formatted_dst), ctx->dst_reg);
     848                 :            : 
     849         [ +  + ]:        990 :                 if (state->dst.is_defined) {
     850         [ -  + ]:        989 :                         TEST_ASSERT_SUCCESS(
     851                 :            :                                 check_domain(debug, ctx->dst_reg, &state->dst,
     852                 :            :                                         ctx->base_reg, ctx->prm.area_size),
     853                 :            :                                 "dst domain check");
     854                 :        989 :                         TEST_LOG_LINE(DEBUG, "Successfully checked r%hhu.", ctx->dst_reg);
     855                 :            :                 } else
     856                 :          1 :                         TEST_LOG_LINE(DEBUG, "Not checking undefined r%hhu.", ctx->dst_reg);
     857                 :            :         }
     858                 :            : 
     859         [ +  + ]:        990 :         if (ctx->src_reg != NO_REGISTER) {
     860                 :        843 :                 format_register(debug, point_ctx->formatted_src,
     861                 :            :                         sizeof(point_ctx->formatted_src), ctx->src_reg);
     862                 :            : 
     863         [ +  - ]:        843 :                 if (state->src.is_defined) {
     864         [ -  + ]:        843 :                         TEST_ASSERT_SUCCESS(
     865                 :            :                                 check_domain(debug, ctx->src_reg, &state->src,
     866                 :            :                                         ctx->base_reg, ctx->prm.area_size),
     867                 :            :                                 "src domain check");
     868                 :        843 :                         TEST_LOG_LINE(DEBUG, "Successfully checked r%hhu.", ctx->src_reg);
     869                 :            :                 } else
     870                 :          0 :                         TEST_LOG_LINE(DEBUG, "Not checking undefined r%hhu.", ctx->src_reg);
     871                 :            :         }
     872                 :            : 
     873                 :        990 :         ++point_ctx->hit_count;
     874                 :            : 
     875                 :        990 :         return TEST_SUCCESS;
     876                 :            : }
     877                 :            : 
     878                 :            : /*
     879                 :            :  * Invoked before the tested instruction and checks pre-conditions.
     880                 :            :  *
     881                 :            :  * Also formats registers in the pre state for postmortem, if needed.
     882                 :            :  */
     883                 :            : static int
     884                 :        414 : pre_callback(struct rte_bpf_validate_debug *debug, void *void_ctx)
     885                 :            : {
     886                 :            :         struct verify_instruction_context *const ctx = void_ctx;
     887                 :            : 
     888                 :        414 :         TEST_LOG_LINE(DEBUG, "Pre callback invoked.");
     889                 :            : 
     890         [ -  + ]:        414 :         TEST_ASSERT_SUCCESS(
     891                 :            :                 point_callback(debug, ctx, &ctx->pre, &ctx->prm.pre),
     892                 :            :                 "pre-state check");
     893                 :            : 
     894                 :            :         return TEST_SUCCESS;
     895                 :            : }
     896                 :            : 
     897                 :            : /* Invoked after the tested instruction and checks post-conditions. */
     898                 :            : static int
     899                 :        333 : post_callback(struct rte_bpf_validate_debug *debug, void *void_ctx)
     900                 :            : {
     901                 :            :         struct verify_instruction_context *const ctx = void_ctx;
     902                 :            : 
     903                 :        333 :         TEST_LOG_LINE(DEBUG, "Post callback invoked.");
     904                 :            : 
     905         [ -  + ]:        333 :         TEST_ASSERT_SUCCESS(
     906                 :            :                 point_callback(debug, ctx, &ctx->post, &ctx->prm.post),
     907                 :            :                 "post-state check");
     908                 :            : 
     909                 :            :         return TEST_SUCCESS;
     910                 :            : }
     911                 :            : 
     912                 :            : /* Invoked after the tested instruction jumped and checks jump post-conditions. */
     913                 :            : static int
     914                 :        243 : jump_callback(struct rte_bpf_validate_debug *debug, void *void_ctx)
     915                 :            : {
     916                 :            :         struct verify_instruction_context *const ctx = void_ctx;
     917                 :            : 
     918                 :        243 :         TEST_LOG_LINE(DEBUG, "Jump callback invoked.");
     919                 :            : 
     920         [ -  + ]:        243 :         TEST_ASSERT_SUCCESS(
     921                 :            :                 point_callback(debug, ctx, &ctx->jump, &ctx->prm.jump),
     922                 :            :                 "jump-state check");
     923                 :            : 
     924                 :            :         return TEST_SUCCESS;
     925                 :            : }
     926                 :            : 
     927                 :            : static int
     928                 :        414 : debug_validation(struct verify_instruction_context *ctx, const struct ebpf_insn *ins,
     929                 :            :         uint32_t nb_ins)
     930                 :            : {
     931                 :        414 :         struct rte_bpf_validate_debug *const debug = rte_bpf_validate_debug_create();
     932         [ -  + ]:        414 :         TEST_ASSERT_NOT_NULL(debug, "validate debug create error %d", rte_errno);
     933                 :            : 
     934                 :        414 :         const struct rte_bpf_prm_ex prm = {
     935                 :            :                 .sz = sizeof(struct rte_bpf_prm_ex),
     936                 :            :                 .origin = RTE_BPF_ORIGIN_RAW,
     937                 :            :                 .raw.ins = ins,
     938                 :            :                 .raw.nb_ins = nb_ins,
     939                 :            :                 .prog_arg[0] = {
     940                 :            :                         .type = RTE_BPF_ARG_PTR,
     941                 :        414 :                         .size = ctx->prm.area_size,
     942                 :            :                 },
     943                 :            :                 .nb_prog_arg = 1,
     944                 :            :                 .debug = debug,
     945                 :            :         };
     946                 :            : 
     947                 :            :         /* Catch invalid states. */
     948         [ -  + ]:        414 :         TEST_ASSERT_NOT_NULL(rte_bpf_validate_debug_catch(debug,
     949                 :            :                 RTE_BPF_VALIDATE_DEBUG_EVENT_INVALID_STATE,
     950                 :            :                 &(struct rte_bpf_validate_debug_callback){
     951                 :            :                         .fn = invalid_state_cb,
     952                 :            :                         .ctx = ctx,
     953                 :            :                 }), "add catchpoint error %d", rte_errno);
     954                 :            : 
     955                 :            :         /* Break on pre test instruction. */
     956         [ -  + ]:        414 :         TEST_ASSERT_NOT_NULL(rte_bpf_validate_debug_break(debug, ctx->pre.program_counter,
     957                 :            :                 &(struct rte_bpf_validate_debug_callback){
     958                 :            :                         .fn = pre_callback,
     959                 :            :                         .ctx = ctx,
     960                 :            :                 }), "add pre breakpoint error %d", rte_errno);
     961                 :            : 
     962                 :            :         /* Break on post test instruction. */
     963         [ -  + ]:        414 :         TEST_ASSERT_NOT_NULL(rte_bpf_validate_debug_break(debug, ctx->post.program_counter,
     964                 :            :                 &(struct rte_bpf_validate_debug_callback){
     965                 :            :                         .fn = post_callback,
     966                 :            :                         .ctx = ctx,
     967                 :            :                 }), "add post breakpoint error %d", rte_errno);
     968                 :            : 
     969         [ +  + ]:        414 :         if (ctx->jump.program_counter != NO_PROGRAM_COUNTER)
     970                 :            :                 /* Break on jump test instruction. */
     971         [ -  + ]:        324 :                 TEST_ASSERT_NOT_NULL(rte_bpf_validate_debug_break(debug, ctx->jump.program_counter,
     972                 :            :                         &(struct rte_bpf_validate_debug_callback){
     973                 :            :                                 .fn = jump_callback,
     974                 :            :                                 .ctx = ctx,
     975                 :            :                         }), "add jump breakpoint error %d", rte_errno);
     976                 :            : 
     977                 :        414 :         struct rte_bpf *const bpf = rte_bpf_load_ex(&prm);
     978                 :        414 :         const int validation_errno = rte_errno;
     979                 :            : 
     980                 :        414 :         rte_bpf_destroy(bpf);
     981                 :        414 :         rte_bpf_validate_debug_destroy(debug);
     982                 :            : 
     983         [ -  + ]:        414 :         TEST_ASSERT_NOT_NULL(bpf, "validation error %d", validation_errno);
     984                 :            : 
     985         [ -  + ]:        414 :         TEST_ASSERT_EQUAL(ctx->pre.hit_count, !ctx->prm.pre.is_unreachable,
     986                 :            :                 "pre hit_count = %d", ctx->pre.hit_count);
     987         [ -  + ]:        414 :         TEST_ASSERT_EQUAL(ctx->post.hit_count, !ctx->prm.post.is_unreachable,
     988                 :            :                 "post hit_count = %d", ctx->post.hit_count);
     989         [ -  + ]:        414 :         TEST_ASSERT_EQUAL(ctx->jump.hit_count, !ctx->prm.jump.is_unreachable,
     990                 :            :                 "jump hit_count = %d", ctx->jump.hit_count);
     991                 :            : 
     992                 :            :         return TEST_SUCCESS;
     993                 :            : }
     994                 :            : 
     995                 :            : /* Dump whole program to log. */
     996                 :            : static void
     997                 :          0 : log_program_dump(const struct ebpf_insn *ins, uint32_t nb_ins, uint32_t pre_pc)
     998                 :            : {
     999                 :            :         char hexadecimal[DISASSEMBLY_FORMAT_BUFFER_SIZE];
    1000                 :            :         char disassembly[DISASSEMBLY_FORMAT_BUFFER_SIZE];
    1001                 :            : 
    1002                 :          0 :         TEST_LOG_LINE(NOTICE, "\tTested program:");
    1003         [ #  # ]:          0 :         for (uint32_t pc = 0; pc != nb_ins; ++pc) {
    1004                 :          0 :                 rte_bpf_format(hexadecimal, sizeof(hexadecimal), &ins[pc], pc,
    1005                 :            :                         RTE_BPF_FORMAT_FLAG_HEXADECIMAL |
    1006                 :            :                         RTE_BPF_FORMAT_FLAG_NEVER_WIDE);
    1007                 :          0 :                 rte_bpf_format(disassembly, sizeof(disassembly), &ins[pc], pc,
    1008                 :            :                         RTE_BPF_FORMAT_FLAG_DISASSEMBLY |
    1009                 :            :                         RTE_BPF_FORMAT_FLAG_ABSOLUTE_JUMPS);
    1010         [ #  # ]:          0 :                 TEST_LOG_LINE(NOTICE, "\t%5u: \t%s \t%s%s",
    1011                 :            :                         pc, hexadecimal, disassembly,
    1012                 :            :                         pc != pre_pc ? "" : "  ; tested instruction");
    1013                 :            : 
    1014         [ #  # ]:          0 :                 if (!rte_bpf_insn_is_wide(&ins[pc]))
    1015                 :          0 :                         continue;
    1016                 :            : 
    1017                 :          0 :                 ++pc;
    1018                 :            : 
    1019                 :          0 :                 rte_bpf_format(hexadecimal, sizeof(hexadecimal), &ins[pc], pc,
    1020                 :            :                         RTE_BPF_FORMAT_FLAG_HEXADECIMAL |
    1021                 :            :                         RTE_BPF_FORMAT_FLAG_NEVER_WIDE);
    1022                 :          0 :                 TEST_LOG_LINE(NOTICE, "\t%6s \t%s", "", hexadecimal);
    1023                 :            :         }
    1024                 :          0 : }
    1025                 :            : 
    1026                 :            : static void
    1027                 :          0 : log_formatted_registers(const char *heading, const struct verify_instruction_context *ctx,
    1028                 :            :         const struct point_context *point_ctx)
    1029                 :            : {
    1030                 :            :         char register_name[8];
    1031                 :            : 
    1032                 :          0 :         TEST_LOG_LINE(NOTICE, "\t%s", heading);
    1033         [ #  # ]:          0 :         if (ctx->dst_reg != NO_REGISTER) {
    1034                 :          0 :                 snprintf(register_name, sizeof(register_name), "r%hhu", ctx->dst_reg);
    1035                 :          0 :                 TEST_LOG_LINE(NOTICE, "\t%5s: \t%s", register_name, point_ctx->formatted_dst);
    1036                 :            :         }
    1037         [ #  # ]:          0 :         if (ctx->src_reg != NO_REGISTER) {
    1038                 :          0 :                 snprintf(register_name, sizeof(register_name), "r%hhu", ctx->src_reg);
    1039                 :          0 :                 TEST_LOG_LINE(NOTICE, "\t%5s: \t%s", register_name, point_ctx->formatted_src);
    1040                 :            :         }
    1041                 :          0 : }
    1042                 :            : 
    1043                 :            : /*
    1044                 :            :  * Verify instruction validation behaviour described by prm.
    1045                 :            :  *
    1046                 :            :  * Generate the program containing specified instruction on the code path with
    1047                 :            :  * specified register pre-domains and verify specified register post-domains.
    1048                 :            :  *
    1049                 :            :  * See comment to `generate_program` for more requirements and limitations.
    1050                 :            :  */
    1051                 :            : static int
    1052                 :        414 : verify_instruction(struct verify_instruction_param prm)
    1053                 :            : {
    1054                 :        414 :         struct verify_instruction_context ctx = {
    1055                 :            :                 .prm = prm,
    1056                 :            :         };
    1057                 :            :         struct ebpf_insn ins_buf[64];
    1058                 :            : 
    1059                 :        414 :         const uint32_t nb_ins = generate_program(&ctx, ins_buf);
    1060                 :            :         RTE_ASSERT(nb_ins <= RTE_DIM(ins_buf));
    1061                 :            : 
    1062                 :        414 :         const int rc = debug_validation(&ctx, ins_buf, nb_ins);
    1063                 :            : 
    1064                 :            :         /* Log more data at DEBUG level on success, NOTICE on failure. */
    1065   [ +  -  -  + ]:        414 :         if (rte_log_can_log(RTE_LOGTYPE_TEST_BPF_VALIDATE, RTE_LOG_DEBUG) ||
    1066                 :            :                         rc != TEST_SUCCESS) {
    1067                 :          0 :                 log_program_dump(ins_buf, nb_ins, ctx.pre.program_counter);
    1068                 :          0 :                 log_formatted_registers("Pre-state:", &ctx, &ctx.pre);
    1069                 :          0 :                 log_formatted_registers("Post-state:", &ctx, &ctx.post);
    1070         [ #  # ]:          0 :                 if (ctx.jump.program_counter != NO_PROGRAM_COUNTER)
    1071                 :          0 :                         log_formatted_registers("Jump-state:", &ctx, &ctx.jump);
    1072                 :            :         }
    1073                 :            : 
    1074                 :        414 :         return rc;
    1075                 :            : }
    1076                 :            : 
    1077                 :            : static int
    1078                 :        808 : opcode_comparison_index(uint8_t opcode)
    1079                 :            : {
    1080         [ +  - ]:       6260 :         for (int index = 0; index != RTE_DIM(comparisons_opcode); ++index)
    1081         [ +  + ]:       6260 :                 if (comparisons_opcode[index] == opcode)
    1082                 :        808 :                         return index;
    1083                 :          0 :         TEST_LOG_LINE(ERR, "Unsupported or not a comparison opcode: %hhx", opcode);
    1084                 :          0 :         RTE_VERIFY(false);
    1085                 :            : }
    1086                 :            : 
    1087                 :            : /* Change two-register comparison verification to immediate one. */
    1088                 :            : static bool
    1089                 :        304 : make_comparison_immediate(struct verify_instruction_param *prm)
    1090                 :            : {
    1091                 :        304 :         int comparison_index = opcode_comparison_index(prm->tested_instruction.code);
    1092                 :        304 :         const int64_t value = prm->pre.src.s.min;
    1093                 :            : 
    1094         [ -  + ]:        304 :         if ((comparison_index & COMPARISON_INDEX_IMMEDIATE) != 0) {
    1095                 :          0 :                 TEST_LOG_LINE(ERR, "Comparison %hhx is already immediate.",
    1096                 :            :                         prm->tested_instruction.code);
    1097                 :          0 :                 RTE_VERIFY(false);
    1098                 :            :         }
    1099                 :            : 
    1100   [ +  +  +  -  :        432 :         if (!domain_is_singleton(&prm->pre.src) || !domain_is_singleton(&prm->post.src) ||
                   -  + ]
    1101                 :            :                         !domain_is_singleton(&prm->jump.src)) {
    1102                 :        240 :                 TEST_LOG_LINE(DEBUG, "Cannot make immediate out of a non-singleton domain.");
    1103                 :        240 :                 return false;
    1104                 :            :         }
    1105   [ +  -  +  -  :         64 :         if (prm->pre.src.is_pointer || prm->post.src.is_pointer || prm->jump.src.is_pointer) {
                   -  + ]
    1106                 :          0 :                 TEST_LOG_LINE(DEBUG, "Cannot make immediate out of a pointer.");
    1107                 :          0 :                 return false;
    1108                 :            :         }
    1109   [ +  +  +  + ]:         64 :         if (prm->post.src.s.min != value || prm->jump.src.s.min != value) {
    1110                 :         46 :                 TEST_LOG_LINE(DEBUG, "Cannot make immediate if the value changes.");
    1111                 :         46 :                 return false;
    1112                 :            :         }
    1113         [ -  + ]:         18 :         if (!fits_in_imm32(value)) {
    1114                 :          0 :                 TEST_LOG_LINE(ERR, "Cannot make immediate unless value fits in int32.");
    1115                 :          0 :                 return false;
    1116                 :            :         }
    1117                 :            : 
    1118                 :         18 :         comparison_index |= COMPARISON_INDEX_IMMEDIATE;
    1119                 :         18 :         prm->tested_instruction.code = comparisons_opcode[comparison_index];
    1120                 :         18 :         prm->tested_instruction.imm = value;
    1121                 :            : 
    1122         [ -  + ]:         18 :         RTE_VERIFY(prm->pre.src.is_defined);
    1123                 :         18 :         prm->pre.src.is_defined = false;
    1124                 :            : 
    1125         [ +  + ]:         18 :         if (!prm->post.is_unreachable) {
    1126         [ -  + ]:         17 :                 RTE_VERIFY(prm->post.src.is_defined);
    1127                 :         17 :                 prm->post.src.is_defined = false;
    1128                 :            :         }
    1129                 :            : 
    1130         [ +  + ]:         18 :         if (!prm->jump.is_unreachable) {
    1131         [ -  + ]:         17 :                 RTE_VERIFY(prm->jump.src.is_defined);
    1132                 :         17 :                 prm->jump.src.is_defined = false;
    1133                 :            :         }
    1134                 :            : 
    1135                 :            :         return true;
    1136                 :            : }
    1137                 :            : 
    1138                 :            : /* Change immediate comparison verification to two-register one. */
    1139                 :            : static void
    1140                 :         12 : make_comparison_two_register(struct verify_instruction_param *prm)
    1141                 :            : {
    1142                 :         12 :         int comparison_index = opcode_comparison_index(prm->tested_instruction.code);
    1143                 :         12 :         const int64_t value = prm->tested_instruction.imm;
    1144                 :            : 
    1145         [ -  + ]:         12 :         if ((comparison_index & COMPARISON_INDEX_IMMEDIATE) == 0) {
    1146                 :          0 :                 TEST_LOG_LINE(ERR, "Comparison %hhx is already two-register.",
    1147                 :            :                         prm->tested_instruction.code);
    1148                 :          0 :                 RTE_VERIFY(false);
    1149                 :            :         }
    1150                 :            : 
    1151                 :         12 :         comparison_index &= ~COMPARISON_INDEX_IMMEDIATE;
    1152                 :         12 :         prm->tested_instruction.code = comparisons_opcode[comparison_index];
    1153                 :         12 :         prm->tested_instruction.imm = 0;
    1154                 :            : 
    1155         [ -  + ]:         12 :         RTE_VERIFY(!prm->pre.src.is_defined);
    1156                 :         12 :         prm->pre.src = make_singleton_domain(value);
    1157                 :            : 
    1158         [ +  + ]:         12 :         if (!prm->post.is_unreachable) {
    1159         [ -  + ]:          8 :                 RTE_VERIFY(!prm->post.src.is_defined);
    1160                 :          8 :                 prm->post.src = prm->pre.src;
    1161                 :            :         }
    1162                 :            : 
    1163         [ +  + ]:         12 :         if (!prm->jump.is_unreachable) {
    1164         [ -  + ]:          8 :                 RTE_VERIFY(!prm->jump.src.is_defined);
    1165                 :          8 :                 prm->jump.src = prm->pre.src;
    1166                 :            :         }
    1167                 :         12 : }
    1168                 :            : 
    1169                 :            : /* Change comparison verification to complement (negated result) one. */
    1170                 :            : static void
    1171                 :        152 : make_comparison_complement(struct verify_instruction_param *prm)
    1172                 :            : {
    1173                 :        152 :         int comparison_index = opcode_comparison_index(prm->tested_instruction.code);
    1174                 :        152 :         comparison_index ^= COMPARISON_INDEX_GREATER | COMPARISON_INDEX_INCLUSIVE;
    1175                 :        152 :         prm->tested_instruction.code = comparisons_opcode[comparison_index];
    1176                 :        152 :         RTE_SWAP(prm->post, prm->jump);
    1177                 :        152 : }
    1178                 :            : 
    1179                 :            : /* Change comparison verification to converse (swapped operands) one. */
    1180                 :            : static void
    1181                 :        304 : make_comparison_converse(struct verify_instruction_param *prm)
    1182                 :            : {
    1183                 :        304 :         int comparison_index = opcode_comparison_index(prm->tested_instruction.code);
    1184                 :        304 :         comparison_index ^= COMPARISON_INDEX_GREATER;
    1185                 :        304 :         prm->tested_instruction.code = comparisons_opcode[comparison_index];
    1186                 :        304 :         RTE_SWAP(prm->pre.dst, prm->pre.src);
    1187                 :        304 :         RTE_SWAP(prm->post.dst, prm->post.src);
    1188                 :        304 :         RTE_SWAP(prm->jump.dst, prm->jump.src);
    1189                 :        304 : }
    1190                 :            : 
    1191                 :            : /* Change signed comparison verification to unsigned one. */
    1192                 :            : static void
    1193                 :         36 : make_comparison_signed(struct verify_instruction_param *prm)
    1194                 :            : {
    1195                 :         36 :         int comparison_index = opcode_comparison_index(prm->tested_instruction.code);
    1196         [ -  + ]:         36 :         if ((comparison_index & COMPARISON_INDEX_SIGNED) != 0) {
    1197                 :          0 :                 TEST_LOG_LINE(ERR, "Comparison %hhx is already signed.",
    1198                 :            :                         prm->tested_instruction.code);
    1199                 :          0 :                 RTE_VERIFY(false);
    1200                 :            :         }
    1201                 :         36 :         comparison_index |= COMPARISON_INDEX_SIGNED;
    1202                 :         36 :         prm->tested_instruction.code = comparisons_opcode[comparison_index];
    1203                 :         36 : }
    1204                 :            : 
    1205                 :            : /* Verify specified two-register comparison and, if possible, immediate one. */
    1206                 :            : static int
    1207                 :        304 : verify_comparison_subcase(struct verify_instruction_param prm)
    1208                 :            : {
    1209         [ -  + ]:        304 :         TEST_ASSERT_SUCCESS(verify_instruction(prm), "two-register version check");
    1210                 :            : 
    1211         [ +  + ]:        304 :         if (make_comparison_immediate(&prm))
    1212         [ -  + ]:         18 :                 TEST_ASSERT_SUCCESS(verify_instruction(prm), "immediate version check");
    1213                 :            : 
    1214                 :            :         return TEST_SUCCESS;
    1215                 :            : }
    1216                 :            : 
    1217                 :            : /*
    1218                 :            :  * Verify comparison instruction validation behaviour.
    1219                 :            :  *
    1220                 :            :  * Call `verify_instruction` for all valid variations of the instruction.
    1221                 :            :  *
    1222                 :            :  * For instance, `jgt r2, r3` verifies:
    1223                 :            :  * * `jgt r2, r3`;
    1224                 :            :  * * `jlt r3, r2` src and dst swapped with each other;
    1225                 :            :  * * `jle r2, r3` with post and jump domains swapped with each other;
    1226                 :            :  * * `jge r3, r2` with all corresponding swaps;
    1227                 :            :  * * immediate versions of everything above where possible,
    1228                 :            :  *   that is, register on the right is an int32 scalar singleton;
    1229                 :            :  * * signed versions of everything above if `also_signed` is true;
    1230                 :            :  *
    1231                 :            :  * Regardless if passed instruction compares with immediate or singleton src
    1232                 :            :  * both cases are generated and tested.
    1233                 :            :  */
    1234                 :            : static int
    1235                 :         40 : verify_comparison(struct verify_instruction_param prm, bool also_signed)
    1236                 :            : {
    1237                 :         40 :         fill_verify_instruction_defaults(&prm);
    1238                 :            : 
    1239         [ +  + ]:         40 :         if (!prm.pre.src.is_defined)
    1240                 :            :                 /* Convert from immediate form to simplify further logic. */
    1241                 :         12 :                 make_comparison_two_register(&prm);
    1242                 :            : 
    1243                 :            :         /* All reachable domains must be defined by this point. */
    1244         [ -  + ]:         40 :         RTE_VERIFY(prm.pre.dst.is_defined);
    1245         [ -  + ]:         40 :         RTE_VERIFY(prm.pre.src.is_defined);
    1246         [ +  + ]:         40 :         if (!prm.post.is_unreachable) {
    1247         [ -  + ]:         31 :                 RTE_VERIFY(prm.post.dst.is_defined);
    1248         [ -  + ]:         31 :                 RTE_VERIFY(prm.post.src.is_defined);
    1249                 :            :         }
    1250         [ +  + ]:         40 :         if (!prm.jump.is_unreachable) {
    1251         [ -  + ]:         27 :                 RTE_VERIFY(prm.jump.dst.is_defined);
    1252         [ +  - ]:         27 :                 RTE_VERIFY(prm.jump.src.is_defined);
    1253                 :            :         }
    1254                 :            : 
    1255         [ +  + ]:        116 :         for (int make_signed = 0; make_signed <= also_signed; ++make_signed) {
    1256         [ +  + ]:         76 :                 if (make_signed)
    1257                 :         36 :                         make_comparison_signed(&prm);
    1258                 :            : 
    1259         [ +  + ]:        228 :                 for (int complement = false; complement <= true; ++complement) {
    1260                 :            : 
    1261         [ +  + ]:        456 :                         for (int converse = false; converse <= true; ++converse) {
    1262                 :            : 
    1263         [ -  + ]:        304 :                                 TEST_ASSERT_SUCCESS(verify_comparison_subcase(prm),
    1264                 :            :                                         "make_signed=%d, complement=%d, converse=%d",
    1265                 :            :                                         make_signed, complement, converse);
    1266                 :            : 
    1267                 :        304 :                                 make_comparison_converse(&prm);
    1268                 :            :                         }
    1269                 :            : 
    1270                 :        152 :                         make_comparison_complement(&prm);
    1271                 :            :                 }
    1272                 :            :         }
    1273                 :            : 
    1274                 :            :         return TEST_SUCCESS;
    1275                 :            : }
    1276                 :            : 
    1277                 :            : 
    1278                 :            : /* TESTS FOR SPECIFIC INSTRUCTIONS */
    1279                 :            : 
    1280                 :            : /* 64-bit addition of immediate to a range. */
    1281                 :            : static int
    1282                 :          1 : test_alu64_add_k(void)
    1283                 :            : {
    1284                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1285                 :            :                 .tested_instruction = {
    1286                 :            :                         .code = (EBPF_ALU64 | BPF_ADD | BPF_K),
    1287                 :            :                         .imm = 17,
    1288                 :            :                 },
    1289                 :            :                 .pre.dst = make_signed_domain(11, 29),
    1290                 :            :                 .post.dst = make_signed_domain(11 + 17, 29 + 17),
    1291                 :            :         });
    1292                 :            : }
    1293                 :            : 
    1294                 :            : /* 64-bit addition of immediate to a pointer range. */
    1295                 :            : static int
    1296                 :          1 : test_alu64_add_k_pointer(void)
    1297                 :            : {
    1298                 :          2 :         return verify_instruction((struct verify_instruction_param){
    1299                 :            :                 .tested_instruction = {
    1300                 :            :                         .code = (EBPF_ALU64 | BPF_ADD | BPF_K),
    1301                 :            :                         .imm = 17,
    1302                 :            :                 },
    1303                 :            :                 .area_size = 256,
    1304                 :            :                 .pre.dst = make_pointer_domain(11, 29),
    1305                 :            :                 .post.dst = make_pointer_domain(11 + 17, 29 + 17),
    1306                 :            :         });
    1307                 :            : }
    1308                 :            : 
    1309                 :            : /* 64-bit addition of pointer to a pointer. */
    1310                 :            : static int
    1311                 :          1 : test_alu64_add_x_pointer_pointer(void)
    1312                 :            : {
    1313                 :          2 :         return verify_instruction((struct verify_instruction_param){
    1314                 :            :                 .tested_instruction = {
    1315                 :            :                         .code = (EBPF_ALU64 | BPF_ADD | BPF_X),
    1316                 :            :                 },
    1317                 :            :                 .area_size = 256,
    1318                 :            :                 .pre.dst = make_pointer_domain(11, 29),
    1319                 :            :                 .pre.src = make_pointer_domain(17, 23),
    1320                 :            :                 .post.dst = unknown,
    1321                 :            :         });
    1322                 :            : }
    1323                 :            : 
    1324                 :            : /* 64-bit addition of scalar to a pointer. */
    1325                 :            : static int
    1326                 :          1 : test_alu64_add_x_pointer_scalar(void)
    1327                 :            : {
    1328                 :          2 :         return verify_instruction((struct verify_instruction_param){
    1329                 :            :                 .tested_instruction = {
    1330                 :            :                         .code = (EBPF_ALU64 | BPF_ADD | BPF_X),
    1331                 :            :                 },
    1332                 :            :                 .area_size = 256,
    1333                 :            :                 .pre.dst = make_pointer_domain(11, 29),
    1334                 :            :                 .pre.src = make_signed_domain(17, 23),
    1335                 :            :                 .post.dst = make_pointer_domain(11 + 17, 29 + 23),
    1336                 :            :         });
    1337                 :            : }
    1338                 :            : 
    1339                 :            : /* 64-bit addition of pointer to a scalar. */
    1340                 :            : static int
    1341                 :          1 : test_alu64_add_x_scalar_pointer(void)
    1342                 :            : {
    1343                 :          2 :         return verify_instruction((struct verify_instruction_param){
    1344                 :            :                 .tested_instruction = {
    1345                 :            :                         .code = (EBPF_ALU64 | BPF_ADD | BPF_X),
    1346                 :            :                 },
    1347                 :            :                 .area_size = 256,
    1348                 :            :                 .pre.dst = make_signed_domain(11, 29),
    1349                 :            :                 .pre.src = make_pointer_domain(17, 23),
    1350                 :            :                 .post.dst = make_pointer_domain(11 + 17, 29 + 23),
    1351                 :            :         });
    1352                 :            : }
    1353                 :            : 
    1354                 :            : /* 64-bit addition of scalar to a scalar. */
    1355                 :            : static int
    1356                 :          1 : test_alu64_add_x_scalar_scalar(void)
    1357                 :            : {
    1358                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1359                 :            :                 .tested_instruction = {
    1360                 :            :                         .code = (EBPF_ALU64 | BPF_ADD | BPF_X),
    1361                 :            :                 },
    1362                 :            :                 .area_size = 256,
    1363                 :            :                 .pre.dst = make_signed_domain(11, 29),
    1364                 :            :                 .pre.src = make_signed_domain(17, 23),
    1365                 :            :                 .post.dst = make_signed_domain(11 + 17, 29 + 23),
    1366                 :            :         });
    1367                 :            : }
    1368                 :            : 
    1369                 :            : /* 64-bit bitwise AND between a scalar range and immediate. */
    1370                 :            : static int
    1371                 :          1 : test_alu64_and_k(void)
    1372                 :            : {
    1373                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1374                 :            :                 .tested_instruction = {
    1375                 :            :                         .code = (EBPF_ALU64 | BPF_AND | BPF_K),
    1376                 :            :                         .imm = 5,
    1377                 :            :                 },
    1378                 :            :                 .pre.dst = make_signed_domain(6, 8),
    1379                 :            :                 .post.dst = make_signed_domain(0, 7),
    1380                 :            :         });
    1381                 :            : }
    1382                 :            : 
    1383                 :            : /* 64-bit division and modulo of UINT64_MAX*2/3. */
    1384                 :            : static int
    1385                 :          1 : test_alu64_div_mod_big_constant(void)
    1386                 :            : {
    1387                 :            :         const uint64_t dividend = UINT64_MAX / 3 * 2;
    1388                 :            :         static const uint64_t divisors[] = {
    1389                 :            :                 1,
    1390                 :            :                 2,
    1391                 :            :                 3,
    1392                 :            :                 UINT64_MAX / 3,
    1393                 :            :                 INT64_MAX,
    1394                 :            :                 INT64_MIN,
    1395                 :            :                 UINT64_MAX / 3 * 2,
    1396                 :            :                 UINT64_MAX / 4 * 3,
    1397                 :            :                 UINT64_MAX,
    1398                 :            :         };
    1399         [ +  + ]:         10 :         for (int index = 0; index != RTE_DIM(divisors); ++index) {
    1400                 :          9 :                 const uint64_t divisor = divisors[index];
    1401                 :            : 
    1402         [ -  + ]:          9 :                 TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1403                 :            :                         .tested_instruction = {
    1404                 :            :                                 .code = (EBPF_ALU64 | BPF_DIV | BPF_X),
    1405                 :            :                         },
    1406                 :            :                         .pre.dst = make_singleton_domain(dividend),
    1407                 :            :                         .pre.src = make_singleton_domain(divisor),
    1408                 :            :                         .post.dst = make_singleton_domain(dividend / divisor),
    1409                 :            :                 }), "(EBPF_ALU64 | BPF_DIV | BPF_X) check, index=%d", index);
    1410                 :            : 
    1411         [ -  + ]:          9 :                 TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1412                 :            :                         .tested_instruction = {
    1413                 :            :                                 .code = (EBPF_ALU64 | BPF_MOD | BPF_X),
    1414                 :            :                         },
    1415                 :            :                         .pre.dst = make_singleton_domain(dividend),
    1416                 :            :                         .pre.src = make_singleton_domain(divisor),
    1417                 :            :                         .post.dst = make_singleton_domain(dividend % divisor),
    1418                 :            :                 }), "(EBPF_ALU64 | BPF_MOD | BPF_X) check, index=%d", index);
    1419                 :            :         }
    1420                 :            : 
    1421                 :            :         return TEST_SUCCESS;
    1422                 :            : }
    1423                 :            : 
    1424                 :            : /* 64-bit division and modulo of UINT64_MAX/3..UINT64_MAX*2/3 by a constant. */
    1425                 :            : static int
    1426                 :          1 : test_alu64_div_mod_big_range(void)
    1427                 :            : {
    1428                 :            :         const uint64_t dividend_first = UINT64_MAX / 3;
    1429                 :            :         const uint64_t dividend_last = UINT64_MAX / 3 * 2;
    1430                 :            :         static const uint64_t divisors[] = {
    1431                 :            :                 1,
    1432                 :            :                 2,
    1433                 :            :                 3,
    1434                 :            :                 UINT64_MAX / 3,
    1435                 :            :                 INT64_MAX,
    1436                 :            :                 INT64_MIN,
    1437                 :            :                 UINT64_MAX / 3 * 2,
    1438                 :            :                 UINT64_MAX / 4 * 3,
    1439                 :            :                 UINT64_MAX,
    1440                 :            :         };
    1441         [ +  + ]:         10 :         for (int index = 0; index != RTE_DIM(divisors); ++index) {
    1442                 :          9 :                 const uint64_t divisor = divisors[index];
    1443                 :            : 
    1444         [ -  + ]:          9 :                 TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1445                 :            :                         .tested_instruction = {
    1446                 :            :                                 .code = (EBPF_ALU64 | BPF_DIV | BPF_X),
    1447                 :            :                         },
    1448                 :            :                         .pre.dst = make_unsigned_domain(dividend_first, dividend_last),
    1449                 :            :                         .pre.src = make_singleton_domain(divisor),
    1450                 :            :                         .post.dst = make_unsigned_domain(0, dividend_last),
    1451                 :            :                 }), "(EBPF_ALU64 | BPF_DIV | BPF_X) check, index=%d", index);
    1452                 :            : 
    1453         [ -  + ]:          9 :                 TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1454                 :            :                         .tested_instruction = {
    1455                 :            :                                 .code = (EBPF_ALU64 | BPF_MOD | BPF_X),
    1456                 :            :                         },
    1457                 :            :                         .pre.dst = make_unsigned_domain(dividend_first, dividend_last),
    1458                 :            :                         .pre.src = make_singleton_domain(divisor),
    1459                 :            :                         .post.dst = make_unsigned_domain(0, RTE_MIN(dividend_last, divisor - 1)),
    1460                 :            :                 }), "(EBPF_ALU64 | BPF_MOD | BPF_X) check, index=%d", index);
    1461                 :            :         }
    1462                 :            : 
    1463                 :            :         return TEST_SUCCESS;
    1464                 :            : }
    1465                 :            : 
    1466                 :            : /* 64-bit division and modulo of INT64_MIN by -1. */
    1467                 :            : static int
    1468                 :          1 : test_alu64_div_mod_overflow(void)
    1469                 :            : {
    1470         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1471                 :            :                 .tested_instruction = {
    1472                 :            :                         .code = (EBPF_ALU64 | BPF_DIV | BPF_K),
    1473                 :            :                         .imm = -1,
    1474                 :            :                 },
    1475                 :            :                 .pre.dst = make_singleton_domain(INT64_MIN),
    1476                 :            :                 .post.dst = make_singleton_domain(0),
    1477                 :            :         }), "(EBPF_ALU64 | BPF_DIV | BPF_K) check");
    1478                 :            : 
    1479         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1480                 :            :                 .tested_instruction = {
    1481                 :            :                         .code = (EBPF_ALU64 | BPF_DIV | BPF_X),
    1482                 :            :                 },
    1483                 :            :                 .pre.dst = make_singleton_domain(INT64_MIN),
    1484                 :            :                 .pre.src = make_singleton_domain(-1),
    1485                 :            :                 .post.dst = make_singleton_domain(0),
    1486                 :            :         }), "(EBPF_ALU64 | BPF_DIV | BPF_X) check");
    1487                 :            : 
    1488         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1489                 :            :                 .tested_instruction = {
    1490                 :            :                         .code = (EBPF_ALU64 | BPF_MOD | BPF_K),
    1491                 :            :                         .imm = -1,
    1492                 :            :                 },
    1493                 :            :                 .pre.dst = make_singleton_domain(INT64_MIN),
    1494                 :            :                 .post.dst = make_singleton_domain(INT64_MIN),
    1495                 :            :         }), "(EBPF_ALU64 | BPF_MOD | BPF_K) check");
    1496                 :            : 
    1497         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1498                 :            :                 .tested_instruction = {
    1499                 :            :                         .code = (EBPF_ALU64 | BPF_MOD | BPF_X),
    1500                 :            :                 },
    1501                 :            :                 .pre.dst = make_singleton_domain(INT64_MIN),
    1502                 :            :                 .pre.src = make_singleton_domain(-1),
    1503                 :            :                 .post.dst = make_singleton_domain(INT64_MIN),
    1504                 :            :         }), "(EBPF_ALU64 | BPF_MOD | BPF_X) check");
    1505                 :            : 
    1506                 :          1 :         return TEST_SUCCESS;
    1507                 :            : }
    1508                 :            : 
    1509                 :            : /* 64-bit left shift by 63. */
    1510                 :            : static int
    1511                 :          1 : test_alu64_lsh_63(void)
    1512                 :            : {
    1513                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1514                 :            :                 .tested_instruction = {
    1515                 :            :                         .code = (EBPF_ALU64 | BPF_LSH | BPF_K),
    1516                 :            :                         .imm = 63,
    1517                 :            :                 },
    1518                 :            :                 .pre.dst = make_signed_domain(3, 5),
    1519                 :            :                 .post.dst = unknown,
    1520                 :            :         });
    1521                 :            : }
    1522                 :            : 
    1523                 :            : /* 64-bit multiplication of constant and immediate with overflow. */
    1524                 :            : static int
    1525                 :          1 : test_alu64_mul_k_overflow(void)
    1526                 :            : {
    1527                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1528                 :            :                 .tested_instruction = {
    1529                 :            :                         .code = (EBPF_ALU64 | BPF_MUL | BPF_K),
    1530                 :            :                         .imm = 0x12345678,
    1531                 :            :                 },
    1532                 :            :                 .pre.dst = make_singleton_domain(0x9876543210),
    1533                 :            :                 .post.dst = make_singleton_domain(0x9876543210u * 0x12345678),
    1534                 :            :         });
    1535                 :            : }
    1536                 :            : 
    1537                 :            : /* 64-bit mul of small scalar range and immediate. */
    1538                 :            : static int
    1539                 :          1 : test_alu64_mul_k_range_small(void)
    1540                 :            : {
    1541                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1542                 :            :                 .tested_instruction = {
    1543                 :            :                         .code = (EBPF_ALU64 | BPF_MUL | BPF_K),
    1544                 :            :                         .imm = 11,
    1545                 :            :                 },
    1546                 :            :                 .pre.dst = make_unsigned_domain(17, 29),
    1547                 :            :                 .post.dst = make_unsigned_domain(17 * 11, 29 * 11),
    1548                 :            :         });
    1549                 :            : }
    1550                 :            : 
    1551                 :            : /* 64-bit negation when interval first element is INT64_MIN. */
    1552                 :            : static int
    1553                 :          1 : test_alu64_neg_int64min_first(void)
    1554                 :            : {
    1555                 :            :         static const int64_t other_values[] = {
    1556                 :            :                 INT64_MIN,
    1557                 :            :                 INT64_MIN + 1,
    1558                 :            :                 INT64_MIN + 13,
    1559                 :            :                 -17,
    1560                 :            :                 -1,
    1561                 :            :                 0,
    1562                 :            :                 1,
    1563                 :            :                 19,
    1564                 :            :                 INT64_MAX - 23,
    1565                 :            :                 INT64_MAX - 1,
    1566                 :            :                 INT64_MAX,
    1567                 :            :         };
    1568         [ +  + ]:         12 :         for (int other_index = 0; other_index != RTE_DIM(other_values); ++other_index) {
    1569                 :         11 :                 const int64_t other_value = other_values[other_index];
    1570   [ +  +  -  + ]:         11 :                 TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1571                 :            :                         .tested_instruction = {
    1572                 :            :                                 .code = (EBPF_ALU64 | BPF_NEG),
    1573                 :            :                         },
    1574                 :            :                         .pre.dst = make_signed_domain(INT64_MIN, other_value),
    1575                 :            :                         .post.dst = other_value > 0 ? unknown :
    1576                 :            :                                 make_unsigned_domain(-(uint64_t)other_value, INT64_MIN),
    1577                 :            :                 }), "other_index=%d", other_index);
    1578                 :            :         }
    1579                 :            :         return TEST_SUCCESS;
    1580                 :            : }
    1581                 :            : 
    1582                 :            : /* 64-bit negation when interval last element is INT64_MIN. */
    1583                 :            : static int
    1584                 :          1 : test_alu64_neg_int64min_last(void)
    1585                 :            : {
    1586                 :            :         static const uint64_t other_values[] = {
    1587                 :            :                 0,
    1588                 :            :                 1,
    1589                 :            :                 19,
    1590                 :            :                 INT64_MAX - 23,
    1591                 :            :                 INT64_MAX - 1,
    1592                 :            :                 INT64_MAX,
    1593                 :            :                 INT64_MIN,
    1594                 :            :         };
    1595         [ +  + ]:          8 :         for (int other_index = 0; other_index != RTE_DIM(other_values); ++other_index) {
    1596                 :          7 :                 const int64_t other_value = other_values[other_index];
    1597         [ -  + ]:          7 :                 TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1598                 :            :                         .tested_instruction = {
    1599                 :            :                                 .code = (EBPF_ALU64 | BPF_NEG),
    1600                 :            :                         },
    1601                 :            :                         .pre.dst = make_unsigned_domain(other_value, INT64_MIN),
    1602                 :            :                         .post.dst = make_signed_domain(INT64_MIN, -(uint64_t)other_value),
    1603                 :            :                 }), "other_index=%d", other_index);
    1604                 :            :         }
    1605                 :            :         return TEST_SUCCESS;
    1606                 :            : }
    1607                 :            : 
    1608                 :            : /* 64-bit negation when interval first element is zero. */
    1609                 :            : static int
    1610                 :          1 : test_alu64_neg_zero_first(void)
    1611                 :            : {
    1612                 :            :         static const uint64_t other_values[] = {
    1613                 :            :                 0,
    1614                 :            :                 1,
    1615                 :            :                 19,
    1616                 :            :                 INT64_MAX - 23,
    1617                 :            :                 INT64_MAX - 1,
    1618                 :            :                 INT64_MAX,
    1619                 :            :                 INT64_MIN,
    1620                 :            :                 INT64_MIN + 1,
    1621                 :            :                 INT64_MIN + 13,
    1622                 :            :                 -17,
    1623                 :            :                 -1,
    1624                 :            :         };
    1625         [ +  + ]:         12 :         for (int other_index = 0; other_index != RTE_DIM(other_values); ++other_index) {
    1626                 :         11 :                 const uint64_t other_value = other_values[other_index];
    1627   [ +  +  -  + ]:         11 :                 TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1628                 :            :                         .tested_instruction = {
    1629                 :            :                                 .code = (EBPF_ALU64 | BPF_NEG),
    1630                 :            :                         },
    1631                 :            :                         .pre.dst = make_unsigned_domain(0, other_value),
    1632                 :            :                         .post.dst = other_value > (uint64_t)INT64_MIN ? unknown :
    1633                 :            :                                 make_signed_domain(-(uint64_t)other_value, 0),
    1634                 :            :                 }), "other_index=%d", other_index);
    1635                 :            :         }
    1636                 :            :         return TEST_SUCCESS;
    1637                 :            : }
    1638                 :            : 
    1639                 :            : /* 64-bit negation when interval last element is zero. */
    1640                 :            : static int
    1641                 :          1 : test_alu64_neg_zero_last(void)
    1642                 :            : {
    1643                 :            :         static const int64_t other_values[] = {
    1644                 :            :                 INT64_MIN,
    1645                 :            :                 INT64_MIN + 1,
    1646                 :            :                 INT64_MIN + 13,
    1647                 :            :                 -17,
    1648                 :            :                 -1,
    1649                 :            :                 0,
    1650                 :            :         };
    1651         [ +  + ]:          7 :         for (int other_index = 0; other_index != RTE_DIM(other_values); ++other_index) {
    1652                 :          6 :                 const int64_t other_value = other_values[other_index];
    1653         [ -  + ]:          6 :                 TEST_ASSERT_SUCCESS(verify_instruction((struct verify_instruction_param){
    1654                 :            :                         .tested_instruction = {
    1655                 :            :                                 .code = (EBPF_ALU64 | BPF_NEG),
    1656                 :            :                         },
    1657                 :            :                         .pre.dst = make_signed_domain(other_value, 0),
    1658                 :            :                         .post.dst = make_unsigned_domain(0, -(uint64_t)other_value),
    1659                 :            :                 }), "other_index=%d", other_index);
    1660                 :            :         }
    1661                 :            : 
    1662                 :            :         return TEST_SUCCESS;
    1663                 :            : }
    1664                 :            : 
    1665                 :            : /* 64-bit bitwise OR between a positive scalar range and negative immediate. */
    1666                 :            : static int
    1667                 :          1 : test_alu64_or_k_negative(void)
    1668                 :            : {
    1669                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1670                 :            :                 .tested_instruction = {
    1671                 :            :                         .code = (EBPF_ALU64 | BPF_OR | BPF_K),
    1672                 :            :                         .imm = -2,
    1673                 :            :                 },
    1674                 :            :                 .pre.dst = make_signed_domain(5, 6),
    1675                 :            :                 .post.dst = make_signed_domain(-2, -1),
    1676                 :            :         });
    1677                 :            : }
    1678                 :            : 
    1679                 :            : /* 64-bit bitwise OR between a positive scalar range and positive immediate. */
    1680                 :            : static int
    1681                 :          1 : test_alu64_or_k_positive(void)
    1682                 :            : {
    1683                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1684                 :            :                 .tested_instruction = {
    1685                 :            :                         .code = (EBPF_ALU64 | BPF_OR | BPF_K),
    1686                 :            :                         .imm = 2,
    1687                 :            :                 },
    1688                 :            :                 .pre.dst = make_signed_domain(5, 6),
    1689                 :            :                 .post.dst = make_signed_domain(5, 7),
    1690                 :            :         });
    1691                 :            : }
    1692                 :            : 
    1693                 :            : /* 64-bit difference between two negative ranges.. */
    1694                 :            : static int
    1695                 :          1 : test_alu64_sub_x_src_signed_max_zero(void)
    1696                 :            : {
    1697                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1698                 :            :                 .tested_instruction = {
    1699                 :            :                         .code = (EBPF_ALU64 | BPF_SUB | BPF_X),
    1700                 :            :                 },
    1701                 :            :                 .pre.dst = make_signed_domain(INT64_MIN, 0),
    1702                 :            :                 .pre.src = make_signed_domain(INT64_MIN, 0),
    1703                 :            :                 .post.dst = unknown,
    1704                 :            :         });
    1705                 :            : }
    1706                 :            : 
    1707                 :            : /* 64-bit bitwise XOR between a negative scalar range and zero immediate. */
    1708                 :            : static int
    1709                 :          1 : test_alu64_xor_k_negative(void)
    1710                 :            : {
    1711                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1712                 :            :                 .tested_instruction = {
    1713                 :            :                         .code = (EBPF_ALU64 | BPF_XOR | BPF_K),
    1714                 :            :                         .imm = 0,
    1715                 :            :                 },
    1716                 :            :                 .pre.dst = make_signed_domain(INT64_MIN, 0),
    1717                 :            :                 .post.dst = unknown,
    1718                 :            :         });
    1719                 :            : }
    1720                 :            : 
    1721                 :            : /* Jump if greater than immediate. */
    1722                 :            : static int
    1723                 :          1 : test_jmp64_jeq_k(void)
    1724                 :            : {
    1725                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1726                 :            :                 .tested_instruction = {
    1727                 :            :                         .code = (BPF_JMP | BPF_JGT | BPF_K),
    1728                 :            :                         .imm = 0,
    1729                 :            :                 },
    1730                 :            :                 .pre.dst = make_unsigned_domain(0, 1),
    1731                 :            :                 .post.dst = make_singleton_domain(0),
    1732                 :            :                 .jump.dst = make_singleton_domain(1),
    1733                 :            :         });
    1734                 :            : }
    1735                 :            : 
    1736                 :            : /* Jump if signed less than another register. */
    1737                 :            : static int
    1738                 :          1 : test_jmp64_jslt_x(void)
    1739                 :            : {
    1740                 :          1 :         return verify_instruction((struct verify_instruction_param){
    1741                 :            :                 .tested_instruction = {
    1742                 :            :                         .code = (BPF_JMP | EBPF_JSLT | BPF_X),
    1743                 :            :                 },
    1744                 :            :                 .pre.dst = make_signed_domain(-3, 3),
    1745                 :            :                 .pre.src = make_signed_domain(0, 0),
    1746                 :            :                 .post.dst = make_signed_domain(0, 3),
    1747                 :            :                 .jump.dst = make_signed_domain(-3, -1),
    1748                 :            :         });
    1749                 :            : }
    1750                 :            : 
    1751                 :            : /* Jump on ordering comparisons with potential bound overflow. */
    1752                 :            : static int
    1753                 :          1 : test_jmp64_ordering_overflow(void)
    1754                 :            : {
    1755                 :            :         /* In this test signed and unsigned cases are spelled out explicitly. */
    1756                 :            :         const bool also_signed = false;
    1757                 :            : 
    1758         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1759                 :            :                 .tested_instruction = {
    1760                 :            :                         .code = (BPF_JMP | EBPF_JSLT | BPF_X),
    1761                 :            :                 },
    1762                 :            :                 .pre.dst = make_singleton_domain(42),
    1763                 :            :                 .pre.src = make_singleton_domain(INT64_MIN),
    1764                 :            :                 .jump = unreachable,
    1765                 :            :         }, also_signed), "signed less than INT64_MIN");
    1766                 :            : 
    1767         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1768                 :            :                 .tested_instruction = {
    1769                 :            :                         .code = (BPF_JMP | EBPF_JSGT | BPF_X),
    1770                 :            :                 },
    1771                 :            :                 .pre.dst = make_singleton_domain(42),
    1772                 :            :                 .pre.src = make_singleton_domain(INT64_MAX),
    1773                 :            :                 .jump = unreachable,
    1774                 :            :         }, also_signed), "signed greater than INT64_MAX");
    1775                 :            : 
    1776         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1777                 :            :                 .tested_instruction = {
    1778                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_X),
    1779                 :            :                 },
    1780                 :            :                 .pre.dst = make_singleton_domain(42),
    1781                 :            :                 .pre.src = make_singleton_domain(0),
    1782                 :            :                 .jump = unreachable,
    1783                 :            :         }, also_signed), "unsigned less than zero");
    1784                 :            : 
    1785         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1786                 :            :                 .tested_instruction = {
    1787                 :            :                         .code = (BPF_JMP | BPF_JGT | BPF_X),
    1788                 :            :                 },
    1789                 :            :                 .pre.dst = make_singleton_domain(42),
    1790                 :            :                 .pre.src = make_singleton_domain(UINT64_MAX),
    1791                 :            :                 .jump = unreachable,
    1792                 :            :         }, also_signed), "unsigned greater than UINT64_MAX");
    1793                 :            : 
    1794                 :          1 :         return TEST_SUCCESS;
    1795                 :            : }
    1796                 :            : 
    1797                 :            : /* Jump on ordering comparisons between two ranges. */
    1798                 :            : static int
    1799                 :          1 : test_jmp64_ordering_ranges(void)
    1800                 :            : {
    1801                 :            :         /* All ranges used are valid for both signed and unsigned comparisons. */
    1802                 :            :         const bool also_signed = true;
    1803                 :            : 
    1804                 :            :         /*
    1805                 :            :          *               20 ---- dst ---- 60
    1806                 :            :          * 0 - src - 10
    1807                 :            :          */
    1808                 :            : 
    1809         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1810                 :            :                 .tested_instruction = {
    1811                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_X),
    1812                 :            :                 },
    1813                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1814                 :            :                 .pre.src = make_signed_domain(0, 10),
    1815                 :            :                 .jump = unreachable,
    1816                 :            :         }, also_signed), "strict, dst range strongly greater than src range");
    1817                 :            : 
    1818         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1819                 :            :                 .tested_instruction = {
    1820                 :            :                         .code = (BPF_JMP | EBPF_JLE | BPF_X),
    1821                 :            :                 },
    1822                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1823                 :            :                 .pre.src = make_signed_domain(0, 10),
    1824                 :            :                 .jump = unreachable,
    1825                 :            :         }, also_signed), "non-strict, dst range strongly greater than src range");
    1826                 :            : 
    1827                 :            :         /*
    1828                 :            :          *     20 ---- dst ---- 60
    1829                 :            :          * 10 -- src -- 40
    1830                 :            :          */
    1831                 :            : 
    1832         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1833                 :            :                 .tested_instruction = {
    1834                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_X),
    1835                 :            :                 },
    1836                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1837                 :            :                 .pre.src = make_signed_domain(10, 40),
    1838                 :            :                 .jump.dst = make_signed_domain(20, 39),
    1839                 :            :                 .jump.src = make_signed_domain(21, 40),
    1840                 :            :         }, also_signed), "strict, dst range weakly greater than src range");
    1841                 :            : 
    1842         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1843                 :            :                 .tested_instruction = {
    1844                 :            :                         .code = (BPF_JMP | EBPF_JLE | BPF_X),
    1845                 :            :                 },
    1846                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1847                 :            :                 .pre.src = make_signed_domain(10, 40),
    1848                 :            :                 .jump.dst = make_signed_domain(20, 40),
    1849                 :            :                 .jump.src = make_signed_domain(20, 40),
    1850                 :            :         }, also_signed), "non-strict, dst range weakly greater than src range");
    1851                 :            : 
    1852                 :            :         /*
    1853                 :            :          *     20 ---- dst ---- 60
    1854                 :            :          * 10 -------- src -------- 70
    1855                 :            :          */
    1856                 :            : 
    1857         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1858                 :            :                 .tested_instruction = {
    1859                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_X),
    1860                 :            :                 },
    1861                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1862                 :            :                 .pre.src = make_signed_domain(10, 70),
    1863                 :            :                 .post.src = make_signed_domain(10, 60),
    1864                 :            :                 .jump.src = make_signed_domain(21, 70),
    1865                 :            :         }, also_signed), "strict, dst range included in src range");
    1866                 :            : 
    1867         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1868                 :            :                 .tested_instruction = {
    1869                 :            :                         .code = (BPF_JMP | EBPF_JLE | BPF_X),
    1870                 :            :                 },
    1871                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1872                 :            :                 .pre.src = make_signed_domain(10, 70),
    1873                 :            :                 .post.src = make_signed_domain(10, 59),
    1874                 :            :                 .jump.src = make_signed_domain(20, 70),
    1875                 :            :         }, also_signed), "non-strict, dst range included in src range");
    1876                 :            : 
    1877                 :            :         /*
    1878                 :            :          *     20 ---- dst ---- 60
    1879                 :            :          *        30 - src - 50
    1880                 :            :          */
    1881                 :            : 
    1882         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1883                 :            :                 .tested_instruction = {
    1884                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_X),
    1885                 :            :                 },
    1886                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1887                 :            :                 .pre.src = make_signed_domain(30, 50),
    1888                 :            :                 .post.dst = make_signed_domain(30, 60),
    1889                 :            :                 .jump.dst = make_signed_domain(20, 49),
    1890                 :            :         }, also_signed), "strict, dst range includes src range");
    1891                 :            : 
    1892         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1893                 :            :                 .tested_instruction = {
    1894                 :            :                         .code = (BPF_JMP | EBPF_JLE | BPF_X),
    1895                 :            :                 },
    1896                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1897                 :            :                 .pre.src = make_signed_domain(30, 50),
    1898                 :            :                 .post.dst = make_signed_domain(31, 60),
    1899                 :            :                 .jump.dst = make_signed_domain(20, 50),
    1900                 :            :         }, also_signed), "non-strict, dst range includes src range");
    1901                 :            : 
    1902                 :            :         /*
    1903                 :            :          *     20 ---- dst ---- 60
    1904                 :            :          *             40 -- src -- 70
    1905                 :            :          */
    1906                 :            : 
    1907         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1908                 :            :                 .tested_instruction = {
    1909                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_X),
    1910                 :            :                 },
    1911                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1912                 :            :                 .pre.src = make_signed_domain(40, 70),
    1913                 :            :                 .post.dst = make_signed_domain(40, 60),
    1914                 :            :                 .post.src = make_signed_domain(40, 60),
    1915                 :            :         }, also_signed), "strict, dst range weakly less than src range");
    1916                 :            : 
    1917         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1918                 :            :                 .tested_instruction = {
    1919                 :            :                         .code = (BPF_JMP | EBPF_JLE | BPF_X),
    1920                 :            :                 },
    1921                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1922                 :            :                 .pre.src = make_signed_domain(40, 70),
    1923                 :            :                 .post.dst = make_signed_domain(41, 60),
    1924                 :            :                 .post.src = make_signed_domain(40, 59),
    1925                 :            :         }, also_signed), "non-strict, dst range weakly less than src range");
    1926                 :            : 
    1927                 :            :         /*
    1928                 :            :          *     20 ---- dst ---- 60
    1929                 :            :          *                          70 - src - 80
    1930                 :            :          */
    1931                 :            : 
    1932         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1933                 :            :                 .tested_instruction = {
    1934                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_X),
    1935                 :            :                 },
    1936                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1937                 :            :                 .pre.src = make_signed_domain(70, 80),
    1938                 :            :                 .post = unreachable,
    1939                 :            :         }, also_signed), "strict, dst range strongly less than src range");
    1940                 :            : 
    1941         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1942                 :            :                 .tested_instruction = {
    1943                 :            :                         .code = (BPF_JMP | EBPF_JLE | BPF_X),
    1944                 :            :                 },
    1945                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1946                 :            :                 .pre.src = make_signed_domain(70, 80),
    1947                 :            :                 .post = unreachable,
    1948                 :            :         }, also_signed), "non-strict, dst range strongly less than src range");
    1949                 :            : 
    1950                 :          1 :         return TEST_SUCCESS;
    1951                 :            : }
    1952                 :            : 
    1953                 :            : /* Jump on ordering comparisons with singleton inside the range. */
    1954                 :            : static int
    1955                 :          1 : test_jmp64_ordering_singleton_inside(void)
    1956                 :            : {
    1957                 :            :         /* All ranges used are valid for both signed and unsigned comparisons. */
    1958                 :            :         const bool also_signed = true;
    1959                 :            : 
    1960                 :            :         /*
    1961                 :            :          *     20 ---- dst ---- 60
    1962                 :            :          *             imm
    1963                 :            :          */
    1964                 :            : 
    1965         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1966                 :            :                 .tested_instruction = {
    1967                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_K),
    1968                 :            :                         .imm = 40,
    1969                 :            :                 },
    1970                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1971                 :            :                 .post.dst = make_signed_domain(40, 60),
    1972                 :            :                 .jump.dst = make_signed_domain(20, 39),
    1973                 :            :         }, also_signed), "(BPF_JMP | EBPF_JLT | BPF_K) check");
    1974                 :            : 
    1975         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1976                 :            :                 .tested_instruction = {
    1977                 :            :                         .code = (BPF_JMP | BPF_JGT | BPF_K),
    1978                 :            :                         .imm = 40,
    1979                 :            :                 },
    1980                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1981                 :            :                 .post.dst = make_signed_domain(20, 40),
    1982                 :            :                 .jump.dst = make_signed_domain(41, 60),
    1983                 :            :         }, also_signed), "(BPF_JMP | EBPF_JGT | BPF_K) check");
    1984                 :            : 
    1985         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1986                 :            :                 .tested_instruction = {
    1987                 :            :                         .code = (BPF_JMP | EBPF_JLE | BPF_K),
    1988                 :            :                         .imm = 40,
    1989                 :            :                 },
    1990                 :            :                 .pre.dst = make_signed_domain(20, 60),
    1991                 :            :                 .post.dst = make_signed_domain(41, 60),
    1992                 :            :                 .jump.dst = make_signed_domain(20, 40),
    1993                 :            :         }, also_signed), "(BPF_JMP | EBPF_JLE | BPF_K) check");
    1994                 :            : 
    1995         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    1996                 :            :                 .tested_instruction = {
    1997                 :            :                         .code = (BPF_JMP | BPF_JGE | BPF_K),
    1998                 :            :                         .imm = 40,
    1999                 :            :                 },
    2000                 :            :                 .pre.dst = make_signed_domain(20, 60),
    2001                 :            :                 .post.dst = make_signed_domain(20, 39),
    2002                 :            :                 .jump.dst = make_signed_domain(40, 60),
    2003                 :            :         }, also_signed), "(BPF_JMP | EBPF_JGE | BPF_K) check");
    2004                 :            : 
    2005                 :          1 :         return TEST_SUCCESS;
    2006                 :            : }
    2007                 :            : 
    2008                 :            : /* Jump on ordering comparisons with singleton outside the range. */
    2009                 :            : static int
    2010                 :          1 : test_jmp64_ordering_singleton_outside(void)
    2011                 :            : {
    2012                 :            :         /* All ranges used are valid for both signed and unsigned comparisons. */
    2013                 :            :         const bool also_signed = true;
    2014                 :            : 
    2015                 :            :         /*
    2016                 :            :          *       20 ---- dst ---- 60
    2017                 :            :          *  imm
    2018                 :            :          */
    2019                 :            : 
    2020         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2021                 :            :                 .tested_instruction = {
    2022                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_K),
    2023                 :            :                         .imm = 10,
    2024                 :            :                 },
    2025                 :            :                 .pre.dst = make_signed_domain(20, 60),
    2026                 :            :                 .jump = unreachable,
    2027                 :            :         }, also_signed), "(BPF_JMP | EBPF_JLT | BPF_K) check, range greater than imm");
    2028                 :            : 
    2029         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2030                 :            :                 .tested_instruction = {
    2031                 :            :                         .code = (BPF_JMP | EBPF_JLE | BPF_K),
    2032                 :            :                         .imm = 10,
    2033                 :            :                 },
    2034                 :            :                 .pre.dst = make_signed_domain(20, 60),
    2035                 :            :                 .jump = unreachable,
    2036                 :            :         }, also_signed), "(BPF_JMP | EBPF_JLE | BPF_K) check, range greater than imm");
    2037                 :            : 
    2038         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2039                 :            :                 .tested_instruction = {
    2040                 :            :                         .code = (BPF_JMP | BPF_JGT | BPF_K),
    2041                 :            :                         .imm = 10,
    2042                 :            :                 },
    2043                 :            :                 .pre.dst = make_signed_domain(20, 60),
    2044                 :            :                 .post = unreachable,
    2045                 :            :         }, also_signed), "(BPF_JMP | EBPF_JGT | BPF_K) check, range greater than imm");
    2046                 :            : 
    2047         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2048                 :            :                 .tested_instruction = {
    2049                 :            :                         .code = (BPF_JMP | BPF_JGE | BPF_K),
    2050                 :            :                         .imm = 10,
    2051                 :            :                 },
    2052                 :            :                 .pre.dst = make_signed_domain(20, 60),
    2053                 :            :                 .post = unreachable,
    2054                 :            :         }, also_signed), "(BPF_JMP | EBPF_JGE | BPF_K) check, range greater than imm");
    2055                 :            : 
    2056                 :            :         /*
    2057                 :            :          *       20 ---- dst ---- 60
    2058                 :            :          *                            imm
    2059                 :            :          */
    2060                 :            : 
    2061         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2062                 :            :                 .tested_instruction = {
    2063                 :            :                         .code = (BPF_JMP | EBPF_JLT | BPF_K),
    2064                 :            :                         .imm = 70,
    2065                 :            :                 },
    2066                 :            :                 .pre.dst = make_signed_domain(20, 60),
    2067                 :            :                 .post = unreachable,
    2068                 :            :         }, also_signed), "(BPF_JMP | EBPF_JLT | BPF_K) check, range less than imm");
    2069                 :            : 
    2070         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2071                 :            :                 .tested_instruction = {
    2072                 :            :                         .code = (BPF_JMP | EBPF_JLE | BPF_K),
    2073                 :            :                         .imm = 70,
    2074                 :            :                 },
    2075                 :            :                 .pre.dst = make_signed_domain(20, 60),
    2076                 :            :                 .post = unreachable,
    2077                 :            :         }, also_signed), "(BPF_JMP | EBPF_JLE | BPF_K) check, range less than imm");
    2078                 :            : 
    2079         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2080                 :            :                 .tested_instruction = {
    2081                 :            :                         .code = (BPF_JMP | BPF_JGT | BPF_K),
    2082                 :            :                         .imm = 70,
    2083                 :            :                 },
    2084                 :            :                 .pre.dst = make_signed_domain(20, 60),
    2085                 :            :                 .jump = unreachable,
    2086                 :            :         }, also_signed), "(BPF_JMP | EBPF_JGT | BPF_K) check, range less than imm");
    2087                 :            : 
    2088         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2089                 :            :                 .tested_instruction = {
    2090                 :            :                         .code = (BPF_JMP | BPF_JGE | BPF_K),
    2091                 :            :                         .imm = 70,
    2092                 :            :                 },
    2093                 :            :                 .pre.dst = make_signed_domain(20, 60),
    2094                 :            :                 .jump = unreachable,
    2095                 :            :         }, also_signed), "(BPF_JMP | EBPF_JGE | BPF_K) check, range less than imm");
    2096                 :            : 
    2097                 :          1 :         return TEST_SUCCESS;
    2098                 :            : }
    2099                 :            : 
    2100                 :            : /* Jump on ordering comparisons with ranges "touching" each other. */
    2101                 :            : static int
    2102                 :          1 : test_jmp64_ordering_touching(void)
    2103                 :            : {
    2104                 :            :         /* All ranges used are valid for both signed and unsigned comparisons. */
    2105                 :            :         const bool also_signed = true;
    2106                 :            : 
    2107         [ +  + ]:          4 :         for (int overlap = 0; overlap != 3; ++overlap) {
    2108                 :            : 
    2109                 :            :                 /*
    2110                 :            :                  *                  20 - dst - 30
    2111                 :            :                  * 10 - src - (19 + overlap)
    2112                 :            :                  */
    2113                 :            : 
    2114   [ +  +  -  + ]:          3 :                 TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2115                 :            :                         .tested_instruction = {
    2116                 :            :                                 .code = (BPF_JMP | EBPF_JLT | BPF_X),
    2117                 :            :                         },
    2118                 :            :                         .pre.dst = make_signed_domain(20, 30),
    2119                 :            :                         .pre.src = make_signed_domain(10, 19 + overlap),
    2120                 :            :                         .jump = overlap <= 1 ? unreachable : (struct state){
    2121                 :            :                                 .dst = make_singleton_domain(20),
    2122                 :            :                                 .src = make_singleton_domain(21),
    2123                 :            :                         },
    2124                 :            :                 }, also_signed), "strict, dst left touching src right, overlap=%d", overlap);
    2125                 :            : 
    2126   [ +  +  -  + ]:          3 :                 TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2127                 :            :                         .tested_instruction = {
    2128                 :            :                                 .code = (BPF_JMP | EBPF_JLE | BPF_X),
    2129                 :            :                         },
    2130                 :            :                         .pre.dst = make_signed_domain(20, 30),
    2131                 :            :                         .pre.src = make_signed_domain(10, 19 + overlap),
    2132                 :            :                         .jump = overlap < 1 ? unreachable : (struct state){
    2133                 :            :                                 .dst = make_signed_domain(20, 19 + overlap),
    2134                 :            :                                 .src = make_signed_domain(20, 19 + overlap),
    2135                 :            :                         },
    2136                 :            :                 }, also_signed), "non-strict, dst left touching src right, overlap=%d", overlap);
    2137                 :            : 
    2138                 :            :                 /*
    2139                 :            :                  * 10 - dst - (19 + overlap)
    2140                 :            :                  *                  20 - src - 30
    2141                 :            :                  */
    2142                 :            : 
    2143   [ +  +  -  + ]:          3 :                 TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2144                 :            :                         .tested_instruction = {
    2145                 :            :                                 .code = (BPF_JMP | EBPF_JLT | BPF_X),
    2146                 :            :                         },
    2147                 :            :                         .pre.dst = make_signed_domain(10, 19 + overlap),
    2148                 :            :                         .pre.src = make_signed_domain(20, 30),
    2149                 :            :                         .post = overlap < 1 ? unreachable : (struct state){
    2150                 :            :                                 .dst = make_signed_domain(20, 19 + overlap),
    2151                 :            :                                 .src = make_signed_domain(20, 19 + overlap),
    2152                 :            :                         },
    2153                 :            :                 }, also_signed), "strict, dst right touching src left, overlap=%d", overlap);
    2154                 :            : 
    2155   [ +  +  -  + ]:          3 :                 TEST_ASSERT_SUCCESS(verify_comparison((struct verify_instruction_param){
    2156                 :            :                         .tested_instruction = {
    2157                 :            :                                 .code = (BPF_JMP | EBPF_JLE | BPF_X),
    2158                 :            :                         },
    2159                 :            :                         .pre.dst = make_signed_domain(10, 19 + overlap),
    2160                 :            :                         .pre.src = make_signed_domain(20, 30),
    2161                 :            :                         .post = overlap <= 1 ? unreachable : (struct state){
    2162                 :            :                                 .dst = make_singleton_domain(21),
    2163                 :            :                                 .src = make_singleton_domain(20),
    2164                 :            :                         },
    2165                 :            :                 }, also_signed), "non-strict, dst right touching src left, overlap=%d", overlap);
    2166                 :            :         }
    2167                 :            : 
    2168                 :            :         return TEST_SUCCESS;
    2169                 :            : }
    2170                 :            : 
    2171                 :            : /* 64-bit load from heap (should be set to unknown). */
    2172                 :            : static int
    2173                 :          1 : test_mem_ldx_dw_heap(void)
    2174                 :            : {
    2175                 :          1 :         return verify_instruction((struct verify_instruction_param){
    2176                 :            :                 .tested_instruction = {
    2177                 :            :                         .code = (BPF_MEM | BPF_LDX | EBPF_DW),
    2178                 :            :                         .off = 16,
    2179                 :            :                 },
    2180                 :            :                 .area_size = 24,
    2181                 :            :                 .pre.src = make_pointer_domain(0, 0),
    2182                 :            :                 .post.dst = unknown,
    2183                 :            :         });
    2184                 :            : }
    2185                 :            : 
    2186                 :            : static struct
    2187                 :            : unit_test_suite test_bpf_validate_suite  = {
    2188                 :            :         .suite_name = "Test BPF Validate Unit Test Suite",
    2189                 :            :         .unit_test_cases = {
    2190                 :            :                 TEST_CASE(test_alu64_add_k),
    2191                 :            :                 TEST_CASE(test_alu64_add_k_pointer),
    2192                 :            :                 TEST_CASE(test_alu64_add_x_pointer_pointer),
    2193                 :            :                 TEST_CASE(test_alu64_add_x_pointer_scalar),
    2194                 :            :                 TEST_CASE(test_alu64_add_x_scalar_pointer),
    2195                 :            :                 TEST_CASE(test_alu64_add_x_scalar_scalar),
    2196                 :            :                 TEST_CASE(test_alu64_and_k),
    2197                 :            :                 TEST_CASE(test_alu64_div_mod_big_constant),
    2198                 :            :                 TEST_CASE(test_alu64_div_mod_big_range),
    2199                 :            :                 TEST_CASE(test_alu64_div_mod_overflow),
    2200                 :            :                 TEST_CASE(test_alu64_lsh_63),
    2201                 :            :                 TEST_CASE(test_alu64_mul_k_overflow),
    2202                 :            :                 TEST_CASE(test_alu64_mul_k_range_small),
    2203                 :            :                 TEST_CASE(test_alu64_neg_int64min_first),
    2204                 :            :                 TEST_CASE(test_alu64_neg_int64min_last),
    2205                 :            :                 TEST_CASE(test_alu64_neg_zero_first),
    2206                 :            :                 TEST_CASE(test_alu64_neg_zero_last),
    2207                 :            :                 TEST_CASE(test_alu64_or_k_negative),
    2208                 :            :                 TEST_CASE(test_alu64_or_k_positive),
    2209                 :            :                 TEST_CASE(test_alu64_sub_x_src_signed_max_zero),
    2210                 :            :                 TEST_CASE(test_alu64_xor_k_negative),
    2211                 :            :                 TEST_CASE(test_jmp64_jeq_k),
    2212                 :            :                 TEST_CASE(test_jmp64_jslt_x),
    2213                 :            :                 TEST_CASE(test_jmp64_ordering_overflow),
    2214                 :            :                 TEST_CASE(test_jmp64_ordering_ranges),
    2215                 :            :                 TEST_CASE(test_jmp64_ordering_singleton_inside),
    2216                 :            :                 TEST_CASE(test_jmp64_ordering_singleton_outside),
    2217                 :            :                 TEST_CASE(test_jmp64_ordering_touching),
    2218                 :            :                 TEST_CASE(test_mem_ldx_dw_heap),
    2219                 :            :                 TEST_CASES_END()
    2220                 :            :         }
    2221                 :            : };
    2222                 :            : 
    2223                 :            : static int
    2224                 :          1 : test_bpf_validate(void)
    2225                 :            : {
    2226                 :          1 :         return unit_test_suite_runner(&test_bpf_validate_suite);
    2227                 :            : }
    2228                 :            : 
    2229                 :        303 : REGISTER_FAST_TEST(bpf_validate_autotest, NOHUGE_OK, ASAN_OK, test_bpf_validate);

Generated by: LCOV version 1.14