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