Skip to content

Commit 76a1dcf

Browse files
FuzzTest Teamcopybara-github
authored andcommitted
Refactor Flatbuffers domain visitor dispatch.
This change introduces a helper for wrapping visitors with tag types and refactors `VisitFlatbufferField` to handle types within containers. It also improves enum type detection and error reporting. PiperOrigin-RevId: 918444646
1 parent 36a7acd commit 76a1dcf

6 files changed

Lines changed: 135 additions & 40 deletions

File tree

domain_tests/arbitrary_domains_flatbuffers_test.cc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@
3636
#include "./domain_tests/domain_testing.h"
3737
#include "./fuzztest/flatbuffers.h"
3838
#include "./fuzztest/internal/meta.h"
39+
#include "./fuzztest/internal/test_flatbuffers_64bits_generated.h"
3940
#include "./fuzztest/internal/test_flatbuffers_generated.h"
4041

4142
namespace fuzztest {
4243
namespace {
4344

4445
using ::fuzztest::internal::BoolTable;
4546
using ::fuzztest::internal::DefaultTable;
47+
using ::fuzztest::internal::DefaultTable64;
4648
using ::fuzztest::internal::OptionalTable;
4749
using ::fuzztest::internal::RecursiveTable;
4850
using ::fuzztest::internal::RequiredTable;
@@ -592,5 +594,29 @@ TEST(FlatbuffersTableDomainImplTest, RecursiveTable) {
592594
ASSERT_THAT(new_table, IsNull());
593595
}
594596

597+
TEST(FlatbuffersTableDomainImplTest, DefaultTable64ValueRoundTrip) {
598+
flatbuffers::FlatBufferBuilder64 fbb;
599+
auto str_offset = fbb.CreateString<flatbuffers::Offset64>("foo bar baz");
600+
auto table_offset = internal::CreateDefaultTable64(fbb, str_offset);
601+
fbb.Finish(table_offset);
602+
auto table = flatbuffers::GetRoot<DefaultTable64>(fbb.GetBufferPointer());
603+
604+
auto domain = Arbitrary<const DefaultTable64*>();
605+
auto corpus = domain.FromValue(table);
606+
ASSERT_TRUE(corpus.has_value());
607+
ASSERT_OK(domain.ValidateCorpusValue(*corpus));
608+
609+
auto ir = domain.SerializeCorpus(corpus.value());
610+
611+
auto new_corpus = domain.ParseCorpus(ir);
612+
ASSERT_TRUE(new_corpus.has_value());
613+
ASSERT_OK(domain.ValidateCorpusValue(*new_corpus));
614+
615+
auto new_table = domain.GetValue(*new_corpus);
616+
ASSERT_THAT(new_table, NotNull());
617+
ASSERT_THAT(new_table->str(), NotNull());
618+
EXPECT_EQ(new_table->str()->str(), "foo bar baz");
619+
}
620+
595621
} // namespace
596622
} // namespace fuzztest

