JVM如何验证字节码?

15

JVM 如何对字节码进行验证?

2个回答

21

Oracle官方有一小页介绍了其工作原理,可以在这里找到。

基本上,JRE不信任JDK。这是因为它不知道哪个JDK编译器创建了该类文件。它把该类文件视为敌对的,直到经过验证。

更进一步说,字节码验证是保护免受Sun所谓的“敌对编译器”攻击的必要步骤。Sun自己的Java编译器确保Java源代码不违反安全规则,但是当应用程序导入代码片段时,它实际上并不知道代码片段是否遵循Java语言的安全规则。换句话说,该代码可能不是由可信赖的Java编译器产生的。

在这种情况下,您计算机上的Java运行时系统不得不假定该片段是有问题的,并将其提交给字节码验证。

在字节码通过验证之前,Java虚拟机甚至看不到它。在加载字节码时进行验证还具有一个优点,即不需要每次执行代码时执行大量的运行时检查。因为已经验证了正确性,所以一旦开始运行,它就可以比否则可能更快地运行。

以下是链接图表的版本:

                    <<<=== Unsafe / Safe ===>>>
                                  \
+---------------+        +-------------------+
|  Java source  |   +--> |   Class loader    | --+
+---------------+   |    | Bytecode verifier |   |
        |           |    +-------------------+   |
        V           |             /              |
+---------------+   |             \              V
| Java compiler |  Network        /    +-------------------+
+---------------+   |             \    |      JVM/JIT      |
        |           |             /    +-------------------+
        V           |             \              |
+---------------+   |             /              V
| Java bytecode | --+             \    +-------------------+
+---------------+                 /    | Operating system  |
                                  \    +-------------------+
                                  /              |
                                  \              V
                                  /    +-------------------+
                                  \    |     Hardware      |
                                  /    +-------------------+
                                  \
                    <<<=== Unsafe / Safe ===>>>

问题不仅仅在于恶意编译器。如果有人真的想编写一个漏洞利用程序,他们很可能会手动编写字节码,或者至少是重要部分。 - Antimony
但是对于从本地CLASSPATH加载的类,验证器未启用(或未执行),是吗? - Dinis Cruz
@Dinis,我非常确定字节码验证适用于所有类文件。即使是来自文件系统的文件也可能来自敌对编译器。 - paxdiablo

9
最好的信息来源可能是JVM规范中相关部分,4.10 验证类文件
请查看链接以获取详细信息,但大体上来说:
链接时间验证增强了解释器的性能。否则每个解释指令都必须执行昂贵的检查以验证约束条件,这些检查可以被消除。Java虚拟机可以假设这些检查已经完成。例如,Java虚拟机已经知道以下内容:
- 操作数栈没有溢出或下溢。 - 所有局部变量使用和存储都是有效的。 - 所有Java虚拟机指令的参数都是有效的类型。
验证器还执行可以在不查看代码数组的Code属性(§4.7.3)中完成的验证。执行的检查包括以下内容:
- 确保最终类没有被子类化,最终方法没有被覆盖(§5.4.5)。 - 检查每个类(除了Object)是否有一个直接超类。 - 确保常量池满足文档中的静态约束;例如,每个常量池中的CONSTANT_Class_info结构都包含在其name_index项中为CONSTANT_Utf8_info结构的有效常量池索引。 - 检查常量池中的所有字段引用和方法引用是否具有有效的名称、有效的类和有效的类型描述符。

有一些晦涩的边角情况,规范在 Hotspot VM 中是模糊的或者直接被违反了。如果你确定你的字节码应该是可验证的,但是不知道为什么无法通过验证,你也可以查看 OpenJDK 源代码库。 - Antimony
虽然这个链接可能回答了问题,但最好在此处包含答案的必要部分并提供参考链接。仅链接的答案如果链接页面更改可能会变得无效。- 【来自审核】 - André Fratelli
@AndréFratelli:确实,这不是我现在会添加的答案(请注意,它已经有将近7年了...)。这也不是我现在会回答的问题,而是将其关闭为过于宽泛。我在这里添加了一些细节,但如果链接再次失效(今天已经以不同的方式修复了两次),它仍然不会非常有用。同时,虽然问题本身仍未被删除,但我认为这是一个有用的答案... - Jon Skeet
@JonSkeet,没问题,我刚刚在我的审核队列中看到了这个,并且在那种情况下似乎很合适。由于它太旧了,可能已被标记为需要审核的内容。 - André Fratelli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接