Add a DescriptorPool API for avoiding stack overflows.

It's up to the user how to set up the threads, but this will allow them to dispatch any recursive work during the descriptor builds to separate threads with their own stack limit.

PiperOrigin-RevId: 586774613
pull/14887/head
Mike Kruskal 1 year ago committed by Copybara-Service
parent 118171bf5f
commit 962135a727
  1. 14
      src/google/protobuf/descriptor.cc
  2. 23
      src/google/protobuf/descriptor.h
  3. 3
      src/google/protobuf/descriptor_unittest.cc

@ -4549,9 +4549,17 @@ const FileDescriptor* DescriptorPool::BuildFileFromDatabase(
if (tables_->known_bad_files_.contains(proto.name())) {
return nullptr;
}
const FileDescriptor* result =
DescriptorBuilder::New(this, tables_.get(), default_error_collector_)
->BuildFile(proto);
const FileDescriptor* result;
const auto build_file = [&] {
result =
DescriptorBuilder::New(this, tables_.get(), default_error_collector_)
->BuildFile(proto);
};
if (dispatcher_ != nullptr) {
(*dispatcher_)(build_file);
} else {
build_file();
}
if (result == nullptr) {
tables_->known_bad_files_.insert(proto.name());
}

@ -36,6 +36,7 @@
#include <iterator>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "google/protobuf/stubs/common.h"
@ -43,6 +44,8 @@
#include "absl/base/call_once.h"
#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
#include "absl/functional/function_ref.h"
#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
#include "absl/strings/str_format.h"
@ -2299,6 +2302,20 @@ class PROTOBUF_EXPORT DescriptorPool {
void EnforceExtensionDeclarations(bool enforce) {
enforce_extension_declarations_ = enforce;
}
#ifndef SWIG
// Dispatch recursive builds to a callback that may stick them onto a separate
// thread. This is primarily to avoid stack overflows on untrusted inputs.
// The dispatcher must always synchronously execute the provided callback.
// Asynchronous execution is undefined behavior.
void SetRecursiveBuildDispatcher(
absl::AnyInvocable<void(absl::FunctionRef<void()>) const> dispatcher) {
dispatcher_ = std::make_unique<
absl::AnyInvocable<void(absl::FunctionRef<void()>) const>>(
std::move(dispatcher));
}
#endif // SWIG
// Internal stuff --------------------------------------------------
// These methods MUST NOT be called from outside the proto2 library.
// These methods may contain hidden pitfalls and may be removed in a
@ -2460,6 +2477,12 @@ class PROTOBUF_EXPORT DescriptorPool {
ErrorCollector* default_error_collector_;
const DescriptorPool* underlay_;
#ifndef SWIG
// Dispatcher for recursive calls during builds.
std::unique_ptr<absl::AnyInvocable<void(absl::FunctionRef<void()>) const>>
dispatcher_;
#endif // SWIG
// This class contains a lot of hash maps with complicated types that
// we'd like to keep out of the header.
class Tables;

@ -30,15 +30,18 @@
#include <gtest/gtest.h>
#include "absl/container/btree_set.h"
#include "absl/container/flat_hash_set.h"
#include "absl/functional/any_invocable.h"
#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
#include "absl/log/die_if_null.h"
#include "absl/log/scoped_mock_log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "absl/synchronization/notification.h"
#include "google/protobuf/compiler/importer.h"
#include "google/protobuf/compiler/parser.h"
#include "google/protobuf/cpp_features.pb.h"

Loading…
Cancel
Save