fuzzpuzz.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /*
  2. * fuzzpuzz.c: Fuzzing frontend to all puzzles.
  3. */
  4. /*
  5. * The idea here is that this front-end supports all back-ends and can
  6. * feed them save files. It then asks the back-end to draw the puzzle
  7. * (through a null drawing API) and reserialises the state. This
  8. * tests the deserialiser, the code for loading game descriptions, the
  9. * processing of move strings, the redraw code, and the serialisation
  10. * routines, but is still pretty quick.
  11. *
  12. * To use AFL++ to drive fuzzpuzz, you can do something like:
  13. *
  14. * CC=afl-cc cmake -B build-afl
  15. * cmake --build build-afl --target fuzzpuzz
  16. * mkdir fuzz-in && ln icons/''*.sav fuzz-in
  17. * afl-fuzz -i fuzz-in -o fuzz-out -x fuzzpuzz.dict -- build-afl/fuzzpuzz
  18. *
  19. * Similarly with Honggfuzz:
  20. *
  21. * CC=hfuzz-cc cmake -B build-honggfuzz
  22. * cmake --build build-honggfuzz --target fuzzpuzz
  23. * mkdir fuzz-corpus && ln icons/''*.sav fuzz-corpus
  24. * honggfuzz -s -i fuzz-corpus -w fuzzpuzz.dict -- build-honggfuzz/fuzzpuzz
  25. *
  26. * You can also use libFuzzer, though it's not really a good fit for
  27. * Puzzles. The experimental forking mode seems to work OK:
  28. *
  29. * CC=clang cmake -B build-clang -DWITH_LIBFUZZER=Y
  30. * cmake --build build-clang --target fuzzpuzz
  31. * mkdir fuzz-corpus && ln icons/''*.sav fuzz-corpus
  32. * build-clang/fuzzpuzz -fork=1 -ignore_crashes=1 -dict=fuzzpuzz.dict \
  33. * fuzz-corpus
  34. */
  35. #include <stdbool.h>
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #ifdef __AFL_FUZZ_TESTCASE_LEN
  40. # include <unistd.h> /* read() is used by __AFL_FUZZ_TESTCASE_LEN. */
  41. #endif
  42. #include "puzzles.h"
  43. #ifdef __AFL_FUZZ_INIT
  44. __AFL_FUZZ_INIT();
  45. #endif
  46. #ifdef HAVE_HF_ITER
  47. extern int HF_ITER(unsigned char **, size_t *);
  48. #endif
  49. /* This function is expected by libFuzzer. */
  50. int LLVMFuzzerTestOneInput(unsigned char *data, size_t size);
  51. static const char *fuzz_one(bool (*readfn)(void *, void *, int), void *rctx,
  52. void (*rewindfn)(void *),
  53. void (*writefn)(void *, const void *, int),
  54. void *wctx)
  55. {
  56. const char *err;
  57. char *gamename;
  58. int i, w, h;
  59. const game *ourgame = NULL;
  60. static const drawing_api drapi = { NULL };
  61. midend *me;
  62. err = identify_game(&gamename, readfn, rctx);
  63. if (err != NULL) return err;
  64. for (i = 0; i < gamecount; i++)
  65. if (strcmp(gamename, gamelist[i]->name) == 0)
  66. ourgame = gamelist[i];
  67. sfree(gamename);
  68. if (ourgame == NULL)
  69. return "Game not recognised";
  70. me = midend_new(NULL, ourgame, &drapi, NULL);
  71. rewindfn(rctx);
  72. err = midend_deserialise(me, readfn, rctx);
  73. if (err != NULL) {
  74. midend_free(me);
  75. return err;
  76. }
  77. w = h = INT_MAX;
  78. midend_size(me, &w, &h, false, 1);
  79. midend_redraw(me);
  80. midend_serialise(me, writefn, wctx);
  81. midend_free(me);
  82. return NULL;
  83. }
  84. #if defined(__AFL_FUZZ_TESTCASE_LEN) || defined(HAVE_HF_ITER) || \
  85. !defined(OMIT_MAIN)
  86. static void savefile_write(void *wctx, const void *buf, int len)
  87. {
  88. FILE *fp = (FILE *)wctx;
  89. fwrite(buf, 1, len, fp);
  90. }
  91. #endif
  92. struct memread {
  93. const unsigned char *buf;
  94. size_t pos;
  95. size_t len;
  96. };
  97. static bool mem_read(void *wctx, void *buf, int len)
  98. {
  99. struct memread *ctx = wctx;
  100. if (ctx->pos + len > ctx->len) return false;
  101. memcpy(buf, ctx->buf + ctx->pos, len);
  102. ctx->pos += len;
  103. return true;
  104. }
  105. static void mem_rewind(void *wctx)
  106. {
  107. struct memread *ctx = wctx;
  108. ctx->pos = 0;
  109. }
  110. static void null_write(void *wctx, const void *buf, int len)
  111. {
  112. }
  113. int LLVMFuzzerTestOneInput(unsigned char *data, size_t size) {
  114. struct memread ctx;
  115. ctx.buf = data;
  116. ctx.len = size;
  117. ctx.pos = 0;
  118. fuzz_one(mem_read, &ctx, mem_rewind, null_write, NULL);
  119. return 0;
  120. }
  121. #if defined(__AFL_FUZZ_TESTCASE_LEN) || defined(HAVE_HF_ITER)
  122. static const char *fuzz_one_mem(unsigned char *data, size_t size) {
  123. struct memread ctx;
  124. ctx.buf = data;
  125. ctx.len = size;
  126. ctx.pos = 0;
  127. return fuzz_one(mem_read, &ctx, mem_rewind, savefile_write, stdout);
  128. }
  129. #endif
  130. /*
  131. * Three different versions of main(), for standalone, AFL, and
  132. * Honggfuzz modes. LibFuzzer brings its own main().
  133. */
  134. #ifdef OMIT_MAIN
  135. /* Nothing. */
  136. #elif defined(__AFL_FUZZ_TESTCASE_LEN)
  137. /*
  138. * AFL persistent mode, where we fuzz from a RAM buffer provided
  139. * by AFL in a loop. This version can still be run standalone if
  140. * necessary, for instance to diagnose a crash.
  141. */
  142. int main(int argc, char **argv)
  143. {
  144. const char *err;
  145. int ret;
  146. if (argc != 1) {
  147. fprintf(stderr, "usage: %s\n", argv[0]);
  148. return 1;
  149. }
  150. #ifdef __AFL_HAVE_MANUAL_CONTROL
  151. __AFL_INIT();
  152. #endif
  153. while (__AFL_LOOP(10000)) {
  154. err = fuzz_one_mem(__AFL_FUZZ_TESTCASE_BUF, __AFL_FUZZ_TESTCASE_LEN);
  155. if (err != NULL) {
  156. fprintf(stderr, "%s\n", err);
  157. ret = 1;
  158. } else
  159. ret = 0;
  160. }
  161. return ret;
  162. }
  163. #elif defined(HAVE_HF_ITER)
  164. /*
  165. * Honggfuzz persistent mode. Unlike AFL persistent mode, the
  166. * resulting executable cannot be run outside of Honggfuzz.
  167. */
  168. int main(int argc, char **argv)
  169. {
  170. if (argc != 1) {
  171. fprintf(stderr, "usage: %s\n", argv[0]);
  172. return 1;
  173. }
  174. while (true) {
  175. unsigned char *testcase_buf;
  176. size_t testcase_len;
  177. HF_ITER(&testcase_buf, &testcase_len);
  178. fuzz_one_mem(testcase_buf, testcase_len);
  179. }
  180. }
  181. #else
  182. /*
  183. * Stand-alone mode: just handle a single test case on stdin.
  184. */
  185. static bool savefile_read(void *wctx, void *buf, int len)
  186. {
  187. FILE *fp = (FILE *)wctx;
  188. int ret;
  189. ret = fread(buf, 1, len, fp);
  190. return (ret == len);
  191. }
  192. static void savefile_rewind(void *wctx)
  193. {
  194. FILE *fp = (FILE *)wctx;
  195. rewind(fp);
  196. }
  197. int main(int argc, char **argv)
  198. {
  199. const char *err;
  200. if (argc != 1) {
  201. fprintf(stderr, "usage: %s\n", argv[0]);
  202. return 1;
  203. }
  204. /* Might in theory use this mode under AFL. */
  205. #ifdef __AFL_HAVE_MANUAL_CONTROL
  206. __AFL_INIT();
  207. #endif
  208. err = fuzz_one(savefile_read, stdin, savefile_rewind,
  209. savefile_write, stdout);
  210. if (err != NULL) {
  211. fprintf(stderr, "%s\n", err);
  212. return 1;
  213. }
  214. return 0;
  215. }
  216. #endif