Commit message: Throw on skipping an invalid type.

Summary:
Certain values (e.g.) T_STOP should not appear as a skip type. Allowing them to
can cause thrift to loop unboundedly.

Reviewed By: spalamarchuk

Differential Revision: D15102451

fbshipit-source-id: c08d52f44f37e9c212d3480233ac217105586c9f
CVE: CVE-2019-3565
CWE: CWE-20
Vulnerability description: Legacy C++ Facebook Thrift servers (using cpp instead of cpp2) would not error upon receiving messages with containers of fields of unknown type. As a result, malicious clients could send short messages which would take a long time for the server to parse, potentially leading to denial of service. This issue affects Facebook Thrift prior to v2019.05.06.00.

Project: fbthrift

  uint32_t writeListBegin(const TType elemType, const uint32_t size) {
    T_VIRTUAL_CALL();
    return writeListBegin_virt(elemType, size);
  }

Project: fbthrift

  uint32_t writeListEnd() {
    T_VIRTUAL_CALL();
    return writeListEnd_virt();
  }

Project: fbthrift

  uint32_t readBool(std::vector<bool>::reference value) {
    T_VIRTUAL_CALL();
    return readBool_virt(value);
  }

Project: fbthrift

  writeMapBegin(const TType keyType, const TType valType, const uint32_t size) {
    T_VIRTUAL_CALL();
    return writeMapBegin_virt(keyType, valType, size);
  }

Project: fbthrift

  uint32_t writeFieldEnd() {
    T_VIRTUAL_CALL();
    return writeFieldEnd_virt();
  }

Project: fbthrift

  TSingleProtocolFactory() {
    factory_.reset(new Factory_());
  }

Project: fbthrift

  std::shared_ptr<TProtocolFactory> getInputProtocolFactory() override {
    return factory_;
  }

Project: fbthrift

  uint32_t writeBool(const bool value) {
    T_VIRTUAL_CALL();
    return writeBool_virt(value);
  }

Project: fbthrift

  inline std::shared_ptr<TTransport> getOutputTransport() {
    return ptrans_;
  }

Project: fbthrift

  uint32_t readSetBegin(TType& elemType, uint32_t& size, bool& sizeUnknown) {
    T_VIRTUAL_CALL();
    return readSetBegin_virt(elemType, size, sizeUnknown);
  }

Project: fbthrift

  uint32_t writeMapEnd() {
    T_VIRTUAL_CALL();
    return writeMapEnd_virt();
  }

Project: fbthrift

  uint32_t skip(TType type) {
    T_VIRTUAL_CALL();
    return skip_virt(type);
  }

Project: fbthrift

  uint32_t writeString(const folly::fbstring& str) {
    T_VIRTUAL_CALL();
    return writeString_virt(str.toStdString());
  }

Project: fbthrift

  std::shared_ptr<TProtocolFactory> getInputProtocolFactory() override {
    return inputFactory_;
  }

Project: fbthrift

  uint32_t readSetEnd() {
    T_VIRTUAL_CALL();
    return readSetEnd_virt();
  }

Project: fbthrift

  TProtocolPair getProtocol(transport::TTransportPair transports) override {
    return std::make_pair(
        factory_->getProtocol(transports.first),
        factory_->getProtocol(transports.second));
  }

Project: fbthrift

  uint32_t readBool(bool& value) {
    T_VIRTUAL_CALL();
    return readBool_virt(value);
  }

Project: fbthrift

  TDuplexProtocolFactory() {}

Project: fbthrift

  uint32_t writeSetBegin(const TType elemType, const uint32_t size) {
    T_VIRTUAL_CALL();
    return writeSetBegin_virt(elemType, size);
  }

Project: fbthrift

  uint32_t readStructEnd() {
    T_VIRTUAL_CALL();
    return readStructEnd_virt();
  }

Project: fbthrift

  uint32_t readListBegin(TType& elemType, uint32_t& size, bool& sizeUnknown) {
    T_VIRTUAL_CALL();
    return readListBegin_virt(elemType, size, sizeUnknown);
  }

Project: fbthrift

uint32_t readFloatingPoint(Protocol_& prot, TType arg_type, T& value) {
  switch (arg_type) {
    case TType::T_DOUBLE: {
      double dub;
      auto res = prot.readDouble(dub);
      value = static_cast<T>(dub);
      return res;
    }
    case TType::T_FLOAT: {
      float flt;
      auto res = prot.readFloat(flt);
      value = static_cast<T>(flt);
      return res;
    }
    default: {
      throw TProtocolException(
          std::string("Cannot parse floating number of ") +
          std::to_string(arg_type) + " type");
    }
  }
}

