• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016, 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 <stdint.h>
18 #include <stdio.h>
19 
20 #include "apf.h"
21 
22 // If "c" is of an unsigned type, generate a compile warning that gets promoted to an error.
23 // This makes bounds checking simpler because ">= 0" can be avoided. Otherwise adding
24 // superfluous ">= 0" with unsigned expressions generates compile warnings.
25 #define ENFORCE_UNSIGNED(c) ((c)==(uint32_t)(c))
26 
print_opcode(const char * opcode)27 static void print_opcode(const char* opcode) {
28   printf("%-6s", opcode);
29 }
30 
31 // Mapping from opcode number to opcode name.
32 static const char* opcode_names [] = {
33     [LDB_OPCODE] = "ldb",
34     [LDH_OPCODE] = "ldh",
35     [LDW_OPCODE] = "ldw",
36     [LDBX_OPCODE] = "ldb",
37     [LDHX_OPCODE] = "ldh",
38     [LDWX_OPCODE] = "ldw",
39     [ADD_OPCODE] = "add",
40     [MUL_OPCODE] = "mul",
41     [DIV_OPCODE] = "div",
42     [AND_OPCODE] = "and",
43     [OR_OPCODE] = "or",
44     [SH_OPCODE] = "sh",
45     [LI_OPCODE] = "li",
46     [JMP_OPCODE] = "jmp",
47     [JEQ_OPCODE] = "jeq",
48     [JNE_OPCODE] = "jne",
49     [JGT_OPCODE] = "jgt",
50     [JLT_OPCODE] = "jlt",
51     [JSET_OPCODE] = "jset",
52     [JNEBS_OPCODE] = "jnebs",
53     [LDDW_OPCODE] = "lddw",
54     [STDW_OPCODE] = "stdw",
55 };
56 
print_jump_target(uint32_t target,uint32_t program_len)57 static void print_jump_target(uint32_t target, uint32_t program_len) {
58     if (target == program_len) {
59         printf("pass");
60     } else if (target == program_len + 1) {
61         printf("drop");
62     } else {
63         printf("%u", target);
64     }
65 }
66 
67 // Disassembles an APF program. A hex dump of the program is supplied on stdin.
68 //
69 // NOTE: This is a simple debugging tool not meant for shipping or production use.  It is by no
70 //       means hardened against malicious input and contains known vulnerabilities.
71 //
72 // Example usage:
73 // adb shell dumpsys wifi ipmanager | sed '/Last program:/,+1!d;/Last program:/d;s/[ ]*//' | out/host/linux-x86/bin/apf_disassembler
main(void)74 int main(void) {
75   uint32_t program_len = 0;
76   uint8_t program[10000];
77 
78   // Read in hex program bytes
79   int byte;
80   while (scanf("%2x", &byte) == 1 && program_len < sizeof(program)) {
81       program[program_len++] = byte;
82   }
83 
84   for (uint32_t pc = 0; pc < program_len;) {
85       printf("%8u: ", pc);
86       const uint8_t bytecode = program[pc++];
87       const uint32_t opcode = EXTRACT_OPCODE(bytecode);
88 #define PRINT_OPCODE() print_opcode(opcode_names[opcode])
89       const uint32_t reg_num = EXTRACT_REGISTER(bytecode);
90       // All instructions have immediate fields, so load them now.
91       const uint32_t len_field = EXTRACT_IMM_LENGTH(bytecode);
92       uint32_t imm = 0;
93       int32_t signed_imm = 0;
94       if (len_field != 0) {
95           const uint32_t imm_len = 1 << (len_field - 1);
96           uint32_t i;
97           for (i = 0; i < imm_len && pc < program_len; i++)
98               imm = (imm << 8) | program[pc++];
99           // Sign extend imm into signed_imm.
100           signed_imm = imm << ((4 - imm_len) * 8);
101           signed_imm >>= (4 - imm_len) * 8;
102       }
103       switch (opcode) {
104           case LDB_OPCODE:
105           case LDH_OPCODE:
106           case LDW_OPCODE:
107               PRINT_OPCODE();
108               printf("r%d, [%u]", reg_num, imm);
109               break;
110           case LDBX_OPCODE:
111           case LDHX_OPCODE:
112           case LDWX_OPCODE:
113               PRINT_OPCODE();
114               printf("r%d, [%u+r1]", reg_num, imm);
115               break;
116           case JMP_OPCODE:
117               PRINT_OPCODE();
118               print_jump_target(pc + imm, program_len);
119               break;
120           case JEQ_OPCODE:
121           case JNE_OPCODE:
122           case JGT_OPCODE:
123           case JLT_OPCODE:
124           case JSET_OPCODE:
125           case JNEBS_OPCODE: {
126               PRINT_OPCODE();
127               printf("r0, ");
128               // Load second immediate field.
129               uint32_t cmp_imm = 0;
130               if (reg_num == 1) {
131                   printf("r1, ");
132               } else if (len_field == 0) {
133                   printf("0, ");
134               } else {
135                   uint32_t cmp_imm_len = 1 << (len_field - 1);
136                   uint32_t i;
137                   for (i = 0; i < cmp_imm_len && pc < program_len; i++)
138                       cmp_imm = (cmp_imm << 8) | program[pc++];
139                   printf("0x%x, ", cmp_imm);
140               }
141               if (opcode == JNEBS_OPCODE) {
142                   print_jump_target(pc + imm + cmp_imm, program_len);
143                   printf(", ");
144                   while (cmp_imm--)
145                       printf("%02x", program[pc++]);
146               } else {
147                   print_jump_target(pc + imm, program_len);
148               }
149               break;
150           }
151           case ADD_OPCODE:
152           case SH_OPCODE:
153               PRINT_OPCODE();
154               if (reg_num) {
155                   printf("r0, r1");
156               } else {
157                   printf("r0, %d", signed_imm);
158               }
159               break;
160           case MUL_OPCODE:
161           case DIV_OPCODE:
162           case AND_OPCODE:
163           case OR_OPCODE:
164               PRINT_OPCODE();
165               if (reg_num) {
166                   printf("r0, r1");
167               } else {
168                   printf("r0, %u", imm);
169               }
170               break;
171           case LI_OPCODE:
172               PRINT_OPCODE();
173               printf("r%d, %d", reg_num, signed_imm);
174               break;
175           case EXT_OPCODE:
176               if (
177 // If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result,
178 // instead just enforce that imm is unsigned (so it's always greater or equal to 0).
179 #if LDM_EXT_OPCODE == 0
180                   ENFORCE_UNSIGNED(imm) &&
181 #else
182                   imm >= LDM_EXT_OPCODE &&
183 #endif
184                   imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) {
185                   print_opcode("ldm");
186                   printf("r%d, m[%u]", reg_num, imm - LDM_EXT_OPCODE);
187               } else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) {
188                   print_opcode("stm");
189                   printf("r%d, m[%u]", reg_num, imm - STM_EXT_OPCODE);
190               } else switch (imm) {
191                   case NOT_EXT_OPCODE:
192                       print_opcode("not");
193                       printf("r%d", reg_num);
194                       break;
195                   case NEG_EXT_OPCODE:
196                       print_opcode("neg");
197                       printf("r%d", reg_num);
198                       break;
199                   case SWAP_EXT_OPCODE:
200                       print_opcode("swap");
201                       break;
202                   case MOV_EXT_OPCODE:
203                       print_opcode("mov");
204                       printf("r%d, r%d", reg_num, reg_num ^ 1);
205                       break;
206                   default:
207                       printf("unknown_ext %u", imm);
208                       break;
209               }
210               break;
211           case LDDW_OPCODE:
212           case STDW_OPCODE:
213               PRINT_OPCODE();
214               printf("r%u, [%d+r%u]", reg_num, signed_imm, reg_num ^ 1);
215               break;
216 
217           // Unknown opcode
218           default:
219               printf("unknown %u", opcode);
220               break;
221       }
222       printf("\n");
223   }
224   return 0;
225 }
226