// Protocol Buffers - Google's data interchange format // Copyright 2023 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd #include "upb/test/fuzz_util.h" #include #include #include "upb/base/descriptor_constants.h" #include "upb/base/status.hpp" #include "upb/mem/arena.h" #include "upb/mini_descriptor/decode.h" #include "upb/mini_table/enum.h" #include "upb/mini_table/extension.h" #include "upb/mini_table/extension_registry.h" #include "upb/mini_table/field.h" #include "upb/mini_table/message.h" #include "upb/mini_table/sub.h" // Must be last #include "upb/port/def.inc" 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); if (!LinkMessages()) return nullptr; return mini_tables_.empty() ? nullptr : mini_tables_.front(); } private: void BuildMessages(); void BuildEnums(); void BuildExtensions(upb_ExtensionRegistry** exts); bool LinkExtension(upb_MiniTableExtension* ext); bool 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_MiniTableEnum* 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 = upb_MiniTable_Build(d.data(), d.size(), 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_MiniTableEnum* enum_table = upb_MiniTableEnum_Build(d.data(), d.size(), arena_, status.ptr()); if (enum_table) enum_tables_.push_back(enum_table); } } bool Builder::LinkExtension(upb_MiniTableExtension* ext) { upb_MiniTableField* field = &ext->UPB_PRIVATE(field); if (upb_MiniTableField_CType(field) == kUpb_CType_Message) { auto mt = NextMiniTable(); if (!mt) field->UPB_PRIVATE(descriptortype) = kUpb_FieldType_Int32; ext->UPB_PRIVATE(sub) = upb_MiniTableSub_FromMessage(mt); } if (upb_MiniTableField_IsClosedEnum(field)) { auto et = NextEnumTable(); if (!et) field->UPB_PRIVATE(descriptortype) = kUpb_FieldType_Int32; ext->UPB_PRIVATE(sub) = upb_MiniTableSub_FromEnum(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_MiniTableExtension* ext = reinterpret_cast( upb_Arena_Malloc(arena_, sizeof(*ext))); upb_MiniTableSub sub; const upb_MiniTable* extendee = NextMiniTable(); if (!extendee) break; ptr = upb_MiniTableExtension_Init(ptr, end - ptr, ext, extendee, sub, status.ptr()); if (!ptr) break; if (!LinkExtension(ext)) continue; if (upb_ExtensionRegistry_Lookup(*exts, ext->UPB_PRIVATE(extendee), upb_MiniTableExtension_Number(ext))) continue; upb_ExtensionRegistry_AddArray( *exts, const_cast(&ext), 1); } } } bool 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->UPB_PRIVATE(field_count); i++) { upb_MiniTableField* field = const_cast(&table->UPB_PRIVATE(fields)[i]); if (link_ == input_->links.size()) link_ = 0; if (upb_MiniTableField_CType(field) == kUpb_CType_Message && !upb_MiniTable_SetSubMessage(table, field, NextMiniTable())) { return false; } if (upb_MiniTableField_IsClosedEnum(field)) { auto* et = NextEnumTable(); if (et) { if (!upb_MiniTable_SetSubEnum(table, field, et)) return false; } else { // We don't have any sub-enums. Override the field type so that it is // not needed. field->UPB_PRIVATE(descriptortype) = kUpb_FieldType_Int32; } } } } return true; } } // 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