linux/arch/x86/tools/insn_sanity.c
<<
>>
Prefs
   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
  47static const char       *prog;          /* Program name */
  48static int              verbose;        /* Verbosity */
  49static int              x86_64;         /* x86-64 bit mode flag */
  50static unsigned int     seed;           /* Random seed */
  51static unsigned long    iter_start;     /* Start of iteration number */
  52static unsigned long    iter_end = DEFAULT_MAX_ITER;    /* End of iteration number */
  53static FILE             *input_file;    /* Input file name */
  54
  55static void usage(const char *err)
  56{
  57        if (err)
  58                fprintf(stderr, "%s: Error: %s\n\n", prog, 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
  69static 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
  80static 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
  98static 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
 122static 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;
 135fail:
 136        usage("Failed to open /dev/urandom");
 137}
 138
 139/* Read given instruction sequence from the input file */
 140static 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
 158static 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
 175static 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
 236int 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, sizeof(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((errors) ? stderr : stdout,
 273                "%s: %s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n",
 274                prog,
 275                (errors) ? "Failure" : "Success",
 276                insns,
 277                (input_file) ? "given" : "random",
 278                errors,
 279                seed);
 280
 281        return errors ? 1 : 0;
 282}
 283