Заставьте валгринда быстро потерпеть неудачу на неинициализированном значении - PullRequest
4 голосов
/ 02 июля 2019

Valgrind memcheck использует кучу эвристик, чтобы избежать ложных срабатываний, при использовании «безвредных» неинициализированных значений, поскольку такие применения распространены как в корректном, так и в некорректном, но иным образом работающем коде.

В частности, оно не преграждается, пока вы на самом деле не используете такое значение серьезным, возможно, «необратимым» образом, например, прыгаете в зависимости от его значения.

Это означает, что иногда ошибка возникает очень далеко от источника проблемы, и даже невозможно определить, какое значение имеет место. Есть ли какой-нибудь способ «проверить» значение во время выполнения, например use(x), который заставит Valgrind выдать ошибку в этом месте, если x неинициализирован?

Ответы [ 2 ]

1 голос
/ 02 июля 2019

Вы можете сделать свой макрос use(x), используя Valgrind VALGRIND_CHECK_VALUE_IS_DEFINED Клиентский запрос , чтобы получить ошибку на месте.

Для этого включите valgrind/memcheck.h и определите свой макросas

#define use(x) VALGRIND_CHECK_VALUE_IS_DEFINED(x)

И обязательно всегда передавайте lvalue.

Вы также можете запустить memcheck с помощью --track-origins=yes для более интенсивного отслеживания, которое должно показать, откуда возникли неинициализированные данные.

См. Также FAQ Valgrind по ошибкам неинициализированных значений , который объясняет и то, и другое, а также почему Valgrind не жалуется на копирование неинициализированных значений.

0 голосов
/ 02 июля 2019

Обычно для подобных вещей требуется инструментарий кода (либо выполняется автоматически с помощью инструмента, либо вставляется вручную в исходный код).

Как отмечено в моем комментарии, если вы можете работать с необходимостью вставлять операторы 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 s, которые содержат отступ выравнивания. Заполнение никогда не используется ни для чего, поэтому это может быть законной причиной для передачи неинициализированных данных. Valgrind может в этом случае вызвать ложные ошибки, связанные с неинициализированными чтениями.

Эти два сообщения обсуждают эту проблему конкретно: Есть ли в любом случае сообщение valgrind «Условный переход или перемещение зависит от неинициализированного значения» может быть так называемым «ложноположительным»

Должен ли я беспокоиться о том, что «условный переход или перемещение зависит от неинициализированных значений»?

...