【问题标题】:clang-tidy: Assigned value is garbage or undefined when accessing raw memory of an array or structclang-tidy:访问数组或结构的原始内存时分配的值是垃圾或未定义
【发布时间】:2021-10-15 06:20:30
【问题描述】:

运行 clang-tidy 时,我在访问数组地址时收到警告:

src/main.cpp:10:9: warning: Assigned value is garbage or undefined [clang-analyzer-core.uninitialized.Assign]
    sum += *(addr++);
        ^

代码是从一个更复杂的示例中剥离出来的(完整的校验和实现 - 取自 RFC1071 - 并使用结构而不是数组):

#include <iostream>
#include <cstring>

static uint16_t csum(void* buffer, size_t size)
{
  uint32_t sum = 0;
  auto *addr = static_cast<uint16_t *>(buffer);
  while( size > 1 )  {
    /*  This is the inner loop */
    sum += *(addr++);
    size -= sizeof(*addr);
  }
  return static_cast<uint16_t>(~sum);
}

int main (int argc, char* argv[])
{
  uint32_t test[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  //memset(&test, 0, sizeof(test));
  uint16_t sum = csum(test, sizeof(test));
  std::cout << argc << " " << argv[0] << " " << sum << " " << sizeof(test) << std::endl;
}

clang-tidy 在数组的类型为 uint32_tuint64_t(或使用任何自定义打包结构)时显示此 value is garbage or undefined,但在 uint16_tuint8_t 时不显示 - 一些对齐问题?

但至少对我来说更令人惊讶 - 在取消注释 memset 时,uint32_tuint64_t 或任何自定义打包结构不会显示警告 - 为什么?之前没有正确初始化吗?我是否遇到了一些没有正确定义 C/C++ 的区域?

或者clang-tidy 会分析csum 的每一个用法——还有那些buffer 的大小与size 变量不匹配的用法?

【问题讨论】:

  • 如果您将 1 字节对齐的指针输入到转换为 uint16_t 的指针中,则应该会出现对齐问题。您必须通过 memcpy 将其转换为临时的 - 不要担心性能,编译器会消除它并将其读取为“哦,数据可能未对齐”。
  • 即使您确实传递了类似struct { uint32_t; uint8_t; } 的内容,由于未定义的填充字节,警告仍然有效。
  • 我最初使用了一个只有 uint16_t 和 uint8_t 的结构并使用了__attribute__((packed)) - 并得到了同样的警告。但也许这个属性没有被 clang-tidy 检查。

标签: c++ c++11 clang-tidy


【解决方案1】:

对于不是uint16_t 的所有内容,该警告在技术上是正确的。

您不能只是将指向某个类型化内存的指针重新解释转换为另一种不兼容的类型。其结果是未定义的行为,并且超出了 C++ 标准为您提供的任何保证。这里唯一出乎意料的情况是您没有收到uint_8 情况的警告,但我希望这只是为了避免在其他用例中出现误报。

为什么是 UB?因为您正在对超出定义的内存布局做出假设。你的代码例如由于这种演员阵容,小端机器和大端机器之间的行为完全不同。

您可以使用该模式绕过该警告:

uint8_t *src;
uint16_t tmp;
std::memcpy(&tmp, src, sizeof(tmp));
src += sizeof(tmp);

您不会执行任何非法强制转换,并且实际上对原始内存访问承担所有责任。

【讨论】:

  • 我将演员表更改为uint64_t(并相应地更改了代码)。因此,uint8_tuint16_tuint32_tuint64_t 没有警告 - 但 __attribute__((packed)) 结构仍然不起作用。此外,当按值传递此结构时(例如使用模板方法),警告消失了 - 但通过 const reference 传递它时,我再次收到相同的警告。
  • 你有什么想法,为什么memset 会抑制警告?关于字节序:校验和适用于具有网络字节顺序内容的结构 - 但 clang-tidy 不能假设。
  • memset 可能只是提示clang您已明确设置内存内容,因此不能说您无法知道更复杂的“0”的内存布局在重新解释演员表期间输入。
  • 虽然将施法目标更改为更大的目标时警告消失了,但没有任何意义......
猜你喜欢
  • 2013-03-02
  • 1970-01-01
  • 2015-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-22
相关资源
最近更新 更多