diff --git a/.gitignore b/.gitignore index 800ae845..e8a5296c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ setup.* .ignore _opam *.exe +fuzz-*-input +fuzz-*-output diff --git a/fuzz/ccsexp_parse_string_does_not_crash.ml b/fuzz/ccsexp_parse_string_does_not_crash.ml new file mode 100644 index 00000000..358968c2 --- /dev/null +++ b/fuzz/ccsexp_parse_string_does_not_crash.ml @@ -0,0 +1,3 @@ +let () = + Crowbar.add_test ~name:"ccsexp_parse_string_does_not_crash" [ Crowbar.bytes ] + (fun s -> CCSexp.parse_string s |> ignore) diff --git a/fuzz/ccutf8_string_uchar_to_bytes_is_same_as_simple_version.ml b/fuzz/ccutf8_string_uchar_to_bytes_is_same_as_simple_version.ml new file mode 100644 index 00000000..af930e6c --- /dev/null +++ b/fuzz/ccutf8_string_uchar_to_bytes_is_same_as_simple_version.ml @@ -0,0 +1,168 @@ +let simple_uchar_to_string (c : Uchar.t) : string = + let c = Uchar.to_int c in + let bits = + Array.make 64 false + |> Array.mapi (fun i _ -> + ((Int.shift_right c (63 - i)) land 0x1) <> 0 + ) + in + let char_of_bit_list bits = + let bits = Array.of_list bits in + assert (Array.length bits = 8); + let res = ref 0 in + for i=0 to 7 do + if bits.(i) then + res := !res lor (0x1 lsl (7-i)) + done; + Char.chr !res + in + let get_start_from_right i = + Array.get bits (63 - i) + in + let chars = + if c <= 0x7F then ( + [ + [ + false; + get_start_from_right 6; + get_start_from_right 5; + get_start_from_right 4; + get_start_from_right 3; + get_start_from_right 2; + get_start_from_right 1; + get_start_from_right 0; + ] + ] + ) + else if c <= 0x7FF then ( + [ + [ + true; + true; + false; + get_start_from_right 10; + get_start_from_right 9; + get_start_from_right 8; + get_start_from_right 7; + get_start_from_right 6; + ]; + [ + true; + false; + get_start_from_right 5; + get_start_from_right 4; + get_start_from_right 3; + get_start_from_right 2; + get_start_from_right 1; + get_start_from_right 0; + ]; + ] + ) + else if c <= 0xFFFF then ( + [ + [ + true; + true; + true; + false; + get_start_from_right 15; + get_start_from_right 14; + get_start_from_right 13; + get_start_from_right 12; + ]; + [ + true; + false; + get_start_from_right 11; + get_start_from_right 10; + get_start_from_right 9; + get_start_from_right 8; + get_start_from_right 7; + get_start_from_right 6; + ]; + [ + true; + false; + get_start_from_right 5; + get_start_from_right 4; + get_start_from_right 3; + get_start_from_right 2; + get_start_from_right 1; + get_start_from_right 0; + ]; + ] + ) + else if c <= 0x10FFFF then ( + [ + [ + true; + true; + true; + true; + false; + get_start_from_right 20; + get_start_from_right 19; + get_start_from_right 18; + ]; + [ + true; + false; + get_start_from_right 17; + get_start_from_right 16; + get_start_from_right 15; + get_start_from_right 14; + get_start_from_right 13; + get_start_from_right 12; + ]; + [ + true; + false; + get_start_from_right 11; + get_start_from_right 10; + get_start_from_right 9; + get_start_from_right 8; + get_start_from_right 7; + get_start_from_right 6; + ]; + [ + true; + false; + get_start_from_right 5; + get_start_from_right 4; + get_start_from_right 3; + get_start_from_right 2; + get_start_from_right 1; + get_start_from_right 0; + ]; + ] + ) + else ( + failwith "Unexpected case" + ) + in + chars + |> List.map char_of_bit_list + |> List.to_seq + |> String.of_seq + +let () = + Crowbar.add_test ~name:"ccutf8_string_uchar_to_bytes_is_same_as_simple_version" [ Crowbar.range (succ 0x10FFFF) ] + (fun c -> + Crowbar.guard (Uchar.is_valid c); + let c = Uchar.of_int c in + let simple_answer = + simple_uchar_to_string c + in + let answer = + let buf = ref [] in + CCUtf8_string.uchar_to_bytes c (fun c -> + buf := c :: !buf; + ); + !buf + |> List.rev + |> List.to_seq + |> String.of_seq + in + Crowbar.check_eq + simple_answer answer + ) diff --git a/fuzz/dune b/fuzz/dune new file mode 100644 index 00000000..cdc34acc --- /dev/null +++ b/fuzz/dune @@ -0,0 +1,9 @@ +(executables + (flags (-w "+a-4-9-29-37-40-42-44-48-50-32" -g)) + (names ccsexp_parse_string_does_not_crash + ccutf8_string_uchar_to_bytes_is_same_as_simple_version + ) + (libraries crowbar + containers + ) +) diff --git a/fuzz/list.sh b/fuzz/list.sh new file mode 100755 index 00000000..eafc9162 --- /dev/null +++ b/fuzz/list.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +script_dir=$(dirname $(readlink -f "$0")) + +echo "Building" + +dune build @all + +echo "" + +echo "Fuzzing tests available:" + +for file in "$script_dir"/../_build/default/fuzz/*.exe; do + echo "- "$(basename $file | sed 's/\.exe$//') +done diff --git a/fuzz/run.sh b/fuzz/run.sh new file mode 100755 index 00000000..8bdd3b88 --- /dev/null +++ b/fuzz/run.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +script_dir=$(dirname $(readlink -f "$0")) + +echo "Building" + +dune build @all + +if [[ "$1" == "" ]]; then + echo "Please enter a fuzzing test to run" + exit 1 +fi + +name=$(echo "$1" | sed 's/\.exe$//') + +echo "Creating input directory" + +input_dir="$script_dir"/../"fuzz-""$name""-input" + +output_dir="$script_dir"/../"fuzz-""$name""-output" + +mkdir -p "$input_dir" + +echo "abcd" > "$input_dir"/dummy + +mkdir -p "$output_dir" + +afl-fuzz -t 1000 -i "$input_dir" -o "$output_dir" "$script_dir"/../_build/default/fuzz/"$name".exe @@