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