• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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