diff --git a/third_party/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.h b/third_party/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.h index f2da4d17d538..7ff652191e11 100644 --- a/third_party/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.h +++ b/third_party/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.h @@ -73,10 +73,21 @@ class TCompactProtocolT { std::stack lastField_; int16_t lastFieldId_; + // Recursion-depth guard against stack overflow on deeply nested input; see + // TInputRecursionTracker in TProtocol.h. Input and output depths are tracked + // separately so one protocol can be used for both. The limit comes from the + // transport's TConfiguration (default DEFAULT_RECURSION_DEPTH = 64). + uint32_t input_recursion_depth_; + uint32_t output_recursion_depth_; + uint32_t recursion_limit_; + public: TCompactProtocolT(std::shared_ptr trans) : trans_(trans.get()), lastFieldId_(0), + input_recursion_depth_(0), + output_recursion_depth_(0), + recursion_limit_(trans->getConfiguration()->getRecursionLimit()), string_limit_(0), string_buf_(nullptr), string_buf_size_(0), @@ -90,6 +101,9 @@ class TCompactProtocolT { int32_t container_limit) : trans_(trans.get()), lastFieldId_(0), + input_recursion_depth_(0), + output_recursion_depth_(0), + recursion_limit_(trans->getConfiguration()->getRecursionLimit()), string_limit_(string_limit), string_buf_(nullptr), string_buf_size_(0), @@ -100,6 +114,23 @@ class TCompactProtocolT { ~TCompactProtocolT() { free(string_buf_); } + // Recursion-depth tracking, driven by TInputRecursionTracker / + // TOutputRecursionTracker (TProtocol.h). Throws once the configured limit is + // exceeded, before the C++ stack overflows. + void incrementInputRecursionDepth() { + if (recursion_limit_ < ++input_recursion_depth_) { + throw TProtocolException(TProtocolException::DEPTH_LIMIT); + } + } + void decrementInputRecursionDepth() { --input_recursion_depth_; } + + void incrementOutputRecursionDepth() { + if (recursion_limit_ < ++output_recursion_depth_) { + throw TProtocolException(TProtocolException::DEPTH_LIMIT); + } + } + void decrementOutputRecursionDepth() { --output_recursion_depth_; } + /** * Writing functions */ diff --git a/third_party/thrift/lib/cpp/src/thrift/protocol/TProtocol.h b/third_party/thrift/lib/cpp/src/thrift/protocol/TProtocol.h index d3fe2e699894..02d3c2e9312a 100644 --- a/third_party/thrift/lib/cpp/src/thrift/protocol/TProtocol.h +++ b/third_party/thrift/lib/cpp/src/thrift/protocol/TProtocol.h @@ -167,24 +167,28 @@ namespace protocol { using apache::thrift::transport::TTransport; -// Firebolt: no-op recursion trackers. The full Apache Thrift versions guard -// against stack overflow from deeply nested structures via TProtocol -// recursion-depth counters, which this minimal vendored copy omits. The -// generated parquet code (parquet_types.tcc) instantiates these at the top of -// each read()/write(); we provide no-op stand-ins, templated on the protocol -// type, so that generated code compiles unchanged against the bare-bones -// transport without requiring the recursion-depth machinery. +// RAII recursion-depth guards against stack overflow from deeply nested +// structures in untrusted input (e.g. a malicious Parquet footer). The +// generated parquet code (parquet_types.tcc) constructs one at the top of each +// read()/write(), and skip() constructs one per level; the ctor increments and +// the dtor decrements a depth counter on the protocol, throwing DEPTH_LIMIT +// once the configured limit is exceeded. Templated on the protocol type since +// this minimal vendored copy has no common TProtocol base class. +template struct TInputRecursionTracker { - template - explicit TInputRecursionTracker(Protocol_&) {} - // User-declared destructor so the generated code's `tracker` locals are not - // flagged as unused (RAII types with non-trivial destructors are exempt). - ~TInputRecursionTracker() {} + Protocol_& prot_; + explicit TInputRecursionTracker(Protocol_& prot) : prot_(prot) { + prot_.incrementInputRecursionDepth(); + } + ~TInputRecursionTracker() { prot_.decrementInputRecursionDepth(); } }; +template struct TOutputRecursionTracker { - template - explicit TOutputRecursionTracker(Protocol_&) {} - ~TOutputRecursionTracker() {} + Protocol_& prot_; + explicit TOutputRecursionTracker(Protocol_& prot) : prot_(prot) { + prot_.incrementOutputRecursionDepth(); + } + ~TOutputRecursionTracker() { prot_.decrementOutputRecursionDepth(); } }; /** @@ -194,6 +198,8 @@ struct TOutputRecursionTracker { */ template uint32_t skip(Protocol_& prot, TType type) { + TInputRecursionTracker tracker(prot); + switch (type) { case T_BOOL: { bool boolv;