在时间中很难实现sound的算法,所以专家们提出一个与Soundness不同的概念Soundiness。(关于Soundness,可以回顾B站上的前两节课)。
本文主要内容如下:
- Soundness and Soundiness
- Hard Language Feature: Java Reflection
- Hard Language Feature: Native Code
回顾Soundness的定义(没有漏报bug就是sound):
Conservative approximation: the analysis captures all program behaviors, or the analysis result models all possible executions of the program.
无论是学术界还是工业界的算法,都是不完全sound的。
这是由于很多语言拥有的部分高级特性对于静态分析来说是难以分析的:
- Java
- Reflection, native code, dynamic class loading, etc.
- JavaScript
- eval, document object model (DOM), etc.
- C/C++
- Pointer arithmetic, function pointers, etc.
例如对C/C++中的一个指针p,加上某个经过运算的偏移量x,为了分析的安全,只能假设p+x可以指向内存中的任何一个地方。
为了解决这一问题,专家们提出一个新的概念Soundiness,对应的形容词是Soundy。
A soundy analysis typically means that the analysis is mostly sound, with well-identified unsound treatments to hard/specific language features.
把新旧词汇放到一起做个比较:
- A sound analysis requires to capture all dynamic behaviors
- 完全理想情况
- A soundy analysis aims to capture all dynamic behaviors with certain hard language features unsoundly handled within reason
- 现实情况
- An unsound analysis deliberately ignores certain behaviors in its design for better efficiency, precision or accessibility
- 过于现实的情况
说了这么多抽象的概念,接下来具体说说在Java里给静态分析添乱的特性。
首先,右上角Run-time代码块中的前三行的Class、Method和Field都是Metaobject。然后就能够以Metaobject执行一系列操作,如c.newInstance()
就相当于左下角的new Person()
。
使用Reflection时,无法在编译时确定其行为,只能在运行时确定。
可能会错失检测出某些bug的机会(忽略m.invoke
所引入的调用时发生),也可能导致分析的结果不安全(忽略f.set(a,a)
的作用时发生,将会错误地认为箭头指向处的cast可以优化掉)。
一种方法是模拟动态运行过程,只要知道关键调用时的字符串,就能分析出结果:
吗?
因为有太多的因素(见右边黄框内容)可能会影响到字符串的值,而其中很多是无法静态确定的。基于字符串常量的分析此时并不奏效……
这来源于一个巧妙的观察:
When string arguments cannot be resolved statically, infer the reflective targets at their usage points!
getClass获得Class的Metaobject。
这里的cmd是动态的命令,静态时无法分析……
但是!在篮圈处的调用中,由于只传入parameter这一个参数,而在155行我们已经知道其类型就是``FrameworkCommandInterpreter`(或其子类/父类)。
此外也有和动态分析结合的方法:
在Java中,一句简单的println
最后会调用与底层平台相关的C或C++代码,这些代码就称为Native Code。Java也借此实现了 Run anywhere
的目标。
JNI允许JVM与Native Code写出的Native Lib交互。进而提供与OS交互、提高性能和代码复用(指在Java中使用别的C/C++编写的libraries)的功能。
为了使用Native Library中的一个Method,需要经过这些步骤:
- 用C/C++按照标准写一个库并编译生成
*.so
文件 - 在Java中加载Native Library
- 在Java中声明Native Method
- 调用Native Method
然而:
- 类似的JNI可提供的操作有230种(在2020年如此)
- 由此引出紫框中的问题:
如何分析Native Call?
接下来介绍一个现有的解决方法,然后给新的拓展阅读方向。对,本次讲的都是前沿内容。
arraycopy(src,3,dest,4,5)就是从src的第三个元素起,拷贝起始位置是dest的第四个元素,总共拷贝5个元素。
如果不分析Native Code,我们就会失去很多信息。而Modeling的分析方法是:
- 先将其副作用用Java代码表示出来(图中第一个方框)
- 然后用指针分析的方法进一步抽象其副作用(图中第二个方框)
如果对Soundiness感兴趣,推荐网站http://soundiness.org
- Understand soundiness: its motivation and concept
- Understand why Java reflection and native code are hard to analyze