1 /* 2 * x86 decoder sanity test - based on test_get_insn.c 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 * 18 * Copyright (C) IBM Corporation, 2009 19 * Copyright (C) Hitachi, Ltd., 2011 20 */ 21 22 #include <stdlib.h> 23 #include <stdio.h> 24 #include <string.h> 25 #include <assert.h> 26 #include <unistd.h> 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <fcntl.h> 30 31 #define unlikely(cond) (cond) 32 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) 33 34 #include <asm/insn.h> 35 #include <inat.c> 36 #include <insn.c> 37 38 /* 39 * Test of instruction analysis against tampering. 40 * Feed random binary to instruction decoder and ensure not to 41 * access out-of-instruction-buffer. 42 */ 43 44 #define DEFAULT_MAX_ITER 10000 45 #define INSN_NOP 0x90 46 47 static const char *prog; /* Program name */ 48 static int verbose; /* Verbosity */ 49 static int x86_64; /* x86-64 bit mode flag */ 50 static unsigned int seed; /* Random seed */ 51 static unsigned long iter_start; /* Start of iteration number */ 52 static unsigned long iter_end = DEFAULT_MAX_ITER; /* End of iteration number */ 53 static FILE *input_file; /* Input file name */ 54 55 static void usage(const char *err) 56 { 57 if (err) 58 fprintf(stderr, "Error: %s\n\n", err); 59 fprintf(stderr, "Usage: %s [-y|-n|-v] [-s seed[,no]] [-m max] [-i input]\n", prog); 60 fprintf(stderr, "\t-y 64bit mode\n"); 61 fprintf(stderr, "\t-n 32bit mode\n"); 62 fprintf(stderr, "\t-v Verbosity(-vv dumps any decoded result)\n"); 63 fprintf(stderr, "\t-s Give a random seed (and iteration number)\n"); 64 fprintf(stderr, "\t-m Give a maximum iteration number\n"); 65 fprintf(stderr, "\t-i Give an input file with decoded binary\n"); 66 exit(1); 67 } 68 69 static void dump_field(FILE *fp, const char *name, const char *indent, 70 struct insn_field *field) 71 { 72 fprintf(fp, "%s.%s = {\n", indent, name); 73 fprintf(fp, "%s\t.value = %d, bytes[] = {%x, %x, %x, %x},\n", 74 indent, field->value, field->bytes[0], field->bytes[1], 75 field->bytes[2], field->bytes[3]); 76 fprintf(fp, "%s\t.got = %d, .nbytes = %d},\n", indent, 77 field->got, field->nbytes); 78 } 79 80 static void dump_insn(FILE *fp, struct insn *insn) 81 { 82 fprintf(fp, "Instruction = {\n"); 83 dump_field(fp, "prefixes", "\t", &insn->prefixes); 84 dump_field(fp, "rex_prefix", "\t", &insn->rex_prefix); 85 dump_field(fp, "vex_prefix", "\t", &insn->vex_prefix); 86 dump_field(fp, "opcode", "\t", &insn->opcode); 87 dump_field(fp, "modrm", "\t", &insn->modrm); 88 dump_field(fp, "sib", "\t", &insn->sib); 89 dump_field(fp, "displacement", "\t", &insn->displacement); 90 dump_field(fp, "immediate1", "\t", &insn->immediate1); 91 dump_field(fp, "immediate2", "\t", &insn->immediate2); 92 fprintf(fp, "\t.attr = %x, .opnd_bytes = %d, .addr_bytes = %d,\n", 93 insn->attr, insn->opnd_bytes, insn->addr_bytes); 94 fprintf(fp, "\t.length = %d, .x86_64 = %d, .kaddr = %p}\n", 95 insn->length, insn->x86_64, insn->kaddr); 96 } 97 98 static void dump_stream(FILE *fp, const char *msg, unsigned long nr_iter, 99 unsigned char *insn_buf, struct insn *insn) 100 { 101 int i; 102 103 fprintf(fp, "%s:\n", msg); 104 105 dump_insn(fp, insn); 106 107 fprintf(fp, "You can reproduce this with below command(s);\n"); 108 109 /* Input a decoded instruction sequence directly */ 110 fprintf(fp, " $ echo "); 111 for (i = 0; i < MAX_INSN_SIZE; i++) 112 fprintf(fp, " %02x", insn_buf[i]); 113 fprintf(fp, " | %s -i -\n", prog); 114 115 if (!input_file) { 116 fprintf(fp, "Or \n"); 117 /* Give a seed and iteration number */ 118 fprintf(fp, " $ %s -s 0x%x,%lu\n", prog, seed, nr_iter); 119 } 120 } 121 122 static void init_random_seed(void) 123 { 124 int fd; 125 126 fd = open("/dev/urandom", O_RDONLY); 127 if (fd < 0) 128 goto fail; 129 130 if (read(fd, &seed, sizeof(seed)) != sizeof(seed)) 131 goto fail; 132 133 close(fd); 134 return; 135 fail: 136 usage("Failed to open /dev/urandom"); 137 } 138 139 /* Read given instruction sequence from the input file */ 140 static int read_next_insn(unsigned char *insn_buf) 141 { 142 char buf[256] = "", *tmp; 143 int i; 144 145 tmp = fgets(buf, ARRAY_SIZE(buf), input_file); 146 if (tmp == NULL || feof(input_file)) 147 return 0; 148 149 for (i = 0; i < MAX_INSN_SIZE; i++) { 150 insn_buf[i] = (unsigned char)strtoul(tmp, &tmp, 16); 151 if (*tmp != ' ') 152 break; 153 } 154 155 return i; 156 } 157 158 static int generate_insn(unsigned char *insn_buf) 159 { 160 int i; 161 162 if (input_file) 163 return read_next_insn(insn_buf); 164 165 /* Fills buffer with random binary up to MAX_INSN_SIZE */ 166 for (i = 0; i < MAX_INSN_SIZE - 1; i += 2) 167 *(unsigned short *)(&insn_buf[i]) = random() & 0xffff; 168 169 while (i < MAX_INSN_SIZE) 170 insn_buf[i++] = random() & 0xff; 171 172 return i; 173 } 174 175 static void parse_args(int argc, char **argv) 176 { 177 int c; 178 char *tmp = NULL; 179 int set_seed = 0; 180 181 prog = argv[0]; 182 while ((c = getopt(argc, argv, "ynvs:m:i:")) != -1) { 183 switch (c) { 184 case 'y': 185 x86_64 = 1; 186 break; 187 case 'n': 188 x86_64 = 0; 189 break; 190 case 'v': 191 verbose++; 192 break; 193 case 'i': 194 if (strcmp("-", optarg) == 0) 195 input_file = stdin; 196 else 197 input_file = fopen(optarg, "r"); 198 if (!input_file) 199 usage("Failed to open input file"); 200 break; 201 case 's': 202 seed = (unsigned int)strtoul(optarg, &tmp, 0); 203 if (*tmp == ',') { 204 optarg = tmp + 1; 205 iter_start = strtoul(optarg, &tmp, 0); 206 } 207 if (*tmp != '\0' || tmp == optarg) 208 usage("Failed to parse seed"); 209 set_seed = 1; 210 break; 211 case 'm': 212 iter_end = strtoul(optarg, &tmp, 0); 213 if (*tmp != '\0' || tmp == optarg) 214 usage("Failed to parse max_iter"); 215 break; 216 default: 217 usage(NULL); 218 } 219 } 220 221 /* Check errors */ 222 if (iter_end < iter_start) 223 usage("Max iteration number must be bigger than iter-num"); 224 225 if (set_seed && input_file) 226 usage("Don't use input file (-i) with random seed (-s)"); 227 228 /* Initialize random seed */ 229 if (!input_file) { 230 if (!set_seed) /* No seed is given */ 231 init_random_seed(); 232 srand(seed); 233 } 234 } 235 236 int main(int argc, char **argv) 237 { 238 struct insn insn; 239 int insns = 0; 240 int errors = 0; 241 unsigned long i; 242 unsigned char insn_buf[MAX_INSN_SIZE * 2]; 243 244 parse_args(argc, argv); 245 246 /* Prepare stop bytes with NOPs */ 247 memset(insn_buf + MAX_INSN_SIZE, INSN_NOP, MAX_INSN_SIZE); 248 249 for (i = 0; i < iter_end; i++) { 250 if (generate_insn(insn_buf) <= 0) 251 break; 252 253 if (i < iter_start) /* Skip to given iteration number */ 254 continue; 255 256 /* Decode an instruction */ 257 insn_init(&insn, insn_buf, x86_64); 258 insn_get_length(&insn); 259 260 if (insn.next_byte <= insn.kaddr || 261 insn.kaddr + MAX_INSN_SIZE < insn.next_byte) { 262 /* Access out-of-range memory */ 263 dump_stream(stderr, "Error: Found an access violation", i, insn_buf, &insn); 264 errors++; 265 } else if (verbose && !insn_complete(&insn)) 266 dump_stream(stdout, "Info: Found an undecodable input", i, insn_buf, &insn); 267 else if (verbose >= 2) 268 dump_insn(stdout, &insn); 269 insns++; 270 } 271 272 fprintf(stdout, "%s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n", (errors) ? "Failure" : "Success", insns, (input_file) ? "given" : "random", errors, seed); 273 274 return errors ? 1 : 0; 275 } 276