使valgrind在未初始化的值上快速失败

时间:2019-07-02 05:41:57

标签: c valgrind memcheck

Valgrind memcheck使用了一堆启发式方法来避免对“无害”使用未初始化的值的误报,因为这种使用在正确和不正确的功能代码中都是常见的。

尤其是在您以一种严肃的,也许是“不可逆的”方式实际使用这样的值之前,例如基于该值的跳跃,它不会令人沮丧。

这意味着有时错误会发生在距问题根源很远的地方,甚至无法确定涉及哪个值。是否有某种方法可以在运行时“检查”值,例如use(x),如果x未初始化,将使Valgrind在该位置发出错误?

2 个答案:

答案 0 :(得分:1)

您可以使use(x)宏使用Valgrind VALGRIND_CHECK_VALUE_IS_DEFINED Client Request来当场出错。

为此,请添加valgrind/memcheck.h并将您的宏定义为

#define use(x) VALGRIND_CHECK_VALUE_IS_DEFINED(x)

并确保始终传递左值。

您还可以使用--track-origins=yes运行memcheck进行更严格的跟踪,该跟踪应显示未初始化数据的起源。

另请参阅the Valgrind FAQ on uninitialised value errors,其中解释了这两种方法,以及为何Valgrind不会抱怨复制未初始化的值。

答案 1 :(得分:0)

通常,类似这样的事情需要对代码进行检测(通过工具自动完成,或手动插入源代码中)。

如我的评论中所述,如果您可以自己插入use(x)语句,则可以执行以下操作:

static FILE* dev_null = 0;
static void use_var(char* var_addr, size_t var_size)
{
  if (dev_null == 0) /* make sure we only open FILE* dev_null once */
  {
    dev_null = fopen("/dev/null", "wb");
    assert(dev_null != 0); /* opening /dev/null CAN actually fail */
  }
  size_t i;
  for (i = 0; i < var_size; ++i)
  {
    fputc(var_addr[i], dev_null); /* read every byte in the variable, write to dev_null */
  }
}

#define use(x) use_var((char*)&x, sizeof(x))

/* Example of usage */
int main()
{
  long x = 80;
  struct { double d; char c[123]; } y;
  memset(&y, 0, sizeof(y) - 1); /* initialize all bytes in y, except the last */
  double z[2] = {3.14, 42.0};

  use(x);
  use(y);
  use(z);

  return 0;
}

但是,使用struct包含对齐填充的问题。 填充永远不会被用于任何东西,因此这可能是传递未初始化数据的合法原因。在这种情况下,Valgrind可能会导致有关未初始化读取的虚假错误。

这两篇文章专门讨论了这个问题: Is there anyway a valgrind message "Conditional jump or move depends on uninitialized value" can be a so called 'false positive'

Should I worry about "Conditional jump or move depends on uninitialised value(s)"?