Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6478546: FileInputStream.read() throws OutOfMemoryError when there is plenty available #8235

Closed
wants to merge 16 commits into from
89 changes: 57 additions & 32 deletions src/java.base/share/native/libjava/io_util.c
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -45,17 +45,19 @@ readSingle(JNIEnv *env, jobject this, jfieldID fid) {
return -1;
}
nread = IO_Read(fd, &ret, 1);
if (nread == 0) { /* EOF */
if (nread == 0) { // EOF
return -1;
} else if (nread == -1) { /* error */
} else if (nread == -1) { // error
JNU_ThrowIOExceptionWithLastError(env, "Read error");
}
return ret & 0xFF;
}

/* The maximum size of a stack-allocated buffer.
*/
#define BUF_SIZE 8192
// The size of a stack-allocated buffer.
#define STACK_BUF_SIZE 8192

// The maximum size of a dynamically allocated buffer.
#define MAX_MALLOC_SIZE 65536

/*
* Returns true if the array slice defined by the given offset and length
Expand All @@ -74,9 +76,10 @@ jint
readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
jint off, jint len, jfieldID fid)
{
jint nread;
char stackBuf[BUF_SIZE];
char stackBuf[STACK_BUF_SIZE];
bplb marked this conversation as resolved.
Show resolved Hide resolved
char *buf = NULL;
jint buf_size, read_size;
jint n, nread;
FD fd;

if (IS_NULL(bytes)) {
Expand All @@ -91,28 +94,44 @@ readBytes(JNIEnv *env, jobject this, jbyteArray bytes,

if (len == 0) {
return 0;
} else if (len > BUF_SIZE) {
buf = malloc(len);
} else if (len > STACK_BUF_SIZE) {
buf_size = len < MAX_MALLOC_SIZE ? len : MAX_MALLOC_SIZE;
buf = malloc(buf_size);
if (buf == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
return 0;
}
} else {
buf = stackBuf;
buf_size = STACK_BUF_SIZE;
}

fd = getFD(env, this, fid);
if (fd == -1) {
JNU_ThrowIOException(env, "Stream Closed");
nread = -1;
} else {
nread = IO_Read(fd, buf, len);
if (nread > 0) {
(*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf);
} else if (nread == -1) {
JNU_ThrowIOExceptionWithLastError(env, "Read error");
} else { /* EOF */
nread = 0;
while (nread < len) {
read_size = len - nread;
if (read_size > buf_size)
read_size = buf_size;
fd = getFD(env, this, fid);
if (fd == -1) {
JNU_ThrowIOException(env, "Stream Closed");
nread = -1;
break;
}
n = IO_Read(fd, buf, read_size);
if (n > 0) {
(*env)->SetByteArrayRegion(env, bytes, off, n, (jbyte*)buf);
nread += n;
// Exit loop on short read
if (n < read_size)
break;
off += n;
} else if (n == -1) {
JNU_ThrowIOExceptionWithLastError(env, "Read error");
bplb marked this conversation as resolved.
Show resolved Hide resolved
break;
} else { // EOF
if (nread == 0)
nread = -1;
break;
bplb marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -146,9 +165,10 @@ void
writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
jint off, jint len, jboolean append, jfieldID fid)
{
jint n;
char stackBuf[BUF_SIZE];
char stackBuf[STACK_BUF_SIZE];
char *buf = NULL;
jint buf_size, write_size;
jint n;
FD fd;

if (IS_NULL(bytes)) {
Expand All @@ -163,39 +183,44 @@ writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,

if (len == 0) {
return;
} else if (len > BUF_SIZE) {
buf = malloc(len);
} else if (len > STACK_BUF_SIZE) {
buf_size = len < MAX_MALLOC_SIZE ? len : MAX_MALLOC_SIZE;
buf = malloc(buf_size);
if (buf == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
return;
}
} else {
buf = stackBuf;
buf_size = STACK_BUF_SIZE;
}

(*env)->GetByteArrayRegion(env, bytes, off, len, (jbyte *)buf);

if (!(*env)->ExceptionOccurred(env)) {
off = 0;
while (len > 0) {
while (len > 0) {
write_size = len < buf_size ? len : buf_size;
(*env)->GetByteArrayRegion(env, bytes, off, write_size, (jbyte*)buf);
if (!(*env)->ExceptionOccurred(env)) {
bplb marked this conversation as resolved.
Show resolved Hide resolved
fd = getFD(env, this, fid);
if (fd == -1) {
JNU_ThrowIOException(env, "Stream Closed");
break;
}
if (append == JNI_TRUE) {
n = IO_Append(fd, buf+off, len);
n = IO_Append(fd, buf, write_size);
} else {
n = IO_Write(fd, buf+off, len);
n = IO_Write(fd, buf, write_size);
}
if (n == -1) {
JNU_ThrowIOExceptionWithLastError(env, "Write error");
break;
}
off += n;
len -= n;
} else { // ArrayIndexOutOfBoundsException
(*env)->ExceptionClear(env);
break;
}
}

if (buf != stackBuf) {
free(buf);
}
Expand Down
42 changes: 31 additions & 11 deletions test/jdk/java/io/FileInputStream/ReadXBytes.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -26,22 +26,23 @@
* @library /test/lib
* @build jdk.test.lib.RandomFactory
* @run main ReadXBytes
* @bug 8264777
* @summary Test read{All,N}Bytes overrides (use -Dseed=X to set PRNG seed)
* @bug 6478546 8264777
* @summary Test read(byte[],int,int) and read{All,N}Bytes overrides (use -Dseed=X to set PRNG seed)
* @key randomness
*/

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Random;
import jdk.test.lib.RandomFactory;

public class ReadXBytes {
private static final int ITERATIONS = 10;
private static final int MAX_FILE_SIZE = 1_000_000;
private static final int MAX_EXTRA_FILE_SIZE = 1_000_000;
private static final int MIN_LARGE_FILE_SIZE = 2_500_000;
private static final Random RND = RandomFactory.getRandom();

public static void main(String args[]) throws IOException {
Expand Down Expand Up @@ -73,12 +74,30 @@ public static void main(String args[]) throws IOException {
File file = File.createTempFile("foo", "bar", dir);
file.deleteOnExit();

int size = 1 + RND.nextInt(MAX_FILE_SIZE);
int baseSize = i % 2 == 0 ? 1 : MIN_LARGE_FILE_SIZE;
int size = baseSize + RND.nextInt(MAX_EXTRA_FILE_SIZE);
System.out.printf("size %d%n", size);
byte[] bytes = new byte[size];
int offset = RND.nextInt(size/4);
byte[] bytes = new byte[offset + size];
RND.nextBytes(bytes);
try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
raf.write(bytes);
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(bytes, offset, size);
}

try (FileInputStream fis = new FileInputStream(file)) {
int pos = RND.nextInt(size);
int len = RND.nextInt(size - pos);
fis.getChannel().position(pos);
byte[] nbytes = new byte[size];
int n = fis.read(nbytes, 0, 0);
if (n != 0)
throw new RuntimeException("read() zero length");
n = fis.read(nbytes, pos, len);
if (n != len)
throw new RuntimeException("read() length");
if (!Arrays.equals(nbytes, pos, pos + len,
bytes, offset + pos, offset + pos + len))
throw new RuntimeException("read() content");
}

try (FileInputStream fis = new FileInputStream(file)) {
Expand All @@ -91,7 +110,8 @@ public static void main(String args[]) throws IOException {
nbytes = fis.readNBytes(len);
if (nbytes.length != len)
throw new RuntimeException("readNBytes() length");
if (!Arrays.equals(nbytes, 0, len, bytes, pos, pos + len))
if (!Arrays.equals(nbytes, 0, len,
bytes, pos + offset, offset + pos + len))
throw new RuntimeException("readNBytes() content");
}

Expand All @@ -102,7 +122,7 @@ public static void main(String args[]) throws IOException {
if (allbytes.length != size - pos)
throw new RuntimeException("readAllBytes() length");
if (!Arrays.equals(allbytes, 0, allbytes.length,
bytes, pos, pos + allbytes.length))
bytes, offset + pos, offset + pos + allbytes.length))
throw new RuntimeException("readAllBytes() content");
}

Expand Down