基于Joern的Scala脚本函数参数异常:IF-ELSE分析结果不符
问题描述
使用Joern分析demo.c的IF-ELSE结构时,两段逻辑一致的Scala脚本运行结果不同:
- 封装复用函数的Scala1脚本中,
node2的结果为0(正确结果应为1) - 未封装函数的Scala2脚本运行结果正确
疑问:是否是函数参数Traversal[ControlStructure]传递错误?如何修改Scala1以得到正确结果?
附相关代码
demo.c
#include <stdio.h> int main () { /* local variable definition */ int a = 100; /* check the boolean condition */ if(a>10) { if(a>100){ printf("a > 100"); }else{ printf("10<a<100"); } }else if( a <= 10 && a > 0) { printf("Value of a is 20\n" ); }else { printf("None of the values is matching\n" ); } printf("Exact value of a is: %d\n", a ); if ( x > 10 ) { printf("111"); }else{ printf("222") } while(x++ < MAX) { if(x!=0) { int y = 2*x; sink(y); } } return 0; }
Scala脚本1(结果异常)
open("demo") def Outermost_layer_branch = cpg.method("main").block.astChildren.isControlStructure.controlStructureType("IF") def fun(node:Traversal[ControlStructure]){ def node1 = node.astChildren.isControlStructure.controlStructureType("ELSE") println(node1.size) // the result is 1, it's right def node2 = node1.astChildren.filter(_.isBlock).astChildren.isControlStructure.controlStructureType("IF") println(node2.size) //the result is 0, it's wrong!! } fun(Outermost_layer_branch.order(3))
Scala脚本2(结果正确)
open("demo") def Outermost_layer_branch= cpg.method("main").block.astChildren.isControlStructure.controlStructureType("IF") def node1 = Outermost_layer_branch.order(3).astChildren.isControlStructure.controlStructureType("ELSE") println(node1.size) // the result is 1,and it is right! def node2 = node1.astChildren.filter(_.isBlock).astChildren.isControlStructure.controlStructureType("IF") println(node2.size) // the result is 1,and it is right!
问题分析与解决
问题原因
并非参数传递错误,核心问题在于Joern的Traversal是惰性求值的查询链:
- 传递
Traversal[ControlStructure]给函数时,该查询链并未立即执行求值 - 函数内用
def定义node1、node2会导致每次引用都重新执行查询,结合Traversal的惰性特性,可能出现子节点查询匹配失效的情况 - 直接对整个Traversal进行链式操作时,查询上下文的传递可能出现预期外的偏差
修改方案
方案一:传递单个ControlStructure节点
将函数参数类型改为单个ControlStructure节点,调用时用.head提取Traversal中的目标节点,同时将def改为val避免重复求值:
open("demo") def Outermost_layer_branch = cpg.method("main").block.astChildren.isControlStructure.controlStructureType("IF") def fun(node: ControlStructure){ val node1 = node.astChildren.isControlStructure.controlStructureType("ELSE") println(node1.size) val node2 = node1.astChildren.filter(_.isBlock).astChildren.isControlStructure.controlStructureType("IF") println(node2.size) } fun(Outermost_layer_branch.order(3).head)
方案二:在函数内提取单个节点
保留Traversal[ControlStructure]参数,在函数内部先提取单个节点再操作:
open("demo") def Outermost_layer_branch = cpg.method("main").block.astChildren.isControlStructure.controlStructureType("IF") def fun(nodeTraversal: Traversal[ControlStructure]){ val node = nodeTraversal.head // 提取Traversal中的单个节点 val node1 = node.astChildren.isControlStructure.controlStructureType("ELSE") println(node1.size) val node2 = node1.astChildren.filter(_.isBlock).astChildren.isControlStructure.controlStructureType("IF") println(node2.size) } fun(Outermost_layer_branch.order(3))
两种方案都能确保查询基于明确的单个节点执行,避免惰性求值带来的上下文问题,最终node2.size会输出正确的1。
内容的提问来源于stack exchange,提问作者kiki Shao
相关产品推荐
相关产品推荐

