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