• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 package android.net.apf;
17 
18 import static android.net.apf.BaseApfGenerator.Rbit.Rbit1;
19 import static android.net.apf.BaseApfGenerator.Register.R0;
20 import static android.net.apf.BaseApfGenerator.Register.R1;
21 
22 import android.annotation.NonNull;
23 
24 import com.android.internal.annotations.VisibleForTesting;
25 
26 import java.util.List;
27 import java.util.Set;
28 
29 /**
30  * APFv4 assembler/generator. A tool for generating an APFv4 program.
31  *
32  * @hide
33  */
34 public final class ApfV4Generator extends ApfV4GeneratorBase<ApfV4Generator> {
35 
36     public final short mCountAndDropLabel;
37     public final short mCountAndPassLabel;
38 
39     /**
40      * Returns true if we support the specified {@code version}, otherwise false.
41      */
supportsVersion(int version)42     public static boolean supportsVersion(int version) {
43         return version >= APF_VERSION_2;
44     }
45 
46     /**
47      * Creates an ApfV4Generator instance which is able to emit instructions for the specified
48      * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if
49      * the requested version is unsupported.
50      */
51     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
ApfV4Generator(int version, int ramSize, int clampSize, boolean disableCounterRangeCheck)52     public ApfV4Generator(int version, int ramSize, int clampSize, boolean disableCounterRangeCheck)
53             throws IllegalInstructionException {
54         // make sure mVersion is not greater than 4 when using this class
55         super(version > 4 ? 4 : version, ramSize, clampSize, disableCounterRangeCheck);
56         mCountAndDropLabel = version > 2 ? getUniqueLabel() : DROP_LABEL;
57         mCountAndPassLabel = version > 2 ? getUniqueLabel() : PASS_LABEL;
58     }
59 
60     /**
61      * Creates an ApfV4Generator instance which is able to emit instructions for the specified
62      * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if
63      * the requested version is unsupported.
64      */
ApfV4Generator(int version, int ramSize, int clampSize)65     public ApfV4Generator(int version, int ramSize, int clampSize)
66             throws IllegalInstructionException {
67         this(version, ramSize, clampSize, false);
68     }
69 
70     @Override
getBaseProgramSize()71     public int getBaseProgramSize() {
72         return 0;
73     }
74 
75     @Override
addR0ArithR1(Opcodes opcode)76     void addR0ArithR1(Opcodes opcode) {
77         append(new Instruction(opcode, Rbit1));  // APFv2/4: R0 op= R1
78     }
79 
80     /**
81      * Generates instructions to prepare to increment the specified counter and jump to the
82      * "__COUNT_AND_PASS__" label.
83      * In APFv2, it will directly return PASS.
84      *
85      * @param counter The ApfCounterTracker.Counter to increment
86      * @return Type the generator object
87      */
88     @Override
addCountAndPass(ApfCounterTracker.Counter counter)89     public ApfV4Generator addCountAndPass(ApfCounterTracker.Counter counter) {
90         checkPassCounterRange(counter);
91         return maybeAddLoadCounterOffset(R1, counter).addJump(mCountAndPassLabel);
92     }
93 
94     /**
95      * Generates instructions to prepare to increment the specified counter and jump to the
96      * "__COUNT_AND_DROP__" label.
97      * In APFv2, it will directly return DROP.
98      *
99      * @param counter The ApfCounterTracker.Counter to increment
100      * @return Type the generator object
101      */
102     @Override
addCountAndDrop(ApfCounterTracker.Counter counter)103     public ApfV4Generator addCountAndDrop(ApfCounterTracker.Counter counter) {
104         checkDropCounterRange(counter);
105         return maybeAddLoadCounterOffset(R1, counter).addJump(mCountAndDropLabel);
106     }
107 
108     @Override
addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt)109     public ApfV4Generator addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt) {
110         checkDropCounterRange(cnt);
111         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0Equals(val, mCountAndDropLabel);
112     }
113 
114     @Override
addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt)115     public ApfV4Generator addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt) {
116         checkPassCounterRange(cnt);
117         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0Equals(val, mCountAndPassLabel);
118     }
119 
120     @Override
addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt)121     public ApfV4Generator addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) {
122         checkDropCounterRange(cnt);
123         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0NotEquals(val, mCountAndDropLabel);
124     }
125 
126     @Override
addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt)127     public ApfV4Generator addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) {
128         checkPassCounterRange(cnt);
129         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0NotEquals(val, mCountAndPassLabel);
130     }
131 
132     @Override
addCountAndDropIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt)133     public ApfV4Generator addCountAndDropIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt) {
134         checkDropCounterRange(cnt);
135         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0AnyBitsSet(val, mCountAndDropLabel);
136     }
137 
138     @Override
addCountAndPassIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt)139     public ApfV4Generator addCountAndPassIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt) {
140         checkPassCounterRange(cnt);
141         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0AnyBitsSet(val, mCountAndPassLabel);
142     }
143 
144     @Override
addCountAndDropIfR0LessThan(long val, ApfCounterTracker.Counter cnt)145     public ApfV4Generator addCountAndDropIfR0LessThan(long val, ApfCounterTracker.Counter cnt) {
146         checkDropCounterRange(cnt);
147         if (val <= 0) {
148             throw new IllegalArgumentException("val must > 0, current val: " + val);
149         }
150         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0LessThan(val, mCountAndDropLabel);
151     }
152 
153     @Override
addCountAndPassIfR0LessThan(long val, ApfCounterTracker.Counter cnt)154     public ApfV4Generator addCountAndPassIfR0LessThan(long val, ApfCounterTracker.Counter cnt) {
155         checkPassCounterRange(cnt);
156         if (val <= 0) {
157             throw new IllegalArgumentException("val must > 0, current val: " + val);
158         }
159         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0LessThan(val, mCountAndPassLabel);
160     }
161 
162     @Override
addCountAndDropIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)163     public ApfV4Generator addCountAndDropIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)
164             throws IllegalInstructionException {
165         checkDropCounterRange(cnt);
166         if (val < 0 || val >= 4294967295L) {
167             throw new IllegalArgumentException("val must >= 0 and < 2^32-1, current val: " + val);
168         }
169         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0GreaterThan(val, mCountAndDropLabel);
170     }
171 
172     @Override
addCountAndPassIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)173     public ApfV4Generator addCountAndPassIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)
174             throws IllegalInstructionException {
175         checkPassCounterRange(cnt);
176         if (val < 0 || val >= 4294967295L) {
177             throw new IllegalArgumentException("val must >= 0 and < 2^32-1, current val: " + val);
178         }
179         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfR0GreaterThan(val, mCountAndPassLabel);
180     }
181 
182     @Override
addCountAndDropIfBytesAtR0NotEqual(byte[] bytes, ApfCounterTracker.Counter cnt)183     public ApfV4Generator addCountAndDropIfBytesAtR0NotEqual(byte[] bytes,
184             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
185         checkDropCounterRange(cnt);
186         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfBytesAtR0NotEqual(bytes,
187                 mCountAndDropLabel);
188     }
189 
190     @Override
addCountAndPassIfBytesAtR0NotEqual(byte[] bytes, ApfCounterTracker.Counter cnt)191     public ApfV4Generator addCountAndPassIfBytesAtR0NotEqual(byte[] bytes,
192             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
193         checkPassCounterRange(cnt);
194         return maybeAddLoadCounterOffset(R1, cnt).addJumpIfBytesAtR0NotEqual(bytes,
195                 mCountAndPassLabel);
196     }
197 
198     @Override
addCountAndPassIfR0IsOneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)199     public ApfV4Generator addCountAndPassIfR0IsOneOf(@NonNull Set<Long> values,
200             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
201         if (values.isEmpty()) {
202             throw new IllegalArgumentException("values cannot be empty");
203         }
204         checkPassCounterRange(cnt);
205         maybeAddLoadCounterOffset(R1, cnt);
206         for (Long v : values) {
207             addJumpIfR0Equals(v, mCountAndPassLabel);
208         }
209         return this;
210     }
211 
212     @Override
addCountAndDropIfR0IsOneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)213     public ApfV4Generator addCountAndDropIfR0IsOneOf(@NonNull Set<Long> values,
214             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
215         if (values.isEmpty()) {
216             throw new IllegalArgumentException("values cannot be empty");
217         }
218         checkDropCounterRange(cnt);
219         maybeAddLoadCounterOffset(R1, cnt);
220         for (Long v : values) {
221             addJumpIfR0Equals(v, mCountAndDropLabel);
222         }
223         return this;
224     }
225 
226     @Override
addCountAndPassIfR0IsNoneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)227     public ApfV4Generator addCountAndPassIfR0IsNoneOf(@NonNull Set<Long> values,
228             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
229         if (values.isEmpty()) {
230             throw new IllegalArgumentException("values cannot be empty");
231         }
232         short tgt = getUniqueLabel();
233         for (Long v : values) {
234             addJumpIfR0Equals(v, tgt);
235         }
236         addCountAndPass(cnt);
237         defineLabel(tgt);
238         return this;
239     }
240 
241     @Override
addCountAndDropIfR0IsNoneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)242     public ApfV4Generator addCountAndDropIfR0IsNoneOf(@NonNull Set<Long> values,
243             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
244         if (values.isEmpty()) {
245             throw new IllegalArgumentException("values cannot be empty");
246         }
247         short tgt = getUniqueLabel();
248         for (Long v : values) {
249             addJumpIfR0Equals(v, tgt);
250         }
251         addCountAndDrop(cnt);
252         defineLabel(tgt);
253         return this;
254     }
255 
addCountAndDropOrPassByMatchingBytesAtR0(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt, boolean matchAny, boolean drop)256     private ApfV4Generator addCountAndDropOrPassByMatchingBytesAtR0(@NonNull List<byte[]> bytesList,
257             ApfCounterTracker.Counter cnt, boolean matchAny, boolean drop)
258             throws IllegalInstructionException {
259         final List<byte[]> deduplicatedList = validateDeduplicateBytesList(bytesList);
260         maybeAddLoadCounterOffset(R1, cnt);
261         short matchLabel = getUniqueLabel();
262         short allNoMatchLabel = getUniqueLabel();
263         for (byte[] v : deduplicatedList) {
264             short notMatchLabel = getUniqueLabel();
265             addJumpIfBytesAtR0NotEqual(v, notMatchLabel);
266             addJump(matchLabel);
267             defineLabel(notMatchLabel);
268         }
269         if (matchAny) {
270             addJump(allNoMatchLabel);
271             defineLabel(matchLabel);
272         }
273         if (drop) {
274             addCountAndDrop(cnt);
275         } else {
276             addCountAndPass(cnt);
277         }
278         if (matchAny) {
279             defineLabel(allNoMatchLabel);
280         } else {
281             defineLabel(matchLabel);
282         }
283         return this;
284     }
285 
286     @Override
addCountAndDropIfBytesAtR0EqualsAnyOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)287     public ApfV4Generator addCountAndDropIfBytesAtR0EqualsAnyOf(@NonNull List<byte[]> bytesList,
288             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
289         return addCountAndDropOrPassByMatchingBytesAtR0(bytesList, cnt, true /* matchAny */,
290                 true /* drop */);
291     }
292 
293     @Override
addCountAndPassIfBytesAtR0EqualsAnyOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)294     public ApfV4Generator addCountAndPassIfBytesAtR0EqualsAnyOf(@NonNull List<byte[]> bytesList,
295             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
296         return addCountAndDropOrPassByMatchingBytesAtR0(bytesList, cnt, true /* matchAny */,
297                 false /* drop */);
298     }
299 
300     @Override
addCountAndDropIfBytesAtR0EqualsNoneOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)301     public ApfV4Generator addCountAndDropIfBytesAtR0EqualsNoneOf(@NonNull List<byte[]> bytesList,
302             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
303         return addCountAndDropOrPassByMatchingBytesAtR0(bytesList, cnt, false /* matchAny */,
304                 true /* drop */);
305     }
306 
307     @Override
addCountAndPassIfBytesAtR0EqualsNoneOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)308     public ApfV4Generator addCountAndPassIfBytesAtR0EqualsNoneOf(@NonNull List<byte[]> bytesList,
309             ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
310         return addCountAndDropOrPassByMatchingBytesAtR0(bytesList, cnt, false /* matchAny */,
311                 false /* drop */);
312     }
313 
314     /**
315      * Add an instruction to the end of the program to load 32 bits from the data memory into
316      * {@code register}. The source address is computed by adding the signed immediate
317      * {@code offset} to the other register.
318      * Requires APF v4 or greater.
319      */
addLoadData(Register dst, int ofs)320     public final ApfV4Generator addLoadData(Register dst, int ofs)
321             throws IllegalInstructionException {
322         requireApfVersion(3);
323         return append(new Instruction(Opcodes.LDDW, dst).addSigned(ofs));
324     }
325 
326     /**
327      * Add an instruction to the end of the program to store 32 bits from {@code register} into the
328      * data memory. The destination address is computed by adding the signed immediate
329      * {@code offset} to the other register.
330      * Requires APF v4 or greater.
331      */
addStoreData(Register src, int ofs)332     public final ApfV4Generator addStoreData(Register src, int ofs)
333             throws IllegalInstructionException {
334         requireApfVersion(3);
335         return append(new Instruction(Opcodes.STDW, src).addSigned(ofs));
336     }
337 
338     @Override
addLoadCounter(Register register, ApfCounterTracker.Counter counter)339     public ApfV4Generator addLoadCounter(Register register, ApfCounterTracker.Counter counter)
340             throws IllegalInstructionException {
341         if (mVersion <= 2) return self();
342         return maybeAddLoadCounterOffset(register.other(), counter).addLoadData(register, 0);
343     }
344 
345     @Override
addStoreCounter(ApfCounterTracker.Counter counter, Register register)346     public ApfV4Generator addStoreCounter(ApfCounterTracker.Counter counter, Register register)
347             throws IllegalInstructionException {
348         if (mVersion <= 2) return self();
349         return maybeAddLoadCounterOffset(register.other(), counter).addStoreData(register, 0);
350     }
351 
352     @Override
addAdd(long val)353     public ApfV4Generator addAdd(long val) {
354         if (val == 0) return self();  // nop, as APFv6 would '+= R1'
355         return append(new Instruction(Opcodes.ADD).addTwosCompUnsigned(val));
356     }
357 
358     @Override
addAnd(long val)359     public ApfV4Generator addAnd(long val) {
360         if (val == 0) return addLoadImmediate(R0, 0);  // equivalent, as APFv6 would '+= R1'
361         return append(new Instruction(Opcodes.AND).addTwosCompUnsigned(val));
362     }
363 
364     /**
365      * Append the count & (pass|drop) trampoline, which increments the counter at the data address
366      * pointed to by R1, then jumps to the (pass|drop) label. This saves a few bytes over inserting
367      * the entire sequence inline for every counter.
368      * This instruction is necessary to be called at the end of any APFv4 program in order to make
369      * counter incrementing logic work.
370      * In APFv2, it is a noop.
371      */
372     @Override
addCountTrampoline()373     public ApfV4Generator addCountTrampoline() throws IllegalInstructionException {
374         if (mVersion <= 2) return self();
375         return defineLabel(mCountAndPassLabel)
376                 .addLoadData(R0, 0)  // R0 = *(R1 + 0)
377                 .addAdd(1)           // R0++
378                 .addStoreData(R0, 0) // *(R1 + 0) = R0
379                 .addJump(PASS_LABEL)
380                 .defineLabel(mCountAndDropLabel)
381                 .addLoadData(R0, 0)  // R0 = *(R1 + 0)
382                 .addAdd(1)           // R0++
383                 .addStoreData(R0, 0) // *(R1 + 0) = R0
384                 .addJump(DROP_LABEL);
385     }
386 
387     @Override
getDefaultPacketHandlingSizeOverEstimate()388     public int getDefaultPacketHandlingSizeOverEstimate() {
389         // addCountAndPass(PASSED_IPV6_ICMP); -> 7 bytes
390         // defineLabel(mCountAndPassLabel)
391         // .addLoadData(R0, 0) ->  1 bytes
392         // .addAdd(1) -> 2 bytes
393         // .addStoreData(R0, 0) -> 1 bytes
394         // .addJump(PASS_LABEL) -> 5 bytes
395         // .defineLabel(mCountAndDropLabel)
396         // .addLoadData(R0, 0) -> 1 bytes
397         // .addAdd(1) -> 2 bytes
398         // .addStoreData(R0, 0) -> 1 bytes
399         // .addJump(DROP_LABEL); -> 5 bytes
400         return 25;
401     }
402 
403     /**
404      * This function is no-op in APFv4
405      */
406     @Override
updateExceptionBufferSize(int programSize)407     void updateExceptionBufferSize(int programSize) { }
408 
maybeAddLoadCounterOffset(Register reg, ApfCounterTracker.Counter cnt)409     private ApfV4Generator maybeAddLoadCounterOffset(Register reg, ApfCounterTracker.Counter cnt) {
410         if (mVersion <= 2) return self();
411         return addLoadImmediate(reg, cnt.offset());
412     }
413 }
414