Диагностика загадочного вальгринда «неверная запись»

[Это долго и полно деталей. Мои конкретные вопросы представлены жирным шрифтом Вопрос ниже.]

Резюме

Мы запускаем некоторые из наших тестовых наборов под valgrind и сталкиваемся с ошибкой, которая не имеет большого смысла для меня. Я ищу совет по выяснению более подробно, что может пойти не так.

  • Вальгринд жалуется на «неверную запись размером 8».
  • Ошибка постоянна от запуска к запуску, но появляется и уходит с изменениями кода, которые не должны иметь значения, с разными версиями компилятора / stdlib и т. Д.
  • Адрес, по которому пишется, находится в стеке, и, насколько я вижу, это вполне разумный адрес для нашего кода для записи.
  • Его выравнивание соответствует размеру записи.
  • Место, где это происходит, глубоко внутри стандартной библиотеки.

Все это пахнет так, как будто настоящая проблема в другом: что-то искажается и в дальнейшем приводит к путанице. Но это первый Отчеты о проблемах в valgrind, поэтому, если в другом месте есть переполнение памяти, то valgrind не сможет его поймать. Я подозреваю, что либо я упускаю что-то очевидное, либо есть тонкая проблема, на которую могут указывать те, кто обладает большим опытом, чем я.

Некоторые детали

Вот некоторые детали и некоторые конкретные вопросы.

Это на Linux-машине с Ubuntu 14.04 на оборудовании x64.

Вот жалоба Вальгринда в одном довольно типичном случае:

==14259== Invalid write of size 8
==14259==    at 0x662BBC9: __printf_fp (printf_fp.c:663)
==14259==    by 0x6629792: vfprintf (vfprintf.c:1660)
==14259==    by 0x664D578: vsnprintf (vsnprintf.c:119)
==14259==    by 0x52DCE0F: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259==    by 0x52E3263: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_float<double>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, char, double) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259==    by 0x52E354F: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, double) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259==    by 0x52EEAF4: std::ostream& std::ostream::_M_insert<double>(double) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259==    by 0x694725: CRVinfo::appendValue(std::string const&, double) (CRVinfo.cpp:197)
==14259==    by 0x6902DB: CRVdouble::info(CRVinfo&) const (CRVdouble.cpp:103)
==14259==    by 0x6913B4: CRVcollection::info(CRVinfo&) const (CRVcollection.cpp:60)
==14259==    by 0x6913B4: CRVcollection::info(CRVinfo&) const (CRVcollection.cpp:60)
==14259==    by 0x68F87F: CRVvalue::generate() (CRVvalue.cpp:71)
==14259==  Address 0xffeffde68 is on thread 1's stack
==14259==  in frame #0, created by __printf_fp (printf_fp.c:161)

Вещи, начинающиеся с «CRV», наши; все вышеперечисленное находится в libstdc ++ и glibc. Ubuntu 14.04 использует версию 2.19 glibc — за исключением того, что фактически он использует eglibc 2.19, а не обычный glibc 2.19; вы можете найти соответствующую версию printf_fp.c Вот.

Запуск Вальгринда с --vgdb и запросить у gdb утверждения о разборке (в соответствии с исходным кодом, указанным выше), что инструкция, которую мы на самом деле собираемся выполнить, когда valgrind останавливает нас, callq __mpn_lshift,

Самый верхний кадр стека, включающий «наш» код, выглядит так:

void CRVinfo::appendValue(const std::string &name, double value){

addIndent();
addElementBegin(name);

std::ostringstream  oss;
oss << value;
m_valueTree.append(oss.str());

addElementEnd(name);
}

и это внутри oss << value; что проблема возникает. m_valueTree это std::string; Вы можете догадаться, что за вещь addIndent а также addElementBegin делать; последний использует для этого поток строк, первый — нет. (Вероятно, нерелевантное примечание: вы можете подумать, что это выглядит неэффективно, и вы были бы правы, но это совсем не критичный для производительности код.)

Итак, в любом случае, мы получаем неверную запись размером 8 по адресу 0xffeffde68, на callq инструкция. Вы ожидаете callq записать в память, указанную rspи так оно и есть (я проверил, что на данный момент rsp равно 0xffeffde68) … но valgrind возражает против этого, и мне не понятно почему.

(Одно очевидное предположение может заключаться в том, что мы переполняем наш стек. Но (1) я бы подумал, что это произойдет по адресу, выглядящему более круглым, и (2) я попытался увеличить размер стека, и это не заставил эти жалобы Valgrind уйти, и (3) я бы ожидал, что при переполнении стека произошла ошибка, и (4) мы все равно не использовали много стека на этом этапе, в самом начале был в состоянии исследовать, rsp 0xfff000598, поэтому мы использовали менее 10 Кбайт стека в момент сбоя.)

Вопрос: Это должно быть очевидно для меня какие Вальгринд возражает об этом написать? Если нет, есть ли способ заставить Вальгринда рассказать мне больше о том, почему ему это не нравится?

Вопрос: Возможно ли, что непосредственная проблема здесь — это ошибка в valgrind (хотя, возможно, спровоцированная некоторым более ранним неправильным поведением в нашем коде)? Если так, есть ли хороший способ отследить такие вещи или исключить их?

Вопрос: Похоже ли это на любую известную проблему с glibc или libstdc ++? (Такой поиск в Интернете, который я делал до сих пор, не выявил такой известной проблемы.)

Больше информации, если это полезно

Если я разрешу продолжить выполнение после этой недопустимой записи, то valgrind пожалуется — внутри __mpn_lshift функция вызывается здесь — недействительным читать размера 8. Он читает с того же адреса и разбирает в GDB, что неудивительно, что это retq инструкция в конце __mpn_lshift это виновато.

Ни один из моих стековых фреймов не выглядит ужасно большим. Valgrind не жалуется на большие кадры стека, спрашивает, переместился ли стек, предлагает увеличить --max-stacksizeили что-нибудь в этом роде.

На другом компьютере с немного другой версией gcc и, возможно, другими версиями стандартных библиотек, valgrind снова сообщает о недопустимой записи размером 8 в __printf_fp но в другой части этого и на этот раз не по инструкции звонка. (К сожалению, это было на компьютере коллеги, и, поскольку мы наблюдали это, были сделаны некоторые изменения, которые сделали его версию такой же ошибочной, как и моя, поэтому я не могу дать более подробную информацию с уверенностью. Но я на 95% уверен, что сбой произошел на mov инструкция, и писал строго внутри текущего стека кадра.)

6

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]