Spaces:
Sleeping
Sleeping
| /* | |
| * fuzzpuzz.c: Fuzzing frontend to all puzzles. | |
| */ | |
| /* | |
| * The idea here is that this front-end supports all back-ends and can | |
| * feed them save files. It then asks the back-end to draw the puzzle | |
| * (through a null drawing API) and reserialises the state. This | |
| * tests the deserialiser, the code for loading game descriptions, the | |
| * processing of move strings, the redraw code, and the serialisation | |
| * routines, but is still pretty quick. | |
| * | |
| * To use AFL++ to drive fuzzpuzz, you can do something like: | |
| * | |
| * CC=afl-cc cmake -B build-afl | |
| * cmake --build build-afl --target fuzzpuzz | |
| * mkdir fuzz-in && ln icons/''*.sav fuzz-in | |
| * afl-fuzz -i fuzz-in -o fuzz-out -x fuzzpuzz.dict -- build-afl/fuzzpuzz | |
| * | |
| * Similarly with Honggfuzz: | |
| * | |
| * CC=hfuzz-cc cmake -B build-honggfuzz | |
| * cmake --build build-honggfuzz --target fuzzpuzz | |
| * mkdir fuzz-corpus && ln icons/''*.sav fuzz-corpus | |
| * honggfuzz -s -i fuzz-corpus -w fuzzpuzz.dict -- build-honggfuzz/fuzzpuzz | |
| * | |
| * You can also use libFuzzer, though it's not really a good fit for | |
| * Puzzles. The experimental forking mode seems to work OK: | |
| * | |
| * CC=clang cmake -B build-clang -DWITH_LIBFUZZER=Y | |
| * cmake --build build-clang --target fuzzpuzz | |
| * mkdir fuzz-corpus && ln icons/''*.sav fuzz-corpus | |
| * build-clang/fuzzpuzz -fork=1 -ignore_crashes=1 -dict=fuzzpuzz.dict \ | |
| * fuzz-corpus | |
| */ | |
| __AFL_FUZZ_INIT(); | |
| extern int HF_ITER(unsigned char **, size_t *); | |
| /* This function is expected by libFuzzer. */ | |
| int LLVMFuzzerTestOneInput(unsigned char *data, size_t size); | |
| static const char *fuzz_one(bool (*readfn)(void *, void *, int), void *rctx, | |
| void (*rewindfn)(void *), | |
| void (*writefn)(void *, const void *, int), | |
| void *wctx) | |
| { | |
| const char *err; | |
| char *gamename; | |
| int i, w, h; | |
| const game *ourgame = NULL; | |
| static const drawing_api drapi = { NULL }; | |
| midend *me; | |
| err = identify_game(&gamename, readfn, rctx); | |
| if (err != NULL) return err; | |
| for (i = 0; i < gamecount; i++) | |
| if (strcmp(gamename, gamelist[i]->name) == 0) | |
| ourgame = gamelist[i]; | |
| sfree(gamename); | |
| if (ourgame == NULL) | |
| return "Game not recognised"; | |
| me = midend_new(NULL, ourgame, &drapi, NULL); | |
| rewindfn(rctx); | |
| err = midend_deserialise(me, readfn, rctx); | |
| if (err != NULL) { | |
| midend_free(me); | |
| return err; | |
| } | |
| w = h = INT_MAX; | |
| midend_size(me, &w, &h, false, 1); | |
| midend_redraw(me); | |
| midend_serialise(me, writefn, wctx); | |
| midend_free(me); | |
| return NULL; | |
| } | |
| static void savefile_write(void *wctx, const void *buf, int len) | |
| { | |
| FILE *fp = (FILE *)wctx; | |
| fwrite(buf, 1, len, fp); | |
| } | |
| struct memread { | |
| const unsigned char *buf; | |
| size_t pos; | |
| size_t len; | |
| }; | |
| static bool mem_read(void *wctx, void *buf, int len) | |
| { | |
| struct memread *ctx = wctx; | |
| if (ctx->pos + len > ctx->len) return false; | |
| memcpy(buf, ctx->buf + ctx->pos, len); | |
| ctx->pos += len; | |
| return true; | |
| } | |
| static void mem_rewind(void *wctx) | |
| { | |
| struct memread *ctx = wctx; | |
| ctx->pos = 0; | |
| } | |
| static void null_write(void *wctx, const void *buf, int len) | |
| { | |
| } | |
| int LLVMFuzzerTestOneInput(unsigned char *data, size_t size) { | |
| struct memread ctx; | |
| ctx.buf = data; | |
| ctx.len = size; | |
| ctx.pos = 0; | |
| fuzz_one(mem_read, &ctx, mem_rewind, null_write, NULL); | |
| return 0; | |
| } | |
| static const char *fuzz_one_mem(unsigned char *data, size_t size) { | |
| struct memread ctx; | |
| ctx.buf = data; | |
| ctx.len = size; | |
| ctx.pos = 0; | |
| return fuzz_one(mem_read, &ctx, mem_rewind, savefile_write, stdout); | |
| } | |
| /* | |
| * Three different versions of main(), for standalone, AFL, and | |
| * Honggfuzz modes. LibFuzzer brings its own main(). | |
| */ | |
| /* Nothing. */ | |
| /* | |
| * AFL persistent mode, where we fuzz from a RAM buffer provided | |
| * by AFL in a loop. This version can still be run standalone if | |
| * necessary, for instance to diagnose a crash. | |
| */ | |
| int main(int argc, char **argv) | |
| { | |
| const char *err; | |
| int ret; | |
| if (argc != 1) { | |
| fprintf(stderr, "usage: %s\n", argv[0]); | |
| return 1; | |
| } | |
| __AFL_INIT(); | |
| while (__AFL_LOOP(10000)) { | |
| err = fuzz_one_mem(__AFL_FUZZ_TESTCASE_BUF, __AFL_FUZZ_TESTCASE_LEN); | |
| if (err != NULL) { | |
| fprintf(stderr, "%s\n", err); | |
| ret = 1; | |
| } else | |
| ret = 0; | |
| } | |
| return ret; | |
| } | |
| /* | |
| * Honggfuzz persistent mode. Unlike AFL persistent mode, the | |
| * resulting executable cannot be run outside of Honggfuzz. | |
| */ | |
| int main(int argc, char **argv) | |
| { | |
| if (argc != 1) { | |
| fprintf(stderr, "usage: %s\n", argv[0]); | |
| return 1; | |
| } | |
| while (true) { | |
| unsigned char *testcase_buf; | |
| size_t testcase_len; | |
| HF_ITER(&testcase_buf, &testcase_len); | |
| fuzz_one_mem(testcase_buf, testcase_len); | |
| } | |
| } | |
| /* | |
| * Stand-alone mode: just handle a single test case on stdin. | |
| */ | |
| static bool savefile_read(void *wctx, void *buf, int len) | |
| { | |
| FILE *fp = (FILE *)wctx; | |
| int ret; | |
| ret = fread(buf, 1, len, fp); | |
| return (ret == len); | |
| } | |
| static void savefile_rewind(void *wctx) | |
| { | |
| FILE *fp = (FILE *)wctx; | |
| rewind(fp); | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| const char *err; | |
| if (argc != 1) { | |
| fprintf(stderr, "usage: %s\n", argv[0]); | |
| return 1; | |
| } | |
| /* Might in theory use this mode under AFL. */ | |
| __AFL_INIT(); | |
| err = fuzz_one(savefile_read, stdin, savefile_rewind, | |
| savefile_write, stdout); | |
| if (err != NULL) { | |
| fprintf(stderr, "%s\n", err); | |
| return 1; | |
| } | |
| return 0; | |
| } | |