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