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 };
54 
print_jump_target(uint32_t target,uint32_t program_len)55 static void print_jump_target(uint32_t target, uint32_t program_len) {
56     if (target == program_len) {
57         printf("pass");
58     } else if (target == program_len + 1) {
59         printf("drop");
60     } else {
61         printf("%u", target);
62     }
63 }
64 
65 // Disassembles an APF program. A hex dump of the program is supplied on stdin.
66 //
67 // NOTE: This is a simple debugging tool not meant for shipping or production use.  It is by no
68 //       means hardened against malicious input and contains known vulnerabilities.
69 //
70 // Example usage:
71 // adb shell dumpsys wifi ipmanager | sed '/Last program:/,+1!d;/Last program:/d;s/[ ]*//' | out/host/linux-x86/bin/apf_disassembler
main(void)72 int main(void) {
73   uint32_t program_len = 0;
74   uint8_t program[10000];
75 
76   // Read in hex program bytes
77   int byte;
78   while (scanf("%2x", &byte) == 1 && program_len < sizeof(program)) {
79       program[program_len++] = byte;
80   }
81 
82   for (uint32_t pc = 0; pc < program_len;) {
83       printf("%8u: ", pc);
84       const uint8_t bytecode = program[pc++];
85       const uint32_t opcode = EXTRACT_OPCODE(bytecode);
86 #define PRINT_OPCODE() print_opcode(opcode_names[opcode])
87       const uint32_t reg_num = EXTRACT_REGISTER(bytecode);
88       // All instructions have immediate fields, so load them now.
89       const uint32_t len_field = EXTRACT_IMM_LENGTH(bytecode);
90       uint32_t imm = 0;
91       int32_t signed_imm = 0;
92       if (len_field != 0) {
93           const uint32_t imm_len = 1 << (len_field - 1);
94           uint32_t i;
95           for (i = 0; i < imm_len; i++)
96               imm = (imm << 8) | program[pc++];
97           // Sign extend imm into signed_imm.
98           signed_imm = imm << ((4 - imm_len) * 8);
99           signed_imm >>= (4 - imm_len) * 8;
100       }
101       switch (opcode) {
102           case LDB_OPCODE:
103           case LDH_OPCODE:
104           case LDW_OPCODE:
105               PRINT_OPCODE();
106               printf("r%d, [%u]", reg_num, imm);
107               break;
108           case LDBX_OPCODE:
109           case LDHX_OPCODE:
110           case LDWX_OPCODE:
111               PRINT_OPCODE();
112               printf("r%d, [%u+r1]", reg_num, imm);
113               break;
114           case JMP_OPCODE:
115               PRINT_OPCODE();
116               print_jump_target(pc + imm, program_len);
117               break;
118           case JEQ_OPCODE:
119           case JNE_OPCODE:
120           case JGT_OPCODE:
121           case JLT_OPCODE:
122           case JSET_OPCODE:
123           case JNEBS_OPCODE: {
124               PRINT_OPCODE();
125               printf("r0, ");
126               // Load second immediate field.
127               uint32_t cmp_imm = 0;
128               if (reg_num == 1) {
129                   printf("r1, ");
130               } else if (len_field == 0) {
131                   printf("0, ");
132               } else {
133                   uint32_t cmp_imm_len = 1 << (len_field - 1);
134                   uint32_t i;
135                   for (i = 0; i < cmp_imm_len; i++)
136                       cmp_imm = (cmp_imm << 8) | program[pc++];
137                   printf("0x%x, ", cmp_imm);
138               }
139               if (opcode == JNEBS_OPCODE) {
140                   print_jump_target(pc + imm + cmp_imm, program_len);
141                   printf(", ");
142                   while (cmp_imm--)
143                       printf("%02x", program[pc++]);
144               } else {
145                   print_jump_target(pc + imm, program_len);
146               }
147               break;
148           }
149           case ADD_OPCODE:
150           case SH_OPCODE:
151               PRINT_OPCODE();
152               if (reg_num) {
153                   printf("r0, r1");
154               } else {
155                   printf("r0, %d", signed_imm);
156               }
157               break;
158           case MUL_OPCODE:
159           case DIV_OPCODE:
160           case AND_OPCODE:
161           case OR_OPCODE:
162               PRINT_OPCODE();
163               if (reg_num) {
164                   printf("r0, r1");
165               } else {
166                   printf("r0, %u", imm);
167               }
168               break;
169           case LI_OPCODE:
170               PRINT_OPCODE();
171               printf("r%d, %d", reg_num, signed_imm);
172               break;
173           case EXT_OPCODE:
174               if (
175 // If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result,
176 // instead just enforce that imm is unsigned (so it's always greater or equal to 0).
177 #if LDM_EXT_OPCODE == 0
178                   ENFORCE_UNSIGNED(imm) &&
179 #else
180                   imm >= LDM_EXT_OPCODE &&
181 #endif
182                   imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) {
183                   print_opcode("ldm");
184                   printf("r%d, m[%u]", reg_num, imm - LDM_EXT_OPCODE);
185               } else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) {
186                   print_opcode("stm");
187                   printf("r%d, m[%u]", reg_num, imm - STM_EXT_OPCODE);
188               } else switch (imm) {
189                   case NOT_EXT_OPCODE:
190                       print_opcode("not");
191                       printf("r%d", reg_num);
192                       break;
193                   case NEG_EXT_OPCODE:
194                       print_opcode("neg");
195                       printf("r%d", reg_num);
196                       break;
197                   case SWAP_EXT_OPCODE:
198                       print_opcode("swap");
199                       break;
200                   case MOV_EXT_OPCODE:
201                       print_opcode("mov");
202                       printf("r%d, r%d", reg_num, reg_num ^ 1);
203                       break;
204                   default:
205                       printf("unknown_ext %u", imm);
206                       break;
207               }
208               break;
209           // Unknown opcode
210           default:
211               printf("unknown %u", opcode);
212               break;
213       }
214       printf("\n");
215   }
216   return 0;
217 }
218