深入浅出解读 Java 虚拟机的差别测试技术
<div id="n3wdry" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/png/168324/1546483351638-35ab03b8-68d2-4176-8325-89abdd3c5736.png" data-width="827">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/png/168324/1546483351638-35ab03b8-68d2-4176-8325-89abdd3c5736.png" width="827" />
</div>
或者IBM 的 OpenJ9。</div>
- 怎么样去发现一个 JVM 缺陷或者是安全漏洞?
- 怎样生成一个有效的测试包?对于测试输入,怎样能够有更多的这样的字节码,或者产生更多可运行的应用程序并在虚拟机上再跑一跑?1.1 如何暴露出产品级虚拟机的缺陷对于第一个问题,即怎么样去暴露出一个产品及虚拟机的缺陷,这里面在跑的时候就会发现有一个困难,这个困难就是在学术圈里叫做“缺少一个测试喻言”。如果要测一个 Java 虚拟机的话,我们拿一个类过来跑跑,在一个 Java 虚拟机上面,会得到一个真实的结果,这个时候我们把真实结果和一个预期结果来比较一下,如果能够发现它们里面的不一致,那么这个就说明 Java 虚拟机出现了一些问题。
<div id="k6y2au" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483408236-9d59042d-2544-4fd6-aa25-9fd52903027d.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483408236-9d59042d-2544-4fd6-aa25-9fd52903027d.jpeg" width="720" />
</div>我们的预期结果到底是怎么来的?实际上有一个 Java 虚拟机规范,假如 Java 虚拟机规范,它也是一个能够运行的机器,那么它跑一跑,能够得到一个运预期结果。但是实际上,我们说 Java 虚拟机规范它本身也不能跑,所以这个事情实际上没有很好的解决方案。后来,我们意识到差别测试技术,这个也是 Java 虚拟机开发中,大家都采用的一个方法,也就是说有多个虚拟机,把一个类或者是应用拿到不同的虚拟机上去跑,比较它们之间的结果是不是有差别。如果这个大家结果都一致,那就很好,如果结果不一致,那么就可以去预测一下这里面是不是有 Java 虚拟机的实现出了问题。1.2 如何获得一个有效的测试包对于第二个问题,就是怎么样能够有更加复杂的或者更加花样繁多的字节码来做测试?一开始,我们尝试去使用现实中大量的类,从网上甚至从 openJDK 里面自己的包里解压出很多类文件,放在 JVM 不同版本上面去跑。这里面的确还是能够发现一些问题,一些不一致的现象,但是这个不一致更多是兼容性问题。于是我们很快就转向了第二个技术,叫“领域感知的模糊测试技术”。模糊测试是应用在安全领域里面的一个测试技术,它可以帮助发现一些安全问题。比如说有一个文件,有个图像,把这个图像一位一位地变化,用以查看应用软件是否比较健壮。如果把技术应用到 Java 虚拟机上面,就要做一些调整,这种调整是领域感知的 ,也就是说我们知道 Java 字节码它本身的一些特性,根据它的特性来做一些变化,这个工作更加泛,我们有一个种子类,通过这个种子,我们会把它变来变去,变成一堆的测试类,放到 Java 虚拟机上跑。<div id="selsgg" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483438172-fc3af688-ebb7-4d82-a652-35809ecd111e.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483438172-fc3af688-ebb7-4d82-a652-35809ecd111e.jpeg" width="720" />
</div>这个工作我们曾发在 PLDI2016,还有一个明年的 ICSE 上的工作,第一个是 classfuzz,第二个 classming。让我们对于 Java的类执行过程进行一个深入了解,一开始做的工作比较偏向于上层,就是更多的去关注了 Java 类的是怎么样去导进来,怎么样链接,怎么样去初始化等等。这个是 classfuzz 的主要工作。后来做到一定的程度,我们就转向了下层,怎么样去做验证,执行,这个时候就会去想类的执行会不会引发一些差别,我能不能在不同虚拟机上真的跑出一些不一样的结果?<div id="ygt6ag" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483462686-aabb0aaf-f940-4f49-9b47-8723a87d9b45.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483462686-aabb0aaf-f940-4f49-9b47-8723a87d9b45.jpeg" width="720" />
</div>Classfuzz下面,我就分别对这两个工作进行介绍。classfuzz 是一个很简单的一个想法,就有点像一开始最传统的模糊测试的技术,对合法的 Java 字节码文件,我们想进行一个语法变种,变种以后,比如说对于 Java 类我们得到它的一个语法树,去尝试修改,比如说把 public 改成 private,把文件名改一改,把这个函数名改一改,这样的话可以生成很多比较奇怪的类,把奇怪的类拿过来以后,就可以去测试一下 Java 虚拟机的一个健壮性。<div id="ngm3de" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483478284-1e9e1dd2-9d2a-4387-83de-71d296d59df1.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483478284-1e9e1dd2-9d2a-4387-83de-71d296d59df1.jpeg" width="720" />
</div>但是这个时候我们很快就意识到,这个时候它有一个缺陷,我们只是去挑战了虚拟机的一个启动过程,就是看看它的格式检查对不对?去看看他的链接过程对不对,看看它的初始化对不对。Classfuzz 是 2016 年的一个论文,但一直到近期我们还是用它发现了一点问题。右边是一个字节码,当然这个字节码比较繁杂,把这样的一个类,放到 Open J9 和 HotSpot 上面跑, HotSpot 立刻就报了一个格式错误,那么 Open J9 是属于一个正常运行,这里面是因为没有 main 函数,但是它总体算是一个正常通过的类。后来我们就研究了差别原因,它的主要原因就是这里面它有一个flag,表明这个是一个接口文件 interface。那么从规范上来说,如果接口 flag 被设定了以后,它就要同时去设一个 abstract 的 flag,所以 HotSpot 报了一个格式问题,这个是正确的。那么 Open J9 上我们就找到一个缺陷。</div>
<div id="q646ke" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483496969-126615ab-4105-4d2f-9135-358e35667512.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483496969-126615ab-4105-4d2f-9135-358e35667512.jpeg" width="720" />
</div>我们把这个问题其实也报给了 Open J9,经过了几轮反复,他们很快就修复了,修复完了以后又引入了新的问题,又修复,大概就是这样的一个过程。<div id="4f41fn" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483514160-a0029bb7-c338-4893-83ba-16410828e08b.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483514160-a0029bb7-c338-4893-83ba-16410828e08b.jpeg" width="720" />
</div>通过 classfuzz 这样一个技术,甚至可以发现 Java 虚拟机规范里面的二义性。那么左边是这样的一个类,它这里面有个 public abstract{},它代表的是类初始化函数。我们或者是把某个方法名字改成了 clinit,或者是把正常的类初始化函数前面加了一个abstract。那么实际上 Open J9 和 HotSpot 又有了一个行为上的差异。我们回头去看了一下原因,这个是因为 Java 虚拟机规范的问题,Java 虚拟机规范里是这样说的,other methods named<clinit> in a class file are of noconsequence, “除了类初始化这个函数以外,其他的函数加上这种标识符 of no consequence”,这到底是一个什么含义?这个里面大家就有误解了。Hotspot 认为它是一个常规的方法,但是 J9 认为这里面就是一个格式错误,这个就是大家对 of no consequence 会有认识上的不一样。</div>
<div id="t2nlan" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483534596-d1562e79-ac4f-4c0f-9243-a25a40cffd45.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483534596-d1562e79-ac4f-4c0f-9243-a25a40cffd45.jpeg" width="720" />
</div>Classfuzz 框架接下来我来介绍一下 classfuzz 的框架,假设有个种子,进行了一个变种,变种结束以后,把变种类放到 Java7、Java8、Java9、J9、GCJ 上面一起去跑。那么就可以通过一个类,生成了很多的变种文件,在不同虚拟机上面跑。这个里面其实想随机的变种,随机生成很多的变种类,效果非常差。于是又引入了这样一个过程:有一个选择和测试的过程,有很多的变种算子,我们研究怎么样去选择更有效的变种算子,也选择更加有代表性的一些类文件来做测试。<div id="dqa0ul" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483552031-90a9a3f9-2540-4bf4-a25f-509157f4912e.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483552031-90a9a3f9-2540-4bf4-a25f-509157f4912e.jpeg" width="720" />
</div>那么这里面有几个技术要点,由于时间限制,我就简单过一下。Classfuzz 的技术要点 1我们设计了 129 个变种算子,其中 123 个是用来修改类的语法的,像我刚才说的 public 改成 private,删掉一个名字,改掉一个函数名等等,或者删掉一个函数等等。<div id="d64rve" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483567778-bfe91ec8-f981-49bc-ac57-e4555a540d64.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483567778-bfe91ec8-f981-49bc-ac57-e4555a540d64.jpeg" width="720" />
</div>我们还有 6 个修改语义,右边是修改语义的一个简单的办法。我们采用一个 Java 字节码分析工具是 SOOT,这里面它会把类转成 Jimple 文件,那么对 Jimple 文件的第 2 个语句和第 3 个语句,可以给它顺序颠倒一下。但是这个颠倒效果没有那么理想,不是说所有程序的字节码都有一个先后关系。Classfuzz 的技术要点 2刚才说到有 129 个变种算子,这个算子数其实挺多的。我们的选择性非常广,那么这里面就采用了一个直觉,直觉是哪些变种算子更加有效,就让它用的更加频繁一点,所以采用了一个有点偏机器学习的一个算法,马尔可夫链蒙特卡洛算法来选择更加有效的算子。我们预期会形成一个分布,有些算子给它一些高的概率,有些给低的概率。实际分布不是所预期的这样,但总体上趋势还是比较接近的。Classfuzz的技术要点 3会有很多的测试类会被生成,这个时候怎么样去选择一些有代表性的测试类?我们采用了传统测试里面一个等价类划分的技术,就把它们放到某个虚拟机上去跑,放到 Hotspot 上面,特别是 classloader 那一块代码,就收集一下它的行覆盖率和分支覆盖率,比较一下。这个时候立刻就有一个数字上的感觉,假如这个数字不一样,那么就说明类在 Java 虚拟机里面的处理逻辑是不一样的,如果处理逻辑不一样,那么就应该说两个类特性还是不一样的。如果有新生成的类的话,拿到 Java 虚拟机上跑,再来算一下它的覆盖率,看看它是不是有代表性,这里面代表性有两个用途,第一个是用于多个Java 虚拟机差别测试,第二个是把它作为新的种子来做变种,能够得到新的变种。</div>
<div id="uqzuql" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483598620-e6fedaf9-473d-426a-8d64-0bdedfe741db.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483598620-e6fedaf9-473d-426a-8d64-0bdedfe741db.jpeg" width="720" />
</div>Classfuzz 的技术要点 4第四个技术要点是差别测试,我们拿类到多个 Java 虚拟机上跑,去观察它们的执行结果,试图去分析到底是在哪一个阶段所抛出的什么问题。观察在哪个阶段报了错,为什么?当有几个 JVM 的时候,就采用一个从众原则推测哪个 Java 虚拟机出错了,这是差别测试的过程。<div id="swikue" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483613907-4eb58227-781b-4439-bf72-596672efc7b9.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483613907-4eb58227-781b-4439-bf72-596672efc7b9.jpeg" width="720" />
</div>classfuzz 也发现了更多的 Java 虚拟机的这种区别,这里面有一个变量叫 R0,我们把 R0 的类型改了一下,从 map 改成 String,也发现了虚拟机差别。我们还发现 J9 和 Hotspot 的验证方式不一样,当导进来一个类的时候,HotSpot 会把所有的方法都会验证一遍,但是 J9 就显得比较 lazy 一点,它只是对将来有可能运行的方法会去做一个验证,所以这个时候也有一个差别。那么此外还发现 GU 缺少维护,当然它现在更缺少维护了。<div id="n2esyo" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483646303-554462b0-f407-441e-92d1-674f2ed27d5d.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483646303-554462b0-f407-441e-92d1-674f2ed27d5d.jpeg" width="720" />
</div>我们这个时候就意识到还会有很多的工作要做,于是就再接再厉,又做了下一个工作。classfuzz 并没有能够深入测试 Java 虚拟机的底层,我们至始终在测的可能编译那块的同学比较感兴趣一点,但是对于研究运行时的同学可能没有那么大的兴趣,那么主要的原因就是我们只是修改了语法,生成了很多格式正确或者不正确的字节码文件,但是去运行的时候,除了很少数的能够修改语义操作的一些算子以外,生成的大部分的东西或者是被拒了,或者是它的执行和前面的一些类没有什么差别。这个时候我们就思考这样的一个事情,我们是不是能够生成格式正确、但是语义不一样的程序,语义不一样也就是说你真的能够在Java虚拟机上跑,实际上语义不一样的字节码。这样我们能够测试两个功能模块:第一个,验证器;第二个,它的执行功能,或者执行引擎。大家觉得可能就有点意思了。好,在做这两件事情的时候,其实有一些执念:<li data-type="list-item" data-list-type="unordered-list">
<div data-type="p">第一个执念,有很多同学都学了编译,那么编译原理里面其实有很多程序分析和优化的算法。当时在做这件事情的时候,就很好奇,这么多经典的算法在 Java 虚拟机实现当中,都正确地实现了吗?我们能不能在实现里面,找到一个实现错了的一个算法?</div>
</li>
<li data-type="list-item" data-list-type="unordered-list">
<div data-type="p">第二个执念,是不是能够找到在两个 Java 虚拟机上运行结果不一样的程序?这个典型的就拿主流的 J9 和 Hotspot,在上面能不能用同样字节码,能够运行不一样,还有为什么?比如执行的时候是不是还会有各种各样奇怪的现象,例如 double free 等问题。 </div>
</li><div id="46pecg" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483672724-23157def-cbc8-4c90-b5d5-e049aadf2ec8.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483672724-23157def-cbc8-4c90-b5d5-e049aadf2ec8.jpeg" width="720" />
</div>好,那么接下来我们 show 一点例子,帮助大家了解 Java 虚拟机的上述差别。<div id="hyeyvx" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483694031-3e20da89-374c-4024-9926-3636fbaf1b39.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483694031-3e20da89-374c-4024-9926-3636fbaf1b39.jpeg" width="720" />
</div>右边一小段代码,那么这两段代码我们看是不是真的有什么语义上的不一致?实际上左边代码是创建了一个对象,从栈顶拿出了一个元素,做了一个比较,对吧?右边代码表示从栈顶拿了一个元素,创建了一个对象,再做了比较。这两个代码语义其实是一模一样。一个是 o 等于 this,一个是 this 等于 o。这两段代码其实本质上都是错误代码,因为我们 new 完了以后其实没有给对象初始化。但是到 Hotspot 和 J9 上面去运行的时候,Hotspot 给两个都报了一个验证错误,我们就发现,J9 在非常罕见的情况下,在某一个初始化函数里面,如果你写了代码,它会通过验证。实际上我们抓住了一个缺陷。这是一个比较简单的例子。那么再来看比较复杂一点的例子,我们说数据流分析可能实现错了,那能不能找一找运行结果不一样的程序?右边是一个种子类,先创建了一个对象,初始化,把这个对象设为空。接下来用 monitorenter 和 monitorexit。那么 Jimple 正好反了一下。把它转换为类文件以后,Hotspot 和 J9 它是比较一致的,Hotspot 抛出了空指针异常,J9 也抛出了空指针异常。这是因为 Java 虚拟机规范里面说,假如对象是空,我们遇到的第一个 R0,因为是空的,那么它应该抛一个空指针异常。<div id="0gspdf" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483721959-24be042f-389f-406c-aa1a-a63df8c8fb0a.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483721959-24be042f-389f-406c-aa1a-a63df8c8fb0a.jpeg" width="720" />
</div>那么接下来看看到底怎么样去修改代码,发生了什么?第一个干的事情就是在里面插入一个循环,直接跳到这,entermonitor R0,这个地方又做了一个循环回去,也就是说 entermonitorR0 会执行 20 遍, exitmonitor r0 执行了一遍。这个时候我们发现这个 Hotspot 抛出了一个 IMSE,但是 J9 是正常执行。追究原因,我们发现这里面其实有一个叫结构锁的机制,假如一个Java 虚拟机要求去实现结构锁这样的一个机制,并且类违反了结构锁规则,那么就抛出一个 IMSE,Hotspot满足结构锁机制,但是 J9 不要求,所以这里面会形成一个差别,这是所发现的第一个差别。</div>
<div id="zp0gns" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483741979-60b05b2f-77f3-47a2-aed1-8bf7680c5ea6.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483741979-60b05b2f-77f3-47a2-aed1-8bf7680c5ea6.jpeg" width="720" />
</div>又去跑模糊测试,继续去撞。这个时候从 new string,初始化之后,正好插入了一个 goto 语句到这,也就是说这是一个 new string r0,entermonitor r0,exitmonitor r0。Hotspot 就是正常的运行了,J9 就抛出了一个验证错误。HotSpot 反馈说这应该是一个正确的例子,因为虽然 R0 没有初始化,但是这个里面没有什么危害,所以就可以放过它。那么 J9 就认为它存在一个缺陷。实际上 Java 虚拟机规范里是这样说的,一个验证器如果遇上一个没有初始化的对象,在使用的时候应该要报一个验证问题。好,那么既然到这种情况下面,entermonitor r0 它是使用了,就说明这个规则被违反了。又做了做,又撞了一个问题。entermonitor r0 这个东西是一个正常的对象,那么 exitmonitor r0,这个时候 R0 是空对象,它本来应该匹配的,但是这个时候实际上变成空引用。<div id="hefceg" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483758078-491e9e88-8826-4260-9346-d1513a7add41.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483758078-491e9e88-8826-4260-9346-d1513a7add41.jpeg" width="720" />
</div>J9 抛出了一个空指针异常,Hotspot 抛出了 IMSE。J9 的解释很合理,因为从规范上来说,monitorexit null 应该抛出一个空指针异常。Hotspot 开发人员也找了很久,实际上发现在这做了一个优化,在这个时候 Hotspot 会抛出几个异常,但是这个时候会做一个优化,把其他异常都扔掉,留了一个 IMSE。但是由于它们是抛的不一样的异常,由于这些异常可以被分别捕获,所以程序可以产生不同的运行结果。针对于同样的一个种子,我们变化,会发现,这个程序它的运行还有点不太一样。这个里面还发现了一些,比如说 Hotspot 的不同版本之间也会有一些差别,当我们的测试类比较复杂的时候,有控制流的归并,有数组的访问,有异常处理等等,它会遇上一些问题。<div id="43ggpo" data-type="image" data-display="block" data-align="" data-src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483773231-becfbfdd-9227-43cb-a907-ee89a780cdfd.jpeg" data-width="720">
<img src="https://intranetproxy.alipay.com/skylark/lark/0/2019/jpeg/168324/1546483773231-becfbfdd-9227-43cb-a907-ee89a780cdfd.jpeg" width="720" />
</div>技术方面,还是采用类变种,意图是生成语义不一样的一些文件。怎么样算语义不一样?也就是方法里面能够被运行到的字节码是不一样的。那么一个主要的思想就是要修改这个种子里字节码。我们记录哪些字节码被运行到了,在里边去修改一下,去修改它的控制流,那么修改完了控制流以后,它的数据流也可能发生改变,这个时候会出现很异常的控制流或者数据流,如果我们把这种异常的情况放到 Java 虚拟机上跑,有可能它的数据流分析会错了,有可能其他情况也会出错,这是很简单的思想。我们的技术要点,第一个会记录一下哪些字节码会被执行到,反正做一些插装就可以。第二个我们要做一些变种,在每个语句后面,就插入 goto。实际上除了 goto 之外,我们还可以插入 return、throw、lookupswitch,都是 Jimple 里支持的。当然也可以去用 ASM 插入更多能够修改控制流的指令。变种过程也是一个频繁试错的过程,实际上遵循了一个流程,变种出错我就把它拒了,变种过程就是有个种子,变完了以后,决定要接收、拒绝等等,得到新的类,继续变种、接受、拒绝等。我们差别测试主要是看看有什么验证问题,还有没有可能会撞上系统崩溃,有没有输出差异,这种输出差异并不是由并发导致的,而由 Java 虚拟机实现上面的差异导致的。最后介绍一个例子。右边有一个函数,R2 等于 new string,那么在这 R2 是一个对象,这个 R2 被用了,所以理论上他不能通过验证,因为 R2 被使用之前没有被初始化,违反了 Java 虚拟机规范。但是在这个里面 Hotspot 成功地抛出了验证错误,但是 J9 没有能够拒绝,说明验证器出错了。实际上大家可以看一下这个问题是怎么来的,其实就是植入了一个 goto,初始化中跳出去了,它正好 R2 就被使用了,这个时候就发现了这个问题。总结一下我们的工作,我们做了一个 Java 字节码变种及 Java 虚拟机差别测试的一个技术方案,这个里面可以暴露出 Java 虚拟机的缺陷。进一步我们希望去看看,既然有这么多的变种,为什么不把它应用到内存管理当中,看看内存管理有什么问题,看看性能有什么问题,特别是变种有可能会对一些高强度的计算,进行反复的迭代,反复计算,那么是不是能够发现性能方面的一些缺陷?这项工作是和现在在苏黎世理工的苏振东老师,九州大学的赵建军教授,还有南洋理工的苏亭,谷歌孙诚年一起做的一项工作。那么我的汇报就到这里,谢谢大家。
原文链接
本文为云栖社区原创内容,未经允许不得转载。
深入浅出解读 Java 虚拟机的差别测试技术的更多相关文章
- 深入理解多线程(五)—— Java虚拟机的锁优化技术
本文是<深入理解多线程>的第五篇文章,前面几篇文章中我们从synchronized的实现原理开始,一直介绍到了Monitor的实现原理. 前情提要 通过前面几篇文章,我们已经知道: 1.同 ...
- 全面解读java虚拟机(面试考点大全)d
学习java以来,jvm的原理已经看过好多遍了,可是很多知识点都串不起来. 今天我把jvm相关知识整理了一下,看完之后肯定会对JVM很的清楚. JVM是虚拟机,也是一种规范,他遵循着冯·诺依曼体系结构 ...
- Java虚拟机垃圾收集器
一.判断对象存活的算法 1.引用计数(Reference Counting)算法 给对象添加一个引用计数器,每当有一个地方引用时,计数器加1.当引用失效时,计数器减1.当计数器的值为0的时候说该对象不 ...
- Atitit.java 虚拟机的构成 与指令分类 与 指令集合 以及字节码查看工具javjap
Atitit.java 虚拟机的构成 与指令分类 与 指令集合 以及字节码查看工具javjap 1.1. 虚拟机的构成 java虚拟机--处理器.堆栈.寄存器.指令系统. 1 1.2. 虚拟机执行过程 ...
- 《深入理解Java虚拟机》(六)堆内存使用分析,垃圾收集器 GC 日志解读
堆内存使用分析,GC 日志解读 重要的东东 在Java中,对象实例都是在堆上创建.一些类信息,常量,静态变量等存储在方法区.堆和方法区都是线程共享的. GC机制是由JVM提供,用来清理需要清除的对象, ...
- 🏆【JVM深层系列】「云原生时代的Java虚拟机」针对于GraalVM的技术知识脉络的重塑和探究
GraalVM 背景 新.旧编程语言的兴起躁动,说明必然有其需求动力所在,譬如互联网之于JavaScript.人工智能之于Python,微服务风潮之于Golang等等.大家都清楚不太可能有哪门语言能在 ...
- JAVA:测试java虚拟机支持的最大内存 Xmx 值?Tomcat 内存溢出?(转)
如下命令,即可测试:不断调整n的值,windows上32位的1.6x为: 1610m java -Xmx1610M -versionjava -Xmx1610m -version 网摘的tomcat内 ...
- 深入理解java虚拟机----java技术体系(一)
1.java技术体系 举例: class文件格式:如下图所示,java源代码可以根据不同的编译器可以编译成不同的代码.即可以自定义语言规范比如beanshell,并编写代码; 然后自己编写java编译 ...
- 【深入理解JAVA虚拟机】第4部分.程序编译与代码优化.2.运行期优化。这章提到的具体的优化技术,应该对以后做性能工作会有帮助。
1.概述 Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”(Hot Spot Code). 为了提高 ...
随机推荐
- 大数据平台Hive数据迁移至阿里云ODPS平台流程与问题记录
一.背景介绍 最近几天,接到公司的一个将当前大数据平台数据全部迁移到阿里云ODPS平台上的任务.而申请的这个ODPS平台是属于政务内网的,因考虑到安全问题当前的大数据平台与阿里云ODPS的网络是不通的 ...
- TypeScript 函数-重载
function attr(name:string):string; function attr(age:number):string; function attr(nameorage:any):an ...
- go-设计思想
1, 围绕 简单 这一核心的设计 隐式接口,切片, 类的弱化,强制用组合 简洁高效的并发 弱化的指针 err 判定,先判错的习俗. 2, 有自己的坚持,不盲目攀比 比优点比不过很多语言,没C快,没ja ...
- pom string
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...
- windows服务定时任务
其实定时任务时不时会碰到,只不过解决方案也不是只有一个,网上也有很多文章,但是没有一篇说得很清楚,尤其是安装环节,今天就着重说一下安装, 其他步骤带过,C#开发windows服务,开发,安装,调试 1 ...
- String类,StringBuffer类转字符数组
String不可变类型和StringBuffer可变类型 String类和StringBuffer类都是字符串表示类,区别在于String对象引用变量是不可变的,而StringBuffer类对象引用变 ...
- Linux下如何查看定位当前正在运行的Nginx的配置文件
1. 查看nginx的PID,以常用的80端口为例: [root@xiaoyuer scripts]# netstat -lntup|grep 80 tcp 0 0 0.0.0.0:80 0.0.0. ...
- 百度AI
官网:http://ai.baidu.com/tech/face SDK:https://ai.baidu.com/sdk#bfr API说明 百度Face SDK Android 版是一种面向 An ...
- [.net 面向对象程序设计深入](31)实战设计模式——使用Ioc模式(控制反转或依赖注入)实现松散耦合设计(1)
[.net 面向对象程序设计深入](31)实战设计模式——使用IoC模式(控制反转或依赖注入)实现松散耦合设计(1) 1,关于IOC模式 先看一些名词含义: IOC: Inversion of con ...
- Java面试题:Hibernate的二级缓存与Hibernate多表查询
我们来看两个有关Java框架之Hibernate的面试题,这是关于Hibernate的常考知识点. 1.请介绍一下Hibernate的二级缓存 解题按照以下思路来回答: (1)首先说清楚什么是缓存: ...