{"id":16717353,"url":"https://github.com/nathanreb/ocaml-afl-examples","last_synced_at":"2025-04-10T06:43:00.538Z","repository":{"id":82831286,"uuid":"184729230","full_name":"NathanReb/ocaml-afl-examples","owner":"NathanReb","description":"Small examples of how to use AFL to fuzz OCaml programs","archived":false,"fork":false,"pushed_at":"2019-08-30T12:12:06.000Z","size":109,"stargazers_count":15,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-24T07:49:09.068Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NathanReb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-03T09:20:55.000Z","updated_at":"2024-11-05T00:26:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"98b9bedc-0537-42dc-b9d7-a0937ce7b780","html_url":"https://github.com/NathanReb/ocaml-afl-examples","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NathanReb%2Focaml-afl-examples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NathanReb%2Focaml-afl-examples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NathanReb%2Focaml-afl-examples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NathanReb%2Focaml-afl-examples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NathanReb","download_url":"https://codeload.github.com/NathanReb/ocaml-afl-examples/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248173408,"owners_count":21059592,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-12T21:31:36.610Z","updated_at":"2025-04-10T06:43:00.491Z","avatar_url":"https://github.com/NathanReb.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OCaml AFL fuzzing examples\n\nSmall examples of how to use AFL to fuzz OCaml programs\n\nThese examples are here to help you quickly get set up with AFL and to illustrate an upcoming\narticle on the [Tarides blog](https://tarides.com/blog.html).\n\n## Setup\n\nTo be able to correctly run the examples in this repo and toy around with fuzzing you will need to\ninstall `afl` and have a `+afl` opam switch so that the binaries are properly instrumented for\nfuzzing.\n\nYou can setup the afl switch by running:\n```\n$ opam switch create fuzzing-switch 4.07.1+afl\n```\n\nYou can either install AFL from your distribution, e.g. on Debian:\n```\n$ apt update \u0026\u0026 apt install afl\n```\n\nOr by using the convenience opam package:\n```\n$ opam install --switch=fuzzing-switch afl\n```\n\nSome of the examples have extra dependencies such as `crowbar` and `bun`. You can install all of\nthem by running:\n\n```\n$ opam install --switch=fuzzing-switch crowbar bun\n```\n\n## Simple parser\n\nThe `simple-parser` folder contains the most basic example and shows how you can use afl-fuzz to\nfuzz a simple parsing function written in OCaml.\n\nThe `lib` subfolder contains a library with a single `parse_int` function that parses an int from a\nstring, with a little twist.\n\nThe `fuzz` subfolder contains the code to be compiled to the fuzzing binary `fuzz_me.exe` which\nmust be passed to afl and an `inputs/` folder with a couple starting test cases.\n\nYou can try fuzzing it by yourself:\n```\n$ dune build simple-parser/fuzz/fuzz_me.exe\n$ afl-fuzz -i simple-parser/fuzz/inputs -o _build/default/simple-parser/fuzz/findings _build/default/simple-parser/fuzz/fuzz_me.exe @@\n```\n\nOr simply run:\n```\n$ dune build @simple-parser/fuzz --no-buffer\n```\n\nwhich will do pretty much exactly the above.\n\nAFL should find the crash fairly quickly. It will show up in the top right corner of `afl-fuzz`'s\noutput, under `uniq crashes`, see the picture below.\n\n![afl-output-screenshot-emphasized-crashes](img/afl-output-screenshot-emphasized-crashes.png)\n\nYou can inspect the input that triggered the crash by running:\n```\n$ cat _build/default/simple-parser/fuzz/findings/crashes/id*\nabc\n```\n\nand reproduce it by running:\n```\n$ cd _build/default/simple-parser/fuzz\n$ ./fuzz_me.exe findings/crashes/id*\nFatal error: exception Failure(\"secret crash\")\n```\n\n## Awesome list\n\nThe `awesome-list` folder contains an example of how AFL can be used conjointly with the `crowbar`\nlibrary to both find crashes and do some property based testing.\n\nThe `lib` subfolder contains a library with a single `sort` function that sorts lists of integers.\nAgain it mostly works fine except in two specific cases where it will either crash or sort the list\nin reverse order.\n\nThe `fuzz` subfolder contains the code for the fuzzing binary. It is slightly different from the\nprevious example as here we use `crowbar` to build the correct binary instead of doing it by hand.\nFurthermore, we don't check for crashes only anymore but also want to know if the function under\ntest invariant, i.e. the resulting list is sorted in increasing order, stands. The resulting fuzzing\nbinary has roughly two modes of execution: an AFL one and a QuickCheck one.\n\nIn QuickCheck mode it uses OCaml's randonmess source to try a fixed number of inputs. To try that\nmode you can run:\n```\n$ dune exec awesome-list/fuzz/fuzz_me.exe\n```\n\nAlternatively you can also run the QuickCheck mode until a test failure is discovered with the `-i`\noption of the binary like this:\n```\n$ dune exec -- awesome-list/fuzz/fuzz_me.exe -i\n```\n\nIn AFL mode, it just use the input supplied by AFL as a source of randomness to supply values of the\nright form to your test functions. You can run it just like with a regular fuzzing binary:\n```\n$ dune build awesome-list/fuzz/fuzz_me.exe\n$ afl-fuzz -i awesome-list/fuzz/inputs -o _build/default/awesome-list/fuzz/findings ./_build/default/awesome-list/fuzz/fuzz_me.exe @@\n```\n\nOr use the convenience dune alias:\n```\n$ dune build @awesome-list/fuzz --no-buffer\n```\n\nBoth modes should find the bugs in a split second. In QuickCheck mode it'll pretty print the input\nvalue that triggered the failure. In AFL mode you can proceed as in the above example, i.e. kill the\n`afl-fuzz` process once it found the two unique crashes. From there, inspecting the input files\nwon't tell you much as it's just used to seed `crowbar` PRNG but you can run the fuzz binary on\nthose and the input values will be pretty printed the same way they are in QuickCheck mode:\n```\n$ cd _build/default/awesome-list/fuzz\n$ ./fuzz_me.exe findings/crashes/\u003cinput_file\u003e\nAwesome_list.sort: ....\nAwesome_list.sort: FAIL\n\nWhen given the input:\n\n    [4; 5; 6]\n\nthe test failed:\n\n    check false\n\nFatal error: exception Crowbar.TestFailure\n```\n\n## Bun and fuzz testing in CI\n\nThis repo also contain Drone CI scripts that run fuzz testing for both our examples.\n`afl-fuzz` is not very CI friendly by essence so we use\n[`bun`](https://github.com/yomimono/ocaml-bun). `bun` is a CLI wrapper for `afl-fuzz`, written in\nOCaml. It takes care of a few things for you, such as running several fuzzing processes in parallel\nwhile correctly setting `afl-fuzz` to do so but most of all it wraps each of those processes so that\nthe execution fail whenever one of them finds a crash. Finally, it will also display the input that\nlead to the test failure so that you can try to reproduce and debug it locally.\n\nThe CI script itself is in the `.drone.yml` file and it looks like that:\n```yml\nkind: pipeline\nname: amd\n\nplatform:\n  os: linux\n  arch: amd64\n\nsteps:\n- name: build\n  image: ocaml/opam2:4.07\n  commands:\n  - sudo apt-get update \u0026\u0026 sudo apt-get -y install afl\n  - sudo chown -R opam .\n  - git -C /home/opam/opam-repository pull origin \u0026\u0026 opam update\n  - opam switch 4.07+afl\n  - opam depext crowbar bun\n  - opam install -y crowbar bun\n  - opam exec -- dune build @bun-fuzz --no-buffer\n```\n\nAs you can see, there's nothing special here. We install the dependencies and build the `bun-fuzz`\nalias.\n\nThere's one for `simple-parser` and one  for `awesome-list`. The aliases are identical and defined\nas:\n```\n(alias\n (name bun-fuzz)\n (locks %{project_root}/bun)\n (deps\n  (:exe fuzz_me.exe)\n  (source_tree input))\n (action\n  (run bun --input inputs --output findings -- ./%{exe})))\n```\n\nThe default `bun` invocation is very similar to a regular `afl-fuzz` invocation.\nYou'll note the use of `(locks %{project_root}/bun)` to prevent concurrent execution of fuzz tests\nby dune. This is required because by default `bun` will use all available cores and `afl-fuzz` won't\nuse a core that's already running an `afl-fuzz` process.\n\nYou may try it locally:\n```\n$ dune build @awesome-list/bun-fuzz\n09:05.39:Fuzzers launched: [1 (pid=7357); 2 (pid=7358); 3 (pid=7359);\n                            4 (pid=7360); 5 (pid=7361); 6 (pid=7362);\n                            7 (pid=7363); 8 (pid=7364)].\n09:05.39:Fuzzer 1 (pid=7357) finished\nCrashes found! Take a look; copy/paste to save for reproduction:\necho J3JhaWl0IA== | base64 -d \u003e crash_0.$(date -u +%s)\n09:05.39:[ERROR]All fuzzers finished, but some crashes were found!\n```\n\nor you can take a look at the [latest\nbuild](https://cloud.drone.io/NathanReb/ocaml-afl-examples/16/1/2).\n\n`bun` comes with a bunch of configuration CLI options and I invite you to take a look at its\ndocumentation by running `bun --help` to find out how to make the most out of it in your particular\nuse case.\n\nOne useful bun feature is its `no-kill` mode. There's a `bun-fuzz-no-kill` alias available for\n`awesome-list` fuzz tests, it's defined as:\n```\n(alias\n (name bun-fuzz-no-kill)\n (locks %{project_root}/bun)\n (deps\n  (:exe fuzz_me.exe)\n  (source_tree input))\n (action\n  (run timeout --preserve-status 1m bun --no-kill --input inputs --output\n    findings -- ./%{exe})))\n```\n\nThe `--no-kill` option tells `bun` to let the fuzzing processes run even after they found their\nfirst crash. That can be convenient if you're not very confident about an implementation you're\nfuzzing and you are expecting to find several bugs in it.\nWhat makes this mode so nice is that it integrates with `timeout` fairly well. When receiving\n`SIGTERM`, `bun` will shutdown all the `afl-fuzz` processes and will pretty print the crash\ntriggering inputs to the standard output in the same way it does in regular mode which makes this\na viable option for running fuzz tests in CI as well.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnathanreb%2Focaml-afl-examples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnathanreb%2Focaml-afl-examples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnathanreb%2Focaml-afl-examples/lists"}