哪些整数运算不安全?

时间:2018-06-13 12:15:16

标签: c++ arithmetic-expressions

我的应用程序评估用户指定的一些整数表达式。我想检测所有潜在的错误并报告它们。

所有计算均在int64_t(已签名)中完成。公式可能包括几乎所有C ++二元运算符(+-*/%|||&&&和六个比较运算符)和整数(可能是负数)。

问题是:在评估可能使我的程序终止的表达式时可能发生什么错误?我想出了其中两个:

  1. 划分(或模数)零
  2. std::numeric_limits<int64_t>::min()除以-1。
  3. 也可能出现有符号整数溢出,但据我所知,在这样的设置中,它不能对大多数CPU造成任何伤害,所以我们忽略它。

3 个答案:

答案 0 :(得分:5)

这是一个很好的参考:https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow

正如它解释的那样,有符号整数溢出是未定义的行为。您可能认为这无关紧要,因为您发现INT64_MAX + x在您的特定系统上没有做任何奇怪的事情。您可能还认为它永远不会做任何奇怪的事情,因为优化器无法知道x的值。

但未定义的行为仍未定义,并且在许多其他可能的结果中,某些平台可能会终止您的程序(您说要避免),因为它们在硬件中实现溢出陷阱或算术异常。

要编写符合要求的C ++程序,对有符号整数进行算术运算,必须先检查它们的值。一种便宜又简单的方法可能就是在添加或减去之前检查每个整数是否在[INT64_MIN/2, INT64_MAX/2]内。有关更详细的方法,请参阅此处:How to detect integer overflow?

答案 1 :(得分:1)

本身不是不安全的操作。它是有问题的有符号整数溢出,undefined behavior。这样(几乎)所有操作员都可以参与导致UB,尽管你可能会使用arithmetic operators到达那里。长话短说:不要让有符号整数溢出/调用UB。

答案 2 :(得分:0)

总结先前的观察结果,与您的操作列表相关的未定义行为恰好有两个可能的原因:

  • 除以零
  • 溢出(无论是消极的还是积极的) - 否定std::numeric_limits<int64_t>::min()的例子 - 无论是通过除法还是其他方式 - 只是一个例子。

只有算术运算符(列表中的前五个)受这些问题中的任何一个影响,所有其他算法对所有输入都有明确定义的行为。

我想要做的是扩展整数溢出和未定义行为的危险。首先,我强烈建议你观看Piotr Padlewski的Undefined Behavior is Awesome和Chandler Carruth的Garbage In, Garbage Out演讲。

另外,请考虑整数溢出是如何在CVE中重复出现的主题(软件漏洞报告)。整数溢出本身通常不会造成直接损坏,但是由于溢出会导致许多其他问题。你可以把溢流比作针刺,它本身几乎无害,但可以帮助危险的毒素和细菌绕过你身体的免疫系统。

例如,至少有一个hole in OpenSSH与整数溢出直接相关,而且这个甚至不涉及任何“疯狂”的编译器优化,或者就此而言,根本不涉及任何优化。 / p>

最后,存在像UBSAN(Clang / GCC中未定义的行为消毒器)之类的东西。如果您在一个地方允许有符号整数溢出,并尝试从UBSAN获得有意义的结果,则可能会出现意外的陷阱和/或过多的误报。

TL; DR:避免所有未定义的行为。

John Zwinck已经提到添加范围检查作为补救措施,小心避免任何会溢出的中间操作。假设您只需要支持GCC,那么如果您感到懒惰,还有两个命令行选项可以帮助您:

  • -ftrapv将导致有符号整数溢出陷阱。
  • -fwrapv将导致有符号整数换行溢出。

哪一个更安全?实际上,这在很大程度上取决于您的应用领域。你的意见似乎是崩溃等于“更安全”的可能性更小。但是,可能会考虑上述OpenSSH漏洞。当从远程客户端提供垃圾数据(可能还有shellcode)时,您希望SSH服务器做什么?

  • A)终止(与-ftrapv一样)
  • B)继续并可能执行shellcode(与-fwrapv
  • 一样

我很确定大多数管理员会选择A),如果要终止的进程不是侦听实际套接字的进程,而是特别fork()来处理目前的连接,所以甚至没有多少DoS。换句话说,虽然-fwrapv为您提供了定义的行为,但并不一定意味着行为在使用点期望,因此“安全”

此外,我建议您避免在脑海中制造错误的二分法,例如进程崩溃与继续使用垃圾数据。您可以选择各种错误处理策略如果您添加了正确的检查,无论是使用特殊的返回值还是异常处理,都可以安全地离开狭小的空间而无需完全停止服务请求。