Domanda

I am reading this tutorial from Oracle http://docs.oracle.com/javase/tutorial/extra/generics/legacy.html

But I can't figure out what this line means

As a result, the type safety and integrity of the Java virtual machine are never at risk, even in the presence of unchecked warnings.

Can someone explain it for me more clearly? Added: What exactly is the "integrity" of JVM and what does "at risk" really mean?

È stato utile?

Soluzione

It means that the JVM is never fooled into thinking an object is a type that it is not. Tricking a runtime into thinking a piece of data has a different type is a powerful attack vector, particularly if you can trick the runtime into allowing you to assign a value to what it thinks is a long or int, but is actually a pointer.

The fundamental security model of the JVM relies on an object being the type that the runtime thinks it is.

I read a fascinating paper that detailed an attack on a machine running Java that involved using a heat lamp to increase memory errors drastically. They then used a program with billions of objects strategically aligned in memory, and waited for one to have a sporadic bit flip. Doing so tricked the JVM into thinking it was dealing with a different type of object, and ultimately allowed the JVM to run arbitrary code (for the full read, see Using Memory Errors to Attack a Virtual Machine).

The fact that they used bit flips is irrelevant to Generics. But the power of tricking the runtime into thinking an object is of a different type is detailed in that article. To sum up, imagine you have classes A and B:

class A {
    public long data = 0;
}

class B {
}

If you can somehow trick the JVM to allow this:

A aButActuallyB = someMagicAssignment(new B());

Where someMagicAssignment is a method that can take a reference to B and somehow return the referent object as an A. Then think about what would actually happen when you then did:

aButActuallyB.data = 0x05124202;

You might be writing into arbitrary data in the JVM's raw memory! That data could be the location of a method, for example. Changing it to point to the contents of some byte array could allow you to then run arbitrary code.

So when Oracle says

the type safety and integrity of the Java virtual machine are never at risk, even in the presence of unchecked warnings.

What it's saying is that even though you can do this:

public static <T> T someMagicAssignment(B b){ 
   return (T) b; //unchecked cast warning
}

And then call it with:

A a = MyClass.<A>someMagicAssignment(new B());

This is still going to do a runtime check at the assignment to a.

Thus writing that method someMagicAssignment is no easier than it was before. Generics have not in any way increased the surface area of this attack vector, because the JVM ignores generics in its internal type system. Never is the JVM going to allow you to give a method a List<String>, and then have that method execute String operations on the elements of that list, without at runtime checking that the elements are in fact Strings. Never is it going to allow you to treat a B as an A without manually checking.

Altri suggerimenti

I think what they are getting at is that generics are implemented in the compiler but not in the virtual machine, and thus the bytecode that the compiler emits is the same regardless of whether you suppress unchecked warnings or not; suppressing unchecked warnings is not implemented in a way that bypasses type-checking at the virtual machine level, only at the compiler level.

It simply means, when you have List<String> stringList or List stringList in your code, once compiled and run, the JVM isn't at risk at all. The JVM just sees List stringList.

This is because of type erasure, where the Generics parameterized type is erased (removed) at runtime.

The same article clearly states:

Basically, erasure gets rid of (or erases) all generic type information. All the type information betweeen angle brackets is thrown out, so, for example, a parameterized type like List<String> is converted into List.


The risk in the documentation simply implies that, should you have an example of

List<String> stringList = new Arrays.asList("one", "two", "three");
String number = stringList.get(0);

The JVM understands it as:

List stringList = new Arrays.asList("one", "two", "three");
String number = (String)stringList.get(0);

Though, the second version will complain of Generics raw-types, the JVM removes the parameterized type and implicitly retrieves elements as Object and the type conversion rules of the JVM still applies.

The JVM never stores/uses Generics information (even though the metadata resides in the class file).

I hope this helps.

The answer is in the sentence immediately preceding that one:

The reason for this is, that generics are implemented by the Java compiler as a front-end conversion called erasure. You can (almost) think of it as a source-to-source translation, whereby the generic version of loophole() is converted to the non-generic version.

It means that, due to erasure, the source code with generics will compile down to the same (or at least similar) bytecode as the non-generic source code.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top