Class Circularity Error.

K

Karl Krukow

Hello,
I'm writing a Java Security Manager extension, and in the process, I've
run into a problem that I've never seen before.

The code installs a security manager, and then runs a test-program under
the manager. Basically, the problem is that I'm getting a
ClassCircularityError at runtime:

Exception in thread "main" java.lang.ClassCircularityError:
InductiveQSemantics
at ArrayBasedDMC.New(ArrayBasedDMC.java:49)
at QSecMan.checkPermission(QSecMan.java:48)
at java.lang.SecurityManager.checkRead(SecurityManager.java:871)
....

Now, as far as I understand, a class circularity occurs in a class
"hierarchy" where a class A is a subclass of class B and at the same
time class B is a subclass of class A. Now, normally, the compiler would
ensure that all classes used are cycle-free, so this should be detected
at compile-time. The apparent problem-class "InductiveQSemantics" does
extend another class, but not vice-versa (then the compiler would have
noticed).

I'm not sure that this is important, but note that I'am not using
reflection (at least not directly) in my code, though library code may
use it - but those shouldn't load my classes?

I'm also not sure if this is a bug in the VM, or my code is somehow
flawed. What I would like to know is whether this could be an instance
of a known bug. I tried to trace the bug on bugs.sun.com - and it may be
related to the bug:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4670071
But, I'm really not sure-

Any "expert" help would be appreciated ;-) Regards,
- Karl
(NB. X-posted to comp.lang.programmer)
 
O

Oliver Wong

[SNIP]
Basically, the problem is that I'm getting a ClassCircularityError at
runtime: [SNIP]
I'm also not sure if this is a bug in the VM, or my code is somehow
flawed. What I would like to know is whether this could be an instance of
a known bug. I tried to trace the bug on bugs.sun.com - and it may be
related to the bug:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4670071

Actually, bug 4699981 sounds more like yours
(http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4699981), but this bug
has been marked as "FIXED".

If that doesn't magically solve your problem, I recommend you try to
reduce your code to make it as small as possible while still exhibint the
problem. You can see this page for some advice on doing so:
http://www.physci.org/codes/sscce.jsp

Notice, for example, that in bug 4699981, the submitter of the bug
included 4 small Java files to demonstrate the problem, thus "proving" that
this was a bug in the VM and not in his/her code.

In your case, you do not yet know if the bug is in your code or if it is
in the VM, which is why reducing the amount of code to look at is a good
idea: The idea is that eventually, the code you have will be so small that
it'll be "obvious" from inspection whether the fault lies in the VM or your
code, and if the latter, what you need to do to fix it. And if it isn't
obvious to you, then perhaps you could post your shortened code for other
people to look at, and maybe it'll be obvious to them.

- Oliver
 
K

Karl Krukow

Oliver said:
Actually, bug 4699981 sounds more like yours
(http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4699981), but this bug
has been marked as "FIXED".

Yeah, it does sound related, but "FIXED" should be a good indication
that it has been fixed ;-) Also, I don't see any alternative solution
present in that reference.

If that doesn't magically solve your problem, I recommend you try to
reduce your code to make it as small as possible while still exhibint the
problem.

It didn't magically solve my problem. But I did find a temporary
hack-solution. Apparently, the problem does not appear if I ensure that
the "problem class" InductiveQSemantics is loaded *before* I install a
new security manager. So the hack is simply creating a new instance of
the class (and then throwing the reference away) *before* I set the
security manager.

Not exactly pretty, but it works. This suggests to me that this *is*
indeed an error in the VM. I will try and follow you advice to cut down
the code to a minimum and present the problem to Sun.

Cheers,

- Karl
 
K

Karl Krukow

Karl said:
I will try and follow you advice to cut down
the code to a minimum and present the problem to Sun.


Wow, surprisingly simple code really can reproduce the Bug (and I am now
sure that this is a bug).

Soucecode:

