1 /*
2 * Copyright 2024, 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, memcpy, memset
20
21 #if __GNUC__ >= 7 || __clang__
22 #define FALLTHROUGH __attribute__((fallthrough))
23 #else
24 #define FALLTHROUGH
25 #endif
26
27 #undef bool
28 #undef true
29 #undef false
30 typedef enum { False, True } Boolean;
31 #define bool Boolean
32 #define true True
33 #define false False
34
35 #include "apf_defs.h"
36 #include "apf.h"
37 #include "apf_utils.h"
38 #include "apf_dns.h"
39 #include "apf_checksum.h"
40
41 // User hook for interpreter debug tracing.
42 #ifdef APF_TRACE_HOOK
43 extern void APF_TRACE_HOOK(u32 pc, const u32* regs, const u8* program,
44 u32 program_len, const u8 *packet, u32 packet_len,
45 const u32* memory, u32 ram_len);
46 #else
47 #define APF_TRACE_HOOK(pc, regs, program, program_len, packet, packet_len, memory, memory_len) \
48 do { /* nop*/ \
49 } while (0)
50 #endif
51
52 // Return code indicating "packet" should accepted.
53 #define PASS 1
54 // Return code indicating "packet" should be accepted (and something unexpected happened).
55 #define EXCEPTION 2
56 // Return code indicating "packet" should be dropped.
57 #define DROP 0
58 // Verify an internal condition and accept packet if it fails.
59 #define ASSERT_RETURN(c) if (!(c)) return EXCEPTION
60 // If "c" is of an unsigned type, generate a compile warning that gets promoted to an error.
61 // This makes bounds checking simpler because ">= 0" can be avoided. Otherwise adding
62 // superfluous ">= 0" with unsigned expressions generates compile warnings.
63 #define ENFORCE_UNSIGNED(c) ((c)==(u32)(c))
64
apf_version(void)65 u32 apf_version(void) {
66 return 20240510;
67 }
68
69 typedef struct {
70 // Note: the following 4 fields take up exactly 8 bytes.
71 u16 except_buf_sz; // Length of the exception buffer (at program_len offset)
72 u8 ptr_size; // sizeof(void*)
73 u8 v6; // Set to 1 by first jmpdata (APFv6+) instruction
74 u32 pc; // Program counter.
75 // All the pointers should be next to each other for better struct packing.
76 // We are at offset 8, so even 64-bit pointers will not need extra padding.
77 void *caller_ctx; // Passed in to interpreter, passed through to alloc/transmit.
78 u8* tx_buf; // The output buffer pointer
79 u8* program; // Pointer to program/data buffer
80 const u8* packet; // Pointer to input packet buffer
81 // Order fields in order of decreasing size
82 u32 tx_buf_len; // The length of the output buffer
83 u32 program_len; // Length of the program
84 u32 ram_len; // Length of the entire apf program/data region
85 u32 packet_len; // Length of the input packet buffer
86 u32 R[2]; // Register values.
87 memory_type mem; // Memory slot values. (array of u32s)
88 // Note: any extra u16s go here, then u8s
89 } apf_context;
90
FUNC(int do_transmit_buffer (apf_context * ctx,u32 pkt_len,u8 dscp))91 FUNC(int do_transmit_buffer(apf_context* ctx, u32 pkt_len, u8 dscp)) {
92 int ret = apf_transmit_buffer(ctx->caller_ctx, ctx->tx_buf, pkt_len, dscp);
93 ctx->tx_buf = NULL;
94 ctx->tx_buf_len = 0;
95 return ret;
96 }
97
do_discard_buffer(apf_context * ctx)98 static int do_discard_buffer(apf_context* ctx) {
99 return do_transmit_buffer(ctx, 0 /* pkt_len */, 0 /* dscp */);
100 }
101
102 #define DECODE_U8() (ctx->program[ctx->pc++])
103
decode_be16(apf_context * ctx)104 static u16 decode_be16(apf_context* ctx) {
105 u16 v = DECODE_U8();
106 v <<= 8;
107 v |= DECODE_U8();
108 return v;
109 }
110
111 // Decode an immediate, lengths [0..4] all work, does not do range checking.
112 // But note that program is at least 20 bytes shorter than ram, so first few
113 // immediates can always be safely decoded without exceeding ram buffer.
decode_imm(apf_context * ctx,u32 length)114 static u32 decode_imm(apf_context* ctx, u32 length) {
115 u32 i, v = 0;
116 for (i = 0; i < length; ++i) v = (v << 8) | DECODE_U8();
117 return v;
118 }
119
120 // Warning: 'ofs' should be validated by caller!
read_packet_u8(apf_context * ctx,u32 ofs)121 static u8 read_packet_u8(apf_context* ctx, u32 ofs) {
122 return ctx->packet[ofs];
123 }
124
do_apf_run(apf_context * ctx)125 static int do_apf_run(apf_context* ctx) {
126 // Is offset within ram bounds?
127 #define IN_RAM_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < ctx->ram_len)
128 // Is offset within packet bounds?
129 #define IN_PACKET_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < ctx->packet_len)
130 // Is access to offset |p| length |size| within data bounds?
131 #define IN_DATA_BOUNDS(p, size) (ENFORCE_UNSIGNED(p) && \
132 ENFORCE_UNSIGNED(size) && \
133 (p) + (size) <= ctx->ram_len && \
134 (p) + (size) >= (p)) // catch wraparounds
135 // Accept packet if not within ram bounds
136 #define ASSERT_IN_RAM_BOUNDS(p) ASSERT_RETURN(IN_RAM_BOUNDS(p))
137 // Accept packet if not within packet bounds
138 #define ASSERT_IN_PACKET_BOUNDS(p) ASSERT_RETURN(IN_PACKET_BOUNDS(p))
139 // Accept packet if not within data bounds
140 #define ASSERT_IN_DATA_BOUNDS(p, size) ASSERT_RETURN(IN_DATA_BOUNDS(p, size))
141
142 // Counters start at end of RAM and count *backwards* so this array takes negative integers.
143 u32 *counter = (u32*)(ctx->program + ctx->ram_len);
144
145 // Count of instructions remaining to execute. This is done to ensure an
146 // upper bound on execution time. It should never be hit and is only for
147 // safety. Initialize to the number of bytes in the program which is an
148 // upper bound on the number of instructions in the program.
149 u32 instructions_remaining = ctx->program_len;
150
151 // APFv6 requires at least 5 u32 counters at the end of ram, this makes counter[-5]++ valid
152 // This cannot wrap due to previous check, that enforced program_len & ram_len < 2GiB.
153 if (ctx->program_len + 20 > ctx->ram_len) return EXCEPTION;
154
155 // Only populate if packet long enough, and IP version is IPv4.
156 // Note: this doesn't actually check the ethertype...
157 if ((ctx->packet_len >= ETH_HLEN + IPV4_HLEN)
158 && ((read_packet_u8(ctx, ETH_HLEN) & 0xf0) == 0x40)) {
159 ctx->mem.named.ipv4_header_size = (read_packet_u8(ctx, ETH_HLEN) & 15) * 4;
160 }
161
162 // Is access to offset |p| length |size| within output buffer bounds?
163 #define IN_OUTPUT_BOUNDS(p, size) (ENFORCE_UNSIGNED(p) && \
164 ENFORCE_UNSIGNED(size) && \
165 (p) + (size) <= ctx->tx_buf_len && \
166 (p) + (size) >= (p))
167 // Accept packet if not write within allocated output buffer
168 #define ASSERT_IN_OUTPUT_BOUNDS(p, size) ASSERT_RETURN(IN_OUTPUT_BOUNDS(p, size))
169
170 do {
171 APF_TRACE_HOOK(ctx->pc, ctx->R, ctx->program, ctx->program_len,
172 ctx->packet, ctx->packet_len, ctx->mem.slot, ctx->ram_len);
173 if (ctx->pc == ctx->program_len + 1) return DROP;
174 if (ctx->pc == ctx->program_len) return PASS;
175 if (ctx->pc > ctx->program_len) return EXCEPTION;
176
177 { // half indent to avoid needless line length...
178
179 const u8 bytecode = DECODE_U8();
180 const u8 opcode = EXTRACT_OPCODE(bytecode);
181 const u8 reg_num = EXTRACT_REGISTER(bytecode);
182 #define REG (ctx->R[reg_num])
183 #define OTHER_REG (ctx->R[reg_num ^ 1])
184 // All instructions have immediate fields, so load them now.
185 const u8 len_field = EXTRACT_IMM_LENGTH(bytecode);
186 const u8 imm_len = ((len_field + 1u) >> 2) + len_field; // 0,1,2,3 -> 0,1,2,4
187 u32 pktcopy_src_offset = 0; // used for various pktdatacopy opcodes
188 u32 imm = 0;
189 s32 signed_imm = 0;
190 u32 arith_imm;
191 s32 arith_signed_imm;
192 if (len_field != 0) {
193 imm = decode_imm(ctx, imm_len); // 1st imm, at worst bytes 1-4 past opcode/program_len
194 // Sign extend imm into signed_imm.
195 signed_imm = (s32)(imm << ((4 - imm_len) * 8));
196 signed_imm >>= (4 - imm_len) * 8;
197 }
198
199 // See comment at ADD_OPCODE for the reason for ARITH_REG/arith_imm/arith_signed_imm.
200 #define ARITH_REG (ctx->R[reg_num & ctx->v6])
201 arith_imm = (ctx->v6) ? (len_field ? imm : OTHER_REG) : (reg_num ? ctx->R[1] : imm);
202 arith_signed_imm = (ctx->v6) ? (len_field ? signed_imm : (s32)OTHER_REG) : (reg_num ? (s32)ctx->R[1] : signed_imm);
203
204 switch (opcode) {
205 case PASSDROP_OPCODE: { // APFv6+
206 if (len_field > 2) return EXCEPTION; // max 64K counters (ie. imm < 64K)
207 if (imm) {
208 if (4 * imm > ctx->ram_len) return EXCEPTION;
209 counter[-(s32)imm]++;
210 }
211 return reg_num ? DROP : PASS;
212 }
213 case LDB_OPCODE:
214 case LDH_OPCODE:
215 case LDW_OPCODE:
216 case LDBX_OPCODE:
217 case LDHX_OPCODE:
218 case LDWX_OPCODE: {
219 u32 load_size = 0;
220 u32 offs = imm;
221 // Note: this can overflow and actually decrease offs.
222 if (opcode >= LDBX_OPCODE) offs += ctx->R[1];
223 ASSERT_IN_PACKET_BOUNDS(offs);
224 switch (opcode) {
225 case LDB_OPCODE:
226 case LDBX_OPCODE:
227 load_size = 1;
228 break;
229 case LDH_OPCODE:
230 case LDHX_OPCODE:
231 load_size = 2;
232 break;
233 case LDW_OPCODE:
234 case LDWX_OPCODE:
235 load_size = 4;
236 break;
237 // Immediately enclosing switch statement guarantees
238 // opcode cannot be any other value.
239 }
240 {
241 const u32 end_offs = offs + (load_size - 1);
242 u32 val = 0;
243 // Catch overflow/wrap-around.
244 ASSERT_RETURN(end_offs >= offs);
245 ASSERT_IN_PACKET_BOUNDS(end_offs);
246 // load_size underflow on final iteration not an issue as not used after loop.
247 while (load_size--) val = (val << 8) | read_packet_u8(ctx, offs++);
248 REG = val;
249 }
250 break;
251 }
252 case JMP_OPCODE:
253 if (reg_num && !ctx->v6) { // APFv6+
254 // First invocation of APFv6 jmpdata instruction
255 counter[-1] = 0x12345678; // endianness marker
256 counter[-2]++; // total packets ++
257 ctx->v6 = (u8)true;
258 }
259 // This can jump backwards. Infinite looping prevented by instructions_remaining.
260 ctx->pc += imm;
261 break;
262 case JEQ_OPCODE:
263 case JNE_OPCODE:
264 case JGT_OPCODE:
265 case JLT_OPCODE:
266 case JSET_OPCODE:
267 case JNSET_OPCODE: {
268 u32 cmp_imm = 0;
269 // Load second immediate field.
270 if (reg_num == 1) {
271 cmp_imm = ctx->R[1];
272 } else {
273 cmp_imm = decode_imm(ctx, imm_len); // 2nd imm, at worst 8 bytes past prog_len
274 }
275 switch (opcode) {
276 case JEQ_OPCODE: if ( ctx->R[0] == cmp_imm ) ctx->pc += imm; break;
277 case JNE_OPCODE: if ( ctx->R[0] != cmp_imm ) ctx->pc += imm; break;
278 case JGT_OPCODE: if ( ctx->R[0] > cmp_imm ) ctx->pc += imm; break;
279 case JLT_OPCODE: if ( ctx->R[0] < cmp_imm ) ctx->pc += imm; break;
280 case JSET_OPCODE: if ( ctx->R[0] & cmp_imm ) ctx->pc += imm; break;
281 case JNSET_OPCODE: if (!(ctx->R[0] & cmp_imm)) ctx->pc += imm; break;
282 }
283 break;
284 }
285 case JBSMATCH_OPCODE: {
286 // Load second immediate field.
287 u32 cmp_imm = decode_imm(ctx, imm_len); // 2nd imm, at worst 8 bytes past prog_len
288 u32 cnt = (cmp_imm >> 11) + 1; // 1+, up to 32 fits in u16
289 u32 len = cmp_imm & 2047; // 0..2047
290 u32 bytes = cnt * len;
291 const u32 last_packet_offs = ctx->R[0] + len - 1;
292 bool matched = false;
293 // bytes = cnt * len is size in bytes of data to compare.
294 // pc is offset of program bytes to compare.
295 // imm is jump target offset.
296 // R0 is offset of packet bytes to compare.
297 if (bytes > 0xFFFF) return EXCEPTION;
298 // pc < program_len < ram_len < 2GiB, thus pc + bytes cannot wrap
299 if (!IN_RAM_BOUNDS(ctx->pc + bytes - 1)) return EXCEPTION;
300 ASSERT_IN_PACKET_BOUNDS(ctx->R[0]);
301 // Note: this will return EXCEPTION (due to wrap) if imm_len (ie. len) is 0
302 ASSERT_RETURN(last_packet_offs >= ctx->R[0]);
303 ASSERT_IN_PACKET_BOUNDS(last_packet_offs);
304 // cnt underflow on final iteration not an issue as not used after loop.
305 while (cnt--) {
306 matched |= !memcmp(ctx->program + ctx->pc, ctx->packet + ctx->R[0], len);
307 // skip past comparison bytes
308 ctx->pc += len;
309 }
310 if (matched ^ !reg_num) ctx->pc += imm;
311 break;
312 }
313 // There is a difference in APFv4 and APFv6 arithmetic behaviour!
314 // APFv4: R[0] op= Rbit ? R[1] : imm; (and it thus doesn't make sense to have R=1 && len_field>0)
315 // APFv6+: REG op= len_field ? imm : OTHER_REG; (note: this is *DIFFERENT* with R=1 len_field==0)
316 // Furthermore APFv4 uses unsigned imm (except SH), while APFv6 uses signed_imm for ADD/AND/SH.
317 case ADD_OPCODE: ARITH_REG += (ctx->v6) ? (u32)arith_signed_imm : arith_imm; break;
318 case MUL_OPCODE: ARITH_REG *= arith_imm; break;
319 case AND_OPCODE: ARITH_REG &= (ctx->v6) ? (u32)arith_signed_imm : arith_imm; break;
320 case OR_OPCODE: ARITH_REG |= arith_imm; break;
321 case DIV_OPCODE: { // see above comment!
322 const u32 div_operand = arith_imm;
323 ASSERT_RETURN(div_operand);
324 ARITH_REG /= div_operand;
325 break;
326 }
327 case SH_OPCODE: { // see above comment!
328 if (arith_signed_imm >= 0)
329 ARITH_REG <<= arith_signed_imm;
330 else
331 ARITH_REG >>= -arith_signed_imm;
332 break;
333 }
334 case LI_OPCODE:
335 REG = (u32)signed_imm;
336 break;
337 case PKTDATACOPY_OPCODE:
338 pktcopy_src_offset = imm;
339 imm = PKTDATACOPYIMM_EXT_OPCODE;
340 FALLTHROUGH;
341 case EXT_OPCODE:
342 if (// imm >= LDM_EXT_OPCODE && -- but note imm is u32 and LDM_EXT_OPCODE is 0
343 imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) {
344 REG = ctx->mem.slot[imm - LDM_EXT_OPCODE];
345 } else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) {
346 ctx->mem.slot[imm - STM_EXT_OPCODE] = REG;
347 } else switch (imm) {
348 case NOT_EXT_OPCODE: REG = ~REG; break;
349 case NEG_EXT_OPCODE: REG = -REG; break;
350 case MOV_EXT_OPCODE: REG = OTHER_REG; break;
351 case SWAP_EXT_OPCODE: {
352 u32 tmp = REG;
353 REG = OTHER_REG;
354 OTHER_REG = tmp;
355 break;
356 }
357 case ALLOCATE_EXT_OPCODE:
358 ASSERT_RETURN(ctx->tx_buf == NULL);
359 if (reg_num == 0) {
360 ctx->tx_buf_len = REG;
361 } else {
362 ctx->tx_buf_len = decode_be16(ctx); // 2nd imm, at worst 6 B past prog_len
363 }
364 // checksumming functions requires minimum 266 byte buffer for correctness
365 if (ctx->tx_buf_len < 266) ctx->tx_buf_len = 266;
366 ctx->tx_buf = apf_allocate_buffer(ctx->caller_ctx, ctx->tx_buf_len);
367 if (!ctx->tx_buf) { // allocate failure
368 ctx->tx_buf_len = 0;
369 counter[-3]++;
370 return EXCEPTION;
371 }
372 memset(ctx->tx_buf, 0, ctx->tx_buf_len);
373 ctx->mem.named.tx_buf_offset = 0;
374 break;
375 case TRANSMIT_EXT_OPCODE: {
376 // tx_buf_len cannot be large because we'd run out of RAM,
377 // so the above unsigned comparison effectively guarantees casting pkt_len
378 // to a signed value does not result in it going negative.
379 u8 ip_ofs = DECODE_U8(); // 2nd imm, at worst 5 B past prog_len
380 u8 csum_ofs = DECODE_U8(); // 3rd imm, at worst 6 B past prog_len
381 u8 csum_start = 0;
382 u16 partial_csum = 0;
383 u32 pkt_len = ctx->mem.named.tx_buf_offset;
384 ASSERT_RETURN(ctx->tx_buf);
385 // If pkt_len > allocate_buffer_len, it means sth. wrong
386 // happened and the tx_buf should be deallocated.
387 if (pkt_len > ctx->tx_buf_len) {
388 do_discard_buffer(ctx);
389 return EXCEPTION;
390 }
391 if (csum_ofs < 255) {
392 csum_start = DECODE_U8(); // 4th imm, at worst 7 B past prog_len
393 partial_csum = decode_be16(ctx); // 5th imm, at worst 9 B past prog_len
394 }
395 {
396 int dscp = csum_and_return_dscp(ctx->tx_buf, (s32)pkt_len, ip_ofs,
397 partial_csum, csum_start, csum_ofs,
398 (bool)reg_num);
399 int ret = do_transmit_buffer(ctx, pkt_len, dscp);
400 if (ret) { counter[-4]++; return EXCEPTION; } // transmit failure
401 }
402 break;
403 }
404 case EPKTDATACOPYIMM_EXT_OPCODE: // 41
405 case EPKTDATACOPYR1_EXT_OPCODE: // 42
406 pktcopy_src_offset = ctx->R[0];
407 FALLTHROUGH;
408 case PKTDATACOPYIMM_EXT_OPCODE: { // 65536
409 u32 dst_offs = ctx->mem.named.tx_buf_offset;
410 u32 copy_len = ctx->R[1];
411 if (imm != EPKTDATACOPYR1_EXT_OPCODE) {
412 copy_len = DECODE_U8(); // 2nd imm, at worst 8 bytes past prog_len
413 }
414 ASSERT_RETURN(ctx->tx_buf);
415 ASSERT_IN_OUTPUT_BOUNDS(dst_offs, copy_len);
416 if (reg_num == 0) { // copy from packet
417 const u32 last_packet_offs = pktcopy_src_offset + copy_len - 1;
418 ASSERT_IN_PACKET_BOUNDS(pktcopy_src_offset);
419 ASSERT_RETURN(last_packet_offs >= pktcopy_src_offset);
420 ASSERT_IN_PACKET_BOUNDS(last_packet_offs);
421 memcpy(ctx->tx_buf + dst_offs, ctx->packet + pktcopy_src_offset, copy_len);
422 } else { // copy from data
423 ASSERT_IN_RAM_BOUNDS(pktcopy_src_offset + copy_len - 1);
424 memcpy(ctx->tx_buf + dst_offs, ctx->program + pktcopy_src_offset, copy_len);
425 }
426 dst_offs += copy_len;
427 ctx->mem.named.tx_buf_offset = dst_offs;
428 break;
429 }
430 case JDNSQMATCH_EXT_OPCODE: // 43
431 case JDNSAMATCH_EXT_OPCODE: // 44
432 case JDNSQMATCHSAFE_EXT_OPCODE: // 45
433 case JDNSAMATCHSAFE_EXT_OPCODE: { // 46
434 u32 jump_offs = decode_imm(ctx, imm_len); // 2nd imm, at worst 8 B past prog_len
435 int qtype = -1;
436 if (imm & 1) { // JDNSQMATCH & JDNSQMATCHSAFE are *odd* extended opcodes
437 qtype = DECODE_U8(); // 3rd imm, at worst 9 bytes past prog_len
438 }
439 {
440 u32 udp_payload_offset = ctx->R[0];
441 match_result_type match_rst = match_names(ctx->program + ctx->pc,
442 ctx->program + ctx->program_len,
443 ctx->packet + udp_payload_offset,
444 ctx->packet_len - udp_payload_offset,
445 qtype);
446 if (match_rst == error_program) return EXCEPTION;
447 if (match_rst == error_packet) {
448 counter[-5]++; // increment error dns packet counter
449 return (imm >= JDNSQMATCHSAFE_EXT_OPCODE) ? PASS : DROP;
450 }
451 while (ctx->pc + 1 < ctx->program_len &&
452 (ctx->program[ctx->pc] || ctx->program[ctx->pc + 1])) {
453 ctx->pc++;
454 }
455 ctx->pc += 2; // skip the final double 0 needle end
456 // relies on reg_num in {0,1} and match_rst being {false=0, true=1}
457 if (!(reg_num ^ (u32)match_rst)) ctx->pc += jump_offs;
458 }
459 break;
460 }
461 case EWRITE1_EXT_OPCODE:
462 case EWRITE2_EXT_OPCODE:
463 case EWRITE4_EXT_OPCODE: {
464 const u32 write_len = 1 << (imm - EWRITE1_EXT_OPCODE);
465 u32 i;
466 ASSERT_RETURN(ctx->tx_buf);
467 ASSERT_IN_OUTPUT_BOUNDS(ctx->mem.named.tx_buf_offset, write_len);
468 for (i = 0; i < write_len; ++i) {
469 ctx->tx_buf[ctx->mem.named.tx_buf_offset++] =
470 (u8)(REG >> (write_len - 1 - i) * 8);
471 }
472 break;
473 }
474 case JONEOF_EXT_OPCODE: {
475 u32 jump_offs = decode_imm(ctx, imm_len); // 2nd imm, at worst 8 B past prog_len
476 u8 imm3 = DECODE_U8(); // 3rd imm, at worst 9 bytes past prog_len
477 bool jmp = imm3 & 1; // =0 jmp on match, =1 jmp on no match
478 u8 len = ((imm3 >> 1) & 3) + 1; // size [1..4] in bytes of an element
479 u8 cnt = (imm3 >> 3) + 2; // number [2..33] of elements in set
480 if (ctx->pc + cnt * len > ctx->program_len) return EXCEPTION;
481 // cnt underflow on final iteration not an issue as not used after loop.
482 while (cnt--) {
483 u32 v = 0;
484 int i;
485 for (i = 0; i < len; ++i) v = (v << 8) | DECODE_U8();
486 if (REG == v) jmp ^= true;
487 }
488 if (jmp) ctx->pc += jump_offs;
489 break;
490 }
491 case EXCEPTIONBUFFER_EXT_OPCODE: {
492 ctx->except_buf_sz = decode_be16(ctx);
493 break;
494 }
495 default: // Unknown extended opcode
496 return EXCEPTION; // Bail out
497 }
498 break;
499 case LDDW_OPCODE:
500 case STDW_OPCODE:
501 if (ctx->v6) {
502 if (!imm) return EXCEPTION;
503 if (imm > 0xFFFF) return EXCEPTION;
504 if (imm * 4 > ctx->ram_len) return EXCEPTION;
505 if (opcode == LDDW_OPCODE) {
506 REG = counter[-(s32)imm];
507 } else {
508 counter[-(s32)imm] = REG;
509 }
510 } else {
511 u32 size = 4;
512 u32 offs = OTHER_REG + (u32)signed_imm;
513 // Negative offsets wrap around the end of the address space.
514 // This allows us to efficiently access the end of the
515 // address space with one-byte immediates without using %=.
516 if (offs & 0x80000000) offs += ctx->ram_len; // unsigned overflow intended
517 ASSERT_IN_DATA_BOUNDS(offs, size);
518 if (opcode == LDDW_OPCODE) {
519 u32 val = 0;
520 // size underflow on final iteration not an issue as not used after loop.
521 while (size--) val = (val << 8) | ctx->program[offs++];
522 REG = val;
523 } else {
524 u32 val = REG;
525 // size underflow on final iteration not an issue as not used after loop.
526 while (size--) {
527 ctx->program[offs++] = (val >> 24);
528 val <<= 8;
529 }
530 }
531 }
532 break;
533 case WRITE_OPCODE: {
534 ASSERT_RETURN(ctx->tx_buf);
535 ASSERT_RETURN(len_field);
536 {
537 const u32 write_len = 1 << (len_field - 1);
538 u32 i;
539 ASSERT_IN_OUTPUT_BOUNDS(ctx->mem.named.tx_buf_offset, write_len);
540 for (i = 0; i < write_len; ++i) {
541 ctx->tx_buf[ctx->mem.named.tx_buf_offset++] =
542 (u8)(imm >> (write_len - 1 - i) * 8);
543 }
544 }
545 break;
546 }
547 default: // Unknown opcode
548 return EXCEPTION; // Bail out
549 }
550 }
551 // instructions_remaining underflow on final iteration not an issue as not used after loop.
552 } while (instructions_remaining--);
553 return EXCEPTION;
554 }
555
apf_runner(void * ctx,u32 * const program,const u32 program_len,const u32 ram_len,const u8 * const packet,const u32 packet_len,const u32 filter_age_16384ths)556 static int apf_runner(void* ctx, u32* const program, const u32 program_len,
557 const u32 ram_len, const u8* const packet,
558 const u32 packet_len, const u32 filter_age_16384ths) {
559 // Due to direct 32-bit read/write access to counters at end of ram
560 // APFv6 interpreter requires program & ram_len to be 4 byte aligned.
561 if (3 & (uintptr_t)program) return EXCEPTION;
562 if (3 & ram_len) return EXCEPTION;
563
564 // We rely on ram_len + 65536 not overflowing, so require ram_len < 2GiB
565 // Similarly LDDW/STDW have special meaning for negative ram offsets.
566 // We also don't want garbage like program_len == 0xFFFFFFFF
567 if ((program_len | ram_len) >> 31) return EXCEPTION;
568
569 {
570 apf_context apf_ctx = { 0 };
571 int ret;
572
573 apf_ctx.ptr_size = sizeof(void*);
574 apf_ctx.caller_ctx = ctx;
575 apf_ctx.program = (u8*)program;
576 apf_ctx.program_len = program_len;
577 apf_ctx.ram_len = ram_len;
578 apf_ctx.packet = packet;
579 apf_ctx.packet_len = packet_len;
580 // Fill in pre-filled memory slot values.
581 apf_ctx.mem.named.program_size = program_len;
582 apf_ctx.mem.named.ram_len = ram_len;
583 apf_ctx.mem.named.packet_size = packet_len;
584 apf_ctx.mem.named.apf_version = apf_version();
585 apf_ctx.mem.named.filter_age = filter_age_16384ths >> 14;
586 apf_ctx.mem.named.filter_age_16384ths = filter_age_16384ths;
587
588 ret = do_apf_run(&apf_ctx);
589 if (apf_ctx.tx_buf) do_discard_buffer(&apf_ctx);
590 // Convert any exceptions internal to the program to just normal 'PASS'
591 if (ret >= EXCEPTION) {
592 u16 buf_size = apf_ctx.except_buf_sz;
593 if (buf_size >= sizeof(apf_ctx) && apf_ctx.program_len + buf_size <= apf_ctx.ram_len) {
594 u8* buf = apf_ctx.program + apf_ctx.program_len;
595 memcpy(buf, &apf_ctx, sizeof(apf_ctx));
596 buf_size -= sizeof(apf_ctx);
597 buf += sizeof(apf_ctx);
598 if (buf_size > apf_ctx.packet_len) buf_size = apf_ctx.packet_len;
599 memcpy(buf, apf_ctx.packet, buf_size);
600 }
601 ret = PASS;
602 }
603 return ret;
604 }
605 }
606
apf_run(void * ctx,u32 * const program,const u32 program_len,const u32 ram_len,const u8 * const packet,const u32 packet_len,const u32 filter_age_16384ths)607 int apf_run(void* ctx, u32* const program, const u32 program_len,
608 const u32 ram_len, const u8* const packet,
609 const u32 packet_len, const u32 filter_age_16384ths) {
610 // Any valid ethernet packet should be at least ETH_HLEN long...
611 if (!packet) return EXCEPTION;
612 if (packet_len < ETH_HLEN) return EXCEPTION;
613
614 return apf_runner(ctx, program, program_len, ram_len, packet, packet_len, filter_age_16384ths);
615 }
616