如何创建轻量级C代码沙箱?

时间:2009-06-11 09:28:35

标签: c sandbox

13 个答案:

答案 0 :(得分:16)

由于C标准太宽泛而不允许,您需要反过来:指定您需要的C的最小子集,并尝试实现它。即使ANSI C已经过于复杂并且允许不必要的行为。

最有问题的C方面是指针:C语言需要指针arithmitic,并且不检查那些。例如:

char a[100];
printf("%p %p\n", a[10], 10[a]);

将打印相同的地址。自a[10] == 10[a] == *(10 + a) == *(a + 10)

在编译时无法检查所有这些指针访问。这与向编译器询问“程序中的所有错误”一样复杂,这需要解决暂停问题。

由于您希望此函数能够在同一进程中运行(可能在不同的线程中),因此您需要在应用程序和“安全”模块之间共享内存,因为这是拥有线程的重点:共享数据以加快速度访问。但是,这也意味着两个线程都可以读写相同的内存。

由于你无法证明指针结束的编译时间,你必须在运行时这样做。这意味着像'a [10]'这样的代码必须被翻译成类似'get_byte(a + 10)'的东西,此时我不再称它为C.

Google Native Client

所以,如果这是真的,谷歌怎么做呢?嗯,与此处的要求(跨平台(包括嵌入式系统))相比,Google专注于x86,除了页面保护之外,还有分段寄存器。这允许它创建一个沙箱,其中另一个线程不以相同的方式共享相同的内存:沙箱通过分段限制为仅更改其自己的内存范围。此外:

  • 组装安全x86汇编构造列表
  • gcc被更改为发出那些安全的构造
  • 此列表以可验证的方式构建。
  • 加载模块后,验证完成

所以这是特定于平台的,并不是一个“简单”的解决方案,尽管是一个有效的解决方案。阅读research paper

<强>结论

无论你走哪条路,你都需要从可以验证的新东西开始 只有这样,您才能从调整现有编译器或生成新编译器开始。但是,尝试模仿ANSI C需要考虑指针问题。谷歌不是在ANSI C上建模他们的沙箱,而是在x86的一个子集上,这使他们能够在很大程度上使用现有的编译器,并且与x86相关联。

答案 1 :(得分:9)

我认为,在阅读谷歌在设计Native Client时所做的一些实施问题和选择时,你会得到很多,这是一个在浏览器中执行x86代码(安全,我们希望)的系统。您可能需要进行一些源代码重写或源代码编译,以便使代码安全,如果不是,但您应该能够依赖NaCL沙箱捕获生成的汇编代码,如果它试图做任何太时髦的事情。

答案 2 :(得分:5)

如果我要这样做,我会调查两种方法之一:

  • 使用CERN的CINT在解释器中运行沙盒代码,并查看限制解释器允许的内容。这可能不会给出非常好的表现。
  • 使用LLVM创建C ++代码的中间表示,然后查看在沙盒Java风格的VM中运行该字节码是否可行。

但是,我同意其他人的说法,这可能是一个非常复杂的项目。看看网络浏览器遇到的错误或挂起插件会破坏整个浏览器的问题。或者查看Wireshark项目的发行说明;似乎几乎每个版本都包含其协议解析器中的问题的安全修复程序,然后影响整个程序。如果C / C ++沙箱是可行的,我希望这些项目现在已经锁定到一个。

答案 3 :(得分:5)

这不是微不足道的,但并不是那么难。

您可以在沙箱中运行二进制代码。每个操作系统都会一整天都这样做。

他们将不得不使用您的标准库(与通用C lib相比)。您的标准库将强制执行您想要施加的任何控件。

接下来,您需要确保它们无法在运行时创建“可运行代码”。也就是说,堆栈不可执行,它们不能分配任何可执行的内存等。这意味着只有编译器(您的编译器)生成的代码才是可执行的。

如果您的编译器以加密方式签署其可执行文件,您的运行时将能够检测到被篡改的二进制文件,并且根本不加载它们。这可以防止他们“捅”到您根本不希望他们拥有的二进制文件中。

使用受控编译器生成“安全”代码和受控系统库,即使使用实际的机器语言代码,也应该提供合理控制的沙箱。

想要施加内存限制吗?把登记入住malloc。想限制分配多少堆栈?限制堆栈段。

操作系统整天使用虚拟内存管理器创建这些受限制的环境,因此您可以在现代操作系统上轻松完成这些操作。

使用现成的虚拟机和字节码运行时,是否值得这样做是值得的。我不能说。

答案 4 :(得分:4)

我偶然发现Tiny C Compiler (TCC)。这可能是我需要的:

