Prevent Java's TProtocolUtil.skip from overflowing the stack.


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665577 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/java/src/protocol/TProtocolUtil.java b/lib/java/src/protocol/TProtocolUtil.java
index 5c665ab..dc4aeeb 100644
--- a/lib/java/src/protocol/TProtocolUtil.java
+++ b/lib/java/src/protocol/TProtocolUtil.java
@@ -7,7 +7,6 @@
 package com.facebook.thrift.protocol;
 
 import com.facebook.thrift.TException;
-import com.facebook.thrift.transport.TTransport;
 
 /**
  * Utility class with static methods for interacting with protocol data
@@ -16,9 +15,52 @@
  * @author Mark Slee <mcslee@facebook.com>
  */
 public class TProtocolUtil {
+
+  /**
+   * The maximum recursive depth the skip() function will traverse before
+   * throwing a TException.
+   */
+  private static int maxSkipDepth = Integer.MAX_VALUE;
+
+  /**
+   * Specifies the maximum recursive depth that the skip function will
+   * traverse before throwing a TException.  This is a global setting, so
+   * any call to skip in this JVM will enforce this value.
+   *
+   * @param depth  the maximum recursive depth.  A value of 2 would allow
+   *    the skip function to skip a structure or collection with basic children,
+   *    but it would not permit skipping a struct that had a field containing
+   *    a child struct.  A value of 1 would only allow skipping of simple
+   *    types and empty structs/collections.
+   */
+  public static void setMaxSkipDepth(int depth) {
+    maxSkipDepth = depth;
+  }
+
+  /**
+   * Skips over the next data element from the provided input TProtocol object.
+   *
+   * @param prot  the protocol object to read from
+   * @param type  the next value will be intepreted as this TType value.
+   */
   public static void skip(TProtocol prot, byte type)
     throws TException {
+    skip(prot, type, maxSkipDepth);
+  }
 
+  /**
+   * Skips over the next data element from the provided input TProtocol object.
+   *
+   * @param prot  the protocol object to read from
+   * @param type  the next value will be intepreted as this TType value.
+   * @param maxDepth  this function will only skip complex objects to this
+   *   recursive depth, to prevent Java stack overflow.
+   */
+  public static void skip(TProtocol prot, byte type, int maxDepth)
+  throws TException {
+    if (maxDepth <= 0) {
+      throw new TException("Maximum skip depth exceeded");
+    }
     switch (type) {
     case TType.BOOL:
       {
@@ -63,7 +105,7 @@
           if (field.type == TType.STOP) {
             break;
           }
-          skip(prot, field.type);
+          skip(prot, field.type, maxDepth - 1);
           prot.readFieldEnd();
         }
         prot.readStructEnd();
@@ -73,8 +115,8 @@
       {
         TMap map = prot.readMapBegin();
         for (int i = 0; i < map.size; i++) {
-          skip(prot, map.keyType);
-          skip(prot, map.valueType);
+          skip(prot, map.keyType, maxDepth - 1);
+          skip(prot, map.valueType, maxDepth - 1);
         }
         prot.readMapEnd();
         break;
@@ -83,7 +125,7 @@
       {
         TSet set = prot.readSetBegin();
         for (int i = 0; i < set.size; i++) {
-          skip(prot, set.elemType);
+          skip(prot, set.elemType, maxDepth - 1);
         }
         prot.readSetEnd();
         break;
@@ -92,7 +134,7 @@
       {
         TList list = prot.readListBegin();
         for (int i = 0; i < list.size; i++) {
-          skip(prot, list.elemType);
+          skip(prot, list.elemType, maxDepth - 1);
         }
         prot.readListEnd();
         break;