I can inspect private instance field!!? WHY!!?

suppose I have the following classes

class C { private int x = 10;}

Written by AA. It is compiled and only the C.class file is sent to BB.

BB wrote two classes

class D extends C { int x=20;}

class E {...D d = new D(); System.out.println (d.x);..}

after C.class is received, BB compiled class E and run it. The result is 20. Then BB deleted D.java and D.class from his directroy and modified class D into:

class D extends C {}

new D.java is recompiled only.

Then running E would produce 10.

In other words, the private instance field x in class C is inspected.

Could any one explain it to me? Thank you very much!

chen chen

[717 byte] By [nehcnehcC] at [2007-9-26 9:07:40]
# 1

Scary but it really works... Here's my "java -version":

java version "1.3.1_01"

Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_01)

Java HotSpot(TM) Client VM (build 1.3.1_01, mixed mode)

Micro$oft's VM doesn't let you do that, by the way. I wonder if there's a bug report on this already?

jsalonen at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 2

> suppose I have the following classes

> class C { private int x = 10;}

> Written by AA. It is compiled and only the C.class

> file is sent to BB.

>

> BB wrote two classes

> class D extends C { int x=20;}

> class E {...D d = new D(); System.out.println

> (d.x);..}

>

> after C.class is received, BB compiled class E and run

> it. The result is 20. Then BB deleted D.java and

> D.class from his directroy and modified class D into:

> class D extends C {}

> new D.java is recompiled only.

> Then running E would produce 10.

> In other words, the private instance field x in class

> C is inspected.

> Could any one explain it to me? Thank you very much!

>

> chen chen

Class D has a different x than class C.

If you write a public method on C revealing x,(such as public int getX() {return x;};)

the method would return the value of C's x, not D's. This is the way IBM's VAJava 3.0 worked for me when I tried it.

I believe that Microsoft's compiler is wrong in refusing to compile an "overridden" private member.

bhackmann at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 3

>If you write a public method on C revealing x,(such as public int getX() {return x;};)

>the method would return the value of C's x, not D's

True, but I don't see how that is relevant. The problem is that by recompiling one gains access to a private member of another class. And as best as I can make out via the JVM spec that shouldn't be allowed. Consequently it would appear to be a bug.

jschell at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 4
I missed part of the problem statement;VA Java correctly identifies the problem when D.x is removed; There would seem to be a problem with your compiler.Hackmann
bhackmann at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 5

Please allow me to restate the problem.

First, this file(with the 3 source files in it) is attempted to be compiled.

class E{

public static void main(String[]args){

D d = new D();

System.out.println(d.x);}

}

class C{private int x = 10;}

class D extends C {}

//

C:\WINDOWS\Desktop javac E.java

E.java:4: x has private access in C

System.out.println(d.x);}

^

1 compiler error

Then, this file(as follows) is compiled(with the 3 sources).

class E{

public static void main(String[]args){

D d = new D();

System.out.println(d.x);}

}

class C{private int x = 10;}

class D extends C {int x = 20;}

That program is run(by typing >java E) and gives the output == 20

Everything so far is as we expected.

Next, the file D.class is deleted.

Then a new D.java is created as follows.

class D extendsC{}

This is compiled by its self and now the new D.class is

substituted for the previous one(into the common directory).

Now, when we run the program the output == 10

EXPERTS! Please explain and restore our faith in Java.

w234 at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 6

> VA Java correctly identifies the problem when D.x is

> removed; There would seem to be a problem with your

> compiler.

Not completely true. VA4Java helpfully recompiles all classes that depended on class D when it is changed - this problem arises when class E is not recompiled to notice the change in D.

The desired behaviour in this case, IMHO, is that a runtime error should be thrown saying that the referenced field does not exist in the class table.

This bug is listed in the java bug database, after some searching...

http://developer.java.sun.com/developer/bugParade/bugs/4293149.html

Basically, it seems like it was first reported about nearly two years (!!) ago, and is currently scheduled (?) to be fixed in Java 1.5. Or something.

This is, however, a good reason why to be very careful about public APIs, especially in cases where you may not be able to get clients to recompile against new versions, and a strong argument for composition over extension. (That is, extension is 'bad' because it breaks encapsulation at the best of times)

Restore faith? Sorry I couldn't, but think of it as a chance to 'improve' the robustness of your applications by using a composition technique :-) Or something...

Regards,

-Troy

