xref: /linux/arch/x86/tools/insn_sanity.c (revision 8e07e0e3964ca4e23ce7b68e2096fe660a888942)
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 
30 static const char	*prog;		/* Program name */
31 static int		verbose;	/* Verbosity */
32 static int		x86_64;		/* x86-64 bit mode flag */
33 static unsigned int	seed;		/* Random seed */
34 static unsigned long	iter_start;	/* Start of iteration number */
35 static unsigned long	iter_end = DEFAULT_MAX_ITER;	/* End of iteration number */
36 static FILE		*input_file;	/* Input file name */
37 
38 static 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 
52 static 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 
63 static 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 
81 static 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 
105 static 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;
118 fail:
119 	usage("Failed to open /dev/urandom");
120 }
121 
122 /* Read given instruction sequence from the input file */
123 static 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 
141 static 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 
158 static 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 
219 int 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