insn_sanity.c 7.3 KB

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