diff --git a/CMakeLists.txt b/CMakeLists.txt index 985d0434..2953b067 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ if(WITH_FLOATING_POINT AND NOT WITH_FREESTANDING) option(WITH_CBOR2JSON "Compile code to convert from CBOR to JSON" ON) option(BUILD_EXAMPLES "Compile the TinyCBOR examples" OFF) option(BUILD_TOOLS "Compile the TinyCBOR tools" ON) + option(BUILD_FUZZERS "Compile the TinyCBOR fuzzers (see fuzz/README)" OFF) endif() # Include additional modules that are used unconditionally @@ -217,3 +218,6 @@ endif() if(BUILD_TOOLS) add_subdirectory(tools) endif() +if(BUILD_FUZZERS) + add_subdirectory(fuzz) +endif() diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt new file mode 100644 index 00000000..163b76ec --- /dev/null +++ b/fuzz/CMakeLists.txt @@ -0,0 +1,52 @@ +# /**************************************************************************** +# ** +# ** Copyright (C) 2026 Topogamer0 +# ** +# ** Permission is hereby granted, free of charge, to any person obtaining a copy +# ** of this software and associated documentation files (the "Software"), to deal +# ** in the Software without restriction, including without limitation the rights +# ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# ** copies of the Software, and to permit persons to whom the Software is +# ** furnished to do so, subject to the following conditions: +# ** +# ** The above copyright notice and this permission notice shall be included in +# ** all copies or substantial portions of the Software. +# ** +# ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# ** THE SOFTWARE. +# ** +# ****************************************************************************/ + +include(TinyCBORHelpers) + +# The library and the fuzzers must be compiled with the instrumentation the +# fuzzing engine needs (for libFuzzer, -fsanitize=fuzzer-no-link in +# CMAKE_C_FLAGS) and any sanitizers. LIB_FUZZING_ENGINE is what provides +# main(), as in OSS-Fuzz (which sets it in the environment). +if(DEFINED ENV{LIB_FUZZING_ENGINE}) + set(LIB_FUZZING_ENGINE "$ENV{LIB_FUZZING_ENGINE}" CACHE STRING "Fuzzing engine to link the fuzzers with") +else() + set(LIB_FUZZING_ENGINE "-fsanitize=fuzzer" CACHE STRING "Fuzzing engine to link the fuzzers with") + if(NOT CMAKE_C_FLAGS MATCHES "-fsanitize=[^ ]*fuzzer") + message(WARNING "CMAKE_C_FLAGS has no -fsanitize=fuzzer-no-link: the fuzzers will not be instrumented") + endif() +endif() + +# The fuzzing engines are written in C++ +enable_language(CXX) + +set(fuzzers encoder parser pretty validate) +if(WITH_CBOR2JSON) + list(APPEND fuzzers tojson) +endif() + +foreach(fuzzer ${fuzzers}) + tinycbor_add_executable(${fuzzer}_fuzzer ${fuzzer}_fuzzer.c) + target_link_libraries(${fuzzer}_fuzzer ${LIB_FUZZING_ENGINE}) + set_target_properties(${fuzzer}_fuzzer PROPERTIES LINKER_LANGUAGE CXX) +endforeach() diff --git a/fuzz/README b/fuzz/README new file mode 100644 index 00000000..0d01994c --- /dev/null +++ b/fuzz/README @@ -0,0 +1,40 @@ +TinyCBOR fuzzers +---------------- + +These are libFuzzer-style fuzzers for TinyCBOR, also used by OSS-Fuzz. + + parser_fuzzer parses the input as a sequence of CBOR items, visiting + every item and extracting every value; optionally uses + the reader API (cbor_parser_init_reader) + validate_fuzzer cbor_value_validate() with any combination of flags + tojson_fuzzer cbor_value_to_json_advance() with any combination of flags + pretty_fuzzer cbor_value_to_pretty_advance_flags() and + cbor_value_to_pretty_stream() with any combination of flags + encoder_fuzzer interprets the input as a sequence of encoder commands, + then parses the result back and compares it + +For all fuzzers except encoder_fuzzer, the first 4 bytes of the input are a +configuration word (flags, mode) and the rest is the CBOR payload. The seed +corpus in corpus/ uses a zero configuration word followed by the CBOR data +from the unit tests; cbor.dict is a dictionary for those fuzzers. + +The assert()s in TinyCBOR check that the API is used correctly, so the +fuzzers must be built without NDEBUG: an assertion failure is a bug in the +fuzzer. The fuzzers also abort if the library contradicts its documented +behaviour (for example, if cbor_value_validate() succeeds but parsing the same +data fails). + +To build with Clang and libFuzzer: + + cmake -S. -Bbuild -DBUILD_FUZZERS=ON \ + -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_C_FLAGS="-g -O1 -fsanitize=fuzzer-no-link,address,undefined" \ + -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined" + cmake --build build + +LIB_FUZZING_ENGINE selects what to link the fuzzers with (default: +-fsanitize=fuzzer; if set in the environment, as in OSS-Fuzz, that is used). + +To run: + + build/fuzz/parser_fuzzer -dict=fuzz/cbor.dict new-corpus fuzz/corpus diff --git a/fuzz/cbor.dict b/fuzz/cbor.dict new file mode 100644 index 00000000..ec1dbf10 --- /dev/null +++ b/fuzz/cbor.dict @@ -0,0 +1,105 @@ +# /**************************************************************************** +# ** +# ** Copyright (C) 2026 Topogamer0 +# ** +# ** Permission is hereby granted, free of charge, to any person obtaining a copy +# ** of this software and associated documentation files (the "Software"), to deal +# ** in the Software without restriction, including without limitation the rights +# ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# ** copies of the Software, and to permit persons to whom the Software is +# ** furnished to do so, subject to the following conditions: +# ** +# ** The above copyright notice and this permission notice shall be included in +# ** all copies or substantial portions of the Software. +# ** +# ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# ** THE SOFTWARE. +# ** +# ****************************************************************************/ + +# Initial bytes of CBOR items (RFC 8949), for the parsing fuzzers + +# major type 0: unsigned integers +"\x00" +"\x17" +"\x18" +"\x19" +"\x1a" +"\x1b" +# major type 1: negative integers +"\x20" +"\x37" +"\x38" +"\x39" +"\x3a" +"\x3b" +# major type 2: byte strings +"\x40" +"\x57" +"\x58" +"\x59" +"\x5a" +"\x5b" +"\x5f" +# major type 3: text strings +"\x60" +"\x77" +"\x78" +"\x79" +"\x7a" +"\x7b" +"\x7f" +# major type 4: arrays +"\x80" +"\x97" +"\x98" +"\x99" +"\x9a" +"\x9b" +"\x9f" +# major type 5: maps +"\xa0" +"\xb7" +"\xb8" +"\xb9" +"\xba" +"\xbb" +"\xbf" +# major type 6: tags +"\xc0" +"\xc1" +"\xc2" +"\xc3" +"\xc4" +"\xc5" +"\xd5" +"\xd6" +"\xd7" +"\xd8" +"\xd9" +"\xda" +"\xdb" +"\xd8\x18" +"\xd8\x20" +"\xd9\xd9\xf7" +# major type 7: simple values and floating point +"\xe0" +"\xf4" +"\xf5" +"\xf6" +"\xf7" +"\xf8" +"\xf9" +"\xfa" +"\xfb" +"\xff" +"\xf9\x7c\x00" +"\xf9\xfc\x00" +"\xf9\x7e\x00" +"\xfa\x7f\xc0\x00\x00" +"\xfb\x7f\xf8\x00\x00\x00\x00\x00\x00" diff --git a/fuzz/corpus/014f895dd135e86b9c26361a455ce9bf15a09fda b/fuzz/corpus/014f895dd135e86b9c26361a455ce9bf15a09fda new file mode 100644 index 00000000..cb4970e7 Binary files /dev/null and b/fuzz/corpus/014f895dd135e86b9c26361a455ce9bf15a09fda differ diff --git a/fuzz/corpus/05e70084b7f8d73c500867cfbc579ea65ca3af32 b/fuzz/corpus/05e70084b7f8d73c500867cfbc579ea65ca3af32 new file mode 100644 index 00000000..d718891a Binary files /dev/null and b/fuzz/corpus/05e70084b7f8d73c500867cfbc579ea65ca3af32 differ diff --git a/fuzz/corpus/086c1bebeb1c5b2c20aefcb15ff8959c4402f65a b/fuzz/corpus/086c1bebeb1c5b2c20aefcb15ff8959c4402f65a new file mode 100644 index 00000000..3a0a7946 Binary files /dev/null and b/fuzz/corpus/086c1bebeb1c5b2c20aefcb15ff8959c4402f65a differ diff --git a/fuzz/corpus/09d360a8595c2bb49f808c2e0841586737fed1f7 b/fuzz/corpus/09d360a8595c2bb49f808c2e0841586737fed1f7 new file mode 100644 index 00000000..7507167d Binary files /dev/null and b/fuzz/corpus/09d360a8595c2bb49f808c2e0841586737fed1f7 differ diff --git a/fuzz/corpus/0a033e3d52c5a619fb6c65e790a88e07c68d704d b/fuzz/corpus/0a033e3d52c5a619fb6c65e790a88e07c68d704d new file mode 100644 index 00000000..d3477a40 Binary files /dev/null and b/fuzz/corpus/0a033e3d52c5a619fb6c65e790a88e07c68d704d differ diff --git a/fuzz/corpus/0b7297f312717b3024fc4062079985bcf192589d b/fuzz/corpus/0b7297f312717b3024fc4062079985bcf192589d new file mode 100644 index 00000000..e211c1be Binary files /dev/null and b/fuzz/corpus/0b7297f312717b3024fc4062079985bcf192589d differ diff --git a/fuzz/corpus/0c8c3f1dc468af54b996c44219cf4816a0037ca6 b/fuzz/corpus/0c8c3f1dc468af54b996c44219cf4816a0037ca6 new file mode 100644 index 00000000..a4c1a74f Binary files /dev/null and b/fuzz/corpus/0c8c3f1dc468af54b996c44219cf4816a0037ca6 differ diff --git a/fuzz/corpus/0df3f4d23dcecbb7926dee75666404b1e9b67c40 b/fuzz/corpus/0df3f4d23dcecbb7926dee75666404b1e9b67c40 new file mode 100644 index 00000000..b4ebb3ef Binary files /dev/null and b/fuzz/corpus/0df3f4d23dcecbb7926dee75666404b1e9b67c40 differ diff --git a/fuzz/corpus/10cd401ccee82e25d326c9ce0dd6f7419c837a80 b/fuzz/corpus/10cd401ccee82e25d326c9ce0dd6f7419c837a80 new file mode 100644 index 00000000..df0ccb74 Binary files /dev/null and b/fuzz/corpus/10cd401ccee82e25d326c9ce0dd6f7419c837a80 differ diff --git a/fuzz/corpus/13887ac5dc6c08d719daaa094c143f2955e553ef b/fuzz/corpus/13887ac5dc6c08d719daaa094c143f2955e553ef new file mode 100644 index 00000000..d5787e7e Binary files /dev/null and b/fuzz/corpus/13887ac5dc6c08d719daaa094c143f2955e553ef differ diff --git a/fuzz/corpus/1409c17079b2428205273f09eedc8df201e40be8 b/fuzz/corpus/1409c17079b2428205273f09eedc8df201e40be8 new file mode 100644 index 00000000..c884136e Binary files /dev/null and b/fuzz/corpus/1409c17079b2428205273f09eedc8df201e40be8 differ diff --git a/fuzz/corpus/15b9bcdcdcaaddd89bec8ed27e3910631837c564 b/fuzz/corpus/15b9bcdcdcaaddd89bec8ed27e3910631837c564 new file mode 100644 index 00000000..aa9ce7b9 Binary files /dev/null and b/fuzz/corpus/15b9bcdcdcaaddd89bec8ed27e3910631837c564 differ diff --git a/fuzz/corpus/15cfc192a73e1718c34cae0ec98ff9f66f4997d8 b/fuzz/corpus/15cfc192a73e1718c34cae0ec98ff9f66f4997d8 new file mode 100644 index 00000000..6a8afeeb Binary files /dev/null and b/fuzz/corpus/15cfc192a73e1718c34cae0ec98ff9f66f4997d8 differ diff --git a/fuzz/corpus/199e138c7b38a2936099f37839bd1422fc94bc16 b/fuzz/corpus/199e138c7b38a2936099f37839bd1422fc94bc16 new file mode 100644 index 00000000..7765040e Binary files /dev/null and b/fuzz/corpus/199e138c7b38a2936099f37839bd1422fc94bc16 differ diff --git a/fuzz/corpus/1adb291927afd05380e83f87133422350eab2557 b/fuzz/corpus/1adb291927afd05380e83f87133422350eab2557 new file mode 100644 index 00000000..bdedfb3c Binary files /dev/null and b/fuzz/corpus/1adb291927afd05380e83f87133422350eab2557 differ diff --git a/fuzz/corpus/1bc07015c76422ba9310425720dee495754458d6 b/fuzz/corpus/1bc07015c76422ba9310425720dee495754458d6 new file mode 100644 index 00000000..f21e9160 Binary files /dev/null and b/fuzz/corpus/1bc07015c76422ba9310425720dee495754458d6 differ diff --git a/fuzz/corpus/1bdde59cf453795380dc12a70053a8e5ea05e3d8 b/fuzz/corpus/1bdde59cf453795380dc12a70053a8e5ea05e3d8 new file mode 100644 index 00000000..302b4515 Binary files /dev/null and b/fuzz/corpus/1bdde59cf453795380dc12a70053a8e5ea05e3d8 differ diff --git a/fuzz/corpus/1c3a23b9e6943d5b9be200d1999df9917163eba2 b/fuzz/corpus/1c3a23b9e6943d5b9be200d1999df9917163eba2 new file mode 100644 index 00000000..8ccdaf73 Binary files /dev/null and b/fuzz/corpus/1c3a23b9e6943d5b9be200d1999df9917163eba2 differ diff --git a/fuzz/corpus/2225d2f8215c9e415ac6ce0d41855413fa460307 b/fuzz/corpus/2225d2f8215c9e415ac6ce0d41855413fa460307 new file mode 100644 index 00000000..9f88e65b Binary files /dev/null and b/fuzz/corpus/2225d2f8215c9e415ac6ce0d41855413fa460307 differ diff --git a/fuzz/corpus/23c8f61758d49990b5633c3da32e6ad02bbff30c b/fuzz/corpus/23c8f61758d49990b5633c3da32e6ad02bbff30c new file mode 100644 index 00000000..dff057f9 Binary files /dev/null and b/fuzz/corpus/23c8f61758d49990b5633c3da32e6ad02bbff30c differ diff --git a/fuzz/corpus/24ab22a223f470d3c112238c1ee7384614cdf8e0 b/fuzz/corpus/24ab22a223f470d3c112238c1ee7384614cdf8e0 new file mode 100644 index 00000000..1de53fc5 Binary files /dev/null and b/fuzz/corpus/24ab22a223f470d3c112238c1ee7384614cdf8e0 differ diff --git a/fuzz/corpus/25a928530b1982973183c9d1b64cb500d3b54c3c b/fuzz/corpus/25a928530b1982973183c9d1b64cb500d3b54c3c new file mode 100644 index 00000000..c21b4beb Binary files /dev/null and b/fuzz/corpus/25a928530b1982973183c9d1b64cb500d3b54c3c differ diff --git a/fuzz/corpus/2934ab98a050eb4187581b73d028d20a2d5beeca b/fuzz/corpus/2934ab98a050eb4187581b73d028d20a2d5beeca new file mode 100644 index 00000000..ef390f08 Binary files /dev/null and b/fuzz/corpus/2934ab98a050eb4187581b73d028d20a2d5beeca differ diff --git a/fuzz/corpus/2b12413703657e68436d0a6196461b9fc1333b77 b/fuzz/corpus/2b12413703657e68436d0a6196461b9fc1333b77 new file mode 100644 index 00000000..f46da714 Binary files /dev/null and b/fuzz/corpus/2b12413703657e68436d0a6196461b9fc1333b77 differ diff --git a/fuzz/corpus/2bb8fa769be54095af1baefcc4f40d8563d25630 b/fuzz/corpus/2bb8fa769be54095af1baefcc4f40d8563d25630 new file mode 100644 index 00000000..7088df54 Binary files /dev/null and b/fuzz/corpus/2bb8fa769be54095af1baefcc4f40d8563d25630 differ diff --git a/fuzz/corpus/31af79055f44c1226f87c02ea97bb442d20b5d7f b/fuzz/corpus/31af79055f44c1226f87c02ea97bb442d20b5d7f new file mode 100644 index 00000000..5efa7470 Binary files /dev/null and b/fuzz/corpus/31af79055f44c1226f87c02ea97bb442d20b5d7f differ diff --git a/fuzz/corpus/3371ce6be3d727bc05559e06c5e3014e43972297 b/fuzz/corpus/3371ce6be3d727bc05559e06c5e3014e43972297 new file mode 100644 index 00000000..85170e1c Binary files /dev/null and b/fuzz/corpus/3371ce6be3d727bc05559e06c5e3014e43972297 differ diff --git a/fuzz/corpus/3388e311ce4feda955c6d268047166485e8bc1ef b/fuzz/corpus/3388e311ce4feda955c6d268047166485e8bc1ef new file mode 100644 index 00000000..c0278ef6 Binary files /dev/null and b/fuzz/corpus/3388e311ce4feda955c6d268047166485e8bc1ef differ diff --git a/fuzz/corpus/338e72b519807fd9041fa921ac4dc42f354e064d b/fuzz/corpus/338e72b519807fd9041fa921ac4dc42f354e064d new file mode 100644 index 00000000..d5dc2b5f Binary files /dev/null and b/fuzz/corpus/338e72b519807fd9041fa921ac4dc42f354e064d differ diff --git a/fuzz/corpus/33cdae5f3d9e02f549f8d4e69bceb0850190c78d b/fuzz/corpus/33cdae5f3d9e02f549f8d4e69bceb0850190c78d new file mode 100644 index 00000000..9bd9f991 Binary files /dev/null and b/fuzz/corpus/33cdae5f3d9e02f549f8d4e69bceb0850190c78d differ diff --git a/fuzz/corpus/33db23a38ab67ce204fd304e95d8908fa72ae194 b/fuzz/corpus/33db23a38ab67ce204fd304e95d8908fa72ae194 new file mode 100644 index 00000000..382d40c4 Binary files /dev/null and b/fuzz/corpus/33db23a38ab67ce204fd304e95d8908fa72ae194 differ diff --git a/fuzz/corpus/34e11cb5310343ced27d0d06b3aa716c4927e701 b/fuzz/corpus/34e11cb5310343ced27d0d06b3aa716c4927e701 new file mode 100644 index 00000000..d1d9541b Binary files /dev/null and b/fuzz/corpus/34e11cb5310343ced27d0d06b3aa716c4927e701 differ diff --git a/fuzz/corpus/3526e9257647e436cdbefd57476a0ee7f8d04079 b/fuzz/corpus/3526e9257647e436cdbefd57476a0ee7f8d04079 new file mode 100644 index 00000000..918f3daf Binary files /dev/null and b/fuzz/corpus/3526e9257647e436cdbefd57476a0ee7f8d04079 differ diff --git a/fuzz/corpus/3539e2d8ddc8acc73d33771dd038ef8279db8a61 b/fuzz/corpus/3539e2d8ddc8acc73d33771dd038ef8279db8a61 new file mode 100644 index 00000000..37c59296 Binary files /dev/null and b/fuzz/corpus/3539e2d8ddc8acc73d33771dd038ef8279db8a61 differ diff --git a/fuzz/corpus/38a5f141a69fc4a7d743f558195c0e11cf01cb09 b/fuzz/corpus/38a5f141a69fc4a7d743f558195c0e11cf01cb09 new file mode 100644 index 00000000..edabd5cf Binary files /dev/null and b/fuzz/corpus/38a5f141a69fc4a7d743f558195c0e11cf01cb09 differ diff --git a/fuzz/corpus/3a9387b8cb1f1c49a0225c1d5b017df766622c4c b/fuzz/corpus/3a9387b8cb1f1c49a0225c1d5b017df766622c4c new file mode 100644 index 00000000..98a3fc17 Binary files /dev/null and b/fuzz/corpus/3a9387b8cb1f1c49a0225c1d5b017df766622c4c differ diff --git a/fuzz/corpus/3a9eccb024e670ba60dd68e6dbc6941d9856009d b/fuzz/corpus/3a9eccb024e670ba60dd68e6dbc6941d9856009d new file mode 100644 index 00000000..80d1b212 Binary files /dev/null and b/fuzz/corpus/3a9eccb024e670ba60dd68e6dbc6941d9856009d differ diff --git a/fuzz/corpus/3b5aa3f4c1a38f16fb96f03d86f73142490a3977 b/fuzz/corpus/3b5aa3f4c1a38f16fb96f03d86f73142490a3977 new file mode 100644 index 00000000..b0a5b747 Binary files /dev/null and b/fuzz/corpus/3b5aa3f4c1a38f16fb96f03d86f73142490a3977 differ diff --git a/fuzz/corpus/3ddf0e90744d3dd73cf395f85fa8864233e58704 b/fuzz/corpus/3ddf0e90744d3dd73cf395f85fa8864233e58704 new file mode 100644 index 00000000..907c10ca Binary files /dev/null and b/fuzz/corpus/3ddf0e90744d3dd73cf395f85fa8864233e58704 differ diff --git a/fuzz/corpus/3e2b2f0ab1581461dfc9e0b2b6d33244df8b9507 b/fuzz/corpus/3e2b2f0ab1581461dfc9e0b2b6d33244df8b9507 new file mode 100644 index 00000000..48e5df09 Binary files /dev/null and b/fuzz/corpus/3e2b2f0ab1581461dfc9e0b2b6d33244df8b9507 differ diff --git a/fuzz/corpus/3e5432a0f6ec0b3b6577462b5574f8bbdf964020 b/fuzz/corpus/3e5432a0f6ec0b3b6577462b5574f8bbdf964020 new file mode 100644 index 00000000..dacad05c Binary files /dev/null and b/fuzz/corpus/3e5432a0f6ec0b3b6577462b5574f8bbdf964020 differ diff --git a/fuzz/corpus/3fcbfab37368a03f327a90b9314800eb392f9aa9 b/fuzz/corpus/3fcbfab37368a03f327a90b9314800eb392f9aa9 new file mode 100644 index 00000000..9a60ff65 Binary files /dev/null and b/fuzz/corpus/3fcbfab37368a03f327a90b9314800eb392f9aa9 differ diff --git a/fuzz/corpus/419141f38e7bd8efcf3bc5c55742692a10c050f1 b/fuzz/corpus/419141f38e7bd8efcf3bc5c55742692a10c050f1 new file mode 100644 index 00000000..0e5beb6b Binary files /dev/null and b/fuzz/corpus/419141f38e7bd8efcf3bc5c55742692a10c050f1 differ diff --git a/fuzz/corpus/41b04a983188a907a110c63971b21151c9843843 b/fuzz/corpus/41b04a983188a907a110c63971b21151c9843843 new file mode 100644 index 00000000..2d81f98c Binary files /dev/null and b/fuzz/corpus/41b04a983188a907a110c63971b21151c9843843 differ diff --git a/fuzz/corpus/469deca0443da8487a9119ab1900f269ab97913b b/fuzz/corpus/469deca0443da8487a9119ab1900f269ab97913b new file mode 100644 index 00000000..b4866f47 Binary files /dev/null and b/fuzz/corpus/469deca0443da8487a9119ab1900f269ab97913b differ diff --git a/fuzz/corpus/47d6dc79f407358af11ff1271d0c4a2b4f366e2f b/fuzz/corpus/47d6dc79f407358af11ff1271d0c4a2b4f366e2f new file mode 100644 index 00000000..7d6f75f9 Binary files /dev/null and b/fuzz/corpus/47d6dc79f407358af11ff1271d0c4a2b4f366e2f differ diff --git a/fuzz/corpus/4935117d68863051ef3eee686e3fa335bc85a33b b/fuzz/corpus/4935117d68863051ef3eee686e3fa335bc85a33b new file mode 100644 index 00000000..bfd35207 Binary files /dev/null and b/fuzz/corpus/4935117d68863051ef3eee686e3fa335bc85a33b differ diff --git a/fuzz/corpus/4c15423f492dcade8bc1f6a4d4df64f20e34bb67 b/fuzz/corpus/4c15423f492dcade8bc1f6a4d4df64f20e34bb67 new file mode 100644 index 00000000..17e3cc86 Binary files /dev/null and b/fuzz/corpus/4c15423f492dcade8bc1f6a4d4df64f20e34bb67 differ diff --git a/fuzz/corpus/4cc6c9971bf85188da00d466f88f8407b6761293 b/fuzz/corpus/4cc6c9971bf85188da00d466f88f8407b6761293 new file mode 100644 index 00000000..7e60a2ab Binary files /dev/null and b/fuzz/corpus/4cc6c9971bf85188da00d466f88f8407b6761293 differ diff --git a/fuzz/corpus/4f4235cc7ef3ba2972e976f9170a4089f4be43b4 b/fuzz/corpus/4f4235cc7ef3ba2972e976f9170a4089f4be43b4 new file mode 100644 index 00000000..76010dac Binary files /dev/null and b/fuzz/corpus/4f4235cc7ef3ba2972e976f9170a4089f4be43b4 differ diff --git a/fuzz/corpus/5206b211b425fa5601783562fc254972a5527757 b/fuzz/corpus/5206b211b425fa5601783562fc254972a5527757 new file mode 100644 index 00000000..ef9d396c Binary files /dev/null and b/fuzz/corpus/5206b211b425fa5601783562fc254972a5527757 differ diff --git a/fuzz/corpus/52a670d56c5a6d7af94c402ca654988587e576d5 b/fuzz/corpus/52a670d56c5a6d7af94c402ca654988587e576d5 new file mode 100644 index 00000000..e025cc01 Binary files /dev/null and b/fuzz/corpus/52a670d56c5a6d7af94c402ca654988587e576d5 differ diff --git a/fuzz/corpus/531250e07aa93ce051b3bd354107f4fe8cdb435a b/fuzz/corpus/531250e07aa93ce051b3bd354107f4fe8cdb435a new file mode 100644 index 00000000..f688168e Binary files /dev/null and b/fuzz/corpus/531250e07aa93ce051b3bd354107f4fe8cdb435a differ diff --git a/fuzz/corpus/5371a4f527a693ac664c0fd647a5eabc036c5d17 b/fuzz/corpus/5371a4f527a693ac664c0fd647a5eabc036c5d17 new file mode 100644 index 00000000..7d825e10 Binary files /dev/null and b/fuzz/corpus/5371a4f527a693ac664c0fd647a5eabc036c5d17 differ diff --git a/fuzz/corpus/5521945da7469e0136707f2d1445a170ef6c3f84 b/fuzz/corpus/5521945da7469e0136707f2d1445a170ef6c3f84 new file mode 100644 index 00000000..25b625b9 Binary files /dev/null and b/fuzz/corpus/5521945da7469e0136707f2d1445a170ef6c3f84 differ diff --git a/fuzz/corpus/556e1fa17f8b24bb601377ac747ea4479274d59a b/fuzz/corpus/556e1fa17f8b24bb601377ac747ea4479274d59a new file mode 100644 index 00000000..2fa1b11a Binary files /dev/null and b/fuzz/corpus/556e1fa17f8b24bb601377ac747ea4479274d59a differ diff --git a/fuzz/corpus/56e836645bd178d8f7586ba5277ec58d1359e16a b/fuzz/corpus/56e836645bd178d8f7586ba5277ec58d1359e16a new file mode 100644 index 00000000..5157c78f Binary files /dev/null and b/fuzz/corpus/56e836645bd178d8f7586ba5277ec58d1359e16a differ diff --git a/fuzz/corpus/56f78df0ffeb23e148a0b12acb3ed2b43feed6d9 b/fuzz/corpus/56f78df0ffeb23e148a0b12acb3ed2b43feed6d9 new file mode 100644 index 00000000..b3a931b4 Binary files /dev/null and b/fuzz/corpus/56f78df0ffeb23e148a0b12acb3ed2b43feed6d9 differ diff --git a/fuzz/corpus/57996c03c2e504cb16d7dbcb30f31d3ab7356913 b/fuzz/corpus/57996c03c2e504cb16d7dbcb30f31d3ab7356913 new file mode 100644 index 00000000..cca8c32c Binary files /dev/null and b/fuzz/corpus/57996c03c2e504cb16d7dbcb30f31d3ab7356913 differ diff --git a/fuzz/corpus/5b184fd25a784954bbe08ddae81bb85193c0edcf b/fuzz/corpus/5b184fd25a784954bbe08ddae81bb85193c0edcf new file mode 100644 index 00000000..75a8ee09 Binary files /dev/null and b/fuzz/corpus/5b184fd25a784954bbe08ddae81bb85193c0edcf differ diff --git a/fuzz/corpus/5c69c00bb03a69f66a68cefe51a7ab9c5729266b b/fuzz/corpus/5c69c00bb03a69f66a68cefe51a7ab9c5729266b new file mode 100644 index 00000000..44af236f Binary files /dev/null and b/fuzz/corpus/5c69c00bb03a69f66a68cefe51a7ab9c5729266b differ diff --git a/fuzz/corpus/5f9ac4584d79796138ed1b02eb51da20b269d56b b/fuzz/corpus/5f9ac4584d79796138ed1b02eb51da20b269d56b new file mode 100644 index 00000000..0b070b03 Binary files /dev/null and b/fuzz/corpus/5f9ac4584d79796138ed1b02eb51da20b269d56b differ diff --git a/fuzz/corpus/62d74378c462475adc37477b8512f53cb2469943 b/fuzz/corpus/62d74378c462475adc37477b8512f53cb2469943 new file mode 100644 index 00000000..fb9658ed Binary files /dev/null and b/fuzz/corpus/62d74378c462475adc37477b8512f53cb2469943 differ diff --git a/fuzz/corpus/63825b8d35cb53f9aa0f8adea417e79e39e9e2a0 b/fuzz/corpus/63825b8d35cb53f9aa0f8adea417e79e39e9e2a0 new file mode 100644 index 00000000..e7b648bf Binary files /dev/null and b/fuzz/corpus/63825b8d35cb53f9aa0f8adea417e79e39e9e2a0 differ diff --git a/fuzz/corpus/64f13a6846284c580a9038e50dd501e0b9a58408 b/fuzz/corpus/64f13a6846284c580a9038e50dd501e0b9a58408 new file mode 100644 index 00000000..febf9f86 Binary files /dev/null and b/fuzz/corpus/64f13a6846284c580a9038e50dd501e0b9a58408 differ diff --git a/fuzz/corpus/65da5e58845e3d63e86765eb54108abb64f8375a b/fuzz/corpus/65da5e58845e3d63e86765eb54108abb64f8375a new file mode 100644 index 00000000..b7abb76e Binary files /dev/null and b/fuzz/corpus/65da5e58845e3d63e86765eb54108abb64f8375a differ diff --git a/fuzz/corpus/693e002a34afd6bb8dab5772f3bc3d1d3cec210d b/fuzz/corpus/693e002a34afd6bb8dab5772f3bc3d1d3cec210d new file mode 100644 index 00000000..3bce4997 Binary files /dev/null and b/fuzz/corpus/693e002a34afd6bb8dab5772f3bc3d1d3cec210d differ diff --git a/fuzz/corpus/6b86233c7df6e2f956777e0483d5854d8a460c39 b/fuzz/corpus/6b86233c7df6e2f956777e0483d5854d8a460c39 new file mode 100644 index 00000000..7ce4efbe Binary files /dev/null and b/fuzz/corpus/6b86233c7df6e2f956777e0483d5854d8a460c39 differ diff --git a/fuzz/corpus/6c3f6c108f8cf3e1ddbffb4159038e39b0d0b057 b/fuzz/corpus/6c3f6c108f8cf3e1ddbffb4159038e39b0d0b057 new file mode 100644 index 00000000..7905677f Binary files /dev/null and b/fuzz/corpus/6c3f6c108f8cf3e1ddbffb4159038e39b0d0b057 differ diff --git a/fuzz/corpus/6ed55b9c0d90d80924c48bf1b11b8103f741926e b/fuzz/corpus/6ed55b9c0d90d80924c48bf1b11b8103f741926e new file mode 100644 index 00000000..bccb830c Binary files /dev/null and b/fuzz/corpus/6ed55b9c0d90d80924c48bf1b11b8103f741926e differ diff --git a/fuzz/corpus/6f88ebe387c78c8b302633de4cc1fdffe5f660ca b/fuzz/corpus/6f88ebe387c78c8b302633de4cc1fdffe5f660ca new file mode 100644 index 00000000..c6c9ad99 Binary files /dev/null and b/fuzz/corpus/6f88ebe387c78c8b302633de4cc1fdffe5f660ca differ diff --git a/fuzz/corpus/7161b6ffe9a2efc1c8598dbd496cfcf7034d0043 b/fuzz/corpus/7161b6ffe9a2efc1c8598dbd496cfcf7034d0043 new file mode 100644 index 00000000..86936f11 Binary files /dev/null and b/fuzz/corpus/7161b6ffe9a2efc1c8598dbd496cfcf7034d0043 differ diff --git a/fuzz/corpus/722adabc5640a783b2f8f2500f28764b4ac6aaf3 b/fuzz/corpus/722adabc5640a783b2f8f2500f28764b4ac6aaf3 new file mode 100644 index 00000000..f70a7efb Binary files /dev/null and b/fuzz/corpus/722adabc5640a783b2f8f2500f28764b4ac6aaf3 differ diff --git a/fuzz/corpus/72549fa4dfc341f9b5aa0369021210d727a25b7d b/fuzz/corpus/72549fa4dfc341f9b5aa0369021210d727a25b7d new file mode 100644 index 00000000..b4cde399 Binary files /dev/null and b/fuzz/corpus/72549fa4dfc341f9b5aa0369021210d727a25b7d differ diff --git a/fuzz/corpus/73ebe48acb4c94ee354584cc54cce01e62868bf7 b/fuzz/corpus/73ebe48acb4c94ee354584cc54cce01e62868bf7 new file mode 100644 index 00000000..a88f87ee Binary files /dev/null and b/fuzz/corpus/73ebe48acb4c94ee354584cc54cce01e62868bf7 differ diff --git a/fuzz/corpus/744a71d6a17275896f51f084fd56279502086687 b/fuzz/corpus/744a71d6a17275896f51f084fd56279502086687 new file mode 100644 index 00000000..79f8f719 Binary files /dev/null and b/fuzz/corpus/744a71d6a17275896f51f084fd56279502086687 differ diff --git a/fuzz/corpus/749798e4ffcb312af64c726b60031643c1a7ae4f b/fuzz/corpus/749798e4ffcb312af64c726b60031643c1a7ae4f new file mode 100644 index 00000000..16dd5562 Binary files /dev/null and b/fuzz/corpus/749798e4ffcb312af64c726b60031643c1a7ae4f differ diff --git a/fuzz/corpus/75ec53f355a6c3a90cf2dcc24ddbb2446081b9cb b/fuzz/corpus/75ec53f355a6c3a90cf2dcc24ddbb2446081b9cb new file mode 100644 index 00000000..af4d149f Binary files /dev/null and b/fuzz/corpus/75ec53f355a6c3a90cf2dcc24ddbb2446081b9cb differ diff --git a/fuzz/corpus/75ed39927daeab8292dc6f6c695e054eefa7f634 b/fuzz/corpus/75ed39927daeab8292dc6f6c695e054eefa7f634 new file mode 100644 index 00000000..dde93897 Binary files /dev/null and b/fuzz/corpus/75ed39927daeab8292dc6f6c695e054eefa7f634 differ diff --git a/fuzz/corpus/784da0594a769a4cb3e52cb60b9a2cf669b10116 b/fuzz/corpus/784da0594a769a4cb3e52cb60b9a2cf669b10116 new file mode 100644 index 00000000..dc02cd92 Binary files /dev/null and b/fuzz/corpus/784da0594a769a4cb3e52cb60b9a2cf669b10116 differ diff --git a/fuzz/corpus/785ece10aa6b6dfda662f87abef617f3398ac9a6 b/fuzz/corpus/785ece10aa6b6dfda662f87abef617f3398ac9a6 new file mode 100644 index 00000000..a67eeaf6 Binary files /dev/null and b/fuzz/corpus/785ece10aa6b6dfda662f87abef617f3398ac9a6 differ diff --git a/fuzz/corpus/7a327da3074102e0539f06bcad19c1e8ce28844a b/fuzz/corpus/7a327da3074102e0539f06bcad19c1e8ce28844a new file mode 100644 index 00000000..202bfbc4 Binary files /dev/null and b/fuzz/corpus/7a327da3074102e0539f06bcad19c1e8ce28844a differ diff --git a/fuzz/corpus/7ae651f8139ddaaa91220f141f96b2ab0f4f1b1d b/fuzz/corpus/7ae651f8139ddaaa91220f141f96b2ab0f4f1b1d new file mode 100644 index 00000000..f5cbb924 Binary files /dev/null and b/fuzz/corpus/7ae651f8139ddaaa91220f141f96b2ab0f4f1b1d differ diff --git a/fuzz/corpus/7b2ec7e552b0a19d42c7c0c4b7d3e9f0687eba9e b/fuzz/corpus/7b2ec7e552b0a19d42c7c0c4b7d3e9f0687eba9e new file mode 100644 index 00000000..7cf996b6 Binary files /dev/null and b/fuzz/corpus/7b2ec7e552b0a19d42c7c0c4b7d3e9f0687eba9e differ diff --git a/fuzz/corpus/7ba72368458f84a104187d7a7ae98ef9451aa754 b/fuzz/corpus/7ba72368458f84a104187d7a7ae98ef9451aa754 new file mode 100644 index 00000000..3207ad15 Binary files /dev/null and b/fuzz/corpus/7ba72368458f84a104187d7a7ae98ef9451aa754 differ diff --git a/fuzz/corpus/7cec76a63eebb15a620b9f1bb58bcff223629c0d b/fuzz/corpus/7cec76a63eebb15a620b9f1bb58bcff223629c0d new file mode 100644 index 00000000..13dd4e02 Binary files /dev/null and b/fuzz/corpus/7cec76a63eebb15a620b9f1bb58bcff223629c0d differ diff --git a/fuzz/corpus/7f3dc1b701ab60200ac463fc4b57aa4fc498522e b/fuzz/corpus/7f3dc1b701ab60200ac463fc4b57aa4fc498522e new file mode 100644 index 00000000..fa6d3535 Binary files /dev/null and b/fuzz/corpus/7f3dc1b701ab60200ac463fc4b57aa4fc498522e differ diff --git a/fuzz/corpus/7f895ef6eed5ff4669394fd3b7b864dbde83b36f b/fuzz/corpus/7f895ef6eed5ff4669394fd3b7b864dbde83b36f new file mode 100644 index 00000000..3f921d4a Binary files /dev/null and b/fuzz/corpus/7f895ef6eed5ff4669394fd3b7b864dbde83b36f differ diff --git a/fuzz/corpus/83623f2b6453da9f0fe497745853c8df03a5b942 b/fuzz/corpus/83623f2b6453da9f0fe497745853c8df03a5b942 new file mode 100644 index 00000000..98715f33 Binary files /dev/null and b/fuzz/corpus/83623f2b6453da9f0fe497745853c8df03a5b942 differ diff --git a/fuzz/corpus/839c5d2fe334f6bee779a01a6cad244e0bc6ee3a b/fuzz/corpus/839c5d2fe334f6bee779a01a6cad244e0bc6ee3a new file mode 100644 index 00000000..24178362 Binary files /dev/null and b/fuzz/corpus/839c5d2fe334f6bee779a01a6cad244e0bc6ee3a differ diff --git a/fuzz/corpus/85c18dd8dc33ea9703ab3b67bd6d976188932ad1 b/fuzz/corpus/85c18dd8dc33ea9703ab3b67bd6d976188932ad1 new file mode 100644 index 00000000..c8b52b5e Binary files /dev/null and b/fuzz/corpus/85c18dd8dc33ea9703ab3b67bd6d976188932ad1 differ diff --git a/fuzz/corpus/87b5e15e41d61dcce8befa6be032bf32bc862599 b/fuzz/corpus/87b5e15e41d61dcce8befa6be032bf32bc862599 new file mode 100644 index 00000000..746e188f Binary files /dev/null and b/fuzz/corpus/87b5e15e41d61dcce8befa6be032bf32bc862599 differ diff --git a/fuzz/corpus/87e7ac9dbb6aace3fca5e87df7347b4a9b606530 b/fuzz/corpus/87e7ac9dbb6aace3fca5e87df7347b4a9b606530 new file mode 100644 index 00000000..7ea0817b Binary files /dev/null and b/fuzz/corpus/87e7ac9dbb6aace3fca5e87df7347b4a9b606530 differ diff --git a/fuzz/corpus/88f93b9e3bc228dad823fe1fcb9d67776f63d045 b/fuzz/corpus/88f93b9e3bc228dad823fe1fcb9d67776f63d045 new file mode 100644 index 00000000..2793aa7b Binary files /dev/null and b/fuzz/corpus/88f93b9e3bc228dad823fe1fcb9d67776f63d045 differ diff --git a/fuzz/corpus/8a2de019e26cd22fe9f797b48c6edbac3b509f1c b/fuzz/corpus/8a2de019e26cd22fe9f797b48c6edbac3b509f1c new file mode 100644 index 00000000..eee3ac51 Binary files /dev/null and b/fuzz/corpus/8a2de019e26cd22fe9f797b48c6edbac3b509f1c differ diff --git a/fuzz/corpus/8df289206a301da6f097e7108044b817834448fd b/fuzz/corpus/8df289206a301da6f097e7108044b817834448fd new file mode 100644 index 00000000..13573e04 Binary files /dev/null and b/fuzz/corpus/8df289206a301da6f097e7108044b817834448fd differ diff --git a/fuzz/corpus/8f1eda3de0bf21c6e4ec77af5fdc3a68abaaf6b5 b/fuzz/corpus/8f1eda3de0bf21c6e4ec77af5fdc3a68abaaf6b5 new file mode 100644 index 00000000..ac25cafe Binary files /dev/null and b/fuzz/corpus/8f1eda3de0bf21c6e4ec77af5fdc3a68abaaf6b5 differ diff --git a/fuzz/corpus/8faa8c23d29ae9628519eb50d49106580bb49a9e b/fuzz/corpus/8faa8c23d29ae9628519eb50d49106580bb49a9e new file mode 100644 index 00000000..303a0f8b Binary files /dev/null and b/fuzz/corpus/8faa8c23d29ae9628519eb50d49106580bb49a9e differ diff --git a/fuzz/corpus/913fddf15ed755486038346a61da94380885f871 b/fuzz/corpus/913fddf15ed755486038346a61da94380885f871 new file mode 100644 index 00000000..9b521479 Binary files /dev/null and b/fuzz/corpus/913fddf15ed755486038346a61da94380885f871 differ diff --git a/fuzz/corpus/929510601da41d3b093e08eb172c360b078beb37 b/fuzz/corpus/929510601da41d3b093e08eb172c360b078beb37 new file mode 100644 index 00000000..f5e25f52 Binary files /dev/null and b/fuzz/corpus/929510601da41d3b093e08eb172c360b078beb37 differ diff --git a/fuzz/corpus/95ca61e0b847c5ca2014598932a87d5f4fd224c3 b/fuzz/corpus/95ca61e0b847c5ca2014598932a87d5f4fd224c3 new file mode 100644 index 00000000..69471107 Binary files /dev/null and b/fuzz/corpus/95ca61e0b847c5ca2014598932a87d5f4fd224c3 differ diff --git a/fuzz/corpus/95dc062c15738f5547ee1bd184b2bdc9e6863179 b/fuzz/corpus/95dc062c15738f5547ee1bd184b2bdc9e6863179 new file mode 100644 index 00000000..f0d1e96d Binary files /dev/null and b/fuzz/corpus/95dc062c15738f5547ee1bd184b2bdc9e6863179 differ diff --git a/fuzz/corpus/95ece661a83f44d6ad347fe956156935de828310 b/fuzz/corpus/95ece661a83f44d6ad347fe956156935de828310 new file mode 100644 index 00000000..991ec276 Binary files /dev/null and b/fuzz/corpus/95ece661a83f44d6ad347fe956156935de828310 differ diff --git a/fuzz/corpus/962f0f994e9f2f45028be48317a9b099b479c88b b/fuzz/corpus/962f0f994e9f2f45028be48317a9b099b479c88b new file mode 100644 index 00000000..70c3f0b5 Binary files /dev/null and b/fuzz/corpus/962f0f994e9f2f45028be48317a9b099b479c88b differ diff --git a/fuzz/corpus/9690f81d995c899149953cfda66c494bfbf9f932 b/fuzz/corpus/9690f81d995c899149953cfda66c494bfbf9f932 new file mode 100644 index 00000000..90149c84 Binary files /dev/null and b/fuzz/corpus/9690f81d995c899149953cfda66c494bfbf9f932 differ diff --git a/fuzz/corpus/988ac4cd44d339f917d20329f0b83cb911098134 b/fuzz/corpus/988ac4cd44d339f917d20329f0b83cb911098134 new file mode 100644 index 00000000..78220696 Binary files /dev/null and b/fuzz/corpus/988ac4cd44d339f917d20329f0b83cb911098134 differ diff --git a/fuzz/corpus/9a08160f112708ae57fe800e1a513936b5ff6fed b/fuzz/corpus/9a08160f112708ae57fe800e1a513936b5ff6fed new file mode 100644 index 00000000..d38ce4e2 Binary files /dev/null and b/fuzz/corpus/9a08160f112708ae57fe800e1a513936b5ff6fed differ diff --git a/fuzz/corpus/9b2446528d0cb9316575d3329fca50383a568681 b/fuzz/corpus/9b2446528d0cb9316575d3329fca50383a568681 new file mode 100644 index 00000000..15c0bb60 Binary files /dev/null and b/fuzz/corpus/9b2446528d0cb9316575d3329fca50383a568681 differ diff --git a/fuzz/corpus/9b91e3ff93d9576a92fe1611f083f754c2e0c12c b/fuzz/corpus/9b91e3ff93d9576a92fe1611f083f754c2e0c12c new file mode 100644 index 00000000..ee268565 Binary files /dev/null and b/fuzz/corpus/9b91e3ff93d9576a92fe1611f083f754c2e0c12c differ diff --git a/fuzz/corpus/9c3b4a7a3190481d1962e8aaa7ee080ed756e371 b/fuzz/corpus/9c3b4a7a3190481d1962e8aaa7ee080ed756e371 new file mode 100644 index 00000000..7d46072b Binary files /dev/null and b/fuzz/corpus/9c3b4a7a3190481d1962e8aaa7ee080ed756e371 differ diff --git a/fuzz/corpus/9e696677edd5d0e273e23553b4ee5362f1876f35 b/fuzz/corpus/9e696677edd5d0e273e23553b4ee5362f1876f35 new file mode 100644 index 00000000..42717078 Binary files /dev/null and b/fuzz/corpus/9e696677edd5d0e273e23553b4ee5362f1876f35 differ diff --git a/fuzz/corpus/9e891040b46c530baab056ec2e114eaacf34945f b/fuzz/corpus/9e891040b46c530baab056ec2e114eaacf34945f new file mode 100644 index 00000000..6c6c684d Binary files /dev/null and b/fuzz/corpus/9e891040b46c530baab056ec2e114eaacf34945f differ diff --git a/fuzz/corpus/9f9fbb4007d7c8f37d131a919b540889d1d9e4bc b/fuzz/corpus/9f9fbb4007d7c8f37d131a919b540889d1d9e4bc new file mode 100644 index 00000000..7fe5da34 Binary files /dev/null and b/fuzz/corpus/9f9fbb4007d7c8f37d131a919b540889d1d9e4bc differ diff --git a/fuzz/corpus/a02172af424a66f6fc9a278213adfa944f7608fd b/fuzz/corpus/a02172af424a66f6fc9a278213adfa944f7608fd new file mode 100644 index 00000000..ef9bcbae Binary files /dev/null and b/fuzz/corpus/a02172af424a66f6fc9a278213adfa944f7608fd differ diff --git a/fuzz/corpus/a03f1dcf20bbdd19ebefe4f80cf38d799c8c00c4 b/fuzz/corpus/a03f1dcf20bbdd19ebefe4f80cf38d799c8c00c4 new file mode 100644 index 00000000..98e5332e Binary files /dev/null and b/fuzz/corpus/a03f1dcf20bbdd19ebefe4f80cf38d799c8c00c4 differ diff --git a/fuzz/corpus/a0a174d3d10f29c30356e231947b9051c4e6be54 b/fuzz/corpus/a0a174d3d10f29c30356e231947b9051c4e6be54 new file mode 100644 index 00000000..0416f80e Binary files /dev/null and b/fuzz/corpus/a0a174d3d10f29c30356e231947b9051c4e6be54 differ diff --git a/fuzz/corpus/a0d5afca37acf3d7eea972dd2977f3ffe4a8f37d b/fuzz/corpus/a0d5afca37acf3d7eea972dd2977f3ffe4a8f37d new file mode 100644 index 00000000..710dcd54 Binary files /dev/null and b/fuzz/corpus/a0d5afca37acf3d7eea972dd2977f3ffe4a8f37d differ diff --git a/fuzz/corpus/a1ba5f16e0e092d24f7857752935b290e0124e72 b/fuzz/corpus/a1ba5f16e0e092d24f7857752935b290e0124e72 new file mode 100644 index 00000000..f087da4f Binary files /dev/null and b/fuzz/corpus/a1ba5f16e0e092d24f7857752935b290e0124e72 differ diff --git a/fuzz/corpus/a3e78b1df456fd877b9f6179f135a5d98378b29c b/fuzz/corpus/a3e78b1df456fd877b9f6179f135a5d98378b29c new file mode 100644 index 00000000..2dace095 Binary files /dev/null and b/fuzz/corpus/a3e78b1df456fd877b9f6179f135a5d98378b29c differ diff --git a/fuzz/corpus/a7473834a9451bf3bb31ee5278bba4bdd43a6f1f b/fuzz/corpus/a7473834a9451bf3bb31ee5278bba4bdd43a6f1f new file mode 100644 index 00000000..830eadb6 Binary files /dev/null and b/fuzz/corpus/a7473834a9451bf3bb31ee5278bba4bdd43a6f1f differ diff --git a/fuzz/corpus/a7cdd52296420455fc828ddd761ea103dbb456d0 b/fuzz/corpus/a7cdd52296420455fc828ddd761ea103dbb456d0 new file mode 100644 index 00000000..a5342d75 Binary files /dev/null and b/fuzz/corpus/a7cdd52296420455fc828ddd761ea103dbb456d0 differ diff --git a/fuzz/corpus/a7dacaf428193dae8af833750cef9a6d00b5c773 b/fuzz/corpus/a7dacaf428193dae8af833750cef9a6d00b5c773 new file mode 100644 index 00000000..d114aa70 Binary files /dev/null and b/fuzz/corpus/a7dacaf428193dae8af833750cef9a6d00b5c773 differ diff --git a/fuzz/corpus/a9b7509a44e1cddcd3e79115d15da0fb6084459a b/fuzz/corpus/a9b7509a44e1cddcd3e79115d15da0fb6084459a new file mode 100644 index 00000000..a22db506 Binary files /dev/null and b/fuzz/corpus/a9b7509a44e1cddcd3e79115d15da0fb6084459a differ diff --git a/fuzz/corpus/aac03222d9b7a4fb9933e8f65538338cb28d6389 b/fuzz/corpus/aac03222d9b7a4fb9933e8f65538338cb28d6389 new file mode 100644 index 00000000..3eae8533 Binary files /dev/null and b/fuzz/corpus/aac03222d9b7a4fb9933e8f65538338cb28d6389 differ diff --git a/fuzz/corpus/ab6ad7b89748a1fe481186f091f9e391c426726a b/fuzz/corpus/ab6ad7b89748a1fe481186f091f9e391c426726a new file mode 100644 index 00000000..e039249d Binary files /dev/null and b/fuzz/corpus/ab6ad7b89748a1fe481186f091f9e391c426726a differ diff --git a/fuzz/corpus/b04cfc32b841d9438740b4aab517588d3cfdbdb5 b/fuzz/corpus/b04cfc32b841d9438740b4aab517588d3cfdbdb5 new file mode 100644 index 00000000..b9e47a3f Binary files /dev/null and b/fuzz/corpus/b04cfc32b841d9438740b4aab517588d3cfdbdb5 differ diff --git a/fuzz/corpus/b0d538ba10aa6aca3af422156c25e592df549bfa b/fuzz/corpus/b0d538ba10aa6aca3af422156c25e592df549bfa new file mode 100644 index 00000000..f530220d Binary files /dev/null and b/fuzz/corpus/b0d538ba10aa6aca3af422156c25e592df549bfa differ diff --git a/fuzz/corpus/b0f3fda2e1bf328f1d6f3f1497aa7fa4c02cbf65 b/fuzz/corpus/b0f3fda2e1bf328f1d6f3f1497aa7fa4c02cbf65 new file mode 100644 index 00000000..c15ebd52 Binary files /dev/null and b/fuzz/corpus/b0f3fda2e1bf328f1d6f3f1497aa7fa4c02cbf65 differ diff --git a/fuzz/corpus/b31db2a9303539638df51abecaea9edc4d0aa660 b/fuzz/corpus/b31db2a9303539638df51abecaea9edc4d0aa660 new file mode 100644 index 00000000..9eb99222 Binary files /dev/null and b/fuzz/corpus/b31db2a9303539638df51abecaea9edc4d0aa660 differ diff --git a/fuzz/corpus/b3e2615d6c47bd80f5e5a4b0ed3f208d5628cd20 b/fuzz/corpus/b3e2615d6c47bd80f5e5a4b0ed3f208d5628cd20 new file mode 100644 index 00000000..c0fc997e Binary files /dev/null and b/fuzz/corpus/b3e2615d6c47bd80f5e5a4b0ed3f208d5628cd20 differ diff --git a/fuzz/corpus/b44d71cf10b459b618a5b31186cf92b5cc302bc7 b/fuzz/corpus/b44d71cf10b459b618a5b31186cf92b5cc302bc7 new file mode 100644 index 00000000..78c749c8 Binary files /dev/null and b/fuzz/corpus/b44d71cf10b459b618a5b31186cf92b5cc302bc7 differ diff --git a/fuzz/corpus/b5220686b2d4f078319318dcafdcaff665fe6c8d b/fuzz/corpus/b5220686b2d4f078319318dcafdcaff665fe6c8d new file mode 100644 index 00000000..5d11f490 Binary files /dev/null and b/fuzz/corpus/b5220686b2d4f078319318dcafdcaff665fe6c8d differ diff --git a/fuzz/corpus/b6ddeebc1d724c77817a0b9ab5d390e26303e03e b/fuzz/corpus/b6ddeebc1d724c77817a0b9ab5d390e26303e03e new file mode 100644 index 00000000..dd48a8d6 Binary files /dev/null and b/fuzz/corpus/b6ddeebc1d724c77817a0b9ab5d390e26303e03e differ diff --git a/fuzz/corpus/b7336217e23ff6cddc65cb7c798fc71e207bac6d b/fuzz/corpus/b7336217e23ff6cddc65cb7c798fc71e207bac6d new file mode 100644 index 00000000..baa32d1d Binary files /dev/null and b/fuzz/corpus/b7336217e23ff6cddc65cb7c798fc71e207bac6d differ diff --git a/fuzz/corpus/b78d7aac72fdcec7bb5fede4cdd570287d37ad54 b/fuzz/corpus/b78d7aac72fdcec7bb5fede4cdd570287d37ad54 new file mode 100644 index 00000000..5ff32228 Binary files /dev/null and b/fuzz/corpus/b78d7aac72fdcec7bb5fede4cdd570287d37ad54 differ diff --git a/fuzz/corpus/bb9bf63afed6ba7543f9ff957f03baf383bcd00b b/fuzz/corpus/bb9bf63afed6ba7543f9ff957f03baf383bcd00b new file mode 100644 index 00000000..cccc07ee Binary files /dev/null and b/fuzz/corpus/bb9bf63afed6ba7543f9ff957f03baf383bcd00b differ diff --git a/fuzz/corpus/bef08279879afd4e08e0521f0b646877d7037b69 b/fuzz/corpus/bef08279879afd4e08e0521f0b646877d7037b69 new file mode 100644 index 00000000..d8503fac Binary files /dev/null and b/fuzz/corpus/bef08279879afd4e08e0521f0b646877d7037b69 differ diff --git a/fuzz/corpus/c0df355f326bb9914e67bf7da788853c914fc704 b/fuzz/corpus/c0df355f326bb9914e67bf7da788853c914fc704 new file mode 100644 index 00000000..cfa9eeef Binary files /dev/null and b/fuzz/corpus/c0df355f326bb9914e67bf7da788853c914fc704 differ diff --git a/fuzz/corpus/c3f035a36c11df93158ed7111b880c3a372ea835 b/fuzz/corpus/c3f035a36c11df93158ed7111b880c3a372ea835 new file mode 100644 index 00000000..d0affa8c Binary files /dev/null and b/fuzz/corpus/c3f035a36c11df93158ed7111b880c3a372ea835 differ diff --git a/fuzz/corpus/c3fb0103c6da6d6158209bd99aeca5a359a15639 b/fuzz/corpus/c3fb0103c6da6d6158209bd99aeca5a359a15639 new file mode 100644 index 00000000..e20724dc Binary files /dev/null and b/fuzz/corpus/c3fb0103c6da6d6158209bd99aeca5a359a15639 differ diff --git a/fuzz/corpus/c41e90f8c259305e7eef5818f762b7f8363fdb2f b/fuzz/corpus/c41e90f8c259305e7eef5818f762b7f8363fdb2f new file mode 100644 index 00000000..981dec4d Binary files /dev/null and b/fuzz/corpus/c41e90f8c259305e7eef5818f762b7f8363fdb2f differ diff --git a/fuzz/corpus/c41efbb0f5b4a095bd3bb1638a8cbfe89ab761d7 b/fuzz/corpus/c41efbb0f5b4a095bd3bb1638a8cbfe89ab761d7 new file mode 100644 index 00000000..55468289 Binary files /dev/null and b/fuzz/corpus/c41efbb0f5b4a095bd3bb1638a8cbfe89ab761d7 differ diff --git a/fuzz/corpus/c7ad6adc133f798b5cfff03a27ab95c394c048c0 b/fuzz/corpus/c7ad6adc133f798b5cfff03a27ab95c394c048c0 new file mode 100644 index 00000000..6bb220b8 Binary files /dev/null and b/fuzz/corpus/c7ad6adc133f798b5cfff03a27ab95c394c048c0 differ diff --git a/fuzz/corpus/c96bb8bdd516bb5cd2f51ea887b02c95974ce850 b/fuzz/corpus/c96bb8bdd516bb5cd2f51ea887b02c95974ce850 new file mode 100644 index 00000000..99b02819 Binary files /dev/null and b/fuzz/corpus/c96bb8bdd516bb5cd2f51ea887b02c95974ce850 differ diff --git a/fuzz/corpus/ca3971ff6a78ec291e71d633948c5881ee928d57 b/fuzz/corpus/ca3971ff6a78ec291e71d633948c5881ee928d57 new file mode 100644 index 00000000..cbd10bae Binary files /dev/null and b/fuzz/corpus/ca3971ff6a78ec291e71d633948c5881ee928d57 differ diff --git a/fuzz/corpus/cae31911e8672e5d7ca0444eb70aeb86aa0f89c9 b/fuzz/corpus/cae31911e8672e5d7ca0444eb70aeb86aa0f89c9 new file mode 100644 index 00000000..885ecd99 Binary files /dev/null and b/fuzz/corpus/cae31911e8672e5d7ca0444eb70aeb86aa0f89c9 differ diff --git a/fuzz/corpus/cc30e664354997c9b2eeacc8cf80ebe9d6981232 b/fuzz/corpus/cc30e664354997c9b2eeacc8cf80ebe9d6981232 new file mode 100644 index 00000000..317ced9f Binary files /dev/null and b/fuzz/corpus/cc30e664354997c9b2eeacc8cf80ebe9d6981232 differ diff --git a/fuzz/corpus/ccbc73bfebcc697019281a88d69ed411abd022b3 b/fuzz/corpus/ccbc73bfebcc697019281a88d69ed411abd022b3 new file mode 100644 index 00000000..59d554c8 Binary files /dev/null and b/fuzz/corpus/ccbc73bfebcc697019281a88d69ed411abd022b3 differ diff --git a/fuzz/corpus/ccc9b5c481c7005a2b18c6f7b3895d57055b57c3 b/fuzz/corpus/ccc9b5c481c7005a2b18c6f7b3895d57055b57c3 new file mode 100644 index 00000000..37176a5d Binary files /dev/null and b/fuzz/corpus/ccc9b5c481c7005a2b18c6f7b3895d57055b57c3 differ diff --git a/fuzz/corpus/ce778ab2b71c7520a4bf6a53c71ae8788b92abd7 b/fuzz/corpus/ce778ab2b71c7520a4bf6a53c71ae8788b92abd7 new file mode 100644 index 00000000..01bf3076 Binary files /dev/null and b/fuzz/corpus/ce778ab2b71c7520a4bf6a53c71ae8788b92abd7 differ diff --git a/fuzz/corpus/cece690ab3ae1d247b714f0d31bf47b315d5232d b/fuzz/corpus/cece690ab3ae1d247b714f0d31bf47b315d5232d new file mode 100644 index 00000000..bd4f69c1 Binary files /dev/null and b/fuzz/corpus/cece690ab3ae1d247b714f0d31bf47b315d5232d differ diff --git a/fuzz/corpus/d0c1f6a6ee9e21381e14d656da926258b063b41d b/fuzz/corpus/d0c1f6a6ee9e21381e14d656da926258b063b41d new file mode 100644 index 00000000..cfce562e Binary files /dev/null and b/fuzz/corpus/d0c1f6a6ee9e21381e14d656da926258b063b41d differ diff --git a/fuzz/corpus/d2896fed2190b5bd98329d9758c26e5bc441ae5b b/fuzz/corpus/d2896fed2190b5bd98329d9758c26e5bc441ae5b new file mode 100644 index 00000000..8f550124 Binary files /dev/null and b/fuzz/corpus/d2896fed2190b5bd98329d9758c26e5bc441ae5b differ diff --git a/fuzz/corpus/d44ae1090f46442bf6f82571122388c07ff2df5b b/fuzz/corpus/d44ae1090f46442bf6f82571122388c07ff2df5b new file mode 100644 index 00000000..262e8741 Binary files /dev/null and b/fuzz/corpus/d44ae1090f46442bf6f82571122388c07ff2df5b differ diff --git a/fuzz/corpus/d643dddfa978b15326ac185fd8592a4064e213a4 b/fuzz/corpus/d643dddfa978b15326ac185fd8592a4064e213a4 new file mode 100644 index 00000000..8239db0b Binary files /dev/null and b/fuzz/corpus/d643dddfa978b15326ac185fd8592a4064e213a4 differ diff --git a/fuzz/corpus/d8cd66165e33ac8171904dec514c4f50ab610cdb b/fuzz/corpus/d8cd66165e33ac8171904dec514c4f50ab610cdb new file mode 100644 index 00000000..3f38cf58 Binary files /dev/null and b/fuzz/corpus/d8cd66165e33ac8171904dec514c4f50ab610cdb differ diff --git a/fuzz/corpus/d9f33a1e75a97cc33db614bc02d71d8fd4176ee4 b/fuzz/corpus/d9f33a1e75a97cc33db614bc02d71d8fd4176ee4 new file mode 100644 index 00000000..98722b1f Binary files /dev/null and b/fuzz/corpus/d9f33a1e75a97cc33db614bc02d71d8fd4176ee4 differ diff --git a/fuzz/corpus/dadbd79924eef32b6199431701f3c8e7bad9fdcc b/fuzz/corpus/dadbd79924eef32b6199431701f3c8e7bad9fdcc new file mode 100644 index 00000000..7f98e6b6 Binary files /dev/null and b/fuzz/corpus/dadbd79924eef32b6199431701f3c8e7bad9fdcc differ diff --git a/fuzz/corpus/de19e810b9e2ebb6b12dd37b5e7d1076f34af5dd b/fuzz/corpus/de19e810b9e2ebb6b12dd37b5e7d1076f34af5dd new file mode 100644 index 00000000..1c458cfc Binary files /dev/null and b/fuzz/corpus/de19e810b9e2ebb6b12dd37b5e7d1076f34af5dd differ diff --git a/fuzz/corpus/de61430a06e786fa828a1841997ca746f314a50f b/fuzz/corpus/de61430a06e786fa828a1841997ca746f314a50f new file mode 100644 index 00000000..5b874cc3 Binary files /dev/null and b/fuzz/corpus/de61430a06e786fa828a1841997ca746f314a50f differ diff --git a/fuzz/corpus/df3bc063c169d37389a06837ed46208f1fe1c25b b/fuzz/corpus/df3bc063c169d37389a06837ed46208f1fe1c25b new file mode 100644 index 00000000..0e219a52 Binary files /dev/null and b/fuzz/corpus/df3bc063c169d37389a06837ed46208f1fe1c25b differ diff --git a/fuzz/corpus/e0aceb3ef023d2f1af7eb7c8926a1b1df5cdbe89 b/fuzz/corpus/e0aceb3ef023d2f1af7eb7c8926a1b1df5cdbe89 new file mode 100644 index 00000000..a63e767a Binary files /dev/null and b/fuzz/corpus/e0aceb3ef023d2f1af7eb7c8926a1b1df5cdbe89 differ diff --git a/fuzz/corpus/e14e7128b5b6eb4fd639e7510a9e58b4fc91fbec b/fuzz/corpus/e14e7128b5b6eb4fd639e7510a9e58b4fc91fbec new file mode 100644 index 00000000..17ec8d91 Binary files /dev/null and b/fuzz/corpus/e14e7128b5b6eb4fd639e7510a9e58b4fc91fbec differ diff --git a/fuzz/corpus/e1a4624b772e1c6f5bc3f85ae05d2853dbaeeceb b/fuzz/corpus/e1a4624b772e1c6f5bc3f85ae05d2853dbaeeceb new file mode 100644 index 00000000..167c4c11 Binary files /dev/null and b/fuzz/corpus/e1a4624b772e1c6f5bc3f85ae05d2853dbaeeceb differ diff --git a/fuzz/corpus/e1b5f7e47927e4843e9b029365eb6a5a6e8530d2 b/fuzz/corpus/e1b5f7e47927e4843e9b029365eb6a5a6e8530d2 new file mode 100644 index 00000000..3416fc2d Binary files /dev/null and b/fuzz/corpus/e1b5f7e47927e4843e9b029365eb6a5a6e8530d2 differ diff --git a/fuzz/corpus/e64ba7edd4532eff9b608d6877c63eb35d3bb65e b/fuzz/corpus/e64ba7edd4532eff9b608d6877c63eb35d3bb65e new file mode 100644 index 00000000..112363ac Binary files /dev/null and b/fuzz/corpus/e64ba7edd4532eff9b608d6877c63eb35d3bb65e differ diff --git a/fuzz/corpus/e7542ae2ec4ae9324af87522693fa2a84e0258ce b/fuzz/corpus/e7542ae2ec4ae9324af87522693fa2a84e0258ce new file mode 100644 index 00000000..136e162d Binary files /dev/null and b/fuzz/corpus/e7542ae2ec4ae9324af87522693fa2a84e0258ce differ diff --git a/fuzz/corpus/e7bfe09199cdf08f4e43dd7faf1526043aefeb62 b/fuzz/corpus/e7bfe09199cdf08f4e43dd7faf1526043aefeb62 new file mode 100644 index 00000000..dfcb27b0 Binary files /dev/null and b/fuzz/corpus/e7bfe09199cdf08f4e43dd7faf1526043aefeb62 differ diff --git a/fuzz/corpus/e887d9a2218498334f1f5ca31610312bee121842 b/fuzz/corpus/e887d9a2218498334f1f5ca31610312bee121842 new file mode 100644 index 00000000..ee743513 Binary files /dev/null and b/fuzz/corpus/e887d9a2218498334f1f5ca31610312bee121842 differ diff --git a/fuzz/corpus/e8e62b69bc04cb5ed30fb7fad6e2a77c320f88f5 b/fuzz/corpus/e8e62b69bc04cb5ed30fb7fad6e2a77c320f88f5 new file mode 100644 index 00000000..cc7e1176 Binary files /dev/null and b/fuzz/corpus/e8e62b69bc04cb5ed30fb7fad6e2a77c320f88f5 differ diff --git a/fuzz/corpus/ebca472922552feb96b5a1fb246827e155ef2549 b/fuzz/corpus/ebca472922552feb96b5a1fb246827e155ef2549 new file mode 100644 index 00000000..a4e38b44 Binary files /dev/null and b/fuzz/corpus/ebca472922552feb96b5a1fb246827e155ef2549 differ diff --git a/fuzz/corpus/ec30f45e3fa2f22524a1edf302dd5cf739e2a2e6 b/fuzz/corpus/ec30f45e3fa2f22524a1edf302dd5cf739e2a2e6 new file mode 100644 index 00000000..85b05a26 Binary files /dev/null and b/fuzz/corpus/ec30f45e3fa2f22524a1edf302dd5cf739e2a2e6 differ diff --git a/fuzz/corpus/ecbdd50b499c5ee8d76340846bbc3e9ffc20bd4f b/fuzz/corpus/ecbdd50b499c5ee8d76340846bbc3e9ffc20bd4f new file mode 100644 index 00000000..014aed19 Binary files /dev/null and b/fuzz/corpus/ecbdd50b499c5ee8d76340846bbc3e9ffc20bd4f differ diff --git a/fuzz/corpus/ed244dab55c0d6fbb36ab9d4bc33c22f8d7653b0 b/fuzz/corpus/ed244dab55c0d6fbb36ab9d4bc33c22f8d7653b0 new file mode 100644 index 00000000..66ba623d Binary files /dev/null and b/fuzz/corpus/ed244dab55c0d6fbb36ab9d4bc33c22f8d7653b0 differ diff --git a/fuzz/corpus/ee1e299cce1b26876ce104f944c81abd59233afb b/fuzz/corpus/ee1e299cce1b26876ce104f944c81abd59233afb new file mode 100644 index 00000000..db676e17 Binary files /dev/null and b/fuzz/corpus/ee1e299cce1b26876ce104f944c81abd59233afb differ diff --git a/fuzz/corpus/efbf741518ed3a5b72cbff940acf2b34e816ecfb b/fuzz/corpus/efbf741518ed3a5b72cbff940acf2b34e816ecfb new file mode 100644 index 00000000..6c93a969 Binary files /dev/null and b/fuzz/corpus/efbf741518ed3a5b72cbff940acf2b34e816ecfb differ diff --git a/fuzz/corpus/f08abb06077a2e1699becdfbf16e92d3a54f2fa1 b/fuzz/corpus/f08abb06077a2e1699becdfbf16e92d3a54f2fa1 new file mode 100644 index 00000000..983c3427 Binary files /dev/null and b/fuzz/corpus/f08abb06077a2e1699becdfbf16e92d3a54f2fa1 differ diff --git a/fuzz/corpus/f2f074dfe49dbe033fe324236aead5176b8938b5 b/fuzz/corpus/f2f074dfe49dbe033fe324236aead5176b8938b5 new file mode 100644 index 00000000..d7f6bb70 Binary files /dev/null and b/fuzz/corpus/f2f074dfe49dbe033fe324236aead5176b8938b5 differ diff --git a/fuzz/corpus/f404023e17bbf5179c1d2a65d8e5656f60123e17 b/fuzz/corpus/f404023e17bbf5179c1d2a65d8e5656f60123e17 new file mode 100644 index 00000000..c30d5041 Binary files /dev/null and b/fuzz/corpus/f404023e17bbf5179c1d2a65d8e5656f60123e17 differ diff --git a/fuzz/corpus/f5fb06e95d6695b370477edefb4c300298cbe2cf b/fuzz/corpus/f5fb06e95d6695b370477edefb4c300298cbe2cf new file mode 100644 index 00000000..2cc8e61d Binary files /dev/null and b/fuzz/corpus/f5fb06e95d6695b370477edefb4c300298cbe2cf differ diff --git a/fuzz/corpus/f602046b30549b3f62dd5ab123d28094521aa513 b/fuzz/corpus/f602046b30549b3f62dd5ab123d28094521aa513 new file mode 100644 index 00000000..a6f34fc8 Binary files /dev/null and b/fuzz/corpus/f602046b30549b3f62dd5ab123d28094521aa513 differ diff --git a/fuzz/corpus/f84127d4aefd8cf967c8d1ad7efc9a7ef0a83fd1 b/fuzz/corpus/f84127d4aefd8cf967c8d1ad7efc9a7ef0a83fd1 new file mode 100644 index 00000000..315be7ff Binary files /dev/null and b/fuzz/corpus/f84127d4aefd8cf967c8d1ad7efc9a7ef0a83fd1 differ diff --git a/fuzz/corpus/f87df90d7bc4c3081af67a387f13a2f5e5e7b663 b/fuzz/corpus/f87df90d7bc4c3081af67a387f13a2f5e5e7b663 new file mode 100644 index 00000000..ed316a55 Binary files /dev/null and b/fuzz/corpus/f87df90d7bc4c3081af67a387f13a2f5e5e7b663 differ diff --git a/fuzz/corpus/f961cfb05ce44cfbb80b49108f1c24f203e53008 b/fuzz/corpus/f961cfb05ce44cfbb80b49108f1c24f203e53008 new file mode 100644 index 00000000..9e39b1b3 Binary files /dev/null and b/fuzz/corpus/f961cfb05ce44cfbb80b49108f1c24f203e53008 differ diff --git a/fuzz/corpus/f96d3c8ec87eb79a2d5a32e766f2fa364a1787d8 b/fuzz/corpus/f96d3c8ec87eb79a2d5a32e766f2fa364a1787d8 new file mode 100644 index 00000000..d456a405 Binary files /dev/null and b/fuzz/corpus/f96d3c8ec87eb79a2d5a32e766f2fa364a1787d8 differ diff --git a/fuzz/corpus/fa056d3eebf1859a8b702d2520d3f879913ce8d7 b/fuzz/corpus/fa056d3eebf1859a8b702d2520d3f879913ce8d7 new file mode 100644 index 00000000..00d7e885 Binary files /dev/null and b/fuzz/corpus/fa056d3eebf1859a8b702d2520d3f879913ce8d7 differ diff --git a/fuzz/corpus/fa253cc79703f60938775baede93c2cab056d6da b/fuzz/corpus/fa253cc79703f60938775baede93c2cab056d6da new file mode 100644 index 00000000..3403c6f6 Binary files /dev/null and b/fuzz/corpus/fa253cc79703f60938775baede93c2cab056d6da differ diff --git a/fuzz/corpus/fa7622e5400b857832a96fd860653b10c9ba4f52 b/fuzz/corpus/fa7622e5400b857832a96fd860653b10c9ba4f52 new file mode 100644 index 00000000..9141dfbc Binary files /dev/null and b/fuzz/corpus/fa7622e5400b857832a96fd860653b10c9ba4f52 differ diff --git a/fuzz/corpus/fb71d501937402a04cfeca84078922fe59ee75f3 b/fuzz/corpus/fb71d501937402a04cfeca84078922fe59ee75f3 new file mode 100644 index 00000000..e11d5e64 Binary files /dev/null and b/fuzz/corpus/fb71d501937402a04cfeca84078922fe59ee75f3 differ diff --git a/fuzz/corpus/fb888d425a2cef4158ef3bf0b456dfa9acec78a0 b/fuzz/corpus/fb888d425a2cef4158ef3bf0b456dfa9acec78a0 new file mode 100644 index 00000000..640cf9a3 Binary files /dev/null and b/fuzz/corpus/fb888d425a2cef4158ef3bf0b456dfa9acec78a0 differ diff --git a/fuzz/encoder_fuzzer.c b/fuzz/encoder_fuzzer.c new file mode 100644 index 00000000..36992ebc --- /dev/null +++ b/fuzz/encoder_fuzzer.c @@ -0,0 +1,728 @@ +/**************************************************************************** +** +** Copyright (C) 2026 Topogamer0 +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +/* + * Fuzzes the encoder API. The input is a program: after two configuration + * bytes, each byte is a command (low nibble) with a small argument (high + * nibble), optionally followed by operand bytes. See run_program() for the + * command list. + * + * The program is run with an output buffer of the size given in the + * configuration; if that buffer was too small, it is run again with the size + * that cbor_encoder_get_extra_bytes_needed() asked for. It is also run with a + * writer function. All runs must produce the same bytes, which are then + * parsed back, validated, and compared to what the program encoded. + */ + +#include "cbor.h" + +#include +#include +#include +#include + +#ifdef NDEBUG +# error "The fuzzers must be built without NDEBUG: TinyCBOR's assertions check the API usage" +#endif + +#define MAX_NESTING 256 + +/* Nested containers and chained tags must stay within the parser's limit + * (CBOR_PARSER_MAX_RECURSIONS, 1024) for the result to be parsed back */ +#define MAX_TAGS 512 + +/* Abort on a result that contradicts the documented behavior */ +#define CHECK(cond) do { if (!(cond)) abort(); } while (0) + +enum Command { + CmdUInt = 0, + CmdNegativeInt, + CmdInt, + CmdSimpleValue, + CmdBooleanNullUndefined, + CmdTag, + CmdTextString, + CmdByteString, + CmdHalfFloat, + CmdFloat, + CmdDouble, + CmdFloatAsHalfFloat, + CmdCreateArray, + CmdCreateMap, + CmdCloseContainer, + CmdRaw +}; + +/* what the program encoded, to be compared with what is parsed back */ +enum EventType { + EventInteger, + EventNegativeInteger, + EventSimple, /* includes booleans, null and undefined */ + EventTag, + EventString, + EventHalfFloat, + EventFloatAsHalfFloat, /* cbor_encode_float_as_half_float(): value is the float */ + EventFloat, + EventDouble, + EventArray, + EventMap, + EventEnd +}; + +struct Event +{ + enum EventType type; + CborType string_type; + bool indefinite; + uint64_t value; /* value, bit pattern, length or number of pairs */ + const uint8_t *data; /* string contents */ +}; + +struct Input +{ + const uint8_t *ptr; + const uint8_t *end; +}; + +struct Level +{ + CborEncoder encoder; + size_t length; /* declared number of items (pairs for maps) */ + size_t count; /* number of items written, tags excluded */ + bool map; + bool tag_pending; +}; + +struct WriterOutput +{ + uint8_t *data; + size_t size; + int command; /* the command being run, to check the append type */ +}; + +struct Run +{ + struct Level levels[MAX_NESTING + 1]; + int depth; + int tags; + struct WriterOutput *writer; + + struct Event *events; + size_t event_count; + size_t event_capacity; + bool record_events; + + CborError error; /* first error other than CborErrorOutOfMemory */ + bool out_of_memory; + bool used_raw; +}; + +static uint8_t get_byte(struct Input *in) +{ + return in->ptr < in->end ? *in->ptr++ : 0; +} + +static uint64_t get_number(struct Input *in, unsigned bytes) +{ + uint64_t v = 0; + while (bytes--) + v = (v << 8) | get_byte(in); + return v; +} + +/* small immediates (< 8) or 1, 2, 4 or 8 bytes from the input */ +static uint64_t get_value(struct Input *in, unsigned arg) +{ + if (arg < 8) + return arg; + return get_number(in, 1U << (arg & 3)); +} + +static size_t get_data(struct Input *in, unsigned arg, const uint8_t **data) +{ + size_t len = arg < 15 ? arg : (size_t)get_number(in, 2); + size_t available = (size_t)(in->end - in->ptr); + if (len > available) + len = available; + *data = in->ptr; + in->ptr += len; + return len; +} + +static CborError writer_function(void *token, const void *data, size_t len, CborEncoderAppendType type) +{ + struct WriterOutput *out = (struct WriterOutput *)token; + uint8_t *ptr; + + switch (type) { + case CborEncoderAppendCborData: + CHECK(out->command != CmdRaw); + break; + case CborEncoderAppendStringData: + CHECK(out->command == CmdTextString || out->command == CmdByteString); + break; + case CborEncoderAppendRawData: + CHECK(out->command == CmdRaw); + break; + default: + abort(); + } + + if (len == 0) + return CborNoError; + ptr = (uint8_t *)realloc(out->data, out->size + len); + if (!ptr) + return CborErrorOutOfMemory; + memcpy(ptr + out->size, data, len); + out->data = ptr; + out->size += len; + return CborNoError; +} + +static void record(struct Run *run, enum EventType type, uint64_t value) +{ + struct Event *ev; + if (!run->record_events) + return; + if (run->event_count == run->event_capacity) { + run->event_capacity = run->event_capacity ? 2 * run->event_capacity : 64; + run->events = (struct Event *)realloc(run->events, run->event_capacity * sizeof(struct Event)); + CHECK(run->events); + } + ev = &run->events[run->event_count++]; + memset(ev, 0, sizeof(*ev)); + ev->type = type; + ev->value = value; +} + +/* returns false if the program must stop */ +static bool check_error(struct Run *run, CborError err) +{ + if (err == CborErrorOutOfMemory) { + /* documented as recoverable: the encoder keeps counting */ + run->out_of_memory = true; + return true; + } + if (err) { + run->error = err; + return false; + } + return true; +} + +static void item_added(struct Run *run) +{ + struct Level *level = &run->levels[run->depth]; + if (level->tag_pending) + level->tag_pending = false; + ++level->count; +} + +static bool encode_null(struct Run *run) +{ + record(run, EventSimple, CborNullType & 0x1f); + item_added(run); + return check_error(run, cbor_encode_null(&run->levels[run->depth].encoder)); +} + +static bool close_container(struct Run *run) +{ + struct Level *level = &run->levels[run->depth]; + size_t needed = level->map ? 2 * level->length : level->length; + CborError err; + + /* complete what we can, so the result is more often valid CBOR */ + if (level->tag_pending && !encode_null(run)) + return false; + if (level->length == CborIndefiniteLength) { + if (level->map && level->count % 2 && !encode_null(run)) + return false; + } else if (level->count < needed && needed - level->count <= 16) { + while (level->count < needed) { + if (!encode_null(run)) + return false; + } + } + + record(run, EventEnd, 0); + --run->depth; + err = cbor_encoder_close_container(&run->levels[run->depth].encoder, &level->encoder); + return check_error(run, err); +} + +static bool create_container(struct Run *run, bool map, unsigned arg, struct Input *in) +{ + struct Level *parent = &run->levels[run->depth]; + struct Level *level; + size_t length; + CborError err; + + if (arg < 14) + length = arg; + else if (arg == 14) + length = CborIndefiniteLength; + else + length = (size_t)get_number(in, 8); + if (run->depth == MAX_NESTING) + return true; + level = &run->levels[run->depth + 1]; + memset(level, 0, sizeof(*level)); + level->length = length; + level->map = map; + + if (map) + err = cbor_encoder_create_map(&parent->encoder, &level->encoder, length); + else + err = cbor_encoder_create_array(&parent->encoder, &level->encoder, length); + if (!check_error(run, err)) + return false; + + record(run, map ? EventMap : EventArray, length); + if (run->record_events) + run->events[run->event_count - 1].indefinite = length == CborIndefiniteLength; + item_added(run); + ++run->depth; + return true; +} + +/* Runs the whole program on the encoder in run->levels[0] */ +static void run_program(struct Run *run, struct Input in) +{ + while (in.ptr < in.end) { + struct Level *level = &run->levels[run->depth]; + CborEncoder *encoder = &level->encoder; + uint8_t command = get_byte(&in); + unsigned arg = command >> 4; + const uint8_t *data; + size_t len; + uint64_t v; + uint8_t u8; + uint16_t u16; + uint32_t u32; + float f; + CborError err = CborNoError; + + if (run->writer) + run->writer->command = command & 0xf; + + switch (command & 0xf) { + case CmdUInt: + v = get_value(&in, arg); + record(run, EventInteger, v); + err = cbor_encode_uint(encoder, v); + item_added(run); + break; + + case CmdNegativeInt: + /* zero encodes -2^64 */ + v = get_value(&in, arg); + record(run, EventNegativeInteger, v - 1); + err = cbor_encode_negative_int(encoder, v); + item_added(run); + break; + + case CmdInt: + v = get_value(&in, arg); + if (arg & 4) + v = ~v; /* make it negative */ + if ((int64_t)v < 0) + record(run, EventNegativeInteger, ~v); + else + record(run, EventInteger, v); + err = cbor_encode_int(encoder, (int64_t)v); + item_added(run); + break; + + case CmdSimpleValue: + /* 25 to 31 are rejected with CborErrorIllegalSimpleType; 24 is + * accepted but can't be encoded as well-formed CBOR */ + u8 = get_byte(&in); + if (u8 == 24) + break; + record(run, EventSimple, u8); + err = cbor_encode_simple_value(encoder, u8); + item_added(run); + break; + + case CmdBooleanNullUndefined: + switch (arg & 3) { + case 0: + record(run, EventSimple, (CborBooleanType & 0x1f) - 1); + err = cbor_encode_boolean(encoder, false); + break; + case 1: + record(run, EventSimple, CborBooleanType & 0x1f); + err = cbor_encode_boolean(encoder, true); + break; + case 2: + record(run, EventSimple, CborNullType & 0x1f); + err = cbor_encode_null(encoder); + break; + case 3: + record(run, EventSimple, CborUndefinedType & 0x1f); + err = cbor_encode_undefined(encoder); + break; + } + item_added(run); + break; + + case CmdTag: + v = get_value(&in, arg); + if (++run->tags > MAX_TAGS) + break; + record(run, EventTag, v); + err = cbor_encode_tag(encoder, v); + level->tag_pending = true; + break; + + case CmdTextString: + case CmdByteString: + len = get_data(&in, arg, &data); + record(run, EventString, len); + if (run->record_events) { + struct Event *ev = &run->events[run->event_count - 1]; + ev->data = data; + ev->string_type = (command & 0xf) == CmdTextString ? CborTextStringType : CborByteStringType; + } + if ((command & 0xf) == CmdTextString) + err = cbor_encode_text_string(encoder, (const char *)data, len); + else + err = cbor_encode_byte_string(encoder, data, len); + item_added(run); + break; + + case CmdHalfFloat: + u16 = (uint16_t)get_number(&in, 2); + record(run, EventHalfFloat, u16); + err = cbor_encode_half_float(encoder, &u16); + item_added(run); + break; + + case CmdFloat: + u32 = (uint32_t)get_number(&in, 4); + memcpy(&f, &u32, sizeof(f)); + record(run, EventFloat, u32); + err = cbor_encode_float(encoder, f); + item_added(run); + break; + + case CmdDouble: { + double d; + v = get_number(&in, 8); + memcpy(&d, &v, sizeof(d)); + record(run, EventDouble, v); + err = cbor_encode_double(encoder, d); + item_added(run); + break; + } + + case CmdFloatAsHalfFloat: + u32 = (uint32_t)get_number(&in, 4); + memcpy(&f, &u32, sizeof(f)); + /* the value must be representable as half-precision */ + if (!isnan(f) && !isinf(f) && fabsf(f) > 65504.f) + break; + record(run, EventFloatAsHalfFloat, u32); + err = cbor_encode_float_as_half_float(encoder, f); + item_added(run); + break; + + case CmdCreateArray: + case CmdCreateMap: + if (!create_container(run, (command & 0xf) == CmdCreateMap, arg, &in)) + return; + continue; + + case CmdCloseContainer: + if (run->depth && !close_container(run)) + return; + continue; + + case CmdRaw: + /* arbitrary bytes: the result may not be valid CBOR any more */ + len = get_data(&in, arg, &data); + run->used_raw = true; + err = cbor_encode_raw(encoder, data, len); + break; + } + + if (!check_error(run, err)) + return; + } + + if (run->writer) + run->writer->command = CmdCloseContainer; + while (run->depth) { + if (!close_container(run)) + return; + } + if (run->levels[0].tag_pending) + encode_null(run); +} + +static double half_to_double(uint16_t half) +{ + int exponent = (half >> 10) & 0x1f; + int mantissa = half & 0x3ff; + double d; + if (exponent == 0) + d = ldexp(mantissa, -24); + else if (exponent == 0x1f) + d = mantissa ? NAN : INFINITY; + else + d = ldexp(mantissa + 1024, exponent - 25); + return half & 0x8000 ? -d : d; +} + +/* f was in range, so the result must be within half a unit in the last place */ +static void check_half_conversion(float f, uint16_t half) +{ + double d = half_to_double(half); + if (isnan(f)) { + CHECK(isnan(d)); + } else if (isinf(f)) { + CHECK(d == f); + } else { + int exponent = f == 0 ? -14 : ilogb(f); + CHECK(fabs(d - f) <= ldexp(1, (exponent < -14 ? -14 : exponent) - 11)); + CHECK(d == 0 || !signbit(d) == !signbit(f)); + } +} + +static size_t parse_back(CborValue *it, const struct Event *events, size_t count, size_t i, int nesting); + +static size_t parse_back_one(CborValue *it, const struct Event *events, size_t count, size_t i, int nesting) +{ + const struct Event *ev; + CborValue recursed; + uint64_t v; + uint16_t u16; + float f; + double d; + uint8_t *buffer; + size_t len; + bool b; + + CHECK(i < count); + ev = &events[i++]; + switch (cbor_value_get_type(it)) { + case CborIntegerType: + CHECK(ev->type == (cbor_value_is_negative_integer(it) ? EventNegativeInteger : EventInteger)); + cbor_value_get_raw_integer(it, &v); + CHECK(v == ev->value); + break; + + case CborSimpleType: { + uint8_t simple; + CHECK(ev->type == EventSimple); + cbor_value_get_simple_type(it, &simple); + CHECK(simple == ev->value); + break; + } + + case CborBooleanType: + CHECK(ev->type == EventSimple); + cbor_value_get_boolean(it, &b); + CHECK(ev->value == (CborBooleanType & 0x1f) - 1U + b); + break; + + case CborNullType: + case CborUndefinedType: + CHECK(ev->type == EventSimple && ev->value == (cbor_value_get_type(it) & 0x1fU)); + break; + + case CborTagType: + CHECK(ev->type == EventTag); + cbor_value_get_tag(it, &v); + CHECK(v == ev->value); + break; + + case CborHalfFloatType: + CHECK(ev->type == EventHalfFloat || ev->type == EventFloatAsHalfFloat); + cbor_value_get_half_float(it, &u16); + if (ev->type == EventHalfFloat) { + CHECK(u16 == ev->value); + } else { + uint32_t u32 = (uint32_t)ev->value; + memcpy(&f, &u32, sizeof(f)); + check_half_conversion(f, u16); + } + break; + + case CborFloatType: + CHECK(ev->type == EventFloat); + cbor_value_get_float(it, &f); + memcpy(&v, &f, sizeof(f)); + CHECK((uint32_t)v == ev->value); + break; + + case CborDoubleType: + CHECK(ev->type == EventDouble); + cbor_value_get_double(it, &d); + memcpy(&v, &d, sizeof(d)); + CHECK(v == ev->value); + break; + + case CborByteStringType: + case CborTextStringType: + CHECK(ev->type == EventString && ev->string_type == cbor_value_get_type(it)); + CHECK(cbor_value_is_length_known(it)); + if (cbor_value_is_text_string(it)) + CHECK(cbor_value_dup_text_string(it, (char **)&buffer, &len, it) == CborNoError); + else + CHECK(cbor_value_dup_byte_string(it, &buffer, &len, it) == CborNoError); + CHECK(len == ev->value); + CHECK(len == 0 || memcmp(buffer, ev->data, len) == 0); + free(buffer); + return i; + + case CborArrayType: + case CborMapType: + CHECK(ev->type == (cbor_value_is_map(it) ? EventMap : EventArray)); + CHECK(cbor_value_is_length_known(it) == !ev->indefinite); + if (!ev->indefinite) { + if (cbor_value_is_map(it)) + CHECK(cbor_value_get_map_length(it, &len) == CborNoError); + else + CHECK(cbor_value_get_array_length(it, &len) == CborNoError); + CHECK(len == ev->value); + } + CHECK(nesting > 0); + CHECK(cbor_value_enter_container(it, &recursed) == CborNoError); + i = parse_back(&recursed, events, count, i, nesting - 1); + CHECK(i < count && events[i].type == EventEnd); + CHECK(cbor_value_leave_container(it, &recursed) == CborNoError); + return i + 1; + + default: + abort(); + } + + CHECK(cbor_value_advance_fixed(it) == CborNoError); + return i; +} + +static size_t parse_back(CborValue *it, const struct Event *events, size_t count, size_t i, int nesting) +{ + while (!cbor_value_at_end(it)) + i = parse_back_one(it, events, count, i, nesting); + return i; +} + +static void free_run(struct Run *run) +{ + free(run->events); +} + +static void init_run(struct Run *run, bool record_events) +{ + memset(run, 0, sizeof(*run)); + run->record_events = record_events; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + static struct Run run, writer_run; + struct Input in; + struct WriterOutput writer_output = { NULL, 0, -1 }; + uint8_t *buffer; + size_t buffer_size; + size_t used; + const uint8_t *ptr; + const uint8_t *end; + + if (size < 2) + return 0; + buffer_size = data[0] | (data[1] & 0x0f) << 8; + in.ptr = data + 2; + in.end = data + size; + + /* first run, with a buffer that is possibly too small */ + init_run(&run, true); + buffer = (uint8_t *)malloc(buffer_size); + CHECK(buffer || buffer_size == 0); + cbor_encoder_init(&run.levels[0].encoder, buffer, buffer_size, 0); + run_program(&run, in); + + if (run.error == CborNoError && run.out_of_memory) { + /* "Returns how many more bytes the original buffer [...] needs to be + * extended by so that no CborErrorOutOfMemory condition will happen" */ + size_t extra = cbor_encoder_get_extra_bytes_needed(&run.levels[0].encoder); + CHECK(extra > 0); + buffer_size += extra; + free(buffer); + free_run(&run); + + init_run(&run, true); + buffer = (uint8_t *)malloc(buffer_size); + CHECK(buffer); + cbor_encoder_init(&run.levels[0].encoder, buffer, buffer_size, 0); + run_program(&run, in); + CHECK(!run.out_of_memory); + CHECK(cbor_encoder_get_buffer_size(&run.levels[0].encoder, buffer) == buffer_size); + } + + /* the same program with a writer function */ + init_run(&writer_run, false); + writer_run.writer = &writer_output; + cbor_encoder_init_writer(&writer_run.levels[0].encoder, writer_function, &writer_output); + run_program(&writer_run, in); + CHECK(writer_run.error == run.error); + CHECK(!writer_run.out_of_memory); + + if (run.error == CborNoError) { + used = cbor_encoder_get_buffer_size(&run.levels[0].encoder, buffer); + CHECK(cbor_encoder_get_extra_bytes_needed(&run.levels[0].encoder) == 0); + CHECK(writer_output.size == used); + CHECK(used == 0 || memcmp(writer_output.data, buffer, used) == 0); + } + + if (run.error == CborNoError && !run.used_raw) { + /* parse it back: it's a sequence of items. The encoder always uses + * the shortest encoding for integers and lengths; parse_back() + * checks that indeterminate length was used where requested. */ + size_t i = 0; + + ptr = buffer; + end = buffer + used; + while (ptr < end) { + CborParser parser; + CborValue it; + CborError err; + CHECK(cbor_parser_init(ptr, (size_t)(end - ptr), 0, &parser, &it) == CborNoError); + err = cbor_value_validate(&it, CborValidateShortestIntegrals); + CHECK(err == CborNoError); + i = parse_back(&it, run.events, run.event_count, i, MAX_NESTING); + CHECK(cbor_value_get_next_byte(&it) > ptr); + ptr = cbor_value_get_next_byte(&it); + } + CHECK(i == run.event_count); + } + + free(writer_output.data); + free(buffer); + free_run(&run); + free_run(&writer_run); + return 0; +} diff --git a/fuzz/parser_fuzzer.c b/fuzz/parser_fuzzer.c new file mode 100644 index 00000000..8119987c --- /dev/null +++ b/fuzz/parser_fuzzer.c @@ -0,0 +1,525 @@ +/**************************************************************************** +** +** Copyright (C) 2026 Topogamer0 +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +/* + * Fuzzes the parser API: the input is decoded as a sequence of CBOR items and + * every item is visited, including those nested inside arrays, maps and tags, + * extracting each value with the corresponding cbor_value_get_* function. + * + * The first 4 bytes of the input are a configuration word (see FuzzConfig); + * the rest is the CBOR payload. + */ + +#include "cbor.h" + +#include +#include +#include + +#ifdef NDEBUG +# error "The fuzzers must be built without NDEBUG: TinyCBOR's assertions check the API usage" +#endif + +/* Bounds the harness' own recursion. The library has its own limit + * (CBOR_PARSER_MAX_RECURSIONS) for the functions that recurse. */ +#define MAX_NESTING 1024 + +/* Cross-checks whose cost is proportional to the size of the item (instead + * of being constant per item) are limited, so the total time stays linear in + * the size of the input */ +#define MAX_EXPENSIVE_CHECKS 16 + +enum FuzzConfig { + /* use cbor_parser_init_reader() instead of cbor_parser_init() */ + ConfigUseReader = 0x01, + + /* reader only: make the first item visible one byte at a time, calling + * cbor_value_reparse() after each CborErrorUnexpectedEOF */ + ConfigIncremental = 0x02, + + /* reader only: skip containers with cbor_value_advance() and read + * strings with cbor_value_copy_*_string() instead of chunk by chunk */ + ConfigReaderAdvance = 0x04 +}; + +/* Abort on a result that is inconsistent with another API's result: if + * this triggers, the library disagrees with itself. */ +#define CHECK(cond) do { if (!(cond)) abort(); } while (0) + +/* keeps the decoded values from being optimized out */ +static volatile uint64_t sink; + +struct ReaderInput +{ + const uint8_t *data; + size_t size; /* how much of the data is visible to the parser */ + size_t consumed; +}; + +struct Context +{ + bool reader; + bool reader_advance; + int expensive_checks; + uint8_t *buffer; /* for cbor_value_copy_*_string() in reader mode */ + size_t buffer_size; +}; + +static bool reader_can_read_bytes(void *token, size_t len) +{ + struct ReaderInput *input = (struct ReaderInput *)token; + return input->size - input->consumed >= len; +} + +static void *reader_read_bytes(void *token, void *dst, size_t offset, size_t len) +{ + struct ReaderInput *input = (struct ReaderInput *)token; + size_t available = input->size - input->consumed; + + /* the library must have checked with can_read_bytes() first */ + CHECK(offset <= available && len <= available - offset); + return memcpy(dst, input->data + input->consumed + offset, len); +} + +static void reader_advance_bytes(void *token, size_t len) +{ + struct ReaderInput *input = (struct ReaderInput *)token; + CHECK(len <= input->size - input->consumed); + input->consumed += len; +} + +static CborError reader_transfer_string(void *token, const void **userptr, size_t offset, size_t len) +{ + struct ReaderInput *input = (struct ReaderInput *)token; + size_t available = input->size - input->consumed; + if (offset > available || len > available - offset) + return CborErrorUnexpectedEOF; + input->consumed += offset; + *userptr = input->data + input->consumed; + input->consumed += len; + return CborNoError; +} + +static const struct CborParserOperations reader_ops = { + reader_can_read_bytes, + reader_read_bytes, + reader_advance_bytes, + reader_transfer_string +}; + +static bool expensive_check(struct Context *ctx) +{ + if (ctx->expensive_checks == 0) + return false; + --ctx->expensive_checks; + return true; +} + +static uint64_t checksum(const void *ptr, size_t len) +{ + const uint8_t *bytes = (const uint8_t *)ptr; + uint64_t sum = 0; + while (len--) + sum = sum * 31 + *bytes++; + return sum; +} + +static CborError get_string_chunk(CborValue *it, const void **ptr, size_t *len) +{ + if (cbor_value_is_text_string(it)) + return cbor_value_get_text_string_chunk(it, (const char **)ptr, len, it); + return cbor_value_get_byte_string_chunk(it, (const uint8_t **)ptr, len, it); +} + +static CborError copy_string(const CborValue *it, void *buffer, size_t *len, CborValue *next) +{ + if (cbor_value_is_text_string(it)) + return cbor_value_copy_text_string(it, (char *)buffer, len, next); + return cbor_value_copy_byte_string(it, (uint8_t *)buffer, len, next); +} + +static CborError parse_value(struct Context *ctx, CborValue *it, int nesting); + +static void parse_integer(const CborValue *it) +{ + uint64_t raw, u64; + int64_t i64, i64_unchecked; + int i, i_unchecked; + + cbor_value_get_raw_integer(it, &raw); + sink += raw; + if (cbor_value_is_unsigned_integer(it)) { + cbor_value_get_uint64(it, &u64); + CHECK(u64 == raw); + } else { + CHECK(cbor_value_is_negative_integer(it)); + } + + /* compare with the unchecked getters where the value fits */ + if (cbor_value_get_int64_checked(it, &i64) == CborNoError) { + cbor_value_get_int64(it, &i64_unchecked); + CHECK(i64 == i64_unchecked); + CHECK(cbor_value_is_negative_integer(it) ? i64 < 0 : i64 >= 0); + } + if (cbor_value_get_int_checked(it, &i) == CborNoError) { + cbor_value_get_int(it, &i_unchecked); + CHECK(i == i_unchecked); + } +} + +static void parse_floating_point(const CborValue *it) +{ + float f; + double d; + uint16_t half; + + switch (cbor_value_get_type(it)) { + case CborHalfFloatType: + cbor_value_get_half_float(it, &half); + cbor_value_get_half_float_as_float(it, &f); + sink += half; + /* NaN in half-precision is NaN in single-precision */ + CHECK(((half & 0x7c00) == 0x7c00 && (half & 0x3ff)) == (f != f)); + break; + case CborFloatType: + cbor_value_get_float(it, &f); + memcpy(&half, &f, sizeof(half)); + sink += half; + break; + case CborDoubleType: + cbor_value_get_double(it, &d); + memcpy(&half, &d, sizeof(half)); + sink += half; + break; + default: + break; + } +} + +/* Reads a string with every method and cross-checks them. On success, *it + * points to the next item. */ +static CborError parse_string(CborValue *it) +{ + CborError err; + CborValue chunks = *it; + CborValue next; + size_t len, total, n; + uint8_t *buffer; + uint8_t *copy; + const void *ptr; + bool equals; + bool text = cbor_value_is_text_string(it); + + if (cbor_value_is_length_known(it)) { + err = cbor_value_get_string_length(it, &len); + if (err) + return err; + } + + err = cbor_value_calculate_string_length(it, &len); + if (err) + return err; + + if (text) + err = cbor_value_dup_text_string(it, (char **)&buffer, &n, &next); + else + err = cbor_value_dup_byte_string(it, &buffer, &n, &next); + if (err) + return err; + CHECK(n == len); + sink += checksum(buffer, len); + + /* copy into a buffer without room for the NUL and into one that is one + * byte too small */ + copy = (uint8_t *)malloc(len ? len : 1); + CHECK(copy); + n = len; + CHECK(copy_string(it, copy, &n, NULL) == CborNoError); + CHECK(n == len && memcmp(copy, buffer, len) == 0); + if (len) { + n = len - 1; + CHECK(copy_string(it, copy, &n, NULL) == CborErrorOutOfMemory); + } + free(copy); + + /* iterate over the chunks and compare them to the dup'ed string */ + err = cbor_value_begin_string_iteration(&chunks); + total = 0; + while (!err) { + err = get_string_chunk(&chunks, &ptr, &n); + if (err == CborErrorNoMoreStringChunks) { + err = cbor_value_finish_string_iteration(&chunks); + break; + } + if (!err) { + CHECK(n <= len - total); + CHECK(memcmp(buffer + total, ptr, n) == 0); + total += n; + } + } + CHECK(err == CborNoError); + CHECK(total == len); + CHECK(cbor_value_get_next_byte(&chunks) == cbor_value_get_next_byte(&next)); + + if (text && memchr(buffer, '\0', len) == NULL) { + CHECK(cbor_value_text_string_equals(it, (const char *)buffer, &equals) == CborNoError); + CHECK(equals); + if (len) { + buffer[len - 1] = '\0'; + CHECK(cbor_value_text_string_equals(it, (const char *)buffer, &equals) == CborNoError); + CHECK(!equals); + } + } + + free(buffer); + *it = next; + return CborNoError; +} + +/* Reads a string when using the reader API: the source can't be rewound, so + * the string must be read exactly once. */ +static CborError parse_string_reader(struct Context *ctx, CborValue *it) +{ + CborError err; + const void *ptr; + size_t n; + size_t len = 0; + size_t total = 0; + bool length_known = cbor_value_is_length_known(it); + + if (length_known) { + err = cbor_value_get_string_length(it, &len); + if (err) + return err; + } + + if (ctx->reader_advance) { + /* the buffer is as large as the whole input, so it's big enough */ + n = ctx->buffer_size; + err = copy_string(it, ctx->buffer, &n, it); + if (err) + return err; + CHECK(!length_known || n == len); + sink += checksum(ctx->buffer, n); + return CborNoError; + } + + err = cbor_value_begin_string_iteration(it); + while (!err) { + err = get_string_chunk(it, &ptr, &n); + if (err == CborErrorNoMoreStringChunks) { + CHECK(!length_known || total == len); + return cbor_value_finish_string_iteration(it); + } + if (!err) { + sink += checksum(ptr, n); + total += n; + } + } + return err; +} + +static CborError parse_container(struct Context *ctx, CborValue *it, int nesting) +{ + CborError err; + CborValue recursed; + CborValue element; + size_t len; + + if (nesting == 0) + return CborErrorNestingTooDeep; + + if (cbor_value_is_length_known(it)) { + if (cbor_value_is_array(it)) + err = cbor_value_get_array_length(it, &len); + else + err = cbor_value_get_map_length(it, &len); + if (err) + return err; + } + + if (ctx->reader_advance) + return cbor_value_advance(it); + + if (!ctx->reader && cbor_value_is_map(it) && expensive_check(ctx)) { + /* the result is irrelevant, we only need the search to run */ + cbor_value_map_find_value(it, "a", &element); + } + + err = cbor_value_enter_container(it, &recursed); + if (err) + return err; + while (!cbor_value_at_end(&recursed)) { + err = parse_value(ctx, &recursed, nesting - 1); + if (err) + return err; + } + return cbor_value_leave_container(it, &recursed); +} + +/* Decodes the item at *it and advances past it */ +static CborError parse_value(struct Context *ctx, CborValue *it, int nesting) +{ + CborValue skipped; + CborTag tag; + uint8_t simple; + bool boolean; + + switch (cbor_value_get_type(it)) { + case CborIntegerType: + parse_integer(it); + return cbor_value_advance_fixed(it); + + case CborByteStringType: + case CborTextStringType: + return ctx->reader ? parse_string_reader(ctx, it) : parse_string(it); + + case CborArrayType: + case CborMapType: + return parse_container(ctx, it, nesting); + + case CborTagType: + cbor_value_get_tag(it, &tag); + sink += tag; + if (!ctx->reader && expensive_check(ctx)) { + skipped = *it; + if (cbor_value_skip_tag(&skipped) == CborNoError) + CHECK(!cbor_value_is_tag(&skipped)); + /* this skips the tags too */ + cbor_value_text_string_equals(it, "", &boolean); + } + /* the tagged item is visited by the caller as the next item */ + return cbor_value_advance_fixed(it); + + case CborSimpleType: + cbor_value_get_simple_type(it, &simple); + sink += simple; + return cbor_value_advance_fixed(it); + + case CborBooleanType: + cbor_value_get_boolean(it, &boolean); + sink += boolean; + return cbor_value_advance_fixed(it); + + case CborNullType: + case CborUndefinedType: + return cbor_value_advance_fixed(it); + + case CborHalfFloatType: + case CborFloatType: + case CborDoubleType: + parse_floating_point(it); + return cbor_value_advance_fixed(it); + + case CborInvalidType: + default: + /* preparsing never produces an invalid iterator without an error */ + abort(); + } +} + +static void parse_reader(struct Context *ctx, const uint8_t *data, size_t size, uint32_t config) +{ + struct ReaderInput input = { data, size, 0 }; + CborParser parser; + CborValue it; + CborError err; + + ctx->buffer_size = size + 1; + ctx->buffer = (uint8_t *)malloc(ctx->buffer_size); + CHECK(ctx->buffer); + + if (config & ConfigIncremental) + input.size = 0; + + while (input.consumed < size) { + size_t consumed = input.consumed; + memset(&it, 0, sizeof(it)); + err = cbor_parser_init_reader(&reader_ops, &parser, &it, &input); + while (err == CborErrorUnexpectedEOF && input.size < size) { + /* make one more byte available and try again */ + CHECK(input.consumed == consumed); + ++input.size; + err = cbor_value_reparse(&it); + } + input.size = size; + + while (!err && !cbor_value_at_end(&it)) + err = parse_value(ctx, &it, MAX_NESTING); + if (err || input.consumed == consumed) + break; + } + free(ctx->buffer); +} + +static void parse_buffer(struct Context *ctx, const uint8_t *data, size_t size) +{ + const uint8_t *ptr = data; + const uint8_t *end = data + size; + CborParser parser; + CborValue it; + CborValue advanced; + CborError err; + CborError advanced_err; + + /* the input is a sequence of items (RFC 8742) */ + while (ptr < end) { + err = cbor_parser_init(ptr, (size_t)(end - ptr), 0, &parser, &it); + while (!err && !cbor_value_at_end(&it)) { + /* reference result to compare with the item-by-item traversal */ + advanced = it; + advanced_err = cbor_value_advance(&advanced); + err = parse_value(ctx, &it, MAX_NESTING); + if (!err && !advanced_err) + CHECK(cbor_value_get_next_byte(&it) == cbor_value_get_next_byte(&advanced)); + } + if (err) + break; + CHECK(cbor_value_get_next_byte(&it) > ptr && cbor_value_get_next_byte(&it) <= end); + ptr = cbor_value_get_next_byte(&it); + } +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + struct Context ctx; + uint32_t config; + + if (size < sizeof(config)) + return 0; + memcpy(&config, data, sizeof(config)); + data += sizeof(config); + size -= sizeof(config); + + memset(&ctx, 0, sizeof(ctx)); + ctx.expensive_checks = MAX_EXPENSIVE_CHECKS; + if (config & ConfigUseReader) { + ctx.reader = true; + ctx.reader_advance = (config & ConfigReaderAdvance) != 0; + parse_reader(&ctx, data, size, config); + } else { + parse_buffer(&ctx, data, size); + } + return 0; +} diff --git a/fuzz/pretty_fuzzer.c b/fuzz/pretty_fuzzer.c new file mode 100644 index 00000000..92e7502b --- /dev/null +++ b/fuzz/pretty_fuzzer.c @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2026 Topogamer0 +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +/* + * Fuzzes the conversion of CBOR to diagnostic notation, both with + * cbor_value_to_pretty_advance_flags() (writing to a FILE *) and with + * cbor_value_to_pretty_stream() (writing through a callback). + * + * The first 4 bytes of the input are a configuration word whose low bits are + * the CborPrettyFlags to use (see FuzzConfig); the rest is the CBOR payload, + * which is converted as a sequence of items. The callback can be made to fail + * after a number of calls, to exercise the error handling. + */ + +#include "cbor.h" + +#include +#include +#include +#include + +#ifdef NDEBUG +# error "The fuzzers must be built without NDEBUG: TinyCBOR's assertions check the API usage" +#endif + +enum FuzzConfig { + PrettyFlagsMask = CborPrettyNumericEncodingIndicators | CborPrettyIndicateIndeterminateLength | + CborPrettyIndicateOverlongNumbers | CborPrettyShowStringFragments, + + /* if not zero, the callback fails on that call (bits 16 to 23) */ + ConfigFailShift = 16, + ConfigFailMask = 0xff, + + /* use cbor_value_to_pretty_stream() instead of a FILE * */ + ConfigUseStream = 0x80000000U +}; + +struct Output +{ + char buffer[256]; + size_t total; + unsigned calls_until_failure; +}; + +static CborError stream_function(void *token, const char *fmt, ...) +{ + struct Output *output = (struct Output *)token; + va_list ap; + int n; + + if (output->calls_until_failure && --output->calls_until_failure == 0) + return CborErrorIO; + + va_start(ap, fmt); + n = vsnprintf(output->buffer, sizeof(output->buffer), fmt, ap); + va_end(ap); + if (n < 0) + return CborErrorIO; + output->total += (size_t)n; + return CborNoError; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + static FILE *out; + struct Output output = { { 0 }, 0, 0 }; + CborParser parser; + CborValue it; + CborError err; + uint32_t config; + int flags; + const uint8_t *ptr; + const uint8_t *end; + + if (!out) + out = fopen("/dev/null", "w"); + if (!out || size < sizeof(config)) + return 0; + memcpy(&config, data, sizeof(config)); + data += sizeof(config); + size -= sizeof(config); + end = data + size; + flags = (int)(config & PrettyFlagsMask); + output.calls_until_failure = (config >> ConfigFailShift) & ConfigFailMask; + + ptr = data; + while (ptr < end) { + err = cbor_parser_init(ptr, (size_t)(end - ptr), 0, &parser, &it); + if (!err) { + if (config & ConfigUseStream) + err = cbor_value_to_pretty_stream(stream_function, &output, &it, flags); + else + err = cbor_value_to_pretty_advance_flags(out, &it, flags); + } + if (err || cbor_value_get_next_byte(&it) <= ptr) + break; + ptr = cbor_value_get_next_byte(&it); + } + return 0; +} diff --git a/fuzz/tojson_fuzzer.c b/fuzz/tojson_fuzzer.c new file mode 100644 index 00000000..a371fb2d --- /dev/null +++ b/fuzz/tojson_fuzzer.c @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2026 Topogamer0 +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +/* + * Fuzzes the conversion of CBOR to JSON with cbor_value_to_json_advance(). + * + * The first 4 bytes of the input are a configuration word whose low bits are + * the CborToJsonFlags to use; the rest is the CBOR payload, which is + * converted as a sequence of items. Where available (glibc), the output + * stream can be made to fail after a number of bytes, to exercise the error + * handling. + */ + +#define _GNU_SOURCE 1 /* for fopencookie() */ + +#include "cbor.h" +#include "cborjson.h" + +#include +#include +#include + +#ifdef NDEBUG +# error "The fuzzers must be built without NDEBUG: TinyCBOR's assertions check the API usage" +#endif + +enum FuzzConfig { + JsonFlagsMask = CborConvertAddMetadata | CborConvertTagsToObjects | + CborConvertByteStringsToBase64Url | CborConvertStringifyMapKeys, + + /* if not zero, writing fails after this many bytes (bits 16 to 31) */ + ConfigFailShift = 16 +}; + +#ifdef __GLIBC__ +static ssize_t failing_write(void *cookie, const char *buf, size_t size) +{ + size_t *bytes_until_failure = (size_t *)cookie; + (void)buf; + if (size > *bytes_until_failure) { + *bytes_until_failure = 0; + return -1; + } + *bytes_until_failure -= size; + return (ssize_t)size; +} + +static FILE *open_failing_stream(size_t *bytes_until_failure) +{ + cookie_io_functions_t functions = { NULL, failing_write, NULL, NULL }; + FILE *f = fopencookie(bytes_until_failure, "w", functions); + if (f) + setvbuf(f, NULL, _IONBF, 0); /* fail as soon as possible */ + return f; +} +#endif + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + static FILE *devnull; + FILE *out; + CborParser parser; + CborValue it; + CborError err; + uint32_t config; + size_t bytes_until_failure; + const uint8_t *ptr; + const uint8_t *end; + + if (!devnull) + devnull = fopen("/dev/null", "w"); + if (!devnull || size < sizeof(config)) + return 0; + memcpy(&config, data, sizeof(config)); + data += sizeof(config); + size -= sizeof(config); + end = data + size; + + out = devnull; + bytes_until_failure = config >> ConfigFailShift; +#ifdef __GLIBC__ + if (bytes_until_failure) { + out = open_failing_stream(&bytes_until_failure); + if (!out) + return 0; + } +#endif + + ptr = data; + while (ptr < end) { + err = cbor_parser_init(ptr, (size_t)(end - ptr), 0, &parser, &it); + if (!err) + err = cbor_value_to_json_advance(out, &it, (int)(config & JsonFlagsMask)); + if (err || cbor_value_get_next_byte(&it) <= ptr) + break; + ptr = cbor_value_get_next_byte(&it); + } + + if (out != devnull) + fclose(out); + return 0; +} diff --git a/fuzz/validate_fuzzer.c b/fuzz/validate_fuzzer.c new file mode 100644 index 00000000..0241afda --- /dev/null +++ b/fuzz/validate_fuzzer.c @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2026 Topogamer0 +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +/* + * Fuzzes cbor_value_validate() with every combination of validation flags. + * + * The first 4 bytes of the input are the CborValidationFlags to use; the rest + * is the CBOR payload. + */ + +#include "cbor.h" + +#include +#include +#include + +#ifdef NDEBUG +# error "The fuzzers must be built without NDEBUG: TinyCBOR's assertions check the API usage" +#endif + +/* Abort on a result that contradicts the documented behavior */ +#define CHECK(cond) do { if (!(cond)) abort(); } while (0) + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + CborParser parser; + CborValue it; + CborValue copy; + CborError err; + CborError basic_err; + uint32_t flags; + + if (size < sizeof(flags)) + return 0; + memcpy(&flags, data, sizeof(flags)); + data += sizeof(flags); + size -= sizeof(flags); + + if (cbor_parser_init(data, size, 0, &parser, &it) != CborNoError) + return 0; + + err = cbor_value_validate(&it, flags); + basic_err = cbor_value_validate_basic(&it); + + /* "If flags is CborValidateBasic, the result should be the same as + * cbor_value_validate_basic()". That one advances over a tag without + * looking at the tagged item, so only compare for other types. The two + * also count nesting differently towards CBOR_PARSER_MAX_RECURSIONS. */ + if (flags == CborValidateBasic && !cbor_value_is_tag(&it) && + err != CborErrorNestingTooDeep && basic_err != CborErrorNestingTooDeep) + CHECK((err == CborNoError) == (basic_err == CborNoError)); + + /* "If no error was found, it returns CborNoError and the application can + * iterate over the items with certainty that no errors will appear + * during parsing" */ + if (err == CborNoError) { + CHECK(basic_err == CborNoError); + /* cbor_value_advance() over a tag stops at the tagged item */ + copy = it; + CHECK(cbor_value_skip_tag(©) == CborNoError); + CHECK(cbor_value_advance(©) == CborNoError); + if (flags & CborValidateCompleteData) + CHECK(cbor_value_get_next_byte(©) == data + size); + } + return 0; +}