[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--