18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * x86 decoder sanity test - based on test_get_insn.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) IBM Corporation, 2009 68c2ecf20Sopenharmony_ci * Copyright (C) Hitachi, Ltd., 2011 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <stdlib.h> 108c2ecf20Sopenharmony_ci#include <stdio.h> 118c2ecf20Sopenharmony_ci#include <string.h> 128c2ecf20Sopenharmony_ci#include <assert.h> 138c2ecf20Sopenharmony_ci#include <unistd.h> 148c2ecf20Sopenharmony_ci#include <sys/types.h> 158c2ecf20Sopenharmony_ci#include <sys/stat.h> 168c2ecf20Sopenharmony_ci#include <fcntl.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define unlikely(cond) (cond) 198c2ecf20Sopenharmony_ci#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <asm/insn.h> 228c2ecf20Sopenharmony_ci#include <inat.c> 238c2ecf20Sopenharmony_ci#include <insn.c> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Test of instruction analysis against tampering. 278c2ecf20Sopenharmony_ci * Feed random binary to instruction decoder and ensure not to 288c2ecf20Sopenharmony_ci * access out-of-instruction-buffer. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define DEFAULT_MAX_ITER 10000 328c2ecf20Sopenharmony_ci#define INSN_NOP 0x90 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic const char *prog; /* Program name */ 358c2ecf20Sopenharmony_cistatic int verbose; /* Verbosity */ 368c2ecf20Sopenharmony_cistatic int x86_64; /* x86-64 bit mode flag */ 378c2ecf20Sopenharmony_cistatic unsigned int seed; /* Random seed */ 388c2ecf20Sopenharmony_cistatic unsigned long iter_start; /* Start of iteration number */ 398c2ecf20Sopenharmony_cistatic unsigned long iter_end = DEFAULT_MAX_ITER; /* End of iteration number */ 408c2ecf20Sopenharmony_cistatic FILE *input_file; /* Input file name */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void usage(const char *err) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci if (err) 458c2ecf20Sopenharmony_ci fprintf(stderr, "%s: Error: %s\n\n", prog, err); 468c2ecf20Sopenharmony_ci fprintf(stderr, "Usage: %s [-y|-n|-v] [-s seed[,no]] [-m max] [-i input]\n", prog); 478c2ecf20Sopenharmony_ci fprintf(stderr, "\t-y 64bit mode\n"); 488c2ecf20Sopenharmony_ci fprintf(stderr, "\t-n 32bit mode\n"); 498c2ecf20Sopenharmony_ci fprintf(stderr, "\t-v Verbosity(-vv dumps any decoded result)\n"); 508c2ecf20Sopenharmony_ci fprintf(stderr, "\t-s Give a random seed (and iteration number)\n"); 518c2ecf20Sopenharmony_ci fprintf(stderr, "\t-m Give a maximum iteration number\n"); 528c2ecf20Sopenharmony_ci fprintf(stderr, "\t-i Give an input file with decoded binary\n"); 538c2ecf20Sopenharmony_ci exit(1); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void dump_field(FILE *fp, const char *name, const char *indent, 578c2ecf20Sopenharmony_ci struct insn_field *field) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci fprintf(fp, "%s.%s = {\n", indent, name); 608c2ecf20Sopenharmony_ci fprintf(fp, "%s\t.value = %d, bytes[] = {%x, %x, %x, %x},\n", 618c2ecf20Sopenharmony_ci indent, field->value, field->bytes[0], field->bytes[1], 628c2ecf20Sopenharmony_ci field->bytes[2], field->bytes[3]); 638c2ecf20Sopenharmony_ci fprintf(fp, "%s\t.got = %d, .nbytes = %d},\n", indent, 648c2ecf20Sopenharmony_ci field->got, field->nbytes); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void dump_insn(FILE *fp, struct insn *insn) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci fprintf(fp, "Instruction = {\n"); 708c2ecf20Sopenharmony_ci dump_field(fp, "prefixes", "\t", &insn->prefixes); 718c2ecf20Sopenharmony_ci dump_field(fp, "rex_prefix", "\t", &insn->rex_prefix); 728c2ecf20Sopenharmony_ci dump_field(fp, "vex_prefix", "\t", &insn->vex_prefix); 738c2ecf20Sopenharmony_ci dump_field(fp, "opcode", "\t", &insn->opcode); 748c2ecf20Sopenharmony_ci dump_field(fp, "modrm", "\t", &insn->modrm); 758c2ecf20Sopenharmony_ci dump_field(fp, "sib", "\t", &insn->sib); 768c2ecf20Sopenharmony_ci dump_field(fp, "displacement", "\t", &insn->displacement); 778c2ecf20Sopenharmony_ci dump_field(fp, "immediate1", "\t", &insn->immediate1); 788c2ecf20Sopenharmony_ci dump_field(fp, "immediate2", "\t", &insn->immediate2); 798c2ecf20Sopenharmony_ci fprintf(fp, "\t.attr = %x, .opnd_bytes = %d, .addr_bytes = %d,\n", 808c2ecf20Sopenharmony_ci insn->attr, insn->opnd_bytes, insn->addr_bytes); 818c2ecf20Sopenharmony_ci fprintf(fp, "\t.length = %d, .x86_64 = %d, .kaddr = %p}\n", 828c2ecf20Sopenharmony_ci insn->length, insn->x86_64, insn->kaddr); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void dump_stream(FILE *fp, const char *msg, unsigned long nr_iter, 868c2ecf20Sopenharmony_ci unsigned char *insn_buff, struct insn *insn) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int i; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci fprintf(fp, "%s:\n", msg); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci dump_insn(fp, insn); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci fprintf(fp, "You can reproduce this with below command(s);\n"); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Input a decoded instruction sequence directly */ 978c2ecf20Sopenharmony_ci fprintf(fp, " $ echo "); 988c2ecf20Sopenharmony_ci for (i = 0; i < MAX_INSN_SIZE; i++) 998c2ecf20Sopenharmony_ci fprintf(fp, " %02x", insn_buff[i]); 1008c2ecf20Sopenharmony_ci fprintf(fp, " | %s -i -\n", prog); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (!input_file) { 1038c2ecf20Sopenharmony_ci fprintf(fp, "Or \n"); 1048c2ecf20Sopenharmony_ci /* Give a seed and iteration number */ 1058c2ecf20Sopenharmony_ci fprintf(fp, " $ %s -s 0x%x,%lu\n", prog, seed, nr_iter); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void init_random_seed(void) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci int fd; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci fd = open("/dev/urandom", O_RDONLY); 1148c2ecf20Sopenharmony_ci if (fd < 0) 1158c2ecf20Sopenharmony_ci goto fail; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (read(fd, &seed, sizeof(seed)) != sizeof(seed)) 1188c2ecf20Sopenharmony_ci goto fail; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci close(fd); 1218c2ecf20Sopenharmony_ci return; 1228c2ecf20Sopenharmony_cifail: 1238c2ecf20Sopenharmony_ci usage("Failed to open /dev/urandom"); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* Read given instruction sequence from the input file */ 1278c2ecf20Sopenharmony_cistatic int read_next_insn(unsigned char *insn_buff) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci char buf[256] = "", *tmp; 1308c2ecf20Sopenharmony_ci int i; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci tmp = fgets(buf, ARRAY_SIZE(buf), input_file); 1338c2ecf20Sopenharmony_ci if (tmp == NULL || feof(input_file)) 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = 0; i < MAX_INSN_SIZE; i++) { 1378c2ecf20Sopenharmony_ci insn_buff[i] = (unsigned char)strtoul(tmp, &tmp, 16); 1388c2ecf20Sopenharmony_ci if (*tmp != ' ') 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return i; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int generate_insn(unsigned char *insn_buff) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci int i; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (input_file) 1508c2ecf20Sopenharmony_ci return read_next_insn(insn_buff); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Fills buffer with random binary up to MAX_INSN_SIZE */ 1538c2ecf20Sopenharmony_ci for (i = 0; i < MAX_INSN_SIZE - 1; i += 2) 1548c2ecf20Sopenharmony_ci *(unsigned short *)(&insn_buff[i]) = random() & 0xffff; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci while (i < MAX_INSN_SIZE) 1578c2ecf20Sopenharmony_ci insn_buff[i++] = random() & 0xff; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return i; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void parse_args(int argc, char **argv) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int c; 1658c2ecf20Sopenharmony_ci char *tmp = NULL; 1668c2ecf20Sopenharmony_ci int set_seed = 0; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci prog = argv[0]; 1698c2ecf20Sopenharmony_ci while ((c = getopt(argc, argv, "ynvs:m:i:")) != -1) { 1708c2ecf20Sopenharmony_ci switch (c) { 1718c2ecf20Sopenharmony_ci case 'y': 1728c2ecf20Sopenharmony_ci x86_64 = 1; 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci case 'n': 1758c2ecf20Sopenharmony_ci x86_64 = 0; 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci case 'v': 1788c2ecf20Sopenharmony_ci verbose++; 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci case 'i': 1818c2ecf20Sopenharmony_ci if (strcmp("-", optarg) == 0) 1828c2ecf20Sopenharmony_ci input_file = stdin; 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci input_file = fopen(optarg, "r"); 1858c2ecf20Sopenharmony_ci if (!input_file) 1868c2ecf20Sopenharmony_ci usage("Failed to open input file"); 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci case 's': 1898c2ecf20Sopenharmony_ci seed = (unsigned int)strtoul(optarg, &tmp, 0); 1908c2ecf20Sopenharmony_ci if (*tmp == ',') { 1918c2ecf20Sopenharmony_ci optarg = tmp + 1; 1928c2ecf20Sopenharmony_ci iter_start = strtoul(optarg, &tmp, 0); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci if (*tmp != '\0' || tmp == optarg) 1958c2ecf20Sopenharmony_ci usage("Failed to parse seed"); 1968c2ecf20Sopenharmony_ci set_seed = 1; 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci case 'm': 1998c2ecf20Sopenharmony_ci iter_end = strtoul(optarg, &tmp, 0); 2008c2ecf20Sopenharmony_ci if (*tmp != '\0' || tmp == optarg) 2018c2ecf20Sopenharmony_ci usage("Failed to parse max_iter"); 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci default: 2048c2ecf20Sopenharmony_ci usage(NULL); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* Check errors */ 2098c2ecf20Sopenharmony_ci if (iter_end < iter_start) 2108c2ecf20Sopenharmony_ci usage("Max iteration number must be bigger than iter-num"); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (set_seed && input_file) 2138c2ecf20Sopenharmony_ci usage("Don't use input file (-i) with random seed (-s)"); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* Initialize random seed */ 2168c2ecf20Sopenharmony_ci if (!input_file) { 2178c2ecf20Sopenharmony_ci if (!set_seed) /* No seed is given */ 2188c2ecf20Sopenharmony_ci init_random_seed(); 2198c2ecf20Sopenharmony_ci srand(seed); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ciint main(int argc, char **argv) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct insn insn; 2268c2ecf20Sopenharmony_ci int insns = 0; 2278c2ecf20Sopenharmony_ci int errors = 0; 2288c2ecf20Sopenharmony_ci unsigned long i; 2298c2ecf20Sopenharmony_ci unsigned char insn_buff[MAX_INSN_SIZE * 2]; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci parse_args(argc, argv); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Prepare stop bytes with NOPs */ 2348c2ecf20Sopenharmony_ci memset(insn_buff + MAX_INSN_SIZE, INSN_NOP, MAX_INSN_SIZE); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci for (i = 0; i < iter_end; i++) { 2378c2ecf20Sopenharmony_ci if (generate_insn(insn_buff) <= 0) 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (i < iter_start) /* Skip to given iteration number */ 2418c2ecf20Sopenharmony_ci continue; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* Decode an instruction */ 2448c2ecf20Sopenharmony_ci insn_init(&insn, insn_buff, sizeof(insn_buff), x86_64); 2458c2ecf20Sopenharmony_ci insn_get_length(&insn); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (insn.next_byte <= insn.kaddr || 2488c2ecf20Sopenharmony_ci insn.kaddr + MAX_INSN_SIZE < insn.next_byte) { 2498c2ecf20Sopenharmony_ci /* Access out-of-range memory */ 2508c2ecf20Sopenharmony_ci dump_stream(stderr, "Error: Found an access violation", i, insn_buff, &insn); 2518c2ecf20Sopenharmony_ci errors++; 2528c2ecf20Sopenharmony_ci } else if (verbose && !insn_complete(&insn)) 2538c2ecf20Sopenharmony_ci dump_stream(stdout, "Info: Found an undecodable input", i, insn_buff, &insn); 2548c2ecf20Sopenharmony_ci else if (verbose >= 2) 2558c2ecf20Sopenharmony_ci dump_insn(stdout, &insn); 2568c2ecf20Sopenharmony_ci insns++; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci fprintf((errors) ? stderr : stdout, 2608c2ecf20Sopenharmony_ci "%s: %s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n", 2618c2ecf20Sopenharmony_ci prog, 2628c2ecf20Sopenharmony_ci (errors) ? "Failure" : "Success", 2638c2ecf20Sopenharmony_ci insns, 2648c2ecf20Sopenharmony_ci (input_file) ? "given" : "random", 2658c2ecf20Sopenharmony_ci errors, 2668c2ecf20Sopenharmony_ci seed); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return errors ? 1 : 0; 2698c2ecf20Sopenharmony_ci} 270