fiontan at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 7
To "chen chen", thanks for bring this to light.To "Troy", thanks for doing the research. But I don't understand what you meant by>composition technique Could you or someone demostrate how to protect our "privates" ?
w234 at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 8
>VA Java correctly identifies the problem when D.x is removed; >There would seem to be a problem with your compiler.Nope. The problem is with the JVM, not the compiler.
jschell at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 9
> This bug is listed in the java bug database,> after some searching...> http://developer.java.sun.com/developer/bugParade/bugs/> 293149.htmlPlease do go there and vote for it.
msundman at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 10

> Could you or someone demostrate how to protect

> our "privates" ?

Other than wearing a box/cup? But I digress...

Below is a small example contrasting extension and composition, adapted from Joshua Bloch's Effective Java Programming Language Guide...

The following interface and concrete implementation are provided to you, with or without source.

public interface List {

public void add(Object element);

public void addAll(Object[] elements);

}

public class ArrayList implements List {

public void add(Object element) {

// some kind of add logic

}

public void addAll(Object[] elements) {

for (int i = 0, n = elements.length; i < n; i++) {

add(elements[ i]);

}

}

}

You wanted to extend this class to count the number of additions made to the list. The first try using extension...

public class CountingList extends ArrayList {

private int additions = 0;

public void add(Object element) {

additions++;

super.add(element);

}

public void addAll(Object[] elements) {

additions += elements.length;

super.add(element);

}

}

If you note, however, this implementation will not work - if you call addAll(new Object[] {"one", "two"}), it will increment additions by two, then in turn increment additions twice more through calls to add. Two solutions are to reimplement addAll in the CountingList (BAD), or to assume that addAll will iteratively call add, and so not increment additions in addAll (BAD if this detail ever changes).

That is, using extension, CountingList is dependent on the implementation of List. All of the benefits of encapsulation have been lost. To contrast, using composition...

public class CountingList implements List {

private int additions = 0;

private final List list;

public CountingList(List list) {

this.list = list;

}

public void add(Object element) {

additions++;

list.add(element);

}

public void addAll(Object[] elements) {

additions += elements.length;

list.addAll(elements);

}

}

This is also an example of the Decorator pattern, and in the example above it is based on the fact that CountingList is a kind of List, while it just happens to use an ArrayList to implement its List functionality.

Of course, this is only valid in situations where an enclosing interface is available - it would be possible to extend the base class and use composition as well, but this will essentially double the size of the object in memory. It will also probably not work properly where the base class forwards references to this as a callback mechanism, as the wrapper 'base' class will be bypassed by any callbacks.

This unfortunately doesn't alleviate the bug highlighted in the original post, since it does not prevent clients from maliciously gaining access to private members of distributed classes, as long as they can guess the name of the private member. Has anyone tested if this technique works for methods as well?

Regards,

-Troy

Afterthought...

I guess you could hide private members inside a private static inner class... there would be no way to extend the class to exploit this bug. Don't know if I'd recommend it unless it was appropriate to the implementation though. Then again, if it's all private, then it's implementation detail and so up to the developer, so whatever works...

I can see it now...

class C {

private static Cup {

public int x = 10;

}

private Cup protector = new Cup();

public int getX() {

return protector.x;

}

}

fiontan at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 11

If you have:

class C

{

private int x;

}

class D extends C

{

int x=20;

}

Then there are two variables called x, not one. The second one, defined in D, has package visibility (because there is no access modifier), which is why it can be seen from other classes.

It's a simple rule: wherever you specify the variable's type, i.e., 'int x', you are redeclaring it, and it cannot be the same as any previously declared variable.

ricky_clarkson at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 12
ricky_clarkson said>Then there are two variables called x, not one. The second one, defined in D, has package visibility...You might want to re-read this thread. Everyone agrees with what you just said. That is not the problem.
jschell at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 13

> To "chen chen", thanks for bring this to light.

> To "Troy", thanks for doing the research. But I don't

> understand what you meant by

> >composition technique

> Could you or someone demostrate how to protect our

> "privates" ?

Try using java -verify YourClass. That'll force runtime verification of bytecode. Very useful when running untrusted code :)

moonsaint at 2007-7-1 20:14:35 > top of Java-index,Core,Core APIs...
# 14
Thanks for resurrecting a 2-year-old thread.
warnerja at 2007-7-1 20:14:36 > top of Java-index,Core,Core APIs...
# 15
> Thanks for resurrecting a 2-year-old thread.:) The bug is approaching it's 4th birthday. We should all chip in and buy it something nice.... Who's got the card? I've got to sign it, still.
dmbdmb at 2007-7-1 20:14:36 > top of Java-index,Core,Core APIs...
# 16
Thanks for keeping it alive :)
moonsaint at 2007-7-1 20:14:36 > top of Java-index,Core,Core APIs...