сбой gcc free() только в многопоточном окружении

#multithreading #gcc #free

Вопрос:

проблема:

Мое приложение случайным образом выходит из строя при вызове на бесплатный().

Вопрос

У кого-нибудь есть какие-либо намеки на то, что может привести free() к такому провалу? должен ли я добавить некоторые операции по ограничению памяти? какие-либо параметры GCC или детали потоковой передачи, которые я, возможно, пропустил?

контекст:

Я опытный программист на языке Си, и у меня никогда не было такой проблемы. это в основном убивает 4 месяца работы. В среде с модульным тестированием и постоянной очисткой кода.

это TCP — сервер, использующий winsock 2 select() .

компиляция с помощью gcc в Windows 10: gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0

Тот же код, только завершается сбоем при запуске в под — потоке. все попытки воссоздать проблему потерпели неудачу в циклах тестирования с одним потоком и в тяжелых испытаниях на пытки.

Интересным фактом является то, что приложение не аварийно завершает работу при запуске в GDB.

У меня даже есть уровень управления распределением/удалением (используемый моим кодом и библиотеками), который сбрасывает каждый вызов в calloc() таблицу и free() внутри нее. Дополнительный инструмент анализа дампа проверяет все управление памятью, и в результате все они free() правильно сбалансированы (у меня нет двойного удаления или освобождения случайного указателя).

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

при выполнении в потоке этот код не использует общую память с основным потоком, он в основном запускает работника, который выполняет ВСЮ работу. в настоящее время существует только один вспомогательный поток, поэтому это не может быть столкновением рабочего потока. основной поток ничего не делает для ожидания tcp-порта и добавления узла в диспетчер пула потоков.

Код публикации бесполезен, так как ошибка не воспроизводима за пределами ее выполнения в подпространстве в моем основном приложении… т. е. это не кажется логической проблемой.

запуск того же кода из основного потока не приведет к сбою!

Сбой free() всегда происходит в определенной комбинации обрабатываемых данных, но он может быть успешным неопределенное количество раз до сбоя, и одна и та же функция вызывается МНОГО раз для эквивалентных данных и не завершается сбоем.

Комментарии:

1. Похоже, вы рассмотрели обычные ситуации. Другие ситуации заключаются в том, что вы записали в память и повредили данные. Поэтому следующим шагом было бы поиск переполнения буфера (в обоих основных потоках a). Попробуйте использовать valgrind

2. Насколько сложна/надежна репликация сбоев? Не могли бы вы использовать такие методы, как удаление некоторых вызовов free и проверка того, исчезнет ли сбой, или добавление «буферов безопасности» к распределениям и проверка того, исчезнет ли сбой?

3. @MartinYork в последний раз, когда я проверял, использование valgrind в Windows казалось довольно сложным. могу ли я использовать valgrind через WSL для тестирования своего приложения для Windows?

4. @DavidSchwartz добавление буферов было бы легко сделать (через мой слой mem mgt)… было бы по крайней мере интересно посмотреть, изменит ли это симптомы.

5. У вас есть возможность запустить такой valgrind или подобный инструмент, чтобы проверить, нет ли здесь какой-то ошибки в памяти? Это может быть основной причиной.

Ответ №1:

наконец-то нашел проблему.

В моем коде возникла странная проблема, когда был сгенерирован отрицательный размер выделения памяти, и мой malloc() код использования не выявил проблему и вернул поддельный адрес.

фактическая malloc() неудача, попытка освободить его, была ошибкой.

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

Когда мой код попытался memmove() ввести ошибочный адрес, он сгенерировал исключение доступа к ОС, и я смог отследить ошибку сразу, когда это произошло.

Наконец, насколько я могу судить, это не имело никакого отношения к потоковому коду. Я все еще чувствую, что странно, что он не разбился в gdb.

спасибо всем, кто дал мне идеи для изучения.