Hi Gary,

I will be your submitter for this fix.

I don't think it's worth adding checks for array length being non-negative.

We can add tests to the existing test case
jdk/test/java/io/readBytes/ReadBytesBounds.java

I am providing a patch which has a revised patch and test.

Please forgive me for having refactored everything.
I created an outOfBounds function, suitable for copying and pasting.

I propose to commit this, if Gary and Alan give me thumbs up.

The patch to the test is hard to read.

public class ReadBytesBounds {

    static final FileInputStream fis;
    static final RandomAccessFile raf;
    static final byte[] b = new byte[32];

    static {
        try {
            String dir = System.getProperty("test.src", ".");
            File testFile = new File(dir, "input.txt");
            fis = new FileInputStream(testFile);
            raf = new RandomAccessFile(testFile , "r");
        } catch (Throwable t) {
            throw new Error(t);
        }
    }

    public static void main(String argv[]) throws Throwable {
        byte b[] = new byte[32];
        testRead(-1, -1, false);
        testRead(-1,  0, false);
        testRead( 0, -1, false);
        testRead( 0, 33, false);
        testRead(33,  0, false);
        testRead(33,  4, false);
        testRead( 0, 32, true);
        testRead(32,  0, true);
        testRead(32,  4, false);
        testRead( 4, 16, true);
        testRead( 1, 31, true);
        testRead( 0,  0, true);
        testRead(31,  Integer.MAX_VALUE, false);
        testRead( 0,  Integer.MAX_VALUE, false);
        testRead(-1,  Integer.MAX_VALUE, false);
        testRead(-4,  Integer.MIN_VALUE, false);
        testRead( 0,  Integer.MIN_VALUE, false);
    }

    static void testRead(int off, int len, boolean expected) throws Throwable {
        System.err.printf("off=%d len=%d expected=%b%n", off, len, expected);
        boolean result;
        try {
            fis.read(b, off, len);
            raf.read(b, off, len);
            result = true;
        } catch (IndexOutOfBoundsException e) {
            result = false;
        }

        if (result != expected) {
            throw new RuntimeException
                (String.format("Unexpected result off=%d len=%d expected=%b",
                               off, len, expected));
        }
    }
}

Martin

