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