🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

6 total messages Started by David Griswold Thu, 16 Nov 1995 18:14
Fundamental flaw in Java library distribution scheme
#50
Author: David Griswold
Date: Thu, 16 Nov 1995 18:14
136 lines
6723 bytes
Fundamental flaw in Java library distribution scheme
----------------------------------------------------

One of the major claims made in the Java white paper
(http://java.sun.com/1.0alpha3/doc/overview/java/index.html) and other
publications is that it will allow a true "Software IC" market to develop,
by solving the "third-party library" problem so that vendors can transparently 
distribute new versions of libraries without requiring recompilation 
of client programs (and as a corollary, that you don't need makefiles). 
In other words, it is claimed that Java solves the "fragile interface" and
"fragile superclass" problems, which are significant problems in C++.

This claim is not true.  After some investigation, it appears that programs
depending on this feature can behave incorrectly (i.e. not according to the
language specification).
In addition,
	- this faulty behavior occurs silently, and is not detected 
	  at runtime.
	- this behavior can violate interface encapsulation, so that
	  Java objects can not be relied on as secure 'capabilities' 
	  (in OS parlance), as claimed.  
  	- these problems are not fixable without significant changes to the 
  	  Java binary distribution standard.  The fixes required would 
  	  probably significantly increase the size of Java binary files (and
	  slow downloads). The changes required would also require breaking
	  upward compatibility with the beta release of the Java
	  distribution standard, which is claimed to be final and stable.


The Details
-----------

The problem, which can occur in several forms, is caused by an 
interaction between static typing and Java's signature-based runtime linking.
Consider the following example:

	class BankAccount {
		double savingsBalance;
		public BankAccount savingsDeposit(double amount) {
			savingsBalance = savingsBalance + amount;
			return this;
		}
	}

	class DeluxeAccount extends BankAccount {
		double checkingBalance;
		// ...
	}

Note that savingsDeposit() returns a BankAccount (perhaps other 
implementations might allocate a new account on every deposit, so that
the accounts are immutable and could be used in an audit trail).

Now suppose there is a method in a client library somewhere that
takes a DeluxeAccount and deposits some money into savings:
	...
		saveALittle(DeluxeAccount acct) {
			acct.savingsDeposit(1.00d);
		}

So far, everything is fine.  But suppose a new version of the bank account
library is distributed, that adds a new instance variable to keep track
of the total balance for deluxe accounts:
	...
	class DeluxeAccount extends BankAccount {
		double checkingBalance;
		double totalBalance;
		public DeluxeAccount savingsDeposit(double amount) {
			super.savingsDeposit(amount);
			totalBalance = totalBalance + amount;
			return this;
		}
		// ...
	}

To update the totalBalance, we have overridden the savingsDeposit method.
Note that we have specialized the return type to be DeluxeAccount (because
that is what the receiver is). 

If the saveALittle() method is executed (without recompiling) with this new 
library, a corrupted (wrong) totalBalance will result.  This occurs because
at runtime, the lookup process looks for a method with a signature that
EXACTLY matches the statically determined type of the method, for security
reasons.  Since the static type of the savingsDeposit() method was
BankAccount when saveALittle() was compiled, the overridden version of 
savingsDeposit() doesn't match, and is (erroneously) not found.  

Recompiling the compilation unit containing saveALittle() will produce a 
program that behaves differently, since the signature is different when it 
is recompiled.

The nasty thing about this is that the version of the method inherited
from BankAccount is NOT ACCESSIBLE to clients of a DeluxeAccount,
according to the language specification.  This means that supposedly, we
can count on the fact that there is no way the totalBalance could be
corrupted by clients of a DeluxeAccount.  This 'absolute' interface security
is the basis for the claim that Java objects can be used as
"capabilities".  But in the above example, object encapsulation
is violated.

Note that in this example, the signatures involved are for an overridden
method, where the only change in the signature is to a return type.
With some work, this could probably be fixed by changing the lookup
process to look for methods that have a return type that is the same OR
A SUBTYPE of the expected signature.  

Unfortunately, the problem has consequences that go further, and that
are far harder to fix.  A similar situation can occur when different argument
types are involved, which means that the methods are overloaded, instead of 
overridden.  In that case, it is not simply a matter of detecting signature
changes for a single method.  The problem here is that the client might
actually PICK A DIFFERENT METHOD ALTOGETHER if it was recompiled, based on
the overloading resolution rules.  In this case, the method signature
found at compile time is simply inadequate to find a matching method at
runtime.


Impact
------

Fixing this problem would require doing overload resolution at load time, 
which in addition to being slower, is not possible given the information
accessible in the (supposedly stable) Java binary distribution standard.
The exact static types of all actual arguments are required, which are 
not available at runtime (as opposed to the formal signature of the method 
invoked, which is what Java currently keeps).  This means that there is 
simply not enough information to solve the problem in the distribution 
format for Java.  Adding full actual argument type information to the 
binary form would allow runtime resolution to be done, but this would 
have a real size (and download delay) impact, since there are far more 
calls than there are method signatures called.

While it is marginally ok for decorative applets to "usually" behave
correctly, this is an unacceptable property if Java is to be used for
mission critical software.  It looks like some rethinking will have
to be done.
Re: Fundamental flaw in Java library distribution scheme
#52
Author: Anthony Tomasic
Date: Fri, 17 Nov 1995 09:39
20 lines
1423 bytes
A partial solution to this problem appears in 

@InProceedings{ss94,
  author = 	 "Schwarz, Peter and Shoens, Kurt",
  title = 	 "Managing Change in the {Rufus} System",
  booktitle =	 "Proceedings of the IEEE International Conference on Data
		  Engineering",
  year =	 1994
}

Essentially a new release is considered a new version (in the database sense)
of the signatures of methods.  Object created under the old release continue
to access old versions of the code (unless the new version ``patches'' the
old version -- this is used only for bug fixes).  New objects access new
versions.  There are some other differences, since Rufus has an implicit
type hierarchy, but perhaps the idea can be adopted to Java. 
Unfortunately, the old release must be kept around until all objects
reference only the new release.
-- 
Anthony.Tomasic@inria.fr
Re: Fundamental flaw in Java library distribution scheme
#56
Author: David Griswold
Date: Thu, 23 Nov 1995 01:12
38 lines
2101 bytes
Jacob Weintraub wrote:
> 
> I believe that there are two misunderstandings here.
> 
> 1) Method lookup on overriden functions is based solely on the
> arguments to the function, not on the return type.  There is a
> requirement that "The return type of an overriding method be
> assignable to the return type of the overridden method."
> 
> 2) Overridden methods of a superclass can be called by
> using the super.methodname() syntax.
> 
> See 6.4.9 and 9.7 in the beta documentation.
> 

