[RFC] 基于 ClangIR 的安全 C++

这篇文章提出了基于 ClangIR 的 Safe C++的想法,作为 clang 中的扩展。

  • 动机:受Safe C++提案启发,想要创建一个纯 C++子集加上一些可忽略的 pragma 和属性来实现 Safe C++,以减少设计、实现和用户的负担。在文中将提议称为Safe C++,将上述提案称为Safe C++2,线程外可称为Clang Safe C++
  • 快速示例:通过#pragma clang SafeCXX标记关键部分,如检测悬空指针的示例,在invalid(bool)函数中展示了借用检查的过程,在不同条件下对变量的借用和使用进行了分析和诊断。
  • 演示实现:可在GitHub - ChuanqiXu9/clangir at safe-c++找到演示实现,示例在clangir/clang/test/CIR/SafeC++ at safe-c++ · ChuanqiXu9/clangir · GitHub,使用时需启用mlir-DCLANG_ENABLE_CIR=ON
  • 提议:添加两个 pragma#pragma clang SafeCXX#pragma clang UnsafeCXX,被#pragma clang SafeCXX标记且未通过#pragma clang UnsafeCXX的 C++代码称为安全 C++代码,编译器可在此类代码上添加新检查并拒绝有效 C++程序。
  • “#pragma clang SafeCXX”的属性:不会被包含传播,可确保包含的“不安全”文件不会影响安全部分。
  • 检查:主要包括借用检查,确保同一时间最多只有一个可变借用,帮助明确变量间的依赖关系以发现可能的悬空指针等。具体检查包括在常量借用时不写入、不允许多个可变借用同时存在、非借用不与可变借用重叠等,并通过多个示例进行了说明。
  • 弃用调用:借鉴“十亿美金错误”的概念,通过弃用一些调用(如std::unique_ptr的默认构造函数等)来避免类型安全问题,也可使用[[clang::SafeCXXDeprecated]]标记用户代码中的弃用函数。
  • 禁止使用指针等(未实现):考虑到指针是安全的噩梦,希望在安全 C++中禁止使用指针(如p.reset(nullptr); p = nullptr;等),虽未在 ClangIR 中实现,但假设在 AST 中实现较容易。
  • 异步函数中的引用(未实现):异步函数中引用的使用存在已知问题,目前借用检查算法仅在同步世界中检查引用的使用,无法识别 C++中的异步函数,可通过标记异步函数或使用配置文件来解决,但这不是完美的方案,仍有开放的讨论空间。
  • 常量全局(未实现):全局变量必须是常量,以避免在函数中借用时出现问题,实现起来较简单。
  • 标准化和更高层次路线图:先将提议发送至社区寻求帮助,此提议不添加新的语法结构,仅添加新检查,安全 C++代码应能被其他 C++编译器编译,当认为合适时再提交给 WG21 成为标准的一部分。
  • 路线图:包括添加 pragma 到 clang 、在社区讨论 Safe C++设计、在 clangir 仓库中实现设计的检查、将 clangir 合并到主仓库后继续在主仓库中实现、将 SafeC++作为实验发布、正式发布并提议成为标准等步骤,其中第四步可能需要讨论使 clang 默认依赖 MLIR。
  • 结论:提议添加两个 pragma 来指定 C++的安全部分,并添加新检查以拒绝有效 C++程序,通过实现和示例让用户感受 Safe C++,一些主题仍有开放的设计空间。
  • 可能的问题与答案:解释了需要 ClangIR 的原因是为了在安全 C++中进行借用检查,不一定要依赖 ClangIR 进行借用检查,但在 AST 中可能无法进行精确分析,其他检查如路径不敏感检查可在 AST 中直接进行,目前 ClangIR 在分析方面存在表达性和抽象层次的问题。
阅读 35
0 条评论