我想构建一个 C 预处理器/编译器,允许从本地和在线资源收集函数。IE:
#fetch MP3FileBuilder http://scripts.com/MP3Builder.gz
#fetch IpodDeviceReader http://apple.com/modules/MP3Builder.gz
void mymodule_main() {
MP3FileBuilder(&some_data);
}
那是容易的部分。
困难的部分是我需要一种可靠的方法来“沙箱”导入代码,从直接或不受限制地访问磁盘或系统资源(包括内存分配和堆栈)。我想要一种方法来安全地运行不受信任的 C 代码(模块)的小片段,而无需将它们放在单独的进程、VM 或解释器中(尽管可以接受单独的线程)。
要求
- 我需要为其访问数据和资源(包括 CPU 时间)设置配额。
- 我将阻止直接访问标准库
- 我想阻止产生无限递归的恶意代码
- 我想将静态和动态分配限制为特定限制
- 我想捕获模块可能引发的所有异常(例如除以 0)。
- 模块只能通过核心接口与其他模块交互
- 模块只能通过核心接口与系统(I/O 等)交互
- 模块必须允许位操作、数学、数组、枚举、循环和分支。
- 模块不能使用 ASM
- 我想限制指针和数组访问为模块保留的内存(通过自定义 safe_malloc())
- 必须支持 ANSI C 或子集(见下文)
- 系统必须是轻量级和跨平台的(包括嵌入式系统)。
- 系统必须与 GPL 或 LGPL 兼容。
我很乐意接受 C 的一个子集。我不需要模板或类之类的东西。我主要对高级语言不擅长的事情感兴趣,比如快速数学、位运算以及二进制数据的搜索和处理。
现有的 C 代码可以在不修改的情况下重复使用来创建模块,这并不是意图。目的是要求模块符合一组规则和限制,旨在将模块限制为基本逻辑和转换操作(例如视频转码或压缩操作)。
这种编译器/预处理器的理论输入将是具有 module_main 函数的单个 ANSI C 文件(或安全子集),没有包含或预处理器指令,没有 ASM,它将允许循环、分支、函数调用、指针数学(仅限于分配给模块的范围)、位移、位域、强制转换、枚举、数组、整数、浮点数、字符串和数学。其他任何东西都是可选的。
示例实现
这是一个伪代码片段,可以更好地解释这一点。这里一个模块超出了它的内存分配配额并且还创建了无限递归。
buffer* transcodeToAVI_main( &in_buffer ) {
int buffer[1000000000]; // allocation exceeding quota
while(true) {} // infinite loop
return buffer;
}
这是一个转换后的版本,我们的预处理器添加了观察点来检查内存使用和递归,并将整个事情包装在异常处理程序中。
buffer* transcodeToAVI_main( &in_buffer ) {
try {
core_funcStart(__FILE__,__FUNC__); // tell core we're executing this function
buffer = core_newArray(1000000000, __FILE__, __FUNC__); // memory allocation from quota
while(true) {
core_checkLoop(__FILE__, __FUNC__, __LINE__) && break; // break loop on recursion limit
}
core_moduleEnd(__FILE__,__FUNC__);
} catch {
core_exceptionHandler(__FILE__, __FUNC__);
}
return buffer;
}
我意识到执行这些检查会影响模块性能,但我怀疑它仍然会胜过高级或 VM 语言来解决它打算解决的任务。我并不是想直接阻止模块做危险的事情,我只是想强迫那些危险的事情以可控的方式发生(比如通过用户反馈)。即:“模块 X 已超出其内存分配,继续还是中止?”。
更新
到目前为止,我最好的方法是使用带有边界检查和一些自定义函数和循环代码的自定义编译器(如被黑的 TCC)来捕获递归。我仍然想听听关于我还需要检查什么或有什么解决方案的想法。我想在使用前删除 ASM 并检查指针可以解决下面之前的答案中表达的许多问题。我添加了一个赏金来从 SO 社区中获取更多反馈。
对于我正在寻找的赏金:
- 针对上述理论系统的潜在攻击细节
- 对每次访问检查指针的可能优化
- 概念的实验性开源实现(如 Google Native Client)
- 支持多种操作系统和设备的解决方案(无基于操作系统/硬件的解决方案)
- 支持大多数 C 操作甚至 C++(如果可能的话)的解决方案
可以与 GCC 一起工作的方法(即预处理器或小型GCC 补丁)的额外功劳。
我也会考虑任何能够最终证明我正在尝试的事情根本无法完成的人。不过,您需要非常有说服力,因为到目前为止,没有任何反对意见真正指出了他们认为不可能的技术方面。为那些拒绝的人辩护,这个问题最初是作为安全运行 C++ 的一种方式提出的。我现在已将要求缩减到 C 的有限子集。
我对 C 的理解可以归类为“中级”,我对 PC 硬件的理解可能比“高级”低了一步。如果可以,请尝试指导您针对该级别的答案。由于我不是 C 专家,我将主要基于对答案的投票以及答案与我的要求的接近程度。您可以通过为您的索赔(受访者)和投票(其他所有人)提供充分的证据来提供帮助。一旦赏金倒计时达到 6 小时,我将分配答案。
最后,我相信解决这个问题将是在一个日益网络化和偏执的世界中保持 C 语言相关性的重要一步。随着其他语言在性能方面缩小差距和计算能力的增长,将越来越难以证明 C 开发的额外风险是合理的(就像现在使用 ASM 一样)。我相信您的答案将比获得一些 SO 点具有更大的相关性,因此即使赏金已过期,请尽您所能贡献。