1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include "google/protobuf/feature_resolver.h"
9
10 #include <algorithm>
11 #include <cstddef>
12 #include <iterator>
13 #include <memory>
14 #include <string>
15 #include <utility>
16 #include <vector>
17
18 #include "absl/algorithm/container.h"
19 #include "absl/container/btree_set.h"
20 #include "absl/container/flat_hash_set.h"
21 #include "absl/log/absl_check.h"
22 #include "absl/memory/memory.h"
23 #include "absl/status/status.h"
24 #include "absl/status/statusor.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/strings/str_join.h"
27 #include "absl/strings/str_split.h"
28 #include "absl/strings/string_view.h"
29 #include "absl/types/span.h"
30 #include "google/protobuf/cpp_features.pb.h"
31 #include "google/protobuf/descriptor.h"
32 #include "google/protobuf/descriptor.pb.h"
33 #include "google/protobuf/dynamic_message.h"
34 #include "google/protobuf/message.h"
35 #include "google/protobuf/reflection_ops.h"
36 #include "google/protobuf/text_format.h"
37
38 // Must be included last.
39 #include "google/protobuf/port_def.inc"
40
41 #define RETURN_IF_ERROR(expr) \
42 do { \
43 const absl::Status _status = (expr); \
44 if (PROTOBUF_PREDICT_FALSE(!_status.ok())) return _status; \
45 } while (0)
46
47 namespace google {
48 namespace protobuf {
49 namespace {
50
51 template <typename... Args>
Error(Args...args)52 absl::Status Error(Args... args) {
53 return absl::FailedPreconditionError(absl::StrCat(args...));
54 }
55
ValidateFeatureSupport(const FieldOptions::FeatureSupport & support,absl::string_view full_name)56 absl::Status ValidateFeatureSupport(const FieldOptions::FeatureSupport& support,
57 absl::string_view full_name) {
58 if (support.has_edition_deprecated()) {
59 if (support.edition_deprecated() < support.edition_introduced()) {
60 return Error("Feature ", full_name,
61 " was deprecated before it was introduced.");
62 }
63 if (!support.has_deprecation_warning()) {
64 return Error(
65 "Feature ", full_name,
66 " is deprecated but does not specify a deprecation warning.");
67 }
68 }
69 if (!support.has_edition_deprecated() && support.has_deprecation_warning()) {
70 return Error("Feature ", full_name,
71 " specifies a deprecation warning but is not marked "
72 "deprecated in any edition.");
73 }
74 if (support.has_edition_removed()) {
75 if (support.edition_deprecated() >= support.edition_removed()) {
76 return Error("Feature ", full_name,
77 " was deprecated after it was removed.");
78 }
79 if (support.edition_removed() < support.edition_introduced()) {
80 return Error("Feature ", full_name,
81 " was removed before it was introduced.");
82 }
83 }
84
85 return absl::OkStatus();
86 }
87
ValidateFieldFeatureSupport(const FieldDescriptor & field)88 absl::Status ValidateFieldFeatureSupport(const FieldDescriptor& field) {
89 if (!field.options().has_feature_support()) {
90 return Error("Feature field ", field.full_name(),
91 " has no feature support specified.");
92 }
93
94 const FieldOptions::FeatureSupport& support =
95 field.options().feature_support();
96 if (!support.has_edition_introduced()) {
97 return Error("Feature field ", field.full_name(),
98 " does not specify the edition it was introduced in.");
99 }
100 RETURN_IF_ERROR(ValidateFeatureSupport(support, field.full_name()));
101
102 // Validate edition defaults specification wrt support windows.
103 for (const auto& d : field.options().edition_defaults()) {
104 if (d.edition() < Edition::EDITION_2023) {
105 // Allow defaults to be specified in proto2/proto3, predating
106 // editions.
107 continue;
108 }
109 if (d.edition() < support.edition_introduced()) {
110 return Error("Feature field ", field.full_name(),
111 " has a default specified for edition ", d.edition(),
112 ", before it was introduced.");
113 }
114 if (support.has_edition_removed() &&
115 d.edition() > support.edition_removed()) {
116 return Error("Feature field ", field.full_name(),
117 " has a default specified for edition ", d.edition(),
118 ", after it was removed.");
119 }
120 }
121
122 return absl::OkStatus();
123 }
124
ValidateValueFeatureSupport(const FieldOptions::FeatureSupport & parent,const EnumValueDescriptor & value,absl::string_view field_name)125 absl::Status ValidateValueFeatureSupport(
126 const FieldOptions::FeatureSupport& parent,
127 const EnumValueDescriptor& value, absl::string_view field_name) {
128 if (!value.options().has_feature_support()) {
129 // We allow missing support windows on feature values, and they'll inherit
130 // from the feature spec.
131 return absl::OkStatus();
132 }
133
134 FieldOptions::FeatureSupport support = parent;
135 support.MergeFrom(value.options().feature_support());
136 RETURN_IF_ERROR(ValidateFeatureSupport(support, value.full_name()));
137
138 // Make sure the value doesn't expand any bounds.
139 if (support.edition_introduced() < parent.edition_introduced()) {
140 return Error("Feature value ", value.full_name(),
141 " was introduced before feature ", field_name, " was.");
142 }
143 if (parent.has_edition_removed() &&
144 support.edition_removed() > parent.edition_removed()) {
145 return Error("Feature value ", value.full_name(),
146 " was removed after feature ", field_name, " was.");
147 }
148 if (parent.has_edition_deprecated() &&
149 support.edition_deprecated() > parent.edition_deprecated()) {
150 return Error("Feature value ", value.full_name(),
151 " was deprecated after feature ", field_name, " was.");
152 }
153
154 return absl::OkStatus();
155 }
156
ValidateValuesFeatureSupport(const FieldDescriptor & field)157 absl::Status ValidateValuesFeatureSupport(const FieldDescriptor& field) {
158 // This only applies to enum features.
159 ABSL_CHECK(field.enum_type() != nullptr);
160
161 const FieldOptions::FeatureSupport& parent =
162 field.options().feature_support();
163
164 for (int i = 0; i < field.enum_type()->value_count(); ++i) {
165 const EnumValueDescriptor& value = *field.enum_type()->value(i);
166 RETURN_IF_ERROR(
167 ValidateValueFeatureSupport(parent, value, field.full_name()));
168 }
169
170 return absl::OkStatus();
171 }
172
ValidateDescriptor(const Descriptor & descriptor)173 absl::Status ValidateDescriptor(const Descriptor& descriptor) {
174 if (descriptor.oneof_decl_count() > 0) {
175 return Error("Type ", descriptor.full_name(),
176 " contains unsupported oneof feature fields.");
177 }
178 for (int i = 0; i < descriptor.field_count(); ++i) {
179 const FieldDescriptor& field = *descriptor.field(i);
180
181 if (field.is_required()) {
182 return Error("Feature field ", field.full_name(),
183 " is an unsupported required field.");
184 }
185 if (field.is_repeated()) {
186 return Error("Feature field ", field.full_name(),
187 " is an unsupported repeated field.");
188 }
189 if (field.type() != FieldDescriptor::TYPE_ENUM &&
190 field.type() != FieldDescriptor::TYPE_BOOL) {
191 return Error("Feature field ", field.full_name(),
192 " is not an enum or boolean.");
193 }
194 if (field.options().targets().empty()) {
195 return Error("Feature field ", field.full_name(),
196 " has no target specified.");
197 }
198
199 bool has_legacy_default = false;
200 for (const auto& d : field.options().edition_defaults()) {
201 if (d.edition() == Edition::EDITION_LEGACY) {
202 has_legacy_default = true;
203 continue;
204 }
205 }
206 if (!has_legacy_default) {
207 return Error("Feature field ", field.full_name(),
208 " has no default specified for EDITION_LEGACY, before it "
209 "was introduced.");
210 }
211
212 RETURN_IF_ERROR(ValidateFieldFeatureSupport(field));
213 if (field.enum_type() != nullptr) {
214 RETURN_IF_ERROR(ValidateValuesFeatureSupport(field));
215 }
216 }
217
218 return absl::OkStatus();
219 }
220
ValidateExtension(const Descriptor & feature_set,const FieldDescriptor * extension)221 absl::Status ValidateExtension(const Descriptor& feature_set,
222 const FieldDescriptor* extension) {
223 if (extension == nullptr) {
224 return Error("Unknown extension of ", feature_set.full_name(), ".");
225 }
226
227 if (extension->containing_type() != &feature_set) {
228 return Error("Extension ", extension->full_name(),
229 " is not an extension of ", feature_set.full_name(), ".");
230 }
231
232 if (extension->message_type() == nullptr) {
233 return Error("FeatureSet extension ", extension->full_name(),
234 " is not of message type. Feature extensions should "
235 "always use messages to allow for evolution.");
236 }
237
238 if (extension->is_repeated()) {
239 return Error(
240 "Only singular features extensions are supported. Found "
241 "repeated extension ",
242 extension->full_name());
243 }
244
245 if (extension->message_type()->extension_count() > 0 ||
246 extension->message_type()->extension_range_count() > 0) {
247 return Error("Nested extensions in feature extension ",
248 extension->full_name(), " are not supported.");
249 }
250
251 return absl::OkStatus();
252 }
253
MaybeInsertEdition(Edition edition,Edition maximum_edition,absl::btree_set<Edition> & editions)254 void MaybeInsertEdition(Edition edition, Edition maximum_edition,
255 absl::btree_set<Edition>& editions) {
256 if (edition <= maximum_edition) {
257 editions.insert(edition);
258 }
259 }
260
261 // This collects all of the editions that are relevant to any features defined
262 // in a message descriptor. We only need to consider editions where something
263 // has changed.
CollectEditions(const Descriptor & descriptor,Edition maximum_edition,absl::btree_set<Edition> & editions)264 void CollectEditions(const Descriptor& descriptor, Edition maximum_edition,
265 absl::btree_set<Edition>& editions) {
266 for (int i = 0; i < descriptor.field_count(); ++i) {
267 const FieldOptions& options = descriptor.field(i)->options();
268 // Editions where a new feature is introduced should be captured.
269 MaybeInsertEdition(options.feature_support().edition_introduced(),
270 maximum_edition, editions);
271
272 // Editions where a feature is removed should be captured.
273 if (options.feature_support().has_edition_removed()) {
274 MaybeInsertEdition(options.feature_support().edition_removed(),
275 maximum_edition, editions);
276 }
277
278 // Any edition where a default value changes should be captured.
279 for (const auto& def : options.edition_defaults()) {
280 MaybeInsertEdition(def.edition(), maximum_edition, editions);
281 }
282 }
283 }
284
FillDefaults(Edition edition,Message & fixed,Message & overridable)285 absl::Status FillDefaults(Edition edition, Message& fixed,
286 Message& overridable) {
287 const Descriptor& descriptor = *fixed.GetDescriptor();
288 ABSL_CHECK(&descriptor == overridable.GetDescriptor());
289
290 auto comparator = [](const FieldOptions::EditionDefault& a,
291 const FieldOptions::EditionDefault& b) {
292 return a.edition() < b.edition();
293 };
294 FieldOptions::EditionDefault edition_lookup;
295 edition_lookup.set_edition(edition);
296
297 for (int i = 0; i < descriptor.field_count(); ++i) {
298 const FieldDescriptor& field = *descriptor.field(i);
299 Message* msg = &overridable;
300 if (field.options().has_feature_support()) {
301 if ((field.options().feature_support().has_edition_introduced() &&
302 edition < field.options().feature_support().edition_introduced()) ||
303 (field.options().feature_support().has_edition_removed() &&
304 edition >= field.options().feature_support().edition_removed())) {
305 msg = &fixed;
306 }
307 }
308
309 msg->GetReflection()->ClearField(msg, &field);
310 ABSL_CHECK(!field.is_repeated());
311 ABSL_CHECK(field.cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE);
312
313 std::vector<FieldOptions::EditionDefault> defaults{
314 field.options().edition_defaults().begin(),
315 field.options().edition_defaults().end()};
316 absl::c_sort(defaults, comparator);
317 auto first_nonmatch =
318 absl::c_upper_bound(defaults, edition_lookup, comparator);
319 if (first_nonmatch == defaults.begin()) {
320 return Error("No valid default found for edition ", edition,
321 " in feature field ", field.full_name());
322 }
323
324 const std::string& def = std::prev(first_nonmatch)->value();
325 if (!TextFormat::ParseFieldValueFromString(def, &field, msg)) {
326 return Error("Parsing error in edition_defaults for feature field ",
327 field.full_name(), ". Could not parse: ", def);
328 }
329 }
330
331 return absl::OkStatus();
332 }
333
ValidateMergedFeatures(const FeatureSet & features)334 absl::Status ValidateMergedFeatures(const FeatureSet& features) {
335 // Avoid using reflection here because this is called early in the descriptor
336 // builds. Instead, a reflection-based test will be used to keep this in sync
337 // with descriptor.proto. These checks should be run on every global feature
338 // in FeatureSet.
339 #define CHECK_ENUM_FEATURE(FIELD, CAMELCASE, UPPERCASE) \
340 if (!FeatureSet::CAMELCASE##_IsValid(features.FIELD()) || \
341 features.FIELD() == FeatureSet::UPPERCASE##_UNKNOWN) { \
342 return Error("Feature field `" #FIELD \
343 "` must resolve to a known value, found " #UPPERCASE \
344 "_UNKNOWN"); \
345 }
346
347 CHECK_ENUM_FEATURE(field_presence, FieldPresence, FIELD_PRESENCE)
348 CHECK_ENUM_FEATURE(enum_type, EnumType, ENUM_TYPE)
349 CHECK_ENUM_FEATURE(repeated_field_encoding, RepeatedFieldEncoding,
350 REPEATED_FIELD_ENCODING)
351 CHECK_ENUM_FEATURE(utf8_validation, Utf8Validation, UTF8_VALIDATION)
352 CHECK_ENUM_FEATURE(message_encoding, MessageEncoding, MESSAGE_ENCODING)
353 CHECK_ENUM_FEATURE(json_format, JsonFormat, JSON_FORMAT)
354
355 #undef CHECK_ENUM_FEATURE
356
357 return absl::OkStatus();
358 }
359
ValidateSingleFeatureLifetimes(Edition edition,absl::string_view full_name,const FieldOptions::FeatureSupport & support,FeatureResolver::ValidationResults & results)360 void ValidateSingleFeatureLifetimes(
361 Edition edition, absl::string_view full_name,
362 const FieldOptions::FeatureSupport& support,
363 FeatureResolver::ValidationResults& results) {
364 // Skip fields that don't have feature support specified.
365 if (&support == &FieldOptions::FeatureSupport::default_instance()) return;
366
367 if (edition < support.edition_introduced()) {
368 results.errors.emplace_back(
369 absl::StrCat("Feature ", full_name, " wasn't introduced until edition ",
370 support.edition_introduced(),
371 " and can't be used in edition ", edition));
372 }
373 if (support.has_edition_removed() && edition >= support.edition_removed()) {
374 results.errors.emplace_back(absl::StrCat(
375 "Feature ", full_name, " has been removed in edition ",
376 support.edition_removed(), " and can't be used in edition ", edition));
377 } else if (support.has_edition_deprecated() &&
378 edition >= support.edition_deprecated()) {
379 results.warnings.emplace_back(absl::StrCat(
380 "Feature ", full_name, " has been deprecated in edition ",
381 support.edition_deprecated(), ": ", support.deprecation_warning()));
382 }
383 }
384
ValidateFeatureLifetimesImpl(Edition edition,const Message & message,FeatureResolver::ValidationResults & results)385 void ValidateFeatureLifetimesImpl(Edition edition, const Message& message,
386 FeatureResolver::ValidationResults& results) {
387 std::vector<const FieldDescriptor*> fields;
388 message.GetReflection()->ListFields(message, &fields);
389 for (const FieldDescriptor* field : fields) {
390 // Recurse into message extension.
391 if (field->is_extension() &&
392 field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
393 ValidateFeatureLifetimesImpl(
394 edition, message.GetReflection()->GetMessage(message, field),
395 results);
396 continue;
397 }
398
399 if (field->enum_type() != nullptr) {
400 int number = message.GetReflection()->GetEnumValue(message, field);
401 auto value = field->enum_type()->FindValueByNumber(number);
402 if (value == nullptr) {
403 results.errors.emplace_back(absl::StrCat(
404 "Feature ", field->full_name(), " has no known value ", number));
405 continue;
406 }
407 ValidateSingleFeatureLifetimes(edition, value->full_name(),
408 value->options().feature_support(),
409 results);
410 }
411
412 ValidateSingleFeatureLifetimes(edition, field->full_name(),
413 field->options().feature_support(), results);
414 }
415 }
416
417 } // namespace
418
CompileDefaults(const Descriptor * feature_set,absl::Span<const FieldDescriptor * const> extensions,Edition minimum_edition,Edition maximum_edition)419 absl::StatusOr<FeatureSetDefaults> FeatureResolver::CompileDefaults(
420 const Descriptor* feature_set,
421 absl::Span<const FieldDescriptor* const> extensions,
422 Edition minimum_edition, Edition maximum_edition) {
423 if (minimum_edition > maximum_edition) {
424 return Error("Invalid edition range, edition ", minimum_edition,
425 " is newer than edition ", maximum_edition);
426 }
427 // Find and validate the FeatureSet in the pool.
428 if (feature_set == nullptr) {
429 return Error(
430 "Unable to find definition of google.protobuf.FeatureSet in descriptor pool.");
431 }
432 RETURN_IF_ERROR(ValidateDescriptor(*feature_set));
433
434 // Collect and validate all the FeatureSet extensions.
435 for (const auto* extension : extensions) {
436 RETURN_IF_ERROR(ValidateExtension(*feature_set, extension));
437 RETURN_IF_ERROR(ValidateDescriptor(*extension->message_type()));
438 }
439
440 // Collect all the editions with unique defaults.
441 absl::btree_set<Edition> editions;
442 CollectEditions(*feature_set, maximum_edition, editions);
443 for (const auto* extension : extensions) {
444 CollectEditions(*extension->message_type(), maximum_edition, editions);
445 }
446 // Sanity check validation conditions above.
447 ABSL_CHECK(!editions.empty());
448 if (*editions.begin() != EDITION_LEGACY) {
449 return Error("Minimum edition ", *editions.begin(),
450 " is not EDITION_LEGACY");
451 }
452
453 if (*editions.begin() > minimum_edition) {
454 return Error("Minimum edition ", minimum_edition,
455 " is earlier than the oldest valid edition ",
456 *editions.begin());
457 }
458
459 // Fill the default spec.
460 FeatureSetDefaults defaults;
461 defaults.set_minimum_edition(minimum_edition);
462 defaults.set_maximum_edition(maximum_edition);
463 auto message_factory = absl::make_unique<DynamicMessageFactory>();
464 for (const auto& edition : editions) {
465 auto fixed_defaults_dynamic =
466 absl::WrapUnique(message_factory->GetPrototype(feature_set)->New());
467 auto overridable_defaults_dynamic =
468 absl::WrapUnique(message_factory->GetPrototype(feature_set)->New());
469 RETURN_IF_ERROR(FillDefaults(edition, *fixed_defaults_dynamic,
470 *overridable_defaults_dynamic));
471 for (const auto* extension : extensions) {
472 RETURN_IF_ERROR(FillDefaults(
473 edition,
474 *fixed_defaults_dynamic->GetReflection()->MutableMessage(
475 fixed_defaults_dynamic.get(), extension),
476 *overridable_defaults_dynamic->GetReflection()->MutableMessage(
477 overridable_defaults_dynamic.get(), extension)));
478 }
479 auto* edition_defaults = defaults.mutable_defaults()->Add();
480 edition_defaults->set_edition(edition);
481 edition_defaults->mutable_fixed_features()->MergeFromString(
482 fixed_defaults_dynamic->SerializeAsString());
483 edition_defaults->mutable_overridable_features()->MergeFromString(
484 overridable_defaults_dynamic->SerializeAsString());
485 }
486 return defaults;
487 }
488
Create(Edition edition,const FeatureSetDefaults & compiled_defaults)489 absl::StatusOr<FeatureResolver> FeatureResolver::Create(
490 Edition edition, const FeatureSetDefaults& compiled_defaults) {
491 if (edition < compiled_defaults.minimum_edition()) {
492 return Error("Edition ", edition,
493 " is earlier than the minimum supported edition ",
494 compiled_defaults.minimum_edition());
495 }
496 if (compiled_defaults.maximum_edition() < edition) {
497 return Error("Edition ", edition,
498 " is later than the maximum supported edition ",
499 compiled_defaults.maximum_edition());
500 }
501
502 // Validate compiled defaults.
503 Edition prev_edition = EDITION_UNKNOWN;
504 for (const auto& edition_default : compiled_defaults.defaults()) {
505 if (edition_default.edition() == EDITION_UNKNOWN) {
506 return Error("Invalid edition ", edition_default.edition(),
507 " specified.");
508 }
509 if (prev_edition != EDITION_UNKNOWN) {
510 if (edition_default.edition() <= prev_edition) {
511 return Error(
512 "Feature set defaults are not strictly increasing. Edition ",
513 prev_edition, " is greater than or equal to edition ",
514 edition_default.edition(), ".");
515 }
516 }
517 FeatureSet features = edition_default.fixed_features();
518 features.MergeFrom(edition_default.overridable_features());
519 RETURN_IF_ERROR(ValidateMergedFeatures(features));
520
521 prev_edition = edition_default.edition();
522 }
523
524 // Select the matching edition defaults.
525 auto comparator = [](const auto& a, const auto& b) {
526 return a.edition() < b.edition();
527 };
528 FeatureSetDefaults::FeatureSetEditionDefault search;
529 search.set_edition(edition);
530 auto first_nonmatch =
531 absl::c_upper_bound(compiled_defaults.defaults(), search, comparator);
532 if (first_nonmatch == compiled_defaults.defaults().begin()) {
533 return Error("No valid default found for edition ", edition);
534 }
535
536 FeatureSet features = std::prev(first_nonmatch)->fixed_features();
537 features.MergeFrom(std::prev(first_nonmatch)->overridable_features());
538 return FeatureResolver(std::move(features));
539 }
540
MergeFeatures(const FeatureSet & merged_parent,const FeatureSet & unmerged_child) const541 absl::StatusOr<FeatureSet> FeatureResolver::MergeFeatures(
542 const FeatureSet& merged_parent, const FeatureSet& unmerged_child) const {
543 FeatureSet merged = defaults_;
544 merged.MergeFrom(merged_parent);
545 merged.MergeFrom(unmerged_child);
546
547 RETURN_IF_ERROR(ValidateMergedFeatures(merged));
548
549 return merged;
550 }
551
ValidateFeatureLifetimes(Edition edition,const FeatureSet & features,const Descriptor * pool_descriptor)552 FeatureResolver::ValidationResults FeatureResolver::ValidateFeatureLifetimes(
553 Edition edition, const FeatureSet& features,
554 const Descriptor* pool_descriptor) {
555 const Message* pool_features = nullptr;
556 DynamicMessageFactory factory;
557 std::unique_ptr<Message> features_storage;
558 if (pool_descriptor == nullptr) {
559 // The FeatureSet descriptor can be null if no custom extensions are defined
560 // in any transitive dependency. In this case, we can just use the
561 // generated pool for validation, since there wouldn't be any feature
562 // extensions defined anyway.
563 pool_features = &features;
564 } else {
565 // Move the features back to the current pool so that we can reflect on any
566 // extensions.
567 features_storage =
568 absl::WrapUnique(factory.GetPrototype(pool_descriptor)->New());
569 features_storage->ParseFromString(features.SerializeAsString());
570 pool_features = features_storage.get();
571 }
572 ABSL_CHECK(pool_features != nullptr);
573
574 ValidationResults results;
575 ValidateFeatureLifetimesImpl(edition, *pool_features, results);
576 return results;
577 }
578
579 } // namespace protobuf
580 } // namespace google
581
582 #include "google/protobuf/port_undef.inc"
583