LINUX.ORG.RU

Можно ли игнорировать SIGABRT?

 , , , ,


0

2

Возникла у меня такая мысль. Все мы знаем стабильность типичной говнокодерской программы на C или C++. Сегфолты и всё такое. Можно ли в такой кривой программе поставить игнорирование SIGABRT? То есть допустить любое UB, лишь бы не упасть. К чему такое может привести?

★★★

Ответ на: комментарий от annulen

Можно перехватить любой сигнал, кроме SIGKILL

Это я знаю.

Вопрос в том, что делать дальше.

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

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

Я так делал однажды. Было приложение для терминала оплаты и я восстанавливал работу с помощью соответствующих jump’ов. Опыт интересный, но больше я так не делал, т.к. этому всё же довольно узкое применение.

unDEFER ★★★★★
()

SIGABRT в 99% случаях вызывается самой программой через функцию abort(), процесс сам себе посылает сигнал. Его можно проигнорировать signal(SIGABRT, fake_function);, но сам abort() всё равно завершит процесс. Тебе надо именно abort() подменять.

То есть допустить любое UB, лишь бы не упасть.

Обычно abort() дёргают в безвыходной ситуации когда продолжать работу программы невозможно. Хотя разработчик может куда лишнего втюрить. В любом случае это не решение проблемы, упадёт снова, но по другому.

К чему такое может привести?

К порче данных с которыми работает программа например. Что угодно. От ничего до всего.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от Werenter

По идее, с большой вероятностью после сегфолта идёт нормальный код. Поэтому, можно попытаться продолжить работу

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

Но в начале речь шла о SIGABRT, а это уже, скорее всего, разрушение кучи.

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

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

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

после сегфолта

Сегфолты это уже не сигналы, сигфорл регистрирует MMU процессора и сообщает ядру (вернее констатирует факт невозможности трансляции адресов), и то процесс грохает.

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

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Сегфолты это уже не сигналы, сигфорл регистрирует MMU процессора и сообщает ядру (вернее констатирует факт невозможности трансляции адресов), и то процесс грохает.

Не грохает, а отправляет ему SIGSEGV, а как процесс будет его обрабатывать, это уже его дело.

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

ну смотри, ты поймаешь сегфолт хэндлером, он вызовет твою функцию, но после возврата из хэндлера вернёшься к той же инструкции, которая приводит к сегфолту и так и будешь прыгать в этот хэндлер и обратно. Чтобы пропустить инструкцию надо узнать её размер (на x86 он разный) и сдвинуть rip на этот размер.
Но тут ещё вопрос какая эта инструкция. Если это какой-то ret, то пропустив её ты можешь попасть в следующую функцию (а что если там какой-то rm / -rf?). ret может сегфолтить если адрес возврата затёрт. и тут recover уже так просто не сделать.
я кстати иногда затыкаю так глючный код делая catch + return в gdb. Даже браузер с похеренной памятью так работал.
Ещё пример рабочего антисегфолта делал в xash3d. Там есть функция AbortCurrentFrame, начинающая кадр с нуля через longjmp. Я в неё прыгал через gdb catch и позволял таком образом движку ещё сколько-то проработать.
так же не забываем про всякие мьютексы и условные семафоры, которые от такого могут поломаться.

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

Ядру плевать же, он отправит сигнал просто как предупреждение перед убийством процесса. MMU физически не может адреса транслировать виртуальные в физические (не помню там что-то на уровне прав на границы адресов), работа программы физически невозможна будет. Да обработает процесс сигнал или нет это его дело, но после сигнала он упадёт в любом случае в случае сегфолта.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от Werenter

Если ulimit -c показывает 0 то вроде нигде. А так я фиг знаю, так получилось что корками никогда не пользовался. Там заранее можно вроде и место указать и процесс задать с которого получать. Но… я не пользовался.

Проще просто под gdb запустить и увидеть где падает, от того и прыгать исходники же есть .

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

