[kaffe] RandomAccessFile causes StackOverflowError
Dalibor Topic
robilad@yahoo.com
Wed Apr 2 03:27:01 2003
--0-1654370434-1049282914=:72678
Content-Type: text/plain; charset=us-ascii
Content-Id:
Content-Disposition: inline
Hi Ito,
--- Ito Kazumitsu <ito.kazumitsu@hitachi-cable.co.jp>
wrote:
>
> In message "Re: [kaffe] RandomAccessFile causes
> StackOverflowError"
> on 03/03/28, Ito Kazumitsu
> <ito.kazumitsu@hitachi-cable.co.jp> writes:
>
> > > The attached program causes StackOverflowError
> when run with
> > > kaffe
>
> > And here is a patch.
>
> The problem is simple.
>
> The attached programs go into an infinite loop as is
> easily seen.
> And kaffe's read() of java.io.RandomAccessFile and
> some others
> have the same kind of bug.
that made it much clearer, thanks.
So in short, the problem is that some IO classes
implement read() or read(char/byte[]) by delegating it
directly to read(char/byte[], int, int) which can be
reimplemented in a subclass to call read() or
read(char/byte []), resulting in a circular sequence
of calls blowing up the stack eventually.
You patch elegantly solves the issue by delegating to
an internal worker method _read(char/byte[], int,
int). I'll check it in tonight.
It would be great if you could review other IO code
and
send me the patches.
In general, the problem can be summed up as:
method M is implemented using overwriteable method N.
Since there is no way to guarantee that N won't be
overwritten to utilize M, the safest way to implement
M and N is to delegate their calls to a private worker
method implementation N' .
I bet there is a lot of code out there that suffers
from this problem ;)
I'm wondering how to write a program that
automatically tests for this bug pattern ... Or is
there some static checker out there that can do this
already?
I've attached some hand written tests as a start. They
have both discovered new bugs.
cheers,
dalibor topic
__________________________________________________
Do you Yahoo!?
Yahoo! Tax Center - File online, calculators, forms, and more
http://tax.yahoo.com
--0-1654370434-1049282914=:72678
Content-Type: text/x-java; name="CircularDelegation.java"
Content-Description: CircularDelegation.java
Content-Disposition: inline; filename="CircularDelegation.java"
/* This class tests if circular read delegation leads to stack overflows
*
* Author: Dalibor Topic <robilad@yahoo.com>
*
* Based on a test case from Ito Kasumitsu.
*/
import java.io.*;
class ZeroInputStream extends InputStream {
public int read() throws IOException {
return 0;
}
}
public class CircularDelegation {
private static final ZeroInputStream ZERO_STREAM = new ZeroInputStream();
private static final InputStream [] ISTREAMS = {
new BufferedInputStream(ZERO_STREAM)
{
public int read() throws IOException {
byte[] b = new byte[1];
if (read(b) != 0) {
return b[0];
}
else {
return -1;
}
}
},
new ByteArrayInputStream(new byte[1024])
{
public int read() {
byte[] b = new byte[1];
try {
if (read(b) != 0) {
return b[0];
}
else {
return -1;
}
}
catch (IOException e) {
e.printStackTrace();
return -1;
}
}
},
new DataInputStream(ZERO_STREAM)
{
public int read() throws IOException {
byte[] b = new byte[1];
if (read(b) != 0) {
return b[0];
}
else {
return -1;
}
}
},
new FilterInputStream(ZERO_STREAM)
{
public int read() throws IOException {
byte[] b = new byte[1];
if (read(b) != 0) {
return b[0];
}
else {
return -1;
}
}
},
new PushbackInputStream(ZERO_STREAM)
{
public int read() throws IOException {
byte[] b = new byte[1];
if (read(b) != 0) {
return b[0];
}
else {
return -1;
}
}
},
new LineNumberInputStream(ZERO_STREAM)
{
public int read() throws IOException {
byte[] b = new byte[1];
if (read(b) != 0) {
return b[0];
}
else {
return -1;
}
}
},
// new FileInputStream("/dev/zero")
// {
// public int read() throws IOException {
// byte[] b = new byte[1];
// if (read(b) != 0) {
// return b[0];
// }
// else {
// return -1;
// }
// }
// },
};
public static void main(String [] args) {
for (int i = 0 ; i < ISTREAMS.length; ++i) {
InputStream istream = ISTREAMS[i];
System.out.println(istream.getClass().getSuperclass());
try {
istream.read();
}
catch (IOException e) {
e.printStackTrace();
}
catch (StackOverflowError e) {
System.out.println("Failed");
}
}
}
}
--0-1654370434-1049282914=:72678
Content-Type: text/x-java; name="CircularDelegation2.java"
Content-Description: CircularDelegation2.java
Content-Disposition: inline; filename="CircularDelegation2.java"
/* This class tests if circular read delegation leads to stack overflows
*
* Author: Dalibor Topic <robilad@yahoo.com>
*
* Based on a test case from Ito Kasumitsu.
*/
import java.io.*;
class ZeroInputStream extends InputStream {
public int read() throws IOException {
return 0;
}
}
public class CircularDelegation2 {
private static final ZeroInputStream ZERO_STREAM = new ZeroInputStream();
private static final InputStream [] ISTREAMS = {
new BufferedInputStream(ZERO_STREAM)
{
public int read(byte [] buf, int off, int len) throws IOException {
buf[0] = (byte) read();
return 1;
}
},
new ByteArrayInputStream(new byte[1024])
{
public int read(byte [] buf, int off, int len) {
buf[0] = (byte) read();
return 1;
}
},
new DataInputStream(ZERO_STREAM)
{
},
new FilterInputStream(ZERO_STREAM)
{
public int read(byte [] buf, int off, int len) throws IOException {
buf[0] = (byte) read();
return 1;
}
},
new PushbackInputStream(ZERO_STREAM)
{
public int read(byte [] buf, int off, int len) throws IOException {
buf[0] = (byte) read();
return 1;
}
},
new LineNumberInputStream(ZERO_STREAM)
{
public int read(byte [] buf, int off, int len) throws IOException {
buf[0] = (byte) read();
return 1;
}
},
};
public static void main(String [] args) {
for (int i = 0 ; i < ISTREAMS.length; ++i) {
InputStream istream = ISTREAMS[i];
System.out.println(istream.getClass().getSuperclass());
try {
istream.read(new byte[1], 0, 1);
}
catch (IOException e) {
e.printStackTrace();
}
catch (StackOverflowError e) {
System.out.println("Failed");
}
}
}
}
--0-1654370434-1049282914=:72678--