Project: fbthrift

  TDualProtocolFactory(
      std::shared_ptr<TProtocolFactory> inputFactory,
      std::shared_ptr<TProtocolFactory> outputFactory)
      : inputFactory_(inputFactory), outputFactory_(outputFactory) {}

Project: fbthrift

  uint32_t readDouble(double& dub) {
    T_VIRTUAL_CALL();
    return readDouble_virt(dub);
  }

Project: fbthrift

  void setNextStructType(uint64_t reflection_id) {
    T_VIRTUAL_CALL();
    return setNextStructType_virt(reflection_id);
  }

Project: fbthrift

  uint32_t writeString(const std::string& str) {
    T_VIRTUAL_CALL();
    return writeString_virt(str);
  }

Project: fbthrift

  uint32_t readBinary(folly::fbstring& str) {
    T_VIRTUAL_CALL();
    std::string data;
    uint32_t ret = readBinary_virt(data);
    str = data;
    return ret;
  }

Project: fbthrift

  uint32_t writeFloat(const float flt) {
    T_VIRTUAL_CALL();
    return writeFloat_virt(flt);
  }

Project: fbthrift

  explicit TProtocol(TTransport* ptrans)
      : ptrans_(ptrans, [](TTransport*) {}) {}

Project: fbthrift

  uint32_t readI16(int16_t& i16) {
    T_VIRTUAL_CALL();
    return readI16_virt(i16);
  }

Project: fbthrift

  uint32_t writeFieldBegin(
      const char* name,
      const TType fieldType,
      const int16_t fieldId) {
    T_VIRTUAL_CALL();
    return writeFieldBegin_virt(name, fieldType, fieldId);
  }

Project: fbthrift

  uint32_t readI64(int64_t& i64) {
    T_VIRTUAL_CALL();
    return readI64_virt(i64);
  }

Project: fbthrift

  TProtocol() {}

Project: fbthrift

  uint32_t writeI32(const int32_t i32) {
    T_VIRTUAL_CALL();
    return writeI32_virt(i32);
  }

Project: fbthrift

  void setVersion(const int8_t version) {
    T_VIRTUAL_CALL();
    return setVersion_virt(version);
  }

Project: fbthrift

uint32_t skip(Protocol_& prot, TType arg_type) {
  switch (arg_type) {
    case T_BOOL: {
      bool boolv;
      return prot.readBool(boolv);
    }
    case T_BYTE: {
      int8_t bytev = 0;
      return prot.readByte(bytev);
    }
    case T_I16: {
      int16_t i16;
      return prot.readI16(i16);
    }
    case T_I32: {
      int32_t i32;
      return prot.readI32(i32);
    }
    case T_I64: {
      int64_t i64;
      return prot.readI64(i64);
    }
    case T_DOUBLE: {
      double dub;
      return prot.readDouble(dub);
    }
    case T_FLOAT: {
      float flt;
      return prot.readFloat(flt);
    }
    case T_STRING: {
      std::string str;
      return prot.readBinary(str);
    }
    case T_STRUCT: {
      uint32_t result = 0;
      std::string name;
      int16_t fid;
      TType ftype;
      result += prot.readStructBegin(name);
      while (true) {
        result += prot.readFieldBegin(name, ftype, fid);
        if (ftype == T_STOP) {
          break;
        }
        result += skip(prot, ftype);
        result += prot.readFieldEnd();
      }
      result += prot.readStructEnd();
      return result;
    }
    case T_MAP: {
      uint32_t result = 0;
      TType keyType;
      TType valType;
      uint32_t i, size;
      bool sizeUnknown;
      result += prot.readMapBegin(keyType, valType, size, sizeUnknown);
      if (!sizeUnknown) {
        for (i = 0; i < size; i++) {
          result += skip(prot, keyType);
          result += skip(prot, valType);
        }
      } else {
        while (prot.peekMap()) {
          result += skip(prot, keyType);
          result += skip(prot, valType);
        }
      }
      result += prot.readMapEnd();
      return result;
    }
    case T_SET: {
      uint32_t result = 0;
      TType elemType;
      uint32_t i, size;
      bool sizeUnknown;
      result += prot.readSetBegin(elemType, size, sizeUnknown);
      if (!sizeUnknown) {
        for (i = 0; i < size; i++) {
          result += skip(prot, elemType);
        }
      } else {
        while (prot.peekSet()) {
          result += skip(prot, elemType);
        }
      }
      result += prot.readSetEnd();
      return result;
    }
    case T_LIST: {
      uint32_t result = 0;
      TType elemType;
      uint32_t i, size;
      bool sizeUnknown;
      result += prot.readListBegin(elemType, size, sizeUnknown);
      if (!sizeUnknown) {
        for (i = 0; i < size; i++) {
          result += skip(prot, elemType);
        }
      } else {
        while (prot.peekList()) {
          result += skip(prot, elemType);
        }
      }
      result += prot.readListEnd();
      return result;
    }
    default: {
      TProtocolException::throwInvalidSkipType(arg_type);
    }
  }
}

