Программа segfaults на альпийском Linux. Как мне решить это?

Я работал над библиотекой данных webrtc на C/C++ и написал программу на C для:

  1. Создайте двух пиров из одного процесса.
  2. Установите связь между ними.
  3. Закройте соединение, если оно успешно.

Все отлично работает в доке-контейнере Debian и на моем хосте tumbleweed opensuse (все x86_64 и 64-битные), но в альпийском контейнере Linux (64-битный x86_64) я получаю SEGFAULT внутри дочерних процессов:

Сегфо в Либнице

Вышеприведенная функция взята из зависимости программы "libnice". Кажется, что *agent == NULL, и нет никакого способа сделать это нулевым в области действия вызывающей стороны. Я даже вставил printf("Argument is %p", agent); прямо перед вызовом функции, и она распечатывает свою память, и я могу убедиться, что она не равна нулю. Из разборки это выглядит как строка, в которой копирование содержимого агента (0x557a1d20) в качестве локальной переменной в стеке вызываемого приводит к segfault. Segfault всегда происходит в этой точке, даже после make clean и перекомпиляция. Сбой при активации записи? Стек коррупции?

ОБНОВЛЕНИЕ: я сделал более легкий контейнер и запустил его, и теперь он segfaults в другом месте в том же самом priv_conn_keepalive_tick_unlocked, Похоже, что аргумент установлен (обратите внимание на 0x7ffff7f9ad08): segfault2

Так как я думал, что могу достичь предела стека libmusl по умолчанию 80k, я использовал getrlimit(RLIMIT_STACK, &rl) чтобы получить размер стека, и похоже, что это уже 8 МБ, а не 80 КБ. Дальнейшее увеличение этого предела, похоже, не имеет никакого значения, за исключением того, что, если я назначу более 8 МБ, моя программа вылетает на ранней стадии внутри Gdb. GDB говорит, что получил неизвестный сигнал "??"; вне gdb он падает в нормальной точке, где обычно происходит сбой без изменения размера стека.

Я не уверен, что именно проблема (повреждение стека?) И что делать дальше, чтобы решить эту проблему.

Вот поток моей программы:

Для каждого создаваемого узла создается дочерний процесс с помощью fork(). Родительское <-> дочернее взаимодействие осуществляется ZeroMQ, и я использую буферы протокола для пересылки любых обратных вызовов (и их аргументов), которые запускаются внутри дочернего элемента, в цикл обработки событий, выполняемый в родительском процессе.

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

Действия по воспроизведению:

2 ответа

Решение

При дальнейшем расследовании сбой заключается в написании инструкции с небольшим большим отрицательным смещением от указателя базы стека, так что, вероятно, это просто простое переполнение стека.

Правильный способ исправить это - уменьшить использование избыточного стека или явно запросить большой стек в pthread_create время, но я не вижу, где pthread_create вызывается из. Быстрая проверка, чтобы убедиться, что это проблема, состоит в том, чтобы переопределить размер стека по умолчанию для новых потоков, выполнив следующее где-то в начале программы:

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 1<<20); // 1 MB
pthread_setattr_default_np(&attr);

Добавлять -Werror=implicit-function-declaration к вашим CFLAGS, и вы сразу же найдете причину. Ключом является значение указателя 0x557a1d20, которое почти наверняка является результатом усечения указателя до 32 бит. Это происходит, когда вам не удалось объявить функцию, которая возвращает указатель, а компилятор (по ужасному обратному значению по умолчанию) предполагает, что он возвращает int, а не выдает ошибку, а затем разрешает неявное преобразование из int в указатель, несмотря на то, что язык C запрещает его.

Другие вопросы по тегам