Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions third_party/thrift/lib/cpp/src/thrift/protocol/TCompactProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,21 @@ class TCompactProtocolT {
std::stack<int16_t> 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<Transport_> 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),
Expand All @@ -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),
Expand All @@ -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
*/
Expand Down
36 changes: 21 additions & 15 deletions third_party/thrift/lib/cpp/src/thrift/protocol/TProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename Protocol_>
struct TInputRecursionTracker {
template <typename Protocol_>
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 <typename Protocol_>
struct TOutputRecursionTracker {
template <typename Protocol_>
explicit TOutputRecursionTracker(Protocol_&) {}
~TOutputRecursionTracker() {}
Protocol_& prot_;
explicit TOutputRecursionTracker(Protocol_& prot) : prot_(prot) {
prot_.incrementOutputRecursionDepth();
}
~TOutputRecursionTracker() { prot_.decrementOutputRecursionDepth(); }
};

/**
Expand All @@ -194,6 +198,8 @@ struct TOutputRecursionTracker {
*/
template <class Protocol_>
uint32_t skip(Protocol_& prot, TType type) {
TInputRecursionTracker<Protocol_> tracker(prot);

@lorenzhs lorenzhs Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it enough to have this on skip()? Don't we need this on all read()s in cpp/src/generated/parquet_types.tcc as well? See 24d7106


switch (type) {
case T_BOOL: {
bool boolv;
Expand Down