“普通人不想自由。他只是想安全。” - HL 门肯
我正在尝试编写非常安全的 C。下面我列出了我使用的一些技术,并询问它们是否像我认为的那样安全。请不要犹豫,将我的代码/成见撕成碎片。任何能找到最微不足道的漏洞或教给我新想法的答案都将受到高度重视。
从流中读取:
根据GNU C Programming Tutorial getline:
getline 函数将通过 realloc 函数根据需要自动扩大内存块,因此永远不会出现空间短缺——这是 getline 如此安全的原因之一。[..] 请注意,无论输入多长,getline 都可以安全地处理您的输入行。
我假设 getline在所有输入下都应该防止从流中读取时发生缓冲区溢出。
- 我的假设正确吗?是否存在可能导致漏洞利用的输入和/或分配方案?例如,如果流中的第一个字符是一些奇怪的控制字符,可能是 0x08 BACKSPACE (ctl-H)。
- 有没有做任何工作来从数学上证明 getline 是安全的?
Malloc 在失败时返回 Null:
如果 malloc 遇到错误 malloc 返回一个 NULL 指针。这带来了安全风险,因为仍然可以将指针算术应用于 NULL (0x0) 指针,因此维基百科建议
/* Allocate space for an array with ten elements of type int. */
int *ptr = (int*)malloc(10 * sizeof (int));
if (ptr == NULL) {
/* Memory could not be allocated, the program should handle
the error here as appropriate. */
}
安全 sscanf:
使用sscanf时,我养成了将要提取的字符串大小分配给输入字符串的大小的习惯,希望避免出现溢出的可能性。例如:
const char *inputStr = "a01234b4567c";
const char *formatStr = "a%[0-9]b%[0-9]c":
char *str1[strlen(inputStr)];
char *str2[strlen(inputStr)];
sscanf(inputStr, formatStr, str1, str2);
因为 str1 和 str2 是 inputStr 的大小,并且不能从 inputStr 读取超过 strlen(inputStr) 的字符,所以考虑到 inputStr 的所有可能值导致缓冲区溢出,这似乎是不可能的?
- 我对么?有没有我没有想到的奇怪的角落案例?
- 有没有更好的方法来写这个?已经解决的库?
一般的问题:
虽然我已经发布了大量问题,但我不希望任何人都能回答所有问题。这些问题更像是我正在寻找的各种答案的指南。我真的很想学习安全的 C 思维方式。
- 还有哪些其他安全的 C 习惯用法?
- 我需要经常检查哪些极端情况?
- 如何编写单元测试来强制执行这些规则?
- 如何以可测试性或可证明正确的方式强制执行约束?
- 任何推荐的 C 静态/动态分析技术或工具?
- 您遵循哪些安全的 C 实践,您如何向自己和他人证明它们的合理性?
资源:
许多资源都是从答案中借来的。
- David Wheeler的 Linux 和 Unix 安全编程 HOWTO
- 安全 C 编程 - SUN Microsystems
- 通过示例进行不安全编程
- 添加更多 NOPS - 涵盖这些问题的博客
- CERT 安全编码倡议
- 探伤器- 静态分析工具
- Yannick Moy使用 Thm Provers 证明安全性
- 安全库