Java中的泛型 - 与遗留代码互操作

时间:2013-05-09 10:52:10

标签: java generics jvm legacy-code

我正在阅读Oracle http://docs.oracle.com/javase/tutorial/extra/generics/legacy.html

中的本教程

但我无法弄清楚这条线的含义

  

因此, Java虚拟机的类型安全性和完整性   即使存在未经检查的警告,也不会有风险。

有人可以更清楚地为我解释一下吗? 补充:JVM的“完整性”究竟是什么?“风险”究竟是什么意思?

4 个答案:

答案 0 :(得分:5)

这意味着JVM永远不会被认为对象是一种它不是的类型。欺骗运行时将一段数据视为具有不同类型是一种强大的攻击向量,特别是如果您可以欺骗运行时允许您为其认为是longint的值分配值,但实际上是一个指针。

JVM的基本安全模型依赖于对象是运行时认为的类型。

我读了一篇引人入胜的论文,详细介绍了对运行Java的机器的攻击,该机器涉及使用加热灯来大幅增加内存错误。然后,他们使用了一个程序,数十亿个对象在内存中策略性地对齐,并等待一个有零星的位翻转。这样做会欺骗JVM认为它正在处理不同类型的对象,并最终允许JVM运行任意代码(对于完整的读取,请参阅Using Memory Errors to Attack a Virtual Machine)。

他们使用位翻转这一事实与Generics无关。但是,该文章详细介绍了欺骗运行时思考对象的不同类型的能力。总而言之,假设您有课程AB

class A {
    public long data = 0;
}

class B {
}

如果你能以某种方式欺骗JVM来允许这个:

A aButActuallyB = someMagicAssignment(new B());

其中someMagicAssignment是一个方法,可以引用B并以某种方式将引用对象作为A返回。然后考虑一下当你做了实际时会发生什么:

aButActuallyB.data = 0x05124202;

您可能正在写入JVM原始内存中的任意数据!例如,该数据可以是方法的位置。将其更改为指向某个字节数组的内容可以让您随后运行任意代码。

所以当Oracle说

  

即使存在未经检查的警告,Java虚拟机的类型安全性和完整性也不会存在风险。

它的含义是即使你能做到这一点:

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

然后用:

来调用它
A a = MyClass.<A>someMagicAssignment(new B());

这仍然会在分配a进行运行时检查。

因此,编写该方法someMagicAssignment并不比以前容易。泛型不会以任何方式增加此攻击向量的表面区域,因为JVM在其内部类型系统中忽略了泛型。从来没有JVM允许您给方法List<String>,然后让该方法对该列表的元素执行String操作,而不是在运行时检查元素实际上是{{ 1}}秒。在没有手动检查的情况下,决不允许您将String视为B

答案 1 :(得分:1)

这只是意味着,当您的代码中有List<String> stringListList stringList时,一旦编译并运行,JVM就没有风险了。 JVM只看到List stringList

这是因为类型擦除,其中Generics参数化类型在运行时被删除(删除)。

同一篇文章明确指出:

  

基本上,擦除会删除(或删除)所有泛型类型   信息。尖括号之间的所有类型信息都是   抛出,例如,像List<String>这样的参数化类型   转换为List


文档中的风险只是意味着,如果你有一个

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

JVM将其理解为:

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

虽然,第二个版本会抱怨Generics raw-types ,但JVM会删除参数化类型并隐式检索元素为Object,并且JVM的类型转换规则仍然适用。

JVM 从不存储/使用泛型信息(即使元数据位于类文件中)。

我希望这会有所帮助。

答案 2 :(得分:1)

我认为他们所得到的是泛型是在编译器中实现的,而不是在虚拟机中实现的,因此无论是否抑制未经检查的警告,编译器发出的字节码都是相同的;抑制未经检查的警告未以绕过虚拟机级别的类型检查的方式实现,仅在编译器级别实现。

答案 3 :(得分:0)

答案在紧接在那一句之前的句子中:

  

原因是,泛型是由Java实现的   编译器作为前端转换称为擦除。你可以(差不多)   将其视为源到源的翻译,即通用的   版本的漏洞()被转换为非通用版本。

这意味着,由于擦除,带有泛型的源代码将编译为与非通用源代码相同(或至少相似)的字节码。