LLVM 项目博客

LLVM 项目新闻和来自前线的细节

clang-tidy 中新增的代码检查规则可以检测到(一些)特洛伊木马源代码

特洛伊木马源代码

原始的 特洛伊木马源代码论文 包含了一系列攻击,这些攻击利用 Unicode 属性使代码看起来与编译器处理的方式不同。例如,以下代码摘自论文

#include <stdio.h>
#include <stdbool.h>

int main() {
  bool isAdmin = false;
  /*‮ } ⁦if (isAdmin)⁩ ⁦begin admins only */
  printf("You are an admin.\n");
  /* end admins only ‮ { ⁦*/
  return 0;
}

看起来对 isAdmin 进行了保护,而实际上编译器读取的字节流如下

/* <U+0x202E> } <U+0x2066> if (isAdmin) <U+0x2069> <U+0x2066> begin admins only */

这个问题在正式发布之前提交给了 LLVM 安全小组,虽然我们一致认为这更多是一个显示问题,而不是一个实际的编译器相关问题,但我们也一致认为,为论文中描述的每个缺陷添加一个 clang-tidy 检查并不会造成任何损害。

使用 clang-tidy

名为 clang-tidy 的工具可以在代码库上运行一系列额外的代码检查规则,以检测编码规范问题、API 误用、安全漏洞等。我们添加了三个新的检查规则

检测误导性的双向字符

新的检查规则 misc-misleading-bidirectional 解析代码库中的每个注释和字符串文字,并查找未结束的双向序列,即越过注释或字符串文字结尾的序列,导致正常的代码从右到左显示,而不是通常的从左到右显示。在上面的例子中,我们会得到一个接近以下内容的警告

5:3: warning: comment contains misleading bidirectional Unicode characters [misc-misleading-bidirectional]

检测误导性的标识符

C++ 允许在标识符中使用一些 Unicode 代码点,包括具有强右到左方向的标识符,这会导致误导性的语句。例如,在以下代码中,

int א = ג;

我们是在给 א 赋值,还是给 ג 赋值?实际上我们是在给后者赋值,这很令人困惑。misc-misleading-identifier 代码检查规则检测到这种配置并输出类似以下内容的警告

10:3: warning: identifier has right-to-left codepoints [misc-misleading-identifier]

检测令人困惑的标识符

谁没有收到过使用看起来像 ASCII 字符的 Unicode 字符来绕过假设的反垃圾邮件扫描的垃圾邮件?C 类语言并没有摆脱这种趋势,而且在程序的某个地方定义

int foo;

,而在另一个地方定义

int 𝐟oo;

,这是完全有效的,也是令人困惑的。misc-homoglyph 检查规则根据 Unicode 联盟维护的 confusables.txt 列表检测这种易混淆的标识符(也称为同形异义词)。在上面的例子中,你会得到一个类似以下内容的警告

7:5: warning: 𝐟oo is confusable with foo [misc-homoglyph]

总结

如本文所述,我们选择将特洛伊木马源代码防御措施实现为多个 clang-tidy 检查规则。这样做而不是将它们实现为 Clang 警告,是在解析时间上的权衡。

感兴趣的读者可以在 这篇专门的博客文章 中了解 GCC 的替代方法!