From 350011f5816bb5c9d8d4ef22f93b5989af4763d7 Mon Sep 17 00:00:00 2001
From: Wouter van Oortmerssen <wvo@google.com>
Date: Tue, 1 Jul 2014 17:56:12 -0700
Subject: [PATCH] Fixed a bugs in the Java runtime that could cause an index
 out of bounds exception.

Tested: on Windows.

Change-Id: I0d4cdafc21690eb9a509ba31f21e80dacfb602ff
---
 java/flatbuffers/FlatBufferBuilder.java | 20 +++++++++++++-------
 src/idl_gen_java.cpp                    | 16 ++++++++++------
 tests/JavaTest.bat                      |  2 +-
 tests/JavaTest.java                     |  7 +++++--
 tests/MyGame/Example/Any.java           |  5 -----
 tests/MyGame/Example/Color.java         |  5 -----
 tests/MyGame/Example/Test.java          |  2 +-
 tests/MyGame/Example/Vec3.java          |  4 ++--
 8 files changed, 32 insertions(+), 29 deletions(-)

diff --git a/java/flatbuffers/FlatBufferBuilder.java b/java/flatbuffers/FlatBufferBuilder.java
index c6864223..ee040178 100755
--- a/java/flatbuffers/FlatBufferBuilder.java
+++ b/java/flatbuffers/FlatBufferBuilder.java
@@ -28,7 +28,7 @@ import java.nio.charset.Charset;
 public class FlatBufferBuilder {
     ByteBuffer bb;       // Where we construct the FlatBuffer.
     int space;           // Remaining space in the ByteBuffer.
-    final Charset utf8charset = Charset.forName("UTF-8");
+    static final Charset utf8charset = Charset.forName("UTF-8");
     int minalign = 1;    // Minimum alignment encountered so far.
     int[] vtable;        // The vtable for the current table, null otherwise.
     int object_start;    // Starting offset of the current struct/table.
@@ -42,6 +42,7 @@ public class FlatBufferBuilder {
 
     // Start with a buffer of size `initial_size`, then grow as required.
     public FlatBufferBuilder(int initial_size) {
+        if (initial_size <= 0) initial_size = 1;
         space = initial_size;
         bb = newByteBuffer(new byte[initial_size]);
     }
@@ -57,7 +58,7 @@ public class FlatBufferBuilder {
     ByteBuffer growByteBuffer(ByteBuffer bb) {
         byte[] old_buf = bb.array();
         int old_buf_size = old_buf.length;
-        if ((old_buf_size & 0xC0000000) != 0)
+        if ((old_buf_size & 0xC0000000) != 0)  // Ensure we don't grow beyond what fits in an int.
             throw new AssertionError("FlatBuffers: cannot grow buffer beyond 2 gigabytes.");
         int new_buf_size = old_buf_size << 1;
         byte[] new_buf = new byte[new_buf_size];
@@ -135,7 +136,7 @@ public class FlatBufferBuilder {
 
     public int createString(String s) {
         byte[] utf8 = s.getBytes(utf8charset);
-        bb.put(--space, (byte)0);
+        addByte((byte)0);
         startVector(1, utf8.length);
         System.arraycopy(utf8, 0, bb.array(), space -= utf8.length, utf8.length);
         return endVector();
@@ -192,12 +193,12 @@ public class FlatBufferBuilder {
         for (int i = vtable.length - 1; i >= 0 ; i--) {
             // Offset relative to the start of the table.
             short off = (short)(vtable[i] != 0 ? vtableloc - vtable[i] : 0);
-            putShort(off);
+            addShort(off);
         }
 
         final int standard_fields = 2; // The fields below:
-        putShort((short)(vtableloc - object_start));
-        putShort((short)((vtable.length + standard_fields) * SIZEOF_SHORT));
+        addShort((short)(vtableloc - object_start));
+        addShort((short)((vtable.length + standard_fields) * SIZEOF_SHORT));
 
         // Search for an existing vtable that matches the current one.
         int existing_vtable = 0;
@@ -245,6 +246,11 @@ public class FlatBufferBuilder {
 
     // The FlatBuffer data doesn't start at offset 0 in the ByteBuffer:
     public int dataStart() {
-        return bb.array().length - offset();
+        return space;
+    }
+
+    // Utility function for copying a byte array that starts at 0.
+    public byte[] sizedByteArray() {
+        return Arrays.copyOfRange(bb.array(), dataStart(), bb.array().length);
     }
 }
diff --git a/src/idl_gen_java.cpp b/src/idl_gen_java.cpp
index 979a80a9..486d2695 100755
--- a/src/idl_gen_java.cpp
+++ b/src/idl_gen_java.cpp
@@ -160,7 +160,8 @@ static void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
 static void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
                           const char *nameprefix) {
   std::string &code = *code_ptr;
-  code += "    builder.prep(" + NumToString(struct_def.minalign) + ", 0);\n";
+  code += "    builder.prep(" + NumToString(struct_def.minalign) + ", ";
+  code += NumToString(struct_def.bytesize) + ");\n";
   for (auto it = struct_def.fields.vec.rbegin();
        it != struct_def.fields.vec.rend();
        ++it) {
@@ -347,7 +348,8 @@ static void GenStruct(StructDef &struct_def,
 // Save out the generated code for a single Java class while adding
 // declaration boilerplate.
 static bool SaveClass(const Parser &parser, const Definition &def,
-                      const std::string &classcode, const std::string &path) {
+                      const std::string &classcode, const std::string &path,
+                      bool needs_imports) {
   if (!classcode.length()) return true;
 
   std::string name_space_java;
@@ -365,8 +367,10 @@ static bool SaveClass(const Parser &parser, const Definition &def,
 
   std::string code = "// automatically generated, do not modify\n\n";
   code += "package " + name_space_java + ";\n\n";
-  code += "import java.nio.*;\nimport java.lang.*;\nimport java.util.*;\n";
-  code += "import flatbuffers.*;\n\n";
+  if (needs_imports) {
+    code += "import java.nio.*;\nimport java.lang.*;\nimport java.util.*;\n";
+    code += "import flatbuffers.*;\n\n";
+  }
   code += classcode;
   auto filename = name_space_dir + PATH_SEPARATOR + def.name + ".java";
   return SaveFile(filename.c_str(), code, false);
@@ -383,7 +387,7 @@ bool GenerateJava(const Parser &parser,
        it != parser.enums_.vec.end(); ++it) {
     std::string enumcode;
     GenEnum(**it, &enumcode);
-    if (!SaveClass(parser, **it, enumcode, path))
+    if (!SaveClass(parser, **it, enumcode, path, false))
       return false;
   }
 
@@ -391,7 +395,7 @@ bool GenerateJava(const Parser &parser,
        it != parser.structs_.vec.end(); ++it) {
     std::string declcode;
     GenStruct(**it, &declcode, parser.root_struct_def);
-    if (!SaveClass(parser, **it, declcode, path))
+    if (!SaveClass(parser, **it, declcode, path, true))
       return false;
   }
 
diff --git a/tests/JavaTest.bat b/tests/JavaTest.bat
index b8a0b5a0..9ee9c4bd 100755
--- a/tests/JavaTest.bat
+++ b/tests/JavaTest.bat
@@ -17,5 +17,5 @@ rem Compile then run the Java test.
 
 set batch_file_dir=%~d0%~p0
 
-javac -classpath %batch_file_dir%\..\java;%batch_file_dir% JavaTest.java
+javac -g -classpath %batch_file_dir%\..\java;%batch_file_dir% JavaTest.java
 java -classpath %batch_file_dir%\..\java;%batch_file_dir% JavaTest
diff --git a/tests/JavaTest.java b/tests/JavaTest.java
index 86424b18..089e76b5 100755
--- a/tests/JavaTest.java
+++ b/tests/JavaTest.java
@@ -44,9 +44,12 @@ class JavaTest {
         TestBuffer(bb, 0);
 
         // Second, let's create a FlatBuffer from scratch in Java, and test it also.
-        // We set up the same values as monsterdata.json:
+        // We use an initial size of 1 to exercise the reallocation algorithm,
+        // normally a size larger than the typical FlatBuffer you generate would be
+        // better for performance.
+        FlatBufferBuilder fbb = new FlatBufferBuilder(1);
 
-        FlatBufferBuilder fbb = new FlatBufferBuilder(1024);
+        // We set up the same values as monsterdata.json:
 
         int str = fbb.createString("MyMonster");
 
diff --git a/tests/MyGame/Example/Any.java b/tests/MyGame/Example/Any.java
index 9cbafb15..7d5a1a00 100755
--- a/tests/MyGame/Example/Any.java
+++ b/tests/MyGame/Example/Any.java
@@ -2,11 +2,6 @@
 
 package MyGame.Example;
 
-import java.nio.*;
-import java.lang.*;
-import java.util.*;
-import flatbuffers.*;
-
 public class Any {
   public static final byte NONE = 0;
   public static final byte Monster = 1;
diff --git a/tests/MyGame/Example/Color.java b/tests/MyGame/Example/Color.java
index f983c392..44738ea4 100755
--- a/tests/MyGame/Example/Color.java
+++ b/tests/MyGame/Example/Color.java
@@ -2,11 +2,6 @@
 
 package MyGame.Example;
 
-import java.nio.*;
-import java.lang.*;
-import java.util.*;
-import flatbuffers.*;
-
 public class Color {
   public static final byte Red = 0;
   public static final byte Green = 1;
diff --git a/tests/MyGame/Example/Test.java b/tests/MyGame/Example/Test.java
index 1773658b..93261f96 100755
--- a/tests/MyGame/Example/Test.java
+++ b/tests/MyGame/Example/Test.java
@@ -13,7 +13,7 @@ public class Test extends Struct {
   public byte b() { return bb.get(bb_pos + 2); }
 
   public static int createTest(FlatBufferBuilder builder, short a, byte b) {
-    builder.prep(2, 0);
+    builder.prep(2, 4);
     builder.pad(1);
     builder.putByte(b);
     builder.putShort(a);
diff --git a/tests/MyGame/Example/Vec3.java b/tests/MyGame/Example/Vec3.java
index a71cbccd..652cba94 100755
--- a/tests/MyGame/Example/Vec3.java
+++ b/tests/MyGame/Example/Vec3.java
@@ -18,9 +18,9 @@ public class Vec3 extends Struct {
   public Test test3(Test obj) { return obj.__init(bb_pos + 26, bb); }
 
   public static int createVec3(FlatBufferBuilder builder, float x, float y, float z, double test1, byte test2, short Test_a, byte Test_b) {
-    builder.prep(16, 0);
+    builder.prep(16, 32);
     builder.pad(2);
-    builder.prep(2, 0);
+    builder.prep(2, 4);
     builder.pad(1);
     builder.putByte(Test_b);
     builder.putShort(Test_a);
-- 
GitLab