CodeQL 文档

C 和 C++ 中的表达式、类型和语句

您可以使用 CodeQL 探索 C 和 C++ 代码中的表达式、类型和语句,例如,查找错误的赋值。

CodeQL 中的表达式和类型

C 中的每个表达式部分都成为 Expr 类的实例。例如,C 代码 x = x + 1 变成一个 AssignExpr、一个 AddExpr、两个 VariableAccess 实例和一个 Literal。所有这些 CodeQL 类都扩展了 Expr

查找赋值为零

在以下示例中,我们找到 AssignExpr 的实例,这些实例将常量值零赋值给它

import cpp

from AssignExpr e
where e.getRValue().getValue().toInt() = 0
select e, "Assigning the value 0 to something."

此示例中的 where 子句获取赋值右侧的表达式 getRValue(),并将其与零进行比较。请注意,没有检查以确保赋值右侧是整数或它有一个值(即,它是编译时常量,而不是变量)。对于其中任何一个假设错误的表达式,关联的谓词根本不返回任何内容,并且 where 子句不会产生结果。您可以将其视为在这一行的开头有一个隐式的 exists(e.getRValue().getValue().toInt())

还值得注意的是,上面的查询会找到以下 C 代码

yPtr = NULL;

这是因为数据库包含预处理器转换运行后的代码库的表示。这意味着,在数据库创建期间,任何宏调用(如这里使用的 NULL 定义)都将被扩展。如果您想编写关于宏的查询,那么有一些专门为此目的设计的特殊库类(例如,MacroMacroInvocation 类和 Element.isInMacroExpansion() 等谓词)。在这种情况下,宏被扩展是件好事,但我们不想找到对指针的赋值。有关更多信息,请参阅 数据库创建

查找将 0 赋值给整数

我们可以通过为表达式的左侧定义一个条件来使查询更加具体。例如

import cpp

from AssignExpr e
where e.getRValue().getValue().toInt() = 0
  and e.getLValue().getType().getUnspecifiedType() instanceof IntegralType
select e, "Assigning the value 0 to an integer."

这将检查赋值的左侧是否具有某种整数类型。注意对 Type.getUnspecifiedType() 的调用。这会将 typedef 类型解析为它们的底层类型,以便查询找到诸如以下赋值之类的赋值

typedef int myInt;
myInt i;

i = 0;

CodeQL 中的语句

我们可以使用语句进一步细化查询。在这种情况下,我们使用 ForStmt

  • Stmt - C/C++ 语句
    • 循环
      • WhileStmt
      • ForStmt
      • DoStmt
    • ConditionalStmt
      • IfStmt
      • SwitchStmt
    • TryStmt
    • ExprStmt - 用作语句的表达式;例如,赋值
    • Block - 包含更多语句的 { } 块

查找在 'for' 循环初始化中将 0 赋值给它

我们可以限制先前的查询,使其仅考虑 for 语句内部的赋值,方法是将 ForStmt 类添加到查询中。然后,我们要将表达式与 ForStmt.getInitialization() 进行比较

import cpp

from AssignExpr e, ForStmt f
// the assignment is the for loop initialization
where e = f.getInitialization()
...

不幸的是,这并不完全奏效,因为循环初始化实际上是一个 Stmt 而不是一个 Expr - AssignExpr 类被包装在一个 ExprStmt 类中。相反,我们需要使用 Expr.getEnclosingStmt() 查找表达式的最接近的封闭 Stmt

import cpp

from AssignExpr e, ForStmt f
// the assignment is in the 'for' loop initialization statement
where e.getEnclosingStmt() = f.getInitialization()
  and e.getRValue().getValue().toInt() = 0
  and e.getLValue().getType().getUnspecifiedType() instanceof IntegralType
select e, "Assigning the value 0 to an integer, inside a for loop initialization."

查找循环体内的 0 赋值

我们可以使用类似的代码使用谓词 Loop.getStmt(): 查找循环体内的赋值

import cpp

from AssignExpr e, ForStmt f
// the assignment is in the for loop body
where e.getEnclosingStmt().getParentStmt*() = f.getStmt()
  and e.getRValue().getValue().toInt() = 0
  and e.getLValue().getType().getUnderlyingType() instanceof IntegralType
select e, "Assigning the value 0 to an integer, inside a for loop body."

请注意,我们用 e.getEnclosingStmt().getParentStmt*() 替换了 e.getEnclosingStmt(),以查找深度嵌套在循环体内的赋值表达式。这里的传递闭包修饰符 * 表示 Stmt.getParentStmt() 可以跟随零次或多次,而不仅仅是一次,这会给我们语句、其父语句、其父的父语句等等。

  • ©GitHub, Inc.
  • 条款
  • 隐私