【问题标题】:How to get a warning in GCC for unsigned integer overflow instead of wrap-around?如何在 GCC 中获得无符号整数溢出而不是环绕的警告?
【发布时间】:2019-02-23 02:38:38
【问题描述】:

测试环境

  • Linux
  • 英特尔 x86-64 GCC 8.2.1
  • 启用标志:-Wextra -Wall -Wfloat-equal -Wundef -Wshadow -Winit-self -Wpointer-arith -Wcast-align -Wstrict-prototypes -Wstrict-overflow=5 -Wwrite-strings -Waggregate-return -Wcast-qual -Wswitch-default -Wswitch-enum -Wconversion -Wunreachable-code -Wformat=2 -pedantic -pedantic-errors -Werror-implicit-function-declaration -Wformat-security -fstrict-overflow
  • sizeof(long) 是 8。
  • sizeof(int) 是 4。

示例 1,收到警告,很好:

long x = 2147483647 * 3;

示例2,没有警告,不好:

long x = 2147483647U * 3U; // Suffix U

unsigned int a = 2147483647;
unsigned int b = 3;
long x = a*b;

示例 3,没有警告,但按预期工作:

long x = 2147483647L * 3L; // Suffix L

在示例 2 中,我知道它是环绕而不是整数溢出,但这些是编译器无法警告的情况?

来自标准:

(6.3.1.8)

否则,整数提升将在两个操作数上执行。然后 以下规则适用于提升的操作数:

如果两个操作数的类型相同,则无需进一步转换。 否则,如果两个操作数都是有符号整数类型或都具有无符号整数类型,则整数转换等级较小的操作数将转换为等级较大的操作数类型。

否则,如果无符号整数类型的操作数的秩大于或等于另一个操作数类型的秩,则将有符号整数类型的操作数转换为无符号整数类型的操作数的类型。

否则,如果有符号整数类型的操作数的类型可以表示无符号整数类型的操作数的所有值,则将无符号整数类型的操作数转换为有符号整数类型的操作数的类型输入。

否则,两个操作数都转换为无符号整数类型,对应有符号整数类型的操作数。

(6.5):

如果在计算表达式期间出现异常情况(即,如果结果未在数学上定义或不在其(类型)的可表示值范围内,则行为未定义。


开始使用带有标志 -fsanitize=unsigned-integer-overflow 的 Clang,这对处理来自环绕的不需要的值有很大帮助。这不是整数溢出,但不是预期值。由于 GCC,直到现在还不支持这样的警告,继续使用 Clang。

【问题讨论】:

  • 仅供参考:在 C 标准的术语中,unsigned 整数 never 溢出;他们环绕。
  • 在示例二中,您将两个无符号整数相乘,然后将结果回绕,然后该结果将存储在 long 中。
  • 值 2147483645 应该是环绕?

标签: c gcc gcc-warning


【解决方案1】:

有符号整数溢出调用undefined behavior,而无符号整数溢出定义明确。

对于无符号整数,会发生溢出,就好像这些值是在给定类型的最大值大一模后计算得出的。换句话说,如果类型为n 位宽,则只保留结果的低位n 位。这实际上不是溢出,而是称为 wraparound

这在section 6.5p9中有详细说明:

有符号整数的非负值范围
type 是相应无符号整数的子范围 类型,以及每种类型中相同值的表示 是一样的。 涉及无符号操作数的计算可以 永不溢出,因为无法表示的结果 得到的无符号整数类型被减少模 比最大值大一的数字 由结果类型表示。

因为这种行为是明确定义的,编译器触发警告是没有意义的。

在你的第二个例子中:

long x = 2147483647U * 3U; 

乘法是在 unsigned 类型上完成的,因此数学结果 6442450941 环绕到 2147483645,它在 long 的范围内。没有溢出(只是环绕),也没有超出范围的转换,所以没有警告。

【讨论】:

  • 经过您的解释,我有了一个更好的主意。但我认为环绕的结果类型在intunsigned int 的范围内,而不是在long 的范围内,因为表达式是在unsigned int 上完成的,这就是结果类型。
  • GCC中是否存在环绕警告的概念?
  • @UndefinedBehavior 没错,使用unsigned int 类型发生了环绕,结果值 2147483645 也具有该类型。然后将该值转换为类型long,然后再分配给x
【解决方案2】:

不是 GCC,但一些静态分析器规则会警告此类“溢出”。

例如,示例 1 和 2 都会被 MISRA C 检查器标记, 因为这些在表达式中溢出了常量——这表明了程序员的错误。 2012 年规则 12.4:“常量表达式的计算不应导致无符号整数回绕。”

INT30-C 在 SEI CERT C 编码标准中描述了一个更通用的情况,它建议避免安全应用程序的任何类型的溢出,并提供遵守规则的自动检查器列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 2011-11-07
    • 2012-02-29
    • 1970-01-01
    • 1970-01-01
    • 2021-09-12
    相关资源
    最近更新 更多