LCOV - code coverage report
Current view: top level - bpfilter/cgen/matcher - cmp.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 88.5 % 104 92
Test Date: 2026-03-19 16:00:55 Functions: 100.0 % 6 6
Branches: 53.9 % 128 69

             Branch data     Line data    Source code
       1                 :             : /* SPDX-License-Identifier: GPL-2.0-only */
       2                 :             : /*
       3                 :             :  * Copyright (c) Meta Platforms, Inc. and affiliates.
       4                 :             :  */
       5                 :             : 
       6                 :             : #include "cgen/matcher/cmp.h"
       7                 :             : 
       8                 :             : #include <linux/bpf.h>
       9                 :             : #include <linux/bpf_common.h>
      10                 :             : 
      11                 :             : #include <assert.h>
      12                 :             : #include <string.h>
      13                 :             : 
      14                 :             : #include <bpfilter/logger.h>
      15                 :             : #include <bpfilter/matcher.h>
      16                 :             : 
      17                 :             : #include "cgen/jmp.h"
      18                 :             : #include "cgen/program.h"
      19                 :             : 
      20                 :             : #define _BF_MASK_LAST_BYTE 15
      21                 :             : 
      22                 :             : static inline uint64_t _bf_read_u64(const void *ptr)
      23                 :             : {
      24                 :             :     uint64_t val;
      25                 :             : 
      26                 :         466 :     memcpy(&val, ptr, sizeof(val));
      27                 :             : 
      28                 :             :     return val;
      29                 :             : }
      30                 :             : 
      31                 :             : /**
      32                 :             :  * @brief Emit a 4-instruction sequence to build a 64-bit immediate from 8 bytes.
      33                 :             :  *
      34                 :             :  * Produces:
      35                 :             :  * @code
      36                 :             :  * MOV32_IMM(dst, high32) -> LSH(dst, 32) -> MOV32_IMM(scratch, low32) -> OR(dst, scratch)
      37                 :             :  * @endcode
      38                 :             :  *
      39                 :             :  * @param program Program to emit into. Can't be NULL.
      40                 :             :  * @param dst_reg Destination register for the 64-bit value.
      41                 :             :  * @param scratch_reg Scratch register (clobbered).
      42                 :             :  * @param data 64-bit value to load.
      43                 :             :  */
      44                 :         466 : static int _bf_cmp_build_imm64(struct bf_program *program, int dst_reg,
      45                 :             :                                int scratch_reg, uint64_t data)
      46                 :             : {
      47         [ -  + ]:         466 :     EMIT(program, BPF_MOV32_IMM(dst_reg, (uint32_t)(data >> 32)));
      48         [ -  + ]:         466 :     EMIT(program, BPF_ALU64_IMM(BPF_LSH, dst_reg, 32));
      49         [ -  + ]:         466 :     EMIT(program, BPF_MOV32_IMM(scratch_reg, (uint32_t)data));
      50         [ -  + ]:         466 :     EMIT(program, BPF_ALU64_REG(BPF_OR, dst_reg, scratch_reg));
      51                 :             : 
      52                 :         466 :     return 0;
      53                 :             : }
      54                 :             : 
      55                 :             : /**
      56                 :             :  * @brief Compute a network prefix mask.
      57                 :             :  *
      58                 :             :  * @param prefixlen Prefix length in bits.
      59                 :             :  * @param mask Output buffer. Can't be NULL.
      60                 :             :  * @param mask_len Size of mask buffer in bytes (4 or 16).
      61                 :             :  */
      62                 :         139 : static void _bf_prefix_to_mask(unsigned int prefixlen, uint8_t *mask,
      63                 :             :                                size_t mask_len)
      64                 :             : {
      65                 :             :     assert(mask);
      66                 :             : 
      67                 :         139 :     memset(mask, 0x00, mask_len);
      68                 :         139 :     memset(mask, 0xff, prefixlen / 8);
      69         [ +  + ]:         139 :     if (prefixlen % 8)
      70                 :          41 :         mask[prefixlen / 8] = (0xff << (8 - prefixlen % 8)) & 0xff;
      71                 :         139 : }
      72                 :             : 
      73                 :        1008 : int bf_cmp_value(struct bf_program *program, enum bf_matcher_op op,
      74                 :             :                  const void *ref, unsigned int size, int reg)
      75                 :             : {
      76                 :             :     assert(program);
      77                 :             :     assert(ref);
      78                 :             : 
      79         [ -  + ]:        1008 :     if (op != BF_MATCHER_EQ && op != BF_MATCHER_NE)
      80         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "unsupported operator %d", op);
      81                 :             : 
      82   [ +  +  -  +  :        1008 :     switch (size) {
                      - ]
      83                 :         845 :     case 1:
      84                 :             :     case 2: {
      85                 :             :         /* Small values: compare directly via JMP_IMM.
      86                 :             :          * For size 1, ref is uint8_t; for size 2, ref is uint16_t.
      87                 :             :          * Both fit in a signed 32-bit immediate. */
      88                 :         845 :         uint32_t val =
      89         [ +  + ]:         845 :             (size == 1) ? *(const uint8_t *)ref : *(const uint16_t *)ref;
      90                 :             : 
      91   [ +  +  -  + ]:        1154 :         EMIT_FIXUP_JMP_NEXT_RULE(
      92                 :             :             program,
      93                 :             :             BPF_JMP_IMM(op == BF_MATCHER_EQ ? BPF_JNE : BPF_JEQ, reg, val, 0));
      94                 :         845 :         break;
      95                 :             :     }
      96                 :          78 :     case 4: {
      97                 :             :         /* 32-bit values: may exceed signed 32-bit immediate range, so
      98                 :             :          * use MOV32_IMM into R2 + JMP_REG. */
      99                 :          78 :         uint32_t val = *(const uint32_t *)ref;
     100                 :             : 
     101         [ -  + ]:          78 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_2, val));
     102   [ +  +  -  + ]:         107 :         EMIT_FIXUP_JMP_NEXT_RULE(
     103                 :             :             program, BPF_JMP_REG(op == BF_MATCHER_EQ ? BPF_JNE : BPF_JEQ, reg,
     104                 :             :                                  BPF_REG_2, 0));
     105                 :          78 :         break;
     106                 :             :     }
     107                 :             :     case 8: {
     108                 :             :         /* 64-bit values: build immediate in R2 via `_bf_cmp_build_imm64`,
     109                 :             :          * then compare with `JMP_REG`. */
     110                 :             :         int r;
     111                 :             : 
     112                 :           0 :         r = _bf_cmp_build_imm64(program, BPF_REG_2, BPF_REG_3,
     113                 :             :                                 _bf_read_u64(ref));
     114         [ #  # ]:           0 :         if (r)
     115                 :             :             return r;
     116   [ #  #  #  # ]:           0 :         EMIT_FIXUP_JMP_NEXT_RULE(
     117                 :             :             program, BPF_JMP_REG(op == BF_MATCHER_EQ ? BPF_JNE : BPF_JEQ, reg,
     118                 :             :                                  BPF_REG_2, 0));
     119                 :           0 :         break;
     120                 :             :     }
     121                 :          85 :     case 16: {
     122                 :             :         /* 128-bit values: reg holds low 64 bits, reg+1 holds high 64 bits.
     123                 :             :          * Compare each half against the reference. */
     124                 :             :         const uint8_t *addr = ref;
     125                 :             :         int r;
     126                 :             : 
     127                 :          85 :         r = _bf_cmp_build_imm64(program, BPF_REG_3, BPF_REG_4,
     128                 :             :                                 _bf_read_u64(addr));
     129         [ +  - ]:          85 :         if (r)
     130                 :             :             return r;
     131                 :             : 
     132         [ +  + ]:          85 :         if (op == BF_MATCHER_EQ) {
     133         [ -  + ]:          44 :             EMIT_FIXUP_JMP_NEXT_RULE(program,
     134                 :             :                                      BPF_JMP_REG(BPF_JNE, reg, BPF_REG_3, 0));
     135                 :             : 
     136                 :          44 :             r = _bf_cmp_build_imm64(program, BPF_REG_3, BPF_REG_4,
     137                 :             :                                     _bf_read_u64(addr + 8));
     138         [ +  - ]:          44 :             if (r)
     139                 :             :                 return r;
     140         [ -  + ]:          44 :             EMIT_FIXUP_JMP_NEXT_RULE(
     141                 :             :                 program, BPF_JMP_REG(BPF_JNE, reg + 1, BPF_REG_3, 0));
     142                 :             :         } else {
     143                 :             :             /* NE: the address must differ in at least one half.
     144                 :             :              * If the first half differs, the matcher matched — jump
     145                 :             :              * past the second half check and the unconditional
     146                 :             :              * jump-to-next-rule. If the first half matches, check the
     147                 :             :              * second half: if it also matches, the full address is
     148                 :             :              * equal, so the NE matcher fails — jump to next rule. */
     149                 :          41 :             _clean_bf_jmpctx_ struct bf_jmpctx j0 = bf_jmpctx_default();
     150                 :          41 :             _clean_bf_jmpctx_ struct bf_jmpctx j1 = bf_jmpctx_default();
     151                 :             : 
     152                 :          41 :             j0 =
     153         [ -  + ]:          41 :                 bf_jmpctx_get(program, BPF_JMP_REG(BPF_JNE, reg, BPF_REG_3, 0));
     154                 :             : 
     155                 :          41 :             r = _bf_cmp_build_imm64(program, BPF_REG_3, BPF_REG_4,
     156                 :             :                                     _bf_read_u64(addr + 8));
     157         [ +  - ]:          41 :             if (r)
     158                 :             :                 return r;
     159         [ -  + ]:          41 :             j1 = bf_jmpctx_get(program,
     160                 :             :                                BPF_JMP_REG(BPF_JNE, reg + 1, BPF_REG_3, 0));
     161                 :             : 
     162         [ -  + ]:          41 :             EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
     163                 :             :         }
     164                 :             :         break;
     165                 :             :     }
     166                 :           0 :     default:
     167         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "unsupported comparison size %u", size);
     168                 :             :     }
     169                 :             : 
     170                 :             :     return 0;
     171                 :             : }
     172                 :             : 
     173                 :         139 : int bf_cmp_masked_value(struct bf_program *program, enum bf_matcher_op op,
     174                 :             :                         const void *ref, unsigned int prefixlen,
     175                 :             :                         unsigned int size, int reg)
     176                 :             : {
     177                 :             :     assert(program);
     178                 :             :     assert(ref);
     179                 :             : 
     180         [ -  + ]:         139 :     if (op != BF_MATCHER_EQ && op != BF_MATCHER_NE)
     181         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "unsupported operator %d", op);
     182                 :             : 
     183      [ +  +  - ]:         139 :     switch (size) {
     184                 :          44 :     case 4: {
     185                 :             :         uint32_t mask;
     186                 :             :         const uint32_t *addr = ref;
     187                 :             : 
     188                 :          44 :         _bf_prefix_to_mask(prefixlen, (uint8_t *)&mask, 4);
     189                 :             : 
     190         [ -  + ]:          44 :         EMIT(program, BPF_MOV32_IMM(BPF_REG_2, *addr));
     191                 :             : 
     192         [ +  - ]:          44 :         if (mask != ~0U) {
     193         [ -  + ]:          44 :             EMIT(program, BPF_MOV32_IMM(BPF_REG_3, mask));
     194         [ -  + ]:          44 :             EMIT(program, BPF_ALU32_REG(BPF_AND, reg, BPF_REG_3));
     195         [ -  + ]:          44 :             EMIT(program, BPF_ALU32_REG(BPF_AND, BPF_REG_2, BPF_REG_3));
     196                 :             :         }
     197                 :             : 
     198   [ +  +  -  + ]:          65 :         EMIT_FIXUP_JMP_NEXT_RULE(
     199                 :             :             program, BPF_JMP_REG(op == BF_MATCHER_EQ ? BPF_JNE : BPF_JEQ, reg,
     200                 :             :                                  BPF_REG_2, 0));
     201                 :          44 :         break;
     202                 :             :     }
     203                 :          95 :     case 16: {
     204                 :             :         uint8_t mask[16];
     205                 :             :         uint8_t masked_lo[8], masked_hi[8];
     206                 :             :         const uint8_t *addr = ref;
     207                 :             :         int r;
     208                 :             : 
     209                 :          95 :         _bf_prefix_to_mask(prefixlen, mask, 16);
     210                 :             : 
     211                 :             :         // Apply mask to loaded reg/reg+1 if not a full /128
     212         [ +  + ]:          95 :         if (mask[_BF_MASK_LAST_BYTE] != (uint8_t)~0) {
     213                 :          53 :             r = _bf_cmp_build_imm64(program, BPF_REG_3, BPF_REG_4,
     214                 :             :                                     _bf_read_u64(mask));
     215         [ +  - ]:          53 :             if (r)
     216                 :           0 :                 return r;
     217         [ -  + ]:          53 :             EMIT(program, BPF_ALU64_REG(BPF_AND, reg, BPF_REG_3));
     218                 :             : 
     219                 :          53 :             r = _bf_cmp_build_imm64(program, BPF_REG_3, BPF_REG_4,
     220                 :             :                                     _bf_read_u64(mask + 8));
     221         [ +  - ]:          53 :             if (r)
     222                 :             :                 return r;
     223         [ -  + ]:          53 :             EMIT(program, BPF_ALU64_REG(BPF_AND, reg + 1, BPF_REG_3));
     224                 :             :         }
     225                 :             : 
     226         [ +  + ]:         855 :         for (int i = 0; i < 8; i++)
     227                 :         760 :             masked_lo[i] = addr[i] & mask[i];
     228         [ +  + ]:         855 :         for (int i = 0; i < 8; i++)
     229                 :         760 :             masked_hi[i] = addr[i + 8] & mask[i + 8];
     230                 :             : 
     231                 :          95 :         r = _bf_cmp_build_imm64(program, BPF_REG_3, BPF_REG_4,
     232                 :             :                                 _bf_read_u64(masked_lo));
     233         [ +  - ]:          95 :         if (r)
     234                 :             :             return r;
     235                 :             : 
     236         [ +  + ]:          95 :         if (op == BF_MATCHER_EQ) {
     237         [ -  + ]:          54 :             EMIT_FIXUP_JMP_NEXT_RULE(program,
     238                 :             :                                      BPF_JMP_REG(BPF_JNE, reg, BPF_REG_3, 0));
     239                 :             : 
     240                 :          54 :             r = _bf_cmp_build_imm64(program, BPF_REG_3, BPF_REG_4,
     241                 :             :                                     _bf_read_u64(masked_hi));
     242         [ +  - ]:          54 :             if (r)
     243                 :             :                 return r;
     244         [ -  + ]:          54 :             EMIT_FIXUP_JMP_NEXT_RULE(
     245                 :             :                 program, BPF_JMP_REG(BPF_JNE, reg + 1, BPF_REG_3, 0));
     246                 :             :         } else {
     247                 :          41 :             _clean_bf_jmpctx_ struct bf_jmpctx j0 = bf_jmpctx_default();
     248                 :          41 :             _clean_bf_jmpctx_ struct bf_jmpctx j1 = bf_jmpctx_default();
     249                 :             : 
     250                 :          41 :             j0 =
     251         [ -  + ]:          41 :                 bf_jmpctx_get(program, BPF_JMP_REG(BPF_JNE, reg, BPF_REG_3, 0));
     252                 :             : 
     253                 :          41 :             r = _bf_cmp_build_imm64(program, BPF_REG_3, BPF_REG_4,
     254                 :             :                                     _bf_read_u64(masked_hi));
     255         [ +  - ]:          41 :             if (r)
     256                 :             :                 return r;
     257         [ -  + ]:          41 :             j1 = bf_jmpctx_get(program,
     258                 :             :                                BPF_JMP_REG(BPF_JNE, reg + 1, BPF_REG_3, 0));
     259                 :             : 
     260         [ -  + ]:          41 :             EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_A(0));
     261                 :             :         }
     262                 :          95 :         break;
     263                 :             :     }
     264                 :           0 :     default:
     265         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "unsupported masked comparison size %u", size);
     266                 :             :     }
     267                 :             : 
     268                 :             :     return 0;
     269                 :             : }
     270                 :             : 
     271                 :         146 : int bf_cmp_range(struct bf_program *program, uint32_t min, uint32_t max,
     272                 :             :                  int reg)
     273                 :             : {
     274                 :             :     assert(program);
     275                 :             : 
     276         [ -  + ]:         146 :     EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP32_IMM(BPF_JLT, reg, min, 0));
     277         [ -  + ]:         146 :     EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP32_IMM(BPF_JGT, reg, max, 0));
     278                 :             : 
     279                 :         146 :     return 0;
     280                 :             : }
     281                 :             : 
     282                 :          60 : int bf_cmp_bitfield(struct bf_program *program, enum bf_matcher_op op,
     283                 :             :                     uint32_t flags, int reg)
     284                 :             : {
     285                 :             :     assert(program);
     286                 :             : 
     287         [ -  + ]:          60 :     if (op != BF_MATCHER_ANY && op != BF_MATCHER_ALL)
     288         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "unsupported operator %d", op);
     289                 :             : 
     290         [ -  + ]:          60 :     EMIT(program, BPF_ALU32_IMM(BPF_AND, reg, flags));
     291   [ +  +  +  +  :         120 :     EMIT_FIXUP_JMP_NEXT_RULE(
                   -  + ]
     292                 :             :         program, BPF_JMP32_IMM(op == BF_MATCHER_ANY ? BPF_JEQ : BPF_JNE, reg,
     293                 :             :                                op == BF_MATCHER_ANY ? 0 : flags, 0));
     294                 :             : 
     295                 :          60 :     return 0;
     296                 :             : }
        

Generated by: LCOV version 2.0-1