162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * x86 decoder sanity test - based on test_get_insn.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) IBM Corporation, 2009 662306a36Sopenharmony_ci * Copyright (C) Hitachi, Ltd., 2011 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include <stdio.h> 1162306a36Sopenharmony_ci#include <string.h> 1262306a36Sopenharmony_ci#include <assert.h> 1362306a36Sopenharmony_ci#include <unistd.h> 1462306a36Sopenharmony_ci#include <sys/types.h> 1562306a36Sopenharmony_ci#include <sys/stat.h> 1662306a36Sopenharmony_ci#include <fcntl.h> 1762306a36Sopenharmony_ci#include <asm/insn.h> 1862306a36Sopenharmony_ci#include <inat.c> 1962306a36Sopenharmony_ci#include <insn.c> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * Test of instruction analysis against tampering. 2362306a36Sopenharmony_ci * Feed random binary to instruction decoder and ensure not to 2462306a36Sopenharmony_ci * access out-of-instruction-buffer. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define DEFAULT_MAX_ITER 10000 2862306a36Sopenharmony_ci#define INSN_NOP 0x90 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic const char *prog; /* Program name */ 3162306a36Sopenharmony_cistatic int verbose; /* Verbosity */ 3262306a36Sopenharmony_cistatic int x86_64; /* x86-64 bit mode flag */ 3362306a36Sopenharmony_cistatic unsigned int seed; /* Random seed */ 3462306a36Sopenharmony_cistatic unsigned long iter_start; /* Start of iteration number */ 3562306a36Sopenharmony_cistatic unsigned long iter_end = DEFAULT_MAX_ITER; /* End of iteration number */ 3662306a36Sopenharmony_cistatic FILE *input_file; /* Input file name */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void usage(const char *err) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci if (err) 4162306a36Sopenharmony_ci fprintf(stderr, "%s: Error: %s\n\n", prog, err); 4262306a36Sopenharmony_ci fprintf(stderr, "Usage: %s [-y|-n|-v] [-s seed[,no]] [-m max] [-i input]\n", prog); 4362306a36Sopenharmony_ci fprintf(stderr, "\t-y 64bit mode\n"); 4462306a36Sopenharmony_ci fprintf(stderr, "\t-n 32bit mode\n"); 4562306a36Sopenharmony_ci fprintf(stderr, "\t-v Verbosity(-vv dumps any decoded result)\n"); 4662306a36Sopenharmony_ci fprintf(stderr, "\t-s Give a random seed (and iteration number)\n"); 4762306a36Sopenharmony_ci fprintf(stderr, "\t-m Give a maximum iteration number\n"); 4862306a36Sopenharmony_ci fprintf(stderr, "\t-i Give an input file with decoded binary\n"); 4962306a36Sopenharmony_ci exit(1); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void dump_field(FILE *fp, const char *name, const char *indent, 5362306a36Sopenharmony_ci struct insn_field *field) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci fprintf(fp, "%s.%s = {\n", indent, name); 5662306a36Sopenharmony_ci fprintf(fp, "%s\t.value = %d, bytes[] = {%x, %x, %x, %x},\n", 5762306a36Sopenharmony_ci indent, field->value, field->bytes[0], field->bytes[1], 5862306a36Sopenharmony_ci field->bytes[2], field->bytes[3]); 5962306a36Sopenharmony_ci fprintf(fp, "%s\t.got = %d, .nbytes = %d},\n", indent, 6062306a36Sopenharmony_ci field->got, field->nbytes); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void dump_insn(FILE *fp, struct insn *insn) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci fprintf(fp, "Instruction = {\n"); 6662306a36Sopenharmony_ci dump_field(fp, "prefixes", "\t", &insn->prefixes); 6762306a36Sopenharmony_ci dump_field(fp, "rex_prefix", "\t", &insn->rex_prefix); 6862306a36Sopenharmony_ci dump_field(fp, "vex_prefix", "\t", &insn->vex_prefix); 6962306a36Sopenharmony_ci dump_field(fp, "opcode", "\t", &insn->opcode); 7062306a36Sopenharmony_ci dump_field(fp, "modrm", "\t", &insn->modrm); 7162306a36Sopenharmony_ci dump_field(fp, "sib", "\t", &insn->sib); 7262306a36Sopenharmony_ci dump_field(fp, "displacement", "\t", &insn->displacement); 7362306a36Sopenharmony_ci dump_field(fp, "immediate1", "\t", &insn->immediate1); 7462306a36Sopenharmony_ci dump_field(fp, "immediate2", "\t", &insn->immediate2); 7562306a36Sopenharmony_ci fprintf(fp, "\t.attr = %x, .opnd_bytes = %d, .addr_bytes = %d,\n", 7662306a36Sopenharmony_ci insn->attr, insn->opnd_bytes, insn->addr_bytes); 7762306a36Sopenharmony_ci fprintf(fp, "\t.length = %d, .x86_64 = %d, .kaddr = %p}\n", 7862306a36Sopenharmony_ci insn->length, insn->x86_64, insn->kaddr); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void dump_stream(FILE *fp, const char *msg, unsigned long nr_iter, 8262306a36Sopenharmony_ci unsigned char *insn_buff, struct insn *insn) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci int i; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci fprintf(fp, "%s:\n", msg); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci dump_insn(fp, insn); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci fprintf(fp, "You can reproduce this with below command(s);\n"); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Input a decoded instruction sequence directly */ 9362306a36Sopenharmony_ci fprintf(fp, " $ echo "); 9462306a36Sopenharmony_ci for (i = 0; i < MAX_INSN_SIZE; i++) 9562306a36Sopenharmony_ci fprintf(fp, " %02x", insn_buff[i]); 9662306a36Sopenharmony_ci fprintf(fp, " | %s -i -\n", prog); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (!input_file) { 9962306a36Sopenharmony_ci fprintf(fp, "Or \n"); 10062306a36Sopenharmony_ci /* Give a seed and iteration number */ 10162306a36Sopenharmony_ci fprintf(fp, " $ %s -s 0x%x,%lu\n", prog, seed, nr_iter); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void init_random_seed(void) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci int fd; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci fd = open("/dev/urandom", O_RDONLY); 11062306a36Sopenharmony_ci if (fd < 0) 11162306a36Sopenharmony_ci goto fail; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (read(fd, &seed, sizeof(seed)) != sizeof(seed)) 11462306a36Sopenharmony_ci goto fail; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci close(fd); 11762306a36Sopenharmony_ci return; 11862306a36Sopenharmony_cifail: 11962306a36Sopenharmony_ci usage("Failed to open /dev/urandom"); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* Read given instruction sequence from the input file */ 12362306a36Sopenharmony_cistatic int read_next_insn(unsigned char *insn_buff) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci char buf[256] = "", *tmp; 12662306a36Sopenharmony_ci int i; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci tmp = fgets(buf, ARRAY_SIZE(buf), input_file); 12962306a36Sopenharmony_ci if (tmp == NULL || feof(input_file)) 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci for (i = 0; i < MAX_INSN_SIZE; i++) { 13362306a36Sopenharmony_ci insn_buff[i] = (unsigned char)strtoul(tmp, &tmp, 16); 13462306a36Sopenharmony_ci if (*tmp != ' ') 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return i; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int generate_insn(unsigned char *insn_buff) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int i; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (input_file) 14662306a36Sopenharmony_ci return read_next_insn(insn_buff); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Fills buffer with random binary up to MAX_INSN_SIZE */ 14962306a36Sopenharmony_ci for (i = 0; i < MAX_INSN_SIZE - 1; i += 2) 15062306a36Sopenharmony_ci *(unsigned short *)(&insn_buff[i]) = random() & 0xffff; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci while (i < MAX_INSN_SIZE) 15362306a36Sopenharmony_ci insn_buff[i++] = random() & 0xff; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return i; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void parse_args(int argc, char **argv) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci int c; 16162306a36Sopenharmony_ci char *tmp = NULL; 16262306a36Sopenharmony_ci int set_seed = 0; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci prog = argv[0]; 16562306a36Sopenharmony_ci while ((c = getopt(argc, argv, "ynvs:m:i:")) != -1) { 16662306a36Sopenharmony_ci switch (c) { 16762306a36Sopenharmony_ci case 'y': 16862306a36Sopenharmony_ci x86_64 = 1; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci case 'n': 17162306a36Sopenharmony_ci x86_64 = 0; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci case 'v': 17462306a36Sopenharmony_ci verbose++; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci case 'i': 17762306a36Sopenharmony_ci if (strcmp("-", optarg) == 0) 17862306a36Sopenharmony_ci input_file = stdin; 17962306a36Sopenharmony_ci else 18062306a36Sopenharmony_ci input_file = fopen(optarg, "r"); 18162306a36Sopenharmony_ci if (!input_file) 18262306a36Sopenharmony_ci usage("Failed to open input file"); 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci case 's': 18562306a36Sopenharmony_ci seed = (unsigned int)strtoul(optarg, &tmp, 0); 18662306a36Sopenharmony_ci if (*tmp == ',') { 18762306a36Sopenharmony_ci optarg = tmp + 1; 18862306a36Sopenharmony_ci iter_start = strtoul(optarg, &tmp, 0); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci if (*tmp != '\0' || tmp == optarg) 19162306a36Sopenharmony_ci usage("Failed to parse seed"); 19262306a36Sopenharmony_ci set_seed = 1; 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci case 'm': 19562306a36Sopenharmony_ci iter_end = strtoul(optarg, &tmp, 0); 19662306a36Sopenharmony_ci if (*tmp != '\0' || tmp == optarg) 19762306a36Sopenharmony_ci usage("Failed to parse max_iter"); 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci default: 20062306a36Sopenharmony_ci usage(NULL); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Check errors */ 20562306a36Sopenharmony_ci if (iter_end < iter_start) 20662306a36Sopenharmony_ci usage("Max iteration number must be bigger than iter-num"); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (set_seed && input_file) 20962306a36Sopenharmony_ci usage("Don't use input file (-i) with random seed (-s)"); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Initialize random seed */ 21262306a36Sopenharmony_ci if (!input_file) { 21362306a36Sopenharmony_ci if (!set_seed) /* No seed is given */ 21462306a36Sopenharmony_ci init_random_seed(); 21562306a36Sopenharmony_ci srand(seed); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ciint main(int argc, char **argv) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int insns = 0, ret; 22262306a36Sopenharmony_ci struct insn insn; 22362306a36Sopenharmony_ci int errors = 0; 22462306a36Sopenharmony_ci unsigned long i; 22562306a36Sopenharmony_ci unsigned char insn_buff[MAX_INSN_SIZE * 2]; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci parse_args(argc, argv); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Prepare stop bytes with NOPs */ 23062306a36Sopenharmony_ci memset(insn_buff + MAX_INSN_SIZE, INSN_NOP, MAX_INSN_SIZE); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci for (i = 0; i < iter_end; i++) { 23362306a36Sopenharmony_ci if (generate_insn(insn_buff) <= 0) 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (i < iter_start) /* Skip to given iteration number */ 23762306a36Sopenharmony_ci continue; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Decode an instruction */ 24062306a36Sopenharmony_ci ret = insn_decode(&insn, insn_buff, sizeof(insn_buff), 24162306a36Sopenharmony_ci x86_64 ? INSN_MODE_64 : INSN_MODE_32); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (insn.next_byte <= insn.kaddr || 24462306a36Sopenharmony_ci insn.kaddr + MAX_INSN_SIZE < insn.next_byte) { 24562306a36Sopenharmony_ci /* Access out-of-range memory */ 24662306a36Sopenharmony_ci dump_stream(stderr, "Error: Found an access violation", i, insn_buff, &insn); 24762306a36Sopenharmony_ci errors++; 24862306a36Sopenharmony_ci } else if (verbose && ret < 0) 24962306a36Sopenharmony_ci dump_stream(stdout, "Info: Found an undecodable input", i, insn_buff, &insn); 25062306a36Sopenharmony_ci else if (verbose >= 2) 25162306a36Sopenharmony_ci dump_insn(stdout, &insn); 25262306a36Sopenharmony_ci insns++; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci fprintf((errors) ? stderr : stdout, 25662306a36Sopenharmony_ci "%s: %s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n", 25762306a36Sopenharmony_ci prog, 25862306a36Sopenharmony_ci (errors) ? "Failure" : "Success", 25962306a36Sopenharmony_ci insns, 26062306a36Sopenharmony_ci (input_file) ? "given" : "random", 26162306a36Sopenharmony_ci errors, 26262306a36Sopenharmony_ci seed); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return errors ? 1 : 0; 26562306a36Sopenharmony_ci} 266