MMU физически не может адреса транслировать виртуальные в физические

MMU не смог транслировать один конкретный адрес, что не мешает ему успешно транслировать следующий.

но после сигнала он упадёт в любом случае в случае сегфолта

Нет.

annulen ★★★★★
()

То есть допустить любое UB, лишь бы не упасть.

UB не имеет отношения к сигналам тут.

Но твой способ чреват потерей данных. Лучше уж перезагрузиться через fork-exec.

hateyoufeel ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

после сегфолта

Сегфолты это уже не сигналы, сигфорл регистрирует MMU процессора и сообщает ядру (вернее констатирует факт невозможности трансляции адресов), и то процесс грохает.

Ни разу. MMU выдаёт ошибку, что страницы с таким адресом сейчас нет в физической памяти. А вот почему её нет, это MMU уже не знает. Её может не быть потому что:

  • Кривой указатель
  • Страницы была выгружена в своп
  • Страница ещё не была создана
  • Ещё N вариантов

Как видишь, SIGSEGV из них прилетает только в одном.

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

ну смотри, ты поймаешь сегфолт хэндлером, он вызовет твою функцию, но после возврата из хэндлера вернёшься к той же инструкции

Не обязательно. Если приложение интерактивное, можно вернуться в меню.

unDEFER ★★★★★
()

Sigabrt вызывается из abort(), которая обычно помечена как noreturn функция. Так что вполне может быть, что там просто нету кода после неё, некуда возвращаться.

Может быть интереснее segmentation failed обрабатывать.

KivApple ★★★★★
()

Возникла у меня такая мысль. Все мы знаем стабильность типичной говнокодерской программы на C или C++. Сегфолты и всё такое. Можно ли в такой кривой программе поставить игнорирование SIGABRT?

Можно. Только, во-первых, это не тот сигнал - падения это как правило SEGV, ILL, BUS и FPE. ABRT обычно выкидывается явно по abort(3). Во-вторых, тебе нужно исправить поведение которое вызвало сигнал, иначе после возвращения из хэндлера сигнал опять выкинется. Удачи с этим.

То есть допустить любое UB, лишь бы не упасть. К чему такое может привести?

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

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

Да, если сделать так

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>


void handle() 
{
    printf("Ой! Аборт\n");
};

int main(int argc, char *argv[])
{
    signal(SIGABRT, handle);
    char * data = malloc(1);
    for (int i = 0; 1; ++i)
    {
        data[i]=i;
        abort();// как сказал TC процесс где то сам себе шлёт аборт
        printf("Привет %d\n",i);
    }
    return 0;
}

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

Если у нас ситуация вот такая

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void handle() 
{
    printf("Ой! Память\n");
};


int main(int argc, char *argv[])
{
    signal(SIGSEGV, handle);

    char * data = malloc(1);
    for (int i = 0; 1; ++i)
    {
        data[i]=i;
        printf("Привет %d\n",i);
    }
    return 0;
}

То да, ядро инициирует сигнал SIGSEGV на основе VM_FAULT_BADMAP или VM_FAULT_BADACCESS fault.c

Но если в его программе случай что у меня выше его программа зависнет в бесконечном цикле обработки хандлера. Что тоже жъёпка пёпка. =)

Когда SIGSEGV прилетает в программу которая уже наалоцировала кучу всего и дрыгает кучу всего. С вероятностью в 99,999999999999% у неё просто не будет обработчика исключительной ситуации для каждого куска памяти с которой программа работает. В целом да, ты прав и выше @annulen прав. Ну пришёл сигнал, ну обработал ты его, ну и едешь дальше. Но у случае ТС автор ПО дрыгает abort() в исключительной ситуации и всё.

И ещё мы тут пока обсуждали смешали в кучу котлеты и яйца, а именно вызов abort() сам по себе SIGABRT по отдельности и SIGSEGV. Чего там наворочено в таинственной и загадочной программе у ТС нипанятна.