No, there are no misunderstandings.

1) Semantically, you are right, but that has nothing to do with
my point.  That point is that the
current lookup mechanism does not implement the
semantics correctly.  In the current java implementation,
the return type IS used (wrongly) during lookup, causing 
the override bug I describe.  As I pointed out, this form
of the problem easy to fix (but is still a nasty hole in the
current system).

2) I understand how to use super.  The point is that people
outside a class should NEVER be able to call the overridden
method directly; that violates the object's design integrity
(and makes it more fragile for future changes).
Invoking it from within the object intentionally using super is
an entirely different operation.

More importantly:
You may have missed the little paragraph at the end mentioning
the overload form of the problem.  It can be PROVED that the
overload form of the problem CANNOT be solved without
changing the .class distribution file format.
Re: Fundamental flaw in Java library distribution scheme
#53
Author: David Griswold
Date: Thu, 23 Nov 1995 01:20
9 lines
888 bytes
Anthony Tomasic wrote:
>[how a versioning scheme could be used to solve the problem]

Yes, of course you are right, and the sort of thing you discuss
is probably the right way to tackle the problem.  My point
is that Sun has been denying that they need any such scheme,
whereas they definitely do.

-Dave
Re: Fundamental flaw in Java library distribution scheme
#55
Author: David Strauss
Date: Thu, 23 Nov 1995 21:43
41 lines
2187 bytes
In article <48hl8o$mfq@news-rocq.inria.fr>, tomasic@monica.inria.fr says...
>
>A partial solution to this problem appears in 
>
>@InProceedings{ss94,
>  author =       "Schwarz, Peter and Shoens, Kurt",
>  title =        "Managing Change in the {Rufus} System",
>  booktitle =    "Proceedings of the IEEE International Conference on Data
>                  Engineering",
>  year =         1994
>}
>
>Essentially a new release is considered a new version (in the database sense)
>of the signatures of methods.  Object created under the old release continue
>to access old versions of the code (unless the new version ``patches'' the
>old version -- this is used only for bug fixes).  New objects access new
>versions.  There are some other differences, since Rufus has an implicit
>type hierarchy, but perhaps the idea can be adopted to Java. 
>Unfortunately, the old release must be kept around until all objects
>reference only the new release.
>-- 



What's meant here by "objects created under the <old, new> version"?  If 
"created under" means compiled under then the problem isn't solved at all,
since the problem is precisely the need to re-compile.
If "created under" is a meant as a run-time thing then the question is
being begged -- the point made in the original post is that an object
"running under" the new release is going to resolve to a method that
has been overridden in that new release.




-- 
------------------------------------------------
David Strauss
E-mail: dstrauss@popserver.panix.com 
              dstrauss@lehman.com 
-------------------------------------------------
Re: Fundamental flaw in Java library distribution scheme
#57
Author: Pierre Baillargeon
Date: Wed, 29 Nov 1995 14:02
30 lines
2124 bytes
: Impact
: ------

: Fixing this problem would require doing overload resolution at load time, 
: which in addition to being slower, is not possible given the information
: accessible in the (supposedly stable) Java binary distribution standard.
: The exact static types of all actual arguments are required, which are 
: not available at runtime (as opposed to the formal signature of the method 
: invoked, which is what Java currently keeps).  This means that there is 
: simply not enough information to solve the problem in the distribution 
: format for Java.  Adding full actual argument type information to the 
: binary form would allow runtime resolution to be done, but this would 
: have a real size (and download delay) impact, since there are far more 
: calls than there are method signatures called.

Of course the fast and easy way to correct this is to disallow automatic
conversion of arguments (always say what you mean) and disallow covariance
of returned type (always do what you say). Two fixes that would make the
language cleaner IMHO.

Also, let's not confuse the Java language and the Java environment. And
anyway, anyone who has ever updated softwares know that even if you comply
with interface, nothing ganrantee that the actual implementation code is
correct and compatible with all unspecified assumption of the calling
code. Nothing beats testing.

: While it is marginally ok for decorative applets to "usually" behave
: correctly, this is an unacceptable property if Java is to be used for
: mission critical software.  It looks like some rethinking will have
: to be done.
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads