/* * Copyright (c) 2009-2022, Google LLC * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Google LLC nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "upb/fuzz_test_util.h" #include "upb/msg.h" #include "upb/upb.hpp" namespace upb { namespace fuzz { namespace { class Builder { public: Builder(const MiniTableFuzzInput& input, upb_Arena* arena) : input_(&input), arena_(arena) {} const upb_MiniTable* Build(upb_ExtensionRegistry** exts) { BuildMessages(); BuildEnums(); BuildExtensions(exts); LinkMessages(); return mini_tables_.empty() ? nullptr : mini_tables_.front(); } private: void BuildMessages(); void BuildEnums(); void BuildExtensions(upb_ExtensionRegistry** exts); bool LinkExtension(upb_MiniTable_Extension* ext); void LinkMessages(); size_t NextLink() { if (input_->links.empty()) return 0; if (link_ == input_->links.size()) link_ = 0; return input_->links[link_++]; } const upb_MiniTable* NextMiniTable() { return mini_tables_.empty() ? nullptr : mini_tables_[NextLink() % mini_tables_.size()]; } const upb_MiniTable_Enum* NextEnumTable() { return enum_tables_.empty() ? nullptr : enum_tables_[NextLink() % enum_tables_.size()]; } const MiniTableFuzzInput* input_; upb_Arena* arena_; std::vector mini_tables_; std::vector enum_tables_; size_t link_ = 0; }; void Builder::BuildMessages() { upb::Status status; mini_tables_.reserve(input_->mini_descriptors.size()); for (const auto& d : input_->mini_descriptors) { upb_MiniTable* table; if (d == "\n") { // We special-case this input string, which is not a valid // mini-descriptor, to mean message set. table = upb_MiniTable_BuildMessageSet(kUpb_MiniTablePlatform_Native, arena_); } else { table = upb_MiniTable_Build(d.data(), d.size(), kUpb_MiniTablePlatform_Native, arena_, status.ptr()); } if (table) mini_tables_.push_back(table); } } void Builder::BuildEnums() { upb::Status status; enum_tables_.reserve(input_->enum_mini_descriptors.size()); for (const auto& d : input_->enum_mini_descriptors) { upb_MiniTable_Enum* enum_table = upb_MiniTable_BuildEnum(d.data(), d.size(), arena_, status.ptr()); if (enum_table) enum_tables_.push_back(enum_table); } } bool Builder::LinkExtension(upb_MiniTable_Extension* ext) { upb_MiniTable_Field* field = &ext->field; ext->extendee = NextMiniTable(); if (!ext->extendee) return false; if (field->descriptortype == kUpb_FieldType_Message || field->descriptortype == kUpb_FieldType_Group) { auto mt = NextMiniTable(); if (!mt) field->descriptortype = kUpb_FieldType_Int32; ext->sub.submsg = mt; } if (field->descriptortype == kUpb_FieldType_Enum) { auto et = NextEnumTable(); if (!et) field->descriptortype = kUpb_FieldType_Int32; ext->sub.subenum = et; } return true; } void Builder::BuildExtensions(upb_ExtensionRegistry** exts) { upb::Status status; if (input_->extensions.empty()) { *exts = nullptr; } else { *exts = upb_ExtensionRegistry_New(arena_); const char* ptr = input_->extensions.data(); const char* end = ptr + input_->extensions.size(); // Iterate through the buffer, building extensions as long as we can. while (ptr < end) { upb_MiniTable_Extension* ext = reinterpret_cast( upb_Arena_Malloc(arena_, sizeof(*ext))); upb_MiniTable_Sub sub; ptr = upb_MiniTable_BuildExtension(ptr, end - ptr, ext, sub, status.ptr()); if (!ptr) break; if (!LinkExtension(ext)) continue; if (_upb_extreg_get(*exts, ext->extendee, ext->field.number)) continue; _upb_extreg_add(*exts, const_cast(&ext), 1); } } } void Builder::LinkMessages() { for (auto* t : mini_tables_) { upb_MiniTable* table = const_cast(t); // For each field that requires a sub-table, assign one as appropriate. for (size_t i = 0; i < table->field_count; i++) { upb_MiniTable_Field* field = const_cast(&table->fields[i]); if (link_ == input_->links.size()) link_ = 0; if (field->descriptortype == kUpb_FieldType_Message || field->descriptortype == kUpb_FieldType_Group) { upb_MiniTable_SetSubMessage(table, field, NextMiniTable()); } if (field->descriptortype == kUpb_FieldType_Enum) { auto* et = NextEnumTable(); if (et) { upb_MiniTable_SetSubEnum(table, field, et); } else { // We don't have any sub-enums. Override the field type so that it is // not needed. field->descriptortype = kUpb_FieldType_Int32; } } } } } } // namespace const upb_MiniTable* BuildMiniTable(const MiniTableFuzzInput& input, upb_ExtensionRegistry** exts, upb_Arena* arena) { Builder builder(input, arena); return builder.Build(exts); } } // namespace fuzz } // namespace upb