import java.security.Permission;
public class Bug {
public static class A {}

public static void main(String[] args) throws Exception {
System.out.println("Setting Security Manager");
System.setSecurityManager(new SecurityManager() {
public void checkPermission(Permission p) {
new A();
}
});
System.out.println("Post set.");
}
}

The code basically installs a "dummy" security manager, which causes the
ClassLoader to load a new class when the checkPermission method is
invoked. For some reason this causes a ClassCircularityError, even
though such error should obviously not occur.

Runtime Behaviour:

[krukow@thyra03:...HBAC-Security/test]$ uname -a
Linux thyra03 2.6.12-1.1378_FC3smp #1 SMP Wed Sep 14 04:52:36 EDT 2005
i686 i686 i386 GNU/Linux
[krukow@thyra03:...HBAC-Security/test]$ java -version
java version "1.5.0_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05)
Java HotSpot(TM) Server VM (build 1.5.0_04-b05, mixed mode)
[krukow@thyra03:/var/spool/mail]$ javac -version
javac 1.5.0_04
....
[krukow@thyra03:...HBAC-Security/test]$ javac Bug.java
[krukow@thyra03:...HBAC-Security/test]$ java Bug
Setting Security Manager
Exception in thread "main" java.lang.ClassCircularityError: Bug$A
at Bug$1.checkPermission(Bug.java:9)
at java.lang.SecurityManager.checkRead(SecurityManager.java:871)
at java.io.File.exists(File.java:700)
at
sun.misc.URLClassPath$FileLoader.getResource(URLClassPath.java:893)
at sun.misc.URLClassPath.getResource(URLClassPath.java:161)
at java.net.URLClassLoader$1.run(URLClassLoader.java:192)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at Bug$1.checkPermission(Bug.java:9)
at java.security.Security.getProperty(Security.java:724)
at
sun.net.InetAddressCachePolicy$1.run(InetAddressCachePolicy.java:81)
at java.security.AccessController.doPrivileged(Native Method)
at
sun.net.InetAddressCachePolicy.<clinit>(InetAddressCachePolicy.java:77)
at java.lang.System.setSecurityManager0(System.java:275)
at java.lang.System.setSecurityManager(System.java:244)
at Bug.main(Bug.java:7)
[krukow@thyra03:...HBAC-Security/test]$


I will submit this problem to Sun.

Thanks for your reply,

- Karl
 
C

Chris Uppal

Karl said:
Wow, surprisingly simple code really can reproduce the Bug (and I am now
sure that this is a bug).

I suspect you are right, but I'm not sure if a fix would help you -- i.e I
think your code is wrong too.

When a class A is loaded, the JVM first creates a placeholder for that class
(as discussed in the bugs parade entries that you and Oliver have mentioned),
and later fills it in as the data is read in. If, when loading a class, it
finds an already existing placeholder (for that class) then it assumes that
it's because loading A requires A to be defined first -- a class circularity
error.

In your test case, when the first A is created, the JVM loads class A. In the
process of doing so, it needs to read the file A.class. So it consults with
the SecurityMangager to see if that is acceptable. You security manager
responds by attempting to create an instance of A. The JVM looks for the class
A (in its internal tables) and finds the placeholder. So it responds by
throwing a ClassCircularityError.

Now that may be a bug, but the JVM has to throw /some/ sort of error in this
situation since it is being required to load the class before reading the file
in which the class is defined. The real error here is that your SecurityManger
is setting up an impossible situation.

(
Incidentally, and unrelated to your problem, your new SecurityManager is being
used immediately after it is installed (inside System.setSecurityManager0()) as
a side-affect of class InternetAddressCachePolicy being initialised which in
turn is as a side-effect of executing the line:

InetAddressCachePolicy.setIfNotSet(InetAddressCachePolicy.FOREVER);

I have been trying to think of a reason why installing a security manager
should require that this setting is defined. Very strange code, and not a word
of explanation...
)

