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)); + } } - }