*  SMALL! You can compile and execute C code everywhere, for example on rescue disks (about 100KB for x86 TCC executable, including C preprocessor, C compiler, assembler and linker).
* FAST! tcc generates x86 code. No byte code overhead. Compile, assemble and link several times faster than GCC.
* UNLIMITED! Any C dynamic library can be used directly. TCC is heading torward full ISOC99 compliance. TCC can of course compile itself.
* SAFE! tcc includes an optional memory and bound checker. Bound checked code can be mixed freely with standard code.
* Compile and execute C source directly. No linking or assembly necessary. Full C preprocessor and GNU-like assembler included.
* C script supported : just add '#!/usr/local/bin/tcc -run' at the first line of your C source, and execute it directly from the command line.
* With libtcc, you can use TCC as a backend for dynamic code generation.

这是一个非常小的程序,它使黑客攻击成为一个可行的选择(破解GCC?,而不是在这一生中!)。我怀疑它将成为构建我自己的受限编译器的绝佳基础。我将删除对我无法安全的语言功能的支持,并包装或替换内存分配和循环处理。

TCC已经可以bounds checking on memory accesses,这是我的要求之一。

libtcc也是一个很棒的功能,因为我可以在内部管理代码编译。

我不希望它变得容易,但它让我希望我能以较低的风险获得接近C的表现。

但仍想听其他想法。

答案 5 :(得分:3)

完全不可能。这种语言不起作用。在大多数编译器(包括GCC)中,类的概念很早就丢失了。即使它是,也没有办法将每个内存分配与一个活动对象相关联,更不用说“模块”了。

答案 6 :(得分:3)

我没有仔细研究过这个问题,但是那些在Chromium(又名谷歌Chrome)上工作的人正在开发一个几乎就是这样的沙盒,这可能值得一试。

http://dev.chromium.org/developers/design-documents/sandbox/Sandbox-FAQ

它是开源的,所以应该可以使用它。

答案 7 :(得分:2)

如果语言是图灵完成的,那么就不可能制作一个静态代码验证器,它可以确定所有可能的代码,一组代码是安全的还是不安全的。这相当于停止问题。

当然,如果你有一个较低级别的管理员代码或者是一种解释性语言(即模拟机器资源),这一点就没有意义。

执行此操作的最佳方法是在另一个进程中启动代码(ipc并不坏),并在linuxes http://linux.die.net/man/2/ptrace

中捕获系统调用,如Ptrace

答案 8 :(得分:2)

Liran在上面的评论中指出codepad.org。它不适合,因为它依赖于非常繁重的环境(由ptrace,chroot和出站防火墙组成)但是我发现有一些g ++安全开关我认为我会在这里分享:

gcc 4.1.2 flags:-O -fmessage-length = 0 -fno-merge-constants -fstrict-aliasing -fstack-protector-all

g ++ 4.1.2 flags:-O -std = c ++ 98 -pedantic-errors -Wfatal-errors -Werror -Wall -Wextra -Wno-missing-field-initializers -Wwrite-strings -Wno-deprecated -Wno-unused -Wno-non virtual-dtor -Wno-variadic-macros -fmessage-length = 0 -ftemplate-depth-128 -fno-merge-constants -fno-nonansi-builtins -fno-gnu-keywords -fno-elide-constructors -fstrict-aliasing - fstack-protector-all -Winvalid-pch

选项在GCC manual

中说明

真正引起我注意的是堆栈保护标志。我相信这是IBM研究项目(Stack-Smashing Protector)与官方GCC的合并。

  

通过缓冲区溢出检测和变量重新排序功能来实现保护,以避免指针损坏。缓冲区溢出检测的基本思想来自StackGuard系统。

     

新颖的功能是(1)重新排序局部变量以在指针之后放置缓冲区以避免可能用于进一步破坏任意内存位置的指针的损坏,(2)将函数参数中的指针复制到某个区域在本地变量缓冲区之前,以防止可能用于进一步破坏任意内存位置的指针的损坏,以及(3)从某些函数中省略检测代码以降低性能开销。

答案 9 :(得分:1)

8年后,我发现了一个满足我所有原始要求的新平台。 Web Assembly允许您在浏览器中安全地运行C / C ++子集,并对我的要求提供类似的安全限制,例如限制内存访问和防止操作系统和父进程的不安全操作。它已经在Firefox 52中实现,并且有其他浏览器将来支持它的有希望的迹象。

答案 10 :(得分:0)

好主意,但我很确定你用C或C ++做什么是不可能的。如果你放弃了沙盒的想法,它可能会起作用。

Java已经在Maven2中获得了类似的(如在第三方代码的大型库中)系统

答案 11 :(得分:0)

如果你想确定,我认为最好的也许唯一的方法就是沿着单独的进程行,让O / S处理访问控制。编写通用的线程加载器并不是 ,一旦你拥有它,你可以覆盖一些函数来加载特定的库。

答案 12 :(得分:0)

你似乎试图解决两个非问题。在我自己的代码中,我没有内存分配问题或递归或无限循环的问题。

你似乎提出的是与C ++不同,更有限的语言。这当然是你可以追求的,但是正如其他人已经注意到你必须为它编写一个编译器 - 简单的文本处理不会给你你想要的东西。