On Tue, Jan 6, 2009 at 08:38, Gary Benson <gben...@redhat.com> wrote:
> Hi Martin,
>
> I like your method of avoiding the overflow, it's a nice idea.
> I've attached an updated version of my original patch, with that,
> and with an expanded comment too, to make sure the fix doesn't
> get reverted later on in the interests of readability or whatever.
>
> Can I ask that you file a seperate bug for your other changes?
> They're not specifically related to 6788196, and I feel it
> confuses the issue somewhat having a bunch of unrelated changes
> in the patch.
>
> Cheers,
> Gary
>
> Martin Buchholz wrote:
>> I have sympathy for Gary's reluctance to use jlong,
>> even though we all know that here the performance of 64-bit
>> operands doesn't matter.
>>
>> I propose an alternate patch, which avoids the overflow problem
>> by simply rearranging the operands, and adds other pedantic
>> improvements.
>>
>> I believe it may not be 100% portable to replace
>>   (len < 0) || (off < 0)
>> with
>>   ((len | off) < 0)
>> I'll leave that optimization to the compiler.
>>
>> diff --git a/src/share/native/java/io/io_util.c
>> b/src/share/native/java/io/io_util.c
>> --- a/src/share/native/java/io/io_util.c
>> +++ b/src/share/native/java/io/io_util.c
>> @@ -25,6 +25,7 @@
>>
>>  #include <stdlib.h>
>>  #include <string.h>
>> +#include <stddef.h>
>>
>>  #include "jni.h"
>>  #include "jni_util.h"
>> @@ -34,9 +35,9 @@
>>
>>  /* IO helper functions */
>>
>> -int
>> +jint
>>  readSingle(JNIEnv *env, jobject this, jfieldID fid) {
>> -    int nread;
>> +    jint nread;
>>      char ret;
>>      FD fd = GET_FD(this, fid);
>>      if (fd == -1) {
>> @@ -59,13 +60,14 @@ readSingle(JNIEnv *env, jobject this, jf
>>  #define BUF_SIZE 8192
>>
>>
>> -int
>> +jint
>>  readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
>>            jint off, jint len, jfieldID fid)
>>  {
>> -    int nread, datalen;
>> +    jint nread;
>> +    jsize datalen;
>>      char stackBuf[BUF_SIZE];
>> -    char *buf = 0;
>> +    char *buf = NULL;
>>      FD fd;
>>
>>      if (IS_NULL(bytes)) {
>> @@ -74,8 +76,10 @@ readBytes(JNIEnv *env, jobject this, jby
>>      }
>>      datalen = (*env)->GetArrayLength(env, bytes);
>>
>> -    if ((off < 0) || (off > datalen) ||
>> -        (len < 0) || ((off + len) > datalen) || ((off + len) < 0)) {
>> +    if ((off < 0) ||
>> +        (len < 0) ||
>> +        /* Avoid undefined signed integer overflow. */
>> +        (datalen - off < len)) {
>>          JNU_ThrowByName(env, "java/lang/IndexOutOfBoundsException", 0);
>>          return -1;
>>      }
>> @@ -84,7 +88,7 @@ readBytes(JNIEnv *env, jobject this, jby
>>          return 0;
>>      } else if (len > BUF_SIZE) {
>>          buf = malloc(len);
>> -        if (buf == 0) {
>> +        if (buf == NULL) {
>>              JNU_ThrowOutOfMemoryError(env, 0);
>>              return 0;
>>          }
>> @@ -118,7 +122,7 @@ void
>>  void
>>  writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
>>      char c = byte;
>> -    int n;
>> +    jint n;
>>      FD fd = GET_FD(this, fid);
>>      if (fd == -1) {
>>          JNU_ThrowIOException(env, "Stream Closed");
>> @@ -134,11 +138,12 @@ writeSingle(JNIEnv *env, jobject this, j
>>
>>  void
>>  writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
>> -          jint off, jint len, jfieldID fid)
>> +           jint off, jint len, jfieldID fid)
>>  {
>> -    int n, datalen;
>> +    jint n;
>> +    jsize datalen;
>>      char stackBuf[BUF_SIZE];
>> -    char *buf = 0;
>> +    char *buf = NULL;
>>      FD fd;
>>
>>      if (IS_NULL(bytes)) {
>> @@ -147,8 +152,10 @@ writeBytes(JNIEnv *env, jobject this, jb
>>      }
>>      datalen = (*env)->GetArrayLength(env, bytes);
>>
>> -    if ((off < 0) || (off > datalen) ||
>> -        (len < 0) || ((off + len) > datalen) || ((off + len) < 0)) {
>> +    if ((off < 0) ||
>> +        (len < 0) ||
>> +        /* Avoid undefined signed integer overflow. */
>> +        (datalen - off < len)) {
>>          JNU_ThrowByName(env, "java/lang/IndexOutOfBoundsException", 0);
>>          return;
>>      }
>> @@ -157,7 +164,7 @@ writeBytes(JNIEnv *env, jobject this, jb
>>          return;
>>      } else if (len > BUF_SIZE) {
>>          buf = malloc(len);
>> -        if (buf == 0) {
>> +        if (buf == NULL) {
>>              JNU_ThrowOutOfMemoryError(env, 0);
>>              return;
>>          }
>> @@ -196,19 +203,19 @@ throwFileNotFoundException(JNIEnv *env,
>>  throwFileNotFoundException(JNIEnv *env, jstring path)
>>  {
>>      char buf[256];
>> -    int n;
>> +    jint n;
>>      jobject x;
>>      jstring why = NULL;
>>
>>      n = JVM_GetLastErrorString(buf, sizeof(buf));
>>      if (n > 0) {
>> -    why = JNU_NewStringPlatform(env, buf);
>> +        why = JNU_NewStringPlatform(env, buf);
>>      }
>>      x = JNU_NewObjectByName(env,
>>                  "java/io/FileNotFoundException",
>>                  "(Ljava/lang/String;Ljava/lang/String;)V",
>>                  path, why);
>>      if (x != NULL) {
>> -    (*env)->Throw(env, x);
>> +        (*env)->Throw(env, x);
>>      }
>>  }
>> diff --git a/src/share/native/java/io/io_util.h
>> b/src/share/native/java/io/io_util.h
>> --- a/src/share/native/java/io/io_util.h
>> +++ b/src/share/native/java/io/io_util.h
>> @@ -38,9 +38,9 @@ extern jfieldID IO_handle_fdID;
>>   * IO helper functions
>>   */
>>
>> -int readSingle(JNIEnv *env, jobject this, jfieldID fid);
>> -int readBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off,
>> -              jint len, jfieldID fid);
>> +jint readSingle(JNIEnv *env, jobject this, jfieldID fid);
>> +jint readBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off,
>> +               jint len, jfieldID fid);
>>  void writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid);
>>  void writeBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off,
>>                  jint len, jfieldID fid);
>>
>> Martin
>
6788196: (porting) Bounds checks in io_util.c rely on undefined behaviour
Reviewed-By: alanb
Contributed-By: gben...@redhat.com

diff --git a/src/share/native/java/io/io_util.c b/src/share/native/java/io/io_util.c
--- a/src/share/native/java/io/io_util.c
+++ b/src/share/native/java/io/io_util.c
@@ -58,12 +58,24 @@
  */
 #define BUF_SIZE 8192
 
+/*
+ * Returns true if the array slice defined by the given offset and length
+ * is out of bounds.
+ */
+static int
+outOfBounds(JNIEnv *env, jint off, jint len, jbyteArray array) {
+    return ((off < 0) ||
+            (len < 0) ||
+            // We are very careful to avoid signed integer overflow,
+            // the result of which is undefined in C.
+            ((*env)->GetArrayLength(env, array) - off < len));
+}
 
 int
 readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
           jint off, jint len, jfieldID fid)
 {
-    int nread, datalen;
+    int nread;
     char stackBuf[BUF_SIZE];
     char *buf = 0;
     FD fd;
@@ -72,10 +84,8 @@
         JNU_ThrowNullPointerException(env, 0);
         return -1;
     }
-    datalen = (*env)->GetArrayLength(env, bytes);
 
-    if ((off < 0) || (off > datalen) ||
-        (len < 0) || ((off + len) > datalen) || ((off + len) < 0)) {
+    if (outOfBounds(env, off, len, bytes)) {
         JNU_ThrowByName(env, "java/lang/IndexOutOfBoundsException", 0);
         return -1;
     }
@@ -136,7 +146,7 @@
 writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
           jint off, jint len, jfieldID fid)
 {
-    int n, datalen;
+    int n;
     char stackBuf[BUF_SIZE];
     char *buf = 0;
     FD fd;
@@ -145,10 +155,8 @@
         JNU_ThrowNullPointerException(env, 0);
         return;
     }
-    datalen = (*env)->GetArrayLength(env, bytes);
 
-    if ((off < 0) || (off > datalen) ||
-        (len < 0) || ((off + len) > datalen) || ((off + len) < 0)) {
+    if (outOfBounds(env, off, len, bytes)) {
         JNU_ThrowByName(env, "java/lang/IndexOutOfBoundsException", 0);
         return;
     }
diff --git a/test/java/io/readBytes/ReadBytesBounds.java b/test/java/io/readBytes/ReadBytesBounds.java
--- a/test/java/io/readBytes/ReadBytesBounds.java
+++ b/test/java/io/readBytes/ReadBytesBounds.java
@@ -23,7 +23,7 @@
 
 /*
   @test
-  @bug 4017728 4079849
+  @bug 4017728 4079849 6788196
   @summary Check for correct Array Bounds check in read of FileInputStream and
   RandomAccessFile
   */
@@ -42,87 +42,57 @@
 
 public class ReadBytesBounds {
 
-    public static void main(String argv[]) throws Exception{
+    static final FileInputStream fis;
+    static final RandomAccessFile raf;
+    static final byte[] b = new byte[32];
 
-        int num_test_cases = 12;
-        int off[] =     {-1 , -1 ,  0 , 0  , 33 , 33 , 0  , 32 , 32 , 4  , 1  , 0};
-        int len[] =     {-1 ,  0 , -1 , 33 , 0  , 4  , 32 , 0  , 4  , 16 , 31 , 0};
-        boolean results[] = { false ,  false ,  false , false  , false  , false  ,
-                              true  , true  , false  , true  , true  , true};
+    static {
+        try {
+            String dir = System.getProperty("test.src", ".");
+            File testFile = new File(dir, "input.txt");
+            fis = new FileInputStream(testFile);
+            raf = new RandomAccessFile(testFile , "r");
+        } catch (Throwable t) {
+            throw new Error(t);
+        }
+    }
 
+    public static void main(String argv[]) throws Throwable {
+        byte b[] = new byte[32];
+        testRead(-1, -1, false);
+        testRead(-1,  0, false);
+        testRead( 0, -1, false);
+        testRead( 0, 33, false);
+        testRead(33,  0, false);
+        testRead(33,  4, false);
+        testRead( 0, 32, true);
+        testRead(32,  0, true);
+        testRead(32,  4, false);
+        testRead( 4, 16, true);
+        testRead( 1, 31, true);
+        testRead( 0,  0, true);
+        testRead(31,  Integer.MAX_VALUE, false);
+        testRead( 0,  Integer.MAX_VALUE, false);
+        testRead(-1,  Integer.MAX_VALUE, false);
+        testRead(-4,  Integer.MIN_VALUE, false);
+        testRead( 0,  Integer.MIN_VALUE, false);
+    }
 
-        FileInputStream fis = null;
-        RandomAccessFile raf = null;
-        byte b[] = new byte[32];
-
-        int num_good = 0;
-        int num_bad = 0;
-
-        String dir = System.getProperty("test.src", ".");
-        File testFile = new File(dir, "input.txt");
-        fis = new FileInputStream(testFile);
-        for(int i = 0; i < num_test_cases; i++) {
-
-            try {
-                int bytes_read = fis.read(b , off[i] , len[i]);
-            } catch(IndexOutOfBoundsException aiobe) {
-                if (results[i]) {
-                    throw new RuntimeException("Unexpected result");
-                }
-                else {
-                    num_good++;
-                }
-                continue;
-            }
-
-            if (results[i]) {
-                num_good++;
-            }
-            else {
-                throw new RuntimeException("Unexpected result");
-            }
-
-        }
-        System.out.println("Results for FileInputStream.read");
-        System.out.println("\nTotal number of test cases = " + num_test_cases +
-                           "\nNumber succeded = " + num_good +
-                           "\nNumber failed   = " + num_bad);
-
-
-
-        num_good = 0;
-        num_bad = 0;
-
-        raf = new RandomAccessFile(testFile , "r");
-        for(int i = 0; i < num_test_cases; i++) {
-
-            try {
-                int bytes_read = raf.read(b , off[i] , len[i]);
-            } catch(IndexOutOfBoundsException aiobe) {
-                if (results[i]) {
-                    throw new RuntimeException("Unexpected result");
-                }
-                else {
-                    num_good++;
-                }
-                continue;
-            }
-
-            if (results[i]) {
-                num_good++;
-            }
-            else {
-                throw new RuntimeException("Unexpected result");
-            }
-
+    static void testRead(int off, int len, boolean expected) throws Throwable {
+        System.err.printf("off=%d len=%d expected=%b%n", off, len, expected);
+        boolean result;
+        try {
+            fis.read(b, off, len);
+            raf.read(b, off, len);
+            result = true;
+        } catch (IndexOutOfBoundsException e) {
+            result = false;
         }
 
-        System.out.println("Results for RandomAccessFile.read");
-        System.out.println("\nTotal number of test cases = " + num_test_cases +
-                           "\nNumber succeded = " + num_good +
-                           "\nNumber failed   = " + num_bad);
-
-
+        if (result != expected) {
+            throw new RuntimeException
+                (String.format("Unexpected result off=%d len=%d expected=%b",
+                               off, len, expected));
+        }
     }
-
 }

Reply via email to