软件分析实验Tai-e代码理解与踩坑记录

  • A1
  • A2
  • A3
  • A4
    • 实现类层次结构分析(CHA)
    • 实现过程间常量传播
    • 实现过程间 Worklist 求解器
  • A5
  • A6
  • A7
    • 实例字段
      • load
      • store
    • 静态字段
      • load
    • 数组
      • load
  • A8

静态分析实验太阿地址
同学优质的课程专属博客
太阿实验保姆级提示

A1

  1. 每个SetFact<Var>包括了一个Var集合的类成员和若干操作集合的方法,每个程序点的IN/OUT都拥有一个SetFact<Var>. 初始化时因为LiveVariableAnalysis都要初始化为空集,返回new的值即可。
@Overridepublic SetFact<Var> newBoundaryFact(CFG<Stmt> cfg) {//IN[exit]=∅//空集->集合中没有任何var->新建一个SetFact即可return new SetFact<>();}@Overridepublic SetFact<Var> newInitialFact() {//IN[B]=∅ 为了完成meet策略,OUT赋一样的初值∅//空集->集合中没有任何var->新建一个SetFact即可return new SetFact<>();}
  1. 这里逻辑简单(因为analysis写好了),我因为在每轮循环开始忘记恢复change的值耽误了一会儿。
@Overrideprotected void doSolveBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {boolean change = false;//记录这一轮是否有至少一个变化的INdo{change = false;//这里我忘记恢复初值了,一度导致死循环for (Node node : cfg) {if(!cfg.isExit(node)){for (Node succ : cfg.getSuccsOf(node)) {//把每个succ的IN与target的OUT取并集analysis.meetInto(result.getInFact(succ), result.getOutFact(node));}//cfg中的node在LiveVariableAnalysis中的类型是Stmt,所以调用了自己实现的代码//利用返回值判断是否有IN值改变if(analysis.transferNode(node, result.getInFact(node), result.getOutFact(node)) && !change){change = true;}}}}while (change);}
  1. 因为LValue和RValue不是永远是Var,所以强制类型转换前一定要用instance of判断,否则会报错exp.invokespecial cannot be cast to Var这样的类型转换错误。
  2. Optional<T>可以有效提醒程序员里面的值可能为空,为空isPresent()则返回false,不为空调用get()则获得T类型实例。
        if(defB.isPresent()){//java.util.Optional<T>中的isPresent可判断是否为空if(defB.get() instanceof Var) {//注意一定要判断,LValue不是永远为Var->不判断时会报cannot be cast to Varres.remove((Var) defB.get());//OUT[B]-DEF[B]}}for(RValue rValue: stmt.getUses()){//注意一定要判断,LValue不是永远为Var->不判断时会报cannot be cast to Varif(rValue instanceof Var){res.add((Var) rValue);}}

A2

  1. 关注细节 issue

  2. evaluate函数判断常量不是看Var的isTempConst,这是理论(能有和真的存int有区别)要看实际:Value.isConstant判断

  3. 利用好copyFrom函数,然后要用中间值。如果不使用虽然比较了赋值语句对IN的修改,但没有比较旧的IN和旧的OUT。

  4. newBoundaryFact要用cfg.getIR().getParams()获得方法的参数(不是getVars,这个是变量+%this),记得设置为NAC。(因为不分析过程外的方法,这里可能返回任何职,需要做最保守的假设。显然这是sound但是不精确的。第七课介绍的过程间分析会精确很多。)

  5. IN依然记得赋初值。为了meet策略。

  6. worklist中用有序队列存储,而不是set,因为有forward顺序需求

  7. worklist添加时记得去重(用contains判断)

A3

  1. 用BFS/DFS遍历CFG图,我使用的是BFS,用queue进队出队实现。不能无脑遍历node,这样无法检测控制流不可达代码。
  2. 分支中的可达代码不用立即处理,加入队列之后处理即可。毕竟分支内的语句一般不止一条。
  3. 遍历BFS可处理分支不可达代码和无用赋值,但不能处理控制流不可达代码,所以BFS结束后还要另外进行处理。
  for(Stmt stmt: cfg.getIR().getStmts()){//这里处理控制流不可达if(!liveCode.contains(stmt) && !deadCode.contains(stmt)){deadCode.add(stmt);}}
  1. AssignStmt 的 LValue 不一定是 Var,A1也有这个问题,见继承关系即知。issue
  2. transferNode函数在处理backward时自己会处理in/out的反转,传参时不用手动改,我在第一次写worklist.java时出错了。
if(analysis.transferNode(node, result.getInFact(node), result.getOutFact(node))){for(Node pred : cfg.getPredsOf(node)){if(!nodeQueue.contains(pred)){nodeQueue.add(pred);}}
}
  1. 实现时的思路

A4

实现类层次结构分析(CHA)

  1. Resolve(b.foo())=ACD:这里为什么不是special?b.foo()并不代表直接调用super.foo(),所以是virtual
  2. getDeclaringClass返回class type;getSubsignature:返回被调用方法的子签名(两方法结合就能获得完整子签名了)

一个方法的子签名只包含它的方法名和方法签名的描述符,如 foo 的子签名是:“T foo(P,Q,R)” ,而它的完整签名是:“<C: T foo(P,Q,R)>”。

  1. 子类包括直接和间接,所以要用bfs/dfs。我使用了bfs。
  2. 注意virtual call包含interface的情况。
 if(jClass.isInterface()){q.addAll(hierarchy.getDirectImplementorsOf(jClass));q.addAll(hierarchy.getDirectSubinterfacesOf(jClass));}else {q.addAll(hierarchy.getDirectSubclassesOf(jClass));}

实现过程间常量传播

  1. edge transfer逻辑:定义了 transferEdge(edge, fact) 函数来实现 edge transfer
  • normal edge黑实线:transferEdge(edge, fact) = fact
  • call-to-return edge黑虚线:左值LHS kill掉,其他往下传。
    • 若左侧没有变量的调用,比如 m(…):不修改 fact,edge transfer 是一个恒等函数。
  • call-edge:将实参(argument)在调用点中的值传递给被调用函数的形参(parameter)。返回值为被调用函数的形参,如x。
    • 首先从调用点的 OUT fact 中获取实参的值
    • 返回一个新的 fact
    • 这个 fact 把形参映射到它对应的实参的值
    • 易错点:
 for(int i = 0; i < params.size(); i++){//callee中的param与Invoke中的arg值一一对应res.update(params.get(i), callSiteOut.get(args.get(i)));}
  • return edge:edge transfer 函数将被调用方法的返回值传递给调用点等号左侧的变量。

    • 从被调用方法的 exit 节点的 OUT fact 中获取返回值(可能有多个,你需要思考一下该怎么处理)

    • 返回一个将调用点等号左侧的变量映射到返回值的 fact。(对应等号左边的变量)

    • 如果该调用点等号左侧没有变量,那么 edge transfer 函数仅会返回一个空 fact。

    • 易错点:多个返回值的处理利用cp.meetValue

 for (Var var : edge.getReturnVars()) {val = cp.meetValue(val, returnOut.get(var));}
  1. 理解callNode和NonCallNode的区别。都是利用返回值判断OUT值是否改变,但CallNode调用TransferEdge实现对IN的修改,再与没变的OUT(存储OLD_OUT)进行对比,就实现判断OUT值是否改变。这也是为什么“你在实现 transfer*Edge() 方法的时候,不应该修改第二个参数,也就是该边的源节点的 OUT fact。”
/*** Dispatches {@code Node} to specific node transfer functions for* call nodes and non-call nodes.判断OUT是否有变化*/
@Override
public boolean transferNode(Node node, Fact in, Fact out) {if (icfg.isCallSite(node)) {return transferCallNode(node, in, out);} else {return transferNonCallNode(node, in, out);}
}

实现过程间 Worklist 求解器

过程间与过程内求解器仅有两处不同:

  1. 在计算一个节点的 IN fact 时,过程间求解器需要对传入的 edge 和前驱们的 OUT facts 应用 edge transfer 函数(transferEdge)。过程内直接是OUT[B]。
  2. 个人不太懂“仅需要”,感觉逻辑和过程间几乎一样。因为其他方法的 entry 节点和非 entry 节点还是要设置initial fact,否则会报错。可能这是实现meetInto策略的原因,算法本身没有特殊要求?不懂。

在初始化的过程中,过程间求解器需要初始化程序中所有的 IN/OUT fact,也就是 ICFG 的全部节点。但你仅需要对 ICFG 的 entry 方法(比如 main 方法)的 entry 节点设置 boundary fact。这意味着其他方法的 entry 节点和非 entry 节点的初始 fact 是一样的。

A5

  1. addReachable:处理 New Copy Invoke (static) StoreField (static) LoadField (static)

    1. 访问者模式:stmt.accept(stmtProcessor);
    2. 如何对new创建对象:Obj getObj(New allocSite);
    3. 遍历S_m:method.getIR().getStmts().forEach
    4. static storefield中怎么获得T.f?pointerFlowGraph.getStaticField(field)
    5. 静态和动态invoke都可以用Solver.resolveCallee(Obj,Invoke)获得callee,静态时令obj=null即可
  2. addPFGEdge:AddEdge
    1. 如何获得pointer的指针集pt(s)?用pointer类自带的getPointsToSet()方法
  3. analyze:while循环,处理 Invoke - processCall StoreField LoadField StoreArray LoadArray
    1. 如何判断pointer能否代表变量x?pointer instanceof VarPtr ptr
    2. f怎么获得?JField field= storeField.getFieldAccess().getFieldRef().resolve();
    3. oi_f怎么拼凑?pointerFlowGraph.getInstanceField 会创建
    4. oi[*]怎么表示?用pointerFlowGraph.getArrayIndex()
  4. PointsToSet propagate(Pointer,PointsToSet):用filter函数
  5. void processCall(Var,Obj)
    1. m_ret怎么获取?用 callee.getIR().getReturnVars()
    2. 函数传参和A4中一样
    3. 判断CG里有没有边不能写callGraph.hasEdge(callsite.getContainer(), callee),而要写callGraph.getCalleesOf(callsite).contains(callee)。不是很理解,可能跟数据结构有关。

A6

  1. 关于A5->A6的变化这里写的十分清楚
  2. processSingleCall不用传Context,因为callee和callsite可以得到
  3. 注意new语句
//  New {o_i}用heapModel创建
public Void visit(New stmt) {Obj obj = heapModel.getObj(stmt);//获得o_iContext c = contextSelector.selectHeapContext(csMethod, obj);//获得cPointsToSet pts = PointsToSetFactory.make(csManager.getCSObj(c, obj));//获得c:o_iworkList.addEntry(csManager.getCSVar(context, stmt.getLValue()), pts);return null;
}
  1. 不需要make多个参数或将CSCallSite变成Context时,不用使用ListContext
  2. selectContext中
  • CallSelector :静态和非静态的处理一致,只需要关注 CSCallSite
  • ObjSelector&TypeSelector:静态直接返回CallSite的上下文,而非静态关注 CSObj

A7

从A7开始是自行设计算法,不保证正确,仅通过平台的少量用例。
参考了这个知乎思考
个人思路是

  • 对于load,把对应的store的rhs用meet计算出来,赋给load的lhs
  • 对于store,若y.f的值有变化,把对应的load加入worklist

对应形容的规则为:

  • load/store:在别名中找field一致的
  • array:在别名中找index满足表格的

下面是汇总的太阿官方手册和SPA-Freestyle-Guidance,侵删

实例字段

load

找到所有对这一实例字段(以及其别名)进行修改的 store 语句,并将这些语句要 store 的值(即rhs) meet 之后赋给 L 等号左侧的变量。相同为精确值,不同为NAC。

   y = 5;p.f = y; // p.f is an alias of a.f(这里的y就是要找的值)...
L: x = a.f; // meet val(y) to val(x)

利用指针分析计算程序的别名信息,若 xy 的指针集有交集,则x.fy.f互为别名

store

在发现y.f的值改变了(也就是rhs,x的值改变了)之后,也要将某些节点加入worklist. 这些节点,也就是这次load可能产生影响的语句,是所有对y.f的读取,x = y.f,以及所有对y的潜在别名的读取,x = z.f.

// x.f = y storeField遍历x的指针集,看指向对象o.f在store后有无变化,有变化时将所有潜在节点加入worklist
for obj in pts(x):obj.f = meet(obj.f, y)if obj.f changes:for z in obj.alias:foreach l: w = z.f:kill (w, _), gen (w, obj.f)start cp from l

这个算法基本上和上面的图片有差不多的意思了。现在需要解决的问题就是:

  • 如何存储 obj.f 的值?这个问题就留给你来想了。“field 随便你加”。
  • 怎么从 l: w = z.f 开始常量传播?讲义里面写了,InterCP 里面是有一个 solver 的,只需要稍加改造 solver 就可以把 l 加入传播流程。

静态字段

load

当处理一个静态字段的 load 语句时(假设为 x = T.f;),你只需要找到对同一个字段(T.f)的 store 语句,并将这些保存进字段的值进行 meet 后赋给 load 语句等号左侧的变量(x)。这一处理可以在没有指针分析和别名信息的情况下完成。

因为 JClass 不像 Var 那样自带所有的 RelevantStmt,所以这似乎就需要花费一次 icfg 遍历来自行收集了。可能也有更好的办法,比如一些隐藏的 API,但是,我不清楚。

数组

load

当分析一个数组的 load 语句如 x = a[i]; 时,你需要找到所有修改 a[i] 别名的值的 store 语句,并将这些值 meet 后赋给 x。此处对数组的处理要更复杂一些:当你判断两个对数组的访问 a[i]b[j] 是否互为别名时,你不仅需要考虑它们的 base 变量(ab)的指针集是否有交集,还需要考虑索引值 ij 的关系。有趣的是,由于数组索引也是 int 类型,因此你可以用常量分析来实时计算 ij 的关系。

下图判断 a[i]b[j] 是否互为别名

a[i] and b[j] j = UNDEF j =c 2 j = NAC
i = UNDEF 不互为别名 不互为别名 不互为别名
i =c 1 不互为别名 当且仅当 c1=c2时互为别名 互为别名
i = NAC 不互为别名 互为别名 互为别名

A8

source:类似指针分析里的new rule,会增加taint obj,也就是在遇到函数调用时,我们额外检查callee是否是source,是的话额外增加taints到worklist中。

sink:写在collectTaintFlows()中,在PTA分析结束后检查指针集情况来收集污点流,遍历所有的CSCallSite即可
taintTransfer:接口写在analysis中,而PTA 对 Invoke 的处理地点这么多,在哪里进行 transfer 的检测?当一个 Var 的 taint object 子集发生变化,检查其相关的语句,并看是否有符合条件的 transfer,然后将变化通过 worklist 算法传播出去。所以个人在函数调用的后面都调用了transfer,因为source会引起 taint object的变化(注意静态的base为null)

【软件分析】Tai-e实验代码理解与踩坑记录相关推荐

  1. seg代码配置的踩坑记录

    01. SEGMENTATION FAULT 正在配置OCNET的代码,在自己的本地运行没有任何问题,但是在服务器上一直报错:SEGMENTATION FAULT 这属于很概括的报错,无法直接看明白到 ...

  2. 【STM32+机智云】机智云手机APP点灯实验踩坑记录

    [STM32+机智云]机智云手机APP点灯实验踩坑记录 一.实验背景 因为项目开发需要用到云平台,所以开始学习机智云平台,听说机智云比较容易入门,还有手机APP.因此开始了踩坑之旅,一切的一切开始于一 ...

  3. VS Code Python 代码智能提示(自动补全)编译环境设置踩坑记录

    VS Code Python 编译环境设置踩坑记录 本菜鸡非常喜欢VS code的简洁风格,而且跟原来再用的VS2017相比简直不要太快,前段时间电脑重装之后又要在windows系统上重新搭建整套环境 ...

  4. 亚马逊云科技 Build On 第二季物联网(AIoT)专场实验心得(附踩坑大全)

    写在前面的废话: 本身是物联网专业的,一直以来在学校都觉得没有太多实践机会,不清楚到底专业本身就业后是做什么的,以后能干什么更是一无所知. 总之没用的课程学了一大堆,然后去考试.迷茫,没有方向,想要行 ...

  5. Mybatis-plus一键生成代码(踩坑记录,用了3种方式)

    Mybatis-plus一键生成代码 本人现在从事Web前端开发,但是转型之前一直在写SpringMVC项目,所以对后端开发还留有一点兴趣.今晚就有了想法,用Springboot整合Mybatis(后 ...

  6. STM32-CAN通讯实验代码理解

    CAN目前是我见过最复杂的一个通讯了,但是不得不说CAN通讯更好,最直观的感受就是速度以及他可以判断错误,这让通讯的正确性有了大大的提升. 写的可能有点乱,但是我是按照自己的思路逐一把代码剖析了一遍, ...

  7. 钉钉微应用php代码示例,钉钉微应用踩坑记录

    一.要解决的问题: 钉钉微应用开发,应对未来可能的PC端使用场景:PC访问网页,工作台登陆后,通过钉钉服务端接口完成自身业务. 二.开发前准备: 根据官方文档,钉钉微应用PC端开发和移动端开发已经统一 ...

  8. Windows 10环境下「MSCOCO Captions」评估代码踩坑记录

    文章目录 引言 解决python2→python3.x问题 验证评估代码是否可以正确运行 解决 jar 包运行出错的原因 排查jar包运行出错的原因 解决Could not reserve enoug ...

  9. 在Cent OS云服务器上部署基于TP5后端代码踩坑记录_艾孜尔江撰

    推荐使用镜像安装Cent OS系统,或者在纯净安装完成之后在完成Apache+MySQL+PHP的时候不要每个单独安装,因为这样会出一些三者之间版本不配的问题,网上各种说法都有,查起来也非常困难,版本 ...

最新文章

  1. 开始升级我的工作流系统
  2. php数字加零,php实现数字补零的两种方法
  3. C#日期格式化(转)
  4. 零基础如何学Java?能学会Java吗?
  5. SAP CRM呼叫中心polling and C4C notification polling
  6. 大一计算机论文_大一计算机论文发表.doc
  7. java绘画imo,搞死帮忙看下这个:需要为class interface或enum imort java.ut
  8. 多版本Python共存的配置和使用
  9. 第一次知识补充及用户登录(三次机会重试)作业
  10. 【CodeVs 6128 Lence的方块们】
  11. python安卓吾爱_python编程视频教程v1.0.0下载_Python编程安卓版下载_吾爱游戏网
  12. 支持向量机原理与高斯核函数
  13. HTTP中的status code什么意思
  14. veil-evasion介绍
  15. 声网 X 牛客网 200万场视频面试背后的实时互动技术支撑
  16. BugkuCTF:宽带泄露;隐写2
  17. Android项目实践(二)——日记本APP
  18. 文通电脑版车牌识别软件,让违章驾车无处可躲
  19. Android组件化开发实践和案例分享 | 融合数10个项目模块
  20. 雨林木风linux如何安装教程,u盘安装linux图解教程m2硬盘怎么装win7系统

热门文章

  1. lldb断点framework中方法
  2. 【最新】.Net程序员学习路线图以及就业现状
  3. 走出杀毒误区,让电脑,软件,网络更安全
  4. UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0x80 in position 0: invalid start byte 错误解决
  5. 前景无限的golang,分分钟高薪offer
  6. 知识图谱:小小的入门级综合应用
  7. 《Oracle编程艺术》学习笔记(15)-事务原子性
  8. 用Java实现opc通信协议
  9. C++ 算法 高精度(较详细.)
  10. 每一次成长,都缘于打破固有的认知