Home | History | Annotate | Download | only in apf
      1 /*
      2  * Copyright 2018, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "apf_interpreter.h"
     18 
     19 #include <string.h> // For memcmp
     20 
     21 #include "apf.h"
     22 
     23 // Return code indicating "packet" should accepted.
     24 #define PASS_PACKET 1
     25 // Return code indicating "packet" should be dropped.
     26 #define DROP_PACKET 0
     27 // Verify an internal condition and accept packet if it fails.
     28 #define ASSERT_RETURN(c) if (!(c)) return PASS_PACKET
     29 // If "c" is of an unsigned type, generate a compile warning that gets promoted to an error.
     30 // This makes bounds checking simpler because ">= 0" can be avoided. Otherwise adding
     31 // superfluous ">= 0" with unsigned expressions generates compile warnings.
     32 #define ENFORCE_UNSIGNED(c) ((c)==(uint32_t)(c))
     33 
     34 int accept_packet(uint8_t* program, uint32_t program_len, uint32_t ram_len,
     35                   const uint8_t* packet, uint32_t packet_len,
     36                   uint32_t filter_age) {
     37 // Is offset within program bounds?
     38 #define IN_PROGRAM_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < program_len)
     39 // Is offset within packet bounds?
     40 #define IN_PACKET_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < packet_len)
     41 // Is access to offset |p| length |size| within data bounds?
     42 #define IN_DATA_BOUNDS(p, size) (ENFORCE_UNSIGNED(p) && \
     43                                  ENFORCE_UNSIGNED(size) && \
     44                                  (p) + (size) <= ram_len && \
     45                                  (p) >= program_len && \
     46                                  (p) + (size) >= (p))  // catch wraparounds
     47 // Accept packet if not within program bounds
     48 #define ASSERT_IN_PROGRAM_BOUNDS(p) ASSERT_RETURN(IN_PROGRAM_BOUNDS(p))
     49 // Accept packet if not within packet bounds
     50 #define ASSERT_IN_PACKET_BOUNDS(p) ASSERT_RETURN(IN_PACKET_BOUNDS(p))
     51 // Accept packet if not within data bounds
     52 #define ASSERT_IN_DATA_BOUNDS(p, size) ASSERT_RETURN(IN_DATA_BOUNDS(p, size))
     53 
     54   // Program counter.
     55   uint32_t pc = 0;
     56 // Accept packet if not within program or not ahead of program counter
     57 #define ASSERT_FORWARD_IN_PROGRAM(p) ASSERT_RETURN(IN_PROGRAM_BOUNDS(p) && (p) >= pc)
     58   // Memory slot values.
     59   uint32_t memory[MEMORY_ITEMS] = {};
     60   // Fill in pre-filled memory slot values.
     61   memory[MEMORY_OFFSET_PROGRAM_SIZE] = program_len;
     62   memory[MEMORY_OFFSET_DATA_SIZE] = ram_len;
     63   memory[MEMORY_OFFSET_PACKET_SIZE] = packet_len;
     64   memory[MEMORY_OFFSET_FILTER_AGE] = filter_age;
     65   ASSERT_IN_PACKET_BOUNDS(APF_FRAME_HEADER_SIZE);
     66   // Only populate if IP version is IPv4.
     67   if ((packet[APF_FRAME_HEADER_SIZE] & 0xf0) == 0x40) {
     68       memory[MEMORY_OFFSET_IPV4_HEADER_SIZE] = (packet[APF_FRAME_HEADER_SIZE] & 15) * 4;
     69   }
     70   // Register values.
     71   uint32_t registers[2] = {};
     72   // Count of instructions remaining to execute. This is done to ensure an
     73   // upper bound on execution time. It should never be hit and is only for
     74   // safety. Initialize to the number of bytes in the program which is an
     75   // upper bound on the number of instructions in the program.
     76   uint32_t instructions_remaining = program_len;
     77 
     78   do {
     79       if (pc == program_len) {
     80           return PASS_PACKET;
     81       } else if (pc == (program_len + 1)) {
     82           return DROP_PACKET;
     83       }
     84       ASSERT_IN_PROGRAM_BOUNDS(pc);
     85       const uint8_t bytecode = program[pc++];
     86       const uint32_t opcode = EXTRACT_OPCODE(bytecode);
     87       const uint32_t reg_num = EXTRACT_REGISTER(bytecode);
     88 #define REG (registers[reg_num])
     89 #define OTHER_REG (registers[reg_num ^ 1])
     90       // All instructions have immediate fields, so load them now.
     91       const uint32_t len_field = EXTRACT_IMM_LENGTH(bytecode);
     92       uint32_t imm = 0;
     93       int32_t signed_imm = 0;
     94       if (len_field != 0) {
     95           const uint32_t imm_len = 1 << (len_field - 1);
     96           ASSERT_FORWARD_IN_PROGRAM(pc + imm_len - 1);
     97           uint32_t i;
     98           for (i = 0; i < imm_len; i++)
     99               imm = (imm << 8) | program[pc++];
    100           // Sign extend imm into signed_imm.
    101           signed_imm = imm << ((4 - imm_len) * 8);
    102           signed_imm >>= (4 - imm_len) * 8;
    103       }
    104       switch (opcode) {
    105           case LDB_OPCODE:
    106           case LDH_OPCODE:
    107           case LDW_OPCODE:
    108           case LDBX_OPCODE:
    109           case LDHX_OPCODE:
    110           case LDWX_OPCODE: {
    111               uint32_t offs = imm;
    112               if (opcode >= LDBX_OPCODE) {
    113                   // Note: this can overflow and actually decrease offs.
    114                   offs += registers[1];
    115               }
    116               ASSERT_IN_PACKET_BOUNDS(offs);
    117               uint32_t load_size;
    118               switch (opcode) {
    119                   case LDB_OPCODE:
    120                   case LDBX_OPCODE:
    121                     load_size = 1;
    122                     break;
    123                   case LDH_OPCODE:
    124                   case LDHX_OPCODE:
    125                     load_size = 2;
    126                     break;
    127                   case LDW_OPCODE:
    128                   case LDWX_OPCODE:
    129                     load_size = 4;
    130                     break;
    131                   // Immediately enclosing switch statement guarantees
    132                   // opcode cannot be any other value.
    133               }
    134               const uint32_t end_offs = offs + (load_size - 1);
    135               // Catch overflow/wrap-around.
    136               ASSERT_RETURN(end_offs >= offs);
    137               ASSERT_IN_PACKET_BOUNDS(end_offs);
    138               uint32_t val = 0;
    139               while (load_size--)
    140                   val = (val << 8) | packet[offs++];
    141               REG = val;
    142               break;
    143           }
    144           case JMP_OPCODE:
    145               // This can jump backwards. Infinite looping prevented by instructions_remaining.
    146               pc += imm;
    147               break;
    148           case JEQ_OPCODE:
    149           case JNE_OPCODE:
    150           case JGT_OPCODE:
    151           case JLT_OPCODE:
    152           case JSET_OPCODE:
    153           case JNEBS_OPCODE: {
    154               // Load second immediate field.
    155               uint32_t cmp_imm = 0;
    156               if (reg_num == 1) {
    157                   cmp_imm = registers[1];
    158               } else if (len_field != 0) {
    159                   uint32_t cmp_imm_len = 1 << (len_field - 1);
    160                   ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm_len - 1);
    161                   uint32_t i;
    162                   for (i = 0; i < cmp_imm_len; i++)
    163                       cmp_imm = (cmp_imm << 8) | program[pc++];
    164               }
    165               switch (opcode) {
    166                   case JEQ_OPCODE:
    167                       if (registers[0] == cmp_imm)
    168                           pc += imm;
    169                       break;
    170                   case JNE_OPCODE:
    171                       if (registers[0] != cmp_imm)
    172                           pc += imm;
    173                       break;
    174                   case JGT_OPCODE:
    175                       if (registers[0] > cmp_imm)
    176                           pc += imm;
    177                       break;
    178                   case JLT_OPCODE:
    179                       if (registers[0] < cmp_imm)
    180                           pc += imm;
    181                       break;
    182                   case JSET_OPCODE:
    183                       if (registers[0] & cmp_imm)
    184                           pc += imm;
    185                       break;
    186                   case JNEBS_OPCODE: {
    187                       // cmp_imm is size in bytes of data to compare.
    188                       // pc is offset of program bytes to compare.
    189                       // imm is jump target offset.
    190                       // REG is offset of packet bytes to compare.
    191                       ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1);
    192                       ASSERT_IN_PACKET_BOUNDS(REG);
    193                       const uint32_t last_packet_offs = REG + cmp_imm - 1;
    194                       ASSERT_RETURN(last_packet_offs >= REG);
    195                       ASSERT_IN_PACKET_BOUNDS(last_packet_offs);
    196                       if (memcmp(program + pc, packet + REG, cmp_imm))
    197                           pc += imm;
    198                       // skip past comparison bytes
    199                       pc += cmp_imm;
    200                       break;
    201                   }
    202               }
    203               break;
    204           }
    205           case ADD_OPCODE:
    206               registers[0] += reg_num ? registers[1] : imm;
    207               break;
    208           case MUL_OPCODE:
    209               registers[0] *= reg_num ? registers[1] : imm;
    210               break;
    211           case DIV_OPCODE: {
    212               const uint32_t div_operand = reg_num ? registers[1] : imm;
    213               ASSERT_RETURN(div_operand);
    214               registers[0] /= div_operand;
    215               break;
    216           }
    217           case AND_OPCODE:
    218               registers[0] &= reg_num ? registers[1] : imm;
    219               break;
    220           case OR_OPCODE:
    221               registers[0] |= reg_num ? registers[1] : imm;
    222               break;
    223           case SH_OPCODE: {
    224               const int32_t shift_val = reg_num ? (int32_t)registers[1] : signed_imm;
    225               if (shift_val > 0)
    226                   registers[0] <<= shift_val;
    227               else
    228                   registers[0] >>= -shift_val;
    229               break;
    230           }
    231           case LI_OPCODE:
    232               REG = signed_imm;
    233               break;
    234           case EXT_OPCODE:
    235               if (
    236 // If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result,
    237 // instead just enforce that imm is unsigned (so it's always greater or equal to 0).
    238 #if LDM_EXT_OPCODE == 0
    239                   ENFORCE_UNSIGNED(imm) &&
    240 #else
    241                   imm >= LDM_EXT_OPCODE &&
    242 #endif
    243                   imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) {
    244                 REG = memory[imm - LDM_EXT_OPCODE];
    245               } else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) {
    246                 memory[imm - STM_EXT_OPCODE] = REG;
    247               } else switch (imm) {
    248                   case NOT_EXT_OPCODE:
    249                     REG = ~REG;
    250                     break;
    251                   case NEG_EXT_OPCODE:
    252                     REG = -REG;
    253                     break;
    254                   case SWAP_EXT_OPCODE: {
    255                     uint32_t tmp = REG;
    256                     REG = OTHER_REG;
    257                     OTHER_REG = tmp;
    258                     break;
    259                   }
    260                   case MOV_EXT_OPCODE:
    261                     REG = OTHER_REG;
    262                     break;
    263                   // Unknown extended opcode
    264                   default:
    265                     // Bail out
    266                     return PASS_PACKET;
    267               }
    268               break;
    269           case LDDW_OPCODE: {
    270               uint32_t offs = OTHER_REG + signed_imm;
    271               uint32_t size = 4;
    272               uint32_t val = 0;
    273               // Negative offsets wrap around the end of the address space.
    274               // This allows us to efficiently access the end of the
    275               // address space with one-byte immediates without using %=.
    276               if (offs & 0x80000000) {
    277                   offs = ram_len + offs;  // unsigned overflow intended
    278               }
    279               ASSERT_IN_DATA_BOUNDS(offs, size);
    280               while (size--)
    281                   val = (val << 8) | program[offs++];
    282               REG = val;
    283               break;
    284           }
    285           case STDW_OPCODE: {
    286               uint32_t offs = OTHER_REG + signed_imm;
    287               uint32_t size = 4;
    288               uint32_t val = REG;
    289               // Negative offsets wrap around the end of the address space.
    290               // This allows us to efficiently access the end of the
    291               // address space with one-byte immediates without using %=.
    292               if (offs & 0x80000000) {
    293                   offs = ram_len + offs;  // unsigned overflow intended
    294               }
    295               ASSERT_IN_DATA_BOUNDS(offs, size);
    296               while (size--) {
    297                   program[offs++] = (val >> 24);
    298                   val <<= 8;
    299               }
    300               break;
    301           }
    302           // Unknown opcode
    303           default:
    304               // Bail out
    305               return PASS_PACKET;
    306       }
    307   } while (instructions_remaining--);
    308   return PASS_PACKET;
    309 }
    310