Ошибка Valgrind с новым массивом [duplicate]

Oneliner с использованием Newtonsoft.Json:

string prettyJson = JToken.Parse(uglyJsonString).ToString(Formatting.Indented);
2
задан razzak 26 January 2016 в 20:35
поделиться

2 ответа

Речь идет не о символе новой строки, а о спецификаторе формата printf. Вы обнаружили, что, возможно, ошибка в strlen(), и я могу сказать, что вы должны использовать gcc.

. Ваш программный код в порядке. Спецификатор формата printf может быть немного лучше, но это не приведет к ошибке valgrind, которую вы видите. Давайте посмотрим на эту ошибку valgrind:

==18929== Invalid read of size 4
==18929==    at 0x804847E: main (in /tmp/test)
==18929==  Address 0x4204050 is 40 bytes inside a block of size 41 alloc'd
==18929==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==18929==    by 0x8048415: main (in /tmp/test)

«Неверное чтение размера 4» - это первое сообщение, которое мы должны понять. Это означает, что процессор выполнил команду, которая загрузила бы 4 последовательных байта из памяти. Следующая строка указывает, что адрес, который был прочитан, был «Адрес 0x4204050 равен 40 байтам внутри блока размера 41 alloc'd».

С помощью этой информации мы можем понять это. Во-первых, если вы замените '\n' на '$' или любой другой символ, будет произведена та же ошибка. Попробуйте.

Во-вторых, мы видим, что ваша строка содержит 40 символов. Добавление символа завершения \0 приводит к тому, что общие байты используются для представления строки до 41.

Поскольку у нас есть сообщение «Адрес 0x4204050 составляет 40 байт внутри блока размером 41 alloc'd», мы теперь все знают о том, что происходит не так.

  1. strdup() выделил правильный объем памяти, 41 байт.
  2. strlen() попытался прочитать 4 байта, начиная с 40-й, который будет распространяться на несуществующий 43-й байт.
  3. valgrind поймал проблему

Это ошибка glib (). Когда-то начинался проект под названием Tiny C Compiler (TCC). По совпадению, glib был полностью изменен так, что нормальные строковые функции, такие как strlen(), больше не существовали. Они были заменены оптимизированными версиями, которые считывают память с использованием различных методов, таких как чтение по четыре байта за раз. gcc был изменен одновременно с тем, чтобы генерировать вызовы для соответствующих реализаций, в зависимости от выравнивания указателя ввода, скомпилированного оборудования и т. д. Проект TCC был оставлен, когда это изменение среды GNU затруднило создание новый компилятор C, убрав возможность использования glib для стандартной библиотеки.

Если вы сообщите об ошибке, поддерживающие glib, вероятно, не исправит ее. Причина в том, что при практическом использовании это, вероятно, никогда не приведет к фактическому сбою. Функция strlen считывает байты 4 за раз, потому что видит, что адреса выровнены по 4 байт. Всегда можно читать 4 байта из 4-байтового выровненного адреса без segfault, учитывая, что чтение 1 байта с этого адреса будет успешным. Поэтому предупреждение от valgrind не выявляет потенциального сбоя, просто несоответствие в предположениях о том, как программировать. Я считаю, что valgrind технически корректен, но я думаю, что нет никаких шансов, что разработчики glib сделают все, чтобы отменить предупреждение.

8
ответ дан Heath Hunnicutt 21 August 2018 в 22:30
поделиться
  • 1
    Я могу подтвердить, что замена \n на любые другие символы приводит к той же ошибке, я также использую gcc. Спасибо за длинное объяснение, все это имеет смысл сейчас. – razzak 26 January 2016 в 21:36
  • 2
    Хороший ответ, но результат, который он дал, не имеет никакого отношения к коду. В выводе он показал нам это total heap usage: 2 allocs, 2 frees, 84 bytes allocated. Где alloc происходит два раза? – Michi 27 January 2016 в 00:02
  • 3
    @Michi - возможно внутри printf. – Jim Buck 27 January 2016 в 00:30
  • 4
    @Michi, вероятно, в сворачивающем стартовом коде: crt0.c. Во всяком случае, неважно. – Heath Hunnicutt 27 January 2016 в 01:15
  • 5
    @razzak - Он выглядит здесь ( bugzilla.redhat.com/show_bug.cgi?id=678518 ), как будто никакого обходного пути valgrind нет, но компиляция с -fno-builtin-strlen будет генерировать более медленный код, но не вызывает это предупреждение. – Heath Hunnicutt 27 January 2016 в 19:10

Сообщение об ошибке, похоже, указывает на то, что это strlen, прочитанное за буфером malloc ed, выделенным strdup. На 32-битной платформе оптимальная реализация strlen может считывать по 4 байта за раз в 32-разрядный регистр и делать несколько бит-скрипов, чтобы увидеть, есть ли там нулевой байт. Если в конце строки осталось меньше 4 байтов, но 4 байта по-прежнему читаются для выполнения проверки нулевого байта, тогда я мог видеть, что эта ошибка печатается. В этом случае предположительно исполнитель strlen будет знать, «безопасно» ли это сделать на конкретной платформе, и в этом случае ошибка valgrind является ложным положительным.

3
ответ дан Jim Buck 21 August 2018 в 22:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: