LINUX.ORG.RU

Прилетает SIGABRT

 


0

2

Есть некий софт, он вполне себе нормально работает, но иногда ему прилетает SIGABRT. Прилетает редко, примерно раз в месяц. Надо это отловить. Повесил обработчик на SIGSEGV и SIGABRT, в обработчике запускаю backtrace, даже собрал альтернативу утилите addr2line, так что при падении в лог пишется не просто текущий стек с адресами, а полностью вижу имя функции и строку, где произошло падение. Проверяю разными способами: посылка сигнала, повторный вызов free(), обращение по нулевому адресу, вызов abort(), бесконечная рекурсия. Везде приходит соответствующий сигнал, программа падает, в логе все выдается как надо.

Однако. Когда прилетает реальный аборт, эта схема почему-то не работает. Вижу в логе, что обработчик аборта вызывает backtrace, backtrace не проходит, прилетает сегфолт, обработчик сегфолт тоже пытается вызвать backtrace, и на этом все.

Добавил при запуске вызов sigaltstack, который выделяет отдельный стек для обработчика сигналов на случай порчи основного. Не помогло.

Почему на реальном SIGABRT backtrace падает, хотя на тестовых ошибках падение вполне нормально отлавливается? Как еще может прийти аборт, если я в софте не юзаю ни abort(), ни assert()? Еще вариант - вызов abort() из какой-то либы, но почему-то не получить backtrace..



Последнее исправление: a400k4a (всего исправлений: 2)

Возьми кордамп и посмотри в нём трейс, не? Только убери сначала свои обработчики SIGABRT и SIGSEGV. Они могут помешать сдампать корку. И собрать с дебажными символами не забудь.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 2)

Стек скорее всего портится. Что-то пишет по неправильному адресу, портит данные и адрес возврата. После этого assert кидает SIGABRT, но так как стек испорчен, попытка его увидеть приводит к сегфолту.

monk ★★★★★
()

Есть некий софт, он вполне себе нормально работает, но иногда ему прилетает SIGABRT.

Может это OOM killer присылает?

X512 ★★★★★
()

Прилетает редко, примерно раз в месяц. Надо это отловить.

Внешний отладчик установить нельзя?

X512 ★★★★★
()

если падает в обработчике какого-то прерывания, вряд ли ты увидишь оттуда свой стек. ничего такого там нет?

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от X512

Взял исходники addr2line, там зависимости от неких bsd либ, которые под linux как бы есть, но с bsd частично несовместимы. В итоге собрал эти либы у себя и потом вместе с ними уже addr2line. Либы: libdwarf, libelf, libelftc. Могу поделиться.

a400k4a
() автор топика
Ответ на: комментарий от X512

Может это OOM killer присылает?

А как проверить? VmPeak в /proc/pid/status показывает меньше 200 Mb всегда, не течет вроде ничего.

Внешний отладчик установить нельзя?

Не пробовал. Хотелось бы постоянно действующий инструмент для анализа падений.

если падает в обработчике какого-то прерывания, вряд ли ты увидишь оттуда свой стек. ничего такого там нет?

Нет, там все просто, прерываний нет, сбор данных с удаленных устройств по tcp и запись в БД.

Стек скорее всего портится. Что-то пишет по неправильному адресу, портит данные и адрес возврата. После этого assert кидает SIGABRT, но так как стек испорчен, попытка его увидеть приводит к сегфолту.

Ну вот я и думал про стек, сделал так:

	// Prepare alternative stack for signal handler
	stack_t ss;
	ss.ss_sp = malloc(SIGSTKSZ);
	if (ss.ss_sp == NULL) {
		perror("malloc");
		exit(EXIT_FAILURE);
	}
	ss.ss_size = 512*1024;
	ss.ss_flags = 0;
	if (sigaltstack(&ss, NULL) == -1) {
		perror("sigaltstack");
		exit(EXIT_FAILURE);
	}

	struct sigaction sa;
	sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
	sigemptyset(&sa.sa_mask);
	sa.sa_sigaction = sighandler_faults;
	if (sigaction(SIGSEGV, &sa, NULL) == -1) {
		perror("sigaction");
		exit(EXIT_FAILURE);
	}
	if (sigaction(SIGABRT, &sa, NULL) == -1) {
		perror("sigaction");
		exit(EXIT_FAILURE);
	}
	if (sigaction(SIGPIPE, &sa, NULL) == -1) {
		perror("sigaction");
		exit(EXIT_FAILURE);
	}

По идее, хендлер должен запуститься со своим стеком.

a400k4a
() автор топика
Ответ на: комментарий от a400k4a

ss.ss_sp = malloc(SIGSTKSZ);

#define SIGSTKSZ 8192

Не мало ли, особенно с кодом трассировки стека? Возможно требуется выравнивание.

Тут предлагают выделять через mmap с защитной страницей.

ss.ss_size = 512*1024;

Почему не тоже число, что и в malloc?

X512 ★★★★★
()
Ответ на: комментарий от a400k4a

Хотелось бы постоянно действующий инструмент для анализа падений.

