C语言非常强大,并且在很多地方使用,尤其是在Linux内核中,但是它也非常危险。 一位Linux内核开发人员描述了如何处理C安全漏洞。您几乎可以在C中做任何事情,但这并不意味着需要做。 C代码非常快,但是没有安全带。 即使您是专家,像
大多数Linux内核开发人员一样 ,致命错误仍然可能发生。
除了诸如
指针别名之类的陷阱外,C语言还有一些根本的,未纠正的错误等待着他们的受害者。 这些是Google Linux内核安全工程师
Case Cook在温哥华举行
的Linux安全会议上解决的漏洞。
“ C是一种汇编程序。 库克说,这几乎是机器代码,指的是数百名了解并欣赏C语言应用程序速度的同事的听众。但坏消息是“ C语言带有一些危险的包,、模糊的行为和其他弱点,导致
安全漏洞和易受攻击的基础架构。”
如果在项目中使用C,则应注意安全性问题。
Linux内核保护
随着时间的推移,Cook及其同事发现了本机C的许多问题。为了解决这些问题,启动了
内核自我保护项目 。 他缓慢而稳定地致力于保护Linux内核免受攻击,并从那里删除有问题的代码。
库克说,这很困难,因为“内核需要做特定于特定体系结构的事情,以进行内存管理,中断处理,调度等等”。 大量的代码涉及需要仔细检查的特定任务。 他说,例如,“ C没有用于设置页表或切换到64位模式的API”。
有了这样的负载,并且C语言中的标准库很弱,行为就太模糊了。 Cook引用了Raf Levien
的博客文章,并表示同意:
“有了未定义的行为,一切皆有可能 。
”库克举了一些具体的例子:“未初始化的”变量的内容是什么? 这就是我记忆中的全部! void指针中没有类型,但是可以通过它们调用类型化的函数吗? 当然可以! 程序集是一样的:您可以联系任何地址! 为什么
memcpy()
没有参数“最大目标长度”? 没关系,按照您所说的去做。 所有内存区域都相同!”
忽略警告...但并非总是如此
其中一些功能相对易于处理。 库克评论说:“ Linus [Torvalds]喜欢总是初始化局部变量的想法。 所以就去做。”
但是有保留。 如果在switch中初始化局部变量,则会收到警告:“由于编译器处理代码的方式,该语句将永远不会执行
[-Wswitch-unreachable]
”。 此警告可以忽略。
但是并非所有警告都可以忽略。 “可变长度数组总是不好的,”库克说。 其他问题包括堆栈耗尽,行溢出和页面保护冲突。 另外,库克提请注意
VLA的
缓慢性 。 从内核中删除所有VLA可使性能提高13%。 提高速度和安全性是双重利益。
尽管VLA几乎已从内核中删除,但它们仍保留在某些代码中。 幸运的是,使用
-Wvla
编译器
-Wvla
可以轻松找到
-Wvla
。
C的语义中隐藏了另一个问题。如果开关中缺少中断,那么程序员是什么意思? 跳过中断会导致在多种情况下执行代码。 这是一个众所周知的问题。
如果要在现有代码中查找break / switch语句,则可以使用
-Wimplicit-fallthrough
添加新的switch语句。 这实际上是一个注释,但是现代编译器会对其进行解析。 您还可以
使用“ fallthrough”注释来
明确标记不存在中断 。
Cook还检查
板坯内存分配的边界时发现性能
下降 。 例如,检查
strcpy()-family
将性能降低2%。 诸如
strncpy()
类的替代方法
strncpy()
其自身的问题。 事实证明Strncpy并不总是以空字符结尾。 库克遗憾地对观众说:“我在哪里可以获得最好的API?”
在问答环节中,一位Linux开发人员问:“我可以摆脱旧的,错误的API吗?” 库克回答说,Linux支持旧版API已有一段时间了。 但是,Torvalds拒绝了这个想法,认为如果任何API过时,则应将其完全丢弃。 但是,库克补充说,永远删除该API会“在政治上是危险的”。 因此,当我们陷入困境时。
长期解决问题? 更多开发人员了解安全问题
库克预见到漫长而艰难的旅程。 创建Linux C语言的想法曾经很吸引人,但没有。 他说,危险代码的真正问题在于“人们不想清理代码-不仅是不良代码,而且还有C本身。” 与所有开源项目一样,“我们需要更多敬业的开发人员,审阅者,测试人员和支持专家。”
危险的C:课程
- C是一种成熟而强大的语言,但它会带来技术难题和安全问题。
- Linux开发人员特别注意确保C的安全(不损失其功能),因为大多数操作系统都是在C上编写的。
- Linux Linux内核安全工程师确定了特定的语言漏洞,并说明了如何避免这些漏洞。