fuzztest/internal/BUILD

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,8 +616,13 @@ cc_test(
616616

617617
flatbuffer_library_public(
618618
name = "test_flatbuffers_fbs",
619-
srcs = ["test_flatbuffers.fbs"],
619+
srcs = [
620+
"test_flatbuffers.fbs",
621+
"test_flatbuffers_64bits.fbs",
622+
],
620623
outs = [
624+
"test_flatbuffers_64bits_bfbs_generated.h",
625+
"test_flatbuffers_64bits_generated.h",
621626
"test_flatbuffers_bfbs_generated.h",
622627
"test_flatbuffers_generated.h",
623628
],

fuzztest/internal/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ if (FUZZTEST_BUILD_FLATBUFFERS)
574574
test_flatbuffers_headers
575575
SCHEMAS
576576
"test_flatbuffers.fbs"
577+
"test_flatbuffers_64bits.fbs"
577578
FLAGS
578579
--bfbs-gen-embed --gen-name-strings
579580
TESTONLY

fuzztest/internal/domains/flatbuffers_domain_impl.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,11 @@ bool FlatbuffersTableUntypedDomainImpl::IsSupportedField(
276276
}
277277

278278
uint32_t FlatbuffersTableUntypedDomainImpl::BuildTable(
279-
const corpus_type& value, flatbuffers::FlatBufferBuilder& builder) const {
279+
const corpus_type& value, flatbuffers::FlatBufferBuilder64& builder) const {
280280
// Add all the fields to the builder.
281281

282282
// Offsets is the map of field id to its offset in the table.
283-
absl::flat_hash_map<typename corpus_type::key_type, flatbuffers::uoffset_t>
283+
absl::flat_hash_map<typename corpus_type::key_type, flatbuffers::uoffset64_t>
284284
offsets;
285285

286286
// Some fields are stored inline in the flatbuffer table itself (a.k.a

fuzztest/internal/domains/flatbuffers_domain_impl.h

Lines changed: 79 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
#include "absl/strings/str_format.h"
3838
#include "absl/synchronization/mutex.h"
3939
#include "flatbuffers/base.h"
40+
#include "flatbuffers/buffer.h"
4041
#include "flatbuffers/flatbuffer_builder.h"
42+
#include "flatbuffers/reflection.h"
4143
#include "flatbuffers/reflection_generated.h"
4244
#include "flatbuffers/string.h"
4345
#include "flatbuffers/table.h"
@@ -82,67 +84,80 @@ struct FlatbuffersStructTag;
8284
struct FlatbuffersUnionTag;
8385
struct FlatbuffersVectorTag;
8486

87+
// Helper to wrap the visitor with the correct tag type.
88+
template <template <typename> typename Wrapper, typename Visitor>
89+
struct VisitorTagWrapper {
90+
Visitor&& visitor;
91+
template <typename T>
92+
void Visit(const reflection::Field* absl_nonnull field) const {
93+
std::forward<Visitor>(visitor).template Visit<Wrapper<T>>(field);
94+
}
95+
};
96+
8597
// Dynamic to static dispatch visitor pattern.
86-
template <typename Visitor>
87-
auto VisitFlatbufferField(const reflection::Schema* absl_nonnull schema,
98+
template <typename Visitor, bool in_container = false>
99+
void VisitFlatbufferField(const reflection::Schema* absl_nonnull schema,
88100
const reflection::Field* absl_nonnull field,
89-
Visitor visitor) {
90-
auto field_index = field->type()->index();
91-
switch (field->type()->base_type()) {
101+
Visitor&& visitor) {
102+
const auto type =
103+
in_container ? field->type()->element() : field->type()->base_type();
104+
const auto field_index = field->type()->index();
105+
const bool is_enum = flatbuffers::IsInteger(type) && field_index >= 0;
106+
switch (type) {
92107
case reflection::BaseType::Bool:
93108
visitor.template Visit<bool>(field);
94109
break;
95110
case reflection::BaseType::Byte:
96-
if (field_index >= 0) {
111+
if (is_enum) {
97112
visitor.template Visit<FlatbuffersEnumTag<int8_t>>(field);
98113
} else {
99114
visitor.template Visit<int8_t>(field);
100115
}
101116
break;
102117
case reflection::BaseType::Short:
103-
if (field_index >= 0) {
118+
if (is_enum) {
104119
visitor.template Visit<FlatbuffersEnumTag<int16_t>>(field);
105120
} else {
106121
visitor.template Visit<int16_t>(field);
107122
}
108123
break;
109124
case reflection::BaseType::Int:
110-
if (field_index >= 0) {
125+
if (is_enum) {
111126
visitor.template Visit<FlatbuffersEnumTag<int32_t>>(field);
112127
} else {
113128
visitor.template Visit<int32_t>(field);
114129
}
115130
break;
116131
case reflection::BaseType::Long:
117-
if (field_index >= 0) {
132+
if (is_enum) {
118133
visitor.template Visit<FlatbuffersEnumTag<int64_t>>(field);
119134
} else {
120135
visitor.template Visit<int64_t>(field);
121136
}
122137
break;
123138
case reflection::BaseType::UByte:
124-
if (field_index >= 0) {
139+
if (is_enum) {
125140
visitor.template Visit<FlatbuffersEnumTag<uint8_t>>(field);
126141
} else {
127142
visitor.template Visit<uint8_t>(field);
128143
}
129144
break;
130145
case reflection::BaseType::UShort:
131-
if (field_index >= 0) {
146+
if (is_enum) {
132147
visitor.template Visit<FlatbuffersEnumTag<uint16_t>>(field);
133148
} else {
134149
visitor.template Visit<uint16_t>(field);
135150
}
136151
break;
137152
case reflection::BaseType::UInt:
138-
if (field_index >= 0) {
153+
if (is_enum) {
139154
visitor.template Visit<FlatbuffersEnumTag<uint32_t>>(field);
140155
} else {
141156
visitor.template Visit<uint32_t>(field);
142157
}
143158
break;
144159
case reflection::BaseType::ULong:
145-
if (field_index >= 0) {
160+
if (is_enum) {
146161
visitor.template Visit<FlatbuffersEnumTag<uint64_t>>(field);
147162
} else {
148163
visitor.template Visit<uint64_t>(field);
@@ -159,29 +174,36 @@ auto VisitFlatbufferField(const reflection::Schema* absl_nonnull schema,
159174
break;
160175
case reflection::BaseType::Vector:
161176
case reflection::BaseType::Vector64:
162-
visitor.template Visit<FlatbuffersVectorTag>(field);
177+
if constexpr (in_container) {
178+
FUZZTEST_LOG(FATAL) << "Nested containers are not supported.";
179+
} else {
180+
visitor.template Visit<FlatbuffersVectorTag>(field);
181+
}
163182
break;
164183
case reflection::BaseType::Array:
165-
visitor.template Visit<FlatbuffersArrayTag>(field);
184+
if constexpr (in_container) {
185+
FUZZTEST_LOG(FATAL) << "Nested containers are not supported.";
186+
} else {
187+
visitor.template Visit<FlatbuffersArrayTag>(field);
188+
}
166189
break;
167-
case reflection::BaseType::Obj: {
168-
auto sub_object = schema->objects()->Get(field->type()->index());
169-
if (sub_object->is_struct()) {
190+
case reflection::BaseType::Obj:
191+
if (schema->objects()->Get(field_index)->is_struct()) {
170192
visitor.template Visit<FlatbuffersStructTag>(field);
171193
} else {
172194
visitor.template Visit<FlatbuffersTableTag>(field);
173195
}
174196
break;
175-
}
176197
case reflection::BaseType::Union:
177198
visitor.template Visit<FlatbuffersUnionTag>(field);
178199
break;
179200
case reflection::BaseType::UType:
180-
// Noop
201+
// Noop: Union type fields are handled when processing their
202+
// corresponding union field
181203
break;
182204
default:
183205
FUZZTEST_LOG(FATAL) << "Unsupported base type: "
184-
<< field->type()->base_type();
206+
<< reflection::EnumNameBaseType(type);
185207
}
186208
}
187209

@@ -365,7 +387,7 @@ class FlatbuffersTableUntypedDomainImpl
365387
bool IsSupportedField(const reflection::Field* absl_nonnull field) const;
366388

367389
uint32_t BuildTable(const corpus_type& value,
368-
flatbuffers::FlatBufferBuilder& builder) const;
390+
flatbuffers::FlatBufferBuilder64& builder) const;
369391

370392
// Returns the domain for the given field.
371393
// The domain is cached, and the same instance is returned for the same field.
@@ -441,9 +463,15 @@ class FlatbuffersTableUntypedDomainImpl
441463
}
442464
} else if constexpr (std::is_same_v<T, std::string>) {
443465
if (user_value->CheckField(field->offset())) {
444-
inner_value = std::optional(
445-
user_value->GetPointer<flatbuffers::String*>(field->offset())
446-
->str());
466+
if (field->offset64()) {
467+
inner_value = std::optional(
468+
user_value->GetPointer64<flatbuffers::String*>(field->offset())
469+
->str());
470+
} else {
471+
inner_value = std::optional(
472+
user_value->GetPointer<flatbuffers::String*>(field->offset())
473+
->str());
474+
}
447475
}
448476
} else if constexpr (std::is_same_v<T, FlatbuffersTableTag>) {
449477
auto sub_object = self.schema_->objects()->Get(field->type()->index());
@@ -464,9 +492,9 @@ class FlatbuffersTableUntypedDomainImpl
464492
// Create out-of-line table fields, see `BuildTable` for details.
465493
struct TableFieldBuilderVisitor {
466494
const FlatbuffersTableUntypedDomainImpl& self;
467-
flatbuffers::FlatBufferBuilder& builder;
468-
absl::flat_hash_map<typename corpus_type::key_type, flatbuffers::uoffset_t>&
469-
offsets;
495+
flatbuffers::FlatBufferBuilder64& builder;
496+
absl::flat_hash_map<typename corpus_type::key_type,
497+
flatbuffers::uoffset64_t>& offsets;
470498
const typename corpus_type::mapped_type& corpus_value;
471499

472500
template <typename T>
@@ -475,8 +503,16 @@ class FlatbuffersTableUntypedDomainImpl
475503
auto& domain = self.GetCachedDomain<T>(field);
476504
auto user_value = domain.GetValue(corpus_value);
477505
if (user_value.has_value()) {
478-
auto offset =
479-
builder.CreateString(user_value->data(), user_value->size()).o;
506+
flatbuffers::uoffset64_t offset;
507+
if (field->offset64()) {
508+
offset = builder
509+
.CreateString<flatbuffers::Offset64>(
510+
user_value->data(), user_value->size())
511+
.o;
512+
} else {
513+
offset =
514+
builder.CreateString(user_value->data(), user_value->size()).o;
515+
}
480516
offsets.insert({field->id(), offset});
481517
}
482518
} else if constexpr (std::is_same_v<T, FlatbuffersTableTag>) {
@@ -502,9 +538,9 @@ class FlatbuffersTableUntypedDomainImpl
502538
// offsets for "out-of-line fields". See `BuildTable` for details.
503539
struct TableBuilderVisitor {
504540
const FlatbuffersTableUntypedDomainImpl& self;
505-
flatbuffers::FlatBufferBuilder& builder;
506-
const absl::flat_hash_map<typename corpus_type::key_type,
507-
flatbuffers::uoffset_t>& offsets;
541+
flatbuffers::FlatBufferBuilder64& builder;
542+
absl::flat_hash_map<typename corpus_type::key_type,
543+
flatbuffers::uoffset64_t>& offsets;
508544
const typename corpus_type::value_type::second_type& corpus_value;
509545

510546
template <typename T>
@@ -521,9 +557,15 @@ class FlatbuffersTableUntypedDomainImpl
521557
} else if constexpr (std::is_same_v<T, std::string>) {
522558
// "Out-of-line field". Store just offset.
523559
if (auto it = offsets.find(field->id()); it != offsets.end()) {
524-
builder.AddOffset(
525-
field->offset(),
526-
flatbuffers::Offset<flatbuffers::String>(it->second));
560+
if (field->offset64()) {
561+
builder.AddOffset(
562+
field->offset(),
563+
flatbuffers::Offset64<flatbuffers::String>(it->second));
564+
} else {
565+
builder.AddOffset(
566+
field->offset(),
567+
flatbuffers::Offset<flatbuffers::String>(it->second));
568+
}
527569
}
528570
} else if constexpr (std::is_same_v<T, FlatbuffersTableTag>) {
529571
// "Out-of-line field". Store just offset.
@@ -753,7 +795,7 @@ class FlatbuffersTableDomainImpl
753795

754796
// Converts corpus value into the exact flatbuffer.
755797
value_type GetValue(const corpus_type& value) const {
756-
flatbuffers::FlatBufferBuilder builder;
798+
flatbuffers::FlatBufferBuilder64 builder;
757799
const uint32_t offset = inner_->BuildTable(value.untyped_corpus, builder);
758800
builder.Finish(flatbuffers::Offset<flatbuffers::Table>(offset));
759801
value.buffer =
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace fuzztest.internal;
16+
17+
table DefaultTable64 {
18+
str:string (offset64);
19+
}
20+
21+
root_type DefaultTable64;

0 commit comments

Comments
 (0)