Можно всегда запускать программу в gdb как указано тут.

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от a400k4a

(Должен запуститься со своим стеком, но результат тот же, это не помогло).

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

либо надо под отладчиком пускать, либо делать свой бектрейс, который будет сразу дампить фреймы стека на консоль например, и вот когда он упадет, сразу будет видно на каком фрейме.

а расписать стек легко переполнением локального буфера, вот напишешь туда байта 4 лишних, и стек у тебя упал.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 2)
Ответ на: комментарий от X512

Почему не тоже число, что и в malloc?

Потому что я накосячил) Сначала было SIGSTKSZ везде, потом я решил увеличить. Смэппить с защитными страницами интересно, да. Попробую. Только непонятно, зачем там предлагают и сверху, и снизу по странице. Стек-то ведь в одну сторону растет.

a400k4a
() автор топика
Ответ на: комментарий от a400k4a

Только непонятно, зачем там предлагают и сверху, и снизу по странице.

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

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от a400k4a

Только непонятно, зачем там предлагают и сверху, и снизу по странице. Стек-то ведь в одну сторону растет.

стек в интеле растет в сторону младших адресов(пуш декрементирует стекпоинтер), а массив растет в сторону старших(больший индекс больший адрес).

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

зы. включи все варнинги типа -Wall, и билди. посмотри всякие полученные варнинги на преобразованиях типов. особенно когда короткий тип принудительно преобразуют к длинному, берут адрес и отдают какой-то функции. тогда функция считая, что там длинный тип, пишет в физически короткий и расписывает стек. также опасен варнинг - нет возврата резульата из функции что должна его возвращать(его надо делать ошибкой, а не варнингом вообще-то). тут система может себя чудесно вести. Также могут быть опасны хардкорные преобразования типов (type)value, я правда про С++ говорю. их лучше менять на статик и прочие касты.

вообщем все сильно похоже на попорченный стек.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от a400k4a

Попробуйте еще вот эти флаги

-pedantic -Wmissing-prototypes -Wstrict-prototypes

Немного занудно, но смысл есть.

anonymous
()
Ответ на: комментарий от anonymous

Попробовал, не нашел ничего, к сожалению. И правда, туча занудных ворнингов на тему «в С90 так было нельзя» :)

a400k4a
() автор топика
Ответ на: комментарий от a400k4a

Попробовал, не нашел ничего, к сожалению. И правда, туча занудных ворнингов на тему «в С90 так было нельзя» :)

Возможно, в процессе их устранения глаз за что-нибудь да зацепится.

На худой конец, valgrind натравливать уже пробовали?

anonymous
()
Ответ на: комментарий от a400k4a

это попробуй, strong который

https://mudongliang.github.io/2016/05/24/stack-protector.html

вообще, если бага реально раз в месяц,…это она работает весь месяц а потом падает, или ты ее пускаешь по сто раз на дню и она падает раз в месяц?

если она работает непрерывно месяц, а потом падает, значит раз в месяц что-то такое происходит, от чего ей становится плохо.

alysnix ★★★
()
Ответ на: комментарий от a400k4a

Нет, там все просто, прерываний нет, сбор данных с удаленных устройств по tcp и запись в БД.

Так заюзай скриптуху, Люк!

pihter ★★★★★
()
Ответ на: комментарий от anonymous

Asan натравил, молчит пока. Нет, не сто раз запускаю, постоянно работает. Раз в месяц - не строго регулярно, то есть с внешним событием связать пока не получается. Да там событий-то особых нет, собираются данные раз в 15 сек с сотни удаленных постов, кладутся в базу. Раз в сутки - подтягиваются логи и обслуживается база, удаляются старые записи и т.п.. Падает причем не во время этих действий.

a400k4a
() автор топика
Ответ на: комментарий от alysnix

еще можно собрать код с проверкой переполнения стека. GCC умеет такое

cvs-255 ★★★★★
()

Контрольный срок вышел, больше падений нет, поэтому считаю проблему решенной. Ура. Ошибка найдена: sprintf с выходом за границу строки из-за прилета неожиданно большого ответа от внешнего устройства. И ведь всегда использовал snprintf, а тут почему-то поленился и написал sprintf, и вот он результат. Поскольку больше не падает, пока считаю, что это и была причина.

Да, корку собрал, это и помогло найти. Об этом писал и первый оратор, но я как-то не проникся, а зря! Оказалось, что это самый простой и быстрый путь, даже настраивать ничего не надо, в ubuntu уже есть все для сбора и анализа. Для таких же как я законспектирую:

  1. В ubuntu есть служба apport, которая пишет coredump при падении в /var/crash
  2. Сгенерированный crash report распаковывается командой apport-unpack path_to_report.crash new_dir_to_unpack
  3. В папке с распакованным репортом запустить gdb `cat ExecutablePath` CoreDump
  4. В консоли gdb выполнить команду bt и смотреть стек

Мои ухищрения с бэктрейсом оказались очередным велосипедом. Хотя, может быть, когда-то и пригодится, на какой-нибудь embedded системе.

a400k4a
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.