-- chris
 
K

Karl Krukow

Chris said:
When a class A is loaded, the JVM first creates a placeholder for that class
(as discussed in the bugs parade entries that you and Oliver have mentioned),
and later fills it in as the data is read in. If, when loading a class, it
finds an already existing placeholder (for that class) then it assumes that
it's because loading A requires A to be defined first -- a class circularity
error.

Ah, I see. There is certainly circularity here. My understanding of
class circularity was that class A is a subtype of class B, and at the
same time class B is a subtype of class A, i.e., a cycle in the
class-hierarchy (graph).

In either case, it (ClassCircularityError) is very pooly documented.
Now that may be a bug, but the JVM has to throw /some/ sort of error in this
situation since it is being required to load the class before reading the file
in which the class is defined. The real error here is that your SecurityManger
is setting up an impossible situation.

I see the problem. The solution I've adopted in my code is to ensure
that all appropriate classes are already loaded before the security
manager is installed (which works, at least in my code).

In some sense it is a question of "security context." What I mean is
that there are two levels of security-domains here.

a) the code running, "monitored" by a security manager: "low" domain.

b) the security manager itself: "high" domain.

The problem is that an action required/performed by the security manager
itself is being monitored and checked by the security manager itself.
Another model would be to have the security manager run un-checked,
i.e., at the "high"/"trusted" security domain.

Whether this is correct or not is not an easy problem. An Eg, if the
"low" domain code can exploit the fact that the monitor is priviledged
is not clear. Do you see what I mean?

In anycase, I've submitted this as a "bug" to sun. We'll see what their
reply is ;-)
(
Incidentally, and unrelated to your problem, your new SecurityManager is being
used immediately after it is installed (inside System.setSecurityManager0()) as
a side-affect of class InternetAddressCachePolicy being initialised which in
turn is as a side-effect of executing the line:

InetAddressCachePolicy.setIfNotSet(InetAddressCachePolicy.FOREVER);

I have been trying to think of a reason why installing a security manager
should require that this setting is defined. Very strange code, and not a word
of explanation...
)

Agreed! I was puzzled my this myself.

Thanks for your insightful reply,
- Karl
 
C

Chris Uppal

Karl said:
Ah, I see. There is certainly circularity here. My understanding of
class circularity was that class A is a subtype of class B, and at the
same time class B is a subtype of class A, i.e., a cycle in the
class-hierarchy (graph).

In either case, it (ClassCircularityError) is very pooly documented.

I agree. I'm not sure that the ClassCircularityError doc should warn about
SevurityManager problems, but /something/ should describe the issue -- unless
it is indeed just a bug.

I see the problem. The solution I've adopted in my code is to ensure
that all appropriate classes are already loaded before the security
manager is installed (which works, at least in my code).

That should be a good workaround for this specific issue. In general it seems
that you have to be very careful about what you do in a security manager. As
you say:
The problem is that an action required/performed by the security manager
itself is being monitored and checked by the security manager itself.

Another model would be to have the security manager run un-checked,
i.e., at the "high"/"trusted" security domain.

Whether this is correct or not is not an easy problem. An Eg, if the
"low" domain code can exploit the fact that the monitor is priviledged
is not clear. Do you see what I mean?

That seems more natural to me too, or even better each security manager's code
could run in the security context defined by the previous security manager.
But that's not how it works, <shrug/>. I think that the newer security stuff
( AccessController, SecurityDomains, etc) is intended to provide that kind of
flexibility, rather than programmers being required to do the hard stuff
themselves. But I've never used it myself, and have only ever skimmed over the
(rather long) guide at:

http://java.sun.com/j2se/1.5.0/docs/guide/security/spec/security-spec.doc.html

In anycase, I've submitted this as a "bug" to sun. We'll see what their
reply is ;-)

<grin> Your guess is as good as mine. In fact I'd bet you guess is the /same/
as mine...

-- chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top