В программе внештатная ситуация, будет большой удачей что можно просто обработать сигнал заглушкой и проигнорировать и всё продолжит работать. Для этого автор кода должен был просто по приколу в случае отклонения внутренних расчётов например на 0.1 слать самому себе сигнал аборта. Ну нинаю =)

А вообще сложна, пойду на lua «привет мир» напишу и какава попью, а то борода вырастет :D

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Ты не совсем понял. Дефолтный обработчик SIGSEGV как раз дёргет abort, следовательно можно abort перехватить и продолжить работу. А выбран именно SIGABRT, а не SIGSEGV, так как сегфолт - не единственная ошибка. Есть же ещё SIGILL, который в принципе близок к сегфолту.

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

Дефолтный обработчик SIGSEGV как раз дёргет abort

Так ты хочешь свой повесить, обработчик то =) Ой, мы ту на гуще гадаем. Запускай gdb (расшифровывается как ГдеБл**ть ошибка?) и фикси. А то сигналы сигналами, но пробелма то в коде может быть просто фатальна для самой программы по определению. И хоть ты что отлавливай не отлавливай, некорректный код так и останется некорректным и будет некорректно работать, если например есть в ПО деление на 0, ну отловишь ты этом момент, а толку? Вычисления то уже неверные, ну и всё =)

Так шта, я не верю что можно не глядя заигнорить околофатальный сигнал в программе и она корректно будет работать. Это как проигнорировать то что конфигурационного файла нет и fopen вернул NULL, мы начали читать файл и получили исключение, тупо проигнорили его и типа ПО конфиг сама себе из мусора в памяти нарисует, примеров можно привести мильярд. Я вот про это. =)

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Эти деления на ноль это что-то. Вон в Расте сделали даже проверку на переполнения всех целочисленных вычислений медленючую, а NaN во float’е отлавливать приходится по старинке, ну или озаботиться об этом заранее и использовать спецтип из соответствующего крейта.

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

с большой вероятностью после сегфолта идёт нормальный код.

Только в данных скорее всего мусор. Что-то не записалось или записалось не полностью. У тебя какая-то парадигма «fail early» наоборот получается, причем в самом херовом варианте исполнения.

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

в хаскеле делить на ноль можно, получается бесконечность

А на powerpc при делении на ноль на ассемблере/C получается ноль(и не бросается никакое исключение) - задокументированная особенность архитектуры, между прочим. Дальше-то что?

Pinkbyte ★★★★★
()
Последнее исправление: Pinkbyte (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

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

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

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

по умолчанию установлен лимит на его создание.

ulimit -c unlimited

ЕМНИП. Но вообще то все давно уже не так, в бубунте шоб корку глянуть достаточно

coredumpctl gdb
AntonI ★★★★
()
Ответ на: комментарий от Pinkbyte

Я бы уж тогда делал регулярные снэпшоты, и после таких сигналов полностью перегружал процесс из последнего снэпшота. Но как это Ъ сделать (перегрузить процесс с чистого листа) я ХЗ… если только есть родительский процесс который следит за такими вещами.

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

По идее, с большой вероятностью после сегфолта идёт нормальный код. Поэтому, можно попытаться продолжить работу

Уже не важно, что идет дальше. Во-первых, возобновление выполнения может быть невозможно, потому что например произошло переполнение буффера, стек переписался, и куда возвращаться из него уже не известно. Нету этого «дальше», оно затерлось. Во-вторых, программа пытается сохранить свои данные в чужом месте. ОС ей этого сделать не дает. Если продолжить выполнение, и допустим, что это был просто единичный доступ к одной ячейке памяти, так что стек в порядке, но программа будет считать, что данные записаны, хотя это не так. И ошибка распространится, возможно, откладывая свое проявление на потом (возможно, на через 5 лет) и по разным потокам. Это будет полный пипец для отладки.

seiken ★★★★★
()