【问题标题】:Catching overflow of left shift of constant 1 using compiler warning?使用编译器警告捕获常量 1 左移的溢出?
【发布时间】:2009-07-16 18:57:22
【问题描述】:

我们正在 Linux 内核中编写代码,因此,尽管我尽可能尝试,但我无法让 PC-Lint/Flexelint 在 Linux 内核代码上工作。内置符号等太多了。但这是一个附带问题。

我们有任意数量的编译器,从 gcc 开始,但也有其他的。随着时间的推移,它们的警告选项越来越强大,它们也是非常强大的静态分析工具。

这是我想要捕捉的。是的,我知道它违反了一些在代码审查中很容易发现的东西,例如“没有幻数”和“当心位移”,但前提是你碰巧看到了那段代码。无论如何,这里是:

unsigned long long foo;
unsigned long bar;

[... lots of other code ...]

foo = ~(foo + (1<<bar));

进一步更新了问题描述——即使 bar 限制为 16,仍然是个问题。澄清一下,问题是隐含的 int 类型的常量,它在计划外使复杂的表达式违反了所有计算都以相同的大小和符号进行的规则。

问题:'1' 不是 long long,但是,作为一个小值常量,默认为 int。因此,即使 bar 的实际值从未超过,例如 16,(1&lt;&lt;bar) 表达式仍然会溢出并破坏整个计算。

可能正确的解决方案:改写 1ULL。

是否有众所周知的编译器和编译器警告标志会指出这个(修订后的)问题?

【问题讨论】:

  • 无论 int 的大小是多少,如果 bar 太大,则移位是未定义的(我认为。无论如何,有些不好)。您似乎要求编译器警告您第二个变量操作数要在 int 上移位。但是您说问题在于“1”并不长:您是否希望没有关于长变量移位的警告?我不明白为什么编译器编写者会期望该警告有用,而关于
  • @onebyone:假设int 是32 位,long long 是64,~(foo + (1&lt;&lt;bar)) 仅适用于0&lt;=bar&lt;32,而~(foo + (1LL&lt;&lt;bar)) 适用于0&lt;=bar&lt;64。我假设当写前者而不是后者时,OP 想要一个警告。
  • 当然,但是如果 foo 是 int 并且 bar 在逻辑上被限制(但不是按类型)为 1 或 2,他是否希望发出警告? foo 是 long long 而 bar 是 1 还是 2 怎么样?由于溢出结果未定义,对任何有符号常量进行乘法或加法的警告?编译器通常无法识别逻辑约束,因此我希望任何此类警告都极易出现误报。并不意味着这是一个坏主意,我只是在猜测为什么很难定义它应该是什么,并且无论如何编译器编写者的优先级很低。
  • 如果编译器警告隐式加宽强制转换(即 (int)(1&lt;&lt;bar)(long long)(1&lt;&lt;bar) 可以解决 OP 的情况,但我看不到任何类似的选项。更不用说,像有人说,它会产生很多误报......

标签: c static-analysis compiler-warnings bit-shift integer-overflow


【解决方案1】:

我不确定你想标记什么标准 这种结构很可疑。有明显的 如果 bar 的值大于 int 的大小(以位为单位),但通常是编译器 不会知道的。 从启发式、错误发现工具的角度来看, 有很好的模式来区分可能的错误 正常的结构是避免太多错误的关键 积极因素(这使用户讨厌该工具并拒绝 使用它)。

我的 URL 中的开源工具将逻辑移位标记为更大的数字 比类型的大小,但它主要是一个验证 用于关键嵌入式软件的工具,并期待大量工作 如果您打算在 Linux 内核上使用它,请使用它 及其相关的结构和其他困难。

【讨论】:

  • 我会看看你的网站。我意识到,通过建议 64 岁的酒吧,我误导了。我将其更改为 16。即便如此,仍然存在问题,即棉绒被捕获,但我正在寻找 gcc 来捕获。问题本身并不是太大的转变。这是一个复杂的表达式,其中包含了意想不到的多种类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-06
  • 2018-01-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多