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