Project: fbthrift

  uint32_t readByte(int8_t& byte) {
    T_VIRTUAL_CALL();
    return readByte_virt(byte);
  }

Project: fbthrift

  uint32_t readMessageBegin(
      std::string& name,
      TMessageType& messageType,
      int32_t& seqid) {
    T_VIRTUAL_CALL();
    return readMessageBegin_virt(name, messageType, seqid);
  }

Project: fbthrift

  std::shared_ptr<TProtocolFactory> getOutputProtocolFactory() override {
    return outputFactory_;
  }

Project: fbthrift

  uint32_t writeSetEnd() {
    T_VIRTUAL_CALL();
    return writeSetEnd_virt();
  }

Project: fbthrift

  ::apache::thrift::reflection::Schema* getSchema() {
    T_VIRTUAL_CALL();
    return getSchema_virt();
  }

Project: fbthrift

  uint32_t writeStructEnd() {
    T_VIRTUAL_CALL();
    return writeStructEnd_virt();
  }

Project: fbthrift

  virtual uint32_t skip_virt(TType type) {
    return ::apache::thrift::protocol::skip(*this, type);
  }

Project: fbthrift

  uint32_t readI32(int32_t& i32) {
    T_VIRTUAL_CALL();
    return readI32_virt(i32);
  }

Project: fbthrift

  uint32_t readListEnd() {
    T_VIRTUAL_CALL();
    return readListEnd_virt();
  }

Project: fbthrift

  TProtocolPair getProtocol(transport::TTransportPair transports) override {
    return std::make_pair(
        inputFactory_->getProtocol(transports.first),
        outputFactory_->getProtocol(transports.second));
  }

Project: fbthrift

  uint32_t writeByte(const int8_t byte) {
    T_VIRTUAL_CALL();
    return writeByte_virt(byte);
  }

Project: fbthrift

  uint32_t readFloat(float& flt) {
    T_VIRTUAL_CALL();
    return readFloat_virt(flt);
  }

Project: fbthrift

  inline std::shared_ptr<TTransport> getInputTransport() {
    return ptrans_;
  }

Project: fbthrift

  uint32_t writeI64(const int64_t i64) {
    T_VIRTUAL_CALL();
    return writeI64_virt(i64);
  }

Project: fbthrift

  uint32_t writeBinary(const folly::fbstring& str) {
    T_VIRTUAL_CALL();
    return writeBinary_virt(str.toStdString());
  }

Project: fbthrift

  explicit TSingleProtocolFactory(std::shared_ptr<Factory_> factory)
      : factory_(factory) {}

Project: fbthrift

  uint32_t readMessageEnd() {
    T_VIRTUAL_CALL();
    return readMessageEnd_virt();
  }

Project: fbthrift

  uint32_t readBinary(std::string& str) {
    T_VIRTUAL_CALL();
    return readBinary_virt(str);
  }

Project: fbthrift

  uint32_t readMapEnd() {
    T_VIRTUAL_CALL();
    return readMapEnd_virt();
  }

Project: fbthrift

  std::shared_ptr<TProtocolFactory> getOutputProtocolFactory() override {
    return factory_;
  }

Project: fbthrift

  uint32_t writeDouble(const double dub) {
    T_VIRTUAL_CALL();
    return writeDouble_virt(dub);
  }

Project: fbthrift

  readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId) {
    T_VIRTUAL_CALL();
    return readFieldBegin_virt(name, fieldType, fieldId);
  }

