/* * Copyright (c) 2018-2021 Nicholas Fraser and the MPack authors * * 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. */ #ifdef MPACK_FUZZ /* * fuzz.c is a test program to assist with fuzzing MPack. It: * * - decodes stdin with the dynamic Reader API; * - encodes the data to a growable buffer with the Write API; * - parses the resulting buffer with the Node API; * - and finally, prints a debug dump of the node tree to stdout. * * It thus passes all data through three major components of MPack (but not * the Expect API.) */ #include "mpack/mpack.h" #ifndef MPACK_FUZZ_CONFIG_H #error "This should be built with fuzz-config.h as a prefix header." #endif static void print_callback(void* context, const char* data, size_t count) { fwrite(data, 1, count, stdout); } static void transfer_bytes(mpack_reader_t* reader, mpack_writer_t* writer, uint32_t count) { if (mpack_should_read_bytes_inplace(reader, count)) { const char* data = mpack_read_bytes_inplace(reader, count); if (mpack_reader_error(reader) == mpack_ok) mpack_write_bytes(writer, data, count); return; } while (count > 0) { char buffer[79]; uint32_t step = (count < sizeof(buffer)) ? count : sizeof(buffer); mpack_read_bytes(reader, buffer, step); if (mpack_reader_error(reader) != mpack_ok) return; mpack_write_bytes(writer, buffer, step); count -= step; } } static void transfer_element(mpack_reader_t* reader, mpack_writer_t* writer, int depth) { // We apply a depth limit manually right now to avoid a stack overflow. A // depth limit should probably be added to the reader and tree at some // point because even though the reader and tree can themselves handle // arbitrary depths, any dynamic use that doesn't account for this is // likely to be vulnerable to such stack overflows. if (depth >= 1024) { fprintf(stderr, "hit depth limit!\n"); mpack_reader_flag_error(reader, mpack_error_too_big); return; } ++depth; mpack_tag_t tag = mpack_read_tag(reader); if (mpack_reader_error(reader) != mpack_ok) { fprintf(stderr, "error reading tag!\n"); return; } /* static char describe_buffer[64]; mpack_tag_debug_describe(tag, describe_buffer, sizeof(describe_buffer)); printf("%s\n", describe_buffer); */ mpack_write_tag(writer, tag); switch (tag.type) { #if MPACK_EXTENSIONS case mpack_type_ext: // fallthrough #endif case mpack_type_str: // fallthrough case mpack_type_bin: transfer_bytes(reader, writer, mpack_tag_bytes(&tag)); if (mpack_reader_error(reader) != mpack_ok) return; mpack_done_type(reader, tag.type); mpack_finish_type(writer, tag.type); break; case mpack_type_map: for (uint32_t i = 0; i < mpack_tag_map_count(&tag); ++i) { transfer_element(reader, writer, depth); if (mpack_reader_error(reader) != mpack_ok) return; transfer_element(reader, writer, depth); if (mpack_reader_error(reader) != mpack_ok) return; } mpack_done_map(reader); mpack_finish_map(writer); break; case mpack_type_array: for (uint32_t i = 0; i < mpack_tag_array_count(&tag); ++i) { transfer_element(reader, writer, depth); if (mpack_reader_error(reader) != mpack_ok) return; } mpack_done_array(reader); mpack_finish_array(writer); break; default: break; } } int main(int argc, char** argv) { char* data; size_t size; mpack_writer_t writer; mpack_writer_init_growable(&writer, &data, &size); mpack_reader_t reader; mpack_reader_init_stdfile(&reader, stdin, false); transfer_element(&reader, &writer, 0); if (mpack_reader_destroy(&reader) != mpack_ok || mpack_writer_destroy(&writer) != mpack_ok) { fprintf(stderr, "error in reader or writer!\n"); return EXIT_FAILURE; } mpack_tree_t tree; mpack_tree_init_stdfile(&tree, stdin, 0, false); mpack_tree_parse(&tree); if (mpack_tree_error(&tree) != mpack_ok) { fprintf(stderr, "error parsing tree!\n"); return EXIT_FAILURE; } mpack_node_print_to_callback(mpack_tree_root(&tree), print_callback, NULL); if (mpack_tree_destroy(&tree) != mpack_ok) { fprintf(stderr, "error printing or destroying tree!\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } #else typedef int mpack_pedantic_allow_empty_translation_unit; #endif