Macroni:基于宏和MLIR的C语言渐进式增强方案

本文介绍了Macroni工具,它通过结合C语言宏和MLIR中间表示,实现了对C代码的渐进式增强,包括强类型定义、Linux内核Sparse检查增强、Rust风格不安全区域以及安全信号处理等创新功能。

Holy Macroni! 渐进式语言增强方案

尽管Clang在代码重构和静态分析工具中广泛应用,但其AST存在重大缺陷:无法提供关于AST节点源自哪些CPP宏展开的溯源信息,也不将宏展开降级到LLVM IR代码。这使得构建宏感知的静态分析和转换工具极为困难,成为持续研究的课题。今年夏天在Trail of Bits,我开发了Macroni来解决这个问题。

Macroni允许开发者通过宏定义新的C语言构造语法,并用MLIR提供这些构造的语义。它使用VAST将C代码降级到MLIR,并通过PASTA获取宏到AST的溯源信息。开发者可以定义自定义MLIR转换器,将Macroni输出转换为领域特定的MLIR方言以进行更细致的分析。

强化类型定义

C语言的typedef虽然能为底层类型提供语义名称,但编译器仅检查底层类型。通过Macroni,我们可以使用宏定义强类型typedef语法,并用MLIR实现自定义类型检查:

1
2
3
#define STRONG_TYPEDEF(name) name
typedef double STRONG_TYPEDEF(fahrenheit);
typedef double STRONG_TYPEDEF(celsius);

Macroni会将这些typedef转换为自定义MLIR方言类型,实现严格的类型检查。相比使用struct实现强类型,这种方法保持了API和ABI兼容性。

增强Linux内核Sparse检查

Macroni可以挂钩到Sparse宏(如__user)执行安全检查。与仅支持C的Sparse不同,Macroni适用于所有Clang可解析的代码(C/C++/Objective C)。例如:

1
2
# define __user __attribute__((noderef, address_space(__user)))
u32 __user *uaddr;

Macroni将这类声明降级到MLIR时会将用户空间注解嵌入类型系统,实现自定义类型检查。

Rust风格不安全区域

为便于逐步采用强类型系统,Macroni实现了类似Rust的unsafe机制:

1
2
3
4
5
6
7
8
9
#define unsafe if (0); else

fahrenheit convert(celsius C) {
    fahrenheit F;
    unsafe {
        F = (C * 9.0 / 5.0) + 32.0;
    }
    return F;
}

在unsafe区域外执行强类型检查,区域内则保持原有语义。

安全信号处理

Macroni可以确保信号处理器只执行信号安全代码:

1
2
3
4
5
#define SIG_HANDLER(name) name
#define SIG_SAFE(name) name

int SIG_SAFE(do_detach)(int, const char*);
static void SIG_HANDLER(sig_handler)(int signo) { ... }

通过标记信号处理器和安全函数,Macroni在MLIR层验证信号处理器只调用安全函数。

为什么选择Macroni?

Macroni将C代码和宏降级到MLIR,避免了Clang AST的限制,使开发者能够构建考虑宏的静态分析工具。它支持定义自定义分析、转换和优化,甚至可以结合宏和MLIR定义新的C语法和语义。

Macroni是免费开源工具,GitHub仓库提供了试用机会。特别感谢Trail of Bits团队的支持,特别是Peter Goodman的创意指导,以及Lukas Korencik的代码审查和改进建议。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计