Project: fbthrift

  int32_t getContainerSizeLimit() {
    return 0; // No limit
  }

Project: fbthrift

  uint32_t writeFieldStop() {
    T_VIRTUAL_CALL();
    return writeFieldStop_virt();
  }

Project: fbthrift

  uint32_t writeStructBegin(const char* name) {
    T_VIRTUAL_CALL();
    return writeStructBegin_virt(name);
  }

Project: fbthrift

  uint32_t writeMessageBegin(
      const std::string& name,
      const TMessageType messageType,
      const int32_t seqid) {
    T_VIRTUAL_CALL();
    return writeMessageBegin_virt(name, messageType, seqid);
  }

Project: fbthrift

  bool peekSet() {
    T_VIRTUAL_CALL();
    return peekSet_virt();
  }

Project: fbthrift

  virtual std::shared_ptr<TProtocolFactory> getInputProtocolFactory() {
    return std::shared_ptr<TProtocolFactory>();
  }

Project: fbthrift

  explicit TProtocol(std::shared_ptr<TTransport> ptrans) : ptrans_(ptrans) {}

Project: fbthrift

  uint32_t readString(std::string& str) {
    T_VIRTUAL_CALL();
    return readString_virt(str);
  }

Project: fbthrift

  bool peekMap() {
    T_VIRTUAL_CALL();
    return peekMap_virt();
  }

Project: fbthrift

  int32_t getStringSizeLimit() {
    return 0; // No limit
  }

Project: fbthrift

  uint32_t readString(folly::fbstring& str) {
    T_VIRTUAL_CALL();
    std::string data;
    uint32_t ret = readString_virt(data);
    str = data;
    return ret;
  }

Project: fbthrift

  TProtocolFactory() {}

Project: fbthrift

  virtual ~TDuplexProtocolFactory() {}

Project: fbthrift

uint32_t readIntegral(Protocol_& prot, TType arg_type, T& value) {
  switch (arg_type) {
    case TType::T_BOOL: {
      bool boolv;
      auto res = prot.readBool(boolv);
      value = static_cast<T>(boolv);
      return res;
    }
    case TType::T_BYTE: {
      int8_t bytev;
      auto res = prot.readByte(bytev);
      value = static_cast<T>(bytev);
      return res;
    }
    case TType::T_I16: {
      int16_t i16;
      auto res = prot.readI16(i16);
      value = static_cast<T>(i16);
      return res;
    }
    case TType::T_I32: {
      int32_t i32;
      auto res = prot.readI32(i32);
      value = static_cast<T>(i32);
      return res;
    }
    case TType::T_I64: {
      int64_t i64;
      auto res = prot.readI64(i64);
      value = static_cast<T>(i64);
      return res;
    }
    default: {
      throw TProtocolException(
          std::string("Cannot parse integral number of ") +
          std::to_string(arg_type) + " type");
    }
  }
}

Project: fbthrift

  bool peekList() {
    T_VIRTUAL_CALL();
    return peekList_virt();
  }

Project: fbthrift

  uint32_t readStructBegin(std::string& name) {
    T_VIRTUAL_CALL();
    return readStructBegin_virt(name);
  }

Project: fbthrift

  inline std::shared_ptr<TTransport> getTransport() {
    return ptrans_;
  }

Project: fbthrift

  uint32_t readMapBegin(
      TType& keyType,
      TType& valType,
      uint32_t& size,
      bool& sizeUnknown) {
    T_VIRTUAL_CALL();
    return readMapBegin_virt(keyType, valType, size, sizeUnknown);
  }

Project: fbthrift

  uint32_t writeI16(const int16_t i16) {
    T_VIRTUAL_CALL();
    return writeI16_virt(i16);
  }

Project: fbthrift

  uint32_t writeMessageEnd() {
    T_VIRTUAL_CALL();
    return writeMessageEnd_virt();
  }

Project: fbthrift

  uint32_t readFieldEnd() {
    T_VIRTUAL_CALL();
    return readFieldEnd_virt();
  }

Project: fbthrift

  virtual std::shared_ptr<TProtocolFactory> getOutputProtocolFactory() {
    return std::shared_ptr<TProtocolFactory>();
  }

Project: fbthrift

  uint32_t writeBinary(const std::string& str) {
    T_VIRTUAL_CALL();
    return writeBinary_virt(str);
  }